zcatpdf/docs/PLAN_MAESTRO_ZPDF.md
reugenio 2996289953 feat: v0.2 - Complete text system (cell, multiCell, alignment)
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>
2025-12-08 19:46:30 +01:00

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:

  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)

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

Librerías de Referencia

Documentación fpdf2


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.