[reportlab-users] Re: Avery CD Label Templates
Jerome Alet
reportlab-users@reportlab.com
Mon, 20 Oct 2003 14:54:35 +0200
--bp/iNruPH9dso1Pn
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
See the attached files.
Needs some work, but you get an idea.
The xml file is not read at all for example.
I guess that Dinu's circular text function is more powerful, but
this one is dedicated to CD labels.
hoping this helps.
bye
Jerome Alet
--bp/iNruPH9dso1Pn
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 labelpage(canv, reference, infos) :
"""Draw labels on a page according to a page description."""
infos = info[0]
for disklabel in reference["disklabels"] :
label = CDLabel(info["title"], info["subtitle"], info["tracks"])
canv.saveState()
canv.translate(disklabel["xcenter"], disklabel["ycenter"])
label.draw(canv)
canv.restoreState()
for flatlabel in reference["flatlabel"] :
label = FlatLabel(info["title"])
canv.saveState()
canv.translate(flatlabel["xposition"], flatlabel["yposition"])
canv.rotate(flatlabel["rotation"]
label.draw(canv)
canv.restoreState()
canv.showPage()
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()
--bp/iNruPH9dso1Pn
Content-Type: text/xml; charset=us-ascii
Content-Disposition: attachment; filename="cdlabels.xml"
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- $Id: cdlabels.xml,v 1.1.1.1 2002/03/19 14:55:52 jerome Exp $ -->
<cdlabels>
<!-- please send any correction to : alet@librelogiciel.com -->
<reference name="avery5931" format="LETTER" orientation="portrait">
<disklabel>
<xcenter unit="inch">4.25</xcenter>
<ycenter unit="inch">3.0</ycenter>
<innerradius unit="inch">0.8077</innerradius>
<outerradius unit="inch">2.3125</outerradius>
</disklabel>
<disklabel>
<xcenter unit="inch">4.25</xcenter>
<ycenter unit="inch">8.0</ycenter>
<innerradius unit="inch">0.8077</innerradius>
<outerradius unit="inch">2.3125</outerradius>
</disklabel>
<flatlabel rotation="90">
<xposition unit="inch">0.5</xposition>
<yposition unit="inch">5.34375</yposition>
<width unit="inch">4.6875</width>
<height unit="inch">0.21875</height>
</flatlabel>
<flatlabel rotation="90">
<xposition unit="inch">0.96875</xposition>
<yposition unit="inch">5.34375</yposition>
<width unit="inch">4.6875</width>
<height unit="inch">0.21875</height>
</flatlabel>
<flatlabel rotation="90">
<xposition unit="inch">0.5</xposition>
<yposition unit="inch">10.34375</yposition>
<width unit="inch">4.6875</width>
<height unit="inch">0.21875</height>
</flatlabel>
<flatlabel rotation="90">
<xposition unit="inch">0.96875</xposition>
<yposition unit="inch">10.34375</yposition>
<width unit="inch">4.6875</width>
<height unit="inch">0.21875</height>
</flatlabel>
</reference>
</cdlabels>
--bp/iNruPH9dso1Pn--