test_afm.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from io import BytesIO
  2. import pytest
  3. import logging
  4. from matplotlib import _afm
  5. from matplotlib import font_manager as fm
  6. # See note in afm.py re: use of comma as decimal separator in the
  7. # UnderlineThickness field and re: use of non-ASCII characters in the Notice
  8. # field.
  9. AFM_TEST_DATA = b"""StartFontMetrics 2.0
  10. Comment Comments are ignored.
  11. Comment Creation Date:Mon Nov 13 12:34:11 GMT 2017
  12. FontName MyFont-Bold
  13. EncodingScheme FontSpecific
  14. FullName My Font Bold
  15. FamilyName Test Fonts
  16. Weight Bold
  17. ItalicAngle 0.0
  18. IsFixedPitch false
  19. UnderlinePosition -100
  20. UnderlineThickness 56,789
  21. Version 001.000
  22. Notice Copyright \xa9 2017 No one.
  23. FontBBox 0 -321 1234 369
  24. StartCharMetrics 3
  25. C 0 ; WX 250 ; N space ; B 0 0 0 0 ;
  26. C 42 ; WX 1141 ; N foo ; B 40 60 800 360 ;
  27. C 99 ; WX 583 ; N bar ; B 40 -10 543 210 ;
  28. EndCharMetrics
  29. EndFontMetrics
  30. """
  31. def test_nonascii_str():
  32. # This tests that we also decode bytes as utf-8 properly.
  33. # Else, font files with non ascii characters fail to load.
  34. inp_str = "привет"
  35. byte_str = inp_str.encode("utf8")
  36. ret = _afm._to_str(byte_str)
  37. assert ret == inp_str
  38. def test_parse_header():
  39. fh = BytesIO(AFM_TEST_DATA)
  40. header = _afm._parse_header(fh)
  41. assert header == {
  42. b'StartFontMetrics': 2.0,
  43. b'FontName': 'MyFont-Bold',
  44. b'EncodingScheme': 'FontSpecific',
  45. b'FullName': 'My Font Bold',
  46. b'FamilyName': 'Test Fonts',
  47. b'Weight': 'Bold',
  48. b'ItalicAngle': 0.0,
  49. b'IsFixedPitch': False,
  50. b'UnderlinePosition': -100,
  51. b'UnderlineThickness': 56.789,
  52. b'Version': '001.000',
  53. b'Notice': b'Copyright \xa9 2017 No one.',
  54. b'FontBBox': [0, -321, 1234, 369],
  55. b'StartCharMetrics': 3,
  56. }
  57. def test_parse_char_metrics():
  58. fh = BytesIO(AFM_TEST_DATA)
  59. _afm._parse_header(fh) # position
  60. metrics = _afm._parse_char_metrics(fh)
  61. assert metrics == (
  62. {0: (250.0, 'space', [0, 0, 0, 0]),
  63. 42: (1141.0, 'foo', [40, 60, 800, 360]),
  64. 99: (583.0, 'bar', [40, -10, 543, 210]),
  65. },
  66. {'space': (250.0, 'space', [0, 0, 0, 0]),
  67. 'foo': (1141.0, 'foo', [40, 60, 800, 360]),
  68. 'bar': (583.0, 'bar', [40, -10, 543, 210]),
  69. })
  70. def test_get_familyname_guessed():
  71. fh = BytesIO(AFM_TEST_DATA)
  72. font = _afm.AFM(fh)
  73. del font._header[b'FamilyName'] # remove FamilyName, so we have to guess
  74. assert font.get_familyname() == 'My Font'
  75. def test_font_manager_weight_normalization():
  76. font = _afm.AFM(BytesIO(
  77. AFM_TEST_DATA.replace(b"Weight Bold\n", b"Weight Custom\n")))
  78. assert fm.afmFontProperty("", font).weight == "normal"
  79. @pytest.mark.parametrize(
  80. "afm_data",
  81. [
  82. b"""nope
  83. really nope""",
  84. b"""StartFontMetrics 2.0
  85. Comment Comments are ignored.
  86. Comment Creation Date:Mon Nov 13 12:34:11 GMT 2017
  87. FontName MyFont-Bold
  88. EncodingScheme FontSpecific""",
  89. ],
  90. )
  91. def test_bad_afm(afm_data):
  92. fh = BytesIO(afm_data)
  93. with pytest.raises(RuntimeError):
  94. _afm._parse_header(fh)
  95. @pytest.mark.parametrize(
  96. "afm_data",
  97. [
  98. b"""StartFontMetrics 2.0
  99. Comment Comments are ignored.
  100. Comment Creation Date:Mon Nov 13 12:34:11 GMT 2017
  101. Aardvark bob
  102. FontName MyFont-Bold
  103. EncodingScheme FontSpecific
  104. StartCharMetrics 3""",
  105. b"""StartFontMetrics 2.0
  106. Comment Comments are ignored.
  107. Comment Creation Date:Mon Nov 13 12:34:11 GMT 2017
  108. ItalicAngle zero degrees
  109. FontName MyFont-Bold
  110. EncodingScheme FontSpecific
  111. StartCharMetrics 3""",
  112. ],
  113. )
  114. def test_malformed_header(afm_data, caplog):
  115. fh = BytesIO(afm_data)
  116. with caplog.at_level(logging.ERROR):
  117. _afm._parse_header(fh)
  118. assert len(caplog.records) == 1