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 # print 'using bounds from bounds layer' except: pass # print 'using bounds from glyph' 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() # print bounds, -(bounds[3]-bounds[1]) 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({}) 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
def makeTransform(x, y, rotation, scalex, scaley, tcenterx, tcentery, scaleUsesCenter=True): rotation = math.radians(rotation) if not scaleUsesCenter: tcenterx *= scalex tcentery *= scaley t = Transform() t = t.translate(x + tcenterx, y + tcentery) t = t.rotate(rotation) t = t.translate(-tcenterx, -tcentery) t = t.scale(scalex, scaley) else: t = Transform() t = t.translate(x + tcenterx, y + tcentery) t = t.rotate(rotation) t = t.scale(scalex, scaley) t = t.translate(-tcenterx, -tcentery) return t
def makeTransform(x, y, rotation, scalex, scaley, tcenterx, tcentery): rotation = math.radians(rotation) t = Transform() t = t.translate(x + tcenterx, y + tcentery) t = t.rotate(rotation) t = t.scale(scalex, scaley) t = t.translate(-tcenterx, -tcentery) return t
def makeTransformVarCo(x, y, rotation, scalex, scaley, skewx, skewy, tcenterx, tcentery): t = Transform() t = t.translate(x + tcenterx, y + tcentery) t = t.rotate(math.radians(rotation)) t = t.scale(scalex, scaley) t = t.skew(math.radians(skewx), math.radians(skewy)) t = t.translate(-tcenterx, -tcentery) return t
def scale(self, scaleX, scaleY=None, point=None): """Scale this shape by a percentage amount (1-scale).""" t = Transform() if point != False: point = self.bounds().point("C") if point == None else point # maybe should be getFrame()? t = t.translate(point.x, point.y) t = t.scale(scaleX, scaleY or scaleX) if point != False: t = t.translate(-point.x, -point.y) return self.transform(t)
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 # print 'using bounds from bounds layer' except: pass # print 'using bounds from glyph' 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() # print bounds, -(bounds[3]-bounds[1]) 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({}) 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
def scalePenToStyle(self, glyph, in_pen): s = self.scale() t = Transform() try: bs = self.style.baselineShift[idx] except: bs = self.style.baselineShift if callable(bs): t = t.translate(0, bs(idx)) else: try: t = t.translate(0, bs[idx]) except: try: t = t.translate(0, bs) except: pass t = t.scale(s) t = t.translate(glyph.frame.x / self.scale(), glyph.frame.y / self.scale()) #t = t.translate(0, bs) out_pen = DATPen() tp = TransformPen(out_pen, (t[0], t[1], t[2], t[3], t[4], t[5])) ip = DATPen().record(in_pen) if self.style.mods and glyph.name in self.style.mods: w, mod = self.style.mods[glyph.name] mod(-1, ip) ip.replay(tp) if self.style.rotate: out_pen.rotate(self.style.rotate) # TODO this shouldn't be necessary if True: valid_values = [] for (move, pts) in out_pen.value: if move != "addComponent": valid_values.append((move, pts)) out_pen.value = valid_values return out_pen
def potrace(self, rect, poargs=[], invert=True, pen_class=None, context=None): import skia from PIL import Image from pathlib import Path from subprocess import run from fontTools.svgLib import SVGPath pc, ctx = self._get_renderer_state(pen_class, context) img = pc.Precompose(self, rect, context=ctx) pilimg = Image.fromarray( img.convert(alphaType=skia.kUnpremul_AlphaType)) binpo = Path("bin/potrace") if not binpo.exists(): binpo = Path(__file__).parent.parent.parent / "bin/potrace" with tempfile.NamedTemporaryFile(prefix="coldtype_tmp", suffix=".bmp") as tmp_bmp: pilimg.save(tmp_bmp.name) rargs = [str(binpo), "-s"] if invert: rargs.append("--invert") rargs.extend([str(x) for x in poargs]) rargs.extend(["-o", "-", "--", tmp_bmp.name]) if False: print(">>>", " ".join(rargs)) result = run(rargs, capture_output=True) t = Transform() t = t.scale(0.1, 0.1) svgp = SVGPath.fromstring(result.stdout, transform=t) dp = self.single_pen_class() svgp.draw(dp) return dp.f(0)
def parsePositions(baseGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight): xx, xy, yx, yy, x, y = 1, 0, 0, 1, advanceWidth, advanceHeight baseGlyphX = baseGlyphY = baseGlyph markFixedX = markFixedY = False flipX = flipY = False if positionSplit in markGlyph: markGlyph, position = markGlyph.split(positionSplit) if positionXYSplit in position: positions = position.split(positionXYSplit) if len(positions) == 6: xx, xy, yx, yy, positionX, positionY = positions xx = float(xx) xy = float(xy) yx = float(yx) yy = float(yy) elif len(positions) == 2: positionX, positionY = positions else: raise GlyphBuilderError( "mark positions should have 6 or 2 options") else: positionX = positionY = position if positionBaseSplit in positionX: baseGlyphX, positionX = positionX.split(positionBaseSplit) if positionBaseSplit in positionY: baseGlyphY, positionY = positionY.split(positionBaseSplit) if flipMarkGlyphSplit in positionX: flipX = True positionX = positionX.replace(flipMarkGlyphSplit, "") if flipMarkGlyphSplit in positionY: flipY = True positionY = positionY.replace(flipMarkGlyphSplit, "") if positionX and positionY: baseX = baseY = 0 markX = markY = 0 if markGlyph not in font: if glyphSuffixSplit in markGlyph: markGlyph = markGlyph.split(glyphSuffixSplit)[0] markPoint1, markAngle1, markFixedX = parsePosition(markGlyph, font, positionX, direction="x", prefix="_") markPoint2, markAngle2, markFixedY = parsePosition(markGlyph, font, positionY, direction="y", prefix="_") intersection = _intersectAngles(markPoint1, markAngle1, markPoint2, markAngle2) if intersection is not None: markX, markY = intersection if baseGlyphX in font and baseGlyphY in font: basePoint1, baseAngle1, _ = parsePosition(baseGlyphX, font, positionX, direction="x", isBase=True) basePoint2, baseAngle2, _ = parsePosition(baseGlyphY, font, positionY, direction="y", isBase=True) intersection = _intersectAngles(basePoint1, baseAngle1, basePoint2, baseAngle2) if intersection is not None: baseX, baseY = intersection # calculate the offset if not markFixedX: x += baseX - markX else: x += markX if not markFixedY: y += baseY - markY else: y += markY if not markFixedX: baseTransform = markTransformMap.get(baseGlyphX) if baseTransform: x += baseTransform[4] - advanceWidth if not markFixedY: baseTransform = markTransformMap.get(baseGlyphY) if baseTransform: y += baseTransform[5] - advanceHeight transformMatrix = (xx, xy, yx, yy, x, y) if flipX: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(0, miny) t = t.scale(1, -1) t = t.translate(0, -maxy) t = t.transform(bt) transformMatrix = t[:] if flipY: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(minx, 0) t = t.scale(-1, 1) t = t.translate(-maxx, 0) t = t.transform(bt) transformMatrix = t[:] return markGlyph, transformMatrix
def test_examples(self): t = Transform() assert repr(t) == "<Transform [1 0 0 1 0 0]>" assert t.scale(2) == Transform(2, 0, 0, 2, 0, 0) assert t.scale(2.5, 5.5) == Transform(2.5, 0, 0, 5.5, 0, 0) assert t.scale(2, 3).transformPoint((100, 100)) == (200, 300)
def test_scale(self): t = Transform() assert t.scale(5) == Transform(5, 0, 0, 5, 0, 0) assert t.scale(5, 6) == Transform(5, 0, 0, 6, 0, 0)
from fontTools.misc.transform import Transform size(200, 200) with savedState(): rotate(15, center=(50, 50)) rect(25, 25, 50, 50) with savedState(): scale(1.2, center=(150, 50)) rect(125, 25, 50, 50) with savedState(): skew(15, 10, center=(50, 150)) rect(25, 125, 50, 50) with savedState(): t = Transform() t = t.scale(0.9, 1.1) t = t.rotate(0.2) transform(t, center=(150, 150)) rect(125, 125, 50, 50)
def parsePositions(baseGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight): xx, xy, yx, yy, x, y = 1, 0, 0, 1, advanceWidth, advanceHeight baseGlyphX = baseGlyphY = baseGlyph markFixedX = markFixedY = False flipX = flipY = False if positionSplit in markGlyph: markGlyph, position = markGlyph.split(positionSplit) if positionXYSplit in position: positions = position.split(positionXYSplit) if len(positions) == 6: xx, xy, yx, yy, positionX, positionY = positions xx = float(xx) xy = float(xy) yx = float(yx) yy = float(yy) elif len(positions) == 2: positionX, positionY = positions else: raise GlyphBuilderError("mark positions should have 6 or 2 options") else: positionX = positionY = position if positionBaseSplit in positionX: baseGlyphX, positionX = positionX.split(positionBaseSplit) if positionBaseSplit in positionY: baseGlyphY, positionY = positionY.split(positionBaseSplit) if flipMarkGlyphSplit in positionX: flipY = True positionX = positionX.replace(flipMarkGlyphSplit, "") if flipMarkGlyphSplit in positionY: flipX = True positionY = positionY.replace(flipMarkGlyphSplit, "") if positionX and positionY: baseX = baseY = 0 markX = markY = 0 if markGlyph not in font: if glyphSuffixSplit in markGlyph: markGlyph = markGlyph.split(glyphSuffixSplit)[0] markPoint1, markAngle1, markFixedX = parsePosition(markGlyph, font, positionX, direction="x", prefix="_") markPoint2, markAngle2, markFixedY = parsePosition(markGlyph, font, positionY, direction="y", prefix="_") intersection = _intersectAngles(markPoint1, markAngle1, markPoint2, markAngle2) if intersection is not None: markX, markY = intersection if baseGlyphX in font and baseGlyphY in font: basePoint1, baseAngle1, _ = parsePosition(baseGlyphX, font, positionX, direction="x", isBase=True) basePoint2, baseAngle2, _ = parsePosition(baseGlyphY, font, positionY, direction="y", isBase=True) intersection = _intersectAngles(basePoint1, baseAngle1, basePoint2, baseAngle2) if intersection is not None: baseX, baseY = intersection # calculate the offset if not markFixedX: x += baseX - markX else: x += markX if not markFixedY: y += baseY - markY else: y += markY if not markFixedX: baseTransform = markTransformMap.get(baseGlyphX) if baseTransform: x += baseTransform[4] - advanceWidth if not markFixedY: baseTransform = markTransformMap.get(baseGlyphY) if baseTransform: y += baseTransform[5] - advanceHeight transformMatrix = (xx, xy, yx, yy, x, y) if flipX: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(0, miny) t = t.scale(1, -1) t = t.translate(0, -maxy) t = t.transform(bt) transformMatrix = t[:] if flipY: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(minx, 0) t = t.scale(-1, 1) t = t.translate(-maxx, 0) t = t.transform(bt) transformMatrix = t[:] return markGlyph, transformMatrix
def test_examples(self): t = Transform() assert repr(t) == "<Transform [1 0 0 1 0 0]>" assert t.scale(2) == Transform(2, 0, 0, 2, 0, 0) assert t.scale(2.5, 5.5) == Transform(2.5, 0, 0, 5.5, 0, 0) assert t.scale(2, 3).transformPoint((100, 100)) == (200, 300)
def test_scale(self): t = Transform() assert t.scale(5) == Transform(5, 0, 0, 5, 0, 0) assert t.scale(5, 6) == Transform(5, 0, 0, 6, 0, 0)