From 74f870f4eff8d722708e7ecb65f89891c2ac4f57 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 20 Mar 2024 15:36:32 -0600 Subject: [PATCH] [varc] Add ConditionSets --- Lib/fontTools/subset/__init__.py | 16 + Lib/fontTools/ttLib/tables/otData.py | 20 + Lib/fontTools/ttLib/tables/otTables.py | 14 +- Lib/fontTools/ttLib/ttGlyphSet.py | 22 + Tests/ttLib/data/varc-6868.ttf | Bin 9836 -> 8784 bytes Tests/ttLib/data/varc-ac00-ac01-500upem.ttx | 490 +++++++++----------- Tests/ttLib/data/varc-ac00-ac01.ttf | Bin 3284 -> 2748 bytes Tests/ttLib/tables/V_A_R_C_test.py | 12 +- Tests/ttLib/ttGlyphSet_test.py | 4 +- 9 files changed, 287 insertions(+), 291 deletions(-) diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index ed296ed65..c924d574b 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -2693,6 +2693,22 @@ def prune_post_subset(self, font, options): if comp.axisIndicesIndex is not None: comp.axisIndicesIndex = mapping[comp.axisIndicesIndex] + conditionSetList = table.ConditionSetList + if conditionSetList is not None: + conditionSets = conditionSetList.ConditionSet + usedIndices = set() + for glyph in table.VarCompositeGlyphs.VarCompositeGlyph: + for comp in glyph.components: + if comp.conditionSetIndex is not None: + usedIndices.add(comp.conditionSetIndex) + usedIndices = sorted(usedIndices) + conditionSetList.ConditionSet = _list_subset(conditionSets, usedIndices) + mapping = {old: new for new, old in enumerate(usedIndices)} + for glyph in table.VarCompositeGlyphs.VarCompositeGlyph: + for comp in glyph.components: + if comp.conditionSetIndex is not None: + comp.conditionSetIndex = mapping[comp.conditionSetIndex] + return True diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index cb7ad5de9..a2071b1f5 100644 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -3168,6 +3168,25 @@ otData = [ ), ], ), + ( + "ConditionSetList", + [ + ( + "uint32", + "ConditionSetCount", + None, + None, + "Number of condition-set tables in the ConditionSet array", + ), + ( + "LOffset", + "ConditionSet", + "ConditionSetCount", + 0, + "Array of condition-set tables.", + ), + ], + ), ( "ConditionSet", [ @@ -3377,6 +3396,7 @@ otData = [ ), ("LOffset", "Coverage", None, None, ""), ("LOffset", "MultiVarStore", None, None, "(may be NULL)"), + ("LOffset", "ConditionSetList", None, None, "(may be NULL)"), ("LOffset", "AxisIndicesList", None, None, "(may be NULL)"), ("LOffset", "VarCompositeGlyphs", None, None, ""), ], diff --git a/Lib/fontTools/ttLib/tables/otTables.py b/Lib/fontTools/ttLib/tables/otTables.py index 95fa83f8a..15797fbed 100644 --- a/Lib/fontTools/ttLib/tables/otTables.py +++ b/Lib/fontTools/ttLib/tables/otTables.py @@ -60,7 +60,7 @@ class VarComponentFlags(IntFlag): HAVE_TRANSLATE_Y = 1 << 5 HAVE_ROTATION = 1 << 6 - USE_MY_METRICS = 1 << 7 + HAVE_CONDITION = 1 << 7 HAVE_SCALE_X = 1 << 8 HAVE_SCALE_Y = 1 << 9 @@ -158,6 +158,7 @@ class VarComponent: def populateDefaults(self, propagator=None): self.flags = 0 self.glyphName = None + self.conditionSetIndex = None self.axisIndicesIndex = None self.axisValues = () self.axisValuesVarIndex = NO_VARIATION_INDEX @@ -174,6 +175,9 @@ class VarComponent: i += gidSize self.glyphName = font.glyphOrder[glyphID] + if flags & VarComponentFlags.HAVE_CONDITION: + self.conditionSetIndex, i = _read_uint32var(data, i) + if flags & VarComponentFlags.HAVE_AXES: self.axisIndicesIndex, i = _read_uint32var(data, i) else: @@ -244,6 +248,10 @@ class VarComponent: flags &= ~VarComponentFlags.GID_IS_24BIT data.append(_packer[2](glyphID)) + if self.conditionSetIndex is not None: + flags |= VarComponentFlags.HAVE_CONDITION + data.append(_write_uint32var(self.conditionSetIndex)) + numAxes = len(self.axisValues) if numAxes: @@ -293,6 +301,8 @@ class VarComponent: write("glyphName", self.glyphName) + if self.conditionSetIndex is not None: + write("conditionSetIndex", self.conditionSetIndex) if self.axisIndicesIndex is not None: write("axisIndicesIndex", self.axisIndicesIndex) if ( @@ -332,6 +342,8 @@ class VarComponent: if name == "glyphName": self.glyphName = v + elif name == "conditionSetIndex": + self.conditionSetIndex = safeEval(v) elif name == "axisIndicesIndex": self.axisIndicesIndex = safeEval(v) elif name == "axisValues": diff --git a/Lib/fontTools/ttLib/ttGlyphSet.py b/Lib/fontTools/ttLib/ttGlyphSet.py index 0e1bcd6d6..9772ae813 100644 --- a/Lib/fontTools/ttLib/ttGlyphSet.py +++ b/Lib/fontTools/ttLib/ttGlyphSet.py @@ -299,6 +299,28 @@ class _TTGlyphVARC(_TTGlyph): ) for comp in glyph.components: + + if comp.flags & VarComponentFlags.HAVE_CONDITION: + conditionSet = varc.ConditionSetList.ConditionSet[ + comp.conditionSetIndex + ] + # Evaluate condition + show = True + for condition in conditionSet.ConditionTable: + if condition.Format == 1: + axisIndex = condition.AxisIndex + axisTag = fvarAxes[axisIndex].axisTag + axisValue = self.glyphSet.location[axisTag] + minValue = condition.FilterRangeMinValue + maxValue = condition.FilterRangeMaxValue + if not (minValue <= axisValue <= maxValue): + show = False + break + else: + show = False # Unkonwn condition format + if not show: + continue + location = {} if comp.axisIndicesIndex is not None: axisIndices = varc.AxisIndicesList.Item[comp.axisIndicesIndex] diff --git a/Tests/ttLib/data/varc-6868.ttf b/Tests/ttLib/data/varc-6868.ttf index bb2c518b72e14b98e4e667857aa8f6c093e69b24..6041d6cc1f179e0354957e4c0a978e6e77281839 100644 GIT binary patch delta 2399 zcmZXWd2Ccw6o=2f_sz_krPF1m(@v*!Wfx>-N-2A%E0ri^Q3w#lp|%ugZ3|VXs8ehR z5@Jg7Tq03%Ax0B|n(9ADkXjUDL`X0pBqmBEQWRVgS)zvVcV`aJ#QS>Qd}n#*-FxTU z)46j0nfe3?BJxl_DT=MESzdSDc6|+zV=0mQL`!G9n>_8G5{X9_?Go+2J47? zej@qwhK|0r=DE$w;6DWa(GBn)Z1~_9QSLq@J=WeDe{5~I9=Yfn$gFl4l5oVtGBc(>QckH+sPClG<^KL7Cnuco*u4a0K$m%txS{Kc*^|e`Gy{zPk7L$P zmNMHYVbBCt>~KtBc7Ufdo8Snu6I{vc0?%P~gBLJ+z%|Sn;09(d_&(-L@B^B$evt)b z89TDU4>9L}S2O#-4>SA0apqibD{}z6kvR|C$(#@FVGe?yU@ibB`dA4;d4{p$q|@xMfzL7PZ}kUe z2lx`R3I3hg3BJMX0wWC{nf2b{^A0nTIgf{Q7US`7kBF^L^n;2F%>;40=Ea5b|J zyolKkZf4E}uVM~>*D>dTH!$aeyO@IsDBD;m04JD3;N8rH;6dhb;8&Q7zz4RpY}$-2 z)Uv8|IdFin!?Pp&}I$me)0t0CW_ zV)T)fIOvX4J?XL}Sr7S#$aWCt_|>6Jt}+okPRI3Q`Y=-r7s!($Ys6h+OL?uNJILho zR53Mp%B&ubKWyAdkap+<_Owl4>$IwSnO%r^4Qt~E-m#8(4ya`5fVIpUnBk!ew2(~7 zqHM}R@AIP>2Plv7(M$>`M1^Q1MN~{BXd-1)KJ>D;O=h}~*1&RYXeM@=Xmw`%ToFv! zY|wS}I?09Q{6D!)eJAnIjO+*HGGD=zszuA!v~>18OVTTqVY55jnZDdWupm@uOY0fZ z=5S_svvPd?+(3TNleU^pm)n!!+s*BiIwIMJ?Tbx5jP7wApnf^oQ-9LnnQhZ2U3Wb(B^)T$AiOgn45 zjq1{KF>$3oW^Ua%Of*a)StWan1FAw*$OKJ9Qp46*u`ACNT~hh>q{BIbqG_Nm|GK@+ z{o1)7}$aB zjg`>nD5B!z5tGz@TA&j+tV8|@Qkm9S|D=TJZ1hf0_2s)}?XLVO@Sff1kHu(&@F#A~ zmiPX;O%AG4>J+&Ka2j1r-t(d_BrBIz$iQX8l+$C>A9I`@Avr=yT$FWx?U6gw;emPS zSC5b(&d9mcBwTk*n5Ues+NcQ&cb2#ZM#w%w4siyB&?NU`vkCPjQAPMwLH$Et869$J z%36%dBbth0!BF})L}~~n^~jMq#%iO_`Y^B7`Z2!|GM{Dn7`|;A`?-|;B}RWH9kq^z zwpz`FjYFRlE*2`APFR1GueCOhUq5tp{6b-!Dlbt%`JE+;64qNqwU#QbwOS!3AyZ0f Zt-X+|kmk}_>!Z@zAz#@MVcj#a=06{ma1Q_g delta 2612 zcma)+Yiv|S6vxlZ+`Et4?e2ElExX-ryM0nx3ftZ73)F5~Ahlv7pcugbTck9kwS^L; zVBA7A1Y!bmgCB?nVvG?(5Unu=iD_-cC!tysgZM&;ROA!Uq%oxRfA>x+?kA_c_x#R( z&YYQZXLcr5+*+L^Mnn!8Bth}@&1+jvSHE?UNDC8XT;1A}=p#qh2SoPo5f$rvDzR%@ z&y^}7<$WS?VSD#LXCij*dLr9y^k3MH{!fcv>>u9<~I8A)bO=Xm|{m^HuxrUu+3x)c92^#OHwZ7OUDR`NEYB_k`-`^ zWEI>l*#ef6F)Mh3)Qzb+B-_C|Bx~SZk~6@2!AY!?Ip7?S9hu<6lAYjVl3m~vlHK4} zC1-)(lIZ3{x1#qY>{gMN@CX)<{1ht=SXB znKk2TrLB{;UfKp}&3tBb7^BR5G0bF)kI9*06m#Q1<3~lDmJ)tZG%{S8-OZEXaHMf| z7QWGp2x5ida5S8zg`m}>X+_W?X<9L~Xqr|6EtaO0LaT+AG-jNexC~-lT3|V}`ZR4J zw1zaT0$O?@i^AbpdLfn2?kc1uHG37r^hy>(ORuCFT6!f*^fRnd5AnsmY1}blN=#wv zsiue(iK*%E9iN!ezhM!5H4nOs&#Pf)xG^blebgIF=1?Wo(?hfgiqVhf+rvM6yxDcn zUnGzV?U}MyM&|5vc;+?bxW%}wL@E#B%_t+nY|~-mMnp6ELbDCH{oaPQPC0IFuo-1W z=v!E=USlnf8b_i4k*o1sMP`OS#ms>VPQ`ND@H~!;PF&xp&~j$@1#2;@CyL__(YUfD zsUtLkq0lVzq|8%Biu6xx&AP{)qlfJgz1tpqG;cm2IG+d$k}19Oz1b!)<11qZZoC;& z*=*6!-I~hS@|7DCC-qDAlWu%5DRd(|2k)OS7=*O!81ReiB|>Qz3<@h)Sq*_+71&`~9h8DiRsDwjIecD3wS*ss)Byo$GkLmRC{T z5Wj!j=H6$Ct6V6m#p*PT&8}rQGMz5B<3GOOLbd2O+>xY=%W!3TbNzXN{7kM|?U_!u zCp*WR>+_>t#n_$!`jKs6w}+ZYXmRJkLF#`%yu^F$hiv`iRwuP{)__&Bb!1L@n?{L7 zG2$ZK6emO!BRCseq0I0g>k_;vFiL!s#LP81rCd|aDovzh=zFsQj#1+P@Y$D#_K{UD z^5k$Vu-;RK<>;F{3#(NTub}ZfjGN07`OHBA0rwgHe1hkDknu$M0t|RF`td= zUu5}{W5maBsC)VA3Q`|QlB%{^HF3jo#Tc$m4#t&6q)^7NPwgz~xXwDoPBuwp2({Q& zIm^U~lIQCV*8ZCNhD8~S$7!7K&t@9 zc&#aa!t0Q>IL)8a|DQeT>qAZ>Hm(f1=#IX{-wz?APx@CGcG#cS&V*md(Vq(@Qyrq9 zC1qDidr;cXq|GQa`yZ6{2<&Af{7TyFP|6>EBy^tXiC{=%F-7kTY)mB`4RoYtEy=%+ J>8|4De*k~Gu#Nx# diff --git a/Tests/ttLib/data/varc-ac00-ac01-500upem.ttx b/Tests/ttLib/data/varc-ac00-ac01-500upem.ttx index 49b1696d6..9976b5bf7 100644 --- a/Tests/ttLib/data/varc-ac00-ac01-500upem.ttx +++ b/Tests/ttLib/data/varc-ac00-ac01-500upem.ttx @@ -58,12 +58,12 @@ - + - + - - + + @@ -94,31 +94,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -140,6 +116,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -161,6 +161,36 @@ + + rcjk + + + varc + + + Weight + + + OpticalSize + + + 0000 + + + 0001 + + + 0002 + + + 0003 + + + 0004 + + + 0005 + rcjk @@ -191,30 +221,6 @@ 0005 - - 0006 - - - 0007 - - - 0008 - - - 0009 - - - 0010 - - - 0011 - - - 0012 - - - 0013 - @@ -235,10 +241,10 @@ - - + + @@ -285,88 +291,88 @@ - - - - - - - + + + + + + + - - + + - + - + - + - + - + - - - - + + + + + + + - - - - - + + + + - - + + - - + + - + - - - - - + + + - + - + - - + + @@ -453,201 +459,12 @@ 1.0 263 - - - - 0006 - 0x0 - -1.0 - 0.0 - 1.0 - 264 - - - - - 0007 - 0x0 - -1.0 - 0.0 - 1.0 - 265 - - - - - 0008 - 0x0 - -1.0 - 0.0 - 1.0 - 266 - - - - - 0009 - 0x0 - -1.0 - 0.0 - 1.0 - 267 - - - - - 0010 - 0x0 - -1.0 - 0.0 - 1.0 - 268 - - - - - 0011 - 0x0 - -1.0 - 0.0 - 1.0 - 269 - - - - - 0012 - 0x0 - -1.0 - 0.0 - 1.0 - 270 - - - - - 0013 - 0x0 - -1.0 - 0.0 - 1.0 - 271 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1080,6 +897,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ttLib/data/varc-ac00-ac01.ttf b/Tests/ttLib/data/varc-ac00-ac01.ttf index 3134c0ad3eeaf37f9f23f9ac49fc69e9c2ee59c4..fb3a08780ef5b927454ff894cbb1819300d3a6c2 100644 GIT binary patch delta 1186 zcmYk4TS$~a6vxlZeAo5MHrdQ_wUt(~(D2#?EJl-vAc~5jC~8|TYigQGbRqn_JS0Uy z)Ab>`Kok{S=%t_siFybmD0(Qcdg-Bp>LHShaQlDXevxN5^Zm{_Gv~~AW?s*JJhP7& z5jo_NAi242!^&{Ui7F!Tk%%oxb|kvUxxES8h*j0i`xD*S@ePtlkHWs!(tfb{^!=~5 z5WffeXba-~@%RqJM~Sp=sis8Z?lUXzBLgpo_982X+bU*xp1(Q}wxBSBdyi_=dYWdwOwjh`=6-`9Fgn%Dp?I zaOd>epCrU5FFIAy6X`o*NX&yz5U<9^>i|^5(nu;kr6(vNhJ5eTecbnCQJA<|q~%7v z%)&b*1Y3w-&oFHxQm?g|S%Ha9Ax1?o8q^c9TwN6NPie({L=hSfDlzhU;++M#z*>gk zFvtp+Q5M?auVLPr*#Qf1LBQV5qJVX9ali(6LBJ-sj07%Uv*EIEHsLb|FUv5hI8+xf zqg+}Wu(u`|umHCOtbuz1*1?AZ_KrUoun9iao!rxgmy+C4($w0L>Rs2>+nP+YZ)`o( zRGyIvd@7+;&}wLny2+AWcZ9lP%w3S_Q6B7tUb7&Np)c_xCX}O;4zkErtMqk!q3md^ zbm?+ePCjy_abswJ!u@rVZ(-GI4QvJ*J~}{7zx-3{N_N(jk_P4ELsweESRER4+4$%H zvJ1v^a~kIY?bR^r2An2iX1Hi!?HXJ;UZD;Tp)T1g(BJy+M?}^8vGv5#}29wiX zrhg#B>)-9;C*P0tbAeEKE-qM(x~?qzrKNwbpb*u)*!;Q7MTtF=m6<1jM~B1_)Qqd` z{BnhVj}|r(mEui!a@?Q&iJirGl^MLkbB(AEM!p&`=BUd?w)*00NON;oRE?Ph%C@3$ z!}a(6o1_Q%D4Xw+&oD>SK`W}RSrPTziZ~@MT%Je-Y&)uI?1i)&?C;4zovl4#Q*>R delta 1491 zcmZ{jUuYCZ9LImNvpajYdzW02i-}1T4~-2NVJ|81(x$RW^HQrONU$XcdQFT$OiisV zTH4$u_96B~a7rzqg}#&uw)7u}f)D9K9txo#1d6Q&9~A<9C=FHQ*6-ZS-qC~3GCQB& z_xET1?cQl#+gcz-L{A2LMhN2@M+Bs_?%`J`~i3`R>~4E7PKP?{zP*f_^Rf5@RB^vCq_I{PhaWn z(LO!q^w?h-v)anyG*xsK8!L~!I2XeH4fHfpEttw2Tb&s9XV z8Q#&q~UEc`(DAsZ=X_{qEB-&<%bM*eCL_}l-b z@acYvv!Ms?7+2Qs&s#>gN#1^PXz;hAkM{ zT)c@ka1l+;LF0h&t1_1>_u`3^T<3OlKe9!%S6JQKA>qnDxT`f&9v063*=AtV3Aq^W zl)v*jc_ZE?@9_i@O}P@!@kX{GkBMy+?V{+Au1I40Bmy}oT2$FoJFcQVAYUyKEn253 ze-UZwG+bRV*oJ_(VoQ!X&5>VDI&VG)Dl{wp diff --git a/Tests/ttLib/tables/V_A_R_C_test.py b/Tests/ttLib/tables/V_A_R_C_test.py index 9ddc107f1..dcd35da8e 100644 --- a/Tests/ttLib/tables/V_A_R_C_test.py +++ b/Tests/ttLib/tables/V_A_R_C_test.py @@ -9,7 +9,7 @@ DATA_DIR = os.path.join(CURR_DIR, "data") class VarCompositeTest(unittest.TestCase): - def test_trim_varComposite_glyph(self): + def test_basic(self): font_path = os.path.join(DATA_DIR, "..", "..", "data", "varc-ac00-ac01.ttf") font = TTFont(font_path) varc = font["VARC"] @@ -18,10 +18,10 @@ class VarCompositeTest(unittest.TestCase): "uniAC00", "uniAC01", "glyph00003", - "glyph00004", "glyph00005", - "glyph00006", "glyph00007", + "glyph00008", + "glyph00009", ] font_path = os.path.join(DATA_DIR, "..", "..", "data", "varc-6868.ttf") @@ -31,11 +31,11 @@ class VarCompositeTest(unittest.TestCase): assert varc.table.Coverage.glyphs == [ "uni6868", "glyph00002", - "glyph00003", - "glyph00004", + "glyph00005", + "glyph00007", ] - def test_varComposite_basic(self): + def test_roundtrip(self): font_path = os.path.join(DATA_DIR, "..", "..", "data", "varc-ac00-ac01.ttf") font = TTFont(font_path) tables = [ diff --git a/Tests/ttLib/ttGlyphSet_test.py b/Tests/ttLib/ttGlyphSet_test.py index c9c5150d2..d4bc5ad85 100644 --- a/Tests/ttLib/ttGlyphSet_test.py +++ b/Tests/ttLib/ttGlyphSet_test.py @@ -226,7 +226,7 @@ class TTGlyphSetTest(object): ( "addVarComponent", ( - "glyph00007", + "glyph00003", DecomposedTransform( translateX=0, translateY=0, @@ -244,7 +244,7 @@ class TTGlyphSetTest(object): ( "addVarComponent", ( - "glyph00003", + "glyph00005", DecomposedTransform( translateX=0, translateY=0,