def to_ufo_gasp_table(value):
    # XXX maybe the parser should cast the gasp values to int?
    value = {int(k): int(v) for k, v in value.items()}
    gasp_records = []
    # gasp range records must be sorted in ascending rangeMaxPPEM
    for max_ppem, gasp_behavior in sorted(value.items()):
        gasp_records.append({
            'rangeMaxPPEM': max_ppem,
            'rangeGaspBehavior': bin_to_int_list(gasp_behavior)
        })
    return gasp_records
Exemple #2
0
 def test_bin_to_int_list(self):
     self.assertEqual([], bin_to_int_list(0))
     self.assertEqual([0], bin_to_int_list(1))
     self.assertEqual([1], bin_to_int_list(2))
     self.assertEqual([0, 1], bin_to_int_list(3))
     self.assertEqual([2], bin_to_int_list(4))
     self.assertEqual([7, 30], bin_to_int_list((1 << 7) + (1 << 30)))
Exemple #3
0
def set_custom_params(ufo, parsed=None, data=None, misc_keys=(), non_info=()):
    """Set Glyphs custom parameters in UFO info or lib, where appropriate.

    Custom parameter data can be pre-parsed out of Glyphs data and provided via
    the `parsed` argument, otherwise `data` should be provided and will be
    parsed. The `parsed` option is provided so that custom params can be popped
    from Glyphs data once and used several times; in general this is used for
    debugging purposes (to detect unused Glyphs data).

    The `non_info` argument can be used to specify potential UFO info attributes
    which should not be put in UFO info.
    """

    if parsed is None:
        parsed = parse_custom_params(data or {}, misc_keys)
    else:
        assert data is None, "Shouldn't provide parsed data and data to parse."

    fsSelection_flags = {'Use Typo Metrics', 'Has WWS Names'}
    for name, value in parsed:
        name = normalize_custom_param_name(name)

        if name in fsSelection_flags:
            if value:
                if ufo.info.openTypeOS2Selection is None:
                    ufo.info.openTypeOS2Selection = []
                if name == 'Use Typo Metrics':
                    ufo.info.openTypeOS2Selection.append(7)
                elif name == 'Has WWS Names':
                    ufo.info.openTypeOS2Selection.append(8)
            continue

        # deal with any Glyphs naming quirks here
        if name == 'disablesNiceNames':
            name = 'useNiceNames'
            value = int(not value)

        # convert code page numbers to OS/2 ulCodePageRange bits
        if name == 'codePageRanges':
            value = [CODEPAGE_RANGES[v] for v in value]

        # convert Glyphs' GASP Table to UFO openTypeGaspRangeRecords
        if name == 'GASP Table':
            name = 'openTypeGaspRangeRecords'
            # XXX maybe the parser should cast the gasp values to int?
            value = {int(k): int(v) for k, v in value.items()}
            gasp_records = []
            # gasp range records must be sorted in ascending rangeMaxPPEM
            for max_ppem, gasp_behavior in sorted(value.items()):
                gasp_records.append({
                    'rangeMaxPPEM':
                    max_ppem,
                    'rangeGaspBehavior':
                    bin_to_int_list(gasp_behavior)
                })
            value = gasp_records

        opentype_attr_prefix_pairs = (('hhea', 'Hhea'), ('description',
                                                         'NameDescription'),
                                      ('license',
                                       'NameLicense'), ('panose', 'OS2Panose'),
                                      ('typo',
                                       'OS2Typo'), ('unicodeRanges',
                                                    'OS2UnicodeRanges'),
                                      ('codePageRanges', 'OS2CodePageRanges'),
                                      ('weightClass',
                                       'OS2WeightClass'), ('widthClass',
                                                           'OS2WidthClass'),
                                      ('win', 'OS2Win'), ('vendorID',
                                                          'OS2VendorID'),
                                      ('versionString',
                                       'NameVersion'), ('fsType', 'OS2Type'))
        for glyphs_prefix, ufo_prefix in opentype_attr_prefix_pairs:
            name = re.sub('^' + glyphs_prefix, 'openType' + ufo_prefix, name)

        postscript_attrs = ('underlinePosition', 'underlineThickness')
        if name in postscript_attrs:
            name = 'postscript' + name[0].upper() + name[1:]

        # enforce that winAscent/Descent are positive, according to UFO spec
        if name.startswith('openTypeOS2Win') and value < 0:
            value = -value

        # The value of these could be a float, and ufoLib/defcon expect an int.
        if name in ('openTypeOS2WeightClass', 'openTypeOS2WidthClass'):
            value = int(value)

        if name == 'glyphOrder':
            # store the public.glyphOrder in lib.plist
            ufo.lib[PUBLIC_PREFIX + name] = value
        elif name == 'Filter':
            filter_struct = parse_glyphs_filter(value)
            if not filter_struct:
                continue
            if UFO2FT_FILTERS_KEY not in ufo.lib.keys():
                ufo.lib[UFO2FT_FILTERS_KEY] = []
            ufo.lib[UFO2FT_FILTERS_KEY].append(filter_struct)
        elif hasattr(ufo.info, name) and name not in non_info:
            # most OpenType table entries go in the info object
            setattr(ufo.info, name, value)
        else:
            # everything else gets dumped in the lib
            ufo.lib[GLYPHS_PREFIX + name] = value