All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m51s
- 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
264 lines
5.5 KiB
TypeScript
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 + '...'
|
|
}
|