fontforge-variable-font 0.3.0__tar.gz → 0.4.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.
- {fontforge_variable_font-0.3.0/fontforge_variable_font.egg-info → fontforge_variable_font-0.4.1}/PKG-INFO +2 -1
- fontforge_variable_font-0.4.1/fontforgeVF/__init__.py +50 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/__main__.py +12 -21
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/delete.py +6 -9
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/design_axes.py +21 -15
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/export.py +49 -40
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/instance.py +15 -7
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/language.py +1 -8
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/load.py +23 -39
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforgeVF/utils.py +62 -16
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1/fontforge_variable_font.egg-info}/PKG-INFO +2 -1
- fontforge_variable_font-0.4.1/fontforge_variable_font.egg-info/requires.txt +3 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/setup.cfg +2 -1
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/test/test_export.py +2 -1
- fontforge_variable_font-0.3.0/fontforgeVF/__init__.py +0 -6
- fontforge_variable_font-0.3.0/fontforge_variable_font.egg-info/requires.txt +0 -2
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/LICENSE +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/README.md +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforge_variable_font.egg-info/SOURCES.txt +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforge_variable_font.egg-info/dependency_links.txt +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforge_variable_font.egg-info/entry_points.txt +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/fontforge_variable_font.egg-info/top_level.txt +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/pyproject.toml +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/test/test_design_axes.py +1 -1
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/test/test_language.py +0 -0
- {fontforge_variable_font-0.3.0 → fontforge_variable_font-0.4.1}/test/test_utils.py +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fontforge_variable_font
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
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
|
|
@@ -17,6 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: fonttools
|
|
19
19
|
Requires-Dist: fontmake
|
|
20
|
+
Requires-Dist: fontforge_plugin_helper
|
|
20
21
|
Dynamic: license-file
|
|
21
22
|
|
|
22
23
|
Fontforge Variable Font Plugin
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""FontForge plugin to create a variable font"""
|
|
2
|
+
|
|
3
|
+
from .delete import deleteVFInfo
|
|
4
|
+
from .design_axes import getAxisValue
|
|
5
|
+
from .export import exportVariableFont
|
|
6
|
+
from .language import languageCodeIterator, languageCodeLookup, languageCodeReverseLookup, getLanguageList
|
|
7
|
+
from .load import openVariableFont
|
|
8
|
+
from .utils import (
|
|
9
|
+
intOrFloat,
|
|
10
|
+
initPersistentDict,
|
|
11
|
+
vfInfoExists,
|
|
12
|
+
getVFValue,
|
|
13
|
+
setVFValue,
|
|
14
|
+
deleteEmptyDicts,
|
|
15
|
+
deleteVFValue,
|
|
16
|
+
setOrDeleteVFValue,
|
|
17
|
+
checkExtensionTtfOrWoff2,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
# delete
|
|
23
|
+
"deleteVFInfo",
|
|
24
|
+
|
|
25
|
+
# design_axes
|
|
26
|
+
"getAxisValue",
|
|
27
|
+
|
|
28
|
+
# export
|
|
29
|
+
"exportVariableFont",
|
|
30
|
+
|
|
31
|
+
# language
|
|
32
|
+
'languageCodeIterator',
|
|
33
|
+
'languageCodeLookup',
|
|
34
|
+
'languageCodeReverseLookup',
|
|
35
|
+
'getLanguageList',
|
|
36
|
+
|
|
37
|
+
# load
|
|
38
|
+
"openVariableFont",
|
|
39
|
+
|
|
40
|
+
# utils
|
|
41
|
+
"intOrFloat",
|
|
42
|
+
"initPersistentDict",
|
|
43
|
+
"vfInfoExists",
|
|
44
|
+
"getVFValue",
|
|
45
|
+
"setVFValue",
|
|
46
|
+
"deleteEmptyDicts",
|
|
47
|
+
"deleteVFValue",
|
|
48
|
+
"setOrDeleteVFValue",
|
|
49
|
+
"checkExtensionTtfOrWoff2",
|
|
50
|
+
]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fontforge
|
|
2
|
+
|
|
1
3
|
from fontforgeVF import (
|
|
2
4
|
delete,
|
|
3
5
|
design_axes,
|
|
@@ -5,25 +7,7 @@ from fontforgeVF import (
|
|
|
5
7
|
instance,
|
|
6
8
|
load
|
|
7
9
|
)
|
|
8
|
-
import
|
|
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
|
|
10
|
+
from fontforge_plugin_helper import addSystemHook
|
|
27
11
|
|
|
28
12
|
|
|
29
13
|
def fontforge_plugin_init(**kw):
|
|
@@ -50,6 +34,13 @@ def fontforge_plugin_init(**kw):
|
|
|
50
34
|
submenu="_Variable Font",
|
|
51
35
|
name="_Generate a variable font..."
|
|
52
36
|
)
|
|
37
|
+
|
|
38
|
+
fontforge.registerMenuItem(
|
|
39
|
+
divider=True,
|
|
40
|
+
context="Font",
|
|
41
|
+
submenu="_Variable Font",
|
|
42
|
+
)
|
|
43
|
+
|
|
53
44
|
fontforge.registerMenuItem(
|
|
54
45
|
callback=design_axes.designAxesMenu,
|
|
55
46
|
enable=None,
|
|
@@ -73,5 +64,5 @@ def fontforge_plugin_init(**kw):
|
|
|
73
64
|
)
|
|
74
65
|
|
|
75
66
|
if fontforge.hasUserInterface:
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
addSystemHook('loadFontHook', load.loadHook)
|
|
68
|
+
addSystemHook('newFontHook', load.newFontHook)
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
from fontforgeVF import utils
|
|
2
1
|
import fontforge
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
__all__ = [
|
|
6
|
-
"deleteVFInfo",
|
|
7
|
-
]
|
|
3
|
+
from . import utils
|
|
8
4
|
|
|
9
5
|
|
|
10
6
|
def deleteVFInfo(font: fontforge.font) -> bool:
|
|
@@ -17,6 +13,7 @@ def deleteVFInfo(font: fontforge.font) -> bool:
|
|
|
17
13
|
:return: ``True`` if the key was deleted, ``False`` otherwise.
|
|
18
14
|
"""
|
|
19
15
|
if utils.vfInfoExists(font):
|
|
16
|
+
assert isinstance(font.persistent, dict)
|
|
20
17
|
del font.persistent['VF']
|
|
21
18
|
if len(font.persistent) == 0:
|
|
22
19
|
font.persistent = None
|
|
@@ -25,13 +22,13 @@ def deleteVFInfo(font: fontforge.font) -> bool:
|
|
|
25
22
|
return False
|
|
26
23
|
|
|
27
24
|
|
|
28
|
-
def deleteVFInfoMenu(u,
|
|
25
|
+
def deleteVFInfoMenu(u, font: fontforge.font):
|
|
29
26
|
"""Menu emtry to delete VF info from UI
|
|
30
27
|
|
|
31
28
|
This menu is enabled if active font has VF info.
|
|
32
29
|
"""
|
|
33
|
-
deleteVFInfo(
|
|
30
|
+
deleteVFInfo(font)
|
|
34
31
|
|
|
35
32
|
|
|
36
|
-
def deleteVFInfoEnable(u,
|
|
37
|
-
return utils.vfInfoExists(
|
|
33
|
+
def deleteVFInfoEnable(u, font: fontforge.font):
|
|
34
|
+
return utils.vfInfoExists(font)
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
from fontforgeVF import utils
|
|
2
1
|
import fontforge
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
__all__ = [
|
|
6
|
-
"getAxisValue",
|
|
7
|
-
]
|
|
3
|
+
from . import utils
|
|
8
4
|
|
|
9
5
|
|
|
10
6
|
def _getWidthFromOS2Width(font: fontforge.font) -> int | float:
|
|
@@ -28,7 +24,7 @@ def _searchCustomAxis(font: fontforge.font, tag: str) -> str | None:
|
|
|
28
24
|
return tag
|
|
29
25
|
else:
|
|
30
26
|
for k, v in designAxes.items():
|
|
31
|
-
if k.startswith('custom') and utils.getVFValue(font, 'axes.' + k + '.tag', '').rstrip() == tag.rstrip():
|
|
27
|
+
if k.startswith('custom') and str(utils.getVFValue(font, 'axes.' + k + '.tag', '')).rstrip() == tag.rstrip():
|
|
32
28
|
return k
|
|
33
29
|
return None
|
|
34
30
|
|
|
@@ -60,21 +56,21 @@ def _getFlags(font: fontforge.font, labelAddr: str) -> int:
|
|
|
60
56
|
|
|
61
57
|
def _loadLabels(tag: str, lang=None):
|
|
62
58
|
font = fontforge.activeFont()
|
|
59
|
+
assert font is not None
|
|
63
60
|
addr = 'axes.' + tag + '.labels'
|
|
64
|
-
if label := utils.
|
|
61
|
+
if label := utils.getVFValueAsDict(font, addr):
|
|
65
62
|
text = ''
|
|
66
|
-
assert isinstance(label, dict)
|
|
67
63
|
for k, v in label.items():
|
|
68
64
|
labelAddr = addr + '.' + str(k).replace('.', ',') # escape decimal point
|
|
69
65
|
if lang:
|
|
70
66
|
text += str(k) + ',' + \
|
|
71
|
-
utils.getVFValue(font, labelAddr + '.localNames.' + hex(lang), '') + \
|
|
67
|
+
str(utils.getVFValue(font, labelAddr + '.localNames.' + hex(lang), '')) + \
|
|
72
68
|
', '
|
|
73
69
|
else:
|
|
74
70
|
text += str(k) + ',' + \
|
|
75
71
|
str(_getFlags(font, labelAddr)) + \
|
|
76
72
|
',' + str(utils.getVFValue(font, labelAddr + '.linkedValue', '')) + \
|
|
77
|
-
',' + utils.getVFValue(font, labelAddr + '.name', '') + \
|
|
73
|
+
',' + str(utils.getVFValue(font, labelAddr + '.name', '')) + \
|
|
78
74
|
', '
|
|
79
75
|
return text[:-2]
|
|
80
76
|
elif utils.vfInfoExists(font):
|
|
@@ -105,9 +101,10 @@ def _loadLabels(tag: str, lang=None):
|
|
|
105
101
|
def _prepareQuestions_languages(questions):
|
|
106
102
|
from fontforgeVF.language import getLanguageList
|
|
107
103
|
font = fontforge.activeFont()
|
|
104
|
+
assert font is not None
|
|
108
105
|
languages = set()
|
|
109
106
|
for k, v in designAxes.items():
|
|
110
|
-
languages |= set(utils.
|
|
107
|
+
languages |= set(utils.getVFValueAsDict(font, 'axes.' + k + '.localNames').keys())
|
|
111
108
|
languages = tuple(languages)
|
|
112
109
|
localNameCategory = len(questions)
|
|
113
110
|
localNameRange = range(1, max(((len(languages) + 7) // 4) * 4, 8)+1)
|
|
@@ -120,6 +117,7 @@ def _prepareQuestions_languages(questions):
|
|
|
120
117
|
|
|
121
118
|
def _prepareQuestions_values(questions, k, v):
|
|
122
119
|
font = fontforge.activeFont()
|
|
120
|
+
assert font is not None
|
|
123
121
|
tagStat = 1 if utils.getVFValue(font, 'axes.' + k + '.active', False) else 0
|
|
124
122
|
if tagStat == 1 and not k.startswith('custom') and utils.getVFValue(font, 'axes.' + k + '.useDefault', False):
|
|
125
123
|
tagStat = 2
|
|
@@ -159,19 +157,21 @@ def _prepareQuestions_values(questions, k, v):
|
|
|
159
157
|
|
|
160
158
|
def _prepareQuestions_map(questions, k, v):
|
|
161
159
|
font = fontforge.activeFont()
|
|
160
|
+
assert font is not None
|
|
162
161
|
questions[2]["questions"].append({
|
|
163
162
|
'type': 'string',
|
|
164
163
|
'question': v["name"] + ':',
|
|
165
164
|
'tag': k + 'map',
|
|
166
165
|
'default': ', '.join(list(map(
|
|
167
166
|
lambda x: str(x[0]) + ',' + str(x[1]),
|
|
168
|
-
utils.
|
|
167
|
+
utils.getVFValueAsList(font, 'axes.' + k + '.map')
|
|
169
168
|
))),
|
|
170
169
|
})
|
|
171
170
|
|
|
172
171
|
|
|
173
172
|
def _prepareQuestions_order(questions, k, v):
|
|
174
173
|
font = fontforge.activeFont()
|
|
174
|
+
assert font is not None
|
|
175
175
|
questions[3]["questions"].append({
|
|
176
176
|
'type': 'string',
|
|
177
177
|
'question': v["name"] + ':',
|
|
@@ -182,6 +182,7 @@ def _prepareQuestions_order(questions, k, v):
|
|
|
182
182
|
|
|
183
183
|
def _prepareQuestions_names(questions, k, v):
|
|
184
184
|
font = fontforge.activeFont()
|
|
185
|
+
assert font is not None
|
|
185
186
|
questions[4]["questions"].append({
|
|
186
187
|
'type': 'string',
|
|
187
188
|
'question': v["name"] + ':',
|
|
@@ -202,6 +203,7 @@ def _prepareQuestions_names(questions, k, v):
|
|
|
202
203
|
|
|
203
204
|
def _prepareQuestions_localNames(questions, k, v, languages, localNameRange, localNameCategory):
|
|
204
205
|
font = fontforge.activeFont()
|
|
206
|
+
assert font is not None
|
|
205
207
|
for i in localNameRange:
|
|
206
208
|
questions[localNameCategory + i - 1]["questions"].append({
|
|
207
209
|
'type': 'string',
|
|
@@ -221,6 +223,7 @@ def _prepareQuestions_localNames(questions, k, v, languages, localNameRange, loc
|
|
|
221
223
|
|
|
222
224
|
def _prepareQuestions_custom(questions, k, v):
|
|
223
225
|
font = fontforge.activeFont()
|
|
226
|
+
assert font is not None
|
|
224
227
|
if k.startswith('custom'):
|
|
225
228
|
questions[1]["questions"].append({
|
|
226
229
|
'type': 'string',
|
|
@@ -253,6 +256,7 @@ def _prepareQuestions():
|
|
|
253
256
|
# Save result
|
|
254
257
|
def _saveResult_values(result, k, v):
|
|
255
258
|
font = fontforge.activeFont()
|
|
259
|
+
assert font is not None
|
|
256
260
|
utils.setVFValue(font, 'axes.' + k + '.active', result[k] != 'unset')
|
|
257
261
|
if not k.startswith('custom') and result[k] != 'unset':
|
|
258
262
|
utils.setVFValue(font, 'axes.' + k + '.useDefault', result[k] == 'auto')
|
|
@@ -270,6 +274,7 @@ def _saveResult_values(result, k, v):
|
|
|
270
274
|
|
|
271
275
|
def _saveResult_labels(result, k, v):
|
|
272
276
|
font = fontforge.activeFont()
|
|
277
|
+
assert font is not None
|
|
273
278
|
utils.setOrDeleteVFValue(font, 'axes.' + k + '.order', int(result[k + 'order']) if result[k + 'order'] else None)
|
|
274
279
|
utils.deleteVFValue(font, 'axes.' + k + '.labels')
|
|
275
280
|
if result[k + 'labels']:
|
|
@@ -280,9 +285,9 @@ def _saveResult_labels(result, k, v):
|
|
|
280
285
|
val = val.replace('.', ',') # escape decimal point
|
|
281
286
|
valAddr = 'axes.' + k + '.labels.' + val
|
|
282
287
|
utils.setOrDeleteVFValue(font, valAddr + '.olderSibling',
|
|
283
|
-
None if el == '' else bool(
|
|
288
|
+
None if el == '' else bool(int(el) & 1))
|
|
284
289
|
utils.setOrDeleteVFValue(font, valAddr + '.elidable',
|
|
285
|
-
None if el == '' else bool(
|
|
290
|
+
None if el == '' else bool(int(el) & 2))
|
|
286
291
|
utils.setOrDeleteVFValue(font, valAddr + '.linkedValue',
|
|
287
292
|
None if lv == '' else utils.intOrFloat(lv))
|
|
288
293
|
utils.setOrDeleteVFValue(font, valAddr + '.name',
|
|
@@ -299,6 +304,7 @@ def _saveResult_labels(result, k, v):
|
|
|
299
304
|
|
|
300
305
|
def _saveResult_localNames(result, k, v):
|
|
301
306
|
font = fontforge.activeFont()
|
|
307
|
+
assert font is not None
|
|
302
308
|
utils.setOrDeleteVFValue(font, 'axes.' + k + '.name', result[k + 'name'])
|
|
303
309
|
utils.deleteVFValue(font, 'axes.' + k + '.localNames')
|
|
304
310
|
for i in (x.replace('lang', '') for x in result if x.startswith('lang')):
|
|
@@ -421,6 +427,6 @@ def designAxesMenu(u, glyph):
|
|
|
421
427
|
* Axis value
|
|
422
428
|
* Name
|
|
423
429
|
"""
|
|
424
|
-
result = fontforge.askMulti("Design axes", _prepareQuestions())
|
|
430
|
+
result = fontforge.askMulti("Design axes", _prepareQuestions()) # type: ignore
|
|
425
431
|
if result:
|
|
426
432
|
_saveResult(result)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from fontforgeVF import utils, language
|
|
2
|
-
from fontforgeVF.design_axes import designAxes, getAxisValue
|
|
3
|
-
import fontforge
|
|
4
|
-
import tempfile
|
|
5
1
|
from os import PathLike
|
|
2
|
+
import tempfile
|
|
3
|
+
|
|
4
|
+
import fontforge
|
|
5
|
+
from fontTools import ttLib
|
|
6
6
|
from fontTools.designspaceLib import (
|
|
7
7
|
DesignSpaceDocument,
|
|
8
8
|
SourceDescriptor,
|
|
@@ -11,12 +11,9 @@ from fontTools.designspaceLib import (
|
|
|
11
11
|
AxisLabelDescriptor,
|
|
12
12
|
InstanceDescriptor,
|
|
13
13
|
)
|
|
14
|
-
from fontTools import ttLib
|
|
15
|
-
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
]
|
|
15
|
+
from . import utils, language
|
|
16
|
+
from .design_axes import designAxes, getAxisValue
|
|
20
17
|
|
|
21
18
|
|
|
22
19
|
def _getSourceFonts(defaultFont: fontforge.font, filterItalicRoman: bool | None = None) -> list[fontforge.font]:
|
|
@@ -33,7 +30,7 @@ def _axisMinValue(defaultFont: fontforge.font, tag: str, filterItalicRoman: bool
|
|
|
33
30
|
getAxisValue(f, tag) for f
|
|
34
31
|
in _getSourceFonts(defaultFont, filterItalicRoman)
|
|
35
32
|
if getAxisValue(f, tag) is not None
|
|
36
|
-
)
|
|
33
|
+
) # type: ignore
|
|
37
34
|
)
|
|
38
35
|
|
|
39
36
|
|
|
@@ -43,7 +40,7 @@ def _axisMaxValue(defaultFont: fontforge.font, tag: str, filterItalicRoman: bool
|
|
|
43
40
|
getAxisValue(f, tag) for f
|
|
44
41
|
in _getSourceFonts(defaultFont, filterItalicRoman)
|
|
45
42
|
if getAxisValue(f, tag) is not None
|
|
46
|
-
)
|
|
43
|
+
) # type: ignore
|
|
47
44
|
)
|
|
48
45
|
|
|
49
46
|
|
|
@@ -133,7 +130,7 @@ def _outputUfo(font: fontforge.font, outputDir: str | PathLike, outputFile: str
|
|
|
133
130
|
import re
|
|
134
131
|
# import shutil # for debug
|
|
135
132
|
|
|
136
|
-
assert outputFile.endswith('.ufo')
|
|
133
|
+
assert str(outputFile).endswith('.ufo')
|
|
137
134
|
ufoPath = str(outputDir) + '/' + str(outputFile)
|
|
138
135
|
changed = font.changed
|
|
139
136
|
unlinkRmOvrlpSave = {}
|
|
@@ -151,10 +148,10 @@ def _outputUfo(font: fontforge.font, outputDir: str | PathLike, outputFile: str
|
|
|
151
148
|
info = _ufoInfo()
|
|
152
149
|
ufo.readInfo(info)
|
|
153
150
|
if _isFixedPitch(font):
|
|
154
|
-
ufo.postscriptIsFixedPitch = True
|
|
155
|
-
ufo.styleMapFamilyName = _getFontFamilyName(font)
|
|
156
|
-
ufo.styleMapStyleName = _getFontSubFamilyName(font)
|
|
157
|
-
ufo.writeInfo(info)
|
|
151
|
+
ufo.postscriptIsFixedPitch = True # type: ignore
|
|
152
|
+
ufo.styleMapFamilyName = _getFontFamilyName(font) # type: ignore
|
|
153
|
+
ufo.styleMapStyleName = _getFontSubFamilyName(font) # type: ignore
|
|
154
|
+
ufo.writeInfo(info) # type: ignore
|
|
158
155
|
|
|
159
156
|
if _aaltExists(font) or _allGSUBTags(font):
|
|
160
157
|
feat = ufo.readFeatures()
|
|
@@ -172,7 +169,7 @@ def _outputUfo(font: fontforge.font, outputDir: str | PathLike, outputFile: str
|
|
|
172
169
|
if aaltInclude or existingAalt:
|
|
173
170
|
newAalt = "feature aalt {\n" + aaltInclude + existingAalt + "} aalt;\n\n"
|
|
174
171
|
feat = re.sub(featPosPattern, newAalt, feat, count=1)
|
|
175
|
-
ufo.writeFeatures(feat)
|
|
172
|
+
ufo.writeFeatures(feat) # type: ignore
|
|
176
173
|
|
|
177
174
|
font.changed = changed
|
|
178
175
|
|
|
@@ -185,6 +182,7 @@ def _designSpaceSources(font: fontforge.font, doc: DesignSpaceDocument, filterIt
|
|
|
185
182
|
# print(_getSourceFonts(font))
|
|
186
183
|
for f in _getSourceFonts(font, filterItalicRoman):
|
|
187
184
|
s = SourceDescriptor()
|
|
185
|
+
assert isinstance(f.temporary, dict)
|
|
188
186
|
s.path = f.temporary['ufo']
|
|
189
187
|
if f is font:
|
|
190
188
|
s.copyLib = True
|
|
@@ -197,11 +195,15 @@ def _designSpaceSources(font: fontforge.font, doc: DesignSpaceDocument, filterIt
|
|
|
197
195
|
for k, v in designAxes.items():
|
|
198
196
|
active = 'axes.' + k + '.active'
|
|
199
197
|
if utils.getVFValue(font, active, False) and utils.getVFValue(f, active, False):
|
|
200
|
-
tag =
|
|
198
|
+
tag = str(
|
|
199
|
+
utils.getVFValue(font, 'axes.' + k + '.tag', '????')
|
|
201
200
|
if k.startswith('custom') else k
|
|
202
|
-
|
|
201
|
+
)
|
|
202
|
+
name = str(
|
|
203
|
+
utils.getVFValue(font, 'axes.' + k + '.name', v['name'])
|
|
203
204
|
if k.startswith('custom') else v['name']
|
|
204
|
-
|
|
205
|
+
)
|
|
206
|
+
s.location[name] = getAxisValue(f, tag) # type: ignore
|
|
205
207
|
s.familyName = _getFontFamilyName(f)
|
|
206
208
|
s.styleName = _getFontSubFamilyName(f)
|
|
207
209
|
doc.addSource(s)
|
|
@@ -210,7 +212,7 @@ def _designSpaceSources(font: fontforge.font, doc: DesignSpaceDocument, filterIt
|
|
|
210
212
|
def _designSpaceAxes_labels(labels, a: AxisDescriptor | DiscreteAxisDescriptor):
|
|
211
213
|
L = []
|
|
212
214
|
for u, d in labels.items():
|
|
213
|
-
if not (a.minimum <= u <= a.maximum):
|
|
215
|
+
if not (a.minimum <= u <= a.maximum): # type: ignore
|
|
214
216
|
fontforge.logWarning('Ignored label {0} = {1} because out of range'.format(
|
|
215
217
|
a.name, str(u)))
|
|
216
218
|
elif 'name' not in d:
|
|
@@ -232,15 +234,17 @@ def _designSpaceAxes(font: fontforge.font, doc: DesignSpaceDocument, filterItali
|
|
|
232
234
|
for k, v in designAxes.items():
|
|
233
235
|
if utils.getVFValue(font, 'axes.' + k + '.active', False):
|
|
234
236
|
a = DiscreteAxisDescriptor() if k == 'ital' else AxisDescriptor()
|
|
235
|
-
a.tag =
|
|
237
|
+
a.tag = str(
|
|
238
|
+
utils.getVFValue(font, 'axes.' + k + '.tag', '????')
|
|
236
239
|
if k.startswith('custom') else k
|
|
240
|
+
)
|
|
237
241
|
if k == 'ital' and filterItalicRoman is not None:
|
|
238
|
-
a.minimum = 1 if filterItalicRoman else _axisMinValue(font, a.tag)
|
|
239
|
-
a.maximum = 0 if not filterItalicRoman else _axisMaxValue(font, a.tag)
|
|
242
|
+
a.minimum = 1 if filterItalicRoman else _axisMinValue(font, a.tag) # type: ignore
|
|
243
|
+
a.maximum = 0 if not filterItalicRoman else _axisMaxValue(font, a.tag) # type: ignore
|
|
240
244
|
else:
|
|
241
|
-
a.minimum = _axisMinValue(font, a.tag)
|
|
242
|
-
a.maximum = _axisMaxValue(font, a.tag)
|
|
243
|
-
a.default = getAxisValue(font, a.tag)
|
|
245
|
+
a.minimum = _axisMinValue(font, a.tag) # type: ignore
|
|
246
|
+
a.maximum = _axisMaxValue(font, a.tag) # type: ignore
|
|
247
|
+
a.default = getAxisValue(font, a.tag) # type: ignore
|
|
244
248
|
a.name = utils.getVFValue(font, 'axes.' + k + '.name', v['name'])
|
|
245
249
|
if val := utils.getVFValue(font, 'axes.' + k + '.map'):
|
|
246
250
|
a.map = val
|
|
@@ -252,7 +256,8 @@ def _designSpaceAxes(font: fontforge.font, doc: DesignSpaceDocument, filterItali
|
|
|
252
256
|
|
|
253
257
|
|
|
254
258
|
def _designSpaceInstances(font: fontforge.font, doc: DesignSpaceDocument, filterItalicRoman: bool | None = None):
|
|
255
|
-
|
|
259
|
+
instances = utils.getVFValueAsList(font, 'instances')
|
|
260
|
+
for instance in instances:
|
|
256
261
|
i = InstanceDescriptor()
|
|
257
262
|
i.styleName = instance['name']
|
|
258
263
|
i.postScriptFontName = instance['psName']
|
|
@@ -325,7 +330,7 @@ def _fixTtf_axes(font: fontforge.font, ttf: ttLib.TTFont):
|
|
|
325
330
|
if k.startswith('custom') else k
|
|
326
331
|
)
|
|
327
332
|
if utils.getVFValue(font, 'axes.' + k + '.active', False):
|
|
328
|
-
localNames = utils.
|
|
333
|
+
localNames = utils.getVFValueAsDict(font, 'axes.' + k + '.localNames')
|
|
329
334
|
for lang, name in localNames.items():
|
|
330
335
|
axis = [a for a in ttf["fvar"].axes if a.axisTag == tag]
|
|
331
336
|
if axis:
|
|
@@ -337,9 +342,9 @@ def _fixTtf_labels(font: fontforge.font, ttf: ttLib.TTFont):
|
|
|
337
342
|
axisIndex = axisLabel.AxisIndex
|
|
338
343
|
tag = ttf['STAT'].table.DesignAxisRecord.Axis[axisIndex].AxisTag
|
|
339
344
|
value = axisLabel.Value
|
|
340
|
-
localNames = utils.
|
|
345
|
+
localNames = utils.getVFValueAsDict(
|
|
341
346
|
font, 'axes.' + tag + '.labels.' + str(int(value)) + '.localNames',
|
|
342
|
-
utils.
|
|
347
|
+
utils.getVFValueAsDict(
|
|
343
348
|
font, 'axes.' + tag + '.labels.' + str(float(value)).replace('.', ',') + '.localNames',
|
|
344
349
|
{}
|
|
345
350
|
)
|
|
@@ -351,7 +356,7 @@ def _fixTtf_labels(font: fontforge.font, ttf: ttLib.TTFont):
|
|
|
351
356
|
def _fixTtf_instances(font: fontforge.font, ttf: ttLib.TTFont):
|
|
352
357
|
for i, instance in enumerate(ttf['fvar'].instances):
|
|
353
358
|
subfamilyNameID = instance.subfamilyNameID
|
|
354
|
-
localNames = utils.
|
|
359
|
+
localNames = utils.getVFValueAsDict(
|
|
355
360
|
font, 'instances[' + str(i) + '].localNames', {}
|
|
356
361
|
)
|
|
357
362
|
for lang, name in localNames.items():
|
|
@@ -362,8 +367,12 @@ def _fixTtf(font: fontforge.font, filename: str | PathLike):
|
|
|
362
367
|
with ttLib.TTFont(str(filename)) as ttf:
|
|
363
368
|
for i in font.sfnt_names:
|
|
364
369
|
if i[0] != 'English (US)':
|
|
370
|
+
if isinstance(i[0], str): # likely
|
|
371
|
+
langCode = language.languageCodeReverseLookup(i[0])
|
|
372
|
+
else: # unlikely
|
|
373
|
+
langCode = i[0]
|
|
365
374
|
if i[1] in _fields:
|
|
366
|
-
ttf['name'].setName(i[2], _fields[i[1]], 3, 1,
|
|
375
|
+
ttf['name'].setName(i[2], _fields[i[1]], 3, 1, langCode)
|
|
367
376
|
_fixTtf_axes(font, ttf)
|
|
368
377
|
_fixTtf_labels(font, ttf)
|
|
369
378
|
_fixTtf_instances(font, ttf)
|
|
@@ -543,7 +552,7 @@ def _exportVariableFont(font: fontforge.font, dialogResult: dict[str, str]):
|
|
|
543
552
|
|
|
544
553
|
def _saveMenuDialog(font: fontforge.font) -> dict | None:
|
|
545
554
|
if _hasBothRomanAndItalic(font):
|
|
546
|
-
questions = [
|
|
555
|
+
questions: list = [
|
|
547
556
|
{
|
|
548
557
|
'type': 'savepath', 'question': '_Roman VF:', 'tag': 'file',
|
|
549
558
|
'default':
|
|
@@ -560,7 +569,7 @@ def _saveMenuDialog(font: fontforge.font) -> dict | None:
|
|
|
560
569
|
},
|
|
561
570
|
]
|
|
562
571
|
else:
|
|
563
|
-
questions = [
|
|
572
|
+
questions: list = [
|
|
564
573
|
{
|
|
565
574
|
'type': 'savepath', 'question': '_Save as:', 'tag': 'file',
|
|
566
575
|
'default':
|
|
@@ -583,10 +592,10 @@ def _saveMenuDialog(font: fontforge.font) -> dict | None:
|
|
|
583
592
|
return fontforge.askMulti("Save variable font", questions)
|
|
584
593
|
|
|
585
594
|
|
|
586
|
-
def saveMenu(u,
|
|
587
|
-
if result := _saveMenuDialog(
|
|
588
|
-
_exportVariableFont(
|
|
595
|
+
def saveMenu(u, font: fontforge.font):
|
|
596
|
+
if result := _saveMenuDialog(font):
|
|
597
|
+
_exportVariableFont(font, result)
|
|
589
598
|
|
|
590
599
|
|
|
591
|
-
def saveEnable(u,
|
|
592
|
-
return utils.vfInfoExists(
|
|
600
|
+
def saveEnable(u, font: fontforge.font):
|
|
601
|
+
return utils.vfInfoExists(font)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fontforge
|
|
2
|
-
|
|
3
|
-
from
|
|
2
|
+
|
|
3
|
+
from . import utils
|
|
4
|
+
from .design_axes import designAxes
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def _instances_getval(font: fontforge.font, cnt: int, key: str, defaultVal):
|
|
7
|
-
instances = utils.
|
|
8
|
+
instances = utils.getVFValueAsList(font, 'instances')
|
|
8
9
|
if (cnt - 1) < len(instances):
|
|
9
10
|
if isinstance(instances[cnt - 1], dict):
|
|
10
11
|
if key in instances[cnt - 1]:
|
|
@@ -14,6 +15,7 @@ def _instances_getval(font: fontforge.font, cnt: int, key: str, defaultVal):
|
|
|
14
15
|
|
|
15
16
|
def _prepareQuestions_instances(cnt: int):
|
|
16
17
|
font = fontforge.activeFont()
|
|
18
|
+
assert font is not None
|
|
17
19
|
questions = [
|
|
18
20
|
{
|
|
19
21
|
'type': 'string',
|
|
@@ -57,13 +59,16 @@ def _prepareQuestions_instanceLocalNames(questions: list):
|
|
|
57
59
|
from math import ceil
|
|
58
60
|
|
|
59
61
|
font = fontforge.activeFont()
|
|
60
|
-
|
|
62
|
+
assert font is not None
|
|
63
|
+
instances = utils.getVFValueAsList(font, 'instances')
|
|
64
|
+
numberOfInstances = max(((len(instances) + 7) // 4) * 4, 8)
|
|
61
65
|
|
|
62
66
|
languages = set()
|
|
63
67
|
numberOfLanguages = 8
|
|
64
|
-
for i in range(len(
|
|
68
|
+
for i in range(len(instances)):
|
|
65
69
|
for k, v in designAxes.items():
|
|
66
|
-
|
|
70
|
+
langdict = utils.getVFValueAsDict(font, 'instances[' + str(i) + '].localNames')
|
|
71
|
+
languages |= set(langdict.keys())
|
|
67
72
|
languages = tuple(languages)
|
|
68
73
|
numberOfLanguages = max(((len(languages) + 7) // 4) * 4, 8)
|
|
69
74
|
|
|
@@ -100,8 +105,10 @@ def _prepareQuestions_instanceLocalNames(questions: list):
|
|
|
100
105
|
|
|
101
106
|
def _prepareQuestions():
|
|
102
107
|
font = fontforge.activeFont()
|
|
108
|
+
assert font is not None
|
|
103
109
|
questions = []
|
|
104
|
-
|
|
110
|
+
instances = utils.getVFValueAsList(font, 'instances')
|
|
111
|
+
numberOfInstances = max(((len(instances) + 7) // 4) * 4, 8)
|
|
105
112
|
|
|
106
113
|
for i in range(numberOfInstances):
|
|
107
114
|
questions.append({
|
|
@@ -115,6 +122,7 @@ def _prepareQuestions():
|
|
|
115
122
|
|
|
116
123
|
def _saveInstances(result: dict):
|
|
117
124
|
font = fontforge.activeFont()
|
|
125
|
+
assert font is not None
|
|
118
126
|
instanceList = []
|
|
119
127
|
cnt = 1
|
|
120
128
|
while 'psName' + str(cnt) in result:
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
__all__ = [
|
|
2
|
-
'languageCodeIterator',
|
|
3
|
-
'languageCodeLookup',
|
|
4
|
-
'languageCodeReverseLookup',
|
|
5
|
-
'getLanguageList',
|
|
6
|
-
]
|
|
7
|
-
|
|
8
1
|
languageData = {
|
|
9
2
|
1: {
|
|
10
3
|
'name': 'Arabic',
|
|
@@ -832,7 +825,7 @@ def getLanguageList(listNumber: int, defaultCode: int | None = None):
|
|
|
832
825
|
for langId, langCode, langName in sorted(languageCodeIterator(), key=lambda x: x[2]):
|
|
833
826
|
languageList.append({'name': langName, 'tag': hex(langId), 'default': langId == defaultCode})
|
|
834
827
|
questions = {
|
|
835
|
-
'category': 'Localized names ' + (
|
|
828
|
+
'category': 'Localized names ' + str(
|
|
836
829
|
languageCodeLookup(defaultCode) if defaultCode else str(listNumber)
|
|
837
830
|
),
|
|
838
831
|
'questions': [
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import fontforge
|
|
2
|
-
from fontforgeVF.utils import intOrFloat, checkExtensionTtfOrWoff2
|
|
3
|
-
from os import PathLike
|
|
4
|
-
from fontTools import ttLib
|
|
5
1
|
import faulthandler
|
|
6
|
-
from
|
|
2
|
+
from os import PathLike
|
|
7
3
|
|
|
4
|
+
import fontforge
|
|
5
|
+
from fontTools import ttLib
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
]
|
|
7
|
+
from .utils import intOrFloat, checkExtensionTtfOrWoff2, ensureTuple
|
|
8
|
+
from fontforge_plugin_helper import addFontGenerateHook
|
|
12
9
|
|
|
13
10
|
|
|
14
11
|
def _checkAxisValue(ttf: ttLib.TTFont, axisValues: dict[str, int | float]):
|
|
@@ -64,7 +61,7 @@ def _denormalize(minimum, default, maximum, value):
|
|
|
64
61
|
def _getVFData_fvar_axes(ttf: ttLib.TTFont, axisValues: dict[str, int | float], vfData: dict):
|
|
65
62
|
for axis in ttf['fvar'].axes:
|
|
66
63
|
tag = axis.axisTag
|
|
67
|
-
axisData = {'active': True}
|
|
64
|
+
axisData: dict[str, str | int | float] = {'active': True}
|
|
68
65
|
if tag in axisValues:
|
|
69
66
|
axisData['useDefault'] = False
|
|
70
67
|
axisData['value'] = intOrFloat(axisValues[tag])
|
|
@@ -211,6 +208,7 @@ def _doOpenVariableFont(
|
|
|
211
208
|
partial.save(instancePath)
|
|
212
209
|
font = fontforge.open(instancePath)
|
|
213
210
|
initPersistentDict(font)
|
|
211
|
+
assert isinstance(font.persistent, dict)
|
|
214
212
|
font.persistent['VF'] = vfData
|
|
215
213
|
return font
|
|
216
214
|
|
|
@@ -222,12 +220,12 @@ def _openVF(
|
|
|
222
220
|
) -> fontforge.font:
|
|
223
221
|
with ttLib.TTFont(filename) as ttf:
|
|
224
222
|
if 'fvar' not in ttf:
|
|
225
|
-
return fontforge.open(filename)
|
|
223
|
+
return fontforge.open(str(filename))
|
|
226
224
|
elif isinstance(axisValuesOrInstance, dict):
|
|
227
225
|
if [axis for axis in ttf['fvar'].axes if axis.minValue != axis.maxValue]:
|
|
228
226
|
return _doOpenVariableFont(filename, axisValuesOrInstance, ttf, tmpdir)
|
|
229
227
|
else:
|
|
230
|
-
return fontforge.open(filename)
|
|
228
|
+
return fontforge.open(str(filename))
|
|
231
229
|
elif isinstance(axisValuesOrInstance, int):
|
|
232
230
|
return _doOpenVariableFont(
|
|
233
231
|
filename,
|
|
@@ -290,10 +288,11 @@ def openVariableFont(
|
|
|
290
288
|
filetype = checkExtensionTtfOrWoff2(filename)
|
|
291
289
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
292
290
|
if filetype == 'ttf':
|
|
293
|
-
_openVF(filename, axisValuesOrInstance, tmpdir)
|
|
291
|
+
font = _openVF(filename, axisValuesOrInstance, tmpdir)
|
|
294
292
|
else: # woff2
|
|
295
293
|
ttFile = _woff2Decompress(filename, tmpdir)
|
|
296
|
-
_openVF(ttFile, axisValuesOrInstance, tmpdir)
|
|
294
|
+
font = _openVF(ttFile, axisValuesOrInstance, tmpdir)
|
|
295
|
+
return font
|
|
297
296
|
|
|
298
297
|
|
|
299
298
|
def _setParameterDialog(filename: str | PathLike, ttf: ttLib.TTFont, tmpdir: str | PathLike):
|
|
@@ -314,8 +313,8 @@ def _setParameterDialog(filename: str | PathLike, ttf: ttLib.TTFont, tmpdir: str
|
|
|
314
313
|
|
|
315
314
|
if result := fontforge.askMulti('Please specify an instance to open', questions):
|
|
316
315
|
for key in result:
|
|
317
|
-
result[key] = intOrFloat(result[key])
|
|
318
|
-
_doOpenVariableFont(filename, result, ttf, tmpdir)
|
|
316
|
+
result[key] = intOrFloat(result[key]) # type: ignore
|
|
317
|
+
_doOpenVariableFont(filename, result, ttf, tmpdir) # type: ignore
|
|
319
318
|
|
|
320
319
|
|
|
321
320
|
def _chooseInstanceDialog(filename: str | PathLike, ttf: ttLib.TTFont, tmpdir: str | PathLike):
|
|
@@ -323,10 +322,10 @@ def _chooseInstanceDialog(filename: str | PathLike, ttf: ttLib.TTFont, tmpdir: s
|
|
|
323
322
|
result = fontforge.askChoices(
|
|
324
323
|
'Choose instance(s) to open',
|
|
325
324
|
'Instances in this font',
|
|
326
|
-
[str(ttf['name'].getName(i.subfamilyNameID, 3, 1, 0x409)) for i in ttf['fvar'].instances],
|
|
325
|
+
tuple([str(ttf['name'].getName(i.subfamilyNameID, 3, 1, 0x409)) for i in ttf['fvar'].instances]),
|
|
327
326
|
multiple=True
|
|
328
327
|
)
|
|
329
|
-
for i in (x[1] for x in zip(result, ttf['fvar'].instances) if x[0]):
|
|
328
|
+
for i in (x[1] for x in zip(ensureTuple(result), ttf['fvar'].instances) if x[0]):
|
|
330
329
|
_doOpenVariableFont(filename, i.coordinates, ttf, tmpdir)
|
|
331
330
|
|
|
332
331
|
|
|
@@ -370,6 +369,7 @@ def loadMenu(u, glyph):
|
|
|
370
369
|
def _generatePreHook(font: fontforge.font, target: str):
|
|
371
370
|
from fontforgeVF.utils import vfInfoExists
|
|
372
371
|
|
|
372
|
+
assert isinstance(font.temporary, dict)
|
|
373
373
|
changed = font.changed
|
|
374
374
|
if vfInfoExists(font):
|
|
375
375
|
if target.endswith('.ttf') or target.endswith('.woff2'):
|
|
@@ -378,7 +378,7 @@ def _generatePreHook(font: fontforge.font, target: str):
|
|
|
378
378
|
"This font has variable font metadata in its persistent dict.\n"
|
|
379
379
|
"Did you intend to output a variable font?\n"
|
|
380
380
|
"(all masters need to be opened beforehand)",
|
|
381
|
-
["_Yes", "_No"],
|
|
381
|
+
["_Yes", "_No"], # type: ignore
|
|
382
382
|
)
|
|
383
383
|
font.temporary['generateVF'] = (ans == 0)
|
|
384
384
|
font.changed = changed
|
|
@@ -388,6 +388,7 @@ def _generatePostHook(font: fontforge.font, target: str):
|
|
|
388
388
|
from fontforgeVF.utils import vfInfoExists
|
|
389
389
|
from fontforgeVF.export import exportVariableFont
|
|
390
390
|
|
|
391
|
+
assert isinstance(font.temporary, dict)
|
|
391
392
|
changed = font.changed
|
|
392
393
|
if vfInfoExists(font):
|
|
393
394
|
if 'generateVF' in font.temporary:
|
|
@@ -401,29 +402,11 @@ def _generatePostHook(font: fontforge.font, target: str):
|
|
|
401
402
|
font.changed = changed
|
|
402
403
|
|
|
403
404
|
|
|
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
405
|
def _addGenerateHook(font: fontforge.font):
|
|
423
406
|
if not isinstance(font.temporary, dict):
|
|
424
407
|
font.temporary = {}
|
|
425
|
-
|
|
426
|
-
|
|
408
|
+
addFontGenerateHook(font, 'generateFontPreHook', _generatePreHook)
|
|
409
|
+
addFontGenerateHook(font, 'generateFontPostHook', _generatePostHook)
|
|
427
410
|
|
|
428
411
|
|
|
429
412
|
def _loadHook_ttf(font: fontforge.font):
|
|
@@ -442,13 +425,14 @@ def _loadHook_ttf(font: fontforge.font):
|
|
|
442
425
|
"_Yes",
|
|
443
426
|
"Open with _parameters",
|
|
444
427
|
"_No",
|
|
445
|
-
]
|
|
428
|
+
] # type: ignore
|
|
446
429
|
)
|
|
447
430
|
if ans == 0 or ans == 1:
|
|
448
431
|
_selectInstanceDialog(font.path, ttf, ans)
|
|
449
432
|
else:
|
|
450
433
|
_selectInstanceDialog(font.path, ttf, 1)
|
|
451
434
|
initPersistentDict(font)
|
|
435
|
+
assert isinstance(font.persistent, dict)
|
|
452
436
|
vfData = _getVFData(ttf, {})
|
|
453
437
|
font.persistent['VF'] = vfData
|
|
454
438
|
|
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
import fontforge
|
|
2
|
-
import re
|
|
3
1
|
from os import PathLike
|
|
2
|
+
import re
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
"intOrFloat",
|
|
8
|
-
"initPersistentDict",
|
|
9
|
-
"vfInfoExists",
|
|
10
|
-
"getVFValue",
|
|
11
|
-
"setVFValue",
|
|
12
|
-
"deleteEmptyDicts",
|
|
13
|
-
"deleteVFValue",
|
|
14
|
-
"setOrDeleteVFValue",
|
|
15
|
-
"checkExtensionTtfOrWoff2",
|
|
16
|
-
]
|
|
4
|
+
import fontforge
|
|
17
5
|
|
|
18
6
|
|
|
19
|
-
def intOrFloat(val):
|
|
7
|
+
def intOrFloat(val: str | int | float):
|
|
20
8
|
"""Convert to ``int`` or ``float`` if possible
|
|
21
9
|
|
|
22
10
|
If parameter represents an integer, returns it converted to ``int``
|
|
@@ -43,6 +31,33 @@ def intOrFloat(val):
|
|
|
43
31
|
return f
|
|
44
32
|
|
|
45
33
|
|
|
34
|
+
def ensureList(obj) -> list:
|
|
35
|
+
if obj is None:
|
|
36
|
+
return []
|
|
37
|
+
elif isinstance(obj, list):
|
|
38
|
+
return obj
|
|
39
|
+
else:
|
|
40
|
+
return list(obj)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def ensureTuple(obj) -> tuple:
|
|
44
|
+
if obj is None:
|
|
45
|
+
return ()
|
|
46
|
+
elif isinstance(obj, tuple):
|
|
47
|
+
return obj
|
|
48
|
+
else:
|
|
49
|
+
return tuple(obj)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def ensureDict(obj) -> dict:
|
|
53
|
+
if obj is None:
|
|
54
|
+
return {}
|
|
55
|
+
elif isinstance(obj, dict):
|
|
56
|
+
return obj
|
|
57
|
+
else:
|
|
58
|
+
return dict(obj)
|
|
59
|
+
|
|
60
|
+
|
|
46
61
|
def initPersistentDict(font: fontforge.font):
|
|
47
62
|
"""Make sure ``font.persistent`` is a ``dict``
|
|
48
63
|
|
|
@@ -135,6 +150,7 @@ def getVFValue(font: fontforge.font, key: str, default=None):
|
|
|
135
150
|
if not vfInfoExists(font):
|
|
136
151
|
return default
|
|
137
152
|
else:
|
|
153
|
+
assert isinstance(font.persistent, dict)
|
|
138
154
|
info = font.persistent["VF"]
|
|
139
155
|
for part in _checkVFAddr(key):
|
|
140
156
|
c, k = part
|
|
@@ -149,6 +165,34 @@ def getVFValue(font: fontforge.font, key: str, default=None):
|
|
|
149
165
|
return info
|
|
150
166
|
|
|
151
167
|
|
|
168
|
+
def getVFValueAsList(font: fontforge.font, key: str, default: list = []) -> list:
|
|
169
|
+
"""Gets a value from VF info
|
|
170
|
+
|
|
171
|
+
Same as ``getVFValue()`` but ensures returning a list
|
|
172
|
+
|
|
173
|
+
:param font: Fontforge font object
|
|
174
|
+
:param key: Name of key
|
|
175
|
+
:param default: Optional. Returns this value if ``key`` does not \
|
|
176
|
+
exist. Without this parameter defaults to ``[]``.
|
|
177
|
+
:return: the value for ``key``, or ``default`` if no such ``key``.
|
|
178
|
+
"""
|
|
179
|
+
return ensureList(getVFValue(font, key, default))
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def getVFValueAsDict(font: fontforge.font, key: str, default: dict = {}) -> dict:
|
|
183
|
+
"""Gets a value from VF info
|
|
184
|
+
|
|
185
|
+
Same as ``getVFValue()`` but ensures returning a dict
|
|
186
|
+
|
|
187
|
+
:param font: Fontforge font object
|
|
188
|
+
:param key: Name of key
|
|
189
|
+
:param default: Optional. Returns this value if ``key`` does not \
|
|
190
|
+
exist. Without this parameter defaults to ``{}``.
|
|
191
|
+
:return: the value for ``key``, or ``default`` if no such ``key``.
|
|
192
|
+
"""
|
|
193
|
+
return ensureDict(getVFValue(font, key, default))
|
|
194
|
+
|
|
195
|
+
|
|
152
196
|
def _makeSureKeyExists(container, key):
|
|
153
197
|
if isinstance(container, list):
|
|
154
198
|
while len(container) <= key:
|
|
@@ -179,6 +223,7 @@ def setVFValue(font: fontforge.font, key: str, val):
|
|
|
179
223
|
:raises ``RuntimeError``: user refused ``initPersistentDict``.
|
|
180
224
|
"""
|
|
181
225
|
initPersistentDict(font)
|
|
226
|
+
assert isinstance(font.persistent, dict)
|
|
182
227
|
parent = font.persistent
|
|
183
228
|
info = parent["VF"]
|
|
184
229
|
prevk = "VF"
|
|
@@ -200,7 +245,7 @@ def setVFValue(font: fontforge.font, key: str, val):
|
|
|
200
245
|
parent[k] = val
|
|
201
246
|
|
|
202
247
|
|
|
203
|
-
def _deleteEmptyLists(d: dict):
|
|
248
|
+
def _deleteEmptyLists(d: dict | list):
|
|
204
249
|
if isinstance(d, list):
|
|
205
250
|
for i in range(len(d)):
|
|
206
251
|
if isinstance(d[i], dict):
|
|
@@ -253,6 +298,7 @@ def deleteVFValue(font: fontforge.font, key: str) -> bool:
|
|
|
253
298
|
:return: ``True`` if the key was deleted, ``False`` otherwise.
|
|
254
299
|
"""
|
|
255
300
|
if vfInfoExists(font):
|
|
301
|
+
assert isinstance(font.persistent, dict)
|
|
256
302
|
parent = font.persistent
|
|
257
303
|
info = parent["VF"]
|
|
258
304
|
for part in _checkVFAddr(key):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fontforge_variable_font
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
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
|
|
@@ -17,6 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: fonttools
|
|
19
19
|
Requires-Dist: fontmake
|
|
20
|
+
Requires-Dist: fontforge_plugin_helper
|
|
20
21
|
Dynamic: license-file
|
|
21
22
|
|
|
22
23
|
Fontforge Variable Font Plugin
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = fontforge_variable_font
|
|
3
|
-
version = 0.
|
|
3
|
+
version = 0.4.1
|
|
4
4
|
author = MihailJP
|
|
5
5
|
author_email = mihailjp@gmail.com
|
|
6
6
|
description = A FontForge_plugin to create a variable font
|
|
@@ -24,6 +24,7 @@ python_requires = >=3.10
|
|
|
24
24
|
install_requires =
|
|
25
25
|
fonttools
|
|
26
26
|
fontmake
|
|
27
|
+
fontforge_plugin_helper
|
|
27
28
|
|
|
28
29
|
[options.entry_points]
|
|
29
30
|
fontforge_plugin =
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|