# zcatpdf - PDF Generation Library for Zig A pure Zig library for creating PDF documents with minimal dependencies. ```zig const zcatpdf = @import("zcatpdf"); pub fn main() !void { var pdf = zcatpdf.Pdf.init(allocator, .{}); defer pdf.deinit(); var page = try pdf.addPage(.{}); try page.setFont(.helvetica_bold, 24); try page.drawText(50, 750, "Hello, PDF!"); try pdf.save("hello.pdf"); } ``` ## Features - **Pure Zig** - Minimal dependencies (only libdeflate for compression) - **PDF 1.4** - Compatible with all PDF readers - **Complete text system** - Fonts, alignment, word wrap, cells - **Images** - JPEG and PNG (with alpha/transparency) - **Vector graphics** - Lines, curves, shapes, gradients - **Tables** - Helper for formatted tables - **Barcodes** - Code128 (1D) and QR Code (2D) - **Links** - Clickable URLs and internal page links - **Bookmarks** - Document outline/navigation - **Transformations** - Rotate, scale, translate, skew - **Transparency** - Fill and stroke opacity - **Templates** - Reusable document layouts - **Markdown** - Styled text with Markdown syntax ## Installation Add zcatpdf to your `build.zig.zon`: ```zig .dependencies = .{ .zcatpdf = .{ .url = "https://git.reugenio.com/reugenio/zcatpdf/archive/v1.0.tar.gz", .hash = "...", }, }, ``` In your `build.zig`: ```zig const zcatpdf_dep = b.dependency("zcatpdf", .{ .target = target, .optimize = optimize, }); exe.root_module.addImport("zcatpdf", zcatpdf_dep.module("zcatpdf")); ``` ## Quick Reference ### Table of Contents 1. [Document Creation](#1-document-creation) 2. [Pages](#2-pages) 3. [Text](#3-text) 4. [Fonts](#4-fonts) 5. [Colors](#5-colors) 6. [Graphics](#6-graphics) 7. [Images](#7-images) 8. [Tables](#8-tables) 9. [Links](#9-links) 10. [Bookmarks](#10-bookmarks) 11. [Barcodes](#11-barcodes) 12. [Transformations](#12-transformations) 13. [Transparency](#13-transparency) 14. [Gradients](#14-gradients) 15. [Templates](#15-templates) 16. [Markdown](#16-markdown) 17. [Compression](#17-compression) --- ## 1. Document Creation **File:** `src/pdf.zig` ```zig const zcatpdf = @import("zcatpdf"); // Create document with default settings var pdf = zcatpdf.Pdf.init(allocator, .{}); defer pdf.deinit(); // Create document with options var pdf = zcatpdf.Pdf.init(allocator, .{ .page_size = .a4, .orientation = .portrait, .unit = .pt, }); // Set metadata pdf.setTitle("Document Title"); pdf.setAuthor("Author Name"); pdf.setSubject("Subject"); pdf.setKeywords("keyword1, keyword2"); pdf.setCreator("Application Name"); // Save to file try pdf.save("output.pdf"); // Or get as bytes const data = try pdf.output(); defer allocator.free(data); ``` ### Types | Type | File | Description | |------|------|-------------| | `Pdf` | `src/pdf.zig` | Main document struct | | `Config` | `src/pdf.zig` | Document configuration | --- ## 2. Pages **File:** `src/page.zig`, `src/objects/base.zig` ```zig // Add page with document defaults var page = try pdf.addPage(.{}); // Add page with specific size var page = try pdf.addPage(.{ .size = .letter }); // Add page with custom dimensions (points) var page = try pdf.addPageCustom(612, 792); // Page sizes available .a3 // 842 x 1191 pt .a4 // 595 x 842 pt .a5 // 420 x 595 pt .letter // 612 x 792 pt .legal // 612 x 1008 pt // Orientations .portrait .landscape ``` ### Types | Type | File | Description | |------|------|-------------| | `Page` | `src/page.zig` | Page struct with drawing methods | | `PageSize` | `src/objects/base.zig` | Standard page sizes | | `Orientation` | `src/objects/base.zig` | Portrait/Landscape | --- ## 3. Text **File:** `src/page.zig` ```zig // Simple text at position try page.drawText(x, y, "Hello World"); // Cell with border and background try page.cell(width, height, "Text", Border.all, .center, true); // Multi-line text with word wrap try page.multiCell(width, line_height, long_text, Border.none, .left, false); // Get/set cursor position const x = page.getX(); const y = page.getY(); page.setXY(100, 500); // Line break page.ln(20); // Move down 20 points ``` ### Cell Parameters ```zig try page.cell( width, // f32 - Cell width height, // f32 - Cell height text, // []const u8 - Text content border, // Border - Border style align, // Align - Text alignment fill, // bool - Fill background ); ``` ### Border Options ```zig Border.none // No border Border.all // All sides Border.left // Left only Border.right // Right only Border.top // Top only Border.bottom // Bottom only // Combine with Border.combine() ``` ### Alignment ```zig .left .center .right ``` --- ## 4. Fonts **File:** `src/fonts/type1.zig`, `src/fonts/ttf.zig` ```zig // Set font try page.setFont(.helvetica, 12); try page.setFont(.helvetica_bold, 14); try page.setFont(.times_roman, 11); try page.setFont(.courier, 10); // Calculate string width const width = zcatpdf.Font.helvetica.stringWidth("Hello", 12.0); ``` ### Available Fonts (Type1 built-in) | Font | Constant | |------|----------| | Helvetica | `.helvetica` | | Helvetica Bold | `.helvetica_bold` | | Helvetica Oblique | `.helvetica_oblique` | | Helvetica Bold Oblique | `.helvetica_bold_oblique` | | Times Roman | `.times_roman` | | Times Bold | `.times_bold` | | Times Italic | `.times_italic` | | Times Bold Italic | `.times_bold_italic` | | Courier | `.courier` | | Courier Bold | `.courier_bold` | | Courier Oblique | `.courier_oblique` | | Courier Bold Oblique | `.courier_bold_oblique` | | Symbol | `.symbol` | | ZapfDingbats | `.zapf_dingbats` | ### TrueType Fonts (parsing only) ```zig // Load TTF file const font_data = try std.fs.cwd().readFileAlloc(allocator, "font.ttf", 10_000_000); var ttf = try zcatpdf.TrueTypeFont.parse(allocator, font_data); defer ttf.deinit(); // Get font info std.debug.print("Family: {s}\n", .{ttf.family_name}); std.debug.print("Ascender: {d}\n", .{ttf.ascender}); // Calculate string width const width = ttf.stringWidth("Hello", 12.0); ``` ### Types | Type | File | Description | |------|------|-------------| | `Font` | `src/fonts/type1.zig` | Type1 font enum | | `TrueTypeFont` | `src/fonts/ttf.zig` | TTF parser | --- ## 5. Colors **File:** `src/graphics/color.zig` ```zig // Set colors page.setFillColor(zcatpdf.Color.red); page.setStrokeColor(zcatpdf.Color.blue); // RGB (0-255) page.setFillColor(zcatpdf.Color.rgb(255, 128, 0)); // Hex page.setFillColor(zcatpdf.Color.hex(0xFF8000)); // Grayscale (0.0-1.0) page.setFillColor(zcatpdf.Color.gray(0.5)); // CMYK (0.0-1.0) page.setFillColor(zcatpdf.Color.cmyk(0, 1, 1, 0)); // Red ``` ### Predefined Colors ```zig Color.black Color.white Color.red Color.green Color.blue Color.yellow Color.cyan Color.magenta Color.orange Color.purple Color.pink Color.brown Color.gray Color.light_gray Color.dark_gray ``` ### Types | Type | File | Description | |------|------|-------------| | `Color` | `src/graphics/color.zig` | Color struct (RGB, CMYK, Gray) | --- ## 6. Graphics **File:** `src/page.zig`, `src/content_stream.zig` ### Lines ```zig try page.setLineWidth(2.0); try page.drawLine(x1, y1, x2, y2); ``` ### Rectangles ```zig // Stroke only try page.drawRect(x, y, width, height); // Fill only try page.fillRect(x, y, width, height); // Fill and stroke try page.fillAndStrokeRect(x, y, width, height); ``` ### Circles and Ellipses ```zig // Circle try page.drawCircle(cx, cy, radius); try page.fillCircle(cx, cy, radius); // Ellipse try page.drawEllipse(cx, cy, rx, ry); try page.fillEllipse(cx, cy, rx, ry); // Arc try page.drawArc(cx, cy, rx, ry, start_angle, end_angle); ``` ### Bezier Curves ```zig // Cubic Bezier try page.drawBezier(x0, y0, x1, y1, x2, y2, x3, y3); // Quadratic Bezier try page.drawQuadBezier(x0, y0, x1, y1, x2, y2); ``` ### Line Style ```zig try page.setLineWidth(2.0); try page.setLineCap(.round); // .butt, .round, .square try page.setLineJoin(.round); // .miter, .round, .bevel try page.setDashPattern(&.{5, 3}, 0); // [dash, gap], phase ``` ### Types | Type | File | Description | |------|------|-------------| | `ContentStream` | `src/content_stream.zig` | Low-level PDF operators | | `LineCap` | `src/content_stream.zig` | Line cap styles | | `LineJoin` | `src/content_stream.zig` | Line join styles | --- ## 7. Images **File:** `src/images/jpeg.zig`, `src/images/png.zig`, `src/images/image_info.zig` ```zig // Load JPEG const jpeg_idx = try pdf.addJpegImageFromFile("photo.jpg"); // Load PNG const png_idx = try pdf.addPngImageFromFile("logo.png"); // Load from memory const img_idx = try pdf.addJpegImage(jpeg_bytes); const img_idx = try pdf.addPngImage(png_bytes); // Get image info const info = pdf.getImage(img_idx).?; std.debug.print("Size: {d}x{d}\n", .{info.width, info.height}); // Draw image at position with size try page.image(img_idx, info, x, y, width, height); // Draw image preserving aspect ratio try page.imageFit(img_idx, info, x, y, max_width, max_height); ``` ### Supported Formats | Format | Features | |--------|----------| | JPEG | RGB, Grayscale | | PNG | RGB, RGBA (with alpha), Grayscale, Indexed | ### Types | Type | File | Description | |------|------|-------------| | `ImageInfo` | `src/images/image_info.zig` | Image metadata | | `ImageFormat` | `src/images/image_info.zig` | JPEG, PNG enum | --- ## 8. Tables **File:** `src/table.zig` ```zig const col_widths = [_]f32{ 200, 100, 100, 100 }; var table = zcatpdf.Table.init(page, .{ .x = 50, .y = 700, .col_widths = &col_widths, .row_height = 20, .header_bg_color = zcatpdf.Color.hex(0xE0E0E0), .border = true, }); // Header row try table.header(&.{ "Description", "Qty", "Price", "Total" }); // Data rows try table.row(&.{ "Product A", "2", "$10.00", "$20.00" }); try table.row(&.{ "Product B", "1", "$25.00", "$25.00" }); // Footer row try table.footer(&.{ "", "", "Total:", "$45.00" }); ``` ### Table Options ```zig TableOptions{ .x = 50, // X position .y = 700, // Y position .col_widths = &widths, // Column widths array .row_height = 20, // Row height .header_bg_color = Color.gray, // Header background .row_bg_color = null, // Row background .alt_row_bg_color = null, // Alternating row background .border = true, // Draw borders .header_font = .helvetica_bold, // Header font .body_font = .helvetica, // Body font .font_size = 10, // Font size } ``` ### Types | Type | File | Description | |------|------|-------------| | `Table` | `src/table.zig` | Table helper | | `TableOptions` | `src/table.zig` | Table configuration | --- ## 9. Links **File:** `src/links.zig`, `src/page.zig` ```zig // URL link with visual styling (blue + underline) _ = try page.urlLink(x, y, "Click here", "https://example.com"); // URL link from current position _ = try page.writeUrlLink("Visit website", "https://example.com"); // Internal link (jump to page) try page.addInternalLink(target_page, x, y, width, height); // URL link without visual (just annotation) try page.addUrlLink("https://example.com", x, y, width, height); ``` ### Types | Type | File | Description | |------|------|-------------| | `Link` | `src/links.zig` | Link struct | | `PageLinks` | `src/links.zig` | Page link collection | --- ## 10. Bookmarks **File:** `src/outline.zig` ```zig // Add bookmark to page try pdf.addBookmark("Chapter 1", 0); // page index 0 // Add bookmark with Y position try pdf.addBookmarkAt("Section 1.1", 0, 500); ``` Bookmarks appear in the PDF reader's sidebar for navigation. ### Types | Type | File | Description | |------|------|-------------| | `Outline` | `src/outline.zig` | Document outline | | `OutlineItem` | `src/outline.zig` | Single bookmark | --- ## 11. Barcodes **File:** `src/barcodes/code128.zig`, `src/barcodes/qr.zig` ### Code128 (1D) ```zig // Basic barcode try page.drawCode128(x, y, "ABC-12345", height, module_width); // Barcode with text below try page.drawCode128WithText(x, y, "ABC-12345", height, module_width, show_text); ``` ### QR Code (2D) ```zig try page.drawQRCode(x, y, "https://example.com", size, error_correction); // Error correction levels zcatpdf.QRCode.ErrorCorrection.L // 7% recovery zcatpdf.QRCode.ErrorCorrection.M // 15% recovery zcatpdf.QRCode.ErrorCorrection.Q // 25% recovery zcatpdf.QRCode.ErrorCorrection.H // 30% recovery ``` ### Types | Type | File | Description | |------|------|-------------| | `Code128` | `src/barcodes/code128.zig` | Code128 encoder | | `QRCode` | `src/barcodes/qr.zig` | QR Code encoder | --- ## 12. Transformations **File:** `src/page.zig` ```zig // Save state before transforming try page.saveState(); // Rotate around point (degrees) try page.rotate(45, center_x, center_y); // Scale from point try page.scale(2.0, 1.5, origin_x, origin_y); // Translate try page.translate(100, 50); // Skew (degrees) try page.skew(15, 0); // X skew try page.skew(0, 10); // Y skew // Custom transformation matrix try page.transform(a, b, c, d, e, f); // Restore state try page.restoreState(); ``` **Important:** Always use `saveState()`/`restoreState()` to limit transformation scope. --- ## 13. Transparency **File:** `src/graphics/extgstate.zig`, `src/page.zig` ```zig // Set fill opacity (0.0 = transparent, 1.0 = opaque) try page.setFillOpacity(0.5); // Set stroke opacity try page.setStrokeOpacity(0.75); // Set both at once try page.setOpacity(0.3); // Reset to fully opaque try page.setOpacity(1.0); ``` ### Types | Type | File | Description | |------|------|-------------| | `ExtGState` | `src/graphics/extgstate.zig` | Extended graphics state | --- ## 14. Gradients **File:** `src/graphics/gradient.zig`, `src/page.zig` ### Linear Gradients ```zig // Horizontal gradient try page.linearGradientRect(x, y, width, height, zcatpdf.Color.red, zcatpdf.Color.blue, .horizontal); // Vertical gradient try page.linearGradientRect(x, y, width, height, zcatpdf.Color.green, zcatpdf.Color.yellow, .vertical); // Diagonal gradient try page.linearGradientRect(x, y, width, height, zcatpdf.Color.purple, zcatpdf.Color.cyan, .diagonal); ``` ### Radial Gradients ```zig // Circle gradient (center to edge) try page.radialGradientCircle(cx, cy, radius, zcatpdf.Color.white, zcatpdf.Color.blue); // Ellipse gradient try page.radialGradientEllipse(cx, cy, rx, ry, zcatpdf.Color.yellow, zcatpdf.Color.red); ``` ### Types | Type | File | Description | |------|------|-------------| | `Gradient` | `src/graphics/gradient.zig` | Gradient definitions | | `LinearGradient` | `src/graphics/gradient.zig` | Linear gradient | | `RadialGradient` | `src/graphics/gradient.zig` | Radial gradient | | `GradientDirection` | `src/page.zig` | horizontal, vertical, diagonal | --- ## 15. Templates **File:** `src/template/template.zig` Templates define reusable document layouts with named regions. ```zig // Use predefined invoice template var tmpl = try zcatpdf.Template.invoiceTemplate(allocator); defer tmpl.deinit(); // Or create custom template var tmpl = zcatpdf.Template.init(allocator, "custom", 595, 842); defer tmpl.deinit(); try tmpl.defineRegion("header", .{ .x = 50, .y = 750, .width = 495, .height = 80, .region_type = .text, }); // Use regions to position content if (tmpl.getRegion("header")) |region| { try page.drawText(region.x, region.y, "Header Content"); } ``` ### Predefined Templates - `Template.invoiceTemplate()` - Invoice with header, customer, items, totals, footer - `Template.letterTemplate()` - Letter with sender, recipient, date, subject, body, signature ### Types | Type | File | Description | |------|------|-------------| | `Template` | `src/template/template.zig` | Template struct | | `TemplateRegion` | `src/template/template.zig` | Region definition | | `RegionType` | `src/template/template.zig` | text, image, table, custom | | `FixedContent` | `src/template/template.zig` | Repeating content | --- ## 16. Markdown **File:** `src/markdown/markdown.zig` Parse Markdown-style text and render with appropriate styles. ```zig var renderer = zcatpdf.MarkdownRenderer.init(allocator); defer renderer.deinit(); try renderer.parse( \\# Heading 1 \\ \\This is **bold** and *italic* text. \\ \\- Bullet item \\- Another item \\ \\[Link text](https://example.com) ); // Render to PDF for (renderer.getLines()) |line| { for (line.spans) |span| { const font = zcatpdf.MarkdownRenderer.fontForStyle(span.style); try page.setFont(font, span.font_size orelse 12); if (span.color) |color| { page.setFillColor(zcatpdf.Color.hex(color)); } try page.drawText(x, y, span.text); x += font.stringWidth(span.text, font_size); } y -= line_height; } ``` ### Supported Syntax | Syntax | Result | |--------|--------| | `**text**` or `__text__` | **Bold** | | `*text*` or `_text_` | *Italic* | | `***text***` | ***Bold + Italic*** | | `~~text~~` | ~~Strikethrough~~ | | `[text](url)` | Link | | `# Heading` | Heading 1 | | `## Heading` | Heading 2 | | `### Heading` | Heading 3 | | `- item` | Bullet list | | `1. item` | Numbered list | ### Types | Type | File | Description | |------|------|-------------| | `MarkdownRenderer` | `src/markdown/markdown.zig` | Parser/renderer | | `TextSpan` | `src/markdown/markdown.zig` | Styled text span | | `SpanStyle` | `src/markdown/markdown.zig` | Style flags | --- ## 17. Compression **File:** `src/compression/zlib.zig`, `src/output/producer.zig` ```zig // Configure compression when creating document var pdf = zcatpdf.Pdf.init(allocator, .{ .compression = .{ .enabled = true, .level = 6, // 0-12, higher = better compression .min_size = 256, // Don't compress streams smaller than this }, }); ``` ### Compression Options | Option | Default | Description | |--------|---------|-------------| | `enabled` | `true` | Enable FlateDecode compression | | `level` | `6` | Compression level (0-12) | | `min_size` | `256` | Minimum stream size to compress | ### Types | Type | File | Description | |------|------|-------------| | `CompressionOptions` | `src/output/producer.zig` | Compression config | --- ## File Structure ``` src/ ├── root.zig # Public exports ├── pdf.zig # Pdf document facade ├── page.zig # Page with drawing methods ├── content_stream.zig # PDF operators ├── table.zig # Table helper ├── pagination.zig # Page numbers, headers, footers ├── links.zig # Link types ├── outline.zig # Bookmarks ├── fonts/ │ ├── mod.zig # Font exports │ ├── type1.zig # 14 Type1 fonts + metrics │ └── ttf.zig # TrueType parser ├── graphics/ │ ├── mod.zig # Graphics exports │ ├── color.zig # Color types │ ├── extgstate.zig # Transparency (ExtGState) │ └── gradient.zig # Linear/Radial gradients ├── images/ │ ├── mod.zig # Image exports │ ├── image_info.zig # ImageInfo struct │ ├── jpeg.zig # JPEG parser │ └── png.zig # PNG parser ├── barcodes/ │ ├── mod.zig # Barcode exports │ ├── code128.zig # Code128 encoder │ └── qr.zig # QR Code encoder ├── compression/ │ ├── mod.zig # Compression exports │ └── zlib.zig # libdeflate wrapper ├── objects/ │ ├── mod.zig # Object exports │ └── base.zig # PageSize, Orientation ├── output/ │ ├── mod.zig # Output exports │ └── producer.zig # PDF serialization ├── security/ │ ├── mod.zig # Security exports │ ├── rc4.zig # RC4 cipher │ └── encryption.zig # PDF encryption ├── forms/ │ ├── mod.zig # Forms exports │ └── field.zig # TextField, CheckBox ├── svg/ │ ├── mod.zig # SVG exports │ └── parser.zig # SVG parser ├── template/ │ ├── mod.zig # Template exports │ └── template.zig # Template definitions └── markdown/ ├── mod.zig # Markdown exports └── markdown.zig # Markdown parser ``` --- ## Examples 16 example files in `examples/`: | File | Description | |------|-------------| | `hello.zig` | Minimal PDF | | `invoice.zig` | Complete invoice | | `text_demo.zig` | Text system (cell, multiCell) | | `image_demo.zig` | JPEG and PNG images | | `table_demo.zig` | Table helper | | `pagination_demo.zig` | Multi-page with page numbers | | `links_demo.zig` | Clickable links | | `bookmarks_demo.zig` | Document outline | | `curves_demo.zig` | Bezier, circles, arcs | | `transforms_demo.zig` | Rotate, scale, skew | | `transparency_demo.zig` | Opacity/alpha | | `gradient_demo.zig` | Linear and radial gradients | | `barcode_demo.zig` | Code128 and QR codes | | `ttf_demo.zig` | TrueType font parsing | | `template_demo.zig` | Document templates | | `markdown_demo.zig` | Markdown styled text | ### Run Examples ```bash # Build all zig build # Run specific example ./zig-out/bin/hello ./zig-out/bin/invoice ./zig-out/bin/barcode_demo # etc. ``` --- ## Known Limitations - **TTF Font Subsetting**: When embedding TrueType fonts, the entire font file is included rather than just the glyphs used. This can result in larger PDF files when using custom fonts. Font subsetting is planned for a future version. --- ## Building ```bash # Build library zig build # Run tests zig build test # Build specific example zig build hello ``` --- ## License MIT --- ## Version **v1.0** - Feature Complete (2025-12-09)