Example #1
0
 def glyph_bounds(self, glyph) -> Tuple[int]:
     glyph = self.glyph_name(glyph)
     ttglyphset = self.ttglyphset
     ttglyph = ttglyphset[glyph]
     bounds = BoundsPen(ttglyphset)
     ttglyph.draw(bounds)
     return bounds.bounds
Example #2
0
def getBounds(drawable: Drawable,
              layer: GlyphSet | None) -> BoundingBox | None:
    pen = BoundsPen(layer)
    # raise 'KeyError' when a referenced component is missing from glyph set
    pen.skipMissingComponents = False
    drawable.draw(pen)
    return None if pen.bounds is None else BoundingBox(*pen.bounds)
Example #3
0
def getBounds(drawable: Drawable, layer: Any) -> Optional[BoundingBox]:
    # XXX: layer should behave like a mapping of glyph names to Glyph objects, but
    # cyclic imports...
    pen = BoundsPen(layer)
    # raise 'KeyError' when a referenced component is missing from glyph set
    pen.skipMissingComponents = False
    drawable.draw(pen)
    return None if pen.bounds is None else BoundingBox(*pen.bounds)
Example #4
0
 def _get_bounds(self):
     """
     Subclasses may override this method.
     """
     from fontTools.pens.boundsPen import BoundsPen
     pen = BoundsPen(self.layer)
     self.draw(pen)
     return pen.bounds
Example #5
0
    def test_compileVariable_filters(self, designspace, compileFunc):
        filters = [TransformationsFilter(OffsetY=10)]
        varfont = compileFunc(designspace, filters=filters)

        ufo = designspace.sources[0].font
        pen1 = BoundsPen(ufo)
        glyph = ufo["a"]
        glyph.draw(pen1)

        glyphSet = varfont.getGlyphSet()
        tt_glyph = glyphSet["a"]
        pen2 = BoundsPen(glyphSet)
        tt_glyph.draw(pen2)

        assert pen1.bounds[0] == pen2.bounds[0]
        assert pen1.bounds[1] + 10 == pen2.bounds[1]
        assert pen1.bounds[2] == pen2.bounds[2]
        assert pen1.bounds[3] + 10 == pen2.bounds[3]
Example #6
0
    def test_compile_filters(self, compileFunc, FontClass):
        ufo = FontClass(getpath("LayerFont-Regular.ufo"))
        filters = [TransformationsFilter(OffsetY=10)]
        ttf = compileFunc(ufo, filters=filters)

        pen1 = BoundsPen(ufo)
        glyph = ufo["a"]
        glyph.draw(pen1)

        glyphSet = ttf.getGlyphSet()
        tt_glyph = glyphSet["a"]
        pen2 = BoundsPen(glyphSet)
        tt_glyph.draw(pen2)

        assert pen1.bounds[0] == pen2.bounds[0]
        assert pen1.bounds[1] + 10 == pen2.bounds[1]
        assert pen1.bounds[2] == pen2.bounds[2]
        assert pen1.bounds[3] + 10 == pen2.bounds[3]
Example #7
0
 def bounds(self):
     """Calculate the bounds of this shape; mostly for internal use."""
     try:
         cbp = BoundsPen(None)
         self.replay(cbp)
         mnx, mny, mxx, mxy = cbp.bounds
         return Rect((mnx, mny, mxx - mnx, mxy - mny))
     except:
         return Rect(0, 0, 0, 0)
Example #8
0
def makeSVGShape(glyph, name=None, width=None, opacity=None):
    attrs = {
        'id':
        'mathShape',
        'title':
        "None",
        'xmlns':
        "http://www.w3.org/2000/svg",
        'xmlns:xlink':
        "http://www.w3.org/1999/xlink",
        'xml:space':
        'preserve',
        'style':
        "fill-rule:nonzero;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;",
    }
    # try to get the bounds from the bounds layer.
    # if that does not work, get it from the glyph itself.
    bounds = None
    try:
        boundsGlyph = glyph.getLayer('bounds')
        if boundsGlyph is not None:
            bounds = boundsGlyph.box
    except:
        pass
    if bounds is None:
        boundsPen = BoundsPen({})
        glyph.draw(boundsPen)
        bounds = boundsPen.bounds

    xOffset = 0
    yOffset = 0
    attrs['id'] = name
    if width is None:
        attrs['width'] = "100%"
    else:
        attrs['width'] = width
    if name is not None:
        attrs['name'] = name
    else:
        attrs['name'] = glyph.name
    if opacity is not None:
        attrs['fill-opacity'] = "%3.3f" % opacity

    t = Transform()
    t = t.scale(1, -1)
    t = t.translate(0, -bounds[3])
    vb = (0, 0, glyph.width, bounds[3] - bounds[1])
    attrs['viewBox'] = "%3.3f %3.3f %3.3f %3.3f" % (vb[0], vb[1], vb[2], vb[3])
    attrs['enable-background'] = attrs['viewBox']
    sPen = MathImageSVGPathPen({}, optimise=False, lineAsCurve=True)
    tPen = TransformPen(sPen, t)
    glyph.draw(tPen)
    path = "<path d=\"%s\"/>" % (sPen.getCommands())
    tag = "<svg %s>%s</svg>" % (" ".join(
        ["%s=\"%s\"" % (k, v) for k, v in attrs.items()]), path)
    return vb, tag
Example #9
0
    def test_compileInterpolatableTTFs(self, FontClass):
        ufos = [
            FontClass(getpath("NestedComponents-Regular.ufo")),
            FontClass(getpath("NestedComponents-Bold.ufo")),
        ]
        filters = [TransformationsFilter(OffsetY=10)]
        ttfs = compileInterpolatableTTFs(ufos, filters=filters)

        for i, ttf in enumerate(ttfs):
            glyph = ufos[i]["a"]
            pen1 = BoundsPen(ufos[i])
            glyph.draw(pen1)

            glyphSet = ttf.getGlyphSet()
            tt_glyph = glyphSet["uni0061"]
            pen2 = BoundsPen(glyphSet)
            tt_glyph.draw(pen2)

            assert pen1.bounds[0] == pen2.bounds[0]
            assert pen1.bounds[1] + 10 == pen2.bounds[1]
            assert pen1.bounds[2] == pen2.bounds[2]
            assert pen1.bounds[3] + 10 == pen2.bounds[3]
Example #10
0
def com_google_fonts_check_iso15008_interline_spacing(ttFont):
    """Check if spacing between lines is adequate for display use"""
    glyphset = ttFont.getGlyphSet()
    if "h" not in glyphset or "g" not in glyphset:
        yield FAIL,\
              Message('glyph-not-present',
                      "There was no 'g'/'h' glyph in the font,"
                      " so the spacing could not be tested")
        return

    h_glyph = glyphset["h"]
    pen = BoundsPen(glyphset)
    h_glyph._glyph.draw(pen, ttFont.get("glyf"))
    (_, _, _, h_yMax) = pen.bounds

    g_glyph = glyphset["g"]
    pen = BoundsPen(glyphset)
    g_glyph._glyph.draw(pen, ttFont.get("glyf"))
    (_, g_yMin, _, _) = pen.bounds

    linegap = (
        (g_yMin - ttFont["OS/2"].sTypoDescender)
        + ttFont["OS/2"].sTypoLineGap
        + (ttFont["OS/2"].sTypoAscender - h_yMax)
    )
    width = stem_width(ttFont)
    if width is None:
        yield FAIL,\
              Message('no-stem-width',
                      "Could not determine stem width")
        return
    if linegap < width:
        yield FAIL,\
              Message('bad-interline-spacing',
                      f"The interline space {linegap} should"
                      f" be more than the stem width {width}")
        return
    yield PASS, "Amount of interline space was adequate"
def _get_cp_metrics(font, cp):
    # returns metrics for nominal glyph for cp, or None if cp not in font
    cmap = font_data.get_cmap(font)
    if cp not in cmap:
      return None
    glyphs = font.getGlyphSet()
    g = glyphs[cmap[cp]]
    pen = BoundsPen(glyphs)
    g.draw(pen)
    if not pen.bounds:
      return None
    xmin, ymin, xmax, ymax = pen.bounds
    return GMetrics(
        xmin, g.width - xmax, xmax - xmin, g.width, (ymin + ymax) / 2)
Example #12
0
def glyphBoundsRepresentationFactory(glyph):
    # base glyph
    pen = BoundsPen(glyph.getParent())
    glyph.draw(pen)
    bounds = pen.bounds
    # components
    for component in glyph.components:
        b = component.bounds
        if b is not None:
            if bounds is None:
                bounds = b
            else:
                bounds = unionRect(bounds, b)
    return bounds
Example #13
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)
Example #14
0
def com_google_fonts_check_iso15008_proportions(ttFont):
    """Check if 0.65 => (H width / H height) => 0.80"""
    glyphset = ttFont.getGlyphSet()
    if "H" not in glyphset:
        yield FAIL,\
              Message('glyph-not-present',
                      "There was no 'H' glyph in the font,"
                      " so the proportions could not be tested")

    h_glyph = glyphset["H"]
    pen = BoundsPen(glyphset)
    h_glyph._glyph.draw(pen, ttFont.get("glyf"))
    (xMin, yMin, xMax, yMax) = pen.bounds
    proportion = (xMax - xMin) / (yMax - yMin)
    if 0.65 <= proportion <= 0.80:
        yield PASS, "the letter H is not too narrow or too wide"
    else:
        yield FAIL,\
              Message('invalid-proportion',
                      f"The proportion of H width to H height ({proportion})"
                      f"does not conform to the expected range of 0.65-0.80")
Example #15
0
def com_google_fonts_check_iso15008_interword_spacing(font, ttFont):
    """Check if spacing between words is adequate for display use"""
    l_intersections = xheight_intersections(ttFont, "l")
    if len(l_intersections) < 2:
        yield FAIL,\
              Message('glyph-not-present',
                      "There was no 'l' glyph in the font,"
                      " so the spacing could not be tested")
        return

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

    glyphset = ttFont.getGlyphSet()
    h_glyph = glyphset["m"]
    pen = BoundsPen(glyphset)
    h_glyph._glyph.draw(pen, ttFont.get("glyf"))
    (xMin, yMin, xMax, yMax) = pen.bounds
    m_advance = ttFont["hmtx"]["m"][0]
    m_lsb = xMin
    m_rsb = m_advance - (m_lsb + xMax - xMin)

    n_lsb = ttFont["hmtx"]["n"][1]

    l_m = l_rsb + pair_kerning(font, "l", "m") + m_lsb
    space_width = ttFont["hmtx"]["space"][0]
    # Add spacing caused by normal sidebearings
    space_width += m_rsb + n_lsb

    if 2.50 <= space_width / l_m <= 3.0:
        yield PASS, "Advance width of interword space was adequate"
    else:
        yield FAIL,\
              Message('bad-interword-spacing',
                      f"The interword space ({space_width}) was"
                      f" outside the recommended range ({l_m*2.5}-{l_m*3.0})")
Example #16
0
 def getBounds(self, layer=None):
     pen = BoundsPen(layer)
     pen.skipMissingComponents = False
     self.draw(pen)
     return None if pen.bounds is None else BoundingBox(*pen.bounds)
Example #17
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 _get_bounds(self):
     pen = BoundsPen(None)
     self.draw(pen)
     return pen.bounds
Example #19
0
	def calcBounds(self, glyphSet):
		boundsPen = BoundsPen(glyphSet)
		self.draw(boundsPen)
		return boundsPen.bounds
Example #20
0
 def _get_box(self):
     from fontTools.pens.boundsPen import BoundsPen
     bP = BoundsPen(None)
     self.draw(bP)
     return bP.bounds
Example #21
0
 def _getGlyphBounds(self, glyphName):
     glyph = self.glyphSet[glyphName]
     pen = BoundsPen(self.glyphSet)
     glyph.draw(pen)
     return pen.bounds
def contourBoundsRepresentationFactory(obj):
    pen = BoundsPen(None)
    obj.draw(pen)
    return pen.bounds
Example #23
0
def getFontBounds(font):
    gs = font.getGlyphSet()
    pen = BoundsPen(gs)
    for g in gs.keys():
        gs[g].draw(pen)
    return pen.bounds
Example #24
0
 def _get_bounds(self):
     if self._bounds is None:
         pen = BoundsPen(None)
         self.draw(pen)
         self._bounds = pen.bounds
     return self._bounds
def componentBoundsRepresentationFactory(obj):
    pen = BoundsPen(obj.layer)
    obj.draw(pen)
    return pen.bounds
Example #26
0
 def calcBounds(self):
     boundsPen = BoundsPen(None)
     self.draw(boundsPen)
     return boundsPen.bounds
Example #27
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
Example #28
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))
Example #29
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]
Example #30
0
def getGlyphBox(glyph):
    pen = BoundsPen(glyph.layer)
    glyph.draw(pen)
    return pen.bounds