Skip to content

Instantly share code, notes, and snippets.

@albertmatyi
Last active July 16, 2023 20:49
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save albertmatyi/7c23a679a4a81c61c3628f6c15480b76 to your computer and use it in GitHub Desktop.
Save albertmatyi/7c23a679a4a81c61c3628f6c15480b76 to your computer and use it in GitHub Desktop.
Bookstack PDF embedding support snippet

Bookstack PDF Embed plugin

Setup

On /settings page Custom HTML head content add paste in the script/style above

Usage

The setup will result in a PDF button in the toolbar whenever you open the editor.

Pressing the button pops up a dialog, where you may enter the URL to the PDF.

This in turn will add a:

<p>&nbsp;<canvas data-pdfurl="[URL]"></canvas></p>

to your content, which in edit mode renders as a simple gray block.

In view mode, it will load the referenced PDF and render it on canvas elements using the pdfjs library.

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.466/pdf.min.js"></script>
<style>
canvas[data-pdfurl] {
background-color: lightgrey;
width: 100%;
}
.page-content a {
color: #39f;
text-decoration: underline;
}
.pdf-wrapper {
position: relative;
height: 80vh;
width: 100%;
}
.pdf-wrapper .download-link {
position: absolute;
top: -2em;
right: 0;
z-index: 50;
}
.pdf-wrapper .pdf-scroller {
height: 100%;
overflow: auto;
}
</style>
<script type="text/javascript">
var createButton = function(text,details,id,callback) {
var btnWrapper = document.createElement('div');
btnWrapper.id = id;
btnWrapper.className='mce-widget mce-btn';
btnWrapper.tabindex=-1;
btnWrapper.role='button';
btnWrapper.ariaLabel=details;
var btn = document.createElement('button');
btn.id=id+'-button';
btn.innerText = text;
btn.role='presentation';
btn.type='button';
btn.tabindex='-1';
btn.style.border = 'solid 1px';
btn.style.padding = ' 3px 7px';
btnWrapper.append(btn);
btnWrapper.onclick = callback;
var ar =document.querySelectorAll('.mce-btn.mce-last');
var lastBtn = ar[ar.length-2];
lastBtn.parentNode.append(btnWrapper);
};
window.addEventListener('load', function () {
// ------------------- THIS SECTION ADDS A PDF BUTTON TO THE EDITOR TOOLBAR THAT ALLOWS YOU TO EMBED PDFS
var btn = document.querySelectorAll('#mceu_20')[0];
if (btn) {
createButton('pdf', 'Insert a PDF', 'mceu_pdf', function(e) {
// show dialog
var editor = tinyMCE.editors[0];
editor.windowManager.open({
title: 'Insert PDF',
body: [
{type: 'textbox', name: 'pdfurl', label: 'PDF URL'}
],
onsubmit: function(e) {
// Insert content when the window form is submitted
editor.insertContent('<p>&nbsp;<canvas data-pdfurl="' + e.data.pdfurl + '"></canvas>&nbsp;</p>');
}
});
});
}
//-------------------- THE CODE BELOW SHALL BE ACTIVE IN VIEWING MODE TO EMBED PDFS
var renderPdf=function(canvas) {
var url = canvas.dataset.pdfurl;
var pdf = null;
// wrap canvas in div
var wrapper = document.createElement('div');
wrapper.className='pdf-wrapper';
var scroller = document.createElement('div');
scroller.className='pdf-scroller';
wrapper.appendChild(scroller);
canvas.parentNode.insertBefore(wrapper, canvas.nextSibling);
scroller.insertBefore(canvas, null);
var downloadLink = document.createElement('a');
downloadLink.href = url;
downloadLink.className="download-link";
downloadLink.innerText = 'Download PDF now ↓';
wrapper.appendChild(downloadLink);
var renderPage = function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
// Fetch canvas' 2d context
var context = canvas.getContext('2d');
// Set dimensions to Canvas
canvas.height = viewport.height;
canvas.width = viewport.width;
canvas.style.maxWidth='100%';
// Prepare object needed by render method
var renderContext = {
canvasContext: context,
viewport: viewport
};
// Render PDF page
page.render(renderContext);
if (currentPage < pdf.numPages) {
currentPage++;
var newCanvas = document.createElement('canvas');
scroller.insertBefore(newCanvas, canvas.nextSibling);
scroller.insertBefore(document.createElement('hr'), canvas.nextSibling);
canvas=newCanvas;
pdf.getPage(currentPage).then(renderPage);
}
};
var currentPage = 1;
pdfjsLib.getDocument(url)
.then(function(pdfLocal) {
pdf = pdfLocal;
return pdf.getPage(1);
})
.then(renderPage);
}
Array.prototype.forEach.call(
document.querySelectorAll('canvas[data-pdfurl]'),
renderPdf);
});
</script>
@jadedmia
Copy link

Any idea how to get local files to render using file:///C:/path to document/file.pdf format?

@christianhz01
Copy link

Hi, since version 22 it stopped working 😐

@yuelu714
Copy link

yuelu714 commented Mar 3, 2022

We regret that we upgraded :( It would great if you can update the plugin to work with version 22. Thank you!

@mattisaoldgit
Copy link

Just showing support, myself and lots of others would be very grateful if you can find time to take a look.

Thanks

@sp4m4r
Copy link

sp4m4r commented Mar 24, 2022

Would be good to have it again. :)

@detritus420
Copy link

We'd like this as well

@Seventy-9DegreesNorth
Copy link

Matyas, thank you so much for this script. We just started using it on [BookStack v21.12.5] and it appears to be working great. Although, initially, I pasted in the script into the HTML head TWICE -- and it made the rendering really wonky and showed two buttons. After I figured out my dumb mistake, and repasted the code ONCE -- it works great.

The other great thing is that you can post multiple PDFs in one Bookstack page.

Thank you again -- much appreciated and very elegant and lightweight way to approach this issue.

Final question, do you think this is "upgrade safe"? Meaning, if we make a time investment to structure our PDF distribution around the ability to do these embeds, do you think Bookstack upgrades will break this capability?

@Nexow91
Copy link

Nexow91 commented Jul 16, 2023

Hello I would like to use the plugin to embed pdf as you described unfortunately I do not know exactly where and how I have to insert what where. Can you give more info about this ?

Do I have to insert it in a new page, in an .ini file and somewhere in the settings ?

/settings doesn't work you either end up at /settings/features or /settings/maintance etc.

Would be grateful for any info.

Edit. I have found the settings and the hmtl header option no buttons show on editor sites. what do im wrong ? My version is 23.06.2 its a version issue ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment