[reportlab-users] Circular text widget/drawing?
Jerome Alet
reportlab-users@reportlab.com
Tue, 28 Jan 2003 19:58:32 +0100
--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Hi,
On Tue, Jan 28, 2003 at 05:49:07PM +0100, Dinu Gherman wrote:
> Hi, just being nosey... has anybody written a circular text widget
> or drawing she'd like to share?
this was a starting point of something I wanted to write.
unfortunately lack of time etc...
hoping you'll like it.
include it as a demo if you want : I hereby give you the right
to remove the GPL notice at the top and use and redistribute it
as you see fit, for example just put a RL license into it instead.
bye,
Jerome Alet
--
(c) 1928-???? The letters U, S, and A and the U.S.A. flag are
the copyrighted property of Disney Enterprises Inc.
Any infringment will be punished with the death penalty.
Details : http://yro.slashdot.org/article.pl?sid=03/01/15/1528253&tid=123
--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="cdlabels.py"
#! /usr/bin/env python
#
# cdlabels (c) 2002 Jerome Alet
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
#
# author: Jerome Alet - <alet@librelogiciel.com>
#
# This is the cdlabels, a CD labels generator written in Python
#
# Feel free to send me any feedback about this software at: alet@librelogiciel.com
#
# $Id: cdlabels.py,v 1.2 2002/03/19 19:11:51 jerome Exp $
#
# $Log: cdlabels.py,v $
# Revision 1.2 2002/03/19 19:11:51 jerome
# Modified sample data
#
# Revision 1.1.1.1 2002/03/19 14:55:52 jerome
# Initial import into CVS
#
#
#
import sys
import string
import cStringIO
from math import pi,sqrt
import PIL.Image
try :
import DateTime
except ImportError :
from mx import DateTime
from reportlab.lib.units import cm
from reportlab.pdfgen import canvas
__author__ = "alet@librelogiciel.com (Jerome Alet)"
__version__ = "0.1"
__doc__ = """cdlabels v%s (c) 2002 Jerome Alet - <%s>""" % (__version__, __author__)
class CDLabelsError :
"""CDLabel's Exception class."""
def __init__(self, value) :
self.value = value
def __str__(self) :
return str(self.value)
DISKSIZES = { \
# id : (minradius, maxradius) (innerskip, outerskip) in centimeters
"CD" : ((0.75, 6), (0.5, 0.1)), \
}
class CDLabel :
"""A class for CD labels."""
def __init__(self, title, subtitle="", tracks=[], date=DateTime.now(), size="CD") :
"""Initialize a CD label."""
if not DISKSIZES.has_key(size) :
raise CDLabelsError, "wrong label size %s" % size
self.title = title
self.subtitle = subtitle
self.tracks = tracks
self.date = "Date : %s" % str(date)
((self.minradius, self.maxradius), (self.innerskip, self.outerskip)) = DISKSIZES[size]
self.minradius = self.minradius * cm
self.maxradius = self.maxradius * cm
self.innerskip = self.innerskip * cm
self.outerskip = self.outerskip * cm
def adjustCircularText(self, canv, text, radius, fontname, fontsize) :
"""Adjusts radius and font size to ensure a circular text will be drawn correctly.
returns a tuple : (new radius, new fontsize).
raises a CDLabelError exception when adjustment is impossible.
TODO : make this method smarter.
"""
oldfontsize = fontsize
oldradius = radius
ok = 0
while (not ok) and fontsize and ((radius + fontsize) < (self.maxradius - self.outerskip)) :
width = canv.stringWidth(text, fontname, fontsize)
outercircle = 2.0 * pi * (radius + fontsize)
if width < outercircle :
ok = 1
else :
# TODO : do not do both at the same time
fontsize = fontsize - 1
radius = radius + 1.0
if (not fontsize) or ((radius + fontsize) >= (self.maxradius - self.outerskip)) :
raise CDLabelsError, "Text %s too long to be drawn with a radius of %f in font size %i" % (text, oldradius, oldfontsize)
if oldfontsize != fontsize :
sys.stderr.write("Text %s too long, font size changed from %i to %i\n" % (text, oldfontsize, fontsize))
if oldradius != radius :
sys.stderr.write("Text %s too long, radius changed from %f to %f\n" % (text, oldradius, radius))
return (radius, fontsize)
def drawCircularText(self, canv, text, radius, fontname, fontsize, adjust=1) :
"""Draws a circular text at a given distance from the centre.
Algorithm from the PostScript Blue Book.
If text is too long then font size is reduced and/or radius is increased if adjust=1.
"""
if adjust :
(radius, fontsize) = self.adjustCircularText(canv, text, radius, fontname, fontsize)
canv.saveState()
canv.setFont(fontname, fontsize)
width = canv.stringWidth(text, fontname, fontsize)
xradius = radius + (fontsize / 4.0)
ratio = 360.0 / (4.0 * pi * xradius)
canv.rotate(90.0 + (width * ratio))
for letter in text :
width = canv.stringWidth(letter, fontname, fontsize)
halfangle = width * ratio
canv.saveState()
canv.rotate(-halfangle)
canv.translate(radius, 0)
canv.rotate(-90.0)
canv.drawCentredString(0, 0, letter)
canv.restoreState()
canv.rotate(-2.0 * halfangle)
canv.restoreState()
def drawDate(self, canv) :
self.drawCircularText(canv, self.date, self.minradius + self.innerskip + 2, 'Times-Roman', 8)
def drawTitles(self, canv) :
fontsize = 30
self.drawCircularText(canv, self.title, self.maxradius - self.outerskip - (fontsize + 10), 'Helvetica-Bold', fontsize)
self.drawCircularText(canv, self.subtitle, self.maxradius - self.outerskip - ((1.5 * fontsize) + 20), 'Helvetica', fontsize / 2)
def drawTracks(self, canv) :
if self.tracks :
fontname = 'Helvetica'
fontsize = 6
mxlen = 0
for track in self.tracks :
mxlen = max(mxlen, canv.stringWidth(track, fontname, fontsize))
yorigin = self.minradius + self.innerskip + (3*fontsize)
# why the f..k are my maths this old ???
#availablewidth = 2 * sqrt((self.maxradius - self.outerskip)**2 - yorigin**2)
#print availablewidth
canv.saveState()
canv.setFont(fontname, fontsize)
tracks = string.join(self.tracks, "\n")
t = canv.beginText(-mxlen / 2.0, -yorigin)
t.textLines(tracks)
canv.drawText(t)
canv.restoreState()
def clip(self, canv) :
"""Sets a clipping region for future drawings."""
# begin the clipping path
path = canv.beginPath()
# draw the internal and external limits
path.circle(0, 0, self.minradius + self.innerskip)
path.circle(0, 0, self.maxradius - self.outerskip)
# validates the drawing path, with or without filling
# TODO : stroke=0
canv.clipPath(path, stroke = 1, fill = 0)
def draw(self, canv) :
"""Draws the CD label on the PDF canvas."""
# save current state of canvas
canv.saveState()
# set drawing thickness
canv.setLineWidth(0.01)
# set clipping region
self.clip(canv)
# then really draw the label's content
self.drawDate(canv)
self.drawTitles(canv)
self.drawTracks(canv)
# restore original state
canv.restoreState()
def main() :
tracks = ["1 - What the fuck is this ?", "2 - Hello point C", "3 - Foo Bar"]
label = CDLabel("Jerome Alet", "and his orchestra", tracks)
c = canvas.Canvas("label.pdf")
c.translate(10.5*cm, 14.85*cm)
label.draw(c)
c.showPage()
c.save()
print __doc__
if __name__ == "__main__" :
main()
--YZ5djTAD1cGYuMQK--