
Pandoc Pdf Generation
- 503 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
pandoc-pdf-generation is a Claude Code skill that converts Markdown to professional PDFs via Pandoc and XeLaTeX for developers who need numbered sections, bibliographies, and production-tuned LaTeX output.
About
pandoc-pdf-generation is a documentation build skill in terrilyca/cc-skills doc-tools plugin for Pandoc PDF workflows. It ships `build-pdf.sh` and `table-spacing-template.tex` assets supporting landscape default, `--portrait`, `--monospace` for ASCII diagrams, and `--hide-details` to strip `<details>` blocks from print output. Quick commands use `--pdf-engine=xelatex`, `--toc`, `--number-sections`, and DejaVu fonts; the universal script auto-detects single `.md` inputs, `references.bib`, and CSL files with pre-flight and post-build validation. Seven reference guides cover markdown structure, YAML front matter, LaTeX parameters, bibliographies, document patterns, and troubleshooting. The skill mandates graph-easy via `itp:graph-easy` for diagrams—not hand-typed ASCII—and includes printing workflows with `lpr` duplex flags after user PDF review. Developers reach for it when agents must render architecture docs, ADRs, or handouts from repo markdown without broken tables or duplicate section numbers.
- pandoc-pdf-generation
Pandoc Pdf Generation by the numbers
- 503 all-time installs (skills.sh)
- +6 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #814 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill pandoc-pdf-generationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 503 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
How do you convert Markdown to PDF with Pandoc?
Use pandoc-pdf-generation for development tasks
Who is it for?
Developers generating print-ready technical PDFs from Markdown with Pandoc, XeLaTeX, and repeatable build-pdf.sh automation.
Skip if: Teams needing interactive HTML docs only or PDFs without installing Pandoc, XeLaTeX, and DejaVu fonts locally.
When should I use this skill?
User asks to convert markdown to PDF, run pandoc with xelatex, print a technical document, or fix LaTeX table/TOC issues.
What you get
XeLaTeX-rendered PDF with TOC, section numbers, optional bibliography, and validated page count/file size.
- PDF document
- Optional bibliography-rendered output
- Validated build logs
By the numbers
- Bundles 2 production assets: build-pdf.sh and table-spacing-template.tex
- Includes 7 reference markdown guides under references/
- Supports 4 CLI modes: default landscape, --portrait, --monospace, --hide-details
Files
Pandoc PDF Generation
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
Overview
Generate professional PDF documents from Markdown using Pandoc with the XeLaTeX engine. This skill covers automatic section numbering, table of contents, bibliography management, LaTeX customization, and common troubleshooting patterns learned through production use.
When to Use This Skill
Use this skill when:
- Converting Markdown to PDF with professional formatting requirements
- Needing automatic section numbering and table of contents
- Managing citations and bibliographies without manual duplication
- Controlling table formatting and page breaks in LaTeX output
- Building automated PDF generation workflows
Quick Start: Universal Build Script
Single Source of Truth Pattern
This skill provides production-proven assets in ${CLAUDE_PLUGIN_ROOT}/skills/pandoc-pdf-generation/assets/:
table-spacing-template.tex- Production-tuned LaTeX preamble (booktabs, colortbl, ToC fixes)build-pdf.sh- Universal auto-detecting build script
From Any Project
/usr/bin/env bash << 'DETECT_EOF'
# Create symlink once per project (git-friendly)
ln -s ${CLAUDE_PLUGIN_ROOT}/skills/pandoc-pdf-generation/assets/build-pdf.sh build-pdf.sh
# Auto-detect single .md file in directory (landscape default)
./build-pdf.sh
# Portrait mode
./build-pdf.sh --portrait document.md
# Monospace font for ASCII diagrams
./build-pdf.sh --monospace diagrams.md
# Explicit input/output
./build-pdf.sh input.md output.pdf
DETECT_EOFOptions:
| Flag | Description |
|---|---|
--landscape | Landscape orientation (default) |
--portrait | Portrait orientation |
--monospace | Use DejaVu Sans Mono - ideal for ASCII diagrams |
--hide-details | Hide <details> blocks (e.g., graph-easy source) from PDF |
-h, --help | Show help message |
Features:
- ✅ Auto-detects input file (if single .md exists)
- ✅ Auto-detects bibliography (
references.bib) and CSL files - ✅ Always uses production-proven LaTeX preamble from skill
- ✅ Pre-flight checks (pandoc, xelatex, files exist)
- ✅ Post-build validation (file size, page count)
- ✅ Code blocks stay on same page (no splitting across pages)
- ✅ Lua filter to hide
<details>blocks from PDF output
Landscape PDF (Quick Command)
For landscape PDFs with blue hyperlinks (no build-pdf.sh dependency):
pandoc file.md -o file.pdf \
--pdf-engine=xelatex \
-V geometry:a4paper,landscape \
-V geometry:margin=1in \
-V fontsize=11pt \
-V mainfont="DejaVu Sans" \
-V colorlinks=true \
-V linkcolor=blue \
-V urlcolor=blue \
--toc --toc-depth=2 \
--number-sectionsUse landscape for: Wide data tables, comparison matrices, technical docs with code blocks.
Manual Command (With LaTeX Preamble)
/usr/bin/env bash << 'SKILL_SCRIPT_EOF'
pandoc document.md \
-o document.pdf \
--pdf-engine=xelatex \
--toc \
--toc-depth=3 \
--number-sections \
-V geometry:margin=1in \
-V mainfont="DejaVu Sans" \
-H ${CLAUDE_PLUGIN_ROOT}/skills/pandoc-pdf-generation/assets/table-spacing-template.tex
SKILL_SCRIPT_EOF---
ASCII Diagrams: Always Use graph-easy
CRITICAL: Never manually type ASCII diagrams. Always use the itp:graph-easy skill.
Manual ASCII art causes alignment issues in PDFs. The graph-easy skill ensures:
- Proper boxart character alignment
- Consistent spacing
- Reproducible output
# Invoke the skill for general diagrams
Skill(itp:graph-easy)
# For ADR architecture diagrams
Skill(itp:adr-graph-easy-architect)Also important: Keep annotations OUTSIDE code blocks. Don't add inline comments like # contains: file1, file2 inside diagram code blocks - they break alignment.
---
Hiding Content for PDF Output
Use --hide-details to remove <details> blocks from PDF output. This is useful when:
- graph-easy source blocks: Keep source in markdown for diagram regeneration, but hide from printed PDFs
- Technical implementation notes: Show in web/markdown view, hide from printed handouts
- Collapsible sections: HTML
<details>tags don't render as collapsible in PDF
Usage:
./build-pdf.sh --hide-details document.mdMarkdown pattern:
````markdown
My Section
┌─────┐ ┌─────┐
│ Box │ ──> │ Box │
└─────┘ └─────┘````
<details> <summary>graph-easy source</summary>
[Box] -> [Box]</details>
With `--hide-details`, the entire `<details>` block is stripped from PDF output while remaining visible in markdown/HTML.
---
## Verification Checklist
Before considering a PDF "done", verify:
**Pre-Generation:**
- [ ] No manual section numbering in markdown (use `--number-sections`)
- [ ] All ASCII diagrams generated via `itp:graph-easy` skill
- [ ] Annotations are outside code blocks, not inside
**Post-Generation:**
- [ ] Open PDF and visually inspect each page
- [ ] Verify diagrams don't break across pages
- [ ] Check section numbering is correct (no "1. 1. Title" duplication)
- [ ] Confirm bullet lists render as bullets, not inline dashes
**Pre-Print:**
- [ ] Get user approval before printing
- [ ] Confirm orientation preference (landscape/portrait)
- [ ] Confirm duplex preference (one-sided/two-sided)
---
## Printing Workflow
Always let the user review the PDF before printing.
**Open for review:**
open output.pdf
**Print one-sided (simplex):**
lpr -P "PRINTER_NAME" -o Duplex=None output.pdf
**Print two-sided (duplex):**
lpr -P "PRINTER_NAME" -o Duplex=DuplexNoTumble output.pdf # Long-edge binding lpr -P "PRINTER_NAME" -o Duplex=DuplexTumble output.pdf # Short-edge binding
**Find printer name:**
lpstat -p -d
**Never print without user approval** - this wastes paper if issues exist.
---
## Reference Documentation
For detailed information, see:
- [Core Development Principles](./references/core-principles.md) - **START HERE** - Universal principles learned from production failures
- [Markdown for PDF](./references/markdown-for-pdf.md) - Markdown structure patterns for clean landscape PDFs
- [YAML Front Matter Structure](./references/yaml-structure.md) - YAML metadata patterns
- [LaTeX Customization](./references/latex-parameters.md) - Preamble and table formatting
- [Bibliography & Citations](./references/bibliography-citations.md) - BibTeX and CSL styles
- [Document Patterns](./references/document-patterns.md) - Document type templates
- [Troubleshooting](./references/troubleshooting-pandoc.md) - Common issues and fixes
---
## Troubleshooting
| Issue | Cause | Solution |
| ----------------------------- | ---------------------------- | ------------------------------------------------- |
| Font not found | DejaVu Sans not installed | `brew install font-dejavu` |
| xelatex not found | MacTeX not installed | `brew install --cask mactex` |
| Table breaks across pages | Missing longtable package | Include table-spacing-template.tex preamble |
| Double section numbers | Manual numbering in markdown | Remove manual numbers, use --number-sections only |
| ASCII diagram misaligned | Manual ASCII art | Use graph-easy skill for all diagrams |
| Bullet list renders as dashes | Markdown formatting issue | Check for proper blank lines before lists |
| Bibliography not rendering | Missing references.bib | Create .bib file or remove --bibliography flag |
| PDF file size too large | Embedded fonts | Use --pdf-engine-opt=-dEmbedAllFonts=false |
## Post-Execution Reflection
After this skill completes, check before closing:
1. **Did the command succeed?** — If not, fix the instruction or error table that caused the failure.
2. **Did parameters or output change?** — If the underlying tool's interface drifted, update Usage examples and Parameters table to match.
3. **Was a workaround needed?** — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.
Only update if the issue is real and reproducible — not speculative.
#!/bin/bash
# Production-Ready PDF Build Script
# Demonstrates error handling, validation, and logging
set -e # Exit on any error
set -u # Error on undefined variables
set -o pipefail # Catch errors in pipes
# ==============================================================================
# Configuration
# ==============================================================================
INPUT_FILE="DOCUMENT.md"
OUTPUT_FILE="DOCUMENT.pdf"
BIBLIOGRAPHY="references.bib"
LATEX_PREAMBLE="table-spacing.tex"
LOG_FILE="build-pdf.log"
# ==============================================================================
# Log Rotation - prevent unbounded growth
# ADR: /docs/adr/2025-12-07-idempotency-backup-traceability.md
# ==============================================================================
LOG_ROTATION_KEEP_COUNT=5
rotate_log() {
local log_file="$1"
local keep_count="${2:-$LOG_ROTATION_KEEP_COUNT}"
if [ -f "$log_file" ]; then
mv "$log_file" "${log_file}.$(date +%s)"
# Keep only last N logs
ls -t "${log_file}."* 2>/dev/null | tail -n +$((keep_count + 1)) | xargs rm -f 2>/dev/null || true
fi
}
# Rotate log before starting new build
rotate_log "$LOG_FILE" "$LOG_ROTATION_KEEP_COUNT"
# ==============================================================================
# Color Output
# ==============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}✓${NC} $1" | tee -a "$LOG_FILE"; }
log_warn() { echo -e "${YELLOW}⚠${NC} $1" | tee -a "$LOG_FILE"; }
log_error() { echo -e "${RED}✗${NC} $1" | tee -a "$LOG_FILE"; }
# ==============================================================================
# Pre-flight Checks
# ==============================================================================
echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting PDF build" > "$LOG_FILE"
# Check if input file exists
if [[ ! -f "$INPUT_FILE" ]]; then
log_error "Input file not found: $INPUT_FILE"
exit 1
fi
# Check if Pandoc is installed
if ! command -v pandoc &> /dev/null; then
log_error "Pandoc is not installed. Install with: brew install pandoc"
exit 1
fi
# Check Pandoc version
PANDOC_VERSION=$(pandoc --version | head -n1)
log_info "Using $PANDOC_VERSION"
# Check if XeLaTeX is available
if ! command -v xelatex &> /dev/null; then
log_error "XeLaTeX not found. Install MacTeX: brew install --cask mactex"
exit 1
fi
# Check optional files
[[ -f "$BIBLIOGRAPHY" ]] && log_info "Bibliography file found" || log_warn "Bibliography file not found (optional)"
[[ -f "$LATEX_PREAMBLE" ]] && log_info "LaTeX preamble found" || log_warn "LaTeX preamble not found (optional)"
# ==============================================================================
# Build PDF
# ==============================================================================
log_info "Generating PDF: $OUTPUT_FILE"
# Backup existing PDF if it exists
if [[ -f "$OUTPUT_FILE" ]]; then
BACKUP_FILE="${OUTPUT_FILE}.backup-$(date +%Y%m%d-%H%M%S)"
cp "$OUTPUT_FILE" "$BACKUP_FILE"
log_info "Backed up existing PDF to: $BACKUP_FILE"
fi
# Build command with optional flags
PANDOC_CMD=(
pandoc "$INPUT_FILE"
-o "$OUTPUT_FILE"
--pdf-engine=xelatex
--toc
--toc-depth=3
--number-sections
-V mainfont="DejaVu Sans"
-V geometry:margin=1in
-V toc-title="Table of Contents"
)
# Add bibliography if available
if [[ -f "$BIBLIOGRAPHY" ]]; then
PANDOC_CMD+=(--citeproc --bibliography="$BIBLIOGRAPHY")
fi
# Add LaTeX preamble if available
if [[ -f "$LATEX_PREAMBLE" ]]; then
PANDOC_CMD+=(-H "$LATEX_PREAMBLE")
fi
# Execute Pandoc
if "${PANDOC_CMD[@]}" >> "$LOG_FILE" 2>&1; then
log_info "PDF generated successfully"
else
log_error "PDF generation failed. Check $LOG_FILE for details"
exit 1
fi
# ==============================================================================
# Post-build Validation
# ==============================================================================
# Check if output file exists
if [[ ! -f "$OUTPUT_FILE" ]]; then
log_error "Output file was not created"
exit 1
fi
# Get file size
FILE_SIZE=$(ls -lh "$OUTPUT_FILE" | awk '{print $5}')
log_info "PDF size: $FILE_SIZE"
# Get page count (requires pdfinfo)
if command -v pdfinfo &> /dev/null; then
PAGE_COUNT=$(pdfinfo "$OUTPUT_FILE" 2>/dev/null | grep "^Pages:" | awk '{print $2}')
if [[ -n "$PAGE_COUNT" ]]; then
log_info "Page count: $PAGE_COUNT pages"
fi
else
log_warn "pdfinfo not available (install with: brew install poppler)"
fi
# ==============================================================================
# Success Summary
# ==============================================================================
echo ""
echo "======================================"
log_info "Build completed successfully"
echo "======================================"
echo "Output: $OUTPUT_FILE"
echo "Size: $FILE_SIZE"
[[ -n "${PAGE_COUNT:-}" ]] && echo "Pages: $PAGE_COUNT"
echo "Log: $LOG_FILE"
echo "======================================"
echo ""
log_info "To view: open $OUTPUT_FILE"
#!/usr/bin/env bash
# Universal PDF Build Script for Pandoc
# Usage: ./build-pdf.sh [OPTIONS] [input.md] [output.pdf]
#
# Options:
# --landscape Landscape orientation (default)
# --portrait Portrait orientation
# --monospace Use monospace font (DejaVu Sans Mono) - ideal for ASCII diagrams
# --hide-details Hide <details> blocks (e.g., graph-easy source) from PDF output
# -h, --help Show this help message
#
# If no input file provided, looks for single .md file in current directory
set -euo pipefail
# ==============================================================================
# Configuration
# ==============================================================================
# Resolve the actual directory of this script (works with symlinks)
SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")" && pwd)"
LATEX_PREAMBLE="$SCRIPT_DIR/table-spacing-template.tex"
HIDE_DETAILS_FILTER="$SCRIPT_DIR/hide-details-for-pdf.lua"
# Defaults
ORIENTATION="landscape"
FONT="DejaVu Sans"
USE_HIDE_DETAILS=""
# Color output
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
log_info() { echo -e "${GREEN}✓${NC} $1"; }
log_warn() { echo -e "${YELLOW}⚠${NC} $1"; }
log_error() { echo -e "${RED}✗${NC} $1"; }
show_help() {
echo "Usage: $0 [OPTIONS] [input.md] [output.pdf]"
echo ""
echo "Options:"
echo " --landscape Landscape orientation (default)"
echo " --portrait Portrait orientation"
echo " --monospace Use monospace font (DejaVu Sans Mono) - ideal for ASCII diagrams"
echo " --hide-details Hide <details> blocks (e.g., graph-easy source) from PDF output"
echo " -h, --help Show this help message"
echo ""
echo "If no input file provided, auto-detects single .md file in current directory."
echo ""
echo "Examples:"
echo " $0 # Auto-detect, landscape"
echo " $0 --portrait doc.md # Portrait mode"
echo " $0 --monospace diagrams.md # Monospace font for ASCII art"
echo " $0 --hide-details doc.md # Hide <details> blocks in PDF"
echo " $0 doc.md output.pdf # Explicit input/output"
}
# ==============================================================================
# Parse Arguments
# ==============================================================================
POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do
case $1 in
--landscape)
ORIENTATION="landscape"
shift
;;
--portrait)
ORIENTATION="portrait"
shift
;;
--monospace)
FONT="DejaVu Sans Mono"
shift
;;
--hide-details)
USE_HIDE_DETAILS="yes"
shift
;;
-h|--help)
show_help
exit 0
;;
-*)
log_error "Unknown option: $1"
show_help
exit 1
;;
*)
POSITIONAL_ARGS+=("$1")
shift
;;
esac
done
# Restore positional parameters
set -- "${POSITIONAL_ARGS[@]}"
# ==============================================================================
# Input Detection
# ==============================================================================
# If input file provided as argument
if [[ -n "${1:-}" ]]; then
INPUT_FILE="$1"
# Otherwise, auto-detect single .md file in current directory
else
MD_FILES=(*.md)
if [[ ${#MD_FILES[@]} -eq 0 || ! -f "${MD_FILES[0]}" ]]; then
log_error "No Markdown files found in current directory"
show_help
exit 1
elif [[ ${#MD_FILES[@]} -gt 1 ]]; then
log_error "Multiple Markdown files found. Please specify which one:"
printf ' - %s\n' "${MD_FILES[@]}"
show_help
exit 1
fi
INPUT_FILE="${MD_FILES[0]}"
fi
# Verify input file exists
if [[ ! -f "$INPUT_FILE" ]]; then
log_error "Input file not found: $INPUT_FILE"
exit 1
fi
# If output file provided as argument
if [[ -n "${2:-}" ]]; then
OUTPUT_FILE="$2"
else
# Auto-generate output filename from input
OUTPUT_FILE="${INPUT_FILE%.md}.pdf"
fi
log_info "Input: $INPUT_FILE"
log_info "Output: $OUTPUT_FILE"
log_info "Orientation: $ORIENTATION"
log_info "Font: $FONT"
if [[ -n "$USE_HIDE_DETAILS" ]]; then
log_info "Hide details: enabled"
fi
# ==============================================================================
# Pre-flight Checks
# ==============================================================================
# Check if Pandoc is installed
if ! command -v pandoc &> /dev/null; then
log_error "Pandoc is not installed. Install with: brew install pandoc"
exit 1
fi
# Check if XeLaTeX is available
if ! command -v xelatex &> /dev/null; then
log_error "XeLaTeX not found. Install MacTeX: brew install --cask mactex"
exit 1
fi
# Check if LaTeX preamble exists
if [[ ! -f "$LATEX_PREAMBLE" ]]; then
log_error "LaTeX preamble not found: $LATEX_PREAMBLE"
exit 1
fi
# ==============================================================================
# Build PDF
# ==============================================================================
log_info "Generating PDF with table of contents..."
# Check for local or global bibliography
BIBLIOGRAPHY=""
if [[ -f "references.bib" ]]; then
BIBLIOGRAPHY="--citeproc --bibliography=references.bib"
log_info "Using bibliography: references.bib"
fi
# Check for CSL style
CSL=""
if [[ -f "chicago-note-bibliography.csl" ]]; then
CSL="--csl=chicago-note-bibliography.csl"
log_info "Using citation style: chicago-note-bibliography.csl"
fi
# Build geometry string based on orientation
if [[ "$ORIENTATION" == "landscape" ]]; then
GEOMETRY="a4paper,landscape"
else
GEOMETRY="a4paper"
fi
# Build Lua filter option if requested
LUA_FILTER=""
if [[ -n "$USE_HIDE_DETAILS" ]]; then
if [[ -f "$HIDE_DETAILS_FILTER" ]]; then
LUA_FILTER="--lua-filter=$HIDE_DETAILS_FILTER"
log_info "Using Lua filter: hide-details-for-pdf.lua"
else
log_warn "Hide details filter not found: $HIDE_DETAILS_FILTER"
fi
fi
# Build command - use array for optional arguments to avoid SC2086
PANDOC_OPTS=()
[[ -n "$LUA_FILTER" ]] && PANDOC_OPTS+=("$LUA_FILTER")
[[ -n "$BIBLIOGRAPHY" ]] && PANDOC_OPTS+=("$BIBLIOGRAPHY")
[[ -n "$CSL" ]] && PANDOC_OPTS+=("$CSL")
pandoc "$INPUT_FILE" \
-o "$OUTPUT_FILE" \
--pdf-engine=xelatex \
--toc \
--toc-depth=3 \
--number-sections \
-V mainfont="$FONT" \
-V monofont="DejaVu Sans Mono" \
-V geometry:"$GEOMETRY" \
-V geometry:margin=1in \
-V toc-title="Table of Contents" \
-H "$LATEX_PREAMBLE" \
"${PANDOC_OPTS[@]}"
# ==============================================================================
# Post-build Validation
# ==============================================================================
if [[ ! -f "$OUTPUT_FILE" ]]; then
log_error "PDF generation failed - output file not created"
exit 1
fi
# Use stat for portable file size (avoids SC2012 ls warning)
if [[ "$OSTYPE" == "darwin"* ]]; then
FILE_SIZE=$(stat -f%z "$OUTPUT_FILE" | awk '{printf "%.1fK", $1/1024}')
else
FILE_SIZE=$(stat --printf="%s" "$OUTPUT_FILE" | awk '{printf "%.1fK", $1/1024}')
fi
log_info "PDF generated: $OUTPUT_FILE ($FILE_SIZE)"
# Get page count if pdfinfo available
if command -v pdfinfo &> /dev/null; then
PAGE_COUNT=$(pdfinfo "$OUTPUT_FILE" 2>/dev/null | grep "^Pages:" | awk '{print $2}')
if [[ -n "$PAGE_COUNT" ]]; then
log_info "Page count: $PAGE_COUNT pages"
fi
fi
echo ""
echo "Build complete!"
echo " View: open $OUTPUT_FILE"
--[[
hide-details-for-pdf.lua
Pandoc Lua filter to hide HTML <details> blocks when generating PDF
PURPOSE:
When converting Markdown to PDF, this filter removes <details>...</details>
blocks entirely. These blocks often contain source code (like graph-easy
source) that is useful for regeneration in markdown but clutters PDF output.
USAGE:
pandoc input.md -o output.pdf --lua-filter=hide-details-for-pdf.lua
The filter checks FORMAT to only apply when outputting to LaTeX/PDF,
preserving <details> blocks for HTML output.
AUTHOR: Claude Code (cc-skills plugin)
VERSION: 1.0.0
]]
-- Track whether we're inside a details block
local in_details = false
local details_depth = 0
-- Handle raw HTML blocks
function RawBlock(el)
if FORMAT:match("latex") or FORMAT:match("pdf") then
if el.format == "html" then
-- Check for opening <details> tag
if el.text:match("<details[^>]*>") then
details_depth = details_depth + 1
in_details = true
return {} -- Remove the opening tag
end
-- Check for closing </details> tag
if el.text:match("</details>") then
details_depth = details_depth - 1
if details_depth <= 0 then
in_details = false
details_depth = 0
end
return {} -- Remove the closing tag
end
-- If we're inside details, remove the content
if in_details then
return {}
end
end
end
return el
end
-- Handle inline HTML elements
function RawInline(el)
if FORMAT:match("latex") or FORMAT:match("pdf") then
if el.format == "html" then
if el.text:match("<details[^>]*>") or el.text:match("</details>") or
el.text:match("<summary[^>]*>") or el.text:match("</summary>") then
return {}
end
end
end
return el
end
-- Handle blocks that might be inside details
function Block(el)
if FORMAT:match("latex") or FORMAT:match("pdf") then
if in_details then
return {}
end
end
return el
end
-- Handle divs that Pandoc might create from details
function Div(el)
if FORMAT:match("latex") or FORMAT:match("pdf") then
-- Check for details class or data attribute
if el.classes:includes("details") then
return {}
end
end
return el
end
% Better table row spacing for PDF generation
\usepackage{booktabs}
% More compact spacing to fit tables on single page
\renewcommand{\arraystretch}{1.0}
% Minimal extra padding at top of cells
\usepackage{array}
\setlength{\extrarowheight}{2pt}
% Let booktabs handle spacing around rules with default values
% (toprule, midrule, bottomrule have built-in spacing)
% Space before and after entire table
\usepackage{longtable}
\setlength{\LTpre}{8pt}
\setlength{\LTpost}{12pt}
% Compact column spacing to fit tables on one page
\setlength{\tabcolsep}{6pt}
% ==============================================================================
% Reduce Table Font Size
% ==============================================================================
% Automatically use smaller font for all table content (Pandoc generates longtables)
% Options: \small (~90%), \footnotesize (~80%), \scriptsize (~70%), \tiny (~50%)
\usepackage{etoolbox}
\AtBeginEnvironment{longtable}{\footnotesize}
% Note: This affects table content only, not captions
% To also reduce caption size, add: \usepackage[font=footnotesize]{caption}
% Thicker rules for better visual separation between rows
\setlength{\heavyrulewidth}{1.0pt}
\setlength{\lightrulewidth}{0.4pt}
\setlength{\cmidrulewidth}{0.4pt}
% Colors for potential alternating rows
\usepackage{colortbl}
\usepackage{xcolor}
\definecolor{lightgray}{gray}{0.96}
% Page break control for tables
\usepackage{needspace}
% Discourage page breaks within tables
% Increase chunk size to process more rows before considering page break
\LTchunksize=100
% Keep table captions with table content
\setlength{\LTcapwidth}{\textwidth}
% Discourage page breaks immediately before or after tables
% by increasing penalties around table environments
\widowpenalty=10000
\clubpenalty=10000
% ==============================================================================
% Table of Contents Spacing Fix
% ==============================================================================
% Problem: Multi-digit subsection numbers (2.5.10, 2.5.11) overlap with titles
% Solution: Increase allocated space for section numbers using tocloft package
\usepackage{tocloft}
% Increase width allocated for section numbers in ToC
% Default values are too small for documents with many subsections
\setlength{\cftsecnumwidth}{2.5em} % Section numbers (1, 2, 3)
\setlength{\cftsubsecnumwidth}{3.5em} % Subsection numbers (2.1, 2.5.10) ← Fix
\setlength{\cftsubsubsecnumwidth}{4.5em} % Subsubsection numbers (2.5.10.1)
% Optional: Add spacing between ToC entries for better readability
% \setlength{\cftbeforesecskip}{6pt}
% \setlength{\cftbeforesubsecskip}{3pt}
% ==============================================================================
% Footnotes Configuration
% ==============================================================================
% Pandoc renders footnotes correctly by default with xelatex.
% Footnotes appear at the bottom of each page (standard professional formatting).
% Do NOT use the endnotes package with Pandoc unless you add a Lua filter
% to call \theendnotes at document end.
% ==============================================================================
% Code Block Page Break Prevention
% ==============================================================================
% Problem: Code blocks (especially ASCII diagrams) split across pages
% Solution: Configure fancyvrb to keep Verbatim environments on same page
%
% Note: This works for moderately-sized code blocks. For very tall diagrams
% that exceed page height, use \newpage in markdown BEFORE the code block.
%
% Reference: Our conversation learnings (Dec 2025) - diagrams breaking across pages
\usepackage{fancyvrb}
\fvset{samepage=true}
% Also wrap Pandoc's Shaded environment (syntax-highlighted code) in samepage
\BeforeBeginEnvironment{Shaded}{\begin{samepage}}
\AfterEndEnvironment{Shaded}{\end{samepage}}
% ==============================================================================
% Text Alignment
% ==============================================================================
% Use ragged-right (left-aligned) instead of justified text
% Justified text can create awkward spacing; ragged-right is more readable
\raggedright
% ==============================================================================
% Professional Hyperlink Styling - Underlined Links for Cross-Viewer Compatibility
% ==============================================================================
% Problem: Skim and Preview.app on macOS don't render colorlinks=true properly
% Solution: Use underlined links with colored borders instead
% Reference: https://tex.stackexchange.com/questions/26071/
% Configure hyperref AFTER Pandoc loads it
% \AtBeginDocument executes after all package loading is complete
% This overrides Pandoc's default hyperref settings without breaking layout
\AtBeginDocument{%
\hypersetup{%
colorlinks=false,% % Required for borders/underlines to work
pdfborderstyle={/S/U/W 1},% % /S/U = Underline style, /W 1 = 1pt width
urlbordercolor={0 0.3 0.6},% % Navy blue for URL links (RGB 0-1 scale)
linkbordercolor={0 0.3 0.6},% % Navy blue for internal links
citebordercolor={0 0.3 0.6}% % Navy blue for citations
}%
}
% ==============================================================================
% Table of Contents Pagination
% ==============================================================================
% Force page break after TOC and before first section
% Pandoc's TOC generation doesn't respond to standard \tableofcontents patching
% Solution: Add page break before the very first section
\AtBeginDocument{%
% Save the original \section command
\let\oldsection\section
% Redefine \section to add clearpage before FIRST section only
\renewcommand{\section}{%
\clearpage % Page break before first section (after TOC)
\global\let\section\oldsection % Restore original \section for subsequent sections
\oldsection % Call the original section command
}%
}
Skill: Pandoc PDF Generation
Bibliography and Citations Management
The Problem with Manual References
Duplicating footnote references is error-prone:
In the main text[^ref1]
## References
[^ref1]: Source citation here ← Footnote definition
1. Source citation here ← Manual duplication for References sectionProblems:
- Must maintain two copies of each citation
- Updates require changing multiple locations
- Risk of inconsistencies between footnotes and References
Automated Solution: Pandoc --citeproc
Step 1: Create a bibliography file (references.bib)
@misc{nsw-law-2025,
title = {How to Prepare for the 2025 NSW Strata Law Changes},
url = {https://netstrata.com.au/how-to-prepare-for-the-2025-nsw-strata-law-changes/},
year = {2025},
month = {October}
}
@misc{mcgrathnicol-2025,
title = {McGrathNicol Review: Key Updates & Improvements},
url = {https://netstrata.com.au/mcgrathnicol-review-key-updates-improvements/},
year = {2025},
month = {May}
}Step 2: Use citations in Markdown
New reforms came into effect October 27, 2025 [@nsw-law-2025].
The McGrathNicol review shows progress [@mcgrathnicol-2025].Step 3: Build with --citeproc
pandoc document.md \
-o document.pdf \
--pdf-engine=xelatex \
--citeproc \
--bibliography=references.bib \
--csl=chicago-author-date.cslResult:
- Citations automatically formatted in text
- Bibliography automatically generated at end
- Single source of truth for all references
- Change citation style by swapping CSL file
CSL Citation Styles
Download styles from:
Common styles:
chicago-author-date.csl(default if not specified)apa.cslmla.cslieee.csl
Alternative: YAML References
For smaller documents, embed references in YAML:
---
title: Document Title
references:
- id: nsw-law-2025
title: How to Prepare for the 2025 NSW Strata Law Changes
URL: https://netstrata.com.au/...
issued:
year: 2025
month: 10
---Skill: Pandoc PDF Generation
Core Development Principles for PDF Generation
Overview
This document captures production-learned principles from PDF generation failures, elevated to universal development practices applicable beyond just PDF workflows.
Canonical Implementations Over Ad-Hoc Solutions
Principle
When tooling or workflows exist in ~/.claude/, always use the canonical implementation rather than creating ad-hoc alternatives.
Why This Matters
- Canonical implementations encode production-tested configurations
- They capture edge cases discovered through actual usage
- Example: LaTeX
\raggedrightrequirement for proper bullet list rendering
- Ad-hoc solutions inevitably miss critical details
- Quick inline commands seem to work initially
- Hidden edge cases only surface in production
- Example: PDF bullet lists rendered as inline text with justified alignment
- Maintenance burden multiplies with each ad-hoc variant
- Every custom script needs independent updates
- Bug fixes don't propagate automatically
- Knowledge fragments across codebase
Examples
✅ Correct - Use Canonical Implementation:
# Invoke the skill which uses production-proven build script
Skill(doc-tools:pandoc-pdf-generation)
# Or use the bundled build script directly (relative to skill location)
./assets/build-pdf.sh input.md output.pdf❌ Wrong - Ad-Hoc Pandoc Command:
# Missing critical LaTeX preamble (\raggedright), will break bullet lists
pandoc input.md -o output.pdf --pdf-engine=xelatex --toc✅ Correct - Symlink for Project Use:
# Create symlink to canonical script (git-friendly)
# Marketplace plugins install to: ~/.claude/plugins/cache/cc-skills/plugins/doc-tools/
PLUGIN_PATH=~/.claude/plugins/cache/cc-skills/plugins/doc-tools
ln -s "$PLUGIN_PATH/skills/pandoc-pdf-generation/assets/build-pdf.sh" build-pdf.sh
./build-pdf.sh❌ Wrong - Copy-Paste and Modify:
# Creates divergence, misses future updates to canonical version
cp path/to/build-pdf.sh ./my-custom-build.sh
# Edit my-custom-build.sh...When to Create New Canonical Implementations
Only create new canonical implementations when:
1. Functionality doesn't exist in ~/.claude/ 2. Existing implementation has fundamental limitations that can't be addressed through configuration 3. After creation:
- Document in appropriate skill
- Add to relevant documentation indexes
- Update CLAUDE.md with link if universally applicable
Verification is Mandatory, Not Optional
Principle
All generated artifacts (PDFs, compiled binaries, deployed services) must be verified before presenting to users or marking tasks complete.
Verification Requirements
1. Automated checks: Scripts that verify expected properties
- Exit code checks
- Output format validation
- Pattern matching for known failure signatures
2. Visual inspection: Human review of critical outputs
- Open generated files in appropriate viewers
- Spot-check formatting, layout, content
- Verify edge cases mentioned in requirements
3. Documented verification process: Add checks to skills documentation
- Update SKILL.md with verification steps
- Add to reference docs as troubleshooting patterns
- Create automated verification scripts when applicable
Examples for PDF Generation
Automated Verification:
# Check for broken bullet rendering (expect 0 matches)
pdftotext output.pdf - | grep -E '^\w.*: -'
# Check for visible bare URLs (expect 0 matches)
pdftotext output.pdf - | grep -c "https://"
# Verify PDF metadata
pdfinfo output.pdf | grep -E "Pages|File size"Visual Verification Checklist:
- [ ] Bullet lists render as bullets (•), not inline dashes
- [ ] Tables don't overflow page margins
- [ ] All hyperlinks are clickable (no bare URLs visible)
- [ ] Font rendering is consistent
- [ ] Page breaks are appropriate
- [ ] Table of contents links work correctly
Verification Scripts:
- Project-level skill:
pdf-generation-verification - Comprehensive automated checks + reporting
- Integration with build workflows
Failure Mode
Presenting unverified work creates reactive debugging cycles:
1. User receives unverified output 2. User discovers formatting issue 3. Developer investigates root cause 4. Fix is applied and regenerated 5. User verifies again → repeat if issues remain
This wastes both developer and user time, erodes confidence in deliverables.
Prevention: Verification before presentation catches issues at step 1.
Document Root Causes When Failures Occur
Principle
When a production failure occurs (like PDF bullet rendering), document the root cause comprehensively for future reference.
Documentation Requirements
1. Root Cause Analysis:
- What actually went wrong technically
- Why did it happen (underlying mechanism)
- What edge case was missed
2. Prevention Measures:
- How to avoid recurrence
- What checks to add
- What configurations are required
3. Update Skills Documentation:
- Integrate learnings into existing skills
- Add troubleshooting sections
- Update verification checklists
4. Update Global Memory:
- Add holistic principles to CLAUDE.md (if universally applicable)
- Link to detailed documentation in skills
- Ensure patterns are discoverable
Examples
PDF Bullet Rendering Failure (November 2025):
Root Cause Analysis Created:
- Skills documentation:
troubleshooting-pandoc.md→ "Bullet Lists Rendering as Inline Text" section - Detailed technical explanation of LaTeX justified text breaking list structures
- Test case verification showing when it fails vs. works
Skills Documentation Updated:
pandoc-pdf-generation/SKILL.md- Added verification requirementspdf-generation-verification/SKILL.md- Added bullet rendering checkspdf-generation-verification/references/pdf-best-practices.md- Added LaTeX preamble requirements
Global Memory Updated:
- CLAUDE.md - Links to pandoc-pdf-generation skill
- Reference to this principles document
Why This Matters
Documentation transforms individual failures into organizational knowledge:
- Future work automatically benefits from past learnings
- Prevents entire classes of errors, not just specific bugs
- New team members (or AI agents in new sessions) inherit knowledge
- Failure patterns become searchable and discoverable
Application Beyond PDF Generation
While these principles were learned through PDF generation failures, they apply universally:
Canonical Implementations
- Build scripts for any compiled language
- Deployment automation scripts
- Configuration management tools
- Testing frameworks
Verification Requirements
- Compiled binaries (run test suites)
- Deployed services (health checks)
- Database migrations (validation queries)
- API responses (contract testing)
Root Cause Documentation
- Production outages (postmortem documents)
- Security vulnerabilities (security advisories)
- Performance regressions (performance analysis reports)
- Integration failures (integration debugging guides)
Related Resources
- PDF Generation Skill: SKILL.md - Main skill documentation
- Troubleshooting Guide: troubleshooting-pandoc.md
- PDF Verification Skill: Project-level
.claude/skills/pdf-generation-verification/SKILL.md(when available) - Global Memory:
~/.claude/CLAUDE.md- Hub-and-spoke navigation - Skill Invocation:
Skill(doc-tools:pandoc-pdf-generation)
Summary
Three Universal Principles:
1. Use Canonical Implementations - Don't recreate what already exists and works 2. Verify Before Presenting - Catch issues before users do 3. Document Root Causes - Transform failures into organizational knowledge
These principles prevent entire classes of errors through systematic application of production-learned patterns.
Skill: Pandoc PDF Generation
Common Patterns and Solutions
Pattern 0: Markdown Best Practices for PDF Generation
Heading Numbering
Never manually number headings - Pandoc handles this automatically
❌ Bad:
# 1. Executive Summary
## 1.1 Background
## 1.2 Key Findings
# 2. Analysis
## 2.1 Methodology✅ Good:
---
title: Strategic Analysis Report
---
# Executive Summary
## Background
## Key Findings
# Analysis
## MethodologyPandoc command:
pandoc input.md -o output.pdf --number-sectionsResult: Pandoc automatically numbers all sections as 1, 1.1, 1.2, 2, 2.1, etc.
Why this matters:
- Manual numbering creates "1. 1. Executive Summary" duplication
- Reorganizing sections requires manual renumbering (error-prone)
--number-sectionsprovides consistent, automatic numbering- Section numbers update automatically when structure changes
Applies to: All markdown intended for PDF generation (technical docs, business proposals, reports, research papers)
---
Pattern 1: Technical Documentation
Use case: API docs, user manuals, specifications
Features needed:
- Automatic section numbering
- Deep table of contents (level 3-4)
- Code syntax highlighting
- Monospace fonts for technical content
Command:
pandoc document.md \
-o document.pdf \
--pdf-engine=xelatex \
--toc \
--toc-depth=4 \
--number-sections \
--highlight-style=tango \
-V mainfont="DejaVu Sans" \
-V monofont="DejaVu Sans Mono"Pattern 2: Academic Papers
Use case: Research papers, theses, dissertations
Features needed:
- Bibliography management
- Citation style control
- Academic formatting
- Abstract support
YAML:
---
title: Research Paper Title
author: Author Name
date: 2025-11-04
abstract: |
Research abstract here.
bibliography: references.bib
csl: apa.csl
---Command:
pandoc paper.md \
-o paper.pdf \
--pdf-engine=xelatex \
--citeproc \
--number-sections \
-V fontsize=12pt \
-V linestretch=2Pattern 3: Business Proposals
Use case: Strategic proposals, executive reports
Features needed:
- Table of contents
- Section numbering
- Professional formatting
- Landscape orientation for wide tables
Command:
pandoc proposal.md \
-o proposal.pdf \
--pdf-engine=xelatex \
--toc \
--toc-depth=2 \
--number-sections \
-V geometry:landscape \
-V geometry:margin=1in \
-V mainfont="DejaVu Sans" \
-H table-spacing.texEvolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
Skill: Pandoc PDF Generation
LaTeX Parameters Reference for Pandoc
Table of Contents
- Document Class and Layout
- Document Class
- Page Geometry
- Typography
- Fonts
- Spacing
- Headers and Footers
- Table of Contents
- Section Numbering
- Colors
- Tables
- Default Table Parameters (in LaTeX preamble)
- Page Breaks
- Code Highlighting
- Bibliography and Citations
- Hyphenation and Language
- Advanced LaTeX Customization
- Custom Preamble File
- Include LaTeX Inline
- Common Combinations
- Academic Paper
- Technical Manual
- Business Report
- Troubleshooting
- Font Not Found
- Package Not Found
- Page Break Issues
- Section Numbering Starting at 0
- Resources
- LaTeX Customization
- Custom LaTeX Preamble
- Common LaTeX Variables
- Table Spacing Troubleshooting
- Reducing Table Font Size
- Production Build Script Pattern
- Example: build-pdf.sh
- Common Patterns and Solutions
Comprehensive guide to LaTeX variables and customizations available through Pandoc's -V flag and custom preambles.
Document Class and Layout
Document Class
-V documentclass=article # Standard document (default)
-V documentclass=report # Longer documents with chapters
-V documentclass=book # Books with front/back matter
-V documentclass=memoir # Flexible book classPage Geometry
Single margin:
-V geometry:margin=1in # All margins 1 inchIndividual margins:
-V geometry:top=1in
-V geometry:bottom=1in
-V geometry:left=1.5in
-V geometry:right=1.5inPage orientation:
-V geometry:landscape # Landscape mode
-V geometry:portrait # Portrait mode (default)Paper size:
-V geometry:a4paper # A4 (210 × 297 mm)
-V geometry:letterpaper # US Letter (8.5 × 11 in) [default]
-V geometry:a3paper # A3 (297 × 420 mm)Typography
Fonts
Main document font:
-V mainfont="DejaVu Sans"
-V mainfont="Times New Roman"
-V mainfont="Latin Modern Roman" # LaTeX defaultMonospace font (code blocks):
-V monofont="DejaVu Sans Mono"
-V monofont="Courier New"
-V monofont="Fira Code"Sans-serif font:
-V sansfont="Arial"
-V sansfont="Helvetica"Font size:
-V fontsize=10pt
-V fontsize=11pt
-V fontsize=12pt
-V fontsize=14ptSpacing
Line spacing:
-V linestretch=1.0 # Single spacing
-V linestretch=1.5 # 1.5 spacing
-V linestretch=2.0 # Double spacingParagraph spacing:
-V parskip=half # Half line between paragraphs
-V parskip=full # Full line between paragraphsParagraph indentation:
-V indent=true # Indent first line (default)
-V indent=false # No indentationHeaders and Footers
Page numbering:\\
-V pagestyle=plain # Page numbers at bottom center (default)
-V pagestyle=empty # No headers/footers
-V pagestyle=headings # Chapter/section in headerCustom headers:\\
-V header-includes='\\usepackage{fancyhdr}\\pagestyle{fancy}\\fancyhead[L]{Left Header}\\fancyhead[R]{Right Header}'Table of Contents
ToC title:
-V toc-title="Table of Contents"
-V toc-title="Contents"ToC depth (via command line):
--toc-depth=2 # Include h2, h3
--toc-depth=3 # Include h2, h3, h4
--toc-depth=4 # Include all heading levelsToC number spacing (fix overlapping multi-digit numbers):
% Add to LaTeX preamble to fix subsection numbers like "2.5.10" overlapping titles
\usepackage{tocloft}
\setlength{\cftsecnumwidth}{2.5em} % Section numbers (1, 2, 3)
\setlength{\cftsubsecnumwidth}{3.5em} % Subsection numbers (2.1, 2.5.10)
\setlength{\cftsubsubsecnumwidth}{4.5em} % Subsubsection numbers (2.5.10.1)Default widths (often too small):
\cftsecnumwidth: 1.5em\cftsubsecnumwidth: 2.3em (causes overlap with multi-digit subsections)\cftsubsubsecnumwidth: 3.2em
Section Numbering
Control section depth:
-V secnumdepth=2 # Number up to subsections
-V secnumdepth=3 # Number up to subsubsections (default)
-V secnumdepth=0 # No section numberingColors
Link colors:
-V linkcolor=blue
-V urlcolor=blue
-V citecolor=blueNamed colors: black, blue, brown, cyan, darkgray, gray, green, lightgray, lime, magenta, olive, orange, pink, purple, red, teal, violet, white, yellow
Tables
Default Table Parameters (in LaTeX preamble)
Row spacing:
\renewcommand{\arraystretch}{1.2} % 1.0 = tight, 1.5 = looseCell padding:
\setlength{\extrarowheight}{4pt} % Top padding in cellsColumn spacing:
\setlength{\tabcolsep}{8pt} % Space between columnsTable breaking:
\LTchunksize=50 % Rows processed before page breakPage Breaks
Penalties (higher = less likely to break):
\widowpenalty=10000 % Orphaned line at top of page
\clubpenalty=10000 % Orphaned line at bottom of page
\brokenpenalty=10000 % Hyphenated word across pagesCode Highlighting
Syntax highlighting theme:
--highlight-style=pygments
--highlight-style=tango
--highlight-style=espresso
--highlight-style=zenburn
--highlight-style=kate
--highlight-style=monochromeCustom highlight theme:
--highlight-style=custom.themeGenerate theme file:
pandoc --print-highlight-style=pygments > custom.themeBibliography and Citations
Citation style:
--csl=chicago-author-date.csl
--csl=apa.csl
--csl=mla.cslBibliography file:
--bibliography=references.bib
--bibliography=refs1.bib --bibliography=refs2.bibBibliography title:
-V reference-section-title="References"
-V reference-section-title="Bibliography"Hyphenation and Language
Language:
-V lang=en-US # American English (default)
-V lang=en-GB # British English
-V lang=de-DE # German
-V lang=fr-FR # FrenchHyphenation:
-V hyphenate=true # Allow hyphenation (default)
-V hyphenate=false # Disable hyphenationAdvanced LaTeX Customization
Custom Preamble File
Create .tex file with LaTeX commands:
example-preamble.tex:
% Custom packages
\usepackage{booktabs} % Professional tables
\usepackage{longtable} % Multi-page tables
\usepackage{graphicx} % Enhanced graphics
\usepackage{xcolor} % Extended colors
% Custom commands
\newcommand{\mycommand}[1]{\textbf{#1}}
% Custom spacing
\setlength{\parskip}{1em}
\setlength{\parindent}{0em}Usage:
pandoc document.md -o document.pdf -H example-preamble.texInclude LaTeX Inline
For simple customizations:
-V header-includes='\\usepackage{booktabs}'
-V header-includes='\\renewcommand{\\arraystretch}{1.2}'Common Combinations
Academic Paper
pandoc paper.md -o paper.pdf \
--pdf-engine=xelatex \
--number-sections \
--citeproc \
--bibliography=refs.bib \
--csl=apa.csl \
-V fontsize=12pt \
-V linestretch=2 \
-V geometry:margin=1inTechnical Manual
pandoc manual.md -o manual.pdf \
--pdf-engine=xelatex \
--toc \
--toc-depth=3 \
--number-sections \
--highlight-style=tango \
-V mainfont="DejaVu Sans" \
-V monofont="Fira Code" \
-V fontsize=11ptBusiness Report
pandoc report.md -o report.pdf \
--pdf-engine=xelatex \
--toc \
--number-sections \
-V geometry:landscape \
-V geometry:margin=1in \
-V mainfont="Calibri" \
-H table-spacing.texTroubleshooting
Font Not Found
Problem: Font 'Arial' not found
Solution:
# List available fonts
fc-list | grep -i arial
# Use system-available font
-V mainfont="Helvetica"
# Or use LaTeX default
-V mainfont="Latin Modern Roman"Package Not Found
Problem: LaTeX Error: File 'package.sty' not found
Solution:
# Install missing LaTeX package (macOS)
sudo tlmgr install package-name
# Or install full MacTeX distribution
brew install --cask mactexPage Break Issues
Problem: Tables or figures breaking awkwardly
Solution:
% Add to preamble (-H file.tex)
\usepackage{needspace}
\widowpenalty=10000
\clubpenalty=10000Section Numbering Starting at 0
Problem: Sections numbered 0.1, 0.2 instead of 1, 2
Solution: Use YAML front matter for title instead of # Title heading
Resources
LaTeX Customization
Custom LaTeX Preamble
Create a .tex file with LaTeX commands for fine-grained control:
table-spacing.tex:
% Compact table spacing to prevent page breaks
\renewcommand{\arraystretch}{1.0} % Row spacing (default: 1.0)
\setlength{\extrarowheight}{2pt} % Cell padding (default: 0pt)
\setlength{\tabcolsep}{6pt} % Column spacing (default: 6pt)
% Discourage awkward table page breaks
\usepackage{needspace}
\LTchunksize=100 % Process more rows before page break
\widowpenalty=10000 % Discourage orphaned lines
\clubpenalty=10000Use in build:
pandoc document.md -o document.pdf -H table-spacing.texCommon LaTeX Variables
Set LaTeX variables with -V flag:
-V geometry:margin=1in # Page margins
-V geometry:landscape # Landscape orientation
-V mainfont="DejaVu Sans" # Font family
-V fontsize=11pt # Font size
-V linestretch=1.5 # Line spacing
-V documentclass=article # Document classTable Spacing Troubleshooting
Problem: Tables breaking across pages awkwardly
Solution 1: Compact spacing (reduces table height 20-25%)
\renewcommand{\arraystretch}{1.0} % Was 1.2
\setlength{\extrarowheight}{2pt} % Was 4pt
\setlength{\tabcolsep}{6pt} % Was 10ptSolution 2: Increase page break penalties
\usepackage{needspace}
\LTchunksize=100
\widowpenalty=10000
\clubpenalty=10000Trade-off: Denser tables vs. better page break behavior. Very long tables will still break (correct behavior for readability).
Reducing Table Font Size
Problem: Tables with many columns or dense content need to fit better on pages
Idiomatic Solution: Automatic font reduction for all tables
% Add to LaTeX preamble (e.g., table-spacing.tex)
\usepackage{etoolbox}
\AtBeginEnvironment{longtable}{\small}How it works:
- Uses
etoolboxpackage's\AtBeginEnvironmenthook - Automatically applies to all Pandoc-generated tables (Pandoc uses
longtableenvironment) - No markdown changes required - applies globally to all tables
- Captions remain at normal size for visual hierarchy
Font size options (from largest to smallest):
\small(~90% of normal) - Subtle reduction, recommended default\footnotesize(~80% of normal) - Moderate reduction\scriptsize(~70% of normal) - Significant reduction\tiny(~50% of normal) - Very small, use sparingly
To also reduce caption size:
\usepackage[font=small]{caption}Benefits:
- Better space efficiency without markdown modifications
- More content fits per page (especially wide tables)
- Maintains readability while improving density
- Idiomatic LaTeX pattern, widely used in academic publishing
Production Build Script Pattern
Example: build-pdf.sh
#!/bin/bash
# Build PDF with professional formatting
# Usage: ./build-pdf.sh
set -e # Exit on error
echo "Generating PDF with ToC and automatic numbering..."
pandoc DOCUMENT.md \
-o DOCUMENT.pdf \
--pdf-engine=xelatex \
--toc \
--toc-depth=3 \
--number-sections \
--citeproc \
--bibliography=references.bib \
-V mainfont="DejaVu Sans" \
-V geometry:margin=1in \
-V toc-title="Table of Contents" \
-H table-spacing.tex
echo "✅ PDF generated: DOCUMENT.pdf"
ls -lh DOCUMENT.pdf
pdfinfo DOCUMENT.pdf | grep PagesMake executable:
chmod +x build-pdf.shRun:
./build-pdf.shCommon Patterns and Solutions
Markdown Structure for PDF Generation
Best practices for structuring Markdown documents that produce clean, professional landscape PDFs with Pandoc.
Quick Pandoc Command (Standalone)
When you need to generate a PDF without relying on build-pdf.sh:
pandoc file.md -o file.pdf \
--pdf-engine=xelatex \
-V geometry:a4paper,landscape \
-V geometry:margin=1in \
-V fontsize=11pt \
-V mainfont="DejaVu Sans" \
-V colorlinks=true \
-V linkcolor=blue \
-V urlcolor=blue \
--toc --toc-depth=2 \
--number-sectionsKey flags explained:
| Flag | Purpose |
|---|---|
--pdf-engine=xelatex | Required for Unicode and custom fonts |
-V geometry:a4paper,landscape | Landscape orientation |
-V mainfont="DejaVu Sans" | Professional sans-serif font |
--number-sections | Auto-number headings (1, 1.1, 1.1.1) |
--toc --toc-depth=2 | Table of contents with H1/H2 |
-V colorlinks=true | Clickable blue hyperlinks |
---
Heading Structure
Never Manually Number Headings
Wrong - manual numbering breaks when sections are added/removed:
# 1. Introduction
## 1.1 Background
## 1.2 Objectives
# 2. MethodologyCorrect - let Pandoc number with --number-sections:
# Introduction
## Background
## Objectives
# MethodologyHeading Hierarchy
Use consistent heading levels for proper ToC structure:
# Top-Level Section (H1)
## Subsection (H2)
### Sub-subsection (H3)
Content goes here. Avoid skipping levels (H1 → H3).---
Tables for Landscape Format
Width Considerations
Landscape A4 provides ~25cm usable width. Design tables accordingly:
Wide data tables (ideal for landscape):
| Project | Duration | Commits | Releases | Cadence | Pattern |
| --------- | -------- | ------- | -------- | ------- | -------------- |
| cc-skills | 9 days | 167 | 64 | 7.1/day | Intense sprint |
| netstrata | 27 days | 118 | 34 | 1.3/day | Responsive |Narrow tables - consider portrait or split into multiple tables.
Table Best Practices
1. Use pipe tables - most portable Markdown table format 2. Align columns - use :--- (left), :---: (center), ---: (right) 3. Keep headers short - abbreviate if needed 4. No merged cells - Pandoc doesn't support them
---
Links for Clickable PDFs
External URLs
**Profile**: [github.com/terrylica](https://github.com/terrylica)With -V colorlinks=true -V urlcolor=blue, this renders as clickable blue text.
Internal Cross-References
See [Architecture Decision Records](#architecture-decision-records) below.Note: Anchor IDs are auto-generated from heading text (lowercase, hyphens).
---
ASCII Diagrams
Always Use graph-easy Skill
CRITICAL: Never manually type ASCII diagrams. Always use the itp:graph-easy skill.
# General diagrams
Skill(itp:graph-easy)
# ADR architecture diagrams
Skill(itp:adr-graph-easy-architect)Why this matters:
- Manual ASCII art has inconsistent character spacing
- graph-easy produces properly aligned boxart characters
- Output is reproducible and editable
Keep Annotations Outside Code Blocks
Wrong - inline comments break diagram alignment:
Place annotations like "contains: file1, file2" inside the diagram code block.
Correct - annotations in regular markdown:
**Contains**: file1, file2
[diagram code block here]Preventing Page Breaks in Diagrams
The canonical LaTeX preamble prevents code blocks from breaking across pages. For very tall diagrams that exceed page height, add \newpage before the section:
\newpage
## Section with Tall Diagram---
Code Blocks
Fenced Code with Language
````markdown
pandoc file.md -o file.pdf --pdf-engine=xelatex````
````
Syntax highlighting works automatically with XeLaTeX.
Inline Code
Use backticks for commands, filenames, and technical terms:
Run `./build-pdf.sh` to generate the PDF.---
Lists
Bullet Lists
- First item
- Second item
- Nested item
- Another nested
- Third itemNumbered Lists
1. First step
2. Second step
3. Third stepTip: Pandoc auto-renumbers, so you can use 1. for all items during drafting.
---
Horizontal Rules
Use --- for section breaks (renders as thin line in PDF):
## Section One
Content here.
---
## Section Two
More content.---
When to Use Landscape vs Portrait
Use Landscape For
- Wide data tables (5+ columns)
- Comparison matrices
- Technical documentation with code blocks
- Dashboards and reports
Use Portrait For
- Narrative documents (essays, letters)
- Simple documents with few tables
- Documents intended for printing
Switching Orientation
If you need both in one document, use the build-pdf.sh script which defaults to landscape, or modify the geometry flag:
# Portrait
-V geometry:a4paper
# Landscape
-V geometry:a4paper,landscape---
Common Issues
Table Overflow
If tables extend beyond page margins:
1. Reduce column count 2. Abbreviate headers 3. Split into multiple tables 4. Use landscape orientation
ToC Number Overlap
If section numbers like "2.5.10" overlap with titles, the table-spacing-template.tex preamble fixes this automatically.
Bullet Rendering
If bullets render as boxes or question marks, ensure you're using mainfont="DejaVu Sans" which has proper Unicode support.
Skill: Pandoc PDF Generation
Issue: Everything numbered under "1.x"
Cause: Document title is a level-1 heading (# Title)
Solution: Move title to YAML front matter
---
title: Document Title
---
## First Section ← Now correctly Section 1, not 1.1Issue: Tables breaking across pages
Solution: Add compact spacing in LaTeX preamble (see "LaTeX Customization" above)
Issue: ToC too detailed
Solution: Reduce --toc-depth from 3 to 2
Issue: Multi-digit subsection numbers overlap with titles in ToC
Problem: Section numbers like "2.5.10", "2.5.11" overlap with section titles in Table of Contents
Cause: Default LaTeX allocates only 2.3em for subsection numbers, insufficient for multi-digit numbers
Solution: Add to LaTeX preamble using tocloft package
\usepackage{tocloft}
\setlength{\cftsecnumwidth}{2.5em} % Section numbers (1, 2, 3)
\setlength{\cftsubsecnumwidth}{3.5em} % Subsection numbers (2.1, 2.5.10)
\setlength{\cftsubsubsecnumwidth}{4.5em} % Subsubsection numbers (2.5.10.1)Result: Proper spacing for all subsection number lengths
Issue: Footnotes not appearing in References section
Expected behavior: Pandoc footnotes appear at bottom of each page (LaTeX standard)
For consolidated references: Use --citeproc with bibliography file instead of footnote syntax
Issue: Font not found
Common problem: XeLaTeX requires system fonts
Solution: List available fonts:
fc-list | grep -i "dejavu"Or use standard LaTeX fonts:
-V mainfont="Latin Modern Roman"Issue: Bullet Lists Rendering as Inline Text (CRITICAL)
Problem: Bullet lists appear as inline text with dashes instead of proper bullets (•)
Bad Rendering:
Multi-layer validation frameworks: - HTTP/API layer validation - Schema validation - Sanity checks...Expected Rendering:
Multi-layer validation frameworks:
• HTTP/API layer validation
• Schema validation
• Sanity checksRoot Cause: LaTeX's default justified text alignment breaks Pandoc-generated bullet list structures.
LaTeX's justification algorithm tries to make every line the same width by:
1. Adding/removing inter-word spaces 2. Hyphenating words 3. Sometimes reflowing line breaks in ways that break Pandoc's list structures
When a list appears after a paragraph ending with a colon (common pattern), the justification algorithm may:
- Merge list items onto previous lines
- Convert bullet markers (
-) into inline dashes - Collapse vertical list structure into horizontal flow
Solution: Always include \raggedright in LaTeX preamble
The canonical build script includes this automatically:
% Use ragged-right (left-aligned) instead of justified text
% Justified text can create awkward spacing and break list structures
\raggedrightLocation: ./assets/table-spacing-template.tex (lines 89-90, relative to skill directory)
Verification:
Automated check for broken bullets (expect 0 matches):
pdftotext output.pdf - | grep -E '^\w.*: -'Manual visual inspection:
- Open PDF in viewer
- Scan sections with bullet lists
- Verify bullets (•) appear, not inline dashes
Prevention:
1. ✅ Always invoke the skill: Skill(doc-tools:pandoc-pdf-generation) 2. ❌ Never create ad-hoc pandoc commands without LaTeX preamble 3. ✅ Verify all PDFs before presenting to users
Why This Matters: This issue only surfaces in production with certain text patterns. Ad-hoc Pandoc commands without proper LaTeX configuration will miss this critical requirement.
Reference: See Core Principles for universal development patterns learned from this failure.
Issue: Code Blocks/Diagrams Breaking Across Pages
Problem: ASCII diagrams or code blocks split between two pages, making them unreadable.
Root Cause: LaTeX treats code blocks as normal content flow without page break protection.
Solution: The canonical LaTeX preamble now includes fancyvrb with samepage:
\usepackage{fancyvrb}
\fvset{samepage=true}
\BeforeBeginEnvironment{Shaded}{\begin{samepage}}
\AfterEndEnvironment{Shaded}{\end{samepage}}For very tall diagrams that exceed page height, add \newpage in markdown BEFORE the code block.
Prevention:
1. Always use the canonical build script (includes page break protection) 2. For tall diagrams, add \newpage before the section 3. Visually inspect PDF before presenting to users
Issue: Double Section Numbering ("1. 1. Title")
Problem: Section headings display as "1. 1. Introduction" instead of "1. Introduction"
Root Cause: Manual numbering in markdown combined with --number-sections flag.
Bad: # 1. Introduction with --number-sections
Good: # Introduction with --number-sections
Solution: NEVER manually number markdown headings. Let --number-sections handle it.
Prevention:
1. Never manually number headings in markdown 2. Always use --number-sections flag for numbered output 3. Verify section numbering before finalizing
Issue: ASCII Diagram Misalignment
Problem: ASCII box diagrams have misaligned edges, broken arrows, or inconsistent spacing.
Root Cause: Manually typed ASCII art instead of using graph-easy tool.
Solution: ALWAYS use the itp:graph-easy skill for ASCII diagrams:
# General diagrams
Skill(itp:graph-easy)
# ADR architecture diagrams
Skill(itp:adr-graph-easy-architect)Also: Keep annotations OUTSIDE code blocks. Don't add inline comments inside diagrams - they break alignment. Place descriptive text in regular markdown paragraphs before or after the diagram.
Prevention:
1. Never manually type ASCII diagrams 2. Always use graph-easy skills 3. Never add inline comments inside diagram code blocks
Issue: Unwanted Double-Sided Printing
Problem: Printer outputs double-sided when single-sided is needed.
Solution: Use -o Duplex=None with lpr:
# One-sided (simplex)
lpr -P "PRINTER_NAME" -o Duplex=None output.pdf
# Two-sided (duplex) - long edge binding
lpr -P "PRINTER_NAME" -o Duplex=DuplexNoTumble output.pdf
# Two-sided (duplex) - short edge binding (landscape)
lpr -P "PRINTER_NAME" -o Duplex=DuplexTumble output.pdfFind printer name:
lpstat -p -dNote: Some systems have default duplex settings in ~/.lpoptions that may override command-line options.
Skill: Pandoc PDF Generation
Why Use YAML Front Matter
Instead of using # Title as a level-1 heading (which creates numbering issues), use YAML front matter for document metadata:
Problem with heading-based title:
# Document Title ← Makes this Section 1
## Executive Summary ← Becomes 1.1 instead of 1
## Introduction ← Becomes 1.2 instead of 2Solution with YAML front matter:
---
title: Document Title
author: Your Name
date: 2025-11-04
---
## Executive Summary ← Properly numbered as Section 1
## Introduction ← Properly numbered as Section 2Full YAML Options
---
title: Strategic Technology Advisory Proposal
author: Terry Li
date: November 3, 2025
abstract: |
Multi-line abstract text here.
Second line of abstract.
keywords: [automation, AI, compliance]
---Related skills
How it compares
Use pandoc-pdf-generation for print-grade LaTeX PDFs from markdown; use simpler HTML export when pagination and bibliographies are unnecessary.
FAQ
What engines does pandoc-pdf-generation require?
pandoc-pdf-generation requires Pandoc with the XeLaTeX engine (`--pdf-engine=xelatex`), plus DejaVu fonts. Troubleshooting suggests `brew install font-dejavu` and MacTeX when `xelatex` is missing.
What does build-pdf.sh auto-detect?
pandoc-pdf-generation's build-pdf.sh auto-detects a single `.md` file in the directory, optional `references.bib` and CSL files, runs pre-flight binary checks, and validates output file size and page count post-build.
How does pandoc-pdf-generation handle diagrams?
pandoc-pdf-generation forbids hand-typed ASCII diagrams and directs agents to invoke the `itp:graph-easy` skill so box-drawing characters align correctly in LaTeX PDF output.