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
218 lines
9.7 KiB
JavaScript
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}`);
|
|
}
|