Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/font-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,25 @@ This section contains the advance widths of all the glyphs.

This section lists contextual substitutions. If OpenType contextual substitution support is unavailable, this section can be used to select replacement glyphs and their metadata. Each item in `inputGlyphs`, `backtrackGlyphs`, and `lookaheadGlyphs` is a class of matching glyphs. The `index` in a substitution identifies the item in `inputGlyphs` that should be replaced.

When a contextual lookup uses an OpenType `MarkAttachmentType`, `markAttachmentClasses` lists the glyphs in each attachment class and the rule's `markAttachmentClass` identifies the applicable class. Marks outside that class are ignored while matching the rule.

```js
{
...
"markAttachmentClasses": {
"gorgon": [
"digorgon",
"digorgonDottedLeftAbove",
"digorgonDottedLeftBelow",
"digorgonDottedRight",
"gorgonAbove",
"gorgonDottedLeft",
"gorgonDottedRight",
"trigorgon",
"trigorgonDottedLeftAbove",
"trigorgonDottedRight"
]
},
"contextualSubstitutions": [
{
"inputGlyphs": [
Expand All @@ -165,7 +181,8 @@ This section lists contextual substitutions. If OpenType contextual substitution
"from": "yporroi",
"to": "yporroi.gorgon"
}
]
],
"markAttachmentClass": "gorgon"
}
],
...
Expand Down
23 changes: 20 additions & 3 deletions fonts/neanes.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7898,6 +7898,20 @@
]
}
},
"markAttachmentClasses": {
"gorgon": [
"digorgon",
"digorgonDottedLeftAbove",
"digorgonDottedLeftBelow",
"digorgonDottedRight",
"gorgonAbove",
"gorgonDottedLeft",
"gorgonDottedRight",
"trigorgon",
"trigorgonDottedLeftAbove",
"trigorgonDottedRight"
]
},
"contextualSubstitutions": [
{
"inputGlyphs": [
Expand Down Expand Up @@ -7964,7 +7978,8 @@
"from": "yporroi",
"to": "yporroi.gorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -7987,7 +8002,8 @@
"from": "yporroi",
"to": "yporroi.digorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8009,7 +8025,8 @@
"from": "yporroi",
"to": "yporroi.trigorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand Down
23 changes: 20 additions & 3 deletions fonts/neanesengraving.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7898,6 +7898,20 @@
]
}
},
"markAttachmentClasses": {
"gorgon": [
"digorgon",
"digorgonDottedLeftAbove",
"digorgonDottedLeftBelow",
"digorgonDottedRight",
"gorgonAbove",
"gorgonDottedLeft",
"gorgonDottedRight",
"trigorgon",
"trigorgonDottedLeftAbove",
"trigorgonDottedRight"
]
},
"contextualSubstitutions": [
{
"inputGlyphs": [
Expand Down Expand Up @@ -7964,7 +7978,8 @@
"from": "yporroi",
"to": "yporroi.gorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -7987,7 +8002,8 @@
"from": "yporroi",
"to": "yporroi.digorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8009,7 +8025,8 @@
"from": "yporroi",
"to": "yporroi.trigorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand Down
23 changes: 20 additions & 3 deletions fonts/neanesstathisseries.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7930,6 +7930,20 @@
]
}
},
"markAttachmentClasses": {
"gorgon": [
"digorgon",
"digorgonDottedLeftAbove",
"digorgonDottedLeftBelow",
"digorgonDottedRight",
"gorgonAbove",
"gorgonDottedLeft",
"gorgonDottedRight",
"trigorgon",
"trigorgonDottedLeftAbove",
"trigorgonDottedRight"
]
},
"contextualSubstitutions": [
{
"inputGlyphs": [
Expand Down Expand Up @@ -7996,7 +8010,8 @@
"from": "yporroi",
"to": "yporroi.gorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8019,7 +8034,8 @@
"from": "yporroi",
"to": "yporroi.digorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8041,7 +8057,8 @@
"from": "yporroi",
"to": "yporroi.trigorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand Down
23 changes: 20 additions & 3 deletions fonts/neanesstathisseriesengraving.metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -7930,6 +7930,20 @@
]
}
},
"markAttachmentClasses": {
"gorgon": [
"digorgon",
"digorgonDottedLeftAbove",
"digorgonDottedLeftBelow",
"digorgonDottedRight",
"gorgonAbove",
"gorgonDottedLeft",
"gorgonDottedRight",
"trigorgon",
"trigorgonDottedLeftAbove",
"trigorgonDottedRight"
]
},
"contextualSubstitutions": [
{
"inputGlyphs": [
Expand Down Expand Up @@ -7996,7 +8010,8 @@
"from": "yporroi",
"to": "yporroi.gorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8019,7 +8034,8 @@
"from": "yporroi",
"to": "yporroi.digorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand All @@ -8041,7 +8057,8 @@
"from": "yporroi",
"to": "yporroi.trigorgon"
}
]
],
"markAttachmentClass": "gorgon"
},
{
"inputGlyphs": [
Expand Down
52 changes: 41 additions & 11 deletions scripts/generate-font-metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,12 @@ def asdict(self):
if alternates:
d["glyphsWithAlternates"] = alternates

contextual_substitutions = self.contextual_substitutions()
contextual_substitutions, mark_attachment_classes = (
self.contextual_substitutions()
)
if mark_attachment_classes:
d["markAttachmentClasses"] = mark_attachment_classes

if contextual_substitutions:
d["contextualSubstitutions"] = contextual_substitutions

Expand Down Expand Up @@ -450,8 +455,11 @@ def contextual_substitutions(self):

substitutions_by_lookup = self._substitutions_by_lookup(feature_file)
contextual_substitutions = []
mark_attachment_classes = {}

for statement in self._walk_statements(feature_file):
for statement, lookup_flag in self._walk_statements_with_lookup_flags(
feature_file
):
if isinstance(statement, ast.ChainContextSubstStatement):
input_glyphs = [self._glyph_set(g) for g in statement.glyphs]
backtrack_glyphs = [self._glyph_set(g) for g in statement.prefix]
Expand Down Expand Up @@ -498,16 +506,25 @@ def contextual_substitutions(self):
continue

if substitutions:
contextual_substitutions.append(
{
"inputGlyphs": input_glyphs,
"backtrackGlyphs": backtrack_glyphs,
"lookaheadGlyphs": lookahead_glyphs,
"substitutions": substitutions,
}
)
contextual_substitution = {
"inputGlyphs": input_glyphs,
"backtrackGlyphs": backtrack_glyphs,
"lookaheadGlyphs": lookahead_glyphs,
"substitutions": substitutions,
}

if lookup_flag is not None and lookup_flag.markAttachment is not None:
mark_attachment_class = lookup_flag.markAttachment.glyphclass.name
mark_attachment_classes[mark_attachment_class] = self._glyph_set(
lookup_flag.markAttachment
)
contextual_substitution["markAttachmentClass"] = (
mark_attachment_class
)

return contextual_substitutions
contextual_substitutions.append(contextual_substitution)

return contextual_substitutions, mark_attachment_classes

def _substitutions_by_lookup(self, node):
substitutions_by_lookup = {}
Expand Down Expand Up @@ -549,6 +566,19 @@ def _walk_statements(self, node):
yield statement
yield from self._walk_statements(statement)

def _walk_statements_with_lookup_flags(self, node, lookup_flag=None):
current_lookup_flag = lookup_flag

for statement in getattr(node, "statements", []):
if isinstance(statement, ast.LookupFlagStatement):
current_lookup_flag = statement
continue

yield statement, current_lookup_flag
yield from self._walk_statements_with_lookup_flags(
statement, current_lookup_flag
)

@staticmethod
def _glyph_set(glyph_expr):
return sorted(glyph_expr.glyphSet())
Expand Down