How to Generate a PDF Report
Options for different setups
Overview
There are several ways to turn your analysis into a polished PDF report. Which option works best depends on what software you already have installed. This guide covers six approaches, from the most feature-rich to the most minimal.
Option 1: Quarto (recommended if available)
Requires: Quarto (plus R or Python only if your .qmd executes those languages); pandoc is bundled with Quarto.
Quarto is the modern standard. It ships with pandoc, so no separate install is needed. You write code and narrative together in a .qmd file, and Quarto handles execution and typesetting.
Install:
# Windows (winget)
winget install --id Posit.Quarto --source winget --exact
# macOS (Homebrew)
brew install --cask quarto
# Or download from https://quarto.org/docs/get-started/Verify setup:
quarto --version
quarto checkFor PDF output, you also need LaTeX. The easiest route is TinyTeX:
quarto install tinytexRender:
quarto render report.qmd # uses format specified in YAML
quarto render report.qmd --to pdf # force PDF output
quarto render report.qmd --to html # HTML fallback (no LaTeX needed)Tip: Quarto 1.4+ supports format: typst, which bundles its own typesetting engine and does not need LaTeX at all:
---
title: "My Report"
format: typst
---Option 2: R Markdown
Requires: R + rmarkdown package + pandoc + LaTeX
The predecessor to Quarto. Uses .Rmd files with R code chunks. Very mature ecosystem with many templates.
If you do not use RStudio or Quarto, install pandoc separately (see Option 4).
Install:
# In R console
install.packages("rmarkdown")
# For LaTeX (minimal distribution, ~250 MB)
install.packages("tinytex")
tinytex::install_tinytex()Verify setup:
rmarkdown::find_pandoc()
tinytex::is_tinytex()Render:
rmarkdown::render("report.Rmd", output_format = "pdf_document")
rmarkdown::render("report.Rmd", output_format = "html_document") # no LaTeX neededOption 3: Python + LaTeX
Requires: Python (pandas, matplotlib) + xelatex or pdflatex
Python generates figures as PNGs and writes a .tex file. A LaTeX engine compiles it to PDF. This gives you full control over layout.
Install:
# Python packages
pip install pandas matplotlib numpy
# LaTeX -- pick one:
# Windows: MiKTeX (smaller/easier) or TeX Live
# https://miktex.org/download
# https://tug.org/texlive/
# macOS:
brew install --cask basictex # minimal (~100 MB)
brew install --cask mactex # full (~5 GB)
# Or use TinyTeX via R (see Option 2)Verify setup:
xelatex --version # or: pdflatex --versionRun:
python report.py
# Typical flow: your script writes report.tex, then you compile with xelatexManual compilation (if needed):
xelatex report.tex # run twice for table of contents
xelatex report.texOption 4: Python + pandoc
Requires: Python (pandas, matplotlib) + pandoc + LaTeX
Python generates figures, you write the narrative in plain Markdown, and pandoc converts everything to PDF. Easier than writing raw LaTeX.
Install:
# Python packages
pip install pandas matplotlib numpy
# pandoc
# Windows:
winget install --source winget --exact --id JohnMacFarlane.Pandoc
# macOS:
brew install pandoc
# You still need LaTeX for PDF output (see Option 3)Verify setup:
pandoc --versionRun:
# First generate your figures
python generate_figures.py
# Then convert Markdown to PDF
pandoc report.md -o report.pdf --pdf-engine=xelatex
# Or to HTML (no LaTeX needed)
pandoc report.md -o report.html --standaloneOption 5: Jupyter Notebook export
Requires: Python + Jupyter + nbconvert + pandoc + LaTeX (for PDF)
Write code and narrative together in .ipynb notebook cells, then export.
Install:
pip install jupyter nbconvert
# For PDF export, also install:
# - pandoc (see Option 4)
# - LaTeX (see Option 3)Export:
jupyter nbconvert --to pdf notebook.ipynb # needs pandoc + LaTeX
jupyter nbconvert --to html notebook.ipynb # no LaTeX neededNotebook UIs also offer Export/Download-as-PDF actions, but menu labels vary by app/version.
Option 6: Python + fpdf2 (no LaTeX needed)
Requires: Python 3.10+ (pandas, matplotlib, fpdf2)
A pure-Python option that produces text and figures in a PDF without any LaTeX or pandoc install. Limited typographic control but zero external dependencies.
Install:
pip install pandas matplotlib fpdf2Verify setup:
python -c "from fpdf import FPDF; print('fpdf2 ok')"Example:
from fpdf import FPDF
pdf = FPDF()
pdf.set_auto_page_break(auto=True, margin=15)
# Title page
pdf.add_page()
pdf.set_font("Helvetica", "B", 24)
pdf.cell(w=0, h=20, text="My Report Title", new_x="LMARGIN", new_y="NEXT", align="C")
# Section with text
pdf.set_font("Helvetica", "B", 14)
pdf.cell(w=0, h=12, text="1. Introduction", new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", size=11)
pdf.multi_cell(w=0, h=6, text="Your narrative text goes here...")
# Insert a figure
pdf.image("exhibits/fig1.png", w=170)
pdf.output("report.pdf")Quick reference
| Option | Tools needed | LaTeX required? | Notes |
|---|---|---|---|
| Quarto | Quarto (+ R/Python if executing code) | Yes (or use typst) |
Best overall experience |
| R Markdown | R, rmarkdown, pandoc | Yes (use tinytex) |
Mature, many templates |
| Python + LaTeX | Python, LaTeX engine | Yes | Full layout control |
| Python + pandoc | Python, pandoc | Yes (HTML fallback: no) | Write in Markdown |
| Jupyter export | Python, Jupyter, nbconvert, pandoc | Yes (HTML fallback: no) | Good for notebooks |
| Python + fpdf2 | Python, fpdf2 | No | Lowest barrier to PDF |
Which should I pick?
- Already have Quarto? Use Option 1. It’s the simplest end-to-end workflow.
- Already have R? Use Option 2. Install
tinytexfor LaTeX and you’re set. - Only have Python + LaTeX? Use Option 3 (this is what we used for the earnings report).
- Have Python but no LaTeX? Use Option 6 (
fpdf2) for a quick PDF, or Option 4/5 with--to htmlas a fallback. - Want to avoid installing anything heavy? Use Quarto with
format: typst(Option 1) orfpdf2(Option 6) — neither needs LaTeX.