import fs from 'fs'; import os from 'os'; import path from 'path'; import { execFileSync } from 'child_process'; const root = process.cwd(); const sourcePdf = path.join(root, 'Patentes', 'SOLICITUD_PATENTE_OEPM_FARMAFINDER.pdf'); const outDir = path.join(root, 'Patentes'); function ensureExists(file) { if (!fs.existsSync(file)) { throw new Error(`Missing file: ${file}`); } } function run(cmd, args) { execFileSync(cmd, args, { stdio: 'inherit' }); } function mergePages(output, pages) { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'farmafinder-clean-')); run('pdfseparate', [sourcePdf, path.join(tmp, 'page-%d.pdf')]); const inputs = pages.map((n) => path.join(tmp, `page-${n}.pdf`)); run('pdfunite', [...inputs, output]); } function esc(s) { return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); } function latin1(s) { return Buffer.from(s, 'latin1').toString('latin1'); } function wrapText(text, widthChars) { const words = String(text).split(/\s+/); const lines = []; let line = ''; for (const word of words) { const candidate = line ? `${line} ${word}` : word; if (candidate.length > widthChars && line) { lines.push(line); line = word; } else { line = candidate; } } if (line) lines.push(line); return lines; } function writeDrawingsPdf(output) { const PAGE_W = 595.28; const PAGE_H = 841.89; const MARGIN_L = 56; const MARGIN_R = 56; const MARGIN_T = 56; const MARGIN_B = 54; const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R; const lines = [ { type: 'title', text: 'Dibujos' }, { type: 'subtitle', text: 'Bloque limpio para la presentación de la solicitud' }, { type: 'spacer', lines: 1 }, { type: 'heading', text: 'Breve descripción de los dibujos' }, { type: 'paragraph', text: 'Figura 1. Diagrama de bloques general del sistema.' }, { type: 'paragraph', text: 'Figura 2. Flujo de captura y validación del escaneo de la TSI.' }, { type: 'paragraph', text: 'Figura 3. Flujo de contraste entre necesidad terapéutica y stock de farmacias.' }, { type: 'paragraph', text: 'Figura 4. Flujo de acción cuando no existe stock disponible.' }, { type: 'spacer', lines: 1 }, { type: 'heading', text: 'Nota' }, { type: 'paragraph', text: 'El documento fuente contiene referencias esquemáticas a las figuras, pero no incluye láminas técnicas dibujadas de forma separada. Si la Oficina requiere placas gráficas, habrá que añadirlas antes de la presentación definitiva.' }, ]; const objects = ['']; const add = (body) => { objects.push(body); return objects.length - 1; }; const fontReg = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>'); const fontBold = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>'); const fontIt = add('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>'); const pagesTree = add(''); const ops = []; let y = PAGE_H - MARGIN_T; const write = (txt, x, size, font = 'F1') => { ops.push(`BT /${font} ${size} Tf ${x} ${y} Td (${esc(latin1(txt))}) Tj ET`); y -= size * 1.25; }; const ensure = (needed) => y - needed > MARGIN_B; for (const item of lines) { if (item.type === 'title') { for (const line of wrapText(item.text, 26)) write(line, MARGIN_L + 175, 18, 'F2'); continue; } if (item.type === 'subtitle') { for (const line of wrapText(item.text, 40)) write(line, MARGIN_L + 45, 11.5, 'F3'); y -= 6; continue; } if (item.type === 'spacer') { y -= (item.lines || 1) * 12; continue; } if (item.type === 'heading') { if (!ensure(20)) break; write(item.text, MARGIN_L, 13, 'F2'); y -= 2; ops.push(`0.3 w ${MARGIN_L} ${y} m ${PAGE_W - MARGIN_R} ${y} l S`); y -= 8; continue; } if (item.type === 'paragraph') { const wrapped = wrapText(item.text, 80); if (!ensure(wrapped.length * 14 + 6)) break; for (const line of wrapped) write(line, MARGIN_L, 11.5, 'F1'); y -= 2; } } ops.push(`BT /F1 9 Tf ${PAGE_W - 100} ${24} Td (Page 1) Tj ET`); const content = ops.join('\n'); const contentObj = add(`<< /Length ${Buffer.byteLength(content, 'latin1')} >>\nstream\n${content}\nendstream`); const pageObj = add(`<< /Type /Page /Parent ${pagesTree} 0 R /MediaBox [0 0 ${PAGE_W} ${PAGE_H}] /Resources << /Font << /F1 ${fontReg} 0 R /F2 ${fontBold} 0 R /F3 ${fontIt} 0 R >> >> /Contents ${contentObj} 0 R >>`); objects[pagesTree] = `<< /Type /Pages /Kids [${pageObj} 0 R] /Count 1 >>`; const catalog = add(`<< /Type /Catalog /Pages ${pagesTree} 0 R >>`); let pdf = '%PDF-1.4\n'; const offsets = [0]; for (let i = 1; i < objects.length; i += 1) { offsets[i] = Buffer.byteLength(pdf, 'latin1'); pdf += `${i} 0 obj\n${objects[i]}\nendobj\n`; } const xref = Buffer.byteLength(pdf, 'latin1'); pdf += `xref\n0 ${objects.length}\n`; pdf += '0000000000 65535 f \n'; for (let i = 1; i < objects.length; i += 1) { pdf += `${String(offsets[i]).padStart(10, '0')} 00000 n \n`; } pdf += `trailer << /Size ${objects.length} /Root ${catalog} 0 R >>\nstartxref\n${xref}\n%%EOF\n`; fs.writeFileSync(output, Buffer.from(pdf, 'latin1')); } ensureExists(sourcePdf); mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_descripcion_limpia.pdf'), [2, 3, 4]); mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_reivindicaciones_limpia.pdf'), [5]); mergePages(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_resumen_limpia.pdf'), [6]); writeDrawingsPdf(path.join(outDir, 'SOLICITUD_PATENTE_OEPM_FARMAFINDER_dibujos_limpia.pdf')); console.log('Clean section PDFs generated.');