Merge remote-tracking branch 'origin/main' into variable-colr
This commit is contained in:
commit
fcd2a67f52
3
.github/workflows/publish.yml
vendored
3
.github/workflows/publish.yml
vendored
@ -9,6 +9,9 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '*.*.*' # e.g. 1.0.0 or 20.15.10
|
- '*.*.*' # e.g. 1.0.0 or 20.15.10
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
3
.github/workflows/test.yml
vendored
3
.github/workflows/test.yml
vendored
@ -6,6 +6,9 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -764,7 +764,7 @@ class Builder(object):
|
|||||||
gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
|
gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
|
||||||
if self.varstorebuilder:
|
if self.varstorebuilder:
|
||||||
store = self.varstorebuilder.finish()
|
store = self.varstorebuilder.finish()
|
||||||
if store.VarData:
|
if store:
|
||||||
gdef.Version = 0x00010003
|
gdef.Version = 0x00010003
|
||||||
gdef.VarStore = store
|
gdef.VarStore = store
|
||||||
varidx_map = store.optimize()
|
varidx_map = store.optimize()
|
||||||
|
@ -73,6 +73,7 @@ class Parser(object):
|
|||||||
self.next_token_location_ = None
|
self.next_token_location_ = None
|
||||||
lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
|
lexerClass = IncludingLexer if followIncludes else NonIncludingLexer
|
||||||
self.lexer_ = lexerClass(featurefile, includeDir=includeDir)
|
self.lexer_ = lexerClass(featurefile, includeDir=includeDir)
|
||||||
|
self.missing = {}
|
||||||
self.advance_lexer_(comments=True)
|
self.advance_lexer_(comments=True)
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
@ -125,6 +126,16 @@ class Parser(object):
|
|||||||
),
|
),
|
||||||
self.cur_token_location_,
|
self.cur_token_location_,
|
||||||
)
|
)
|
||||||
|
# Report any missing glyphs at the end of parsing
|
||||||
|
if self.missing:
|
||||||
|
error = [
|
||||||
|
" %s (first found at %s)" % (name, loc)
|
||||||
|
for name, loc in self.missing.items()
|
||||||
|
]
|
||||||
|
raise FeatureLibError(
|
||||||
|
"The following glyph names are referenced but are missing from the "
|
||||||
|
"glyph set:\n" + ("\n".join(error)), None
|
||||||
|
)
|
||||||
return self.doc_
|
return self.doc_
|
||||||
|
|
||||||
def parse_anchor_(self):
|
def parse_anchor_(self):
|
||||||
@ -2073,19 +2084,18 @@ class Parser(object):
|
|||||||
raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
|
raise FeatureLibError("Expected a glyph name or CID", self.cur_token_location_)
|
||||||
|
|
||||||
def check_glyph_name_in_glyph_set(self, *names):
|
def check_glyph_name_in_glyph_set(self, *names):
|
||||||
"""Raises if glyph name (just `start`) or glyph names of a
|
"""Adds a glyph name (just `start`) or glyph names of a
|
||||||
range (`start` and `end`) are not in the glyph set.
|
range (`start` and `end`) which are not in the glyph set
|
||||||
|
to the "missing list" for future error reporting.
|
||||||
|
|
||||||
If no glyph set is present, does nothing.
|
If no glyph set is present, does nothing.
|
||||||
"""
|
"""
|
||||||
if self.glyphNames_:
|
if self.glyphNames_:
|
||||||
missing = [name for name in names if name not in self.glyphNames_]
|
for name in names:
|
||||||
if missing:
|
if name in self.glyphNames_:
|
||||||
raise FeatureLibError(
|
continue
|
||||||
"The following glyph names are referenced but are missing from the "
|
if name not in self.missing:
|
||||||
f"glyph set: {', '.join(missing)}",
|
self.missing[name] = self.cur_token_location_
|
||||||
self.cur_token_location_,
|
|
||||||
)
|
|
||||||
|
|
||||||
def expect_markClass_reference_(self):
|
def expect_markClass_reference_(self):
|
||||||
name = self.expect_class_name_()
|
name = self.expect_class_name_()
|
||||||
|
@ -534,7 +534,9 @@ class OTTableWriter(object):
|
|||||||
https://github.com/harfbuzz/uharfbuzz/blob/main/src/uharfbuzz/_harfbuzz.pyx#L1149
|
https://github.com/harfbuzz/uharfbuzz/blob/main/src/uharfbuzz/_harfbuzz.pyx#L1149
|
||||||
"""
|
"""
|
||||||
internedTables = {}
|
internedTables = {}
|
||||||
self._doneWriting(internedTables, shareExtension=True)
|
# TODO: Restore shareExtension=True after we fix
|
||||||
|
# https://github.com/fonttools/fonttools/issues/2661
|
||||||
|
self._doneWriting(internedTables, shareExtension=False)
|
||||||
tables = []
|
tables = []
|
||||||
obj_list = []
|
obj_list = []
|
||||||
done = {}
|
done = {}
|
||||||
|
@ -488,7 +488,7 @@ def _get_advance_metrics(font, masterModel, master_ttfs,
|
|||||||
vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
|
vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
|
||||||
|
|
||||||
indirectStore = storeBuilder.finish()
|
indirectStore = storeBuilder.finish()
|
||||||
mapping2 = indirectStore.optimize()
|
mapping2 = indirectStore.optimize(use_NO_VARIATION_INDEX=False)
|
||||||
advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
|
advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
|
||||||
advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
|
advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
|
||||||
|
|
||||||
@ -608,7 +608,7 @@ def _add_BASE(font, masterModel, master_ttfs, axisTags):
|
|||||||
merger.mergeTables(font, master_ttfs, ['BASE'])
|
merger.mergeTables(font, master_ttfs, ['BASE'])
|
||||||
store = merger.store_builder.finish()
|
store = merger.store_builder.finish()
|
||||||
|
|
||||||
if not store.VarData:
|
if not store:
|
||||||
return
|
return
|
||||||
base = font['BASE'].table
|
base = font['BASE'].table
|
||||||
assert base.Version == 0x00010000
|
assert base.Version == 0x00010000
|
||||||
@ -623,7 +623,7 @@ def _merge_OTL(font, model, master_fonts, axisTags):
|
|||||||
|
|
||||||
merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
|
merger.mergeTables(font, master_fonts, ['GSUB', 'GDEF', 'GPOS'])
|
||||||
store = merger.store_builder.finish()
|
store = merger.store_builder.finish()
|
||||||
if not store.VarData:
|
if not store:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
GDEF = font['GDEF'].table
|
GDEF = font['GDEF'].table
|
||||||
|
@ -633,6 +633,7 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits):
|
|||||||
for major, deltas in enumerate(defaultDeltaArray)
|
for major, deltas in enumerate(defaultDeltaArray)
|
||||||
for minor, delta in enumerate(deltas)
|
for minor, delta in enumerate(deltas)
|
||||||
}
|
}
|
||||||
|
defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0
|
||||||
return defaultDeltas
|
return defaultDeltas
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,6 +7,10 @@ from functools import partial
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
NO_VARIATION_INDEX = 0xFFFFFFFF
|
||||||
|
ot.VarStore.NO_VARIATION_INDEX = NO_VARIATION_INDEX
|
||||||
|
|
||||||
|
|
||||||
def _getLocationKey(loc):
|
def _getLocationKey(loc):
|
||||||
return tuple(sorted(loc.items(), key=lambda kv: kv[0]))
|
return tuple(sorted(loc.items(), key=lambda kv: kv[0]))
|
||||||
|
|
||||||
@ -135,6 +139,11 @@ def VarRegion_get_support(self, fvar_axes):
|
|||||||
|
|
||||||
ot.VarRegion.get_support = VarRegion_get_support
|
ot.VarRegion.get_support = VarRegion_get_support
|
||||||
|
|
||||||
|
def VarStore___bool__(self):
|
||||||
|
return bool(self.VarData)
|
||||||
|
|
||||||
|
ot.VarStore.__bool__ = VarStore___bool__
|
||||||
|
|
||||||
class VarStoreInstancer(object):
|
class VarStoreInstancer(object):
|
||||||
|
|
||||||
def __init__(self, varstore, fvar_axes, location={}):
|
def __init__(self, varstore, fvar_axes, location={}):
|
||||||
@ -169,6 +178,7 @@ class VarStoreInstancer(object):
|
|||||||
|
|
||||||
def __getitem__(self, varidx):
|
def __getitem__(self, varidx):
|
||||||
major, minor = varidx >> 16, varidx & 0xFFFF
|
major, minor = varidx >> 16, varidx & 0xFFFF
|
||||||
|
if varidx == NO_VARIATION_INDEX: return 0.
|
||||||
varData = self._varData
|
varData = self._varData
|
||||||
scalars = [self._getScalar(ri) for ri in varData[major].VarRegionIndex]
|
scalars = [self._getScalar(ri) for ri in varData[major].VarRegionIndex]
|
||||||
deltas = varData[major].Item[minor]
|
deltas = varData[major].Item[minor]
|
||||||
@ -431,7 +441,7 @@ class _EncodingDict(dict):
|
|||||||
return chars
|
return chars
|
||||||
|
|
||||||
|
|
||||||
def VarStore_optimize(self):
|
def VarStore_optimize(self, use_NO_VARIATION_INDEX=True):
|
||||||
"""Optimize storage. Returns mapping from old VarIdxes to new ones."""
|
"""Optimize storage. Returns mapping from old VarIdxes to new ones."""
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
@ -455,6 +465,10 @@ def VarStore_optimize(self):
|
|||||||
row[regionIdx] += v
|
row[regionIdx] += v
|
||||||
row = tuple(row)
|
row = tuple(row)
|
||||||
|
|
||||||
|
if use_NO_VARIATION_INDEX and not any(row):
|
||||||
|
front_mapping[(major<<16)+minor] = None
|
||||||
|
continue
|
||||||
|
|
||||||
encodings.add_row(row)
|
encodings.add_row(row)
|
||||||
front_mapping[(major<<16)+minor] = row
|
front_mapping[(major<<16)+minor] = row
|
||||||
|
|
||||||
@ -537,9 +551,9 @@ def VarStore_optimize(self):
|
|||||||
back_mapping[item] = (major<<16)+minor
|
back_mapping[item] = (major<<16)+minor
|
||||||
|
|
||||||
# Compile final mapping.
|
# Compile final mapping.
|
||||||
varidx_map = {}
|
varidx_map = {NO_VARIATION_INDEX:NO_VARIATION_INDEX}
|
||||||
for k,v in front_mapping.items():
|
for k,v in front_mapping.items():
|
||||||
varidx_map[k] = back_mapping[v]
|
varidx_map[k] = back_mapping[v] if v is not None else NO_VARIATION_INDEX
|
||||||
|
|
||||||
# Remove unused regions.
|
# Remove unused regions.
|
||||||
self.prune_regions()
|
self.prune_regions()
|
||||||
|
@ -316,7 +316,7 @@ class ParserTest(unittest.TestCase):
|
|||||||
def test_strict_glyph_name_check(self):
|
def test_strict_glyph_name_check(self):
|
||||||
self.parse("@bad = [a b ccc];", glyphNames=("a", "b", "ccc"))
|
self.parse("@bad = [a b ccc];", glyphNames=("a", "b", "ccc"))
|
||||||
|
|
||||||
with self.assertRaisesRegex(FeatureLibError, "missing from the glyph set: ccc"):
|
with self.assertRaisesRegex(FeatureLibError, "(?s)missing from the glyph set:.*ccc"):
|
||||||
self.parse("@bad = [a b ccc];", glyphNames=("a", "b"))
|
self.parse("@bad = [a b ccc];", glyphNames=("a", "b"))
|
||||||
|
|
||||||
def test_glyphclass(self):
|
def test_glyphclass(self):
|
||||||
|
@ -360,7 +360,7 @@ class InstantiateHVARTest(object):
|
|||||||
assert support == pytest.approx(expectedRegion[axisTag])
|
assert support == pytest.approx(expectedRegion[axisTag])
|
||||||
|
|
||||||
assert len(varStore.VarData) == 1
|
assert len(varStore.VarData) == 1
|
||||||
assert varStore.VarData[0].ItemCount == 2
|
assert varStore.VarData[0].ItemCount == 1
|
||||||
|
|
||||||
assert hvar.AdvWidthMap is not None
|
assert hvar.AdvWidthMap is not None
|
||||||
advWithMap = hvar.AdvWidthMap.mapping
|
advWithMap = hvar.AdvWidthMap.mapping
|
||||||
@ -368,9 +368,7 @@ class InstantiateHVARTest(object):
|
|||||||
assert advWithMap[".notdef"] == advWithMap["space"]
|
assert advWithMap[".notdef"] == advWithMap["space"]
|
||||||
varIdx = advWithMap[".notdef"]
|
varIdx = advWithMap[".notdef"]
|
||||||
# these glyphs have no metrics variations in the test font
|
# these glyphs have no metrics variations in the test font
|
||||||
assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == (
|
assert varIdx == varStore.NO_VARIATION_INDEX
|
||||||
[0] * varStore.VarData[0].VarRegionCount
|
|
||||||
)
|
|
||||||
|
|
||||||
varIdx = advWithMap["hyphen"]
|
varIdx = advWithMap["hyphen"]
|
||||||
assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == expectedDeltas
|
assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == expectedDeltas
|
||||||
@ -458,6 +456,8 @@ class InstantiateItemVariationStoreTest(object):
|
|||||||
|
|
||||||
defaultDeltaArray = []
|
defaultDeltaArray = []
|
||||||
for varidx, delta in sorted(defaultDeltas.items()):
|
for varidx, delta in sorted(defaultDeltas.items()):
|
||||||
|
if varidx == varStore.NO_VARIATION_INDEX:
|
||||||
|
continue
|
||||||
major, minor = varidx >> 16, varidx & 0xFFFF
|
major, minor = varidx >> 16, varidx & 0xFFFF
|
||||||
if major == len(defaultDeltaArray):
|
if major == len(defaultDeltaArray):
|
||||||
defaultDeltaArray.append([])
|
defaultDeltaArray.append([])
|
||||||
|
@ -13,7 +13,7 @@ from fontTools.ttLib.tables.otTables import VarStore
|
|||||||
(
|
(
|
||||||
[{}, {"a": 1}],
|
[{}, {"a": 1}],
|
||||||
[
|
[
|
||||||
[10, 20],
|
[10, 10], # Test NO_VARIATION_INDEX
|
||||||
[100, 2000],
|
[100, 2000],
|
||||||
[100, 22000],
|
[100, 22000],
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user