From e2e715417612a5b9456398f7f11be6cbe3fd46af Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Fri, 16 Jun 2006 16:59:00 +0200 Subject: [PATCH] text along path Implement a decorator curvedtext for setting text along a given path. curvedtext switches to singlecharmode, but only while it is needed. The basic idea has been discussed on the list and goes back to the PyX grand masters. Signed-off-by: Michael J Gruber --- examples/text/INDEX | 1 + examples/text/textalongpath.py | 15 +++++++++++ examples/text/textalongpath.txt | 5 ++++ pyx/deco.py | 59 +++++++++++++++++++++++++++++++++++++++++ pyx/dvi/dvifile.py | 5 ++-- pyx/text.py | 8 +++--- 6 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 examples/text/textalongpath.py create mode 100644 examples/text/textalongpath.txt diff --git a/examples/text/INDEX b/examples/text/INDEX index 04692ae2..c982f897 100644 --- a/examples/text/INDEX +++ b/examples/text/INDEX @@ -5,3 +5,4 @@ marker color texrunner textbox +textalongpath diff --git a/examples/text/textalongpath.py b/examples/text/textalongpath.py new file mode 100644 index 00000000..c410aa42 --- /dev/null +++ b/examples/text/textalongpath.py @@ -0,0 +1,15 @@ +from pyx import * + +c = canvas.canvas() + +R = 1.3 +p = path.path(path.arc(0,0, R, 0,270)) + path.line(0,-R, R,-R) +label = (r"\PyX{} is fun. " * 4)[:-1] # chop off last space + +c.draw(p, [deco.stroked([color.rgb.blue]), deco.curvedtext(label)]) +c.draw(p, [trafo.translate(2.5*R,0), deco.stroked([color.rgb.blue]), deco.curvedtext(label,textattrs=[text.halign.right],relarclenpos=1)]) +c.draw(p.reversed(), [trafo.translate(0, -2.5*R), deco.stroked([color.rgb.blue]), deco.curvedtext(label,textattrs=[text.halign.right],relarclenpos=1)]) +c.draw(p.reversed(), [trafo.translate(2.5*R, -2.5*R), deco.stroked([color.rgb.blue]), deco.curvedtext(label)]) + +c.writeEPSfile("textalongpath") +c.writePDFfile("textalongpath") diff --git a/examples/text/textalongpath.txt b/examples/text/textalongpath.txt new file mode 100644 index 00000000..17379936 --- /dev/null +++ b/examples/text/textalongpath.txt @@ -0,0 +1,5 @@ +Text along path + +! In order to set text along a given path, you can use the `curvedtext()` +decorator. The examples show how you can position the text relative to the +path. diff --git a/pyx/deco.py b/pyx/deco.py index 656845f8..a6a3359c 100644 --- a/pyx/deco.py +++ b/pyx/deco.py @@ -564,6 +564,65 @@ class text(deco, attr.attr): t.linealign(self.textdist, math.cos(angle), math.sin(angle)) dp.ornaments.insert(t) +class curvedtext(deco, attr.attr): + """a text decorator for curved text + + - text: is typeset along the path to which this decorator is applied + - relarclenpos: position for the base point of the text (default: 0) + - arlenfrombegin, arclenfromend: alternative ways of specifying the position of the base point; + use of relarclenpos, arclenfrombegin and arclenfromend is mutually exclusive + - textattrs, texrunner: standard text arguments (defaults: [] resp None) + + """ + + def __init__(self, text, textattrs=[], + relarclenpos=0, arclenfrombegin=None, arclenfromend=None, + texrunner=None): + if arclenfrombegin is not None and arclenfromend is not None: + raise ValueError("either set arclenfrombegin or arclenfromend") + self.text = text + self.textattrs = textattrs + self.relarclenpos = relarclenpos + self.arclenfrombegin = arclenfrombegin + self.arclenfromend = arclenfromend + self.texrunner = texrunner + + def decorate(self, dp, texrunner): + if self.texrunner: + texrunner = self.texrunner + import text as textmodule + + dp.ensurenormpath() + if self.arclenfrombegin is not None: + textpos = dp.path.begin() + self.arclenfrombegin + elif self.arclenfromend is not None: + textpos = dp.path.end() - self.arclenfromend + else: + # relarcpos is used if neither arcfrombegin nor arcfromend is given + textpos = self.relarclenpos * dp.path.arclen() + + c = canvas.canvas() + + singlecharmode=texrunner.singlecharmode # usually 0 + texrunner.singlecharmode=1 + t = texrunner.text(0, 0, self.text, self.textattrs) + + # copy over attr ops (colour...) + # isinstance(op, canvas._canvas) should not occur before ensuredvicanvas; should we even care to check? + [ c.insert(op) for op in t.items if not isinstance(op, canvas._canvas)] + + t.ensuredvicanvas() + texrunner.singlecharmode=singlecharmode + + items = t.dvicanvas.items + xs = [item.bbox().center()[0] for item in items] + trafos = dp.path.trafo([textpos +x for x in xs]) + for x, op, atrafo in zip(xs, items, trafos): + c.insert(op, [trafo.translate(-x, 0), atrafo]) # reversed trafos: fix for change in canvas.py from r2728 to 2730 + + dp.ornaments.insert(c) + + class shownormpath(deco, attr.attr): diff --git a/pyx/dvi/dvifile.py b/pyx/dvi/dvifile.py index b1e81237..d65e952f 100644 --- a/pyx/dvi/dvifile.py +++ b/pyx/dvi/dvifile.py @@ -112,12 +112,13 @@ class _restoretrafo(canvasitem.canvasitem): class DVIfile: - def __init__(self, filename, debug=0, debugfile=sys.stdout): + def __init__(self, filename, debug=0, debugfile=sys.stdout, singlecharmode=0): """ opens the dvi file and reads the preamble """ self.filename = filename self.debug = debug self.debugfile = debugfile self.debugstack = [] + self.singlecharmode = singlecharmode self.fonts = {} self.activefont = None @@ -197,7 +198,7 @@ class DVIfile: self.activetext[2].append(char) self.pos[_POS_H] += dx - if not advancepos: + if (not advancepos) or self.singlecharmode: self.flushtext(fontmap) def usefont(self, fontnum, id1234, fontmap): diff --git a/pyx/text.py b/pyx/text.py index 242a4b96..a7e94acb 100644 --- a/pyx/text.py +++ b/pyx/text.py @@ -791,6 +791,7 @@ class texrunner: waitfortex=config.getint("text", "waitfortex", 60), showwaitfortex=config.getint("text", "showwaitfortex", 5), texipc=config.getboolean("text", "texipc", 0), + singlecharmode=0, texdebug=None, dvidebug=0, errordebug=1, @@ -812,6 +813,7 @@ class texrunner: self.waitfortex = waitfortex self.showwaitfortex = showwaitfortex self.texipc = texipc + self.singlecharmode = singlecharmode if texdebug is not None: if texdebug[-4:] == ".tex": self.texdebug = open(texdebug, "w") @@ -1031,7 +1033,7 @@ class texrunner: self.execute(None, self.defaulttexmessagesend + self.texmessagesend) dvifilename = "%s.dvi" % self.texfilename if not self.texipc: - self.dvifile = dvifile.DVIfile(dvifilename, debug=self.dvidebug) + self.dvifile = dvifile.DVIfile(dvifilename, debug=self.dvidebug, singlecharmode=self.singlecharmode) page = 1 for box in self.needdvitextboxes: box.setdvicanvas(self.dvifile.readpage([ord("P"), ord("y"), ord("X"), page, 0, 0, 0, 0, 0, 0], fontmap=box.fontmap)) @@ -1190,7 +1192,7 @@ class texrunner: raise e if self.texipc: if first: - self.dvifile = dvifile.DVIfile("%s.dvi" % self.texfilename, debug=self.dvidebug) + self.dvifile = dvifile.DVIfile("%s.dvi" % self.texfilename, debug=self.dvidebug, singlecharmode=self.singlecharmode) match = self.PyXBoxPattern.search(self.texmessage) if not match or int(match.group("page")) != self.page: raise TexResultError("box extents not found", self) @@ -1252,7 +1254,7 @@ class texrunner: "\\vfill\\supereject%%\n" % text, [texmessage.ignore]) if self.texipc: if self.dvifile is None: - self.dvifile = dvifile.DVIfile("%s.dvi" % self.texfilename, debug=self.dvidebug) + self.dvifile = dvifile.DVIfile("%s.dvi" % self.texfilename, debug=self.dvidebug, singlecharmode=self.singlecharmode) else: raise RuntimeError("textboxes currently needs texipc") lastparnos = parnos -- 2.11.4.GIT