Skip to main content

WeasyMerge is a tool for doing mail merges and other bulk document generation without having to write your own glue code. You get the power of WeasyPrint's best-in-class PDF generation with the ease of writing a Jinja2 template with some HTML/CSS and providing a CSV file of data to merge.

WeasyMerge can generate individual PDFs or batch rows together into one or more PDFs.

Installation

WeasyMerge is a command-line tool, so the easiest way to install it is with pipx or uv:

pipx install weasymerge
uv tool install weasymerge

You can also install it into an existing environment with pip:

pip install weasymerge

WeasyMerge relies on WeasyPrint, which needs some system libraries — see its installation guide. On Windows this is much smoother under WSL.

Usage

weasymerge --data guests.csv --template invite.html.j2 --output "pdfs/Invite {row_number} {row[Name]}.pdf"

pipx run and uvx run WeasyMerge on demand, fetching it if it isn't already available, so you can invoke it without installing it first:

pipx run weasymerge --data guests.csv --template invite.html.j2 --output "pdfs/Invite {row_number} {row[Name]}.pdf"
uvx weasymerge --data guests.csv --template invite.html.j2 --output "pdfs/Invite {row_number} {row[Name]}.pdf"

The first row of the CSV file is assumed to be the header row and the header is used as the key for the row dictionary.

HTML escaping is on by default in the Jinja2 template.

The row number for the first data row is 1.

WeasyMerge provides "safe" row data to the output filename template (ASCII letters, digits, space, underscore, hyphen, and dot). Non-ASCII letters are converted to their closest ASCII equivalent.

Batch mode (multiple rows per document)

Pass --rows-per-document=N to render N rows into a single document, or --rows-per-document=all to render every row into one document. Use this for label sheets, ID cards, or any layout where the template needs to see more than one row at a time.

weasymerge --data seeds.csv --template labels.html.j2 \
  --output "labels-{batch.from_row_number}-{batch.to_row_number}.pdf" \
  --rows-per-document=all

In batch mode the template receives a batch object instead of row and row_number:

  • batch.rows — list of row dicts in this batch
  • batch.numbered_rows — list of (row_number, row) pairs
  • batch.from_row_number, batch.to_row_number — 1-indexed inclusive range
  • batch.total_rows — total rows in the source data

The same fields are available in the --output template (e.g. {batch.from_row_number}, {batch.rows[0][Plant]}). Page breaks within a single document are the template's responsibility — use CSS page-break-after: always or break-after: page.

See examples/labels/ for a 70×37mm, 24-per-A4 label sheet.

Barcodes and QR codes

WeasyMerge comes with barcode and QR code Jinja2 filters, powered by pyStrich.

Each filter turns a value into a data: URL you can drop straight into an <img> tag in your template:

<img src="{{ row['TicketID'] | qrcode_svg_dataurl }}" alt="Ticket QR code">

The available filters are:

  • qrcode_svg_dataurl
  • datamatrix_svg_dataurl
  • aztec_svg_dataurl
  • pdf417_svg_dataurl
  • code128_svg_dataurl
  • ean13_svg_dataurl
  • code39_svg_dataurl

See examples/tickets/ for A6 event tickets with a QR code and a Code128 barcode.

Metadata

Release files for weasymerge 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for weasymerge 0.1.0
File Size Uploaded
weasymerge-0.1.0.tar.gz 57.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for weasymerge 0.1.0
File Interpreter ABI Platform
weasymerge-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 63.9 kB

Release files / weasymerge-0.1.0.tar.gz

Download URL weasymerge-0.1.0.tar.gz
Size 57.3 kB
Tags Source
SHA-256 checksum
How to use checksums
13dbdbf4952503d658f15e5d812c93064880404019470371badaf4955a17f3e9
BLAKE2b-256 checksum
How to use checksums
f6ceabe6af7e9dbd8bb713ce41a4ea65e1aaf9d91a9421b73cfa54851e1f50ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 1, 2026.

Transparency log

Release files / weasymerge-0.1.0-py3-none-any.whl

Download URL weasymerge-0.1.0-py3-none-any.whl
Size 6.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
af1328c47499fc29cd48ae951429b9cc25f3af097ec31ca5d0c16fb728a3ff29
BLAKE2b-256 checksum
How to use checksums
fd73ea6450f93211c39bb5184d7598a2bfe6c0d851eaef6938225c65335b76a9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page