Beispiel #1
0
def buildDesignSpace(masterFont=None,
                     destPath=None,
                     glyphNames=[],
                     compositionType="rotate",
                     outlineAmount=None,
                     zOffset=None,
                     shadowLengthFactor=1,
                     doForceSmooth=False,
                     doMakeSubSources=False,
                     familyName=None,
                     alwaysConnect=False,
                     cap="RoundSimple",
                     connection="Round",
                     layerName=None,
                     styleName=None):

    # Open the master UFO
    if type(masterFont) == str:
        masterFont = OpenFont(masterFont, showInterface=False)

    # Get the master file name, if it's saved
    basePath = None
    masterFileName = "Font"
    if masterFont.path:
        basePath, masterFileName = os.path.split(masterFont.path)

    # Try to make a dest path, if there isn't one
    if destPath == None:
        if basePath:
            destPath = os.path.join(basePath, "Rotated")

    # Make new folders for the destPath
    if not os.path.exists(destPath):
        os.makedirs(destPath)

    # Use all glyphs, if no names are called for
    if glyphNames == []:
        glyphNames = list(masterFont.keys())
        glyphNames.sort()

    # Default names
    if not familyName:
        familyName = masterFont.info.familyName
    if not styleName:
        styleName = "Regular"
    """ Collect glyph data """
    # Organize the point data out of the glyph lib
    # and check to see which glyphs need to be present in a SubSource
    glyphPointData = {}
    needSubHROT = [
    ]  # Glyphs that need to be included in a SubSource when HROT is default
    needSubVROT = []
    for gName in glyphNames:
        if gName in masterFont:
            g = masterFont[gName]
            if layerName:
                # Copy point data to the layer
                if ZPOSITIONLIBKEY in g.lib.keys():
                    libdata = copy.deepcopy(g.lib[ZPOSITIONLIBKEY])
                else:
                    libdata = {}
                g = g.getLayer(layerName)
                g.lib[ZPOSITIONLIBKEY] = libdata
            pointData = readGlyphPointData(g)
            glyphPointData[gName] = pointData
            # Test for self-overlapping contours
            if doMakeSubSources == True:
                overlapResult = checkCurveOverlap(g)
                if "x" in overlapResult:
                    needSubHROT.append(gName)
                elif "y" in overlapResult:
                    needSubVROT.append(gName)
    """ Organize Source combinations """

    # Collect source info, based on the layout type
    # Organize file names, designspace locations, glyph lists, etc.
    sourceCombinations = []
    for locHROT, tagHROT in [(-45, "HROTn"), (0, "HROTd"), (45, "HROTx")]:
        for locVROT, tagVROT in [(-45, "VROTn"), (0, "VROTd"), (45, "VROTx")]:
            if "shadow" in compositionType:
                for locSLEN, tagSLEN in [(0, "SLENn"), (100, "SLENx")]:
                    for locSANG, tagSANG in [(-45, "SANGn"), (45, "SANGx")]:
                        # Rotate and Shadow
                        fileName = "Source-%s_%s_%s_%s.ufo" % (
                            tagHROT, tagVROT, tagSLEN, tagSANG)
                        loc = dict(HROT=locHROT,
                                   VROT=locVROT,
                                   SLEN=locSLEN,
                                   SANG=locSANG)
                        sourceInfo = dict(glyphNames=glyphNames,
                                          loc=loc,
                                          fileName=fileName,
                                          nudgeLoc=[0, 0])
                        sourceCombinations.append(sourceInfo)
            elif "depth" in compositionType:
                for locDPTH, tagDPTH in [(0, "DPTHn"), (100, "DPTHx")]:
                    # Rotate and depth
                    fileName = "Source-%s_%s_%s.ufo" % (tagHROT, tagVROT,
                                                        tagDPTH)
                    loc = dict(HROT=locHROT, VROT=locVROT, DPTH=locDPTH)
                    sourceInfo = dict(glyphNames=glyphNames,
                                      loc=loc,
                                      fileName=fileName,
                                      nudgeLoc=[0, 0])
                    sourceCombinations.append(sourceInfo)
            else:
                # Rotate only
                fileName = "Source-%s_%s.ufo" % (tagHROT, tagVROT)
                loc = dict(HROT=locHROT, VROT=locVROT)
                sourceInfo = dict(glyphNames=glyphNames,
                                  loc=loc,
                                  fileName=fileName,
                                  nudgeLoc=[0, 0])
                sourceCombinations.append(sourceInfo)

    # Process the sourceCombinations and make SubSources if necessary
    #print("needSubHROT", needSubHROT)
    #print("needSubVROT", needSubVROT)
    # @@@ Temporarily force all glyphs to be in all submasters
    needSubHROT = glyphNames
    needSubVROT = glyphNames
    if doMakeSubSources:
        doSubHROT = len(needSubHROT)
        doSubVROT = len(needSubVROT)
    else:
        doSubHROT = False
        doSubVROT = False
    # Loop through once to add new HROT SubSources
    newSourceCombos = []
    for sourceInfo in sourceCombinations:
        if sourceInfo["loc"]["HROT"] == 0:
            if doSubHROT:
                subSourceInfo = copy.deepcopy(sourceInfo)
                subSourceInfo["nudgeLoc"][
                    0] = 0  # Don't nudge, move the location instead
                subSourceInfo["loc"]["HROT"] += SLIGHTLYOFFAXIS
                subSourceInfo["glyphNames"] = needSubHROT
                subSourceInfo[
                    "fileName"] = "Sub" + subSourceInfo["fileName"].replace(
                        "HROTd", "HROTdd")
                newSourceCombos.append(subSourceInfo)
                # Nudge the default source
                sourceInfo["nudgeLoc"][0] -= SLIGHTLYOFFAXIS
    sourceCombinations += newSourceCombos
    # Looping back through to add VROT SubSources and to catch all of the new HROT SubSources
    newSourceCombos = []
    for sourceInfo in sourceCombinations:
        if sourceInfo["loc"]["VROT"] == 0:
            if doSubVROT:
                subSourceInfo = copy.deepcopy(sourceInfo)
                subSourceInfo["nudgeLoc"][
                    1] = 0  # Don't nudge, move the location instead
                subSourceInfo["loc"]["VROT"] -= SLIGHTLYOFFAXIS
                # Append the glyph list if this was the HROT=SLIGHTLYOFFAXIS
                if not subSourceInfo["loc"]["HROT"] == SLIGHTLYOFFAXIS:
                    subSourceInfo["glyphNames"] = []
                subSourceInfo["glyphNames"] += needSubVROT
                subSourceInfo["fileName"] = subSourceInfo["fileName"].replace(
                    "VROTd", "VROTdd")
                if not "Sub" in subSourceInfo["fileName"]:
                    subSourceInfo[
                        "fileName"] = "Sub" + subSourceInfo["fileName"]
                newSourceCombos.append(subSourceInfo)
                # Nudge the default source
                sourceInfo["nudgeLoc"][1] += SLIGHTLYOFFAXIS
    sourceCombinations += newSourceCombos
    """ Make the source UFOs """

    for sourceInfo in sourceCombinations:
        sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"])
        if not os.path.exists(sourceUfoPath):
            sourceFont = NewFont(showInterface=False)
            sourceFont.save(sourceUfoPath)
            sourceFont.info.familyName = familyName
            sourceFont.info.styleName = styleName
            sourceFont.save()
            sourceFont.close()
    """ Process Glyphs into Source UFOs """

    # Process each UFO source, one at a time
    for sourceInfo in sourceCombinations:

        # Combine the nudgeLoc and loc, use this value when rotating
        rotateLoc = copy.deepcopy(sourceInfo["loc"])
        rotateLoc["HROT"] += sourceInfo["nudgeLoc"][0]
        rotateLoc["VROT"] += sourceInfo["nudgeLoc"][1]

        sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"])
        sourceFont = OpenFont(sourceUfoPath, showInterface=False)

        for gName in sourceInfo["glyphNames"]:
            if gName in glyphPointData:
                pointData = copy.deepcopy(glyphPointData[gName])
            else:
                pointData = {}

            # Get the glyph started
            g = masterFont[gName]
            if layerName:
                g = g.getLayer(layerName)
            # Remove the glyph if it already existed and make a new one
            if gName in sourceFont:
                for layer in sourceFont.layers:
                    if gName in layer:
                        layer.removeGlyph(gName)
            sourceFont.newGlyph(gName)
            gDest = sourceFont[gName]
            gDest.appendGlyph(g)
            gDest.width = g.width
            gDest.unicode = g.unicode

            # Add anchors to the pointData so that they shift correctly
            # Use anchor + str(idx) as the ident
            for aIdx, anc in enumerate(gDest.anchors):
                ident = "anchor%s" % aIdx
                pos = list(anc.position)
                pointData[ident] = dict(x=pos[0], y=pos[1], z=0)

            # Decompose components in glyphs that were not bulit with Glyph Builder
            #   If the glyph has components, and if it's not marked with the "Glyph Builder Gray",
            #   it will need to be decomposed at this stage (but leave overlaps of course)
            #   Otherwise, glyphs that are gray will have their components reapplied after rotating with Glyph Builder.
            isComponent = False
            if not g.markColor == COMPOSITEGRAY:
                if len(gDest.components):
                    for c in gDest.components:
                        baseName = c.baseGlyph
                        masterBaseGlyph = masterFont[baseName]
                        # Decomposing from the master font because the base glyph might not be in the source font yet
                        for mc in masterBaseGlyph.contours:
                            gDest.appendContour(mc, offset=c.offset)
                        gDest.removeComponent(c)
                        # ...and copy over point data, taking into account the component offset
                        if baseName in glyphPointData:
                            basePointData = copy.deepcopy(
                                glyphPointData[baseName])
                            for ident in basePointData:
                                pointData[ident] = dict(
                                    x=basePointData[ident]["x"] + c.offset[0],
                                    y=basePointData[ident]["y"] + c.offset[1],
                                    z=basePointData[ident]["z"])
                        isComponent = True

            # Flatten the depth
            if "DPTH" in sourceInfo["loc"].keys():
                for ident in pointData:
                    pointData[ident]["z"] *= (sourceInfo["loc"]["DPTH"] * 0.01
                                              )  # Scale by the depth value

            # Shift the "z" value by an offset
            if not zOffset == None:
                for ident in pointData:
                    if not "anchor" in ident:  # ...but don't shift the anchors, the components are already shifted
                        pointData[ident]["z"] += zOffset

            # Extend the shadow
            if "SANG" in sourceInfo["loc"].keys():
                if sourceInfo["loc"]["SANG"] == -45:
                    shadowDirection = "left"
                else:
                    shadowDirection = "right"
                finalShadowLengthFactor = (sourceInfo["loc"]["SLEN"] *
                                           0.01) * shadowLengthFactor
                pointData = flattenShadow(gDest, pointData, shadowDirection,
                                          finalShadowLengthFactor)

            # Rotate the glyph
            # Merge the location and the nudgeLoc, if there is one
            marginChange, pointData = rotateGlyphPointData(
                gDest, rotateLoc, pointData)

            # Move the contour points into the correct position
            for c in gDest.contours:
                for pt in c.points:
                    ident = getIdent(pt)
                    if ident in pointData:
                        pt.x = pointData[ident]["x"]
                        pt.y = pointData[ident]["y"]

            # Move the anchors into the correct position
            for aIdx, anc in enumerate(gDest.anchors):
                ident = "anchor%s" % aIdx
                if ident in pointData:
                    anc.position = (int(round(pointData[ident]["x"])),
                                    int(round(pointData[ident]["y"])))

            # Shift the glyph to take in the sidebearings
            gDest.moveBy((-marginChange[0], 0))
            gDest.width -= marginChange[0] * 2

            if doForceSmooth and not isComponent:
                # If a bPoint was a smooth curve point in the original glyph,
                # force the related bPoint in the rotated glyph to be smooth
                for cIdx, c in enumerate(gDest.contours):
                    for bptIdx, thisBPt in enumerate(c.bPoints):
                        sourceBPt = g.contours[cIdx].bPoints[bptIdx]
                        if sourceBPt.type == "curve":
                            forceSmooth(thisBPt)

            # Round the point coordinates before outlining
            gDest.round()
            gDest.changed()

            # Outline the glyph
            if outlineAmount:
                outlineGlyph(sourceFont,
                             gDest,
                             outlineAmount,
                             alwaysConnect=alwaysConnect,
                             cap=cap,
                             connection=connection)

                # Round the point coordinates again, now that it's outlined
                gDest.round()
                gDest.changed()

            # Update
            #gDest.changed()

        # Resort the font
        sourceFont.glyphOrder = masterFont.glyphOrder

        # Copy the kerning
        sourceFont.groups.update(copy.deepcopy(masterFont.groups))
        sourceFont.kerning.update(copy.deepcopy(masterFont.kerning))

        # Copy the features
        sourceFont.features.text = masterFont.features.text

        # Done, save
        sourceFont.changed()
        sourceFont.save()
    """ New DesignSpaceDocument """

    designSpace = DesignSpaceDocument()
    designSpaceDocFilename = os.path.splitext(
        masterFileName)[0] + ".designspace"
    designSpaceDocPath = os.path.join(destPath, designSpaceDocFilename)
    """ Axis Descriptors """

    for tag in sourceCombinations[0]["loc"].keys():
        a = AxisDescriptor()
        a.minimum = AXISINFO[tag]["minimum"]
        a.maximum = AXISINFO[tag]["maximum"]
        a.default = AXISINFO[tag]["default"]
        a.name = AXISINFO[tag]["name"]
        a.tag = tag
        a.labelNames[u'en'] = AXISINFO[tag]["name"]
        designSpace.addAxis(a)
    """ Source Descriptors """

    for sourceInfo in sourceCombinations:
        sourceUfoPath = os.path.join(destPath, sourceInfo["fileName"])
        # Make a source description
        s = SourceDescriptor()
        s.path = sourceUfoPath
        s.name = os.path.splitext(sourceInfo["fileName"])[0]
        #s.font = defcon.Font(s.name)
        s.copyLib = True
        s.copyInfo = True
        s.copyInfoures = True
        s.familyName = masterFont.info.familyName
        s.styleName = s.name
        # Convert the loc from tags to names
        loc = {}
        for tag, value in sourceInfo["loc"].items():
            axisName = AXISINFO[tag]["name"]
            loc[axisName] = value
        s.location = loc
        designSpace.addSource(s)

    designSpace.write(designSpaceDocPath)
Beispiel #2
0
                f = OpenFont(destPath)
                src = OpenFont(destPath)
            else:
                f = OpenFont(destPath)

            if buildSlugs:
                print "Building slugs..."
                for slug in slugs.items():
                    slugName, slugInfo = slug
                    slugComponents, slugUnicode = slugInfo
                    setSlug(f, slugName, slugComponents)
                    f[slugName].unicodes = [slugUnicode]

            if i != 0 and addMissingGlyphsFromDefault:
                print "Adding missing glyphs in sources from default..."
                for gname in src.keys():
                    g = src[gname]

                    if g.name not in f or (g.name in f
                                           and not f[g.name].contours
                                           and not f[g.name].components):
                        f.insertGlyph(g, name=g.name)

            f.save()
            masters2.append('temp/' + master)
    else:
        masters2 = masters
    print masters2

    project = FontProject()
    project.run_from_ufos(