Exemplo n.º 1
0
 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))
Exemplo n.º 2
0
 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)
Exemplo n.º 3
0
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])
Exemplo n.º 4
0
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)
Exemplo n.º 5
0
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])
Exemplo n.º 6
0
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])
Exemplo n.º 7
0
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])
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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)
Exemplo n.º 10
0
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)
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
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)
Exemplo n.º 13
0
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)
Exemplo n.º 14
0
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)
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
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)
Exemplo n.º 17
0
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)
Exemplo n.º 18
0
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)
Exemplo n.º 19
0
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)
Exemplo n.º 20
0
 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)
Exemplo n.º 21
0
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)
Exemplo n.º 22
0
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)
Exemplo n.º 23
0
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)
Exemplo n.º 24
0
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)
Exemplo n.º 25
0
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)
Exemplo n.º 26
0
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])
Exemplo n.º 27
0
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])
Exemplo n.º 28
0
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)
Exemplo n.º 29
0
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)
Exemplo n.º 30
0
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)
Exemplo n.º 31
0
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)
Exemplo n.º 32
0
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
Exemplo n.º 33
0
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
Exemplo n.º 34
0
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])
Exemplo n.º 35
0
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])
Exemplo n.º 36
0
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)
Exemplo n.º 37
0
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)
Exemplo n.º 38
0
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)
Exemplo n.º 39
0
 def nameToFileName(name):
     return userNameToFileName(unicode(name))
Exemplo n.º 40
0
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)
Exemplo n.º 41
0
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)
Exemplo n.º 42
0
 def plistValue(self):
     return unicode(self.value)