Merge remote-tracking branch 'origin/main' into variable-colr

This commit is contained in:
Cosimo Lupo 2022-06-24 10:16:51 +01:00
commit fcd2a67f52
11 changed files with 56 additions and 23 deletions

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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_()

View File

@ -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 = {}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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):

View File

@ -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([])

View File

@ -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],
], ],