Skip to content

Commit e18701d

Browse files
authored
Merge pull request matplotlib#20450 from aitikgupta/font-subset-docs
[Doc] Font Types and Font Subsetting
2 parents 9e0bb9c + a94f521 commit e18701d

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

doc/users/fonts.rst

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
Fonts in Matplotlib Text Engine
2+
===============================
3+
4+
Matplotlib needs fonts to work with its text engine, some of which are shipped
5+
alongside the installation. However, users can configure the default fonts, or
6+
even provide their own custom fonts! For more details, see :doc:`Customizing
7+
text properties </tutorials/text/text_props>`.
8+
9+
However, Matplotlib also provides an option to offload text rendering to a TeX
10+
engine (``usetex=True``),
11+
see :doc:`Text rendering with LaTeX </tutorials/text/usetex>`.
12+
13+
Font Specifications
14+
-------------------
15+
Fonts have a long and sometimes incompatible history in computing, leading to
16+
different platforms supporting different types of fonts. In practice, there are
17+
3 types of font specifications Matplotlib supports (in addition to 'core
18+
fonts', more about which is explained later in the guide):
19+
20+
.. list-table:: Type of Fonts
21+
:header-rows: 1
22+
23+
* - Type 1 (PDF)
24+
- Type 3 (PDF/PS)
25+
- TrueType (PDF)
26+
* - One of the oldest types, introduced by Adobe
27+
- Similar to Type 1 in terms of introduction
28+
- Newer than previous types, used commonly today, introduced by Apple
29+
* - Restricted subset of PostScript, charstrings are in bytecode
30+
- Full PostScript language, allows embedding arbitrary code
31+
(in theory, even render fractals when rasterizing!)
32+
- Include a virtual machine that can execute code!
33+
* - These fonts support font hinting
34+
- Do not support font hinting
35+
- Hinting supported (virtual machine processes the "hints")
36+
* - Non-subsetted through Matplotlib
37+
- Subsetted via external module `ttconv <https://github.com/sandflow/ttconv>`_
38+
- Subsetted via external module `fonttools <https://github.com/fonttools/fonttools>`_
39+
40+
NOTE: Adobe will disable support for authoring with Type 1 fonts in
41+
January 2023. `Read more here. <https://helpx.adobe.com/fonts/kb/postscript-type-1-fonts-end-of-support.html>`_
42+
43+
Special Mentions
44+
^^^^^^^^^^^^^^^^
45+
Other font specifications which Matplotlib supports:
46+
47+
- Type 42 fonts (PS):
48+
49+
- PostScript wrapper around TrueType fonts
50+
- 42 is the `Answer to Life, the Universe, and Everything! <https://en.wikipedia.org/wiki/Answer_to_Life,_the_Universe,_and_Everything>`_
51+
- Matplotlib uses an external library called `fonttools <https://github.com/fonttools/fonttools>`_
52+
to subset these types of fonts
53+
54+
- OpenType fonts:
55+
56+
- OpenType is a new standard for digital type fonts, developed jointly by
57+
Adobe and Microsoft
58+
- Generally contain a much larger character set!
59+
- Limited Support with Matplotlib
60+
61+
Subsetting
62+
----------
63+
Matplotlib is able to generate documents in multiple different formats. Some of
64+
those formats (for example, PDF, PS/EPS, SVG) allow embedding font data in such
65+
a way that when these documents are visually scaled, the text does not appear
66+
pixelated.
67+
68+
This can be achieved by embedding the *whole* font file within the
69+
output document. However, this can lead to very large documents, as some
70+
fonts (for instance, CJK - Chinese/Japanese/Korean fonts) can contain a large
71+
number of glyphs, and thus their embedded size can be quite huge.
72+
73+
Font Subsetting can be used before generating documents, to embed only the
74+
*required* glyphs within the documents. Fonts can be considered as a collection
75+
of glyphs, so ultimately the goal is to find out *which* glyphs are required
76+
for a certain array of characters, and embed only those within the output.
77+
78+
.. note::
79+
The role of subsetter really shines when we encounter characters like **ä**
80+
(composed by calling subprograms for **a** and **¨**); since the subsetter
81+
has to find out *all* such subprograms being called by every glyph included
82+
in the subset, this is a generally difficult problem!
83+
84+
Luckily, Matplotlib uses a fork of an external dependency called
85+
`ttconv <https://github.com/sandflow/ttconv>`_, which helps in embedding and
86+
subsetting font data. (however, recent versions have moved away from ttconv to
87+
pure Python for certain types: for more details visit
88+
`these <https://github.com/matplotlib/matplotlib/pull/18370>`_, `links <https://github.com/matplotlib/matplotlib/pull/18181>`_)
89+
90+
| *Type 1 fonts are still non-subsetted* through Matplotlib. (though one will encounter these mostly via *usetex*/*dviread* in PDF backend)
91+
| **Type 3 and Type 42 fonts are subsetted**, with a fair amount of exceptions and bugs for the latter.
92+
93+
What to use?
94+
------------
95+
Practically, most fonts that are readily available on most operating systems or
96+
are readily available on the internet to download include *TrueType fonts* and
97+
its "extensions" such as MacOS-resource fork fonts and the newer OpenType
98+
fonts.
99+
100+
PS and PDF backends provide support for yet another type of fonts, which remove
101+
the need of subsetting altogether! These are called **Core Fonts**, and
102+
Matplotlib calls them via the keyword **AFM**; all that is supplied from
103+
Matplotlib to such documents are font metrics (specified in AFM format), and it
104+
is the job of the viewer applications to supply the glyph definitions.
105+
106+
This is especially helpful to generate *really lightweight* documents.::
107+
108+
# trigger core fonts for PDF backend
109+
plt.rcParams["pdf.use14corefonts"] = True
110+
# trigger core fonts for PS backend
111+
plt.rcParams["ps.useafm"] = True
112+
113+
chars = "AFM ftw!"
114+
fig, ax = plt.subplots()
115+
ax.text(0.5, 0.5, chars)
116+
117+
fig.savefig("AFM_PDF.pdf", format="pdf")
118+
fig.savefig("AFM_PS.ps", format="ps)
119+
120+
.. note::
121+
These core fonts are limited to PDF and PS backends only; they can not be
122+
rendered in other backends.
123+
124+
Another downside to this is that while the font metric are standardized,
125+
different PDF viewer applications will have different fonts to render these
126+
metrics. In other words, the **output might look different on different
127+
viewers**, as well as (let's say) Windows and Linux, if Linux tools included
128+
free versions of the proprietary fonts.
129+
130+
This also violates the *what-you-see-is-what-you-get* feature of Matplotlib.

doc/users/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ User's guide
1313
:maxdepth: 2
1414

1515
interactive.rst
16+
fonts.rst
1617
release_notes.rst
1718
license.rst
1819
../citing.rst

0 commit comments

Comments
 (0)