papr-cli 2.1.0

Generate printable calendar PDFs and SVGs
papr-cli-2.1.0 is not a library.

codecov

papr - Generate Calendar Stationeries

Command line tool to generate empty calendar templates to print. Outputs PDF and SVG.

ScreenShot

Quick Start

papr month -o calendar.pdf -y 2026 -p A4
papr month -o calendar.svg -y 2026 -p A4
papr big -o year.pdf -y 2026 -f 'Avenir Next' -p A3

Installation

Homebrew (macOS)

brew tap peetzweg/tap
brew install papr

Cargo (from source)

Requires system libraries: Cairo and Pango.

# macOS
brew install pango cairo

# Debian/Ubuntu
apt install libpango1.0-dev libcairo2-dev

# Then install papr
cargo install --git https://github.com/peetzweg/papr

From source (manual)

git clone https://github.com/peetzweg/papr.git
cd papr
cargo build --release
# Binary at ./target/release/papr

Usage

Each layout is a subcommand with shared options and optional layout-specific flags:

papr <COMMAND> [OPTIONS]

Commands:
  month    Single month portrait calendar
  big      Full year landscape, days flow in rows
  classic  Two months landscape, columnar layout
  column   Four months landscape, vertical columns
  oneyear  Full year on one landscape sheet
  batch    Generate calendars from a YAML batch config

Shared Options (all layout commands)

Flag Description Default
-o, --output <FILE> Output file (.pdf or .svg, detected from extension) out.pdf
-y, --year <YEAR> Calendar year current year
-m, --month <MONTH> Starting month (1-12) current month
-p, --paper <SIZE> Paper size A4
-f, --font <FONT> Font family (pass twice for heading + body font) Sans
-l, --locale <LOCALE> Locale for date formatting en_US
--margin <MM> Page margin in millimeters 5

Paper sizes: A5, A4, A3, A2, A1, A0, USLetter, USTabloid, USLedger

Layout-Specific Options

Some layouts accept additional flags that only apply to them:

classic and column:

Flag Description
-a Abbreviate weekday names
-A Abbreviate both weekday and month names

classic only:

Flag Description
-b, --brand <TEXT> Brand string printed on the calendar
-c, --color Color date numbers

Examples

# Portrait month calendar
papr month -o april.pdf -y 2026 -m 4 -p A4

# Full year with custom font on A3
papr big -o 2026.pdf -y 2026 -f 'Avenir Next' -p A3

# Classic layout with abbreviated weekdays and branding
papr classic -o classic.pdf -y 2026 -a -b "My Calendar" -c

# Four-month column layout, starting June
papr column -o summer.pdf -y 2026 -m 6

# One-year overview with separate heading font
papr oneyear -o overview.svg -y 2026 -f Sans -f 'Georgia'

# SVG output (detected from file extension)
papr month -o calendar.svg

Layouts

month

Single month on a portrait page. The top third is a header with year and month name. The bottom two thirds is a 7-column (Mon-Sun) grid with 6 rows. Week numbers appear on Mondays.

big

Full year on a single landscape sheet. Days flow left-to-right in rows of 21 columns. Month boundaries are marked with colored flag labels. Supports starting at any month with year-transition labels.

classic

Two months in landscape orientation. Uses a 4-page columnar layout designed for back-to-back printing -- the first month is rotated 180 degrees. Supports brand text, colored numbers, and weekday/month abbreviation.

column

Four months in landscape, each in a vertical column. Includes a folding margin after day 15, so the calendar can be folded in half. Supports weekday and month abbreviation.

oneyear

Full year on one landscape sheet as 12 mini-columns (one per month). Uses a separate heading font for month titles if provided via a second -f flag. Weekday names are shown as single letters.

ScreenShot

Batch Mode

Generate many calendars at once from a single YAML file, using GitHub Actions-style matrix expansion:

papr batch config.yaml

The matrix computes the Cartesian product of all listed values, so every combination is generated. Use exclude to skip specific ones.

YAML Schema

# Default values applied to every combination
defaults:
  year: 2026
  font: "Avenir Next"
  paper: A4
  margin: 5

# Matrix axes — Cartesian product of all lists
matrix:
  layout: [month, big, classic, oneyear]
  paper: [A4, A3]
  month: [1, 6]

# Skip specific combinations (all keys in an entry must match)
exclude:
  - layout: big
    paper: A3
    month: 6

# Per-layout options for layouts with custom flags
layout_options:
  classic:
    abbreviate: true
    brand: "My Brand"
    color: true
  column:
    abbreviate_all: true

# Output path template — {key} placeholders are replaced
output: "calendars/{layout}_{year}_{month}_{paper}.pdf"

Template Variables

Use {key} in the output path. Available variables: layout, year, month, paper, font, locale, margin. Values come from the matrix combination first, then fall back to defaults.

Batch Error Handling

All combinations are attempted even if some fail. A summary is printed at the end showing successes and failures, and the exit code is non-zero if anything failed.

Example

Given the YAML above (4 layouts x 2 papers x 2 months = 16, minus 1 excluded = 15):

$ papr batch config.yaml
Matrix expanded to 15 combination(s)
[1/15] calendars/month_2026_1_A4.pdf ... Written: calendars/month_2026_1_A4.pdf
[2/15] calendars/month_2026_1_A3.pdf ... Written: calendars/month_2026_1_A3.pdf
...
All 15 file(s) generated successfully.

Output directories are created automatically.

Architecture

CLI Design

The CLI uses subcommands per layout rather than a single flat argument list. This allows each layout to define its own flags without polluting the global namespace. Shared options (year, month, paper, font, etc.) are defined once in a SharedArgs struct and flattened into every subcommand via clap's #[command(flatten)].

This design means adding a new layout-specific option (e.g., --days-per-row for big) only requires adding a field to that layout's args struct -- no changes to shared code.

Project Structure

src/
  main.rs          CLI entry point, subcommand dispatch, render_one()
  config.rs        Config struct (shared fields), PaperSize, PageSetup
  batch.rs         YAML batch mode: parsing, matrix expansion, orchestration
  canvas.rs        Drawing API wrapping Cairo + Pango (PDF/SVG)
  calendar.rs      Date utilities (day iteration, weekends, month spans)
  style.rs         Color constants and visual defaults
  layout/
    mod.rs         Layout trait definition
    month.rs       Month layout (portrait, 7-col grid)
    big.rs         Big layout (landscape, day rows with flags)
    classic.rs     Classic layout (landscape, 2-month columnar)
    column.rs      Column layout (landscape, 4-month vertical)
    oneyear.rs     One-year layout (landscape, 12 mini-columns)

Rendering Pipeline

  1. CLI parsing -- clap parses subcommand + args into typed structs
  2. Config construction -- build_config() resolves paper size, font stack, year/month defaults
  3. Layout construction -- layout struct created with any layout-specific options
  4. Page setup -- paper dimensions resolved for the layout's orientation (portrait/landscape)
  5. Canvas creation -- Cairo surface created (PDF or SVG based on output file extension)
  6. Drawing -- layout.draw() renders the calendar onto the canvas
  7. Output -- canvas drops, surface finishes, file is written

In batch mode, steps 2-7 are repeated for each matrix combination.

Layout Trait

Every layout implements a simple trait:

pub trait Layout {
    fn orientation(&self) -> Orientation;  // Portrait or Landscape
    fn draw(&self, canvas: &Canvas, config: &Config, page: &PageSetup);
}

The Config struct carries shared fields (year, month, font, paper, etc.). Layout-specific options are stored as fields on the layout struct itself and accessed via self during drawing. This separation keeps Config lean and lets each layout own its customization.

Dependencies

Crate Purpose
cairo-rs 2D rendering to PDF and SVG surfaces
pango / pangocairo Text layout, shaping, and font handling
clap CLI argument parsing with derive macros
chrono Date calculations (weekdays, leap years, month spans)
serde / serde_yml YAML deserialization for batch mode