Skip to main content

Agent Skills Framework Extension

PDF Publishing Patterns Skill

When to Use This Skill

Use this skill when implementing pdf publishing patterns patterns in your codebase.

How to Use This Skill

  1. Review the patterns and examples below
  2. Apply the relevant patterns to your implementation
  3. Follow the best practices outlined in this skill

Automated PDF generation from Markdown with custom branding, table of contents, headers/footers, and CI/CD integration.

Core Capabilities

  1. Markdown to PDF - High-quality PDF generation
  2. Custom Branding - Logos, colors, and styling
  3. TOC Generation - Automatic table of contents
  4. Headers/Footers - Page numbers and metadata
  5. Document Merging - Multi-file PDF compilation

Pandoc PDF Generation

#!/usr/bin/env bash
# generate-pdf.sh - Professional PDF generation with Pandoc

set -euo pipefail

# Configuration
INPUT_MD="$1"
OUTPUT_PDF="${2:-output.pdf}"
TEMPLATE_DIR="templates"
ASSETS_DIR="assets"

# Metadata
TITLE="${PDF_TITLE:-Documentation}"
AUTHOR="${PDF_AUTHOR:-CODITECT}"
DATE="${PDF_DATE:-$(date +%Y-%m-%d)}"
VERSION="${PDF_VERSION:-1.0}"

# Styling
MARGIN_TOP="${MARGIN_TOP:-1in}"
MARGIN_BOTTOM="${MARGIN_BOTTOM:-1in}"
MARGIN_LEFT="${MARGIN_LEFT:-1in}"
MARGIN_RIGHT="${MARGIN_RIGHT:-1in}"
FONT_SIZE="${FONT_SIZE:-11pt}"

log_info() { echo "[INFO] $1"; }
log_error() { echo "[ERROR] $1" >&2; }

generate_pdf() {
log_info "Generating PDF: $OUTPUT_PDF"

# Create metadata file
cat > metadata.yaml <<EOF
---
title: "$TITLE"
author: "$AUTHOR"
date: "$DATE"
version: "$VERSION"
documentclass: report
geometry:
- top=$MARGIN_TOP
- bottom=$MARGIN_BOTTOM
- left=$MARGIN_LEFT
- right=$MARGIN_RIGHT
fontsize: $FONT_SIZE
toc: true
toc-depth: 3
numbersections: true
header-includes: |
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[L]{$TITLE}
\fancyhead[R]{v$VERSION}
\fancyfoot[C]{\thepage}
\usepackage{graphicx}
\usepackage{xcolor}
\definecolor{linkcolor}{RGB}{0, 102, 204}
\hypersetup{
colorlinks=true,
linkcolor=linkcolor,
urlcolor=linkcolor
}
---
EOF

# Generate PDF with Pandoc
pandoc \
metadata.yaml \
"$INPUT_MD" \
-o "$OUTPUT_PDF" \
--pdf-engine=xelatex \
--template="$TEMPLATE_DIR/custom.latex" \
--highlight-style=tango \
--listings \
--number-sections \
--toc \
--toc-depth=3 \
--variable mainfont="DejaVu Sans" \
--variable monofont="DejaVu Sans Mono" \
--variable fontsize="$FONT_SIZE" \
--variable geometry:"top=$MARGIN_TOP, bottom=$MARGIN_BOTTOM, left=$MARGIN_LEFT, right=$MARGIN_RIGHT"

rm metadata.yaml

log_info "PDF generated successfully: $OUTPUT_PDF"
}

generate_pdf

Custom LaTeX Template

% custom.latex - Custom Pandoc LaTeX template with branding
\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(papersize)$$papersize$paper,$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$}

% Fonts
\usepackage{fontspec}
\setmainfont{$mainfont$}
\setmonofont{$monofont$}

% Page geometry
\usepackage[
top=$geometry.top$,
bottom=$geometry.bottom$,
left=$geometry.left$,
right=$geometry.right$
]{geometry}

% Headers and footers
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[L]{\small\textit{$title$}}
\fancyhead[R]{\small v$version$}
\fancyfoot[C]{\thepage}
\renewcommand{\headrulewidth}{0.4pt}

% Colors
\usepackage{xcolor}
\definecolor{linkcolor}{RGB}{0, 102, 204}
\definecolor{codebackground}{RGB}{245, 245, 245}

% Hyperlinks
\usepackage{hyperref}
\hypersetup{
colorlinks=true,
linkcolor=linkcolor,
urlcolor=linkcolor,
pdftitle={$title$},
pdfauthor={$author$}
}

% Code highlighting
\usepackage{listings}
\lstset{
backgroundcolor=\color{codebackground},
basicstyle=\ttfamily\small,
breaklines=true,
frame=single,
numbers=left,
numberstyle=\tiny,
showstringspaces=false
}

% Graphics
\usepackage{graphicx}

% Title page
\title{
\vspace{2in}
\includegraphics[width=2in]{assets/logo.png}\\
\vspace{0.5in}
{\Huge\bfseries $title$}\\
\vspace{0.2in}
{\Large Version $version$}
}
\author{$author$}
\date{$date$}

\begin{document}

% Custom title page
\maketitle
\thispagestyle{empty}
\newpage

% Table of contents
$if(toc)$
\tableofcontents
\newpage
$endif$

% Document body
$body$

\end{document}

Python PDF Generator

#!/usr/bin/env python3
"""
PDF generation with custom branding using ReportLab.
Provides more control than Pandoc for complex layouts.
"""

from reportlab.lib.pagesizes import letter, A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak, Image, TableOfContents
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
import markdown
from pathlib import Path

class PDFGenerator:
def __init__(self, output_path: str, title: str, author: str, version: str):
self.output_path = output_path
self.title = title
self.author = author
self.version = version
self.doc = SimpleDocTemplate(
output_path,
pagesize=letter,
topMargin=1*inch,
bottomMargin=1*inch,
leftMargin=1*inch,
rightMargin=1*inch
)
self.story = []
self.styles = getSampleStyleSheet()
self._setup_custom_styles()

def _setup_custom_styles(self):
"""Create custom paragraph styles."""
# Title style
self.styles.add(ParagraphStyle(
name='CustomTitle',
parent=self.styles['Heading1'],
fontSize=24,
textColor=colors.HexColor('#003366'),
spaceAfter=30,
alignment=TA_CENTER
))

# Heading styles
self.styles.add(ParagraphStyle(
name='CustomHeading1',
parent=self.styles['Heading1'],
fontSize=18,
textColor=colors.HexColor('#003366'),
spaceBefore=20,
spaceAfter=12
))

# Code style
self.styles.add(ParagraphStyle(
name='Code',
parent=self.styles['Code'],
fontSize=9,
fontName='Courier',
backColor=colors.HexColor('#f5f5f5'),
borderWidth=1,
borderColor=colors.HexColor('#cccccc'),
borderPadding=5
))

def add_cover_page(self, logo_path: str = None):
"""Add custom cover page."""
if logo_path and Path(logo_path).exists():
logo = Image(logo_path, width=2*inch, height=2*inch)
logo.hAlign = 'CENTER'
self.story.append(Spacer(1, 2*inch))
self.story.append(logo)
self.story.append(Spacer(1, 0.5*inch))

# Title
title = Paragraph(self.title, self.styles['CustomTitle'])
self.story.append(title)
self.story.append(Spacer(1, 0.2*inch))

# Version
version_text = Paragraph(f"Version {self.version}", self.styles['Normal'])
version_text.hAlign = 'CENTER'
self.story.append(version_text)
self.story.append(Spacer(1, 0.5*inch))

# Author
author_text = Paragraph(f"by {self.author}", self.styles['Normal'])
author_text.hAlign = 'CENTER'
self.story.append(author_text)

self.story.append(PageBreak())

def add_table_of_contents(self):
"""Add table of contents."""
toc = TableOfContents()
toc.levelStyles = [
ParagraphStyle(name='TOCHeading1', fontSize=14, leftIndent=20, spaceBefore=5, spaceAfter=5),
ParagraphStyle(name='TOCHeading2', fontSize=12, leftIndent=40, spaceBefore=3, spaceAfter=3),
ParagraphStyle(name='TOCHeading3', fontSize=10, leftIndent=60, spaceBefore=2, spaceAfter=2),
]
self.story.append(Paragraph("Table of Contents", self.styles['CustomHeading1']))
self.story.append(toc)
self.story.append(PageBreak())

def add_markdown_content(self, markdown_path: str):
"""Convert Markdown to PDF paragraphs."""
content = Path(markdown_path).read_text()

# Convert markdown to HTML
html = markdown.markdown(content, extensions=['fenced_code', 'tables', 'toc'])

# Parse HTML and convert to ReportLab elements
# This is simplified - production would use HTMLParser
lines = html.split('\n')

for line in lines:
if line.startswith('<h1>'):
text = line.replace('<h1>', '').replace('</h1>', '')
self.story.append(Paragraph(text, self.styles['CustomHeading1']))
elif line.startswith('<h2>'):
text = line.replace('<h2>', '').replace('</h2>', '')
self.story.append(Paragraph(text, self.styles['Heading2']))
elif line.startswith('<p>'):
text = line.replace('<p>', '').replace('</p>', '')
self.story.append(Paragraph(text, self.styles['Normal']))
self.story.append(Spacer(1, 0.1*inch))
elif line.startswith('<code>'):
text = line.replace('<code>', '').replace('</code>', '')
self.story.append(Paragraph(text, self.styles['Code']))

def add_header_footer(self, canvas, doc):
"""Add header and footer to each page."""
canvas.saveState()

# Header
canvas.setFont('Helvetica', 9)
canvas.drawString(1*inch, doc.height + 1.5*inch, self.title)
canvas.drawRightString(doc.width + 1*inch, doc.height + 1.5*inch, f"v{self.version}")

# Footer
canvas.drawCentredString(doc.width/2 + 1*inch, 0.5*inch, f"Page {doc.page}")

canvas.restoreState()

def generate(self):
"""Generate the PDF."""
self.doc.build(
self.story,
onFirstPage=self.add_header_footer,
onLaterPages=self.add_header_footer
)

# Usage
if __name__ == '__main__':
generator = PDFGenerator(
output_path='documentation.pdf',
title='CODITECT Documentation',
author='AZ1.AI',
version='1.0'
)

generator.add_cover_page(logo_path='assets/logo.png')
generator.add_table_of_contents()
generator.add_markdown_content('README.md')
generator.generate()

print("✅ PDF generated: documentation.pdf")

CI/CD PDF Publishing

# .github/workflows/publish-docs.yml
name: Publish Documentation PDF

on:
push:
branches: [main]
paths:
- 'docs/**/*.md'
release:
types: [published]

jobs:
generate-pdf:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install Pandoc
run: |
wget https://github.com/jgm/pandoc/releases/download/3.1.9/pandoc-3.1.9-1-amd64.deb
sudo dpkg -i pandoc-3.1.9-1-amd64.deb

- name: Install LaTeX
run: |
sudo apt-get update
sudo apt-get install -y texlive-xetex texlive-fonts-recommended

- name: Generate PDF
run: |
bash scripts/generate-pdf.sh docs/README.md documentation.pdf

- name: Upload PDF artifact
uses: actions/upload-artifact@v4
with:
name: documentation-pdf
path: documentation.pdf

- name: Attach to Release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v1
with:
files: documentation.pdf

Usage Examples

Generate Branded PDF from Markdown

Apply pdf-publishing-patterns skill to convert Markdown documentation to professional PDF with custom branding

Multi-Document PDF Compilation

Apply pdf-publishing-patterns skill to merge multiple Markdown files into single PDF with table of contents

Automated PDF Publishing in CI/CD

Apply pdf-publishing-patterns skill to setup GitHub Actions workflow for automatic PDF generation on documentation changes

Success Output

When successfully implementing PDF publishing patterns:

✅ SKILL COMPLETE: pdf-publishing-patterns

Completed:
- [x] Pandoc PDF generation script created with custom LaTeX template
- [x] Metadata YAML configuration generated (title, author, date, version)
- [x] Custom branding applied (logo, colors, headers/footers)
- [x] Table of contents auto-generated with 3-level depth
- [x] Page numbers and metadata headers/footers configured
- [x] CI/CD workflow setup for automated PDF generation on doc changes
- [x] PDF artifact uploaded and attached to releases

Outputs:
- generate-pdf.sh script with Pandoc configuration
- custom.latex template with branding
- Generated PDF file at specified output path
- .github/workflows/publish-docs.yml (if CI/CD integration)
- PDF artifact attached to GitHub releases (if release workflow)

Completion Checklist

Before marking this skill as complete, verify:

  • Pandoc and XeLaTeX installed and functional
  • generate-pdf.sh script created with metadata configuration
  • Custom LaTeX template includes branding (logo, colors, fonts)
  • Table of contents generated with proper depth (toc-depth: 3)
  • Headers show document title and version
  • Footers show page numbers
  • Code blocks styled with highlighting and line numbers
  • Hyperlinks colored and functional in PDF
  • PDF generates without LaTeX errors
  • Output PDF verified to match expected formatting
  • CI/CD workflow (if applicable) triggers on docs/** changes
  • PDF artifact uploads successfully to releases

Failure Indicators

This skill has FAILED if:

  • ❌ Pandoc not installed or wrong version (need 3.1.9+)
  • ❌ XeLaTeX not available (texlive-xetex package missing)
  • ❌ LaTeX compilation errors (syntax errors in template)
  • ❌ Table of contents missing from generated PDF
  • ❌ Logo image not found or fails to render
  • ❌ Headers/footers not appearing on pages
  • ❌ Code blocks not highlighted or improperly formatted
  • ❌ Hyperlinks not working in PDF (broken links)
  • ❌ CI/CD workflow fails to generate PDF artifact
  • ❌ PDF not attached to GitHub releases

When NOT to Use

Do NOT use this skill when:

  • Publishing HTML documentation only (no PDF needed)
  • Interactive documentation required (use Docusaurus, GitBook)
  • Frequent updates with version control (use web-based docs)
  • Documentation needs embedded videos or interactive elements
  • Target audience prefers web over downloadable PDFs

Use alternatives instead:

  • For HTML docs → Use Docusaurus, GitBook, MkDocs
  • For interactive elements → Use web-based documentation platforms
  • For frequently updated docs → Use hosted documentation sites
  • For API docs → Use Swagger/OpenAPI, Redoc
  • For live collaboration → Use Notion, Confluence

Anti-Patterns (Avoid)

Anti-PatternProblemSolution
Hardcoding metadata in templateMust edit template for each docUse YAML metadata variables
Missing logo path validationBuild fails if logo not foundCheck file exists before Pandoc call
No CI/CD cachingSlow builds, installs Pandoc every timeCache Pandoc/LaTeX in workflow
Ignoring LaTeX errorsSilent failures, incomplete PDFsCheck exit codes, fail on errors
Fixed margins for all docsPoor formatting for different contentUse environment variables for margins
No version in filenameOverwrites previous PDFsInclude version: documentation-v1.2.pdf

Principles

This skill embodies CODITECT automation principles:

  • #1 Recycle → Extend → Re-Use → Create - Reuses custom.latex template across all PDF generations
  • #3 Self-Provisioning - CI/CD workflow auto-installs Pandoc and LaTeX dependencies
  • #4 Keep It Simple - Clear Bash script with environment variables for configuration
  • #5 Eliminate Ambiguity - YAML frontmatter explicitly defines all metadata
  • #6 Clear, Understandable, Explainable - Comments in scripts and templates explain each section
  • #10 Complete Execution - End-to-end automation from Markdown to published PDF

Full Standard: CODITECT-STANDARD-AUTOMATION.md


Integration Points

  • documentation-patterns - Documentation standards
  • cicd-pipeline-design - Automated publishing
  • project-organization-patterns - Documentation structure