[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--