From: Michael J G. <mic...@fa...> - 2006-03-16 15:55:29
|
Hi again, I'm kinda getting to like the deco concept... I remembered an old post by Andre, showing a method (due to J=F6rg, I think) for laying out text along a curve. I adjusted it to the current API and coded it as a decorator dubbed deco.curvedtext(). I'm attaching a simple example (how to achieve typical line ups) and the code. The code goes into deco.py (current svn). I'm unsure about the following: a) Is the patch to dvifile.dvifile.putchar appropriate for going into the trunk, or are there side effects (I don't know that module)? b) Is it appropriate to attach eps examples here? c) What's the best format for code suggestions? I'd be happy to send diffs, but the patch mentioned above made it somehow inconvenient here (the "local" patch I'm applying won't be the final form, of course). As for the other text decorators (text as it is or (t)text as suggested by me recently or text rotated parallel to the curve [tangential labels]): I imagine a universal decorator deco.insert() would be best which inserts a given canvas at certain points of the path, with or without a user specified alignment, with or without rotation with respect to the absolute co system or the local path tangent. Then, all text decorators (except for deco.curvedtext) would be simple calls to deco.insert(text.text(...)). But one could also easily decorate with symbols "moving" along the path and such. Please tell me what you think, or else I might start hacking away ;) Cheers, Michael |
From: Joerg L. <jo...@us...> - 2006-03-16 16:43:19
|
Hi Michael, On 16.03.06, Michael J Gruber wrote: > I'm kinda getting to like the deco concept... I remembered an old post > by Andre, showing a method (due to Jörg, I think) for laying out text > along a curve. I adjusted it to the current API and coded it as a > decorator dubbed deco.curvedtext(). I'm attaching a simple example (how > to achieve typical line ups) and the code. The code goes into deco.py > (current svn). I'm unsure about the following: > > a) Is the patch to dvifile.dvifile.putchar appropriate for going into > the trunk, or are there side effects (I don't know that module)? No, in this form it's not appropriate because this means that we adjust the position after each character, which is rather inefficient. The correct way would be to either add an option to the texrunner or to allow switching of the single char mode on and off in a dynamical way, such that you can request this feature for only parts of the output. Because the curved-text thing was really more of quick and dirty a hack, I didn't follow it further. But it's really neat so maybe we should integrate it in the core. > b) Is it appropriate to attach eps examples here? Depends on the size, I would say. > c) What's the best format for code suggestions? I'd be happy to send > diffs, but the patch mentioned above made it somehow inconvenient here > (the "local" patch I'm applying won't be the final form, of course). > > As for the other text decorators (text as it is or (t)text as suggested > by me recently or text rotated parallel to the curve [tangential > labels]): I imagine a universal decorator deco.insert() would be best > which inserts a given canvas at certain points of the path, with or > without a user specified alignment, with or without rotation with > respect to the absolute co system or the local path tangent. Then, all > text decorators (except for deco.curvedtext) would be simple calls to > deco.insert(text.text(...)). But one could also easily decorate with > symbols "moving" along the path and such. Please tell me what you think, > or else I might start hacking away ;) I like this idea. > Cheers, > Michael > import os,sys > sys.path.insert(0, os.environ.get("HOME")+"/lib/python/PyX-svn") Just a side remark: There is os.path.expanduser which allows you to just write os.path.expanduser("~/lib/...") > 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(__file__[:-3],fittosize=1, paperformat=document.paperformat.A4) > import dvifile,type1font > > oldputchar = dvifile.dvifile.putchar > > def newputchar(self, char, advancepos=1): > oldputchar(self, char, advancepos) > self.flushtext() > > dvifile.dvifile.putchar = newputchar > > class curvedtext(deco, attr.attr): > """a text decorator for curved text""" > > 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) > for op in t.items: # copy over attr ops (colour...) > if not isinstance(op, canvas._canvas): # should not occur before ensuredvicanvas > c.insert(op) > > t.ensuredvicanvas() This is what I mean with rather hackish... > for op in t.dvicanvas.items: > if isinstance(op, type1font.text_pt): > x = textpos + unit.t_pt*(op.x_pt+op.width_pt/2) # Make sure we rotate with respect to the middle of the character. > op.x_pt = -op.width_pt/2 > c.insert(op, [dp.path.trafo(x)]) > else: > c.insert(op) My latest version looked like: items = t.dvicanvas.items xs = [item.bbox().center()[0] for item in items] trafos = p.trafo(xs) for x, op, atrafo in zip(xs, items, trafos): c.insert(op, [atrafo, trafo.translate(-x, 0)]) > dp.ornaments.insert(c) Jörg |
From: Michael J G. <mic...@fa...> - 2006-03-17 16:03:49
Attachments:
curvedtext.diff
|
Joerg Lehmann venit, vidit, dixit 2006-03-16 17:43: > Hi Michael, >=20 > On 16.03.06, Michael J Gruber wrote: >> I'm kinda getting to like the deco concept... I remembered an old post >> by Andre, showing a method (due to J=F6rg, I think) for laying out tex= t >> along a curve. I adjusted it to the current API and coded it as a >> decorator dubbed deco.curvedtext(). I'm attaching a simple example (ho= w >> to achieve typical line ups) and the code. The code goes into deco.py >> (current svn). I'm unsure about the following: >=20 >> a) Is the patch to dvifile.dvifile.putchar appropriate for going into >> the trunk, or are there side effects (I don't know that module)? >=20 > No, in this form it's not appropriate because this means that we adjust > the position after each character, which is rather inefficient. > The correct way would be to either add an option to the texrunner or > to allow switching of the single char mode on and off in a dynamical > way, such that you can request this feature for only parts of the > output. I see. It can't be a "set" option either because TeX might be running already, and it should still be able to change the option. I implemented a workaround, but I don't know how you think about changing class variables from the outside... ... >> sys.path.insert(0, os.environ.get("HOME")+"/lib/python/PyX-svn") >=20 > Just a side remark: There is os.path.expanduser which allows you to > just write os.path.expanduser("~/lib/...") Not much shorter, but nicer. >> for op in t.dvicanvas.items: >> if isinstance(op, type1font.text_pt): >> x =3D textpos + unit.t_pt*(op.x_pt+op.width_pt/2) # Ma= ke sure we rotate with respect to the middle of the character. >> op.x_pt =3D -op.width_pt/2 >> c.insert(op, [dp.path.trafo(x)]) >> else: >> c.insert(op) >=20 > My latest version looked like: >=20 > items =3D t.dvicanvas.items > xs =3D [item.bbox().center()[0] for item in items] > trafos =3D p.trafo(xs) > for x, op, atrafo in zip(xs, items, trafos): > c.insert(op, [atrafo, trafo.translate(-x, 0)]) >=20 >> dp.ornaments.insert(c) Maybe not shorter, but definitely more pythonish! Also, it's good to avoid isinstance(). But is this code really more efficient than having one loop running through all op? It seems we have two list comprehensions plus one loop now instead of the single loop before. So, here comes the diff. Cheers, Michael |