# PLAN MAESTRO: zpdf - La Mejor Librería PDF en Zig **Fecha inicio:** 2025-12-08 **Objetivo:** Crear la mejor librería PDF para Zig, basada en fpdf2 (Python) **Filosofía:** Sin prisa, hacerlo perfecto --- ## DECISIÓN ARQUITECTÓNICA ### Fuente Principal: fpdf2 (Python) **Repositorio:** https://github.com/py-pdf/fpdf2 **Documentación:** https://py-pdf.github.io/fpdf2/ **Por qué fpdf2:** 1. Arquitectura moderna y refinada (evolución de 20+ años de FPDF) 2. 1300+ tests = especificación ejecutable 3. UTF-8 nativo desde el diseño 4. Código Python limpio, fácil de traducir a Zig 5. Documentación exhaustiva 6. Features completos para documentos comerciales **Descartados:** - go-pdf/fpdf: Arrastra diseño antiguo del PHP original - UniPDF: Comercial, no podemos estudiar el código - lopdf (Rust): Muy bajo nivel, más para manipular que crear --- ## FASES DE IMPLEMENTACIÓN ### FASE 0: Estudio y Documentación (ACTUAL) - [ ] Clonar fpdf2 - [ ] Leer y analizar fpdf.py completo - [ ] Documentar arquitectura interna - [ ] Identificar clases y métodos principales - [ ] Mapear tipos Python → Zig - [ ] Documentar el "core mínimo" necesario ### FASE 1: Core PDF Engine **Objetivo:** Generar PDF válido mínimo Componentes: - [ ] PDFObject: Representación de objetos PDF (dict, array, stream, etc.) - [ ] Document: Contenedor principal - [ ] Page: Páginas individuales - [ ] ContentStream: Comandos de dibujo - [ ] Writer: Serialización a bytes PDF - [ ] CrossReference: Tabla xref correcta Entregable: PDF vacío válido que abre en cualquier lector ### FASE 2: Sistema de Texto **Objetivo:** Texto con fuentes Type1 y posicionamiento Componentes: - [ ] Font: Gestión de fuentes Type1 (14 estándar) - [ ] TextState: Estado actual (fuente, tamaño, color) - [ ] Cell(): Celda rectangular con texto - [ ] MultiCell(): Texto con saltos de línea automáticos - [ ] Write(): Texto fluido - [ ] Text(): Texto en posición absoluta - [ ] Alineación: left, center, right, justify Entregable: PDF con texto formateado, múltiples fuentes ### FASE 3: Sistema de Gráficos **Objetivo:** Líneas, formas, colores Componentes: - [ ] Color: RGB, grayscale, CMYK - [ ] Line(): Líneas - [ ] Rect(): Rectángulos (stroke, fill, both) - [ ] Circle/Ellipse(): Círculos y elipses - [ ] Polygon(): Polígonos - [ ] Bezier curves - [ ] SetLineWidth, SetLineCap, SetLineJoin - [ ] Transformaciones: translate, rotate, scale Entregable: PDF con gráficos vectoriales ### FASE 4: Sistema de Imágenes **Objetivo:** Embeber imágenes en PDF Componentes: - [ ] JPEG: Embebido directo (DCTDecode) - [ ] PNG: Con y sin alpha (FlateDecode) - [ ] Image(): Posicionar y escalar imágenes - [ ] Aspect ratio automático - [ ] Caché de imágenes (no duplicar) Entregable: PDF con imágenes embebidas ### FASE 5: Layout y Tablas **Objetivo:** Helpers de alto nivel para documentos Componentes: - [ ] Table: Helper para crear tablas - [ ] Columns: Sistema de columnas - [ ] SetMargins, SetAutoPageBreak - [ ] Header/Footer callbacks - [ ] Numeración de páginas - [ ] Word wrap inteligente Entregable: Facturas completas con tablas ### FASE 6: Features Avanzados **Objetivo:** Completar la librería Componentes: - [ ] Links internos y externos - [ ] Bookmarks/Outline - [ ] Metadata (título, autor, etc.) - [ ] Compresión streams (zlib) - [ ] UTF-8 con fuentes TrueType embebidas - [ ] Encriptación básica (opcional) Entregable: Librería completa nivel producción --- ## ARQUITECTURA ZPDF (Diseño Preliminar) ``` zpdf/ ├── src/ │ ├── root.zig # Exports públicos │ ├── document.zig # Document principal │ ├── page.zig # Página individual │ ├── objects.zig # Tipos PDF (dict, array, stream, etc.) │ ├── writer.zig # Serialización PDF │ ├── content_stream.zig # Comandos gráficos │ ├── fonts/ │ │ ├── font.zig # Interfaz Font │ │ ├── type1.zig # Fuentes Type1 estándar │ │ └── metrics.zig # Métricas de caracteres │ ├── graphics/ │ │ ├── color.zig # Colores RGB/CMYK/Gray │ │ ├── path.zig # Paths vectoriales │ │ └── transform.zig # Transformaciones │ ├── text/ │ │ ├── state.zig # Estado de texto │ │ ├── layout.zig # Word wrap, alineación │ │ └── cell.zig # Cell, MultiCell, Write │ ├── image/ │ │ ├── jpeg.zig # Parser/embebido JPEG │ │ └── png.zig # Parser/embebido PNG │ └── util/ │ ├── buffer.zig # Buffer de bytes │ └── encoding.zig # Encoding texto PDF ├── examples/ │ ├── hello.zig │ ├── invoice.zig │ ├── table.zig │ └── images.zig ├── tests/ │ └── ... (muchos tests) └── docs/ ├── PLAN_MAESTRO_ZPDF.md # Este archivo ├── ARQUITECTURA_FPDF2.md # Análisis de fpdf2 └── API.md # Documentación API ``` --- ## API OBJETIVO (Inspirada en fpdf2) ```zig const std = @import("std"); const pdf = @import("zpdf"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // Crear documento var doc = try pdf.Document.init(allocator, .{ .orientation = .portrait, .unit = .mm, .format = .a4, }); defer doc.deinit(); // Metadata doc.setTitle("Factura #001"); doc.setAuthor("ACME Corp"); // Nueva página var page = try doc.addPage(); // Márgenes page.setMargins(10, 10, 10); // Fuente try page.setFont(.helvetica_bold, 24); // Colores page.setTextColor(pdf.Color.rgb(0, 100, 200)); // Texto try page.cell(.{ .width = 0, // Hasta el margen .height = 10, .text = "FACTURA", .align = .right, }); // Salto de línea page.ln(10); // Línea page.setDrawColor(pdf.Color.gray); try page.line(10, page.getY(), 200, page.getY()); // Imagen try page.image("logo.png", .{ .x = 10, .y = 10, .width = 40, }); // Tabla try page.table(.{ .x = 10, .y = 100, .columns = &.{ .{ .header = "Descripción", .width = 80, .align = .left }, .{ .header = "Cant.", .width = 20, .align = .center }, .{ .header = "Precio", .width = 30, .align = .right }, .{ .header = "Total", .width = 30, .align = .right }, }, .rows = &.{ &.{ "Producto A", "2", "10.00", "20.00" }, &.{ "Producto B", "1", "25.00", "25.00" }, }, }); // Guardar try doc.save("factura.pdf"); } ``` --- ## MAPEO TIPOS PYTHON → ZIG | Python (fpdf2) | Zig (zpdf) | |----------------|------------| | `class FPDF` | `pub const Document = struct` | | `str` | `[]const u8` | | `float` | `f32` | | `int` | `i32` o `u32` | | `list` | `std.ArrayList` o slice | | `dict` | `std.StringHashMap` o struct | | `bytes` | `[]u8` | | `Optional[T]` | `?T` | | `Union[A, B]` | `union(enum)` | | Exception | `error` union | | `with open()` | `std.fs.File` | | `io.BytesIO` | `std.ArrayList(u8)` | --- ## COMANDOS ```bash # Zig ZIG=/mnt/cello2/arno/re/recode/zig/zig-0.15.2/zig-x86_64-linux-0.15.2/zig # Build $ZIG build # Tests $ZIG build test # Ejemplos $ZIG build hello $ZIG build invoice # Ver PDF generado evince hello.pdf ``` --- ## REFERENCIAS ### Especificación PDF - PDF 1.4: https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.4.pdf - PDF 1.7 (ISO 32000): https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf ### Librerías de Referencia - fpdf2 (Python): https://github.com/py-pdf/fpdf2 - go-pdf/fpdf (Go): https://codeberg.org/go-pdf/fpdf - FPDF original (PHP): https://github.com/Setasign/FPDF ### Documentación fpdf2 - Tutorial: https://py-pdf.github.io/fpdf2/Tutorial.html - API Reference: https://py-pdf.github.io/fpdf2/fpdf/ --- ## NOTAS DE DESARROLLO *Se irán añadiendo conforme avance el proyecto* ### 2025-12-08 - Inicio del proyecto - Decisión: usar fpdf2 como fuente principal - Razón: arquitectura más moderna, mejor documentación, más tests - Primer paso: clonar y estudiar fpdf2 --- ## SI ESTA CONVERSACIÓN SE CORTA 1. Leer este documento completo 2. Leer CLAUDE.md del proyecto 3. Continuar desde donde se quedó según las fases 4. El código de fpdf2 estará clonado en: `/mnt/cello2/arno/re/recode/zig/zpdf/reference/fpdf2/` 5. El análisis de arquitectura estará en: `docs/ARQUITECTURA_FPDF2.md` --- **Recuerda:** Sin prisa, lo importante es hacerlo perfecto.