""" Fix glyph anchors for Roboto VF. Script uses lxml since ufoLib rearranges data. The script will do the following: 1. Deletes anchors used for design puposes e.g making glyphs built with components 2. Renames anchors so no custom MarkWriterFeature is needed for fontmake 3. Ensures each mark glyph only has 1 anchor which starts with an '_' """ from __future__ import print_function import sys from lxml import etree import copy import os from glob import glob DUPLICATE_ANCHOR_GLYPHS = [ "A", "A", "A.smcp", "A.smcp", "A.unic", "A.unic", "AE", "AE", "AE.smcp", "AE.smcp", "B", "B", "B.smcp", "B.smcp", "C", "C", "C.smcp", "C.smcp", "D", "D", "D.smcp", "D.smcp", "E", "E", "E.smcp", "E.smcp", "E.unic", "E.unic", "Eng", "Eng", "Eng.smcp", "Eng.smcp", "Epsilon1", "Epsilon1", "Esh", "Esh", "F", "F", "F.smcp", "F.smcp", "G", "G", "G.smcp", "G.smcp", "Gamma", "Gamma", "Gamma.smcp", "Gamma.smcp", "Gbar", "Gbar", "Gsmallcap", "Gsmallcap", "Gsmallcaphook", "Gsmallcaphook", "H", "H", "H.smcp", "H.smcp", "Hbar", "Hbar", "Hbar.smcp", "Hbar.smcp", "I", "I", "I.smcp", "I.smcp", "Ibar", "Ibar", "Ismallcap", "Ismallcap", "J", "J", "J.smcp", "J.smcp", "K", "K", "K.smcp", "K.smcp", "L", "L", "L.smcp", "L.smcp", "Lslash", "Lslash", "Lslash.smcp", "Lslash.smcp", "Lsmallcap", "Lsmallcap", "M", "M", "M.smcp", "M.smcp", "M.unic", "M.unic", "N", "N", "N.smcp", "N.smcp", "N.unic", "N.unic", "Nsmallcap", "Nsmallcap", "O", "O", "O.smcp", "O.smcp", "OE", "OE", "OEsmallcap", "OEsmallcap", "Ohorn", "Ohorn", "Oslash", "Oslash", "Oslash.smcp", "Oslash.smcp", "P", "P", "P.smcp", "P.smcp", "Q", "Q", "Q.smcp", "Q.smcp", "R", "R", "R.alt", "R.alt", "R.smcp", "R.smcp", "Rsmallcap", "Rsmallcap", "Rsmallcapinv", "Rsmallcapinv", "S", "S", "S.smcp", "S.smcp", "Schwa", "Schwa", "T", "T", "T.smcp", "T.smcp", "Thorn.smcp", "Thorn.smcp", "U", "U", "U.smcp", "U.smcp", "U.unic", "U.unic", "Uhorn", "Uhorn", "V", "V", "V.smcp", "V.smcp", "W", "W", "W.smcp", "W.smcp", "X", "X", "X.smcp", "X.smcp", "Y", "Y", "Y.smcp", "Y.smcp", "Ysmallcap", "Ysmallcap", "Z", "Z", "Z.smcp", "Z.smcp", "Zbar", "Zbar", "a", "a", "a_uni02DE", "a_uni02DE", "ae", "ae", "alpha", "alpha", "ascript", "ascript", "ascriptturn", "ascriptturn", "aturn", "aturn", "b", "b", "babygamma", "babygamma", "bbar", "bbar", "bbar_uni1ABE", "bbar_uni1ABE", "bhook", "bhook", "bullseye", "bullseye", "c", "c", "ccurl", "ccurl", "cstretch", "cstretch", "d", "d", "dcroat", "dcroat", "dcroat_uni1ABE", "dcroat_uni1ABE", "dhook", "dhook", "dotlessi", "dotlessi", "drthook", "drthook", "dyogh", "dyogh", "dz", "dz", "dzcurl", "dzcurl", "e", "e", "eng", "eng", "eng_uni1ABE", "eng_uni1ABE", "epsilon", "epsilon", "epsilon1", "epsilon1", "epsilon1rev", "epsilon1rev", "epsilon1revclosed", "epsilon1revclosed", "epsilon1revhook", "epsilon1revhook", "epsilonclosed", "epsilonclosed", "erev", "erev", "esh", "esh", "eshcurl", "eshcurl", "eshshortrev", "eshshortrev", "eta", "eta", "f", "f", "finalkaf", "finalkaf", "finalmem", "finalmem", "finalnun", "finalnun", "g", "g", "gamma1", "gamma1", "gbar", "gbar", "gbar_uni1ABE", "gbar_uni1ABE", "gcursive", "gcursive", "ghook", "ghook", "glottalstop", "glottalstop", "glottalstopbar", "glottalstopbar", "glottalstopbarrev", "glottalstopbarrev", "glottalstopinv", "glottalstopinv", "glottalstoprevinv", "glottalstoprevinv", "h", "h", "henghook", "henghook", "hhook", "hhook", "hturn", "hturn", "i", "i", "ibar", "ibar", "iota", "iota", "iota1", "iota1", "j", "j", "jcrosstail", "jcrosstail", "jdotlessbar", "jdotlessbar", "jhookdblbar", "jhookdblbar", "k", "k", "k.alt", "k.alt", "kgreenlandic", "kgreenlandic", "kturn", "kturn", "l", "l", "lbar", "lbar", "lbelt", "lbelt", "lmidtilde", "lmidtilde", "longs", "longs", "lrthook", "lrthook", "lslash", "lslash", "lyogh", "lyogh", "m", "m", "mhook", "mhook", "mturn", "mturn", "mturndescend", "mturndescend", "n", "n", "nlfthook", "nlfthook", "nrthook", "nrthook", "o", "o", "oe", "oe", "ohorn", "ohorn", "omega", "omega", "omegaclosed", "omegaclosed", "oopen", "oopen", "oslash", "oslash", "p", "p", "philatin", "philatin", "psi", "psi", "q", "q", "qhook", "qhook", "r", "r", "rdescend", "rdescend", "rfishhook", "rfishhook", "rfishhookrev", "rfishhookrev", "rho", "rho", "rrthook", "rrthook", "rturn", "rturn", "rturnascend", "rturnascend", "rturnrthook", "rturnrthook", "s", "s", "schwa", "schwa", "schwahook", "schwahook", "srthook", "srthook", "t", "t", "tccurl", "tccurl", "tesh", "tesh", "trthook", "trthook", "ts", "ts", "tturn", "tturn", "u", "u", "ubar", "ubar", "uhorn", "uhorn", "uni0069.ccmp", "uni0069.ccmp", "uni006A.ccmp", "uni006A.ccmp", "uni012F.ccmp", "uni012F.ccmp", "uni0237", "uni0237", "uni023D", "uni023D", "uni0243", "uni0243", "uni0244", "uni0244", "uni0248", "uni0248", "uni0249", "uni0249", "uni0249.ccmp", "uni0249.ccmp", "uni024C", "uni024C", "uni024D", "uni024D", "uni024E", "uni024E", "uni024F", "uni024F", "uni0268.ccmp", "uni0268.ccmp", "uni0292", "uni0292", "uni0293", "uni0293", "uni029D.ccmp", "uni029D.ccmp", "uni02A9", "uni02A9", "uni02AB", "uni02AB", "uni02AC", "uni02AC", "uni02AE", "uni02AE", "uni02B2.ccmp", "uni02B2.ccmp", "uni03D2", "uni03D2", "uni03D6", "uni03D6", "uni03F3.ccmp", "uni03F3.ccmp", "uni0404", "uni0404", "uni0404.smcp", "uni0404.smcp", "uni0416", "uni0416", "uni0416.smcp", "uni0416.smcp", "uni0417", "uni0417", "uni0417.smcp", "uni0417.smcp", "uni0418", "uni0418", "uni0418.smcp", "uni0418.smcp", "uni0423", "uni0423", "uni0423.smcp", "uni0423.smcp", "uni0427", "uni0427", "uni0427.smcp", "uni0427.smcp", "uni042B", "uni042B", "uni042B.smcp", "uni042B.smcp", "uni042D", "uni042D", "uni042D.smcp", "uni042D.smcp", "uni042E", "uni042E", "uni042E.smcp", "uni042E.smcp", "uni042F", "uni042F", "uni042F.smcp", "uni042F.smcp", "uni0432", "uni0432", "uni0433", "uni0433", "uni0434", "uni0434", "uni0436", "uni0436", "uni0437", "uni0437", "uni0438", "uni0438", "uni043A", "uni043A", "uni043B", "uni043B", "uni043C", "uni043C", "uni043D", "uni043D", "uni043F", "uni043F", "uni0442", "uni0442", "uni0444", "uni0444", "uni0446", "uni0446", "uni0447", "uni0447", "uni0448", "uni0448", "uni0449", "uni0449", "uni044B", "uni044B", "uni044D", "uni044D", "uni044E", "uni044E", "uni044F", "uni044F", "uni0454", "uni0454", "uni0456.ccmp", "uni0456.ccmp", "uni0458.ccmp", "uni0458.ccmp", "uni0460", "uni0460", "uni0461", "uni0461", "uni046F", "uni046F", "uni0472", "uni0472", "uni0473", "uni0473", "uni0474", "uni0474", "uni0475", "uni0475", "uni047B", "uni047B", "uni047F", "uni047F", "uni0480", "uni0480", "uni0481", "uni0481", "uni048A", "uni048A", "uni048B", "uni048B", "uni04A8", "uni04A8", "uni04A8.smcp", "uni04A8.smcp", "uni04A9", "uni04A9", "uni04BA", "uni04BA", "uni04BA.smcp", "uni04BA.smcp", "uni04D8", "uni04D8", "uni04D8.smcp", "uni04D8.smcp", "uni04E8.smcp", "uni04E8.smcp", "uni0504", "uni0504", "uni0506", "uni0506", "uni050A", "uni050A", "uni050C", "uni050C", "uni050D", "uni050D", "uni050E", "uni050E", "uni0510", "uni0510", "uni1D00", "uni1D00", "uni1D01", "uni1D01", "uni1D02", "uni1D02", "uni1D03", "uni1D03", "uni1D04", "uni1D04", "uni1D05", "uni1D05", "uni1D06", "uni1D06", "uni1D07", "uni1D07", "uni1D08", "uni1D08", "uni1D09", "uni1D09", "uni1D0A", "uni1D0A", "uni1D0B", "uni1D0B", "uni1D0C", "uni1D0C", "uni1D0D", "uni1D0D", "uni1D0E", "uni1D0E", "uni1D0F", "uni1D0F", "uni1D10", "uni1D10", "uni1D14", "uni1D14", "uni1D15", "uni1D15", "uni1D18", "uni1D18", "uni1D19", "uni1D19", "uni1D1A", "uni1D1A", "uni1D1B", "uni1D1B", "uni1D1C", "uni1D1C", "uni1D20", "uni1D20", "uni1D21", "uni1D21", "uni1D22", "uni1D22", "uni1D23", "uni1D23", "uni1D24", "uni1D24", "uni1D25", "uni1D25", "uni1D26", "uni1D26", "uni1D27", "uni1D27", "uni1D28", "uni1D28", "uni1D29", "uni1D29", "uni1D2A", "uni1D2A", "uni1D2B", "uni1D2B", "uni1D62.ccmp", "uni1D62.ccmp", "uni1D6B", "uni1D6B", "uni1D6C", "uni1D6C", "uni1D6D", "uni1D6D", "uni1D6E", "uni1D6E", "uni1D6F", "uni1D6F", "uni1D70", "uni1D70", "uni1D71", "uni1D71", "uni1D72", "uni1D72", "uni1D73", "uni1D73", "uni1D74", "uni1D74", "uni1D75", "uni1D75", "uni1D76", "uni1D76", "uni1D77", "uni1D77", "uni1D79", "uni1D79", "uni1D7A", "uni1D7A", "uni1D7C", "uni1D7C", "uni1D7D", "uni1D7D", "uni1D7E", "uni1D7E", "uni1D7F", "uni1D7F", "uni1D80", "uni1D80", "uni1D81", "uni1D81", "uni1D82", "uni1D82", "uni1D83", "uni1D83", "uni1D84", "uni1D84", "uni1D85", "uni1D85", "uni1D86", "uni1D86", "uni1D87", "uni1D87", "uni1D88", "uni1D88", "uni1D89", "uni1D89", "uni1D8A", "uni1D8A", "uni1D8B", "uni1D8B", "uni1D8C", "uni1D8C", "uni1D8D", "uni1D8D", "uni1D8E", "uni1D8E", "uni1D8F", "uni1D8F", "uni1D90", "uni1D90", "uni1D91", "uni1D91", "uni1D92", "uni1D92", "uni1D93", "uni1D93", "uni1D94", "uni1D94", "uni1D95", "uni1D95", "uni1D96", "uni1D96", "uni1D96.ccmp", "uni1D96.ccmp", "uni1D97", "uni1D97", "uni1D98", "uni1D98", "uni1D99", "uni1D99", "uni1D9A", "uni1D9A", "uni1DA4.ccmp", "uni1DA4.ccmp", "uni1DA8.ccmp", "uni1DA8.ccmp", "uni1E2C", "uni1E2C", "uni1E2D", "uni1E2D", "uni1E2E", "uni1E2E", "uni1E2F", "uni1E2F", "uni2071.ccmp", "uni2071.ccmp", "uni2C7C.ccmp", "uni2C7C.ccmp", "upsilon", "upsilon", "upsilonlatin", "upsilonlatin", "v", "v", "vscript", "vscript", "vturn", "vturn", "w", "w", "wturn", "wturn", "x", "x", "y", "y", "yturn", "yturn", "z", "z", "zbar", "zbar", "zcurl", "zcurl", "zrthook", "zrthook" # mkmk "uni0351", "uni0350", "uni0357", "uni035B", "uni0343", "uni0342", "acuterightnosp", "graveleftnosp", "uni0346", "diaeresistonosnosp", "uni034A", "overscoredblnosp", "tildevertsupnosp", "xsupnosp", "anglesupnosp", "uni030C_uni1ABB", "dieresisnosp_uni1ABB", "uni034C", "tildecomb_uni1ABB", "uni0352", "uni034B", "macroncomb_uni1ABB", "tildecomb_uni1ABC", "tildevertsupnosp", "overscoredblnosp", "xsupnosp", "anglesupnosp", "uni0357", "uni0352", "uni0350", "uni0351", "uni035B", "uni0346", "diaeresistonosnosp", "acuterightnosp", "uni034C", "uni034B", "tildecomb_uni1ABC", "dieresisnosp_uni1ABB", "tildecomb_uni1ABB", "uni0342", "uni0343", "graveleftnosp", "uni030C_uni1ABB", "macroncomb_uni1ABB", "uni034A" ] def duplicate_anchor(glyph, src, dst): outline = glyph.find('outline') src_anc = glyph.xpath("//point[@name='{}']".format(src))[0] dst_anc = glyph.xpath("//point[@name='{}']".format(dst)) if not dst_anc: dst_anc = copy.deepcopy(src_anc.getparent()) dst_anc.getchildren()[0].attrib['name'] = dst outline.append(dst_anc) def rename_anchor(glyph, src, dst): anc = glyph.xpath("//point[@name='{}']".format(src)) if anc: anc[0].attrib['name'] = dst def delete_anchor(glyph, anc_name): anc = glyph.xpath("//point[@name='{}']".format(anc_name)) if anc: anc = anc[0].getparent() anc.getparent().remove(anc) def _delete_design_anchors(glyph): anchors_to_delete = [ "_ogonek", "_rhalfring", "_right", "_tonos", "_top", "_bottom", "_bottomu", "_caron", "_cross", "_dot", "_left", "ogonek", "rhalfring", "right", "tonos", "bottomu", "caron", "cross", "dot", "left" ] for anchor in anchors_to_delete: delete_anchor(glyph, anchor) def _rename_anchors(glyph): anchors = [ ("_marktop", "_top"), ("_markbottom", "_bottom"), ("_marktop_dd", "_top_dd"), ("_markbottom_dd", "_bottom_dd"), ("_markrhotichook", "_rhotichook"), ("_marktop0315", "_top0315"), ("_markparent_top", "_parent_top"), ("_markparenthesses.w1", "_parenthesses.w1"), ("_markparenthesses.w2", "_parenthesses.w2"), ("_markparenthesses.w3", "_parenthesses.w3"), ("mkmktop", "top"), ("mkmkbottom_acc", "bottom"), ] for before_anchor, after_anchor in anchors: rename_anchor(glyph, before_anchor, after_anchor) def _script_already_used(glyph): """Check if a user has already executed the script on the .ufo. Executing the script multiple times on the same .ufo will lead to unforeseen modifications.""" prev_added_anc = glyph.xpath("//point[@name='parent_top']") if prev_added_anc: return True return False def main(ufo_path): if not ufo_path.endswith('ufo'): raise Exception('{} is not a ufo'.format(ufo_path)) glifs = glob(os.path.join(ufo_path, 'glyphs', '*.glif')) # check script has not already been run on this font a_glyph = etree.parse(os.path.join(ufo_path, 'glyphs', 'a.glif')) if _script_already_used(a_glyph): raise Exception('Script has already been used') for path in glifs: glyph = etree.parse(path) glyph_name = glyph.getroot().attrib['name'] _delete_design_anchors(glyph) _rename_anchors(glyph) if glyph_name in ['cedilla', 'commaaccent', 'ogonek']: delete_anchor(glyph, 'bottom') if glyph_name in ['uni1ABB', 'uni1ABC']: delete_anchor(glyph, '_top') if glyph_name in DUPLICATE_ANCHOR_GLYPHS: duplicate_anchor(glyph, "top", "parent_top") glyph.write(path) print('Fixed anchors: {}'.format(ufo_path)) if __name__ == '__main__': if len(sys.argv) != 2: raise Exception("Include path to ufo") else: ufo_path = sys.argv[1] main(sys.argv[1])