Example #1
0
    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
Example #2
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
Example #4
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
Example #5
0
 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"
Example #6
0
    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')
Example #7
0
 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)
Example #8
0
    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
Example #9
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
Example #11
0
 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)
Example #12
0
    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 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
Example #14
0
    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
Example #15
0
    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
Example #16
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
Example #17
0
    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)
Example #18
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")
Example #19
0
    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
Example #20
0
    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
Example #21
0
    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)
Example #22
0
    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)
Example #23
0
 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)
Example #26
0
    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)
Example #27
0
    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)
Example #28
0
    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
Example #29
0
    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
Example #30
0
 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)
Example #32
0
    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)
Example #33
0
    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)
Example #34
0
    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")
Example #35
0
    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)
Example #37
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

        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)
Example #38
0
 def test_without_blue_zones(self):
     info = TestInfoObject()
     postscriptBlueScale = getAttrWithFallback(info, "postscriptBlueScale")
     self.assertEqual(postscriptBlueScale, 0.039625)
Example #39
0
    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
Example #40
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)
Example #42
0
    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