zcatpdf/examples/markdown_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

183 lines
5.9 KiB
Zig

//! Markdown Demo - Rendering styled text with Markdown syntax
//!
//! Run with: zig build markdown_demo && ./zig-out/bin/markdown_demo
const std = @import("std");
const zpdf = @import("zpdf");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
std.debug.print("=== zpdf Markdown Demo ===\n\n", .{});
// Create PDF
var pdf = zpdf.Pdf.init(allocator, .{});
defer pdf.deinit();
pdf.setTitle("Markdown Styled Document");
pdf.setAuthor("zpdf Markdown Renderer");
var page = try pdf.addPage(.{});
// Sample markdown text
const markdown_text =
\\# Welcome to zpdf Markdown
\\
\\This is a demonstration of **Markdown-styled** text rendering in PDF.
\\
\\## Features
\\
\\The renderer supports the following formatting:
\\
\\- **Bold text** using double asterisks
\\- *Italic text* using single asterisks
\\- ***Bold and italic*** combined
\\- ~~Strikethrough~~ using tildes
\\- [Clickable links](https://github.com)
\\
\\### Code and Technical Content
\\
\\You can write technical documentation with styled text.
\\For example, the *zpdf* library is written in **Zig** and provides
\\a simple API for PDF generation.
\\
\\## Numbered Lists
\\
\\1. First item in the list
\\2. Second item with **bold** text
\\3. Third item with a [link](https://example.com)
\\
\\### Conclusion
\\
\\The Markdown renderer makes it easy to create styled documents
\\without manually managing fonts and styles.
;
// Parse markdown
var renderer = zpdf.MarkdownRenderer.init(allocator);
defer renderer.deinit();
try renderer.parse(markdown_text);
std.debug.print("Parsed {d} lines of Markdown\n", .{renderer.getLines().len});
// Render to PDF
const base_font_size: f32 = 11;
const line_height: f32 = 16;
const left_margin: f32 = 50;
const bullet_indent: f32 = 20;
var y: f32 = 780;
var list_number: u32 = 0;
for (renderer.getLines()) |line| {
// Handle blank lines
if (line.line_type == .blank) {
y -= line_height;
list_number = 0;
continue;
}
// Calculate y position based on line type
const extra_spacing: f32 = switch (line.line_type) {
.heading1 => 20,
.heading2 => 15,
.heading3 => 10,
else => 0,
};
y -= extra_spacing;
// Handle list prefixes
var x: f32 = left_margin;
switch (line.line_type) {
.bullet => {
try page.setFont(.helvetica, base_font_size);
page.setFillColor(zpdf.Color.black);
try page.drawText(x, y, "\xe2\x80\xa2"); // Bullet point (UTF-8)
// Note: PDF Type1 fonts don't support UTF-8, so we use a simple dash
try page.drawText(x, y, "-");
x += bullet_indent;
list_number = 0;
},
.numbered => {
list_number += 1;
try page.setFont(.helvetica, base_font_size);
page.setFillColor(zpdf.Color.black);
var num_buf: [16]u8 = undefined;
const num_str = std.fmt.bufPrint(&num_buf, "{d}.", .{list_number}) catch "?.";
try page.drawText(x, y, num_str);
x += bullet_indent;
},
else => {
list_number = 0;
},
}
// Render spans
for (line.spans) |span| {
// Set font based on style
const font = zpdf.MarkdownRenderer.fontForStyle(span.style);
const font_size = span.font_size orelse base_font_size;
try page.setFont(font, font_size);
// Set color
if (span.color) |color| {
page.setFillColor(zpdf.Color.hex(color));
} else {
page.setFillColor(zpdf.Color.black);
}
// Draw text
try page.drawText(x, y, span.text);
// Draw underline if needed
if (span.style.underline) {
const text_width = font.stringWidth(span.text, font_size);
page.setStrokeColor(if (span.color) |c| zpdf.Color.hex(c) else zpdf.Color.black);
try page.setLineWidth(0.5);
try page.drawLine(x, y - 2, x + text_width, y - 2);
}
// Draw strikethrough if needed
if (span.style.strikethrough) {
const text_width = font.stringWidth(span.text, font_size);
page.setStrokeColor(zpdf.Color.black);
try page.setLineWidth(0.5);
const strike_y = y + font_size * 0.3;
try page.drawLine(x, strike_y, x + text_width, strike_y);
}
// Advance x position
x += font.stringWidth(span.text, font_size);
// Add link annotation if present
if (span.url) |url| {
const text_width = font.stringWidth(span.text, font_size);
try page.addUrlLink(url, x - text_width, y - 2, text_width, font_size + 4);
}
}
// Move to next line
const line_font_size: f32 = if (line.spans.len > 0 and line.spans[0].font_size != null)
line.spans[0].font_size.?
else
base_font_size;
y -= line_font_size + 4;
// Add extra space after headings
y -= extra_spacing / 2;
// Check for page break
if (y < 80) {
page = try pdf.addPage(.{});
y = 780;
}
}
// Save
try pdf.save("markdown_demo.pdf");
std.debug.print("Saved: markdown_demo.pdf\n", .{});
}