PDFPipe

Fonts / What the engine does with it

Using a variable font in a paged document, and when not to

One file carrying a continuous range of weights instead of several files carrying fixed ones, which solves a download problem a generated document does not have.

What is actually going on

A variable font exists to avoid shipping five files to a browser when a page uses five weights. A generated document has a different economics: it is rendered once, on a machine, and the download happens on a connection you control, so the saving is small. Against that, a variable font puts a burden on the whole downstream chain. What ends up embedded in the finished file depends on the engine, and tooling further along, a preflight tool, a validator, a printer's workflow, may handle a variable font differently from a static one or not at all. That is a lot of uncertainty to accept in exchange for a download saving on a render nobody is waiting for.

How fonts reach this API

There is no font option on the render. A font arrives the way every other subresource arrives: your @font-face names a URL, the renderer fetches it while the document loads, and what was used is embedded in the output. So there is no font upload, no list of available fonts, no substitution control and no subsetting switch. It also means the two failures that actually stop a font are network-shaped rather than typographic: the URL has to be absolute and publicly reachable, and the fetch has to finish before the page is captured. Everything else on this page is decided in your stylesheet and in the font file itself.

The decisions

Reasons rather than a description of the code. Each of these has a default, and the default is chosen by the engine rather than by you.

  • Prefer static instances for the weights you actually use. Three static files is a smaller set of unknowns than one variable file, and the download difference does not matter here.
  • If you use a variable font, use named instances at the axis values you need rather than arbitrary interpolated values, so the result is a weight the designer drew rather than one between two of them.
  • Verify what ended up in the output rather than assuming. Open the finished document's font list and see what is actually embedded.
  • Check the whole downstream chain before committing, especially if the document goes to a printer or through a conformance check. That is where the handling differences show up.
  • Do not animate or vary an axis across the document. A weight that drifts is a weight nobody chose, and in print it reads as an error.
  • Where the document has to satisfy an archiving standard, static instances remove a question you would otherwise have to answer.

What it looks like

A fragment, with the wrong version kept in a comment where the wrong version is the thing people actually write.

css
/* Preferred for a generated document: static instances at the weights
   used, which is a smaller set of unknowns than one variable file. */
@font-face {
  font-family: "Brand Sans";
  src: url("brand-sans-400.woff2") format("woff2");
  font-weight: 400;
}
@font-face {
  font-family: "Brand Sans";
  src: url("brand-sans-700.woff2") format("woff2");
  font-weight: 700;
}

/* If a variable font is used, declare the supported range honestly and
   use values within it that correspond to instances the designer drew. */
@font-face {
  font-family: "Brand Variable";
  src: url("brand-variable.woff2") format("woff2-variations");
  font-weight: 300 800;
}
h1 { font-family: "Brand Variable"; font-weight: 700; }  /* a named instance */
/* not: font-weight: 642; */

What people do instead

Adopting a variable font in a document pipeline for the file-size benefit. The benefit is a download saving on a render nobody is waiting for, and the cost is uncertainty about what is embedded and how every downstream tool treats it. That is the wrong trade for a document, and the right one for a website.

How to find out rather than assume

Render the document and inspect the fonts embedded in the output. Confirm you got what you expected and that the weights used are the weights you asked for. If the document goes to a printer or through a validator, put a real file through that path before adopting the font, not after.

Frequently asked

Can I upload a font instead of serving one?

No. There is no font upload and no font list. The renderer fetches what your @font-face points at, so the file has to be somewhere it can reach over the network. In practice that means a public URL on your own asset host or a font service, and it means a font on a private path or behind an authenticated endpoint fails to load silently and the fallback is used instead.

How do I know which fonts ended up in the document?

Open the finished file in a reader and look at its document properties, which list the embedded fonts. That list is the truth: it says whether the face you meant was used, whether a fallback was substituted, and how many faces the document is carrying. It is a five second check and it catches most of the failures in this cluster.

Why does the font work in my browser and not in the render?

Almost always because the URL resolves for you and not for the renderer. A relative path, a local file, a development host, or an asset behind a login all work while you are looking at the page and none of them work when a machine somewhere else loads it. The fallback is then used and nothing reports an error, which is why the symptom is a document that looks subtly wrong rather than a render that failed.

Related font topics

The topics that cause each other, then the rest of the same group.

Fonts reach a render the way every other subresource does, over the network from the URL you named. Render the document and look at what actually got embedded.