Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More types #1018

Merged
merged 11 commits into from
Dec 11, 2023
80 changes: 80 additions & 0 deletions src/fontra/client/core/classes.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"axes": {
"type": "list",
"subtype": "GlobalAxis"
},
"sources": {
"type": "list",
"subtype": "GlobalSource"
}
},
"VariableGlyph": {
Expand Down Expand Up @@ -66,6 +70,10 @@
"type": "dict",
"subtype": "float"
},
"locationBase": {
"type": "str",
"optional": true
},
"inactive": {
"type": "bool"
},
Expand Down Expand Up @@ -102,6 +110,14 @@
"verticalOrigin": {
"type": "float",
"optional": true
},
"anchors": {
"type": "list",
"subtype": "Anchor"
},
"guidelines": {
"type": "list",
"subtype": "Guideline"
}
},
"PackedPath": {
Expand Down Expand Up @@ -167,6 +183,40 @@
"type": "float"
}
},
"Anchor": {
"name": {
"type": "str"
},
"x": {
"type": "float"
},
"y": {
"type": "float"
},
"customData": {
"type": "dict",
"subtype": "Any"
}
},
"Guideline": {
"name": {
"type": "str",
"optional": true
},
"x": {
"type": "float"
},
"y": {
"type": "float"
},
"angle": {
"type": "float"
},
"customData": {
"type": "dict",
"subtype": "Any"
}
},
"GlobalAxis": {
"name": {
"type": "str"
Expand All @@ -193,5 +243,35 @@
"hidden": {
"type": "bool"
}
},
"GlobalSource": {
"name": {
"type": "str"
},
"location": {
"type": "dict",
"subtype": "float"
},
"verticalMetrics": {
"type": "dict",
"subtype": "GlobalMetric"
},
"guidelines": {
"type": "list",
"subtype": "Guideline"
},
"customData": {
"type": "dict",
"subtype": "Any"
}
},
"GlobalMetric": {
"value": {
"type": "float"
},
"customData": {
"type": "dict",
"subtype": "Any"
}
}
}
193 changes: 122 additions & 71 deletions src/fontra/core/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,87 @@

from .path import PackedPath, Path, Point, PointType

Location = dict[str, float]
CustomData = dict[str, Any]

@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)
sources: list[GlobalSource] = 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 Component:
class GlobalSource:
name: str
transformation: DecomposedTransform = field(default_factory=DecomposedTransform)
location: Location = field(default_factory=Location)
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)


@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 GlobalMetric:
value: float
customData: CustomData = field(default_factory=dict)

def convertToPackedPaths(self):
return replace(self, path=self.path.asPackedPath())

def convertToPaths(self):
return replace(self, path=self.path.asPath())
@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 Source:
name: str
layerName: str
location: Location = field(default_factory=Location)
inactive: bool = False
customData: CustomData = field(default_factory=CustomData)
class HorizontalGuideline:
name: Optional[str]
y: float
customData: CustomData = field(default_factory=dict)


@dataclass(kw_only=True)
class Layer:
glyph: StaticGlyph
customData: CustomData = field(default_factory=CustomData)
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
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)
Expand All @@ -65,7 +107,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)
Expand All @@ -74,11 +116,59 @@ def convertToPaths(self):
return _convertToPathType(self, False)


def _hasAnyPathType(varGlyph, pathType):
return any(
isinstance(layer.glyph.path, pathType) for layer in varGlyph.layers.values()
@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)


@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
anchors: list[Anchor] = field(default_factory=list)
guidelines: list[Union[Guideline, HorizontalGuideline, VerticalGuideline]] = field(
default_factory=list
)

def convertToPackedPaths(self):
return replace(self, path=self.path.asPackedPath())

def convertToPaths(self):
return replace(self, path=self.path.asPath())


@dataclass(kw_only=True)
class Component:
name: str
transformation: DecomposedTransform = field(default_factory=DecomposedTransform)
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]


def _convertToPathType(varGlyph, packedPath):
if not _hasAnyPathType(varGlyph, Path if packedPath else PackedPath):
Expand All @@ -97,49 +187,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


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)
customData: CustomData = field(default_factory=CustomData)
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):
Expand Down
3 changes: 2 additions & 1 deletion test-common/fonts/MutatorSans.fontra/font-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@
"mapping": [],
"hidden": false
}
]
],
"sources": []
}