diora-web/static/js/app/58-pdf.js

670 lines
25 KiB
JavaScript
Raw Normal View History

'use strict';
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
// ---------------------------------------------------------------------------
// The PDF reader: one document, shown either scrolling (every page has its
// place, only those near the view are drawn) or page by page, cropped to the
// page's text (66-pdf-paginated.js). Whichever, a page is drawn only through
// _pdfDraw: one drawing at a time per canvas, sharp at the device's pixel
// ratio but never larger than PDF_MAX_PIXELS.
// ---------------------------------------------------------------------------
// pdf.js's own viewer caps a canvas at this many pixels: beyond it phones run out of memory.
const PDF_MAX_PIXELS = 16777216;
// One pdf.js worker for every document (the reader, covers, uploads): starting one takes a
// moment — on an e-reader a long one.
let _pdfWorker = null;
function _pdfSharedWorker() {
if (!_pdfWorker && typeof pdfjsLib !== 'undefined' && pdfjsLib.PDFWorker) {
try { _pdfWorker = new pdfjsLib.PDFWorker(); } catch (e) { _pdfWorker = null; }
}
return _pdfWorker;
}
/** pdf.js's loading task for [bytes] (a Uint8Array), on the shared worker. */
function _pdfLoad(bytes) {
const worker = _pdfSharedWorker();
return pdfjsLib.getDocument(worker ? {data: bytes, worker} : {data: bytes});
}
/**
* The page (from 1) a destination leads to — a named one or an explicit array, whose page is
* a reference or, in some files, a plain index. 0 when it leads nowhere.
*/
async function _pdfDestPage(pdf, dest) {
try {
if (typeof dest === 'string') dest = await pdf.getDestination(dest);
if (!dest || dest[0] == null) return 0;
return (typeof dest[0] === 'number' ? dest[0] : await pdf.getPageIndex(dest[0])) + 1;
} catch (e) {
return 0;
}
}
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
// The table of contents: all entries resolved at once (one after the other cost a round trip
// to the worker each).
async function _parsePdfOutline(pdf, items, depth) {
depth = depth || 0;
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const parts = await Promise.all(items.map(async item => {
let href = '';
if (item.dest) {
const n = await _pdfDestPage(pdf, item.dest);
if (n) href = `#pdf-page-${n}`;
}
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const kids = item.items && item.items.length ? await _parsePdfOutline(pdf, item.items, depth + 1) : [];
return [{label: item.title || '(untitled)', href, depth}].concat(kids);
}));
return [].concat.apply([], parts);
}
// The document being loaded (pdf.js loading task), so opening can be cancelled.
let _pdfLoadingTask = null;
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
// The open document's pages and how they are drawn (renderPdf; stopped on the next one).
let _pdfView = null;
// Backwards name: the reader stops "the lazy drawing" when a book closes.
let _pdfLazy = null;
// The open document's text, page by page (pdf.js text content): for the text layer, the
// search and the crop to the text box. Kept for the document, not for one layout of it.
let _pdfTexts = null;
function _pdfPageText(pdf, num) {
if (!_pdfTexts || _pdfTexts.pdf !== pdf) _pdfTexts = {pdf, pages: new Map()};
let p = _pdfTexts.pages.get(num);
if (!p) {
p = pdf.getPage(num).then(page => page.getTextContent()).catch(() => ({items: [], styles: {}}));
_pdfTexts.pages.set(num, p);
}
return p;
}
/**
* A page's text as one string, lower case, for searching — with where each item (each span of
* the text layer, in order) starts in it. Lines ending inside it get a space.
*/
function _pdfPageString(tc) {
if (tc._diora) return tc._diora;
let str = '';
const starts = [];
for (const item of tc.items) {
starts.push(str.length);
str += item.str || '';
if (item.hasEOL) str += ' ';
}
tc._diora = {str: str.toLowerCase(), starts};
return tc._diora;
}
// Title, table of contents and page labels: asked for once per document, alongside the
// pages (opening doesn't wait for them; a layout anew reuses them).
let _pdfExtras = null;
function _pdfDocExtras(pdf) {
if (_pdfExtras && _pdfExtras.pdf === pdf) return _pdfExtras;
const meta = pdf.getMetadata().then(m => ({
title: (m && m.info && m.info.Title || '').trim(),
author: (m && m.info && m.info.Author || '').trim(),
}), () => ({title: '', author: ''}));
const toc = pdf.getOutline().then(o => (o && o.length ? _parsePdfOutline(pdf, o) : []), () => []);
const labels = pdf.getPageLabels().then(l => {
// Labels that only say 1, 2, 3 … are no labels.
if (!l || !l.length || l.every((s, i) => !s || s === String(i + 1))) return null;
return l.map((s, i) => s || String(i + 1));
}, () => null);
_pdfExtras = {pdf, meta, toc, labels};
return _pdfExtras;
}
// The page labels of the open document (front matter "iv", appendix "A-3" …), null without.
let _pdfLabels = null;
/** Page [n] (from 1) as the book numbers it. */
function pdfPageLabel(n) {
return (_pdfLabels && _pdfLabels[n - 1]) || String(n);
}
/** The page meant by [value] typed in: a label of the book first, else a page number. 0: none. */
function _pdfPageFromInput(value) {
const v = String(value || '').trim();
if (!v) return 0;
if (_pdfLabels) {
let i = _pdfLabels.indexOf(v);
if (i < 0) i = _pdfLabels.findIndex(l => l.toLowerCase() === v.toLowerCase());
if (i >= 0) return i + 1;
}
return parseInt(v, 10) || 0;
}
/**
* The pages in the reader's colours (Leserfarben): {background, foreground} as #rrggbb, the
* reader's (Dunkel, Sepia …). null: as printed (also when the reader is black on white).
*/
function _pdfPageColors() {
if (!readerSettings.pdfPageColors || readerSettings.pdfInverted) return null;
const contentEl = $('reader-content');
if (!contentEl) return null;
let bg = null;
for (let el = contentEl; el && !bg; el = el.parentElement) bg = _pdfCssHex(getComputedStyle(el).backgroundColor);
const fg = _pdfCssHex(getComputedStyle(contentEl).color);
// On white the page is as printed anyway (Hell).
if (!bg || !fg || bg === '#ffffff') return null;
return {background: bg, foreground: fg};
}
/** "rgb(…)" / "rgba(…)" as #rrggbb; null for transparent. */
function _pdfCssHex(css) {
const m = /rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)(?:[,\s/]+([\d.]+))?/.exec(css || '');
if (!m || (m[4] !== undefined && parseFloat(m[4]) === 0)) return null;
return '#' + [m[1], m[2], m[3]].map(x => (+x).toString(16).padStart(2, '0')).join('');
}
// Whether the pages are drawn for Leserfarben (with their pictures laid over them, below):
// switching that draws them anew; another theme only changes the filter's colours.
function _pdfColorsKey() {
return _pdfPageColors() ? 'reader' : '';
}
/**
* Leserfarben: the page is drawn as printed and recoloured by an SVG filter on its canvas —
* its brightness from the reader's text colour (black) to its background (white). (pdf.js's
* own pageColors recolours the pictures too, and lets nothing be left out.) Kept up to date
* with the colours; true when it applies.
*/
function _pdfColorFilter() {
const c = _pdfPageColors();
if (!c) return false;
const ns = 'http://www.w3.org/2000/svg';
let svg = document.getElementById('pdf-reader-colors-svg');
if (!svg) {
svg = document.createElementNS(ns, 'svg');
svg.id = 'pdf-reader-colors-svg';
svg.setAttribute('width', '0');
svg.setAttribute('height', '0');
svg.setAttribute('aria-hidden', 'true');
svg.style.position = 'absolute';
svg.innerHTML = '<filter id="pdf-reader-colors" color-interpolation-filters="sRGB">' +
'<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 ' +
'0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>' +
'<feComponentTransfer><feFuncR type="table"/><feFuncG type="table"/><feFuncB type="table"/>' +
'</feComponentTransfer></filter>';
document.body.appendChild(svg);
}
const ch = (hex, i) => (parseInt(hex.substr(1 + i * 2, 2), 16) / 255).toFixed(3);
['R', 'G', 'B'].forEach((name, i) => {
svg.querySelector('feFunc' + name).setAttribute('tableValues', `${ch(c.foreground, i)} ${ch(c.background, i)}`);
});
return true;
}
/**
* Where page [page]'s pictures are, in CSS pixels of [viewport]: from its drawing operations
* (the unit square of each image under the transform at that point). Image masks are left
* out (they're drawn in a colour, like text).
*/
async function _pdfImageRects(page, viewport) {
const OPS = pdfjsLib.OPS;
let list;
try {
list = await page.getOperatorList({annotationMode: pdfjsLib.AnnotationMode.ENABLE_FORMS});
} catch (e) {
return [];
}
const mul = (m, n) => [m[0] * n[0] + m[2] * n[1], m[1] * n[0] + m[3] * n[1],
m[0] * n[2] + m[2] * n[3], m[1] * n[2] + m[3] * n[3],
m[0] * n[4] + m[2] * n[5] + m[4], m[1] * n[4] + m[3] * n[5] + m[5]];
let ctm = viewport.transform.slice();
const stack = [];
const rects = [];
for (let i = 0; i < list.fnArray.length; i++) {
const fn = list.fnArray[i], args = list.argsArray[i];
if (fn === OPS.save) stack.push(ctm);
else if (fn === OPS.restore) ctm = stack.pop() || ctm;
else if (fn === OPS.transform) ctm = mul(ctm, args);
else if (fn === OPS.paintFormXObjectBegin) {
stack.push(ctm);
if (Array.isArray(args[0]) && args[0].length === 6) ctm = mul(ctm, args[0]);
} else if (fn === OPS.paintFormXObjectEnd) ctm = stack.pop() || ctm;
else if (fn === OPS.paintImageXObject || fn === OPS.paintInlineImageXObject) {
const xs = [ctm[4], ctm[0] + ctm[4], ctm[2] + ctm[4], ctm[0] + ctm[2] + ctm[4]];
const ys = [ctm[5], ctm[1] + ctm[5], ctm[3] + ctm[5], ctm[1] + ctm[3] + ctm[5]];
const x = Math.max(0, Math.min.apply(null, xs)), y = Math.max(0, Math.min.apply(null, ys));
const w = Math.min(viewport.width, Math.max.apply(null, xs)) - x;
const h = Math.min(viewport.height, Math.max.apply(null, ys)) - y;
if (w > 2 && h > 2) rects.push({x, y, w, h});
}
}
return rects;
}
// A picture covering this much of the page is the page itself (a scan): recoloured with it.
const PDF_SCAN_SHARE = 0.5;
/** Leserfarben: page [e]'s pictures laid over it as printed (copied from its canvas). */
async function _pdfImageLayer(e, page, viewport, ratio, token) {
const rects = await _pdfImageRects(page, viewport);
if (e.token !== token) return;
const area = viewport.width * viewport.height;
const shown = rects.filter(r => r.w * r.h < area * PDF_SCAN_SHARE);
if (!shown.length) return;
const layer = document.createElement('div');
layer.className = 'pdf-image-layer';
for (const r of shown) {
const c = document.createElement('canvas');
const sx = Math.floor(r.x * ratio), sy = Math.floor(r.y * ratio);
const sw = Math.min(e.canvas.width - sx, Math.ceil(r.w * ratio)), sh = Math.min(e.canvas.height - sy, Math.ceil(r.h * ratio));
if (sw <= 0 || sh <= 0) continue;
c.width = sw;
c.height = sh;
c.getContext('2d').drawImage(e.canvas, sx, sy, sw, sh, 0, 0, sw, sh);
c.style.left = sx / ratio + 'px';
c.style.top = sy / ratio + 'px';
c.style.width = sw / ratio + 'px';
c.style.height = sh / ratio + 'px';
layer.appendChild(c);
}
e.inner.insertBefore(layer, e.canvas.nextSibling);
}
// Internal links followed, the pages they were followed from (the back button).
let _pdfBackStack = [];
/** The document is gone (another one, or the reader closed): what belonged to it goes too. */
function _pdfForgetDoc() {
_pdfTexts = null;
_pdfExtras = null;
_pdfLabels = null;
_pdfBackStack = [];
_pdfShowBack();
if (typeof _pdfSearchStop === 'function') _pdfSearchStop();
}
async function _pdfFollowDest(dest) {
const pdf = currentPdfDoc;
if (!pdf) return;
const n = await _pdfDestPage(pdf, dest);
if (!n || pdf !== currentPdfDoc) return;
const contentEl = $('reader-content');
const from = readerSettings.pdfPaginated || !contentEl ? pdfCurrentPage : _pdfTopPage(contentEl);
if (n === from) return;
_pdfBackStack.push(from);
pdfGoToPage(n);
_pdfShowBack();
}
/** The button back to where the last link was followed from (gone when there's none). */
function _pdfShowBack() {
let btn = document.getElementById('pdf-back-btn');
const from = _pdfBackStack[_pdfBackStack.length - 1];
if (!from) {
if (btn) btn.remove();
return;
}
if (!btn) {
const overlay = $('reader-overlay');
if (!overlay) return;
btn = document.createElement('button');
btn.id = 'pdf-back-btn';
btn.type = 'button';
btn.className = 'btn btn-sm pdf-back-btn';
btn.addEventListener('click', ev => {
ev.stopPropagation();
const page = _pdfBackStack.pop();
if (page) pdfGoToPage(page);
_pdfShowBack();
});
overlay.appendChild(btn);
}
btn.textContent = `← Zurück zu S. ${pdfPageLabel(from)}`;
}
/** A page's text layer and links taken off (another scale, or the page let go). */
function _pdfDropLayers(e) {
if (e.textTask) { try { e.textTask.cancel(); } catch (err) {} }
e.textTask = null;
e.textDivs = null;
e.textInfo = null;
e.marked = null;
e.inner.querySelectorAll('.pdf-text-layer, .pdf-link-layer, .pdf-image-layer').forEach(el => el.remove());
}
/**
* Over a drawn page: its text (transparent, to select and copy, and where the search marks
* go) and its links. After the pixels, so a turn isn't held up by them.
*/
async function _pdfLayers(e, page, viewport, token) {
const pdf = currentPdfDoc;
if (!pdf) return;
let annots = [];
try { annots = await page.getAnnotations({intent: 'display'}); } catch (err) {}
if (e.token !== token) return;
_pdfLinks(e, viewport, annots);
if (typeof pdfjsLib.renderTextLayer !== 'function') return;
const text = await _pdfPageText(pdf, e.num);
if (e.token !== token || !text.items.length) return;
const layer = document.createElement('div');
layer.className = 'pdf-text-layer';
layer.style.setProperty('--scale-factor', viewport.scale);
e.inner.appendChild(layer);
const divs = [];
const task = pdfjsLib.renderTextLayer({textContentSource: text, container: layer, viewport, textDivs: divs});
e.textTask = task;
try {
await task.promise;
} catch (err) {
return;
}
if (e.token !== token || e.textTask !== task) return;
e.textTask = null;
e.textDivs = divs;
e.textInfo = {items: text.items, string: _pdfPageString(text)};
if (typeof _pdfSearchMarkPage === 'function') _pdfSearchMarkPage(e);
}
// Links: ones into the document go there (and back, _pdfShowBack); web addresses open outside.
function _pdfLinks(e, viewport, annots) {
const links = annots.filter(a => a.subtype === 'Link' && (a.url || a.dest));
if (!links.length) return;
const layer = document.createElement('div');
layer.className = 'pdf-link-layer';
for (const a of links) {
const r = viewport.convertToViewportRectangle(a.rect);
const link = document.createElement('a');
link.className = 'pdf-link';
link.style.left = Math.min(r[0], r[2]) + 'px';
link.style.top = Math.min(r[1], r[3]) + 'px';
link.style.width = Math.abs(r[2] - r[0]) + 'px';
link.style.height = Math.abs(r[3] - r[1]) + 'px';
if (a.url) {
link.href = a.url;
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.title = a.url;
} else {
const dest = a.dest;
link.href = '#';
link.addEventListener('click', ev => {
ev.preventDefault();
ev.stopPropagation();
_pdfFollowDest(dest);
});
}
layer.appendChild(link);
}
e.inner.appendChild(layer);
}
function _pdfStopLazy() {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const v = _pdfView;
if (!v) return;
v.stopped = true;
if (v.observer) v.observer.disconnect();
if (v.resize) v.resize.disconnect();
clearTimeout(v.resizeTimer);
v.entries.forEach(e => { e.token++; if (e.task) { try { e.task.cancel(); } catch (err) {} } });
_pdfView = _pdfLazy = null;
}
/**
* Draw page [page] of entry [e] at [cssScale] (CSS pixels per PDF point). Cancels a drawing
* of the same canvas still under way (pdf.js refuses two at once) and waits for it to let go.
* Returns false when a newer drawing (or letting go) came first.
*/
function _pdfDraw(e, page, cssScale) {
// The same drawing asked for again while it's under way (the page shown, then laid out
// again as the bars go): that one, not a new one cancelling it.
if (e.drawing && e.drawing.scale === cssScale) return e.drawing.promise;
const drawing = {scale: cssScale, promise: null};
drawing.promise = _pdfDrawNow(e, page, cssScale).then(ok => {
if (e.drawing === drawing) e.drawing = null;
return ok;
}, err => {
if (e.drawing === drawing) e.drawing = null;
throw err;
});
e.drawing = drawing;
return drawing.promise;
}
async function _pdfDrawNow(e, page, cssScale) {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const token = ++e.token;
_pdfDropLayers(e);
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const prev = e.task;
if (prev) {
try { prev.cancel(); } catch (err) {}
try { await prev.promise; } catch (err) {}
if (e.token !== token) return false;
}
const viewport = page.getViewport({scale: cssScale});
let ratio = window.devicePixelRatio || 1;
const area = viewport.width * viewport.height;
if (area * ratio * ratio > PDF_MAX_PIXELS) ratio = Math.max(0.5, Math.sqrt(PDF_MAX_PIXELS / area));
e.canvas.width = Math.round(viewport.width * ratio);
e.canvas.height = Math.round(viewport.height * ratio);
e.canvas.style.width = viewport.width + 'px';
e.canvas.style.height = viewport.height + 'px';
const task = page.render({
canvasContext: e.canvas.getContext('2d'),
viewport,
transform: ratio !== 1 ? [ratio, 0, 0, ratio, 0, 0] : null,
});
e.task = task;
try {
await task.promise;
} catch (err) {
if (e.task === task) e.task = null;
if (err && err.name !== 'RenderingCancelledException') console.warn('PDF page', e.num, err);
return false;
}
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (e.task === task) e.task = null;
if (e.token !== token) return false;
e.drawn = cssScale;
const recolor = _pdfColorFilter();
e.canvas.classList.toggle('pdf-recolored', recolor);
if (recolor) _pdfImageLayer(e, page, viewport, ratio, token).catch(err => console.warn('PDF page pictures', e.num, err));
_pdfLayers(e, page, viewport, token).catch(err => console.warn('PDF page layers', e.num, err));
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
return true;
}
/** Let a page's pixels go (its place stays) and pdf.js forget what it parsed of it. */
function _pdfLetGo(e, pdf) {
e.token++;
e.drawing = null;
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (e.task) { try { e.task.cancel(); } catch (err) {} }
_pdfDropLayers(e);
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
e.drawn = 0;
e.canvas.width = 0;
e.canvas.height = 0;
if (pdf) pdf.getPage(e.num).then(p => { if (!e.task) p.cleanup(); }, () => {});
}
/** The page shown at the top of the scrolling view (binary search, the pages are in order). */
function _pdfTopPage(contentEl) {
const v = _pdfView;
if (!v || !v.entries.length) return pdfCurrentPage || 1;
const top = contentEl.getBoundingClientRect().top + 20;
let lo = 0, hi = v.entries.length - 1;
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (v.entries[mid].wrapper.getBoundingClientRect().bottom > top) hi = mid; else lo = mid + 1;
}
return v.entries[lo].num;
}
// Every page gets its place at once (sized like the first page until it is drawn), but only the
// pages near the view are drawn; ones far away are let go again. A book of 1000 scanned pages
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
// opens as fast as one of ten, and doesn't hold 1000 canvases. Page by page (pdfPaginated),
// 66-pdf-paginated.js shows and draws the pages itself.
async function renderPdf(arrayBuffer, contentEl, scaleOverride, pivotPage) {
const myGen = ++_pdfRenderGen;
_pdfStopLazy();
let pdf = currentPdfDoc;
if (!pdf) {
_pdfForgetDoc();
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
_pdfLoadingTask = _pdfLoad(new Uint8Array(arrayBuffer.slice(0)));
try {
pdf = await _pdfLoadingTask.promise;
} finally {
_pdfLoadingTask = null;
}
}
if (_pdfRenderGen !== myGen) return null;
currentPdfDoc = pdf;
// [pivotPage] may be given as what of the document it is (the place saved, a fraction).
if (typeof pivotPage === 'function') pivotPage = pivotPage(pdf.numPages);
const extras = _pdfDocExtras(pdf);
extras.labels.then(labels => {
if (pdf !== currentPdfDoc) return;
_pdfLabels = labels;
if (labels) _pdfSetCurrentPage(pdfCurrentPage);
});
contentEl.innerHTML = '';
const pdfVp = document.createElement('div');
pdfVp.id = 'pdf-viewport';
contentEl.appendChild(pdfVp);
const containerWidth = readerSettings.pdfSpread
? contentEl.clientWidth - 32
: Math.min(contentEl.clientWidth - 32, 900);
const pageWidth = readerSettings.pdfSpread ? (containerWidth - 8) / 2 : containerWidth;
const scaleFor = vp => (scaleOverride != null ? scaleOverride
: Math.max(0.5, pageWidth / vp.width) * (readerSettings.pdfZoom / 100));
// The first page's size stands for all until each is drawn (a scan's pages are alike).
const first = await pdf.getPage(1);
if (_pdfRenderGen !== myGen) return null;
const firstVp = first.getViewport({scale: scaleFor(first.getViewport({scale: 1}))});
const entries = [];
let spreadContainer = null;
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const wrapper = document.createElement('div');
wrapper.className = 'pdf-page-wrapper';
wrapper.id = `pdf-page-${pageNum}`;
const inner = document.createElement('div');
inner.className = 'pdf-page-inner';
const canvas = document.createElement('canvas');
canvas.className = 'pdf-page';
canvas.width = 0;
canvas.height = 0;
canvas.style.width = firstVp.width + 'px';
canvas.style.height = firstVp.height + 'px';
inner.appendChild(canvas);
wrapper.appendChild(inner);
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (!readerSettings.pdfSpread || readerSettings.pdfPaginated) {
pdfVp.appendChild(wrapper);
} else if (pageNum === 1) {
wrapper.classList.add('pdf-spread-cover');
pdfVp.appendChild(wrapper);
spreadContainer = null;
} else {
if (pageNum % 2 === 0) {
spreadContainer = document.createElement('div');
spreadContainer.className = 'pdf-spread-wrapper';
pdfVp.appendChild(spreadContainer);
}
if (spreadContainer) spreadContainer.appendChild(wrapper);
else pdfVp.appendChild(wrapper);
}
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
entries.push({num: pageNum, wrapper, inner, canvas, drawn: 0, near: false, task: null, token: 0});
}
pdfTotalPages = pdf.numPages;
const pivot = Math.max(1, Math.min(pdf.numPages, pivotPage || 1));
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
const view = {pdf, contentEl, entries, gen: myGen, stopped: false, observer: null, resize: null,
resizeTimer: 0, queue: new Set(), working: false, scaleFor, showToken: 0,
colorsKey: _pdfColorsKey()};
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
_pdfView = _pdfLazy = view;
const gone = () => view.stopped || _pdfRenderGen !== myGen;
async function draw(e) {
const page = await pdf.getPage(e.num);
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (gone() || !e.near || readerSettings.pdfPaginated) return;
await _pdfDraw(e, page, scaleFor(page.getViewport({scale: 1})));
}
// One page at a time, the nearest to the current one first.
async function work() {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (view.working) return;
view.working = true;
try {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
while (!gone() && view.queue.size && !readerSettings.pdfPaginated) {
const cur = _pdfTopPage(contentEl);
const next = Array.from(view.queue).sort((a, b) => Math.abs(a.num - cur) - Math.abs(b.num - cur))[0];
view.queue.delete(next);
if (next.near && !next.drawn) await draw(next);
}
} finally {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
view.working = false;
}
}
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
// Turning the device, a narrower window, the bars coming or going: laid out again at the same page.
let lastW = contentEl.clientWidth, lastH = contentEl.clientHeight;
if (typeof ResizeObserver === 'function') {
view.resize = new ResizeObserver(() => {
const w = contentEl.clientWidth, h = contentEl.clientHeight;
if (!w || !h || (w === lastW && h === lastH)) return;
const widthChanged = Math.abs(w - lastW) > 2;
lastW = w;
lastH = h;
clearTimeout(view.resizeTimer);
view.resizeTimer = setTimeout(() => {
if (gone()) return;
if (readerSettings.pdfPaginated) _pdfShowPage(pdfCurrentPage);
else if (widthChanged) reRenderPdf(_pdfTopPage(contentEl));
}, readerSettings.pdfPaginated ? 150 : 300);
});
view.resize.observe(contentEl);
}
if (readerSettings.pdfPaginated) {
// Page by page: the pivot page cropped to its text, at once (66-pdf-paginated.js).
pdfCurrentPage = pivot;
await _pdfShowPage(pivot);
if (gone()) return null;
return _pdfResult(pdf, extras);
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
}
if (pivot > 1) entries[pivot - 1].wrapper.scrollIntoView({block: 'start'});
pdfCurrentPage = pivot;
const byEl = new Map(entries.map(e => [e.wrapper, e]));
if (typeof IntersectionObserver === 'function') {
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
view.observer = new IntersectionObserver(items => {
if (readerSettings.pdfPaginated) return;
for (const it of items) {
const e = byEl.get(it.target);
if (!e) continue;
e.near = it.isIntersecting;
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
if (e.near) { if (!e.drawn) view.queue.add(e); }
else { view.queue.delete(e); if (e.drawn || e.task) _pdfLetGo(e, pdf); }
}
work();
}, {root: contentEl, rootMargin: '150% 0px'});
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
entries.forEach(e => view.observer.observe(e.wrapper));
} else {
// Very old WebViews: every page, from the pivot on (as before).
PDF-Reader: ein Zeichenweg, Seitenmodus neu, Seitensprung für beide Modi (WIP, ungetestet) - 58-pdf.js: _pdfDraw – eine Zeichnung je Canvas (die vorige wird abgebrochen und abgewartet), Pixeldichte, höchstens 16,7 MP; Seiten, die gehen, geben auch pdf.js' Seitendaten frei; ein gemeinsamer pdf.js-Worker (_pdfLoad) auch für Uploads, Seitenzahl, Hefte und Cover; Inhalts- verzeichnis parallel aufgelöst, auch mit Seitenindex als Ziel; ResizeObserver: Drehen, Breite, Leisten – neu an derselben Seite. - 66-pdf-paginated.js: Seitenmodus auf _pdfDraw, Zuschnitt in Bildschirmkoordinaten (gedrehte Seiten) und höchstens 2,5× der ganzen Seite (eine fast leere Seite wird nicht unendlich groß); nächste und vorige Seite vorgezeichnet; Tippen zum Blättern blendet die Leisten nicht mehr ein; Wischen nur deutlich seitwärts; Mausrad; Pinch setzt die Vorschau zurück; nach dem Blättern wird gespeichert und 'diora-pdf-turn' gemeldet (E-Ink-Refresh). - pdfGoToPage für beide Modi: Inhaltsverzeichnis, Seitenzahl im Kopf, Lesezeichen, Tasten (Seitenmodus jetzt auch Leertaste, Bild auf/ab). - Beim Scrollen wird die aktuelle Seite mitgeführt (Lesezeichen sagten immer „Seite 1“). - Schalter Scrollen | Seiten im PDF-Panel; Zoom-Regler zeichnet erst neu, wenn er ruht. - openBook hängt seine Listener über _readerListen an; closeReader nimmt sie ab (vorher sammelten sie sich bei jedem Öffnen an). Abbrechen und ein zweites Buch geben das alte Dokument frei; der entschlüsselte PDF-Puffer wird nicht mehr gehalten. - Invertieren dreht die Farbtöne zurück (Fotos keine Negative). - e2e/fixtures/make_test_pdf.py: Test-PDF mit gemischten Formaten, Outline, Links und einer fast leeren Seite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:28 +01:00
entries.forEach(e => { e.near = true; view.queue.add(e); });
work();
}
// The book shows as soon as the page it opens at is drawn.
const pe = entries[pivot - 1];
pe.near = true;
if (!pe.drawn) await draw(pe);
if (gone()) return null;
return _pdfResult(pdf, extras);
}
// What openBook needs of the document: the title (asked for while the page was drawn) and
// the table of contents to come (a long one resolves its pages after the book is shown).
async function _pdfResult(pdf, extras) {
const meta = await extras.meta;
return {title: meta.title, author: meta.author, toc: [], tocLater: extras.toc, numPages: pdf.numPages};
}