Patentes internacionales + Forgot Pwd
Run Tests on Branches / Detect Changes (push) Successful in 19s
Run Tests on Branches / Backend Tests (push) Successful in 2m46s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Successful in 2m24s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped

This commit is contained in:
Antoni Nuñez Romeu
2026-08-18 11:01:54 +02:00
parent 87df61ab15
commit d68463548f
31 changed files with 4134 additions and 3 deletions
@@ -0,0 +1,198 @@
import fs from 'fs';
import path from 'path';
const outPath = path.resolve('Patentes/INTERNATIONAL_PRIOR_ART_SEARCH_FARMAFINDER_EN.pdf');
const sections = [
{ type: 'title', text: 'International Prior Art Search' },
{ type: 'subtitle', text: 'Project: FarmaFinder' },
{ type: 'spacer', lines: 1 },
{ type: 'paragraph', text: 'This document summarizes the international prior-art position for the technical concept described in the FarmaFinder patent materials.' },
{ type: 'heading', text: 'Technical scope' },
{ type: 'paragraph', text: 'The invention concerns a computer-implemented system and method for identifying a therapeutic need from the scan of an individual health card or equivalent patient-related identifier, associating that result with an authenticated user, comparing the inferred need with geolocated pharmacy inventory, and generating supply-related actions when the required product is not available.' },
{ type: 'heading', text: 'Main categories of prior art identified' },
{ type: 'number', text: 'Medicine search platforms that allow users to locate medicines by name or active ingredient.' },
{ type: 'number', text: 'Pharmacy locator systems that display nearby pharmacies and, in some cases, opening hours, distance, or approximate availability.' },
{ type: 'number', text: 'Inventory management systems used by pharmacies, distributors, or health-related retail networks.' },
{ type: 'number', text: 'Digital health applications that store user profiles, location data, reminders, or treatment-related information.' },
{ type: 'number', text: 'Reservation, referral, or notification workflows that allow a user or a pharmacy to react when a product is out of stock.' },
{ type: 'heading', text: 'Distinguishing technical features' },
{ type: 'bullet', text: 'secure capture of a health-card identifier or equivalent patient-related credential;' },
{ type: 'bullet', text: 'verification of the captured data and association with an authenticated user profile;' },
{ type: 'bullet', text: 'automatic derivation of a medicine reference or therapeutic need;' },
{ type: 'bullet', text: 'real-time comparison against pharmacy stock data from geolocated pharmacies;' },
{ type: 'bullet', text: 'ranking of pharmacies according to availability, distance, and opening status;' },
{ type: 'bullet', text: 'automatic generation of reserve, notification, order, or referral actions when stock is insufficient;' },
{ type: 'bullet', text: 'technical traceability of the event across the processing chain.' },
{ type: 'heading', text: 'Assessment' },
{ type: 'paragraph', text: 'The reviewed prior-art categories appear to disclose individual parts of the technical problem, but not the complete integrated architecture described above in a single system. In particular, the combination of patient-context capture through a health identifier, automated therapeutic inference, geolocated stock comparison, and machine-generated supply actions does not appear to be disclosed as a unified technical chain in the documents reviewed at the project level.' },
{ type: 'heading', text: 'Suggested search focus for the European search phase' },
{ type: 'bullet', text: 'patient identifier scanning;' },
{ type: 'bullet', text: 'health-card reading or optical capture of patient credentials;' },
{ type: 'bullet', text: 'medicine search and pharmacy availability systems;' },
{ type: 'bullet', text: 'pharmacy reservation and referral workflows;' },
{ type: 'bullet', text: 'location-based ranking of pharmacies;' },
{ type: 'bullet', text: 'automated inventory-driven notification or order generation;' },
{ type: 'bullet', text: 'health-related digital assistants that combine user context with stock availability.' },
{ type: 'heading', text: 'Working conclusion' },
{ type: 'paragraph', text: 'On the basis of the current project documentation, the invention is presented as a computer-implemented technical solution that integrates identification, verification, geolocation, inventory contrast, and automated supply actions in one workflow. No single reference has been identified at the project level that clearly discloses the full combination of these features.' },
{ type: 'heading', text: 'Date' },
{ type: 'paragraph', text: '13 August 2026' },
];
const PAGE_W = 595.28;
const PAGE_H = 841.89;
const MARGIN_L = 56;
const MARGIN_R = 56;
const MARGIN_T = 54;
const MARGIN_B = 50;
const CONTENT_W = PAGE_W - MARGIN_L - MARGIN_R;
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, size) {
const avg = size * 0.48;
const max = Math.max(25, Math.floor(CONTENT_W / avg));
const words = String(text).split(/\s+/);
const lines = [];
let line = '';
for (const word of words) {
const candidate = line ? `${line} ${word}` : word;
if (candidate.length > max && line) {
lines.push(line);
line = word;
} else {
line = candidate;
}
}
if (line) lines.push(line);
return lines;
}
function buildPdf() {
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 contentNums = [];
const pageNums = [];
const pageContent = (pageLines, pageNo) => {
const ops = [];
let y = PAGE_H - MARGIN_T;
let numberCounter = 1;
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 pageLines) {
if (item.type === 'title') {
const lines = wrapText(item.text, 18);
for (const line of lines) write(line, MARGIN_L + 82, 18, 'F2');
y -= 4;
continue;
}
if (item.type === 'subtitle') {
const lines = wrapText(item.text, 12);
for (const line of lines) write(line, MARGIN_L + 100, 12, 'F3');
y -= 8;
continue;
}
if (item.type === 'spacer') {
y -= (item.lines || 1) * 12;
continue;
}
if (item.type === 'heading') {
if (!ensure(24)) break;
write(item.text, MARGIN_L, 13, 'F2');
y -= 4;
ops.push(`0.3 w ${MARGIN_L} ${y} m ${PAGE_W - MARGIN_R} ${y} l S`);
y -= 8;
continue;
}
if (item.type === 'paragraph') {
const lines = wrapText(item.text, 11.5);
if (!ensure(lines.length * 14 + 6)) break;
for (const line of lines) write(line, MARGIN_L, 11.5, 'F1');
y -= 2;
continue;
}
if (item.type === 'bullet') {
const lines = wrapText(item.text, 11.2);
if (!ensure(lines.length * 14 + 6)) break;
let first = true;
for (const line of lines) {
write((first ? '- ' : ' ') + line, MARGIN_L + 8, 11.2, 'F1');
first = false;
}
continue;
}
if (item.type === 'number') {
const lines = wrapText(item.text, 11.2);
if (!ensure(lines.length * 14 + 6)) break;
let first = true;
for (const line of lines) {
write((first ? `${numberCounter}. ` : ' ') + line, MARGIN_L + 8, 11.2, 'F1');
first = false;
}
numberCounter += 1;
continue;
}
}
ops.push(`BT /F1 9 Tf ${PAGE_W - 100} ${24} Td (Page ${pageNo}) Tj ET`);
return ops.join('\n');
};
const page1 = sections.slice(0, 12);
const page2 = sections.slice(12);
const pages = [page1, page2];
for (let i = 0; i < pages.length; i += 1) {
const content = pageContent(pages[i], i + 1);
contentNums.push(add(`<< /Length ${Buffer.byteLength(content, 'latin1')} >>\nstream\n${content}\nendstream`));
}
for (let i = 0; i < pages.length; i += 1) {
pageNums.push(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 ${contentNums[i]} 0 R >>`));
}
objects[pagesTree] = `<< /Type /Pages /Kids [${pageNums.map(n => `${n} 0 R`).join(' ')}] /Count ${pageNums.length} >>`;
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(outPath, Buffer.from(pdf, 'latin1'));
}
buildPdf();
console.log(`Wrote ${outPath}`);
+150
View File
@@ -0,0 +1,150 @@
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.');