python-fontbro 0.28.0__tar.gz → 0.28.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. {python_fontbro-0.28.0/python_fontbro.egg-info → python_fontbro-0.28.1}/PKG-INFO +1 -1
  2. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/metadata.py +1 -1
  3. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/names.py +28 -12
  4. {python_fontbro-0.28.0 → python_fontbro-0.28.1/python_fontbro.egg-info}/PKG-INFO +1 -1
  5. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_names.py +80 -2
  6. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/LICENSE.txt +0 -0
  7. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/MANIFEST.in +0 -0
  8. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/README.md +0 -0
  9. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/__init__.py +0 -0
  10. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/bitmap.py +0 -0
  11. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/color.py +0 -0
  12. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/data/family-classifications.json +0 -0
  13. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/data/features.json +0 -0
  14. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/data/unicode-blocks.json +0 -0
  15. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/data/unicode-scripts.json +0 -0
  16. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/embedding_permissions.py +0 -0
  17. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/exceptions.py +0 -0
  18. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/family_classification.py +0 -0
  19. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/features.py +0 -0
  20. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/files.py +0 -0
  21. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/fingerprint.py +0 -0
  22. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/flags.py +0 -0
  23. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/font.py +0 -0
  24. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/glyphs.py +0 -0
  25. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/math.py +0 -0
  26. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/metrics.py +0 -0
  27. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/monospace.py +0 -0
  28. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/pixel.py +0 -0
  29. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/py.typed +0 -0
  30. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/render.py +0 -0
  31. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/sanitize.py +0 -0
  32. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/style_flags.py +0 -0
  33. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/subset.py +0 -0
  34. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/support.py +0 -0
  35. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/tables.py +0 -0
  36. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/unicode.py +0 -0
  37. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/utils.py +0 -0
  38. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/fontbro/variable.py +0 -0
  39. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/pyproject.toml +0 -0
  40. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/python_fontbro.egg-info/SOURCES.txt +0 -0
  41. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/python_fontbro.egg-info/dependency_links.txt +0 -0
  42. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/python_fontbro.egg-info/requires.txt +0 -0
  43. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/python_fontbro.egg-info/top_level.txt +0 -0
  44. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/setup.cfg +0 -0
  45. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/setup.py +0 -0
  46. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_bitmap.py +0 -0
  47. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_characters.py +0 -0
  48. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_clone.py +0 -0
  49. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_close.py +0 -0
  50. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_collection.py +0 -0
  51. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_color.py +0 -0
  52. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_context_manager.py +0 -0
  53. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_embedding_permissions.py +0 -0
  54. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_family_classification.py +0 -0
  55. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_family_name.py +0 -0
  56. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_features.py +0 -0
  57. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_filename.py +0 -0
  58. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_fingerprint.py +0 -0
  59. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_format.py +0 -0
  60. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_glyphs.py +0 -0
  61. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_image.py +0 -0
  62. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_init.py +0 -0
  63. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_instantiation.py +0 -0
  64. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_issues.py +0 -0
  65. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_italic_angle.py +0 -0
  66. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_monospace.py +0 -0
  67. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_pixel.py +0 -0
  68. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_rename.py +0 -0
  69. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_sanitize.py +0 -0
  70. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_save.py +0 -0
  71. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_str.py +0 -0
  72. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_style_flags.py +0 -0
  73. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_style_name.py +0 -0
  74. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_subset.py +0 -0
  75. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_support.py +0 -0
  76. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_svg.py +0 -0
  77. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_tables.py +0 -0
  78. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_unicode.py +0 -0
  79. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_unicode_blocks_and_scripts.py +0 -0
  80. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_update_unicode_data.py +0 -0
  81. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_variable.py +0 -0
  82. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_version.py +0 -0
  83. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_vertical_metrics.py +0 -0
  84. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_weight.py +0 -0
  85. {python_fontbro-0.28.0 → python_fontbro-0.28.1}/tests/test_width.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fontbro
3
- Version: 0.28.0
3
+ Version: 0.28.1
4
4
  Summary: friendly font operations on top of fontTools.
5
5
  Author-email: Fabio Caccamo <fabio.caccamo@gmail.com>
6
6
  Maintainer-email: Fabio Caccamo <fabio.caccamo@gmail.com>
@@ -4,4 +4,4 @@ __description__ = "friendly font operations on top of fontTools."
4
4
  __email__ = "fabio.caccamo@gmail.com"
5
5
  __license__ = "MIT"
6
6
  __title__ = "python-fontbro"
7
- __version__ = "0.28.0"
7
+ __version__ = "0.28.1"
@@ -116,11 +116,20 @@ def get_name(
116
116
  Gets the name by its identifier from the name table of the given font.
117
117
  """
118
118
  name_id = _get_name_id(key)
119
- name_table = ttfont["name"]
120
- name = name_table.getName(name_id, **_NAMES_WIN_IDS)
121
- if not name:
122
- name = name_table.getName(name_id, **_NAMES_MAC_IDS)
123
- return str(name.toUnicode()) if name else None
119
+ name_table = ttfont.get("name")
120
+ if not name_table:
121
+ return None
122
+ # the windows english record is preferred: mac records are legacy
123
+ # and they can follow different naming conventions (eg. name id 1)
124
+ name_record = name_table.getName(name_id, **_NAMES_WIN_IDS)
125
+ if name_record:
126
+ try:
127
+ return str(name_record.toUnicode())
128
+ except UnicodeDecodeError:
129
+ pass
130
+ # fallback to the english (mac) record or to any other decodable record
131
+ name: str | None = name_table.getDebugName(name_id)
132
+ return name
124
133
 
125
134
 
126
135
  def get_names(
@@ -129,12 +138,12 @@ def get_names(
129
138
  """
130
139
  Gets the names records of the given font mapped by their property name.
131
140
  """
132
- names_by_id = {record.nameID: f"{record}" for record in ttfont["name"].names}
133
- names = {
134
- _NAMES_BY_ID[name_id]["key"]: value
135
- for name_id, value in names_by_id.items()
136
- if name_id in _NAMES_BY_ID
137
- }
141
+ # read each name as get_name does, to get consistent values
142
+ names = {}
143
+ for name_id, name_item in _NAMES_BY_ID.items():
144
+ name = get_name(ttfont, name_id)
145
+ if name is not None:
146
+ names[name_item["key"]] = name
138
147
  return names
139
148
 
140
149
 
@@ -149,10 +158,17 @@ def set_name(
149
158
  name_id = _get_name_id(key)
150
159
  name_table = ttfont["name"]
151
160
  # https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/ttLib/tables/_n_a_m_e.py#L568
161
+ # the windows record is always written, it's the one read first by get_name
152
162
  name_table.setName(value, name_id, **_NAMES_WIN_IDS)
163
+ # mac records are legacy (modern tools don't produce them anymore),
164
+ # so they are updated only if the font already has some of them
165
+ mac_platform_id = _NAMES_MAC_IDS["platformID"]
166
+ if not any(record.platformID == mac_platform_id for record in name_table.names):
167
+ return
153
168
  # the mac roman encoding can't encode many characters (eg. greek, cyrillic, cjk),
154
169
  # in that case the mac name record is removed (as fontTools addMultilingualName
155
- # does), otherwise the font would raise UnicodeEncodeError when saved
170
+ # does), otherwise the font would raise UnicodeEncodeError when saved;
171
+ # removing it is safe because the windows record has just been written
156
172
  if _is_encodable(value, _NAMES_MAC_IDS):
157
173
  name_table.setName(value, name_id, **_NAMES_MAC_IDS)
158
174
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fontbro
3
- Version: 0.28.0
3
+ Version: 0.28.1
4
4
  Summary: friendly font operations on top of fontTools.
5
5
  Author-email: Fabio Caccamo <fabio.caccamo@gmail.com>
6
6
  Maintainer-email: Fabio Caccamo <fabio.caccamo@gmail.com>
@@ -85,9 +85,25 @@ class NamesTestCase(AbstractTestCase):
85
85
  font_saved = Font(font.save_to_fileobject())
86
86
  self.assertEqual(font_saved.get_name(Font.NAME_FAMILY_NAME), value)
87
87
 
88
+ def _add_mac_name_record(self, font):
89
+ # the fixtures fonts have only windows name records
90
+ name_table = font.get_ttfont()["name"]
91
+ name_table.setName("Roboto Mono", 1, 1, 0, 0)
92
+
93
+ def test_set_name_without_mac_records(self):
94
+ # mac records are legacy (modern tools don't produce them anymore),
95
+ # so they are not added to fonts having only windows records
96
+ font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
97
+ font.set_name(Font.NAME_FAMILY_NAME, "Café Sans")
98
+ self.assertEqual(self._get_name_records_by_platform(font, 1), {3: "Café Sans"})
99
+ platforms = {record.platformID for record in font.get_ttfont()["name"].names}
100
+ self.assertEqual(platforms, {3})
101
+
88
102
  def test_set_name_encodable_in_mac_roman(self):
89
103
  # values encodable in mac roman are written in both windows and mac records
104
+ # if the font already has mac records
90
105
  font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
106
+ self._add_mac_name_record(font)
91
107
  font.set_name(Font.NAME_FAMILY_NAME, "Café Sans")
92
108
  self.assertEqual(
93
109
  self._get_name_records_by_platform(font, 1),
@@ -95,11 +111,17 @@ class NamesTestCase(AbstractTestCase):
95
111
  )
96
112
 
97
113
  def test_set_name_not_encodable_in_mac_roman_removes_mac_record(self):
98
- # an existing mac name record is removed to not keep an outdated value
114
+ # an existing mac name record is removed to not keep an outdated value,
115
+ # the name is still readable from the windows record
99
116
  font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
100
- font.set_name(Font.NAME_FAMILY_NAME, "Café Sans")
117
+ self._add_mac_name_record(font)
118
+ self.assertEqual(
119
+ self._get_name_records_by_platform(font, 1),
120
+ {1: "Roboto Mono", 3: "Roboto Mono"},
121
+ )
101
122
  font.set_name(Font.NAME_FAMILY_NAME, "Шрифт")
102
123
  self.assertEqual(self._get_name_records_by_platform(font, 1), {3: "Шрифт"})
124
+ self.assertEqual(font.get_name(Font.NAME_FAMILY_NAME), "Шрифт")
103
125
 
104
126
  def test_rename_not_encodable_in_mac_roman(self):
105
127
  # regression: renaming with non-latin names must not prevent saving
@@ -126,3 +148,59 @@ class NamesTestCase(AbstractTestCase):
126
148
  self.assertEqual(family_name, "Roboto Mono Renamed")
127
149
  subfamily_name = font.get_name(Font.NAME_SUBFAMILY_NAME)
128
150
  self.assertEqual(subfamily_name, "Regular Renamed")
151
+
152
+ def test_get_names_consistent_with_get_name(self):
153
+ # regression: get_names returned the last name record found for each name id,
154
+ # (eg. the japanese family name), instead of the same value of get_name
155
+ font = self._get_font("/DotGothic16/DotGothic16-Regular.ttf")
156
+ font_names = font.get_names()
157
+ self.assertEqual(font_names["family_name"], "DotGothic16")
158
+ for key, value in font_names.items():
159
+ with self.subTest(key=key):
160
+ self.assertEqual(font.get_name(key), value)
161
+
162
+ def _replace_name_records(self, font, platform_id, plat_enc_id, lang_id):
163
+ # replace all the name records with records of the given platform/language
164
+ name_table = font.get_ttfont()["name"]
165
+ for record in list(name_table.names):
166
+ name_table.setName(
167
+ record.toUnicode(), record.nameID, platform_id, plat_enc_id, lang_id
168
+ )
169
+ for record in list(name_table.names):
170
+ if (record.platformID, record.platEncID, record.langID) != (
171
+ platform_id,
172
+ plat_enc_id,
173
+ lang_id,
174
+ ):
175
+ name_table.names.remove(record)
176
+
177
+ def test_get_name_without_windows_english_records(self):
178
+ # regression: names were not found without windows english name records
179
+ platforms_ids = {
180
+ "unicode": (0, 3, 0),
181
+ "windows japanese": (3, 1, 0x411),
182
+ "mac english": (1, 0, 0),
183
+ }
184
+ for label, (platform_id, plat_enc_id, lang_id) in platforms_ids.items():
185
+ with self.subTest(records=label):
186
+ font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
187
+ self._replace_name_records(font, platform_id, plat_enc_id, lang_id)
188
+ self.assertEqual(font.get_name(Font.NAME_FAMILY_NAME), "Roboto Mono")
189
+ self.assertEqual(font.get_family_name(), "Roboto Mono")
190
+ self.assertEqual(font.get_names()["family_name"], "Roboto Mono")
191
+
192
+ def test_get_name_with_undecodable_windows_record(self):
193
+ # an undecodable windows record is skipped, the other records are read
194
+ font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
195
+ self._add_mac_name_record(font)
196
+ name_table = font.get_ttfont()["name"]
197
+ # lone utf-16 surrogate
198
+ name_table.getName(1, 3, 1, 0x409).string = b"\xd8\x00"
199
+ self.assertEqual(font.get_name(Font.NAME_FAMILY_NAME), "Roboto Mono")
200
+
201
+ def test_get_name_without_name_table(self):
202
+ font = self._get_font("/Roboto_Mono/static/RobotoMono-Regular.ttf")
203
+ del font.get_ttfont()["name"]
204
+ self.assertIsNone(font.get_name(Font.NAME_FAMILY_NAME))
205
+ self.assertEqual(font.get_names(), {})
206
+ self.assertEqual(font.get_family_name(), "")