//! 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", .{}); }