class asciiTextFill extends Paged.Handler { constructor(chunker, polisher, caller) { super(chunker, polisher, caller); } afterRendered(pages) { const PAD = 4; /* ── 1. Mesurer charW et lineH avec une pre cachée ── */ const probe = document.createElement('pre'); probe.style.cssText = [ 'position:absolute', 'visibility:hidden', 'top:0', 'left:0', 'font-family:"Courier New",monospace', 'font-size:9pt', 'line-height:1.5', 'white-space:pre' ].join(';'); // 10 lignes de 100 tirets → mesure fiable probe.textContent = Array(10).fill('-'.repeat(100)).join('\n'); document.body.appendChild(probe); const pr = probe.getBoundingClientRect(); const charW = pr.width / 200; const lineH = pr.height / 20; document.body.removeChild(probe); /* ── 2. COLS = nb de caractères par ligne dans la zone imprimable ── */ const COLS = Math.floor(pages[0].width / charW); // const COLS = pages[0].width; const DASH = '-'.repeat(COLS); /* ── 3. makeBox centré dans COLS ── */ function makeBox(lines) { const maxLen = Math.max(...lines.map(l => l.length)); const innerW = maxLen + PAD * 2; const border = '|' + '-'.repeat(innerW) + '|'; const empty = ' '.repeat(innerW + 2); const rows = lines.map(l => ' '.repeat(PAD) + l + ' '.repeat(innerW - PAD - l.length) ); const offset = Math.max(0, Math.floor((COLS - (innerW + 2)) / 2)); const sp = ' '.repeat(offset); return [sp + border, sp + empty, ...rows.map(r => sp + r), sp + empty, sp + border].join('\n'); } /* ── 4. Titre : vider SEULEMENT #chapter-title ── */ const titleEl = document.getElementById('chapter-title'); const titleLines = Array.from(titleEl.querySelectorAll('h2, h3')) .map(n => n.textContent.trim()).filter(Boolean); if (titleLines.length) { const pre = document.createElement('pre'); pre.textContent = titleEl.innerHTML = ''; // vide le div pre.textContent = makeBox(titleLines); titleEl.appendChild(pre); } /* ── 5. Bio : vider SEULEMENT #biographie ── */ const bioEl = document.getElementById('biographie'); const bioText = Array.from(bioEl.querySelectorAll('p')) .map(p => p.textContent.trim()).join(' '); const bioInnerW = COLS - 50; // place pour les | | if (bioText) { // word-wrap manuel const wrapped = []; let cur = ''; bioText.split(/\s+/).forEach(w => { const test = cur ? cur + ' ' + w : w; if (test.length <= bioInnerW) { cur = test; } else { if (cur) wrapped.push(cur); cur = w; } }); if (cur) wrapped.push(cur); const border = '|' + '-'.repeat(bioInnerW) + '|'; // const bioRows = wrapped.map(l => '|' + l + ' '.repeat(bioInnerW - l.length) + '|'); const pre = document.createElement('pre'); // pre.textContent = [border, ...bioRows, border].join('\n'); // const bioRows = wrapped.map(l => '|' + l + ' '.repeat(bioInnerW - l.length) + '|'); pre.textContent = border + '\n\n' + wrapped + '\n\n' + border; bioEl.innerHTML = ''; bioEl.appendChild(pre); } /* ── 6. Calculer l'espace dispo et remplir les fills ── */ // Hauteur intérieure de la page (après padding CSS) const totalH = pages[0].height; // Hauteur réelle des blocs de contenu après génération const fixedH = titleEl.offsetHeight + bioEl.offsetHeight; // Espace à distribuer entre les 3 fills const spare = Math.max(0, totalH - fixedH); // Répartition : 1/3 haut, 1/3 milieu, 1/3 bas // (modifie ces ratios pour décaler le contenu verticalement) const ratios = [1/3, 1/3, 1/3]; const ids = ['fill-top', 'fill-mid', 'fill-bottom']; ids.forEach((id, i) => { const el = document.getElementById(id); const h = spare * ratios[i]; const n = Math.max(0, Math.floor(h / lineH)); el.textContent = Array(n).fill(DASH).join('\n'); }); } } Paged.registerHandlers(asciiTextFill);