def setupTable_post(self): """ Make the post table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["post"] = post = newTable("post") font = self.ufo post.formatType = 3.0 # italic angle italicAngle = getAttrWithFallback(font.info, "italicAngle") post.italicAngle = italicAngle # underline underlinePosition = getAttrWithFallback(font.info, "postscriptUnderlinePosition") if underlinePosition is None: underlinePosition = 0 post.underlinePosition = _roundInt(underlinePosition) underlineThickness = getAttrWithFallback(font.info, "postscriptUnderlineThickness") if underlineThickness is None: underlineThickness = 0 post.underlineThickness = _roundInt(underlineThickness) # determine if the font has a fixed width widths = set([glyph.width for glyph in self.allGlyphs.values()]) post.isFixedPitch = getAttrWithFallback(font.info, "postscriptIsFixedPitch") # misc post.minMemType42 = 0 post.maxMemType42 = 0 post.minMemType1 = 0 post.maxMemType1 = 0
def test_caret_slope(self): info = TestInfoObject() self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRise"), 1) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRun"), 0) info.italicAngle = -12 self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRise"), 1000) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRun"), 213) info.italicAngle = 12 self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRise"), 1000) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRun"), -213) info.openTypeHheaCaretSlopeRise = 2048 self.assertFalse(hasattr(info, "openTypeHheaCaretSlopeRun")) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRise"), 2048) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRun"), -435) del info.openTypeHheaCaretSlopeRise info.openTypeHheaCaretSlopeRun = 200 self.assertFalse(hasattr(info, "openTypeHheaCaretSlopeRise")) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRise"), -941) self.assertEqual( getAttrWithFallback(info, "openTypeHheaCaretSlopeRun"), 200)
def setupTable_post(self): """ Make the post table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["post"] = post = newTable("post") font = self.ufo post.formatType = 3.0 # italic angle italicAngle = getAttrWithFallback(font.info, "italicAngle") post.italicAngle = italicAngle # underline underlinePosition = getAttrWithFallback(font.info, "postscriptUnderlinePosition") post.underlinePosition = _roundInt(underlinePosition) underlineThickness = getAttrWithFallback( font.info, "postscriptUnderlineThickness") post.underlineThickness = _roundInt(underlineThickness) # determine if the font has a fixed width widths = set([glyph.width for glyph in self.allGlyphs.values()]) post.isFixedPitch = getAttrWithFallback(font.info, "postscriptIsFixedPitch") # misc post.minMemType42 = 0 post.maxMemType42 = 0 post.minMemType1 = 0 post.maxMemType1 = 0
def test_empty_info_2048(self, InfoClass): info = InfoClass() info.unitsPerEm = 2048 assert getAttrWithFallback(info, "unitsPerEm") == 2048 assert getAttrWithFallback(info, "ascender") == 1638 assert getAttrWithFallback(info, "capHeight") == 1434 assert getAttrWithFallback(info, "xHeight") == 1024 assert getAttrWithFallback(info, "descender") == -410
def test_head_created(self, info): os.environ["SOURCE_DATE_EPOCH"] = "1514485183" try: assert (getAttrWithFallback( info, "openTypeHeadCreated") == "2017/12/28 18:19:43") finally: del os.environ["SOURCE_DATE_EPOCH"] assert getAttrWithFallback( info, "openTypeHeadCreated") != "2017/12/28 18:19:43"
def test_head_created(self): info = TestInfoObject() os.environ["SOURCE_DATE_EPOCH"] = '1514485183' try: self.assertEqual(getAttrWithFallback(info, "openTypeHeadCreated"), '2017/12/28 18:19:43') finally: del os.environ["SOURCE_DATE_EPOCH"] self.assertNotEqual(getAttrWithFallback(info, "openTypeHeadCreated"), '2017/12/28 18:19:43')
def get_origin_height(self, font, origin): if origin is self.Origin.BASELINE: return 0 elif origin is self.Origin.CAP_HEIGHT: return getAttrWithFallback(font.info, "capHeight") elif origin is self.Origin.HALF_CAP_HEIGHT: return otRound(getAttrWithFallback(font.info, "capHeight") / 2) elif origin is self.Origin.X_HEIGHT: return getAttrWithFallback(font.info, "xHeight") elif origin is self.Origin.HALF_X_HEIGHT: return otRound(getAttrWithFallback(font.info, "xHeight") / 2) else: raise AssertionError(origin)
def test_redundant_metadata(self, info): assert getAttrWithFallback(info, "openTypeNameVersion") == "Version 0.000" info.versionMinor = 1 info.versionMajor = 1 assert getAttrWithFallback(info, "openTypeNameVersion") == "Version 1.001" assert ( getAttrWithFallback(info, "openTypeNameUniqueID") == "1.001;NONE;FamilyName-StyleName" ) assert getAttrWithFallback(info, "postscriptSlantAngle") == 0
def test_redundant_metadata(self): info = TestInfoObject() self.assertEqual(getAttrWithFallback(info, "openTypeNameVersion"), "Version 0.000") info.versionMinor = 1 info.versionMajor = 1 self.assertEqual(getAttrWithFallback(info, "openTypeNameVersion"), "Version 1.001") self.assertEqual(getAttrWithFallback(info, "openTypeNameUniqueID"), "Version 1.001;NONE;Family Name Style Name Regular") self.assertEqual(getAttrWithFallback(info, "postscriptSlantAngle"), 0) self.assertEqual(getAttrWithFallback(info, "postscriptWeightName"), "Normal")
def makeMissingRequiredGlyphs(self): """ Add .notdef to the font if it is not present. **This should not be called externally.** Subclasses may override this method to handle the glyph creation in a different way if desired. """ glyphs = {} font = self.ufo unitsPerEm = _roundInt(getAttrWithFallback(font.info, "unitsPerEm")) ascender = _roundInt(getAttrWithFallback(font.info, "ascender")) descender = _roundInt(getAttrWithFallback(font.info, "descender")) defaultWidth = _roundInt(unitsPerEm * 0.5) if ".notdef" not in self.ufo: glyphs[".notdef"] = StubGlyph(name=".notdef", width=defaultWidth, unitsPerEm=unitsPerEm, ascender=ascender, descender=descender) return glyphs
def test_with_blue_zones(self): info = TestInfoObject() info.postscriptBlueValues = [ -13, 0, 470, 483, 534, 547, 556, 569, 654, 667, 677, 690, 738, 758 ] info.postscriptOtherBlues = [-255, -245] postscriptBlueScale = getAttrWithFallback(info, "postscriptBlueScale") self.assertEqual(postscriptBlueScale, 0.0375)
def set_context(self, font, glyphSet): ctx = super(CubicToQuadraticFilter, self).set_context(font, glyphSet) relativeError = self.options.conversionError or DEFAULT_MAX_ERR ctx.absoluteError = relativeError * getAttrWithFallback( font.info, "unitsPerEm") ctx.stats = {} return ctx
def setupTable_head(self): """ Make the head table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["head"] = head = newTable("head") font = self.ufo head.checkSumAdjustment = 0 head.tableVersion = 1.0 head.magicNumber = 0x5F0F3CF5 # version numbers # limit minor version to 3 digits as recommended in OpenType spec: # https://www.microsoft.com/typography/otspec/recom.htm versionMajor = getAttrWithFallback(font.info, "versionMajor") versionMinor = getAttrWithFallback(font.info, "versionMinor") fullFontRevision = float("%d.%03d" % (versionMajor, versionMinor)) head.fontRevision = round(fullFontRevision, 3) if head.fontRevision != fullFontRevision: print("Minor version in %s has too many digits and won't fit into " "the head table's fontRevision field; rounded to %s." % (fullFontRevision, head.fontRevision)) # upm head.unitsPerEm = getAttrWithFallback(font.info, "unitsPerEm") # times head.created = dateStringToTimeValue(getAttrWithFallback(font.info, "openTypeHeadCreated")) - mac_epoch_diff head.modified = dateStringToTimeValue(dateStringForNow()) - mac_epoch_diff # bounding box xMin, yMin, xMax, yMax = self.fontBoundingBox head.xMin = xMin head.yMin = yMin head.xMax = xMax head.yMax = yMax # style mapping styleMapStyleName = getAttrWithFallback(font.info, "styleMapStyleName") macStyle = [] if styleMapStyleName == "bold": macStyle = [0] elif styleMapStyleName == "bold italic": macStyle = [0, 1] elif styleMapStyleName == "italic": macStyle = [1] head.macStyle = intListToNum(macStyle, 0, 16) # misc head.flags = intListToNum(getAttrWithFallback(font.info, "openTypeHeadFlags"), 0, 16) head.lowestRecPPEM = _roundInt(getAttrWithFallback(font.info, "openTypeHeadLowestRecPPEM")) head.fontDirectionHint = 2 head.indexToLocFormat = 0 head.glyphDataFormat = 0
def test_empty_info(self, InfoClass): info = InfoClass() assert getAttrWithFallback(info, "familyName") == "New Font" assert getAttrWithFallback(info, "styleName") == "Regular" assert getAttrWithFallback(info, "unitsPerEm") == 1000 assert getAttrWithFallback(info, "ascender") == 800 assert getAttrWithFallback(info, "capHeight") == 700 assert getAttrWithFallback(info, "xHeight") == 500 assert getAttrWithFallback(info, "descender") == -200
def makeMissingRequiredGlyphs(font, glyphSet): """ Add .notdef to the glyph set if it is not present. **This should not be called externally.** Subclasses may override this method to handle the glyph creation in a different way if desired. """ if ".notdef" in glyphSet: return unitsPerEm = round(getAttrWithFallback(font.info, "unitsPerEm")) ascender = round(getAttrWithFallback(font.info, "ascender")) descender = round(getAttrWithFallback(font.info, "descender")) defaultWidth = round(unitsPerEm * 0.5) glyphSet[".notdef"] = StubGlyph(name=".notdef", width=defaultWidth, unitsPerEm=unitsPerEm, ascender=ascender, descender=descender)
def test_redundant_metadata(self): info = TestInfoObject() self.assertEqual( getAttrWithFallback(info, "openTypeNameVersion"), "Version 0.000") info.versionMinor = 1 info.versionMajor = 1 self.assertEqual( getAttrWithFallback(info, "openTypeNameVersion"), "Version 1.001") self.assertEqual( getAttrWithFallback(info, "openTypeNameUniqueID"), "Version 1.001;NONE;Family Name Style Name Regular") self.assertEqual(getAttrWithFallback(info, "postscriptSlantAngle"), 0) self.assertEqual( getAttrWithFallback(info, "postscriptWeightName"), "Normal")
def test_caret_slope(self, info): assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRise") == 1 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRun") == 0 info.italicAngle = -12 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRise") == 1000 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRun") == 213 info.italicAngle = 12 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRise") == 1000 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRun") == -213 info.openTypeHheaCaretSlopeRise = 2048 assert info.openTypeHheaCaretSlopeRun is None assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRise") == 2048 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRun") == -435 info.openTypeHheaCaretSlopeRise = None info.openTypeHheaCaretSlopeRun = 200 assert info.openTypeHheaCaretSlopeRise is None assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRise") == -941 assert getAttrWithFallback(info, "openTypeHheaCaretSlopeRun") == 200
def test_vertical_metrics(self, info): assert getAttrWithFallback(info, "openTypeHheaAscender") == 950 assert getAttrWithFallback(info, "openTypeHheaDescender") == -250 assert getAttrWithFallback(info, "openTypeOS2TypoAscender") == 650 assert getAttrWithFallback(info, "openTypeOS2TypoDescender") == -250 assert getAttrWithFallback(info, "openTypeOS2WinAscent") == 950 assert getAttrWithFallback(info, "openTypeOS2WinDescent") == 250
def setupTable_hhea(self): """ Make the hhea table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["hhea"] = hhea = newTable("hhea") font = self.ufo hhea.tableVersion = 0x00010000 # vertical metrics hhea.ascent = _roundInt(getAttrWithFallback(font.info, "openTypeHheaAscender")) hhea.descent = _roundInt(getAttrWithFallback(font.info, "openTypeHheaDescender")) hhea.lineGap = _roundInt(getAttrWithFallback(font.info, "openTypeHheaLineGap")) # horizontal metrics widths = [] lefts = [] rights = [] extents = [] for glyph in self.allGlyphs.values(): left = glyph.leftMargin right = glyph.rightMargin if left is None: left = 0 if right is None: right = 0 widths.append(glyph.width) lefts.append(left) rights.append(right) # robofab if hasattr(glyph, "box"): bounds = glyph.box # others else: bounds = glyph.bounds if bounds is not None: xMin, yMin, xMax, yMax = bounds else: xMin = 0 xMax = 0 extent = left + (xMax - xMin) # equation from spec for calculating xMaxExtent: Max(lsb + (xMax - xMin)) extents.append(extent) hhea.advanceWidthMax = _roundInt(max(widths)) hhea.minLeftSideBearing = _roundInt(min(lefts)) hhea.minRightSideBearing = _roundInt(min(rights)) hhea.xMaxExtent = _roundInt(max(extents)) # misc hhea.caretSlopeRise = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRise") hhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRun") hhea.caretOffset = _roundInt(getAttrWithFallback(font.info, "openTypeHheaCaretOffset")) hhea.reserved0 = 0 hhea.reserved1 = 0 hhea.reserved2 = 0 hhea.reserved3 = 0 hhea.metricDataFormat = 0 # glyph count hhea.numberOfHMetrics = len(self.allGlyphs)
def setupTable_hhea(self): """ Make the hhea table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["hhea"] = hhea = newTable("hhea") font = self.ufo hhea.tableVersion = 1.0 # vertical metrics hhea.ascent = _roundInt(getAttrWithFallback(font.info, "openTypeHheaAscender")) hhea.descent = _roundInt(getAttrWithFallback(font.info, "openTypeHheaDescender")) hhea.lineGap = _roundInt(getAttrWithFallback(font.info, "openTypeHheaLineGap")) # horizontal metrics widths = [] lefts = [] rights = [] extents = [] for glyph in self.allGlyphs.values(): left = glyph.leftMargin right = glyph.rightMargin if left is None: left = 0 if right is None: right = 0 widths.append(glyph.width) lefts.append(left) rights.append(right) # robofab if hasattr(glyph, "box"): bounds = glyph.box # others else: bounds = glyph.bounds if bounds is not None: xMin, yMin, xMax, yMax = bounds else: xMin = 0 xMax = 0 extent = left + (xMax - xMin) # equation from spec for calculating xMaxExtent: Max(lsb + (xMax - xMin)) extents.append(extent) hhea.advanceWidthMax = _roundInt(max(widths)) hhea.minLeftSideBearing = _roundInt(min(lefts)) hhea.minRightSideBearing = _roundInt(min(rights)) hhea.xMaxExtent = _roundInt(max(extents)) # misc hhea.caretSlopeRise = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRise") hhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRun") hhea.caretOffset = _roundInt(getAttrWithFallback(font.info, "openTypeHheaCaretOffset")) hhea.reserved0 = 0 hhea.reserved1 = 0 hhea.reserved2 = 0 hhea.reserved3 = 0 hhea.metricDataFormat = 0 # glyph count hhea.numberOfHMetrics = len(self.allGlyphs)
def __init__(self, font, glyphOrder=None, convertCubics=True, cubicConversionError=None): super(OutlineTTFCompiler, self).__init__(font, glyphOrder) if convertCubics: unitsPerEm = getAttrWithFallback(font.info, "unitsPerEm") if cubicConversionError is None: from cu2qu.ufo import DEFAULT_MAX_ERR cubicConversionError = DEFAULT_MAX_ERR cubicConversionError *= unitsPerEm self.convertCubics = convertCubics self.cubicConversionError = cubicConversionError
def __init__( self, ufos, inplace=False, flattenComponents=False, conversionError=None, reverseDirection=True, rememberCurveType=True, layerNames=None, skipExportGlyphs=None, filters=None, ): from cu2qu.ufo import DEFAULT_MAX_ERR self.ufos = ufos self.inplace = inplace self.flattenComponents = flattenComponents if layerNames is None: layerNames = [None] * len(ufos) assert len(ufos) == len(layerNames) self.layerNames = layerNames self.glyphSets = [ _GlyphSet.from_layer( ufo, layerName, copy=not inplace, skipExportGlyphs=skipExportGlyphs ) for ufo, layerName in zip(ufos, layerNames) ] self._conversionErrors = [ (conversionError or DEFAULT_MAX_ERR) * getAttrWithFallback(ufo.info, "unitsPerEm") for ufo in ufos ] self._reverseDirection = reverseDirection self._rememberCurveType = rememberCurveType self.preFilters, self.postFilters = [], [] if filters is None: for ufo in ufos: pre, post = loadFilters(ufo) self.preFilters.append(pre) self.postFilters.append(post) else: pre = [f for f in filters if f.pre] post = [f for f in filters if not f.pre] for _ in ufos: self.preFilters.append(pre) self.postFilters.append(post)
def setupTable_vhea(self): """ Make the vhea table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["vhea"] = vhea = newTable("vhea") font = self.ufo head = self.otf["head"] # vhea.tableVersion = 0x00011000 # see https://github.com/behdad/fonttools/issues/540 vhea.tableVersion = 1.0625 # horizontal metrics vhea.ascent = _roundInt( getAttrWithFallback(font.info, "openTypeVheaVertTypoAscender")) vhea.descent = _roundInt( getAttrWithFallback(font.info, "openTypeVheaVertTypoDescender")) vhea.lineGap = _roundInt( getAttrWithFallback(font.info, "openTypeVheaVertTypoLineGap")) # vertical metrics heights = [] tops = [] bottoms = [] for glyph in self.allGlyphs.values(): top = glyph.topMargin bottom = glyph.rightMargin if top is None: top = 0 if bottom is None: bottom = 0 heights.append(glyph.height) tops.append(top) bottoms.append(bottom) vhea.advanceHeightMax = _roundInt(max(heights)) vhea.minTopSideBearing = _roundInt(max(tops)) vhea.minBottomSideBearing = _roundInt(max(bottoms)) vhea.yMaxExtent = _roundInt(vhea.minTopSideBearing - (head.yMax - head.yMin)) # misc vhea.caretSlopeRise = getAttrWithFallback( font.info, "openTypeVheaCaretSlopeRise") vhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRun") vhea.caretOffset = getAttrWithFallback(font.info, "openTypeVheaCaretOffset") vhea.reserved0 = 0 vhea.reserved1 = 0 vhea.reserved2 = 0 vhea.reserved3 = 0 vhea.reserved4 = 0 vhea.metricDataFormat = 0 # glyph count vhea.numberOfVMetrics = len(self.allGlyphs)
def setupTable_hhea(self): """ Make the hhea table. This assumes that the hmtx table was made first. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["hhea"] = hhea = newTable("hhea") hmtx = self.otf["hmtx"] font = self.ufo hhea.tableVersion = 0x00010000 # vertical metrics hhea.ascent = round( getAttrWithFallback(font.info, "openTypeHheaAscender")) hhea.descent = round( getAttrWithFallback(font.info, "openTypeHheaDescender")) hhea.lineGap = round( getAttrWithFallback(font.info, "openTypeHheaLineGap")) # horizontal metrics widths = [] lefts = [] rights = [] extents = [] for glyphName in self.allGlyphs: width, left = hmtx[glyphName] widths.append(width) bounds = self.glyphBoundingBoxes[glyphName] if bounds is None: continue right = width - left - (bounds.xMax - bounds.xMin) lefts.append(left) rights.append(right) # equation from the hhea spec for calculating xMaxExtent: # Max(lsb + (xMax - xMin)) extent = left + (bounds.xMax - bounds.xMin) extents.append(extent) hhea.advanceWidthMax = max(widths) hhea.minLeftSideBearing = min(lefts) hhea.minRightSideBearing = min(rights) hhea.xMaxExtent = max(extents) # misc hhea.caretSlopeRise = getAttrWithFallback( font.info, "openTypeHheaCaretSlopeRise") hhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeHheaCaretSlopeRun") hhea.caretOffset = round( getAttrWithFallback(font.info, "openTypeHheaCaretOffset")) hhea.reserved0 = 0 hhea.reserved1 = 0 hhea.reserved2 = 0 hhea.reserved3 = 0 hhea.metricDataFormat = 0 # glyph count hhea.numberOfHMetrics = len(self.allGlyphs)
def setupTable_vhea(self): """ Make the vhea table. This assumes that the head and vmtx tables were made first. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["vhea"] = vhea = newTable("vhea") font = self.ufo head = self.otf["head"] vmtx = self.otf["vmtx"] vhea.tableVersion = 0x00011000 # horizontal metrics vhea.ascent = round( getAttrWithFallback(font.info, "openTypeVheaVertTypoAscender")) vhea.descent = round( getAttrWithFallback(font.info, "openTypeVheaVertTypoDescender")) vhea.lineGap = round( getAttrWithFallback(font.info, "openTypeVheaVertTypoLineGap")) # vertical metrics heights = [] tops = [] bottoms = [] for glyphName in self.allGlyphs: height, top = vmtx[glyphName] heights.append(height) bounds = self.glyphBoundingBoxes[glyphName] if bounds is None: continue bottom = height - top - (bounds.yMax - bounds.yMin) tops.append(top) bottoms.append(bottom) vhea.advanceHeightMax = max(heights) vhea.minTopSideBearing = max(tops) vhea.minBottomSideBearing = max(bottoms) vhea.yMaxExtent = vhea.minTopSideBearing - (head.yMax - head.yMin) # misc vhea.caretSlopeRise = getAttrWithFallback( font.info, "openTypeVheaCaretSlopeRise") vhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRun") vhea.caretOffset = getAttrWithFallback(font.info, "openTypeVheaCaretOffset") vhea.reserved0 = 0 vhea.reserved1 = 0 vhea.reserved2 = 0 vhea.reserved3 = 0 vhea.reserved4 = 0 vhea.metricDataFormat = 0 # glyph count vhea.numberOfVMetrics = len(self.allGlyphs)
def getCharStringForGlyph(self, glyph, private, globalSubrs): """ Get a Type2CharString for the *glyph* **This should not be called externally.** Subclasses may override this method to handle the charstring creation in a different way if desired. """ width = glyph.width # subtract the nominal width postscriptNominalWidthX = getAttrWithFallback(self.ufo.info, "postscriptNominalWidthX") if postscriptNominalWidthX: width = width - postscriptNominalWidthX # round width = _roundInt(width) pen = T2CharStringPen(width, self.allGlyphs) glyph.draw(pen) charString = pen.getCharString(private, globalSubrs) return charString
def test_with_blue_zones(self, info): info.postscriptBlueValues = [ -13, 0, 470, 483, 534, 547, 556, 569, 654, 667, 677, 690, 738, 758, ] info.postscriptOtherBlues = [-255, -245] postscriptBlueScale = getAttrWithFallback(info, "postscriptBlueScale") assert postscriptBlueScale == 0.0375
def setupTable_vhea(self): """ Make the vhea table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["vhea"] = vhea = newTable("vhea") font = self.ufo head = self.otf["head"] # vhea.tableVersion = 0x00011000 # see https://github.com/behdad/fonttools/issues/540 vhea.tableVersion = 1.0625 # horizontal metrics vhea.ascent = _roundInt(getAttrWithFallback(font.info, "openTypeVheaVertTypoAscender")) vhea.descent = _roundInt(getAttrWithFallback(font.info, "openTypeVheaVertTypoDescender")) vhea.lineGap = _roundInt(getAttrWithFallback(font.info, "openTypeVheaVertTypoLineGap")) # vertical metrics heights = [] tops = [] bottoms = [] for glyph in self.allGlyphs.values(): top = glyph.topMargin bottom = glyph.rightMargin if top is None: top = 0 if bottom is None: bottom = 0 heights.append(glyph.height) tops.append(top) bottoms.append(bottom) vhea.advanceHeightMax = _roundInt(max(heights)) vhea.minTopSideBearing = _roundInt(max(tops)) vhea.minBottomSideBearing = _roundInt(max(bottoms)) vhea.yMaxExtent = _roundInt(vhea.minTopSideBearing - (head.yMax - head.yMin)) # misc vhea.caretSlopeRise = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRise") vhea.caretSlopeRun = getAttrWithFallback(font.info, "openTypeVheaCaretSlopeRun") vhea.caretOffset = getAttrWithFallback(font.info, "openTypeVheaCaretOffset") vhea.reserved0 = 0 vhea.reserved1 = 0 vhea.reserved2 = 0 vhea.reserved3 = 0 vhea.reserved4 = 0 vhea.metricDataFormat = 0 # glyph count vhea.numberOfVMetrics = len(self.allGlyphs)
def test_vertical_metrics(self): info = TestInfoObject() self.assertEqual( getAttrWithFallback(info, "openTypeHheaAscender"), 950) self.assertEqual( getAttrWithFallback(info, "openTypeHheaDescender"), -250) self.assertEqual( getAttrWithFallback(info, "openTypeOS2TypoAscender"), 950) self.assertEqual( getAttrWithFallback(info, "openTypeOS2TypoDescender"), -250) self.assertEqual( getAttrWithFallback(info, "openTypeOS2WinAscent"), 950) self.assertEqual( getAttrWithFallback(info, "openTypeOS2WinDescent"), 250)
def test_vertical_metrics(self): info = TestInfoObject() self.assertEqual(getAttrWithFallback(info, "openTypeHheaAscender"), 950) self.assertEqual(getAttrWithFallback(info, "openTypeHheaDescender"), -250) self.assertEqual(getAttrWithFallback(info, "openTypeOS2TypoAscender"), 650) self.assertEqual(getAttrWithFallback(info, "openTypeOS2TypoDescender"), -250) self.assertEqual(getAttrWithFallback(info, "openTypeOS2WinAscent"), 950) self.assertEqual(getAttrWithFallback(info, "openTypeOS2WinDescent"), 250)
def test_family_and_style_names(self): info = TestInfoObject() self.assertEqual(getAttrWithFallback(info, "familyName"), "Family Name") self.assertEqual(getAttrWithFallback(info, "styleName"), "Style Name") self.assertEqual(getAttrWithFallback(info, "styleMapFamilyName"), "Family Name Style Name") info.styleMapFamilyName = "Style Map Family Name" self.assertEqual(getAttrWithFallback(info, "styleMapFamilyName"), "Style Map Family Name") self.assertEqual( getAttrWithFallback(info, "openTypeNamePreferredFamilyName"), "Family Name") self.assertEqual( getAttrWithFallback(info, "openTypeNamePreferredSubfamilyName"), "Style Name") self.assertEqual( getAttrWithFallback(info, "openTypeNameCompatibleFullName"), "Style Map Family Name")
def test_family_and_style_names(self): info = TestInfoObject() self.assertEqual(getAttrWithFallback(info, "familyName"), "Family Name") self.assertEqual(getAttrWithFallback(info, "styleName"), "Style Name") self.assertEqual( getAttrWithFallback(info, "styleMapFamilyName"), "Family Name Style Name") info.styleMapFamilyName = "Style Map Family Name" self.assertEqual( getAttrWithFallback(info, "styleMapFamilyName"), "Style Map Family Name") self.assertEqual( getAttrWithFallback(info, "openTypeNamePreferredFamilyName"), "Family Name") self.assertEqual( getAttrWithFallback(info, "openTypeNamePreferredSubfamilyName"), "Style Name") self.assertEqual( getAttrWithFallback(info, "openTypeNameCompatibleFullName"), "Style Map Family Name")
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { "0": getAttrWithFallback(font.info, "copyright"), "1": familyName, "2": styleName, "3": getAttrWithFallback(font.info, "openTypeNameUniqueID"), "4": fullName, "5": getAttrWithFallback(font.info, "openTypeNameVersion"), "6": getAttrWithFallback(font.info, "postscriptFontName"), "7": getAttrWithFallback(font.info, "trademark"), "8": getAttrWithFallback(font.info, "openTypeNameManufacturer"), "9": getAttrWithFallback(font.info, "openTypeNameDesigner"), "10": getAttrWithFallback(font.info, "openTypeNameDescription"), "11": getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), "12": getAttrWithFallback(font.info, "openTypeNameDesignerURL"), "13": getAttrWithFallback(font.info, "openTypeNameLicense"), "14": getAttrWithFallback(font.info, "openTypeNameLicenseURL") } # don't add typographic names if they are the same as the legacy ones typographicFamilyName = getAttrWithFallback( font.info, "openTypeNamePreferredFamilyName") typographicSubfamilyName = getAttrWithFallback( font.info, "openTypeNamePreferredSubfamilyName") if nameVals["1"] != typographicFamilyName: nameVals["16"] = typographicFamilyName if nameVals["2"] != typographicSubfamilyName: nameVals["17"] = typographicSubfamilyName self.otf["name"] = name = newTable("name") name.names = [] for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameIdVal = int(nameId) if nameIdVal == 6: # postscript font name nameVal = normalizeStringForPostscript(nameVal) rec = NameRecord() rec.platformID = 3 rec.platEncID = 10 if _isNonBMP(nameVal) else 1 rec.langID = 0x409 rec.nameID = nameIdVal rec.string = nameVal.encode(rec.getEncoding()) name.names.append(rec)
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { "0": getAttrWithFallback(font.info, "copyright"), "1": familyName, "2": styleName, "3": getAttrWithFallback(font.info, "openTypeNameUniqueID"), "4": fullName, "5": getAttrWithFallback(font.info, "openTypeNameVersion"), "6": getAttrWithFallback(font.info, "postscriptFontName"), "7": getAttrWithFallback(font.info, "trademark"), "8": getAttrWithFallback(font.info, "openTypeNameManufacturer"), "9": getAttrWithFallback(font.info, "openTypeNameDesigner"), "11": getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), "12": getAttrWithFallback(font.info, "openTypeNameDesignerURL"), "13": getAttrWithFallback(font.info, "openTypeNameLicense"), "14": getAttrWithFallback(font.info, "openTypeNameLicenseURL")} # don't add typographic names if they are the same as the legacy ones typographicFamilyName = getAttrWithFallback(font.info, "openTypeNamePreferredFamilyName") typographicSubfamilyName = getAttrWithFallback(font.info, "openTypeNamePreferredSubfamilyName") if nameVals["1"] != typographicFamilyName: nameVals["16"] = typographicFamilyName if nameVals["2"] != typographicSubfamilyName: nameVals["17"] = typographicSubfamilyName self.otf["name"] = name = newTable("name") name.names = [] for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameIdVal = int(nameId) if nameIdVal == 6: # postscript font name nameVal = normalizeStringForPostscript(nameVal) rec = NameRecord() rec.platformID = 3 rec.platEncID = 10 if _isNonBMP(nameVal) else 1 rec.langID = 0x409 rec.nameID = nameIdVal rec.string = nameVal.encode(rec.getEncoding()) name.names.append(rec)
def test_without_blue_zones(self): info = TestInfoObject() postscriptBlueScale = getAttrWithFallback(info, "postscriptBlueScale") self.assertEqual(postscriptBlueScale, 0.039625)
def setupTable_OS2(self): """ Make the OS/2 table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["OS/2"] = os2 = newTable("OS/2") font = self.ufo os2.version = 0x0004 # average glyph width widths = [glyph.width for glyph in self.allGlyphs.values() if glyph.width > 0] os2.xAvgCharWidth = _roundInt(sum(widths) / len(widths)) # weight and width classes os2.usWeightClass = getAttrWithFallback(font.info, "openTypeOS2WeightClass") os2.usWidthClass = getAttrWithFallback(font.info, "openTypeOS2WidthClass") # embedding os2.fsType = intListToNum(getAttrWithFallback(font.info, "openTypeOS2Type"), 0, 16) # subscript v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXSize") if v is None: v = 0 os2.ySubscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYSize") if v is None: v = 0 os2.ySubscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXOffset") if v is None: v = 0 os2.ySubscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYOffset") if v is None: v = 0 os2.ySubscriptYOffset = _roundInt(v) # superscript v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXSize") if v is None: v = 0 os2.ySuperscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYSize") if v is None: v = 0 os2.ySuperscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXOffset") if v is None: v = 0 os2.ySuperscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYOffset") if v is None: v = 0 os2.ySuperscriptYOffset = _roundInt(v) # strikeout v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutSize") if v is None: v = 0 os2.yStrikeoutSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutPosition") if v is None: v = 0 os2.yStrikeoutPosition = _roundInt(v) # family class ibmFontClass, ibmFontSubclass = getAttrWithFallback( font.info, "openTypeOS2FamilyClass") os2.sFamilyClass = (ibmFontClass << 8) + ibmFontSubclass # panose data = getAttrWithFallback(font.info, "openTypeOS2Panose") panose = Panose() panose.bFamilyType = data[0] panose.bSerifStyle = data[1] panose.bWeight = data[2] panose.bProportion = data[3] panose.bContrast = data[4] panose.bStrokeVariation = data[5] panose.bArmStyle = data[6] panose.bLetterForm = data[7] panose.bMidline = data[8] panose.bXHeight = data[9] os2.panose = panose # Unicode ranges uniRanges = getAttrWithFallback(font.info, "openTypeOS2UnicodeRanges") os2.ulUnicodeRange1 = intListToNum(uniRanges, 0, 32) os2.ulUnicodeRange2 = intListToNum(uniRanges, 32, 32) os2.ulUnicodeRange3 = intListToNum(uniRanges, 64, 32) os2.ulUnicodeRange4 = intListToNum(uniRanges, 96, 32) # codepage ranges codepageRanges = getAttrWithFallback(font.info, "openTypeOS2CodePageRanges") os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32) os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32) # vendor id os2.achVendID = tounicode( getAttrWithFallback(font.info, "openTypeOS2VendorID"), encoding="ascii", errors="ignore") # vertical metrics os2.sxHeight = _roundInt(getAttrWithFallback(font.info, "xHeight")) os2.sCapHeight = _roundInt(getAttrWithFallback(font.info, "capHeight")) os2.sTypoAscender = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoAscender")) os2.sTypoDescender = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoDescender")) os2.sTypoLineGap = _roundInt(getAttrWithFallback(font.info, "openTypeOS2TypoLineGap")) os2.usWinAscent = _roundInt(getAttrWithFallback(font.info, "openTypeOS2WinAscent")) os2.usWinDescent = _roundInt(getAttrWithFallback(font.info, "openTypeOS2WinDescent")) # style mapping selection = list(getAttrWithFallback(font.info, "openTypeOS2Selection")) styleMapStyleName = getAttrWithFallback(font.info, "styleMapStyleName") if styleMapStyleName == "regular": selection.append(6) elif styleMapStyleName == "bold": selection.append(5) elif styleMapStyleName == "italic": selection.append(0) elif styleMapStyleName == "bold italic": selection += [0, 5] os2.fsSelection = intListToNum(selection, 0, 16) # characetr indexes unicodes = [i for i in self.unicodeToGlyphNameMapping.keys() if i is not None] if unicodes: minIndex = min(unicodes) maxIndex = max(unicodes) else: # the font may have *no* unicode values # (it really happens!) so there needs # to be a fallback. use space for this. minIndex = 0x0020 maxIndex = 0x0020 if maxIndex > 0xFFFF: # the spec says that 0xFFFF should be used # as the max if the max exceeds 0xFFFF maxIndex = 0xFFFF os2.fsFirstCharIndex = minIndex os2.fsLastCharIndex = maxIndex os2.usBreakChar = 32 os2.usDefaultChar = 0 # maximum contextual lookup length os2.usMaxContex = 0
def setupTable_name(self): """ Make the name table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ font = self.ufo self.otf["name"] = name = newTable("name") name.names = [] # Set name records from font.info.openTypeNameRecords for nameRecord in getAttrWithFallback( font.info, "openTypeNameRecords"): nameId = nameRecord["nameID"] platformId = nameRecord["platformID"] platEncId = nameRecord["encodingID"] langId = nameRecord["languageID"] # on Python 2, plistLib (used by ufoLib) returns unicode strings # only when plist data contain non-ascii characters, and returns # ascii-encoded bytes when it can. On the other hand, fontTools's # name table `setName` method wants unicode strings, so we must # decode them first nameVal = tounicode(nameRecord["string"], encoding='ascii') name.setName(nameVal, nameId, platformId, platEncId, langId) # Build name records familyName = getAttrWithFallback(font.info, "styleMapFamilyName") styleName = getAttrWithFallback(font.info, "styleMapStyleName").title() # If name ID 2 is "Regular", it can be omitted from name ID 4 fullName = familyName if styleName != "Regular": fullName += " %s" % styleName nameVals = { 0: getAttrWithFallback(font.info, "copyright"), 1: familyName, 2: styleName, 3: getAttrWithFallback(font.info, "openTypeNameUniqueID"), 4: fullName, 5: getAttrWithFallback(font.info, "openTypeNameVersion"), 6: getAttrWithFallback(font.info, "postscriptFontName"), 7: getAttrWithFallback(font.info, "trademark"), 8: getAttrWithFallback(font.info, "openTypeNameManufacturer"), 9: getAttrWithFallback(font.info, "openTypeNameDesigner"), 10: getAttrWithFallback(font.info, "openTypeNameDescription"), 11: getAttrWithFallback(font.info, "openTypeNameManufacturerURL"), 12: getAttrWithFallback(font.info, "openTypeNameDesignerURL"), 13: getAttrWithFallback(font.info, "openTypeNameLicense"), 14: getAttrWithFallback(font.info, "openTypeNameLicenseURL"), 16: getAttrWithFallback( font.info, "openTypeNamePreferredFamilyName"), 17: getAttrWithFallback( font.info, "openTypeNamePreferredSubfamilyName"), } # don't add typographic names if they are the same as the legacy ones if nameVals[1] == nameVals[16]: del nameVals[16] if nameVals[2] == nameVals[17]: del nameVals[17] # postscript font name if nameVals[6]: nameVals[6] = normalizeStringForPostscript(nameVals[6]) for nameId in sorted(nameVals.keys()): nameVal = nameVals[nameId] if not nameVal: continue nameVal = tounicode(nameVal, encoding='ascii') platformId = 3 platEncId = 10 if _isNonBMP(nameVal) else 1 langId = 0x409 # Set built name record if not set yet if name.getName(nameId, platformId, platEncId, langId): continue name.setName(nameVal, nameId, platformId, platEncId, langId)
def setupTable_CFF(self): """Make the CFF table.""" self.otf["CFF "] = cff = newTable("CFF ") cff = cff.cff # set up the basics cff.major = 1 cff.minor = 0 cff.hdrSize = 4 cff.offSize = 4 cff.fontNames = [] strings = IndexedStrings() cff.strings = strings private = PrivateDict(strings=strings) private.rawDict.update(private.defaults) globalSubrs = GlobalSubrsIndex(private=private) topDict = TopDict(GlobalSubrs=globalSubrs, strings=strings) topDict.Private = private charStrings = topDict.CharStrings = CharStrings( file=None, charset=None, globalSubrs=globalSubrs, private=private, fdSelect=None, fdArray=None) charStrings.charStringsAreIndexed = True topDict.charset = [] charStringsIndex = charStrings.charStringsIndex = SubrsIndex( private=private, globalSubrs=globalSubrs) cff.topDictIndex = topDictIndex = TopDictIndex() topDictIndex.append(topDict) topDictIndex.strings = strings cff.GlobalSubrs = globalSubrs # populate naming data info = self.ufo.info psName = getAttrWithFallback(info, "postscriptFontName") cff.fontNames.append(psName) topDict = cff.topDictIndex[0] topDict.version = "%d.%d" % (getAttrWithFallback( info, "versionMajor"), getAttrWithFallback(info, "versionMinor")) trademark = getAttrWithFallback(info, "trademark") if trademark: trademark = normalizeStringForPostscript( trademark.replace("\u00A9", "Copyright")) if trademark != self.ufo.info.trademark: self.log.append( "[Warning] The trademark was normalized for storage in the CFF table and consequently some characters were dropped: '%s'" % trademark) if trademark is None: trademark = "" topDict.Notice = trademark copyright = getAttrWithFallback(info, "copyright") if copyright: copyright = normalizeStringForPostscript( copyright.replace("\u00A9", "Copyright")) if copyright != self.ufo.info.copyright: self.log.append( "[Warning] The copyright was normalized for storage in the CFF table and consequently some characters were dropped: '%s'" % copyright) if copyright is None: copyright = "" topDict.Copyright = copyright topDict.FullName = getAttrWithFallback(info, "postscriptFullName") topDict.FamilyName = getAttrWithFallback( info, "openTypeNamePreferredFamilyName") topDict.Weight = getAttrWithFallback(info, "postscriptWeightName") topDict.FontName = psName # populate various numbers topDict.isFixedPitch = getAttrWithFallback(info, "postscriptIsFixedPitch") topDict.ItalicAngle = getAttrWithFallback(info, "italicAngle") underlinePosition = getAttrWithFallback(info, "postscriptUnderlinePosition") topDict.UnderlinePosition = _roundInt(underlinePosition) underlineThickness = getAttrWithFallback( info, "postscriptUnderlineThickness") topDict.UnderlineThickness = _roundInt(underlineThickness) # populate font matrix unitsPerEm = _roundInt(getAttrWithFallback(info, "unitsPerEm")) topDict.FontMatrix = [1.0 / unitsPerEm, 0, 0, 1.0 / unitsPerEm, 0, 0] # populate the width values defaultWidthX = _roundInt( getAttrWithFallback(info, "postscriptDefaultWidthX")) if defaultWidthX: private.rawDict["defaultWidthX"] = defaultWidthX nominalWidthX = _roundInt( getAttrWithFallback(info, "postscriptNominalWidthX")) if nominalWidthX: private.rawDict["nominalWidthX"] = nominalWidthX # populate hint data blueFuzz = _roundInt(getAttrWithFallback(info, "postscriptBlueFuzz")) blueShift = _roundInt(getAttrWithFallback(info, "postscriptBlueShift")) blueScale = getAttrWithFallback(info, "postscriptBlueScale") forceBold = getAttrWithFallback(info, "postscriptForceBold") blueValues = getAttrWithFallback(info, "postscriptBlueValues") if isinstance(blueValues, list): blueValues = [_roundInt(i) for i in blueValues] otherBlues = getAttrWithFallback(info, "postscriptOtherBlues") if isinstance(otherBlues, list): otherBlues = [_roundInt(i) for i in otherBlues] familyBlues = getAttrWithFallback(info, "postscriptFamilyBlues") if isinstance(familyBlues, list): familyBlues = [_roundInt(i) for i in familyBlues] familyOtherBlues = getAttrWithFallback(info, "postscriptFamilyOtherBlues") if isinstance(familyOtherBlues, list): familyOtherBlues = [_roundInt(i) for i in familyOtherBlues] stemSnapH = getAttrWithFallback(info, "postscriptStemSnapH") if isinstance(stemSnapH, list): stemSnapH = [_roundInt(i) for i in stemSnapH] stemSnapV = getAttrWithFallback(info, "postscriptStemSnapV") if isinstance(stemSnapV, list): stemSnapV = [_roundInt(i) for i in stemSnapV] # only write the blues data if some blues are defined. if (blueValues or otherBlues): private.rawDict["BlueFuzz"] = blueFuzz private.rawDict["BlueShift"] = blueShift private.rawDict["BlueScale"] = blueScale private.rawDict["ForceBold"] = forceBold private.rawDict["BlueValues"] = blueValues private.rawDict["OtherBlues"] = otherBlues private.rawDict["FamilyBlues"] = familyBlues private.rawDict["FamilyOtherBlues"] = familyOtherBlues # only write the stems if both are defined. if (stemSnapH and stemSnapV): private.rawDict["StemSnapH"] = stemSnapH private.rawDict["StdHW"] = stemSnapH[0] private.rawDict["StemSnapV"] = stemSnapV private.rawDict["StdVW"] = stemSnapV[0] # populate glyphs for glyphName in self.glyphOrder: glyph = self.allGlyphs[glyphName] unicodes = glyph.unicodes charString = self.getCharStringForGlyph(glyph, private, globalSubrs) # add to the font if glyphName in charStrings: # XXX a glyph already has this name. should we choke? glyphID = charStrings.charStrings[glyphName] charStringsIndex.items[glyphID] = charString else: charStringsIndex.append(charString) glyphID = len(topDict.charset) charStrings.charStrings[glyphName] = glyphID topDict.charset.append(glyphName) topDict.FontBBox = self.fontBoundingBox # write the glyph order self.otf.setGlyphOrder(self.glyphOrder)
def setupTable_CFF(self): """Make the CFF table.""" self.otf["CFF "] = cff = newTable("CFF ") cff = cff.cff # set up the basics cff.major = 1 cff.minor = 0 cff.hdrSize = 4 cff.offSize = 4 cff.fontNames = [] strings = IndexedStrings() cff.strings = strings private = PrivateDict(strings=strings) private.rawDict.update(private.defaults) globalSubrs = GlobalSubrsIndex(private=private) topDict = TopDict(GlobalSubrs=globalSubrs, strings=strings) topDict.Private = private charStrings = topDict.CharStrings = CharStrings(file=None, charset=None, globalSubrs=globalSubrs, private=private, fdSelect=None, fdArray=None) charStrings.charStringsAreIndexed = True topDict.charset = [] charStringsIndex = charStrings.charStringsIndex = SubrsIndex(private=private, globalSubrs=globalSubrs) cff.topDictIndex = topDictIndex = TopDictIndex() topDictIndex.append(topDict) topDictIndex.strings = strings cff.GlobalSubrs = globalSubrs # populate naming data info = self.ufo.info psName = getAttrWithFallback(info, "postscriptFontName") cff.fontNames.append(psName) topDict = cff.topDictIndex[0] topDict.version = "%d.%d" % (getAttrWithFallback(info, "versionMajor"), getAttrWithFallback(info, "versionMinor")) trademark = getAttrWithFallback(info, "trademark") if trademark: trademark = normalizeStringForPostscript(trademark.replace("\u00A9", "Copyright")) if trademark != self.ufo.info.trademark: self.log.append("[Warning] The trademark was normalized for storage in the CFF table and consequently some characters were dropped: '%s'" % trademark) if trademark is None: trademark = "" topDict.Notice = trademark copyright = getAttrWithFallback(info, "copyright") if copyright: copyright = normalizeStringForPostscript(copyright.replace("\u00A9", "Copyright")) if copyright != self.ufo.info.copyright: self.log.append("[Warning] The copyright was normalized for storage in the CFF table and consequently some characters were dropped: '%s'" % copyright) if copyright is None: copyright = "" topDict.Copyright = copyright topDict.FullName = getAttrWithFallback(info, "postscriptFullName") topDict.FamilyName = getAttrWithFallback(info, "openTypeNamePreferredFamilyName") topDict.Weight = getAttrWithFallback(info, "postscriptWeightName") topDict.FontName = getAttrWithFallback(info, "postscriptFontName") # populate various numbers topDict.isFixedPitch = getAttrWithFallback(info, "postscriptIsFixedPitch") topDict.ItalicAngle = getAttrWithFallback(info, "italicAngle") underlinePosition = getAttrWithFallback(info, "postscriptUnderlinePosition") if underlinePosition is None: underlinePosition = 0 topDict.UnderlinePosition = _roundInt(underlinePosition) underlineThickness = getAttrWithFallback(info, "postscriptUnderlineThickness") if underlineThickness is None: underlineThickness = 0 topDict.UnderlineThickness = _roundInt(underlineThickness) # populate font matrix unitsPerEm = _roundInt(getAttrWithFallback(info, "unitsPerEm")) topDict.FontMatrix = [1.0 / unitsPerEm, 0, 0, 1.0 / unitsPerEm, 0, 0] # populate the width values defaultWidthX = _roundInt(getAttrWithFallback(info, "postscriptDefaultWidthX")) if defaultWidthX: private.rawDict["defaultWidthX"] = defaultWidthX nominalWidthX = _roundInt(getAttrWithFallback(info, "postscriptNominalWidthX")) if nominalWidthX: private.rawDict["nominalWidthX"] = nominalWidthX # populate hint data blueFuzz = _roundInt(getAttrWithFallback(info, "postscriptBlueFuzz")) blueShift = _roundInt(getAttrWithFallback(info, "postscriptBlueShift")) blueScale = getAttrWithFallback(info, "postscriptBlueScale") forceBold = getAttrWithFallback(info, "postscriptForceBold") blueValues = getAttrWithFallback(info, "postscriptBlueValues") if isinstance(blueValues, list): blueValues = [_roundInt(i) for i in blueValues] otherBlues = getAttrWithFallback(info, "postscriptOtherBlues") if isinstance(otherBlues, list): otherBlues = [_roundInt(i) for i in otherBlues] familyBlues = getAttrWithFallback(info, "postscriptFamilyBlues") if isinstance(familyBlues, list): familyBlues = [_roundInt(i) for i in familyBlues] familyOtherBlues = getAttrWithFallback(info, "postscriptFamilyOtherBlues") if isinstance(familyOtherBlues, list): familyOtherBlues = [_roundInt(i) for i in familyOtherBlues] stemSnapH = getAttrWithFallback(info, "postscriptStemSnapH") if isinstance(stemSnapH, list): stemSnapH = [_roundInt(i) for i in stemSnapH] stemSnapV = getAttrWithFallback(info, "postscriptStemSnapV") if isinstance(stemSnapV, list): stemSnapV = [_roundInt(i) for i in stemSnapV] # only write the blues data if some blues are defined. if (blueValues or otherBlues): private.rawDict["BlueFuzz"] = blueFuzz private.rawDict["BlueShift"] = blueShift private.rawDict["BlueScale"] = blueScale private.rawDict["ForceBold"] = forceBold private.rawDict["BlueValues"] = blueValues private.rawDict["OtherBlues"] = otherBlues private.rawDict["FamilyBlues"] = familyBlues private.rawDict["FamilyOtherBlues"] = familyOtherBlues # only write the stems if both are defined. if (stemSnapH and stemSnapV): private.rawDict["StemSnapH"] = stemSnapH private.rawDict["StdHW"] = stemSnapH[0] private.rawDict["StemSnapV"] = stemSnapV private.rawDict["StdVW"] = stemSnapV[0] # populate glyphs for glyphName in self.glyphOrder: glyph = self.allGlyphs[glyphName] unicodes = glyph.unicodes charString = self.getCharStringForGlyph(glyph, private, globalSubrs) # add to the font if glyphName in charStrings: # XXX a glyph already has this name. should we choke? glyphID = charStrings.charStrings[glyphName] charStringsIndex.items[glyphID] = charString else: charStringsIndex.append(charString) glyphID = len(topDict.charset) charStrings.charStrings[glyphName] = glyphID topDict.charset.append(glyphName) topDict.FontBBox = self.fontBoundingBox # write the glyph order self.otf.setGlyphOrder(self.glyphOrder)
def setupTable_OS2(self): """ Make the OS/2 table. **This should not be called externally.** Subclasses may override or supplement this method to handle the table creation in a different way if desired. """ self.otf["OS/2"] = os2 = newTable("OS/2") font = self.ufo os2.version = 0x0004 # average glyph width widths = [ glyph.width for glyph in self.allGlyphs.values() if glyph.width > 0 ] os2.xAvgCharWidth = _roundInt(sum(widths) / len(widths)) # weight and width classes os2.usWeightClass = getAttrWithFallback(font.info, "openTypeOS2WeightClass") os2.usWidthClass = getAttrWithFallback(font.info, "openTypeOS2WidthClass") # embedding os2.fsType = intListToNum( getAttrWithFallback(font.info, "openTypeOS2Type"), 0, 16) # subscript, superscript, strikeout values, taken from AFDKO: # FDK/Tools/Programs/makeotf/makeotf_lib/source/hotconv/hot.c unitsPerEm = getAttrWithFallback(font.info, "unitsPerEm") italicAngle = getAttrWithFallback(font.info, "italicAngle") xHeight = getAttrWithFallback(font.info, "xHeight") def adjustOffset(offset, angle): """Adjust Y offset based on italic angle, to get X offset.""" return offset * math.tan(math.radians(-angle)) if angle else 0 v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXSize") if v is None: v = unitsPerEm * 0.65 os2.ySubscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYSize") if v is None: v = unitsPerEm * 0.6 os2.ySubscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptYOffset") if v is None: v = unitsPerEm * 0.075 os2.ySubscriptYOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SubscriptXOffset") if v is None: v = adjustOffset(-os2.ySubscriptYOffset, italicAngle) os2.ySubscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXSize") if v is None: v = os2.ySubscriptXSize os2.ySuperscriptXSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYSize") if v is None: v = os2.ySubscriptYSize os2.ySuperscriptYSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptYOffset") if v is None: v = unitsPerEm * 0.35 os2.ySuperscriptYOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2SuperscriptXOffset") if v is None: v = adjustOffset(os2.ySuperscriptYOffset, italicAngle) os2.ySuperscriptXOffset = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutSize") if v is None: v = getAttrWithFallback(font.info, "postscriptUnderlineThickness") os2.yStrikeoutSize = _roundInt(v) v = getAttrWithFallback(font.info, "openTypeOS2StrikeoutPosition") if v is None: v = xHeight * 0.6 if xHeight else unitsPerEm * 0.22 os2.yStrikeoutPosition = _roundInt(v) # family class ibmFontClass, ibmFontSubclass = getAttrWithFallback( font.info, "openTypeOS2FamilyClass") os2.sFamilyClass = (ibmFontClass << 8) + ibmFontSubclass # panose data = getAttrWithFallback(font.info, "openTypeOS2Panose") panose = Panose() panose.bFamilyType = data[0] panose.bSerifStyle = data[1] panose.bWeight = data[2] panose.bProportion = data[3] panose.bContrast = data[4] panose.bStrokeVariation = data[5] panose.bArmStyle = data[6] panose.bLetterForm = data[7] panose.bMidline = data[8] panose.bXHeight = data[9] os2.panose = panose # Unicode ranges uniRanges = getAttrWithFallback(font.info, "openTypeOS2UnicodeRanges") os2.ulUnicodeRange1 = intListToNum(uniRanges, 0, 32) os2.ulUnicodeRange2 = intListToNum(uniRanges, 32, 32) os2.ulUnicodeRange3 = intListToNum(uniRanges, 64, 32) os2.ulUnicodeRange4 = intListToNum(uniRanges, 96, 32) # codepage ranges codepageRanges = getAttrWithFallback(font.info, "openTypeOS2CodePageRanges") os2.ulCodePageRange1 = intListToNum(codepageRanges, 0, 32) os2.ulCodePageRange2 = intListToNum(codepageRanges, 32, 32) # vendor id os2.achVendID = tounicode(getAttrWithFallback(font.info, "openTypeOS2VendorID"), encoding="ascii", errors="ignore") # vertical metrics os2.sxHeight = _roundInt(getAttrWithFallback(font.info, "xHeight")) os2.sCapHeight = _roundInt(getAttrWithFallback(font.info, "capHeight")) os2.sTypoAscender = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoAscender")) os2.sTypoDescender = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoDescender")) os2.sTypoLineGap = _roundInt( getAttrWithFallback(font.info, "openTypeOS2TypoLineGap")) os2.usWinAscent = _roundInt( getAttrWithFallback(font.info, "openTypeOS2WinAscent")) os2.usWinDescent = _roundInt( getAttrWithFallback(font.info, "openTypeOS2WinDescent")) # style mapping selection = list(getAttrWithFallback(font.info, "openTypeOS2Selection")) styleMapStyleName = getAttrWithFallback(font.info, "styleMapStyleName") if styleMapStyleName == "regular": selection.append(6) elif styleMapStyleName == "bold": selection.append(5) elif styleMapStyleName == "italic": selection.append(0) elif styleMapStyleName == "bold italic": selection += [0, 5] os2.fsSelection = intListToNum(selection, 0, 16) # characetr indexes unicodes = [ i for i in self.unicodeToGlyphNameMapping.keys() if i is not None ] if unicodes: minIndex = min(unicodes) maxIndex = max(unicodes) else: # the font may have *no* unicode values (it really happens!) so # there needs to be a fallback. use 0xFFFF, as AFDKO does: # FDK/Tools/Programs/makeotf/makeotf_lib/source/hotconv/map.c minIndex = 0xFFFF maxIndex = 0xFFFF if maxIndex > 0xFFFF: # the spec says that 0xFFFF should be used # as the max if the max exceeds 0xFFFF maxIndex = 0xFFFF os2.fsFirstCharIndex = minIndex os2.fsLastCharIndex = maxIndex os2.usBreakChar = 32 os2.usDefaultChar = 0 # maximum contextual lookup length os2.usMaxContex = 0