zcatpdf/src/root.zig
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

329 lines
10 KiB
Zig

//! zcatpdf - PDF generation library for Zig
//!
//! A pure Zig library for creating PDF documents with minimal dependencies.
//! Based on fpdf2 (Python) architecture.
//!
//! ## Quick Start
//!
//! ```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");
//! }
//! ```
const std = @import("std");
// =============================================================================
// Module Re-exports
// =============================================================================
/// Main PDF document facade
pub const pdf = @import("pdf.zig");
pub const Pdf = pdf.Pdf;
pub const Config = pdf.Config;
/// Page representation
pub const page = @import("page.zig");
pub const Page = page.Page;
pub const Align = page.Align;
pub const Border = page.Border;
pub const CellPosition = page.Page.CellPosition;
/// Content stream (low-level PDF operators)
pub const content_stream = @import("content_stream.zig");
pub const ContentStream = content_stream.ContentStream;
pub const RenderStyle = content_stream.RenderStyle;
pub const LineCap = content_stream.LineCap;
pub const LineJoin = content_stream.LineJoin;
pub const TextRenderMode = content_stream.TextRenderMode;
/// Graphics (colors, transparency, gradients)
pub const graphics = @import("graphics/mod.zig");
pub const Color = graphics.Color;
pub const ExtGState = graphics.ExtGState;
pub const Gradient = graphics.Gradient;
pub const LinearGradient = graphics.LinearGradient;
pub const RadialGradient = graphics.RadialGradient;
pub const ColorStop = graphics.ColorStop;
pub const GradientDirection = page.Page.GradientDirection;
/// Fonts
pub const fonts = @import("fonts/mod.zig");
pub const Font = fonts.Font;
pub const FontFamily = fonts.FontFamily;
pub const FontState = fonts.FontState;
pub const TrueTypeFont = fonts.TrueTypeFont;
/// Objects (base types, page sizes, units)
pub const objects = @import("objects/mod.zig");
pub const PageSize = objects.PageSize;
pub const Orientation = objects.Orientation;
pub const Unit = objects.Unit;
/// Output (PDF generation)
pub const output = @import("output/mod.zig");
pub const OutputProducer = output.OutputProducer;
pub const CompressionOptions = output.CompressionOptions;
/// Images (JPEG, PNG)
pub const images = @import("images/mod.zig");
pub const ImageInfo = images.ImageInfo;
pub const ImageFormat = images.ImageFormat;
/// Compression (zlib/deflate)
pub const compression = @import("compression/mod.zig");
/// Table helper
pub const table = @import("table.zig");
pub const Table = table.Table;
pub const TableOptions = table.TableOptions;
/// Pagination (page numbers, headers, footers)
pub const pagination = @import("pagination.zig");
pub const Pagination = pagination.Pagination;
pub const PageNumberOptions = pagination.PageNumberOptions;
pub const FooterOptions = pagination.FooterOptions;
pub const HeaderOptions = pagination.HeaderOptions;
pub const addHeader = pagination.addHeader;
pub const addFooterWithLine = pagination.addFooterWithLine;
/// Links (URL annotations)
pub const links = @import("links.zig");
pub const Link = links.Link;
pub const PageLinks = links.PageLinks;
/// Outline/Bookmarks
pub const outline_mod = @import("outline.zig");
pub const Outline = outline_mod.Outline;
pub const OutlineItem = outline_mod.OutlineItem;
/// Barcodes (Code128, QR)
pub const barcodes = @import("barcodes/mod.zig");
pub const Code128 = barcodes.Code128;
pub const QRCode = barcodes.QRCode;
/// Security (Encryption)
pub const security = @import("security/mod.zig");
pub const Encryption = security.Encryption;
pub const EncryptionOptions = security.EncryptionOptions;
pub const Permissions = security.Permissions;
/// Forms (AcroForms)
pub const forms = @import("forms/mod.zig");
pub const TextField = forms.TextField;
pub const CheckBox = forms.CheckBox;
pub const FieldFlags = forms.FieldFlags;
pub const FormField = forms.FormField;
/// SVG Import
pub const svg = @import("svg/mod.zig");
pub const SvgParser = svg.SvgParser;
pub const SvgElement = svg.SvgElement;
/// Templates (reusable layouts)
pub const template = @import("template/mod.zig");
pub const Template = template.Template;
pub const TemplateRegion = template.TemplateRegion;
pub const RegionType = template.RegionType;
/// Markdown styling
pub const markdown = @import("markdown/mod.zig");
pub const MarkdownRenderer = markdown.MarkdownRenderer;
pub const TextSpan = markdown.TextSpan;
pub const SpanStyle = markdown.SpanStyle;
// =============================================================================
// Backwards Compatibility - Old API (Document)
// =============================================================================
/// Legacy Document type (use Pdf instead for new code).
/// Provided for backwards compatibility with existing code.
pub const Document = struct {
inner: Pdf,
const Self = @This();
/// Creates a new empty PDF document.
pub fn init(allocator: std.mem.Allocator) Self {
return .{
.inner = Pdf.init(allocator, .{}),
};
}
/// Frees all resources.
pub fn deinit(self: *Self) void {
self.inner.deinit();
}
/// Adds a new page with a standard size.
pub fn addPage(self: *Self, size: PageSize) !*Page {
return try self.inner.addPage(.{ .size = size });
}
/// Adds a new page with custom dimensions (in points).
pub fn addPageCustom(self: *Self, width: f32, height: f32) !*Page {
return try self.inner.addPageCustom(width, height);
}
/// Renders the document to a byte buffer.
pub fn render(self: *Self, allocator: std.mem.Allocator) ![]u8 {
_ = allocator; // Use inner allocator
return try self.inner.render();
}
/// Saves the document to a file.
pub fn saveToFile(self: *Self, path: []const u8) !void {
return try self.inner.save(path);
}
};
// =============================================================================
// Tests
// =============================================================================
test "zcatpdf re-exports" {
// Test that all types are accessible
_ = Pdf;
_ = Page;
_ = ContentStream;
_ = Color;
_ = Font;
_ = PageSize;
_ = Document;
}
test "Document backwards compatibility" {
const allocator = std.testing.allocator;
var doc = Document.init(allocator);
defer doc.deinit();
var pg = try doc.addPage(.a4);
try pg.setFont(.helvetica_bold, 24);
try pg.drawText(50, 750, "Hello");
try pg.drawLine(50, 740, 200, 740);
const data = try doc.render(allocator);
defer allocator.free(data);
try std.testing.expect(std.mem.startsWith(u8, data, "%PDF-1.4"));
}
test "new Pdf API" {
const allocator = std.testing.allocator;
var pdf_doc = Pdf.init(allocator, .{
.page_size = .a4,
.orientation = .portrait,
});
defer pdf_doc.deinit();
pdf_doc.setTitle("Test Document");
pdf_doc.setAuthor("zcatpdf");
var pg = try pdf_doc.addPage(.{});
try pg.setFont(.helvetica_bold, 24);
pg.setFillColor(Color.blue);
try pg.drawText(50, 750, "Hello zcatpdf!");
pg.setStrokeColor(Color.red);
try pg.setLineWidth(2);
try pg.drawLine(50, 740, 200, 740);
pg.setFillColor(Color.light_gray);
try pg.fillRect(50, 600, 150, 100);
const data = try pdf_doc.output();
defer allocator.free(data);
try std.testing.expect(std.mem.startsWith(u8, data, "%PDF-1.4"));
try std.testing.expect(std.mem.indexOf(u8, data, "/Title (Test Document)") != null);
}
test "Color types" {
const red = Color.rgb(255, 0, 0);
const floats = red.toRgbFloats();
try std.testing.expectApproxEqAbs(@as(f32, 1.0), floats.r, 0.01);
try std.testing.expectApproxEqAbs(@as(f32, 0.0), floats.g, 0.01);
try std.testing.expectApproxEqAbs(@as(f32, 0.0), floats.b, 0.01);
const hex_color = Color.hex(0xFF8000);
try std.testing.expectEqual(@as(u8, 255), hex_color.rgb_val.r);
try std.testing.expectEqual(@as(u8, 128), hex_color.rgb_val.g);
try std.testing.expectEqual(@as(u8, 0), hex_color.rgb_val.b);
}
test "Font metrics" {
const font = Font.helvetica;
try std.testing.expectEqualStrings("Helvetica", font.pdfName());
const width = font.stringWidth("Hello", 12.0);
try std.testing.expect(width > 0);
}
test "ContentStream operators" {
const allocator = std.testing.allocator;
var cs = ContentStream.init(allocator);
defer cs.deinit();
try cs.saveState();
try cs.setLineWidth(2.0);
try cs.setStrokeColorRgb(1.0, 0.0, 0.0);
try cs.moveTo(100, 200);
try cs.lineTo(300, 200);
try cs.stroke();
try cs.restoreState();
const content = cs.getContent();
try std.testing.expect(std.mem.indexOf(u8, content, "q\n") != null);
try std.testing.expect(std.mem.indexOf(u8, content, "Q\n") != null);
try std.testing.expect(std.mem.indexOf(u8, content, "1.000 0.000 0.000 RG") != null);
}
// Import all module tests
comptime {
_ = @import("content_stream.zig");
_ = @import("page.zig");
_ = @import("pdf.zig");
_ = @import("graphics/color.zig");
_ = @import("graphics/extgstate.zig");
_ = @import("graphics/gradient.zig");
_ = @import("barcodes/mod.zig");
_ = @import("barcodes/code128.zig");
_ = @import("barcodes/qr.zig");
_ = @import("fonts/type1.zig");
_ = @import("fonts/ttf.zig");
_ = @import("objects/base.zig");
_ = @import("output/producer.zig");
_ = @import("images/mod.zig");
_ = @import("images/image_info.zig");
_ = @import("images/jpeg.zig");
_ = @import("images/png.zig");
_ = @import("compression/mod.zig");
_ = @import("compression/zlib.zig");
_ = @import("table.zig");
_ = @import("pagination.zig");
_ = @import("links.zig");
_ = @import("outline.zig");
_ = @import("security/mod.zig");
_ = @import("security/rc4.zig");
_ = @import("security/encryption.zig");
_ = @import("forms/mod.zig");
_ = @import("forms/field.zig");
_ = @import("svg/mod.zig");
_ = @import("svg/parser.zig");
_ = @import("template/mod.zig");
_ = @import("template/template.zig");
_ = @import("markdown/mod.zig");
_ = @import("markdown/markdown.zig");
}