Markdown to PDF
Convert CommonMark + GFM Markdown to styled PDF documents with a single method call. Built-in parser with zero managed dependencies — no Markdig, no external packages.
Quick Start
Add Markdown content to any page using the .Markdown() extension method:
Document.Create(doc =>
{
doc.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(50);
page.Content().Markdown("# Hello World\n\nThis is **bold** and *italic*.");
});
}).GeneratePdf("hello.pdf");
Standalone Document
Create a complete PDF from a Markdown string without manually setting up pages:
MarkdownExtensions.CreateFromMarkdown(markdownText)
.WithMetadata(new DocumentMetadata
{
Title = "Quarterly Report",
Author = "Finance Team"
})
.GeneratePdf("report.pdf");
This creates an A4 document with default margins and applies the standard MarkdownStyle.
Supported Syntax
FolioPDF's Markdown parser implements CommonMark 0.31.2 (90% spec compliance) plus GFM extensions:
| Feature | Syntax | PDF Rendering |
|---|---|---|
| Headings (1–6) | # H1 through ###### H6, setext | Bold text with scaled font sizes |
| Paragraphs | Blank-line separated blocks | Spaced paragraphs with configurable line height |
| Bold / Italic | **bold**, *italic*, ***both*** | Font weight and style changes |
| Strikethrough | ~~deleted~~ | Strikethrough text decoration |
| Links | [text](url), reference links | Clickable hyperlinks with configurable color |
| Images | , reference images | Embedded images (local files via resolver) |
| Code spans | `inline code` | Monospace font with background highlight |
| Fenced code blocks | ```lang ... ``` | Monospace block with background panel |
| Block quotes | > quote | Left border with padding and italic text |
| Unordered lists | - item | Bullet characters with configurable indent |
| Ordered lists | 1. item | Numbered items with configurable indent |
| Task lists (GFM) | - [x] done | Checked/unchecked characters |
| Tables (GFM) | Pipe-delimited with alignment | Full table layout with header styling |
| Thematic breaks | ---, ***, ___ | Horizontal rule |
| HTML entities | &, © | Decoded to Unicode characters |
| Backslash escapes | \*not italic\* | Literal punctuation characters |
| Autolinks | <https://example.com> | Clickable hyperlinks |
| Hard line breaks | Two trailing spaces or \ | Forced line break within paragraph |
Link Reference Definitions
Define link targets once and reference them throughout the document:
var md =
"Read the [getting started guide][gs] first, then check the [API reference][api].\n" +
"\n" +
"[gs]: https://example.com/docs/getting-started \"Getting Started Guide\"\n" +
"[api]: https://example.com/docs/api\n";
page.Content().Markdown(md);
Supports full references ([text][label]), collapsed references ([text][]), and shortcut references ([text]). Labels are case-insensitive.
Customizing Styles
Pass a MarkdownStyle to control every visual aspect of the rendered output:
var style = new MarkdownStyle
{
// Headings
Heading1FontSize = 36,
Heading2FontSize = 28,
Heading3FontSize = 22,
HeadingColor = "#1a1a2e",
// Body text
BodyFontSize = 12,
BodyFontFamily = "Lato",
LineHeight = 1.5f,
ParagraphSpacing = 10,
// Code
CodeFontFamily = "Courier",
CodeFontSize = 10,
CodeBlockBackground = "#282c34",
CodeBlockTextColor = "#abb2bf",
CodeBlockCornerRadius = 4,
CodeBlockPadding = 12,
InlineCodeBackground = "#f0f0f0",
// Links
LinkColor = "#0366d6",
// Block quotes
BlockQuoteBorderColor = "#dfe2e5",
BlockQuoteBorderWidth = 3,
BlockQuoteTextColor = "#6a737d",
// Lists
ListIndent = 20,
BulletCharacter = "\u2022",
TaskListCheckedCharacter = "\u2611",
TaskListUncheckedCharacter = "\u2610",
// Tables
TableHeaderBackground = "#f6f8fa",
TableBorderColor = "#dfe2e5",
TableCellPadding = 6,
// Thematic breaks
ThematicBreakColor = "#eaecef",
ThematicBreakThickness = 1,
// Semantic tags (for PDF/UA accessibility)
EnableSemanticTags = true,
};
page.Content().Markdown(markdownText, style);
MarkdownStyle Properties
| Category | Property | Type | Default |
|---|---|---|---|
| Headings | Heading1FontSize – Heading6FontSize | float | 28, 24, 20, 16, 14, 12 |
HeadingColor | string | "#000000" | |
| Body | BodyFontSize | float | 12 |
BodyFontFamily | string | "Lato" | |
LineHeight | float | 1.4 | |
ParagraphSpacing | float | 8 | |
| Code | CodeFontFamily | string | "Courier" |
CodeBlockBackground | string | "#f6f8fa" | |
CodeBlockPadding | float | 10 | |
InlineCodeBackground | string | "#f0f0f0" | |
| Links | LinkColor | string | "#0366d6" |
| Block quotes | BlockQuoteBorderColor | string | "#dfe2e5" |
BlockQuoteBorderWidth | float | 3 | |
| Lists | ListIndent | float | 20 |
BulletCharacter | string | "\u2022" (bullet) | |
| Tables | TableHeaderBackground | string | "#f6f8fa" |
TableBorderColor | string | "#dfe2e5" | |
| Thematic breaks | ThematicBreakColor | string | "#eaecef" |
| Accessibility | EnableSemanticTags | bool | true |
Image Handling
Markdown images () are resolved through an IMarkdownImageResolver. The default resolver loads images from the local file system:
// Default: FileSystemImageResolver resolves relative paths from the working directory
page.Content().Markdown("");
// Custom resolver for different base paths
var resolver = new FileSystemImageResolver("C:\\Assets\\Images");
page.Content().Markdown(markdownText, new MarkdownStyle(), resolver);
// Null resolver: skip all images (useful for text-only rendering)
page.Content().Markdown(markdownText, new MarkdownStyle(), new NullImageResolver());
IMarkdownImageResolver
public interface IMarkdownImageResolver
{
byte[]? ResolveImage(string url);
}
Return the image bytes for a given URL/path, or null to skip the image. Implement this interface for custom image sources (databases, HTTP, embedded resources, etc.).
Architecture
The Markdown pipeline is a three-stage process:
| Stage | Class | Description |
|---|---|---|
| Parse | MarkdownParser |
Converts Markdown text into an AST (MarkdownDocument). Two-phase: block parsing with link reference definition extraction, then delimiter-stack inline parsing. |
| Render to PDF | MarkdownPdfRenderer |
Walks the AST and emits FolioPDF layout elements (Column, Row, Table, Text, etc.) using the fluent API. Each AST node maps to one or more layout elements. |
| Render to HTML | MarkdownHtmlRenderer |
Walks the AST and emits HTML. Used exclusively for CommonMark spec compliance testing — not part of the PDF pipeline. |
AST Node Types
Block nodes: MarkdownDocument, MarkdownHeading, MarkdownParagraph, MarkdownCodeBlock, MarkdownBlockQuote, MarkdownList, MarkdownListItem, MarkdownThematicBreak, MarkdownTable, MarkdownTableCell
Inline nodes: MarkdownText, MarkdownEmphasis, MarkdownCodeSpan, MarkdownLink, MarkdownImage, MarkdownAutolink, MarkdownStrikethrough, MarkdownLineBreak, MarkdownSoftBreak
CommonMark Compliance
The parser is tested against the official CommonMark 0.31.2 specification (652 test examples). Each example runs as an individual enforced test case.
| Section | Pass Rate |
|---|---|
| ATX headings | 100% |
| Emphasis and strong emphasis | 97% |
| Setext headings | 96% |
| Link reference definitions | 96% |
| Images | 96% |
| Autolinks | 95% |
| Fenced code blocks | 93% |
| Backslash escapes | 92% |
| Code spans | 91% |
| Block quotes | 88% |
| Links | 87% |
| Hard line breaks | 87% |
| Entity references | 82% |
| List items | 77% |
| Lists | 69% |
| Overall | 90% |
HTML blocks and Raw HTML sections are architecturally skipped (raw HTML passthrough has no meaning in PDF output).
Complete Example
var md =
"# Project Status Report\n" +
"\n" +
"**Project:** Widget Redesign\n" +
"**Date:** April 2026\n" +
"**Status:** On Track\n" +
"\n" +
"## Summary\n" +
"\n" +
"The widget redesign project has completed Phase 2. Key metrics:\n" +
"\n" +
"| Metric | Target | Actual |\n" +
"|-----------------|--------|--------|\n" +
"| Performance | 50ms | 42ms |\n" +
"| Test Coverage | 90% | 94% |\n" +
"| Bug Count | < 10 | 7 |\n" +
"\n" +
"## Next Steps\n" +
"\n" +
"1. Begin Phase 3 integration testing\n" +
"2. Schedule stakeholder review for April 18\n" +
"3. Finalize documentation\n" +
"\n" +
"> **Note:** All deadlines assume no scope changes from the April 4 review.\n" +
"\n" +
"---\n" +
"\n" +
"For details, see the [full project plan](https://internal.example.com/project/widget-v2).\n";
var style = new MarkdownStyle
{
Heading1FontSize = 32,
BodyFontSize = 11,
LinkColor = "#1a73e8",
TableHeaderBackground = "#e8f0fe",
};
Document.Create(doc =>
{
doc.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(50);
page.Header().Text("Widget Redesign").FontSize(10).FontColor("#999999");
page.Content().Markdown(md, style);
page.Footer().AlignCenter().Text(t =>
{
t.Span("Page ");
t.CurrentPageNumber();
t.Span(" of ");
t.TotalPages();
});
});
}).GeneratePdf("status-report.pdf");
Next Steps
- Text & Typography — font management, OpenType features, RTL support
- Tables — advanced table layout with spanning and pagination
- Images & SVG — embedding images and vector graphics
- PDF/UA Accessibility — semantic tags for screen readers
- Output Formats — PDF, SVG, and image output options