Esempio n. 1
0
def doit(args) :

    font = args.font
    logger = args.logger
    newversion = args.newversion


    fi = font.fontinfo
    otelem = fi["openTypeNameVersion"][1] if "openTypeNameVersion" in fi else None
    majelem = fi["versionMajor"][1] if "versionMajor" in fi else None
    minelem = fi["versionMinor"][1] if "versionMinor" in fi else None
    otnv = None if otelem is None else otelem.text
    vmaj = None if majelem is None else majelem.text
    vmin = None if minelem is None else minelem.text

    if otnv is None or vmaj is None or vmin is None : logger.log("At least one of openTypeNameVersion, versionMajor or versionMinor missing fron fontinfo.plist", "S")

    if newversion is None:
        if otnvre.match(otnv) is None:
            logger.log("Current version is '" + otnv + "' which is non-standard", "E")
        else :
            logger.log("Current version is '" + otnv + "'", "P")
            (otmaj,otmin,otextrainfo) = parseotnv(otnv)
            if (otmaj, int(otmin)) != (vmaj,int(vmin)) :
                logger.log("openTypeNameVersion values don't match versionMajor (" + vmaj + ") and versionMinor (" + vmin + ")", "E")
    else:
        if newversion[0] == "+" :
            if otnvre.match(otnv) is None:
                logger.log("Current openTypeNameVersion is non-standard so can't be incremented: " + otnv , "S")
            else :
                (otmaj,otmin,otextrainfo) = parseotnv(otnv)
                if (otmaj, int(otmin)) != (vmaj,int(vmin)) :
                    logger.log("openTypeNameVersion (" + otnv + ") doesn't match versionMajor (" + vmaj + ") and versionMinor (" + vmin + ")", "S")
            # Process increment to versionMinor.  Note vmin is treated as 3 digit mpp where m and pp are minor and patch versions respectively
            increment = newversion[1:]
            if increment not in ("1", "0.001", ".001", "0.1", ".1") :
                logger.log("Invalid increment value - must be one of 1, 0.001, .001, 0.1 or .1", "S")
            increment = 100 if increment in ("0.1", ".1") else 1
            if (increment == 100 and vmin[0] == "9") or (increment == 1 and vmin[1:2] == "99") :
                logger.log("Version already at maximum so can't be incremented", "S")
            otmin = str(int(otmin) + increment).zfill(3)
        else :
            newversion = "Version " + newversion
            if otnvre.match(newversion) is None:
                logger.log("newversion format invalid - should be 'M.mpp' or 'M.mpp extrainfo'", "S")
            else :
                (otmaj,otmin,otextrainfo) = parseotnv(newversion)
        newotnv = "Version " + otmaj + "." + otmin + otextrainfo # Extrainfo already as leading space
        logger.log("Updating version from '" + otnv + "' to '" + newotnv + "'","P")

        # Update and write to disk
        otelem.text = newotnv
        majelem.text = otmaj
        minelem.text = otmin
        UFO.writeXMLobject(fi,font.outparams,font.ufodir, "fontinfo.plist" , True, fobject = True)

    return
Esempio n. 2
0
def doit(args) :

    font = args.font
    logger = args.logger
    newversion = args.newversion


    fi = font.fontinfo
    otelem = fi["openTypeNameVersion"][1] if "openTypeNameVersion" in fi else None
    majelem = fi["versionMajor"][1] if "versionMajor" in fi else None
    minelem = fi["versionMinor"][1] if "versionMinor" in fi else None
    otnv = None if otelem is None else otelem.text
    vmaj = None if majelem is None else majelem.text
    vmin = None if minelem is None else minelem.text

    if otnv is None or vmaj is None or vmin is None : logger.log("At least one of openTypeNameVersion, versionMajor or versionMinor missing fron fontinfo.plist", "S")

    if newversion is None:
        if otnvre.match(otnv) is None:
            logger.log("Current version is '" + otnv + "' which is non-standard", "E")
        else :
            logger.log("Current version is '" + otnv + "'", "P")
            (otmaj,otmin,otextrainfo) = parseotnv(otnv)
            if (otmaj, int(otmin)) != (vmaj,int(vmin)) :
                logger.log("openTypeNameVersion values don't match versionMajor (" + vmaj + ") and versionMinor (" + vmin + ")", "E")
    else:
        if newversion[0:1] == "+" :
            if otnvre.match(otnv) is None:
                logger.log("Current openTypeNameVersion is non-standard so can't be incremented: " + otnv , "S")
            else :
                (otmaj,otmin,otextrainfo) = parseotnv(otnv)
                if (otmaj, int(otmin)) != (vmaj,int(vmin)) :
                    logger.log("openTypeNameVersion (" + otnv + ") doesn't match versionMajor (" + vmaj + ") and versionMinor (" + vmin + ")", "S")
            # Process increment to versionMinor.  Note vmin is treated as 3 digit mpp where m and pp are minor and patch versions respectively
            increment = newversion[1:]
            if increment not in ("1", "0.001", ".001", "0.1", ".1") :
                logger.log("Invalid increment value - must be one of 1, 0.001, .001, 0.1 or .1", "S")
            increment = 100 if increment in ("0.1", ".1") else 1
            if (increment == 100 and vmin[0:1] == "9") or (increment == 1 and vmin[1:2] == "99") :
                logger.log("Version already at maximum so can't be incremented", "S")
            otmin = str(int(otmin) + increment).zfill(3)
        else :
            newversion = "Version " + newversion
            if otnvre.match(newversion) is None:
                logger.log("newversion format invalid - should be 'M.mpp' or 'M.mpp extrainfo'", "S")
            else :
                (otmaj,otmin,otextrainfo) = parseotnv(newversion)
        newotnv = "Version " + otmaj + "." + otmin + otextrainfo # Extrainfo already as leading space
        logger.log("Updating version from '" + otnv + "' to '" + newotnv + "'","P")

        # Update and write to disk
        otelem.text = newotnv
        majelem.text = otmaj
        minelem.text = otmin
        UFO.writeXMLobject(fi,font.outparams,font.ufodir, "fontinfo.plist" , True, fobject = True)

    return
Esempio n. 3
0
 def write(self, plistn):
     filen = plistn + self.newfile + ".plist"
     self.logger.log("Writing updated " + plistn + ".plist to " + filen,
                     "P")
     exists = True if os.path.isfile(os.path.join(self.ufodir,
                                                  filen)) else False
     plist = getattr(self, plistn)
     UFO.writeXMLobject(plist,
                        self.outparams,
                        self.ufodir,
                        filen,
                        exists,
                        fobject=True)
Esempio n. 4
0
def doit(args) :
    standardstyles = ["Regular", "Italic", "Bold", "BoldItalic"]
    finfoignore = ["openTypeHeadCreated", "openTypeOS2Panose", "postscriptBlueScale", "postscriptBlueShift",
                   "postscriptBlueValues", "postscriptOtherBlues", "postscriptStemSnapH", "postscriptStemSnapV", "postscriptForceBold"]
    libfields = ["public.postscriptNames", "public.glyphOrder", "com.schriftgestaltung.glyphOrder"]

    font = args.ifont
    logger = args.logger
    singlefont = args.single
    mfont = args.master
    newfile = "_new" if args.new else ""
    reportonly = args.reportonly
    updatemessage = " to be updated: " if reportonly else " updated: "
    params = args.paramsobj
    precision = font.paramset["precision"]

    # Increase screen logging level to W unless specific level supplied on command-line
    if not(args.quiet or "scrlevel" in params.sets["command line"]) : logger.scrlevel = "W"

    # Process UFO name
    (path,base) = os.path.split(font.ufodir)
    (base,ext) = os.path.splitext(base)
    if '-' not in base : logger.log("Non-standard UFO name - must be <family>-<style>", "S")
    (family,style) = base.split('-')

    styles = [style]
    fonts = {}
    fonts[style] = font

    # Process single and master settings
    if singlefont :
        if mfont :
            mastertext = "Master" # Used in log messages
        else : # Check against Regular font from same family
            mfont = openfont(params, path, family, "Regular")
            if mfont is None : logger.log("No regular font to check against - use -m to specify master font", "S")
            mastertext = "Regular"
            fonts["Regular"] =mfont
    else : # Supplied font must be Regular
        if mfont : logger.log("-m --master must only be used with -s --single", "S")
        if style != "Regular" : logger.log("Must specify a Regular font unless -s is used", "S")
        mastertext = "Regular"
        mfont = font

    # Check for required fields in master font
    mfinfo = mfont.fontinfo
    if "familyName" in mfinfo :
        spacedfamily = mfinfo["familyName"][1].text
    else:
        logger.log("No familyName field in " + mastertext, "S")
    if "openTypeNameManufacturer" in mfinfo :
        manufacturer = mfinfo["openTypeNameManufacturer"][1].text
    else:
        logger.log("No openTypeNameManufacturer field in " + mastertext, "S")
    mlib = mfont.lib

    # Open the remaining fonts in the family
    if not singlefont :
        for style in standardstyles :
            if not style in fonts :
                fonts[style] = openfont(params, path, family, style) # Will return None if font does not exist
                if fonts[style] is not None : styles.append(style)

    # Process fonts
    psuniqueidlist = []
    fieldscopied = False
    for style in styles :
        font = fonts[style]
        if font.UFOversion != "2" : logger.log("This script only works with UFO 2 format fonts","S")

        fontname = family + "-" + style
        spacedstyle = "Bold Italic" if style == "BoldItalic" else style
        spacedname = spacedfamily + " " + spacedstyle
        logger.log("************ Processing " + fontname, "P")

        ital = True if "Italic" in style else False
        bold = True if "Bold" in style else False

        # Process fontinfo.plist
        finfo=font.fontinfo
        fieldlist = list(set(finfo) | set(mfinfo)) # Need all fields from both to detect missing fields
        fchanged = False

        for field in fieldlist:
            action = None; issue = ""; newval = ""
            if field in finfo :
                elem = finfo[field][1]
                tag = elem.tag
                text = elem.text
                if text is None : text = ""
                if tag == "real" : text = processnum(text,precision)
            # Field-specific actions

            if field not in finfo :
                if field not in finfoignore : action = "Copyfield"
            elif field == "italicAngle" :
                if ital and text == "0" :
                    issue = "is zero"
                    action = "Warn"
                if not ital and text != "0" :
                    issue = "is non-zero"
                    newval = 0
                    action = "Update"
            elif field == "openTypeNameUniqueID" :
                newval = manufacturer + ": " + spacedname + ": " + datetime.now().strftime("%Y")
                if text != newval :
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "openTypeOS2WeightClass" :
                if bold and text != "700" :
                    issue = "is not 700"
                    newval = 700
                    action = "Update"
                if not bold and text != "400" :
                    issue = "is not 400"
                    newval = 400
                    action = "Update"
            elif field == "postscriptFontName" :
                if text != fontname :
                    newval = fontname
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "postscriptFullName" :
                if text != spacedname :
                    newval = spacedname
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "postscriptUniqueID" :
                if text in psuniqueidlist :
                    issue = "has same value as another font: " + text
                    action = "Warn"
                else :
                    psuniqueidlist.append(text)
            elif field == "postscriptWeightName" :
                newval = 'bold' if bold else 'regular'
                if text != newval :
                    issue = "Incorrect value"
                    action = 'Update'
            elif field == "styleMapStyleName" :
                if text != spacedstyle.lower() :
                    newval = spacedstyle.lower()
                    issue = "Incorrect value"
                    action = "Update"
            elif field in ("styleName", "openTypeNamePreferredSubfamilyName") :
                if text != spacedstyle :
                    newval = spacedstyle
                    issue = "Incorrect value"
                    action = "Update"
            elif field in finfoignore :
                action = "Ignore"
            # Warn for fields in this font but not master
            elif field not in mfinfo :
                issue = "is in " + spacedstyle + " but not in " + mastertext
                action = "Warn"
            # for all other fields, sync values from master
            else :
                melem = mfinfo[field][1]
                mtag = melem.tag
                mtext = melem.text
                if mtext is None : mtext = ""
                if mtag is 'real' : mtext = processnum(mtext,precision)
                if tag in ("real", "integer", "string") :
                    if mtext != text :
                        issue = "does not match " + mastertext + " value"
                        newval = mtext
                        action = "Update"
                elif tag in ("true, false") :
                    if tag != mtag :
                        issue = "does not match " + mastertext + " value"
                        action = "FlipBoolean"
                elif tag == "array" : # Assume simple array with just values to compare
                    marray = mfinfo.getval(field)
                    array = finfo.getval(field)
                    if array != marray: action = "CopyArray"
                else : logger.log("Non-standard fontinfo field type in " + fontname, "X")

            # Now process the actions, create log messages etc
            if action is None or action == "Ignore" :
                pass
            elif action == "Warn" :
                logger.log(field + " needs manual correction: " + issue, "W")
            elif action == "Error" :
                logger.log(field + " needs manual correction: " + issue, "E")
            elif action in ("Update", "FlipBoolean", "Copyfield", "CopyArray") : # Updating actions
                fchanged = True
                message = field + updatemessage
                if action == "Update" :
                    message = message + issue + " Old: '" + text + "' New: '" + str(newval) + "'"
                    elem.text = newval
                elif action == "FlipBoolean" :
                    newval = "true" if tag == "false" else "false"
                    message = message + issue + " Old: '" + tag + "' New: '" + newval + "'"
                    finfo.setelem(field, ET.fromstring("<" + newval + "/>"))
                elif action == "Copyfield" :
                    message = message + "is missing so will be copied from " + mastertext
                    fieldscopied = True
                    finfo.addelem(field, ET.fromstring(ET.tostring(mfinfo[field][1])))
                elif action == "CopyArray" :
                    message = message + "Some values different Old: " + str(array) + " New: " + str(marray)
                    finfo.setelem(field, ET.fromstring(ET.tostring(melem)))
                logger.log(message, "W")
            else:
                logger.log("Uncoded action: " + action + " - oops", "X")

        # Process lib.plist - currently just public.postscriptNames and glyph order fields which are all simple dicts or arrays
        lib = font.lib
        lchanged = False

        for field in libfields:
            # Check the values
            action = None; issue = ""; newval = ""
            if field in mlib:
                if field in lib:
                    if lib.getval(field) != mlib.getval(field):  # will only work for arrays or dicts with simple values
                        action = "Updatefield"
                else:
                    action = "Copyfield"
            else:
                action = "Error" if field == ("public.GlyphOrder", "public.postscriptNames") else "Warn"
                issue = field + " not in " + mastertext + " lib.plist"

            # Process the actions, create log messages etc
            if action is None or action == "Ignore":
                pass
            elif action == "Warn":
                logger.log(field + " needs manual correction: " + issue, "W")
            elif action == "Error":
                logger.log(field + " needs manual correction: " + issue, "E")
            elif action in ("Updatefield", "Copyfield"):  # Updating actions
                lchanged = True
                message = field + updatemessage
                if action == "Copyfield":
                    message = message + "is missing so will be copied from " + mastertext
                    lib.addelem(field, ET.fromstring(ET.tostring(mlib[field][1])))
                elif action == "Updatefield":
                    message = message + "Some values different"
                    lib.setelem(field, ET.fromstring(ET.tostring(mlib[field][1])))
                logger.log(message, "W")
            else:
                logger.log("Uncoded action: " + action + " - oops", "X")

        # Now update on disk
        if not reportonly:
            if args.normalize:
                font.write(os.path.join(path, family + "-" + style + newfile + ".ufo"))
            else:  # Just update fontinfo and lib
                if fchanged:
                    filen = "fontinfo" + newfile + ".plist"
                    logger.log("Writing updated fontinfo to " + filen, "P")
                    exists = True if os.path.isfile(os.path.join(font.ufodir, filen)) else False
                    UFO.writeXMLobject(finfo, font.outparams, font.ufodir, filen, exists, fobject=True)
                if lchanged:
                    filen = "lib" + newfile + ".plist"
                    logger.log("Writing updated lib.plist to " + filen, "P")
                    exists = True if os.path.isfile(os.path.join(font.ufodir, filen)) else False
                    UFO.writeXMLobject(lib, font.outparams, font.ufodir, filen, exists, fobject=True)

    if fieldscopied :
        message = "After updating, UFOsyncMeta will need to be re-run to validate these fields" if reportonly else "Re-run UFOsyncMeta to validate these fields"
        logger.log("*** Some fields were missing and so copied from " + mastertext + ". " + message, "P")

    return
Esempio n. 5
0
def doit(args):
    font = args.font
    pfn = args.primaryname
    orgid = args.orgid
    fontlog = args.fontlog
    logger = args.logger
    ofn = args.output

    # Find & process info required in the UFO

    fi = font.fontinfo

    ufofields = {}
    missing = None
    for field in ("versionMajor", "versionMinor", "openTypeNameManufacturer", "openTypeNameManufacturerURL",
                  "openTypeNameLicense", "copyright", "trademark"):
        if field in fi:
            ufofields[field] = fi[field][1].text
        elif field != 'trademark':      # trademark is no longer required
            missing = field if missing is None else missing + ", " + field
    if missing is not None: logger.log("Field(s) missing from fontinfo.plist: " + missing, "S")

    version = ufofields["versionMajor"] + "." + ufofields["versionMinor"].zfill(3)

    # Find & process WOFF fields if present in the UFO

    missing = None
    ufowoff = {"woffMetadataCredits": "credits", "woffMetadataDescription": "text"} # Field, dict name
    for field in ufowoff:
        elem = fi[field][1] if field in fi else None
        if elem is None:
            missing = field if missing is None else missing + ", " + field
            ufofields[field] = None
        else:
            ufofields[field] = fi.getval(field)[ufowoff[field]]

    # Process --populateufowoff setting, if present
    if args.populateufowoff:
        if missing != "woffMetadataCredits, woffMetadataDescription" and not args.force:
            logger.log("Data exists in the UFO for woffMetadataCredits or woffMetadataDescription", "W")
            logger.log("Use --force to force update of those fields, or rerun without --populateufowoff", "S")

    if args.populateufowoff or missing is not None:
        # if missing: logger.log("WOFF field(s) missing from fontinfo.plist will be generated from FONTLOG.txt: " + missing, "W")
        # Parse the fontlog file
        (section, match) = readuntil(fontlog, ("Basic Font Information",))  # Skip until start of "Basic Font Information" section
        if match is None: logger.log("No 'Basic Font Information' section in fontlog", "S")
        (fldescription, match) = readuntil(fontlog, ("Information for C", "Acknowledgements"))  # Desciption ends when first of these sections is found
        fldescription = [{"text": fldescription}]
        if match == "Information for C": (section, match) = readuntil(fontlog, ("Acknowledgements",))  # If Info... section present then skip on to Acknowledgements
        if match is None: logger.log("No 'Acknowledgements' section in fontlog", "S")
        (acksection, match) = readuntil(fontlog, ("No match needed!!",))

        flcredits = []
        credit = {}
        acktype = ""
        flog2woff = {"N": "name", "E": "Not used", "W": "url", "D": "role"}
        for line in acksection.splitlines():
            if line == "":
                if acktype != "":  # Must be at the end of a credit section
                    if "name" in credit:
                        flcredits.append(credit)
                    else:
                        logger.log("Credit section found with no N: entry", "E")
                credit = {}
                acktype = ""
            else:
                match = re.match("^([NEWD]): (.*)", line)
                if match is None:
                    if acktype == "N": credit["name"] = credit["name"] + line  # Name entries can be multiple lines
                else:
                    acktype = match.group(1)
                    if acktype in credit:
                        logger.log("Multiple " + acktype + " entries found in a credit section", "E")
                    else:
                        credit[flog2woff[acktype]] = match.group(2)
        if flcredits == []: logger.log("No credits found in fontlog", "S")
        if args.populateufowoff:
            ufofields["woffMetadataDescription"] = fldescription # Force fontlog values to be used writing metatdata.xml later
            ufofields["woffMetadataCredits"] = flcredits
            # Create xml strings and update fontinfo
            xmlstring = "<dict><key>text</key><array><dict><key>text</key><string>" + textprotect(fldescription[0]["text"]) \
                  + "</string></dict></array></dict>"
            fi.setelem("woffMetadataDescription", ET.fromstring(xmlstring))

            xmlstring = "<dict><key>credits</key><array>"
            for credit in flcredits:
                xmlstring += '<dict><key>name</key><string>' + textprotect(credit["name"]) + '</string>'
                if "url" in credit: xmlstring += '<key>url</key><string>' + textprotect(credit["url"]) + '</string>'
                if "role" in credit: xmlstring += '<key>role</key><string>' + textprotect(credit["role"]) + '</string>'
                xmlstring += '</dict>'
            xmlstring += '</array></dict>'
            fi.setelem("woffMetadataCredits", ET.fromstring(xmlstring))

            fi.setval("openTypeHeadCreated", "string", datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S"))
            logger.log("Writing updated fontinfo.plist with values from FONTLOG.txt", "P")
            exists = True if os.path.isfile(os.path.join(font.ufodir, "fontinfo.plist")) else False
            UFO.writeXMLobject(fi, font.outparams, font.ufodir, "fontinfo.plist", exists, fobject=True)

    description = ufofields["woffMetadataDescription"]
    if description == None: description = fldescription
    credits = ufofields["woffMetadataCredits"]
    if credits == None : credits = flcredits

    # Construct output file name
    (folder, ufoname) = os.path.split(font.ufodir)
    filename = os.path.join(folder, pfn + "-WOFF-metadata.xml") if ofn is None else ofn
    try:
        file = open(filename, "w")
    except Exception as e:
        logger.log("Unable to open " + filename + " for writing:\n" + str(e), "S")
    logger.log("Writing to : " + filename, "P")

    file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    file.write('<metadata version="1.0">\n')
    file.write('  <uniqueid id="' + orgid + '.' + pfn + '.' + version + '" />\n')
    file.write('  <vendor name="' + attrprotect(ufofields["openTypeNameManufacturer"]) + '" url="'
               + attrprotect(ufofields["openTypeNameManufacturerURL"]) + '" />\n')
    file.write('  <credits>\n')
    for credit in credits:
        file.write('    <credit\n')
        file.write('      name="' + attrprotect(credit["name"]) + '"\n')
        if "url" in credit: file.write('      url="' + attrprotect(credit["url"]) + '"\n')
        if "role" in credit: file.write('      role="' + attrprotect(credit["role"]) + '"\n')
        file.write('    />\n')
    file.write('  </credits>\n')

    file.write('  <description>\n')
    file.write('    <text lang="en">\n')
    for entry in description:
        for line in entry["text"].splitlines():
            file.write('      ' + textprotect(line) + '\n')
    file.write('    </text>\n')
    file.write('  </description>\n')

    file.write('  <license url="http://scripts.sil.org/OFL" id="org.sil.ofl.1.1">\n')
    file.write('    <text lang="en">\n')
    for line in ufofields["openTypeNameLicense"].splitlines(): file.write('      ' + textprotect(line) + '\n')
    file.write('    </text>\n')
    file.write('  </license>\n')

    file.write('  <copyright>\n')
    file.write('    <text lang="en">\n')
    for line in ufofields["copyright"].splitlines(): file.write('      ' + textprotect(line) + '\n')
    file.write('    </text>\n')
    file.write('  </copyright>\n')

    if 'trademark' in ufofields:
        file.write('  <trademark>\n')
        file.write('    <text lang="en">' + textprotect(ufofields["trademark"]) + '</text>\n')
        file.write('  </trademark>\n')

    file.write('</metadata>')

    file.close()
Esempio n. 6
0
def doit(args) :

    fields = ["copyright", "openTypeNameDescription", "openTypeNameDesigner", "openTypeNameDesignerURL", "openTypeNameLicense", # General feilds
                "openTypeNameLicenseURL", "openTypeNameManufacturer", "openTypeNameManufacturerURL", "openTypeOS2CodePageRanges",
                "openTypeOS2UnicodeRanges", "openTypeOS2VendorID", "trademark",
                "openTypeNameVersion", "versionMajor", "versionMinor", # Version fields
                "ascender", "descender", "openTypeHheaAscender", "openTypeHheaDescender", "openTypeHheaLineGap", # Design fields
                "openTypeOS2TypoAscender", "openTypeOS2TypoDescender", "openTypeOS2TypoLineGap", "openTypeOS2WinAscent", "openTypeOS2WinDescent"]
    libfields = ["public.postscriptNames", "public.glyphOrder", "com.schriftgestaltung.glyphOrder"]

    fromfont = args.fromfont
    tofont = args.tofont
    logger = args.logger
    reportonly = args.reportonly

    updatemessage = " to be updated: " if reportonly else " updated: "
    precision = fromfont.paramset["precision"]
    # Increase screen logging level to W unless specific level supplied on command-line
    if not(args.quiet or "scrlevel" in args.paramsobj.sets["command line"]) : logger.scrlevel = "W"

    # Process fontinfo.plist
    ffi = fromfont.fontinfo
    tfi = tofont.fontinfo
    fupdated = False
    for field in fields:
        if field in ffi :
            felem = ffi[field][1]
            ftag = felem.tag
            ftext = felem.text
            if ftag is 'real' : ftext = processnum(ftext,precision)
            message = field + updatemessage

            if field in tfi : # Need to compare values to see if update is needed
                telem = tfi[field][1]
                ttag = telem.tag
                ttext = telem.text
                if ttag is 'real' : ttext = processnum(ttext,precision)

                if ftag in ("real", "integer", "string") :
                    if ftext != ttext :
                        if field == "openTypeNameLicense" : # Too long to display all
                            addmess = " Old: '" + ttext[0:80] + "...' New: '" + ftext[0:80] + "...'"
                        else: addmess = " Old: '" + ttext + "' New: '" + str(ftext) + "'"
                        telem.text = ftext
                        logger.log(message + addmess, "W")
                        fupdated = True
                elif ftag in ("true, false") :
                    if ftag != ttag :
                        fti.setelem(field, ET.fromstring("<" + ftag + "/>"))
                        logger.log(message + " Old: '" + ttag + "' New: '" + str(ftag) + "'", "W")
                        fupdated = True
                elif ftag == "array" : # Assume simple array with just values to compare
                    farray = []
                    for subelem in felem : farray.append(subelem.text)
                    tarray = []
                    for subelem in telem : tarray.append(subelem.text)
                    if farray != tarray :
                        tfi.setelem(field, ET.fromstring(ET.tostring(felem)))
                        logger.log(message + "Some values different Old: " + str(tarray) + " New: " + str(farray), "W")
                        fupdated = True
                else : logger.log("Non-standard fontinfo field type: "+ ftag + " in " + fontname, "S")
            else :
                tfi.addelem(field, ET.fromstring(ET.tostring(felem)))
                logger.log(message + "is missing from destination font so will be copied from source font", "W")
                fupdated = True
        else: # Field not in from font
            if field in tfi :
                logger.log( field +  " is missing from source font but present in destination font", "E")
            else :
                logger.log( field +  " is in neither font", "W")

    # Process lib.plist - currently just public.postscriptNames and glyph order fields which are all simple dicts or arrays
    flib = fromfont.lib
    tlib = tofont.lib
    lupdated = False
    for field in libfields:
        action = None
        if field in flib:
            if field in tlib:  # Need to compare values to see if update is needed
                if flib.getval(field) != tlib.getval(field):
                    action = "Updatefield"
            else:
                action = "Copyfield"
        else:
            action = "Error" if field == ("public.GlyphOrder", "public.postscriptNames") else "Warn"
            issue = field + " not in source font lib.plist"

        # Process the actions, create log messages etc
        if action is None or action == "Ignore":
            pass
        elif action == "Warn":
            logger.log(field + " needs manual correction: " + issue, "W")
        elif action == "Error":
            logger.log(field + " needs manual correction: " + issue, "E")
        elif action in ("Updatefield", "Copyfield"):  # Updating actions
            lupdated = True
            message = field + updatemessage
            if action == "Copyfield":
                message = message + "is missing so will be copied from source font"
                tlib.addelem(field, ET.fromstring(ET.tostring(flib[field][1])))
            elif action == "Updatefield":
                message = message + "Some values different"
                tlib.setelem(field, ET.fromstring(ET.tostring(flib[field][1])))
            logger.log(message, "W")
        else:
            logger.log("Uncoded action: " + action + " - oops", "X")

    # Now update on disk
    if not reportonly:
        if fupdated:
            logger.log("Writing updated fontinfo.plist", "P")
            UFO.writeXMLobject(tfi, tofont.outparams, tofont.ufodir, "fontinfo.plist", True, fobject=True)
        if lupdated:
            logger.log("Writing updated lib.plist", "P")
            UFO.writeXMLobject(tlib, tofont.outparams, tofont.ufodir, "lib.plist", True, fobject=True)

    return
Esempio n. 7
0
 def write(self, plistn):
     filen = plistn + self.newfile + ".plist"
     self.logger.log("Writing updated " + plistn + ".plist to " + filen, "P")
     exists = True if os.path.isfile(os.path.join(self.ufodir, filen)) else False
     plist = getattr(self, plistn)
     UFO.writeXMLobject(plist, self.outparams, self.ufodir, filen, exists, fobject=True)
Esempio n. 8
0
def doit(args) :
    standardstyles = ["Regular", "Italic", "Bold", "BoldItalic"]
    finfoignore = ["openTypeHeadCreated", "openTypeOS2Panose", "postscriptBlueScale", "postscriptBlueShift",
                   "postscriptBlueValues", "postscriptOtherBlues", "postscriptStemSnapH", "postscriptStemSnapV", "postscriptForceBold"]
    libfields = ["public.postscriptNames", "public.glyphOrder", "com.schriftgestaltung.glyphOrder"]

    font = args.ifont
    logger = args.logger
    singlefont = args.single
    mfont = args.master
    newfile = "_new" if args.new else ""
    reportonly = args.reportonly
    updatemessage = " to be updated: " if reportonly else " updated: "
    params = args.paramsobj
    precision = font.paramset["precision"]

    # Increase screen logging level to W unless specific level supplied on command-line
    if not(args.quiet or "scrlevel" in params.sets["command line"]) : logger.scrlevel = "W"

    # Process UFO name
    (path,base) = os.path.split(font.ufodir)
    (base,ext) = os.path.splitext(base)
    if '-' not in base : logger.log("Non-standard UFO name - must be <family>-<style>", "S")
    (family,style) = base.split('-')

    styles = [style]
    fonts = {}
    fonts[style] = font

    # Process single and master settings
    if singlefont :
        if mfont :
            mastertext = "Master" # Used in log messages
        else : # Check against Regular font from same family
            mfont = openfont(params, path, family, "Regular")
            if mfont is None : logger.log("No regular font to check against - use -m to specify master font", "S")
            mastertext = "Regular"
            fonts["Regular"] =mfont
    else : # Supplied font must be Regular
        if mfont : logger.log("-m --master must only be used with -s --single", "S")
        if style != "Regular" : logger.log("Must specify a Regular font unless -s is used", "S")
        mastertext = "Regular"
        mfont = font

    # Check for required fields in master font
    mfinfo = mfont.fontinfo
    if "familyName" in mfinfo :
        spacedfamily = mfinfo["familyName"][1].text
    else:
        logger.log("No familyName field in " + mastertext, "S")
    if "openTypeNameManufacturer" in mfinfo :
        manufacturer = mfinfo["openTypeNameManufacturer"][1].text
    else:
        logger.log("No openTypeNameManufacturer field in " + mastertext, "S")
    mlib = mfont.lib

    # Open the remaining fonts in the family
    if not singlefont :
        for style in standardstyles :
            if not style in fonts :
                fonts[style] = openfont(params, path, family, style) # Will return None if font does not exist
                if fonts[style] is not None : styles.append(style)

    # Process fonts
    psuniqueidlist = []
    fieldscopied = False
    for style in styles :
        font = fonts[style]
        if font.UFOversion != "2" : logger.log("This script only works with UFO 2 format fonts","S")

        fontname = family + "-" + style
        spacedstyle = "Bold Italic" if style == "BoldItalic" else style
        spacedname = spacedfamily + " " + spacedstyle
        logger.log("************ Processing " + fontname, "P")

        ital = True if "Italic" in style else False
        bold = True if "Bold" in style else False

        # Process fontinfo.plist
        finfo=font.fontinfo
        fieldlist = list(set(finfo) | set(mfinfo)) # Need all fields from both to detect missing fields
        fchanged = False

        for field in fieldlist:
            action = None; issue = ""; newval = ""
            if field in finfo :
                elem = finfo[field][1]
                tag = elem.tag
                text = elem.text
                if text is None : text = ""
                if tag == "real" : text = processnum(text,precision)
            # Field-specific actions

            if field not in finfo :
                if field not in finfoignore : action = "Copyfield"
            elif field == "italicAngle" :
                if ital and text == "0" :
                    issue = "is zero"
                    action = "Warn"
                if not ital and text != "0" :
                    issue = "is non-zero"
                    newval = 0
                    action = "Update"
            elif field == "openTypeNameUniqueID" :
                newval = manufacturer + ": " + spacedname + ": " + datetime.now().strftime("%Y")
                if text != newval :
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "openTypeOS2WeightClass" :
                if bold and text != "700" :
                    issue = "is not 700"
                    newval = 700
                    action = "Update"
                if not bold and text != "400" :
                    issue = "is not 400"
                    newval = 400
                    action = "Update"
            elif field == "postscriptFontName" :
                if text != fontname :
                    newval = fontname
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "postscriptFullName" :
                if text != spacedname :
                    newval = spacedname
                    issue = "Incorrect value"
                    action = "Update"
            elif field == "postscriptUniqueID" :
                if text in psuniqueidlist :
                    issue = "has same value as another font: " + text
                    action = "Warn"
                else :
                    psuniqueidlist.append(text)
            elif field == "postscriptWeightName" :
                newval = 'bold' if bold else 'regular'
                if text != newval :
                    issue = "Incorrect value"
                    action = 'Update'
            elif field == "styleMapStyleName" :
                if text != spacedstyle.lower() :
                    newval = spacedstyle.lower()
                    issue = "Incorrect value"
                    action = "Update"
            elif field in ("styleName", "openTypeNamePreferredSubfamilyName") :
                if text != spacedstyle :
                    newval = spacedstyle
                    issue = "Incorrect value"
                    action = "Update"
            elif field in finfoignore :
                action = "Ignore"
            # Warn for fields in this font but not master
            elif field not in mfinfo :
                issue = "is in " + spacedstyle + " but not in " + mastertext
                action = "Warn"
            # for all other fields, sync values from master
            else :
                melem = mfinfo[field][1]
                mtag = melem.tag
                mtext = melem.text
                if mtext is None : mtext = ""
                if mtag is 'real' : mtext = processnum(mtext,precision)
                if tag in ("real", "integer", "string") :
                    if mtext != text :
                        issue = "does not match " + mastertext + " value"
                        newval = mtext
                        action = "Update"
                elif tag in ("true, false") :
                    if tag != mtag :
                        issue = "does not match " + mastertext + " value"
                        action = "FlipBoolean"
                elif tag == "array" : # Assume simple array with just values to compare
                    marray = mfinfo.getval(field)
                    array = finfo.getval(field)
                    if array != marray: action = "CopyArray"
                else : logger.log("Non-standard fontinfo field type in " + fontname, "X")

            # Now process the actions, create log messages etc
            if action is None or action == "Ignore" :
                pass
            elif action == "Warn" :
                logger.log(field + " needs manual correction: " + issue, "W")
            elif action == "Error" :
                logger.log(field + " needs manual correction: " + issue, "E")
            elif action in ("Update", "FlipBoolean", "Copyfield", "CopyArray") : # Updating actions
                fchanged = True
                message = field + updatemessage
                if action == "Update" :
                    message = message + issue + " Old: '" + text + "' New: '" + str(newval) + "'"
                    elem.text = newval
                elif action == "FlipBoolean" :
                    newval = "true" if tag == "false" else "false"
                    message = message + issue + " Old: '" + tag + "' New: '" + newval + "'"
                    finfo.setelem(field, ET.fromstring("<" + newval + "/>"))
                elif action == "Copyfield" :
                    message = message + "is missing so will be copied from " + mastertext
                    fieldscopied = True
                    finfo.addelem(field, ET.fromstring(ET.tostring(mfinfo[field][1])))
                elif action == "CopyArray" :
                    message = message + "Some values different Old: " + str(array) + " New: " + str(marray)
                    finfo.setelem(field, ET.fromstring(ET.tostring(melem)))
                logger.log(message, "W")
            else:
                logger.log("Uncoded action: " + action + " - oops", "X")

        # Process lib.plist - currently just public.postscriptNames and glyph order fields which are all simple dicts or arrays
        lib = font.lib
        lchanged = False

        for field in libfields:
            # Check the values
            action = None; issue = ""; newval = ""
            if field in mlib:
                if field in lib:
                    if lib.getval(field) != mlib.getval(field):  # will only work for arrays or dicts with simple values
                        action = "Updatefield"
                else:
                    action = "Copyfield"
            else:
                action = "Error" if field == ("public.GlyphOrder", "public.postscriptNames") else "Warn"
                issue = field + " not in " + mastertext + " lib.plist"

            # Process the actions, create log messages etc
            if action is None or action == "Ignore":
                pass
            elif action == "Warn":
                logger.log(field + " needs manual correction: " + issue, "W")
            elif action == "Error":
                logger.log(field + " needs manual correction: " + issue, "E")
            elif action in ("Updatefield", "Copyfield"):  # Updating actions
                lchanged = True
                message = field + updatemessage
                if action == "Copyfield":
                    message = message + "is missing so will be copied from " + mastertext
                    lib.addelem(field, ET.fromstring(ET.tostring(mlib[field][1])))
                elif action == "Updatefield":
                    message = message + "Some values different"
                    lib.setelem(field, ET.fromstring(ET.tostring(mlib[field][1])))
                logger.log(message, "W")
            else:
                logger.log("Uncoded action: " + action + " - oops", "X")

        # Now update on disk
        if not reportonly:
            if args.normalize:
                font.write(os.path.join(path, family + "-" + style + newfile + ".ufo"))
            else:  # Just update fontinfo and lib
                if fchanged:
                    filen = "fontinfo" + newfile + ".plist"
                    logger.log("Writing updated fontinfo to " + filen, "P")
                    exists = True if os.path.isfile(os.path.join(font.ufodir, filen)) else False
                    UFO.writeXMLobject(finfo, font.outparams, font.ufodir, filen, exists, fobject=True)
                if lchanged:
                    filen = "lib" + newfile + ".plist"
                    logger.log("Writing updated lib.plist to " + filen, "P")
                    exists = True if os.path.isfile(os.path.join(font.ufodir, filen)) else False
                    UFO.writeXMLobject(lib, font.outparams, font.ufodir, filen, exists, fobject=True)

    if fieldscopied :
        message = "After updating, UFOsyncMeta will need to be re-run to validate these fields" if reportonly else "Re-run UFOsyncMeta to validate these fields"
        logger.log("*** Some fields were missing and so copied from " + mastertext + ". " + message, "P")

    return
Esempio n. 9
0
def doit(args):

    fields = [
        "copyright",
        "openTypeNameDescription",
        "openTypeNameDesigner",
        "openTypeNameDesignerURL",
        "openTypeNameLicense",  # General feilds
        "openTypeNameLicenseURL",
        "openTypeNameManufacturer",
        "openTypeNameManufacturerURL",
        "openTypeOS2CodePageRanges",
        "openTypeOS2UnicodeRanges",
        "openTypeOS2VendorID",
        "trademark",
        "year",
        "openTypeNameVersion",
        "versionMajor",
        "versionMinor",  # Version fields
        "ascender",
        "descender",
        "openTypeHheaAscender",
        "openTypeHheaDescender",
        "openTypeHheaLineGap",  # Design fields
        "openTypeOS2TypoAscender",
        "openTypeOS2TypoDescender",
        "openTypeOS2TypoLineGap",
        "openTypeOS2WinAscent",
        "openTypeOS2WinDescent"
    ]

    fromfont = args.fromfont
    tofont = args.tofont
    logger = args.logger
    reportonly = args.reportonly

    ffi = fromfont.fontinfo
    tfi = tofont.fontinfo
    updatemessage = " to be updated: " if reportonly else " updated: "
    precision = fromfont.paramset["precision"]
    # Increase screen logging level to W unless specific level supplied on command-line
    if not (args.quiet or "scrlevel" in args.paramsobj.sets["command line"]):
        logger.scrlevel = "W"

    updated = False
    for field in fields:
        if field in ffi:
            felem = ffi[field][1]
            ftag = felem.tag
            ftext = felem.text
            if ftag is 'real': ftext = processnum(ftext, precision)
            message = field + updatemessage

            if field in tfi:  # Need to compare values to see if update is needed
                telem = tfi[field][1]
                ttag = telem.tag
                ttext = telem.text
                if ttag is 'real': ttext = processnum(ttext, precision)

                if ftag in ("real", "integer", "string"):
                    if ftext != ttext:
                        if field == "openTypeNameLicense":  # Too long to display all
                            addmess = " Old: '" + ttext[
                                0:80] + "...' New: '" + ftext[0:80] + "...'"
                        else:
                            addmess = " Old: '" + ttext + "' New: '" + str(
                                ftext) + "'"
                        telem.text = ftext
                        logger.log(message + addmess, "W")
                        updated = True
                elif ftag in ("true, false"):
                    if ftag != ttag:
                        fti.setelem(field, ET.fromstring("<" + ftag + "/>"))
                        logger.log(
                            message + " Old: '" + ttag + "' New: '" +
                            str(ftag) + "'", "W")
                        updated = True
                elif ftag == "array":  # Assume simple array with just values to compare
                    farray = []
                    for subelem in felem:
                        farray.append(subelem.text)
                    tarray = []
                    for subelem in telem:
                        tarray.append(subelem.text)
                    if farray != tarray:
                        tfi.setelem(field, ET.fromstring(ET.tostring(felem)))
                        logger.log(
                            message + "Some values different Old: " +
                            str(tarray) + " New: " + str(farray), "W")
                        updated = True
                else:
                    logger.log(
                        "Non-standard fontinfo field type: " + ftag + " in " +
                        fontname, "X")
            else:
                tfi.addelem(field, ET.fromstring(ET.tostring(felem)))
                logger.log(
                    message +
                    "is missing from destination font so will be copied from source font",
                    "W")
                updated = True
        else:  # Field not in from font
            if field in tfi:
                logger.log(
                    field +
                    " is missing from source font but present in destination font",
                    "E")
            else:
                logger.log(field + " is in neither font", "W")

    # Now update on disk
    if not reportonly and updated:
        logger.log("Writing updated fontinfo.plist", "P")
        UFO.writeXMLobject(tfi,
                           tofont,
                           tofont.ufodir,
                           "fontinfo.plist",
                           True,
                           fobject=True)

    return
Esempio n. 10
0
def doit(args) :

    fields = ["copyright", "openTypeNameDescription", "openTypeNameDesigner", "openTypeNameDesignerURL", "openTypeNameLicense", # General feilds
                "openTypeNameLicenseURL", "openTypeNameManufacturer", "openTypeNameManufacturerURL", "openTypeOS2CodePageRanges",
                "openTypeOS2UnicodeRanges", "openTypeOS2VendorID", "trademark",
                "openTypeNameVersion", "versionMajor", "versionMinor", # Version fields
                "ascender", "descender", "openTypeHheaAscender", "openTypeHheaDescender", "openTypeHheaLineGap", # Design fields
                "openTypeOS2TypoAscender", "openTypeOS2TypoDescender", "openTypeOS2TypoLineGap", "openTypeOS2WinAscent", "openTypeOS2WinDescent"]
    libfields = ["public.postscriptNames", "public.glyphOrder", "com.schriftgestaltung.glyphOrder"]

    fromfont = args.fromfont
    tofont = args.tofont
    logger = args.logger
    reportonly = args.reportonly

    updatemessage = " to be updated: " if reportonly else " updated: "
    precision = fromfont.paramset["precision"]
    # Increase screen logging level to W unless specific level supplied on command-line
    if not(args.quiet or "scrlevel" in args.paramsobj.sets["command line"]) : logger.scrlevel = "W"

    # Process fontinfo.plist
    ffi = fromfont.fontinfo
    tfi = tofont.fontinfo
    fupdated = False
    for field in fields:
        if field in ffi :
            felem = ffi[field][1]
            ftag = felem.tag
            ftext = felem.text
            if ftag is 'real' : ftext = processnum(ftext,precision)
            message = field + updatemessage

            if field in tfi : # Need to compare values to see if update is needed
                telem = tfi[field][1]
                ttag = telem.tag
                ttext = telem.text
                if ttag is 'real' : ttext = processnum(ttext,precision)

                if ftag in ("real", "integer", "string") :
                    if ftext != ttext :
                        if field == "openTypeNameLicense" : # Too long to display all
                            addmess = " Old: '" + ttext[0:80] + "...' New: '" + ftext[0:80] + "...'"
                        else: addmess = " Old: '" + ttext + "' New: '" + str(ftext) + "'"
                        telem.text = ftext
                        logger.log(message + addmess, "W")
                        fupdated = True
                elif ftag in ("true, false") :
                    if ftag != ttag :
                        fti.setelem(field, ET.fromstring("<" + ftag + "/>"))
                        logger.log(message + " Old: '" + ttag + "' New: '" + str(ftag) + "'", "W")
                        fupdated = True
                elif ftag == "array" : # Assume simple array with just values to compare
                    farray = []
                    for subelem in felem : farray.append(subelem.text)
                    tarray = []
                    for subelem in telem : tarray.append(subelem.text)
                    if farray != tarray :
                        tfi.setelem(field, ET.fromstring(ET.tostring(felem)))
                        logger.log(message + "Some values different Old: " + str(tarray) + " New: " + str(farray), "W")
                        fupdated = True
                else : logger.log("Non-standard fontinfo field type: "+ ftag + " in " + fontname, "S")
            else :
                tfi.addelem(field, ET.fromstring(ET.tostring(felem)))
                logger.log(message + "is missing from destination font so will be copied from source font", "W")
                fupdated = True
        else: # Field not in from font
            if field in tfi :
                logger.log( field +  " is missing from source font but present in destination font", "E")
            else :
                logger.log( field +  " is in neither font", "W")

    # Process lib.plist - currently just public.postscriptNames and glyph order fields which are all simple dicts or arrays
    flib = fromfont.lib
    tlib = tofont.lib
    lupdated = False
    for field in libfields:
        action = None
        if field in flib:
            if field in tlib:  # Need to compare values to see if update is needed
                if flib.getval(field) != tlib.getval(field):
                    action = "Updatefield"
            else:
                action = "Copyfield"
        else:
            action = "Error" if field == ("public.GlyphOrder", "public.postscriptNames") else "Warn"
            issue = field + " not in source font lib.plist"

        # Process the actions, create log messages etc
        if action is None or action == "Ignore":
            pass
        elif action == "Warn":
            logger.log(field + " needs manual correction: " + issue, "W")
        elif action == "Error":
            logger.log(field + " needs manual correction: " + issue, "E")
        elif action in ("Updatefield", "Copyfield"):  # Updating actions
            lupdated = True
            message = field + updatemessage
            if action == "Copyfield":
                message = message + "is missing so will be copied from source font"
                tlib.addelem(field, ET.fromstring(ET.tostring(flib[field][1])))
            elif action == "Updatefield":
                message = message + "Some values different"
                tlib.setelem(field, ET.fromstring(ET.tostring(flib[field][1])))
            logger.log(message, "W")
        else:
            logger.log("Uncoded action: " + action + " - oops", "X")

    # Now update on disk
    if not reportonly:
        if fupdated:
            logger.log("Writing updated fontinfo.plist", "P")
            UFO.writeXMLobject(tfi, tofont.outparams, tofont.ufodir, "fontinfo.plist", True, fobject=True)
        if lupdated:
            logger.log("Writing updated lib.plist", "P")
            UFO.writeXMLobject(tlib, tofont.outparams, tofont.ufodir, "lib.plist", True, fobject=True)

    return