From e848b71db15d2770385acdfe805388684684ac76 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sat, 2 Dec 2023 20:57:46 +0100 Subject: [PATCH 01/11] Add locationBase field to Source --- src/fontra/client/core/classes.json | 4 ++++ src/fontra/core/classes.py | 1 + 2 files changed, 5 insertions(+) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index ef5436b18..835c24261 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -66,6 +66,10 @@ "type": "dict", "subtype": "float" }, + "locationBase": { + "type": "str", + "optional": true + }, "inactive": { "type": "bool" }, diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 89cad5320..84f8f7d82 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -41,6 +41,7 @@ class Source: name: str layerName: str location: Location = field(default_factory=Location) + locationBase: Optional[str] = None inactive: bool = False customData: CustomData = field(default_factory=CustomData) From 8537d602e3e56ce6f32bc5c57ae7069d3a83e1c6 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:25:28 +0100 Subject: [PATCH 02/11] No need for named dict types --- src/fontra/core/classes.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 84f8f7d82..489bcfe3f 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -121,15 +121,11 @@ class GlobalDiscreteAxis: hidden: bool = False -GlyphSet = dict[str, VariableGlyph] -GlyphMap = dict[str, list[int]] - - @dataclass(kw_only=True) class Font: unitsPerEm: int = 1000 - glyphs: GlyphSet = field(default_factory=GlyphSet) - glyphMap: GlyphMap = field(default_factory=GlyphMap) + glyphs: dict[str, VariableGlyph] = field(default_factory=dict) + glyphMap: dict[str, list[int]] = field(default_factory=dict) customData: CustomData = field(default_factory=CustomData) axes: list[Union[GlobalAxis, GlobalDiscreteAxis]] = field(default_factory=list) From 4ed32449379df890e3a9aaa309ab6710e436215c Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:30:57 +0100 Subject: [PATCH 03/11] Use generic factory where possible in default_factory, making it easier to reorder the classes --- src/fontra/core/classes.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 489bcfe3f..c2e3d90d3 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -18,7 +18,7 @@ class Component: name: str transformation: DecomposedTransform = field(default_factory=DecomposedTransform) - location: Location = field(default_factory=Location) + location: Location = field(default_factory=dict) @dataclass(kw_only=True) @@ -40,16 +40,16 @@ def convertToPaths(self): class Source: name: str layerName: str - location: Location = field(default_factory=Location) + location: Location = field(default_factory=dict) locationBase: Optional[str] = None inactive: bool = False - customData: CustomData = field(default_factory=CustomData) + customData: CustomData = field(default_factory=dict) @dataclass(kw_only=True) class Layer: glyph: StaticGlyph - customData: CustomData = field(default_factory=CustomData) + customData: CustomData = field(default_factory=dict) @dataclass(kw_only=True) @@ -66,7 +66,7 @@ class VariableGlyph: axes: list[LocalAxis] = field(default_factory=list) sources: list[Source] = field(default_factory=list) layers: dict[str, Layer] = field(default_factory=dict) - customData: CustomData = field(default_factory=CustomData) + customData: CustomData = field(default_factory=dict) def convertToPackedPaths(self): return _convertToPathType(self, True) @@ -126,7 +126,7 @@ class Font: unitsPerEm: int = 1000 glyphs: dict[str, VariableGlyph] = field(default_factory=dict) glyphMap: dict[str, list[int]] = field(default_factory=dict) - customData: CustomData = field(default_factory=CustomData) + customData: CustomData = field(default_factory=dict) axes: list[Union[GlobalAxis, GlobalDiscreteAxis]] = field(default_factory=list) def _trackAssignedAttributeNames(self): From 332e2253d1c4863d351eebc162e2f58e30ccac6c Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:45:56 +0100 Subject: [PATCH 04/11] Reorder classes --- src/fontra/core/classes.py | 149 +++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 74 deletions(-) diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index c2e3d90d3..4f1577480 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -10,46 +10,61 @@ from .path import PackedPath, Path, Point, PointType -Location = dict[str, float] -CustomData = dict[str, Any] - @dataclass(kw_only=True) -class Component: - name: str - transformation: DecomposedTransform = field(default_factory=DecomposedTransform) - location: Location = field(default_factory=dict) +class Font: + unitsPerEm: int = 1000 + glyphs: dict[str, VariableGlyph] = field(default_factory=dict) + glyphMap: dict[str, list[int]] = field(default_factory=dict) + customData: CustomData = field(default_factory=dict) + axes: list[Union[GlobalAxis, GlobalDiscreteAxis]] = field(default_factory=list) + + def _trackAssignedAttributeNames(self): + # see fonthandler.py + self._assignedAttributeNames = set() + + def __setattr__(self, attrName, value): + if hasattr(self, "_assignedAttributeNames"): + self._assignedAttributeNames.add(attrName) + super().__setattr__(attrName, value) @dataclass(kw_only=True) -class StaticGlyph: - path: Union[PackedPath, Path] = field(default_factory=PackedPath) - components: list[Component] = field(default_factory=list) - xAdvance: Optional[float] = None - yAdvance: Optional[float] = None - verticalOrigin: Optional[float] = None +class VariableGlyph: + name: str + axes: list[LocalAxis] = field(default_factory=list) + sources: list[Source] = field(default_factory=list) + layers: dict[str, Layer] = field(default_factory=dict) + customData: CustomData = field(default_factory=dict) def convertToPackedPaths(self): - return replace(self, path=self.path.asPackedPath()) + return _convertToPathType(self, True) def convertToPaths(self): - return replace(self, path=self.path.asPath()) + return _convertToPathType(self, False) @dataclass(kw_only=True) -class Source: - name: str - layerName: str - location: Location = field(default_factory=dict) - locationBase: Optional[str] = None - inactive: bool = False - customData: CustomData = field(default_factory=dict) +class GlobalAxis: + name: str # this identifies the axis + label: str # a user friendly label + tag: str # the opentype 4-char tag + minValue: float + defaultValue: float + maxValue: float + mapping: list[list[float]] = field(default_factory=list) + hidden: bool = False @dataclass(kw_only=True) -class Layer: - glyph: StaticGlyph - customData: CustomData = field(default_factory=dict) +class GlobalDiscreteAxis: + name: str # this identifies the axis + label: str # a user friendly label + tag: str # the opentype 4-char tag + values: list[float] + defaultValue: float + mapping: list[list[float]] = field(default_factory=list) + hidden: bool = False @dataclass(kw_only=True) @@ -61,24 +76,45 @@ class LocalAxis: @dataclass(kw_only=True) -class VariableGlyph: +class Source: name: str - axes: list[LocalAxis] = field(default_factory=list) - sources: list[Source] = field(default_factory=list) - layers: dict[str, Layer] = field(default_factory=dict) + layerName: str + location: Location = field(default_factory=dict) + locationBase: Optional[str] = None + inactive: bool = False customData: CustomData = field(default_factory=dict) + +@dataclass(kw_only=True) +class Layer: + glyph: StaticGlyph + customData: CustomData = field(default_factory=dict) + + +@dataclass(kw_only=True) +class StaticGlyph: + path: Union[PackedPath, Path] = field(default_factory=PackedPath) + components: list[Component] = field(default_factory=list) + xAdvance: Optional[float] = None + yAdvance: Optional[float] = None + verticalOrigin: Optional[float] = None + def convertToPackedPaths(self): - return _convertToPathType(self, True) + return replace(self, path=self.path.asPackedPath()) def convertToPaths(self): - return _convertToPathType(self, False) + return replace(self, path=self.path.asPath()) -def _hasAnyPathType(varGlyph, pathType): - return any( - isinstance(layer.glyph.path, pathType) for layer in varGlyph.layers.values() - ) +@dataclass(kw_only=True) +class Component: + name: str + transformation: DecomposedTransform = field(default_factory=DecomposedTransform) + location: Location = field(default_factory=dict) + + +Location = dict[str, float] +CustomData = dict[str, Any] def _convertToPathType(varGlyph, packedPath): @@ -98,45 +134,10 @@ def _convertToPathType(varGlyph, packedPath): ) -@dataclass(kw_only=True) -class GlobalAxis: - name: str # this identifies the axis - label: str # a user friendly label - tag: str # the opentype 4-char tag - minValue: float - defaultValue: float - maxValue: float - mapping: list[list[float]] = field(default_factory=list) - hidden: bool = False - - -@dataclass(kw_only=True) -class GlobalDiscreteAxis: - name: str # this identifies the axis - label: str # a user friendly label - tag: str # the opentype 4-char tag - values: list[float] - defaultValue: float - mapping: list[list[float]] = field(default_factory=list) - hidden: bool = False - - -@dataclass(kw_only=True) -class Font: - unitsPerEm: int = 1000 - glyphs: dict[str, VariableGlyph] = field(default_factory=dict) - glyphMap: dict[str, list[int]] = field(default_factory=dict) - customData: CustomData = field(default_factory=dict) - axes: list[Union[GlobalAxis, GlobalDiscreteAxis]] = field(default_factory=list) - - def _trackAssignedAttributeNames(self): - # see fonthandler.py - self._assignedAttributeNames = set() - - def __setattr__(self, attrName, value): - if hasattr(self, "_assignedAttributeNames"): - self._assignedAttributeNames.add(attrName) - super().__setattr__(attrName, value) +def _hasAnyPathType(varGlyph, pathType): + return any( + isinstance(layer.glyph.path, pathType) for layer in varGlyph.layers.values() + ) def makeSchema(*classes, schema=None): From cd4e8b4c55d372157ccefea0f3308e0666321cc9 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:50:43 +0100 Subject: [PATCH 05/11] Add GlobalSource type --- src/fontra/core/classes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 4f1577480..6df216108 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -18,6 +18,7 @@ class Font: glyphMap: dict[str, list[int]] = field(default_factory=dict) customData: CustomData = field(default_factory=dict) axes: list[Union[GlobalAxis, GlobalDiscreteAxis]] = field(default_factory=list) + sources: list[GlobalSource] = field(default_factory=list) def _trackAssignedAttributeNames(self): # see fonthandler.py @@ -29,6 +30,13 @@ def __setattr__(self, attrName, value): super().__setattr__(attrName, value) +@dataclass(kw_only=True) +class GlobalSource: + name: str + location: Location = field(default_factory=dict) + customData: CustomData = field(default_factory=dict) + + @dataclass(kw_only=True) class VariableGlyph: name: str From b99108c3df8726fa794d80829e847f4e17bff951 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:51:47 +0100 Subject: [PATCH 06/11] Move class --- src/fontra/core/classes.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 6df216108..6afa0f895 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -37,21 +37,6 @@ class GlobalSource: customData: CustomData = field(default_factory=dict) -@dataclass(kw_only=True) -class VariableGlyph: - name: str - axes: list[LocalAxis] = field(default_factory=list) - sources: list[Source] = field(default_factory=list) - layers: dict[str, Layer] = field(default_factory=dict) - customData: CustomData = field(default_factory=dict) - - def convertToPackedPaths(self): - return _convertToPathType(self, True) - - def convertToPaths(self): - return _convertToPathType(self, False) - - @dataclass(kw_only=True) class GlobalAxis: name: str # this identifies the axis @@ -83,6 +68,21 @@ class LocalAxis: maxValue: float +@dataclass(kw_only=True) +class VariableGlyph: + name: str + axes: list[LocalAxis] = field(default_factory=list) + sources: list[Source] = field(default_factory=list) + layers: dict[str, Layer] = field(default_factory=dict) + customData: CustomData = field(default_factory=dict) + + def convertToPackedPaths(self): + return _convertToPathType(self, True) + + def convertToPaths(self): + return _convertToPathType(self, False) + + @dataclass(kw_only=True) class Source: name: str From 8437087b9eb2f4b3985d519e6d19c9a93eebeab0 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:52:11 +0100 Subject: [PATCH 07/11] Adjust to new types --- src/fontra/client/core/classes.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index 835c24261..895a70fe5 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -18,6 +18,10 @@ "axes": { "type": "list", "subtype": "GlobalAxis" + }, + "sources": { + "type": "list", + "subtype": "GlobalSource" } }, "VariableGlyph": { @@ -197,5 +201,18 @@ "hidden": { "type": "bool" } + }, + "GlobalSource": { + "name": { + "type": "str" + }, + "location": { + "type": "dict", + "subtype": "float" + }, + "customData": { + "type": "dict", + "subtype": "Any" + } } } From 628327cbce99e5688465bf4000a9511a878190ce Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 11:58:27 +0100 Subject: [PATCH 08/11] Add GlobalMetric type --- src/fontra/client/core/classes.json | 13 +++++++++++++ src/fontra/core/classes.py | 7 +++++++ test-common/fonts/MutatorSans.fontra/font-data.json | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index 895a70fe5..7bdd3e2fe 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -210,6 +210,19 @@ "type": "dict", "subtype": "float" }, + "verticalMetrics": { + "type": "dict", + "subtype": "GlobalMetric" + }, + "customData": { + "type": "dict", + "subtype": "Any" + } + }, + "GlobalMetric": { + "value": { + "type": "float" + }, "customData": { "type": "dict", "subtype": "Any" diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 6afa0f895..5db0a76a6 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -34,6 +34,13 @@ def __setattr__(self, attrName, value): class GlobalSource: name: str location: Location = field(default_factory=dict) + verticalMetrics: dict[str, GlobalMetric] = field(default_factory=dict) + customData: CustomData = field(default_factory=dict) + + +@dataclass(kw_only=True) +class GlobalMetric: + value: float customData: CustomData = field(default_factory=dict) diff --git a/test-common/fonts/MutatorSans.fontra/font-data.json b/test-common/fonts/MutatorSans.fontra/font-data.json index 80631efbd..34f511d6a 100644 --- a/test-common/fonts/MutatorSans.fontra/font-data.json +++ b/test-common/fonts/MutatorSans.fontra/font-data.json @@ -43,5 +43,6 @@ "mapping": [], "hidden": false } -] +], +"sources": [] } From aeda06cd123ab7a7030bb5fcf4b26b53ab382e51 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 16:00:07 +0100 Subject: [PATCH 09/11] Add guidelines to sources --- src/fontra/client/core/classes.json | 23 +++++++++++++++++++++++ src/fontra/core/classes.py | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index 7bdd3e2fe..d49a7e0e7 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -214,6 +214,10 @@ "type": "dict", "subtype": "GlobalMetric" }, + "guidelines": { + "type": "list", + "subtype": "Guideline" + }, "customData": { "type": "dict", "subtype": "Any" @@ -227,5 +231,24 @@ "type": "dict", "subtype": "Any" } + }, + "Guideline": { + "name": { + "type": "str", + "optional": true + }, + "x": { + "type": "float" + }, + "y": { + "type": "float" + }, + "angle": { + "type": "float" + }, + "customData": { + "type": "dict", + "subtype": "Any" + } } } diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 5db0a76a6..82b108ffa 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -35,6 +35,9 @@ class GlobalSource: name: str location: Location = field(default_factory=dict) verticalMetrics: dict[str, GlobalMetric] = field(default_factory=dict) + guidelines: list[Union[Guideline, HorizontalGuideline, VerticalGuideline]] = field( + default_factory=list + ) customData: CustomData = field(default_factory=dict) @@ -44,6 +47,29 @@ class GlobalMetric: customData: CustomData = field(default_factory=dict) +@dataclass(kw_only=True) +class Guideline: + name: Optional[str] + x: float + y: float + angle: float + customData: CustomData = field(default_factory=dict) + + +@dataclass(kw_only=True) +class HorizontalGuideline: + name: Optional[str] + y: float + customData: CustomData = field(default_factory=dict) + + +@dataclass(kw_only=True) +class VerticalGuideline: + name: str | None + x: float + customData: CustomData = field(default_factory=dict) + + @dataclass(kw_only=True) class GlobalAxis: name: str # this identifies the axis From 382bb94b64096703fe8d8cf709c7a56914b7a3c9 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 10 Dec 2023 16:02:19 +0100 Subject: [PATCH 10/11] Add guidelines to glyph --- src/fontra/client/core/classes.json | 42 ++++++++++++++++------------- src/fontra/core/classes.py | 3 +++ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index d49a7e0e7..50e47c6ff 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -110,6 +110,10 @@ "verticalOrigin": { "type": "float", "optional": true + }, + "guidelines": { + "type": "list", + "subtype": "Guideline" } }, "PackedPath": { @@ -175,6 +179,25 @@ "type": "float" } }, + "Guideline": { + "name": { + "type": "str", + "optional": true + }, + "x": { + "type": "float" + }, + "y": { + "type": "float" + }, + "angle": { + "type": "float" + }, + "customData": { + "type": "dict", + "subtype": "Any" + } + }, "GlobalAxis": { "name": { "type": "str" @@ -231,24 +254,5 @@ "type": "dict", "subtype": "Any" } - }, - "Guideline": { - "name": { - "type": "str", - "optional": true - }, - "x": { - "type": "float" - }, - "y": { - "type": "float" - }, - "angle": { - "type": "float" - }, - "customData": { - "type": "dict", - "subtype": "Any" - } } } diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 82b108ffa..77a0fab6b 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -139,6 +139,9 @@ class StaticGlyph: xAdvance: Optional[float] = None yAdvance: Optional[float] = None verticalOrigin: Optional[float] = None + guidelines: list[Union[Guideline, HorizontalGuideline, VerticalGuideline]] = field( + default_factory=list + ) def convertToPackedPaths(self): return replace(self, path=self.path.asPackedPath()) From 52fb29de523ccceffd225bcb8e6e6cdf24c897c8 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 11 Dec 2023 20:24:30 +0100 Subject: [PATCH 11/11] Add Anchor type and anchors field to StaticGlyph --- src/fontra/client/core/classes.json | 19 +++++++++++++++++++ src/fontra/core/classes.py | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/src/fontra/client/core/classes.json b/src/fontra/client/core/classes.json index 50e47c6ff..5e465627c 100644 --- a/src/fontra/client/core/classes.json +++ b/src/fontra/client/core/classes.json @@ -111,6 +111,10 @@ "type": "float", "optional": true }, + "anchors": { + "type": "list", + "subtype": "Anchor" + }, "guidelines": { "type": "list", "subtype": "Guideline" @@ -179,6 +183,21 @@ "type": "float" } }, + "Anchor": { + "name": { + "type": "str" + }, + "x": { + "type": "float" + }, + "y": { + "type": "float" + }, + "customData": { + "type": "dict", + "subtype": "Any" + } + }, "Guideline": { "name": { "type": "str", diff --git a/src/fontra/core/classes.py b/src/fontra/core/classes.py index 77a0fab6b..4e86393f6 100644 --- a/src/fontra/core/classes.py +++ b/src/fontra/core/classes.py @@ -139,6 +139,7 @@ class StaticGlyph: xAdvance: Optional[float] = None yAdvance: Optional[float] = None verticalOrigin: Optional[float] = None + anchors: list[Anchor] = field(default_factory=list) guidelines: list[Union[Guideline, HorizontalGuideline, VerticalGuideline]] = field( default_factory=list ) @@ -157,6 +158,14 @@ class Component: location: Location = field(default_factory=dict) +@dataclass(kw_only=True) +class Anchor: + name: str + x: float + y: float + customData: CustomData = field(default_factory=dict) + + Location = dict[str, float] CustomData = dict[str, Any]