fontforge-variable-font 0.2.0__tar.gz → 0.3.0__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 (24) hide show
  1. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/PKG-INFO +24 -1
  2. fontforge_variable_font-0.2.0/fontforge_variable_font.egg-info/PKG-INFO → fontforge_variable_font-0.3.0/README.md +19 -17
  3. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/__main__.py +26 -4
  4. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/design_axes.py +2 -6
  5. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/export.py +16 -22
  6. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/instance.py +1 -5
  7. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/load.py +108 -16
  8. fontforge_variable_font-0.2.0/README.md → fontforge_variable_font-0.3.0/fontforge_variable_font.egg-info/PKG-INFO +40 -0
  9. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/setup.cfg +7 -1
  10. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/LICENSE +0 -0
  11. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/__init__.py +0 -0
  12. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/delete.py +0 -0
  13. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/language.py +0 -0
  14. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforgeVF/utils.py +0 -0
  15. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforge_variable_font.egg-info/SOURCES.txt +0 -0
  16. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforge_variable_font.egg-info/dependency_links.txt +0 -0
  17. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforge_variable_font.egg-info/entry_points.txt +0 -0
  18. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforge_variable_font.egg-info/requires.txt +0 -0
  19. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/fontforge_variable_font.egg-info/top_level.txt +0 -0
  20. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/pyproject.toml +0 -0
  21. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/test/test_design_axes.py +0 -0
  22. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/test/test_export.py +0 -0
  23. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/test/test_language.py +0 -0
  24. {fontforge_variable_font-0.2.0 → fontforge_variable_font-0.3.0}/test/test_utils.py +0 -0
@@ -1,10 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fontforge_variable_font
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A FontForge_plugin to create a variable font
5
5
  Home-page: https://github.com/MihailJP/fontforge-variable-font
6
6
  Author: MihailJP
7
7
  Author-email: mihailjp@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Plugins
11
+ Classifier: Intended Audience :: Developers
8
12
  Classifier: Programming Language :: Python :: 3
9
13
  Classifier: Operating System :: OS Independent
10
14
  Classifier: Topic :: Text Processing :: Fonts
@@ -330,6 +334,25 @@ Deletes VF data.
330
334
  > [!WARNING]
331
335
  > You will see **no** warning.
332
336
 
337
+ ### Hooks
338
+
339
+ This plugin installs new/open font hooks which does:
340
+
341
+ - sets font generation hooks to output VF if metadata exists
342
+ - If you export a TTF or a WOFF2 when VF metadata exists, you will be
343
+ asked if you intend a VF. In this case too, all the masters must be
344
+ opened beforehands, however unlike the dedicated menu, VF-specific
345
+ options or italic counterpart cannot be set.
346
+ - For technical reason, first the static font gets exported as usual,
347
+ then VF overwrites it. Failed attempt of exporting a VF leaves the
348
+ static font.
349
+ - loads VF-specific metadata if available
350
+ - If you load a variable font from the ordinary 'load' menu, you will be
351
+ asked if you will open additional instances and which one(s.)
352
+
353
+ > [!NOTE]
354
+ > These hooks work in interactive mode only.
355
+
333
356
  ### Script usage
334
357
 
335
358
  As a Python module, in addition to `fontforge` module, scripting to export
@@ -1,20 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: fontforge_variable_font
3
- Version: 0.2.0
4
- Summary: A FontForge_plugin to create a variable font
5
- Home-page: https://github.com/MihailJP/fontforge-variable-font
6
- Author: MihailJP
7
- Author-email: mihailjp@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Operating System :: OS Independent
10
- Classifier: Topic :: Text Processing :: Fonts
11
- Requires-Python: >=3.10
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: fonttools
15
- Requires-Dist: fontmake
16
- Dynamic: license-file
17
-
18
1
  Fontforge Variable Font Plugin
19
2
  ==============================
20
3
 
@@ -330,6 +313,25 @@ Deletes VF data.
330
313
  > [!WARNING]
331
314
  > You will see **no** warning.
332
315
 
316
+ ### Hooks
317
+
318
+ This plugin installs new/open font hooks which does:
319
+
320
+ - sets font generation hooks to output VF if metadata exists
321
+ - If you export a TTF or a WOFF2 when VF metadata exists, you will be
322
+ asked if you intend a VF. In this case too, all the masters must be
323
+ opened beforehands, however unlike the dedicated menu, VF-specific
324
+ options or italic counterpart cannot be set.
325
+ - For technical reason, first the static font gets exported as usual,
326
+ then VF overwrites it. Failed attempt of exporting a VF leaves the
327
+ static font.
328
+ - loads VF-specific metadata if available
329
+ - If you load a variable font from the ordinary 'load' menu, you will be
330
+ asked if you will open additional instances and which one(s.)
331
+
332
+ > [!NOTE]
333
+ > These hooks work in interactive mode only.
334
+
333
335
  ### Script usage
334
336
 
335
337
  As a Python module, in addition to `fontforge` module, scripting to export
@@ -6,12 +6,30 @@ from fontforgeVF import (
6
6
  load
7
7
  )
8
8
  import fontforge
9
+ from typing import Literal, Callable
10
+
11
+
12
+ def _addHook(
13
+ name: Literal['newFontHook', 'loadFontHook'],
14
+ hook: Callable[[fontforge.font], None]
15
+ ):
16
+ assert isinstance(fontforge.hooks, dict)
17
+ if name in fontforge.hooks:
18
+ currentHook = fontforge.hooks[name]
19
+
20
+ def chainHook(font: fontforge.font):
21
+ currentHook(font)
22
+ hook(font)
23
+
24
+ fontforge.hooks[name] = chainHook
25
+ else:
26
+ fontforge.hooks[name] = hook
9
27
 
10
28
 
11
29
  def fontforge_plugin_init(**kw):
12
30
  fontforge.registerMenuItem(
13
31
  callback=load.loadMenu,
14
- enable=load.loadEnable,
32
+ enable=None,
15
33
  context="Font",
16
34
  submenu=["_Variable Font", '_Open a variable font'],
17
35
  name="By named _instance...",
@@ -19,7 +37,7 @@ def fontforge_plugin_init(**kw):
19
37
  )
20
38
  fontforge.registerMenuItem(
21
39
  callback=load.loadMenu,
22
- enable=load.loadEnable,
40
+ enable=None,
23
41
  context="Font",
24
42
  submenu=["_Variable Font", '_Open a variable font'],
25
43
  name="By _parameter...",
@@ -34,14 +52,14 @@ def fontforge_plugin_init(**kw):
34
52
  )
35
53
  fontforge.registerMenuItem(
36
54
  callback=design_axes.designAxesMenu,
37
- enable=design_axes.designAxesEnable,
55
+ enable=None,
38
56
  context="Font",
39
57
  submenu="_Variable Font",
40
58
  name="Design _axes..."
41
59
  )
42
60
  fontforge.registerMenuItem(
43
61
  callback=instance.instanceMenu,
44
- enable=instance.instanceEnable,
62
+ enable=None,
45
63
  context="Font",
46
64
  submenu="_Variable Font",
47
65
  name="Named _instances..."
@@ -53,3 +71,7 @@ def fontforge_plugin_init(**kw):
53
71
  submenu="_Variable Font",
54
72
  name="_Delete VF info"
55
73
  )
74
+
75
+ if fontforge.hasUserInterface:
76
+ _addHook('loadFontHook', load.loadHook)
77
+ _addHook('newFontHook', load.newFontHook)
@@ -288,7 +288,7 @@ def _saveResult_labels(result, k, v):
288
288
  utils.setOrDeleteVFValue(font, valAddr + '.name',
289
289
  None if name == '' else name)
290
290
  utils.deleteVFValue(font, 'axes.' + k + '.labels.' + val + '.localNames')
291
- for i in list(map(lambda x: x.replace('lang', ''), filter(lambda x: x.startswith('lang'), result))):
291
+ for i in (x.replace('lang', '') for x in result if x.startswith('lang')):
292
292
  if result['lang' + i] and result[k + 'labels' + i]:
293
293
  for val, name in list(zip(_x := iter(result[k + 'labels' + i].split(',')), _x)):
294
294
  val = val.replace('.', ',') # escape decimal point
@@ -301,7 +301,7 @@ def _saveResult_localNames(result, k, v):
301
301
  font = fontforge.activeFont()
302
302
  utils.setOrDeleteVFValue(font, 'axes.' + k + '.name', result[k + 'name'])
303
303
  utils.deleteVFValue(font, 'axes.' + k + '.localNames')
304
- for i in list(map(lambda x: x.replace('lang', ''), filter(lambda x: x.startswith('lang'), result))):
304
+ for i in (x.replace('lang', '') for x in result if x.startswith('lang')):
305
305
  if result['lang' + i]:
306
306
  utils.setOrDeleteVFValue(
307
307
  font, 'axes.' + k + '.localNames.' + result['lang' + i],
@@ -424,7 +424,3 @@ def designAxesMenu(u, glyph):
424
424
  result = fontforge.askMulti("Design axes", _prepareQuestions())
425
425
  if result:
426
426
  _saveResult(result)
427
-
428
-
429
- def designAxesEnable(u, glyph):
430
- return True
@@ -20,33 +20,29 @@ __all__ = [
20
20
 
21
21
 
22
22
  def _getSourceFonts(defaultFont: fontforge.font, filterItalicRoman: bool | None = None) -> list[fontforge.font]:
23
- tmpIter = filter(lambda f: f.familyname == defaultFont.familyname, fontforge.fonts())
23
+ tmpIter = (font for font in fontforge.fonts() if font.familyname == defaultFont.familyname)
24
24
  if filterItalicRoman is None:
25
25
  return list(tmpIter)
26
26
  else:
27
- return list(filter(lambda f: getAxisValue(f, 'ital') == filterItalicRoman, tmpIter))
27
+ return [font for font in tmpIter if getAxisValue(font, 'ital') == filterItalicRoman]
28
28
 
29
29
 
30
30
  def _axisMinValue(defaultFont: fontforge.font, tag: str, filterItalicRoman: bool | None = None) -> int | float:
31
31
  return min(
32
- filter(
33
- lambda x: x is not None,
34
- map(
35
- lambda f: getAxisValue(f, tag),
36
- _getSourceFonts(defaultFont, filterItalicRoman)
37
- )
32
+ (
33
+ getAxisValue(f, tag) for f
34
+ in _getSourceFonts(defaultFont, filterItalicRoman)
35
+ if getAxisValue(f, tag) is not None
38
36
  )
39
37
  )
40
38
 
41
39
 
42
40
  def _axisMaxValue(defaultFont: fontforge.font, tag: str, filterItalicRoman: bool | None = None) -> int | float:
43
41
  return max(
44
- filter(
45
- lambda x: x is not None,
46
- map(
47
- lambda f: getAxisValue(f, tag),
48
- _getSourceFonts(defaultFont, filterItalicRoman)
49
- )
42
+ (
43
+ getAxisValue(f, tag) for f
44
+ in _getSourceFonts(defaultFont, filterItalicRoman)
45
+ if getAxisValue(f, tag) is not None
50
46
  )
51
47
  )
52
48
 
@@ -86,12 +82,7 @@ def _getFontSubFamilyName(font: fontforge.font, lang: str = 'English (US)') -> s
86
82
  def _isFixedPitch(font: fontforge.font) -> bool:
87
83
  return len(
88
84
  set(
89
- map(
90
- lambda x: x.width,
91
- filter(
92
- lambda x: 0x20 <= x.unicode <= 0x7e, font.glyphs()
93
- )
94
- )
85
+ (glyph.width for glyph in font.glyphs() if 0x20 <= glyph.unicode <= 0x7e)
95
86
  )
96
87
  ) == 1
97
88
 
@@ -336,7 +327,7 @@ def _fixTtf_axes(font: fontforge.font, ttf: ttLib.TTFont):
336
327
  if utils.getVFValue(font, 'axes.' + k + '.active', False):
337
328
  localNames = utils.getVFValue(font, 'axes.' + k + '.localNames', {})
338
329
  for lang, name in localNames.items():
339
- axis = list(filter(lambda x: x.axisTag == tag, ttf["fvar"].axes))
330
+ axis = [a for a in ttf["fvar"].axes if a.axisTag == tag]
340
331
  if axis:
341
332
  ttf['name'].setName(name, axis[0].axisNameID, 3, 1, lang)
342
333
 
@@ -426,10 +417,13 @@ def _exportVF(
426
417
  _doExportVF(font, tmpdir, italicFilename, tmpdir + '/vf2.designspace', options)
427
418
  except CalledProcessError as e:
428
419
  if fontforge.hasUserInterface():
420
+ cmd = e.cmd
421
+ if isinstance(cmd, str):
422
+ cmd = e.cmd.split(' ')
429
423
  fontforge.logWarning(e.stderr)
430
424
  fontforge.postError(
431
425
  "Failed to export",
432
- "'{0}' failed with return code {1}".format(e.cmd.split(' ')[0], e.returncode)
426
+ "'{0}' failed with return code {1}".format(cmd[0], e.returncode)
433
427
  )
434
428
  else:
435
429
  raise
@@ -129,7 +129,7 @@ def _saveInstances(result: dict):
129
129
  else:
130
130
  instance[k] = utils.intOrFloat(result[k + '_' + str(cnt)])
131
131
  instance['localNames'] = {}
132
- for lang in filter(lambda x: x.startswith('lang') and x.find('name') == -1, result.keys()):
132
+ for lang in (x for x in result.keys() if x.startswith('lang') and x.find('name') == -1):
133
133
  if result[lang] and result[lang + 'name' + str(cnt)]:
134
134
  instance['localNames'][utils.intOrFloat(result[lang])] = result[lang + 'name' + str(cnt)]
135
135
  instanceList.append(instance)
@@ -169,7 +169,3 @@ def instanceMenu(u, glyph):
169
169
  result = fontforge.askMulti("Named instances", _prepareQuestions())
170
170
  if result:
171
171
  _saveInstances(result)
172
-
173
-
174
- def instanceEnable(u, glyph):
175
- return True
@@ -3,6 +3,7 @@ from fontforgeVF.utils import intOrFloat, checkExtensionTtfOrWoff2
3
3
  from os import PathLike
4
4
  from fontTools import ttLib
5
5
  import faulthandler
6
+ from typing import Literal, Callable
6
7
 
7
8
 
8
9
  __all__ = [
@@ -42,10 +43,7 @@ def _loadInstanceNames(varfont: ttLib.TTFont, partial: ttLib.TTFont, postscriptN
42
43
  def _addNames(ttf: ttLib.TTFont, data: dict, nameID: int):
43
44
  data['name'] = ''
44
45
  data['localNames'] = {}
45
- for name in filter(
46
- lambda x: x.nameID == nameID and x.platformID == 3 and x.platEncID == 1,
47
- ttf['name'].names
48
- ):
46
+ for name in (n for n in ttf['name'].names if n.nameID == nameID and n.platformID == 3 and n.platEncID == 1):
49
47
  if name.langID == 0x409:
50
48
  data['name'] = str(name)
51
49
  else:
@@ -133,7 +131,7 @@ def _getVFData_STAT(ttf: ttLib.TTFont, vfData: dict):
133
131
  def _getVFData_customTags(ttf: ttLib.TTFont, vfData: dict):
134
132
  from fontforgeVF.design_axes import designAxes
135
133
 
136
- customAxes = list(filter(lambda x: x not in designAxes.keys(), vfData['axes'].keys()))
134
+ customAxes = [axis for axis in vfData['axes'].keys() if axis not in designAxes.keys()]
137
135
  for i, a in enumerate(customAxes):
138
136
  if i < 3:
139
137
  vfData['axes']['custom' + str(i + 1)] = vfData['axes'][a]
@@ -186,7 +184,7 @@ def _doOpenVariableFont(
186
184
  from fontforgeVF.utils import initPersistentDict
187
185
  from pathlib import Path
188
186
 
189
- axes = [a.axisTag for a in filter(lambda x: x.minValue < x.maxValue, varfont["fvar"].axes)]
187
+ axes = [a.axisTag for a in varfont["fvar"].axes if a.minValue < a.maxValue]
190
188
  if extra := set(axisValues.keys()) - set(axes): # extra axes set
191
189
  fontforge.logWarning(', '.join(["'" + a + "'" for a in list(extra)]) + ' ignored')
192
190
  for tag in list(extra):
@@ -194,7 +192,7 @@ def _doOpenVariableFont(
194
192
  if unset := set(axes) - set(axisValues.keys()): # unset axes
195
193
  fontforge.logWarning(', '.join(["'" + a + "'" for a in list(unset)]) + ' not set (default value used)')
196
194
  for tag in list(unset):
197
- axisValues[tag] = [a.defaultValue for a in filter(lambda x: x.axisTag == tag, varfont["fvar"].axes)][0]
195
+ axisValues[tag] = [a.defaultValue for a in varfont["fvar"].axes if a.axisTag == tag][0]
198
196
  _checkAxisValue(varfont, axisValues) # out of range
199
197
  vfData = _getVFData(varfont, axisValues)
200
198
 
@@ -226,7 +224,7 @@ def _openVF(
226
224
  if 'fvar' not in ttf:
227
225
  return fontforge.open(filename)
228
226
  elif isinstance(axisValuesOrInstance, dict):
229
- if list(filter(lambda x: x.minValue != x.maxValue, ttf['fvar'].axes)):
227
+ if [axis for axis in ttf['fvar'].axes if axis.minValue != axis.maxValue]:
230
228
  return _doOpenVariableFont(filename, axisValuesOrInstance, ttf, tmpdir)
231
229
  else:
232
230
  return fontforge.open(filename)
@@ -240,10 +238,10 @@ def _openVF(
240
238
  elif isinstance(axisValuesOrInstance, str):
241
239
  return _doOpenVariableFont(
242
240
  filename,
243
- list(filter(
244
- lambda x: str(ttf['name'].getName(x.subfamilyNameID, 3, 1, 0x409)) == axisValuesOrInstance,
245
- ttf['fvar'].instances
246
- ))[0].coordinates,
241
+ [
242
+ i for i in ttf['fvar'].instances
243
+ if str(ttf['name'].getName(i.subfamilyNameID, 3, 1, 0x409)) == axisValuesOrInstance
244
+ ][0].coordinates,
247
245
  ttf,
248
246
  tmpdir
249
247
  )
@@ -328,7 +326,7 @@ def _chooseInstanceDialog(filename: str | PathLike, ttf: ttLib.TTFont, tmpdir: s
328
326
  [str(ttf['name'].getName(i.subfamilyNameID, 3, 1, 0x409)) for i in ttf['fvar'].instances],
329
327
  multiple=True
330
328
  )
331
- for i in [i[1] for i in list(filter(lambda x: x[0], zip(result, ttf['fvar'].instances)))]:
329
+ for i in (x[1] for x in zip(result, ttf['fvar'].instances) if x[0]):
332
330
  _doOpenVariableFont(filename, i.coordinates, ttf, tmpdir)
333
331
 
334
332
 
@@ -361,7 +359,7 @@ def loadMenu(u, glyph):
361
359
  if 'fvar' not in ttf:
362
360
  fontforge.logWarning(filename + " does not have 'fvar' table")
363
361
  fontforge.open(filename)
364
- elif list(filter(lambda x: x.minValue != x.maxValue, ttf['fvar'].axes)):
362
+ elif [axis for axis in ttf['fvar'].axes if axis.minValue != axis.maxValue]:
365
363
  _selectInstanceDialog(filename, ttf, u)
366
364
  else:
367
365
  fontforge.logWarning(filename + " has 'fvar' table but all axes are fixed")
@@ -369,5 +367,99 @@ def loadMenu(u, glyph):
369
367
  faulthandler.disable()
370
368
 
371
369
 
372
- def loadEnable(u, glyph):
373
- return True
370
+ def _generatePreHook(font: fontforge.font, target: str):
371
+ from fontforgeVF.utils import vfInfoExists
372
+
373
+ changed = font.changed
374
+ if vfInfoExists(font):
375
+ if target.endswith('.ttf') or target.endswith('.woff2'):
376
+ ans = fontforge.ask(
377
+ "Variable font",
378
+ "This font has variable font metadata in its persistent dict.\n"
379
+ "Did you intend to output a variable font?\n"
380
+ "(all masters need to be opened beforehand)",
381
+ ["_Yes", "_No"],
382
+ )
383
+ font.temporary['generateVF'] = (ans == 0)
384
+ font.changed = changed
385
+
386
+
387
+ def _generatePostHook(font: fontforge.font, target: str):
388
+ from fontforgeVF.utils import vfInfoExists
389
+ from fontforgeVF.export import exportVariableFont
390
+
391
+ changed = font.changed
392
+ if vfInfoExists(font):
393
+ if 'generateVF' in font.temporary:
394
+ if target.endswith('.ttf') or target.endswith('.woff2'):
395
+ if font.temporary['generateVF']:
396
+ exportVariableFont(font, target, None)
397
+ try:
398
+ del font.temporary['generateVF']
399
+ except KeyError:
400
+ pass
401
+ font.changed = changed
402
+
403
+
404
+ def _addHook(
405
+ font: fontforge.font,
406
+ name: Literal['generateFontPreHook', 'generateFontPostHook'],
407
+ hook: Callable[[fontforge.font, str], None]
408
+ ):
409
+ assert isinstance(font.temporary, dict)
410
+ if name in font.temporary:
411
+ currentHook = font.temporary[name]
412
+
413
+ def chainHook(font: fontforge.font, target: str):
414
+ currentHook(font, target)
415
+ hook(font, target)
416
+
417
+ font.temporary[name] = chainHook
418
+ else:
419
+ font.temporary[name] = hook
420
+
421
+
422
+ def _addGenerateHook(font: fontforge.font):
423
+ if not isinstance(font.temporary, dict):
424
+ font.temporary = {}
425
+ _addHook(font, 'generateFontPreHook', _generatePreHook)
426
+ _addHook(font, 'generateFontPostHook', _generatePostHook)
427
+
428
+
429
+ def _loadHook_ttf(font: fontforge.font):
430
+ from fontforgeVF.utils import initPersistentDict
431
+
432
+ with ttLib.TTFont(font.path) as ttf:
433
+ if 'fvar' in ttf and [axis for axis in ttf['fvar'].axes if axis.minValue != axis.maxValue]:
434
+ if ttf['fvar'].instances:
435
+ ans = fontforge.ask(
436
+ "Variable font",
437
+ "The font '" + font.familyname + "' in \n"
438
+ "'" + font.path + "'\n"
439
+ "seems to be a variable font.\n"
440
+ "Would you like to open another instance of this font?",
441
+ [
442
+ "_Yes",
443
+ "Open with _parameters",
444
+ "_No",
445
+ ]
446
+ )
447
+ if ans == 0 or ans == 1:
448
+ _selectInstanceDialog(font.path, ttf, ans)
449
+ else:
450
+ _selectInstanceDialog(font.path, ttf, 1)
451
+ initPersistentDict(font)
452
+ vfData = _getVFData(ttf, {})
453
+ font.persistent['VF'] = vfData
454
+
455
+
456
+ def loadHook(font: fontforge.font):
457
+ if font.path.endswith('.ttf') or font.path.endswith('.woff2'):
458
+ _loadHook_ttf(font)
459
+ _addGenerateHook(font)
460
+ elif font.path.endswith('.sfd'):
461
+ _addGenerateHook(font)
462
+
463
+
464
+ def newFontHook(font: fontforge.font):
465
+ _addGenerateHook(font)
@@ -1,3 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: fontforge_variable_font
3
+ Version: 0.3.0
4
+ Summary: A FontForge_plugin to create a variable font
5
+ Home-page: https://github.com/MihailJP/fontforge-variable-font
6
+ Author: MihailJP
7
+ Author-email: mihailjp@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Plugins
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Text Processing :: Fonts
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: fonttools
19
+ Requires-Dist: fontmake
20
+ Dynamic: license-file
21
+
1
22
  Fontforge Variable Font Plugin
2
23
  ==============================
3
24
 
@@ -313,6 +334,25 @@ Deletes VF data.
313
334
  > [!WARNING]
314
335
  > You will see **no** warning.
315
336
 
337
+ ### Hooks
338
+
339
+ This plugin installs new/open font hooks which does:
340
+
341
+ - sets font generation hooks to output VF if metadata exists
342
+ - If you export a TTF or a WOFF2 when VF metadata exists, you will be
343
+ asked if you intend a VF. In this case too, all the masters must be
344
+ opened beforehands, however unlike the dedicated menu, VF-specific
345
+ options or italic counterpart cannot be set.
346
+ - For technical reason, first the static font gets exported as usual,
347
+ then VF overwrites it. Failed attempt of exporting a VF leaves the
348
+ static font.
349
+ - loads VF-specific metadata if available
350
+ - If you load a variable font from the ordinary 'load' menu, you will be
351
+ asked if you will open additional instances and which one(s.)
352
+
353
+ > [!NOTE]
354
+ > These hooks work in interactive mode only.
355
+
316
356
  ### Script usage
317
357
 
318
358
  As a Python module, in addition to `fontforge` module, scripting to export
@@ -1,13 +1,19 @@
1
1
  [metadata]
2
2
  name = fontforge_variable_font
3
- version = 0.2.0
3
+ version = 0.3.0
4
4
  author = MihailJP
5
5
  author_email = mihailjp@gmail.com
6
6
  description = A FontForge_plugin to create a variable font
7
+ license = MIT
8
+ license_files =
9
+ LICENSE
7
10
  long_description = file: README.md
8
11
  long_description_content_type = text/markdown
9
12
  url = https://github.com/MihailJP/fontforge-variable-font
10
13
  classifiers =
14
+ Development Status :: 4 - Beta
15
+ Environment :: Plugins
16
+ Intended Audience :: Developers
11
17
  Programming Language :: Python :: 3
12
18
  Operating System :: OS Independent
13
19
  Topic :: Text Processing :: Fonts