|
From: André W. <wo...@us...> - 2011-10-10 22:04:36
|
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/
|