コード例 #1
0
    def assertDesignspaceRoundtrip(self, designspace):
        directory = tempfile.mkdtemp()
        font = to_glyphs(designspace, minimize_ufo_diffs=True)

        # Check that round-tripping in memory is the same as writing on disk
        roundtrip_in_mem = to_designspace(font, propagate_anchors=False)

        tmpfont_path = os.path.join(directory, "font.glyphs")
        font.save(tmpfont_path)
        font_rt = classes.GSFont(tmpfont_path)
        roundtrip = to_designspace(font_rt, propagate_anchors=False)

        font.save("intermediary.glyphs")

        write_designspace_and_UFOs(designspace, "expected/test.designspace")
        for source in designspace.sources:
            normalize_ufo_lib(source.path)
            normalizeUFO(source.path, floatPrecision=3, writeModTimes=False)
        write_designspace_and_UFOs(roundtrip, "actual/test.designspace")
        for source in roundtrip.sources:
            normalize_ufo_lib(source.path)
            normalizeUFO(source.path, floatPrecision=3, writeModTimes=False)
        self.assertDesignspacesEqual(
            roundtrip_in_mem,
            roundtrip,
            "The round-trip in memory or written to disk should be equivalent",
        )
        self.assertDesignspacesEqual(
            designspace, roundtrip, "The font should not be modified by the roundtrip"
        )
コード例 #2
0
def run(args):
    options = Options(args)

    # Set the current dir to the design space dir, so that relative paths in
    # the design space file will work.
    dsDir, dsFile = os.path.split(os.path.abspath(options.dsPath))
    os.chdir(dsDir)
    options.dsPath = dsFile

    dsPath, newInstancesList = readDesignSpaceFile(options)
    if not dsPath:
        return

    version = 2
    if len(newInstancesList) == 1:
        logMsg.log("Building 1 instance...")
    else:
        logMsg.log("Building %s instances..." % (len(newInstancesList)))
    mutatorMathBuild(documentPath=dsPath,
                     outputUFOFormatVersion=version,
                     roundGeometry=(not options.allowDecimalCoords))
    if (dsPath != options.dsPath) and os.path.exists(dsPath):
        os.remove(dsPath)

    logMsg.log("Built %s instances." % (len(newInstancesList)))
    # Remove glyph.lib and font.lib (except for "public.glyphOrder")
    for instancePath in newInstancesList:
        postProcessInstance(instancePath, options)

    if options.doNormalize:
        logMsg.log("Applying UFO normalization...")
        for instancePath in newInstancesList:
            normalizeUFO(instancePath,
                         outputPath=None,
                         onlyModified=True,
                         writeModTimes=False)

    if options.doAutoHint or options.doOverlapRemoval:
        logMsg.log("Applying post-processing...")
        # Apply autohint and checkoutlines, if requested.
        for instancePath in newInstancesList:
            # make new instance font.
            updateInstance(options, instancePath)

    # checkoutlinesufo does ufotools.validateLayers()
    if not options.doOverlapRemoval:
        for instancePath in newInstancesList:
            # make sure that that there are no old glyphs left in the
            # processed glyphs folder
            validateLayers(instancePath)

    # The defcon library renames glyphs. Need to fix them again
    if options.doOverlapRemoval or options.doAutoHint:
        for instancePath in newInstancesList:
            if options.doNormalize:
                normalizeUFO(instancePath,
                             outputPath=None,
                             onlyModified=False,
                             writeModTimes=False)
コード例 #3
0
def build_masters(filename,
                  master_dir,
                  designspace_instance_dir=None,
                  designspace_path=None,
                  family_name=None,
                  propagate_anchors=True,
                  minimize_glyphs_diffs=False,
                  normalize_ufos=False,
                  create_background_layers=False):
    """Write and return UFOs from the masters and the designspace defined in a
    .glyphs file.

    Args:
        master_dir: Directory where masters are written.
        designspace_instance_dir: If provided, a designspace document will be
            written alongside the master UFOs though no instances will be built.
        family_name: If provided, the master UFOs will be given this name and
            only instances with this name will be included in the designspace.

    Returns:
        A named tuple of master UFOs (`ufos`) and the path to the designspace
        file (`designspace_path`).
    """

    font = GSFont(filename)

    if designspace_instance_dir is None:
        instance_dir = None
    else:
        instance_dir = os.path.relpath(designspace_instance_dir, master_dir)

    designspace = to_designspace(font,
                                 family_name=family_name,
                                 propagate_anchors=propagate_anchors,
                                 instance_dir=instance_dir,
                                 minimize_glyphs_diffs=minimize_glyphs_diffs)

    ufos = []
    for source in designspace.sources:
        ufos.append(source.font)

        if create_background_layers:
            ufo_create_background_layer_for_all_glyphs(source.font)

        ufo_path = os.path.join(master_dir, source.filename)
        clean_ufo(ufo_path)
        source.font.save(ufo_path)

        if normalize_ufos:
            import ufonormalizer
            ufonormalizer.normalizeUFO(ufo_path, writeModTimes=False)

    if not designspace_path:
        designspace_path = os.path.join(master_dir, designspace.filename)
    designspace.write(designspace_path)

    return Masters(ufos, designspace_path)
コード例 #4
0
def run(options):

    # Set the current dir to the design space dir, so that relative paths in
    # the design space file will work.
    dsDir, dsFile = os.path.split(os.path.abspath(options.dsPath))
    os.chdir(dsDir)
    options.dsPath = dsFile

    dsPath, newInstancesList = readDesignSpaceFile(options)
    if not dsPath:
        return

    if len(newInstancesList) == 1:
        logger.info("Building 1 instance...")
    else:
        logger.info("Building %s instances..." % len(newInstancesList))
    ufoProcessorBuild(documentPath=dsPath,
                      outputUFOFormatVersion=options.ufo_version,
                      roundGeometry=(not options.no_round),
                      logger=logger)
    if (dsPath != options.dsPath) and os.path.exists(dsPath):
        os.remove(dsPath)

    logger.info("Built %s instances." % len(newInstancesList))
    # Remove glyph.lib and font.lib (except for "public.glyphOrder")
    for instancePath in newInstancesList:
        postProcessInstance(instancePath, options)

    if options.doNormalize:
        logger.info("Applying UFO normalization...")
        for instancePath in newInstancesList:
            normalizeUFO(instancePath, outputPath=None, onlyModified=True,
                         writeModTimes=False)

    if options.doAutoHint or options.doOverlapRemoval:
        logger.info("Applying post-processing...")
        # Apply autohint and checkoutlines, if requested.
        for instancePath in newInstancesList:
            # make new instance font.
            updateInstance(options, instancePath)

    # checkoutlinesufo does ufotools.validateLayers()
    if not options.doOverlapRemoval:
        for instancePath in newInstancesList:
            # make sure that that there are no old glyphs left in the
            # processed glyphs folder
            validateLayers(instancePath)

    # The defcon library renames glyphs. Need to fix them again
    if options.doOverlapRemoval or options.doAutoHint:
        for instancePath in newInstancesList:
            if options.doNormalize:
                normalizeUFO(instancePath, outputPath=None, onlyModified=False,
                             writeModTimes=False)
コード例 #5
0
    def assertDesignspacesEqual(self, expected, actual, message=""):
        directory = tempfile.mkdtemp()

        def git(*args):
            return subprocess.check_output(["git", "-C", directory] + list(args))

        def clean_git_folder():
            with os.scandir(directory) as entries:
                for entry in entries:
                    if entry.is_file() or entry.is_symlink():
                        os.remove(entry.path)
                    elif entry.is_dir() and entry.name != ".git":
                        shutil.rmtree(entry.path)

        # Strategy: init a git repo, dump expected, commit, dump actual, diff
        designspace_filename = os.path.join(directory, "test.designspace")
        git("init")
        write_designspace_and_UFOs(expected, designspace_filename)
        for source in expected.sources:
            normalize_ufo_lib(source.path)
            normalizeUFO(source.path, floatPrecision=3, writeModTimes=False)
        git("add", ".")
        git("commit", "-m", "expected")

        clean_git_folder()
        write_designspace_and_UFOs(actual, designspace_filename)
        for source in actual.sources:
            normalize_ufo_lib(source.path)
            normalizeUFO(source.path, floatPrecision=3, writeModTimes=False)
        git("add", ".")
        status = git("status")
        diff = git(
            "diff", "--staged", "--src-prefix= original/", "--dst-prefix=roundtrip/"
        )

        if diff:
            sys.stderr.write(status)
            sys.stderr.write(diff)

        self.assertEqual(0, len(diff), message)
コード例 #6
0
def run(options):

    ds_doc = DesignSpaceDocument.fromfile(options.dsPath)

    # can still have a successful read but useless DSD (no sources, no
    # instances, sources non-existent, other conditions
    validateDesignspaceDoc(ds_doc, options)

    copy_features = any(src.copyFeatures for src in ds_doc.sources)
    features_store = {}
    if not copy_features:
        # '<features copy="1"/>' is NOT set in any of masters.
        # Collect the contents of 'features.fea' of any existing
        # instances so that they can be restored later.
        features_store = collect_features_content(ds_doc.instances,
                                                  options.indexList)

    # Set the current dir to the design space dir, so that relative paths in
    # the design space file will work.
    dsDir, dsFile = os.path.split(os.path.abspath(options.dsPath))
    os.chdir(dsDir)
    options.dsPath = dsFile

    if options.indexList:
        dsPath = filterDesignspaceInstances(ds_doc, options)
    else:
        dsPath = dsFile

    newInstancesList = [inst.path for inst in ds_doc.instances]
    newInstancesCount = len(newInstancesList)

    if newInstancesCount == 1:
        logger.info("Building 1 instance...")
    else:
        logger.info("Building %s instances..." % newInstancesCount)
    ufoProcessorBuild(documentPath=dsPath,
                      outputUFOFormatVersion=options.ufo_version,
                      roundGeometry=(not options.no_round),
                      logger=logger)
    if (dsPath != options.dsPath) and os.path.exists(dsPath):
        os.remove(dsPath)

    logger.info("Built %s instances." % newInstancesCount)
    # Remove glyph.lib and font.lib (except for "public.glyphOrder")
    for instancePath in newInstancesList:
        postProcessInstance(instancePath, options)

    if options.doNormalize:
        logger.info("Applying UFO normalization...")
        for instancePath in newInstancesList:
            normalizeUFO(instancePath,
                         outputPath=None,
                         onlyModified=True,
                         writeModTimes=False)

    if options.doAutoHint or options.doOverlapRemoval:
        logger.info("Applying post-processing...")
        # Apply autohint and checkoutlines, if requested.
        for instancePath in newInstancesList:
            # make new instance font.
            updateInstance(instancePath, options)

    # checkoutlinesufo does ufotools.validateLayers()
    if not options.doOverlapRemoval:
        for instancePath in newInstancesList:
            # make sure that there are no old glyphs left in the
            # processed glyphs folder
            validateLayers(instancePath)

    # The defcon library renames glyphs. Need to fix them again
    if options.doOverlapRemoval or options.doAutoHint:
        for instancePath in newInstancesList:
            if options.doNormalize:
                normalizeUFO(instancePath,
                             outputPath=None,
                             onlyModified=False,
                             writeModTimes=False)

    # Restore the contents of the instances' 'features.fea' files
    for fea_pth, fea_cntnts in features_store.items():
        with open(fea_pth, 'w') as fp:
            fp.write(fea_cntnts)
コード例 #7
0
def build_masters(
    filename,
    master_dir,
    designspace_instance_dir=None,
    designspace_path=None,
    family_name=None,
    propagate_anchors=True,
    minimize_glyphs_diffs=False,
    normalize_ufos=False,
    create_background_layers=False,
    generate_GDEF=True,
    store_editor_state=True,
    write_skipexportglyphs=False,
):
    """Write and return UFOs from the masters and the designspace defined in a
    .glyphs file.

    Args:
        master_dir: Directory where masters are written.
        designspace_instance_dir: If provided, a designspace document will be
            written alongside the master UFOs though no instances will be built.
        family_name: If provided, the master UFOs will be given this name and
            only instances with this name will be included in the designspace.

    Returns:
        A named tuple of master UFOs (`ufos`) and the path to the designspace
        file (`designspace_path`).
    """

    font = GSFont(filename)

    if not os.path.isdir(master_dir):
        os.mkdir(master_dir)

    if designspace_instance_dir is None:
        instance_dir = None
    else:
        instance_dir = os.path.relpath(designspace_instance_dir, master_dir)

    designspace = to_designspace(
        font,
        family_name=family_name,
        propagate_anchors=propagate_anchors,
        instance_dir=instance_dir,
        minimize_glyphs_diffs=minimize_glyphs_diffs,
        generate_GDEF=generate_GDEF,
        store_editor_state=store_editor_state,
        write_skipexportglyphs=write_skipexportglyphs,
    )

    # Only write full masters to disk. This assumes that layer sources are always part
    # of another full master source, which must always be the case in a .glyphs file.
    ufos = {}
    for source in designspace.sources:
        if source.filename in ufos:
            assert source.font is ufos[source.filename]
            continue

        if create_background_layers:
            ufo_create_background_layer_for_all_glyphs(source.font)

        ufo_path = os.path.join(master_dir, source.filename)
        clean_ufo(ufo_path)
        source.font.save(ufo_path)

        if normalize_ufos:
            import ufonormalizer

            ufonormalizer.normalizeUFO(ufo_path, writeModTimes=False)

        ufos[source.filename] = source.font

    if not designspace_path:
        designspace_path = os.path.join(master_dir, designspace.filename)
    designspace.write(designspace_path)

    return Masters(ufos, designspace_path)
コード例 #8
0
import sys
import ufonormalizer

# The only argument is the base name of the font. No directory or extension.
fileName = sys.argv[1]

# File paths
ttf = os.path.join(os.path.join("..", "fonts", "ttf"), fileName + ".ttf")
ufo = os.path.join(os.path.join("..", "sources"), fileName + ".ufo")

# Create a new FontForge font object from the TrueType font file
font = fontforge.open(ttf)

#font.correctReferences()

#for glyph in font.glyphs():
#    glyph.removeOverlap()
#    glyph.correctDirection()
#    glyph.addExtrema()
#    glyph.simplify()
#    glyph.round()

# Save the font object as a UFO (Unified Font Object)
font.generate(ufo)

# Close the font object since we are going to normalize it next
font.close()

# Normalize the UFO without writing modification times
ufonormalizer.normalizeUFO(ufo, writeModTimes=False)