🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| .claude/commands | ||
| docs | ||
| examples | ||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| claude.md | ||
| FUTURE_IMPROVEMENTS.md | ||
| IMPLEMENTATION_PLAN.md | ||
| README.md | ||
| VERIFIED_FEATURES.md | ||
| ZIG_VERSION_NOTES.md | ||
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.
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
# 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)