Files
FarmaFinder/scripts/generate_oepm_addenda.mjs
Antoni Nuñez Romeu 2c1b3cfca6
Build & Push Docker Images / test-backend (push) Successful in 24s
Build & Push Docker Images / test-frontend (push) Successful in 23s
Build & Push Docker Images / build-backend (push) Successful in 2m22s
Build & Push Docker Images / build-frontend (push) Successful in 57s
Documentos + Homepage rediseñada
2026-06-26 16:59:20 +02:00

218 lines
9.7 KiB
JavaScript

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}`);
}