merge py33 to default
authorrobin
Wed, 26 Mar 2014 12:32:02 +0000
changeset 4067 712e1822ca31
parent 4044 f97d40349d37 (current diff)
parent 4064 2a7f47b3d12c (diff)
child 4068 600b2453d5a3
merge py33 to default
src/reportlab/graphics/charts/axes.py
src/reportlab/graphics/charts/barcharts.py
src/reportlab/graphics/renderPM.py
src/reportlab/graphics/widgets/table.py
src/reportlab/lib/abag.py
src/reportlab/lib/pdfencrypt.py
src/reportlab/lib/styles.py
src/reportlab/lib/utils.py
src/reportlab/lib/xmllib.py
src/reportlab/pdfbase/pdfdoc.py
src/reportlab/pdfgen/canvas.py
src/reportlab/pdfgen/pdfimages.py
src/reportlab/pdfgen/pycanvas.py
src/reportlab/pdfgen/textobject.py
src/reportlab/platypus/__init__.py
src/reportlab/platypus/doctemplate.py
src/reportlab/platypus/flowables.py
src/reportlab/platypus/frames.py
src/reportlab/platypus/paragraph.py
src/reportlab/platypus/paraparser.py
src/reportlab/rl_config.py
src/rl_addons/renderPM/MANIFEST.in
src/rl_addons/renderPM/_renderPM.c
src/rl_addons/renderPM/setup.cfg
src/rl_addons/renderPM/setup.py
src/rl_addons/rl_accel/MANIFEST.in
src/rl_addons/rl_accel/Makefile.pre.in
src/rl_addons/rl_accel/Setup.in
src/rl_addons/rl_accel/__BUILD.dsw
src/rl_addons/rl_accel/_rl_accel.dsp
src/rl_addons/rl_accel/pyHnj.dsp
src/rl_addons/rl_accel/sgmlop.c
src/rl_addons/rl_accel/sgmlop.dsp
tests/test_encrypt.py
tests/test_pdfbase_encodings.py
tests/test_pdfencryption.py
tests/test_pdfgen_general.py
tests/test_pdfgen_links.py
tests/test_pdfgen_pycanvas.py
tests/test_platypus_lists.py
tests/test_platypus_paragraphs.py
tests/test_platypus_tables.py
--- a/.hgeol	Mon Feb 17 10:12:24 2014 +0000
+++ b/.hgeol	Wed Mar 26 12:32:02 2014 +0000
@@ -17,7 +17,6 @@
 **.py=native
 **.pyw=native
 **.pyx=native
-**.pyd=native
 **.tac=native
 **.tap=native
 **.c=native
--- a/.hgignore	Mon Feb 17 10:12:24 2014 +0000
+++ b/.hgignore	Wed Mar 26 12:32:02 2014 +0000
@@ -35,21 +35,27 @@
 build
 __pycache__
 *.pyd
-demos/stdfonts/*.pdf
-docs/*.pdf
-docs/*/*.pdf
-src/reportlab/fonts/*.pfb
-src/reportlab/fonts/*.afm
-src/reportlab/fonts/*.ttf
-src/reportlab/graphics/barcode/*.pdf
-src/reportlab/graphics/barcode/test_cbcim.*
-tools/docco/*.pdf
-tests/test_*.pdf
-tests/test_*.svg
-tests/_i_am_actually_a_gif.jpg
-tests/_i_am_actually_a_jpeg.gif
-tests/axestest0.svg
-tests/pythonpoint.pdf
-tests/test_pdfgen_pycanvas_out.txt
-tests/test_renderSVG_output.html
-tests/test_source_chars.txt
+
+syntax: regexp
+^dist/.*
+^demos/stdfonts/.*\.pdf
+^docs/.*\.pdf
+^docs/.*/.*\.pdf
+^src/reportlab/fonts/.*\.pfb
+^src/reportlab/fonts/.*\.afm
+^src/reportlab/fonts/.*\.ttf
+^src/reportlab/graphics/barcode/.*\.pdf
+^src/reportlab/graphics/barcode/test_cbcim\..*
+^src/reportlab/graphics/pmout
+^tools/docco/.*\.pdf
+^tests/test_.*\.pdf
+^tests/test_.*\.svg
+^tests/_i_am_actually_a_gif\.jpg
+^tests/_i_am_actually_a_jpeg\.gif
+^tests/axestest0\.svg
+^tests/pythonpoint\.pdf
+^tests/test_pdfgen_pycanvas_out\.txt
+^tests/test_renderSVG_output\.html
+^tests/test_source_chars\.txt
+^tests/barcode-out
+^tests/render-out
--- a/.hgtags	Mon Feb 17 10:12:24 2014 +0000
+++ b/.hgtags	Wed Mar 26 12:32:02 2014 +0000
@@ -42,3 +42,4 @@
 daa62b6d12e9e045842a8ef1a95f7fd5f2e0100d ReportLab_1_19
 ed379b28de00522798c2a57624e128652750e308 ReportLab_0_94
 97456f60eb12ee217cbe2a57f124f8b10d6ce67f ReportLab_2_7
+6382a792db9edbfa6a9bb876aa0fb92786f03b58 ReportLab_3_0
--- a/CHANGES.txt	Mon Feb 17 10:12:24 2014 +0000
+++ b/CHANGES.txt	Wed Mar 26 12:32:02 2014 +0000
@@ -8,6 +8,77 @@
 The contributors lists are in no order and apologies to those accidentally not
 mentioned. If we missed you, please let us know!
 
+
+#################################################################################
+#################### RELEASE 3.0  14/02/2014                    #################
+#################################################################################
+
+ReportLab 3.0 now supports Python 2.7, 3.3 and higher.  
+
+There has been a substantial internal rewrite to ensure consistent use of unicode strings for
+  natural-language text, and of bytes for all file format internals.  The intent
+  is to make as few API changes as possible so that there should be little or no
+  impact on users and their applications.  Changes are too numerous but can be
+  seen on Bitbucket.
+
+### Python 3.x compatibility
+  * Python 3.x compatibility.  A single line of code should run on 2.7 and 3.3
+  * __init__.py restricts to 2.7 or >=3.3
+  * __init__.py allow the import of on optional reportlab.local_rl_mods to allow monkey patching etc.
+  * rl_config now imports rl_settings & optionally local_rl_settings
+  * ReportLab C extensions now live inside reportlab; _rl_accel is no longer required. All _rl_accel imports now pass through reportlab.lib.rl_accel
+  * xmllib is gone, alongside the paraparser stuff that caused issues in favour of HTMLParser.
+  * some obsolete C extensions (sgmlop and pyHnj) are gone
+  * Improved support for multi-threaded systems to the _rl_accel extension module.
+  * Removed reportlab/lib/ para.py & pycanvas.py.  These would better belong in third party packages, which can make use of the monkeypatching feature above.
+
+
+### New features
+  *  Add ability to output greyscale and 1-bit PIL images without conversion to RGB. (contributed by Matthew Duggan)
+  
+  * highlight annotation (contributed by Ben Echols)
+
+
+### Other
+  * numerous very minor fixes, visible through BitBucket.
+
+
+
+
+
+#################################################################################
+#################### RELEASE 2.7  04/04/2013                    #################
+#################################################################################
+
+###Charts / graphics enhancements
+  * Added SimpleTimeSeriesPlot
+  * added _computeMaxSpace
+  * added in lineStyle (for bars)
+  * improved SVG rendering
+  * Pie Chart now has an 'innerRadiusFraction' to allow doughnut-like appearance for 2d charts  (it has no effect with 3d charts).  The separate 'doughnut' chart lacks many pie chart features and should only be used if you wanted multiple nested doughnuts. 
+
+###Charts/graphics bug fixes:
+  * piecharts.py: fix Pie3d __init__ to call its superclass
+  * linecharts.py: fix swatch creation
+  * fixed y axis in the simple time series plot
+
+###PDF
+
+  * Fixes to testshapes & pdfform resetting
+  * colors.py
+  * various minor fixes
+
+###Platypus
+
+  * Defined a small bullet rather than a big circle as the default for unordered lists
+  * fixed attribute spelling bug
+  * fixed CJK + endDots
+
+###Acknowledgements
+
+Many thanks to Andrew Cutler, Dinu Gherman, Matthias Kirst and Stephan Richter for their contributions to this release.
+
+
 #################################################################################
 #################### RELEASE 2.6  27/09/2012                    #################
 #################################################################################
--- a/LICENSE.txt	Mon Feb 17 10:12:24 2014 +0000
+++ b/LICENSE.txt	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,6 @@
 #####################################################################################
 #
-#	Copyright (c) 2000-2010, ReportLab Inc.
+#	Copyright (c) 2000-2014, ReportLab Inc.
 #	All rights reserved.
 #
 #	Redistribution and use in source and binary forms, with or without modification,
--- a/README.txt	Mon Feb 17 10:12:24 2014 +0000
+++ b/README.txt	Wed Mar 26 12:32:02 2014 +0000
@@ -2,7 +2,7 @@
 README 
 =====================================
 
-(C) Copyright ReportLab Inc. 2000-2013.
+(C) Copyright ReportLab Europe Ltd. 2000-2014.
 See ``LICENSE.txt`` for license details.
 
 This is the ReportLab PDF Toolkit. It allows rapid creation 
@@ -210,5 +210,5 @@
 ``lib/normalDate.py`` originally by Jeff Bauer.
 
 Many, many contributors have helped out between 2000 and 2013.
-we keep a list in the first chapter of the User Guide; if you
+We try to keep a list in the first chapter of the User Guide; if you
 have contributed and are not listed there, please let us know.
--- a/demos/colors/colortest.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/colors/colortest.py	Wed Mar 26 12:32:02 2014 +0000
@@ -64,7 +64,7 @@
     #do all named colors
     framePage(c, 'Color Demo - RGB Space - page %d' % c.getPageNumber())
 
-    all_colors = reportlab.lib.colors.getAllNamedColors().items()
+    all_colors = list(reportlab.lib.colors.getAllNamedColors().items())
     all_colors.sort() # alpha order by name
     c.setFont('Times-Roman', 12)
     c.drawString(72,730, 'This shows all the named colors in the HTML standard.')
--- a/demos/odyssey/dodyssey.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/odyssey/dodyssey.py	Wed Mar 26 12:32:02 2014 +0000
@@ -74,7 +74,7 @@
 
 chNum = 0
 def myOnDrawCB(canv,kind,label):
-    print 'myOnDrawCB(%s)'%kind, 'Page number=', canv.getPageNumber(), 'label value=', label
+    print('myOnDrawCB(%s)'%kind, 'Page number=', canv.getPageNumber(), 'label value=', label)
 
 def chapter(txt, style=ChapterStyle):
     global chNum
@@ -113,7 +113,7 @@
     if _REDCAP:
         fs, fe = '<font color="red" size="+2">', '</font>'
         n = len(txt)
-        for i in xrange(n):
+        for i in range(n):
             if 'a'<=txt[i]<='z' or 'A'<=txt[i]<='Z':
                 txt = (txt[:i]+(fs+txt[i]+fe))+txt[i+1:]
                 break
@@ -125,7 +125,7 @@
         if _REDCAP==3 and n>20:
             n = len(txt)
             fs = '<font color="green" size="+1">'
-            for i in xrange(n-1,-1,-1):
+            for i in range(n-1,-1,-1):
                 if 'a'<=txt[i]<='z' or 'A'<=txt[i]<='Z':
                     txt = txt[:i]+((fs+txt[i]+fe)+txt[i+1:])
                     break
@@ -152,9 +152,9 @@
     i0 = text.index('Book I')
     endMarker = 'covenant of peace between the two contending parties.'
     i1 = text.index(endMarker)+len(endMarker)
-    PREAMBLE=map(str.strip,text[0:i0].split('\n'))
-    L=map(str.strip,text[i0:i1].split('\n'))
-    POSTAMBLE=map(str.strip,text[i1:].split('\n'))
+    PREAMBLE=list(map(str.strip,text[0:i0].split('\n')))
+    L=list(map(str.strip,text[i0:i1].split('\n')))
+    POSTAMBLE=list(map(str.strip,text[i1:].split('\n')))
 
     def ambleText(L):
         while L and not L[0]: L.pop(0)
@@ -184,7 +184,7 @@
             yield B,T,P
 
     t1 = time()
-    print "open(%s,'r').read() took %.4f seconds" %(fn,t1-t0)
+    print("open(%s,'r').read() took %.4f seconds" %(fn,t1-t0))
 
     E.append([spacer,2])
     E.append([fTitle,'<font color="red">%s</font>' % Title, InitialStyle])
@@ -203,24 +203,24 @@
         E.append([p,'\n'.join(T)])
 
     t3 = time()
-    print "Parsing into memory took %.4f seconds" %(t3-t1)
+    print("Parsing into memory took %.4f seconds" %(t3-t1))
     del L
     t4 = time()
-    print "Deleting list of lines took %.4f seconds" %(t4-t3)
-    for i in xrange(len(E)):
+    print("Deleting list of lines took %.4f seconds" %(t4-t3))
+    for i in range(len(E)):
         E[i][0](*E[i][1:])
     t5 = time()
-    print "Moving into platypus took %.4f seconds" %(t5-t4)
+    print("Moving into platypus took %.4f seconds" %(t5-t4))
     del E
     t6 = time()
-    print "Deleting list of actions took %.4f seconds" %(t6-t5)
+    print("Deleting list of actions took %.4f seconds" %(t6-t5))
     go()
     t7 = time()
-    print "saving to PDF took %.4f seconds" %(t7-t6)
-    print "Total run took %.4f seconds"%(t7-t0)
+    print("saving to PDF took %.4f seconds" %(t7-t6))
+    print("Total run took %.4f seconds"%(t7-t0))
 
-    import md5
-    print 'file digest: %s' % md5.md5(open('dodyssey.pdf','rb').read()).hexdigest()
+    import hashlib
+    print('file digest: %s' % hashlib.md5(open('dodyssey.pdf','rb').read()).hexdigest())
 
 def run():
     for fn in ('odyssey.full.txt','odyssey.txt'):
--- a/demos/odyssey/fodyssey.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/odyssey/fodyssey.py	Wed Mar 26 12:32:02 2014 +0000
@@ -86,9 +86,9 @@
     i0 = text.index('Book I')
     endMarker = 'covenant of peace between the two contending parties.'
     i1 = text.index(endMarker)+len(endMarker)
-    PREAMBLE=map(str.strip,text[0:i0].split('\n'))
-    L=map(str.strip,text[i0:i1].split('\n'))
-    POSTAMBLE=map(str.strip,text[i1:].split('\n'))
+    PREAMBLE=list(map(str.strip,text[0:i0].split('\n')))
+    L=list(map(str.strip,text[i0:i1].split('\n')))
+    POSTAMBLE=list(map(str.strip,text[i1:].split('\n')))
 
     def ambleText(L):
         while L and not L[0]: L.pop(0)
@@ -118,7 +118,7 @@
             yield B,T,P
 
     t1 = time()
-    print "open(%s,'r').read() took %.4f seconds" %(fn,t1-t0)
+    print("open(%s,'r').read() took %.4f seconds" %(fn,t1-t0))
 
     E.append([spacer,2])
     E.append([fTitle,'<font color=red>%s</font>' % Title, InitialStyle])
@@ -137,21 +137,21 @@
         E.append([p,'\n'.join(T)])
 
     t3 = time()
-    print "Parsing into memory took %.4f seconds" %(t3-t1)
+    print("Parsing into memory took %.4f seconds" %(t3-t1))
     del L
     t4 = time()
-    print "Deleting list of lines took %.4f seconds" %(t4-t3)
-    for i in xrange(len(E)):
+    print("Deleting list of lines took %.4f seconds" %(t4-t3))
+    for i in range(len(E)):
         E[i][0](*E[i][1:])
     t5 = time()
-    print "Moving into platypus took %.4f seconds" %(t5-t4)
+    print("Moving into platypus took %.4f seconds" %(t5-t4))
     del E
     t6 = time()
-    print "Deleting list of actions took %.4f seconds" %(t6-t5)
+    print("Deleting list of actions took %.4f seconds" %(t6-t5))
     go()
     t7 = time()
-    print "saving to PDF took %.4f seconds" %(t7-t6)
-    print "Total run took %.4f seconds"%(t7-t0)
+    print("saving to PDF took %.4f seconds" %(t7-t6))
+    print("Total run took %.4f seconds"%(t7-t0))
 
 for fn in ('odyssey.full.txt','odyssey.txt'):
     if os.path.isfile(fn):
--- a/demos/odyssey/odyssey.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/odyssey/odyssey.py	Wed Mar 26 12:32:02 2014 +0000
@@ -67,7 +67,7 @@
         accelStr = 'with _rl_accel'
     else:
         accelStr = 'without _rl_accel'
-    print 'Benchmark of %s %s %s' % (impl, verStr, accelStr)
+    print('Benchmark of %s %s %s' % (impl, verStr, accelStr))
 
     started = time.time()
     canv = canvas.Canvas('odyssey.pdf', invariant=1)
@@ -119,7 +119,7 @@
             #page
             pg = canv.getPageNumber()
             if verbose and pg % 10 == 0:
-                print 'formatted page %d' % canv.getPageNumber()
+                print('formatted page %d' % canv.getPageNumber())
 
     if tx:
         canv.drawText(tx)
@@ -127,7 +127,7 @@
         drawPageFrame(canv)
 
     if verbose:
-        print 'about to write to disk...'
+        print('about to write to disk...')
 
     canv.save()
 
@@ -136,10 +136,10 @@
     pages = canv.getPageNumber()-1
     speed =  pages / elapsed
     fileSize = os.stat('odyssey.pdf')[6] / 1024
-    print '%d pages in %0.2f seconds = %0.2f pages per second, file size %d kb' % (
-                pages, elapsed, speed, fileSize)
-    import md5
-    print 'file digest: %s' % md5.md5(open('odyssey.pdf','rb').read()).hexdigest()
+    print('%d pages in %0.2f seconds = %0.2f pages per second, file size %d kb' % (
+                pages, elapsed, speed, fileSize))
+    import hashlib
+    print('file digest: %s' % hashlib.md5(open('odyssey.pdf','rb').read()).hexdigest())
 
 if __name__=='__main__':
     quiet = ('-q' in sys.argv)
--- a/demos/rlzope/rlzope.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/rlzope/rlzope.py	Wed Mar 26 12:32:02 2014 +0000
@@ -12,19 +12,18 @@
 #
 #
 
-import string, cStringIO
 try :
     from Shared.reportlab.platypus.paragraph import Paragraph
     from Shared.reportlab.platypus.doctemplate import *
     from Shared.reportlab.lib.units import inch
     from Shared.reportlab.lib import styles
-    from Shared.reportlab.lib.utils import ImageReader
+    from Shared.reportlab.lib.utils import ImageReader, getBytesIO
 except ImportError :
     from reportlab.platypus.paragraph import Paragraph
     from reportlab.platypus.doctemplate import *
     from reportlab.lib.units import inch
     from reportlab.lib import styles
-    from reportlab.lib.utils import ImageReader
+    from reportlab.lib.utils import ImageReader, getBytesIO
 
 class MyPDFDoc :
     class MyPageTemplate(PageTemplate) :
@@ -55,7 +54,7 @@
                 return None
 
             # Convert it to PIL
-            image = ImageReader(cStringIO.StringIO(str(logo.data)))
+            image = ImageReader(getBytesIO(str(logo.data)))
             (width, height) = image.getSize()
 
             # scale it to be 0.75 inch high
@@ -84,7 +83,7 @@
 
         # we will build an in-memory document
         # instead of creating an on-disk file.
-        self.report = cStringIO.StringIO()
+        self.report = getBytesIO()
 
         # initialise a PDF document using ReportLab's platypus
         self.document = BaseDocTemplate(self.report)
@@ -123,10 +122,10 @@
 
     def escapexml(self, s) :
         """Escape some xml entities."""
-        s = string.strip(s)
-        s = string.replace(s, "&", "&amp;")
-        s = string.replace(s, "<", "&lt;")
-        return string.replace(s, ">", "&gt;")
+        s = s.strip()
+        s = s.replace("&", "&amp;")
+        s = s.replace("<", "&lt;")
+        return s.replace(">", "&gt;")
 
 def rlzope(self) :
     """A sample external method to show people how to use ReportLab from within Zope."""
@@ -158,7 +157,7 @@
         self.REQUEST.RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s' % filename)
     except:
         import traceback, sys, cgi
-        content = sys.stdout = sys.stderr = cStringIO.StringIO()
+        content = sys.stdout = sys.stderr = getBytesIO()
         self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/html')
         traceback.print_exc()
         sys.stdout = sys.__stdout__
--- a/demos/stdfonts/stdfonts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/stdfonts/stdfonts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -64,10 +64,10 @@
     if len(sys.argv)==2:
         mode = string.lower(sys.argv[1])
         if mode not in ['dec','oct','hex']:
-            print __doc__
+            print(__doc__)
 
     elif len(sys.argv) == 1:
         mode = 'dec'
         run(mode)
     else:
-        print __doc__
+        print(__doc__)
--- a/demos/tests/testdemos.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/demos/tests/testdemos.py	Wed Mar 26 12:32:02 2014 +0000
@@ -10,4 +10,4 @@
 for p in ('pythonpoint/pythonpoint.py','stdfonts/stdfonts.py','odyssey/odyssey.py', 'gadflypaper/gfe.py'):
     fn = os.path.normcase(os.path.normpath(os.path.join(os.path.dirname(pdfgen.__file__),'..','demos',p)))
     os.chdir(os.path.dirname(fn))
-    execfile(fn,_globals.copy())
+    exec(compile(open(fn).read(), fn, 'exec'),_globals.copy())
--- a/docs/genAll.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/genAll.py	Wed Mar 26 12:32:02 2014 +0000
@@ -6,9 +6,9 @@
     from reportlab.lib.testutils import testsFolder
     topDir=os.path.dirname(testsFolder)
     L = [os.path.join(topDir,f) for f in (
-            'docs/reference/genreference.py',
+            #'docs/reference/genreference.py',
             'docs/userguide/genuserguide.py',
-            'tools/docco/graphdocpy.py',
+            #'tools/docco/graphdocpy.py',
             )   
         ]
     for f in ('src/rl_addons/pyRXP/docs/PyRXP_Documentation.rml',
@@ -27,7 +27,7 @@
                 if verbose: traceback.print_exc()
         else:
             cmd = '"%s" %s %s' % (sys.executable,os.path.basename(p), not verbose and '-s' or '')
-            if verbose: print cmd
+            if verbose: print(cmd)
             os.system(cmd)
 
 """Runs the manual-building scripts"""
--- a/docs/reference/genreference.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/reference/genreference.py	Wed Mar 26 12:32:02 2014 +0000
@@ -15,11 +15,11 @@
     sys.path.insert(0,topDir)
     from tools.docco import yaml2pdf
     yaml2pdf.run('reference.yml','reportlab-reference.pdf')
-    if verbose: print 'Saved reportlab-reference.pdf'
+    if verbose: print('Saved reportlab-reference.pdf')
     if not outDir: outDir = os.path.join(topDir,'docs')
     destfn = os.path.join(outDir,'reportlab-reference.pdf')
     shutil.copyfile('reportlab-reference.pdf', destfn)
-    if verbose: print 'copied to %s' % destfn
+    if verbose: print('copied to %s' % destfn)
 
 def makeSuite():
     "standard test harness support - run self as separate process"
--- a/docs/source/conf.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/source/conf.py	Wed Mar 26 12:32:02 2014 +0000
@@ -37,8 +37,8 @@
 master_doc = 'index'
 
 # General information about the project.
-project = u'reportlab'
-copyright = u'2010, Robinson, Becker, Watters and many more'
+project = 'reportlab'
+copyright = '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
@@ -172,8 +172,8 @@
 # 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'),
+  ('index', 'reportlab.tex', 'reportlab Documentation',
+   'Robinson, Becker, Watters and many more', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
--- a/docs/userguide/ch1_intro.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/ch1_intro.py	Wed Mar 26 12:32:02 2014 +0000
@@ -183,7 +183,9 @@
 Albertas Agejevas, 
 Alex Buck, 
 Andre Reitz, 
+Andrew Cutler,
 Andrew Mercer, 
+Ben Echols,
 Benjamin Dumke,
 Benn B,
 Chad Miller, 
@@ -206,11 +208,15 @@
 Jorge Godoy,
 Keven D Smith,
 Magnus Lie Hetland,
-Marcel Tromp, Ty Sarna
+Marcel Tromp, 
 Marius Gedminas,
+Matthew Duggan,
+Matthias Kirst,
+Matthias Klose,
 Max M, 
 Michael Egorov,
 Mike Folwell,
+Mirko Dziadzka,
 Moshe Wagner,
 Nate Silva,
 Paul McNett, 
@@ -223,14 +229,17 @@
 Robert Kern,
 Ron Peleg,
 Simon King,
+Stephan Richter,
 Steve Halasz, 
 T Blatter,
 Tim Roberts,
 Tomasz Swiderski,
+Ty Sarna,
 Volker Haas,
 Yoann Roman, 
 and many more.""")
 
+
 disc("""Special thanks go to Just van Rossum for his valuable assistance with
 font technicalities.""")
 
@@ -245,10 +254,8 @@
 disc("""To avoid duplication, the installation instructions are kept in the README file
 in our distribution, which can be viewed online at ^http://bitbucket.org/rptlab/reportlab/^""")
 
-disc("""This release (2.7) of ReportLab requires Python versions 2.5, 2.6 or 2.7.   We will
-be maintaining a 2.7.x branch for essential bug fixes, but after this release the focus of
-our development will shift to dual support for Python 2.7 and 3.3+.  We advise everyone who
-can to move to Python 2.7.
+disc("""This release (3.0) of ReportLab requires Python versions 2.7, 3.3 or higher.  
+	If you need to use Python 2.5 or 2.6, please use the latest ReportLab 2.x package.
 """)
 
 
@@ -363,25 +370,31 @@
 indent0_style = styles['Indent0']
 indent1_style = styles['Indent1']
 
-heading2("Goals for the 2.x and 3.x release series")
-disc("""The main rationale for 2.0 was an incompatible change at the character level:
-to properly support Unicode input. Since then, we have tried to maintain backward
-compatibility up to this version 2.7""")
-
-disc("""One main area where we have tried to make progress from release to release is with documentation
-and installability. We now offer full support for distutils, setuptools, pip and so on. """)
+heading2("Goals for the 3.x release series")
+disc("""ReportLab 3.0 has been produced to help in the migration to Python 3.x.  Python 3.x will
+be standard in future Ubuntu releases and is gaining popularity, and a good proportion
+of major Python packages now run on Python 3.  """)
 
-disc("""
-Immediately after this release, we will be shifting focus to work on compatibility with both
-Python 2.7 and 3.3+, and starting to make larger changes to the ReportLab Toolkit to bring it
-up to date - using more modern Python libraries, supporting newer PDF features and more.  The
-next ReportLab release is likely to be numbered 3.0.  Inevitability this modernisation will need
-some changes to applications, although we do not plan to needlessly change APIs.
-""")
-
-heading2("What's New in ReportLab 2.7")
-disc("""This is a minor release to collect a small number of bug fixes since the last release
-at end of September 2012.   Detailed release notes are available at 
-$http://www.reportlab.com/software/documentation/relnotes/27/$""")
 
 
+bullet("""Python 3.x compatibility.  A single line of code should run on 2.7 and 3.3""")
+bullet(""" __init__.py restricts to 2.7 or >=3.3""")
+bullet("""__init__.py allow the import of on optional reportlab.local_rl_mods to allow monkey patching etc.""")
+bullet("""rl_config now imports rl_settings & optionally local_rl_settings""")
+bullet("""ReportLab C extensions now live inside reportlab; _rl_accel is no longer required. All _rl_accel imports now pass through reportlab.lib.rl_accel""")
+bullet("""xmllib is gone, alongside the paraparser stuff that caused issues in favour of HTMLParser.""")
+bullet("""some obsolete C extensions (sgmlop and pyHnj) are gone""")
+bullet("""Improved support for multi-threaded systems to the _rl_accel C extension module.""")
+bullet("""Removed reportlab/lib/ para.py & pycanvas.py.  These would better belong in third party packages, which can make use of the monkeypatching feature above.""")
+bullet("""Add ability to output greyscale and 1-bit PIL images without conversion to RGB. (contributed by Matthew Duggan)""")
+bullet("""highlight annotation (contributed by Ben Echols)""")
+bullet("""full compliance with pip, easy_install, wheels etc""")
+
+
+
+
+disc("""Detailed release notes are available at 
+$http://www.reportlab.com/software/documentation/relnotes/30/$""")
+
+
+
--- a/docs/userguide/ch2a_fonts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/ch2a_fonts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -49,7 +49,7 @@
 
 
 
-disc(u"""
+disc("""
 If your data is not encoded as UTF8, you will get a UnicodeDecodeError as
 soon as you feed in a non-ASCII character.  For example, this snippet below is
 attempting to read in and print a series of names, including one with a French
@@ -57,7 +57,7 @@
 what character it doesn't like:
 """)
 
-eg(u"""
+eg("""
 >>> from reportlab.pdfgen.canvas import Canvas
 >>> c = Canvas('temp.pdf')
 >>> y = 700
@@ -327,7 +327,7 @@
 registerFontFamily('Vera',normal='Vera',bold='VeraBd',italic='VeraIt',boldItalic='VeraBI')
 
 disc("""Before using the TT Fonts in Platypus we should add a mapping from the family name to the
-individual font names that describe the behaviour under the $<b>$ and $<i>$ attributes.""")
+individual font names that describe the behaviour under the $&lt;b&gt;$ and $&lt;i&gt;$ attributes.""")
 
 eg("""
 from reportlab.pdfbase.pdfmetrics import registerFontFamily
--- a/docs/userguide/ch4_platypus_concepts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/ch4_platypus_concepts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -334,7 +334,7 @@
 creates a document template suitable for creating a basic document. It comes with quite a lot
 of internal machinery, but no default page templates. The required $filename$ can be a string,
 the name of a file to  receive the created <b>PDF</b> document; alternatively it
-can be an object which has a $write$ method such as a $StringIO$ or $file$ or $socket$.
+can be an object which has a $write$ method such as a $BytesIO$ or $file$ or $socket$.
 """)
 
 disc("""
--- a/docs/userguide/ch5_paragraphs.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/ch5_paragraphs.py	Wed Mar 26 12:32:02 2014 +0000
@@ -195,7 +195,7 @@
         else:
             S[a] = "%s, %s" %(S[a],k)
 
-    K = S.keys()
+    K = list(S.keys())
     K.sort()
     D=[('Attribute','Synonyms')]
     for k in K:
@@ -371,7 +371,7 @@
 overrides the implied bullet style and ^bulletText^ specified in the  ^Paragraph^
 creation.
 """)
-parabox("""<bullet>\xe2\x80\xa2</bullet>this is a bullet point.  Spam
+parabox("""<bullet>\u2022</bullet>this is a bullet point.  Spam
 spam spam spam spam spam spam spam spam spam spam spam
 spam spam spam spam spam spam spam spam spam spam """,
         styleSheet['Bullet'],
--- a/docs/userguide/ch6_tables.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/ch6_tables.py	Wed Mar 26 12:32:02 2014 +0000
@@ -424,7 +424,7 @@
         self.caseSensitive = caseSensitive
         if maximumLineLength and text:
             text = self.stopLine(text, maximumLineLength, splitCharacters)
-        cleaner = lambda text, dedent=dedent: string.join(_dedenter(text or '',dedent),'')
+        cleaner = lambda text, dedent=dedent: ''.join(_dedenter(text or '',dedent))
         self._setup(text, style, bulletText, frags, cleaner)
 '''
 t=Preformatted(text,normalStyle,maxLineLength=60, newLineChars='> ')
@@ -446,7 +446,7 @@
    <b><font color=red>XML</font></b> tags are allowed in <i>text</i> and have the same
 
       meanings as for the <b>Paragraph</b> class.
-   As for <b>Preformatted</b>, if dedent is non zero <font color=red size=+1>dedent</font>
+   As for <b>Preformatted</b>, if dedent is non zero <font color="red" size="+1">dedent</font>
        common leading spaces will be removed from the
    front of each line.
    You can have &amp;amp; style entities as well for &amp; &lt; &gt; and &quot;.
--- a/docs/userguide/genuserguide.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/genuserguide.py	Wed Mar 26 12:32:02 2014 +0000
@@ -9,7 +9,7 @@
 
 def run(pagesize=None, verbose=0, outDir=None):
     import sys,os
-    from reportlab.lib.utils import open_and_read
+    from reportlab.lib.utils import open_and_read, asUnicode
     cwd = os.getcwd()
     docsDir=os.path.dirname(os.path.dirname(sys.argv[0]) or cwd)
     topDir=os.path.dirname(docsDir)
@@ -26,7 +26,7 @@
     registerFontFamily('Vera',normal='Vera',bold='VeraBd',italic='VeraIt',boldItalic='VeraBI')
     from tools.docco.rl_doc_utils import setStory, getStory, RLDocTemplate, defaultPageSize, H1, H2, H3, H4
     from tools.docco import rl_doc_utils
-    exec 'from tools.docco.rl_doc_utils import *' in G, G
+    exec('from tools.docco.rl_doc_utils import *', G, G)
     destfn = os.path.join(outDir,'reportlab-userguide.pdf')
     doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize)
 
@@ -50,13 +50,14 @@
         'graph_widgets',
         'app_demos',
         ):
-        exec open_and_read(f+'.py',mode='t') in G, G
+        #python source is supposed to be utf8 these days
+        exec(asUnicode(open_and_read(f+'.py')), G, G)
     del G
 
     story = getStory()
-    if verbose: print 'Built story contains %d flowables...' % len(story)
+    if verbose: print('Built story contains %d flowables...' % len(story))
     doc.multiBuild(story)
-    if verbose: print 'Saved "%s"' % destfn
+    if verbose: print('Saved "%s"' % destfn)
 
 def makeSuite():
     "standard test harness support - run self as separate process"
@@ -65,7 +66,7 @@
 
 def main():
     import sys
-    outDir = filter(lambda x: x[:9]=='--outdir=',sys.argv)
+    outDir = [x for x in sys.argv if x[:9]=='--outdir=']
     if outDir:
         outDir = outDir[0]
         sys.argv.remove(outDir)
@@ -83,10 +84,10 @@
         try:
             pagesize = (w,h) = eval(sys.argv[1])
         except:
-            print 'Expected page size in argument 1', sys.argv[1]
+            print('Expected page size in argument 1', sys.argv[1])
             raise
         if verbose:
-            print 'set page size to',sys.argv[1]
+            print('set page size to',sys.argv[1])
     else:
         pagesize = None
     if timing:
@@ -94,7 +95,7 @@
         t0 = time()
         run(pagesize, verbose,outDir)
         if verbose:
-            print 'Generation of userguide took %.2f seconds' % (time()-t0)
+            print('Generation of userguide took %.2f seconds' % (time()-t0))
     elif prof:
         import profile
         profile.run('run(pagesize,verbose,outDir)','genuserguide.stats')
--- a/docs/userguide/graph_charts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/docs/userguide/graph_charts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -20,32 +20,32 @@
 disc("Here are some of the design goals: ")
 
 disc("<i>Make simple top-level use really simple </i>")
-disc("""<para lindent=+36>It should be possible to create a simple chart with minimum lines of
+disc("""<para lindent="+36">It should be possible to create a simple chart with minimum lines of
        code, yet have it 'do the right things' with sensible automatic
        settings. The pie chart snippets above do this. If a real chart has
        many subcomponents, you still should not need to interact with them
-       unless you want to customize what they do.""")
+       unless you want to customize what they do.</para>""")
 
 disc("<i>Allow precise positioning </i>")
-disc("""<para lindent=+36>An absolute requirement in publishing and graphic design is to control
+disc("""<para lindent="+36">An absolute requirement in publishing and graphic design is to control
        the placing and style of every element. We will try to have properties
        that specify things in fixed sizes and proportions of the drawing,
        rather than having automatic resizing. Thus, the 'inner plot
        rectangle' will not magically change when you make the font size of
        the y labels bigger, even if this means your labels can spill out of
        the left edge of the chart rectangle. It is your job to preview the
-       chart and choose sizes and spaces which will work.""")
+       chart and choose sizes and spaces which will work.</para>""")
 
-disc("""<para lindent=+36>Some things do need to be automatic. For example, if you want to fit N
+disc("""<para lindent="+36">Some things do need to be automatic. For example, if you want to fit N
        bars into a 200 point space and don't know N in advance, we specify
        bar separation as a percentage of the width of a bar rather than a
        point size, and let the chart work it out. This is still deterministic
-       and controllable.""")
+       and controllable.</para>""")
 
 disc("<i>Control child elements individually or as a group</i>")
-disc("""<para lindent=+36>We use smart collection classes that let you customize a group of
+disc("""<para lindent="+36">We use smart collection classes that let you customize a group of
        things, or just one of them. For example you can do this in our
-       experimental pie chart:""")
+       experimental pie chart:</para>""")
 
 eg("""
 d = Drawing(400,200)
@@ -63,23 +63,23 @@
 d.add(pc, '')
 """)
 
-disc("""<para lindent=+36>pc.slices[3] actually lazily creates a little object which holds
+disc("""<para lindent="+36">pc.slices[3] actually lazily creates a little object which holds
        information about the slice in question; this will be used to format a
-       fourth slice at draw-time if there is one.""")
+       fourth slice at draw-time if there is one.</para>""")
 
 disc("<i>Only expose things you should change </i>")
-disc("""<para lindent=+36>It would be wrong from a statistical viewpoint to let you directly
+disc("""<para lindent="+36">It would be wrong from a statistical viewpoint to let you directly
        adjust the angle of one of the pie wedges in the above example, since
        that is determined by the data. So not everything will be exposed
        through the public properties. There may be 'back doors' to let you
        violate this when you really need to, or methods to provide advanced
-       functionality, but in general properties will be orthogonal.""")
+       functionality, but in general properties will be orthogonal.</para>""")
 
 disc("<i>Composition and component based </i>")
-disc("""<para lindent=+36>Charts are built out of reusable child widgets. A Legend is an
+disc("""<para lindent="+36">Charts are built out of reusable child widgets. A Legend is an
        easy-to-grasp example. If you need a specialized type of legend (e.g.
        circular colour swatches), you should subclass the standard Legend
-       widget. Then you could either do something like...""")
+       widget. Then you could either do something like...</para>""")
 
 eg("""
 c = MyChartWithLegend()
@@ -88,26 +88,26 @@
 c.data = [10,20,30]   #   and then configure as usual...
 """)
 
-disc("""<para lindent=+36>...or create/modify your own chart or drawing class which creates one
+disc("""<para lindent="+36">...or create/modify your own chart or drawing class which creates one
        of these by default. This is also very relevant for time series
-       charts, where there can be many styles of x axis.""")
+       charts, where there can be many styles of x axis.</para>""")
 
-disc("""<para lindent=+36>Top level chart classes will create a number of such components, and
+disc("""<para lindent="+36">Top level chart classes will create a number of such components, and
        then either call methods or set private properties to tell them their
        height and position - all the stuff which should be done for you and
        which you cannot customise. We are working on modelling what the
        components should be and will publish their APIs here as a consensus
-       emerges.""")
+       emerges.</para>""")
 
 disc("<i>Multiples </i>")
-disc("""<para lindent=+36>A corollary of the component approach is that you can create diagrams
+disc("""<para lindent="+36">A corollary of the component approach is that you can create diagrams
        with multiple charts, or custom data graphics. Our favourite example
        of what we are aiming for is the weather report in our gallery
        contributed by a user; we'd like to make it easy to create such
        drawings, hook the building blocks up to their legends, and feed that
-       data in a consistent way.""")
-disc("""<para lindent=+36>(If you want to see the image, it is available on our website
-<font color="blue"><a href="https://www.reportlab.com/media/imadj/data/RLIMG_e5e5cb85cc0a555f5433528ac38c5884.PDF">here</a></font>)""")
+       data in a consistent way.</para>""")
+disc("""<para lindent="+36">(If you want to see the image, it is available on our website
+<font color="blue"><a href="https://www.reportlab.com/media/imadj/data/RLIMG_e5e5cb85cc0a555f5433528ac38c5884.PDF">here</a></font>)</para>""")
 
 
 ##heading3("Key Concepts and Components")
@@ -900,7 +900,7 @@
 lc.width = 300
 lc.data = data
 lc.joinedLines = 1
-catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
+catNames = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
 lc.categoryAxis.categoryNames = catNames
 lc.categoryAxis.labels.boxAnchor = 'n'
 lc.valueAxis.valueMin = 0
@@ -927,7 +927,7 @@
 lc.width = 300
 lc.data = data
 lc.joinedLines = 1
-catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
+catNames = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
 lc.categoryAxis.categoryNames = catNames
 lc.categoryAxis.labels.boxAnchor = 'n'
 lc.valueAxis.valueMin = 0
--- a/setup.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/setup.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,7 +1,27 @@
 #Copyright ReportLab Europe Ltd. 2000-2012
 #see license.txt for license details
 __version__=''' $Id$ '''
-import os, sys, glob, ConfigParser, shutil
+import os, sys, glob, shutil
+def specialOption(n):
+    v = False
+    while n in sys.argv:
+        v = True
+        sys.argv.remove(n)
+    return v
+
+#defaults for these options may be configured in local-setup.cfg
+#[OPTIONS]
+#no-download-t1-files=yes
+#ignore-system-libart=yes
+# if used on command line the config values are not used
+dlt1 = not specialOption('--no-download-t1-files')
+isla = specialOption('--ignore-system-libart')
+
+try:
+    import configparser
+except ImportError:
+    import ConfigParser as configparser
+isPy3 = sys.version_info[0]==3
 platform = sys.platform
 pjoin = os.path.join
 abspath = os.path.abspath
@@ -19,11 +39,14 @@
 try:
     os.chdir(pkgDir)
 except:
-    print '!!!!! warning could not change directory to %r' % pkgDir
-daily=os.environ.get('RL_EXE_DAILY','')
+    print('!!!!! warning could not change directory to %r' % pkgDir)
+daily=int(os.environ.get('RL_EXE_DAILY','0'))
 
 import distutils
-from distutils.core import setup, Extension
+try:
+    from setuptools import setup, Extension
+except ImportError:
+    from distutils.core import setup, Extension
 from distutils import sysconfig
 
 # from Zope - App.Common.package_home
@@ -50,8 +73,9 @@
     try:
         for l in open(pjoin(FN+'.py'),'r').readlines():
             if l.startswith('Version'):
-                exec l.strip()
-                return Version
+                D = {}
+                exec(l.strip(),D)
+                return D['Version']
     except:
         pass
 
@@ -70,8 +94,8 @@
 class config:
     def __init__(self):
         try:
-            self.parser = ConfigParser.RawConfigParser()
-            self.parser.read(pjoin(pkgDir,'setup.cfg'))
+            self.parser = configparser.RawConfigParser()
+            self.parser.read([pjoin(pkgDir,'setup.cfg'),pjoin(pkgDir,'local-setup.cfg')])
         except:
             self.parser = None
 
@@ -82,6 +106,13 @@
             return default
 config = config()
 
+if dlt1:
+    #not set on command line so try for config value
+    dlt1 = not config('OPTIONS','no-download-t1-files','0').lower() in ('1','true','yes')
+if not isla:
+    #not set on command line so try for config value
+    isla = config('OPTIONS','ignore-system-libart','0').lower() in ('1','true','yes')
+
 #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:
@@ -139,9 +170,6 @@
         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'''
     _ = []
@@ -162,9 +190,9 @@
         if isfile(fn):
             _.append(x)
     if _:
-        _ = filter(_rl_dir_info(cn),_)
+        _ = list(filter(_rl_dir_info(cn),_))
         if len(_):
-            _.sort(_cmp_rl_ccode_dirs)
+            _.sort(key=_rl_dir_info)
             return abspath(_[0])
     return None
 
@@ -181,7 +209,7 @@
 
 INFOLINES=[]
 def infoline(t):
-    print t
+    print(t)
     INFOLINES.append(t)
 
 reportlab_files= [
@@ -216,16 +244,23 @@
         ]
 
 def get_fonts(PACKAGE_DIR, reportlab_files):
-    import sys, os, os.path, urllib2, zipfile, StringIO
+    import sys, os, os.path, zipfile, io
+    if isPy3:
+        import urllib.request as ureq
+    else:
+        import urllib2 as ureq
     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
+    elif not dlt1:
+        infoline('not downloading T1 font curve files')
+        return
     try:
         infoline("Downloading standard T1 font curves")
 
-        remotehandle = urllib2.urlopen("http://www.reportlab.com/ftp/pfbfer-20070710.zip")
-        zipdata = StringIO.StringIO(remotehandle.read())
+        remotehandle = ureq.urlopen("http://www.reportlab.com/ftp/pfbfer-20070710.zip")
+        zipdata = io.BytesIO(remotehandle.read())
         remotehandle.close()
         archive = zipfile.ZipFile(zipdata)
         dst = pjoin(rl_dir, 'fonts')
@@ -255,10 +290,23 @@
         os.system("%s runAll.py" % sys.executable)
         return
 
+    debug_compile_args = []
+    debug_link_args = []
+    debug_macros = []
+    debug = int(os.environ.get('RL_DEBUG','0'))
+    if debug:
+        if sys.platform == 'win32':
+            debug_compile_args=['/Zi']
+            debug_link_args=['/DEBUG']
+            if debug>1:
+                debug_macros.extend([('RL_DEBUG',debug), ('ROBIN_DEBUG',None)])
+
+
     SPECIAL_PACKAGE_DATA = {}
     RL_ACCEL = _find_rl_ccode('rl_accel','_rl_accel.c')
     LIBRARIES=[]
     EXT_MODULES = []
+
     if not RL_ACCEL:
         infoline( '***************************************************')
         infoline( '*No rl_accel code found, you can obtain it at     *')
@@ -266,36 +314,36 @@
         infoline( '***************************************************')
     else:
         infoline( '################################################')
-        infoline( '#Attempting install of _rl_accel, sgmlop & pyHnj')
+        infoline( '#Attempting install of _rl_accel & 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',
+                    Extension( 'reportlab.lib._rl_accel',
                                 [pjoin(RL_ACCEL,'_rl_accel.c')],
                                 include_dirs=[],
-                            define_macros=[],
+                            define_macros=[]+debug_macros,
                             library_dirs=[],
                             libraries=[], # libraries to link against
+                            extra_compile_args=debug_compile_args,
+                            extra_link_args=debug_link_args,
                             ),
-                    Extension( 'sgmlop',
-                            [pjoin(RL_ACCEL,'sgmlop.c')],
+                        ]
+        if not isPy3:
+            EXT_MODULES += [
+                    Extension( 'reportlab.lib.pyHnj',
+                            [pjoin(RL_ACCEL,'pyHnjmodule.c'),
+                            pjoin(RL_ACCEL,'hyphen.c'),
+                            pjoin(RL_ACCEL,'hnjalloc.c')],
                             include_dirs=[],
-                            define_macros=[],
+                            define_macros=[]+debug_macros,
                             library_dirs=[],
                             libraries=[], # libraries to link against
+                            extra_compile_args=debug_compile_args,
+                            extra_link_args=debug_link_args,
                             ),
-                    Extension( 'pyHnj',
-                            [pjoin(RL_ACCEL,'pyHnjmodule.c'),
-                             pjoin(RL_ACCEL,'hyphen.c'),
-                             pjoin(RL_ACCEL,'hnjalloc.c')],
-                            include_dirs=[],
-                            define_macros=[],
-                            library_dirs=[],
-                            libraries=[], # libraries to link against
-                            ),
-                    ]
+                        ]
     RENDERPM = _find_rl_ccode('renderPM','_renderPM.c')
     if not RENDERPM:
         infoline( '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
@@ -306,21 +354,30 @@
         infoline( '################################################')
         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():
-            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'),
+
+        #check for an installed libart
+        if isla:
+            LIBART_INC=None
+        else:
+            LIBART_INC = list(sorted(glob.glob('/usr/include/libart-*/libart_lgpl/libart-features.h')))
+        if LIBART_INC:
+            def installed_libart_version(fn):
+                for l in open(fn, 'r').readlines():
+                    if l.startswith('#define LIBART_VERSION'):
+                        v = l[:-1].split(' ')[-1]
+                        return v
+                return '"0.0.0"'
+            LIBART_INC = LIBART_INC[-1]
+            LIBART_VERSION = installed_libart_version(LIBART_INC)
+            LIBART_INC = os.path.dirname(LIBART_INC)
+            LIBART_SOURCES=[]
+            LIBART_LIB = ['art_lgpl_2']
+            infoline('will use installed libart %s' % LIBART_VERSION.replace('"',''))
+        else:
+            LIBART_DIR = LIBART_INC = pjoin(RENDERPM,'libart_lgpl')
+            LIBART_LIB = []
+            LIBART_SOURCES=[
                     pjoin(LIBART_DIR,'art_vpath_bpath.c'),
                     pjoin(LIBART_DIR,'art_rgb_pixbuf_affine.c'),
                     pjoin(LIBART_DIR,'art_rgb_svp.c'),
@@ -339,11 +396,25 @@
                     pjoin(LIBART_DIR,'art_svp_intersect.c'),
                     pjoin(LIBART_DIR,'art_svp_render_aa.c'),
                     pjoin(LIBART_DIR,'art_misc.c'),
+                    ]
+            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()
+            infoline('will use package libart %s' % LIBART_VERSION.replace('"',''))
+
+        SOURCES=[pjoin(RENDERPM,'_renderPM.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'),
-                    ]
+                    ]+LIBART_SOURCES
 
         if platform=='win32':
             FT_LIB=os.environ.get('FT_LIB','')
@@ -358,8 +429,8 @@
                 FT_LIB_DIR = [dirname(FT_LIB)]
                 FT_INC_DIR = [FT_INC_DIR or pjoin(dirname(FT_LIB_DIR[0]),'include')]
                 FT_LIB_PATH = FT_LIB
-                FT_LIB = [os.path.splitext(os.path.basename(FT_LIB))[0]]                
-                if isdir(FT_INC_DIR[0]):                   
+                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])
@@ -367,8 +438,12 @@
             else:
                 FT_LIB=FT_LIB_DIR=FT_INC_DIR=FT_MACROS=[]
         else:
-            FT_LIB_DIR=config('FREETYPE','lib')
-            FT_INC_DIR=config('FREETYPE','inc')
+            if os.path.isdir('/usr/include/freetype2'):
+                FT_LIB_DIR = []
+                FT_INC_DIR = ['/usr/include/freetype2']
+            else:
+                FT_LIB_DIR=config('FREETYPE','lib')
+                FT_INC_DIR=config('FREETYPE','inc')
             I,L=inc_lib_dirs()
             ftv = None
             for d in I:
@@ -398,24 +473,24 @@
             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',
+
+        EXT_MODULES +=  [Extension( 'reportlab.graphics._renderPM',
                                         SOURCES,
-                                        include_dirs=[RENDERPM,LIBART_DIR,GT1_DIR]+FT_INC_DIR,
-                                        define_macros=FT_MACROS+[('LIBART_COMPILATION',None)]+MACROS+[('LIBART_VERSION',LIBART_VERSION)],
+                                        include_dirs=[RENDERPM,LIBART_INC,GT1_DIR]+FT_INC_DIR,
+                                        define_macros=FT_MACROS+[('LIBART_COMPILATION',None)]+debug_macros+[('LIBART_VERSION',LIBART_VERSION)],
                                         library_dirs=[]+FT_LIB_DIR,
 
                                         # libraries to link against
-                                        libraries=FT_LIB,
-                                        #extra_objects=['gt1.lib','libart.lib',],
-                                        #extra_compile_args=['/Z7'],
-                                        extra_link_args=[]
+                                        libraries=FT_LIB+LIBART_LIB,
+                                        extra_compile_args=debug_compile_args,
+                                        extra_link_args=debug_link_args,
                                         ),
                             ]
         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():
+    for fn,dst in SPECIAL_PACKAGE_DATA.items():
         shutil.copyfile(fn,pjoin(PACKAGE_DIR['reportlab'],dst))
         reportlab_files.append(dst)
     get_fonts(PACKAGE_DIR, reportlab_files)
@@ -442,22 +517,20 @@
                     '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},
             ext_modules =   EXT_MODULES,
+            
+            #this probably only works for setuptools, but distutils seems to ignore it
+            install_requires=['pillow >= 2.3.0'],
             )
-        print
-        print '########## SUMMARY INFO #########'
-        print '\n'.join(INFOLINES)
+        print()
+        print('########## SUMMARY INFO #########')
+        print('\n'.join(INFOLINES))
     finally:
-        for dst in SPECIAL_PACKAGE_DATA.itervalues():
+        for dst in SPECIAL_PACKAGE_DATA.values():
             os.remove(pjoin(PACKAGE_DIR['reportlab'],dst))
             reportlab_files.remove(dst)
 
-
 if __name__=='__main__':
     main()
--- a/src/reportlab/__init__.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/__init__.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,43 +3,46 @@
 #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.7"
+Version = "3.1.1"
 
-import sys
+import sys, os, imp
 
-if sys.version_info[0:2] < (2, 7):
-    warning = """The trunk of reportlab currently requires Python 2.7 or higher.
+if sys.version_info[0:2]!=(2, 7) and sys.version_info<(3, 3):
+    raise ImportError("""reportlab requires Python 2.7+ or 3.3+; 3.0-3.2 are not supported.""")
 
-    This is being done to let us move forwards with 2.7/3.x compatibility
-    with the minimum of baggage.
-    
-    ReportLab 2.7 was the last packaged version to suppo0rt Python 2.5 and 2.6.
+#define these early in reportlab's life
+isPy3 = sys.version_info[0]==3
+if isPy3:
+    def cmp(a,b):
+        return -1 if a<b else (1 if a>b else 0)
 
-    Python 2.3 users may still use ReportLab 2.4 or any other bugfixes
-    derived from it, and Python 2.4 users may use ReportLab 2.5.  
-    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.
-    
-    Our current plan is to remove Python 2.5 compatibility on our next release,
-    allowing us to use the 2to3 tool and work on Python 3.0 compatibility.
-    If you have a choice, Python 2.7.x is best long term version to use.
-    """
-    raise ImportError("reportlab needs Python 2.5 or higher", warning)
+    import builtins
+    builtins.cmp = cmp
+    builtins.xrange = range
+    del cmp, builtins
+else:
+    from future_builtins import ascii
+    import __builtin__
+    __builtin__.ascii = ascii
+    del ascii, __builtin__
 
-def getStory(context):
-    "This is a helper for our old autogenerated documentation system"
-    if context.target == 'UserGuide':
-        # parse some local file
-        import os
-        myDir = os.path.split(__file__)[0]
-        import yaml
-        return yaml.parseFile(myDir + os.sep + 'mydocs.yaml')
-    else:
-        # this signals that it should revert to default processing
-        return None
+#try to use dynamic modifications from
+#reportlab.local_rl_mods.py
+#reportlab_mods.py or ~/.reportlab_mods
+try:
+    import reportlab.local_rl_mods
+except ImportError:
+    pass
 
+def _fake_import(fn,name):
+    if os.path.isfile(fn):
+        with open(fn,'rb') as f:
+            imp.load_source('reportlab_mods',fn,f)
 
-def getMonitor():
-    import reportlab.monitor
-    mon = reportlab.monitor.ReportLabToolkitMonitor()
-    return mon
+try:
+    import reportlab_mods   #application specific modifications can be anywhere on python path
+except ImportError:
+    try:
+        _fake_import(os.path.expanduser(os.path.join('~','.reportlab_mods')),'reportlab_mods')
+    except ImportError:
+        pass
--- a/src/reportlab/graphics/barcode/__init__.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/__init__.py	Wed Mar 26 12:32:02 2014 +0000
@@ -36,14 +36,14 @@
 def getCodes():
     """Returns a dict mapping code names to widgets"""
 
-    from widgets import BarcodeI2of5, BarcodeCode128, BarcodeStandard93,\
+    from reportlab.graphics.barcode.widgets import BarcodeI2of5, BarcodeCode128, BarcodeStandard93,\
                         BarcodeExtended93, BarcodeStandard39, BarcodeExtended39,\
                         BarcodeMSI, BarcodeCodabar, BarcodeCode11, BarcodeFIM,\
                         BarcodePOSTNET, BarcodeUSPS_4State
 
     #newer codes will typically get their own module
-    from eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget, UPCA
-    from qr import QrCodeWidget
+    from reportlab.graphics.barcode.eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget, UPCA
+    from reportlab.graphics.barcode.qr import QrCodeWidget
 
 
     #the module exports a dictionary of names to widgets, to make it easy for
@@ -87,7 +87,7 @@
     height = options.pop('height',None)
     isoScale = options.pop('isoScale',0)
     kw = {}
-    for k,v in options.iteritems():
+    for k,v in options.items():
         if k.startswith('_') or k in bcc._attrMap: kw[k] = v
     bc = bcc(**kw)
 
--- a/src/reportlab/graphics/barcode/code128.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/code128.py	Wed Mar 26 12:32:02 2014 +0000
@@ -31,7 +31,8 @@
 #
 
 from reportlab.lib.units import inch
-from common import MultiWidthBarcode
+from reportlab.lib.utils import asNative
+from reportlab.graphics.barcode.common import MultiWidthBarcode
 from string import digits
 
 _patterns = {
@@ -166,7 +167,7 @@
     'START_B' : (startb, setb, seta),
     'START_C' : (startc, setc, None),
 }
-tos = setmap.keys()
+tos = list(setmap.keys())
 
 class Code128(MultiWidthBarcode):
     """
@@ -219,11 +220,9 @@
     quiet = 1
     barHeight = None
     def __init__(self, value='', **args):
-
-        if type(value) is type(1):
-            value = str(value)
+        value = str(value) if isinstance(value,int) else asNative(value)
             
-        for (k, v) in args.items():
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
--- a/src/reportlab/graphics/barcode/code39.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/code39.py	Wed Mar 26 12:32:02 2014 +0000
@@ -31,8 +31,9 @@
 #
 
 from reportlab.lib.units import inch
-from common import Barcode
-import string
+from reportlab.lib.utils import asNative
+from reportlab.graphics.barcode.common import Barcode
+from string import digits as string_digits
 
 _patterns = {
     '0':    ("bsbSBsBsb", 0),       '1': ("BsbSbsbsB", 1),
@@ -59,7 +60,8 @@
     '+':    ("bSbsbSbSb", 41),      '%': ("bsbSbSbSb", 42)
     }
 
-_stdchrs = string.digits + string.uppercase + "-. $/+%"
+from reportlab.lib.utils import ascii_uppercase, ascii_lowercase
+_stdchrs = string_digits + ascii_uppercase + "-. $/+%"
 
 _extended = {
     '\0':   "%U",    '\01':  "$A",    '\02':  "$B",    '\03':  "$C",
@@ -88,7 +90,7 @@
     }
 
 
-_extchrs = _stdchrs + string.lowercase + \
+_extchrs = _stdchrs + ascii_lowercase + \
     "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" + \
     "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" + \
     "*!'#&\"(),:;<=>?@[\\]^_`{|}~\177"
@@ -112,7 +114,8 @@
     bearers = 0.0
     stop = 1
     def __init__(self, value = "", **args):
-        for k, v in args.iteritems():
+        value = asNative(value)
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -138,7 +141,7 @@
     """
     Options that may be passed to constructor:
 
-        value (int, or numeric string. required.):
+        value (int, or numeric string required.):
             The value to encode.
 
         barWidth (float, default .0075):
@@ -192,8 +195,8 @@
         vval = [].append
         self.valid = 1
         for c in self.value:
-            if c in string.lowercase:
-                c = string.upper(c)
+            if c in ascii_lowercase:
+                c = c.upper()
             if c not in _stdchrs:
                 self.valid = 0
                 continue
--- a/src/reportlab/graphics/barcode/code93.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/code93.py	Wed Mar 26 12:32:02 2014 +0000
@@ -31,8 +31,8 @@
 #
 
 from reportlab.lib.units import inch
-from common import MultiWidthBarcode
-import string
+from reportlab.lib.utils import asNative
+from reportlab.graphics.barcode.common import MultiWidthBarcode
 
 _patterns = {
   '0' : ('AcAaAb', 0),  '1' : ('AaAbAc', 1),  '2' : ('AaAcAb', 2),
@@ -85,7 +85,7 @@
 }
 
 def _encode93(str):
-    s = map(None, str)
+    s = list(str)
     s.reverse()
 
     # compute 'C' checksum
@@ -108,7 +108,7 @@
 
     s.reverse()
 
-    return string.join(s, '')
+    return ''.join(s)
 
 class _Code93Base(MultiWidthBarcode):
     barWidth = inch * 0.0075
@@ -120,9 +120,9 @@
     def __init__(self, value='', **args):
 
         if type(value) is type(1):
-            value = str(value)
+            value = asNative(value)
             
-        for (k, v) in args.iteritems():
+        for (k, v) in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -184,9 +184,7 @@
     def validate(self):
         vval = ""
         self.valid = 1
-        for c in self.value:
-            if c in string.lowercase:
-                c = string.upper(c)
+        for c in self.value.upper():
             if c not in _patterns:
                 self.valid = 0
                 continue
--- a/src/reportlab/graphics/barcode/common.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/common.py	Wed Mar 26 12:32:02 2014 +0000
@@ -32,7 +32,8 @@
 
 from reportlab.platypus.flowables import Flowable
 from reportlab.lib.units import inch
-import string
+from reportlab.lib.utils import ascii_uppercase, ascii_lowercase
+from string import digits as string_digits
 
 class Barcode(Flowable):
     """Abstract Base for barcodes. Includes implementations of
@@ -46,7 +47,7 @@
         return self.encoded
 
     def __init__(self, value='',**kwd):
-        self.value = value
+        self.value = str(value)
 
         self._setKeywords(**kwd)
         if not hasattr(self, 'gap'):
@@ -60,7 +61,7 @@
         self.computeSize()
 
     def _setKeywords(self,**kwd):
-        for (k, v) in kwd.iteritems():
+        for (k, v) in kwd.items():
             setattr(self, k, v)
 
     def validate(self):
@@ -187,9 +188,9 @@
 
         for c in self.decomposed:
             oc = ord(c)
-            if c in string.lowercase:
+            if c in ascii_lowercase:
                 w = w + barWidth * (oc - oa)
-            elif c in string.uppercase:
+            elif c in ascii_uppercase:
                 w = w + barWidth * (oc - oA)
 
         if self.barHeight is None:
@@ -210,9 +211,9 @@
 
         for c in self.decomposed:
             oc = ord(c)
-            if c in string.lowercase:
+            if c in ascii_lowercase:
                 left = left + (oc - oa) * barWidth
-            elif c in string.uppercase:
+            elif c in ascii_uppercase:
                 w = (oc - oA) * barWidth
                 self.rect(left, 0, w, self.barHeight)
                 left += w
@@ -225,7 +226,7 @@
 
     Options that may be passed to constructor:
 
-        value (int, or numeric string. required.):
+        value (int, or numeric string required.):
             The value to encode.
 
         barWidth (float, default .0075):
@@ -308,7 +309,7 @@
         if type(value) == type(1):
             value = str(value)
 
-        for (k, v) in args.items():
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -323,8 +324,8 @@
     def validate(self):
         vval = ""
         self.valid = 1
-        for c in string.strip(self.value):
-            if c not in string.digits:
+        for c in self.value.strip():
+            if c not in string_digits:
                 self.valid = 0
                 continue
             vval = vval + c
@@ -342,7 +343,7 @@
             c += 1
 
         if cs:
-            c = 3*sum([int(s[i]) for i in xrange(0,c,2)])+sum([int(s[i]) for i in xrange(1,c,2)])
+            c = 3*sum([int(s[i]) for i in range(0,c,2)])+sum([int(s[i]) for i in range(1,c,2)])
             s += str((10 - c) % 10)
 
         self.encoded = s
@@ -351,7 +352,7 @@
         dval = self.stop and [self.patterns['start']] or []
         a = dval.append
 
-        for i in xrange(0, len(self.encoded), 2):
+        for i in range(0, len(self.encoded), 2):
             b = self.patterns['B' + self.encoded[i]]
             s = self.patterns['S' + self.encoded[i+1]]
 
@@ -368,7 +369,7 @@
 
     Options that may be passed to constructor:
 
-        value (int, or numeric string. required.):
+        value (int, or numeric string required.):
             The value to encode.
 
         barWidth (float, default .0075):
@@ -433,7 +434,7 @@
         if type(value) == type(1):
             value = str(value)
 
-        for (k, v) in args.items():
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -448,8 +449,8 @@
     def validate(self):
         vval = ""
         self.valid = 1
-        for c in string.strip(self.value):
-            if c not in string.digits:
+        for c in self.value.strip():
+            if c not in string_digits:
                 self.valid = 0
                 continue
             vval = vval + c
@@ -489,7 +490,7 @@
 
     Options that may be passed to constructor:
 
-        value (string. required.):
+        value (string required.):
             The value to encode.
 
         barWidth (float, default .0065):
@@ -553,7 +554,7 @@
         '+' : 15,   'A' : 16,   'B' : 17,   'C' : 18,   'D' : 19
         }
 
-    chars = string.digits + "-$:/.+"
+    chars = string_digits + "-$:/.+"
 
     stop = 1
     barHeight = None
@@ -569,7 +570,7 @@
         if type(value) == type(1):
             value = str(value)
 
-        for (k, v) in args.items():
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -584,7 +585,7 @@
     def validate(self):
         vval = ""
         self.valid = 1
-        s = string.strip(self.value)
+        s = self.value.strip()
         for i in range(0, len(s)):
             c = s[i]
             if c not in self.chars:
@@ -621,7 +622,7 @@
     Code 11 is an almost-numeric barcode. It encodes the digits 0-9 plus
     dash ("-"). 11 characters total, hence the name.
 
-        value (int or string. required.):
+        value (int or string required.):
             The value to encode.
 
         barWidth (float, default .0075):
@@ -689,7 +690,7 @@
         if type(value) == type(1):
             value = str(value)
 
-        for (k, v) in args.items():
+        for k, v in args.items():
             setattr(self, k, v)
 
         if self.quiet:
@@ -704,8 +705,8 @@
     def validate(self):
         vval = ""
         self.valid = 1
-        s = string.strip(self.value)
-        for i in xrange(0, len(s)):
+        s = self.value.strip()
+        for i in range(0, len(s)):
             c = s[i]
             if c not in self.chars:
                 self.Valid = 0
--- a/src/reportlab/graphics/barcode/eanbc.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/eanbc.py	Wed Mar 26 12:32:02 2014 +0000
@@ -8,6 +8,7 @@
 from reportlab.lib.attrmap import *
 from reportlab.graphics.charts.areas import PlotArea
 from reportlab.lib.units import mm
+from reportlab.lib.utils import asNative
 
 #work out a list of manufacturer codes....
 _eanNumberSystems = [
@@ -186,8 +187,9 @@
     x = 0
     y = 0
     def __init__(self,value='123456789012',**kw):
+        value = str(value) if isinstance(value,int) else asNative(value)
         self.value=max(self._digits-len(value),0)*'0'+value[:self._digits]
-        for k, v in kw.iteritems():
+        for k, v in kw.items():
             setattr(self, k, v)
 
     width = property(lambda self: self.barWidth*(self._nbars-18+self._calc_quiet(self.lquiet)+self._calc_quiet(self.rquiet)))
--- a/src/reportlab/graphics/barcode/fourstate.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/fourstate.py	Wed Mar 26 12:32:02 2014 +0000
@@ -31,7 +31,7 @@
 #
 
 from reportlab.lib.units import inch
-from common import Barcode
+from reportlab.graphics.barcode.common import Barcode
 import string
 
 # . 3 T Tracker
--- a/src/reportlab/graphics/barcode/lto.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/lto.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,11 +1,11 @@
 # (c) 2008 Jerome Alet - <alet@librelogiciel.com>
 # Licensing terms : ReportLab's license.
 
-import string
-
-from code39 import Standard39
+from reportlab.graphics.barcode.code39 import Standard39
 from reportlab.lib import colors
 from reportlab.lib.units import cm
+from string import digits as string_digits
+from reportlab.lib.utils import ascii_uppercase
 
 class BaseLTOLabel(Standard39) :
     """
@@ -44,16 +44,16 @@
         self.height = max(availheight, self.CODEBARHEIGHT)
         self.border = border
         if (len(subtype) != 1) \
-            or (subtype not in string.ascii_uppercase + string.digits) :
-            raise ValueError, "Invalid subtype '%s'" % subtype
+            or (subtype not in ascii_uppercase + string_digits) :
+            raise ValueError("Invalid subtype '%s'" % subtype)
         if ((not number) and (len(prefix) > 6)) \
            or not prefix.isalnum() :
-            raise ValueError, "Invalid prefix '%s'" % prefix
+            raise ValueError("Invalid prefix '%s'" % prefix)
         label = "%sL%s" % ((prefix + str(number or 0).zfill(6 - len(prefix)))[:6],
                            subtype)
         if len(label) != 8 :
-            raise ValueError, "Invalid set of parameters (%s, %s, %s)" \
-                                % (prefix, number, subtype)
+            raise ValueError("Invalid set of parameters (%s, %s, %s)" \
+                                % (prefix, number, subtype))
         self.label = label
         Standard39.__init__(self,
                             label,
--- a/src/reportlab/graphics/barcode/qr.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/qr.py	Wed Mar 26 12:32:02 2014 +0000
@@ -27,6 +27,11 @@
 from reportlab.lib.attrmap import *
 from reportlab.graphics.charts.areas import PlotArea
 from reportlab.lib.units import mm
+from reportlab.lib.utils import asNative
+try:
+    from itertools import zip_longest
+except:
+    from itertools import izip_longest as zip_longest
 
 class isLevel(Validator):
     def test(self,x):
@@ -62,8 +67,9 @@
     barLevel = 'L'
 
     def __init__(self,value='Hello World',**kw):
+        value = str(value) if isinstance(value,int) else asNative(value)
         self.value=value
-        for k, v in kw.iteritems():
+        for k, v in kw.items():
             setattr(self, k, v)
 
     def wrap(self,aW,aH):
@@ -123,7 +129,7 @@
             if not re.search('^[%s]+$' % self.valid, data):
                 raise ValueError
         else:
-            self.valid = ''.join(chr(c) for c in range(256))
+            self.valid = ''.join(chr(c) for c in xrange(256))
         self.data = data
 
     def getLength(self):
@@ -133,10 +139,10 @@
         return self.data
 
     def write(self, buffer):
-        for g in map(None, *[iter(self.data)] * self.group):
+        for g in zip_longest(*[iter(self.data)] * self.group):
             bits = 0
             n = 0
-            for i in range(self.group):
+            for i in xrange(self.group):
                 if g[i] is not None:
                     n *= len(self.valid)
                     n += self.valid.index(g[i])
@@ -156,7 +162,7 @@
     mode = QRMode.MODE_ALPHA_NUM
 
 class QR8bitByte(QR):
-    valid = None #''.join(chr(c) for c in range(256))
+    valid = None #''.join(chr(c) for c in xrange(256))
     bits = (8,)
     group = 1
     mode = QRMode.MODE_8BIT_BYTE
@@ -944,7 +950,7 @@
         rsBlock = QRRSBlock.getRsBlockTable(typeNumber, errorCorrectLevel);
         if rsBlock == None:
             raise Exception("bad rs block @ typeNumber:" + typeNumber + "/errorCorrectLevel:" + errorCorrectLevel)
-        length = len(rsBlock) / 3
+        length = len(rsBlock) // 3
         list = []
         for i in xrange(length):
             count = rsBlock[i * 3 + 0]
--- a/src/reportlab/graphics/barcode/test.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/test.py	Wed Mar 26 12:32:02 2014 +0000
@@ -79,7 +79,7 @@
     f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1)
     f.addFromList(story, c)
     c.save()
-    print 'saved out.pdf'
+    print('saved out.pdf')
 
 def fullTest(fileName="test_full.pdf"):
     """Creates large-ish test document with a variety of parameters"""
@@ -167,8 +167,8 @@
     if height: options['height'] = height[0]
     if isoScale: options['isoScale'] = isoScale[0]
     scales = [x[8:].split(',') for x in sys.argv if x.startswith('--scale=')]
-    scales = map(float,scales and flatten(scales) or [1])
-    scales = map(float,scales and flatten(scales) or [1])
+    scales = list(map(float,scales and flatten(scales) or [1]))
+    scales = list(map(float,scales and flatten(scales) or [1]))
     for scale in scales:
         story.append(PageBreak())
         story.append(Paragraph('Scale = %.1f'%scale, styleH2))
@@ -185,7 +185,7 @@
             story.append(KeepTogether(s))
 
     SimpleDocTemplate(fileName).build(story)
-    print 'created', fileName
+    print('created', fileName)
 
 if __name__=='__main__':
     run()
--- a/src/reportlab/graphics/barcode/usps.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/usps.py	Wed Mar 26 12:32:02 2014 +0000
@@ -31,8 +31,9 @@
 #
 
 from reportlab.lib.units import inch
-from common import Barcode
-import string
+from reportlab.graphics.barcode.common import Barcode
+from string import digits as string_digits, whitespace as string_whitespace
+from reportlab.lib.utils import asNative
 
 _fim_patterns = {
     'A' : "||  |  ||",
@@ -97,7 +98,8 @@
     lquiet = inch * (15.0/32.0)
     quiet = 0
     def __init__(self, value='', **args):
-        for (k, v) in args.items():
+        value = str(value) if isinstance(value,int) else asNative(value)
+        for k, v in args.items():
             setattr(self, k, v)
 
         Barcode.__init__(self, value)
@@ -106,15 +108,15 @@
         self.valid = 1
         self.validated = ''
         for c in self.value:
-            if c in string.whitespace:
+            if c in string_whitespace:
                 continue
             elif c in "abcdABCD":
-                self.validated = self.validated + string.upper(c)
+                self.validated = self.validated + c.upper()
             else:
                 self.valid = 0
 
         if len(self.validated) != 1:
-            raise ValueError, "Input must be exactly one character"
+            raise ValueError("Input must be exactly one character")
 
         return self.validated
 
@@ -161,8 +163,8 @@
     barWidth = inch * 0.018
     spaceWidth = inch * 0.0275
     def __init__(self, value='', **args):
-
-        for (k, v) in args.items():
+        value = str(value) if isinstance(value,int) else asNative(value)
+        for k, v in args.items():
             setattr(self, k, v)
 
         Barcode.__init__(self, value)
@@ -172,9 +174,9 @@
         self.valid = 1
         count = 0
         for c in self.value:
-            if c in (string.whitespace + '-'):
+            if c in (string_whitespace + '-'):
                 pass
-            elif c in string.digits:
+            elif c in string_digits:
                 count = count + 1
                 if count == 6:
                     self.validated = self.validated + '-'
@@ -191,13 +193,13 @@
         self.encoded = "S"
         check = 0
         for c in self.validated:
-            if c in string.digits:
+            if c in string_digits:
                 self.encoded = self.encoded + c
-                check = check + string.atoi(c)
+                check = check + int(c)
             elif c == '-':
                 pass
             else:
-                raise ValueError, "Invalid character in input"
+                raise ValueError("Invalid character in input")
         check = (10 - check) % 10
         self.encoded = self.encoded + repr(check) + 'S'
         return self.encoded
--- a/src/reportlab/graphics/barcode/usps4s.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/usps4s.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,7 +4,8 @@
 __all__ = ('USPS_4State',)
 
 from reportlab.lib.colors import black
-from common import Barcode
+from reportlab.graphics.barcode.common import Barcode
+from reportlab.lib.utils import asNative
 
 class USPS_4State(Barcode):
     ''' USPS 4-State OneView (TM) barcode. All info from USPS-B-3200A
@@ -34,8 +35,9 @@
 
     def __init__(self,value='01234567094987654321',routing='',**kwd):
         self._init()
-        self.tracking = value
-        self.routing = routing
+        value = str(value) if isinstance(value,int) else asNative(value)
+        self._tracking = value
+        self._routing = routing
         self._setKeywords(**kwd)
 
     def _init(self):
@@ -125,7 +127,7 @@
                 svalue = tracking[j:i]
                 try:
                     if len(svalue)!=nd: raise ValueError
-                    for j in xrange(nd):
+                    for j in range(nd):
                         value *= 10
                         value += int(svalue[j])
                 except:
@@ -150,7 +152,7 @@
             A, D = divmod(A,1365)
             A, C = divmod(A,1365)
             A, B = divmod(A,1365)
-            assert 0<=A<=658, 'improper value %s passed to _2codewords A-->%s' % (hex(long(value)),A)
+            assert 0<=A<=658, 'improper value %s passed to _2codewords A-->%s' % (hex(int(value)),A)
             self._fcs = _crc11(value)
             if self._fcs&1024: A += 659
             J *= 2
@@ -181,7 +183,7 @@
             aC = C.append
             table1 = self.table1
             table2 = self.table2
-            for i in xrange(10):
+            for i in range(10):
                 cw = codewords[i]
                 if cw<=1286:
                     c = table1[cw]
@@ -313,12 +315,12 @@
     >>> print ' '.join(hex(_crc11(USPS_4State('01234567094987654321',x).binary)) for x in ('','01234','012345678','01234567891'))
     0x51 0x65 0x606 0x751
     '''
-    bytes = hex(long(value))[2:-1]
+    bytes = hex(int(value))[2:-1]
     bytes = '0'*(26-len(bytes))+bytes
     gp = 0x0F35
     fcs = 0x07FF
     data = int(bytes[:2],16)<<5
-    for b in xrange(2,8):
+    for b in range(2,8):
         if (fcs ^ data)&0x400:
             fcs = (fcs<<1)^gp
         else:
@@ -326,9 +328,9 @@
         fcs &= 0x7ff
         data <<= 1
 
-    for x in xrange(2,2*13,2):
+    for x in range(2,2*13,2):
         data = int(bytes[x:x+2],16)<<3
-        for b in xrange(8):
+        for b in range(8):
             if (fcs ^ data)&0x400:
                 fcs = (fcs<<1)^gp
             else:
@@ -343,7 +345,7 @@
     31 7936 7808 47
     '''
     r = 0
-    for x in xrange(13):
+    for x in range(13):
         r <<= 1
         r |= i & 1
         i >>= 1
@@ -358,9 +360,9 @@
     T = lenT*[None]
     l = 0
     u = lenT-1
-    for c in xrange(8192):
+    for c in range(8192):
         bc = 0
-        for b in xrange(13):
+        for b in range(13):
             bc += (c&(1<<b))!=0
         if bc!=N: continue
         r = _ru13(c)
--- a/src/reportlab/graphics/barcode/widgets.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/barcode/widgets.py	Wed Mar 26 12:32:02 2014 +0000
@@ -19,6 +19,7 @@
 from reportlab.lib.validators import isInt, isNumber, isColor, isString, isColorOrNone, OneOf, isBoolean, EitherOr, isNumberOrNone
 from reportlab.lib.attrmap import AttrMap, AttrMapValue
 from reportlab.lib.colors import black
+from reportlab.lib.utils import rl_exec
 from reportlab.graphics.shapes import Line, Rect, Group, NotImplementedError, String
 from reportlab.graphics.charts.areas import PlotArea
 
@@ -58,16 +59,13 @@
     barStrokeColor = None
     barStrokeWidth = 0
     _BCC = None
-    def __init__(self,BCC=None,_value='',**kw):
-        self._BCC = BCC
-        class Combiner(self.__class__,BCC):
-            __name__ = self.__class__.__name__
-        self.__class__ = Combiner
+    def __init__(self,_value='',**kw):
         PlotArea.__init__(self)
-        del self.width, self.height
+        del self.__dict__['width']
+        del self.__dict__['height']
         self.x = self.y = 0
         kw.setdefault('value',_value)
-        BCC.__init__(self,**kw)
+        self._BCC.__init__(self,**kw)
 
     def rect(self,x,y,w,h,**kw):
         self._Gadd(Rect(self.x+x,self.y+y,w,h,
@@ -87,24 +85,38 @@
         self._Gadd(String(self.x+x,self.y+y,text,fontName=fontName,fontSize=fontSize,
                             textAnchor=anchor,fillColor=self.textColor))
 
-class BarcodeI2of5(_BarcodeWidget):
+def _BCW(doc,codeName,attrMap,mod,value,**kwds):
+    """factory for Barcode Widgets"""
+    _pre_init = kwds.pop('_pre_init','')
+    _methods = kwds.pop('_methods','')
+    name = 'Barcode'+codeName
+    ns = vars().copy()
+    code = 'from %s import %s' % (mod,codeName)
+    rl_exec(code,ns)
+    ns['_BarcodeWidget'] = _BarcodeWidget
+    code = '''class %(name)s(_BarcodeWidget,%(codeName)s):
+\t_BCC = %(codeName)s
+\tcodeName = %(codeName)r
+\tdef __init__(self,**kw):%(_pre_init)s
+\t\t_BarcodeWidget.__init__(self,%(value)r,**kw)%(_methods)s''' % ns
+    rl_exec(code,ns)
+    Klass = ns[name]
+    if attrMap: Klass._attrMap = attrMap
+    if doc: Klass.__doc__ = doc
+    for k, v in kwds.items():
+        setattr(Klass,k,v)
+    return Klass
+
+BarcodeI2of5 = _BCW(
     """Interleaved 2 of 5 is used in distribution and warehouse industries.
 
     It encodes an even-numbered sequence of numeric digits. There is an optional
     module 10 check digit; if including this, the total length must be odd so that
     it becomes even after including the check digit.  Otherwise the length must be
     even. Since the check digit is optional, our library does not check it.
-    """
-
-    _tests = [
-        '12',
-        '1234',
-        '123456',
-        '12345678',
-        '1234567890'
-        ]
-    codeName = "I2of5"
-    _attrMap = AttrMap(BASE=_BarcodeWidget,
+    """,
+    "I2of5",
+    AttrMap(BASE=_BarcodeWidget,
         barWidth = AttrMapValue(isNumber,'''(float, default .0075):
             X-Dimension, or width of the smallest element
             Minumum is .0075 inch (7.5 mils).'''),
@@ -139,190 +151,157 @@
         fontSize = AttrMapValue(isNumber, desc='human readable font size'),
         humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
         stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
-        )
-    _bcTransMap = {}
-
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.common import I2of5
-        _BarcodeWidget.__init__(self,I2of5,1234,**kw)
-
-class BarcodeCode128(BarcodeI2of5):
-    """Code 128 encodes any number of characters in the ASCII character set.
-    """
+        ),
+    'reportlab.graphics.barcode.common',
+    1234,
     _tests = [
-        'ReportLab Rocks!'
-        ]
-    codeName = "Code128"
-    _attrMap = AttrMap(BASE=BarcodeI2of5,UNWANTED=('bearers','checksum','ratio','checksum','stop'))
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.code128 import Code128
-        _BarcodeWidget.__init__(self,Code128,"AB-12345678",**kw)
+        '12',
+        '1234',
+        '123456',
+        '12345678',
+        '1234567890'
+        ],
+    )
 
-class BarcodeStandard93(BarcodeCode128):
-    """This is a compressed form of Code 39"""
-    codeName = "Standard93"
-    _attrMap = AttrMap(BASE=BarcodeCode128,
-        stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
-        )
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.code93 import Standard93
-        _BarcodeWidget.__init__(self,Standard93,"CODE 93",**kw)
+BarcodeCode128 = _BCW("""Code 128 encodes any number of characters in the ASCII character set.""",
+                "Code128",
+                AttrMap(BASE=BarcodeI2of5,UNWANTED=('bearers','checksum','ratio','checksum','stop')),
+                'reportlab.graphics.barcode.code128',
+                "AB-12345678",
+                _tests = ['ReportLab Rocks!', 'PFWZF'],
+                )
 
-class BarcodeExtended93(BarcodeStandard93):
-    """This is a compressed form of Code 39, allowing the full ASCII charset"""
-    codeName = "Extended93"
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.code93 import Extended93
-        _BarcodeWidget.__init__(self,Extended93,"L@@K! Code 93 ;-)",**kw)
+BarcodeStandard93=_BCW("""This is a compressed form of Code 39""",
+                        "Standard93",
+                        AttrMap(BASE=BarcodeCode128,
+                                stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
+                                ),
+                        'reportlab.graphics.barcode.code93',
+                        "CODE 93",
+                        )
 
-class BarcodeStandard39(BarcodeI2of5):
-    """Code39 is widely used in non-retail, especially US defence and health.
-    Allowed characters are 0-9, A-Z (caps only), space, and -.$/+%*.
-    """
-
-    codeName = "Standard39"
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.code39 import Standard39
-        _BarcodeWidget.__init__(self,Standard39,"A012345B%R",**kw)
+BarcodeExtended93=_BCW("""This is a compressed form of Code 39, allowing the full ASCII charset""",
+                        "Extended93",
+                        AttrMap(BASE=BarcodeCode128,
+                                stop = AttrMapValue(isBoolean, desc='if we use start/stop symbols (default 1)'),
+                                ),
+                        'reportlab.graphics.barcode.code93',
+                        "L@@K! Code 93 ;-)",
+                        )
 
-class BarcodeExtended39(BarcodeI2of5):
-    """Extended 39 encodes the full ASCII character set by encoding
-    characters as pairs of Code 39 characters; $, /, % and + are used as
-    shift characters."""
-
-    codeName = "Extended39"
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.code39 import Extended39
-        _BarcodeWidget.__init__(self,Extended39,"A012345B}",**kw)
+BarcodeStandard39=_BCW("""Code39 is widely used in non-retail, especially US defence and health.
+                        Allowed characters are 0-9, A-Z (caps only), space, and -.$/+%*.""",
+                        "Standard39",
+                        AttrMap(BASE=BarcodeI2of5),
+                        'reportlab.graphics.barcode.code39',
+                        "A012345B%R",
+                        )
 
-class BarcodeMSI(BarcodeI2of5):
-    """MSI is used for inventory control in retail applications.
+BarcodeExtended39=_BCW("""Extended 39 encodes the full ASCII character set by encoding
+                        characters as pairs of Code 39 characters; $, /, % and + are used as
+                        shift characters.""",
+                        "Extended39",
+                        AttrMap(BASE=BarcodeI2of5),
+                        'reportlab.graphics.barcode.code39',
+                        "A012345B}",
+                        )
+
+BarcodeMSI=_BCW("""MSI is used for inventory control in retail applications.
 
-    There are several methods for calculating check digits so we
-    do not implement one.
-    """
-    codeName = "MSI"
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.common import MSI
-        _BarcodeWidget.__init__(self,MSI,1234,**kw)
+                There are several methods for calculating check digits so we
+                do not implement one.
+                """,
+                "MSI",
+                AttrMap(BASE=BarcodeI2of5),
+                'reportlab.graphics.barcode.common',
+                1234,
+                )
 
-class BarcodeCodabar(BarcodeI2of5):
-    """Used in blood banks, photo labs and FedEx labels.
-    Encodes 0-9, -$:/.+, and four start/stop characters A-D.
-    """
-    codeName = "Codabar"
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.common import Codabar
-        _BarcodeWidget.__init__(self,Codabar,"A012345B",**kw)
+BarcodeCodabar=_BCW("""Used in blood banks, photo labs and FedEx labels.
+                    Encodes 0-9, -$:/.+, and four start/stop characters A-D.""",
+                    "Codabar",
+                    AttrMap(BASE=BarcodeI2of5),
+                    'reportlab.graphics.barcode.common',
+                    "A012345B",
+                    )
 
-class BarcodeCode11(BarcodeI2of5):
-    """Used mostly for labelling telecommunications equipment.
-    It encodes numeric digits.
-    """
-    codeName = "Code11"
-    _attrMap = AttrMap(BASE=BarcodeI2of5,
-        checksum = AttrMapValue(isInt,'''(integer, default 2):
-            Whether to compute and include the check digit(s).
-            (0 none, 1 1-digit, 2 2-digit, -1 auto, default -1):
-            How many checksum digits to include. -1 ("auto") means
-            1 if the number of digits is 10 or less, else 2.'''),
-            )
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.common import Code11
-        _BarcodeWidget.__init__(self,Code11,"01234545634563",**kw)
+BarcodeCode11=_BCW("""Used mostly for labelling telecommunications equipment.
+                    It encodes numeric digits.""",
+                    'Code11',
+                    AttrMap(BASE=BarcodeI2of5,
+                        checksum = AttrMapValue(isInt,'''(integer, default 2):
+                            Whether to compute and include the check digit(s).
+                            (0 none, 1 1-digit, 2 2-digit, -1 auto, default -1):
+                            How many checksum digits to include. -1 ("auto") means
+                            1 if the number of digits is 10 or less, else 2.'''),
+                            ),
+                    'reportlab.graphics.barcode.common',
+                    "01234545634563",
+                    )
 
-class BarcodeFIM(_BarcodeWidget):
-    """
-    FIM was developed as part of the POSTNET barcoding system. FIM (Face Identification Marking) is used by the cancelling machines to sort mail according to whether or not they have bar code and their postage requirements. There are four types of FIM called FIM A, FIM B, FIM C, and FIM D.
+BarcodeFIM=_BCW("""
+                FIM was developed as part of the POSTNET barcoding system.
+                FIM (Face Identification Marking) is used by the cancelling machines
+                to sort mail according to whether or not they have bar code
+                and their postage requirements. There are four types of FIM
+                called FIM A, FIM B, FIM C, and FIM D.
 
-    The four FIM types have the following meanings:
-        FIM A- Postage required pre-barcoded
-        FIM B - Postage pre-paid, no bar code exists
-        FIM C- Postage prepaid prebarcoded
-        FIM D- Postage required, no bar code exists
-    """
-    codeName = "FIM"
-    _attrMap = AttrMap(BASE=_BarcodeWidget,
-        barWidth = AttrMapValue(isNumber,'''(float, default 1/32in): the bar width.'''),
-        spaceWidth = AttrMapValue(isNumber,'''(float or None, default 1/16in):
-            width of intercharacter gap. None means "use barWidth".'''),
-        barHeight = AttrMapValue(isNumber,'''(float, default 5/8in): The bar height.'''),
-        quiet = AttrMapValue(isBoolean,'''(bool, default 0):
-            Whether to include quiet zones in the symbol.'''),
-        lquiet = AttrMapValue(isNumber,'''(float, default: 15/32in):
-            Quiet zone size to left of code, if quiet is true.'''),
-        rquiet = AttrMapValue(isNumber,'''(float, default 1/4in):
-            Quiet zone size to right left of code, if quiet is true.'''),
-        fontName = AttrMapValue(isString, desc='human readable font'),
-        fontSize = AttrMapValue(isNumber, desc='human readable font size'),
-        humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
-        )
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.usps import FIM
-        _BarcodeWidget.__init__(self,FIM,"A",**kw)
+                The four FIM types have the following meanings:
+                    FIM A- Postage required pre-barcoded
+                    FIM B - Postage pre-paid, no bar code exists
+                    FIM C- Postage prepaid prebarcoded
+                    FIM D- Postage required, no bar code exists""",
+                "FIM",
+                AttrMap(BASE=_BarcodeWidget,
+                    barWidth = AttrMapValue(isNumber,'''(float, default 1/32in): the bar width.'''),
+                    spaceWidth = AttrMapValue(isNumber,'''(float or None, default 1/16in):
+                        width of intercharacter gap. None means "use barWidth".'''),
+                    barHeight = AttrMapValue(isNumber,'''(float, default 5/8in): The bar height.'''),
+                    quiet = AttrMapValue(isBoolean,'''(bool, default 0):
+                        Whether to include quiet zones in the symbol.'''),
+                    lquiet = AttrMapValue(isNumber,'''(float, default: 15/32in):
+                        Quiet zone size to left of code, if quiet is true.'''),
+                    rquiet = AttrMapValue(isNumber,'''(float, default 1/4in):
+                        Quiet zone size to right left of code, if quiet is true.'''),
+                    fontName = AttrMapValue(isString, desc='human readable font'),
+                    fontSize = AttrMapValue(isNumber, desc='human readable font size'),
+                    humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
+                    ),
+                'reportlab.graphics.barcode.usps',
+                "A",
+                )
 
-class BarcodePOSTNET(_BarcodeWidget):
-    codeName = "POSTNET"
-    _attrMap = AttrMap(BASE=_BarcodeWidget,
-        barWidth = AttrMapValue(isNumber,'''(float, default 0.018*in): the bar width.'''),
-        spaceWidth = AttrMapValue(isNumber,'''(float or None, default 0.0275in): width of intercharacter gap.'''),
-        shortHeight = AttrMapValue(isNumber,'''(float, default 0.05in): The short bar height.'''),
-        barHeight = AttrMapValue(isNumber,'''(float, default 0.125in): The full bar height.'''),
-        fontName = AttrMapValue(isString, desc='human readable font'),
-        fontSize = AttrMapValue(isNumber, desc='human readable font size'),
-        humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
-        )
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.usps import POSTNET
-        _BarcodeWidget.__init__(self,POSTNET,"78247-1043",**kw)
+BarcodePOSTNET=_BCW('',
+                    "POSTNET",
+                    AttrMap(BASE=_BarcodeWidget,
+                            barWidth = AttrMapValue(isNumber,'''(float, default 0.018*in): the bar width.'''),
+                            spaceWidth = AttrMapValue(isNumber,'''(float or None, default 0.0275in): width of intercharacter gap.'''),
+                            shortHeight = AttrMapValue(isNumber,'''(float, default 0.05in): The short bar height.'''),
+                            barHeight = AttrMapValue(isNumber,'''(float, default 0.125in): The full bar height.'''),
+                            fontName = AttrMapValue(isString, desc='human readable font'),
+                            fontSize = AttrMapValue(isNumber, desc='human readable font size'),
+                            humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
+                            ),
+                    'reportlab.graphics.barcode.usps',
+                    "78247-1043",
+                    )
 
-class BarcodeUSPS_4State(_BarcodeWidget):
-    codeName = "USPS_4State"
-    _attrMap = AttrMap(BASE=_BarcodeWidget,
-        widthSize = AttrMapValue(isNumber,'''(float, default 1): the bar width size adjustment between 0 and 1.'''),
-        heightSize = AttrMapValue(isNumber,'''(float, default 1): the bar height size adjustment between 0 and 1.'''),
-        fontName = AttrMapValue(isString, desc='human readable font'),
-        fontSize = AttrMapValue(isNumber, desc='human readable font size'),
-        tracking = AttrMapValue(isString, desc='tracking data'),
-        routing = AttrMapValue(isString, desc='routing data'),
-        humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
-        )
-    def __init__(self,**kw):
-        from reportlab.graphics.barcode.usps4s import USPS_4State
-        kw.setdefault('routing','01234567891')
-        _BarcodeWidget.__init__(self,USPS_4State,'01234567094987654321',**kw)
-
-    def annotate(self,x,y,text,fontName,fontSize,anchor='middle'):
-        _BarcodeWidget.annotate(self,x,y,text,fontName,fontSize,anchor='start')
+BarcodeUSPS_4State=_BCW('',
+                        "USPS_4State",
+                        AttrMap(BASE=_BarcodeWidget,
+                            widthSize = AttrMapValue(isNumber,'''(float, default 1): the bar width size adjustment between 0 and 1.'''),
+                            heightSize = AttrMapValue(isNumber,'''(float, default 1): the bar height size adjustment between 0 and 1.'''),
+                            fontName = AttrMapValue(isString, desc='human readable font'),
+                            fontSize = AttrMapValue(isNumber, desc='human readable font size'),
+                            tracking = AttrMapValue(isString, desc='tracking data'),
+                            routing = AttrMapValue(isString, desc='routing data'),
+                            humanReadable = AttrMapValue(isBoolean, desc='if human readable'),
+                            ),
+                        'reportlab.graphics.barcode.usps4s',
+                        '01234567094987654321',
+                        _pre_init="\n\t\tkw.setdefault('routing','01234567891')\n",
+                        _methods = "\n\tdef annotate(self,x,y,text,fontName,fontSize,anchor='middle'):\n\t\t_BarcodeWidget.annotate(self,x,y,text,fontName,fontSize,anchor='start')\n"
+                        )
 
 if __name__=='__main__':
-    import os, sys, glob
-    from reportlab.graphics.shapes import Drawing
-    os.chdir(os.path.dirname(sys.argv[0]))
-    if not os.path.isdir('out'):
-        os.mkdir('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,
-            BarcodeCode128,
-            BarcodeStandard93,
-            BarcodeExtended93,
-            BarcodeStandard39,
-            BarcodeExtended39,
-            BarcodeMSI,
-            BarcodeCodabar,
-            BarcodeCode11,
-            BarcodeFIM,
-            BarcodePOSTNET,
-            BarcodeUSPS_4State,
-            ):
-        name = C.__name__
-        i = C()
-        D = Drawing(100,50)
-        D.add(i)
-        D.save(formats=['gif','pict'],outDir='out',fnRoot=name)
-        a('<h2>%s</h2><img src="%s.gif"><br>' % (name, name))
-    a('</body></html>')
-    open(os.path.join('out','index.html'),'w').write('\n'.join(html))
+    raise ValueError('widgets.py has no script function')
--- a/src/reportlab/graphics/charts/axes.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/axes.py	Wed Mar 26 12:32:02 2014 +0000
@@ -33,7 +33,7 @@
 
 from reportlab.lib.validators import    isNumber, isNumberOrNone, isListOfStringsOrNone, isListOfNumbers, \
                                         isListOfNumbersOrNone, isColorOrNone, OneOf, isBoolean, SequenceOf, \
-                                        isString, EitherOr, Validator, _SequenceTypes, NoneOr, isInstanceOf, \
+                                        isString, EitherOr, Validator, NoneOr, isInstanceOf, \
                                         isNormalDate
 from reportlab.lib.attrmap import *
 from reportlab.lib import normalDate
@@ -43,20 +43,24 @@
 from reportlab.graphics.charts.utils import nextRoundNumber
 from reportlab.graphics.widgets.grids import ShadedRect
 from reportlab.lib.colors import Color
+from reportlab.lib.utils import isSeq
 import copy
-
+try:
+    reduce  # Python 2.x
+except NameError:
+    from functools import reduce
 
 # Helpers.
 def _findMinMaxValue(V, x, default, func, special=None):
-    if isinstance(V[0][0],_SequenceTypes):
+    if isSeq(V[0][0]):
         if special:
             f=lambda T,x=x,special=special,func=func: special(T,x,func)
         else:
             f=lambda T,x=x: T[x]
-        V=map(lambda e,f=f: map(f,e),V)
-    V = filter(len,map(lambda x: filter(lambda x: x is not None,x),V))
+        V=list(map(lambda e,f=f: list(map(f,e)),V))
+    V = list(filter(len,[[x for x in x if x is not None] for x in V]))
     if len(V)==0: return default
-    return func(map(func,V))
+    return func(list(map(func,V)))
 
 def _findMin(V, x, default,special=None):
     '''find minimum over V[i][x]'''
@@ -140,7 +144,7 @@
                     d = hi
                 axis._get_line_pos = lambda x: d
             L = func(v)
-            for k,v in kwds.iteritems():
+            for k,v in kwds.items():
                 setattr(L,k,v)
         finally:
             axis._get_line_pos = oaglp
@@ -185,7 +189,7 @@
         G = Group()
         ncolors = len(colors)
         v0 = axis._get_line_pos(tv[0])
-        for i in xrange(1,len(tv)):
+        for i in range(1,len(tv)):
             v1 = axis._get_line_pos(tv[i])
             c = colors[(i-1)%ncolors]
             if c:
@@ -294,7 +298,7 @@
                 L.strokeLineCap = strokeLineCap
                 L.strokeMiterLimit = strokeMiterLimit
                 if t in specials:
-                    for a,v in specials[t].iteritems():
+                    for a,v in specials[t].items():
                         setattr(L,a,v)
                 g.add(L)
 
@@ -517,7 +521,7 @@
         self._length = float(length)
 
     def configure(self, multiSeries,barWidth=None):
-        self._catCount = max(map(len,multiSeries))
+        self._catCount = max(list(map(len,multiSeries)))
         self._barWidth = barWidth or ((self._length-self.loPad-self.hiPad)/float(self._catCount or 1))
         self._calcTickmarkPositions()
         if self.labelAxisMode == 'axispmv':
@@ -526,12 +530,12 @@
     def _calcTickmarkPositions(self):
         n = self._catCount
         if self.tickShift:
-            self._tickValues = [t+0.5 for t in xrange(n)]
+            self._tickValues = [t+0.5 for t in range(n)]
         else:
             if self.reverseDirection:
-                self._tickValues = range(-1,n)
+                self._tickValues = list(range(-1,n))
             else:
-                self._tickValues = range(n+1)
+                self._tickValues = list(range(n+1))
 
     def _scale(self,idx):
         if self.reverseDirection: idx = self._catCount-idx-1
@@ -596,7 +600,7 @@
                 if OTV[-1]<vx: OTV.append(OTV[-1]+dst)
                 dst /= float(nst+1)
                 for i,x in enumerate(OTV[:-1]):
-                    for j in xrange(nst):
+                    for j in range(nst):
                         t = x+dCnv((j+1)*dst)
                         if t<=vn or t>=vx: continue
                         T(t)
@@ -744,7 +748,7 @@
             _x = self._x
             pmv = self._pmv if self.labelAxisMode=='axispmv' else None
 
-            for i in xrange(catCount):
+            for i in range(catCount):
                 if reverseDirection: ic = catCount-i-1
                 else: ic = i
                 if ic>=n: continue
@@ -858,7 +862,7 @@
             _y = self._y
             pmv = self._pmv if self.labelAxisMode=='axispmv' else None
 
-            for i in xrange(catCount):
+            for i in range(catCount):
                 if reverseDirection: ic = catCount-i-1
                 else: ic = i
                 if ic>=n: continue
@@ -1174,10 +1178,10 @@
         abf = self.avoidBoundFrac
         do_rr = not getattr(self,'valueSteps',None)
         do_abf = abf and do_rr
-        if not isinstance(abf,_SequenceTypes):
+        if not isSeq(abf):
             abf = abf, abf
         abfiz = getattr(self,'abf_ignore_zero', False)
-        if not isinstance(abfiz,_SequenceTypes):
+        if not isSeq(abfiz):
             abfiz = abfiz, abfiz
         do_rr = rangeRound is not 'none' and do_rr
         if do_rr:
@@ -1189,7 +1193,7 @@
         abS = self.avoidBoundSpace
         do_abs = abS
         if do_abs:
-            if not isinstance(abS,_SequenceTypes):
+            if not isSeq(abS):
                 abS = abS, abS
         aL = float(self._length)
 
@@ -1313,7 +1317,7 @@
         if rangeRound in ('both','ceiling'):
             if v<valueMax-fuzz: i1 += 1
         elif v>valueMax+fuzz: i1 -= 1
-        return valueStep,[i*valueStep for i in xrange(i0,i1+1)]
+        return valueStep,[i*valueStep for i in range(i0,i1+1)]
 
     def _calcTickPositions(self):
         return self._calcStepAndTickPositions()[1]
@@ -1388,8 +1392,8 @@
                         t = tick*scl
                     else:
                         t = tick
-                    if type(f) is str: txt = f % t
-                    elif isinstance(f,_SequenceTypes):
+                    if isinstance(f, str): txt = f % t
+                    elif isSeq(f):
                         #it's a list, use as many items as we get
                         if i < len(f):
                             txt = f[i]
@@ -1401,7 +1405,7 @@
                         else:
                             txt = f(t)
                     else:
-                        raise ValueError, 'Invalid labelTextFormat %s' % f
+                        raise ValueError('Invalid labelTextFormat %s' % f)
                     if post: txt = post % txt
                     pos[d] = v
                     label.setOrigin(*pos)
@@ -1546,7 +1550,7 @@
     for recurring dates.
     """
     def test(self,x):
-        if isinstance(x,_SequenceTypes):
+        if isSeq(x):
             answer = True
             for element in x:
                 try:
@@ -1680,7 +1684,7 @@
         #specified the days of year to be ticked.  Other explicit routes may
         #be added.
         if self.forceDatesEachYear:
-            forcedPartialDates = map(parseDayAndMonth, self.forceDatesEachYear)
+            forcedPartialDates = list(map(parseDayAndMonth, self.forceDatesEachYear))
             #generate the list of dates in the range.
             #print 'dates range from %s to %s' % (firstDate, endDate)
             firstYear = firstDate.year()
@@ -1770,7 +1774,7 @@
 
         VC = self._valueClass
         for D in data:
-            for i in xrange(len(D)):
+            for i in range(len(D)):
                 x, y = D[i]
                 if not isinstance(x,VC):
                     D[i] = (VC(x),y)
@@ -1796,7 +1800,7 @@
     def configure(self, data):
         self._convertXV(data)
         from reportlab.lib.set_ops import union
-        xVals = reduce(union,map(lambda x: map(lambda dv: dv[0],x),data),[])
+        xVals = reduce(union,[[dv[0] for dv in x] for x in data],[])
         xVals.sort()
         steps,labels = self._getStepsAndLabels(xVals)
         valueMin, valueMax = self.valueMin, self.valueMax
@@ -1914,7 +1918,7 @@
         from reportlab.graphics.charts.utils import find_good_grid, ticks
         y_min, y_max = self._valueMin, self._valueMax
         m = self.maximumTicks
-        n = filter(lambda x,m=m: x<=m,[4,5,6,7,8,9])
+        n = list(filter(lambda x,m=m: x<=m,[4,5,6,7,8,9]))
         if not n: n = [m]
 
         valueStep, requiredRange = self.valueStep, self.requiredRange
@@ -1936,7 +1940,7 @@
         abf = self.avoidBoundFrac
         if abf:
             i1 = (T[1]-T[0])
-            if not isinstance(abf,_SequenceTypes):
+            if not isSeq(abf):
                 i0 = i1 = i1*abf
             else:
                 i0 = i1*abf[0]
@@ -1963,7 +1967,7 @@
             self._valueMin = self._valueMin - m
 
         if self.leftAxisSkipLL0:
-            if isinstance(self.leftAxisSkipLL0,_SequenceTypes):
+            if isSeq(self.leftAxisSkipLL0):
                 for x in self.leftAxisSkipLL0:
                     try:
                         L[x] = ''
--- a/src/reportlab/graphics/charts/barcharts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/barcharts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -9,7 +9,7 @@
 
 """
 
-import copy
+import copy, functools
 
 from reportlab.lib import colors
 from reportlab.lib.validators import isNumber, isNumberOrNone, isColor, isColorOrNone, isString,\
@@ -161,7 +161,7 @@
     def demo(self):
         """Shows basic use of a bar chart"""
         if self.__class__.__name__=='BarChart':
-            raise NotImplementedError, 'Abstract Class BarChart has no demo'
+            raise NotImplementedError('Abstract Class BarChart has no demo')
         drawing = Drawing(200, 100)
         bc = self.__class__()
         drawing.add(bc)
@@ -172,9 +172,9 @@
         data = self.data
         if cA.style not in ('parallel','parallel_3d'):
             _data = data
-            data = max(map(len,_data))*[0]
+            data = max(list(map(len,_data)))*[0]
             for d in _data:
-                for i in xrange(len(d)):
+                for i in range(len(d)):
                     data[i] = data[i] + (d[i] or 0)
             data = list(_data) + [data]
         self._configureData = data
@@ -245,7 +245,7 @@
                 except:
                     raise ValueError('Bad zIndex value %r in clause %r of zIndex\nallowed variables are\n%s' % (v,z,zIndex,'\n'.join(['%s=%r'% (k,Z[k]) for k in sorted(Z.keys())])))
                 Z[k] = v
-            Z = [(v,k) for k,v in Z.iteritems()]
+            Z = [(v,k) for k,v in Z.items()]
             Z.sort()
             b = self.makeBars()
             bl = b.contents.pop(-1)
@@ -288,7 +288,7 @@
 
         data = self.data
         seriesCount = self._seriesCount = len(data)
-        self._rowLength = rowLength = max(map(len,data))
+        self._rowLength = rowLength = max(list(map(len,data)))
         wG = self.groupSpacing
         barSpacing = self.barSpacing
         barWidth = self.barWidth
@@ -359,7 +359,7 @@
             baseLine = vScale(vM)
         self._baseLine = baseLine
 
-        nC = max(map(len,data))
+        nC = max(list(map(len,data)))
 
         width = barWidth*fB
         offs = 0.5*wG*fG
@@ -376,7 +376,7 @@
         self._barPositions = []
         aBP = self._barPositions.append
         reversePlotOrder = self.reversePlotOrder
-        for rowNo in xrange(seriesCount):
+        for rowNo in range(seriesCount):
             barRow = []
             if reversePlotOrder:
                 xVal = seriesCount-1 - rowNo
@@ -384,7 +384,7 @@
                 xVal = rowNo
             xVal = offs + xVal*bGap
             row = data[rowNo]
-            for colNo in xrange(nC):
+            for colNo in range(nC):
                 datum = row[colNo]
 
                 # Ufff...
@@ -426,7 +426,7 @@
             labelText = labelFmt(self.data[rowNo][colNo])
         else:
             msg = "Unknown formatter type %s, expected string or function" % labelFmt
-            raise Exception, msg
+            raise Exception(msg)
         return labelText
 
     def _labelXY(self,label,x,y,width,height):
@@ -544,19 +544,19 @@
             CBL = []
             rowNoL = lenData - 1
             #find all the categories that have at least one value
-            for rowNo in xrange(lenData):
+            for rowNo in range(lenData):
                 row = BP[rowNo]
-                for colNo in xrange(len(row)):
+                for colNo in range(len(row)):
                     x, y, width, height = row[colNo]
                     if None not in (width,height):
                         catNNA[colNo] = 1
 
-        for rowNo in xrange(lenData):
+        for rowNo in range(lenData):
             row = BP[rowNo]
             styleCount = len(bars)
             styleIdx = rowNo % styleCount
             rowStyle = bars[styleIdx]
-            for colNo in xrange(len(row)):
+            for colNo in range(len(row)):
                 style = (styleIdx,colNo) in bars and bars[(styleIdx,colNo)] or rowStyle
                 x, y, width, height = row[colNo]
                 if None in (width,height):
@@ -678,7 +678,7 @@
             lo = self.x
             hi = lo + self.width
             end = self.y+self.height
-            for i in xrange(lenData):
+            for i in range(lenData):
                 for x, y, w, h in BP[i]:
                     v = x+w
                     z = y+h
@@ -687,7 +687,7 @@
             lo = self.y
             hi = lo + self.height
             end = self.x+self.width
-            for i in xrange(lenData):
+            for i in range(lenData):
                 for x, y, w, h in BP[i]:
                     v = y+h
                     z = x+w
@@ -741,7 +741,7 @@
         bars = self.bars
         R = [].append
         BP = self._barPositions
-        for rowNo in xrange(lenData):
+        for rowNo in range(lenData):
             row = BP[rowNo]
             C = [].append
             for colNo in range(len(row)):
@@ -777,7 +777,7 @@
         style = self.categoryAxis.style
         data = self.data
         n = len(data)
-        m = max(map(len,data))
+        m = max(list(map(len,data)))
         if style=='parallel':
             groupWidth = (n-1)*self.barSpacing+n*self.barWidth
         else:
@@ -805,7 +805,7 @@
 class _FakeGroup:
     def __init__(self, cmp=None):
         self._data = []
-        self._cmp = cmp
+        self._key = functools.cmp_to_key(cmp)
 
     def add(self,what):
         self._data.append(what)
@@ -814,7 +814,7 @@
         return self._data
 
     def sort(self):
-        self._data.sort(self._cmp)
+        self._data.sort(key=self._key)
 
 class BarChart3D(BarChart):
     _attrMap = AttrMap(BASE=BarChart,
@@ -886,7 +886,7 @@
         g.add((1,z0,z1,x,y,width,height,rowNo,colNo))
 
     def makeBars(self):
-        from utils3d import _draw_3d_bar
+        from reportlab.graphics.charts.utils3d import _draw_3d_bar
         fg = _FakeGroup(cmp=self._cmpZ)
         self._makeBars(fg,fg)
         fg.sort()
@@ -1034,7 +1034,7 @@
     bc.categoryAxis.labels.angle = 30
 
     catNames = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
-    catNames = map(lambda n:n+'-99', catNames)
+    catNames = [n+'-99' for n in catNames]
     bc.categoryAxis.categoryNames = catNames
     drawing.add(bc)
 
@@ -1655,7 +1655,7 @@
 
     bc.categoryAxis.labels.boxAnchor = 'e'
     catNames = 'Jan Feb Mar Apr May Jun Jul Aug'.split(' ')
-    catNames = map(lambda n:n+'-99', catNames)
+    catNames = [n+'-99' for n in catNames]
     bc.categoryAxis.categoryNames = catNames
     drawing.add(bc, 'barchart')
 
--- a/src/reportlab/graphics/charts/doughnut.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/doughnut.py	Wed Mar 26 12:32:02 2014 +0000
@@ -13,7 +13,6 @@
 
 import copy
 from math import sin, cos, pi
-from types import ListType, TupleType
 from reportlab.lib import colors
 from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\
                                     isListOfNumbers, isColorOrNone, isString,\
@@ -30,6 +29,7 @@
 from reportlab.graphics.charts.piecharts import AbstractPieChart, WedgeProperties, _addWedgeLabel, fixLabelOverlaps
 from reportlab.graphics.charts.textlabels import Label
 from reportlab.graphics.widgets.markers import Marker
+from functools import reduce
 
 class SectorProperties(WedgeProperties):
     """This holds descriptive information about the sectors in a doughnut chart.
@@ -113,11 +113,11 @@
     def normalizeData(self, data=None):
         from operator import add
         sum = float(reduce(add,data,0))
-        return abs(sum)>=1e-8 and map(lambda x,f=360./sum: f*x, data) or len(data)*[0]
+        return abs(sum)>=1e-8 and list(map(lambda x,f=360./sum: f*x, data)) or len(data)*[0]
 
     def makeSectors(self):
         # normalize slice data
-        if type(self.data) in (ListType, TupleType) and type(self.data[0]) in (ListType, TupleType):
+        if isinstance(self.data,(list,tuple)) and isinstance(self.data[0],(list,tuple)):
             #it's a nested list, more than one sequence
             normData = []
             n = []
@@ -138,7 +138,7 @@
         
         if self.labels is None:
             labels = []
-            if type(n) not in (ListType,TupleType):
+            if not isinstance(n,(list,tuple)):
                 labels = [''] * n
             else:
                 for m in n:
@@ -147,7 +147,7 @@
             labels = self.labels
             #there's no point in raising errors for less than enough labels if
             #we silently create all for the extreme case of no labels.
-            if type(n) not in (ListType,TupleType):
+            if not isinstance(n,(list,tuple)):
                 i = n-len(labels)
                 if i>0:
                     labels = list(labels) + [''] * i
@@ -173,7 +173,7 @@
         
         startAngle = self.startAngle #% 360
         styleCount = len(self.slices)
-        if type(self.data[0]) in (ListType, TupleType):
+        if isinstance(self.data[0],(list,tuple)):
             #multi-series doughnut
             iradius = (self.height/5.0)/len(self.data)
             for sn,series in enumerate(normData):
@@ -204,7 +204,7 @@
                         cx = centerx + popdistance * cos(aveAngleRadians)
                         cy = centery + popdistance * sin(aveAngleRadians)
 
-                    if type(n) in (ListType,TupleType):
+                    if isinstance(n,(list,tuple)):
                         theSector = Wedge(cx, cy, xradius+(sn*iradius)-iradius, a1, a2, yradius=yradius+(sn*iradius)-iradius, radius1=yradius+(sn*iradius)-(2*iradius))
                     else:
                         theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, radius1=iradius)
--- a/src/reportlab/graphics/charts/legends.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/legends.py	Wed Mar 26 12:32:02 2014 +0000
@@ -17,8 +17,9 @@
 from reportlab.graphics.shapes import Drawing, Group, String, Rect, Line, STATE_DEFAULTS
 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.lib.utils import isSeq, find_locals
 from reportlab.graphics.shapes import _baseGFontName
+from functools import reduce
 
 def _transMax(n,A):
     X = n*[0]
@@ -28,31 +29,31 @@
         for i,x in enumerate(a):
             X[i] = max(X[i],x)
     X = [0] + X[:m]
-    for i in xrange(m):
+    for i in range(m):
         X[i+1] += X[i]
     return X
 
 def _objStr(s):
-    if isinstance(s,basestring):
+    if isinstance(s,str):
         return s
     else:
         return str(s)
 
 def _getStr(s):
-    if isSeqType(s):
-        return map(_getStr,s)
+    if isSeq(s):
+        return list(map(_getStr,s))
     else:
         return _objStr(s)
 
 def _getLines(s):
-    if isSeqType(s):
+    if isSeq(s):
         return tuple([(x or '').split('\n') for x in s])
     else:
         return (s or '').split('\n')
 
 def _getLineCount(s):
     T = _getLines(s)
-    if isSeqType(s):
+    if isSeq(s):
         return max([len(x) for x in T])
     else:
         return len(T)
@@ -60,7 +61,7 @@
 def _getWidths(i,s, fontName, fontSize, subCols):
     S = []
     aS = S.append
-    if isSeqType(s):
+    if isSeq(s):
         for j,t in enumerate(s):
             sc = subCols[j,i]
             fN = getattr(sc,'fontName',fontName)
@@ -239,7 +240,7 @@
             texts = [_getStr(p[1]) for p in colorNamePairs]
         else:
             chart = getattr(colorNamePairs,'chart',getattr(colorNamePairs,'obj',None))
-            texts = [chart.getSeriesName(i,'series %d' % i) for i in xrange(chart._seriesCount)]
+            texts = [chart.getSeriesName(i,'series %d' % i) for i in range(chart._seriesCount)]
         return texts
 
     def _calculateMaxBoundaries(self, colorNamePairs):
@@ -254,7 +255,7 @@
         n = max([len(m) for m in M])
         if self.variColumn:
             columnMaximum = self.columnMaximum
-            return [_transMax(n,M[r:r+columnMaximum]) for r in xrange(0,len(M),self.columnMaximum)]
+            return [_transMax(n,M[r:r+columnMaximum]) for r in range(0,len(M),self.columnMaximum)]
         else:
             return _transMax(n,M)
 
@@ -371,7 +372,7 @@
             dividerOffsX = self.dividerOffsX
             dividerOffsY = self.dividerOffsY
 
-        for i in xrange(n):
+        for i in range(n):
             if autoCP:
                 col = autoCP
                 col.index = i
@@ -400,8 +401,8 @@
                 x = thisx+dx+dxTextSpace
                 xn = thisx
             else:
-                raise ValueError, "bad alignment"
-            if not isSeqType(name):
+                raise ValueError("bad alignment")
+            if not isSeq(name):
                 T = [T]
             yd = y
             for k,lines in enumerate(T):
@@ -525,7 +526,7 @@
         legend.y = 100
         legend.dxTextSpace = 5
         items = 'red green blue yellow pink black white'.split()
-        items = map(lambda i:(getattr(colors, i), i), items)
+        items = [(getattr(colors, i), i) for i in items]
         legend.colorNamePairs = items
 
         d.add(legend, 'legend')
@@ -641,7 +642,7 @@
     legend.y = 100
     legend.dxTextSpace = 5
     items = 'red green blue yellow pink black white'.split()
-    items = map(lambda i:(getattr(colors, i), i), items)
+    items = [(getattr(colors, i), i) for i in items]
     legend.colorNamePairs = items
 
     d.add(legend, 'legend')
@@ -662,7 +663,7 @@
     legend.dxTextSpace = 10
     legend.columnMaximum = 4
     items = 'red green blue yellow pink black white'.split()
-    items = map(lambda i:(getattr(colors, i), i), items)
+    items = [(getattr(colors, i), i) for i in items]
     legend.colorNamePairs = items
 
     d.add(legend, 'legend')
@@ -682,7 +683,7 @@
     legend.dxTextSpace = 10
     legend.columnMaximum = 4
     items = 'red green blue yellow pink black white'.split()
-    items = map(lambda i:(getattr(colors, i), i), items)
+    items = [(getattr(colors, i), i) for i in items]
     legend.colorNamePairs = items
     d.add(legend, 'legend')
 
--- a/src/reportlab/graphics/charts/linecharts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/linecharts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -214,7 +214,7 @@
         """
 
         self._seriesCount = len(self.data)
-        self._rowLength = max(map(len,self.data))
+        self._rowLength = max(list(map(len,self.data)))
 
         if self.useAbsolute:
             # Dimensions are absolute.
@@ -285,7 +285,7 @@
         g = Group()
 
         labelFmt = self.lineLabelFormat
-        P = range(len(self._positions))
+        P = list(range(len(self._positions)))
         if self.reversePlotOrder: P.reverse()
         inFill = self.inFill
         if inFill:
@@ -398,9 +398,9 @@
         for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
         return g
 
-def _cmpFakeItem(a,b):
+def _fakeItemKey(a):
     '''t, z0, z1, x, y = a[:5]'''
-    return cmp((-a[1],a[3],a[0],-a[4]),(-b[1],b[3],b[0],-b[4]))
+    return (-a[1],a[3],a[0],-a[4])
 
 class _FakeGroup:
     def __init__(self):
@@ -413,7 +413,7 @@
         return self._data
 
     def sort(self):
-        self._data.sort(_cmpFakeItem)
+        self._data.sort(key=_fakeItemKey)
         #for t in self._data: print t
 
 class HorizontalLineChart3D(HorizontalLineChart):
@@ -453,7 +453,7 @@
 
     def makeLines(self):
         labelFmt = self.lineLabelFormat
-        P = range(len(self._positions))
+        P = list(range(len(self._positions)))
         if self.reversePlotOrder: P.reverse()
         inFill = self.inFill
         assert not inFill, "inFill not supported for 3d yet"
@@ -467,7 +467,7 @@
         theta_x = self.theta_x
         theta_y = self.theta_y
         F = _FakeGroup()
-        from utils3d import _make_3d_line_info
+        from reportlab.graphics.charts.utils3d import _make_3d_line_info
         tileWidth = getattr(self,'_3d_tilewidth',None)
         if not tileWidth and self.categoryAxis.style!='parallel_3d': tileWidth = 1
 
@@ -494,7 +494,7 @@
             if self.joinedLines:
                 if n:
                     x0, y0 = row[0]
-                    for colNo in xrange(1,n):
+                    for colNo in range(1,n):
                         x1, y1 = row[colNo]
                         _make_3d_line_info( F, x0, x1, y0, y1, z0, z1,
                                 theta_x, theta_y,
@@ -511,14 +511,14 @@
                 uSymbol = None
 
             if uSymbol:
-                for colNo in xrange(n):
+                for colNo in range(n):
                     x1, y1 = row[colNo]
                     x1, y1 = _zadjust(x1,y1,z0)
                     symbol = uSymbol2Symbol(uSymbol,x1,y1,rowColor)
                     if symbol: F.add((2,z0,z0,x1,y1,symbol))
 
             # Draw item labels.
-            for colNo in xrange(n):
+            for colNo in range(n):
                 x1, y1 = row[colNo]
                 x1, y1 = _zadjust(x1,y1,z0)
                 L = self._innerDrawLabel(rowNo, colNo, x1, y1)
--- a/src/reportlab/graphics/charts/lineplots.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/lineplots.py	Wed Mar 26 12:32:02 2014 +0000
@@ -71,7 +71,7 @@
 class PolyFiller(Filler,Polygon):
     pass
 
-from linecharts import AbstractLineChart
+from reportlab.graphics.charts.linecharts import AbstractLineChart
 class LinePlot(AbstractLineChart):
     """Line plot with multiple lines.
 
@@ -184,14 +184,14 @@
         """
 
         self._seriesCount = len(self.data)
-        self._rowLength = max(map(len,self.data))
+        self._rowLength = max(list(map(len,self.data)))
 
         self._positions = []
         for rowNo in range(len(self.data)):
             line = []
             for colNo in range(len(self.data[rowNo])):
                 datum = self.data[rowNo][colNo] # x,y value
-                if type(datum[0]) == type(''):
+                if isinstance(datum[0],str):
                     x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0])))
                 else:
                     x = self.xValueAxis.scale(datum[0])
@@ -207,7 +207,7 @@
 
         if labelFmt is None:
             labelText = None
-        elif type(labelFmt) is StringType:
+        elif isinstance(labelFmt,str):
             if labelFmt == 'values':
                 labelText = self.lineLabelArray[rowNo][colNo]
             else:
@@ -218,7 +218,7 @@
             else:
                 labelText = labelFmt(self,rowNo,colNo,x,y)
         else:
-            raise ValueError("Unknown formatter type %s, expected string or function"%labelFmt)
+            raise ValueError("Unknown formatter type %s, expected string or function"% labelFmt)
 
         if labelText:
             label = self.lineLabels[(rowNo, colNo)]
@@ -249,7 +249,7 @@
 
         labelFmt = self.lineLabelFormat
 
-        P = range(len(self._positions))
+        P = list(range(len(self._positions)))
         if self.reversePlotOrder: P.reverse()
         inFill = getattr(self,'_inFill',None)
         styleCount = len(self.lines)
@@ -405,9 +405,9 @@
             x = xScale(xv)
             y = yScale(yv)
             g = Group()
-            xA = xScale.im_self #the x axis
+            xA = xScale.__self__ #the x axis
             g.add(Line(xA._x,y,xA._x+xA._length,y,strokeColor=strokeColor,strokeWidth=strokeWidth))
-            yA = yScale.im_self #the y axis
+            yA = yScale.__self__ #the y axis
             g.add(Line(x,yA._y,x,yA._y+yA._length,strokeColor=strokeColor,strokeWidth=strokeWidth))
             return g
         annotation.beforeLines = beforeLines
@@ -461,7 +461,7 @@
         labelFmt = self.lineLabelFormat
         positions = self._positions
 
-        P = range(len(positions))
+        P = list(range(len(positions)))
         if self.reversePlotOrder: P.reverse()
         inFill = getattr(self,'_inFill',None)
         assert not inFill, "inFill not supported for 3d yet"
@@ -474,15 +474,15 @@
         _zadjust = self._zadjust
         theta_x = self.theta_x
         theta_y = self.theta_y
-        from linecharts import _FakeGroup
+        from reportlab.graphics.charts.linecharts import _FakeGroup
         F = _FakeGroup()
 
-        from utils3d import _make_3d_line_info, find_intersections
+        from reportlab.graphics.charts.utils3d import _make_3d_line_info, find_intersections
         if self.xValueAxis.style!='parallel_3d':
             tileWidth = getattr(self,'_3d_tilewidth',1)
             if getattr(self,'_find_intersections',None):
                 from copy import copy
-                fpositions = map(copy,positions)
+                fpositions = list(map(copy,positions))
                 I = find_intersections(fpositions,small=tileWidth)
                 ic = None
                 for i,j,x,y in I:
@@ -522,7 +522,7 @@
                 if n:
                     frow = fpositions[rowNo]
                     x0, y0 = frow[0]
-                    for colNo in xrange(1,len(frow)):
+                    for colNo in range(1,len(frow)):
                         x1, y1 = frow[colNo]
                         _make_3d_line_info( F, x0, x1, y0, y1, z0, z1,
                                 theta_x, theta_y,
@@ -546,7 +546,7 @@
                     if symbol: F.add((1,z0,z0,x1,y1,symbol))
 
             # Draw data labels.
-            for colNo in xrange(n):
+            for colNo in range(n):
                 x1, y1 = row[colNo]
                 x1, y1 = _zadjust(x1,y1,z0)
                 L = self._innerDrawLabel(rowNo, colNo, x1, y1)
@@ -716,13 +716,13 @@
         back = self.background
         if isinstance(back, Grid):
             if back.orientation == 'vertical' and xva._tickValues:
-                xpos = map(xva.scale, [xva._valueMin] + xva._tickValues)
+                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                 steps = []
                 for i in range(len(xpos)-1):
                     steps.append(xpos[i+1] - xpos[i])
                 back.deltaSteps = steps
             elif back.orientation == 'horizontal' and yva._tickValues:
-                ypos = map(yva.scale, [yva._valueMin] + yva._tickValues)
+                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                 steps = []
                 for i in range(len(ypos)-1):
                     steps.append(ypos[i+1] - ypos[i])
@@ -740,25 +740,25 @@
 
             # some room left for optimization...
             if back.grid0.orientation == 'vertical' and xva._tickValues:
-                xpos = map(xva.scale, [xva._valueMin] + xva._tickValues)
+                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                 steps = []
                 for i in range(len(xpos)-1):
                     steps.append(xpos[i+1] - xpos[i])
                 back.grid0.deltaSteps = steps
             elif back.grid0.orientation == 'horizontal' and yva._tickValues:
-                ypos = map(yva.scale, [yva._valueMin] + yva._tickValues)
+                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                 steps = []
                 for i in range(len(ypos)-1):
                     steps.append(ypos[i+1] - ypos[i])
                 back.grid0.deltaSteps = steps
             if back.grid1.orientation == 'vertical' and xva._tickValues:
-                xpos = map(xva.scale, [xva._valueMin] + xva._tickValues)
+                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                 steps = []
                 for i in range(len(xpos)-1):
                     steps.append(xpos[i+1] - xpos[i])
                 back.grid1.deltaSteps = steps
             elif back.grid1.orientation == 'horizontal' and yva._tickValues:
-                ypos = map(yva.scale, [yva._valueMin] + yva._tickValues)
+                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                 steps = []
                 for i in range(len(ypos)-1):
                     steps.append(ypos[i+1] - ypos[i])
@@ -795,9 +795,9 @@
             m = len(odata[0])
             S = n*[0]
             self.data = []
-            for i in xrange(1,m):
+            for i in range(1,m):
                 D = []
-                for j in xrange(n):
+                for j in range(n):
                     S[j] = S[j] + odata[j][i]
                     D.append((odata[j][0],S[j]))
                 self.data.append(D)
@@ -821,9 +821,9 @@
 
 def _maxWidth(T, fontName, fontSize):
     '''return max stringWidth for the list of strings T'''
-    if type(T) not in (type(()),type([])): T = (T,)
-    T = filter(None,T)
-    return T and max(map(lambda t,sW=stringWidth,fN=fontName, fS=fontSize: sW(t,fN,fS),T)) or 0
+    if not isinstance(T,(tuple,list)): T = (T,)
+    T = [_f for _f in T if _f]
+    return T and max(list(map(lambda t,sW=stringWidth,fN=fontName, fS=fontSize: sW(t,fN,fS),T))) or 0
 
 class ScatterPlot(LinePlot):
     """A scatter plot widget"""
@@ -1086,7 +1086,7 @@
 def preprocessData(series):
     "Convert date strings into seconds and multiply values by 100."
 
-    return map(lambda x: (str2seconds(x[0]), x[1]*100), series)
+    return [(str2seconds(x[0]), x[1]*100) for x in series]
 
 
 def sample2():
--- a/src/reportlab/graphics/charts/piecharts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/piecharts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -13,7 +13,7 @@
 supports elliptical and circular pies.
 """
 
-import copy
+import copy, functools
 from math import sin, cos, pi
 
 from reportlab.lib import colors
@@ -256,7 +256,7 @@
     '''determine a set of overlaps in bounding boxes B or return None'''
     n = len(B)
     if n>1:
-        for i in xrange(n-1):
+        for i in range(n-1):
             R = _findOverlapRun(B,i,wrap)
             if len(R)>1: return R
     return None
@@ -353,10 +353,14 @@
             a = (1,sa,offs+90),(0,offs+90,offs+270),(1,offs+270,360+sa)
     return tuple([a for a in a if a[1]<a[2]])
 
+def _keyFLA(x,y):
+    return cmp(y[1]-y[0],x[1]-x[0])
+_keyFLA = functools.cmp_to_key(_keyFLA)
+
 def _findLargestArc(xArcs,side):
     a = [a[1] for a in xArcs if a[0]==side and a[1] is not None]
     if not a: return None
-    if len(a)>1: a.sort(lambda x,y: cmp(y[1]-y[0],x[1]-x[0]))
+    if len(a)>1: a.sort(key=_keyFLA)
     return a[0]
 
 def _fPLSide(l,width,side=None):
@@ -392,11 +396,13 @@
     data['side'] = side
     return side,w
 
-def _fPLCF(a,b):
+#key functions
+def _fPLCF(a,b): 
     return cmp(b._origdata['smid'],a._origdata['smid'])
+_fPLCF = functools.cmp_to_key(_fPLCF)
 
-def _arcCF(a,b):
-    return cmp(a[1],b[1])
+def _arcCF(a):
+    return a[1]
 
 def _fixPointerLabels(n,L,x,y,width,height,side=None):
     LR = [],[]
@@ -418,7 +424,7 @@
             aB = B.append
             S = []
             aS = S.append
-            T.sort(_fPLCF)
+            T.sort(key=_fPLCF)
             p = 0
             yh = y+height
             for l in T:
@@ -481,7 +487,7 @@
     vstar = len(data)*1e6
     rstar = 0
     delta = pi/36.0
-    for i in xrange(36):
+    for i in range(36):
         r = i*delta
         v = sum([abs(sin(r+a)) for a in hrads])
         if v < vstar:
@@ -658,7 +664,7 @@
         return PL(centerx,centery,xradius,yradius,G,lu,ru)
 
     def normalizeData(self,keepData=False):
-        data = map(abs,self.data)
+        data = list(map(abs,self.data))
         s = self._sum = float(sum(data))
         if s<=1e-8: s = 0
         f = 360./s
@@ -678,7 +684,7 @@
         D = [a for a in enumerate(self.normalizeData(keepData=wr))]
         if self.orderMode=='alternate' and not self.sideLabels:
             W = [a for a in D if abs(a[1])>=1e-5]
-            W.sort(_arcCF)
+            W.sort(key=_arcCF)
             T = [[],[]]
             i = 0
             while W:
@@ -968,7 +974,7 @@
         self.legend1.columnMaximum = 7
         self.legend1.alignment = 'right'
         self.legend_names = ['AAA:','AA:','A:','BBB:','NR:']
-        for f in xrange(len(self.data)):
+        for f in range(len(self.data)):
             self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f]))
         self.legend1.fontName = "Helvetica-Bold"
         self.legend1.fontSize = 6
@@ -993,7 +999,7 @@
         if self.drawLegend:
             self.legend1.colorNamePairs = []
             self._legend2.colorNamePairs = []
-        for f in xrange(len(self.data)):
+        for f in range(len(self.data)):
             if self.legend_names == None:
                 self.slices[f].fillColor = self.pieAndLegend_colors[f]
                 self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], None))
@@ -1007,28 +1013,15 @@
             if self.legend_data != None:
                 ldf = self.legend_data[f]
                 lNF = self.legendNumberFormat
-                from types import StringType
                 if ldf is None or lNF is None:
                     pass
-                elif type(lNF) is StringType:
+                elif isinstance(lNF,str):
                     ldf = lNF % ldf
                 elif hasattr(lNF,'__call__'):
                     ldf = lNF(ldf)
                 else:
-                    p = self.legend_names[f]
-                if self.legend_data != None:
-                    ldf = self.legend_data[f]
-                    lNF = self.legendNumberFormat
-                    if ldf is None or lNF is None:
-                        pass
-                    elif type(lNF) is StringType:
-                        ldf = lNF % ldf
-                    elif hasattr(lNF,'__call__'):
-                        ldf = lNF(ldf)
-                    else:
-                        msg = "Unknown formatter type %s, expected string or function" % self.legendNumberFormat
-                        raise Exception, msg
-                    self._legend2.colorNamePairs.append((None,ldf))
+                    raise ValueError("Unknown formatter type %s, expected string or function" % ascii(self.legendNumberFormat))
+                self._legend2.colorNamePairs.append((None,ldf))
         p = Pie.draw(self)
         if self.drawLegend:
             p.add(self.legend1)
@@ -1143,6 +1136,10 @@
     def __str__(self):
         return '_SL3D(%.2f,%.2f)' % (self.lo,self.hi)
 
+def _keyS3D(a,b):
+    return -cmp(a[0],b[0])
+_keyS3D = functools.cmp_to_key(_keyS3D)
+
 _270r = _2rad(270)
 class Pie3d(Pie):
     _attrMap = AttrMap(BASE=Pie,
@@ -1243,7 +1240,7 @@
     
         checkLabelOverlap = self.checkLabelOverlap
 
-        for i in xrange(n):
+        for i in range(n):
             style = slices[i]
             if not style.visible: continue
             sl = _sl3d[i]
@@ -1310,7 +1307,7 @@
                 self._radiusx = radiusx
                 self._radiusy = radiusy
 
-        S.sort(lambda a,b: -cmp(a[0],b[0]))
+        S.sort(key=_keyS3D)
         if checkLabelOverlap and L:
             fixLabelOverlaps(L,sideLabels)
         for x in ([s[1] for s in S]+T+L):
--- a/src/reportlab/graphics/charts/spider.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/spider.py	Wed Mar 26 12:32:02 2014 +0000
@@ -30,7 +30,7 @@
 from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
 from reportlab.graphics.charts.areas import PlotArea
 from reportlab.graphics.charts.legends import _objStr
-from piecharts import WedgeLabel
+from reportlab.graphics.charts.piecharts import WedgeLabel
 from reportlab.graphics.widgets.markers import makeMarker, uSymbol2Symbol, isSymbol
 
 class StrandProperty(PropHolder):
@@ -75,7 +75,7 @@
 class SpokeLabel(WedgeLabel):
     def __init__(self,**kw):
         WedgeLabel.__init__(self,**kw)
-        if '_text' not in kw.keys(): self._text = ''
+        if '_text' not in list(kw.keys()): self._text = ''
 
 class StrandLabel(SpokeLabel):
     _attrMap = AttrMap(BASE=SpokeLabel,
@@ -189,8 +189,8 @@
         """Turns data into normalized ones where each datum is < 1.0,
         and 1.0 = maximum radius.  Adds 10% at outside edge by default"""
         data = self.data
-        assert min(map(min,data)) >=0, "Cannot do spider plots of negative numbers!"
-        norm = max(map(max,data))
+        assert min(list(map(min,data))) >=0, "Cannot do spider plots of negative numbers!"
+        norm = max(list(map(max,data)))
         norm *= (1.0+outer)
         if norm<1e-9: norm = 1.0
         self._norm = norm
@@ -259,7 +259,7 @@
         angleBetween = direction*(2 * pi)/float(n)
         spokes = self.spokes
         spokeLabels = self.spokeLabels
-        for i in xrange(n):
+        for i in range(n):
             car = cos(angle)*radius
             sar = sin(angle)*radius
             csa.append((car,sar,angle))
@@ -286,7 +286,7 @@
             r = row[-1]
             points.append(cx+car*r)
             points.append(cy+sar*r)
-            for i in xrange(n):
+            for i in range(n):
                 car, sar, angle = csa[i]
                 r = row[i]
                 points.append(cx+car*r)
--- a/src/reportlab/graphics/charts/textlabels.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/textlabels.py	Wed Mar 26 12:32:02 2014 +0000
@@ -29,20 +29,22 @@
     O = []
     P = []
     R = []
+    if G and len(G)==1 and G[0][0]=='lineTo':
+        G = (('moveToClosed',)+G[0][1:],)+G #hack fix for some errors
     for g in G+(('end',),):
         op = g[0]
         if O and op in ['moveTo', 'moveToClosed','end']:
             if O[0]=='moveToClosed':
                 O = O[1:]
                 if pathReverse:
-                    for i in xrange(0,len(P),2):
+                    for i in range(0,len(P),2):
                         P[i+1], P[i] = P[i:i+2]
                     P.reverse()
                     O.reverse()
                 O.insert(0,'moveTo')
                 O.append('closePath')
             i = 0
-            if truncate: P = map(_pathNumTrunc,P)
+            if truncate: P = list(map(_pathNumTrunc,P))
             for o in O:
                 j = i + _PATH_OP_ARG_COUNT[_PATH_OP_NAMES.index(o)]
                 if o=='closePath':
@@ -58,11 +60,8 @@
 
 def _text2PathDescription(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
                             anchor='start', truncate=1, pathReverse=0):
-    global _gs
-    if not _gs:
-        import _renderPM
-        _gs = _renderPM.gstate(1,1)
-    from reportlab.graphics import renderPM
+    from reportlab.graphics import renderPM, _renderPM
+    _gs = _renderPM.gstate(1,1)
     renderPM._setFont(_gs,fontName,fontSize)
     P = []
     if not anchor=='start':
@@ -76,9 +75,9 @@
     return P
 
 def _text2Path(text, x=0, y=0, fontName=_baseGFontName, fontSize=1000,
-                anchor='start', truncate=1, pathReverse=0):
+                anchor='start', truncate=1, pathReverse=0,**kwds):
     return definePath(_text2PathDescription(text,x=x,y=y,fontName=fontName,
-                    fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse))
+                    fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse),**kwds)
 
 _BA2TA={'w':'start','nw':'start','sw':'start','e':'end', 'ne': 'end', 'se':'end', 'n':'middle','s':'middle','c':'middle'}
 class Label(Widget):
--- a/src/reportlab/graphics/charts/utils.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/utils.py	Wed Mar 26 12:32:02 2014 +0000
@@ -16,7 +16,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, timeString.split('/'))
+    dd, mm, yyyy = list(map(int, timeString.split('/')))
     list[:3] = [yyyy, mm, dd]
 
     return tuple(list)
@@ -84,7 +84,7 @@
                 lo = 0.9*lo
                 hi = 1.1*hi
         else:
-            raise ValueError, "lo>hi"
+            raise ValueError("lo>hi")
     x=(hi - lo)/float(I)
     b= (x>0 and (x<1 or x>10)) and 10**floor(log10(x)) or 1
     b = b
@@ -129,7 +129,7 @@
         try:
             n[0]
         except TypeError:
-            n = xrange(max(1,n-2),max(n+3,2))
+            n = range(max(1,n-2),max(n+3,2))
 
         w = 1e308
         for i in n:
@@ -166,13 +166,13 @@
     n = int(float(hi-t)/grid+0.1)+1
     if split:
         labels = []
-        for i in xrange(n):
+        for i in range(n):
             v = t+grid*i
             T.append(v)
             labels.append(format % (v+labelVOffset))
         return T, labels
     else:
-        for i in xrange(n):
+        for i in range(n):
             v = t+grid*i
             T.append((v, format % (v+labelVOffset)))
         return T
@@ -189,7 +189,7 @@
             l -= 1
         l+=1
         if b or l: data = data[b:l]
-        I = [i for i in xrange(len(data)) if data[i] is None]
+        I = [i for i in range(len(data)) if data[i] is None]
         for i in I:
             data[i] = 0.5*(data[i-1]+data[i+1])
         return b, l, data
@@ -206,7 +206,7 @@
 
 def maverage(data,n=6):
     data = (n-1)*[data[0]]+data
-    data = [float(sum(data[i-n:i]))/n for i in xrange(n,len(data)+1)]
+    data = [float(sum(data[i-n:i]))/n for i in range(n,len(data)+1)]
     return data
 
 def pairMaverage(data,n=6):
@@ -279,7 +279,7 @@
             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)]
+            p = [(p[i],p[i+1]) for i in range(0,len(p),2)]
 
         D = kwds.copy()
         D['poly'] = self.transformAndFlatten(A,p)
@@ -298,14 +298,18 @@
         finally:
             f.close()
 
-def xyDist( (x0,y0),(x1,y1) ):
+def xyDist(xxx_todo_changeme, xxx_todo_changeme1 ):
     '''return distance between two points'''
+    (x0,y0) = xxx_todo_changeme
+    (x1,y1) = xxx_todo_changeme1
     return hypot((x1-x0),(y1-y0))
 
-def lineSegmentIntersect(
-                (x00,y00),(x01,y01),
-                (x10,y10),(x11,y11)
+def lineSegmentIntersect(xxx_todo_changeme2, xxx_todo_changeme3, xxx_todo_changeme4, xxx_todo_changeme5
                 ):
+    (x00,y00) = xxx_todo_changeme2
+    (x01,y01) = xxx_todo_changeme3
+    (x10,y10) = xxx_todo_changeme4
+    (x11,y11) = xxx_todo_changeme5
     p = x00,y00
     r = x01-x00,y01-y00
 
@@ -373,7 +377,7 @@
             self.store = self._changer(obj)
             assert isinstance(self.store,dict), '%s.changer should return a dict of changed attributes' % self.__class__.__name__
         elif self.store is not None:
-            for a,v in self.store.iteritems():
+            for a,v in self.store.items():
                 setattr(obj,a,v)
             self.store = None
 
--- a/src/reportlab/graphics/charts/utils3d.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/charts/utils3d.py	Wed Mar 26 12:32:02 2014 +0000
@@ -83,7 +83,7 @@
             dy = float(y1-y0)/n
             D = []
             a = D.append
-            for i in xrange(1,n):
+            for i in range(1,n):
                 a((x0+dx*i,y0+dy*i))
 
     a = G.add
@@ -176,8 +176,8 @@
             t = o.s,o.i,x,y
             if t not in I:  I.append(t)
 
-def _segCmp(a,b):
-    return cmp((a.x0,a.x1,a.y0,a.y1,a.s,a.i),(b.x0,b.x1,b.y0,b.y1,b.s,b.i))
+def _segKey(a):
+    return (a.x0,a.x1,a.y0,a.y1,a.s,a.i)
 
 def find_intersections(data,small=0):
     '''
@@ -199,20 +199,20 @@
     #find all line segments
     S = []
     a = S.append
-    for s in xrange(len(data)):
+    for s in range(len(data)):
         ds = data[s]
         if not ds: continue
         n = len(ds)
         if n==1: continue
-        for i in xrange(1,n):
+        for i in range(1,n):
             seg = _Segment(s,i,data)
             if seg.a+abs(seg.b)>=small: a(seg)
-    S.sort(_segCmp)
+    S.sort(key=_segKey)
     I = []
     n = len(S)
-    for i in xrange(0,n-1):
+    for i in range(0,n-1):
         s = S[i]
-        for j in xrange(i+1,n):
+        for j in range(i+1,n):
             if s.intersect(S[j],I)==1: break
     I.sort()
     return I
@@ -226,8 +226,8 @@
 
     D.save(formats=['pdf'],outDir='.',fnRoot='_draw_3d_bar')
 
-    print find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(.2666666667,0.4),(0.1,0.4),(0.1,0.2),(0,0),(1,1)],[(0,1),(0.4,0.1),(1,0.1)]])
-    print find_intersections([[(0.1, 0.2), (0.1, 0.4)], [(0, 1), (0.4, 0.1)]])
-    print find_intersections([[(0.2, 0.4), (0.1, 0.4)], [(0.1, 0.8), (0.4, 0.1)]])
-    print find_intersections([[(0,0),(1,1)],[(0.4,0.1),(1,0.1)]])
-    print find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(0,0),(1,1)],[(0.1,0.8),(0.4,0.1),(1,0.1)]])
+    print(find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(.2666666667,0.4),(0.1,0.4),(0.1,0.2),(0,0),(1,1)],[(0,1),(0.4,0.1),(1,0.1)]]))
+    print(find_intersections([[(0.1, 0.2), (0.1, 0.4)], [(0, 1), (0.4, 0.1)]]))
+    print(find_intersections([[(0.2, 0.4), (0.1, 0.4)], [(0.1, 0.8), (0.4, 0.1)]]))
+    print(find_intersections([[(0,0),(1,1)],[(0.4,0.1),(1,0.1)]]))
+    print(find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(0,0),(1,1)],[(0.1,0.8),(0.4,0.1),(1,0.1)]]))
--- a/src/reportlab/graphics/renderPDF.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/renderPDF.py	Wed Mar 26 12:32:02 2014 +0000
@@ -18,9 +18,9 @@
 from reportlab.graphics.shapes import *
 from reportlab.pdfgen.canvas import Canvas
 from reportlab.pdfbase.pdfmetrics import stringWidth
-from reportlab.lib.utils import getStringIO
+from reportlab.lib.utils import getBytesIO
 from reportlab import rl_config
-from renderbase import Renderer, StateTracker, getStateDelta, renderScaledDrawing
+from reportlab.graphics.renderbase import Renderer, StateTracker, getStateDelta, renderScaledDrawing
 
 # the main entry point for users...
 def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
@@ -165,7 +165,7 @@
                 elif text_anchor=='numeric':
                     x -= numericXShift(text_anchor,text,textLen,font,font_size,enc)
                 else:
-                    raise ValueError, 'bad value for textAnchor '+str(text_anchor)
+                    raise ValueError('bad value for textAnchor '+str(text_anchor))
             t = self._canvas.beginText(x,y)
             t.textLine(text)
             self._canvas.drawText(t)
@@ -303,7 +303,7 @@
 
 def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1):
     "Returns a PDF as a string in memory, without touching the disk"
-    s = getStringIO()
+    s = getBytesIO()
     drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize)
     return s.getvalue()
 
@@ -313,9 +313,14 @@
 #   Routine to draw them comes at the end.
 #
 #########################################################
-def test():
+def test(outDir='pdfout',shout=False):
     from reportlab.graphics.shapes import _baseGFontName, _baseGFontNameBI
-    c = Canvas('renderPDF.pdf')
+    from reportlab.rl_config import verbose
+    import os
+    if not os.path.isdir(outDir):
+        os.mkdir(outDir)
+    fn = os.path.join(outDir,'renderPDF.pdf')
+    c = Canvas(fn)
     c.setFont(_baseGFontName, 36)
     c.drawString(80, 750, 'Graphics Test')
 
@@ -356,7 +361,8 @@
     if y!=740: c.showPage()
 
     c.save()
-    print 'saved renderPDF.pdf'
+    if shout or verbose>2:
+        print('saved %s' % ascii(fn))
 
 ##def testFlowable():
 ##    """Makes a platypus document"""
@@ -385,5 +391,11 @@
 ##    print 'saves test_flowable.pdf'
 
 if __name__=='__main__':
-    test()
+    test(shout=True)
+    import sys
+    if len(sys.argv)>1:
+        outdir = sys.argv[1]
+    else:
+        outdir = 'pdfout'
+    test(outdir,shout=True)
     #testFlowable()
--- a/src/reportlab/graphics/renderPM.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/renderPM.py	Wed Mar 26 12:32:02 2014 +0000
@@ -16,20 +16,20 @@
 from reportlab.graphics.renderbase import StateTracker, getStateDelta, renderScaledDrawing
 from reportlab.pdfbase.pdfmetrics import getFont, unicode2T1
 from math import sin, cos, pi, ceil
-from reportlab.lib.utils import getStringIO, open_and_read
+from reportlab.lib.utils import getStringIO, getBytesIO, open_and_read, isUnicode
 from reportlab import rl_config
 
 class RenderPMError(Exception):
     pass
 
-import string, os, sys
+import os, sys
 
 try:
-    import _renderPM
-except ImportError, errMsg:
-    raise ImportError, "No module named _renderPM\n" + \
+    from reportlab.graphics import _renderPM
+except ImportError as 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 https://www.reportlab.com/software/opensource/rl-addons/")
+                                    "see https://www.reportlab.com/software/opensource/rl-addons/"))
 
 def _getImage():
     try:
@@ -124,7 +124,7 @@
 
     def drawImage(self, image):
         path = image.path
-        if isinstance(path,basestring):
+        if isinstance(path,str):
             if not (path and os.path.isfile(path)): return
             im = _getImage().open(path).convert('RGB')
         elif hasattr(path,'convert'):
@@ -186,7 +186,7 @@
                 elif text_anchor=='numeric':
                     x -= numericXShift(text_anchor,text,textLen,fontName,fontSize,stringObj.encoding)
                 else:
-                    raise ValueError, 'bad value for textAnchor '+str(text_anchor)
+                    raise ValueError('bad value for textAnchor '+str(text_anchor))
             canv.drawString(x,y,text,_fontInfo=(fontName,fontSize))
 
     def drawPath(self, path):
@@ -216,18 +216,15 @@
 def _setFont(gs,fontName,fontSize):
     try:
         gs.setFont(fontName,fontSize)
-    except _renderPM.Error, errMsg:
-        if errMsg.args[0]!="Can't find font!": raise
+    except ValueError as e:
+        if not e.args[0].endswith("Can't find font!"): raise
         #here's where we try to add a font to the canvas
         try:
             f = getFont(fontName)
-            if _renderPM._version<='0.98':  #added reader arg in 0.99
-                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector)
-            else:
-                _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
+            _renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
         except:
-            s1, s2 = map(str,sys.exc_info()[:2])
-            raise RenderPMError, "Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2)
+            s1, s2 = list(map(str,sys.exc_info()[:2]))
+            raise RenderPMError("Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2))
         gs.setFont(fontName,fontSize)
 
 def _convert2pilp(im):
@@ -246,7 +243,7 @@
     #s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette(),transparent is not None and Color2Hex(transparent) or -1)
     s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette())
     if not hasattr(fn,'write'):
-        open(os.path.splitext(fn)[0]+'.'+string.lower(fmt),'wb').write(s)
+        open(os.path.splitext(fn)[0]+'.'+fmt.lower(),'wb').write(s)
         if os.name=='mac':
             from reportlab.lib.utils import markfilename
             markfilename(fn,ext='PICT')
@@ -290,8 +287,8 @@
     def saveToFile(self,fn,fmt=None):
         im = self.toPIL()
         if fmt is None:
-            if type(fn) is not StringType:
-                raise ValueError, "Invalid type '%s' for fn when fmt is None" % type(fn)
+            if not isinstance(fn,str):
+                raise ValueError("Invalid value '%s' for fn when fmt is None" % ascii(fn))
             fmt = os.path.splitext(fn)[1]
             if fmt.startswith('.'): fmt = fmt[1:]
         configPIL = self.configPIL or {}
@@ -299,7 +296,7 @@
         preConvertCB=configPIL.pop('preConvertCB')
         if preConvertCB:
             im = preConvertCB(im)
-        fmt = string.upper(fmt)
+        fmt = fmt.upper()
         if fmt in ('GIF',):
             im = _convert2pilp(im)
         elif fmt in ('TIFF','TIFFP','TIFFL','TIF','TIFF1'):
@@ -328,7 +325,7 @@
         elif fmt in ('GIF',):
             pass
         else:
-            raise RenderPMError,"Unknown image kind %s" % fmt
+            raise RenderPMError("Unknown image kind %s" % fmt)
         if fmt=='TIFF':
             tc = configPIL.get('transparent',None)
             if tc:
@@ -336,9 +333,9 @@
                 T = 768*[0]
                 for o, c in zip((0,256,512), tc.bitmap_rgb()):
                     T[o+c] = 255
-                #if type(fn) is type(''): ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])).save(fn+'_mask.gif','GIF')
+                #if isinstance(fn,str): ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])).save(fn+'_mask.gif','GIF')
                 im = Image.merge('RGBA', im.split()+(ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])),))
-                #if type(fn) is type(''): im.save(fn+'_masked.gif','GIF')
+                #if isinstance(fn,str): im.save(fn+'_masked.gif','GIF')
             for a,d in ('resolution',self._dpi),('resolution unit','inch'):
                 configPIL[a] = configPIL.get(a,d)
         configPIL.setdefault('chops_invert',0)
@@ -355,7 +352,7 @@
             markfilename(fn,ext=fmt)
 
     def saveToString(self,fmt='GIF'):
-        s = getStringIO()
+        s = getBytesIO()
         self.saveToFile(s,fmt=fmt)
         return s.getvalue()
 
@@ -489,29 +486,27 @@
             gfont = None
         font = getFont(fontName)
         if font._dynamicFont:
-            if isinstance(text,unicode): text = text.encode('utf8')
             gs.drawString(x,y,text)
         else:
             fc = font
-            if not isinstance(text,unicode):
+            if not isUnicode(text):
                 try:
                     text = text.decode('utf8')
-                except UnicodeDecodeError,e:
+                except UnicodeDecodeError as e:
                     i,j = e.args[2:4]
                     raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],text[i-10:i],text[i:j],text[j:j+10]),)))
 
             FT = unicode2T1(text,[font]+font.substitutionFonts)
             n = len(FT)
             nm1 = n-1
-            wscale = 0.001*fontSize
-            for i in xrange(n):
+            for i in range(n):
                 f, t = FT[i]
                 if f!=fc:
                     _setFont(gs,f.fontName,fontSize)
                     fc = f
                 gs.drawString(x,y,t)
                 if i!=nm1:
-                    x += wscale*sum(map(f.widths.__getitem__,map(ord,t)))
+                    x += f.stringWidth(t.decode(f.encName),fontSize)
             if font!=fc:
                 _setFont(gs,fontName,fontSize)
 
@@ -661,13 +656,13 @@
     c.saveToFile(fn,fmt)
 
 def drawToString(d,fmt='GIF', dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
-    s = getStringIO()
+    s = getBytesIO()
     drawToFile(d,s,fmt=fmt, dpi=dpi, bg=bg, configPIL=configPIL)
     return s.getvalue()
 
 save = drawToFile
 
-def test(verbose=True):
+def test(outDir='pmout', shout=False):
     def ext(x):
         if x=='tiff': x='tif'
         return x
@@ -675,10 +670,11 @@
     #make a page of links in HTML to assist viewing.
     import os
     from reportlab.graphics import testshapes
+    from reportlab.rl_config import verbose
     getAllTestDrawings = testshapes.getAllTestDrawings
     drawings = []
-    if not os.path.isdir('pmout'):
-        os.mkdir('pmout')
+    if not os.path.isdir(outDir):
+        os.mkdir(outDir)
     htmlTop = """<html><head><title>renderPM output results</title></head>
     <body>
     <h1>renderPM results of output</h1>
@@ -698,13 +694,13 @@
         E = (','.join([a[6:] for a in E])).split(',')
 
     errs = []
-    import StringIO, traceback
+    import traceback
     from xml.sax.saxutils import escape
     def handleError(name,fmt):
         msg = 'Problem drawing %s fmt=%s file'%(name,fmt)
-        print msg
+        if shout or verbose>2: print(msg)
         errs.append('<br/><h2 style="color:red">%s</h2>' % msg)
-        buf = StringIO.StringIO()
+        buf = getStringIO()
         traceback.print_exc(file=buf)
         errs.append('<pre>%s</pre>' % escape(buf.getvalue()))
 
@@ -720,17 +716,17 @@
 
         for k in E:
             if k in ['gif','png','jpg','pct']:
-                html.append('<p>%s format</p>\n' % string.upper(k))
+                html.append('<p>%s format</p>\n' % k.upper())
             try:
                 filename = '%s.%s' % (fnRoot, ext(k))
-                fullpath = os.path.join('pmout', filename)
+                fullpath = os.path.join(outDir, 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)
+                    drawing.save(formats=['py','svg'],outDir=outDir,fnRoot=fnRoot)
                 else:
                     drawToFile(drawing,fullpath,fmt=k)
                 if k in ['gif','png','jpg']:
@@ -739,13 +735,13 @@
                     html.append('<a href="%s">python source</a><br>\n' % filename)
                 elif k=='svg':
                     html.append('<a href="%s">SVG</a><br>\n' % filename)
-                if verbose: print 'wrote',fullpath
+                if shout or verbose>2: print('wrote %s'%ascii(fullpath))
             except AttributeError:
                 handleError(name,k)
         if os.environ.get('RL_NOEPSPREVIEW','0')=='1': drawing.__dict__['preview'] = 0
         for k in ('eps', 'pdf'):
             try:
-                drawing.save(formats=[k],outDir='pmout',fnRoot=fnRoot)
+                drawing.save(formats=[k],outDir=outDir,fnRoot=fnRoot)
             except:
                 handleError(name,k)
 
@@ -754,12 +750,12 @@
         html.append('<a name="errors"/>')
         html.extend(errs)
     html.append(htmlBottom)
-    htmlFileName = os.path.join('pmout', 'index.html')
+    htmlFileName = os.path.join(outDir, 'pm-index.html')
     open(htmlFileName, 'w').writelines(html)
     if sys.platform=='mac':
         from reportlab.lib.utils import markfilename
         markfilename(htmlFileName,ext='HTML')
-    if verbose: print 'wrote %s' % htmlFileName
+    if shout or verbose>2: print('wrote %s' % htmlFileName)
 
 if __name__=='__main__':
-    test()
+    test(shout=True)
--- a/src/reportlab/graphics/renderPS.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/renderPS.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,32 +4,32 @@
 __version__=''' $Id$ '''
 __doc__="""Render drawing objects in Postscript"""
 
-import string, types
 from reportlab.pdfbase.pdfmetrics import getFont, stringWidth, unicode2T1 # for font info
-from reportlab.lib.utils import fp_str, getStringIO
+from reportlab.lib.utils import getBytesIO, getStringIO, asBytes, char2int, rawBytes, asNative, isUnicode
+from reportlab.lib.rl_accel import fp_str
 from reportlab.lib.colors import black
 from reportlab.graphics.renderbase import Renderer, StateTracker, getStateDelta, renderScaledDrawing
 from reportlab.graphics.shapes import STATE_DEFAULTS
 import math
-from types import StringType
 from operator import getitem
 from reportlab import rl_config
 _ESCAPEDICT={}
 for c in xrange(256):
     if c<32 or c>=127:
-        _ESCAPEDICT[chr(c)]= '\\%03o' % c
+        _ESCAPEDICT[c]= '\\%03o' % c
     elif c in (ord('\\'),ord('('),ord(')')):
-        _ESCAPEDICT[chr(c)] = '\\'+chr(c)
+        _ESCAPEDICT[c] = '\\'+chr(c)
     else:
-        _ESCAPEDICT[chr(c)] = chr(c)
-    del c
+        _ESCAPEDICT[c] = chr(c)
+del c
 
 def _escape_and_limit(s):
+    s = asBytes(s)
     R = []
     aR = R.append
     n = 0
     for c in s:
-        c = _ESCAPEDICT[c]
+        c = _ESCAPEDICT[char2int(c)]
         aR(c)
         n += len(c)
         if n>=200:
@@ -37,53 +37,53 @@
             aR('\\\n')
     return ''.join(R)
 
-# we need to create encoding vectors for each font we use, or they will      
- # come out in Adobe's old StandardEncoding, which NOBODY uses.      
-PS_WinAnsiEncoding="""       
-/RE { %def       
-  findfont begin     
-  currentdict dup length dict begin  
- { %forall   
-   1 index /FID ne { def } { pop pop } ifelse    
- } forall    
- /FontName exch def dup length 0 ne { %if    
-   /Encoding Encoding 256 array copy def     
-   0 exch { %forall      
-     dup type /nametype eq { %ifelse     
-       Encoding 2 index 2 index put      
-       pop 1 add     
-     }{ %else    
-       exch pop      
-     } ifelse    
-   } forall      
- } if pop    
-  currentdict dup end end    
-  /FontName get exch definefont pop  
-} bind def       
- 
-/WinAnsiEncoding [       
-  39/quotesingle 96/grave 128/euro 130/quotesinglbase/florin/quotedblbase    
-  /ellipsis/dagger/daggerdbl/circumflex/perthousand  
-  /Scaron/guilsinglleft/OE 145/quoteleft/quoteright  
-  /quotedblleft/quotedblright/bullet/endash/emdash       
-  /tilde/trademark/scaron/guilsinglright/oe/dotlessi     
-  159/Ydieresis 164/currency 166/brokenbar 168/dieresis/copyright    
-  /ordfeminine 172/logicalnot 174/registered/macron/ring     
-  177/plusminus/twosuperior/threesuperior/acute/mu       
-  183/periodcentered/cedilla/onesuperior/ordmasculine    
-  188/onequarter/onehalf/threequarters 192/Agrave/Aacute     
-  /Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla    
-  /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute     
-  /Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute    
-  /Ocircumflex/Otilde/Odieresis/multiply/Oslash  
-  /Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn  
-  /germandbls/agrave/aacute/acircumflex/atilde/adieresis     
-  /aring/ae/ccedilla/egrave/eacute/ecircumflex       
-  /edieresis/igrave/iacute/icircumflex/idieresis     
-  /eth/ntilde/ograve/oacute/ocircumflex/otilde       
-  /odieresis/divide/oslash/ugrave/uacute/ucircumflex     
-  /udieresis/yacute/thorn/ydieresis  
-] def    
+# we need to create encoding vectors for each font we use, or they will
+ # come out in Adobe's old StandardEncoding, which NOBODY uses.
+PS_WinAnsiEncoding="""
+/RE { %def
+  findfont begin
+  currentdict dup length dict begin
+ { %forall
+   1 index /FID ne { def } { pop pop } ifelse
+ } forall
+ /FontName exch def dup length 0 ne { %if
+   /Encoding Encoding 256 array copy def
+   0 exch { %forall
+     dup type /nametype eq { %ifelse
+       Encoding 2 index 2 index put
+       pop 1 add
+     }{ %else
+       exch pop
+     } ifelse
+   } forall
+ } if pop
+  currentdict dup end end
+  /FontName get exch definefont pop
+} bind def
+
+/WinAnsiEncoding [
+  39/quotesingle 96/grave 128/euro 130/quotesinglbase/florin/quotedblbase
+  /ellipsis/dagger/daggerdbl/circumflex/perthousand
+  /Scaron/guilsinglleft/OE 145/quoteleft/quoteright
+  /quotedblleft/quotedblright/bullet/endash/emdash
+  /tilde/trademark/scaron/guilsinglright/oe/dotlessi
+  159/Ydieresis 164/currency 166/brokenbar 168/dieresis/copyright
+  /ordfeminine 172/logicalnot 174/registered/macron/ring
+  177/plusminus/twosuperior/threesuperior/acute/mu
+  183/periodcentered/cedilla/onesuperior/ordmasculine
+  188/onequarter/onehalf/threequarters 192/Agrave/Aacute
+  /Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
+  /Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
+  /Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute
+  /Ocircumflex/Otilde/Odieresis/multiply/Oslash
+  /Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn
+  /germandbls/agrave/aacute/acircumflex/atilde/adieresis
+  /aring/ae/ccedilla/egrave/eacute/ecircumflex
+  /edieresis/igrave/iacute/icircumflex/idieresis
+  /eth/ntilde/ograve/oacute/ocircumflex/otilde
+  /odieresis/divide/oslash/ugrave/uacute/ucircumflex
+  /udieresis/yacute/thorn/ydieresis
+] def
 """
 
 class PSCanvas:
@@ -125,21 +125,21 @@
 
     def _t1_re_encode(self):
         if not self._fontsUsed: return
-        # for each font used, reencode the vectors   
+        # for each font used, reencode the vectors
         C = []
-        for fontName in self._fontsUsed:     
+        for fontName in self._fontsUsed:
             fontObj = getFont(fontName)
             if not fontObj._dynamicFont and fontObj.encName=='WinAnsiEncoding':
-                C.append('WinAnsiEncoding /%s /%s RE' % (fontName, fontName))    
+                C.append('WinAnsiEncoding /%s /%s RE' % (fontName, fontName))
         if C:
             C.insert(0,PS_WinAnsiEncoding)
-            self.code.insert(1, string.join(C, self._sep))
+            self.code.insert(1, self._sep.join(C))
 
     def save(self,f=None):
         if not hasattr(f,'write'):
-            file = open(f,'wb')
+            _f = open(f,'wb')
         else:
-            file = f
+            _f = f
         if self.code[-1]!='showpage': self.clear()
         self.code.insert(0,'''\
 %%!PS-Adobe-3.0 EPSF-3.0
@@ -151,9 +151,9 @@
 ''' % (self.width,self.height))
 
         self._t1_re_encode()
-        file.write(string.join(self.code,self._sep))
-        if file is not f:
-            file.close()
+        _f.write(rawBytes(self._sep.join(self.code)))
+        if _f is not f:
+            _f.close()
             from reportlab.lib.utils import markfilename
             markfilename(f,creatorcode='XPR3',filetype='EPSF')
 
@@ -190,7 +190,7 @@
             self.code_append('[%s %s] 0 %s' % (array, phase, psoperation))
         elif isinstance(array,(tuple,list)):
             assert phase >= 0, "phase is a length in user space"
-            textarray = string.join(map(str, array))
+            textarray = ' '.join(map(str, array))
             self.code_append('[%s] %s %s' % (textarray, phase, psoperation))
 
     def setStrokeColor(self, color):
@@ -235,7 +235,7 @@
         try:
             return _escape_and_limit(s)
         except:
-            raise ValueError("cannot escape %s %s" % (s, repr(s)))
+            raise ValueError("cannot escape %s" % ascii(s))
 
     def _issueT1String(self,fontObj,x,y,s):
         fc = fontObj
@@ -243,16 +243,16 @@
         fontSize = self._fontSize
         fontsUsed = self._fontsUsed
         escape = self._escape
-        if not isinstance(s,unicode):
+        if not isUnicode(s):
             try:
                 s = s.decode('utf8')
-            except UnicodeDecodeError,e:
+            except UnicodeDecodeError as e:
                 i,j = e.args[2:4]
                 raise UnicodeDecodeError(*(e.args[:4]+('%s\n%s-->%s<--%s' % (e.args[4],s[i-10:i],s[i:j],s[j:j+10]),)))
 
         for f, t in unicode2T1(s,[fontObj]+fontObj.substitutionFonts):
             if f!=fc:
-                psName = f.face.name
+                psName = asNative(f.face.name)
                 code_append('(%s) findfont %s scalefont setfont' % (psName,fp_str(fontSize)))
                 if psName not in fontsUsed:
                     fontsUsed.append(psName)
@@ -267,7 +267,7 @@
         if self._fillColor != None:
             fontObj = getFont(self._font)
             if not self.code[self._fontCodeLoc]:
-                psName = fontObj.face.name
+                psName = asNative(fontObj.face.name)
                 self.code[self._fontCodeLoc]='(%s) findfont %s scalefont setfont' % (psName,fp_str(self._fontSize))
                 if psName not in self._fontsUsed:
                     self._fontsUsed.append(psName)
@@ -491,7 +491,7 @@
                     a("%s l" % fp_str(args[:2]))
                 a("%s curveto" % fp_str(args[2:]))
             else:
-                raise TypeError, "unknown figure operator: "+op
+                raise TypeError("unknown figure operator: "+op)
 
         if closed:
             a("closepath")
@@ -602,8 +602,8 @@
     def _AsciiHexEncode(self, input):  # also based on piddlePDF
         "Helper function used by images"
         output = getStringIO()
-        for char in input:
-            output.write('%02x' % ord(char))
+        for char in asBytes(input):
+            output.write('%02x' % char2int(char))
         return output.getvalue()
 
     def _drawImageLevel2(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version
@@ -673,7 +673,7 @@
     from reportlab.graphics import renderPS
     renderPS.draw(drawing, canvas, x, y)
 Execute the script to see some test drawings."""
-from shapes import *
+from reportlab.graphics.shapes import *
 
 # hack so we only get warnings once each
 #warnOnce = WarnOnce()
@@ -691,7 +691,7 @@
     '''
     P=[]
     a = P.append
-    for i in xrange(0,len(L),2):
+    for i in range(0,len(L),2):
         a((L[i],L[i+1]))
     return P
 
@@ -801,7 +801,7 @@
                 elif text_anchor=='numeric':
                     x -= numericXShift(text_anchor,text,textLen,font,fontSize,encoding='winansi')
                 else:
-                    raise ValueError, 'bad value for text_anchor '+str(text_anchor)
+                    raise ValueError('bad value for text_anchor '+str(text_anchor))
             self._canvas.drawString(x,y,text)
 
     def drawPath(self, path):
@@ -879,7 +879,7 @@
 
 def drawToString(d, showBoundary=rl_config.showBoundary):
     "Returns a PS as a string in memory, without touching the disk"
-    s = getStringIO()
+    s = getBytesIO()
     drawToFile(d, s, showBoundary=showBoundary)
     return s.getvalue()
 
@@ -889,30 +889,33 @@
 #   Routine to draw them comes at the end.
 #
 #########################################################
-def test(outdir='epsout'):
-    import os
-    # print all drawings and their doc strings from the test
-    # file
-    if not os.path.isdir(outdir):
-        os.mkdir(outdir)
-    #grab all drawings from the test module
-    import testshapes
-    drawings = []
+def test(outDir='epsout',shout=False):
+    from reportlab.graphics import testshapes
+    from reportlab.rl_config import verbose
+    OLDFONTS = testshapes._FONTS[:]
+    testshapes._FONTS[:] = ['Times-Roman','Times-Bold','Times-Italic', 'Times-BoldItalic','Courier']
+    try:
+        import os
+        # save all drawings and their doc strings from the test file
+        if not os.path.isdir(outDir):
+            os.mkdir(outDir)
+        #grab all drawings from the test module
+        drawings = []
 
-    for funcname in dir(testshapes):
-        #if funcname[0:11] == 'getDrawing2':
-        #    print 'hacked to only show drawing 2'
-        if funcname[0:10] == 'getDrawing':
-            drawing = eval('testshapes.' + funcname + '()')  #execute it
-            docstring = eval('testshapes.' + funcname + '.__doc__')
-            drawings.append((drawing, docstring))
+        for funcname in dir(testshapes):
+            if funcname[0:10] == 'getDrawing':
+                drawing = eval('testshapes.' + funcname + '()')  #execute it
+                docstring = eval('testshapes.' + funcname + '.__doc__')
+                drawings.append((drawing, docstring))
 
-    i = 0
-    for (d, docstring) in drawings:
-        filename = outdir + os.sep + 'renderPS_%d.eps'%i
-        drawToFile(d,filename)
-        print 'saved', filename
-        i = i + 1
+        i = 0
+        for (d, docstring) in drawings:
+            filename = outDir + os.sep + 'renderPS_%d.eps'%i
+            drawToFile(d,filename)
+            if shout or verbose>2: print('renderPS test saved %s' % ascii(filename))
+            i += 1
+    finally:
+        testshapes._FONTS[:] = OLDFONTS
 
 if __name__=='__main__':
     import sys
@@ -920,4 +923,4 @@
         outdir = sys.argv[1]
     else:
         outdir = 'epsout'
-    test(outdir)
+    test(outdir,shout=True)
--- a/src/reportlab/graphics/renderSVG.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/renderSVG.py	Wed Mar 26 12:32:02 2014 +0000
@@ -11,13 +11,14 @@
 from operator import getitem
 
 from reportlab.pdfbase.pdfmetrics import stringWidth # for font info
-from reportlab.lib.utils import fp_str
+from reportlab.lib.rl_accel import fp_str
 from reportlab.lib.colors import black
+from reportlab.lib.utils import asNative
 from reportlab.graphics.renderbase import StateTracker, getStateDelta, Renderer, renderScaledDrawing
 from reportlab.graphics.shapes import STATE_DEFAULTS, Path, UserNode
 from reportlab.graphics.shapes import * # (only for test0)
 from reportlab import rl_config
-from reportlab.lib.utils import getStringIO, RLString
+from reportlab.lib.utils import getBytesIO, RLString, isPy3, isUnicode, isBytes
 
 from xml.dom import getDOMImplementation
 
@@ -34,7 +35,7 @@
 ### top-level user function ###
 def drawToString(d, showBoundary=rl_config.showBoundary,**kwds):
     "Returns a SVG as a string in memory, without touching the disk"
-    s = getStringIO()
+    s = getBytesIO()
     drawToFile(d, s, showBoundary=showBoundary,**kwds)
     return s.getvalue()
 
@@ -96,8 +97,8 @@
 class EncodedWriter(list):
     '''
     EncodedWriter(encoding) assumes .write will be called with
-    either unicode or utf8 encoded strings.  it will accumulate
-    strings encoded as the specified encoding.
+    either unicode or utf8 encoded bytes. it will accumulate
+    unicode
     '''
     BOMS =  {
         'utf-32':codecs.BOM_UTF32,
@@ -113,22 +114,18 @@
         if bom and '16' in encoding or '32' in encoding:
             self.write(self.BOMS[encoding])
 
-    def write(self,s):
-        if isinstance(s,unicode):
-            s = s.encode(self.encoding)
-        elif isinstance(s,str):
+    def write(self,u):
+        if isBytes(u):
             try:
-                 u = s.decode('utf-8')
+                 u = u.decode('utf-8')
             except:
                 et, ev, tb = sys.exc_info()
                 ev = str(ev)
                 del et, tb
-                raise ValueError("String %r not encoded as 'utf-8'\nerror=%s" % (s,ev))
-            if self.encoding!='utf-8':
-                s = u.decode(self.encoding)
-        else:
-            raise ValueError("EncodedWriter.write(%r) argument should be 'utf-8' string or unicode" % s)
-        self.append(s)
+                raise ValueError("String %r not encoded as 'utf-8'\nerror=%s" % (u,ev))
+        elif not isUnicode(u):
+            raise ValueError("EncodedWriter.write(%s) argument should be 'utf-8' bytes or str" % ascii(u))
+        self.append(u)
 
     def getvalue(self):
         r = ''.join(self)
@@ -194,7 +191,7 @@
                     )
         svgAttrs["xmlns:xlink"] = "http://www.w3.org/1999/xlink"
         svgAttrs.update(kwds.pop('svgAttrs',{}))
-        for k,v in svgAttrs.iteritems():
+        for k,v in svgAttrs.items():
             self.svg.setAttribute(k,v)
 
         title = self.doc.createElement('title')
@@ -242,15 +239,19 @@
         writer = EncodedWriter(self.encoding,bom=self.bom)
         self.doc.writexml(writer,addindent="\t",newl="\n",encoding=self.encoding)
 
-        if type(fn) in types.StringTypes:
-            f = open(fn, 'w')
+        if hasattr(fn,'write'):
+            f = fn
         else:
-            f = fn
+            if isPy3:
+                f = open(fn, 'w',encoding=self.encoding)
+            else:
+                f = open(fn, 'w')
+
         svg = writer.getvalue()
         exd = self.extraXmlDecl
         if exd:
             svg = svg.replace('?>','?>'+exd)
-        f.write(svg)
+        f.write(svg if isPy3 else svg.encode(self.encoding))
         if f is not fn:
             f.close()
 
@@ -268,7 +269,7 @@
     def _formatStyle(self, include=[], exclude='',**kwds):
         style = self.style.copy()
         style.update(kwds)
-        keys = style.keys()
+        keys = list(style.keys())
         if include:
             keys = [k for k in keys if k in include]
         if exclude:
@@ -387,9 +388,9 @@
                     del style[k]
             svgAttrs = self.fontHacks[font] if font in self.fontHacks else {}
             if isinstance(font,RLString):
-                svgAttrs.update(font.svgAttrs.iteritems())
+                svgAttrs.update(iter(font.svgAttrs.items()))
             if svgAttrs:
-                for k,v in svgAttrs.iteritems():
+                for k,v in svgAttrs.items():
                     a = 'font-'+k
                     if a in TEXT_STYLES:
                         style[a] = v
@@ -407,7 +408,7 @@
     def rect(self, x1,y1, x2,y2, rx=8, ry=8, link_info=None, **_svgAttrs):
         "Draw a rectangle between x1,y1 and x2,y2."
 
-        if self.verbose: print "+++ SVGCanvas.rect"
+        if self.verbose: print("+++ SVGCanvas.rect")
 
         x = min(x1,x2)
         y = min(y1,y2)
@@ -439,7 +440,8 @@
         self.currGroup.appendChild(rect)
 
     def drawString(self, s, x, y, angle=0, link_info=None,**_svgAttrs):
-        if self.verbose: print "+++ SVGCanvas.drawString"
+        s = asNative(s)
+        if self.verbose: print("+++ SVGCanvas.drawString")
 
         if self._fillColor != None:
             self.setColor(self._fillColor)
@@ -462,7 +464,7 @@
             self.currGroup.appendChild(text)
 
     def drawCentredString(self, s, x, y, angle=0, text_anchor='middle', link_info=None):
-        if self.verbose: print "+++ SVGCanvas.drawCentredString"
+        if self.verbose: print("+++ SVGCanvas.drawCentredString")
 
         if self._fillColor != None:
             if not text_anchor in ['start', 'inherited']:
@@ -474,7 +476,7 @@
                 elif text_anchor=='numeric':
                     x -= numericXShift(text_anchor,s,textLen,self._font,self._fontSize)
                 else:
-                    raise ValueError, 'bad value for text_anchor ' + str(text_anchor)
+                    raise ValueError('bad value for text_anchor ' + str(text_anchor))
         self.drawString(x,y,text,angle=angle, link_info=link_info)
 
     def drawRightString(self, text, x, y, angle=0, link_info=None):
@@ -580,7 +582,7 @@
         if self._strokeColor != None:
             self.setColor(self._strokeColor)
             pairs = []
-            for i in xrange(len(points)):
+            for i in range(len(points)):
                 pairs.append("%f %f" % (points[i]))
             pts = ', '.join(pairs)
             polyline = transformNode(self.doc, "polygon",
@@ -609,7 +611,7 @@
         if self._strokeColor != None:
             self.setColor(self._strokeColor)
             pairs = []
-            for i in xrange(len(points)):
+            for i in range(len(points)):
                 pairs.append("%f %f" % (points[i]))
             pts = ', '.join(pairs)
             polyline = transformNode(self.doc, "polyline",
@@ -618,20 +620,20 @@
 
     ### groups ###
     def startGroup(self):
-        if self.verbose: print "+++ begin SVGCanvas.startGroup"
+        if self.verbose: print("+++ begin SVGCanvas.startGroup")
         currGroup, group = self.currGroup, transformNode(self.doc, "g", transform="")
         currGroup.appendChild(group)
         self.currGroup = group
-        if self.verbose: print "+++ end SVGCanvas.startGroup"
+        if self.verbose: print("+++ end SVGCanvas.startGroup")
         return currGroup
 
     def endGroup(self,currGroup):
-        if self.verbose: print "+++ begin SVGCanvas.endGroup"
+        if self.verbose: print("+++ begin SVGCanvas.endGroup")
         self.currGroup = currGroup
-        if self.verbose: print "+++ end SVGCanvas.endGroup"
+        if self.verbose: print("+++ end SVGCanvas.endGroup")
 
     def transform(self, a, b, c, d, e, f):
-        if self.verbose: print "!!! begin SVGCanvas.transform", a, b, c, d, e, f
+        if self.verbose: print("!!! begin SVGCanvas.transform", a, b, c, d, e, f)
         tr = self.currGroup.getAttribute("transform")
         t = 'matrix(%f, %f, %f, %f, %f, %f)' % (a,b,c,d,e,f)
         if (a, b, c, d, e, f) != (1, 0, 0, 1, 0, 0):
@@ -639,7 +641,7 @@
 
     def translate(self, x, y):
         # probably never used
-        print "!!! begin SVGCanvas.translate"
+        print("!!! begin SVGCanvas.translate")
         return
 
         tr = self.currGroup.getAttribute("transform")
@@ -648,7 +650,7 @@
 
     def scale(self, x, y):
         # probably never used
-        print "!!! begin SVGCanvas.scale"
+        print("!!! begin SVGCanvas.scale")
         return
 
         tr = self.groups[-1].getAttribute("transform")
@@ -686,7 +688,7 @@
         """This is the recursive method called for each node in the tree.
         """
 
-        if self.verbose: print "### begin _SVGRenderer.drawNode(%r)" % node
+        if self.verbose: print("### begin _SVGRenderer.drawNode(%r)" % node)
 
         self._canvas.comment('begin node %r'%node)
         color = self._canvas._color
@@ -714,7 +716,7 @@
                 setattr(self._canvas,self._restores[k],v)
         self._canvas.style = style
 
-        if self.verbose: print "### end _SVGRenderer.drawNode(%r)" % node
+        if self.verbose: print("### end _SVGRenderer.drawNode(%r)" % node)
 
     _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap',
                 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font',
@@ -752,7 +754,7 @@
             return None
 
     def drawGroup(self, group):
-        if self.verbose: print "### begin _SVGRenderer.drawGroup"
+        if self.verbose: print("### begin _SVGRenderer.drawGroup")
 
         currGroup = self._canvas.startGroup()
         a, b, c, d, e, f = self._tracker.getState()['transform']
@@ -765,7 +767,7 @@
         self._canvas.transform(a, b, c, d, e, f)
         self._canvas.endGroup(currGroup)
 
-        if self.verbose: print "### end _SVGRenderer.drawGroup"
+        if self.verbose: print("### end _SVGRenderer.drawGroup")
 
     def drawRect(self, rect):
         link_info = self._get_link_info_dict(rect)
@@ -798,7 +800,7 @@
                 elif text_anchor=='numeric':
                     x -= numericXShift(text_anchor,text,textLen,font,fontSize)
                 else:
-                    raise ValueError, 'bad value for text_anchor ' + str(text_anchor)
+                    raise ValueError('bad value for text_anchor ' + str(text_anchor))
             self._canvas.drawString(text,x,y,link_info=self._get_link_info_dict(stringObj),**getattr(stringObj,'_svgAttrs',{}))
 
     def drawLine(self, line):
@@ -879,11 +881,11 @@
                 fontsize = delta.get('fontSize', self._canvas._fontSize)
                 self._canvas.setFont(fontname, fontsize)
 
-def test0(outdir='svgout'):
+def test(outDir='out-svg'):
     # print all drawings and their doc strings from the test
     # file
-    if not os.path.isdir(outdir):
-        os.mkdir(outdir)
+    if not os.path.isdir(outDir):
+        os.mkdir(outDir)
     #grab all drawings from the test module
     from reportlab.graphics import testshapes
     drawings = []
@@ -896,30 +898,24 @@
             docstring = eval('testshapes.' + funcname + '.__doc__')
             drawings.append((drawing, docstring))
 
-    # return
 
     i = 0
     for (d, docstring) in drawings:
-        filename = outdir + os.sep + 'renderSVG_%d.svg' % i
+        filename = os.path.join(outDir,'renderSVG_%d.svg' % i)
         drawToFile(d, filename)
-        # print 'saved', filename
         i += 1
 
-def test1():
     from reportlab.graphics.testshapes import getDrawing01
     d = getDrawing01()
-    drawToFile(d, "svgout/test.svg")
+    drawToFile(d, os.path.join(outDir,"test.svg"))
 
-def test2():
     from reportlab.lib.corp import RL_CorpLogo
     from reportlab.graphics.shapes import Drawing
 
     rl = RL_CorpLogo()
     d = Drawing(rl.width,rl.height)
     d.add(rl)
-    drawToFile(d, "svgout/corplogo.svg")
+    drawToFile(d, os.path.join(outDir,"corplogo.svg"))
 
 if __name__=='__main__':
-    test0()
-    test1()
-    test2()
+    test()
--- a/src/reportlab/graphics/renderbase.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/renderbase.py	Wed Mar 26 12:32:02 2014 +0000
@@ -37,7 +37,7 @@
     to set the pen color to red in between. Returns the effect
     the given shape would have on the graphics state"""
     delta = {}
-    for (prop, value) in shape.getProperties().items():
+    for prop, value in shape.getProperties().items():
         if prop in STATE_DEFAULTS:
             delta[prop] = value
     return delta
@@ -72,7 +72,7 @@
         through getState()"""
 
         newstate = self._combined[-1].copy()
-        for (key, value) in delta.items():
+        for key, value in delta.items():
             if key == 'transform':  #do cumulative matrix
                 newstate['transform'] = delta['transform']
                 newstate['ctm'] = mmult(self._combined[-1]['ctm'], delta['transform'])
@@ -125,7 +125,7 @@
         self._combined[-1][key] = value
 
 def testStateTracker():
-    print 'Testing state tracker'
+    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 = [
@@ -137,16 +137,16 @@
         ]
 
     st = StateTracker(defaults)
-    print 'initial:', st.getState()
-    print
+    print('initial:', st.getState())
+    print()
     for delta in deltas:
-        print 'pushing:', delta
+        print('pushing:', delta)
         st.push(delta)
-        print 'state:  ',st.getState(),'\n'
+        print('state:  ',st.getState(),'\n')
 
     for delta in deltas:
-        print 'popping:',st.pop()
-        print 'state:  ',st.getState(),'\n'
+        print('popping:',st.pop())
+        print('state:  ',st.getState(),'\n')
 
 
 def _expandUserNode(node,canvas):
@@ -181,7 +181,7 @@
         self._nodeStack = []   #track nodes visited
 
     def undefined(self, operation):
-        raise ValueError, "%s operation not defined at superclass class=%s" %(operation, self.__class__)
+        raise ValueError("%s operation not defined at superclass class=%s" %(operation, self.__class__))
 
     def draw(self, drawing, canvas, x=0, y=0, showBoundary=rl_config._unset_):
         """This is the top level function, which draws the drawing at the given
@@ -230,7 +230,7 @@
         parent.
         
         """
-        for (key, value) in node.__dict__.items():
+        for key, value in node.__dict__.items():
             if isinstance(value, DerivedValue):
                 #just replace with default for key?
                 #print '    fillDerivedValues(%s)' % key
@@ -281,7 +281,7 @@
             elif isinstance(node, Wedge):
                 self.drawWedge(node)
             else:
-                print 'DrawingError','Unexpected element %s in drawing!' % str(node)
+                print('DrawingError','Unexpected element %s in drawing!' % str(node))
         finally:
             if not ocanvas: del node._canvas
 
@@ -350,5 +350,5 @@
         self.undefined("applyStateChanges")
 
 if __name__=='__main__':
-    print "this file has no script interpretation"
-    print __doc__
+    print("this file has no script interpretation")
+    print(__doc__)
--- a/src/reportlab/graphics/samples/bubble.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/bubble.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.lineplots import ScatterPlot
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class Bubble(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/clustered_bar.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/clustered_bar.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,6 @@
 #Autogenerated by ReportLab guiedit do not edit
 from reportlab.graphics.charts.legends import Legend
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 from reportlab.graphics.charts.barcharts import HorizontalBarChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
--- a/src/reportlab/graphics/samples/clustered_column.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/clustered_column.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,6 @@
 #Autogenerated by ReportLab guiedit do not edit
 from reportlab.graphics.charts.legends import Legend
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 from reportlab.graphics.charts.barcharts import VerticalBarChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
--- a/src/reportlab/graphics/samples/exploded_pie.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/exploded_pie.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,6 @@
 #Autogenerated by ReportLab guiedit do not edit
 from reportlab.graphics.charts.piecharts import Pie
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 from reportlab.graphics.widgets.grids import ShadedRect
 from reportlab.graphics.charts.legends import Legend
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
--- a/src/reportlab/graphics/samples/filled_radar.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/filled_radar.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.spider import SpiderChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class FilledRadarChart(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/line_chart.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/line_chart.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.lineplots import LinePlot
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class LineChart(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/linechart_with_markers.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/linechart_with_markers.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,7 +4,7 @@
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.widgets.markers import makeMarker
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class LineChartWithMarkers(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/radar.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/radar.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,6 @@
 #Autogenerated by ReportLab guiedit do not edit
 from reportlab.graphics.charts.legends import Legend
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 from reportlab.graphics.charts.spider import SpiderChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
--- a/src/reportlab/graphics/samples/runall.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/runall.py	Wed Mar 26 12:32:02 2014 +0000
@@ -2,12 +2,11 @@
 # makes a PDF sample for eaxh existing chart type
 import sys
 import glob
-import string
 import inspect
 import types
 
 def moduleClasses(mod):
-    def P(obj, m=mod.__name__, CT=types.ClassType):
+    def P(obj, m=mod.__name__, CT=type):
         return (type(obj)==CT and obj.__module__==m)
     try:
         return inspect.getmembers(mod, P)[0][1]
@@ -18,24 +17,24 @@
     return moduleClasses(__import__(f))
 
 def run(format, VERBOSE=0):
-    formats = string.split(format, ',')
+    formats = format.split( ',')
     for i in range(0, len(formats)):
-        formats[i] == string.lower(string.strip(formats[i]))
+        formats[i] == formats[i].strip().lower()
     allfiles = glob.glob('*.py')
     allfiles.sort()
     for fn in allfiles:
-        f = string.split(fn, '.')[0]
+        f = fn.split('.')[0]
         c = getclass(f)
         if c != None:
-            print c.__name__
+            print(c.__name__)
             try:
                 for fmt in formats:
                     if fmt:
                         c().save(formats=[fmt],outDir='.',fnRoot=c.__name__)
                         if VERBOSE:
-                            print "  %s.%s" % (c.__name__, fmt)
+                            print("  %s.%s" % (c.__name__, fmt))
             except:
-                print "  COULDN'T CREATE '%s.%s'!" % (c.__name__, format)
+                print("  COULDN'T CREATE '%s.%s'!" % (c.__name__, format))
 
 if __name__ == "__main__":
     if len(sys.argv) == 1:
@@ -43,17 +42,17 @@
     else:
         try:
             if sys.argv[1] == "-h":
-                print 'usage: runall.py [FORMAT] [-h]'
-                print '   if format is supplied is should be one or more of pdf,gif,eps,png etc'
-                print '   if format is missing the following formats are assumed: pdf,pict,png'
-                print '   -h prints this message'
+                print('usage: runall.py [FORMAT] [-h]')
+                print('   if format is supplied is should be one or more of pdf,gif,eps,png etc')
+                print('   if format is missing the following formats are assumed: pdf,pict,png')
+                print('   -h prints this message')
             else:
                 t = sys.argv[1:]
                 for f in t:
                     run(f)
         except:
-            print 'usage: runall.py [FORMAT][-h]'
-            print '   if format is supplied is should be one or more of pdf,gif,eps,png etc'
-            print '   if format is missing the following formats are assumed: pdf,pict,png'
-            print '   -h prints this message'
+            print('usage: runall.py [FORMAT][-h]')
+            print('   if format is supplied is should be one or more of pdf,gif,eps,png etc')
+            print('   if format is missing the following formats are assumed: pdf,pict,png')
+            print('   -h prints this message')
             raise
--- a/src/reportlab/graphics/samples/scatter.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/scatter.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.lineplots import ScatterPlot
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class Scatter(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/scatter_lines.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/scatter_lines.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.lineplots import ScatterPlot
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class ScatterLines(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/scatter_lines_markers.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/scatter_lines_markers.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.lineplots import ScatterPlot
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class ScatterLinesMarkers(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/simple_pie.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/simple_pie.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,7 +4,7 @@
 from reportlab.graphics.charts.legends import Legend
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class SimplePie(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/stacked_bar.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/stacked_bar.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.barcharts import HorizontalBarChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class StackedBar(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/samples/stacked_column.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/samples/stacked_column.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,7 +3,7 @@
 from reportlab.graphics.charts.barcharts import VerticalBarChart
 from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
 from reportlab.graphics.charts.textlabels import Label
-from excelcolors import *
+from reportlab.graphics.samples.excelcolors import *
 
 class StackedColumn(_DrawingEditorMixin,Drawing):
     def __init__(self,width=200,height=150,*args,**kw):
--- a/src/reportlab/graphics/shapes.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/shapes.py	Wed Mar 26 12:32:02 2014 +0000
@@ -5,9 +5,8 @@
 __version__=''' $Id$ '''
 __doc__='''Core of the graphics library - defines Drawing and Shapes'''
 
-import string, os, sys
+import os, sys
 from math import pi, cos, sin, tan, sqrt
-from types import FloatType, IntType, ListType, TupleType, StringType, InstanceType
 from pprint import pprint
 
 from reportlab.platypus import Flowable
@@ -15,9 +14,10 @@
 from reportlab.lib import logger
 from reportlab.lib import colors
 from reportlab.lib.validators import *
+from reportlab.lib.utils import isSeq, asBytes
 isOpacity = NoneOr(isNumberInRange(0,1))
 from reportlab.lib.attrmap import *
-from reportlab.lib.utils import fp_str
+from reportlab.lib.rl_accel import fp_str
 from reportlab.pdfbase.pdfmetrics import stringWidth
 from reportlab.lib.fonts import tt2ps
 _baseGFontNameB = tt2ps(_baseGFontName,1,0)
@@ -120,10 +120,10 @@
     return (A[0]*v[0]+A[2]*v[1]+A[4],A[1]*v[0]+A[3]*v[1]+A[5])
 
 def transformPoints(matrix, V):
-    return map(transformPoint, V)
+    return list(map(transformPoint, V))
 
 def zTransformPoints(matrix, V):
-    return map(lambda x,matrix=matrix: zTransformPoint(matrix,x), V)
+    return list(map(lambda x,matrix=matrix: zTransformPoint(matrix,x), V))
 
 def _textBoxLimits(text, font, fontSize, leading, textAnchor, boxAnchor):
     w = 0
@@ -165,8 +165,8 @@
     return the corner points and the min max points in the original space
     '''
     C = zTransformPoints(rotate(angle),((x,y),(x+w,y),(x+w,y+h),(x,y+h)))
-    X = map(lambda x: x[0], C)
-    Y = map(lambda x: x[1], C)
+    X = [x[0] for x in C]
+    Y = [x[1] for x in C]
     return min(X), max(X), min(Y), max(Y), C
 
 
@@ -195,7 +195,7 @@
 
 def getRectsBounds(rectList):
     # filter out any None objects, e.g. empty groups
-    L = filter(lambda x: x is not None, rectList)
+    L = [x for x in rectList if x is not None]
     if not L: return None
 
     xMin, yMin, xMax, yMax = L[0]
@@ -266,16 +266,16 @@
 def getPathBounds(points):
     n = len(points)
     f = lambda i,p = points: p[i]
-    xs = map(f,xrange(0,n,2))
-    ys = map(f,xrange(1,n,2))
+    xs = list(map(f,range(0,n,2)))
+    ys = list(map(f,range(1,n,2)))
     return (min(xs), min(ys), max(xs), max(ys))
 
 def getPointsBounds(pointList):
     "Helper function for list of points"
     first = pointList[0]
-    if type(first) in (ListType, TupleType):
-        xs = map(lambda xy: xy[0],pointList)
-        ys = map(lambda xy: xy[1],pointList)
+    if isSeq(first):
+        xs = [xy[0] for xy in pointList]
+        ys = [xy[1] for xy in pointList]
         return (min(xs), min(ys), max(xs), max(ys))
     else:
         return getPathBounds(pointList)
@@ -298,7 +298,7 @@
         """Return a clone of this shape."""
 
         # implement this in the descendants as they need the right init methods.
-        raise NotImplementedError, "No copy method implemented for %s" % self.__class__.__name__
+        raise NotImplementedError("No copy method implemented for %s" % self.__class__.__name__)
 
     def getProperties(self,recur=1):
         """Interface to make it easy to extract automatic
@@ -325,12 +325,12 @@
         may provide a prefix - mostly helps to generate code
         samples for documentation."""
 
-        propList = self.getProperties().items()
+        propList = list(self.getProperties().items())
         propList.sort()
         if prefix:
             prefix = prefix + '.'
         for (name, value) in propList:
-            print '%s%s = %s' % (prefix, name, value)
+            print('%s%s = %s' % (prefix, name, value))
 
     def verify(self):
         """If the programmer has provided the optional
@@ -344,7 +344,7 @@
             for key in self.__dict__.keys():
                 if key[0] != '_':
                     assert key in self._attrMap, "Unexpected attribute %s found in %s" % (key, self)
-            for (attr, metavalue) in self._attrMap.items():
+            for attr, metavalue in self._attrMap.items():
                 assert hasattr(self, attr), "Missing attribute %s from %s" % (attr, self)
                 value = getattr(self, attr)
                 assert metavalue.validate(value), "Invalid value %s for attribute %s in class %s" % (value, attr, self.__class__.__name__)
@@ -396,7 +396,7 @@
     def _addNamedNode(self,name,node):
         'if name is not None add an attribute pointing to node and add to the attrMap'
         if name:
-            if name not in self._attrMap.keys():
+            if name not in list(self._attrMap.keys()):
                 self._attrMap[name] = AttrMapValue(isValidChild)
             setattr(self, name, node)
 
@@ -473,8 +473,8 @@
     def _copyNamedContents(self,obj,aKeys=None,noCopy=('contents',)):
         from copy import copy
         self_contents = self.contents
-        if not aKeys: aKeys = self._attrMap.keys()
-        for (k, v) in self.__dict__.items():
+        if not aKeys: aKeys = list(self._attrMap.keys())
+        for k, v in self.__dict__.items():
             if v in self_contents:
                 pos = self_contents.index(v)
                 setattr(obj, k, obj.contents[pos])
@@ -564,36 +564,33 @@
 
 def _repr(self,I=None):
     '''return a repr style string with named fixed args first, then keywords'''
-    if type(self) is InstanceType:
-        if self is EmptyClipPath:
-            _addObjImport(self,I,'EmptyClipPath')
-            return 'EmptyClipPath'
-        if I: _addObjImport(self,I)
-        if isinstance(self,Shape):
-            from inspect import getargs
-            args, varargs, varkw = getargs(self.__init__.im_func.func_code)
-            P = self.getProperties()
-            s = self.__class__.__name__+'('
-            for n in args[1:]:
-                v = P[n]
-                del P[n]
-                s = s + '%s,' % _repr(v,I)
-            for n,v in P.items():
-                v = P[n]
-                s = s + '%s=%s,' % (n, _repr(v,I))
-            return s[:-1]+')'
-        else:
-            return repr(self)
-    elif type(self) is FloatType:
+    if isinstance(self,float):
         return fp_str(self)
-    elif type(self) in (ListType,TupleType):
+    elif isSeq(self):
         s = ''
         for v in self:
             s = s + '%s,' % _repr(v,I)
-        if type(self) is ListType:
+        if isinstance(self,list):
             return '[%s]' % s[:-1]
         else:
             return '(%s%s)' % (s[:-1],len(self)==1 and ',' or '')
+    elif self is EmptyClipPath:
+        if I: _addObjImport(self,I,'EmptyClipPath')
+        return 'EmptyClipPath'
+    elif isinstance(self,Shape):
+        if I: _addObjImport(self,I)
+        from inspect import getargs
+        args, varargs, varkw = getargs(self.__init__.__func__.__code__)
+        P = self.getProperties()
+        s = self.__class__.__name__+'('
+        for n in args[1:]:
+            v = P[n]
+            del P[n]
+            s = s + '%s,' % _repr(v,I)
+        for n,v in P.items():
+            v = P[n]
+            s = s + '%s=%s,' % (n, _repr(v,I))
+        return s[:-1]+')'
     else:
         return repr(self)
 
@@ -662,7 +659,7 @@
         n = 'ExplodedDrawing_' + self.__class__.__name__
         s = '#Autogenerated by ReportLab guiedit do not edit\n'
         for m, o in I.items():
-            s = s + 'from %s import %s\n' % (m,string.replace(str(o)[1:-1],"'",""))
+            s = s + 'from %s import %s\n' % (m,str(o)[1:-1].replace("'",""))
         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\tDrawing.__init__(self,width,height,*args,**kw)\n'
@@ -674,7 +671,7 @@
         """This is used by the Platypus framework to let the document
         draw itself in a story.  It is specific to PDF and should not
         be used directly."""
-        import renderPDF
+        from reportlab.graphics import renderPDF
         renderPDF.draw(self, self.canv, 0, 0, showBoundary=showBoundary)
 
     def wrap(self, availWidth, availHeight):
@@ -725,7 +722,7 @@
             else:
                 try:
                     fnRoot = fnRoot % chartId
-                except TypeError, err:
+                except TypeError as err:
                     #the exact error message changed from 2.2 to 2.3 so we need to
                     #check a substring
                     if str(err).find('not all arguments converted') < 0: raise
@@ -740,7 +737,7 @@
         if not os.path.isdir(outDir): os.makedirs(outDir)
         fnroot = os.path.normpath(os.path.join(outDir,fnRoot))
         plotMode = os.path.splitext(fnroot)
-        if string.lower(plotMode[1][1:]) in self._saveModes:
+        if plotMode[1][1:].lower() in self._saveModes:
             fnroot = plotMode[0]
 
         plotMode = [x.lower() for x in (formats or getattr(self,'formats',['pdf']))]
@@ -750,7 +747,7 @@
         if 'pdf' in plotMode:
             from reportlab.graphics import renderPDF
             filename = fnroot+'.pdf'
-            if verbose: print genFmt % ('PDF',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':
@@ -762,7 +759,7 @@
             if bmFmt in plotMode:
                 from reportlab.graphics import renderPM
                 filename = '%s.%s' % (fnroot,bmFmt)
-                if verbose: print genFmt % (bmFmt,filename)
+                if verbose: print(genFmt % (bmFmt,filename))
                 dtc = getattr(self,'_drawTimeCollector',None)
                 if dtc:
                     dtcfmts = getattr(dtc,'formats',[bmFmt])
@@ -780,7 +777,7 @@
             except ImportError:
                 from reportlab.graphics import renderPS
             filename = fnroot+'.eps'
-            if verbose: print genFmt % ('EPS',filename)
+            if verbose: print(genFmt % ('EPS',filename))
             renderPS.drawToFile(self,
                                 filename,
                                 title = fnroot,
@@ -795,7 +792,7 @@
         if 'svg' in plotMode:
             from reportlab.graphics import renderSVG
             filename = fnroot+'.svg'
-            if verbose: print genFmt % ('SVG',filename)
+            if verbose: print(genFmt % ('SVG',filename))
             renderSVG.drawToFile(self,
                                 filename,
                                 showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderSVG_',**kw))
@@ -804,14 +801,14 @@
         if 'ps' in plotMode:
             from reportlab.graphics import renderPS
             filename = fnroot+'.ps'
-            if verbose: print genFmt % ('EPS',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 genFmt % ('py',filename)
-            open(filename,'w').write(self._renderPy())
+            if verbose: print(genFmt % ('py',filename))
+            open(filename,'wb').write(asBytes(self._renderPy().replace('\n',os.linesep)))
             ext = ext +  '/.py'
 
         logger.warnOnce.enabled, logger.infoOnce.enabled = _saved
@@ -885,7 +882,7 @@
         elif name:
             setattr(obj,name,value)
         else:
-            raise ValueError, "Can't add, need name"
+            raise ValueError("Can't add, need name")
 
 class LineShape(Shape):
     # base for types of lines
@@ -951,7 +948,7 @@
 
 
 # path operator  constants
-_MOVETO, _LINETO, _CURVETO, _CLOSEPATH = range(4)
+_MOVETO, _LINETO, _CURVETO, _CLOSEPATH = list(range(4))
 _PATH_OP_ARG_COUNT = (2, 2, 6, 0)  # [moveTo, lineTo, curveTo, closePath]
 _PATH_OP_NAMES=['moveTo','lineTo','curveTo','closePath']
 
@@ -1065,7 +1062,7 @@
         n = 1
         radiansdelta = 0
 
-    for angle in xrange(n):
+    for angle in range(n):
         angle = startangle+angle*radiansdelta
         a((centerx+radius*cos(angle),centery+yradius*sin(angle)))
 
@@ -1085,21 +1082,21 @@
     O = []
     P = []
     for seg in pathSegs:
-        if type(seg) not in [ListType,TupleType]:
+        if not isSeq(seg):
             opName = seg
             args = []
         else:
             opName = seg[0]
             args = seg[1:]
         if opName not in _PATH_OP_NAMES:
-            raise ValueError, 'bad operator name %s' % opName
+            raise ValueError('bad operator name %s' % opName)
         op = _PATH_OP_NAMES.index(opName)
         if len(args)!=_PATH_OP_ARG_COUNT[op]:
-            raise ValueError, '%s bad arguments %s' % (opName,str(args))
+            raise ValueError('%s bad arguments %s' % (opName,str(args)))
         O.append(op)
         P.extend(list(args))
     for d,o in (dx,0), (dy,1):
-        for i in xrange(o,len(P),2):
+        for i in range(o,len(P),2):
             P[i] = P[i]+d
     return Path(P,O,isClipPath,**kw)
 
@@ -1272,7 +1269,7 @@
         CA = []
         CAA = CA.append
         a = points.append
-        for angle in xrange(n):
+        for angle in range(n):
             angle = startangle+angle*radiansdelta
             CAA((cos(angle),sin(angle)))
         for c,s in CA:
@@ -1334,7 +1331,7 @@
         points = points or []
         lenPoints = len(points)
         if lenPoints:
-            if type(points[0]) in (ListType,TupleType):
+            if isSeq(points[0]):
                 L = []
                 for (x,y) in points:
                     L.append(x)
@@ -1423,24 +1420,24 @@
         added to drawings; they must create a shape (typically a group)
         so that the renderer can draw the custom node."""
 
-        raise NotImplementedError, "this method must be redefined by the user/programmer"
+        raise NotImplementedError("this method must be redefined by the user/programmer")
 
 
 def test():
     r = Rect(10,10,200,50)
     import pprint
     pp = pprint.pprint
-    print 'a Rectangle:'
+    w = sys.stdout.write
+    w('a Rectangle: ')
     pp(r.getProperties())
-    print
-    print 'verifying...',
+    w('\nverifying...')
     r.verify()
-    print 'OK'
+    w(' OK\n')
     #print 'setting rect.z = "spam"'
     #r.z = 'spam'
-    print 'deleting rect.width'
+    w('deleting rect.width ')
     del r.width
-    print 'verifying...',
+    w('verifying...')
     r.verify()
 
 
--- a/src/reportlab/graphics/testdrawings.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/testdrawings.py	Wed Mar 26 12:32:02 2014 +0000
@@ -293,4 +293,4 @@
 
 
 if __name__=='__main__':
-    print __doc__
+    print(__doc__)
--- a/src/reportlab/graphics/testshapes.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/testshapes.py	Wed Mar 26 12:32:02 2014 +0000
@@ -19,6 +19,7 @@
 
 from reportlab.lib import colors
 from reportlab.lib.units import cm
+from reportlab.lib.utils import asNative
 from reportlab.pdfgen.canvas import Canvas
 from reportlab.pdfbase.pdfmetrics import stringWidth
 from reportlab.platypus import Flowable
@@ -112,7 +113,7 @@
     D = Drawing(400, 200)
     D.add(Rect(50, 50, 300, 100, fillColor=colors.yellow))
     D.add(String(180,100, 'Hello World', fillColor=colors.red))
-    D.add(String(180,86, 'Special characters \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xce\xb1\xce\xb2', fillColor=colors.red))
+    D.add(String(180,86, b'Special characters \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xce\xb1\xce\xb2', fillColor=colors.red))
 
     return D
 
@@ -384,7 +385,7 @@
 
     return D
 
-from widgets.signsandsymbols import SmileyFace
+from reportlab.graphics.widgets.signsandsymbols import SmileyFace
 def getDrawing11():
     '''test of anchoring'''
     def makeSmiley(x, y, size, color):
@@ -439,7 +440,7 @@
         maxx = 0
         for fontName in F:
             y -= th
-            text = fontName+": I should be totally horizontal and enclosed in a box and end in alphabetagamma \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xca\xa5\xd0\x96\xd6\x83\xd7\x90\xd9\x82\xe0\xa6\x95\xce\xb1\xce\xb2\xce\xb3"
+            text = fontName+asNative(b': I should be totally horizontal and enclosed in a box and end in alphabetagamma \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xca\xa5\xd0\x96\xd6\x83\xd7\x90\xd9\x82\xe0\xa6\x95\xce\xb1\xce\xb2\xce\xb3')
             textWidth = stringWidth(text, fontName, fontSize)
             maxx = max(maxx,textWidth+20)
             D.add(
@@ -481,7 +482,7 @@
     funcNames = []
 
     # Here we get the names from the global name space.
-    symbols = globals().keys()
+    symbols = list(globals().keys())
     symbols.sort()
     for funcName in symbols:
         if funcName[0:10] == 'getDrawing':
@@ -534,7 +535,7 @@
         i = i + 1
 
     c.save()
-    print 'wrote %s ' % pdfPath
+    print('wrote %s ' % pdfPath)
 
 
 class ShapesTestCase(unittest.TestCase):
--- a/src/reportlab/graphics/widgetbase.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgetbase.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,8 +4,6 @@
 __version__=''' $Id$ '''
 __doc__='''Base class for user-defined graphical widgets'''
 
-import string
-
 from reportlab.graphics import shapes
 from reportlab import rl_config
 from reportlab.lib import colors
@@ -31,7 +29,7 @@
                 if key[0] != '_':
                     msg = "Unexpected attribute %s found in %s" % (key, self)
                     assert key in self._attrMap, msg
-            for (attr, metavalue) in self._attrMap.items():
+            for attr, metavalue in self._attrMap.items():
                 msg = "Missing attribute %s from %s" % (attr, self)
                 assert hasattr(self, attr), msg
                 value = getattr(self, attr)
@@ -71,7 +69,7 @@
                 if recur and isValidChild(component):
                     # child object, get its properties too
                     childProps = component.getProperties(recur=recur)
-                    for (childKey, childValue) in childProps.items():
+                    for childKey, childValue in childProps.items():
                         #key might be something indexed like '[2].fillColor'
                         #or simple like 'fillColor'; in the former case we
                         #don't need a '.' between me and my child.
@@ -98,8 +96,8 @@
         """
 
         childPropDicts = {}
-        for (name, value) in propDict.items():
-            parts = string.split(name, '.', 1)
+        for name, value in propDict.items():
+            parts = name.split('.', 1)
             if len(parts) == 1:
                 #simple attribute, set it now
                 setattr(self, name, value)
@@ -111,7 +109,7 @@
                     childPropDicts[childName] = {remains: value}
 
         # now assign to children
-        for (childName, childPropDict) in childPropDicts.items():
+        for childName, childPropDict in childPropDicts.items():
             child = getattr(self, childName)
             child.setProperties(childPropDict)
 
@@ -122,12 +120,12 @@
         samples for documentation.
         """
 
-        propList = self.getProperties().items()
+        propList = list(self.getProperties().items())
         propList.sort()
         if prefix:
             prefix = prefix + '.'
         for (name, value) in propList:
-            print '%s%s = %s' % (prefix, name, value)
+            print('%s%s = %s' % (prefix, name, value))
 
 
 class Widget(PropHolder, shapes.UserNode):
@@ -142,11 +140,11 @@
 
     def draw(self):
         msg = "draw() must be implemented for each Widget!"
-        raise shapes.NotImplementedError, msg
+        raise shapes.NotImplementedError(msg)
 
     def demo(self):
         msg = "demo() must be implemented for each Widget!"
-        raise shapes.NotImplementedError, msg
+        raise shapes.NotImplementedError(msg)
 
     def provideNode(self):
         return self.draw()
@@ -251,7 +249,7 @@
                     child._index = None
             else:
                 child._index = None
-            for i in filter(lambda x,K=child.__dict__.keys(): x in K,child._attrMap.keys()):
+            for i in filter(lambda x,K=list(child.__dict__.keys()): x in K,list(child._attrMap.keys())):
                 del child.__dict__[i]
 
             self._children[index] = child
@@ -266,19 +264,19 @@
         assert isinstance(value, self._value.__class__), msg
 
     def __len__(self):
-        return len(self._children.keys())
+        return len(list(self._children.keys()))
 
     def getProperties(self,recur=1):
         # return any children which are defined and whatever
         # differs from the parent
         props = {}
 
-        for (key, value) in self._value.getProperties(recur=recur).items():
+        for key, value in self._value.getProperties(recur=recur).items():
             props['%s' % key] = value
 
         for idx in self._children.keys():
             childProps = self._children[idx].getProperties(recur=recur)
-            for (key, value) in childProps.items():
+            for key, value in childProps.items():
                 if not hasattr(self,key) or getattr(self, key)!=value:
                     newKey = '[%s].%s' % (idx, key)
                     props[newKey] = value
@@ -286,7 +284,7 @@
 
     def setVector(self,**kw):
         for name, value in kw.items():
-            for i in xrange(len(value)):
+            for i in range(len(value)):
                 setattr(self[i],name,value[i])
 
     def __getattr__(self,name):
@@ -456,7 +454,7 @@
     def _addNamedNode(self,name,node):
         'if name is not None add an attribute pointing to node and add to the attrMap'
         if name:
-            if name not in self._attrMap.keys():
+            if name not in list(self._attrMap.keys()):
                 self._attrMap[name] = AttrMapValue(isValidChild)
             setattr(self, name, node)
 
@@ -501,29 +499,29 @@
     wedges = TypedPropertyCollection(WedgeProperties)
     wedges.fillColor = colors.red
     wedges.setVector(fillColor=(colors.blue,colors.green,colors.white))
-    print len(_ItemWrapper)
+    print(len(_ItemWrapper))
 
     d = shapes.Drawing(400, 200)
     tc = TwoCircles()
     d.add(tc)
-    import renderPDF
+    from reportlab.graphics import renderPDF
     renderPDF.drawToFile(d, 'sample_widget.pdf', 'A Sample Widget')
-    print 'saved sample_widget.pdf'
+    print('saved sample_widget.pdf')
 
     d = shapes.Drawing(400, 200)
     f = Face()
     f.skinColor = colors.yellow
     f.mood = "sad"
     d.add(f, name='theFace')
-    print 'drawing 1 properties:'
+    print('drawing 1 properties:')
     d.dumpProperties()
     renderPDF.drawToFile(d, 'face.pdf', 'A Sample Widget')
-    print 'saved face.pdf'
+    print('saved face.pdf')
 
     d2 = d.expandUserNodes()
     renderPDF.drawToFile(d2, 'face_copy.pdf', 'An expanded drawing')
-    print 'saved face_copy.pdf'
-    print 'drawing 2 properties:'
+    print('saved face_copy.pdf')
+    print('drawing 2 properties:')
     d2.dumpProperties()
 
 
--- a/src/reportlab/graphics/widgets/eventcal.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/eventcal.py	Wed Mar 26 12:32:02 2014 +0000
@@ -298,7 +298,7 @@
     for format in ['pdf']:#,'gif','png']:
         out = d.asString(format)
         open('eventcal.%s' % format, 'wb').write(out)
-        print 'saved eventcal.%s' % format
+        print('saved eventcal.%s' % format)
 
 if __name__=='__main__':
     test()
--- a/src/reportlab/graphics/widgets/flags.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/flags.py	Wed Mar 26 12:32:02 2014 +0000
@@ -36,7 +36,7 @@
 from reportlab.graphics.shapes import Line, Rect, Polygon, Drawing, Group, String, Circle, Wedge
 from reportlab.graphics.widgetbase import Widget
 from reportlab.graphics import renderPDF
-from signsandsymbols import _Symbol
+from reportlab.graphics.widgets.signsandsymbols import _Symbol
 import copy
 from math import sin, cos, pi
 
@@ -113,7 +113,7 @@
         r = R*sin(18*(pi/180.0))/cos(36*(pi/180.0))
         P = []
         angle = 90
-        for i in xrange(5):
+        for i in range(5):
             for radius in R, r:
                 theta = angle*(pi/180.0)
                 P.append(radius*cos(theta))
@@ -158,7 +158,7 @@
 
     def availableFlagNames(self):
         '''return a list of the things we can display'''
-        return filter(lambda x: x is not None, self._attrMap['kind'].validate._enum)
+        return [x for x in self._attrMap['kind'].validate._enum if x is not None]
 
     def _Flag_None(self):
         s = _size  # abbreviate as we will use this a lot
@@ -199,11 +199,11 @@
         fx.y = 0
         D.add(fx)
         labelFontSize = 10
-        D.add(String(fx.x+(fx.size/2),(fx.y-(1.2*labelFontSize)),
+        D.add(String(fx.x+(fx.size/2.0),(fx.y-(1.2*labelFontSize)),
                             name, fillColor=colors.black, textAnchor='middle',
                             fontSize=labelFontSize))
-        labelFontSize = int(fx.size/4)
-        D.add(String(fx.x+(fx.size),(fx.y+((fx.size/2))),
+        labelFontSize = int(fx.size/4.0)
+        D.add(String(fx.x+(fx.size),(fx.y+((fx.size/2.0))),
                             "SAMPLE", fillColor=colors.gold, textAnchor='middle',
                             fontSize=labelFontSize, fontName="Helvetica-Bold"))
         return D
@@ -215,10 +215,10 @@
         g.add(Rect(0, 0, w, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0))
         g.add(Polygon([0,0, s*.225,0, w,s*(1-.1125), w,s, w-s*.225,s, 0, s*.1125], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
         g.add(Polygon([0,s*(1-.1125), 0, s, s*.225,s, w, s*.1125, w,0, w-s*.225,0], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
-        g.add(Polygon([0, s-(s/15), (s-((s/10)*4)), (s*0.65), (s-(s/10)*3), (s*0.65), 0, s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
-        g.add(Polygon([0, 0, (s-((s/10)*3)), (s*0.35), (s-((s/10)*2)), (s*0.35), (s/10), 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
-        g.add(Polygon([w, s, (s+((s/10)*3)), (s*0.65), (s+((s/10)*2)), (s*0.65), w-(s/10), s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
-        g.add(Polygon([w, (s/15), (s+((s/10)*4)), (s*0.35), (s+((s/10)*3)), (s*0.35), w, 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
+        g.add(Polygon([0, s-(s/15.0), (s-((s/10.0)*4)), (s*0.65), (s-(s/10.0)*3), (s*0.65), 0, s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
+        g.add(Polygon([0, 0, (s-((s/10.0)*3)), (s*0.35), (s-((s/10.0)*2)), (s*0.35), (s/10.0), 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
+        g.add(Polygon([w, s, (s+((s/10.0)*3)), (s*0.65), (s+((s/10.0)*2)), (s*0.65), w-(s/10.0), s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
+        g.add(Polygon([w, (s/15.0), (s+((s/10.0)*4)), (s*0.35), (s+((s/10.0)*3)), (s*0.35), w, 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
         g.add(Rect(((s*0.42)*2), 0, width=(0.16*s)*2, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
         g.add(Rect(0, (s*0.35), width=w, height=s*0.3, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
         g.add(Rect(((s*0.45)*2), 0, width=(0.1*s)*2, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
@@ -247,14 +247,14 @@
         g.add(bluebox)
 
         lss = s*0.045
-        lss2 = lss/2
-        s9 = s/9
-        s7 = s/7
+        lss2 = lss/2.0
+        s9 = s/9.0
+        s7 = s/7.0
         for starxcounter in range(5):
             for starycounter in range(4):
                 ls = Star()
                 ls.size = lss
-                ls.x = 0-s/22+lss/2+s7+starxcounter*s7
+                ls.x = 0-s/22.0+lss/2.0+s7+starxcounter*s7
                 ls.fillColor = colors.mintcream
                 ls.y = s-(starycounter+1)*s9+lss2
                 g.add(ls)
@@ -263,9 +263,9 @@
             for starycounter in range(5):
                 ls = Star()
                 ls.size = lss
-                ls.x = 0-(s/22)+lss/2+s/14+starxcounter*s7
+                ls.x = 0-(s/22.0)+lss/2.0+s/14.0+starxcounter*s7
                 ls.fillColor = colors.mintcream
-                ls.y = s-(starycounter+1)*s9+(s/18)+lss2
+                ls.y = s-(starycounter+1)*s9+(s/18.0)+lss2
                 g.add(ls)
         return g
 
@@ -332,7 +332,7 @@
         self._width = w = s*1.5
         g.add(Rect(0, 0, w, s, fillColor=colors.red, strokeColor=None, strokeWidth=0))
 
-        def addStar(x,y,size,angle,g=g,w=s/20,x0=0,y0=s/2):
+        def addStar(x,y,size,angle,g=g,w=s/20.0,x0=0,y0=s/2.0):
             s = Star()
             s.fillColor=colors.yellow
             s.angle = angle
@@ -353,20 +353,20 @@
         g = Group()
 
         for i in range(5):
-            stripe = Rect(0, i*s/5, width=s*2, height=s/5,
+            stripe = Rect(0, i*s/5.0, width=s*2, height=s/5.0,
                 fillColor = [colors.darkblue, colors.mintcream][i%2],
                 strokeColor = None,
                 strokeWidth=0)
             g.add(stripe)
 
-        redwedge = Polygon(points = [ 0, 0, 4*s/5, (s/2), 0, s],
+        redwedge = Polygon(points = [ 0, 0, 4*s/5.0, (s/2.0), 0, s],
                     fillColor = colors.red, strokeColor = None, strokeWidth=0)
         g.add(redwedge)
 
         star = Star()
-        star.x = 2.5*s/10
-        star.y = s/2
-        star.size = 3*s/10
+        star.x = 2.5*s/10.0
+        star.y = s/2.0
+        star.size = 3*s/10.0
         star.fillColor = colors.white
         g.add(star)
 
@@ -387,11 +387,11 @@
             fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
         g.add(box)
 
-        whitebox1 = Rect(((s/5)*2), 0, width=s/6, height=s,
+        whitebox1 = Rect(((s/5.0)*2), 0, width=s/6.0, height=s,
             fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
         g.add(whitebox1)
 
-        whitebox2 = Rect(0, ((s/2)-(s/12)), width=w, height=s/6,
+        whitebox2 = Rect(0, ((s/2.0)-(s/12.0)), width=w, height=s/6.0,
             fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
         g.add(whitebox2)
         return g
@@ -507,8 +507,8 @@
         s = _size
         g = Group()
         g.add(Rect(0,0,s*2,s,fillColor=colors.forestgreen,strokeColor=None, strokeWidth=0))
-        g.add(Rect((2*s)/3, 0, width=(s*4)/3, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
-        g.add(Rect((4*s)/3, 0, width=(s*2)/3, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
+        g.add(Rect((2*s)/3.0, 0, width=(s*4)/3.0, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
+        g.add(Rect((4*s)/3.0, 0, width=(s*2)/3.0, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
         return g
 
     def _Flag_Japan(self):
@@ -516,7 +516,7 @@
         g = Group()
         w = self._width = s*1.5
         g.add(Rect(0,0,w,s,fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
-        g.add(Circle(cx=w/2,cy=s/2,r=0.3*w,fillColor=colors.red,strokeColor=None, strokeWidth=0))
+        g.add(Circle(cx=w/2.0,cy=s/2.0,r=0.3*w,fillColor=colors.red,strokeColor=None, strokeWidth=0))
         return g
 
     def _Flag_Luxembourg(self):
@@ -560,7 +560,7 @@
         s = _size
         g = Group()
         w = self._width = s*1.5
-        t = s/3
+        t = s/3.0
         g.add(Rect(0, 0, width=w, height=t, fillColor = colors.red, strokeColor = None, strokeWidth=0))
         g.add(Rect(0, t, width=w, height=t, fillColor = colors.blue, strokeColor = None, strokeWidth=0))
         g.add(Rect(0, 2*t, width=w, height=t, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
@@ -571,7 +571,7 @@
         g = Group()
         w = self._width = s*1.5
         g.add(Rect(0, 0, width=w, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
-        g.add(Rect(0, (s/4), width=w, height=s/2, fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
+        g.add(Rect(0, (s/4.0), width=w, height=s/2.0, fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
         return g
 
     def _Flag_Sweden(self):
@@ -582,11 +582,11 @@
             fillColor = colors.dodgerblue, strokeColor = colors.black, strokeWidth=0)
         g.add(box)
 
-        box1 = Rect(((s/5)*2), 0, width=s/6, height=s,
+        box1 = Rect(((s/5.0)*2), 0, width=s/6.0, height=s,
                 fillColor = colors.gold, strokeColor = None, strokeWidth=0)
         g.add(box1)
 
-        box2 = Rect(0, ((s/2)-(s/12)), width=self._width, height=s/6,
+        box2 = Rect(0, ((s/2.0)-(s/12.0)), width=self._width, height=s/6.0,
             fillColor = colors.gold,
             strokeColor = None,
             strokeWidth=0)
@@ -632,13 +632,13 @@
             strokeWidth=0)
         g.add(box)
 
-        redbox = Rect(0, 0, width=s*2, height=s/2,
+        redbox = Rect(0, 0, width=s*2, height=s/2.0,
             fillColor = colors.red,
             strokeColor = None,
             strokeWidth=0)
         g.add(redbox)
 
-        bluewedge = Polygon(points = [ 0, 0, s, (s/2), 0, s],
+        bluewedge = Polygon(points = [ 0, 0, s, (s/2.0), 0, s],
                     fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
         g.add(bluewedge)
         return g
@@ -646,25 +646,25 @@
     def _Flag_Palestine(self):
         s = _size
         g = Group()
-        box = Rect(0, s/3, s*2, s/3,
+        box = Rect(0, s/3.0, s*2, s/3.0,
             fillColor = colors.mintcream,
                         strokeColor = None,
             strokeWidth=0)
         g.add(box)
 
-        greenbox = Rect(0, 0, width=s*2, height=s/3,
+        greenbox = Rect(0, 0, width=s*2, height=s/3.0,
             fillColor = colors.limegreen,
             strokeColor = None,
             strokeWidth=0)
         g.add(greenbox)
 
-        blackbox = Rect(0, 2*s/3, width=s*2, height=s/3,
+        blackbox = Rect(0, 2*s/3.0, width=s*2, height=s/3.0,
             fillColor = colors.black,
             strokeColor = None,
             strokeWidth=0)
         g.add(blackbox)
 
-        redwedge = Polygon(points = [ 0, 0, 2*s/3, (s/2), 0, s],
+        redwedge = Polygon(points = [ 0, 0, 2*s/3.0, (s/2.0), 0, s],
                     fillColor = colors.red, strokeColor = None, strokeWidth=0)
         g.add(redwedge)
         return g
@@ -679,13 +679,13 @@
             strokeWidth=0)
         g.add(box)
 
-        whitecircle = Circle(cx=((s*0.35)*2), cy=s/2, r=s*0.3,
+        whitecircle = Circle(cx=((s*0.35)*2), cy=s/2.0, r=s*0.3,
             fillColor = colors.mintcream,
             strokeColor = None,
             strokeWidth=0)
         g.add(whitecircle)
 
-        redcircle = Circle(cx=((s*0.39)*2), cy=s/2, r=s*0.24,
+        redcircle = Circle(cx=((s*0.39)*2), cy=s/2.0, r=s*0.24,
             fillColor = colors.red,
             strokeColor = None,
             strokeWidth=0)
@@ -693,8 +693,8 @@
 
         ws = Star()
         ws.angle = 15
-        ws.size = s/5
-        ws.x = (s*0.5)*2+ws.size/2
+        ws.size = s/5.0
+        ws.x = (s*0.5)*2+ws.size/2.0
         ws.y = (s*0.5)
         ws.fillColor = colors.mintcream
         ws.strokeColor = None
@@ -707,10 +707,10 @@
         self._width = s
 
         g.add(Rect(0, 0, s, s, fillColor = colors.red, strokeColor = colors.black, strokeWidth=0))
-        g.add(Line((s/2), (s/5.5), (s/2), (s-(s/5.5)),
-            fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=(s/5)))
-        g.add(Line((s/5.5), (s/2), (s-(s/5.5)), (s/2),
-            fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=s/5))
+        g.add(Line((s/2.0), (s/5.5), (s/2), (s-(s/5.5)),
+            fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=(s/5.0)))
+        g.add(Line((s/5.5), (s/2.0), (s-(s/5.5)), (s/2.0),
+            fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=s/5.0))
         return g
 
     def _Flag_EU(self):
@@ -719,9 +719,9 @@
         w = self._width = 1.5*s
 
         g.add(Rect(0, 0, w, s, fillColor = colors.darkblue, strokeColor = None, strokeWidth=0))
-        centerx=w/2
-        centery=s/2
-        radius=s/3
+        centerx=w/2.0
+        centery=s/2.0
+        radius=s/3.0
         yradius = radius
         xradius = radius
         nStars = 12
@@ -731,7 +731,7 @@
             gs = Star()
             gs.x=cos(rad)*radius+centerx
             gs.y=sin(rad)*radius+centery
-            gs.size=s/10
+            gs.size=s/10.0
             gs.fillColor=colors.gold
             g.add(gs)
         return g
@@ -740,33 +740,33 @@
         s = _size  # abbreviate as we will use this a lot
         g = Group()
 
-        m = s/14
+        m = s/14.0
         self._width = w = (m * 20)
 
         def addStar(x,y,size, g=g, w=w, s=s, m=m):
             st = Star()
             st.fillColor=colors.mintcream
             st.size = size*m
-            st.x = (w/2) + (x * (0.35 * m))
-            st.y = (s/2) + (y * (0.35 * m))
+            st.x = (w/2.0) + (x * (0.35 * m))
+            st.y = (s/2.0) + (y * (0.35 * m))
             g.add(st)
 
         g.add(Rect(0, 0, w, s, fillColor = colors.green, strokeColor = None, strokeWidth=0))
-        g.add(Polygon(points = [ 1.7*m, (s/2), (w/2), s-(1.7*m), w-(1.7*m),(s/2),(w/2), 1.7*m],
+        g.add(Polygon(points = [ 1.7*m, (s/2.0), (w/2.0), s-(1.7*m), w-(1.7*m),(s/2.0),(w/2.0), 1.7*m],
                       fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
-        g.add(Circle(cx=w/2, cy=s/2, r=3.5*m,
+        g.add(Circle(cx=w/2.0, cy=s/2.0, r=3.5*m,
                      fillColor=colors.blue,strokeColor=None, strokeWidth=0))
-        g.add(Wedge((w/2)-(2*m), 0, 8.5*m, 50, 98.1, 8.5*m,
+        g.add(Wedge((w/2.0)-(2*m), 0, 8.5*m, 50, 98.1, 8.5*m,
                     fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
-        g.add(Wedge((w/2), (s/2), 3.501*m, 156, 352, 3.501*m,
+        g.add(Wedge((w/2.0), (s/2.0), 3.501*m, 156, 352, 3.501*m,
                     fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
-        g.add(Wedge((w/2)-(2*m), 0, 8*m, 48.1, 100, 8*m,
+        g.add(Wedge((w/2.0)-(2*m), 0, 8*m, 48.1, 100, 8*m,
                     fillColor=colors.blue,strokeColor=None, strokeWidth=0))
-        g.add(Rect(0, 0, w, (s/4) + 1.7*m,
+        g.add(Rect(0, 0, w, (s/4.0) + 1.7*m,
                    fillColor = colors.green, strokeColor = None, strokeWidth=0))
-        g.add(Polygon(points = [ 1.7*m,(s/2), (w/2),s/2 - 2*m,  w-(1.7*m),(s/2) , (w/2),1.7*m],
+        g.add(Polygon(points = [ 1.7*m,(s/2.0), (w/2.0),s/2.0 - 2*m,    w-(1.7*m),(s/2.0) , (w/2.0),1.7*m],
                       fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
-        g.add(Wedge(w/2, s/2, 3.502*m, 166, 342.1, 3.502*m,
+        g.add(Wedge(w/2.0, s/2.0, 3.502*m, 166, 342.1, 3.502*m,
                     fillColor=colors.blue,strokeColor=None, strokeWidth=0))
 
         addStar(3.2,3.5,0.3)
@@ -800,7 +800,7 @@
         nTmp = len(sTmp)
         delta = 0.850848010347/nTmp
         radius = 7.9 *m
-        centerx = (w/2)-(2*m)
+        centerx = (w/2.0)-(2*m)
         centery = 0
         for i in range(nTmp):
             rad = 2*pi - i*delta -4.60766922527
@@ -867,7 +867,7 @@
         flag.x = X[i%2]
         flag.y = y
         D.add(flag)
-        D.add(String(flag.x+(flag.size/2),(flag.y-(1.2*labelFontSize)),
+        D.add(String(flag.x+(flag.size/2.0),(flag.y-(1.2*labelFontSize)),
                 name, fillColor=colors.black, textAnchor='middle', fontSize=labelFontSize))
         if i%2: y = y - 125
         if (i%2 and y<0) or name==flags[-1]:
--- a/src/reportlab/graphics/widgets/grids.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/grids.py	Wed Mar 26 12:32:02 2014 +0000
@@ -374,7 +374,7 @@
         numShades = self.numShades
         if self.cylinderMode:
             if not numShades%2: numShades = numShades+1
-            halfNumShades = (numShades-1)/2 + 1
+            halfNumShades = int((numShades-1)/2) + 1
         num = float(numShades) # must make it float!
         vertical = self.orientation == 'vertical'
         if vertical:
@@ -452,10 +452,10 @@
     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 = list(map(parallelAxisDist,P))
     L.sort()
     a0, a1 = L[0], L[-1]
-    L = map(orthogonalAxisDist,P)
+    L = list(map(orthogonalAxisDist,P))
     L.sort()
     b0, b1 = L[0], L[-1]
     rect.x, rect.width = a0, a1-a0
@@ -485,8 +485,8 @@
 
     def draw(self):
         P = self.points
-        P = map(lambda i, P=P:(P[i],P[i+1]),xrange(0,len(P),2))
-        path = definePath([('moveTo',)+P[0]]+map(lambda x: ('lineTo',)+x,P[1:])+['closePath'],
+        P = list(map(lambda i, P=P:(P[i],P[i+1]),range(0,len(P),2)))
+        path = definePath([('moveTo',)+P[0]]+[('lineTo',)+x for x in P[1:]]+['closePath'],
             fillColor=None, strokeColor=None)
         path.isClipPath = 1
         g = Group()
--- a/src/reportlab/graphics/widgets/markers.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/markers.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,21 +1,19 @@
-#Copyright ReportLab Europe Ltd. 2000-2012
+#Copyright ReportLab Europe Ltd. 2000-2013
 #see license.txt for license details
-#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/markers.py
 
 __version__=''' $Id$ '''
 __doc__="""This modules defines a collection of markers used in charts.
 """
 
-from types import FunctionType, ClassType
 from reportlab.graphics.shapes import Rect, Line, Circle, Polygon, Drawing, Group
 from reportlab.graphics.widgets.signsandsymbols import SmileyFace
 from reportlab.graphics.widgetbase import Widget
 from reportlab.lib.validators import isNumber, isColorOrNone, OneOf, Validator
 from reportlab.lib.attrmap import AttrMap, AttrMapValue
 from reportlab.lib.colors import black
+from reportlab.lib.utils import isFunction, isClass
 from reportlab.graphics.widgets.flags import Flag
 from math import sin, cos, pi
-import copy, new
 _toradians = pi/180.0
 
 class Marker(Widget):
@@ -59,8 +57,10 @@
             arrowHeight = 1.875,
             )
 
-    def clone(self):
-        return new.instance(self.__class__,self.__dict__.copy())
+    def clone(self,**kwds):
+        n = self.__class__(**self.__dict__)
+        if kwds: n.__dict__.update(kwds)
+        return n
 
     def _Smiley(self):
         x, y = self.x+self.dx, self.y+self.dy
@@ -117,7 +117,7 @@
         r = R*sin(18*_toradians)/cos(36*_toradians)
         P = []
         angle = 90
-        for i in xrange(5):
+        for i in range(5):
             for radius in R, r:
                 theta = angle*_toradians
                 P.append(radius*cos(theta))
@@ -145,7 +145,7 @@
 
     def _doPolygon(self,P):
         x, y = self.x+self.dx, self.y+self.dy
-        if x or y: P = map(lambda i,P=P,A=[x,y]: P[i] + A[i&1], range(len(P)))
+        if x or y: P = list(map(lambda i,P=P,A=[x,y]: P[i] + A[i&1], list(range(len(P)))))
         return Polygon(P, strokeWidth =self.strokeWidth, strokeColor=self.strokeColor, fillColor=self.fillColor)
 
     def _doFill(self):
@@ -159,7 +159,7 @@
     def _doNgon(self,n):
         P = []
         size = float(self.size)/2
-        for i in xrange(n):
+        for i in range(n):
             r = (2.*i/n+0.5)*pi
             P.append(size*cos(r))
             P.append(size*sin(r))
@@ -201,9 +201,9 @@
         return m
 
 def uSymbol2Symbol(uSymbol,x,y,color):
-    if type(uSymbol) == FunctionType:
+    if isFunction(uSymbol):
         symbol = uSymbol(x, y, 5, color)
-    elif type(uSymbol) == ClassType and issubclass(uSymbol,Widget):
+    elif isClass(uSymbol) and issubclass(uSymbol,Widget):
         size = 10.
         symbol = uSymbol()
         symbol.x = x - (size/2)
@@ -223,8 +223,7 @@
 
 class _isSymbol(Validator):
     def test(self,x):
-        return hasattr(x,'__call__') or isinstance(x,Marker) or isinstance(x,Flag) \
-                or (type(x)==ClassType and issubclass(x,Widget))
+        return hasattr(x,'__call__') or isinstance(x,Marker) or isinstance(x,Flag) or (isinstance(x,type) and issubclass(x,Widget))
 
 isSymbol = _isSymbol()
 
@@ -237,7 +236,7 @@
         m.kind = name[:-5]
         m.size = 10
     else:
-        raise ValueError, "Invalid marker name %s" % name
+        raise ValueError("Invalid marker name %s" % name)
     return m
 
 if __name__=='__main__':
--- a/src/reportlab/graphics/widgets/signsandsymbols.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/signsandsymbols.py	Wed Mar 26 12:32:02 2014 +0000
@@ -927,7 +927,7 @@
                             fontSize=labelFontSize))
 
     renderPDF.drawToFile(D, 'signsandsymbols.pdf', 'signsandsymbols.py')
-    print 'wrote file: signsandsymbols.pdf'
+    print('wrote file: signsandsymbols.pdf')
 
 if __name__=='__main__':
     test()
--- a/src/reportlab/graphics/widgets/table.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/graphics/widgets/table.py	Wed Mar 26 12:32:02 2014 +0000
@@ -63,7 +63,7 @@
 
 
         for k, v in kw.items():
-            if k in self.__class__._attrMap.keys():
+            if k in list(self.__class__._attrMap.keys()):
                 setattr(self, k, v)
             else:
                 raise ValueError('invalid argument supplied for class %s'%self.__class__)
@@ -103,7 +103,7 @@
         #print "(row_step,col_step)=(%s, %s)"%(row_step,col_step)
         # draw the grid
         if self.horizontalDividerStrokeColor:
-            for i in xrange(rows): # make horizontal lines
+            for i in range(rows): # make horizontal lines
                 x1 = self.x
                 x2 = self.x + self.width
                 y = self.y + row_step*i
@@ -114,7 +114,7 @@
                 line.strokeColor = self.horizontalDividerStrokeColor
                 g.add(line)
         if self.verticalDividerStrokeColor:
-            for i in xrange(cols): # make vertical lines
+            for i in range(cols): # make vertical lines
                 x = self.x+col_step*i
                 y1 = self.y
                 y2 = self.y + self.height
--- a/src/reportlab/lib/PyFontify.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/PyFontify.py	Wed Mar 26 12:32:02 2014 +0000
@@ -157,4 +157,4 @@
     f.close()
     tags = fontify(text)
     for tag, start, end, sublist in tags:
-        print tag, repr(text[start:end])
+        print(tag, repr(text[start:end]))
--- a/src/reportlab/lib/abag.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/abag.py	Wed Mar 26 12:32:02 2014 +0000
@@ -25,12 +25,12 @@
 
     def __repr__(self):
         D = self.__dict__
-        K = D.keys()
+        K = list(D.keys())
         K.sort()
         return '%s(%s)' % (self.__class__.__name__,', '.join(['%s=%r' % (k,D[k]) for k in K]))
 
 if __name__=="__main__":
     AB = ABag(a=1, c="hello")
     CD = AB.clone()
-    print AB
-    print CD
+    print(AB)
+    print(CD)
--- a/src/reportlab/lib/arciv.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/arciv.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,8 +3,9 @@
 '''
 Arciv Stream  ciphering
 '''
-__version__=''' $Id$ '''
-from types import StringType
+__all__='''ArcIV encode decode'''.split()
+__version__=''' 1.0 '''
+from reportlab.lib.utils import isUnicode, isPy3
 class ArcIV:
 	'''
 	performs 'ArcIV' Stream Encryption of S using key
@@ -22,17 +23,22 @@
 		#Initialize private key, k With the values of the key mod 256.
 		#and sbox With numbers 0 - 255. Then compute sbox
 		key = self._key
-		sbox = range(256)
-		k = range(256)
+		if isUnicode(key): key = key.encode('utf8')
+		sbox = list(range(256))
+		k = list(range(256))
 		lk = len(key)
-		for i in sbox:
-			k[i] = ord(key[i % lk]) % 256
+		if isPy3:
+			for i in sbox:
+				k[i] = key[i % lk] % 256
+		else:
+			for i in sbox:
+				k[i] = ord(key[i % lk]) % 256
 
 		#Re-order sbox using the private key, k.
 		#Iterating each element of sbox re-calculate the counter j
 		#Then interchange the elements sbox[a] & sbox[b]
 		j = 0
-		for i in xrange(256):
+		for i in range(256):
 			j = (j+sbox[i]+k[i]) % 256
 			sbox[i], sbox[j] = sbox[j], sbox[i]
 		self._sbox, self._i, self._j = sbox, 0, 0
@@ -44,7 +50,12 @@
 		'''
 		sbox, i, j = self._sbox, self._i, self._j
 
-		C = type(B) is StringType and map(ord,B) or B[:]
+		if isPy3:
+			C = list(B.encode('utf8')) if isinstance(B,str) else (list(B) if isinstance(B,bytes) else B[:])
+		elif isinstance(B,basestring):
+			C = list(map(ord,B.encode('utf8') if isinstance(B,unicode) else B))
+		else:
+			C = B[:]
 		n = len(C)
 		p = 0
 		while p<n:
@@ -55,40 +66,45 @@
 			sbox[i], sbox[j] = sbox[j], sbox[i]
 			#overwrite the plaintext with the ciphered byte
 			C[p] = C[p] ^ sbox[(sbox[i] + sbox[j]) % 256]
-			p = p + 1
+			p += 1
 		return C
 
-	def encode(self,S):
-		'ArcIV encode string S'
-		return "".join(map(chr,self._encode(S)))
+	if isPy3:
+		def encode(self,S):
+			'ArcIV encode string S'
+			return bytes(self._encode(S))
+	else:
+		def encode(self,S):
+			'ArcIV encode string S'
+			return "".join(map(chr,self._encode(S)))
 
 _TESTS=[{
-		'key': "\x01\x23\x45\x67\x89\xab\xcd\xef",
-		'input': "\x01\x23\x45\x67\x89\xab\xcd\xef",
-		'output': "\x75\xb7\x87\x80\x99\xe0\xc5\x96",
+		'key': b"\x01\x23\x45\x67\x89\xab\xcd\xef",
+		'input': b"\x01\x23\x45\x67\x89\xab\xcd\xef",
+		'output': b"\x75\xb7\x87\x80\x99\xe0\xc5\x96",
 		},
 
 		{
-		'key': "\x01\x23\x45\x67\x89\xab\xcd\xef",
-		'input': "\x00\x00\x00\x00\x00\x00\x00\x00",
-		'output': "\x74\x94\xc2\xe7\x10\x4b\x08\x79",
+		'key': b"\x01\x23\x45\x67\x89\xab\xcd\xef",
+		'input': b"\x00\x00\x00\x00\x00\x00\x00\x00",
+		'output': b"\x74\x94\xc2\xe7\x10\x4b\x08\x79",
 		},
 
 		{
-		'key': "\x00\x00\x00\x00\x00\x00\x00\x00",
-		'input': "\x00\x00\x00\x00\x00\x00\x00\x00",
-		'output': "\xde\x18\x89\x41\xa3\x37\x5d\x3a",
+		'key': b"\x00\x00\x00\x00\x00\x00\x00\x00",
+		'input': b"\x00\x00\x00\x00\x00\x00\x00\x00",
+		'output': b"\xde\x18\x89\x41\xa3\x37\x5d\x3a",
 		},
 
 		{
-		'key': "\xef\x01\x23\x45",
-		'input': "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
-		'output': "\xd6\xa1\x41\xa7\xec\x3c\x38\xdf\xbd\x61",
+		'key': b"\xef\x01\x23\x45",
+		'input': b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+		'output': b"\xd6\xa1\x41\xa7\xec\x3c\x38\xdf\xbd\x61",
 		},
 
 		{
-		'key': "\x01\x23\x45\x67\x89\xab\xcd\xef",
-		'input': "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
+		'key': b"\x01\x23\x45\x67\x89\xab\xcd\xef",
+		'input': b"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
 \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
 \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
 \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
@@ -140,7 +156,7 @@
 \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
 \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
 \x01",
-	'output': "\x75\x95\xc3\xe6\x11\x4a\x09\x78\x0c\x4a\xd4\
+	'output': b"\x75\x95\xc3\xe6\x11\x4a\x09\x78\x0c\x4a\xd4\
 \x52\x33\x8e\x1f\xfd\x9a\x1b\xe9\x49\x8f\
 \x81\x3d\x76\x53\x34\x49\xb6\x77\x8d\xca\
 \xd8\xc7\x8a\x8d\x2b\xa9\xac\x66\x08\x5d\
@@ -208,7 +224,5 @@
 	i = 0
 	for t in _TESTS:
 		o = ArcIV(t['key']).encode(t['input'])
-		print 'Forward test %d %s!' %(i,o!=t['output'] and 'failed' or 'succeeded')
 		o = ArcIV(t['key']).encode(t['output'])
-		print 'Reverse test %d %s!' %(i,o!=t['input'] and 'failed' or 'succeeded')
 		i += 1
--- a/src/reportlab/lib/attrmap.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/attrmap.py	Wed Mar 26 12:32:02 2014 +0000
@@ -28,8 +28,8 @@
 
 
 '''
-from UserDict import UserDict
-from reportlab.lib.validators import isAnything, _SequenceTypes, DerivedValue
+from reportlab.lib.validators import isAnything, DerivedValue
+from reportlab.lib.utils import isSeq
 from reportlab import rl_config
 
 class CallableValue:
@@ -60,32 +60,29 @@
             return self._initial
         elif name=='hidden':
             return 0
-        raise AttributeError, name
+        raise AttributeError(name)
 
     def __repr__(self):
-        return 'AttrMapValue(%s)' % ', '.join(['%s=%r' % i for i in self.__dict__.iteritems()])
+        return 'AttrMapValue(%s)' % ', '.join(['%s=%r' % i for i in self.__dict__.items()])
 
-class AttrMap(UserDict):
+class AttrMap(dict):
     def __init__(self,BASE=None,UNWANTED=[],**kw):
         data = {}
         if BASE:
             if isinstance(BASE,AttrMap):
-                data = BASE.data                        #they used BASECLASS._attrMap
+                data = BASE
             else:
-                if type(BASE) not in (type(()),type([])): BASE = (BASE,)
+                if not isSeq(BASE): BASE = (BASE,)
                 for B in BASE:
-                    if hasattr(B,'_attrMap'):
-                        data.update(getattr(B._attrMap,'data',{}))
+                    am = getattr(B,'_attrMap',self)
+                    if am is not self:
+                        if am: data.update(am)
                     else:
-                        raise ValueError, 'BASE=%s has wrong kind of value' % str(B)
+                        raise ValueError('BASE=%s has wrong kind of value' % ascii(B))
 
-        UserDict.__init__(self,data)
+        dict.__init__(self,data)
         self.remove(UNWANTED)
-        self.data.update(kw)
-
-    def update(self,kw):
-        if isinstance(kw,AttrMap): kw = kw.data
-        self.data.update(kw)
+        self.update(kw)
 
     def remove(self,unwanted):
         for k in unwanted:
@@ -113,9 +110,9 @@
                 try:
                     validate = map[name].validate
                     if not validate(value):
-                        raise AttributeError, "Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__)
+                        raise AttributeError("Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__))
                 except KeyError:
-                    raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
+                    raise AttributeError("Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__))
     obj.__dict__[name] = value
 
 def _privateAttrMap(obj,ret=0):
@@ -134,7 +131,7 @@
 def _findObjectAndAttr(src, P):
     '''Locate the object src.P for P a string, return parent and name of attribute
     '''
-    P = string.split(P, '.')
+    P = P.split('.')
     if len(P) == 0:
         return None, None
     else:
@@ -157,11 +154,11 @@
     #sanity
     assert hasattr(src,'_attrMap'), 'src object has no _attrMap'
     A, oA = _privateAttrMap(src,1)
-    if type(dst) not in _SequenceTypes: dst = dst,
+    if not isSeq(dst): dst = dst,
     D = []
     DV = []
     for d in dst:
-        if type(d) in _SequenceTypes:
+        if isSeq(d):
             d, e = d[0], d[1:]
         obj, attr = _findObjectAndAttr(src,d)
         if obj:
--- a/src/reportlab/lib/codecharts.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/codecharts.py	Wed Mar 26 12:32:02 2014 +0000
@@ -9,7 +9,6 @@
 and fonts, we need some routines to display all those characters.
 These are defined herein.  The idea is to include flowable, drawable
 and graphic objects for single and multi-byte fonts. """
-import string
 import codecs
 
 from reportlab.pdfgen.canvas import Canvas
@@ -18,6 +17,7 @@
 from reportlab.graphics.shapes import Drawing, Group, String, Circle, Rect
 from reportlab.graphics.widgetbase import Widget
 from reportlab.lib import colors
+from reportlab.lib.utils import int2Byte
 
 adobe2codec = {
     'WinAnsiEncoding':'winansi',
@@ -150,7 +150,7 @@
 
     def draw(self):
         self.drawLabels()
-        charList = [None] * 32 + map(chr, range(32, 256))
+        charList = [None] * 32 + list(map(int2Byte, list(range(32, 256))))
 
         #we need to convert these to Unicode, since ReportLab
         #2.0 can only draw in Unicode.
@@ -207,15 +207,15 @@
     def makeRow(self, row):
         """Works out the character values for this kuten row"""
         cells = []
-        if string.find(self.encodingName, 'EUC') > -1:
+        if self.encodingName.find('EUC') > -1:
             # it is an EUC family encoding.
             for col in range(1, 95):
-                ch = chr(row + 160) + chr(col+160)
+                ch = int2Byte(row + 160) + int2Byte(col+160)
                 cells.append(ch)
-##        elif string.find(self.encodingName, 'GB') > -1:
+##        elif self.encodingName.find('GB') > -1:
 ##            # it is an EUC family encoding.
 ##            for col in range(1, 95):
-##                ch = chr(row + 160) + chr(col+160)
+##                ch = int2Byte(row + 160) + int2Byte(col+160)
         else:
             cells.append([None] * 94)
         return cells
@@ -224,7 +224,7 @@
         self.drawLabels(topLeft= 'R%d' % self.row)
 
         # work out which characters we need for the row
-        #assert string.find(self.encodingName, 'EUC') > -1, 'Only handles EUC encoding today, you gave me %s!' % self.encodingName
+        #assert self.encodingName.find('EUC') > -1, 'Only handles EUC encoding today, you gave me %s!' % self.encodingName
 
         # pad out by 1 to match Ken Lunde's tables
         charList = [None] + self.makeRow(self.row)
@@ -262,12 +262,12 @@
         """Works out the character values for this Big5 row.
         Rows start at 0xA1"""
         cells = []
-        if string.find(self.encodingName, 'B5') > -1:
+        if self.encodingName.find('B5') > -1:
             # big 5, different row size
             for y in [4,5,6,7,10,11,12,13,14,15]:
                 for x in range(16):
                     col = y*16+x
-                    ch = chr(row) + chr(col)
+                    ch = int2Byte(row) + int2Byte(col)
                     cells.append(ch)
 
         else:
@@ -321,7 +321,7 @@
                 charValue = y * 16 + x
                 if charValue > 32:
                     s = String(self.x + x * dx,
-                               self.y + (self.height - y*dy), chr(charValue))
+                               self.y + (self.height - y*dy), int2Byte(charValue))
                     g.add(s)
         return g
 
@@ -357,9 +357,7 @@
 ##    #Big5CodeChart(0xA1, 'MSungStd-Light-Acro','ETenms-B5-H').drawOn(c, 72, 500)
 
     c.save()
-    print 'saved codecharts.pdf'
+    print('saved codecharts.pdf')
 
 if __name__=='__main__':
     test()
-
-
--- a/src/reportlab/lib/colors.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/colors.py	Wed Mar 26 12:32:02 2014 +0000
@@ -5,7 +5,7 @@
 __doc__='''Defines standard colour-handling classes and colour names.
 
 We define standard classes to hold colours in two models:  RGB and CMYK.
-These can be constructed from several popular formats.  We also include
+rhese can be constructed from several popular formats.  We also include
 
 - pre-built colour objects for the HTML standard colours
 
@@ -39,8 +39,11 @@
     ....
 ValueError: css color 'pcmyka(100,0,0,0)' has wrong number of components
 '''
-import math, re
-from reportlab.lib.utils import fp_str
+import math, re, functools
+from reportlab import isPy3
+from reportlab.lib.rl_accel import fp_str
+from reportlab.lib.utils import asNative, isStr
+import collections
 
 class Color:
     """This class is used to represent color.  Components red, green, blue
@@ -56,25 +59,38 @@
     def __repr__(self):
         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, self.alpha))
-
-    def __cmp__(self,other):
+    @property
+    def __key__(self):
         '''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)
+        (1, True)
         '''
-        if isinstance(other,CMYKColor) or not isinstance(other,Color): return -1
+        return self.red, self.green, self.blue, self.alpha
+
+    def __hash__(self):
+        return hash(self.__key__)
+
+    def __comparable__(self,other):
+        return not isinstance(other,CMYKColor) and isinstance(other,Color)
+
+    def __lt__(self,other):
+        if not self.__comparable__(other): return True
         try:
-            return cmp((self.red, self.green, self.blue, self.alpha),
-                    (other.red, other.green, other.blue, other.alpha))
+            return self.__key__ < other.__key__
         except:
-            return -1
-        return 0
+            pass
+        return True
+
+    def __eq__(self,other):
+        if not self.__comparable__(other): return False
+        try:
+            return self.__key__ == other.__key__
+        except:
+            return False
 
     def rgb(self):
         "Returns a three-tuple of components"
@@ -85,10 +101,10 @@
         return (self.red, self.green, self.blue, self.alpha)
 
     def bitmap_rgb(self):
-        return tuple(map(lambda x: int(x*255)&255, self.rgb()))
+        return tuple([int(x*255)&255 for x in self.rgb()])
 
     def bitmap_rgba(self):
-        return tuple(map(lambda x: int(x*255)&255, self.rgba()))
+        return tuple([int(x*255)&255 for x in self.rgba()])
 
     def hexval(self):
         return '0x%02x%02x%02x' % self.bitmap_rgb()
@@ -118,7 +134,7 @@
 
     def _lookupName(self,D={}):
         if not D:
-            for n,v in getAllNamedColors().iteritems():
+            for n,v in getAllNamedColors().items():
                 if not isinstance(v,CMYKColor):
                     t = v.red,v.green,v.blue
                     if t in D:
@@ -130,7 +146,7 @@
     @property
     def normalizedAlpha(self):
         return self.alpha
-
+if isPy3: Color = functools.total_ordering(Color)
 
 class CMYKColor(Color):
     """This represents colors using the CMYK (cyan, magenta, yellow, black)
@@ -190,14 +206,12 @@
         *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)]
+        L = [self.clone(density=scale - i*dd) for i in range(n)]
         if reverse: L.reverse()
         return L
 
-    def __hash__(self):
-        return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName, self.alpha) )
-
-    def __cmp__(self,other):
+    @property
+    def __key__(self):
         """obvious way to compare colours
         Comparing across the two color models is of limited use.
         >>> cmp(CMYKColor(0,0,0,1),None)
@@ -209,14 +223,10 @@
         >>> 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
+        return self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName, self.alpha
+
+    def __comparable__(self,other):
+        return isinstance(other,CMYKColor)
 
     def cmyk(self):
         "Returns a tuple of four color components - syntactic sugar"
@@ -232,7 +242,7 @@
 
     def _lookupName(self,D={}):
         if not D:
-            for n,v in getAllNamedColors().iteritems():
+            for n,v in getAllNamedColors().items():
                 if isinstance(v,CMYKColor):
                     t = v.cyan,v.magenta,v.yellow,v.black
                     if t in D:
@@ -353,7 +363,8 @@
 
     """ #" for emacs
 
-    if isinstance(val,basestring):
+    if isStr(val):
+        val = asNative(val)
         b = 10
         if val[:1] == '#':
             val = val[1:]
@@ -385,7 +396,7 @@
     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
-        raise ValueError, "Can't interpolate: x=%f is not between %f and %f!" % (x,x0,x1)
+        raise ValueError("Can't interpolate: x=%f is not between %f and %f!" % (x,x0,x1))
     if x<=x0:
         return c0
     elif x>=x1:
@@ -481,7 +492,7 @@
             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
+        raise ValueError("Can't interpolate: Unknown color class %s!" % cname)
 
 def obj_R_G_B(c):
     '''attempt to convert an object to (red,green,blue)'''
@@ -703,9 +714,9 @@
     # uses a singleton for efficiency
     global _namedColors
     if _namedColors is not None: return _namedColors
-    import colors
+    from reportlab.lib import colors
     _namedColors = {}
-    for (name, value) in colors.__dict__.items():
+    for name, value in colors.__dict__.items():
         if isinstance(value, Color):
             _namedColors[name] = value
 
@@ -719,18 +730,18 @@
     '''
     namedColors = getAllNamedColors()
     closest = (10, None, None)  #big number, name, color
-    for (name, color) in namedColors.items():
+    for name, color in namedColors.items():
         distance = colorDistance(aColor, color)
         if distance < closest[0]:
             closest = (distance, name, color)
     if mode<=1:
         s = 'best match is %s, distance %0.4f' % (closest[1], closest[0])
-        if mode==0: print s
+        if mode==0: print(s)
         else: return s
     elif mode==2:
         return (closest[1], closest[0])
     else:
-        raise ValueError, "Illegal value for mode "+str(mode)
+        raise ValueError("Illegal value for mode "+str(mode))
 
 def hue2rgb(m1, m2, h):
     if h<0: h += 1
@@ -816,7 +827,7 @@
             if hsl:
                 R,G,B= hsl2rgb(self.hueVal(n[0]),self.pcVal(n[1]),self.pcVal(n[2]))
             else:
-                R,G,B = map('%' in n[0] and self.rgbPcVal or self.rgbVal,n)
+                R,G,B = list(map('%' in n[0] and self.rgbPcVal or self.rgbVal,n))
 
             return Color(R,G,B,a)
 
@@ -839,7 +850,8 @@
             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):
+        elif isStr(arg):
+            arg = asNative(arg)
             C = cssParse(arg)
             if C: return C
             if arg in self.extraColorsNS: return self.extraColorsNS[arg]
@@ -875,7 +887,7 @@
         progress = 0
         for k, v in kw.items():
             if isinstance(v,(tuple,list)):
-                c = map(lambda x,UNDEF=UNDEF: toColor(x,UNDEF),v)
+                c = list(map(lambda x,UNDEF=UNDEF: toColor(x,UNDEF),v))
                 if isinstance(v,tuple): c = tuple(c)
                 ok = UNDEF not in c
             else:
@@ -1000,8 +1012,8 @@
     return tc
 
 def _chooseEnforceColorSpace(enforceColorSpace):
-    if enforceColorSpace is not None and not callable(enforceColorSpace):
-        if isinstance(enforceColorSpace,basestring): enforceColorSpace=enforceColorSpace.upper()
+    if enforceColorSpace is not None and not isinstance(enforceColorSpace, collections.Callable):
+        if isinstance(enforceColorSpace,str): enforceColorSpace=enforceColorSpace.upper()
         if enforceColorSpace=='CMYK':
             enforceColorSpace = _enforceCMYK
         elif enforceColorSpace=='RGB':
--- a/src/reportlab/lib/corp.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/corp.py	Wed Mar 26 12:32:02 2014 +0000
@@ -78,12 +78,12 @@
                         ] or []
         if _ocolors:
             g.add(definePath(OP,strokeColor=_ocolors[0],strokeWidth=strokeWidth,fillColor=_ocolors[1], dx=dx, dy=dy))
-            print '_ocolors',_ocolors
+            print('_ocolors',_ocolors)
         else:
             P += OP
         if self.showPage and _pagecolors:
             g.add(definePath(PP,strokeColor=_pagecolors[0],strokeWidth=strokeWidth,fillColor=_pagecolors[1], dx=dx, dy=dy))
-            print '_pagecolors',_pagecolors
+            print('_pagecolors',_pagecolors)
         else:
             P += PP
         g.add(definePath(P,strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, dx=dx, dy=dy))
--- a/src/reportlab/lib/extformat.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/extformat.py	Wed Mar 26 12:32:02 2014 +0000
@@ -2,7 +2,13 @@
 #see license.txt for license details
 __version__='''$Id$'''
 __doc__='''Apparently not used anywhere, purpose unknown!'''
-from tokenize import tokenprog
+try:
+	from tokenize import tokenprog
+except ImportError:
+	from tokenize import Token
+	import re
+	tokenprog = re.compile(Token)
+	del Token, re
 import sys
 
 def _matchorfail(text, pos):
@@ -71,15 +77,15 @@
 	percent=79.2
 	class dingo:
 		a=3
-	print(magicformat('''
+	print((magicformat('''
 $%%(df(x,dp=3))s --> $%(df(x,dp=3))s
 $%%(df(x,dp=2,ds=',',ts='.'))s --> $%(df(x,dp=2,ds=',',ts='.'))s
 %%(percent).2f%%%% --> %(percent).2f%%
 %%(dingo.a)s --> %(dingo.a)s
 %%(Z['abc'][0])s --> %(Z['abc'][0])s
-'''))
+''')))
 	def func0(aa=1):
 		def func1(bb=2):
-			print(magicformat('bb=%(bb)s Z=%(Z)r'))
+			print((magicformat('bb=%(bb)s Z=%(Z)r')))
 		func1('BB')
 	func0('AA')
--- a/src/reportlab/lib/fontfinder.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/fontfinder.py	Wed Mar 26 12:32:02 2014 +0000
@@ -58,7 +58,7 @@
 Future plans might include using this to auto-register fonts; and making it
 update itself smartly on repeated instantiation.
 """
-import sys, time, os, cPickle, tempfile
+import sys, time, os, pickle, tempfile
 from xml.sax.saxutils import quoteattr
 try:
     from hashlib import md5
@@ -105,7 +105,7 @@
     def getTag(self):
         "Return an XML tag representation"
         attrs = []
-        for (k, v) in self.__dict__.items():
+        for k, v in self.__dict__.items():
             if k not in ['timeModified']:
                 if v:
                     attrs.append('%s=%s' % (k, quoteattr(str(v))))
@@ -148,7 +148,7 @@
                     self._fontsByFamily[fam].append(font)
                 else:
                     self._fontsByFamily[fam] = [font]
-        names = self._fontsByFamily.keys()
+        names = list(self._fontsByFamily.keys())
         names.sort()
         return names
 
@@ -178,7 +178,7 @@
         selected = []
         for font in self._fonts:
             OK = True
-            for (k, v) in kwds.items():
+            for k, v in kwds.items():
                 if getattr(font, k, None) != v:
                     OK = False
             if OK:
@@ -206,12 +206,12 @@
 
     def save(self, fileName):
         f = open(fileName, 'w')
-        cPickle.dump(self, f)
+        pickle.dump(self, f)
         f.close()
 
     def load(self, fileName):
         f = open(fileName, 'r')
-        finder2 = cPickle.load(f)
+        finder2 = pickle.load(f)
         f.close()
         self.__dict__.update(finder2.__dict__)
 
@@ -310,25 +310,25 @@
     ff.addDirectory(rlFontDir)
     ff.search()
 
-    print 'cache file name...'
-    print ff._getCacheFileName()
+    print('cache file name...')
+    print(ff._getCacheFileName())
 
-    print 'families...'
+    print('families...')
     for familyName in ff.getFamilyNames():
-        print '\t%s' % familyName
+        print('\t%s' % familyName)
 
-    print
-    print 'fonts called Vera:',
+    print()
+    outw = sys.stdout.write
+    outw('fonts called Vera:')
     for font in ff.getFontsInFamily('Bitstream Vera Sans'):
-        print '\t%s' % font.name
-
-    print
-    print 'Bold fonts\n\t'
+        outw(' %s' % font.name)
+    print()
+    outw('Bold fonts\n\t')
     for font in ff.getFontsWithAttributes(isBold=True, isItalic=False):
-        print font.fullName ,
-
-    print 'family report'
-    print ff.getFamilyXmlReport()
+        outw(font.fullName+' ')
+    print()
+    print('family report')
+    print(ff.getFamilyXmlReport())
 
 if __name__=='__main__':
     test()
--- a/src/reportlab/lib/formatters.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/formatters.py	Wed Mar 26 12:32:02 2014 +0000
@@ -94,7 +94,7 @@
     def t(n, s, places=2, decimalSep='.', thousandSep=None, prefix=None, suffix=None):
         f=DecimalFormatter(places,decimalSep,thousandSep,prefix,suffix)
         r = f(n)
-        print "places=%2d dot=%-4s comma=%-4s prefix=%-4s suffix=%-4s result=%10s %s" %(f.places, f.dot, f.comma, f.prefix, f.suffix,r, r==s and 'OK' or 'BAD')
+        print("places=%2d dot=%-4s comma=%-4s prefix=%-4s suffix=%-4s result=%10s %s" %(f.places, f.dot, f.comma, f.prefix, f.suffix,r, r==s and 'OK' or 'BAD'))
     t(1000.9,'1,000.9',1,thousandSep=',')
     t(1000.95,'1,001.0',1,thousandSep=',')
     t(1000.95,'1,001',-1,thousandSep=',')
--- a/src/reportlab/lib/logger.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/logger.py	Wed Mar 26 12:32:02 2014 +0000
@@ -16,7 +16,7 @@
 
     def add(self,fp):
         '''add the file/string fp to the destinations'''
-        if type(fp) is StringType:
+        if isinstance(fp,str):
             if fp in self._fns: return
             fp = open(fn,'wb')
             self._fns[fn] = fp
@@ -24,7 +24,7 @@
 
     def remove(self,fp):
         '''remove the file/string fp from the destinations'''
-        if type(fp) is StringType:
+        if isinstance(fp,str):
             if fp not in self._fns: return
             fn = fp
             fp = self._fns[fn]
--- a/src/reportlab/lib/normalDate.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/normalDate.py	Wed Mar 26 12:32:02 2014 +0000
@@ -33,16 +33,16 @@
 _iso_re = re.compile(r'(\d\d\d\d|\d\d)-(\d\d)-(\d\d)')
 
 def getStdMonthNames():
-    return map(string.lower,_monthName)
+    return list(map(string.lower,_monthName))
 
 def getStdShortMonthNames():
-    return map(lambda x: x[:3],getStdMonthNames())
+    return [x[:3] for x in getStdMonthNames()]
 
 def getStdDayNames():
-    return map(string.lower,_dayOfWeekName)
+    return list(map(string.lower,_dayOfWeekName))
 
 def getStdShortDayNames():
-    return map(lambda x: x[:3],getStdDayNames())
+    return [x[:3] for x in getStdDayNames()]
 
 def isLeapYear(year):
     """determine if specified year is leap year, returns Python boolean"""
@@ -183,7 +183,7 @@
         else:
             daysByMonth = _daysInMonthNormal
         priorMonthDays = 0
-        for m in xrange(self.month() - 1):
+        for m in range(self.month() - 1):
             priorMonthDays = priorMonthDays + daysByMonth[m]
         return self.day() + priorMonthDays
 
@@ -390,14 +390,14 @@
         else:
             daysByMonth = _daysInMonthNormal
         dc = 0; month = 12
-        for m in xrange(len(daysByMonth)):
+        for m in range(len(daysByMonth)):
             dc = dc + daysByMonth[m]
             if dc >= days:
                 month = m + 1
                 break
         # add up the days in prior months
         priorMonthDays = 0
-        for m in xrange(month - 1):
+        for m in range(month - 1):
             priorMonthDays = priorMonthDays + daysByMonth[m]
         day = days - priorMonthDays
         self.setNormalDate((year, month, day))
@@ -427,10 +427,10 @@
         (year, month, day) = self.toTuple()
         days = firstDayOfYear(year) + day - 1
         if self.isLeapYear():
-            for m in xrange(month - 1):
+            for m in range(month - 1):
                 days = days + _daysInMonthLeapYear[m]
         else:
-            for m in xrange(month - 1):
+            for m in range(month - 1):
                 days = days + _daysInMonthNormal[m]
         if year == 1582:
             if month > 10 or (month == 10 and day > 4):
@@ -459,7 +459,7 @@
         (year, month, day, ...)"""
         if isinstance(normalDate,int):
             self.normalDate = normalDate
-        elif isinstance(normalDate,basestring):
+        elif isinstance(normalDate,str):
             try:
                 self.normalDate = int(normalDate)
             except:
@@ -599,20 +599,20 @@
 
 if __name__ == '__main__':
     today = NormalDate()
-    print "NormalDate test:"
-    print "  Today (%s) is: %s %s" % (today, today.dayOfWeekAbbrev(), today.localeFormat())
+    print("NormalDate test:")
+    print("  Today (%s) is: %s %s" % (today, today.dayOfWeekAbbrev(), today.localeFormat()))
     yesterday = today - 1
-    print "  Yesterday was: %s %s" % (yesterday.dayOfWeekAbbrev(), yesterday.localeFormat())
+    print("  Yesterday was: %s %s" % (yesterday.dayOfWeekAbbrev(), yesterday.localeFormat()))
     tomorrow = today + 1
-    print "  Tomorrow will be: %s %s" % (tomorrow.dayOfWeekAbbrev(), tomorrow.localeFormat())
-    print "  Days between tomorrow and yesterday: %d" % (tomorrow - yesterday)
-    print today.formatMS('{d}/{m}/{yy}')
-    print today.formatMS('{dd}/{m}/{yy}')
-    print today.formatMS('{ddd} {d}/{m}/{yy}')
-    print today.formatMS('{dddd} {d}/{m}/{yy}')
-    print today.formatMS('{d}/{mm}/{yy}')
-    print today.formatMS('{d}/{mmm}/{yy}')
-    print today.formatMS('{d}/{mmmm}/{yy}')
-    print today.formatMS('{d}/{m}/{yyyy}')
+    print("  Tomorrow will be: %s %s" % (tomorrow.dayOfWeekAbbrev(), tomorrow.localeFormat()))
+    print("  Days between tomorrow and yesterday: %d" % (tomorrow - yesterday))
+    print(today.formatMS('{d}/{m}/{yy}'))
+    print(today.formatMS('{dd}/{m}/{yy}'))
+    print(today.formatMS('{ddd} {d}/{m}/{yy}'))
+    print(today.formatMS('{dddd} {d}/{m}/{yy}'))
+    print(today.formatMS('{d}/{mm}/{yy}'))
+    print(today.formatMS('{d}/{mmm}/{yy}'))
+    print(today.formatMS('{d}/{mmmm}/{yy}'))
+    print(today.formatMS('{d}/{m}/{yyyy}'))
     b = BusinessDate('20010116')
-    print 'b=',b,'b.scalar()', b.scalar()
+    print('b=',b,'b.scalar()', b.scalar())
--- a/src/reportlab/lib/pdfencrypt.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/pdfencrypt.py	Wed Mar 26 12:32:02 2014 +0000
@@ -3,24 +3,18 @@
 __version__=''' $Id$ '''
 
 """helpers for pdf encryption/decryption"""
-
-import string, sys, os
-try:
-    from hashlib import md5
-except ImportError:
-    from md5 import md5
-
-from reportlab.lib.utils import getStringIO
-import tempfile
-
+import sys, os, tempfile
+from reportlab.lib.utils import getBytesIO, md5, asBytes, int2Byte, char2int, rawUnicode, rawBytes, isPy3
 from reportlab.pdfgen.canvas import Canvas
 from reportlab.pdfbase import pdfutils
+from reportlab.pdfbase.pdfdoc import PDFObject
 from reportlab.platypus.flowables import Flowable
+from reportlab import rl_config
 
 #AR debug hooks - leaving in for now
 CLOBBERID = 0  # set a constant Doc ID to allow comparison with other software like iText
 CLOBBERPERMISSIONS = 0
-DEBUG = 0  # print stuff to trace calculations
+DEBUG = rl_config.debug # print stuff to trace calculations
 
 # permission bits
 reserved1 = 1               # bit 1 must be 0
@@ -34,7 +28,6 @@
 for i in range(6,31):
     higherbits = higherbits | (1<<i)
 
-
 # no encryption
 class StandardEncryption:
     prepared = 0
@@ -77,15 +70,15 @@
     def encode(self, t):
         "encode a string, stream, text"
         if not self.prepared:
-            raise ValueError, "encryption not prepared!"
+            raise ValueError("encryption not prepared!")
         if self.objnum is None:
-            raise ValueError, "not registered in PDF object"
+            raise ValueError("not registered in PDF object")
         return encodePDF(self.key, self.objnum, self.version, t, revision=self.revision)
     def prepare(self, document, overrideID=None):
         # get ready to do encryption
-        if DEBUG: print 'StandardEncryption.prepare(...) - revision %d' % self.revision
+        if DEBUG: print('StandardEncryption.prepare(...) - revision %d' % self.revision)
         if self.prepared:
-            raise ValueError, "encryption already prepared!"
+            raise ValueError("encryption already prepared!")
         # get the unescaped string value of the document id (first array element).
         # we allow one to be passed in instead to permit reproducible tests
         # of our algorithm, but in real life overrideID will always be None
@@ -99,41 +92,40 @@
                 internalID = "xxxxxxxxxxxxxxxx"
 
         if DEBUG:
-            print 'userPassword    = %s' % self.userPassword
-            print 'ownerPassword   = %s' % self.ownerPassword
-            print 'internalID      = %s' % internalID
-        self.P = int(self.permissionBits() - 2**31L)
+            print('userPassword    = %s' % self.userPassword)
+            print('ownerPassword   = %s' % self.ownerPassword)
+            print('internalID      = %s' % internalID)
+        self.P = int(self.permissionBits() - 2**31)
         if CLOBBERPERMISSIONS: self.P = -44 # AR hack
         if DEBUG:
-            print "self.P          = %s" % repr(self.P)
+            print("self.P          = %s" % repr(self.P))
         self.O = computeO(self.userPassword, self.ownerPassword, self.revision)
         if DEBUG:
-            print "self.O (as hex) = %s" % hexText(self.O)
+            print("self.O (as hex) = %s" % hexText(self.O))
 
         #print "\nself.O", self.O, repr(self.O)
         self.key = encryptionkey(self.userPassword, self.O, self.P, internalID, revision=self.revision)
         if DEBUG:
-            print "self.key (hex)  = %s" % hexText(self.key)
+            print("self.key (hex)  = %s" % hexText(self.key))
         self.U = computeU(self.key, revision=self.revision, documentId=internalID)
         if DEBUG:
-            print "self.U (as hex) = %s" % hexText(self.U)
+            print("self.U (as hex) = %s" % hexText(self.U))
         self.objnum = self.version = None
         self.prepared = 1
     def register(self, objnum, version):
         # enter a new direct object
         if not self.prepared:
-            raise ValueError, "encryption not prepared!"
+            raise ValueError("encryption not prepared!")
         self.objnum = objnum
         self.version = version
     def info(self):
         # the representation of self in file if any (should be None or PDFDict)
         if not self.prepared:
-            raise ValueError, "encryption not prepared!"
+            raise ValueError("encryption not prepared!")
         return StandardEncryptionDictionary(O=self.O, U=self.U, P=self.P, revision=self.revision)
 
-class StandardEncryptionDictionary:
+class StandardEncryptionDictionary(PDFObject):
     __RefOnly__ = 1
-    __PDFObject__ = True
     def __init__(self, O, U, P, revision):
         self.O, self.U, self.P = O,U,P
         self.revision = revision
@@ -160,64 +152,54 @@
 28 BF 4E 5E 4E 75 8A 41 64 00 4E 56 FF FA 01 08
 2E 2E 00 B6 D0 68 3E 80 2F 0C A9 FE 64 53 69 7A
 """
-if hasattr(padding,'join'):
+if isPy3:
+    def xorKey(num,key):
+        "xor's each byte of the key with the number, which is <256"
+        if num==0: return key
+        return bytes(num^k for k in key)
+else:
     def xorKey(num,key):
         "xor's each bytes of the key with the number, which is <256"
         if num==0: return key
-        from operator import xor
-        return ''.join(map(chr,map(xor,len(key)*[num],map(ord,key))))
-else:
-    def xorKey(num, key):
-        "xor's each bytes of the key with the number, which is <256"
-        from operator import xor
-        out = ''
-        for ch in key:
-            out = out + chr(xor(num, ord(ch)))
-        return out
-
-def hexchar(x):
-    return chr(string.atoi(x, 16))
+        return ''.join(chr(num^ord(k)) for k in key)
 
 def hexText(text):
     "a legitimate way to show strings in PDF"
-    out = ''
-    for char in text:
-        out = out + '%02X' % ord(char)
-    return '<' + out + '>'
+    return '<' + ''.join('%02X' % ord(c) for c in rawUnicode(text)) + '>'
 
 def unHexText(hexText):
-    assert hexText[0] == '<', 'bad hex text'
-    assert hexText[-1] == '>', 'bad hex text'
+    equalityCheck(hexText[0], '<', 'bad hex text')
+    equalityCheck(hexText[-1], '>', 'bad hex text')
     hexText = hexText[1:-1]
-    out = ''
+    out = b''
     for i in range(int(len(hexText)/2.0)):
         slice = hexText[i*2: i*2+2]
-        char = chr(eval('0x'+slice))
+        char = int2Byte(eval('0x'+slice))
         out = out + char
     return out
 
-PadString = string.join(map(hexchar, string.split(string.strip(padding))), "")
+PadString = rawBytes(''.join(chr(int(c, 16)) for c in padding.strip().split()))
 
 def encryptionkey(password, OwnerKey, Permissions, FileId1, revision=2):
     # FileId1 is first string of the fileid array
     # add padding string
     #AR force same as iText example
     #Permissions =  -1836   #int(Permissions - 2**31)
-    password = password + PadString
+    password = asBytes(password) + PadString
     # truncate to 32 bytes
     password = password[:32]
     # translate permissions to string, low order byte first
     p = Permissions# + 2**32L
-    permissionsString = ""
+    permissionsString = b""
     for i in range(4):
         byte = (p & 0xff)    # seems to match what iText does
         p = p>>8
-        permissionsString = permissionsString + chr(byte % 256)
+        permissionsString += int2Byte(byte % 256)
 
-    hash = md5(password)
-    hash.update(OwnerKey)
-    hash.update(permissionsString)
-    hash.update(FileId1)
+    hash = md5(asBytes(password))
+    hash.update(asBytes(OwnerKey))
+    hash.update(asBytes(permissionsString))
+    hash.update(asBytes(FileId1))
 
     md5output = hash.digest()
 
@@ -227,23 +209,24 @@
         for x in range(50):
             md5output = md5(md5output).digest()
         key = md5output[:16]
-    if DEBUG: print 'encryptionkey(%s,%s,%s,%s,%s)==>%s' % tuple(map(lambda x: hexText(str(x)),(password, OwnerKey, Permissions, FileId1, revision, key)))
+    if DEBUG: print('encryptionkey(%s,%s,%s,%s,%s)==>%s' % tuple([hexText(str(x)) for x in (password, OwnerKey, Permissions, FileId1, revision, key)]))
     return key
 
 def computeO(userPassword, ownerPassword, revision):
     from reportlab.lib.arciv import ArcIV
     #print 'digest of hello is %s' % md5('hello').digest()
     assert revision in (2,3), 'Unknown algorithm revision %s' % revision
-    if ownerPassword in (None, ''):
+    if not ownerPassword:
         ownerPassword = userPassword
 
-    ownerPad = ownerPassword + PadString
+    ownerPad = asBytes(ownerPassword) + PadString
     ownerPad = ownerPad[0:32]
 
-    password = userPassword + PadString
+    password = asBytes(userPassword) + PadString
     userPad = password[:32]
 
     digest = md5(ownerPad).digest()
+    if DEBUG: print('PadString=%s\nownerPad=%s\npassword=%s\nuserPad=%s\ndigest=%s\nrevision=%s' % (ascii(PadString),ascii(ownerPad),ascii(password),ascii(userPad),ascii(digest),revision))
     if revision == 2:
         O = ArcIV(digest[:5]).encode(userPad)
     elif revision == 3:
@@ -254,7 +237,7 @@
         for i in range(20):
             thisKey = xorKey(i, digest)
             O = ArcIV(thisKey).encode(O)
-    if DEBUG: print 'computeO(%s,%s,%s)==>%s' % tuple(map(lambda x: hexText(str(x)),(userPassword, ownerPassword, revision,O)))
+    if DEBUG: print('computeO(%s,%s,%s)==>%s' % tuple([hexText(str(x)) for x in (userPassword, ownerPassword, revision,O)]))
     return O
 
 def computeU(encryptionkey, encodestring=PadString,revision=2,documentId=None):
@@ -264,16 +247,16 @@
     elif revision == 3:
         assert documentId is not None, "Revision 3 algorithm needs the document ID!"
         h = md5(PadString)
-        h.update(documentId)
+        h.update(rawBytes(documentId))
         tmp = h.digest()
         tmp = ArcIV(encryptionkey).encode(tmp)
         for n in range(1,20):
             thisKey = xorKey(n, encryptionkey)
             tmp = ArcIV(thisKey).encode(tmp)
         while len(tmp) < 32:
-            tmp = tmp + '\000'
+            tmp += b'\0'
         result = tmp
-    if DEBUG: print 'computeU(%s,%s,%s,%s)==>%s' % tuple(map(lambda x: hexText(str(x)),(encryptionkey, encodestring,revision,documentId,result)))
+    if DEBUG: print('computeU(%s,%s,%s,%s)==>%s' % tuple([hexText(str(x)) for x in (encryptionkey, encodestring,revision,documentId,result)]))
     return result
 
 def checkU(encryptionkey, U):
@@ -281,8 +264,8 @@
     #print len(decoded), len(U), len(PadString)
     if decoded!=PadString:
         if len(decoded)!=len(PadString):
-            raise ValueError, "lengths don't match! (password failed)"
-        raise ValueError, "decode of U doesn't match fixed padstring (password failed)"
+            raise ValueError("lengths don't match! (password failed)")
+        raise ValueError("decode of U doesn't match fixed padstring (password failed)")
 
 def encodePDF(key, objectNumber, generationNumber, string, revision=2):
     "Encodes a string or stream"
@@ -291,12 +274,12 @@
     newkey = key
     n = objectNumber
     for i in range(3):
-        newkey = newkey + chr(n & 0xff)
+        newkey += int2Byte(n & 0xff)
         n = n>>8
     # extend 2 bytes of the generationNumber
     n = generationNumber
     for i in range(2):
-        newkey = newkey + chr(n & 0xff)
+        newkey += int2Byte(n & 0xff)
         n = n>>8
     md5output = md5(newkey).digest()
     if revision == 2:
@@ -306,39 +289,40 @@
     from reportlab.lib.arciv import ArcIV
     encrypted = ArcIV(key).encode(string)
     #print 'encrypted=', hexText(encrypted)
-    if DEBUG: print 'encodePDF(%s,%s,%s,%s,%s)==>%s' % tuple(map(lambda x: hexText(str(x)),(key, objectNumber, generationNumber, string, revision,encrypted)))
+    if DEBUG: print('encodePDF(%s,%s,%s,%s,%s)==>%s' % tuple([hexText(str(x)) for x in (key, objectNumber, generationNumber, string, revision,encrypted)]))
     return encrypted
 
-    ######################################################################
-    #
-    #  quick tests of algorithm, should be moved elsewhere
-    #
-    ######################################################################
-
+def equalityCheck(observed,expected,label):
+    assert observed==expected,'%s\n expected=%s\n observed=%s' % (label,expected,observed)
+######################################################################
+#
+#  quick tests of algorithm, should be moved elsewhere
+#
+######################################################################
 def test():
     # do a 40 bit example known to work in Acrobat Reader 4.0
-    enc = StandardEncryption('userpass','ownerpass', strength=40)
-    enc.prepare(None, overrideID = 'xxxxxxxxxxxxxxxx')
+    enc = StandardEncryption('User','Owner', strength=40)
+    enc.prepare(None, overrideID='xxxxxxxxxxxxxxxx')
 
-    expectedO = '<6A835A92E99DCEA39D51CF34FDBDA42162690D2BD5F8E08E3008F91FE5B8512E>'
-    expectedU = '<9997BDB61E7F288DAE6A8C4246A8F9CDCDBBC3D909D703CABA5D65A0CC6D4083>'
-    expectedKey = '<A3A68B5CB1>'  # 5 byte key = 40 bits
+    expectedO = '<FA7F558FACF8205D25A7F1ABFA02629F707AE7B0211A2BB26F5DF4C30F684301>'
+    expectedU = '<09F26CF46190AF8F93B304AD50C16B615DC43C228C9B2D2EA34951A80617B2B1>'
+    expectedKey = '<BB2C00EB3D>'    # 5 byte key = 40 bits
 
-    assert hexText(enc.O) == expectedO, '40 bit unexpected O value %s' % hexText(enc.O)
-    assert hexText(enc.U) == expectedU, '40 bit unexpected U value %s' % hexText(enc.U)
-    assert hexText(enc.key) == expectedKey, '40 bit unexpected key value %s' % hexText(enc.key)
+    equalityCheck(hexText(enc.O),expectedO, '40 bit O value')
+    equalityCheck(hexText(enc.U),expectedU, '40 bit U value')
+    equalityCheck(hexText(enc.key),expectedKey, '40 bit key value')
 
     # now for 128 bit example
     enc = StandardEncryption('userpass','ownerpass', strength=128)
     enc.prepare(None, overrideID = 'xxxxxxxxxxxxxxxx')
 
-    expectedO = '<19BDBD240E0866B84C49AEEF7E2350045DB8BDAE96E039BF4E3F12DAC3427DB6>'
-    expectedU = '<564747DADFF35F5F2078A2CA1705B50800000000000000000000000000000000>'
-    expectedKey = '<DC1E019846B1EEABA0CDB8ED6D53B5C4>'  # 16 byte key = 128 bits
+    expectedO = '<68E5704AC779A5F0CD89704406587A52F25BF61CADC56A0F8DB6C4DB0052534D>'
+    expectedU = '<A9AE45CDE827FE0B7D6536267948836A00000000000000000000000000000000>'
+    expectedKey = '<13DDE7585D9BE366C976DDD56AF541D1>'  # 16 byte key = 128 bits
 
-    assert hexText(enc.O) == expectedO, '128 bit unexpected O value %s' % hexText(enc.O)
-    assert hexText(enc.U) == expectedU, '128 bit unexpected U value %s' % hexText(enc.U)
-    assert hexText(enc.key) == expectedKey, '128 bit unexpected key value %s' % hexText(enc.key)
+    equalityCheck(hexText(enc.O), expectedO, '128 bit O value')
+    equalityCheck(hexText(enc.U), expectedU, '128 bit U value')
+    equalityCheck(hexText(enc.key), expectedKey, '128 key value')
 
     ######################################################################
     #
@@ -403,12 +387,12 @@
 See http://developer.reportlab.com''')
 
     (bboxInfo, pickledForms) = storeFormsInMemory(inputPDF, all=1, BBoxes=1)
-    names = bboxInfo.keys()
+    names = list(bboxInfo.keys())
 
     firstPageSize = bboxInfo['PageForms0'][2:]
 
     #now make a new PDF document
-    buf = getStringIO()
+    buf = getBytesIO()
     canv = Canvas(buf, pagesize=firstPageSize)
 
     # set a standard ID while debugging
@@ -503,7 +487,7 @@
     argv = list(sys_argv)[1:]
     if len(argv)>0:
         if argv[0] == '-h' or argv[0] == 'help':
-            print usage
+            print(usage)
             return
         if len(argv)<2:
             raise ValueError("Must include a filename and one or more arguments!")
@@ -563,11 +547,8 @@
             if thisarg[0] in argv:
                 pos = argv.index(thisarg[0])
                 if thisarg[0] in binaryrequired:
-                    #try:
                     if argv[pos+1] not in ('1', '0'):
-                        raise "%s value must be either '1' or '0'!" % thisarg[1]
-                    #except:
-                        #raise "Unable to set %s." % thisarg[4]
+                        raise ValueError("%s value must be either '1' or '0'!" % thisarg[1])
                 try:
                     if argv[pos+1] not in known_modes:
                         if thisarg[0] in binaryrequired:
@@ -575,7 +556,7 @@
                         else:
                             exec(thisarg[1] +' = argv[pos+1]')
                         if verbose:
-                            print "%s set to: '%s'." % (thisarg[3], argv[pos+1])
+                            print("%s set to: '%s'." % (thisarg[3], argv[pos+1]))
                         argv.remove(argv[pos+1])
                         argv.remove(thisarg[0])
                 except:
@@ -583,17 +564,17 @@
 
         if verbose>4:
             #useful if feeling paranoid and need to double check things at this point...
-            print "\ninfile:", infile
-            print "STRENGTH:", STRENGTH
-            print "SAVEFILE:", SAVEFILE
-            print "USER:", USER
-            print "OWNER:", OWNER
-            print "PRINTABLE:", PRINTABLE
-            print "MODIFIABLE:", MODIFIABLE
-            print "COPYPASTABLE:", COPYPASTABLE
-            print "ANNOTATABLE:", ANNOTATABLE
-            print "SAVEFILE:", SAVEFILE
-            print "VERBOSE:", verbose
+            print("\ninfile:", infile)
+            print("STRENGTH:", STRENGTH)
+            print("SAVEFILE:", SAVEFILE)
+            print("USER:", USER)
+            print("OWNER:", OWNER)
+            print("PRINTABLE:", PRINTABLE)
+            print("MODIFIABLE:", MODIFIABLE)
+            print("COPYPASTABLE:", COPYPASTABLE)
+            print("ANNOTATABLE:", ANNOTATABLE)
+            print("SAVEFILE:", SAVEFILE)
+            print("VERBOSE:", verbose)
 
 
         if SAVEFILE == 'encrypted.pdf':
@@ -608,21 +589,21 @@
                                     strength=STRENGTH)
 
         if verbose:
-            print "wrote output file '%s'(%s bytes)\n  owner password is '%s'\n  user password is '%s'" % (SAVEFILE, filesize, OWNER, USER)
+            print("wrote output file '%s'(%s bytes)\n  owner password is '%s'\n  user password is '%s'" % (SAVEFILE, filesize, OWNER, USER))
 
         if len(argv)>0:
-            raise "\nUnrecognised arguments : %s\nknown arguments are:\n%s" % (str(argv)[1:-1], known_modes)
+            raise valueError("\nUnrecognised arguments : %s\nknown arguments are:\n%s" % (str(argv)[1:-1], known_modes))
     else:
-        print usage
+        print(usage)
 
 def main():
     from reportlab.rl_config import verbose
     scriptInterp()
 
 if __name__=="__main__": #NO RUNTESTS
-    a = filter(lambda x: x[:7]=='--debug',sys.argv)
+    a = [x for x in sys.argv if x[:7]=='--debug']
     if a:
-        sys.argv = filter(lambda x: x[:7]!='--debug',sys.argv)
+        sys.argv = [x for x in sys.argv if x[:7]!='--debug']
         DEBUG = len(a)
     if '--test' in sys.argv: test()
     else: main()
--- a/src/reportlab/lib/pygments2xpre.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/pygments2xpre.py	Wed Mar 26 12:32:02 2014 +0000
@@ -33,11 +33,11 @@
     l = get_lexer_by_name(language)
     
     h = HtmlFormatter()
-    from StringIO import StringIO
+    from io import StringIO
     out = StringIO()
     highlight(s,l,h,out)
     styles = [(cls, style.split(';')[0].split(':')[1].strip())
-                for cls, (style, ttype, level) in h.class2style.iteritems()
+                for cls, (style, ttype, level) in h.class2style.items()
                 if cls and style and style.startswith('color:')]
     return _2xpre(out.getvalue(),styles)
 
@@ -57,12 +57,12 @@
         fmt = pygments2xpre(src)
         S(XPreformatted(fmt, style=styC))
     doc.build(S.__self__)
-    print 'saved pygments2xpre.pdf'
+    print('saved pygments2xpre.pdf')
 
 if __name__=='__main__':
     import sys
     filenames = sys.argv[1:]
     if not filenames:
-        print 'usage:  pygments2xpre.py file1.py [file2.py] [...]'
+        print('usage:  pygments2xpre.py file1.py [file2.py] [...]')
         sys.exit(0)
     convertSourceFiles(filenames)
--- a/src/reportlab/lib/randomtext.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/randomtext.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,5 +1,5 @@
 #!/bin/env python
-#Copyright ReportLab Europe Ltd. 2000-2012
+#Copyright ReportLab Europe Ltd. 2000-2013
 #see license.txt for license details
 #history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/randomtext.py
 
@@ -305,7 +305,7 @@
     prevparts = []
     newparts = []
     output = []
-    for i in xrange(times):
+    for i in range(times):
         for partlist in (leadins, subjects, verbs, objects):
             while 1:
                 part = random.choice(partlist)
@@ -322,7 +322,7 @@
     if not getattr(rl_config,'_random',None):
         rl_config._random = 1
         import random
-        random.seed(2342471922L)
+        random.seed(2342471922)
         del random
 del rl_config
 
@@ -362,10 +362,10 @@
         else:
             sentences = 5
         try:
-            print randomText(theme,sentences)
+            print(randomText(theme,sentences))
         except:
-            print>>sys.stderr,"Usage: randomtext.py [theme [#sentences]]"
-            print>>sys.stderr," theme in chomsky|STARTUP|COMPUTERS|BLAH|BUZZWORD|STARTREK|PRINTING|PYTHON"
+            sys.stderr.write("Usage: randomtext.py [theme [#sentences]]\n")
+            sys.stderr.write(" theme in chomsky|STARTUP|COMPUTERS|BLAH|BUZZWORD|STARTREK|PRINTING|PYTHON\n")
             raise
     else:
-        print chomsky(5)
+        print(chomsky(5))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/reportlab/lib/rl_accel.py	Wed Mar 26 12:32:02 2014 +0000
@@ -0,0 +1,352 @@
+#this is the interface module that imports all from the C extension _rl_accel
+_c_funcs = {}
+_py_funcs = {}
+### NOTE!  FP_STR SHOULD PROBABLY ALWAYS DO A PYTHON STR() CONVERSION ON ARGS
+### IN CASE THEY ARE "LAZY OBJECTS".  ACCELLERATOR DOESN'T DO THIS (YET)
+__all__ = list(filter(None,'''
+        fp_str
+        unicode2T1
+        instanceStringWidthT1
+        instanceStringWidthTTF
+        asciiBase85Encode
+        asciiBase85Decode
+        escapePDF
+        sameFrag
+        calcChecksum
+        add32
+        hex32
+        '''.split()))
+import __main__
+testing = getattr(__main__,'_rl_testing',False)
+del __main__
+
+for fn in __all__:
+    try:
+        exec('from reportlab.lib._rl_accel import %s as f' % fn)
+        _c_funcs[fn] = f
+        if testing: _py_funcs[fn] = None
+    except ImportError:
+        _py_funcs[fn] = None
+
+if _py_funcs:
+    from reportlab.lib.utils import isBytes, isUnicode, isSeq, isPy3, rawBytes, asNative
+    from math import log
+    from struct import unpack
+
+if 'fp_str' in _py_funcs:
+    _log_10 = lambda x,log=log,_log_e_10=log(10.0): log(x)/_log_e_10
+    _fp_fmts = "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"
+    def fp_str(*a):
+        '''convert separate arguments (or single sequence arg) into space separated numeric strings'''
+        if len(a)==1 and isSeq(a[0]): a = a[0]
+        s = []
+        A = s.append
+        for i in a:
+            sa =abs(i)
+            if sa<=1e-7: A('0')
+            else:
+                l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6)
+                n = _fp_fmts[l]%i
+                if l:
+                    j = len(n)
+                    while j:
+                        j -= 1
+                        if n[j]!='0':
+                            if n[j]!='.': j += 1
+                            break
+                    n = n[:j]
+                A((n[0]!='0' or len(n)==1) and n or n[1:])
+        return ' '.join(s)
+
+    #hack test for comma users
+    if ',' in fp_str(0.25):
+        _FP_STR = _fp_str
+        def _fp_str(*a):
+            return _FP_STR(*a).replace(',','.')
+    _py_funcs['fp_str'] = fp_str
+
+if 'unicode2T1' in _py_funcs:
+    def unicode2T1(utext,fonts):
+        '''return a list of (font,string) pairs representing the unicode text'''
+        R = []
+        font, fonts = fonts[0], fonts[1:]
+        enc = font.encName
+        if 'UCS-2' in enc:
+            enc = 'UTF16'
+        while utext:
+            try:
+                if isUnicode(utext):
+                    s = utext.encode(enc)
+                else:
+                    s = utext
+                R.append((font,s))
+                break
+            except UnicodeEncodeError as e:
+                i0, il = e.args[2:4]
+                if i0:
+                    R.append((font,utext[:i0].encode(enc)))
+                if fonts:
+                    R.extend(unicode2T1(utext[i0:il],fonts))
+                else:
+                    R.append((font._notdefFont,font._notdefChar*(il-i0)))
+                utext = utext[il:]
+        return R
+    _py_funcs['unicode2T1'] = unicode2T1
+
+if 'instanceStringWidthT1' in _py_funcs:
+    if isPy3:
+        def instanceStringWidthT1(self, text, size, encoding='utf8'):
+            """This is the "purist" approach to width"""
+            if not isUnicode(text): text = text.decode(encoding)
+            return sum([sum(map(f.widths.__getitem__,t)) for f, t in unicode2T1(text,[self]+self.substitutionFonts)])*0.001*size
+    else:
+        def instanceStringWidthT1(self, text, size, encoding='utf8'):
+            """This is the "purist" approach to width"""
+            if not isUnicode(text): text = text.decode(encoding)
+            return sum([sum(map(f.widths.__getitem__,list(map(ord,t)))) for f, t in unicode2T1(text,[self]+self.substitutionFonts)])*0.001*size
+    _py_funcs['instanceStringWidthT1'] = instanceStringWidthT1
+
+if 'instanceStringWidthTTF' in _py_funcs:
+    def instanceStringWidthTTF(self, text, size, encoding='utf-8'):
+        "Calculate text width"
+        if not isUnicode(text):
+            text = text.decode(encoding or 'utf-8')
+        g = self.face.charWidths.get
+        dw = self.face.defaultWidth
+        return 0.001*size*sum([g(ord(u),dw) for u in text])
+    _py_funcs['instanceStringWidthTTF'] = instanceStringWidthTTF
+
+if 'hex32' in _py_funcs:
+    def hex32(i):
+        return '0X%8.8X' % (int(i)&0xFFFFFFFF)
+    _py_funcs['hex32'] = hex32
+
+if 'add32' in _py_funcs:
+    def add32(x, y):
+        "Calculate (x + y) modulo 2**32"
+        return (x+y) & 0xFFFFFFFF
+    _py_funcs['add32'] = add32
+
+if 'calcChecksum' in _py_funcs:
+    def calcChecksum(data):
+        """Calculates TTF-style checksums"""
+        data = rawBytes(data)
+        if len(data)&3: data = data + (4-(len(data)&3))*b"\0"
+        return sum(unpack(">%dl" % (len(data)>>2), data)) & 0xFFFFFFFF
+    _py_funcs['calcChecksum'] = calcChecksum
+
+if 'escapePDF' in _py_funcs:
+    _ESCAPEDICT={}
+    for c in range(256):
+        if c<32 or c>=127:
+            _ESCAPEDICT[c]= '\\%03o' % c
+        elif c in (ord('\\'),ord('('),ord(')')):
+            _ESCAPEDICT[c] = '\\'+chr(c)
+        else:
+            _ESCAPEDICT[c] = chr(c)
+    del c
+    #Michael Hudson donated this
+    def escapePDF(s):
+        r = []
+        for c in s:
+            if not type(c) is int:
+                c = ord(c)
+            r.append(_ESCAPEDICT[c])
+        return ''.join(r)
+    _py_funcs['escapePDF'] = escapePDF
+
+if 'asciiBase85Encode' in _py_funcs:
+    def asciiBase85Encode(input):
+        """Encodes input using ASCII-Base85 coding.
+
+        This is a compact encoding used for binary data within
+        a PDF file.  Four bytes of binary data become five bytes of
+        ASCII.  This is the default method used for encoding images."""
+        doOrd =  not isPy3 or isUnicode(input)
+        # special rules apply if not a multiple of four bytes.
+        whole_word_count, remainder_size = divmod(len(input), 4)
+        cut = 4 * whole_word_count
+        body, lastbit = input[0:cut], input[cut:]
+
+        out = [].append
+        for i in range(whole_word_count):
+            offset = i*4
+            b1 = body[offset]
+            b2 = body[offset+1]
+            b3 = body[offset+2]
+            b4 = body[offset+3]
+            if doOrd:
+                b1 = ord(b1)
+                b2 = ord(b2)
+                b3 = ord(b3)
+                b4 = ord(b4)
+
+            if b1<128:
+                num = (((((b1<<8)|b2)<<8)|b3)<<8)|b4
+            else:
+                num = 16777216 * b1 + 65536 * b2 + 256 * b3 + b4
+
+            if num == 0:
+                #special case
+                out('z')
+            else:
+                #solve for five base-85 numbers
+                temp, c5 = divmod(num, 85)
+                temp, c4 = divmod(temp, 85)
+                temp, c3 = divmod(temp, 85)
+                c1, c2 = divmod(temp, 85)
+                assert ((85**4) * c1) + ((85**3) * c2) + ((85**2) * c3) + (85*c4) + c5 == num, 'dodgy code!'
+                out(chr(c1+33))
+                out(chr(c2+33))
+                out(chr(c3+33))
+                out(chr(c4+33))
+                out(chr(c5+33))
+
+        # now we do the final bit at the end.  I repeated this separately as
+        # the loop above is the time-critical part of a script, whereas this
+        # happens only once at the end.
+
+        #encode however many bytes we have as usual
+        if remainder_size > 0:
+            lastbit += (4-len(lastbit))*('\0' if doOrd else b'\000')
+            b1 = lastbit[0]
+            b2 = lastbit[1]
+            b3 = lastbit[2]
+            b4 = lastbit[3]
+            if doOrd:
+                b1 = ord(b1)
+                b2 = ord(b2)
+                b3 = ord(b3)
+                b4 = ord(b4)
+
+            num = 16777216 * b1 + 65536 * b2 + 256 * b3 + b4
+
+            #solve for c1..c5
+            temp, c5 = divmod(num, 85)
+            temp, c4 = divmod(temp, 85)
+            temp, c3 = divmod(temp, 85)
+            c1, c2 = divmod(temp, 85)
+
+            #print 'encoding: %d %d %d %d -> %d -> %d %d %d %d %d' % (
+            #    b1,b2,b3,b4,num,c1,c2,c3,c4,c5)
+            lastword = chr(c1+33) + chr(c2+33) + chr(c3+33) + chr(c4+33) + chr(c5+33)
+            #write out most of the bytes.
+            out(lastword[0:remainder_size + 1])
+
+        #terminator code for ascii 85
+        out('~>')
+        return ''.join(out.__self__)
+    _py_funcs['asciiBase85Encode'] = asciiBase85Encode
+
+if 'asciiBase85Decode' in _py_funcs:
+    def asciiBase85Decode(input):
+        """Decodes input using ASCII-Base85 coding.
+
+        This is not normally used - Acrobat Reader decodes for you
+        - but a round trip is essential for testing."""
+        #strip all whitespace
+        stripped = ''.join(asNative(input).split())
+        #check end
+        assert stripped[-2:] == '~>', 'Invalid terminator for Ascii Base 85 Stream'
+        stripped = stripped[:-2]  #chop off terminator
+
+        #may have 'z' in it which complicates matters - expand them
+        stripped = stripped.replace('z','!!!!!')
+        # special rules apply if not a multiple of five bytes.
+        whole_word_count, remainder_size = divmod(len(stripped), 5)
+        #print '%d words, %d leftover' % (whole_word_count, remainder_size)
+        #assert remainder_size != 1, 'invalid Ascii 85 stream!'
+        cut = 5 * whole_word_count
+        body, lastbit = stripped[0:cut], stripped[cut:]
+
+        out = [].append
+        for i in range(whole_word_count):
+            offset = i*5
+            c1 = ord(body[offset]) - 33
+            c2 = ord(body[offset+1]) - 33
+            c3 = ord(body[offset+2]) - 33
+            c4 = ord(body[offset+3]) - 33
+            c5 = ord(body[offset+4]) - 33
+
+            num = ((85**4) * c1) + ((85**3) * c2) + ((85**2) * c3) + (85*c4) + c5
+
+            temp, b4 = divmod(num,256)
+            temp, b3 = divmod(temp,256)
+            b1, b2 = divmod(temp, 256)
+
+            assert  num == 16777216 * b1 + 65536 * b2 + 256 * b3 + b4, 'dodgy code!'
+            out(chr(b1))
+            out(chr(b2))
+            out(chr(b3))
+            out(chr(b4))
+
+        #decode however many bytes we have as usual
+        if remainder_size > 0:
+            while len(lastbit) < 5:
+                lastbit = lastbit + '!'
+            c1 = ord(lastbit[0]) - 33
+            c2 = ord(lastbit[1]) - 33
+            c3 = ord(lastbit[2]) - 33
+            c4 = ord(lastbit[3]) - 33
+            c5 = ord(lastbit[4]) - 33
+            num = (((85*c1+c2)*85+c3)*85+c4)*85 + (c5
+                     +(0,0,0xFFFFFF,0xFFFF,0xFF)[remainder_size])
+            temp, b4 = divmod(num,256)
+            temp, b3 = divmod(temp,256)
+            b1, b2 = divmod(temp, 256)
+            assert  num == 16777216 * b1 + 65536 * b2 + 256 * b3 + b4, 'dodgy code!'
+            #print 'decoding: %d %d %d %d %d -> %d -> %d %d %d %d' % (
+            #    c1,c2,c3,c4,c5,num,b1,b2,b3,b4)
+
+            #the last character needs 1 adding; the encoding loses
+            #data by rounding the number to x bytes, and when
+            #divided repeatedly we get one less
+            if remainder_size == 2:
+                lastword = chr(b1)
+            elif remainder_size == 3:
+                lastword = chr(b1) + chr(b2)
+            elif remainder_size == 4:
+                lastword = chr(b1) + chr(b2) + chr(b3)
+            else:
+                lastword = ''
+            out(lastword)
+
+        r = ''.join(out.__self__)
+        return r.encode('latin1') if isUnicode(input) else r
+    _py_funcs['asciiBase85Decode'] = asciiBase85Decode
+
+if 'sameFrag' in _py_funcs:
+    def sameFrag(f,g):
+        'returns 1 if two ParaFrags map out the same'
+        if (hasattr(f,'cbDefn') or hasattr(g,'cbDefn')
+                or hasattr(f,'lineBreak') or hasattr(g,'lineBreak')): return 0
+        for a in ('fontName', 'fontSize', 'textColor', 'rise', 'underline', 'strike', 'link', "backColor"):
+            if getattr(f,a,None)!=getattr(g,a,None): return 0
+        return 1
+    _py_funcs['sameFrag'] = sameFrag
+
+G=globals()
+for fn in __all__:
+    f = _c_funcs[fn] if fn in _c_funcs else _py_funcs[fn]
+    if not f:
+        raise RuntimeError('function %s is not properly defined' % fn)
+    G[fn] = f
+del fn, f, G
+
+if __name__=='__main__':
+    import sys, os
+    for modname in 'reportlab.lib.rl_accel','reportlab.lib._rl_accel':
+        for cmd  in (
+            #"unicode2T1('abcde fghi . jkl ; mno',fonts)",
+            #"unicode2T1(u'abcde fghi . jkl ; mno',fonts)",
+            "_instanceStringWidthU(font,'abcde fghi . jkl ; mno',10)",
+            "_instanceStringWidthU(font,u'abcde fghi . jkl ; mno',10)",
+            ):
+            print('%s %s' % (modname,cmd))
+            s=';'.join((
+                "from reportlab.pdfbase.pdfmetrics import getFont",
+                "from %s import unicode2T1,_instanceStringWidthU" % modname,
+                "fonts=[getFont('Helvetica')]+getFont('Helvetica').substitutionFonts""",
+                "font=fonts[0]",
+                ))
+            os.system('%s -m timeit -s"%s" "%s"' % (sys.executable,s,cmd))
--- a/src/reportlab/lib/rparsexml.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/rparsexml.py	Wed Mar 26 12:32:02 2014 +0000
@@ -55,13 +55,12 @@
 
 RequirePyRXP = 0        # set this to 1 to disable the nonvalidating fallback parser.
 
-import string
 try:
     #raise ImportError, "dummy error"
     simpleparse = 0
     import pyRXPU
     def warnCB(s):
-        print s
+        print(s)
     pyRXP_parser = pyRXPU.Parser(
                         ErrorOnValidityErrors=1,
                         NoNoDTDWarning=1,
@@ -69,7 +68,7 @@
                         ExpandGeneralEntities=1,
                         warnCB = warnCB,
                         srcName='string input',
-                        ReturnUTF8 = 1,
+                        ReturnUTF8 = 0,
                         )
     def parsexml(xmlText, oneOutermostTag=0,eoCB=None,entityReplacer=None,parseOpts={}):
         pyRXP_parser.eoCB = eoCB
@@ -88,18 +87,17 @@
 #replacelist = []
 def unEscapeContentList(contentList):
     result = []
-    from string import replace
     for e in contentList:
         if "&" in e:
             for (old, new) in replacelist:
-                e = replace(e, old, new)
+                e = e.replace(old, new)
         result.append(e)
     return result
 
 def parsexmlSimple(xmltext, oneOutermostTag=0,eoCB=None,entityReplacer=unEscapeContentList):
     """official interface: discard unused cursor info"""
     if RequirePyRXP:
-        raise ImportError, "pyRXP not found, fallback parser disabled"
+        raise ImportError("pyRXP not found, fallback parser disabled")
     (result, cursor) = parsexml0(xmltext,entityReplacer=entityReplacer)
     if oneOutermostTag:
         return result[2][0]
@@ -118,12 +116,11 @@
 def skip_prologue(text, cursor):
     """skip any prologue found after cursor, return index of rest of text"""
     ### NOT AT ALL COMPLETE!!! definitely can be confused!!!
-    from string import find
     prologue_elements = ("!DOCTYPE", "?xml", "!--")
     done = None
     while done is None:
         #print "trying to skip:", repr(text[cursor:cursor+20])
-        openbracket = find(text, "<", cursor)
+        openbracket = text.find("<", cursor)
         if openbracket<0: break
         past = openbracket+1
         found = None
@@ -131,9 +128,9 @@
             le = len(e)
             if text[past:past+le]==e:
                 found = 1
-                cursor = find(text, ">", past)
+                cursor = text.find(">", past)
                 if cursor<0:
-                    raise ValueError, "can't close prologue %r" % e
+                    raise ValueError("can't close prologue %r" % e)
                 cursor = cursor+1
         if found is None:
             done=1
@@ -142,14 +139,13 @@
 
 def parsexml0(xmltext, startingat=0, toplevel=1,
         # snarf in some globals
-        strip=string.strip, split=string.split, find=string.find, entityReplacer=unEscapeContentList,
+        entityReplacer=unEscapeContentList,
         #len=len, None=None
         #LENCDATAMARKER=LENCDATAMARKER, CDATAMARKER=CDATAMARKER
         ):
     """simple recursive descent xml parser...
        return (dictionary, endcharacter)
        special case: comment returns (None, endcharacter)"""
-    #from string import strip, split, find
     #print "parsexml0", repr(xmltext[startingat: startingat+10])
     # DEFAULTS
     NameString = NONAME
@@ -158,13 +154,13 @@
         #if verbose: print "at top level"
         #if startingat!=0:
         #    raise ValueError, "have to start at 0 for top level!"
-        xmltext = strip(xmltext)
+        xmltext = xmltext.strip()
     cursor = startingat
     #look for interesting starting points
-    firstbracket = find(xmltext, "<", cursor)
+    firstbracket = xmltext.find("<", cursor)
     afterbracket2char = xmltext[firstbracket+1:firstbracket+3]
     #print "a", repr(afterbracket2char)
-    #firstampersand = find(xmltext, "&", cursor)
+    #firstampersand = xmltext.find("&", cursor)
     #if firstampersand>0 and firstampersand<firstbracket:
     #    raise ValueError, "I don't handle ampersands yet!!!"
     docontents = 1
@@ -177,7 +173,7 @@
                 if entityReplacer: ContentList = entityReplacer(ContentList)
                 return (NameString, AttDict, ContentList, ExtraStuff), len(xmltext)
             else:
-                raise ValueError, "no tags at non-toplevel %s" % repr(xmltext[cursor:cursor+20])
+                raise ValueError("no tags at non-toplevel %s" % repr(xmltext[cursor:cursor+20]))
     #D = {}
     L = []
     # look for start tag
@@ -190,15 +186,15 @@
             cursor = skip_prologue(xmltext, cursor)
             #break
     elif firstbracket<0:
-            raise ValueError, "non top level entry should be at start tag: %s" % repr(xmltext[:10])
+            raise ValueError("non top level entry should be at start tag: %s" % repr(xmltext[:10]))
     # special case: CDATA
     elif afterbracket2char=="![" and xmltext[firstbracket:firstbracket+9]=="<![CDATA[":
             #print "in CDATA", cursor
             # skip straight to the close marker
             startcdata = firstbracket+9
-            endcdata = find(xmltext, CDATAENDMARKER, startcdata)
+            endcdata = xmltext.find(CDATAENDMARKER, startcdata)
             if endcdata<0:
-                raise ValueError, "unclosed CDATA %s" % repr(xmltext[cursor:cursor+20])
+                raise ValueError("unclosed CDATA %s" % repr(xmltext[cursor:cursor+20]))
             NameString = CDATAMARKER
             ContentList = [xmltext[startcdata: endcdata]]
             cursor = endcdata+len(CDATAENDMARKER)
@@ -206,18 +202,18 @@
     # special case COMMENT
     elif afterbracket2char=="!-" and xmltext[firstbracket:firstbracket+4]=="<!--":
             #print "in COMMENT"
-            endcommentdashes = find(xmltext, "--", firstbracket+4)
+            endcommentdashes = xmltext.find("--", firstbracket+4)
             if endcommentdashes<firstbracket:
-                raise ValueError, "unterminated comment %s" % repr(xmltext[cursor:cursor+20])
+                raise ValueError("unterminated comment %s" % repr(xmltext[cursor:cursor+20]))
             endcomment = endcommentdashes+2
             if xmltext[endcomment]!=">":
-                raise ValueError, "invalid comment: contains double dashes %s" % repr(xmltext[cursor:cursor+20])
+                raise ValueError("invalid comment: contains double dashes %s" % repr(xmltext[cursor:cursor+20]))
             return (None, endcomment+1) # shortcut exit
     else:
             # get the rest of the tag
             #if verbose: print "parsing start tag"
             # make sure the tag isn't in doublequote pairs
-            closebracket = find(xmltext, ">", firstbracket)
+            closebracket = xmltext.find(">", firstbracket)
             noclose = closebracket<0
             startsearch = closebracket+1
             pastfirstbracket = firstbracket+1
@@ -229,7 +225,7 @@
                     #print "simple case", tagcontent
                     tagcontent = tagcontent[:-1]
                     docontents = None
-                name = strip(tagcontent)
+                name = tagcontent.strip()
                 NameString = name
                 cursor = startsearch
             else:
@@ -237,18 +233,18 @@
                     # check double quotes
                     stop = None
                     # not inside double quotes! (the split should have odd length)
-                    if noclose or len(split(tagcontent+".", '"'))% 2:
+                    if noclose or len((tagcontent+".").split('"'))% 2:
                         stop=1
                     while stop is None:
-                        closebracket = find(xmltext, ">", startsearch)
+                        closebracket = xmltext.find(">", startsearch)
                         startsearch = closebracket+1
                         noclose = closebracket<0
                         tagcontent = xmltext[pastfirstbracket:closebracket]
                         # not inside double quotes! (the split should have odd length)
-                        if noclose or len(split(tagcontent+".", '"'))% 2:
+                        if noclose or len((tagcontent+".").split('"'))% 2:
                             stop=1
                 if noclose:
-                    raise ValueError, "unclosed start tag %s" % repr(xmltext[firstbracket:firstbracket+20])
+                    raise ValueError("unclosed start tag %s" % repr(xmltext[firstbracket:firstbracket+20]))
                 cursor = startsearch
                 #cursor = closebracket+1
                 # handle simple tag /> syntax
@@ -258,12 +254,12 @@
                     tagcontent = tagcontent[:-1]
                     docontents = None
                 #tagcontent = xmltext[firstbracket+1:closebracket]
-                tagcontent = strip(tagcontent)
-                taglist = split(tagcontent, "=")
+                tagcontent = tagcontent.strip()
+                taglist = tagcontent.split("=")
                 #if not taglist:
                 #    raise ValueError, "tag with no name %s" % repr(xmltext[firstbracket:firstbracket+20])
                 taglist0 = taglist[0]
-                taglist0list = split(taglist0)
+                taglist0list = taglist0.split()
                 #if len(taglist0list)>2:
                 #    raise ValueError, "bad tag head %s" % repr(taglist0)
                 name = taglist0list[0]
@@ -281,25 +277,25 @@
                     #print "looking for attribute named", attributename
                     attentry = taglist[taglistindex]
                     taglistindex = taglistindex+1
-                    attentry = strip(attentry)
+                    attentry = attentry.strip()
                     if attentry[0]!='"':
-                        raise ValueError, "attribute value must start with double quotes" + repr(attentry)
+                        raise ValueError("attribute value must start with double quotes" + repr(attentry))
                     while '"' not in attentry[1:]:
                         # must have an = inside the attribute value...
                         if taglistindex>lasttaglistindex:
-                            raise ValueError, "unclosed value " + repr(attentry)
+                            raise ValueError("unclosed value " + repr(attentry))
                         nextattentry = taglist[taglistindex]
                         taglistindex = taglistindex+1
                         attentry = "%s=%s" % (attentry, nextattentry)
-                    attentry = strip(attentry) # only needed for while loop...
-                    attlist = split(attentry)
+                    attentry = attentry.strip() # only needed for while loop...
+                    attlist = attentry.split()
                     nextattname = attlist[-1]
                     attvalue = attentry[:-len(nextattname)]
-                    attvalue = strip(attvalue)
+                    attvalue = attvalue.strip()
                     try:
                         first = attvalue[0]; last=attvalue[-1]
                     except:
-                        raise ValueError, "attvalue,attentry,attlist="+repr((attvalue, attentry,attlist))
+                        raise ValueError("attvalue,attentry,attlist="+repr((attvalue, attentry,attlist)))
                     if first==last=='"' or first==last=="'":
                         attvalue = attvalue[1:-1]
                     #print attributename, "=", attvalue
@@ -310,7 +306,7 @@
         #print "now looking for end tag"
         ContentList = L
     while docontents is not None:
-            nextopenbracket = find(xmltext, "<", cursor)
+            nextopenbracket = xmltext.find("<", cursor)
             if nextopenbracket<cursor:
                 #if verbose: print "no next open bracket found"
                 if name==NONAME:
@@ -321,15 +317,15 @@
                     if remainder:
                         L.append(remainder)
                 else:
-                    raise ValueError, "no close bracket for %s found after %s" % (name,repr(xmltext[cursor: cursor+20]))
+                    raise ValueError("no close bracket for %s found after %s" % (name,repr(xmltext[cursor: cursor+20])))
             # is it a close bracket?
             elif xmltext[nextopenbracket+1]=="/":
                 #print "found close bracket", repr(xmltext[nextopenbracket:nextopenbracket+20])
-                nextclosebracket = find(xmltext, ">", nextopenbracket)
+                nextclosebracket = xmltext.find(">", nextopenbracket)
                 if nextclosebracket<nextopenbracket:
-                    raise ValueError, "unclosed close tag %s" % repr(xmltext[nextopenbracket: nextopenbracket+20])
+                    raise ValueError("unclosed close tag %s" % repr(xmltext[nextopenbracket: nextopenbracket+20]))
                 closetagcontents = xmltext[nextopenbracket+2: nextclosebracket]
-                closetaglist = split(closetagcontents)
+                closetaglist = closetagcontents.split()
                 #if len(closetaglist)!=1:
                     #print closetagcontents
                     #raise ValueError, "bad close tag format %s" % repr(xmltext[nextopenbracket: nextopenbracket+20])
@@ -338,12 +334,11 @@
                 #if verbose: print "closetag name is", closename
                 if name!=closename:
                     prefix = xmltext[:cursor]
-                    endlinenum = len(split(prefix, "\n"))
+                    endlinenum = len(prefix.split("\n"))
                     prefix = xmltext[:startingat]
-                    linenum = len(split(prefix, "\n"))
-                    raise ValueError, \
-                       "at lines %s...%s close tag name doesn't match %s...%s %s" %(
-                       linenum, endlinenum, repr(name), repr(closename), repr(xmltext[cursor: cursor+100]))
+                    linenum = len(prefix.split("\n"))
+                    raise ValueError("at lines %s...%s close tag name doesn't match %s...%s %s" %(
+                       linenum, endlinenum, repr(name), repr(closename), repr(xmltext[cursor: cursor+100])))
                 remainder = xmltext[cursor:nextopenbracket]
                 if remainder:
                     #if verbose: print "remainder", repr(remainder)
@@ -365,7 +360,7 @@
                     L.append(parsetree)
         # maybe should check for trailing garbage?
         # toplevel:
-        #    remainder = strip(xmltext[cursor:])
+        #    remainder = xmltext[cursor:].strip()
         #    if remainder:
         #        raise ValueError, "trailing garbage at top level %s" % repr(remainder[:20])
     if ContentList:
@@ -376,28 +371,27 @@
 import types
 def pprettyprint(parsedxml):
     """pretty printer mainly for testing"""
-    st = types.StringType
+    st = bytes
     if type(parsedxml) is st:
         return parsedxml
     (name, attdict, textlist, extra) = parsedxml
     if not attdict: attdict={}
-    join = string.join
     attlist = []
     for k in attdict.keys():
         v = attdict[k]
         attlist.append("%s=%s" % (k, repr(v)))
-    attributes = join(attlist, " ")
+    attributes = " ".join(attlist)
     if not name and attributes:
-        raise ValueError, "name missing with attributes???"
+        raise ValueError("name missing with attributes???")
     if textlist is not None:
         # with content
-        textlistpprint = map(pprettyprint, textlist)
-        textpprint = join(textlistpprint, "\n")
+        textlistpprint = list(map(pprettyprint, textlist))
+        textpprint = "\n".join(textlistpprint)
         if not name:
             return textpprint # no outer tag
         # indent it
-        nllist = string.split(textpprint, "\n")
-        textpprint = "   "+join(nllist, "\n   ")
+        nllist = textpprint.split("\n")
+        textpprint = "   "+ ("\n   ".join(nllist))
         return "<%s %s>\n%s\n</%s>" % (name, attributes, textpprint, name)
     # otherwise must be a simple tag
     return "<%s %s/>" % (name, attributes)
@@ -408,14 +402,14 @@
     from pprint import pprint
     now = time()
     D = parsexmlSimple(s)
-    print "DONE", time()-now
+    print("DONE", time()-now)
     if dump&4:
         pprint(D)
     #pprint(D)
     if dump&1:
-        print "============== reformatting"
+        print("============== reformatting")
         p = pprettyprint(D)
-        print p
+        print(p)
 
 def test():
     testparse("""<this type="xml">text &lt;&gt;<b>in</b> <funnytag foo="bar"/> xml</this>
@@ -437,6 +431,6 @@
     now = time()
     for f in filenames:
         t = open(f).read()
-        print "parsing", f
+        print("parsing", f)
         testparse(t)
-    print "elapsed", time()-now
+    print("elapsed", time()-now)
--- a/src/reportlab/lib/sequencer.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/sequencer.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,6 +1,5 @@
-#Copyright ReportLab Europe Ltd. 2000-2012
+#Copyright ReportLab Europe Ltd. 2000-2013
 #see license.txt for license details
-#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/sequencer.py
 __version__=''' $Id$ '''
 __doc__="""A Sequencer class counts things. It aids numbering and formatting lists."""
 __all__='''Sequencer getSequencer setSequencer'''.split()
@@ -12,12 +11,13 @@
 # fredrik@pythonware.com
 # http://www.pythonware.com
 
-_RN_TEMPLATES = [ 0, 01, 011, 0111, 012, 02, 021, 0211, 02111, 013 ]
+_RN_TEMPLATES = [ 0, 0o1, 0o11, 0o111, 0o12, 0o2, 0o21, 0o211, 0o2111, 0o13 ]
 _RN_LETTERS = "IVXLCDM"
+from reportlab import isPy3
 
 def _format_I(value):
     if value < 0 or value > 3999:
-        raise ValueError, "illegal value"
+        raise ValueError("illegal value")
     str = ""
     base = -1
     while value:
@@ -26,7 +26,7 @@
         while tmp:
             tmp, index = divmod(tmp, 8)
             str = _RN_LETTERS[index+base] + str
-        base = base + 2
+        base += 2
     return str
 
 def _format_i(num):
@@ -78,18 +78,24 @@
             self._value = self._base
 
     def next(self):
-        self._value = self._value + 1
+        self._value += 1
         v = self._value
         for counter in self._resets:
             counter.reset()
         return v
+    __next__ = next
 
     def _this(self):
         return self._value
 
-    def nextf(self):
-        """Returns next value formatted"""
-        return self._formatter(self.next())
+    if isPy3:
+        def nextf(self):
+            """Returns next value formatted"""
+            return self._formatter(next(self))
+    else:
+        def nextf(self):
+            """Returns next value formatted"""
+            return self._formatter(self.__next__())
 
     def thisf(self):
         return self._formatter(self._this())
@@ -159,12 +165,29 @@
             counter = self._defaultCounter
         return self._getCounter(counter)._this()
 
-    def next(self, counter=None):
-        """Retrieves the numeric value for the given counter, then
-        increments it by one.  New counters start at one."""
-        if not counter:
-            counter = self._defaultCounter
-        return self._getCounter(counter).next()
+    if isPy3:
+        def __next__(self):
+            """Retrieves the numeric value for the given counter, then
+            increments it by one.  New counters start at one."""
+            return next(self._getCounter(self._defaultCounter))
+
+        def next(self,counter=None):
+            if not counter:
+                return next(self)
+            else:
+                dc = self._defaultCounter
+                try:
+                    self._defaultCounter = counter
+                    return next(self)
+                finally:
+                    self._defaultCounter = dc
+    else:
+        def next(self, counter=None):
+            """Retrieves the numeric value for the given counter, then
+            increments it by one.  New counters start at one."""
+            if not counter:
+                counter = self._defaultCounter
+            return self._getCounter(counter).next()
 
     def thisf(self, counter=None):
         if not counter:
@@ -219,12 +242,12 @@
 
     def dump(self):
         """Write current state to stdout for diagnostics"""
-        counters = self._counters.items()
+        counters = list(self._counters.items())
         counters.sort()
-        print 'Sequencer dump:'
+        print('Sequencer dump:')
         for (key, counter) in counters:
-            print '    %s: value = %d, base = %d, format example = %s' % (
-                key, counter._this(), counter._base, counter.thisf())
+            print('    %s: value = %d, base = %d, format example = %s' % (
+                key, counter._this(), counter._base, counter.thisf()))
 
 """Your story builder needs to set this to"""
 _sequencer = None
@@ -252,52 +275,50 @@
 
 def test():
     s = Sequencer()
-    print 'Counting using default sequence: %d %d %d' % (s.next(),s.next(), s.next())
-    print 'Counting Figures: Figure %d, Figure %d, Figure %d' % (
-        s.next('figure'), s.next('figure'), s.next('figure'))
-    print 'Back to default again: %d' % s.next()
+    print('Counting using default sequence: %d %d %d' % (next(s),next(s), next(s)))
+    print('Counting Figures: Figure %d, Figure %d, Figure %d' % (
+        s.next('figure'), s.next('figure'), s.next('figure')))
+    print('Back to default again: %d' % next(s))
     s.setDefaultCounter('list1')
-    print 'Set default to list1: %d %d %d' % (s.next(),s.next(), s.next())
+    print('Set default to list1: %d %d %d' % (next(s),next(s), next(s)))
     s.setDefaultCounter()
-    print 'Set default to None again: %d %d %d' % (s.next(),s.next(), s.next())
-    print
-    print 'Creating Appendix counter with format A, B, C...'
+    print('Set default to None again: %d %d %d' % (next(s),next(s), next(s)))
+    print()
+    print('Creating Appendix counter with format A, B, C...')
     s.setFormat('Appendix', 'A')
-    print '    Appendix %s, Appendix %s, Appendix %s' % (
-        s.nextf('Appendix'),    s.nextf('Appendix'),s.nextf('Appendix'))
+    print('    Appendix %s, Appendix %s, Appendix %s' % (
+        s.nextf('Appendix'),    s.nextf('Appendix'),s.nextf('Appendix')))
 
     def format_french(num):
         return ('un','deux','trois','quatre','cinq')[(num-1)%5]
-    print
-    print 'Defining a custom format with french words:'
+    print()
+    print('Defining a custom format with french words:')
     s.registerFormat('french', format_french)
     s.setFormat('FrenchList', 'french')
-    print '   ',
-    for i in range(1,6):
-        print s.nextf('FrenchList'),
-    print
-    print 'Chaining H1 and H2 - H2 goes back to one when H1 increases'
+    print('   ' +(' '.join(str(s.nextf('FrenchList')) for i in range(1,6))))
+    print()
+    print('Chaining H1 and H2 - H2 goes back to one when H1 increases')
     s.chain('H1','H2')
-    print '    H1 = %d' % s.next('H1')
-    print '      H2 = %d' % s.next('H2')
-    print '      H2 = %d' % s.next('H2')
-    print '      H2 = %d' % s.next('H2')
-    print '    H1 = %d' % s.next('H1')
-    print '      H2 = %d' % s.next('H2')
-    print '      H2 = %d' % s.next('H2')
-    print '      H2 = %d' % s.next('H2')
-    print
-    print 'GetItem notation - append a plus to increment'
-    print '    seq["Appendix"] = %s' % s["Appendix"]
-    print '    seq["Appendix+"] = %s' % s["Appendix+"]
-    print '    seq["Appendix+"] = %s' % s["Appendix+"]
-    print '    seq["Appendix"] = %s' % s["Appendix"]
-    print
-    print 'Finally, string format notation for nested lists.  Cool!'
-    print 'The expression ("Figure %(Chapter)s.%(Figure+)s" % seq) gives:'
-    print '    Figure %(Chapter)s.%(Figure+)s' % s
-    print '    Figure %(Chapter)s.%(Figure+)s' % s
-    print '    Figure %(Chapter)s.%(Figure+)s' % s
+    print('    H1 = %d' % s.next('H1'))
+    print('      H2 = %d' % s.next('H2'))
+    print('      H2 = %d' % s.next('H2'))
+    print('      H2 = %d' % s.next('H2'))
+    print('    H1 = %d' % s.next('H1'))
+    print('      H2 = %d' % s.next('H2'))
+    print('      H2 = %d' % s.next('H2'))
+    print('      H2 = %d' % s.next('H2'))
+    print()
+    print('GetItem notation - append a plus to increment')
+    print('    seq["Appendix"] = %s' % s["Appendix"])
+    print('    seq["Appendix+"] = %s' % s["Appendix+"])
+    print('    seq["Appendix+"] = %s' % s["Appendix+"])
+    print('    seq["Appendix"] = %s' % s["Appendix"])
+    print()
+    print('Finally, string format notation for nested lists.  Cool!')
+    print('The expression ("Figure %(Chapter)s.%(Figure+)s" % seq) gives:')
+    print('    Figure %(Chapter)s.%(Figure+)s' % s)
+    print('    Figure %(Chapter)s.%(Figure+)s' % s)
+    print('    Figure %(Chapter)s.%(Figure+)s' % s)
 
 
 if __name__=='__main__':
--- a/src/reportlab/lib/set_ops.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/set_ops.py	Wed Mar 26 12:32:02 2014 +0000
@@ -6,15 +6,14 @@
 __doc__="""From before Python had a Set class..."""
 
 import types
-import string
 
 def __set_coerce(t, S):
-    if t is types.ListType:
+    if t is list:
         return list(S)
-    elif t is types.TupleType:
+    elif t is tuple:
         return tuple(S)
-    elif t is types.StringType:
-        return string.join(S, '')
+    elif t is bytes:
+        return ''.join(S)
     return S
 
 def unique(seq):
@@ -26,7 +25,7 @@
 
 def intersect(seq1, seq2):
     result = []
-    if type(seq1) != type(seq2) and type(seq2) == types.StringType: seq2 = list(seq2)
+    if type(seq1) != type(seq2) and type(seq2) == bytes: seq2 = list(seq2)
     for i in seq1:
         if i in seq2 and i not in result: result.append(i)
     return __set_coerce(type(seq1), result)
@@ -34,8 +33,8 @@
 def union(seq1, seq2):
     if type(seq1) == type(seq2):
         return unique(seq1 + seq2)
-    if type(seq1) == types.ListType or type(seq2) == types.ListType:
+    if type(seq1) == list or type(seq2) == list:
         return unique(list(seq1) + list(seq2))
-    if type(seq1) == types.TupleType or type(seq2) == types.TupleType:
+    if type(seq1) == tuple or type(seq2) == tuple:
         return unique(tuple(seq1) + tuple(seq2))
     return unique(list(seq1) + list(seq2))
--- a/src/reportlab/lib/styles.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/styles.py	Wed Mar 26 12:32:02 2014 +0000
@@ -62,7 +62,7 @@
 
     def _setKwds(self,**kw):
         #step three - copy keywords if any
-        for (key, value) in kw.items():
+        for key, value in kw.items():
              self.__dict__[key] = value
 
     def __repr__(self):
@@ -73,20 +73,20 @@
         use if you have been hacking the styles.  This is
         used by __init__"""
         if self.parent:
-            for (key, value) in self.parent.__dict__.items():
+            for key, value in self.parent.__dict__.items():
                 if (key not in ['name','parent']):
                     self.__dict__[key] = value
 
     def listAttrs(self, indent=''):
-        print indent + 'name =', self.name
-        print indent + 'parent =', self.parent
-        keylist = self.__dict__.keys()
+        print(indent + 'name =', self.name)
+        print(indent + 'parent =', self.parent)
+        keylist = list(self.__dict__.keys())
         keylist.sort()
         keylist.remove('name')
         keylist.remove('parent')
         for key in keylist:
             value = self.__dict__.get(key, None)
-            print indent + '%s = %s' % (key, value)
+            print(indent + '%s = %s' % (key, value))
 
     def clone(self, name, parent=None, **kwds):
         r = self.__class__(name,parent)
@@ -221,16 +221,16 @@
             self.byAlias[alias] = style
 
     def list(self):
-        styles = self.byName.items()
+        styles = list(self.byName.items())
         styles.sort()
         alii = {}
-        for (alias, style) in self.byAlias.items():
+        for (alias, style) in list(self.byAlias.items()):
             alii[style] = alias
         for (name, style) in styles:
             alias = alii.get(style, None)
-            print name, alias
+            print(name, alias)
             style.listAttrs('    ')
-            print
+            print()
 
 def testStyles():
     pNormal = ParagraphStyle('Normal',None)
@@ -239,7 +239,7 @@
     pNormal.leading = 14.4
 
     pNormal.listAttrs()
-    print
+    print()
     pPre = ParagraphStyle('Literal', pNormal)
     pPre.fontName = 'Courier'
     pPre.listAttrs()
--- a/src/reportlab/lib/testutils.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/testutils.py	Wed Mar 26 12:32:02 2014 +0000
@@ -1,5 +1,8 @@
-#Copyright ReportLab Europe Ltd. 2000-2012
+#Copyright ReportLab Europe Ltd. 2000-2013
 #see license.txt for license details
+import __main__
+__main__._rl_testing=True
+del __main__
 __version__='''$Id$'''
 __doc__="""Provides support for the test suite.
 
@@ -9,9 +12,13 @@
 nothing more than "reportlab.whatever..."
 """
 
-import sys, os, string, fnmatch, copy, re
-from ConfigParser import ConfigParser
+import sys, os, fnmatch, re
+try:
+    from configparser import ConfigParser
+except ImportError:
+    from ConfigParser import ConfigParser
 import unittest
+from reportlab.lib.utils import isCompactDistro, __loader__, rl_isdir, asUnicode
 
 # Helper functions.
 def isWritable(D):
@@ -85,7 +92,7 @@
     if sys._getframe(depth).f_locals.get('__name__')=='__main__':
         outDir = outputfile('')
         if outDir!=_OUTDIR:
-            print 'Logs and output files written to folder "%s"' % outDir
+            print('Logs and output files written to folder "%s"' % outDir)
 
 def makeSuiteForClasses(*classes):
     "Return a test suite with tests loaded from provided classes."
@@ -106,7 +113,6 @@
     """
 
     join = os.path.join
-    split = string.split
 
     # If CVS subfolder doesn't exist return empty list.
     try:
@@ -119,7 +125,7 @@
     for line in f.readlines():
         if folders and line[0] == 'D' \
            or files and line[0] != 'D':
-            entry = split(line, '/')[1]
+            entry = line.split('/')[1]
             if entry:
                 allEntries.append(join(folder, entry))
 
@@ -139,7 +145,7 @@
 
         # This seems to allow for newlines inside values
         # of the config file, but be careful!!
-        val = string.replace(value, '\n', '')
+        val = value.replace('\n', '')
 
         if self.pat.match(val):
             return eval(val)
@@ -161,13 +167,12 @@
             self.stack = [directory]
             self.files = []
         else:
-            from reportlab.lib.utils import isCompactDistro, __loader__, rl_isdir
             if not isCompactDistro() or not __loader__ or not rl_isdir(directory):
                 raise ValueError('"%s" is not a directory' % directory)
             self.directory = directory[len(__loader__.archive)+len(os.sep):]
             pfx = self.directory+os.sep
             n = len(pfx)
-            self.files = map(lambda x, n=n: x[n:],filter(lambda x,pfx=pfx: x.startswith(pfx),__loader__._files.keys()))
+            self.files = list(map(lambda x, n=n: x[n:],list(filter(lambda x,pfx=pfx: x.startswith(pfx),list(__loader__._files.keys())))))
             self.stack = []
 
     def __getitem__(self, index):
@@ -216,7 +221,7 @@
         "Filters all items from files matching patterns to ignore."
 
         indicesToDelete = []
-        for i in xrange(len(files)):
+        for i in range(len(files)):
             f = files[i]
             for p in self.ignoredPatterns:
                 if fnmatch.fnmatch(f, p):
@@ -243,7 +248,7 @@
         cvsFiles = getCVSEntries(folder)
         if cvsFiles:
             indicesToDelete = []
-            for i in xrange(len(files)):
+            for i in range(len(files)):
                 f = files[i]
                 if join(folder, f) not in cvsFiles:
                     indicesToDelete.append(i)
@@ -273,14 +278,11 @@
 
     def setUp(self):
         "Remember sys.path and current working directory."
-
-        self._initialPath = copy.copy(sys.path)
+        self._initialPath = sys.path[:]
         self._initialWorkDir = os.getcwd()
 
-
     def tearDown(self):
         "Restore previous sys.path and working directory."
-
         sys.path = self._initialPath
         os.chdir(self._initialWorkDir)
 
@@ -327,7 +329,13 @@
         p = os.popen(fmt % (sys.executable,self.scriptName),'r')
         out = p.read()
         if self.verbose:
-            print out
+            print(out)
         status = p.close()
         assert os.path.isfile(self.outFileName), "File %s not created!" % self.outFileName
 
+def equalStrings(a,b,enc='utf8'):
+    return a==b if type(a)==type(b) else asUnicode(a,enc)==asUnicode(b,enc)
+
+def eqCheck(r,x):
+    if r!=x:
+        print('Strings unequal\nexp: %s\ngot: %s' % (ascii(x),ascii(r)))
--- a/src/reportlab/lib/textsplit.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/textsplit.py	Wed Mar 26 12:32:02 2014 +0000
@@ -12,10 +12,10 @@
 
 __version__=''' $Id$ '''
 
-from types import StringType, UnicodeType
 from unicodedata import category
 from reportlab.pdfbase.pdfmetrics import stringWidth
 from reportlab.rl_config import _FUZZ
+from reportlab.lib.utils import isUnicode
 
 CANNOT_START_LINE = [
     #strongly prohibited e.g. end brackets, stop, exclamation...
@@ -41,7 +41,7 @@
     return (ord(ch) >= 0x3000)
     
 def getCharWidths(word, fontName, fontSize):
-    """Returns a list of glyph widths.  Should be easy to optimize in _rl_accel
+    """Returns a list of glyph widths.
 
     >>> getCharWidths('Hello', 'Courier', 10)
     [6.0, 6.0, 6.0, 6.0, 6.0]
@@ -72,7 +72,7 @@
     >>> wordSplit('HelloWorld', 31, 'Courier', 10)
     [[1.0, 'Hello'], [1.0, 'World']]
     """
-    if type(word) is not UnicodeType:
+    if not isUnicode(word):
         uword = word.decode(encoding)
     else:
         uword = word
@@ -80,7 +80,7 @@
     charWidths = getCharWidths(uword, fontName, fontSize)
     lines = dumbSplit(uword, charWidths, maxWidths)
 
-    if type(word) is not UnicodeType:
+    if not isUnicode(word):
         lines2 = []
         #convert back
         for (extraSpace, text) in lines:
@@ -115,7 +115,7 @@
     (u'\u65e5\u672c\u8a9e', u'\u306f\u96e3\u3057\u3044\u3067\u3059\u306d\uff01')
     """
     if not isinstance(maxWidths,(list,tuple)): maxWidths = [maxWidths]
-    assert type(word) is UnicodeType
+    assert isUnicode(word)
     lines = []
     i = widthUsed = lineStartPos = 0
     maxWidth = maxWidths[0]
@@ -141,7 +141,7 @@
                 #  - reversion to Kanji (which would be a good split point)
                 #  - in the worst case, roughly half way back along the line
                 limitCheck = (lineStartPos+i)>>1        #(arbitrary taste issue)
-                for j in xrange(i-1,limitCheck,-1):
+                for j in range(i-1,limitCheck,-1):
                     cj = word[j]
                     if category(cj)=='Zs' or ord(cj)>=0x3000:
                         k = j+1
@@ -224,7 +224,7 @@
 #
 #  http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
 import re
-rx=re.compile(u"([\u2e80-\uffff])", re.UNICODE)
+rx=re.compile("([\u2e80-\uffff])", re.UNICODE)
 def cjkwrap(text, width, encoding="utf8"):
      return reduce(lambda line, word, width=width: '%s%s%s' %
                 (line,
@@ -232,10 +232,10 @@
                        + len(word.split('\n',1)[0] ) >= width) or
                       line[-1:] == '\0' and 2],
                  word),
-                rx.sub(r'\1\0 ', unicode(text,encoding)).split(' ')
+                rx.sub(r'\1\0 ', str(text,encoding)).split(' ')
             ).replace('\0', '').encode(encoding)
 
 if __name__=='__main__':
     import doctest
-    import textsplit
+    from reportlab.lib import textsplit
     doctest.testmod(textsplit)
--- a/src/reportlab/lib/units.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/units.py	Wed Mar 26 12:32:02 2014 +0000
@@ -27,4 +27,4 @@
         if s[-4:]=='pica': return float(s[:-4])*pica
         return float(s)
     except:
-        raise ValueError, "Can't convert '%s' to length" % s
+        raise ValueError("Can't convert '%s' to length" % s)
--- a/src/reportlab/lib/utils.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/utils.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,28 +4,251 @@
 __version__=''' $Id$ '''
 __doc__='''Gazillions of miscellaneous internal utility functions'''
 
-import os, sys, imp, time
+import os, sys, imp, time, types
+from base64 import decodestring as base64_decodestring, encodestring as base64_encodestring
+try:
+    from cPickle import dumps as pickle_dumps, loads as pickle_loads, dump as pickle_dump, load as pickle_load
+except ImportError:
+    from pickle import dumps as pickle_dumps, loads as pickle_loads, dump as pickle_dump, load as pickle_load
+from reportlab import isPy3
+from reportlab.lib.logger import warnOnce
+from reportlab.lib.rltempfile import get_rl_tempfile, get_rl_tempdir, _rl_getuid
+
 try:
     from hashlib import md5
-except:
-    from md5 import md5
-from reportlab.lib.logger import warnOnce
-from rltempfile import get_rl_tempfile, get_rl_tempdir, _rl_getuid
+except ImportError:
+    import md5
+
+def isFunction(v):
+    return type(v) == type(isFunction)
+
+class c:
+    def m(self): pass
 
-def isSeqType(v,_st=(tuple,list)):
+def isMethod(v,mt=type(c.m)):
+    return type(v) == mt
+del c
+
+def isModule(v):
+    return type(v) == type(sys)
+
+def isSeq(v,_st=(tuple,list)):
     return isinstance(v,_st)
 
-if sys.hexversion<0x2030000:
-    True = 1
-    False = 0
+def isNative(v):
+    return isinstance(v, str)
 
-if sys.hexversion >= 0x02000000:
+#isStr is supposed to be for arbitrary stringType
+#isBytes for bytes strings only
+#isUnicode for proper unicode
+if isPy3:
+    bytesT = bytes
+    unicodeT = str
+    strTypes = (str,bytes)
     def _digester(s):
-        return md5(s).hexdigest()
+        return md5(s if isBytes(s) else s.encode('utf8')).hexdigest()
+
+    def asBytes(v,enc='utf8'):
+        return v if isinstance(v,bytes) else v.encode(enc)
+
+    def asUnicode(v,enc='utf8'):
+        return v if isinstance(v,str) else v.decode(enc)
+
+    def asUnicodeEx(v,enc='utf8'):
+        return v if isinstance(v,str) else v.decode(enc) if isinstance(v,bytes) else str(v)
+
+    def asNative(v,enc='utf8'):
+        return asUnicode(v,enc=enc)
+
+    uniChr = chr
+
+    def int2Byte(i):
+        return bytes([i])
+
+    def isStr(v):
+        return isinstance(v, (str,bytes))
+
+    def isBytes(v):
+        return isinstance(v, bytes)
+
+    def isUnicode(v):
+        return isinstance(v, str)
+
+    def isClass(v):
+        return isinstance(v, type)
+
+    def isNonPrimitiveInstance(x):
+        return not isinstance(x,(float,int,type,tuple,list,dict,str,bytes,complex,bool,slice,
+            types.FunctionType,types.LambdaType,types.CodeType,
+            types.MappingProxyType,types.SimpleNamespace,
+            types.GeneratorType,types.MethodType,types.BuiltinFunctionType,
+            types.BuiltinMethodType,types.ModuleType,types.TracebackType,
+            types.FrameType,types.GetSetDescriptorType,types.MemberDescriptorType))
+
+    def instantiated(v):
+        return not isinstance(v,type)
+
+    from string import ascii_letters, ascii_uppercase, ascii_lowercase
+
+    from io import BytesIO, StringIO
+    def getBytesIO(buf=None):
+        '''unified StringIO instance interface'''
+        if buf:
+            return BytesIO(buf)
+        return BytesIO()
+    _bytesIOType = BytesIO 
+
+    def getStringIO(buf=None):
+        '''unified StringIO instance interface'''
+        if buf:
+            return StringIO(buf)
+        return StringIO()
+
+    def bytestr(x,enc='utf8'):
+        if isinstance(x,str):
+            return x.encode(enc)
+        elif isinstance(x,bytes):
+            return x
+        else:
+            return str(x).encode(enc)
+
+    def encode_label(args):
+        return base64_encodestring(pickle_dumps(args)).strip().decode('latin1')
+
+    def decode_label(label):
+        return pickle_loads(base64_decodestring(label.encode('latin1')))
+
+    def rawUnicode(s):
+        '''converts first 256 unicodes 1-1'''
+        return s.decode('latin1') if not isinstance(s,str) else s
+
+    def rawBytes(s):
+        '''converts first 256 unicodes 1-1'''
+        return s.encode('latin1') if isinstance(s,str) else s
+    import builtins
+    rl_exec = getattr(builtins,'exec')
+    del builtins
+    def char2int(s):
+        return  s if isinstance(s,int) else ord(s if isinstance(s,str) else s.decode('latin1'))
+    def rl_reraise(t, v, b=None):
+        if v.__traceback__ is not b:
+            raise v.with_traceback(b)
+        raise v
+    def rl_add_builtins(**kwd):
+        import builtins
+        for k,v in kwd.items():
+            setattr(builtins,k,v)
 else:
-    # hexdigest not available in 1.5
-    def _digester(s):
-        return join(map(lambda x : "%02x" % ord(x), md5(s).digest()), '')
+    bytesT = str
+    unicodeT = unicode
+    strTypes = basestring
+    if sys.hexversion >= 0x02000000:
+        def _digester(s):
+            return md5(s).hexdigest()
+    else:
+        # hexdigest not available in 1.5
+        def _digester(s):
+            return join(["%02x" % ord(x) for x in md5(s).digest()], '')
+
+    def asBytes(v,enc='utf8'):
+        return v if isinstance(v,str) else v.encode(enc)
+
+    def asNative(v,enc='utf8'):
+        return asBytes(v,enc=enc)
+
+    def uniChr(v):
+        return unichr(v)
+
+    def isStr(v):
+        return isinstance(v, basestring)
+
+    def isBytes(v):
+        return isinstance(v, str)
+
+    def isUnicode(v):
+        return isinstance(v, unicode)
+
+    def asUnicode(v,enc='utf8'):
+        return v if isinstance(v,unicode) else v.decode(enc)
+
+    def asUnicodeEx(v,enc='utf8'):
+        return v if isinstance(v,unicode) else v.decode(enc) if isinstance(v,str) else  unicode(v)
+
+    def isClass(v):
+        return isinstance(v,(types.ClassType,type))
+
+    def isNonPrimitiveInstance(x):
+        return isinstance(x,types.InstanceType) or not isinstance(x,(float,int,long,type,tuple,list,dict,bool,unicode,str,buffer,complex,slice,
+                    types.FunctionType,types.LambdaType,types.CodeType,types.GeneratorType,
+                    types.ClassType,types.UnboundMethodType,types.MethodType,types.BuiltinFunctionType,
+                    types.BuiltinMethodType,types.ModuleType,types.FileType,types.XRangeType,
+                    types.TracebackType,types.FrameType,types.EllipsisType,types.DictProxyType,
+                    types.NotImplementedType,types.GetSetDescriptorType,types.MemberDescriptorType
+                    ))
+
+    def instantiated(v):
+        return not isinstance(v,type) and hasattr(v,'__class__')
+
+    int2Byte = chr
+
+    from StringIO import StringIO
+    def getBytesIO(buf=None):
+        '''unified StringIO instance interface'''
+        if buf:
+            return StringIO(buf)
+        return StringIO()
+    getStringIO = getBytesIO
+    _bytesIOType = StringIO 
+
+    def bytestr(x,enc='utf8'):
+        if isinstance(x,unicode):
+            return x.encode(enc)
+        elif isinstance(x,str):
+            return x
+        else:
+            return str(x).encode(enc)
+    from string import letters as ascii_letters, uppercase as ascii_uppercase, lowercase as ascii_lowercase
+
+    def encode_label(args):
+        return base64_encodestring(pickle_dumps(args)).strip()
+
+    def decode_label(label):
+        return pickle_loads(base64_decodestring(label))
+
+    def rawUnicode(s):
+        '''converts first 256 unicodes 1-1'''
+        return s.decode('latin1') if not isinstance(s,unicode) else s
+
+    def rawBytes(s):
+        '''converts first 256 unicodes 1-1'''
+        return s.encode('latin1') if isinstance(s,unicode) else s
+
+    def rl_exec(obj, G=None, L=None):
+        if G is None:
+            frame = sys._getframe(1)
+            G = frame.f_globals
+            if L is None:
+                L = frame.f_locals
+            del frame
+        elif L is None:
+            L = G
+        exec("""exec obj in G, L""")
+    rl_exec("""def rl_reraise(t, v, b=None):\n\traise t, v, b\n""")
+
+    char2int = ord
+    def rl_add_builtins(**kwd):
+        import __builtin__
+        for k,v in kwd.items():
+            setattr(__builtin__,k,v)
+
+def zipImported(ldr=None):
+    try:
+        if not ldr:
+            ldr = sys._getframe(1).f_globals['__loader__']
+        from zipimport import zipimporter
+        return ldr if isinstance(ldr,zipimporter) else None
+    except:
+        return None
 
 def _findFiles(dirList,ext='.ttf'):
     from os.path import isfile, isdir, join as path_join
@@ -40,12 +263,7 @@
             if isfile(fn) and (not ext or fn.lower().endswith(ext)): A(fn)
     return R
 
-try:
-    _UserDict = dict
-except:
-    from UserDict import UserDict as _UserDict
-
-class CIDict(_UserDict):
+class CIDict(dict):
     def __init__(self,*args,**kwds):
         for a in args: self.update(a)
         self.update(kwds)
@@ -58,21 +276,21 @@
             k = k.lower()
         except:
             pass
-        _UserDict.__setitem__(self,k,v)
+        dict.__setitem__(self,k,v)
 
     def __getitem__(self,k):
         try:
             k = k.lower()
         except:
             pass
-        return _UserDict.__getitem__(self,k)
+        return dict.__getitem__(self,k)
 
     def __delitem__(self,k):
         try:
             k = k.lower()
         except:
             pass
-        return _UserDict.__delitem__(self,k)
+        return dict.__delitem__(self,k)
 
     def get(self,k,dv=None):
         try:
@@ -92,14 +310,14 @@
             k = k.lower()
         except:
             pass
-        return _UserDict.pop(*((self,k)+a))
+        return dict.pop(*((self,k)+a))
 
     def setdefault(self,k,*a):
         try:
             k = k.lower()
         except:
             pass
-        return _UserDict.setdefault(*((self,k)+a))
+        return dict.setdefault(*((self,k)+a))
 
 if os.name == 'mac':
     #with the Mac, we need to tag the file in a special
@@ -189,7 +407,7 @@
         c, pfn = __startswith_rl(pattern)
         r = glob(pfn)
         if c or r==[]:
-            r += map(lambda x,D=_archivepfx,pjoin=pjoin: pjoin(_archivepfx,x),filter(lambda x,pfn=pfn,fnmatch=fnmatch: fnmatch(x,pfn),__loader__._files.keys()))
+            r += list(map(lambda x,D=_archivepfx,pjoin=pjoin: pjoin(_archivepfx,x),list(filter(lambda x,pfn=pfn,fnmatch=fnmatch: fnmatch(x,pfn),list(__loader__._files.keys())))))
         return r
 except:
     _isFSD = os.path.isfile(__file__)   #slight risk of wrong path
@@ -213,54 +431,12 @@
     '''return truth if a source file system distribution'''
     return _isFSSD
 
-try:
-    #raise ImportError
-    ### NOTE!  FP_STR SHOULD PROBABLY ALWAYS DO A PYTHON STR() CONVERSION ON ARGS
-    ### IN CASE THEY ARE "LAZY OBJECTS".  ACCELLERATOR DOESN'T DO THIS (YET)
-    try:
-        from _rl_accel import fp_str                # in case of builtin version
-    except ImportError:
-        from reportlab.lib._rl_accel import fp_str  # specific
-except ImportError:
-    from math import log
-    _log_10 = lambda x,log=log,_log_e_10=log(10.0): log(x)/_log_e_10
-    _fp_fmts = "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"
-    import re
-    _tz_re = re.compile('0+$')
-    del re
-    def fp_str(*a):
-        '''convert separate arguments (or single sequence arg) into space separated numeric strings'''
-        if len(a)==1 and isSeqType(a[0]): a = a[0]
-        s = []
-        A = s.append
-        for i in a:
-            sa =abs(i)
-            if sa<=1e-7: A('0')
-            else:
-                l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6)
-                n = _fp_fmts[l]%i
-                if l:
-                    n = _tz_re.sub('',n)
-                    try:
-                        if n[-1]=='.': n = n[:-1]
-                    except:
-                        print i, n
-                        raise
-                A((n[0]!='0' or len(n)==1) and n or n[1:])
-        return ' '.join(s)
-
-#hack test for comma users
-if ',' in fp_str(0.25):
-    _FP_STR = fp_str
-    def fp_str(*a):
-        return _FP_STR(*a).replace(',','.')
-
 def recursiveImport(modulename, baseDir=None, noCWD=0, debug=0):
     """Dynamically imports possible packagized module, or raises ImportError"""
     normalize = lambda x: os.path.normcase(os.path.abspath(os.path.normpath(x)))
-    path = map(normalize,sys.path)
+    path = [normalize(p) for p in sys.path]
     if baseDir:
-        if not isSeqType(baseDir):
+        if not isSeq(baseDir):
             tp = [baseDir]
         else:
             tp = filter(None,list(baseDir))
@@ -271,7 +447,7 @@
     if noCWD:
         for p in ('','.',normalize('.')):
             while p in path:
-                if debug: print 'removed "%s" from path' % p
+                if debug: print('removed "%s" from path' % p)
                 path.remove(p)
     elif '.' not in path:
             path.insert(0,'.')
@@ -279,27 +455,29 @@
     if debug:
         import pprint
         pp = pprint.pprint
-        print 'path=',
+        print('path=')
         pp(path)
 
     #make import errors a bit more informative
     opath = sys.path
     try:
-        sys.path = path
-        exec 'import %s\nm = %s\n' % (modulename,modulename) in locals()
-        sys.path = opath
-        return m
-    except ImportError:
+        try:
+            sys.path = path
+            NS = {}
+            rl_exec('import %s as m' % modulename,NS)
+            return NS['m']
+        except ImportError:
+            sys.path = opath
+            msg = "Could not import '%s'" % modulename
+            if baseDir:
+                msg = msg + " under %s" % baseDir
+            annotateException(msg)
+        except:
+            e = sys.exc_info()
+            msg = "Exception raised while importing '%s': %s" % (modulename, e[1])
+            annotateException(msg)
+    finally:
         sys.path = opath
-        msg = "Could not import '%s'" % modulename
-        if baseDir:
-            msg = msg + " under %s" % baseDir
-        raise ImportError, msg
-
-    except Exception, e:
-        msg = "Exception raised while importing '%s': %s" % (modulename, e.message)
-        raise ImportError, msg
-        
 
 def recursiveGetAttr(obj, name):
     "Can call down into e.g. object1.object2[4].attr"
@@ -347,15 +525,6 @@
             Image = None
     haveImages = Image is not None
 
-try:
-    from cStringIO import StringIO as __StringIO
-except ImportError:
-    from StringIO import StringIO as __StringIO
-def getStringIO(buf=None):
-    '''unified StringIO instance interface'''
-    return buf is not None and __StringIO(buf) or __StringIO()
-_StringIOKlass=__StringIO().__class__
-
 class ArgvDictValue:
     '''A type to allow clients of getArgvDict to specify a conversion function'''
     def __init__(self,value,func):
@@ -371,8 +540,7 @@
         if func:
             v = func(av)
         else:
-            if isinstance(v,basestring):
-                if isinstance(v,unicode): v = v.encode('utf8')
+            if isStr(v):
                 v = av
             elif isinstance(v,float):
                 v = float(av)
@@ -412,7 +580,7 @@
         from reportlab.lib.pyHnj import Hyphen
         if hDict is None: hDict=os.path.join(os.path.dirname(__file__),'hyphen.mashed')
         return Hyphen(hDict)
-    except ImportError, errMsg:
+    except ImportError as errMsg:
         if str(errMsg)!='No module named pyHnj': raise
         return None
 
@@ -437,20 +605,26 @@
         name = _startswith_rl(name)
         s = __loader__.get_data(name)
         if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
-        return getStringIO(s)
+        return getBytesIO(s)
 
-import urllib2
-def open_for_read(name,mode='b', urlopen=urllib2.urlopen):
+try:
+    import urllib2
+    urlopen=urllib2.urlopen
+except ImportError:
+    import urllib.request
+    urlopen=urllib.request.urlopen
+
+def open_for_read(name,mode='b', urlopen=urlopen):
     '''attempt to open a file or URL for reading'''
     if hasattr(name,'read'): return name
     try:
         return open_for_read_by_name(name,mode)
     except:
         try:
-            return getStringIO(urlopen(name).read())
+            return getBytesIO(urlopen(name).read())
         except:
             raise IOError('Cannot open resource "%s"' % name)
-del urllib2
+del urlopen
 
 def open_and_read(name,mode='b'):
     return open_for_read(name,mode).read()
@@ -463,14 +637,14 @@
     if os_path_isfile(fn): return True
     if _isFSD or __loader__ is None: return False
     fn = _startswith_rl(fn)
-    return fn in __loader__._files.keys()
+    return fn in list(__loader__._files.keys())
 
 def rl_isdir(pn,os_path_isdir=os.path.isdir,os_path_normpath=os.path.normpath):
     if os_path_isdir(pn): return True
     if _isFSD or __loader__ is None: return False
     pn = _startswith_rl(os_path_normpath(pn))
     if not pn.endswith(os.sep): pn += os.sep
-    return len(filter(lambda x,pn=pn: x.startswith(pn),__loader__._files.keys()))>0
+    return len(list(filter(lambda x,pn=pn: x.startswith(pn),list(__loader__._files.keys()))))>0
 
 def rl_listdir(pn,os_path_isdir=os.path.isdir,os_path_normpath=os.path.normpath,os_listdir=os.listdir):
     if os_path_isdir(pn) or _isFSD or __loader__ is None: return os_listdir(pn)
@@ -547,7 +721,7 @@
             try:
                 from reportlab.rl_config import imageReaderFlags
                 self.fp = open_for_read(fileName,'b')
-                if isinstance(self.fp,_StringIOKlass):  imageReaderFlags=0 #avoid messing with already internal files
+                if isinstance(self.fp,_bytesIOType): imageReaderFlags=0 #avoid messing with already internal files
                 if imageReaderFlags>0:  #interning
                     data = self.fp.read()
                     if imageReaderFlags&2:  #autoclose
@@ -559,9 +733,9 @@
                         if not self._cache:
                             from rl_config import register_reset
                             register_reset(self._cache.clear)
-                        data=self._cache.setdefault(md5(data).digest(),data)
-                    self.fp=getStringIO(data)
-                elif imageReaderFlags==-1 and isinstance(fileName,(str,unicode)):
+                        data=self._cache.setdefault(_digester(data),data)
+                    self.fp=getBytesIO(data)
+                elif imageReaderFlags==-1 and isinstance(fileName,str):
                     #try Ralf Schmitt's re-opening technique of avoiding too many open files
                     self.fp.close()
                     del self.fp #will become a property in the next statement
@@ -587,7 +761,7 @@
     def identity(self):
         '''try to return information that will identify the instance'''
         fn = self.fileName
-        if not isinstance(fn,basestring):
+        if not isStr(fn):
             fn = getattr(getattr(self,'fp',None),'name',None)
         ident = self._ident
         return '[%s@%s%s%s]' % (self.__class__.__name__,hex(id(self)),ident and (' ident=%r' % ident) or '',fn and (' filename=%r' % fn) or '')
@@ -676,7 +850,10 @@
                         palette = palette.data
                     except:
                         return None
-                return map(ord, palette[transparency:transparency+3])
+                if isPy3:
+                    return palette[transparency:transparency+3]
+                else:
+                    return [ord(c) for c in palette[transparency:transparency+3]]
             else:
                 return None
 
@@ -736,7 +913,7 @@
         self.store = store = {}
         if capture_traceback and sys.exc_info() != (None,None,None):
             import traceback
-            s = getStringIO()
+            s = getBytesIO()
             traceback.print_exc(None,s)
             store['__traceback'] = s.getvalue()
         cwd=os.getcwd()
@@ -753,7 +930,7 @@
         except:
             pass
         env = os.environ
-        K=env.keys()
+        K=list(env.keys())
         K.sort()
         store.update({  'gmt': time.asctime(time.gmtime(time.time())),
                         'platform': sys.platform,
@@ -769,7 +946,6 @@
                         'lcwd': lcwd,
                         'lpcwd': lpcwd,
                         'byteorder': sys.byteorder,
-                        'maxint': sys.maxint,
                         'maxint': getattr(sys,'maxunicode','????'),
                         'api_version': getattr(sys,'api_version','????'),
                         'version_info': getattr(sys,'version_info','????'),
@@ -807,8 +983,8 @@
         for n,m in sys.modules.items():
             if n=='reportlab' or n=='rlextra' or n[:10]=='reportlab.' or n[:8]=='rlextra.':
                 v = [getattr(m,x,None) for x in ('__version__','__path__','__file__')]
-                if filter(None,v):
-                    v = [v[0]] + filter(None,v[1:])
+                if [_f for _f in v if _f]:
+                    v = [v[0]] + [_f for _f in v[1:] if _f]
                     module_versions[n] = tuple(v)
         store['__module_versions'] = module_versions
         self.store['__payload'] = {}
@@ -823,20 +999,19 @@
         self._add(kw)
 
     def _dump(self,f):
-        import pickle
         try:
             pos=f.tell()
-            pickle.dump(self.store,f)
+            pickle_dump(self.store,f)
         except:
             S=self.store.copy()
-            ff=getStringIO()
-            for k,v in S.iteritems():
+            ff=getBytesIO()
+            for k,v in S.items():
                 try:
-                    pickle.dump({k:v},ff)
+                    pickle_dump({k:v},ff)
                 except:
                     S[k] = '<unpicklable object %r>' % v
             f.seek(pos,0)
-            pickle.dump(S,f)
+            pickle_dump(S,f)
 
     def dump(self):
         f = open(self.fn,'wb')
@@ -846,13 +1021,12 @@
             f.close()
 
     def dumps(self):
-        f = getStringIO()
+        f = getBytesIO()
         self._dump(f)
         return f.getvalue()
 
     def _load(self,f):
-        import pickle
-        self.store = pickle.load(f)
+        self.store = pickle_load(f)
 
     def load(self):
         f = open(self.fn,'rb')
@@ -862,17 +1036,18 @@
             f.close()
 
     def loads(self,s):
-        self._load(getStringIO(s))
+        self._load(getBytesIO(s))
 
     def _show_module_versions(self,k,v):
         self._writeln(k[2:])
-        K = v.keys()
+        K = list(v.keys())
         K.sort()
         for k in K:
             vk = vk0 = v[k]
             if isinstance(vk,tuple): vk0 = vk[0]
             try:
-                m = recursiveImport(k,sys.path[:],1)
+                __import__(k)
+                m = sys.modules[k]
                 d = getattr(m,'__version__',None)==vk0 and 'SAME' or 'DIFFERENT'
             except:
                 m = None
@@ -908,7 +1083,8 @@
         for mn in ('_rl_accel','_renderPM','sgmlop','pyRXP','pyRXPU','_imaging','Image'):
             try:
                 A = [mn].append
-                m = recursiveImport(mn,sys.path[:],1)
+                __import__(mn)
+                m = sys.modules[mn]
                 A(m.__file__)
                 for vn in ('__version__','VERSION','_version','version'):
                     if hasattr(m,vn):
@@ -923,12 +1099,12 @@
                 '__script': _show_file,
                 }
     def show(self):
-        K = self.store.keys()
+        K = list(self.store.keys())
         K.sort()
         for k in K:
-            if k not in self.specials.keys(): self._writeln('%-15s = %s' % (k,self.store[k]))
+            if k not in list(self.specials.keys()): self._writeln('%-15s = %s' % (k,self.store[k]))
         for k in K:
-            if k in self.specials.keys(): self.specials[k](self,k,self.store[k])
+            if k in list(self.specials.keys()): self.specials[k](self,k,self.store[k])
         self._show_extensions()
 
     def payload(self,name):
@@ -945,7 +1121,7 @@
 
 def _flatten(L,a):
     for x in L:
-        if isSeqType(x): _flatten(x,a)
+        if isSeq(x): _flatten(x,a)
         else: a(x)
 
 def flatten(L):
@@ -999,7 +1175,7 @@
 
 def simpleSplit(text,fontName,fontSize,maxWidth):
     from reportlab.pdfbase.pdfmetrics import stringWidth
-    lines = text.split('\n')
+    lines = asUnicode(text).split(u'\n')
     SW = lambda text, fN=fontName, fS=fontSize: stringWidth(text, fN, fS)
     if maxWidth:
         L = []
@@ -1013,22 +1189,37 @@
     from xml.sax.saxutils import escape
     if text is None:
         return text
+    if isBytes(text): s = text.decode('utf8')
     text = escape(text)
-    text = text.replace('&amp;amp;', '&amp;')
-    text = text.replace('&amp;gt;', '&gt;')
-    text = text.replace('&amp;lt;', '&lt;')
+    text = text.replace(u'&amp;amp;',u'&amp;')
+    text = text.replace(u'&amp;gt;', u'&gt;')
+    text = text.replace(u'&amp;lt;', u'&lt;')
     return text
 
-def fileName2Utf8(fn):
-    '''attempt to convert a filename to utf8'''
-    from reportlab.rl_config import fsEncodings
-    for enc in fsEncodings:
-        try:
-            return fn.decode(enc).encode('utf8')
-        except:
-            pass
-    raise ValueError('cannot convert %r to utf8' % fn)
-
+if isPy3:
+    def fileName2FSEnc(fn):
+        if isUnicode(fn):
+            return  fn
+        else:
+            for enc in fsEncodings:
+                try:
+                    return fn.decode(enc)
+                except:
+                    pass
+        raise ValueError('cannot convert %r to filesystem encoding' % fn)
+else:
+    def fileName2FSEnc(fn):
+        '''attempt to convert a filename to utf8'''
+        from reportlab.rl_config import fsEncodings
+        if isUnicode(fn):
+            return asBytes(fn)
+        else:
+            for enc in fsEncodings:
+                try:
+                    return fn.decode(enc).encode('utf8')
+                except:
+                    pass
+        raise ValueError('cannot convert %r to utf8 for file path name' % fn)
 
 import itertools
 def prev_this_next(items):
@@ -1050,12 +1241,12 @@
     extend = itertools.chain([None], items, [None])
     prev, this, next = itertools.tee(extend, 3)
     try:
-        this.next()
-        next.next()
-        next.next()
+        next(this)
+        next(next)
+        next(next)
     except StopIteration:
         pass
-    return itertools.izip(prev, this, next)
+    return zip(prev, this, next)
 
 def commasplit(s):
     '''
@@ -1064,25 +1255,25 @@
     To avoid the ambiguity of 3 successive commas to denote a comma at the beginning
     or end of an item, add a space between the item seperator and the escaped comma.
     
-    >>> commasplit('a,b,c')
-    ['a', 'b', 'c']
-    >>> commasplit('a,, , b , c    ')
-    ['a,', 'b', 'c']
-    >>> commasplit('a, ,,b, c')
-    ['a', ',b', 'c']
+    >>> commasplit(u'a,b,c') == [u'a', u'b', u'c']
+    True
+    >>> commasplit('a,, , b , c    ') == [u'a,', u'b', u'c']
+    True
+    >>> commasplit(u'a, ,,b, c') == [u'a', u',b', u'c']
     '''
+    if isBytes(s): s = s.decode('utf8')
     n = len(s)-1
-    s += ' '
+    s += u' '
     i = 0
-    r=['']
+    r=[u'']
     while i<=n:
-        if s[i]==',':
-            if s[i+1]==',':
-                r[-1]+=','
+        if s[i]==u',':
+            if s[i+1]==u',':
+                r[-1]+=u','
                 i += 1
             else:
                 r[-1] = r[-1].strip()
-                if i!=n: r.append('')
+                if i!=n: r.append(u'')
         else:
             r[-1] += s[i]
         i+=1
@@ -1094,14 +1285,13 @@
     Inverse of commasplit, except that whitespace around items is not conserved.
     Adds more whitespace than needed for simplicity and performance.
     
-    >>> commasplit(commajoin(['a', 'b', 'c']))
-    ['a', 'b', 'c']
-    >>> commasplit((commajoin(['a,', ' b ', 'c']))
-    ['a,', 'b', 'c']
-    >>> commasplit((commajoin(['a ', ',b', 'c']))
-    ['a', ',b', 'c']    
+    >>> commasplit(commajoin(['a', 'b', 'c'])) == [u'a', u'b', u'c']
+    True
+    >>> commasplit((commajoin([u'a,', u' b ', u'c'])) == [u'a,', u'b', u'c']
+    True
+    >>> commasplit((commajoin([u'a ', u',b', u'c'])) == [u'a', u',b', u'c'] 
     '''
-    return ','.join([ ' ' + i.replace(',', ',,') + ' ' for i in l ])
+    return u','.join([ u' ' + asUnicode(i).replace(u',', u',,') + u' ' for i in l ])
 
 def findInPaths(fn,paths,isfile=True,fail=False):
     '''search for relative files in likely places'''
@@ -1124,18 +1314,19 @@
     e = -1
     A = list(v.args)
     for i,a in enumerate(A):
-        if isinstance(a,basestring):
+        if isinstance(a,str):
             e = i
             break
     if e>=0:
-        if isinstance(a,unicode):
-            if not isinstance(msg,unicode):
-                msg=msg.decode(enc)
-        else:
-            if isinstance(msg,unicode):
-                msg=msg.encode(enc)
+        if not isPy3:
+            if isUnicode(a):
+                if not isUnicode(msg):
+                    msg=msg.decode(enc)
             else:
-                msg = str(msg)
+                if isUnicode(msg):
+                    msg=msg.encode(enc)
+                else:
+                    msg = str(msg)
         if isinstance(v,IOError) and getattr(v,'strerror',None):
             v.strerror = msg+'\n'+str(v.strerror)
         else:
@@ -1143,8 +1334,8 @@
     else:
         A.append(msg)
     v.args = tuple(A)
-    raise t,v,b
-    
+    rl_reraise(t,v,b)
+
 def escapeOnce(data):
     """Ensure XML output is escaped just once, irrespective of input
 
@@ -1171,7 +1362,7 @@
     data = data.replace("&amp;gt;", "&gt;")
     data = data.replace("&amp;lt;", "&lt;")
     return data
-
+    
 class IdentStr(str):
     '''useful for identifying things that get split'''
     def __new__(cls,value):
@@ -1193,13 +1384,12 @@
     '''
     def __new__(cls,v,**kwds):
         self = str.__new__(cls,v)
-        for k,v in kwds.iteritems():
+        for k,v in kwds.items():
             setattr(self,k,v)
         return self
 
 def makeFileName(s):
     '''force filename strings to unicode so python can handle encoding stuff'''
-    assert isinstance(s,basestring),"filename is %r should be str or unicode" % s
-    if isinstance(s,str):
+    if not isUnicode(s):
         s = s.decode('utf8')
     return s
--- a/src/reportlab/lib/validators.py	Mon Feb 17 10:12:24 2014 +0000
+++ b/src/reportlab/lib/validators.py	Wed Mar 26 12:32:02 2014 +0000
@@ -4,14 +4,9 @@
 __version__=''' $Id$ '''
 __doc__="""Standard verifying functions used by attrmap."""
 
-import string, sys, codecs
-from types import *
-_SequenceTypes = (ListType,TupleType)
-_NumberTypes = (FloatType,IntType)
+import sys, codecs
+from reportlab.lib.utils import isSeq, isBytes, isStr, isPy3
 from reportlab.lib import colors
-if sys.hexversion<0x2030000:
-    True = 1
-    False = 0
 
 class Percentage(float):
     pass
@@ -43,32 +38,27 @@
         return False
 
 class _isBoolean(Validator):
-    if sys.hexversion>=0x2030000:
-        def test(self,x):
-            if type(x) in (IntType,BooleanType): return x in (0,1)
-            return self.normalizeTest(x)
-    else:
-        def test(self,x):
-            if type(x) is IntType: return x in (0,1)
-            return self.normalizeTest(x)
+    def test(self,x):
+        if isinstance(int,bool): return x in (0,1)
+        return self.normalizeTest(x)
 
     def normalize(self,x):
         if x in (0,1): return x
         try:
-            S = string.upper(x)
+            S = x.upper()
         except:
-            raise ValueError, 'Must be boolean'
+            raise ValueError('Must be boolean not %s' % ascii(s))
         if S in ('YES','TRUE'): return True
         if S in ('NO','FALSE',None): return False
-        raise ValueError, 'Must be boolean'
+        raise ValueError('Must be boolean not %s' % ascii(s))
 
 class _isString(Validator):
     def test(self,x):
-        return isinstance(x,(str,unicode))
+        return isStr(x)
 
 class _isCodec(Validator):
     def test(self,x):
-        if type(x) not in (StringType, UnicodeType):
+        if not isStr(x):
             return False
         try:
             a,b,c,d = codecs.lookup(x)
@@ -78,7 +68,7 @@
 
 class _isNumber(Validator):
     def test(self,x):
-        if type(x) in _NumberTypes: return True
+        if isinstance(x,(float,int)): return True
         return self.normalizeTest(x)
 
     def normalize(self,x):
@@ -89,11 +79,15 @@
 
 class _isInt(Validator):
     def test(self,x):
-        if type(x) not in (IntType,StringType): return False
+        if not isinstance(x,int) and not isStr(x): return False
         return self.normalizeTest(x)
 
-    def normalize(self,x):
-        return int(x)
+    if not isPy3:
+        def normalize(self,x):
+            return int(x)
+    else:
+        def normalize(self,x):
+            return int(x.decode('utf8') if isBytes(x) else x)
 
 class _isNumberOrNone(_isNumber):
     def test(self,x):
@@ -128,7 +122,7 @@
     "ListOfShapes validator class."
     def test(self, x):
         from reportlab.graphics.shapes import Shape
-        if type(x) in _SequenceTypes:
+        if isSeq(x):
             answer = 1
             for e in x:
                 if not isinstance(e, Shape):
@@ -147,7 +141,7 @@
 class _isTransform(Validator):
     "Transform validator class."
     def test(self, x):
-        if type(x) in _SequenceTypes:
+        if isSeq(x):
             if len(x) == 6:
                 for element in x:
                     if not isNumber(element):
@@ -208,9 +202,9 @@
     (1,1,0)
     """
     def __init__(self, enum,*args):
-        if type(enum) in [ListType,TupleType]:
+        if isSeq(enum):
             if args!=():
-                raise ValueError, "Either all singleton args or a single sequence argument"
+                raise ValueError("Either all singleton args or a single sequence argument")
             self._enum = tuple(enum)+args
         else:
             self._enum = (enum,)+args
@@ -227,7 +221,7 @@
         if name: self._str = name
 
     def test(self, x):
-        if type(x) not in _SequenceTypes:
+        if not isSeq(x):
             if x is None: return self._NoneOK
             return False
         if x==[] or x==():
@@ -239,7 +233,7 @@
 
 class EitherOr(Validator):
     def __init__(self,tests,name=None):
-        if type(tests) not in _SequenceTypes: tests = (tests,)
+        if not isSeq(tests): tests = (tests,)
         self._tests = tests
         if name: self._str = name
 
@@ -280,12 +274,9 @@
         self._pattern = re.compile(pattern)
 
     def test(self,x):
-        print 'testing %s against %s' % (x, self._pattern)
-        if type(x) is StringType:
-            text = x
-        else:
-            text = str(x)
-        return (self._pattern.match(text) != None)
+        x = str(x)
+        print('testing %s against %s' % (x, self._pattern))
+        return (self._pattern.match(x) != None)
 
 class DerivedValue:
     """This is used for magic values which work themselves out.
--- a/src/reportlab/lib/xmllib.py	Mon Feb 17 10:12:24 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,776 +0,0 @@
-# A parser for XML, using the derived class as static DTD.
-# Author: Sjoerd Mullender.
-
-# sgmlop support added by fredrik@pythonware.com (May 19, 1998)
-__version__=''' $Id$ '''
-__doc__='''From before xmllib was in the Python standard library.
-
-Probably ought to be removed'''
-
-import re
-import string
-
-try:
-    import sgmlop   # this works for both builtin on the path or relative
-except ImportError:
-    sgmlop = None
-
-# standard entity defs
-
-ENTITYDEFS = {
-    'lt': '<',
-    'gt': '>',
-    'amp': '&',
-    'quot': '"',
-    'apos': '\''
-    }
-
-# XML parser base class -- find tags and call handler functions.
-# Usage: p = XMLParser(); p.feed(data); ...; p.close().
-# The dtd is defined by deriving a class which defines methods with
-# special names to handle tags: start_foo and end_foo to handle <foo>
-# and </foo>, respectively.  The data between tags is passed to the
-# parser by calling self.handle_data() with some data as argument (the
-# data may be split up in arbutrary chunks).  Entity references are
-# passed by calling self.handle_entityref() with the entity reference
-# as argument.
-
-# --------------------------------------------------------------------
-# original re-based XML parser
-
-_S = '[ \t\r\n]+'
-_opS = '[ \t\r\n]*'
-_Name = '[a-zA-Z_:][-a-zA-Z0-9._:]*'
-interesting = re.compile('[&<]')
-incomplete = re.compile('&(' + _Name + '|#[0-9]*|#x[0-9a-fA-F]*)?|'
-                           '<([a-zA-Z_:][^<>]*|'
-                              '/([a-zA-Z_:][^<>]*)?|'
-                              '![^<>]*|'
-                              '\?[^<>]*)?')
-
-ref = re.compile('&(' + _Name + '|#[0-9]+|#x[0-9a-fA-F]+);?')
-entityref = re.compile('&(?P<name>' + _Name + ')[^-a-zA-Z0-9._:]')
-charref = re.compile('&#(?P<char>[0-9]+[^0-9]|x[0-9a-fA-F]+[^0-9a-fA-F])')
-space = re.compile(_S)
-newline = re.compile('\n')
-
-starttagopen = re.compile('<' + _Name)
-endtagopen = re.compile('</')
-starttagend = re.compile(_opS + '(?P<slash>/?)>')
-endbracket = re.compile('>')
-tagfind = re.compile(_Name)
-cdataopen = re.compile('<!\[CDATA\[')
-cdataclose = re.compile('\]\]>')
-special = re.compile('<!(?P<special>[^<>]*)>')
-procopen = re.compile('<\?(?P<proc>' + _Name + ')' + _S)
-procclose = re.compile('\?>')
-commentopen = re.compile('<!--')
-commentclose = re.compile('-->')
-doubledash = re.compile('--')
-attrfind = re.compile(
-    _opS + '(?P<name>' + _Name + ')'
-    '(' + _opS + '=' + _opS +
-    '(?P<value>\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9.:+*%?!()_#=~]+))')
-
-class SlowXMLParser:
-
-    # Interface -- initialize and reset this instance
-    def __init__(self, verbose=0):
-        self.verbose = verbose
-        self.reset()
-
-    # Interface -- reset this instance.  Loses all unprocessed data
-    def reset(self):
-        self.rawdata = ''
-        self.stack = []
-        self.lasttag = '???'
-        self.nomoretags = 0
-        self.literal = 0
-        self.lineno = 1
-
-    # For derived classes only -- enter literal mode (CDATA) till EOF
-    def setnomoretags(self):
-        self.nomoretags = self.literal = 1
-
-    # For derived classes only -- enter literal mode (CDATA)
-    def setliteral(self, *args):
-        self.literal = 1
-
-    # Interface -- feed some data to the parser.  Call this as
-    # often as you want, with as little or as much text as you
-    # want (may include '\n').  (This just saves the text, all the
-    # processing is done by goahead().)
-    def feed(self, data):
-        self.rawdata = self.rawdata + data
-        self.goahead(0)
-
-    # Interface -- handle the remaining data
-    def close(self):
-        self.goahead(1)
-
-    # Interface -- translate references
-    def translate_references(self, data):
-        newdata = []
-        i = 0
-        while 1:
-            res = ref.search(data, i)
-            if res is None:
-                newdata.append(data[i:])
-                return string.join(newdata, '')
-            if data[res.end(0) - 1] != ';':
-                self.syntax_error(self.lineno,
-                                  '; missing after entity/char reference')
-            newdata.append(data[i:res.start(0)])
-            str = res.group(1)
-            if str[0] == '#':
-                if str[1] == 'x':
-                    newdata.append(chr(string.atoi(str[2:], 16)))
-                else:
-                    newdata.append(chr(string.atoi(str[1:])))
-            else:
-                try:
-                    newdata.append(self.entitydefs[str])
-                except KeyError:
-                    # can't do it, so keep the entity ref in
-                    newdata.append('&' + str + ';')
-            i = res.end(0)
-
-    # Internal -- handle data as far as reasonable.  May leave state
-    # and data to be processed by a subsequent call.  If 'end' is
-    # true, force handling all data as if followed by EOF marker.
-    def goahead(self, end):
-        rawdata = self.rawdata
-        i = 0
-        n = len(rawdata)
-        while i < n:
-            if self.nomoretags:
-                data = rawdata[i:n]
-                self.handle_data(data)
-                self.lineno = self.lineno + string.count(data, '\n')
-                i = n
-                break
-            res = interesting.search(rawdata, i)
-            if res:
-                    j = res.start(0)
-            else:
-                    j = n
-            if i < j:
-                data = rawdata[i:j]
-                self.handle_data(data)
-                self.lineno = self.lineno + string.count(data, '\n')
-            i = j
-            if i == n: break
-            if rawdata[i] == '<':
-                if starttagopen.match(rawdata, i):
-                    if self.literal:
-                        data = rawdata[i]
-                        self.handle_data(data)
-                        self.lineno = self.lineno + string.count(data, '\n')
-                        i = i+1
-                        continue
-                    k = self.parse_starttag(i)
-                    if k < 0: break
-                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
-                    i = k
-                    continue
-                if endtagopen.match(rawdata, i):
-                    k = self.parse_endtag(i)
-                    if k < 0: break
-                    self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
-                    i =  k
-                    self.literal = 0
-                    continue
-                if commentopen.match(rawdata, i):
-                    if self.literal:
-                        data = rawdata[i]
-                        self.handle_data(data)