Skip to main content

markdown-to-pdf

Converting Markdown to professional PDFs using Pandoc, WeasyPrint, ReportLab, and related tools

Datos de origen

Repositorio
cosmicstack-labs/mercury-agent-skills
Última actividad en el origen
12 de mayo de 2026 a las 09:02
Idioma detectado de SKILL.md
inglés
Estrellas
476
Forks
63

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
markdown-to-pdf
description
Converting Markdown to professional PDFs using Pandoc, WeasyPrint, ReportLab, and related tools
metadata
{"author":"cosmicstack-labs","version":"1.0.0","category":"pdf-generation","tags":["markdown","pdf","pandoc","weasyprint","reportlab","conversion"]}
# Markdown to PDF Conversion Convert Markdown documents into polished, production-ready PDFs. This skill covers the major toolchains — Pandoc, WeasyPrint, ReportLab — plus CSS print styling, template systems, and batch conversion workflows. --- ## Toolchain Overview | Tool | Approach | Best For | Output Quality | |------|----------|----------|----------------| | **Pandoc** | Markdown → PDF via LaTeX/Context/Wkhtmltopdf | Books, papers, reports | High | | **WeasyPrint** | Markdown → HTML → PDF via CSS print | Web-like documents, branded PDFs | High | | **ReportLab** | Programmatic PDF generation via Python | Dynamic, data-driven PDFs | Very High | | **md-to-pdf** | Node.js CLI for quick markdown → PDF | Simple documents, quick output | Medium | | **Puppeteer/Playwright** | Markdown → HTML → Headless browser → PDF | Pixel-perfect web-to-PDF | Very High | --- ## 1. Pandoc Workflows Pandoc is the Swiss Army knife of document conversion. It reads Markdown and outputs PDF via an intermediate engine (default: LaTeX). ### Basic Conversion ```bash # Simple markdown to PDF (uses pdflatex) pandoc input.md -o output.pdf # With metadata file pandoc input.md --metadata-file=metadata.yaml -o output.pdf # Specify output engine pandoc input.md --pdf-engine=xelatex -o output.pdf # Using wkhtmltopdf instead of LaTeX pandoc input.md --pdf-engine=wkhtmltopdf -o output.pdf ``` ### Advanced Pandoc with Templates ```bash # Custom LaTeX template pandoc input.md \ --template=custom-template.tex \ --pdf-engine=xelatex \ --toc \ --number-sections \ -o output.pdf # With bibliography pandoc input.md \ --bibliography=references.bib \ --csl=ieee.csl \ --citeproc \ -o output.pdf # Custom font and margins pandoc input.md \ -V geometry:"top=2cm, bottom=2cm, left=2.5cm, right=2.5cm" \ -V mainfont="Times New Roman" \ -V fontsize=12pt \ -o output.pdf ``` ### Metadata YAML for Pandoc ```yaml # metadata.yaml --- title: "Technical Report on Distributed Systems" author: - "Jane Doe" - "John Smith" date: "2025-01-15" subtitle: "Performance Analysis of Event-Driven Architectures" abstract: | This report analyzes the performance characteristics of event-driven architectures in distributed systems, comparing Apache Kafka, RabbitMQ, and AWS SQS across latency, throughput, and fault tolerance metrics. keywords: [distributed-systems, event-driven, kafka, rabbitmq, performance] lang: en-US geometry: - top=25mm - bottom=25mm - left=30mm - right=25mm fontsize: 11pt mainfont: "DejaVu Serif" monofont: "DejaVu Sans Mono" toc: true toc-depth: 3 numbersections: true linestretch: 1.5 --- ``` ### Pandoc Filter: Custom Transformations ```python #!/usr/bin/env python3 """Pandoc filter to add custom styling to code blocks.""" import pandocfilters as pf def code_blocks(key, value, format, meta): """Wrap code blocks in a styled container.""" if key == 'CodeBlock': [[ident, classes, keyvals], code] = value if 'python' in classes: # Add a custom div wrapper for Python code return pf.Div( ([ident, ['python-block'], keyvals], [pf.CodeBlock([ident, classes, keyvals], code)]) ) if __name__ == '__main__': pf.toJSONFilter(code_blocks) ``` ```bash # Use the filter pandoc input.md --filter=./code_styler.py -o output.pdf ``` --- ## 2. WeasyPrint: HTML/CSS to PDF WeasyPrint renders HTML with CSS print styles into PDFs. It's ideal for documents that need pixel-perfect branding, colors, and web-like layouts. ### Basic WeasyPrint Workflow ```python """Convert HTML to PDF using WeasyPrint.""" from weasyprint import HTML # Basic conversion HTML('document.html').write_pdf('output.pdf') # From a URL HTML('https://example.com/report').write_pdf('webpage.pdf') # From a string html_content = """ <html> <body> <h1>Hello World</h1> <p>This is a PDF generated from an HTML string.</p> </body> </html> """ HTML(string=html_content).write_pdf('from_string.pdf') ``` ### Markdown → HTML → PDF Pipeline ```python """Full pipeline: Markdown to HTML to PDF with CSS styling.""" import markdown from weasyprint import HTML def markdown_to_pdf(md_path, css_path, output_path): """Convert markdown to PDF through HTML intermediate.""" with open(md_path, 'r') as f: md_content = f.read() # Convert markdown to HTML html_content = markdown.markdown( md_content, extensions=['tables', 'fenced_code', 'codehilite', 'toc', 'sane_lists', 'attr_list'] ) # Wrap in full HTML document full_html = f""" <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="{css_path}"> </head> <body> {html_content} </body> </html> """ # Generate PDF HTML(string=full_html).write_pdf(output_path) print(f"PDF generated: {output_path}") # Usage markdown_to_pdf('report.md', 'print.css', 'report.pdf') ``` ### CSS Print Stylesheet Template ```css /* print.css — Professional print stylesheet for PDF generation */ /* Page setup */ @page { size: A4; margin: 2.5cm 2cm 2.5cm 2cm; @top-center { content: element(pageHeader); font-size: 9pt; color: #666; } @bottom-center { content: counter(page) " / " counter(pages); font-size: 9pt; color: #666; } @bottom-left { content: "Confidential"; font-size: 8pt; color: #999; font-style: italic; } } /* First page — no header, different margins */ @page:first { margin-top: 4cm; @top-center { content: none; } } /* Section start — reset page counter */ @page chapter { @top-center { content: "Chapter " counter(chapter); } } /* Base typography */ body { font-family: "DejaVu Serif", Georgia, "Times New Roman", serif; font-size: 11pt; line-height: 1.6; color: #1a1a1a; counter-reset: h2 h3 figure table; } /* Heading styles */ h1 { font-size: 24pt; font-weight: bold; color: #1a1a1a; page-break-before: always; page-break-after: avoid; margin-top: 2cm; border-bottom: 2px solid #333; padding-bottom: 8pt; } h1:first-of-type { page-break-before: avoid; } h2 { font-size: 18pt; font-weight: bold; color: #333; page-break-after: avoid; margin-top: 1.5cm; counter-increment: h2; } h2::before { content: counter(h2) ". "; } h3 { font-size: 14pt; font-weight: bold; color: #555; page-break-after: avoid; margin-top: 1cm; counter-increment: h3; } h3::before { content: counter(h2) "." counter(h3) " "; } /* Paragraphs */ p { text-align: justify; orphans: 3; widows: 3; } /* Code blocks */ pre { font-family: "DejaVu Sans Mono", "Courier New", monospace; font-size: 9pt; background: #f5f5f5; border: 1px solid #ddd; border-left: 3px solid #007acc; padding: 12px; page-break-inside: avoid; white-space: pre-wrap; word-wrap: break-word; } code { font-family: "DejaVu Sans Mono", "Courier New", monospace; font-size: 9pt; background: #f0f0f0; padding: 1px 4px; border-radius: 2px; } pre code { background: none; padding: 0; } /* Tables */ table { width: 100%; border-collapse: collapse; margin: 1em 0; font-size: 10pt; page-break-inside: auto; } thead { display: table-header-group; } tr { page-break-inside: avoid; page-break-after: auto; } th { background: #2c3e50; color: white; font-weight: bold; padding: 8px 12px; text-align: left; } td { padding: 6px 12px; border-bottom: 1px solid #ddd; } tr:nth-child(even) { background: #f9f9f9; } /* Images */ img { max-width: 100%; height: auto; page-break-inside: avoid; } figure { margin: 1em 0; text-align: center; page-break-inside: avoid; } figcaption { font-size: 10pt; font-style: italic; color: #666; margin-top: 4px; } /* Links */ a { color: #0066cc; text-decoration: none; } /* Lists */ ul, ol { margin: 0.5em 0; padding-left: 2em; } li { margin: 0.3em 0; } /* Blockquotes */ blockquote { border-left: 4px solid #2c3e50; margin: 1em 0; padding: 0.5em 1em; background: #f9f9f9; font-style: italic; } /* Cover page */ .cover-page { page-break-after: always; text-align: center; padding-top: 6cm; } .cover-page h1 { font-size: 32pt; border: none; margin-bottom: 0.5cm; } .cover-page .subtitle { font-size: 18pt; color: #666; margin-bottom: 2cm; } .cover-page .author { font-size: 14pt; color: #333; } .cover-page .date { font-size: 12pt; color: #999; margin-top: 1cm; } /* Table of contents */ .toc { page-break-after: always; } .toc h2 { border: none; font-size: 20pt; } .toc ul { list-style: none; padding: 0; } .toc li { padding: 4px 0; border-bottom: 1px dotted #ccc; } .toc a { color: #333; text-decoration: none; } .toc .toc-h2 { font-weight: bold; padding-left: 0; } .toc .toc-h3 { padding-left: 2em; font-size: 10pt; } ``` --- ## 3. ReportLab: Programmatic PDF Generation ReportLab gives you complete control over PDF layout programmatically from Python. ### Basic ReportLab Document ```python """Create a basic PDF with ReportLab.""" from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import cm, mm from reportlab.lib import colors from reportlab.platypus import ( SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, PageBreak, ListFlowable, ListItem ) from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY def create_report(output_path): """Generate a professional report PDF.""" doc = SimpleDocTemplate( output_path, pagesize=A4, rightMargin=2*cm, leftMargin=2*cm, topMargin=2*cm, bottomMargin=2*cm, title="Annual Report 2025", author="Data Analytics Team", ) styles = getSampleStyleSheet() story = [] # Title page title_style = ParagraphStyle( 'CustomTitle', parent=styles['Title'], fontSize=28, leading=34, spaceAfter=20, alignment=TA_CENTER, textColor=colors.HexColor('#1a1a2e'), ) story.append(Spacer(1, 5*cm)) story.append(Paragraph("Annual Report 2025", title_style))
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub