Fonts / Getting the font into the document
Which font file format to serve for a document that embeds it
WOFF2, WOFF, TTF and OTF all work, and the one you serve changes the download size, not what ends up in the finished document.
What is actually going on
A web font format is a container. WOFF2 is a compressed wrapper around the same outline data a TTF or OTF holds, and the compression exists to make the download smaller. What gets embedded in the finished document is not the file you served, it is the glyph data the engine used, so serving a smaller file makes the render faster without making the output smaller. That is worth knowing because people optimise the wrong end: they agonise over WOFF2 against TTF for a document that is generated once and read a thousand times, where the only thing that mattered was whether the file was reachable at all.
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.
- Serve WOFF2 first. It is the smallest and it is read by any current engine, so the render fetches less and starts sooner.
- Keep a TTF or OTF available if anything else in your pipeline consumes the same font, because tooling that is not a browser frequently does not read WOFF2.
- Do not serve EOT or SVG fonts. They exist for browsers nobody is rendering with, and a src list that includes them is a list with dead entries in it.
- List formats in the src with a format hint so the engine picks without fetching what it cannot use.
- The output size is driven by how many glyphs were used and whether the engine subset them, not by the format you served. Optimising the served format to shrink the document is optimising the wrong thing.
- Whatever the format, the URL has to be absolute and publicly reachable. That is the failure that actually happens, and it is covered on the @font-face page.
What it looks like
A fragment, with the wrong version kept in a comment where the wrong version is the thing people actually write.
@font-face {
font-family: "Brand Sans";
src:
/* smallest, read by any current engine */
url("https://assets.example.com/brand-sans-400.woff2") format("woff2"),
/* kept for non-browser tooling that shares the same asset */
url("https://assets.example.com/brand-sans-400.ttf") format("truetype");
font-weight: 400;
font-style: normal;
font-display: block;
}
/* Not this: dead entries for engines nobody renders with, and each one
is a format the engine has to consider before moving on.
src: url("brand.eot"), url("brand.svg#brand"); */What people do instead
Choosing the format to make the PDF smaller. The embedded data is derived from the glyphs used, so the same document comes out the same size whichever container you served it from. If the document is too large, the lever is how many faces you loaded and whether they were subset, not whether the source was WOFF2.
How to find out rather than assume
Render the document twice, once serving WOFF2 and once serving TTF, and compare the file sizes. They will be close to identical. If you actually need the document smaller, count the faces you loaded: four weights and two styles is eight embedded fonts and most documents use three.
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.
Whether your font licence lets you embed it in a generated document
Embedding a font into a document distributes that font, and whether you are permitted to do that is a question about the licence rather than about the technology.
Designing a font fallback chain for a document you cannot see
The list of families after the one you want, which decides what the document looks like on the day the font does not load.
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.
@font-face in paged output
The property behind this decision, with its real support status.
Every font topic
The full list, grouped by supply, engine behaviour, and what happens to the text afterwards.
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.