Skip to main content

Mail merge and bulk document generation from CSV to PDF using WeasyPrint and Jinja2.

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

weasymerge-0.1.0.tar.gz (57.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

weasymerge-0.1.0-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

File details

Details for the file weasymerge-0.1.0.tar.gz.

File metadata

  • Download URL: weasymerge-0.1.0.tar.gz
  • Upload date:
  • Size: 57.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for weasymerge-0.1.0.tar.gz
Algorithm Hash digest
SHA256 13dbdbf4952503d658f15e5d812c93064880404019470371badaf4955a17f3e9
MD5 47f3eb9360af5725f204b49850ba45ea
BLAKE2b-256 f6ceabe6af7e9dbd8bb713ce41a4ea65e1aaf9d91a9421b73cfa54851e1f50ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for weasymerge-0.1.0.tar.gz:

Publisher: python-release.yml on mmulqueen/weasymerge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file weasymerge-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: weasymerge-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for weasymerge-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 af1328c47499fc29cd48ae951429b9cc25f3af097ec31ca5d0c16fb728a3ff29
MD5 f9405771cf3598a257a9d100890a5e76
BLAKE2b-256 fd73ea6450f93211c39bb5184d7598a2bfe6c0d851eaef6938225c65335b76a9

See more details on using hashes here.

Provenance

The following attestation bundles were made for weasymerge-0.1.0-py3-none-any.whl:

Publisher: python-release.yml on mmulqueen/weasymerge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page