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>
183 lines
5.9 KiB
Zig
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", .{});
|
|
}
|