zcatpdf/README.md
reugenio f09922076f refactor: Rename zpdf to zcatpdf for consistency with zcat* family
- Renamed all references from zpdf to zcatpdf
- Module import: @import("zcatpdf")
- Consistent with zcatui, zcatgui naming convention
- All lowercase per Zig standards

Note: Directory rename (zpdf -> zcatpdf) and Forgejo repo rename
should be done manually after this commit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 02:10:57 +01:00

937 lines
21 KiB
Markdown

# 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.
```
---
## 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)