rtl-support: merged trunk changes r3591:3803 rtl-support
authorrptlab
Thu, 21 Oct 2010 10:34:13 +0000
branchrtl-support
changeset 3467 4f25b3a34c5f
parent 3267 c2db88548c0a
child 3486 55cc3834554a
rtl-support: merged trunk changes r3591:3803
CHANGES.txt
INSTALL.txt
LICENSE.txt
README.txt
demos/odyssey/dodyssey.py
demos/odyssey/fodyssey.py
docs/Makefile
docs/genAll.py
docs/make.bat
docs/source/_static/basic.css
docs/source/_static/default.css
docs/source/_templates/layout.html
docs/source/_templates/page.html
docs/source/conf.py
docs/source/graphics.rst
docs/source/index.rst
docs/source/lib.rst
docs/source/pdfgen.rst
docs/source/platypus.rst
docs/userguide/ch1_intro.py
docs/userguide/ch2_graphics.py
docs/userguide/ch5_paragraphs.py
docs/userguide/ch6_tables.py
ez_setup.py
setup.cfg
setup.py
setup_egg.py
src/reportlab/__init__.py
src/reportlab/graphics/barcode/__init__.py
src/reportlab/graphics/barcode/code128.py
src/reportlab/graphics/barcode/code39.py
src/reportlab/graphics/barcode/code93.py
src/reportlab/graphics/barcode/common.py
src/reportlab/graphics/barcode/eanbc.py
src/reportlab/graphics/barcode/lto.py
src/reportlab/graphics/barcode/qr.py
src/reportlab/graphics/barcode/usps.py
src/reportlab/graphics/barcode/widgets.py
src/reportlab/graphics/charts/areas.py
src/reportlab/graphics/charts/axes.py
src/reportlab/graphics/charts/barcharts.py
src/reportlab/graphics/charts/doughnut.py
src/reportlab/graphics/charts/legends.py
src/reportlab/graphics/charts/linecharts.py
src/reportlab/graphics/charts/lineplots.py
src/reportlab/graphics/charts/piecharts.py
src/reportlab/graphics/charts/spider.py
src/reportlab/graphics/charts/textlabels.py
src/reportlab/graphics/charts/utils.py
src/reportlab/graphics/renderPDF.py
src/reportlab/graphics/renderPM.py
src/reportlab/graphics/renderPS.py
src/reportlab/graphics/renderSVG.py
src/reportlab/graphics/renderbase.py
src/reportlab/graphics/samples/bubble.py
src/reportlab/graphics/samples/clustered_bar.py
src/reportlab/graphics/samples/clustered_column.py
src/reportlab/graphics/samples/exploded_pie.py
src/reportlab/graphics/samples/filled_radar.py
src/reportlab/graphics/samples/line_chart.py
src/reportlab/graphics/samples/linechart_with_markers.py
src/reportlab/graphics/samples/radar.py
src/reportlab/graphics/samples/scatter.py
src/reportlab/graphics/samples/scatter_lines.py
src/reportlab/graphics/samples/scatter_lines_markers.py
src/reportlab/graphics/samples/simple_pie.py
src/reportlab/graphics/samples/stacked_bar.py
src/reportlab/graphics/samples/stacked_column.py
src/reportlab/graphics/shapes.py
src/reportlab/graphics/testshapes.py
src/reportlab/graphics/widgetbase.py
src/reportlab/graphics/widgets/eventcal.py
src/reportlab/graphics/widgets/grids.py
src/reportlab/graphics/widgets/markers.py
src/reportlab/graphics/widgets/signsandsymbols.py
src/reportlab/lib/PyFontify.py
src/reportlab/lib/__init__.py
src/reportlab/lib/abag.py
src/reportlab/lib/attrmap.py
src/reportlab/lib/colors.py
src/reportlab/lib/corp.py
src/reportlab/lib/extformat.py
src/reportlab/lib/fontfinder.py
src/reportlab/lib/fonts.py
src/reportlab/lib/logger.py
src/reportlab/lib/normalDate.py
src/reportlab/lib/pdfencrypt.py
src/reportlab/lib/pygments2xpre.py
src/reportlab/lib/rparsexml.py
src/reportlab/lib/styles.py
src/reportlab/lib/testutils.py
src/reportlab/lib/textsplit.py
src/reportlab/lib/utils.py
src/reportlab/lib/validators.py
src/reportlab/lib/xmllib.py
src/reportlab/lib/yaml.py
src/reportlab/pdfbase/_can_cmap_data.py
src/reportlab/pdfbase/_fontdata.py
src/reportlab/pdfbase/_fontdata_enc_macexpert.py
src/reportlab/pdfbase/_fontdata_enc_macroman.py
src/reportlab/pdfbase/_fontdata_enc_pdfdoc.py
src/reportlab/pdfbase/_fontdata_enc_standard.py
src/reportlab/pdfbase/_fontdata_enc_symbol.py
src/reportlab/pdfbase/_fontdata_enc_winansi.py
src/reportlab/pdfbase/_fontdata_enc_zapfdingbats.py
src/reportlab/pdfbase/_fontdata_widths_courier.py
src/reportlab/pdfbase/_fontdata_widths_courierbold.py
src/reportlab/pdfbase/_fontdata_widths_courierboldoblique.py
src/reportlab/pdfbase/_fontdata_widths_courieroblique.py
src/reportlab/pdfbase/_fontdata_widths_helvetica.py
src/reportlab/pdfbase/_fontdata_widths_helveticabold.py
src/reportlab/pdfbase/_fontdata_widths_helveticaboldoblique.py
src/reportlab/pdfbase/_fontdata_widths_helveticaoblique.py
src/reportlab/pdfbase/_fontdata_widths_symbol.py
src/reportlab/pdfbase/_fontdata_widths_timesbold.py
src/reportlab/pdfbase/_fontdata_widths_timesbolditalic.py
src/reportlab/pdfbase/_fontdata_widths_timesitalic.py
src/reportlab/pdfbase/_fontdata_widths_timesroman.py
src/reportlab/pdfbase/_fontdata_widths_zapfdingbats.py
src/reportlab/pdfbase/cidfonts.py
src/reportlab/pdfbase/pdfdoc.py
src/reportlab/pdfbase/pdfform.py
src/reportlab/pdfbase/pdfmetrics.py
src/reportlab/pdfbase/pdfpattern.py
src/reportlab/pdfbase/pdfutils.py
src/reportlab/pdfbase/rl_codecs.py
src/reportlab/pdfbase/ttfonts.py
src/reportlab/pdfgen/canvas.py
src/reportlab/pdfgen/pdfimages.py
src/reportlab/pdfgen/pycanvas.py
src/reportlab/pdfgen/textobject.py
src/reportlab/platypus/doctemplate.py
src/reportlab/platypus/figures.py
src/reportlab/platypus/flowables.py
src/reportlab/platypus/para.py
src/reportlab/platypus/paragraph.py
src/reportlab/platypus/paraparser.py
src/reportlab/platypus/tableofcontents.py
src/reportlab/platypus/tables.py
src/reportlab/platypus/xpreformatted.py
src/reportlab/rl_config.py
src/rl_addons/renderPM/_renderPM.c
src/rl_addons/renderPM/pfm.py
src/rl_addons/renderPM/setup.cfg
src/rl_addons/renderPM/setup.py
src/rl_addons/rl_accel/_rl_accel.c
tests/runAll.py
tests/test_charts_textlabels.py
tests/test_docs_build.py
tests/test_docstrings.py
tests/test_graphics_charts.py
tests/test_graphics_layout.py
tests/test_hello.py
tests/test_lib_utils.py
tests/test_multibyte_jpn.py
tests/test_paragraphs.py
tests/test_pdfbase_pdfdoc.py
tests/test_pdfbase_ttfonts.py
tests/test_pdfgen_general.py
tests/test_pdfgen_overprint.py
tests/test_pdfgen_pycanvas.py
tests/test_platypus_breaking.py
tests/test_platypus_general.py
tests/test_platypus_indents.py
tests/test_platypus_index.py
tests/test_platypus_leftright.py
tests/test_platypus_paragraphs.py
tests/test_platypus_paraparser.py
tests/test_platypus_tables.py
tests/test_platypus_toc.py
tests/test_platypus_xref.py
tests/test_pyfiles.py
tests/test_rl_accel.py
tests/test_source_chars.py
tools/docco/codegrab.py
tools/docco/docpy.py
tools/docco/examples.py
tools/docco/graphdocpy.py
tools/docco/rltemplate.py
tools/docco/t_parse.py
tools/docco/yaml.py
tools/pythonpoint/demos/slidebox.py
tools/pythonpoint/pythonpoint.py
tools/pythonpoint/stdparser.py
--- a/CHANGES.txt	Mon Nov 23 13:27:57 2009 +0000
+++ b/CHANGES.txt	Thu Oct 21 10:34:13 2010 +0000
@@ -8,6 +8,120 @@
 The contributors lists are in no order and apologies to those accidentally not
 mentioned. If we missed you, please let us know!
 
+
+#################################################################################
+#################### RELEASE 2.5  at 18:00 GMT  01/Oct/2010     #################
+#################################################################################
+
+Many new features have been added and numerous bugs have been fixed.
+
+Thanks to everybody who has contributed to the open-source toolkit in
+the run-up to the 2.5 release, whether by reporting bugs, sending patches,
+or contributing to the reportlab-users mailing list.
+Major contributors are credited in the user documentation.
+
+   * Support for colour separated PDF output and other optimisations and
+     features for high-quality printing, including enforcement of colour
+     models for CMYK, RGB, and "spot colours"
+   * Long table optimisations are now turned on by default.  Previously,
+     documents with very long tables spanning many pages could take a long
+     time to create because we considered the whole table to work out row
+     and column sizes.  A patch was submitted some time ago to fix this
+     controlled by a flag in the rl_config file, but this was set 'off'
+     for compatibility.  Users are often not aware of this and we haven't
+     found any real-world cases where the new layout technique works badly,
+     so we are turning this behaviour on.
+   * New support for QR barcodes - [try our demo!](https://www.reportlab.com/demos/qr/)
+
+###PDF
+   * Colour separation and other enhancements for high-end print
+   * Python 2.7 support
+
+###Charts
+   * reportlab.graphics.charts.axes
+       * ValueAxis
+           * avoidBoundSpace - Space to allow above and below
+           * abf_ignore_zero - Set to True to make the avoidBoundFrac calculations treat zero as non-special
+           * keepTickLabelsInside - Ensure tick labels do not project beyond bounds of axis if true
+       * NormalDateXValueAxis
+           * specialTickClear - clear rather than delete close ticks when forced first/end dates
+       * AdjYValueAxis
+           * labelVOffset - add this to the labels
+   * reportlab.graphics.charts.barcharts
+       * BarChart
+           * categoryLabelBarSize - width to leave for a category label to go between categories
+           * categoryLabelBarOrder - where any label bar should appear first/last
+           * barRecord (advanced) - callable(bar,label=labelText,value=value,**kwds) to record bar information
+   * reportlab.graphics.charts.legends
+       * SubColProperty
+           * dx - x offset from default position
+           * dy - y offset from default position
+       * Legend
+           * swdx - x position adjustment for the swatch
+           * swdy - y position adjustment for the swatch
+   * reportlab.graphics.charts.piecharts
+       * Pie
+           * wedgeRecord (advanced) - callable(wedge,*args,**kwds)
+
+   * reportlab.graphics.charts.utils
+       * DrawTimeCollector - generic mechanism for collecting information about nodes at the time they are about to be drawn
+
+#################################################################################
+#################### RELEASE 2.4  at 18:00 GMT  20/Jan/2010     #################
+#################################################################################
+
+PDF
+    lots of improvements and verbosity to error messages and the way they are handled.
+    font size can now be specified in pixels
+    unicode file names are now accepted
+
+Platypus
+    canvas auto cropmarks
+    added support for styles h4-h6
+    Improved support for onDraw and SimpleIndex
+    Add support for index tableStyle
+    Added an alphabetic grouping indexing class
+    Added support for multi-level and alphabetical indexes
+    Added support for an unlimited number of TOC levels with default styles
+    Index entries can now be clickable.
+
+Graphics:
+    Axes values can be reversible.
+    Labels on the axes can now be drawn above or below the axes (hi or low).
+    A per swatch callout is now allowed in the legend.
+    A new anchroing mode for string 'numeric' that align numerical strings by their decimal place.
+    Shapes have new attributes to specify if the shape should grow to take all canvas area (vertically or horizontally) or if the canvas should shrink to fit the shape size.
+    color objects now have a clone method.
+    colors module has a fade function that returns a list of different shades made up of one base colour.
+    added in support for Overprint/Opacity & Separated colours
+
+Bugs fixes
+    word counting in complex paragraphs has been fixed.
+    SimpleIndex and TableOfContents bugs have been fixed.
+    Fix for position of hyperlinks when crop marks are added.
+    flowables.py: fix special case of doctemplate with no frames
+    PDFFormXObject.format missing Resources bug patch from Scott Meyer
+    KeepInFrame justification bug has been fixed.
+    paragraph.py: fix linebreaking bug thanks to Roberto Alsina
+    fix unicode/str issue bug found by Michael Egorov <michwill@gmail.com>
+    YCategoryAxis makeTickLabels fix contributed by Mike Folwell <mjf@pearson.co.uk>
+    pdfdoc.py: fix ro PDFDate contributed by Robert Alsina
+    and others ..
+
+contributers
+------------
+    PJACock's (<peter@maubp.freeserve.co.uk>)
+    Hans Brand
+    Ian Stevens
+    Yoann Roman <yroman-reportlab@altalang.com>
+    Randolph Bentson
+    Volker Haas
+    Simon King
+    Henning Vonbargen
+    Michael Egorov <michwill@gmail.com>
+    Mike Folwell <mjf@pearson.co.uk>
+    Robert Alsina
+    and more ...
 #################################################################################
 #################### RELEASE 2.3  at 18:00 GMT  04/Feb/2009     #################
 #################################################################################
--- a/INSTALL.txt	Mon Nov 23 13:27:57 2009 +0000
+++ b/INSTALL.txt	Thu Oct 21 10:34:13 2010 +0000
@@ -1,17 +1,1 @@
-0)	All install Python, freetype2 and PIL
-	unixers will need development versions of python and freetype2.
-	Windows edit setup.cfg to add the path to the freetype2 .lib
-	and the freetype2 include files
-
-1)	python setup.py tests-preinstall
-
-2)	python setup.py install
-	if you see trailing message saaying installed renderPM with freetype
-	then things should be OK. If it fails to locate freetype you may see
-	installed renderPm without freetype no ttf; sorry.
-
-3)	python setup.py tests
-
-
-hopefully the tests in 1 should complain about not having renderPM and in
-2 should not.
+Please see README.txt for installation instructions.
--- a/LICENSE.txt	Mon Nov 23 13:27:57 2009 +0000
+++ b/LICENSE.txt	Thu Oct 21 10:34:13 2010 +0000
@@ -1,6 +1,6 @@
 #####################################################################################
 #
-#	Copyright (c) 2000-2008, ReportLab Inc.
+#	Copyright (c) 2000-2010, ReportLab Inc.
 #	All rights reserved.
 #
 #	Redistribution and use in source and binary forms, with or without modification,
--- a/README.txt	Mon Nov 23 13:27:57 2009 +0000
+++ b/README.txt	Thu Oct 21 10:34:13 2010 +0000
@@ -1,37 +1,87 @@
-#copyright ReportLab Inc. 2000-2008
+#copyright ReportLab Inc. 2000-2010
 #see LICENSE.txt for license details
 
-This is the ReportLab PDF Library.
-It allows rapid creation of rich PDF documents,
-and also creation of charts in a variety of bitmap
-and vector formats.
+This is the ReportLab PDF Toolkit. It allows rapid creation 
+of rich PDF documents, and also creation of charts in a variety 
+of bitmap and vector formats.  
+
+This library is also the foundation for our commercial product
+Report Markup Language (RML), available in the ReportLab PLUS
+package. RML offers many more features, a template-based style
+of document development familiar to all web developers, and
+higher development productivity.  Please consider trying out
+RML for your project, as the license sales support our open
+source development.
 
 
-Licensing
-=========
+Contents of this file:
+1. Licensing
+2. Installation
+ 2.1 Source distribution or subversion
+ 2.2 Manual installation without C compiler
+ 2.3 easy_install
+ 2.4 Windows .exe installer 
+ 2.5 Ubuntu and other Debian-based systems
+3. Prerequisites / dependencies
+4. Documentation
+5. Acknowledgments
+
+1.Licensing
+===========
 BSD license.  See LICENSE.txt for details
 
 
-Installation
-============
-This should now (Sep 2008) be distutils-compliant. Installation
-depends which distribution you are using.
+2.Installation
+==============
+You need to have installed Python (versions 2.3 through 2.7),
+and ideally PIL with Freetype support; more notes on prerequisites
+follow below.  
+
+We aim to be compatible with several of the popular installation
+techniques - please pick your preferred one below...
 
 
-(1). Subversion or source distributions:
+2.1. Subversion or source distributions:
+---------------------------------------
 
 Use
-   python setup.py install
+    python setup.py install
+
+After this has completed you should be able to run
+
+    python setup.py tests
+
+and see error-free output.
+
+(Note 1: If you see a line of dots, and a small number of errors
+relating to 'renderPM', it's likely that your C compiler
+environment is incorrect and that the renderPM C extension
+could not be installed. However, it's only needed if you
+want to generate bitmap graphics - more on this below)
+
+(Note 2: there is also an option 'python setup.py tests-preinstall', 
+which will run the tests where you unpack the files; this is 
+expected to fail on one or two tests involving renderPM as 
+that extension has not been compiled yet.)
+
 
 This assumes you have a C compiler and the necessary
-packages to build Python extensions.  On Ubuntu, you
-will need at least build-essentials and python-devel.
+packages to build Python extensions. If you are installing
+system-wide you will need root permissions e.g.:
+
+    sudo python setup.py install
+
+On Ubuntu, you will need
+build-essential, libfreetype6-dev, python-dev and python-imaging.
 Most other Linux and xBSD distributions have packages with
 similar names.
+
 On Windows you need the correct version of Visual Studio
 for the Python you are using.
 
-(2) Manual installation without C compiler (e.g. Windows):
+
+2.2 Manual installation without C compiler (e.g. Windows):
+---------------------------------------------------------
 
 - either place the src/ folder on your path, or move
 the 'reportlab' package inside it to somewhere on your
@@ -43,24 +93,45 @@
 bitmap image generation capabilities.
    http://www.reportlab.org/ftp/win32-dlls/
 
-(3) setuptools / easy-install 
+
+2.3 easy-install
+----------------
+easy_install is a popular Python deployment tool.
 
-We also have a setuptools-based setup script, setup_egg.py,
-contributed by Dirk Holtwick.  It does not yet build the
-C extensions.  We welcome contributions to improve this
-for future releases.
+As of this version, you should be able to install with 
+"easy_install reportlab".   We do NOT use a setuptools-based
+script, but have modified our distribution to be compatible with 
+easy_install.
 
-(4) Binary distributions (e.g. windows .exe)
-We are starting to experiment with these.  At the time of the
-writing (Sep 11 2008), distutils builds self-installing EXEs.
-As and when these get built they will appear at
-http://www.reportlab.org/downloads.
+
+2.4 Windows .exe installer
+--------------------------
+A binary .exe installer for Windows (built with distutils) is
+available on our website.  
 
 
-Prerequisites / dependencies
-============================
-This works with Python 23, 2.4 and 2.5.
-2.6 is not tested yet but you are welcome to try.
+2.5 Ubuntu and other Debian-based systems
+-----------------------------------------
+The latest releases are generally available in the ubuntu repositories
+within 2-3 weeks.  At the time of writing (20th Jan 2010) the basic
+reportlab installer does not include the C extensions, so we recommend
+installing these THREE packages for a full-speed, full-features installation:
+
+    sudo apt-get install python-reportlab python-reportlab-accel python-renderpm  
+    
+There is also a package 'python-reportlab-doc' including the built PDF manuals,
+which are also available on our website.
+
+Alternatively, if you would rather compile from source
+you will need compilers and other dependencies as follows, and can then
+follow the instructions in 2.1 above...
+    sudo apt-get install build-essential libfreetype6-dev python-dev python-imaging
+
+
+3. Prerequisites / dependencies
+===============================
+This works with Python 2.3 - 2.6.
+2.7 is not tested yet but you are welcome to try.
 
 There are no absolute prerequisites beyond the Python
 standard library; but the Python Imaging Library (PIL)
@@ -72,20 +143,20 @@
 as bitmap images for the web, as well as inside PDFs.
 
 
-Documentation
-=============
+4. Documentation
+================
 Naturally, we generate our own manuals using the library.
 In a 'built' distribution, they may already be present in the
 docs/ directory.  If not, execute "python genAll.py" in
 that directory, and it will create the manuals.
 
 
-Acknowledgements and Thanks
-===========================
+5. Acknowledgements and Thanks
+==============================
 lib/normalDate.py originally by Jeff Bauer
 
-(this section needs updating badly! Many, many contributors
-between 2000 and 2008 - please let us know your names)
+Many, many contributors have helped out between 2000 and 2010.
+we keep a list in the first chapter of the User Guide; if you
+have contributed and are not listed there, please let us know.
 
-setup_egg script and utilities contributed by Dirk Holtwick
 
--- a/demos/odyssey/dodyssey.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/demos/odyssey/dodyssey.py	Thu Oct 21 10:34:13 2010 +0000
@@ -39,7 +39,7 @@
 def go():
     def myCanvasMaker(fn,**kw):
         from reportlab.pdfgen.canvas import Canvas
-        canv = apply(Canvas,(fn,),kw)
+        canv = Canvas(fn,**kw)
         # attach our callback to the canvas
         canv.myOnDrawCB = myOnDrawCB
         return canv
@@ -208,7 +208,7 @@
     t4 = time()
     print "Deleting list of lines took %.4f seconds" %(t4-t3)
     for i in xrange(len(E)):
-        apply(E[i][0],E[i][1:])
+        E[i][0](*E[i][1:])
     t5 = time()
     print "Moving into platypus took %.4f seconds" %(t5-t4)
     del E
--- a/demos/odyssey/fodyssey.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/demos/odyssey/fodyssey.py	Thu Oct 21 10:34:13 2010 +0000
@@ -142,7 +142,7 @@
     t4 = time()
     print "Deleting list of lines took %.4f seconds" %(t4-t3)
     for i in xrange(len(E)):
-        apply(E[i][0],E[i][1:])
+        E[i][0](*E[i][1:])
     t5 = time()
     print "Moving into platypus took %.4f seconds" %(t5-t4)
     del E
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/Makefile	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,89 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  dirhtml   to make HTML files named index.html in directories"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  qthelp    to make HTML files and a qthelp project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+	@echo "  doctest   to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/reportlab.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/reportlab.qhc"
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
--- a/docs/genAll.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/docs/genAll.py	Thu Oct 21 10:34:13 2010 +0000
@@ -26,7 +26,7 @@
             except:
                 if verbose: traceback.print_exc()
         else:
-            cmd = '%s %s %s' % (sys.executable,os.path.basename(p), not verbose and '-s' or '')
+            cmd = '"%s" %s %s' % (sys.executable,os.path.basename(p), not verbose and '-s' or '')
             if verbose: print cmd
             os.system(cmd)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/make.bat	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,113 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+set SPHINXBUILD=sphinx-build
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html      to make standalone HTML files
+	echo.  dirhtml   to make HTML files named index.html in directories
+	echo.  pickle    to make pickle files
+	echo.  json      to make JSON files
+	echo.  htmlhelp  to make HTML files and a HTML help project
+	echo.  qthelp    to make HTML files and a qthelp project
+	echo.  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  changes   to make an overview over all changed/added/deprecated items
+	echo.  linkcheck to check all external links for integrity
+	echo.  doctest   to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\reportlab.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\reportlab.ghc
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+:end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/_static/default.css	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,995 @@
+/*
+
+ReportLab website stylesheet
+
+Reorder into
+
+1. reset
+
+2. standard styles, which should appear normally in the main content div
+
+3. named divs for layout, with clear explanation of where they go
+
+
+4. everything else that's left over. (this will be most of it at first and
+we can work through seeing where each item is used)
+
+
+
+*/
+
+html, body {
+    margin: 0 0 1px;
+    padding: 0;
+}
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, th, td {
+	margin:0;
+	padding:0;
+	font-family:"Trebuchet MS",Verdana,sans-serif;
+	font-weight:normal;
+	font-size: 13px;
+	color: #444444;
+	}
+body{
+	background: #43505d;
+	}
+a {
+	font-weight:bold;
+	/* text-decoration: underline; */
+	}
+A:link.menu2{
+	color:white;
+	text-decoration:none;
+	}
+A:visited.menu2{
+	color:white;
+	text-decoration:none;
+	}
+A:active.menu2{
+	color:white;
+	}
+A:hover.menu2{
+	color:white;
+	}
+A:link{
+	color:#00337f;
+	text-decoration:none;
+/*	text-decoration: underline;*/
+	}
+A:visited{
+	color:#00337f;
+	text-decoration:none;
+	}
+A:active{
+	color:#ca002a;
+	}
+A:hover{
+	color:#ca002a;
+	}
+a.txt-green {
+	color:#00800d;
+}
+a.txt-green:link {
+	color:#00800d;
+}
+a.txt-green:active {
+	color:#00800d;
+}
+a.txt-green:visited {
+	color:#00800d;
+}
+ul{
+	margin: 5px 0;
+	list-style-type: square;
+	/*#list-style-position: inside;*/
+	}
+	
+ol{
+	list-style-type:decimal;
+	}	
+	
+	
+/*ul.sidemenu {*/
+/*	padding-top: 0;*/
+/*}*/
+	
+li.sidemenu {
+    margin-left:15px;
+    padding: 2px 0;
+	/*text-indent: -1em;*/
+    /*#overflow:hidden;*/
+    /*#list-style: none; url('http://www.reportlab.com/media/level2/menu_arrow_bg.gif');*/
+}
+
+li.mlevel1 {
+    width:150px;
+    margin-left:4px;
+    /*#padding: 10px 0;*/
+    /*#background:url('http://www.reportlab.com/media/level2/menu_bar_bg.gif') repeat-x;*/
+    /*#list-style: none; url('http://www.reportlab.com/media/level2/menu_arrow_bg.gif');*/
+}
+
+li.selected {
+	color: #ca002a;
+	font-weight: bold;
+}
+pre, .dp-highlighter li { /* Javascript code syntax highlighter */
+    font-family: monospace;
+    font-size:11px;
+    }
+
+.blue{background-color:#00337f}
+.red{background-color:#ca002a}
+.lightblue{background-color:#44ADC7}
+.green{background-color:#9ac083}
+.purple{background-color:#714893}
+.pink{background-color:#c04b95}
+.gray{background-color:#ccc}
+.txt-blue{color:#00337f}
+.txt-red{color:#ca002a}
+.txt-lightblue{color:#44ADC7}
+.txt-green{color:#00800d}
+.txt-purple{color:#714893}
+.txt-pink{color:#c04b95}
+.txt-gray{color:#ccc}
+.txt-white{color:#fff}
+
+.txt-blue>a, .txt-blue>a:link, .txt-blue>a:visited {color:#00337f}
+.txt-red>a, .txt-red>a:link, .txt-red>a:visited {color:#ca002a}
+.txt-lightblue>a, .txt-lightblue>a:link, .txt-lightblue>a:visited {color:#44ADC7}
+.txt-green>a, .txt-green>a:link, .txt-green>a:visited {color:#00800d}
+.txt-purple>a, .txt-purple>a:link, .txt-purple>a:visited {color:#714893}
+.txt-pink>a, .txt-pink>a:link, .txt-pink>a:visited {color:#c04b95}
+.txt-gray>a, .txt-gray>a:link, .txt-gray>a:visited {color:#ccc}
+.txt-white>a, .txt-white>a:link, .txt-white>a:visited {color:#fff}
+
+
+a#logo{
+	display: block;
+	width:182px;
+	height:113px;
+	position:absolute;
+	left:41px;
+	top:23px;
+	}
+a#logo img{
+	border: 0 none;
+	}
+
+
+div#logo{
+	width:182px;
+	height:113px;
+	position:absolute;
+	left:41px;
+	top:23px;
+	background: url('http://www.reportlab.com/media/base/logo_bg.gif') no-repeat;
+	}
+div#topbar{
+	width:566px;
+	height:7px;
+	position:absolute;
+	left:241px;
+	top:77px;
+	background: url('http://www.reportlab.com/media/base/topbar_bg.gif') no-repeat;
+	}
+div#container{
+	position:relative;
+	/*#border:1px none red;*/
+	top:0px;
+	width:926px;
+	margin: 17px auto 17px auto;
+	background: #fdfdfd url('http://www.reportlab.com/media/base/page_bg.jpg') no-repeat 797px -1px;
+	}
+div#content{
+	margin-left:43px;
+	margin-right:43px;
+	margin-bottom:20px;
+	}
+div#footer{
+	margin-left:43px;
+	margin-right:43px;
+	}
+div#footer_slogan{
+    width: 838px;
+    height: 19px;
+    background: #fff url('http://www.reportlab.com/media/base/slogan_img.gif') no-repeat;
+	}
+.footer{
+	font-size:11px;
+	}
+img#login_btn{
+	cursor:pointer;
+	position:absolute;
+	top:10px;
+	right:125px;
+	}
+div#header{
+	height: 150px;
+	}
+	
+#call_us{
+	position:absolute;
+	text-align:right;
+	top:100px;
+	right:125px;
+	font-size:12pt;
+	line-height:25px;
+	}
+ul#menu{
+	padding-top: 0;
+	margin-top: 0;
+	cursor:pointer;
+	position:absolute;
+	top:56px;
+	left:270px;
+	list-style-type:none;
+	}
+li.menu{
+	margin-left: 0;
+	padding-top: 0;
+	width: 95px;
+	line-height:18px;
+	text-align:center;
+	font-size:13px;
+	font-weight:bold;
+	float:left;
+	border-right:2px solid #00337f;
+	}
+li.menu:hover, li.menu:hover>a, li.menu:hover>a:link, li.menu:hover>a:visited, li.menu:hover>a:hover {
+	color: black;
+	
+	}
+li.menu>a {
+    display: block;
+    }
+li.highlight_blue:hover{
+	background: #00337f;
+	}
+li.highlight_lightblue:hover{
+	background: #44ADC7;
+	}
+li.highlight_green:hover{
+	background: #9ac083;
+	}
+li.highlight_purple:hover{
+	background: #714893;
+	}
+li.highlight_pink:hover{
+	background: #c04b95;
+	}
+div#shadow{
+	position:absolute;
+	top:68px;
+	left:163px;
+	background: url('http://www.reportlab.com/media/base/menu_bg.jpg') no-repeat;
+	width:550px;
+	height:48px;
+	}
+div#clear{
+	clear:both;
+	height:10px;
+	}
+.intro{
+	padding:20px;
+	padding-top:30px;
+	font-size:10pt;
+	line-height:14pt;
+	font-weight:normal;
+	text-align:justify;
+	}
+td#slogan{
+	text-align:center;
+	}
+table#footer_table{
+	width:100%;
+	}
+p.title{
+	font-size:13pt;
+	line-height:16pt;
+	color: #00337F;
+	}
+
+p.teaser{
+    font-style: italic;
+    font-weight: bold;
+	}
+
+
+/*	Added by andy. This is all wrong; the base styles should
+cover standard text, and the 'home page' or 'menu' styles.
+should be overridden in each page that needs them, and
+with selectors like '#menu li'.  */
+
+table {
+    /*#border: 1px solid #e5e5e5;*/
+    border-bottom: 0;
+    border-collapse: collapse;
+}
+table th {
+    font-weight: bold;
+    text-align: left;
+    background-color: #e5e5e5;
+    padding: 3px;
+}
+table th.left {
+    width: 12em;
+}
+table td {
+    /*#border-bottom: 1px solid #e5e5e5;*/
+    padding: 3px 8px;
+}
+table#main_content td {
+    /*#border-bottom: 1px solid #e5e5e5;*/
+    padding: 0;
+}
+
+hr {
+    border: 0;
+    border-top: 1px solid #e5e5e5;
+}
+
+#main_content_teaser{
+	font-style:italic;
+    font-weight:bold;
+	font-size:13pt;
+    margin: 15px 0;
+}
+
+#main_content_sub_pages{
+	width:450px;
+	min-height:700px;
+	margin:10px;
+	float:left;
+	/*font-size:16px;*/
+}
+
+#main_content_sub_pages_wide{
+	width:600px;
+	min-height:700px;
+	margin:10px;
+	float:left;
+	/*font-size:16px;*/
+}
+
+
+#content h1 {
+	font-size:20px;
+    margin: 0 0 20px 0;
+	color:#264D90;
+	font-weight:bold;
+	line-height:40px;
+	}
+
+#content h2 {
+	font-size:18px;
+	margin: 25px 0 10px 0;
+	font-weight: bold;
+	color: #264D90;
+	}
+
+#content h3 {
+	font-size:15px;
+    margin: 12px 0 4px 0;
+    font-weight: bold;
+    color: #264D90;
+    }
+#content h4 {
+	font-size:13px;
+    margin: 5px 0 0 0;
+    font-weight: bold;
+    color: #264D90;
+    }
+
+
+#content p {
+    margin: 10px 0;
+    font-size: 13px;
+    }
+li {
+    margin-left: 15px;
+    list-style-position: outside;
+	/*#list-style-type:square;*/
+    }
+ul {
+    margin-left: 10px;
+    list-style-position: outside;
+    }
+
+
+#breadcrumb{
+
+    position: absolute;
+    left: 250px;
+    top: 90px;
+	height: 22px;
+	width: 560px;
+	font-weight:bold;
+
+	/*background:pink;  uncomment to see it*/
+	margin-top:20px;
+	/*border:1px solid #f2f2f2;*/
+	font-size:12px;
+	line-height:24px;
+	text-indent:21px;
+	color:#264D90;
+}
+#divider{
+	height:100%;
+	background:white;
+	border-top:1px solid #E5E5E5;
+	margin-top:10px;
+}
+#nav_bar{
+	float:left;
+	margin-left:0px; 
+	margin-top:16px;
+}
+
+#nav_bar_top{
+	width:180px;
+	height:100%;
+	background:url('http://www.reportlab.com/media/level2/menu_bg.gif') repeat-y;
+	color:white;
+	/*#text-indent:10px;*/
+	color:#264D90;
+	/*#padding-left:10pt;*/
+    padding: 5px 10px;
+}
+#nav_bar_bottom{
+	height:37px;
+	width:200px;
+	background:url('http://www.reportlab.com/media/level2/menu_footer_bg.gif') no-repeat;
+	line-height:37px;
+}
+
+#nav_bar li {
+    /*#margin-left: 8px;*/
+    /*#list-style-position: inside;*/
+	font-size:12px;
+
+	font-weight:bold;
+    }
+#nav_bar ul {
+    margin-left: 5px;
+    list-style-position: inside;
+    }
+
+
+
+#side_div h1 {
+    font-size: 13px;
+    margin: 0 0 12px 25px;
+    color: #000;
+    font-weight:bold;
+    text-align: left;
+}
+
+#side_div a.related_item img {
+    width:100px;
+    height:100px;
+    display:block;
+    margin:0 auto;
+    border: 0;
+    padding-top: 10px;
+}
+#side_div li {
+    list-style: none;
+}
+
+
+table.price_table th  {
+	padding-right:10pt;
+	text-align:right;
+	}
+
+td.tick {
+	text-align:center;
+	}
+
+.notification {
+    border: 2px solid #43505d;
+    padding: 15px;
+    background: #c0c4c9;
+    color: #000;
+    margin: 10px 0;
+}
+
+/* juraj starts here */
+
+#users-navigation {
+	float: right; 
+	height: 22px;
+	margin: 10px 120px 10px 10px;
+	background-image: url(status_left.gif); 
+	background-repeat: no-repeat; 
+	background-position: left; 
+	padding-left:2px;
+	}
+
+#users-navigation div, #users-navigation a{
+	color: white;
+	font-size: 90%;
+	line-height: 22px;
+}
+
+#users-navigation div{
+	float: left; 
+	height: 22px; 
+	background-image: url(status_middle.gif); 
+	padding-left: 3px;
+}
+
+#users-navigation a{
+	padding-right: 3px;
+}
+
+#users-navigation a:hover{
+	text-decoration: underline;
+	}
+
+#users-navigation div.last{
+	background-image: url(status_right.gif); 
+	background-repeat: no-repeat;
+}
+
+#loggedAs{
+	float: right; 
+	margin: 10px  10px 10px;
+	height: 22px; 
+	line-height: 25px; 
+	font-size: 10px;
+}
+
+#messages{
+	margin-left:57px;
+	margin-right:43px;
+	font-size:14px;
+	line-height:24px;
+	text-indent:21px;
+	color:#264D90;
+}
+
+#overlay{
+	display: block;
+	position: absolute;
+	top: 0pt;
+	left: 0pt;
+	z-index: 2;
+	width: 100%;
+	/* background-image:url('overlay.png'); */
+}
+* html #overlay{
+	background-color: #333;
+	back\ground-color: transparent;
+	background-image: url(blank.gif);
+	filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src="overlay.png", sizingMethod="scale");
+}
+
+.overlaydiv{
+	background-color: #ffffff;
+	position: absolute;
+	width: 550px;
+	left: 50%;
+	top: 100px;
+	margin-left: -275px;
+	padding: 10px;
+	z-index: 102;
+	border: 2px solid #000000;
+	display: none;
+}
+
+.overlaydiv div{
+	padding: 10px;
+	width: 500px;
+	margin-left: auto;
+	margin-right: auto;
+	z-index: 102;
+	/* border: 2px solid #00337f; */
+}
+
+.noborder{
+	border: none;
+}
+
+/* :::::::::::: START accesible forms styles :::::::::::: */
+/* color #00337f is reportlab blue */
+
+form.accform li {
+	list-style: none;
+	border: none;
+}
+
+form.accform ul{
+	margin: 0px 0px 0px 0px;
+	padding: 0px 0px 0px 0px;
+	border: none;
+}
+
+form.accform li{
+	margin: 0px 0px 5px 0px;
+	padding: 0px 0px 5px 0px;
+}
+
+
+/* borders */
+
+form.accform textarea, form.accform input[type="text"]{
+	border: 1px solid #CCCCCC;
+	display: inline-block;
+}
+
+/* labels are used for alignement : overide width if needed */
+
+form.accform label{
+	display: block;
+	text-align: left;
+	float: left;
+	width: 80px;
+}
+
+/* fieldset with legend */
+
+form.accform fieldset{
+	border: none;
+	border-top: 2px solid #00337f;
+	padding-top: 10px;
+}
+
+form.accform legend{
+	font-weight: bold;
+	color: #00337f;
+}
+
+/* highlight elements with errors not working in IE6 */
+
+form.accform ul.errorlist + label + input, form.accform ul.errorlist + label + textarea, form.accform ul.errorlist + label + dl + textarea, form.accform ul.errorlist + label + div + textarea, form.accform ul.errorlist + label + select{
+	border: 1px solid #800000;
+}
+
+/* this margin should be same as label width */
+form.accform ul.errorlist li {
+	margin-left: 80px;
+}
+
+/* some help text dd dt styles */
+
+dl.accform {
+	float: right;
+}
+
+dl.accform > dt:first-child{
+	padding-bottom: 5px;
+	color: #00337f;
+}
+
+
+/* :::::::::::: END accesible forms styles  :::::::::::: */
+
+/* :::::::::::: START definition list  :::::::::::: */
+/* inline ; definition type bold */
+
+/* #content #sidebar dl dt, #content #sidebar dl dd { */
+/*     float: left; */
+/*     margin-right: 0.5em; */
+/*     margin-bottom: 0.5em; */
+/* } */
+
+/* #content #sidebar dl dt { */
+/*     clear: left; */
+/*     font-weight: bold; */
+/* } */
+
+
+dl.inline  dt, dl.inline dd {
+    float: left;
+    margin-right: 0.5em;
+    margin-bottom: 0.5em;
+}
+
+dl.inline dd {
+	*float: none;
+}
+
+dl.inline dt{
+    clear: left;
+    font-weight: bold;
+}
+
+/* :::::::::::: END definition list  :::::::::::: */
+
+
+
+/* :::::::::::: START snippets form  :::::::::::: */
+
+#snippetForm label {
+	width: 130px;
+}
+
+#snippetForm ul.errorlist li {
+	margin-left: 100px;
+}
+
+#snippetForm textarea, #snippetForm input[type="text"]{
+	width: 410px
+}
+
+.help{
+	display: none;
+	float: right;
+	width: 270px;
+}
+
+.help textarea{
+	width: 250px !important;
+}
+
+ul.snippets li{
+	padding-bottom: 5px;
+}
+
+/* :::::::::::: END snippets form  :::::::::::: */
+
+/* :::::::::::: START contact us form  :::::::::::: */
+
+#contactusContainer{
+	display: none;
+	background-color: white;
+}
+
+#contactusContainer fieldset{
+	border: none;
+	width: 500px;
+}
+
+#contactusContainer textarea, #contactusContainer input[type="text"]{
+	width: 410px
+}
+
+/* :::::::::::: END contact us form  :::::::::::: */
+
+/* :::::::::::: START footer  :::::::::::: */
+.newFooter{
+	position: relative;
+	clear: both;
+}
+
+hr#topbar{
+	width:566px;
+	height:7px;
+	position:absolute;
+	left:241px;
+	top:77px;
+	}
+
+
+.newFooter hr, hr#topbar{
+	color: #00337f;
+	background-color: #00337f;
+	height: 5px;
+}
+
+.newFooter span{
+	color: red;
+}
+
+.newFooter div{
+	position: absolute;
+	left: 500px;
+	top: -13px;
+	background-color: #ffffff;
+	padding: 5px;
+	color: #00337f;
+	font-weight: bold;
+	font-size: 120%;
+}
+
+/* :::::::::::: END footer  :::::::::::: */
+
+/* :::::::::::: START columns div  :::::::::::: */
+
+div.columns{
+	overflow: auto;
+	width: 100%;
+	padding: 5px 0px;
+}
+
+div.columns br{
+	clear: both;
+	
+}
+
+div.columns > div{
+	float: left;
+	width: 47%;
+	/* padding: 1%; */
+	/* border: 1px solid red; */
+}
+
+div.columns  div + div{
+	float: right;
+	clear: right;
+}
+
+div.columns_80_20 > div{
+	width: 78%;
+}
+
+div.columns_80_20 > div + div{
+	width: 17%
+}
+
+div.columns_70_30 > div{
+	width: 68%;
+}
+
+div.columns_70_30  > div + div{
+	width: 27%
+}
+
+/* :::::::::::: END columns div  :::::::::::: */
+
+/* :::::::::::: START general  :::::::::::: */
+
+.floatright{
+	display: block;
+	float: right;
+}
+
+span.contact{
+	color: #00337f;
+	text-decoration: underline;
+	font-weight: bold;
+	cursor: pointer;
+}
+span.contact:hover{
+	color: #ca002a;
+}
+/* :::::::::::: END general  :::::::::::: */
+
+.center{
+	margin-left:auto; 
+	margin-right:auto;
+	display:block;
+}
+
+.border{
+	border: 1px solid #cccccc;
+	padding: 2px;
+}
+
+
+/* :::::::::::: END general  :::::::::::: */
+
+/* :::::::::::: START prifile  :::::::::::: */
+
+
+#accordion{
+	display: none;
+}
+
+#accordion h3{
+	margin: 0px;
+	padding: 0px;
+}
+
+#accordion div.notification{
+	margin: 0px;
+	padding: 5px;
+}
+
+div.gcbutton{
+	/* height: 48px; */
+	line-height: 40px;
+	width: 200px;
+	overflow-x: hidden;
+}
+	
+div.gcbutton img{
+
+	display: inline;
+}
+
+span.expand{
+	font-weight: normal;
+	font-size: 80%;
+	cursor: pointer;
+	padding-left: 10px;
+	/* display: inline-block; */
+
+}
+
+div.profileSection{
+	border-bottom: 2px solid #00337f;
+	padding: 5px 0px;
+}
+
+/* div.profileSection div.toogle{ */
+/* 	display: nonex; */
+/* } */
+
+/* remove important  later when css is fixed !!! */
+div.profileSection h2{
+	margin: 0px 0px 0px 0px !important;
+	display: inline
+}
+
+div.profileSection p{
+	margin: 5px 0px !important;
+}
+
+
+div.center{
+	width: 600px;
+	margin-left: auto;
+	margin-right: auto;
+}
+div.profileSection div.notification{
+	margin: 0px 0px 0px 0px !important;
+}
+
+div.profileSection div.notification h3{
+	margin: 0px 0px 0px 0px !important;
+}
+
+dl.profile dt{
+	width: 100px;
+
+}
+
+
+.tag_icons {
+    float: right;
+}
+.tag_icons img {
+    border: 0;
+}
+
+/* Fix for Jquery accordion in IE~6 */
+.ui-accordion-content{ zoom: 1; } 
+
+
+
+
+
+/* Sphinx docs */
+div.related, body>div.sphinxsidebar, body>div.document>div.sphinxsidebar, body>div.footer {
+    display: none;
+}
+pre {
+    background:#ECF0F3;
+    border:1px solid #d7e7f3;
+    border-left:0;
+    border-right:0;
+    padding:0.5em;
+    margin:10px 0;
+}
+
+dl {
+    margin-bottom:15px;
+}
+
+tt {
+    background-color:#ECF0F3;
+    font-size:0.95em;
+    padding:0 1px;
+}
+tt.descname {
+    font-size:1.2em;
+    font-weight:bold;
+}
+
+dd {
+    margin-bottom:10px;
+    margin-left:30px;
+    margin-top:3px;
+}
+div.body p, div.body dd, div.body li {
+    line-height:130%;
+}
+a.headerlink {
+    color:#ECF0F3;
+}
+a.headerlink:hover{
+    color:#ca002a;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/_templates/layout.html	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,6 @@
+{% extends "!layout.html" %}
+{% block rootrellink %}
+    <li><a href="http://www.reportlab.com/">ReportLab Homepage</a> &raquo;</li>
+    {{ super() }}
+{% endblock %}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/_templates/page.html	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,35 @@
+{% extends "layout.html" %}
+{% block body %}
+<div id="container">
+    <div id="header">
+        <a href="/"><div id="logo"></div></a>
+        <div id="topbar"></div>
+        <ul id="menu">
+            <li class="menu txt-blue highlight_blue"><a href="/">Home</a></li>
+            <li class="menu txt-lightblue highlight_lightblue"><a href="/solutions/">Solutions</a></li>
+            <li class="menu txt-green highlight_green"><a href="/software/">Software</a></li>
+            <li class="menu txt-purple highlight_purple"><a href="/about/">About Us</a></li>
+            <li class="menu txt-pink highlight_pink" style="border:0px;"><a href="/contact/">Contact</a></li>
+        </ul>
+        <a style="color:white" href="/accounts/login/"><img id="login_btn" src="/media/base/login_btn.gif" alt="Login / Register" style="border: 0" /></a>
+    </div>
+    <div id="content">
+        <div id="breadcrumb">
+        </div>
+        <div id="divider">
+            <div id="nav_bar">
+                <div id="nav_bar_top">
+                    {% block sidebar1 %}{{ sidebar() }}{% endblock %}
+                </div>
+            </div>
+            <div id="main_content_sub_pages_wide">
+                {% block relbar1 %}{{ relbar() }}{% endblock %}
+                {{ body }}
+                {% block relbar2 %}{{ relbar() }}{% endblock %}
+            </div>
+        </div>
+        <div id="clear"></div>
+    </div>
+</div>
+{% endblock %}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/conf.py	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+#
+# reportlab documentation build configuration file, created by
+# sphinx-quickstart on Fri Feb  5 21:41:03 2010.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.append(os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'reportlab'
+copyright = u'2010, Robinson, Becker, Watters and many more'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '2.4'
+# The full version, including alpha/beta/rc tags.
+release = '2.4'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = []
+
+# The reST default role (used for this markup: 'text') to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'reportlabdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'reportlab.tex', u'reportlab Documentation',
+   u'Robinson, Becker, Watters and many more', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/graphics.rst	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,97 @@
+===================================
+Graphics and Charts
+===================================
+
+.. rubric:: Graphics and Charts
+
+
+barcode
+-----------------------
+
+.. automodule:: reportlab.graphics.barcode
+   :members:
+
+
+charts
+-----------------------
+
+.. automodule:: reportlab.graphics.charts
+   :members:
+
+
+renderPDF
+-----------------------
+
+.. automodule:: reportlab.graphics.renderPDF
+   :members:
+
+
+renderPM
+-----------------------
+
+.. automodule:: reportlab.graphics.renderPM
+   :members:
+
+
+renderPS
+-----------------------
+
+.. automodule:: reportlab.graphics.renderPS
+   :members:
+
+
+renderSVG
+-----------------------
+
+.. automodule:: reportlab.graphics.renderSVG
+   :members:
+
+
+renderbase
+-----------------------
+
+.. automodule:: reportlab.graphics.renderbase
+   :members:
+
+
+samples
+-----------------------
+
+.. automodule:: reportlab.graphics.samples
+   :members:
+
+
+shapes
+-----------------------
+
+.. automodule:: reportlab.graphics.shapes
+   :members:
+
+
+testdrawings
+-----------------------
+
+.. automodule:: reportlab.graphics.testdrawings
+   :members:
+
+
+testshapes
+-----------------------
+
+.. automodule:: reportlab.graphics.testshapes
+   :members:
+
+
+widgetbase
+-----------------------
+
+.. automodule:: reportlab.graphics.widgetbase
+   :members:
+
+
+widgets
+-----------------------
+
+.. automodule:: reportlab.graphics.widgets
+   :members:
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/index.rst	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,30 @@
+.. reportlab documentation master file, created by
+   sphinx-quickstart on Fri Feb  5 21:41:03 2010.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+===================================
+ReportLab PDF Toolkit documentation
+===================================
+
+.. rubric:: API references and more for the ReportLab PDF Toolkit
+
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   pdfgen package (reportlab.pdfgen) - the basics <pdfgen.rst>
+   Platypus (reportlab.platypus) - long flowing documents <platypus.rst>
+   Graphics and Charts (reportlab.graphics) <graphics.rst>
+   Library and utilities (reportlab.lib) <lib.rst>
+    
+
+Sphinx is being used for the automated API references.  We have
+always been able to make our own documents in PDF, without using other
+peoples' libraries, and don't want to back away from this. So, the main
+User Guide will remain in PDF format for now.  However, programmers need
+API refs online, and we want to learn Sphinx (and use rst2pdf) in due
+course.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/lib.rst	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,44 @@
+===================================
+ReportLab Library Reference
+===================================
+
+.. rubric:: API main index
+
+This page covers modules within reportlab/lib which may be of
+use to developers.  We have tried only to include things
+we intend people to use; a lot of code in /lib/ is used
+by our package itself, and we reserve the right to change it
+if it's not documented here.
+
+Hint:  review tests and user guide and see what is actually imported
+
+Users definitely need:
+
+* units
+* colors
+* utils
+
+See what else is commonly imported and used in the test scripts!
+
+If in doubt, include it and put a comment so Andy can review.
+
+units
+-----------------------
+
+.. automodule:: reportlab.lib.units
+   :members:
+
+
+colors
+--------------------
+
+.. automodule:: reportlab.lib.colors
+   :members:
+
+
+utils
+--------------------
+
+.. automodule:: reportlab.lib.utils
+   :members:
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/pdfgen.rst	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,11 @@
+===================================
+package reportlab.pdfgen
+===================================
+
+.. rubric:: The basics
+
+module reportlab.pdfgen.canvas
+------------------------------
+
+.. automodule:: reportlab.pdfgen.canvas
+   :members:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/source/platypus.rst	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,70 @@
+===================================
+Platypus
+===================================
+
+.. rubric:: long flowing documents
+
+
+doctemplate
+-----------------------
+
+.. automodule:: reportlab.platypus.doctemplate
+   :members:
+
+
+paragraph
+-----------------------
+
+.. automodule:: reportlab.platypus.paragraph
+   :members:
+
+
+paraparser
+-----------------------
+
+.. automodule:: reportlab.platypus.paraparser
+   :members:
+
+
+flowables
+-----------------------
+
+.. automodule:: reportlab.platypus.flowables
+   :members:
+
+
+frames
+-----------------------
+
+.. automodule:: reportlab.platypus.frames
+   :members:
+
+
+figures
+-----------------------
+
+.. automodule:: reportlab.platypus.figures
+   :members:
+
+
+tables
+-----------------------
+
+.. automodule:: reportlab.platypus.tables
+   :members:
+
+
+tableofcontents
+-----------------------
+
+.. automodule:: reportlab.platypus.tableofcontents
+   :members:
+
+
+xpreformatted
+-----------------------
+
+.. automodule:: reportlab.platypus.xpreformatted
+   :members:
+
+
--- a/docs/userguide/ch1_intro.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/docs/userguide/ch1_intro.py	Thu Oct 21 10:34:13 2010 +0000
@@ -1,6 +1,6 @@
 #Copyright ReportLab Europe Ltd. 2000-2004
 #see license.txt for license details
-#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch1_intro.py
+__version__ = '$Id$'
 from tools.docco.rl_doc_utils import *
 from reportlab.platypus.tableofcontents import TableOfContents
 from datetime import datetime
@@ -61,7 +61,7 @@
 todo("""
 We need your help to make sure this manual is complete and helpful.
 Please send any feedback to our user mailing list,
-which is signposted from <a href="http://www.reportlab.org/">www.reportlab.org</a>.
+which is signposted from <a href="http://www.reportlab.com/">www.reportlab.com</a>.
 """)
 
 heading2("What is the ReportLab PDF Library?")
@@ -121,9 +121,34 @@
 bullet("""A 'build system' for complex documents with charts, tables
 and text such as management accounts, statistical reports and
 scientific papers """)
-bullet("""Going from XML to PDF in one step!""")
+bullet("""Going from XML to PDF in one step""")
 
 
+heading2("ReportLab's commercial software")
+disc("""
+The ReportLab library forms the foundation of our commercial solution for
+PDF generation, Report Markup Language (RML).  This is available for evaluation
+on our web site with full documentation.   We believe that RML is the fastest
+and easiest way to develop rich PDF workflows.  You work in a markup language
+at a similar level to HTML, using your favorite templating system to populate
+an RML document; then call our rml2pdf API function to generate a PDF.  It's
+what ReportLab staff use to build all of the solutions you can see on reportlab.com.
+Key differences:
+""")
+bullet("""Fully documented with two manuals, a formal specification (the DTD) and extensive self-documenting tests.  (By contrast, we try to make sure the open source documentation isn't wrong, but we don't always keep up with the code)""")
+bullet("""Work in high-level markup rather than constructing graphs of Python objects """)
+bullet("""Requires no Python expertise - your colleagues may thank you after you've left!'""")
+bullet("""Support for vector graphics and inclusion of other PDF documents""")
+bullet("""Many more useful features expressed with a single tag, which would need a lot
+of coding in the open source package""")
+bullet("""Commercial support is included""")
+
+
+disc("""
+We ask open source developers to consider trying out RML where it is appropriate.
+You can register on our site and try out a copy before buying.
+The costs are reasonable and linked to the volume of the project, and the revenue
+helps us spend more time developing this software.""")
 
 
 heading2("What is Python?")
@@ -153,16 +178,59 @@
 """)
 
 heading2("Acknowledgements")
-disc("""Many people have contributed to ReportLab.  We would like to thank
-in particular (in approximately chronological order) Chris Lee, Magnus Lie Hetland,
-Robert Kern, Jeff Bauer (who contributed normalDate.py); Jerome Alet (numerous patches
-and the rlzope demo), Andre Reitz, Max M, Albertas Agejevas, T Blatter, Ron Peleg,
-Gary Poster, Steve Halasz, Andrew Mercer, Paul McNett, Chad Miller, Tim Roberts,
-Jorge Godoy and Benn B.""")
+disc("""Many people have contributed to ReportLab.  We would like to thank in particular 
+(in alphabetical order): 
+Albertas Agejevas, 
+Andre Reitz, 
+Andrew Mercer, 
+Benjamin Dumke,
+Benn B,
+Chad Miller, 
+Chris Lee, 
+Christian Jacobs, 
+Dinu Gherman,
+Eric Johnson, 
+Gary Poster, 
+Germán M. Bravo,
+Hans Brand,
+Henning Vonbargen,
+Hosam Aly,
+Ian Stevens,
+Jeff Bauer,
+Jerome Alet,
+Jerry Casiano,
+Jorge Godoy,
+Keven D Smith,
+Magnus Lie Hetland,
+Marcel Tromp, Ty Sarna
+Marius Gedminas,
+Max M, 
+Michael Egorov,
+Mike Folwell,
+Moshe Wagner,
+Nate Silva,
+Paul McNett, 
+PJACock,
+Publio da Costa Melo,  
+Randolph Bentson,
+Robert Alsina,
+Robert Hölzl,
+Robert Kern,
+Ron Peleg,
+Simon King,
+Steve Halasz, 
+T Blatter,
+Tim Roberts,
+Tomasz Swiderski,
+Volker Haas,
+Yoann Roman, 
+and many more.""")
 
 disc("""Special thanks go to Just van Rossum for his valuable assistance with
 font technicalities.""")
 
+disc("""Moshe Wagner and Hosam Aly deserve a huge thanks for contributing to the RTL patch, which is not yet on thr trunk.""")
+
 disc("""Marius Gedminas deserves a big hand for contributing the work on TrueType fonts and we
 are glad to include these in the toolkit. Finally we thank Michal Kosmulski for the DarkGarden font
 for and Bitstream Inc. for the Vera fonts.""")
@@ -170,28 +238,27 @@
 heading2("Installation and Setup")
 
 heading3("A note on available versions")
-disc("""The latest version of the ReportLab library can be found at
-^http://www.reportlab.org/downloads.html^.  Older versions can be found at ^http://www.reportlab.com/ftp/^.
+disc("""Our website ^http://www.reportlab.com/^ will always have up-to-date
+information on setups and installations. The latest version of the ReportLab library can be found at
+^http://www.reportlab.com/software/opensource/rl-toolkit/download/^.  Older versions can be found at ^http://www.reportlab.com/ftp/^.
   Each successive version is stored in both zip
 and tgz format, but the contents are identical apart from line endings.
-Versions are numbered:  $ReportLab_1_00.zip$, $ReportLab_1_01.zip$ and so on. The
-latest stable version is also available as just $reportlab.zip$ (or
-$reportlab.tgz$), which is actually a symbolic link to the latest
-numbered version.  Daily snapshots of the trunk are available as
-$current.zip$ or $current.tgz$.
-Finally, from version 2.3 onwards, there is also a Windows installer
-available for Python versions 2.3 - 2.6, named $ReportLab-2.x.win32-py2.x.exe$
+Versions are numbered:  $ReportLab_<major_version>_<minor_version>.zip$, 
+$ReportLab_<major_version>_<minor_version>.tgz$ and so on. 
+The latest stable version is $reportlab2.5$ (.zip or .tgz), 
+Daily snapshots of the trunk are available as
+$reportlab-daily-unix.tar.gz$ or $reportlab-daily-win32.zip$.
+  Finally, from version 2.4 onwards, there is also a Windows installer
+available for Python versions 2.4 - 2.7, named $ReportLab-2.x.win32-py2.x.exe$
 """)
 
-
 heading3("Installation on Windows")
 
-
 restartList()
 
 list("""First, install Python from $http://www.python.org/.$
-Reportlab 2.x works with Python 2.3 upwards but we recommend to use
-the latest stable version of Python 2.5.  
+Reportlab 2.x works with Python 2.4 upwards but we recommend to use
+the latest stable version of Python 2.5 or 2.6.  
 After installing, you should be able to run the
 'Python (command line)' option from the Start Menu.
 """)
@@ -214,14 +281,14 @@
 
 list("""
 If, however, you wish to install from source, download and unzip the archive
-from http://www.reportlab.org/downloads.html and copy the $reportlab$ directory
+from from the downloads page on ^http://www.reportlab.com/^ and copy the $reportlab$ directory
 onto your PythonPath;  You should now be able to go to a Python
 command line interpreter and type $import reportlab$ without getting
 an error message.
 """)
 
 list("""Next, Download the zip file of precompiled DLLs for your Python version from
-the bottom of the ^http://www.reportlab.org/downloads.html^ downloads page, and unzip
+the bottom of the downloads page on ^http://www.reportlab.com/^, and unzip
 them into ^C:\Python2x\lib\site-packages^ (or its equivalent for other Python versions
 """)
 
@@ -232,7 +299,7 @@
 
 list("""
 Finally, we recommend you download and run the script ^rl_check.py^ from
-^http://www.reportlab.org/ftp/^. This will health-check all the above
+^http://www.reportlab.com/ftp/^. This will health-check all the above
 steps and warn you if anything is missing or mismatched.""")
 
 heading3("Installation instructions for Unix")
@@ -253,7 +320,7 @@
 """)
 
 list("""
-Download the latest ReportLab.tgz from the download page on http://www.reportlab.org.
+Download the latest ReportLab.tgz from the download page on http://www.reportlab.com.
 """)
 
 list("""
@@ -287,9 +354,10 @@
 We have an open standard for report objects, so if you have written a nice
 chart or table class, why not contribute it?""")
 
-bullet("""Demonstrations and Case Studies: If you have produced some nice
-output, send it to us (with or without scripts).  If ReportLab solved a
-problem for you at work, write a little 'case study' and send it in.
+bullet("""Snippets and Case Studies: If you have produced some nice
+output, register online on ^http://www.reportlab.com^ and submit a snippet
+of your output (with or without scripts).  If ReportLab solved a
+problem for you at work, write a little 'case study' and submit it.
 And if your web site uses our tools to make reports, let us link to it.
 We will be happy to display your work (and credit it with your name
 and company) on our site!""")
@@ -340,15 +408,15 @@
 """)
 
 
-bullet("""<b>Introductory Material on Python.  </b>
-A list of tutorials on the Python.org web site.
-$http://www.python.org/doc/Intros.html$
+bullet("""<b>Python Documentation.  </b>
+A list of documentation on the Python.org web site.
+$http://www.python.org/doc/$
 """)
 
 
 bullet("""<b>Python Tutorial.  </b>
-The official Python Tutorial by Guido van Rossum (edited by Fred L. Drake, Jr.)
-$http://www.python.org/doc/tut/$
+The official Python Tutorial , originally written by Guido van Rossum himself.
+$http://docs.python.org/tutorial/$
 """)
 
 
@@ -382,32 +450,13 @@
 indent0_style = styles['Indent0']
 indent1_style = styles['Indent1']
 
-heading2("What's New in ReportLab 2.0")
-disc("""
-Many new features have been added, foremost amongst which is the support
-for unicode. This page documents what has changed since version 1.20.""")
-
-disc("""
-Adding full unicode support meant that we had to break backwards-compatibility,
-so old code written for ReportLab 1 will sometimes need changes before it will
-run correctly with ReportLab 2. Now that we have made the clean break to
-introduce this important new feature, we intend to keep the API
-backwards-compatible throughout the 2.* series.
-""")
-heading3("Goals for the 2.x series")
-disc("""
-The main rationale for 2.0 was an incompatible change at the character level:
+heading2("Goals for the 2.x series")
+disc("""The main rationale for 2.0 was an incompatible change at the character level:
 to properly support Unicode input. Now that it's out we will maintain compatibility
 with 2.0. There are no pressing feature wishlists and new features will be driven,
 as always, by contributions and the demands of projects.""")
 
-disc("""
-Our 1.x code base is still Python 2.1 compatible. The new version lets us move forwards
-with a baseline of Python 2.4 (2.3 will work too, for the moment, but we don't promise
-that going forwards) so we can use newer language features freely in our development.""")
-
-disc("""
-One area where we do want to make progress from release to release is with documentation
+disc("""One area where we do want to make progress from release to release is with documentation
 and installability. We'll be looking into better support for distutils, setuptools,
 eggs and so on; and into better examples and tools to help people learn what's in the
 (substantial) code base.""")
@@ -417,176 +466,44 @@
 target dates.
 """)
 
-heading3("Contributions")
-disc("""Thanks to everybody who has contributed to the open-source toolkit in the run-up
-to the 2.0 release, whether by reporting bugs, sending patches, or contributing to the
-reportlab-users mailing list. Thanks especially to the following people, who contributed
-code that has gone into 2.0: Andre Reitz, Max M, Albertas Agejevas, T Blatter, Ron Peleg,
-Gary Poster, Steve Halasz, Andrew Mercer, Paul McNett, Chad Miller.
-""")
-todo("""If we missed you, please let us know!""")
-
-heading3("Unicode support")
-disc("""
-This is the Big One, and the reason some apps may break. You must now pass in text either
-in UTF-8 or as unicode string objects. The library will handle everything to do with output
-encoding. There is more information on this below.
-Since this is the biggest change, we'll start by reviewing how it worked in the past.""")
-
-disc("""
-In ReportLab 1.x, any string input you passed to our APIs was supposed to be in the same
-encoding as the font you selected for output. If using the default fonts in Acrobat Reader
-(Helvetica/Times/Courier), you would have implicitly used WinAnsi encoding, which is almost
-exactly the same as Latin-1. However, if using TrueType fonts, you would have been using UTF-8.""")
-
-disc("""For Asian fonts, you had a wide choice of encodings but had to specify which one
-(e.g Shift-JIS or EUC for Japanese). This state of affairs meant that you had
-to make sure that every piece of text input was in the same encoding as the font used
-to display it.""")
-
-
+heading2("What's New in ReportLab 2.4")
+disc("""Many new features have been added and numerous bugs have been fixed, a big
+thanks goes to the community for their help in reporting bugs and providing patches. 
+Thanks to everybody who has contributed to the open-source toolkit in the run-up to the 2.4 release, 
+whether by reporting bugs, sending patches, or contributing to the reportlab-users mailing list. 
+Thanks especially to the following people: PJACock, Hans Brand, Ian Stevens, Yoann Roman, Hosam Aly 
+Randolph Bentson, Volker Haas, Simon King, Henning Vonbargen, Michael Egorov, Mike Folwell and 
+Roberto Alsina.  
+This page documents what has changed since version 2.3.""")
 
-disc("""Input text encoding is UTF-8 or Python Unicode strings""")
-disc("""
-Any text you pass to a canvas API (drawString etc.), Paragraph or other flowable
-constructor, into a table cell, or as an attribute of a graphic (e.g. chart.title.text),
-is supposed to be unicode. If you use a traditional Python string, it is assumed to be UTF-8.
-If you pass a Unicode object, we know it's unicode.""", style=indent1_style)
-
-disc("""Font encodings""")
-disc("""
-Fonts still work in different ways, and the built-in ones will still use WinAnsi or MacRoman
-internally while TrueType will use UTF-8. However, the library hides this from you; it converts
-as it writes out the PDF file. As before, it's still your job to make sure the font you use has
-the characters you need, or you may get either a traceback or a visible error character.""",style=indent1_style)
-
-disc("""Asian CID fonts""")
-disc("""
-You no longer need to specify the encoding for the built-in Asian fonts, just the face name.
-ReportLab knows about the standard fonts in Adobe's Asian Language Packs
-""", style=indent1_style)
-
-disc("""Asian Truetype fonts""")
-disc("""
-The standard Truetype fonts differ slightly for Asian languages (e.g msmincho.ttc).
-These can now be read and used, albeit somewhat inefficiently. 
-""", style=indent1_style)
-
-disc("""Asian word wrapping""")
-disc("""
-Previously we could display strings in Asian languages, but could not properly
-wrap paragraphs as there are no gaps between the words. We now have a basic word wrapping
-algorithm.
-""", style=indent1_style)
-
-disc("""unichar tag""")
-disc("""
-A convenience tag, &lt;unichar/&gt; has also been added. You can now do <unichar code="0xfc"/>
-or &lt;unichar name='LATIN SMALL LETTER U WITH DIAERESIS'/&gt; and
-get a lowercase u umlaut. Names should be those in the Unicode Character Database.
-""", style=indent1_style)
+disc('Reportlab 2.4 is installable with easy_install. You must have installed a compatible C compiler and the dependencies such as Freetype and PIL.')
 
-disc("""Accents, greeks and symbols""")
-disc("""
-The correct way to refer to all non-ASCII characters is to use their unicode representation.
-This can be literal Unicode or UTF-8. Special symbols and Greek letters (collectively, "greeks")
-inserted in paragraphs using the greek tag (e.g. &lt;greek&gt;lambda&lt;/greek&gt;) or using the entity
-references (e.g. &lambda;) are now processed in a different way than in version 1.""", style=indent1_style)
-disc("""
-Previously, these were always rendered using the Zapf Dingbats font. Now they are always output
-in the font you specified, unless that font does not support that character. If the font does
-not support the character, and the font you specified was an Adobe Type 1 font, Zapf Dingbats
-is used as a fallback. However, at present there is no fallback in the case of TTF fonts.
-Note that this means that documents that contain greeks and specify a TTF font may need
-changing to explicitly specify the font to use for the greek character, or you will see a black
-square in place of that character when you view your PDF output in Acrobat Reader.
-""", style=indent1_style)
-
-# Other New Features Section #######################
-heading3("Other New Features")
-disc("""PDF""")
-disc("""Improved low-level annotation support for PDF "free text annotations"
-""", style=indent0_style)
-disc("""FreeTextAnnotation allows showing and hiding of an arbitrary PDF "form"
-(reusable chunk of PDF content) depending on whether the document is printed or
-viewed on-screen, or depending on whether the mouse is hovered over the content, etc.
-""", style=indent1_style)
-
-disc("""TTC font collection files are now readable"
-""", style=indent0_style)
-disc("""ReportLab now supports using TTF fonts packaged in .TTC files""", style=indent1_style)
-
-disc("""East Asian font support (CID and TTF)""", style=indent0_style)
-disc("""You no longer need to specify the encoding for the built-in Asian fonts,
-just the face name. ReportLab knows about the standard fonts in Adobe's Asian Language Packs.
-""", style=indent1_style)
-
-disc("""Native support for JPEG CMYK images""", style=indent0_style)
-disc("""ReportLab now takes advantage of PDF's native JPEG CMYK image support,
-so that JPEG CMYK images are no longer (lossily) converted to RGB format before including
-them in PDF.""", style=indent1_style)
-
+heading4('PDF')
+bullet("""Canvas automatic cropmarks.""")
+bullet("""RGB alpha colours - colours can now be transparent with an alpha value.""")
+bullet("""CMYK overPrint - physical colour mix in the printer - similar to RGB alpha but
+ used in professional printing.""")
+bullet("""Colours module has a fade function that returns a list of different shades made
+ up of one base colour.""")
+bullet("""Unicode font file names are now accepted.""")
+bullet("""Lots of improvements and verbosity to error messages and the way they are handled. 
+Font size can now be specified in pixels.""")
 
-disc("""Platypus""")
-disc("""Link support in paragraphs""", style=indent0_style)
-disc("""
-Platypus paragraphs can now contain link elements, which support both internal links
-to the same PDF document, links to other local PDF documents, and URL links to pages on
-the web. Some examples:""", style=indent1_style) 
-disc("""Web links:""", style=indent1_style)
-disc("""&lt;link href="http://www.reportlab.com/"&gt;ReportLab&lt;link&gt;""", style=styles['Link'])
-
-disc("""Internal link to current PDF document:""", style=indent1_style)
-disc("""&lt;link href="summary"&gt;ReportLab&lt;link&gt;""", style=styles['Link'])
-
-disc("""External link to a PDF document on the local filesystem:""", style=indent1_style)
-disc("""&lt;link href="pdf:C:/john/report.pdf"&gt;ReportLab&lt;link&gt;""", style=styles['Link'])
-
-disc("""Improved wrapping support""", style=indent0_style)
-disc("""Support for wrapping arbitrary sequence of flowables around an image, using
-reportlab.platypus.flowables.ImageAndFlowables (similar to ParagraphAndImage)."""
-,style=indent1_style)
+heading4('Platypus')
+bullet("""Added support for heading styles h4-h6.""")
+bullet("""Improved support for onDraw and SimpleIndex.""")
+bullet("""Add support for index tableStyle.""")
+bullet("""Added an alphabetic grouping indexing class.""")
+bullet("""Added support for multi-level and alphabetical indexes.""")
+bullet("""Added support for an unlimited number of TOC levels with default styles.""")
+bullet("""Index entries can now be clickable.""")
 
-disc("""KeepInFrame""", style=indent0_style)
-disc("""Sometimes the length of a piece of text you'd like to include in a fixed piece
-of page "real estate" is not guaranteed to be constrained to a fixed maximum length.
-In these cases, KeepInFrame allows you to specify an appropriate action to take when
-the text is too long for the space allocated for it. In particular, it can shrink the text
-to fit, mask (truncate) overflowing text, allow the text to overflow into the rest of the document,
-or raise an error.""",style=indent1_style)
-
-
-disc("""Improved convenience features for inserting unicode symbols and other characters
-""", style=indent0_style)
-disc("""<unichar/> lets you conveniently insert unicode characters using the standard long name
-or code point. Characters inserted with the &lt;greek&gt; tags (e.g. <greek>lambda</greek>) or corresponding
-entity references (e.g. &lambda;) support arbitrary fonts (rather than only Zapf Dingbats).""",style=indent1_style)
-
-disc("""Improvements to Legending""", style=indent0_style)
-disc("""Instead of manual placement, there is now a attachment point (N, S, E, W, etc.), so that
-the legend is always automatically positioned correctly relative to the chart. Swatches (the small
-sample squares of colour / pattern fill sometimes displayed in the legend) can now be automatically
-created from the graph data. Legends can now have automatically-computed totals (useful for
-financial applications).""",style=indent1_style)
-
-disc("""More and better ways to place piechart labels""", style=indent0_style)
-disc("""New smart algorithms for automatic pie chart label positioning have been added.
-You can now produce nice-looking labels without manual positioning even for awkward cases in
-big runs of charts.""",style=indent1_style)
-
-disc("""Adjustable piechart slice ordering""", style=indent0_style)
-disc("""For example. pie charts with lots of small slices can be configured to alternate thin and
-thick slices to help the lagel placememt algorithm work better.""",style=indent1_style)
-disc("""Improved spiderplots""", style=indent0_style)
-
+heading4('Graphics')
+bullet("""Chart axes values can be reversible.""")
+bullet("""Labels on chart axes can now be drawn above or below the axes (hi or low).""")
+bullet("""A per swatch callout is now allowed in the legend.""")
+bullet("""A new anchoring mode for string 'numeric' that align numerical strings by their decimal place.""")
+bullet("""Drawing has a resized method now to change the size dynamically.""")
 
 # Noteworthy bug fixes Section #######################
-heading3("Noteworthy bug fixes")
-disc("""Fixes to TTF splitting (patch from Albertas Agejevas)""")
-disc("""This affected some documents using font subsetting""", style=indent0_style)
-
-disc("""Tables with spans improved splitting""")
-disc("""Splitting of tables across pages did not work correctly when the table had
-row/column spans""", style=indent0_style)
-
-disc("""Fix runtime error affecting keepWithNext""")
+#heading3("Noteworthy bug fixes")
--- a/docs/userguide/ch2_graphics.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/docs/userguide/ch2_graphics.py	Thu Oct 21 10:34:13 2010 +0000
@@ -600,46 +600,119 @@
 """)
 
 heading2("Colors")
+disc("""
+There are generally two types of colors used in PDF depending on the media where
+the PDF will be used. The most commonly known screen colors model RGB can be used
+in PDF, however in professional printing another color model CMYK is mainly used 
+which gives more control over how inks are applied to paper. More on these color
+models below.
+""")
+
+heading3("RGB Colors")
+disc("""
+The $RGB$ or additive color representation follows the way a computer
+screen adds different levels of the red, green, and blue light to make
+any color in between, where white is formed by turning all three lights on full
+($1,1,1$).
+""")
 
 disc("""
-There are four ways to specify colors in $pdfgen$: by name (using the $color$
-module, by red/green/blue (additive, $RGB$) value,
-by cyan/magenta/yellow/darkness (subtractive, $CMYK$), or by gray level.
+There are three ways to specify RGB colors in $pdfgen$: by name (using the $color$
+module, by red/green/blue (additive, $RGB$) value, or by gray level.
 The $colors$ function below exercises each of the four methods.
 """)
 
-eg(examples.testcolors)
+eg(examples.testRGBcolors)
+illust(examples.colorsRGB, "RGB Color Models")
+
+heading4("RGB Color Transparency")
+
+disc("""
+Objects may be painted over other objects to good effect in $pdfgen$.  Generally
+There are two modes of handling objects that overlap in space, the default objects 
+in the top layer will hide any part of other objects that falls underneath it. If you
+need transparency you got two choices:
+""")
 
 disc("""
-The $RGB$ or additive color specification follows the way a computer
-screen adds different levels of the red, green, or blue light to make
-any color, where white is formed by turning all three lights on full
-$(1,1,1)$.""")
+1. If your document is intended to be printed in a professional way and you are 
+working in CMYK color space then you can use overPrint. In overPrinting the colors
+physically mix in the printer and thus a new color is obtained. By default a 
+knockout will be applied and only top object appears. Read the CMYK section
+if this is what you intend to use.
+""")
+
+disc("""
+2. If your document is intended for screen output and you are using RGB colors 
+then you can set an alpha value, where alpha is the opacity value of the color.
+The default alpha value is $1$ (fully opaque) and you can use any real number
+value in the range 0-1.
+""")
 
-disc("""The $CMYK$ or subtractive method follows the way a printer
+disc("""
+Alpha transparency ($alpha$) is similar to overprint but works in RGB color space
+this example below demonstrates the alpha funtionality. Refer to our website
+http://www.reportlab.com/snippets/ and look for snippets of overPrint and alpha
+to see the code that generates the graph below.
+""")
+
+eg(examples.testalpha)
+illust(examples.alpha, "Alpha example")
+
+heading3("CMYK Colors")
+disc("""
+The $CMYK$ or subtractive method follows the way a printer
 mixes three pigments (cyan, magenta, and yellow) to form colors.
 Because mixing chemicals is more difficult than combining light there
 is a fourth parameter for darkness.  For example a chemical
 combination of the $CMY$ pigments generally never makes a perfect
 black -- instead producing a muddy color -- so, to get black printers
-don't use the $CMY$ pigments but use a direct black ink.  Because
+don not use the $CMY$ pigments but use a direct black ink.  Because
 $CMYK$ maps more directly to the way printer hardware works it may
 be the case that colors specified in $CMYK$ will provide better fidelity
 and better control when printed.
 """)
 
-illust(examples.colors, "Color Models")
+disc("""
+There are two ways of representing CMYK Color: each color can be represented either
+by a real value between 0 and 1, or integer value between 0 and 100. Depending
+on your preference you can either use CMYKColor (for real values) or PCMYKColor ( for 
+integer values). 0 means 'no ink', so printing on white papers gives you white. 1 (or 100
+if you use PCMYKColor) means 'the maximum amount of ink'. e.g. CMYKColor(0,0,0,1) is black,
+CMYKColor(0,0,0,0) means 'no ink', and CMYKColor(0.5,0,0,0) means 50 percent cyan color.
+""")
+eg(examples.testCMYKcolors)
+illust(examples.colorsCMYK, "CMYK Color Models")
 
-heading2('Painting back to front')
+heading2("Color space checking")
+disc("""The $enforceColorSpace$ argument of the canvas is used to enforce the consistency
+of the colour model used in a document. It accepts these values: CMYK, RGB, SEP, SEP_BLACK,
+SEP_CMYK. 'SEP' refers to named color separations such as Pantone spot colors - these can
+be mixed with CMYK or RGB according to the parameter used.  The default is 'MIXED' which
+allows you to use colors from any color space.  An exception is raised if any colors used
+are not convertible to the specified model, e.g. rgb and cmyk (more information in
+test_pdfgen_general). This approach doesn't check external images included in document.
+""")
+
+heading2("Color Overprinting")
 
 disc("""
-Objects may be painted over other objects to good effect in $pdfgen$.  As
-in painting with oils the object painted last will show up on top.  For
-example, the $spumoni$ function below paints up a base of colors and then
-paints a white text over the base.
+When two CMYK colored objects overlap in printing, then either the object 'on top'
+will knock out the color of the the one underneath it, or the colors of the two
+objects will mix in the overlapped area.
+This behaviour can be set using the property $overPrint$.
 """)
 
-eg(examples.testspumoni)
+disc("""
+The $overPrint$ function will cause ovelapping areas of color to mix. In the example
+below, the colors of the rectangles on the left should appear mixed where they overlap
+- If you can't see this effect then you may need to enable the 'overprint preview'
+option in your PDF viewing software.  Some PDF viewers such as $evince$ do not
+support overPrint; however Adobe Acrobat Reader does support it.
+""")
+illust(examples.overPrint, "overPrint example")
+
+heading3("Other Object Order of Printing Examples")
 
 disc("""
 The word "SPUMONI" is painted in white over the colored rectangles,
@@ -647,6 +720,7 @@
 the word.
 """)
 
+eg(examples.testspumoni)
 illust(examples.spumoni, "Painting over colors")
 
 disc("""
@@ -670,7 +744,6 @@
 """)
 illust(examples.spumoni2, "building up a drawing in layers")
 
-
 heading2('Standard fonts and text objects')
 
 disc("""
--- a/docs/userguide/ch5_paragraphs.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/docs/userguide/ch5_paragraphs.py	Thu Oct 21 10:34:13 2010 +0000
@@ -190,7 +190,7 @@
     S={}
     for k, v in A.items():
         a = v[0]
-        if not S.has_key(a):
+        if a not in S:
             S[a] = k
         else:
             S[a] = "%s, %s" %(S[a],k)
@@ -204,7 +204,7 @@
     rows=len(D)*[None]
     return D,cols,rows
 
-t=apply(Table,getAttrs(_paraAttrMap))
+t=Table(*getAttrs(_paraAttrMap))
 t.setStyle(TableStyle([
             ('FONT',(0,0),(-1,1),'Times-Bold',10,12),
             ('FONT',(0,1),(-1,-1),'Courier',8,8),
@@ -356,7 +356,7 @@
 characters but we suggest first trying the Unicode bullet ($&bull;$), which may
 be written as $&amp;bull;$,  $&amp;#x2022;$ or (in utf8) $\\xe2\\x80\\xa2$):""")
 
-t=apply(Table,getAttrs(_bulletAttrMap))
+t=Table(*getAttrs(_bulletAttrMap))
 t.setStyle([
             ('FONT',(0,0),(-1,1),'Times-Bold',10,12),
             ('FONT',(0,1),(-1,-1),'Courier',8,8),
--- a/docs/userguide/ch6_tables.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/docs/userguide/ch6_tables.py	Thu Oct 21 10:34:13 2010 +0000
@@ -563,7 +563,7 @@
 
     def __init__(self, filename, **kw):
         self.allowSplitting = 0
-        apply(BaseDocTemplate.__init__, (self, filename), kw)
+        BaseDocTemplate.__init__(self, filename, **kw)
         template = PageTemplate('normal', [Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')])
         self.addPageTemplates(template)
 
@@ -654,4 +654,4 @@
 
 disc("""
 This indexes the terms "comma (,)", "," and "...".
-""")
\ No newline at end of file
+""")
--- a/ez_setup.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/ez_setup.py	Thu Oct 21 10:34:13 2010 +0000
@@ -9,7 +9,7 @@
 
 If you want to require a specific version of setuptools, set a download
 mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
+the appropriate options to "use_setuptools()".
 
 This file can also be run as a script to install or upgrade setuptools.
 """
@@ -71,13 +71,13 @@
 ):
     """Automatically find/download setuptools and make it available on sys.path
 
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end with
-    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
-    it is not already available.  If `download_delay` is specified, it should
+    "version" should be a valid setuptools version number that is available
+    as an egg for download under the "download_base" URL (which should end with
+    a '/').  "to_dir" is the directory where setuptools will be downloaded, if
+    it is not already available.  If "download_delay" is specified, it should
     be the number of seconds that will be paused before initiating a download,
     should one be required.  If an older version of setuptools is installed,
-    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    this routine will print a message to "sys.stderr" and raise SystemExit in
     an attempt to abort the calling script.
     """
     was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
@@ -112,10 +112,10 @@
 ):
     """Download setuptools from a specified location and return its filename
 
-    `version` should be a valid setuptools version number that is available
-    as an egg for download under the `download_base` URL (which should end
-    with a '/'). `to_dir` is the directory where the egg will be downloaded.
-    `delay` is the number of seconds to pause before an actual download attempt.
+    "version" should be a valid setuptools version number that is available
+    as an egg for download under the "download_base" URL (which should end
+    with a '/'). "to_dir" is the directory where the egg will be downloaded.
+    "delay" is the number of seconds to pause before an actual download attempt.
     """
     import urllib2, shutil
     egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
--- a/setup.cfg	Mon Nov 23 13:27:57 2009 +0000
+++ b/setup.cfg	Thu Oct 21 10:34:13 2010 +0000
@@ -1,5 +1,4 @@
 #This is normally only used for win32
 [FREETYPE]
-#lib=path to your freetype2 .lib file  eg C:\Python\devel\freetype-2.1.5\objs\freetype214.lib
-#libdir=path the the containing folder; leave empty uses dirname(of the lib)
-#incdir=path to the include files for your setup or it uses ../include from the libdir
+lib=c:\devel\libs_x86\freetype.lib
+inc=C:\devel\freetype-2.3.12\include
--- a/setup.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/setup.py	Thu Oct 21 10:34:13 2010 +0000
@@ -6,7 +6,7 @@
 pjoin = os.path.join
 abspath = os.path.abspath
 isfile = os.path.isfile
-isdir = os.path.isfile
+isdir = os.path.isdir
 dirname = os.path.dirname
 if __name__=='__main__':
     pkgDir=dirname(sys.argv[0])
@@ -197,8 +197,50 @@
         'fonts/VeraBd.ttf',
         'fonts/VeraBI.ttf',
         'fonts/VeraIt.ttf',
+        'fonts/_abi____.pfb',
+        'fonts/_ab_____.pfb',
+        'fonts/_ai_____.pfb',
+        'fonts/_a______.pfb',
+        'fonts/cobo____.pfb',
+        'fonts/cob_____.pfb',
+        'fonts/com_____.pfb',
+        'fonts/coo_____.pfb',
+        'fonts/_ebi____.pfb',
+        'fonts/_eb_____.pfb',
+        'fonts/_ei_____.pfb',
+        'fonts/_er_____.pfb',
+        'fonts/Sy______.pfb',
+        'fonts/Zd______.pfb',
+        'fonts/Zx______.pfb',
+        'fonts/Zy______.pfb',
         ]
 
+def get_fonts(PACKAGE_DIR, reportlab_files):
+    import sys, os, os.path, urllib2, zipfile, StringIO
+    rl_dir = PACKAGE_DIR['reportlab']
+    if not [x for x in reportlab_files if not os.path.isfile(pjoin(rl_dir,x))]:
+        infoline("Standard T1 font curves already downloaded")
+        return
+    try:
+        infoline("Downloading standard T1 font curves")
+
+        remotehandle = urllib2.urlopen("http://www.reportlab.com/ftp/fonts/pfbfer.zip")
+        zipdata = StringIO.StringIO(remotehandle.read())
+        remotehandle.close()
+        archive = zipfile.ZipFile(zipdata)
+        dst = pjoin(rl_dir, 'fonts')
+
+        for name in archive.namelist():
+            if not name.endswith('/'):
+                outfile = open(os.path.join(dst, name), 'wb')
+                outfile.write(archive.read(name))
+                outfile.close()
+        xitmsg = "Finished download of standard T1 font curves"
+    except:
+        xitmsg = "Failed to download standard T1 font curves"
+    reportlab_files = [x for x in reportlab_files if os.path.isfile(pjoin(rl_dir,x))]
+    infoline(xitmsg)
+
 def main():
     #test to see if we've a special command
     if 'tests' in sys.argv or 'tests-preinstall' in sys.argv:
@@ -215,7 +257,6 @@
 
     SPECIAL_PACKAGE_DATA = {}
     RL_ACCEL = _find_rl_ccode('rl_accel','_rl_accel.c')
-    LIBS = []
     LIBRARIES=[]
     EXT_MODULES = []
     if not RL_ACCEL:
@@ -236,14 +277,14 @@
                                 include_dirs=[],
                             define_macros=[],
                             library_dirs=[],
-                            libraries=LIBS, # libraries to link against
+                            libraries=[], # libraries to link against
                             ),
                     Extension( 'sgmlop',
                             [pjoin(RL_ACCEL,'sgmlop.c')],
                             include_dirs=[],
                             define_macros=[],
                             library_dirs=[],
-                            libraries=LIBS, # libraries to link against
+                            libraries=[], # libraries to link against
                             ),
                     Extension( 'pyHnj',
                             [pjoin(RL_ACCEL,'pyHnjmodule.c'),
@@ -252,11 +293,10 @@
                             include_dirs=[],
                             define_macros=[],
                             library_dirs=[],
-                            libraries=LIBS, # libraries to link against
+                            libraries=[], # libraries to link against
                             ),
                     ]
     RENDERPM = _find_rl_ccode('renderPM','_renderPM.c')
-    LIBS = []
     if not RENDERPM:
         infoline( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
         infoline( '!No rl_accel code found, you can obtain it at     !')
@@ -267,6 +307,7 @@
         infoline( '#Attempting install of _renderPM')
         infoline( '#extensions from %r'%RENDERPM)
         LIBART_DIR=pjoin(RENDERPM,'libart_lgpl')
+        GT1_DIR=pjoin(RENDERPM,'gt1')
         MACROS=[('ROBIN_DEBUG',None)]
         MACROS=[]
         def libart_version():
@@ -279,25 +320,55 @@
                     if len(D)==3: break
             return (sys.platform == 'win32' and '\\"%s\\"' or '"%s"') % '.'.join(map(lambda k,D=D: D.get(k,'?'),K))
         LIBART_VERSION = libart_version()
-        SOURCES=[pjoin(RENDERPM,'_renderPM.c')]
-        LIBART_SRCS=glob.glob(pjoin(LIBART_DIR, 'art_*.c'))
-        GT1_DIR=pjoin(RENDERPM,'gt1')
-        LIBS = []       #assume empty libraries list
+        SOURCES=[pjoin(RENDERPM,'_renderPM.c'),
+                    pjoin(LIBART_DIR,'art_vpath_bpath.c'),
+                    pjoin(LIBART_DIR,'art_rgb_pixbuf_affine.c'),
+                    pjoin(LIBART_DIR,'art_rgb_svp.c'),
+                    pjoin(LIBART_DIR,'art_svp.c'),
+                    pjoin(LIBART_DIR,'art_svp_vpath.c'),
+                    pjoin(LIBART_DIR,'art_svp_vpath_stroke.c'),
+                    pjoin(LIBART_DIR,'art_svp_ops.c'),
+                    pjoin(LIBART_DIR,'art_vpath.c'),
+                    pjoin(LIBART_DIR,'art_vpath_dash.c'),
+                    pjoin(LIBART_DIR,'art_affine.c'),
+                    pjoin(LIBART_DIR,'art_rect.c'),
+                    pjoin(LIBART_DIR,'art_rgb_affine.c'),
+                    pjoin(LIBART_DIR,'art_rgb_affine_private.c'),
+                    pjoin(LIBART_DIR,'art_rgb.c'),
+                    pjoin(LIBART_DIR,'art_rgb_rgba_affine.c'),
+                    pjoin(LIBART_DIR,'art_svp_intersect.c'),
+                    pjoin(LIBART_DIR,'art_svp_render_aa.c'),
+                    pjoin(LIBART_DIR,'art_misc.c'),
+                    pjoin(GT1_DIR,'gt1-parset1.c'),
+                    pjoin(GT1_DIR,'gt1-dict.c'),
+                    pjoin(GT1_DIR,'gt1-namecontext.c'),
+                    pjoin(GT1_DIR,'gt1-region.c'),
+                    ]
 
         if platform=='win32':
-            FT_LIB=config('FREETYPE','lib',r'C:\devel\freetype-2.1.5\objs\freetype214.lib')
+            FT_LIB=os.environ.get('FT_LIB','')
+            if not FT_LIB: FT_LIB=config('FREETYPE','lib','')
+            if FT_LIB and not os.path.isfile(FT_LIB):
+                infoline('# freetype lib %r not found' % FT_LIB)
+                FT_LIB=[]
             if FT_LIB:
-                FT_INC_DIR=config('FREETYPE','incdir')
+                FT_INC_DIR=os.environ.get('FT_INC','')
+                if not FT_INC_DIR: FT_INC_DIR=config('FREETYPE','inc')
                 FT_MACROS = [('RENDERPM_FT',None)]
                 FT_LIB_DIR = [dirname(FT_LIB)]
                 FT_INC_DIR = [FT_INC_DIR or pjoin(dirname(FT_LIB_DIR[0]),'include')]
-                FT_LIB = [os.path.splitext(os.path.basename(FT_LIB))[0]]
-                infoline('# installing with win32 freetype %r' % FT_LIB[0])
+                FT_LIB_PATH = FT_LIB
+                FT_LIB = [os.path.splitext(os.path.basename(FT_LIB))[0]]                
+                if isdir(FT_INC_DIR[0]):                   
+                    infoline('# installing with freetype %r' % FT_LIB_PATH)
+                else:
+                    infoline('# freetype2 include folder %r not found' % FT_INC_DIR[0])
+                    FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
             else:
                 FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
         else:
-            FT_LIB_DIR=config('FREETYPE','libdir')
-            FT_INC_DIR=config('FREETYPE','incdir')
+            FT_LIB_DIR=config('FREETYPE','lib')
+            FT_INC_DIR=config('FREETYPE','inc')
             I,L=inc_lib_dirs()
             ftv = None
             for d in I:
@@ -323,26 +394,10 @@
                 FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
         if not FT_LIB:
             infoline('# installing without freetype no ttf, sorry!')
-
-        LIBRARIES+= [
-                    ('_renderPM_libart',
-                    {
-                    'sources':  LIBART_SRCS,
-                    'include_dirs': [RENDERPM,LIBART_DIR,],
-                    'macros': [('LIBART_COMPILATION',None),]+BIGENDIAN('WORDS_BIGENDIAN')+MACROS,
-                    #'extra_compile_args':['/Z7'],
-                    }
-                    ),
-                    ('_renderPM_gt1',
-                    {
-                    'sources':  pfxJoin(GT1_DIR,'gt1-dict.c','gt1-namecontext.c','gt1-parset1.c','gt1-region.c','parseAFM.c'),
-                    'include_dirs': [RENDERPM,GT1_DIR,],
-                    'macros': MACROS,
-                    #'extra_compile_args':['/Z7'],
-                    }
-                    ),
-                    ]
-
+            infoline('# You need to install a static library version of the freetype2 software')
+            infoline('# If you need truetype support in renderPM')
+            infoline('# You may need to edit setup.cfg (win32)')
+            infoline('# or edit this file to access the library if it is installed')
         EXT_MODULES +=  [Extension( '_renderPM',
                                         SOURCES,
                                         include_dirs=[RENDERPM,LIBART_DIR,GT1_DIR]+FT_INC_DIR,
@@ -350,7 +405,7 @@
                                         library_dirs=[]+FT_LIB_DIR,
 
                                         # libraries to link against
-                                        libraries=LIBS+FT_LIB,
+                                        libraries=FT_LIB,
                                         #extra_objects=['gt1.lib','libart.lib',],
                                         #extra_compile_args=['/Z7'],
                                         extra_link_args=[]
@@ -363,17 +418,17 @@
     for fn,dst in SPECIAL_PACKAGE_DATA.iteritems():
         shutil.copyfile(fn,pjoin(PACKAGE_DIR['reportlab'],dst))
         reportlab_files.append(dst)
-
+    get_fonts(PACKAGE_DIR, reportlab_files)
     try:
         setup(
             name="reportlab",
             version=get_version(),
-            license="BSD license (see license.txt for details), Copyright (c) 2000-2008, ReportLab Inc.",
+            license="BSD license (see license.txt for details), Copyright (c) 2000-2010, ReportLab Inc.",
             description="The Reportlab Toolkit",
             long_description="""The ReportLab Toolkit. An Open Source Python library for generating PDFs and graphics.""",
 
-            author="Robinson, Watters, Lee, Precedo, Becker and many more...",
-            author_email="info@reportlab.com",
+            author="Andy Robinson, Robin Becker, the ReportLab team and the community",
+            author_email="reportlab-users@lists2.reportlab.com",
             url="http://www.reportlab.com/",
             packages=[
                     'reportlab',
@@ -387,9 +442,12 @@
                     'reportlab.pdfgen',
                     'reportlab.platypus',
                     ],
+            # Ideally we'd have this but PIL via easy_install doesn't seem stable
+            #install_requires=[
+            #        'PIL',
+            #],
             package_dir = PACKAGE_DIR,
             package_data = {'reportlab': reportlab_files},
-            libraries = LIBRARIES,
             ext_modules =   EXT_MODULES,
             )
         print
@@ -400,5 +458,6 @@
             os.remove(pjoin(PACKAGE_DIR['reportlab'],dst))
             reportlab_files.remove(dst)
 
+
 if __name__=='__main__':
     main()
--- a/setup_egg.py	Mon Nov 23 13:27:57 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,412 +0,0 @@
-# -*- coding: UTF-8 -*-
-import os, sys, glob, ConfigParser, shutil
-from distutils import sysconfig
-
-try:
-    from setuptools import setup, find_packages, Extension
-except ImportError:
-    from ez_setup import use_setuptools
-    use_setuptools()
-    from setuptools import setup, find_packages, Extension
-
-platform = sys.platform
-pjoin = os.path.join
-abspath = os.path.abspath
-isfile = os.path.isfile
-isdir = os.path.isfile
-dirname = os.path.dirname
-
-if __name__=='__main__':
-    pkgDir=dirname(sys.argv[0])
-else:
-    pkgDir=dirname(__file__)
-if not pkgDir:
-    pkgDir=os.getcwd()
-elif not os.path.isabs(pkgDir):
-    pkgDir=os.path.abspath(pkgDir)
-
-long_description = """
-The ReportLab Toolkit.
-An Open Source Python library for generating PDFs and graphics.
-""".strip()
-
-
-# from Zope - App.Common.package_home
-def package_home(globals_dict):
-    __name__=globals_dict['__name__']
-    m=sys.modules[__name__]
-    r=os.path.split(m.__path__[0])[0]
-    return r
-
-package_path = pjoin('src/reportlab')
-
-def get_version():
-    #determine Version
-    if __name__=='__main__':
-        HERE=os.path.dirname(sys.argv[0])
-    else:
-        HERE=os.path.dirname(__file__)
-
-    #first try source
-    FN = pjoin(HERE,'src','reportlab','__init__')
-    try:
-        for l in open(pjoin(FN+'.py'),'r').readlines():
-            if l.startswith('Version'):
-                exec l.strip()
-                return Version
-    except:
-        pass
-
-    #don't have source, try import
-    import imp
-    for desc in ('.pyc', 'rb', 2), ('.pyo', 'rb', 2):
-        try:
-            fn = FN+desc[0]
-            f = open(fn,desc[1])
-            m = imp.load_module('reportlab',f,fn,desc)
-            return m.Version
-        except:
-            pass
-    raise ValueError('Cannot determine ReportLab Version')
-
-class config:
-    def __init__(self):
-        try:
-            self.parser = ConfigParser.RawConfigParser()
-            self.parser.read(pjoin(pkgDir,'setup.cfg'))
-        except:
-            self.parser = None
-
-    def __call__(self,sect,name,default=None):
-        try:
-            return self.parser.get(sect,name)
-        except:
-            return default
-config = config()
-
-#this code from /FBot's PIL setup.py
-def aDir(P, d, x=None):
-    if d and os.path.isdir(d) and d not in P:
-        if x is None:
-            P.append(d)
-        else:
-            P.insert(x, d)
-
-class inc_lib_dirs:
-    L = None
-    I = None
-    def __call__(self):
-        if self.L is None:
-            L = []
-            I = []
-            if platform == "cygwin":
-                aDir(L, os.path.join("/usr/lib", "python%s" % sys.version[:3], "config"))
-            elif platform == "darwin":
-                # attempt to make sure we pick freetype2 over other versions
-                aDir(I, "/sw/include/freetype2")
-                aDir(I, "/sw/lib/freetype2/include")
-                # fink installation directories
-                aDir(L, "/sw/lib")
-                aDir(I, "/sw/include")
-                # darwin ports installation directories
-                aDir(L, "/opt/local/lib")
-                aDir(I, "/opt/local/include")
-            aDir(I, "/usr/local/include")
-            aDir(L, "/usr/local/lib")
-            prefix = sysconfig.get_config_var("prefix")
-            if prefix:
-                aDir(L, pjoin(prefix, "lib"))
-                aDir(I, pjoin(prefix, "include"))
-            self.L=L
-            self.I=I
-        return self.I,self.L
-inc_lib_dirs=inc_lib_dirs()
-
-def getVersionFromCCode(fn):
-    import re
-    tag = re.search(r'^#define\s+VERSION\s+"([^"]*)"',open(fn,'r').read(),re.M)
-    return tag and tag.group(1) or ''
-
-class _rl_dir_info:
-    def __init__(self,cn):
-        self.cn=cn
-    def __call__(self,dir):
-        import stat
-        fn = pjoin(dir,self.cn)
-        try:
-            return getVersionFromCCode(fn),os.stat(fn)[stat.ST_MTIME]
-        except:
-            return None
-
-def _cmp_rl_ccode_dirs(a,b):
-    return cmp(_rl_dir_info(b),_rl_dir_info(a))
-
-def _find_rl_ccode(dn='rl_accel',cn='_rl_accel.c'):
-    '''locate where the accelerator code lives'''
-    _ = []
-    for x in [
-            pjoin('src','rl_addons',dn),
-            dn,
-            ] \
-            + glob.glob(pjoin(dn+'-*',dn))\
-            :
-        fn = pjoin(pkgDir,x,cn)
-        if isfile(fn):
-            _.append(x)
-    if _:
-        _ = filter(_rl_dir_info(cn),_)
-        if len(_):
-            _.sort(_cmp_rl_ccode_dirs)
-            #return abspath(_[0])
-            return _[0]
-    return None
-
-
-def BIGENDIAN(macname,value=None):
-    'define a macro if bigendian'
-    return sys.byteorder=='big' and [(macname,value)] or []
-
-def pfxJoin(pfx,*N):
-    R=[]
-    for n in N:
-        R.append(os.path.join(pfx,n))
-    return R
-
-INFOLINES=[]
-def infoline(t):
-    print t
-    INFOLINES.append(t)
-
-reportlab_files= [
-        'extensions/README',
-        'fonts/00readme.txt',
-        'fonts/bitstream-vera-license.txt',
-        'fonts/DarkGarden-copying-gpl.txt',
-        'fonts/DarkGarden-copying.txt',
-        'fonts/DarkGarden-readme.txt',
-        'fonts/DarkGarden.sfd',
-        'fonts/DarkGardenMK.afm',
-        'fonts/DarkGardenMK.pfb',
-        'fonts/Vera.ttf',
-        'fonts/VeraBd.ttf',
-        'fonts/VeraBI.ttf',
-        'fonts/VeraIt.ttf',
-        ]
-
-def main():
-    # Skip if the command is clean.
-    cmd = sys.argv[-1]
-    if cmd and cmd == "clean":
-        return
-
-    SPECIAL_PACKAGE_DATA = {}
-    RL_ACCEL = _find_rl_ccode('rl_accel','_rl_accel.c')
-    LIBS = []
-    LIBRARIES=[]
-    EXT_MODULES = []
-    if not RL_ACCEL:
-        infoline( '***************************************************')
-        infoline( '*No rl_accel code found, you can obtain it at     *')
-        infoline( '*http://www.reportlab.org/downloads.html#_rl_accel*')
-        infoline( '***************************************************')
-    else:
-        infoline( '################################################')
-        infoline( '#Attempting install of _rl_accel, sgmlop & pyHnj')
-        infoline( '#extensions from %r'%RL_ACCEL)
-        infoline( '################################################')
-        fn = pjoin(RL_ACCEL,'hyphen.mashed')
-        SPECIAL_PACKAGE_DATA = {fn: pjoin('lib','hyphen.mashed')}
-        EXT_MODULES += [
-                    Extension( '_rl_accel',
-                                [pjoin(RL_ACCEL,'_rl_accel.c')],
-                                include_dirs=[],
-                            define_macros=[],
-                            library_dirs=[],
-                            libraries=LIBS, # libraries to link against
-                            ),
-                    Extension( 'sgmlop',
-                            [pjoin(RL_ACCEL,'sgmlop.c')],
-                            include_dirs=[],
-                            define_macros=[],
-                            library_dirs=[],
-                            libraries=LIBS, # libraries to link against
-                            ),
-                    Extension( 'pyHnj',
-                            [pjoin(RL_ACCEL,'pyHnjmodule.c'),
-                             pjoin(RL_ACCEL,'hyphen.c'),
-                             pjoin(RL_ACCEL,'hnjalloc.c')],
-                            include_dirs=[],
-                            define_macros=[],
-                            library_dirs=[],
-                            libraries=LIBS, # libraries to link against
-                            ),
-                    ]
-    RENDERPM = _find_rl_ccode('renderPM','_renderPM.c')
-    LIBS = []
-    if not RENDERPM:
-        infoline( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
-        infoline( '!No rl_accel code found, you can obtain it at     !')
-        infoline( '!http://www.reportlab.org/downloads.html          !')
-        infoline( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
-    else:
-        infoline( '################################################')
-        infoline( '#Attempting install of _renderPM')
-        infoline( '#extensions from %r'%RENDERPM)
-        LIBART_DIR=pjoin(RENDERPM,'libart_lgpl')
-        MACROS=[('ROBIN_DEBUG',None)]
-        MACROS=[]
-        def libart_version():
-            K = ('LIBART_MAJOR_VERSION','LIBART_MINOR_VERSION','LIBART_MICRO_VERSION')
-            D = {}
-            for l in open(pjoin(LIBART_DIR,'configure.in'),'r').readlines():
-                l = l.strip().split('=')
-                if len(l)>1 and l[0].strip() in K:
-                    D[l[0].strip()] = l[1].strip()
-                    if len(D)==3: break
-            return (sys.platform == 'win32' and '\\"%s\\"' or '"%s"') % '.'.join(map(lambda k,D=D: D.get(k,'?'),K))
-        LIBART_VERSION = libart_version()
-        SOURCES=[pjoin(RENDERPM,'_renderPM.c')]
-        LIBART_SRCS=glob.glob(pjoin(LIBART_DIR, 'art_*.c'))
-        GT1_DIR=pjoin(RENDERPM,'gt1')
-        LIBS = []       #assume empty libraries list
-
-        if platform=='win32':
-            FT_LIB=config('FREETYPE','lib',r'C:\Python\devel\freetype-2.1.5\objs\freetype214.lib')
-            if FT_LIB:
-                FT_INC_DIR=config('FREETYPE','incdir')
-                FT_MACROS = [('RENDERPM_FT',None)]
-                FT_LIB_DIR = [dirname(FT_LIB)]
-                FT_INC_DIR = [FT_INC_DIR or pjoin(dirname(FT_LIB_DIR[0]),'include')]
-                FT_LIB = [os.path.splitext(os.path.basename(FT_LIB))[0]]
-                infoline('# installing with win32 freetype %r' % FT_LIB[0])
-            else:
-                FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
-        else:
-            FT_LIB_DIR=config('FREETYPE','libdir')
-            FT_INC_DIR=config('FREETYPE','incdir')
-            I,L=inc_lib_dirs()
-            ftv = None
-            for d in I:
-                if isfile(pjoin(d, "ft2build.h")):
-                    ftv = 21
-                    FT_INC_DIR=[d,pjoin(d, "freetype2")]
-                    break
-                d = pjoin(d, "freetype2")
-                if isfile(pjoin(d, "ft2build.h")):
-                    ftv = 21
-                    FT_INC_DIR=[d]
-                    break
-                if isdir(pjoin(d, "freetype")):
-                    ftv = 20
-                    FT_INC_DIR=[d]
-                    break
-            if ftv:
-                FT_LIB=['freetype']
-                FT_LIB_DIR=L
-                FT_MACROS = [('RENDERPM_FT',None)]
-                infoline('# installing with freetype version %d' % ftv)
-            else:
-                FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
-        if not FT_LIB:
-            infoline('# installing without freetype no ttf, sorry!')
-
-        LIBRARIES+= [
-                    ('_renderPM_libart',
-                    {
-                    'sources':  LIBART_SRCS,
-                    'include_dirs': [RENDERPM,LIBART_DIR,],
-                    'macros': [('LIBART_COMPILATION',None),]+BIGENDIAN('WORDS_BIGENDIAN')+MACROS,
-                    #'extra_compile_args':['/Z7'],
-                    }
-                    ),
-                    ('_renderPM_gt1',
-                    {
-                    'sources':  pfxJoin(GT1_DIR,'gt1-dict.c','gt1-namecontext.c','gt1-parset1.c','gt1-region.c','parseAFM.c'),
-                    'include_dirs': [RENDERPM,GT1_DIR,],
-                    'macros': MACROS,
-                    #'extra_compile_args':['/Z7'],
-                    }
-                    ),
-                    ]
-
-        EXT_MODULES +=  [Extension( '_renderPM',
-                                        SOURCES,
-                                        include_dirs=[RENDERPM,LIBART_DIR,GT1_DIR]+FT_INC_DIR,
-                                        define_macros=FT_MACROS+[('LIBART_COMPILATION',None)]+MACROS+[('LIBART_VERSION',LIBART_VERSION)],
-                                        library_dirs=[]+FT_LIB_DIR,
-
-                                        # libraries to link against
-                                        libraries=LIBS+FT_LIB,
-                                        #extra_objects=['gt1.lib','libart.lib',],
-                                        #extra_compile_args=['/Z7'],
-                                        extra_link_args=[]
-                                        ),
-                            ]
-        infoline('################################################')
-
-    infoline('Attempting to install PIL')
-    ret = os.system("easy_install --find-links http://www.pythonware.com/products/pil/ Imaging")
-    if not ret:
-        infoline('PIL Installation failed. Proceeding ...')
-    infoline('################################################')
-
-    #copy some special case files into place so package_data will treat them properly
-    PACKAGE_DIR = {'reportlab': pjoin('src','reportlab')}
-    for fn,dst in SPECIAL_PACKAGE_DATA.iteritems():
-        shutil.copyfile(fn,pjoin(PACKAGE_DIR['reportlab'],dst))
-        reportlab_files.append(dst)
-
-    setup(
-        name="reportlab",
-        version = get_version(),
-        license="BSD license (see license.txt for details), Copyright (c) 2000-2008, ReportLab Inc.",
-        description="The Reportlab Toolkit",
-        long_description=long_description,
-
-        #classifiers = [x.strip() for x in """
-        #    """.strip().splitlines()],
-
-        author="Robinson, Watters, Lee, Precedo, Becker and many more...",
-        author_email="info@reportlab.com",
-        url="http://www.reportlab.com/",
-        download_url = "http://www.reportlab.com/",
-
-        # Installing PIL as a dependency is a pain. Here we are
-        # executing the below command directly as part of the setup.
-        # easy_install --find-links http://www.pythonware.com/products/pil/ Imaging
-        # Got the idea from http://www.martin-geber.com/weblog/2007/08/22/problems-installing-easy_install-pil/
-        install_requires = [
-            #"PIL>=1.1.3",
-            ],
-
-        package_dir = {
-            '': 'src'
-            },
-
-        # packages = find_packages(exclude=['ez_setup']),
-
-        packages=[ # include anything with an __init__
-                'reportlab',
-                'reportlab.extensions',
-                'reportlab.graphics.charts',
-                'reportlab.graphics.samples',
-                'reportlab.graphics.widgets',
-                'reportlab.graphics.barcode',
-                'reportlab.graphics',
-                'reportlab.lib',
-                'reportlab.pdfbase',
-                'reportlab.pdfgen',
-                'reportlab.platypus',
-                 ],
-
-        include_package_data = True,
-
-        test_suite = "tests",
-
-        #data_files = DATA_FILES.items(),
-        libraries = LIBRARIES,
-        #ext_modules =   EXT_MODULES,
-    )
-
-if __name__ == "__main__":
-    main()
--- a/src/reportlab/__init__.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/__init__.py	Thu Oct 21 10:34:13 2010 +0000
@@ -3,19 +3,22 @@
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/__init__.py
 __version__=''' $Id$ '''
 __doc__="""The Reportlab PDF generation library."""
-Version = "2.3+"
+Version = "2.5"
 
 import sys
 
-if sys.version_info[0:2] < (2, 3):
-    warning = """The trunk of reportlab requires Python 2.3 or higher.
-    Any older applications should either use released versions beginning
-    with 1.x (e.g. 1.21), or snapshots or checkouts from our 'version1'
-    branch.
+if sys.version_info[0:2] < (2, 4):
+    warning = """The trunk of reportlab requires Python 2.4 or higher.
+
+    Python 2.3 users may still use ReportLab 2.4 or any other bugfixes
+    derived from it.  Python 2.2 and below need to use released versions 
+    beginning     with 1.x (e.g. 1.21), or snapshots or checkouts from 
+    our 'version1' branch.
     """
-    raise ImportError("reportlab needs Python 2.3 or higher", warning)
+    raise ImportError("reportlab needs Python 2.4 or higher", warning)
 
 def getStory(context):
+    "This is a helper for our old autogenerated documentation system"
     if context.target == 'UserGuide':
         # parse some local file
         import os
--- a/src/reportlab/graphics/barcode/__init__.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/__init__.py	Thu Oct 21 10:34:13 2010 +0000
@@ -43,6 +43,7 @@
 
     #newer codes will typically get their own module
     from eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget
+    from qr import QrCodeWidget
 
 
     #the module exports a dictionary of names to widgets, to make it easy for
@@ -63,6 +64,7 @@
                 BarcodeUSPS_4State,
                 Ean13BarcodeWidget,
                 Ean8BarcodeWidget,
+                QrCodeWidget,
                 ):
         codeName = widget.codeName
         codes[codeName] = widget
--- a/src/reportlab/graphics/barcode/code128.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/code128.py	Thu Oct 21 10:34:13 2010 +0000
@@ -276,7 +276,7 @@
         s = self.validated
         l = ['START_B']
         for c in s:
-            if not setb.has_key(c):
+            if c not in setb:
                 l = l + ['TO_A', c, 'TO_B']
             else:
                 l.append(c)
@@ -288,7 +288,7 @@
         if l[1] in tos:
             l[:2] = ['START_' + l[1][-1]]
 
-#        print `l`
+#        print repr(l)
 
         # encode into numbers
         start, set, shset = setmap[l[0]]
--- a/src/reportlab/graphics/barcode/code39.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/code39.py	Thu Oct 21 10:34:13 2010 +0000
@@ -232,7 +232,7 @@
     def encode(self):
         self.encoded = ""
         for c in self.validated:
-            if _extended.has_key(c):
+            if c in _extended:
                 self.encoded = self.encoded + _extended[c]
             elif c in _stdchrs:
                 self.encoded = self.encoded + c
--- a/src/reportlab/graphics/barcode/code93.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/code93.py	Thu Oct 21 10:34:13 2010 +0000
@@ -187,7 +187,7 @@
         for c in self.value:
             if c in string.lowercase:
                 c = string.upper(c)
-            if not _patterns.has_key(c):
+            if c not in _patterns:
                 self.valid = 0
                 continue
             vval = vval + c
@@ -213,7 +213,7 @@
         self.valid = 1
         a = vval.append
         for c in self.value:
-            if not _patterns.has_key(c) and not _extended.has_key(c):
+            if c not in _patterns and c not in _extended:
                 self.valid = 0
                 continue
             a(c)
@@ -223,9 +223,9 @@
     def encode(self):
         self.encoded = ""
         for c in self.validated:
-            if _patterns.has_key(c):
+            if c in _patterns:
                 self.encoded = self.encoded + c
-            elif _extended.has_key(c):
+            elif c in _extended:
                 self.encoded = self.encoded + _extended[c]
             else:
                 raise ValueError
--- a/src/reportlab/graphics/barcode/common.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/common.py	Thu Oct 21 10:34:13 2010 +0000
@@ -661,7 +661,7 @@
     http://www.cwi.nl/people/dik/english/codes/barcodes.html
     """
 
-    chars = string.digits + '-'
+    chars = '0123456789-'
 
     patterns = {
         '0' : 'bsbsB',        '1' : 'BsbsB',        '2' : 'bSbsB',
@@ -705,7 +705,7 @@
         vval = ""
         self.valid = 1
         s = string.strip(self.value)
-        for i in range(0, len(s)):
+        for i in xrange(0, len(s)):
             c = s[i]
             if c not in self.chars:
                 self.Valid = 0
@@ -715,40 +715,33 @@
         self.validated = vval
         return vval
 
+    def _addCSD(self,s,m):
+        # compute first checksum
+        i = c = 0
+        v = 1
+        V = self.values
+        while i < len(s):
+            c += v * V[s[-(i+1)]]
+            i += 1
+            v += 1
+            if v==m:
+                v = 1
+        return s+self.chars[c % 11]
+
     def encode(self):
         s = self.validated
 
-        if self.checksum == -1:
-            if len(s) <= 10:
-                self.checksum = 1
-            else:
-                self.checksum = 2
+        tcs = self.checksum
+        if tcs<0:
+            self.checksum = tcs = 1+int(len(s)>10)
 
-        if self.checksum > 0:
-            # compute first checksum
-            i = 0; v = 1; c = 0
-            while i < len(s):
-                c = c + v * string.index(self.chars, s[-(i+1)])
-                i = i + 1; v = v + 1
-                if v > 10:
-                    v = 1
-            s = s + self.chars[c % 11]
-
-        if self.checksum > 1:
-            # compute second checksum
-            i = 0; v = 1; c = 0
-            while i < len(s):
-                c = c + v * string.index(self.chars, s[-(i+1)])
-                i = i + 1; v = v + 1
-                if v > 9:
-                    v = 1
-            s = s + self.chars[c % 10]
+        if tcs > 0: s = self._addCSD(s,11)
+        if tcs > 1: s = self._addCSD(s,10)
 
         self.encoded = self.stop and ('S' + s + 'S') or s
 
     def decompose(self):
-        dval = [self.patterns[c]+'i' for c in self.encoded]
-        self.decomposed = ''.join(dval[:-1])
+        self.decomposed = ''.join([(self.patterns[c]+'i') for c in self.encoded])[:-1]
         return self.decomposed
 
     def _humanText(self):
--- a/src/reportlab/graphics/barcode/eanbc.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/eanbc.py	Thu Oct 21 10:34:13 2010 +0000
@@ -179,7 +179,8 @@
             }
     fontSize = 8        #millimeters
     fontName = 'Helvetica'
-    textColor = barFillColor = barStrokeColor = colors.black
+    textColor = barFillColor = colors.black
+    barStrokeColor = None
     barStrokeWidth = 0
     x = 0
     y = 0
@@ -243,6 +244,7 @@
         fontSize = self.fontSize
         barFillColor = self.barFillColor
         barStrokeWidth = self.barStrokeWidth
+        barStrokeColor = self.barStrokeColor
 
         fth = fontSize*1.2
         b = ''.join(b)
@@ -255,7 +257,7 @@
                 if lrect and lrect.y==yh:
                     lrect.width += barWidth
                 else:
-                    lrect = Rect(x,yh,barWidth,barHeight-dh,fillColor=barFillColor,strokeWidth=barStrokeWidth,strokeColor=barFillColor)
+                    lrect = Rect(x,yh,barWidth,barHeight-dh,fillColor=barFillColor,strokeWidth=barStrokeWidth,strokeColor=barStrokeColor)
                     gAdd(lrect)
             else:
                 lrect = None
--- a/src/reportlab/graphics/barcode/lto.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/lto.py	Thu Oct 21 10:34:13 2010 +0000
@@ -108,7 +108,7 @@
 
         colored : boolean to determine if blocks have to be colorized.
         """
-        if kwargs.has_key("colored") :
+        if "colored" in kwargs:
             self.colored = kwargs["colored"]
             del kwargs["colored"]
         else :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/reportlab/graphics/barcode/qr.py	Thu Oct 21 10:34:13 2010 +0000
@@ -0,0 +1,996 @@
+# QRCode for Python
+#
+# Ported from the Javascript library by Sam Curren
+# ReportLab module by German M. Bravo
+#
+# QRCode for Javascript
+# http://d-project.googlecode.com/svn/trunk/misc/qrcode/js/qrcode.js
+#
+# Copyright (c) 2009 Kazuhiko Arase
+#
+# URL: http://www.d-project.com/
+#
+# Licensed under the MIT license:
+#   http://www.opensource.org/licenses/mit-license.php
+#
+# The word "QR Code" is registered trademark of
+# DENSO WAVE INCORPORATED
+#   http://www.denso-wave.com/qrcode/faqpatent-e.html
+
+__all__=(
+        'QrCodeWidget',
+        )
+import math, re
+from reportlab.graphics.shapes import Group, Rect
+from reportlab.lib import colors
+from reportlab.lib.validators import isNumber, isColor, isString, Validator
+from reportlab.lib.attrmap import *
+from reportlab.graphics.charts.areas import PlotArea
+from reportlab.lib.units import mm
+
+class isLevel(Validator):
+    def test(self,x):
+        # level L : About 7% or less errors can be corrected.
+        # level M : About 15% or less errors can be corrected.
+        # level Q : About 25% or less errors can be corrected.
+        # level H : About 30% or less errors can be corrected.
+        return type(x) is str and len(x)==1 and x in ['L', 'M', 'Q', 'H']
+isLevel = isLevel()
+
+class QrCodeWidget(PlotArea):
+    codeName = "QR"
+    _attrMap = AttrMap(BASE=PlotArea,
+        value = AttrMapValue(isString, desc='the text'),
+        x = AttrMapValue(isNumber, desc='x-coord'),
+        y = AttrMapValue(isNumber, desc='y-coord'),
+        barFillColor = AttrMapValue(isColor, desc='bar color'),
+        barWidth = AttrMapValue(isNumber, desc='Width of bars.'), # maybe should be named just width?
+        barHeight = AttrMapValue(isNumber, desc='Height of bars.'), # maybe should be named just height?
+        barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'), # maybe removed?
+        barStrokeColor = AttrMapValue(isColor, desc='Color of bar borders.'), # maybe removed?
+        barBorder = AttrMapValue(isNumber, desc='Width of QR border.'), # maybe should be named qrBorder?
+        barLevel = AttrMapValue(isLevel, desc='QR Code level.'), # maybe should be named qrLevel
+        )
+    x = 0
+    y = 0
+    barFillColor = colors.black
+    barStrokeColor = None
+    barStrokeWidth = 0
+    barHeight = 32*mm
+    barWidth = 32*mm
+    barBorder = 4
+    barLevel = 'L'
+
+    def __init__(self,value='Hello World',**kw):
+        self.value=value
+        for k, v in kw.iteritems():
+            setattr(self, k, v)
+
+    def wrap(self,aW,aH):
+        return self.width,self.height
+
+
+    def draw(self):
+        g = Group()
+        gAdd = g.add
+        barWidth = self.barWidth
+        barHeight = self.barHeight
+        x = self.x
+        y = self.y
+        gAdd(Rect(x,y,barWidth,barHeight,fillColor=None,strokeColor=None,strokeWidth=0))
+
+        barFillColor = self.barFillColor
+        barStrokeWidth = self.barStrokeWidth
+        barStrokeColor = self.barStrokeColor
+        barBorder = self.barBorder
+
+        correctLevel = {
+            'L': QRErrorCorrectLevel.L,
+            'M': QRErrorCorrectLevel.M,
+            'Q': QRErrorCorrectLevel.Q,
+            'H': QRErrorCorrectLevel.H,
+        }[self.barLevel]
+
+        qr = QRCode(None, correctLevel)
+
+        qr.addData(self.value)
+        qr.make()
+
+        moduleCount = qr.getModuleCount()
+        boxsize = min(barWidth, barHeight) / (moduleCount + barBorder * 2)
+        offsetX = (barWidth - min(barWidth, barHeight)) / 2
+        offsetY = (min(barWidth, barHeight) - barHeight) / 2
+
+        for r in xrange(moduleCount):
+            for c in xrange(moduleCount):
+                if (qr.isDark(r, c) ):
+                    x = (c + barBorder) * boxsize
+                    y = (r + barBorder+1) * boxsize
+                    qrect = Rect(offsetX+x,offsetY+barHeight-y,boxsize,boxsize,fillColor=barFillColor,strokeWidth=barStrokeWidth,strokeColor=barStrokeColor)
+                    gAdd(qrect)
+
+        return g
+
+class QRMode:
+    MODE_NUMBER = 1 << 0
+    MODE_ALPHA_NUM = 1 << 1
+    MODE_8BIT_BYTE = 1 << 2
+    MODE_KANJI = 1 << 3
+
+class QR:
+    def __init__(self, data):
+        if self.valid:
+            if not re.search('^[%s]+$' % self.valid, data):
+                raise ValueError
+        else:
+            self.valid = ''.join(chr(c) for c in range(256))
+        self.data = data
+
+    def getLength(self):
+        return len(self.data)
+
+    def __repr__(self):
+        return self.data
+
+    def write(self, buffer):
+        for g in map(None, *[iter(self.data)] * self.group):
+            bits = 0
+            n = 0
+            for i in range(self.group):
+                if g[i] is not None:
+                    n *= len(self.valid)
+                    n += self.valid.index(g[i])
+                    bits += self.bits[i]
+            buffer.put(n, bits)
+
+class QRNumber(QR):
+    valid = '0123456789'
+    bits = (4,3,3)
+    group = 3
+    mode = QRMode.MODE_NUMBER
+
+class QRAlphaNum(QR):
+    valid = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'
+    bits = (6,5)
+    group = 2
+    mode = QRMode.MODE_ALPHA_NUM
+
+class QR8bitByte(QR):
+    valid = None #''.join(chr(c) for c in range(256))
+    bits = (8,)
+    group = 1
+    mode = QRMode.MODE_8BIT_BYTE
+
+    def write(self, buffer):
+        for c in self.data:
+            buffer.put(ord(c), 8)
+
+class QRKanji(QR):
+    valid = None
+    bits = (8,)
+    group = 1
+    mode = QRMode.MODE_KANJI
+
+class QRCode:
+    def __init__(self, typeNumber, errorCorrectLevel):
+        self.typeNumber = typeNumber
+        self.errorCorrectLevel = errorCorrectLevel
+        self.modules = None
+        self.moduleCount = 0
+        self.dataCache = None
+        self.dataList = []
+
+    def addData(self, data):
+        try :
+            newData = QRNumber(data)
+        except ValueError:
+            try:
+                newData = QRAlphaNum(data)
+            except ValueError:
+                try:
+                    newData = QR8bitByte(data)
+                except ValueError:
+                    try:
+                        newData = QRKanji(data)
+                    except:
+                        raise
+        self.dataList.append(newData)
+        self.dataCache = None
+
+    def isDark(self, row, col):
+        if (row < 0 or self.moduleCount <= row or col < 0 or self.moduleCount <= col):
+            raise Exception("%s,%s - %s" % (row, col, self.moduleCount))
+        return self.modules[row][col]
+
+    def getModuleCount(self):
+        return self.moduleCount
+
+    def make(self):
+        if self.typeNumber is None:
+            # Calculate typeNumber for data to fit the QR Code capacity
+            errorCorrectLevel = self.errorCorrectLevel
+            for typeNumber in xrange(1, 40):
+                rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
+                totalDataCount = 0;
+                for i in xrange(len(rsBlocks)):
+                    totalDataCount += rsBlocks[i].dataCount
+                length = 0
+                for i in xrange(len(self.dataList)):
+                    data = self.dataList[i]
+                    length += 4
+                    length += QRUtil.getLengthInBits(data.mode, typeNumber)
+                    length += len(data.data) * 8
+                if length <= totalDataCount * 8:
+                    break
+            self.typeNumber = typeNumber
+        self.makeImpl(False, self.getBestMaskPattern())
+
+    def makeImpl(self, test, maskPattern):
+        self.moduleCount = self.typeNumber * 4 + 17
+        self.modules = [None for x in xrange(self.moduleCount)]
+        for row in xrange(self.moduleCount):
+            self.modules[row] = [None for x in xrange(self.moduleCount)]
+            for col in xrange(self.moduleCount):
+                self.modules[row][col] = None #(col + row) % 3;
+        self.setupPositionProbePattern(0, 0)
+        self.setupPositionProbePattern(self.moduleCount - 7, 0)
+        self.setupPositionProbePattern(0, self.moduleCount - 7)
+        self.setupPositionAdjustPattern()
+        self.setupTimingPattern()
+        self.setupTypeInfo(test, maskPattern)
+        if (self.typeNumber >= 7):
+            self.setupTypeNumber(test)
+        if (self.dataCache == None):
+            self.dataCache = QRCode.createData(self.typeNumber, self.errorCorrectLevel, self.dataList)
+        self.mapData(self.dataCache, maskPattern)
+
+    def setupPositionProbePattern(self, row, col):
+        for r in xrange(-1, 8):
+            if (row + r <= -1 or self.moduleCount <= row + r): continue
+            for c in xrange(-1, 8):
+                if (col + c <= -1 or self.moduleCount <= col + c): continue
+                if ( (0 <= r and r <= 6 and (c == 0 or c == 6) )
+                        or (0 <= c and c <= 6 and (r == 0 or r == 6) )
+                        or (2 <= r and r <= 4 and 2 <= c and c <= 4) ):
+                    self.modules[row + r][col + c] = True;
+                else:
+                    self.modules[row + r][col + c] = False;
+
+    def getBestMaskPattern(self):
+        minLostPoint = 0
+        pattern = 0
+        for i in xrange(8):
+            self.makeImpl(True, i);
+            lostPoint = QRUtil.getLostPoint(self);
+            if (i == 0 or minLostPoint > lostPoint):
+                minLostPoint = lostPoint
+                pattern = i
+        return pattern
+
+    def setupTimingPattern(self):
+        for r in xrange(8, self.moduleCount - 8):
+            if (self.modules[r][6] != None):
+                continue
+            self.modules[r][6] = (r % 2 == 0)
+        for c in xrange(8, self.moduleCount - 8):
+            if (self.modules[6][c] != None):
+                continue
+            self.modules[6][c] = (c % 2 == 0)
+
+    def setupPositionAdjustPattern(self):
+        pos = QRUtil.getPatternPosition(self.typeNumber)
+        for i in xrange(len(pos)):
+            for j in xrange(len(pos)):
+                row = pos[i]
+                col = pos[j]
+                if (self.modules[row][col] != None):
+                    continue
+                for r in xrange(-2, 3):
+                    for c in xrange(-2, 3):
+                        if (r == -2 or r == 2 or c == -2 or c == 2 or (r == 0 and c == 0) ):
+                            self.modules[row + r][col + c] = True
+                        else:
+                            self.modules[row + r][col + c] = False
+
+    def setupTypeNumber(self, test):
+        bits = QRUtil.getBCHTypeNumber(self.typeNumber)
+        for i in xrange(18):
+            mod = (not test and ( (bits >> i) & 1) == 1)
+            self.modules[i // 3][i % 3 + self.moduleCount - 8 - 3] = mod;
+        for i in xrange(18):
+            mod = (not test and ( (bits >> i) & 1) == 1)
+            self.modules[i % 3 + self.moduleCount - 8 - 3][i // 3] = mod;
+
+    def setupTypeInfo(self, test, maskPattern):
+        data = (self.errorCorrectLevel << 3) | maskPattern
+        bits = QRUtil.getBCHTypeInfo(data)
+        # vertical
+        for i in xrange(15):
+            mod = (not test and ( (bits >> i) & 1) == 1)
+            if (i < 6):
+                self.modules[i][8] = mod
+            elif (i < 8):
+                self.modules[i + 1][8] = mod
+            else:
+                self.modules[self.moduleCount - 15 + i][8] = mod
+        # horizontal
+        for i in xrange(15):
+            mod = (not test and ( (bits >> i) & 1) == 1);
+            if (i < 8):
+                self.modules[8][self.moduleCount - i - 1] = mod
+            elif (i < 9):
+                self.modules[8][15 - i - 1 + 1] = mod
+            else:
+                self.modules[8][15 - i - 1] = mod
+        # fixed module
+        self.modules[self.moduleCount - 8][8] = (not test)
+
+    def mapData(self, data, maskPattern):
+        inc = -1
+        row = self.moduleCount - 1
+        bitIndex = 7
+        byteIndex = 0
+        for col in xrange(self.moduleCount - 1, 0, -2):
+            if (col == 6): col-=1
+            while (True):
+                for c in xrange(2):
+                    if (self.modules[row][col - c] == None):
+                        dark = False
+                        if (byteIndex < len(data)):
+                            dark = ( ( (data[byteIndex] >> bitIndex) & 1) == 1)
+                        mask = QRUtil.getMask(maskPattern, row, col - c)
+                        if (mask):
+                            dark = not dark
+                        self.modules[row][col - c] = dark
+                        bitIndex-=1
+                        if (bitIndex == -1):
+                            byteIndex+=1
+                            bitIndex = 7
+                row += inc
+                if (row < 0 or self.moduleCount <= row):
+                    row -= inc
+                    inc = -inc
+                    break
+    PAD0 = 0xEC
+    PAD1 = 0x11
+
+    @staticmethod
+    def createData(typeNumber, errorCorrectLevel, dataList):
+        rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel)
+        buffer = QRBitBuffer();
+        for i in xrange(len(dataList)):
+            data = dataList[i]
+            buffer.put(data.mode, 4)
+            buffer.put(data.getLength(), QRUtil.getLengthInBits(data.mode, typeNumber) )
+            data.write(buffer)
+        # calc num max data.
+        totalDataCount = 0;
+        for i in xrange(len(rsBlocks)):
+            totalDataCount += rsBlocks[i].dataCount
+        if (buffer.getLengthInBits() > totalDataCount * 8):
+            raise Exception("code length overflow. (%d > %d)" % (buffer.getLengthInBits(), totalDataCount * 8))
+        # end code
+        if (buffer.getLengthInBits() + 4 <= totalDataCount * 8):
+            buffer.put(0, 4)
+        # padding
+        while (buffer.getLengthInBits() % 8 != 0):
+            buffer.putBit(False)
+        # padding
+        while (True):
+            if (buffer.getLengthInBits() >= totalDataCount * 8):
+                break
+            buffer.put(QRCode.PAD0, 8)
+            if (buffer.getLengthInBits() >= totalDataCount * 8):
+                break
+            buffer.put(QRCode.PAD1, 8)
+        return QRCode.createBytes(buffer, rsBlocks)
+
+    @staticmethod
+    def createBytes(buffer, rsBlocks):
+        offset = 0
+        maxDcCount = 0
+        maxEcCount = 0
+        dcdata = [0 for x in xrange(len(rsBlocks))]
+        ecdata = [0 for x in xrange(len(rsBlocks))]
+        for r in xrange(len(rsBlocks)):
+            dcCount = rsBlocks[r].dataCount
+            ecCount = rsBlocks[r].totalCount - dcCount
+            maxDcCount = max(maxDcCount, dcCount)
+            maxEcCount = max(maxEcCount, ecCount)
+            dcdata[r] = [0 for x in xrange(dcCount)]
+            for i in xrange(len(dcdata[r])):
+                dcdata[r][i] = 0xff & buffer.buffer[i + offset]
+            offset += dcCount
+            rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount)
+            rawPoly = QRPolynomial(dcdata[r], rsPoly.getLength() - 1)
+            modPoly = rawPoly.mod(rsPoly)
+            ecdata[r] = [0 for x in xrange(rsPoly.getLength()-1)]
+            for i in xrange(len(ecdata[r])):
+                modIndex = i + modPoly.getLength() - len(ecdata[r])
+                if (modIndex >= 0):
+                    ecdata[r][i] = modPoly.get(modIndex)
+                else:
+                    ecdata[r][i] = 0
+        totalCodeCount = 0
+        for i in xrange(len(rsBlocks)):
+            totalCodeCount += rsBlocks[i].totalCount
+        data = [None for x in xrange(totalCodeCount)]
+        index = 0
+        for i in xrange(maxDcCount):
+            for r in xrange(len(rsBlocks)):
+                if (i < len(dcdata[r])):
+                    data[index] = dcdata[r][i]
+                    index+=1
+        for i in xrange(maxEcCount):
+            for r in xrange(len(rsBlocks)):
+                if (i < len(ecdata[r])):
+                    data[index] = ecdata[r][i]
+                    index+=1
+        return data
+
+
+class QRErrorCorrectLevel:
+    L = 1
+    M = 0
+    Q = 3
+    H = 2
+
+class QRMaskPattern:
+    PATTERN000 = 0
+    PATTERN001 = 1
+    PATTERN010 = 2
+    PATTERN011 = 3
+    PATTERN100 = 4
+    PATTERN101 = 5
+    PATTERN110 = 6
+    PATTERN111 = 7
+
+class QRUtil(object):
+    PATTERN_POSITION_TABLE = [
+        [],
+        [6, 18],
+        [6, 22],
+        [6, 26],
+        [6, 30],
+        [6, 34],
+        [6, 22, 38],
+        [6, 24, 42],
+        [6, 26, 46],
+        [6, 28, 50],
+        [6, 30, 54],
+        [6, 32, 58],
+        [6, 34, 62],
+        [6, 26, 46, 66],
+        [6, 26, 48, 70],
+        [6, 26, 50, 74],
+        [6, 30, 54, 78],
+        [6, 30, 56, 82],
+        [6, 30, 58, 86],
+        [6, 34, 62, 90],
+        [6, 28, 50, 72, 94],
+        [6, 26, 50, 74, 98],
+        [6, 30, 54, 78, 102],
+        [6, 28, 54, 80, 106],
+        [6, 32, 58, 84, 110],
+        [6, 30, 58, 86, 114],
+        [6, 34, 62, 90, 118],
+        [6, 26, 50, 74, 98, 122],
+        [6, 30, 54, 78, 102, 126],
+        [6, 26, 52, 78, 104, 130],
+        [6, 30, 56, 82, 108, 134],
+        [6, 34, 60, 86, 112, 138],
+        [6, 30, 58, 86, 114, 142],
+        [6, 34, 62, 90, 118, 146],
+        [6, 30, 54, 78, 102, 126, 150],
+        [6, 24, 50, 76, 102, 128, 154],
+        [6, 28, 54, 80, 106, 132, 158],
+        [6, 32, 58, 84, 110, 136, 162],
+        [6, 26, 54, 82, 110, 138, 166],
+        [6, 30, 58, 86, 114, 142, 170]
+    ]
+
+    G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0)
+    G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0)
+    G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1)
+
+    @staticmethod
+    def getBCHTypeInfo(data):
+        d = data << 10;
+        while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) >= 0):
+            d ^= (QRUtil.G15 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G15) ) )
+        return ( (data << 10) | d) ^ QRUtil.G15_MASK
+
+    @staticmethod
+    def getBCHTypeNumber(data):
+        d = data << 12;
+        while (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) >= 0):
+            d ^= (QRUtil.G18 << (QRUtil.getBCHDigit(d) - QRUtil.getBCHDigit(QRUtil.G18) ) )
+        return (data << 12) | d
+
+    @staticmethod
+    def getBCHDigit(data):
+        digit = 0;
+        while (data != 0):
+            digit += 1
+            data >>= 1
+        return digit
+
+    @staticmethod
+    def getPatternPosition(typeNumber):
+        return QRUtil.PATTERN_POSITION_TABLE[typeNumber - 1]
+
+    @staticmethod
+    def getMask(maskPattern, i, j):
+        if maskPattern == QRMaskPattern.PATTERN000 : return (i + j) % 2 == 0
+        if maskPattern == QRMaskPattern.PATTERN001 : return i % 2 == 0
+        if maskPattern == QRMaskPattern.PATTERN010 : return j % 3 == 0
+        if maskPattern == QRMaskPattern.PATTERN011 : return (i + j) % 3 == 0
+        if maskPattern == QRMaskPattern.PATTERN100 : return (math.floor(i / 2) + math.floor(j / 3) ) % 2 == 0
+        if maskPattern == QRMaskPattern.PATTERN101 : return (i * j) % 2 + (i * j) % 3 == 0
+        if maskPattern == QRMaskPattern.PATTERN110 : return ( (i * j) % 2 + (i * j) % 3) % 2 == 0
+        if maskPattern == QRMaskPattern.PATTERN111 : return ( (i * j) % 3 + (i + j) % 2) % 2 == 0
+        raise Exception("bad maskPattern:" + maskPattern)
+
+    @staticmethod
+    def getErrorCorrectPolynomial(errorCorrectLength):
+        a = QRPolynomial([1], 0);
+        for i in xrange(errorCorrectLength):
+            a = a.multiply(QRPolynomial([1, QRMath.gexp(i)], 0) )
+        return a
+
+    @staticmethod
+    def getLengthInBits(mode, type):
+        if 1 <= type and type < 10:
+            # 1 - 9
+            if mode == QRMode.MODE_NUMBER     : return 10
+            if mode == QRMode.MODE_ALPHA_NUM     : return 9
+            if mode == QRMode.MODE_8BIT_BYTE    : return 8
+            if mode == QRMode.MODE_KANJI      : return 8
+            raise Exception("mode:" + mode)
+        elif (type < 27):
+            # 10 - 26
+            if mode == QRMode.MODE_NUMBER     : return 12
+            if mode == QRMode.MODE_ALPHA_NUM     : return 11
+            if mode == QRMode.MODE_8BIT_BYTE    : return 16
+            if mode == QRMode.MODE_KANJI      : return 10
+            raise Exception("mode:" + mode)
+        elif (type < 41):
+            # 27 - 40
+            if mode == QRMode.MODE_NUMBER     : return 14
+            if mode == QRMode.MODE_ALPHA_NUM    : return 13
+            if mode == QRMode.MODE_8BIT_BYTE    : return 16
+            if mode == QRMode.MODE_KANJI      : return 12
+            raise Exception("mode:" + mode)
+        else:
+            raise Exception("type:" + type)
+
+    @staticmethod
+    def getLostPoint(qrCode):
+        moduleCount = qrCode.getModuleCount();
+        lostPoint = 0;
+        # LEVEL1
+        for row in xrange(moduleCount):
+            for col in xrange(moduleCount):
+                sameCount = 0;
+                dark = qrCode.isDark(row, col);
+                for r in xrange(-1, 2):
+                    if (row + r < 0 or moduleCount <= row + r):
+                        continue
+                    for c in xrange(-1, 2):
+                        if (col + c < 0 or moduleCount <= col + c):
+                            continue
+                        if (r == 0 and c == 0):
+                            continue
+                        if (dark == qrCode.isDark(row + r, col + c) ):
+                            sameCount+=1
+                if (sameCount > 5):
+                    lostPoint += (3 + sameCount - 5)
+        # LEVEL2
+        for row in xrange(moduleCount - 1):
+            for col in xrange(moduleCount - 1):
+                count = 0;
+                if (qrCode.isDark(row,     col    ) ): count+=1
+                if (qrCode.isDark(row + 1, col    ) ): count+=1
+                if (qrCode.isDark(row,     col + 1) ): count+=1
+                if (qrCode.isDark(row + 1, col + 1) ): count+=1
+                if (count == 0 or count == 4):
+                    lostPoint += 3
+        # LEVEL3
+        for row in xrange(moduleCount):
+            for col in xrange(moduleCount - 6):
+                if (qrCode.isDark(row, col)
+                        and not qrCode.isDark(row, col + 1)
+                        and  qrCode.isDark(row, col + 2)
+                        and  qrCode.isDark(row, col + 3)
+                        and  qrCode.isDark(row, col + 4)
+                        and not qrCode.isDark(row, col + 5)
+                        and  qrCode.isDark(row, col + 6) ):
+                    lostPoint += 40
+        for col in xrange(moduleCount):
+            for row in xrange(moduleCount - 6):
+                if (qrCode.isDark(row, col)
+                        and not qrCode.isDark(row + 1, col)
+                        and  qrCode.isDark(row + 2, col)
+                        and  qrCode.isDark(row + 3, col)
+                        and  qrCode.isDark(row + 4, col)
+                        and not qrCode.isDark(row + 5, col)
+                        and  qrCode.isDark(row + 6, col) ):
+                    lostPoint += 40
+        # LEVEL4
+        darkCount = 0;
+        for col in xrange(moduleCount):
+            for row in xrange(moduleCount):
+                if (qrCode.isDark(row, col) ):
+                    darkCount+=1
+        ratio = abs(100 * darkCount / moduleCount / moduleCount - 50) / 5
+        lostPoint += ratio * 10
+        return lostPoint
+
+class QRMath:
+    @staticmethod
+    def glog(n):
+        if (n < 1):
+            raise Exception("glog(" + n + ")")
+        return LOG_TABLE[n];
+
+    @staticmethod
+    def gexp(n):
+        while n < 0:
+            n += 255
+        while n >= 256:
+            n -= 255
+        return EXP_TABLE[n];
+
+EXP_TABLE = [x for x in xrange(256)]
+LOG_TABLE = [x for x in xrange(256)]
+for i in xrange(8):
+    EXP_TABLE[i] = 1 << i;
+for i in xrange(8, 256):
+    EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]
+for i in xrange(255):
+    LOG_TABLE[EXP_TABLE[i] ] = i
+
+class QRPolynomial:
+    def __init__(self, num, shift):
+        if (len(num) == 0):
+            raise Exception(num.length + "/" + shift)
+        offset = 0
+        while offset < len(num) and num[offset] == 0:
+            offset += 1
+        self.num = [0 for x in xrange(len(num)-offset+shift)]
+        for i in xrange(len(num) - offset):
+            self.num[i] = num[i + offset]
+
+    def get(self, index):
+        return self.num[index]
+
+    def getLength(self):
+        return len(self.num)
+
+    def multiply(self, e):
+        num = [0 for x in xrange(self.getLength() + e.getLength() - 1)];
+        for i in xrange(self.getLength()):
+            for j in xrange(e.getLength()):
+                num[i + j] ^= QRMath.gexp(QRMath.glog(self.get(i) ) + QRMath.glog(e.get(j) ) )
+        return QRPolynomial(num, 0);
+
+    def mod(self, e):
+        if (self.getLength() - e.getLength() < 0):
+            return self;
+        ratio = QRMath.glog(self.get(0) ) - QRMath.glog(e.get(0) )
+        num = [0 for x in xrange(self.getLength())]
+        for i in xrange(self.getLength()):
+            num[i] = self.get(i);
+        for i in xrange(e.getLength()):
+            num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio)
+        # recursive call
+        return QRPolynomial(num, 0).mod(e);
+
+class QRRSBlock:
+    RS_BLOCK_TABLE = [
+        # L
+        # M
+        # Q
+        # H
+
+        # 1
+        [1, 26, 19],
+        [1, 26, 16],
+        [1, 26, 13],
+        [1, 26, 9],
+
+        # 2
+        [1, 44, 34],
+        [1, 44, 28],
+        [1, 44, 22],
+        [1, 44, 16],
+
+        # 3
+        [1, 70, 55],
+        [1, 70, 44],
+        [2, 35, 17],
+        [2, 35, 13],
+
+        # 4
+        [1, 100, 80],
+        [2, 50, 32],
+        [2, 50, 24],
+        [4, 25, 9],
+
+        # 5
+        [1, 134, 108],
+        [2, 67, 43],
+        [2, 33, 15, 2, 34, 16],
+        [2, 33, 11, 2, 34, 12],
+
+        # 6
+        [2, 86, 68],
+        [4, 43, 27],
+        [4, 43, 19],
+        [4, 43, 15],
+
+        # 7
+        [2, 98, 78],
+        [4, 49, 31],
+        [2, 32, 14, 4, 33, 15],
+        [4, 39, 13, 1, 40, 14],
+
+        # 8
+        [2, 121, 97],
+        [2, 60, 38, 2, 61, 39],
+        [4, 40, 18, 2, 41, 19],
+        [4, 40, 14, 2, 41, 15],
+
+        # 9
+        [2, 146, 116],
+        [3, 58, 36, 2, 59, 37],
+        [4, 36, 16, 4, 37, 17],
+        [4, 36, 12, 4, 37, 13],
+
+        # 10
+        [2, 86, 68, 2, 87, 69],
+        [4, 69, 43, 1, 70, 44],
+        [6, 43, 19, 2, 44, 20],
+        [6, 43, 15, 2, 44, 16],
+
+        # 11
+        [4, 101, 81],
+        [1, 80, 50, 4, 81, 51],
+        [4, 50, 22, 4, 51, 23],
+        [3, 36, 12, 8, 37, 13],
+
+        # 12
+        [2, 116, 92, 2, 117, 93],
+        [6, 58, 36, 2, 59, 37],
+        [4, 46, 20, 6, 47, 21],
+        [7, 42, 14, 4, 43, 15],
+
+        # 13
+        [4, 133, 107],
+        [8, 59, 37, 1, 60, 38],
+        [8, 44, 20, 4, 45, 21],
+        [12, 33, 11, 4, 34, 12],
+
+        # 14
+        [3, 145, 115, 1, 146, 116],
+        [4, 64, 40, 5, 65, 41],
+        [11, 36, 16, 5, 37, 17],
+        [11, 36, 12, 5, 37, 13],
+
+        # 15
+        [5, 109, 87, 1, 110, 88],
+        [5, 65, 41, 5, 66, 42],
+        [5, 54, 24, 7, 55, 25],
+        [11, 36, 12],
+
+        # 16
+        [5, 122, 98, 1, 123, 99],
+        [7, 73, 45, 3, 74, 46],
+        [15, 43, 19, 2, 44, 20],
+        [3, 45, 15, 13, 46, 16],
+
+        # 17
+        [1, 135, 107, 5, 136, 108],
+        [10, 74, 46, 1, 75, 47],
+        [1, 50, 22, 15, 51, 23],
+        [2, 42, 14, 17, 43, 15],
+
+        # 18
+        [5, 150, 120, 1, 151, 121],
+        [9, 69, 43, 4, 70, 44],
+        [17, 50, 22, 1, 51, 23],
+        [2, 42, 14, 19, 43, 15],
+
+        # 19
+        [3, 141, 113, 4, 142, 114],
+        [3, 70, 44, 11, 71, 45],
+        [17, 47, 21, 4, 48, 22],
+        [9, 39, 13, 16, 40, 14],
+
+        # 20
+        [3, 135, 107, 5, 136, 108],
+        [3, 67, 41, 13, 68, 42],
+        [15, 54, 24, 5, 55, 25],
+        [15, 43, 15, 10, 44, 16],
+
+        # 21
+        [4, 144, 116, 4, 145, 117],
+        [17, 68, 42],
+        [17, 50, 22, 6, 51, 23],
+        [19, 46, 16, 6, 47, 17],
+
+        # 22
+        [2, 139, 111, 7, 140, 112],
+        [17, 74, 46],
+        [7, 54, 24, 16, 55, 25],
+        [34, 37, 13],
+
+        # 23
+        [4, 151, 121, 5, 152, 122],
+        [4, 75, 47, 14, 76, 48],
+        [11, 54, 24, 14, 55, 25],
+        [16, 45, 15, 14, 46, 16],
+
+        # 24
+        [6, 147, 117, 4, 148, 118],
+        [6, 73, 45, 14, 74, 46],
+        [11, 54, 24, 16, 55, 25],
+        [30, 46, 16, 2, 47, 17],
+
+        # 25
+        [8, 132, 106, 4, 133, 107],
+        [8, 75, 47, 13, 76, 48],
+        [7, 54, 24, 22, 55, 25],
+        [22, 45, 15, 13, 46, 16],
+
+        # 26
+        [10, 142, 114, 2, 143, 115],
+        [19, 74, 46, 4, 75, 47],
+        [28, 50, 22, 6, 51, 23],
+        [33, 46, 16, 4, 47, 17],
+
+        # 27
+        [8, 152, 122, 4, 153, 123],
+        [22, 73, 45, 3, 74, 46],
+        [8, 53, 23, 26, 54, 24],
+        [12, 45, 15, 28, 46, 16],
+
+        # 28
+        [3, 147, 117, 10, 148, 118],
+        [3, 73, 45, 23, 74, 46],
+        [4, 54, 24, 31, 55, 25],
+        [11, 45, 15, 31, 46, 16],
+
+        # 29
+        [7, 146, 116, 7, 147, 117],
+        [21, 73, 45, 7, 74, 46],
+        [1, 53, 23, 37, 54, 24],
+        [19, 45, 15, 26, 46, 16],
+
+        # 30
+        [5, 145, 115, 10, 146, 116],
+        [19, 75, 47, 10, 76, 48],
+        [15, 54, 24, 25, 55, 25],
+        [23, 45, 15, 25, 46, 16],
+
+        # 31
+        [13, 145, 115, 3, 146, 116],
+        [2, 74, 46, 29, 75, 47],
+        [42, 54, 24, 1, 55, 25],
+        [23, 45, 15, 28, 46, 16],
+
+        # 32
+        [17, 145, 115],
+        [10, 74, 46, 23, 75, 47],
+        [10, 54, 24, 35, 55, 25],
+        [19, 45, 15, 35, 46, 16],
+
+        # 33
+        [17, 145, 115, 1, 146, 116],
+        [14, 74, 46, 21, 75, 47],
+        [29, 54, 24, 19, 55, 25],
+        [11, 45, 15, 46, 46, 16],
+
+        # 34
+        [13, 145, 115, 6, 146, 116],
+        [14, 74, 46, 23, 75, 47],
+        [44, 54, 24, 7, 55, 25],
+        [59, 46, 16, 1, 47, 17],
+
+        # 35
+        [12, 151, 121, 7, 152, 122],
+        [12, 75, 47, 26, 76, 48],
+        [39, 54, 24, 14, 55, 25],
+        [22, 45, 15, 41, 46, 16],
+
+        # 36
+        [6, 151, 121, 14, 152, 122],
+        [6, 75, 47, 34, 76, 48],
+        [46, 54, 24, 10, 55, 25],
+        [2, 45, 15, 64, 46, 16],
+
+        # 37
+        [17, 152, 122, 4, 153, 123],
+        [29, 74, 46, 14, 75, 47],
+        [49, 54, 24, 10, 55, 25],
+        [24, 45, 15, 46, 46, 16],
+
+        # 38
+        [4, 152, 122, 18, 153, 123],
+        [13, 74, 46, 32, 75, 47],
+        [48, 54, 24, 14, 55, 25],
+        [42, 45, 15, 32, 46, 16],
+
+        # 39
+        [20, 147, 117, 4, 148, 118],
+        [40, 75, 47, 7, 76, 48],
+        [43, 54, 24, 22, 55, 25],
+        [10, 45, 15, 67, 46, 16],
+
+        # 40
+        [19, 148, 118, 6, 149, 119],
+        [18, 75, 47, 31, 76, 48],
+        [34, 54, 24, 34, 55, 25],
+        [20, 45, 15, 61, 46, 16]
+
+    ]
+
+    def __init__(self, totalCount, dataCount):
+        self.totalCount = totalCount
+        self.dataCount = dataCount
+
+    @staticmethod
+    def getRSBlocks(typeNumber, errorCorrectLevel):
+        rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
+        if rsBlock == None:
+            raise Exception("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel)
+        length = len(rsBlock) / 3
+        list = []
+        for i in xrange(length):
+            count = rsBlock[i * 3 + 0]
+            totalCount = rsBlock[i * 3 + 1]
+            dataCount  = rsBlock[i * 3 + 2]
+            for j in xrange(count):
+                list.append(QRRSBlock(totalCount, dataCount))
+        return list;
+
+    @staticmethod
+    def getRsBlockTable(typeNumber, errorCorrectLevel):
+        if errorCorrectLevel == QRErrorCorrectLevel.L:
+            return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
+        elif errorCorrectLevel == QRErrorCorrectLevel.M:
+            return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
+        elif errorCorrectLevel ==  QRErrorCorrectLevel.Q:
+            return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
+        elif errorCorrectLevel ==  QRErrorCorrectLevel.H:
+            return QRRSBlock.RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
+        else:
+            return None;
+
+class QRBitBuffer:
+    def __init__(self):
+        self.buffer = []
+        self.length = 0
+
+    def __repr__(self):
+        return ".".join([str(n) for n in self.buffer])
+
+    def get(self, index):
+        bufIndex = math.floor(index / 8)
+        val = ( (self.buffer[bufIndex] >> (7 - index % 8) ) & 1) == 1
+        return ( (self.buffer[bufIndex] >> (7 - index % 8) ) & 1) == 1
+
+    def put(self, num, length):
+        for i in xrange(length):
+            self.putBit( ( (num >> (length - i - 1) ) & 1) == 1)
+
+    def getLengthInBits(self):
+        return self.length
+
+    def putBit(self, bit):
+        bufIndex = self.length // 8
+        if len(self.buffer) <= bufIndex:
+            self.buffer.append(0)
+        if bit:
+            self.buffer[bufIndex] |= (0x80 >> (self.length % 8) )
+        self.length += 1
--- a/src/reportlab/graphics/barcode/usps.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/usps.py	Thu Oct 21 10:34:13 2010 +0000
@@ -199,7 +199,7 @@
             else:
                 raise ValueError, "Invalid character in input"
         check = (10 - check) % 10
-        self.encoded = self.encoded + `check` + 'S'
+        self.encoded = self.encoded + repr(check) + 'S'
         return self.encoded
 
     def decompose(self):
--- a/src/reportlab/graphics/barcode/widgets.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/barcode/widgets.py	Thu Oct 21 10:34:13 2010 +0000
@@ -54,7 +54,8 @@
         gap = AttrMapValue(isNumberOrNone, desc='Width of inter character gaps.'),
         )
 
-    barStrokeColor = barFillColor = textColor = black
+    textColor = barFillColor = black
+    barStrokeColor = None
     barStrokeWidth = 0
     _BCC = None
     def __init__(self,BCC=None,_value='',**kw):
@@ -300,7 +301,8 @@
     os.chdir(os.path.dirname(sys.argv[0]))
     if not os.path.isdir('out'):
         os.mkdir('out')
-    map(os.remove,glob.glob(os.path.join('out','*')))
+    for x in glob.glob(os.path.join('out','*')):
+        os.remove(x)
     html = ['<html><head></head><body>']
     a = html.append
     for C in (BarcodeI2of5,
--- a/src/reportlab/graphics/charts/areas.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/areas.py	Thu Oct 21 10:34:13 2010 +0000
@@ -20,7 +20,7 @@
         strokeColor = AttrMapValue(isColorOrNone, desc='Color of the plot area border.'),
         strokeWidth = AttrMapValue(isNumber, desc='Width plot area border.'),
         fillColor = AttrMapValue(isColorOrNone, desc='Color of the plot area interior.'),
-        background = AttrMapValue(isNoneOrShape, desc='Handle to background object.'),
+        background = AttrMapValue(isNoneOrShape, desc='Handle to background object e.g. Rect(0,0,width,height).'),
         debug = AttrMapValue(isNumber, desc='Used only for debugging.'),
         )
 
--- a/src/reportlab/graphics/charts/axes.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/axes.py	Thu Oct 21 10:34:13 2010 +0000
@@ -1,6 +1,5 @@
-#Copyright ReportLab Europe Ltd. 2000-2004
+#Copyright ReportLab Europe Ltd. 2000-2010
 #see license.txt for license details
-#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/axes.py
 __version__=''' $Id$ '''
 __doc__="""Collection of axes for charts.
 
@@ -32,17 +31,18 @@
 the former axes in its own coordinate system.
 """
 
-import string
-
 from reportlab.lib.validators import    isNumber, isNumberOrNone, isListOfStringsOrNone, isListOfNumbers, \
                                         isListOfNumbersOrNone, isColorOrNone, OneOf, isBoolean, SequenceOf, \
-                                        isString, EitherOr, Validator, _SequenceTypes, NoneOr, isInstanceOf
+                                        isString, EitherOr, Validator, _SequenceTypes, NoneOr, isInstanceOf, \
+                                        isNormalDate
 from reportlab.lib.attrmap import *
 from reportlab.lib import normalDate
 from reportlab.graphics.shapes import Drawing, Line, PolyLine, Group, STATE_DEFAULTS, _textBoxLimits, _rotatedBoxLimits
 from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection
 from reportlab.graphics.charts.textlabels import Label
 from reportlab.graphics.charts.utils import nextRoundNumber
+import copy
+
 
 # Helpers.
 def _findMinMaxValue(V, x, default, func, special=None):
@@ -101,7 +101,7 @@
         e = kwds.pop('end',None)
         if s is None or e is None:
             dim = getattr(getattr(axis,'joinAxis',None),'getGridDims',None)
-            if dim and callable(dim):
+            if dim and hasattr(dim,'__call__'):
                 dim = dim()
             if dim:
                 if s is None: s = dim[0]
@@ -137,6 +137,24 @@
                 axis._get_line_pos = oaglp
         return L
 
+class TickLU:
+    '''lookup special cases for tick values'''
+    def __init__(self,*T,**kwds):
+        self.accuracy = kwds.pop('accuracy',1e-8)
+        self.T = T
+    def __contains__(self,t):
+        accuracy = self.accuracy
+        for x,v in self.T:
+            if abs(x-t)<accuracy:
+                return True
+        return False
+    def __getitem__(self,t):
+        accuracy = self.accuracy
+        for x,v in self.T:
+            if abs(x-t)<self.accuracy:
+                return v
+        raise IndexError('cannot locate index %r' % t)
+
 class _AxisG(Widget):
     def _get_line_pos(self,v):
         v = self.scale(v)
@@ -180,21 +198,29 @@
             f = self.isYAxis and self._cyLine or self._cxLine
             return lambda v, s=start, e=end, f=f: f(v,s,e)
 
-    def _makeLines(self,g,start,end,strokeColor,strokeWidth,strokeDashArray,strokeLineJoin,strokeLineCap,strokeMiterLimit,parent=None):
+    def _makeLines(self,g,start,end,strokeColor,strokeWidth,strokeDashArray,strokeLineJoin,strokeLineCap,strokeMiterLimit,parent=None,exclude=[],specials={}):
         func = self._getLineFunc(start,end,parent)
         if not hasattr(self,'_tickValues'):
             self._pseudo_configure()
+        if exclude:
+            exf = self.isYAxis and (lambda l: l.y1 in exclude) or (lambda l: l.x1 in exclude)
+        else:
+            exf = None
         for t in self._tickValues:
                 L = func(t)
+                if exf and exf(L): continue
                 L.strokeColor = strokeColor
                 L.strokeWidth = strokeWidth
                 L.strokeDashArray = strokeDashArray
                 L.strokeLineJoin = strokeLineJoin
                 L.strokeLineCap = strokeLineCap
                 L.strokeMiterLimit = strokeMiterLimit
+                if t in specials:
+                    for a,v in specials[t].iteritems():
+                        setattr(L,a,v)
                 g.add(L)
 
-    def makeGrid(self,g,dim=None,parent=None):
+    def makeGrid(self,g,dim=None,parent=None,exclude=[]):
         '''this is only called by a container object'''
         c = self.gridStrokeColor
         w = self.gridStrokeWidth or 0
@@ -202,7 +228,7 @@
             s = self.gridStart
             e = self.gridEnd
             if s is None or e is None:
-                if dim and callable(dim):
+                if dim and hasattr(dim,'__call__'):
                     dim = dim()
                 if dim:
                     if s is None: s = dim[0]
@@ -213,10 +239,10 @@
             if s or e:
                 if self.isYAxis: offs = self._x
                 else: offs = self._y
-                self._makeLines(g,s-offs,e-offs,c,w,self.gridStrokeDashArray,self.gridStrokeLineJoin,self.gridStrokeLineCap,self.gridStrokeMiterLimit,parent=parent)
-        self._makeSubGrid(g,dim,parent)
+                self._makeLines(g,s-offs,e-offs,c,w,self.gridStrokeDashArray,self.gridStrokeLineJoin,self.gridStrokeLineCap,self.gridStrokeMiterLimit,parent=parent,exclude=exclude,specials=getattr(self,'_gridSpecials',{}))
+        self._makeSubGrid(g,dim,parent,exclude=[])
 
-    def _makeSubGrid(self,g,dim=None,parent=None):
+    def _makeSubGrid(self,g,dim=None,parent=None,exclude=[]):
         '''this is only called by a container object'''
         if not (getattr(self,'visibleSubGrid',0) and self.subTickNum>0): return
         c = self.subGridStrokeColor
@@ -225,7 +251,7 @@
         s = self.subGridStart
         e = self.subGridEnd
         if s is None or e is None:
-            if dim and callable(dim):
+            if dim and hasattr(dim,'__call__'):
                 dim = dim()
             if dim:
                 if s is None: s = dim[0]
@@ -238,7 +264,7 @@
             else: offs = self._y
             otv = self._calcSubTicks()
             try:
-                self._makeLines(g,s-offs,e-offs,c,w,self.subGridStrokeDashArray,self.subGridStrokeLineJoin,self.subGridStrokeLineCap,self.subGridStrokeMiterLimit,parent=parent)
+                self._makeLines(g,s-offs,e-offs,c,w,self.subGridStrokeDashArray,self.subGridStrokeLineJoin,self.subGridStrokeLineCap,self.subGridStrokeMiterLimit,parent=parent,exclude=exclude)
             finally:
                 self._tickValues = otv
 
@@ -259,10 +285,58 @@
         return acn[0]=='X' or acn[:11]=='NormalDateX'
     isXAxis = property(isXAxis)
 
-    def addAnnotations(self,g):
-        for x in getattr(self,'annotations',[]):
+    def addAnnotations(self,g,A=None):
+        if A is None: getattr(self,'annotations',[])
+        for x in A:
             g.add(x(self))
 
+    def _splitAnnotations(self):
+        A = getattr(self,'annotations',[])[:]
+        D = {}
+        for v in ('early','beforeAxis','afterAxis','beforeTicks',
+                'afterTicks','beforeTickLabels',
+                'afterTickLabels','late'):
+            R = [].append
+            P = [].append
+            for a in A:
+                if getattr(a,v,0):
+                    R(a)
+                else:
+                    P(a)
+            D[v] = R.__self__
+            A[:] = P.__self__
+        D['late'] += A
+        return D
+
+    def draw(self):
+        g = Group()
+        A = self._splitAnnotations()
+        self.addAnnotations(g,A['early'])
+
+        if self.visible:
+            self.addAnnotations(g,A['beforeAxis'])
+            g.add(self.makeAxis())
+            self.addAnnotations(g,A['afterAxis'])
+            self.addAnnotations(g,A['beforeTicks'])
+            g.add(self.makeTicks())
+            self.addAnnotations(g,A['afterTicks'])
+            self.addAnnotations(g,A['beforeTickLabels'])
+            g.add(self.makeTickLabels())
+            self.addAnnotations(g,A['afterTickLabels'])
+
+        self.addAnnotations(g,A['late'])
+        return g
+
+class CALabel(Label):
+    _attrMap = AttrMap(BASE=Label,
+        labelPosFrac = AttrMapValue(isNumber, desc='where in the category range [0,1] the labels should be anchored'),
+        )
+    def __init__(self,**kw):
+        Label.__init__(self,**kw)
+        self._setKeywords(
+                labelPosFrac = 0.5,
+                )
+
 # Category axes.
 class CategoryAxis(_AxisG):
     "Abstract category axis, unusable in itself."
@@ -337,7 +411,7 @@
         self.strokeLineCap = STATE_DEFAULTS['strokeLineCap']
         self.strokeMiterLimit = STATE_DEFAULTS['strokeMiterLimit']
 
-        self.labels = TypedPropertyCollection(Label)
+        self.labels = TypedPropertyCollection(CALabel)
         # if None, they don't get labels. If provided,
         # you need one name per data point and they are
         # used for label text.
@@ -378,19 +452,6 @@
             else:
                 self._tickValues = range(n+1)
 
-    def draw(self):
-        g = Group()
-
-        if not self.visible:
-            return g
-
-        g.add(self.makeAxis())
-        g.add(self.makeTicks())
-        g.add(self.makeTickLabels())
-        self.addAnnotations(g)
-
-        return g
-
     def _scale(self,idx):
         if self.reverseDirection: idx = self._catCount-idx-1
         return idx
@@ -425,7 +486,15 @@
             self._pseudo_configure()
         otv = self._tickValues
         if not hasattr(self,'_subTickValues'):
-            OTV = otv[:]
+            acn = self.__class__.__name__
+            if acn[:11]=='NormalDateX':
+                iFuzz = 0
+                dCnv = int
+            else:
+                iFuzz = 1e-8
+                dCnv = lambda x:x
+
+            OTV = [tv for tv in otv if getattr(tv,'_doSubTicks',1)]
             T = [].append
             nst = int(self.subTickNum)
             i = len(OTV)
@@ -439,7 +508,7 @@
                 else:
                     i >>= 1
                     dst = OTV[i+1] - OTV[i]
-                fuzz = dst*1e-8
+                fuzz = dst*iFuzz
                 vn = self._valueMin+fuzz
                 vx = self._valueMax-fuzz
                 if OTV[0]>vn: OTV.insert(0,OTV[0]-dst)
@@ -447,7 +516,7 @@
                 dst /= float(nst+1)
                 for i,x in enumerate(OTV[:-1]):
                     for j in xrange(nst):
-                        t = x+(j+1)*dst
+                        t = x+dCnv((j+1)*dst)
                         if t<=vn or t>=vx: continue
                         T(t)
                 self._subTickValues = T.__self__
@@ -604,8 +673,13 @@
                 if reverseDirection: ic = catCount-i-1
                 else: ic = i
                 if ic>=n: continue
-                x = _x + (i+0.5) * barWidth
-                label = self.labels[i]
+                label=i-catCount
+                if label in self.labels:
+                    label = self.labels[label]
+                else:
+                    label = self.labels[i]
+                lpf = label.labelPosFrac
+                x = _x + (i+lpf) * barWidth
                 label.setOrigin(x, _y)
                 label.setText(categoryNames[ic] or '')
                 g.add(label)
@@ -708,12 +782,18 @@
             labels = self.labels
             _x = self._labelAxisPos()
             _y = self._y
+
             for i in xrange(catCount):
                 if reverseDirection: ic = catCount-i-1
                 else: ic = i
                 if ic>=n: continue
-                y = _y + (i+0.5) * barWidth
-                label = labels[i]
+                label=i-catCount
+                if label in self.labels:
+                    label = self.labels[label]
+                else:
+                    label = self.labels[i]
+                lpf = label.labelPosFrac
+                y = _y + (i+lpf) * barWidth
                 label.setOrigin(_x, y)
                 label.setText(categoryNames[ic] or '')
                 g.add(label)
@@ -764,6 +844,8 @@
         valueStep = AttrMapValue(isNumberOrNone, desc='Step size used between ticks.'),
         valueSteps = AttrMapValue(isListOfNumbersOrNone, desc='List of step sizes used between ticks.'),
         avoidBoundFrac = AttrMapValue(EitherOr((isNumberOrNone,SequenceOf(isNumber,emptyOK=0,lo=2,hi=2))), desc='Fraction of interval to allow above and below.'),
+        avoidBoundSpace = AttrMapValue(EitherOr((isNumberOrNone,SequenceOf(isNumber,emptyOK=0,lo=2,hi=2))), desc='Space to allow above and below.'),
+        abf_ignore_zero = AttrMapValue(EitherOr((NoneOr(isBoolean),SequenceOf(isBoolean,emptyOK=0,lo=2,hi=2))), desc='Set to True to make the avoidBoundFrac calculations treat zero as non-special'),
         rangeRound=AttrMapValue(OneOf('none','both','ceiling','floor'),'How to round the axis limits'),
         zrangePref = AttrMapValue(isNumberOrNone, desc='Zero range axis limit preference.'),
         style = AttrMapValue(OneOf('normal','stacked','parallel_3d'),"How values are plotted!"),
@@ -789,6 +871,7 @@
         subGridStrokeMiterLimit = AttrMapValue(isNumber,desc="Grid miter limit control miter line joins"),
         subGridStart = AttrMapValue(isNumberOrNone, desc='Start of grid lines wrt axis origin'),
         subGridEnd = AttrMapValue(isNumberOrNone, desc='End of grid lines wrt axis origin'),
+        keepTickLabelsInside = AttrMapValue(isBoolean, desc='Ensure tick labels do not project beyond bounds of axis if true'),
         )
 
     def __init__(self,**kw):
@@ -839,8 +922,8 @@
                         subGridStrokeDashArray = STATE_DEFAULTS['strokeDashArray'],
                         subGridStart = None,
                         subGridEnd = None,
-
                         labels = TypedPropertyCollection(Label),
+                        keepTickLabelsInside = 0,
 
                         # how close can the ticks be?
                         minimumTickSpacing = 10,
@@ -861,6 +944,8 @@
                         valueMax = None,
                         valueStep = None,
                         avoidBoundFrac = None,
+                        avoidBoundSpace = None,
+                        abf_ignore_zero = False,
                         rangeRound = 'none',
                         zrangePref = 0,
                         style = 'normal',
@@ -969,6 +1054,8 @@
             if oMin is None: valueMin = self._cValueMin = _findMin(dataSeries,self._dataIndex,0,special=special)
             if oMax is None: valueMax = self._cValueMax = _findMax(dataSeries,self._dataIndex,0,special=special)
 
+        cMin = valueMin
+        cMax = valueMax
         forceZero = self.forceZero
         if forceZero:
             if forceZero=='near':
@@ -982,6 +1069,9 @@
         do_abf = abf and do_rr
         if not isinstance(abf,_SequenceTypes):
             abf = abf, abf
+        abfiz = getattr(self,'abf_ignore_zero', False)
+        if not isinstance(abfiz,_SequenceTypes):
+            abfiz = abfiz, abfiz
         do_rr = rangeRound is not 'none' and do_rr
         if do_rr:
             rrn = rangeRound in ['both','floor']
@@ -989,28 +1079,40 @@
         else:
             rrn = rrx = 0
 
-        go = do_rr or do_abf
+        abS = self.avoidBoundSpace
+        do_abs = abS
+        if do_abs:
+            if not isinstance(abS,_SequenceTypes):
+                abS = abS, abS
+        aL = float(self._length)
+
+        go = do_rr or do_abf or do_abs
         cache = {}
-        cMin = valueMin
-        cMax = valueMax
         iter = 0
         while go and iter<=10:
             iter += 1
             go = 0
-            if do_abf:
+            if do_abf or do_abs:
                 valueStep, T, fuzz = self._getValueStepAndTicks(valueMin, valueMax, cache)
-                i0 = valueStep*abf[0]
-                i1 = valueStep*abf[1]
+                if do_abf:
+                    i0 = valueStep*abf[0]
+                    i1 = valueStep*abf[1]
+                else:
+                    i0 = i1 = 0
+                if do_abs:
+                    sf = (valueMax-valueMin)/aL
+                    i0 = max(i0,abS[0]*sf)
+                    i1 = max(i1,abS[1]*sf)
                 if rrn: v = T[0]
                 else: v = valueMin
                 u = cMin-i0
-                if abs(v)>fuzz and v>=u+fuzz:
+                if (abfiz[0] or abs(v)>fuzz) and v>=u+fuzz:
                     valueMin = u
                     go = 1
                 if rrx: v = T[-1]
                 else: v = valueMax
                 u = cMax+i1
-                if abs(v)>fuzz and v<=u-fuzz:
+                if (abfiz[1] or abs(v)>fuzz) and v<=u-fuzz:
                     valueMax = u
                     go = 1
 
@@ -1159,9 +1261,15 @@
         else:
             sk = []
 
-        i = 0
-        for tick in self._tickValues:
-            if f and labels[i].visible:
+        nticks = len(self._tickValues)
+        nticks1 = nticks - 1
+        for i,tick in enumerate(self._tickValues):
+            label = i-nticks
+            if label in labels:
+                label = labels[label]
+            else:
+                label = labels[i]
+            if f and label.visible:
                 v = self.scale(tick)
                 if sk:
                     for skv in sk:
@@ -1180,7 +1288,7 @@
                             txt = f[i]
                         else:
                             txt = ''
-                    elif callable(f):
+                    elif hasattr(f,'__call__'):
                         if isinstance(f,TickLabeller):
                             txt = f(self,t)
                         else:
@@ -1188,25 +1296,24 @@
                     else:
                         raise ValueError, 'Invalid labelTextFormat %s' % f
                     if post: txt = post % txt
-                    label = labels[i]
                     pos[d] = v
                     label.setOrigin(*pos)
                     label.setText(txt)
+
+                    #special property to ensure a label doesn't project beyond the bounds of an x-axis
+                    if self.keepTickLabelsInside: 
+                        if isinstance(self, XValueAxis):  #not done yet for y axes
+                            a_x = self._x
+                            if not i:  #first one
+                                x0, y0, x1, y1 = label.getBounds()
+                                if x0 < a_x:
+                                    label = label.clone(dx=label.dx + a_x - x0)
+                            if i==nticks1:  #final one
+                                a_x1 = a_x +self._length
+                                x0, y0, x1, y1 = label.getBounds()
+                                if x1 > a_x1:
+                                    label=label.clone(dx=label.dx-x1+a_x1)
                     g.add(label)
-            i += 1
-
-        return g
-
-    def draw(self):
-        g = Group()
-
-        if not self.visible:
-            return g
-
-        g.add(self.makeAxis())
-        g.add(self.makeTicks())
-        g.add(self.makeTickLabels())
-        self.addAnnotations(g)
 
         return g
 
@@ -1376,7 +1483,8 @@
         dayOfWeekName = AttrMapValue(SequenceOf(isString,emptyOK=0,lo=7,hi=7), desc='Weekday names.'),
         monthName = AttrMapValue(SequenceOf(isString,emptyOK=0,lo=12,hi=12), desc='Month names.'),
         dailyFreq = AttrMapValue(isBoolean, desc='True if we are to assume daily data to be ticked at end of month.'),
-        specifiedTickDates = AttrMapValue(NoneOr(SequenceOf(isInstanceOf(normalDate.ND))), desc='Actual tick values to use; no calculations done'),
+        specifiedTickDates = AttrMapValue(NoneOr(SequenceOf(isNormalDate)), desc='Actual tick values to use; no calculations done'),
+        specialTickClear = AttrMapValue(isBoolean, desc='clear rather than delete close ticks when forced first/end dates'),
         )
 
     _valueClass = normalDate.ND
@@ -1395,6 +1503,7 @@
         self.dayOfWeekName = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
         self.monthName = ['January', 'February', 'March', 'April', 'May', 'June', 'July',
                             'August', 'September', 'October', 'November', 'December']
+        self.specialTickClear = 0
         self.valueSteps = self.specifiedTickDates = None
 
     def _scalar2ND(self, x):
@@ -1424,10 +1533,15 @@
         """
         axisLength = self._length
         formatter = self._dateFormatter
+        if isinstance(formatter,TickLabeller):
+            def formatter(tick):
+                return self._dateFormatter(self,tick)
+        firstDate = xVals[0]
+        endDate = xVals[-1]
         labels = self.labels
         fontName, fontSize, leading = labels.fontName, labels.fontSize, labels.leading
         textAnchor, boxAnchor, angle = labels.textAnchor, labels.boxAnchor, labels.angle
-        RBL = _textBoxLimits(string.split(formatter(xVals[0]),'\n'),fontName,
+        RBL = _textBoxLimits(formatter(firstDate).split('\n'),fontName,
                     fontSize,leading or 1.2*fontSize,textAnchor,boxAnchor)
         RBL = _rotatedBoxLimits(RBL[0],RBL[1],RBL[2],RBL[3], angle)
         xLabelW = RBL[1]-RBL[0]
@@ -1440,25 +1554,35 @@
         labels = []
         maximumTicks = self.maximumTicks
 
+        if self.specifiedTickDates:
+            VC = self._valueClass
+            ticks = [VC(x) for x in self.specifiedTickDates]
+            labels = [formatter(d) for d in ticks]
+            if self.forceFirstDate and firstDate==ticks[0] and (axisLength/float(ticks[-1]-ticks[0]))*(ticks[1]-ticks[0])<=W:
+                if self.specialTickClear:
+                    labels[1] = ''
+                else:
+                    del ticks[1], labels[1]
+            if self.forceEndDate and endDate==ticks[-1] and (axisLength/float(ticks[-1]-ticks[0]))*(ticks[-1]-ticks[-2])<=W:
+                if self.specialTickClear:
+                    labels[-2] = ''
+                else:
+                    del ticks[-2], labels[-2]
+            return ticks, labels
+
         def addTick(i, xVals=xVals, formatter=formatter, ticks=ticks, labels=labels):
             ticks.insert(0,xVals[i])
             labels.insert(0,formatter(xVals[i]))
 
-        if self.specifiedTickDates:
-            ticks = self.specifiedTickDates[:]
-            return ticks,[formatter(d) for d in ticks]
-
         #AR 20060619 - first we try the approach where the user has explicitly
         #specified the days of year to be ticked.  Other explicit routes may
         #be added.
         if self.forceDatesEachYear:
             forcedPartialDates = map(parseDayAndMonth, self.forceDatesEachYear)
             #generate the list of dates in the range.
-            firstDate = xVals[0]
-            lastDate = xVals[-1]
-            #print 'dates range from %s to %s' % (firstDate, lastDate)
+            #print 'dates range from %s to %s' % (firstDate, endDate)
             firstYear = firstDate.year()
-            lastYear = lastDate.year()
+            lastYear = endDate.year()
             ticks = []
             labels = []
             yyyy = firstYear
@@ -1467,18 +1591,28 @@
             while yyyy <= lastYear:
                 for (dd, mm) in forcedPartialDates:
                     theDate = normalDate.ND((yyyy, mm, dd))
-                    if theDate >= firstDate and theDate <= lastDate:
+                    if theDate >= firstDate and theDate <= endDate:
                         ticks.append(theDate)
                         labels.append(formatter(theDate))
                 yyyy += 1
 
             #first and last may still be forced in.
-            if self.forceFirstDate and xVals[0] <> ticks[0]:
+            if self.forceFirstDate and firstDate!=ticks[0]:
                 ticks.insert(0, firstDate)
                 labels.insert(0,formatter(firstDate))
-            if self.forceEndDate and xVals[-1] <> ticks[-1]:
-                ticks.append(lastDate)
-                labels.append(formatter(lastDate))
+                if (axisLength/float(ticks[-1]-ticks[0]))*(ticks[1]-ticks[0])<=W:
+                    if self.specialTickClear:
+                        labels[1] = ''
+                    else:
+                        del ticks[1], labels[1]
+            if self.forceEndDate and endDate!=ticks[-1]:
+                ticks.append(endDate)
+                labels.append(formatter(endDate))
+                if (axisLength/float(ticks[-1]-ticks[0]))*(ticks[-1]-ticks[-2])<=W:
+                    if self.specialTickClear:
+                        labels[-2] = ''
+                    else:
+                        del ticks[-2], labels[-2]
 
             #print 'xVals found on forced dates =', ticks
             return ticks, labels
@@ -1490,28 +1624,37 @@
             if k<=maximumTicks and k*W <= axisLength:
                 i = n-1
                 if self.niceMonth:
-                    j = xVals[-1].month() % (d<=12 and d or 12)
+                    j = endDate.month() % (d<=12 and d or 12)
                     if j:
-                        if self.forceEndDate: addTick(i)
-                        i = i - j
+                        if self.forceEndDate:
+                            addTick(i)
+                            ticks[0]._doSubTicks=0
+                        i -= j
 
                 #weird first date ie not at end of month
                 try:
-                    wfd = xVals[0].month() == xVals[1].month()
+                    wfd = firstDate.month() == xVals[1].month()
                 except:
                     wfd = 0
 
                 while i>=wfd:
                     addTick(i)
-                    i = i - d
+                    i -= d
 
-                if self.forceFirstDate and ticks[0] != xVals[0]:
+                if self.forceFirstDate and ticks[0]!=firstDate:
                     addTick(0)
-                    if (axisLength/(ticks[-1]-ticks[0]))*(ticks[1]-ticks[0])<=w:
-                        del ticks[1], labels[1]
+                    ticks[0]._doSubTicks=0
+                    if (axisLength/float(ticks[-1]-ticks[0]))*(ticks[1]-ticks[0])<=W:
+                        if self.specialTickClear:
+                            labels[1] = ''
+                        else:
+                            del ticks[1], labels[1]
                 if self.forceEndDate and self.niceMonth and j:
-                    if (axisLength/(ticks[-1]-ticks[0]))*(ticks[-1]-ticks[-2])<=w:
-                        del ticks[-2], labels[-2]
+                    if (axisLength/float(ticks[-1]-ticks[0]))*(ticks[-1]-ticks[-2])<=W:
+                        if self.specialTickClear:
+                            labels[-2] = ''
+                        else:
+                            del ticks[-2], labels[-2]
                 try:
                     if labels[0] and labels[0]==labels[1]:
                         del ticks[1], labels[1]
@@ -1657,7 +1800,8 @@
         leftAxisPercent = AttrMapValue(isBoolean, desc='When true add percent sign to label values.'),
         leftAxisOrigShiftIPC = AttrMapValue(isNumber, desc='Lowest label shift interval ratio.'),
         leftAxisOrigShiftMin = AttrMapValue(isNumber, desc='Minimum amount to shift.'),
-        leftAxisSkipLL0 = AttrMapValue(EitherOr((isBoolean,isListOfNumbers)), desc='Skip/Keep lowest tick label when true/false.\nOr skiplist')
+        leftAxisSkipLL0 = AttrMapValue(EitherOr((isBoolean,isListOfNumbers)), desc='Skip/Keep lowest tick label when true/false.\nOr skiplist'),
+        labelVOffset = AttrMapValue(isNumber, desc='add this to the labels'),
         )
 
     def __init__(self,**kw):
@@ -1666,7 +1810,7 @@
         self.leftAxisPercent = 1
         self.leftAxisOrigShiftIPC = 0.15
         self.leftAxisOrigShiftMin = 12
-        self.leftAxisSkipLL0 = 0
+        self.leftAxisSkipLL0 = self.labelVOffset = 0
         self.valueSteps = None
 
     def _rangeAdjust(self):
@@ -1693,7 +1837,7 @@
                     y1 = 0
             self._valueMin, self._valueMax = y1, y2
 
-        T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep)
+        T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep, labelVOffset=self.labelVOffset)
         abf = self.avoidBoundFrac
         if abf:
             i1 = (T[1]-T[0])
@@ -1706,11 +1850,11 @@
             _x = getattr(self,'_cValueMax',T[-1])
             if _n - T[0] < i0: self._valueMin = self._valueMin - i0
             if T[-1]-_x < i1: self._valueMax = self._valueMax + i1
-            T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep)
+            T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep, labelVOffset=self.labelVOffset)
 
         self._valueMin = T[0]
         self._valueMax = T[-1]
-        self._tickValues = self.valueSteps = T
+        self._tickValues = T
         if self.labelTextFormat is None:
             self._labelTextFormat = L
         else:
@@ -1765,6 +1909,7 @@
 
 def sample1():
     "Sample drawing containing two unconnected axes."
+    from reportlab.graphics.shapes import _baseGFontNameB
     drawing = Drawing(400, 200)
     data = [(10, 20, 30, 42)]
     xAxis = XCategoryAxis()
@@ -1774,7 +1919,7 @@
     xAxis.labels.boxAnchor = 'n'
     xAxis.labels[3].dy = -15
     xAxis.labels[3].angle = 30
-    xAxis.labels[3].fontName = 'Times-Bold'
+    xAxis.labels[3].fontName = _baseGFontNameB
     yAxis = YValueAxis()
     yAxis.setPosition(50, 50, 125)
     yAxis.configure(data)
--- a/src/reportlab/graphics/charts/barcharts.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/barcharts.py	Thu Oct 21 10:34:13 2010 +0000
@@ -14,7 +14,7 @@
 from reportlab.lib import colors
 from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isString,\
             isListOfStrings, SequenceOf, isBoolean, isNoneOrShape, isStringOrNone,\
-            NoneOr, isListOfNumbersOrNone
+            NoneOr, isListOfNumbersOrNone, EitherOr, OneOf
 from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol
 from reportlab.lib.formatters import Formatter
 from reportlab.lib.attrmap import AttrMap, AttrMapValue
@@ -32,9 +32,9 @@
         fillColor = AttrMapValue(isColorOrNone, desc='Color of the bar interior area.'),
         strokeWidth = AttrMapValue(isNumber, desc='Width of the bar border.'),
         strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
-        symbol = AttrMapValue(None, desc='A widget to be used instead of a normal bar.'),
+        symbol = AttrMapValue(None, desc='A widget to be used instead of a normal bar.',advancedUsage=1),
         name = AttrMapValue(isString, desc='Text to be associated with a bar (eg seriesname)'),
-        swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ..."),
+        swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ...",advancedUsage=1),
         )
 
     def __init__(self):
@@ -49,7 +49,7 @@
     "Abstract base class, unusable by itself."
 
     _attrMap = AttrMap(BASE=PlotArea,
-        useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'),
+        useAbsolute = AttrMapValue(EitherOr((isBoolean,EitherOr((isString,isNumber)))), desc='Flag to use absolute spacing values; use string of gsb for finer control\n(g=groupSpacing,s=barSpacing,b=barWidth). ',advancedUsage=1),
         barWidth = AttrMapValue(isNumber, desc='The width of an individual bar.'),
         groupSpacing = AttrMapValue(isNumber, desc='Width between groups of bars.'),
         barSpacing = AttrMapValue(isNumber, desc='Width between individual bars.'),
@@ -59,11 +59,14 @@
         data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
         barLabels = AttrMapValue(None, desc='Handle to the list of bar labels.'),
         barLabelFormat = AttrMapValue(None, desc='Formatting string or function used for bar labels.'),
-        barLabelCallOut = AttrMapValue(None, desc='Callout function(label)\nlabel._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)'),
+        barLabelCallOut = AttrMapValue(None, desc='Callout function(label)\nlabel._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)',advancedUsage=1),
         barLabelArray = AttrMapValue(None, desc='explicit array of bar label values, must match size of data if present.'),
-        reversePlotOrder = AttrMapValue(isBoolean, desc='If true, reverse common category plot order.'),
-        naLabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for N/A values.'),
+        reversePlotOrder = AttrMapValue(isBoolean, desc='If true, reverse common category plot order.',advancedUsage=1),
+        naLabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for N/A values.',advancedUsage=1),
         annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
+        categoryLabelBarSize = AttrMapValue(isNumber, desc='width to leave for a category label to go between categories.'),
+        categoryLabelBarOrder = AttrMapValue(OneOf('first','last','auto'), desc='where any label bar should appear first/last'),
+        barRecord = AttrMapValue(None, desc='callable(bar,label=labelText,value=value,**kwds) to record bar information', advancedUsage=1),
         )
 
     def makeSwatchSample(self, rowNo, x, y, width, height):
@@ -224,31 +227,63 @@
         data = self.data
         seriesCount = self._seriesCount = len(data)
         self._rowLength = rowLength = max(map(len,data))
-        groupSpacing, barSpacing, barWidth = self.groupSpacing, self.barSpacing, self.barWidth
-        style = self.categoryAxis.style
+        wG = self.groupSpacing
+        barSpacing = self.barSpacing
+        barWidth = self.barWidth
+        clbs = getattr(self,'categoryLabelBarSize',0)
+        clbo = getattr(self,'categoryLabelBarOrder','auto')
+        if clbo=='auto': clbo = flipXY and 'last' or 'first'
+        clbo = clbo=='first'
+        style = cA.style
         if style=='parallel':
-            groupWidth = groupSpacing+(seriesCount*barWidth)+(seriesCount-1)*barSpacing
-            bGap = barWidth+barSpacing
+            wB = seriesCount*barWidth
+            wS = (seriesCount-1)*barSpacing
+            bGapB = barWidth
+            bGapS = barSpacing
         else:
             accum = rowLength*[0]
-            groupWidth = groupSpacing+barWidth
-            bGap = 0
-        self._groupWidth = groupWidth
+            wB = barWidth
+            wS = bGapB = bGapS = 0
+        self._groupWidth = groupWidth = wG+wB+wS
         useAbsolute = self.useAbsolute
 
         if useAbsolute:
-            # bar dimensions are absolute
-            normFactor = 1.0
+            if not isinstance(useAbsolute,str):
+                useAbsolute = 7 #all three are fixed
+            else:
+                useAbsolute = 0 + 1*('b' in useAbsolute)+2*('g' in useAbsolute)+4*('s' in useAbsolute)
         else:
-            # bar dimensions are normalized to fit.  How wide
-            # notionally is one group of bars?
-            availWidth = cScale(0)[1]
-            normFactor = availWidth/float(groupWidth)
-            if self.debug:
-                print '%d series, %d points per series' % (seriesCount, self._rowLength)
-                print 'width = %d group + (%d bars * %d barWidth) + (%d gaps * %d interBar) = %d total' % (
-                    groupSpacing, seriesCount, barWidth,
-                    seriesCount-1, barSpacing, groupWidth)
+            useAbsolute = 0
+
+        aW0 = float(cScale(0)[1])
+        aW = aW0 - clbs
+
+        if useAbsolute==0: #case 0 all are free
+            self._normFactor = fB = fG = fS = aW/groupWidth
+        elif useAbsolute==7:    #all fixed
+            fB = fG = fS = 1.0
+            _cscale = cA._scale
+        elif useAbsolute==1: #case 1 barWidth is fixed
+            fB = 1.0
+            fG = fS = (aW-wB)/(wG+wS)
+        elif useAbsolute==2: #groupspacing is fixed
+            fG=1.0
+            fB = fS = (aW-wG)/(wB+wS)
+        elif useAbsolute==3: #groupspacing & barwidth are fixed
+            fB = fG = 1.0
+            fS = (aW-wG-wB)/wS
+        elif useAbsolute==4: #barspacing is fixed
+            fS=1.0
+            fG = fB = (aW-wS)/(wG+wB)
+        elif useAbsolute==5: #barspacing & barWidth are fixed
+            fS = fB = 1.0
+            fG = (aW-wB-wS)/wG
+        elif useAbsolute==6: #barspacing & groupspacing are fixed
+            fS = fG = 1
+            fB = (aW-wS-wG)/wB
+        self._normFactorB = fB
+        self._normFactorG = fG
+        self._normFactorS = fS
 
         # 'Baseline' correction...
         vA = self.valueAxis
@@ -262,30 +297,39 @@
             baseLine = vScale(vM)
         self._baseLine = baseLine
 
-        COLUMNS = range(max(map(len,data)))
-        if useAbsolute:
-            _cScale = cA._scale
-
-        self._normFactor = normFactor
-        width = self.barWidth*normFactor
+        nC = max(map(len,data))
+
+        width = barWidth*fB
+        offs = 0.5*wG*fG
+        bGap = bGapB*fB+bGapS*fS
+
+        if clbs:
+            if clbo: #the lable bar comes first
+                lbpf = (offs+clbs/6.0)/aW0
+                offs += clbs
+            else:
+                lbpf = (offs+wB*fB+wS*fS+clbs/6.0)/aW0
+            cA.labels.labelPosFrac = lbpf
+
         self._barPositions = []
         reversePlotOrder = self.reversePlotOrder
-        for rowNo in range(seriesCount):
+        for rowNo in xrange(seriesCount):
             barRow = []
             if reversePlotOrder:
                 xVal = seriesCount-1 - rowNo
             else:
                 xVal = rowNo
-            xVal = 0.5*groupSpacing+xVal*bGap
-            for colNo in COLUMNS:
-                datum = data[rowNo][colNo]
+            xVal = offs + xVal*bGap
+            row = data[rowNo]
+            for colNo in xrange(nC):
+                datum = row[colNo]
 
                 # Ufff...
-                if useAbsolute:
-                    x = groupWidth*_cScale(colNo) + xVal + org
+                if useAbsolute==7:
+                    x = groupWidth*_cscale(colNo) + xVal + org
                 else:
-                    (g, gW) = cScale(colNo)
-                    x = g + normFactor*xVal
+                    (g, _) = cScale(colNo)
+                    x = g + xVal
 
                 if datum is None:
                     height = None
@@ -302,7 +346,7 @@
                     if -1e-8<height<=1e-8:
                         height = 1e-8
                         if datum<-1e-8: height = -1e-8
-                barRow.append(flipXY and (y,x,height,width) or (x, y, width, height))
+                barRow.append(flipXY and (y,x,height,width) or (x,y,width,height))
 
             self._barPositions.append(barRow)
 
@@ -315,7 +359,7 @@
             labelText = self.barLabelArray[rowNo][colNo]
         elif type(labelFmt) is str:
             labelText = labelFmt % self.data[rowNo][colNo]
-        elif callable(labelFmt):
+        elif hasattr(labelFmt,'__call__'):
             labelText = labelFmt(self.data[rowNo][colNo])
         else:
             msg = "Unknown formatter type %s, expected string or function" % labelFmt
@@ -426,15 +470,15 @@
     def _makeBars(self,g,lg):
         lenData = len(self.data)
         bars = self.bars
-        for rowNo in range(lenData):
+        br = getattr(self,'barRecord',None)
+        for rowNo in xrange(lenData):
             row = self._barPositions[rowNo]
             styleCount = len(bars)
             styleIdx = rowNo % styleCount
             rowStyle = bars[styleIdx]
             for colNo in range(len(row)):
-                barPos = row[colNo]
-                style = bars.has_key((styleIdx,colNo)) and bars[(styleIdx,colNo)] or rowStyle
-                (x, y, width, height) = barPos
+                style = (styleIdx,colNo) in bars and bars[(styleIdx,colNo)] or rowStyle
+                (x, y, width, height) = row[colNo]
                 if None in (width,height):
                     self._addNABarLabel(lg,rowNo,colNo,x,y,width,height)
                     continue
@@ -455,9 +499,93 @@
                     g.add(symbol)
                 elif abs(width)>1e-7 and abs(height)>=1e-7 and (style.fillColor is not None or style.strokeColor is not None):
                     self._makeBar(g,x,y,width,height,rowNo,style)
+                    if br: br(g.contents[-1],label=self._getLabelText(rowNo,colNo),value=self.data[rowNo][colNo],rowNo=rowNo,colNo=colNo)
 
                 self._addBarLabel(lg,rowNo,colNo,x,y,width,height)
 
+    def _computeLabelPosition(self, text, label, rowNo, colNo, x, y, width, height):
+        if label.visible:
+            labelWidth = stringWidth(text, label.fontName, label.fontSize)
+            flipXY = self._flipXY
+            if flipXY:
+                y0, x0, pm = self._labelXY(label,y,x,height,width)
+            else:
+                x0, y0, pm = self._labelXY(label,x,y,width,height)
+            fixedEnd = getattr(label,'fixedEnd', None)
+            if fixedEnd is not None:
+                v = fixedEnd._getValue(self,pm)
+                x00, y00 = x0, y0
+                if flipXY:
+                    x0 = v
+                else:
+                    y0 = v
+            else:
+                if flipXY:
+                    x00 = x0
+                    y00 = y+height/2.0
+                else:
+                    x00 = x+width/2.0
+                    y00 = y0
+            fixedStart = getattr(label,'fixedStart', None)
+            if fixedStart is not None:
+                v = fixedStart._getValue(self,pm)
+                if flipXY:
+                    x00 = v
+                else:
+                    y00 = v
+
+            if pm<0:
+                if flipXY:
+                    dx = -2*label.dx
+                    dy = 0
+                else:
+                    dy = -2*label.dy
+                    dx = 0
+            else:
+                dy = dx = 0
+            label.setOrigin(x0+dx, y0+dy)
+            label.setText(text)
+            return pm,label.getBounds()
+
+    def _computeSimpleBarLabelPositions(self):
+        """Information function, can be called by charts which want to mess with labels"""
+        cA, vA = self.categoryAxis, self.valueAxis
+        if vA: ovAjA, vA.joinAxis = vA.joinAxis, cA
+        if cA: ocAjA, cA.joinAxis = cA.joinAxis, vA
+        if self._flipXY:
+            cA.setPosition(self._drawBegin(self.x,self.width), self.y, self.height)
+        else:
+            cA.setPosition(self.x, self._drawBegin(self.y,self.height), self.width)
+        cA.configure(self._configureData)
+        self.calcBarPositions()
+
+        lenData = len(self.data)
+        bars = self.bars
+        R = [].append
+        for rowNo in xrange(lenData):
+            row = self._barPositions[rowNo]
+            C = [].append
+            for colNo in range(len(row)):
+                x, y, width, height = row[colNo]
+                if None in (width,height):
+                    na = self.naLabel
+                    if na and na.text:
+                        na = copy.copy(na)
+                        v = self.valueAxis._valueMax<=0 and -1e-8 or 1e-8
+                        if width is None: width = v
+                        if height is None: height = v
+                        C(self._computeLabelPosition(na.text, na, rowNo, colNo, x, y, width, height))
+                    else:
+                        C(None)
+                else:
+                    text = self._getLabelText(rowNo,colNo)
+                    if text:
+                        C(self._computeLabelPosition(text, self.barLabels[(rowNo, colNo)], rowNo, colNo, x, y, width, height))
+                    else:
+                        C(None)
+            R(C.__self__)
+        return R.__self__
+
     def makeBars(self):
         g = Group()
         lg = Group()
@@ -1953,7 +2081,7 @@
     "Simple bar chart with absolute spacing."
 
     def __init__(self,width=400,height=200,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         bc = HorizontalBarChart()
         bc.x = 50
         bc.y = 50
--- a/src/reportlab/graphics/charts/doughnut.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/doughnut.py	Thu Oct 21 10:34:13 2010 +0000
@@ -157,24 +157,25 @@
             whichWay = -1
 
         g  = Group()
-        sn = 0
 
         startAngle = self.startAngle #% 360
         styleCount = len(self.slices)
         if type(self.data[0]) in (ListType, TupleType):
             #multi-series doughnut
             iradius = (self.height/5.0)/len(self.data)
-            for series in normData:
-                i = 0
-                for angle in series:
+            for sn,series in enumerate(normData):
+                for i,angle in enumerate(series):
                     endAngle = (startAngle + (angle * whichWay)) #% 360
-                    if abs(startAngle-endAngle)>=1e-5:
-                        if startAngle < endAngle:
-                            a1 = startAngle
-                            a2 = endAngle
-                        else:
-                            a1 = endAngle
-                            a2 = startAngle
+                    if abs(startAngle-endAngle)<1e-5:
+                        startAngle = endAngle
+                        continue
+                    if startAngle < endAngle:
+                        a1 = startAngle
+                        a2 = endAngle
+                    else:
+                        a1 = endAngle
+                        a2 = startAngle
+                    startAngle = endAngle
 
                     #if we didn't use %stylecount here we'd end up with the later sectors
                     #all having the default style
@@ -201,7 +202,6 @@
                     theSector.strokeDashArray = sectorStyle.strokeDashArray
 
                     g.add(theSector)
-                    startAngle = endAngle
 
                     text = self.getSeriesName(i,'')
                     if text:
@@ -211,22 +211,22 @@
                         labelX = centerx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius)
                         labelY = centery + (0.5 * self.height * sin(aveAngleRadians) * labelRadius)
                         g.add(_addWedgeLabel(self,text,averageAngle,labelX,labelY,sectorStyle))
-                    i += 1
-                sn += 1
 
         else:
-            i = 0
             #single series doughnut
             iradius = self.height/5.0
-            for angle in normData:
+            for i,angle in enumerate(normData):
                 endAngle = (startAngle + (angle * whichWay)) #% 360
-                if abs(startAngle-endAngle)>=1e-5:
-                    if startAngle < endAngle:
-                        a1 = startAngle
-                        a2 = endAngle
-                    else:
-                        a1 = endAngle
-                        a2 = startAngle
+                if abs(startAngle-endAngle)<1e-5:
+                    startAngle = endAngle
+                    continue
+                if startAngle < endAngle:
+                    a1 = startAngle
+                    a2 = endAngle
+                else:
+                    a1 = endAngle
+                    a2 = startAngle
+                startAngle = endAngle
 
                 #if we didn't use %stylecount here we'd end up with the later sectors
                 #all having the default style
@@ -270,8 +270,6 @@
 
                     g.add(theLabel)
 
-                startAngle = endAngle
-                i += 1
 
         return g
 
--- a/src/reportlab/graphics/charts/legends.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/legends.py	Thu Oct 21 10:34:13 2010 +0000
@@ -18,6 +18,7 @@
 from reportlab.graphics.charts.areas import PlotArea
 from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol
 from reportlab.lib.utils import isSeqType, find_locals
+from reportlab.graphics.shapes import _baseGFontName
 
 def _transMax(n,A):
     X = n*[0]
@@ -89,6 +90,8 @@
         fillColor = AttrMapValue(isColorOrNone, desc="fontColor"),
         underlines = AttrMapValue(EitherOr((NoneOr(isInstanceOf(Line)),SequenceOf(isInstanceOf(Line),emptyOK=0,lo=0,hi=0x7fffffff))), desc="underline definitions"),
         overlines = AttrMapValue(EitherOr((NoneOr(isInstanceOf(Line)),SequenceOf(isInstanceOf(Line),emptyOK=0,lo=0,hi=0x7fffffff))), desc="overline definitions"),
+        dx = AttrMapValue(isNumber, desc="x offset from default position"),
+        dy = AttrMapValue(isNumber, desc="y offset from default position"),
         )
 
 class LegendCallout:
@@ -102,12 +105,12 @@
         L = find_locals(lambda L: L.get('self',None) is legend and L or None)
         return tuple([getattr(self,a,L[a]) for a in args])
 
-    def __call__(self,legend,g,thisx,y,(col,name)):
-        pass
+    def __call__(self,legend,g,thisx,y,colName):
+        col, name = colName
 
 class LegendSwatchCallout(LegendCallout):
-    def __call__(self,legend,g,thisx,y,i,(col,name),swatch):
-        pass
+    def __call__(self,legend,g,thisx,y,i,colName,swatch):
+        col, name = colName
 
 class LegendColEndCallout(LegendCallout):
     def __call__(self,legend, g, x, xt, y, width, lWidth):
@@ -133,9 +136,9 @@
         deltax = AttrMapValue(isNumberOrNone, desc="x-distance between neighbouring swatches"),
         deltay = AttrMapValue(isNumberOrNone, desc="y-distance between neighbouring swatches"),
         dxTextSpace = AttrMapValue(isNumber, desc="Distance between swatch rectangle and text"),
-        autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None"),
-        autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None"),
-        yGap = AttrMapValue(isNumber, desc="Additional gap between rows"),
+        autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None",advancedUsage=1),
+        autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None",advancedUsage=1),
+        yGap = AttrMapValue(isNumber, desc="Additional gap between rows",advancedUsage=1),
         dx = AttrMapValue(isNumber, desc="Width of swatch rectangle"),
         dy = AttrMapValue(isNumber, desc="Height of swatch rectangle"),
         columnMaximum = AttrMapValue(isNumber, desc="Max. number of items per column"),
@@ -143,22 +146,24 @@
         colorNamePairs = AttrMapValue(None, desc="List of color/name tuples (color can also be widget)"),
         fontName = AttrMapValue(isString, desc="Font name of the strings"),
         fontSize = AttrMapValue(isNumber, desc="Font size of the strings"),
-        fillColor = AttrMapValue(isColorOrNone, desc=""),
+        fillColor = AttrMapValue(isColorOrNone, desc="swatches filling color"),
         strokeColor = AttrMapValue(isColorOrNone, desc="Border color of the swatches"),
         strokeWidth = AttrMapValue(isNumber, desc="Width of the border color of the swatches"),
-        swatchMarker = AttrMapValue(NoneOr(AutoOr(isSymbol)), desc="None, Auto() or makeMarker('Diamond') ..."),
-        callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))"),
+        swatchMarker = AttrMapValue(NoneOr(AutoOr(isSymbol)), desc="None, Auto() or makeMarker('Diamond') ...",advancedUsage=1),
+        callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))",advancedUsage=1),
         boxAnchor = AttrMapValue(isBoxAnchor,'Anchor point for the legend area'),
-        variColumn = AttrMapValue(isBoolean,'If true column widths may vary (default is false)'),
-        dividerLines = AttrMapValue(OneOf(0,1,2,3,4,5,6,7),'If 1 we have dividers between the rows | 2 for extra top | 4 for bottom'),
-        dividerWidth = AttrMapValue(isNumber, desc="dividerLines width"),
-        dividerColor = AttrMapValue(isColorOrNone, desc="dividerLines color"),
-        dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'),
-        dividerOffsX = AttrMapValue(SequenceOf(isNumber,emptyOK=0,lo=2,hi=2), desc='divider lines X offsets'),
-        dividerOffsY = AttrMapValue(isNumber, desc="dividerLines Y offset"),
-        colEndCallout = AttrMapValue(None, desc="a user callout(self,g, x, xt, y,width, lWidth)"),
+        variColumn = AttrMapValue(isBoolean,'If true column widths may vary (default is false)',advancedUsage=1),
+        dividerLines = AttrMapValue(OneOf(0,1,2,3,4,5,6,7),'If 1 we have dividers between the rows | 2 for extra top | 4 for bottom',advancedUsage=1),
+        dividerWidth = AttrMapValue(isNumber, desc="dividerLines width",advancedUsage=1),
+        dividerColor = AttrMapValue(isColorOrNone, desc="dividerLines color",advancedUsage=1),
+        dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.',advancedUsage=1),
+        dividerOffsX = AttrMapValue(SequenceOf(isNumber,emptyOK=0,lo=2,hi=2), desc='divider lines X offsets',advancedUsage=1),
+        dividerOffsY = AttrMapValue(isNumber, desc="dividerLines Y offset",advancedUsage=1),
+        colEndCallout = AttrMapValue(None, desc="a user callout(self,g, x, xt, y,width, lWidth)",advancedUsage=1),
         subCols = AttrMapValue(None,desc="subColumn properties"),
-        swatchCallout = AttrMapValue(None, desc="a user swatch callout(self,g,x,y,i,(col,name),swatch)"),
+        swatchCallout = AttrMapValue(None, desc="a user swatch callout(self,g,x,y,i,(col,name),swatch)",advancedUsage=1),
+        swdx = AttrMapValue(isNumber, desc="x position adjustment for the swatch"),
+        swdy = AttrMapValue(isNumber, desc="y position adjustment for the swatch"),
         )
 
     def __init__(self):
@@ -179,6 +184,9 @@
         self.dx = 10
         self.dy = 10
 
+        self.swdx = 0
+        self.swdy = 0
+
         # Distance between swatch rectangle and text.
         self.dxTextSpace = 10
 
@@ -214,7 +222,7 @@
     def _init_subCols(self):
         sc = self.subCols = TypedPropertyCollection(SubColProperty)
         sc.rpad = 1
-        sc.minWidth = 0
+        sc.dx = sc.dy = sc.minWidth = 0
         sc.align = 'right'
         sc[0].align = 'left' 
 
@@ -321,7 +329,7 @@
             deltay = max(dy,leading)+self.autoYPadding
         ba = self.boxAnchor
         maxWidth = self._calculateMaxBoundaries(colorNamePairs)
-        nCols = int((n+columnMaximum-1)/columnMaximum)
+        nCols = int((n+columnMaximum-1)/(columnMaximum*1.0))
         xW = dx+dxTextSpace+self.autoXPadding
         variColumn = self.variColumn
         if variColumn:
@@ -378,7 +386,7 @@
             T = _getLines(name)
             S = []
             aS = S.append
-            j = int(i/columnMaximum)
+            j = int(i/(columnMaximum*1.0))
             jOffs = maxWidth[j]
 
             # thisy+dy/2 = y+leading/2
@@ -403,6 +411,8 @@
                 x2 = x+jOffs[kk+1]
                 sc = subCols[k,i]
                 anchor = sc.align
+                scdx = sc.dx
+                scdy = sc.dy
                 fN = getattr(sc,'fontName',fontName)
                 fS = getattr(sc,'fontSize',fontSize)
                 fC = getattr(sc,'fillColor',fillColor)
@@ -425,7 +435,7 @@
                     anchor = 'middle'
                     xoffs = 0.5*(x1+x2)
                 for t in lines:
-                    aS(String(xoffs,y,t,fontName=fN,fontSize=fS,fillColor=fC, textAnchor = anchor))
+                    aS(String(xoffs+scdx,y+scdy,t,fontName=fN,fontSize=fS,fillColor=fC, textAnchor = anchor))
                     y -= fL
                 yd = min(yd,y)
                 y += fL
@@ -457,19 +467,22 @@
                         strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray))
 
             # Make a 'normal' color swatch...
+            swatchX = x + getattr(self,'swdx',0)
+            swatchY = thisy + getattr(self,'swdy',0)
+
             if isAuto(col):
                 chart = getattr(col,'chart',getattr(col,'obj',None))
-                c = chart.makeSwatchSample(getattr(col,'index',i),x,thisy,dx,dy)
+                c = chart.makeSwatchSample(getattr(col,'index',i),swatchX,swatchY,dx,dy)
             elif isinstance(col, colors.Color):
                 if isSymbol(swatchMarker):
-                    c = uSymbol2Symbol(swatchMarker,x+dx/2.,thisy+dy/2.,col)
+                    c = uSymbol2Symbol(swatchMarker,swatchX+dx/2.,swatchY+dy/2.,col)
                 else:
-                    c = self._defaultSwatch(x,thisy,dx,dy,fillColor=col,strokeWidth=strokeWidth,strokeColor=strokeColor)
+                    c = self._defaultSwatch(swatchX,swatchY,dx,dy,fillColor=col,strokeWidth=strokeWidth,strokeColor=strokeColor)
             elif col is not None:
                 try:
                     c = copy.deepcopy(col)
-                    c.x = x
-                    c.y = thisy
+                    c.x = swatchX
+                    c.y = swatchY
                     c.width = dx
                     c.height = dy
                 except:
@@ -481,7 +494,7 @@
                 g.add(c)
                 if scallout: scallout(self,g,thisx,y0,i,(col,name),c)
 
-            map(g.add,S)
+            for s in S: g.add(s)
             if self.colEndCallout and (i%columnMaximum==lim or i==(n-1)):
                 if alignment == "left":
                     xt = thisx
@@ -520,7 +533,7 @@
         return d
 
 class TotalAnnotator(LegendColEndCallout):
-    def __init__(self, lText='Total', rText='0.0', fontName='Times-Roman', fontSize=10,
+    def __init__(self, lText='Total', rText='0.0', fontName=_baseGFontName, fontSize=10,
             fillColor=colors.black, strokeWidth=0.5, strokeColor=colors.black, strokeDashArray=None,
             dx=0, dy=0, dly=0, dlx=(0,0)):
         self.lText = lText
--- a/src/reportlab/graphics/charts/linecharts.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/linecharts.py	Thu Oct 21 10:34:13 2010 +0000
@@ -24,9 +24,9 @@
         strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'),
         strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'),
         strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
-        symbol = AttrMapValue(NoneOr(isSymbol), desc='Widget placed at data points.'),
-        shader = AttrMapValue(None, desc='Shader Class.'),
-        filler = AttrMapValue(None, desc='Filler Class.'),
+        symbol = AttrMapValue(NoneOr(isSymbol), desc='Widget placed at data points.',advancedUsage=1),
+        shader = AttrMapValue(None, desc='Shader Class.',advancedUsage=1),
+        filler = AttrMapValue(None, desc='Filler Class.',advancedUsage=1),
         name = AttrMapValue(isStringOrNone, desc='Name of the line.'),
         )
 
@@ -108,8 +108,8 @@
     """
 
     _attrMap = AttrMap(BASE=LineChart,
-        useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'),
-        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.'),
+        useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.',advancedUsage=1),
+        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
         lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
         lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
         lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
@@ -120,9 +120,9 @@
         categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'),
         categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'),
         data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
-        inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.'),
-        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.'),
-        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
+        inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.',advancedUsage=1),
+        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
+        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
         )
 
     def __init__(self):
@@ -247,7 +247,7 @@
                     labelText = None
             else:
                 labelText = labelFmt % labelValue
-        elif callable(labelFmt):
+        elif hasattr(labelFmt,'__call__'):
             labelText = labelFmt(labelValue)
         else:
             raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)
@@ -503,7 +503,7 @@
 
         F.sort()
         g = Group()
-        map(lambda x,a=g.add: a(x[-1]),F.value())
+        for v in F.value(): g.add(v[-1])
         return g
 
 class VerticalLineChart(LineChart):
--- a/src/reportlab/graphics/charts/lineplots.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/lineplots.py	Thu Oct 21 10:34:13 2010 +0000
@@ -26,11 +26,11 @@
         strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'),
         strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'),
         strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
-        symbol = AttrMapValue(None, desc='Widget placed at data points.'),
-        shader = AttrMapValue(None, desc='Shader Class.'),
-        filler = AttrMapValue(None, desc='Filler Class.'),
+        symbol = AttrMapValue(None, desc='Widget placed at data points.',advancedUsage=1),
+        shader = AttrMapValue(None, desc='Shader Class.',advancedUsage=1),
+        filler = AttrMapValue(None, desc='Filler Class.',advancedUsage=1),
         name = AttrMapValue(isStringOrNone, desc='Name of the line.'),
-        inFill = AttrMapValue(isBoolean, desc='If true flood fill to x axis'),
+        inFill = AttrMapValue(isBoolean, desc='If true flood fill to x axis',advancedUsage=1),
         )
 
 class Shader(_SetKeyWordArgs):
@@ -79,8 +79,8 @@
     X and Y versions of this class).
     """
     _attrMap = AttrMap(BASE=PlotArea,
-        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.'),
-        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.'),
+        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
+        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
         lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
         lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
         lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
@@ -91,9 +91,9 @@
         xValueAxis = AttrMapValue(None, desc='Handle of the x axis.'),
         yValueAxis = AttrMapValue(None, desc='Handle of the y axis.'),
         data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) x/y tuples.'),
-        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
-        behindAxes = AttrMapValue(isBoolean, desc='If true use separate line group.'),
-        gridFirst = AttrMapValue(isBoolean, desc='If true use draw grids before axes.'),
+        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
+        behindAxes = AttrMapValue(isBoolean, desc='If true use separate line group.',advancedUsage=1),
+        gridFirst = AttrMapValue(isBoolean, desc='If true use draw grids before axes.',advancedUsage=1),
         )
 
     def __init__(self):
@@ -212,7 +212,7 @@
                 labelText = self.lineLabelArray[rowNo][colNo]
             else:
                 labelText = labelFmt % labelValue
-        elif callable(labelFmt):
+        elif hasattr(labelFmt,'__call__'):
             labelText = labelFmt(labelValue)
         else:
             raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)
@@ -260,7 +260,7 @@
         for rowNo in P:
             row = self._positions[rowNo]
             rowStyle = self.lines[rowNo % styleCount]
-            rowColor = rowStyle.strokeColor
+            rowColor = getattr(rowStyle,'strokeColor',None)
             dash = getattr(rowStyle, 'strokeDashArray', None)
 
             if hasattr(rowStyle, 'strokeWidth'):
@@ -274,7 +274,7 @@
             if self.joinedLines:
                 points = []
                 for xy in row:
-                    points = points + [xy[0], xy[1]]
+                    points += [xy[0], xy[1]]
                 if inFill or getattr(rowStyle,'inFill',False):
                     fpoints = [inFillX0,inFillY] + points + [inFillX1,inFillY]
                     filler = getattr(rowStyle, 'filler', None)
@@ -351,21 +351,23 @@
         if self.gridFirst:
             xA.makeGrid(g,parent=self,dim=yA.getGridDims)
             yA.makeGrid(g,parent=self,dim=xA.getGridDims)
-        g.add(xA)
-        g.add(yA)
+        g.add(xA.draw())
+        g.add(yA.draw())
+        xAex = xA.visibleAxis and (xA._y,) or ()
+        yAex = yA.visibleAxis and (yA._x,) or ()
         if not self.gridFirst:
             xAdgl = getattr(xA,'drawGridLast',False)
             yAdgl = getattr(yA,'drawGridLast',False)
-            if not xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims)
-            if not yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims)
+            if not xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims,exclude=yAex)
+            if not yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims,exclude=xAex)
         annotations = getattr(self,'annotations',[])
         for a in annotations:
             if getattr(a,'beforeLines',None):
                 g.add(a(self,xA.scale,yA.scale))
         g.add(self.makeLines())
         if not self.gridFirst:
-            if xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims)
-            if yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims)
+            if xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims,exclude=yAex)
+            if yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims,exclude=xAex)
         for a in annotations:
             if not getattr(a,'beforeLines',None):
                 g.add(a(self,xA.scale,yA.scale))
@@ -527,7 +529,7 @@
 
         F.sort()
         g = Group()
-        map(lambda x,a=g.add: a(x[-1]),F.value())
+        for v in F.value(): g.add(v[-1])
         return g
 
 _monthlyIndexData = [[(19971202, 100.0),
@@ -800,7 +802,7 @@
                     outerBorderOn = AttrMapValue(isBoolean, desc="Is there an outer border (continuation of axes)"),
                     outerBorderColor = AttrMapValue(isColorOrNone, desc="Color of outer border (if any)"),
                     background = AttrMapValue(isColorOrNone, desc="Background color (if any)"),
-                    labelOffset = AttrMapValue(isNumber, desc="Space between label and Axis (or other labels)"),
+                    labelOffset = AttrMapValue(isNumber, desc="Space between label and Axis (or other labels)",advancedUsage=1),
                     axisTickLengths = AttrMapValue(isNumber, desc="Lenth of the ticks on both axes"),
                     axisStrokeWidth = AttrMapValue(isNumber, desc="Stroke width for both axes"),
                     xLabel = AttrMapValue(isString, desc="Label for the whole X-Axis"),
--- a/src/reportlab/graphics/charts/piecharts.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/piecharts.py	Thu Oct 21 10:34:13 2010 +0000
@@ -59,33 +59,33 @@
     format method.
     """
     _attrMap = AttrMap(
-        strokeWidth = AttrMapValue(isNumber),
-        fillColor = AttrMapValue(isColorOrNone),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        strokeLineCap = AttrMapValue(OneOf(0,1,2),desc="Line Cap parameter o, 1 or 2"),
-        strokeLineJoin = AttrMapValue(OneOf(0,1,2),desc="Line Join parameter 0, 1 or 2"),
-        strokeMiterLimit = AttrMapValue(isNumber),
-        popout = AttrMapValue(isNumber),
-        fontName = AttrMapValue(isString),
-        fontSize = AttrMapValue(isNumber),
-        fontColor = AttrMapValue(isColorOrNone),
-        labelRadius = AttrMapValue(isNumber),
-        label_dx = AttrMapValue(isNumber),
-        label_dy = AttrMapValue(isNumber),
-        label_angle = AttrMapValue(isNumber),
-        label_boxAnchor = AttrMapValue(isBoxAnchor),
-        label_boxStrokeColor = AttrMapValue(isColorOrNone),
-        label_boxStrokeWidth = AttrMapValue(isNumber),
-        label_boxFillColor = AttrMapValue(isColorOrNone),
-        label_strokeColor = AttrMapValue(isColorOrNone),
-        label_strokeWidth = AttrMapValue(isNumber),
-        label_text = AttrMapValue(isStringOrNone),
-        label_leading = AttrMapValue(isNumberOrNone),
-        label_width = AttrMapValue(isNumberOrNone),
-        label_maxWidth = AttrMapValue(isNumberOrNone),
-        label_height = AttrMapValue(isNumberOrNone),
-        label_textAnchor = AttrMapValue(isTextAnchor),
+        strokeWidth = AttrMapValue(isNumber,desc=''),
+        fillColor = AttrMapValue(isColorOrNone,desc=''),
+        strokeColor = AttrMapValue(isColorOrNone,desc=''),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc=''),
+        strokeLineCap = AttrMapValue(OneOf(0,1,2),desc="Line cap 0=butt, 1=round & 2=square"),
+        strokeLineJoin = AttrMapValue(OneOf(0,1,2),desc="Line join 0=miter, 1=round & 2=bevel"),
+        strokeMiterLimit = AttrMapValue(isNumber,desc='miter limit control miter line joins'),
+        popout = AttrMapValue(isNumber,desc="how far of centre a wedge to pop."),
+        fontName = AttrMapValue(isString,desc=''),
+        fontSize = AttrMapValue(isNumber,desc=''),
+        fontColor = AttrMapValue(isColorOrNone,desc=''),
+        labelRadius = AttrMapValue(isNumber,desc=''),
+        label_dx = AttrMapValue(isNumber,desc=''),
+        label_dy = AttrMapValue(isNumber,desc=''),
+        label_angle = AttrMapValue(isNumber,desc=''),
+        label_boxAnchor = AttrMapValue(isBoxAnchor,desc=''),
+        label_boxStrokeColor = AttrMapValue(isColorOrNone,desc=''),
+        label_boxStrokeWidth = AttrMapValue(isNumber,desc=''),
+        label_boxFillColor = AttrMapValue(isColorOrNone,desc=''),
+        label_strokeColor = AttrMapValue(isColorOrNone,desc=''),
+        label_strokeWidth = AttrMapValue(isNumber,desc=''),
+        label_text = AttrMapValue(isStringOrNone,desc=''),
+        label_leading = AttrMapValue(isNumberOrNone,desc=''),
+        label_width = AttrMapValue(isNumberOrNone,desc=''),
+        label_maxWidth = AttrMapValue(isNumberOrNone,desc=''),
+        label_height = AttrMapValue(isNumberOrNone,desc=''),
+        label_textAnchor = AttrMapValue(isTextAnchor,desc=''),
         label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
         label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
         label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
@@ -97,7 +97,7 @@
         label_pointer_elbowLength = AttrMapValue(isNumber,desc='length of final indicator line segment'),
         label_pointer_edgePad = AttrMapValue(isNumber,desc='pad between pointer label and box'),
         label_pointer_piePad = AttrMapValue(isNumber,desc='pad between pointer label and pie'),
-        swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ..."),
+        swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ...",advancedUsage=1),
         visible = AttrMapValue(isBoolean,'set to false to skip displaying'),
         )
 
@@ -415,6 +415,13 @@
         mul = -1
     return G, mlr[0], mlr[1], mel
 
+class AngleData(float):
+    '''use this to carry the data along with the angle'''
+    def __new__(cls,angle,data):
+        self = float.__new__(cls,angle)
+        self._data = data
+        return self
+
 class Pie(AbstractPieChart):
     _attrMap = AttrMap(BASE=AbstractPieChart,
         data = AttrMapValue(isListOfNumbers, desc='list of numbers defining wedge sizes; need not sum to 1'),
@@ -422,14 +429,15 @@
         startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"),
         direction = AttrMapValue(OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"),
         slices = AttrMapValue(None, desc="collection of wedge descriptor objects"),
-        simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel"),
-        other_threshold = AttrMapValue(isNumber, desc='A value for doing threshholding, not used yet.'),
-        checkLabelOverlap = AttrMapValue(isBoolean, desc="If true check and attempt to fix standard label overlaps(default off)"),
-        pointerLabelMode = AttrMapValue(OneOf(None,'LeftRight','LeftAndRight'), desc=""),
-        sameRadii = AttrMapValue(isBoolean, desc="If true make x/y radii the same(default off)"),
-        orderMode = AttrMapValue(OneOf('fixed','alternate')),
+        simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel",advancedUsage=1),
+        other_threshold = AttrMapValue(isNumber, desc='A value for doing threshholding, not used yet.',advancedUsage=1),
+        checkLabelOverlap = AttrMapValue(isBoolean, desc="If true check and attempt to fix\n standard label overlaps(default off)",advancedUsage=1),
+        pointerLabelMode = AttrMapValue(OneOf(None,'LeftRight','LeftAndRight'), desc='',advancedUsage=1),
+        sameRadii = AttrMapValue(isBoolean, desc="If true make x/y radii the same(default off)",advancedUsage=1),
+        orderMode = AttrMapValue(OneOf('fixed','alternate'),advancedUsage=1),
         xradius = AttrMapValue(isNumberOrNone, desc="X direction Radius"),
         yradius = AttrMapValue(isNumberOrNone, desc="Y direction Radius"),
+        wedgeRecord = AttrMapValue(None, desc="callable(wedge,*args,**kwds)",advancedUsage=1),
         )
     other_threshold=None
 
@@ -566,19 +574,21 @@
         if self.sameRadii: xradius=yradius=min(xradius,yradius)
         return PL(centerx,centery,xradius,yradius,G,lu,ru)
 
-    def normalizeData(self):
+    def normalizeData(self,keepData=False):
         data = map(abs,self.data)
         s = self._sum = float(sum(data))
-        if s>1e-8:
-            f = 360./s
+        if s<=1e-8: s = 0
+        f = 360./s
+        if keepData:
+            return [AngleData(f*x,x) for x in data]
+        else:
             return [f*x for x in data]
-        else:
-            return [0]*len(data)
 
     def makeAngles(self):
+        wr = getattr(self,'wedgeRecord',None)
         startAngle = self.startAngle % 360
         whichWay = self.direction == "clockwise" and -1 or 1
-        D = [a for a in enumerate(self.normalizeData())]
+        D = [a for a in enumerate(self.normalizeData(keepData=wr))]
         if self.orderMode=='alternate':
             W = [a for a in D if abs(a[1])>=1e-5]
             W.sort(_arcCF)
@@ -605,6 +615,8 @@
                     aa = startAngle,endAngle
             else:
                 aa = startAngle, None
+            if wr:
+                aa = (AngleData(aa[0],angle._data),aa[1])
             startAngle = endAngle
             a((i,aa))
         return A
@@ -613,6 +625,7 @@
         angles = self.makeAngles()
         n = len(angles)
         labels = _fixLabels(self.labels,n)
+        wr = getattr(self,'wedgeRecord',None)
 
         self._seriesCount = n
         styleCount = len(self.slices)
@@ -679,6 +692,8 @@
             theWedge.strokeDashArray = wedgeStyle.strokeDashArray
 
             g_add(theWedge)
+            if wr:
+                wr(theWedge,value=a1._data,label=text)
             if wedgeStyle.label_visible:
                 if text:
                     labelRadius = wedgeStyle.labelRadius
@@ -714,7 +729,7 @@
 
         if checkLabelOverlap and L:
             fixLabelOverlaps(L)
-        map(g_add,L)
+        for l in L: g_add(l)
 
         if not plMode:
             for l in L:
@@ -838,7 +853,7 @@
                     pass
                 elif type(lNF) is StringType:
                     ldf = lNF % ldf
-                elif callable(lNF):
+                elif hasattr(lNF,'__call__'):
                     ldf = lNF(ldf)
                 else:
                     p = self.legend_names[f]
@@ -849,7 +864,7 @@
                         pass
                     elif type(lNF) is StringType:
                         ldf = lNF % ldf
-                    elif callable(lNF):
+                    elif hasattr(lNF,'__call__'):
                         ldf = lNF(ldf)
                     else:
                         msg = "Unknown formatter type %s, expected string or function" % self.legendNumberFormat
@@ -894,39 +909,39 @@
     format method.
     """
     _attrMap = AttrMap(
-        fillColor = AttrMapValue(isColorOrNone),
-        fillColorShaded = AttrMapValue(isColorOrNone),
-        fontColor = AttrMapValue(isColorOrNone),
-        fontName = AttrMapValue(isString),
-        fontSize = AttrMapValue(isNumber),
-        label_angle = AttrMapValue(isNumber),
+        fillColor = AttrMapValue(isColorOrNone,desc=''),
+        fillColorShaded = AttrMapValue(isColorOrNone,desc=''),
+        fontColor = AttrMapValue(isColorOrNone,desc=''),
+        fontName = AttrMapValue(isString,desc=''),
+        fontSize = AttrMapValue(isNumber,desc=''),
+        label_angle = AttrMapValue(isNumber,desc=''),
         label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
-        label_boxAnchor = AttrMapValue(isBoxAnchor),
-        label_boxFillColor = AttrMapValue(isColorOrNone),
-        label_boxStrokeColor = AttrMapValue(isColorOrNone),
-        label_boxStrokeWidth = AttrMapValue(isNumber),
-        label_dx = AttrMapValue(isNumber),
-        label_dy = AttrMapValue(isNumber),
-        label_height = AttrMapValue(isNumberOrNone),
-        label_leading = AttrMapValue(isNumberOrNone),
+        label_boxAnchor = AttrMapValue(isBoxAnchor,desc=''),
+        label_boxFillColor = AttrMapValue(isColorOrNone,desc=''),
+        label_boxStrokeColor = AttrMapValue(isColorOrNone,desc=''),
+        label_boxStrokeWidth = AttrMapValue(isNumber,desc=''),
+        label_dx = AttrMapValue(isNumber,desc=''),
+        label_dy = AttrMapValue(isNumber,desc=''),
+        label_height = AttrMapValue(isNumberOrNone,desc=''),
+        label_leading = AttrMapValue(isNumberOrNone,desc=''),
         label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
-        label_maxWidth = AttrMapValue(isNumberOrNone),
+        label_maxWidth = AttrMapValue(isNumberOrNone,desc=''),
         label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
         label_simple_pointer = AttrMapValue(isBoolean,'set to True for simple pointers'),
-        label_strokeColor = AttrMapValue(isColorOrNone),
-        label_strokeWidth = AttrMapValue(isNumber),
-        label_text = AttrMapValue(isStringOrNone),
-        label_textAnchor = AttrMapValue(isTextAnchor),
+        label_strokeColor = AttrMapValue(isColorOrNone,desc=''),
+        label_strokeWidth = AttrMapValue(isNumber,desc=''),
+        label_text = AttrMapValue(isStringOrNone,desc=''),
+        label_textAnchor = AttrMapValue(isTextAnchor,desc=''),
         label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
         label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
-        label_width = AttrMapValue(isNumberOrNone),
-        labelRadius = AttrMapValue(isNumber),
-        popout = AttrMapValue(isNumber),
-        shading = AttrMapValue(isNumber),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeColorShaded = AttrMapValue(isColorOrNone),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        strokeWidth = AttrMapValue(isNumber),
+        label_width = AttrMapValue(isNumberOrNone,desc=''),
+        labelRadius = AttrMapValue(isNumber,desc=''),
+        popout = AttrMapValue(isNumber,desc=''),
+        shading = AttrMapValue(isNumber,desc=''),
+        strokeColor = AttrMapValue(isColorOrNone,desc=''),
+        strokeColorShaded = AttrMapValue(isColorOrNone,desc=''),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc=''),
+        strokeWidth = AttrMapValue(isNumber,desc=''),
         visible = AttrMapValue(isBoolean,'set to false to skip displaying'),
         )
 
@@ -1098,17 +1113,16 @@
             #connect to top
             if lo < a0 < hi: angle0 = a0
             if lo < a1 < hi: angle1 = a1
-            if 1:
-                p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)
-                p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1)
-                p.lineTo(OX(i,angle1,0),OY(i,angle1,0))
-                p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1)
-                p.closePath()
-                if angle0<=_3dva and angle1>=_3dva:
-                    rd = 0
-                else:
-                    rd = min(rad_dist(angle0),rad_dist(angle1))
-                S.append((rd,p))
+            p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)
+            p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1)
+            p.lineTo(OX(i,angle1,0),OY(i,angle1,0))
+            p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1)
+            p.closePath()
+            if angle0<=_3dva and angle1>=_3dva:
+                rd = 0
+            else:
+                rd = min(rad_dist(angle0),rad_dist(angle1))
+            S.append((rd,p))
             _fillSide(S,i,lo,strokeColor,strokeWidth,fillColor)
             _fillSide(S,i,hi,strokeColor,strokeWidth,fillColor)
 
@@ -1139,7 +1153,8 @@
         S.sort(lambda a,b: -cmp(a[0],b[0]))
         if checkLabelOverlap and L:
             fixLabelOverlaps(L)
-        map(g.add,map(lambda x:x[1],S)+T+L)
+        for x in ([s[1] for s in S]+T+L):
+            g.add(x)
         return g
 
     def demo(self):
--- a/src/reportlab/graphics/charts/spider.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/spider.py	Thu Oct 21 10:34:13 2010 +0000
@@ -36,12 +36,12 @@
 class StrandProperty(PropHolder):
 
     _attrMap = AttrMap(
-        strokeWidth = AttrMapValue(isNumber),
-        fillColor = AttrMapValue(isColorOrNone),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        symbol = AttrMapValue(EitherOr((isStringOrNone,isSymbol)), desc='Widget placed at data points.'),
-        symbolSize= AttrMapValue(isNumber, desc='Symbol size.'),
+        strokeWidth = AttrMapValue(isNumber,desc='width'),
+        fillColor = AttrMapValue(isColorOrNone,desc='filling color'),
+        strokeColor = AttrMapValue(isColorOrNone,desc='stroke color'),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc='dashing pattern, e.g. (3,2)'),
+        symbol = AttrMapValue(EitherOr((isStringOrNone,isSymbol)), desc='Widget placed at data points.',advancedUsage=1),
+        symbolSize= AttrMapValue(isNumber, desc='Symbol size.',advancedUsage=1),
         name = AttrMapValue(isStringOrNone, desc='Name of the strand.'),
         )
 
@@ -56,11 +56,11 @@
 
 class SpokeProperty(PropHolder):
     _attrMap = AttrMap(
-        strokeWidth = AttrMapValue(isNumber),
-        fillColor = AttrMapValue(isColorOrNone),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        labelRadius = AttrMapValue(isNumber),
+        strokeWidth = AttrMapValue(isNumber,desc='width'),
+        fillColor = AttrMapValue(isColorOrNone,desc='filling color'),
+        strokeColor = AttrMapValue(isColorOrNone,desc='stroke color'),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc='dashing pattern, e.g. (2,1)'),
+        labelRadius = AttrMapValue(isNumber,desc='label radius',advancedUsage=1),
         visible = AttrMapValue(isBoolean,desc="True if the spoke line is to be drawn"),
         )
 
@@ -79,8 +79,8 @@
 
 class StrandLabel(SpokeLabel):
     _attrMap = AttrMap(BASE=SpokeLabel,
-            format = AttrMapValue(EitherOr((isStringOrNone,isCallable)),"Format for the label"),
-            dR = AttrMapValue(isNumberOrNone,"radial shift for label"),
+            format = AttrMapValue(EitherOr((isStringOrNone,isCallable)),desc="Format for the label"),
+            dR = AttrMapValue(isNumberOrNone,desc="radial shift for label"),
             )
     def __init__(self,**kw):
         self.format = ''
@@ -207,7 +207,7 @@
                 text = sty._text
             else:
                 text = fmt % value
-        elif callable(fmt):
+        elif hasattr(fmt,'__call__'):
             text = fmt(value)
         else:
             raise ValueError("Unknown formatter type %s, expected string or function" % fmt)
@@ -343,7 +343,7 @@
                 STRANDS.append(strand)
             rowIdx += 1
 
-        map(g.add,STRANDAREAS+STRANDS+syms+S+labs)
+        for s in (STRANDAREAS+STRANDS+syms+S+labs): g.add(s)
         return g
 
 def sample1():
--- a/src/reportlab/graphics/charts/textlabels.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/textlabels.py	Thu Oct 21 10:34:13 2010 +0000
@@ -13,6 +13,7 @@
 from reportlab.graphics.shapes import Drawing, Group, Circle, Rect, String, STATE_DEFAULTS
 from reportlab.graphics.shapes import _PATH_OP_ARG_COUNT, _PATH_OP_NAMES, definePath
 from reportlab.graphics.widgetbase import Widget, PropHolder
+from reportlab.graphics.shapes import _baseGFontName
 
 _gs = None
 _A2BA=  {
@@ -55,7 +56,7 @@
         P.extend(g[1:])
     return R
 
-def _text2PathDescription(text, x=0, y=0, fontName='Times-Roman', fontSize=1000,
+def _text2PathDescription(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
                             anchor='start', truncate=1, pathReverse=0):
     global _gs
     if not _gs:
@@ -74,7 +75,7 @@
         P.extend(_processGlyph(g,truncate=truncate,pathReverse=pathReverse))
     return P
 
-def _text2Path(text, x=0, y=0, fontName='Times-Roman', fontSize=1000,
+def _text2Path(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
                 anchor='start', truncate=1, pathReverse=0):
     return definePath(_text2PathDescription(text,x=x,y=y,fontName=fontName,
                     fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse))
@@ -91,32 +92,32 @@
     # framework.
 
     _attrMap = AttrMap(
-        x = AttrMapValue(isNumber),
-        y = AttrMapValue(isNumber),
-        dx = AttrMapValue(isNumber),
-        dy = AttrMapValue(isNumber),
-        angle = AttrMapValue(isNumber),
-        boxAnchor = AttrMapValue(isBoxAnchor),
-        boxStrokeColor = AttrMapValue(isColorOrNone),
-        boxStrokeWidth = AttrMapValue(isNumber),
-        boxFillColor = AttrMapValue(isColorOrNone),
-        boxTarget = AttrMapValue(OneOf('normal','anti','lo','hi')),
-        fillColor = AttrMapValue(isColorOrNone),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeWidth = AttrMapValue(isNumber),
-        text = AttrMapValue(isString),
-        fontName = AttrMapValue(isString),
-        fontSize = AttrMapValue(isNumber),
-        leading = AttrMapValue(isNumberOrNone),
-        width = AttrMapValue(isNumberOrNone),
-        maxWidth = AttrMapValue(isNumberOrNone),
-        height = AttrMapValue(isNumberOrNone),
-        textAnchor = AttrMapValue(isTextAnchor),
+        x = AttrMapValue(isNumber,desc=''),
+        y = AttrMapValue(isNumber,desc=''),
+        dx = AttrMapValue(isNumber,desc='delta x - offset'),
+        dy = AttrMapValue(isNumber,desc='delta y - offset'),
+        angle = AttrMapValue(isNumber,desc='angle of label: default (0), 90 is vertical, 180 is upside down, etc'),
+        boxAnchor = AttrMapValue(isBoxAnchor,desc='anchoring point of the label'),
+        boxStrokeColor = AttrMapValue(isColorOrNone,desc='border color of the box'),
+        boxStrokeWidth = AttrMapValue(isNumber,desc='border width'),
+        boxFillColor = AttrMapValue(isColorOrNone,desc='the filling color of the box'),
+        boxTarget = AttrMapValue(OneOf('normal','anti','lo','hi'),desc="one of ('normal','anti','lo','hi')"),
+        fillColor = AttrMapValue(isColorOrNone,desc='label text color'),
+        strokeColor = AttrMapValue(isColorOrNone,desc='label text border color'),
+        strokeWidth = AttrMapValue(isNumber,desc='label text border width'),
+        text = AttrMapValue(isString,desc='the actual text to display'),
+        fontName = AttrMapValue(isString,desc='the name of the font used'),
+        fontSize = AttrMapValue(isNumber,desc='the size of the font'),
+        leading = AttrMapValue(isNumberOrNone,desc=''),
+        width = AttrMapValue(isNumberOrNone,desc='the width of the label'),
+        maxWidth = AttrMapValue(isNumberOrNone,desc='maximum width the label can grow to'),
+        height = AttrMapValue(isNumberOrNone,desc='the height of the text'),
+        textAnchor = AttrMapValue(isTextAnchor,desc='the anchoring point of the text inside the label'),
         visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
-        topPadding = AttrMapValue(isNumber,'padding at top of box'),
-        leftPadding = AttrMapValue(isNumber,'padding at left of box'),
-        rightPadding = AttrMapValue(isNumber,'padding at right of box'),
-        bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
+        topPadding = AttrMapValue(isNumber,desc='padding at top of box'),
+        leftPadding = AttrMapValue(isNumber,desc='padding at left of box'),
+        rightPadding = AttrMapValue(isNumber,desc='padding at right of box'),
+        bottomPadding = AttrMapValue(isNumber,desc='padding at bottom of box'),
         )
 
     def __init__(self,**kw):
@@ -292,25 +293,25 @@
 
 class LabelDecorator:
     _attrMap = AttrMap(
-        x = AttrMapValue(isNumberOrNone),
-        y = AttrMapValue(isNumberOrNone),
-        dx = AttrMapValue(isNumberOrNone),
-        dy = AttrMapValue(isNumberOrNone),
-        angle = AttrMapValue(isNumberOrNone),
-        boxAnchor = AttrMapValue(isBoxAnchor),
-        boxStrokeColor = AttrMapValue(isColorOrNone),
-        boxStrokeWidth = AttrMapValue(isNumberOrNone),
-        boxFillColor = AttrMapValue(isColorOrNone),
-        fillColor = AttrMapValue(isColorOrNone),
-        strokeColor = AttrMapValue(isColorOrNone),
-        strokeWidth = AttrMapValue(isNumberOrNone),
-        fontName = AttrMapValue(isNoneOrString),
-        fontSize = AttrMapValue(isNumberOrNone),
-        leading = AttrMapValue(isNumberOrNone),
-        width = AttrMapValue(isNumberOrNone),
-        maxWidth = AttrMapValue(isNumberOrNone),
-        height = AttrMapValue(isNumberOrNone),
-        textAnchor = AttrMapValue(isTextAnchor),
+        x = AttrMapValue(isNumberOrNone,desc=''),
+        y = AttrMapValue(isNumberOrNone,desc=''),
+        dx = AttrMapValue(isNumberOrNone,desc=''),
+        dy = AttrMapValue(isNumberOrNone,desc=''),
+        angle = AttrMapValue(isNumberOrNone,desc=''),
+        boxAnchor = AttrMapValue(isBoxAnchor,desc=''),
+        boxStrokeColor = AttrMapValue(isColorOrNone,desc=''),
+        boxStrokeWidth = AttrMapValue(isNumberOrNone,desc=''),
+        boxFillColor = AttrMapValue(isColorOrNone,desc=''),
+        fillColor = AttrMapValue(isColorOrNone,desc=''),
+        strokeColor = AttrMapValue(isColorOrNone,desc=''),
+        strokeWidth = AttrMapValue(isNumberOrNone),desc='',
+        fontName = AttrMapValue(isNoneOrString,desc=''),
+        fontSize = AttrMapValue(isNumberOrNone,desc=''),
+        leading = AttrMapValue(isNumberOrNone,desc=''),
+        width = AttrMapValue(isNumberOrNone,desc=''),
+        maxWidth = AttrMapValue(isNumberOrNone,desc=''),
+        height = AttrMapValue(isNumberOrNone,desc=''),
+        textAnchor = AttrMapValue(isTextAnchor,desc=''),
         visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
         )
 
--- a/src/reportlab/graphics/charts/utils.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/charts/utils.py	Thu Oct 21 10:34:13 2010 +0000
@@ -4,10 +4,7 @@
 
 __version__=''' $Id$ '''
 __doc__="Utilities used here and there."
-
 from time import mktime, gmtime, strftime
-import string
-
 
 ### Dinu's stuff used in some line plots (likely to vansih).
 
@@ -15,7 +12,7 @@
     "Convert a 'dd/mm/yyyy' formatted string to a tuple for use in the time module."
 
     list = [0] * 9
-    dd, mm, yyyy = map(int, string.split(timeString, '/'))
+    dd, mm, yyyy = map(int, timeString.split('/'))
     list[:3] = [yyyy, mm, dd]
 
     return tuple(list)
@@ -153,7 +150,7 @@
     return t, hi, grid
 
 
-def ticks(lower, upper, n=(4,5,6,7,8,9), split=1, percent=0, grid=None):
+def ticks(lower, upper, n=(4,5,6,7,8,9), split=1, percent=0, grid=None, labelVOffset=0):
     '''
     return tick positions and labels for range lower<=x<=upper
     n=number of intervals to try (can be a list or sequence)
@@ -166,14 +163,14 @@
     w = int(w)!=w
 
     if power > 3 or power < -3:
-        format = '%+'+`w+7`+'.0e'
+        format = '%+'+repr(w+7)+'.0e'
     else:
         if power >= 0:
             digits = int(power)+w
-            format = '%' + `digits`+'.0f'
+            format = '%' + repr(digits)+'.0f'
         else:
             digits = w-int(power)
-            format = '%'+`digits+2`+'.'+`digits`+'f'
+            format = '%'+repr(digits+2)+'.'+repr(digits)+'f'
 
     if percent: format=format+'%%'
     T = []
@@ -183,12 +180,12 @@
         for i in xrange(n):
             v = t+grid*i
             T.append(v)
-            labels.append(format % v)
+            labels.append(format % (v+labelVOffset))
         return T, labels
     else:
         for i in xrange(n):
             v = t+grid*i
-            T.append((v, format % v))
+            T.append((v, format % (v+labelVOffset)))
         return T
 
 def findNones(data):
@@ -225,3 +222,92 @@
 
 def pairMaverage(data,n=6):
     return [(x[0],s) for x,s in zip(data, maverage([x[1] for x in data],n))]
+
+import weakref
+from reportlab.graphics.shapes import transformPoint, transformPoints, inverse, Ellipse
+from reportlab.lib.utils import flatten
+class DrawTimeCollector(object):
+    '''
+    generic mechanism for collecting information about nodes at the time they are about to be drawn
+    '''
+    def __init__(self,formats=['gif']):
+        self._nodes = weakref.WeakKeyDictionary()
+        self.clear()
+        self._pmcanv = None
+        self.formats = formats
+        self.disabled = False
+
+    def clear(self):
+        self._info = []
+        self._info_append = self._info.append
+
+    def record(self,func,node,*args,**kwds):
+        self._nodes[node] = (func,args,kwds)
+        node.__dict__['_drawTimeCallback'] = self
+
+    def __call__(self,node,canvas,renderer):
+        func = self._nodes.get(node,None)
+        if func:
+            func, args, kwds = func
+            i = func(node,canvas,renderer, *args, **kwds)
+            if i is not None: self._info_append(i)
+
+    @staticmethod
+    def rectDrawTimeCallback(node,canvas,renderer,**kwds):
+        A = getattr(canvas,'ctm',None)
+        if not A: return
+        x1 = node.x
+        y1 = node.y
+        x2 = x1 + node.width
+        y2 = y1 + node.height
+
+        D = kwds.copy()
+        D['rect']=DrawTimeCollector.transformAndFlatten(A,((x1,y1),(x2,y2)))
+        return D
+
+    @staticmethod
+    def transformAndFlatten(A,p):
+        ''' transform an flatten a list of points
+        A   transformation matrix
+        p   points [(x0,y0),....(xk,yk).....]
+        '''
+        if tuple(A)!=(1,0,0,1,0,0):
+            iA = inverse(A)
+            p = transformPoints(iA,p)
+        return tuple(flatten(p))
+
+    @property
+    def pmcanv(self):
+        if not self._pmcanv:
+            import renderPM
+            self._pmcanv = renderPM.PMCanvas(1,1)
+        return self._pmcanv
+
+    def wedgeDrawTimeCallback(self,node,canvas,renderer,**kwds):
+        A = getattr(canvas,'ctm',None)
+        if not A: return
+        if isinstance(node,Ellipse):
+            c = self.pmcanv
+            c.ellipse(node.cx, node.cy, node.rx,node.ry)
+            p = c.vpath
+            p = [(x[1],x[2]) for x in p]
+        else:
+            p = node.asPolygon().points
+            p = [(p[i],p[i+1]) for i in xrange(0,len(p),2)]
+
+        D = kwds.copy()
+        D['poly'] = self.transformAndFlatten(A,p)
+        return D
+
+    def save(self,fnroot):
+        '''
+        save the current information known to this collector
+        fnroot is the root name of a resource to name the saved info
+        override this to get the right semantics for your collector
+        '''
+        import pprint
+        f=open(fnroot+'.default-collector.out','w')
+        try:
+            pprint.pprint(self._info,f)
+        finally:
+            f.close()
--- a/src/reportlab/graphics/renderPDF.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/renderPDF.py	Thu Oct 21 10:34:13 2010 +0000
@@ -237,13 +237,17 @@
                 fontsize = delta.get('fontSize', self._canvas._fontsize)
                 self._canvas.setFont(fontname, fontsize)
             elif key=='fillOpacity':
-                self._canvas.setFillAlpha(value)
+                if value is not None:
+                    self._canvas.setFillAlpha(value)
             elif key=='strokeOpacity':
-                self._canvas.setStrokeAlpha(value)
+                if value is not None:
+                    self._canvas.setStrokeAlpha(value)
             elif key=='fillOverprint':
                 self._canvas.setFillOverprint(value)
             elif key=='strokeOverprint':
                 self._canvas.setStrokeOverprint(value)
+            elif key=='overprintMask':
+                self._canvas.setOverprintMask(value)
 
 from reportlab.platypus import Flowable
 class GraphicsFlowable(Flowable):
@@ -304,8 +308,9 @@
 #
 #########################################################
 def test():
+    from reportlab.graphics.shapes import _baseGFontName, _baseGFontNameBI
     c = Canvas('renderPDF.pdf')
-    c.setFont('Times-Roman', 36)
+    c.setFont(_baseGFontName, 36)
     c.drawString(80, 750, 'Graphics Test')
 
     # print all drawings and their doc strings from the test
@@ -321,7 +326,7 @@
             drawings.append((drawing, docstring))
 
     #print in a loop, with their doc strings
-    c.setFont('Times-Roman', 12)
+    c.setFont(_baseGFontName, 12)
     y = 740
     i = 1
     for (drawing, docstring) in drawings:
@@ -331,9 +336,9 @@
             y = 740
         # draw a title
         y = y - 30
-        c.setFont('Times-BoldItalic',12)
+        c.setFont(_baseGFontNameBI,12)
         c.drawString(80, y, 'Drawing %d' % i)
-        c.setFont('Times-Roman',12)
+        c.setFont(_baseGFontName,12)
         y = y - 14
         textObj = c.beginText(80, y)
         textObj.textLines(docstring)
--- a/src/reportlab/graphics/renderPM.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/renderPM.py	Thu Oct 21 10:34:13 2010 +0000
@@ -29,7 +29,7 @@
 except ImportError, errMsg:
     raise ImportError, "No module named _renderPM\n" + \
         (str(errMsg)!='No module named _renderPM' and "it may be the wrong version or badly installed!" or
-                                    "see http://www.reportlab.org/oss/rl-addons/")
+                                    "see https://www.reportlab.com/software/opensource/rl-addons/")
 
 def _getImage():
     try:
@@ -71,13 +71,19 @@
         s = self._tracker.getState()
         self._canvas.ctm = s['ctm']
         self._canvas.strokeWidth = s['strokeWidth']
-        self._canvas.strokeColor = Color2Hex(s['strokeColor'])
+        alpha = s['strokeOpacity']
+        if alpha is not None:
+            self._canvas.strokeOpacity = alpha
+        self._canvas.setStrokeColor(s['strokeColor'])
         self._canvas.lineCap = s['strokeLineCap']
         self._canvas.lineJoin = s['strokeLineJoin']
         da = s['strokeDashArray']
         da = da and (0,da) or None
         self._canvas.dashArray = da
-        self._canvas.fillColor = Color2Hex(s['fillColor'])
+        alpha = s['fillOpacity']
+        if alpha is not None:
+            self._canvas.fillOpacity = alpha
+        self._canvas.setFillColor(s['fillColor'])
         self._canvas.setFont(s['fontName'], s['fontSize'])
 
     def initState(self,x,y):
@@ -603,9 +609,15 @@
 
     def setFillColor(self,aColor):
         self.fillColor = Color2Hex(aColor)
+        alpha = getattr(aColor,'alpha',None)
+        if alpha is not None:
+            self.fillOpacity = alpha
 
     def setStrokeColor(self,aColor):
         self.strokeColor = Color2Hex(aColor)
+        alpha = getattr(aColor,'alpha',None)
+        if alpha is not None:
+            self.strokeOpacity = alpha
 
     restoreState = saveState
 
@@ -666,39 +678,53 @@
     </html>
     """
     html = [htmlTop]
+    names = {}
+    argv = sys.argv[1:]
+    E = [a for a in argv if a.startswith('--ext=')]
+    if not E:
+        E = ['gif','tiff', 'png', 'jpg', 'pct', 'py', 'svg']
+    else:
+        for a in E:
+            argv.remove(a)
+        E = (','.join([a[6:] for a in E])).split(',')
 
-    i = 0
     #print in a loop, with their doc strings
     for (drawing, docstring, name) in getAllTestDrawings(doTTF=hasattr(_renderPM,'ft_get_face')):
-        fnRoot = 'renderPM%d' % i
-        if 1 or i==10:
-            w = int(drawing.width)
-            h = int(drawing.height)
-            html.append('<hr><h2>Drawing %s %d</h2>\n<pre>%s</pre>' % (name, i, docstring))
+        i = names[name] = names.setdefault(name,0)+1
+        if i>1: name += '.%02d' % (i-1)
+        if argv and name not in argv: continue
+        fnRoot = name
+        w = int(drawing.width)
+        h = int(drawing.height)
+        html.append('<hr><h2>Drawing %s</h2>\n<pre>%s</pre>' % (name, docstring))
 
-            for k in ['gif','tiff', 'png', 'jpg', 'pct']:
-                if k in ['gif','png','jpg','pct']:
-                    html.append('<p>%s format</p>\n' % string.upper(k))
-                try:
-                    filename = '%s.%s' % (fnRoot, ext(k))
-                    fullpath = os.path.join('pmout', filename)
-                    if os.path.isfile(fullpath):
-                        os.remove(fullpath)
-                    if k=='pct':
-                        from reportlab.lib.colors import white
-                        drawToFile(drawing,fullpath,fmt=k,configPIL={'transparent':white})
-                    else:
-                        drawToFile(drawing,fullpath,fmt=k)
-                    if k in ['gif','png','jpg']:
-                        html.append('<img src="%s" border="1"><br>\n' % filename)
-                    print 'wrote',fullpath
-                except AttributeError:
-                    print 'Problem drawing %s file'%k
-                    raise
+        for k in E:
+            if k in ['gif','png','jpg','pct']:
+                html.append('<p>%s format</p>\n' % string.upper(k))
+            try:
+                filename = '%s.%s' % (fnRoot, ext(k))
+                fullpath = os.path.join('pmout', filename)
+                if os.path.isfile(fullpath):
+                    os.remove(fullpath)
+                if k=='pct':
+                    from reportlab.lib.colors import white
+                    drawToFile(drawing,fullpath,fmt=k,configPIL={'transparent':white})
+                elif k in ['py','svg']:
+                    drawing.save(formats=['py','svg'],outDir='pmout',fnRoot=fnRoot)
+                else:
+                    drawToFile(drawing,fullpath,fmt=k)
+                if k in ['gif','png','jpg']:
+                    html.append('<img src="%s" border="1"><br>\n' % filename)
+                elif k=='py':
+                    html.append('<a href="%s">python source</a><br>\n' % filename)
+                elif k=='svg':
+                    html.append('<a href="%s">SVG</a><br>\n' % filename)
+                print 'wrote',fullpath
+            except AttributeError:
+                print 'Problem drawing %s file'%k
+                raise
         if os.environ.get('RL_NOEPSPREVIEW','0')=='1': drawing.__dict__['preview'] = 0
         drawing.save(formats=['eps','pdf'],outDir='pmout',fnRoot=fnRoot)
-        i = i + 1
-        #if i==10: break
     html.append(htmlBottom)
     htmlFileName = os.path.join('pmout', 'index.html')
     open(htmlFileName, 'w').writelines(html)
--- a/src/reportlab/graphics/renderPS.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/renderPS.py	Thu Oct 21 10:34:13 2010 +0000
@@ -592,7 +592,7 @@
         outstream = getStringIO(hex_encoded)
 
         dataline = outstream.read(78)
-        while dataline <> "":
+        while dataline != "":
             self.code_append(dataline)
             dataline= outstream.read(78)
         self.code_append('% end of image data') # for clarity
@@ -662,7 +662,7 @@
         outstream = getStringIO(hex_encoded)
 
         dataline = outstream.read(78)
-        while dataline <> "":
+        while dataline != "":
             self.code_append(dataline)
             dataline= outstream.read(78)
         self.code_append('> % end of image data') # > is EOD for hex encoded filterfor clarity
@@ -706,7 +706,7 @@
     def drawNode(self, node):
         """This is the recursive method called for each node
         in the tree"""
-        self._canvas.comment('begin node %s'%`node`)
+        self._canvas.comment('begin node %r'%node)
         color = self._canvas._color
         if not (isinstance(node, Path) and node.isClipPath):
             self._canvas.saveState()
@@ -722,12 +722,12 @@
         rDeltas = self._tracker.pop()
         if not (isinstance(node, Path) and node.isClipPath):
             self._canvas.restoreState()
-        self._canvas.comment('end node %s'%`node`)
+        self._canvas.comment('end node %r'%node)
         self._canvas._color = color
 
         #restore things we might have lost (without actually doing anything).
         for k, v in rDeltas.items():
-            if self._restores.has_key(k):
+            if k in self._restores:
                 setattr(self._canvas,self._restores[k],v)
 
 ##  _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap',
@@ -799,7 +799,7 @@
                 elif text_anchor=='middle':
                     x -= textLen/2
                 elif text_anchor=='numeric':
-                    x -= numericXShift(text_anchor,text,textLen,font,fontSize)
+                    x -= numericXShift(text_anchor,text,textLen,font,fontSize,encoding='winansi')
                 else:
                     raise ValueError, 'bad value for text_anchor '+str(text_anchor)
             self._canvas.drawString(x,y,text)
--- a/src/reportlab/graphics/renderSVG.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/renderSVG.py	Thu Oct 21 10:34:13 2010 +0000
@@ -27,7 +27,8 @@
 cos = math.cos
 pi = math.pi
 
-LINE_STYLES = 'stroke-width stroke-linecap stroke fill stroke-dasharray'
+AREA_STYLES = 'stroke-width stroke-linecap stroke fill stroke-dasharray'
+LINE_STYLES = 'stroke-width stroke-linecap stroke stroke-dasharray'
 TEXT_STYLES = 'font-family font-size'
 
 ### top-level user function ###
@@ -183,23 +184,17 @@
 
         return stringWidth(s, font, fontSize)
 
-    def _formatStyle(self, include=''):
-        include = include.split()
-        keys = self.style.keys()
+    def _formatStyle(self, include='', exclude='',**kwds):
+        style = self.style.copy()
+        style.update(kwds)
+        keys = style.keys()
         if include:
-            #2.1-safe version of the line below follows:
-            #keys = filter(lambda k: k in include, keys)
-            tmp = []
-            for word in keys:
-                if word in include:
-                    tmp.append(word)
-            keys = tmp
-
-        items = []
-        for k in keys:
-            items.append((k, self.style[k]))
-        items = map(lambda i: "%s: %s"%(i[0], i[1]), items)
-
+            keys = [k for k in keys if k in include.split()]
+        if exclude:
+            exclude = exclude.split()
+            items = [k+': '+str(style[k]) for k in keys if k not in exclude]
+        else:
+            items = [k+': '+str(style[k]) for k in keys]
         return '; '.join(items) + ';'
 
     def _escape(self, s):
@@ -233,9 +228,9 @@
 
         return codeline % data
 
-    def _fillAndStroke(self, code, clip=0, link_info=None):
+    def _fillAndStroke(self, code, clip=0, link_info=None,styles=AREA_STYLES):
         path = transformNode(self.doc, "path",
-            d=self.path, style=self._formatStyle(LINE_STYLES))
+            d=self.path, style=self._formatStyle(styles))
         if link_info :
             path = self._add_link(path, link_info)
         self.currGroup.appendChild(path)
@@ -309,9 +304,11 @@
 
         if self.verbose: print "+++ SVGCanvas.rect"
 
+        x = min(x1,x2)
+        y = min(y1,y2)
         rect = transformNode(self.doc, "rect",
-            x=x1, y=y1, width=x2-x1, height=y2-y1,
-            style=self._formatStyle(LINE_STYLES))
+            x=x, y=y, width=max(x1,x2)-x, height=max(y1,y2)-y,
+            style=self._formatStyle(AREA_STYLES))
 
         if link_info :
             rect = self._add_link(rect, link_info)
@@ -327,7 +324,7 @@
 
         rect = transformNode(self.doc, "rect",
             x=x1, y=y1, width=x2-x1, height=y2-y1, rx=rx, ry=ry,
-            style=self._formatStyle(LINE_STYLES))
+            style=self._formatStyle(AREA_STYLES))
 
         if link_info :
             rect = self._add_link(rect, link_info)
@@ -403,7 +400,7 @@
 
         ellipse = transformNode(self.doc, "ellipse",
             cx=(x1+x2)/2.0, cy=(y1+y2)/2.0, rx=(x2-x1)/2.0, ry=(y2-y1)/2.0,
-            style=self._formatStyle(LINE_STYLES))
+            style=self._formatStyle(AREA_STYLES))
 
         if link_info:
             ellipse = self._add_link(ellipse, link_info)
@@ -413,7 +410,7 @@
     def circle(self, xc, yc, r, link_info=None):
         circle = transformNode(self.doc, "circle",
             cx=xc, cy=yc, r=r,
-            style=self._formatStyle(LINE_STYLES))
+            style=self._formatStyle(AREA_STYLES))
 
         if link_info:
             circle = self._add_link(circle, link_info)
@@ -478,7 +475,7 @@
                 pairs.append("%f %f" % (points[i]))
             pts = ', '.join(pairs)
             polyline = transformNode(self.doc, "polygon",
-                points=pts, style=self._formatStyle(LINE_STYLES))
+                points=pts, style=self._formatStyle(AREA_STYLES))
 
             if link_info:
                 polyline = self._add_link(polyline, link_info)
@@ -507,7 +504,7 @@
                 pairs.append("%f %f" % (points[i]))
             pts = ', '.join(pairs)
             polyline = transformNode(self.doc, "polyline",
-                points=pts, style=self._formatStyle(LINE_STYLES))
+                points=pts, style=self._formatStyle(AREA_STYLES,fill=None))
             self.currGroup.appendChild(polyline)
 
     ### groups ###
@@ -582,7 +579,7 @@
 
         if self.verbose: print "### begin _SVGRenderer.drawNode(%r)" % node
 
-        self._canvas.comment('begin node %s'%`node`)
+        self._canvas.comment('begin node %r'%node)
         color = self._canvas._color
         style = self._canvas.style.copy()
         if not (isinstance(node, Path) and node.isClipPath):
@@ -599,12 +596,12 @@
         rDeltas = self._tracker.pop()
         if not (isinstance(node, Path) and node.isClipPath):
             pass #self._canvas.restoreState()
-        self._canvas.comment('end node %s'%`node`)
+        self._canvas.comment('end node %r'%node)
         self._canvas._color = color
 
         #restore things we might have lost (without actually doing anything).
         for k, v in rDeltas.items():
-            if self._restores.has_key(k):
+            if k in self._restores:
                 setattr(self._canvas,self._restores[k],v)
         self._canvas.style = style
 
--- a/src/reportlab/graphics/renderbase.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/renderbase.py	Thu Oct 21 10:34:13 2010 +0000
@@ -38,7 +38,7 @@
     the given shape would have on the graphics state"""
     delta = {}
     for (prop, value) in shape.getProperties().items():
-        if STATE_DEFAULTS.has_key(prop):
+        if prop in STATE_DEFAULTS:
             delta[prop] = value
     return delta
 
@@ -62,7 +62,7 @@
         if defaults is None:
             defaults = STATE_DEFAULTS.copy()
         #ensure  that if we have a transform, we have a CTM
-        if defaults.has_key('transform'):
+        if 'transform' in defaults:
             defaults['ctm'] = defaults['transform']
         self._combined.append(defaults)
 
@@ -100,7 +100,7 @@
         for key, curValue in lastDelta.items():
             #print '   key=%s, value=%s' % (key, curValue)
             prevValue = newState[key]
-            if prevValue <> curValue:
+            if prevValue != curValue:
                 #print '    state popping "%s"="%s"' % (key, curValue)
                 if key == 'transform':
                     reverseDelta[key] = inverse(lastDelta['transform'])
@@ -127,9 +127,10 @@
 def testStateTracker():
     print 'Testing state tracker'
     defaults = {'fillColor':None, 'strokeColor':None,'fontName':None, 'transform':[1,0,0,1,0,0]}
+    from reportlab.graphics.shapes import _baseGFontName
     deltas = [
         {'fillColor':'red'},
-        {'fillColor':'green', 'strokeColor':'blue','fontName':'Times-Roman'},
+        {'fillColor':'green', 'strokeColor':'blue','fontName':_baseGFontName},
         {'transform':[0.5,0,0,0.5,0,0]},
         {'transform':[0.5,0,0,0.5,2,3]},
         {'strokeColor':'red'}
@@ -253,6 +254,9 @@
                 ocanvas = None
 
             self.fillDerivedValues(node)
+            dtcb = getattr(node,'_drawTimeCallback',None)
+            if dtcb:
+                dtcb(node,canvas=canvas,renderer=self)
             #draw the object, or recurse
             if isinstance(node, Line):
                 self.drawLine(node)
--- a/src/reportlab/graphics/samples/bubble.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/bubble.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class Bubble(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/clustered_bar.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/clustered_bar.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class ClusteredBar(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
@@ -81,4 +81,4 @@
         self._add(self,0,name='preview',validate=None,desc=None)
 
 if __name__=="__main__": #NORUNTESTS
-    ClusteredBar().save(formats=['pdf'],outDir=None,fnRoot='clustered_bar')
\ No newline at end of file
+    ClusteredBar().save(formats=['pdf'],outDir=None,fnRoot='clustered_bar')
--- a/src/reportlab/graphics/samples/clustered_column.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/clustered_column.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class ClusteredColumn(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/exploded_pie.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/exploded_pie.py	Thu Oct 21 10:34:13 2010 +0000
@@ -8,7 +8,7 @@
 
 class ExplodedPie(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,Pie(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 100
         self.chart.height     = 100
@@ -62,4 +62,4 @@
         self._add(self,0,name='preview',validate=None,desc=None)
 
 if __name__=="__main__": #NORUNTESTS
-    ExplodedPie().save(formats=['pdf'],outDir=None,fnRoot='exploded_pie')
\ No newline at end of file
+    ExplodedPie().save(formats=['pdf'],outDir=None,fnRoot='exploded_pie')
--- a/src/reportlab/graphics/samples/filled_radar.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/filled_radar.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class FilledRadarChart(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 90
         self.chart.height     = 90
--- a/src/reportlab/graphics/samples/line_chart.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/line_chart.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class LineChart(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/linechart_with_markers.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/linechart_with_markers.py	Thu Oct 21 10:34:13 2010 +0000
@@ -8,7 +8,7 @@
 
 class LineChartWithMarkers(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/radar.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/radar.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class RadarChart(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 90
         self.chart.height     = 90
--- a/src/reportlab/graphics/samples/scatter.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/scatter.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class Scatter(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/scatter_lines.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/scatter_lines.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class ScatterLines(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/scatter_lines_markers.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/scatter_lines_markers.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class ScatterLinesMarkers(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/samples/simple_pie.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/simple_pie.py	Thu Oct 21 10:34:13 2010 +0000
@@ -8,7 +8,7 @@
 
 class SimplePie(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,Pie(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 100
         self.chart.height     = 100
@@ -58,4 +58,4 @@
         self._add(self,0,name='preview',validate=None,desc=None)
 
 if __name__=="__main__": #NORUNTESTS
-    SimplePie().save(formats=['pdf'],outDir=None,fnRoot=None)
\ No newline at end of file
+    SimplePie().save(formats=['pdf'],outDir=None,fnRoot=None)
--- a/src/reportlab/graphics/samples/stacked_bar.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/stacked_bar.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class StackedBar(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
@@ -82,4 +82,4 @@
         self._add(self,0,name='preview',validate=None,desc=None)
 
 if __name__=="__main__": #NORUNTESTS
-    StackedBar().save(formats=['pdf'],outDir=None,fnRoot='stacked_bar')
\ No newline at end of file
+    StackedBar().save(formats=['pdf'],outDir=None,fnRoot='stacked_bar')
--- a/src/reportlab/graphics/samples/stacked_column.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/samples/stacked_column.py	Thu Oct 21 10:34:13 2010 +0000
@@ -7,7 +7,7 @@
 
 class StackedColumn(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
-        apply(Drawing.__init__,(self,width,height)+args,kw)
+        Drawing.__init__(self,width,height,*args,**kw)
         self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart")
         self.chart.width      = 115
         self.chart.height     = 80
--- a/src/reportlab/graphics/shapes.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/shapes.py	Thu Oct 21 10:34:13 2010 +0000
@@ -11,13 +11,18 @@
 from pprint import pprint
 
 from reportlab.platypus import Flowable
-from reportlab.rl_config import shapeChecking, verbose, defaultGraphicsFontName, _unset_
+from reportlab.rl_config import shapeChecking, verbose, defaultGraphicsFontName as _baseGFontName, _unset_
 from reportlab.lib import logger
 from reportlab.lib import colors
 from reportlab.lib.validators import *
+isOpacity = NoneOr(isNumberInRange(0,1))
 from reportlab.lib.attrmap import *
 from reportlab.lib.utils import fp_str
 from reportlab.pdfbase.pdfmetrics import stringWidth
+from reportlab.lib.fonts import tt2ps
+_baseGFontNameB = tt2ps(_baseGFontName,1,0)
+_baseGFontNameI = tt2ps(_baseGFontName,0,1)
+_baseGFontNameBI = tt2ps(_baseGFontName,1,1)
 
 class NotImplementedError(Exception):
     pass
@@ -43,16 +48,17 @@
     'strokeLineJoin': 0,
     'strokeMiterLimit' : 10,    # don't know yet so let bomb here
     'strokeDashArray': None,
-    'strokeOpacity': 1.0,  #100%
-    'fillOpacity': 1.0,
+    'strokeOpacity': None, #100%
+    'fillOpacity': None,
     'fillOverprint': False,
     'strokeOverprint': False,
+    'overprintMask': 0,
 
     'fillColor': colors.black,   #...or text will be invisible
     #'fillRule': NON_ZERO_WINDING, - these can be done later
 
     'fontSize': 10,
-    'fontName': defaultGraphicsFontName,
+    'fontName': _baseGFontName,
     'textAnchor':  'start' # can be start, middle, end, inherited
     }
 
@@ -250,7 +256,7 @@
         #may need to override this.
         props = {}
         for key, value in self.__dict__.items():
-            if key[0:1] <> '_':
+            if key[0:1] != '_':
                 props[key] = value
         return props
 
@@ -283,8 +289,8 @@
 
         if self._attrMap is not None:
             for key in self.__dict__.keys():
-                if key[0] <> '_':
-                    assert self._attrMap.has_key(key), "Unexpected attribute %s found in %s" % (key, self)
+                if key[0] != '_':
+                    assert key in self._attrMap, "Unexpected attribute %s found in %s" % (key, self)
             for (attr, metavalue) in self._attrMap.items():
                 assert hasattr(self, attr), "Missing attribute %s from %s" % (attr, self)
                 value = getattr(self, attr)
@@ -311,10 +317,11 @@
     case they are subsequently accessible as properties."""
 
     _attrMap = AttrMap(
-        transform = AttrMapValue(isTransform,desc="Coordinate transformation to apply"),
+        transform = AttrMapValue(isTransform,desc="Coordinate transformation to apply",advancedUsage=1),
         contents = AttrMapValue(isListOfShapes,desc="Contained drawable elements"),
         strokeOverprint = AttrMapValue(isBoolean,desc='Turn on stroke overprinting'),
-        fillOverprint = AttrMapValue(isBoolean,desc='Turn on fill overprinting'),
+        fillOverprint = AttrMapValue(isBoolean,desc='Turn on fill overprinting',advancedUsage=1),
+        overprintMask = AttrMapValue(isBoolean,desc='overprinting for ordinary CMYK',advancedUsage=1),
         )
 
     def __init__(self, *elements, **keywords):
@@ -497,7 +504,7 @@
     c = obj.__class__
     m = getmodule(c).__name__
     n = n or c.__name__
-    if not I.has_key(m):
+    if m not in I:
         I[m] = [n]
     elif n not in I[m]:
         I[m].append(n)
@@ -576,7 +583,7 @@
         width = AttrMapValue(isNumber,desc="Drawing width in points."),
         height = AttrMapValue(isNumber,desc="Drawing height in points."),
         canv = AttrMapValue(None),
-        background = AttrMapValue(isValidChildOrNone,desc="Background widget for the drawing"),
+        background = AttrMapValue(isValidChildOrNone,desc="Background widget for the drawing e.g. Rect(0,0,width,height)"),
         hAlign = AttrMapValue(OneOf("LEFT", "RIGHT", "CENTER", "CENTRE"), desc="Horizontal alignment within parent document"),
         vAlign = AttrMapValue(OneOf("TOP", "BOTTOM", "CENTER", "CENTRE"), desc="Vertical alignment within parent document"),
         #AR temporary hack to track back up.
@@ -605,7 +612,7 @@
             s = s + 'from %s import %s\n' % (m,string.replace(str(o)[1:-1],"'",""))
         s = s + '\nclass %s(_DrawingEditorMixin,Drawing):\n' % n
         s = s + '\tdef __init__(self,width=%s,height=%s,*args,**kw):\n' % (self.width,self.height)
-        s = s + '\t\tapply(Drawing.__init__,(self,width,height)+args,kw)\n'
+        s = s + '\t\tDrawing.__init__(self,width,height,*args,**kw)\n'
         s = s + G
         s = s + '\n\nif __name__=="__main__": #NORUNTESTS\n\t%s().save(formats=[\'pdf\'],outDir=\'.\',fnRoot=None)\n' % n
         return s
@@ -638,7 +645,7 @@
         return self._copy(self.__class__(self.width, self.height))
 
     def asGroup(self,*args,**kw):
-        return self._copy(apply(Group,args,kw))
+        return self._copy(Group(*args,**kw))
 
     def save(self, formats=None, verbose=None, fnRoot=None, outDir=None, title='', **kw):
         """Saves copies of self in desired location and formats.
@@ -647,16 +654,24 @@
         the extra keywords can be of the form
         _renderPM_dpi=96 (which passes dpi=96 to renderPM)
         """
+        genFmt = kw.pop('seqNumber','')
+        if isinstance(genFmt,int):
+            genFmt = '%4d: ' % genFmt
+        else:
+            genFmt = ''
+        genFmt += 'generating %s file %s'
         from reportlab import rl_config
         ext = ''
         if not fnRoot:
             fnRoot = getattr(self,'fileNamePattern',(self.__class__.__name__+'%03d'))
             chartId = getattr(self,'chartId',0)
-            if callable(fnRoot):
+            if hasattr(chartId,'__call__'):
+                chartId = chartId(self)
+            if hasattr(fnRoot,'__call__'):
                 fnRoot = fnRoot(chartId)
             else:
                 try:
-                    fnRoot = fnRoot % getattr(self,'chartId',0)
+                    fnRoot = fnRoot % chartId
                 except TypeError, err:
                     #the exact error message changed from 2.2 to 2.3 so we need to
                     #check a substring
@@ -682,7 +697,7 @@
         if 'pdf' in plotMode:
             from reportlab.graphics import renderPDF
             filename = fnroot+'.pdf'
-            if verbose: print "generating PDF file %s" % filename
+            if verbose: print genFmt % ('PDF',filename)
             renderPDF.drawToFile(self, filename, title, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPDF_',**kw))
             ext = ext +  '/.pdf'
             if sys.platform=='mac':
@@ -694,9 +709,17 @@
             if bmFmt in plotMode:
                 from reportlab.graphics import renderPM
                 filename = '%s.%s' % (fnroot,bmFmt)
-                if verbose: print "generating %s file %s" % (bmFmt,filename)
+                if verbose: print genFmt % (bmFmt,filename)
+                dtc = getattr(self,'_drawTimeCollector',None)
+                if dtc:
+                    dtcfmts = getattr(dtc,'formats',[bmFmt])
+                    if bmFmt in dtcfmts and not getattr(dtc,'disabled',0):
+                        dtc.clear()
+                    else:
+                        dtc = None
                 renderPM.drawToFile(self, filename,fmt=bmFmt,showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPM_',**kw))
                 ext = ext + '/.' + bmFmt
+                if dtc: dtc.save(fnroot)
 
         if 'eps' in plotMode:
             try:
@@ -704,7 +727,7 @@
             except ImportError:
                 from reportlab.graphics import renderPS
             filename = fnroot+'.eps'
-            if verbose: print "generating EPS file %s" % filename
+            if verbose: print genFmt % ('EPS',filename)
             renderPS.drawToFile(self,
                                 filename,
                                 title = fnroot,
@@ -719,7 +742,7 @@
         if 'svg' in plotMode:
             from reportlab.graphics import renderSVG
             filename = fnroot+'.svg'
-            if verbose: print "generating EPS file %s" % filename
+            if verbose: print genFmt % ('SVG',filename)
             renderSVG.drawToFile(self,
                                 filename,
                                 showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderSVG_',**kw))
@@ -728,13 +751,13 @@
         if 'ps' in plotMode:
             from reportlab.graphics import renderPS
             filename = fnroot+'.ps'
-            if verbose: print "generating EPS file %s" % filename
+            if verbose: print genFmt % ('EPS',filename)
             renderPS.drawToFile(self, filename, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPS_',**kw))
             ext = ext +  '/.ps'
 
         if 'py' in plotMode:
             filename = fnroot+'.py'
-            if verbose: print "generating py file %s" % filename
+            if verbose: print genFmt % ('py',filename)
             open(filename,'w').write(self._renderPy())
             ext = ext +  '/.py'
 
@@ -797,7 +820,7 @@
         '''
         ivc = isValidChild(value)
         if name and hasattr(obj,'_attrMap'):
-            if not obj.__dict__.has_key('_attrMap'):
+            if '_attrMap' not in obj.__dict__:
                 obj._attrMap = obj._attrMap.clone()
             if ivc and validate is None: validate = isValidChild
             obj._attrMap[name] = AttrMapValue(validate,desc)
@@ -820,9 +843,10 @@
         strokeLineCap = AttrMapValue(OneOf(0,1,2),desc="Line cap 0=butt, 1=round & 2=square"),
         strokeLineJoin = AttrMapValue(OneOf(0,1,2),desc="Line join 0=miter, 1=round & 2=bevel"),
         strokeMiterLimit = AttrMapValue(isNumber,desc="miter limit control miter line joins"),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        strokeOpacity = AttrMapValue(isNumberInRange(0, 1)),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc="a sequence of numbers represents on and off, e.g. (2,1)"),
+        strokeOpacity = AttrMapValue(isOpacity,desc="The level of transparency of the line, any real number betwen 0 and 1"),
         strokeOverprint = AttrMapValue(isBoolean,desc='Turn on stroke overprinting'),
+        overprintMask = AttrMapValue(isBoolean,desc='overprinting for ordinary CMYK',advancedUsage=1),
         )
 
     def __init__(self, kw):
@@ -832,16 +856,16 @@
         self.strokeLineJoin = 0
         self.strokeMiterLimit = 0
         self.strokeDashArray = None
-        self.strokeOpacity = 1
+        self.strokeOpacity = None
         self.setProperties(kw)
 
 
 class Line(LineShape):
     _attrMap = AttrMap(BASE=LineShape,
-        x1 = AttrMapValue(isNumber),
-        y1 = AttrMapValue(isNumber),
-        x2 = AttrMapValue(isNumber),
-        y2 = AttrMapValue(isNumber),
+        x1 = AttrMapValue(isNumber,desc=""),
+        y1 = AttrMapValue(isNumber,desc=""),
+        x2 = AttrMapValue(isNumber,desc=""),
+        y2 = AttrMapValue(isNumber,desc=""),
         )
 
     def __init__(self, x1, y1, x2, y2, **kw):
@@ -855,19 +879,19 @@
         "Returns bounding rectangle of object as (x1,y1,x2,y2)"
         return (self.x1, self.y1, self.x2, self.y2)
 
-
 class SolidShape(LineShape):
     # base for anything with outline and content
 
     _attrMap = AttrMap(BASE=LineShape,
-        fillColor = AttrMapValue(isColorOrNone),
-        fillOpacity = AttrMapValue(isNumberInRange(0, 1)),
+        fillColor = AttrMapValue(isColorOrNone,desc="filling color of the shape, e.g. red"),
+        fillOpacity = AttrMapValue(isOpacity,desc="the level of transparency of the color, any real number between 0 and 1"),
         fillOverprint = AttrMapValue(isBoolean,desc='Turn on fill overprinting'),
+        overprintMask = AttrMapValue(isBoolean,desc='overprinting for ordinary CMYK',advancedUsage=1),
         )
 
     def __init__(self, kw):
         self.fillColor = STATE_DEFAULTS['fillColor']
-        self.fillOpacity = 1
+        self.fillOpacity = None
         # do this at the end so keywords overwrite
         #the above settings
         LineShape.__init__(self, kw)
@@ -889,7 +913,7 @@
         nArgs = _PATH_OP_ARG_COUNT[op]
         func = drawFuncs[op]
         j = i + nArgs
-        apply(func, points[i:j])
+        func(*points[i:j])
         i = j
         if op == _CLOSEPATH:
             hadClosePath = hadClosePath + 1
@@ -999,7 +1023,7 @@
     for d,o in (dx,0), (dy,1):
         for i in xrange(o,len(P),2):
             P[i] = P[i]+d
-    return apply(Path,(P,O,isClipPath),kw)
+    return Path(P,O,isClipPath,**kw)
 
 class Rect(SolidShape):
     """Rectangle, possibly with rounded corners."""
@@ -1007,8 +1031,8 @@
     _attrMap = AttrMap(BASE=SolidShape,
         x = AttrMapValue(isNumber),
         y = AttrMapValue(isNumber),
-        width = AttrMapValue(isNumber),
-        height = AttrMapValue(isNumber),
+        width = AttrMapValue(isNumber,desc="width of the object in points"),
+        height = AttrMapValue(isNumber,desc="height of the objects in points"),
         rx = AttrMapValue(isNumber),
         ry = AttrMapValue(isNumber),
         )
@@ -1037,8 +1061,8 @@
     _attrMap = AttrMap(BASE=SolidShape,
         x = AttrMapValue(isNumber),
         y = AttrMapValue(isNumber),
-        width = AttrMapValue(isNumberOrNone),
-        height = AttrMapValue(isNumberOrNone),
+        width = AttrMapValue(isNumberOrNone,desc="width of the object in points"),
+        height = AttrMapValue(isNumberOrNone,desc="height of the objects in points"),
         path = AttrMapValue(None),
         )
 
@@ -1062,9 +1086,9 @@
 class Circle(SolidShape):
 
     _attrMap = AttrMap(BASE=SolidShape,
-        cx = AttrMapValue(isNumber),
-        cy = AttrMapValue(isNumber),
-        r = AttrMapValue(isNumber),
+        cx = AttrMapValue(isNumber,desc="x of the centre"),
+        cy = AttrMapValue(isNumber,desc="y of the centre"),
+        r = AttrMapValue(isNumber,desc="radius in points"),
         )
 
     def __init__(self, cx, cy, r, **kw):
@@ -1083,10 +1107,10 @@
 
 class Ellipse(SolidShape):
     _attrMap = AttrMap(BASE=SolidShape,
-        cx = AttrMapValue(isNumber),
-        cy = AttrMapValue(isNumber),
-        rx = AttrMapValue(isNumber),
-        ry = AttrMapValue(isNumber),
+        cx = AttrMapValue(isNumber,desc="x of the centre"),
+        cy = AttrMapValue(isNumber,desc="y of the centre"),
+        rx = AttrMapValue(isNumber,desc="x radius"),
+        ry = AttrMapValue(isNumber,desc="y radius"),
         )
 
     def __init__(self, cx, cy, rx, ry, **kw):
@@ -1109,9 +1133,9 @@
        from start angle to end angle"""
 
     _attrMap = AttrMap(BASE=SolidShape,
-        centerx = AttrMapValue(isNumber),
-        centery = AttrMapValue(isNumber),
-        radius = AttrMapValue(isNumber),
+        centerx = AttrMapValue(isNumber,desc="x of the centre"),
+        centery = AttrMapValue(isNumber,desc="y of the centre"),
+        radius = AttrMapValue(isNumber,desc="radius in points"),
         startangledegrees = AttrMapValue(isNumber),
         endangledegrees = AttrMapValue(isNumber),
         yradius = AttrMapValue(isNumberOrNone),
@@ -1202,13 +1226,13 @@
     joined back to the start for you."""
 
     _attrMap = AttrMap(BASE=SolidShape,
-        points = AttrMapValue(isListOfNumbers),
+        points = AttrMapValue(isListOfNumbers,desc="list of numbers in the form x1, y1, x2, y2 ... xn, yn"),
         )
 
     def __init__(self, points=[], **kw):
         SolidShape.__init__(self, kw)
         assert len(points) % 2 == 0, 'Point list must have even number of elements!'
-        self.points = points
+        self.points = points or []
 
     def copy(self):
         new = self.__class__(self.points)
@@ -1224,11 +1248,12 @@
     Put the numbers in the list, not two-tuples."""
 
     _attrMap = AttrMap(BASE=LineShape,
-        points = AttrMapValue(isListOfNumbers),
+        points = AttrMapValue(isListOfNumbers,desc="list of numbers in the form x1, y1, x2, y2 ... xn, yn"),
         )
 
     def __init__(self, points=[], **kw):
         LineShape.__init__(self, kw)
+        points = points or []
         lenPoints = len(points)
         if lenPoints:
             if type(points[0]) in (ListType,TupleType):
@@ -1249,8 +1274,8 @@
     def getBounds(self):
         return getPointsBounds(self.points)
 
-def numericXShift(tA,text,w,fontName,fontSize,encoding=None):
-    dp = getattr(tA,'_dp','.')
+def numericXShift(tA,text,w,fontName,fontSize,encoding=None,pivotCharacter='.'):
+    dp = getattr(tA,'_dp',pivotCharacter)
     i = text.rfind(dp)
     if i>=0:
         dpOffs = getattr(tA,'_dpLen',0)
@@ -1263,13 +1288,13 @@
 
     # to do.
     _attrMap = AttrMap(
-        x = AttrMapValue(isNumber),
-        y = AttrMapValue(isNumber),
-        text = AttrMapValue(isString),
-        fontName = AttrMapValue(None),
-        fontSize = AttrMapValue(isNumber),
-        fillColor = AttrMapValue(isColorOrNone),
-        textAnchor = AttrMapValue(OneOf('start','middle','end','numeric')),
+        x = AttrMapValue(isNumber,desc="x point of anchoring"),
+        y = AttrMapValue(isNumber,desc="y point of anchoring"),
+        text = AttrMapValue(isString,desc="the text of the string"),
+        fontName = AttrMapValue(None,desc="font name of the text - font is either acrobat standard or registered when using external font."),
+        fontSize = AttrMapValue(isNumber,desc="font size"),
+        fillColor = AttrMapValue(isColorOrNone,desc="color of the font"),
+        textAnchor = AttrMapValue(OneOf('start','middle','end','numeric'),desc="treat (x,y) as one of the options below."),
         encoding = AttrMapValue(isString),
         )
     encoding = 'utf8'
--- a/src/reportlab/graphics/testshapes.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/testshapes.py	Thu Oct 21 10:34:13 2010 +0000
@@ -26,15 +26,47 @@
 from reportlab.graphics.renderPDF import _PDFRenderer
 import unittest
 
-_FONTS = ['Times-Roman','Courier','Times-BoldItalic',]
+_FONTS = ['Times-Roman','Vera','Times-BoldItalic',]
+
+def _setup():
+    from reportlab.pdfbase import pdfmetrics, ttfonts
+    pdfmetrics.registerFont(ttfonts.TTFont("Vera", "Vera.ttf"))
+    pdfmetrics.registerFont(ttfonts.TTFont("VeraBd", "VeraBd.ttf"))
+    pdfmetrics.registerFont(ttfonts.TTFont("VeraIt", "VeraIt.ttf"))
+    pdfmetrics.registerFont(ttfonts.TTFont("VeraBI", "VeraBI.ttf"))
+    F = ['Times-Roman','Courier','Helvetica','Vera', 'VeraBd', 'VeraIt', 'VeraBI']
+    if sys.platform=='win32':
+        for name, ttf in [
+            ('Adventurer Light SF','Advlit.ttf'),('ArialMS','ARIAL.TTF'),
+            ('Arial Unicode MS', 'ARIALUNI.TTF'),
+            ('Book Antiqua','BKANT.TTF'),
+            ('Century Gothic','GOTHIC.TTF'),
+            ('Comic Sans MS', 'COMIC.TTF'),
+            ('Elementary Heavy SF Bold','Vwagh.ttf'),
+            ('Firenze SF','flot.ttf'),
+            ('Garamond','GARA.TTF'),
+            ('Jagger','Rols.ttf'),
+            ('Monotype Corsiva','MTCORSVA.TTF'),
+            ('Seabird SF','seag.ttf'),
+            ('Tahoma','TAHOMA.TTF'),
+            ('VerdanaMS','VERDANA.TTF'),
+            ]:
+            for D in ('c:\WINNT','c:\Windows'):
+                fn = os.path.join(D,'Fonts',ttf)
+                if os.path.isfile(fn):
+                    try:
+                        f = ttfonts.TTFont(name, fn)
+                        pdfmetrics.registerFont(f)
+                        F.append(name)
+                    except:
+                        pass
+_setup()
 
 #########################################################
 #
 #   Collections of shape drawings.
 #
 #########################################################
-
-
 def getFailedDrawing(funcName):
     """Generate a drawing in case something goes really wrong.
 
@@ -193,8 +225,7 @@
                fillColor=green))  #square corners
     D.add(Rect(220, 150, 60, 30, 10, 10, fillColor=green))  #round corners
 
-    from reportlab.lib.validators import inherit
-    D.add(String(10,50, 'Basic Shapes', fillColor=colors.black, fontName=inherit))
+    D.add(String(10,50, 'Basic Shapes', fillColor=colors.black, fontName='Helvetica'))
 
     return D
 
@@ -391,39 +422,6 @@
 
 def getDrawing13():
     'Test Various TTF Fonts'
-    from reportlab.pdfbase import pdfmetrics, ttfonts
-    pdfmetrics.registerFont(ttfonts.TTFont("Vera", "Vera.ttf"))
-    pdfmetrics.registerFont(ttfonts.TTFont("VeraBd", "VeraBd.ttf"))
-    pdfmetrics.registerFont(ttfonts.TTFont("VeraIt", "VeraIt.ttf"))
-    pdfmetrics.registerFont(ttfonts.TTFont("VeraBI", "VeraBI.ttf"))
-    _FONTS[1]='Vera'
-    _FONTS[1]='VeraBI'
-    F = ['Times-Roman','Courier','Helvetica','Vera', 'VeraBd', 'VeraIt', 'VeraBI']
-    if sys.platform=='win32':
-        for name, ttf in [
-            ('Adventurer Light SF','Advlit.ttf'),('ArialMS','ARIAL.TTF'),
-            ('Arial Unicode MS', 'ARIALUNI.TTF'),
-            ('Book Antiqua','BKANT.TTF'),
-            ('Century Gothic','GOTHIC.TTF'),
-            ('Comic Sans MS', 'COMIC.TTF'),
-            ('Elementary Heavy SF Bold','Vwagh.ttf'),
-            ('Firenze SF','flot.ttf'),
-            ('Garamond','GARA.TTF'),
-            ('Jagger','Rols.ttf'),
-            ('Monotype Corsiva','MTCORSVA.TTF'),
-            ('Seabird SF','seag.ttf'),
-            ('Tahoma','TAHOMA.TTF'),
-            ('VerdanaMS','VERDANA.TTF'),
-            ]:
-            for D in ('c:\WINNT','c:\Windows'):
-                fn = os.path.join(D,'Fonts',ttf)
-                if os.path.isfile(fn):
-                    try:
-                        f = ttfonts.TTFont(name, fn)
-                        pdfmetrics.registerFont(f)
-                        F.append(name)
-                    except:
-                        pass
 
     def drawit(F,w=400,h=200,fontSize=12,slack=2,gap=5):
         D = Drawing(w,h)
--- a/src/reportlab/graphics/widgetbase.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/widgetbase.py	Thu Oct 21 10:34:13 2010 +0000
@@ -28,9 +28,9 @@
 
         if self._attrMap is not None:
             for key in self.__dict__.keys():
-                if key[0] <> '_':
+                if key[0] != '_':
                     msg = "Unexpected attribute %s found in %s" % (key, self)
-                    assert self._attrMap.has_key(key), msg
+                    assert key in self._attrMap, msg
             for (attr, metavalue) in self._attrMap.items():
                 msg = "Missing attribute %s from %s" % (attr, self)
                 assert hasattr(self, attr), msg
@@ -65,7 +65,7 @@
 
         props = {}
         for name in self.__dict__.keys():
-            if name[0:1] <> '_':
+            if name[0:1] != '_':
                 component = getattr(self, name)
 
                 if recur and isValidChild(component):
@@ -137,7 +137,7 @@
 
     def _setKeywords(self,**kw):
         for k,v in kw.items():
-            if not self.__dict__.has_key(k):
+            if k not in self.__dict__:
                 setattr(self,k,v)
 
     def draw(self):
@@ -177,6 +177,14 @@
 
 _ItemWrapper={}
 
+class CloneMixin:
+    def clone(self,**kwds):
+        n = self.__class__()
+        n.__dict__.clear()
+        n.__dict__.update(self.__dict__)
+        if kwds: n.__dict__.update(kwds)
+        return n
+
 class TypedPropertyCollection(PropHolder):
     """A container with properties for objects of the same kind.
 
@@ -206,7 +214,7 @@
         self.__dict__['_children'] = {}
 
     def wKlassFactory(self,Klass):
-        class WKlass(Klass):
+        class WKlass(Klass,CloneMixin):
             def __getattr__(self,name):
                 try:
                     return self.__class__.__bases__[0].__getattr__(self,name)
@@ -214,11 +222,11 @@
                     i = self._index
                     if i:
                         c = self._parent._children
-                        if c.has_key(i) and c[i].__dict__.has_key(name):
+                        if i in c and name in c[i].__dict__:
                             return getattr(c[i],name)
                         elif len(i)==1:
                             i = i[0]
-                            if c.has_key(i) and c[i].__dict__.has_key(name):
+                            if i in c and name in c[i].__dict__:
                                 return getattr(c[i],name)
                     return getattr(self._parent,name)
         return WKlass
@@ -228,7 +236,7 @@
             return self._children[index]
         except KeyError:
             Klass = self._value.__class__
-            if _ItemWrapper.has_key(Klass):
+            if Klass in _ItemWrapper:
                 WKlass = _ItemWrapper[Klass]
             else:
                 _ItemWrapper[Klass] = WKlass = self.wKlassFactory(Klass)
@@ -249,9 +257,9 @@
             self._children[index] = child
             return child
 
-    def has_key(self,key):
+    def __contains__(self,key):
         if type(key) in (type(()),type([])): key = tuple(key)
-        return self._children.has_key(key)
+        return key in self._children
 
     def __setitem__(self, key, value):
         msg = "This collection can only hold objects of type %s" % self._value.__class__.__name__
@@ -271,7 +279,7 @@
         for idx in self._children.keys():
             childProps = self._children[idx].getProperties(recur=recur)
             for (key, value) in childProps.items():
-                if not hasattr(self,key) or getattr(self, key)<>value:
+                if not hasattr(self,key) or getattr(self, key)!=value:
                     newKey = '[%s].%s' % (idx, key)
                     props[newKey] = value
         return props
@@ -311,14 +319,14 @@
     """
 
     _attrMap = AttrMap(
-        strokeWidth = AttrMapValue(isNumber),
-        strokeLineCap = AttrMapValue(isNumber),
-        strokeLineJoin = AttrMapValue(isNumber),
-        strokeMiterLimit = AttrMapValue(None),
-        strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
-        strokeOpacity = AttrMapValue(isNumber),
-        strokeColor = AttrMapValue(isColorOrNone),
-        fillColor = AttrMapValue(isColorOrNone),
+        strokeWidth = AttrMapValue(isNumber,desc='width of the stroke line'),
+        strokeLineCap = AttrMapValue(isNumber,desc='Line cap 0=butt, 1=round & 2=square',advancedUsage=1),
+        strokeLineJoin = AttrMapValue(isNumber,desc='Line join 0=miter, 1=round & 2=bevel',advancedUsage=1),
+        strokeMiterLimit = AttrMapValue(None,desc='miter limit control miter line joins',advancedUsage=1),
+        strokeDashArray = AttrMapValue(isListOfNumbersOrNone,desc='dashing patterns e.g. (1,3)'),
+        strokeOpacity = AttrMapValue(isNumber,desc='level of transparency (alpha) accepts values between 0..1',advancedUsage=1),
+        strokeColor = AttrMapValue(isColorOrNone,desc='the color of the stroke'),
+        fillColor = AttrMapValue(isColorOrNone,desc='the filling color'),
         desc = AttrMapValue(isString),
         )
 
--- a/src/reportlab/graphics/widgets/eventcal.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/widgets/eventcal.py	Thu Oct 21 10:34:13 2010 +0000
@@ -104,7 +104,7 @@
         used = []
         for talk in talkList:
             (title, speaker, trackId, day, hours, duration) = talk
-            assert trackId <> 0, "trackId must be None or 1,2,3... zero not allowed!"
+            assert trackId != 0, "trackId must be None or 1,2,3... zero not allowed!"
             if day == self.day:
                 if (((self.startTime is None) or ((hours + duration) >= self.startTime))
                 and ((self.endTime is None) or (hours <= self.endTime))):
--- a/src/reportlab/graphics/widgets/grids.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/widgets/grids.py	Thu Oct 21 10:34:13 2010 +0000
@@ -440,9 +440,11 @@
     x0, y0 = centroid(P)
     theta = (angle/180.)*pi
     s,c=sin(theta),cos(theta)
-    def parallelAxisDist((x,y),s=s,c=c,x0=x0,y0=y0):
+    def parallelAxisDist(xy,s=s,c=c,x0=x0,y0=y0):
+        x,y = xy
         return (s*(y-y0)+c*(x-x0))
-    def orthogonalAxisDist((x,y),s=s,c=c,x0=x0,y0=y0):
+    def orthogonalAxisDist(xy,s=s,c=c,x0=x0,y0=y0):
+        x,y = xy
         return (c*(y-y0)+s*(x-x0))
     L = map(parallelAxisDist,P)
     L.sort()
--- a/src/reportlab/graphics/widgets/markers.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/widgets/markers.py	Thu Oct 21 10:34:13 2010 +0000
@@ -223,17 +223,17 @@
 
 class _isSymbol(Validator):
     def test(self,x):
-        return callable(x) or isinstance(x,Marker) or isinstance(x,Flag) \
+        return hasattr(x,'__call__') or isinstance(x,Marker) or isinstance(x,Flag) \
                 or (type(x)==ClassType and issubclass(x,Widget))
 
 isSymbol = _isSymbol()
 
 def makeMarker(name,**kw):
     if Marker._attrMap['kind'].validate(name):
-        m = apply(Marker,(),kw)
+        m = Marker(**kw)
         m.kind = name
     elif name[-5:]=='_Flag' and Flag._attrMap['kind'].validate(name[:-5]):
-        m = apply(Flag,(),kw)
+        m = Flag(**kw)
         m.kind = name[:-5]
         m.size = 10
     else:
--- a/src/reportlab/graphics/widgets/signsandsymbols.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/graphics/widgets/signsandsymbols.py	Thu Oct 21 10:34:13 2010 +0000
@@ -743,29 +743,33 @@
         self.y = 0
         self.size = 100
         self.fillColor = colors.red
+        self.strokeWidth = 0
+        self.strokeColor = None
 
     def draw(self):
         # general widget bits
         s = float(self.size)  # abbreviate as we will use this a lot
         g = shapes.Group()
 
-
-        # arrow specific bits
-        body = shapes.Rect(x=self.x, y=(self.y+(s/2))-(s/6), width=2*(s/3), height=(s/3),
-               fillColor = self.fillColor,
-               strokeColor = None,
-               strokeWidth=0)
-        g.add(body)
-
-        head = shapes.Polygon(points = [self.x+(3*(s/6)), (self.y+(s/2)),
-                                       self.x+(3*(s/6)), self.y+8*(s/10),
-                                       self.x+s, self.y+(s/2),
-                                       self.x+(3*(s/6)), self.y+2*(s/10)],
-               fillColor = self.fillColor,
-               strokeColor = None,
-               strokeWidth=0)
-        g.add(head)
-
+        x = self.x
+        y = self.y
+        s2 = s/2
+        s3 = s/3
+        s5 = s/5
+        g.add(shapes.Polygon(points = [
+                                        x,y+s3,
+                                        x,y+2*s3,
+                                        x+s2,y+2*s3,
+                                        x+s2,y+4*s5,
+                                        x+s,y+s2,
+                                        x+s2,y+s5,
+                                        x+s2,y+s3,
+                                       ],
+                fillColor = self.fillColor,
+                strokeColor = self.strokeColor,
+                strokeWidth = self.strokeWidth,
+                )
+            )
         return g
 
 class ArrowTwo(ArrowOne):
@@ -781,28 +785,36 @@
         self.y = 0
         self.size = 100
         self.fillColor = colors.blue
+        self.strokeWidth = 0
+        self.strokeColor = None
 
     def draw(self):
         # general widget bits
         s = float(self.size)  # abbreviate as we will use this a lot
         g = shapes.Group()
 
-
         # arrow specific bits
-        body = shapes.Rect(x=self.x, y=(self.y+(s/2))-(s/24), width=9*(s/10), height=(s/12),
-               fillColor = self.fillColor,
-               strokeColor = None,
-               strokeWidth=0)
-        g.add(body)
+        x = self.x
+        y = self.y
+        s2 = s/2
+        s3 = s/3
+        s5 = s/5
+        s24 = s/24
 
-        head = shapes.Polygon(points = [self.x+(2.5*(s/3)), (self.y+(s/2)),
-                                       self.x+(4*(s/6)), self.y+4*(s/6),
-                                       self.x+s, self.y+(s/2),
-                                       self.x+(4*(s/6)), self.y+2*(s/6)],
-               fillColor = self.fillColor,
-               strokeColor = None,
-               strokeWidth=0)
-        g.add(head)
+        g.add(shapes.Polygon(
+            points = [
+                    x,y+11*s24,
+                    x,y+13*s24,
+                    x+18.75*s24, y+13*s24,
+                    x+2*s3, y+2*s3,
+                    x+s, y+s2,
+                    x+2*s3, y+s3,
+                    x+18.75*s24, y+11*s24,
+                    ],
+            fillColor = self.fillColor,
+            strokeColor = self.strokeColor,
+            strokeWidth = self.strokeWidth)
+            )
 
         return g
 
--- a/src/reportlab/lib/PyFontify.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/PyFontify.py	Thu Oct 21 10:34:13 2010 +0000
@@ -124,7 +124,7 @@
         c = match[0]
         if c not in "#'\"":
             # Must have matched a keyword.
-            if start <> searchfrom:
+            if start != searchfrom:
                 # there's still a redundant char before and after it, strip!
                 match = match[1:-1]
                 start = start + 1
@@ -157,4 +157,4 @@
     f.close()
     tags = fontify(text)
     for tag, start, end, sublist in tags:
-        print tag, `text[start:end]`
+        print tag, repr(text[start:end])
--- a/src/reportlab/lib/__init__.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/__init__.py	Thu Oct 21 10:34:13 2010 +0000
@@ -4,4 +4,4 @@
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/__init__.py
 __version__=''' $Id$ '''
 import os
-RL_DEBUG = os.environ.has_key('RL_DEBUG')
\ No newline at end of file
+RL_DEBUG = 'RL_DEBUG' in os.environ
--- a/src/reportlab/lib/abag.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/abag.py	Thu Oct 21 10:34:13 2010 +0000
@@ -1,4 +1,4 @@
-#Copyright ReportLab Europe Ltd. 2000-2004
+#Copyright ReportLab Europe Ltd. 2000-2010
 #see license.txt for license details
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/abag.py
 __version__=''' $Id$ '''
--- a/src/reportlab/lib/attrmap.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/attrmap.py	Thu Oct 21 10:34:13 2010 +0000
@@ -41,14 +41,15 @@
         self.kw = kw
 
     def __call__(self):
-        return apply(self.func,self.args,self.kw)
+        return self.func(*self.args,**self.kw)
 
 class AttrMapValue:
     '''Simple multi-value holder for attribute maps'''
-    def __init__(self,validate=None,desc=None,initial=None, **kw):
+    def __init__(self,validate=None,desc=None,initial=None, advancedUsage=0, **kw):
         self.validate = validate or isAnything
         self.desc = desc
         self._initial = initial
+        self._advancedUsage = advancedUsage
         for k,v in kw.items():
             setattr(self,k,v)
 
@@ -61,6 +62,9 @@
             return 0
         raise AttributeError, name
 
+    def __repr__(self):
+        return 'AttrMapValue(%s)' % ', '.join(['%s=%r' % i for i in self.__dict__.iteritems()])
+
 class AttrMap(UserDict):
     def __init__(self,BASE=None,UNWANTED=[],**kw):
         data = {}
--- a/src/reportlab/lib/colors.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/colors.py	Thu Oct 21 10:34:13 2010 +0000
@@ -1,4 +1,4 @@
-#Copyright ReportLab Europe Ltd. 2000-2004
+#Copyright ReportLab Europe Ltd. 2000-2010
 #see license.txt for license details
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/colors.py
 __version__=''' $Id$ '''
@@ -20,36 +20,57 @@
     """This class is used to represent color.  Components red, green, blue
     are in the range 0 (dark) to 1 (full intensity)."""
 
-    def __init__(self, red=0, green=0, blue=0):
+    def __init__(self, red=0, green=0, blue=0, alpha=1):
         "Initialize with red, green, blue in range [0-1]."
-        self.red, self.green, self.blue = red,green,blue
+        self.red = red
+        self.green = green
+        self.blue = blue
+        self.alpha = alpha
 
     def __repr__(self):
-        return "Color(%s)" % fp_str(self.red, self.green, self.blue).replace(' ',',')
+        return "Color(%s)" % fp_str(*(self.red, self.green, self.blue,self.alpha)).replace(' ',',')
 
     def __hash__(self):
-        return hash( (self.red, self.green, self.blue) )
+        return hash((self.red, self.green, self.blue, self.alpha))
 
     def __cmp__(self,other):
+        '''simple comparison by component; cmyk != color ever
+        >>> cmp(Color(0,0,0),None)
+        -1
+        >>> cmp(Color(0,0,0),black)
+        0
+        >>> cmp(Color(0,0,0),CMYKColor(0,0,0,1)),Color(0,0,0).rgba()==CMYKColor(0,0,0,1).rgba()
+        (-1, True)
+        '''
+        if isinstance(other,CMYKColor) or not isinstance(other,Color): return -1
         try:
-            dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue
+            return cmp((self.red, self.green, self.blue, self.alpha),
+                    (other.red, other.green, other.blue, other.alpha))
         except:
             return -1
-        if dsum > 0: return 1
-        if dsum < 0: return -1
         return 0
 
     def rgb(self):
         "Returns a three-tuple of components"
         return (self.red, self.green, self.blue)
 
+    def rgba(self):
+        "Returns a four-tuple of components"
+        return (self.red, self.green, self.blue, self.alpha)
+
     def bitmap_rgb(self):
         return tuple(map(lambda x: int(x*255)&255, self.rgb()))
 
+    def bitmap_rgba(self):
+        return tuple(map(lambda x: int(x*255)&255, self.rgba()))
+
     def hexval(self):
         return '0x%02x%02x%02x' % self.bitmap_rgb()
 
-    _cKwds='red green blue'.split()
+    def hexvala(self):
+        return '0x%02x%02x%02x%02x' % self.bitmap_rgba()
+
+    _cKwds='red green blue alpha'.split()
     def cKwds(self):
         for k in self._cKwds:
             yield k,getattr(self,k)
@@ -61,6 +82,18 @@
         D.update(kwds)
         return self.__class__(**D)
 
+    def _lookupName(self,D={}):
+        if not D:
+            for n,v in getAllNamedColors().iteritems():
+                if not isinstance(v,CMYKColor):
+                    t = v.red,v.green,v.blue
+                    if t in D:
+                        n = n+'/'+D[t]
+                    D[t] = n
+        t = self.red,self.green,self.blue
+        return t in D and D[t] or None
+
+
 class CMYKColor(Color):
     """This represents colors using the CMYK (cyan, magenta, yellow, black)
     model commonly used in professional printing.  This is implemented
@@ -74,8 +107,9 @@
     Extra attributes may be attached to the class to support specific ink models,
     and renderers may look for these."""
 
+    _scale = 1.0
     def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
-                spotName=None, density=1, knockout=None):
+                spotName=None, density=1, knockout=None, alpha=1):
         """
         Initialize with four colors in range [0-1]. the optional
         spotName, density & knockout may be of use to specific renderers.
@@ -91,6 +125,7 @@
         self.spotName = spotName
         self.density = max(min(density,1),0)    # force into right range
         self.knockout = knockout
+        self.alpha = alpha
 
         # now work out the RGB approximation. override
         self.red, self.green, self.blue = cmyk2rgb( (cyan, magenta, yellow, black) )
@@ -104,62 +139,88 @@
             self.red, self.green, self.blue = (r,g,b)
 
     def __repr__(self):
-        return "%s(%s%s%s%s)" % (self.__class__.__name__,
+        return "%s(%s%s%s%s%s)" % (self.__class__.__name__,
             fp_str(self.cyan, self.magenta, self.yellow, self.black).replace(' ',','),
             (self.spotName and (',spotName='+repr(self.spotName)) or ''),
             (self.density!=1 and (',density='+fp_str(self.density)) or ''),
             (self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
+            (self.alpha is not None and (',alpha=%s' % self.alpha) or ''),
             )
 
+    def fader(self, n, reverse=False):
+        '''return n colors based on density fade
+        *NB* note this dosen't reach density zero'''
+        scale = self._scale
+        dd = scale/float(n)
+        L = [self.clone(density=scale - i*dd) for i in xrange(n)]
+        if reverse: L.reverse()
+        return L
+
     def __hash__(self):
-        return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName) )
+        return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName, self.alpha) )
 
     def __cmp__(self,other):
-        """Partial ordering of colors according to a notion of distance.
-
-        Comparing across the two color models is of limited use."""
-        # why the try-except?  What can go wrong?
-        if isinstance(other, CMYKColor):
-            dsum = (((( (self.cyan-other.cyan)*2 +
-                        (self.magenta-other.magenta))*2+
-                        (self.yellow-other.yellow))*2+
-                        (self.black-other.black))*2+
-                        (self.density-other.density))*2 + cmp(self.spotName or '',other.spotName or '')
-        else:  # do the RGB comparison
-            try:
-                dsum = ((self.red-other.red)*2+(self.green-other.green))*2+(self.blue-other.blue)
-            except: # or just return 'not equal' if not a color
-                return -1
-        if dsum >= 0:
-            return dsum>0
-        else:
+        """obvious way to compare colours
+        Comparing across the two color models is of limited use.
+        >>> cmp(CMYKColor(0,0,0,1),None)
+        -1
+        >>> cmp(CMYKColor(0,0,0,1),_CMYK_black)
+        0
+        >>> cmp(PCMYKColor(0,0,0,100),_CMYK_black)
+        0
+        >>> cmp(CMYKColor(0,0,0,1),Color(0,0,1)),Color(0,0,0).rgba()==CMYKColor(0,0,0,1).rgba()
+        (-1, True)
+        """
+        if not isinstance(other, CMYKColor): return -1
+        try:
+            return cmp(
+                (self.cyan, self.magenta, self.yellow, self.black, self.density, self.alpha, self.spotName),
+                (other.cyan, other.magenta, other.yellow, other.black, other.density, other.alpha, other.spotName))
+        except: # or just return 'not equal' if not a color
             return -1
+        return 0
 
     def cmyk(self):
         "Returns a tuple of four color components - syntactic sugar"
         return (self.cyan, self.magenta, self.yellow, self.black)
 
+    def cmyka(self):
+        "Returns a tuple of five color components - syntactic sugar"
+        return (self.cyan, self.magenta, self.yellow, self.black, self.alpha)
+
     def _density_str(self):
         return fp_str(self.density)
+    _cKwds='cyan magenta yellow black density alpha spotName knockout'.split()
 
-    _cKwds='cyan magenta yellow black density spotName knockout'.split()
+    def _lookupName(self,D={}):
+        if not D:
+            for n,v in getAllNamedColors().iteritems():
+                if isinstance(v,CMYKColor):
+                    t = v.cyan,v.magenta,v.yellow,v.black
+                    if t in D:
+                        n = n+'/'+D[t]
+                    D[t] = n
+        t = self.cyan,self.magenta,self.yellow,self.black
+        return t in D and D[t] or None
 
 class PCMYKColor(CMYKColor):
     '''100 based CMYKColor with density and a spotName; just like Rimas uses'''
-    def __init__(self,cyan,magenta,yellow,black,density=100,spotName=None,knockout=None):
-        CMYKColor.__init__(self,cyan/100.,magenta/100.,yellow/100.,black/100.,spotName,density/100.,knockout=knockout)
+    _scale = 100.
+    def __init__(self,cyan,magenta,yellow,black,density=100,spotName=None,knockout=None,alpha=100):
+        CMYKColor.__init__(self,cyan/100.,magenta/100.,yellow/100.,black/100.,spotName,density/100.,knockout=knockout,alpha=alpha/100.)
 
     def __repr__(self):
-        return "%s(%s%s%s%s)" % (self.__class__.__name__,
+        return "%s(%s%s%s%s%s)" % (self.__class__.__name__,
             fp_str(self.cyan*100, self.magenta*100, self.yellow*100, self.black*100).replace(' ',','),
             (self.spotName and (',spotName='+repr(self.spotName)) or ''),
             (self.density!=1 and (',density='+fp_str(self.density*100)) or ''),
             (self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
+            (self.alpha is not None and (',alpha=%s' % (fp_str(self.alpha*100))) or ''),
             )
 
     def cKwds(self):
         K=self._cKwds
-        S=K[:5]
+        S=K[:6]
         for k in self._cKwds:
             v=getattr(self,k)
             if k in S: v*=100
@@ -168,20 +229,23 @@
 
 class CMYKColorSep(CMYKColor):
     '''special case color for making separating pdfs'''
+    _scale = 1.
     def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
-                spotName=None, density=1):
-        CMYKColor.__init__(self,cyan,magenta,yellow,black,spotName,density,knockout=None)
-    _cKwds='cyan magenta yellow black density spotName'.split()
+                spotName=None, density=1,alpha=1):
+        CMYKColor.__init__(self,cyan,magenta,yellow,black,spotName,density,knockout=None,alpha=alpha)
+    _cKwds='cyan magenta yellow black density alpha spotName'.split()
 
 class PCMYKColorSep(PCMYKColor,CMYKColorSep):
     '''special case color for making separating pdfs'''
+    _scale = 100.
     def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
-                spotName=None, density=100):
-        PCMYKColor.__init__(self,cyan,magenta,yellow,black,density,spotName,knockout=None)
-    _cKwds='cyan magenta yellow black density spotName'.split()
+                spotName=None, density=100, alpha=100):
+        PCMYKColor.__init__(self,cyan,magenta,yellow,black,density,spotName,knockout=None,alpha=alpha)
+    _cKwds='cyan magenta yellow black density alpha spotName'.split()
 
-def cmyk2rgb((c,m,y,k),density=1):
+def cmyk2rgb(cmyk,density=1):
     "Convert from a CMYK color tuple to an RGB color tuple"
+    c,m,y,k = cmyk
     # From the Adobe Postscript Ref. Manual 2nd ed.
     r = 1.0 - min(1.0, c + k)
     g = 1.0 - min(1.0, m + k)
@@ -204,37 +268,40 @@
     "Transform an RGB color to a black and white equivalent."
 
     col = colorRGB
-    r, g, b = col.red, col.green, col.blue
+    r, g, b, a = col.red, col.green, col.blue, col.alpha
     n = (r + g + b) / 3.0
-    bwColorRGB = Color(n, n, n)
+    bwColorRGB = Color(n, n, n, a)
     return bwColorRGB
 
-def HexColor(val, htmlOnly=False):
+def HexColor(val, htmlOnly=False, alpha=False):
     """This function converts a hex string, or an actual integer number,
     into the corresponding color.  E.g., in "#AABBCC" or 0xAABBCC,
     AA is the red, BB is the green, and CC is the blue (00-FF).
 
+    An alpha value can also be given in the form #AABBCCDD or 0xAABBCCDD where
+    DD is the alpha value.
+
     For completeness I assume that #aabbcc or 0xaabbcc are hex numbers
     otherwise a pure integer is converted as decimal rgb.  If htmlOnly is true,
     only the #aabbcc form is allowed.
 
     >>> HexColor('#ffffff')
-    Color(1,1,1)
+    Color(1,1,1,1)
     >>> HexColor('#FFFFFF')
-    Color(1,1,1)
+    Color(1,1,1,1)
     >>> HexColor('0xffffff')
-    Color(1,1,1)
+    Color(1,1,1,1)
     >>> HexColor('16777215')
-    Color(1,1,1)
+    Color(1,1,1,1)
 
     An '0x' or '#' prefix is required for hex (as opposed to decimal):
 
     >>> HexColor('ffffff')
     Traceback (most recent call last):
-    ValueError: invalid literal for int(): ffffff
+    ValueError: invalid literal for int() with base 10: 'ffffff'
 
     >>> HexColor('#FFFFFF', htmlOnly=True)
-    Color(1,1,1)
+    Color(1,1,1,1)
     >>> HexColor('0xffffff', htmlOnly=True)
     Traceback (most recent call last):
     ValueError: not a hex string
@@ -249,13 +316,19 @@
         if val[:1] == '#':
             val = val[1:]
             b = 16
+            if len(val) == 8:
+                alpha = True
         else:
             if htmlOnly:
                 raise ValueError('not a hex string')
             if val[:2].lower() == '0x':
                 b = 16
                 val = val[2:]
+                if len(val) == 8:
+                    alpha = True
         val = int(val,b)
+    if alpha:
+        return Color((val>>24)&0xFF/255.0,((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0)
     return Color(((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0)
 
 def linearlyInterpolatedColor(c0, c1, x0, x1, x):
@@ -266,7 +339,7 @@
     """
 
     if c0.__class__ != c1.__class__:
-        raise ValueError, "Color classes must be the same for interpolation!"
+        raise ValueError("Color classes must be the same for interpolation!\nGot %r and %r'"%(c0,c1))
     if x1<x0:
         x0,x1,c0,c1 = x1,x0,c1,c0 # normalized so x1>x0
     if x<x0-1e-8 or x>x1+1e-8: # fudge factor for numerical problems
@@ -284,14 +357,46 @@
         r = c0.red+x*(c1.red - c0.red)/dx
         g = c0.green+x*(c1.green- c0.green)/dx
         b = c0.blue+x*(c1.blue - c0.blue)/dx
-        return Color(r,g,b)
+        a = c0.alpha+x*(c1.alpha - c0.alpha)/dx
+        return Color(r,g,b,alpha=a)
     elif cname == 'CMYKColor':
-        c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
-        m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
-        y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
-        k = c0.black+x*(c1.black - c0.black)/dx
-        d = c0.density+x*(c1.density - c0.density)/dx
-        return CMYKColor(c,m,y,k, density=d)
+        if cmykDistance(c0,c1)<1e-8:
+            #colors same do density and preserve spotName if any
+            assert c0.spotName == c1.spotName, "Identical cmyk, but different spotName"
+            c = c0.cyan
+            m = c0.magenta
+            y = c0.yellow
+            k = c0.black
+            d = c0.density+x*(c1.density - c0.density)/dx
+            a = c0.alpha+x*(c1.alpha - c0.alpha)/dx
+            return CMYKColor(c,m,y,k, density=d, spotName=c0.spotName, alpha=a)
+        elif cmykDistance(c0,_CMYK_white)<1e-8:
+            #special c0 is white
+            c = c1.cyan
+            m = c1.magenta
+            y = c1.yellow
+            k = c1.black
+            d = x*c1.density/dx
+            a = x*c1.alpha/dx
+            return CMYKColor(c,m,y,k, density=d, spotName=c1.spotName, alpha=a)
+        elif cmykDistance(c1,_CMYK_white)<1e-8:
+            #special c1 is white
+            c = c0.cyan
+            m = c0.magenta
+            y = c0.yellow
+            k = c0.black
+            d = x*c0.density/dx
+            d = c0.density*(1-x/dx)
+            a = c0.alpha*(1-x/dx)
+            return PCMYKColor(c,m,y,k, density=d, spotName=c0.spotName, alpha=a)
+        else:
+            c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
+            m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
+            y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
+            k = c0.black+x*(c1.black - c0.black)/dx
+            d = c0.density+x*(c1.density - c0.density)/dx
+            a = c0.alpha+x*(c1.alpha - c0.alpha)/dx
+            return CMYKColor(c,m,y,k, density=d, alpha=a)
     elif cname == 'PCMYKColor':
         if cmykDistance(c0,c1)<1e-8:
             #colors same do density and preserve spotName if any
@@ -301,7 +406,9 @@
             y = c0.yellow
             k = c0.black
             d = c0.density+x*(c1.density - c0.density)/dx
-            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
+            a = c0.alpha+x*(c1.alpha - c0.alpha)/dx
+            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100,
+                              spotName=c0.spotName, alpha=100*a)
         elif cmykDistance(c0,_CMYK_white)<1e-8:
             #special c0 is white
             c = c1.cyan
@@ -309,7 +416,9 @@
             y = c1.yellow
             k = c1.black
             d = x*c1.density/dx
-            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c1.spotName)
+            a = x*c1.alpha/dx
+            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100,
+                              spotName=c1.spotName, alpha=a*100)
         elif cmykDistance(c1,_CMYK_white)<1e-8:
             #special c1 is white
             c = c0.cyan
@@ -318,14 +427,17 @@
             k = c0.black
             d = x*c0.density/dx
             d = c0.density*(1-x/dx)
-            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
+            a = c0.alpha*(1-x/dx)
+            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100,
+                              spotName=c0.spotName, alpha=a*100)
         else:
             c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
             m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
             y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
             k = c0.black+x*(c1.black - c0.black)/dx
             d = c0.density+x*(c1.density - c0.density)/dx
-            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100)
+            a = c0.alpha+x*(c1.alpha - c0.alpha)/dx
+            return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, alpha=a*100)
     else:
         raise ValueError, "Can't interpolate: Unknown color class %s!" % cname
 
@@ -343,7 +455,7 @@
 
 # special case -- indicates no drawing should be done
 # this is a hangover from PIDDLE - suggest we ditch it since it is not used anywhere
-#transparent = Color(-1, -1, -1)
+transparent = Color(0,0,0,alpha=0)
 
 _CMYK_white=CMYKColor(0,0,0,0)
 _PCMYK_white=PCMYKColor(0,0,0,0)
@@ -578,28 +690,167 @@
     else:
         raise ValueError, "Illegal value for mode "+str(mode)
 
-def toColor(arg,default=None):
-    '''try to map an arbitrary arg to a color instance'''
-    if isinstance(arg,Color): return arg
-    if isinstance(arg,(tuple,list)):
-        assert 3<=len(arg)<=4, 'Can only convert 3 and 4 sequences to color'
-        assert 0<=min(arg) and max(arg)<=1
-        return len(arg)==3 and Color(arg[0],arg[1],arg[2]) or CMYKColor(arg[0],arg[1],arg[2],arg[3])
-    elif isinstance(arg,basestring):
-        C = getAllNamedColors()
-        s = arg.lower()
-        if C.has_key(s): return C[s]
+def hue2rgb(m1, m2, h):
+    if h<0: h += 1
+    if h>1: h -= 1
+    if h*6<1: return m1+(m2-m1)*h*6
+    if h*2<1: return m2
+    if h*3<2: return m1+(m2-m1)*(4-6*h)
+    return m1
+
+def hsl2rgb(h, s, l):