Exemple #1
0
def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
	"""Unlike TrueType glyphs, neither advance width nor bounding box
	info is stored in a CFF2 charstring. The width data exists only in
	the hmtx and HVAR tables. Since LSB data cannot be interpolated
	reliably from the master LSB values in the hmtx table, we traverse
	the charstring to determine the actual bound box. """

	charstrings = topDict.CharStrings
	boundsPen = BoundsPen(glyphOrder)
	hmtx = varfont['hmtx']
	hvar_table = None
	if 'HVAR' in varfont:
		hvar_table = varfont['HVAR'].table
		fvar = varfont['fvar']
		varStoreInstancer = VarStoreInstancer(hvar_table.VarStore, fvar.axes, loc)

	for gid, gname in enumerate(glyphOrder):
		entry = list(hmtx[gname])
		# get width delta.
		if hvar_table:
			if hvar_table.AdvWidthMap:
				width_idx = hvar_table.AdvWidthMap.mapping[gname]
			else:
				width_idx = gid
			width_delta = otRound(varStoreInstancer[width_idx])
		else:
			width_delta = 0

		# get LSB.
		boundsPen.init()
		charstring = charstrings[gname]
		charstring.draw(boundsPen)
		if boundsPen.bounds is None:
			# Happens with non-marking glyphs
			lsb_delta = 0
		else:
			lsb = boundsPen.bounds[0]
		lsb_delta = entry[1] - lsb

		if lsb_delta or width_delta:
			if width_delta:
				entry[0] += width_delta
			if lsb_delta:
				entry[1] = lsb
			hmtx[gname] = tuple(entry)
Exemple #2
0
 def _get_box(self):
     from fontTools.pens.boundsPen import BoundsPen
     bP = BoundsPen(None)
     self.draw(bP)
     return bP.bounds
Exemple #3
0
 def _getGlyphBounds(self, glyphName):
     glyph = self.glyphSet[glyphName]
     pen = BoundsPen(self.glyphSet)
     glyph.draw(pen)
     return pen.bounds
Exemple #4
0
 def calcBounds(self, glyphSet):
     boundsPen = BoundsPen(glyphSet)
     self.draw(boundsPen)
     return boundsPen.bounds
def componentBoundsRepresentationFactory(obj):
    pen = BoundsPen(obj.layer)
    obj.draw(pen)
    return pen.bounds
 def calcBounds(self):
     boundsPen = BoundsPen(None)
     self.draw(boundsPen)
     return boundsPen.bounds
Exemple #7
0
    def clientInitData(self):
        txFont = self.parentFont.clientFont
        if not hasattr(txFont, 'vmetrics'):
            try:
                txFont.vmetrics = txFont['vmtx'].metrics
            except KeyError:
                txFont.vmetrics = None
            try:
                txFont.vorg = txFont['VORG']
            except KeyError:
                txFont.vorg = None

        fTopDict = txFont['CFF '].cff.topDictIndex[0]
        self.isCID = self.parentFont.isCID
        charstring = fTopDict.CharStrings[self.name]

        # Get the list of points
        pen = FontPDFPen(None)
        drawCharString(charstring, pen)
        self.hintTable = charstring.hintTable

        self.hhints = charstring.hhints
        self.vhints = charstring.vhints
        self.numMT = pen.numMT
        self.numLT = pen.numLT
        self.numCT = pen.numCT
        self.numPaths = pen.numPaths
        self.pathList = pen.pathList
        for path in self.pathList:
            lenPath = len(path)
            path[-1].next = path[0]
            path[0].last = path[-1]
            if lenPath > 1:
                path[0].next = path[1]
                path[-1].last = path[-2]
                for i in list(range(lenPath)[1:-1]):
                    pt = path[i]
                    pt.next = path[i + 1]
                    pt.last = path[i - 1]

        assert len(self.pathList) == self.numPaths, (
            "Path lengths don't match %s %s" %
            (len(self.pathList), self.numPaths))
        # get the bbox and width.
        pen = BoundsPen(None)
        charstring.draw(pen)
        self.xAdvance = charstring.width
        self.BBox = pen.bounds
        if not self.BBox:
            self.BBox = [0, 0, 0, 0]

        self.yOrigin = self.parentFont.emSquare + self.parentFont.getBaseLine()
        if txFont.vorg:
            try:
                self.yOrigin = txFont.vorg[self.name]
            except KeyError:
                if txFont.vmetrics:
                    try:
                        mtx = txFont.vmetrics[self.name]
                        self.yOrigin = mtx[1] + self.BBox[3]
                    except KeyError:
                        pass

        haveVMTX = 0
        if txFont.vmetrics:
            try:
                mtx = txFont.vmetrics[self.name]
                self.yAdvance = mtx[0]
                self.tsb = mtx[1]
                haveVMTX = 1
            except KeyError:
                pass
        if not haveVMTX:
            self.yAdvance = self.parentFont.getEmSquare()
            self.tsb = (self.yOrigin - self.BBox[3] +
                        self.parentFont.getBaseLine())

        # Get the fdIndex, so we can later determine
        # which set of blue values to use.
        self.fdIndex = 0
        if hasattr(fTopDict, "ROS"):
            gid = fTopDict.CharStrings.charStrings[self.name]
            self.fdIndex = fTopDict.FDSelect[gid]
Exemple #8
0
def getGlyphBox(glyph):
    pen = BoundsPen(glyph.layer)
    glyph.draw(pen)
    return pen.bounds
 def test_quadraticCurve(self):
     pen = BoundsPen(None)
     pen.moveTo((0, 0))
     pen.qCurveTo((6, 6), (10, 0))
     self.assertEqual("0 0 10 3", bounds_(pen))
Exemple #10
0
 def test_curve(self):
     pen = BoundsPen(None)
     pen.moveTo((0, 0))
     pen.curveTo((20, 10), (90, 40), (0, 0))
     self.assertEqual("0 0 45 20", bounds_(pen))
Exemple #11
0
 def bounds(self):
     """Returns the bounding box of the path."""
     pen = BoundsPen(self)
     self.draw(pen)
     return pen.bounds
Exemple #12
0
def getGlyphBox(glyph):
    pen = BoundsPen(glyph.getParent())
    glyph.draw(pen)
    return pen.bounds
Exemple #13
0
 def _get_bounds(self):
     from fontTools.pens.boundsPen import BoundsPen
     pen = BoundsPen(None)
     self.draw(pen)
     return pen.bounds
Exemple #14
0
def com_google_fonts_check_iso15008_intercharacter_spacing(font, ttFont):
    """Check if spacing between characters is adequate for display use"""
    width = stem_width(ttFont)

    # Because an l can have a curly tail, we don't want the *glyph* sidebearings;
    # we want the sidebearings measured using a line at Y=x-height.
    l_intersections = xheight_intersections(ttFont, "l")
    if width is None or len(l_intersections) < 2:
        yield FAIL, Message("no-stem-width", "Could not determine stem width")
        return

    l_lsb = l_intersections[0].point.x
    l_advance = ttFont["hmtx"]["l"][0]
    l_rsb = l_advance - l_intersections[-1].point.x

    l_l = l_rsb + pair_kerning(font, "l", "l") + l_lsb
    if l_l is None:
        yield FAIL,\
              Message('glyph-not-present',
                      "There was no 'l' glyph in the font,"
                      " so the spacing could not be tested")
        return
    if 1.5 <= (l_l / width) <= 2.4:
        yield PASS, "Distance between vertical strokes was adequate"
    else:
        yield FAIL,\
              Message('bad-vertical-vertical-spacing',
                      f"The space between vertical strokes ({l_l})"
                      f" does not conform to the expected"
                      f" range of {width * 1.5}-{width * 2.4}")

    # For v, however, a simple LSB/RSB is adequate.
    glyphset = ttFont.getGlyphSet()
    h_glyph = glyphset["v"]
    pen = BoundsPen(glyphset)
    h_glyph._glyph.draw(pen, ttFont.get("glyf"))
    (xMin, yMin, xMax, yMax) = pen.bounds
    v_advance = ttFont["hmtx"]["v"][0]

    v_lsb = xMin
    v_rsb = v_advance - (v_lsb + xMax - xMin)

    l_v = l_rsb + pair_kerning(font, "l", "v") + v_lsb

    if l_v is None:
        yield FAIL,\
              Message('glyph-not-present',
                      "There was no 'v' glyph in the font,"
                      " so the spacing could not be tested")
        return

    if (l_v / width) > 0.85:
        yield PASS, "Distance between vertical and diagonal strokes was adequate"
    else:
        yield FAIL,\
              Message('bad-vertical-diagonal-spacing',
                      f"The space between vertical and diagonal strokes ({l_v})"
                      f" was less than the expected"
                      f" value of {width * 0.85}")

    v_v = v_rsb + pair_kerning(font, "v", "v") + v_lsb
    if v_v > 0:
        yield PASS, "Distance between diagonal strokes was adequate"
    else:
        yield FAIL,\
              Message('bad-diagonal-diagonal-spacing',
                      "Diagonal strokes (vv) were touching")
Exemple #15
0
def getFontBounds(font):
    gs = font.getGlyphSet()
    pen = BoundsPen(gs)
    for g in gs.keys():
        gs[g].draw(pen)
    return pen.bounds
Exemple #16
0
	def clientInitData(self):
		self.isTT = 1
		self.isCID = 0
		txFont = self.parentFont.clientFont
		glyphSet = txFont.getGlyphSet(preferCFF=1)
		clientGlyph = glyphSet[self.name]
		# Get the list of points
		pen = FontPDFPen(None)
		clientGlyph.draw(pen)

		if not hasattr(txFont, 'vmetrics'):
			try:
				txFont.vmetrics = txFont['vmtx'].metrics
			except KeyError:
				txFont.vmetrics = None
			try:
				txFont.vorg = txFont['VORG']
			except KeyError:
				txFont.vorg = None

		self.hhints = []
		self.vhints =[]
		self.numMT = pen.numMT
		self.numLT = pen.numLT
		self.numCT = pen.numCT
		self.numPaths = pen.numPaths
		self.pathList = pen.pathList
		for path in self.pathList :
			lenPath = len(path)
			path[-1].next = path[0]
			path[0].last = path[-1]
			if lenPath > 1:
				path[0].next = path[1]
				path[-1].last = path[-2]
				for i in range(lenPath)[1:-1]:
					pt = path[i]
					pt.next =  path[i+1]
					pt.last =  path[i-1]

		assert len(self.pathList) == self.numPaths, " Path lengths don't match %s %s" % (len(self.pathList) , self.numPaths)
		# get the bbox and width.
		pen = BoundsPen(None)
		clientGlyph.draw(pen)
		self.xAdvance = clientGlyph.width
		glyph_bounds = pen.bounds
		if not glyph_bounds:
			self.BBox = [0, 0, 0, 0]
		else:
			self.BBox = [round(item) for item in glyph_bounds]

		self.yOrigin = self.parentFont.emSquare + self.parentFont.getBaseLine()
		if txFont.vorg:
			try:
				self.yOrigin  = txFont.vorg[self.name]
			except KeyError:
				if txFont.vmetrics:
					try:
						mtx = txFont.vmetrics[self.name]
						self.yOrigin = mtx[1] + self.BBox[3]
					except KeyError:
						pass

		haveVMTX = 0
		if txFont.vmetrics:
			try:
				mtx = txFont.vmetrics[self.name]
				self.yAdvance = mtx[0]
				self.tsb = mtx[1]
				haveVMTX =1
			except KeyError:
				pass
		if not haveVMTX:
			self.yAdvance = self.parentFont.getEmSquare()
			self.tsb = self.yOrigin - self.BBox[3] + self.parentFont.getBaseLine()

		# Get the fdIndex, so we can laterdetermine which set of blue values to use.
		self.fdIndex = 0
		return
 def test_draw(self):
     pen = BoundsPen(None)
     draw_(pen)
     self.assertEqual("-55 0 58 100", bounds_(pen))
Exemple #18
0
def get_glyph_cleaned_extents(ttglyph, glyf_set):
    pen = BoundsPen(glyf_set, ignoreSinglePoints=True)
    ttglyph.draw(pen)
    if not pen.bounds:
        return None, None
    return pen.bounds[1], pen.bounds[3]
 def test_empty(self):
     pen = BoundsPen(None)
     self.assertEqual(None, pen.bounds)
Exemple #20
0
def gen_glyphs(ttfont, out):
    math = ttfont['MATH'].table
    cmap = ttfont['cmap'].getcmap(3, 10).cmap
    glyphs = ttfont.getGlyphSet()
    metrics = { name: {
        "usv": codepoint,
        "xmin": 0,
        "ymin": 0,
        "xmax": 0,
        "ymax": 0,
        "attachment": 0,
        "italics": 0,
        "advance": 0,
        "lsb": 0,
    } for codepoint, name in cmap.items() }

    # Gather bounding box information
    pen = BoundsPen(None)
    for glyph in cmap.values():
        bbox = (0, 0, 0, 0)
        glyphs.get(glyph).draw(pen)

        if pen.bounds is not None:
            (xmin, ymin, xmax, ymax) = pen.bounds
            bbox = (int(xmin), int(ymin), int(xmax), int(ymax))

        metrics[glyph]['xmin'] = bbox[0]
        metrics[glyph]['ymin'] = bbox[1]
        metrics[glyph]['xmax'] = bbox[2]
        metrics[glyph]['ymax'] = bbox[3]

        pen.bounds = None
        pen._start = None

    # Gather accent attachment
    accent_table = math.MathGlyphInfo.MathTopAccentAttachment
    accent_coverage = accent_table.TopAccentCoverage.glyphs
    for glyph in accent_coverage:
        value = accent_table \
            .TopAccentAttachment[accent_coverage.index(glyph)] \
            .Value
        metrics[glyph]["attachment"] = value

    # Gather italics offsets
    italics_table = math.MathGlyphInfo.MathItalicsCorrectionInfo
    italics_coverage = italics_table.Coverage.glyphs
    for glyph in italics_coverage:
        value = italics_table \
            .ItalicsCorrection[italics_coverage.index(glyph)] \
            .Value
        metrics[glyph]["italics"] = value

    # Gather advance and left side bearing
    hmtx = ttfont['hmtx'].metrics
    for glyph in cmap.values():
        (advance, lsb) = hmtx[glyph]
        metrics[glyph]["advance"] = advance
        metrics[glyph]["lsb"]     = lsb

    # Insert shim
    shim = []
    cmap = ttfont['cmap'].getcmap(3, 10).cmap
    for (new, old) in SHIM:
        if old in cmap:
            name = cmap[old]
            data = deepcopy(metrics[name])
            shim.append((new, data))
        else:
            print("Ignoring shim for", hex(old))

    template = Template(filename="tools/mako/glyphs.mako.rs")
    with open(out + "glyphs.rs", 'w') as file:
        file.write(template.render(glyphs=metrics, shim=shim))
 def test_curve(self):
     pen = BoundsPen(None)
     pen.moveTo((0, 0))
     pen.curveTo((20, 10), (90, 40), (0, 0))
     self.assertEqual("0 0 45 20", bounds_(pen))
Exemple #22
0
 def _get_bounds(self):
     if self._bounds is None:
         pen = BoundsPen(None)
         self.draw(pen)
         self._bounds = pen.bounds
     return self._bounds
 def test_quadraticCurve(self):
     pen = BoundsPen(None)
     pen.moveTo((0, 0))
     pen.qCurveTo((6, 6), (10, 0))
     self.assertEqual("0 0 10 3", bounds_(pen))
def contourBoundsRepresentationFactory(obj):
    pen = BoundsPen(None)
    obj.draw(pen)
    return pen.bounds
Exemple #25
0
def build(instance, isTTF, version):
    font = instance.parent
    source = font.masters[0]

    fea, marks = makeFeatures(instance, source)

    source.blueValues = []
    source.otherBlues = []

    for zone in sorted(source.alignmentZones):
        pos = zone.position
        size = zone.size
        vals = sorted((pos, pos + size))
        if pos == 0 or size >= 0:
            source.blueValues.extend(vals)
        else:
            source.otherBlues.extend(vals)

    fontinfo = f"""
        FontName {instance.fontName}
        OrigEmSqUnits {font.upm}
        DominantV {source.verticalStems}
        DominantH {source.horizontalStems}
        BaselineOvershoot {source.blueValues[0]}
        BaselineYCoord {source.blueValues[1]}
        LcHeight {source.blueValues[2]}
        LcOvershoot {source.blueValues[3] - source.blueValues[2]}
        CapHeight {source.blueValues[4]}
        CapOvershoot {source.blueValues[5] - source.blueValues[4]}
        AscenderHeight {source.blueValues[6]}
        AscenderOvershoot {source.blueValues[7] - source.blueValues[6]}
        Baseline5 {source.otherBlues[1]}
        Baseline5Overshoot {source.otherBlues[0] - source.otherBlues[1]}

        FlexOK true
        BlueFuzz 1
    """

    characterMap = {}
    glyphs = {}
    metrics = {}
    layerSet = {g.name: g.layers[source.id] for g in font.glyphs}

    if isTTF:
        from fontTools.pens.cu2quPen import Cu2QuPen
        from fontTools.pens.recordingPen import RecordingPen

        for glyph in font.glyphs:
            layer = glyph.layers[source.id]
            pen = RecordingPen()
            layer.draw(pen)
            layer.paths = []
            layer.components = []
            pen.replay(Cu2QuPen(layer.getPen(), 1.0, reverse_direction=True))

    for glyph in font.glyphs:
        if not glyph.export and not isTTF:
            continue
        name = glyph.name

        for code in glyph.unicodes:
            characterMap[int(code, 16)] = name

        layer = glyph.layers[source.id]
        width = 0 if name in marks else layer.width

        pen = BoundsPen(layerSet)
        layer.draw(pen)
        metrics[name] = (width, pen.bounds[0] if pen.bounds else 0)

        if isTTF:
            from fontTools.pens.ttGlyphPen import TTGlyphPen

            pen = TTGlyphPen(layerSet)
            if layer.paths:
                # Decompose and remove overlaps.
                path = Path()
                layer.draw(DecomposePathPen(path, layerSet))
                path.simplify(fix_winding=True, keep_starting_points=True)
                path.draw(pen)
            else:
                # Composite-only glyph, no need to decompose.
                layer.draw(FlattenComponentsPen(pen, layerSet))
            glyphs[name] = pen.glyph()
        else:
            from fontTools.pens.t2CharStringPen import T2CharStringPen

            # Draw glyph and remove overlaps.
            path = Path()
            layer.draw(DecomposePathPen(path, layerSet))
            path.simplify(fix_winding=True, keep_starting_points=True)

            # Build CharString.
            pen = T2CharStringPen(width, None)
            path.draw(pen)
            glyphs[name] = pen.getCharString()

    vendor = font.customParameters["vendorID"]
    names = {
        "copyright": font.copyright,
        "familyName": instance.familyName,
        "styleName": instance.name,
        "uniqueFontIdentifier": f"{version};{vendor};{instance.fontName}",
        "fullName": instance.fullName,
        "version": f"Version {version}",
        "psName": instance.fontName,
        "manufacturer": font.manufacturer,
        "designer": font.designer,
        "description": font.customParameters["description"],
        "vendorURL": font.manufacturerURL,
        "designerURL": font.designerURL,
        "licenseDescription": font.customParameters["license"],
        "licenseInfoURL": font.customParameters["licenseURL"],
        "sampleText": font.customParameters["sampleText"],
    }

    date = int(font.date.timestamp()) - epoch_diff
    fb = FontBuilder(font.upm, isTTF=isTTF)
    fb.updateHead(fontRevision=version, created=date, modified=date)
    fb.setupGlyphOrder(font.glyphOrder)
    fb.setupCharacterMap(characterMap)
    fb.setupNameTable(names, mac=False)
    fb.setupHorizontalHeader(
        ascent=source.ascender,
        descent=source.descender,
        lineGap=source.customParameters["typoLineGap"],
    )

    if isTTF:
        fb.setupGlyf(glyphs)
    else:
        privateDict = {
            "BlueValues": source.blueValues,
            "OtherBlues": source.otherBlues,
            "StemSnapH": source.horizontalStems,
            "StemSnapV": source.verticalStems,
            "StdHW": source.horizontalStems[0],
            "StdVW": source.verticalStems[0],
        }

        fontInfo = {
            "FullName": names["fullName"],
            "Notice": names["copyright"].replace("©", "\(c\)"),
            "version": f"{version}",
            "Weight": instance.name,
        }

        fb.setupCFF(names["psName"], fontInfo, glyphs, privateDict)

    fb.setupHorizontalMetrics(metrics)

    codePages = [CODEPAGE_RANGES[v] for v in font.customParameters["codePageRanges"]]
    fb.setupOS2(
        version=4,
        sTypoAscender=source.ascender,
        sTypoDescender=source.descender,
        sTypoLineGap=source.customParameters["typoLineGap"],
        usWinAscent=source.ascender,
        usWinDescent=-source.descender,
        sxHeight=source.xHeight,
        sCapHeight=source.capHeight,
        achVendID=vendor,
        fsType=calcBits(font.customParameters["fsType"], 0, 16),
        fsSelection=calcFsSelection(instance),
        ulUnicodeRange1=calcBits(font.customParameters["unicodeRanges"], 0, 32),
        ulCodePageRange1=calcBits(codePages, 0, 32),
    )

    underlineThickness = int(source.customParameters["underlineThickness"])
    underlinePosition = int(source.customParameters["underlinePosition"])
    fb.setupPost(
        keepGlyphNames=False,
        underlineThickness=underlineThickness,
        underlinePosition=underlinePosition + underlineThickness // 2,
    )

    fb.font["meta"] = meta = newTable("meta")
    meta.data = {"dlng": "Arab", "slng": "Arab"}

    fb.addOpenTypeFeatures(fea)

    if isTTF:
        from fontTools.ttLib.tables import ttProgram

        fb.setupDummyDSIG()

        fb.font["gasp"] = gasp = newTable("gasp")
        gasp.gaspRange = {0xFFFF: 15}

        fb.font["prep"] = prep = newTable("prep")
        prep.program = ttProgram.Program()
        assembly = ["PUSHW[]", "511", "SCANCTRL[]", "PUSHB[]", "4", "SCANTYPE[]"]
        prep.program.fromAssembly(assembly)
    else:
        from cffsubr import subroutinize

        subroutinize(fb.font)

    return fb.font
Exemple #26
0
 def _get_bounds(self):
     pen = BoundsPen(None)
     self.draw(pen)
     return pen.bounds
Exemple #27
0
def glyphBoundsRepresentationFactory(glyph):
    pen = BoundsPen(glyph.layer)
    glyph.draw(pen)
    return pen.bounds