Phase 1 - Refactoring: - Modular architecture: fonts/, graphics/, objects/, output/ - Fixed Zig 0.15 API changes (ArrayListUnmanaged) - Fixed memory issues in render() Phase 2 - Text System: - cell() with borders, fill, alignment - cellAdvanced() with position control - multiCell() with automatic word wrap - ln() for line breaks - getStringWidth() for text width calculation - Page margins (setMargins, setCellMargin) - Align enum (left, center, right) - Border packed struct New features: - New Pdf API (cleaner than legacy Document) - Document metadata (setTitle, setAuthor, setSubject) - Color: RGB, CMYK, Grayscale support - 52 unit tests passing - New example: text_demo.zig 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.7 KiB
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:
- Arquitectura moderna y refinada (evolución de 20+ años de FPDF)
- 1300+ tests = especificación ejecutable
- UTF-8 nativo desde el diseño
- Código Python limpio, fácil de traducir a Zig
- Documentación exhaustiva
- 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)
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
# 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
- Leer este documento completo
- Leer CLAUDE.md del proyecto
- Continuar desde donde se quedó según las fases
- El código de fpdf2 estará clonado en:
/mnt/cello2/arno/re/recode/zig/zpdf/reference/fpdf2/ - El análisis de arquitectura estará en:
docs/ARQUITECTURA_FPDF2.md
Recuerda: Sin prisa, lo importante es hacerlo perfecto.