PDFPipe

Fonts / What happens to the text afterwards

Icon fonts in a PDF, and why an SVG is almost always better

Icons delivered as font glyphs, which look fine on the page and turn the document's extracted text into rubbish.

What is actually going on

An icon font maps arbitrary codepoints to pictures. Usually those codepoints are in a private-use area, which is exactly what it sounds like: a range with no agreed meaning, so the character in the document is a character no reader can interpret. On the page it is a tick or an arrow. In the extracted text it is a private-use codepoint or a stray letter, and every one of them lands in the middle of your content. A document whose text extracts as a warehouse address with a random letter in the middle of it is a document that fails a search, fails an accessibility check, and reads badly when anything machine-processes it. An inline SVG has none of these problems: it is a picture, it is understood to be a picture, it scales, and it leaves the text alone.

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.

  • Use inline SVG for icons in a document. It is a picture in the place a picture belongs, and it does not sit in the text stream pretending to be a character.
  • Where an icon carries meaning, give it a text alternative, because the picture is not readable by anything that is reading the text.
  • If an icon font is unavoidable, mark the glyph so it is not treated as content and provide the meaning as real text beside it.
  • Never use an icon font for something that must survive extraction, such as a status or a value. If it matters, it should be a word.
  • Watch for icon fonts arriving through a component library rather than through a decision. That is how they get into documents: nobody chose them for the document, they came with something else.
  • Remember an icon font is a whole embedded font for a handful of pictures, which is usually larger than the SVGs would have been.

What it looks like

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

html
<!-- Preferred: a picture, in the place a picture belongs. -->
<span class="status">
  <svg class="icon" viewBox="0 0 16 16" role="img" aria-label="Checked">
    <path d="M2 8.5 6 12.5 14 4" fill="none" stroke="currentColor"
          stroke-width="2"/>
  </svg>
  Checked
</span>

<style>
  .icon { width: 3.2mm; height: 3.2mm; vertical-align: -0.4mm; }

  /* If an icon font cannot be avoided, keep the glyph out of the meaning
     and put the meaning in real text beside it. */
  .legacy-icon {
    font-family: "Legacy Icons";
    speak: never;
  }
</style>

<!-- Not this: the tick is a private-use codepoint, so the extracted text
     of this line is the word Checked with a stray character in front of it.
<span class="legacy-icon">&#xE001;</span> Checked -->

What people do instead

Carrying an icon font into a document because the application already used one. It was a reasonable choice for a screen, where nobody extracts the text, and it is a poor one for a document, where extraction is half the point of the format.

How to find out rather than assume

Render a page with icons on it, copy all the text out, and read it in a plain text editor. Every stray or unreadable character is an icon that got into the text stream. Then search the document for a phrase that has an icon in the middle of it and see whether it is still findable.

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.