← All comparisons
vs WeasyPrint

WeasyPrint vs makesPDF — alternative for Python developers

Python devs — an API call, not a 50-dependency install.

For: Python teams using WeasyPrint to convert HTML/CSS to PDF.

WeasyPrint

The one-line difference

WeasyPrint is a Python library that converts HTML + CSS to PDF using its own layout engine (no Chromium). makesPDF is a hosted REST API: any language can POST /api/v1/md and get a deterministic, tagged, archival PDF back. No Pango, no Cairo, no GTK headers, no apt install list, no Python version drift, no CI image to maintain.

Feature matrix

WeasyPrint makesPDF
Shape Python library Hosted REST API (/api/v1/*)
Authoring surface HTML + CSS Markdown, builder DSL, JSON
Runtime Your Python process + native deps (Pango, Cairo, harfbuzz) Cloudflare Workers (we host)
Install footprint Python + ~5 native libraries 0 — you call an HTTP endpoint
Tagged PDF (PDF/UA-1) Partial — emits structure but coverage gaps remain Yes, full structure tree, by default
PDF/A archival PDF/A-1, -2, -3 PDF/A-2A
veraPDF-validated Mixed (depends on input HTML) Yes
Markdown input No (HTML only) Yes (POST /api/v1/md)
Determinism Subject to Pango/font drift Byte-stable across deploys
Agent-first No Yes — public skill file, MCP server, x402-payable
License BSD-3-Clause Proprietary hosted; skill file MIT

Same input, two outputs

A "Hello, Ada" report.

WeasyPrint — Python + HTML + CSS:

from weasyprint import HTML, CSS

html = """
<!doctype html>
<html><body>
  <h1>Hello, Ada</h1>
  <p>Welcome to the report.</p>
</body></html>
"""

css = CSS(string="""
  @page { size: A4; margin: 40pt; }
  body { font-family: sans-serif; font-size: 12pt; }
  h1 { font-size: 24pt; margin-bottom: 12pt; }
""")

HTML(string=html).write_pdf("hello.pdf", stylesheets=[css])

Plus the install dance: apt install libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libcairo2, pin a compatible WeasyPrint version, hope your CI image still has the right glibc.

makesPDF — same Python, one HTTP call, no native deps:

import requests, os

resp = requests.post(
    "https://makespdf.com/api/v1/md",
    headers={"Authorization": f"Bearer {os.environ['MAKESPDF_API_KEY']}"},
    json={"markdown": "# Hello, Ada\n\nWelcome to the report."},
)
resp.raise_for_status()
open("hello.pdf", "wb").write(resp.content)

Why people switch

  • The dependency footprint. WeasyPrint pulls Pango, Cairo, harfbuzz, fontconfig, and a stack of font packages into your image. CI builds slow down, base images grow, and a glibc bump in your Docker base can break PDF output. Hosted means none of that lives in your repo.
  • Markdown is faster than HTML for content. Most documents are mostly content. Writing markdown — or generating it from a database — is a fraction of the work of templating HTML + a CSS print stylesheet.
  • Compliance is structural. WeasyPrint emits a structure tree but coverage is uneven, and validating PDF/UA-1 against veraPDF often needs hand-tuning per template. makesPDF tags by construction and validates by default.
  • Determinism. Pango shaping and font-fallback behaviour change between versions. Same HTML can produce slightly different line breaks across deploys. Our renderer is a single owned pipeline; output is byte-stable.
  • Agents. A Python wrapper is fine for batch jobs; it's awkward for an agent. A REST endpoint with a skill file is the agent-native path.

When WeasyPrint is still the right call

  • Air-gapped or strictly-on-prem. A self-hosted Python library with no egress is the obvious answer.
  • You already have a CSS-print stylesheet that's been tuned for years and you don't want to migrate.
  • BSD-licensed, fork-and-audit is a hard procurement requirement.
  • Genuinely complex CSS layouts (CSS Grid templates, advanced page rules) we don't support.

For most Python teams who just want a PDF endpoint that works the same way in dev, CI, and prod, the hosted API removes a whole class of native-dependency problems.

Migration

Most WeasyPrint code paths are converting a Jinja-templated HTML string. Two paths:

  1. Markdown — render your data into markdown and POST to /api/v1/md. Headings, tables, alerts, footnotes, and Mermaid diagrams all render natively.
  2. DSL — for tables-heavy documents (invoices, receipts, statements), the builder DSL gives you precise grid + colspan control without a CSS-print stylesheet.

Both endpoints accept JSON, so the Python migration is a requests.post call.