- 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>
21 KiB
zcatpdf - PDF Generation Library for Zig
A pure Zig library for creating PDF documents with minimal dependencies.
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:
.dependencies = .{
.zcatpdf = .{
.url = "https://git.reugenio.com/reugenio/zcatpdf/archive/v1.0.tar.gz",
.hash = "...",
},
},
In your build.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
- Document Creation
- Pages
- Text
- Fonts
- Colors
- Graphics
- Images
- Tables
- Links
- Bookmarks
- Barcodes
- Transformations
- Transparency
- Gradients
- Templates
- Markdown
- Compression
1. Document Creation
File: src/pdf.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
// 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
// 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
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
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
.left
.center
.right
4. Fonts
File: src/fonts/type1.zig, src/fonts/ttf.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)
// 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
// 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
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
try page.setLineWidth(2.0);
try page.drawLine(x1, y1, x2, y2);
Rectangles
// 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
// 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
// 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
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
// 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
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
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
// 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
// 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)
// 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)
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
// 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
// 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
// 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
// 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.
// 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, footerTemplate.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.
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~~ |
|
[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
// 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
# Build all
zig build
# Run specific example
./zig-out/bin/hello
./zig-out/bin/invoice
./zig-out/bin/barcode_demo
# etc.
Building
# 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)