def writeValue(self, value, forKey=None, forType=None): if isinstance(value, (list, glyphsLib.classes.Proxy)): if isinstance(value, glyphsLib.classes.UserDataProxy): self.writeUserData(value) else: self.writeArray(value) elif hasattr(value, "plistValue"): value = value.plistValue() if value is not None: self.file.write(value) elif isinstance(value, (dict, OrderedDict, glyphsLib.classes.GSBase)): self.writeDict(value) elif type(value) == float: self.file.write(floatToString(value, 5)) elif type(value) == int: self.file.write(unicode(value)) elif type(value) == bool: if value: self.file.write("1") else: self.file.write("0") elif type(value) == datetime.datetime: self.file.write("\"%s +0000\"" % str(value)) else: if forKey != "unicode": value = feature_syntax_encode(value) self.file.write(unicode(value))
def writeValue(self, value, forKey=None, forType=None): if hasattr(value, "plistValue"): value = value.plistValue() if value is not None: self.file.write(value) elif forKey == "color" and hasattr(value, "__iter__"): # We have to write color tuples on one line or Glyphs 2.4.x # misreads it. self.file.write(unicode(tuple(value))) elif isinstance(value, (list, glyphsLib.classes.Proxy)): if isinstance(value, glyphsLib.classes.UserDataProxy): self.writeUserData(value) else: self.writeArray(value) elif isinstance(value, (dict, OrderedDict, glyphsLib.classes.GSBase)): self.writeDict(value) elif type(value) == float: self.file.write(floatToString(value, 5)) elif type(value) == int: self.file.write(unicode(value)) elif type(value) == bool: if value: self.file.write("1") else: self.file.write("0") elif type(value) == datetime.datetime: self.file.write("\"%s +0000\"" % str(value)) else: value = unicode(value) if forKey != "unicode": value = escape_string(value) self.file.write(value)
def normalizeKerningKey(value): """ Normalizes kerning key. * **value** must be a ``tuple`` or ``list``. * **value** must contain only two members. * **value** items must be :ref:`type-string`. * **value** items must be at least one character long. * Returned value will be a two member ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Kerning key must be a tuple instance, not %s." % type(value).__name__) if len(value) != 2: raise ValueError("Kerning key must be a tuple containing two items, " "not %d." % len(value)) for v in value: if not isinstance(v, basestring): raise TypeError("Kerning key items must be strings, not %s." % type(v).__name__) if len(v) < 1: raise ValueError("Kerning key items must be one character long") if value[0].startswith("public.") and not value[0].startswith( "public.kern1."): raise ValueError("Left Kerning key group must start with " "public.kern1.") if value[1].startswith("public.") and not value[1].startswith( "public.kern2."): raise ValueError("Right Kerning key group must start with " "public.kern2.") return tuple([unicode(v) for v in value])
def normalizeSegmentType(value): """ Normalizes segment type. * **value** must be a :ref:`type-string`. * **value** must be one of the following: +--------+ | move | +--------+ | line | +--------+ | curve | +--------+ | qcurve | +--------+ * Returned value will be an unencoded ``unicode`` string. """ allowedTypes = ['move', 'line', 'curve', 'qcurve'] if not isinstance(value, basestring): raise TypeError("Segment type must be a string, not %s." % type(value).__name__) if value not in allowedTypes: raise ValueError("Segment type must be '%s'; not %r." % ("', '".join(allowedTypes), value)) return unicode(value)
def normalizeLayerOrder(value, font): """ Normalizes layer order. ** **value** must be a ``tuple`` or ``list``. * **value** items must normalize as layer names with :func:`normalizeLayerName`. * **value** must contain layers that exist in **font**. * **value** must not contain duplicate layers. * Returned ``tuple`` will be unencoded ``unicode`` strings for each layer name. """ if not isinstance(value, (tuple, list)): raise TypeError("Layer order must be a list, not %s." % type(value).__name__) for v in value: normalizeLayerName(v) fontLayers = [layer.name for layer in font.layers] for name in value: if name not in fontLayers: raise ValueError("Layer must exist in font. %s does not exist " "in font.layers." % name) duplicates = [v for v, count in Counter(value).items() if count > 1] if len(duplicates) != 0: raise ValueError("Duplicate layers are not allowed. Layer name(s) " "'%s' are duplicate(s)." % ", ".join(duplicates)) return tuple([unicode(v) for v in value])
def normalizeKerningKey(value): """ Normalizes kerning key. * **value** must be a ``tuple`` or ``list``. * **value** must contain only two members. * **value** items must be :ref:`type-string`. * **value** items must be at least one character long. * Returned value will be a two member ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Kerning key must be a tuple instance, not %s." % type(value).__name__) if len(value) != 2: raise ValueError("Kerning key must be a tuple containing two items, " "not %d." % len(value)) for v in value: if not isinstance(v, basestring): raise TypeError("Kerning key items must be strings, not %s." % type(v).__name__) if len(v) < 1: raise ValueError("Kerning key items must be one character long") if value[0].startswith( "public.") and not value[0].startswith("public.kern1."): raise ValueError("Left Kerning key group must start with " "public.kern1.") if value[1].startswith( "public.") and not value[1].startswith("public.kern2."): raise ValueError("Right Kerning key group must start with " "public.kern2.") return tuple([unicode(v) for v in value])
def normalizeLayerOrder(value, font): """ Normalizes layer order. ** **value** must be a ``tuple`` or ``list``. * **value** items must normalize as layer names with :func:`normalizeLayerName`. * **value** must contain layers that exist in **font**. * **value** must not contain duplicate layers. * Returned ``tuple`` will be unencoded ``unicode`` strings for each layer name. """ if not isinstance(value, (tuple, list)): raise TypeError("Layer order must be a list, not %s." % type(value).__name__) for v in value: normalizeLayerName(v) fontLayers = [layer.name for layer in font.layers] for name in value: if name not in fontLayers: raise ValueError("Layer must exist in font. %s does not exist " "in font.layers." % name) duplicates = [v for v, count in Counter(value).items() if count > 1] if len(duplicates) != 0: raise ValueError("Duplicate layers are not allowed. Layer name(s) " "'%s' are duplicate(s)." % ", ".join(duplicates)) return tuple([unicode(v) for v in value])
def normalizeSegmentType(value): """ Normalizes segment type. * **value** must be a :ref:`type-string`. * **value** must be one of the following: +--------+ | move | +--------+ | line | +--------+ | curve | +--------+ | qcurve | +--------+ * Returned value will be an unencoded ``unicode`` string. """ allowedTypes = ['move', 'line', 'curve', 'qcurve'] if not isinstance(value, basestring): raise TypeError("Segment type must be a string, not %s." % type(value).__name__) if value not in allowedTypes: raise ValueError("Segment type must be '%s'; not %r." % ("', '".join(allowedTypes), value)) return unicode(value)
def normalizeIdentifier(value): """ Normalizes identifier. * **value** must be an :ref:`type-string` or `None`. * **value** must not be longer than 100 characters. * **value** must not contain a character out the range of 0x20 - 0x7E. * Returned value is an unencoded ``unicode`` string. """ if value is None: return value if not isinstance(value, basestring): raise TypeError("Identifiers must be strings, not %s." % type(value).__name__) if len(value) == 0: raise ValueError("The identifier string is empty.") if len(value) > 100: raise ValueError("The identifier string has a length (%d) greater " "than the maximum allowed (100)." % len(value)) for c in value: v = ord(c) if v < 0x20 or v > 0x7E: raise ValueError("The identifier string ('%s') contains a " "character out size of the range 0x20 - 0x7E." % value) return unicode(value)
def normalizeIdentifier(value): """ Normalizes identifier. * **value** must be an :ref:`type-string` or `None`. * **value** must not be longer than 100 characters. * **value** must not contain a character out the range of 0x20 - 0x7E. * Returned value is an unencoded ``unicode`` string. """ if value is None: return value if not isinstance(value, basestring): raise TypeError("Identifiers must be strings, not %s." % type(value).__name__) if len(value) == 0: raise ValueError("The identifier string is empty.") if len(value) > 100: raise ValueError("The identifier string has a length (%d) greater " "than the maximum allowed (100)." % len(value)) for c in value: v = ord(c) if v < 0x20 or v > 0x7E: raise ValueError("The identifier string ('%s') contains a " "character out size of the range 0x20 - 0x7E." % value) return unicode(value)
def __init__(self, value=None): if value is None: unicodes = [] elif isinstance(value, (str, unicode)): unicodes = value.split(",") else: unicodes = [unicode(v) for v in value] super(UnicodesList, self).__init__(unicodes)
def build_gdef(ufo): """Build a table GDEF statement for ligature carets.""" bases, ligatures, marks, carets = set(), set(), set(), {} for glyph in ufo: has_attaching_anchor = False for anchor in glyph.anchors: name = anchor.get('name') if name and not name.startswith('_'): has_attaching_anchor = True if name and name.startswith('caret_') and 'x' in anchor: carets.setdefault(glyph.name, []).append(round(anchor['x'])) glyphinfo = glyphsLib.glyphdata.get_glyph(glyph.name) if glyphinfo: category, subCategory = glyphinfo.category, glyphinfo.subCategory else: category, subCategory = None, None # Glyphs.app assigns glyph classes like this: # # * Base: any glyph that has an attaching anchor # (such as "top"; "_top" does not count) and is neither # classified as Ligature nor Mark using the definitions below; # # * Ligature: if subCategory is "Ligature" and the glyph has # at least one attaching anchor; # # * Mark: if category is "Mark" and subCategory is either # "Nonspacing" or "Spacing Combining"; # # * Compound: never assigned by Glyphs.app. # # https://github.com/googlei18n/glyphsLib/issues/85 # https://github.com/googlei18n/glyphsLib/pull/100#issuecomment-275430289 if subCategory == 'Ligature' and has_attaching_anchor: ligatures.add(glyph.name) elif category == 'Mark' and (subCategory == 'Nonspacing' or subCategory == 'Spacing Combining'): marks.add(glyph.name) elif has_attaching_anchor: bases.add(glyph.name) if not any((bases, ligatures, marks, carets)): return None lines = ['table GDEF {', ' # automatic'] glyphOrder = ufo.lib[PUBLIC_PREFIX + 'glyphOrder'] glyphIndex = lambda glyph: glyphOrder.index(glyph) fmt = lambda g: ('[%s]' % ' '.join(sorted(g, key=glyphIndex))) if g else '' lines.extend([ ' GlyphClassDef', ' %s, # Base' % fmt(bases), ' %s, # Liga' % fmt(ligatures), ' %s, # Mark' % fmt(marks), ' ;' ]) for glyph, caretPos in sorted(carets.items()): lines.append(' LigatureCaretByPos %s %s;' % (glyph, ' '.join(unicode(p) for p in sorted(caretPos)))) lines.append('} GDEF;') return '\n'.join(lines)
def normalizeFeatureText(value): """ Normalizes feature text. * **value** must be a :ref:`type-string`. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Feature text must be a string, not %s." % type(value).__name__) return unicode(value)
def normalizeFilePath(value): """ Normalizes file path. * **value** must be a :ref:`type-string`. * Returned value is an unencoded ``unicode`` string """ if not isinstance(value, basestring): raise TypeError("File paths must be strings, not %s." % type(value).__name__) return unicode(value)
def makeFileName(self, fileName): """ Make a file system legal version of **fileName**. """ fileName = unicode(fileName) suffix = "" if fileName.lower().endswith(".png"): suffix = fileName[-4:] fileName = fileName[:-4] existing = set([i.lower() for i in self.fileNames]) return userNameToFileName(fileName, existing, suffix=suffix)
def normalizeFeatureText(value): """ Normalizes feature text. * **value** must be a :ref:`type-string`. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Feature text must be a string, not %s." % type(value).__name__) return unicode(value)
def normalizeGlyphNote(value): """ Normalizes Glyph Note. * **value** must be a :ref:`type-string`. * Returned value is an unencoded ``unicode`` string """ if not isinstance(value, basestring): raise TypeError("Note must be a string, not %s." % type(value).__name__) return unicode(value)
def normalizeGlyphNote(value): """ Normalizes Glyph Note. * **value** must be a :ref:`type-string`. * Returned value is an unencoded ``unicode`` string """ if not isinstance(value, basestring): raise TypeError("Note must be a string, not %s." % type(value).__name__) return unicode(value)
def normalizeFilePath(value): """ Normalizes file path. * **value** must be a :ref:`type-string`. * Returned value is an unencoded ``unicode`` string """ if not isinstance(value, basestring): raise TypeError("File paths must be strings, not %s." % type(value).__name__) return unicode(value)
def makeFileName(self, fileName): """ Make a file system legal version of **fileName**. """ fileName = unicode(fileName) suffix = "" if fileName.lower().endswith(".png"): suffix = fileName[-4:] fileName = fileName[:-4] existing = set([i.lower() for i in self.fileNames]) return userNameToFileName(fileName, existing, suffix=suffix)
def normalizeDefaultLayerName(value, font): """ Normalizes default layer name. * **value** must normalize as layer name with :func:`normalizeLayerName`. * **value** must be a layer in **font**. * Returned value will be an unencoded ``unicode`` string. """ value = normalizeLayerName(value) if value not in font.layerOrder: raise ValueError("No layer with the name '%s' exists." % value) return unicode(value)
def normalizeDefaultLayerName(value, font): """ Normalizes default layer name. * **value** must normalize as layer name with :func:`normalizeLayerName`. * **value** must be a layer in **font**. * Returned value will be an unencoded ``unicode`` string. """ value = normalizeLayerName(value) if value not in font.layerOrder: raise ValueError("No layer with the name '%s' exists." % value) return unicode(value)
def normalizeDefaultLayerName(value, font): """ Normalizes default layer name. * **value** must be a :ref:`type-string`. * **value** must be a layer in **font**. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Layer names must be strings, not %s." % type(value).__name__) if value not in font.layerOrder: raise ValueError("No layer with the name '%s' exists." % value) return unicode(value)
def normalizePointName(value): """ Normalizes point name. * **value** must be a :ref:`type-string`. * **value** must be at least one character long. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Point names must be strings, not %s." % type(value).__name__) if len(value) < 1: raise ValueError("Point names must be at least one character long.") return unicode(value)
def normalizeGroupKey(value): """ Normalizes group key. * **value** must be a :ref:`type-string`. * **value** must be least one character long. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Group key must be a string, not %s." % type(value).__name__) if len(value) < 1: raise ValueError("Group key must be at least one character long.") return unicode(value)
def normalizeGroupValue(value): """ Normalizes group value. * **value** must be a ``list``. * **value** items must normalize as glyph names with :func:`normalizeGlyphName`. * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Group value must be a list, not %s." % type(value).__name__) value = [normalizeGlyphName(v) for v in value] return tuple([unicode(v) for v in value])
def normalizeGroupValue(value): """ Normalizes group value. * **value** must be a ``list``. * **value** items must normalize as glyph names with :func:`normalizeGlyphName`. * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Group value must be a list, not %s." % type(value).__name__) value = [normalizeGlyphName(v) for v in value] return tuple([unicode(v) for v in value])
def normalizeGroupKey(value): """ Normalizes group key. * **value** must be a :ref:`type-string`. * **value** must be least one character long. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Group key must be a string, not %s." % type(value).__name__) if len(value) < 1: raise ValueError("Group key must be at least one character long.") return unicode(value)
def normalizePointName(value): """ Normalizes point name. * **value** must be a :ref:`type-string`. * **value** must be at least one character long. * Returned value will be an unencoded ``unicode`` string. """ if not isinstance(value, basestring): raise TypeError("Point names must be strings, not %s." % type(value).__name__) if len(value) < 1: raise ValueError("Point names must be at least one character long.") return unicode(value)
def normalizeAnchorName(value): """ Normalizes anchor name. * **value** must be a :ref:`type-string` or ``None``. * **value** must be at least one character long if :ref:`type-string`. * Returned value will be an unencoded ``unicode`` string or ``None``. """ if value is None: return None if not isinstance(value, basestring): raise TypeError("Anchor names must be strings, not %s." % type(value).__name__) if len(value) < 1: raise ValueError(("Anchor names must be at least one character " "long or None.")) return unicode(value)
def normalizeAnchorName(value): """ Normalizes anchor name. * **value** must be a :ref:`type-string` or ``None``. * **value** must be at least one character long if :ref:`type-string`. * Returned value will be an unencoded ``unicode`` string or ``None``. """ if value is None: return None if not isinstance(value, basestring): raise TypeError("Anchor names must be strings, not %s." % type(value).__name__) if len(value) < 1: raise ValueError(("Anchor names must be at least one character " "long or None.")) return unicode(value)
def normalizeLibValue(value): """ Normalizes lib value. * **value** must not be ``None``. * Returned value is the same type as the input value. """ if value is None: raise ValueError("Lib value must not be None.") if isinstance(value, (list, tuple)): for v in value: normalizeLibValue(v) elif isinstance(value, dict): for k, v in value.items(): normalizeLibKey(k) normalizeLibValue(v) elif isinstance(value, basestring): value = unicode(value) return value
def normalizeLibValue(value): """ Normalizes lib value. * **value** must not be ``None``. * Returned value is the same type as the input value. """ if value is None: raise ValueError("Lib value must not be None.") if isinstance(value, (list, tuple)): for v in value: normalizeLibValue(v) elif isinstance(value, dict): for k, v in value.items(): normalizeLibKey(k) normalizeLibValue(v) elif isinstance(value, basestring): value = unicode(value) return value
def normalizeGlyphOrder(value): """ Normalizes glyph order. ** **value** must be a ``tuple`` or ``list``. * **value** items must normalize as glyph names with :func:`normalizeGlyphName`. * **value** must not repeat glyph names. * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Glyph order must be a list, not %s." % type(value).__name__) for v in value: normalizeGlyphName(v) duplicates = sorted(v for v, count in Counter(value).items() if count > 1) if len(duplicates) != 0: raise ValueError("Duplicate glyph names are not allowed. Glyph " "name(s) '%s' are duplicate." % ", ".join(duplicates)) return tuple([unicode(v) for v in value])
def normalizeGlyphOrder(value): """ Normalizes glyph order. ** **value** must be a ``tuple`` or ``list``. * **value** items must normalize as glyph names with :func:`normalizeGlyphName`. * **value** must not repeat glyph names. * Returned value will be a ``tuple`` of unencoded ``unicode`` strings. """ if not isinstance(value, (tuple, list)): raise TypeError("Glyph order must be a list, not %s." % type(value).__name__) for v in value: normalizeGlyphName(v) duplicates = sorted(v for v, count in Counter(value).items() if count > 1) if len(duplicates) != 0: raise ValueError("Duplicate glyph names are not allowed. Glyph " "name(s) '%s' are duplicate." % ", ".join(duplicates)) return tuple([unicode(v) for v in value])
def normalizeBPointType(value): """ Normalizes bPoint type. * **value** must be an string. * **value** must be one of the following: +--------+ | corner | +--------+ | curve | +--------+ * Returned value will be an unencoded ``unicode`` string. """ allowedTypes = ['corner', 'curve'] if not isinstance(value, basestring): raise TypeError("bPoint type must be a string, not %s." % type(value).__name__) if value not in allowedTypes: raise ValueError("bPoint type must be 'corner' or 'curve', not %r." % value) return unicode(value)
def normalizeBPointType(value): """ Normalizes bPoint type. * **value** must be an string. * **value** must be one of the following: +--------+ | corner | +--------+ | curve | +--------+ * Returned value will be an unencoded ``unicode`` string. """ allowedTypes = ['corner', 'curve'] if not isinstance(value, basestring): raise TypeError("bPoint type must be a string, not %s." % type(value).__name__) if value not in allowedTypes: raise ValueError("bPoint type must be 'corner' or 'curve', not %r." % value) return unicode(value)
def _build_gdef(ufo): """Build a GDEF table statement (GlyphClassDef and LigatureCaretByPos). Building GlyphClassDef requires anchor propagation or user care to work as expected, as Glyphs.app also looks at anchors for classification: * Base: any glyph that has an attaching anchor (such as "top"; "_top" does not count) and is neither classified as Ligature nor Mark using the definitions below; * Ligature: if subCategory is "Ligature" and the glyph has at least one attaching anchor; * Mark: if category is "Mark" and subCategory is either "Nonspacing" or "Spacing Combining"; * Compound: never assigned by Glyphs.app. See: * https://github.com/googlei18n/glyphsLib/issues/85 * https://github.com/googlei18n/glyphsLib/pull/100#issuecomment-275430289 """ from glyphsLib import glyphdata bases, ligatures, marks, carets = set(), set(), set(), {} category_key = GLYPHLIB_PREFIX + "category" subCategory_key = GLYPHLIB_PREFIX + "subCategory" for glyph in ufo: has_attaching_anchor = False for anchor in glyph.anchors: name = anchor.name if name and not name.startswith("_"): has_attaching_anchor = True if name and name.startswith("caret_") and "x" in anchor: carets.setdefault(glyph.name, []).append(round(anchor["x"])) # First check glyph.lib for category/subCategory overrides. Otherwise, # use global values from GlyphData. glyphinfo = glyphdata.get_glyph(glyph.name) category = glyph.lib.get(category_key) or glyphinfo.category subCategory = glyph.lib.get(subCategory_key) or glyphinfo.subCategory if subCategory == "Ligature" and has_attaching_anchor: ligatures.add(glyph.name) elif category == "Mark" and (subCategory == "Nonspacing" or subCategory == "Spacing Combining"): marks.add(glyph.name) elif has_attaching_anchor: bases.add(glyph.name) if not any((bases, ligatures, marks, carets)): return None def fmt(g): return ("[%s]" % " ".join(sorted(g, key=ufo.glyphOrder.index))) if g else "" lines = [ "table GDEF {", " # automatic", " GlyphClassDef", " %s, # Base" % fmt(bases), " %s, # Liga" % fmt(ligatures), " %s, # Mark" % fmt(marks), " ;", ] for glyph, caretPos in sorted(carets.items()): lines.append(" LigatureCaretByPos %s %s;" % (glyph, " ".join(unicode(p) for p in sorted(caretPos)))) lines.append("} GDEF;") return "\n".join(lines)
def nameToFileName(name): return userNameToFileName(unicode(name))
def main(argv): # command argument tests print(" ") if len(argv) < 2: sys.stderr.write( "[fontname.py] ERROR: you did not include enough arguments to the script." + os.linesep) sys.stderr.write( "Usage: python fontname.py [FONT FAMILY NAME] [FONT PATH 1] <FONT PATH ...>" + os.linesep) sys.exit(1) # begin parsing command line arguments try: font_name = tounicode( argv[0]) # the first argument is the new typeface name except UnicodeDecodeError as e: sys.stderr.write( "[fontname.py] ERROR: Unable to convert argument to Unicode. " + unicode(e) + os.linesep) sys.exit(1) font_path_list = argv[ 1:] # all remaining arguments on command line are file paths to fonts # iterate through all paths provided on command line and rename to `font_name` defined by user for font_path in font_path_list: # test for existence of font file on requested file path if not file_exists(font_path): sys.stderr.write("[fontname.py] ERROR: the path '" + font_path + "' does not appear to be a valid file path." + os.linesep) sys.exit(1) tt = ttLib.TTFont(font_path) namerecord_list = tt["name"].names variant = "" # determine font variant for this file path from name record nameID 2 for record in namerecord_list: if record.nameID == 2: variant = (record.toUnicode() ) # cast to str type in Py 3, unicode type in Py 2 break # test that a variant name was found in the OpenType tables of the font if len(variant) == 0: sys.stderr.write( "[fontname.py] Unable to detect the font variant from the OpenType name table in '" + font_path + "'." + os.linesep) sys.stderr.write("Unable to complete execution of the script.") sys.exit(1) else: # used for the Postscript name in the name table (no spaces allowed) postscript_font_name = font_name.replace(" ", "") # font family name nameID1_string = font_name # full font name nameID4_string = font_name + " " + variant # Postscript name # - no spaces allowed in family name or the PostScript suffix. should be dash delimited nameID6_string = postscript_font_name + "-" + variant.replace( " ", "") # modify the opentype table data in memory with updated values for record in namerecord_list: if record.nameID == 1: record.string = nameID1_string elif record.nameID == 4: record.string = nameID4_string elif record.nameID == 6: record.string = nameID6_string # write changes to the font file try: tt.save(font_path) print("[OK] Updated '" + font_path + "' with the name '" + nameID4_string + "'") except Exception as e: sys.stderr.write( "[fontname.py] ERROR: unable to write new name to OpenType tables for '" + font_path + "'." + os.linesep) sys.stderr.write(unicode(e)) sys.exit(1)
def main(argv): # command argument tests print(" ") if len(argv) < 2: sys.stderr.write( "[fontname.py] ERROR: you did not include enough arguments to the script." + os.linesep ) sys.stderr.write( "Usage: python fontname.py [FONT FAMILY NAME] [FONT PATH 1] <FONT PATH ...>" + os.linesep ) sys.exit(1) # begin parsing command line arguments try: font_name = tounicode(argv[0]) # the first argument is the new typeface name except UnicodeDecodeError as e: sys.stderr.write( "[fontname.py] ERROR: Unable to convert argument to Unicode. " + unicode(e) + os.linesep ) sys.exit(1) font_path_list = argv[ 1: ] # all remaining arguments on command line are file paths to fonts # iterate through all paths provided on command line and rename to `font_name` defined by user for font_path in font_path_list: # test for existence of font file on requested file path if not file_exists(font_path): sys.stderr.write( "[fontname.py] ERROR: the path '" + font_path + "' does not appear to be a valid file path." + os.linesep ) sys.exit(1) tt = ttLib.TTFont(font_path) namerecord_list = tt["name"].names variant = "" # determine font variant for this file path from name record nameID 2 for record in namerecord_list: if record.nameID == 2: variant = ( record.toUnicode() ) # cast to str type in Py 3, unicode type in Py 2 break # test that a variant name was found in the OpenType tables of the font if len(variant) == 0: sys.stderr.write( "[fontname.py] Unable to detect the font variant from the OpenType name table in '" + font_path + "'." + os.linesep ) sys.stderr.write("Unable to complete execution of the script.") sys.exit(1) else: # used for the Postscript name in the name table (no spaces allowed) postscript_font_name = font_name.replace(" ", "") # font family name nameID1_string = font_name # full font name nameID4_string = font_name + " " + variant # Postscript name # - no spaces allowed in family name or the PostScript suffix. should be dash delimited nameID6_string = postscript_font_name + "-" + variant.replace(" ", "") # modify the opentype table data in memory with updated values for record in namerecord_list: if record.nameID == 1: record.string = nameID1_string elif record.nameID == 4: record.string = nameID4_string elif record.nameID == 6: record.string = nameID6_string # write changes to the font file try: tt.save(font_path) print( "[OK] Updated '" + font_path + "' with the name '" + nameID4_string + "'" ) except Exception as e: sys.stderr.write( "[fontname.py] ERROR: unable to write new name to OpenType tables for '" + font_path + "'." + os.linesep ) sys.stderr.write(unicode(e)) sys.exit(1)
def plistValue(self): return unicode(self.value)