-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] Preview for PDF Attachments #705
Comments
@Yoginth @ITforHome Currently the attachements are only downloaded. In case of PDF there are severall possibilities to implement a "preview". There can be a real preview in form of a generated image including only the first page of the pdf, change the 'Content-Type' for this download (as suggested by @Yoginth) so the browser uses the defined preview(plugin) or embedd a preview using pdfjs or something. In the last 2 cases the file technically is already downloaded to your computer. |
@Yoginth yes and no. |
@Yoginth @lommes General: |
When I open the Bookstack App on Mobile Safari or Chrome I get a preview about attached PDF-Files when I click on it. |
@albertmatyi your solution works great! thank you! |
Created a more userfriendly version. Code & Readme at: https://gist.github.com/albertmatyi/7c23a679a4a81c61c3628f6c15480b76 |
For anyone coming across this and wanting to view PDF attachments in the browser, change the return statement of
|
Where? There are a lot of functions in that file. |
@aldoblack I think it's intended to be a replacement of this line: Note, Such changes to core files are not officially supported and may cause complications, or be lost, when you upgrade. |
I understand. Thank You very much. |
Tried out the solution by @albertmatyi. The pdf is successfully embedded on the page. Big thanks to @albertmatyi. But it also causes errors if you want to export that page as a pdf later. |
Just to note on this, Within BookStack v21.05.2 it's now possible to open up attachments in an "inline" (Not forced download) via Ctrl+Click of the attachment (Or Cmd+click on Mac). Alternatively, an attachment link of this type can be manually formed by adding an |
ctrl+click doesn't work if the filesize is over the default upload size limit for some reason, which is weird |
Good evening @ssddanbrown , is there still an integrated solution planned here in the future or should users use @albertmatyi solution? I think this would be an important feature for many people... Christian |
my current hacked version of this feature is adding this to the source code of the page i want the PDF to be on (assuming the pdf is uploaded somewhere on the site as an attachment: What it looks like: |
Here's the above made into a bookmarklet:
Usage
Tested that on Firefox. This is all a hack and any of these workarounds may break upon update. |
Updated script by @albertmatyi for Bookstack v22.02: <!--Based on original by github.com/albertmatyi-->
<!--Updated March 10th 2022 by github.com/nmpeckham-->
<!--Confirmed working on Bookstack version 22.02-->
<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, callback) {
var btnWrapper = document.createElement('button');
btnWrapper.className = 'tox-btn';
btnWrapper.tabindex = -1;
btnWrapper.ariaLabel = details;
btnWrapper.type = 'button';
btnWrapper.title = details
btnWrapper.ariaDisabled = "false";
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('div.tox-toolbar__group')[5];
ar.prepend(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('div.tox-toolbar__group')[5];
if (btn) {
createButton('pdf', 'Insert a PDF', function (e) {
// show dialog
var editor = tinyMCE.editors[0];
editor.windowManager.open({
title: 'Insert PDF', // The dialog's title - displayed in the dialog header
body: {
type: 'panel', // Root panel
items: [
{
type: 'input', // HTML text input component
name: 'pdfurl'
}
]
},
buttons: [ // A list of footer buttons
{
type: 'submit',
text: 'OK'
},
{
type: 'cancel',
text: "Cancel"
}
],
onSubmit: function (api) {
// Insert content when the window form is submitted
var data = api.getData();
editor.insertContent('<p> <canvas data-pdfurl="' + data.pdfurl + '"></canvas> </p>');
api.close();
}
});
});
}
//-------------------- 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> |
@nmpeckham Hi, I just tested it in version 22.02.3, the function is also given. Unfortunately, no button icon is displayed (Edge and Firefox on the latest version). With me there is simply a teaching stelle (which can be clicked). But thanks for the work! |
I can confirm this works in version 22.02.3. The button is back |
@nmpeckham Hi, the butten is unfortunately still not there :( |
Here's an altered version of the above scripts that I recently amended for a BookStack user, that was using the above hacks. Think I tested it in v22.06. This script is not official nor supported: View Code<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">
// ------------------- THIS SECTION ADDS A PDF BUTTON TO THE EDITOR TOOLBAR THAT ALLOWS YOU TO EMBED PDFS
// Use BookStack editor event to add custom "Insert PDF" button into main toolbar
window.addEventListener('editor-tinymce::pre-init', event => {
const mceConfig = event.detail.config;
mceConfig.toolbar = mceConfig.toolbar.replace('link', 'link insertpdf')
});
// Use BookStack editor event to define the custom "Insert PDF" button.
window.addEventListener('editor-tinymce::setup', event => {
const editor = event.detail.editor;
// Add PDF insert button
editor.ui.registry.addButton('insertpdf', {
tooltip: 'Insert PDF',
icon: 'document-properties',
onAction() {
editor.windowManager.open({
title: 'Insert PDF',
body: {
type: 'panel',
items: [
{type: 'textarea', name: 'pdfurl', label: 'PDF URL'}
]
},
onSubmit: function(e) {
// Insert content when the window form is submitted
editor.insertContent('<p> <canvas data-pdfurl="' + e.getData().pdfurl + '"></canvas> </p>');
e.close();
},
buttons: [
{
type: 'submit',
text: 'Insert PDF'
}
]
});
}
});
});
//-------------------- 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);
};
window.addEventListener('DOMContentLoaded', function() {
Array.prototype.forEach.call(document.querySelectorAll('canvas[data-pdfurl]'), renderPdf);
});
</script> |
Thanks for this but... |
@BertCurbit To confirm, do you see a little page icon next to the link toolbar icon? |
@ssddanbrown Oh my... I was looking for the button with PDF text in it, like the previous version. But now it's just a blank page icon that I completely overlooked. It looks way better now and works perfectly. Thanks for this! |
Is there a way to have this working offline? I have tried placing the pdf.min.js file inside the public folder in bookstack and pointing to it. For example I can reach this file at this address from a browser however embedded PDFs are not working. They work fine when I bring bookstacks online and point to the cdnjs address. |
I eventually got it working by also having a second file named pdf.worker.min.js along side pdf.min.js. |
I'm trying to implement this, but I don't know where to add the code. I inserted it in the settings table using the app-custom-head key. The code is added in the header, but no icon is added to the tinymce toolbar. |
This issue hints at a potential fix #3586 (comment) (worked for me). |
Does someone have a working version of this script on the BookStack Version 23.06.2? Currently trying to implement it for use and i only get a grey window inside the page, i expected it to be like that in the editor, but its also not showing on the view page. The download button points to the right .pdf though so it should be a working .pdf file. |
Anyone can help? |
this #705 (comment) works like a charm in my case.
P.S. Would be nice to have it by default in BookStack |
FWIW having the exact same issue. Any yes "built in" PDf support would be great. |
I did some change to @ssddanbrown's code. I think now it's working. <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">
// ------------------- THIS SECTION ADDS A PDF BUTTON TO THE EDITOR TOOLBAR THAT ALLOWS YOU TO EMBED PDFS
// Use BookStack editor event to add custom "Insert PDF" button into main toolbar
window.addEventListener('editor-tinymce::pre-init', event => {
const mceConfig = event.detail.config;
mceConfig.toolbar = mceConfig.toolbar.replace('link', 'link insertpdf')
});
// Use BookStack editor event to define the custom "Insert PDF" button.
window.addEventListener('editor-tinymce::setup', event => {
const editor = event.detail.editor;
// Add PDF insert button
editor.ui.registry.addButton('insertpdf', {
tooltip: 'Insert PDF',
icon: 'document-properties',
onAction() {
editor.windowManager.open({
title: 'Insert PDF',
body: {
type: 'panel',
items: [
{type: 'textarea', name: 'pdfurl', label: 'PDF URL'}
]
},
onSubmit: function(e) {
// Insert content when the window form is submitted
editor.insertContent('<p id="bkmrk-%C2%A0%C2%A0"> <canvas data-pdfurl="' + e.getData().pdfurl + '"></canvas> </p>');
e.close();
},
buttons: [
{
type: 'submit',
text: 'Insert PDF'
}
]
});
}
});
});
//-------------------- 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);
};
window.addEventListener('DOMContentLoaded', function() {
Array.prototype.forEach.call(document.querySelectorAll('canvas[data-pdfurl]'), renderPdf);
});
</script> |
Based on @UvinduS 's code, I did some change to use another pdf-viewer for better interactive (text selection, copy, zoom, navigation) |
Feature Request
Hey Everybody,
i am very happy to see the rapid progress about the App - great Work
But i miss the function, that I can get a preview of attached pdf-Files, before I downloading it.
It would be very nice, when these feature where implement in the next versions of Bookstack.
The text was updated successfully, but these errors were encountered: