test_ft2font.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. import itertools
  2. import io
  3. from pathlib import Path
  4. import numpy as np
  5. import pytest
  6. import matplotlib as mpl
  7. from matplotlib import ft2font
  8. from matplotlib.testing.decorators import check_figures_equal
  9. import matplotlib.font_manager as fm
  10. import matplotlib.path as mpath
  11. import matplotlib.pyplot as plt
  12. def test_ft2image_draw_rect_filled():
  13. width = 23
  14. height = 42
  15. for x0, y0, x1, y1 in itertools.product([1, 100], [2, 200], [4, 400], [8, 800]):
  16. im = ft2font.FT2Image(width, height)
  17. im.draw_rect_filled(x0, y0, x1, y1)
  18. a = np.asarray(im)
  19. assert a.dtype == np.uint8
  20. assert a.shape == (height, width)
  21. if x0 == 100 or y0 == 200:
  22. # All the out-of-bounds starts should get automatically clipped.
  23. assert np.sum(a) == 0
  24. else:
  25. # Otherwise, ends are clipped to the dimension, but are also _inclusive_.
  26. filled = (min(x1 + 1, width) - x0) * (min(y1 + 1, height) - y0)
  27. assert np.sum(a) == 255 * filled
  28. def test_ft2font_dejavu_attrs():
  29. file = fm.findfont('DejaVu Sans')
  30. font = ft2font.FT2Font(file)
  31. assert font.fname == file
  32. # Names extracted from FontForge: Font Information → PS Names tab.
  33. assert font.postscript_name == 'DejaVuSans'
  34. assert font.family_name == 'DejaVu Sans'
  35. assert font.style_name == 'Book'
  36. assert font.num_faces == 1 # Single TTF.
  37. assert font.num_named_instances == 0 # Not a variable font.
  38. assert font.num_glyphs == 6241 # From compact encoding view in FontForge.
  39. assert font.num_fixed_sizes == 0 # All glyphs are scalable.
  40. assert font.num_charmaps == 5
  41. # Other internal flags are set, so only check the ones we're allowed to test.
  42. expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT |
  43. ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.KERNING |
  44. ft2font.FaceFlags.GLYPH_NAMES)
  45. assert expected_flags in font.face_flags
  46. assert font.style_flags == ft2font.StyleFlags.NORMAL
  47. assert font.scalable
  48. # From FontForge: Font Information → General tab → entry name below.
  49. assert font.units_per_EM == 2048 # Em Size.
  50. assert font.underline_position == -175 # Underline position.
  51. assert font.underline_thickness == 90 # Underline height.
  52. # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below.
  53. assert font.ascender == 1901 # HHead Ascent.
  54. assert font.descender == -483 # HHead Descent.
  55. # Unconfirmed values.
  56. assert font.height == 2384
  57. assert font.max_advance_width == 3838
  58. assert font.max_advance_height == 2384
  59. assert font.bbox == (-2090, -948, 3673, 2524)
  60. def test_ft2font_cm_attrs():
  61. file = fm.findfont('cmtt10')
  62. font = ft2font.FT2Font(file)
  63. assert font.fname == file
  64. # Names extracted from FontForge: Font Information → PS Names tab.
  65. assert font.postscript_name == 'Cmtt10'
  66. assert font.family_name == 'cmtt10'
  67. assert font.style_name == 'Regular'
  68. assert font.num_faces == 1 # Single TTF.
  69. assert font.num_named_instances == 0 # Not a variable font.
  70. assert font.num_glyphs == 133 # From compact encoding view in FontForge.
  71. assert font.num_fixed_sizes == 0 # All glyphs are scalable.
  72. assert font.num_charmaps == 2
  73. # Other internal flags are set, so only check the ones we're allowed to test.
  74. expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT |
  75. ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.GLYPH_NAMES)
  76. assert expected_flags in font.face_flags
  77. assert font.style_flags == ft2font.StyleFlags.NORMAL
  78. assert font.scalable
  79. # From FontForge: Font Information → General tab → entry name below.
  80. assert font.units_per_EM == 2048 # Em Size.
  81. assert font.underline_position == -143 # Underline position.
  82. assert font.underline_thickness == 20 # Underline height.
  83. # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below.
  84. assert font.ascender == 1276 # HHead Ascent.
  85. assert font.descender == -489 # HHead Descent.
  86. # Unconfirmed values.
  87. assert font.height == 1765
  88. assert font.max_advance_width == 1536
  89. assert font.max_advance_height == 1765
  90. assert font.bbox == (-12, -477, 1280, 1430)
  91. def test_ft2font_stix_bold_attrs():
  92. file = fm.findfont('STIXSizeTwoSym:bold')
  93. font = ft2font.FT2Font(file)
  94. assert font.fname == file
  95. # Names extracted from FontForge: Font Information → PS Names tab.
  96. assert font.postscript_name == 'STIXSizeTwoSym-Bold'
  97. assert font.family_name == 'STIXSizeTwoSym'
  98. assert font.style_name == 'Bold'
  99. assert font.num_faces == 1 # Single TTF.
  100. assert font.num_named_instances == 0 # Not a variable font.
  101. assert font.num_glyphs == 20 # From compact encoding view in FontForge.
  102. assert font.num_fixed_sizes == 0 # All glyphs are scalable.
  103. assert font.num_charmaps == 3
  104. # Other internal flags are set, so only check the ones we're allowed to test.
  105. expected_flags = (ft2font.FaceFlags.SCALABLE | ft2font.FaceFlags.SFNT |
  106. ft2font.FaceFlags.HORIZONTAL | ft2font.FaceFlags.GLYPH_NAMES)
  107. assert expected_flags in font.face_flags
  108. assert font.style_flags == ft2font.StyleFlags.BOLD
  109. assert font.scalable
  110. # From FontForge: Font Information → General tab → entry name below.
  111. assert font.units_per_EM == 1000 # Em Size.
  112. assert font.underline_position == -133 # Underline position.
  113. assert font.underline_thickness == 20 # Underline height.
  114. # From FontForge: Font Information → OS/2 tab → Metrics tab → entry name below.
  115. assert font.ascender == 2095 # HHead Ascent.
  116. assert font.descender == -404 # HHead Descent.
  117. # Unconfirmed values.
  118. assert font.height == 2499
  119. assert font.max_advance_width == 1130
  120. assert font.max_advance_height == 2499
  121. assert font.bbox == (4, -355, 1185, 2095)
  122. def test_ft2font_invalid_args(tmp_path):
  123. # filename argument.
  124. with pytest.raises(TypeError, match='to a font file or a binary-mode file object'):
  125. ft2font.FT2Font(None)
  126. with pytest.raises(TypeError, match='to a font file or a binary-mode file object'):
  127. ft2font.FT2Font(object()) # Not bytes or string, and has no read() method.
  128. file = tmp_path / 'invalid-font.ttf'
  129. file.write_text('This is not a valid font file.')
  130. with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'),
  131. file.open('rt') as fd):
  132. ft2font.FT2Font(fd)
  133. with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'),
  134. file.open('wt') as fd):
  135. ft2font.FT2Font(fd)
  136. with (pytest.raises(TypeError, match='to a font file or a binary-mode file object'),
  137. file.open('wb') as fd):
  138. ft2font.FT2Font(fd)
  139. file = fm.findfont('DejaVu Sans')
  140. # hinting_factor argument.
  141. with pytest.raises(TypeError, match='incompatible constructor arguments'):
  142. ft2font.FT2Font(file, 1.3)
  143. with pytest.raises(ValueError, match='hinting_factor must be greater than 0'):
  144. ft2font.FT2Font(file, 0)
  145. with pytest.raises(TypeError, match='incompatible constructor arguments'):
  146. # failing to be a list will fail before the 0
  147. ft2font.FT2Font(file, _fallback_list=(0,)) # type: ignore[arg-type]
  148. with pytest.raises(TypeError, match='incompatible constructor arguments'):
  149. ft2font.FT2Font(file, _fallback_list=[0]) # type: ignore[list-item]
  150. # kerning_factor argument.
  151. with pytest.raises(TypeError, match='incompatible constructor arguments'):
  152. ft2font.FT2Font(file, _kerning_factor=1.3)
  153. def test_ft2font_clear():
  154. file = fm.findfont('DejaVu Sans')
  155. font = ft2font.FT2Font(file)
  156. assert font.get_num_glyphs() == 0
  157. assert font.get_width_height() == (0, 0)
  158. assert font.get_bitmap_offset() == (0, 0)
  159. font.set_text('ABabCDcd')
  160. assert font.get_num_glyphs() == 8
  161. assert font.get_width_height() != (0, 0)
  162. assert font.get_bitmap_offset() != (0, 0)
  163. font.clear()
  164. assert font.get_num_glyphs() == 0
  165. assert font.get_width_height() == (0, 0)
  166. assert font.get_bitmap_offset() == (0, 0)
  167. def test_ft2font_set_size():
  168. file = fm.findfont('DejaVu Sans')
  169. # Default is 12pt @ 72 dpi.
  170. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=1)
  171. font.set_text('ABabCDcd')
  172. orig = font.get_width_height()
  173. font.set_size(24, 72)
  174. font.set_text('ABabCDcd')
  175. assert font.get_width_height() == tuple(pytest.approx(2 * x, 1e-1) for x in orig)
  176. font.set_size(12, 144)
  177. font.set_text('ABabCDcd')
  178. assert font.get_width_height() == tuple(pytest.approx(2 * x, 1e-1) for x in orig)
  179. def test_ft2font_charmaps():
  180. def enc(name):
  181. # We don't expose the encoding enum from FreeType, but can generate it here.
  182. # For DejaVu, there are 5 charmaps, but only 2 have enum entries in FreeType.
  183. e = 0
  184. for x in name:
  185. e <<= 8
  186. e += ord(x)
  187. return e
  188. file = fm.findfont('DejaVu Sans')
  189. font = ft2font.FT2Font(file)
  190. assert font.num_charmaps == 5
  191. # Unicode.
  192. font.select_charmap(enc('unic'))
  193. unic = font.get_charmap()
  194. font.set_charmap(0) # Unicode platform, Unicode BMP only.
  195. after = font.get_charmap()
  196. assert len(after) <= len(unic)
  197. for chr, glyph in after.items():
  198. assert unic[chr] == glyph == font.get_char_index(chr)
  199. font.set_charmap(1) # Unicode platform, modern subtable.
  200. after = font.get_charmap()
  201. assert unic == after
  202. font.set_charmap(3) # Windows platform, Unicode BMP only.
  203. after = font.get_charmap()
  204. assert len(after) <= len(unic)
  205. for chr, glyph in after.items():
  206. assert unic[chr] == glyph == font.get_char_index(chr)
  207. font.set_charmap(4) # Windows platform, Unicode full repertoire, modern subtable.
  208. after = font.get_charmap()
  209. assert unic == after
  210. # This is just a random sample from FontForge.
  211. glyph_names = {
  212. 'non-existent-glyph-name': 0,
  213. 'plusminus': 115,
  214. 'Racute': 278,
  215. 'perthousand': 2834,
  216. 'seveneighths': 3057,
  217. 'triagup': 3721,
  218. 'uni01D3': 405,
  219. 'uni0417': 939,
  220. 'uni2A02': 4464,
  221. 'u1D305': 5410,
  222. 'u1F0A1': 5784,
  223. }
  224. for name, index in glyph_names.items():
  225. assert font.get_name_index(name) == index
  226. if name == 'non-existent-glyph-name':
  227. name = '.notdef'
  228. # This doesn't always apply, but it does for DejaVu Sans.
  229. assert font.get_glyph_name(index) == name
  230. # Apple Roman.
  231. font.select_charmap(enc('armn'))
  232. armn = font.get_charmap()
  233. font.set_charmap(2) # Macintosh platform, Roman.
  234. after = font.get_charmap()
  235. assert armn == after
  236. assert len(armn) <= 256 # 8-bit encoding.
  237. # The first 128 characters of Apple Roman match ASCII, which also matches Unicode.
  238. for o in range(1, 128):
  239. if o not in armn or o not in unic:
  240. continue
  241. assert unic[o] == armn[o]
  242. # Check a couple things outside the ASCII set that are different in each charset.
  243. examples = [
  244. # (Unicode, Macintosh)
  245. (0x2020, 0xA0), # Dagger.
  246. (0x00B0, 0xA1), # Degree symbol.
  247. (0x00A3, 0xA3), # Pound sign.
  248. (0x00A7, 0xA4), # Section sign.
  249. (0x00B6, 0xA6), # Pilcrow.
  250. (0x221E, 0xB0), # Infinity symbol.
  251. ]
  252. for u, m in examples:
  253. # Though the encoding is different, the glyph should be the same.
  254. assert unic[u] == armn[m]
  255. _expected_sfnt_names = {
  256. 'DejaVu Sans': {
  257. 0: 'Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.\n'
  258. 'Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.\n'
  259. 'DejaVu changes are in public domain\n',
  260. 1: 'DejaVu Sans',
  261. 2: 'Book',
  262. 3: 'DejaVu Sans',
  263. 4: 'DejaVu Sans',
  264. 5: 'Version 2.35',
  265. 6: 'DejaVuSans',
  266. 8: 'DejaVu fonts team',
  267. 11: 'http://dejavu.sourceforge.net',
  268. 13: 'Fonts are (c) Bitstream (see below). '
  269. 'DejaVu changes are in public domain. '
  270. '''Glyphs imported from Arev fonts are (c) Tavmjung Bah (see below)
  271. Bitstream Vera Fonts Copyright
  272. ------------------------------
  273. Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
  274. a trademark of Bitstream, Inc.
  275. Permission is hereby granted, free of charge, to any person obtaining a copy
  276. of the fonts accompanying this license ("Fonts") and associated
  277. documentation files (the "Font Software"), to reproduce and distribute the
  278. Font Software, including without limitation the rights to use, copy, merge,
  279. publish, distribute, and/or sell copies of the Font Software, and to permit
  280. persons to whom the Font Software is furnished to do so, subject to the
  281. following conditions:
  282. The above copyright and trademark notices and this permission notice shall
  283. be included in all copies of one or more of the Font Software typefaces.
  284. The Font Software may be modified, altered, or added to, and in particular
  285. the designs of glyphs or characters in the Fonts may be modified and
  286. additional glyphs or characters may be added to the Fonts, only if the fonts
  287. are renamed to names not containing either the words "Bitstream" or the word
  288. "Vera".
  289. This License becomes null and void to the extent applicable to Fonts or Font
  290. Software that has been modified and is distributed under the "Bitstream
  291. Vera" names.
  292. The Font Software may be sold as part of a larger software package but no
  293. copy of one or more of the Font Software typefaces may be sold by itself.
  294. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  295. OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
  296. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
  297. TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
  298. FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
  299. ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
  300. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
  301. THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
  302. FONT SOFTWARE.
  303. Except as contained in this notice, the names of Gnome, the Gnome
  304. Foundation, and Bitstream Inc., shall not be used in advertising or
  305. otherwise to promote the sale, use or other dealings in this Font Software
  306. without prior written authorization from the Gnome Foundation or Bitstream
  307. Inc., respectively. For further information, contact: fonts at gnome dot
  308. org. ''' '''
  309. Arev Fonts Copyright
  310. ------------------------------
  311. Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
  312. Permission is hereby granted, free of charge, to any person obtaining
  313. a copy of the fonts accompanying this license ("Fonts") and
  314. associated documentation files (the "Font Software"), to reproduce
  315. and distribute the modifications to the Bitstream Vera Font Software,
  316. including without limitation the rights to use, copy, merge, publish,
  317. distribute, and/or sell copies of the Font Software, and to permit
  318. persons to whom the Font Software is furnished to do so, subject to
  319. the following conditions:
  320. The above copyright and trademark notices and this permission notice
  321. shall be included in all copies of one or more of the Font Software
  322. typefaces.
  323. The Font Software may be modified, altered, or added to, and in
  324. particular the designs of glyphs or characters in the Fonts may be
  325. modified and additional glyphs or characters may be added to the
  326. Fonts, only if the fonts are renamed to names not containing either
  327. the words "Tavmjong Bah" or the word "Arev".
  328. This License becomes null and void to the extent applicable to Fonts
  329. or Font Software that has been modified and is distributed under the ''' '''
  330. "Tavmjong Bah Arev" names.
  331. The Font Software may be sold as part of a larger software package but
  332. no copy of one or more of the Font Software typefaces may be sold by
  333. itself.
  334. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  335. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
  336. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
  337. OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
  338. TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  339. INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
  340. DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  341. FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
  342. OTHER DEALINGS IN THE FONT SOFTWARE.
  343. Except as contained in this notice, the name of Tavmjong Bah shall not
  344. be used in advertising or otherwise to promote the sale, use or other
  345. dealings in this Font Software without prior written authorization
  346. from Tavmjong Bah. For further information, contact: tavmjong @ free
  347. . fr.''',
  348. 14: 'http://dejavu.sourceforge.net/wiki/index.php/License',
  349. 16: 'DejaVu Sans',
  350. 17: 'Book',
  351. },
  352. 'cmtt10': {
  353. 0: 'Copyright (C) 1994, Basil K. Malyshev. All Rights Reserved.'
  354. '012BaKoMa Fonts Collection, Level-B.',
  355. 1: 'cmtt10',
  356. 2: 'Regular',
  357. 3: 'FontMonger:cmtt10',
  358. 4: 'cmtt10',
  359. 5: '1.1/12-Nov-94',
  360. 6: 'Cmtt10',
  361. },
  362. 'STIXSizeTwoSym:bold': {
  363. 0: 'Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the '
  364. 'American Chemical Society, the American Institute of Physics, the American '
  365. 'Mathematical Society, the American Physical Society, Elsevier, Inc., and '
  366. 'The Institute of Electrical and Electronic Engineers, Inc. Portions '
  367. 'copyright (c) 1998-2003 by MicroPress, Inc. Portions copyright (c) 1990 by '
  368. 'Elsevier, Inc. All rights reserved.',
  369. 1: 'STIXSizeTwoSym',
  370. 2: 'Bold',
  371. 3: 'FontMaster:STIXSizeTwoSym-Bold:1.0.0',
  372. 4: 'STIXSizeTwoSym-Bold',
  373. 5: 'Version 1.0.0',
  374. 6: 'STIXSizeTwoSym-Bold',
  375. 7: 'STIX Fonts(TM) is a trademark of The Institute of Electrical and '
  376. 'Electronics Engineers, Inc.',
  377. 9: 'MicroPress Inc., with final additions and corrections provided by Coen '
  378. 'Hoffman, Elsevier (retired)',
  379. 10: 'Arie de Ruiter, who in 1995 was Head of Information Technology '
  380. 'Development at Elsevier Science, made a proposal to the STI Pub group, an '
  381. 'informal group of publishers consisting of representatives from the '
  382. 'American Chemical Society (ACS), American Institute of Physics (AIP), '
  383. 'American Mathematical Society (AMS), American Physical Society (APS), '
  384. 'Elsevier, and Institute of Electrical and Electronics Engineers (IEEE). '
  385. 'De Ruiter encouraged the members to consider development of a series of '
  386. 'Web fonts, which he proposed should be called the Scientific and '
  387. 'Technical Information eXchange, or STIX, Fonts. All STI Pub member '
  388. 'organizations enthusiastically endorsed this proposal, and the STI Pub '
  389. 'group agreed to embark on what has become a twelve-year project. The goal '
  390. 'of the project was to identify all alphabetic, symbolic, and other '
  391. 'special characters used in any facet of scientific publishing and to '
  392. 'create a set of Unicode-based fonts that would be distributed free to '
  393. 'every scientist, student, and other interested party worldwide. The fonts '
  394. 'would be consistent with the emerging Unicode standard, and would permit '
  395. 'universal representation of every character. With the release of the STIX '
  396. "fonts, de Ruiter's vision has been realized.",
  397. 11: 'http://www.stixfonts.org',
  398. 12: 'http://www.micropress-inc.com',
  399. 13: 'As a condition for receiving these fonts at no charge, each person '
  400. 'downloading the fonts must agree to some simple license terms. The '
  401. 'license is based on the SIL Open Font License '
  402. '<http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL>. The '
  403. 'SIL License is a free and open source license specifically designed for '
  404. 'fonts and related software. The basic terms are that the recipient will '
  405. 'not remove the copyright and trademark statements from the fonts and '
  406. 'that, if the person decides to create a derivative work based on the STIX '
  407. 'Fonts but incorporating some changes or enhancements, the derivative work '
  408. '("Modified Version") will carry a different name. The copyright and '
  409. 'trademark restrictions are part of the agreement between the STI Pub '
  410. 'companies and the typeface designer. The "renaming" restriction results '
  411. 'from the desire of the STI Pub companies to assure that the STIX Fonts '
  412. 'will continue to function in a predictable fashion for all that use them. '
  413. 'No copy of one or more of the individual Font typefaces that form the '
  414. 'STIX Fonts(TM) set may be sold by itself, but other than this one '
  415. 'restriction, licensees are free to sell the fonts either separately or as '
  416. 'part of a package that combines other software or fonts with this font '
  417. 'set.',
  418. 14: 'http://www.stixfonts.org/user_license.html',
  419. },
  420. }
  421. @pytest.mark.parametrize('font_name, expected', _expected_sfnt_names.items(),
  422. ids=_expected_sfnt_names.keys())
  423. def test_ft2font_get_sfnt(font_name, expected):
  424. file = fm.findfont(font_name)
  425. font = ft2font.FT2Font(file)
  426. sfnt = font.get_sfnt()
  427. for name, value in expected.items():
  428. # Macintosh, Unicode 1.0, English, name.
  429. assert sfnt.pop((1, 0, 0, name)) == value.encode('ascii')
  430. # Microsoft, Unicode, English United States, name.
  431. assert sfnt.pop((3, 1, 1033, name)) == value.encode('utf-16be')
  432. assert sfnt == {}
  433. _expected_sfnt_tables = {
  434. 'DejaVu Sans': {
  435. 'invalid': None,
  436. 'head': {
  437. 'version': (1, 0),
  438. 'fontRevision': (2, 22937),
  439. 'checkSumAdjustment': -175678572,
  440. 'magicNumber': 0x5F0F3CF5,
  441. 'flags': 31,
  442. 'unitsPerEm': 2048,
  443. 'created': (0, 3514699492), 'modified': (0, 3514699492),
  444. 'xMin': -2090, 'yMin': -948, 'xMax': 3673, 'yMax': 2524,
  445. 'macStyle': 0,
  446. 'lowestRecPPEM': 8,
  447. 'fontDirectionHint': 0,
  448. 'indexToLocFormat': 1,
  449. 'glyphDataFormat': 0,
  450. },
  451. 'maxp': {
  452. 'version': (1, 0),
  453. 'numGlyphs': 6241,
  454. 'maxPoints': 852, 'maxComponentPoints': 104, 'maxTwilightPoints': 16,
  455. 'maxContours': 43, 'maxComponentContours': 12,
  456. 'maxZones': 2,
  457. 'maxStorage': 153,
  458. 'maxFunctionDefs': 64,
  459. 'maxInstructionDefs': 0,
  460. 'maxStackElements': 1045,
  461. 'maxSizeOfInstructions': 534,
  462. 'maxComponentElements': 8,
  463. 'maxComponentDepth': 4,
  464. },
  465. 'OS/2': {
  466. 'version': 1,
  467. 'xAvgCharWidth': 1038,
  468. 'usWeightClass': 400, 'usWidthClass': 5,
  469. 'fsType': 0,
  470. 'ySubscriptXSize': 1331, 'ySubscriptYSize': 1433,
  471. 'ySubscriptXOffset': 0, 'ySubscriptYOffset': 286,
  472. 'ySuperscriptXSize': 1331, 'ySuperscriptYSize': 1433,
  473. 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 983,
  474. 'yStrikeoutSize': 102, 'yStrikeoutPosition': 530,
  475. 'sFamilyClass': 0,
  476. 'panose': b'\x02\x0b\x06\x03\x03\x08\x04\x02\x02\x04',
  477. 'ulCharRange': (3875565311, 3523280383, 170156073, 67117068),
  478. 'achVendID': b'PfEd',
  479. 'fsSelection': 64, 'fsFirstCharIndex': 32, 'fsLastCharIndex': 65535,
  480. },
  481. 'hhea': {
  482. 'version': (1, 0),
  483. 'ascent': 1901, 'descent': -483, 'lineGap': 0,
  484. 'advanceWidthMax': 3838,
  485. 'minLeftBearing': -2090, 'minRightBearing': -1455,
  486. 'xMaxExtent': 3673,
  487. 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0,
  488. 'metricDataFormat': 0, 'numOfLongHorMetrics': 6226,
  489. },
  490. 'vhea': None,
  491. 'post': {
  492. 'format': (2, 0),
  493. 'isFixedPitch': 0, 'italicAngle': (0, 0),
  494. 'underlinePosition': -130, 'underlineThickness': 90,
  495. 'minMemType42': 0, 'maxMemType42': 0,
  496. 'minMemType1': 0, 'maxMemType1': 0,
  497. },
  498. 'pclt': None,
  499. },
  500. 'cmtt10': {
  501. 'invalid': None,
  502. 'head': {
  503. 'version': (1, 0),
  504. 'fontRevision': (1, 0),
  505. 'checkSumAdjustment': 555110277,
  506. 'magicNumber': 0x5F0F3CF5,
  507. 'flags': 3,
  508. 'unitsPerEm': 2048,
  509. 'created': (0, 0), 'modified': (0, 0),
  510. 'xMin': -12, 'yMin': -477, 'xMax': 1280, 'yMax': 1430,
  511. 'macStyle': 0,
  512. 'lowestRecPPEM': 6,
  513. 'fontDirectionHint': 2,
  514. 'indexToLocFormat': 1,
  515. 'glyphDataFormat': 0,
  516. },
  517. 'maxp': {
  518. 'version': (1, 0),
  519. 'numGlyphs': 133,
  520. 'maxPoints': 94, 'maxComponentPoints': 0, 'maxTwilightPoints': 12,
  521. 'maxContours': 5, 'maxComponentContours': 0,
  522. 'maxZones': 2,
  523. 'maxStorage': 6,
  524. 'maxFunctionDefs': 64,
  525. 'maxInstructionDefs': 0,
  526. 'maxStackElements': 200,
  527. 'maxSizeOfInstructions': 100,
  528. 'maxComponentElements': 4,
  529. 'maxComponentDepth': 1,
  530. },
  531. 'OS/2': {
  532. 'version': 0,
  533. 'xAvgCharWidth': 1075,
  534. 'usWeightClass': 400, 'usWidthClass': 5,
  535. 'fsType': 0,
  536. 'ySubscriptXSize': 410, 'ySubscriptYSize': 369,
  537. 'ySubscriptXOffset': 0, 'ySubscriptYOffset': -469,
  538. 'ySuperscriptXSize': 410, 'ySuperscriptYSize': 369,
  539. 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 1090,
  540. 'yStrikeoutSize': 102, 'yStrikeoutPosition': 530,
  541. 'sFamilyClass': 0,
  542. 'panose': b'\x02\x0b\x05\x00\x00\x00\x00\x00\x00\x00',
  543. 'ulCharRange': (0, 0, 0, 0),
  544. 'achVendID': b'\x00\x00\x00\x00',
  545. 'fsSelection': 64, 'fsFirstCharIndex': 32, 'fsLastCharIndex': 9835,
  546. },
  547. 'hhea': {
  548. 'version': (1, 0),
  549. 'ascent': 1276, 'descent': -489, 'lineGap': 0,
  550. 'advanceWidthMax': 1536,
  551. 'minLeftBearing': -12, 'minRightBearing': -29,
  552. 'xMaxExtent': 1280,
  553. 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0,
  554. 'metricDataFormat': 0, 'numOfLongHorMetrics': 133,
  555. },
  556. 'vhea': None,
  557. 'post': {
  558. 'format': (2, 0),
  559. 'isFixedPitch': 0, 'italicAngle': (0, 0),
  560. 'underlinePosition': -133, 'underlineThickness': 20,
  561. 'minMemType42': 0, 'maxMemType42': 0,
  562. 'minMemType1': 0, 'maxMemType1': 0,
  563. },
  564. 'pclt': {
  565. 'version': (1, 0),
  566. 'fontNumber': 2147483648,
  567. 'pitch': 1075,
  568. 'xHeight': 905,
  569. 'style': 0,
  570. 'typeFamily': 0,
  571. 'capHeight': 1276,
  572. 'symbolSet': 0,
  573. 'typeFace': b'cmtt10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
  574. 'characterComplement': b'\xff\xff\xff\xff7\xff\xff\xfe',
  575. 'strokeWeight': 0,
  576. 'widthType': -5,
  577. 'serifStyle': 64,
  578. },
  579. },
  580. 'STIXSizeTwoSym:bold': {
  581. 'invalid': None,
  582. 'head': {
  583. 'version': (1, 0),
  584. 'fontRevision': (1, 0),
  585. 'checkSumAdjustment': 1803408080,
  586. 'magicNumber': 0x5F0F3CF5,
  587. 'flags': 11,
  588. 'unitsPerEm': 1000,
  589. 'created': (0, 3359035786), 'modified': (0, 3359035786),
  590. 'xMin': 4, 'yMin': -355, 'xMax': 1185, 'yMax': 2095,
  591. 'macStyle': 1,
  592. 'lowestRecPPEM': 8,
  593. 'fontDirectionHint': 2,
  594. 'indexToLocFormat': 0,
  595. 'glyphDataFormat': 0,
  596. },
  597. 'maxp': {
  598. 'version': (1, 0),
  599. 'numGlyphs': 20,
  600. 'maxPoints': 37, 'maxComponentPoints': 0, 'maxTwilightPoints': 0,
  601. 'maxContours': 1, 'maxComponentContours': 0,
  602. 'maxZones': 2,
  603. 'maxStorage': 1,
  604. 'maxFunctionDefs': 64,
  605. 'maxInstructionDefs': 0,
  606. 'maxStackElements': 64,
  607. 'maxSizeOfInstructions': 0,
  608. 'maxComponentElements': 0,
  609. 'maxComponentDepth': 0,
  610. },
  611. 'OS/2': {
  612. 'version': 2,
  613. 'xAvgCharWidth': 598,
  614. 'usWeightClass': 700, 'usWidthClass': 5,
  615. 'fsType': 0,
  616. 'ySubscriptXSize': 500, 'ySubscriptYSize': 500,
  617. 'ySubscriptXOffset': 0, 'ySubscriptYOffset': 250,
  618. 'ySuperscriptXSize': 500, 'ySuperscriptYSize': 500,
  619. 'ySuperscriptXOffset': 0, 'ySuperscriptYOffset': 500,
  620. 'yStrikeoutSize': 20, 'yStrikeoutPosition': 1037,
  621. 'sFamilyClass': 0,
  622. 'panose': b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
  623. 'ulCharRange': (3, 192, 0, 0),
  624. 'achVendID': b'STIX',
  625. 'fsSelection': 32, 'fsFirstCharIndex': 32, 'fsLastCharIndex': 10217,
  626. },
  627. 'hhea': {
  628. 'version': (1, 0),
  629. 'ascent': 2095, 'descent': -404, 'lineGap': 0,
  630. 'advanceWidthMax': 1130,
  631. 'minLeftBearing': 0, 'minRightBearing': -55,
  632. 'xMaxExtent': 1185,
  633. 'caretSlopeRise': 1, 'caretSlopeRun': 0, 'caretOffset': 0,
  634. 'metricDataFormat': 0, 'numOfLongHorMetrics': 19,
  635. },
  636. 'vhea': None,
  637. 'post': {
  638. 'format': (2, 0),
  639. 'isFixedPitch': 0, 'italicAngle': (0, 0),
  640. 'underlinePosition': -123, 'underlineThickness': 20,
  641. 'minMemType42': 0, 'maxMemType42': 0,
  642. 'minMemType1': 0, 'maxMemType1': 0,
  643. },
  644. 'pclt': None,
  645. },
  646. }
  647. @pytest.mark.parametrize('font_name', _expected_sfnt_tables.keys())
  648. @pytest.mark.parametrize('header', _expected_sfnt_tables['DejaVu Sans'].keys())
  649. def test_ft2font_get_sfnt_table(font_name, header):
  650. file = fm.findfont(font_name)
  651. font = ft2font.FT2Font(file)
  652. assert font.get_sfnt_table(header) == _expected_sfnt_tables[font_name][header]
  653. @pytest.mark.parametrize('left, right, unscaled, unfitted, default', [
  654. # These are all the same class.
  655. ('A', 'A', 57, 248, 256), ('A', 'À', 57, 248, 256), ('A', 'Á', 57, 248, 256),
  656. ('A', 'Â', 57, 248, 256), ('A', 'Ã', 57, 248, 256), ('A', 'Ä', 57, 248, 256),
  657. # And a few other random ones.
  658. ('D', 'A', -36, -156, -128), ('T', '.', -243, -1056, -1024),
  659. ('X', 'C', -149, -647, -640), ('-', 'J', 114, 495, 512),
  660. ])
  661. def test_ft2font_get_kerning(left, right, unscaled, unfitted, default):
  662. file = fm.findfont('DejaVu Sans')
  663. # With unscaled, these settings should produce exact values found in FontForge.
  664. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  665. font.set_size(100, 100)
  666. assert font.get_kerning(font.get_char_index(ord(left)),
  667. font.get_char_index(ord(right)),
  668. ft2font.Kerning.UNSCALED) == unscaled
  669. assert font.get_kerning(font.get_char_index(ord(left)),
  670. font.get_char_index(ord(right)),
  671. ft2font.Kerning.UNFITTED) == unfitted
  672. assert font.get_kerning(font.get_char_index(ord(left)),
  673. font.get_char_index(ord(right)),
  674. ft2font.Kerning.DEFAULT) == default
  675. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  676. match='Use Kerning.UNSCALED instead'):
  677. k = ft2font.KERNING_UNSCALED
  678. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  679. match='Use Kerning enum values instead'):
  680. assert font.get_kerning(font.get_char_index(ord(left)),
  681. font.get_char_index(ord(right)),
  682. int(k)) == unscaled
  683. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  684. match='Use Kerning.UNFITTED instead'):
  685. k = ft2font.KERNING_UNFITTED
  686. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  687. match='Use Kerning enum values instead'):
  688. assert font.get_kerning(font.get_char_index(ord(left)),
  689. font.get_char_index(ord(right)),
  690. int(k)) == unfitted
  691. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  692. match='Use Kerning.DEFAULT instead'):
  693. k = ft2font.KERNING_DEFAULT
  694. with pytest.warns(mpl.MatplotlibDeprecationWarning,
  695. match='Use Kerning enum values instead'):
  696. assert font.get_kerning(font.get_char_index(ord(left)),
  697. font.get_char_index(ord(right)),
  698. int(k)) == default
  699. def test_ft2font_set_text():
  700. file = fm.findfont('DejaVu Sans')
  701. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  702. xys = font.set_text('')
  703. np.testing.assert_array_equal(xys, np.empty((0, 2)))
  704. assert font.get_width_height() == (0, 0)
  705. assert font.get_num_glyphs() == 0
  706. assert font.get_descent() == 0
  707. assert font.get_bitmap_offset() == (0, 0)
  708. # This string uses all the kerning pairs defined for test_ft2font_get_kerning.
  709. xys = font.set_text('AADAT.XC-J')
  710. np.testing.assert_array_equal(
  711. xys,
  712. [(0, 0), (512, 0), (1024, 0), (1600, 0), (2112, 0), (2496, 0), (2688, 0),
  713. (3200, 0), (3712, 0), (4032, 0)])
  714. assert font.get_width_height() == (4288, 768)
  715. assert font.get_num_glyphs() == 10
  716. assert font.get_descent() == 192
  717. assert font.get_bitmap_offset() == (6, 0)
  718. def test_ft2font_loading():
  719. file = fm.findfont('DejaVu Sans')
  720. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  721. for glyph in [font.load_char(ord('M')),
  722. font.load_glyph(font.get_char_index(ord('M')))]:
  723. assert glyph is not None
  724. assert glyph.width == 576
  725. assert glyph.height == 576
  726. assert glyph.horiBearingX == 0
  727. assert glyph.horiBearingY == 576
  728. assert glyph.horiAdvance == 640
  729. assert glyph.linearHoriAdvance == 678528
  730. assert glyph.vertBearingX == -384
  731. assert glyph.vertBearingY == 64
  732. assert glyph.vertAdvance == 832
  733. assert glyph.bbox == (54, 0, 574, 576)
  734. assert font.get_num_glyphs() == 2 # Both count as loaded.
  735. # But neither has been placed anywhere.
  736. assert font.get_width_height() == (0, 0)
  737. assert font.get_descent() == 0
  738. assert font.get_bitmap_offset() == (0, 0)
  739. def test_ft2font_drawing():
  740. expected_str = (
  741. ' ',
  742. '11 11 ',
  743. '11 11 ',
  744. '1 1 1 1 ',
  745. '1 1 1 1 ',
  746. '1 1 1 1 ',
  747. '1 11 1 ',
  748. '1 11 1 ',
  749. '1 1 ',
  750. '1 1 ',
  751. ' ',
  752. )
  753. expected = np.array([
  754. [int(c) for c in line.replace(' ', '0')] for line in expected_str
  755. ])
  756. expected *= 255
  757. file = fm.findfont('DejaVu Sans')
  758. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  759. font.set_text('M')
  760. font.draw_glyphs_to_bitmap(antialiased=False)
  761. image = font.get_image()
  762. np.testing.assert_array_equal(image, expected)
  763. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  764. glyph = font.load_char(ord('M'))
  765. image = ft2font.FT2Image(expected.shape[1], expected.shape[0])
  766. font.draw_glyph_to_bitmap(image, -1, 1, glyph, antialiased=False)
  767. np.testing.assert_array_equal(image, expected)
  768. def test_ft2font_get_path():
  769. file = fm.findfont('DejaVu Sans')
  770. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0)
  771. vertices, codes = font.get_path()
  772. assert vertices.shape == (0, 2)
  773. assert codes.shape == (0, )
  774. font.load_char(ord('M'))
  775. vertices, codes = font.get_path()
  776. expected_vertices = np.array([
  777. (0.843750, 9.000000), (2.609375, 9.000000), # Top left.
  778. (4.906250, 2.875000), # Top of midpoint.
  779. (7.218750, 9.000000), (8.968750, 9.000000), # Top right.
  780. (8.968750, 0.000000), (7.843750, 0.000000), # Bottom right.
  781. (7.843750, 7.906250), # Point under top right.
  782. (5.531250, 1.734375), (4.296875, 1.734375), # Bar under midpoint.
  783. (1.984375, 7.906250), # Point under top left.
  784. (1.984375, 0.000000), (0.843750, 0.000000), # Bottom left.
  785. (0.843750, 9.000000), # Back to top left corner.
  786. (0.000000, 0.000000),
  787. ])
  788. np.testing.assert_array_equal(vertices, expected_vertices)
  789. expected_codes = np.full(expected_vertices.shape[0], mpath.Path.LINETO,
  790. dtype=mpath.Path.code_type)
  791. expected_codes[0] = mpath.Path.MOVETO
  792. expected_codes[-1] = mpath.Path.CLOSEPOLY
  793. np.testing.assert_array_equal(codes, expected_codes)
  794. @pytest.mark.parametrize('family_name, file_name',
  795. [("WenQuanYi Zen Hei", "wqy-zenhei.ttc"),
  796. ("Noto Sans CJK JP", "NotoSansCJK.ttc"),
  797. ("Noto Sans TC", "NotoSansTC-Regular.otf")]
  798. )
  799. def test_fallback_smoke(family_name, file_name):
  800. fp = fm.FontProperties(family=[family_name])
  801. if Path(fm.findfont(fp)).name != file_name:
  802. pytest.skip(f"Font {family_name} ({file_name}) is missing")
  803. plt.rcParams['font.size'] = 20
  804. fig = plt.figure(figsize=(4.75, 1.85))
  805. fig.text(0.05, 0.45, "There are 几个汉字 in between!",
  806. family=['DejaVu Sans', family_name])
  807. fig.text(0.05, 0.85, "There are 几个汉字 in between!",
  808. family=[family_name])
  809. # TODO enable fallback for other backends!
  810. for fmt in ['png', 'raw']: # ["svg", "pdf", "ps"]:
  811. fig.savefig(io.BytesIO(), format=fmt)
  812. @pytest.mark.parametrize('family_name, file_name',
  813. [("WenQuanYi Zen Hei", "wqy-zenhei"),
  814. ("Noto Sans CJK JP", "NotoSansCJK"),
  815. ("Noto Sans TC", "NotoSansTC-Regular.otf")]
  816. )
  817. @check_figures_equal(extensions=["png", "pdf", "eps", "svg"])
  818. def test_font_fallback_chinese(fig_test, fig_ref, family_name, file_name):
  819. fp = fm.FontProperties(family=[family_name])
  820. if file_name not in Path(fm.findfont(fp)).name:
  821. pytest.skip(f"Font {family_name} ({file_name}) is missing")
  822. text = ["There are", "几个汉字", "in between!"]
  823. plt.rcParams["font.size"] = 20
  824. test_fonts = [["DejaVu Sans", family_name]] * 3
  825. ref_fonts = [["DejaVu Sans"], [family_name], ["DejaVu Sans"]]
  826. for j, (txt, test_font, ref_font) in enumerate(
  827. zip(text, test_fonts, ref_fonts)
  828. ):
  829. fig_ref.text(0.05, .85 - 0.15*j, txt, family=ref_font)
  830. fig_test.text(0.05, .85 - 0.15*j, txt, family=test_font)
  831. @pytest.mark.parametrize("font_list",
  832. [['DejaVu Serif', 'DejaVu Sans'],
  833. ['DejaVu Sans Mono']],
  834. ids=["two fonts", "one font"])
  835. def test_fallback_missing(recwarn, font_list):
  836. fig = plt.figure()
  837. fig.text(.5, .5, "Hello 🙃 World!", family=font_list)
  838. fig.canvas.draw()
  839. assert all(isinstance(warn.message, UserWarning) for warn in recwarn)
  840. # not sure order is guaranteed on the font listing so
  841. assert recwarn[0].message.args[0].startswith(
  842. "Glyph 128579 (\\N{UPSIDE-DOWN FACE}) missing from font(s)")
  843. assert all([font in recwarn[0].message.args[0] for font in font_list])
  844. @pytest.mark.parametrize(
  845. "family_name, file_name",
  846. [
  847. ("WenQuanYi Zen Hei", "wqy-zenhei"),
  848. ("Noto Sans CJK JP", "NotoSansCJK"),
  849. ("Noto Sans TC", "NotoSansTC-Regular.otf")
  850. ],
  851. )
  852. def test__get_fontmap(family_name, file_name):
  853. fp = fm.FontProperties(family=[family_name])
  854. found_file_name = Path(fm.findfont(fp)).name
  855. if file_name not in found_file_name:
  856. pytest.skip(f"Font {family_name} ({file_name}) is missing")
  857. text = "There are 几个汉字 in between!"
  858. ft = fm.get_font(
  859. fm.fontManager._find_fonts_by_props(
  860. fm.FontProperties(family=["DejaVu Sans", family_name])
  861. )
  862. )
  863. fontmap = ft._get_fontmap(text)
  864. for char, font in fontmap.items():
  865. if ord(char) > 127:
  866. assert Path(font.fname).name == found_file_name
  867. else:
  868. assert Path(font.fname).name == "DejaVuSans.ttf"