From 96abb95d8a66e3ee574f14632f3d1b007b139832 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Mon, 6 Jun 2022 23:51:26 +0100 Subject: [PATCH 1/8] ufo-mm-fix: wip --- bin/gftools-ufo-mm-fix.py | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 bin/gftools-ufo-mm-fix.py diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py new file mode 100644 index 000000000..5564e4ae6 --- /dev/null +++ b/bin/gftools-ufo-mm-fix.py @@ -0,0 +1,88 @@ +""" +Primitive prepolator! + +Warning mega destructive! TODO work on duplicates instead of overwriting + +This may take a while to run so grab dinner +""" +from fontTools.varLib.interpolatable import test +from fontmake.compatibility import CompatibilityChecker +from ufoLib2 import Font +import sys +import os + + +def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): + aa = base_font[glyph] + gg = font[glyph] + font.save() + res = test([glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names) + res2 = point_compat(base_font, font, glyph) + if len(res) == 0 and res2: + print(f"Fixed {glyph}") + return True + if idx == len(gg): + return False + + for _ in range(len(gg[idx])): + gg[idx].append(gg[idx].pop(0)) + gg_types = [n.type for n in gg[idx]] + aa_types = [n.type for n in aa[idx]] + if gg_types != aa_types: + continue + + if _fix_starting_points(glyphsets, base_font, font, glyph, names, idx+1): + return True + return False + + +def fix_starting_points(glyphsets, base_font, font, glyph, names): + fixed = _fix_starting_points(glyphsets, base_font, font, glyph, names, 0) + if not fixed: + raise ValueError("shit Curves") + return True + +def point_compat(base_font, font, glyph): + base_points = [] + font_points = [] + + for cont in base_font[glyph]: + for pt in cont: + base_points.append(pt.type) + + for cont in font[glyph]: + for pt in cont: + font_points.append(pt.type) + return base_points == font_points + + +def main(): + names = [os.path.basename(f) for f in sys.argv[1:]] + fonts = [Font.open(f) for f in sys.argv[1:]] + glyphsets = [f.reader.getGlyphSet() for f in fonts] + base_font = fonts[0] + fonts = fonts[1:] + + base_glyphset = glyphsets[0] + glyphsets = glyphsets[1:] + + glyphs = base_font.reader.getGlyphSet().keys() + for glyph in glyphs: + for idx, glyphset in enumerate(glyphsets): + font = fonts[idx] + res = test([base_glyphset, glyphset], glyphs=[glyph], names=names) + points_compat = point_compat(base_font, font, glyph) + if not res and points_compat: + continue + if not points_compat: + fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) + else: + for issue in res[glyph]: + if issue['type'] in ["wrong_start_point", "node_incompatibility"]: + fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) + + print("Done!") + + +if __name__ == "__main__": + main() \ No newline at end of file From 6aee2e9651f035fab9165e12f012f72e460d720f Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 09:41:39 +0100 Subject: [PATCH 2/8] fix contour order --- bin/gftools-ufo-mm-fix.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py index 5564e4ae6..797cf371f 100644 --- a/bin/gftools-ufo-mm-fix.py +++ b/bin/gftools-ufo-mm-fix.py @@ -1,7 +1,8 @@ """ Primitive prepolator! -Warning mega destructive! TODO work on duplicates instead of overwriting +Warning mega destructive! TODO work on duplicates instead of overwriting. +Always use version control! This may take a while to run so grab dinner """ @@ -10,6 +11,7 @@ from ufoLib2 import Font import sys import os +from itertools import permutations def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): @@ -39,9 +41,10 @@ def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): def fix_starting_points(glyphsets, base_font, font, glyph, names): fixed = _fix_starting_points(glyphsets, base_font, font, glyph, names, 0) if not fixed: - raise ValueError("shit Curves") + raise ValueError(f"{glyph}: shit Curves") return True + def point_compat(base_font, font, glyph): base_points = [] font_points = [] @@ -56,6 +59,21 @@ def point_compat(base_font, font, glyph): return base_points == font_points +def fix_contour_order(glyphsets, font, glyph, names): + contours = font[glyph].contours + perms = permutations(contours) + for perm in perms: + font[glyph].clearContours() + for contour in perm: + font[glyph].appendContour(contour) + font.save() + res = test([glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names) + if len(res) == 0: + print(f"{glyph}: fixed") + return + raise ValueError("bad times!") + + def main(): names = [os.path.basename(f) for f in sys.argv[1:]] fonts = [Font.open(f) for f in sys.argv[1:]] @@ -74,7 +92,9 @@ def main(): points_compat = point_compat(base_font, font, glyph) if not res and points_compat: continue - if not points_compat: + if any(i['type'] == "contour_order" for i in res[glyph]): + fix_contour_order([base_glyphset, glyphset], font, glyph, names) + elif not points_compat: fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) else: for issue in res[glyph]: From 9c9dbc2fdaf837d1490cbda9033f3afa913b0943 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 10:13:29 +0100 Subject: [PATCH 3/8] fix component compat --- bin/gftools-ufo-mm-fix.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py index 797cf371f..c1962ea2d 100644 --- a/bin/gftools-ufo-mm-fix.py +++ b/bin/gftools-ufo-mm-fix.py @@ -59,6 +59,12 @@ def point_compat(base_font, font, glyph): return base_points == font_points +def comp_compat(base_font, font, glyph): + base_comps = [c.baseGlyph for c in base_font[glyph].components] + font_comps = [c.baseGlyph for c in font[glyph].components] + return base_comps == font_comps + + def fix_contour_order(glyphsets, font, glyph, names): contours = font[glyph].contours perms = permutations(contours) @@ -71,9 +77,24 @@ def fix_contour_order(glyphsets, font, glyph, names): if len(res) == 0: print(f"{glyph}: fixed") return - raise ValueError("bad times!") + print("failed fixing contour order") +def fix_composite_order(base_font, font, glyph): + components = font[glyph].components + perms = permutations(components) + for perm in perms: + font[glyph].clearComponents() + for comp in perm: + font[glyph].components.append(comp) + font.save() + res = comp_compat(base_font, font, glyph) + if res: + print(f"{glyph}: fixed") + return + print("failed fixing composite order") + + def main(): names = [os.path.basename(f) for f in sys.argv[1:]] fonts = [Font.open(f) for f in sys.argv[1:]] @@ -90,9 +111,12 @@ def main(): font = fonts[idx] res = test([base_glyphset, glyphset], glyphs=[glyph], names=names) points_compat = point_compat(base_font, font, glyph) - if not res and points_compat: + comps_compat = comp_compat(base_font, font, glyph) + if all([not res, points_compat, comps_compat]): continue - if any(i['type'] == "contour_order" for i in res[glyph]): + if not comps_compat: + fix_composite_order(base_font, font, glyph) + elif any(i['type'] == "contour_order" for i in res[glyph]): fix_contour_order([base_glyphset, glyphset], font, glyph, names) elif not points_compat: fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) From ee6976ed111058ad8c11bdc2ac070cd58a995d20 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 10:19:28 +0100 Subject: [PATCH 4/8] print out issues which can not be fixed --- bin/gftools-ufo-mm-fix.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py index c1962ea2d..99baca85d 100644 --- a/bin/gftools-ufo-mm-fix.py +++ b/bin/gftools-ufo-mm-fix.py @@ -116,14 +116,17 @@ def main(): continue if not comps_compat: fix_composite_order(base_font, font, glyph) - elif any(i['type'] == "contour_order" for i in res[glyph]): + if any(i['type'] == "contour_order" for i in res[glyph]): fix_contour_order([base_glyphset, glyphset], font, glyph, names) - elif not points_compat: + if not points_compat: fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) - else: + if res: for issue in res[glyph]: if issue['type'] in ["wrong_start_point", "node_incompatibility"]: fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) + else: + print("Cannot fix") + print(issue) print("Done!") From 6c0bfebe4c7ba79854fd839d588d91116f9e3817 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 11:42:10 +0100 Subject: [PATCH 5/8] cleanup --- bin/gftools-ufo-mm-fix.py | 149 +++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 49 deletions(-) diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py index 99baca85d..6ad52c2f2 100644 --- a/bin/gftools-ufo-mm-fix.py +++ b/bin/gftools-ufo-mm-fix.py @@ -1,39 +1,61 @@ +#!/usr/bin/env python3 """ -Primitive prepolator! +Primitive ufo prepolator! + +Fix ufo and designspace glyph incompatibilities. It can +currently fix: +- starting points +- contour order +- component order + +Users will need to manually fix paths which have different node counts. Warning mega destructive! TODO work on duplicates instead of overwriting. Always use version control! -This may take a while to run so grab dinner +This may take a while for families which contain many masters. + + +Usage: + +# fix ufos +gftools ufo-fix-mm font1.ufo font2.ufo + +# fix designspaces +gftools ufo-fix-mm family.designspace """ +from fontTools.designspaceLib import DesignSpaceDocument from fontTools.varLib.interpolatable import test -from fontmake.compatibility import CompatibilityChecker from ufoLib2 import Font -import sys import os from itertools import permutations +import argparse def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): - aa = base_font[glyph] - gg = font[glyph] + base_glyph = base_font[glyph] + font_glyph = font[glyph] font.save() - res = test([glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names) - res2 = point_compat(base_font, font, glyph) - if len(res) == 0 and res2: - print(f"Fixed {glyph}") + test_order = test( + [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names + ) + test_compatibility = nodes_compatible(base_font, font, glyph) + if len(test_order) == 0 and test_compatibility: + print(f"{glyph}: Fixed starting points") return True - if idx == len(gg): + if idx == len(font_glyph): return False - - for _ in range(len(gg[idx])): - gg[idx].append(gg[idx].pop(0)) - gg_types = [n.type for n in gg[idx]] - aa_types = [n.type for n in aa[idx]] - if gg_types != aa_types: + + for _ in range(len(font_glyph[idx])): + # incrememt starting point by next node + font_glyph[idx].append(font_glyph[idx].pop(0)) + # check that both paths have the same node types + font_nodes = [n.type for n in font_glyph[idx]] + base_nodes = [n.type for n in base_glyph[idx]] + if font_nodes != base_nodes: continue - - if _fix_starting_points(glyphsets, base_font, font, glyph, names, idx+1): + + if _fix_starting_points(glyphsets, base_font, font, glyph, names, idx + 1): return True return False @@ -41,28 +63,29 @@ def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): def fix_starting_points(glyphsets, base_font, font, glyph, names): fixed = _fix_starting_points(glyphsets, base_font, font, glyph, names, 0) if not fixed: - raise ValueError(f"{glyph}: shit Curves") + print(f"{glyph}: Failed fixing starting points") + return False return True -def point_compat(base_font, font, glyph): - base_points = [] - font_points = [] +def nodes_compatible(base_font, font, glyph): + base_font_nodes = [] + font_nodes = [] for cont in base_font[glyph]: for pt in cont: - base_points.append(pt.type) - + base_font_nodes.append(pt.type) + for cont in font[glyph]: for pt in cont: - font_points.append(pt.type) - return base_points == font_points + font_nodes.append(pt.type) + return base_font_nodes == font_nodes -def comp_compat(base_font, font, glyph): - base_comps = [c.baseGlyph for c in base_font[glyph].components] - font_comps = [c.baseGlyph for c in font[glyph].components] - return base_comps == font_comps +def components_compatible(base_font, font, glyph): + base_font_components = [c.baseGlyph for c in base_font[glyph].components] + font_components = [c.baseGlyph for c in font[glyph].components] + return base_font_components == font_components def fix_contour_order(glyphsets, font, glyph, names): @@ -73,14 +96,16 @@ def fix_contour_order(glyphsets, font, glyph, names): for contour in perm: font[glyph].appendContour(contour) font.save() - res = test([glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names) + res = test( + [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names + ) if len(res) == 0: - print(f"{glyph}: fixed") + print(f"{glyph}: Fixed contour order") return - print("failed fixing contour order") + print(f"{glyph}: Failed fixing contour order") -def fix_composite_order(base_font, font, glyph): +def fix_component_order(base_font, font, glyph): components = font[glyph].components perms = permutations(components) for perm in perms: @@ -88,16 +113,37 @@ def fix_composite_order(base_font, font, glyph): for comp in perm: font[glyph].components.append(comp) font.save() - res = comp_compat(base_font, font, glyph) + res = components_compatible(base_font, font, glyph) if res: - print(f"{glyph}: fixed") + print(f"{glyph}: Fixed component order") return - print("failed fixing composite order") - + print(f"{glyph}: Failed fixing component order") + def main(): - names = [os.path.basename(f) for f in sys.argv[1:]] - fonts = [Font.open(f) for f in sys.argv[1:]] + parser = argparse.ArgumentParser(__doc__) + parser.add_argument( + "fonts", nargs="+", help="Source files. Either ufos of designspace." + ) + args = parser.parse_args() + + sources = [] + for src in args.fonts: + if src.endswith(".designspace"): + ds = DesignSpaceDocument() + ds.read(src) + for s in ds.sources: + if s in sources: + continue + sources.append(s.path) + else: + sources.append(src) + + if len(sources) == 1: + raise ValueError("Single source family! cannot check.") + + names = [os.path.basename(f) for f in sources[1:]] + fonts = [Font.open(f) for f in sources[1:]] glyphsets = [f.reader.getGlyphSet() for f in fonts] base_font = fonts[0] fonts = fonts[1:] @@ -107,23 +153,28 @@ def main(): glyphs = base_font.reader.getGlyphSet().keys() for glyph in glyphs: + # Check each font against the base font for idx, glyphset in enumerate(glyphsets): font = fonts[idx] res = test([base_glyphset, glyphset], glyphs=[glyph], names=names) - points_compat = point_compat(base_font, font, glyph) - comps_compat = comp_compat(base_font, font, glyph) + points_compat = nodes_compatible(base_font, font, glyph) + comps_compat = components_compatible(base_font, font, glyph) if all([not res, points_compat, comps_compat]): continue if not comps_compat: - fix_composite_order(base_font, font, glyph) - if any(i['type'] == "contour_order" for i in res[glyph]): + fix_component_order(base_font, font, glyph) + if any(i["type"] == "contour_order" for i in res[glyph]): fix_contour_order([base_glyphset, glyphset], font, glyph, names) if not points_compat: - fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) + fix_starting_points( + [base_glyphset, glyphset], base_font, font, glyph, names + ) if res: for issue in res[glyph]: - if issue['type'] in ["wrong_start_point", "node_incompatibility"]: - fix_starting_points([base_glyphset, glyphset], base_font, font, glyph, names) + if issue["type"] in ["wrong_start_point", "node_incompatibility"]: + fix_starting_points( + [base_glyphset, glyphset], base_font, font, glyph, names + ) else: print("Cannot fix") print(issue) @@ -132,4 +183,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() From 0acb304cd1e1714462d793a5397a900a9776dc5a Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 11:42:36 +0100 Subject: [PATCH 6/8] rename --- bin/gftools-ufo-mm-fix.py | 186 -------------------------------------- 1 file changed, 186 deletions(-) delete mode 100644 bin/gftools-ufo-mm-fix.py diff --git a/bin/gftools-ufo-mm-fix.py b/bin/gftools-ufo-mm-fix.py deleted file mode 100644 index 6ad52c2f2..000000000 --- a/bin/gftools-ufo-mm-fix.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python3 -""" -Primitive ufo prepolator! - -Fix ufo and designspace glyph incompatibilities. It can -currently fix: -- starting points -- contour order -- component order - -Users will need to manually fix paths which have different node counts. - -Warning mega destructive! TODO work on duplicates instead of overwriting. -Always use version control! - -This may take a while for families which contain many masters. - - -Usage: - -# fix ufos -gftools ufo-fix-mm font1.ufo font2.ufo - -# fix designspaces -gftools ufo-fix-mm family.designspace -""" -from fontTools.designspaceLib import DesignSpaceDocument -from fontTools.varLib.interpolatable import test -from ufoLib2 import Font -import os -from itertools import permutations -import argparse - - -def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): - base_glyph = base_font[glyph] - font_glyph = font[glyph] - font.save() - test_order = test( - [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names - ) - test_compatibility = nodes_compatible(base_font, font, glyph) - if len(test_order) == 0 and test_compatibility: - print(f"{glyph}: Fixed starting points") - return True - if idx == len(font_glyph): - return False - - for _ in range(len(font_glyph[idx])): - # incrememt starting point by next node - font_glyph[idx].append(font_glyph[idx].pop(0)) - # check that both paths have the same node types - font_nodes = [n.type for n in font_glyph[idx]] - base_nodes = [n.type for n in base_glyph[idx]] - if font_nodes != base_nodes: - continue - - if _fix_starting_points(glyphsets, base_font, font, glyph, names, idx + 1): - return True - return False - - -def fix_starting_points(glyphsets, base_font, font, glyph, names): - fixed = _fix_starting_points(glyphsets, base_font, font, glyph, names, 0) - if not fixed: - print(f"{glyph}: Failed fixing starting points") - return False - return True - - -def nodes_compatible(base_font, font, glyph): - base_font_nodes = [] - font_nodes = [] - - for cont in base_font[glyph]: - for pt in cont: - base_font_nodes.append(pt.type) - - for cont in font[glyph]: - for pt in cont: - font_nodes.append(pt.type) - return base_font_nodes == font_nodes - - -def components_compatible(base_font, font, glyph): - base_font_components = [c.baseGlyph for c in base_font[glyph].components] - font_components = [c.baseGlyph for c in font[glyph].components] - return base_font_components == font_components - - -def fix_contour_order(glyphsets, font, glyph, names): - contours = font[glyph].contours - perms = permutations(contours) - for perm in perms: - font[glyph].clearContours() - for contour in perm: - font[glyph].appendContour(contour) - font.save() - res = test( - [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names - ) - if len(res) == 0: - print(f"{glyph}: Fixed contour order") - return - print(f"{glyph}: Failed fixing contour order") - - -def fix_component_order(base_font, font, glyph): - components = font[glyph].components - perms = permutations(components) - for perm in perms: - font[glyph].clearComponents() - for comp in perm: - font[glyph].components.append(comp) - font.save() - res = components_compatible(base_font, font, glyph) - if res: - print(f"{glyph}: Fixed component order") - return - print(f"{glyph}: Failed fixing component order") - - -def main(): - parser = argparse.ArgumentParser(__doc__) - parser.add_argument( - "fonts", nargs="+", help="Source files. Either ufos of designspace." - ) - args = parser.parse_args() - - sources = [] - for src in args.fonts: - if src.endswith(".designspace"): - ds = DesignSpaceDocument() - ds.read(src) - for s in ds.sources: - if s in sources: - continue - sources.append(s.path) - else: - sources.append(src) - - if len(sources) == 1: - raise ValueError("Single source family! cannot check.") - - names = [os.path.basename(f) for f in sources[1:]] - fonts = [Font.open(f) for f in sources[1:]] - glyphsets = [f.reader.getGlyphSet() for f in fonts] - base_font = fonts[0] - fonts = fonts[1:] - - base_glyphset = glyphsets[0] - glyphsets = glyphsets[1:] - - glyphs = base_font.reader.getGlyphSet().keys() - for glyph in glyphs: - # Check each font against the base font - for idx, glyphset in enumerate(glyphsets): - font = fonts[idx] - res = test([base_glyphset, glyphset], glyphs=[glyph], names=names) - points_compat = nodes_compatible(base_font, font, glyph) - comps_compat = components_compatible(base_font, font, glyph) - if all([not res, points_compat, comps_compat]): - continue - if not comps_compat: - fix_component_order(base_font, font, glyph) - if any(i["type"] == "contour_order" for i in res[glyph]): - fix_contour_order([base_glyphset, glyphset], font, glyph, names) - if not points_compat: - fix_starting_points( - [base_glyphset, glyphset], base_font, font, glyph, names - ) - if res: - for issue in res[glyph]: - if issue["type"] in ["wrong_start_point", "node_incompatibility"]: - fix_starting_points( - [base_glyphset, glyphset], base_font, font, glyph, names - ) - else: - print("Cannot fix") - print(issue) - - print("Done!") - - -if __name__ == "__main__": - main() From 21529504a06614cb26e810aaa09e930c52132870 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 11:44:21 +0100 Subject: [PATCH 7/8] change perms --- bin/gftools-ufo-fix-mm.py | 186 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100755 bin/gftools-ufo-fix-mm.py diff --git a/bin/gftools-ufo-fix-mm.py b/bin/gftools-ufo-fix-mm.py new file mode 100755 index 000000000..6ad52c2f2 --- /dev/null +++ b/bin/gftools-ufo-fix-mm.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +""" +Primitive ufo prepolator! + +Fix ufo and designspace glyph incompatibilities. It can +currently fix: +- starting points +- contour order +- component order + +Users will need to manually fix paths which have different node counts. + +Warning mega destructive! TODO work on duplicates instead of overwriting. +Always use version control! + +This may take a while for families which contain many masters. + + +Usage: + +# fix ufos +gftools ufo-fix-mm font1.ufo font2.ufo + +# fix designspaces +gftools ufo-fix-mm family.designspace +""" +from fontTools.designspaceLib import DesignSpaceDocument +from fontTools.varLib.interpolatable import test +from ufoLib2 import Font +import os +from itertools import permutations +import argparse + + +def _fix_starting_points(glyphsets, base_font, font, glyph, names, idx): + base_glyph = base_font[glyph] + font_glyph = font[glyph] + font.save() + test_order = test( + [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names + ) + test_compatibility = nodes_compatible(base_font, font, glyph) + if len(test_order) == 0 and test_compatibility: + print(f"{glyph}: Fixed starting points") + return True + if idx == len(font_glyph): + return False + + for _ in range(len(font_glyph[idx])): + # incrememt starting point by next node + font_glyph[idx].append(font_glyph[idx].pop(0)) + # check that both paths have the same node types + font_nodes = [n.type for n in font_glyph[idx]] + base_nodes = [n.type for n in base_glyph[idx]] + if font_nodes != base_nodes: + continue + + if _fix_starting_points(glyphsets, base_font, font, glyph, names, idx + 1): + return True + return False + + +def fix_starting_points(glyphsets, base_font, font, glyph, names): + fixed = _fix_starting_points(glyphsets, base_font, font, glyph, names, 0) + if not fixed: + print(f"{glyph}: Failed fixing starting points") + return False + return True + + +def nodes_compatible(base_font, font, glyph): + base_font_nodes = [] + font_nodes = [] + + for cont in base_font[glyph]: + for pt in cont: + base_font_nodes.append(pt.type) + + for cont in font[glyph]: + for pt in cont: + font_nodes.append(pt.type) + return base_font_nodes == font_nodes + + +def components_compatible(base_font, font, glyph): + base_font_components = [c.baseGlyph for c in base_font[glyph].components] + font_components = [c.baseGlyph for c in font[glyph].components] + return base_font_components == font_components + + +def fix_contour_order(glyphsets, font, glyph, names): + contours = font[glyph].contours + perms = permutations(contours) + for perm in perms: + font[glyph].clearContours() + for contour in perm: + font[glyph].appendContour(contour) + font.save() + res = test( + [glyphsets[0], font.reader.getGlyphSet()], glyphs=[glyph], names=names + ) + if len(res) == 0: + print(f"{glyph}: Fixed contour order") + return + print(f"{glyph}: Failed fixing contour order") + + +def fix_component_order(base_font, font, glyph): + components = font[glyph].components + perms = permutations(components) + for perm in perms: + font[glyph].clearComponents() + for comp in perm: + font[glyph].components.append(comp) + font.save() + res = components_compatible(base_font, font, glyph) + if res: + print(f"{glyph}: Fixed component order") + return + print(f"{glyph}: Failed fixing component order") + + +def main(): + parser = argparse.ArgumentParser(__doc__) + parser.add_argument( + "fonts", nargs="+", help="Source files. Either ufos of designspace." + ) + args = parser.parse_args() + + sources = [] + for src in args.fonts: + if src.endswith(".designspace"): + ds = DesignSpaceDocument() + ds.read(src) + for s in ds.sources: + if s in sources: + continue + sources.append(s.path) + else: + sources.append(src) + + if len(sources) == 1: + raise ValueError("Single source family! cannot check.") + + names = [os.path.basename(f) for f in sources[1:]] + fonts = [Font.open(f) for f in sources[1:]] + glyphsets = [f.reader.getGlyphSet() for f in fonts] + base_font = fonts[0] + fonts = fonts[1:] + + base_glyphset = glyphsets[0] + glyphsets = glyphsets[1:] + + glyphs = base_font.reader.getGlyphSet().keys() + for glyph in glyphs: + # Check each font against the base font + for idx, glyphset in enumerate(glyphsets): + font = fonts[idx] + res = test([base_glyphset, glyphset], glyphs=[glyph], names=names) + points_compat = nodes_compatible(base_font, font, glyph) + comps_compat = components_compatible(base_font, font, glyph) + if all([not res, points_compat, comps_compat]): + continue + if not comps_compat: + fix_component_order(base_font, font, glyph) + if any(i["type"] == "contour_order" for i in res[glyph]): + fix_contour_order([base_glyphset, glyphset], font, glyph, names) + if not points_compat: + fix_starting_points( + [base_glyphset, glyphset], base_font, font, glyph, names + ) + if res: + for issue in res[glyph]: + if issue["type"] in ["wrong_start_point", "node_incompatibility"]: + fix_starting_points( + [base_glyphset, glyphset], base_font, font, glyph, names + ) + else: + print("Cannot fix") + print(issue) + + print("Done!") + + +if __name__ == "__main__": + main() From fcc3d4a75bfb1c67ac07faac321ae11055bc9e54 Mon Sep 17 00:00:00 2001 From: Marc Foley Date: Tue, 7 Jun 2022 15:08:55 +0100 Subject: [PATCH 8/8] fix --- bin/gftools-ufo-fix-mm.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/gftools-ufo-fix-mm.py b/bin/gftools-ufo-fix-mm.py index 6ad52c2f2..112128ce9 100755 --- a/bin/gftools-ufo-fix-mm.py +++ b/bin/gftools-ufo-fix-mm.py @@ -138,12 +138,12 @@ def main(): sources.append(s.path) else: sources.append(src) - + if len(sources) == 1: raise ValueError("Single source family! cannot check.") names = [os.path.basename(f) for f in sources[1:]] - fonts = [Font.open(f) for f in sources[1:]] + fonts = [Font.open(f) for f in sources] glyphsets = [f.reader.getGlyphSet() for f in fonts] base_font = fonts[0] fonts = fonts[1:] @@ -161,14 +161,14 @@ def main(): comps_compat = components_compatible(base_font, font, glyph) if all([not res, points_compat, comps_compat]): continue - if not comps_compat: - fix_component_order(base_font, font, glyph) - if any(i["type"] == "contour_order" for i in res[glyph]): - fix_contour_order([base_glyphset, glyphset], font, glyph, names) if not points_compat: fix_starting_points( [base_glyphset, glyphset], base_font, font, glyph, names ) + if not comps_compat: + fix_component_order(base_font, font, glyph) + if res and any(i["type"] == "contour_order" for i in res[glyph]): + fix_contour_order([base_glyphset, glyphset], font, glyph, names) if res: for issue in res[glyph]: if issue["type"] in ["wrong_start_point", "node_incompatibility"]: