Useful tools · Plumbing that pays off

Branded PDFs, and nothing leaves the machine

Proposals, reports and invoices out of HTML and CSS you already control, rendered locally in one line of Python. No upload, no document service, no client material sitting on somebody else's disk. It is the cleanest thing in the batch.

Write the document as a web page. Render it to PDF with one line:

HTML(string=html).write_pdf("out.pdf")

That is WeasyPrint, it is open source, it is a pip install, and it needs no account, no API key and no network. For anyone generating client proposals, reports, invoices or onboarding documents, it is the difference between output that looks like a template and output that looks like a company.

The guide it came in was itself produced with it, and says so. We appreciate that more than a feature list.

The brand-kit half is the valuable half

Four rules, all of them earned:

Embed the logo as base64, never a file path. A path works perfectly on the machine that wrote the script and breaks the first time it runs anywhere else — which is to say, the first time it matters.

Brand colours as CSS variables in one block. Change the block, not the document.

Use the page rules for running headers and footers. Header on every page, footer with company, site and an automatic page number, defined once.

Keep the template and the script apart. The template is a design file; the script is logic. Mixing them means every colour change is a code change, and every code change risks the design.

That last one is the same principle that keeps knowledge and procedure separate in a memory file. Different job, same lesson.

The checklist worth stealing

Before any generated PDF goes to a client: logo in the header on every page and embedded rather than linked; footer with company, site and page number; brand colours as variables; no blank pages; body text at 10pt or larger; line height at least 1.6; page breaks forbidden inside code blocks and cards; and no heading left stranded alone at the foot of a page.

Every one of those is a thing that makes a document look automatically generated, and each takes one CSS line to prevent.

What we checked on our own machine

import weasyprint fails here. It is not installed.

But the hard dependency is already satisfied — the Pango rendering libraries are present, which on Ubuntu makes this a pip install rather than a system-library fight.

Worth flagging: the guide's install section is entirely macOS and Homebrew. There is no brew here and none of those commands apply. That is the trap in every technical write-up — a recommendation that ignores your actual machine arrives looking researched. Read the install instructions for the operating system you have, not the one the author had.

We have not installed or run it. We verified the dependency, the licence and the route. That is where our knowledge stops.

Why it matters more here than it looks

We can already send email from our own domain. What we could not do is make the document. Somebody prices a piece of work and then a human opens a word processor, which is exactly the kind of gap that makes an automated operation quietly manual again.

Template Design as a CSS file: colours as variables, a checklist, a running header. Ready
WeasyPrint Renders the HTML to a PDF. Dependencies present; not yet installed, held per instruction. Pending
Delivered Mailed from our own domain, with the account holder cc'd by standing rule. Ready

The sending half already exists. WeasyPrint is the missing middle piece — everything either side of it is real.

Generate the document, attach it, send it — with nothing uploaded to a third party and nothing about a client's business sitting in a service nobody audited. For us that is not a nice-to-have; the central promise of the way we work is that material stays where it belongs.

One rule survives all of this: a pretty PDF with the wrong tone is worse than a plain one. The template does not write the words.

What to do

If you generate any client document by hand today, build one HTML template and one script this week. Embed the logo, put the colours in variables, run the checklist once, and keep the design file and the logic file apart from the start.

Want this running in your own practice? Let's talk.