Part Two — Typography Chapter Eleven

Web Fonts in a Print Context

A typeface that loads beautifully in a browser may behave differently on its way to a PDF. Understanding the pipeline prevents surprises.

Typography in a browser and typography in a PDF share the same CSS, the same HTML, and the same fonts — but the path from source to output is different enough that it warrants its own chapter. When you design for screen, the font loads once, the browser renders it continuously, and any loading failures are visible and correctable in real time. When you design for print via Paged.js, the font must be present and fully loaded before pagination begins, must embed correctly in the PDF output, and must survive any licensing restrictions that govern its redistribution. Each of these requirements has a specific technical response, and knowing them in advance saves considerable frustration.

This chapter covers the full lifecycle of a web font in a Paged.js document — from the loading strategy that prevents pagination errors, through the question of when system fonts are actually the better choice, to the licensing considerations that govern what you can and cannot do with a font file once it is embedded in a distributed PDF.

· · ·

The font loading problem Why timing matters for pagination

Paged.js works by running a JavaScript polyfill that reads your HTML and CSS, applies the CSS Paged Media specification, and renders the content as a sequence of fixed-size pages in the browser. This pagination process — calculating where lines break, where pages break, how much content fits on each page — depends on the font metrics of the typefaces being used. The width of each character, the height of each line, the space occupied by each word — all of this is determined by the font. If the font is not loaded when Paged.js runs, it uses the browser's fallback font metrics instead, and the pagination is calculated against a typeface that may be significantly different from the one you intended.

The consequences are visible and consistent: text that overflows pages, page breaks in the wrong places, line counts that differ from the final rendered version, and a document that looks correct in the Paged.js preview but produces a different layout when the fonts eventually load. This is the font loading problem in Paged.js, and it has a reliable solution: ensure the fonts are fully loaded before Paged.js begins its work.

The most robust approach is the document.fonts.ready promise, which resolves when all font faces referenced in the document's CSS have finished loading. Paged.js provides a mechanism to delay its automatic preview until this promise resolves. In the simplest setup, where Paged.js runs automatically on page load, you can use the previewer API to trigger pagination manually after fonts are ready:

/* In your HTML, include Paged.js but prevent
   auto-run by adding the data attribute       */
<script
  src="paged.js"
  data-paged-preview="false"
></script>

/* Then in a separate script block           */
<script>
  document.fonts.ready.then(() => {
    // All fonts loaded — safe to paginate
    window.PagedPolyfill.preview();
  });
</script>

An alternative, and often simpler, approach is to use the font-display: block descriptor in your @font-face declarations. This instructs the browser to block rendering entirely until the font loads, rather than falling back to a system font. While font-display: block is generally discouraged for performance-sensitive web pages — it can cause a visible flash of invisible text — it is entirely appropriate for a print document where visual correctness matters more than loading speed. When Paged.js runs, the fonts will already be in place, because the browser has been waiting for them.

/* Self-hosted font with font-display: block  */
@font-face {
  font-family:   'EB Garamond';
  font-style:    normal;
  font-weight:   400;
  font-display:  block; /* Wait — don't fall back */
  src: url('fonts/eb-garamond-400.woff2')
       format('woff2');
}

When using Google Fonts rather than self-hosted files, you cannot control the font-display descriptor directly — it is set by Google's servers, typically to swap. In this case, the document.fonts.ready approach is the more reliable option for ensuring fonts are loaded before Paged.js paginates.

Best for most projects Self-hosted + font-display: block Use when: offline capability needed, CI/CD pipeline, production documents

Font files live alongside the HTML document. No network dependency. font-display: block guarantees the browser waits for the correct font before rendering, so Paged.js always paginates against the right metrics. Requires downloading font files and managing them in the project folder.

@font-face {
  font-family: 'EB Garamond';
  font-display: block;
  src: url('fonts/eb-garamond.woff2');
}
Good for development Google Fonts + fonts.ready promise Use when: iterating quickly, network access reliable, license is SIL OFL

Fast to set up and covers a wide range of quality typefaces under open licenses. Requires the document.fonts.ready guard to prevent premature pagination. Requires an internet connection when the document is opened for preview.

document.fonts.ready.then(() => {
  window.PagedPolyfill.preview();
});
Consider carefully Commercial CDN (Adobe Fonts, etc.) Use when: specific commercial typeface required, subscription active

Gives access to professional typefaces not available on Google Fonts. Requires an active subscription, network access, and the fonts.ready guard. Check the license carefully — many commercial web font licenses do not permit PDF embedding, which is what Paged.js does when generating a downloadable document.

Figure 11.1 — Font loading strategies for Paged.js. Three approaches in order of reliability for print output. Self-hosting with font-display: block is the most robust for production documents. Google Fonts with the fonts.ready guard is the fastest to set up for development. Commercial CDN fonts require careful license checking before use in distributed PDFs.

· · ·

System fonts as a deliberate choice When the best font is already on the machine

There is a reflexive assumption in web typography that web fonts are always preferable to system fonts — that loading a font from a server is inherently better than using what is already installed on the device. For screen work, this assumption is broadly defensible: web fonts give you control over the typeface regardless of what the reader has installed, and that consistency is valuable. For print work via Paged.js, the assumption deserves more scrutiny.

When a document is intended to be generated locally — by you, on your own machine, in a controlled environment — the system fonts available on that machine are fully available to the CSS without any loading consideration. There are no network requests, no loading delays, no fonts.ready timing issues, and no PDF embedding questions. The font renders in the browser, Paged.js paginates against its metrics, and the PDF is generated with the font embedded from the local font cache. This is, in some respects, the cleanest possible setup.

The practical limitation is that system font availability varies between operating systems and even between machines running the same operating system. A font that ships with macOS — Georgia, for instance, or Charter — may not be available on Windows or Linux. This matters if the document will be generated on multiple machines, by multiple people, or in a CI/CD environment where the operating system is unpredictable.

The solution is the system font stack — a fallback chain that specifies multiple fonts in order of preference, so that whichever is available on the current system is used. For a document that needs to work well on any machine without network access, a carefully constructed system font stack can produce surprisingly good results:

Serif system font stack — for body text
1 'Charter' macOS / iOS — excellent text face, generous x-height, clean at small sizes
2 'Bitstream Charter' Linux (some distributions) — the open-source ancestor of Charter
3 'Georgia' Windows, macOS, most systems — designed for screens, reads well at text sizes
4 'Cambria' Windows — a well-made transitional serif with good screen and print performance
5 serif Browser default — Times New Roman on most systems; last resort only
Sans-serif system font stack — for UI elements and labels
1 -apple-system macOS / iOS — San Francisco; the system UI font, well-hinted, clean at any size
2 BlinkMacSystemFont Chrome on macOS — same San Francisco, different identifier
3 'Segoe UI' Windows — the Windows system UI font; readable, professional
4 'Helvetica Neue' macOS (older) — reliable neutral sans, well-spaced
5 Arial Universal — ubiquitous if not distinguished; better than system default
6 sans-serif Browser default — last resort

For the project document, we use web fonts — EB Garamond and Outfit — because the document is intended to be used by readers on any machine, including those without access to the best system serif fonts. But for a document you are producing yourself, on your own machine, for your own use or for a small number of specific recipients, a system font stack is a perfectly legitimate and technically simpler choice.

· · ·

Font licensing for PDF distribution What the licence actually permits

Font licensing is one of the least understood and most important aspects of professional typography. Every font file is software, and every font file is distributed under a licence that governs what you can and cannot do with it. The licence applies whether you paid for the font or downloaded it for free, and "I didn't know" is not a defence that holds in any professional context.

For a document produced with Paged.js, there are two distinct uses of the font that must be permitted by the licence. The first is web use: serving the font from a server (or a CDN like Google Fonts) and rendering it in a browser. The second is PDF embedding: including the font data inside the PDF file so that readers who do not have the font installed can still see the correct typeface. Both uses must be permitted. A licence that permits only web use — common in some commercial web font licences — does not permit the PDF embedding that Paged.js performs.1

The simplest and most reliable approach is to use typefaces licensed under the SIL Open Font License, which explicitly permits both web use and embedding in documents. All Google Fonts are SIL OFL licensed. Many high-quality typefaces from independent designers are also SIL OFL — Source Serif, EB Garamond, Lora, Libre Baskerville, and many others that are entirely suitable for professional document work.

For commercial typefaces — those requiring a purchase or subscription — the licence must be read carefully. The terms vary considerably between foundries. Some commercial licences permit PDF embedding as part of their standard web font licence; others require a separate desktop or embedding licence; others prohibit embedding entirely for files intended for wide distribution. When in doubt, contact the foundry directly. Most are willing to clarify what their licence permits, and many offer specific embedding licences at reasonable cost.

Licence type Web use PDF embedding Notes
SIL Open Font License ✓ Yes ✓ Yes All Google Fonts. Explicitly permits both uses. Redistribution of the font file itself in modified form requires the same licence.
Web font licence (commercial) ✓ Yes ⚠ Check terms Varies by foundry. Many commercial web font licences permit embedding for documents not primarily intended to distribute the font itself — but read the specific terms.
Desktop licence (commercial) ✗ No ✓ Yes Desktop licences permit installation and embedding in static documents but not web serving. Cannot be used with Google Fonts or any CDN delivery.
Adobe Fonts (subscription) ✓ Yes ⚠ Limited Adobe Fonts permits embedding in PDFs for personal and internal use. Distribution of PDFs containing Adobe Fonts to external parties may require additional licensing — check the current terms.
System fonts (pre-installed) ✗ No ✓ Usually yes System fonts like Georgia and Charter are licensed for use on the system they are installed on, which includes embedding in documents created on that system. Not for web serving.

One practical point worth emphasising: when Paged.js generates a PDF via the browser's print function, font embedding is handled by the browser's PDF engine, not by Paged.js itself. The browser will attempt to embed font data in the PDF according to the font's embedding permissions flags — a field set in the font file itself by the foundry. Fonts with the embedding permissions set to "no embedding" will not be embedded, and the PDF will fall back to a substitute font for readers who do not have the typeface installed. Fonts with the SIL Open Font License, and most web fonts, have their embedding permissions set to allow embedding.

· · ·

Performance vs. typographic richness Making the trade-off consciously

For a document that will be opened in a browser, paginated by Paged.js, and exported as a PDF, loading performance is a consideration — but it is a different consideration than for a public-facing website. A website visited by thousands of users must load in under two seconds or risk losing them. A document opened by one person, or a handful of people, on a machine with a reliable internet connection, can afford to take five or ten seconds to load its fonts without any meaningful consequence.

This shifts the performance calculation considerably. For a web page, you might choose a system font stack or a single-weight web font to minimise load time. For a Paged.js document, you can afford to load four or five font files — roman and italic in two weights, plus a companion sans-serif — without causing problems for any realistic use case. The trade-off is real but the stakes are lower than they first appear.

Where performance does matter in this context is in automated pipelines — situations where the document is generated programmatically, perhaps hundreds of times, in a headless browser environment. In this case, network requests add up, and self-hosted fonts become essential. They also improve consistency: a headless browser with access to local font files will produce identical output every time, regardless of network conditions or CDN availability.

The guiding principle is: load what you need, no more. For the project document, this means loading EB Garamond in two weights (400 and 500), both roman and italic, plus Outfit in two weights (400 and 500). That is five font files — or fewer, if using a variable font — which is a comfortable load for the browser and a complete typographic toolkit for the document. Request only the weights and styles you actually use in the CSS; loading a full font family "just in case" adds weight without adding benefit.

/* ── Optimised Google Fonts request ─────────
   Only the weights and styles actually used
   in the project stylesheet                  */

<!-- Preconnect for performance -->
<link rel="preconnect"
      href="https://fonts.googleapis.com">
<link rel="preconnect"
      href="https://fonts.gstatic.com"
      crossorigin>

<!-- EB Garamond: 400 and 500, roman + italic
     Outfit: 400 and 500, roman only
     Variable font syntax for EB Garamond      -->
<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?
  family=EB+Garamond:ital,wght@
    0,400..500;1,400..500
  &family=Outfit:wght@400;500
  &display=block">

Note the display=block parameter appended to the Google Fonts URL. This applies font-display: block to all the fonts in the request — instructing the browser to wait for the fonts rather than rendering with a fallback. As discussed earlier, this is the correct behavior for a Paged.js document and is now supported in Google Fonts URL parameters.2

· · ·

Putting it all together The complete font setup for the project

The following is the complete, production-ready font loading setup for The Compositor's Garden, combining everything from this chapter: the optimised Google Fonts request, the fonts.ready guard for Paged.js, and a graceful fallback for offline or restricted environments.

<!-- ════════════════════════════════════════
     Font loading — The Compositor's Garden
     ════════════════════════════════════════ -->

<!-- 1. Preconnect hints -->
<link rel="preconnect"
      href="https://fonts.googleapis.com">
<link rel="preconnect"
      href="https://fonts.gstatic.com"
      crossorigin>

<!-- 2. Font request — only used weights
     Variable weight range: 400–500         -->
<link rel="stylesheet"
      href="https://fonts.googleapis.com/css2?
  family=EB+Garamond:ital,wght@
    0,400..500;1,400..500
  &family=Outfit:wght@400;500
  &display=block">

<!-- 3. Paged.js — disable auto-preview -->
<script
  src="paged.polyfill.js"
  data-paged-preview="false"
></script>

<!-- 4. Font-ready guard -->
<script>
  document.fonts.ready.then(function () {
    window.PagedPolyfill.preview();
  });
</script>

/* 5. CSS fallback stack — in stylesheet     */
:root {
  /* Web font first, quality system fonts as
     fallback for offline/restricted envs    */
  --font-body: 'EB Garamond',
               Charter,
               'Bitstream Charter',
               Georgia,
               serif;

  --font-ui:   'Outfit',
               -apple-system,
               BlinkMacSystemFont,
               'Segoe UI',
               sans-serif;
}

A note on offline use

If you will be working on the project document in environments without reliable internet access — on a plane, in a location with poor connectivity, or in a build environment without outbound network access — download the font files from Google Fonts using the google-webfonts-helper tool and switch to the self-hosted @font-face approach from earlier in this chapter. Change font-display: block in the @font-face declarations, remove the Google Fonts <link> elements, and remove the fonts.ready guard — it is no longer needed, because the fonts load from local files before the page script runs.

The companion project files for this book include both versions of the font setup — online and offline — with comments indicating which lines to switch between them. For most development work, the Google Fonts version is more convenient. For production output, self-hosting is more reliable.

· · ·

Font loading, licensing, and performance are not the most glamorous topics in typography, but they are the ones most likely to cause real problems in a real project. Getting them right is invisible — the document simply works, the PDF is correct, the typefaces render as intended. Getting them wrong produces a document that breaks in ways that are difficult to diagnose if you do not know where to look.

With this chapter, Part Two is complete. The typographic foundation of The Compositor's Garden is fully in place: typefaces chosen and loaded, scale defined, spacing system established, OpenType features configured, and the font pipeline understood. In Part Three, we build the spatial structure on top of this foundation — the grid, the margins, and the column system that give the type a physical home on the page.