{"id":35812185,"url":"https://github.com/alnah/go-md2pdf","last_synced_at":"2026-03-06T01:09:48.049Z","repository":{"id":330728808,"uuid":"1123742495","full_name":"alnah/go-md2pdf","owner":"alnah","description":"Go library and CLI for Markdown to PDF. Cover pages, auto table of contents, watermarks, signatures, CSS themes, and custom assets support. Parallel batch processing via headless Chrome. No LaTeX. No complexity.","archived":false,"fork":false,"pushed_at":"2026-03-05T15:54:26.000Z","size":963,"stargazers_count":70,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-03-05T18:47:25.988Z","etag":null,"topics":["batch-processing","chrome-headless","cli","document-generator","go","go-rod","golang","goldmark","markdown","markdown-to-pdf","pdf","pdf-generation"],"latest_commit_sha":null,"homepage":"https://alnah.io","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alnah.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-27T14:22:27.000Z","updated_at":"2026-03-05T16:06:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alnah/go-md2pdf","commit_stats":null,"previous_names":["alnah/go-md2pdf"],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/alnah/go-md2pdf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alnah%2Fgo-md2pdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alnah%2Fgo-md2pdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alnah%2Fgo-md2pdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alnah%2Fgo-md2pdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alnah","download_url":"https://codeload.github.com/alnah/go-md2pdf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alnah%2Fgo-md2pdf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30157029,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["batch-processing","chrome-headless","cli","document-generator","go","go-rod","golang","goldmark","markdown","markdown-to-pdf","pdf","pdf-generation"],"created_at":"2026-01-07T15:14:26.073Z","updated_at":"2026-03-06T01:09:48.038Z","avatar_url":"https://github.com/alnah.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-md2pdf\n\n[![Go Reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go\u0026logoColor=white)](https://pkg.go.dev/github.com/alnah/go-md2pdf)\n[![Go Report Card](https://img.shields.io/badge/go%20report-A+-brightgreen)](https://goreportcard.com/report/github.com/alnah/go-md2pdf)\n[![Build Status](https://img.shields.io/github/actions/workflow/status/alnah/go-md2pdf/ci.yml?branch=main)](https://github.com/alnah/go-md2pdf/actions)\n[![Coverage](https://img.shields.io/codecov/c/github/alnah/go-md2pdf)](https://codecov.io/gh/alnah/go-md2pdf)\n[![License](https://img.shields.io/badge/License-BSD--3--Clause-blue.svg)](LICENSE)\n\n\u003e Go library and CLI for Markdown to PDF conversion using headless Chrome. Auto-downloads Chromium on first run. Features cover pages, automatic table of contents, footers with page numbers, signatures, watermarks, and 8 built-in CSS themes with custom template support. Supports parallel batch processing.\n\n[See example outputs](examples/)\n\n![Example PDF outputs](examples/illustration.png)\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Features](#features)\n- [CLI Reference](#cli-reference)\n- [Environment Variables](#environment-variables)\n- [Configuration](#configuration)\n- [Config Init Wizard](#config-init-wizard)\n- [Library Usage](#library-usage)\n- [Troubleshooting](#troubleshooting)\n- [Known Limitations](#known-limitations)\n- [Contributing](#contributing)\n\n## Installation\n\n```bash\ngo install github.com/alnah/go-md2pdf/cmd/md2pdf@latest\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eOther installation methods\u003c/summary\u003e\n\n### Docker\n\n```bash\ndocker pull ghcr.io/alnah/go-md2pdf:latest\n```\n\n### Binary Download\n\nDownload pre-built binaries from [GitHub Releases](https://github.com/alnah/go-md2pdf/releases).\n\n\u003c/details\u003e\n\n## Requirements\n\n- Go 1.25+\n- Chrome/Chromium (downloaded automatically on first run)\n\n\u003e **Docker/CI users:** See [Troubleshooting](#troubleshooting) for setup instructions.\n\n## Quick Start\n\n### CLI\n\n```bash\nmd2pdf convert document.md                # Single file\nmd2pdf convert ./docs/ -o ./output/       # Batch convert\nmd2pdf convert -c work document.md        # With config\nmd2pdf config init                        # Create config with wizard\n```\n\n### Library\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    \"os\"\n\n    \"github.com/alnah/go-md2pdf\"\n)\n\nfunc main() {\n    conv, err := md2pdf.NewConverter()\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer conv.Close()\n\n    result, err := conv.Convert(context.Background(), md2pdf.Input{\n        Markdown: \"# Hello World\\n\\nGenerated with go-md2pdf.\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    os.WriteFile(\"output.pdf\", result.PDF, 0644)\n}\n```\n\nThe `Convert()` method returns a `ConvertResult` containing:\n- `result.PDF` - the generated PDF bytes\n- `result.HTML` - the intermediate HTML (useful for debugging)\n\nUse `Input.HTMLOnly: true` to skip PDF generation and only produce HTML.\n\n## Features\n\n- **CLI + Library** - Use as `md2pdf` command or import in Go, with shell completion\n- **Batch conversion** - Process directories with parallel workers\n- **Cover pages** - Title, subtitle, logo, author, organization, date, version\n- **Table of contents** - Auto-generated from headings with configurable depth\n- **Frontmatter stripping** - YAML frontmatter (`---` blocks) stripped before conversion\n- **Custom styling** - Embedded themes or your own CSS ([some limitations](#known-limitations))\n- **Page settings** - Size (letter, A4, legal), orientation, margins\n- **Signatures** - Name, title, email, photo, links\n- **Footers** - Page numbers, dates, status text\n- **Watermarks** - Diagonal background text (BRAND, etc.)\n\n## CLI Reference\n\n```bash\nmd2pdf convert document.md                # Single file\nmd2pdf convert ./docs/ -o ./output/       # Batch convert\nmd2pdf convert -c work document.md        # With config\nmd2pdf convert --style technical doc.md   # With style\nmd2pdf config init                        # Interactive config wizard\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eAll flags\u003c/summary\u003e\n\n```\nmd2pdf \u003ccommand\u003e [flags] [args]\n\nCommands:\n  convert      Convert markdown files to PDF\n  config       Manage configuration files\n  doctor       Check system configuration\n  completion   Generate shell completion script\n  version      Show version information\n  help         Show help for a command\n\nmd2pdf convert \u003cinput\u003e [flags]\n\nInput/Output:\n  -o, --output \u003cpath\u003e       Output file or directory\n  -c, --config \u003cname\u003e       Config file name or path\n  -w, --workers \u003cn\u003e         Parallel workers (0 = auto)\n  -t, --timeout \u003cduration\u003e  PDF generation timeout (default: 30s)\n                            Examples: 30s, 2m, 1m30s\n\nAuthor:\n      --author-name \u003cs\u003e     Author name\n      --author-title \u003cs\u003e    Author professional title\n      --author-email \u003cs\u003e    Author email\n      --author-org \u003cs\u003e      Organization name\n      --author-phone \u003cs\u003e    Author phone number\n      --author-address \u003cs\u003e  Author postal address\n      --author-dept \u003cs\u003e     Author department\n\nDocument:\n      --doc-title \u003cs\u003e       Document title (\"\" = auto from H1)\n      --doc-subtitle \u003cs\u003e    Document subtitle\n      --doc-version \u003cs\u003e     Version string\n      --doc-date \u003cs\u003e        Date (see Date Formats section)\n      --doc-client \u003cs\u003e      Client name\n      --doc-project \u003cs\u003e     Project name\n      --doc-type \u003cs\u003e        Document type\n      --doc-id \u003cs\u003e          Document ID/reference\n      --doc-desc \u003cs\u003e        Document description\n\nPage:\n  -p, --page-size \u003cs\u003e       letter, a4, legal (default: letter)\n      --orientation \u003cs\u003e     portrait, landscape (default: portrait)\n      --margin \u003cf\u003e          Margin in inches (default: 0.5)\n\nFooter:\n      --footer-position \u003cs\u003e left, center, right (default: right)\n      --footer-text \u003cs\u003e     Custom footer text\n      --footer-page-number  Show page numbers\n      --footer-doc-id       Show document ID in footer\n      --no-footer           Disable footer\n\nCover:\n      --cover-logo \u003cpath\u003e   Logo path or URL\n      --cover-dept          Show author department on cover\n      --no-cover            Disable cover page\n\nSignature:\n      --sig-image \u003cpath\u003e    Signature image path\n      --no-signature        Disable signature block\n\nTable of Contents:\n      --toc-title \u003cs\u003e       TOC heading text\n      --toc-min-depth \u003cn\u003e   Min heading depth (1-6, default: 2)\n                            1=H1, 2=H2, etc. Use 2 to skip title\n      --toc-max-depth \u003cn\u003e   Max heading depth (1-6, default: 3)\n      --no-toc              Disable table of contents\n\nWatermark:\n      --wm-text \u003cs\u003e         Watermark text\n      --wm-color \u003cs\u003e        Color hex (default: #888888)\n      --wm-opacity \u003cf\u003e      Opacity 0.0-1.0 (default: 0.1)\n      --wm-angle \u003cf\u003e        Angle in degrees (default: -45)\n      --no-watermark        Disable watermark\n\nPage Breaks:\n      --break-before \u003cs\u003e    Break before headings: h1,h2,h3\n      --orphans \u003cn\u003e         Min lines at page bottom (default: 2)\n      --widows \u003cn\u003e          Min lines at page top (default: 2)\n      --no-page-breaks      Disable page break features\n\nAssets \u0026 Styling:\n      --style \u003cname|path\u003e   CSS style name or file path (default: default)\n                            Name: uses embedded or custom asset (e.g., \"technical\")\n                            Path: reads file directly (contains / or \\)\n      --template \u003cname|path\u003e Template set name or directory path\n      --asset-path \u003cdir\u003e    Custom asset directory (overrides config)\n      --no-style            Disable CSS styling\n\nDebug Output:\n      --html                Output HTML alongside PDF\n      --html-only           Output HTML only, skip PDF generation\n\nOutput Control:\n  -q, --quiet               Only show errors\n  -v, --verbose             Show detailed timing\n\nmd2pdf config init [flags]\n\nConfig Init:\n      --output \u003cpath\u003e       Output path for generated config (default: ./md2pdf.yaml)\n      --force               Overwrite destination if it exists\n      --no-input            Use defaults without interactive prompts\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExamples\u003c/summary\u003e\n\n```bash\n# Single file with custom output\nmd2pdf convert -o report.pdf input.md\n\n# Batch with config\nmd2pdf convert -c work ./docs/ -o ./pdfs/\n\n# Custom CSS, no footer\nmd2pdf convert --style ./custom.css --no-footer document.md\n\n# A4 landscape with 1-inch margins\nmd2pdf convert -p a4 --orientation landscape --margin 1.0 document.md\n\n# With watermark\nmd2pdf convert --wm-text \"DRAFT\" --wm-opacity 0.15 document.md\n\n# Override document title\nmd2pdf convert --doc-title \"Final Report\" document.md\n\n# Page breaks before H1 and H2 headings\nmd2pdf convert --break-before h1,h2 document.md\n\n# Use embedded style by name\nmd2pdf convert --style technical document.md\n\n# Debug: output HTML alongside PDF\nmd2pdf convert --html document.md\n\n# Debug: output HTML only (no PDF)\nmd2pdf convert --html-only document.md\n\n# Use custom assets directory\nmd2pdf convert --asset-path ./my-assets document.md\n\n# Interactive config wizard\nmd2pdf config init\n\n# Non-interactive config generation (CI/scripts)\nmd2pdf config init --no-input --output ./configs/work.yaml --force\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eShell Completion\u003c/summary\u003e\n\nGenerate shell completion scripts for tab-completion of commands, flags, and file arguments:\n\n```bash\n# Bash - add to ~/.bashrc\neval \"$(md2pdf completion bash)\"\n\n# Zsh - add to ~/.zshrc\neval \"$(md2pdf completion zsh)\"\n\n# Fish - save to completions directory\nmd2pdf completion fish \u003e ~/.config/fish/completions/md2pdf.fish\n\n# PowerShell - add to $PROFILE\nmd2pdf completion powershell | Out-String | Invoke-Expression\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eExit Codes\u003c/summary\u003e\n\n| Code | Name | Description |\n|------|------|-------------|\n| 0 | Success | Conversion completed successfully |\n| 1 | General | Unexpected or unclassified error |\n| 2 | Usage | Invalid flags, configuration, or validation failure |\n| 3 | I/O | File not found, permission denied, write failure |\n| 4 | Browser | Chrome not found, connection failed, timeout |\n\nExample usage in scripts:\n\n```bash\nmd2pdf convert document.md\ncase $? in\n    0) echo \"Success\" ;;\n    2) echo \"Check your flags or config\" ;;\n    3) echo \"Check file permissions\" ;;\n    4) echo \"Check Chrome installation\" ;;\n    *) echo \"Unknown error\" ;;\nesac\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDoctor Command\u003c/summary\u003e\n\nDiagnose system configuration before running conversions:\n\n```bash\nmd2pdf doctor           # Human-readable output\nmd2pdf doctor --json    # JSON output for CI/scripts\n```\n\nChecks performed:\n- Chrome/Chromium: binary exists, version, sandbox status\n- Environment: container detection (Docker, Podman, Kubernetes)\n- System: temp directory writability\n\nExit codes:\n- `0` - All checks passed (including warnings)\n- `1` - Errors found (conversion will likely fail)\n\nExample CI usage:\n\n```bash\n# Fail pipeline early if setup is broken\nmd2pdf doctor --json | jq -e '.status != \"errors\"' || exit 1\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eDocker\u003c/summary\u003e\n\n```bash\n# Convert a single file\ndocker run --rm -v $(pwd):/data ghcr.io/alnah/go-md2pdf convert document.md\n\n# Convert with output path\ndocker run --rm -v $(pwd):/data ghcr.io/alnah/go-md2pdf convert -o output.pdf input.md\n\n# Batch convert directory\ndocker run --rm -v $(pwd):/data ghcr.io/alnah/go-md2pdf convert ./docs/ -o ./pdfs/\n```\n\n\u003e **Note:** The official Docker image has all dependencies pre-installed. For custom images, see [Troubleshooting](#troubleshooting).\n\n\u003c/details\u003e\n\n## Environment Variables\n\nEnvironment variables provide CI/CD-friendly configuration without requiring YAML files.\n\n**Priority:** CLI flags \u003e config file \u003e environment variables \u003e defaults\n\n### MD2PDF Variables\n\n| Variable | Description |\n|----------|-------------|\n| `MD2PDF_CONFIG` | Config file path (e.g., `/app/config.yaml`) |\n| `MD2PDF_INPUT_DIR` | Default input directory |\n| `MD2PDF_OUTPUT_DIR` | Default output directory |\n| `MD2PDF_TIMEOUT` | PDF generation timeout (e.g., `2m`, `90s`) |\n| `MD2PDF_STYLE` | CSS style name or path (e.g., `technical`) |\n| `MD2PDF_WORKERS` | Parallel workers (e.g., `4`) |\n| `MD2PDF_AUTHOR_NAME` | Author name for cover/signature |\n| `MD2PDF_AUTHOR_ORG` | Organization name |\n| `MD2PDF_AUTHOR_EMAIL` | Author email |\n| `MD2PDF_DOC_VERSION` | Document version |\n| `MD2PDF_DOC_DATE` | Document date (supports `auto`) |\n| `MD2PDF_DOC_ID` | Document ID |\n| `MD2PDF_PAGE_SIZE` | Page size: `letter`, `a4`, `legal` |\n| `MD2PDF_COVER_LOGO` | Cover logo path/URL (auto-enables cover) |\n| `MD2PDF_WATERMARK_TEXT` | Watermark text (auto-enables watermark) |\n| `MD2PDF_CONTAINER` | Set to `1` to force container detection (for `md2pdf doctor`) |\n\nUnknown `MD2PDF_*` variables trigger a warning to catch typos.\n\n\u003cdetails\u003e\n\u003csummary\u003eCI/CD Examples\u003c/summary\u003e\n\n**GitHub Actions:**\n```yaml\n- name: Generate PDFs\n  env:\n    MD2PDF_STYLE: technical\n    MD2PDF_AUTHOR_ORG: ${{ github.repository_owner }}\n    MD2PDF_DOC_VERSION: ${{ github.ref_name }}\n    MD2PDF_WATERMARK_TEXT: ${{ github.ref_name == 'main' \u0026\u0026 '' || 'DRAFT' }}\n  run: md2pdf convert ./docs/ -o ./output/\n```\n\n**GitLab CI:**\n```yaml\npdf:\n  variables:\n    MD2PDF_STYLE: corporate\n    MD2PDF_OUTPUT_DIR: ./artifacts/pdf\n    MD2PDF_DOC_DATE: auto\n  script:\n    - md2pdf convert ./docs/\n```\n\n**Docker:**\n```bash\ndocker run --rm \\\n  -e MD2PDF_STYLE=technical \\\n  -e MD2PDF_AUTHOR_ORG=\"Acme Corp\" \\\n  -e ROD_NO_SANDBOX=1 \\\n  -v $(pwd):/data \\\n  ghcr.io/alnah/go-md2pdf convert ./docs/\n```\n\n\u003c/details\u003e\n\n### Browser Variables (go-rod)\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ROD_NO_SANDBOX` | - | Set to `1` to disable Chrome sandbox (required for Docker/CI) |\n| `ROD_BROWSER_BIN` | - | Path to custom Chrome/Chromium binary |\n\nThese are used by the underlying [go-rod](https://github.com/go-rod/rod) browser automation library. Error messages will suggest these variables when browser issues are detected in CI/Docker environments.\n\n## Configuration\n\nConfig files are searched in the current directory first, then in the user config directory:\n\n| OS      | User Config Directory                      |\n| ------- | ------------------------------------------ |\n| Linux   | `~/.config/go-md2pdf/`                     |\n| macOS   | `~/Library/Application Support/go-md2pdf/` |\n| Windows | `%APPDATA%\\go-md2pdf\\`                     |\n\nSupported formats: `.yaml`, `.yml`\n\n## Config Init Wizard\n\nUse the wizard to generate a valid config file without writing YAML manually:\n\n```bash\n# Interactive wizard (TTY required)\nmd2pdf config init\n\n# Custom destination\nmd2pdf config init --output ./configs/work.yaml\n\n# Non-interactive defaults (CI/scripts)\nmd2pdf config init --no-input --output ./configs/work.yaml --force\n```\n\nWizard behavior:\n- Prompts are in English and include available options plus an example value.\n- Type `?` at a prompt to display inline help and a YAML snippet.\n- Interactive mode collects style, author fields, page size, and optional signature/watermark/cover settings.\n- Interactive mode shows a summary and YAML preview before write confirmation.\n- Without `--force`, existing files are preserved; with `--force`, overwrite is explicit and safe.\n\n| Option                  | Type   | Default      | Description                              |\n| ----------------------- | ------ | ------------ | ---------------------------------------- |\n| `input.defaultDir`      | string | -            | Default input directory                  |\n| `output.defaultDir`     | string | -            | Default output directory                 |\n| `timeout`               | string | `\"30s\"`      | PDF generation timeout (e.g., \"30s\", \"2m\") |\n| `style`                 | string | `\"default\"`  | CSS style name or path                   |\n| `assets.basePath`       | string | -            | Custom assets directory (styles, templates) |\n| `author.name`           | string | -            | Author name (used by cover, signature)   |\n| `author.title`          | string | -            | Author professional title                |\n| `author.email`          | string | -            | Author email                             |\n| `author.organization`   | string | -            | Organization name                        |\n| `author.phone`          | string | -            | Contact phone number                     |\n| `author.address`        | string | -            | Postal address (multiline via YAML `\\|`) |\n| `author.department`     | string | -            | Department name                          |\n| `document.title`        | string | -            | Document title (\"\" = auto from H1)       |\n| `document.subtitle`     | string | -            | Document subtitle                        |\n| `document.version`      | string | -            | Version string (used in cover, footer)   |\n| `document.date`         | string | -            | Date (see [Date Formats](#date-formats)) |\n| `document.clientName`   | string | -            | Client/customer name                     |\n| `document.projectName`  | string | -            | Project name                             |\n| `document.documentType` | string | -            | Document type (e.g., \"Specification\")    |\n| `document.documentID`   | string | -            | Document ID (e.g., \"DOC-2025-001\")       |\n| `document.description`  | string | -            | Brief document summary                   |\n| `page.size`             | string | `\"letter\"`   | letter, a4, legal                        |\n| `page.orientation`      | string | `\"portrait\"` | portrait, landscape                      |\n| `page.margin`           | float  | `0.5`        | Margin in inches (0.25-3.0)              |\n| `cover.enabled`         | bool   | `false`      | Show cover page                          |\n| `cover.logo`            | string | -            | Logo path or URL                         |\n| `cover.showDepartment`  | bool   | `false`      | Show author.department on cover          |\n| `toc.enabled`           | bool   | `false`      | Show table of contents                   |\n| `toc.title`             | string | -            | TOC title (empty = no title)             |\n| `toc.minDepth`          | int    | `2`          | Min heading depth (1-6, skips H1)        |\n| `toc.maxDepth`          | int    | `3`          | Max heading depth (1-6)                  |\n| `footer.enabled`        | bool   | `false`      | Show footer                              |\n| `footer.showPageNumber` | bool   | `false`      | Show page numbers                        |\n| `footer.position`       | string | `\"right\"`    | left, center, right                      |\n| `footer.text`           | string | -            | Custom footer text                       |\n| `footer.showDocumentID` | bool   | `false`      | Show document.documentID in footer       |\n| `signature.enabled`     | bool   | `false`      | Show signature block                     |\n| `signature.imagePath`   | string | -            | Photo path or URL                        |\n| `signature.links`       | array  | -            | Links (label, url)                       |\n| `watermark.enabled`     | bool   | `false`      | Show watermark                           |\n| `watermark.text`        | string | -            | Watermark text (required if enabled)     |\n| `watermark.color`       | string | `\"#888888\"`  | Watermark color (hex)                    |\n| `watermark.opacity`     | float  | `0.1`        | Watermark opacity (0.0-1.0)              |\n| `watermark.angle`       | float  | `-45`        | Watermark rotation (degrees)             |\n| `pageBreaks.enabled`    | bool   | `false`      | Enable page break features               |\n| `pageBreaks.beforeH1`   | bool   | `false`      | Page break before H1 headings            |\n| `pageBreaks.beforeH2`   | bool   | `false`      | Page break before H2 headings            |\n| `pageBreaks.beforeH3`   | bool   | `false`      | Page break before H3 headings            |\n| `pageBreaks.orphans`    | int    | `2`          | Min lines at page bottom (1-5)           |\n| `pageBreaks.widows`     | int    | `2`          | Min lines at page top (1-5)              |\n\n\u003cdetails\u003e\n\u003csummary\u003eExample config file\u003c/summary\u003e\n\n```yaml\n# ~/.config/go-md2pdf/work.yaml\n\n# Input/Output directories\ninput:\n  defaultDir: './docs/markdown' # Default input when no arg provided\n\noutput:\n  defaultDir: './docs/pdf' # Default output when no -o flag\n\n# PDF generation timeout (default: 30s)\n# Use Go duration format: 30s, 2m, 1m30s\ntimeout: '1m'\n\n# Shared author info (used by cover and signature)\nauthor:\n  name: 'John Doe'\n  title: 'Senior Developer'\n  email: 'john@example.com'\n  organization: 'Acme Corp'\n  phone: '+1 555-0123'\n  address: |\n    123 Main Street\n    San Francisco, CA 94102\n  department: 'Engineering'\n\n# Shared document metadata (used by cover and footer)\ndocument:\n  title: '' # \"\" = auto from H1 or filename\n  subtitle: 'Internal Document'\n  version: 'v1.0'\n  # Date formats:\n  #   - Literal: '2025-01-11'\n  #   - Auto (ISO): 'auto' -\u003e 2025-01-11\n  #   - Auto with format: 'auto:DD/MM/YYYY' -\u003e 11/01/2025\n  #   - Auto with preset: 'auto:long' -\u003e January 11, 2025\n  # Presets: iso, european, us, long\n  # Tokens: YYYY, YY, MMMM, MMM, MM, M, DD, D\n  # Escaping: [text] -\u003e literal text\n  date: 'auto'\n  clientName: 'Client Corp'\n  projectName: 'Project Alpha'\n  documentType: 'Technical Specification'\n  documentID: 'DOC-2025-001'\n  description: 'Technical documentation for Project Alpha'\n\n# Page layout\npage:\n  size: 'a4'           # letter (default), a4, legal\n  orientation: 'portrait' # portrait (default), landscape\n  margin: 0.75         # inches, 0.25-3.0 (default: 0.5)\n\n# Styling\n# Available styles:\n#   - default: minimal, neutral styling (applied when no style specified)\n#   - technical: system-ui, clean borders, GitHub syntax highlighting\n#   - creative: colorful headings, badges, bullet points\n#   - academic: Georgia/Times serif, 1.8 line height, academic tables\n#   - corporate: Arial/Helvetica, blue accents, business style\n#   - legal: Times New Roman, double line height, wide margins\n#   - invoice: Arial, optimized tables, minimal cover\n#   - manuscript: Courier New mono, scene breaks, simplified cover\n# Accepts name (e.g., \"technical\") or path (e.g., \"./custom.css\")\nstyle: 'technical'\n\nassets:\n  basePath: '' # \"\" = use embedded assets\n\n# Cover page\ncover:\n  enabled: true\n  logo: '/path/to/logo.png' # path or URL\n  showDepartment: true      # show author.department on cover\n\n# Table of contents\ntoc:\n  enabled: true\n  title: 'Table of Contents'\n  minDepth: 2 # 1-6 (default: 2, skips H1)\n  maxDepth: 3 # 1-6 (default: 3)\n\n# Footer\nfooter:\n  enabled: true\n  position: 'center'     # left, center, right (default: right)\n  showPageNumber: true\n  showDocumentID: true   # show document.documentID in footer\n  text: ''               # optional custom text\n\n# Signature block\nsignature:\n  enabled: true\n  imagePath: '/path/to/signature.png'\n  links:\n    - label: 'GitHub'\n      url: 'https://github.com/johndoe'\n    - label: 'LinkedIn'\n      url: 'https://linkedin.com/in/johndoe'\n\n# Watermark\nwatermark:\n  enabled: false\n  text: 'DRAFT'      # DRAFT, CONFIDENTIAL, SAMPLE, PREVIEW, etc.\n  color: '#888888'   # hex color (default: #888888)\n  opacity: 0.1       # 0.0-1.0 (default: 0.1, recommended: 0.05-0.15)\n  angle: -45         # -90 to 90 (default: -45 = diagonal)\n\n# Page breaks\npageBreaks:\n  enabled: true\n  beforeH1: true\n  beforeH2: false\n  beforeH3: false\n  orphans: 2 # min lines at page bottom, 1-5 (default: 2)\n  widows: 2  # min lines at page top, 1-5 (default: 2)\n```\n\n\u003c/details\u003e\n\n### Date Formats\n\nThe `document.date` field supports auto-generation with customizable formats:\n\n| Syntax        | Example           | Output          |\n| ------------- | ----------------- | --------------- |\n| `auto`        | `auto`            | 2026-01-09      |\n| `auto:FORMAT` | `auto:DD/MM/YYYY` | 09/01/2026      |\n| `auto:preset` | `auto:long`       | January 9, 2026 |\n\n**Presets:** `iso` (YYYY-MM-DD), `european` (DD/MM/YYYY), `us` (MM/DD/YYYY), `long` (MMMM D, YYYY)\n\n**Tokens:** `YYYY`, `YY`, `MMMM` (January), `MMM` (Jan), `MM`, `M`, `DD`, `D`\n\n**Escaping:** Use brackets for literal text: `auto:[Date:] YYYY-MM-DD` → \"Date: 2026-01-09\"\n\n## Library Usage\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Relative Images\u003c/summary\u003e\n\nWhen your markdown contains relative image paths like `![logo](./images/logo.png)`, specify the source directory so they resolve correctly:\n\n```go\ncontent, _ := os.ReadFile(\"docs/report.md\")\n\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown:  string(content),\n    SourceDir: \"docs/\", // Images resolve relative to this directory\n})\n```\n\nThe CLI automatically sets `SourceDir` to the input file's directory, so relative images work out of the box.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Cover Page\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    Cover: \u0026md2pdf.Cover{\n        Title:        \"Project Report\",\n        Subtitle:     \"Q4 2025 Analysis\",\n        Author:       \"John Doe\",\n        AuthorTitle:  \"Senior Analyst\",\n        Organization: \"Acme Corp\",\n        Date:         \"2025-12-15\",\n        Version:      \"v1.0\",\n        Logo:         \"/path/to/logo.png\", // or URL\n        ClientName:   \"Client Corp\",       // extended metadata\n        ProjectName:  \"Project Alpha\",\n        DocumentType: \"Technical Report\",\n        DocumentID:   \"DOC-2025-001\",\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Table of Contents\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    TOC: \u0026md2pdf.TOC{\n        Title:    \"Contents\",\n        MinDepth: 2, // Start at h2 (skip document title)\n        MaxDepth: 3, // Include up to h3\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Footer\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    Footer: \u0026md2pdf.Footer{\n        ShowPageNumber: true,\n        Position:       \"center\",\n        Date:           \"2025-12-15\",\n        Status:         \"DRAFT\",\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Signature\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    Signature: \u0026md2pdf.Signature{\n        Name:         \"John Doe\",\n        Title:        \"Senior Developer\",\n        Email:        \"john@example.com\",\n        Organization: \"Acme Corp\",\n        Phone:        \"+1 555-0123\",  // extended metadata\n        Department:   \"Engineering\",\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Watermark\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    Watermark: \u0026md2pdf.Watermark{\n        Text:    \"CONFIDENTIAL\",\n        Color:   \"#888888\",\n        Opacity: 0.1,\n        Angle:   -45,\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Page Settings\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    Page: \u0026md2pdf.PageSettings{\n        Size:        md2pdf.PageSizeA4,\n        Orientation: md2pdf.OrientationLandscape,\n        Margin:      1.0, // inches\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Page Breaks\u003c/summary\u003e\n\n```go\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    PageBreaks: \u0026md2pdf.PageBreaks{\n        BeforeH1: true, // Page break before H1 headings\n        BeforeH2: true, // Page break before H2 headings\n        Orphans:  3,    // Min 3 lines at page bottom\n        Widows:   3,    // Min 3 lines at page top\n    },\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Custom CSS\u003c/summary\u003e\n\nThe `CSS` field in `Input` accepts a CSS string that is injected into the HTML for this specific conversion:\n\n```go\n// CSS string injected into this document only\nresult, err := conv.Convert(ctx, md2pdf.Input{\n    Markdown: content,\n    CSS: `\n        body { font-family: Georgia, serif; }\n        h1 { color: #2c3e50; }\n        code { background: #f8f9fa; }\n    `,\n})\n```\n\nThis is useful for:\n- Document-specific styling that differs from the base theme\n- Dynamically generated CSS (e.g., user-selected colors)\n- Quick overrides without changing service configuration\n\nFor reusable styles across all conversions, see [With Custom Assets](#with-custom-assets).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Custom Assets\u003c/summary\u003e\n\nOverride embedded CSS styles and HTML templates:\n\n```go\n// Option 1: Use embedded style by name\nconv, err := md2pdf.NewConverter(md2pdf.WithStyle(\"technical\"))\n\n// Option 2: Load CSS from file path\nconv, err := md2pdf.NewConverter(md2pdf.WithStyle(\"./custom.css\"))\n\n// Option 3: Provide CSS content directly\nconv, err := md2pdf.NewConverter(md2pdf.WithStyle(\"body { font-family: Georgia; }\"))\n\n// Option 4: Load from custom directory (with fallback to embedded)\nconv, err := md2pdf.NewConverter(md2pdf.WithAssetPath(\"/path/to/assets\"))\n\n// Option 5: Provide template set directly\nts := md2pdf.NewTemplateSet(\"custom\", coverHTML, signatureHTML)\nconv, err := md2pdf.NewConverter(md2pdf.WithTemplateSet(ts))\n\n// Option 6: Full control with custom loader\nloader, err := md2pdf.NewAssetLoader(\"/path/to/assets\")\nif err != nil {\n    log.Fatal(err)\n}\nconv, err := md2pdf.NewConverter(md2pdf.WithAssetLoader(loader))\n```\n\n`WithStyle` accepts a style name, file path, or CSS content:\n- **Name**: `\"technical\"` loads the embedded style\n- **Path**: `\"./custom.css\"` reads from file (detected by `/` or `\\`)\n- **CSS**: `\"body { ... }\"` uses content directly (detected by `{`)\n\nExpected directory structure for `WithAssetPath`:\n\n```\n/path/to/assets/\n├── styles/\n│   ├── default.css      # Override default style\n│   └── technical.css    # Add custom style\n└── templates/\n    └── default/         # Template set directory\n        ├── cover.html       # Cover page template\n        └── signature.html   # Signature block template\n```\n\nAvailable embedded styles: `default`, `technical`, `creative`, `academic`, `corporate`, `legal`, `invoice`, `manuscript`\n\nMissing files fall back to embedded defaults silently.\n\n\u003e **Note:** Converter-level options (`WithAssetPath`, `WithStyle`, `WithAssetLoader`) configure the base theme for all conversions. To add document-specific CSS on top of the base theme, use `Input.CSS` in the `Convert()` call.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWith Converter Pool (Parallel Processing)\u003c/summary\u003e\n\nFor batch conversion, use `ConverterPool` to process multiple files in parallel:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n    \"os\"\n    \"sync\"\n\n    \"github.com/alnah/go-md2pdf\"\n)\n\nfunc main() {\n    // Create pool with 4 workers (each has its own browser instance)\n    pool := md2pdf.NewConverterPool(4)\n    defer pool.Close()\n\n    files := []string{\"doc1.md\", \"doc2.md\", \"doc3.md\", \"doc4.md\"}\n    var wg sync.WaitGroup\n\n    for _, file := range files {\n        wg.Add(1)\n        go func(f string) {\n            defer wg.Done()\n\n            conv := pool.Acquire()\n            if conv == nil {\n                log.Printf(\"failed to acquire converter: %v\", pool.InitError())\n                return\n            }\n            defer pool.Release(conv)\n\n            content, _ := os.ReadFile(f)\n            result, err := conv.Convert(context.Background(), md2pdf.Input{\n                Markdown: string(content),\n            })\n            if err != nil {\n                log.Printf(\"convert %s: %v\", f, err)\n                return\n            }\n            os.WriteFile(f+\".pdf\", result.PDF, 0644)\n        }(file)\n    }\n    wg.Wait()\n}\n```\n\nUse `md2pdf.ResolvePoolSize(0)` to auto-calculate optimal pool size based on CPU cores.\n\n\u003c/details\u003e\n\n## Documentation\n\nFull API documentation with runnable examples: [pkg.go.dev/github.com/alnah/go-md2pdf](https://pkg.go.dev/github.com/alnah/go-md2pdf)\n\n## Troubleshooting\n\nRun `md2pdf doctor` to diagnose system configuration issues:\n\n```bash\nmd2pdf doctor           # Human-readable diagnostics\nmd2pdf doctor --json    # JSON output for CI/scripts\n```\n\n### Docker and CI/CD\n\n#### \"Failed to connect to browser\" or blank PDF\n\nChrome requires disabling its sandbox in containerized environments:\n\n```bash\nexport ROD_NO_SANDBOX=1\nmd2pdf convert document.md\n```\n\nOr in Docker:\n```bash\ndocker run -e ROD_NO_SANDBOX=1 -v $(pwd):/data ghcr.io/alnah/go-md2pdf convert doc.md\n```\n\n#### Missing dependencies on Linux\n\nIf Chrome fails to start, install required libraries:\n\n```bash\n# Debian/Ubuntu\nsudo apt-get update\nsudo apt-get install -y \\\n    libnss3 \\\n    libatk-bridge2.0-0 \\\n    libcups2 \\\n    libdrm2 \\\n    libxkbcommon0 \\\n    libxcomposite1 \\\n    libxdamage1 \\\n    libxrandr2 \\\n    libgbm1 \\\n    libasound2\n\n# Alpine\napk add --no-cache \\\n    chromium \\\n    nss \\\n    freetype \\\n    harfbuzz \\\n    ca-certificates \\\n    ttf-freefont\n```\n\n\u003e **Note:** Dependency lists may change with Chrome versions. See [chromedp dependencies](https://github.com/chromedp/chromedp#dependencies) for the latest requirements.\n\n#### Using custom Chrome/Chromium\n\nPoint to a specific browser binary:\n\n```bash\nexport ROD_BROWSER_BIN=/usr/bin/chromium-browser\nmd2pdf convert document.md\n```\n\n### Common Errors\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| \"failed to connect to browser\" | Chrome not installed or sandbox issue | Install Chrome or set `ROD_NO_SANDBOX=1` |\n| \"page load failed\" | Timeout on large document | Use `--timeout 2m` or longer |\n| Blank PDF | Missing system libraries | Install Chrome dependencies (see above) |\n| \"style not found\" | Invalid style name | Use: default, technical, creative, academic, corporate, legal, invoice, manuscript |\n| Fonts look different | System fonts vary | Use Docker image for consistent fonts |\n\n### Platform Notes\n\n- **macOS/Windows:** Chrome is downloaded automatically. No special setup needed.\n- **Linux:** May require installing Chrome dependencies (see above).\n- **Docker/CI:** Always set `ROD_NO_SANDBOX=1` and install dependencies, or use the official Docker image.\n\n## Known Limitations\n\n\u003e **Design philosophy:** Professional PDF generation from Markdown. No LaTeX. No complexity.\n\n### By Design\n\nThese are intentional to keep the tool simple:\n\n| Not Supported | Why | Alternative |\n|---------------|-----|-------------|\n| **Raw HTML tags** | Security (prevents code execution during conversion) | Cover config for logos, native markdown `![]()` for images, custom CSS for styling |\n| LaTeX/MathJax | Adds complexity, requires external tools | Pre-render as PNG/SVG |\n| Wikilinks `[[...]]` | Not relevant for PDF output | Use `[text](url)` |\n| Admonitions `:::` | Not implemented | Use blockquotes |\n\n### Chrome PDF Engine\n\nInherited from the browser's print-to-PDF:\n\n- No PDF/A archival format\n- No multi-column layouts\n- No per-page headers/footers\n- No mixed orientation in one document\n- System fonts only (not embedded)\n\n### Platform Notes\n\n| Issue | Solution |\n|-------|----------|\n| Long code lines overflow | Keep lines under ~80 chars |\n| Fonts differ across systems | Use Docker for consistency |\n| Docker/CI fails | Set `ROD_NO_SANDBOX=1` (see [Troubleshooting](#troubleshooting)) |\n\n## Contributing\n\nSee: [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nSee: [BSD-3-Clause](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falnah%2Fgo-md2pdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falnah%2Fgo-md2pdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falnah%2Fgo-md2pdf/lists"}