import { join } from 'node:path' import { readFileSync, existsSync } from 'node:fs' // Polyfills nécessaires pour pdfjs-dist en Node.js pur (pas de DOM) // On n'a besoin que du parsing, pas du rendu if (typeof globalThis.DOMMatrix === 'undefined') { // @ts-expect-error polyfill minimal pour pdfjs globalThis.DOMMatrix = class DOMMatrix { constructor() { return Object.assign(this, { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }) } isIdentity = true translate() { return new DOMMatrix() } scale() { return new DOMMatrix() } inverse() { return new DOMMatrix() } multiply() { return new DOMMatrix() } } } if (typeof globalThis.Path2D === 'undefined') { // @ts-expect-error polyfill stub globalThis.Path2D = class Path2D { constructor() {} } } if (typeof globalThis.ImageData === 'undefined') { // @ts-expect-error polyfill stub globalThis.ImageData = class ImageData { constructor(w: number, h: number) { this.width = w; this.height = h; this.data = new Uint8ClampedArray(w * h * 4) } } } export default defineEventHandler(async () => { const config = await readYaml('bookplayer.config.yml') const pdfFile = config?.book?.pdfFile || '/pdf/une-economie-du-don.pdf' // Résolution du chemin PDF : dev (public/) et prod (.output/public/) const cwd = process.cwd() const candidates = [ join(cwd, 'public', pdfFile), join(cwd, '.output', 'public', pdfFile), ] const pdfPath = candidates.find(p => existsSync(p)) if (!pdfPath) { console.warn('[pdf-outline] PDF non trouvé. cwd:', cwd, 'candidats:', candidates) return [] } let data: Uint8Array try { data = new Uint8Array(readFileSync(pdfPath)) } catch (err) { console.warn('[pdf-outline] Erreur lecture PDF:', err) return [] } const pdfjsLib = await import('pdfjs-dist/legacy/build/pdf.mjs') let doc try { doc = await pdfjsLib.getDocument({ data, useSystemFonts: true }).promise } catch (err) { console.warn('[pdf-outline] Erreur getDocument:', err) return [] } const outline = await doc.getOutline() if (!outline || outline.length === 0) { doc.destroy() return [] } const entries: Array<{ title: string; page: number; level: number }> = [] async function extract(items: any[], level: number) { for (const item of items) { let page: number | null = null try { let dest = item.dest if (typeof dest === 'string') dest = await doc.getDestination(dest) if (dest) page = (await doc.getPageIndex(dest[0])) + 1 } catch {} if (page !== null) entries.push({ title: item.title, page, level }) if (item.items?.length) await extract(item.items, level + 1) } } await extract(outline, 0) doc.destroy() return entries })