From: Michael J G. <mic...@us...> - 2011-10-07 16:14:52
|
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 <mic...@us...> --- 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 04692ae..c982f89 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 0000000..c410aa4 --- /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 0000000..1737993 --- /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 656845f..a6a3359 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 b1e8123..d65e952 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 242a4b9..a7e94ac 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 -- 1.7.7.602.g8c3f8 |
From: André W. <wo...@us...> - 2011-10-07 21:06:57
Attachments:
smime.p7s
|
Hi Michael, thank you for your patch. Overall I like it very much. I didn't try it yet, just browsed it. In general I'm very happy to accept your patch. However I have one question which I would like to address first. Couldn't be the singlecharmode an option to the text method of the texrunner. Could it be passed to be applied to the single page on the dvifile being created/processed? I didn't try but my impression is that this could be possible. Maybe I don't see the real trouble ahead when trying to do so, but it might be possible without major problems. Could you comment on that? André Am 07.10.2011 um 18:14 schrieb Michael J Gruber: > 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 <mic...@us...> > --- > 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 04692ae..c982f89 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 0000000..c410aa4 > --- /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 0000000..1737993 > --- /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 656845f..a6a3359 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 b1e8123..d65e952 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 242a4b9..a7e94ac 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 > -- > 1.7.7.602.g8c3f8 > > > ------------------------------------------------------------------------------ > All of the data generated in your IT infrastructure is seriously valuable. > Why? It contains a definitive record of application performance, security > threats, fraudulent activity, and more. Splunk takes this data and makes > sense of it. IT sense. And common sense. > http://p.sf.net/sfu/splunk-d2dcopy2 > _______________________________________________ > PyX-devel mailing list > PyX...@li... > https://lists.sourceforge.net/lists/listinfo/pyx-devel -- by _ _ _ Dr. André Wobst, Amselweg 22, 85716 Unterschleißheim / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |
From: Michael J G. <mic...@us...> - 2011-10-09 15:26:02
|
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 <mic...@us...> --- v2 has singlecharmode as a parameter to text() rather than the texrunner. The drawback is that ensuredvicanvas() and finishdvi() need that parameter also, or else the last few characters come out wrong. --- examples/text/INDEX | 1 + examples/text/textalongpath.py | 15 ++++++++++ examples/text/textalongpath.txt | 5 +++ pyx/deco.py | 56 +++++++++++++++++++++++++++++++++++++++ pyx/dvi/dvifile.py | 5 ++- pyx/text.py | 12 ++++---- 6 files changed, 86 insertions(+), 8 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 04692ae..c982f89 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 0000000..f4aad4e --- /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 0000000..1737993 --- /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 656845f..ceb6712 100644 --- a/pyx/deco.py +++ b/pyx/deco.py @@ -564,6 +564,62 @@ 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() + + t = texrunner.text(0, 0, self.text, self.textattrs, singlecharmode=1) + + # 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(singlecharmode=1) + + 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 b1e8123..d65e952 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 242a4b9..b55667d 100644 --- a/pyx/text.py +++ b/pyx/text.py @@ -700,9 +700,9 @@ class textbox(box.rect, canvas._canvas): raise RuntimeError("multiple call to setdvicanvas") self.dvicanvas = dvicanvas - def ensuredvicanvas(self): + def ensuredvicanvas(self, singlecharmode=0): if self.dvicanvas is None: - self.finishdvi() + self.finishdvi(singlecharmode=singlecharmode) assert self.dvicanvas is not None, "finishdvi is broken" if not self.insertdvicanvas: self.insert(self.dvicanvas, [self.texttrafo]) @@ -1024,14 +1024,14 @@ class texrunner: else: raise TexResultError("TeX didn't respond as expected within the timeout period (%i seconds)." % self.waitfortex, self) - def finishdvi(self, ignoretail=0): + def finishdvi(self, ignoretail=0, singlecharmode=0): """finish TeX/LaTeX and read the dvifile - this method ensures that all textboxes can access their dvicanvas""" 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=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)) @@ -1151,7 +1151,7 @@ class texrunner: PyXBoxPattern = re.compile(r"PyXBox:page=(?P<page>\d+),lt=(?P<lt>-?\d*((\d\.?)|(\.?\d))\d*)pt,rt=(?P<rt>-?\d*((\d\.?)|(\.?\d))\d*)pt,ht=(?P<ht>-?\d*((\d\.?)|(\.?\d))\d*)pt,dp=(?P<dp>-?\d*((\d\.?)|(\.?\d))\d*)pt:") - def text(self, x, y, expr, textattrs=[], texmessages=[], fontmap=None): + def text(self, x, y, expr, textattrs=[], texmessages=[], fontmap=None, singlecharmode=0): """create text by passing expr to TeX/LaTeX - returns a textbox containing the result from running expr thru TeX/LaTeX - the box center is set to x, y @@ -1190,7 +1190,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=singlecharmode) match = self.PyXBoxPattern.search(self.texmessage) if not match or int(match.group("page")) != self.page: raise TexResultError("box extents not found", self) -- 1.7.7.338.g0156b |
From: André W. <wo...@us...> - 2011-10-10 22:04:36
Attachments:
smime.p7s
|
Dear Michael, your patch still had the problem, that singlecharmode was a global setting to the dvi file which is wrong. (You could easily break your code by rendering some text without singlecharmode being active and then activating the singlecharmode on the *same* texrunner instance.) Furthermore we properly have to copy all canvas item instance in the correct order. (You need that, when you change the color of the text for some characters in TeX/LaTeX (i.e. special commands).) I also added an exclude feature to the decorator, changed some defaults and did some quite different examples. It is all submitted now, please see changeset 3225. Anyway, thanks a lot for your effort to finally bring this functionality to PyX. I'm pretty sure users will love it ... :-) Best, André Am 09.10.2011 um 17:25 schrieb Michael J Gruber: > 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 <mic...@us...> > --- > v2 has singlecharmode as a parameter to text() rather than the texrunner. > The drawback is that ensuredvicanvas() and finishdvi() need that parameter > also, or else the last few characters come out wrong. > --- > examples/text/INDEX | 1 + > examples/text/textalongpath.py | 15 ++++++++++ > examples/text/textalongpath.txt | 5 +++ > pyx/deco.py | 56 +++++++++++++++++++++++++++++++++++++++ > pyx/dvi/dvifile.py | 5 ++- > pyx/text.py | 12 ++++---- > 6 files changed, 86 insertions(+), 8 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 04692ae..c982f89 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 0000000..f4aad4e > --- /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 0000000..1737993 > --- /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 656845f..ceb6712 100644 > --- a/pyx/deco.py > +++ b/pyx/deco.py > @@ -564,6 +564,62 @@ 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() > + > + t = texrunner.text(0, 0, self.text, self.textattrs, singlecharmode=1) > + > + # 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(singlecharmode=1) > + > + 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 b1e8123..d65e952 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 242a4b9..b55667d 100644 > --- a/pyx/text.py > +++ b/pyx/text.py > @@ -700,9 +700,9 @@ class textbox(box.rect, canvas._canvas): > raise RuntimeError("multiple call to setdvicanvas") > self.dvicanvas = dvicanvas > > - def ensuredvicanvas(self): > + def ensuredvicanvas(self, singlecharmode=0): > if self.dvicanvas is None: > - self.finishdvi() > + self.finishdvi(singlecharmode=singlecharmode) > assert self.dvicanvas is not None, "finishdvi is broken" > if not self.insertdvicanvas: > self.insert(self.dvicanvas, [self.texttrafo]) > @@ -1024,14 +1024,14 @@ class texrunner: > else: > raise TexResultError("TeX didn't respond as expected within the timeout period (%i seconds)." % self.waitfortex, self) > > - def finishdvi(self, ignoretail=0): > + def finishdvi(self, ignoretail=0, singlecharmode=0): > """finish TeX/LaTeX and read the dvifile > - this method ensures that all textboxes can access their > dvicanvas""" > 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=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)) > @@ -1151,7 +1151,7 @@ class texrunner: > > PyXBoxPattern = re.compile(r"PyXBox:page=(?P<page>\d+),lt=(?P<lt>-?\d*((\d\.?)|(\.?\d))\d*)pt,rt=(?P<rt>-?\d*((\d\.?)|(\.?\d))\d*)pt,ht=(?P<ht>-?\d*((\d\.?)|(\.?\d))\d*)pt,dp=(?P<dp>-?\d*((\d\.?)|(\.?\d))\d*)pt:") > > - def text(self, x, y, expr, textattrs=[], texmessages=[], fontmap=None): > + def text(self, x, y, expr, textattrs=[], texmessages=[], fontmap=None, singlecharmode=0): > """create text by passing expr to TeX/LaTeX > - returns a textbox containing the result from running expr thru TeX/LaTeX > - the box center is set to x, y > @@ -1190,7 +1190,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=singlecharmode) > match = self.PyXBoxPattern.search(self.texmessage) > if not match or int(match.group("page")) != self.page: > raise TexResultError("box extents not found", self) > -- > 1.7.7.338.g0156b > -- by _ _ _ Dr. André Wobst, Amselweg 22, 85716 Unterschleißheim / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |
From: Michael J G. <mic...@us...> - 2011-10-11 07:13:09
|
André Wobst venit, vidit, dixit 11.10.2011 00:03: > Dear Michael, > > your patch still had the problem, that singlecharmode was a global > setting to the dvi file which is wrong. (You could easily break your > code by rendering some text without singlecharmode being active and > then activating the singlecharmode on the *same* texrunner > instance.) > > Furthermore we properly have to copy all canvas item instance in the > correct order. (You need that, when you change the color of the text > for some characters in TeX/LaTeX (i.e. special commands).) Thanks for the fix and finishing touches. It's good to have someone with the full overview of the codebase spot these things. > I also added an exclude feature to the decorator, changed some > defaults and did some quite different examples. It is all submitted > now, please see changeset 3225. Nice, "exclude" looks simple and useful. > Anyway, thanks a lot for your effort to finally bring this > functionality to PyX. I'm pretty sure users will love it ... :-) Should give good PR and the means to PyXify some logos! Cheers, Michael |
From: Joerg L. <jo...@us...> - 2011-10-11 07:18:56
|
Hi Michael, On 11.10.11, Michael J Gruber wrote: > André Wobst venit, vidit, dixit 11.10.2011 00:03: [...] > > Anyway, thanks a lot for your effort to finally bring this > > functionality to PyX. I'm pretty sure users will love it ... :-) Very nice, indeed! Thanks a lot for bringing this into a usable form! Cheers, Jörg |
From: André W. <wo...@us...> - 2011-10-11 07:40:20
Attachments:
smime.p7s
|
Hi Michael, Am 11.10.2011 um 09:13 schrieb Michael J Gruber: > Nice, "exclude" looks simple and useful. yeah, when we added this feature to the decorated path this use case was on our mind all the time. It's great to finally make it happen. :-) André -- by _ _ _ Dr. André Wobst, Amselweg 22, 85716 Unterschleißheim / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript and PDF figures (_/ \_)_/\_/ with Python & TeX: visit http://pyx.sourceforge.net/ |