// pdf-generator.js — Cuatro PDFs A4 en blanco/negro con jsPDF (UMD global). const { jsPDF } = window.jspdf; const PAGE_W = 210; // mm const PAGE_H = 297; // mm const MARGIN = 20; // mm const CONTENT_W = PAGE_W - MARGIN * 2; const FOOTER_LABEL = 'Tramoya · Angel E. Pariente'; export function pdfScript(json) { const doc = new jsPDF({ unit: 'mm', format: 'a4' }); const title = json.title_page?.title || 'Guión sin título'; drawHeader(doc, title, 'JSON estructurado'); doc.setFont('courier', 'normal'); doc.setFontSize(9); const text = JSON.stringify(json, null, 2); const lines = doc.splitTextToSize(text, CONTENT_W); let y = MARGIN + 14; const lineHeight = 3.6; const bottom = PAGE_H - MARGIN - 10; for (const line of lines) { if (y > bottom) { drawFooter(doc); doc.addPage(); drawHeader(doc, title, 'JSON estructurado'); doc.setFont('courier', 'normal'); doc.setFontSize(9); y = MARGIN + 14; } doc.text(line, MARGIN, y); y += lineHeight; } drawFooter(doc); return doc; } export function pdfSceneMap(sceneMap, title) { const doc = new jsPDF({ unit: 'mm', format: 'a4' }); drawHeader(doc, title, 'Mapa de escenas'); const cols = [ { key: 'number', label: '#', w: 12 }, { key: 'heading', label: 'Encabezado', w: 78 }, { key: 'characters', label: 'Personajes', w: 50 }, { key: 'page', label: 'Pág.', w: 20 } ]; let y = MARGIN + 14; const rowPadding = 2; // Cabeceras doc.setFont('helvetica', 'bold'); doc.setFontSize(10); let x = MARGIN; for (const c of cols) { doc.text(c.label, x, y); x += c.w; } y += 2; doc.setLineWidth(0.2); doc.line(MARGIN, y, MARGIN + CONTENT_W, y); y += 4; doc.setFont('helvetica', 'normal'); doc.setFontSize(9); for (const scene of sceneMap) { const headingLines = doc.splitTextToSize(scene.heading || '', cols[1].w - 2); const charsText = (scene.characters_present || []).join(', '); const charsLines = doc.splitTextToSize(charsText, cols[2].w - 2); const rowH = Math.max(headingLines.length, charsLines.length, 1) * 4 + rowPadding; if (y + rowH > PAGE_H - MARGIN - 10) { drawFooter(doc); doc.addPage(); drawHeader(doc, title, 'Mapa de escenas'); y = MARGIN + 14; doc.setFont('helvetica', 'bold'); doc.setFontSize(10); let xh = MARGIN; for (const c of cols) { doc.text(c.label, xh, y); xh += c.w; } y += 2; doc.line(MARGIN, y, MARGIN + CONTENT_W, y); y += 4; doc.setFont('helvetica', 'normal'); doc.setFontSize(9); } let xc = MARGIN; doc.text(String(scene.number), xc, y); xc += cols[0].w; doc.text(headingLines, xc, y); xc += cols[1].w; doc.text(charsLines, xc, y); xc += cols[2].w; doc.text(scene.start_page != null ? String(scene.start_page) : '—', xc, y); y += rowH; } drawFooter(doc); return doc; } export function pdfCharacters(report, title) { const doc = new jsPDF({ unit: 'mm', format: 'a4' }); drawHeader(doc, title, 'Personajes'); let y = MARGIN + 14; const bottom = PAGE_H - MARGIN - 10; for (const c of report) { const blockLines = []; blockLines.push({ text: c.name, bold: true, size: 13 }); blockLines.push({ text: `Diálogos: ${c.dialogue_count} · Palabras: ${c.total_words_spoken} · Escenas: ${c.scene_appearances.length}`, size: 10 }); blockLines.push({ text: `Aparece de la escena ${c.first_appearance_scene} a la ${c.last_appearance_scene}.`, size: 10 }); blockLines.push({ text: `Escenas: ${c.scene_appearances.join(', ')}`, size: 10 }); const co = Object.entries(c.scenes_with_other_characters || {}) .sort((a, b) => b[1] - a[1]) .slice(0, 8) .map(([n, k]) => `${n} (${k})`) .join(', '); if (co) { blockLines.push({ text: `Co-apariciones: ${co}`, size: 10 }); } const estimatedH = blockLines.reduce((acc, b) => acc + (b.size === 13 ? 7 : 5), 0) + 6; if (y + estimatedH > bottom) { drawFooter(doc); doc.addPage(); drawHeader(doc, title, 'Personajes'); y = MARGIN + 14; } for (const b of blockLines) { doc.setFont('helvetica', b.bold ? 'bold' : 'normal'); doc.setFontSize(b.size); const lines = doc.splitTextToSize(b.text, CONTENT_W); for (const line of lines) { if (y > bottom) { drawFooter(doc); doc.addPage(); drawHeader(doc, title, 'Personajes'); y = MARGIN + 14; doc.setFont('helvetica', b.bold ? 'bold' : 'normal'); doc.setFontSize(b.size); } doc.text(line, MARGIN, y); y += b.size === 13 ? 6 : 4.5; } } y += 4; doc.setLineWidth(0.1); doc.setDrawColor(220, 220, 220); doc.line(MARGIN, y, MARGIN + CONTENT_W, y); doc.setDrawColor(0, 0, 0); y += 4; } drawFooter(doc); return doc; } export function pdfScaleta(scaleta, title) { const doc = new jsPDF({ unit: 'mm', format: 'a4' }); drawHeader(doc, title, 'Escaleta'); let y = MARGIN + 14; const bottom = PAGE_H - MARGIN - 10; for (const item of scaleta) { const headingText = `${item.scene_number}. ${item.heading}`; const summaryText = item.beat_summary || ''; doc.setFont('helvetica', 'bold'); doc.setFontSize(11); const headLines = doc.splitTextToSize(headingText, CONTENT_W); doc.setFont('helvetica', 'normal'); doc.setFontSize(10); const sumLines = summaryText ? doc.splitTextToSize(summaryText, CONTENT_W) : []; const blockH = headLines.length * 5.5 + sumLines.length * 4.5 + 6; if (y + blockH > bottom) { drawFooter(doc); doc.addPage(); drawHeader(doc, title, 'Escaleta'); y = MARGIN + 14; } doc.setFont('helvetica', 'bold'); doc.setFontSize(11); for (const line of headLines) { doc.text(line, MARGIN, y); y += 5.5; } if (sumLines.length) { doc.setFont('helvetica', 'normal'); doc.setFontSize(10); for (const line of sumLines) { doc.text(line, MARGIN, y); y += 4.5; } } y += 4; } drawFooter(doc); return doc; } // --- helpers --------------------------------------------------------------- function drawHeader(doc, title, section) { doc.setFont('helvetica', 'bold'); doc.setFontSize(10); doc.text(truncate(title, 80), MARGIN, MARGIN); doc.setFont('helvetica', 'normal'); doc.setFontSize(9); doc.text(section, PAGE_W - MARGIN, MARGIN, { align: 'right' }); doc.setLineWidth(0.3); doc.line(MARGIN, MARGIN + 4, PAGE_W - MARGIN, MARGIN + 4); } function drawFooter(doc) { const pageNumber = doc.internal.getNumberOfPages(); doc.setFont('helvetica', 'normal'); doc.setFontSize(8); doc.setTextColor(85, 85, 85); doc.text(FOOTER_LABEL, MARGIN, PAGE_H - MARGIN + 6); doc.text(String(pageNumber), PAGE_W - MARGIN, PAGE_H - MARGIN + 6, { align: 'right' }); doc.setTextColor(0, 0, 0); } function truncate(s, n) { if (!s) return ''; return s.length > n ? s.slice(0, n - 1) + '…' : s; }