summaryrefslogtreecommitdiff
path: root/misc/pylib/fontbuild/Build.pyx
diff options
context:
space:
mode:
Diffstat (limited to 'misc/pylib/fontbuild/Build.pyx')
-rw-r--r--misc/pylib/fontbuild/Build.pyx365
1 files changed, 0 insertions, 365 deletions
diff --git a/misc/pylib/fontbuild/Build.pyx b/misc/pylib/fontbuild/Build.pyx
deleted file mode 100644
index d4fbd91d7..000000000
--- a/misc/pylib/fontbuild/Build.pyx
+++ /dev/null
@@ -1,365 +0,0 @@
-# Copyright 2015 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-import ConfigParser
-import os
-import sys
-import math
-import warnings
-
-from booleanOperations import BooleanOperationManager
-
-from cu2qu.ufo import fonts_to_quadratic
-from fontTools.misc.transform import Transform
-from robofab.world import OpenFont
-from ufo2ft import compileOTF, compileTTF
-
-from fontbuild.decomposeGlyph import decomposeGlyph
-from fontbuild.features import readFeatureFile, writeFeatureFile
-from fontbuild.generateGlyph import generateGlyph
-from fontbuild.instanceNames import setInfoRF
-from fontbuild.italics import italicizeGlyph
-from fontbuild.markFeature import RobotoFeatureCompiler, RobotoKernWriter
-from fontbuild.mitreGlyph import mitreGlyph
-from fontbuild.mix import Mix,Master,narrowFLGlyph
-
-
-class FontProject:
-
- def __init__(self, basefont, basedir, configfile, buildTag=''):
- self.basefont = basefont
- self.basedir = basedir
- self.config = ConfigParser.RawConfigParser()
- self.configfile = os.path.join(self.basedir, configfile)
- self.config.read(self.configfile)
- self.buildTag = buildTag
-
- self.diacriticList = [
- line.strip() for line in self.openResource("diacriticfile")
- if not line.startswith("#")]
- self.adobeGlyphList = dict(
- line.split(";") for line in self.openResource("agl_glyphlistfile")
- if not line.startswith("#"))
- self.glyphOrder = self.openResource("glyphorder")
-
- # map exceptional glyph names in Roboto to names in the AGL
- roboNames = (
- ('Obar', 'Ocenteredtilde'), ('obar', 'obarred'),
- ('eturn', 'eturned'), ('Iota1', 'Iotaafrican'))
- for roboName, aglName in roboNames:
- self.adobeGlyphList[roboName] = self.adobeGlyphList[aglName]
-
- self.builddir = "out"
- self.decompose = self.config.get("glyphs","decompose").split()
- self.predecompose = set(self.config.get("glyphs","predecompose").split())
- self.predecompose_auto = 1 # unless 0, automatically populate predecompose
- self.lessItalic = set(self.config.get("glyphs","lessitalic").split())
- self.deleteList = set(self.config.get("glyphs","delete").split())
- self.noItalic = set(self.config.get("glyphs","noitalic").split())
-
- self.buildOTF = False
- self.compatible = False
- self.generatedFonts = []
- self.justCompileUFO = False
-
- def openResource(self, name):
- with open(os.path.join(
- self.basedir, self.config.get("res", name))) as resourceFile:
- resource = resourceFile.read()
- return resource.splitlines()
-
- def generateOutputPath(self, font, ext):
- family = font.info.familyName.replace(" ", "")
- style = font.info.styleName.replace(" ", "")
- path = os.path.join(self.basedir, self.builddir, family + ext.upper())
- if not os.path.exists(path):
- os.makedirs(path)
- return os.path.join(path, "%s-%s.%s" % (family, style, ext))
-
- def generateFont(self, mix, names, italic=False, swapSuffixes=None, stemWidth=185,
- italicMeanYCenter=-825, scaleX=1, panose=[]):
-
- n = names.split("/")
- log("---------------------\n%s, %s\n----------------------" %(n[0],n[1]))
-
- if isinstance( mix, Mix):
- log(">> Mixing masters")
- f = mix.generateFont(self.basefont, self.deleteList)
- else:
- f = mix.copy()
- if not self.justCompileUFO:
- deleteGlyphs(f, self.deleteList)
-
- if italic == True:
- log(">> Italicizing")
- i = 0
- for g in f:
- decomposeGlyph(f, g)
- removeGlyphOverlap(g)
-
- for g in f:
- i += 1
- if i % 10 == 0: print g.name
-
- if g.name not in self.noItalic:
- if g.name in self.lessItalic:
- italicizeGlyph(f, g, 10, stemWidth=stemWidth,
- meanYCenter=italicMeanYCenter,
- scaleX=scaleX)
- else:
- italicizeGlyph(f, g, 12, stemWidth=stemWidth,
- meanYCenter=italicMeanYCenter,
- scaleX=scaleX)
-
- # set the oblique flag in fsSelection
- f.info.openTypeOS2Selection.append(9)
-
- if swapSuffixes != None:
- for swap in swapSuffixes:
- swapList = [g.name for g in f if g.name.endswith(swap)]
- for gname in swapList:
- print gname
- swapContours(f, gname.replace(swap,""), gname)
-
- if self.predecompose_auto == 1:
- self.predecompose_auto = 2
- for g in self.basefont:
- if len(g.components) > 0:
- self.predecompose.add(g.name)
-
- if not self.justCompileUFO:
- for gname in self.predecompose:
- if f.has_key(gname):
- decomposeGlyph(f, f[gname])
-
- log(">> Generating glyphs")
- self.generateGlyphs(f, self.diacriticList, self.adobeGlyphList)
- # log(">> Reading features")
- # readFeatureFile(f, f.features.text)
-
- # adjust width of italic glyphs
- if italic == True:
- widthAdjustment = -8
- if widthAdjustment != 0:
- leftAdjustment = math.floor(widthAdjustment / 2)
- rightAdjustment = math.ceil(widthAdjustment / 2)
- for g in f:
- if g.name not in self.noItalic:
- if g.width != 0:
- if g.box is None:
- g.width += widthAdjustment
- else:
- newLeftMargin = int(g.leftMargin + leftAdjustment)
- newRightMargin = int(g.rightMargin + rightAdjustment)
- g.leftMargin = newLeftMargin
- g.rightMargin = newRightMargin
-
- if not self.justCompileUFO:
- log(">> Decomposing")
- # for g in f:
- # if len(g.components) > 0:
- # decomposeGlyph(f, g)
- for gname in self.decompose:
- if f.has_key(gname):
- decomposeGlyph(f, f[gname])
-
- copyrightHolderName = ''
- if self.config.has_option('main', 'copyrightHolderName'):
- copyrightHolderName = self.config.get('main', 'copyrightHolderName')
-
- def getcfg(name, fallback=''):
- if self.config.has_option('main', name):
- return self.config.get('main', name)
- else:
- return fallback
-
- setInfoRF(f, n, {
- 'foundry': getcfg('foundry'),
- 'foundryURL': getcfg('foundryURL'),
- 'designer': getcfg('designer'),
- 'copyrightHolderName': getcfg('copyrightHolderName'),
- 'build': self.buildTag,
- 'version': getcfg('version'),
- 'license': getcfg('license'),
- 'licenseURL': getcfg('licenseURL'),
- 'italicAngle': float(getcfg('italicAngle', '-12')),
- }, panose)
-
- if not self.compatible and not self.justCompileUFO:
- # Remove overlaps etc
- cleanCurves(f)
- # deleteGlyphs(f, self.deleteList)
-
- log(">> Generating font files")
- ufoName = self.generateOutputPath(f, "ufo")
- f.save(ufoName)
- self.generatedFonts.append(ufoName)
-
- if self.justCompileUFO:
- print("wrote %s" % ufoName)
- return
-
- # filter glyphorder -- only include glyphs that exists in font
- glyphOrder = []
- seenGlyphNames = set()
- missingGlyphs = []
- for glyphName in self.glyphOrder:
- if glyphName in f:
- if glyphName in seenGlyphNames:
- raise Exception('Duplicate glyphname %r in glyphorder' % glyphName)
- seenGlyphNames.add(glyphName)
- glyphOrder.append(glyphName)
-
- if self.buildOTF:
- newFont = OpenFont(ufoName)
- otfName = self.generateOutputPath(f, "otf")
- log(">> Generating OTF file %s" % otfName)
- saveOTF(newFont, otfName, glyphOrder)
-
- def generateTTFs(self):
- """Build TTF for each font generated since last call to generateTTFs."""
-
- fonts = [OpenFont(ufo) for ufo in self.generatedFonts]
- self.generatedFonts = []
-
- log(">> Converting curves to quadratic")
- # using a slightly higher max error (e.g. 0.0025 em), dots will have
- # fewer control points and look noticeably different
- max_err = 0.001
- if self.compatible:
- fonts_to_quadratic(fonts, max_err_em=max_err, dump_stats=True, reverse_direction=True)
- else:
- for font in fonts:
- fonts_to_quadratic([font], max_err_em=max_err, dump_stats=True, reverse_direction=True)
-
- for font in fonts:
- ttfName = self.generateOutputPath(font, "ttf")
- log(">> Generating TTF file %s" % ttfName)
- log(os.path.basename(ttfName))
- glyphOrder = [n for n in self.glyphOrder if n in font]
- saveOTF(font, ttfName, glyphOrder, truetype=True)
-
- def generateGlyphs(self, f, glyphNames, glyphList={}):
- log(">> Generating diacritics")
- glyphnames = [gname for gname in glyphNames if not gname.startswith("#") and gname != ""]
- for glyphName in glyphNames:
- generateGlyph(f, glyphName, glyphList)
-
-
-# def transformGlyphMembers(g, m):
-# g.width = int(g.width * m.a)
-# g.Transform(m)
-# for a in g.anchors:
-# p = Point(a.p)
-# p.Transform(m)
-# a.p = p
-# for c in g.components:
-# # Assumes that components have also been individually transformed
-# p = Point(0,0)
-# d = Point(c.deltas[0])
-# d.Transform(m)
-# p.Transform(m)
-# d1 = d - p
-# c.deltas[0].x = d1.x
-# c.deltas[0].y = d1.y
-# s = Point(c.scale)
-# s.Transform(m)
-# #c.scale = s
-
-
-def swapContours(f,gName1,gName2):
- try:
- g1 = f[gName1]
- g2 = f[gName2]
- except KeyError:
- log("swapGlyphs failed for %s %s" % (gName1, gName2))
- return
- g3 = g1.copy()
-
- while g1.contours:
- g1.removeContour(0)
- for contour in g2.contours:
- g1.appendContour(contour)
- g1.width = g2.width
-
- while g2.contours:
- g2.removeContour(0)
- for contour in g3.contours:
- g2.appendContour(contour)
- g2.width = g3.width
-
-
-def log(msg):
- print msg
-
-
-def deleteGlyphs(f, deleteList):
- for name in deleteList:
- if f.has_key(name):
- f.removeGlyph(name)
-
-
-def cleanCurves(f):
- log(">> Removing overlaps")
- for g in f:
- if len(g.components) > 0:
- decomposeGlyph(f, g)
- removeGlyphOverlap(g)
-
- # log(">> Mitring sharp corners")
- # for g in f:
- # mitreGlyph(g, 3., .7)
-
- # log(">> Converting curves to quadratic")
- # for g in f:
- # glyphCurvesToQuadratic(g)
-
-
-def removeGlyphOverlap(g):
- """Remove overlaps in contours from a glyph."""
- # Note: Although it theoretically would be more efficient to first check
- # if contours has overlap before applying clipping, boolean ops and
- # re-drawing the shapes, the booleanOperations library's getIntersections
- # function adds more overhead in the real world, compared to bluntly
- # computing the union for every single glyph.
- #
- # If we can find a really cheap/efficient way to check if there's any
- # overlap, then we should do that before going ahead and computing the
- # union. Keep in mind that some glyphs have just a single contour that
- # intersects itself, for instance "e".
-
- contours = g.contours
- g.clearContours()
- BooleanOperationManager.union(contours, g.getPointPen())
-
-
-def saveOTF(font, destFile, glyphOrder, truetype=False):
- """Save a RoboFab font as an OTF binary using ufo2fdk."""
- with warnings.catch_warnings():
- # Note: ignore warnings produced by compreffor, used by ufo2ft:
- # 'Compreffor' class is deprecated; use 'compress' function instead
- # which is triggered by ufo2ft which we can't update because newer
- # versions completely break the API of ufo2ft.
- warnings.simplefilter("ignore")
- if truetype:
- otf = compileTTF(font, featureCompilerClass=RobotoFeatureCompiler,
- kernWriter=RobotoKernWriter, glyphOrder=glyphOrder,
- convertCubics=False,
- useProductionNames=False)
- else:
- otf = compileOTF(font, featureCompilerClass=RobotoFeatureCompiler,
- kernWriter=RobotoKernWriter, glyphOrder=glyphOrder,
- useProductionNames=False)
- otf.save(destFile)