Merge pull request #1551 from fonttools/VVAR-support
[varLib] Add support for building VVAR table from vmtx and VORG tables.
This commit is contained in:
commit
e782c6e9d2
@ -463,46 +463,120 @@ def _merge_TTHinting(font, masterModel, master_ttfs, tolerance=0.5):
|
||||
var = TupleVariation(support, delta)
|
||||
cvar.variations.append(var)
|
||||
|
||||
def _add_HVAR(font, masterModel, master_ttfs, axisTags):
|
||||
MetricsFields = namedtuple('MetricsFields',
|
||||
['tableTag', 'metricsTag', 'sb1', 'sb2', 'advMapping', 'vOrigMapping'])
|
||||
|
||||
log.info("Generating HVAR")
|
||||
hvarFields = MetricsFields(tableTag='HVAR', metricsTag='hmtx', sb1='LsbMap',
|
||||
sb2='RsbMap', advMapping='AdvWidthMap', vOrigMapping=None)
|
||||
|
||||
vvarFields = MetricsFields(tableTag='VVAR', metricsTag='vmtx', sb1='TsbMap',
|
||||
sb2='BsbMap', advMapping='AdvHeightMap', vOrigMapping='VOrgMap')
|
||||
|
||||
def _add_HVAR(font, masterModel, master_ttfs, axisTags):
|
||||
_add_VHVAR(font, masterModel, master_ttfs, axisTags, hvarFields)
|
||||
|
||||
def _add_VVAR(font, masterModel, master_ttfs, axisTags):
|
||||
_add_VHVAR(font, masterModel, master_ttfs, axisTags, vvarFields)
|
||||
|
||||
def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields):
|
||||
|
||||
tableTag = tableFields.tableTag
|
||||
assert tableTag not in font
|
||||
log.info("Generating " + tableTag)
|
||||
VHVAR = newTable(tableTag)
|
||||
tableClass = getattr(ot, tableTag)
|
||||
vhvar = VHVAR.table = tableClass()
|
||||
vhvar.Version = 0x00010000
|
||||
|
||||
glyphOrder = font.getGlyphOrder()
|
||||
|
||||
hAdvanceDeltasAndSupports = {}
|
||||
metricses = [m["hmtx"].metrics for m in master_ttfs]
|
||||
for glyph in glyphOrder:
|
||||
hAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in metricses]
|
||||
hAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(hAdvances)
|
||||
# Build list of source font advance widths for each glyph
|
||||
metricsTag = tableFields.metricsTag
|
||||
advMetricses = [m[metricsTag].metrics for m in master_ttfs]
|
||||
|
||||
singleModel = models.allEqual(id(v[1]) for v in hAdvanceDeltasAndSupports.values())
|
||||
# Build list of source font vertical origin coords for each glyph
|
||||
if tableTag == 'VVAR' and 'VORG' in master_ttfs[0]:
|
||||
vOrigMetricses = [m['VORG'].VOriginRecords for m in master_ttfs]
|
||||
defaultYOrigs = [m['VORG'].defaultVertOriginY for m in master_ttfs]
|
||||
vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
|
||||
else:
|
||||
vOrigMetricses = None
|
||||
|
||||
metricsStore, advanceMapping, vOrigMapping = _get_advance_metrics(font,
|
||||
masterModel, master_ttfs, axisTags, glyphOrder, advMetricses,
|
||||
vOrigMetricses)
|
||||
|
||||
vhvar.VarStore = metricsStore
|
||||
if advanceMapping is None:
|
||||
setattr(vhvar, tableFields.advMapping, None)
|
||||
else:
|
||||
setattr(vhvar, tableFields.advMapping, advanceMapping)
|
||||
if vOrigMapping is not None:
|
||||
setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
|
||||
setattr(vhvar, tableFields.sb1, None)
|
||||
setattr(vhvar, tableFields.sb2, None)
|
||||
|
||||
font[tableTag] = VHVAR
|
||||
return
|
||||
|
||||
def _get_advance_metrics(font, masterModel, master_ttfs,
|
||||
axisTags, glyphOrder, advMetricses, vOrigMetricses=None):
|
||||
|
||||
vhAdvanceDeltasAndSupports = {}
|
||||
vOrigDeltasAndSupports = {}
|
||||
for glyph in glyphOrder:
|
||||
vhAdvances = [metrics[glyph][0] if glyph in metrics else None for metrics in advMetricses]
|
||||
vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vhAdvances)
|
||||
|
||||
singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values())
|
||||
|
||||
if vOrigMetricses:
|
||||
singleModel = False
|
||||
for glyph in glyphOrder:
|
||||
# We need to supply a vOrigs tuple with non-None default values
|
||||
# for each glyph. vOrigMetricses contains values only for those
|
||||
# glyphs which have a non-default vOrig.
|
||||
vOrigs = [metrics[glyph] if glyph in metrics else defaultVOrig
|
||||
for metrics, defaultVOrig in vOrigMetricses]
|
||||
vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(vOrigs)
|
||||
|
||||
directStore = None
|
||||
if singleModel:
|
||||
# Build direct mapping
|
||||
|
||||
supports = next(iter(hAdvanceDeltasAndSupports.values()))[1][1:]
|
||||
supports = next(iter(vhAdvanceDeltasAndSupports.values()))[1][1:]
|
||||
varTupleList = builder.buildVarRegionList(supports, axisTags)
|
||||
varTupleIndexes = list(range(len(supports)))
|
||||
varData = builder.buildVarData(varTupleIndexes, [], optimize=False)
|
||||
for glyphName in glyphOrder:
|
||||
varData.addItem(hAdvanceDeltasAndSupports[glyphName][0])
|
||||
varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0])
|
||||
varData.optimize()
|
||||
directStore = builder.buildVarStore(varTupleList, [varData])
|
||||
|
||||
# Build optimized indirect mapping
|
||||
storeBuilder = varStore.OnlineVarStoreBuilder(axisTags)
|
||||
mapping = {}
|
||||
advMapping = {}
|
||||
for glyphName in glyphOrder:
|
||||
deltas,supports = hAdvanceDeltasAndSupports[glyphName]
|
||||
deltas, supports = vhAdvanceDeltasAndSupports[glyphName]
|
||||
storeBuilder.setSupports(supports)
|
||||
mapping[glyphName] = storeBuilder.storeDeltas(deltas)
|
||||
advMapping[glyphName] = storeBuilder.storeDeltas(deltas)
|
||||
|
||||
if vOrigMetricses:
|
||||
vOrigMap = {}
|
||||
for glyphName in glyphOrder:
|
||||
deltas, supports = vOrigDeltasAndSupports[glyphName]
|
||||
storeBuilder.setSupports(supports)
|
||||
vOrigMap[glyphName] = storeBuilder.storeDeltas(deltas)
|
||||
|
||||
indirectStore = storeBuilder.finish()
|
||||
mapping2 = indirectStore.optimize()
|
||||
mapping = [mapping2[mapping[g]] for g in glyphOrder]
|
||||
advanceMapping = builder.buildVarIdxMap(mapping, glyphOrder)
|
||||
advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
|
||||
advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
|
||||
|
||||
use_direct = False
|
||||
if vOrigMetricses:
|
||||
vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder]
|
||||
|
||||
useDirect = False
|
||||
vOrigMapping = None
|
||||
if directStore:
|
||||
# Compile both, see which is more compact
|
||||
|
||||
@ -515,20 +589,17 @@ def _add_HVAR(font, masterModel, master_ttfs, axisTags):
|
||||
advanceMapping.compile(writer, font)
|
||||
indirectSize = len(writer.getAllData())
|
||||
|
||||
use_direct = directSize < indirectSize
|
||||
useDirect = directSize < indirectSize
|
||||
|
||||
# Done; put it all together.
|
||||
assert "HVAR" not in font
|
||||
HVAR = font["HVAR"] = newTable('HVAR')
|
||||
hvar = HVAR.table = ot.HVAR()
|
||||
hvar.Version = 0x00010000
|
||||
hvar.LsbMap = hvar.RsbMap = None
|
||||
if use_direct:
|
||||
hvar.VarStore = directStore
|
||||
hvar.AdvWidthMap = None
|
||||
if useDirect:
|
||||
metricsStore = directStore
|
||||
advanceMapping = None
|
||||
else:
|
||||
hvar.VarStore = indirectStore
|
||||
hvar.AdvWidthMap = advanceMapping
|
||||
metricsStore = indirectStore
|
||||
if vOrigMetricses:
|
||||
vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder)
|
||||
|
||||
return metricsStore, advanceMapping, vOrigMapping
|
||||
|
||||
def _add_MVAR(font, masterModel, master_ttfs, axisTags):
|
||||
|
||||
@ -840,6 +911,8 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True):
|
||||
_add_MVAR(vf, model, master_fonts, axisTags)
|
||||
if 'HVAR' not in exclude:
|
||||
_add_HVAR(vf, model, master_fonts, axisTags)
|
||||
if 'VVAR' not in exclude and 'vmtx' in vf:
|
||||
_add_VVAR(vf, model, master_fonts, axisTags)
|
||||
if 'GDEF' not in exclude or 'GPOS' not in exclude:
|
||||
_merge_OTL(vf, model, master_fonts, axisTags)
|
||||
if 'gvar' not in exclude and 'glyf' in vf:
|
||||
|
@ -469,11 +469,6 @@ class CFF2CharStringMergePen(T2CharStringPen):
|
||||
arg[0] = round_func(arg[0])
|
||||
program.append(arg[0])
|
||||
for arg in blendlist:
|
||||
# for each coordinate tuple, append the region deltas
|
||||
if len(arg) != 3:
|
||||
print(arg)
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
deltas = var_model.getDeltas(arg)
|
||||
if round_func:
|
||||
deltas = [round_func(delta) for delta in deltas]
|
||||
|
36
Tests/varLib/data/TestVVAR.designspace
Normal file
36
Tests/varLib/data/TestVVAR.designspace
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<designspace format="3">
|
||||
<axes>
|
||||
<axis default="0" maximum="1000" minimum="0" name="weight" tag="wght" />
|
||||
</axes>
|
||||
<sources>
|
||||
<source filename="master_vvar_cff2/TestVVAR.0.ufo" stylename="w0.00">
|
||||
<info copy="1" />
|
||||
<location>
|
||||
<dimension name="weight" xvalue="0.00" />
|
||||
</location>
|
||||
</source>
|
||||
<source filename="master_vvar_cff2/TestVVAR.1.ufo" stylename="w1000.00">
|
||||
|
||||
<location>
|
||||
<dimension name="weight" xvalue="1000.00" />
|
||||
</location>
|
||||
</source>
|
||||
</sources>
|
||||
<instances>
|
||||
<instance familyname="TestVVAR" filename="instances/TestVVAR-ExtraLight.otf" postscriptfontname="TestVVAR-ExtraLight" stylename="ExtraLight">
|
||||
<location>
|
||||
<dimension name="weight" xvalue="0" />
|
||||
</location>
|
||||
<kerning />
|
||||
<info />
|
||||
</instance>
|
||||
<instance familyname="TestVVAR" filename="instances/TestVVAR-Heavy.otf" postscriptfontname="TestVVAR-Heavy" stylename="Heavy">
|
||||
<location>
|
||||
<dimension name="weight" xvalue="1000" />
|
||||
</location>
|
||||
<kerning />
|
||||
<info />
|
||||
</instance>
|
||||
</instances>
|
||||
</designspace>
|
BIN
Tests/varLib/data/master_vvar_cff2/TestVVAR.0.otf
Normal file
BIN
Tests/varLib/data/master_vvar_cff2/TestVVAR.0.otf
Normal file
Binary file not shown.
BIN
Tests/varLib/data/master_vvar_cff2/TestVVAR.1.otf
Normal file
BIN
Tests/varLib/data/master_vvar_cff2/TestVVAR.1.otf
Normal file
Binary file not shown.
59
Tests/varLib/data/test_results/TestVVAR.ttx
Normal file
59
Tests/varLib/data/test_results/TestVVAR.ttx
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="OTTO" ttLibVersion="3.39">
|
||||
|
||||
<VVAR>
|
||||
<Version value="0x00010000"/>
|
||||
<VarStore Format="1">
|
||||
<Format value="1"/>
|
||||
<VarRegionList>
|
||||
<!-- RegionAxisCount=1 -->
|
||||
<!-- RegionCount=1 -->
|
||||
<Region index="0">
|
||||
<VarRegionAxis index="0">
|
||||
<StartCoord value="0.0"/>
|
||||
<PeakCoord value="1.0"/>
|
||||
<EndCoord value="1.0"/>
|
||||
</VarRegionAxis>
|
||||
</Region>
|
||||
</VarRegionList>
|
||||
<!-- VarDataCount=1 -->
|
||||
<VarData index="0">
|
||||
<!-- ItemCount=1 -->
|
||||
<NumShorts value="0"/>
|
||||
<!-- VarRegionCount=0 -->
|
||||
<Item index="0" value="[]"/>
|
||||
</VarData>
|
||||
</VarStore>
|
||||
<AdvHeightMap>
|
||||
<Map glyph=".notdef" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00007" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00008" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00009" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00010" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00011" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00012" outer="0" inner="0"/>
|
||||
<Map glyph="uni3042" outer="0" inner="0"/>
|
||||
<Map glyph="uni56FD" outer="0" inner="0"/>
|
||||
<Map glyph="uni6280" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF20" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF21" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF41" outer="0" inner="0"/>
|
||||
</AdvHeightMap>
|
||||
<VOrgMap>
|
||||
<Map glyph=".notdef" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00007" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00008" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00009" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00010" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00011" outer="0" inner="0"/>
|
||||
<Map glyph="glyph00012" outer="0" inner="0"/>
|
||||
<Map glyph="uni3042" outer="0" inner="0"/>
|
||||
<Map glyph="uni56FD" outer="0" inner="0"/>
|
||||
<Map glyph="uni6280" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF20" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF21" outer="0" inner="0"/>
|
||||
<Map glyph="uniFF41" outer="0" inner="0"/>
|
||||
</VOrgMap>
|
||||
</VVAR>
|
||||
|
||||
</ttFont>
|
@ -442,6 +442,20 @@ class BuildTest(unittest.TestCase):
|
||||
mvar_tags = [vr.ValueTag for vr in varfont["MVAR"].table.ValueRecord]
|
||||
assert all(tag in mvar_tags for tag in fontTools.varLib.mvar.MVAR_ENTRIES)
|
||||
|
||||
def test_varlib_build_VVAR_CFF2(self):
|
||||
ds_path = self.get_test_input('TestVVAR.designspace')
|
||||
suffix = '.otf'
|
||||
expected_ttx_name = 'TestVVAR'
|
||||
tables = ["VVAR"]
|
||||
|
||||
finder = lambda s: s.replace('.ufo', suffix)
|
||||
varfont, model, _ = build(ds_path, finder)
|
||||
varfont = reload_font(varfont)
|
||||
|
||||
expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx')
|
||||
self.expect_ttx(varfont, expected_ttx_path, tables)
|
||||
self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
|
||||
|
||||
|
||||
def test_load_masters_layerName_without_required_font():
|
||||
ds = DesignSpaceDocument()
|
||||
|
Loading…
x
Reference in New Issue
Block a user