From b756bcac187b2699ee528391755d6161514a88ac Mon Sep 17 00:00:00 2001 From: ReadRoberts Date: Wed, 12 Jun 2019 14:30:30 -0700 Subject: [PATCH 1/7] [varLib.merger] Fix merging GPOS SinglePos subtables When a SinglePost lookup is compiled, pretty much all compilers do some compression by combining similar ValueRecords into a single subtable. This compression produces different lengths of the list of subtables between source fonts. The original code required that all SinglePos lookups have the same subtables, often doesn't work. I fixed this in varLib.merger.py by first flattening a SinglePos lookup to a single subtable, using the same record format for all records, and then merging the source fonts. After the merge is complete, I call fontTools.otlLib.builder.buildSinglePos() to rebuild the lookup subtables. --- Lib/fontTools/varLib/merger.py | 62 ++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index b4b170594..ff1321e5b 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -2,6 +2,7 @@ Merge OpenType Layout tables (GDEF / GPOS / GSUB). """ from __future__ import print_function, division, absolute_import +from operator import ior from fontTools.misc.py23 import * from fontTools.misc.fixedTools import otRound from fontTools.misc import classifyTools @@ -12,6 +13,7 @@ from fontTools.varLib import builder, models, varStore from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo from fontTools.varLib.varStore import VarStoreInstancer from functools import reduce +from fontTools.otlLib.builder import buildSinglePos class Merger(object): @@ -726,6 +728,31 @@ def _Lookup_PairPos_subtables_canonicalize(lst, font): return lst +def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format): + lst = list(lst) + glyphs, _ = _merge_GlyphOrders(font, + [v.Coverage.glyphs for v in lst], None) + num_glyphs = len(glyphs) + new = ot.SinglePos() + new.Format = 2 + new.ValueFormat = min_inclusive_rec_format + new.Coverage = ot.Coverage() + new.Coverage.glyphs = glyphs + new.ValueCount = num_glyphs + new.Value = [None] * num_glyphs + for singlePos in lst: + if singlePos.Format == 1: + val_rec = singlePos.Value + for gname in singlePos.Coverage.glyphs: + i = glyphs.index(gname) + new.Value[i] = val_rec + elif singlePos.Format == 2: + for j, gname in enumerate(singlePos.Coverage.glyphs): + val_rec = singlePos.Value[j] + i = glyphs.index(gname) + new.Value[i] = val_rec + return [new] + @AligningMerger.merger(ot.Lookup) def merge(merger, self, lst): subtables = merger.lookup_subtables = [l.SubTable for l in lst] @@ -743,19 +770,37 @@ def merge(merger, self, lst): sts.extend(new_sts) isPairPos = self.SubTable and isinstance(self.SubTable[0], ot.PairPos) - + isSinglePos = False + if isPairPos: - # AFDKO and feaLib sometimes generate two Format1 subtables instead of one. # Merge those before continuing. # https://github.com/fonttools/fonttools/issues/719 self.SubTable = _Lookup_PairPos_subtables_canonicalize(self.SubTable, merger.font) subtables = merger.lookup_subtables = [_Lookup_PairPos_subtables_canonicalize(st, merger.font) for st in subtables] - + else: + isSinglePos = self.SubTable and isinstance(self.SubTable[0], ot.SinglePos) + if isSinglePos: + numSubtables = [len(st) for st in subtables] + if not all([nums == numSubtables[0] for nums in numSubtables]): + # Flatten list of SinglePos subtables to single Format 2 subtable, + # with all value records set to the rec format type. + # We use buildSinglePos() to optimize the lookup after merging. + valueFormatList = [t.ValueFormat for st in subtables for t in st] + # Find the minimum value record that can accomodate all the singlePos subtables. + mirf = reduce(ior, valueFormatList) + self.SubTable = _Lookup_SinglePos_subtables_flatten(self.SubTable, merger.font, mirf) + subtables = merger.lookup_subtables = [ + _Lookup_SinglePos_subtables_flatten(st, merger.font, mirf) for st in subtables] + flattened = True + else: + flattened = False + merger.mergeLists(self.SubTable, subtables) self.SubTableCount = len(self.SubTable) if isPairPos: + isSinglePos = False # If format-1 subtable created during canonicalization is empty, remove it. assert len(self.SubTable) >= 1 and self.SubTable[0].Format == 1 if not self.SubTable[0].Coverage.glyphs: @@ -768,6 +813,17 @@ def merge(merger, self, lst): self.SubTable.pop(-1) self.SubTableCount -= 1 + if isSinglePos: + singlePosTable = self.SubTable[0] + glyphs = singlePosTable.Coverage.glyphs + if flattened: + # We know that singlePosTable is Format 2, as this is set + # in _Lookup_SinglePos_subtables_flatten. + recs = singlePosTable.Value + numRecs = len(recs) + recList = [ (glyphs[i], recs[i]) for i in range(numRecs)] + singlePosMapping = {gname: valRecord for gname, valRecord in recList} + self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap()) merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount']) del merger.lookup_subtables From b44c761526776343cee40ccfb7a5f09c2f6ba9a9 Mon Sep 17 00:00:00 2001 From: ReadRoberts Date: Wed, 12 Jun 2019 14:31:38 -0700 Subject: [PATCH 2/7] [oltLib.builder] Fix bug in building a variation font. The logic for building a Device subtable didn't know about the VariationIndex flavor of a ValueRecord Device subtable. --- Lib/fontTools/otlLib/builder.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py index f8b3ce3b0..1d5e7b6ee 100644 --- a/Lib/fontTools/otlLib/builder.py +++ b/Lib/fontTools/otlLib/builder.py @@ -479,7 +479,10 @@ def _getSinglePosValueKey(valueRecord): valueFormat, result = 0, [] for name, value in valueRecord.__dict__.items(): if isinstance(value, ot.Device): - result.append((name, _makeDeviceTuple(value))) + if value.DeltaFormat & 0x8000: + result.append((name, _makeVariationInstance(value))) + else: + result.append((name, _makeDeviceTuple(value))) else: result.append((name, value)) valueFormat |= valueRecordFormatDict[name][0] @@ -493,12 +496,20 @@ def _makeDeviceTuple(device): return (device.DeltaFormat, device.StartSize, device.EndSize, tuple(device.DeltaValue)) +def _makeVariationInstance(device): + """otTables.Device --> tuple, for making device tables unique""" + return (device.DeltaFormat, device.StartSize, device.EndSize) + def _getSinglePosValueSize(valueKey): """Returns how many ushorts this valueKey (short form of ValueRecord) takes up""" count = 0 for k in valueKey[1:]: if hasattr(k[1], '__len__') and len(k[1]): - count += len(k[1][3]) + 3 + # it is a device table key + if k[1][0] & 0x8000: + count += 3 + else: + count += len(k[1][3]) + 3 else: count += 1 return count From 73501eda46ec83313db7898deabcdf87eec435e0 Mon Sep 17 00:00:00 2001 From: ReadRoberts Date: Wed, 12 Jun 2019 14:32:25 -0700 Subject: [PATCH 3/7] [varLib.merger] Add test data for merging GPOS SinglePos subtables --- .../master_vpal_test/master_vpal_test_0.ttx | 485 ++++++++++++++++++ .../master_vpal_test/master_vpal_test_1.ttx | 473 +++++++++++++++++ Tests/varLib/data/test_results/test_vpal.ttx | 120 +++++ Tests/varLib/data/test_vpal.designspace | 35 ++ Tests/varLib/varLib_test.py | 23 + 5 files changed, 1136 insertions(+) create mode 100644 Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx create mode 100644 Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx create mode 100644 Tests/varLib/data/test_results/test_vpal.ttx create mode 100644 Tests/varLib/data/test_vpal.designspace diff --git a/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx b/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx new file mode 100644 index 000000000..1fbb45d35 --- /dev/null +++ b/Tests/varLib/data/master_vpal_test/master_vpal_test_0.ttx @@ -0,0 +1,485 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved. + + + MasterSet_KanjiFullEX + + + Regular + + + 1.004;UKWN;MasterSet_KanjiFullEX-w0.00 + + + MasterSet_KanjiFullEX + + + Version 1.004;hotconv 1.0.109;makeotfexe 2.5.65596 + + + MasterSet_KanjiFullEX-w0.00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -282 endchar + + + 291 -43 rmoveto + 29 24 rlineto + -74 84 -84 83 -73 58 rrcurveto + -25 -24 rlineto + 72 -56 87 -85 68 -84 rrcurveto + endchar + + + 706 699 rmoveto + -32 -10 rlineto + 50 -192 56 -132 112 -115 rrcurveto + 24 26 rlineto + -143 124 -45 182 -22 117 rrcurveto + -592 -57 rmoveto + 3 -38 rlineto + 18 2 18 3 17 2 rrcurveto + 43 6 101 12 63 13 rrcurveto + -83 -80 -128 -166 0 -210 rrcurveto + 0 -150 92 -88 154 0 rrcurveto + 294 0 65 313 -48 302 rrcurveto + -29 79 rlineto + 64 -402 -96 -263 -249 0 rrcurveto + -109 0 -110 48 0 166 rrcurveto + 0 219 171 196 58 42 rrcurveto + 14 5 30 6 12 3 rrcurveto + -9 30 rlineto + -52 -21 -170 -26 -83 -3 rrcurveto + -18 -1 -21 0 -12 1 rrcurveto + 677 114 rmoveto + -26 -11 rlineto + 22 -34 33 -63 20 -42 rrcurveto + 30 14 rlineto + -21 41 -37 62 -21 33 rrcurveto + 97 40 rmoveto + -26 -13 rlineto + 24 -33 31 -59 21 -44 rrcurveto + 30 14 rlineto + -23 43 -36 60 -21 32 rrcurveto + endchar + + + 706 699 rmoveto + -32 -10 rlineto + 50 -192 56 -132 112 -115 rrcurveto + 24 26 rlineto + -143 124 -45 182 -22 117 rrcurveto + -592 -57 rmoveto + 3 -38 rlineto + 18 2 18 3 17 2 rrcurveto + 43 6 101 12 63 13 rrcurveto + -83 -80 -128 -166 0 -210 rrcurveto + 0 -150 92 -88 154 0 rrcurveto + 294 0 65 313 -48 302 rrcurveto + -29 79 rlineto + 64 -402 -96 -263 -249 0 rrcurveto + -109 0 -110 48 0 166 rrcurveto + 0 219 171 196 58 42 rrcurveto + 14 5 30 6 12 3 rrcurveto + -9 30 rlineto + -52 -21 -170 -26 -83 -3 rrcurveto + -18 -1 -21 0 -12 1 rrcurveto + 681 51 rmoveto + 0 42 33 33 41 0 rrcurveto + 41 0 33 -33 0 -42 rrcurveto + 0 -41 -33 -33 -41 0 rrcurveto + -42 0 -32 33 0 41 rrcurveto + -30 0 rmoveto + 0 -57 46 -47 58 0 rrcurveto + 57 0 48 47 0 57 rrcurveto + 0 58 -48 47 -57 0 rrcurveto + -58 0 -46 -47 0 -58 rrcurveto + endchar + + + 703 676 rmoveto + -28 0 rlineto + 0 -63 0 -106 0 -61 rrcurveto + 0 -103 14 -162 0 -52 rrcurveto + 0 -59 -17 -50 -97 0 rrcurveto + -91 0 -58 31 0 63 rrcurveto + 0 53 62 37 87 0 rrcurveto + 125 0 119 -56 94 -98 rrcurveto + 19 31 rlineto + -90 81 -113 69 -154 0 rrcurveto + -130 0 -47 -64 0 -53 rrcurveto + 0 -80 70 -42 105 0 rrcurveto + 85 0 58 36 0 74 rrcurveto + 0 68 -13 166 0 113 rrcurveto + 0 61 0 96 0 70 rrcurveto + -284 -213 rmoveto + 0 -31 rlineto + 168 -10 194 12 125 13 rrcurveto + 0 31 rlineto + -129 -15 -187 -13 -171 13 rrcurveto + 16 230 rmoveto + 0 -30 rlineto + 139 -7 200 11 108 11 rrcurveto + 0 30 rlineto + -111 -15 -197 -11 -139 11 rrcurveto + -207 51 rmoveto + -37 4 rlineto + 0 -11 -1 -14 -3 -19 rrcurveto + -13 -88 -37 -183 0 -143 rrcurveto + 0 -135 16 -105 20 -74 rrcurveto + 28 3 rlineto + -1 7 -2 12 -1 9 rrcurveto + -1 13 2 16 3 14 rrcurveto + 9 43 39 105 22 55 rrcurveto + -20 17 rlineto + -20 -48 -32 -89 -18 -56 rrcurveto + -10 78 -5 58 0 78 rrcurveto + 0 124 26 166 25 119 rrcurveto + 3 18 4 13 4 13 rrcurveto + endchar + + + 500 465 rmoveto + -47 0 -38 -38 0 -47 rrcurveto + 0 -47 38 -38 47 0 rrcurveto + 47 0 38 38 0 47 rrcurveto + 0 47 -38 38 -47 0 rrcurveto + endchar + + + 500 572 rmoveto + 28 0 28 19 0 37 rrcurveto + 0 37 -28 19 -28 0 rrcurveto + -28 0 -28 -19 0 -37 rrcurveto + 0 -37 28 -19 28 0 rrcurveto + 0 -502 rmoveto + 28 0 28 19 0 37 rrcurveto + 0 37 -28 19 -28 0 rrcurveto + -28 0 -28 -19 0 -37 rrcurveto + 0 -37 28 -19 28 0 rrcurveto + endchar + + + 180 0 rmoveto + 35 0 rlineto + 0 502 rlineto + 0 57 -1 74 -2 59 rrcurveto + 5 0 rlineto + 67 -160 rlineto + 196 -451 rlineto + 39 0 rlineto + 195 451 rlineto + 69 160 rlineto + 5 0 rlineto + -2 -59 -3 -74 0 -57 rrcurveto + 0 -502 rlineto + 37 0 rlineto + 0 726 rlineto + -59 0 rlineto + -191 -438 rlineto + -23 -54 -20 -55 -24 -55 rrcurveto + -4 0 rlineto + -24 55 -23 55 -23 54 rrcurveto + -190 438 rlineto + -59 0 rlineto + 0 -726 rlineto + endchar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx b/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx new file mode 100644 index 000000000..bb439586a --- /dev/null +++ b/Tests/varLib/data/master_vpal_test/master_vpal_test_1.ttx @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Copyright © 2018 Adobe systems Co., Ltd. All Rights Reserved. + + + MasterSet_KanjiFullEX + + + Regular + + + 1.004;UKWN;MasterSet_KanjiFullEX-w1000.00 + + + MasterSet_KanjiFullEX + + + Version 1.004;hotconv 1.0.109;makeotfexe 2.5.65596 + + + MasterSet_KanjiFullEX-w1000.00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -455 endchar + + + 245 -76 rmoveto + 129 111 rlineto + -44 56 -100 103 -70 58 rrcurveto + -127 -109 rlineto + 69 -61 84 -86 59 -72 rrcurveto + endchar + + + 729 763 rmoveto + -152 -42 rlineto + 75 -225 86 -176 109 -123 rrcurveto + 97 140 rlineto + -155 146 -41 152 -19 128 rrcurveto + -677 -48 rmoveto + 11 -156 rlineto + 24 5 15 2 22 3 rrcurveto + 26 3 53 6 31 1 rrcurveto + -96 -130 -50 -109 0 -149 rrcurveto + 0 -190 149 -88 163 0 rrcurveto + 344 0 43 303 -58 260 rrcurveto + -142 223 rlineto + 96 -376 -59 -246 -221 0 rrcurveto + -92 0 -67 46 0 103 rrcurveto + 0 184 125 143 71 54 rrcurveto + 16 10 18 8 15 6 rrcurveto + -46 134 rlineto + -69 -25 -166 -20 -98 -5 rrcurveto + -19 -1 -20 0 -19 1 rrcurveto + 768 103 rmoveto + -88 -27 rlineto + 22 -45 16 -54 15 -49 rrcurveto + 87 28 rlineto + -11 41 -23 62 -18 44 rrcurveto + 110 37 rmoveto + -85 -28 rlineto + 21 -44 19 -57 14 -46 rrcurveto + 88 28 rlineto + -13 40 -22 63 -22 44 rrcurveto + endchar + + + 729 763 rmoveto + -152 -42 rlineto + 75 -225 86 -176 109 -123 rrcurveto + 97 140 rlineto + -155 146 -41 152 -19 128 rrcurveto + -677 -48 rmoveto + 11 -156 rlineto + 24 5 15 2 22 3 rrcurveto + 26 3 53 6 31 1 rrcurveto + -96 -130 -50 -109 0 -149 rrcurveto + 0 -190 149 -88 163 0 rrcurveto + 344 0 43 303 -58 260 rrcurveto + -142 223 rlineto + 96 -376 -59 -246 -221 0 rrcurveto + -92 0 -67 46 0 103 rrcurveto + 0 184 125 143 71 54 rrcurveto + 16 10 18 8 15 6 rrcurveto + -46 134 rlineto + -69 -25 -166 -20 -98 -5 rrcurveto + -19 -1 -20 0 -19 1 rrcurveto + 772 16 rmoveto + 0 27 22 22 27 0 rrcurveto + 27 0 22 -22 0 -27 rrcurveto + 0 -27 -22 -22 -27 0 rrcurveto + -27 0 -22 22 0 27 rrcurveto + -70 0 rmoveto + 0 -66 53 -53 66 0 rrcurveto + 66 0 53 53 0 66 rrcurveto + 0 66 -53 53 -66 0 rrcurveto + -66 0 -53 -53 0 -66 rrcurveto + endchar + + + 758 687 rmoveto + -141 0 rlineto + 0 -42 0 -111 0 -88 rrcurveto + 0 -104 8 -126 0 -63 rrcurveto + 0 -45 -23 -24 -49 0 rrcurveto + -37 0 -24 16 0 29 rrcurveto + 0 28 23 18 48 0 rrcurveto + 107 0 121 -81 97 -98 rrcurveto + 79 131 rlineto + -51 45 -135 115 -213 0 rrcurveto + -137 0 -76 -72 0 -97 rrcurveto + 0 -116 100 -52 119 0 rrcurveto + 142 0 55 65 0 93 rrcurveto + 0 97 -13 99 0 147 rrcurveto + 0 64 0 96 0 76 rrcurveto + -360 -167 rmoveto + 1 -139 rlineto + 195 -9 191 7 133 16 rrcurveto + 0 139 rlineto + -148 -18 -178 -11 -194 15 rrcurveto + 15 235 rmoveto + 0 -134 rlineto + 197 -9 162 8 126 14 rrcurveto + 0 134 rlineto + -127 -19 -163 -9 -195 15 rrcurveto + -117 26 rmoveto + -168 14 rlineto + -1 -37 -6 -47 -4 -29 rrcurveto + -11 -74 -26 -192 0 -153 rrcurveto + 0 -136 20 -117 21 -68 rrcurveto + 139 10 rlineto + -1 16 0 18 0 11 rrcurveto + 0 10 3 23 3 14 rrcurveto + 12 58 30 101 30 90 rrcurveto + -73 59 rlineto + -13 -30 -14 -23 -12 -29 rrcurveto + -1 4 0 20 0 3 rrcurveto + 0 93 33 237 12 52 rrcurveto + 4 18 14 62 9 22 rrcurveto + endchar + + + 500 520 rmoveto + -77 0 -63 -63 0 -77 rrcurveto + 0 -77 63 -63 77 0 rrcurveto + 77 0 63 63 0 77 rrcurveto + 0 77 -63 63 -77 0 rrcurveto + endchar + + + 500 500 rmoveto + 60 0 46 46 0 58 rrcurveto + 0 60 -46 46 -60 0 rrcurveto + -60 0 -46 -46 0 -60 rrcurveto + 0 -58 46 -46 60 0 rrcurveto + 0 -470 rmoveto + 60 0 46 46 0 58 rrcurveto + 0 60 -46 46 -60 0 rrcurveto + -60 0 -46 -46 0 -60 rrcurveto + 0 -58 46 -46 60 0 rrcurveto + endchar + + + 113 0 rmoveto + 160 0 rlineto + 0 255 rlineto + 0 75 -12 113 -9 74 rrcurveto + 4 0 rlineto + 72 -182 rlineto + 121 -272 rlineto + 98 0 rlineto + 120 272 rlineto + 73 182 rlineto + 5 0 rlineto + -10 -74 -12 -113 0 -75 rrcurveto + 0 -255 rlineto + 163 0 rlineto + 0 748 rlineto + -194 0 rlineto + -135 -325 rlineto + -17 -43 -18 -47 -18 -46 rrcurveto + -5 0 rlineto + -16 46 -19 47 -16 43 rrcurveto + -142 325 rlineto + -193 0 rlineto + 0 -748 rlineto + endchar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/test_results/test_vpal.ttx b/Tests/varLib/data/test_results/test_vpal.ttx new file mode 100644 index 000000000..334ced550 --- /dev/null +++ b/Tests/varLib/data/test_results/test_vpal.ttx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/data/test_vpal.designspace b/Tests/varLib/data/test_vpal.designspace new file mode 100644 index 000000000..a736927ed --- /dev/null +++ b/Tests/varLib/data/test_vpal.designspace @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index f3037339e..ddf23132b 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -273,6 +273,29 @@ class BuildTest(unittest.TestCase): self.expect_ttx(varfont, expected_ttx_path, tables) + def test_varlib_build_vpal(self): + ds_path = self.get_test_input('test_vpal.designspace') + ttx_dir = self.get_test_input("master_vpal_test") + expected_ttx_path = self.get_test_output("test_vpal.ttx") + + self.temp_dir() + for path in self.get_file_list(ttx_dir, '.ttx', 'master_vpal_test_'): + self.compile_font(path, ".otf", self.tempdir) + + ds = DesignSpaceDocument.fromfile(ds_path) + for source in ds.sources: + source.path = os.path.join( + self.tempdir, os.path.basename(source.filename).replace(".ufo", ".otf") + ) + ds.updatePaths() + + varfont, _, _ = build(ds) + varfont = reload_font(varfont) + + tables = ["GPOS"] + self.expect_ttx(varfont, expected_ttx_path, tables) + + def test_varlib_main_ttf(self): """Mostly for testing varLib.main() """ From 7f82d111568ea33df74a18f53c58ee92d12cbf3b Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 18 Jun 2019 16:00:39 +0100 Subject: [PATCH 4/7] otlLib: return namedtuple from _makeDeviceTuple makes code slightly more readable. --- Lib/fontTools/otlLib/builder.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py index 1d5e7b6ee..a087b2d3e 100644 --- a/Lib/fontTools/otlLib/builder.py +++ b/Lib/fontTools/otlLib/builder.py @@ -1,4 +1,5 @@ from __future__ import print_function, division, absolute_import +from collections import namedtuple from fontTools import ttLib from fontTools.ttLib.tables import otTables as ot from fontTools.ttLib.tables.otBase import ValueRecord, valueRecordFormatDict @@ -479,10 +480,7 @@ def _getSinglePosValueKey(valueRecord): valueFormat, result = 0, [] for name, value in valueRecord.__dict__.items(): if isinstance(value, ot.Device): - if value.DeltaFormat & 0x8000: - result.append((name, _makeVariationInstance(value))) - else: - result.append((name, _makeDeviceTuple(value))) + result.append((name, _makeDeviceTuple(value))) else: result.append((name, value)) valueFormat |= valueRecordFormatDict[name][0] @@ -491,25 +489,25 @@ def _getSinglePosValueKey(valueRecord): return tuple(result) +_DeviceTuple = namedtuple("_DeviceTuple", "DeltaFormat StartSize EndSize DeltaValue") + + def _makeDeviceTuple(device): """otTables.Device --> tuple, for making device tables unique""" - return (device.DeltaFormat, device.StartSize, device.EndSize, - tuple(device.DeltaValue)) + return _DeviceTuple( + device.DeltaFormat, + device.StartSize, + device.EndSize, + () if device.DeltaFormat & 0x8000 else tuple(device.DeltaValue) + ) -def _makeVariationInstance(device): - """otTables.Device --> tuple, for making device tables unique""" - return (device.DeltaFormat, device.StartSize, device.EndSize) def _getSinglePosValueSize(valueKey): """Returns how many ushorts this valueKey (short form of ValueRecord) takes up""" count = 0 - for k in valueKey[1:]: - if hasattr(k[1], '__len__') and len(k[1]): - # it is a device table key - if k[1][0] & 0x8000: - count += 3 - else: - count += len(k[1][3]) + 3 + for _, v in valueKey[1:]: + if isinstance(v, _DeviceTuple): + count += len(v.DeltaValue) + 3 else: count += 1 return count From def6310f4a81b4163d1dc8dc07a2e9b92735913e Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 18 Jun 2019 16:10:46 +0100 Subject: [PATCH 5/7] varLib.merger: when flattening singlepos, copy valuerecords to avoid modifying original master fonts' SinglePos tables https://github.com/fonttools/fonttools/pull/1641/files#r293302141 --- Lib/fontTools/varLib/merger.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index d77f5c7e4..7c3c6cfef 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -730,7 +730,6 @@ def _Lookup_PairPos_subtables_canonicalize(lst, font): return lst def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format): - lst = list(lst) glyphs, _ = _merge_GlyphOrders(font, [v.Coverage.glyphs for v in lst], None) num_glyphs = len(glyphs) @@ -746,12 +745,12 @@ def _Lookup_SinglePos_subtables_flatten(lst, font, min_inclusive_rec_format): val_rec = singlePos.Value for gname in singlePos.Coverage.glyphs: i = glyphs.index(gname) - new.Value[i] = val_rec + new.Value[i] = copy.deepcopy(val_rec) elif singlePos.Format == 2: for j, gname in enumerate(singlePos.Coverage.glyphs): val_rec = singlePos.Value[j] i = glyphs.index(gname) - new.Value[i] = val_rec + new.Value[i] = copy.deepcopy(val_rec) return [new] @AligningMerger.merger(ot.Lookup) From d6c2ea79b6fd6d6151c7870f15d1ca13bd6b6777 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 18 Jun 2019 16:21:33 +0100 Subject: [PATCH 6/7] varLib.merger: simplify if-stmt logic a bit isPairPos and isSinglePos are mutually exclusive and isSinglePos is only defined if isPairPos is False so it is safe to use it in 'else' or 'elif' branches (logical expressions short-circuit) --- Lib/fontTools/varLib/merger.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index 7c3c6cfef..c507f5c85 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -770,8 +770,7 @@ def merge(merger, self, lst): sts.extend(new_sts) isPairPos = self.SubTable and isinstance(self.SubTable[0], ot.PairPos) - isSinglePos = False - + if isPairPos: # AFDKO and feaLib sometimes generate two Format1 subtables instead of one. # Merge those before continuing. @@ -795,12 +794,11 @@ def merge(merger, self, lst): flattened = True else: flattened = False - + merger.mergeLists(self.SubTable, subtables) self.SubTableCount = len(self.SubTable) if isPairPos: - isSinglePos = False # If format-1 subtable created during canonicalization is empty, remove it. assert len(self.SubTable) >= 1 and self.SubTable[0].Format == 1 if not self.SubTable[0].Coverage.glyphs: @@ -813,17 +811,16 @@ def merge(merger, self, lst): self.SubTable.pop(-1) self.SubTableCount -= 1 - if isSinglePos: + elif isSinglePos and flattened: singlePosTable = self.SubTable[0] glyphs = singlePosTable.Coverage.glyphs - if flattened: - # We know that singlePosTable is Format 2, as this is set - # in _Lookup_SinglePos_subtables_flatten. - recs = singlePosTable.Value - numRecs = len(recs) - recList = [ (glyphs[i], recs[i]) for i in range(numRecs)] - singlePosMapping = {gname: valRecord for gname, valRecord in recList} - self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap()) + # We know that singlePosTable is Format 2, as this is set + # in _Lookup_SinglePos_subtables_flatten. + recs = singlePosTable.Value + numRecs = len(recs) + recList = [ (glyphs[i], recs[i]) for i in range(numRecs)] + singlePosMapping = {gname: valRecord for gname, valRecord in recList} + self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap()) merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount']) del merger.lookup_subtables From 4e4f37aa212b32e1414c93a5731b26312df427a6 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Tue, 18 Jun 2019 16:30:24 +0100 Subject: [PATCH 7/7] varLib.merger: use dict comprehension and zip iterator --- Lib/fontTools/varLib/merger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index c507f5c85..cd16ace45 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -816,10 +816,10 @@ def merge(merger, self, lst): glyphs = singlePosTable.Coverage.glyphs # We know that singlePosTable is Format 2, as this is set # in _Lookup_SinglePos_subtables_flatten. - recs = singlePosTable.Value - numRecs = len(recs) - recList = [ (glyphs[i], recs[i]) for i in range(numRecs)] - singlePosMapping = {gname: valRecord for gname, valRecord in recList} + singlePosMapping = { + gname: valRecord + for gname, valRecord in zip(glyphs, singlePosTable.Value) + } self.SubTable = buildSinglePos(singlePosMapping, merger.font.getReverseGlyphMap()) merger.mergeObjects(self, lst, exclude=['SubTable', 'SubTableCount'])