Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 728ea52

Browse files
committed
TST: Add a test for COLRv0 fonts
1 parent 658fc65 commit 728ea52

8 files changed

Lines changed: 564 additions & 0 deletions

File tree

LICENSE/LICENSE_OPENMOJI

Lines changed: 427 additions & 0 deletions
Large diffs are not rendered by default.

doc/project/license.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ Fonts
169169
.. literalinclude:: ../../LICENSE/LICENSE_LAST_RESORT_FONT
170170
:language: none
171171

172+
.. dropdown:: OpenMoji Color (subset)
173+
:class-container: sdd
174+
175+
.. literalinclude:: ../../LICENSE/LICENSE_OPENMOJI
176+
:language: none
177+
172178
.. dropdown:: STIX
173179
:class-container: sdd
174180

lib/matplotlib/testing/__init__.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,93 @@ def _gen_multi_font_text():
320320
# The resulting string contains 491 unique characters. Some file formats use 8-bit
321321
# tables, which the large number of characters exercises twice over.
322322
return fonts, test_str
323+
324+
325+
def _add_family_suffix(font, suffix):
326+
"""
327+
Add a suffix to all names in a font.
328+
329+
This code comes from a fontTools snippet:
330+
https://github.com/fonttools/fonttools/blob/main/Snippets/rename-fonts.py
331+
"""
332+
WINDOWS_ENGLISH_IDS = 3, 1, 0x409
333+
MAC_ROMAN_IDS = 1, 0, 0
334+
335+
FAMILY_RELATED_IDS = dict(LEGACY_FAMILY=1, TRUETYPE_UNIQUE_ID=3, FULL_NAME=4,
336+
POSTSCRIPT_NAME=6, PREFERRED_FAMILY=16, WWS_FAMILY=21)
337+
338+
def get_current_family_name(table):
339+
family_name_rec = None
340+
for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS):
341+
for name_id in (FAMILY_RELATED_IDS['PREFERRED_FAMILY'],
342+
FAMILY_RELATED_IDS['LEGACY_FAMILY']):
343+
family_name_rec = table.getName(nameID=name_id, platformID=plat_id,
344+
platEncID=enc_id, langID=lang_id)
345+
if family_name_rec is not None:
346+
return family_name_rec.toUnicode()
347+
raise ValueError("family name not found; can't add suffix")
348+
349+
def insert_suffix(string, family_name, suffix):
350+
# check whether family_name is a substring
351+
start = string.find(family_name)
352+
if start != -1:
353+
# insert suffix after the family_name substring
354+
end = start + len(family_name)
355+
return string[:end] + suffix + string[end:]
356+
else:
357+
# it's not, we just append the suffix at the end
358+
return string + suffix
359+
360+
def rename_record(name_record, family_name, suffix):
361+
string = name_record.toUnicode()
362+
new_string = insert_suffix(string, family_name, suffix)
363+
name_record.string = new_string
364+
return string, new_string
365+
366+
table = font['name']
367+
family_name = get_current_family_name(table)
368+
ps_family_name = family_name.replace(' ', '')
369+
ps_suffix = suffix.replace(' ', '')
370+
for rec in table.names:
371+
name_id = rec.nameID
372+
if name_id not in FAMILY_RELATED_IDS.values():
373+
continue
374+
if name_id == FAMILY_RELATED_IDS['POSTSCRIPT_NAME']:
375+
old, new = rename_record(rec, ps_family_name, ps_suffix)
376+
elif name_id == FAMILY_RELATED_IDS['TRUETYPE_UNIQUE_ID']:
377+
# The Truetype Unique ID rec may contain either the PostScript
378+
# Name or the Full Name string, so we try both
379+
if ps_family_name in rec.toUnicode():
380+
old, new = rename_record(rec, ps_family_name, ps_suffix)
381+
else:
382+
old, new = rename_record(rec, family_name, suffix)
383+
else:
384+
old, new = rename_record(rec, family_name, suffix)
385+
386+
return family_name
387+
388+
389+
def _generate_font_subset(path, text):
390+
"""
391+
Generate a subset of a font for testing purposes.
392+
393+
The font name will be suffixed with ' MplSubset'.
394+
395+
Parameters
396+
----------
397+
path : str or bytes or os.PathLike
398+
The path to the font to be subset. The new file will be saved in the same
399+
location with a ``-subset`` suffix.
400+
text : str
401+
The text from which characters to be subset will be derived. Usually fonts do
402+
not have a newline character, so any appearing in this text will be stripped
403+
before subsetting.
404+
"""
405+
from fontTools import subset
406+
options = subset.Options()
407+
font = subset.load_font(path, options)
408+
subsetter = subset.Subsetter(options=options)
409+
subsetter.populate(text=text.replace('\n', ''))
410+
subsetter.subset(font)
411+
_add_family_suffix(font, ' MplSubset')
412+
subset.save_font(font, path.with_stem(path.stem + '-subset'), options)

lib/matplotlib/testing/__init__.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from collections.abc import Callable
2+
import os
23
import subprocess
34
from typing import Any, IO, Literal, overload
5+
from fontTools.ttLib import TTFont
46

57
def set_font_settings_for_testing() -> None: ...
68
def set_reproducibility_for_testing() -> None: ...
@@ -56,3 +58,5 @@ def ipython_in_subprocess(
5658
) -> None: ...
5759
def is_ci_environment() -> bool: ...
5860
def _gen_multi_font_text() -> tuple[list[str], str]: ...
61+
def _add_family_suffix(font: TTFont, suffix: str) -> None: ...
62+
def _generate_font_subset(path: str | bytes | os.PathLike, text: str) -> None: ...

lib/matplotlib/testing/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ def pytest_configure(config):
2424
("filterwarnings",
2525
r"ignore:DynamicImporter.find_spec\(\) not found; "
2626
r"falling back to find_module\(\):ImportWarning"),
27+
("filterwarnings",
28+
r"ignore:Glyph .* \([lp]\) missing from font\(s\) OpenMoji MplSubset\."),
2729
]:
2830
config.addinivalue_line(key, value)
2931

80.5 KB
Loading
23.6 KB
Binary file not shown.

lib/matplotlib/tests/test_text.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import gc
33
import inspect
44
import io
5+
from pathlib import Path
56
import warnings
67

78
import numpy as np
@@ -1331,3 +1332,37 @@ def test_draw_text_as_path_fallback(monkeypatch):
13311332
_test_complex_shaping(subfig[0])
13321333
_test_text_features(subfig[1])
13331334
_test_text_language(subfig[2])
1335+
1336+
1337+
@image_comparison(['colour.png'], remove_text=False, style='mpl20')
1338+
def test_colour_fonts():
1339+
zwj = '\U0000200D'
1340+
adult = '\U0001F9D1'
1341+
man = '\U0001F468'
1342+
woman = '\U0001F469'
1343+
science = '\U0001F52C'
1344+
technology = '\U0001F4BB'
1345+
skin_tones = ['', *(chr(0x1F3FB + i) for i in range(5))]
1346+
1347+
text = '\n'.join([
1348+
''.join(person + tone + zwj + occupation for tone in skin_tones)
1349+
for person in [adult, man, woman]
1350+
for occupation in [science, technology]
1351+
])
1352+
1353+
# To generate the subsetted test file, save the latest
1354+
# OpenMoji-color-glyf_colr_0.ttf from
1355+
# https://github.com/hfg-gmuend/openmoji/tree/master/font to the data directory,
1356+
# set this condition to True, and run the test.
1357+
path = Path(__file__).parent / 'data/OpenMoji-color-glyf_colr_0.ttf'
1358+
if False:
1359+
from matplotlib.testing import _generate_font_subset
1360+
_generate_font_subset(path, text)
1361+
path = path.with_stem(path.stem + '-subset')
1362+
if not path.exists():
1363+
pytest.xfail(f'Test font {path} is not available')
1364+
1365+
fig = plt.figure()
1366+
fig.text(0.5, 0.5, text,
1367+
font=FontProperties(fname=path), fontsize=48, linespacing=0.9,
1368+
horizontalalignment='center', verticalalignment='center')

0 commit comments

Comments
 (0)