#!/usr/bin/env python3 __doc__ = '''Make changes needed to a UFO following processing by FontLab 7. Various items are reset using the backup of the original font that Fontlab creates ''' __url__ = 'https://github.com/silnrsi/pysilfont' __copyright__ = 'Copyright (c) 2021 SIL International (https://www.sil.org)' __license__ = 'Released under the MIT License (https://opensource.org/licenses/MIT)' __author__ = 'David Raymond' from silfont.core import execute, splitfn from silfont.ufo import Ufont import os, shutil, glob argspec = [ ('ifont',{'help': 'Input font file'}, {'type': 'filename'}), ('-l','--log',{'help': 'Log file'}, {'type': 'outfile', 'def': '_fixfontlab.log'})] def doit(args) : fontname = args.ifont logger = args.logger params = args.paramsobj # Locate the oldest backup (path, base, ext) = splitfn(fontname) backuppath = os.path.join(path, base + ".*-*" + ext) # Backup has date/time added in format .yymmdd-hhmm backups = glob.glob(backuppath) if len(backups) == 0: logger.log("No backups found matching %s so no changes made to the font" % backuppath, "P") return backupname = sorted(backups)[0] # Choose the oldest backup - date/time format sorts alphabetically # Reset groups.plist, kerning.plist and any layerinfo.plist(s) from backup ufo for filename in ["groups.plist", "kerning.plist"]: bufullname = os.path.join(backupname, filename) ufofullname = os.path.join(fontname, filename) if os.path.exists(bufullname): try: shutil.copy(bufullname, fontname) logger.log(filename + " restored from backup", "P") except Exception as e: logger.log("Failed to copy %s to %s: %s" % (bufullname, fontname, str(e)), "S") elif os.path.exists(ufofullname): os.remove(ufofullname) logger.log(filename + " removed from ufo", "P") lifolders = [] for ufoname in (fontname, backupname): # Find any layerinfo files in either ufo lis = glob.glob(os.path.join(ufoname, "*/layerinfo.plist")) for li in lis: (lifolder, dummy) = os.path.split(li) # Get full path name for folder (dummy, lifolder) = os.path.split(lifolder) # Now take ufo name off the front if lifolder not in lifolders: lifolders.append(lifolder) for folder in lifolders: filename = os.path.join(folder, "layerinfo.plist") bufullname = os.path.join(backupname, filename) ufofullname = os.path.join(fontname, filename) if os.path.exists(bufullname): try: shutil.copy(bufullname, os.path.join(fontname, folder)) logger.log(filename + " restored from backup", "P") except Exception as e: logger.log("Failed to copy %s to %s: %s" % (bufullname, fontname, str(e)), "S") elif os.path.exists(ufofullname): os.remove(ufofullname) logger.log(filename + " removed from ufo", "P") # Now open the fonts font = Ufont(fontname, params = params) backupfont = Ufont(backupname, params = params) fidel = ("openTypeGaspRangeRecords", "openTypeHheaCaretOffset", "postscriptBlueFuzz", "postscriptBlueScale", "postscriptBlueShift", "postscriptForceBold", "postscriptIsFixedPitch", "postscriptWeightName") libdel = ("com.fontlab.v2.tth", "com.typemytype.robofont.italicSlantOffset") fontinfo = font.fontinfo libplist = font.lib backupfi = backupfont.fontinfo backuplib = backupfont.lib # Delete keys that are not needed for key in fidel: if key in fontinfo: old = fontinfo.getval(key) fontinfo.remove(key) logchange(logger, " removed from fontinfo.plist. ", key, old, None) for key in libdel: if key in libplist: old = libplist.getval(key) libplist.remove(key) logchange(logger, " removed from lib.plist. ", key, old, None) # Correct other metadata: if "guidelines" in backupfi: fontinfo.setelem("guidelines",backupfi["guidelines"][1]) logger.log("fontinfo guidelines copied from backup ufo", "I") elif "guidelines" in fontinfo: fontinfo.remove("guidelines") logger.log("fontinfo guidelines deleted - not in backup ufo", "I") if "italicAngle" in fontinfo and fontinfo.getval("italicAngle") == 0: fontinfo.remove("italicAngle") logger.log("fontinfo italicAngle removed since it was 0", "I") if "openTypeOS2VendorID" in fontinfo: old = fontinfo.getval("openTypeOS2VendorID") if len(old) < 4: new = "%-4s" % (old,) fontinfo.setval("openTypeOS2VendorID", "string", new) logchange(logger, " padded to 4 characters ", "openTypeOS2VendorID", "'%s'" % (old,) , "'%s'" % (new,)) if "woffMetadataCredits" in backupfi: fontinfo.setelem("woffMetadataCredits",backupfi["woffMetadataCredits"][1]) logger.log("fontinfo woffMetadataCredits copied from backup ufo", "I") elif "woffMetadataCredits" in fontinfo: fontinfo.remove("woffMetadataCredits") logger.log("fontinfo woffMetadataCredits deleted - not in backup ufo", "I") if "woffMetadataDescription" in backupfi: fontinfo.setelem("woffMetadataDescription",backupfi["woffMetadataDescription"][1]) logger.log("fontinfo woffMetadataDescription copied from backup ufo", "I") elif "woffMetadataDescription" in fontinfo: fontinfo.remove("woffMetadataDescription") logger.log("fontinfo woffMetadataDescription deleted - not in backup ufo", "I") if "public.glyphOrder" in backuplib: libplist.setelem("public.glyphOrder",backuplib["public.glyphOrder"][1]) logger.log("lib.plist public.glyphOrder copied from backup ufo", "I") elif "public.glyphOrder" in libplist: libplist.remove("public.glyphOrder") logger.log("libplist public.glyphOrder deleted - not in backup ufo", "I") # Now process glif level data updates = False for gname in font.deflayer: glyph = font.deflayer[gname] glines = glyph["guideline"] if glines: for gl in list(glines): glines.remove(gl) # Remove any existing glines updates = True buglines = backupfont.deflayer[gname]["guideline"] if gname in backupfont.deflayer else [] if buglines: for gl in buglines: glines.append(gl) # Add in those from backup updates = True if updates: logger.log("Some updates to glif guidelines may have been made", "I") updates = False for layer in font.layers: if layer.layername == "public.background": for gname in layer: glyph = layer[gname] if glyph["advance"] is not None: glyph.remove("advance") updates = True if updates: logger.log("Some advance elements removed from public.background glifs", "I") font.write(fontname) return def logchange(logger, logmess, key, old, new): oldstr = str(old) if len(str(old)) < 22 else str(old)[0:20] + "..." newstr = str(new) if len(str(new)) < 22 else str(new)[0:20] + "..." logmess = key + logmess if old is None: logmess = logmess + " New value: " + newstr else: if new is None: logmess = logmess + " Old value: " + oldstr else: logmess = logmess + " Old value: " + oldstr + ", new value: " + newstr logger.log(logmess, "I") def cmd() : execute(None,doit, argspec) if __name__ == "__main__": cmd()