|
| 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. |
0 commit comments