PDFs in a slideshow folder now play alongside images and Word documents: a one-page PDF is shown whole, a longer one fits the width and scrolls through its pages during docInterval (first 30 pages - rendering a huge PDF for a few seconds on screen isn't worth the time and memory). - kiosk-app/pdf-render.mjs renders pages to canvases with pdf.js (pdfjs-dist ^6.3.289, new kiosk-app dependency): no viewer toolbar, and the slideshow's existing auto-scroll works on it. pdf.js >= 4.2.67 is required (CVE-2024-4367: older versions could run script embedded in a crafted PDF); isEvalSupported:false too. It's a module because pdf.js only ships as one - confirmed to load (with its worker) from file:// in Electron 44. - Hands pdf.js a copy of the bytes: it transfers the buffer it's given, which would break the cached copy the next time round. - slideshow.js lists .pdf as a document type; the title-request handler serves any listed non-image item (still only from that folder). - provision_install_app now copies kiosk-app/*.mjs as well - without it pdf-render.mjs would never reach an installed kiosk. Tested with the real main.js on Electron 44.5.1 under Xvfb + Openbox, app files copied with provision_install_app's own glob: a photo, a one-page PDF, a three-page PDF and a .docx played in order, twice round; the one-page PDF shown whole, the three-page one scrolled through all pages. Offline bundle rebuilt with pdfjs-dist (437 MB total, +22 MB). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
72 lines
3.2 KiB
JavaScript
72 lines
3.2 KiB
JavaScript
// Browser-side PDF rendering for slideshow.html, via pdf.js (pdfjs-dist,
|
|
// an npm dependency of this app). A module because pdf.js only ships as
|
|
// one; it sets window.KioskPdf for slideshow.html's classic script.
|
|
//
|
|
// Pages are drawn to canvases (no viewer toolbar, and the slideshow can
|
|
// scroll them like a Word document). pdf.js >= 4.2.67 is required - older
|
|
// versions could run JavaScript embedded in a crafted PDF (CVE-2024-4367);
|
|
// isEvalSupported:false is set as well, belt and braces.
|
|
import * as pdfjs from './node_modules/pdfjs-dist/build/pdf.min.mjs';
|
|
|
|
const base = new URL('./node_modules/pdfjs-dist/', import.meta.url).href;
|
|
pdfjs.GlobalWorkerOptions.workerSrc = base + 'build/pdf.worker.min.mjs';
|
|
|
|
// Rendering every page of a huge PDF would take a long time and a lot of
|
|
// memory for something shown for seconds - stop after this many.
|
|
const MAX_PAGES = 30;
|
|
|
|
window.KioskPdf = {
|
|
// Render `bytes` into `container` (emptied first), sized for a screen of
|
|
// width x height: a one-page PDF fits entirely (like an image); a longer
|
|
// one fits the width, pages stacked, for the slideshow to scroll.
|
|
async render(bytes, container, width, height) {
|
|
container.innerHTML = '';
|
|
const task = pdfjs.getDocument({
|
|
// A copy: pdf.js transfers (empties) the buffer it's given, and the
|
|
// slideshow caches these bytes to show the PDF again next round.
|
|
data: bytes.slice(),
|
|
isEvalSupported: false,
|
|
cMapUrl: base + 'cmaps/',
|
|
cMapPacked: true,
|
|
standardFontDataUrl: base + 'standard_fonts/',
|
|
});
|
|
try {
|
|
const doc = await task.promise;
|
|
const count = Math.min(doc.numPages, MAX_PAGES);
|
|
const single = doc.numPages === 1;
|
|
const gap = 16;
|
|
container.style.display = 'flex';
|
|
container.style.flexDirection = 'column';
|
|
container.style.alignItems = 'center';
|
|
container.style.gap = gap + 'px';
|
|
container.style.padding = (single ? 0 : gap) + 'px 0';
|
|
container.style.minHeight = single ? '100%' : '';
|
|
container.style.justifyContent = single ? 'center' : '';
|
|
const dpr = window.devicePixelRatio || 1;
|
|
for (let n = 1; n <= count; n++) {
|
|
const page = await doc.getPage(n);
|
|
const natural = page.getViewport({ scale: 1 });
|
|
const scale = single
|
|
? Math.min(width / natural.width, height / natural.height)
|
|
: Math.min((width - 2 * gap) / natural.width, 1.6 * (96 / 72));
|
|
const vp = page.getViewport({ scale: scale * dpr });
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = Math.floor(vp.width);
|
|
canvas.height = Math.floor(vp.height);
|
|
canvas.style.width = Math.floor(vp.width / dpr) + 'px';
|
|
canvas.style.height = Math.floor(vp.height / dpr) + 'px';
|
|
canvas.style.background = '#fff';
|
|
canvas.style.boxShadow = single ? '' : '0 2px 8px rgba(0,0,0,.4)';
|
|
container.appendChild(canvas);
|
|
await page.render({ canvasContext: canvas.getContext('2d'), viewport: vp }).promise;
|
|
page.cleanup();
|
|
}
|
|
return { pages: doc.numPages, rendered: count };
|
|
} finally {
|
|
// Frees the document and its worker-side data (pdf.js 6 has this on
|
|
// the loading task, not the document).
|
|
task.destroy();
|
|
}
|
|
},
|
|
};
|