[reportlab-users] Circular text (was: iCards...)
Dinu Gherman
reportlab-users@reportlab.com
Wed, 29 Jan 2003 18:30:04 +0100
--Apple-Mail-11--1017788169
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
I wrote:
> I'm attaching an experimental function which does circular text,
> using Drawings, mostly as I like.
Ok, it now is a class CircularString, which I first wanted to sub-
class from shapes.String, but that won't work as you'd need to tell
the renderers how to handle it...
So I need to make it a subclass of shapes.UsewrNode, which seems to
be the canonical way of doing it, but I still don't like it that
much, because the base attributes come from a different class (i.e.
shapes.String) and you need to copy some methods you might need
from the Shape base class as I did with setProperties()...
Anyway, it kind of works, but might need some finetuning when using
text anchors like 'start' and 'end'... Also the angle parameter
should probably become mandatory, isn't it?
Please folks, test it! If it's useful let's check it into CVS!
Dinu
--Apple-Mail-11--1017788169
Content-Disposition: attachment;
filename=circularText.py
Content-Transfer-Encoding: 7bit
Content-Type: application/text;
x-unix-mode=0644;
x-mac-type=54455854;
name="circularText.py"
#! /usr/bin/env python
import time
from math import pi, sqrt, sin, cos
from reportlab.lib.validators import isNumber, OneOf
from reportlab.lib.attrmap import AttrMap, AttrMapValue
from reportlab.lib import colors
from reportlab.graphics import shapes
from reportlab.graphics import renderPDF, renderPM
from reportlab.pdfbase.pdfmetrics import stringWidth
class CircularString(shapes.UserNode):
"A circular string class."
_attrMap = AttrMap(BASE=shapes.String,
radius = AttrMapValue(isNumber),
angle = AttrMapValue(isNumber),
counterClockWise = AttrMapValue(OneOf(0, 1)),
)
def __init__(self, x, y, radius, text, **kw):
"Initialize."
# set mandatory values
self.x = x
self.y = y
self.text = text
self.radius = radius
# provide default values
self.fillColor = colors.black
self.fontName = 'Helvetica-Bold'
self.fontSize = 12
self.textAnchor = 'middle'
self.angle = 0
self.counterClockWise = 0
# overwrite defaults, if needed
self.setProperties(kw)
def setProperties(self, props):
self.__dict__.update(props)
## self.verify()
def provideNode(self):
"Return a group with displaced strings for each letter."
angle = self.angle
radius = self.radius
ccw = self.counterClockWise
fn, fs, = self.fontName, self.fontSize
width = stringWidth(self.text, fn, fs)
alpha = width / self.radius / pi*180
g = shapes.Group()
b = {0: -angle, 1: 180-angle}[ccw]
# apply global textAnchor (might need some finetuning...)
b = b + {'start':alpha/2, 'middle':0, 'end':-alpha/2}[self.textAnchor]
for letter in self.text:
# create a group containing one letter
h = shapes.Group()
## h.add(shapes.Circle(0, 0, 3, fillColor=colors.red))
s = shapes.String(0, 0, letter)
s.textAnchor = 'middle' # really needs to be 'middle'!
s.fillColor = self.fillColor
s.fontName = fn
s.fontSize = fs
h.add(s)
# translate it to new origin: x,y
h.translate(self.x, self.y)
# translate to circle border: dx,dy
width = stringWidth(letter, fn, fs)
beta = width / radius / pi*180
phi = alpha/2-b-beta/2
if ccw:
phi = 180 - phi
dx = -sin(phi*pi/180)*radius
dy = cos(phi*pi/180)*radius
h.translate(dx, dy)
# rotate as needed
if ccw:
phi = phi - 180
h.rotate(phi)
# increment used letter widths in degrees
b = b + beta
g.add(h)
return g
def main() :
col = colors.blue
w, h = 200, 200
x, y = 100, 100
r = 50
fn, fs = "Helvetica-Bold", 28
d = shapes.Drawing(w, h)
g = shapes.Group()
# add a couple of helping widgets
g.add(shapes.Rect(0, 0, w, h, fillColor=None))
g.add(shapes.Circle(x, y, 3, fillColor=colors.black))
g.add(shapes.Circle(x, y, r, fillColor=None))
g.add(shapes.Circle(x, y, r+fs, fillColor=None))
# add two circular texts
# add two circular texts
for text, angle, ccw in [("Python", 0, 0), ("p o w e r e d", 180, 1)]:
cs = CircularString(x, y, r+ccw*fs/2, text)
cs.angle = angle
cs.textAnchor = 'middle'
cs.fontName = fn
cs.fontSize = fs
cs.fillColor = col
cs.counterClockWise = ccw
g.add(cs)
d.add(g)
# renderPM.drawToFile(d, "circularText.jpg", "JPG")
renderPDF.drawToFile(d, "circularText.pdf")
if __name__ == "__main__" :
main()
--Apple-Mail-11--1017788169
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
format=flowed
--
Dinu C. Gherman
......................................................................
"America is the only country that went from barbarism to decadence
without civilization in between." (Oscar Wilde)
--Apple-Mail-11--1017788169--