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)
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(