)EjidZddlmZddlmZddlmZddlmZ ddl m Z ddl m Z mZddlmZd d lmZmZd'd Zd ZdZdZdZdZd'dZdZdZGdde Zee jddZ ee j!e j"fddZ d(dZ#dZ$dZ%dZ&dZ'd Z(d!Z)d"Z*d#Z+d$Z,e-d%kr,dd&l.Z.dd&l/Z/e/j0e.j1j2d&Sd&S))zModule to build FeatureVariation tables: https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table NOTE: The API is experimental and subject to change. )hashdict bit_count)newTable)otTables) TTVisitor) buildLookupbuildSingleSubstSubtable) OrderedDict) VarLibErrorVarLibValidationErrorrvrnc t|tr|gnt|}d|vpt|dk}t t ||t|}t|\}}d|vrt|d<nAt|dj  |}|rtd|t|dj || g}|D](\} }|| fd|Df)t!||dj |||dx} t%d| j| _dSdS) acAdd conditional substitutions to a Variable Font. The `conditionalSubstitutions` argument is a list of (Region, Substitutions) tuples. A Region is a list of Boxes. A Box is a dict mapping axisTags to (minValue, maxValue) tuples. Irrelevant axes may be omitted and they are interpretted as extending to end of axis in each direction. A Box represents an orthogonal 'rectangular' subset of an N-dimensional design space. A Region represents a more complex subset of an N-dimensional design space, ie. the union of all the Boxes in the Region. For efficiency, Boxes within a Region should ideally not overlap, but functionality is not compromised if they do. The minimum and maximum values are expressed in normalized coordinates. A Substitution is a dict mapping source glyph names to substitute glyph names. Example: # >>> f = TTFont(srcPath) # >>> condSubst = [ # ... # A list of (Region, Substitution) tuples. # ... ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}), # ... ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}), # ... ] # >>> addFeatureVariations(f, condSubst) # >>> f.save(dstPath) The `featureTag` parameter takes either a str or a iterable of str (the single str is kept for backwards compatibility), and defines which feature(s) will be associated with the feature variations. Note, if this is "rvrn", then the substitution lookup will be inserted at the beginning of the lookup list so that it is processed before others, otherwise for any other feature tags it will be appended last. rr ) glyphNames substitutionsGSUBz4FeatureVariations already exist for feature tag(s): c g|] }| Sr).0s lookupMaps NC:\PYTHON\MyICR_Suite\python\Lib\site-packages\fontTools/varLib/featureVars.py z(addFeatureVariations..]s@@@QIaL@@@zOS/2N) isinstancestrsortedlen_checkSubstitutionGlyphsExistset getGlyphOrderoverlayFeatureVariationsmakeSubstitutionsHashable buildGSUB_existingVariableFeaturestable intersectionr buildSubstitutionLookupsappendaddFeatureVariationsRawgetmax usMaxContext) fontconditionalSubstitutions featureTag featureTags processLastrallSubstitutions existingTagsconditionsAndLookups conditionSetos2rs @raddFeatureVariationsr9sN#-Z"="=U:,,6*CUCUK +Cs;/?/?!/CK!t))++,,. --EFFM2K22..T {{V 0f1CDDQQ     U|UU  ) V ,kI'?  # m## @@@@-@@@ A    D$v,"46JKXXXxx,q#"233-,rct}t|drV|jO|jj}|jjD]6}|jjD]'}|||j j (7|S)NFeatureVariations) r!hasattrr; FeatureList FeatureRecordFeatureVariationRecordFeatureTableSubstitutionSubstitutionRecordadd FeatureIndex FeatureTag)r'existingFeatureVarsTagsfeaturesfvrftsrs rr&r&ks!eeu)**Tu/F/R$2*A T TC4G T T'++HT5F,G,RSSSS T ""rct}|D]@\}}||z}|t|z}A||z }|r%tdd|dS)NzAMissing glyphs are referenced in conditional substitution rules: z, )r!keysvaluesrjoin)rrreferencedGlyphNames_ substitutionmissings rr r us55(;;< 1 1 3 33L$7$7$9$9 : ::"Z/G # % '"" % %     rc t}|D]9\}}t|}||vr|||4|||<:d|D}~t}t |D]`\}}t t d|Dd}||vr|||Nt|||<att |}~tdff}t|}t|D]\}\}}t|} d|z} |D]z\} } |D]r} t| | \}}|.t|}| |d| z| z| |<|+t|}| |d| z| |<s{| }g}t |dD]h\} } | dkr g}d}| r2| dzr!| ||d| dz} |dz }| 2| t| |fi|S) aCompute overlaps between all conditional substitutions. The `conditionalSubstitutions` argument is a list of (Region, Substitutions) tuples. A Region is a list of Boxes. A Box is a dict mapping axisTags to (minValue, maxValue) tuples. Irrelevant axes may be omitted and they are interpretted as extending to end of axis in each direction. A Box represents an orthogonal 'rectangular' subset of an N-dimensional design space. A Region represents a more complex subset of an N-dimensional design space, ie. the union of all the Boxes in the Region. For efficiency, Boxes within a Region should ideally not overlap, but functionality is not compromised if they do. The minimum and maximum values are expressed in normalized coordinates. A Substitution is a dict mapping source glyph names to substitute glyph names. Returns data is in similar but different format. Overlaps of distinct substitution Boxes (*not* Regions) are explicitly listed as distinct rules, and rules with the same Box merged. The more specific rules appear earlier in the resulting list. Moreover, instead of just a dictionary of substitutions, a list of dictionaries is returned for substitutions corresponding to each unique space, with each dictionary being identical to one of the input substitution dictionaries. These dictionaries are not merged to allow data sharing when they are converted into font tables. Example:: >>> condSubst = [ ... # A list of (Region, Substitution) tuples. ... ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}), ... ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}), ... ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}), ... ([{"wght": (0.5, 1.0), "wdth": (-1, 1.0)}], {"dollar": "dollar.rvrn"}), ... ] >>> from pprint import pprint >>> pprint(overlayFeatureVariations(condSubst)) [({'wdth': (0.5, 1.0), 'wght': (0.5, 1.0)}, [{'dollar': 'dollar.rvrn'}, {'cent': 'cent.rvrn'}]), ({'wdth': (0.5, 1.0)}, [{'cent': 'cent.rvrn'}]), ({'wght': (0.5, 1.0)}, [{'dollar': 'dollar.rvrn'}])] c6g|]\}}|t|fSr)dict)rkvs rrz,overlayFeatureVariations..s&HHHADGG HHHrc3NK|] }tt|V!dSN)r cleanupBox)rrTs r z+overlayFeatureVariations..s066Q*Q--((666666rc^tt|SrW)tupleritems)ds rz*overlayFeatureVariations..seF17799$5$566r)keyrr Nc.t|d S)Nr r) BoxAndRanks rr^z*overlayFeatureVariations..s:a=1I1I0Ir)r rextendr\reversedr[rupdaterSlist enumerate overlayBoxr,r*)r0mergedvaluer_ initMapInitboxMapi currRegionrNnewMapcurrRankboxrankcurrBoxr( remainderr\ substsLists rr#r#s^]]F.  ssmm &== 3K  u % % % %F3KKHHHHH]]F788 & & U 66#66666      &== 3K  u % % % %u++F3KK#HV\\^^$<$<== JJ?$K  % %F'(@AA  ?J[))6 H HIC% H H*4Wc*B*B' i+#+L#9#9L+1::lA+F+F+MPX+XF<(( ( 3 3I(. 9a(@(@4(GF9% H E II . . T 199   ax B!!":1"=a"@AAA QJD FA    d3ii,---- Lrcvi}||||t|t|zD]K}||\}}||\}}t||}t||} || ksd|fcS|| f||<Lt |} d} d} |D] }||vrd} d} |D]u}||vr||\}}||\}}||kr||kr*| r||fcSd} d} ||krt||}|} n||kr|}t||} n||fcS|| f| |<v| r|dfS|| fS)aIOverlays ``top`` box on top of ``bot`` box. Returns two items: * Box for intersection of ``top`` and ``bot``, or None if they don't intersect. * Box for remainder of ``bot``. Remainder box might not be exact (since the remainder might not be a simple box), but is inclusive of the exact remainder. NFT)rdr!r-minrS) topbotr(axisTagmin1max1min2max2minimummaximumrs extruding fullyInsides rrgrgsLs88c#hh&11\ d\ ddD//dD//  9    ' 0 WS IIK c>>     . . #   !'* d\ d 4<d|DS)zReturn a sparse copy of `box`, without redundant (default) values. >>> cleanupBox({}) {} >>> cleanupBox({'wdth': (0.0, 1.0)}) {'wdth': (0.0, 1.0)} >>> cleanupBox({'wdth': (-1.0, 1.0)}) {} c&i|]\}}|dk ||S))gg?r)rtaglimits r zcleanupBox.._s( M M M:38L8LC8L8L8Lr)r\)rps rrXrXTs! N M M M MMrct|tr|gnt|dvptdk}|jdkrd|_t }fd|jjDt z }|rug}t|D]F}t|g}|jj || |Gt|jj|j_ t||D]}|jj |} |j jD]} | jj;t#j} d| _d| _g| _d| _ | | j_d| jjD} | jjg| zD]5} | j | t| j| _ 6|| r8|fd t3|jjDd t3|d jD} t7|d o|jdu}g}|D]R\}}g}t|D]I\}\}}||krt=d t?| |||}| |Jg}t|D]M} |jj| j j!}|r||zn||z}| tE| |N|r[tG|j|x}rD|j$j%&|t|j$j%|j$_'/| tQ||T|rr|jjdkrtSd|jjdd|jj*&|t|jj*|j_+dStY||_dS)z{Low level implementation of addFeatureVariations that directly models the possibilities of the FeatureVariations table.rr c0h|]}|jv |jSrrD)rfeaturer2s r z*addFeatureVariationsRaw..}s4    , ,  , , ,rNrcg|] }|j Sr)LangSys)rlsrs rrz+addFeatureVariationsRaw..sXXXss{XXXrc34K|]\}}|jv|VdSrWr)rindexrr5s rrYz*addFeatureVariationsRaw..sA! ! w!\11 1111! ! rc$i|] \}}|j|Sr)ry)r axisIndexaxiss rrz+addFeatureVariationsRaw..s-$3It irfvarr;zbuildFeatureRecordr* FeatureCountsortFeatureListr ScriptList ScriptRecordScriptDefaultLangSysotr LookupOrderReqFeatureIndexrC LangSysRecordrBrdrfaxesr<r;r\rbuildConditionTableFeatureLookupListIndex#buildFeatureTableSubstitutionRecordfindFeatureVariationRecordr@rArbSubstitutionCountbuildFeatureVariationRecordr r?FeatureVariationCountbuildFeatureVariations)r/r'r0r1r3varFeatureIndicesnewTags varFeatures varFeaturevarFeatureIndex scriptRecordlangSys langSystems axisIndiceshasFeatureVariationsfeatureVariationRecordsr7 lookupIndicesconditionTableryminValuemaxValuectrecordsexistingLookupIndicescombinedLookupIndicesrGr5r2s @@rr+r+gs:#-Z"="=U:,,6*CUCUK +Cs;/?/?!/CK }z!!" (6L +-G3  // + +J+J;;J   + 2 2: > > >   z * * * *),U->-L)M)M&% 3 3J#/=CCJOOO % 0 = E E &5= jllG*.G'.4G++-G(+,G(9@L'6XXl6I6WXXX , 3 BCkQEEG(//@@@+.w/C+D+DG((E  ! !/ 2 2 2 2    ! ! ! ! "+E,=,K"L"L! ! !    7@fAR7S7SK *++S0Gt0S!'?### m-3L4F4F4H4H-I-I & & )G)h(""+R%[%98XNNB  ! !" % % % %%&788  O$)$5$C%o " ;% 55"%:: " NN3#%:     -e.E~VV VC   ( ; B B7 K K K=@,?>>C ( : : $ * *+NGDD     R  " *j 8 8R,4QRRR  6==>UVVV8;  # :9 9 555#99P"Q"Qrctd}tjx}|_d|_tj|_g|j_tj|_g|j_tj |_ g|j _ tj}d|_ tj |_ d|j _ g|j _d|j _tj}tj|_d|j_g|j_|j|j _ |jj|d|j_d|_|S)z Build a GSUB table from scratch.rrDFLTNrrr )rrrr'rrrr=r> LookupListLookup ScriptTagrrr LangSysCountrrrCr* ScriptCountr;) fontTablegsubsreclangrecs rr%r%s  IWYY&D9?DLmooDO#%DO ~''D%'D"mooDODO ?  DDN)++DK!%DK "DK DK  GjllGO&,GO##%GO !(DKO ''---"#DO!D rc@t}g}|D]{\}}g}|D]Z}tt|}||||[|||f||t|fS)zTurn all the substitution dictionaries in sorted tuples of tuples so they are hashable, to detect duplicates so we don't write out redundant data.)r!r[rr\r*rB)r0r4 condSubstr7substitutionMapsrsubstitutionMapsubsts rr$r$ suuI*B88& & / ( (O&!6!6!8!899::E   ' ' '   ' ' ' ', 67777 f-.. ..rceZdZdZdS)ShifterVisitorc||_dSrW)shift)selfrs r__init__zShifterVisitor.__init__s  rN)__name__ __module__ __qualname__rrrrrrs#rrrcT|jfd|D}t|||dS)Ncg|]}|zSrr)rlrs rrzvisit..#s & & &1QY & & &r)rsetattr)visitorobjattrrirs @rvisitr s; ME & & & & & & &E Curc8t|||j|zdSrW)rr)rrrris rrr's# Cw}u,-----rFc|rt|jjnd}i}t|D] \}}||z||<|s\t|}t |}||jj||jjt|D]\}} t| } tt| g} |r |jj | n |jj || |jj|| | usJt|jj|j_ |S)zlBuild the lookups for the glyph substitutions, return a dict mapping the substitution to lookup indices.r)rrrrfrrr=r>rSr r r*insert LookupCount) rr4r3 firstIndexrrlrrrrsubstMaplookups rr)r).sf1<BT_+,,,JI'(89944?%/!^ /"" .$%% '' d&4555 do,---.//BB5;;6x@@ABB  5 O " ) )& 1 1 1 1 O " ) )!V 4 4 4%i&676AAAAA"%do&<"="=DO rcptj}d|_||_t ||_|S)z%Build the FeatureVariations subtable.r)rr;rr?rr)rfvs rrrMs5   BBJ 7B"#:;;B Irctj}||_tj|_||j_|j|S)zBuild a FeatureRecord.)rr>rDrrpopulateDefaults)r1lookupListIndicesfrs rrrVsG   BBMBJ!2BJJ!!! Ircntj}t|dkr>tj|_||j_t||j_nd|_tj|_d|j_||j_t||j_ |S)zBuild a FeatureVariationRecord.rNr) rr?r ConditionSetConditionTableConditionCountr@rrAr)rsubstitutionRecordsrGs rrr`s # % %C >a?,,*8'*-n*=*=''#%#>#@#@C +5C (6IC 3589L5M5MC 2 Jrctj}||_tj|_||j_t ||j_|S)z'Build a FeatureTableSubstitutionRecord.)rFeatureTableSubstitutionRecordrCrrrr) featureIndexrrHs rrrpsG , . .D$D:<.s5 E3 $rcg|]\}}}| Srrrrrrs rrz#sortFeatureList..s&N&N&NsE3s&N&N&Nrcg|]\}}}| Srrrs rrz#sortFeatureList..s 6 6 6sE3U 6 6 6rN) rfr=r>sortrSzipranger remapFeatures)r' tagIndexFea featureRemaps rrrs #E$5$CDDK&N&N+&N&N&NE# 6 6+ 6 6 6c+>N>N8O8OPPL %&&&&&rct|jjD]Y\}}|jj}|t ||t|jjD]\}}|j}t ||Zt|dr7|j 2|j j D]'}|j j D]} || j | _ $dSdSdS)z7Go through the scripts list, and remap feature indices.Nr;)rfrrrr _remapLangSysrrr<r;r?r@rArC) r'r scriptIndexscriptdefaultLangSyslangSysRecordIndex langSysRecrrGrHs rr r s ()9)FGG11 V5  % ., 7 7 7.7 8S.T.T 1 1 *   (G '< 0 0 0 0 1u)**Du/F/R*A D DC4G D D$01B$C!! DDD/R/R D Drcr|jdkr|j|_fd|jD|_dS)Nrc g|] }| Srr)rrrs rrz!_remapLangSys..sRRREL/RRRr)rrC)rrs `rrrsF&((".w/F"GRRRRW=QRRRGr__main__N)r)F)3__doc__fontTools.misc.dictToolsrfontTools.misc.intToolsrfontTools.ttLibrfontTools.ttLib.tablesrrfontTools.ttLib.ttVisitorrfontTools.otlLib.builderr r collectionsr errorsr rr9r&r r#rgrXr+r%r$r register_attrrrSubstLookupRecordPosLookupRecordr)rrrrrrrr rrdoctestsysexittestmodfailedrrrr*s .-----------$$$$$$111111//////JJJJJJJJ######66666666V4V4V4V4r###    pppzR#R#R#j N N N&{R{R{R{RFB / / / Y bj*;<<=< 2-.0A...>       '''& D D D SSS  z CH_W_   %&&&&&r