Esempio n. 1
0
def _to_designspace_source(self, master, is_regular):
    source = self._sources[master.id]
    ufo = source.font

    if is_regular:
        source.copyLib = True
        source.copyInfo = True
        source.copyGroups = True
        source.copyFeatures = True

    source.familyName = ufo.info.familyName
    source.styleName = ufo.info.styleName
    # TODO: recover original source name from userData
    # UFO_SOURCE_NAME_KEY
    source.name = f"{source.familyName} {source.styleName}"

    if UFO_FILENAME_CUSTOM_PARAM in master.customParameters:
        source.filename = master.customParameters[UFO_FILENAME_CUSTOM_PARAM]
    elif UFO_FILENAME_KEY in master.userData:
        source.filename = master.userData[UFO_FILENAME_KEY]
    else:
        source.filename = build_ufo_path("", source.familyName,
                                         source.styleName)

    # Make sure UFO filenames are unique, lest we overwrite masters that
    # happen to have the same weight name. Careful, name clashes can also happen when
    # the masters have a UFO_FILENAME_CUSTOM_PARAM: when the user duplicates a master
    # in Glyphs but forgets to change the custom parameter.
    # Attention: The following is done regardless of where source.filename came from.
    # Depending on the duplicate's order in the master list, this could lead to the
    # legitimate master's filename getting an underscore appended!
    n = 1
    while any(s is not source and s.filename == source.filename
              for s in self._sources.values()):
        filename_stem, filename_ext = os.path.splitext(source.filename)
        source.filename = f"{filename_stem}#{n}{filename_ext}"
        if (UFO_FILENAME_CUSTOM_PARAM in master.customParameters
                or UFO_FILENAME_KEY in master.userData):
            logger.warning(
                "The master with the style name '{}' (ID {}) would be written to the "
                "same destination as another master. Change the "
                "master's custom parameter '{}' "
                "to select a different destination. Proceeding, but appending"
                " '#{}' to the filename on disk.".format(
                    source.styleName, master.id, UFO_FILENAME_CUSTOM_PARAM, n))
        else:
            logger.warning(
                "The master with the style name '{}' (ID {}) has the same style name "
                "as another one. All masters should have distinctive "
                "(style) names. Use the 'Master name' custom parameter"
                " on a master to give it a unique name. Proceeding "
                "with an unchanged name, but appending '#{}' to the file"
                " name on disk.".format(source.styleName, master.id, n))
        n += 1

    location = {}
    for axis_def in get_axis_definitions(self.font):
        location[axis_def.name] = axis_def.get_design_loc(master)
    source.location = location
Esempio n. 2
0
def _to_designspace_source(self, master, is_regular):
    source = self._sources[master.id]
    ufo = source.font

    if is_regular:
        source.copyLib = True
        source.copyInfo = True
        source.copyGroups = True
        source.copyFeatures = True

    source.familyName = ufo.info.familyName
    source.styleName = ufo.info.styleName
    # TODO: recover original source name from userData
    # UFO_SOURCE_NAME_KEY
    source.name = "{} {}".format(source.familyName, source.styleName)

    if UFO_FILENAME_KEY in master.userData:
        source.filename = master.userData[UFO_FILENAME_KEY]
    else:
        # TODO: (jany) allow another naming convention?
        source.filename = build_ufo_path("", source.familyName, source.styleName)

        # Make sure UFO filenames are unique, lest we overwrite masters that
        # happen to have the same weight name.
        n = "_"
        while any(
            s is not source and s.filename == source.filename
            for s in self._sources.values()
        ):
            source.filename = os.path.basename(
                build_ufo_path("", source.familyName, source.styleName + n)
            )
            n += "_"
            logger.warning(
                "The master with id {} has the same style name ({}) "
                "as another one. All masters should have distinctive "
                "(style) names. Use the 'Master name' custom parameter"
                " on a master to give it a unique name. Proceeding "
                "with an unchanged name, but appending '_' to the file"
                " name on disk.".format(master.id, source.styleName)
            )

    location = {}
    for axis_def in get_axis_definitions(self.font):
        location[axis_def.name] = axis_def.get_design_loc(master)
    source.location = location
Esempio n. 3
0
def add_instances_to_writer(writer, family_name, axes, instances, out_dir):
    """Add instances from Glyphs data to a MutatorMath document writer.

    Returns a list of <ufo_path, font_data> pairs, corresponding to the
    instances which will be output by the document writer. The font data is the
    Glyphs data for this instance as a dict.
    """
    ofiles = []
    for instance in instances:
        familyName, postScriptFontName, ufo_path = None, None, None
        for p in instance.customParameters:
            param, value = p.name, p.value
            if param == 'familyName':
                familyName = value
            elif param == 'postscriptFontName':
                # Glyphs uses "postscriptFontName", not "postScriptFontName"
                postScriptFontName = value
            elif param == 'fileName':
                ufo_path = os.path.join(out_dir, value + '.ufo')
        if familyName is None:
            familyName = family_name

        styleName = instance.name
        if not ufo_path:
            ufo_path = build_ufo_path(out_dir, familyName, styleName)
        ofiles.append((ufo_path, instance))
        # MutatorMath.DesignSpaceDocumentWriter iterates over the location
        # dictionary, which is non-deterministic so it can cause test failures.
        # We therefore use an OrderedDict to which we insert in axis order.
        # Since glyphsLib will switch to DesignSpaceDocument once that is
        # integrated into fonttools, it's not worth fixing upstream.
        # https://github.com/googlei18n/glyphsLib/issues/165
        location = OrderedDict()
        for axis in axes:
            location[axis] = getattr(instance, 'interpolation' + axis.title(),
                                     DEFAULT_LOCS[axis])
        styleMapFamilyName, styleMapStyleName = build_stylemap_names(
            family_name=familyName,
            style_name=styleName,
            is_bold=instance.isBold,
            is_italic=instance.isItalic,
            linked_style=instance.linkStyle,
        )
        writer.startInstance(name=' '.join((familyName, styleName)),
                             location=location,
                             familyName=familyName,
                             styleName=styleName,
                             postScriptFontName=postScriptFontName,
                             styleMapFamilyName=styleMapFamilyName,
                             styleMapStyleName=styleMapStyleName,
                             fileName=ufo_path)

        writer.writeInfo()
        writer.writeKerning()
        writer.endInstance()

    return ofiles
Esempio n. 4
0
def add_instances_to_writer(writer, family_name, instance_data, out_dir):
    """Add instances from Glyphs data to a MutatorMath document writer.

    Returns a list of <ufo_path, font_data> pairs, corresponding to the
    instances which will be output by the document writer. The font data is the
    Glyphs data for this instance as a dict.
    """

    default_family_name = instance_data.pop('defaultFamilyName')
    instance_data = instance_data.pop('data')
    ofiles = []

    # only write dimension elements if defined in at least one of the instances
    dimension_names = []
    for s in ('weight', 'width', 'custom'):
        key = 'interpolation' + s.title()
        if any(key in instance for instance in instance_data):
            dimension_names.append(s)

    for instance in instance_data:
        # Glyphs.app recognizes both "exports=0" and "active=0" as a flag
        # to mark instances as inactive. Those should not be instantiated.
        # https://github.com/googlei18n/glyphsLib/issues/129
        if (not int(instance.pop('exports', 1))
                or not int(instance.pop('active', 1))):
            continue

        instance_family = default_family_name
        custom_params = instance.get('customParameters', ())
        for i in range(len(custom_params)):
            if custom_params[i]['name'] == 'familyName':
                instance_family = custom_params[i]['value']
                break
        if not instance_family:
            continue

        style_name = instance.pop('name')
        ufo_path = build_ufo_path(out_dir, instance_family, style_name)
        ofiles.append((ufo_path, instance))

        writer.startInstance(
            name=' '.join((instance_family, style_name)),
            location={
                s: instance.pop('interpolation' + s.title(), DEFAULT_LOC)
                for s in dimension_names},
            familyName=instance_family,
            styleName=style_name,
            fileName=ufo_path)

        writer.writeInfo()
        writer.writeKerning()
        writer.endInstance()

    return ofiles
Esempio n. 5
0
def add_instances_to_writer(writer, family_name, instance_data, out_dir):
    """Add instances from Glyphs data to a MutatorMath document writer.

    Returns a list of <ufo_path, font_data> pairs, corresponding to the
    instances which will be output by the document writer. The font data is the
    Glyphs data for this instance as a dict.
    """

    default_family_name = instance_data.pop('defaultFamilyName')
    instance_data = instance_data.pop('data')
    ofiles = []

    # only write dimension elements if defined in at least one of the instances
    dimension_names = []
    for s in ('weight', 'width', 'custom'):
        key = 'interpolation' + s.title()
        if any(key in instance for instance in instance_data):
            dimension_names.append(s)

    for instance in instance_data:

        if not instance.pop('active', True):
            continue

        # only use instances with the masters' family name
        instance_family = default_family_name
        custom_params = instance.get('customParameters', ())
        for i in range(len(custom_params)):
            if custom_params[i]['name'] == 'familyName':
                instance_family = custom_params[i]['value']
                del custom_params[i]
                break
        if instance_family != family_name:
            continue

        style_name = instance.pop('name')
        ufo_path = build_ufo_path(out_dir, instance_family, style_name)
        ofiles.append((ufo_path, instance))

        writer.startInstance(
            name=' '.join((instance_family, style_name)),
            location={
                s: instance.pop('interpolation' + s.title(), DEFAULT_LOC)
                for s in dimension_names},
            familyName=instance_family,
            styleName=style_name,
            fileName=ufo_path)

        writer.writeInfo()
        writer.writeKerning()
        writer.endInstance()

    return ofiles
Esempio n. 6
0
def _to_designspace_instance(self, instance):
    ufo_instance = self.designspace.newInstanceDescriptor()
    # FIXME: (jany) most of these customParameters are actually attributes,
    # at least according to https://docu.glyphsapp.com/#fontName
    for p in instance.customParameters:
        param, value = p.name, p.value
        if param == 'familyName':
            ufo_instance.familyName = value
        elif param == 'postscriptFontName':
            # Glyphs uses "postscriptFontName", not "postScriptFontName"
            ufo_instance.postScriptFontName = value
        elif param == 'fileName':
            fname = value + '.ufo'
            if self.instance_dir is not None:
                fname = self.instance_dir + '/' + fname
            ufo_instance.filename = fname

    if ufo_instance.familyName is None:
        ufo_instance.familyName = self.family_name
    ufo_instance.styleName = instance.name

    # TODO: (jany) investigate the possibility of storing a relative path in
    #   the `filename` custom parameter. If yes, drop the key below.
    #   Maybe do the same for masters?
    #   https://github.com/googlei18n/glyphsLib/issues/319
    fname = instance.customParameters[FULL_FILENAME_KEY]
    if fname is not None:
        if self.instance_dir:
            fname = self.instance_dir + '/' + os.path.basename(fname)
        ufo_instance.filename = fname
    if not ufo_instance.filename:
        instance_dir = self.instance_dir or 'instance_ufos'
        ufo_instance.filename = build_ufo_path(instance_dir,
                                               ufo_instance.familyName,
                                               ufo_instance.styleName)

    designspace_axis_tags = set(a.tag for a in self.designspace.axes)
    location = {}
    for axis_def in get_axis_definitions(self.font):
        # Only write locations along defined axes
        if axis_def.tag in designspace_axis_tags:
            location[axis_def.name] = axis_def.get_design_loc(instance)
    ufo_instance.location = location

    # FIXME: (jany) should be the responsibility of ufo2ft?
    # Anyway, only generate the styleMap names if the Glyphs instance already
    # has a linkStyle set up, or if we're not round-tripping (i.e. generating
    # UFOs for fontmake, the traditional use-case of glyphsLib.)
    if instance.linkStyle or not self.minimize_glyphs_diffs:
        ufo_instance.styleMapFamilyName, ufo_instance.styleMapStyleName = \
            build_stylemap_names(
                family_name=ufo_instance.familyName,
                style_name=ufo_instance.styleName,
                is_bold=instance.isBold,
                is_italic=instance.isItalic,
                linked_style=instance.linkStyle,
            )

    ufo_instance.name = ' '.join((ufo_instance.familyName
                                  or '', ufo_instance.styleName or ''))

    if self.minimize_glyphs_diffs:
        ufo_instance.lib[EXPORT_KEY] = instance.active
        ufo_instance.lib[WEIGHT_KEY] = instance.weight
        ufo_instance.lib[WIDTH_KEY] = instance.width

        ufo_instance.lib[
            INSTANCE_INTERPOLATIONS_KEY] = instance.instanceInterpolations
        ufo_instance.lib[
            MANUAL_INTERPOLATION_KEY] = instance.manualInterpolation

    # Strategy: dump all custom parameters into the InstanceDescriptor.
    # Later, when using `apply_instance_data`, we will dig out those custom
    # parameters using `InstanceDescriptorAsGSInstance` and apply them to the
    # instance UFO with `to_ufo_custom_params`.
    # NOTE: customParameters are not a dict! One key can have several values
    params = []
    for p in instance.customParameters:
        if p.name in ('familyName', 'postscriptFontName', 'fileName',
                      FULL_FILENAME_KEY):
            # These will be stored in the official descriptor attributes
            continue
        if p.name in ('weightClass', 'widthClass'):
            # No need to store these ones because we can recover them by
            # reading the mapping backward, because the mapping is built from
            # where the instances are.
            continue
        params.append((p.name, p.value))
    if params:
        ufo_instance.lib[CUSTOM_PARAMETERS_KEY] = params

    self.designspace.addInstance(ufo_instance)