Files
administracionHospital/historia_clinica_generator.html

754 lines
30 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<title>Generador HC Ingreso Santojanni</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
<style>
body { font-family: Arial, sans-serif; background: #f0f4f8; display: flex;
flex-direction: column; align-items: center; padding: 40px; }
h1 { font-size: 1.2rem; color: #1a3a5c; margin-bottom: 8px; }
p { color: #555; font-size: .9rem; margin-bottom: 24px; }
button { background: #1a3a5c; color: #fff; border: none; padding: 14px 36px;
font-size: 1rem; border-radius: 6px; cursor: pointer; }
button:hover { background: #25547a; }
#status { margin-top: 16px; font-size: .85rem; color: #444; }
</style>
</head>
<body>
<h1>Historia Clínica de Ingreso Hospital Donación F. Santojanni</h1>
<p>Hace clic en el botón para generar el PDF con las 5 páginas del formulario en blanco.</p>
<button onclick="generarPDF()">Generar PDF</button>
<div id="status"></div>
<script>
/* ============================================================
CONSTANTES GLOBALES
============================================================ */
const A4W = 595.28;
const A4H = 841.89;
const ML = 25; // margen izquierdo
const MR = 25; // margen derecho
const INNER = A4W - ML - MR; // ancho útil: 545.28
// Colores
const NEGRO = PDFLib.rgb(0, 0, 0);
const GRIS_HEAD = PDFLib.rgb(0.82, 0.82, 0.82); // fondo encabezados de sección
const GRIS_LABEL = PDFLib.rgb(0.93, 0.93, 0.93); // fondo celdas etiqueta
const BLANCO = PDFLib.rgb(1, 1, 1);
/* ============================================================
HELPERS
============================================================ */
/** Dibuja un rectángulo con borde negro (relleno opcional). */
function rect(page, x, y, w, h, { fill = null, borderWidth = 0.5 } = {}) {
if (fill) page.drawRectangle({ x, y, width: w, height: h, color: fill });
page.drawRectangle({ x, y, width: w, height: h,
borderColor: NEGRO, borderWidth, color: fill ?? PDFLib.rgb(1,1,1) });
}
/** Dibuja texto centrado horizontalmente en un rango x→x+w. */
function textCenter(page, txt, x, y, w, { font, size = 8, color = NEGRO } = {}) {
const tw = font.widthOfTextAtSize(txt, size);
page.drawText(txt, { x: x + (w - tw) / 2, y, size, font, color });
}
/** Caja de sección con fondo gris y título centrado en negrita. */
function sectionHeader(page, label, x, y, w, h = 14, { bold, regular } = {}) {
rect(page, x, y, w, h, { fill: GRIS_HEAD });
textCenter(page, label, x, y + 3, w, { font: bold, size: 8 });
return y - h; // retorna la Y del borde inferior de la caja
}
/** Celda con etiqueta izquierda (gris) y área de escritura (blanca). */
function labelCell(page, label, x, y, labelW, totalW, h, { regular, bold, fontSize = 7 } = {}) {
// fondo etiqueta
page.drawRectangle({ x, y, width: labelW, height: h, color: GRIS_LABEL });
page.drawRectangle({ x, y, width: labelW, height: h,
borderColor: NEGRO, borderWidth: 0.4, color: GRIS_LABEL });
// área escribible
page.drawRectangle({ x: x + labelW, y, width: totalW - labelW, height: h,
borderColor: NEGRO, borderWidth: 0.4, color: BLANCO });
page.drawText(label, { x: x + 2, y: y + 2.5, size: fontSize, font: regular, color: NEGRO });
}
/** Línea horizontal simple. */
function hLine(page, x, y, w) {
page.drawLine({ start: { x, y }, end: { x: x + w, y }, thickness: 0.4, color: NEGRO });
}
/** Línea vertical simple. */
function vLine(page, x, y1, y2) {
page.drawLine({ start: { x, y: y1 }, end: { x, y: y2 }, thickness: 0.4, color: NEGRO });
}
/* ============================================================
CABECERA COMÚN (páginas 1-5)
============================================================ */
function drawPageHeader(page, pageNum, bold, regular) {
const top = A4H - 18;
// Logo placeholder izquierdo (círculo con "BA")
page.drawCircle({ x: ML + 12, y: top, size: 11, color: PDFLib.rgb(0.2, 0.4, 0.7) });
page.drawText("BA", { x: ML + 6, y: top - 4, size: 7, font: bold, color: BLANCO });
// Texto central
const titulo1 = "HOSPITAL DONACIÓN F. SANTOJANNI DIVISIÓN CLÍNICA MÉDICA";
const titulo2 = "HISTORIA CLÍNICA DE INGRESO";
const tw1 = bold.widthOfTextAtSize(titulo1, 9);
const tw2 = bold.widthOfTextAtSize(titulo2, 9);
page.drawText(titulo1, { x: (A4W - tw1) / 2, y: top + 4, size: 9, font: bold, color: NEGRO });
page.drawText(titulo2, { x: (A4W - tw2) / 2, y: top - 6, size: 9, font: bold, color: NEGRO });
// Logo placeholder derecho
page.drawCircle({ x: A4W - ML - 12, y: top, size: 11, color: PDFLib.rgb(0.2, 0.4, 0.7) });
page.drawText("H", { x: A4W - ML - 15, y: top - 4, size: 7, font: bold, color: BLANCO });
// Número de página
const pTxt = `${pageNum}`;
page.drawText(pTxt, { x: (A4W - regular.widthOfTextAtSize(pTxt, 8)) / 2,
y: 15, size: 8, font: regular, color: NEGRO });
// Línea separadora
hLine(page, ML, A4H - 30, INNER);
return A4H - 38; // Y de inicio del contenido
}
/* ============================================================
PÁGINA 1 Datos filiatorios / Motivo / Enfermedad actual
============================================================ */
async function buildPage1(pdfDoc, bold, regular) {
const page = pdfDoc.addPage([A4W, A4H]);
let y = drawPageHeader(page, 1, bold, regular);
/* --- DATOS FILIATORIOS --- */
y = sectionHeader(page, "DATOS FILIATORIOS", ML, y, INNER, 14, { bold, regular });
// Fila 1: fecha ingreso hospital | fecha ingreso clínica médica | habitación
const colW = [INNER * 0.30, INNER * 0.42, INNER * 0.28];
const rowH = 12;
let cx = ML;
["FECHA DE INGRESO AL HOSPITAL (DD/MM/AA)", "FECHA DE INGRESO A CLÍNICA MÉDICA (DD/MM/AA)", "HABITACIÓN"].forEach((lbl, i) => {
rect(page, cx, y - rowH, colW[i], rowH);
page.drawText(lbl, { x: cx + 2, y: y - rowH + 3.5, size: 5.5, font: regular, color: NEGRO });
cx += colW[i];
});
// Fecha clínica médica pre-cargada (16/04/2026)
const fechaTxt = "16/04/2026";
const mid = ML + colW[0];
page.drawText(fechaTxt, { x: mid + (colW[1] - bold.widthOfTextAtSize(fechaTxt, 9)) / 2,
y: y - rowH + 3, size: 9, font: bold, color: NEGRO });
y -= rowH;
// Fila 2: Apellido y nombre | DNI/CI
const mitad = INNER / 2;
labelCell(page, "APELLIDO y NOMBRE", ML, y - rowH, 45, mitad, rowH, { regular, bold });
labelCell(page, "DNI / C.I", ML + mitad, y - rowH, 30, mitad, rowH, { regular, bold });
y -= rowH;
// Fila 3
labelCell(page, "FECHA DE NACIMIENTO", ML, y - rowH, 50, mitad, rowH, { regular, bold });
labelCell(page, "HISTORIA CLINICA", ML + mitad, y - rowH, 42, mitad, rowH, { regular, bold });
y -= rowH;
// Fila 4
labelCell(page, "EDAD", ML, y - rowH, 22, mitad, rowH, { regular, bold });
labelCell(page, "OBRA SOCIAL", ML + mitad, y - rowH, 42, mitad, rowH, { regular, bold });
y -= rowH;
// Fila 5
labelCell(page, "NACIONALIDAD", ML, y - rowH, 42, mitad, rowH, { regular, bold });
// DERIVADO DE: con valor "Guardia Externa"
labelCell(page, "DERIVADO DE:", ML + mitad, y - rowH, 42, mitad, rowH, { regular, bold });
page.drawText("Guardia Externa", { x: ML + mitad + 44, y: y - rowH + 3,
size: 7, font: regular, color: NEGRO });
y -= rowH;
// Fila 6
labelCell(page, "DIRECCIÓN", ML, y - rowH, 30, mitad, rowH, { regular, bold });
labelCell(page, "APACHE / MORTALIDAD", ML + mitad, y - rowH, 55, mitad, rowH, { regular, bold });
y -= rowH;
// Fila 7: Teléfono de contacto (ancho completo)
labelCell(page, "Teléfono de contacto:", ML, y - rowH, 60, INNER, rowH, { regular, bold });
y -= rowH + 4;
/* --- MOTIVO DE CONSULTA --- */
y = sectionHeader(page, "MOTIVO DE CONSULTA", ML, y, INNER, 13, { bold, regular });
const motivoH = 24;
rect(page, ML, y - motivoH, INNER, motivoH);
y -= motivoH + 4;
/* --- ENFERMEDAD ACTUAL --- */
y = sectionHeader(page, "ENFERMEDAD ACTUAL", ML, y, INNER, 13, { bold, regular });
const eaH = 220;
rect(page, ML, y - eaH, INNER, eaH);
y -= eaH + 4;
/* --- ANTECEDENTES DE ENFERMEDAD ACTUAL --- */
y = sectionHeader(page, "ANTECEDENTES DE ENFERMEDAD ACTUAL", ML, y, INNER, 13, { bold, regular });
const antH = Math.max(y - 30, 40); // lo que quede en la página
rect(page, ML, y - antH, INNER, antH);
}
/* ============================================================
PÁGINA 2 Antecedentes personales / Medicación / Hábitos
============================================================ */
async function buildPage2(pdfDoc, bold, regular) {
const page = pdfDoc.addPage([A4W, A4H]);
let y = drawPageHeader(page, 2, bold, regular);
/* --- ANTECEDENTES PERSONALES --- */
y = sectionHeader(page, "ANTECEDENTES PERSONALES", ML, y, INNER, 13, { bold, regular });
const apSections = [
{ label: "CLÍNICOS", h: 38 },
{ label: "QUIRÚRGICOS /\nTRAUMATOLÓGICOS", h: 38 },
{ label: "GINECO-OBSTÉTRICOS", h: 38 },
{ label: "ALÉRGICOS /\nTRANSFUSIONALES", h: 38 },
];
const labelW = 80;
apSections.forEach(sec => {
rect(page, ML, y - sec.h, labelW, sec.h, { fill: GRIS_LABEL });
rect(page, ML + labelW, y - sec.h, INNER - labelW, sec.h);
// texto label multilinea centrado verticalmente
sec.label.split("\n").forEach((ln, i) => {
page.drawText(ln, { x: ML + 2, y: y - sec.h / 2 + (sec.label.includes("\n") ? (i === 0 ? 5 : -4) : -2),
size: 7, font: regular, color: NEGRO });
});
y -= sec.h;
});
y -= 4;
/* --- MEDICACIÓN HABITUAL --- */
y = sectionHeader(page, "MEDICACIÓN HABITUAL", ML, y, INNER, 13, { bold, regular });
const medH = 9;
const medColW = INNER / 2;
for (let i = 1; i <= 20; i++) {
const col = i <= 10 ? 0 : 1;
const row = i <= 10 ? i - 1 : i - 11;
const cx2 = ML + col * medColW;
const cy = y - row * medH - medH;
// número
rect(page, cx2, cy, 10, medH);
page.drawText(`${i}`, { x: cx2 + 2, y: cy + 2, size: 6.5, font: regular, color: NEGRO });
// área
rect(page, cx2 + 10, cy, medColW - 10, medH);
}
y -= 10 * medH + 4;
/* --- HÁBITOS + ANTECEDENTES HEREDOFAMILIARES (lado a lado) --- */
const halfW = INNER / 2 - 2;
const habX = ML;
const aheX = ML + halfW + 4;
// Hábitos
y = sectionHeader(page, "HÁBITOS", habX, y, halfW, 13, { bold, regular });
// tabla de hábitos
const habRows = [
["Apetito", "Conservado", "Sí"],
["Sueño", "Conservado", "Sí"],
["Catarsis", "Conservado", "Sí"],
["Diuresis", "Conservado", "Sí"],
];
let hy = y;
const hRowH = 9;
habRows.forEach(([item, val, check]) => {
const widths = [35, 40, 12, halfW - 87];
let hx = habX;
[item, val, check, "Aclare:"].forEach((txt, i) => {
rect(page, hx, hy - hRowH, widths[i], hRowH);
page.drawText(txt, { x: hx + 2, y: hy - hRowH + 2.5, size: 6, font: regular, color: NEGRO });
hx += widths[i];
});
hy -= hRowH;
});
hy -= 2;
// Tabaquismo / Alcoholismo / Drogas
const addictionRows = [
{ label: "Tabaquismo", check: "Sí", unit: "paq/y" },
{ label: "Alcoholismo", check: "No", unit: "gr/d" },
{ label: "Drogas\nilícitas", check: "No", unit: "Cuales" },
];
addictionRows.forEach(row => {
const aw = [40, 14, halfW - 54];
let ax = habX;
[row.label, row.check, row.unit + " Desde/Hasta"].forEach((txt, i) => {
rect(page, ax, hy - hRowH * 1.5, aw[i], hRowH * 1.5);
page.drawText(txt.split("\n")[0], { x: ax + 2, y: hy - hRowH * 1.5 + 6, size: 6, font: regular, color: NEGRO });
if (txt.includes("\n")) page.drawText(txt.split("\n")[1], { x: ax + 2, y: hy - hRowH * 1.5 + 1, size: 6, font: regular, color: NEGRO });
ax += aw[i];
});
hy -= hRowH * 1.5;
});
// Antecedentes heredofamiliares (misma Y de inicio)
let aheY = y + 13; // misma línea que "HÁBITOS" header
aheY = sectionHeader(page, "ANTECEDENTES HEREDOFAMILIARES", aheX, aheY, halfW, 13, { bold, regular });
const aheRows = [
{ label: "Madre", extra: ["Vive", "No", "Años:"] },
{ label: "Patología:" },
{ label: "Padre", extra: ["Vive", "No", "Años:"] },
{ label: "Patología:" },
{ label: "Hermanos", extra: ["N.º", "", "Años:"] },
{ label: "Patología:" },
{ label: "Hijos", extra: ["N.º", "", "Años:"] },
{ label: "Patología:" },
{ label: "Comentarios" },
];
const aRowH = 10;
aheRows.forEach(row => {
rect(page, aheX, aheY - aRowH, halfW, aRowH);
page.drawText(row.label, { x: aheX + 2, y: aheY - aRowH + 2.5, size: 6.5, font: regular, color: NEGRO });
if (row.extra) {
const ex = row.extra;
const ew = [(halfW - 50) / 3, (halfW - 50) / 3, (halfW - 50) / 3];
let ex2 = aheX + 50;
ex.forEach((txt, i) => {
rect(page, ex2, aheY - aRowH, ew[i], aRowH);
page.drawText(txt, { x: ex2 + 2, y: aheY - aRowH + 2.5, size: 6, font: regular, color: NEGRO });
ex2 += ew[i];
});
}
aheY -= aRowH;
});
}
/* ============================================================
PÁGINA 3 Contactos / Vacunas / Determinantes / Examen físico
============================================================ */
async function buildPage3(pdfDoc, bold, regular) {
const page = pdfDoc.addPage([A4W, A4H]);
let y = drawPageHeader(page, 3, bold, regular);
const halfW = INNER / 2 - 2;
/* --- CONTACTOS + VACUNACIÓN (lado a lado) --- */
// Headers a la misma Y
const yStart = y;
sectionHeader(page, "CONTACTOS", ML, yStart, halfW, 13, { bold, regular });
sectionHeader(page, "VACUNACIÓN/SEROLOGÍAS", ML + halfW + 4, yStart, halfW, 13, { bold, regular });
y -= 13;
// Contactos
const contactos = [
{ label: "Animales", val: "No", extra: "Cuáles?" },
{ label: "TBC", val: "No", extra: "Cuándo?" },
{ label: "Chagas", val: "No", extra: "Cómo?" },
{ label: "HIV", val: "No", extra: "Conducta\nriesgo?" },
{ label: "Otros", val: "No", extra: "Cuál/es?" },
];
let cy2 = y;
const cRowH = 16;
contactos.forEach(c => {
const cw = [40, 18, halfW - 58];
let cx2 = ML;
[c.label, c.val, c.extra].forEach((txt, i) => {
rect(page, cx2, cy2 - cRowH, cw[i], cRowH);
txt.split("\n").forEach((ln, li) => {
page.drawText(ln, { x: cx2 + 2, y: cy2 - cRowH + (cRowH / 2) + 1 - li * 7, size: 6.5, font: regular, color: NEGRO });
});
cx2 += cw[i];
});
cy2 -= cRowH;
});
// Vacunación
const vacX = ML + halfW + 4;
let vy = y;
// cabecera sub
const vacHeaders = ["Dosis", "Ultima", "Nombre comercial"];
const vacW = [halfW * 0.22, halfW * 0.22, halfW * 0.56];
// fila "Vacuna COVID" + sub-encabezados
const vacRowH = 10;
rect(page, vacX, vy - vacRowH, halfW * 0.3, vacRowH, { fill: GRIS_LABEL });
page.drawText("Vacuna COVID", { x: vacX + 2, y: vy - vacRowH + 2.5, size: 6.5, font: regular, color: NEGRO });
let vx = vacX + halfW * 0.3;
vacHeaders.forEach((h, i) => {
rect(page, vx, vy - vacRowH, vacW[i], vacRowH, { fill: GRIS_LABEL });
page.drawText(h, { x: vx + 2, y: vy - vacRowH + 2.5, size: 6, font: regular, color: NEGRO });
vx += vacW[i];
});
vy -= vacRowH;
// valor dosis 3
rect(page, vacX, vy - vacRowH, halfW * 0.3, vacRowH, { fill: GRIS_LABEL });
rect(page, vacX + halfW * 0.3, vy - vacRowH, vacW[0], vacRowH);
page.drawText("3", { x: vacX + halfW * 0.3 + 4, y: vy - vacRowH + 2.5, size: 7, font: bold, color: NEGRO });
rect(page, vacX + halfW * 0.3 + vacW[0], vy - vacRowH, vacW[1] + vacW[2], vacRowH);
vy -= vacRowH;
const vacRows = [
{ label: "Antigripal", check: "No" },
{ label: "Antineumocócica", check: "No" },
{ label: "Doble Adultos", check: "No" },
{ label: "Otras", check: "" },
];
const vacRH = 10;
vacRows.forEach(row => {
rect(page, vacX, vy - vacRH, halfW * 0.3, vacRH, { fill: GRIS_LABEL });
page.drawText(row.label, { x: vacX + 2, y: vy - vacRH + 2.5, size: 6.5, font: regular, color: NEGRO });
rect(page, vacX + halfW * 0.3, vy - vacRH, vacW[0], vacRH);
page.drawText(row.check, { x: vacX + halfW * 0.3 + 2, y: vy - vacRH + 2.5, size: 7, font: regular, color: NEGRO });
rect(page, vacX + halfW * 0.3 + vacW[0], vy - vacRH, vacW[1] + vacW[2], vacRH);
vy -= vacRH;
});
// Serologías
rect(page, vacX, vy - vacRH, halfW * 0.3, vacRH, { fill: GRIS_LABEL });
page.drawText("Serologías", { x: vacX + 2, y: vy - vacRH + 2.5, size: 6.5, font: regular, color: NEGRO });
rect(page, vacX + halfW * 0.3, vy - vacRH, halfW * 0.7, vacRH);
const resultTxt = "Resultado";
page.drawText(resultTxt, { x: vacX + halfW * 0.3 + (halfW * 0.7 - regular.widthOfTextAtSize(resultTxt, 7)) / 2, y: vy - vacRH + 2.5, size: 7, font: regular, color: NEGRO });
vy -= vacRH;
// 2 filas vacías de serologías
for (let i = 0; i < 2; i++) {
rect(page, vacX, vy - vacRH, halfW, vacRH);
vy -= vacRH;
}
y = Math.min(cy2, vy) - 4;
/* --- DETERMINANTES SOCIO-EPIDEMIOLÓGICOS --- */
y = sectionHeader(page, "DETERMINANTES SOCIO-EPIDEMIOLÓGICOS", ML, y, INNER, 13, { bold, regular });
const detRows = [
{ label: "LUGAR DE NACIMIENTO" },
{ label: "RESIDENCIA PASADA Y ACTUAL" },
{ label: "VIVIENDA", special: "vivienda" },
{ label: "NIVEL DE ESCOLARIDAD ALCANZADO" },
{ label: "OCUPACIÓN/OFICIO/PROFESIÓN" },
{ label: "VIAJES RECIENTES" },
];
const detH = 12;
const detLW = 90;
detRows.forEach(row => {
if (row.special === "vivienda") {
// dos sub-filas: Material/Sanitarios + Agua/Cloacas
rect(page, ML, y - detH, detLW, detH * 2);
page.drawText(row.label, { x: ML + 2, y: y - detH + 2.5, size: 7, font: regular, color: NEGRO });
// sub-fila 1
const subW = (INNER - detLW) / 4;
const subLabels1 = ["Material", "No", "Sanitarios completos", "No"];
let sx = ML + detLW;
subLabels1.forEach((lbl, i) => {
const fill = (i % 2 === 0) ? GRIS_LABEL : null;
rect(page, sx, y - detH, subW, detH, { fill: fill || BLANCO });
page.drawText(lbl, { x: sx + 2, y: y - detH + 3, size: 6.5, font: regular, color: NEGRO });
sx += subW;
});
y -= detH;
// sub-fila 2
const subLabels2 = ["Agua Potable", "No", "Cloacas", "No"];
sx = ML + detLW;
subLabels2.forEach((lbl, i) => {
const fill = (i % 2 === 0) ? GRIS_LABEL : null;
rect(page, sx, y - detH, subW, detH, { fill: fill || BLANCO });
page.drawText(lbl, { x: sx + 2, y: y - detH + 3, size: 6.5, font: regular, color: NEGRO });
sx += subW;
});
y -= detH;
} else {
rect(page, ML, y - detH, detLW, detH, { fill: GRIS_LABEL });
page.drawText(row.label, { x: ML + 2, y: y - detH + 3, size: 7, font: regular, color: NEGRO });
rect(page, ML + detLW, y - detH, INNER - detLW, detH);
y -= detH;
}
});
y -= 4;
/* --- EXAMEN FÍSICO --- */
y = sectionHeader(page, "EXAMEN FÍSICO", ML, y, INNER, 13, { bold, regular });
// Fila 1: TA / FC / FR / Temp / Sat / FiO2
const efRow1 = [
{ label: "TENSIÓN ARTERIAL", w: INNER * 0.18 },
{ label: "FRECUENCIA\nCARDÍACA", w: INNER * 0.14 },
{ label: "FRECUENCIA\nRESPIRATORIA", w: INNER * 0.14 },
{ label: "TEMPERATURA\nAXILAR", w: INNER * 0.14 },
{ label: "SATUROMETRÍA", w: INNER * 0.14 },
{ label: "FIO2(FRACCIÓN\nINSPIRADA DE OXIGENO)", w: INNER * 0.26 },
];
const efH1 = 22;
const efH1b = 12;
let ex = ML;
efRow1.forEach(col => {
rect(page, ex, y - efH1, col.w, efH1 / 2, { fill: GRIS_LABEL });
col.label.split("\n").forEach((ln, i) => {
page.drawText(ln, { x: ex + 2, y: y - efH1 / 2 + (col.label.includes("\n") ? (i === 0 ? 3 : -4) : -1),
size: 5.5, font: regular, color: NEGRO });
});
rect(page, ex, y - efH1, col.w, efH1);
ex += col.w;
});
y -= efH1 + 2;
// Fila 2: SNG / SV / AVP / AVC / Peso-Talla-IMC
const efRow2 = [
{ label: "SONDA\nNASOGÁSTRICA", val: "No", w: INNER * 0.14 },
{ label: "SONDA\nVESICAL", val: "Sonda Vesical",w: INNER * 0.16 },
{ label: "ACCESO VENOSO\nPERIFÉRICO", val: "Si - MSI", w: INNER * 0.16 },
{ label: "ACCESO VENOSO CENTRAL", val: "No", w: INNER * 0.27 },
{ label: "PESO / TALLA / IMC", val: "", w: INNER * 0.27 },
];
const ef2H = 18;
ex = ML;
efRow2.forEach(col => {
rect(page, ex, y - ef2H, col.w, ef2H / 2, { fill: GRIS_LABEL });
col.label.split("\n").forEach((ln, li) => {
page.drawText(ln, { x: ex + 2, y: y - ef2H / 2 + (col.label.includes("\n") ? (li === 0 ? 3 : -4) : -1),
size: 5.5, font: regular, color: NEGRO });
});
rect(page, ex, y - ef2H, col.w, ef2H);
page.drawText(col.val, { x: ex + 2, y: y - ef2H + 3, size: 6.5, font: regular, color: NEGRO });
ex += col.w;
});
y -= ef2H + 2;
// Sistemas
const sistemas = [
"SISTEMA\nNERVIOSO\nCENTRAL",
"CARDIOVASCULAR",
"RESPIRATORIO",
"DIGESTIVO",
"GENITO/URINARIO",
"SISTEMA OSTEO \nMUSCULO -\nARTICULAR",
];
const sysLW = 60;
const sysH = 28;
sistemas.forEach(sis => {
rect(page, ML, y - sysH, sysLW, sysH, { fill: GRIS_LABEL });
sis.split("\n").forEach((ln, i) => {
const lines = sis.split("\n").length;
const baseY = y - sysH + sysH / 2 - 3;
page.drawText(ln, { x: ML + 2, y: baseY + (lines - 1 - i) * 7, size: 6.5, font: regular, color: NEGRO });
});
rect(page, ML + sysLW, y - sysH, INNER - sysLW, sysH);
y -= sysH;
});
}
/* ============================================================
PÁGINA 4 Laboratorio / Bacteriología / ECG / Estudios
============================================================ */
async function buildPage4(pdfDoc, bold, regular) {
const page = pdfDoc.addPage([A4W, A4H]);
let y = drawPageHeader(page, 4, bold, regular);
/* --- LABORATORIO --- */
y = sectionHeader(page, "LABORATORIO", ML, y, INNER, 13, { bold, regular });
// Parámetros en columnas de 3 grupos (label + espacio)
const labParams = [
["HTO (%)", "GLUC (mg/dl)", "Na+ (mEq/L)", "BT (mg/dl)", "TP (%)"],
["HB (G/DL)", "UREA (mg/dl)", "K+ (mEq/L)", "BD (mg/dl)", "KPTT (seg)"],
["GB (X10⁹/L)", "CREAT (mg/dl)", "Cl- (mEq/L)", "GOT (UI/L)", "RIN"],
["PQ (X10⁹/L)", "", "P- (mEq/L)", "GPT (UI/L)", "PROT (g/dL)"],
["", "", "Mg+ (mEq/L)", "FAL (UI/L)", "ALB (g/dL)"],
];
const labCols = 5;
const labColW = INNER / labCols;
const labRowH = 11;
const labLabelW = labColW * 0.55;
labParams.forEach((row, ri) => {
let lx = ML;
row.forEach((param, ci) => {
if (param) {
rect(page, lx, y - labRowH, labLabelW, labRowH, { fill: GRIS_LABEL });
page.drawText(param, { x: lx + 2, y: y - labRowH + 3, size: 5.5, font: regular, color: NEGRO });
rect(page, lx + labLabelW, y - labRowH, labColW - labLabelW, labRowH);
}
lx += labColW;
});
y -= labRowH;
});
// EAB
y -= 2;
const eabLabels = ["EAB", "PH", "CO2", "O2", "HCO3", "EB", "SAT", "A.L", "FIO2"];
const eabW = [28, 30, 30, 30, 35, 30, 30, 30, 30];
const eabH = 11;
let eabX = ML;
eabLabels.forEach((lbl, i) => {
rect(page, eabX, y - eabH, eabW[i], eabH / 2, { fill: GRIS_LABEL });
page.drawText(lbl, { x: eabX + 2, y: y - eabH / 2 + 1, size: 6, font: bold, color: NEGRO });
rect(page, eabX, y - eabH, eabW[i], eabH);
eabX += eabW[i];
});
y -= eabH + 2;
// OTROS / FENA
rect(page, ML, y, INNER, 13, { fill: GRIS_LABEL });
page.drawText("OTROS (INCLUYE LÍQUIDOS)", { x: ML + 2, y: y + 4, size: 7, font: bold, color: NEGRO });
y -= 0;
rect(page, ML, y - 36, INNER, 36);
page.drawText("FENA", { x: ML + 2, y: y - 8, size: 7, font: regular, color: NEGRO });
y -= 36 + 13 + 4;
/* --- BACTERIOLOGÍA --- */
y = sectionHeader(page, "BACTERIOLOGÍA", ML, y, INNER, 13, { bold, regular });
const bactHeaders = ["Fecha", "Cultivo", "Resultado", "Sensibilidad"];
const bactW = [INNER * 0.18, INNER * 0.27, INNER * 0.27, INNER * 0.28];
const bactH = 10;
let bx = ML;
bactHeaders.forEach((h, i) => {
rect(page, bx, y - bactH, bactW[i], bactH, { fill: GRIS_LABEL });
textCenter(page, h, bx, y - bactH + 2, bactW[i], { font: bold, size: 7 });
bx += bactW[i];
});
y -= bactH;
for (let i = 0; i < 5; i++) {
bx = ML;
bactHeaders.forEach((_, j) => { rect(page, bx, y - bactH, bactW[j], bactH); bx += bactW[j]; });
y -= bactH;
}
y -= 4;
/* --- ECG --- */
y = sectionHeader(page, "ELECTROCARDIOGRAMA", ML, y, INNER, 13, { bold, regular });
// Fila con fecha + interpretación
const ecgH = 10;
const ecgLW = 28;
const ecgRW = INNER / 2;
const ecgParams = [
["FECHA", "", ecgRW],
["Ritmo", "PR"],
["FC", "Onda T"],
["Eje", "QT"],
["Onda P",""],
["QRS", ""],
];
// Primera fila: fecha + "INTERPRETACIÓN DIAGNÓSTICA:" (columna derecha grande)
rect(page, ML, y - ecgH, ecgLW, ecgH, { fill: GRIS_LABEL });
page.drawText("FECHA", { x: ML + 2, y: y - ecgH + 3, size: 7, font: bold, color: NEGRO });
rect(page, ML + ecgLW, y - ecgH, ecgRW - ecgLW, ecgH);
rect(page, ML + ecgRW, y - ecgH, INNER - ecgRW, ecgH, { fill: GRIS_LABEL });
page.drawText("INTERPRETACIÓN DIAGNÓSTICA:", { x: ML + ecgRW + 2, y: y - ecgH + 3, size: 7, font: bold, color: NEGRO });
y -= ecgH;
const ecgLeft = [["Ritmo"],["FC"],["Eje"],["Onda P"],["QRS"]];
const ecgRight = [["PR"],["Onda T"],["QT"],[""],[""]];
const ecgDataH = 10;
const lhW = INNER / 4;
// zona interpretación (rectángulo derecho que abarca todas las filas)
rect(page, ML + ecgRW, y - ecgDataH * 5, INNER - ecgRW, ecgDataH * 5);
ecgLeft.forEach((row, i) => {
rect(page, ML, y - ecgDataH, lhW * 0.5, ecgDataH, { fill: GRIS_LABEL });
page.drawText(row[0], { x: ML + 2, y: y - ecgDataH + 3, size: 7, font: regular, color: NEGRO });
rect(page, ML + lhW * 0.5, y - ecgDataH, lhW - lhW * 0.5, ecgDataH);
if (ecgRight[i][0]) {
rect(page, ML + lhW, y - ecgDataH, lhW * 0.5, ecgDataH, { fill: GRIS_LABEL });
page.drawText(ecgRight[i][0], { x: ML + lhW + 2, y: y - ecgDataH + 3, size: 7, font: regular, color: NEGRO });
rect(page, ML + lhW * 1.5, y - ecgDataH, lhW - lhW * 0.5, ecgDataH);
}
y -= ecgDataH;
});
y -= 4;
/* --- ESTUDIOS COMPLEMENTARIOS --- */
y = sectionHeader(page, "ESTUDIOS COMPLEMENTARIOS", ML, y, INNER, 13, { bold, regular });
const estHeaders = ["Fecha", "Estudio", "Descripción"];
const estW = [INNER * 0.14, INNER * 0.16, INNER * 0.70];
let estX = ML;
estHeaders.forEach((h, i) => {
rect(page, estX, y - ecgH, estW[i], ecgH, { fill: GRIS_LABEL });
page.drawText(h, { x: estX + 2, y: y - ecgH + 3, size: 7, font: bold, color: NEGRO });
estX += estW[i];
});
y -= ecgH;
const estRowH = 22;
for (let i = 0; i < 4; i++) {
estX = ML;
estHeaders.forEach((_, j) => { rect(page, estX, y - estRowH, estW[j], estRowH); estX += estW[j]; });
// Fecha marcadas: /03/ y /04/
if (i < 2) {
page.drawText(i === 0 ? "/03/." : "/04/.", { x: ML + 2, y: y - estRowH + 8, size: 10, font: bold, color: NEGRO });
}
y -= estRowH;
}
}
/* ============================================================
PÁGINA 5 Diagnóstico / Plan diagnóstico / Plan terapéutico
============================================================ */
async function buildPage5(pdfDoc, bold, regular) {
const page = pdfDoc.addPage([A4W, A4H]);
let y = drawPageHeader(page, 5, bold, regular);
/* --- IMPRESIÓN DIAGNÓSTICA POR SINDROME --- */
y = sectionHeader(page, "IMPRESIÓN DIAGNÓSTICA POR SINDROME", ML, y, INNER, 13, { bold, regular });
const idH = 200;
rect(page, ML, y - idH, INNER, idH);
y -= idH + 4;
/* --- PLAN DIAGNÓSTICO --- */
y = sectionHeader(page, "PLAN DIAGNÓSTICO", ML, y, INNER, 13, { bold, regular });
const pdH = 40;
rect(page, ML, y - pdH, INNER, pdH);
y -= pdH + 4;
/* --- DIAGNÓSTICO AL INGRESO --- */
y = sectionHeader(page, "DIAGNÓSTICO AL INGRESO", ML, y, INNER, 13, { bold, regular });
const diH = 40;
rect(page, ML, y - diH, INNER, diH);
y -= diH + 4;
/* --- PLAN TERAPÉUTICO --- */
y = sectionHeader(page, "PLAN TERAPÈUTICO", ML, y, INNER, 13, { bold, regular });
const ptH = 9;
const ptColW = INNER / 2;
for (let i = 1; i <= 20; i++) {
const col = i <= 10 ? 0 : 1;
const row = i <= 10 ? i - 1 : i - 11;
const ptX = ML + col * ptColW;
const ptY = y - row * ptH - ptH;
rect(page, ptX, ptY, 10, ptH);
page.drawText(`${i}`, { x: ptX + 2, y: ptY + 2, size: 6.5, font: regular, color: NEGRO });
rect(page, ptX + 10, ptY, ptColW - 10, ptH);
}
// Firma
const firmaY = y - 10 * ptH - 20;
page.drawText("......................................", {
x: A4W - ML - 120, y: firmaY, size: 8, font: regular, color: NEGRO
});
}
/* ============================================================
MAIN ensambla todo y descarga
============================================================ */
async function generarPDF() {
const status = document.getElementById("status");
status.textContent = "Generando PDF...";
try {
const pdfDoc = await PDFLib.PDFDocument.create();
const regular = await pdfDoc.embedFont(PDFLib.StandardFonts.Helvetica);
const bold = await pdfDoc.embedFont(PDFLib.StandardFonts.HelveticaBold);
const fonts = { regular, bold };
await buildPage1(pdfDoc, bold, regular);
await buildPage2(pdfDoc, bold, regular);
await buildPage3(pdfDoc, bold, regular);
await buildPage4(pdfDoc, bold, regular);
await buildPage5(pdfDoc, bold, regular);
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "HC_Ingreso_Santojanni.pdf";
a.click();
URL.revokeObjectURL(url);
status.textContent = "✅ PDF generado y descargado correctamente.";
} catch (e) {
console.error(e);
status.textContent = "❌ Error al generar el PDF. Ver consola para detalles.";
}
}
</script>
</body>
</html>