import fs from 'fs'; import path from 'path'; const today = '22 de junio de 2026'; const docs = [ { outPath: path.resolve('PRECONVERSION_OEPM_FARMAFINDER.pdf'), title: 'DOCUMENTO DE PRECONVERSION', subtitle: 'Hoja de preparacion para expediente de patente', pages: [ [ { type: 'cover' }, ], [ { type: 'heading', text: '1. Identificacion del expediente' }, { type: 'paragraph', text: 'Solicitante: Antoni Nuñez Romeu' }, { type: 'paragraph', text: 'NIF/NIE/DNI: 45858029P' }, { type: 'paragraph', text: 'Domicilio: C/ Font Vella 12, 1, Terrassa, Barcelona, 08221' }, { type: 'paragraph', text: 'Titulo de la invencion: Sistema y procedimiento implementados por ordenador para la identificacion de necesidades terapeuticas mediante escaneo de tarjeta sanitaria individual, contraste con stock farmacutico geolocalizado y generacion de acciones de suministro.' }, { type: 'heading', text: '2. Objeto de este documento' }, { type: 'paragraph', text: 'Este documento se emite como hoja interna de preparacion y control del expediente antes de su presentacion ante la Oficina Española de Patentes y Marcas (OEPM). Su finalidad es reunir en un unico soporte los datos de identificacion, la referencia del objeto tecnico y la declaracion de que la documentacion principal del expediente ha sido preparada para su presentacion.' }, { type: 'heading', text: '3. Contenido del expediente' }, { type: 'bullet', text: 'Solicitud de patente.' }, { type: 'bullet', text: 'Memoria descriptiva.' }, { type: 'bullet', text: 'Reivindicaciones.' }, { type: 'bullet', text: 'Resumen.' }, { type: 'bullet', text: 'Anexo de dibujos.' }, { type: 'bullet', text: 'Solicitud de reduccion de tasas para persona fisica.' }, { type: 'heading', text: '4. Declaracion' }, { type: 'paragraph', text: 'El solicitante declara que la documentacion aportada se corresponde con una unica invencion, que la descripcion y las reivindicaciones han sido redactadas de forma coherente y que el material adjunto se presenta a efectos de tramitacion ante la OEPM.' }, { type: 'heading', text: '5. Firma' }, { type: 'paragraph', text: `En Terrassa, a ${today}.` }, { type: 'paragraph', text: 'Fdo.: Antoni Nuñez Romeu' }, { type: 'paragraph', text: 'DNI/NIF: 45858029P' }, { type: 'paragraph', text: 'Documento de apoyo interno. Revisar con agente de propiedad industrial antes de presentar.' }, ], ], }, { outPath: path.resolve('REDUCCION_TASAS_OEPM_PERSONA_FISICA.pdf'), title: 'SOLICITUD DE REDUCCION DE TASAS', subtitle: 'Persona fisica', pages: [ [ { type: 'cover' }, ], [ { type: 'heading', text: '1. Datos del solicitante' }, { type: 'paragraph', text: 'Nombre y apellidos: Antoni Nuñez Romeu' }, { type: 'paragraph', text: 'DNI/NIF: 45858029P' }, { type: 'paragraph', text: 'Domicilio: C/ Font Vella 12, 1, Terrassa, Barcelona, 08221' }, { type: 'heading', text: '2. Expediente al que se refiere' }, { type: 'paragraph', text: 'Titulo de la invencion: Sistema y procedimiento implementados por ordenador para la identificacion de necesidades terapeuticas mediante escaneo de tarjeta sanitaria individual, contraste con stock farmacutico geolocalizado y generacion de acciones de suministro.' }, { type: 'heading', text: '3. Solicitud' }, { type: 'paragraph', text: 'Por medio del presente escrito, el solicitante, en su condicion de persona fisica, solicita que se le aplique la reduccion de tasas que resulte procedente conforme a la normativa vigente de la Oficina Española de Patentes y Marcas (OEPM) para personas fisicas.' }, { type: 'paragraph', text: 'A tal efecto, manifiesta que la solicitud de patente se presenta a su nombre como persona fisica y pide que la oficina tramite esta peticion de conformidad con los requisitos y porcentajes de reduccion establecidos en la normativa aplicable al momento de la presentacion.' }, { type: 'heading', text: '4. Declaracion responsable' }, { type: 'paragraph', text: 'El solicitante declara bajo su responsabilidad que los datos consignados en este documento son ciertos y que aportara, en su caso, la documentacion adicional que la OEPM pudiera requerir para acreditar la condicion de persona fisica y la procedencia de la reduccion solicitada.' }, { type: 'heading', text: '5. Firma' }, { type: 'paragraph', text: `En Terrassa, a ${today}.` }, { type: 'paragraph', text: 'Fdo.: Antoni Nuñez Romeu' }, { type: 'paragraph', text: 'DNI/NIF: 45858029P' }, { type: 'paragraph', text: 'Nota: revisar con agente o con la OEPM el formulario exacto y los requisitos vigentes antes de presentar.' }, ], ], }, ]; const PAGE_W = 595.28; const PAGE_H = 841.89; const MARGIN_L = 56; const MARGIN_R = 56; const MARGIN_T = 58; const MARGIN_B = 52; const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R; function esc(s) { return String(s).replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)'); } function wrapText(text, maxChars) { const words = String(text).split(/\s+/); const lines = []; let line = ''; for (const word of words) { const candidate = line ? `${line} ${word}` : word; if (candidate.length > maxChars && line) { lines.push(line); line = word; } else { line = candidate; } } if (line) lines.push(line); return lines; } function buildPdf({ title, subtitle, pages, outPath }) { const objects = ['']; const pagesObjNums = []; const contentObjNums = []; function addObject(bodyText) { objects.push(bodyText); return objects.length - 1; } const f1 = addObject('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding /WinAnsiEncoding >>'); const f2 = addObject('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Bold /Encoding /WinAnsiEncoding >>'); const f3 = addObject('<< /Type /Font /Subtype /Type1 /BaseFont /Times-Italic /Encoding /WinAnsiEncoding >>'); function renderPage(pageNo, items, isCover = false) { const ops = []; let y = PAGE_H - MARGIN_T; let numbering = 1; const write = (txt, x, size, font = 'F1') => { ops.push(`BT /${font} ${size} Tf ${x} ${y} Td (${esc(txt)}) Tj ET`); y -= size * 1.28; }; const drawLine = () => { y -= 4; ops.push(`0.2 w ${MARGIN_L} ${y} m ${PAGE_W - MARGIN_R} ${y} l S`); y -= 10; }; if (isCover) { write(title, MARGIN_L + 70, 18, 'F2'); y -= 6; write('Oficina Española de Patentes y Marcas (OEPM)', MARGIN_L + 55, 12, 'F1'); y -= 16; write(subtitle, MARGIN_L + 80, 12, 'F3'); y -= 12; ops.push(`0.8 w ${MARGIN_L} ${y - 66} ${CONTENT_W} 66 re S`); write('Solicitante: Antoni Nuñez Romeu', MARGIN_L + 10, 11, 'F1'); write('DNI/NIF: 45858029P', MARGIN_L + 10, 11, 'F1'); write('Domicilio: C/ Font Vella 12, 1, Terrassa, Barcelona, 08221', MARGIN_L + 10, 11, 'F1'); write('Inventor: Antoni Nuñez Romeu', MARGIN_L + 10, 11, 'F1'); y -= 8; write('Documento de apoyo para expediente de patente', MARGIN_L + 70, 10, 'F3'); return ops.join('\n'); } for (const item of items) { if (item.type === 'heading') { write(item.text, MARGIN_L, 13, 'F2'); drawLine(); continue; } if (item.type === 'paragraph') { const lines = wrapText(item.text, 82); for (const line of lines) write(line, MARGIN_L, 11.2, 'F1'); y -= 1; continue; } if (item.type === 'bullet') { const lines = wrapText(item.text, 78); let first = true; for (const line of lines) { write((first ? '• ' : ' ') + line, MARGIN_L + 8, 11.1, 'F1'); first = false; } continue; } if (item.type === 'number') { const lines = wrapText(item.text, 76); let first = true; for (const line of lines) { write((first ? `${numbering}. ` : ' ') + line, MARGIN_L + 8, 11.1, 'F1'); first = false; } numbering += 1; continue; } } write(`Pagina ${pageNo}`, PAGE_W - 100, 9, 'F1'); return ops.join('\n'); } const pageContents = pages.map((items, idx) => renderPage(idx + 1, items, idx === 0 && items.length === 1 && items[0].type === 'cover')); for (const content of pageContents) { const stream = `<< /Length ${Buffer.byteLength(content, 'latin1')} >>\nstream\n${content}\nendstream`; contentObjNums.push(addObject(stream)); } const pagesTree = addObject(''); for (let i = 0; i < pageContents.length; i++) { const pageObj = addObject(`<< /Type /Page /Parent ${pagesTree} 0 R /MediaBox [0 0 ${PAGE_W} ${PAGE_H}] /Resources << /Font << /F1 ${f1} 0 R /F2 ${f2} 0 R /F3 ${f3} 0 R >> >> /Contents ${contentObjNums[i]} 0 R >>`); pagesObjNums.push(pageObj); } objects[pagesTree] = `<< /Type /Pages /Kids [${pagesObjNums.map(n => `${n} 0 R`).join(' ')}] /Count ${pagesObjNums.length} >>`; const catalog = addObject(`<< /Type /Catalog /Pages ${pagesTree} 0 R >>`); let pdf = '%PDF-1.4\n'; const offsets = [0]; for (let i = 1; i < objects.length; i++) { 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++) { 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(outPath, Buffer.from(pdf, 'latin1')); } for (const doc of docs) { buildPdf(doc); console.log(`Wrote ${doc.outPath}`); }