Files
cataRio/nuxt4/app/utils/pdf/pdfHelpers.ts
josedario87 701d0c8fdb
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m51s
feat: Agregar exportación PDF del formulario de catación EVC-IH01
- Implementar módulo de generación PDF con jsPDF
- Crear composable usePdfExport para exportar muestras y sesiones
- Añadir botones de exportación PDF en header y por muestra
- Replicar layout exacto del formulario físico IHCAFE
- Soportar máximo 3 formularios por hoja carta
2025-11-24 17:20:49 -06:00

264 lines
5.5 KiB
TypeScript

/**
* Funciones auxiliares para dibujar elementos en el PDF
*/
import type { jsPDF } from 'jspdf'
import { PDF_CONFIG } from './pdfLayout'
/**
* Dibuja un checkbox (marcado o sin marcar)
*/
export function dibujarCheckbox(
doc: jsPDF,
x: number,
y: number,
checked: boolean = false,
size: number = PDF_CONFIG.checkboxSize
): void {
doc.setDrawColor(0)
doc.setLineWidth(PDF_CONFIG.lineWidth.normal)
doc.rect(x, y, size, size)
if (checked) {
// Dibujar X dentro del checkbox
const padding = 0.4
doc.line(x + padding, y + padding, x + size - padding, y + size - padding)
doc.line(x + size - padding, y + padding, x + padding, y + size - padding)
}
}
/**
* Dibuja un checkbox con label
*/
export function dibujarCheckboxConLabel(
doc: jsPDF,
x: number,
y: number,
label: string,
checked: boolean = false,
indent: boolean = false
): number {
const checkboxSize = PDF_CONFIG.checkboxSize
const xOffset = indent ? 2 : 0
dibujarCheckbox(doc, x + xOffset, y, checked, checkboxSize)
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.setFont('helvetica', 'normal')
doc.text(label, x + xOffset + checkboxSize + 1, y + checkboxSize - 0.5)
// Retorna el ancho total usado
return xOffset + checkboxSize + 1 + doc.getTextWidth(label)
}
/**
* Dibuja las 5 casillas de tazas con números
*/
export function dibujarCasillasTazas(
doc: jsPDF,
x: number,
y: number,
tazasSeleccionadas: number[],
label: string
): void {
const checkboxSize = PDF_CONFIG.checkboxSize
const spacing = checkboxSize + 1.5
// Label
doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'bold')
doc.text(label, x, y)
// Casillas 1-5
const startX = x
const startY = y + 2
for (let i = 1; i <= 5; i++) {
const checkX = startX + (i - 1) * spacing
const isSelected = tazasSeleccionadas.includes(i)
dibujarCheckbox(doc, checkX, startY, isSelected, checkboxSize)
// Número debajo de la casilla
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
doc.setFont('helvetica', 'normal')
doc.text(String(i), checkX + checkboxSize / 2 - 0.5, startY + checkboxSize + 2)
}
}
/**
* Dibuja un rectángulo con texto centrado
*/
export function dibujarCeldaTexto(
doc: jsPDF,
x: number,
y: number,
width: number,
height: number,
texto: string,
opciones: {
fontSize?: number
fontStyle?: 'normal' | 'bold'
align?: 'left' | 'center' | 'right'
border?: boolean
backgroundColor?: string
} = {}
): void {
const {
fontSize = PDF_CONFIG.fontSize.body,
fontStyle = 'normal',
align = 'center',
border = true,
backgroundColor,
} = opciones
// Fondo si se especifica
if (backgroundColor) {
doc.setFillColor(backgroundColor)
doc.rect(x, y, width, height, 'F')
}
// Borde
if (border) {
doc.setDrawColor(0)
doc.setLineWidth(PDF_CONFIG.lineWidth.thin)
doc.rect(x, y, width, height)
}
// Texto
doc.setFontSize(fontSize)
doc.setFont('helvetica', fontStyle)
let textX = x + 1
if (align === 'center') {
textX = x + width / 2
} else if (align === 'right') {
textX = x + width - 1
}
doc.text(texto, textX, y + height / 2 + fontSize / 4, {
align,
})
}
/**
* Dibuja una línea horizontal
*/
export function dibujarLineaHorizontal(
doc: jsPDF,
x1: number,
y: number,
x2: number,
grosor: number = PDF_CONFIG.lineWidth.normal
): void {
doc.setDrawColor(0)
doc.setLineWidth(grosor)
doc.line(x1, y, x2, y)
}
/**
* Dibuja una línea vertical
*/
export function dibujarLineaVertical(
doc: jsPDF,
x: number,
y1: number,
y2: number,
grosor: number = PDF_CONFIG.lineWidth.normal
): void {
doc.setDrawColor(0)
doc.setLineWidth(grosor)
doc.line(x, y1, x, y2)
}
/**
* Dibuja un campo de texto con label y línea para escribir
*/
export function dibujarCampoTexto(
doc: jsPDF,
x: number,
y: number,
label: string,
valor: string | null,
anchoLinea: number = 30
): void {
doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'bold')
doc.text(label, x, y)
const labelWidth = doc.getTextWidth(label)
// Línea para escribir
dibujarLineaHorizontal(doc, x + labelWidth + 1, y + 0.5, x + labelWidth + 1 + anchoLinea)
// Valor si existe
if (valor) {
doc.setFont('helvetica', 'normal')
doc.text(valor, x + labelWidth + 2, y)
}
}
/**
* Dibuja texto multilínea con word wrap
*/
export function dibujarTextoMultilinea(
doc: jsPDF,
x: number,
y: number,
texto: string,
maxWidth: number,
lineHeight: number = 3
): number {
doc.setFontSize(PDF_CONFIG.fontSize.small)
doc.setFont('helvetica', 'normal')
const lines = doc.splitTextToSize(texto, maxWidth)
let currentY = y
for (const line of lines) {
doc.text(line, x, currentY)
currentY += lineHeight
}
return currentY - y // Retorna la altura total usada
}
/**
* Dibuja un rectángulo con bordes
*/
export function dibujarRectangulo(
doc: jsPDF,
x: number,
y: number,
width: number,
height: number,
grosor: number = PDF_CONFIG.lineWidth.normal
): void {
doc.setDrawColor(0)
doc.setLineWidth(grosor)
doc.rect(x, y, width, height)
}
/**
* Trunca texto si excede el ancho máximo
*/
export function truncarTexto(
doc: jsPDF,
texto: string,
maxWidth: number,
fontSize: number
): string {
doc.setFontSize(fontSize)
if (doc.getTextWidth(texto) <= maxWidth) {
return texto
}
let truncado = texto
while (doc.getTextWidth(truncado + '...') > maxWidth && truncado.length > 0) {
truncado = truncado.slice(0, -1)
}
return truncado + '...'
}