zcatpdf/examples/ttf_demo.zig
reugenio 3826cbaed4 Release v1.0 - Feature Complete PDF Generation Library
Major features added since v0.5:
- PNG support with alpha/transparency (soft masks)
- FlateDecode compression via libdeflate-zig
- Bookmarks/Outline for document navigation
- Bezier curves, circles, ellipses, arcs
- Transformations (rotate, scale, translate, skew)
- Transparency/opacity (fill and stroke alpha)
- Linear and radial gradients (Shading Patterns)
- Code128 (1D) and QR Code (2D) barcodes
- TrueType font parsing (metrics, glyph widths)
- RC4 encryption module (40/128-bit)
- AcroForms module (TextField, CheckBox)
- SVG import (basic shapes and paths)
- Template system (reusable layouts)
- Markdown styling (bold, italic, links, headings, lists)

Documentation:
- README.md: Complete API reference with code examples
- FUTURE_IMPROVEMENTS.md: Detailed roadmap for future development
- CLAUDE.md: Updated to v1.0 release status

Stats:
- 125+ unit tests passing
- 16 demo examples
- 46 source files

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

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

194 lines
6.3 KiB
Zig

//! TrueType Font Demo
//!
//! Demonstrates loading and using TrueType fonts.
const std = @import("std");
const zpdf = @import("zpdf");
const Pdf = zpdf.Pdf;
const Color = zpdf.Color;
const TrueTypeFont = zpdf.TrueTypeFont;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var pdf = Pdf.init(allocator, .{});
defer pdf.deinit();
pdf.setTitle("TrueType Font Demo");
pdf.setAuthor("zpdf");
// Try to load a system TTF font
const font_paths = [_][]const u8{
"/usr/share/fonts/google-carlito-fonts/Carlito-Regular.ttf",
"/usr/share/fonts/adwaita-sans-fonts/AdwaitaSans-Regular.ttf",
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/dejavu/DejaVuSans.ttf",
};
var ttf_font: ?TrueTypeFont = null;
var font_path: ?[]const u8 = null;
var font_data: ?[]u8 = null; // Keep font data alive
for (font_paths) |path| {
const file = std.fs.openFileAbsolute(path, .{}) catch continue;
defer file.close();
const data = file.readToEndAlloc(allocator, 10 * 1024 * 1024) catch continue;
// Don't defer free - font needs to keep data alive
ttf_font = TrueTypeFont.parse(allocator, data) catch {
allocator.free(data);
continue;
};
font_data = data;
font_path = path;
break;
}
defer if (font_data) |data| allocator.free(data);
var page = try pdf.addPage(.{});
// Title
try page.setFont(.helvetica_bold, 28);
page.setFillColor(Color.hex(0x333333));
try page.drawText(50, 780, "TrueType Font Demo");
try page.setFont(.helvetica, 12);
page.setFillColor(Color.hex(0x666666));
try page.drawText(50, 760, "TrueType font parsing and information");
if (ttf_font) |*font| {
defer font.deinit();
// Font information section
try page.setFont(.helvetica_bold, 16);
page.setFillColor(Color.hex(0x333333));
try page.drawText(50, 720, "Loaded Font Information");
try page.setFont(.courier, 10);
page.setFillColor(Color.black);
var y: f32 = 690;
try page.drawText(50, y, "File:");
try page.drawText(150, y, font_path.?);
y -= 20;
try page.drawText(50, y, "Family:");
if (font.family_name.len > 0) {
try page.drawText(150, y, font.family_name);
} else {
try page.drawText(150, y, "(not available)");
}
y -= 20;
try page.drawText(50, y, "Subfamily:");
if (font.subfamily_name.len > 0) {
try page.drawText(150, y, font.subfamily_name);
} else {
try page.drawText(150, y, "(not available)");
}
y -= 20;
try page.drawText(50, y, "PostScript:");
if (font.postscript_name.len > 0) {
try page.drawText(150, y, font.postscript_name);
} else {
try page.drawText(150, y, "(not available)");
}
y -= 20;
// Font metrics
var buf: [128]u8 = undefined;
const units_str = std.fmt.bufPrint(&buf, "{d}", .{font.units_per_em}) catch "(error)";
try page.drawText(50, y, "Units/EM:");
try page.drawText(150, y, units_str);
y -= 20;
const ascender_str = std.fmt.bufPrint(&buf, "{d}", .{font.ascender}) catch "(error)";
try page.drawText(50, y, "Ascender:");
try page.drawText(150, y, ascender_str);
y -= 20;
const descender_str = std.fmt.bufPrint(&buf, "{d}", .{font.descender}) catch "(error)";
try page.drawText(50, y, "Descender:");
try page.drawText(150, y, descender_str);
y -= 20;
const glyphs_str = std.fmt.bufPrint(&buf, "{d}", .{font.num_glyphs}) catch "(error)";
try page.drawText(50, y, "Num Glyphs:");
try page.drawText(150, y, glyphs_str);
y -= 20;
// Bounding box
const bbox_str = std.fmt.bufPrint(&buf, "[{d}, {d}, {d}, {d}]", .{
font.bbox[0],
font.bbox[1],
font.bbox[2],
font.bbox[3],
}) catch "(error)";
try page.drawText(50, y, "BBox:");
try page.drawText(150, y, bbox_str);
y -= 20;
const angle_str = std.fmt.bufPrint(&buf, "{d:.2}", .{font.italic_angle}) catch "(error)";
try page.drawText(50, y, "Italic Angle:");
try page.drawText(150, y, angle_str);
y -= 20;
// Character width test
y -= 20;
try page.setFont(.helvetica_bold, 14);
try page.drawText(50, y, "Character Widths (in font units):");
y -= 20;
try page.setFont(.courier, 10);
const test_chars = "AaBbCcDdEeFf0123456789";
for (test_chars) |c| {
const glyph_id = font.getGlyphIndex(c);
const width = font.getGlyphWidth(glyph_id);
const char_str = std.fmt.bufPrint(&buf, "'{c}' -> glyph {d}, width {d}", .{ c, glyph_id, width }) catch "(error)";
try page.drawText(50, y, char_str);
y -= 15;
if (y < 100) break;
}
// String width calculation
y -= 20;
try page.setFont(.helvetica_bold, 14);
try page.drawText(50, y, "String Width Calculation:");
y -= 20;
try page.setFont(.courier, 10);
const test_string = "Hello, World!";
const width_12pt = font.stringWidth(test_string, 12.0);
const width_str = std.fmt.bufPrint(&buf, "\"{s}\" at 12pt = {d:.2} points", .{ test_string, width_12pt }) catch "(error)";
try page.drawText(50, y, width_str);
} else {
try page.setFont(.helvetica, 14);
page.setFillColor(Color.red);
try page.drawText(50, 700, "No TrueType font found on this system.");
try page.drawText(50, 680, "Tried the following paths:");
try page.setFont(.courier, 10);
var y: f32 = 650;
for (font_paths) |path| {
try page.drawText(70, y, path);
y -= 15;
}
}
// Note about TTF embedding
try page.setFont(.helvetica_oblique, 10);
page.setFillColor(Color.hex(0x999999));
try page.drawText(50, 50, "Note: Full TTF embedding in PDF requires additional implementation for CIDFont Type 2 output.");
try pdf.save("ttf_demo.pdf");
std.debug.print("Generated ttf_demo.pdf\n", .{});
}