'use strict'; async function _parsePdfOutline(pdf, items, depth) { depth = depth || 0; const result = []; for (const item of items) { let href = ''; if (item.dest) { try { const dest = typeof item.dest === 'string' ? await pdf.getDestination(item.dest) : item.dest; if (dest) { const pageIndex = await pdf.getPageIndex(dest[0]); href = `#pdf-page-${pageIndex + 1}`; } } catch (e) {} } result.push({label: item.title || '(untitled)', href, depth}); if (item.items && item.items.length) { result.push(...await _parsePdfOutline(pdf, item.items, depth + 1)); } } return result; } async function renderPdf(arrayBuffer, contentEl, scaleOverride, pivotPage) { const myGen = ++_pdfRenderGen; const pdf = currentPdfDoc || await pdfjsLib.getDocument({data: new Uint8Array(arrayBuffer.slice(0))}).promise; if (_pdfRenderGen !== myGen) return null; currentPdfDoc = pdf; let pdfTitle = '', pdfAuthor = ''; try { const meta = await pdf.getMetadata(); pdfTitle = meta.info?.Title?.trim() || ''; pdfAuthor = meta.info?.Author?.trim() || ''; } catch (e) {} let toc = []; try { const outline = await pdf.getOutline(); if (outline && outline.length) toc = await _parsePdfOutline(pdf, outline); } catch (e) {} 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 dpr = window.devicePixelRatio || 1; // Pass 1: build DOM structure and size all canvases (no pixel rendering yet) const pageEntries = []; let spreadContainer = null; for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) { if (_pdfRenderGen !== myGen) { contentEl.innerHTML = ''; return null; } const page = await pdf.getPage(pageNum); const naturalVp = page.getViewport({scale: 1}); const scale = scaleOverride != null ? scaleOverride : Math.max(0.5, pageWidth / naturalVp.width) * (readerSettings.pdfZoom / 100); const viewport = page.getViewport({scale}); 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 = Math.round(viewport.width * dpr); canvas.height = Math.round(viewport.height * dpr); canvas.style.width = viewport.width + 'px'; canvas.style.height = viewport.height + 'px'; inner.appendChild(canvas); wrapper.appendChild(inner); if (!readerSettings.pdfSpread) { 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); } pageEntries.push({page, canvas, viewport}); } pdfTotalPages = pdf.numPages; // Scroll to pivot page immediately — DOM and canvas sizes are set const pivot = Math.max(1, Math.min(pdf.numPages, pivotPage || 1)); const pivotEl = document.getElementById(`pdf-page-${pivot}`); if (pivotEl && pivot > 1) pivotEl.scrollIntoView({block: 'start'}); // Pass 2: render pixels from pivot outward (pivot→end, then pivot-1→start) const order = []; for (let i = pivot - 1; i < pdf.numPages; i++) order.push(i); for (let i = pivot - 2; i >= 0; i--) order.push(i); for (const idx of order) { if (_pdfRenderGen !== myGen) { contentEl.innerHTML = ''; return null; } const {page, canvas, viewport} = pageEntries[idx]; const ctx = canvas.getContext('2d'); ctx.scale(dpr, dpr); await page.render({canvasContext: ctx, viewport}).promise; } return {title: pdfTitle, author: pdfAuthor, toc, numPages: pdf.numPages}; }