From 09fd27f68535862b130b521457fecf8da1fdcb22 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 11:29:37 +0100 Subject: [PATCH 01/61] Refactor the setup.py bzr revid: stephane@tinyerp.com-20081113102937-9uc99gyeq2kenp8e --- setup.py | 136 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 63 deletions(-) mode change 100644 => 100755 setup.py diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 148b1e1b9a4..13c36d52fa0 --- a/setup.py +++ b/setup.py @@ -50,10 +50,12 @@ execfile(opj('bin', 'release.py')) # get python short version py_short_version = '%s.%s' % sys.version_info[:2] -required_modules = [('psycopg', 'PostgreSQL module'), - ('xml', 'XML Tools for python'), - ('libxml2', 'libxml2 python bindings'), - ('libxslt', 'libxslt python bindings')] +required_modules = [ + ('psycopg', 'PostgreSQL module'), + ('xml', 'XML Tools for python'), + ('libxml2', 'libxml2 python bindings'), + ('libxslt', 'libxslt python bindings') +] def check_modules(): ok = True @@ -69,8 +71,6 @@ def check_modules(): def find_addons(): for (dp, dn, names) in os.walk(opj('bin', 'addons')): - if '.bzr' in dn: - dn.remove('.bzr') for dirpath, dirnames, filenames in os.walk(dp): if '__init__.py' in filenames: modname = dirpath.replace(os.path.sep, '.') @@ -82,53 +82,51 @@ def data_files(): if os.name == 'nt': os.chdir('bin') for (dp,dn,names) in os.walk('addons'): - if '.bzr' in dn: - dn.remove('.bzr') - files.append((dp, map(lambda x: os.path.join('bin', dp,x), names))) + files.append((dp, map(lambda x: opj('bin', dp, x), names))) for (dp,dn,names) in os.walk('i18n'): - if '.bzr' in dn: - dn.remove('.bzr') - files.append((dp, map(lambda x: os.path.join('bin', dp,x), names))) + files.append((dp, map(lambda x: opj('bin', dp, x), names))) os.chdir('..') for (dp,dn,names) in os.walk('doc'): - if '.bzr' in dn: - dn.remove('.bzr') - files.append((dp, map(lambda x: os.path.join(dp,x), names))) + files.append((dp, map(lambda x: opj(dp, x), names))) else: - files.append((opj('share', 'man', 'man1'), ['man/openerp-server.1'])) - files.append((opj('share', 'man', 'man5'), ['man/openerp_serverrc.5'])) - files.append((opj('share','doc', 'openerp-server-%s' % version), [f for - f in glob.glob('doc/*') if os.path.isfile(f)])) - files.append((opj('share','doc', 'openerp-server-%s' % version, - 'migrate', '3.3.0-3.4.0'), [f for f in - glob.glob('doc/migrate/3.3.0-3.4.0/*') if os.path.isfile(f)])) - files.append((opj('share','doc', 'openerp-server-%s' % version, - 'migrate', '3.4.0-4.0.0'), [f for f in - glob.glob('doc/migrate/3.4.0-4.0.0/*') if os.path.isfile(f)])) - files.append((opj('lib','python%s' % py_short_version, 'site-packages', - 'openerp-server', 'i18n'), glob.glob('bin/i18n/*'))) - files.append((opj('lib', 'python%s' % py_short_version, 'site-packages', - 'openerp-server', 'addons', 'custom'), - glob.glob('bin/addons/custom/*xml') + - glob.glob('bin/addons/custom/*rml') + - glob.glob('bin/addons/custom/*xsl'))) + man_directory = opj('share', 'man') + files.append((opj(man_directory, 'man1'), ['man/openerp-server.1'])) + files.append((opj(man_directory, 'man5'), ['man/openerp_serverrc.5'])) + + doc_directory = opj('share', 'doc', 'openerp-server-%s' % version) + files.append((doc_directory, [f for f in glob.glob('doc/*') if os.path.isfile(f)])) + files.append((opj(doc_directory, 'migrate', '3.3.0-3.4.0'), [f for f in glob.glob('doc/migrate/3.3.0-3.4.0/*') if os.path.isfile(f)])) + files.append((opj(doc_directory, 'migrate', '3.4.0-4.0.0'), [f for f in glob.glob('doc/migrate/3.4.0-4.0.0/*') if os.path.isfile(f)])) + + openerp_site_packages = opj('lib', 'python%s' % py_short_version, 'site-packages', 'openerp-server') + + files.append((opj(openerp_site_packages, 'i18n'), glob.glob('bin/i18n/*'))) + files.append((opj(openerp_site_packages, 'addons', 'custom'), + glob.glob('bin/addons/custom/*xml') + glob.glob('bin/addons/custom/*rml') + glob.glob('bin/addons/custom/*xsl'))) + for addon in find_addons(): - add_path = addon.replace('.', os.path.sep).replace('openerp-server', 'bin', - 1) - pathfiles = [(opj('lib', 'python%s' % py_short_version, 'site-packages', - add_path.replace('bin', 'openerp-server', 1)), - glob.glob(opj(add_path, '*xml')) + - glob.glob(opj(add_path, '*csv')) + - glob.glob(opj(add_path, '*sql'))), - (opj('lib', 'python%s' % py_short_version, 'site-packages', - add_path.replace('bin', 'openerp-server', 1), 'data'), - glob.glob(opj(add_path, 'data', '*xml'))), - (opj('lib', 'python%s' % py_short_version, 'site-packages', - add_path.replace('bin', 'openerp-server', 1), 'report'), - glob.glob(opj(add_path, 'report', '*xml')) + - glob.glob(opj(add_path, 'report', '*rml')) + - glob.glob(opj(add_path, 'report', '*sxw')) + - glob.glob(opj(add_path, 'report', '*xsl')))] + add_path = addon.replace('.', os.path.sep).replace('openerp-server', 'bin', 1) + addon_path = opj('lib', 'python%s' % py_short_version, 'site-packages', add_path.replace('bin', 'openerp-server', 1)) + + pathfiles = [ + ( + addon_path, + glob.glob(opj(add_path, '*xml')) + + glob.glob(opj(add_path, '*csv')) + + glob.glob(opj(add_path, '*sql')) + ), + ( + opj(addon_path, 'data'), + glob.glob(opj(add_path, 'data', '*xml')) + ), + ( + opj(addon_path, 'report'), + glob.glob(opj(add_path, 'report', '*xml')) + + glob.glob(opj(add_path, 'report', '*rml')) + + glob.glob(opj(add_path, 'report', '*sxw')) + + glob.glob(opj(add_path, 'report', '*xsl')) + ) + ] files.extend(pathfiles) files.append(('.', [('bin/import_xml.rng')])) return files @@ -139,21 +137,23 @@ check_modules() start_script = \ "#!/bin/sh\n\ cd %s/lib/python%s/site-packages/openerp-server\n\ -exec %s ./openerp-server.py $@" % (sys.prefix, py_short_version, sys.executable) +exec %s ./openerp-server.py $@\n" % (sys.prefix, py_short_version, sys.executable) # write script f = open('openerp-server', 'w') f.write(start_script) f.close() -options = {"py2exe": { - "compressed": 0, - "optimize": 2, - "packages": ["lxml", "lxml.builder", "lxml._elementpath", "lxml.etree", - "lxml.objectify", "decimal", "xml", "xml.dom", "xml.xpath", - "encodings","mx.DateTime","wizard","pychart","PIL", "pyparsing", - "pydot","asyncore","asynchat"], - "excludes" : ["Tkconstants","Tkinter","tcl"], - }} +options = { + "py2exe": { + "compressed": 0, + "optimize": 2, + "packages": ["lxml", "lxml.builder", "lxml._elementpath", "lxml.etree", + "lxml.objectify", "decimal", "xml", "xml.dom", "xml.xpath", + "encodings","mx.DateTime","wizard","pychart","PIL", "pyparsing", + "pydot","asyncore","asynchat"], + "excludes" : ["Tkconstants","Tkinter","tcl"], + } +} setup(name = name, version = version, @@ -166,21 +166,31 @@ setup(name = name, license = license, data_files = data_files(), scripts = ['openerp-server'], - packages = ['openerp-server', 'openerp-server.addons', + packages = ['openerp-server', + 'openerp-server.addons', 'openerp-server.ir', 'openerp-server.osv', 'openerp-server.ssl', - 'openerp-server.service', 'openerp-server.tools', - 'openerp-server.pychart', 'openerp-server.pychart.afm', + 'openerp-server.service', + 'openerp-server.tools', + 'openerp-server.pychart', + 'openerp-server.pychart.afm', 'openerp-server.report', 'openerp-server.report.printscreen', 'openerp-server.report.render', 'openerp-server.report.render.rml2pdf', 'openerp-server.report.render.rml2html', - 'openerp-server.wizard', 'openerp-server.workflow'] + \ + 'openerp-server.wizard', + 'openerp-server.workflow'] + \ list(find_addons()), package_dir = {'openerp-server': 'bin'}, - console = [{"script":"bin\\openerp-server.py", "icon_resources":[(1,"pixmaps\\openerp-icon.ico")]}], + console = [ + { "script" : "bin\\openerp-server.py", + "icon_resources" : [ + (1,"pixmaps\\openerp-icon.ico") + ] + } + ], options = options, ) From 26ec801a6060e79aa19f8a8490fdf092411dea97 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 11:39:57 +0100 Subject: [PATCH 02/61] Add the security directory into the packaging bzr revid: stephane@tinyerp.com-20081113103957-1op6exlhnfxkmftn --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 13c36d52fa0..e4b3b7ddc62 100755 --- a/setup.py +++ b/setup.py @@ -125,7 +125,11 @@ def data_files(): glob.glob(opj(add_path, 'report', '*rml')) + glob.glob(opj(add_path, 'report', '*sxw')) + glob.glob(opj(add_path, 'report', '*xsl')) - ) + ), + ( opj(addon_path, 'security'), + glob.glob(opj(add_path, 'security', '*csv')) + + glob.glob(opj(add_path, 'security', '*xml')) + ), ] files.extend(pathfiles) files.append(('.', [('bin/import_xml.rng')])) From a48a65e138abe2898edc31762d796bd4b37f3d69 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 12:06:12 +0100 Subject: [PATCH 03/61] Bugfix with the rng file bzr revid: stephane@tinyerp.com-20081113110612-yr77ywso0wqyrvk2 --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e4b3b7ddc62..983918761c8 100755 --- a/setup.py +++ b/setup.py @@ -88,6 +88,7 @@ def data_files(): os.chdir('..') for (dp,dn,names) in os.walk('doc'): files.append((dp, map(lambda x: opj(dp, x), names))) + files.append(('.', [('bin/import_xml.rng')])) else: man_directory = opj('share', 'man') files.append((opj(man_directory, 'man1'), ['man/openerp-server.1'])) @@ -104,6 +105,8 @@ def data_files(): files.append((opj(openerp_site_packages, 'addons', 'custom'), glob.glob('bin/addons/custom/*xml') + glob.glob('bin/addons/custom/*rml') + glob.glob('bin/addons/custom/*xsl'))) + files.append((openerp_site_packages, [('bin/import_xml.rng')])) + for addon in find_addons(): add_path = addon.replace('.', os.path.sep).replace('openerp-server', 'bin', 1) addon_path = opj('lib', 'python%s' % py_short_version, 'site-packages', add_path.replace('bin', 'openerp-server', 1)) @@ -132,7 +135,6 @@ def data_files(): ), ] files.extend(pathfiles) - files.append(('.', [('bin/import_xml.rng')])) return files check_modules() From 6c92455cd787ff0f1274ffba49480b96a90ce40b Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 12:13:28 +0100 Subject: [PATCH 04/61] Import rng files bzr revid: stephane@tinyerp.com-20081113111328-kf899dsp9as0aein --- setup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 983918761c8..95c8f30d3b2 100755 --- a/setup.py +++ b/setup.py @@ -129,10 +129,15 @@ def data_files(): glob.glob(opj(add_path, 'report', '*sxw')) + glob.glob(opj(add_path, 'report', '*xsl')) ), - ( opj(addon_path, 'security'), + ( + opj(addon_path, 'security'), glob.glob(opj(add_path, 'security', '*csv')) + glob.glob(opj(add_path, 'security', '*xml')) ), + ( + opj(addon_path, 'rng'), + glob.glob(opj(add_path, 'rng', '*rng')) + ) ] files.extend(pathfiles) return files From 7ad6923620c8bf08eed11b86b8f58464a3add5f7 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 16:29:23 +0100 Subject: [PATCH 05/61] a filestore per database bzr revid: stephane@tinyerp.com-20081113152923-2jgrsq4na87sx0z4 --- addons/document/document.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/document/document.py b/addons/document/document.py index 31de70cc63f..498534379f7 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -471,7 +471,7 @@ def random_name(): def create_directory(path): dir_name = random_name() path = os.path.join(path,dir_name) - os.mkdir(path) + os.makedirs(path) return dir_name class document_file(osv.osv): @@ -484,7 +484,7 @@ class document_file(osv.osv): result[id] = d elif m=='fs': try: - path = os.path.join(os.getcwd(),'filestore') + path = os.path.join(os.getcwd(), cr.dbname,'filestore') value = file(os.path.join(path,r), 'rb').read() result[id] = base64.encodestring(value) except: @@ -500,9 +500,9 @@ class document_file(osv.osv): if not value: return True if (not context) or context.get('store_method','fs')=='fs': - path = os.path.join(os.getcwd(), "filestore") + path = os.path.join(os.getcwd(), cr.dbname, "filestore") if not os.path.isdir(path): - os.mkdir(path) + os.makedirs(path) flag = None # This can be improved for dirs in os.listdir(path): @@ -620,7 +620,7 @@ class document_file(osv.osv): for f in self.browse(cr, uid, ids, context): if f.store_method=='fs': try: - path = os.path.join(os.getcwd(),'filestore',f.store_fname) + path = os.path.join(os.getcwd(), cr.dbname, 'filestore',f.store_fname) os.unlink(path) except: pass From aa62691f5a44806fe55888778d72c78a1da28de3 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Thu, 13 Nov 2008 16:35:28 +0100 Subject: [PATCH 06/61] ;-) swap filestore and cr.dbname bzr revid: stephane@tinyerp.com-20081113153528-5f2i411vnvjcuplu --- addons/document/document.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/document/document.py b/addons/document/document.py index 498534379f7..21064169335 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -484,7 +484,7 @@ class document_file(osv.osv): result[id] = d elif m=='fs': try: - path = os.path.join(os.getcwd(), cr.dbname,'filestore') + path = os.path.join(os.getcwd(), 'filestore', cr.dbname) value = file(os.path.join(path,r), 'rb').read() result[id] = base64.encodestring(value) except: @@ -500,7 +500,7 @@ class document_file(osv.osv): if not value: return True if (not context) or context.get('store_method','fs')=='fs': - path = os.path.join(os.getcwd(), cr.dbname, "filestore") + path = os.path.join(os.getcwd(), "filestore", cr.dbname) if not os.path.isdir(path): os.makedirs(path) flag = None From 9ef1d2071d5299e1dbc969d0845f959371d2e9dd Mon Sep 17 00:00:00 2001 From: Mantavya Gajjar Date: Fri, 14 Nov 2008 12:37:02 +0530 Subject: [PATCH 07/61] small change to test the wiki bzr revid: mga@tinyerp.com-20081114070702-jkox5mlj92hy420d --- addons/product/product.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/product/product.py b/addons/product/product.py index 16674231236..19fed206d07 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -419,6 +419,7 @@ class product_product(osv.osv): 'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the packing order and is mainly used if you use the EDI module."), 'price_extra': fields.float('Variant Price Extra', digits=(16, int(config['price_accuracy']))), 'price_margin': fields.float('Variant Price Margin', digits=(16, int(config['price_accuracy']))), + 'doc':fields.many2one('base.wiki', 'Wiki'), } def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id): From 4008ff421f915a44f463a8570ef04b56dbc502a4 Mon Sep 17 00:00:00 2001 From: Mantavya Gajjar Date: Fri, 14 Nov 2008 12:51:08 +0530 Subject: [PATCH 08/61] remove small change which comitt here by mistake bzr revid: mga@tinyerp.com-20081114072108-zkbt3cty77y3ngjv --- addons/product/product.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/product/product.py b/addons/product/product.py index 19fed206d07..16674231236 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -419,7 +419,6 @@ class product_product(osv.osv): 'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units', help="Gives the different ways to package the same product. This has no impact on the packing order and is mainly used if you use the EDI module."), 'price_extra': fields.float('Variant Price Extra', digits=(16, int(config['price_accuracy']))), 'price_margin': fields.float('Variant Price Margin', digits=(16, int(config['price_accuracy']))), - 'doc':fields.many2one('base.wiki', 'Wiki'), } def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id): From 5539019f6a115519e4d11968d7f64a1a9d8216e2 Mon Sep 17 00:00:00 2001 From: Mustufa Rangwala Date: Fri, 14 Nov 2008 15:07:02 +0530 Subject: [PATCH 09/61] removed bug from charts of account: it did not open lines for consolidated tree with different accounts which does not comes in its range of parent_left bzr revid: mra@tinyerp.com-20081114093702-ed0bmk6momj3h6r7 --- addons/account/account.py | 7 +++++++ addons/account/wizard/wizard_move_line_select.py | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/account/account.py b/addons/account/account.py index 37bf0fc2023..f3be0d159ab 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -154,6 +154,13 @@ class account_account(osv.osv): args[pos] = ('id','in',ids1) pos+=1 + if context and context.has_key('consolidate_childs'): #add concolidated childs of accounts + ids = super(account_account,self).search(cr, uid, args, offset, limit, + order, context=context, count=count) + for consolidate_child in self.browse(cr, uid, context['account_id']).child_consol_ids: + ids.append(consolidate_child.id) + return ids + return super(account_account,self).search(cr, uid, args, offset, limit, order, context=context, count=count) diff --git a/addons/account/wizard/wizard_move_line_select.py b/addons/account/wizard/wizard_move_line_select.py index 645d9f90b84..64c80a8ec8d 100644 --- a/addons/account/wizard/wizard_move_line_select.py +++ b/addons/account/wizard/wizard_move_line_select.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2008 Tiny SPRL (). All Rights Reserved # $Id$ # @@ -27,6 +27,7 @@ class wizard_move_line_select(wizard.interface): def _open_window(self, cr, uid, data, context): mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data') act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window') + account_obj = pooler.get_pool(cr.dbname).get('account.account') fiscalyear_obj = pooler.get_pool(cr.dbname).get('account.fiscalyear') if not context.get('fiscalyear', False): @@ -48,6 +49,10 @@ class wizard_move_line_select(wizard.interface): 'fiscalyear': context.get('fiscalyear', False), 'account_id': data['id'] } + if data['id']: + acc_data = account_obj.browse(cr, uid, data['id']).child_consol_ids + if acc_data: + result['context'].update({'consolidate_childs': True}) result['domain']=result['domain'][0:-1]+','+domain+result['domain'][-1] return result From 882cd7027feca65405cfdf1337064aa676830d4b Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 14 Nov 2008 15:04:53 +0100 Subject: [PATCH 10/61] Use the last version of pydot bzr revid: stephane@tinyerp.com-20081114140453-rwcic5h0ry8uf0z5 --- bin/addons/base/ir/workflow/print_instance.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/addons/base/ir/workflow/print_instance.py b/bin/addons/base/ir/workflow/print_instance.py index 8d40bbeac47..826f753e594 100644 --- a/bin/addons/base/ir/workflow/print_instance.py +++ b/bin/addons/base/ir/workflow/print_instance.py @@ -38,7 +38,7 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}): if n['subflow_id'] and nested: cr.execute('select * from wkf where id=%d', (n['subflow_id'],)) wkfinfo = cr.dictfetchone() - graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize=12, label = "Subflow: "+n['name']+'\\nOSV: '+wkfinfo['osv']) + graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "Subflow: "+n['name']+'\\nOSV: '+wkfinfo['osv']) (s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem) graph.add_subgraph(graph2) actfrom[n['id']] = s2 @@ -76,7 +76,7 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}): activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0]) activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) - graph.add_edge(pydot.Edge( activity_from ,activity_to, fontsize=10, **args)) + graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args)) nodes = cr.dictfetchall() cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%d limit 1', (wkf_id,)) start = cr.fetchone()[0] @@ -143,14 +143,14 @@ showpage''' showpage''' else: inst_id = inst_id[0] - graph = pydot.Dot(fontsize=16, label="\\n\\nWorkflow: %s\\n OSV: %s" % (wkfinfo['name'],wkfinfo['osv'])) + graph = pydot.Dot(fontsize='16', label="""\\\n\\nWorkflow: %s\\n OSV: %s""" % (wkfinfo['name'],wkfinfo['osv'])) graph.set('size', '10.7,7.3') graph.set('center', '1') graph.set('ratio', 'auto') graph.set('rotate', '90') graph.set('rankdir', 'LR') graph_instance_get(cr, graph, inst_id, data.get('nested', False)) - ps_string = graph.create_ps(prog='dot') + ps_string = graph.create(prog='dot', format='ps') except Exception, e: import traceback, sys tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) From 3dae09ac14990a82aa9ef8afc2a0429b877ab123 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Fri, 14 Nov 2008 16:06:02 +0100 Subject: [PATCH 11/61] Remove pydot, pychart and reportlab. bzr revid: stephane@tinyerp.com-20081114150602-cpp1eeo3d0837fev --- bin/addons/base/ir/workflow/pydot/LICENSE | 16 - bin/addons/base/ir/workflow/pydot/PKG-INFO | 33 - bin/addons/base/ir/workflow/pydot/README | 29 - bin/addons/base/ir/workflow/pydot/__init__.py | 1 - .../base/ir/workflow/pydot/dot_parser.py | 352 - bin/addons/base/ir/workflow/pydot/pydot.py | 1075 --- bin/addons/base/ir/workflow/pydot/setup.py | 23 - bin/pychart/__init__.py | 21 - bin/pychart/afm/AvantGarde_Book.py | 5 - bin/pychart/afm/AvantGarde_BookOblique.py | 5 - bin/pychart/afm/AvantGarde_Demi.py | 5 - bin/pychart/afm/AvantGarde_DemiOblique.py | 5 - bin/pychart/afm/Bookman_Demi.py | 5 - bin/pychart/afm/Bookman_DemiItalic.py | 5 - bin/pychart/afm/Bookman_Light.py | 5 - bin/pychart/afm/Bookman_LightItalic.py | 5 - bin/pychart/afm/Courier.py | 5 - bin/pychart/afm/Courier_Bold.py | 5 - bin/pychart/afm/Courier_BoldOblique.py | 5 - bin/pychart/afm/Courier_Oblique.py | 5 - bin/pychart/afm/Helvetica.py | 5 - bin/pychart/afm/Helvetica_Bold.py | 5 - bin/pychart/afm/Helvetica_BoldOblique.py | 5 - bin/pychart/afm/Helvetica_Light.py | 5 - bin/pychart/afm/Helvetica_LightOblique.py | 5 - bin/pychart/afm/Helvetica_Narrow.py | 5 - bin/pychart/afm/Helvetica_Narrow_Bold.py | 5 - .../afm/Helvetica_Narrow_BoldOblique.py | 5 - bin/pychart/afm/Helvetica_Narrow_Oblique.py | 5 - bin/pychart/afm/Helvetica_Oblique.py | 5 - bin/pychart/afm/NewCenturySchlbk_Bold.py | 5 - .../afm/NewCenturySchlbk_BoldItalic.py | 5 - bin/pychart/afm/NewCenturySchlbk_Italic.py | 5 - bin/pychart/afm/NewCenturySchlbk_Roman.py | 5 - bin/pychart/afm/Palatino_Bold.py | 5 - bin/pychart/afm/Palatino_BoldItalic.py | 5 - bin/pychart/afm/Palatino_Italic.py | 5 - bin/pychart/afm/Palatino_Roman.py | 5 - bin/pychart/afm/Symbol.py | 5 - bin/pychart/afm/Times_Bold.py | 5 - bin/pychart/afm/Times_BoldItalic.py | 5 - bin/pychart/afm/Times_Italic.py | 5 - bin/pychart/afm/Times_Roman.py | 5 - bin/pychart/afm/Utopia_Bold.py | 5 - bin/pychart/afm/Utopia_BoldItalic.py | 5 - bin/pychart/afm/Utopia_Italic.py | 5 - bin/pychart/afm/Utopia_Regular.py | 5 - bin/pychart/afm/ZapfChancery_MediumItalic.py | 5 - bin/pychart/afm/ZapfDingbats.py | 5 - bin/pychart/afm/__init__.py | 1 - bin/pychart/afm/dir.py | 2 - bin/pychart/area.py | 249 - bin/pychart/area_doc.py | 63 - bin/pychart/arrow.py | 184 - bin/pychart/arrow_doc.py | 9 - bin/pychart/axis.py | 241 - bin/pychart/axis_doc.py | 53 - bin/pychart/axis_x_doc.py | 65 - bin/pychart/axis_y_doc.py | 65 - bin/pychart/bar_plot.py | 268 - bin/pychart/bar_plot_doc.py | 79 - bin/pychart/basecanvas.py | 505 -- bin/pychart/canvas.py | 123 - bin/pychart/category_coord.py | 47 - bin/pychart/chart_data.py | 387 - bin/pychart/chart_object.py | 73 - bin/pychart/color.py | 611 -- bin/pychart/color_doc.py | 7 - bin/pychart/coord.py | 49 - bin/pychart/doc_support.py | 122 - bin/pychart/empty_docs.py | 44 - bin/pychart/error_bar.py | 159 - bin/pychart/error_bar_doc.py | 34 - bin/pychart/fill_style.py | 285 - bin/pychart/fill_style_doc.py | 7 - bin/pychart/font.py | 454 - bin/pychart/generate_docs.py | 125 - bin/pychart/gs_frontend.py | 87 - bin/pychart/interval_bar_plot.py | 228 - bin/pychart/legend.py | 181 - bin/pychart/legend_doc.py | 28 - bin/pychart/line_plot.py | 150 - bin/pychart/line_plot_doc.py | 25 - bin/pychart/line_style.py | 158 - bin/pychart/line_style_doc.py | 24 - bin/pychart/linear_coord.py | 37 - bin/pychart/log_coord.py | 48 - bin/pychart/object_set.py | 43 - bin/pychart/pdfcanvas.py | 297 - bin/pychart/pie_plot.py | 148 - bin/pychart/pie_plot_doc.py | 34 - bin/pychart/pngcanvas.py | 55 - bin/pychart/pscanvas.py | 257 - bin/pychart/pychart_types.py | 70 - bin/pychart/pychart_util.py | 142 - bin/pychart/range_plot.py | 144 - bin/pychart/range_plot_doc.py | 11 - bin/pychart/rose_plot.py | 143 - bin/pychart/scaling.py | 28 - bin/pychart/svgcanvas.py | 428 - bin/pychart/text_box.py | 152 - bin/pychart/text_box_doc.py | 20 - bin/pychart/theme.py | 234 - bin/pychart/tick_mark.py | 188 - bin/pychart/tick_mark_doc.py | 7 - bin/pychart/typechecker.py | 27 - bin/pychart/version.py | 16 - bin/pychart/x11canvas.py | 25 - bin/pychart/zap.py | 78 - bin/reportlab/MANIFEST.in | 6 - bin/reportlab/README | 35 - bin/reportlab/__init__.py | 33 - bin/reportlab/changes | 7788 ----------------- bin/reportlab/demos/colors/colortest.py | 105 - bin/reportlab/demos/gadflypaper/00readme.txt | 3 - bin/reportlab/demos/gadflypaper/gfe.py | 903 -- bin/reportlab/demos/odyssey/00readme.txt | 56 - bin/reportlab/demos/odyssey/dodyssey.py | 254 - bin/reportlab/demos/odyssey/fodyssey.py | 165 - bin/reportlab/demos/odyssey/odyssey.py | 151 - bin/reportlab/demos/odyssey/odyssey.txt | 207 - bin/reportlab/demos/rlzope/readme.txt | 73 - bin/reportlab/demos/rlzope/rlzope.py | 169 - bin/reportlab/demos/stdfonts/00readme.txt | 7 - bin/reportlab/demos/stdfonts/stdfonts.py | 74 - bin/reportlab/demos/tests/testdemos.py | 14 - bin/reportlab/docs/00readme.txt | 7 - bin/reportlab/docs/genAll.py | 37 - bin/reportlab/docs/graphguide/ch1_intro.py | 105 - bin/reportlab/docs/graphguide/ch2_concepts.py | 376 - bin/reportlab/docs/graphguide/ch3_shapes.py | 416 - bin/reportlab/docs/graphguide/ch4_widgets.py | 422 - bin/reportlab/docs/graphguide/ch5_charts.py | 1229 --- .../docs/graphguide/gengraphguide.py | 59 - bin/reportlab/docs/images/Edit_Prefs.gif | Bin 9685 -> 0 bytes bin/reportlab/docs/images/Python_21.gif | Bin 28106 -> 0 bytes bin/reportlab/docs/images/Python_21_HINT.gif | Bin 29117 -> 0 bytes bin/reportlab/docs/images/fileExchange.gif | Bin 37812 -> 0 bytes bin/reportlab/docs/images/jpn.gif | Bin 10513 -> 0 bytes bin/reportlab/docs/images/jpnchars.jpg | Bin 33898 -> 0 bytes bin/reportlab/docs/images/lj8100.jpg | Bin 12463 -> 0 bytes bin/reportlab/docs/images/replogo.a85 | 439 - bin/reportlab/docs/images/replogo.gif | Bin 16583 -> 0 bytes bin/reportlab/docs/reference/build.bat | 3 - bin/reportlab/docs/reference/genreference.py | 29 - bin/reportlab/docs/reference/reference.yml | 217 - bin/reportlab/docs/userguide/app_demos.py | 113 - bin/reportlab/docs/userguide/ch1_intro.py | 693 -- bin/reportlab/docs/userguide/ch2_graphics.py | 1180 --- bin/reportlab/docs/userguide/ch2a_fonts.py | 513 -- .../docs/userguide/ch3_pdffeatures.py | 271 - .../docs/userguide/ch4_platypus_concepts.py | 509 -- .../docs/userguide/ch5_paragraphs.py | 319 - bin/reportlab/docs/userguide/ch6_tables.py | 419 - bin/reportlab/docs/userguide/ch7_custom.py | 81 - bin/reportlab/docs/userguide/ch9_future.py | 35 - bin/reportlab/docs/userguide/genuserguide.py | 85 - bin/reportlab/docs/userguide/testfile.txt | 2 - bin/reportlab/extensions/README | 25 - bin/reportlab/extensions/__init__.py | 7 - bin/reportlab/extensions/barcode/__init__.py | 33 - bin/reportlab/extensions/barcode/code128.py | 321 - bin/reportlab/extensions/barcode/code39.py | 255 - bin/reportlab/extensions/barcode/code93.py | 232 - bin/reportlab/extensions/barcode/common.py | 705 -- bin/reportlab/extensions/barcode/fourstate.py | 81 - bin/reportlab/extensions/barcode/out.pdf | Bin 7359 -> 0 bytes bin/reportlab/extensions/barcode/test.py | 55 - bin/reportlab/extensions/barcode/usps.py | 231 - bin/reportlab/fonts/00readme.txt | 8 - bin/reportlab/fonts/LeERC___.AFM | 93 - bin/reportlab/fonts/LeERC___.PFB | Bin 48590 -> 0 bytes bin/reportlab/fonts/luxiserif.ttf | Bin 88732 -> 0 bytes bin/reportlab/fonts/luxiserif_license.txt | 40 - bin/reportlab/fonts/rina.ttf | Bin 36176 -> 0 bytes bin/reportlab/fonts/rina_license.txt | 93 - bin/reportlab/graphics/__init__.py | 4 - bin/reportlab/graphics/barcode/README | 59 - bin/reportlab/graphics/barcode/TODO | 24 - bin/reportlab/graphics/barcode/VERSION | 1 - bin/reportlab/graphics/barcode/__init__.py | 126 - bin/reportlab/graphics/barcode/code128.py | 322 - bin/reportlab/graphics/barcode/code39.py | 248 - bin/reportlab/graphics/barcode/code93.py | 236 - bin/reportlab/graphics/barcode/common.py | 748 -- bin/reportlab/graphics/barcode/eanbc.py | 339 - bin/reportlab/graphics/barcode/fourstate.py | 81 - bin/reportlab/graphics/barcode/test.py | 185 - bin/reportlab/graphics/barcode/usps.py | 228 - bin/reportlab/graphics/barcode/widgets.py | 304 - bin/reportlab/graphics/charts/__init__.py | 4 - bin/reportlab/graphics/charts/areas.py | 92 - bin/reportlab/graphics/charts/axes.py | 1979 ----- bin/reportlab/graphics/charts/barcharts.py | 1972 ----- bin/reportlab/graphics/charts/dotbox.py | 165 - bin/reportlab/graphics/charts/doughnut.py | 349 - bin/reportlab/graphics/charts/legends.py | 611 -- bin/reportlab/graphics/charts/linecharts.py | 695 -- bin/reportlab/graphics/charts/lineplots.py | 1083 --- bin/reportlab/graphics/charts/markers.py | 81 - bin/reportlab/graphics/charts/piecharts.py | 1279 --- bin/reportlab/graphics/charts/slidebox.py | 186 - bin/reportlab/graphics/charts/spider.py | 407 - bin/reportlab/graphics/charts/textlabels.py | 442 - bin/reportlab/graphics/charts/utils.py | 191 - bin/reportlab/graphics/charts/utils3d.py | 233 - bin/reportlab/graphics/renderPDF.py | 360 - bin/reportlab/graphics/renderPM.py | 663 -- bin/reportlab/graphics/renderPS.py | 871 -- bin/reportlab/graphics/renderSVG.py | 828 -- bin/reportlab/graphics/renderbase.py | 351 - bin/reportlab/graphics/samples/__init__.py | 0 bin/reportlab/graphics/samples/bubble.py | 73 - .../graphics/samples/clustered_bar.py | 84 - .../graphics/samples/clustered_column.py | 83 - bin/reportlab/graphics/samples/excelcolors.py | 45 - .../graphics/samples/exploded_pie.py | 65 - .../graphics/samples/filled_radar.py | 54 - bin/reportlab/graphics/samples/line_chart.py | 83 - .../samples/linechart_with_markers.py | 94 - bin/reportlab/graphics/samples/radar.py | 66 - bin/reportlab/graphics/samples/runall.py | 59 - bin/reportlab/graphics/samples/scatter.py | 71 - .../graphics/samples/scatter_lines.py | 82 - .../graphics/samples/scatter_lines_markers.py | 72 - bin/reportlab/graphics/samples/simple_pie.py | 61 - bin/reportlab/graphics/samples/stacked_bar.py | 85 - .../graphics/samples/stacked_column.py | 84 - bin/reportlab/graphics/shapes.py | 1289 --- bin/reportlab/graphics/testdrawings.py | 294 - bin/reportlab/graphics/testshapes.py | 574 -- bin/reportlab/graphics/widgetbase.py | 502 -- bin/reportlab/graphics/widgets/__init__.py | 4 - bin/reportlab/graphics/widgets/eventcal.py | 303 - bin/reportlab/graphics/widgets/flags.py | 879 -- bin/reportlab/graphics/widgets/grids.py | 504 -- bin/reportlab/graphics/widgets/markers.py | 228 - .../graphics/widgets/signsandsymbols.py | 919 -- bin/reportlab/lib/PyFontify.py | 155 - bin/reportlab/lib/__init__.py | 7 - bin/reportlab/lib/abag.py | 44 - bin/reportlab/lib/attrmap.py | 138 - bin/reportlab/lib/codecharts.py | 365 - bin/reportlab/lib/colors.py | 565 -- bin/reportlab/lib/corp.py | 452 - bin/reportlab/lib/enums.py | 11 - bin/reportlab/lib/extformat.py | 81 - bin/reportlab/lib/fonts.py | 89 - bin/reportlab/lib/formatters.py | 100 - bin/reportlab/lib/logger.py | 61 - bin/reportlab/lib/normalDate.py | 605 -- bin/reportlab/lib/pagesizes.py | 55 - bin/reportlab/lib/randomtext.py | 348 - bin/reportlab/lib/rltempfile.py | 29 - bin/reportlab/lib/rparsexml.py | 431 - bin/reportlab/lib/sequencer.py | 284 - bin/reportlab/lib/set_ops.py | 38 - bin/reportlab/lib/styles.py | 257 - bin/reportlab/lib/textsplit.py | 210 - bin/reportlab/lib/tocindex.py | 294 - bin/reportlab/lib/units.py | 23 - bin/reportlab/lib/utils.py | 827 -- bin/reportlab/lib/validators.py | 324 - bin/reportlab/lib/xmllib.py | 769 -- bin/reportlab/lib/yaml.py | 188 - bin/reportlab/license.txt | 29 - bin/reportlab/pdfbase/__init__.py | 6 - bin/reportlab/pdfbase/_can_cmap_data.py | 58 - bin/reportlab/pdfbase/_cidfontdata.py | 483 - bin/reportlab/pdfbase/_fontdata.py | 2590 ------ bin/reportlab/pdfbase/cidfonts.py | 516 -- bin/reportlab/pdfbase/pdfdoc.py | 1892 ---- bin/reportlab/pdfbase/pdfform.py | 632 -- bin/reportlab/pdfbase/pdfmetrics.py | 796 -- bin/reportlab/pdfbase/pdfpattern.py | 59 - bin/reportlab/pdfbase/pdfutils.py | 460 - bin/reportlab/pdfbase/rl_codecs.py | 1027 --- bin/reportlab/pdfbase/ttfonts.py | 1113 --- bin/reportlab/pdfgen/__init__.py | 5 - bin/reportlab/pdfgen/canvas.py | 1462 ---- bin/reportlab/pdfgen/pathobject.py | 101 - bin/reportlab/pdfgen/pdfgeom.py | 77 - bin/reportlab/pdfgen/pdfimages.py | 188 - bin/reportlab/pdfgen/pycanvas.py | 309 - bin/reportlab/pdfgen/textobject.py | 398 - bin/reportlab/platypus/__init__.py | 15 - bin/reportlab/platypus/doctemplate.py | 953 -- bin/reportlab/platypus/figures.py | 372 - bin/reportlab/platypus/flowables.py | 1051 --- bin/reportlab/platypus/frames.py | 206 - bin/reportlab/platypus/para.py | 2394 ----- bin/reportlab/platypus/paragraph.py | 1082 --- bin/reportlab/platypus/paraparser.py | 898 -- bin/reportlab/platypus/tableofcontents.py | 329 - bin/reportlab/platypus/tables.py | 1384 --- bin/reportlab/platypus/xpreformatted.py | 316 - bin/reportlab/rl_config.py | 155 - bin/reportlab/setup.py | 258 - bin/reportlab/test/00readme.txt | 20 - bin/reportlab/test/__init__.py | 3 - bin/reportlab/test/pythonpowered.gif | Bin 1077 -> 0 bytes bin/reportlab/test/runAll.py | 93 - bin/reportlab/test/test_charts_textlabels.py | 279 - bin/reportlab/test/test_docs_build.py | 51 - bin/reportlab/test/test_docstrings.py | 175 - bin/reportlab/test/test_extra.py | 102 - bin/reportlab/test/test_graphics_charts.py | 423 - bin/reportlab/test/test_graphics_images.py | 91 - bin/reportlab/test/test_graphics_layout.py | 82 - bin/reportlab/test/test_graphics_speed.py | 86 - bin/reportlab/test/test_hello.py | 34 - bin/reportlab/test/test_images.py | 55 - bin/reportlab/test/test_invariant.py | 56 - bin/reportlab/test/test_lib_colors.py | 161 - bin/reportlab/test/test_lib_sequencer.py | 123 - bin/reportlab/test/test_lib_utils.py | 115 - bin/reportlab/test/test_lib_validators.py | 165 - bin/reportlab/test/test_multibyte_chs.py | 84 - bin/reportlab/test/test_multibyte_cht.py | 131 - bin/reportlab/test/test_multibyte_jpn.py | 460 - bin/reportlab/test/test_multibyte_kor.py | 125 - bin/reportlab/test/test_paragraphs.py | 174 - bin/reportlab/test/test_pdfbase_encodings.py | 255 - bin/reportlab/test/test_pdfbase_fontembed.py | 90 - bin/reportlab/test/test_pdfbase_pdfmetrics.py | 121 - bin/reportlab/test/test_pdfbase_pdfutils.py | 52 - bin/reportlab/test/test_pdfbase_postscript.py | 77 - bin/reportlab/test/test_pdfbase_ttfonts.py | 391 - bin/reportlab/test/test_pdfgen_callback.py | 39 - bin/reportlab/test/test_pdfgen_general.py | 840 -- bin/reportlab/test/test_pdfgen_links.py | 187 - bin/reportlab/test/test_pdfgen_pagemodes.py | 70 - bin/reportlab/test/test_pdfgen_pycanvas.py | 790 -- bin/reportlab/test/test_platypus_breaking.py | 114 - bin/reportlab/test/test_platypus_general.py | 581 -- bin/reportlab/test/test_platypus_indents.py | 140 - bin/reportlab/test/test_platypus_leftright.py | 158 - .../test/test_platypus_paragraphs.py | 184 - .../test/test_platypus_paraparser.py | 91 - bin/reportlab/test/test_platypus_pto.py | 195 - bin/reportlab/test/test_platypus_tables.py | 717 -- bin/reportlab/test/test_platypus_toc.py | 183 - bin/reportlab/test/test_platypus_xref.py | 140 - bin/reportlab/test/test_pyfiles.py | 158 - bin/reportlab/test/test_renderSVG.py | 165 - bin/reportlab/test/test_rl_accel.py | 168 - bin/reportlab/test/test_source_chars.py | 95 - bin/reportlab/test/test_table_layout.py | 425 - bin/reportlab/test/test_tools_pythonpoint.py | 39 - bin/reportlab/test/test_utils.py | 37 - bin/reportlab/test/test_widgetbase_tpc.py | 138 - bin/reportlab/test/test_widgets_grids.py | 454 - bin/reportlab/test/unittest.py | 723 -- bin/reportlab/test/utils.py | 308 - bin/reportlab/tools/README | 10 - bin/reportlab/tools/__init__.py | 3 - bin/reportlab/tools/docco/README | 8 - bin/reportlab/tools/docco/__init__.py | 3 - bin/reportlab/tools/docco/codegrab.py | 228 - bin/reportlab/tools/docco/docpy.py | 1247 --- bin/reportlab/tools/docco/examples.py | 851 -- bin/reportlab/tools/docco/graphdocpy.py | 980 --- bin/reportlab/tools/docco/rl_doc_utils.py | 411 - bin/reportlab/tools/docco/rltemplate.py | 135 - bin/reportlab/tools/docco/stylesheet.py | 165 - bin/reportlab/tools/docco/t_parse.py | 247 - bin/reportlab/tools/docco/yaml.py | 201 - bin/reportlab/tools/docco/yaml2pdf.py | 104 - bin/reportlab/tools/py2pdf/README | 8 - bin/reportlab/tools/py2pdf/__init__.py | 3 - bin/reportlab/tools/py2pdf/demo-config.txt | 11 - bin/reportlab/tools/py2pdf/demo.py | 198 - bin/reportlab/tools/py2pdf/idle_print.py | 56 - bin/reportlab/tools/py2pdf/py2pdf.py | 1567 ---- bin/reportlab/tools/py2pdf/vertpython.jpg | Bin 22872 -> 0 bytes bin/reportlab/tools/pythonpoint/README | 29 - bin/reportlab/tools/pythonpoint/__init__.py | 3 - .../tools/pythonpoint/customshapes.py | 298 - .../tools/pythonpoint/demos/LeERC___.AFM | 93 - .../tools/pythonpoint/demos/LeERC___.PFB | Bin 48590 -> 0 bytes .../tools/pythonpoint/demos/examples.py | 841 -- .../tools/pythonpoint/demos/figures.xml | 71 - bin/reportlab/tools/pythonpoint/demos/htu.xml | 96 - .../tools/pythonpoint/demos/leftlogo.a85 | 53 - .../tools/pythonpoint/demos/leftlogo.gif | Bin 2198 -> 0 bytes .../tools/pythonpoint/demos/lj8100.jpg | Bin 12463 -> 0 bytes .../tools/pythonpoint/demos/monterey.xml | 306 - .../tools/pythonpoint/demos/outline.gif | Bin 12918 -> 0 bytes .../tools/pythonpoint/demos/pplogo.gif | Bin 3429 -> 0 bytes .../tools/pythonpoint/demos/python.gif | Bin 125666 -> 0 bytes .../tools/pythonpoint/demos/pythonpoint.xml | 1051 --- .../tools/pythonpoint/demos/slidebox.py | 29 - .../tools/pythonpoint/demos/spectrum.png | Bin 1855 -> 0 bytes .../tools/pythonpoint/demos/vertpython.gif | Bin 20910 -> 0 bytes .../tools/pythonpoint/pythonpoint.dtd | 275 - .../tools/pythonpoint/pythonpoint.py | 1124 --- bin/reportlab/tools/pythonpoint/stdparser.py | 813 -- .../tools/pythonpoint/styles/__init__.py | 3 - .../tools/pythonpoint/styles/horrible.py | 101 - bin/reportlab/tools/pythonpoint/styles/htu.py | 158 - .../tools/pythonpoint/styles/modern.py | 120 - .../tools/pythonpoint/styles/projection.py | 106 - .../tools/pythonpoint/styles/standard.py | 132 - bin/reportlab/tools/utils/add_bleed.py | 28 - bin/reportlab/tools/utils/dumpttf.py | 60 - 405 files changed, 103567 deletions(-) delete mode 100755 bin/addons/base/ir/workflow/pydot/LICENSE delete mode 100755 bin/addons/base/ir/workflow/pydot/PKG-INFO delete mode 100755 bin/addons/base/ir/workflow/pydot/README delete mode 100644 bin/addons/base/ir/workflow/pydot/__init__.py delete mode 100644 bin/addons/base/ir/workflow/pydot/dot_parser.py delete mode 100644 bin/addons/base/ir/workflow/pydot/pydot.py delete mode 100644 bin/addons/base/ir/workflow/pydot/setup.py delete mode 100644 bin/pychart/__init__.py delete mode 100644 bin/pychart/afm/AvantGarde_Book.py delete mode 100644 bin/pychart/afm/AvantGarde_BookOblique.py delete mode 100644 bin/pychart/afm/AvantGarde_Demi.py delete mode 100644 bin/pychart/afm/AvantGarde_DemiOblique.py delete mode 100644 bin/pychart/afm/Bookman_Demi.py delete mode 100644 bin/pychart/afm/Bookman_DemiItalic.py delete mode 100644 bin/pychart/afm/Bookman_Light.py delete mode 100644 bin/pychart/afm/Bookman_LightItalic.py delete mode 100644 bin/pychart/afm/Courier.py delete mode 100644 bin/pychart/afm/Courier_Bold.py delete mode 100644 bin/pychart/afm/Courier_BoldOblique.py delete mode 100644 bin/pychart/afm/Courier_Oblique.py delete mode 100644 bin/pychart/afm/Helvetica.py delete mode 100644 bin/pychart/afm/Helvetica_Bold.py delete mode 100644 bin/pychart/afm/Helvetica_BoldOblique.py delete mode 100644 bin/pychart/afm/Helvetica_Light.py delete mode 100644 bin/pychart/afm/Helvetica_LightOblique.py delete mode 100644 bin/pychart/afm/Helvetica_Narrow.py delete mode 100644 bin/pychart/afm/Helvetica_Narrow_Bold.py delete mode 100644 bin/pychart/afm/Helvetica_Narrow_BoldOblique.py delete mode 100644 bin/pychart/afm/Helvetica_Narrow_Oblique.py delete mode 100644 bin/pychart/afm/Helvetica_Oblique.py delete mode 100644 bin/pychart/afm/NewCenturySchlbk_Bold.py delete mode 100644 bin/pychart/afm/NewCenturySchlbk_BoldItalic.py delete mode 100644 bin/pychart/afm/NewCenturySchlbk_Italic.py delete mode 100644 bin/pychart/afm/NewCenturySchlbk_Roman.py delete mode 100644 bin/pychart/afm/Palatino_Bold.py delete mode 100644 bin/pychart/afm/Palatino_BoldItalic.py delete mode 100644 bin/pychart/afm/Palatino_Italic.py delete mode 100644 bin/pychart/afm/Palatino_Roman.py delete mode 100644 bin/pychart/afm/Symbol.py delete mode 100644 bin/pychart/afm/Times_Bold.py delete mode 100644 bin/pychart/afm/Times_BoldItalic.py delete mode 100644 bin/pychart/afm/Times_Italic.py delete mode 100644 bin/pychart/afm/Times_Roman.py delete mode 100644 bin/pychart/afm/Utopia_Bold.py delete mode 100644 bin/pychart/afm/Utopia_BoldItalic.py delete mode 100644 bin/pychart/afm/Utopia_Italic.py delete mode 100644 bin/pychart/afm/Utopia_Regular.py delete mode 100644 bin/pychart/afm/ZapfChancery_MediumItalic.py delete mode 100644 bin/pychart/afm/ZapfDingbats.py delete mode 100644 bin/pychart/afm/__init__.py delete mode 100644 bin/pychart/afm/dir.py delete mode 100644 bin/pychart/area.py delete mode 100644 bin/pychart/area_doc.py delete mode 100644 bin/pychart/arrow.py delete mode 100644 bin/pychart/arrow_doc.py delete mode 100644 bin/pychart/axis.py delete mode 100644 bin/pychart/axis_doc.py delete mode 100644 bin/pychart/axis_x_doc.py delete mode 100644 bin/pychart/axis_y_doc.py delete mode 100644 bin/pychart/bar_plot.py delete mode 100644 bin/pychart/bar_plot_doc.py delete mode 100644 bin/pychart/basecanvas.py delete mode 100644 bin/pychart/canvas.py delete mode 100644 bin/pychart/category_coord.py delete mode 100644 bin/pychart/chart_data.py delete mode 100644 bin/pychart/chart_object.py delete mode 100644 bin/pychart/color.py delete mode 100644 bin/pychart/color_doc.py delete mode 100644 bin/pychart/coord.py delete mode 100644 bin/pychart/doc_support.py delete mode 100644 bin/pychart/empty_docs.py delete mode 100644 bin/pychart/error_bar.py delete mode 100644 bin/pychart/error_bar_doc.py delete mode 100644 bin/pychart/fill_style.py delete mode 100644 bin/pychart/fill_style_doc.py delete mode 100644 bin/pychart/font.py delete mode 100644 bin/pychart/generate_docs.py delete mode 100644 bin/pychart/gs_frontend.py delete mode 100644 bin/pychart/interval_bar_plot.py delete mode 100644 bin/pychart/legend.py delete mode 100644 bin/pychart/legend_doc.py delete mode 100644 bin/pychart/line_plot.py delete mode 100644 bin/pychart/line_plot_doc.py delete mode 100644 bin/pychart/line_style.py delete mode 100644 bin/pychart/line_style_doc.py delete mode 100644 bin/pychart/linear_coord.py delete mode 100644 bin/pychart/log_coord.py delete mode 100644 bin/pychart/object_set.py delete mode 100644 bin/pychart/pdfcanvas.py delete mode 100644 bin/pychart/pie_plot.py delete mode 100644 bin/pychart/pie_plot_doc.py delete mode 100644 bin/pychart/pngcanvas.py delete mode 100644 bin/pychart/pscanvas.py delete mode 100644 bin/pychart/pychart_types.py delete mode 100644 bin/pychart/pychart_util.py delete mode 100644 bin/pychart/range_plot.py delete mode 100644 bin/pychart/range_plot_doc.py delete mode 100644 bin/pychart/rose_plot.py delete mode 100644 bin/pychart/scaling.py delete mode 100644 bin/pychart/svgcanvas.py delete mode 100644 bin/pychart/text_box.py delete mode 100644 bin/pychart/text_box_doc.py delete mode 100644 bin/pychart/theme.py delete mode 100644 bin/pychart/tick_mark.py delete mode 100644 bin/pychart/tick_mark_doc.py delete mode 100644 bin/pychart/typechecker.py delete mode 100644 bin/pychart/version.py delete mode 100644 bin/pychart/x11canvas.py delete mode 100644 bin/pychart/zap.py delete mode 100644 bin/reportlab/MANIFEST.in delete mode 100644 bin/reportlab/README delete mode 100644 bin/reportlab/__init__.py delete mode 100644 bin/reportlab/changes delete mode 100644 bin/reportlab/demos/colors/colortest.py delete mode 100644 bin/reportlab/demos/gadflypaper/00readme.txt delete mode 100644 bin/reportlab/demos/gadflypaper/gfe.py delete mode 100644 bin/reportlab/demos/odyssey/00readme.txt delete mode 100644 bin/reportlab/demos/odyssey/dodyssey.py delete mode 100644 bin/reportlab/demos/odyssey/fodyssey.py delete mode 100644 bin/reportlab/demos/odyssey/odyssey.py delete mode 100644 bin/reportlab/demos/odyssey/odyssey.txt delete mode 100644 bin/reportlab/demos/rlzope/readme.txt delete mode 100644 bin/reportlab/demos/rlzope/rlzope.py delete mode 100644 bin/reportlab/demos/stdfonts/00readme.txt delete mode 100644 bin/reportlab/demos/stdfonts/stdfonts.py delete mode 100644 bin/reportlab/demos/tests/testdemos.py delete mode 100644 bin/reportlab/docs/00readme.txt delete mode 100644 bin/reportlab/docs/genAll.py delete mode 100644 bin/reportlab/docs/graphguide/ch1_intro.py delete mode 100644 bin/reportlab/docs/graphguide/ch2_concepts.py delete mode 100644 bin/reportlab/docs/graphguide/ch3_shapes.py delete mode 100644 bin/reportlab/docs/graphguide/ch4_widgets.py delete mode 100644 bin/reportlab/docs/graphguide/ch5_charts.py delete mode 100644 bin/reportlab/docs/graphguide/gengraphguide.py delete mode 100644 bin/reportlab/docs/images/Edit_Prefs.gif delete mode 100644 bin/reportlab/docs/images/Python_21.gif delete mode 100644 bin/reportlab/docs/images/Python_21_HINT.gif delete mode 100644 bin/reportlab/docs/images/fileExchange.gif delete mode 100644 bin/reportlab/docs/images/jpn.gif delete mode 100644 bin/reportlab/docs/images/jpnchars.jpg delete mode 100644 bin/reportlab/docs/images/lj8100.jpg delete mode 100644 bin/reportlab/docs/images/replogo.a85 delete mode 100644 bin/reportlab/docs/images/replogo.gif delete mode 100755 bin/reportlab/docs/reference/build.bat delete mode 100644 bin/reportlab/docs/reference/genreference.py delete mode 100644 bin/reportlab/docs/reference/reference.yml delete mode 100644 bin/reportlab/docs/userguide/app_demos.py delete mode 100644 bin/reportlab/docs/userguide/ch1_intro.py delete mode 100644 bin/reportlab/docs/userguide/ch2_graphics.py delete mode 100644 bin/reportlab/docs/userguide/ch2a_fonts.py delete mode 100644 bin/reportlab/docs/userguide/ch3_pdffeatures.py delete mode 100644 bin/reportlab/docs/userguide/ch4_platypus_concepts.py delete mode 100644 bin/reportlab/docs/userguide/ch5_paragraphs.py delete mode 100644 bin/reportlab/docs/userguide/ch6_tables.py delete mode 100644 bin/reportlab/docs/userguide/ch7_custom.py delete mode 100644 bin/reportlab/docs/userguide/ch9_future.py delete mode 100644 bin/reportlab/docs/userguide/genuserguide.py delete mode 100644 bin/reportlab/docs/userguide/testfile.txt delete mode 100644 bin/reportlab/extensions/README delete mode 100644 bin/reportlab/extensions/__init__.py delete mode 100644 bin/reportlab/extensions/barcode/__init__.py delete mode 100644 bin/reportlab/extensions/barcode/code128.py delete mode 100644 bin/reportlab/extensions/barcode/code39.py delete mode 100644 bin/reportlab/extensions/barcode/code93.py delete mode 100644 bin/reportlab/extensions/barcode/common.py delete mode 100644 bin/reportlab/extensions/barcode/fourstate.py delete mode 100644 bin/reportlab/extensions/barcode/out.pdf delete mode 100644 bin/reportlab/extensions/barcode/test.py delete mode 100644 bin/reportlab/extensions/barcode/usps.py delete mode 100644 bin/reportlab/fonts/00readme.txt delete mode 100644 bin/reportlab/fonts/LeERC___.AFM delete mode 100644 bin/reportlab/fonts/LeERC___.PFB delete mode 100644 bin/reportlab/fonts/luxiserif.ttf delete mode 100644 bin/reportlab/fonts/luxiserif_license.txt delete mode 100644 bin/reportlab/fonts/rina.ttf delete mode 100644 bin/reportlab/fonts/rina_license.txt delete mode 100644 bin/reportlab/graphics/__init__.py delete mode 100644 bin/reportlab/graphics/barcode/README delete mode 100644 bin/reportlab/graphics/barcode/TODO delete mode 100644 bin/reportlab/graphics/barcode/VERSION delete mode 100644 bin/reportlab/graphics/barcode/__init__.py delete mode 100644 bin/reportlab/graphics/barcode/code128.py delete mode 100644 bin/reportlab/graphics/barcode/code39.py delete mode 100644 bin/reportlab/graphics/barcode/code93.py delete mode 100644 bin/reportlab/graphics/barcode/common.py delete mode 100644 bin/reportlab/graphics/barcode/eanbc.py delete mode 100644 bin/reportlab/graphics/barcode/fourstate.py delete mode 100644 bin/reportlab/graphics/barcode/test.py delete mode 100644 bin/reportlab/graphics/barcode/usps.py delete mode 100644 bin/reportlab/graphics/barcode/widgets.py delete mode 100644 bin/reportlab/graphics/charts/__init__.py delete mode 100644 bin/reportlab/graphics/charts/areas.py delete mode 100644 bin/reportlab/graphics/charts/axes.py delete mode 100644 bin/reportlab/graphics/charts/barcharts.py delete mode 100644 bin/reportlab/graphics/charts/dotbox.py delete mode 100644 bin/reportlab/graphics/charts/doughnut.py delete mode 100644 bin/reportlab/graphics/charts/legends.py delete mode 100644 bin/reportlab/graphics/charts/linecharts.py delete mode 100644 bin/reportlab/graphics/charts/lineplots.py delete mode 100644 bin/reportlab/graphics/charts/markers.py delete mode 100644 bin/reportlab/graphics/charts/piecharts.py delete mode 100644 bin/reportlab/graphics/charts/slidebox.py delete mode 100644 bin/reportlab/graphics/charts/spider.py delete mode 100644 bin/reportlab/graphics/charts/textlabels.py delete mode 100644 bin/reportlab/graphics/charts/utils.py delete mode 100644 bin/reportlab/graphics/charts/utils3d.py delete mode 100644 bin/reportlab/graphics/renderPDF.py delete mode 100644 bin/reportlab/graphics/renderPM.py delete mode 100644 bin/reportlab/graphics/renderPS.py delete mode 100644 bin/reportlab/graphics/renderSVG.py delete mode 100644 bin/reportlab/graphics/renderbase.py delete mode 100644 bin/reportlab/graphics/samples/__init__.py delete mode 100644 bin/reportlab/graphics/samples/bubble.py delete mode 100644 bin/reportlab/graphics/samples/clustered_bar.py delete mode 100644 bin/reportlab/graphics/samples/clustered_column.py delete mode 100644 bin/reportlab/graphics/samples/excelcolors.py delete mode 100644 bin/reportlab/graphics/samples/exploded_pie.py delete mode 100644 bin/reportlab/graphics/samples/filled_radar.py delete mode 100644 bin/reportlab/graphics/samples/line_chart.py delete mode 100644 bin/reportlab/graphics/samples/linechart_with_markers.py delete mode 100644 bin/reportlab/graphics/samples/radar.py delete mode 100644 bin/reportlab/graphics/samples/runall.py delete mode 100644 bin/reportlab/graphics/samples/scatter.py delete mode 100644 bin/reportlab/graphics/samples/scatter_lines.py delete mode 100644 bin/reportlab/graphics/samples/scatter_lines_markers.py delete mode 100644 bin/reportlab/graphics/samples/simple_pie.py delete mode 100644 bin/reportlab/graphics/samples/stacked_bar.py delete mode 100644 bin/reportlab/graphics/samples/stacked_column.py delete mode 100644 bin/reportlab/graphics/shapes.py delete mode 100644 bin/reportlab/graphics/testdrawings.py delete mode 100644 bin/reportlab/graphics/testshapes.py delete mode 100644 bin/reportlab/graphics/widgetbase.py delete mode 100644 bin/reportlab/graphics/widgets/__init__.py delete mode 100644 bin/reportlab/graphics/widgets/eventcal.py delete mode 100644 bin/reportlab/graphics/widgets/flags.py delete mode 100644 bin/reportlab/graphics/widgets/grids.py delete mode 100644 bin/reportlab/graphics/widgets/markers.py delete mode 100644 bin/reportlab/graphics/widgets/signsandsymbols.py delete mode 100644 bin/reportlab/lib/PyFontify.py delete mode 100644 bin/reportlab/lib/__init__.py delete mode 100644 bin/reportlab/lib/abag.py delete mode 100644 bin/reportlab/lib/attrmap.py delete mode 100644 bin/reportlab/lib/codecharts.py delete mode 100644 bin/reportlab/lib/colors.py delete mode 100644 bin/reportlab/lib/corp.py delete mode 100644 bin/reportlab/lib/enums.py delete mode 100644 bin/reportlab/lib/extformat.py delete mode 100644 bin/reportlab/lib/fonts.py delete mode 100644 bin/reportlab/lib/formatters.py delete mode 100644 bin/reportlab/lib/logger.py delete mode 100644 bin/reportlab/lib/normalDate.py delete mode 100644 bin/reportlab/lib/pagesizes.py delete mode 100644 bin/reportlab/lib/randomtext.py delete mode 100644 bin/reportlab/lib/rltempfile.py delete mode 100644 bin/reportlab/lib/rparsexml.py delete mode 100644 bin/reportlab/lib/sequencer.py delete mode 100644 bin/reportlab/lib/set_ops.py delete mode 100644 bin/reportlab/lib/styles.py delete mode 100644 bin/reportlab/lib/textsplit.py delete mode 100644 bin/reportlab/lib/tocindex.py delete mode 100644 bin/reportlab/lib/units.py delete mode 100644 bin/reportlab/lib/utils.py delete mode 100644 bin/reportlab/lib/validators.py delete mode 100644 bin/reportlab/lib/xmllib.py delete mode 100644 bin/reportlab/lib/yaml.py delete mode 100644 bin/reportlab/license.txt delete mode 100755 bin/reportlab/pdfbase/__init__.py delete mode 100644 bin/reportlab/pdfbase/_can_cmap_data.py delete mode 100644 bin/reportlab/pdfbase/_cidfontdata.py delete mode 100644 bin/reportlab/pdfbase/_fontdata.py delete mode 100644 bin/reportlab/pdfbase/cidfonts.py delete mode 100755 bin/reportlab/pdfbase/pdfdoc.py delete mode 100644 bin/reportlab/pdfbase/pdfform.py delete mode 100755 bin/reportlab/pdfbase/pdfmetrics.py delete mode 100644 bin/reportlab/pdfbase/pdfpattern.py delete mode 100755 bin/reportlab/pdfbase/pdfutils.py delete mode 100644 bin/reportlab/pdfbase/rl_codecs.py delete mode 100644 bin/reportlab/pdfbase/ttfonts.py delete mode 100755 bin/reportlab/pdfgen/__init__.py delete mode 100755 bin/reportlab/pdfgen/canvas.py delete mode 100755 bin/reportlab/pdfgen/pathobject.py delete mode 100755 bin/reportlab/pdfgen/pdfgeom.py delete mode 100644 bin/reportlab/pdfgen/pdfimages.py delete mode 100644 bin/reportlab/pdfgen/pycanvas.py delete mode 100755 bin/reportlab/pdfgen/textobject.py delete mode 100755 bin/reportlab/platypus/__init__.py delete mode 100644 bin/reportlab/platypus/doctemplate.py delete mode 100644 bin/reportlab/platypus/figures.py delete mode 100644 bin/reportlab/platypus/flowables.py delete mode 100644 bin/reportlab/platypus/frames.py delete mode 100644 bin/reportlab/platypus/para.py delete mode 100644 bin/reportlab/platypus/paragraph.py delete mode 100644 bin/reportlab/platypus/paraparser.py delete mode 100644 bin/reportlab/platypus/tableofcontents.py delete mode 100755 bin/reportlab/platypus/tables.py delete mode 100644 bin/reportlab/platypus/xpreformatted.py delete mode 100644 bin/reportlab/rl_config.py delete mode 100644 bin/reportlab/setup.py delete mode 100644 bin/reportlab/test/00readme.txt delete mode 100644 bin/reportlab/test/__init__.py delete mode 100644 bin/reportlab/test/pythonpowered.gif delete mode 100644 bin/reportlab/test/runAll.py delete mode 100644 bin/reportlab/test/test_charts_textlabels.py delete mode 100644 bin/reportlab/test/test_docs_build.py delete mode 100644 bin/reportlab/test/test_docstrings.py delete mode 100644 bin/reportlab/test/test_extra.py delete mode 100644 bin/reportlab/test/test_graphics_charts.py delete mode 100644 bin/reportlab/test/test_graphics_images.py delete mode 100644 bin/reportlab/test/test_graphics_layout.py delete mode 100644 bin/reportlab/test/test_graphics_speed.py delete mode 100644 bin/reportlab/test/test_hello.py delete mode 100644 bin/reportlab/test/test_images.py delete mode 100644 bin/reportlab/test/test_invariant.py delete mode 100644 bin/reportlab/test/test_lib_colors.py delete mode 100644 bin/reportlab/test/test_lib_sequencer.py delete mode 100644 bin/reportlab/test/test_lib_utils.py delete mode 100644 bin/reportlab/test/test_lib_validators.py delete mode 100644 bin/reportlab/test/test_multibyte_chs.py delete mode 100644 bin/reportlab/test/test_multibyte_cht.py delete mode 100644 bin/reportlab/test/test_multibyte_jpn.py delete mode 100644 bin/reportlab/test/test_multibyte_kor.py delete mode 100644 bin/reportlab/test/test_paragraphs.py delete mode 100644 bin/reportlab/test/test_pdfbase_encodings.py delete mode 100644 bin/reportlab/test/test_pdfbase_fontembed.py delete mode 100644 bin/reportlab/test/test_pdfbase_pdfmetrics.py delete mode 100644 bin/reportlab/test/test_pdfbase_pdfutils.py delete mode 100644 bin/reportlab/test/test_pdfbase_postscript.py delete mode 100644 bin/reportlab/test/test_pdfbase_ttfonts.py delete mode 100644 bin/reportlab/test/test_pdfgen_callback.py delete mode 100644 bin/reportlab/test/test_pdfgen_general.py delete mode 100644 bin/reportlab/test/test_pdfgen_links.py delete mode 100644 bin/reportlab/test/test_pdfgen_pagemodes.py delete mode 100644 bin/reportlab/test/test_pdfgen_pycanvas.py delete mode 100644 bin/reportlab/test/test_platypus_breaking.py delete mode 100644 bin/reportlab/test/test_platypus_general.py delete mode 100644 bin/reportlab/test/test_platypus_indents.py delete mode 100644 bin/reportlab/test/test_platypus_leftright.py delete mode 100644 bin/reportlab/test/test_platypus_paragraphs.py delete mode 100644 bin/reportlab/test/test_platypus_paraparser.py delete mode 100644 bin/reportlab/test/test_platypus_pto.py delete mode 100644 bin/reportlab/test/test_platypus_tables.py delete mode 100644 bin/reportlab/test/test_platypus_toc.py delete mode 100644 bin/reportlab/test/test_platypus_xref.py delete mode 100644 bin/reportlab/test/test_pyfiles.py delete mode 100755 bin/reportlab/test/test_renderSVG.py delete mode 100755 bin/reportlab/test/test_rl_accel.py delete mode 100644 bin/reportlab/test/test_source_chars.py delete mode 100644 bin/reportlab/test/test_table_layout.py delete mode 100644 bin/reportlab/test/test_tools_pythonpoint.py delete mode 100644 bin/reportlab/test/test_utils.py delete mode 100644 bin/reportlab/test/test_widgetbase_tpc.py delete mode 100644 bin/reportlab/test/test_widgets_grids.py delete mode 100644 bin/reportlab/test/unittest.py delete mode 100644 bin/reportlab/test/utils.py delete mode 100644 bin/reportlab/tools/README delete mode 100644 bin/reportlab/tools/__init__.py delete mode 100644 bin/reportlab/tools/docco/README delete mode 100644 bin/reportlab/tools/docco/__init__.py delete mode 100644 bin/reportlab/tools/docco/codegrab.py delete mode 100644 bin/reportlab/tools/docco/docpy.py delete mode 100644 bin/reportlab/tools/docco/examples.py delete mode 100644 bin/reportlab/tools/docco/graphdocpy.py delete mode 100644 bin/reportlab/tools/docco/rl_doc_utils.py delete mode 100644 bin/reportlab/tools/docco/rltemplate.py delete mode 100644 bin/reportlab/tools/docco/stylesheet.py delete mode 100644 bin/reportlab/tools/docco/t_parse.py delete mode 100644 bin/reportlab/tools/docco/yaml.py delete mode 100644 bin/reportlab/tools/docco/yaml2pdf.py delete mode 100644 bin/reportlab/tools/py2pdf/README delete mode 100644 bin/reportlab/tools/py2pdf/__init__.py delete mode 100644 bin/reportlab/tools/py2pdf/demo-config.txt delete mode 100644 bin/reportlab/tools/py2pdf/demo.py delete mode 100644 bin/reportlab/tools/py2pdf/idle_print.py delete mode 100644 bin/reportlab/tools/py2pdf/py2pdf.py delete mode 100644 bin/reportlab/tools/py2pdf/vertpython.jpg delete mode 100644 bin/reportlab/tools/pythonpoint/README delete mode 100644 bin/reportlab/tools/pythonpoint/__init__.py delete mode 100644 bin/reportlab/tools/pythonpoint/customshapes.py delete mode 100644 bin/reportlab/tools/pythonpoint/demos/LeERC___.AFM delete mode 100644 bin/reportlab/tools/pythonpoint/demos/LeERC___.PFB delete mode 100644 bin/reportlab/tools/pythonpoint/demos/examples.py delete mode 100644 bin/reportlab/tools/pythonpoint/demos/figures.xml delete mode 100644 bin/reportlab/tools/pythonpoint/demos/htu.xml delete mode 100644 bin/reportlab/tools/pythonpoint/demos/leftlogo.a85 delete mode 100644 bin/reportlab/tools/pythonpoint/demos/leftlogo.gif delete mode 100644 bin/reportlab/tools/pythonpoint/demos/lj8100.jpg delete mode 100644 bin/reportlab/tools/pythonpoint/demos/monterey.xml delete mode 100644 bin/reportlab/tools/pythonpoint/demos/outline.gif delete mode 100644 bin/reportlab/tools/pythonpoint/demos/pplogo.gif delete mode 100644 bin/reportlab/tools/pythonpoint/demos/python.gif delete mode 100644 bin/reportlab/tools/pythonpoint/demos/pythonpoint.xml delete mode 100644 bin/reportlab/tools/pythonpoint/demos/slidebox.py delete mode 100644 bin/reportlab/tools/pythonpoint/demos/spectrum.png delete mode 100644 bin/reportlab/tools/pythonpoint/demos/vertpython.gif delete mode 100644 bin/reportlab/tools/pythonpoint/pythonpoint.dtd delete mode 100644 bin/reportlab/tools/pythonpoint/pythonpoint.py delete mode 100644 bin/reportlab/tools/pythonpoint/stdparser.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/__init__.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/horrible.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/htu.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/modern.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/projection.py delete mode 100644 bin/reportlab/tools/pythonpoint/styles/standard.py delete mode 100644 bin/reportlab/tools/utils/add_bleed.py delete mode 100644 bin/reportlab/tools/utils/dumpttf.py diff --git a/bin/addons/base/ir/workflow/pydot/LICENSE b/bin/addons/base/ir/workflow/pydot/LICENSE deleted file mode 100755 index 3291fa10051..00000000000 --- a/bin/addons/base/ir/workflow/pydot/LICENSE +++ /dev/null @@ -1,16 +0,0 @@ -Copyright (c) 2004 Ero Carrera - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/bin/addons/base/ir/workflow/pydot/PKG-INFO b/bin/addons/base/ir/workflow/pydot/PKG-INFO deleted file mode 100755 index 098235a0e70..00000000000 --- a/bin/addons/base/ir/workflow/pydot/PKG-INFO +++ /dev/null @@ -1,33 +0,0 @@ -Metadata-Version: 1.0 -Name: pydot -Version: 0.9.9 -Summary: Python interface to Graphiz's Dot -Home-page: http://dkbza.org/pydot.html -Author: Ero Carrera -Author-email: ero@dkbza.org -License: MIT -Description: Graphviz's dot language Python interface. - - This module provides with a full interface to create handle modify - and process graphs in Graphviz's dot language. - - References: - - pydot Homepage: http://www.dkbza.org/pydot.html - Graphviz: http://www.research.att.com/sw/tools/graphviz/ - DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html - - Programmed and tested with Graphviz 1.12 and Python 2.3.3 on GNU/Linux - by Ero Carrera (c) 2004 [ero@dkbza.org] - - Distributed under MIT license [http://opensource.org/licenses/mit-license.html]. - -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: MIT License -Classifier: Natural Language :: English -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Scientific/Engineering :: Visualization -Classifier: Topic :: Software Development :: Libraries :: Python Modules diff --git a/bin/addons/base/ir/workflow/pydot/README b/bin/addons/base/ir/workflow/pydot/README deleted file mode 100755 index d909fb9a225..00000000000 --- a/bin/addons/base/ir/workflow/pydot/README +++ /dev/null @@ -1,29 +0,0 @@ -pydot - Python interface to Graphviz's Dot language -Ero Carrera (c) 2004 -ero@dkbza.org - -This code is distributed under the MIT license. - -Requirements: -------------- - -pyparsing: pydot requires the pyparsing module in order to be - able to load DOT files. - -GraphViz: is needed in order to render the graphs into any of - the plethora of output formats supported. - -Installation: -------------- - -Should suffice with doing: - - python setup.py install - -Needless to say, no installation is needed just to use the module. A mere: - - import pydot - -should do it, provided that the directory containing the modules is on Python -module search path. - diff --git a/bin/addons/base/ir/workflow/pydot/__init__.py b/bin/addons/base/ir/workflow/pydot/__init__.py deleted file mode 100644 index 6232f794cdd..00000000000 --- a/bin/addons/base/ir/workflow/pydot/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from pydot import * diff --git a/bin/addons/base/ir/workflow/pydot/dot_parser.py b/bin/addons/base/ir/workflow/pydot/dot_parser.py deleted file mode 100644 index 67a763144c7..00000000000 --- a/bin/addons/base/ir/workflow/pydot/dot_parser.py +++ /dev/null @@ -1,352 +0,0 @@ -#!/bin/env python - -""" -The dotparser parses graphviz files in dot and dot files and transforms them -into a class representation defined by pydot. - -The module needs pyparsing (tested with version 1.2) and pydot (tested with 0.9.9) - -Author: Michael Krause -""" - -import sys -import glob -import pydot - -from pyparsing import __version__ as pyparsing_version -from pyparsing import Literal, CaselessLiteral, Word, \ - Upcase, OneOrMore, ZeroOrMore, Forward, NotAny, \ - delimitedList, oneOf, Group, Optional, Combine, \ - alphas, nums, restOfLine, cStyleComment, nums, \ - alphanums, printables, empty, quotedString, \ - ParseException, ParseResults, CharsNotIn, _noncomma - - -class P_AttrList: - def __init__(self, toks): - self.attrs = {} - i = 0 - while i < len(toks): - attrname = toks[i] - attrvalue = toks[i+1] - self.attrs[attrname] = attrvalue - i += 2 - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self.attrs) - - -class DefaultStatement(P_AttrList): - def __init__(self, default_type, attrs): - self.default_type = default_type - self.attrs = attrs - - def __repr__(self): - return "%s(%s, %r)" % \ - (self.__class__.__name__, self.default_type, self.attrs) - - -def push_top_graph_stmt(str, loc, toks): - attrs = {} - g = None - - for element in toks: - if isinstance(element, ParseResults) or \ - isinstance(element, tuple) or \ - isinstance(element, list): - - element = element[0] - - if element == 'strict': - attrs['strict'] = True - elif element in ['graph', 'digraph']: - attrs['graph_type'] = element - elif type(element) == type(''): - attrs['graph_name'] = element - elif isinstance(element, pydot.Graph): - g = pydot.Graph(**attrs) - g.__dict__.update(element.__dict__) - for e in g.get_edge_list(): - e.parent_graph = g - for e in g.get_node_list(): - e.parent_graph = g - for e in g.get_subgraph_list(): - e.set_graph_parent(g) - - elif isinstance(element, P_AttrList): - attrs.update(element.attrs) - else: - raise ValueError, "Unknown element statement: %r " % element - - if g is not None: - g.__dict__.update(attrs) - return g - - -def add_defaults(element, defaults): - d = element.__dict__ - for key, value in defaults.items(): - if not d.get(key): - d[key] = value - - -def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None): - - if defaults_graph is None: - defaults_graph = {} - if defaults_node is None: - defaults_node = {} - if defaults_edge is None: - defaults_edge = {} - - for element in toks: - if isinstance(element, pydot.Graph): - add_defaults(element, defaults_graph) - g.add_subgraph(element) - elif isinstance(element, pydot.Node): - add_defaults(element, defaults_node) - g.add_node(element) - elif isinstance(element, pydot.Edge): - add_defaults(element, defaults_edge) - g.add_edge(element) - elif isinstance(element, ParseResults): - for e in element: - add_elements(g, [e], defaults_graph, defaults_node, defaults_edge) - elif isinstance(element, DefaultStatement): - if element.default_type == 'graph': - default_graph_attrs = pydot.Node('graph') - default_graph_attrs.__dict__.update(element.attrs) - g.add_node(default_graph_attrs) -# defaults_graph.update(element.attrs) -# g.__dict__.update(element.attrs) - elif element.default_type == 'node': - default_node_attrs = pydot.Node('node') - default_node_attrs.__dict__.update(element.attrs) - g.add_node(default_node_attrs) - #defaults_node.update(element.attrs) - elif element.default_type == 'edge': - default_edge_attrs = pydot.Node('edge') - default_edge_attrs.__dict__.update(element.attrs) - g.add_node(default_edge_attrs) - #defaults_edge.update(element.attrs) - else: - raise ValueError, "Unknown DefaultStatement: %s " % element.default_type - elif isinstance(element, P_AttrList): - g.__dict__.update(element.attrs) - else: - raise ValueError, "Unknown element statement: %r " % element - - -def push_graph_stmt(str, loc, toks): - g = pydot.Subgraph() - add_elements(g, toks) - return g - - -def push_subgraph_stmt(str, loc, toks): - for e in toks: - if len(e)==3: - g = e[2] - g.set_name(e[1]) - - return g - - -def push_default_stmt(str, loc, toks): - # The pydot class instances should be marked as - # default statements to be inherited by actual - # graphs, nodes and edges. - # print "push_default_stmt", toks - default_type = toks[0][0] - if len(toks) > 1: - attrs = toks[1].attrs - else: - attrs = {} - - if default_type in ['graph', 'node', 'edge']: - return DefaultStatement(default_type, attrs) - else: - raise ValueError, "Unknown default statement: %r " % toks - - -def push_attr_list(str, loc, toks): - p = P_AttrList(toks) - return p - - -def get_port(node): - - if len(node)>1: - if isinstance(node[1], ParseResults): - if len(node[1][0])==2: - if node[1][0][0]==':': - return node[1][0][1] - - return None - - -def push_edge_stmt(str, loc, toks): - - tok_attrs = [a for a in toks if isinstance(a, P_AttrList)] - attrs = {} - for a in tok_attrs: - attrs.update(a.attrs) - - n_prev = toks[0] - e = [] - for n_next in tuple(toks)[2::2]: - port = get_port(n_prev) - if port is not None: - n_prev_port = ':'+port - else: - n_prev_port = '' - - port = get_port(n_next) - if port is not None: - n_next_port = ':'+port - else: - n_next_port = '' - - e.append(pydot.Edge(n_prev[0]+n_prev_port, n_next[0]+n_next_port, **attrs)) - n_prev = n_next - return e - - -def push_node_stmt(str, loc, toks): - - if len(toks) == 2: - attrs = toks[1].attrs - else: - attrs = {} - - node_name = toks[0] - if isinstance(node_name, list) or isinstance(node_name, tuple): - if len(node_name)>0: - node_name = node_name[0] - - n = pydot.Node(node_name, **attrs) - return n - - -def strip_quotes( s, l, t ): - return [ t[0].strip('"') ] - - -graphparser = None -def GRAPH_DEF(): - global graphparser - - if not graphparser: - # punctuation - colon = Literal(":") - lbrace = Literal("{") - rbrace = Literal("}") - lbrack = Literal("[") - rbrack = Literal("]") - lparen = Literal("(") - rparen = Literal(")") - equals = Literal("=") - comma = Literal(",") - dot = Literal(".") - slash = Literal("/") - bslash = Literal("\\") - star = Literal("*") - semi = Literal(";") - at = Literal("@") - minus = Literal("-") - - # keywords - strict_ = Literal("strict") - graph_ = Literal("graph") - digraph_ = Literal("digraph") - subgraph_ = Literal("subgraph") - node_ = Literal("node") - edge_ = Literal("edge") - - identifier = Word(alphanums + "_" ).setName("identifier") - - double_quote = Literal('"') - double_quoted_string = \ - Combine( double_quote + ZeroOrMore(CharsNotIn('"')) + double_quote ) - - alphastring_ = OneOrMore(CharsNotIn(_noncomma)) - - ID = (identifier | double_quoted_string.setParseAction(strip_quotes) |\ - alphastring_).setName("ID") - - html_text = Combine(Literal("<<") + OneOrMore(CharsNotIn(",]"))) - - float_number = Combine(Optional(minus) + \ - OneOrMore(Word(nums + "."))).setName("float_number") - - righthand_id = (float_number | ID | html_text).setName("righthand_id") - - port_angle = (at + ID).setName("port_angle") - - port_location = (Group(colon + ID) | \ - Group(colon + lparen + ID + comma + ID + rparen)).setName("port_location") - - port = (Group(port_location + Optional(port_angle)) | \ - Group(port_angle + Optional(port_location))).setName("port") - - node_id = (ID + Optional(port)) - a_list = OneOrMore(ID + Optional(equals.suppress() + righthand_id) + \ - Optional(comma.suppress())).setName("a_list") - - attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) + \ - rbrack.suppress()).setName("attr_list") - - attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt") - - edgeop = (Literal("--") | Literal("->")).setName("edgeop") - - stmt_list = Forward() - graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) + \ - rbrace.suppress()).setName("graph_stmt") - - subgraph = (Group(Optional(subgraph_ + Optional(ID)) + graph_stmt) | \ - Group(subgraph_ + ID)).setName("subgraph") - - edgeRHS = OneOrMore(edgeop + Group(node_id | subgraph)) - - edge_stmt = Group(node_id | subgraph) + edgeRHS + Optional(attr_list) - - node_stmt = (node_id + Optional(attr_list) + semi.suppress()).setName("node_stmt") - - assignment = (ID + equals.suppress() + righthand_id).setName("assignment") - stmt = (assignment | edge_stmt | attr_stmt | node_stmt | subgraph).setName("stmt") - stmt_list << OneOrMore(stmt + Optional(semi.suppress())) - - graphparser = (Optional(strict_) + Group((graph_ | digraph_)) + \ - Optional(ID) + graph_stmt).setResultsName("graph") - - singleLineComment = "//" + restOfLine - graphparser.ignore(singleLineComment) - graphparser.ignore(cStyleComment) - - assignment.setParseAction(push_attr_list) - a_list.setParseAction(push_attr_list) - edge_stmt.setParseAction(push_edge_stmt) - node_stmt.setParseAction(push_node_stmt) - attr_stmt.setParseAction(push_default_stmt) - - subgraph.setParseAction(push_subgraph_stmt) - graph_stmt.setParseAction(push_graph_stmt) - graphparser.setParseAction(push_top_graph_stmt) - - return graphparser - - -def parse_dot_data(data): - try: - graphparser = GRAPH_DEF() - if pyparsing_version >= '1.2': - graphparser.parseWithTabs() - tokens = graphparser.parseString(data) - graph = tokens.graph - return graph - except ParseException, err: - print err.line - print " "*(err.column-1) + "^" - print err - return None diff --git a/bin/addons/base/ir/workflow/pydot/pydot.py b/bin/addons/base/ir/workflow/pydot/pydot.py deleted file mode 100644 index e793a0a593b..00000000000 --- a/bin/addons/base/ir/workflow/pydot/pydot.py +++ /dev/null @@ -1,1075 +0,0 @@ -# -*- coding: Latin-1 -*- -"""Graphviz's dot language Python interface. - -This module provides with a full interface to create handle modify -and process graphs in Graphviz's dot language. - -References: - -pydot Homepage: http://www.dkbza.org/pydot.html -Graphviz: http://www.research.att.com/sw/tools/graphviz/ -DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html - -Programmed and tested with Graphviz 1.12 and Python 2.3.3 on GNU/Linux -by Ero Carrera (c) 2004 [ero@dkbza.org] - -Distributed under MIT license [http://opensource.org/licenses/mit-license.html]. -""" - -__author__ = 'Ero Carrera' -__version__ = '0.9.9' -__license__ = 'MIT' - -import os -import tempfile -import copy -import dot_parser - - -def graph_from_dot_data(data): - """Load graph as defined by data in DOT format. - - The data is assumed to be in DOT format. It will - be parsed and a Dot class will be returned, - representing the graph. - """ - - graph = dot_parser.parse_dot_data(data) - if graph is not None: - dot = Dot() - dot.__dict__.update(graph.__dict__) - return dot - - return None - -def graph_from_dot_file(path): - """Load graph as defined by a DOT file. - - The file is assumed to be in DOT format. It will - be loaded, parsed and a Dot class will be returned, - representing the graph. - """ - - fd = file(path, 'rb') - data = fd.read() - fd.close() - - return graph_from_dot_data(data) - - -def graph_from_edges(edge_list, node_prefix='', directed=False): - """Creates a basic graph out of an edge list. - - The edge list has to be a list of tuples representing - the nodes connected by the edge. - The values can be anything: bool, int, float, str. - - If the graph is undirected by default, it is only - calculated from one of the symmetric halves of the matrix. - """ - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for edge in edge_list: - e = Edge(node_prefix+str(edge[0]), node_prefix+str(edge[1])) - graph.add_edge(e) - return graph - - -def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False): - """Creates a basic graph out of an adjacency matrix. - - The matrix has to be a list of rows of values - representing an adjacency matrix. - The values can be anything: bool, int, float, as long - as they can evaluate to True or False. - """ - node_orig = 1 - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for row in matrix: - if not directed: - skip = matrix.index(row) - r = row[skip:] - else: - skip = 0 - r = row - node_dest = skip+1 - for e in r: - if e: - graph.add_edge( \ - Edge( node_prefix+str(node_orig), \ - node_prefix+str(node_dest))) - node_dest += 1 - node_orig += 1 - return graph - -def graph_from_incidence_matrix(matrix, node_prefix='', directed=False): - """Creates a basic graph out of an incidence matrix. - - The matrix has to be a list of rows of values - representing an incidence matrix. - The values can be anything: bool, int, float, as long - as they can evaluate to True or False. - """ - node_orig = 1 - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for row in matrix: - nodes = [] - c = 1 - for node in row: - if node: - nodes.append(c*node) - c += 1 - nodes.sort() - if len(nodes) == 2: - graph.add_edge( \ - Edge(node_prefix+str(abs(nodes[0])), \ - node_prefix+str(nodes[1]) )) - if not directed: - graph.set_simplify(True) - return graph - - -def find_graphviz(): - """Locate Graphviz's executables in the system. - - Attempts to locate graphviz's executables in a Unix system. - It will look for 'dot', 'twopi' and 'neato' in all the directories - specified in the PATH environment variable. - It will return a dictionary containing the program names as keys - and their paths as values. - """ - progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''} - if not os.environ.has_key('PATH'): - return None - for path in os.environ['PATH'].split(os.pathsep): - for prg in progs.keys(): - if os.path.exists(path+os.path.sep+prg): - progs[prg] = path+os.path.sep+prg - elif os.path.exists(path+os.path.sep+prg + '.exe'): - progs[prg] = path+os.path.sep+prg + '.exe' - return progs - -class Common: - """Common information to several classes. - - Should not be directly used, several classes are derived from - this one. - """ - chars_ID = None - parent_graph = None - - def char_range(self, a,b): - """Generate a list containing a range of characters. - - Returns a list of characters starting from 'a' up to 'b' - both inclusive. - """ - return map(chr, range(ord(a), ord(b)+1)) - - def is_ID(self, s): - """Checks whether a string is an dot language ID. - - It will check whether the string is solely composed - by the characters allowed in an ID or not. - """ - if not self.chars_ID: - self.chars_ID = self.char_range('a','z')+self.char_range('A','Z')+ \ - self.char_range('0','9')+['_'] - for c in s: - if c not in self.chars_ID: - return False - return True - -class Error(Exception): - """General error handling class. - """ - def __init__(self, value): - self.value = value - def __str__(self): - return self.value - - -class Node(object, Common): - """A graph node. - - This class represents a graph's node with all its attributes. - - node(name, attribute=value, ...) - - name: node's name - - All the attributes defined in the Graphviz dot language should - be supported. - """ - attributes = ['showboxes', 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', \ - 'comment', 'root', 'toplabel', 'vertices', 'width', 'z', 'bottomlabel', \ - 'distortion', 'fixedsize', 'group', 'height', 'orientation', 'pin', \ - 'rects', 'regular', 'shape', 'shapefile', 'sides', 'skew', 'pos', \ - 'layer', 'tooltip', 'style', 'target', 'color', 'peripheries', - 'fillcolor'] - - def __init__(self, name, **attrs): - - if isinstance(name, str) and not name.startswith('"'): - idx = name.find(':') - if idx>0: - name = name[:idx] - - self.name = str(name) - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def set_name(self, node_name): - """Set the node's name.""" - - self.name = str(node_name) - - def get_name(self): - """Get the node's name.""" - - return self.name - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - set_'name'(value) - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - def to_string(self): - """Returns a string representation of the node in dot language. - """ - - if not isinstance(self.name, str): - self.name = str(self.name) - - # RMF: special case defaults for node, edge and graph properties. - if self.name in ['node', 'edge', 'graph']: - node = self.name - else: - node = '"'+self.name+'"' - - node_attr = None - all_attrs = [a for a in self.attributes] - all_attrs += [a for a in Graph.attributes if a not in all_attrs] - all_attrs += [a for a in Edge.attributes if a not in all_attrs] - for attr in all_attrs: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - if not node_attr: - node_attr = '' - else: - node_attr += ', ' - node_attr += attr+'=' - val = self.__dict__[attr] - - if val.startswith('<') and val.endswith('>'): - node_attr += val - elif (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): - node_attr += '"'+val+'"' - else: - node_attr += str(val) - - if node_attr: - node += ' ['+node_attr+']' - node += ';' - - return node - - -class Edge(object, Common): - """A graph edge. - - This class represents a graph's edge with all its attributes. - - edge(src, dst, attribute=value, ...) - - src: source node's name - dst: destination node's name - - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_label, set_fontname - - or using the instance's attributes: - - Edge.[attribute name], i.e. edge_instance.label, edge_instance.fontname - """ - attributes = ['style', 'target', 'pos', 'layer', 'tooltip', 'color', 'showboxes',\ - 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', 'comment', 'lp', \ - 'arrowhead', 'arrowsize', 'arrowtail', 'constraint', 'decorate', 'dir', \ - 'headURL', 'headclip', 'headhref', 'headlabel', 'headport', \ - 'headtarget', 'headtooltip', 'href', 'labelangle', 'labeldistance', \ - 'labelfloat', 'labelfontcolor', 'labelfontname', 'labelfontsize', 'len',\ - 'lhead', 'ltail', 'minlen', 'samehead', 'sametail', 'weight', 'tailURL',\ - 'tailclip', 'tailhref', 'taillabel', 'tailport', 'tailtarget', \ - 'tailtooltip'] - - def __init__(self, src, dst, **attrs): - self.src = src - self.dst = dst - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def get_source(self): - """Get the edges source node name.""" - - return self.src - - def get_destination(self): - """Get the edge's destination node name.""" - - return self.dst - - def __eq__(self, edge): - """Compare two edges. - - If the parent graph is directed, arcs linking - node A to B are considered equal and A->B != B->A - - If the parent graph is undirected, any edge - connecting two nodes is equal to any other - edge connecting the same nodes, A->B == B->A - """ - - if not isinstance(edge, Edge): - raise Error, 'Can\'t compare and edge to a non-edge object.' - if self.parent_graph.graph_type=='graph': - # If the graph is undirected, the edge has neither - # source nor destination. - if (self.src==edge.src and self.dst==edge.dst) or \ - (self.src==edge.dst and self.dst==edge.src): - return True - else: - if self.src==edge.src and self.dst==edge.dst: - return True - return False - - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - set_'name'(value) - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - - def parse_node_ref(self, node_str): - - if not isinstance(node_str, str): - node_str = str(node_str) - - if node_str[0]=='"' and node_str[-1]=='"' and node_str[0].count('"')%2!=0: - return node_str - - node_port_idx = node_str.rfind(':') - - if node_port_idx>0 and node_str[0]=='"' and node_str[node_port_idx-1]=='"': - return node_str - - node_str = node_str.replace('"', '') - - if node_port_idx>0: - a = node_str[:node_port_idx] - b = node_str[node_port_idx+1:] - if self.is_ID(a): - node = a - else: - node = '"'+a+'"' - if self.is_ID(b): - node += ':'+b - else: - node+=':"'+b+'"' - return node - - return '"'+node_str+'"' - - - def to_string(self): - """Returns a string representation of the edge in dot language. - """ - - src = self.parse_node_ref(self.src) - dst = self.parse_node_ref(self.dst) - - edge = src - if self.parent_graph and \ - self.parent_graph.graph_type and \ - self.parent_graph.graph_type=='digraph': - edge+=' -> ' - else: - edge+=' -- ' - edge+=dst - - edge_attr = None - for attr in self.attributes: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - if not edge_attr: - edge_attr = '' - else: - edge_attr+=', ' - edge_attr+=attr+'=' - val = self.__dict__[attr] - if (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): - edge_attr+='"'+val+'"' - else: - edge_attr+=str(val) - - if edge_attr: - edge+=' ['+edge_attr+']' - edge+=';' - - return edge - -class Graph(object, Common): - - """Class representing a graph in Graphviz's dot language. - - This class implements the methods to work on a representation - of a graph in Graphviz's dot language. - - graph(graph_name='G', type='digraph', strict=False, suppress_disconnected=False, attribute=value, ...) - - graph_name: - the graph's name - type: - can be 'graph' or 'digraph' - suppress_disconnected: - defaults to False, which will remove from the - graph any disconnected nodes. - simplify: - if True it will avoid displaying equal edges, i.e. - only one edge between two nodes. removing the - duplicated ones. - - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_size, set_fontname - - or using the instance's attributes: - - Graph.[attribute name], i.e. graph_instance.label, graph_instance.fontname - """ - - attributes = ['Damping', 'bb', 'center', 'clusterrank', 'compound', 'concentrate',\ - 'defaultdist', 'dim', 'fontpath', 'epsilon', 'layers', 'layersep', \ - 'margin', 'maxiter', 'mclimit', 'mindist', 'pack', 'packmode', 'model', \ - 'page', 'pagedir', 'nodesep', 'normalize', 'nslimit1', 'ordering', \ - 'orientation', 'outputorder', 'overlap', 'remincross', 'resolution', \ - 'rankdir', 'ranksep', 'ratio', 'rotate', 'samplepoints', 'searchsize', \ - 'sep', 'size', 'splines', 'start', 'stylesheet', 'truecolor', \ - 'viewport', 'voro_margin', 'quantum', 'bgcolor', 'labeljust', \ - 'labelloc', 'root', 'showboxes', 'URL', 'fontcolor', 'fontsize', \ - 'label' ,'fontname', 'comment', 'lp', 'target', 'color', 'style', \ - 'concentrators'] - - def __init__(self, graph_name='G', graph_type='digraph', strict=False, \ - suppress_disconnected=False, simplify=False, **attrs): - - if graph_type not in ['graph', 'digraph']: - raise Error, 'Invalid type. Accepted graph types are: graph, digraph, subgraph' - self.graph_type = graph_type - self.graph_name = graph_name - self.strict = strict - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - self.parent_graph = self - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def set_simplify(self, simplify): - """Set whether to simplify or not. - - If True it will avoid displaying equal edges, i.e. - only one edge between two nodes. removing the - duplicated ones. - """ - - self.simplify = simplify - - def get_simplify(self): - """Get whether to simplify or not. - - Refer to set_simplify for more information. - """ - - return self.simplify - - - def set_type(self, graph_type): - """Set the graph's type, 'graph' or 'digraph'.""" - self.graph_type = graph_type - - def get_type(self): - """Get the graph's type, 'graph' or 'digraph'.""" - return self.graph_type - - def set_name(self, graph_name): - """Set the graph's name.""" - - self.graph_name = graph_name - - def get_name(self): - """Get the graph's name.""" - - return self.graph_name - - def set_strict(self, val): - """Set graph to 'strict' mode. - - This option is only valid for top level graphs. - """ - - self.strict = val - - def get_strict(self, val): - """Get graph's 'strict' mode (True, False). - - This option is only valid for top level graphs. - """ - - return self.strict - - def set_suppress_disconnected(self, val): - """Suppress disconnected nodes in the output graph. - - This option will skip nodes in the graph with no incoming or outgoing - edges. This option works also for subgraphs and has effect only in the - current graph/subgraph. - """ - - self.suppress_disconnected = val - - def get_suppress_disconnected(self, val): - """Get if suppress disconnected is set. - - Refer to set_suppress_disconnected for more information. - """ - - self.suppress_disconnected = val - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - - set_'name'(value) - - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - def get(self, name): - """Get an attribute value by name. - - Given an attribute 'name' it will get its value. - There's always the possibility of using the methods: - - get_'name'() - - which are defined for all the existing attributes. - """ - return self.__dict__[name] - - def add_node(self, graph_node): - """Adds a node object to the graph. - - It takes a node object as its only argument and returns - None. - """ - - if not isinstance(graph_node, Node): - raise Error, 'add_node received a non node class object' - - if self.get_node(graph_node.get_name()) is None: - self.node_list.append(graph_node) - graph_node.parent_graph = self.parent_graph - - def get_node(self, name): - """Retrieved a node from the graph. - - Given a node's name the corresponding Node - instance will be returned. - - If multiple nodes exist with that name, a list of - Node instances is returned. - If only one node exists, the instance is returned. - None is returned otherwise. - """ - - match = [node for node in self.node_list if node.get_name() == str(name)] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_node_list(self): - """Get the list of Node instances. - - This method returns the list of Node instances - composing the graph. - """ - - return self.node_list - - def add_edge(self, graph_edge): - """Adds an edge object to the graph. - - It takes a edge object as its only argument and returns - None. - """ - - if not isinstance(graph_edge, Edge): - raise Error, 'add_edge received a non edge class object' - - self.edge_list.append(graph_edge) - - src = self.get_node(graph_edge.get_source()) - if src is None: - self.add_node(Node(graph_edge.get_source())) - - dst = self.get_node(graph_edge.get_destination()) - if dst is None: - self.add_node(Node(graph_edge.get_destination())) - - graph_edge.parent_graph = self.parent_graph - - if graph_edge.src not in self.edge_src_list: - self.edge_src_list.append(graph_edge.src) - - if graph_edge.dst not in self.edge_dst_list: - self.edge_dst_list.append(graph_edge.dst) - - def get_edge(self, src, dst): - """Retrieved an edge from the graph. - - Given an edge's source and destination the corresponding - Edge instance will be returned. - - If multiple edges exist with that source and destination, - a list of Edge instances is returned. - If only one edge exists, the instance is returned. - None is returned otherwise. - """ - - match = [edge for edge in self.edge_list if edge.src == src and edge.dst == dst] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_edge_list(self): - """Get the list of Edge instances. - - This method returns the list of Edge instances - composing the graph. - """ - - return self.edge_list - - def add_subgraph(self, sgraph): - """Adds an edge object to the graph. - - It takes a subgraph object as its only argument and returns - None. - """ - if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster): - raise Error, 'add_subgraph received a non subgraph class object' - - self.subgraph_list.append(sgraph) - - sgraph.set_graph_parent(self.parent_graph) - return None - - def get_subgraph(self, name): - """Retrieved a subgraph from the graph. - - Given a subgraph's name the corresponding - Subgraph instance will be returned. - - If multiple subgraphs exist with the same name, a list of - Subgraph instances is returned. - If only one Subgraph exists, the instance is returned. - None is returned otherwise. - """ - - match = [sgraph for sgraph in self.subgraph_list if sgraph.graph_name == name] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_subgraph_list(self): - """Get the list of Subgraph instances. - - This method returns the list of Subgraph instances - in the graph. - """ - - return self.subgraph_list - - def set_graph_parent(self, parent): - """Sets a graph and its elements to point the the parent. - - Any subgraph added to a parent graph receives a reference - to the parent to access some common data. - """ - self.parent_graph = parent - - for elm in self.edge_list: - elm.parent_graph = parent - - for elm in self.node_list: - elm.parent_graph = parent - - for elm in self.subgraph_list: - elm.parent_graph = parent - elm.set_graph_parent(parent) - - def to_string(self, indent=''): - """Returns a string representation of the graph in dot language. - - It will return the graph and all its subelements in string from. - """ - graph = indent+'' - if self.__dict__.has_key('strict'): - if self==self.parent_graph and self.strict: - graph+='strict ' - - graph+=self.graph_type+' '+self.graph_name+' {\n' - - for attr in self.attributes: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - graph += indent+'\t'+attr+'=' - val = self.__dict__[attr] - if isinstance(val, str) and not self.is_ID(val): - graph += '"'+val+'"' - else: - graph += str(val) - graph+=';\n' - - # RMF: for graphviz to handle edge/node defaults correctly, - # it seems they must appear before the rest of the nodes/edges - # so if they exist, dump them out first. - for elm in self.node_list: - if elm.name in ['node', 'edge', 'graph']: - graph += indent+'\t'+elm.to_string()+'\n' - - for elm in self.subgraph_list: - graph += elm.to_string(indent+'\t')+'\n' - - for elm in self.node_list: - # RMF: don't repeat the node/edge defaults - if elm.name in ['node', 'edge', 'graph']: - continue - if self.suppress_disconnected: - if elm.name not in self.edge_src_list+self.edge_dst_list: - continue - graph+=indent+'\t'+elm.to_string()+'\n' - - edges_done = [] - for elm in self.edge_list: - if self.simplify and elm in edges_done: - continue - graph+=indent+'\t'+elm.to_string()+'\n' - edges_done.append(elm) - - graph+=indent+'}\n' - - return graph - - -class Subgraph(Graph): - - """Class representing a subgraph in Graphviz's dot language. - - This class implements the methods to work on a representation - of a subgraph in Graphviz's dot language. - - subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...) - - graph_name: - the subgraph's name - suppress_disconnected: - defaults to false, which will remove from the - subgraph any disconnected nodes. - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_size, set_fontname - - or using the instance's attributes: - - Subgraph.[attribute name], i.e. - subgraph_instance.label, subgraph_instance.fontname - """ - - attributes = Graph.attributes+['rank'] - - # RMF: subgraph should have all the attributes of graph so it can be passed - # as a graph to all methods - def __init__(self, graph_name='subG', suppress_disconnected=False, \ - simplify=False, **attrs): - - self.graph_type = 'subgraph' - self.graph_name = graph_name - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - -class Cluster(Graph): - - """Class representing a cluster in Graphviz's dot language. - - This class implements the methods to work on a representation - of a cluster in Graphviz's dot language. - - cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...) - - graph_name: - the cluster's name (the string 'cluster' will be always prepended) - suppress_disconnected: - defaults to false, which will remove from the - cluster any disconnected nodes. - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_color, set_fontname - - or using the instance's attributes: - - Cluster.[attribute name], i.e. - cluster_instance.color, cluster_instance.fontname - """ - - attributes = ['pencolor', 'bgcolor', 'labeljust', 'labelloc', 'URL', 'fontcolor', \ - 'fontsize', 'label', 'fontname', 'lp', 'style', 'target', 'color', \ - 'peripheries', 'fillcolor'] - - def __init__(self, graph_name='subG', suppress_disconnected=False, \ - simplify=False, **attrs): - - #if type not in ['subgraph']: - # raise Error, 'Invalid type. Accepted graph types are: subgraph' - self.graph_type = 'subgraph' - self.graph_name = 'cluster_'+graph_name - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - -class Dot(Graph): - """A container for handling a dot language file. - - This class implements methods to write and process - a dot language file. It is a derived class of - the base class 'Graph'. - """ - - progs = None - - formats = ['ps', 'ps2', 'hpgl', 'pcl', 'mif', 'pic', 'gd', 'gd2', 'gif', 'jpg', \ - 'jpeg', 'png', 'wbmp', 'ismap', 'imap', 'cmap', 'vrml', 'vtx', 'mp', \ - 'fig', 'svg', 'svgz', 'dia', 'dot', 'canon', 'plain', 'plain-ext', 'xdot'] - - def __init__(self, prog='dot', **args): - Graph.__init__(self, **args) - - self.prog = prog - - # Automatically creates all the methods enabling the creation - # of output in any of the supported formats. - for frmt in self.formats: - self.__setattr__('create_'+frmt, lambda f=frmt, prog=self.prog : self.create(format=f, prog=prog)) - f = self.__dict__['create_'+frmt] - f.__doc__ = '''Refer to docstring from 'create' for more information.''' - - for frmt in self.formats+['raw']: - self.__setattr__('write_'+frmt, lambda path, f=frmt, prog=self.prog : self.write(path, format=f, prog=prog)) - f = self.__dict__['write_'+frmt] - f.__doc__ = '''Refer to docstring from 'write' for more information.''' - - def set_prog(self, prog): - """Sets the default program. - - Sets the default program in charge of processing - the dot file into a graph. - """ - self.prog = prog - - def write(self, path, prog=None, format='raw'): - """Writes a graph to a file. - - Given a filename 'path' it will open/create and truncate - such file and write on it a representation of the graph - defined by the dot object and in the format specified by - 'format'. - The format 'raw' is used to dump the string representation - of the Dot object, without further processing. - The output can be processed by any of graphviz tools, defined - in 'prog', which defaults to 'dot' - Returns True or False according to the success of the write - operation. - - There's also the preferred possibility of using: - - write_'format'(path, prog='program') - - which are automatically defined for all the supported formats. - [write_ps(), write_gif(), write_dia(), ...] - """ - - if prog is None: - prog = self.prog - - dot_fd = file(path, "w+b") - if format == 'raw': - dot_fd.write(self.to_string()) - else: - dot_fd.write(self.create(prog, format)) - dot_fd.close() - - return True - - def create(self, prog=None, format='ps'): - """Creates and returns a Postscript representation of the graph. - - create will write the graph to a temporary dot file and process - it with the program given by 'prog' (which defaults to 'twopi'), - reading the Postscript output and returning it as a string is the - operation is successful. - On failure None is returned. - - There's also the preferred possibility of using: - - create_'format'(prog='program') - - which are automatically defined for all the supported formats. - [create_ps(), create_gif(), create_dia(), ...] - """ - if prog is None: - prog = self.prog - - if self.progs is None: - self.progs = find_graphviz() - if self.progs is None: - return None - if not self.progs.has_key(prog): - # Program not found ?!?! - return None - - tmp_fd, tmp_name = tempfile.mkstemp() - os.close(tmp_fd) - self.write(tmp_name) - stdin, stdout, stderr = os.popen3(self.progs[prog]+' -T'+format+' '+tmp_name, 'b') - stdin.close() - stderr.close() - data = stdout.read() - stdout.close() - os.unlink(tmp_name) - return data - diff --git a/bin/addons/base/ir/workflow/pydot/setup.py b/bin/addons/base/ir/workflow/pydot/setup.py deleted file mode 100644 index 5209f242d81..00000000000 --- a/bin/addons/base/ir/workflow/pydot/setup.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -from distutils.core import setup -import pydot - -setup( name = 'pydot', - version = pydot.__version__, - description = 'Python interface to Graphiz\'s Dot', - author = 'Ero Carrera', - author_email = 'ero@dkbza.org', - url = 'http://dkbza.org/pydot.html', - license = 'MIT', - platforms = ["any"], - classifiers = ['Development Status :: 5 - Production/Stable', \ - 'Intended Audience :: Science/Research', \ - 'License :: OSI Approved :: MIT License',\ - 'Natural Language :: English', \ - 'Operating System :: OS Independent', \ - 'Programming Language :: Python', \ - 'Topic :: Scientific/Engineering :: Visualization',\ - 'Topic :: Software Development :: Libraries :: Python Modules'], - long_description = "\n".join(pydot.__doc__.split('\n')), - py_modules = ['pydot', 'dot_parser']) diff --git a/bin/pychart/__init__.py b/bin/pychart/__init__.py deleted file mode 100644 index 37b0fccf5dc..00000000000 --- a/bin/pychart/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -__all__ = [ "axis", "area", "basecanvas", "canvas", - "line_plot", "pie_plot", "rose_plot", "tick_mark", - "bar_plot", "chart_data", "arrow", "text_box", "color", "font", - "fill_style", "error_bar", "range_plot", "chart_object", - "line_style", "legend", "pychart_util", "theme", "scaling", - "zap", "coord", "linear_coord", "log_coord", - "category_coord", "afm", "interval_bar_plot" ] - diff --git a/bin/pychart/afm/AvantGarde_Book.py b/bin/pychart/afm/AvantGarde_Book.py deleted file mode 100644 index 62afd5ae7a2..00000000000 --- a/bin/pychart/afm/AvantGarde_Book.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font AvantGarde-Book (path: /usr/share/fonts/afms/adobe/pagk8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["AvantGarde-Book"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 277, 295, 309, 554, 554, 775, 757, 351, 369, 369, 425, 606, 277, 332, 277, 437, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 277, 277, 606, 606, 606, 591, 867, 740, 574, 813, 744, 536, 485, 872, 683, 226, 482, 591, 462, 919, 740, 869, 592, 871, 607, 498, 426, 655, 702, 960, 609, 592, 480, 351, 605, 351, 606, 500, 351, 683, 682, 647, 685, 650, 314, 673, 610, 200, 203, 502, 200, 938, 610, 655, 682, 682, 301, 388, 339, 608, 554, 831, 480, 536, 425, 351, 672, 351, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 295, 554, 554, 166, 554, 554, 615, 554, 198, 502, 425, 251, 251, 487, 485, 500, 500, 553, 553, 277, 500, 564, 606, 354, 502, 484, 425, 1000, 1174, 500, 591, 500, 378, 375, 502, 439, 485, 453, 222, 369, 500, 332, 324, 500, 552, 302, 502, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 992, 500, 369, 500, 500, 500, 500, 517, 868, 1194, 369, 500, 500, 500, 500, 500, 1157, 500, 500, 500, 200, 500, 500, 300, 653, 1137, 554, ) diff --git a/bin/pychart/afm/AvantGarde_BookOblique.py b/bin/pychart/afm/AvantGarde_BookOblique.py deleted file mode 100644 index 11c29aa416a..00000000000 --- a/bin/pychart/afm/AvantGarde_BookOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font AvantGarde-BookOblique (path: /usr/share/fonts/afms/adobe/pagko8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["AvantGarde-BookOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 277, 295, 309, 554, 554, 775, 757, 351, 369, 369, 425, 606, 277, 332, 277, 437, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 277, 277, 606, 606, 606, 591, 867, 740, 574, 813, 744, 536, 485, 872, 683, 226, 482, 591, 462, 919, 740, 869, 592, 871, 607, 498, 426, 655, 702, 960, 609, 592, 480, 351, 605, 351, 606, 500, 351, 683, 682, 647, 685, 650, 314, 673, 610, 200, 203, 502, 200, 938, 610, 655, 682, 682, 301, 388, 339, 608, 554, 831, 480, 536, 425, 351, 672, 351, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 295, 554, 554, 166, 554, 554, 615, 554, 198, 502, 425, 251, 251, 487, 485, 500, 500, 553, 553, 277, 500, 564, 606, 354, 502, 484, 425, 1000, 1174, 500, 591, 500, 378, 375, 502, 439, 485, 453, 222, 369, 500, 332, 324, 500, 552, 302, 502, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 992, 500, 369, 500, 500, 500, 500, 517, 868, 1194, 369, 500, 500, 500, 500, 500, 1157, 500, 500, 500, 200, 500, 500, 300, 653, 1137, 554, ) diff --git a/bin/pychart/afm/AvantGarde_Demi.py b/bin/pychart/afm/AvantGarde_Demi.py deleted file mode 100644 index ee69d2fbd74..00000000000 --- a/bin/pychart/afm/AvantGarde_Demi.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font AvantGarde-Demi (path: /usr/share/fonts/afms/adobe/pagd8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["AvantGarde-Demi"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 280, 280, 360, 560, 560, 860, 680, 280, 380, 380, 440, 600, 280, 420, 280, 460, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 280, 280, 600, 600, 600, 560, 740, 740, 580, 780, 700, 520, 480, 840, 680, 280, 480, 620, 440, 900, 740, 840, 560, 840, 580, 520, 420, 640, 700, 900, 680, 620, 500, 320, 640, 320, 600, 500, 280, 660, 660, 640, 660, 640, 280, 660, 600, 240, 260, 580, 240, 940, 600, 640, 660, 660, 320, 440, 300, 600, 560, 800, 560, 580, 460, 340, 600, 340, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 280, 560, 560, 160, 560, 560, 560, 560, 220, 480, 460, 240, 240, 520, 520, 500, 500, 560, 560, 280, 500, 600, 600, 280, 480, 480, 460, 1000, 1280, 500, 560, 500, 420, 420, 540, 480, 420, 480, 280, 500, 500, 360, 340, 500, 700, 340, 540, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 900, 500, 360, 500, 500, 500, 500, 480, 840, 1060, 360, 500, 500, 500, 500, 500, 1080, 500, 500, 500, 240, 500, 500, 320, 660, 1080, 600, ) diff --git a/bin/pychart/afm/AvantGarde_DemiOblique.py b/bin/pychart/afm/AvantGarde_DemiOblique.py deleted file mode 100644 index 09a08c53447..00000000000 --- a/bin/pychart/afm/AvantGarde_DemiOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font AvantGarde-DemiOblique (path: /usr/share/fonts/afms/adobe/pagdo8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["AvantGarde-DemiOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 280, 280, 360, 560, 560, 860, 680, 280, 380, 380, 440, 600, 280, 420, 280, 460, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 280, 280, 600, 600, 600, 560, 740, 740, 580, 780, 700, 520, 480, 840, 680, 280, 480, 620, 440, 900, 740, 840, 560, 840, 580, 520, 420, 640, 700, 900, 680, 620, 500, 320, 640, 320, 600, 500, 280, 660, 660, 640, 660, 640, 280, 660, 600, 240, 260, 580, 240, 940, 600, 640, 660, 660, 320, 440, 300, 600, 560, 800, 560, 580, 460, 340, 600, 340, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 280, 560, 560, 160, 560, 560, 560, 560, 220, 480, 460, 240, 240, 520, 520, 500, 500, 560, 560, 280, 500, 600, 600, 280, 480, 480, 460, 1000, 1280, 500, 560, 500, 420, 420, 540, 480, 420, 480, 280, 500, 500, 360, 340, 500, 700, 340, 540, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 900, 500, 360, 500, 500, 500, 500, 480, 840, 1060, 360, 500, 500, 500, 500, 500, 1080, 500, 500, 500, 240, 500, 500, 320, 660, 1080, 600, ) diff --git a/bin/pychart/afm/Bookman_Demi.py b/bin/pychart/afm/Bookman_Demi.py deleted file mode 100644 index 199315d3a8a..00000000000 --- a/bin/pychart/afm/Bookman_Demi.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Bookman-Demi (path: /usr/share/fonts/afms/adobe/pbkd8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Bookman-Demi"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 340, 360, 420, 660, 660, 940, 800, 320, 320, 320, 460, 600, 340, 360, 340, 600, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 340, 340, 600, 600, 600, 660, 820, 720, 720, 740, 780, 720, 680, 780, 820, 400, 640, 800, 640, 940, 740, 800, 660, 800, 780, 660, 700, 740, 720, 940, 780, 700, 640, 300, 600, 300, 600, 500, 320, 580, 600, 580, 640, 580, 380, 580, 680, 360, 340, 660, 340, 1000, 680, 620, 640, 620, 460, 520, 460, 660, 600, 800, 600, 620, 560, 320, 600, 320, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 360, 660, 660, 120, 660, 660, 600, 660, 240, 540, 400, 220, 220, 740, 740, 500, 500, 440, 380, 340, 500, 800, 460, 320, 540, 540, 400, 1000, 1360, 500, 660, 500, 400, 400, 500, 480, 460, 500, 320, 500, 500, 340, 360, 500, 440, 320, 500, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1140, 500, 400, 500, 500, 500, 500, 640, 800, 1220, 400, 500, 500, 500, 500, 500, 880, 500, 500, 500, 360, 500, 500, 340, 620, 940, 660, ) diff --git a/bin/pychart/afm/Bookman_DemiItalic.py b/bin/pychart/afm/Bookman_DemiItalic.py deleted file mode 100644 index 9e13c8f6516..00000000000 --- a/bin/pychart/afm/Bookman_DemiItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Bookman-DemiItalic (path: /usr/share/fonts/afms/adobe/pbkdi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Bookman-DemiItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 340, 320, 380, 680, 680, 880, 980, 320, 260, 260, 460, 600, 340, 280, 340, 360, 680, 680, 680, 680, 680, 680, 680, 680, 680, 680, 340, 340, 620, 600, 620, 620, 780, 720, 720, 700, 760, 720, 660, 760, 800, 380, 620, 780, 640, 860, 740, 760, 640, 760, 740, 700, 700, 740, 660, 1000, 740, 660, 680, 260, 580, 260, 620, 500, 320, 680, 600, 560, 680, 560, 420, 620, 700, 380, 320, 700, 380, 960, 680, 600, 660, 620, 500, 540, 440, 680, 540, 860, 620, 600, 560, 300, 620, 300, 620, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 320, 680, 680, 120, 680, 680, 620, 680, 180, 520, 380, 220, 220, 820, 820, 500, 500, 420, 420, 340, 500, 680, 360, 300, 520, 520, 380, 1000, 1360, 500, 620, 500, 380, 340, 480, 480, 480, 460, 380, 520, 500, 360, 360, 500, 560, 320, 480, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1140, 500, 440, 500, 500, 500, 500, 640, 760, 1180, 440, 500, 500, 500, 500, 500, 880, 500, 500, 500, 380, 500, 500, 380, 600, 920, 660, ) diff --git a/bin/pychart/afm/Bookman_Light.py b/bin/pychart/afm/Bookman_Light.py deleted file mode 100644 index 031f86a6206..00000000000 --- a/bin/pychart/afm/Bookman_Light.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Bookman-Light (path: /usr/share/fonts/afms/adobe/pbkl8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Bookman-Light"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 320, 300, 380, 620, 620, 900, 800, 220, 300, 300, 440, 600, 320, 400, 320, 600, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 320, 320, 600, 600, 600, 540, 820, 680, 740, 740, 800, 720, 640, 800, 800, 340, 600, 720, 600, 920, 740, 800, 620, 820, 720, 660, 620, 780, 700, 960, 720, 640, 640, 300, 600, 300, 600, 500, 220, 580, 620, 520, 620, 520, 320, 540, 660, 300, 300, 620, 300, 940, 660, 560, 620, 580, 440, 520, 380, 680, 520, 780, 560, 540, 480, 280, 600, 280, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 300, 620, 620, 140, 620, 620, 520, 620, 220, 400, 360, 240, 240, 620, 620, 500, 500, 540, 540, 320, 500, 600, 460, 220, 400, 400, 360, 1000, 1280, 500, 540, 500, 340, 340, 420, 440, 440, 460, 260, 420, 500, 320, 320, 500, 380, 320, 420, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1260, 500, 420, 500, 500, 500, 500, 600, 800, 1240, 420, 500, 500, 500, 500, 500, 860, 500, 500, 500, 300, 500, 500, 320, 560, 900, 660, ) diff --git a/bin/pychart/afm/Bookman_LightItalic.py b/bin/pychart/afm/Bookman_LightItalic.py deleted file mode 100644 index 8a3d8d26fe8..00000000000 --- a/bin/pychart/afm/Bookman_LightItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Bookman-LightItalic (path: /usr/share/fonts/afms/adobe/pbkli8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Bookman-LightItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 300, 320, 360, 620, 620, 800, 820, 280, 280, 280, 440, 600, 300, 320, 300, 600, 620, 620, 620, 620, 620, 620, 620, 620, 620, 620, 300, 300, 600, 600, 600, 540, 780, 700, 720, 720, 740, 680, 620, 760, 800, 320, 560, 720, 580, 860, 720, 760, 600, 780, 700, 640, 600, 720, 680, 960, 700, 660, 580, 260, 600, 260, 600, 500, 280, 620, 600, 480, 640, 540, 340, 560, 620, 280, 280, 600, 280, 880, 620, 540, 600, 560, 400, 540, 340, 620, 540, 880, 540, 600, 520, 360, 600, 380, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 320, 620, 620, 20, 620, 620, 620, 620, 200, 440, 300, 180, 180, 640, 660, 500, 500, 620, 620, 300, 500, 620, 460, 320, 480, 440, 300, 1000, 1180, 500, 540, 500, 340, 320, 440, 440, 440, 440, 260, 420, 500, 300, 320, 500, 340, 260, 440, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1220, 500, 440, 500, 500, 500, 500, 580, 760, 1180, 400, 500, 500, 500, 500, 500, 880, 500, 500, 500, 280, 500, 500, 340, 540, 900, 620, ) diff --git a/bin/pychart/afm/Courier.py b/bin/pychart/afm/Courier.py deleted file mode 100644 index 92da5498f3e..00000000000 --- a/bin/pychart/afm/Courier.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Courier (path: /usr/share/fonts/afms/adobe/pcrr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Courier"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 600, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 500, 600, 500, 500, 500, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 600, 500, 500, 500, 600, 500, 500, 600, 600, 600, 600, ) diff --git a/bin/pychart/afm/Courier_Bold.py b/bin/pychart/afm/Courier_Bold.py deleted file mode 100644 index 3b8d493e022..00000000000 --- a/bin/pychart/afm/Courier_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Courier-Bold (path: /usr/share/fonts/afms/adobe/pcrb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Courier-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 600, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 500, 600, 500, 500, 500, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 600, 500, 500, 500, 600, 500, 500, 600, 600, 600, 600, ) diff --git a/bin/pychart/afm/Courier_BoldOblique.py b/bin/pychart/afm/Courier_BoldOblique.py deleted file mode 100644 index 44abc0b75be..00000000000 --- a/bin/pychart/afm/Courier_BoldOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Courier-BoldOblique (path: /usr/share/fonts/afms/adobe/pcrbo8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Courier-BoldOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 600, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 500, 600, 500, 500, 500, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 600, 500, 500, 500, 600, 500, 500, 600, 600, 600, 600, ) diff --git a/bin/pychart/afm/Courier_Oblique.py b/bin/pychart/afm/Courier_Oblique.py deleted file mode 100644 index 834d14bcacd..00000000000 --- a/bin/pychart/afm/Courier_Oblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Courier-Oblique (path: /usr/share/fonts/afms/adobe/pcrro8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Courier-Oblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 600, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 500, 600, 600, 600, 600, 600, 600, 600, 600, 500, 600, 600, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 600, 500, 600, 500, 500, 500, 500, 600, 600, 600, 600, 500, 500, 500, 500, 500, 600, 500, 500, 500, 600, 500, 500, 600, 600, 600, 600, ) diff --git a/bin/pychart/afm/Helvetica.py b/bin/pychart/afm/Helvetica.py deleted file mode 100644 index b9a0f775ed2..00000000000 --- a/bin/pychart/afm/Helvetica.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica (path: /usr/share/fonts/afms/adobe/phvr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 191, 333, 556, 333, 333, 500, 500, 500, 556, 556, 556, 278, 500, 537, 350, 222, 333, 333, 556, 1000, 1000, 500, 611, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 370, 500, 500, 500, 500, 556, 778, 1000, 365, 500, 500, 500, 500, 500, 889, 500, 500, 500, 278, 500, 500, 222, 611, 944, 611, ) diff --git a/bin/pychart/afm/Helvetica_Bold.py b/bin/pychart/afm/Helvetica_Bold.py deleted file mode 100644 index 1e6b332d384..00000000000 --- a/bin/pychart/afm/Helvetica_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Bold (path: /usr/share/fonts/afms/adobe/phvb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 238, 500, 556, 333, 333, 611, 611, 500, 556, 556, 556, 278, 500, 556, 350, 278, 500, 500, 556, 1000, 1000, 500, 611, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 370, 500, 500, 500, 500, 611, 778, 1000, 365, 500, 500, 500, 500, 500, 889, 500, 500, 500, 278, 500, 500, 278, 611, 944, 611, ) diff --git a/bin/pychart/afm/Helvetica_BoldOblique.py b/bin/pychart/afm/Helvetica_BoldOblique.py deleted file mode 100644 index 98e8ea9edde..00000000000 --- a/bin/pychart/afm/Helvetica_BoldOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-BoldOblique (path: /usr/share/fonts/afms/adobe/phvbo8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-BoldOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 238, 500, 556, 333, 333, 611, 611, 500, 556, 556, 556, 278, 500, 556, 350, 278, 500, 500, 556, 1000, 1000, 500, 611, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 370, 500, 500, 500, 500, 611, 778, 1000, 365, 500, 500, 500, 500, 500, 889, 500, 500, 500, 278, 500, 500, 278, 611, 944, 611, ) diff --git a/bin/pychart/afm/Helvetica_Light.py b/bin/pychart/afm/Helvetica_Light.py deleted file mode 100644 index 69128431e80..00000000000 --- a/bin/pychart/afm/Helvetica_Light.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Light (path: /usr/share/fonts/afms/adobe/phvl8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Light"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 333, 278, 556, 556, 889, 667, 222, 333, 333, 389, 660, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 660, 660, 660, 500, 800, 667, 667, 722, 722, 611, 556, 778, 722, 278, 500, 667, 556, 833, 722, 778, 611, 778, 667, 611, 556, 722, 611, 889, 611, 611, 611, 333, 278, 333, 660, 500, 222, 556, 611, 556, 611, 556, 278, 611, 556, 222, 222, 500, 222, 833, 556, 556, 611, 611, 333, 500, 278, 556, 500, 722, 500, 500, 500, 333, 222, 333, 660, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 222, 389, 556, 389, 389, 500, 500, 500, 500, 556, 556, 278, 500, 650, 500, 222, 389, 389, 556, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 334, 500, 500, 500, 500, 556, 778, 1000, 334, 500, 500, 500, 500, 500, 889, 500, 500, 500, 222, 500, 500, 222, 556, 944, 500, ) diff --git a/bin/pychart/afm/Helvetica_LightOblique.py b/bin/pychart/afm/Helvetica_LightOblique.py deleted file mode 100644 index efc82aabe4f..00000000000 --- a/bin/pychart/afm/Helvetica_LightOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-LightOblique (path: /usr/share/fonts/afms/adobe/phvlo8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-LightOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 333, 278, 556, 556, 889, 667, 222, 333, 333, 389, 660, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 660, 660, 660, 500, 800, 667, 667, 722, 722, 611, 556, 778, 722, 278, 500, 667, 556, 833, 722, 778, 611, 778, 667, 611, 556, 722, 611, 889, 611, 611, 611, 333, 278, 333, 660, 500, 222, 556, 611, 556, 611, 556, 278, 611, 556, 222, 222, 500, 222, 833, 556, 556, 611, 611, 333, 500, 278, 556, 500, 722, 500, 500, 500, 333, 222, 333, 660, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 222, 389, 556, 389, 389, 500, 500, 500, 500, 556, 556, 278, 500, 650, 500, 222, 389, 389, 556, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 334, 500, 500, 500, 500, 556, 778, 1000, 334, 500, 500, 500, 500, 500, 889, 500, 500, 500, 222, 500, 500, 222, 556, 944, 500, ) diff --git a/bin/pychart/afm/Helvetica_Narrow.py b/bin/pychart/afm/Helvetica_Narrow.py deleted file mode 100644 index 4b570843741..00000000000 --- a/bin/pychart/afm/Helvetica_Narrow.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Narrow (path: /usr/share/fonts/afms/adobe/phvr8an.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Narrow"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 228, 228, 291, 456, 456, 729, 547, 182, 273, 273, 319, 479, 228, 273, 228, 228, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 228, 228, 479, 479, 479, 456, 832, 547, 547, 592, 592, 547, 501, 638, 592, 228, 410, 547, 456, 683, 592, 638, 547, 638, 592, 547, 501, 592, 547, 774, 547, 547, 501, 228, 228, 228, 385, 456, 182, 456, 456, 410, 456, 456, 228, 456, 456, 182, 182, 410, 182, 683, 456, 456, 456, 456, 273, 410, 228, 456, 410, 592, 410, 410, 410, 274, 213, 274, 479, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 273, 456, 456, 137, 456, 456, 456, 456, 157, 273, 456, 273, 273, 410, 410, 500, 456, 456, 456, 228, 500, 440, 287, 182, 273, 273, 456, 820, 820, 500, 501, 500, 273, 273, 273, 273, 273, 273, 273, 273, 500, 273, 273, 500, 273, 273, 273, 820, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 820, 500, 303, 500, 500, 500, 500, 456, 638, 820, 299, 500, 500, 500, 500, 500, 729, 500, 500, 500, 228, 500, 500, 182, 501, 774, 501, ) diff --git a/bin/pychart/afm/Helvetica_Narrow_Bold.py b/bin/pychart/afm/Helvetica_Narrow_Bold.py deleted file mode 100644 index 009b6987003..00000000000 --- a/bin/pychart/afm/Helvetica_Narrow_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Narrow-Bold (path: /usr/share/fonts/afms/adobe/phvb8an.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Narrow-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 228, 273, 389, 456, 456, 729, 592, 228, 273, 273, 319, 479, 228, 273, 228, 228, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 273, 273, 479, 479, 479, 501, 800, 592, 592, 592, 592, 547, 501, 638, 592, 228, 456, 592, 501, 683, 592, 638, 547, 638, 592, 547, 501, 592, 547, 774, 547, 547, 501, 273, 228, 273, 479, 456, 228, 456, 501, 456, 501, 456, 273, 501, 501, 228, 228, 456, 228, 729, 501, 501, 501, 501, 319, 456, 273, 501, 456, 638, 456, 456, 410, 319, 230, 319, 479, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 273, 456, 456, 137, 456, 456, 456, 456, 195, 410, 456, 273, 273, 501, 501, 500, 456, 456, 456, 228, 500, 456, 287, 228, 410, 410, 456, 820, 820, 500, 501, 500, 273, 273, 273, 273, 273, 273, 273, 273, 500, 273, 273, 500, 273, 273, 273, 820, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 820, 500, 303, 500, 500, 500, 500, 501, 638, 820, 299, 500, 500, 500, 500, 500, 729, 500, 500, 500, 228, 500, 500, 228, 501, 774, 501, ) diff --git a/bin/pychart/afm/Helvetica_Narrow_BoldOblique.py b/bin/pychart/afm/Helvetica_Narrow_BoldOblique.py deleted file mode 100644 index 98d7e2ef4a7..00000000000 --- a/bin/pychart/afm/Helvetica_Narrow_BoldOblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Narrow-BoldOblique (path: /usr/share/fonts/afms/adobe/phvbo8an.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Narrow-BoldOblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 228, 273, 389, 456, 456, 729, 592, 228, 273, 273, 319, 479, 228, 273, 228, 228, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 273, 273, 479, 479, 479, 501, 800, 592, 592, 592, 592, 547, 501, 638, 592, 228, 456, 592, 501, 683, 592, 638, 547, 638, 592, 547, 501, 592, 547, 774, 547, 547, 501, 273, 228, 273, 479, 456, 228, 456, 501, 456, 501, 456, 273, 501, 501, 228, 228, 456, 228, 729, 501, 501, 501, 501, 319, 456, 273, 501, 456, 638, 456, 456, 410, 319, 230, 319, 479, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 273, 456, 456, 137, 456, 456, 456, 456, 195, 410, 456, 273, 273, 501, 501, 500, 456, 456, 456, 228, 500, 456, 287, 228, 410, 410, 456, 820, 820, 500, 501, 500, 273, 273, 273, 273, 273, 273, 273, 273, 500, 273, 273, 500, 273, 273, 273, 820, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 820, 500, 303, 500, 500, 500, 500, 501, 638, 820, 299, 500, 500, 500, 500, 500, 729, 500, 500, 500, 228, 500, 500, 228, 501, 774, 501, ) diff --git a/bin/pychart/afm/Helvetica_Narrow_Oblique.py b/bin/pychart/afm/Helvetica_Narrow_Oblique.py deleted file mode 100644 index 8612d7211b0..00000000000 --- a/bin/pychart/afm/Helvetica_Narrow_Oblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Narrow-Oblique (path: /usr/share/fonts/afms/adobe/phvro8an.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Narrow-Oblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 228, 228, 291, 456, 456, 729, 547, 182, 273, 273, 319, 479, 228, 273, 228, 228, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 228, 228, 479, 479, 479, 456, 832, 547, 547, 592, 592, 547, 501, 638, 592, 228, 410, 547, 456, 683, 592, 638, 547, 638, 592, 547, 501, 592, 547, 774, 547, 547, 501, 228, 228, 228, 385, 456, 182, 456, 456, 410, 456, 456, 228, 456, 456, 182, 182, 410, 182, 683, 456, 456, 456, 456, 273, 410, 228, 456, 410, 592, 410, 410, 410, 274, 213, 274, 479, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 273, 456, 456, 137, 456, 456, 456, 456, 157, 273, 456, 273, 273, 410, 410, 500, 456, 456, 456, 228, 500, 440, 287, 182, 273, 273, 456, 820, 820, 500, 501, 500, 273, 273, 273, 273, 273, 273, 273, 273, 500, 273, 273, 500, 273, 273, 273, 820, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 820, 500, 303, 500, 500, 500, 500, 456, 638, 820, 299, 500, 500, 500, 500, 500, 729, 500, 500, 500, 228, 500, 500, 182, 501, 774, 501, ) diff --git a/bin/pychart/afm/Helvetica_Oblique.py b/bin/pychart/afm/Helvetica_Oblique.py deleted file mode 100644 index b8ee7e383e6..00000000000 --- a/bin/pychart/afm/Helvetica_Oblique.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Helvetica-Oblique (path: /usr/share/fonts/afms/adobe/phvro8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Helvetica-Oblique"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 556, 556, 191, 333, 556, 333, 333, 500, 500, 500, 556, 556, 556, 278, 500, 537, 350, 222, 333, 333, 556, 1000, 1000, 500, 611, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 370, 500, 500, 500, 500, 556, 778, 1000, 365, 500, 500, 500, 500, 500, 889, 500, 500, 500, 278, 500, 500, 222, 611, 944, 611, ) diff --git a/bin/pychart/afm/NewCenturySchlbk_Bold.py b/bin/pychart/afm/NewCenturySchlbk_Bold.py deleted file mode 100644 index a553f31bb20..00000000000 --- a/bin/pychart/afm/NewCenturySchlbk_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font NewCenturySchlbk-Bold (path: /usr/share/fonts/afms/adobe/pncb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["NewCenturySchlbk-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 287, 296, 333, 574, 574, 833, 852, 241, 389, 389, 500, 606, 278, 333, 278, 278, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 278, 278, 606, 606, 606, 500, 747, 759, 778, 778, 833, 759, 722, 833, 870, 444, 648, 815, 722, 981, 833, 833, 759, 833, 815, 667, 722, 833, 759, 981, 722, 722, 667, 389, 606, 389, 606, 500, 241, 611, 648, 556, 667, 574, 389, 611, 685, 370, 352, 667, 352, 963, 685, 611, 667, 648, 519, 500, 426, 685, 611, 889, 611, 611, 537, 389, 606, 389, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 296, 574, 574, 167, 574, 574, 500, 574, 241, 481, 500, 333, 333, 685, 685, 500, 500, 500, 500, 278, 500, 747, 606, 241, 481, 481, 500, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 981, 500, 367, 500, 500, 500, 500, 722, 833, 1000, 367, 500, 500, 500, 500, 500, 870, 500, 500, 500, 370, 500, 500, 352, 611, 907, 611, ) diff --git a/bin/pychart/afm/NewCenturySchlbk_BoldItalic.py b/bin/pychart/afm/NewCenturySchlbk_BoldItalic.py deleted file mode 100644 index 7daa58ee2ff..00000000000 --- a/bin/pychart/afm/NewCenturySchlbk_BoldItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font NewCenturySchlbk-BoldItalic (path: /usr/share/fonts/afms/adobe/pncbi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["NewCenturySchlbk-BoldItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 287, 333, 400, 574, 574, 889, 889, 259, 407, 407, 500, 606, 287, 333, 287, 278, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 287, 287, 606, 606, 606, 481, 747, 741, 759, 759, 833, 741, 704, 815, 870, 444, 667, 778, 704, 944, 852, 833, 741, 833, 796, 685, 722, 833, 741, 944, 741, 704, 704, 407, 606, 407, 606, 500, 259, 667, 611, 537, 667, 519, 389, 611, 685, 389, 370, 648, 389, 944, 685, 574, 648, 630, 519, 481, 407, 685, 556, 833, 574, 519, 519, 407, 606, 407, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 574, 574, 167, 574, 574, 500, 574, 287, 481, 481, 278, 278, 685, 685, 500, 500, 500, 500, 287, 500, 650, 606, 259, 481, 481, 481, 1000, 1167, 500, 481, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 889, 500, 412, 500, 500, 500, 500, 704, 833, 963, 356, 500, 500, 500, 500, 500, 815, 500, 500, 500, 389, 500, 500, 389, 574, 852, 574, ) diff --git a/bin/pychart/afm/NewCenturySchlbk_Italic.py b/bin/pychart/afm/NewCenturySchlbk_Italic.py deleted file mode 100644 index cfe6b4f060b..00000000000 --- a/bin/pychart/afm/NewCenturySchlbk_Italic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font NewCenturySchlbk-Italic (path: /usr/share/fonts/afms/adobe/pncri8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["NewCenturySchlbk-Italic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 333, 400, 556, 556, 833, 852, 204, 333, 333, 500, 606, 278, 333, 278, 606, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 606, 606, 606, 444, 747, 704, 722, 722, 778, 722, 667, 778, 833, 407, 611, 741, 667, 944, 815, 778, 667, 778, 741, 667, 685, 815, 704, 926, 704, 685, 667, 333, 606, 333, 606, 500, 204, 574, 556, 444, 611, 444, 333, 537, 611, 333, 315, 556, 333, 889, 611, 500, 574, 556, 444, 444, 352, 611, 519, 778, 500, 500, 463, 333, 606, 333, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 556, 556, 167, 556, 556, 500, 556, 278, 389, 426, 333, 333, 611, 611, 500, 500, 500, 500, 278, 500, 650, 606, 204, 389, 389, 426, 1000, 1000, 500, 444, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 870, 500, 422, 500, 500, 500, 500, 667, 778, 981, 372, 500, 500, 500, 500, 500, 722, 500, 500, 500, 333, 500, 500, 333, 500, 778, 556, ) diff --git a/bin/pychart/afm/NewCenturySchlbk_Roman.py b/bin/pychart/afm/NewCenturySchlbk_Roman.py deleted file mode 100644 index 07e62553fed..00000000000 --- a/bin/pychart/afm/NewCenturySchlbk_Roman.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font NewCenturySchlbk-Roman (path: /usr/share/fonts/afms/adobe/pncr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["NewCenturySchlbk-Roman"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 296, 389, 556, 556, 833, 815, 204, 333, 333, 500, 606, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 606, 606, 606, 444, 737, 722, 722, 722, 778, 722, 667, 778, 833, 407, 556, 778, 667, 944, 815, 778, 667, 778, 722, 630, 667, 815, 722, 981, 704, 704, 611, 333, 606, 333, 606, 500, 204, 556, 556, 444, 574, 500, 333, 537, 611, 315, 296, 593, 315, 889, 611, 500, 574, 556, 444, 463, 389, 611, 537, 778, 537, 537, 481, 333, 606, 333, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 296, 556, 556, 167, 556, 556, 500, 556, 204, 389, 426, 259, 259, 611, 611, 500, 556, 500, 500, 278, 500, 606, 606, 204, 389, 389, 426, 1000, 1000, 500, 444, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 334, 500, 500, 500, 500, 667, 778, 1000, 300, 500, 500, 500, 500, 500, 796, 500, 500, 500, 315, 500, 500, 315, 500, 833, 574, ) diff --git a/bin/pychart/afm/Palatino_Bold.py b/bin/pychart/afm/Palatino_Bold.py deleted file mode 100644 index ecc968966cb..00000000000 --- a/bin/pychart/afm/Palatino_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Palatino-Bold (path: /usr/share/fonts/afms/adobe/pplb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Palatino-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 278, 402, 500, 500, 889, 833, 278, 333, 333, 444, 606, 250, 333, 250, 296, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 250, 606, 606, 606, 444, 747, 778, 667, 722, 833, 611, 556, 833, 833, 389, 389, 778, 611, 1000, 833, 833, 611, 833, 722, 611, 667, 778, 778, 1000, 667, 667, 667, 333, 606, 333, 606, 500, 278, 500, 611, 444, 611, 500, 389, 556, 611, 333, 333, 611, 333, 889, 611, 556, 611, 611, 389, 444, 333, 611, 556, 833, 500, 556, 500, 310, 606, 310, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 500, 500, 167, 500, 500, 500, 500, 227, 500, 500, 389, 389, 611, 611, 500, 500, 500, 500, 250, 500, 641, 606, 333, 500, 500, 500, 1000, 1000, 500, 444, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 438, 500, 500, 500, 500, 611, 833, 1000, 488, 500, 500, 500, 500, 500, 778, 500, 500, 500, 333, 500, 500, 333, 556, 833, 611, ) diff --git a/bin/pychart/afm/Palatino_BoldItalic.py b/bin/pychart/afm/Palatino_BoldItalic.py deleted file mode 100644 index 64c12005939..00000000000 --- a/bin/pychart/afm/Palatino_BoldItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Palatino-BoldItalic (path: /usr/share/fonts/afms/adobe/pplbi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Palatino-BoldItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 500, 500, 500, 889, 833, 278, 333, 333, 444, 606, 250, 389, 250, 315, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 250, 606, 606, 606, 444, 833, 722, 667, 685, 778, 611, 556, 778, 778, 389, 389, 722, 611, 944, 778, 833, 667, 833, 722, 556, 611, 778, 667, 1000, 722, 611, 667, 333, 606, 333, 606, 500, 278, 556, 537, 444, 556, 444, 333, 500, 556, 333, 333, 556, 333, 833, 556, 556, 556, 537, 389, 444, 389, 556, 556, 833, 500, 556, 500, 333, 606, 333, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 500, 500, 167, 500, 500, 556, 500, 250, 500, 500, 333, 333, 611, 611, 500, 500, 556, 556, 250, 500, 556, 606, 250, 500, 500, 500, 1000, 1000, 500, 444, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 556, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 944, 500, 333, 500, 500, 500, 500, 611, 833, 944, 333, 500, 500, 500, 500, 500, 738, 500, 500, 500, 333, 500, 500, 333, 556, 778, 556, ) diff --git a/bin/pychart/afm/Palatino_Italic.py b/bin/pychart/afm/Palatino_Italic.py deleted file mode 100644 index 9e22479089d..00000000000 --- a/bin/pychart/afm/Palatino_Italic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Palatino-Italic (path: /usr/share/fonts/afms/adobe/pplri8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Palatino-Italic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 500, 500, 500, 889, 778, 278, 333, 333, 389, 606, 250, 333, 250, 296, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 250, 606, 606, 606, 500, 747, 722, 611, 667, 778, 611, 556, 722, 778, 333, 333, 667, 556, 944, 778, 778, 611, 778, 667, 556, 611, 778, 722, 944, 722, 667, 667, 333, 606, 333, 606, 500, 278, 444, 463, 407, 500, 389, 278, 500, 500, 278, 278, 444, 278, 778, 556, 444, 500, 463, 389, 389, 333, 556, 500, 722, 500, 500, 444, 333, 606, 333, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 500, 500, 167, 500, 500, 500, 500, 333, 500, 500, 333, 333, 528, 545, 500, 500, 500, 500, 250, 500, 500, 500, 278, 500, 500, 500, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 941, 500, 333, 500, 500, 500, 500, 556, 778, 1028, 333, 500, 500, 500, 500, 500, 638, 500, 500, 500, 278, 500, 500, 278, 444, 669, 500, ) diff --git a/bin/pychart/afm/Palatino_Roman.py b/bin/pychart/afm/Palatino_Roman.py deleted file mode 100644 index ada67fc41da..00000000000 --- a/bin/pychart/afm/Palatino_Roman.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Palatino-Roman (path: /usr/share/fonts/afms/adobe/pplr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Palatino-Roman"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 278, 371, 500, 500, 840, 778, 278, 333, 333, 389, 606, 250, 333, 250, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 250, 606, 606, 606, 444, 747, 778, 611, 709, 774, 611, 556, 763, 832, 337, 333, 726, 611, 946, 831, 786, 604, 786, 668, 525, 613, 778, 722, 1000, 667, 667, 667, 333, 606, 333, 606, 500, 278, 500, 553, 444, 611, 479, 333, 556, 582, 291, 234, 556, 291, 883, 582, 546, 601, 560, 395, 424, 326, 603, 565, 834, 516, 556, 500, 333, 606, 333, 606, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 500, 500, 167, 500, 500, 500, 500, 208, 500, 500, 331, 331, 605, 608, 500, 500, 500, 500, 250, 500, 628, 606, 278, 500, 500, 500, 1000, 1144, 500, 444, 500, 333, 333, 333, 333, 333, 333, 250, 333, 500, 333, 333, 500, 380, 313, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 944, 500, 333, 500, 500, 500, 500, 611, 833, 998, 333, 500, 500, 500, 500, 500, 758, 500, 500, 500, 287, 500, 500, 291, 556, 827, 556, ) diff --git a/bin/pychart/afm/Symbol.py b/bin/pychart/afm/Symbol.py deleted file mode 100644 index a9e7b9af4d8..00000000000 --- a/bin/pychart/afm/Symbol.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Symbol (path: /usr/share/fonts/afms/adobe/psyr.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Symbol"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 713, 500, 549, 833, 778, 439, 333, 333, 500, 549, 250, 549, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 549, 549, 549, 444, 549, 722, 667, 722, 612, 611, 763, 603, 722, 333, 631, 722, 686, 889, 722, 722, 768, 741, 556, 592, 611, 690, 439, 768, 645, 795, 611, 333, 863, 333, 658, 500, 500, 631, 549, 549, 494, 439, 521, 411, 603, 329, 603, 549, 549, 576, 521, 549, 549, 521, 549, 603, 439, 576, 713, 686, 493, 686, 494, 480, 200, 480, 549, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 620, 247, 549, 167, 713, 500, 753, 753, 753, 753, 1042, 987, 603, 987, 603, 400, 549, 411, 549, 549, 713, 494, 460, 549, 549, 549, 549, 1000, 603, 1000, 658, 823, 686, 795, 987, 768, 768, 823, 768, 768, 713, 713, 713, 713, 713, 713, 713, 768, 713, 790, 790, 890, 823, 549, 250, 713, 603, 603, 1042, 987, 603, 987, 603, 494, 329, 790, 790, 786, 713, 384, 384, 384, 384, 384, 384, 494, 494, 494, 494, 500, 329, 274, 686, 686, 686, 384, 384, 384, 384, 384, 384, 494, 494, 494, ) diff --git a/bin/pychart/afm/Times_Bold.py b/bin/pychart/afm/Times_Bold.py deleted file mode 100644 index 6db5447bfbf..00000000000 --- a/bin/pychart/afm/Times_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Times-Bold (path: /usr/share/fonts/afms/adobe/ptmb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Times-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 555, 500, 500, 1000, 833, 333, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 500, 500, 167, 500, 500, 500, 500, 278, 500, 500, 333, 333, 556, 556, 500, 500, 500, 500, 250, 500, 540, 350, 333, 500, 500, 500, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 1000, 500, 300, 500, 500, 500, 500, 667, 778, 1000, 330, 500, 500, 500, 500, 500, 722, 500, 500, 500, 278, 500, 500, 278, 500, 722, 556, ) diff --git a/bin/pychart/afm/Times_BoldItalic.py b/bin/pychart/afm/Times_BoldItalic.py deleted file mode 100644 index 3188d46f813..00000000000 --- a/bin/pychart/afm/Times_BoldItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Times-BoldItalic (path: /usr/share/fonts/afms/adobe/ptmbi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Times-BoldItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 389, 555, 500, 500, 833, 778, 333, 333, 333, 500, 570, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500, 832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722, 611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500, 333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500, 500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 389, 500, 500, 167, 500, 500, 500, 500, 278, 500, 500, 333, 333, 556, 556, 500, 500, 500, 500, 250, 500, 500, 350, 333, 500, 500, 500, 1000, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 944, 500, 266, 500, 500, 500, 500, 611, 722, 944, 300, 500, 500, 500, 500, 500, 722, 500, 500, 500, 278, 500, 500, 278, 500, 722, 500, ) diff --git a/bin/pychart/afm/Times_Italic.py b/bin/pychart/afm/Times_Italic.py deleted file mode 100644 index 4a4df2b8a98..00000000000 --- a/bin/pychart/afm/Times_Italic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Times-Italic (path: /usr/share/fonts/afms/adobe/ptmri8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Times-Italic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 420, 500, 500, 833, 778, 333, 333, 333, 500, 675, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500, 920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722, 611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500, 333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500, 500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 389, 500, 500, 167, 500, 500, 500, 500, 214, 556, 500, 333, 333, 500, 500, 500, 500, 500, 500, 250, 500, 523, 350, 333, 556, 556, 500, 889, 1000, 500, 500, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 889, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 889, 500, 276, 500, 500, 500, 500, 556, 722, 944, 310, 500, 500, 500, 500, 500, 667, 500, 500, 500, 278, 500, 500, 278, 500, 667, 500, ) diff --git a/bin/pychart/afm/Times_Roman.py b/bin/pychart/afm/Times_Roman.py deleted file mode 100644 index a03ee6591e4..00000000000 --- a/bin/pychart/afm/Times_Roman.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Times-Roman (path: /usr/share/fonts/afms/adobe/ptmr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Times-Roman"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 250, 333, 408, 500, 500, 833, 778, 333, 333, 333, 500, 564, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 500, 500, 167, 500, 500, 500, 500, 180, 444, 500, 333, 333, 556, 556, 500, 500, 500, 500, 250, 500, 453, 350, 333, 444, 444, 500, 1000, 1000, 500, 444, 500, 333, 333, 333, 333, 333, 333, 333, 333, 500, 333, 333, 500, 333, 333, 333, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 889, 500, 276, 500, 500, 500, 500, 611, 722, 889, 310, 500, 500, 500, 500, 500, 667, 500, 500, 500, 278, 500, 500, 278, 500, 722, 500, ) diff --git a/bin/pychart/afm/Utopia_Bold.py b/bin/pychart/afm/Utopia_Bold.py deleted file mode 100644 index 5f9c25c158f..00000000000 --- a/bin/pychart/afm/Utopia_Bold.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Utopia-Bold (path: /usr/share/fonts/afms/adobe/putb8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Utopia-Bold"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 210, 278, 473, 560, 560, 887, 748, 252, 365, 365, 442, 600, 280, 392, 280, 378, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 280, 280, 600, 600, 600, 456, 833, 644, 683, 689, 777, 629, 593, 726, 807, 384, 386, 707, 585, 918, 739, 768, 650, 768, 684, 561, 624, 786, 645, 933, 634, 617, 614, 335, 379, 335, 600, 500, 252, 544, 605, 494, 605, 519, 342, 533, 631, 316, 316, 582, 309, 948, 638, 585, 615, 597, 440, 446, 370, 629, 520, 774, 522, 524, 483, 365, 284, 365, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 560, 560, 100, 560, 560, 566, 560, 252, 473, 487, 287, 287, 639, 639, 500, 500, 510, 486, 280, 500, 552, 455, 252, 473, 473, 487, 1000, 1289, 500, 456, 500, 430, 430, 430, 430, 430, 430, 430, 430, 500, 430, 430, 500, 430, 430, 430, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 879, 500, 405, 500, 500, 500, 500, 591, 768, 1049, 427, 500, 500, 500, 500, 500, 806, 500, 500, 500, 316, 500, 500, 321, 585, 866, 662, ) diff --git a/bin/pychart/afm/Utopia_BoldItalic.py b/bin/pychart/afm/Utopia_BoldItalic.py deleted file mode 100644 index 9ef88006c6b..00000000000 --- a/bin/pychart/afm/Utopia_BoldItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Utopia-BoldItalic (path: /usr/share/fonts/afms/adobe/putbi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Utopia-BoldItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 210, 285, 455, 560, 560, 896, 752, 246, 350, 350, 500, 600, 280, 392, 280, 260, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 280, 280, 600, 600, 600, 454, 828, 634, 680, 672, 774, 622, 585, 726, 800, 386, 388, 688, 586, 921, 741, 761, 660, 761, 681, 551, 616, 776, 630, 920, 630, 622, 618, 350, 460, 350, 600, 500, 246, 596, 586, 456, 609, 476, 348, 522, 629, 339, 333, 570, 327, 914, 635, 562, 606, 584, 440, 417, 359, 634, 518, 795, 516, 489, 466, 340, 265, 340, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 285, 560, 560, 100, 560, 560, 568, 560, 246, 455, 560, 360, 360, 651, 652, 500, 500, 514, 490, 280, 500, 580, 465, 246, 455, 455, 560, 1000, 1297, 500, 454, 500, 400, 400, 400, 400, 400, 400, 402, 400, 500, 400, 400, 500, 400, 350, 400, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 890, 500, 444, 500, 500, 500, 500, 592, 761, 1016, 412, 500, 500, 500, 500, 500, 789, 500, 500, 500, 339, 500, 500, 339, 562, 811, 628, ) diff --git a/bin/pychart/afm/Utopia_Italic.py b/bin/pychart/afm/Utopia_Italic.py deleted file mode 100644 index 920c1271193..00000000000 --- a/bin/pychart/afm/Utopia_Italic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Utopia-Italic (path: /usr/share/fonts/afms/adobe/putri8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Utopia-Italic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 225, 240, 402, 530, 530, 826, 725, 216, 350, 350, 412, 570, 265, 392, 265, 270, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 265, 265, 570, 570, 570, 425, 794, 624, 632, 661, 763, 596, 571, 709, 775, 345, 352, 650, 565, 920, 763, 753, 614, 753, 640, 533, 606, 794, 637, 946, 632, 591, 622, 330, 390, 330, 570, 500, 216, 561, 559, 441, 587, 453, 315, 499, 607, 317, 309, 545, 306, 912, 618, 537, 590, 559, 402, 389, 341, 618, 510, 785, 516, 468, 468, 340, 270, 340, 570, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 240, 530, 530, 100, 530, 530, 530, 530, 216, 402, 462, 277, 277, 607, 603, 500, 500, 500, 490, 265, 500, 560, 500, 216, 402, 402, 462, 1000, 1200, 500, 425, 500, 400, 400, 400, 400, 400, 400, 402, 400, 500, 400, 400, 500, 400, 350, 400, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 880, 500, 425, 500, 500, 500, 500, 571, 753, 1020, 389, 500, 500, 500, 500, 500, 779, 500, 500, 500, 317, 500, 500, 318, 537, 806, 577, ) diff --git a/bin/pychart/afm/Utopia_Regular.py b/bin/pychart/afm/Utopia_Regular.py deleted file mode 100644 index 656c6a58d7d..00000000000 --- a/bin/pychart/afm/Utopia_Regular.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font Utopia-Regular (path: /usr/share/fonts/afms/adobe/putr8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["Utopia-Regular"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 225, 242, 458, 530, 530, 838, 706, 278, 350, 350, 412, 570, 265, 392, 265, 460, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 265, 265, 570, 570, 570, 389, 793, 635, 646, 684, 779, 606, 580, 734, 798, 349, 350, 658, 568, 944, 780, 762, 600, 762, 644, 541, 621, 791, 634, 940, 624, 588, 610, 330, 460, 330, 570, 500, 278, 523, 598, 496, 598, 514, 319, 520, 607, 291, 280, 524, 279, 923, 619, 577, 608, 591, 389, 436, 344, 606, 504, 768, 486, 506, 480, 340, 228, 340, 570, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 242, 530, 530, 150, 530, 530, 554, 530, 278, 458, 442, 257, 257, 610, 610, 500, 500, 504, 488, 265, 500, 555, 409, 278, 458, 458, 442, 1000, 1208, 500, 389, 500, 400, 400, 400, 400, 400, 400, 400, 400, 500, 400, 400, 500, 400, 400, 400, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 876, 500, 390, 500, 500, 500, 500, 574, 762, 1025, 398, 500, 500, 500, 500, 500, 797, 500, 500, 500, 291, 500, 500, 294, 577, 882, 601, ) diff --git a/bin/pychart/afm/ZapfChancery_MediumItalic.py b/bin/pychart/afm/ZapfChancery_MediumItalic.py deleted file mode 100644 index 00e0e93f809..00000000000 --- a/bin/pychart/afm/ZapfChancery_MediumItalic.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font ZapfChancery-MediumItalic (path: /usr/share/fonts/afms/adobe/pzcmi8a.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["ZapfChancery-MediumItalic"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 220, 280, 220, 440, 440, 680, 780, 240, 260, 220, 420, 520, 220, 280, 220, 340, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 260, 240, 520, 520, 520, 380, 700, 620, 600, 520, 700, 620, 580, 620, 680, 380, 400, 660, 580, 840, 700, 600, 540, 600, 600, 460, 500, 740, 640, 880, 560, 560, 620, 240, 480, 320, 520, 500, 240, 420, 420, 340, 440, 340, 320, 400, 440, 240, 220, 440, 240, 620, 460, 400, 440, 400, 300, 320, 320, 460, 440, 680, 420, 400, 440, 240, 520, 240, 520, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 280, 440, 440, 60, 440, 440, 420, 440, 160, 340, 340, 240, 260, 520, 520, 500, 500, 460, 480, 220, 500, 500, 600, 180, 280, 360, 380, 1000, 960, 500, 400, 500, 220, 300, 340, 440, 440, 440, 220, 360, 500, 300, 300, 500, 400, 280, 340, 1000, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 740, 500, 260, 500, 500, 500, 500, 580, 660, 820, 260, 500, 500, 500, 500, 500, 540, 500, 500, 500, 240, 500, 500, 300, 440, 560, 420, ) diff --git a/bin/pychart/afm/ZapfDingbats.py b/bin/pychart/afm/ZapfDingbats.py deleted file mode 100644 index d93cdda3893..00000000000 --- a/bin/pychart/afm/ZapfDingbats.py +++ /dev/null @@ -1,5 +0,0 @@ -# AFM font ZapfDingbats (path: /usr/share/fonts/afms/adobe/pzdr.afm). -# Derived from Ghostscript distribution. -# Go to www.cs.wisc.edu/~ghost to get the Ghostcript source code. -import dir -dir.afm["ZapfDingbats"] = (500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 974, 961, 974, 980, 719, 789, 790, 791, 690, 960, 939, 549, 855, 911, 933, 911, 945, 974, 755, 846, 762, 761, 571, 677, 763, 760, 759, 754, 494, 552, 537, 577, 692, 786, 788, 788, 790, 793, 794, 816, 823, 789, 841, 823, 833, 816, 831, 923, 744, 723, 749, 790, 792, 695, 776, 768, 792, 759, 707, 708, 682, 701, 826, 815, 789, 789, 707, 687, 696, 689, 786, 787, 713, 791, 785, 791, 873, 761, 762, 762, 759, 759, 892, 892, 788, 784, 438, 138, 277, 415, 392, 392, 668, 668, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 732, 544, 544, 910, 667, 760, 760, 776, 595, 694, 626, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 894, 838, 1016, 458, 748, 924, 748, 918, 927, 928, 928, 834, 873, 828, 924, 924, 917, 930, 931, 463, 883, 836, 836, 867, 867, 696, 696, 874, 500, 874, 760, 946, 771, 865, 771, 888, 967, 888, 831, 873, 927, 970, 918, ) diff --git a/bin/pychart/afm/__init__.py b/bin/pychart/afm/__init__.py deleted file mode 100644 index 93cb9f953e7..00000000000 --- a/bin/pychart/afm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ["Courier_Oblique", "AvantGarde_BookOblique", "Times_Italic", "Helvetica_Bold", "NewCenturySchlbk_Roman", "Helvetica", "Helvetica_Narrow", "AvantGarde_Demi", "Times_BoldItalic", "Helvetica_Narrow_Bold", "Helvetica_Light", "Bookman_DemiItalic", "Utopia_Regular", "Times_Roman", "Palatino_Italic", "Courier_Bold", "ZapfChancery_MediumItalic", "NewCenturySchlbk_Italic", "NewCenturySchlbk_BoldItalic", "Helvetica_Narrow_BoldOblique", "Courier", "AvantGarde_DemiOblique", "Courier_BoldOblique", "Bookman_LightItalic", "Symbol", "Utopia_Bold", "Times_Bold", "Helvetica_BoldOblique", "Utopia_BoldItalic", "AvantGarde_Book", "Bookman_Demi", "Palatino_Roman", "Bookman_Light", "Utopia_Italic", "NewCenturySchlbk_Bold", "Helvetica_LightOblique", "ZapfDingbats", "Helvetica_Narrow_Oblique", "Helvetica_Oblique", "Palatino_BoldItalic", "Palatino_Bold", "dir"] diff --git a/bin/pychart/afm/dir.py b/bin/pychart/afm/dir.py deleted file mode 100644 index a978c1b85b7..00000000000 --- a/bin/pychart/afm/dir.py +++ /dev/null @@ -1,2 +0,0 @@ -afm = {} - diff --git a/bin/pychart/area.py b/bin/pychart/area.py deleted file mode 100644 index a0e76876ec0..00000000000 --- a/bin/pychart/area.py +++ /dev/null @@ -1,249 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import coord -import line_style -import legend -import axis -import pychart_util -import chart_object -import fill_style -import canvas -import area_doc -import linear_coord -import category_coord -import theme -from pychart_types import * -from types import * - -_dummy_legend = legend.T() - -def range_doc(t): - u = t.upper() - - return """Specifies the range of %s values that are displayed in the - chart. IF the value is None, both the values are computed - automatically from the samples. Otherwise, the value must be a - tuple of format (MIN, MAX). MIN and MAX must be either None or a - number. If None, the value is computed automatically from the - samples. For example, if %s_range = (None,5), then the minimum %s - value is computed automatically, but the maximum %s value is fixed - at 5.""" % (u, t, u, u) - -_keys = { - "loc" : (CoordType, (0,0), - """The location of the bottom-left corner of the chart. -@cindex chart location -@cindex location, chart -"""), - "size" : (CoordType, (120,110), - """The size of the chart-drawing area, excluding axis labels, - legends, tick marks, etc. -@cindex chart size -@cindex size, chart - """), - "bg_style": (fill_style.T, None, "Background fill-pattern."), - "border_line_style": (line_style.T, None, "Line style of the outer frame of the chart."), - "x_coord": - (coord.T, linear_coord.T(), - """Set the X coordinate system.""", - """A linear coordinate system."""), - "y_coord": (coord.T, linear_coord.T(), - "Set the Y coordinate system.", - """A linear coordinate system."""), - "x_range": (CoordType, None, range_doc("x")), - "y_range": (CoordType, None, range_doc("y")), - "x_axis": (axis.X, None, "The X axis. <>."), - "x_axis2": (axis.X, None, """The second X axis. This axis should be non-None either when you want to display plots with two distinct domains or when - you just want to display two axes at the top and bottom of the chart. - <>"""), - "y_axis": (axis.Y, None, "The Y axis. <>."), - "y_axis2": (axis.Y, None, - """The second Y axis. This axis should be non-None either when you want to display plots with two distinct ranges or when - you just want to display two axes at the left and right of the chart. <>"""), - "x_grid_style" : (line_style.T, None, - """The style of horizontal grid lines. -@cindex grid lines"""), - "y_grid_style" : (line_style.T, line_style.gray70_dash3, - "The style of vertical grid lines."), - "x_grid_interval": (IntervalType, None, - """The horizontal grid-line interval. - A numeric value - specifies the interval at which - lines are drawn. If value is a function, it - takes two arguments, (MIN, MAX), that tells - the minimum and maximum values found in the - sample data. The function should return a list - of values at which lines are drawn."""), - "y_grid_interval": (IntervalType, None, - "The vertical grid-line interval. See also x_grid_interval"), - "x_grid_over_plot": (IntType, False, - "If True, grid lines are drawn over plots. Otherwise, plots are drawn over grid lines."), - "y_grid_over_plot": (IntType, False, "See x_grid_over_plot."), - "plots": (ListType, pychart_util.new_list, - """Used only internally by pychart."""), - "legend": (legend.T, _dummy_legend, "The legend of the chart.", - """a legend is by default displayed - in the right-center of the chart."""), - } - - -class T(chart_object.T): - keys = _keys - __doc__ = area_doc.doc -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def x_pos(self, xval): - "Return the x position (on the canvas) corresponding to XVAL." - off = self.x_coord.get_canvas_pos(self.size[0], xval, - self.x_range[0], self.x_range[1]) - return self.loc[0] + off - - def y_pos(self, yval): - "Return the y position (on the canvas) corresponding to YVAL." - off = self.y_coord.get_canvas_pos(self.size[1], yval, - self.y_range[0], self.y_range[1]) - return self.loc[1] + off - - def x_tic_points(self, interval): - "Return the list of X values for which tick marks and grid lines are drawn." - if type(interval) == FunctionType: - return apply(interval, self.x_range) - - return self.x_coord.get_tics(self.x_range[0], self.x_range[1], interval) - def y_tic_points(self, interval): - "Return the list of Y values for which tick marks and grid lines are drawn." - if type(interval) == FunctionType: - return apply(interval, self.y_range) - - return self.y_coord.get_tics(self.y_range[0], self.y_range[1], interval) - def __draw_x_grid_and_axis(self, can): - if self.x_grid_style: - for i in self.x_tic_points(self.x_grid_interval): - x = self.x_pos(i) - if x > self.loc[0]: - can.line(self.x_grid_style, - x, self.loc[1], x, self.loc[1]+self.size[1]) - if self.x_axis: - self.x_axis.draw(self, can) - if self.x_axis2: - self.x_axis2.draw(self, can) - def __draw_y_grid_and_axis(self, can): - if self.y_grid_style: - for i in self.y_tic_points(self.y_grid_interval): - y = self.y_pos(i) - if y > self.loc[1]: - can.line(self.y_grid_style, - self.loc[0], y, - self.loc[0]+self.size[0], y) - if self.y_axis: - self.y_axis.draw(self, can) - if self.y_axis2: - self.y_axis2.draw(self, can) - - def __get_data_range(self, r, which, coord, interval): - if isinstance(coord, category_coord.T): - # This info is unused for the category coord type. - # So I just return a random value. - return ((0,0), 1) - - r = r or (None, None) - - if len(self.plots) == 0: - raise ValueError, "No chart to draw, and no data range specified.\n"; - dmin, dmax = 999999, -999999 - - for plot in self.plots: - this_min, this_max = plot.get_data_range(which) - dmin = min(this_min, dmin) - dmax = max(this_max, dmax) - - if interval and type(interval) == FunctionType: - tics = apply(interval, (dmin, dmax)) - dmin = tics[0] - dmax = tics[len(tics)-1] - else: - dmin, dmax, interval = coord.get_min_max(dmin, dmax, interval) - - if r[0] != None: - dmin = r[0] - if r[1] != None: - dmax = r[1] - return ((dmin, dmax), interval) - def draw(self, can = None): - "Draw the charts." - - if can == None: - can = canvas.default_canvas() - - self.type_check() - for plot in self.plots: - plot.check_integrity() - - self.x_range, self.x_grid_interval = \ - self.__get_data_range(self.x_range, 'X', - self.x_coord, - self.x_grid_interval) - - self.y_range, self.y_grid_interval = \ - self.__get_data_range(self.y_range, 'Y', - self.y_coord, - self.y_grid_interval) - - can.rectangle(self.border_line_style, self.bg_style, - self.loc[0], self.loc[1], - self.loc[0] + self.size[0], self.loc[1] + self.size[1]) - - if not self.x_grid_over_plot: - self.__draw_x_grid_and_axis(can) - - if not self.y_grid_over_plot: - self.__draw_y_grid_and_axis(can) - - clipbox = theme.adjust_bounding_box([self.loc[0], self.loc[1], - self.loc[0] + self.size[0], - self.loc[1] + self.size[1]]) - - can.clip(clipbox[0], clipbox[1], - clipbox[2], clipbox[3]) - - for plot in self.plots: - plot.draw(self, can) - - can.endclip() - - if self.x_grid_over_plot: - self.__draw_x_grid_and_axis(can) - if self.y_grid_over_plot: - self.__draw_y_grid_and_axis(can) - - if self.legend == _dummy_legend: - self.legend = legend.T() - - if self.legend: - legends = [] - for plot in self.plots: - entry = plot.get_legend_entry() - if entry == None: - pass - elif type(entry) != ListType: - legends.append(entry) - else: - for e in entry: - legends.append(e) - self.legend.draw(self, legends, can) - - def add_plot(self, *plots): - "Add PLOTS... to the area." - self.plots.extend(plots) diff --git a/bin/pychart/area_doc.py b/bin/pychart/area_doc.py deleted file mode 100644 index 63f8f6ae430..00000000000 --- a/bin/pychart/area_doc.py +++ /dev/null @@ -1,63 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -plots(type:list) default="Used only internally by pychart.". -loc(type:(x,y)) default="The location of the bottom-left corner of the chart. -@cindex chart location -@cindex location, chart -". -y_grid_style(type:line_style.T) default="The style of vertical grid lines.". -y_grid_interval(type:Number or function) default="The vertical grid-line interval. See also x_grid_interval". -x_grid_over_plot(type:int) default="If True, grid lines are drawn over plots. Otherwise, plots are drawn over grid lines.". -x_range(type:(x,y)) default="Specifies the range of X values that are displayed in the - chart. IF the value is None, both the values are computed - automatically from the samples. Otherwise, the value must be a - tuple of format (MIN, MAX). MIN and MAX must be either None or a - number. If None, the value is computed automatically from the - samples. For example, if x_range = (None,5), then the minimum X - value is computed automatically, but the maximum X value is fixed - at 5.". -y_coord(type:coord.T) default="Set the Y coordinate system.". - A linear coordinate system. - -y_range(type:(x,y)) default="Specifies the range of Y values that are displayed in the - chart. IF the value is None, both the values are computed - automatically from the samples. Otherwise, the value must be a - tuple of format (MIN, MAX). MIN and MAX must be either None or a - number. If None, the value is computed automatically from the - samples. For example, if y_range = (None,5), then the minimum Y - value is computed automatically, but the maximum Y value is fixed - at 5.". -x_axis(type:axis.X) default="The X axis. <>.". -bg_style(type:fill_style.T) default="Background fill-pattern.". -x_coord(type:coord.T) default="Set the X coordinate system.". - A linear coordinate system. - -legend(type:legend.T) default="The legend of the chart.". - a legend is by default displayed in the right-center of the - chart. - -y_grid_over_plot(type:int) default="See x_grid_over_plot.". -x_axis2(type:axis.X) default="The second X axis. This axis should be non-None either when you want to display plots with two distinct domains or when - you just want to display two axes at the top and bottom of the chart. - <>". -y_axis2(type:axis.Y) default="The second Y axis. This axis should be non-None either when you want to display plots with two distinct ranges or when - you just want to display two axes at the left and right of the chart. <>". -x_grid_style(type:line_style.T) default="The style of horizontal grid lines. -@cindex grid lines". -y_axis(type:axis.Y) default="The Y axis. <>.". -border_line_style(type:line_style.T) default="Line style of the outer frame of the chart.". -x_grid_interval(type:Number or function) default="The horizontal grid-line interval. - A numeric value - specifies the interval at which - lines are drawn. If value is a function, it - takes two arguments, (MIN, MAX), that tells - the minimum and maximum values found in the - sample data. The function should return a list - of values at which lines are drawn.". -size(type:(x,y)) default="The size of the chart-drawing area, excluding axis labels, - legends, tick marks, etc. -@cindex chart size -@cindex size, chart - ". -""" - diff --git a/bin/pychart/arrow.py b/bin/pychart/arrow.py deleted file mode 100644 index e0b931fb8a2..00000000000 --- a/bin/pychart/arrow.py +++ /dev/null @@ -1,184 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import line_style -import color -import chart_object -import object_set -import math -import arrow_doc -import canvas - -from pychart_types import * -from types import * -from scaling import * - -__doc__ = """ -Arrow is an optional component of a chart that draws line segments with -an arrowhead. To draw an arrow, one creates an arrow.T object, and calls -its "draw" method usually after area.draw() is called (otherwise, area.draw() -may overwrite the arrow). For example, the below code draws an arrow -from (10,10) to (20,30). - -ar = area.T(...) -a = arrow.T(head_style = 1) -ar.draw() -a.draw([(10,10), (20,30)]) -""" - -def draw_arrowhead(can, tailx, taily, tipx, tipy, thickness, head_len, style): - can.comment("ARROWHEAD tail=(%d,%d) tip=(%d,%d)\n" - % (tailx, taily, tipx, tipy)) - - halfthickness = thickness/2.0 - dx = tipx - tailx - dy = tipy - taily - arrow_len = math.sqrt(dx*dx + dy*dy) - angle = math.atan2(dy, dx) * 360 / (2*math.pi) - base = arrow_len - head_len - can.push_transformation((tailx, taily), None, angle) - - can.newpath() - if style == 0: - can.moveto(base, - halfthickness) - can.lineto(base, halfthickness) - can.lineto(arrow_len, 0) - can.closepath() - elif style == 1: - depth = head_len / 2.5 - can.moveto(base - depth, -halfthickness) - can.lineto(base, 0) - can.lineto(base - depth, halfthickness) - can.lineto(arrow_len, 0) - can.closepath() - elif style == 2: - can.moveto(base + head_len/2.0, 0) - can.path_arc(base + head_len / 2.0, 0, head_len / 2.0, 1.0, 0, 400) - elif style == 3: - can.moveto(base, 0) - can.lineto(base + head_len/2.0, -halfthickness) - can.lineto(arrow_len, 0) - can.lineto(base + head_len/2.0, halfthickness) - can.closepath() - else: - raise Exception, "Arrow style must be a number between 0 and 3." - can.fill() - can.pop_transformation() - can.comment("end ARROWHEAD.\n") - -def draw_arrowbody(can, tailx, taily, tipx, tipy, head_len): - dx = tipx - tailx - dy = tipy - taily - arrow_len = math.sqrt(dx*dx + dy*dy) - angle = math.atan2(dy, dx) * 360 / (2*math.pi) - base = arrow_len - head_len - can.push_transformation((tailx, taily), None, angle) - can.moveto(0, 0) - can.lineto(base+head_len*0.1, 0) - can.stroke() - can.pop_transformation() - - -class T(chart_object.T): - __doc__ = arrow_doc.doc - keys = { - "thickness" : (UnitType, 4, - "The width of the arrow head."), - "head_len": (UnitType, 8, - "The length of the arrow head."), - "head_color": (color.T, color.default, - "The color of the arrow head."), - "line_style": (line_style.T, line_style.default, - "Line style."), - "head_style": (IntType, 1, - "The value of 0 draws a triangular arrow head. The value of 1 draws a swallow-tail arrow head. The value of 2 draws a circular head. The value of 3 draws a diamond-shaped head.") - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, points, can = None): - """Parameter specifies the - list of points the arrow traverses through. - It should contain at least two points, i.e., - the tail and tip. Parameter - is an optional parameter that specifies the output. - <> - """ - if can == None: can = canvas.default_canvas() - self.type_check() - xtip = points[-1][0] - ytip = points[-1][1] - - xtail = points[-2][0] - ytail = points[-2][1] - - can.newpath() - can.set_line_style(self.line_style) - if len(points) > 2: - can.moveto(points[0][0], points[0][1]) - for i in range(1, len(points)-1): - can.lineto(points[i][0], points[i][1]) - - draw_arrowbody(can, xscale(xtail), yscale(ytail), - yscale(xtip), yscale(ytip), - nscale(self.head_len)) - - can.set_fill_color(self.head_color) - draw_arrowhead(can, xscale(xtail), yscale(ytail), - xscale(xtip), yscale(ytip), - nscale(self.thickness), - nscale(self.head_len), - self.head_style) - - can.setbb(xtail, ytail) - can.setbb(xtip, ytip) - -standards = object_set.T() -def _intern(a): - global standards - standards.add(a) - return a - -a0 = _intern(T(head_style=0)) -a1 = _intern(T(head_style=1)) -a2 = _intern(T(head_style=2)) -a3 = _intern(T(head_style=3)) -gray0 = _intern(T(head_style=0, head_color = color.gray50, - line_style=line_style.T(color=color.gray50))) -gray1 = _intern(T(head_style=1, head_color = color.gray50, - line_style=line_style.T(color=color.gray50))) -gray2 = _intern(T(head_style=2, head_color = color.gray50, - line_style=line_style.T(color=color.gray50))) -gray3 = _intern(T(head_style=3, head_color = color.gray50, - line_style=line_style.T(color=color.gray50))) - -fat0 = _intern(T(head_style=0, head_len=12, thickness=10, line_style=line_style.T(width=2))) -fat1 = _intern(T(head_style=1, head_len=12, thickness=10, line_style=line_style.T(width=2))) -fat2 = _intern(T(head_style=2, head_len=12, thickness=10, line_style=line_style.T(width=2))) -fat3 = _intern(T(head_style=3, head_len=12, thickness=10, line_style=line_style.T(width=2))) -fatgray0 = _intern(T(head_style=0, head_len=12, thickness=10, - head_color = color.gray50, - line_style=line_style.T(width=2, color=color.gray50))) -fatgray1 = _intern(T(head_style=1, head_len=12, thickness=10, - head_color = color.gray50, - line_style=line_style.T(width=2, color=color.gray50))) -fatgray2 = _intern(T(head_style=2, head_len=12, thickness=10, - head_color = color.gray50, - line_style=line_style.T(width=2, color=color.gray50))) -fatgray3 = _intern(T(head_style=3, head_len=12, thickness=10, - head_color = color.gray50, - line_style=line_style.T(width=2, color=color.gray50))) - -default = a1 - - diff --git a/bin/pychart/arrow_doc.py b/bin/pychart/arrow_doc.py deleted file mode 100644 index f76958d9864..00000000000 --- a/bin/pychart/arrow_doc.py +++ /dev/null @@ -1,9 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -head_color(type:color.T) default="The color of the arrow head.". -head_style(type:int) default="The value of 0 draws a triangular arrow head. The value of 1 draws a swallow-tail arrow head. The value of 2 draws a circular head. The value of 3 draws a diamond-shaped head.". -head_len(type:length in points (\\xref{unit})) default="The length of the arrow head.". -line_style(type:line_style.T) default="Line style.". -thickness(type:length in points (\\xref{unit})) default="The width of the arrow head.". -""" - diff --git a/bin/pychart/axis.py b/bin/pychart/axis.py deleted file mode 100644 index 64d3fd2409d..00000000000 --- a/bin/pychart/axis.py +++ /dev/null @@ -1,241 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import font -import pychart_util -import chart_object -import line_style -import math -import theme -import axis_doc -from pychart_types import * -from types import * - -class T(chart_object.T): - keys = { - "tic_interval" : (IntervalType, None, - pychart_util.interval_desc("tick marks")), - "tic_len" : (UnitType, 6, """The length of tick lines. The value can be negative, in which case the tick lines are drawn right of (or above) the axis."""), - "minor_tic_interval" : (IntervalType, None, - pychart_util.interval_desc("minor tick marks")), - "minor_tic_len" : (UnitType, 3, """The length of minor tick marks. The value can be negative, in which case the tick lines are drawn right of (or above) the axis."""), - "line_style": (line_style.T, line_style.default, - "Specifies the style of axis and tick lines."), - "label": (types.StringType, "axis label", - "The descriptive string displayed below (or to the left of) the axis. <>."), - "format": (FormatType, "%s", - """The format string for tick labels. - It can be a `printf' style format string, or - a single-parameter function that takes an X (or Y) value - and returns a string. """ + - pychart_util.string_desc), - "label_offset": (CoordOrNoneType, (None,None), - """The location for drawing the axis label, - relative to the middle point of the axis. - If the value is None, the label is displayed - below (or to the left of) of axis at the middle."""), - "tic_label_offset": (CoordType, (0,0), - """The location for drawing tick labels, - relative to the tip of the tick line."""), - "offset": (UnitType, 0, - """The location of the axis. - The value of 0 draws the - axis at the left (for the Y axis) or bottom (for the X axis) - edge of the drawing area. - """) - } - -class X(T): - keys = pychart_util.union_dict(T.keys, - {"draw_tics_above": (IntType, 0, - "If true, tick lines and labels are drawn above the axis line.")}) - __doc__ = axis_doc.doc_x -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw_below(self, ar, can): - self.type_check() - self.tic_interval = self.tic_interval or ar.x_grid_interval - y_base = ar.loc[1] + self.offset - - can.line(self.line_style, ar.loc[0], y_base, - ar.loc[0]+ ar.size[0], y_base) - - tic_dic = {} - max_tic_height = 0 - - for i in ar.x_tic_points(self.tic_interval): - tic_dic[i] = 1 - ticx = ar.x_pos(i) - - str = "/hC" + pychart_util.apply_format(self.format, (i, ), 0) - - (total_height, base_height) = font.text_height(str) - max_tic_height = max(max_tic_height, total_height) - - can.line(self.line_style, ticx, y_base, ticx, y_base-self.tic_len) - can.show(ticx+self.tic_label_offset[0], - y_base-self.tic_len-base_height+self.tic_label_offset[1], - str) - - if self.minor_tic_interval: - for i in ar.x_tic_points(self.minor_tic_interval): - if tic_dic.has_key(i): - # a major tic was drawn already. - pass - else: - ticx = ar.x_pos(i) - can.line(self.line_style, ticx, y_base, ticx, - y_base-self.minor_tic_len) - - self.draw_label(ar, can, y_base - self.tic_len - max_tic_height - 10) - - def draw_above(self, ar, can): - y_base = ar.loc[1] + self.offset - - tic_dic = {} - max_tic_height = 0 - - for i in ar.x_tic_points(self.tic_interval): - tic_dic[i] = 1 - ticx = ar.x_pos(i) - - str = "/hC" + pychart_util.apply_format(self.format, (i, ), 0) - - (total_height, base_height) = font.text_height(str) - max_tic_height = max(max_tic_height, total_height) - - can.line(self.line_style, ticx, y_base, ticx, y_base + self.tic_len) - can.show(ticx+self.tic_label_offset[0], - y_base + self.tic_len + base_height + self.tic_label_offset[1], - str) - - if self.minor_tic_interval: - for i in ar.x_tic_points(self.minor_tic_interval): - if tic_dic.has_key(i): - # a major tic was drawn already. - pass - else: - ticx = ar.x_pos(i) - can.line(self.line_style, ticx, y_base, ticx, - y_base + self.minor_tic_len) - self.draw_label(ar, can, y_base + self.tic_len + max_tic_height + 10) - - def draw_label(self, ar, can, ylabel): - if self.label == None: return - - str = "/hC/vM" + self.label - (label_height, base_height) = font.text_height(str) - xlabel = ar.loc[0] + ar.size[0]/2.0 - if self.label_offset[0] != None: - xlabel += self.label_offset[0] - if self.label_offset[1] != None: - ylabel += self.label_offset[1] - can.show(xlabel, ylabel, str) - - def draw(self, ar, can): - self.type_check() - self.tic_interval = self.tic_interval or ar.x_grid_interval - y_base = ar.loc[1] + self.offset - can.line(self.line_style, ar.loc[0], y_base, - ar.loc[0]+ ar.size[0], y_base) - if self.draw_tics_above: - self.draw_above(ar, can) - else: - self.draw_below(ar, can) -class Y(T): - __doc__ = axis_doc.doc_y - keys = pychart_util.union_dict(T.keys, - {"draw_tics_right": (IntType, 0, - "If true, tick lines and labels are drawn right of the axis line.")}) - - def draw_left(self, ar, can): - x_base = ar.loc[0] + self.offset - xmin = 999999 - tic_dic = {} - for i in ar.y_tic_points(self.tic_interval): - y_tic = ar.y_pos(i) - tic_dic[i] = 1 - can.line(self.line_style, x_base, y_tic, - x_base - self.tic_len, y_tic) - str = pychart_util.apply_format(self.format, (i,), 0) - if self.tic_len > 0: str = "/hR" + str - - tic_height, base_height = font.text_height(str) - x = x_base - self.tic_len + self.tic_label_offset[0] - can.show(x, y_tic - tic_height/2.0 + self.tic_label_offset[1], - str) - xmin = min(xmin, x - font.text_width(str)) - - if self.minor_tic_interval: - for i in ar.y_tic_points(self.minor_tic_interval): - if tic_dic.has_key(i): - # a major tic line was drawn already. - pass - else: - y_tic = ar.y_pos(i) - can.line(self.line_style, x_base, y_tic, - x_base - self.minor_tic_len, y_tic) - - self.draw_label(ar, can, xmin - theme.default_font_size/2.0) - - def draw_right(self, ar, can): - x_base = ar.loc[0] + self.offset - xmax = 0 - tic_dic = {} - for i in ar.y_tic_points(self.tic_interval): - y_tic = ar.y_pos(i) - tic_dic[i] = 1 - can.line(self.line_style, x_base, y_tic, - x_base + self.tic_len, y_tic) - str = pychart_util.apply_format(self.format, (i,), 0) - if self.tic_len > 0: str = "/hL" + str - - tic_height, base_height = font.text_height(str) - x = x_base + self.tic_len + self.tic_label_offset[0] - can.show(x, y_tic - tic_height/2.0 + self.tic_label_offset[1], - str) - xmax = max(xmax, x + font.text_width(str)) - - if self.minor_tic_interval: - for i in ar.y_tic_points(self.minor_tic_interval): - if tic_dic.has_key(i): - # a major tic line was drawn already. - pass - else: - y_tic = ar.y_pos(i) - can.line(self.line_style, x_base, y_tic, - x_base + self.minor_tic_len, y_tic) - - self.draw_label(ar, can, xmax + theme.default_font_size) - - def draw_label(self, ar, can, xlabel): - if self.label == None: - return - ylabel = ar.loc[1] + ar.size[1] / 2 - if self.label_offset[0] != None: - xlabel += self.label_offset[0] - if self.label_offset[1] != None: - ylabel += self.label_offset[1] - can.show(xlabel, ylabel, "/a90/hC" + self.label) - - def draw(self, ar, can): - self.type_check() - self.tic_interval = self.tic_interval or ar.y_grid_interval - - x_base = ar.loc[0] + self.offset - can.line(self.line_style, x_base, ar.loc[1], x_base, ar.loc[1]+ar.size[1]) - if self.draw_tics_right: - self.draw_right(ar, can) - else: - self.draw_left(ar, can) diff --git a/bin/pychart/axis_doc.py b/bin/pychart/axis_doc.py deleted file mode 100644 index 48184d405a5..00000000000 --- a/bin/pychart/axis_doc.py +++ /dev/null @@ -1,53 +0,0 @@ -# automatically generated by generate_docs.py. -doc_x="""Attributes supported by this class are: -draw_tics_above(type:int) default="If true, tick lines and labels are drawn above the axis line.". -minor_tic_len(type:length in points (\\xref{unit})) default="The length of minor tick marks. The value can be negative, in which case the tick lines are drawn right of (or above) the axis.". -tic_label_offset(type:(x,y)) default="The location for drawing tick labels, - relative to the tip of the tick line.". -format(type:printf format string) default="The format string for tick labels. - It can be a `printf' style format string, or - a single-parameter function that takes an X (or Y) value - and returns a string. The appearance of the string produced here can be -controlled using escape sequences. <>". -label_offset(type:(x,y) or None) default="The location for drawing the axis label, - relative to the middle point of the axis. - If the value is None, the label is displayed - below (or to the left of) of axis at the middle.". -label(type:str) default="The descriptive string displayed below (or to the left of) the axis. <>.". -offset(type:length in points (\\xref{unit})) default="The location of the axis. - The value of 0 draws the - axis at the left (for the Y axis) or bottom (for the X axis) - edge of the drawing area. - ". -tic_interval(type:Number or function) default="When the value is a number, it specifies the interval at which tick marks are drawn. Otherwise, the value must be a function that takes no argument and returns the list of numbers. The return value specifies the X or Y points at which tick marks are drawn.". -line_style(type:line_style.T) default="Specifies the style of axis and tick lines.". -tic_len(type:length in points (\\xref{unit})) default="The length of tick lines. The value can be negative, in which case the tick lines are drawn right of (or above) the axis.". -minor_tic_interval(type:Number or function) default="When the value is a number, it specifies the interval at which minor tick marks are drawn. Otherwise, the value must be a function that takes no argument and returns the list of numbers. The return value specifies the X or Y points at which minor tick marks are drawn.". -""" - -doc_y="""Attributes supported by this class are: -draw_tics_right(type:int) default="If true, tick lines and labels are drawn right of the axis line.". -minor_tic_len(type:length in points (\\xref{unit})) default="The length of minor tick marks. The value can be negative, in which case the tick lines are drawn right of (or above) the axis.". -tic_label_offset(type:(x,y)) default="The location for drawing tick labels, - relative to the tip of the tick line.". -format(type:printf format string) default="The format string for tick labels. - It can be a `printf' style format string, or - a single-parameter function that takes an X (or Y) value - and returns a string. The appearance of the string produced here can be -controlled using escape sequences. <>". -label_offset(type:(x,y) or None) default="The location for drawing the axis label, - relative to the middle point of the axis. - If the value is None, the label is displayed - below (or to the left of) of axis at the middle.". -label(type:str) default="The descriptive string displayed below (or to the left of) the axis. <>.". -offset(type:length in points (\\xref{unit})) default="The location of the axis. - The value of 0 draws the - axis at the left (for the Y axis) or bottom (for the X axis) - edge of the drawing area. - ". -tic_interval(type:Number or function) default="When the value is a number, it specifies the interval at which tick marks are drawn. Otherwise, the value must be a function that takes no argument and returns the list of numbers. The return value specifies the X or Y points at which tick marks are drawn.". -line_style(type:line_style.T) default="Specifies the style of axis and tick lines.". -tic_len(type:length in points (\\xref{unit})) default="The length of tick lines. The value can be negative, in which case the tick lines are drawn right of (or above) the axis.". -minor_tic_interval(type:Number or function) default="When the value is a number, it specifies the interval at which minor tick marks are drawn. Otherwise, the value must be a function that takes no argument and returns the list of numbers. The return value specifies the X or Y points at which minor tick marks are drawn.". -""" - diff --git a/bin/pychart/axis_x_doc.py b/bin/pychart/axis_x_doc.py deleted file mode 100644 index 899b2b0b1de..00000000000 --- a/bin/pychart/axis_x_doc.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -doc="""Attributes supported by this class are: -tic_label_offset(type:(x,y)): - The location where the tick labels is drawn. Relative to the - tip of the tick mark. - default value=(0, 0). -format(type:printf format string): - The format string for tick labels. It can be a `printf' style - format string, or a single-parameter function that returns a - string. See also font. - default value=%s. -minor_tic_len(type:number): - The length of minor tick marks. - default value=3. -label_offset(type:(x,y) or None): - The location where the axis label is drawn. Relative to the - left-bottom corner of the axis. - default value=(None, None). -grid_interval(type:Number or function): - When the value is a number, it specifies the interval with which - grid lines are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where grid lines are drawn. - default value=None. -label(type:str): - The description of the axis. See also font. - default value=axis label. -grid_style(type:line_style.T): - The style of grid lines. - default value=None. -tic_interval(type:Number or function): - When the value is a number, it specifies the interval with which - tick marks are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where tick marks are drawn. - default value=None. -line_style(type:line_style.T): - The style of tick lines. - default value=default. -tic_len(type:number): - The length of tick lines - default value=6. -minor_tic_interval(type:Number or function): - When the value is a number, it specifies the interval with which - minor tick marks are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where minor tick marks are drawn. - default value=None. -first_tic_value(type:number): - The location of the first tick mark. Defaults to the x_range[0] - (or y_range[0]) of the area. - default value=None. -""" \ No newline at end of file diff --git a/bin/pychart/axis_y_doc.py b/bin/pychart/axis_y_doc.py deleted file mode 100644 index e254f0ef7a8..00000000000 --- a/bin/pychart/axis_y_doc.py +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -doc="""Attributes supported by this class are: -minor_tic_len(type:number): - The length of minor tick marks. - default value=3. -tic_label_offset(type:(x,y)): - The location where the tick labels is drawn. Relative to the - tip of the tick mark. - default value=(0, 0). -format(type:printf format string): - The format string for tick labels. It can be a `printf' style - format string, or a single-parameter function that returns a - string. See also font. - default value=%s. -label_offset(type:(x,y) or None): - The location where the axis label is drawn. Relative to the - left-bottom corner of the axis. - default value=(None, None). -grid_interval(type:Number or function): - When the value is a number, it specifies the interval with which - grid lines are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where grid lines are drawn. - default value=None. -tic_len(type:number): - The length of tick lines - default value=6. -grid_style(type:line_style.T): - - default value=gray70dash3. -tic_interval(type:Number or function): - When the value is a number, it specifies the interval with which - tick marks are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where tick marks are drawn. - default value=None. -line_style(type:line_style.T): - The style of tick lines. - default value=default. -label(type:str): - The description of the axis. See also font. - default value=axis label. -minor_tic_interval(type:Number or function): - When the value is a number, it specifies the interval with which - minor tick marks are drawn. Otherwise, the value must be a function. - It must take no argument and return the list of numbers, which - specifies the X or Y points where minor tick marks are drawn. - default value=None. -first_tic_value(type:number): - The location of the first tick mark. Defaults to the x_range[0] - (or y_range[0]) of the area. - default value=None. -""" diff --git a/bin/pychart/bar_plot.py b/bin/pychart/bar_plot.py deleted file mode 100644 index 7e9a8d815f7..00000000000 --- a/bin/pychart/bar_plot.py +++ /dev/null @@ -1,268 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import line_style -import fill_style -import pychart_util -import chart_object -import legend -import error_bar -import bar_plot_doc -import theme -from types import * -from pychart_types import * - -fill_styles = None - -_keys = { - "direction" : (StringType, "vertical", - """The direction the growth of the bars. The value is either 'horizontal' - or 'vertical'."""), - "data" : (AnyType, None, pychart_util.data_desc), - "data_label_offset": (CoordType, (0, 5), - "The location of data labels relative to the sample point. See also attribute data_label_format."), - - "data_label_format": (FormatType, None, """The - format string for the label displayed besides each - bar. It can be a `printf' style format - string, or a two-parameter function that - takes (x,y) values and returns a string. """ - + pychart_util.string_desc), - - "label": (StringType, "???", pychart_util.label_desc), - "bcol" : (IntType, 0, - """Specifies the column from which base values (i.e., X values when attribute "direction" is "vertical", Y values otherwise) are extracted. -The - combination of "data", "bcol", and "hcol" attributes defines - the set of boxes drawn by this chart. See the - below example: - -@example - d = [[5,10], [7,22], [8,25]] - p = bar_plot.T(data = d, bcol = 1, hcol = 2) -@end example - - Here, three bars will be drawn. The X values of the bars - will be 5, 7, and 8. The Y values of the bars will be - 10, 22, and 25, respectively. (In practice, because - the values of bcol and hcol defaults to 1 and 2, you can - write the above example just as "p = bar_plot.T(data = d)". - """), - "hcol": (IntType, 1, - """The column from which the height of each bar is extracted. - See also the description of the 'bcol' attribute."""), - "line_style": (line_style.T, line_style.default, - "The style of the outer frame of each box."), - "fill_style": (fill_style.T, lambda: fill_styles.next(), - "Defines the fill style of each box.", - "The style is picked from standard styles round-robin."), - "legend_line_style": (line_style.T, None, - """The line style used to draw a legend entry. Usually, the value is None, meaning that the value of "line_style" attribute is used."""), - "legend_fill_style": (fill_style.T, None, - """The fill style used to draw a legend entry. Usually, the value is None, meaning that the value of "fill_style" attribute is used."""), - - "cluster": (TupleType, (0, 1), """This attribute is used to - cluster multiple bar plots side by side in a single chart. - The value should be a tuple of two integers. The second value should be equal to the total number of bar plots in the chart. The first value should be the relative position of this chart; 0 places this chart the leftmost, and N-1 - (where N is the 2nd value of this attribute) places this chart the rightmost. Consider the below example: - -@example - a = area.T(...) - p1 = bar_plot.T(data = [[1,20][2,30]], cluster=(0,2)) - p2 = bar_plot.T(data = [[1,25],[2,10]], cluster=(1,2)) - a.add_plot(p1, p2) - a.draw() -@end example - - In this example, one group of bars will be drawn side-by-side at - position x=1, one with height 20, the other with height 25. The - other two bars will be drawn side by side at position x=2, one - with height 30 and the other with height 10. - """), - "width": (UnitType, 5, """Width of each box. -@cindex width, bar chart -@cindex size, bar chart -"""), - "cluster_sep": (UnitType, 0, """The separation between - clustered boxes."""), - "stack_on": (AnyType, None, - "The value must be either None or bar_plot.T. If not None, bars of this plot are stacked on top of another bar plot."), - "error_minus_col": (IntType, -1, - """Specifies the column from which the depth of the errorbar is extracted. This attribute is meaningful only when - error_bar != None. - """), - "qerror_minus_col": (IntType, -1, - """The depth of the "quartile" errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None. """), - "error_plus_col": (IntType, -1, - """The depth of the errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None."""), - "qerror_plus_col": (IntType, -1, - """The depth of the "quartile" errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None."""), - "error_bar": (error_bar.T, None, - "Specifies the style of the error bar. <>"), - "_abs_data" : (ListType, None, - "Used only internally."), - } - -def find_bar_plot(ar, nth): - "Find the NTH barplot of the cluster in area AR." - for plot in ar.plots: - if isinstance(plot, T) and plot.cluster[0] == nth: - return plot - raise Exception, "The %dth bar plot in the cluster not found." % nth - -class T(chart_object.T): - __doc__ = bar_plot_doc.doc - keys = _keys - def check_integrity(self): - self.type_check() - self.compute_abs_data() - def compute_abs_data(self): - if self._abs_data != None: - return - - if self.stack_on == None: - self._abs_data = self.data - else: - n = [] - for pair in self.data: - self.stack_on.compute_abs_data() - newpair = list(pair[:]) - newpair[self.hcol] = self.stack_on.get_value(newpair[self.bcol]) + pair[self.hcol] - n.append(newpair) - self._abs_data = n - -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def get_value(self, bval): - for pair in self._abs_data: - if pair[self.bcol] == bval: - return pair[self.hcol] - raise ValueError, str(bval) + ": can't find the xval" - - def get_data_range(self, which): - if self.direction == 'vertical': - if which == 'X': - return pychart_util.get_data_range(self._abs_data, self.bcol) - else: - return pychart_util.get_data_range(self._abs_data, self.hcol) - else: - assert self.direction == 'horizontal' - if which == 'Y': - return pychart_util.get_data_range(self._abs_data, self.bcol) - else: - return pychart_util.get_data_range(self._abs_data, self.hcol) - - def get_bar_width(self, ar, nth): - off = 0 - for i in range(0, nth): - plot = find_bar_plot(ar, i) - off += plot.width + plot.cluster_sep - return off - - def draw_vertical(self, ar, can): - for pair in self.data: - xval = pair[self.bcol] - yval = pychart_util.get_sample_val(pair, self.hcol) - - if None in (xval, yval): continue - - ybot = 0 - if self.stack_on: - ybot = self.stack_on.get_value(xval) - yval += ybot - - totalWidth = self.get_bar_width(ar, self.cluster[1]) - firstX = ar.x_pos(xval) - totalWidth/2.0 - thisX = firstX + self.get_bar_width(ar, self.cluster[0]) - - can.rectangle(self.line_style, self.fill_style, - thisX, ar.y_pos(ybot), thisX+self.width, - ar.y_pos(yval)) - - if self.error_bar: - plus = pair[self.error_minus_col or self.error_plus_col] - minus = pair[self.error_plus_col or self.error_minus_col] - qplus = 0 - qminus = 0 - if self.qerror_minus_col or self.qerror_plus_col: - qplus = pair[self.qerror_minus_col or self.qerror_plus_col] - qminus = pair[self.qerror_plus_col or self.qerror_minus_col] - if None not in (plus, minus, qplus, qminus): - self.error_bar.draw(can, (thisX+self.width/2.0, ar.y_pos(yval)), - ar.y_pos(yval - minus), - ar.y_pos(yval + plus), - ar.y_pos(yval - qminus), - ar.y_pos(yval + qplus)) - - if self.data_label_format: - can.show(thisX + self.width/2.0 + self.data_label_offset[0], - ar.y_pos(yval) + self.data_label_offset[1], - "/hC" + pychart_util.apply_format(self.data_label_format, (pair[self.bcol], pair[self.hcol]), 1)) - - def draw_horizontal(self, ar, can): - for pair in self.data: - yval = pair[self.bcol] - xval = pychart_util.get_sample_val(pair, self.hcol) - - if None in (xval, yval): continue - - xbot = 0 - if self.stack_on: - xbot = self.stack_on.get_value(yval) - xval += xbot - totalWidth = self.get_bar_width(ar, self.cluster[1]) - firstY = ar.y_pos(yval) - totalWidth/2.0 - thisY = firstY + self.get_bar_width(ar, self.cluster[0]) - - can.rectangle(self.line_style, self.fill_style, - ar.x_pos(xbot), thisY, - ar.x_pos(xval), thisY+self.width) - - if self.data_label_format: - can.show(ar.x_pos(xval) + self.data_label_offset[0], - thisY + self.width/2.0 + self.data_label_offset[1], - "/vM/hL" + pychart_util.apply_format(self.data_label_format, (pair[self.bcol], pair[self.hcol]), 1)) - - def get_legend_entry(self): - if self.label: - return legend.Entry(line_style=(self.legend_line_style or self.line_style), - fill_style=(self.legend_fill_style or self.fill_style), - label=self.label) - return None - - def draw(self, ar, can): - self.type_check() - can.clip(ar.loc[0], ar.loc[1], - ar.loc[0] + ar.size[0], ar.loc[1] + ar.size[1]) - - if self.direction == "vertical": - self.draw_vertical(ar, can) - else: - self.draw_horizontal(ar, can) - - can.endclip() - - -def init(): - global fill_styles - fill_styles = fill_style.standards.iterate() - -theme.add_reinitialization_hook(init) - diff --git a/bin/pychart/bar_plot_doc.py b/bin/pychart/bar_plot_doc.py deleted file mode 100644 index c47492ef280..00000000000 --- a/bin/pychart/bar_plot_doc.py +++ /dev/null @@ -1,79 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -cluster_sep(type:length in points (\\xref{unit})) default="The separation between - clustered boxes.". -error_plus_col(type:int) default="The depth of the errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None.". -direction(type:str) default="The direction the growth of the bars. The value is either 'horizontal' - or 'vertical'.". -legend_line_style(type:line_style.T) default="The line style used to draw a legend entry. Usually, the value is None, meaning that the value of "line_style" attribute is used.". -error_bar(type:error_bar.T) default="Specifies the style of the error bar. <>". -data_label_format(type:printf format string) default="The - format string for the label displayed besides each - bar. It can be a `printf' style format - string, or a two-parameter function that - takes (x,y) values and returns a string. The appearance of the string produced here can be -controlled using escape sequences. <>". -width(type:length in points (\\xref{unit})) default="Width of each box. -@cindex width, bar chart -@cindex size, bar chart -". -bcol(type:int) default="Specifies the column from which base values (i.e., X values when attribute "direction" is "vertical", Y values otherwise) are extracted. -The - combination of "data", "bcol", and "hcol" attributes defines - the set of boxes drawn by this chart. See the - below example: - -@example - d = [[5,10], [7,22], [8,25]] - p = bar_plot.T(data = d, bcol = 1, hcol = 2) -@end example - - Here, three bars will be drawn. The X values of the bars - will be 5, 7, and 8. The Y values of the bars will be - 10, 22, and 25, respectively. (In practice, because - the values of bcol and hcol defaults to 1 and 2, you can - write the above example just as "p = bar_plot.T(data = d)". - ". -qerror_minus_col(type:int) default="The depth of the "quartile" errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None. ". -_abs_data(type:list) default="Used only internally.". -label(type:str) default="The label to be displayed in the legend. <>, <>". -cluster(type:tuple) default="This attribute is used to - cluster multiple bar plots side by side in a single chart. - The value should be a tuple of two integers. The second value should be equal to the total number of bar plots in the chart. The first value should be the relative position of this chart; 0 places this chart the leftmost, and N-1 - (where N is the 2nd value of this attribute) places this chart the rightmost. Consider the below example: - -@example - a = area.T(...) - p1 = bar_plot.T(data = [[1,20][2,30]], cluster=(0,2)) - p2 = bar_plot.T(data = [[1,25],[2,10]], cluster=(1,2)) - a.add_plot(p1, p2) - a.draw() -@end example - - In this example, one group of bars will be drawn side-by-side at - position x=1, one with height 20, the other with height 25. The - other two bars will be drawn side by side at position x=2, one - with height 30 and the other with height 10. - ". -hcol(type:int) default="The column from which the height of each bar is extracted. - See also the description of the 'bcol' attribute.". -stack_on(type:any) default="The value must be either None or bar_plot.T. If not None, bars of this plot are stacked on top of another bar plot.". -qerror_plus_col(type:int) default="The depth of the "quartile" errorbar is extracted from - this column in data. This attribute is meaningful only - when error_bar != None.". -data_label_offset(type:(x,y)) default="The location of data labels relative to the sample point. See also attribute data_label_format.". -fill_style(type:fill_style.T) default="Defines the fill style of each box.". - The style is picked from standard styles round-robin. - -data(type:any) default="Specifies the data points. <>". -line_style(type:line_style.T) default="The style of the outer frame of each box.". -legend_fill_style(type:fill_style.T) default="The fill style used to draw a legend entry. Usually, the value is None, meaning that the value of "fill_style" attribute is used.". -error_minus_col(type:int) default="Specifies the column from which the depth of the errorbar is extracted. This attribute is meaningful only when - error_bar != None. - ". -""" - diff --git a/bin/pychart/basecanvas.py b/bin/pychart/basecanvas.py deleted file mode 100644 index bef3573cb44..00000000000 --- a/bin/pychart/basecanvas.py +++ /dev/null @@ -1,505 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import math -import sys -import time -import re - -import font -import pychart_util -import version -from scaling import * - -def _compute_bounding_box(points): - """Given the list of coordinates (x,y), this procedure computes - the smallest rectangle that covers all the points.""" - (xmin, ymin, xmax, ymax) = (999999, 999999, -999999, -999999) - for p in points: - xmin = min(xmin, p[0]) - xmax = max(xmax, p[0]) - ymin = min(ymin, p[1]) - ymax = max(ymax, p[1]) - return (xmin, ymin, xmax, ymax) - -def _intersect_box(b1, b2): - xmin = max(b1[0], b2[0]) - ymin = max(b1[1], b2[1]) - xmax = min(b1[2], b2[2]) - ymax = min(b1[3], b2[3]) - return (xmin, ymin, xmax, ymax) - -def invisible_p(x, y): - """Return true if the point (X, Y) is visible in the canvas.""" - if x < -499999 or y < -499999: - return 1 - return 0 - -def to_radian(deg): - return deg*2*math.pi / 360.0 - -def midpoint(p1, p2): - return ( (p1[0]+p2[0])/2.0, (p1[1]+p2[1])/2.0 ) - - -active_canvases = [] - -InvalidCoord = 999999 -class T(object): - def __init__(self): - global active_canvases - - self.__xmax = -InvalidCoord - self.__xmin = InvalidCoord - self.__ymax = -InvalidCoord - self.__ymin = InvalidCoord - self.__clip_box = (-InvalidCoord, -InvalidCoord, InvalidCoord, InvalidCoord) - self.__clip_stack = [] - self.__nr_gsave = 0 - - self.title = re.sub("(.*)\\.py$", "\\1", sys.argv[0]) - self.creator = "pychart %s" % (version.version,) - self.creation_date = time.strftime("(%m/%d/%y) (%I:%M %p)") - self.aux_comments = "" - self.author = None - active_canvases.append(self) - - def set_title(self, s): - """Define the string to shown in EPS/PDF "Title" field. The default value is the name of the script that creates the EPS/PDF file.""" - self.title = s - - def set_creator(self, tag): - """Define the string to be shown in EPS %%Creator or PDF Producer field. The default value is "pychart".""" - self.creator = tag - - def set_creation_date(self, s): - """Define the string to shown in EPS/PDF "CreationDate" field. Defalt value of this field is the current time.""" - self.creation_date = s - - def set_author(self, s): - """Set the author string. Unless this method is called, the Author field is not output in EPS or PDF.""" - self.author = s - - def add_aux_comments(self, s): - """Define an auxiliary comments to be output to the file, just after the required headers""" - self.aux_comments += s - - def close(self): - """This method closes the canvas and writes - contents to the associated file. - Calling this procedure is optional, because - Pychart calls this procedure for every open canvas on normal exit.""" - for i in range(0, len(active_canvases)): - if active_canvases[i] == self: - del active_canvases[i] - return - - def open_output(self, fname): - """Open the output file FNAME. Returns tuple (FD, NEED_CLOSE), - where FD is a file (or file-like) object, and NEED_CLOSE is a - boolean flag that tells whether FD.close() should be called - after finishing writing to the file. - - FNAME can be one of the three things: - (1) None, in which case (sys.stdout, False) is returned. - (2) A file-like object, in which case (fname, False) is returned. - (3) A string, in which case this procedure opens the file and returns - (fd, True).""" - - if not fname: - return (sys.stdout, False) - elif isinstance(fname, str): - return (file(fname, "wb"), True) - else: - if not hasattr(fname, "write"): - raise Exception, "Expecting either a filename or a file-like object, but got %s" % fname - return (fname, False) - - def setbb(self, x, y): - """Call this method when point (X,Y) is to be drawn in the - canvas. This methods expands the bounding box to include - this point.""" - self.__xmin = min(self.__xmin, max(x, self.__clip_box[0])) - self.__xmax = max(self.__xmax, min(x, self.__clip_box[2])) - self.__ymin = min(self.__ymin, max(y, self.__clip_box[1])) - self.__ymax = max(self.__ymax, min(y, self.__clip_box[3])) - - def fill_with_pattern(self, pat, x1, y1, x2, y2): - if invisible_p(x2, y2): return - - self.comment("FILL pat=%s (%d %d)-(%d %d)\n" % (pat, x1, y1, x2, y2)) - self.set_fill_color(pat.bgcolor) - self._path_polygon([(x1, y1), (x1, y2), (x2, y2), (x2, y1)]) - self.fill() - pat.draw(self, x1, y1, x2, y2) - self.comment("end FILL.\n") - - def _path_polygon(self, points): - "Low-level polygon-drawing routine." - (xmin, ymin, xmax, ymax) = _compute_bounding_box(points) - if invisible_p(xmax, ymax): - return - self.setbb(xmin, ymin) - self.setbb(xmax, ymax) - - self.newpath() - self.moveto(xscale(points[0][0]), yscale(points[0][1])) - for point in points[1:]: - self.lineto(xscale(point[0]), yscale(point[1])) - self.closepath() - - def polygon(self, edge_style, pat, points, shadow = None): - """Draw a polygon with EDGE_STYLE, fill with PAT, and the edges - POINTS. POINTS is a sequence of coordinates, e.g., ((10,10), (15,5), - (20,8)). SHADOW is either None or a tuple (XDELTA, YDELTA, - fillstyle). If non-null, a shadow of FILLSTYLE is drawn beneath - the polygon at the offset of (XDELTA, YDELTA).""" - - if pat: - self.comment("POLYGON points=[%s] pat=[%s]" - % (str(points), str(pat))) - (xmin, ymin, xmax, ymax) = _compute_bounding_box(points) - - if shadow: - xoff, yoff, shadow_pat = shadow - self.gsave() - self._path_polygon(map(lambda p, xoff=xoff, yoff=yoff: (p[0]+xoff, p[1]+yoff), points)) - self.clip_sub() - self.fill_with_pattern(shadow_pat, xmin+xoff, ymin+yoff, - xmax+xoff, ymax+yoff) - self.grestore() - - self.gsave() - self._path_polygon(points) - self.clip_sub() - self.fill_with_pattern(pat, xmin, ymin, xmax, ymax) - self.grestore() - if edge_style: - self.comment("POLYGON points=[%s] edge=[%s]" - % (str(points), str(edge_style))) - self.set_line_style(edge_style) - self._path_polygon(points) - self.stroke() - - def set_background(self, pat, x1, y1, x2, y2): - xmax, xmin, ymax, ymin = self.__xmax, self.__xmin, self.__ymax, self.__ymin - self.rectangle(None, pat, x1, y1, x2, y2) - self.__xmax, self.__xmin, self.__ymax, self.__ymin = xmax, xmin, ymax, ymin - - def rectangle(self, edge_style, pat, x1, y1, x2, y2, shadow = None): - """Draw a rectangle with EDGE_STYLE, fill with PAT, and the - bounding box (X1, Y1, X2, Y2). SHADOW is either None or a - tuple (XDELTA, YDELTA, fillstyle). If non-null, a shadow of - FILLSTYLE is drawn beneath the polygon at the offset of - (XDELTA, YDELTA).""" - - self.polygon(edge_style, pat, [(x1,y1), (x1,y2), (x2,y2), (x2, y1)], - shadow) - - def _path_ellipsis(self, x, y, radius, ratio, start_angle, end_angle): - self.setbb(x - radius, y - radius*ratio) - self.setbb(x + radius, y + radius*ratio) - oradius = nscale(radius) - centerx, centery = xscale(x), yscale(y) - startx, starty = centerx+oradius * math.cos(to_radian(start_angle)), \ - centery+oradius * math.sin(to_radian(start_angle)) - self.moveto(centerx, centery) - if start_angle % 360 != end_angle % 360: - self.moveto(centerx, centery) - self.lineto(startx, starty) - else: - self.moveto(startx, starty) - self.path_arc(xscale(x), yscale(y), nscale(radius), - ratio, start_angle, end_angle) - self.closepath() - - def ellipsis(self, line_style, pattern, x, y, radius, ratio = 1.0, - start_angle=0, end_angle=360, shadow=None): - """Draw an ellipsis with line_style and fill PATTERN. The center is \ - (X, Y), X radius is RADIUS, and Y radius is RADIUS*RATIO, whose \ - default value is 1.0. SHADOW is either None or a tuple (XDELTA, - YDELTA, fillstyle). If non-null, a shadow of FILLSTYLE is drawn - beneath the polygon at the offset of (XDELTA, YDELTA).""" - - if invisible_p(x + radius, y + radius*ratio): - return - - if pattern: - if shadow: - x_off, y_off, shadow_pat = shadow - self.gsave() - self.newpath() - self._path_ellipsis(x+x_off, y+y_off, radius, ratio, - start_angle, end_angle) - self.clip_sub() - self.fill_with_pattern(shadow_pat, - x-radius*2+x_off, - y-radius*ratio*2+y_off, - x+radius*2+x_off, - y+radius*ratio*2+y_off) - self.grestore() - self.gsave() - self.newpath() - self._path_ellipsis(x, y, radius, ratio, start_angle, end_angle) - self.clip_sub() - self.fill_with_pattern(pattern, - (x-radius*2), (y-radius*ratio*2), - (x+radius*2), (y+radius*ratio*2)) - self.grestore() - if line_style: - self.set_line_style(line_style) - self.newpath() - self._path_ellipsis(x, y, radius, ratio, start_angle, end_angle) - self.stroke() - - def clip_ellipsis(self, x, y, radius, ratio = 1.0): - """Create an elliptical clip region. You must call endclip() after - you completed drawing. See also the ellipsis method.""" - - self.gsave() - self.newpath() - self.moveto(xscale(x)+nscale(radius), yscale(y)) - self.path_arc(xscale(x), yscale(y), nscale(radius), ratio, 0, 360) - self.closepath() - self.__clip_stack.append(self.__clip_box) - self.clip_sub() - - def clip_polygon(self, points): - """Create a polygonal clip region. You must call endclip() after - you completed drawing. See also the polygon method.""" - self.gsave() - self._path_polygon(points) - self.__clip_stack.append(self.__clip_box) - self.__clip_box = _intersect_box(self.__clip_box, _compute_bounding_box(points)) - self.clip_sub() - - def clip(self, x1, y1, x2, y2): - """Activate a rectangular clip region, (X1, Y1) - (X2, Y2). - You must call endclip() after you completed drawing. - -canvas.clip(x,y,x2,y2) -draw something ... -canvas.endclip() -""" - - self.__clip_stack.append(self.__clip_box) - self.__clip_box = _intersect_box(self.__clip_box, (x1, y1, x2, y2)) - self.gsave() - self.newpath() - self.moveto(xscale(x1), yscale(y1)) - self.lineto(xscale(x1), yscale(y2)) - self.lineto(xscale(x2), yscale(y2)) - self.lineto(xscale(x2), yscale(y1)) - self.closepath() - self.clip_sub() - - def endclip(self): - """End the current clip region. When clip calls are nested, it - ends the most recently created crip region.""" - self.__clip_box = self.__clip_stack[-1] - del self.__clip_stack[-1] - self.grestore() - - def curve(self, style, points): - for p in points: - self.setbb(p[0], p[1]) - self.newpath() - self.set_line_style(style) - self.moveto(xscale(points[0][0]), xscale(points[0][1])) - i = 1 - n = 1 - while i < len(points): - if n == 1: - x2 = points[i] - n += 1 - elif n == 2: - x3 = points[i] - n += 1 - elif n == 3: - x4 = midpoint(x3, points[i]) - self.curveto(xscale(x2[0]), xscale(x2[1]), - xscale(x3[0]), xscale(x3[1]), - xscale(x4[0]), xscale(x4[1])) - n = 1 - i += 1 - if n == 1: - pass - if n == 2: - self.lineto(xscale(x2[0]), xscale(x2[1])) - if n == 3: - self.curveto(xscale(x2[0]), xscale(x2[1]), - xscale(x2[0]), xscale(x2[1]), - xscale(x3[0]), xscale(x3[1])) - self.stroke() - - def line(self, style, x1, y1, x2, y2): - if not style: - return - if invisible_p(x2, y2) and invisible_p(x1, y1): - return - - self.setbb(x1, y1) - self.setbb(x2, y2) - - self.newpath() - self.set_line_style(style) - self.moveto(xscale(x1), yscale(y1)) - self.lineto(xscale(x2), yscale(y2)) - self.stroke() - - def lines(self, style, segments): - if not style: - return - (xmin, ymin, xmax, ymax) = _compute_bounding_box(segments) - if invisible_p(xmax, ymax): - return - - self.setbb(xmin, ymin) - self.setbb(xmax, ymax) - self.newpath() - self.set_line_style(style) - self.moveto(xscale(segments[0][0]), xscale(segments[0][1])) - for i in range(1, len(segments)): - self.lineto(xscale(segments[i][0]), yscale(segments[i][1])) - self.stroke() - - def _path_round_rectangle(self, x1, y1, x2, y2, radius): - self.moveto(xscale(x1 + radius), yscale(y1)) - self.lineto(xscale(x2 - radius), yscale(y1)) - self.path_arc(xscale(x2-radius), yscale(y1+radius), nscale(radius), 1, 270, 360) - self.lineto(xscale(x2), yscale(y2-radius)) - self.path_arc(xscale(x2-radius), yscale(y2-radius), nscale(radius), 1, 0, 90) - self.lineto(xscale(x1+radius), yscale(y2)) - self.path_arc(xscale(x1 + radius), yscale(y2 - radius), nscale(radius), 1, 90, 180) - self.lineto(xscale(x1), xscale(y1+radius)) - self.path_arc(xscale(x1 + radius), yscale(y1 + radius), nscale(radius), 1, 180, 270) - - def round_rectangle(self, style, fill, x1, y1, x2, y2, radius, shadow=None): - """Draw a rectangle with rounded four corners. Parameter specifies the radius of each corner.""" - - if invisible_p(x2, y2): - return - self.setbb(x1, y1) - self.setbb(x2, y2) - - if fill: - if shadow: - x_off, y_off, shadow_fill = shadow - self.gsave(); - self.newpath() - self._path_round_rectangle(x1+x_off, y1+y_off, x2+x_off, y2+y_off, - radius) - self.closepath() - self.clip_sub() - self.fill_with_pattern(shadow_fill, x1+x_off, y1+y_off, - x2+x_off, y2+y_off) - self.grestore() - - self.gsave(); - self.newpath() - self._path_round_rectangle(x1, y1, x2, y2, radius) - self.closepath() - self.clip_sub() - self.fill_with_pattern(fill, x1, y1, x2, y2) - self.grestore() - if style: - self.set_line_style(style) - self.newpath() - self._path_round_rectangle(x1, y1, x2, y2, radius) - self.closepath() - self.stroke() - - def show(self, x, y, str): - global out - y_org = y - org_str = str - - if invisible_p(x, y): - return - - (xmin, xmax, ymin, ymax) = font.get_dimension(str) - - # rectangle(line_style.default, None, x+xmin, y+ymin, x+xmax, y+ymax) - # ellipsis(line_style.default, None, x, y, 1) - self.setbb(x+xmin, y+ymin) - self.setbb(x+xmax, y+ymax) - - (halign, valign, angle) = font.get_align(str) - - base_x = x - base_y = y - - # Handle vertical alignment - if valign == "B": - y = font.unaligned_text_height(str) - elif valign == "T": - y = 0 - elif valign == "M": - y = font.unaligned_text_height(str) / 2.0 - - (xmin, xmax, ymin, ymax) = font.get_dimension(org_str) - # print org_str, xmin, xmax, ymin, ymax, x, y_org, y - self.setbb(x+xmin, y_org+y+ymin) - self.setbb(x+xmax, y_org+y+ymax) - itr = font.text_iterator(None) - - max_width = 0 - - lines = [] - for line in str.split('\n'): - cur_width = 0 - cur_height = 0 - - itr.reset(line) - - strs = [] - - while 1: - elem = itr.next() - if not elem: - break - - (font_name, size, line_height, color, _h, _v, _a, str) = elem - cur_width += font.line_width(font_name, size, str) - max_width = max(cur_width, max_width) - cur_height = max(cur_height, line_height) - - # replace '(' -> '\(', ')' -> '\)' to make - # Postscript string parser happy. - str = str.replace("(", "\\(") - str = str.replace(")", "\\)") - strs.append((font_name, size, color, str)) - lines.append((cur_width, cur_height, strs)) - - for line in lines: - cur_width, cur_height, strs = line - cur_y = y - cur_height - y = y - cur_height - self.comment("cury: %d hei %d str %s\n" % (cur_y, cur_height, strs)) - if halign == 'C': - cur_x = -cur_width/2.0 - elif halign == 'R': - cur_x = -cur_width - else: - cur_x = 0 - - rel_x, rel_y = pychart_util.rotate(cur_x, cur_y, angle) - self.text_begin() - self.text_moveto(xscale(base_x + rel_x), - yscale(base_y + rel_y), angle) - for segment in strs: - font_name, size, color, str = segment - self.text_show(font_name, nscale(size), color, str) - self.text_end() - - diff --git a/bin/pychart/canvas.py b/bin/pychart/canvas.py deleted file mode 100644 index cadceb3b5ce..00000000000 --- a/bin/pychart/canvas.py +++ /dev/null @@ -1,123 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import basecanvas -import pscanvas -import pdfcanvas -import svgcanvas -import pngcanvas -import x11canvas -import theme -import re - -invalid_coord = -999999 -_oldexitfunc = None - -T = basecanvas.T - -def init(fname = None, format = None): - - """This is a "factory" procedure that creates a new canvas.T object. - Both parameters, and - , are optional. Parameter specifies either the output - file name or a file object. Parameter , if specified, defines the - file's format. Its value must be one of "ps", "pdf", "svg", "x11", or - "png". - - When is omitted or is None, the output is sent to standard - output. When is omitted, it is guessed from the 's - suffix; failing that, "ps" is selected.""" - - fname = fname or theme.output_file - format = format or theme.output_format - - if format == None: - if not isinstance(fname, str): - format = "ps" - elif re.search("pdf$", fname): - format = "pdf" - elif re.search("png$", fname): - format = "png" - elif re.search("svg$", fname): - format = "svg" - else: - format = "ps" - - if format == "ps": - can = pscanvas.T(fname) - elif format == "png": - can = pngcanvas.T(fname) - elif format == "x11": - can = x11canvas.T(fname) - elif format == "svg": - can = svgcanvas.T(fname) - else: - can = pdfcanvas.T(fname, theme.compress_output) - return can - -def default_canvas(): - if len(basecanvas.active_canvases) > 0: - return basecanvas.active_canvases[0] - else: - return init(None) - -def _exit(): - global _oldexitfunc, _active_canvases - - for can in basecanvas.active_canvases[:]: - can.close() - - if _oldexitfunc: - foo = _oldexitfunc - _oldexitfunc = None - foo() - -# -# The following procedures are there just for backward compatibility. -# -def line(style, x1, y1, x2, y2): - default_canvas().line(style, x1, y1, x2, y2) -def curve(style, points): - default_canvas().curve(style, points) -def clip(x1, y1, x2, y2): - default_canvas().clip(x1, y1, x2, y2) -def endclip(): - default_canvas().endclip() -def clip_polygon(points): - default_canvas().clip_polygon(points) -def clip_ellipsis(x, y, radius, ratio = 1.0): - default_canvas().clip_ellipsis(x, y, radius, ratio) -def ellipsis(line_style, pattern, x, y, radius, ratio = 1.0, - start_angle=0, end_angle=360, shadow=None): - default_canvas().ellipsis(line_style, pattern, x, y, radius, ratio, - start_angle, end_angle, shadow) -def rectangle(edge_style, pat, x1, y1, x2, y2, shadow = None): - default_canvas().rectangle(edge_style, pat, x1, y1, x2, y2, shadow) -def polygon(edge_style, pat, points, shadow = None): - default_canvas().polygon(edge_style, pat, points, shadow = None) -def close(): - default_canvas().close() -def round_rectangle(style, fill, x1, y1, x2, y2, radius, shadow=None): - default_canvas().round_rectangle(style, fill, x1, y1, x2, y2, radius, shadow) -def show(x, y, str): - default_canvas().show(x, y, str) - - -if not vars(sys).has_key("exitfunc"): - sys.exitfunc = _exit -elif sys.exitfunc != _exit: - _oldexitfunc = sys.exitfunc - sys.exitfunc = _exit - -#theme.add_reinitialization_hook(lambda: init(None)) diff --git a/bin/pychart/category_coord.py b/bin/pychart/category_coord.py deleted file mode 100644 index a84d3eb5d73..00000000000 --- a/bin/pychart/category_coord.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import coord -import canvas - -class T(coord.T): - def __init__(self, data, col): - - """This attribute is meaningful only when x_coord_system == - 'category'. This attribute selects the column of - 'x_category_data' from which X values are computed. - Meaningful only when x_coord_system == 'category'. This - attribute specifies the data-set from which the X values are - extracted. See also x_category_col.""" - - self.data = data - self.col = col - - def get_canvas_pos(self, size, val, min, max): - i = 0.5 - for v in self.data: - if v[self.col] == val: - return size * i / float(len(self.data)) - i += 1 - # the drawing area is clipped. So negative offset will make this plot - # invisible. - return canvas.invalid_coord; - def get_tics(self, min, max, interval): - tics = [] - if interval == None: interval = 1 - - for i in range(0, len(self.data), interval): - tics.append(self.data[i][self.col]) - return tics - #return map(lambda pair, self = self: pair[self.col], self.data) - diff --git a/bin/pychart/chart_data.py b/bin/pychart/chart_data.py deleted file mode 100644 index 6e252c8d585..00000000000 --- a/bin/pychart/chart_data.py +++ /dev/null @@ -1,387 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import pychart_util -import copy -import math - -def _convert_item(v, typ, line): - if typ == "a": - try: - i = float(v) - except ValueError: # non-number - i = v - return i - elif typ == "d": - try: - return int(v) - except ValueError: - raise ValueError, "Can't convert %s to int; line=%s" % (v, line) - elif typ == "f": - try: - return float(v) - except ValueError: - raise ValueError, "Can't convert %s to float; line=%s" % (v, line) - elif typ == "s": - return v - else: - raise ValueError, "Unknown conversion type, type=%s; line=%s" % (typ,line) - -def parse_line(line, delim): - if delim.find("%") < 0: - return [ _convert_item(item, "a", None) for item in line.split(delim) ] - - data = [] - idx = 0 # indexes delim - ch = 'f' - sep = ',' - - while idx < len(delim): - if delim[idx] != '%': - raise ValueError, "bad delimitor: '" + delim + "'" - ch = delim[idx+1] - idx += 2 - sep = "" - while idx < len(delim) and delim[idx] != '%': - sep += delim[idx] - idx += 1 - xx = line.split(sep, 1) - data.append(_convert_item(xx[0], ch, line)) - if len(xx) >= 2: - line = xx[1] - else: - line = "" - break - - if line != "": - for item in line.split(sep): - data.append(_convert_item(item, ch, line)) - return data - -def escape_string(str): - return str.replace("/", "//") - -def extract_rows(data, *rows): - """Extract rows specified in the argument list. - ->>> chart_data.extract_rows([[10,20], [30,40], [50,60]], 1, 2) -[[30,40],[50,60]] -""" - try: - # for python 2.2 - # return [data[r] for r in rows] - out = [] - for r in rows: - out.append(data[r]) - return out - except IndexError: - raise IndexError, "data=%s rows=%s" % (data, rows) - return out - -def extract_columns(data, *cols): - """Extract columns specified in the argument list. - ->>> chart_data.extract_columns([[10,20], [30,40], [50,60]], 0) -[[10],[30],[50]] -""" - out = [] - try: - # for python 2.2: - # return [ [r[c] for c in cols] for r in data] - for r in data: - col = [] - for c in cols: - col.append(r[c]) - out.append(col) - except IndexError: - raise IndexError, "data=%s col=%s" % (data, col) - return out - - - - -def moving_average(data, xcol, ycol, width): - """Compute the moving average of YCOL'th column of each sample point -in DATA. In particular, for each element I in DATA, -this function extracts up to WIDTH*2+1 elements, consisting of - I itself, WIDTH elements before I, and WIDTH -elements after I. It then computes the mean of the YCOL'th -column of these elements, and it composes a two-element sample -consisting of XCOL'th element and the mean. - ->>> data = [[10,20], [20,30], [30,50], [40,70], [50,5]] -... chart_data.moving_average(data, 0, 1, 1) -[(10, 25.0), (20, 33.333333333333336), (30, 50.0), (40, 41.666666666666664), (50, 37.5)] - - The above value actually represents: - -[(10, (20+30)/2), (20, (20+30+50)/3), (30, (30+50+70)/3), - (40, (50+70+5)/3), (50, (70+5)/2)] - -""" - - - out = [] - try: - for i in range(len(data)): - n = 0 - total = 0 - for j in range(i-width, i+width+1): - if j >= 0 and j < len(data): - total += data[j][ycol] - n += 1 - out.append((data[i][xcol], float(total) / n)) - except IndexError: - raise IndexError, "bad data: %s,xcol=%d,ycol=%d,width=%d" % (data,xcol,ycol,width) - - return out - -def filter(func, data): - """Parameter must be a single-argument - function that takes a sequence (i.e., -a sample point) and returns a boolean. This procedure calls on -each element in and returns a list comprising elements for -which returns True. - ->>> data = [[1,5], [2,10], [3,13], [4,16]] -... chart_data.filter(lambda x: x[1] % 2 == 0, data) -[[2,10], [4,16]]. -""" - - out = [] - for r in data: - if func(r): - out.append(r) - return out - -def transform(func, data): - """Apply on each element in and return the list -consisting of the return values from . - ->>> data = [[10,20], [30,40], [50,60]] -... chart_data.transform(lambda x: [x[0], x[1]+1], data) -[[10, 21], [30, 41], [50, 61]] - -""" - out = [] - for r in data: - out.append(func(r)) - return out - -def aggregate_rows(data, col): - out = copy.deepcopy(data) - total = 0 - for r in out: - total += r[col] - r[col] = total - return out - -def empty_line_p(s): - return s.strip() == "" - -def fread_csv(fd, delim = ','): - """This function is similar to read_csv, except that it reads from - an open file handle , or any object that provides method "readline". - -fd = open("foo", "r") -data = chart_data.fread_csv(fd, ",") """ - - data = [] - line = fd.readline() - while line != "": - if line[0] != '#' and not empty_line_p(line): - data.append(parse_line(line, delim)) - line = fd.readline() - return data - -def read_csv(path, delim = ','): - """This function reads - comma-separated values from file . Empty lines and lines - beginning with "#" are ignored. Parameter specifies how - a line is separated into values. If it does not contain the - letter "%", then marks the end of a value. - Otherwise, this function acts like scanf in C: - -chart_data.read_csv("file", "%d,%s:%d") - - Paramter currently supports - only three conversion format specifiers: - "d"(int), "f"(double), and "s"(string).""" - - f = open(path) - data = fread_csv(f, delim) - f.close() - return data - -def fwrite_csv(fd, data): - """This function writes comma-separated to . Parameter must be a file-like object - that supports the |write()| method.""" - for v in data: - fd.write(",".join([str(x) for x in v])) - fd.write("\n") - -def write_csv(path, data): - """This function writes comma-separated values to .""" - fd = file(path, "w") - fwrite_csv(fd, data) - fd.close() - -def read_str(delim = ',', *lines): - """This function is similar to read_csv, but it reads data from the - list of . - -fd = open("foo", "r") -data = chart_data.read_str(",", fd.readlines())""" - - data = [] - for line in lines: - com = parse_line(line, delim) - data.append(com) - return data - -def func(f, xmin, xmax, step = None): - """Create sample points from function , which must be a - single-parameter function that returns a number (e.g., math.sin). - Parameters and specify the first and last X values, and - specifies the sampling interval. - ->>> chart_data.func(math.sin, 0, math.pi * 4, math.pi / 2) -[(0, 0.0), (1.5707963267948966, 1.0), (3.1415926535897931, 1.2246063538223773e-16), (4.7123889803846897, -1.0), (6.2831853071795862, -2.4492127076447545e-16), (7.8539816339744828, 1.0), (9.4247779607693793, 3.6738190614671318e-16), (10.995574287564276, -1.0)] - -""" - - data = [] - x = xmin - if not step: - step = (xmax - xmin) / 100.0 - while x < xmax: - data.append((x, f(x))) - x += step - return data - -def _nr_data(data, col): - nr_data = 0 - for d in data: - nr_data += d[col] - return nr_data - -def median(data, freq_col=1): - """Compute the median of the 'th column of the values is . - ->>> chart_data.median([(10,20), (20,4), (30,5)], 0) -20 ->>> chart_data.median([(10,20), (20,4), (30,5)], 1) -5. - """ - - nr_data = _nr_data(data, freq_col) - median_idx = nr_data / 2 - i = 0 - for d in data: - i += d[freq_col] - if i >= median_idx: - return d - raise Exception, "??? median ???" - -def cut_extremes(data, cutoff_percentage, freq_col=1): - nr_data = _nr_data(data, freq_col) - min_idx = nr_data * cutoff_percentage / 100.0 - max_idx = nr_data * (100 - cutoff_percentage) / 100.0 - r = [] - - i = 0 - for d in data: - if i < min_idx: - if i + d[freq_col] >= min_idx: - x = copy.deepcopy(d) - x[freq_col] = x[freq_col] - (min_idx - i) - r.append(x) - i += d[freq_col] - continue - elif i + d[freq_col] >= max_idx: - if i < max_idx and i + d[freq_col] >= max_idx: - x = copy.deepcopy(d) - x[freq_col] = x[freq_col] - (max_idx - i) - r.append(x) - break - i += d[freq_col] - r.append(d) - return r - -def mean(data, val_col, freq_col): - nr_data = 0 - sum = 0 - for d in data: - sum += d[val_col] * d[freq_col] - nr_data += d[freq_col] - if nr_data == 0: - raise IndexError, "data is empty" - - return sum / float(nr_data) - -def mean_samples(data, xcol, ycollist): - """Create a sample list that contains - the mean of the original list. - ->>> chart_data.mean_samples([ [1, 10, 15], [2, 5, 10], [3, 8, 33] ], 0, (1, 2)) -[(1, 12.5), (2, 7.5), (3, 20.5)] -""" - out = [] - numcol = len(ycollist) - try: - for elem in data: - v = 0 - for col in ycollist: - v += elem[col] - out.append( (elem[xcol], float(v) / numcol) ) - except IndexError: - raise IndexError, "bad data: %s,xcol=%d,ycollist=%s" % (data,xcol,ycollist) - - return out - -def stddev_samples(data, xcol, ycollist, delta = 1.0): - """Create a sample list that contains the mean and standard deviation of the original list. Each element in the returned list contains following values: [MEAN, STDDEV, MEAN - STDDEV*delta, MEAN + STDDEV*delta]. - ->>> chart_data.stddev_samples([ [1, 10, 15, 12, 15], [2, 5, 10, 5, 10], [3, 32, 33, 35, 36], [4,16,66, 67, 68] ], 0, range(1,5)) -[(1, 13.0, 2.1213203435596424, 10.878679656440358, 15.121320343559642), (2, 7.5, 2.5, 5.0, 10.0), (3, 34.0, 1.5811388300841898, 32.418861169915807, 35.581138830084193), (4, 54.25, 22.094965489902897, 32.155034510097103, 76.344965489902904)] -""" - out = [] - numcol = len(ycollist) - try: - for elem in data: - total = 0 - for col in ycollist: - total += elem[col] - mean = float(total) / numcol - variance = 0 - for col in ycollist: - variance += (mean - elem[col]) ** 2 - stddev = math.sqrt(variance / numcol) * delta - out.append( (elem[xcol], mean, stddev, mean-stddev, mean+stddev) ) - - - - except IndexError: - raise IndexError, "bad data: %s,xcol=%d,ycollist=%s" % (data,xcol,ycollist) - return out - -def nearest_match(data, col, val): - min_delta = None - match = None - - for d in data: - if min_delta == None or abs(d[col] - val) < min_delta: - min_delta = abs(d[col] - val) - match = d - pychart_util.warn("XXX ", match) - return match diff --git a/bin/pychart/chart_object.py b/bin/pychart/chart_object.py deleted file mode 100644 index f23fbadd0c0..00000000000 --- a/bin/pychart/chart_object.py +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import pychart_types -import types - -def _check_attr_types(obj, keys): - for attr in obj.__dict__.keys(): - if not keys.has_key(attr): - raise Exception, "%s: unknown attribute '%s'" % (obj, attr) - - typeval, default_value, docstring = keys[attr][0:3] - val = getattr(obj, attr) - if val == None or typeval == pychart_types.AnyType: - pass - elif isinstance(typeval, types.FunctionType): - # user-defined check procedure - error = apply(typeval, (val,)) - if error != None: - raise Exception, "%s: %s for attribute '%s', but got '%s'" % (obj, error, attr, val) - elif 1: - try: - if isinstance(val, typeval): - pass - except: - raise Exception, "%s: Expecting type %s, but got %s (attr=%s, %s)" % (obj, typeval, val, attr, keys[attr]) - - else: - raise Exception, "%s: attribute '%s' expects type %s but found %s" % (obj, attr, typeval, val) - -def set_defaults(cls, **dict): - validAttrs = getattr(cls, "keys") - for attr, val in dict.items(): - if not validAttrs.has_key(attr): - raise Exception, "%s: unknown attribute %s." % (cls, attr) - tuple = list(validAttrs[attr]) - # 0 : type - # 1: defaultValue - # 2: document - # 3: defaultValue document (optional) - tuple[1] = val - validAttrs[attr] = tuple - -class T(object): - def init(self, args): - keys = self.keys - for attr, tuple in keys.items(): - defaultVal = tuple[1] - if isinstance(defaultVal, types.FunctionType): - # if the value is procedure, use the result of the proc call - # as the default value - defaultVal = apply(defaultVal, ()) - setattr(self, attr, defaultVal) - - for key, val in args.items(): - setattr(self, key, val) - _check_attr_types(self, keys) - - def __init__(self, **args): - self.init(args) - - def type_check(self): - _check_attr_types(self, self.keys) diff --git a/bin/pychart/color.py b/bin/pychart/color.py deleted file mode 100644 index 3e1066d319d..00000000000 --- a/bin/pychart/color.py +++ /dev/null @@ -1,611 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import chart_object -import object_set -import types -import theme -import pychart_util -import color_doc -from pychart_types import * - -blackify_colors = 1 - -class T(chart_object.T): - __doc__ = color_doc.doc - keys={ - "r" : (NumType, 0.0, "The intensity of red. The value is between 0 and 1."), - "g" : (NumType, 0.0, "The intensity of green. The value is between 0 and 1."), - "b" : (NumType, 0.0, "The intensity of blue. The value is between 0 and 1.") - } -##AUTOMATICALLY GENERATED -##END AUTOMATICALLY GENERATED - def __str__(self): - s = name_table().lookup(self) - if s: - return s - if self.r == self.g and self.r == self.b: - return "" % self.r - else: - return "" % (self.r, self.g, self.b) - -standards = object_set.T() -def _intern(r,g,b): - c = T(r = r, g = g, b = b) - standards.add(c) - return c - -def gray_scale(g): - """Generate a grayscale color.T object. G=0 produces white, G=1.0 - produces black.""" - return T(r=g,g=g,b=g) - -# generated automatically from rgb.txt - -snow = _intern(r=1.000000,g=0.980392,b=0.980392) -ghostwhite = _intern(r=0.972549,g=0.972549,b=1.000000) -whitesmoke = _intern(r=0.960784,g=0.960784,b=0.960784) -gainsboro = _intern(r=0.862745,g=0.862745,b=0.862745) -floralwhite = _intern(r=1.000000,g=0.980392,b=0.941176) -oldlace = _intern(r=0.992157,g=0.960784,b=0.901961) -linen = _intern(r=0.980392,g=0.941176,b=0.901961) -antiquewhite = _intern(r=0.980392,g=0.921569,b=0.843137) -papayawhip = _intern(r=1.000000,g=0.937255,b=0.835294) -blanchedalmond = _intern(r=1.000000,g=0.921569,b=0.803922) -bisque = _intern(r=1.000000,g=0.894118,b=0.768627) -peachpuff = _intern(r=1.000000,g=0.854902,b=0.725490) -navajowhite = _intern(r=1.000000,g=0.870588,b=0.678431) -moccasin = _intern(r=1.000000,g=0.894118,b=0.709804) -cornsilk = _intern(r=1.000000,g=0.972549,b=0.862745) -ivory = _intern(r=1.000000,g=1.000000,b=0.941176) -lemonchiffon = _intern(r=1.000000,g=0.980392,b=0.803922) -seashell = _intern(r=1.000000,g=0.960784,b=0.933333) -honeydew = _intern(r=0.941176,g=1.000000,b=0.941176) -mintcream = _intern(r=0.960784,g=1.000000,b=0.980392) -azure = _intern(r=0.941176,g=1.000000,b=1.000000) -aliceblue = _intern(r=0.941176,g=0.972549,b=1.000000) -lavender = _intern(r=0.901961,g=0.901961,b=0.980392) -lavenderblush = _intern(r=1.000000,g=0.941176,b=0.960784) -mistyrose = _intern(r=1.000000,g=0.894118,b=0.882353) -white = _intern(r=1.000000,g=1.000000,b=1.000000) -black = _intern(r=0.000000,g=0.000000,b=0.000000) -darkslategray = _intern(r=0.184314,g=0.309804,b=0.309804) -dimgray = _intern(r=0.411765,g=0.411765,b=0.411765) -slategray = _intern(r=0.439216,g=0.501961,b=0.564706) -lightslategray = _intern(r=0.466667,g=0.533333,b=0.600000) -gray = _intern(r=0.745098,g=0.745098,b=0.745098) -lightgray = _intern(r=0.827451,g=0.827451,b=0.827451) -midnightblue = _intern(r=0.098039,g=0.098039,b=0.439216) -navy = _intern(r=0.000000,g=0.000000,b=0.501961) -navyblue = _intern(r=0.000000,g=0.000000,b=0.501961) -cornflowerblue = _intern(r=0.392157,g=0.584314,b=0.929412) -darkslateblue = _intern(r=0.282353,g=0.239216,b=0.545098) -slateblue = _intern(r=0.415686,g=0.352941,b=0.803922) -mediumslateblue = _intern(r=0.482353,g=0.407843,b=0.933333) -lightslateblue = _intern(r=0.517647,g=0.439216,b=1.000000) -mediumblue = _intern(r=0.000000,g=0.000000,b=0.803922) -royalblue = _intern(r=0.254902,g=0.411765,b=0.882353) -blue = _intern(r=0.000000,g=0.000000,b=1.000000) -dodgerblue = _intern(r=0.117647,g=0.564706,b=1.000000) -deepskyblue = _intern(r=0.000000,g=0.749020,b=1.000000) -skyblue = _intern(r=0.529412,g=0.807843,b=0.921569) -lightskyblue = _intern(r=0.529412,g=0.807843,b=0.980392) -steelblue = _intern(r=0.274510,g=0.509804,b=0.705882) -lightsteelblue = _intern(r=0.690196,g=0.768627,b=0.870588) -lightblue = _intern(r=0.678431,g=0.847059,b=0.901961) -powderblue = _intern(r=0.690196,g=0.878431,b=0.901961) -paleturquoise = _intern(r=0.686275,g=0.933333,b=0.933333) -darkturquoise = _intern(r=0.000000,g=0.807843,b=0.819608) -mediumturquoise = _intern(r=0.282353,g=0.819608,b=0.800000) -turquoise = _intern(r=0.250980,g=0.878431,b=0.815686) -cyan = _intern(r=0.000000,g=1.000000,b=1.000000) -lightcyan = _intern(r=0.878431,g=1.000000,b=1.000000) -cadetblue = _intern(r=0.372549,g=0.619608,b=0.627451) -mediumaquamarine = _intern(r=0.400000,g=0.803922,b=0.666667) -aquamarine = _intern(r=0.498039,g=1.000000,b=0.831373) -darkgreen = _intern(r=0.000000,g=0.392157,b=0.000000) -darkolivegreen = _intern(r=0.333333,g=0.419608,b=0.184314) -darkseagreen = _intern(r=0.560784,g=0.737255,b=0.560784) -seagreen = _intern(r=0.180392,g=0.545098,b=0.341176) -mediumseagreen = _intern(r=0.235294,g=0.701961,b=0.443137) -lightseagreen = _intern(r=0.125490,g=0.698039,b=0.666667) -palegreen = _intern(r=0.596078,g=0.984314,b=0.596078) -springgreen = _intern(r=0.000000,g=1.000000,b=0.498039) -lawngreen = _intern(r=0.486275,g=0.988235,b=0.000000) -green = _intern(r=0.000000,g=1.000000,b=0.000000) -chartreuse = _intern(r=0.498039,g=1.000000,b=0.000000) -mediumspringgreen = _intern(r=0.000000,g=0.980392,b=0.603922) -greenyellow = _intern(r=0.678431,g=1.000000,b=0.184314) -limegreen = _intern(r=0.196078,g=0.803922,b=0.196078) -yellowgreen = _intern(r=0.603922,g=0.803922,b=0.196078) -forestgreen = _intern(r=0.133333,g=0.545098,b=0.133333) -olivedrab = _intern(r=0.419608,g=0.556863,b=0.137255) -darkkhaki = _intern(r=0.741176,g=0.717647,b=0.419608) -khaki = _intern(r=0.941176,g=0.901961,b=0.549020) -palegoldenrod = _intern(r=0.933333,g=0.909804,b=0.666667) -lightgoldenrodyellow = _intern(r=0.980392,g=0.980392,b=0.823529) -lightyellow = _intern(r=1.000000,g=1.000000,b=0.878431) -yellow = _intern(r=1.000000,g=1.000000,b=0.000000) -gold = _intern(r=1.000000,g=0.843137,b=0.000000) -lightgoldenrod = _intern(r=0.933333,g=0.866667,b=0.509804) -goldenrod = _intern(r=0.854902,g=0.647059,b=0.125490) -darkgoldenrod = _intern(r=0.721569,g=0.525490,b=0.043137) -rosybrown = _intern(r=0.737255,g=0.560784,b=0.560784) -indianred = _intern(r=0.803922,g=0.360784,b=0.360784) -saddlebrown = _intern(r=0.545098,g=0.270588,b=0.074510) -sienna = _intern(r=0.627451,g=0.321569,b=0.176471) -peru = _intern(r=0.803922,g=0.521569,b=0.247059) -burlywood = _intern(r=0.870588,g=0.721569,b=0.529412) -beige = _intern(r=0.960784,g=0.960784,b=0.862745) -wheat = _intern(r=0.960784,g=0.870588,b=0.701961) -sandybrown = _intern(r=0.956863,g=0.643137,b=0.376471) -tan = _intern(r=0.823529,g=0.705882,b=0.549020) -chocolate = _intern(r=0.823529,g=0.411765,b=0.117647) -firebrick = _intern(r=0.698039,g=0.133333,b=0.133333) -brown = _intern(r=0.647059,g=0.164706,b=0.164706) -darksalmon = _intern(r=0.913725,g=0.588235,b=0.478431) -salmon = _intern(r=0.980392,g=0.501961,b=0.447059) -lightsalmon = _intern(r=1.000000,g=0.627451,b=0.478431) -orange = _intern(r=1.000000,g=0.647059,b=0.000000) -darkorange = _intern(r=1.000000,g=0.549020,b=0.000000) -coral = _intern(r=1.000000,g=0.498039,b=0.313725) -lightcoral = _intern(r=0.941176,g=0.501961,b=0.501961) -tomato = _intern(r=1.000000,g=0.388235,b=0.278431) -orangered = _intern(r=1.000000,g=0.270588,b=0.000000) -red = _intern(r=1.000000,g=0.000000,b=0.000000) -hotpink = _intern(r=1.000000,g=0.411765,b=0.705882) -deeppink = _intern(r=1.000000,g=0.078431,b=0.576471) -pink = _intern(r=1.000000,g=0.752941,b=0.796078) -lightpink = _intern(r=1.000000,g=0.713725,b=0.756863) -palevioletred = _intern(r=0.858824,g=0.439216,b=0.576471) -maroon = _intern(r=0.690196,g=0.188235,b=0.376471) -mediumvioletred = _intern(r=0.780392,g=0.082353,b=0.521569) -violetred = _intern(r=0.815686,g=0.125490,b=0.564706) -magenta = _intern(r=1.000000,g=0.000000,b=1.000000) -violet = _intern(r=0.933333,g=0.509804,b=0.933333) -plum = _intern(r=0.866667,g=0.627451,b=0.866667) -orchid = _intern(r=0.854902,g=0.439216,b=0.839216) -mediumorchid = _intern(r=0.729412,g=0.333333,b=0.827451) -darkorchid = _intern(r=0.600000,g=0.196078,b=0.800000) -darkviolet = _intern(r=0.580392,g=0.000000,b=0.827451) -blueviolet = _intern(r=0.541176,g=0.168627,b=0.886275) -purple = _intern(r=0.627451,g=0.125490,b=0.941176) -mediumpurple = _intern(r=0.576471,g=0.439216,b=0.858824) -thistle = _intern(r=0.847059,g=0.749020,b=0.847059) -snow1 = _intern(r=1.000000,g=0.980392,b=0.980392) -snow2 = _intern(r=0.933333,g=0.913725,b=0.913725) -snow3 = _intern(r=0.803922,g=0.788235,b=0.788235) -snow4 = _intern(r=0.545098,g=0.537255,b=0.537255) -seashell1 = _intern(r=1.000000,g=0.960784,b=0.933333) -seashell2 = _intern(r=0.933333,g=0.898039,b=0.870588) -seashell3 = _intern(r=0.803922,g=0.772549,b=0.749020) -seashell4 = _intern(r=0.545098,g=0.525490,b=0.509804) -antiquewhite1 = _intern(r=1.000000,g=0.937255,b=0.858824) -antiquewhite2 = _intern(r=0.933333,g=0.874510,b=0.800000) -antiquewhite3 = _intern(r=0.803922,g=0.752941,b=0.690196) -antiquewhite4 = _intern(r=0.545098,g=0.513725,b=0.470588) -bisque1 = _intern(r=1.000000,g=0.894118,b=0.768627) -bisque2 = _intern(r=0.933333,g=0.835294,b=0.717647) -bisque3 = _intern(r=0.803922,g=0.717647,b=0.619608) -bisque4 = _intern(r=0.545098,g=0.490196,b=0.419608) -peachpuff1 = _intern(r=1.000000,g=0.854902,b=0.725490) -peachpuff2 = _intern(r=0.933333,g=0.796078,b=0.678431) -peachpuff3 = _intern(r=0.803922,g=0.686275,b=0.584314) -peachpuff4 = _intern(r=0.545098,g=0.466667,b=0.396078) -navajowhite1 = _intern(r=1.000000,g=0.870588,b=0.678431) -navajowhite2 = _intern(r=0.933333,g=0.811765,b=0.631373) -navajowhite3 = _intern(r=0.803922,g=0.701961,b=0.545098) -navajowhite4 = _intern(r=0.545098,g=0.474510,b=0.368627) -lemonchiffon1 = _intern(r=1.000000,g=0.980392,b=0.803922) -lemonchiffon2 = _intern(r=0.933333,g=0.913725,b=0.749020) -lemonchiffon3 = _intern(r=0.803922,g=0.788235,b=0.647059) -lemonchiffon4 = _intern(r=0.545098,g=0.537255,b=0.439216) -cornsilk1 = _intern(r=1.000000,g=0.972549,b=0.862745) -cornsilk2 = _intern(r=0.933333,g=0.909804,b=0.803922) -cornsilk3 = _intern(r=0.803922,g=0.784314,b=0.694118) -cornsilk4 = _intern(r=0.545098,g=0.533333,b=0.470588) -ivory1 = _intern(r=1.000000,g=1.000000,b=0.941176) -ivory2 = _intern(r=0.933333,g=0.933333,b=0.878431) -ivory3 = _intern(r=0.803922,g=0.803922,b=0.756863) -ivory4 = _intern(r=0.545098,g=0.545098,b=0.513725) -honeydew1 = _intern(r=0.941176,g=1.000000,b=0.941176) -honeydew2 = _intern(r=0.878431,g=0.933333,b=0.878431) -honeydew3 = _intern(r=0.756863,g=0.803922,b=0.756863) -honeydew4 = _intern(r=0.513725,g=0.545098,b=0.513725) -lavenderblush1 = _intern(r=1.000000,g=0.941176,b=0.960784) -lavenderblush2 = _intern(r=0.933333,g=0.878431,b=0.898039) -lavenderblush3 = _intern(r=0.803922,g=0.756863,b=0.772549) -lavenderblush4 = _intern(r=0.545098,g=0.513725,b=0.525490) -mistyrose1 = _intern(r=1.000000,g=0.894118,b=0.882353) -mistyrose2 = _intern(r=0.933333,g=0.835294,b=0.823529) -mistyrose3 = _intern(r=0.803922,g=0.717647,b=0.709804) -mistyrose4 = _intern(r=0.545098,g=0.490196,b=0.482353) -azure1 = _intern(r=0.941176,g=1.000000,b=1.000000) -azure2 = _intern(r=0.878431,g=0.933333,b=0.933333) -azure3 = _intern(r=0.756863,g=0.803922,b=0.803922) -azure4 = _intern(r=0.513725,g=0.545098,b=0.545098) -slateblue1 = _intern(r=0.513725,g=0.435294,b=1.000000) -slateblue2 = _intern(r=0.478431,g=0.403922,b=0.933333) -slateblue3 = _intern(r=0.411765,g=0.349020,b=0.803922) -slateblue4 = _intern(r=0.278431,g=0.235294,b=0.545098) -royalblue1 = _intern(r=0.282353,g=0.462745,b=1.000000) -royalblue2 = _intern(r=0.262745,g=0.431373,b=0.933333) -royalblue3 = _intern(r=0.227451,g=0.372549,b=0.803922) -royalblue4 = _intern(r=0.152941,g=0.250980,b=0.545098) -blue1 = _intern(r=0.000000,g=0.000000,b=1.000000) -blue2 = _intern(r=0.000000,g=0.000000,b=0.933333) -blue3 = _intern(r=0.000000,g=0.000000,b=0.803922) -blue4 = _intern(r=0.000000,g=0.000000,b=0.545098) -dodgerblue1 = _intern(r=0.117647,g=0.564706,b=1.000000) -dodgerblue2 = _intern(r=0.109804,g=0.525490,b=0.933333) -dodgerblue3 = _intern(r=0.094118,g=0.454902,b=0.803922) -dodgerblue4 = _intern(r=0.062745,g=0.305882,b=0.545098) -steelblue1 = _intern(r=0.388235,g=0.721569,b=1.000000) -steelblue2 = _intern(r=0.360784,g=0.674510,b=0.933333) -steelblue3 = _intern(r=0.309804,g=0.580392,b=0.803922) -steelblue4 = _intern(r=0.211765,g=0.392157,b=0.545098) -deepskyblue1 = _intern(r=0.000000,g=0.749020,b=1.000000) -deepskyblue2 = _intern(r=0.000000,g=0.698039,b=0.933333) -deepskyblue3 = _intern(r=0.000000,g=0.603922,b=0.803922) -deepskyblue4 = _intern(r=0.000000,g=0.407843,b=0.545098) -skyblue1 = _intern(r=0.529412,g=0.807843,b=1.000000) -skyblue2 = _intern(r=0.494118,g=0.752941,b=0.933333) -skyblue3 = _intern(r=0.423529,g=0.650980,b=0.803922) -skyblue4 = _intern(r=0.290196,g=0.439216,b=0.545098) -lightskyblue1 = _intern(r=0.690196,g=0.886275,b=1.000000) -lightskyblue2 = _intern(r=0.643137,g=0.827451,b=0.933333) -lightskyblue3 = _intern(r=0.552941,g=0.713725,b=0.803922) -lightskyblue4 = _intern(r=0.376471,g=0.482353,b=0.545098) -slategray1 = _intern(r=0.776471,g=0.886275,b=1.000000) -slategray2 = _intern(r=0.725490,g=0.827451,b=0.933333) -slategray3 = _intern(r=0.623529,g=0.713725,b=0.803922) -slategray4 = _intern(r=0.423529,g=0.482353,b=0.545098) -lightsteelblue1 = _intern(r=0.792157,g=0.882353,b=1.000000) -lightsteelblue2 = _intern(r=0.737255,g=0.823529,b=0.933333) -lightsteelblue3 = _intern(r=0.635294,g=0.709804,b=0.803922) -lightsteelblue4 = _intern(r=0.431373,g=0.482353,b=0.545098) -lightblue1 = _intern(r=0.749020,g=0.937255,b=1.000000) -lightblue2 = _intern(r=0.698039,g=0.874510,b=0.933333) -lightblue3 = _intern(r=0.603922,g=0.752941,b=0.803922) -lightblue4 = _intern(r=0.407843,g=0.513725,b=0.545098) -lightcyan1 = _intern(r=0.878431,g=1.000000,b=1.000000) -lightcyan2 = _intern(r=0.819608,g=0.933333,b=0.933333) -lightcyan3 = _intern(r=0.705882,g=0.803922,b=0.803922) -lightcyan4 = _intern(r=0.478431,g=0.545098,b=0.545098) -paleturquoise1 = _intern(r=0.733333,g=1.000000,b=1.000000) -paleturquoise2 = _intern(r=0.682353,g=0.933333,b=0.933333) -paleturquoise3 = _intern(r=0.588235,g=0.803922,b=0.803922) -paleturquoise4 = _intern(r=0.400000,g=0.545098,b=0.545098) -cadetblue1 = _intern(r=0.596078,g=0.960784,b=1.000000) -cadetblue2 = _intern(r=0.556863,g=0.898039,b=0.933333) -cadetblue3 = _intern(r=0.478431,g=0.772549,b=0.803922) -cadetblue4 = _intern(r=0.325490,g=0.525490,b=0.545098) -turquoise1 = _intern(r=0.000000,g=0.960784,b=1.000000) -turquoise2 = _intern(r=0.000000,g=0.898039,b=0.933333) -turquoise3 = _intern(r=0.000000,g=0.772549,b=0.803922) -turquoise4 = _intern(r=0.000000,g=0.525490,b=0.545098) -cyan1 = _intern(r=0.000000,g=1.000000,b=1.000000) -cyan2 = _intern(r=0.000000,g=0.933333,b=0.933333) -cyan3 = _intern(r=0.000000,g=0.803922,b=0.803922) -cyan4 = _intern(r=0.000000,g=0.545098,b=0.545098) -darkslategray1 = _intern(r=0.592157,g=1.000000,b=1.000000) -darkslategray2 = _intern(r=0.552941,g=0.933333,b=0.933333) -darkslategray3 = _intern(r=0.474510,g=0.803922,b=0.803922) -darkslategray4 = _intern(r=0.321569,g=0.545098,b=0.545098) -aquamarine1 = _intern(r=0.498039,g=1.000000,b=0.831373) -aquamarine2 = _intern(r=0.462745,g=0.933333,b=0.776471) -aquamarine3 = _intern(r=0.400000,g=0.803922,b=0.666667) -aquamarine4 = _intern(r=0.270588,g=0.545098,b=0.454902) -darkseagreen1 = _intern(r=0.756863,g=1.000000,b=0.756863) -darkseagreen2 = _intern(r=0.705882,g=0.933333,b=0.705882) -darkseagreen3 = _intern(r=0.607843,g=0.803922,b=0.607843) -darkseagreen4 = _intern(r=0.411765,g=0.545098,b=0.411765) -seagreen1 = _intern(r=0.329412,g=1.000000,b=0.623529) -seagreen2 = _intern(r=0.305882,g=0.933333,b=0.580392) -seagreen3 = _intern(r=0.262745,g=0.803922,b=0.501961) -seagreen4 = _intern(r=0.180392,g=0.545098,b=0.341176) -palegreen1 = _intern(r=0.603922,g=1.000000,b=0.603922) -palegreen2 = _intern(r=0.564706,g=0.933333,b=0.564706) -palegreen3 = _intern(r=0.486275,g=0.803922,b=0.486275) -palegreen4 = _intern(r=0.329412,g=0.545098,b=0.329412) -springgreen1 = _intern(r=0.000000,g=1.000000,b=0.498039) -springgreen2 = _intern(r=0.000000,g=0.933333,b=0.462745) -springgreen3 = _intern(r=0.000000,g=0.803922,b=0.400000) -springgreen4 = _intern(r=0.000000,g=0.545098,b=0.270588) -green1 = _intern(r=0.000000,g=1.000000,b=0.000000) -green2 = _intern(r=0.000000,g=0.933333,b=0.000000) -green3 = _intern(r=0.000000,g=0.803922,b=0.000000) -green4 = _intern(r=0.000000,g=0.545098,b=0.000000) -chartreuse1 = _intern(r=0.498039,g=1.000000,b=0.000000) -chartreuse2 = _intern(r=0.462745,g=0.933333,b=0.000000) -chartreuse3 = _intern(r=0.400000,g=0.803922,b=0.000000) -chartreuse4 = _intern(r=0.270588,g=0.545098,b=0.000000) -olivedrab1 = _intern(r=0.752941,g=1.000000,b=0.243137) -olivedrab2 = _intern(r=0.701961,g=0.933333,b=0.227451) -olivedrab3 = _intern(r=0.603922,g=0.803922,b=0.196078) -olivedrab4 = _intern(r=0.411765,g=0.545098,b=0.133333) -darkolivegreen1 = _intern(r=0.792157,g=1.000000,b=0.439216) -darkolivegreen2 = _intern(r=0.737255,g=0.933333,b=0.407843) -darkolivegreen3 = _intern(r=0.635294,g=0.803922,b=0.352941) -darkolivegreen4 = _intern(r=0.431373,g=0.545098,b=0.239216) -khaki1 = _intern(r=1.000000,g=0.964706,b=0.560784) -khaki2 = _intern(r=0.933333,g=0.901961,b=0.521569) -khaki3 = _intern(r=0.803922,g=0.776471,b=0.450980) -khaki4 = _intern(r=0.545098,g=0.525490,b=0.305882) -lightgoldenrod1 = _intern(r=1.000000,g=0.925490,b=0.545098) -lightgoldenrod2 = _intern(r=0.933333,g=0.862745,b=0.509804) -lightgoldenrod3 = _intern(r=0.803922,g=0.745098,b=0.439216) -lightgoldenrod4 = _intern(r=0.545098,g=0.505882,b=0.298039) -lightyellow1 = _intern(r=1.000000,g=1.000000,b=0.878431) -lightyellow2 = _intern(r=0.933333,g=0.933333,b=0.819608) -lightyellow3 = _intern(r=0.803922,g=0.803922,b=0.705882) -lightyellow4 = _intern(r=0.545098,g=0.545098,b=0.478431) -yellow1 = _intern(r=1.000000,g=1.000000,b=0.000000) -yellow2 = _intern(r=0.933333,g=0.933333,b=0.000000) -yellow3 = _intern(r=0.803922,g=0.803922,b=0.000000) -yellow4 = _intern(r=0.545098,g=0.545098,b=0.000000) -gold1 = _intern(r=1.000000,g=0.843137,b=0.000000) -gold2 = _intern(r=0.933333,g=0.788235,b=0.000000) -gold3 = _intern(r=0.803922,g=0.678431,b=0.000000) -gold4 = _intern(r=0.545098,g=0.458824,b=0.000000) -goldenrod1 = _intern(r=1.000000,g=0.756863,b=0.145098) -goldenrod2 = _intern(r=0.933333,g=0.705882,b=0.133333) -goldenrod3 = _intern(r=0.803922,g=0.607843,b=0.113725) -goldenrod4 = _intern(r=0.545098,g=0.411765,b=0.078431) -darkgoldenrod1 = _intern(r=1.000000,g=0.725490,b=0.058824) -darkgoldenrod2 = _intern(r=0.933333,g=0.678431,b=0.054902) -darkgoldenrod3 = _intern(r=0.803922,g=0.584314,b=0.047059) -darkgoldenrod4 = _intern(r=0.545098,g=0.396078,b=0.031373) -rosybrown1 = _intern(r=1.000000,g=0.756863,b=0.756863) -rosybrown2 = _intern(r=0.933333,g=0.705882,b=0.705882) -rosybrown3 = _intern(r=0.803922,g=0.607843,b=0.607843) -rosybrown4 = _intern(r=0.545098,g=0.411765,b=0.411765) -indianred1 = _intern(r=1.000000,g=0.415686,b=0.415686) -indianred2 = _intern(r=0.933333,g=0.388235,b=0.388235) -indianred3 = _intern(r=0.803922,g=0.333333,b=0.333333) -indianred4 = _intern(r=0.545098,g=0.227451,b=0.227451) -sienna1 = _intern(r=1.000000,g=0.509804,b=0.278431) -sienna2 = _intern(r=0.933333,g=0.474510,b=0.258824) -sienna3 = _intern(r=0.803922,g=0.407843,b=0.223529) -sienna4 = _intern(r=0.545098,g=0.278431,b=0.149020) -burlywood1 = _intern(r=1.000000,g=0.827451,b=0.607843) -burlywood2 = _intern(r=0.933333,g=0.772549,b=0.568627) -burlywood3 = _intern(r=0.803922,g=0.666667,b=0.490196) -burlywood4 = _intern(r=0.545098,g=0.450980,b=0.333333) -wheat1 = _intern(r=1.000000,g=0.905882,b=0.729412) -wheat2 = _intern(r=0.933333,g=0.847059,b=0.682353) -wheat3 = _intern(r=0.803922,g=0.729412,b=0.588235) -wheat4 = _intern(r=0.545098,g=0.494118,b=0.400000) -tan1 = _intern(r=1.000000,g=0.647059,b=0.309804) -tan2 = _intern(r=0.933333,g=0.603922,b=0.286275) -tan3 = _intern(r=0.803922,g=0.521569,b=0.247059) -tan4 = _intern(r=0.545098,g=0.352941,b=0.168627) -chocolate1 = _intern(r=1.000000,g=0.498039,b=0.141176) -chocolate2 = _intern(r=0.933333,g=0.462745,b=0.129412) -chocolate3 = _intern(r=0.803922,g=0.400000,b=0.113725) -chocolate4 = _intern(r=0.545098,g=0.270588,b=0.074510) -firebrick1 = _intern(r=1.000000,g=0.188235,b=0.188235) -firebrick2 = _intern(r=0.933333,g=0.172549,b=0.172549) -firebrick3 = _intern(r=0.803922,g=0.149020,b=0.149020) -firebrick4 = _intern(r=0.545098,g=0.101961,b=0.101961) -brown1 = _intern(r=1.000000,g=0.250980,b=0.250980) -brown2 = _intern(r=0.933333,g=0.231373,b=0.231373) -brown3 = _intern(r=0.803922,g=0.200000,b=0.200000) -brown4 = _intern(r=0.545098,g=0.137255,b=0.137255) -salmon1 = _intern(r=1.000000,g=0.549020,b=0.411765) -salmon2 = _intern(r=0.933333,g=0.509804,b=0.384314) -salmon3 = _intern(r=0.803922,g=0.439216,b=0.329412) -salmon4 = _intern(r=0.545098,g=0.298039,b=0.223529) -lightsalmon1 = _intern(r=1.000000,g=0.627451,b=0.478431) -lightsalmon2 = _intern(r=0.933333,g=0.584314,b=0.447059) -lightsalmon3 = _intern(r=0.803922,g=0.505882,b=0.384314) -lightsalmon4 = _intern(r=0.545098,g=0.341176,b=0.258824) -orange1 = _intern(r=1.000000,g=0.647059,b=0.000000) -orange2 = _intern(r=0.933333,g=0.603922,b=0.000000) -orange3 = _intern(r=0.803922,g=0.521569,b=0.000000) -orange4 = _intern(r=0.545098,g=0.352941,b=0.000000) -darkorange1 = _intern(r=1.000000,g=0.498039,b=0.000000) -darkorange2 = _intern(r=0.933333,g=0.462745,b=0.000000) -darkorange3 = _intern(r=0.803922,g=0.400000,b=0.000000) -darkorange4 = _intern(r=0.545098,g=0.270588,b=0.000000) -coral1 = _intern(r=1.000000,g=0.447059,b=0.337255) -coral2 = _intern(r=0.933333,g=0.415686,b=0.313725) -coral3 = _intern(r=0.803922,g=0.356863,b=0.270588) -coral4 = _intern(r=0.545098,g=0.243137,b=0.184314) -tomato1 = _intern(r=1.000000,g=0.388235,b=0.278431) -tomato2 = _intern(r=0.933333,g=0.360784,b=0.258824) -tomato3 = _intern(r=0.803922,g=0.309804,b=0.223529) -tomato4 = _intern(r=0.545098,g=0.211765,b=0.149020) -orangered1 = _intern(r=1.000000,g=0.270588,b=0.000000) -orangered2 = _intern(r=0.933333,g=0.250980,b=0.000000) -orangered3 = _intern(r=0.803922,g=0.215686,b=0.000000) -orangered4 = _intern(r=0.545098,g=0.145098,b=0.000000) -red1 = _intern(r=1.000000,g=0.000000,b=0.000000) -red2 = _intern(r=0.933333,g=0.000000,b=0.000000) -red3 = _intern(r=0.803922,g=0.000000,b=0.000000) -red4 = _intern(r=0.545098,g=0.000000,b=0.000000) -deeppink1 = _intern(r=1.000000,g=0.078431,b=0.576471) -deeppink2 = _intern(r=0.933333,g=0.070588,b=0.537255) -deeppink3 = _intern(r=0.803922,g=0.062745,b=0.462745) -deeppink4 = _intern(r=0.545098,g=0.039216,b=0.313725) -hotpink1 = _intern(r=1.000000,g=0.431373,b=0.705882) -hotpink2 = _intern(r=0.933333,g=0.415686,b=0.654902) -hotpink3 = _intern(r=0.803922,g=0.376471,b=0.564706) -hotpink4 = _intern(r=0.545098,g=0.227451,b=0.384314) -pink1 = _intern(r=1.000000,g=0.709804,b=0.772549) -pink2 = _intern(r=0.933333,g=0.662745,b=0.721569) -pink3 = _intern(r=0.803922,g=0.568627,b=0.619608) -pink4 = _intern(r=0.545098,g=0.388235,b=0.423529) -lightpink1 = _intern(r=1.000000,g=0.682353,b=0.725490) -lightpink2 = _intern(r=0.933333,g=0.635294,b=0.678431) -lightpink3 = _intern(r=0.803922,g=0.549020,b=0.584314) -lightpink4 = _intern(r=0.545098,g=0.372549,b=0.396078) -palevioletred1 = _intern(r=1.000000,g=0.509804,b=0.670588) -palevioletred2 = _intern(r=0.933333,g=0.474510,b=0.623529) -palevioletred3 = _intern(r=0.803922,g=0.407843,b=0.537255) -palevioletred4 = _intern(r=0.545098,g=0.278431,b=0.364706) -maroon1 = _intern(r=1.000000,g=0.203922,b=0.701961) -maroon2 = _intern(r=0.933333,g=0.188235,b=0.654902) -maroon3 = _intern(r=0.803922,g=0.160784,b=0.564706) -maroon4 = _intern(r=0.545098,g=0.109804,b=0.384314) -violetred1 = _intern(r=1.000000,g=0.243137,b=0.588235) -violetred2 = _intern(r=0.933333,g=0.227451,b=0.549020) -violetred3 = _intern(r=0.803922,g=0.196078,b=0.470588) -violetred4 = _intern(r=0.545098,g=0.133333,b=0.321569) -magenta1 = _intern(r=1.000000,g=0.000000,b=1.000000) -magenta2 = _intern(r=0.933333,g=0.000000,b=0.933333) -magenta3 = _intern(r=0.803922,g=0.000000,b=0.803922) -magenta4 = _intern(r=0.545098,g=0.000000,b=0.545098) -orchid1 = _intern(r=1.000000,g=0.513725,b=0.980392) -orchid2 = _intern(r=0.933333,g=0.478431,b=0.913725) -orchid3 = _intern(r=0.803922,g=0.411765,b=0.788235) -orchid4 = _intern(r=0.545098,g=0.278431,b=0.537255) -plum1 = _intern(r=1.000000,g=0.733333,b=1.000000) -plum2 = _intern(r=0.933333,g=0.682353,b=0.933333) -plum3 = _intern(r=0.803922,g=0.588235,b=0.803922) -plum4 = _intern(r=0.545098,g=0.400000,b=0.545098) -mediumorchid1 = _intern(r=0.878431,g=0.400000,b=1.000000) -mediumorchid2 = _intern(r=0.819608,g=0.372549,b=0.933333) -mediumorchid3 = _intern(r=0.705882,g=0.321569,b=0.803922) -mediumorchid4 = _intern(r=0.478431,g=0.215686,b=0.545098) -darkorchid1 = _intern(r=0.749020,g=0.243137,b=1.000000) -darkorchid2 = _intern(r=0.698039,g=0.227451,b=0.933333) -darkorchid3 = _intern(r=0.603922,g=0.196078,b=0.803922) -darkorchid4 = _intern(r=0.407843,g=0.133333,b=0.545098) -purple1 = _intern(r=0.607843,g=0.188235,b=1.000000) -purple2 = _intern(r=0.568627,g=0.172549,b=0.933333) -purple3 = _intern(r=0.490196,g=0.149020,b=0.803922) -purple4 = _intern(r=0.333333,g=0.101961,b=0.545098) -mediumpurple1 = _intern(r=0.670588,g=0.509804,b=1.000000) -mediumpurple2 = _intern(r=0.623529,g=0.474510,b=0.933333) -mediumpurple3 = _intern(r=0.537255,g=0.407843,b=0.803922) -mediumpurple4 = _intern(r=0.364706,g=0.278431,b=0.545098) -thistle1 = _intern(r=1.000000,g=0.882353,b=1.000000) -thistle2 = _intern(r=0.933333,g=0.823529,b=0.933333) -thistle3 = _intern(r=0.803922,g=0.709804,b=0.803922) -thistle4 = _intern(r=0.545098,g=0.482353,b=0.545098) -gray00 = _intern(r=0.000000,g=0.000000,b=0.000000) -gray01 = _intern(r=0.011765,g=0.011765,b=0.011765) -gray02 = _intern(r=0.019608,g=0.019608,b=0.019608) -gray03 = _intern(r=0.031373,g=0.031373,b=0.031373) -gray04 = _intern(r=0.039216,g=0.039216,b=0.039216) -gray05 = _intern(r=0.050980,g=0.050980,b=0.050980) -gray06 = _intern(r=0.058824,g=0.058824,b=0.058824) -gray07 = _intern(r=0.070588,g=0.070588,b=0.070588) -gray08 = _intern(r=0.078431,g=0.078431,b=0.078431) -gray09 = _intern(r=0.090196,g=0.090196,b=0.090196) -gray10 = _intern(r=0.101961,g=0.101961,b=0.101961) -gray11 = _intern(r=0.109804,g=0.109804,b=0.109804) -gray12 = _intern(r=0.121569,g=0.121569,b=0.121569) -gray13 = _intern(r=0.129412,g=0.129412,b=0.129412) -gray14 = _intern(r=0.141176,g=0.141176,b=0.141176) -gray15 = _intern(r=0.149020,g=0.149020,b=0.149020) -gray16 = _intern(r=0.160784,g=0.160784,b=0.160784) -gray17 = _intern(r=0.168627,g=0.168627,b=0.168627) -gray18 = _intern(r=0.180392,g=0.180392,b=0.180392) -gray19 = _intern(r=0.188235,g=0.188235,b=0.188235) -gray20 = _intern(r=0.200000,g=0.200000,b=0.200000) -gray21 = _intern(r=0.211765,g=0.211765,b=0.211765) -gray22 = _intern(r=0.219608,g=0.219608,b=0.219608) -gray23 = _intern(r=0.231373,g=0.231373,b=0.231373) -gray24 = _intern(r=0.239216,g=0.239216,b=0.239216) -gray25 = _intern(r=0.250980,g=0.250980,b=0.250980) -gray26 = _intern(r=0.258824,g=0.258824,b=0.258824) -gray27 = _intern(r=0.270588,g=0.270588,b=0.270588) -gray28 = _intern(r=0.278431,g=0.278431,b=0.278431) -gray29 = _intern(r=0.290196,g=0.290196,b=0.290196) -gray30 = _intern(r=0.301961,g=0.301961,b=0.301961) -gray31 = _intern(r=0.309804,g=0.309804,b=0.309804) -gray32 = _intern(r=0.321569,g=0.321569,b=0.321569) -gray33 = _intern(r=0.329412,g=0.329412,b=0.329412) -gray34 = _intern(r=0.341176,g=0.341176,b=0.341176) -gray35 = _intern(r=0.349020,g=0.349020,b=0.349020) -gray36 = _intern(r=0.360784,g=0.360784,b=0.360784) -gray37 = _intern(r=0.368627,g=0.368627,b=0.368627) -gray38 = _intern(r=0.380392,g=0.380392,b=0.380392) -gray39 = _intern(r=0.388235,g=0.388235,b=0.388235) -gray40 = _intern(r=0.400000,g=0.400000,b=0.400000) -gray41 = _intern(r=0.411765,g=0.411765,b=0.411765) -gray42 = _intern(r=0.419608,g=0.419608,b=0.419608) -gray43 = _intern(r=0.431373,g=0.431373,b=0.431373) -gray44 = _intern(r=0.439216,g=0.439216,b=0.439216) -gray45 = _intern(r=0.450980,g=0.450980,b=0.450980) -gray46 = _intern(r=0.458824,g=0.458824,b=0.458824) -gray47 = _intern(r=0.470588,g=0.470588,b=0.470588) -gray48 = _intern(r=0.478431,g=0.478431,b=0.478431) -gray49 = _intern(r=0.490196,g=0.490196,b=0.490196) -gray50 = _intern(r=0.498039,g=0.498039,b=0.498039) -gray51 = _intern(r=0.509804,g=0.509804,b=0.509804) -gray52 = _intern(r=0.521569,g=0.521569,b=0.521569) -gray53 = _intern(r=0.529412,g=0.529412,b=0.529412) -gray54 = _intern(r=0.541176,g=0.541176,b=0.541176) -gray55 = _intern(r=0.549020,g=0.549020,b=0.549020) -gray56 = _intern(r=0.560784,g=0.560784,b=0.560784) -gray57 = _intern(r=0.568627,g=0.568627,b=0.568627) -gray58 = _intern(r=0.580392,g=0.580392,b=0.580392) -gray59 = _intern(r=0.588235,g=0.588235,b=0.588235) -gray60 = _intern(r=0.600000,g=0.600000,b=0.600000) -gray61 = _intern(r=0.611765,g=0.611765,b=0.611765) -gray62 = _intern(r=0.619608,g=0.619608,b=0.619608) -gray63 = _intern(r=0.631373,g=0.631373,b=0.631373) -gray64 = _intern(r=0.639216,g=0.639216,b=0.639216) -gray65 = _intern(r=0.650980,g=0.650980,b=0.650980) -gray66 = _intern(r=0.658824,g=0.658824,b=0.658824) -gray67 = _intern(r=0.670588,g=0.670588,b=0.670588) -gray68 = _intern(r=0.678431,g=0.678431,b=0.678431) -gray69 = _intern(r=0.690196,g=0.690196,b=0.690196) -gray70 = _intern(r=0.701961,g=0.701961,b=0.701961) -gray71 = _intern(r=0.709804,g=0.709804,b=0.709804) -gray72 = _intern(r=0.721569,g=0.721569,b=0.721569) -gray73 = _intern(r=0.729412,g=0.729412,b=0.729412) -gray74 = _intern(r=0.741176,g=0.741176,b=0.741176) -gray75 = _intern(r=0.749020,g=0.749020,b=0.749020) -gray76 = _intern(r=0.760784,g=0.760784,b=0.760784) -gray77 = _intern(r=0.768627,g=0.768627,b=0.768627) -gray78 = _intern(r=0.780392,g=0.780392,b=0.780392) -gray79 = _intern(r=0.788235,g=0.788235,b=0.788235) -gray80 = _intern(r=0.800000,g=0.800000,b=0.800000) -gray81 = _intern(r=0.811765,g=0.811765,b=0.811765) -gray82 = _intern(r=0.819608,g=0.819608,b=0.819608) -gray83 = _intern(r=0.831373,g=0.831373,b=0.831373) -gray84 = _intern(r=0.839216,g=0.839216,b=0.839216) -gray85 = _intern(r=0.850980,g=0.850980,b=0.850980) -gray86 = _intern(r=0.858824,g=0.858824,b=0.858824) -gray87 = _intern(r=0.870588,g=0.870588,b=0.870588) -gray88 = _intern(r=0.878431,g=0.878431,b=0.878431) -gray89 = _intern(r=0.890196,g=0.890196,b=0.890196) -gray90 = _intern(r=0.898039,g=0.898039,b=0.898039) -gray91 = _intern(r=0.909804,g=0.909804,b=0.909804) -gray92 = _intern(r=0.921569,g=0.921569,b=0.921569) -gray93 = _intern(r=0.929412,g=0.929412,b=0.929412) -gray94 = _intern(r=0.941176,g=0.941176,b=0.941176) -gray95 = _intern(r=0.949020,g=0.949020,b=0.949020) -gray96 = _intern(r=0.960784,g=0.960784,b=0.960784) -gray97 = _intern(r=0.968627,g=0.968627,b=0.968627) -gray98 = _intern(r=0.980392,g=0.980392,b=0.980392) -gray99 = _intern(r=0.988235,g=0.988235,b=0.988235) -gray100 = _intern(r=1.000000,g=1.000000,b=1.000000) -darkgray = _intern(r=0.662745,g=0.662745,b=0.662745) -darkblue = _intern(r=0.000000,g=0.000000,b=0.545098) -darkcyan = _intern(r=0.000000,g=0.545098,b=0.545098) -darkmagenta = _intern(r=0.545098,g=0.000000,b=0.545098) -darkred = _intern(r=0.545098,g=0.000000,b=0.000000) -lightgreen = _intern(r=0.564706,g=0.933333,b=0.564706) -default = black - -_name_table = None -def name_table(): - global _name_table - if not _name_table: - _name_table = pychart_util.symbol_lookup_table(globals(), standards) - return _name_table diff --git a/bin/pychart/color_doc.py b/bin/pychart/color_doc.py deleted file mode 100644 index 65fa6446a16..00000000000 --- a/bin/pychart/color_doc.py +++ /dev/null @@ -1,7 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -r(type:number) default="The intensity of red. The value is between 0 and 1.". -b(type:number) default="The intensity of blue. The value is between 0 and 1.". -g(type:number) default="The intensity of green. The value is between 0 and 1.". -""" - diff --git a/bin/pychart/coord.py b/bin/pychart/coord.py deleted file mode 100644 index 3c911d2a42a..00000000000 --- a/bin/pychart/coord.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -class T(object): - def get_canvas_pos(self, size, val, min, max): - """ - Compute the screen location at which a sample value would be drawn. - ``size`` is the width or height of the chart, in points. - ``val`` is the sample value. - ``min`` and ``max`` are the minimum and maximum sample values that - are to be displayed over the length of ``size``. - - For example, suppose the width of a chart is 200 points and the - minimum and maximum X values in the sample data are 100 and 150 - respectively. When Pychart wants to draw a sample point at the X - value of 120, it will call - area.T.x_coord.get_canvas_pos(size = 200, val = 120, min = 100, max = 150). - """ - raise Exception - - def get_tics(self, min, max, interval): - """Generate the list of places for drawing tick marks.""" - raise Exception - - def get_min_max(self, min, max, interval): - """Compute the min/max values to be displayed in the chart. - Parameters ``min`` and ``max`` are the minimum and maximum values - of the sample data passed to the plots. Parameter ``interval`` is - the value of attribute area.T.x_grid_interval (or y_grid_interval). - It is None if these attributes are non-specified. - - This method must return tuple (dmin, dmax, dinterval). - dmin should be ``min`` rounded down to some good number. - dmax should be ``max`` rounded up to some good number. - dinterval should be ``interval`` if it is non-None. Otherwise, the - method must compute some good value. - """ - - raise Exception diff --git a/bin/pychart/doc_support.py b/bin/pychart/doc_support.py deleted file mode 100644 index 32537a7437c..00000000000 --- a/bin/pychart/doc_support.py +++ /dev/null @@ -1,122 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import string -import sys -import re -import os.path -from pychart import * -from types import * -from pychart.pychart_types import * - -oldstdout = sys.stdout -if os.path.exists("/dev/null"): - sys.stdout = open("/dev/null", "w") - -modules = {} -values = [] - -sys.stdout = oldstdout -g = globals() -for mod in g.keys(): - val = g[mod] - if type(val) == ModuleType: - dic = {} - for name in val.__dict__.keys(): - v = val.__dict__[name] - if name[0] != '_': - values.append((v, mod + "." + name)) - if type(v) == type and issubclass(v, chart_object.T): - dic[name] = v - modules[mod] = dic - -def stringify_type(t): - s = str(t) - if t == AnyType: - return "any" - if t == ShadowType: - return "(xoff,yoff,fill)" - elif re.search("NumType", s): - return "number" - elif re.search("UnitType", s): - return "length in points (\\\\xref{unit})" - elif re.search("CoordType", s): - return "(x,y)" - elif re.search("CoordSystemType", s): - return "['linear'|'log'|'category']" - elif re.search("CoordOrNoneType", s): - return "(x,y) or None" - elif re.search("TextAlignType", s): - return "['R'|'L'|'C'|None]" - elif re.search("FormatType", s): - return "printf format string" - elif re.search("IntervalType", s): - return "Number or function" - - mo = re.match("", s) - if mo: - return mo.group(1) - mo = re.match("", s) - if mo: - return mo.group(1) - mo = re.match("", s) - if mo: - return mo.group(1) - mo = re.match("pychart\\.(.*)", s) - if mo: - return mo.group(1) - return s - -def stringify_value(val): - t = type(val) - if t == StringType: - return '"' + val + '"' - if t == bool: - if val: return "True" - else: return "False" - - if t in (IntType, LongType, FloatType): - return str(val) - if val == None: - return "None" - if type(val) == ListType: - return map(stringify_value, val) - for pair in values: - if pair[0] == val: - return pair[1] - return str(val) - -def break_string(name): - max_len = 10 - if len(name) < max_len: - return name - - name = re.sub("(\\d\\d)([^\\d])", "\\1-\n\\2", name) - name = re.sub("black(.)", "black-\n\\1", name) - - elems = string.split(name, "\n") - while 1: - broken = 0 - for i in range(len(elems)): - elem = elems[i] - if len(elem) < max_len: - continue - broken = 1 - elem1 = elem[0:len(elem)/2] - elem2 = elem[len(elem)/2:] - elems[i:i+1] = [elem1, elem2] - break - if not broken: - break - name = "\n".join(elems) - return name diff --git a/bin/pychart/empty_docs.py b/bin/pychart/empty_docs.py deleted file mode 100644 index 20408906da0..00000000000 --- a/bin/pychart/empty_docs.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# - -def generate_doc(name, suffix="", append = 0): - if append: - fp = open(name + "_doc.py", "a+") - else: - fp = open(name + "_doc.py", "w") - fp.write("# automatically generated by generate_docs.py.\n") - fp.write("doc" + suffix + "=\" \"\n") - fp.close() - -generate_doc( "area") -generate_doc( "arrow") -generate_doc( "axis", "_x") -generate_doc( "axis", "_y", 1) -generate_doc( "bar_plot") -generate_doc( "color") -generate_doc( "error_bar","_1") -generate_doc( "error_bar", "_2", 1) -generate_doc( "error_bar", "_3", 1) -generate_doc( "error_bar", "_4", 1) -generate_doc( "error_bar", "_5", 1) -generate_doc( "error_bar", "_6", 1) -generate_doc( "fill_style") -generate_doc("line_plot") -generate_doc("pie_plot") -generate_doc("text_box") -generate_doc("range_plot") -generate_doc("legend") -generate_doc("legend", "_entry", 1) -generate_doc("line_style") -generate_doc("tick_mark") diff --git a/bin/pychart/error_bar.py b/bin/pychart/error_bar.py deleted file mode 100644 index 6abecffdb19..00000000000 --- a/bin/pychart/error_bar.py +++ /dev/null @@ -1,159 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import tick_mark -import line_style -import pychart_util -import chart_object -import fill_style -import types -import error_bar_doc -import object_set -from pychart_types import * - -__doc__ = """Pychart offers several styles of error bars. Some of them -only displays the min/max confidence interval, while others can display -quartiles in addition to min/max.""" - -class T(chart_object.T): - keys = {} -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - pass - -# Two horizontal lines at min & max locations. -class error_bar1(T): - __doc__ = error_bar_doc.doc_1 - keys = {"tic_len" : (UnitType, 10, "Length of the horizontal bars"), - "line_style": (line_style.T, line_style.default, "") - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin = None, qmax = None): - x = loc[0] - y = min - can.line(self.line_style, x-self.tic_len/2.0, y, x+self.tic_len/2.0, y) - y = max - can.line(self.line_style, x-self.tic_len/2.0, y, x+self.tic_len/2.0, y) - -class error_bar2(T): - __doc__ = error_bar_doc.doc_2 - keys = {"tic_len" : (UnitType, 3, - "The length of the horizontal bars"), - "hline_style": (line_style.T, line_style.default, - "The style of the horizontal bars."), - "vline_style": (line_style.T, None, - "The style of the vertical bar.") - } - -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin = None, qmax = None): - vline_style = self.vline_style - if not vline_style: - vline_style = self.hline_style - x = loc[0] - y1 = min - can.line(self.hline_style, x-self.tic_len/2.0, y1, x+self.tic_len/2.0, y1) - y2 = max - can.line(self.hline_style, x-self.tic_len/2.0, y2, x+self.tic_len/2.0, y2) - can.line(vline_style, x, y1, x, y2) - -class error_bar3(T): - # Tufte style - __doc__ = "This style is endorsed by the Tufte's books. " \ - + error_bar_doc.doc_3 - keys = { "line_style": (line_style.T, line_style.default, "") - } - -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin, qmax): - x = loc[0] - can.line(self.line_style, x, min, x, qmin) - can.line(self.line_style, x, qmax, x, max) - -class error_bar4(T): - __doc__ = error_bar_doc.doc_4 - keys = { "line_style": (line_style.T, line_style.default, ""), - "fill_style": (fill_style.T, fill_style.gray70, ""), - "box_width": (UnitType, 4, ""), - "tic_len": (UnitType, 4, "") - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin, qmax): - x = loc[0] - style = self.line_style - y1 = min - can.line(style, x-self.tic_len/2.0, y1, x+self.tic_len/2.0, y1) - y2 = max - can.line(style, x-self.tic_len/2.0, y2, x+self.tic_len/2.0, y2) - can.line(style, x, y1, x, y2) - - can.rectangle(style, self.fill_style, - x-self.box_width/2.0, qmin, - x+self.box_width/2.0, qmax) - -# vertical line -class error_bar5(T): - __doc__ = error_bar_doc.doc_5 - keys = { "line_style": (line_style.T, line_style.default, "") - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin = None, qmax = None): - x = loc[0] - y = loc[1] - - min = (min - y) *1 + y - max = (max - y) *1+ y - can.line(self.line_style, x, min, x, max) - -# a box -class error_bar6(T): - __doc__ = error_bar_doc.doc_6 - keys = { "line_style": (line_style.T, line_style.default, ""), - "fill_style": (fill_style.T, fill_style.gray70, ""), - "center_line_style": (line_style.T, line_style.T(width=0.5), ""), - "box_width": (UnitType, 4, ""), - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, can, loc, min, max, qmin = None, qmax = None): - x = loc[0] - y = loc[1] - - can.rectangle(self.line_style, self.fill_style, - x - self.box_width / 2.0, min, - x + self.box_width / 2.0, max) - can.line(self.center_line_style, - x - self.box_width/2.0, (min+max)/2.0, - x + self.box_width/2.0, (min+max)/2.0) - -bar1 = error_bar1() -bar2 = error_bar2() -bar3 = error_bar3() -bar4 = error_bar4() -bar5 = error_bar5() -bar6 = error_bar6() - -standards = object_set.T(bar1, bar2, bar3, bar4, bar5, bar6) - diff --git a/bin/pychart/error_bar_doc.py b/bin/pychart/error_bar_doc.py deleted file mode 100644 index de2362dd50c..00000000000 --- a/bin/pychart/error_bar_doc.py +++ /dev/null @@ -1,34 +0,0 @@ -# automatically generated by generate_docs.py. -doc_1="""Attributes supported by this class are: -line_style(type:line_style.T) default="". -tic_len(type:length in points (\\xref{unit})) default="Length of the horizontal bars". -""" - -doc_2="""Attributes supported by this class are: -vline_style(type:line_style.T) default="The style of the vertical bar.". -hline_style(type:line_style.T) default="The style of the horizontal bars.". -tic_len(type:length in points (\\xref{unit})) default="The length of the horizontal bars". -""" - -doc_3="""Attributes supported by this class are: -line_style(type:line_style.T) default="". -""" - -doc_4="""Attributes supported by this class are: -line_style(type:line_style.T) default="". -box_width(type:length in points (\\xref{unit})) default="". -tic_len(type:length in points (\\xref{unit})) default="". -fill_style(type:fill_style.T) default="". -""" - -doc_5="""Attributes supported by this class are: -line_style(type:line_style.T) default="". -""" - -doc_6="""Attributes supported by this class are: -box_width(type:length in points (\\xref{unit})) default="". -line_style(type:line_style.T) default="". -center_line_style(type:line_style.T) default="". -fill_style(type:fill_style.T) default="". -""" - diff --git a/bin/pychart/fill_style.py b/bin/pychart/fill_style.py deleted file mode 100644 index 1cddd3d9601..00000000000 --- a/bin/pychart/fill_style.py +++ /dev/null @@ -1,285 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import pychart_util -import color -import line_style -import chart_object -import object_set -import types -import theme -import fill_style_doc -from pychart_types import * -from scaling import * - -_keys = { - "bgcolor" : (color.T, color.white, "The background color."), - "line_style": (line_style.T, line_style.default, - pychart_util.line_desc), - "line_interval": (NumType, 3, - "The interval between successive stitch lines.") - } - -class T(chart_object.T): - __doc__ = fill_style_doc.doc - keys = _keys -##AUTOMATICALLY GENERATED -##END AUTOMATICALLY GENERATED - def __str__(self): - s = name_table().lookup(self) - if s: - return s - return "" % \ - (self.bgcolor, self.line_style, self.line_interval) - -class Plain(T): - """This class just fills the region with solid background color. -Attributes line_style and line_interval are ignored.""" - def draw(self, can, x1, y1, x2, y2): - pass - -class Diag(T): - "This class fills the region with diagonal lines." - - def draw(self, can, x1, y1, x2, y2): - line_width = self.line_style.width - interval = self.line_interval * 1.414 - x1 -= line_width - y1 -= line_width - x2 += line_width - y2 += line_width - len = max(y2 - y1, x2 - x1) - curx = x1 - len - while curx < x2: - can.line(self.line_style, curx, y1, curx+len, y1+len) - curx += interval - -class Rdiag(T): - """Fills the region with diagonal lines, but tilted in the opposite -direction from fill_style.Diag.""" - def draw(self, can, x1, y1, x2, y2): - line_width = self.line_style.width - interval = self.line_interval * 1.414 - x1 -= line_width - y1 -= line_width - x2 += line_width - y2 += line_width - len = max(y2 - y1, x2 - x1) - curx = x1 - while curx < x2 + len: - can.line(self.line_style, curx, y1, curx-len, y1+len) - curx += interval - -class Vert(T): - "Fills the region with vertical lines" - def draw(self, can, x1, y1, x2, y2): - interval = self.line_interval - curx = x1 - while curx < x2: - can.line(self.line_style, curx, y1, curx, y2) - curx += interval - -class Horiz(T): - "Fills the region with horizontal lines" - def draw(self, can, x1, y1, x2, y2): - interval = self.line_interval - cury = y1 - while cury < y2: - can.line(self.line_style, x1, cury, x2, cury) - cury += interval - -class Stitch(T): - "Fills the region with horizontal and vertical lines." - def draw(self, can, x1, y1, x2, y2): - interval = self.line_interval - cury = y1 - while cury < y2: - can.line(self.line_style, x1, cury, x2, cury) - cury += interval - curx = x1 - while curx < x2: - can.line(self.line_style, curx, y1, curx, y2) - curx += interval - -class Wave(T): - "Fills the region with horizontal wavy lines." - def draw(self, can, x1, y1, x2, y2): - x1 = xscale(x1) - x2 = xscale(x2) - y1 = yscale(y1) - y2 = yscale(y2) - line_width = nscale(self.line_style.width) - interval = nscale(self.line_interval) - - can.set_line_style(self.line_style) - x1 -= line_width - x2 += line_width - cury = y1 - half = interval/2.0 - while cury < y2: - curx = x1 - can.newpath() - can.moveto(curx, cury) - while curx < x2: - can.lineto(curx + half, cury + half) - can.lineto(curx + interval, cury) - curx += interval - can.stroke() - cury += interval - -class Vwave(T): - """Fills the region with vertical wavy lines.""" - def draw(self, can, x1, y1, x2, y2): - x1 = xscale(x1) - x2 = xscale(x2) - y1 = yscale(y1) - y2 = yscale(y2) - line_width = nscale(self.line_style.width) - interval = nscale(self.line_interval) - - can.set_line_style(self.line_style) - y1 -= line_width - y2 += line_width - curx = x1 - half = interval/2.0 - while curx < x2: - cury = y1 - can.newpath() - can.moveto(curx, cury) - while cury < y2: - can.lineto(curx + half, cury + half) - can.lineto(curx, cury + interval) - cury += interval - can.stroke() - curx += interval - -class Lines(T): - """Fills the region with a series of short line segments.""" - def draw(self, can, x1, y1, x2, y2): - interval = nscale(self.line_interval) - cury = y1 - j = 0 - while cury < y2: - curx = x1 - if j % 2 == 1: - curx += interval/2.0 - while curx < x2: - can.line(self.line_style, curx, cury, curx+interval/2.0, cury) - curx += interval * 1.5 - j += 1 - cury += interval - -default = Plain() - -color_standards = object_set.T() -grayscale_standards = object_set.T() - -def _intern_both(style): - global color_standards, grayscale_standards - color_standards.add(style) - grayscale_standards.add(style) - return style - -def _intern_color(style): - global color_standards, grayscale_standards - color_standards.add(style) - return style - -def _intern_grayscale(style): - global color_standards, grayscale_standards - grayscale_standards.add(style) - return style - -black = _intern_both(Plain(bgcolor=color.gray_scale(0.0), line_style=None)) - -red = _intern_color(Plain(bgcolor=color.red)) -darkseagreen = _intern_color(Plain(bgcolor=color.darkseagreen)) -blue = _intern_color(Plain(bgcolor=color.blue)) -aquamarine1 = _intern_color(Plain(bgcolor=color.aquamarine1)) -gray70 = _intern_both(Plain(bgcolor=color.gray70, line_style=None)) -brown = _intern_color(Plain(bgcolor=color.brown)) -darkorchid = _intern_color(Plain(bgcolor=color.darkorchid)) -diag = _intern_both(Diag(line_style=line_style.T(cap_style=2))) -green = _intern_color(Plain(bgcolor=color.green)) -gray50 = _intern_both(Plain(bgcolor=color.gray50, line_style=None)) -white = _intern_both(Plain(bgcolor=color.gray_scale(1.0), line_style=None)) -goldenrod = _intern_color(Plain(bgcolor=color.goldenrod)) -rdiag = _intern_both(Rdiag(line_style=line_style.T(cap_style=2))) -vert = _intern_both(Vert(line_interval=1.8)) - -gray30 = _intern_both(Plain(bgcolor=color.gray30, line_style=None)) -gray20 = _intern_both(Plain(bgcolor=color.gray20, line_style=None)) -gray10 = _intern_both(Plain(bgcolor=color.gray10, line_style=None)) -diag2 = _intern_both(Diag(line_style=line_style.T(width=3, cap_style=2), - line_interval=6)) -rdiag2 = _intern_both(Rdiag(line_style=line_style.T(width=3, cap_style=2), - line_interval=6)) -yellow = _intern_color(Plain(bgcolor=color.yellow)) -diag3 = _intern_both(Diag(line_style=line_style.T(width=3, color=color.gray50, cap_style=2), - line_interval=6)) -horiz = _intern_both(Horiz(line_interval=1.8)) -gray90 = _intern_both(Plain(bgcolor=color.gray90, line_style=None)) -rdiag3 = _intern_both(Rdiag(line_style=line_style.T(width=3, - color=color.gray50, - cap_style=2), - line_interval=6)) - -wave = _intern_both(Wave(line_style=line_style.T(cap_style=2, join_style=1))) -vwave = _intern_both(Vwave(line_style=line_style.T(cap_style=2, join_style=1))) -stitch = _intern_both(Stitch(line_style=line_style.T(cap_style=2, join_style=1))) -lines = _intern_both(Lines(line_style=line_style.T())) - -diag_fine = _intern_both(Diag(line_style=line_style.T(width=0.75,cap_style=2), - line_interval = 1.5)) -diag2_fine = _intern_both(Diag(line_style=line_style.T(width=0.75, cap_style=2), - line_interval=1.5)) -diag3_fine = _intern_both(Diag(line_style=line_style.T(width=0.75, - color = color.gray50, - cap_style=2), - line_interval=1.5)) -rdiag_fine = _intern_both(Rdiag(line_style=line_style.T(width=0.75,cap_style=2), - line_interval = 1.5)) -rdiag2_fine = _intern_both(Rdiag(line_style=line_style.T(width=0.75, cap_style=2), - line_interval=1.5)) -rdiag3_fine = _intern_both(Rdiag(line_style=line_style.T(width=0.75, - color = color.gray50, - cap_style=2), - line_interval=1.5)) - -horiz_fine = _intern_both(Horiz(line_interval=1.5)) -vert_fine = _intern_both(Vert(line_interval=1.5)) - -# -# Fill styles for color charts. -# - -standards = None -_name_table = None - -def init(): - global standards, _name_table - if theme.use_color: - standards = color_standards - else: - standards = grayscale_standards - _name_table = None - -def name_table(): - global _name_table - if not _name_table: - _name_table = pychart_util.symbol_lookup_table(globals(), standards) - return _name_table - -init() -theme.add_reinitialization_hook(init) - diff --git a/bin/pychart/fill_style_doc.py b/bin/pychart/fill_style_doc.py deleted file mode 100644 index 6274e1b2a89..00000000000 --- a/bin/pychart/fill_style_doc.py +++ /dev/null @@ -1,7 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -bgcolor(type:color.T) default="The background color.". -line_style(type:line_style.T) default="The style of the line. ". -line_interval(type:number) default="The interval between successive stitch lines.". -""" - diff --git a/bin/pychart/font.py b/bin/pychart/font.py deleted file mode 100644 index 593666bb015..00000000000 --- a/bin/pychart/font.py +++ /dev/null @@ -1,454 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import color -import string -import pychart_util -import re -import theme -import afm.dir - - -__doc__ = """The module for manipulating texts and their attributes. - -Pychart supports extensive sets of attributes in texts. All attributes -are specified via "escape sequences", starting from letter "/". For -example, the below examples draws string "Hello" using a 12-point font -at 60-degree angle: - -/12/a60{}Hello - -List of attributes: - -/hA - Specifies horizontal alignment of the text. A is one of L (left - alignment), R (right alignment), or C (center alignment). -/vA - Specifies vertical alignment of the text. A is one of "B" - (bottom), "T" (top), " M" (middle). - -/F{FONT} - Switch to FONT font family. -/T - Shorthand of /F{Times-Roman}. -/H - Shorthand of /F{Helvetica}. -/C - Shorthand of /F{Courier}. -/B - Shorthand of /F{Bookman-Demi}. -/A - Shorthand of /F{AvantGarde-Book}. -/P - Shorthand of /F{Palatino}. -/S - Shorthand of /F{Symbol}. -/b - Switch to bold typeface. -/i - Switch to italic typeface. -/o - Switch to oblique typeface. -/DD - Set font size to DD points. - - /20{}2001 space odyssey! - -/cDD - Set gray-scale to 0.DD. Gray-scale of 00 means black, 99 means white. - -//, /{, /} - Display `/', `@', or `@{'. - -{ ... } - Limit the effect of escape sequences. For example, the below - example draws "Foo" at 12pt, "Bar" at 8pt, and "Baz" at 12pt. - - /12Foo{/8Bar}Baz -\n - Break the line. -""" - -# List of fonts for which their absence have been already warned. -_undefined_font_warned = {} - -def _intern_afm(font, text): - global _undefined_font_warned - font2 = _font_aliases.has_key(font) and _font_aliases[font] - if afm.dir.afm.has_key(font): - return afm.dir.afm[font] - if afm.dir.afm.has_key(font2): - return afm.dir.afm[font2] - - try: - exec("import pychart.afm.%s" % re.sub("-", "_", font)) - return afm.dir.afm[font] - except: - if not font2 and not _undefined_font_warned.has_key(font): - pychart_util.warn("Warning: unknown font '%s' while parsing '%s'" % (font, text)) - _undefined_font_warned[font] = 1 - - if font2: - try: - exec("import pychart.afm.%s" % re.sub("-", "_", font2)) - return afm.dir.afm[font2] - except: - if not _undefined_font_warned.has_key(font): - pychart_util.warn("Warning: unknown font '%s' while parsing '%s'" % (font, text)) - _undefined_font_warned[font] = 1 - return None -def line_width(font, size, text): - table = _intern_afm(font, text) - if not table: - return 0 - - width = 0 - for ch in text: - code = ord(ch) - if code < len(table): - width += table[code] - else: - width += 10000 - - width = float(width) * size / 1000.0 - return width - -_font_family_map = {'T': "Times", - 'H': "Helvetica", - 'C': "Courier", - 'N': "Helvetica-Narrow", - 'B': "Bookman-Demi", - 'A': "AvantGarde-Book", - 'P': "Palatino", - 'S': "Symbol"} - -# Aliases for ghostscript font names. - -_font_aliases = { - 'Bookman-Demi': "URWBookmanL-DemiBold%I", - 'Bookman-DemiItalic': "URWBookmanL-DemiBoldItal", - 'Bookman-Demi-Italic': "URWBookmanL-DemiBoldItal", - 'Bookman-Light': "URWBookmanL-Ligh", - 'Bookman-LightItalic': "URWBookmanL-LighItal", - 'Bookman-Light-Italic': "URWBookmanL-LighItal", - 'Courier': "NimbusMonL-Regu", - 'Courier-Oblique': "NimbusMonL-ReguObli", - 'Courier-Bold': "NimbusMonL-Bold", - 'Courier-BoldOblique': "NimbusMonL-BoldObli", - 'AvantGarde-Book': "URWGothicL-Book", - 'AvantGarde-BookOblique': "URWGothicL-BookObli", - 'AvantGarde-Book-Oblique': "URWGothicL-BookObli", - 'AvantGarde-Demi': "URWGothicL-Demi", - 'AvantGarde-DemiOblique': "URWGothicL-DemiObli", - 'AvantGarde-Demi-Oblique': "URWGothicL-DemiObli", - 'Helvetica': "NimbusSanL-Regu", - 'Helvetica-Oblique': "NimbusSanL-ReguItal", - 'Helvetica-Bold': "NimbusSanL-Bold", - 'Helvetica-BoldOblique': "NimbusSanL-BoldItal", - 'Helvetica-Narrow': "NimbusSanL-ReguCond", - 'Helvetica-Narrow-Oblique': "NimbusSanL-ReguCondItal", - 'Helvetica-Narrow-Bold': "NimbusSanL-BoldCond", - 'Helvetica-Narrow-BoldOblique': "NimbusSanL-BoldCondItal", - 'Palatino-Roman': "URWPalladioL-Roma", - 'Palatino': "URWPalladioL-Roma", - 'Palatino-Italic': "URWPalladioL-Ital", - 'Palatino-Bold': "URWPalladioL-Bold", - 'Palatino-BoldItalic': "URWPalladioL-BoldItal", - 'NewCenturySchlbk-Roman': "CenturySchL-Roma", - 'NewCenturySchlbk': "CenturySchL-Roma", - 'NewCenturySchlbk-Italic': "CenturySchL-Ital", - 'NewCenturySchlbk-Bold': "CenturySchL-Bold", - 'NewCenturySchlbk-BoldItalic': "CenturySchL-BoldItal", - 'Times-Roman': "NimbusRomNo9L-Regu", - 'Times': "NimbusRomNo9L-Regu", - 'Times-Italic': "NimbusRomNo9L-ReguItal", - 'Times-Bold': "NimbusRomNo9L-Medi", - 'Times-BoldItalic': "NimbusRomNo9L-MediItal", - 'Symbol': "StandardSymL", - 'ZapfChancery-MediumItalic': "URWChanceryL-MediItal", - 'ZapfChancery-Medium-Italic': "URWChanceryL-MediItal", - 'ZapfDingbats': "Dingbats" -} - - -class text_state: - def copy(self): - ts = text_state() - ts.family = self.family - ts.modifiers = list(self.modifiers) - ts.size = self.size - ts.line_height = self.line_height - ts.color = self.color - ts.halign = self.halign - ts.valign = self.valign - ts.angle = self.angle - return ts - def __init__(self): - self.family = theme.default_font_family - self.modifiers = [] # 'b' for bold, 'i' for italic, 'o' for oblique. - self.size = theme.default_font_size - self.line_height = theme.default_line_height or theme.default_font_size - self.color = color.default - self.halign = theme.default_font_halign - self.valign = theme.default_font_valign - self.angle = theme.default_font_angle - -class text_iterator: - def __init__(self, s): - self.str = str(s) - self.i = 0 - self.ts = text_state() - self.stack = [] - def reset(self, s): - self.str = str(s) - self.i = 0 - - def __return_state(self, ts, str): - font_name = ts.family - - if ts.modifiers != []: - is_bold = 0 - if 'b' in ts.modifiers: - is_bold = 1 - font_name += "-Bold" - if 'o' in ts.modifiers: - if not is_bold: - font_name += "-" - font_name += "Oblique" - elif 'i' in ts.modifiers: - if not is_bold: - font_name += "-" - font_name += "Italic" - elif font_name in ("Palatino", "Times", "NewCenturySchlbk"): - font_name += "-Roman" - - return (font_name, ts.size, ts.line_height, ts.color, - ts.halign, ts.valign, ts.angle, str) - def __parse_float(self): - istart = self.i - while self.i < len(self.str) and self.str[self.i] in string.digits or self.str[self.i] == '.': - self.i += 1 - return float(self.str[istart:self.i]) - - def __parse_int(self): - istart = self.i - while self.i < len(self.str) and \ - (self.str[self.i] in string.digits or - self.str[self.i] == '-'): - self.i += 1 - return int(self.str[istart:self.i]) - def next(self): - "Get the next text segment. Return an 8-element array: (FONTNAME, SIZE, LINEHEIGHT, COLOR, H_ALIGN, V_ALIGN, ANGLE, STR." - l = [] - changed = 0 - self.old_state = self.ts.copy() - - while self.i < len(self.str): - if self.str[self.i] == '/': - self.i = self.i+1 - ch = self.str[self.i] - self.i = self.i+1 - self.old_state = self.ts.copy() - if ch == '/' or ch == '{' or ch == '}': - l.append(ch) - elif _font_family_map.has_key(ch): - self.ts.family = _font_family_map[ch] - changed = 1 - elif ch == 'F': - # /F{font-family} - if self.str[self.i] != '{': - raise Exception, "'{' must follow /F in \"%s\"" % self.str - self.i += 1 - istart = self.i - while self.str[self.i] != '}': - self.i += 1 - if self.i >= len(self.str): - raise Exception, "Expecting /F{...}. in \"%s\"" % self.str - self.ts.family = self.str[istart:self.i] - self.i += 1 - changed = 1 - - elif ch in string.digits: - self.i -= 1 - self.ts.size = self.__parse_int() - self.ts.line_height = self.ts.size - changed = 1 - elif ch == 'l': - self.ts.line_height = self.__parse_int() - changed = 1 - elif ch == 'b': - self.ts.modifiers.append('b') - changed = 1 - elif ch == 'i': - self.ts.modifiers.append('i') - changed = 1 - elif ch == 'o': - self.ts.modifiers.append('q') - changed = 1 - elif ch == 'c': - self.ts.color = color.gray_scale(self.__parse_float()) - elif ch == 'v': - if self.str[self.i] not in "BTM": - raise Exception, "Undefined escape sequence: /v%c (%s)" % (self.str[self.i], self.str) - self.ts.valign = self.str[self.i] - self.i += 1 - changed = 1 - elif ch == 'h': - if self.str[self.i] not in "LRC": - raise Exception, "Undefined escape sequence: /h%c (%s)" % (self.str[self.i], self.str) - self.ts.halign = self.str[self.i] - self.i += 1 - changed = 1 - elif ch == 'a': - self.ts.angle = self.__parse_int() - changed = 1 - else: - raise Exception, "Undefined escape sequence: /%c (%s)" % (ch, self.str) - elif self.str[self.i] == '{': - self.stack.append(self.ts.copy()) - self.i += 1 - elif self.str[self.i] == '}': - if len(self.stack) == 0: - raise ValueError, "unmatched '}' in \"%s\"" % (self.str) - self.ts = self.stack[-1] - del self.stack[-1] - self.i += 1 - changed = 1 - else: - l.append(self.str[self.i]) - self.i += 1 - - if changed and len(l) > 0: - return self.__return_state(self.old_state, ''.join(l)) - else: - # font change in the beginning of the sequence doesn't count. - self.old_state = self.ts.copy() - changed = 0 - if len(l) > 0: - return self.__return_state(self.old_state, ''.join(l)) - else: - return None - -# -# - -def unaligned_get_dimension(text): - """Return the bounding box of the text, assuming that the left-bottom corner - of the first letter of the text is at (0, 0). This procedure ignores - /h, /v, and /a directives when calculating the BB; it just returns the - alignment specifiers as a part of the return value. The return value is a - tuple (width, height, halign, valign, angle).""" - - xmax = 0 - ymax = 0 - ymax = 0 - angle = None - halign = None - valign = None - - itr = text_iterator(None) - for line in str(text).split("\n"): - cur_height = 0 - cur_width = 0 - itr.reset(line) - while 1: - elem = itr.next() - if not elem: - break - (font, size, line_height, color, new_h, new_v, new_a, chunk) = elem - if halign != None and new_h != halign: - raise Exception, "Only one /h can appear in string '%s'." % str(text) - if valign != None and new_v != valign: - raise Exception, "Only one /v can appear in string '%s'." % str(text) - if angle != None and new_a != angle: - raise Exception, "Only one /a can appear in string '%s'." % str(text) - halign = new_h - valign = new_v - angle = new_a - cur_width += line_width(font, size, chunk) - cur_height = max(cur_height, line_height) - xmax = max(cur_width, xmax) - ymax += cur_height - return (xmax, ymax, - halign or theme.default_font_halign, - valign or theme.default_font_valign, - angle or theme.default_font_angle) - -def get_dimension(text): - """Return the bounding box of the , - assuming that the left-bottom corner - of the first letter of the text is at (0, 0). This procedure ignores - /h, /v, and /a directives when calculating the boundingbox; it just returns the - alignment specifiers as a part of the return value. The return value is a - tuple (width, height, halign, valign, angle).""" - (xmax, ymax, halign, valign, angle) = unaligned_get_dimension(text) - xmin = ymin = 0 - if halign == "C": - xmin = -xmax / 2.0 - xmax = xmax / 2.0 - elif halign == "R": - xmin = -xmax - xmax = 0 - if valign == "M": - ymin = -ymax / 2.0 - ymax = ymax / 2.0 - elif valign == "T": - ymin = -ymax - ymax = 0 - if angle != 0: - (x0, y0) = pychart_util.rotate(xmin, ymin, angle) - (x1, y1) = pychart_util.rotate(xmax, ymin, angle) - (x2, y2) = pychart_util.rotate(xmin, ymax, angle) - (x3, y3) = pychart_util.rotate(xmax, ymax, angle) - xmax = max(x0, x1, x2, x3) - xmin = min(x0, x1, x2, x3) - ymax = max(y0, y1, y2, y3) - ymin = min(y0, y1, y2, y3) - return (xmin, xmax, ymin, ymax) - return (xmin, xmax, ymin, ymax) - -def unaligned_text_width(text): - x = unaligned_get_dimension(text) - return x[0] - -def text_width(text): - """Return the width of the in points.""" - (xmin, xmax, d1, d2) = get_dimension(text) - return xmax-xmin - -def unaligned_text_height(text): - x = unaligned_get_dimension(text) - return x[1] - -def text_height(text): - """Return the total height of the and the length from the - base point to the top of the text box.""" - (d1, d2, ymin, ymax) = get_dimension(text) - return (ymax-ymin, ymax) - -def get_align(text): - "Return (halign, valign, angle) of the ." - (x1, x2, h, v, a) = unaligned_get_dimension(text) - return (h, v, a) - -def quotemeta(text): - """Quote letters with special meanings in pychart so that will display - as-is when passed to canvas.show(). - ->>> font.quotemeta("foo/bar") -"foo//bar" -""" - text = re.sub("/", "//", text) - text = re.sub("\\{", "/{", text) - text = re.sub("\\}", "/}", text) - return text diff --git a/bin/pychart/generate_docs.py b/bin/pychart/generate_docs.py deleted file mode 100644 index 9e0461249a2..00000000000 --- a/bin/pychart/generate_docs.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import string -import doc_support -import sys -import re -import os - -import area -import arrow -import axis -import bar_plot -import line_plot -import pie_plot -import color -import error_bar -import fill_style -import font -import text_box -import line_style -import legend -import range_plot -import tick_mark - -indent = 4 -max_line_len = 64 - -def format_paragraph(fp, str): - line_len = indent - fp.write(" " * indent) - for word in str.split(): - if line_len >= max_line_len: - fp.write("\n") - fp.write(" " * indent) - line_len = indent - fp.write(word + " ") - line_len += len(word) + 1 - -def format_string(fp, str): - str = re.sub("<<([^>]+)>>", "See also pychart.\\1", str) - - str2 = "" - in_example = 0 - for l in str.split("\n"): - if re.match("@example", l): - in_example = 1 - if re.match("@end example", l): - in_example = 0 - if in_example: - str2 += l - else: - l = re.sub("^[ \t]*", "", l) - str2 += l - str2 += "\n" - fname = os.tempnam() - out_fp = open(fname, "w") - out_fp.write(str2) - out_fp.close() - - in_fp = os.popen("makeinfo --fill-column=64 --no-headers " + fname, "r") - for l in in_fp.readlines(): - fp.write(" " * indent) - fp.write(l) - in_fp.close() - os.remove(fname) - -def generate_doc(c, name, suffix="", append = 0): - if append: - fp = open(name + "_doc.py", "a+") - else: - fp = open(name + "_doc.py", "w") - fp.write("# automatically generated by generate_docs.py.\n") - - fp.write("doc" + suffix + "=\"\"\"Attributes supported by this class are:\n") - for key in c.keys.keys(): - val=c.keys[key] - desc = "" - defaultValDesc = None - if len(val) > 3: - desc = val[3] - if len(val) > 4: - defaultValDesc = val[4] - - fp.write(key + "(type:" + doc_support.stringify_type(val[0])) - if defaultValDesc: - fp.write(") default:" + defaultValDesc) - else: - fp.write(") default=" + str(doc_support.stringify_value(val[2])) + ".\n") - format_string(fp, desc) - fp.write("\"\"\"\n\n") - fp.close() - - -generate_doc(arrow.T, "arrow") -generate_doc(area.T, "area") -generate_doc(axis.X, "axis", "_x") -generate_doc(axis.Y, "axis", "_y", 1) -generate_doc(bar_plot.T, "bar_plot") -generate_doc(line_plot.T, "line_plot") -generate_doc(pie_plot.T, "pie_plot") -generate_doc(color.T, "color") -generate_doc(error_bar.error_bar1, "error_bar","_1") -generate_doc(error_bar.error_bar2, "error_bar", "_2", 1) -generate_doc(error_bar.error_bar3, "error_bar", "_3", 1) -generate_doc(error_bar.error_bar4, "error_bar", "_4", 1) -generate_doc(error_bar.error_bar5, "error_bar", "_5", 1) -generate_doc(error_bar.error_bar6, "error_bar", "_6", 1) -generate_doc(fill_style.T, "fill_style") -generate_doc(text_box.T, "text_box") -generate_doc(range_plot.T, "range_plot") -generate_doc(legend.T, "legend") -generate_doc(legend.Entry, "legend", "_entry", 1) -generate_doc(line_style.T, "line_style") -generate_doc(tick_mark.T, "tick_mark") diff --git a/bin/pychart/gs_frontend.py b/bin/pychart/gs_frontend.py deleted file mode 100644 index 144f19de109..00000000000 --- a/bin/pychart/gs_frontend.py +++ /dev/null @@ -1,87 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import pychart_util -import theme -import sys -import os -import os.path -import pscanvas -import tempfile -import string -import basecanvas -from scaling import * - -def get_gs_path(): - """Guess where the Ghostscript executable is - and return its absolute path name.""" - path = os.defpath - if os.environ.has_key("PATH"): - path = os.environ["PATH"] - for dir in path.split(os.pathsep): - for name in ("gs", "gs.exe", "gswin32c.exe"): - g = os.path.join(dir, name) - if os.path.exists(g): - return g - raise Exception, "Ghostscript not found." - -class T(pscanvas.T): - def __write_contents(self, fp): - fp.write(pscanvas.preamble_text) - for name, id in self.__font_ids.items(): - fp.write("/%s {/%s findfont SF} def\n" % (id, name)) - fp.write("%d %d translate\n" % (-self.bbox[0], -self.bbox[1])) - fp.writelines(self.__output_lines) - fp.write("showpage end\n") - fp.flush() - - def close(self): - # Don't call pscanvas.T.close, as it creates a - # ps file. - basecanvas.T.close(self) - - def start_gs(self, arg): - self.bbox = theme.adjust_bounding_box([xscale(self.__xmin), - yscale(self.__ymin), - xscale(self.__xmax), - yscale(self.__ymax)]) - - gs_path = get_gs_path() - self.pipe_fp = None - if self.__output_lines == []: - return - - if sys.platform != "win32" and hasattr(os, "popen"): - # UNIX-like systems - cmdline = "\"%s\" -q %s -g%dx%d -q >/dev/null 2>&1" % \ - (gs_path, arg, - self.bbox[2] - self.bbox[0], - self.bbox[3] - self.bbox[1]) - self.pipe_fp = os.popen(cmdline, "w") - self.__write_contents(self.pipe_fp) - else: - # XXX should use mktemp, but need to support python<=2.2 as well. - fname = tempfile.mktemp("xxx") - fp = open(fname, "wb") - self.__write_contents(fp) - fp.close() - cmdline = "\"%s\" -q %s -g%dx%d -q <%s >NUL" % \ - (gs_path, arg, - self.bbox[2] - self.bbox[0], - self.bbox[3] - self.bbox[1], fname) - os.system(cmdline) - os.unlink(fname) - def close_gs(self): - if self.pipe_fp: - self.pipe_fp.close() - self.pipe_fp = None diff --git a/bin/pychart/interval_bar_plot.py b/bin/pychart/interval_bar_plot.py deleted file mode 100644 index 084ad296f60..00000000000 --- a/bin/pychart/interval_bar_plot.py +++ /dev/null @@ -1,228 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import line_style -import fill_style -import pychart_util -import chart_object -import legend -import bar_plot_doc -import theme -from types import * -from pychart_types import * - -fill_styles = None - -_keys = { - "direction" : (StringType, "vertical", - """The direction the growth of the bars. The value is either 'horizontal' - or 'vertical'."""), - "data" : (AnyType, None, """Specifes data points. Unlike other types - of charts, the "hcol"th column of the data must be a sequence of - numbers, not just a single number. See also the description of - "hcol".""" - ), - "data_label_offset": (CoordType, (0, 5), - "The location of data labels relative to the sample point. See also attribute data_label_format."), - - "data_label_format": (FormatType, None, """The - format string for the label displayed besides each - bar. It can be a `printf' style format - string, or a two-parameter function that - takes (x,y) values and returns a string. """ - + pychart_util.string_desc), - - "label": (StringType, "???", pychart_util.label_desc), - "bcol" : (IntType, 0, - """Specifies the column from which base values (i.e., X values when attribute "direction" is "vertical", Y values otherwise) are extracted. -The - combination of "data", "bcol", and "hcol" attributes defines - the set of boxes drawn by this chart. - See also the descriptions of the 'bcol' and 'data' attributes. - """), - "hcol": (IntType, 1, - """The column from which the base and height of - bars are extracted. See the below example: - -@example - d = [[5,[10,15,22]], [7,[22,23,5,10]], [8,[25,3]]] - p = interval_bar_plot.T(data = d, bcol = 0, hcol = 1) -@end example - - Here, three sequence of bars will be drawn. - The X locations of the bars - will be 5, 7, and 8. For example, at location X=7, - three bars are drawn, - one corresponding to Y values of 22 to 45 (=22+23), - and the second one for values 45 to 50, and the third one - for values 50 to 60. The line and fill styles of the bars - are picked in a round-robin fashion - from attributes "line_styles" and - "fill_styles". - """), - "line_styles": (ListType, [line_style.default, None], - """The list of line styles for bars. - The style of each bar is chosen in a round-robin fashion, if the - number of elements in "line_styles" is smaller than - actual number of boxes."""), - "fill_styles": (ListType, [lambda: fill_styles.next(), None], - """List of fill styles for bars. - The style of each bar is chosen in a round-robin fashion, if the - number of elements in "line_styles" is smaller than - actual number of boxes. - If this attribute is omitted, - a style is picked from standard styles round-robin. <>."""), - "cluster": (TupleType, (0, 1), """This attribute is used to - cluster multiple bar plots side by side in a single chart. - The value should be a tuple of two integers. The second value should be equal to the total number of bar plots in the chart. The first value should be the relative position of this chart; 0 places this chart the leftmost, and N-1 - (where N is the 2nd value of this attribute) places this chart the rightmost. Consider the below example: - -@example - a = area.T(...) - p1 = interval_bar_plot.T(data = [[1, [20,10]][2,[30,5]]], cluster=(0,2)) - p2 = interval_bar_plot.T(data = [[1,[25,11,2]],[2,[10,5,3]]], cluster=(1,2)) - a.add_plot(p1, p2) - a.draw() -@end example - - In this example, one group of bars will be drawn side-by-side at - position x=1. - Other two bars will be drawn side by side at position x=2. - See also the description of attribute "cluster" for bar_plot.T. - """), - "width": (UnitType, 5, """Width of each box. The unit is in points. -@cindex width, bar chart -@cindex size, bar chart -"""), - "cluster_sep": (UnitType, 0, """The separation between - clustered boxes. The unit is points."""), - "stack_on": (AnyType, None, - "The value must be either None or bar_plot.T. If not None, bars of this plot are stacked on top of another bar plot."), - } - -class T(chart_object.T): - __doc__ = bar_plot_doc.doc - keys = _keys - def check_integrity(self): - self.type_check() - def get_value(self, bval): - for pair in self.data: - if pair[self.bcol] == bval: - return pair[self.hcol] - raise ValueError, str(bval) + ": can't find the xval" - - def __get_data_range(self, col): - gmin = 99999999 - gmax = -99999999 - for item in self.data: - seq = item[col] - if seq[0] < gmin: gmin = seq[0] - max = 0 - for v in seq: - max += v - if max > gmax: gmax = max - return (gmin, gmax) - - def get_data_range(self, which): - if self.direction == 'vertical': - if which == 'X': - return pychart_util.get_data_range(self.data, self.bcol) - else: - return self.__get_data_range(self.hcol) - else: - assert self.direction == 'horizontal' - if which == 'Y': - return pychart_util.get_data_range(self.data, self.bcol) - else: - return self.__get_data_range(self.hcol) - - def get_style(self, nth): - line_style = self.line_styles[nth % len(self.line_styles)] - fill_style = self.fill_styles[nth % len(self.fill_styles)] - return (line_style, fill_style) - - def draw_vertical(self, ar, can): - for pair in self.data: - xval = pair[self.bcol] - yvals = pychart_util.get_sample_val(pair, self.hcol) - - if None in (xval, yvals): continue - - ybot = 0 - - totalWidth = (self.width+self.cluster_sep) * self.cluster[1] - self.cluster_sep - firstX = ar.x_pos(xval) - totalWidth/2.0 - thisX = firstX + (self.width+self.cluster_sep) * self.cluster[0] - self.cluster_sep - - cury = yvals[0] - n = 0 - - for yval in yvals[1:]: - (line_style, fill_style) = self.get_style(n) - can.rectangle(line_style, fill_style, - thisX, ar.y_pos(cury), thisX+self.width, - ar.y_pos(cury + yval)) - cury += yval - n += 1 - - if self.data_label_format: - can.show(thisX + self.width/2.0 + self.data_label_offset[0], - ar.y_pos(cury) + self.data_label_offset[1], - "/hC" + pychart_util.apply_format(self.data_label_format, (pair[self.bcol], pair[self.hcol]), 1)) - - def draw_horizontal(self, ar, can): - for pair in self.data: - yval = pair[self.bcol] - xvals = pychart_util.get_sample_val(pair, self.hcol) - - if None in (xvals, yval): continue - - totalWidth = (self.width+self.cluster_sep) * self.cluster[1] - self.cluster_sep - firstY = ar.y_pos(yval) - totalWidth/2.0 - thisY = firstY + (self.width+self.cluster_sep) * self.cluster[0] - self.cluster_sep - - curx = xvals[0] - n = 0 - for xval in xvals[1:]: - line_style, fill_style = self.get_style(n) - can.rectangle(line_style, fill_style, - ar.x_pos(curx), thisY, - ar.x_pos(xval), thisY+self.width) - curx = xval - n += 1 - - def get_legend_entry(self): - if self.label: - return legend.Entry(line_style=self.line_styles[0], - fill_style=self.fill_styles[0], - label=self.label) - return None - - def draw(self, ar, can): - self.type_check() - can.clip(ar.loc[0], ar.loc[1], - ar.loc[0] + ar.size[0], ar.loc[1] + ar.size[1]) - - if self.direction == "vertical": - self.draw_vertical(ar, can) - else: - self.draw_horizontal(ar, can) - - can.endclip() - -def init(): - global fill_styles - fill_styles = fill_style.standards.iterate() - -theme.add_reinitialization_hook(init) - diff --git a/bin/pychart/legend.py b/bin/pychart/legend.py deleted file mode 100644 index c45f6ff5729..00000000000 --- a/bin/pychart/legend.py +++ /dev/null @@ -1,181 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import tick_mark -import font -import line_style -import color -import fill_style -import chart_object -import pychart_util -import types -import legend_doc -import theme - -from pychart_types import * -from types import * - -class Entry(chart_object.T): - keys = {"line_len" : (UnitType, None, - "Length of the sample line for line plots. If omitted, it is set to be theme.default_font_size"), - "rect_size" : (UnitType, None, - "Size of the sample 'blob' for bar range charts. If omitted, it is set to be 70% of theme.default_size"), - "tick_mark": (tick_mark.T, None, ""), - "line_style": (line_style.T, None, ""), - "fill_style": (fill_style.T, None, ""), - "label": (StringType, "???", ""), - } - __doc__ = legend_doc.doc_entry -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - - def label_width(self): - return font.text_width(" " + self.label) - def get_line_len(self): - return self.line_len or theme.default_font_size - def get_rect_size(self): - return self.rect_size or theme.default_font_size * 7 / 10.0 - - def sample_width(self): - w = 0 - if self.fill_style != None: - w += self.get_line_len() - elif self.line_style != None: - w += self.get_line_len() - elif self.tick_mark != None: - w += self.tick_mark.size - return w - def height(self): - h = font.text_height(self.label)[0] - return h - - def draw(self, ar, can, x_tick, x_label, y): - """Draw a legend entry. X_TICK and X_LABEL are the X location \ - (in points) of where the sample and label are drawn.""" - - rect_size = self.get_rect_size() - line_len = self.get_line_len() - - nr_lines = len(self.label.split("\n")) - text_height = font.text_height(self.label)[0] - line_height = text_height / float(nr_lines) - y_center = y + text_height - line_height/1.5 - - if self.fill_style != None: - can.rectangle(self.line_style, self.fill_style, - x_tick, y_center - rect_size/2.0, - x_tick + rect_size, - y_center + rect_size/2.0) - elif self.line_style != None: - can.line(self.line_style, x_tick, y_center, - x_tick + line_len, y_center) - if self.tick_mark != None: - self.tick_mark.draw(can, x_tick + line_len/2.0, y_center) - elif self.tick_mark != None: - self.tick_mark.draw(can, x_tick, y_center) - - can.show(x_label, y, self.label) - -__doc__ = """Legend is a rectangular box drawn in a chart to describe -the meanings of plots. The contents of a legend box is extracted from -plots' "label", "line-style", and "tick-mark" attributes. - -This module exports a single class, legend.T. Legend.T is a part of -an area.T object, and is drawn automatically when area.draw() method -is called. """ - -class T(chart_object.T): - __doc__ = legend_doc.doc - keys = { - "inter_row_sep": (UnitType, 0, - "Space between each row in the legend."), - "inter_col_sep": (UnitType, 0, - "Space between each column in the legend."), - "frame_line_style": (line_style.T, line_style.default, ""), - "frame_fill_style": (fill_style.T, fill_style.white, ""), - "top_fudge": (UnitType, 0, - "Amount of space above the first line."), - "bottom_fudge": (UnitType, 3, - "Amount of space below the last line."), - "left_fudge": (UnitType, 5, - "Amount of space left of the legend."), - "right_fudge": (UnitType, 5, - "Amount of space right of the legend."), - "loc": (CoordType, None, - """Bottom-left corner of the legend. - The default location of a legend is the bottom-right end of the chart."""), - "shadow": (ShadowType, None, pychart_util.shadow_desc), - "nr_rows": (IntType, 9999, "Number of rows in the legend. If the number of plots in a chart is larger than nr_rows, multiple columns are created in the legend."), - - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def draw(self, ar, entries, can): - if not self.loc: - x = ar.loc[0] + ar.size[0] * 1.1 - y = ar.loc[1] - else: - x = self.loc[0] - y = self.loc[1] - - nr_rows = min(self.nr_rows, len(entries)) - nr_cols = (len(entries)-1) / nr_rows + 1 - - ymin = y - max_label_width = [0] * nr_cols - max_sample_width = [0] * nr_cols - heights = [0] * nr_rows - - for i in range(len(entries)): - l = entries[i] - (col, row) = divmod(i, nr_rows) - max_label_width[col] = max(l.label_width(), max_label_width[col]) - max_sample_width[col] = max(l.sample_width(), max_sample_width[col]) - heights[row] = max(l.height(), heights[row]) - - for h in heights: - y += h - y += self.inter_row_sep * (nr_rows - 1) - ymax = y - - tot_width = self.inter_col_sep * (nr_cols -1) - for w in max_label_width: - tot_width += w - for w in max_sample_width: - tot_width += w - - can.rectangle(self.frame_line_style, self.frame_fill_style, - x - self.left_fudge, - ymin - self.bottom_fudge, - x + tot_width + self.right_fudge, - ymax + self.top_fudge, - self.shadow) - - for col in range(nr_cols): - this_y = y - this_x = x - for row in range(nr_rows): - idx = col * nr_rows + row - if idx >= len(entries): - continue - this_y -= heights[row] - l = entries[idx] - if row != 0: - this_y -= self.inter_row_sep - - l.draw(ar, can, this_x, this_x + max_sample_width[col], this_y) - x += max_label_width[col] + max_sample_width[col] + self.inter_col_sep - - diff --git a/bin/pychart/legend_doc.py b/bin/pychart/legend_doc.py deleted file mode 100644 index 2e642fc3b13..00000000000 --- a/bin/pychart/legend_doc.py +++ /dev/null @@ -1,28 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -inter_col_sep(type:length in points (\\xref{unit})) default="Space between each column in the legend.". -loc(type:(x,y)) default="Bottom-left corner of the legend. - The default location of a legend is the bottom-right end of the chart.". -bottom_fudge(type:length in points (\\xref{unit})) default="Amount of space below the last line.". -frame_line_style(type:line_style.T) default="". -nr_rows(type:int) default="Number of rows in the legend. If the number of plots in a chart is larger than nr_rows, multiple columns are created in the legend.". -right_fudge(type:length in points (\\xref{unit})) default="Amount of space right of the legend.". -inter_row_sep(type:length in points (\\xref{unit})) default="Space between each row in the legend.". -top_fudge(type:length in points (\\xref{unit})) default="Amount of space above the first line.". -left_fudge(type:length in points (\\xref{unit})) default="Amount of space left of the legend.". -shadow(type:) default="The value is either None or a tuple. When non-None, -a drop-shadow is drawn beneath the object. X-off, and y-off specifies the -offset of the shadow relative to the object, and fill specifies the -style of the shadow (@pxref{module-fill-style}).". -frame_fill_style(type:fill_style.T) default="". -""" - -doc_entry="""Attributes supported by this class are: -line_len(type:length in points (\\xref{unit})) default="Length of the sample line for line plots. If omitted, it is set to be theme.default_font_size". -label(type:str) default="". -rect_size(type:length in points (\\xref{unit})) default="Size of the sample 'blob' for bar range charts. If omitted, it is set to be 70% of theme.default_size". -tick_mark(type:tick_mark.T) default="". -line_style(type:line_style.T) default="". -fill_style(type:fill_style.T) default="". -""" - diff --git a/bin/pychart/line_plot.py b/bin/pychart/line_plot.py deleted file mode 100644 index 81b995c86e8..00000000000 --- a/bin/pychart/line_plot.py +++ /dev/null @@ -1,150 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import tick_mark -import line_style -import pychart_util -import error_bar -import chart_object -import legend -import object_set -import line_plot_doc -import theme -from pychart_types import * -from types import * - -default_width = 1.2 -line_style_itr = None - - -_keys = { - "data" : (AnyType, None, pychart_util.data_desc), - "label": (StringType, "???", pychart_util.label_desc), - "data_label_offset": (CoordType, (0, 5), - """The location of data labels relative to the sample point. Meaningful only when data_label_format != None."""), - "data_label_format": (FormatType, None, - """The format string for the label printed - beside a sample point. - It can be a `printf' style format string, or - a two-parameter function that takes the (x, y) - values and returns a string. """ - + pychart_util.string_desc), - "xcol" : (IntType, 0, pychart_util.xcol_desc), - "ycol": (IntType, 1, pychart_util.ycol_desc), - "y_error_minus_col": (IntType, 2, - """The column (within "data") from which the depth of the errorbar is extracted. Meaningful only when error_bar != None. <>"""), - "y_error_plus_col": (IntType, -1, - """The column (within "data") from which the height of the errorbar is extracted. Meaningful only when error_bar != None. <>"""), - "y_qerror_minus_col": (IntType, -1, "<>"), - "y_qerror_plus_col": (IntType, -1, "<>"), - - "line_style": (line_style.T, lambda: line_style_itr.next(), pychart_util.line_desc, - "By default, a style is picked from standard styles round-robin. <>"), - - "tick_mark": (tick_mark.T, None, pychart_util.tick_mark_desc), - "error_bar": (error_bar.T, None, - "The style of the error bar. <>"), - } - -class T(chart_object.T): - __doc__ = line_plot_doc.doc - keys = _keys - def check_integrity(self): - self.type_check() - -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def get_data_range(self, which): - if which == 'X': - return pychart_util.get_data_range(self.data, self.xcol) - else: - return pychart_util.get_data_range(self.data, self.ycol) - def get_legend_entry(self): - if self.label: - return legend.Entry(line_style=self.line_style, - tick_mark=self.tick_mark, - fill_style=None, - label=self.label) - return None - - def draw(self, ar, can): - - # Draw the line - - clipbox = theme.adjust_bounding_box([ar.loc[0], ar.loc[1], - ar.loc[0] + ar.size[0], - ar.loc[1] + ar.size[1]]); - - can.clip(clipbox[0],clipbox[1],clipbox[2],clipbox[3]) - if self.line_style: - points = [] - for pair in self.data: - yval = pychart_util.get_sample_val(pair, self.ycol) - xval = pair[self.xcol] - if None not in (xval, yval): - points.append((ar.x_pos(xval), ar.y_pos(yval))) - can.lines(self.line_style, points) - can.endclip() - - # Draw tick marks and error bars - can.clip(ar.loc[0] - 10, ar.loc[1] - 10, - ar.loc[0] + ar.size[0] + 10, - ar.loc[1] + ar.size[1] + 10) - for pair in self.data: - x = pair[self.xcol] - y = pychart_util.get_sample_val(pair, self.ycol) - if None in (x, y): continue - - x_pos = ar.x_pos(x) - y_pos = ar.y_pos(y) - - if self.error_bar: - plus = pair[self.y_error_plus_col or self.y_error_minus_col] - minus = pair[self.y_error_minus_col or self.y_error_plus_col] - if self.y_qerror_minus_col or self.y_qerror_plus_col: - q_plus = pair[self.y_qerror_plus_col or self.y_qerror_minus_col] - q_minus = pair[self.y_qerror_minus_col or self.y_qerror_plus_col] - if None not in (minus,plus,q_minus,q_plus): - self.error_bar.draw(can, (x_pos, y_pos), - ar.y_pos(y - minus), - ar.y_pos(y + plus), - ar.y_pos(y - q_minus), - ar.y_pos(y + q_plus)) - else: - if None not in (minus,plus): #PDS - self.error_bar.draw(can, (x_pos, y_pos), - ar.y_pos(y - minus), - ar.y_pos(y + plus)) - - if self.tick_mark: - self.tick_mark.draw(can, x_pos, y_pos) - if self.data_label_format: - can.show(x_pos + self.data_label_offset[0], - y_pos + self.data_label_offset[1], - "/hC" + pychart_util.apply_format(self.data_label_format, (x, y), 1)) - - can.endclip() - -def init(): - global line_style_itr - line_styles = object_set.T() - for org_style in line_style.standards.list(): - style = line_style.T(width = default_width, color = org_style.color, - dash = org_style.dash) - line_styles.add(style) - - line_style_itr = line_styles.iterate() - -theme.add_reinitialization_hook(init) - diff --git a/bin/pychart/line_plot_doc.py b/bin/pychart/line_plot_doc.py deleted file mode 100644 index a1dfe17749c..00000000000 --- a/bin/pychart/line_plot_doc.py +++ /dev/null @@ -1,25 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -y_qerror_minus_col(type:int) default="<>". -xcol(type:int) default="The column, within attribute "data", from which the X values of sample points are extracted. <>". -error_bar(type:error_bar.T) default="The style of the error bar. <>". -data_label_format(type:printf format string) default="The format string for the label printed - beside a sample point. - It can be a `printf' style format string, or - a two-parameter function that takes the (x, y) - values and returns a string. The appearance of the string produced here can be -controlled using escape sequences. <>". -ycol(type:int) default="The column, within attribute "data", from which the Y values of sample points are extracted. <>". -label(type:str) default="The label to be displayed in the legend. <>, <>". -line_style(type:line_style.T) default="The style of the line. ". - By default, a style is picked from standard styles round-robin. - See also pychart.line_style - -y_error_plus_col(type:int) default="The column (within "data") from which the height of the errorbar is extracted. Meaningful only when error_bar != None. <>". -tick_mark(type:tick_mark.T) default="Tick marks to be displayed at each sample point. <>". -y_error_minus_col(type:int) default="The column (within "data") from which the depth of the errorbar is extracted. Meaningful only when error_bar != None. <>". -data_label_offset(type:(x,y)) default="The location of data labels relative to the sample point. Meaningful only when data_label_format != None.". -data(type:any) default="Specifies the data points. <>". -y_qerror_plus_col(type:int) default="<>". -""" - diff --git a/bin/pychart/line_style.py b/bin/pychart/line_style.py deleted file mode 100644 index c47e09cf669..00000000000 --- a/bin/pychart/line_style.py +++ /dev/null @@ -1,158 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import color -import pychart_util -import chart_object -import object_set -import theme -import line_style_doc -from pychart_types import * -from types import * - -_keys = { - "width" : (UnitType, theme.default_line_width, "Width of the line, in points."), - "color": (color.T, color.default, "The color of the line."), - "dash" : (TupleType, None, - """The value - of None will draw a solid line. Otherwise, this - attribute specifies the style of dashed lines. - The 2N'th value specifies the length of the line (in points), - and 2N+1'th value specifies the length of the blank. - - For example, the dash style of (3,2,4,1) draws a dashed line that - looks like @samp{---__----_---__----_...}. - """), - "cap_style": (IntType, 0, - """Defines the style of the tip of the line segment. - 0: butt cap (square cutoff, with no projection beyond), - 1: round cap (arc), 2: projecting square cap - (square cutoff, but the line extends half the line width). - See also Postscript/PDF reference manual."""), - "join_style": (IntType, 0, - """Join style. 0: Miter join (sharp, pointed corners), - 1: round join (rounded corners), - 2: bevel join (flattened corners). - See also Postscript/PDF reference manual.""") - } - -class T(chart_object.T): - __doc__ = line_style_doc.doc - keys = _keys -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def __str__(self): - s = name_table().lookup(self) - if s: - return s - return "" \ - % (self.width, self.color, self.dash, self.cap_style, self.join_style) - -default = T(color=color.default) - -dash1 = 1.5,1.5 # - - - - -dash2 = 5,2,5,2 # -- -- -- -- -dash3 = 1,1 - -black = T(color=color.black) -black_dash1 = T(color=color.black, dash=dash1) -black_dash2 = T(color=color.black, dash=dash2) -black_dash3 = T(color=color.black, dash=dash3) - -gray70 = T(color=color.gray70) -gray70_dash1 = T(color=color.gray70, dash=dash1) -gray70_dash2 = T(color=color.gray70, dash=dash2) -gray70_dash3 = T(color=color.gray70, dash=dash3) - -gray10 = T(color=color.gray10) -gray10_dash1 = T(color=color.gray10, dash=dash1) -gray10_dash2 = T(color=color.gray10, dash=dash2) -gray10_dash3 = T(color=color.gray10, dash=dash3) - -gray50 = T(color=color.gray50) -gray50_dash1 = T(color=color.gray50, dash=dash1) -gray50_dash2 = T(color=color.gray50, dash=dash2) -gray50_dash3 = T(color=color.gray50, dash=dash3) - -gray60 = T(color=color.gray60) -gray60_dash1 = T(color=color.gray60, dash=dash1) -gray60_dash2 = T(color=color.gray60, dash=dash2) -gray60_dash3 = T(color=color.gray60, dash=dash3) - -gray90 = T(color=color.gray90) -gray90_dash1 = T(color=color.gray90, dash=dash1) -gray90_dash2 = T(color=color.gray90, dash=dash2) -gray90_dash3 = T(color=color.gray90, dash=dash3) - -gray30 = T(color=color.gray30) -gray30_dash1 = T(color=color.gray30, dash=dash1) -gray30_dash2 = T(color=color.gray30, dash=dash2) -gray30_dash3 = T(color=color.gray30, dash=dash3) - -white = T(color=color.white) -default = black - -red = T(color=color.red) -darkblue = T(color=color.darkblue) -darkseagreen = T(color=color.darkseagreen) -darkkhaki = T(color = color.darkkhaki) - -blue = T(color=color.blue) -green = T(color=color.green) - -red_dash1 = T(color=color.red, dash=dash1) -darkblue_dash1 = T(color=color.darkblue, dash=dash1) -darkseagreen_dash1 = T(color=color.darkseagreen, dash=dash1) -darkkhaki_dash1 = T(color=color.darkkhaki, dash=dash1) - -red_dash2 = T(color=color.red, dash=dash2) -darkblue_dash2 = T(color=color.darkblue, dash=dash2) -darkseagreen_dash2 = T(color=color.darkseagreen, dash=dash2) -darkkhaki_dash2 = T(color=color.darkkhaki, dash=dash2) - -standards = None -_name_table = None - -def init(): - global standards, _name_table - standards = object_set.T() - - if theme.use_color: - standards.add(black, red, darkblue, gray70, darkseagreen, - darkkhaki, gray30, - black_dash1, red_dash1, darkblue_dash1, gray70_dash1, - darkseagreen_dash1, darkkhaki_dash1, gray30_dash1, - black_dash2, red_dash2, darkblue_dash2, gray70_dash2, - darkseagreen_dash2, darkkhaki_dash2, gray30_dash2) - else: - standards.add(black, black_dash1, black_dash2, - gray70, gray70_dash1, gray70_dash2, - gray10, gray10_dash1, gray10_dash2, - gray50, gray50_dash1, gray50_dash2, - gray90, gray90_dash1, gray90_dash2, - gray30, gray30_dash1, gray30_dash2, - black_dash3, - gray70_dash3, gray10_dash3, gray50_dash3, gray90_dash3) - for style in standards.list(): - style.width = theme.default_line_width - _name_table = None - -def name_table(): - global _name_table - if not _name_table: - _name_table = pychart_util.symbol_lookup_table(globals(), standards) - return _name_table - -init() -theme.add_reinitialization_hook(init) diff --git a/bin/pychart/line_style_doc.py b/bin/pychart/line_style_doc.py deleted file mode 100644 index b936e805122..00000000000 --- a/bin/pychart/line_style_doc.py +++ /dev/null @@ -1,24 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -color(type:color.T) default="The color of the line.". -width(type:length in points (\\xref{unit})) default="Width of the line, in points.". -dash(type:tuple) default="The value - of None will draw a solid line. Otherwise, this - attribute specifies the style of dashed lines. - The 2N'th value specifies the length of the line (in points), - and 2N+1'th value specifies the length of the blank. - - For example, the dash style of (3,2,4,1) draws a dashed line that - looks like @samp{---__----_---__----_...}. - ". -cap_style(type:int) default="Defines the style of the tip of the line segment. - 0: butt cap (square cutoff, with no projection beyond), - 1: round cap (arc), 2: projecting square cap - (square cutoff, but the line extends half the line width). - See also Postscript/PDF reference manual.". -join_style(type:int) default="Join style. 0: Miter join (sharp, pointed corners), - 1: round join (rounded corners), - 2: bevel join (flattened corners). - See also Postscript/PDF reference manual.". -""" - diff --git a/bin/pychart/linear_coord.py b/bin/pychart/linear_coord.py deleted file mode 100644 index e54d1789f24..00000000000 --- a/bin/pychart/linear_coord.py +++ /dev/null @@ -1,37 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import coord -import math -import pychart_util - -class T(coord.T): - def get_canvas_pos(self, size, val, min, max2): - return size * (val - min) / max(float(max2 - min),0.01) - - def get_tics(self, min, max, interval): - v = [] - x = min - while x <= max: - v.append(x) - x += interval - return v - def get_min_max(self, dmin, dmax, interval): - if not interval: - if dmax == dmin: - interval = 10 - else: - interval = 10 ** (float(int(math.log(dmax-dmin)/math.log(10)))) - dmin = min(dmin, pychart_util.round_down(dmin, interval)) - dmax = max(dmax, pychart_util.round_up(dmax, interval) + interval/2.0) - return dmin, dmax, interval diff --git a/bin/pychart/log_coord.py b/bin/pychart/log_coord.py deleted file mode 100644 index e40308c9cf8..00000000000 --- a/bin/pychart/log_coord.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import coord -import math - -class T(coord.T): - def get_canvas_pos(self, size, val, min, max): - if val <= 0: - return 0 - xminl = math.log(min) - xmaxl = math.log(max) - vl = math.log(val) - return size * (vl-xminl) / float(xmaxl-xminl) - def get_tics(self, min, max, interval): - "Generate the list of places for drawing tick marks." - v = [] - if min <= 0: - raise Exception, "Min value (%s) < 0 in a log coordinate." % min - x = min - while x <= max: - v.append(x) - x = x * interval - return v - def get_min_max(self, dmin, dmax, interval): - interval = interval or 10 - dmin = max(0, dmin) # we can't have a negative value with a log scale. - v = 1.0 - while v > dmin: - v = v / interval - dmin = v - v = 1.0 - while v < dmax: - v = v * interval - dmax = v - - return dmin, dmax, interval - diff --git a/bin/pychart/object_set.py b/bin/pychart/object_set.py deleted file mode 100644 index 7d3c9ab70d7..00000000000 --- a/bin/pychart/object_set.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -class T(object): - def __init__(self, *objs): - self.__objs = list(objs) - def add(self, *objs): - self.__objs.extend(objs) - def add_objects(self, objs): - self.__objs.extend(objs) - def iterate(self): - return Iterator(self) - def list(self): - return self.__objs - def __getitem__(self, idx): - return self.__objs[idx] - def nth(self, idx): - return self.__objs[idx] - -class Iterator(object): - def __init__(self, set_): - self.__set = set_ - self.__idx = 0 - def reset(self): - self.__idx = 0 - def next(self): - val = self.__set.nth(self.__idx) - self.__idx += 1 - if self.__idx >= len(self.__set.list()): - self.__idx = 0 - return val - - diff --git a/bin/pychart/pdfcanvas.py b/bin/pychart/pdfcanvas.py deleted file mode 100644 index 0413e551946..00000000000 --- a/bin/pychart/pdfcanvas.py +++ /dev/null @@ -1,297 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import pychart_util -import string -import re -import math -import theme -import os -import basecanvas -from scaling import * - -try: - import zlib - _zlib_available_p = 1 -except: - _zlib_available_p = 0 - -class pdf_stream(object): - def __init__(self, fp): - self.fp = fp - self.off = 0 - def write(self, str): - self.fp.write(str) - self.off += len(str) - def tell(self): - return self.off - -def to_radian(deg): - return deg*2*math.pi / 360.0 - -class T(basecanvas.T): - def __init__(self, fname, compress_p_): - basecanvas.T.__init__(self) - self.__out_fname = fname - self.__reset_context() - self.__next_obj_id = 1 - self.__next_font_id = 1 - self.__obj_offsets = {} - self.__registered_fonts = {} - self.__lines = [] - self.__nr_gsave = 0 - - if compress_p_ and not _zlib_available_p: - pychart_util.warn("Zlib not available. Compression request ignored.\n") - compress_p_ = 0 - self.__compress_p = compress_p_ - - def __intern_font(self, name): - "Assign an ID to the font NAME. Return its ID." - if not self.__registered_fonts.has_key(name): - self.__registered_fonts[name] = self.__next_font_id - self.__next_font_id += 1 - return self.__registered_fonts[name] - - def __define_obj(self, fp, str): - obj_id = self.__next_obj_id - self.__next_obj_id += 1 - self.__obj_offsets[obj_id] = fp.tell() - fp.write("%d 0 obj\n%s\nendobj\n" % (obj_id, str)) - return obj_id - - def __define_stream_obj(self, fp, s): - if self.__compress_p: - p = zlib.compress(s) - return self.__define_obj(fp, "<>\nstream\n%sendstream" - % (len(p), p)) - else: - return self.__define_obj(fp, "<>\nstream\n%s\nendstream" - % (len(s), s)) - - def __define_font_obj(self, fp, name, font_id): - obj_id = self.__define_obj(fp, """<>""" % (font_id, name)) - return obj_id - - def __reset_context(self): - self.__font_name = None - self.__font_size = -1 - self.__line_style = None - self.__fill_color = None - self.__stroke_color = None - self.__mtx_pushed = 0 - - def newpath(self): - pass - def set_fill_color(self, color): - if self.__fill_color == color: - return - if color.r == color.g and color.r == color.b: - self.__write("%f g\n" % (color.r)) - self.__write("%f G\n" % (color.r)) - else: - self.__write("%f %f %f rg\n" % (color.r, color.g, color.b)) - self.__write("%f %f %f RG\n" % (color.r, color.g, color.b)) - self.__fill_color = color - def set_stroke_color(self, color): - self.set_fill_color(color) - return - - def __arcsub(self, x, y, radius, start, theta): - xcos = math.cos(to_radian(theta)) - xsin = math.sin(to_radian(theta)) - x0 = radius * xcos - y0 = radius * xsin - x1 = radius * (4-xcos)/3.0 - y1 = radius * (1-xcos)*(xcos-3)/(3*xsin) - - xx0, xy0 = pychart_util.rotate(x0, y0, start+theta) - xx1, xy1 = pychart_util.rotate(x1, -y1, start+theta) - xx2, xy2 = pychart_util.rotate(x1, y1, start+theta) - self.__write("%f %f %f %f %f %f c\n" % - (x+xx1, y+xy1, x+xx2, y+xy2, x+xx0, y+xy0)) - def path_arc(self, x, y, radius, ratio, start, end): - self.comment("PATHARC %f %f %f %f %f %f\n" - % (x, y, radius, ratio, start, end)) - step = 10 - if radius < 10: - step = 20 - if radius < 5: - step = 30 - if ratio != 1.0: - self.push_transformation((x, y), (1, ratio), None) - deg = start - while deg < end: - theta = min(step, end-deg) - self.__arcsub(x, y, radius, deg, theta/2) - deg += theta - self.pop_transformation() - else: - deg = start - while deg < end: - theta = min(step, end-deg) - self.__arcsub(x, y, radius, deg, theta/2) - deg += theta - self.comment("end PATHARC\n") - - def text_begin(self): - self.__write("BT ") - self.__font_name = None - self.__font_size = None - - def text_end(self): - self.__write("ET\n") - def text_moveto(self, x, y, angle): - if angle != None: - xcos = math.cos(to_radian(angle)) - xsin = math.sin(to_radian(angle)) - self.__write("%f %f %f %f %f %f Tm " % (xcos, xsin, -xsin, xcos, x, y)) - else: - self.__write("1 0 0 1 %f %f Tm " % (x, y)) - - def text_show(self, font_name, font_size, color, str): - if self.__font_name != font_name or self.__font_size != font_size: - font_id = self.__intern_font(font_name) - self.__write("/F%d %d Tf " % (font_id, font_size)) - self.__font_name = font_name - self.__font_size = font_size - self.set_fill_color(color) - self.__write("(%s) Tj " % (str)) - - def push_transformation(self, baseloc, scale, angle, in_text = 0): - if in_text: - op = "Tm" - else: - op = "cm" - self.gsave() - - if baseloc == None: - baseloc = (0,0) - - if angle != None: - radian = to_radian(angle) - self.__write("%f %f %f %f %f %f %s\n" % - (math.cos(radian), math.sin(radian), - -math.sin(radian), math.cos(radian), - baseloc[0], baseloc[1], op)) - if scale != None: - self.__write("%f 0 0 %f %f %f %s\n" % (scale[0], scale[1], - baseloc[0], - baseloc[1], op)) - - def pop_transformation(self, in_text = 0): - if not in_text: - self.grestore() - def closepath(self): - self.__write("h\n") - def clip_sub(self): - self.__write("W n\n") - def fill(self): - self.__write("f n\n") - def gsave(self): - self.__write("q\n") - def grestore(self): - self.__write("Q\n") - self.__reset_context() - - def moveto(self, x, y): - self.__write('%f %f m ' % (x, y)) - def lineto(self, x, y): - self.__write("%f %f l\n" % (x, y)) - def stroke(self): - self.__write("S\n") - - def set_line_style(self, style): - if (self.__line_style == style): - pass - else: - self.set_stroke_color(style.color) - self.__write("%f w " % nscale(style.width)) - if style.dash != None: - self.__write("[%s] 0 d\n" % - " ".join(map(str, nscale_seq(style.dash)))) - else: - self.__write("[] 0 d\n") - self.__write("%d j %d J\n" % (style.cap_style, style.join_style)) - self.__line_style = style - - def comment(self, str): - if not self.__compress_p: - self.__write("%%" + str + "\n") - - def verbatim(self, str): - self.__write(str) - - def __write(self, str): - self.__lines.append(str) - -# def setbb(self, xmin, ymin, xmax, ymax): -# self.__xmin = xmin -# self.__ymin = ymin -# self.__xmax = xmax -# self.__ymax = ymax - - def close(self): - basecanvas.T.close(self) - if self.__lines == []: - return - - _fp, need_close = self.open_output(self.__out_fname) - fp = pdf_stream(_fp) - - fp.write("%PDF-1.2\n") - - stream_obj_id = self.__define_stream_obj(fp, " ".join(self.__lines)) - - fontstr = "" - for font_name, font_id in self.__registered_fonts.items(): - obj_id = self.__define_font_obj(fp, font_name, font_id) - fontstr += "/F%d %d 0 R " % (font_id, obj_id) - - pages_obj_id = self.__define_obj(fp, " <>" % (self.__next_obj_id + 1)) - - bbox = theme.adjust_bounding_box([xscale(self.__xmin), yscale(self.__ymin), - xscale(self.__xmax), yscale(self.__ymax)]) - - page_obj_id = self.__define_obj(fp, """ <> ->> >>""" % (pages_obj_id, stream_obj_id, - bbox[0], bbox[1], bbox[2], bbox[3], fontstr)) - - info_str = "/Producer (%s)\n/CreationDate (%s)" % (self.creator, self.creation_date) - - if self.title: - info_str += "\n/Title (%s)" % (self.title, ) - if self.author: - info_str += "\n/Author (%s)" % (self.author, ) - - info_obj_id = self.__define_obj(fp, """<<%s>>""" % info_str) - catalog_obj_id = self.__define_obj(fp, """ <>""" % (pages_obj_id)) - - xref_offset = fp.tell() - fp.write("xref\n0 %d\n" % (len(self.__obj_offsets)+1)) - fp.write("0000000000 65535 f \n") - id = 1 - while id <= len(self.__obj_offsets): - fp.write("%010d 00000 n \n" % (self.__obj_offsets[id])) - id += 1 - fp.write("trailer << /Size %d /Root %d 0 R /Info %d 0 R\n>>\n" % (len(self.__obj_offsets)+1, catalog_obj_id, info_obj_id)) - fp.write("startxref\n%d\n%%%%EOF\n" % xref_offset) - - if need_close: - _fp.close() diff --git a/bin/pychart/pie_plot.py b/bin/pychart/pie_plot.py deleted file mode 100644 index c6f405a7ae9..00000000000 --- a/bin/pychart/pie_plot.py +++ /dev/null @@ -1,148 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import text_box -import fill_style -import line_style -import pychart_util -import chart_object -import arrow -import legend -import font -import pie_plot_doc -import theme - -from pychart_types import * -from types import * - -class T(chart_object.T): - __doc__ = pie_plot_doc.doc - keys = { - "start_angle" : (NumType, 90, - """The angle at which the first item is drawn."""), - "center" : (CoordType, None, "The location of the center of the pie."), - "radius" : (UnitType, None, "The radius of the pie."), - "line_style" : (line_style.T, line_style.default, "The style of the outer edge of each pie slice."), - - "fill_styles" : (ListType, fill_style.standards.list(), - """The fill style of each item. The length of the - list should be equal to the length of the data. - """), - "arc_offsets" : (ListType, None, - """You can draw each pie "slice" shifted off-center. - This attribute, if non-None, - must be a number sequence whose length is equal to - the number of pie slices. The Nth value in arc_offsets - specify the amount of offset - (from the center of the circle) - for the Nth slice. - The value of None will draw all the slices - anchored at the center. - """ - ), - "data" : (AnyType, None, pychart_util.data_desc), - "label_format" : (FormatType, "%s", - "Format string of the label"), - "label_col" : (IntType, 0, - """The column, within "data", from which the labels of items are retrieved."""), - "data_col": (IntType, 1, - """ The column, within "data", from which the data values are retrieved."""), - "label_offset": (UnitType, None, "The distance from the center of each label."), - "arrow_style": (arrow.T, None, - """The style of arrow that connects a label - to the corresponding "pie"."""), - "label_line_style": (line_style.T, None, "The style of the frame surrounding each label."), - "label_fill_style": (fill_style.T, fill_style.default, "The fill style of the frame surrounding each label."), - "shadow": (ShadowType, None, pychart_util.shadow_desc) - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def _total(self): - v = 0 - for val in self.data: - v += val[self.data_col] - return v - - def check_integrity(self): - self.type_check() - def get_data_range(self, which): - return (0, 1) - def get_legend_entry(self): - legends = [] - i = 0 - for val in self.data: - fill = self.fill_styles[i] - i = (i + 1) % len(self.fill_styles) - legends.append(legend.Entry(line_style=self.line_style, - fill_style=fill, - label=val[self.label_col])) - return legends - - def draw(self, ar, can): - center = self.center - if not center: - center = (ar.loc[0] + ar.size[0]/2.0, - ar.loc[1] + ar.size[1]/2.0) - radius = self.radius - if not radius: - radius = min(ar.size[0]/2.0, ar.size[1]/2.0) * 0.5 - - label_offset = radius + (self.label_offset or radius * 0.1) - - total = self._total() - i = 0 - cur_angle = self.start_angle - for val in self.data: - fill = self.fill_styles[i] - degree = 360 * float(val[self.data_col]) / float(total) - - off = (0, 0) - if len(self.arc_offsets) > i: - off = pychart_util.rotate(self.arc_offsets[i], 0, cur_angle - degree/2.0) - x_center = center[0]+ off[0] - y_center = center[1]+ off[1] - - can.ellipsis(self.line_style, fill, - x_center, y_center, radius, 1, - cur_angle - degree, cur_angle, - self.shadow) - - label = pychart_util.apply_format(self.label_format, val, - self.label_col) - if label != None: - (x_label, y_label) = pychart_util.rotate(label_offset, 0, cur_angle - degree/2.0) - (x_arrowtip, y_arrowtip) = pychart_util.rotate(radius, 0, cur_angle - degree/2.0) - # Labels on left side of pie need - # their text to avoid obscuring the pie - if x_label < 0: - x_label = x_label - font.text_width(label) - - t = text_box.T(loc = (x_label + x_center, y_label + y_center), - text = label, - line_style = self.label_line_style, - fill_style = self.label_fill_style) - if self.arrow_style: - t.add_arrow((x_arrowtip + x_center, y_arrowtip + y_center), - None, self.arrow_style) - - t.draw(can) - cur_angle = (cur_angle - degree) % 360 - i = (i + 1) % len(self.fill_styles) - - -def init(): - old_val = T.keys["fill_styles"] - T.keys["fill_styles"] = (old_val[0], fill_style.standards.list(), - old_val[2]) -theme.add_reinitialization_hook(init) diff --git a/bin/pychart/pie_plot_doc.py b/bin/pychart/pie_plot_doc.py deleted file mode 100644 index ae80d0a6b88..00000000000 --- a/bin/pychart/pie_plot_doc.py +++ /dev/null @@ -1,34 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -label_fill_style(type:fill_style.T) default="The fill style of the frame surrounding each label.". -center(type:(x,y)) default="The location of the center of the pie.". -arrow_style(type:arrow.T) default="The style of arrow that connects a label - to the corresponding "pie".". -start_angle(type:number) default="The angle at which the first item is drawn.". -label_format(type:printf format string) default="Format string of the label". -arc_offsets(type:list) default="You can draw each pie "slice" shifted off-center. - This attribute, if non-None, - must be a number sequence whose length is equal to - the number of pie slices. The Nth value in arc_offsets - specify the amount of offset - (from the center of the circle) - for the Nth slice. - The value of None will draw all the slices - anchored at the center. - ". -label_offset(type:length in points (\\xref{unit})) default="The distance from the center of each label.". -label_line_style(type:line_style.T) default="The style of the frame surrounding each label.". -label_col(type:int) default="The column, within "data", from which the labels of items are retrieved.". -data_col(type:int) default=" The column, within "data", from which the data values are retrieved.". -radius(type:length in points (\\xref{unit})) default="The radius of the pie.". -fill_styles(type:list) default="The fill style of each item. The length of the - list should be equal to the length of the data. - ". -line_style(type:line_style.T) default="The style of the outer edge of each pie slice.". -data(type:any) default="Specifies the data points. <>". -shadow(type:) default="The value is either None or a tuple. When non-None, -a drop-shadow is drawn beneath the object. X-off, and y-off specifies the -offset of the shadow relative to the object, and fill specifies the -style of the shadow (@pxref{module-fill-style}).". -""" - diff --git a/bin/pychart/pngcanvas.py b/bin/pychart/pngcanvas.py deleted file mode 100644 index b0e95960a06..00000000000 --- a/bin/pychart/pngcanvas.py +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import os -import gs_frontend -import theme - -class T(gs_frontend.T): - def close(self): - gs_frontend.T.close(self) - if self.__output_lines == []: - return - - if theme.use_color: - gs_args = "-sDEVICE=png256 -dTextAlphaBits=4 -q -dNOPAUSE" #PDS - else: - gs_args = "-sDEVICE=pnggray -dTextAlphaBits=4 -q -dNOPAUSE" #PDS - - - temp_fname = None # the temporary file desc. - out_fd = None # the final destination. - - if self.__out_fname and isinstance(self.__out_fname, str): - gs_args += " -sOutputFile=%s" % self.__out_fname - else: - if not self.__out_fname: - out_fd = sys.stdout - else: - if not hasattr(self.__out_fname, "write"): - raise Exception, "Expecting either a filename or a file-like object, but got %s" % self.__out_fname - out_fd = self.__out_fname - import tempfile - temp_fname = tempfile.mktemp() - gs_args += " -sOutputFile=%s" % temp_fname - self.start_gs(gs_args) - self.close_gs() - - if temp_fname: - temp_fd = file(temp_fname, 'rb') - out_fd.write(temp_fd.read()) - temp_fd.close() - - - diff --git a/bin/pychart/pscanvas.py b/bin/pychart/pscanvas.py deleted file mode 100644 index 7b92b6d6ce2..00000000000 --- a/bin/pychart/pscanvas.py +++ /dev/null @@ -1,257 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import re -import theme -import version -import basecanvas -from scaling import * - -comment_p = 0 - -class T(basecanvas.T): - def __init__(self, fname): - basecanvas.T.__init__(self) - self.__out_fname = fname - self.__reset_context() - self.__output_lines = [] - self.__nr_gsave = 0 - self.__font_ids = {} - self.__nr_fonts = 0 - - def __reset_context(self): - self.__font_name = None - self.__font_size = -1 - self.__line_style = None - self.__color = None - self.__mtx_pushed = 0 - self.__txtmtx_pushed = 0 - - def __intern_font(self, name): - if self.__font_ids.has_key(name): - return self.__font_ids[name] - id = "F%d" % self.__nr_fonts - self.__nr_fonts += 1 - self.__font_ids[name] = id - return id - - def newpath(self): - self.__write("N\n") - def stroke(self): - self.__write("ST\n") - def closepath(self): - self.__write("CP\n") - def moveto(self, x, y): - self.__write('%g %g M\n' % (x, y)) - - def set_fill_color(self, color): - if self.__color == color: - pass - else: - if color.r == color.g and color.r == color.b: - self.__write("%g SG\n" % color.r) - else: - self.__write("%g %g %g SC\n" % (color.r, color.g, color.b)) - self.__color = color - def set_stroke_color(self, color): - self.set_fill_color(color) - - def set_line_style(self, style): - self.set_stroke_color(style.color) - if (self.__line_style == style): - pass - else: - self.__write("%g %d %d " % (nscale(style.width), - style.cap_style, style.join_style)) - if style.dash != None: - self.__write("[%s] 0 SLD " % - " ".join(map(str, nscale_seq(style.dash)))) - else: - self.__write("SL ") - self.__line_style = style - - def gsave(self): - self.__nr_gsave += 1 - self.__write("GS\n") - def grestore(self): - self.__write("GR\n") - self.__nr_gsave -= 1 - self.__reset_context() - - def clip_sub(self): - self.__write("clip\n") - - def path_arc(self, x, y, radius, ratio, start_angle, end_angle): - self.push_transformation((x, y), (1, ratio), None) - self.__write("0 0 %g %g %g arc\n" % (radius, start_angle, end_angle)) - self.pop_transformation() - - def curveto(self, a,b,c,d,e,f): - self.__write("%g %g %g %g %g %g curveto\n" % (a,b,c,d,e,f)) - - def push_transformation(self, baseloc, scale, angle, in_text=0): - self.__mtx_pushed += 1 - self.__write("GB\n") - if baseloc != None: - self.__write("%g %g T\n" % (baseloc[0], baseloc[1])) - if angle != None and angle != 0: - self.__write("%g R\n" % (angle)) - if scale != None: - self.__write("%g %g scale\n" % (scale[0], scale[1])) - def pop_transformation(self, in_text=0): - if self.__mtx_pushed == 0: - raise ValueError, "mtx not pushed" - self.__mtx_pushed -= 1 - self.__write("GE\n") - def text_begin(self): - self.__txtmtx_pushed += 1 - self.__write("TB\n") - def text_end(self): - self.__txtmtx_pushed -= 1 - self.__write("TE\n") - def text_moveto(self, x, y, angle): - self.__write("%g %g T " % (x,y)) - if angle != None and angle != 0: - self.__write("%g R " % angle) - self.moveto(0, 0) - - def text_show(self, font_name, size, color, str): - self.set_fill_color(color) - if (self.__font_name == font_name and self.__font_size == size): - pass - else: - id = self.__intern_font(font_name) - self.__write("%g %s\n" % (size, id)) - self.__font_name = font_name - self.__font_size = size - self.__write("(%s) show\n" % (str)) - - def _path_polygon(self, points): - if (len(points) == 4 - and points[0][0] == points[1][0] - and points[2][0] == points[3][0] - and points[0][1] == points[3][1] - and points[1][1] == points[2][1]): - # a rectangle. - (xmin, ymin, xmax, ymax) = basecanvas._compute_bounding_box(points) - if basecanvas.invisible_p(xmax, ymax): - return - self.setbb(xmin, ymin) - self.setbb(xmax, ymax) - self.__write("%g %g %g %g RECT\n" % \ - (xscale(points[0][0]), yscale(points[0][1]), - xscale(points[2][0]), yscale(points[2][1]))) - else: - basecanvas.T._path_polygon(self, points) - - def lineto(self, x, y): - self.__write("%g %g L\n" % (x, y)) - def fill(self): - self.__write("fill\n") - def comment(self, str): - if comment_p: - self.verbatim("%" + str) - def verbatim(self, str): - self.__write(str) - - def close(self): - basecanvas.T.close(self) - if self.__output_lines == []: - return - - fp, need_close = self.open_output(self.__out_fname) - - if self.__nr_gsave != 0: - raise Exception, "gsave misnest (%d)" % (self.__nr_gsave) - self.write_preamble(fp) - - fp.writelines(self.__output_lines) - fp.writelines(["showpage end\n", - "%%Trailer\n", - "%%EOF\n"]) - if need_close: - fp.close() - - def __write(self, str): - self.__output_lines.append(str) - - def writelines(self, l): - self.__output_lines.extend(l) - - def write_preamble(self, fp): - bbox = [self.__xmin-1, self.__ymin-1, self.__xmax+1, self.__ymax+1] - fp.write("%!PS-Adobe-2.0 EPSF-1.2\n") - fp.write("%%Title: " + self.title + "\n") - fp.write("%%Creator: " + self.creator + "\n") - if self.author: - fp.write("%%Author: " + self.author + "\n") - fp.write("%%CreationDate: " + self.creation_date + "\n") - fp.write("%%DocumentFonts: " + " ".join(self.__font_ids.keys()) + "\n") - fp.write("%%Pages: 1\n") - - bbox = theme.adjust_bounding_box(bbox) - - fp.write("%%%%BoundingBox: %d %d %d %d\n" % \ - (round(xscale(bbox[0])), - round(yscale(bbox[1])), - round(xscale(bbox[2])), - round(yscale(bbox[3])))) - fp.write("%%EndComments\n") - if self.aux_comments != "": - for line in self.aux_comments.split("\n"): - fp.write("% " + line + "\n") - - fp.write(preamble_text) - for name, id in self.__font_ids.items(): - fp.write("/%s {/%s findfont SF} def\n" % (id, name)) - fp.write("%%EndProlog\n%%Page: 1 1\n") - - -preamble_text=""" -40 dict begin -/RECT {4 dict begin - /y2 exch def - /x2 exch def - /y1 exch def - /x1 exch def - newpath x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath - end -} def - -/SF {exch scalefont setfont} def -/TB {matrix currentmatrix} def -/TE {setmatrix} def -/GB {matrix currentmatrix} def -/GE {setmatrix} def -/SG {1 1 1 setrgbcolor setgray} def -/SC {1 setgray setrgbcolor} def -/SL {[] 0 setdash setlinejoin setlinecap setlinewidth} def -/SLD {setdash setlinejoin setlinecap setlinewidth} def -/M {moveto} def -/L {lineto} def -/T {translate} def -/R {rotate} def -/N {newpath} def -/ST {stroke} def -/CP {closepath} def -/GR {grestore} def -/GS {gsave} def -""" - -# SL: set line style. -# width [dash] x linecap linejoin SL -> - -# SF: set font. -# name size SF -> - diff --git a/bin/pychart/pychart_types.py b/bin/pychart/pychart_types.py deleted file mode 100644 index 269b83ef31d..00000000000 --- a/bin/pychart/pychart_types.py +++ /dev/null @@ -1,70 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import pychart_util -import types -AnyType = 9998 - -def CoordType(val): - if type(val) != types.TupleType and type(val) != types.ListType: - return (" not a valid coordinate.") - if len(val) != 2: - return "Coordinate must be a pair of numbers.\n" - if val[0] != None: - error = NumType(val[0]) - if error: return error - if val[1] != None: - error = NumType(val[1]) - if error: return error - return None - -def IntervalType(val): - if type(val) in (types.IntType, types.LongType, - types.FloatType, types.FunctionType): - return None - return "Expecting a number or a function" - -def CoordOrNoneType(val): - if type(val) not in (types.TupleType, types.ListType): - return "Expecting a tuple or a list." - if len(val) != 2: - return "Coordinate must be a pair of numbers.\n" - for v in val: - if v != None and NumType(val[0]) != None: - return "Expecting a pair of numbers" - return None - -def NumType(val): - if type(val) in (types.IntType, types.LongType, types.FloatType): - return None - else: - return "Expecting a number, found \"" + str(val) + "\"" - -def UnitType(val): - if type(val) in (types.IntType, types.LongType, types.FloatType): - return None - else: - return "Expecting a unit, found \"" + str(val) + "\"" - -def ShadowType(val): - if type(val) not in (types.TupleType, types.ListType): - return "Expecting tuple or list." - if len(val) != 3: - return "Expecting (xoff, yoff, fill)." - return None - -def FormatType(val): - if type(val) in (types.StringType, types.FunctionType): - return None - return "Format must be a string or a function" - diff --git a/bin/pychart/pychart_util.py b/bin/pychart/pychart_util.py deleted file mode 100644 index a7c0eace35d..00000000000 --- a/bin/pychart/pychart_util.py +++ /dev/null @@ -1,142 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import math -import types -import traceback -from types import * - -def inch_to_point(inch): - return inch * 72.0 -def point_to_inch(pt): - return float(pt) / 72.0 - -def rotate(x, y, degree): - """Rotate a coordinate around point (0,0). - - x and y specify the coordinate. - - degree is a number from 0 to 360. - Returns a new coordinate. - """ - radian = float(degree) * 2 * math.pi / 360.0 - newx = math.cos(radian) * x - math.sin(radian) * y - newy = math.sin(radian) * x + math.cos(radian) * y - return (newx, newy) - -debug_level = 1 - -def warn(*strs): - for s in strs: - sys.stderr.write(str(s)) - sys.stderr.write(" ") - sys.stderr.write("\n") - -def info(*strs): - if debug_level < 100: - return - for s in strs: - sys.stderr.write(str(s)) - sys.stderr.write("\n") - -def get_sample_val(l, col): - if len(l) <= col: - return None - return l[col] - -def get_data_list(data, col): - # data = [ elem[col] for elem in data if elem[col] != None ] - r = [] - for item in data: - val = get_sample_val(item, col) - if val != None: - r.append(val) - return r - -def get_data_range(data, col): - data = get_data_list(data, col) - for item in data: - if type(item) not in (types.IntType, types.LongType, types.FloatType): - raise TypeError, "Non-number passed to data: %s" % (data) - return (min(data), max(data)) - -def round_down(val, bound): - return int(val/float(bound)) * bound - -def round_up(val, bound): - return (int((val-1)/float(bound))+1) * bound - - -# -# Attribute type checking stuff -# - -def new_list(): - return [] - -def union_dict(dict1, dict2): - dict = dict1.copy() - dict.update(dict2) - return dict - -def TextVAlignType(val): - if val in ('T', 'B', 'M', None): - return None - return "Text vertical alignment must be one of T(op), B(ottom), or M(iddle).\n" - -def TextAlignType(val): - if val in ('C', 'R', 'L', None): - return None - return "Text horizontal alignment must be one of C(enter), R(ight), or L(eft)." - -def apply_format(format, val, defaultidx): - if format == None: - return None - elif type(format) == StringType: - return format % val[defaultidx] - else: - return apply(format, val) - - -data_desc = "Specifies the data points. <>" -label_desc = "The label to be displayed in the legend. <>, <>" -xcol_desc = """The column, within attribute "data", from which the X values of sample points are extracted. <>""" -ycol_desc = """The column, within attribute "data", from which the Y values of sample points are extracted. <>""" -tick_mark_desc = "Tick marks to be displayed at each sample point. <>" -line_desc="The style of the line. " - -def interval_desc(w): - return "When the value is a number, it specifies the interval at which %s are drawn. Otherwise, the value must be a function that takes no argument and returns the list of numbers. The return value specifies the X or Y points at which %s are drawn." % (w,w) - -shadow_desc = """The value is either None or a tuple. When non-None, -a drop-shadow is drawn beneath the object. X-off, and y-off specifies the -offset of the shadow relative to the object, and fill specifies the -style of the shadow (@pxref{module-fill-style}).""" - -string_desc = """The appearance of the string produced here can be -controlled using escape sequences. <>""" - -# -# - -class symbol_lookup_table: - def __init__(self, dict, objs): - self.names = {} - for name, val in dict.items(): - for obj in objs.list(): - if val == obj: - self.names[val] = name - break - def lookup(self, obj): - if self.names.has_key(obj): - return self.names[obj] - return None diff --git a/bin/pychart/range_plot.py b/bin/pychart/range_plot.py deleted file mode 100644 index 1fe64338244..00000000000 --- a/bin/pychart/range_plot.py +++ /dev/null @@ -1,144 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import line_style -import pychart_util -import chart_object -import fill_style -import legend -import range_plot_doc -from pychart_types import * -from types import * -from scaling import * - - -class T(chart_object.T): - __doc__ = range_plot_doc.doc - keys = { - "data" : (AnyType, None, pychart_util.data_desc), - "label": (StringType, "???", pychart_util.label_desc), - "xcol" : (IntType, 0, pychart_util.xcol_desc), - "min_col": (IntType, 1, - "The lower bound of the sweep is extracted from " - + "this column of data."), - "max_col": (IntType, 2, - "The upper bound of the sweep is extracted from " - + "this column of data."), - "line_style": (line_style.T, line_style.default, - "The style of the boundary line."), - "fill_style": (fill_style.T, fill_style.default, - ""), - } - -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - - def check_integrity(self): - self.type_check() - def get_data_range(self, which): - if which == 'X': - return pychart_util.get_data_range(self.data, self.xcol) - else: - ymax = (pychart_util.get_data_range(self.data, self.max_col))[1] - ymin = (pychart_util.get_data_range(self.data, self.min_col))[0] - return (ymin, ymax) - def get_legend_entry(self): - if self.label: - return legend.Entry(line_style=self.line_style, - fill_style=self.fill_style, - label=self.label) - return None - - def draw(self, ar, can): - - prevPair = None - - xmin=999999 - xmax=-999999 - ymin=999999 - ymax=-999999 - - # Draw the boundary in a single stroke. - can.gsave() - can.newpath() - for pair in self.data: - x = pair[self.xcol] - y = pychart_util.get_sample_val(pair, self.max_col) - if y == None: - continue - - xmin = min(xmin, ar.x_pos(x)) - xmax = max(xmax, ar.x_pos(x)) - ymin = min(ymin, ar.y_pos(y)) - ymax = max(ymax, ar.y_pos(y)) - if prevPair != None: - can.lineto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - else: - can.moveto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - prevPair = pair - - for i in range(len(self.data)-1, -1, -1): - pair = self.data[i] - x = pair[self.xcol] - y = pychart_util.get_sample_val(pair, self.min_col) - if None in (x, y): - continue - - xmin = min(xmin, ar.x_pos(x)) - xmax = max(xmax, ar.x_pos(x)) - ymin = min(ymin, ar.y_pos(y)) - ymax = max(ymax, ar.y_pos(y)) - can.lineto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - can.closepath() - - # create a clip region, and fill it. - can.clip_sub() - can.fill_with_pattern(self.fill_style, xmin, ymin, xmax, ymax) - can.grestore() - - if self.line_style: - # draw the boundary. - prevPair = None - can.newpath() - can.set_line_style(self.line_style) - for pair in self.data: - x = pair[self.xcol] - y = pychart_util.get_sample_val(pair, self.min_col) - if None in (x, y): - continue - - if prevPair != None: - can.lineto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - else: - can.moveto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - prevPair = pair - can.stroke() - - prevPair = None - can.newpath() - can.set_line_style(self.line_style) - for pair in self.data: - x = pair[self.xcol] - y = pychart_util.get_sample_val(pair, self.max_col) - if y == None: - continue - - if prevPair != None: - can.lineto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - else: - can.moveto(xscale(ar.x_pos(x)), yscale(ar.y_pos(y))) - prevPair = pair - can.stroke() - - diff --git a/bin/pychart/range_plot_doc.py b/bin/pychart/range_plot_doc.py deleted file mode 100644 index 6dcf4250f0b..00000000000 --- a/bin/pychart/range_plot_doc.py +++ /dev/null @@ -1,11 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -xcol(type:int) default="The column, within attribute "data", from which the X values of sample points are extracted. <>". -max_col(type:int) default="The upper bound of the sweep is extracted from this column of data.". -min_col(type:int) default="The lower bound of the sweep is extracted from this column of data.". -label(type:str) default="The label to be displayed in the legend. <>, <>". -line_style(type:line_style.T) default="The style of the boundary line.". -fill_style(type:fill_style.T) default="". -data(type:any) default="Specifies the data points. <>". -""" - diff --git a/bin/pychart/rose_plot.py b/bin/pychart/rose_plot.py deleted file mode 100644 index dcfd06074d4..00000000000 --- a/bin/pychart/rose_plot.py +++ /dev/null @@ -1,143 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import canvas -import fill_style -import line_style -import pychart_util -import chart_object -import legend -import font -import color -from pychart_types import * - -class T(chart_object.T): - """Plots sector diagram which can be superimposed on one another. - Sector diagrams are also known as wind roses""" - keys = { - "start_angle" : (NumType, 90, ""), # top of chart (north) - "center" : (CoordType, None, ""), - "base_radius" : (NumType, None, ""), - "line_style" : (line_style.T, line_style.T(color=color.black, width=0.3), ""), - "fill_styles" : (list, fill_style.standards.list()[:], - """The fill style of each item. The length of the - list should be equal to the length of the data. - """), - "sector_centred":(int, 1, - """Bool indicating whether the sectors should be centred on each sector_width(e.g. on 0)"""), - "dir_offset": (UnitType, None, - """The distance between the directions and the outermost circle. Defaults fine for most cases"""), - "data" : (AnyType, None, pychart_util.data_desc), - "label_col" : (int, 0, - """The column, within "data", from which the labels of items are retrieved."""), - "data_col": (int, 1, - """ The column, within "data", from which the data values are retrieved."""), - "dir_line_style": (line_style.T, None, ""), - "dir_fill_style": (fill_style.T, fill_style.default, ""), - "shadow": (ShadowType, None, pychart_util.shadow_desc), - "sector_width": (int, None, ""), # automatically generated - } - - def __init__(self, colour=True, **args): - chart_object.T.init(self, args) - if colour: - # the theme.color flag does not seem to affect the fill_style.standards, - #besides, I want the first two colors to resemble those of gnuplot's postscript terminal - self.fill_styles = [fill_style.Plain(bgcolor=color.red), - fill_style.Plain(bgcolor=color.green), - fill_style.Plain(bgcolor=color.blue), - fill_style.Plain(bgcolor=color.magenta)] - - def check_integrity(self): - nSectors = len(self.data[0][self.data_col]) - if (360%nSectors != 0): - raise Exception('Length of dataset ' + str(nSectors) + ' not a divisor of 360 degrees!') - for dataset in self.data: - length = len(dataset[self.data_col]) - if length != nSectors: - raise Exception('Lengths of datasets given is different!') - for val in dataset[self.data_col]: - if (val < 0) | (val > 1): - raise Exception('Data value ' + str(val) + ' not between 0 and 1!') - self.sector_width = 360/nSectors - self.type_check() - - def get_data_range(self, which): - return (0, 1) - - def get_legend_entry(self): - legends = [] - i = 0 - for dataset in self.data: - fill = self.fill_styles[i] - i = (i + 1) % len(self.fill_styles) - legends.append(legend.Entry(line_style=self.line_style, - fill_style=fill, - label=dataset[self.label_col])) - return legends - - def draw(self, ar, can): - center = self.center - if not center: - center = (ar.loc[0] + ar.size[0]/2.0, - ar.loc[1] + ar.size[1]/2.0) - base_radius = self.base_radius # the maximum radius of a wedge - if not base_radius: - base_radius = min(ar.size[0]/2.0, ar.size[1]/2.0) #* 0.8 - - sector_decrement = 1./(len(self.data)*2) * self.sector_width # each following sector diagram will have its sector width decremented by half this amount (in degrees) - i = 0 - for dataset in self.data: - cur_angle = self.start_angle - if self.sector_centred: - cur_angle -= self.sector_width/2. - fill = self.fill_styles[i] - x_center = center[0] - y_center = center[1] - - if not i: # draw directions around sector diagram once off - dir_offset = base_radius + (self.dir_offset or base_radius * 0.04) - directions = ['N', 'E', 'S', 'W'] - angle = self.start_angle - - can.ellipsis(line_style.T(color=color.black, width=0.3, dash=line_style.dash1), None, - x_center, y_center, base_radius, 1, - 0, 360) # - - for d in directions: - x_label, y_label = pychart_util.rotate(dir_offset, 0, angle) # coords for bottom left corner of box - tw = font.text_width(d) - half = 1/3. # normal arithmetic does not seem to apply to these text_box objects... - if (angle == 0): # east - y_label -= font.text_height(d)[0]*half # move down half - elif (angle == -180): # west - y_label -= font.text_height(d)[0]*half # move down half - x_label -= font.text_width(d) # move left full - elif (angle == 90): # north - x_label -= font.text_height(d)[0]*half # move left half - elif (angle == -90): # south - y_label -= font.text_height(d)[0]*.8 # move down (couldn't figure out how to set this dynamically so I fudged...) - x_label -= font.text_height(d)[0]*half # move left half - canvas.show(x_label + x_center, y_label + y_center, d) - angle -= 360/len(directions) - - for val in dataset[self.data_col]: # now draw the sectors - radius = base_radius*val # scale the radius - start = cur_angle-self.sector_width+i*sector_decrement - stop = cur_angle-i*sector_decrement # these may seem confusing, but remember that we need to go counterclockwise - - can.ellipsis(self.line_style, fill, - x_center, y_center, radius, 1, start, stop, self.shadow) - cur_angle = (cur_angle - self.sector_width) % 360 # we want to go in anticlockwise direction (North, West, South, etc. as in meteorology) - i = (i + 1) % len(self.fill_styles) - diff --git a/bin/pychart/scaling.py b/bin/pychart/scaling.py deleted file mode 100644 index 1fe7359b9f7..00000000000 --- a/bin/pychart/scaling.py +++ /dev/null @@ -1,28 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import theme - -x_base = 300 -y_base = 300 - -def xscale(x): - return x * theme.scale_factor + x_base -def yscale(y): - return y * theme.scale_factor + y_base - -def nscale(x): - return x * theme.scale_factor -def nscale_seq(x): - return map(nscale, x) - diff --git a/bin/pychart/svgcanvas.py b/bin/pychart/svgcanvas.py deleted file mode 100644 index 8fdb40a7982..00000000000 --- a/bin/pychart/svgcanvas.py +++ /dev/null @@ -1,428 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys,string,re,math -from xml.dom.minidom import Document,Comment -import theme -import basecanvas -import version -from scaling import * - -# Note we flip all y-coords and negate all angles because SVG's coord -# system is inverted wrt postscript/PDF - note it's not enough to -# scale(1,-1) since that turns text into mirror writing with wrong origin - -_comment_p = 0 # whether comment() writes output - -# Convert a PyChart color object to an SVG rgb() value -def _svgcolor(color): # see color.py - return 'rgb(%d,%d,%d)' % tuple(map(lambda x:int(255*x), - [color.r,color.g,color.b])) - -# Take an SVG 'style' attribute string like 'stroke:none;fill:black' -# and parse it into a dictionary like {'stroke' : 'none', 'fill' : 'black'} -def _parseStyleStr(s): - styledict = {} - if s : - # parses L -> R so later keys overwrite earlier ones - for keyval in s.split(';'): - l = keyval.strip().split(':') - if l and len(l) == 2: styledict[l[0].strip()] = l[1].strip() - return styledict - -# Make an SVG style string from the dictionary described above -def _makeStyleStr(styledict): - s = '' - for key in styledict.keys(): - s += "%s:%s;"%(key,styledict[key]) - return s - -def _protectCurrentChildren(elt): - # If elt is a group, check to see whether there are any non-comment - # children, and if so, create a new group to hold attributes - # to avoid affecting previous children. Return either the current - # elt or the newly generated group. - if (elt.nodeName == 'g') : - for kid in elt.childNodes : - if kid.nodeType != Comment.nodeType: - g = elt.ownerDocument.createElement('g') - g.setAttribute('auto','') - if _comment_p: - g.appendChild(g.ownerDocument.createComment - ('auto-generated group')) - elt.appendChild(g) - elt = g - break - return elt - -class T(basecanvas.T): - def __init__(self, fname): - basecanvas.T.__init__(self) - self.__out_fname = fname - self.__xmin, self.__xmax, self.__ymin, self.__ymax = 0,0,0,0 - self.__doc = Document() - self.__doc.appendChild(self.__doc.createComment - ('Created by PyChart ' + version.version + ' ' + version.copyright)) - self.__svg = self.__doc.createElement('svg') # the svg doc - self.__doc.appendChild(self.__svg) - self.__defs = self.__doc.createElement('defs') # for clip paths - self.__svg.appendChild(self.__defs) - self.__currElt = self.__svg - self.gsave() # create top-level group for dflt styles - self._updateStyle(font_family = theme.default_font_family, - font_size = theme.default_font_size, - font_style = 'normal', - font_weight = 'normal', - font_stretch = 'normal', - fill = 'none', - stroke = 'rgb(0,0,0)', #SVG dflt none, PS dflt blk - stroke_width = theme.default_line_width, - stroke_linejoin = 'miter', - stroke_linecap = 'butt', - stroke_dasharray = 'none') - - def _updateStyle(self, **addstyledict): - elt = _protectCurrentChildren(self.__currElt) - - # fetch the current styles for this node - mystyledict = _parseStyleStr(elt.getAttribute('style')) - - # concat all parent style strings to get dflt styles for this node - parent,s = elt.parentNode,'' - while parent.nodeType != Document.nodeType : - # prepend parent str so later keys will override earlier ones - s = parent.getAttribute('style') + s - parent = parent.parentNode - dfltstyledict = _parseStyleStr(s) - - # Do some pre-processing on the caller-supplied add'l styles - # Convert '_' to '-' so caller can specify style tags as python - # variable names, eg. stroke_width => stroke-width. - # Also convert all RHS values to strs - for key in addstyledict.keys(): - k = re.sub('_','-',key) - addstyledict[k] = str(addstyledict[key]) # all vals => strs - if (k != key) : del addstyledict[key] - - for k in addstyledict.keys() : - if (mystyledict.has_key(k) or # need to overwrite it - (not dfltstyledict.has_key(k)) or # need to set it - dfltstyledict[k] != addstyledict[k]) : # need to override it - mystyledict[k] = addstyledict[k] - - s = _makeStyleStr(mystyledict) - if s : elt.setAttribute('style',s) - - self.__currElt = elt - - #################################################################### - # methods below define the pychart backend device API - - # First are a set of methods to start, construct and finalize a path - - def newpath(self): # Start a new path - if (self.__currElt.nodeName != 'g') : - raise OverflowError, "No containing group for newpath" - # Just insert a new 'path' element into the document - p = self.__doc.createElement('path') - self.__currElt.appendChild(p) - self.__currElt = p - - # This set of methods add data to an existing path element, - # simply add to the 'd' (data) attribute of the path elt - - def moveto(self, x, y): # - if (self.__currElt.nodeName != 'path') : - raise OverflowError, "No path for moveto" - d = ' '.join([self.__currElt.getAttribute('d'),'M',`x`,`-y`]).strip() - self.__currElt.setAttribute('d', d) - def lineto(self, x, y): - if (self.__currElt.nodeName != 'path') : - raise OverflowError, "No path for lineto" - d = ' '.join([self.__currElt.getAttribute('d'),'L',`x`,`-y`]).strip() - self.__currElt.setAttribute('d', d) - def path_arc(self, x, y, radius, ratio, start_angle, end_angle): - # mimic PS 'arc' given radius, yr/xr (=eccentricity), start and - # end angles. PS arc draws from CP (if exists) to arc start, - # then draws arc in counterclockwise dir from start to end - # SVG provides an arc command that draws a segment of an - # ellipse (but not a full circle) given these args: - # A xr yr rotate majorArcFlag counterclockwiseFlag xe ye - # We don't use rotate(=0) and flipped axes => all arcs are clockwise - - if (self.__currElt.nodeName != 'path') : - raise OverflowError, "No path for path_arc" - - self.comment('x=%g, y=%g, r=%g, :=%g, %g-%g' - % (x,y,radius,ratio,start_angle,end_angle)) - - xs = x+radius*math.cos(2*math.pi/360.*start_angle) - ys = y+ratio*radius*math.sin(2*math.pi/360.*start_angle) - xe = x+radius*math.cos(2*math.pi/360.*end_angle) - ye = y+ratio*radius*math.sin(2*math.pi/360.*end_angle) - if (end_angle < start_angle) : # make end bigger than start - while end_angle <= start_angle: # '<=' so 360->0 becomes 360->720 - end_angle += 360 - full_circ = (end_angle - start_angle >= 360) # draw a full circle? - - d = self.__currElt.getAttribute('d') - d += ' %s %g %g' % (d and 'L' or 'M',xs,-ys) # draw from CP, if exists - if (radius > 0) : # skip, eg. 0-radius 'rounded' corners which blowup - if (full_circ) : - # If we're drawing a full circle, move to the end coord - # and draw half a circle to the reflected xe,ye - d += ' M %g %g A %g %g 0 1 0 %g %g'%(xe,-ye, - radius,radius*ratio, - 2*x-xe,-(2*y-ye)) - # Draw arc from the CP (either reflected xe,ye for full circle else - # xs,ys) to the end coord - note with full_circ the - # 'bigArcFlag' value is moot, with exactly 180deg left to draw - d += ' A %g %g 0 %d 0 %g %g' % (radius,radius*ratio, - end_angle-start_angle>180, - xe,-ye) - self.__currElt.setAttribute('d',d.strip()) - def curveto(self, x1,y1,x2,y2,x3,y3): - # Equivalent of PostScript's x1 y1 x2 y2 x3 y3 curveto which - # draws a cubic bezier curve from curr pt to x3,y3 with ctrl points - # x1,y1, and x2,y2 - # In SVG this is just d='[M x0 y0] C x1 y1 x2 y2 x3 y3' - #! I can't find an example of this being used to test it - if (self.__currElt.nodeNode != 'path') : - raise OverflowError, "No path for curveto" - d = ' '.join([self.__currElt.getAttribute('d'),'C', - `x1`,`-y1`,`x2`,`-y2`,`x3`,`-y3`,]).strip() - self.__currElt.setAttribute('d', d) - def closepath(self): # close back to start of path - if (self.__currElt.nodeName != 'path') : - raise OverflowError, "No path for closepath" - d = ' '.join([self.__currElt.getAttribute('d'),'Z']).strip() - self.__currElt.setAttribute('d', d) - - # Next we have three methods for finalizing a path element, - # either fill it, clip to it, or draw it (stroke) - # canvas.polygon() can generate fill/clip cmds with - # no corresponding path so just ignore them - def stroke(self): - if (self.__currElt.nodeName != 'path') : - self.comment('No path - ignoring stroke') - return - self._updateStyle(fill='none') - self.__currElt = self.__currElt.parentNode - def fill(self): - if (self.__currElt.nodeName != 'path') : - self.comment('No path - ignoring fill') - return - self._updateStyle(stroke='none') - self.__currElt = self.__currElt.parentNode - def clip_sub(self): - if (self.__currElt.nodeName != 'path') : - self.comment('No path - ignoring clip') - return - - # remove the current path from the tree ... - p = self.__currElt - self.__currElt=p.parentNode - self.__currElt.removeChild(p) - - # ... add it to a clipPath elt in the defs section - clip = self.__doc.createElement('clipPath') - clipid = 'clip'+`len(self.__defs.childNodes)` - clip.setAttribute('id',clipid) - clip.appendChild(p) - self.__defs.appendChild(clip) - - # ... update the local style to point to it - self._updateStyle(clip_path = 'url(#%s)'%clipid) - - # The text_xxx routines specify the start/end and contents of text - def text_begin(self): - if (self.__currElt.nodeName != 'g') : - raise ValueError, "No group for text block" - t = self.__doc.createElement('text') - self.__currElt.appendChild(t) - self.__currElt = t - def text_moveto(self, x, y, angle): - if (self.__currElt.nodeName != 'text') : - raise ValueError, "No text for moveto" - self.__currElt.setAttribute('x',`x`) - self.__currElt.setAttribute('y',`-y`) - if (angle) : - self.__currElt.setAttribute('transform', - 'rotate(%g,%g,%g)' % (-angle,x,-y)) - def text_show(self, font_name, size, color, str): - if (self.__currElt.nodeName != 'text') : - raise ValueError, "No text for show" - - # PyChart constructs a postscript font name, for example: - # - # Helvetica Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique - # Helvetica-Narrow Times-Roman Times-Italic - # Symbol Palatino-Roman Bookman-Demi Courier AvantGarde-Book - # - # We need to deconstruct this to get the font-family (the - # piece before the '-'), and other characteristics. - # Note that 'Courier' seems to correspond to SVGs 'CourierNew' - # and that the SVG Symbol font is Unicode where the ascii text - # 'Symbol' doesn't create greek characters like 'Sigma ...' - - # should really pass a unicode string, or provide translation - # - # SVG defines: - # font-style = normal (aka roman) | italic | oblique - # font-weight = normal | bold (aka demi?) - # font-stretch = normal | wider | narrower | ultra-condensed | - # extra-condensed | condensed | semi-condensed | - # semi-expanded | expanded | extra-expanded | ultra-expanded - # ('narrow' seems to correspond to 'condensed') - - m = re.match(r'([^-]*)(-.*)?',font_name) - font_name,modifiers = m.groups() - if font_name == 'Courier' : font_name = 'CourierNew' - font_style = font_weight = font_stretch = 'normal' - if modifiers : - if re.search('Italic',modifiers) : font_style = 'italic' - elif re.search('Oblique',modifiers) : font_style = 'oblique' - if re.search('Bold|Demi',modifiers) : font_weight = 'bold' - if re.search('Narrow',modifiers) : font_stretch = 'condensed' - #! translate ascii symbol font chars -> unicode (see www.unicode.org) - #! http://www.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/symbol.txt - #! but xml Text element writes unicode chars as '?' to XML file... - str = re.sub(r'\\([()])',r'\1',str) # unescape brackets - self._updateStyle(fill=_svgcolor(color), - stroke='none', - font_family=font_name, - font_size=size, - font_style=font_style, - font_weight=font_weight, - font_stretch=font_stretch) - self.__currElt.appendChild(self.__doc.createTextNode(str)) - def text_end(self): - if (self.__currElt.nodeName != 'text') : - raise ValueError, "No text for close" - self.__currElt = self.__currElt.parentNode - - - # Three methods that change the local style of elements - # If applied to a group, they persist until the next grestore, - # If applied within a path element, they only affect that path - - # although this may not in general correspond to (say) PostScript - # behavior, it appears to correspond to reflect mode of use of this API - def set_fill_color(self, color): - self._updateStyle(fill=_svgcolor(color)) - def set_stroke_color(self, color): - self._updateStyle(stroke=_svgcolor(color)) - def set_line_style(self, style): # see line_style.py - linecap = {0:'butt', 1:'round', 2:'square'} - linejoin = {0:'miter', 1:'round', 2:'bevel'} - if style.dash: dash = ','.join(map(str,style.dash)) - else : dash = 'none' - self._updateStyle(stroke_width = style.width, - stroke = _svgcolor(style.color), - stroke_linecap = linecap[style.cap_style], - stroke_linejoin = linejoin[style.join_style], - stroke_dasharray = dash) - - # gsave & grestore respectively push & pop a new context to hold - # new style and transform parameters. push/pop transformation are - # similar but explicitly specify a coordinate transform at the - # same time - def gsave(self): - if (self.__currElt.nodeName not in ['g','svg']) : - raise ValueError, "No group for gsave" - g = self.__doc.createElement('g') - self.__currElt.appendChild(g) - self.__currElt = g - def grestore(self): - if (self.__currElt.nodeName != 'g'): - raise ValueError, "No group for grestore" - # first pop off any auto-generated groups (see protectCurrentChildren) - while (self.__currElt.hasAttribute('auto')) : - self.__currElt.removeAttribute('auto') - self.__currElt = self.__currElt.parentNode - # then pop off the original caller-generated group - self.__currElt = self.__currElt.parentNode - - def push_transformation(self, baseloc, scale, angle, in_text=0): - #? in_text arg appears to always be ignored - - # In some cases this gets called after newpath, with - # corresonding pop_transformation called after the path is - # finalized so we check specifically for that, and generate - # an enclosing group to hold the incomplete path element - # We could add the transform directly to the path element - # (like we do with line-style etc) but that makes it harder - # to handle the closing 'pop' and might lead to inconsitency - # with PostScript if the closing pop doesn't come right after - # the path element - - elt = self.__currElt - if elt.nodeName == 'g': - elt = None - elif (elt.nodeName == 'path' and not elt.hasAttribute('d')) : - g = elt.parentNode - g.removeChild(elt) - self.__currElt = g - else: - raise ValueError, "Illegal placement of push_transformation" - - t = '' - if baseloc : - t += 'translate(%g,%g) '%(baseloc[0],-baseloc[1]) - if angle : - t += 'rotate(%g) '%-angle - if scale : - t += 'scale(%g,%g) '%tuple(scale) - - self.gsave() - self.__currElt.setAttribute('transform',t.strip()) - if elt: # elt has incomplete 'path' or None - self.__currElt.appendChild(elt) - self.__currElt = elt - - def pop_transformation(self, in_text=0): #? in_text unused? - self.grestore() - - # If verbose, add comments to the output stream (helps debugging) - def comment(self, str): - if _comment_p : - self.__currElt.appendChild(self.__doc.createComment(str)) - - # The verbatim method is currently not supported - presumably with - # the SVG backend the user would require access to the DOM since - # we're not directly outputting plain text here - def verbatim(self, str): - self.__currElt.appendChild(self.__doc.createComment('verbatim not implemented: ' + str)) - - # The close() method finalizes the SVG document and flattens the - # DOM document to XML text to the specified file (or stdout) - def close(self): - basecanvas.T.close(self) - self.grestore() # matching the gsave in __init__ - if (self.__currElt.nodeName != 'svg') : - raise ValueError, "Incomplete document at close!" - - # Don't bother to output an empty document - this can happen - # when we get close()d immediately by theme reinit - if (len(self.__svg.childNodes[-1].childNodes) == 0) : - return - - fp, need_close = self.open_output(self.__out_fname) - bbox = theme.adjust_bounding_box([self.__xmin, self.__ymin, - self.__xmax, self.__ymax]) - self.__svg.setAttribute('viewBox','%g %g %g %g' - % (xscale(bbox[0]), - -yscale(bbox[3]), - xscale(bbox[2])-xscale(bbox[0]), - yscale(bbox[3])-yscale(bbox[1]))) - self.__doc.writexml(fp,'',' ','\n') - if need_close: - fp.close() diff --git a/bin/pychart/text_box.py b/bin/pychart/text_box.py deleted file mode 100644 index a624782f48d..00000000000 --- a/bin/pychart/text_box.py +++ /dev/null @@ -1,152 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import canvas -import line_style -import pychart_util -import fill_style -import font -import chart_object -import color -import arrow -import text_box_doc -from pychart_types import * -from types import * - -class T(chart_object.T): - __doc__ = text_box_doc.doc - keys = {"text": (StringType, "???", "Text body. <>"), - "loc": (TupleType, (0,0), - "The location of the text box."), - "line_style": (line_style.T, line_style.default, - """The line style of the surrounding frame."""), - "fill_style": (fill_style.T, fill_style.white, - "Specifies the fill style of the text box."), - "top_fudge": (UnitType, 0, - "The amount of space (in points) above the first line"), - "bottom_fudge": (UnitType, 5, - "The amount of space below the last line"), - "left_fudge": (UnitType, 5, - "The amount of space left of the box"), - "right_fudge": (UnitType, 5, - "The amount of space right of the box"), - "_arrows": (ListType, pychart_util.new_list, "The list of arrows. Not to be touched by the user directly"), - "radius": (UnitType, 0, - """Radius of the four corners of the rectangle. - If the value is zero, a sharp-cornered - rectangle is drawn."""), - "shadow": (ShadowType, None, - pychart_util.shadow_desc) - } -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - def get_dimension(self): - x = self.loc[0] - self.left_fudge - y = self.loc[1] - self.bottom_fudge - width = font.text_width(self.text) + self.right_fudge + self.left_fudge - height = (font.text_height(self.text))[0] + self.top_fudge + self.bottom_fudge - return (x, y, width, height) - - def choose_end_point(self, tipx, tipy): - (x, y, width, height) = self.get_dimension() - - minDist = -1 - minPoint = None - vertices = [(x, y), - (x+width, y), - (x+width, y+height), - (x, y+height)] - - if tipx >= x and tipx < x+width: - vertices.append((tipx, y)) - vertices.append((tipx, y+height)) - if tipy >= y and tipy < y+height: - vertices.append((x, tipy)) - vertices.append((x+width, tipy)) - - for startPoint in vertices: - dist = ((startPoint[0] - tipx) **2 + (startPoint[1] - tipy) **2) - if not minPoint or dist < minDist: - minPoint = startPoint - minDist = dist - - return minPoint - - def add_arrow(self, tipLoc, tail=None, arrow = arrow.default): - """This method adds a straight arrow that points to - @var{TIPLOC}, which is a tuple of integers. @var{TAIL} - specifies the starting point of the arrow. It is either None - or a string consisting of the following letters: 'l', 'c', - 'r', 't', 'm,', and 'b'. Letters 'l', 'c', or 'r' means to - start the arrow from the left, center, or right of the text - box, respectively. Letters 't', 'm', or 'b' means to start the - arrow from the top, middle or bottom of the text box. For - example, when @samp{tail = 'tc'} then arrow is drawn from - top-center point of the text box. ARROW specifies the style of - the arrow. <>. - """ - self._arrows.append((tipLoc, tail, arrow)) - - def draw(self, can = None): - if can == None: - can = canvas.default_canvas() - x = self.loc[0] - y = self.loc[1] - text_width = font.text_width(self.text) - text_height = font.text_height(self.text)[0] - (halign, valign, angle) = font.get_align(self.text) - - if self.line_style or self.fill_style: - width = text_width+self.left_fudge+self.right_fudge - height = text_height+self.bottom_fudge+self.top_fudge - can.round_rectangle(self.line_style, self.fill_style, - x-self.left_fudge, y-self.bottom_fudge, - x-self.left_fudge+width, y-self.bottom_fudge+height, - self.radius, self.shadow) - - if halign == 'L': - can.show(x, y, self.text) - elif halign == 'C': - can.show(x+text_width/2.0, y, self.text) - elif halign == 'R': - can.show(x+text_width, y, self.text) - else: - raise Exception, "Unsupported alignment (" + halign + ")" - - # draw arrows - for t in self._arrows: - (tipLoc, tail, arrow) = t - if tail: - (x, y, width, height) = self.get_dimension() - origin = [x, y] - for ch in tail: - if ch == 'l': - origin[0] = x - elif ch == 'c': - origin[0] = x+width/2.0 - elif ch == 'r': - origin[0] = x+width - elif ch == 'b': - origin[1] = y - elif ch == 'm': - origin[1] = y+height/2.0 - elif ch == 't': - origin[1] = y+height - else: - raise ValueError, tail + ": unknown tail location spec." - else: - origin = self.choose_end_point(tipLoc[0], tipLoc[1]) - arrow.draw((origin, tipLoc), can) - - diff --git a/bin/pychart/text_box_doc.py b/bin/pychart/text_box_doc.py deleted file mode 100644 index 6b38b841a1d..00000000000 --- a/bin/pychart/text_box_doc.py +++ /dev/null @@ -1,20 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -loc(type:tuple) default="The location of the text box.". -shadow(type:) default="The value is either None or a tuple. When non-None, -a drop-shadow is drawn beneath the object. X-off, and y-off specifies the -offset of the shadow relative to the object, and fill specifies the -style of the shadow (@pxref{module-fill-style}).". -left_fudge(type:length in points (\\xref{unit})) default="The amount of space left of the box". -bottom_fudge(type:length in points (\\xref{unit})) default="The amount of space below the last line". -fill_style(type:fill_style.T) default="Specifies the fill style of the text box.". -right_fudge(type:length in points (\\xref{unit})) default="The amount of space right of the box". -top_fudge(type:length in points (\\xref{unit})) default="The amount of space (in points) above the first line". -text(type:str) default="Text body. <>". -radius(type:length in points (\\xref{unit})) default="Radius of the four corners of the rectangle. - If the value is zero, a sharp-cornered - rectangle is drawn.". -line_style(type:line_style.T) default="The line style of the surrounding frame.". -_arrows(type:list) default="The list of arrows. Not to be touched by the user directly". -""" - diff --git a/bin/pychart/theme.py b/bin/pychart/theme.py deleted file mode 100644 index 20ccc709136..00000000000 --- a/bin/pychart/theme.py +++ /dev/null @@ -1,234 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import sys -import os -import re -import getopt -import pychart_util - -__doc__ = """This module is defines variables for changing the looks -of charts. All the variables can be changed either via environment -variable PYCHART_OPTIONS or via the command-line options. - -The value of PYCHART_OPTIONS should be a sequence of var=val separated -by space. Below is an example, which tells Pychart to write to file -foo.pdf and use Times-Roman as the default font. - -PYCHART_OPTIONS="output=foo.pdf font-family=Times" - -The summary of attributes that can be set via PYCHART_OPTIONS follows: - -output=FILENAME (default: stdout) - - Set the output file name. - -format=[ps|pdf|pdf-uncompressed|png|x11|svg] (default: ps) - - Set the output file format. - -font-family=NAME (default: Helvetica) - - Set the default font to be used by texts. - -font-size=N (default: 9) - - Set the default font to be used by texts. -line-width=X (default: 0.4) - - Set the default line width, in points. See also - pychart.line_style. - -scale=X (default: 1.0) - - Set the scaling factor. The default is 1.0. - -color=[yes|no] (default: no) - - If yes, Pychart colorizes default object attributes. - -You can also set these variables by calling theme.get_options. -""" - -use_color = 0 -scale_factor = 1 -output_format = None # "ps", "pdf", "png", "x11", or "svg" -compress_output = 1 -output_file = "" - -default_font_family = "Helvetica" -default_font_size = 9 -default_line_height = None -default_font_halign = "L" -default_font_valign = "B" -default_font_angle = 0 -default_line_width = 0.4 - -debug_level = 1 -delta_bounding_box = [-3, -3, 3, 3] -bounding_box = {} - -def parse_yesno(str): - if str in ("yes", "true", "1"): - return 1 - else: - return 0 - -def parse_bounding_box(arg): - global delta_bounding_box, bounding_box - - l = arg.split(",") - if len(l) != 4: - raise ValueError, "Need to specify margin=LEFT,BOTTOM,RIGHT,TOP" - for i in range(0, 4): - val = l[i].strip() - if val[0] == '+': - delta_bounding_box[i] = int(val[1:]) - elif val[0] == '-': - delta_bounding_box[i] = int(val[1:]) - else: - bounding_box[i] = int(val) - -def adjust_bounding_box(bbox): - """Adjust the bounding box as specified by user. - Returns the adjusted bounding box. - - - bbox: Bounding box computed from the canvas drawings. - It must be a four-tuple of numbers. - """ - for i in range(0, 4): - if bounding_box.has_key(i): - bbox[i] = bounding_box[i] - else: - bbox[i] += delta_bounding_box[i] - return bbox - -def parse_option(opt, arg): - global use_color, scale_factor, margin - global output_format, output_file, compress_output - global default_font_family, default_font_size - global default_line_height - global default_line_width, debug_level - if opt == "format": - if arg in ("ps", "eps"): - output_format = "ps" - elif arg == "png": - output_format = "png" - elif arg == "svg": - output_format = "svg" - elif arg == "x11": - output_format = "x11" - elif arg == "pdf-uncompressed": - output_format = "pdf" - compress_output = 0 - elif arg in ("pdf-compressed", "pdf"): - output_format = "pdf" - compress_output = 1 - else: - raise ValueError, "Unknown output option: " + str(arg) - elif opt == "output": - output_file = arg - elif opt == "color": - use_color = 1 - elif opt == "scale": - scale_factor = float(arg) - elif opt == "bbox": - parse_bounding_box(arg) - elif opt == "font-family": - default_font_family = arg - elif opt == "font-size": - default_font_size = float(arg) - default_line_height = float(arg) - elif opt == "line-width": - default_line_width = float(arg) - elif opt == "debug-level": - debug_level = int(arg) - else: - raise getopt.GetoptError, "Unknown option: " + opt + " " + arg - -if os.environ.has_key("PYCHART_OPTIONS"): - for opt in os.environ["PYCHART_OPTIONS"].split(): - opt, arg = opt.split("=") - parse_option(opt, arg) - -hooks = [] -def add_reinitialization_hook(proc): - global hooks - hooks.append(proc) - proc() - -def usage(): - print "Usage: %s [options..]" % sys.argv[0] - print """ - --scale=X: Set the scaling factor to X (default: 1.0). - --format=[ps|png|pdf|x11|svg]: Set the output format (default: ps). - --font-family=NAME: Set the default font family (default: Helvetica). - --font-size=NAME: Set the default font size (default: 9pts). - --line-width=NAME: Set the default line width (default: 0.4). - --debug-level=N: Set the messaging verbosity (default: 0). - --bbox=LEFT,BOTTOM,RIGHT,TOP: Specifies the amount of space (in PS points) to be left in the edges of the picture (default: -1,-1,+1,+1). - """ - -def reinitialize(): - """This procedure must be called after setting variables in - the |theme| module. This procedure propagates the new values of - the theme variables to other modules that depend on their values.""" - for proc in hooks: - proc() - -def get_options(argv = None): - """This procedure takes a list of command line arguments in - and parses - options. It returns the non-parsed portion of . Parameter - can be - omitted, in which case its value defaults to |sys.argv[1:]|. - The options supported are: "|--format=[ps,png,pdf,x11,svg]|", - "|--output=|", "|--color=[yes,no]|" - "|--scale=|", "|--font-family=|", "|--font-size=|", - "|--line-width=|", - "|--debug-level=|", "|bbox=|". - The below code shows an example. - -#!/usr/bin/python -from pychart import * -args = theme.get_options() -ar = area.T(...) -... - """ - if argv == None: - argv = sys.argv[1:] - try: - opts, args = getopt.getopt(argv, "d:co:f:", - ["format=", "output=", "color=", - "scale=", "font-family=", "font-size=", - "line-width=", "debug-level=", - "bbox="]) - except getopt.GetoptError, foo: - print foo - usage() - raise getopt.GetoptError - for opt, arg in opts: - if opt == "-d": - parse_option("debug-level", arg) - elif opt == "-c": - parse_option("color", None) - elif opt == "-o": - parse_option("output", arg) - elif opt == "-f": - parse_option("format", arg) - else: - parse_option(opt[2:], arg) - reinitialize() - return args - - diff --git a/bin/pychart/tick_mark.py b/bin/pychart/tick_mark.py deleted file mode 100644 index 21c7777e72a..00000000000 --- a/bin/pychart/tick_mark.py +++ /dev/null @@ -1,188 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import color -import line_style -import fill_style -import chart_object -import object_set -import pychart_util -import tick_mark_doc -from pychart_types import * - -_keys = { - "line_style": (line_style.T, line_style.default, "The line style of the tick mark."), - "fill_style": (fill_style.T, fill_style.white, "The fill style."), - "size": (UnitType, 5, "Size of the tick mark."), - } - -class T(chart_object.T): - __doc__ = tick_mark_doc.doc - keys = _keys -##AUTOMATICALLY GENERATED - -##END AUTOMATICALLY GENERATED - - def predraw_check(self): - if not hasattr(self, "type_checked"): - self.type_check() - self.type_checked = 1 - -class Circle(T): - """Draws a circle. """ - def draw(self, can, x, y): - self.predraw_check() - can.ellipsis(self.line_style, self.fill_style, x, y, - self.size/2.0, 1) - -class Square(T): - """Draws a square.""" - def draw(self, can, x, y): - self.predraw_check() - # move to the bottom-left corner - x = x - self.size/2.0 - y = y - self.size/2.0 - can.rectangle(self.line_style, self.fill_style, - x, y, x+self.size, y+self.size) - -class Triangle(T): - """Draws a triangle pointing up.""" - def draw(self, can, x, y): - self.predraw_check() - can.polygon(self.line_style, self.fill_style, - ((x-self.size/1.6, y-self.size/2.0), - (x+self.size/1.6, y-self.size/2.0), - (x, y+self.size/2.0))) -class DownTriangle(T): - """Draws a triangle pointing down.""" - def draw(self, can, x, y): - self.predraw_check() - can.polygon(self.line_style, self.fill_style, - ((x, y-self.size/2.0), - (x-self.size/1.6, y+self.size/2.0), - (x+self.size/1.6, y+self.size/2.0))) - - -class X(T): - """Draw a "X"-shaped tick mark. Attribute "fill-style" is ignored.""" - keys = pychart_util.union_dict(T.keys, - {"line_style": (line_style.T, - line_style.T(width=0.7), - "The line style of the tick mark")}) - def draw(self, can, x, y): - self.predraw_check() - # move to the bottom-left corner - x = x - self.size/2.0 - y = y - self.size/2.0 - can.line(self.line_style, x, y, x+self.size, y+self.size) - can.line(self.line_style, x+self.size, y, x, y+self.size) - -class Plus(T): - """Draw a "+"-shaped tick mark. Attribute "fill-style" is ignored.""" - keys = pychart_util.union_dict(T.keys, - {"line_style": (line_style.T, - line_style.T(width=1), - "The line style of the tick mark.")}) - def draw(self, can, x, y): - self.predraw_check() - # move to the bottom-left corner - can.line(self.line_style, x-self.size/1.4, y, x+self.size/1.4, y) - can.line(self.line_style, x, y-self.size/1.4, x, y+self.size/1.4) - -class Diamond(T): - """Draw a square rotated at 45 degrees.""" - def draw(self, can, x, y): - self.predraw_check() - # move to the bottom-left corner - can.polygon(self.line_style, self.fill_style, - ((x-self.size/1.4, y), (x, y+self.size/1.4), - (x+self.size/1.4, y), (x, y-self.size/1.4))) - -class Star(T): - """Draw a "*". Attribute "fill-style" is ignored.""" - keys = pychart_util.union_dict(T.keys, - {"line_style": (line_style.T, - line_style.T(width=1), - "The line style of the tick mark.")}) - def draw(self, can, x, y): - self.predraw_check() - # move to the bottom-left corner - midx = x - midy = y - d_len = self.size / 2.0 - r_len = self.size * 1.414 / 2.0 - can.line(self.line_style, x-d_len, y-d_len, x+d_len, y+d_len) - can.line(self.line_style, x+d_len, y-d_len, x-d_len, y+d_len) - can.line(self.line_style, midx, y-r_len, midx, y+r_len) - can.line(self.line_style, x-r_len, midy, x+r_len, midy) - -class Null(T): - """This tickmark doesn't draw anything. All the attributes are ignored.""" - def __init__ (self): - self.line_style = None - self.fill_style = None - self.size = -1 - def draw(self, can, x, y): - pass - -standards = object_set.T() -def _intern(style): - standards.add(style) - return style - -square = _intern(Square()) -square3 = _intern(Square(size=3)) -square5 = square -x = _intern(X()) -x3 = _intern(X(size=3)) -x5 = x -star = _intern(Star()) -star3 = _intern(Star(size=3)) -star5 = star -plus = _intern(Plus()) -plus3 = _intern(Plus(size=3)) -plus5 = plus -dia = _intern(Diamond()) -dia3 = _intern(Diamond(size=3)) -dia5 = dia -tri = _intern(Triangle()) -tri3 = _intern(Triangle(size=3)) -tri5 = tri -dtri = _intern(DownTriangle()) -dtri3 = _intern(DownTriangle(size=3)) -dtri5 = dtri -circle1 = _intern(Circle(size=1)) -circle2 = _intern(Circle(size=3)) -circle3 = _intern(Circle(size=5)) -blacksquare = _intern(Square(fill_style=fill_style.black)) -blacksquare3 = _intern(Square(size=3, fill_style=fill_style.black)) -blackdia = _intern(Diamond(fill_style=fill_style.black)) -blackdia3 = _intern(Diamond(size=3, fill_style=fill_style.black)) -blacktri = _intern(Triangle(fill_style=fill_style.black)) -blacktri3 = _intern(Triangle(size=3, fill_style=fill_style.black)) -blackdtri = _intern(DownTriangle(fill_style=fill_style.black)) -blackdtri3 = _intern(DownTriangle(size=3, fill_style=fill_style.black)) -blackcircle1 = _intern(Circle(size=1, fill_style=fill_style.black)) -blackcircle3 = _intern(Circle(size=3, fill_style=fill_style.black)) -gray70square = _intern(Square(fill_style=fill_style.gray70)) -gray70square3 = _intern(Square(size=3, fill_style=fill_style.gray70)) -gray70dia = _intern(Diamond(fill_style=fill_style.gray70)) -gray70dia3 = _intern(Diamond(size=3, fill_style=fill_style.gray70)) -gray70tri = _intern(Triangle(fill_style=fill_style.gray70)) -gray70tri3 = _intern(Triangle(size=3, fill_style=fill_style.gray70)) -gray70dtri = _intern(DownTriangle(fill_style=fill_style.gray70)) -gray70dtri3 = _intern(DownTriangle(size=3, fill_style=fill_style.gray70)) -gray70circle1 = _intern(Circle(size=1, fill_style=fill_style.gray70)) -gray70circle3 = _intern(Circle(size=3, fill_style=fill_style.gray70)) -default = _intern(Null()) - diff --git a/bin/pychart/tick_mark_doc.py b/bin/pychart/tick_mark_doc.py deleted file mode 100644 index d61e68f1310..00000000000 --- a/bin/pychart/tick_mark_doc.py +++ /dev/null @@ -1,7 +0,0 @@ -# automatically generated by generate_docs.py. -doc="""Attributes supported by this class are: -line_style(type:line_style.T) default="The line style of the tick mark.". -size(type:length in points (\\xref{unit})) default="Size of the tick mark.". -fill_style(type:fill_style.T) default="The fill style.". -""" - diff --git a/bin/pychart/typechecker.py b/bin/pychart/typechecker.py deleted file mode 100644 index ce88ffa97ef..00000000000 --- a/bin/pychart/typechecker.py +++ /dev/null @@ -1,27 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import types -class IntervalTypeClass: - def typeCheck(self, val): - if type(val) in (types.IntType, types.LongType, types.FloatType, - types.FunctionType): - return None - return "Expecting a number or a function, but received '%s'", val - def typeDescription(self): - return "A number or function" - def varDescription(self, name): - return "A number or function" - -IntervalType = IntervalTypeClass() - diff --git a/bin/pychart/version.py b/bin/pychart/version.py deleted file mode 100644 index 0b564a98d7a..00000000000 --- a/bin/pychart/version.py +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -version = "1.37" -copyright = "Copyright 1999-2005 Yasushi Saito" - diff --git a/bin/pychart/x11canvas.py b/bin/pychart/x11canvas.py deleted file mode 100644 index fa432404830..00000000000 --- a/bin/pychart/x11canvas.py +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import os -import gs_frontend -import theme -import sys - -class T(gs_frontend.T): - def close(self): - gs_frontend.T.close(self) - self.start_gs("-sDEVICE=x11") - sys.stdin.readline() - self.close_gs() - diff --git a/bin/pychart/zap.py b/bin/pychart/zap.py deleted file mode 100644 index 2c8672c65c6..00000000000 --- a/bin/pychart/zap.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com) -# -# Jockey is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any -# later version. -# -# Jockey is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# for more details. -# -import fill_style -import line_style -import copy - -def _draw_zap(can, p1, p2, style, pat): - x = copy.deepcopy(p1) - x.extend(p2) - can.polygon(None, pat, x) - can.lines(style, p1) - can.lines(style, p2) - - -def zap_horizontally(can, style, pat, x1, y1, x2, y2, xsize, ysize): - """Draw a horizontal "zapping" symbol on the canvas that shows - that a graph is ripped in the middle. - - Parameter specifies the style for the zig-zag lines. - PAT specifies the pattern with which the area is filled. - The symbol is drawn in the rectangle (, ) - (, ). - Each "zigzag" has the width , height .""" - - assert isinstance(style, line_style.T) - assert isinstance(pat, fill_style.T) - - points = [] - points2 = [] - x = x1 - y = y1 - while x < x2: - points.append((x, y)) - points2.append((x, y + (y2-y1))) - x += xsize - if y == y1: - y += ysize - else: - y -= ysize - - points2.reverse() - _draw_zap(can, points, points2, style, pat) - -def zap_vertically(can, style, pat, x1, y1, x2, y2, xsize, ysize): - """Draw a vertical "zapping" symbol on the canvas that shows - that a graph is ripped in the middle. - - Parameter specifies the style for the zig-zag lines. - PAT specifies the pattern with which the area is filled. - The symbol is drawn in the rectangle (, ) - (, ). - Each "zigzag" has the width , height .""" - - points = [] - points2 = [] - x = x1 - y = y1 - while y < y2: - points.append((x, y)) - points2.append((x + (x2-x1), y)) - y += ysize - if x == x1: - x += xsize - else: - x -= xsize - - points2.reverse() - _draw_zap(can, points, points2, style, pat) - diff --git a/bin/reportlab/MANIFEST.in b/bin/reportlab/MANIFEST.in deleted file mode 100644 index 86f3442fd92..00000000000 --- a/bin/reportlab/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -global-include *.dtd *.txt *.xml *.yml -global-include *.c *.h *.in *.mashed -global-include *.gif *.png *.jpg .a85 -global-include *.AFM *.PFB *.ttf -global-include README -include changes diff --git a/bin/reportlab/README b/bin/reportlab/README deleted file mode 100644 index 40b00be75f6..00000000000 --- a/bin/reportlab/README +++ /dev/null @@ -1,35 +0,0 @@ -#copyright ReportLab Inc. 2000-2001 -#see license.txt for license details -#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/README?cvsroot=reportlab -#$Header: /tmp/reportlab/reportlab/README,v 1.9 2002/03/05 17:21:44 rgbecker Exp $ - -This is the ReportLab PDF library. - -Licensing -========= -BSD license. See license.txt for details - -Installation -============ -Either unpack reportlab.zip or reportlab.tgz to some directory say -d:\ReportLab. If you can, ensure that the line terminator style is -correct for your OS (man zip programs have a text mode option eg -a). - -Create a .pth file, say reportlab.pth in your Python -home directory. It should have one line: -d:/ReportLab. - -Alternatively unpack the archive into a directory which is already on your -python path. - -Documentation -============= -Full documentation is included in PDF format in the docs directory. -If you are working with a CVS version, run the script genAll.py -in that directory to generate the documentation. - -Acknowledgements and Thanks -=========================== -lib/normalDate.py originally by Jeff Bauer -(please let us know of other acknowledgements - we just - started this section - 13/2/2002) diff --git a/bin/reportlab/__init__.py b/bin/reportlab/__init__.py deleted file mode 100644 index 5a381ed9b26..00000000000 --- a/bin/reportlab/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/__init__.py -__version__=''' $Id: __init__.py 2877 2006-05-18 15:11:23Z andy $ ''' -__doc__="""The Reportlab PDF generation library.""" -Version = "2.0" - -import sys - -if sys.version_info[0:2] < (2, 3): - warning = """The trunk of reportlab requires Python 2.3 or higher. - Any older applications should either use released versions beginning - with 1.x (e.g. 1.21), or snapshots or checkouts from our 'version1' - branch. - """ - raise ImportError("reportlab needs Python 2.3 or higher", warning) - -def getStory(context): - 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 - - -def getMonitor(): - import reportlab.monitor - mon = reportlab.monitor.ReportLabToolkitMonitor() - return mon diff --git a/bin/reportlab/changes b/bin/reportlab/changes deleted file mode 100644 index f22b174ddb8..00000000000 --- a/bin/reportlab/changes +++ /dev/null @@ -1,7788 +0,0 @@ -################################################################################# -#################### RELEASE 2.0 at 15:00 GMT 23/May/2006 ################# -################################################################################# -r2905 | andy | 2006-05-23 15:49:28 +0100 (Tue, 23 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - disable loading of CMAPS -r2904 | andy | 2006-05-23 15:19:54 +0100 (Tue, 23 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py - removed CMap references -r2903 | andy | 2006-05-23 15:04:08 +0100 (Tue, 23 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_multibyte_kor.py - M /reportlab/trunk/reportlab/test/test_multibyte_cht.py - M /reportlab/trunk/reportlab/test/test_multibyte_chs.py - proper samples added for CJK languages -r2902 | andy | 2006-05-23 14:08:04 +0100 (Tue, 23 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_multibyte_kor.py - M /reportlab/trunk/reportlab/test/test_multibyte_cht.py - M /reportlab/trunk/reportlab/test/test_multibyte_chs.py - M /reportlab/trunk/reportlab/lib/codecharts.py - partial silencing of cmap tests -r2901 | andy | 2006-05-23 00:02:22 +0100 (Tue, 23 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - M /reportlab/trunk/reportlab/pdfbase/_cidfontdata.py - A /reportlab/trunk/reportlab/pdfbase/_can_cmap_data.py - added canned width data for CID fonts -r2900 | andy | 2006-05-22 22:49:00 +0100 (Mon, 22 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/genuserguide.py - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - D /reportlab/trunk/reportlab/docs/userguide/ch0_whatsnewinrl2.py - updated user guide -r2899 | rgbecker | 2006-05-22 13:42:07 +0100 (Mon, 22 May 2006) | 1 line - M /reportlab/trunk/reportlab/rl_config.py - rl_config.py: fix missing comma in CMap exemplars list -r2893 | rgbecker | 2006-05-21 10:12:50 +0100 (Sun, 21 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/runAll.py - runAll.py: add support for external running -r2892 | rgbecker | 2006-05-19 15:16:02 +0100 (Fri, 19 May 2006) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - A /reportlab/trunk/reportlab/lib/rltempfile.py - reportlab.lib: break out rltempfile.py to avoid rl_accel imports -r2883 | andy | 2006-05-19 12:34:32 +0100 (Fri, 19 May 2006) | 1 line - M /reportlab/trunk/reportlab/tools/docco/stylesheet.py - M /reportlab/trunk/reportlab/tools/docco/rltemplate.py - M /reportlab/trunk/reportlab/test/runAll.py - manual style changes -r2882 | andy | 2006-05-18 22:54:38 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - manual introduction amendments -r2881 | andy | 2006-05-18 17:56:05 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch2_graphics.py - amended John's encoding paragraph -r2880 | oualid | 2006-05-18 17:45:05 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/tools/docco/stylesheet.py - added extra paragraphstyle -r2879 | oualid | 2006-05-18 17:43:07 +0100 (Thu, 18 May 2006) | 1 line - A /reportlab/trunk/reportlab/docs/userguide/ch0_whatsnewinrl2.py - chapter What's new in RL 2 of user guide -r2878 | andy | 2006-05-18 16:13:01 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/lib/codecharts.py - M /reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py - updated codecharts and ch3 of user guide -r2877 | andy | 2006-05-18 16:11:23 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/__init__.py - changed version and added warning for old Python users -r2875 | andy | 2006-05-18 08:00:16 +0100 (Thu, 18 May 2006) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - codec validator for diagra -r2874 | jjlee | 2006-05-17 14:07:13 +0100 (Wed, 17 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch2_graphics.py - Correct docs in introduction re encoding for V2 -r2873 | rgbecker | 2006-05-17 11:59:59 +0100 (Wed, 17 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - pdfmetrics.py: fix rl_accel --> _rl_accel typo -r2872 | rgbecker | 2006-05-17 11:58:04 +0100 (Wed, 17 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/rl_codecs.py - rl_codecs: slight speedup -r2871 | rgbecker | 2006-05-17 10:39:29 +0100 (Wed, 17 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_rl_accel.py - test_rl_accel.py: fix ref count checks -r2868 | jjlee | 2006-05-16 15:58:47 +0100 (Tue, 16 May 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - Fix typos -r2867 | rgbecker | 2006-05-16 14:19:40 +0100 (Tue, 16 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_rl_accel.py - test_rl_accel: added some testsi for new functionality -r2865 | rgbecker | 2006-05-15 17:37:44 +0100 (Mon, 15 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - ttfonts.py: allow for _rl_accel._instanceStringWidthTTF -r2861 | rgbecker | 2006-05-15 08:44:00 +0100 (Mon, 15 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - pdfmetrics.py: _rl_accel._instanceStringWidthU -r2860 | rgbecker | 2006-05-12 18:30:55 +0100 (Fri, 12 May 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - pdfmetrics.py: more version2 accelerator stuff -r2857 | rgbecker | 2006-05-11 14:06:52 +0100 (Thu, 11 May 2006) | 1 line - M /reportlab/trunk/reportlab/platypus/paragraph.py - paragraph: fix misuse of style before setting in cjk breaklines -r2856 | rgbecker | 2006-05-11 10:48:13 +0100 (Thu, 11 May 2006) | 1 line - M /reportlab/trunk/reportlab/demos/odyssey/dodyssey.py - reportlab: add hotshot analysis to dodyssey.py -r2854 | rgbecker | 2006-05-10 13:57:21 +0100 (Wed, 10 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_hello.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - reportlab: PDFString vs Unicode changes -r2853 | rgbecker | 2006-05-10 13:56:39 +0100 (Wed, 10 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - reportlab: fix up links in paragraphs -r2852 | rgbecker | 2006-05-08 16:04:15 +0100 (Mon, 08 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/utils.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/runAll.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - reportlab: minor fixes to platypus, tests and fix jap splitting bug -r2851 | rgbecker | 2006-05-08 15:34:45 +0100 (Mon, 08 May 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/barcode/widgets.py - M /reportlab/trunk/reportlab/graphics/barcode/test.py - barcode: fix to import from reportlab -r2850 | rgbecker | 2006-05-08 14:40:06 +0100 (Mon, 08 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/pythonpowered.gif - we shouldn't ever need to change this -r2849 | andy | 2006-05-06 09:25:23 +0100 (Sat, 06 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/utils.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/pythonpowered.gif - M /reportlab/trunk/reportlab/platypus/tables.py - friendlified way test suite runs -r2848 | andy | 2006-05-05 00:45:29 +0100 (Fri, 05 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/platypus/tables.py - added japanese splitting test, and table preprocessor to convert unicode->utf8 -r2847 | andy | 2006-05-05 00:07:51 +0100 (Fri, 05 May 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - expanded Japanese para tests -r2846 | rgbecker | 2006-05-04 11:48:36 +0100 (Thu, 04 May 2006) | 1 line - A /reportlab/trunk/reportlab/graphics/barcode/widgets.py - A /reportlab/trunk/reportlab/graphics/barcode/usps.py - A /reportlab/trunk/reportlab/graphics/barcode/test.py - A /reportlab/trunk/reportlab/graphics/barcode/fourstate.py - A /reportlab/trunk/reportlab/graphics/barcode/eanbc.py - A /reportlab/trunk/reportlab/graphics/barcode/common.py - A /reportlab/trunk/reportlab/graphics/barcode/code93.py - A /reportlab/trunk/reportlab/graphics/barcode/code39.py - A /reportlab/trunk/reportlab/graphics/barcode/code128.py - A /reportlab/trunk/reportlab/graphics/barcode/__init__.py - A /reportlab/trunk/reportlab/graphics/barcode/VERSION - A /reportlab/trunk/reportlab/graphics/barcode/TODO - A /reportlab/trunk/reportlab/graphics/barcode/README - A /reportlab/trunk/reportlab/graphics/barcode - reportlab: barcode moved to reportlab/graphics -r2845 | rgbecker | 2006-05-03 13:24:35 +0100 (Wed, 03 May 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - shapes.py: merged stable 2843:2844 minor fix to outDir handling in Drawing.save -r2843 | rgbecker | 2006-04-28 18:33:32 +0100 (Fri, 28 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - reportlab: added pound sign -r2842 | rgbecker | 2006-04-28 15:08:58 +0100 (Fri, 28 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - reportlab: fixed added to paragraph -r2841 | rgbecker | 2006-04-27 18:42:12 +0100 (Thu, 27 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/rl_codecs.py - rl_codecs: eliminate decorators -r2840 | rgbecker | 2006-04-20 15:37:13 +0100 (Thu, 20 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - reportlab: wastefully allow TTFont to use ttc files -r2839 | rgbecker | 2006-04-19 17:55:46 +0100 (Wed, 19 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - ttfonts.py: fix up to use struct properly -r2838 | rgbecker | 2006-04-18 18:47:54 +0100 (Tue, 18 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/axes.py - reportlab: minor fix to axes.py -r2834 | rgbecker | 2006-04-05 18:42:59 +0100 (Wed, 05 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - reportlab: fix bad test test_pdfgen_links.py -r2833 | rgbecker | 2006-04-05 17:01:20 +0100 (Wed, 05 Apr 2006) | 1 line - A /reportlab/trunk/reportlab/test/test_platypus_paraparser.py - A /reportlab/trunk/reportlab/pdfbase/rl_codecs.py - A /reportlab/trunk/reportlab/lib/textsplit.py - reportlab: add files from utf8 branch -r2830 | rgbecker | 2006-04-05 16:18:32 +0100 (Wed, 05 Apr 2006) | 1 line - M /reportlab/trunk/reportlab/tools/pythonpoint/stdparser.py - M /reportlab/trunk/reportlab/tools/pythonpoint/pythonpoint.py - M /reportlab/trunk/reportlab/tools/docco/rl_doc_utils.py - M /reportlab/trunk/reportlab/tools/docco/graphdocpy.py - M /reportlab/trunk/reportlab/test/test_tools_pythonpoint.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfmetrics.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/para.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - M /reportlab/trunk/reportlab/pdfbase/_cidfontdata.py - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/lib/styles.py - M /reportlab/trunk/reportlab/lib/rparsexml.py - M /reportlab/trunk/reportlab/graphics/testshapes.py - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPDF.py - M /reportlab/trunk/reportlab/docs/userguide/ch5_paragraphs.py - M /reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - M /reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py - reportlab-utf8 moved to trunk -r2808 | rgbecker | 2006-03-15 16:47:27 +0000 (Wed, 15 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/graphics/renderSVG.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - reportlab: changes to accomodate barcodes -r2807 | andy | 2006-03-15 12:22:10 +0000 (Wed, 15 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - new pattern matcher - partially done -r2806 | andy | 2006-03-13 23:33:51 +0000 (Mon, 13 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - updated _imaging import -r2803 | rgbecker | 2006-03-09 13:29:26 +0000 (Thu, 09 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - reportlab: added FreeTextAnnotation and minor improvements to other annotations -r2802 | rgbecker | 2006-03-09 13:21:50 +0000 (Thu, 09 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - reportlab/graphics cosmetics and adding drawImage to renderPS -r2799 | rgbecker | 2006-03-02 13:54:39 +0000 (Thu, 02 Mar 2006) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: attempt to fix up keepInFrame for one more case -r2794 | rgbecker | 2006-03-01 10:48:40 +0000 (Wed, 01 Mar 2006) | 1 line - A /reportlab/trunk/reportlab/tools/utils/dumpttf.py - reportlab/tools/utils: added dumpttf.py -r2792 | rgbecker | 2006-02-28 18:37:06 +0000 (Tue, 28 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - ttfonts.py: fix index bug in name table read -r2789 | rgbecker | 2006-02-28 15:04:57 +0000 (Tue, 28 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - M /reportlab/trunk/reportlab/lib/utils.py - reportlab: remove cidfonts print, fix get_rl_tempdir -r2786 | rgbecker | 2006-02-27 21:26:46 +0000 (Mon, 27 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - reportlab: minor changes to annotation support -r2775 | rgbecker | 2006-02-15 12:17:24 +0000 (Wed, 15 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/__init__.py - platypus: ImagesAndFlowables in final form -r2774 | rgbecker | 2006-02-14 18:07:27 +0000 (Tue, 14 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/__init__.py - reportlab: added FlowablesAndImage -r2765 | rgbecker | 2006-02-02 18:48:12 +0000 (Thu, 02 Feb 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfgen: fix badly drawn jpegs which are actually gifs -r2762 | rgbecker | 2006-01-30 16:29:07 +0000 (Mon, 30 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/flowables.py - added _offsets handling to paragraph for flow around on left side -r2759 | rgbecker | 2006-01-25 11:25:29 +0000 (Wed, 25 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - utils.py: annotate ImageReader read errors -r2755 | rgbecker | 2006-01-25 11:21:10 +0000 (Wed, 25 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/platypus/para.py - para.py: minor layout changes to example etc -r2752 | rgbecker | 2006-01-09 13:29:53 +0000 (Mon, 09 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - shapes.py: fix wrap for Drawings to take account of renderScale -r2749 | rgbecker | 2006-01-09 11:22:03 +0000 (Mon, 09 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/renderSVG.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPDF.py - reprotlab/graphics: fix so renderScale is used properly -r2746 | rgbecker | 2006-01-05 15:17:03 +0000 (Thu, 05 Jan 2006) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - shapes.py: make copy use derived class if not overridden -r2745 | rgbecker | 2005-12-20 12:43:24 +0000 (Tue, 20 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - shapes.py: allow override for CWD -r2744 | rgbecker | 2005-12-15 12:28:18 +0000 (Thu, 15 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/pdfgen/pdfimages.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - reportlab: fix test_graphics_charts and eliminate 0 x 0 image bug in Acrobat 7.0 -r2743 | rgbecker | 2005-12-12 15:51:29 +0000 (Mon, 12 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - piecharts: now have orderMode 'alternate' and proper vertical spreading -r2742 | oualid | 2005-12-12 11:58:08 +0000 (Mon, 12 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/lib/normalDate.py - normalDate: add initialize support for datetime -r2741 | andy | 2005-12-07 21:52:33 +0000 (Wed, 07 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/lib/attrmap.py - M /reportlab/trunk/reportlab/graphics/testshapes.py - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - datacharts synchronised -r2740 | rgbecker | 2005-12-07 13:30:14 +0000 (Wed, 07 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/rl_config.py - rl_config: fix typo -r2739 | rgbecker | 2005-12-07 13:25:25 +0000 (Wed, 07 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/graphics/shapes.py - reportlab: add defaultGraphicsFontName, svg saving -r2738 | rgbecker | 2005-12-07 11:39:53 +0000 (Wed, 07 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - M /reportlab/trunk/reportlab/graphics/renderSVG.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPDF.py - graphics: added Drawing.renderScale hack for renderer terminal drawing scales -r2737 | rgbecker | 2005-12-06 18:36:58 +0000 (Tue, 06 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - piecharts: initial version of pointer labels -r2736 | andy | 2005-12-01 23:03:45 +0000 (Thu, 01 Dec 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - removed a 2.4-ism -r2733 | rgbecker | 2005-11-22 11:24:22 +0000 (Tue, 22 Nov 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: more optimisations -r2732 | rgbecker | 2005-11-21 19:39:42 +0000 (Mon, 21 Nov 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: speed improvements -r2727 | rgbecker | 2005-10-31 11:20:18 +0000 (Mon, 31 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - pdfgen: abstract color setting ops to textobject._PDFColorSetter -r2724 | rgbecker | 2005-10-30 11:52:47 +0000 (Sun, 30 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/axes.py - axes.py: factor out _allInt -r2723 | rgbecker | 2005-10-30 11:52:10 +0000 (Sun, 30 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - piecharts.py: fix checkLabelOverlap for 3d pie -r2719 | rgbecker | 2005-10-20 14:19:59 +0100 (Thu, 20 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_layout.py - M /reportlab/trunk/reportlab/graphics/shapes.py - reportlab: fix small angle addArc & Wedge.asPolygon -r2715 | rgbecker | 2005-10-10 12:56:30 +0100 (Mon, 10 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: fixes to PTOContainer -r2711 | rgbecker | 2005-10-06 11:04:37 +0100 (Thu, 06 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_pto.py - M /reportlab/trunk/reportlab/platypus/flowables.py - platypus: fixup KeepInFrame error handling -r2707 | rgbecker | 2005-10-04 13:59:21 +0100 (Tue, 04 Oct 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: make KeepInFrame wrapping more robust -r2699 | rgbecker | 2005-09-29 13:35:38 +0100 (Thu, 29 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/frames.py - frames.py: logger only when _debug -r2698 | rgbecker | 2005-09-29 12:49:23 +0100 (Thu, 29 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - platypus: changes to make identity more useful -r2697 | rgbecker | 2005-09-28 18:22:53 +0100 (Wed, 28 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_pto.py - test_platypus_pto.py: changed FrameFlowable to KeepInFrame -r2696 | rgbecker | 2005-09-28 14:22:53 +0100 (Wed, 28 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - flowables.py: keepInFrame now truncates etc properly, doctemplate.py: fix handle_frameEnd -r2695 | andy | 2005-09-27 22:12:08 +0100 (Tue, 27 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - renamed FrameFlowable to KeepInFrame -r2694 | rgbecker | 2005-09-27 18:31:51 +0100 (Tue, 27 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowablse.py: minor fix and hack to FrameFlowable -r2693 | rgbecker | 2005-09-27 16:29:16 +0100 (Tue, 27 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: minor fixes revealed by rml2pdf use -r2692 | rgbecker | 2005-09-27 14:26:12 +0100 (Tue, 27 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_pto.py - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - platypus: initial FrameFlowable implementation -r2689 | rgbecker | 2005-09-22 17:53:18 +0100 (Thu, 22 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - merge stable 2687:2688 pdfdoc.py: attempt to robustify escape attribute access -r2680 | rgbecker | 2005-09-21 17:17:56 +0100 (Wed, 21 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/platypus/flowables.py - platypus: fix up None defaults for table line commands, add HRFlowable dash arg -r2679 | rgbecker | 2005-09-20 16:59:39 +0100 (Tue, 20 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/axes.py - axes.py: make YCategoryAxis accept less than n labels -r2676 | rgbecker | 2005-09-06 11:25:00 +0100 (Tue, 06 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/samples/radar.py - M /reportlab/trunk/reportlab/graphics/samples/filled_radar.py - M /reportlab/trunk/reportlab/graphics/charts/spider.py - merge stable 2674:2675 graphics: fix buglet in spider and non-conforming samples -r2674 | rgbecker | 2005-09-05 17:20:26 +0100 (Mon, 05 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/spider.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - merged utf8 2666:2671 improved spider plots -r2668 | rgbecker | 2005-09-05 11:23:51 +0100 (Mon, 05 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/widgetbase.py - merge utf8 2666:2667 widgetbase: make line[3] back up for line[(3,x)] -r2663 | rgbecker | 2005-09-02 10:44:17 +0100 (Fri, 02 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/utils3d.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - reportlab/graphics/charts: better piechart labels -r2662 | rgbecker | 2005-09-01 17:18:33 +0100 (Thu, 01 Sep 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc: allow special escaping in Annotations and PDFString -r2659 | rgbecker | 2005-08-18 11:28:12 +0100 (Thu, 18 Aug 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - M /reportlab/trunk/reportlab/graphics/charts/axes.py - merge stable 2657:2658 axes/lineplots.py: fix inFill behaviour -r2655 | rgbecker | 2005-08-16 10:40:10 +0100 (Tue, 16 Aug 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_lib_utils.py - merge stable 2653:2654 test_lib_utils.py: ensure we get an incremented count -r2652 | rgbecker | 2005-08-15 12:21:53 +0100 (Mon, 15 Aug 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_lib_utils.py - nerge stable 2649:2650 test_lib_utils.py: fix race in temp creation -r2649 | andy | 2005-07-31 23:47:00 +0100 (Sun, 31 Jul 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/para.py - Chad Miller's table fix -r2648 | rgbecker | 2005-07-28 16:10:18 +0100 (Thu, 28 Jul 2005) | 1 line - M /reportlab/trunk/reportlab/pdfgen/canvas.py - canvas.py: fix probable bug in setdash -r2647 | rgbecker | 2005-07-26 14:47:51 +0100 (Tue, 26 Jul 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/textlabels.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - merge 2643:2644 from stable textlabels.py, barcharts.py: added boxTarget support -r2630 | rgbecker | 2005-07-08 10:18:22 +0100 (Fri, 08 Jul 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_pdfbase_ttfonts.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - merge 2628:2629: ttfonts: altered subset naming -r2623 | rgbecker | 2005-06-27 16:34:29 +0100 (Mon, 27 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - pdfbase.pdfmetrics: fix glob to be zip friendly -r2619 | rgbecker | 2005-06-24 15:49:15 +0100 (Fri, 24 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/test/utils.py - M /reportlab/trunk/reportlab/test/test_widgets_grids.py - M /reportlab/trunk/reportlab/test/test_widgetbase_tpc.py - M /reportlab/trunk/reportlab/test/test_utils.py - M /reportlab/trunk/reportlab/test/test_tools_pythonpoint.py - M /reportlab/trunk/reportlab/test/test_table_layout.py - M /reportlab/trunk/reportlab/test/test_source_chars.py - M /reportlab/trunk/reportlab/test/test_rl_accel.py - M /reportlab/trunk/reportlab/test/test_renderSVG.py - M /reportlab/trunk/reportlab/test/test_pyfiles.py - M /reportlab/trunk/reportlab/test/test_platypus_xref.py - M /reportlab/trunk/reportlab/test/test_platypus_toc.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/test/test_platypus_pto.py - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/test/test_platypus_leftright.py - M /reportlab/trunk/reportlab/test/test_platypus_indents.py - M /reportlab/trunk/reportlab/test/test_platypus_general.py - M /reportlab/trunk/reportlab/test/test_platypus_breaking.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pycanvas.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pagemodes.py - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/test_pdfgen_callback.py - M /reportlab/trunk/reportlab/test/test_pdfbase_ttfonts.py - M /reportlab/trunk/reportlab/test/test_pdfbase_postscript.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfutils.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfmetrics.py - M /reportlab/trunk/reportlab/test/test_pdfbase_fontembed.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - M /reportlab/trunk/reportlab/test/test_multibyte_kor.py - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/test/test_multibyte_cht.py - M /reportlab/trunk/reportlab/test/test_multibyte_chs.py - M /reportlab/trunk/reportlab/test/test_lib_validators.py - M /reportlab/trunk/reportlab/test/test_lib_utils.py - M /reportlab/trunk/reportlab/test/test_lib_sequencer.py - M /reportlab/trunk/reportlab/test/test_lib_colors.py - M /reportlab/trunk/reportlab/test/test_invariant.py - M /reportlab/trunk/reportlab/test/test_images.py - M /reportlab/trunk/reportlab/test/test_hello.py - M /reportlab/trunk/reportlab/test/test_graphics_speed.py - M /reportlab/trunk/reportlab/test/test_graphics_layout.py - M /reportlab/trunk/reportlab/test/test_graphics_images.py - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/test/test_extra.py - M /reportlab/trunk/reportlab/test/test_docstrings.py - M /reportlab/trunk/reportlab/test/test_docs_build.py - M /reportlab/trunk/reportlab/test/test_charts_textlabels.py - M /reportlab/trunk/reportlab/test/runAll.py - reportlab/test: fix so individual tests print output folder location -r2617 | andy | 2005-06-15 21:23:07 +0100 (Wed, 15 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - generalized a rarely-used error message handler which changed in 2.3 -r2613 | rgbecker | 2005-06-15 14:46:10 +0100 (Wed, 15 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/spider.py - spider.py: fix all zero bug (inspired by Max M) -r2611 | rgbecker | 2005-06-13 11:13:47 +0100 (Mon, 13 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - utils.py: fix argument reference -r2608 | rgbecker | 2005-06-12 12:53:56 +0100 (Sun, 12 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_pdfbase_ttfonts.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - ttfonts.py: apply Albertas Agejevas' spaces patch to fix para splitting -r2606 | andy | 2005-06-09 23:07:02 +0100 (Thu, 09 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - added a platypus logger -r2604 | rgbecker | 2005-06-08 11:12:46 +0100 (Wed, 08 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - legends.py: fix 2.1 compatibility problem -r2599 | rgbecker | 2005-06-06 17:07:54 +0100 (Mon, 06 Jun 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - utils.py: raise RuntimeError if no image handling available in ImageReader -r2595 | rgbecker | 2005-05-27 16:40:20 +0100 (Fri, 27 May 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - fundsdemo: added support for special legending -r2592 | rgbecker | 2005-05-16 13:18:46 +0100 (Mon, 16 May 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: allow for h/vAlign constructor agruments to be used -r2591 | andy | 2005-05-13 20:22:10 +0100 (Fri, 13 May 2005) | 1 line - M /reportlab/trunk/reportlab/tools/pythonpoint/pythonpoint.py - added a check for missing files -r2590 | rgbecker | 2005-05-10 14:09:40 +0100 (Tue, 10 May 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/renderPS.py - renderPS.py: attempt to fix setFont error -r2589 | rgbecker | 2005-05-04 12:33:30 +0100 (Wed, 04 May 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/doctemplate.py - doctemplate.py: add rotation argument to BaseDocTemplate -r2588 | rgbecker | 2005-05-03 12:56:56 +0100 (Tue, 03 May 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/doctemplate.py - doctemplate.py: fix syntax error -r2587 | rgbecker | 2005-05-03 12:41:54 +0100 (Tue, 03 May 2005) | 1 line - M /reportlab/trunk/reportlab/lib/colors.py - colors.py: add a fidred -r2586 | rgbecker | 2005-05-03 12:41:17 +0100 (Tue, 03 May 2005) | 1 line - M /reportlab/trunk/reportlab/lib/attrmap.py - attrmap: fix initialization of _initial -r2585 | rgbecker | 2005-05-03 12:40:11 +0100 (Tue, 03 May 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/doctemplate.py - doctemplate.py: support for canvasmaker and rotation -r2584 | rgbecker | 2005-05-03 12:39:22 +0100 (Tue, 03 May 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/lib/utils.py - reportlab: better support for PIL image -r2583 | rgbecker | 2005-04-30 17:31:55 +0100 (Sat, 30 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: fix misspelling of DeviceGray -r2581 | rgbecker | 2005-04-27 13:09:43 +0100 (Wed, 27 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - utils.py: use normpath in _archive etc etc -r2579 | rgbecker | 2005-04-25 11:38:14 +0100 (Mon, 25 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/lib/utils.py - CMYK image improvements -r2576 | rgbecker | 2005-04-20 11:39:42 +0100 (Wed, 20 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fix vertical height bug -r2570 | jjlee | 2005-04-19 13:44:21 +0100 (Tue, 19 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_utils.py - Merge to trunk the fix to a FmtSelfDict test that I accidentally conmitted to a tag (svn merge -r 2568:2569 https://www.reportlab.co.uk/svn/public/reportlab/tags/fidreleases/fidelity-20050310T103800/reportlab) -r2566 | jjlee | 2005-04-18 14:25:25 +0100 (Mon, 18 Apr 2005) | 1 line - A /reportlab/trunk/reportlab/test/test_utils.py - M /reportlab/trunk/reportlab/lib/utils.py - Allow overriding parameters in _fmt utility method -r2561 | rgbecker | 2005-04-15 15:04:49 +0100 (Fri, 15 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/pdfgen/canvas.py - canvas.py: minor changes to bring into line with TBlatter's early serializer version -r2560 | rgbecker | 2005-04-15 15:04:10 +0100 (Fri, 15 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: minor changes to bring into line with TBlatter's early serializer version -r2551 | rgbecker | 2005-04-14 15:53:04 +0100 (Thu, 14 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: minor changes to simplify PDFInfoL/U in other apps -r2550 | rgbecker | 2005-04-12 18:23:17 +0100 (Tue, 12 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fixes to split + spans -r2549 | rgbecker | 2005-04-01 10:38:18 +0100 (Fri, 01 Apr 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: eliminate use of enumerate -r2548 | rgbecker | 2005-03-31 16:35:20 +0100 (Thu, 31 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - pdfmetrics.py: fix minor buglet discovered by Ron Peleg -r2547 | rgbecker | 2005-03-29 14:54:51 +0100 (Tue, 29 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_table_layout.py - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: added in Gary Poster's gary@zope.com latest patch -r2546 | rgbecker | 2005-03-23 13:35:58 +0000 (Wed, 23 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: be evn more robust about width/drawWidth etc -r2545 | rgbecker | 2005-03-23 11:35:10 +0000 (Wed, 23 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fix for case when minWidth fails du eot attribute error -r2544 | rgbecker | 2005-03-18 18:51:22 +0000 (Fri, 18 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: improve join/cap conversion -r2543 | rptlab | 2005-03-18 15:15:23 +0000 (Fri, 18 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: 2.1 compatibility changes -r2542 | rgbecker | 2005-03-18 14:37:49 +0000 (Fri, 18 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - tables.py, paragraph.py: Gary Poster's layout improvement patch -r2541 | rgbecker | 2005-03-18 14:37:01 +0000 (Fri, 18 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/doctemplate.py - doctemplate.py: fix cgitb recursion problem -r2540 | rgbecker | 2005-03-17 12:01:27 +0000 (Thu, 17 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fix Gary's patch (broken by Robin) -r2539 | rgbecker | 2005-03-16 18:46:16 +0000 (Wed, 16 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fixes to join/cap/dash from Gary Poster -r2538 | rgbecker | 2005-03-16 09:47:07 +0000 (Wed, 16 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: empty cellval patch from Jean-Francois Gosset -r2537 | rgbecker | 2005-03-15 14:19:29 +0000 (Tue, 15 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/pdfgen/pathobject.py - pathobject: speed improvements and initial assertion -r2536 | andy | 2005-03-14 20:04:55 +0000 (Mon, 14 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - enhanced decimal alignment -r2535 | rgbecker | 2005-03-11 10:34:29 +0000 (Fri, 11 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/doctemplate.py - doctemplate.py: fix buglet reported by Steve Halasz -r2530 | jjlee | 2005-03-02 14:27:57 +0000 (Wed, 02 Mar 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - Add process environment to logged traceback -r2528 | rgbecker | 2005-02-28 15:55:58 +0000 (Mon, 28 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - canvas.py: added in Andrew Mercer's linkURL fix -r2526 | rgbecker | 2005-02-21 15:40:08 +0000 (Mon, 21 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_paragraphs.py - test_paragraphs.py: added font size change test for underlining test -r2525 | rgbecker | 2005-02-21 14:55:11 +0000 (Mon, 21 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_paragraphs.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - paragraph.py: added proper undeerline color -r2524 | rgbecker | 2005-02-17 08:43:01 +0000 (Thu, 17 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/lib/units.py - units.py: fix pica conversion thanks to Paul McNett -r2523 | rgbecker | 2005-02-07 17:59:09 +0000 (Mon, 07 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - utils.py: added FmtSelfDict class -r2522 | rgbecker | 2005-02-07 11:26:31 +0000 (Mon, 07 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/shapes.py - shapes.py: added save renderer specific argument handling -r2521 | rgbecker | 2005-02-07 10:29:15 +0000 (Mon, 07 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/pdfgen/canvas.py - canvas.py: added setFontSize method -r2520 | rgbecker | 2005-02-05 10:19:05 +0000 (Sat, 05 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: fix 2.1 breakage -r2519 | rgbecker | 2005-02-03 17:14:58 +0000 (Thu, 03 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - platypus: make LCActionFlowable the bad ones -r2518 | rgbecker | 2005-02-03 11:30:21 +0000 (Thu, 03 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - platypus: attempt to make KeepTogether/keepWithNext more robust -r2517 | rgbecker | 2005-02-02 14:43:28 +0000 (Wed, 02 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - reportlab/lib/utils.py: remove bad checkin -r2516 | rgbecker | 2005-02-02 14:40:15 +0000 (Wed, 02 Feb 2005) | 1 line - M /reportlab/trunk/reportlab/lib/utils.py - fix reportlab_functional.txt -r2515 | andy | 2005-01-31 16:47:03 +0000 (Mon, 31 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/paraparser.py - allow solid para background -r2514 | andy | 2005-01-27 21:44:35 +0000 (Thu, 27 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/platypus/paragraph.py - minor fix to allow getPlaintext on more paragraphs -r2512 | rgbecker | 2005-01-14 16:06:57 +0000 (Fri, 14 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: added span rows split handling & test contributed by Andre Reitz -r2511 | rgbecker | 2005-01-14 15:42:12 +0000 (Fri, 14 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/setup.py - setup.py: fix missing comma, contributed by Andre Reitz -r2510 | rgbecker | 2005-01-13 11:50:57 +0000 (Thu, 13 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/axes.py - axes.py: finally make abf/rr work properly -r2509 | rgbecker | 2005-01-10 18:29:17 +0000 (Mon, 10 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/renderPS.py - renderPS.py: fix buglet in color setting -r2507 | rgbecker | 2005-01-10 10:04:09 +0000 (Mon, 10 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - legends.py: fix for empty legend prob -r2506 | rgbecker | 2005-01-06 12:21:25 +0000 (Thu, 06 Jan 2005) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_leftright.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - added canvas.textAnnotation and tests for it -r2504 | rgbecker | 2004-12-31 12:49:02 +0000 (Fri, 31 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - legends.py: fix callout bug -r2502 | rgbecker | 2004-12-31 11:20:06 +0000 (Fri, 31 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/platypus/tables.py - added longTableOptimize config variable -r2501 | rgbecker | 2004-12-31 11:13:44 +0000 (Fri, 31 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - test_graphics_charts.py: added new legend testing -r2500 | rgbecker | 2004-12-30 09:51:30 +0000 (Thu, 30 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - barcharts.py: fix missing getattr -r2499 | rgbecker | 2004-12-29 17:12:34 +0000 (Wed, 29 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/doughnut.py - Fix up missing initialization -r2498 | rgbecker | 2004-12-29 17:11:02 +0000 (Wed, 29 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - M /reportlab/trunk/reportlab/graphics/charts/doughnut.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - barcharts.py: NoneOr was miscapitalized -r2497 | rgbecker | 2004-12-29 14:08:38 +0000 (Wed, 29 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - charts: minor adjustments to legends/swatches for pies/barcharts -r2496 | rgbecker | 2004-12-23 15:38:16 +0000 (Thu, 23 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - legends.py: added variColumn attribute -r2495 | rgbecker | 2004-12-22 18:25:37 +0000 (Wed, 22 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - graphics: minor legend tweaks -r2494 | rgbecker | 2004-12-22 16:50:08 +0000 (Wed, 22 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/axes.py - axes.py: added categegorAxis tickShift -r2493 | rgbecker | 2004-12-22 16:14:25 +0000 (Wed, 22 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/graphics/charts/spider.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - M /reportlab/trunk/reportlab/graphics/charts/linecharts.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - charts: autolegending in place, legend now has boxAnchor -r2492 | rgbecker | 2004-12-21 18:02:28 +0000 (Tue, 21 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - M /reportlab/trunk/reportlab/graphics/charts/linecharts.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - M /reportlab/trunk/reportlab/graphics/charts/axes.py - Auto Legends -r2491 | andy | 2004-12-21 05:29:55 +0000 (Tue, 21 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - missing isString import -r2490 | rgbecker | 2004-12-20 19:00:02 +0000 (Mon, 20 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - validators.py: better auto support -r2489 | rgbecker | 2004-12-20 18:59:35 +0000 (Mon, 20 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - legends.py: more smart changes -r2488 | rgbecker | 2004-12-19 09:19:07 +0000 (Sun, 19 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/legends.py - legends.py: fix missing import -r2487 | rgbecker | 2004-12-19 09:15:56 +0000 (Sun, 19 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/lib/validators.py - validators.py: forgot to check in with AutoOr -r2486 | rgbecker | 2004-12-17 16:58:32 +0000 (Fri, 17 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/widgetbase.py - M /reportlab/trunk/reportlab/graphics/charts/spider.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/doughnut.py - graphics: changes for SmartLegend -r2485 | rgbecker | 2004-12-16 16:42:12 +0000 (Thu, 16 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - lineplots.py: allow filled/unfilled plotting -r2484 | andy | 2004-12-10 07:27:50 +0000 (Fri, 10 Dec 2004) | 1 line - A /reportlab/trunk/reportlab/test/test_platypus_leftright.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - M /reportlab/trunk/reportlab/lib/corp.py - left/right template support -r2483 | rgbecker | 2004-12-06 17:00:44 +0000 (Mon, 06 Dec 2004) | 1 line - M /reportlab/trunk/reportlab/test/runAll.py - reportlab.test.utils.py: fix 2.4 problem -r2480 | rgbecker | 2004-11-25 14:27:07 +0000 (Thu, 25 Nov 2004) | 1 line - M /reportlab/trunk/reportlab/changes - reportlab: prparing for 1.20 re-release -r2478 | rgbecker | 2004-11-25 14:18:03 +0000 (Thu, 25 Nov 2004) | 1 line - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: extend span line blocking to multi lines -################################################################################# -#################### RELEASE 1.20 at 18:00 GMT 25/Nov/2004 ################# -################################################################################# -r2478 | rgbecker | 2004-11-25 14:18:03 +0000 (Thu, 25 Nov 2004) - tables.py: extend span line blocking to multi lines -r2477 | rgbecker | 2004-11-24 18:29:57 +0000 (Wed, 24 Nov 2004) - setup.py: add backin the missing sub-packages -r2476 | rgbecker | 2004-11-24 16:42:12 +0000 (Wed, 24 Nov 2004) - reportlab: table improvments and better documentation -r2475 | rgbecker | 2004-11-23 17:08:33 +0000 (Tue, 23 Nov 2004) - reportlab.setup.py: now works with and without rl_accel -r2462 | rgbecker | 2004-11-03 20:48:51 +0000 (Wed, 03 Nov 2004) - M /reportlab/trunk/reportlab/lib/utils.py - reportlab lib utils.py: add find_locals -r2461 | rgbecker | 2004-11-03 15:38:07 +0000 (Wed, 03 Nov 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - reportlab graphics: changes for canvasadapter -r2460 | rgbecker | 2004-11-02 10:18:55 +0000 (Tue, 02 Nov 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: fix no PIL jpg problem reported by Martin.Zohlhuber@tttech.com -r2459 | rgbecker | 2004-10-23 16:46:56 +0100 (Sat, 23 Oct 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/platypus/flowables.py - canvasadapter.py fixes -r2458 | rgbecker | 2004-10-22 17:37:01 +0100 (Fri, 22 Oct 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/lib/colors.py - minor changes for pdf canvas compatibility and tests -r2457 | rgbecker | 2004-10-20 20:27:07 +0100 (Wed, 20 Oct 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - M /reportlab/trunk/reportlab/platypus/tables.py - CanvasAdapter related changes -r2456 | rgbecker | 2004-10-19 18:52:20 +0100 (Tue, 19 Oct 2004) - M /reportlab/trunk/reportlab/platypus/paragraph.py - paragraph.py: use canvas.setFillColor -r2455 | rgbecker | 2004-10-07 18:51:31 +0100 (Thu, 07 Oct 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - pdfutils.py: added _fusc -r2454 | rgbecker | 2004-10-07 10:36:36 +0100 (Thu, 07 Oct 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/stdparser.py - stdparser.py: fix 2.1 brokenness -r2453 | andy | 2004-10-05 23:34:14 +0100 (Tue, 05 Oct 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - fixed col/row shading bug where last row or col never got shaded - reported R Revill -r2452 | andy | 2004-10-04 21:52:20 +0100 (Mon, 04 Oct 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Tim Roberts' fix to vertical table cell alignment -r2451 | andy | 2004-10-04 08:03:42 +0100 (Mon, 04 Oct 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/stdparser.py - Francesco Pierfederici [fpierfed@noao.edu] fix to incorrect path argument -r2450 | rgbecker | 2004-10-01 13:28:12 +0100 (Fri, 01 Oct 2004) - M /reportlab/trunk/reportlab/rl_config.py - rl_config.py: use import for local_rl_config -r2449 | rgbecker | 2004-10-01 11:40:15 +0100 (Fri, 01 Oct 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: fix buglet in command adding -r2448 | andy | 2004-09-29 11:18:32 +0100 (Wed, 29 Sep 2004) - M /reportlab/trunk/reportlab/lib/colors.py - M /reportlab/trunk/reportlab/lib/corp.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - table color cycle changes -r2447 | andy | 2004-09-29 10:17:52 +0100 (Wed, 29 Sep 2004) - M /reportlab/trunk/reportlab/license.txt - Updated to 2004 -r2446 | rgbecker | 2004-09-23 17:52:19 +0100 (Thu, 23 Sep 2004) - D /reportlab/trunk/reportlab/lib/Makefile.pre.in - D /reportlab/trunk/reportlab/lib/README.extensions - D /reportlab/trunk/reportlab/lib/Setup.in - D /reportlab/trunk/reportlab/lib/__BUILD.dsw - D /reportlab/trunk/reportlab/lib/_rl_accel.c - D /reportlab/trunk/reportlab/lib/_rl_accel.dsp - D /reportlab/trunk/reportlab/lib/_rl_accel.java - D /reportlab/trunk/reportlab/lib/hnjalloc.c - D /reportlab/trunk/reportlab/lib/hnjalloc.h - D /reportlab/trunk/reportlab/lib/hyphen.c - D /reportlab/trunk/reportlab/lib/hyphen.h - D /reportlab/trunk/reportlab/lib/hyphen.mashed - D /reportlab/trunk/reportlab/lib/pyHnj.dsp - D /reportlab/trunk/reportlab/lib/pyHnjmodule.c - D /reportlab/trunk/reportlab/lib/setup.py - D /reportlab/trunk/reportlab/lib/sgmlop.c - D /reportlab/trunk/reportlab/lib/sgmlop.dsp - M /reportlab/trunk/reportlab/setup.py - A /reportlab/trunk/rl_addons/rl_accel - A /reportlab/trunk/rl_addons/rl_accel/Makefile.pre.in (from /reportlab/trunk/reportlab/lib/Makefile.pre.in:2444) - A /reportlab/trunk/rl_addons/rl_accel/README.extensions (from /reportlab/trunk/reportlab/lib/README.extensions:2444) - A /reportlab/trunk/rl_addons/rl_accel/Setup.in (from /reportlab/trunk/reportlab/lib/Setup.in:2444) - A /reportlab/trunk/rl_addons/rl_accel/__BUILD.dsw (from /reportlab/trunk/reportlab/lib/__BUILD.dsw:2444) - A /reportlab/trunk/rl_addons/rl_accel/_rl_accel.c (from /reportlab/trunk/reportlab/lib/_rl_accel.c:2444) - A /reportlab/trunk/rl_addons/rl_accel/_rl_accel.dsp (from /reportlab/trunk/reportlab/lib/_rl_accel.dsp:2444) - A /reportlab/trunk/rl_addons/rl_accel/_rl_accel.java (from /reportlab/trunk/reportlab/lib/_rl_accel.java:2444) - A /reportlab/trunk/rl_addons/rl_accel/hnjalloc.c (from /reportlab/trunk/reportlab/lib/hnjalloc.c:2444) - A /reportlab/trunk/rl_addons/rl_accel/hnjalloc.h (from /reportlab/trunk/reportlab/lib/hnjalloc.h:2444) - A /reportlab/trunk/rl_addons/rl_accel/hyphen.c (from /reportlab/trunk/reportlab/lib/hyphen.c:2444) - A /reportlab/trunk/rl_addons/rl_accel/hyphen.h (from /reportlab/trunk/reportlab/lib/hyphen.h:2444) - A /reportlab/trunk/rl_addons/rl_accel/hyphen.mashed (from /reportlab/trunk/reportlab/lib/hyphen.mashed:2444) - A /reportlab/trunk/rl_addons/rl_accel/pyHnj.dsp (from /reportlab/trunk/reportlab/lib/pyHnj.dsp:2444) - A /reportlab/trunk/rl_addons/rl_accel/pyHnjmodule.c (from /reportlab/trunk/reportlab/lib/pyHnjmodule.c:2444) - A /reportlab/trunk/rl_addons/rl_accel/setup.py (from /reportlab/trunk/reportlab/lib/setup.py:2444) - A /reportlab/trunk/rl_addons/rl_accel/sgmlop.c (from /reportlab/trunk/reportlab/lib/sgmlop.c:2444) - A /reportlab/trunk/rl_addons/rl_accel/sgmlop.dsp (from /reportlab/trunk/reportlab/lib/sgmlop.dsp:2444) - reportlab/lib: moved _rl_accel and friends to rl_addons/rl_accel -r2445 | rgbecker | 2004-09-23 13:36:41 +0100 (Thu, 23 Sep 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: robustify repr -r2443 | rgbecker | 2004-09-22 09:57:45 +0100 (Wed, 22 Sep 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - flowables.py: remove 2.1 incompatibility -r2442 | rgbecker | 2004-09-20 17:31:22 +0100 (Mon, 20 Sep 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/test/test_platypus_pto.py - platypus/test: working pto (& inner pto) -r2441 | rgbecker | 2004-09-17 11:46:56 +0100 (Fri, 17 Sep 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - renderPM.py: backward compatibility fix for _renderPM version <=0.98 -r2440 | rgbecker | 2004-09-15 10:40:03 +0100 (Wed, 15 Sep 2004) - M /reportlab/trunk/reportlab/setup.py - setup.py: fix get_version using patch from michael@stroeder.com -r2439 | rgbecker | 2004-09-14 12:08:13 +0100 (Tue, 14 Sep 2004) - M /reportlab/trunk/reportlab/platypus/__init__.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/tables.py - A /reportlab/trunk/reportlab/test/test_platypus_pto.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - platypus: added ptocontainer, moved tables test -r2438 | rgbecker | 2004-09-13 09:41:41 +0100 (Mon, 13 Sep 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - test_platypus_general.py: remove another 2.1 scope problem -r2437 | rgbecker | 2004-09-12 11:06:45 +0100 (Sun, 12 Sep 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - test_platypus_general.py: eliminate 2.1 scope problem -r2436 | rgbecker | 2004-09-11 15:18:33 +0100 (Sat, 11 Sep 2004) - M /reportlab/trunk/reportlab/lib/randomtext.py - randomtext.py: robustify textwrap import -r2435 | rgbecker | 2004-09-10 10:54:15 +0100 (Fri, 10 Sep 2004) - M /reportlab/trunk/reportlab/lib/randomtext.py - randomtext.py: fix argument handling -r2434 | rgbecker | 2004-09-09 18:42:18 +0100 (Thu, 09 Sep 2004) - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/rl_config.py - move _FUZZ to rl_config -r2433 | rgbecker | 2004-09-09 18:01:57 +0100 (Thu, 09 Sep 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc.py: improve image masking given by Colors -r2432 | rgbecker | 2004-09-09 12:56:24 +0100 (Thu, 09 Sep 2004) - M /reportlab/trunk/reportlab/lib/randomtext.py - randomtext.py: some improvements -r2431 | rgbecker | 2004-09-08 18:41:10 +0100 (Wed, 08 Sep 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - flowables.py: minor change to PTOContainer -r2430 | rgbecker | 2004-09-08 18:37:26 +0100 (Wed, 08 Sep 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/frames.py - platypus: almost finished PTOContainer -r2429 | rgbecker | 2004-09-08 12:02:58 +0100 (Wed, 08 Sep 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - tables.py: implement Eric Stephen's border drawing suggestion -r2428 | rgbecker | 2004-09-08 11:47:55 +0100 (Wed, 08 Sep 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - test_platypus_general.py: fix so all output seen -r2427 | rgbecker | 2004-09-03 10:53:17 +0100 (Fri, 03 Sep 2004) - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/xmlparser.c - pyRXP: fix buf bug in xmlparser.c -->1.05 -r2426 | rgbecker | 2004-09-02 12:52:56 +0100 (Thu, 02 Sep 2004) - M /reportlab/trunk/reportlab/platypus/xpreformatted.py - XPreformatted: make __init__ lead args same as for paragraph -r2425 | rgbecker | 2004-08-27 17:08:32 +0100 (Fri, 27 Aug 2004) - M /reportlab/trunk/reportlab/graphics/charts/axes.py - axes.py: fix format bug -r2424 | rgbecker | 2004-08-25 15:38:48 +0100 (Wed, 25 Aug 2004) - M /reportlab/trunk/reportlab/platypus/paraparser.py - paraparser.py: minor speedup -r2423 | rgbecker | 2004-08-24 12:36:22 +0100 (Tue, 24 Aug 2004) - M /reportlab/trunk/reportlab/lib/sequencer.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - added seqchain/format tags -r2422 | rgbecker | 2004-08-23 16:54:25 +0100 (Mon, 23 Aug 2004) - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - fix backcolour bug -r2421 | rgbecker | 2004-08-20 17:12:33 +0100 (Fri, 20 Aug 2004) - M /reportlab/trunk/reportlab/platypus/__init__.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - minor twitching on Indenter -r2420 | rgbecker | 2004-08-19 23:17:05 +0100 (Thu, 19 Aug 2004) - M /reportlab/trunk/reportlab/lib/utils.py - reportlab/lib/utils.py: added flatten utility function -r2419 | rgbecker | 2004-08-19 10:58:05 +0100 (Thu, 19 Aug 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - _rl_accel.c: patch from Yves Bastide -r2418 | rgbecker | 2004-08-18 14:11:15 +0100 (Wed, 18 Aug 2004) - M /reportlab/trunk/reportlab/graphics/charts/textlabels.py - M /reportlab/trunk/reportlab/graphics/widgetbase.py - remove 2.2 isms -r2417 | rgbecker | 2004-08-18 12:17:54 +0100 (Wed, 18 Aug 2004) - M /reportlab/trunk/reportlab/graphics/charts/textlabels.py - textlabels.py: added keyword arguments -r2416 | rgbecker | 2004-08-17 14:52:21 +0100 (Tue, 17 Aug 2004) - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - lineplots.py: added filler handling -r2415 | rgbecker | 2004-08-07 16:00:28 +0100 (Sat, 07 Aug 2004) - M /reportlab/trunk/reportlab/graphics/renderPDF.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - M /reportlab/trunk/reportlab/graphics/renderSVG.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - M /reportlab/trunk/reportlab/rl_config.py - rl_config add _unset_, graphics.renderxxx refactoring -r2414 | rgbecker | 2004-07-28 08:53:41 +0100 (Wed, 28 Jul 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - pdfdoc: fix rare bug (exposed in tinyrml) -r2413 | rgbecker | 2004-07-26 09:56:04 +0100 (Mon, 26 Jul 2004) - M /reportlab/trunk/rl_addons/pyRXP/rxp/system.h - Attempt to fix LONG_LONG defn problem -r2412 | rgbecker | 2004-07-23 17:57:11 +0100 (Fri, 23 Jul 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/rl_addons/renderPM/_renderPM.c - M /reportlab/trunk/rl_addons/renderPM/gt1/gt1-parset1.c - M /reportlab/trunk/rl_addons/renderPM/gt1/gt1-parset1.h - Add reader callback arg to _renderPM.makeT1Font -r2411 | rgbecker | 2004-07-23 14:48:21 +0100 (Fri, 23 Jul 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Minor fixes to lib.utils.rl_get_module -r2410 | rgbecker | 2004-07-23 10:43:00 +0100 (Fri, 23 Jul 2004) - M /reportlab/trunk/reportlab/test/test_pdfbase_ttfonts.py - test_pdfbase_ttfonts.py add multipage check -r2409 | rgbecker | 2004-07-22 11:59:29 +0100 (Thu, 22 Jul 2004) - M /reportlab/trunk/reportlab/lib/utils.py - reportlab.lib.utils fix rl_isdir -r2408 | rgbecker | 2004-07-17 20:43:44 +0100 (Sat, 17 Jul 2004) - M /reportlab/trunk/rl_addons/pyRXP/patches/differ.bat - D /reportlab/trunk/rl_addons/pyRXP/patches/rl-1.2.5.patch - A /reportlab/trunk/rl_addons/pyRXP/patches/rl-1.4.0-patch - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/COPYRIGHT - M /reportlab/trunk/rl_addons/pyRXP/rxp/Makefile - M /reportlab/trunk/rl_addons/pyRXP/rxp/Manual - A /reportlab/trunk/rl_addons/pyRXP/rxp/catalog.c - A /reportlab/trunk/rl_addons/pyRXP/rxp/catalog.h - A /reportlab/trunk/rl_addons/pyRXP/rxp/catalog_dtd.c - A /reportlab/trunk/rl_addons/pyRXP/rxp/catutil.c - A /reportlab/trunk/rl_addons/pyRXP/rxp/catutil.h - M /reportlab/trunk/rl_addons/pyRXP/rxp/ctype16.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/ctype16.h - M /reportlab/trunk/rl_addons/pyRXP/rxp/dtd.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/dtd.h - A /reportlab/trunk/rl_addons/pyRXP/rxp/entityopener.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/infoset-print.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/input.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/input.h - M /reportlab/trunk/rl_addons/pyRXP/rxp/namespaces.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/namespaces.h - A /reportlab/trunk/rl_addons/pyRXP/rxp/nf16check.c - A /reportlab/trunk/rl_addons/pyRXP/rxp/nf16check.h - A /reportlab/trunk/rl_addons/pyRXP/rxp/nf16data.c - A /reportlab/trunk/rl_addons/pyRXP/rxp/nf16data.h - A /reportlab/trunk/rl_addons/pyRXP/rxp/resolve.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/rxp.1 - M /reportlab/trunk/rl_addons/pyRXP/rxp/rxp.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/rxputil.h - M /reportlab/trunk/rl_addons/pyRXP/rxp/stdio16.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/system.h - M /reportlab/trunk/rl_addons/pyRXP/rxp/url.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/version.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/xmlparser.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/xmlparser.h - M /reportlab/trunk/rl_addons/pyRXP/setup.py - Synchronize with rxp-1.4.0 --> version 1.04 -r2407 | rgbecker | 2004-07-15 11:22:38 +0100 (Thu, 15 Jul 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Another know location -r2406 | rgbecker | 2004-07-09 17:29:34 +0100 (Fri, 09 Jul 2004) - M /reportlab/trunk/reportlab/rl_config.py - Add user home to search paths -r2405 | kirpal | 2004-07-09 15:34:52 +0100 (Fri, 09 Jul 2004) - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - changes to refrences of CVS -r2404 | rgbecker | 2004-07-02 14:37:32 +0100 (Fri, 02 Jul 2004) - A /reportlab/trunk/reportlab/tools/utils - A /reportlab/trunk/reportlab/tools/utils/add_bleed.py - Initial checkin -r2402 | kirpal | 2004-06-29 14:28:59 +0100 (Tue, 29 Jun 2004) - D /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - moved to users/kirpals -r2401 | kirpal | 2004-06-29 13:04:15 +0100 (Tue, 29 Jun 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - changes to presentation -r2400 | kirpal | 2004-06-29 10:52:08 +0100 (Tue, 29 Jun 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml -r2399 | kirpal | 2004-06-29 10:37:59 +0100 (Tue, 29 Jun 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - modified presentation -r2398 | kirpal | 2004-06-29 10:19:21 +0100 (Tue, 29 Jun 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - modified presentation -r2397 | kirpal | 2004-06-28 17:09:41 +0100 (Mon, 28 Jun 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - changes to presentation -r2396 | kirpal | 2004-06-28 15:40:16 +0100 (Mon, 28 Jun 2004) - A /reportlab/trunk/reportlab/tools/pythonpoint/demos/confKit.xml - python point presentation for Confkit -r2395 | rgbecker | 2004-06-25 19:10:50 +0100 (Fri, 25 Jun 2004) - M /reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py - M /reportlab/trunk/reportlab/graphics/renderPDF.py - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - D /reportlab/trunk/reportlab/pdfbase/rl_codecs.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - D /reportlab/trunk/reportlab/test/test_platypus_paraparser.py - Removed unicode changes -r2393 | rgbecker | 2004-06-23 15:56:24 +0100 (Wed, 23 Jun 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Fix 2.2 mktemp argument mismatch -r2392 | rgbecker | 2004-06-23 14:56:44 +0100 (Wed, 23 Jun 2004) - M /reportlab/trunk/reportlab/lib/utils.py - M /reportlab/trunk/reportlab/pdfbase/_fontdata.py - M /reportlab/trunk/reportlab/rl_config.py - More changes related to compact distro path recognition -r2391 | rgbecker | 2004-06-23 09:52:43 +0100 (Wed, 23 Jun 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - Changes to bruteforce search, non-invasive changes to rl_accel stuff -r2390 | rgbecker | 2004-06-23 09:49:10 +0100 (Wed, 23 Jun 2004) - M /reportlab/trunk/reportlab/lib/codecharts.py - Fix for unicode difficulties -r2389 | rgbecker | 2004-06-22 18:00:41 +0100 (Tue, 22 Jun 2004) - A /reportlab/trunk/reportlab/pdfbase/rl_codecs.py - First try at Adobe T1 encodings -r2388 | rgbecker | 2004-06-21 15:07:46 +0100 (Mon, 21 Jun 2004) - M /reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py - Attempt to fix up for unicode -r2387 | jjlee | 2004-06-18 11:39:08 +0100 (Fri, 18 Jun 2004) - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - Fixed zero-length string case in stringWidth -r2386 | rgbecker | 2004-06-17 16:26:26 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - M /reportlab/trunk/rl_addons/pyRXP/rxp/url.h - M /reportlab/trunk/rl_addons/pyRXP/setup.py - Remove $Header:, fix CopyRight & history -r2385 | rgbecker | 2004-06-17 16:26:05 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/reportlab/__init__.py - M /reportlab/trunk/reportlab/demos/colors/colortest.py - M /reportlab/trunk/reportlab/demos/gadflypaper/gfe.py - M /reportlab/trunk/reportlab/demos/odyssey/dodyssey.py - M /reportlab/trunk/reportlab/demos/odyssey/fodyssey.py - M /reportlab/trunk/reportlab/demos/odyssey/odyssey.py - M /reportlab/trunk/reportlab/demos/rlzope/rlzope.py - M /reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py - M /reportlab/trunk/reportlab/demos/tests/testdemos.py - M /reportlab/trunk/reportlab/docs/graphguide/ch1_intro.py - M /reportlab/trunk/reportlab/docs/graphguide/ch2_concepts.py - M /reportlab/trunk/reportlab/docs/graphguide/ch3_shapes.py - M /reportlab/trunk/reportlab/docs/graphguide/ch4_widgets.py - M /reportlab/trunk/reportlab/docs/graphguide/ch5_charts.py - M /reportlab/trunk/reportlab/docs/graphguide/gengraphguide.py - M /reportlab/trunk/reportlab/docs/reference/genreference.py - M /reportlab/trunk/reportlab/docs/userguide/app_demos.py - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - M /reportlab/trunk/reportlab/docs/userguide/ch2_graphics.py - M /reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py - M /reportlab/trunk/reportlab/docs/userguide/ch3_pdffeatures.py - M /reportlab/trunk/reportlab/docs/userguide/ch4_platypus_concepts.py - M /reportlab/trunk/reportlab/docs/userguide/ch5_paragraphs.py - M /reportlab/trunk/reportlab/docs/userguide/ch6_tables.py - M /reportlab/trunk/reportlab/docs/userguide/ch7_custom.py - M /reportlab/trunk/reportlab/docs/userguide/ch9_future.py - M /reportlab/trunk/reportlab/docs/userguide/genuserguide.py - M /reportlab/trunk/reportlab/extensions/__init__.py - M /reportlab/trunk/reportlab/graphics/__init__.py - M /reportlab/trunk/reportlab/graphics/charts/__init__.py - M /reportlab/trunk/reportlab/graphics/charts/areas.py - M /reportlab/trunk/reportlab/graphics/charts/axes.py - M /reportlab/trunk/reportlab/graphics/charts/barcharts.py - M /reportlab/trunk/reportlab/graphics/charts/doughnut.py - M /reportlab/trunk/reportlab/graphics/charts/legends.py - M /reportlab/trunk/reportlab/graphics/charts/linecharts.py - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - M /reportlab/trunk/reportlab/graphics/charts/markers.py - M /reportlab/trunk/reportlab/graphics/charts/piecharts.py - M /reportlab/trunk/reportlab/graphics/charts/spider.py - M /reportlab/trunk/reportlab/graphics/charts/textlabels.py - M /reportlab/trunk/reportlab/graphics/charts/utils.py - M /reportlab/trunk/reportlab/graphics/renderPDF.py - M /reportlab/trunk/reportlab/graphics/renderPM.py - M /reportlab/trunk/reportlab/graphics/renderPS.py - M /reportlab/trunk/reportlab/graphics/renderbase.py - M /reportlab/trunk/reportlab/graphics/samples/__init__.py - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/graphics/testdrawings.py - M /reportlab/trunk/reportlab/graphics/testshapes.py - M /reportlab/trunk/reportlab/graphics/widgetbase.py - M /reportlab/trunk/reportlab/graphics/widgets/__init__.py - M /reportlab/trunk/reportlab/graphics/widgets/eventcal.py - M /reportlab/trunk/reportlab/graphics/widgets/flags.py - M /reportlab/trunk/reportlab/graphics/widgets/grids.py - M /reportlab/trunk/reportlab/graphics/widgets/markers.py - M /reportlab/trunk/reportlab/graphics/widgets/signsandsymbols.py - M /reportlab/trunk/reportlab/lib/__init__.py - M /reportlab/trunk/reportlab/lib/_rl_accel.c - M /reportlab/trunk/reportlab/lib/abag.py - M /reportlab/trunk/reportlab/lib/attrmap.py - M /reportlab/trunk/reportlab/lib/codecharts.py - M /reportlab/trunk/reportlab/lib/colors.py - M /reportlab/trunk/reportlab/lib/corp.py - M /reportlab/trunk/reportlab/lib/enums.py - M /reportlab/trunk/reportlab/lib/extformat.py - M /reportlab/trunk/reportlab/lib/fonts.py - M /reportlab/trunk/reportlab/lib/formatters.py - M /reportlab/trunk/reportlab/lib/logger.py - M /reportlab/trunk/reportlab/lib/pagesizes.py - M /reportlab/trunk/reportlab/lib/randomtext.py - M /reportlab/trunk/reportlab/lib/sequencer.py - M /reportlab/trunk/reportlab/lib/set_ops.py - M /reportlab/trunk/reportlab/lib/setup.py - M /reportlab/trunk/reportlab/lib/styles.py - M /reportlab/trunk/reportlab/lib/tocindex.py - M /reportlab/trunk/reportlab/lib/units.py - M /reportlab/trunk/reportlab/lib/utils.py - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/lib/yaml.py - M /reportlab/trunk/reportlab/pdfbase/__init__.py - M /reportlab/trunk/reportlab/pdfbase/_cidfontdata.py - M /reportlab/trunk/reportlab/pdfbase/_fontdata.py - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfgen/__init__.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/pathobject.py - M /reportlab/trunk/reportlab/pdfgen/pdfgeom.py - M /reportlab/trunk/reportlab/pdfgen/pdfimages.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/platypus/__init__.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - M /reportlab/trunk/reportlab/platypus/figures.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/platypus/tableofcontents.py - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/platypus/xpreformatted.py - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/setup.py - M /reportlab/trunk/reportlab/test/__init__.py - M /reportlab/trunk/reportlab/test/runAll.py - M /reportlab/trunk/reportlab/test/test_charts_textlabels.py - M /reportlab/trunk/reportlab/test/test_docstrings.py - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/test/test_graphics_images.py - M /reportlab/trunk/reportlab/test/test_graphics_layout.py - M /reportlab/trunk/reportlab/test/test_graphics_speed.py - M /reportlab/trunk/reportlab/test/test_hello.py - M /reportlab/trunk/reportlab/test/test_images.py - M /reportlab/trunk/reportlab/test/test_invariant.py - M /reportlab/trunk/reportlab/test/test_lib_colors.py - M /reportlab/trunk/reportlab/test/test_lib_sequencer.py - M /reportlab/trunk/reportlab/test/test_multibyte_chs.py - M /reportlab/trunk/reportlab/test/test_multibyte_cht.py - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfmetrics.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfutils.py - M /reportlab/trunk/reportlab/test/test_pdfbase_postscript.py - M /reportlab/trunk/reportlab/test/test_pdfgen_callback.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pagemodes.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pycanvas.py - M /reportlab/trunk/reportlab/test/test_platypus_breaking.py - M /reportlab/trunk/reportlab/test/test_platypus_general.py - M /reportlab/trunk/reportlab/test/test_platypus_indents.py - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/test/test_platypus_paraparser.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/test/test_platypus_toc.py - M /reportlab/trunk/reportlab/test/test_platypus_xref.py - M /reportlab/trunk/reportlab/test/test_pyfiles.py - M /reportlab/trunk/reportlab/test/test_source_chars.py - M /reportlab/trunk/reportlab/test/test_widgetbase_tpc.py - M /reportlab/trunk/reportlab/tools/__init__.py - M /reportlab/trunk/reportlab/tools/docco/__init__.py - M /reportlab/trunk/reportlab/tools/docco/codegrab.py - M /reportlab/trunk/reportlab/tools/docco/docpy.py - M /reportlab/trunk/reportlab/tools/docco/examples.py - M /reportlab/trunk/reportlab/tools/docco/graphdocpy.py - M /reportlab/trunk/reportlab/tools/docco/rl_doc_utils.py - M /reportlab/trunk/reportlab/tools/docco/rltemplate.py - M /reportlab/trunk/reportlab/tools/docco/stylesheet.py - M /reportlab/trunk/reportlab/tools/docco/t_parse.py - M /reportlab/trunk/reportlab/tools/docco/yaml.py - M /reportlab/trunk/reportlab/tools/docco/yaml2pdf.py - M /reportlab/trunk/reportlab/tools/py2pdf/__init__.py - M /reportlab/trunk/reportlab/tools/py2pdf/idle_print.py - M /reportlab/trunk/reportlab/tools/pythonpoint/__init__.py - M /reportlab/trunk/reportlab/tools/pythonpoint/customshapes.py - M /reportlab/trunk/reportlab/tools/pythonpoint/styles/__init__.py - M /reportlab/trunk/reportlab/tools/pythonpoint/styles/horrible.py - M /reportlab/trunk/reportlab/tools/pythonpoint/styles/modern.py - Remove $Header:, fix CopyRight & history -r2384 | mike | 2004-06-17 15:50:51 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - Change back to native eol to fix line endings -r2383 | mike | 2004-06-17 15:44:11 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - Temporary change to binary to fix line endings -r2382 | rgbecker | 2004-06-17 15:23:21 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - Attempt to fix lineendings -r2381 | rgbecker | 2004-06-17 13:01:26 +0100 (Thu, 17 Jun 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - Fix encoding for zap & symbol -r2380 | rgbecker | 2004-06-16 12:45:56 +0100 (Wed, 16 Jun 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Forgot to check the exactly equal case for _startswith_rl -r2379 | rgbecker | 2004-06-15 14:10:37 +0100 (Tue, 15 Jun 2004) - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - Moved encoding checks into splitString -r2378 | rgbecker | 2004-06-15 12:34:47 +0100 (Tue, 15 Jun 2004) - M /reportlab/trunk/reportlab/test/test_platypus_paraparser.py - Fix properties and possibly line-endings -r2377 | rgbecker | 2004-06-15 11:33:32 +0100 (Tue, 15 Jun 2004) - M /reportlab/trunk/reportlab/graphics/charts/axes.py - remove use of False (invalid in 2.2) -r2376 | andy | 2004-06-14 23:13:04 +0100 (Mon, 14 Jun 2004) - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - fixed widths bug for tt fonts -r2375 | rgbecker | 2004-06-14 18:51:15 +0100 (Mon, 14 Jun 2004) - M /reportlab/trunk/reportlab/graphics/charts/axes.py -r2374 | andy | 2004-06-14 17:41:25 +0100 (Mon, 14 Jun 2004) - M /reportlab/trunk/reportlab/graphics/renderPDF.py - M /reportlab/trunk/reportlab/graphics/shapes.py - M /reportlab/trunk/reportlab/lib/validators.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/paragraph.py - M /reportlab/trunk/reportlab/platypus/paraparser.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - A /reportlab/trunk/reportlab/test/test_platypus_paraparser.py - Unicode and UTF8 support changes -r2373 | rgbecker | 2004-06-14 17:29:04 +0100 (Mon, 14 Jun 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - Fix import of logger -r2372 | mike | 2004-06-11 18:11:22 +0100 (Fri, 11 Jun 2004) - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - Added more accurate checking of UTF8 output -r2371 | mike | 2004-06-11 17:15:45 +0100 (Fri, 11 Jun 2004) - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - Made some corrections and got all TextEncodingTestCase tests working -r2370 | mike | 2004-06-11 16:25:50 +0100 (Fri, 11 Jun 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - Added support for mac_roman encoding and changed to always convert unicode strings to font encoding -r2369 | robin | 2004-06-11 11:17:18 +0100 (Fri, 11 Jun 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - Early 2.x has no UnicodeDecodeError and no ''.decode -r2368 | andy_robinson | 2004-06-10 01:43:21 +0100 (Thu, 10 Jun 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - Now has runnable encoding-conversion -r2367 | rgbecker | 2004-06-07 17:30:37 +0100 (Mon, 07 Jun 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Add xpre example -r2366 | rgbecker | 2004-06-03 12:26:44 +0100 (Thu, 03 Jun 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Another rework of the open for read code -r2365 | rgbecker | 2004-06-03 11:38:25 +0100 (Thu, 03 Jun 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - Attempt to fix the file: form -r2364 | andy_robinson | 2004-05-29 00:41:18 +0100 (Sat, 29 May 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - M /reportlab/trunk/reportlab/platypus/figures.py - M /reportlab/trunk/reportlab/rl_config.py - M /reportlab/trunk/reportlab/tools/pythonpoint/pythonpoint.dtd - M /reportlab/trunk/reportlab/tools/pythonpoint/pythonpoint.py - M /reportlab/trunk/reportlab/tools/pythonpoint/stdparser.py - Prelim support for unicode conversion (disabled by - rl_config.autoConvertEncodings at first); - borders to drawings in pythonpoint and for figure - objects; -r2363 | rgbecker | 2004-05-28 15:39:42 +0100 (Fri, 28 May 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - Attempt to test http urls for images -r2362 | rgbecker | 2004-05-28 15:04:25 +0100 (Fri, 28 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Attempt to fix open_for_read again -r2361 | rgbecker | 2004-05-28 15:02:14 +0100 (Fri, 28 May 2004) - M /reportlab/trunk/reportlab/test/test_lib_utils.py - Added open_and_read tests -r2360 | rgbecker | 2004-05-26 19:44:55 +0100 (Wed, 26 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - More compact distro changes -r2359 | jjlee | 2004-05-26 10:37:07 +0100 (Wed, 26 May 2004) - M /reportlab/trunk/reportlab/graphics/charts/lineplots.py - M /reportlab/trunk/reportlab/graphics/shapes.py - Make a couple of files emacs font-lock friendly -r2358 | rgbecker | 2004-05-25 22:12:31 +0100 (Tue, 25 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Use more advanced __loader__ friendly code -r2357 | rgbecker | 2004-05-25 17:33:56 +0100 (Tue, 25 May 2004) - M /reportlab/trunk/reportlab/docs/reference/genreference.py - Fix missing module -r2356 | rgbecker | 2004-05-24 13:19:53 +0100 (Mon, 24 May 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - Attempt to fix 1.5 problems -r2355 | rgbecker | 2004-05-23 11:39:30 +0100 (Sun, 23 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - rl_get_module now seems to work as intended -r2354 | rgbecker | 2004-05-23 10:27:52 +0100 (Sun, 23 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - rl_get_module added -r2353 | rgbecker | 2004-05-20 11:10:27 +0100 (Thu, 20 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Attempt to robustify the memo -r2352 | rgbecker | 2004-05-18 18:16:55 +0100 (Tue, 18 May 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Fix proposed by Derik Barclay -r2351 | rgbecker | 2004-05-08 18:19:54 +0100 (Sat, 08 May 2004) - M /reportlab/trunk/reportlab/tools/docco/docpy.py - M /reportlab/trunk/reportlab/tools/docco/graphdocpy.py - D /reportlab/trunk/reportlab/tools/docco/inspect.py - Removed private version of inspect -r2350 | rgbecker | 2004-05-07 17:56:29 +0100 (Fri, 07 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added some more stuff and script name to DebugMemo output -r2349 | rgbecker | 2004-05-07 16:28:33 +0100 (Fri, 07 May 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added capture_traceback=1 to DebugMemo init -r2348 | rgbecker | 2004-05-03 09:13:37 +0100 (Mon, 03 May 2004) - M /reportlab/trunk/reportlab/graphics/widgets/grids.py - Make stripe strokeColor same as fill -r2347 | rgbecker | 2004-05-03 09:11:48 +0100 (Mon, 03 May 2004) - M /reportlab/trunk/reportlab/test/test_docstrings.py - Minor changes -r2346 | rgbecker | 2004-05-03 09:09:52 +0100 (Mon, 03 May 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - Fix flagged and penalty values -r2345 | rgbecker | 2004-04-28 16:27:31 +0100 (Wed, 28 Apr 2004) - M /reportlab/trunk/reportlab/tools/docco/graphdocpy.py - Move to using inspect for getsource (actually works) -r2344 | rgbecker | 2004-04-28 15:41:56 +0100 (Wed, 28 Apr 2004) - M /reportlab/trunk/reportlab/tools/docco/rl_doc_utils.py - Added setStory -r2343 | rgbecker | 2004-04-28 15:40:21 +0100 (Wed, 28 Apr 2004) - M /reportlab/trunk/reportlab/docs/graphguide/gengraphguide.py - M /reportlab/trunk/reportlab/docs/reference/genreference.py - M /reportlab/trunk/reportlab/docs/userguide/ch1_intro.py - M /reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py - M /reportlab/trunk/reportlab/docs/userguide/ch6_tables.py - M /reportlab/trunk/reportlab/docs/userguide/genuserguide.py - Fixing up to not use folder relative import -r2342 | rgbecker | 2004-04-22 18:36:41 +0100 (Thu, 22 Apr 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Attempt to fix the Mike Spanning example -r2341 | rgbecker | 2004-04-21 17:39:36 +0100 (Wed, 21 Apr 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Added Mike's counter span example -r2340 | rgbecker | 2004-04-14 14:10:51 +0100 (Wed, 14 Apr 2004) - M /reportlab/trunk/reportlab/graphics/shapes.py - Fix problem with empty charts -r2339 | rgbecker | 2004-04-08 17:02:52 +0100 (Thu, 08 Apr 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - remove Box stuff prior to 2.3 -r2338 | rgbecker | 2004-04-08 15:17:42 +0100 (Thu, 08 Apr 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - removed PyDoc_STR for now; changed to do hex version checks -r2337 | rgbecker | 2004-04-05 19:07:42 +0100 (Mon, 05 Apr 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - M /reportlab/trunk/reportlab/platypus/tables.py - Minor changes -r2336 | rgbecker | 2004-04-05 15:17:29 +0100 (Mon, 05 Apr 2004) - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - Another attempt to fix up wanrings -r2335 | rgbecker | 2004-04-05 13:59:56 +0100 (Mon, 05 Apr 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - Forgot to bump the version -r2334 | rgbecker | 2004-04-05 08:47:18 +0100 (Mon, 05 Apr 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - Added in BoxList, removed AttrDict -r2333 | rgbecker | 2004-04-01 18:00:33 +0100 (Thu, 01 Apr 2004) - M /reportlab/trunk/reportlab/lib/extformat.py - Added header lines -r2332 | rgbecker | 2004-04-01 17:59:22 +0100 (Thu, 01 Apr 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added get_rl_tempfile -r2331 | rgbecker | 2004-04-01 17:58:02 +0100 (Thu, 01 Apr 2004) - A /reportlab/trunk/reportlab/lib/extformat.py - Added for better formatting -r2330 | rgbecker | 2004-04-01 12:17:02 +0100 (Thu, 01 Apr 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Ensure _colWidths is defined -r2329 | rgbecker | 2004-03-30 15:58:35 +0100 (Tue, 30 Mar 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - Added HRFlowable -r2328 | rgbecker | 2004-03-27 09:42:24 +0000 (Sat, 27 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - Indicate output folder -r2327 | rgbecker | 2004-03-27 09:41:37 +0000 (Sat, 27 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - Ensure external mask gets through in a85 cases -r2326 | rgbecker | 2004-03-26 21:34:03 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - Fix bug introduced during transparency/ImageReader changes -r2325 | rgbecker | 2004-03-26 16:54:37 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/test_tools_pythonpoint.py - Fix buglet in compact testing -r2324 | rgbecker | 2004-03-26 15:44:24 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Added percentages -r2323 | rgbecker | 2004-03-26 14:27:24 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/test_pyfiles.py - Move to having all test output in a temp folder -r2322 | rgbecker | 2004-03-26 14:26:50 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - Add test_*.txt cleanup -r2321 | rgbecker | 2004-03-26 14:20:44 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - M /reportlab/trunk/reportlab/test/test_charts_textlabels.py - M /reportlab/trunk/reportlab/test/test_docstrings.py - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/test/test_graphics_images.py - M /reportlab/trunk/reportlab/test/test_graphics_speed.py - M /reportlab/trunk/reportlab/test/test_hello.py - M /reportlab/trunk/reportlab/test/test_invariant.py - M /reportlab/trunk/reportlab/test/test_lib_colors.py - M /reportlab/trunk/reportlab/test/test_multibyte_chs.py - M /reportlab/trunk/reportlab/test/test_multibyte_cht.py - M /reportlab/trunk/reportlab/test/test_multibyte_jpn.py - M /reportlab/trunk/reportlab/test/test_multibyte_kor.py - M /reportlab/trunk/reportlab/test/test_paragraphs.py - M /reportlab/trunk/reportlab/test/test_pdfbase_encodings.py - M /reportlab/trunk/reportlab/test/test_pdfbase_fontembed.py - M /reportlab/trunk/reportlab/test/test_pdfbase_pdfmetrics.py - M /reportlab/trunk/reportlab/test/test_pdfbase_postscript.py - M /reportlab/trunk/reportlab/test/test_pdfbase_ttfonts.py - M /reportlab/trunk/reportlab/test/test_pdfgen_callback.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - M /reportlab/trunk/reportlab/test/test_pdfgen_links.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pagemodes.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pycanvas.py - M /reportlab/trunk/reportlab/test/test_platypus_breaking.py - M /reportlab/trunk/reportlab/test/test_platypus_general.py - M /reportlab/trunk/reportlab/test/test_platypus_indents.py - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/test/test_platypus_tables.py - M /reportlab/trunk/reportlab/test/test_platypus_toc.py - M /reportlab/trunk/reportlab/test/test_platypus_xref.py - M /reportlab/trunk/reportlab/test/test_renderSVG.py - M /reportlab/trunk/reportlab/test/test_source_chars.py - M /reportlab/trunk/reportlab/test/test_table_layout.py - M /reportlab/trunk/reportlab/test/test_tools_pythonpoint.py - M /reportlab/trunk/reportlab/test/test_widgets_grids.py - M /reportlab/trunk/reportlab/test/utils.py - Move to having all test output in a temp folder -r2320 | rgbecker | 2004-03-26 11:37:09 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/test_platypus_indents.py - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - M /reportlab/trunk/reportlab/test/test_widgetbase_tpc.py - Remove unwanted import -r2319 | rgbecker | 2004-03-26 11:34:11 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/reportlab/test/test_charts_textlabels.py - M /reportlab/trunk/reportlab/test/test_graphics_charts.py - M /reportlab/trunk/reportlab/test/test_lib_utils.py - M /reportlab/trunk/reportlab/test/test_platypus_toc.py - Eliminate bad tempdir setting -r2318 | rgbecker | 2004-03-26 10:48:43 +0000 (Fri, 26 Mar 2004) - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - Fix up bug introduced with new CB API -r2317 | rgbecker | 2004-03-24 17:17:05 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/reportlab/tools/pythonpoint/pythonpoint.py - Add eoCB for compactdistro -r2316 | rgbecker | 2004-03-24 17:16:22 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/reportlab/test/test_tools_pythonpoint.py - Fix test_tools_pythonpoint for compact distro -r2315 | rgbecker | 2004-03-24 17:14:53 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - Allow eoCB to return (URI,contents) for compact distro -r2314 | rgbecker | 2004-03-24 14:04:51 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/cidfonts.py - Switch to using get_rl_tempdir -r2313 | rgbecker | 2004-03-24 14:03:31 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added get_rl_tempdir -r2312 | rgbecker | 2004-03-24 09:42:22 +0000 (Wed, 24 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - True/False aren't defined for Python < 2.3 -r2311 | rgbecker | 2004-03-23 18:57:33 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - M /reportlab/trunk/reportlab/test/utils.py - Remove things that python 2.1 fails on -r2310 | rgbecker | 2004-03-23 17:35:42 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - Compact distro friendly -r2309 | rgbecker | 2004-03-23 17:34:11 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added rl_glob -r2308 | rgbecker | 2004-03-23 17:32:20 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/rl_config.py - Use rl_isdir -r2307 | rgbecker | 2004-03-23 16:18:29 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - Now works in compact distro -r2306 | rgbecker | 2004-03-23 15:43:13 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_invariant.py - Now works in compact distro -r2305 | rgbecker | 2004-03-23 15:37:53 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - Fix for compact distro -r2304 | rgbecker | 2004-03-23 15:20:50 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - We prefer absolute RL DIR -r2303 | rgbecker | 2004-03-23 15:19:21 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_platypus_paragraphs.py - Now works in compact distro -r2302 | rgbecker | 2004-03-23 15:18:34 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/utils.py - Switch to using rl_isdir -r2301 | rgbecker | 2004-03-23 14:46:35 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - Allow specifying the pattern -r2300 | rgbecker | 2004-03-23 14:30:01 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added open_and_readlines -r2299 | rgbecker | 2004-03-23 14:28:47 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_lib_utils.py - M /reportlab/trunk/reportlab/test/test_pyfiles.py - Fix up for compact distro -r2298 | rgbecker | 2004-03-23 14:15:47 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_source_chars.py - Fix up for compact distro (leave Zapping alone) -r2297 | rgbecker | 2004-03-23 13:54:42 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Improvements for compact running -r2296 | rgbecker | 2004-03-23 13:50:40 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - Ensure runnable in compact distro -r2295 | rgbecker | 2004-03-23 12:55:44 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/test/test_lib_utils.py - Allow to run in a compact distro -r2294 | rgbecker | 2004-03-23 12:20:08 +0000 (Tue, 23 Mar 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - Make Image to use open_for_read -r2293 | rgbecker | 2004-03-22 18:09:51 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added rl_isfile -r2292 | rgbecker | 2004-03-22 18:08:50 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - M /reportlab/trunk/reportlab/test/test_images.py - M /reportlab/trunk/reportlab/test/test_pdfgen_pycanvas.py - M /reportlab/trunk/reportlab/test/test_platypus_general.py - Small changes to make tests work compactly -r2291 | rgbecker | 2004-03-22 15:59:22 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/test/utils.py - Remove spurious print -r2290 | rgbecker | 2004-03-22 14:06:12 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/platypus/flowables.py - Fix up more Image cases -r2289 | rgbecker | 2004-03-22 14:01:09 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/test/test_platypus_general.py - Add new Image flowable exemplars -r2288 | rgbecker | 2004-03-22 13:21:06 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/test/runAll.py - M /reportlab/trunk/reportlab/test/test_pyfiles.py - M /reportlab/trunk/reportlab/test/utils.py - Minor changes preparing for zip comapct distros -r2287 | rgbecker | 2004-03-22 13:02:43 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - Get the right fp -r2286 | rgbecker | 2004-03-22 12:54:30 +0000 (Mon, 22 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - Attempt to fix the epc bug -r2285 | andy_robinson | 2004-03-19 22:18:37 +0000 (Fri, 19 Mar 2004) - M /reportlab/trunk/reportlab/docs/userguide/ch4_platypus_concepts.py - Documentation about hAlign -r2284 | rgbecker | 2004-03-19 18:00:56 +0000 (Fri, 19 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - First attempt at reading from the zip archive -r2283 | rgbecker | 2004-03-19 10:55:55 +0000 (Fri, 19 Mar 2004) - M /reportlab/trunk/reportlab/rl_config.py - Sys_version was being used in older python -r2282 | rgbecker | 2004-03-18 15:55:50 +0000 (Thu, 18 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - M /reportlab/trunk/reportlab/pdfbase/pdfdoc.py - M /reportlab/trunk/reportlab/pdfbase/pdfutils.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/pdfgen/pdfimages.py - Fix transparency & jpeg behaviour -r2281 | rgbecker | 2004-03-18 12:05:22 +0000 (Thu, 18 Mar 2004) - M /reportlab/trunk/reportlab/pdfgen/textobject.py - TextObject fixes from Ian Millington -r2280 | rgbecker | 2004-03-17 18:54:06 +0000 (Wed, 17 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Move ImageReader open wrapper to where it should be -r2279 | rgbecker | 2004-03-17 16:29:30 +0000 (Wed, 17 Mar 2004) - M /reportlab/trunk/reportlab/demos/rlzope/rlzope.py - Attempt to fix to match ImageReader -r2278 | rgbecker | 2004-03-17 14:35:51 +0000 (Wed, 17 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Improve the fs distro check -r2277 | rgbecker | 2004-03-17 14:34:41 +0000 (Wed, 17 Mar 2004) - M /reportlab/trunk/reportlab/rl_config.py - Added rlhome/../fonts -r2276 | rgbecker | 2004-03-17 00:21:39 +0000 (Wed, 17 Mar 2004) - M /reportlab/trunk/reportlab/pdfgen/pdfimages.py - Remove spurious print reported by Joel Pearson -r2275 | rgbecker | 2004-03-16 15:28:26 +0000 (Tue, 16 Mar 2004) - M /reportlab/trunk/rl_addons/pyRXP/pyRXP.c - Fix BOM removal buglet & valid/ext-sa/014 -r2274 | andy_robinson | 2004-03-15 23:49:48 +0000 (Mon, 15 Mar 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - corrected erroneous comment -r2273 | rgbecker | 2004-03-14 09:47:14 +0000 (Sun, 14 Mar 2004) - M /reportlab/trunk/reportlab/platypus/tables.py - Slightly more efficient linedrawing -r2272 | andy_robinson | 2004-03-12 23:54:11 +0000 (Fri, 12 Mar 2004) - M /reportlab/trunk/reportlab/lib/corp.py - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/platypus/tables.py - M /reportlab/trunk/reportlab/test/test_pdfgen_general.py - Multiple lines and decimal alignments now supported -r2271 | rgbecker | 2004-03-11 10:57:59 +0000 (Thu, 11 Mar 2004) - M /reportlab/trunk/utils/README - Fix missing command instruction -r2270 | andy_robinson | 2004-03-09 23:24:34 +0000 (Tue, 09 Mar 2004) - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/pdfgen/textobject.py - Truetype fonts now convert latin-1 to utf8 if fed non-utf8 -r2269 | andy_robinson | 2004-03-09 22:22:26 +0000 (Tue, 09 Mar 2004) - M /reportlab/trunk/reportlab/__init__.py - M /reportlab/trunk/reportlab/lib/fonts.py - M /reportlab/trunk/reportlab/pdfbase/_fontdata.py - M /reportlab/trunk/reportlab/pdfbase/pdfmetrics.py - M /reportlab/trunk/reportlab/pdfbase/ttfonts.py - M /reportlab/trunk/reportlab/platypus/doctemplate.py - Font reregistration cleanup -r2268 | rgbecker | 2004-03-08 18:36:05 +0000 (Mon, 08 Mar 2004) - M /reportlab/trunk/reportlab/lib/utils.py - Added _findFiles & CIDict -r2267 | rgbecker | 2004-02-22 17:35:43 +0000 (Sun, 22 Feb 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - URL change from Aleksander Piotrowski -r2266 | rgbecker | 2004-02-17 16:10:18 +0000 (Tue, 17 Feb 2004) - M /reportlab/trunk/reportlab/platypus/frames.py - M /reportlab/trunk/reportlab/rl_config.py - Added overlapAttachedSpace -r2265 | andy_robinson | 2004-02-10 00:12:21 +0000 (Tue, 10 Feb 2004) - M /reportlab/trunk/reportlab/pdfgen/canvas.py - M /reportlab/trunk/reportlab/test/test_pdfbase_postscript.py - Added finegrained control of where postscript commands appear -r2264 | rgbecker | 2004-02-05 18:31:35 +0000 (Thu, 05 Feb 2004) - M /reportlab/trunk/reportlab/platypus/paragraph.py - Fix to from Ulrich Screiner -r2263 | rgbecker | 2004-02-05 18:21:50 +0000 (Thu, 05 Feb 2004) - M /reportlab/trunk/reportlab/pdfgen/pdfimages.py - Fix missing import found by Ulrich Schreiner -r2262 | rgbecker | 2004-02-02 08:43:51 +0000 (Mon, 02 Feb 2004) - M /reportlab/trunk/reportlab/lib/_rl_accel.c - Start on tex lib in C -r2261 | rgbecker | 2004-01-29 17:06:24 +0000 (Thu, 29 Jan 2004) - M /reportlab/trunk/reportlab/graphics/renderPS.py - Attempt to get delayed font setting and proper psNames -r2260 | rgbecker | 2004-01-28 13:03:47 +0000 (Wed, 28 Jan 2004) - M /reportlab/trunk/reportlab/graphics/testshapes.py - Allow for bad fonts in getDrawing13 -r2259 | rgbecker | 2004-01-28 13:03:19 +0000 (Wed, 28 Jan 2004) - M /reportlab/trunk/reportlab/graphics/renderPM.py - Allow for no eps preview in tests -r2258 | rgbecker | 2004-01-23 00:04:40 +0000 (Fri, 23 Jan 2004) - M /reportlab/trunk/rl_addons/pyRXP/rxp/url.c - Attempt to fix UNC opening -r2257 | rgbecker | 2004-01-21 18:04:05 +0000 (Wed, 21 Jan 2004) - M /reportlab/trunk/utils/README -################################################################################# -#################### RELEASE 1.19 at 18:00 GMT 21/Jan/2004 ################# -################################################################################# -##### 2004/01/21 ##### - pdfbase/ttfonts.py 1.18 rgbecker Improve error handling & naming -##### 2004/01/14 ##### - rl_addons/renderPM/pfm.py 1.3 rgbecker Changes for 2.3 compatibility -##### 2004/01/13 ##### - pdfgen/canvas.py 1.118 rgbecker Allow for possible restarts - platypus/doctemplate.py 1.71 rgbecker Allow for simple loop detection -##### 2004/01/10 ##### - platypus/figures.py 1.16 rgbecker Added optional memory caching -##### 2004/01/09 ##### - platypus/tables.py 1.71 rgbecker Modified version of Henning von Bargen's LongTables optimisation -##### 2004/01/08 ##### - platypus/doctemplate.py 1.70 andy_robinson Upped empty page limit to 10 -##### 2004/01/07 ##### - platypus/flowables.py 1.44 andy_robinson Candidate fix for infinite looping - platypus/doctemplate.py 1.69 andy_robinson Candidate fix for infinite looping - lib/codecharts.py 1.8 andy_robinson Candidate fix for infinite looping - platypus/figures.py 1.15 rgbecker Fix up caption and split methods etc - platypus/tables.py 1.70 dragan1 merging Jython-branch - platypus/tables.py 1.69 rgbecker Make _listCellGeom more specific - platypus/__init__.py 1.16.2.2 dragan1 added conditional import linee for jython compatilibity - platypus/figures.py 1.14 rgbecker Improved FlexFigure scaling, in memory PageCatcherFigures -##### 2004/01/05 ##### - rl_addons/renderPM/_renderPM.c 1.31 rgbecker Fix bad long field value -##### 2003/12/23 ##### - lib/_rl_accel.c 1.37 rgbecker Added hex32 -##### 2003/12/19 ##### - platypus/tables.py 1.68 rgbecker Added splitfirst/last handling -##### 2003/12/18 ##### - tools/pythonpoint/styles/standard.py 1.3.4.1 dragan1 Platypus import structure changes - tools/pythonpoint/styles/htu.py 1.1.4.1 dragan1 Platypus import structure changes - tools/pythonpoint/pythonpoint.py 1.27.2.1 dragan1 Platypus import structure changes - tools/pythonpoint/demos/examples.py 1.3.4.1 dragan1 Platypus import structure changes - tools/docco/yaml2pdf.py 1.2.4.1 dragan1 Platypus import structure changes - tools/docco/rltemplate.py 1.3.4.1 dragan1 Platypus import structure changes - tools/docco/rl_doc_utils.py 1.6.4.1 dragan1 Platypus import structure changes - tools/docco/examples.py 1.4.4.1 dragan1 Platypus import structure changes - test/test_table_layout.py 1.1.4.1 dragan1 Platypus import structure changes - test/test_rl_accel.py 1.3.2.1 dragan1 Platypus import structure changes - test/test_platypus_xref.py 1.3.4.1 dragan1 Platypus import structure changes - test/test_platypus_tables.py 1.3.4.1 dragan1 Platypus import structure changes - test/test_platypus_paragraphs.py 1.11.4.1 dragan1 Platypus import structure changes - test/test_platypus_indents.py 1.1.4.1 dragan1 Platypus import structure changes - test/test_platypus_general.py 1.13.2.1 dragan1 Platypus import structure changes - test/test_platypus_breaking.py 1.6.4.1 dragan1 Platypus import structure changes - test/test_paragraphs.py 1.17.2.1 dragan1 Platypus import structure changes - test/test_graphics_speed.py 1.12.4.1 dragan1 Platypus import structure changes - test/test_graphics_charts.py 1.14.4.1 dragan1 Platypus import structure changes - platypus/tables.py 1.67.2.1 dragan1 Platypus import structure changes - platypus/paraparser.py 1.53.2.1 dragan1 Platypus import structure changes - platypus/figures.py 1.13.2.1 dragan1 Platypus import structure changes - platypus/__init__.py 1.16.2.1 dragan1 Platypus import structure changes - lib/tocindex.py 1.10.2.1 dragan1 Platypus import structure changes - lib/codecharts.py 1.7.4.1 dragan1 Platypus import structure changes - graphics/shapes.py 1.100.2.1 dragan1 Platypus import structure changes - graphics/renderPDF.py 1.24.2.1 dragan1 Platypus import structure changes - docs/userguide/ch7_custom.py 1.4.4.1 dragan1 Platypus import structure changes - docs/userguide/ch6_tables.py 1.6.4.1 dragan1 Platypus import structure changes - docs/userguide/ch4_platypus_concepts.py 1.4.4.1 dragan1 Platypus import structure changes - docs/userguide/ch2a_fonts.py 1.10.2.1 dragan1 Platypus import structure changes - demos/rlzope/rlzope.py 1.5.4.1 dragan1 Platypus import structure changes - demos/odyssey/fodyssey.py 1.17.4.1 dragan1 Platypus import structure changes - demos/gadflypaper/gfe.py 1.15.4.1 dragan1 Platypus import structure changes -##### 2003/12/17 ##### - graphics/testshapes.py 1.20 rgbecker Attempt to control ttf usage - graphics/renderPM.py 1.44 rgbecker Attempt to control ttf usage - platypus/paragraph.py 1.71 rgbecker Fix missing return -##### 2003/12/16 ##### - platypus/paragraph.py 1.70 rgbecker Underline alignment fix from Marc Stober -##### 2003/12/15 ##### - test/test_lib_sequencer.py 1.7 dragan1 changed method call from this to _this - java keyword - lib/sequencer.py 1.14 dragan1 renamed methid this to _this - java keyword -##### 2003/12/14 ##### - tools/pythonpoint/styles/modern.py 1.4 andy_robinson Permits style file to live in local directory - tools/pythonpoint/stdparser.py 1.20 andy_robinson Permits style file to live in local directory -##### 2003/12/12 ##### - lib/rparsexml.py 1.6 rgbecker Allow for both to have the fake entityReplacer thing - rl_addons/pyRXP/examples/benchmarks.py 1.2 rgbecker -->reportlab.lib.rparsexml -##### 2003/12/11 ##### - lib/rparsexml.py 1.5 rgbecker Add some support for slices etc -################################################################################# -#################### RELEASE 1.08 at 12:00 BST 19/June/2001 ##################### -################################################################################# -##### 2001/06/19 ##### - test/test_graphics_speed.py 1.10 dinu_gherman - Renamed chart attribute names to bars/lines/slices. - test/test_graphics_charts.py 1.12 dinu_gherman - Renamed chart attribute names to bars/lines/slices. - graphics/charts/piecharts.py 1.17 dinu_gherman - Renamed chart attribute names to bars/lines/slices. - graphics/charts/lineplots.py 1.17 dinu_gherman - Renamed chart attribute names to bars/lines/slices. - graphics/charts/linecharts.py 1.12 dinu_gherman - Renamed chart attribute names to bars/lines/slices. - graphics/charts/barcharts.py 1.18 dinu_gherman - Renamed chart attribute names to bars/lines/slices. -##### 2001/06/18 ##### - docs/graphguide/ch2_graphics.py 1.21 rgbecker - Changed from defaultStyles to pieStyles - graphics/widgetbase.py 1.25 rgbecker - Added recur arg to allow non-recurring - graphics/shapes.py 1.32 rgbecker - Added recur arg to allow non-recurring - test/test_graphics_speed.py 1.9 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. - test/test_graphics_charts.py 1.11 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. - graphics/charts/piecharts.py 1.16 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. - graphics/charts/lineplots.py 1.16 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. - graphics/charts/linecharts.py 1.11 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. - graphics/charts/barcharts.py 1.17 dinu_gherman - Renamed defaultStyles attribute with barStyles, lineStyles, pieStyles. -##### 2001/06/16 ##### - graphics/shapes.py 1.31 rgbecker - Added Null _DrawingEditorMixin class -##### 2001/06/15 ##### - pdfbase/pdfdoc.py 1.45 aaron_watters - added missing PDFText() function for consistency (needed by pagecatcher) -##### 2001/06/13 ##### - rl_addons/renderPM/setup.py 1.4 jvr - some changes to make it potentially work under MacOS with the CodeWarrior compiler. Unfortunately it still doesn't :-( - lib/setup.py 1.8 jvr - minor patch: building the sgmlop, _rl_accel and pyHnj now simply *works* with distutils under MacOS with codeWarrior. Good Stuff. (haven't added any install smartness yet) - graphics/renderPM.py 1.8 jvr - Test program: use os.path.join() for path manipulation instead of things like 'pmout%s%s'%(os.sep,filename) as this gets it wrong under MacOS. - rl_addons/renderPM/libart_lgpl/art_misc.c 1.2 jvr - extended #ifndef _WIN32 to also check for undefined-ness of 'macintosh' - rl_addons/renderPM/gt1/gt1-parset1.c 1.2 jvr - change to make it compile under MacOS: added strdup() hack for Mac - rl_addons/renderPM/_renderPM.c 1.3 jvr - changes to make it compile under MacOS: - added strdup() hack for Mac - changed size_t fields in gstateObject to int -- otherwise I'd have to add casts to each and every bpath_add_point() call (can't implicitly go from size_t to int) - repaired "illegal constant expression" in gstate like Robin suggested - lib/_rl_accel.c 1.18 rgbecker - Just's Mac patch - graphics/widgetbase.py 1.24 andy_robinson - Changes to support customer project: allow nulls in bar charts, and added some missing but obvious properties e.g. strokeColor for bar and legend borders. - graphics/charts/piecharts.py 1.15 andy_robinson - Changes to support customer project: allow nulls in bar charts, and added some missing but obvious properties e.g. strokeColor for bar and legend borders. - graphics/charts/legends.py 1.9 andy_robinson - Changes to support customer project: allow nulls in bar charts, and added some missing but obvious properties e.g. strokeColor for bar and legend borders. - graphics/charts/barcharts.py 1.16 andy_robinson - Changes to support customer project: allow nulls in bar charts, and added some missing but obvious properties e.g. strokeColor for bar and legend borders. - graphics/charts/axes.py 1.27 andy_robinson - Changes to support customer project: allow nulls in bar charts, and added some missing but obvious properties e.g. strokeColor for bar and legend borders. -##### 2001/06/11 ##### - pdfgen/canvas.py 1.80 rgbecker - Made canvas default to rl_config.pageCompression - rl_config.py 1.9 rgbecker - Added pageCompression - graphics/renderPDF.py 1.9 andy_robinson - Added autoSize option to rendderPDF, and allowed "no border" around bars in bar charts. To support QIR project. - graphics/charts/barcharts.py 1.15 andy_robinson - Added autoSize option to rendderPDF, and allowed "no border" around bars in bar charts. To support QIR project. -##### 2001/06/09 ##### - platypus/frames.py 1.13 rgbecker - added _FUZZ and changed frame add test -##### 2001/06/07 ##### - platypus/paragraph.py 1.55 rgbecker - Remove explicit £ character to stop Dinu moaning - test/test_pdfgen_pagemodes.py 1.5 rgbecker - Try to shorten filenames - test/test_widgetbase_tpc.py 1.1 rgbecker - Shortened names - test/test_graphics_widgetbase_tpc.py 1.2 rgbecker - Shortened names - test/test_graphics_charts_textlabels.py 1.4 rgbecker - Shortened names - test/test_charts_textlabels.py 1.1 rgbecker - Shortened names - lib/utils.py 1.16 rgbecker - Imported PIL_WARNINGS - graphics/widgetbase.py 1.23 rgbecker - Allow TypedPropertyCollection exemplar class to have getattr etc -##### 2001/06/06 ##### - graphics/widgetbase.py 1.22 rgbecker - Fbot's suggested fix - lib/setup.py 1.7 rgbecker - Fixed up MovePYDs - rl_addons/renderPM/setup.py 1.3 rgbecker - Fixed up MovePYDs - rl_addons/renderPM/_renderPM.c 1.2 rgbecker - Niki Spahiev's PyMem_New patch for 1.5.2 -##### 2001/05/30 ##### - test/test_pdfbase_pdfmetrics.py 1.7 rgbecker - Fixes for 2.1 whining - test/runAll.py 1.6 rgbecker - Fixes for 2.1 whining - rl_addons/renderPM/setup.py 1.2 rgbecker - Moved pyds to DLLs - lib/setup.py 1.6 rgbecker - Moved pyds to DLLs -##### 2001/05/29 ##### - docs/graphguide/ch2_graphics.py 1.20 johnprecedo - A number of small changes made - mainly changing import statements in the examples so that they actually work as given without producing an errors. Done up to page 15. - pdfgen/canvas.py 1.79 rgbecker - Fix the 2.1 _escape fix - rl_config.py 1.8 rgbecker - Changes for 2.1 escape fix - pdfgen/canvas.py 1.78 rgbecker - Changes for 2.1 escape fix - lib/_rl_accel.c 1.17 rgbecker - Changes for 2.1 escape fix - test/test_pyfiles.py 1.6 dinu_gherman - Added a test for first lines containing #!...python... -##### 2001/05/28 ##### - platypus/paragraph.py 1.54 rgbecker - Add some pound signs to test 5 -##### 2001/05/26 ##### - utils/daily.py 1.43 rgbecker - Fixed precedence - utils/daily.py 1.42 rgbecker - Fixed error print' daily.py -##### 2001/05/25 ##### - graphics/charts/legends.py 1.8 dinu_gherman - Added font attributes for text in legends. - lib/_rl_accel.c 1.16 rgbecker - Fix special case ',' - lib/_rl_accel.c 1.15 rgbecker - Added fix for comma decimal point - lib/utils.py 1.15 rgbecker - Attempt to fix the locale mismatch problem - lib/graphdocpy.py 1.8 rgbecker - removed use of renderGD - pdfgen/pathobject.py 1.9 rgbecker - Changed a few %f formats to use %s and fp_str - pdfgen/canvas.py 1.77 rgbecker - Changed a few %f formats to use %s and fp_str -##### 2001/05/23 ##### - graphics/charts/textlabels.py 1.7 rgbecker - Standardize nattribute names - graphics/renderPS.py 1.7 rgbecker - Synchronize backend to PDF standard - graphics/renderPM.py 1.7 rgbecker - Synchronize backend to PDF standard - graphics/renderPDF.py 1.8 rgbecker - Synchronize backend to PDF standard - graphics/widgetbase.py 1.21 rgbecker - Correct names and factorise some behaviour - graphics/shapes.py 1.30 rgbecker - Correct names and factorise some behaviour -##### 2001/05/22 ##### - graphics/charts/barcharts.py 1.14 dinu_gherman - Made strokeColor attribute for the bars be really used. - graphics/charts/textlabels.py 1.6 dinu_gherman - Added missing fillColor attribute for the string itself. - graphics/charts/piecharts.py 1.14 rgbecker - Fixes to piechart label handling - lib/__BUILD.dsw 1.4 rgbecker - Indicated usage of setup.py - lib/README.extensions 1.3 rgbecker - Indicated usage of setup.py -##### 2001/05/21 ##### - graphics/widgetbase.py 1.20 rgbecker - Fixed _ItemWrapper name and made it really cache - graphics/widgetbase.py 1.19 rgbecker - Improved element wrapping seems to work - test/test_graphics_widgetbase_tpc.py 1.1 dinu_gherman - Initial checkin. - graphics/widgetbase.py 1.18 rgbecker - Added element wrapper and fixed getitem so collections work better - test/test_docstrings.py 1.7 dinu_gherman - Substantial rewrite. -##### 2001/05/20 ##### - pdfbase/_fontdata.py 1.10 rgbecker - Fix typo - rl_config.py 1.7 rgbecker - Better Font handling for linux2 - pdfbase/_fontdata.py 1.9 rgbecker - Better Font handling for linux2 - rl_config.py 1.6 rgbecker - Font handling patches for linux2 from L Catucci - pdfbase/_fontdata.py 1.8 rgbecker - Font handling patches for linux2 from L Catucci - test/unittest.py 1.4 rgbecker - Fix linesep for non-JPython - rl_config.py 1.5 rgbecker - Added PIL_WARNINGS/ZLIB_WARNINGS - lib/utils.py 1.14 rgbecker - Added PIL_WARNINGS/ZLIB_WARNINGS -##### 2001/05/18 ##### - test/test_docstrings.py 1.6 dinu_gherman - Applied Robin's patch. - graphics/testshapes.py 1.12 rgbecker - Added locals & globals to the eval - graphics/charts/piecharts.py 1.13 rgbecker - Need some real strokeWidths - rl_addons/renderPM/pfm.py 1.2 rgbecker - Remove above 127 characters - test/runAll.py 1.5 dinu_gherman - Sorted order in which test module are executed (using sort()). - test/test_docstrings.py 1.5 dinu_gherman - Fixed bug where files got written everywhere by using new SecureTestCase. - test/utils.py 1.1 dinu_gherman - Initial checkin. - platypus/paragraph.py 1.53 rgbecker - added minWidth method to Flowable, Paragraph - platypus/flowables.py 1.19 rgbecker - added minWidth method to Flowable, Paragraph - graphics/widgetbase.py 1.17 rgbecker - Make setVector more friendly - graphics/shapes.py 1.29 rgbecker - Fixed expandUserNode method - graphics/widgetbase.py 1.16 rgbecker - Added TypedPropertyCollection.setVector -##### 2001/05/17 ##### - docs/graphguide/ch2_graphics.py 1.19 rgbecker - Pies don't seem to have a wedges collection any longer - graphics/shapes.py 1.28 rgbecker - Use canned routine validateSetattr - lib/validators.py 1.6 rgbecker - New AttrMap emplacement - lib/attrmap.py 1.2 rgbecker - New AttrMap emplacement - graphics/widgets/signsandsymbols.py 1.12 rgbecker - New AttrMap emplacement - graphics/widgets/flags.py 1.5 rgbecker - New AttrMap emplacement - graphics/charts/textlabels.py 1.5 rgbecker - New AttrMap emplacement - graphics/charts/piecharts.py 1.12 rgbecker - New AttrMap emplacement - graphics/charts/lineplots.py 1.15 rgbecker - New AttrMap emplacement - graphics/charts/linecharts.py 1.10 rgbecker - New AttrMap emplacement - graphics/charts/legends.py 1.7 rgbecker - New AttrMap emplacement - graphics/charts/barcharts.py 1.13 rgbecker - New AttrMap emplacement - graphics/charts/axes.py 1.26 rgbecker - New AttrMap emplacement - graphics/widgetbase.py 1.15 rgbecker - New AttrMap emplacement - graphics/shapes.py 1.27 rgbecker - New AttrMap emplacement - graphics/shapes.py 1.26 rgbecker - Removal of the Auto Thing - graphics/charts/linecharts.py 1.9 rgbecker - Removal of the Auto Thing - graphics/charts/barcharts.py 1.12 rgbecker - Removal of the Auto Thing - graphics/charts/axes.py 1.25 rgbecker - Removal of the Auto Thing - docs/graphguide/ch2_graphics.py 1.18 rgbecker - Removal of the Auto Thing - lib/attrmap.py 1.1 rgbecker - Initial version - lib/validators.py 1.5 rgbecker - New Scheme Validators - test/test_lib_validators.py 1.4 rgbecker - New Scheme Validators -##### 2001/05/16 ##### - graphics/renderPM.py 1.6 rgbecker - Added drawToString - test/test_graphics_charts.py 1.10 rgbecker - Renamed Legend0 to Legend, fixed _attrmap typo - graphics/charts/legends.py 1.6 rgbecker - Renamed Legend0 to Legend, fixed _attrmap typo -##### 2001/05/15 ##### - graphics/charts/lineplots.py 1.14 dinu_gherman - Added strokeDashArray attribute to LinePlotProperties class. - test/test_lib_validators.py 1.3 dinu_gherman - Removed isNumberOrAuto validator. - lib/validators.py 1.4 dinu_gherman - Removed isNumberOrAuto validator. - graphics/charts/axes.py 1.24 dinu_gherman - Removed isNumberOrAuto validator. - graphics/widgetbase.py 1.14 dinu_gherman - Adapted to use Validator instances in validators.py. - graphics/shapes.py 1.25 dinu_gherman - Adapted to use Validator instances in validators.py. - graphics/testshapes.py 1.11 rgbecker - Improvements to testshapse.py - graphics/renderPM.py 1.5 rgbecker - Improvements to testshapse.py - platypus/paragraph.py 1.52 rgbecker - Added find to imports - test/test_paragraphs.py 1.10 rgbecker - Added in line font changes -##### 2001/05/11 ##### - platypus/doctemplate.py 1.40 rgbecker - Dynamic page sizes - lib/graphdocpy.py 1.7 dinu_gherman - Fixed buglet. - graphics/widgets/signsandsymbols.py 1.11 dinu_gherman - Adapted to using vlaidator classes. - graphics/widgets/flags.py 1.4 dinu_gherman - Adapted to using vlaidator classes. - lib/graphdocpy.py 1.6 dinu_gherman - Added display of documented public widget attributes. - graphics/charts/textlabels.py 1.4 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/piecharts.py 1.11 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/lineplots.py 1.13 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/linecharts.py 1.8 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/legends.py 1.5 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/barcharts.py 1.11 dinu_gherman - Adapted to using validator classes in attribute maps. - graphics/charts/axes.py 1.23 dinu_gherman - Adapted to using validator classes in attribute maps. - test/test_lib_validators.py 1.2 dinu_gherman - Changed validator functions into classes. Adapted test cases. Added a tiny change to widgetbase.py. - lib/validators.py 1.3 dinu_gherman - Changed validator functions into classes. Adapted test cases. Added a tiny change to widgetbase.py. - graphics/widgetbase.py 1.13 dinu_gherman - Changed validator functions into classes. Adapted test cases. Added a tiny change to widgetbase.py. - test/test_coordtracking.py 1.2 rgbecker - Removed for for later correction -##### 2001/05/10 ##### - test/test_coordtracking.py 1.1 aaron_watters - test file for coordinate tracking and links - pdfgen/canvas.py 1.76 aaron_watters - canvas coordinate matrix tracking and related linkage support - graphics/widgetbase.py 1.12 dinu_gherman - Switched to using lib.validators module. - graphics/widgets/signsandsymbols.py 1.10 dinu_gherman - Switched to using lib.validators module. - graphics/widgets/flags.py 1.3 dinu_gherman - Switched to using lib.validators module. -##### 2001/05/09 ##### - lib/setup.py 1.5 rgbecker - Compatibility fixes - lib/_rl_accel.c 1.14 rgbecker - Compatibility fixes - graphics/widgetbase.py 1.11 dinu_gherman - Code and docstring enhancements. - test/test_graphics_speed.py 1.8 dinu_gherman - Switched to using defaultStyles attribute. - test/test_graphics_charts.py 1.9 dinu_gherman - Switched to using defaultStyles attribute. - graphics/charts/axes.py 1.22 dinu_gherman - Added visibleAxis/visibleTicks attributes. - graphics/charts/piecharts.py 1.10 dinu_gherman - Broken draw method apart (well...). - graphics/charts/piecharts.py 1.9 dinu_gherman - Done some slight code enhancements. - graphics/charts/lineplots.py 1.12 dinu_gherman - Switched to using PropHolder superclass for LinePlotProperties. Added strokeWidth attribute to LinePlotProperties. - test/test_lib_validators.py 1.1 dinu_gherman - Initial checkin. -##### 2001/05/08 ##### - graphics/charts/barcharts.py 1.10 dinu_gherman - Switched to using defaultStyles attribute. - graphics/charts/linecharts.py 1.7 dinu_gherman - Switched to using typed collection styles. - graphics/charts/piecharts.py 1.8 dinu_gherman - Made more consistent use of typed collections. - graphics/charts/lineplots.py 1.11 dinu_gherman - Removed dead code. Removed references to usedSymbol attribute. Made defaultStyles attribute a typed collection. - graphics/widgetbase.py 1.10 dinu_gherman - Added a __len__ method to TypedPropertyCOllection. - graphics/charts/lineplots.py 1.10 dinu_gherman - Removed references to defaultColors. - lib/graphdocpy.py 1.5 dinu_gherman - Opening now showing PDF outline (with closed function sections). Minor fixes. -##### 2001/05/07 ##### - graphics/renderPDF.py 1.7 aaron_watters - get rid of automatic boundary box option - graphics/charts/axes.py 1.21 dinu_gherman - Reduced number of arguments for joinToAxis() methods. - lib/validators.py 1.2 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/shapes.py 1.24 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/textlabels.py 1.3 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/piecharts.py 1.7 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/lineplots.py 1.9 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/linecharts.py 1.6 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/legends.py 1.4 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/barcharts.py 1.9 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - graphics/charts/axes.py 1.20 dinu_gherman - Extracted validator functions into lib.validators. Modified import statements. - lib/validators.py 1.1 dinu_gherman - Added future validators module (stripped off from shapes.py). - graphics/charts/piecharts.py 1.6 dinu_gherman - Various minor changes. - graphics/charts/lineplots.py 1.8 dinu_gherman - Various minor changes. - graphics/charts/linecharts.py 1.5 dinu_gherman - Various minor changes. - graphics/charts/legends.py 1.3 dinu_gherman - Various minor changes. - graphics/charts/barcharts.py 1.8 dinu_gherman - Various minor changes. - graphics/charts/axes.py 1.19 dinu_gherman - Various minor changes. -##### 2001/05/05 ##### - platypus/tables.py 1.39 aaron_watters - hacky fix to nudge bug - pdfgen/canvas.py 1.75 aaron_watters - fix for major font metrics state tracking bug -##### 2001/05/04 ##### - platypus/tables.py 1.38 aaron_watters - use round caps for miter limit - graphics/renderPM.py 1.4 rgbecker - Readded import checks and improved messages - graphics/renderPM.py 1.3 rgbecker - Added fix for png files -##### 2001/05/03 ##### - graphics/renderPM.py 1.2 rgbecker - ImportError checks for _renderPM - graphics/renderPM.py 1.1 rgbecker - Moved from rlextra/graphics/Csrc/renderPM -################################################################################# -#################### RELEASE 1.07 at 11:54 BST ################################## -################################################################################# -##### 2001/05/02 ##### - lib/units.py 1.3 rgbecker - Added toLength function -##### 2001/05/01 ##### - lib/colors.py 1.18 johnprecedo - Added special color - ReportLabBlue. - graphics/charts/axes.py 1.18 rgbecker - makeLabels/Ticks changed - graphics/testshapes.py 1.10 rgbecker - strokeDashArray fixes - graphics/shapes.py 1.23 rgbecker - strokeDashArray fixes - graphics/charts/axes.py 1.17 rgbecker - strokeDashArray fixes -##### 2001/04/30 ##### - pdfbase/pdfmetrics.py 1.37 rgbecker - Added some imports for compatibility/efficiency -##### 2001/04/28 ##### - graphics/widgets/signsandsymbols.py 1.9 rgbecker - try and ensure float division -##### 2001/04/26 ##### - graphics/charts/lineplots.py 1.7 dinu_gherman - Changed defaultColors to defaultStyles. - pdfgen/pdfimages.py 1.12 rgbecker - Fixed typo zlib (not lib) - lib/randomtext.py 1.7 johnprecedo - Minor changes. Now allows you to specify the maximum number of sentences for the randomtext as an argument. Also added an example usage to the docstring. -##### 2001/04/25 ##### - license.txt 1.2 andy_robinson - Trivial change to test CVS write access - graphics/charts/piecharts.py 1.5 dinu_gherman - Added more samples. - graphics/charts/piecharts.py 1.4 johnprecedo - Added a new example. - graphics/charts/piecharts.py 1.3 dinu_gherman - Removed sector line for single sliced pie charts. -##### 2001/04/24 ##### - graphics/charts/axes.py 1.16 dinu_gherman - Made joining of axes possible via axes attributes. -##### 2001/04/23 ##### - pdfbase/pdfmetrics.py 1.36 rgbecker - Added findT1File to base Face, and _fontdata - pdfbase/_fontdata.py 1.7 rgbecker - Added findT1File to base Face, and _fontdata - lib/fonts.py 1.7 rgbecker - Moved T1 search stuff into pdfbase.fontdata - pdfbase/pdfmetrics.py 1.35 rgbecker - Remove debug prints -##### 2001/04/20 ##### - test/test_pdfbase_fontembed.py 1.2 rgbecker - Merged pdfgen.fonts into pdfmetrics again - test/test_pdfbase_encodings.py 1.2 rgbecker - Merged pdfgen.fonts into pdfmetrics again - pdfgen/fonts.py 1.2 rgbecker - Merged pdfgen.fonts into pdfmetrics again - pdfbase/pdfmetrics.py 1.34 rgbecker - Merged pdfgen.fonts into pdfmetrics again - pdfbase/_fontdata.py 1.6 rgbecker - Merged pdfgen.fonts into pdfmetrics again - lib/_rl_accel.c 1.13 rgbecker - Merged pdfgen.fonts into pdfmetrics again - utils/daily.py 1.41 rgbecker - Fix graphguide generation - utils/daily.py 1.40 rgbecker - Add graphguide generation - pdfgen/canvas.py 1.74 rgbecker - pageCompression fix from Mark Charlebois (was undefined if zlib not present); removed stray print statements on font registration. - pdfbase/pdfmetrics.py 1.33 rgbecker - pageCompression fix from Mark Charlebois (was undefined if zlib not present); removed stray print statements on font registration. -##### 2001/04/18 ##### - test/test_pdfbase_fontembed.py 1.1 rgbecker - New font embedding mechanism - test/test_pdfbase_encodings.py 1.1 rgbecker - New font embedding mechanism - pdfgen/fonts.py 1.1 rgbecker - New font embedding mechanism - test/test_pdfgen_general.py 1.4 rgbecker - New font embedding stuff. - test/test_pdfbase_pdfmetrics.py 1.6 rgbecker - New font embedding stuff. - pdfgen/canvas.py 1.73 rgbecker - New font embedding stuff. - pdfbase/pdfmetrics.py 1.32 rgbecker - New font embedding stuff. - pdfbase/pdfdoc.py 1.44 rgbecker - New font embedding stuff. - README 1.5 rgbecker - Testing write access to CVS, only whitespace changed - testfile.txt 1.2 johnprecedo - removed test file - testfile.txt 1.1 johnprecedo - nitial checkin to test CVS write access -##### 2001/04/17 ##### - rl_config.py 1.4 rgbecker - Fix typo in T1SearchPath name - pdfbase/afm2w.py 1.2 rgbecker - Removed file added for test - pdfbase/afm2w.py 1.1 rgbecker - Adding afm2w as a test - docs/userguide/testfile.txt 1.1 andy_robinson - Testing checkin rights - docs/userguide/ch9_future.py 1.3 andy_robinson - Testing SF commit ability -##### 2001/04/16 ##### - __init__.py 1.13 rgbecker - Changed copyright dates - README 1.4 andy_robinson - Whitespace change to test CVS write access -##### 2001/04/13 ##### - test/test_pdfgen_general.py 1.3 rgbecker - Added sausages - test/test_pdfgen_general.py 1.2 andy_robinson - filename case change for an embedded GIF - test/test_platypus_general.py 1.2 andy_robinson - General pdfgen test moved to here - test/test_pdfgen_general.py 1.1 andy_robinson - General pdfgen test moved to here - test/test_platypus_tables.py 1.1 andy_robinson - Added tests formerly under platypus/test - test/test_platypus_general.py 1.1 andy_robinson - Added tests formerly under platypus/test - test/pythonpowered.gif 1.1 andy_robinson - Added tests formerly under platypus/test -##### 2001/04/12 ##### - graphics/charts/barcharts.py 1.7 rgbecker - Moved functionality into the base Barchart class - graphics/renderPDF.py 1.6 rgbecker - Added showBoundary arg -##### 2001/04/11 ##### - graphics/charts/axes.py 1.15 rgbecker - Move common value axis bits into base class - graphics/charts/axes.py 1.14 rgbecker - Try and make min/max finding as complicated as possible - graphics/charts/axes.py 1.13 rgbecker - Moved some common stuff into ValueAxis, added maximumTicks - graphics/shapes.py 1.22 rgbecker - Improved comment somewhat - graphics/charts/axes.py 1.12 rgbecker - Added _findMax/Min funcs - graphics/charts/axes.py 1.11 rgbecker - Added _calcValueStep and _rangeAdjust methods - graphics/widgetbase.py 1.9 rgbecker - Added StyleProeprties class -##### 2001/04/10 ##### - graphics/widgetbase.py 1.8 andy_robinson - Robin's refactoring to separate propholder and Widget - demos/pythonpoint/pythonpoint.py 1.32 andy_robinson - Removing pingo dependency - graphics/charts/markers.py 1.2 dinu_gherman - Added two funcions and tidied-up. - lib/normalDate.py 1.1 dinu_gherman - Initial checkin. - lib/pagesizes.py 1.7 andy_robinson - Fixed size of legal -##### 2001/04/09 ##### - graphics/charts/lineplots.py 1.6 dinu_gherman - Fixed import buglet. - test/test_graphics_charts.py 1.8 dinu_gherman - Adapted to new LineChart class names. - graphics/charts/markers.py 1.1 dinu_gherman - Initial checkin. - graphics/charts/lineplots.py 1.5 dinu_gherman - Splitted draw() method. Removed marker functions. Removed usage of XTimeSeriesAxis. Lots of tiny changes. - graphics/charts/linecharts.py 1.4 dinu_gherman - Aplitted draw method. Moved marker functions into markers.py. Many tiny modifications. - graphics/charts/barcharts.py 1.6 dinu_gherman - Minimal doc string changes. - graphics/charts/axes.py 1.10 dinu_gherman - Added attribute valueSteps to X- and YValueAxis. Commented XTimeSeriesAxes. - graphics/charts/barcharts.py 1.5 dinu_gherman - Added abstract BarChart base class. Applied various code simplifications. Removed 'import *'. - graphics/charts/barcharts.py 1.4 dinu_gherman - Tiny changes (to verify CVS works). - graphics/charts/barcharts.py 1.3 dinu_gherman - Split draw() methods into makeBars() and makeBackground(). Fixed one possible bug (reverted order of configure and setPosition()). - graphics/charts/utils.py 1.2 dinu_gherman - Added Robin's stuff from rgb_ticks.py. - graphics/charts/axes.py 1.9 dinu_gherman - Factorized two more methods into ValueAxis. - graphics/charts/axes.py 1.8 dinu_gherman - Factorized some more methods into abstract base classes. - graphics/charts/axes.py 1.7 dinu_gherman - Added new abstract base classes CategoryAxis and ValueAxis. Factorized some methods in X/Y axes and moved to these base classes. Split draw methods into three new methods. - graphics/charts/lineplots.py 1.4 dinu_gherman - Removed commented code. - graphics/charts/utils.py 1.1 dinu_gherman - Moved utility functions into new module utils.py. - graphics/charts/lineplots.py 1.3 dinu_gherman - Moved utility functions into new module utils.py. - graphics/charts/axes.py 1.6 dinu_gherman - Moved utility functions into new module utils.py. - graphics/charts/axes.py 1.5 dinu_gherman - Removed 'import *'. -##### 2001/04/06 ##### - lib/graphdocpy.py 1.4 andy_robinson - Now adds the current directory to the path; makes it easier to run it over other modules. - graphics/charts/axes.py 1.4 andy_robinson - Changes received from Dinu - required for demo, not sure what's in it (Andy) - graphics/shapes.py 1.21 rgbecker - Fix indent - graphics/shapes.py 1.20 rgbecker - _updater was silly - lib/utils.py 1.13 rgbecker - _updater was silly - lib/utils.py 1.12 rgbecker - Added dict _updater func - graphics/shapes.py 1.19 rgbecker - Group/Drawing slightly cleaner python -##### 2001/04/05 ##### - graphics/shapes.py 1.18 rgbecker - Added Group insert method - graphics/shapes.py 1.17 rgbecker - Added stringWidth import - utils/cvs_status.py 1.2 rgbecker - License text changes - test/test_pyfiles.py 1.5 rgbecker - License text changes - test/test_platypus_toc.py 1.6 rgbecker - License text changes - test/test_platypus_paragraphs.py 1.6 rgbecker - License text changes - test/test_pdfgen_pagemodes.py 1.4 rgbecker - License text changes - test/test_pdfbase_pdfutils.py 1.3 rgbecker - License text changes - test/test_pdfbase_pdfmetrics.py 1.5 rgbecker - License text changes - test/test_paragraphs.py 1.9 rgbecker - License text changes - test/test_lib_sequencer.py 1.4 rgbecker - License text changes - test/test_lib_colors.py 1.6 rgbecker - License text changes - test/test_graphics_speed.py 1.7 rgbecker - License text changes - test/test_graphics_charts_textlabels.py 1.3 rgbecker - License text changes - test/test_graphics_charts.py 1.7 rgbecker - License text changes - test/test_docstrings.py 1.4 rgbecker - License text changes - test/runAll.py 1.4 rgbecker - License text changes - test/__init__.py 1.2 rgbecker - License text changes - platypus/tableofcontents.py 1.2 rgbecker - License text changes - pdfbase/pdfmetrics.py 1.31 rgbecker - License text changes - pdfbase/_fontdata.py 1.5 rgbecker - License text changes - lib/yaml.py 1.2 rgbecker - License text changes - lib/setup.py 1.4 rgbecker - License text changes - lib/graphdocpy.py 1.3 rgbecker - License text changes - rl_config.py 1.3 rgbecker - License text changes - lib/docpy.py 1.4 rgbecker - License text changes - graphics/widgets/signsandsymbols.py 1.8 rgbecker - License text changes - graphics/widgets/flags.py 1.2 rgbecker - License text changes - graphics/widgets/__init__.py 1.2 rgbecker - License text changes - graphics/widgetbase.py 1.7 rgbecker - License text changes - graphics/testshapes.py 1.9 rgbecker - License text changes - graphics/testdrawings.py 1.2 rgbecker - License text changes - graphics/renderbase.py 1.9 rgbecker - License text changes - graphics/renderPS.py 1.6 rgbecker - License text changes - graphics/renderPDF.py 1.5 rgbecker - License text changes - graphics/charts/textlabels.py 1.2 rgbecker - License text changes - graphics/charts/piecharts.py 1.2 rgbecker - License text changes - graphics/charts/lineplots.py 1.2 rgbecker - License text changes - graphics/charts/linecharts.py 1.3 rgbecker - License text changes - graphics/charts/legends.py 1.2 rgbecker - License text changes - graphics/charts/barcharts.py 1.2 rgbecker - License text changes - graphics/charts/axes.py 1.3 rgbecker - License text changes - graphics/charts/__init__.py 1.2 rgbecker - License text changes - graphics/__init__.py 1.2 rgbecker - License text changes - docs/userguide/ch9_future.py 1.2 rgbecker - License text changes - docs/graphguide/t_parse.py 1.2 rgbecker - License text changes - docs/graphguide/platdemos.py 1.2 rgbecker - License text changes - docs/graphguide/gengraphguide.py 1.2 rgbecker - License text changes - docs/graphguide/examples.py 1.2 rgbecker - License text changes - docs/graphguide/ch2_graphics.py 1.17 rgbecker - License text changes - docs/graphguide/ch1_intro.py 1.7 rgbecker - License text changes - graphics/shapes.py 1.16 rgbecker - Fixed indentation - graphics/shapes.py 1.15 rgbecker - Added missing comment - graphics/shapes.py 1.14 rgbecker - Made Drawings inherit from Group - utils/copyr.txt 1.2 rgbecker - Changed copyright period - utils/copyrite.py 1.2 rgbecker - Removed spurious assignnments -##### 2001/04/04 ##### - lib/logger.py 1.3 rgbecker - Added enable attribute to the WarnOnce class - graphics/shapes.py 1.13 rgbecker - Added _textBoxLimits -##### 2001/04/03 ##### - docs/graphguide/ch2_graphics.py 1.16 johnprecedo - Spellchecked - fixed 15 typos. - docs/graphguide/ch2_graphics.py 1.15 johnprecedo - Minor changes - resolved a conflict. - docs/graphguide/ch2_graphics.py 1.14 johnprecedo - Added another sample Pie. -##### 2001/04/02 ##### - docs/graphguide/ch1_intro.py 1.6 johnprecedo - Corrected a couple of typoes. - docs/graphguide/ch2_graphics.py 1.13 johnprecedo - Corrected a couple of typoes. -################################################################################# -#################### RELEASE 1.06 at 14:00 BST ################################## -################################################################################# -##### 2001/03/30 ##### - docs/graphguide/ch2_graphics.py 1.11 dinu_gherman - Minor changes. - docs/graphguide/ch2_graphics.py 1.10 dinu_gherman - Updated text. - docs/graphguide/ch2_graphics.py 1.9 dinu_gherman - Added two bar charts. - docs/graphguide/ch2_graphics.py 1.8 dinu_gherman - Added samples for signsandsymbols. - docs/graphguide/ch2_graphics.py 1.7 dinu_gherman - Added minor changes. - docs/graphguide/ch1_intro.py 1.5 dinu_gherman - Added minor changes. - docs/graphguide/ch1_intro.py 1.4 dinu_gherman - Retrofitted a few changes to the content. - docs/userguide/genuserguide.py 1.46 dinu_gherman - Removed two commented imports (to see if CVS works). - utils/daily.py 1.39 rgbecker - Changed to account for graphdocpy and dos/graphguide - docs/userguide/genuserguide.py 1.45 rgbecker - Cross-refrenced to graphics guide, removed spurious ch8 - docs/userguide/ch8_graphics.py 1.15 rgbecker - Cross-refrenced to graphics guide, removed spurious ch8 - docs/userguide/ch2_graphics.py 1.12 rgbecker - Cross-refrenced to graphics guide, removed spurious ch8 - docs/userguide/ch1_intro.py 1.13 rgbecker - Cross-refrenced to graphics guide, removed spurious ch8 - README 1.3 rgbecker - Touch Test -##### 2001/03/29 ##### - docs/userguide/ch8_graphics.py 1.14 dinu_gherman - Added further minor corrections. - docs/graphguide/ch2_graphics.py 1.6 dinu_gherman - Further changes. - docs/graphguide/ch2_graphics.py 1.5 dinu_gherman - Added a table. - docs/graphguide/ch2_graphics.py 1.4 dinu_gherman - Further changes. - docs/userguide/ch8_graphics.py 1.13 dinu_gherman - Shortened even further. - docs/graphguide/ch2_graphics.py 1.3 dinu_gherman - Minor changes. - docs/graphguide/ch1_intro.py 1.3 dinu_gherman - Minor changes. - docs/graphguide/ch2_graphics.py 1.2 dinu_gherman - Changed granularity of chapters. - docs/graphguide/ch1_intro.py 1.2 dinu_gherman - Changed granularity of chapters. - docs/userguide/genuserguide.py 1.44 dinu_gherman - Removed reference to original graphics chapter. - docs/userguide/ch8_graphics_long.py 1.2 dinu_gherman - Removed graphics chapter into graphics guide. - docs/graphguide/t_parse.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. - docs/graphguide/platdemos.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. - docs/graphguide/gengraphguide.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. - docs/graphguide/examples.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. - docs/graphguide/ch2_graphics.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. - docs/graphguide/ch1_intro.py 1.1 dinu_gherman - Initial checkin, mostly copied from user guide. -##### 2001/03/28 ##### - docs/userguide/ch8_graphics.py 1.12 dinu_gherman - Further changes. - docs/userguide/ch8_graphics.py 1.11 dinu_gherman - Shortened from previous version. - docs/userguide/genuserguide.py 1.43 dinu_gherman - Added import of short graphics chapter. - docs/userguide/ch8_graphics_long.py 1.1 dinu_gherman - Copyied graphics chapter file (to be extracted into a seperate document). - docs/userguide/ch8_graphics.py 1.10 johnprecedo - More images. - docs/userguide/ch8_graphics.py 1.9 johnprecedo - Added more illustrations. - graphics/charts/linecharts.py 1.2 dinu_gherman - Added to docstrings and tidied-up a bit. -##### 2001/03/27 ##### - docs/userguide/ch8_graphics.py 1.8 andy_robinson - Drawings and tables bug fixed. - docs/tools/rltemplate.py 1.11 andy_robinson - Drawings and tables bug fixed. - docs/userguide/ch8_graphics.py 1.7 johnprecedo - More markup. Added todo comments for missing images - docs/userguide/genuserguide.py 1.42 johnprecedo - Added Andy's changes to include illustrations (from code snippets). - docs/userguide/ch8_graphics.py 1.6 johnprecedo - Added Andy's changes to include illustrations (from code snippets). - docs/userguide/ch8_graphics.py 1.5 johnprecedo - More markup changes. Code snippets changed so that they match with current code. - docs/userguide/ch8_graphics.py 1.4 johnprecedo - Lots of small markup changes. Removed reference to paths - they aren't implemented yet in reportlab\graphics. changed references to 'widgets.py' to 'widgetbase.py'. - docs/userguide/genuserguide.py 1.41 johnprecedo - Moved chapter 8 to chapter 9 - new chapter 8 inserted for reportlab\graphics. -##### 2001/03/26 ##### - docs/userguide/ch8_graphics.py 1.3 johnprecedo - Finished markup of reportlab\graphics tutorial. Only one table inserted so far. Any markup other that split into headings/paragraphs/code examples is also missing. - docs/userguide/ch8_graphics.py 1.2 johnprecedo - First partial checkin on new chapter on "Platform Independent Graphics". Little markup done. Later part is still commented out (awaiting markup). - utils/runtests.py 1.16 rgbecker - Improved pattern searches and added better inhibiting - test/unittest.py 1.3 rgbecker - Added utils\runtests.py inhibitor comment - test/test_pyfiles.py 1.4 rgbecker - Added utils\runtests.py inhibitor comment - test/test_platypus_toc.py 1.5 rgbecker - Added utils\runtests.py inhibitor comment - test/test_platypus_paragraphs.py 1.5 rgbecker - Added utils\runtests.py inhibitor comment - test/test_pdfgen_pagemodes.py 1.3 rgbecker - Added utils\runtests.py inhibitor comment - test/test_pdfbase_pdfutils.py 1.2 rgbecker - Added utils\runtests.py inhibitor comment - test/test_pdfbase_pdfmetrics.py 1.4 rgbecker - Added utils\runtests.py inhibitor comment - test/test_paragraphs.py 1.8 rgbecker - Added utils\runtests.py inhibitor comment - test/test_lib_sequencer.py 1.3 rgbecker - Added utils\runtests.py inhibitor comment - test/test_lib_colors.py 1.5 rgbecker - Added utils\runtests.py inhibitor comment - test/test_graphics_speed.py 1.6 rgbecker - Added utils\runtests.py inhibitor comment - test/test_graphics_charts_textlabels.py 1.2 rgbecker - Added utils\runtests.py inhibitor comment - test/test_graphics_charts.py 1.6 rgbecker - Added utils\runtests.py inhibitor comment - test/test_docstrings.py 1.3 rgbecker - Added utils\runtests.py inhibitor comment - test/runAll.py 1.3 rgbecker - Added utils\runtests.py inhibitor comment - docs/userguide/ch9_future.py 1.1 johnprecedo - Existing chapter 8 renamed to chapter 9. Stub for new chapter 8 on graphics added. - docs/userguide/ch8_graphics.py 1.1 johnprecedo - Existing chapter 8 renamed to chapter 9. Stub for new chapter 8 on graphics added. - docs/userguide/ch8_future.py 1.4 johnprecedo - Existing chapter 8 renamed to chapter 9. Stub for new chapter 8 on graphics added. - lib/utils.py 1.11 rgbecker - Changed to prefer PIL as package - platypus/flowables.py 1.18 rgbecker - Changed to PIL_Image - pdfgen/test/testpdfgen.py 1.17 rgbecker - Changed to PIL_Image - pdfgen/pdfimages.py 1.11 rgbecker - Changed to PIL_Image - pdfgen/canvas.py 1.72 rgbecker - Changed to PIL_Image - pdfbase/pdfutils.py 1.21 rgbecker - Changed to PIL_Image - lib/utils.py 1.10 rgbecker - Changed to PIL_Image - graphics/renderPS.py 1.5 rgbecker - Changed to PIL_Image -##### 2001/03/25 ##### - graphics/shapes.py 1.12 dinu_gherman - Improved Auto class. Changed formatting slightly. - test/test_graphics_charts.py 1.5 dinu_gherman - Fixed a buglet with graphdocpy.py running over this file. - lib/docpy.py 1.3 dinu_gherman - Fixed argument bug for UmlPdfDocumentBuilder.begin(). Added bullets to 'imported modules' section. Added cosmetic changes to MyTemplate.afterFlowable(). -##### 2001/03/23 ##### - test/test_pyfiles.py 1.3 dinu_gherman - Changed trailing digit test to really fail. - graphics/widgets/flags0.py 1.5 dinu_gherman - Replaced trailing digit filename. - graphics/widgets/flags.py 1.1 dinu_gherman - Replaced trailing digit filename. - platypus/tableofcontents0.py 1.3 dinu_gherman - Replaced trailing digit file. - platypus/tableofcontents.py 1.1 dinu_gherman - Replaced trailing digit file. - test/test_platypus_toc.py 1.4 dinu_gherman - Fixed imports for non-trailing digit modules. - test/test_platypus_paragraphs.py 1.4 dinu_gherman - Fixed imports for non-trailing digit modules. - test/test_pyfiles.py 1.2 dinu_gherman - Added test for trailing digits in filenames. - lib/graphdocpy.py 1.2 dinu_gherman - Fixed doc strings and imports. - lib/docpy.py 1.2 dinu_gherman - Fixed doc strings and imports. - lib/graphdocpy.py 1.1 dinu_gherman - Initial checkin of non-trailing digit files replacing previous ones. - lib/docpy.py 1.1 dinu_gherman - Initial checkin of non-trailing digit files replacing previous ones. - lib/graphicsdoc0.py 1.12 dinu_gherman - Removed trailing digit file to be replaced with others. - lib/docpy1.py 1.2 dinu_gherman - Removed trailing digit files to be replaced with others. - lib/docpy0.py 1.8 dinu_gherman - Removed trailing digit files to be replaced with others. - graphics/shapes.py 1.11 dinu_gherman - Added isNumberOrAuto() function. - graphics/charts/axes.py 1.2 dinu_gherman - Added missing attribute maps. -##### 2001/03/22 ##### - lib/_rl_accel.c 1.12 rgbecker - Fixed up a better lookup for _AttrDict - platypus/paraparser.py 1.41 andy_robinson - Accepts seqdefault/seqDefault and seqreset/seqReset - lib/_rl_accel.c 1.11 rgbecker - Renamed AttrDict to _AttrDict -##### 2001/03/21 ##### - pdfbase/pdfdoc.py 1.43 aaron_watters - fixed minor bug in forms handling - lib/_rl_accel.c 1.10 rgbecker - AttrDict improvements not yet primetime - lib/setup.py 1.3 rgbecker - avoid import errors for Dinu, no runtests for Robin - lib/_rl_accel.c 1.9 rgbecker - avoid import errors for Dinu, no runtests for Robin - test/test_graphics_charts_textlabel0.py 1.4 dinu_gherman - Removed trailing digit filename to be replaced. - test/test_graphics_speed.py 1.5 dinu_gherman - Replaced one file with a non-trailing digit filename. Modified files using previous trailing digit modules. - test/test_graphics_charts_textlabels.py 1.1 dinu_gherman - Replaced one file with a non-trailing digit filename. Modified files using previous trailing digit modules. - test/test_graphics_charts.py 1.4 dinu_gherman - Replaced one file with a non-trailing digit filename. Modified files using previous trailing digit modules. - graphics/charts/textlabels.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/piecharts.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/lineplots.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/linecharts.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/legends.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/barcharts.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/axes.py 1.1 dinu_gherman - Initial checkin, replacing previous trailing digit filenames. - graphics/charts/textlabel0.py 1.7 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/piechart0.py 1.6 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/lineplot.py 1.2 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/linechart0.py 1.3 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/legends0.py 1.9 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/barchart1.py 1.19 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - graphics/charts/axes0.py 1.18 dinu_gherman - Removed file names to be replaced with non-trailing digit ones. - rl_config.py 1.2 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas - platypus/tables.py 1.37 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas - pdfgen/pdfimages.py 1.10 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas - pdfgen/canvas.py 1.71 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas - docs/userguide/ch7_custom.py 1.7 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas - docs/userguide/ch6_tables.py 1.17 rgbecker - Made imageCaching a positive quantity and proeprty of the canvas -##### 2001/03/20 ##### - graphics/charts/lineplot.py 1.1 dinu_gherman - Initial checkin. Added new experimental time series x value axis. - graphics/charts/axes0.py 1.17 dinu_gherman - Initial checkin. Added new experimental time series x value axis. - graphics/charts/legends0.py 1.8 dinu_gherman - Fixed import buglet. - graphics/charts/axes0.py 1.16 dinu_gherman - Fixed buglet when used with bar charts. - graphics/charts/barchart1.py 1.18 dinu_gherman - Added horizontal bar charts plus samples. -##### 2001/03/19 ##### - platypus/tables.py 1.36 aaron_watters - changed handling of table element styles for better space/time. old code left commented - test/test_paragraphs.py 1.7 andy_robinson - Proving you get leading whitespace in print if you have leading whitespace inside the para tag. -##### 2001/03/17 ##### - lib/styles.py 1.14 rgbecker - Removed spurious CellStyle - pdfbase/pdfmetrics.py 1.29 rgbecker - Fixed up self test -##### 2001/03/16 ##### - graphics/charts/barchart1.py 1.17 dinu_gherman - Fixed bug for bar charts using attribute 'useAbsolute'. - pdfbase/pdfmetrics.py 1.28 rgbecker - Fix bad name error(s) - pdfbase/_fontdata.py 1.4 rgbecker - Fix bad name error(s) - test/test_paragraphs.py 1.6 rgbecker - Renamed config.py to rl_config.py - test/test_graphics_speed.py 1.4 rgbecker - Renamed config.py to rl_config.py - platypus/test/testplatypus.py 1.23 rgbecker - Renamed config.py to rl_config.py - platypus/flowables.py 1.17 rgbecker - Renamed config.py to rl_config.py - platypus/doctemplate.py 1.39 rgbecker - Renamed config.py to rl_config.py - pdfgen/canvas.py 1.70 rgbecker - Renamed config.py to rl_config.py - pdfbase/pdfdoc.py 1.42 rgbecker - Renamed config.py to rl_config.py - lib/tocindex.py 1.7 rgbecker - Renamed config.py to rl_config.py - lib/fonts.py 1.6 rgbecker - Renamed config.py to rl_config.py - graphics/widgetbase.py 1.6 rgbecker - Renamed config.py to rl_config.py - graphics/shapes.py 1.10 rgbecker - Renamed config.py to rl_config.py - docs/userguide/genuserguide.py 1.40 rgbecker - Renamed config.py to rl_config.py - docs/userguide/examples.py 1.18 rgbecker - Renamed config.py to rl_config.py - docs/userguide/ch2_graphics.py 1.11 rgbecker - Renamed config.py to rl_config.py - docs/tools/rltemplate.py 1.10 rgbecker - Renamed config.py to rl_config.py - demos/pythonpoint/pythonpoint.py 1.31 rgbecker - Renamed config.py to rl_config.py - demos/gadflypaper/gfe.py 1.14 rgbecker - Renamed config.py to rl_config.py - utils/simpledoc.py 1.6 rgbecker - Fixed typo added point 8 (upload to SF) - utils/README 1.7 rgbecker - Fixed typo added point 8 (upload to SF) - pdfgen/pdfimages.py 1.9 rgbecker - fixed noImageCaching - pdfbase/_fontdata.py 1.3 rgbecker - Changed in line with _rl_accel 0.3 - rl_config.py 1.1 rgbecker - Renamed config.py to rl_config.py - config.py 1.4 rgbecker - Renamed config.py to rl_config.py - pdfbase/pdfmetrics.py 1.27 rgbecker - Added support for _rl_accel 0.3 - lib/_rl_accel.c 1.8 rgbecker - Added _instanceStringWidth, imroved fontSize arg handling -##### 2001/03/15 ##### - graphics/charts/barchart1.py 1.16 dinu_gherman - Fixed baseline bug and added test sample. - pdfbase/pdfmetrics.py 1.26 rgbecker - addStandardFonts was spurious - pdfgen/canvas.py 1.69 rgbecker - pdfmetrics.addStandardFonts was spurious - pdfbase/pdfmetrics.py 1.25 rgbecker - Stuff for adding standardFonts - pdfgen/canvas.py 1.68 rgbecker - Use pdfmetrics addFonts -##### 2001/03/14 ##### - graphics/charts/barchart1.py 1.15 dinu_gherman - Added more samples. -##### 2001/03/13 ##### - changes 1.7 dinu_gherman - New release 1.05. - __init__.py 1.11 dinu_gherman - New release 1.05. - test/test_graphics_charts.py 1.3 dinu_gherman - Changed to use Legend0 instead of Swatches0. - graphics/charts/legends0.py 1.7 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - graphics/charts/barchart1.py 1.14 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - graphics/charts/axes0.py 1.15 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - lib/setup.py 1.2 rgbecker - Added sgmlop pyHnj extensions - lib/_rl_accel.c 1.7 rgbecker - Fixed up the defaultEncoding thing and added a version number - lib/setup.py 1.1 rgbecker - Initial version of distutils script - lib/_rl_accel.c 1.6 rgbecker - Initial version of distutils script - graphics/charts/barchart1.py 1.13 dinu_gherman - Enabled mono-bar charts and no-bar charts(!) plus more samples. - graphics/charts/axes0.py 1.14 dinu_gherman - Enabled mono-bar charts and no-bar charts(!) plus more samples. - graphics/charts/barchart1.py 1.12 dinu_gherman - Added new samples. - graphics/charts/axes0.py 1.13 dinu_gherman - Set minimum number of items for X/Y axes to 1 instead of 2. - graphics/charts/axes0.py 1.12 dinu_gherman - Fixed bug refusing exactly two data items. - graphics/charts/axes0.py 1.11 dinu_gherman - Improved axes. - graphics/charts/axes0.py 1.10 dinu_gherman - Improved X/Y axes. - graphics/charts/axes0.py 1.9 dinu_gherman - Improved YCategoryAxis slightly. - test/test_graphics_charts.py 1.2 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - platypus/doctemplate.py 1.38 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - graphics/shapes.py 1.9 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - graphics/charts/legends0.py 1.6 andy_robinson - Fixed broken cross reference to another example - test/test_lib_colors.py 1.4 andy_robinson - Added CMYK support to core - pdfgen/canvas.py 1.67 andy_robinson - Added CMYK support to core - lib/colors.py 1.17 andy_robinson - Added CMYK support to core -##### 2001/03/12 ##### - test/test_lib_colors.py 1.3 andy_robinson - Initial checkin of CMYKColor class - lib/colors.py 1.16 andy_robinson - Initial checkin of CMYKColor class - graphics/charts/barchart1.py 1.11 dinu_gherman - Added initial crude version of HorizontalBarChart class. - graphics/charts/axes0.py 1.8 dinu_gherman - Added dual versions for category and value axes. (not all tested) - graphics/charts/piechart0.py 1.5 andy_robinson - Fixed bug with defaultColors changes being erroneouslty banned - graphics/charts/legends0.py 1.5 andy_robinson - Fixed bug with defaultColors changes being erroneouslty banned - test/test_graphics_charts.py 1.1 dinu_gherman - Initial checkin. - graphics/charts/legends0.py 1.4 dinu_gherman - Fixed import error. - test/test_pdfbase_pdfmetrics.py 1.3 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - test/test_paragraphs.py 1.5 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfgen/canvas.py 1.66 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/pdfmetrics.py 1.24 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/pdfdoc.py 1.41 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/_fontdata.py 1.2 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfgen/fonts0.py 1.4 rgbecker - Removed fonts0 stuff to pdfbase.pdfmetrics -##### 2001/03/08 ##### - test/test_pdfbase_pdfmetrics.py 1.2 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfgen/fonts0.py 1.3 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfgen/canvas.py 1.65 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfbase/pdfmetrics.py 1.23 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfbase/_fontdata.py 1.1 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - demos/stdfonts/stdfonts.py 1.10 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - test/test_platypus_paragraphs.py 1.3 dinu_gherman - Added a multi-page paragraph test. - docs/userguide/ch1_intro.py 1.12 rgbecker - Changed bad character -##### 2001/03/07 ##### - utils/simpledoc.py 1.5 rgbecker - Changed things to use config.py for defaults - test/test_paragraphs.py 1.4 rgbecker - Changed things to use config.py for defaults - platypus/test/testplatypus.py 1.22 rgbecker - Changed things to use config.py for defaults - platypus/tables.py 1.35 rgbecker - Changed things to use config.py for defaults - platypus/flowables.py 1.16 rgbecker - Changed things to use config.py for defaults - platypus/doctemplate.py 1.37 rgbecker - Changed things to use config.py for defaults - pdfgen/fonts0.py 1.2 rgbecker - Changed things to use config.py for defaults - pdfgen/canvas.py 1.64 rgbecker - Changed things to use config.py for defaults - pdfbase/pdfdoc.py 1.40 rgbecker - Changed things to use config.py for defaults - lib/tocindex.py 1.6 rgbecker - Changed things to use config.py for defaults - lib/pagesizes.py 1.6 rgbecker - Changed things to use config.py for defaults - lib/fonts.py 1.5 rgbecker - Changed things to use config.py for defaults - docs/userguide/genuserguide.py 1.39 rgbecker - Changed things to use config.py for defaults - docs/userguide/examples.py 1.17 rgbecker - Changed things to use config.py for defaults - docs/userguide/ch4_platypus_concepts.py 1.10 rgbecker - Changed things to use config.py for defaults - docs/userguide/ch2_graphics.py 1.10 rgbecker - Changed things to use config.py for defaults - docs/tools/rltemplate.py 1.9 rgbecker - Changed things to use config.py for defaults - demos/pythonpoint/pythonpoint.py 1.30 rgbecker - Changed things to use config.py for defaults - demos/odyssey/fodyssey.py 1.15 rgbecker - Changed things to use config.py for defaults - demos/gadflypaper/gfe.py 1.13 rgbecker - Changed things to use config.py for defaults - config.py 1.3 rgbecker - Changed things to use config.py for defaults - platypus/paragraph.py 1.51 rgbecker - Added test 5 -##### 2001/03/06 ##### - lib/yaml.py 1.1 andy_robinson - Added - test/test_pdfbase_pdfmetrics.py 1.1 andy_robinson - Added explicit font and encoding support - platypus/paragraph.py 1.50 andy_robinson - Added explicit font and encoding support - platypus/doctemplate.py 1.36 andy_robinson - Added explicit font and encoding support - pdfgen/fonts0.py 1.1 andy_robinson - Added explicit font and encoding support - pdfgen/canvas.py 1.63 andy_robinson - Added explicit font and encoding support - pdfbase/pdfmetrics.py 1.22 andy_robinson - Added explicit font and encoding support - pdfbase/pdfdoc.py 1.39 andy_robinson - Added explicit font and encoding support - lib/graphicsdoc0.py 1.11 andy_robinson - Added explicit font and encoding support - demos/stdfonts/stdfonts.py 1.9 andy_robinson - Added explicit font and encoding support - config.py 1.2 andy_robinson - Added explicit font and encoding support - __init__.py 1.10 andy_robinson - Added explicit font and encoding support -##### 2001/03/02 ##### - lib/fonts.py 1.4 rgbecker - Removed spurious import - lib/fonts.py 1.3 rgbecker - Added standard filename roots and findPFB -##### 2001/03/01 ##### - pdfbase/pdfmetrics.py 1.21 rgbecker - Fixed _rl_accel import -##### 2001/02/28 ##### - platypus/paraparser.py 1.40 rgbecker - Exception tests need str() - pdfbase/pdfutils.py 1.20 rgbecker - Exception tests need str() - pdfbase/pdfmetrics.py 1.20 rgbecker - Exception tests need str() - lib/xmllib.py 1.6 rgbecker - Exception tests need str() - lib/utils.py 1.9 rgbecker - Exception tests need str() - lib/graphicsdoc0.py 1.10 rgbecker - Exception tests need str() - demos/odyssey/dodyssey.py 1.10 rgbecker - slight twitch to onDraw test - pdfbase/pdfutils.py 1.19 rgbecker - Fixed typos - graphics/renderPS.py 1.4 rgbecker - Improved ImportError handling - lib/xmllib.py 1.5 rgbecker - Improved ImportError handling - lib/utils.py 1.8 rgbecker - Improved ImportError handling - lib/graphicsdoc0.py 1.9 rgbecker - Improved ImportError handling - lib/_rl_accel.dsp 1.3 rgbecker - Improved ImportError handling - pdfbase/pdfutils.py 1.18 rgbecker - Improved ImportError handling - pdfbase/pdfmetrics.py 1.19 rgbecker - Improved ImportError handling - pdfbase/pdfdoc.py 1.38 rgbecker - Improved ImportError handling - pdfgen/pdfimages.py 1.8 rgbecker - Imporved ImportError handling - pdfgen/canvas.py 1.62 rgbecker - Imporved ImportError handling - platypus/paraparser.py 1.39 rgbecker - Imporved ImportError handling - graphics/charts/barchart1.py 1.10 dinu_gherman - Changed bar chart labels to have always a boxAttribute of 'c'. - test/test_graphics_charts_textlabel0.py 1.3 dinu_gherman - Added meaningful tests for textAnchor attribute. -##### 2001/02/27 ##### - graphics/charts/textlabel0.py 1.6 rgbecker - Added auto height, width and leading - test/test_graphics_charts_textlabel0.py 1.2 dinu_gherman - Thoroughly changed test file. - graphics/charts/barchart1.py 1.9 dinu_gherman - Splitted function sample2 in sample2a and sample2b. - test/test_graphics_charts_textlabel0.py 1.1 dinu_gherman - Initial checkin. - graphics/charts/textlabel0.py 1.5 dinu_gherman - Removed commented test function. -##### 2001/02/26 ##### - lib/docpy1.py 1.1 andy_robinson - Initial checkin of new experiments -##### 2001/02/24 ##### - graphics/widgets/signsandsymbols.py 1.7 dinu_gherman - Added color attribute to SmileyFace0. - graphics/charts/linechart0.py 1.2 dinu_gherman - General improvements. - graphics/charts/linechart0.py 1.1 dinu_gherman - Initial checkin. -##### 2001/02/23 ##### - test/test_pyfiles.py 1.1 dinu_gherman - Initial checkin. -##### 2001/02/22 ##### - pdfbase/pdfutils.py 1.17 dinu_gherman - Removed commented encoding test functions. - test/test_pdfbase_pdfutils.py 1.1 dinu_gherman - Initial checkin. - pdfbase/pdfutils.py 1.16 dinu_gherman - Commented another test function. - pdfbase/pdfutils.py 1.15 dinu_gherman - Commented one test function. - graphics/charts/textlabel0.py 1.4 dinu_gherman - Minor corrections. - graphics/charts/axes0.py 1.7 dinu_gherman - Minor corrections. -##### 2001/02/21 ##### - pdfgen/canvas.py 1.61 aaron_watters - bugfix: code before of begin/end form should not appear in the form but in page -##### 2001/02/19 ##### - graphics/charts/axes0.py 1.6 dinu_gherman - Added different modes for joined x- and y-axes. - graphics/charts/barchart1.py 1.8 dinu_gherman - Added attribute mappings. - graphics/charts/axes0.py 1.5 dinu_gherman - Added attribute mappings. - graphics/charts/axes0.py 1.4 dinu_gherman - Changed test function to sample function. - graphics/charts/legends0.py 1.3 dinu_gherman - Added combined barchart sample. - graphics/charts/legends0.py 1.2 dinu_gherman - Minor changes. - graphics/charts/legends0.py 1.1 dinu_gherman - Initial checkin. - test/test_pdfgen_pagemodes.py 1.2 dinu_gherman - Added doc string. - test/test_platypus_paragraphs.py 1.2 dinu_gherman - Added doc strings. - test/test_lib_sequencer.py 1.2 dinu_gherman - Added doc strings. - test/test_lib_colors.py 1.2 dinu_gherman - Added doc strings. - test/test_docstrings.py 1.2 dinu_gherman - Fixed filename bug. - test/test_docstrings.py 1.1 dinu_gherman - Initial checkin. -##### 2001/02/18 ##### - test/unittest.py 1.2 andy_robinson - Resynched graphics test with Pie class; runAll now deletes previous PDF files incorporated latest unittest.py - test/test_graphics_speed.py 1.3 andy_robinson - Resynched graphics test with Pie class; runAll now deletes previous PDF files incorporated latest unittest.py - test/runAll.py 1.2 andy_robinson - Resynched graphics test with Pie class; runAll now deletes previous PDF files incorporated latest unittest.py - test/00readme.txt 1.4 andy_robinson - Resynched graphics test with Pie class; runAll now deletes previous PDF files incorporated latest unittest.py -##### 2001/02/16 ##### - test/test_lib_colors.py 1.1 dinu_gherman - Initil checkin. - lib/colors.py 1.15 dinu_gherman - Added color2bw function. -##### 2001/02/15 ##### - graphics/charts/piechart0.py 1.4 andy_robinson - Slimmed down and added demo methods - graphics/charts/barchart1.py 1.7 andy_robinson - Slimmed down and added demo methods - graphics/charts/axes0.py 1.3 andy_robinson - Slimmed down and added demo methods - test/test_platypus_paragraphs.py 1.1 dinu_gherman - Initial checkin. -##### 2001/02/14 ##### - graphics/charts/barchart.py 1.4 dinu_gherman - File has become obsolete. See barchart1.py. - lib/graphicsdoc0.py 1.8 dinu_gherman - Improved displaying widget properties (using pprint). - pdfgen/test/testPageMode.py 1.3 dinu_gherman - Moved to reportlab.test. - test/test_pdfgen_pagemodes.py 1.1 dinu_gherman - Initial checkin, moved from reportlab.pdfgen.test. - test/test_lib_sequencer.py 1.1 dinu_gherman - Initial checkin. - test/runAll.py 1.1 dinu_gherman - Initial checkin. - test/test_platypus_toc.py 1.3 dinu_gherman - Restructured entire test suite to make clean use of the unittest module. - test/test_paragraphs.py 1.3 dinu_gherman - Restructured entire test suite to make clean use of the unittest module. - test/test_graphics_speed.py 1.2 dinu_gherman - Restructured entire test suite to make clean use of the unittest module. - test/00readme.txt 1.3 dinu_gherman - Restructured entire test suite to make clean use of the unittest module. -##### 2001/02/13 ##### - graphics/widgets/flags0.py 1.4 johnprecedo - - Removed white borders from inside edges of flags. - added the word "sample" to the demo flag produced by Flag0 - dummy __init__ and demo methods added for Portugal0 - no example flag or properties are produced. -##### 2001/03/13 ##### - test/test_graphics_charts.py 1.3 dinu_gherman - Changed to use Legend0 instead of Swatches0. - graphics/charts/legends0.py 1.7 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - graphics/charts/barchart1.py 1.14 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - graphics/charts/axes0.py 1.15 dinu_gherman - Renamed Swatches0 to Legend0. Stripped away horizontal bar charts temporyrily. - lib/setup.py 1.2 rgbecker - Added sgmlop pyHnj extensions - lib/_rl_accel.c 1.7 rgbecker - Fixed up the defaultEncoding thing and added a version number - lib/setup.py 1.1 rgbecker - Initial version of distutils script - lib/_rl_accel.c 1.6 rgbecker - Initial version of distutils script - graphics/charts/barchart1.py 1.13 dinu_gherman - Enabled mono-bar charts and no-bar charts(!) plus more samples. - graphics/charts/axes0.py 1.14 dinu_gherman - Enabled mono-bar charts and no-bar charts(!) plus more samples. - graphics/charts/barchart1.py 1.12 dinu_gherman - Added new samples. - graphics/charts/axes0.py 1.13 dinu_gherman - Set minimum number of items for X/Y axes to 1 instead of 2. - graphics/charts/axes0.py 1.12 dinu_gherman - Fixed bug refusing exactly two data items. - graphics/charts/axes0.py 1.11 dinu_gherman - Improved axes. - graphics/charts/axes0.py 1.10 dinu_gherman - Improved X/Y axes. - graphics/charts/axes0.py 1.9 dinu_gherman - Improved YCategoryAxis slightly. - test/test_graphics_charts.py 1.2 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - platypus/doctemplate.py 1.38 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - graphics/shapes.py 1.9 dinu_gherman - Changed attribute postponed to _postponed in doctemplate.py. Added minor changes in shapes.py. Changed test_graphics_charts.py to no longer use page breaks. - graphics/charts/legends0.py 1.6 andy_robinson - Fixed broken cross reference to another example - test/test_lib_colors.py 1.4 andy_robinson - Added CMYK support to core - pdfgen/canvas.py 1.67 andy_robinson - Added CMYK support to core - lib/colors.py 1.17 andy_robinson - Added CMYK support to core -##### 2001/03/12 ##### - test/test_lib_colors.py 1.3 andy_robinson - Initial checkin of CMYKColor class - lib/colors.py 1.16 andy_robinson - Initial checkin of CMYKColor class - graphics/charts/barchart1.py 1.11 dinu_gherman - Added initial crude version of HorizontalBarChart class. - graphics/charts/axes0.py 1.8 dinu_gherman - Added dual versions for category and value axes. (not all tested) - graphics/charts/piechart0.py 1.5 andy_robinson - Fixed bug with defaultColors changes being erroneouslty banned - graphics/charts/legends0.py 1.5 andy_robinson - Fixed bug with defaultColors changes being erroneouslty banned - test/test_graphics_charts.py 1.1 dinu_gherman - Initial checkin. - graphics/charts/legends0.py 1.4 dinu_gherman - Fixed import error. - test/test_pdfbase_pdfmetrics.py 1.3 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - test/test_paragraphs.py 1.5 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfgen/canvas.py 1.66 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/pdfmetrics.py 1.24 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/pdfdoc.py 1.41 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfbase/_fontdata.py 1.2 rgbecker - Changes to basic Font stuff, moved it to pdfmetrics - pdfgen/fonts0.py 1.4 rgbecker - Removed fonts0 stuff to pdfbase.pdfmetrics -##### 2001/03/08 ##### - test/test_pdfbase_pdfmetrics.py 1.2 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfgen/fonts0.py 1.3 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfgen/canvas.py 1.65 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfbase/pdfmetrics.py 1.23 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - pdfbase/_fontdata.py 1.1 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - demos/stdfonts/stdfonts.py 1.10 rgbecker - Initial split of pdfmetrics into code + database; some minor name adjustments - test/test_platypus_paragraphs.py 1.3 dinu_gherman - Added a multi-page paragraph test. -##### 2001/03/07 ##### - test/test_paragraphs.py 1.4 rgbecker - Changed things to use config.py for defaults - platypus/test/testplatypus.py 1.22 rgbecker - Changed things to use config.py for defaults - platypus/tables.py 1.35 rgbecker - Changed things to use config.py for defaults - platypus/flowables.py 1.16 rgbecker - Changed things to use config.py for defaults - platypus/doctemplate.py 1.37 rgbecker - Changed things to use config.py for defaults - pdfgen/fonts0.py 1.2 rgbecker - Changed things to use config.py for defaults - pdfgen/canvas.py 1.64 rgbecker - Changed things to use config.py for defaults - pdfbase/pdfdoc.py 1.40 rgbecker - Changed things to use config.py for defaults - lib/tocindex.py 1.6 rgbecker - Changed things to use config.py for defaults - lib/pagesizes.py 1.6 rgbecker - Changed things to use config.py for defaults - lib/fonts.py 1.5 rgbecker - Changed things to use config.py for defaults - demos/pythonpoint/pythonpoint.py 1.30 rgbecker - Changed things to use config.py for defaults - demos/odyssey/fodyssey.py 1.15 rgbecker - Changed things to use config.py for defaults - demos/gadflypaper/gfe.py 1.13 rgbecker - Changed things to use config.py for defaults - config.py 1.3 rgbecker - Changed things to use config.py for defaults - platypus/paragraph.py 1.51 rgbecker - Added test 5 -##### 2001/03/06 ##### - lib/yaml.py 1.1 andy_robinson - Added - test/test_pdfbase_pdfmetrics.py 1.1 andy_robinson - Added explicit font and encoding support - platypus/paragraph.py 1.50 andy_robinson - Added explicit font and encoding support - platypus/doctemplate.py 1.36 andy_robinson - Added explicit font and encoding support - pdfgen/fonts0.py 1.1 andy_robinson - Added explicit font and encoding support - pdfgen/canvas.py 1.63 andy_robinson - Added explicit font and encoding support - pdfbase/pdfmetrics.py 1.22 andy_robinson - Added explicit font and encoding support - pdfbase/pdfdoc.py 1.39 andy_robinson - Added explicit font and encoding support - lib/graphicsdoc0.py 1.11 andy_robinson - Added explicit font and encoding support - demos/stdfonts/stdfonts.py 1.9 andy_robinson - Added explicit font and encoding support - config.py 1.2 andy_robinson - Added explicit font and encoding support - __init__.py 1.10 andy_robinson - Added explicit font and encoding support -##### 2001/feb/09 release 1.03 -##### 2001/02/09 ##### - platypus/tableofcontents0.py 1.2 dinu_gherman - Fixed wrapped line indentation. - lib/graphicsdoc0.py 1.7 dinu_gherman - Entirely reworked PDF and HTML doc builders. - lib/docpy0.py 1.7 dinu_gherman - Entirely reworked PDF and HTML doc builders. - graphics/widgetbase.py 1.5 dinu_gherman - Shortened two lines that might appear in doc tools. - platypus/tableofcontents0.py 1.1 dinu_gherman - Initial checkin. -##### 2001/02/08 ##### - platypus/doctemplate.py 1.35 dinu_gherman - Commented some disturbing print statements. - lib/graphicsdoc0.py 1.6 dinu_gherman - UmlPdfDocBuilder moved to docpy0.py. Changed some defaults and variable names. Fixed handling of packages with dotted names. Changed output of GraphPdfDocBuilder0. - lib/docpy0.py 1.6 dinu_gherman - UmlPdfDocBuilder moved here from graphicsdoc0.py. Changed some defaults and variable names. Fixed handling of packages with dotted names. - graphics/charts/barchart1.py 1.6 dinu_gherman - One long line reduced in length. -##### 2001/02/07 ##### - lib/randomtext.py 1.6 johnprecedo - fixed a typo - lib/randomtext.py 1.5 johnprecedo - Added a PYTHON theme (it had to be done...). - lib/graphicsdoc0.py 1.5 dinu_gherman - Added displaying drawings returned by functions. - graphics/widgets/signsandsymbols.py 1.5 johnprecedo - Corrected mistake in the doctsring for flags0.py and signsandsymbols.py. - graphics/widgets/flags0.py 1.3 johnprecedo - Corrected mistake in the doctsring for flags0.py and signsandsymbols.py. - lib/graphicsdoc0.py 1.4 dinu_gherman - Fixed import error. - lib/graphicsdoc0.py 1.3 dinu_gherman - Added an experimental PlatypusDocBuilder0. Uncommented UmlPdfDocBuilder0. - lib/graphicsdoc0.py 1.2 dinu_gherman - Moved utility functions from docpy0.py to graphicsdoc0.py. - lib/docpy0.py 1.5 dinu_gherman - Moved utility functions from docpy0.py to graphicsdoc0.py. -##### 2001/02/06 ##### - lib/graphicsdoc0.py 1.1 dinu_gherman - Initial checkin. - lib/docpy0.py 1.4 dinu_gherman - Stripped-off everything widget/graphics-specific. - graphics/charts/barchart1.py 1.5 andy_robinson - Added barLabelBudge property to support a customer - graphics/renderbase.py 1.8 andy_robinson - Fixes for QIR barchart presentation - graphics/renderPS.py 1.3 andy_robinson - Fixes for QIR barchart presentation - graphics/charts/barchart1.py 1.4 andy_robinson - Fixes for QIR barchart presentation -##### 2001/02/05 ##### - pdfbase/pdfdoc.py 1.36 aaron_watters - forms in forms now work - graphics/widgets/signsandsymbols.py 1.4 johnprecedo - bugfixes for the various demo() methods for widgets. - lib/docpy0.py 1.3 dinu_gherman - Fixed string joining for base class names. - graphics/widgets/signsandsymbols.py 1.3 johnprecedo - changes all demo() methods to centre graphics in boxes 200x100 boxes.. - lib/docpy0.py 1.2 dinu_gherman - Modified inspect import. - lib/docpy0.py 1.1 dinu_gherman - Initial checkin. - graphics/widgets/flags0.py 1.2 johnprecedo - tightened up code for stripes in US and Greek flags. - graphics/widgets/flags0.py 1.1 johnprecedo - First checkin of file that used to be users/john/fwidgets.py. 20 flags as widgets - inlcuding UK, US, most EU members and a few non-EU European countries. - pdfbase/pdfdoc.py 1.35 aaron_watters - pdfdictionary has no has_key method bugfix. sorry. -##### 2001/02/04 ##### - graphics/renderbase.py 1.7 andy_robinson - Continued work on bar charts and support - graphics/charts/textlabel0.py 1.3 andy_robinson - Continued work on bar charts and support - graphics/charts/barchart1.py 1.3 andy_robinson - Continued work on bar charts and support - graphics/charts/axes0.py 1.2 andy_robinson - Continued work on bar charts and support - graphics/charts/axes0.py 1.1 andy_robinson - Broke out axes to separate module - graphics/widgetbase.py 1.4 andy_robinson - Fixed user node rendering bug, added barchart1 - graphics/shapes.py 1.8 andy_robinson - Fixed user node rendering bug, added barchart1 - graphics/renderbase.py 1.6 andy_robinson - Fixed user node rendering bug, added barchart1 - graphics/charts/textlabel0.py 1.2 andy_robinson - Fixed user node rendering bug, added barchart1 - graphics/charts/barchart1.py 1.2 andy_robinson - Fixed user node rendering bug, added barchart1 -##### 2001/02/02 ##### - pdfbase/pdfdoc.py 1.34 aaron_watters - minor changes to support pdfparse -##### 2001/02/01 ##### - graphics/testshapes.py 1.8 andy_robinson - Working on chart framework - graphics/shapes.py 1.7 andy_robinson - Working on chart framework - graphics/renderbase.py 1.5 andy_robinson - Working on chart framework - graphics/charts/textlabel0.py 1.1 andy_robinson - Working on chart framework - graphics/charts/barchart1.py 1.1 andy_robinson - Working on chart framework -##### 2001/01/30 ##### - lib/inspect.py 1.1 dinu_gherman - Initial checkin. - graphics/charts/barchart.py 1.3 aaron_watters - more stuff, bug fixes etcetera.... - graphics/testshapes.py 1.7 andy_robinson - Fixed group transform bug and added tests - graphics/renderbase.py 1.4 andy_robinson - Fixed group transform bug and added tests - graphics/renderPS.py 1.2 andy_robinson - Fixed group transform bug and added tests - graphics/renderPDF.py 1.4 andy_robinson - Fixed group transform bug and added tests -##### 2001/01/29 ##### - graphics/widgets/signsandsymbols.py 1.2 johnprecedo - Changed classnames to use the '0' convention for experimental. Changed way test print out is carried out. (all calls to classes wrapped up in a new function 'test()') some tidying up. -##### 2001/01/28 ##### - graphics/testshapes.py 1.6 andy_robinson - Fixes to CTM to support bitmap renderer; extra string rotation and group tests. - graphics/renderbase.py 1.3 andy_robinson - Fixes to CTM to support bitmap renderer; extra string rotation and group tests. -##### 2001/01/26 ##### - graphics/widgetbase.py 1.3 andy_robinson - Added barchart compatibility - graphics/shapes.py 1.6 andy_robinson - Added barchart compatibility - graphics/charts/piechart0.py 1.3 andy_robinson - Added barchart compatibility - graphics/charts/barchart.py 1.2 aaron_watters - added scales -##### 2001/01/25 ##### - graphics/widgets/signsandsymbols.py 1.1 johnprecedo - First checkin of file that used to be users/john/jwidgets.py. 12 new widgets. - graphics/charts/barchart.py 1.1 aaron_watters - A initial go at Aarons last rewrite of chart widgets :) - graphics/testshapes.py 1.5 andy_robinson - Added postscript renderer and tests, fixed renderer bugs - graphics/shapes.py 1.5 andy_robinson - Added postscript renderer and tests, fixed renderer bugs - graphics/renderbase.py 1.2 andy_robinson - Added postscript renderer and tests, fixed renderer bugs - graphics/renderPS.py 1.1 andy_robinson - Added postscript renderer and tests, fixed renderer bugs - test/test_graphics_speed.py 1.1 andy_robinson - added a crude benchmarking script -##### 2001/01/24 ##### - graphics/testshapes.py 1.4 andy_robinson - Added group tests, fixed font bug, resynched with Dinu - graphics/shapes.py 1.4 andy_robinson - Added group tests, fixed font bug, resynched with Dinu - graphics/renderPDF.py 1.3 andy_robinson - Added group tests, fixed font bug, resynched with Dinu - graphics/testshapes.py 1.3 dinu_gherman - Added unit testing plus error drawing. - graphics/testshapes.py 1.2 dinu_gherman - Added some String shapes. - graphics/testshapes.py 1.1 dinu_gherman - Initial checkin. - graphics/widgetbase.py 1.2 andy_robinson - Collection based piechart amd support for it. - graphics/shapes.py 1.3 andy_robinson - Collection based piechart amd support for it. - graphics/renderPDF.py 1.2 andy_robinson - Collection based piechart amd support for it. - graphics/charts/piechart0.py 1.2 andy_robinson - Collection based piechart amd support for it. -##### 2001/01/23 ##### - config.py 1.1 johnprecedo - Whoops, forgot - graphics/shapes.py 1.2 johnprecedo - typos fixed -##### 2001/01/22 ##### - graphics/charts/piechart0.py 1.1 andy_robinson - Experimental pie module - graphics/widgets/__init__.py 1.1 andy_robinson - Added widgets subpackage - graphics/charts/__init__.py 1.1 andy_robinson - Added charts package - graphics/widgetbase.py 1.1 andy_robinson - Added graphics module - graphics/testdrawings.py 1.1 andy_robinson - Added graphics module - graphics/shapes.py 1.1 andy_robinson - Added graphics module - graphics/renderbase.py 1.1 andy_robinson - Added graphics module - graphics/renderPDF.py 1.1 andy_robinson - Added graphics module - graphics/__init__.py 1.1 andy_robinson - Added graphics module - pdfbase/pdfutils.py 1.14 dinu_gherman - Modified docstrings. -##### 2001/01/19 ##### - lib/abag.py 1.4 rgbecker - Changed comment to be slightly more meaningful - platypus/frames.py 1.12 dinu_gherman - Minor changes. - platypus/doctemplate.py 1.34 dinu_gherman - Minor changes. - lib/randomtext.py 1.4 dinu_gherman - Minor changes. -##### 2001/01/18 ##### - lib/colors.py 1.14 rgbecker - Syncing with pingo -##### 2001/01/12 ##### - pdfgen/textobject.py 1.22 dinu_gherman - Minor neglectable changes. - pdfgen/pdfimages.py 1.7 dinu_gherman - Minor neglectable changes. - pdfgen/pdfgeom.py 1.7 dinu_gherman - Minor neglectable changes. - pdfgen/pathobject.py 1.8 dinu_gherman - Minor neglectable changes. - pdfgen/canvas.py 1.60 dinu_gherman - Minor neglectable changes. - lib/utils.py 1.7 dinu_gherman - Minor neglectable changes. - lib/sequencer.py 1.10 dinu_gherman - Minor neglectable changes. -##### 2001/01/10 ##### - lib/abag.py 1.3 dinu_gherman - Docstring modified. -##### 2001/01/02 ##### - lib/randomtext.py 1.3 johnprecedo - Minor tweaks (removing unneccesary capital letters etc). - lib/randomtext.py 1.2 johnprecedo - Added 5 new themes. -##### 2000/12/29 ##### - test/test_paragraphs.py 1.2 andy_robinson - Added a random text module - platypus/flowables.py 1.15 andy_robinson - Added a random text module - lib/randomtext.py 1.1 andy_robinson - Added a random text module -##### 2000/12/25 ##### - platypus/flowables.py 1.14 rgbecker - Allow for non string file names in Image.__init__ -##### 2000/12/20 ##### - pdfgen/pdfimages.py 1.6 andy_robinson - death to two tabs! -##### 2000/12/19 ##### - pdfgen/pdfimages.py 1.5 rgbecker - Fix typo - pdfgen/pdfimages.py 1.4 rgbecker - Fix to PIL_image, changed to warnOnce -##### 2000/12/18 ##### - platypus/paragraph.py 1.49 rgbecker - More Dinu related fixes/edits - pdfgen/canvas.py 1.59 andy_robinson - Stripped a couple of tabs in an otherwise-space-delimited module -##### 2000/12/17 ##### - platypus/paragraph.py 1.48 rgbecker - Hack to fix Dinu's problem - platypus/doctemplate.py 1.33 rgbecker - Hack to fix Dinu's problem -##### 2000/12/15 ##### - platypus/doctemplate.py 1.32 rgbecker - Better error message - platypus/paragraph.py 1.47 rgbecker - Start on new simpler line splitter - lib/abag.py 1.2 aaron_watters - added a __repr__ for debug purposes - platypus/paragraph.py 1.46 rgbecker - Further Dinuistic fixes for rare cbDefn bugs -##### 2000/12/14 ##### - platypus/test/testplatypus.py 1.21 rgbecker - relativisation of firstLineIndent related code - lib/tocindex.py 1.5 rgbecker - relativisation of firstLineIndent related code - lib/styles.py 1.13 rgbecker - relativisation of firstLineIndent related code - demos/pythonpoint/styles_modern.py 1.8 rgbecker - relativisation of firstLineIndent related code - demos/pythonpoint/styles_horrible.py 1.7 rgbecker - relativisation of firstLineIndent related code - demos/pythonpoint/pythonpoint.py 1.29 rgbecker - relativisation of firstLineIndent related code - lib/__init__.py 1.3 rgbecker - Added RL_DEBUG environ flag - lib/__BUILD.dsw 1.3 rgbecker - Added RL_DEBUG environ flag - pdfbase/pdfmetrics.py 1.18 rgbecker - Added extension debug code - platypus/paragraph.py 1.45 rgbecker - Delete fix - platypus/paragraph.py 1.44 rgbecker - Fixed Dinu's tagmadness4 problem -##### 2000/12/13 ##### - platypus/tables.py 1.34 aaron_watters - added __repr__s and enhanced exception messages for debugging - platypus/paragraph.py 1.43 aaron_watters - added __repr__s and enhanced exception messages for debugging - platypus/frames.py 1.11 aaron_watters - added __repr__s and enhanced exception messages for debugging - platypus/flowables.py 1.13 aaron_watters - added __repr__s and enhanced exception messages for debugging - platypus/test/testplatypus.py 1.20 rgbecker - Try harder than ever for line cleaning and add bg alias - platypus/paraparser.py 1.38 rgbecker - Try harder than ever for line cleaning and add bg alias - platypus/paragraph.py 1.42 rgbecker - Try harder than ever for line cleaning and add bg alias - platypus/paragraph.py 1.41 rgbecker - Layout fixes; the text objects needed moves before & after and the bullet width was calculated after maxWidth was set. Try harder for space/tab removal. - pdfgen/textobject.py 1.21 rgbecker - Layout fixes; Td was absolute not relative to the beginning of the line - platypus/tables.py 1.33 rgbecker - Fixed confusing error message -########### 1.02 Released 2000/12/11 -##### 2000/12/11 ##### - utils/README 1.5 rgbecker - z7-->z9 - platypus/paraparser.py 1.37 rgbecker - Comments and cosmetic changes - platypus/paragraph.py 1.40 rgbecker - Comments and cosmetic changes -##### 2000/12/10 ##### - pdfgen/textobject.py 1.20 andy_robinson - Fixed obscure stringwidth bugs in textobject and bullets - pdfgen/canvas.py 1.58 andy_robinson - Fixed obscure stringwidth bugs in textobject and bullets - pdfbase/pdfmetrics.py 1.17 andy_robinson - Fixed obscure stringwidth bugs in textobject and bullets - lib/styles.py 1.12 andy_robinson - Fixed obscure stringwidth bugs in textobject and bullets - platypus/paraparser.py 1.36 andy_robinson - Added a text background color attribute - platypus/paragraph.py 1.39 andy_robinson - Added a text background color attribute - lib/styles.py 1.11 andy_robinson - Added a text background color attribute - test/test_paragraphs.py 1.1 andy_robinson - Added some paragraph style tests - lib/styles.py 1.10 andy_robinson - Added a title style to support rml2pdf -##### 2000/12/08 ##### - platypus/paragraph.py 1.38 rgbecker - Clean up one word justified paragraph fix. -##### 2000/12/07 ##### - platypus/paragraph.py 1.37 aaron_watters - divide by zero problem for one word paragraphs -##### 2000/12/06 ##### - docs/userguide/ch4_platypus_concepts.py 1.9 aaron_watters - less than should be less than or equal in example - platypus/paragraph.py 1.36 rgbecker - Fixed line wrap problem -##### 2000/12/05 ##### - demos/pythonpoint/pythonpoint.xml 1.12 andy_robinson - Changed link to Michael Wiedmann's DTD - platypus/paraparser.py 1.35 rgbecker - Fixed typo -##### 2000/12/04 ##### - extensions/README 1.2 rgbecker - Spelling corrections - platypus/paraparser.py 1.34 rgbecker - More cosmetic changes - platypus/paragraph.py 1.35 rgbecker - Cosmetic changes - lib/tocindex.py 1.4 rgbecker - Minor fixes to appends -##### 2000/12/01 ##### - platypus/paragraph.py 1.34 aaron_watters - mods permitting late string conversions - pdfgen/canvas.py 1.57 aaron_watters - mods permitting late string conversions - lib/utils.py 1.6 aaron_watters - mods permitting late string conversions -##### 2000/11/29 ##### - platypus/xpreformatted.py 1.12 rgbecker - Semantic Name changes - platypus/paraparser.py 1.33 rgbecker - Semantic Name changes - platypus/paragraph.py 1.33 rgbecker - Semantic Name changes - platypus/__init__.py 1.12 rgbecker - Semantic Name changes - lib/abag.py 1.1 rgbecker - Initial version split from paraparser.py -##### 2000/11/24 ##### - platypus/paragraph.py 1.32 rgbecker - Fixed missing linefeed bug for cbDefn frag at end of line - lib/_rl_accel.c 1.5 rgbecker - Fixed non-working cast, added getInfo -##### 2000/11/23 ##### - platypus/paraparser.py 1.32 rgbecker - Slight optimisation in handle_data for cbdefn frags - platypus/paragraph.py 1.31 rgbecker - Force cbDefn frags to be not Same - platypus/flowables.py 1.12 andy_robinson - Added working table of contents framework - platypus/doctemplate.py 1.31 andy_robinson - Added working table of contents framework - lib/tocindex.py 1.3 andy_robinson - Added working table of contents framework - lib/styles.py 1.9 andy_robinson - Added working table of contents framework -##### 2000/11/21 ##### - platypus/paragraph.py 1.30 rgbecker - Fixed call back handling in _getFragWords - lib/tocindex.py 1.2 andy_robinson - Now formats correctly, no dynamic build stuff yet. - lib/tocindex.py 1.1 andy_robinson - Initial checkin -##### 2000/11/13 ##### - platypus/paraparser.py 1.31 rgbecker - More hacks - platypus/paragraph.py 1.29 rgbecker - More hacks - platypus/paraparser.py 1.30 rgbecker - Fixes to stupid parser - platypus/paraparser.py 1.29 rgbecker - Added onDraw tag to paragraphs - platypus/paragraph.py 1.28 rgbecker - Added onDraw tag to paragraphs - pdfbase/pdfmetrics.py 1.16 rgbecker - Added onDraw tag to paragraphs - demos/odyssey/dodyssey.py 1.9 rgbecker - Added onDraw tag to paragraphs -##### 2000/11/08 ##### - test/unittest.py 1.1 andy_robinson - Added unittest.py and packagized - test/__init__.py 1.1 andy_robinson - Added unittest.py and packagized - pdfbase/pdfdoc.py 1.33 andy_robinson - Changes to fix font encodings - demos/stdfonts/stdfonts.py 1.8 andy_robinson - Changes to fix font encodings - utils/runtests.py 1.15 andy_robinson - Added an exclusion for 'test/unittest.py' in the test suite. - test/00readme.txt 1.2 andy_robinson - Fixed font encoding lookup, metrics for JPython, created test directory - pdfbase/pdfmetrics.py 1.15 andy_robinson - Fixed font encoding lookup, metrics for JPython, created test directory - pdfbase/pdfdoc.py 1.32 andy_robinson - Fixed font encoding lookup, metrics for JPython, created test directory -##### 2000/11/06 ##### - demos/pythonpoint/pythonpoint.xml 1.11 andy_robinson - compression on by default. - demos/pythonpoint/pythonpoint.py 1.28 andy_robinson - compression on by default. -##### 2000/11/05 ##### - platypus/doctemplate.py 1.30 andy_robinson - Fixed page transitions; extended Pythonpoint - pdfgen/canvas.py 1.56 andy_robinson - Fixed page transitions; extended Pythonpoint - pdfbase/pdfdoc.py 1.31 andy_robinson - Fixed page transitions; extended Pythonpoint - demos/pythonpoint/stdparser.py 1.13 andy_robinson - Fixed page transitions; extended Pythonpoint - demos/pythonpoint/pythonpoint.xml 1.10 andy_robinson - Fixed page transitions; extended Pythonpoint - demos/pythonpoint/pythonpoint.py 1.27 andy_robinson - Fixed page transitions; extended Pythonpoint - test/00readme.txt 1.1 andy_robinson - initial checkin - docs/userguide/genuserguide.py 1.38 andy_robinson - UserGuide now resizes template header to fit page size - docs/tools/rltemplate.py 1.8 andy_robinson - UserGuide now resizes template header to fit page size -##### 2000/11/01 ##### - platypus/paragraph.py 1.27 andy_robinson - Added experimental method getActualLineWidths0 -##### 2000/10/26 ##### - platypus/tables.py 1.32 rgbecker - Layout error fixes - platypus/paragraph.py 1.26 rgbecker - Layout error fixes - platypus/flowables.py 1.11 rgbecker - Layout error fixes - docs/userguide/genuserguide.py 1.37 rgbecker - Make page size an optional argument -##### 2000/10/25 ##### - utils/simpledoc.py 1.4 rgbecker - Changed to indirect copyright - utils/runtests.py 1.14 rgbecker - Changed to indirect copyright - utils/license.py 1.4 rgbecker - Changed to indirect copyright - utils/license.c 1.4 rgbecker - Changed to indirect copyright - utils/daily.py 1.38 rgbecker - Changed to indirect copyright - utils/cvslh.py 1.4 rgbecker - Changed to indirect copyright - utils/cvs_check.py 1.7 rgbecker - Changed to indirect copyright - utils/compress_images.py 1.2 rgbecker - Changed to indirect copyright - platypus/test/testtables.py 1.12 rgbecker - Changed to indirect copyright - platypus/xpreformatted.py 1.11 rgbecker - Changed to indirect copyright - platypus/test/testplatypus.py 1.19 rgbecker - Changed to indirect copyright - platypus/tables.py 1.31 rgbecker - Changed to indirect copyright - platypus/paraparser.py 1.28 rgbecker - Changed to indirect copyright - platypus/paragraph.py 1.25 rgbecker - Changed to indirect copyright - platypus/frames.py 1.10 rgbecker - Changed to indirect copyright - platypus/flowables.py 1.10 rgbecker - Changed to indirect copyright - platypus/doctemplate.py 1.29 rgbecker - Changed to indirect copyright - platypus/__init__.py 1.11 rgbecker - Changed to indirect copyright - pdfgen/textobject.py 1.19 rgbecker - Changed to indirect copyright - pdfgen/test/testpdfgen.py 1.16 rgbecker - Changed to indirect copyright - pdfgen/test/testPageMode.py 1.2 rgbecker - Changed to indirect copyright - pdfgen/pdfimages.py 1.3 rgbecker - Changed to indirect copyright - pdfgen/pdfgeom.py 1.6 rgbecker - Changed to indirect copyright - pdfgen/pathobject.py 1.7 rgbecker - Changed to indirect copyright - pdfgen/canvas.py 1.55 rgbecker - Changed to indirect copyright - pdfgen/__init__.py 1.5 rgbecker - Changed to indirect copyright - pdfbase/pdfutils.py 1.13 rgbecker - Changed to indirect copyright - pdfbase/pdfmetrics.py 1.14 rgbecker - Changed to indirect copyright - pdfbase/pdfdoc.py 1.30 rgbecker - Changed to indirect copyright - pdfbase/__init__.py 1.5 rgbecker - Changed to indirect copyright - lib/utils.py 1.5 rgbecker - Changed to indirect copyright - lib/units.py 1.2 rgbecker - Changed to indirect copyright - lib/styles.py 1.8 rgbecker - Changed to indirect copyright - lib/sequencer.py 1.9 rgbecker - Changed to indirect copyright - lib/pagesizes.py 1.5 rgbecker - Changed to indirect copyright - lib/logger.py 1.2 rgbecker - Changed to indirect copyright - lib/fonts.py 1.2 rgbecker - Changed to indirect copyright - lib/enums.py 1.2 rgbecker - Changed to indirect copyright - lib/corp.py 1.2 rgbecker - Changed to indirect copyright - lib/colors.py 1.13 rgbecker - Changed to indirect copyright - lib/_rl_accel.c 1.4 rgbecker - Changed to indirect copyright - lib/__init__.py 1.2 rgbecker - Changed to indirect copyright - extensions/__init__.py 1.2 rgbecker - Changed to indirect copyright - docs/userguide/t_parse.py 1.2 rgbecker - Changed to indirect copyright - docs/userguide/platdemos.py 1.4 rgbecker - Changed to indirect copyright - docs/userguide/genuserguide.py 1.36 rgbecker - Changed to indirect copyright - docs/userguide/examples.py 1.16 rgbecker - Changed to indirect copyright - docs/userguide/ch8_future.py 1.3 rgbecker - Changed to indirect copyright - docs/userguide/ch7_custom.py 1.6 rgbecker - Changed to indirect copyright - docs/userguide/ch6_tables.py 1.16 rgbecker - Changed to indirect copyright - docs/userguide/ch5_paragraphs.py 1.6 rgbecker - Changed to indirect copyright - docs/userguide/ch4_platypus_concepts.py 1.8 rgbecker - Changed to indirect copyright - docs/userguide/ch3_pdffeatures.py 1.7 rgbecker - Changed to indirect copyright - docs/userguide/ch2_graphics.py 1.9 rgbecker - Changed to indirect copyright - docs/userguide/ch1_intro.py 1.11 rgbecker - Changed to indirect copyright - docs/userguide/app_demos.py 1.7 rgbecker - Changed to indirect copyright - docs/tools/yaml2pdf.py 1.8 rgbecker - Changed to indirect copyright - docs/tools/yaml.py 1.9 rgbecker - Changed to indirect copyright - docs/tools/stylesheet.py 1.7 rgbecker - Changed to indirect copyright - docs/tools/rltemplate.py 1.7 rgbecker - Changed to indirect copyright - docs/tools/codegrab.py 1.5 rgbecker - Changed to indirect copyright - demos/tests/testdemos.py 1.5 rgbecker - Changed to indirect copyright - demos/stdfonts/stdfonts.py 1.7 rgbecker - Changed to indirect copyright - demos/pythonpoint/styles_modern.py 1.7 rgbecker - Changed to indirect copyright - demos/pythonpoint/styles_horrible.py 1.6 rgbecker - Changed to indirect copyright - demos/pythonpoint/stdparser.py 1.12 rgbecker - Changed to indirect copyright - demos/pythonpoint/pythonpoint.py 1.26 rgbecker - Changed to indirect copyright - demos/pythonpoint/customshapes.py 1.3 rgbecker - Changed to indirect copyright - demos/py2pdf/idle_print.py 1.7 rgbecker - Changed to indirect copyright - demos/odyssey/odyssey.py 1.9 rgbecker - Changed to indirect copyright - demos/odyssey/fodyssey.py 1.14 rgbecker - Changed to indirect copyright - demos/odyssey/dodyssey.py 1.8 rgbecker - Changed to indirect copyright - demos/gadflypaper/gfe.py 1.12 rgbecker - Changed to indirect copyright - demos/colors/colortest.py 1.2 rgbecker - Changed to indirect copyright - __init__.py 1.7 rgbecker - Changed to indirect copyright - README 1.2 rgbecker - Changed to indirect copyright -##### 2000/10/24 ##### - license.txt 1.1 rgbecker - Initial separate license file - utils/copyrite.py 1.1 rgbecker - Copyright changes - utils/copyr.txt 1.1 rgbecker - Copyright changes - pdfgen/pdfimages.py 1.2 rgbecker - Fix zlib import bug - pdfgen/pdfimages.py 1.1 aaron_watters - image functionality factored out of canvas.py initial checkin. tests pass - pdfgen/canvas.py 1.54 aaron_watters - sliced out image functionality (oops adding in pdfimages.py next) tests pass -##### 2000/10/23 ##### - platypus/paragraph.py 1.24 rgbecker - Added J Alet's bug case - demos/pythonpoint/pythonpoint.xml 1.9 andy_robinson - Corrected bad end tags and added links to Michael Wiedmann's DTD -##### 2000/10/22 ##### - demos/pythonpoint/pythonpoint.xml 1.8 andy_robinson - Modified the documentation to correct Michael Wiedmann's error reports. -##### 2000/10/21 ##### - lib/sgmlop.dsp 1.2 rgbecker - hyphen.c includes substrings.pl in comment - lib/pyHnj.dsp 1.2 rgbecker - hyphen.c includes substrings.pl in comment - lib/hyphen.c 1.2 rgbecker - hyphen.c includes substrings.pl in comment - lib/_rl_accel.dsp 1.2 rgbecker - hyphen.c includes substrings.pl in comment - lib/README.extensions 1.2 rgbecker - hyphen.c includes substrings.pl in comment -##### 2000/10/19 ##### - pdfbase/pdfdoc.py 1.29 rgbecker - Aaron's latest update from the newslist -##### 2000/10/18 ##### - pdfbase/pdfdoc.py 1.28 aaron_watters - undid last checkin and added an option for a default outline (different fix) - pdfbase/pdfdoc.py 1.27 aaron_watters - moved the outline preprocessing step into the format method (fixes testing error) - pdfgen/canvas.py 1.53 aaron_watters - complete revision of pdfdoc. Not finished (compression missing, testing needed) I got Robin's last change in at the last moment :) - pdfbase/pdfdoc.py 1.26 aaron_watters - complete revision of pdfdoc. Not finished (compression missing, testing needed) I got Robin's last change in at the last moment :) -##### 2000/10/15 ##### - pdfgen/test/testPageMode.py 1.1 andy_robinson - Test of pageMode functions. - pdfgen/canvas.py 1.52 andy_robinson - Added showFullScreen0 - pdfbase/pdfdoc.py 1.25 andy_robinson - Added showFullScreen0 -########### 1.01 Released 2000/10/10 -##### 2000/10/05 ##### - pdfbase/pdfutils.py 1.12 rgbecker - Fix indentation - pdfbase/pdfutils.py 1.11 rgbecker - Changes to make freezing easier - pdfbase/pdfmetrics.py 1.13 rgbecker - Changes to make freezing easier - lib/xmllib.py 1.4 rgbecker - Changes to make freezing easier - lib/utils.py 1.4 rgbecker - Changes to make freezing easier -##### 2000/10/04 ##### - platypus/xpreformatted.py 1.10 rgbecker - Fixed copying bug -##### 2000/10/03 ##### - lib/styles.py 1.7 rgbecker - Code needs a firstLineIndent same as normal lineIndent - platypus/paragraph.py 1.23 rgbecker - Justified XPreformatteds are OK -##### 2000/10/02 ##### - platypus/frames.py 1.9 rgbecker - Fixed the atTop test again - platypus/frames.py 1.8 rgbecker - Fixed the atTop test - platypus/xpreformatted.py 1.9 rgbecker - Added license - platypus/xpreformatted.py 1.8 rgbecker - Splitting fixes. Mostly caused by XPreformatted not doing it right. - platypus/paragraph.py 1.22 rgbecker - Splitting fixes. Mostly caused by XPreformatted not doing it right. -##### 2000/10/01 ##### - platypus/frames.py 1.7 rgbecker - Fixed atTop bugs thanks to the effbot - platypus/xpreformatted.py 1.7 rgbecker - Cleaning up XPreformatted - platypus/xpreformatted.py 1.6 rgbecker - Removed space split and readding -##### 2000/09/27 ##### - pdfbase/pdfmetrics.py 1.12 andy_robinson - more work on metrics and encodings for custom fonts - pdfbase/pdfmetrics.py 1.11 andy_robinson - Begun work on loading new AFM files - lib/colors.py 1.12 rgbecker - Fix for sun compiler - lib/_rl_accel.c 1.3 rgbecker - Fix for sun compiler -##### 2000/09/25 ##### - lib/styles.py 1.6 andy_robinson - added has_key method to stylesheet1 - demos/stdfonts/stdfonts.py 1.6 andy_robinson - Allows you to generate hex, decimal or octal labels -##### 2000/09/08 ##### - pdfbase/pdfdoc.py 1.24 rgbecker - Paul Eddington's unix tell() returns a LongIntType bugfix -##### 2000/09/04 ##### - pdfgen/canvas.py 1.51 rgbecker - Fix spurious comment reference to layout -##### 2000/09/01 ##### - docs/userguide/ch2_graphics.py 1.8 rgbecker - Correct size setting thanks to Werner Louche - pdfgen/textobject.py 1.18 rgbecker - Improved optimisation checks - pdfgen/canvas.py 1.50 rgbecker - Improved optimisation checks - pdfgen/textobject.py 1.17 rgbecker - Fixed potential 'Td' bug -##### 2000/08/31 ##### - pdfgen/canvas.py 1.49 rgbecker - Fix transform optimisation -##### 2000/08/29 ##### - lib/pyHnj.dsp 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/utils.py 1.3 rgbecker - Initial version of D Yoo's pyHnj - lib/pyHnjmodule.c 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/hyphen.mashed 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/hyphen.h 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/hyphen.c 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/hnjalloc.h 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/hnjalloc.c 1.1 rgbecker - Initial version of D Yoo's pyHnj - lib/__BUILD.dsw 1.2 rgbecker - Initial version of D Yoo's pyHnj - lib/Setup.in 1.2 rgbecker - Initial version of D Yoo's pyHnj - lib/Makefile.pre.in 1.3 rgbecker - Initial version of D Yoo's pyHnj - extensions/__init__.py 1.1 rgbecker - Initial version - extensions/README 1.1 rgbecker - Initial version -##### 2000/08/25 ##### - platypus/xpreformatted.py 1.5 rgbecker - Further tweaks of XPreformatted - platypus/paragraph.py 1.21 rgbecker - Further tweaks of XPreformatted - docs/userguide/ch6_tables.py 1.15 rgbecker - Further tweaks of XPreformatted -##### 2000/08/24 ##### - platypus/xpreformatted.py 1.4 rgbecker - XPreformatted fixed empty lines and leading space - platypus/paragraph.py 1.20 rgbecker - XPreformatted fixed empty lines and leading space - lib/utils.py 1.2 rgbecker - Added _className func - docs/userguide/ch6_tables.py 1.14 rgbecker - harder test of XPreformatted - docs/userguide/ch6_tables.py 1.13 rgbecker - Added blank lines to XPreformatted sample - platypus/xpreformatted.py 1.3 rgbecker - Fixes to _dedenter - platypus/flowables.py 1.9 rgbecker - Fixes to _dedenter - docs/userguide/genuserguide.py 1.35 rgbecker - Added Mac Intro - docs/userguide/ch1_intro.py 1.10 rgbecker - Added Mac Intro - docs/images/Python_1.6a2_HINT.gif 1.1 rgbecker - Initial versions for Mac intro - docs/images/Python_1.6a2.gif 1.1 rgbecker - Initial versions for Mac intro - docs/images/Edit_Prefs.gif 1.1 rgbecker - Initial versions for Mac intro - platypus/xpreformatted.py 1.2 rgbecker - XPreformatted first fixes; now runs - platypus/flowables.py 1.8 rgbecker - XPreformatted first fixes; now runs - platypus/__init__.py 1.10 rgbecker - XPreformatted first fixes; now runs - docs/userguide/genuserguide.py 1.34 rgbecker - Preformatted docco added - docs/userguide/ch6_tables.py 1.12 rgbecker - Preformatted docco added - pdfbase/pdfdoc.py 1.23 aaron_watters - change to PDFLiteral to support "lazy string conversions" (to support lazy crosslinks) -##### 2000/08/23 ##### - platypus/xpreformatted.py 1.1 rgbecker - Initial semi working version - platypus/paragraph.py 1.19 rgbecker - Preparing for cleanup -##### 2000/08/20 ##### - pdfgen/canvas.py 1.48 andy_robinson - Changed an argument syntax for clarity - demos/pythonpoint/leftlogo.a85 1.3 andy_robinson - Added as binary file - demos/pythonpoint/leftlogo.a85 1.2 andy_robinson - Removed ASCII file as Mac was corrupting it -##### 2000/08/17 ##### - platypus/paragraph.py 1.18 rgbecker - Various brutal changes to paragraph, canvas and textobject for speed/size - pdfgen/textobject.py 1.16 rgbecker - Various brutal changes to paragraph, canvas and textobject for speed/size - pdfgen/canvas.py 1.47 rgbecker - Various brutal changes to paragraph, canvas and textobject for speed/size - demos/odyssey/odyssey.txt 1.8 rgbecker - Various brutal changes to paragraph, canvas and textobject for speed/size - platypus/paraparser.py 1.27 rgbecker - Changed test formatting -##### 2000/08/16 ##### - pdfbase/pdfmetrics.py 1.10 rgbecker - Start using _rl_accel -##### 2000/08/12 ##### - lib/colors.py 1.11 rgbecker - Fix missing parens -##### 2000/08/11 ##### - lib/colors.py 1.10 rgbecker - added rgb2cmyk - docs/userguide/ch7_custom.py 1.5 rgbecker - Added rotated image derived class example -##### 2000/08/09 ##### - pdfbase/pdfdoc.py 1.22 rgbecker - Andy's Symbol/Zapf font fix -##### 2000/08/03 ##### - pdfbase/pdfutils.py 1.10 rgbecker - Robin's take on Bernhard Herzog's fix to A85 decode - platypus/frames.py 1.6 rgbecker - Changing to packer led positioning - platypus/flowables.py 1.7 rgbecker - Changing to packer led positioning - lib/_rl_accel.c 1.2 rgbecker - Linux/GnuC fixes - lib/Makefile.pre.in 1.2 rgbecker - Linux/GnuC fixes -##### 2000/08/02 ##### - docs/userguide/genuserguide.py 1.33 rgbecker - Table cell updates - docs/userguide/ch6_tables.py 1.11 rgbecker - Table cell updates - docs/images/replogo.gif 1.3 rgbecker - Readded with -kb flag - docs/images/replogo.gif 1.2 rgbecker - Removed bad replogo.gif - platypus/tables.py 1.30 rgbecker - Flowable cell fixes/changes -##### 2000/08/01 ##### - platypus/tables.py 1.29 rgbecker - Fixed flowable valign bottom/top middle maybe - platypus/tables.py 1.28 rgbecker - Additions/Improvements to LINE CMD Splitting - pdfgen/textobject.py 1.15 rgbecker - Converted to using fp_str - pdfgen/canvas.py 1.46 rgbecker - Converted to using fp_str - utils/runtests.py 1.13 rgbecker - Added -time flag - lib/README.extensions 1.1 rgbecker - Initial version - lib/Makefile.pre.in 1.1 rgbecker - Initial version - pdfbase/pdfutils.py 1.9 rgbecker - moved accelerators to lib - pdfbase/pdfmetrics.py 1.9 rgbecker - moved accelerators to lib - pdfbase/_streams.c 1.2 rgbecker - moved accelerators to lib - pdfbase/_pdfmetrics.c 1.3 rgbecker - moved accelerators to lib - pdfbase/Setup.in 1.2 rgbecker - moved accelerators to lib - lib/Setup.in 1.1 rgbecker - Initial version - lib/utils.py 1.1 rgbecker - Initial version - lib/sgmlop.dsp 1.1 rgbecker - Initial version - lib/sgmlop.c 1.1 rgbecker - Initial version - lib/_rl_accel.dsp 1.1 rgbecker - Initial version - lib/_rl_accel.c 1.1 rgbecker - Initial version - lib/__BUILD.dsw 1.1 rgbecker - Initial version - pdfgen/textobject.py 1.14 andy_robinson - Updated old doc string -##### 2000/07/31 ##### - pdfgen/canvas.py 1.45 rgbecker - B Herzog fix to dimension formats -##### 2000/07/30 ##### - platypus/frames.py 1.5 rgbecker - geometry changing attributes now work -##### 2000/07/28 ##### - pdfgen/textobject.py 1.13 rgbecker - Bernhard herzog inspired fixes - pdfgen/canvas.py 1.44 rgbecker - Bernhard herzog inspired fixes -##### 2000/07/26 ##### - pdfbase/pdfmetrics.py 1.8 rgbecker - Accelerator rearrangements - pdfbase/_pdfmetrics.c 1.2 rgbecker - Accelerator rearrangements - lib/logger.py 1.1 rgbecker - Initial version -##### 2000/07/25 ##### - docs/userguide/ch3_pdffeatures.py 1.6 rgbecker - Bruce Eckel spotted these typos. - docs/userguide/ch2_graphics.py 1.7 rgbecker - Bruce Eckel spotted these typos. - docs/userguide/ch1_intro.py 1.9 rgbecker - Bruce Eckel spotted these typos. -##### 2000/07/20 ##### - platypus/doctemplate.py 1.28 rgbecker - Added pagesize to canvasmaker call - README.pdfgen.txt 1.8 rgbecker - Release 1.00 Administration - README 1.1 rgbecker - Release 1.00 Administration -########### 1.00 Released 2000/07/20 -demos/pythonpoint/pythonpoint.py - 1.25 2000/07/15 21:13:29 Minor additions to styles etc. andy_robinson -docs/userguide/app_demos.py - 1.6 2000/07/18 13:27:47 fixed typos. dinu_gherman - 1.5 2000/07/18 10:03:45 fixed typos, minimal changes to py2pdf. dinu_gherman -docs/userguide/ch1_intro.py - 1.8 2000/07/18 09:58:11 fixed typos. dinu_gherman -docs/userguide/ch2_graphics.py - 1.6 2000/07/18 09:59:25 fixed typos. dinu_gherman -docs/userguide/ch3_pdffeatures.py - 1.5 2000/07/18 10:00:43 fixed typos. dinu_gherman -docs/userguide/ch4_platypus_concepts.py - 1.7 2000/07/18 10:02:06 fixed typos. dinu_gherman -docs/userguide/ch5_paragraphs.py - 1.5 2000/07/18 13:24:38 fixed typos. dinu_gherman -docs/userguide/ch5_tables.py - 1.2 2000/07/16 14:01:44 ch5_tables.py was a hangover and should have been deleted before. Note to AR if and when we do this again never give the chapters numbers as that is extremely confusing. rgbecker -docs/userguide/ch6_tables.py - 1.10 2000/07/18 13:25:38 fixed typos. dinu_gherman - 1.9 2000/07/14 14:07:48 Image probs again rgbecker -docs/userguide/ch7_custom.py - 1.4 2000/07/18 13:26:17 fixed typos. dinu_gherman - 1.3 2000/07/16 14:07:56 most_chapters.py was also irrelevant rgbecker -docs/userguide/genuserguide.py - 1.32 2000/07/18 13:29:19 removed header dashes. dinu_gherman -docs/userguide/lj8100.jpg - 1.2 2000/07/14 14:07:48 Image probs again rgbecker -docs/userguide/most_chapters.py - 1.6 2000/07/16 14:07:56 most_chapters.py was also irrelevant rgbecker - 1.5 2000/07/16 14:01:44 ch5_tables.py was a hangover and should have been deleted before. Note to AR if and when we do this again never give the chapters numbers as that is extremely confusing. rgbecker -lib/xmllib.py - 1.3 2000/07/18 09:31:53 Uncommented methods in fast parser rgbecker -pdfbase/Setup.in - 1.1 2000/07/19 19:06:39 Added _pdfmetrics.c rgbecker -pdfbase/_pdfmetrics.c - 1.1 2000/07/19 19:06:39 Added _pdfmetrics.c rgbecker -pdfbase/pdfmetrics.py - 1.7 2000/07/19 19:06:39 Added _pdfmetrics.c rgbecker -platypus/tables.py - 1.27 2000/07/20 13:32:33 Started debugging Table split rgbecker -utils/daily.py - 1.37 2000/07/14 14:27:26 Wasn't doing docs for releases rgbecker -utils/runtests.py - 1.12 2000/07/19 13:44:54 Added -prof option rgbecker -########### 0.95 Released 2000/07/14 -demos/py2pdf/demo.py - 1.5 2000/06/27 08:58:19 Try to ensure test file removal rgbecker -demos/py2pdf/py2pdf.py - 1.10 2000/06/28 11:12:40 Stop auto testing rgbecker -demos/pythonpoint/pythonpoint.py - 1.24 2000/07/12 14:23:12 Table argument order changed rgbecker - 1.23 2000/07/12 06:33:43 Added Speaker Notes facility andy_robinson - 1.22 2000/07/10 15:25:47 Added tables to PythonPoint andy_robinson -demos/pythonpoint/pythonpoint.xml - 1.7 2000/07/12 06:33:43 Added Speaker Notes facility andy_robinson - 1.6 2000/07/10 15:25:47 Added tables to PythonPoint andy_robinson -demos/pythonpoint/stdparser.py - 1.10 2000/07/11 15:00:39 Added end_spacer rgbecker - 1.9 2000/07/10 15:25:47 Added tables to PythonPoint andy_robinson -lib/sequencer.py - 1.8 2000/07/10 11:58:35 Pre-incrementing bug fixed andy_robinson - 1.7 2000/07/08 07:11:21 Changed to pre-increment andy_robinson - 1.6 2000/06/21 19:46:43 Added Roman formatters rgbecker -pdfbase/_streams.c - 1.1 2000/07/05 12:20:27 Ascii85 fixes/additions rgbecker -pdfbase/pdfdoc.py - 1.21 2000/06/26 15:58:22 Simple fix to widths problem rgbecker - 1.20 2000/06/23 17:51:22 /Producer (ReportLab http://www.reportlab.com) in document aaron_watters -pdfbase/pdfmetrics.py - 1.6 2000/06/26 15:58:22 Simple fix to widths problem rgbecker -pdfbase/pdfutils.py - 1.8 2000/07/05 12:20:27 Ascii85 fixes/additions rgbecker - 1.7 2000/06/30 15:29:59 Allow for non-caching of images rgbecker -pdfgen/canvas.py - 1.43 2000/06/30 15:27:55 Allow for non-caching of images rgbecker - 1.42 2000/06/26 15:58:22 Simple fix to widths problem rgbecker -pdfgen/pathobject.py - 1.6 2000/07/12 16:07:09 fixed nameerror and path.circle bug aaron_watters -platypus/__init__.py - 1.9 2000/07/13 11:41:00 Added KeepTogether rgbecker - 1.8 2000/06/27 10:07:55 Added CondPageBreak rgbecker - 1.7 2000/06/21 12:27:42 remove UserDocTemplate, but add Andy's hook methods rgbecker -platypus/doctemplate.py - 1.27 2000/07/10 11:58:35 Pre-incrementing bug fixed andy_robinson - 1.26 2000/07/07 16:21:12 Cosmetics rgbecker - 1.25 2000/07/06 12:40:37 Push canvas into flowables during wrap/split rgbecker - 1.24 2000/07/05 12:22:21 Force _calc in SimpleDocTemplae.build rgbecker - 1.23 2000/07/03 15:39:51 Documentation fixes rgbecker - 1.22 2000/06/28 14:52:43 Documentation changes rgbecker - 1.21 2000/06/26 15:58:22 Simple fix to widths problem rgbecker - 1.20 2000/06/21 12:27:42 remove UserDocTemplate, but add Andy's hook methods rgbecker - 1.19 2000/06/20 21:56:17 re-synching after sourceforge went weird andy_robinson -platypus/flowables.py - 1.6 2000/07/13 11:42:10 removed debug prints rgbecker - 1.5 2000/07/13 11:41:00 Added KeepTogether rgbecker - 1.4 2000/06/27 10:07:55 Added CondPageBreak rgbecker -platypus/frames.py - 1.4 2000/07/07 16:21:12 Cosmetics rgbecker - 1.3 2000/07/06 12:40:38 Push canvas into flowables during wrap/split rgbecker -platypus/paragraph.py - 1.17 2000/07/14 10:29:50 The Paragraph.split method was wrongly assuming that the firstLineIndent should reset to zero. It should always reset to leftIndent! rgbecker - 1.16 2000/07/03 15:39:51 Documentation fixes rgbecker - 1.15 2000/06/23 13:13:54 Fixes to splitting code rgbecker -platypus/paraparser.py - 1.26 2000/07/10 23:53:46 Changed base of seqdefault tag to 0 andy_robinson - 1.25 2000/07/04 10:50:33 Sequencer fixes rgbecker -platypus/tables.py - 1.26 2000/07/12 15:36:56 Allow automatic leading in FONT command rgbecker - 1.25 2000/07/12 15:26:46 INNERGRID was dumb rgbecker - 1.24 2000/07/12 15:25:42 INNERGRID was dumb rgbecker - 1.23 2000/07/12 15:18:16 Leading changes fixed rgbecker - 1.22 2000/07/12 14:23:12 Table argument order changed rgbecker - 1.21 2000/07/12 09:05:17 Fixed tuple size bug rgbecker - 1.20 2000/07/11 14:29:45 Table splitting start rgbecker - 1.19 2000/07/10 15:25:47 Added tables to PythonPoint andy_robinson - 1.18 2000/07/08 15:30:04 Cosmetics and error testing rgbecker - 1.17 2000/07/07 16:22:10 Fix auto hieght stuff rgbecker - 1.16 2000/07/07 10:23:36 First attempt at VALIGN rgbecker - 1.15 2000/07/06 14:05:55 Adjusted doc string rgbecker - 1.14 2000/07/06 12:41:47 First try at auto sizing rgbecker - 1.13 2000/06/29 17:55:19 support explicit \n line splitting in cells aaron_watters -platypus/test/testplatypus.py - 1.18 2000/07/12 14:23:12 Table argument order changed rgbecker -platypus/test/testtables.py - 1.11 2000/07/12 14:23:12 Table argument order changed rgbecker -utils/README - 1.2 2000/07/13 14:55:30 Added detailed release rgbecker -########### 0.94 Released 2000/06/20 -demos/gadflypaper/gfe.py - 1.11 2000/06/01 16:27:56 pageSize is wrong at present rgbecker - 1.10 2000/06/01 15:23:06 Platypus re-organisation rgbecker -demos/odyssey/dodyssey.py - 1.7 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.6 2000/06/01 09:41:11 test filename case fix rgbecker -demos/odyssey/fodyssey.py - 1.13 2000/06/01 16:27:56 pageSize is wrong at present rgbecker - 1.12 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.11 2000/06/01 09:41:12 test filename case fix rgbecker -demos/odyssey/odyssey.py - 1.8 2000/06/01 09:41:12 test filename case fix rgbecker -demos/odyssey/odyssey.txt - 1.7 2000/05/31 10:12:45 xml tag added rgbecker -demos/py2pdf/demo.py - 1.4 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson -demos/py2pdf/py2pdf.py - 1.9 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson -demos/pythonpoint/pythonpoint.py - 1.21 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.20 2000/05/23 14:06:45 Preformatted objects now know how to split themselves. andy_robinson -demos/pythonpoint/stdparser.py - 1.8 2000/06/01 15:23:06 Platypus re-organisation rgbecker -demos/pythonpoint/styles_horrible.py - 1.5 2000/06/01 15:23:06 Platypus re-organisation rgbecker -demos/pythonpoint/styles_modern.py - 1.6 2000/06/01 15:23:06 Platypus re-organisation rgbecker -docs/00readme.txt - 1.1 2000/06/05 16:38:54 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:38:54 initial import andy_robinson -docs/images/replogo.a85 - 1.1 2000/06/05 16:39:04 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:39:04 initial import andy_robinson -docs/images/replogo.gif - 1.1 2000/06/05 16:39:04 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:39:04 initial import andy_robinson -docs/reference/build.bat - 1.4 2000/06/19 23:52:31 rltemplate now simple, based on UserDocTemplate andy_robinson - 1.3 2000/06/17 07:46:45 Small text changes andy_robinson - 1.2 2000/06/12 11:13:09 Added sequencer tags to paragraph parser andy_robinson - 1.1 2000/06/07 13:39:22 Added some text to the first page of reference, and a build batch file andy_robinson -docs/reference/reference.yml - 1.8 2000/06/19 23:52:31 rltemplate now simple, based on UserDocTemplate andy_robinson - 1.7 2000/06/17 07:46:45 Small text changes andy_robinson - 1.6 2000/06/14 21:22:52 Added docs for library andy_robinson - 1.5 2000/06/12 11:26:34 Numbered list added andy_robinson - 1.4 2000/06/12 11:13:09 Added sequencer tags to paragraph parser andy_robinson - 1.3 2000/06/09 01:44:24 added automatic generation for pathobject and textobject modules. aaron_watters - 1.2 2000/06/07 13:39:22 Added some text to the first page of reference, and a build batch file andy_robinson - 1.1 2000/06/05 16:39:04 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:39:04 initial import andy_robinson -docs/tools/codegrab.py - 1.3 2000/06/20 11:35:08 Python 1.5.1 compatibility fixes rgbecker - 1.2 2000/06/14 21:22:52 Added docs for library andy_robinson - 1.1 2000/06/05 16:38:57 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:38:57 initial import andy_robinson -docs/tools/rltemplate.py - 1.2 2000/06/19 23:52:31 rltemplate now simple, based on UserDocTemplate andy_robinson - 1.1 2000/06/17 07:48:41 Added a separate rltemplate.py file to contain our standard document template. andy_robinson -docs/tools/stylesheet.py - 1.2 2000/06/14 21:22:52 Added docs for library andy_robinson - 1.1 2000/06/13 20:03:30 Docs is now a package; stylesheet separated out andy_robinson -docs/tools/yaml.py - 1.5 2000/06/20 11:34:47 Python 1.5.1 compatibility fixes rgbecker - 1.4 2000/06/19 15:54:21 Changes to imports again sigh mumble rgbecker - 1.3 2000/06/14 21:22:52 Added docs for library andy_robinson - 1.2 2000/06/13 20:03:30 Docs is now a package; stylesheet separated out andy_robinson - 1.1 2000/06/05 16:38:56 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:38:56 initial import andy_robinson -docs/tools/yaml2pdf.py - 1.6 2000/06/19 15:54:21 Changes to imports again sigh mumble rgbecker - 1.5 2000/06/17 07:47:20 Factored out rltemplate andy_robinson - 1.4 2000/06/13 20:03:30 Docs is now a package; stylesheet separated out andy_robinson - 1.3 2000/06/12 11:26:34 Numbered list added andy_robinson - 1.2 2000/06/07 21:08:12 Change to style for definition lists andy_robinson - 1.1 2000/06/05 16:38:55 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:38:55 initial import andy_robinson -docs/userguide/examples.py - 1.3 2000/06/19 21:13:02 2nd try. more text aaron_watters - 1.2 2000/06/17 06:54:54 Example had a reference to layout.py, removed andy_robinson - 1.1 2000/06/17 02:59:05 initial checkin. examples file for userguide generation aaron_watters -docs/userguide/genuserguide.py - 1.2 2000/06/19 21:13:02 2nd try. more text aaron_watters - 1.1 2000/06/17 02:57:56 initial checkin. user guide generation framework. aaron_watters -docs/userguide/platdemos.py - 1.1 2000/06/05 16:39:04 Initial revision andy_robinson - 1.1.1.1 2000/06/05 16:39:04 initial import andy_robinson -lib/colors.py - 1.9 2000/06/14 21:17:30 Some relative imports fixed andy_robinson - 1.8 2000/05/26 09:43:44 stringToColor-->toColor rgbecker -lib/pagesizes.py - 1.4 2000/06/14 21:17:30 Some relative imports fixed andy_robinson -lib/sequencer.py - 1.5 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson - 1.4 2000/06/12 11:27:17 Added Sequencer and associated XML tags andy_robinson - 1.3 2000/06/11 21:34:01 Largely complete class for numbering lists, figures and chapters andy_robinson - 1.2 2000/06/09 16:18:19 Doc strings, sequencer andy_robinson - 1.1 2000/06/01 15:23:06 Platypus re-organisation rgbecker -pdfbase/pdfdoc.py - 1.19 2000/06/01 09:44:26 SaveToFile: only close the file if we opened it. Aggregated from types imports to module level. rgbecker -pdfgen/canvas.py - 1.41 2000/06/09 16:18:19 Doc strings, sequencer andy_robinson - 1.40 2000/06/09 01:45:22 Lots of documentation additions and changes. aaron_watters - 1.39 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.38 2000/05/26 09:44:40 generalised colors slightly rgbecker - 1.37 2000/05/23 14:06:45 Preformatted objects now know how to split themselves. andy_robinson -pdfgen/pathobject.py - 1.5 2000/06/09 08:17:43 Full qualified a local import in pdfgen package andy_robinson -platypus/__init__.py - 1.6 2000/06/19 23:51:23 Added UserDocTemplate class, and paragraph.getPlainText() andy_robinson - 1.5 2000/06/01 15:23:06 Platypus re-organisation rgbecker -platypus/doctemplate.py - 1.18 2000/06/19 23:51:23 Added UserDocTemplate class, and paragraph.getPlainText() andy_robinson - 1.17 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson - 1.16 2000/06/16 13:49:20 new build parameters to allow alternate filename and canvas implementation (in order to support slideshow summary mode, for example, or embedding one document in another). aaron_watters - 1.15 2000/06/13 13:03:31 more documentation changes aaron_watters - 1.14 2000/06/01 16:27:56 pageSize is wrong at present rgbecker - 1.13 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.12 2000/05/26 10:27:37 Fixed infinite recursion bug rgbecker -platypus/flowables.py - 1.3 2000/06/13 13:03:31 more documentation changes aaron_watters - 1.2 2000/06/01 16:27:56 pageSize is wrong at present rgbecker - 1.1 2000/06/01 15:23:06 Platypus re-organisation rgbecker -platypus/frames.py - 1.2 2000/06/13 13:03:31 more documentation changes aaron_watters - 1.1 2000/06/01 15:23:06 Platypus re-organisation rgbecker -platypus/layout.py - 1.31 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.30 2000/05/23 14:08:02 Preformatted objects now splitting andy_robinson -platypus/paragraph.py - 1.14 2000/06/19 23:51:23 Added UserDocTemplate class, and paragraph.getPlainText() andy_robinson - 1.13 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson - 1.12 2000/06/13 13:03:31 more documentation changes aaron_watters - 1.11 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.10 2000/05/31 10:12:20 xml tag added rgbecker -platypus/paraparser.py - 1.24 2000/06/19 11:14:03 Global sequencer put in the 'story builder'. andy_robinson - 1.23 2000/06/13 04:11:49 noted replication of XML markup comment between paraparser.py and paragraph.py aaron_watters - 1.22 2000/06/12 11:27:17 Added Sequencer and associated XML tags andy_robinson - 1.21 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.20 2000/05/31 10:12:20 xml tag added rgbecker - 1.19 2000/05/26 09:49:23 Color fixes; thanks to J Alet rgbecker - 1.18 2000/05/20 15:36:42 Removed 1.5.2-style getattr call andy_robinson -platypus/tables.py - 1.12 2000/06/13 13:03:31 more documentation changes aaron_watters - 1.11 2000/06/01 15:23:06 Platypus re-organisation rgbecker - 1.10 2000/05/26 09:49:23 Color fixes; thanks to J Alet rgbecker -platypus/test/testplatypus.py - 1.17 2000/06/01 15:23:06 Platypus re-organisation rgbecker -platypus/test/testtables.py - 1.10 2000/06/01 16:27:56 pageSize is wrong at present rgbecker - 1.9 2000/06/01 15:23:06 Platypus re-organisation rgbecker diff --git a/bin/reportlab/demos/colors/colortest.py b/bin/reportlab/demos/colors/colortest.py deleted file mode 100644 index 7845c2cd620..00000000000 --- a/bin/reportlab/demos/colors/colortest.py +++ /dev/null @@ -1,105 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/colors/colortest.py -import reportlab.pdfgen.canvas -from reportlab.lib import colors -from reportlab.lib.units import inch - - -def run(): - c = reportlab.pdfgen.canvas.Canvas('colortest.pdf') - - #do a test of CMYK interspersed with RGB - - #first do RGB values - framePage(c, 'Color Demo - RGB Space and CMYK spaces interspersed' ) - - y = 700 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'cyan') - c.setFillColorCMYK(1,0,0,0) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'red') - c.setFillColorRGB(1,0,0) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'magenta') - c.setFillColorCMYK(0,1,0,0) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'green') - c.setFillColorRGB(0,1,0) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'yellow') - c.setFillColorCMYK(0,0,1,0) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'blue') - c.setFillColorRGB(0,0,1) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - c.setFillColorRGB(0,0,0) - c.drawString(100, y, 'black') - c.setFillColorCMYK(0,0,0,1) - c.rect(200, y, 300, 30, fill=1) - y = y - 40 - - - c.showPage() - - #do all named colors - framePage(c, 'Color Demo - RGB Space - page %d' % c.getPageNumber()) - - all_colors = 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.') - y = 700 - for (name, color) in all_colors: - c.setFillColor(colors.black) - c.drawString(100, y, name) - c.setFillColor(color) - c.rect(200, y-10, 300, 30, fill=1) - y = y - 40 - if y < 100: - c.showPage() - framePage(c, 'Color Demo - RGB Space - page %d' % c.getPageNumber()) - y = 700 - - - - - c.save() - -def framePage(canvas, title): - canvas.setFont('Times-BoldItalic',20) - canvas.drawString(inch, 10.5 * inch, title) - - canvas.setFont('Times-Roman',10) - canvas.drawCentredString(4.135 * inch, 0.75 * inch, - 'Page %d' % canvas.getPageNumber()) - - #draw a border - canvas.setStrokeColorRGB(1,0,0) - canvas.setLineWidth(5) - canvas.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch) - #reset carefully afterwards - canvas.setLineWidth(1) - canvas.setStrokeColorRGB(0,0,0) - -if __name__ == '__main__': - run() diff --git a/bin/reportlab/demos/gadflypaper/00readme.txt b/bin/reportlab/demos/gadflypaper/00readme.txt deleted file mode 100644 index f50a9966846..00000000000 --- a/bin/reportlab/demos/gadflypaper/00readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -This is Aaron Watters' first script; -it renders his paper for IPC8 into -PDF. A fascinating read, as well. \ No newline at end of file diff --git a/bin/reportlab/demos/gadflypaper/gfe.py b/bin/reportlab/demos/gadflypaper/gfe.py deleted file mode 100644 index dfc1259d5a8..00000000000 --- a/bin/reportlab/demos/gadflypaper/gfe.py +++ /dev/null @@ -1,903 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/gadflypaper/gfe.py -__version__=''' $Id: gfe.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__='' - -#REPORTLAB_TEST_SCRIPT -import sys -from reportlab.platypus import * -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.rl_config import defaultPageSize -PAGE_HEIGHT=defaultPageSize[1] - -styles = getSampleStyleSheet() - -Title = "Integrating Diverse Data Sources with Gadfly 2" - -Author = "Aaron Watters" - -URL = "http://www.chordate.com/" - -email = "arw@ifu.net" - -Abstract = """This paper describes the primative methods underlying the implementation -of SQL query evaluation in Gadfly 2, a database management system implemented -in Python [Van Rossum]. The major design goals behind -the architecture described here are to simplify the implementation -and to permit flexible and efficient extensions to the gadfly -engine. Using this architecture and its interfaces programmers -can add functionality to the engine such as alternative disk based -indexed table implementations, dynamic interfaces to remote data -bases or or other data sources, and user defined computations.""" - -from reportlab.lib.units import inch - -pageinfo = "%s / %s / %s" % (Author, email, Title) - -def myFirstPage(canvas, doc): - canvas.saveState() - #canvas.setStrokeColorRGB(1,0,0) - #canvas.setLineWidth(5) - #canvas.line(66,72,66,PAGE_HEIGHT-72) - canvas.setFont('Times-Bold',16) - canvas.drawString(108, PAGE_HEIGHT-108, Title) - canvas.setFont('Times-Roman',9) - canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo) - canvas.restoreState() - -def myLaterPages(canvas, doc): - #canvas.drawImage("snkanim.gif", 36, 36) - canvas.saveState() - #canvas.setStrokeColorRGB(1,0,0) - #canvas.setLineWidth(5) - #canvas.line(66,72,66,PAGE_HEIGHT-72) - canvas.setFont('Times-Roman',9) - canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo)) - canvas.restoreState() - -def go(): - Elements.insert(0,Spacer(0,inch)) - doc = SimpleDocTemplate('gfe.pdf') - doc.build(Elements,onFirstPage=myFirstPage, onLaterPages=myLaterPages) - -Elements = [] - -HeaderStyle = styles["Heading1"] # XXXX - -def header(txt, style=HeaderStyle, klass=Paragraph, sep=0.3): - s = Spacer(0.2*inch, sep*inch) - Elements.append(s) - para = klass(txt, style) - Elements.append(para) - -ParaStyle = styles["Normal"] - -def p(txt): - return header(txt, style=ParaStyle, sep=0.1) - -#pre = p # XXX - -PreStyle = styles["Code"] - -def pre(txt): - s = Spacer(0.1*inch, 0.1*inch) - Elements.append(s) - p = Preformatted(txt, PreStyle) - Elements.append(p) - -#header(Title, sep=0.1. style=ParaStyle) -header(Author, sep=0.1, style=ParaStyle) -header(URL, sep=0.1, style=ParaStyle) -header(email, sep=0.1, style=ParaStyle) -header("ABSTRACT") -p(Abstract) - -header("Backgrounder") - -p("""\ -The term "database" usually refers to a persistent -collection of data. Data is persistent if it continues -to exist whether or not it is associated with a running -process on the computer, or even if the computer is -shut down and restarted at some future time. Database -management systems provide support for constructing databases, -maintaining databases, and extracting information from databases.""") -p("""\ -Relational databases manipulate and store persistent -table structures called relations, such as the following -three tables""") - -pre("""\ - -- drinkers who frequent bars (this is a comment) - select * from frequents - - DRINKER | PERWEEK | BAR - ============================ - adam | 1 | lolas - woody | 5 | cheers - sam | 5 | cheers - norm | 3 | cheers - wilt | 2 | joes - norm | 1 | joes - lola | 6 | lolas - norm | 2 | lolas - woody | 1 | lolas - pierre | 0 | frankies -) -""") -pre("""\ - -- drinkers who like beers - select * from likes - - DRINKER | PERDAY | BEER - =============================== - adam | 2 | bud - wilt | 1 | rollingrock - sam | 2 | bud - norm | 3 | rollingrock - norm | 2 | bud - nan | 1 | sierranevada - woody | 2 | pabst - lola | 5 | mickies - -""") -pre("""\ - -- beers served from bars - select * from serves - - BAR | QUANTITY | BEER - ================================= - cheers | 500 | bud - cheers | 255 | samadams - joes | 217 | bud - joes | 13 | samadams - joes | 2222 | mickies - lolas | 1515 | mickies - lolas | 333 | pabst - winkos | 432 | rollingrock - frankies | 5 | snafu -""") -p(""" -The relational model for database structures makes -the simplifying assumption that all data in a database -can be represented in simple table structures -such as these. Although this assumption seems extreme -it provides a good foundation for defining solid and -well defined database management systems and some -of the most successful software companies in the -world, such as Oracle, Sybase, IBM, and Microsoft, -have marketed database management systems based on -the relational model quite successfully. -""") -p(""" -SQL stands for Structured Query Language. -The SQL language defines industry standard -mechanisms for creating, querying, and modified -relational tables. Several years ago SQL was one -of many Relational Database Management System -(RDBMS) query languages in use, and many would -argue not the best on. Now, largely due -to standardization efforts and the -backing of IBM, SQL is THE standard way to talk -to database systems. -""") -p(""" -There are many advantages SQL offers over other -database query languages and alternative paradigms -at this time (please see [O'Neill] or [Korth and Silberschatz] -for more extensive discussions and comparisons between the -SQL/relational approach and others.) -""") -p(""" -The chief advantage over all contenders at this time -is that SQL and the relational model are now widely -used as interfaces and back end data stores to many -different products with different performance characteristics, -user interfaces, and other qualities: Oracle, Sybase, -Ingres, SQL Server, Access, Outlook, -Excel, IBM DB2, Paradox, MySQL, MSQL, POSTgres, and many -others. For this reason a program designed to use -an SQL database as its data storage mechanism can -easily be ported from one SQL data manager to another, -possibly on different platforms. In fact the same -program can seamlessly use several backends and/or -import/export data between different data base platforms -with trivial ease. -No other paradigm offers such flexibility at the moment. -""") -p(""" -Another advantage which is not as immediately -obvious is that the relational model and the SQL -query language are easily understood by semi-technical -and non-technical professionals, such as business -people and accountants. Human resources managers -who would be terrified by an object model diagram -or a snippet of code that resembles a conventional -programming language will frequently feel quite at -ease with a relational model which resembles the -sort of tabular data they deal with on paper in -reports and forms on a daily basis. With a little training the -same HR managers may be able to translate the request -"Who are the drinkers who like bud and frequent cheers?" -into the SQL query -""") -pre(""" - select drinker - from frequents - where bar='cheers' - and drinker in ( - select drinker - from likes - where beer='bud') -""") -p(""" -(or at least they have some hope of understanding -the query once it is written by a technical person -or generated by a GUI interface tool). Thus the use -of SQL and the relational model enables communication -between different communities which must understand -and interact with stored information. In contrast -many other approaches cannot be understood easily -by people without extensive programming experience. -""") -p(""" -Furthermore the declarative nature of SQL -lends itself to automatic query optimization, -and engines such as Gadfly can automatically translate a user query -into an optimized query plan which takes -advantage of available indices and other data characteristics. -In contrast more navigational techniques require the application -program itself to optimize the accesses to the database and -explicitly make use of indices. -""") - -# HACK -Elements.append(PageBreak()) - -p(""" -While it must be admitted that there are application -domains such as computer aided engineering design where -the relational model is unnatural, it is also important -to recognize that for many application domains (such -as scheduling, accounting, inventory, finance, personal -information management, electronic mail) the relational -model is a very natural fit and the SQL query language -make most accesses to the underlying data (even sophisticated -ones) straightforward. """) - -p("""For an example of a moderately -sophisticated query using the tables given above, -the following query lists the drinkers who frequent lolas bar -and like at least two beers not served by lolas -""") - -if 0: - go() - sys.exit(1) - -pre(""" - select f.drinker - from frequents f, likes l - where f.drinker=l.drinker and f.bar='lolas' - and l.beer not in - (select beer from serves where bar='lolas') - group by f.drinker - having count(distinct beer)>=2 -""") -p(""" -yielding the result -""") -pre(""" - DRINKER - ======= - norm -""") -p(""" -Experience shows that queries of this sort are actually -quite common in many applications, and are often much more -difficult to formulate using some navigational database -organizations, such as some "object oriented" database -paradigms. -""") -p(""" -Certainly, -SQL does not provide all you need to interact with -databases -- in order to do "real work" with SQL you -need to use SQL and at least one other language -(such as C, Pascal, C++, Perl, Python, TCL, Visual Basic -or others) to do work (such as readable formatting a report -from raw data) that SQL was not designed to do. -""") - -header("Why Gadfly 1?") - -p("""Gadfly 1.0 is an SQL based relational database implementation -implemented entirely in the Python programming language, with -optional fast data structure accellerators implemented in the -C programming language. Gadfly is relatively small, highly portable, -very easy to use (especially for programmers with previous experience -with SQL databases such as MS Access or Oracle), and reasonably -fast (especially when the kjbuckets C accellerators are used). -For moderate sized problems Gadfly offers a fairly complete -set of features such as transaction semantics, failure recovery, -and a TCP/IP based client/server mode (Please see [Gadfly] for -detailed discussion).""") - - -header("Why Gadfly 2?") - -p("""Gadfly 1.0 also has significant limitations. An active Gadfly -1.0 database keeps all data in (virtual) memory, and hence a Gadfly -1.0 database is limited in size to available virtual memory. Important -features such as date/time/interval operations, regular expression -matching and other standard SQL features are not implemented in -Gadfly 1.0. The optimizer and the query evaluator perform optimizations -using properties of the equality predicate but do not optimize -using properties of inequalities such as BETWEEN or less-than. -It is possible to add "extension views" to a Gadfly -1.0 database, but the mechanism is somewhat clumsy and indices -over extension views are not well supported. The features of Gadfly -2.0 discussed here attempt to address these deficiencies by providing -a uniform extension model that permits addition of alternate table, -function, and predicate implementations.""") - -p("""Other deficiencies, such as missing constructs like "ALTER -TABLE" and the lack of outer joins and NULL values are not -addressed here, although they may be addressed in Gadfly 2.0 or -a later release. This paper also does not intend to explain -the complete operations of the internals; it is intended to provide -at least enough information to understand the basic mechanisms -for extending gadfly.""") - - - - -p("""Some concepts and definitions provided next help with the description -of the gadfly interfaces. [Note: due to the terseness of this -format the ensuing is not a highly formal presentation, but attempts -to approach precision where precision is important.]""") - -header("The semilattice of substitutions") - -p("""Underlying the gadfly implementation are the basic concepts -associated with substitutions. A substitution is a mapping -of attribute names to values (implemented in gadfly using kjbuckets.kjDict -objects). Here an attribute refers to some sort of "descriptive -variable", such as NAME and a value is an assignment for that variable, -like "Dave Ascher". In Gadfly a table is implemented as a sequence -of substitutions, and substitutions are used in many other ways as well. -""") -p(""" -For example consider the substitutions""") - -pre(""" - A = [DRINKER=>'sam'] - B = [DRINKER=>'sam', BAR=>'cheers'] - C = [DRINKER=>'woody', BEER=>'bud'] - D = [DRINKER=>'sam', BEER=>'mickies'] - E = [DRINKER=>'sam', BAR=>'cheers', BEER=>'mickies'] - F = [DRINKER=>'sam', BEER=>'mickies'] - G = [BEER=>'bud', BAR=>'lolas'] - H = [] # the empty substitution - I = [BAR=>'cheers', CAPACITY=>300]""") - -p("""A trivial but important observation is that since substitutions -are mappings, no attribute can assume more than one value in a -substitution. In the operations described below whenever an operator -"tries" to assign more than one value to an attribute -the operator yields an "overdefined" or "inconsistent" -result.""") - -header("Information Semi-order:") - -p("""Substitution B is said to be -more informative than A because B agrees with all assignments -in A (in addition to providing more information as well). Similarly -we say that E is more informative than A, B, D, F. and H but E -is not more informative than the others since, for example G disagrees -with E on the value assigned to the BEER attribute and I provides -additional CAPACITY information not provided in E.""") - -header("Joins and Inconsistency:") - -p("""A join of two substitutions -X and Y is the least informative substitution Z such that Z is -more informative (or equally informative) than both X and Y. For -example B is the join of B with A, E is the join of B with D and""") - -pre(""" - E join I = - [DRINKER=>'sam', BAR=>'cheers', BEER=>'mickies', CAPACITY=>300]""") - -p("""For any two substitutions either (1) they disagree on the value -assigned to some attribute and have no join or (2) they agree -on all common attributes (if there are any) and their join is -the union of all (name, value) assignments in both substitutions. -Written in terms of kjbucket.kjDict operations two kjDicts X and -Y have a join Z = (X+Y) if and only if Z.Clean() is not None. -Two substitutions that have no join are said to be inconsistent. -For example I and G are inconsistent since they disagree on -the value assigned to the BAR attribute and therefore have no -join. The algebra of substitutions with joins technically defines -an abstract algebraic structure called a semilattice.""") - -header("Name space remapping") - -p("""Another primitive operation over substitutions is the remap -operation S2 = S.remap(R) where S is a substitution and R is a -graph of attribute names and S2 is a substitution. This operation -is defined to produce the substitution S2 such that""") - -pre(""" - Name=>Value in S2 if and only if - Name1=>Value in S and Name<=Name1 in R -""") - -p("""or if there is no such substitution S2 the remap value is said -to be overdefined.""") - -p("""For example the remap operation may be used to eliminate attributes -from a substitution. For example""") - -pre(""" - E.remap([DRINKER<=DRINKER, BAR<=BAR]) - = [DRINKER=>'sam', BAR=>'cheers'] -""") - -p("""Illustrating that remapping using the [DRINKER<=DRINKER, -BAR<=BAR] graph eliminates all attributes except DRINKER and -BAR, such as BEER. More generally remap can be used in this way -to implement the classical relational projection operation. (See [Korth and Silberschatz] -for a detailed discussion of the projection operator and other relational -algebra operators such as selection, rename, difference and joins.)""") - -p("""The remap operation can also be used to implement "selection -on attribute equality". For example if we are interested -in the employee names of employees who are their own bosses we -can use the remapping graph""") - -pre(""" - R1 = [NAME<=NAME, NAME<=BOSS] -""") - -p("""and reject substitutions where remapping using R1 is overdefined. -For example""") - -pre(""" - S1 = [NAME=>'joe', BOSS=>'joe'] - S1.remap(R1) = [NAME=>'joe'] - S2 = [NAME=>'fred', BOSS=>'joe'] - S2.remap(R1) is overdefined. -""") - -p("""The last remap is overdefined because the NAME attribute cannot -assume both the values 'fred' and 'joe' in a substitution.""") - -p("""Furthermore, of course, the remap operation can be used to -"rename attributes" or "copy attribute values" -in substitutions. Note below that the missing attribute CAPACITY -in B is effectively ignored in the remapping operation.""") - -pre(""" - B.remap([D<=DRINKER, B<=BAR, B2<=BAR, C<=CAPACITY]) - = [D=>'sam', B=>'cheers', B2=>'cheers'] -""") - -p("""More interestingly, a single remap operation can be used to -perform a combination of renaming, projection, value copying, -and attribute equality selection as one operation. In kjbuckets the remapper -graph is implemented using a kjbuckets.kjGraph and the remap operation -is an intrinsic method of kjbuckets.kjDict objects.""") - -header("Generalized Table Joins and the Evaluator Mainloop""") - -p("""Strictly speaking the Gadfly 2.0 query evaluator only uses -the join and remap operations as its "basic assembly language" --- all other computations, including inequality comparisons and -arithmetic, are implemented externally to the evaluator as "generalized -table joins." """) - -p("""A table is a sequence of substitutions (which in keeping with -SQL semantics may contain redundant entries). The join between -two tables T1 and T2 is the sequence of all possible defined joins -between pairs of elements from the two tables. Procedurally we -might compute the join as""") - -pre(""" - T1JoinT2 = empty - for t1 in T1: - for t2 in T2: - if t1 join t2 is defined: - add t1 join t2 to T1joinT2""") - -p("""In general circumstances this intuitive implementation is a -very inefficient way to compute the join, and Gadfly almost always -uses other methods, particularly since, as described below, a -"generalized table" can have an "infinite" -number of entries.""") - -p("""For an example of a table join consider the EMPLOYEES table -containing""") - -pre(""" - [NAME=>'john', JOB=>'executive'] - [NAME=>'sue', JOB=>'programmer'] - [NAME=>'eric', JOB=>'peon'] - [NAME=>'bill', JOB=>'peon'] -""") - -p("""and the ACTIVITIES table containing""") - -pre(""" - [JOB=>'peon', DOES=>'windows'] - [JOB=>'peon', DOES=>'floors'] - [JOB=>'programmer', DOES=>'coding'] - [JOB=>'secretary', DOES=>'phone']""") - -p("""then the join between EMPLOYEES and ACTIVITIES must containining""") - -pre(""" - [NAME=>'sue', JOB=>'programmer', DOES=>'coding'] - [NAME=>'eric', JOB=>'peon', DOES=>'windows'] - [NAME=>'bill', JOB=>'peon', DOES=>'windows'] - [NAME=>'eric', JOB=>'peon', DOES=>'floors'] - [NAME=>'bill', JOB=>'peon', DOES=>'floors']""") - -p("""A compiled gadfly subquery ultimately appears to the evaluator -as a sequence of generalized tables that must be joined (in combination -with certain remapping operations that are beyond the scope of -this discussion). The Gadfly mainloop proceeds following the very -loose pseudocode:""") - -pre(""" - Subs = [ [] ] # the unary sequence containing "true" - While some table hasn't been chosen yet: - Choose an unchosen table with the least cost join estimate. - Subs = Subs joined with the chosen table - return Subs""") - -p("""[Note that it is a property of the join operation that the -order in which the joins are carried out will not affect the result, -so the greedy strategy of evaluating the "cheapest join next" -will not effect the result. Also note that the treatment of logical -OR and NOT as well as EXIST, IN, UNION, and aggregation and so -forth are not discussed here, even though they do fit into this -approach.]""") - -p("""The actual implementation is a bit more complex than this, -but the above outline may provide some useful intuition. The "cost -estimation" step and the implementation of the join operation -itself are left up to the generalized table object implementation. -A table implementation has the ability to give an "infinite" -cost estimate, which essentially means "don't join me in -yet under any circumstances." """) - -header("Implementing Functions") - -p("""As mentioned above operations such as arithmetic are implemented -using generalized tables. For example the arithmetic Add operation -is implemented in Gadfly internally as an "infinite generalized -table" containing all possible substitutions""") - -pre(""" - ARG0=>a, ARG1=>b, RESULT=>a+b] -""") - -p("""Where a and b are all possible values which can be summed. -Clearly, it is not possible to enumerate this table, but given -a sequence of substitutions with defined values for ARG0 and ARG1 -such as""") - -pre(""" - [ARG0=>1, ARG1=-4] - [ARG0=>2.6, ARG1=50] - [ARG0=>99, ARG1=1] -""") - -p("""it is possible to implement a "join operation" against -this sequence that performs the same augmentation as a join with -the infinite table defined above:""") - -pre(""" - [ARG0=>1, ARG1=-4, RESULT=-3] - [ARG0=>2.6, ARG1=50, RESULT=52.6] - [ARG0=>99, ARG1=1, RESULT=100] -""") - -p("""Furthermore by giving an "infinite estimate" for -all attempts to evaluate the join where ARG0 and ARG1 are not -available the generalized table implementation for the addition -operation can refuse to compute an "infinite join." """) - -p("""More generally all functions f(a,b,c,d) are represented in -gadfly as generalized tables containing all possible relevant -entries""") - -pre(""" - [ARG0=>a, ARG1=>b, ARG2=>c, ARG3=>d, RESULT=>f(a,b,c,d)]""") - -p("""and the join estimation function refuses all attempts to perform -a join unless all the arguments are provided by the input substitution -sequence.""") - -header("Implementing Predicates") - -p("""Similarly to functions, predicates such as less-than and BETWEEN -and LIKE are implemented using the generalized table mechanism. -For example the "x BETWEEN y AND z" predicate is implemented -as a generalized table "containing" all possible""") - -pre(""" - [ARG0=>a, ARG1=>b, ARG2=>c]""") - -p("""where b<a<c. Furthermore joins with this table are not -permitted unless all three arguments are available in the sequence -of input substitutions.""") - -header("Some Gadfly extension interfaces") - -p("""A gadfly database engine may be extended with user defined -functions, predicates, and alternative table and index implementations. -This section snapshots several Gadfly 2.0 interfaces, currently under -development and likely to change before the package is released.""") - -p("""The basic interface for adding functions and predicates (logical tests) -to a gadfly engine are relatively straightforward. For example to add the -ability to match a regular expression within a gadfly query use the -following implementation.""") - -pre(""" - from re import match - - def addrematch(gadflyinstance): - gadflyinstance.add_predicate("rematch", match) -""") -p(""" -Then upon connecting to the database execute -""") -pre(""" - g = gadfly(...) - ... - addrematch(g) -""") -p(""" -In this case the "semijoin operation" associated with the new predicate -"rematch" is automatically generated, and after the add_predicate -binding operation the gadfly instance supports queries such as""") -pre(""" - select drinker, beer - from likes - where rematch('b*', beer) and drinker not in - (select drinker from frequents where rematch('c*', bar)) -""") -p(""" -By embedding the "rematch" operation within the query the SQL -engine can do "more work" for the programmer and reduce or eliminate the -need to process the query result externally to the engine. -""") -p(""" -In a similar manner functions may be added to a gadfly instance,""") -pre(""" - def modulo(x,y): - return x % y - - def addmodulo(gadflyinstance): - gadflyinstance.add_function("modulo", modulo) - - ... - g = gadfly(...) - ... - addmodulo(g) -""") -p(""" -Then after the binding the modulo function can be used whereever -an SQL expression can occur. -""") -p(""" -Adding alternative table implementations to a Gadfly instance -is more interesting and more difficult. An "extension table" implementation -must conform to the following interface:""") - -pre(""" - # get the kjbuckets.kjSet set of attribute names for this table - names = table.attributes() - - # estimate the difficulty of evaluating a join given known attributes - # return None for "impossible" or n>=0 otherwise with larger values - # indicating greater difficulty or expense - estimate = table.estimate(known_attributes) - - # return the join of the rows of the table with - # the list of kjbuckets.kjDict mappings as a list of mappings. - resultmappings = table.join(listofmappings) -""") -p(""" -In this case add the table to a gadfly instance using""") -pre(""" - gadflyinstance.add_table("table_name", table) -""") -p(""" -For example to add a table which automatically queries filenames -in the filesystems of the host computer a gadfly instance could -be augmented with a GLOB table implemented using the standard -library function glob.glob as follows:""") -pre(""" - import kjbuckets - - class GlobTable: - def __init__(self): pass - - def attributes(self): - return kjbuckets.kjSet("PATTERN", "NAME") - - def estimate(self, known_attributes): - if known_attributes.member("PATTERN"): - return 66 # join not too difficult - else: - return None # join is impossible (must have PATTERN) - - def join(self, listofmappings): - from glob import glob - result = [] - for m in listofmappings: - pattern = m["PATTERN"] - for name in glob(pattern): - newmapping = kjbuckets.kjDict(m) - newmapping["NAME"] = name - if newmapping.Clean(): - result.append(newmapping) - return result - - ... - gadfly_instance.add_table("GLOB", GlobTable()) -""") -p(""" -Then one could formulate queries such as "list the files in directories -associated with packages installed by guido" -""") -pre(""" - select g.name as filename - from packages p, glob g - where p.installer = 'guido' and g.pattern=p.root_directory -""") -p(""" -Note that conceptually the GLOB table is an infinite table including -all filenames on the current computer in the "NAME" column, paired with -a potentially infinite number of patterns. -""") -p(""" -More interesting examples would allow queries to remotely access -data served by an HTTP server, or from any other resource. -""") -p(""" -Furthermore an extension table can be augmented with update methods -""") -pre(""" - table.insert_rows(listofmappings) - table.update_rows(oldlist, newlist) - table.delete_rows(oldlist) -""") -p(""" -Note: at present the implementation does not enforce recovery or -transaction semantics for updates to extension tables, although this -may change in the final release. -""") -p(""" -The table implementation is free to provide its own implementations of -indices which take advantage of data provided by the join argument. -""") - -header("Efficiency Notes") - -p("""The following thought experiment attempts to explain why the -Gadfly implementation is surprisingly fast considering that it -is almost entirely implemented in Python (an interpreted programming -language which is not especially fast when compared to alternatives). -Although Gadfly is quite complex, at an abstract level the process -of query evaluation boils down to a series of embedded loops. -Consider the following nested loops:""") - -pre(""" - iterate 1000: - f(...) # fixed cost of outer loop - iterate 10: - g(...) # fixed cost of middle loop - iterate 10: - # the real work (string parse, matrix mul, query eval...) - h(...)""") - -p("""In my experience many computations follow this pattern where -f, g, are complex, dynamic, special purpose and h is simple, general -purpose, static. Some example computations that follow this pattern -include: file massaging (perl), matrix manipulation (python, tcl), -database/cgi page generation, and vector graphics/imaging.""") - -p("""Suppose implementing f, g, h in python is easy but result in -execution times10 times slower than a much harder implementation -in C, choosing arbitrary and debatable numbers assume each function -call consumes 1 tick in C, 5 ticks in java, 10 ticks in python -for a straightforward implementation of each function f, g, and -h. Under these conditions we get the following cost analysis, -eliminating some uninteresting combinations, of implementing the -function f, g, and h in combinations of Python, C and java:""") - -pre(""" -COST | FLANG | GLANG | HLANG -================================== -111000 | C | C | C -115000 | java | C | C -120000 | python | C | C -155000 | java | java | C -210000 | python | python | C -555000 | java | java | java -560000 | python | java | java -610000 | python | python | java -1110000 | python | python | python -""") - -p("""Note that moving only the innermost loop to C (python/python/C) -speeds up the calculation by half an order of magnitude compared -to the python-only implementation and brings the speed to within -a factor of 2 of an implementation done entirely in C.""") - -p("""Although this artificial and contrived thought experiment is -far from conclusive, we may be tempted to draw the conclusion -that generally programmers should focus first on obtaining a working -implementation (because as John Ousterhout is reported to have -said "the biggest performance improvement is the transition -from non-working to working") using the methodology that -is most likely to obtain a working solution the quickest (Python). Only then if the performance -is inadequate should the programmer focus on optimizing -the inner most loops, perhaps moving them to a very efficient -implementation (C). Optimizing the outer loops will buy little -improvement, and should be done later, if ever.""") - -p("""This was precisely the strategy behind the gadfly implementations, -where most of the inner loops are implemented in the kjbuckets -C extension module and the higher level logic is all in Python. -This also explains why gadfly appears to be "slower" -for simple queries over small data sets, but seems to be relatively -"faster" for more complex queries over larger data sets, -since larger queries and data sets take better advantage of the -optimized inner loops.""") - -header("A Gadfly variant for OLAP?") - -p("""In private correspondence Andy Robinson points out that the -basic logical design underlying Gadfly could be adapted to provide -Online Analytical Processing (OLAP) and other forms of data warehousing -and data mining. Since SQL is not particularly well suited for -the kinds of requests common in these domains the higher level -interfaces would require modification, but the underlying logic -of substitutions and name mappings seems to be appropriate.""") - -header("Conclusion") - -p("""The revamped query engine design in Gadfly 2 supports -a flexible and general extension methodology that permits programmers -to extend the gadfly engine to include additional computations -and access to remote data sources. Among other possibilities this -will permit the gadfly engine to make use of disk based indexed -tables and to dynamically retrieve information from remote data -sources (such as an Excel spreadsheet or an Oracle database). -These features will make gadfly a very useful tool for data manipulation -and integration.""") - -header("References") - -p("""[Van Rossum] Van Rossum, Python Reference Manual, Tutorial, and Library Manuals, -please look to http://www.python.org -for the latest versions, downloads and links to printed versions.""") - -p("""[O'Neill] O'Neill, P., Data Base Principles, Programming, Performance, -Morgan Kaufmann Publishers, San Francisco, 1994.""") - -p("""[Korth and Silberschatz] Korth, H. and Silberschatz, A. and Sudarshan, S. -Data Base System Concepts, McGraw-Hill Series in Computer Science, Boston, -1997""") - -p("""[Gadfly]Gadfly: SQL Relational Database in Python, -http://www.chordate.com/kwParsing/gadfly.html""") - -go() \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/00readme.txt b/bin/reportlab/demos/odyssey/00readme.txt deleted file mode 100644 index 95c622ac279..00000000000 --- a/bin/reportlab/demos/odyssey/00readme.txt +++ /dev/null @@ -1,56 +0,0 @@ -This contains a number of benchmarks and demos -based on Homer's Odyssey (which is widely available -in plain, line-oriented text format). There are a large -selection of online books at: - http://classics.mit.edu/ - - -Our distribution ships with just the first chapter -in odyssey.txt. For a more meaningful speed test, -download the full copy from - http://www.reportlab.com/ftp/odyssey.full.zip -or - ftp://ftp.reportlab.com/odyssey.full.zip -and unzip to extract odyssey.full.txt (608kb). - -Benchmark speed depends quite critically -on the presence of our accelerator module, -_rl_accel, which is a C (or Java) extension. -Serious users ought to compile or download this! - -The times quoted are from one machine (Andy Robinson's -home PC, approx 1.2Ghz 128Mb Ram, Win2k in Sep 2003) -in order to give a rough idea of what features cost -what performance. - - -The tests are as follows: - -(1) odyssey.py (produces odyssey.pdf) -This demo takes a large volume of text and prints it -in the simplest way possible. It is a demo of the -basic technique of looping down a page manually and -breaking at the bottom. On my 1.2 Ghz machine this takes -1.91 seconds (124 pages per second) - -(2) fodyssey.py (produces fodyssey.pdf) -This is a 'flowing document' we parse the file and -throw away line breaks to make proper paragraphs. -The Platypus framework renders these. This necessitates -measuring the width of every word in every paragraph -for wrapping purposes. - -This takes 3.27 seconds on the same machine. Paragraph -wrapping basically doubles the work. The text is more -compact with about 50% more words per page. Very roughly, -we can wrap 40 pages of ten-point text per second and save -to PDF. - -(3) dodyssey.py (produced dodyssey.pdf) -This is a slightly fancier version which uses different -page templates (one column for first page in a chapter, -two column for body poages). The additional layout logic -adds about 15%, going up to 3.8 seconds. This is probably -a realistic benchmark for a simple long text document -with a single pass. Documents doing cross-references -and a table of contents might need twice as long. diff --git a/bin/reportlab/demos/odyssey/dodyssey.py b/bin/reportlab/demos/odyssey/dodyssey.py deleted file mode 100644 index edc24354eca..00000000000 --- a/bin/reportlab/demos/odyssey/dodyssey.py +++ /dev/null @@ -1,254 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/dodyssey.py -__version__=''' $Id: dodyssey.py 2856 2006-05-11 09:48:13Z rgbecker $ ''' -__doc__='' - -#REPORTLAB_TEST_SCRIPT -import sys, copy, string, os -from reportlab.platypus import * -_NEW_PARA=os.environ.get('NEW_PARA','0')[0] in ('y','Y','1') -_REDCAP=int(os.environ.get('REDCAP','0')) -_CALLBACK=os.environ.get('CALLBACK','0')[0] in ('y','Y','1') -if _NEW_PARA: - def Paragraph(s,style): - from rlextra.radxml.para import Paragraph as PPPP - return PPPP(s,style) - -from reportlab.lib.units import inch -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY - -import reportlab.rl_config -reportlab.rl_config.invariant = 1 - -styles = getSampleStyleSheet() - -Title = "The Odyssey" -Author = "Homer" - -def myTitlePage(canvas, doc): - canvas.saveState() - canvas.restoreState() - -def myLaterPages(canvas, doc): - canvas.saveState() - canvas.setFont('Times-Roman',9) - canvas.drawString(inch, 0.75 * inch, "Page %d" % doc.page) - canvas.restoreState() - -def go(): - def myCanvasMaker(fn,**kw): - from reportlab.pdfgen.canvas import Canvas - canv = apply(Canvas,(fn,),kw) - # attach our callback to the canvas - canv.myOnDrawCB = myOnDrawCB - return canv - - doc = BaseDocTemplate('dodyssey.pdf',showBoundary=0) - - #normal frame as for SimpleFlowDocument - frameT = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal') - - #Two Columns - frame1 = Frame(doc.leftMargin, doc.bottomMargin, doc.width/2-6, doc.height, id='col1') - frame2 = Frame(doc.leftMargin+doc.width/2+6, doc.bottomMargin, doc.width/2-6, - doc.height, id='col2') - doc.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=myTitlePage), - PageTemplate(id='OneCol',frames=frameT, onPage=myLaterPages), - PageTemplate(id='TwoCol',frames=[frame1,frame2], onPage=myLaterPages), - ]) - doc.build(Elements,canvasmaker=myCanvasMaker) - -Elements = [] - -ChapterStyle = copy.deepcopy(styles["Heading1"]) -ChapterStyle.alignment = TA_CENTER -ChapterStyle.fontsize = 14 -InitialStyle = copy.deepcopy(ChapterStyle) -InitialStyle.fontsize = 16 -InitialStyle.leading = 20 -PreStyle = styles["Code"] - -def newPage(): - Elements.append(PageBreak()) - -chNum = 0 -def myOnDrawCB(canv,kind,label): - print 'myOnDrawCB(%s)'%kind, 'Page number=', canv.getPageNumber(), 'label value=', label - -def chapter(txt, style=ChapterStyle): - global chNum - Elements.append(NextPageTemplate('OneCol')) - newPage() - chNum = chNum + 1 - if _NEW_PARA or not _CALLBACK: - Elements.append(Paragraph(('chap %d'%chNum)+txt, style)) - else: - Elements.append(Paragraph(('foo '%chNum)+txt, style)) - Elements.append(Spacer(0.2*inch, 0.3*inch)) - if useTwoCol: - Elements.append(NextPageTemplate('TwoCol')) - -def fTitle(txt,style=InitialStyle): - Elements.append(Paragraph(txt, style)) - -ParaStyle = copy.deepcopy(styles["Normal"]) -ParaStyle.spaceBefore = 0.1*inch -if 'right' in sys.argv: - ParaStyle.alignment = TA_RIGHT -elif 'left' in sys.argv: - ParaStyle.alignment = TA_LEFT -elif 'justify' in sys.argv: - ParaStyle.alignment = TA_JUSTIFY -elif 'center' in sys.argv or 'centre' in sys.argv: - ParaStyle.alignment = TA_CENTER -else: - ParaStyle.alignment = TA_JUSTIFY - -useTwoCol = 'notwocol' not in sys.argv - -def spacer(inches): - Elements.append(Spacer(0.1*inch, inches*inch)) - -def p(txt, style=ParaStyle): - if _REDCAP: - fs, fe = '', '' - n = len(txt) - for i in xrange(n): - if 'a'<=txt[i]<='z' or 'A'<=txt[i]<='Z': - txt = (txt[:i]+(fs+txt[i]+fe))+txt[i+1:] - break - if _REDCAP>=2 and n>20: - j = i+len(fs)+len(fe)+1+int((n-1)/2) - while not ('a'<=txt[j]<='z' or 'A'<=txt[j]<='Z'): j += 1 - txt = (txt[:j]+(''+txt[j]+''))+txt[j+1:] - - if _REDCAP==3 and n>20: - n = len(txt) - fs = '' - for i in xrange(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 - - Elements.append(Paragraph(txt, style)) - -firstPre = 1 -def pre(txt, style=PreStyle): - global firstPre - if firstPre: - Elements.append(NextPageTemplate('OneCol')) - newPage() - firstPre = 0 - - spacer(0.1) - p = Preformatted(txt, style) - Elements.append(p) - -def parseOdyssey(fn): - from time import time - E = [] - t0=time() - L = open(fn,'r').readlines() - t1 = time() - print "open(%s,'r').readlines() took %.4f seconds" %(fn,t1-t0) - for i in xrange(len(L)): - if L[i][-1]=='\012': - L[i] = L[i][:-1] - t2 = time() - print "Removing all linefeeds took %.4f seconds" %(t2-t1) - L.append('') - L.append('-----') - - def findNext(L, i): - while 1: - if string.strip(L[i])=='': - del L[i] - kind = 1 - if i%s' % Title, InitialStyle]) - E.append([fTitle,'by %s' % Author, InitialStyle]) - - while 1: - if f>=len(L): break - - if string.upper(L[f][0:5])=='BOOK ': - E.append([chapter,L[f]]) - f=f+1 - while string.strip(L[f])=='': del L[f] - style = ParaStyle - func = p - else: - style = PreStyle - func = pre - - while 1: - s=f - f, k=findNext(L,s) - sep= (func is pre) and '\012' or ' ' - E.append([func,string.join(L[s:f],sep),style]) - if k: break - t3 = time() - print "Parsing into memory took %.4f seconds" %(t3-t2) - del L - t4 = time() - print "Deleting list of lines took %.4f seconds" %(t4-t3) - for i in xrange(len(E)): - apply(E[i][0],E[i][1:]) - t5 = time() - print "Moving into platypus took %.4f seconds" %(t5-t4) - del E - t6 = time() - 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) - - import md5 - print 'file digest: %s' % md5.md5(open('dodyssey.pdf','rb').read()).hexdigest() - -def run(): - for fn in ('odyssey.full.txt','odyssey.txt'): - if os.path.isfile(fn): - parseOdyssey(fn) - break - -def doProf(profname,func,*args,**kwd): - import hotshot, hotshot.stats - prof = hotshot.Profile(profname) - prof.runcall(func) - prof.close() - stats = hotshot.stats.load(profname) - stats.strip_dirs() - stats.sort_stats('time', 'calls') - stats.print_stats(20) - -if __name__=='__main__': - if '--prof' in sys.argv: - doProf('dodyssey.prof',run) - else: - run() diff --git a/bin/reportlab/demos/odyssey/fodyssey.py b/bin/reportlab/demos/odyssey/fodyssey.py deleted file mode 100644 index bbb6797e7a7..00000000000 --- a/bin/reportlab/demos/odyssey/fodyssey.py +++ /dev/null @@ -1,165 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/fodyssey.py -__version__=''' $Id: fodyssey.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__='' - -#REPORTLAB_TEST_SCRIPT -import sys, copy, string, os -from reportlab.platypus import * -from reportlab.lib.units import inch -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY - -styles = getSampleStyleSheet() - -Title = "The Odyssey" -Author = "Homer" - -def myFirstPage(canvas, doc): - canvas.saveState() - canvas.restoreState() - -def myLaterPages(canvas, doc): - canvas.saveState() - canvas.setFont('Times-Roman',9) - canvas.drawString(inch, 0.75 * inch, "Page %d" % doc.page) - canvas.restoreState() - -def go(): - doc = SimpleDocTemplate('fodyssey.pdf',showBoundary='showboundary' in sys.argv) - doc.allowSplitting = not 'nosplitting' in sys.argv - doc.build(Elements,myFirstPage,myLaterPages) - -Elements = [] - -ChapterStyle = copy.copy(styles["Heading1"]) -ChapterStyle.alignment = TA_CENTER -ChapterStyle.fontsize = 16 -InitialStyle = copy.deepcopy(ChapterStyle) -InitialStyle.fontsize = 16 -InitialStyle.leading = 20 -PreStyle = styles["Code"] - -def newPage(): - Elements.append(PageBreak()) - -def chapter(txt, style=ChapterStyle): - newPage() - Elements.append(Paragraph(txt, style)) - Elements.append(Spacer(0.2*inch, 0.3*inch)) - -def fTitle(txt,style=InitialStyle): - Elements.append(Paragraph(txt, style)) - -ParaStyle = copy.deepcopy(styles["Normal"]) -ParaStyle.spaceBefore = 0.1*inch -if 'right' in sys.argv: - ParaStyle.alignment = TA_RIGHT -elif 'left' in sys.argv: - ParaStyle.alignment = TA_LEFT -elif 'justify' in sys.argv: - ParaStyle.alignment = TA_JUSTIFY -elif 'center' in sys.argv or 'centre' in sys.argv: - ParaStyle.alignment = TA_CENTER -else: - ParaStyle.alignment = TA_JUSTIFY - -def spacer(inches): - Elements.append(Spacer(0.1*inch, inches*inch)) - -def p(txt, style=ParaStyle): - Elements.append(Paragraph(txt, style)) - -def pre(txt, style=PreStyle): - spacer(0.1) - p = Preformatted(txt, style) - Elements.append(p) - -def parseOdyssey(fn): - from time import time - E = [] - t0=time() - L = open(fn,'r').readlines() - t1 = time() - print "open(%s,'r').readlines() took %.4f seconds" %(fn,t1-t0) - for i in xrange(len(L)): - if L[i][-1]=='\012': - L[i] = L[i][:-1] - t2 = time() - print "Removing all linefeeds took %.4f seconds" %(t2-t1) - L.append('') - L.append('-----') - - def findNext(L, i): - while 1: - if string.strip(L[i])=='': - del L[i] - kind = 1 - if i%s' % Title, InitialStyle]) - E.append([fTitle,'by %s' % Author, InitialStyle]) - - while 1: - if f>=len(L): break - - if string.upper(L[f][0:5])=='BOOK ': - E.append([chapter,L[f]]) - f=f+1 - while string.strip(L[f])=='': del L[f] - style = ParaStyle - func = p - else: - style = PreStyle - func = pre - - while 1: - s=f - f, k=findNext(L,s) - sep= (func is pre) and '\012' or ' ' - E.append([func,string.join(L[s:f],sep),style]) - if k: break - t3 = time() - print "Parsing into memory took %.4f seconds" %(t3-t2) - del L - t4 = time() - print "Deleting list of lines took %.4f seconds" %(t4-t3) - for i in xrange(len(E)): - apply(E[i][0],E[i][1:]) - t5 = time() - print "Moving into platypus took %.4f seconds" %(t5-t4) - del E - t6 = time() - 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) - -for fn in ('odyssey.full.txt','odyssey.txt'): - if os.path.isfile(fn): - break -if __name__=='__main__': - parseOdyssey(fn) \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/odyssey.py b/bin/reportlab/demos/odyssey/odyssey.py deleted file mode 100644 index ae8d9d6188c..00000000000 --- a/bin/reportlab/demos/odyssey/odyssey.py +++ /dev/null @@ -1,151 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/odyssey/odyssey.py -__version__=''' $Id: odyssey.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -___doc__='' -#odyssey.py -# -#Demo/benchmark of PDFgen rendering Homer's Odyssey. - - - -#results on my humble P266 with 64MB: -# Without page compression: -# 239 pages in 3.76 seconds = 77 pages per second - -# With textOut rather than textLine, i.e. computing width -# of every word as we would for wrapping: -# 239 pages in 10.83 seconds = 22 pages per second - -# With page compression and textLine(): -# 239 pages in 39.39 seconds = 6 pages per second - -from reportlab.pdfgen import canvas -import time, os, sys - -#find out what platform we are on and whether accelerator is -#present, in order to print this as part of benchmark info. -try: - import _rl_accel - ACCEL = 1 -except ImportError: - ACCEL = 0 - - - - -from reportlab.lib.units import inch, cm -from reportlab.lib.pagesizes import A4 - -#precalculate some basics -top_margin = A4[1] - inch -bottom_margin = inch -left_margin = inch -right_margin = A4[0] - inch -frame_width = right_margin - left_margin - - -def drawPageFrame(canv): - canv.line(left_margin, top_margin, right_margin, top_margin) - canv.setFont('Times-Italic',12) - canv.drawString(left_margin, top_margin + 2, "Homer's Odyssey") - canv.line(left_margin, top_margin, right_margin, top_margin) - - - canv.line(left_margin, bottom_margin, right_margin, bottom_margin) - canv.drawCentredString(0.5*A4[0], 0.5 * inch, - "Page %d" % canv.getPageNumber()) - - - -def run(verbose=1): - if sys.platform[0:4] == 'java': - impl = 'Jython' - else: - impl = 'Python' - verStr = '%d.%d' % (sys.version_info[0:2]) - if ACCEL: - accelStr = 'with _rl_accel' - else: - accelStr = 'without _rl_accel' - print 'Benchmark of %s %s %s' % (impl, verStr, accelStr) - - started = time.time() - canv = canvas.Canvas('odyssey.pdf', invariant=1) - canv.setPageCompression(1) - drawPageFrame(canv) - - #do some title page stuff - canv.setFont("Times-Bold", 36) - canv.drawCentredString(0.5 * A4[0], 7 * inch, "Homer's Odyssey") - - canv.setFont("Times-Bold", 18) - canv.drawCentredString(0.5 * A4[0], 5 * inch, "Translated by Samuel Burton") - - canv.setFont("Times-Bold", 12) - tx = canv.beginText(left_margin, 3 * inch) - tx.textLine("This is a demo-cum-benchmark for PDFgen. It renders the complete text of Homer's Odyssey") - tx.textLine("from a text file. On my humble P266, it does 77 pages per secondwhile creating a 238 page") - tx.textLine("document. If it is asked to computer text metrics, measuring the width of each word as ") - tx.textLine("one would for paragraph wrapping, it still manages 22 pages per second.") - tx.textLine("") - tx.textLine("Andy Robinson, Robinson Analytics Ltd.") - canv.drawText(tx) - - canv.showPage() - #on with the text... - drawPageFrame(canv) - - canv.setFont('Times-Roman', 12) - tx = canv.beginText(left_margin, top_margin - 0.5*inch) - - for fn in ('odyssey.full.txt','odyssey.txt'): - if os.path.isfile(fn): - break - - data = open(fn,'r').readlines() - for line in data: - #this just does it the fast way... - tx.textLine(line) - #this forces it to do text metrics, which would be the slow - #part if we were wrappng paragraphs. - #canv.textOut(line) - #canv.textLine('') - - #page breaking - y = tx.getY() #get y coordinate - if y < bottom_margin + 0.5*inch: - canv.drawText(tx) - canv.showPage() - drawPageFrame(canv) - canv.setFont('Times-Roman', 12) - tx = canv.beginText(left_margin, top_margin - 0.5*inch) - - #page - pg = canv.getPageNumber() - if verbose and pg % 10 == 0: - print 'formatted page %d' % canv.getPageNumber() - - if tx: - canv.drawText(tx) - canv.showPage() - drawPageFrame(canv) - - if verbose: - print 'about to write to disk...' - - canv.save() - - finished = time.time() - elapsed = finished - started - 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() - -if __name__=='__main__': - quiet = ('-q' in sys.argv) - run(verbose = not quiet) \ No newline at end of file diff --git a/bin/reportlab/demos/odyssey/odyssey.txt b/bin/reportlab/demos/odyssey/odyssey.txt deleted file mode 100644 index f5e4b7f9d60..00000000000 --- a/bin/reportlab/demos/odyssey/odyssey.txt +++ /dev/null @@ -1,207 +0,0 @@ -Provided by The Internet Classics Archive. -See bottom for copyright. Available online at - http://classics.mit.edu//Homer/odyssey.html - -The Odyssey -By Homer - - -Translated by Samuel Butler - ----------------------------------------------------------------------- - -BOOK I -ITell me, O muse, of that ingenious hero who travelled far and wide - a b c &| & | A' A ' after he had sacked the famous town of Troy. Many cities did he visit, -and many were the nations with whose manners and customs he was acquainted; -moreover he suffered much by sea while trying to save his own life -and bring his men safely home; but do what he might he could not -save1 -his men, for they perished through their own sheer folly in eating -the cattle of the Sun-god Hyperion; so the god prevented them from -ever reaching home. Tell me, too, about all these things, O daughter -of Jove, from whatsoever source you may know them. - -So now all who escaped death in battle or by shipwreck had got safely -home except Ulysses, and he, though he was longing to return to his -wife and country, was detained by the goddess Calypso, who had got -him into a large cave and wanted to marry him. But as years went by, -there came a time when the gods settled that he should go back to -Ithaca; even then, however, when he was among his own people, his -troubles were not yet over; nevertheless all the gods had now begun -to pity him except Neptune, who still persecuted him without ceasing -and would not let him get home. - -Now Neptune had gone off to the Ethiopians, who are at the world's -end, and lie in two halves, the one looking West and the other East. -He had gone there to accept a hecatomb of sheep and oxen, and was -enjoying himself at his festival; but the other gods met in the house -of Olympian Jove, and the sire of gods and men spoke first. At that -moment he was thinking of Aegisthus, who had been killed by Agamemnon's -son Orestes; so he said to the other gods: - -"See now, how men lay blame upon us gods for what is after all nothing -but their own folly. Look at Aegisthus; he must needs make love to -Agamemnon's wife unrighteously and then kill Agamemnon, though he -knew it would be the death of him; for I sent Mercury to warn him -not to do either of these things, inasmuch as Orestes would be sure -to take his revenge when he grew up and wanted to return home. Mercury -told him this in all good will but he would not listen, and now he -has paid for everything in full." - -Then Minerva said, "Father, son of Saturn, King of kings, it served -Aegisthus right, and so it would any one else who does as he did; -but Aegisthus is neither here nor there; it is for Ulysses that my -heart bleeds, when I think of his sufferings in that lonely sea-girt -island, far away, poor man, from all his friends. It is an island -covered with forest, in the very middle of the sea, and a goddess -lives there, daughter of the magician Atlas, who looks after the bottom -of the ocean, and carries the great columns that keep heaven and earth -asunder. This daughter of Atlas has got hold of poor unhappy Ulysses, -and keeps trying by every kind of blandishment to make him forget -his home, so that he is tired of life, and thinks of nothing but how -he may once more see the smoke of his own chimneys. You, sir, take -no heed of this, and yet when Ulysses was before Troy did he not propitiate -you with many a burnt sacrifice? Why then should you keep on being -so angry with him?" - -And Jove said, "My child, what are you talking about? How can I forget -Ulysses than whom there is no more capable man on earth, nor more -liberal in his offerings to the immortal gods that live in heaven? -Bear in mind, however, that Neptune is still furious with Ulysses -for having blinded an eye of Polyphemus king of the Cyclopes. Polyphemus -is son to Neptune by the nymph Thoosa, daughter to the sea-king Phorcys; -therefore though he will not kill Ulysses outright, he torments him -by preventing him from getting home. Still, let us lay our heads together -and see how we can help him to return; Neptune will then be pacified, -for if we are all of a mind he can hardly stand out against us." - -And Minerva said, "Father, son of Saturn, King of kings, if, then, -the gods now mean that Ulysses should get home, we should first send -Mercury to the Ogygian island to tell Calypso that we have made up -our minds and that he is to return. In the meantime I will go to Ithaca, -to put heart into Ulysses' son Telemachus; I will embolden him to -call the Achaeans in assembly, and speak out to the suitors of his -mother Penelope, who persist in eating up any number of his sheep -and oxen; I will also conduct him to Sparta and to Pylos, to see if -he can hear anything about the return of his dear father- for this -will make people speak well of him." - -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. -Ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis ellipsis. - -"Men of Ithaca, it is all your own fault that things have turned out -as they have; you would not listen to me, nor yet to Mentor, when -we bade you check the folly of your sons who were doing much wrong -in the wantonness of their hearts- wasting the substance and dishonouring -the wife of a chieftain who they thought would not return. Now, however, -let it be as I say, and do as I tell you. Do not go out against Ulysses, -or you may find that you have been drawing down evil on your own heads." - -This was what he said, and more than half raised a loud shout, and -at once left the assembly. But the rest stayed where they were, for -the speech of Halitherses displeased them, and they sided with Eupeithes; -they therefore hurried off for their armour, and when they had armed -themselves, they met together in front of the city, and Eupeithes -led them on in their folly. He thought he was going to avenge the -murder of his son, whereas in truth he was never to return, but was -himself to perish in his attempt. - -Then Minerva said to Jove, "Father, son of Saturn, king of kings, -answer me this question- What do you propose to do? Will you set them -fighting still further, or will you make peace between them?" - -And Jove answered, "My child, why should you ask me? Was it not by -your own arrangement that Ulysses came home and took his revenge upon -the suitors? Do whatever you like, but I will tell you what I think -will be most reasonable arrangement. Now that Ulysses is revenged, -let them swear to a solemn covenant, in virtue of which he shall continue -to rule, while we cause the others to forgive and forget the massacre -of their sons and brothers. Let them then all become friends as heretofore, -and let peace and plenty reign." - -This was what Minerva was already eager to bring about, so down she -darted from off the topmost summits of Olympus. - -Now when Laertes and the others had done dinner, Ulysses began by -saying, "Some of you go out and see if they are not getting close -up to us." So one of Dolius's sons went as he was bid. Standing on -the threshold he could see them all quite near, and said to Ulysses, -"Here they are, let us put on our armour at once." - -They put on their armour as fast as they could- that is to say Ulysses, -his three men, and the six sons of Dolius. Laertes also and Dolius -did the same- warriors by necessity in spite of their grey hair. When -they had all put on their armour, they opened the gate and sallied -forth, Ulysses leading the way. - -Then Jove's daughter Minerva came up to them, having assumed the form -and voice of Mentor. Ulysses was glad when he saw her, and said to -his son Telemachus, "Telemachus, now that are about to fight in an -engagement, which will show every man's mettle, be sure not to disgrace -your ancestors, who were eminent for their strength and courage all -the world over." - -"You say truly, my dear father," answered Telemachus, "and you shall -see, if you will, that I am in no mind to disgrace your family." - -Laertes was delighted when he heard this. "Good heavens, he exclaimed, -"what a day I am enjoying: I do indeed rejoice at it. My son and grandson -are vying with one another in the matter of valour." - -On this Minerva came close up to him and said, "Son of Arceisius- -best friend I have in the world- pray to the blue-eyed damsel, and -to Jove her father; then poise your spear and hurl it." - -As she spoke she infused fresh vigour into him, and when he had prayed -to her he poised his spear and hurled it. He hit Eupeithes' helmet, -and the spear went right through it, for the helmet stayed it not, -and his armour rang rattling round him as he fell heavily to the ground. -Meantime Ulysses and his son fell the front line of the foe and smote -them with their swords and spears; indeed, they would have killed -every one of them, and prevented them from ever getting home again, -only Minerva raised her voice aloud, and made every one pause. "Men -of Ithaca," she cried, cease this dreadful war, and settle the matter -at once without further bloodshed." - -On this pale fear seized every one; they were so frightened that their -arms dropped from their hands and fell upon the ground at the sound -of the goddess's voice, and they fled back to the city for their lives. -But Ulysses gave a great cry, and gathering himself together swooped -down like a soaring eagle. Then the son of Saturn sent a thunderbolt -of fire that fell just in front of Minerva, so she said to Ulysses, -"Ulysses, noble son of Laertes, stop this warful strife, or Jove will -be angry with you." - -Thus spoke Minerva, and Ulysses obeyed her gladly. Then Minerva assumed -the form and voice of Mentor, and presently made a covenant of peace -between the two contending parties. - -THE END - ----------------------------------------------------------------------- - -Copyright statement: -The Internet Classics Archive by Daniel C. Stevenson, Web Atomics. -World Wide Web presentation is copyright (C) 1994-1998, Daniel -C. Stevenson, Web Atomics. -All rights reserved under international and pan-American copyright -conventions, including the right of reproduction in whole or in part -in any form. Direct permission requests to classics@classics.mit.edu. -Translation of "The Deeds of the Divine Augustus" by Augustus is -copyright (C) Thomas Bushnell, BSG. - - -To really test that reportlab can produce pages quickly download the -complete version of the test from http://classics.mit.edu//Homer/odyssey.html -and copy it to this directory as odyssey.full.txt. - -A zipped version of the full text is available for download at -ftp://ftp.reportlab.com/odyssey.full.zip diff --git a/bin/reportlab/demos/rlzope/readme.txt b/bin/reportlab/demos/rlzope/readme.txt deleted file mode 100644 index 4bd60a5bb64..00000000000 --- a/bin/reportlab/demos/rlzope/readme.txt +++ /dev/null @@ -1,73 +0,0 @@ -# rlzope : an external Zope method to show people how to use -# the ReportLab toolkit from within Zope. -# -# this method searches an image named 'logo' in the -# ZODB then prints it at the top of a simple PDF -# document made with ReportLab -# -# the resulting PDF document is returned to the -# user's web browser and, if possible, it is -# simultaneously saved into the ZODB. -# -# this method illustrates how to use both the platypus -# and canvas frameworks. -# -# License : The ReportLab Toolkit's license (similar to BSD) -# -# Author : Jerome Alet - alet@unice.fr -# - -Installation instructions : -=========================== - - 0 - If not installed then install Zope. - - 1 - Install reportlab in the Zope/lib/python/Shared directory by unpacking - the tarball and putting a reportlabs.pth file in site-packages for the Zope - used with Python. The path value in the reportlabs.pth file must be - relative. For a typical Zope installation, the path is "../../python/Shared". - Remember to restart Zope so the new path is instantiated. - - 2 - Install PIL in the Zope/lib/python/Shared directory. You need to - ensure that the _imaging.so or .pyd is also installed appropriately. - It should be compatible with the python running the zope site. - - 3 - Copy rlzope.py to your Zope installation's "Extensions" - subdirectory, e.g. /var/lib/zope/Extensions/ under Debian GNU/Linux. - - 4 - From within Zope's management interface, add an External Method with - these parameters : - - Id : rlzope - Title : rlzope - Module Name : rlzope - Function Name : rlzope - - 5 - From within Zope's management interface, add an image called "logo" - in the same Folder than rlzope, or somewhere above in the Folder - hierarchy. For example you can use ReportLab's logo which you - can find in reportlab/docs/images/replogo.gif - - 6 - Point your web browser to rlzope, e.g. on my laptop under - Debian GNU/Linux : - - http://localhost:9673/rlzope - - This will send a simple PDF document named 'dummy.pdf' to your - web browser, and if possible save it as a File object in the - Zope Object DataBase, with this name. Note, however, that if - an object with the same name already exists then it won't - be replaced for security reasons. - - You can optionally add a parameter called 'name' with - a filename as the value, to specify another filename, - e.g. : -logo - http://localhost:9673/rlzope?name=sample.pdf - - 7 - Adapt it to your own needs. - - 8 - Enjoy ! - -Send comments or bug reports at : alet@unice.fr - diff --git a/bin/reportlab/demos/rlzope/rlzope.py b/bin/reportlab/demos/rlzope/rlzope.py deleted file mode 100644 index 867d8466759..00000000000 --- a/bin/reportlab/demos/rlzope/rlzope.py +++ /dev/null @@ -1,169 +0,0 @@ -# -# Using the ReportLab toolkit from within Zope -# -# WARNING : The MyPDFDoc class deals with ReportLab's platypus framework, -# while the MyPageTemplate class directly deals with ReportLab's -# canvas, this way you know how to do with both... -# -# License : the ReportLab Toolkit's one -# see : http://www.reportlab.com -# -# Author : Jerome Alet - alet@unice.fr -# -# - -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 -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 - -class MyPDFDoc : - class MyPageTemplate(PageTemplate) : - """Our own page template.""" - def __init__(self, parent) : - """Initialise our page template.""" - # - # we must save a pointer to our parent somewhere - self.parent = parent - - # Our doc is made of a single frame - content = Frame(0.75 * inch, 0.5 * inch, parent.document.pagesize[0] - 1.25 * inch, parent.document.pagesize[1] - (1.5 * inch)) - PageTemplate.__init__(self, "MyTemplate", [content]) - - # get all the images we need now, in case we've got - # several pages this will save some CPU - self.logo = self.getImageFromZODB("logo") - - def getImageFromZODB(self, name) : - """Retrieves an Image from the ZODB, converts it to PIL, - and makes it 0.75 inch high. - """ - try : - # try to get it from ZODB - logo = getattr(self.parent.context, name) - except AttributeError : - # not found ! - return None - - # Convert it to PIL - image = ImageReader(cStringIO.StringIO(str(logo.data))) - (width, height) = image.getSize() - - # scale it to be 0.75 inch high - multi = ((height + 0.0) / (0.75 * inch)) - width = int(width / multi) - height = int(height / multi) - - return ((width, height), image) - - def beforeDrawPage(self, canvas, doc) : - """Draws a logo and an contribution message on each page.""" - canvas.saveState() - if self.logo is not None : - # draws the logo if it exists - ((width, height), image) = self.logo - canvas.drawImage(image, inch, doc.pagesize[1] - inch, width, height) - canvas.setFont('Times-Roman', 10) - canvas.drawCentredString(inch + (doc.pagesize[0] - (1.5 * inch)) / 2, 0.25 * inch, "Contributed by Jerome Alet - alet@unice.fr") - canvas.restoreState() - - def __init__(self, context, filename) : - # save some datas - self.context = context - self.built = 0 - self.objects = [] - - # we will build an in-memory document - # instead of creating an on-disk file. - self.report = cStringIO.StringIO() - - # initialise a PDF document using ReportLab's platypus - self.document = BaseDocTemplate(self.report) - - # add our page template - # (we could add more than one, but I prefer to keep it simple) - self.document.addPageTemplates(self.MyPageTemplate(self)) - - # get the default style sheets - self.StyleSheet = styles.getSampleStyleSheet() - - # then build a simple doc with ReportLab's platypus - sometext = "A sample script to show how to use ReportLab from within Zope" - url = self.escapexml(context.absolute_url()) - urlfilename = self.escapexml(context.absolute_url() + '/%s' % filename) - self.append(Paragraph("Using ReportLab from within Zope", self.StyleSheet["Heading3"])) - self.append(Spacer(0, 10)) - self.append(Paragraph("You launched it from : %s" % url, self.StyleSheet['Normal'])) - self.append(Spacer(0, 40)) - self.append(Paragraph("If possible, this report will be automatically saved as : %s" % urlfilename, self.StyleSheet['Normal'])) - - # generation du document PDF - self.document.build(self.objects) - self.built = 1 - - def __str__(self) : - """Returns the PDF document as a string of text, or None if it's not ready yet.""" - if self.built : - return self.report.getvalue() - else : - return None - - def append(self, object) : - """Appends an object to our platypus "story" (using ReportLab's terminology).""" - self.objects.append(object) - - def escapexml(self, s) : - """Escape some xml entities.""" - s = string.strip(s) - s = string.replace(s, "&", "&") - s = string.replace(s, "<", "<") - return string.replace(s, ">", ">") - -def rlzope(self) : - """A sample external method to show people how to use ReportLab from within Zope.""" - try: - # - # which file/object name to use ? - # append ?name=xxxxx to rlzope's url to - # choose another name - filename = self.REQUEST.get("name", "dummy.pdf") - if filename[-4:] != '.pdf' : - filename = filename + '.pdf' - - # tell the browser we send some PDF document - # with the requested filename - - # get the document's content itself as a string of text - content = str(MyPDFDoc(self, filename)) - - # we will return it to the browser, but before that we also want to - # save it into the ZODB into the current folder - try : - self.manage_addFile(id = filename, file = content, title = "A sample PDF document produced with ReportLab", precondition = '', content_type = "application/pdf") - except : - # it seems an object with this name already exists in the ZODB: - # it's more secure to not replace it, since we could possibly - # destroy an important PDF document of this name. - pass - self.REQUEST.RESPONSE.setHeader('Content-Type', 'application/pdf') - self.REQUEST.RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s' % filename) - except: - import traceback, sys, cgi - content = sys.stdout = sys.stderr = cStringIO.StringIO() - self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/html') - traceback.print_exc() - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - content = '
%s
' % cgi.escape(content.getvalue()) - - # then we also return the PDF content to the browser - return content diff --git a/bin/reportlab/demos/stdfonts/00readme.txt b/bin/reportlab/demos/stdfonts/00readme.txt deleted file mode 100644 index 9803d8e0c9d..00000000000 --- a/bin/reportlab/demos/stdfonts/00readme.txt +++ /dev/null @@ -1,7 +0,0 @@ -This lists out the standard 14 fonts -in a very plain and simple fashion. - -Notably, the output is huge - it makes -two separate text objects for each glyph. -Smarter programming would make tighter -PDF, but more lines of Python! diff --git a/bin/reportlab/demos/stdfonts/stdfonts.py b/bin/reportlab/demos/stdfonts/stdfonts.py deleted file mode 100644 index 44ce94f4739..00000000000 --- a/bin/reportlab/demos/stdfonts/stdfonts.py +++ /dev/null @@ -1,74 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/stdfonts/stdfonts.py -__version__=''' $Id: stdfonts.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' -__doc__=""" -This generates tables showing the 14 standard fonts in both -WinAnsi and MacRoman encodings, and their character codes. -Supply an argument of 'hex' or 'oct' to get code charts -in those encodings; octal is what you need for \\n escape -sequences in Python literals. - -usage: standardfonts.py [dec|hex|oct] -""" -import sys -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfgen import canvas -import string - -label_formats = {'dec':('%d=', 'Decimal'), - 'oct':('%o=','Octal'), - 'hex':('0x%x=', 'Hexadecimal')} - -def run(mode): - - label_formatter, caption = label_formats[mode] - - for enc in ['MacRoman', 'WinAnsi']: - canv = canvas.Canvas( - 'StandardFonts_%s.pdf' % enc, - ) - canv.setPageCompression(0) - - for faceName in pdfmetrics.standardFonts: - if faceName in ['Symbol', 'ZapfDingbats']: - encLabel = faceName+'Encoding' - else: - encLabel = enc + 'Encoding' - - fontName = faceName + '-' + encLabel - pdfmetrics.registerFont(pdfmetrics.Font(fontName, - faceName, - encLabel) - ) - - canv.setFont('Times-Bold', 18) - canv.drawString(80, 744, fontName) - canv.setFont('Times-BoldItalic', 12) - canv.drawRightString(515, 744, 'Labels in ' + caption) - - - #for dingbats, we need to use another font for the numbers. - #do two parallel text objects. - for byt in range(32, 256): - col, row = divmod(byt - 32, 32) - x = 72 + (66*col) - y = 720 - (18*row) - canv.setFont('Helvetica', 14) - canv.drawString(x, y, label_formatter % byt) - canv.setFont(fontName, 14) - canv.drawString(x+44, y, chr(byt).decode(encLabel,'ignore').encode('utf8')) - canv.showPage() - canv.save() - -if __name__ == '__main__': - if len(sys.argv)==2: - mode = string.lower(sys.argv[1]) - if mode not in ['dec','oct','hex']: - print __doc__ - - elif len(sys.argv) == 1: - mode = 'dec' - run(mode) - else: - print __doc__ diff --git a/bin/reportlab/demos/tests/testdemos.py b/bin/reportlab/demos/tests/testdemos.py deleted file mode 100644 index 3a215bb2269..00000000000 --- a/bin/reportlab/demos/tests/testdemos.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/demos/tests/testdemos.py -__version__=''' $Id: testdemos.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__='Test all demos' -_globals=globals().copy() -import os, sys -from reportlab import pdfgen - -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()) \ No newline at end of file diff --git a/bin/reportlab/docs/00readme.txt b/bin/reportlab/docs/00readme.txt deleted file mode 100644 index d1810ae46d0..00000000000 --- a/bin/reportlab/docs/00readme.txt +++ /dev/null @@ -1,7 +0,0 @@ -Thid directory holds documentation. For end users, -it should contain a number of PDF manuals. For -people working with the source, this directory will -be the destination for any manuals built. - -If you don't see the pdf manual you expected or you wich to -ensure an up to date copy run the script tools/genAll.py! diff --git a/bin/reportlab/docs/genAll.py b/bin/reportlab/docs/genAll.py deleted file mode 100644 index 0776903c9a2..00000000000 --- a/bin/reportlab/docs/genAll.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/env python -import os, sys -def _genAll(d=None,verbose=1): - if not d: d = '.' - if not os.path.isabs(d): - d = os.path.normpath(os.path.join(os.getcwd(),d)) - L = ['reference/genreference.py', - 'userguide/genuserguide.py', - 'graphguide/gengraphguide.py', - '../tools/docco/graphdocpy.py', - ] - if os.path.isdir('../rl_addons'): - L = L + ['../rl_addons/pyRXP/docs/PyRXP_Documentation.rml'] - elif os.path.isdir('../../rl_addons'): - L = L + ['../../rl_addons/pyRXP/docs/PyRXP_Documentation.rml'] - for p in L: - os.chdir(d) - os.chdir(os.path.dirname(p)) - if p[-4:]=='.rml': - try: - from rlextra.rml2pdf.rml2pdf import main - main(exe=0,fn=[os.path.basename(p)], quiet=not verbose, outDir=d) - except: - pass - else: - cmd = '%s %s %s' % (sys.executable,os.path.basename(p), not verbose and '-s' or '') - if verbose: print cmd - os.system(cmd) - -"""Runs the manual-building scripts""" -if __name__=='__main__': - #need a quiet mode for the test suite - if '-s' in sys.argv: # 'silent - verbose = 0 - else: - verbose = 1 - _genAll(os.path.dirname(sys.argv[0]),verbose) \ No newline at end of file diff --git a/bin/reportlab/docs/graphguide/ch1_intro.py b/bin/reportlab/docs/graphguide/ch1_intro.py deleted file mode 100644 index 794f4a769fc..00000000000 --- a/bin/reportlab/docs/graphguide/ch1_intro.py +++ /dev/null @@ -1,105 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch1_intro.py -from reportlab.tools.docco.rl_doc_utils import * -import reportlab - -title("Graphics Guide") -centred('ReportLab Version ' + reportlab.Version) - -nextTemplate("Normal") - -######################################################################## -# -# Chapter 1 -# -######################################################################## - - -heading1("Introduction") - - -heading2("About this document") -disc(""" -This document is intended to be a helpful and reasonably full -introduction to the use of the ReportLab Graphics sub-package. -Starting with simple drawings and shapes, we will take you through the -slightly more complex reusable widgets all the way through to our -powerful and flexible chart library. You will see examples of using -reportlab/graphics to make bar charts, line charts, line plots, pie -charts... and a smiley face. -""") - -disc(""" -We presume that you have already installed both the Python programming -language and the core ReportLab library. If you have not done either -of these, look in the ReportLab User Guide where chapter one -talks you through all the required steps. -""") - -disc(""" -We recommend that you read some or all of the User Guide and have at -least a basic understanding of how the ReportLab library works before -you start getting to grips with ReportLab Graphics. -""") - -disc("") -todo(""" -Be warned! This document is in a very preliminary form. We need -your help to make sure it is complete and helpful. Please send any -feedback to our user mailing list, reportlab-users@reportlab.com. -""") - -heading2("What is ReportLab?") -disc("""ReportLab is a software library that lets you directly -create documents in Adobe's Portable Document Format (PDF) using -the Python programming language. """) - -disc("""The ReportLab library directly creates PDF based on -your graphics commands. There are no intervening steps. Your applications -can generate reports extremely fast - sometimes orders -of magnitude faster than traditional report-writing -tools.""") - -heading2("What is ReportLab Graphics?") -disc(""" -ReportLab Graphics is one of the sub-packages to the ReportLab -library. It started off as a stand-alone set of programs, but is now a -fully integrated part of the ReportLab toolkit that allows you to use -its powerful charting and graphics features to improve your PDF forms -and reports. -""") - -heading2("Getting Involved") -disc("""ReportLab is an Open Source project. Although we are -a commercial company we provide the core PDF generation -sources freely, even for commercial purposes, and we make no income directly -from these modules. We also welcome help from the community -as much as any other Open Source project. There are many -ways in which you can help:""") - -bullet("""General feedback on the core API. Does it work for you? -Are there any rough edges? Does anything feel clunky and awkward?""") - -bullet("""New objects to put in reports, or useful utilities for the library. -We have an open standard for report objects, so if you have written a nice -chart or table class, why not contribute it?""") - -bullet("""Demonstrations and Case Studies: If you have produced some nice -output, send it to us (with or without scripts). If ReportLab solved a -problem for you at work, write a little 'case study' and send it in. -And if your web site uses our tools to make reports, let us link to it. -We will be happy to display your work (and credit it with your name -and company) on our site!""") - -bullet("""Working on the core code: we have a long list of things -to refine or to implement. If you are missing some features or -just want to help out, let us know!""") - -disc("""The first step for anyone wanting to learn more or -get involved is to join the mailing list. To Subscribe visit -$http://two.pairlist.net/mailman/listinfo/reportlab-users$. -From there you can also browse through the group's archives -and contributions. The mailing list is -the place to report bugs and get support. """) - diff --git a/bin/reportlab/docs/graphguide/ch2_concepts.py b/bin/reportlab/docs/graphguide/ch2_concepts.py deleted file mode 100644 index 28dab45fece..00000000000 --- a/bin/reportlab/docs/graphguide/ch2_concepts.py +++ /dev/null @@ -1,376 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch2_concepts.py -from reportlab.tools.docco.rl_doc_utils import * - -heading1("General Concepts") - -disc(""" -In this chapter we will present some of the more fundamental principles of -the graphics library, which will show-up later in various places. -""") - - -heading2("Drawings and Renderers") - -disc(""" -A Drawing is a platform-independent description of a collection of -shapes. -It is not directly associated with PDF, Postscript or any other output -format. -Fortunately, most vector graphics systems have followed the Postscript -model and it is possible to describe shapes unambiguously. -""") - -disc(""" -A drawing contains a number of primitive Shapes. -Normal shapes are those widely known as rectangles, circles, lines, -etc. -One special (logic) shape is a Group, which can hold other -shapes and apply a transformation to them. -Groups represent composites of shapes and allow to treat the -composite as if it were a single shape. -Just about anything can be built up from a small number of basic -shapes. -""") - -disc(""" -The package provides several Renderers which know how to draw a -drawing into different formats. -These include PDF (of course), Postscript, and bitmap output. -The bitmap renderer uses Raph Levien's libart rasterizer -and Fredrik Lundh's Python Imaging Library (PIL). -Very recently, an experimental SVG renderer was also added. -It makes use of Python's standard library XML modules, so you don't -need to install the XML-SIG's additional package named PyXML. -If you have the right extensions installed, you can generate drawings -in bitmap form for the web as well as vector form for PDF documents, -and get "identical output". -""") - -disc(""" -The PDF renderer has special "privileges" - a Drawing object is also -a Flowable and, hence, can be placed directly in the story -of any Platypus document, or drawn directly on a Canvas with -one line of code. -In addition, the PDF renderer has a utility function to make -a one-page PDF document quickly. -""") - -disc(""" -The SVG renderer is special as it is still pretty experimental. -The SVG code it generates is not really optimised in any way and -maps only the features available in ReportLab Graphics (RLG) to -SVG. This means there is no support for SVG animation, interactivity, -scripting or more sophisticated clipping, masking or graduation -shapes. -So, be careful, and please report any bugs you find! -""") - -disc(""" -We expect to add both input and output filters for many vector -graphics formats in future. -SVG was the most prominent first one to start with for which there -is now an output filter in the graphics package. -An SVG input filter will probably become available in Summer 2002 -as an additional module. -GUIs will be able to obtain screen images from the bitmap output -filter working with PIL, so a chart could appear in a Tkinter -GUI window. -""") - - -heading2("Coordinate System") - -disc(""" -The Y-direction in our X-Y coordinate system points from the -bottom up. -This is consistent with PDF, Postscript and mathematical notation. -It also appears to be more natural for people, especially when -working with charts. -Note that in other graphics models (such as SVG) the Y-coordinate -points down. -For the SVG renderer this is actually no problem as it will take -your drawings and flip things as needed, so your SVG output looks -just as expected. -""") - -disc(""" -The X-coordinate points, as usual, from left to right. -So far there doesn't seem to be any model advocating the opposite -direction - at least not yet (with interesting exceptions, as it -seems, for Arabs looking at time series charts...). -""") - - -heading2("Getting Started") - -disc(""" -Let's create a simple drawing containing the string "Hello World", -displayed on top of a coloured rectangle. -After creating it we will save the drawing to a standalone PDF file. -""") - -eg(""" - from reportlab.lib import colors - from reportlab.graphics.shapes import * - - d = Drawing(400, 200) - d.add(Rect(50, 50, 300, 100, fillColor=colors.yellow)) - d.add(String(150,100, 'Hello World', - fontSize=18, fillColor=colors.red)) - - from reportlab.graphics import renderPDF - renderPDF.drawToFile(d, 'example1.pdf', 'My First Drawing') -""") - -disc("This will produce a PDF file containing the following graphic:") - -from reportlab.graphics.shapes import * -from reportlab.graphics import testshapes -t = testshapes.getDrawing01() -draw(t, "'Hello World'") - -disc(""" -Each renderer is allowed to do whatever is appropriate for its format, -and may have whatever API is needed. -If it refers to a file format, it usually has a $drawToFile$ function, -and that's all you need to know about the renderer. -Let's save the same drawing in Encapsulated Postscript format: -""") - -##eg(""" -## from reportlab.graphics import renderPS -## renderPS.drawToFile(D, 'example1.eps', 'My First Drawing') -##""") -eg(""" - from reportlab.graphics import renderPS - renderPS.drawToFile(d, 'example1.eps') -""") - -disc(""" -This will produce an EPS file with the identical drawing, which -may be imported into publishing tools such as Quark Express. -If we wanted to generate the same drawing as a bitmap file for -a website, say, all we need to do is write code like this: -""") - -eg(""" - from reportlab.graphics import renderPM - renderPM.saveToFile(d, 'example1.png', 'PNG') -""") - -disc(""" -Many other bitmap formats, like GIF, JPG, TIFF, BMP and PPN are -genuinely available, making it unlikely you'll need to add external -postprocessing steps to convert to the final format you need. -""") - -disc(""" -To produce an SVG file containing the identical drawing, which -may be imported into graphical editing tools such as Illustrator -all we need to do is write code like this: -""") - -eg(""" - from reportlab.graphics import renderSVG - renderSVG.drawToFile(d, 'example1.svg') -""") - - -heading2("Attribute Verification") - -disc(""" -Python is very dynamic and lets us execute statements at run time that -can easily be the source for unexpected behaviour. -One subtle 'error' is when assigning to an attribute that the framework -doesn't know about because the used attribute's name contains a typo. -Python lets you get away with it (adding a new attribute to an object, -say), but the graphics framework will not detect this 'typo' without -taking special counter-measures. -""") - -disc(""" -There are two verification techniques to avoid this situation. -The default is for every object to check every assignment at run -time, such that you can only assign to 'legal' attributes. -This is what happens by default. -As this imposes a small performance penalty, this behaviour can -be turned off when you need it to be. -""") - -eg(""" ->>> r = Rect(10,10,200,100, fillColor=colors.red) ->>> ->>> r.fullColor = colors.green # note the typo ->>> r.x = 'not a number' # illegal argument type ->>> del r.width # that should confuse it -""") - -disc(""" -These statements would be caught by the compiler in a statically -typed language, but Python lets you get away with it. -The first error could leave you staring at the picture trying to -figure out why the colors were wrong. -The second error would probably become clear only later, when -some back-end tries to draw the rectangle. -The third, though less likely, results in an invalid object that -would not know how to draw itself. -""") - -eg(""" ->>> r = shapes.Rect(10,10,200,80) ->>> r.fullColor = colors.green -Traceback (most recent call last): - File "", line 1, in ? - File "C:\code\users\andy\graphics\shapes.py", line 254, in __setattr__ - validateSetattr(self,attr,value) #from reportlab.lib.attrmap - File "C:\code\users\andy\lib\attrmap.py", line 74, in validateSetattr - raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__) -AttributeError: Illegal attribute 'fullColor' in class Rect ->>> -""") - -disc(""" -This imposes a performance penalty, so this behaviour can be turned -off when you need it to be. -To do this, you should use the following lines of code before you -first import reportlab.graphics.shapes: -""") - -eg(""" ->>> import reportlab.rl_config ->>> reportlab.rl_config.shapeChecking = 0 ->>> from reportlab.graphics import shapes ->>> -""") - -disc(""" -Once you turn off $shapeChecking$, the classes are actually built -without the verification hook; code should get faster, then. -Currently the penalty seems to be about 25% on batches of charts, -so it is hardly worth disabling. -However, if we move the renderers to C in future (which is eminently -possible), the remaining 75% would shrink to almost nothing and -the saving from verification would be significant. -""") - -disc(""" -Each object, including the drawing itself, has a $verify()$ method. -This either succeeds, or raises an exception. -If you turn off automatic verification, then you should explicitly -call $verify()$ in testing when developing the code, or perhaps -once in a batch process. -""") - - -heading2("Property Editing") - -disc(""" -A cornerstone of the reportlab/graphics which we will cover below is -that you can automatically document widgets. -This means getting hold of all of their editable properties, -including those of their subcomponents. -""") - -disc(""" -Another goal is to be able to create GUIs and config files for -drawings. -A generic GUI can be built to show all editable properties -of a drawing, and let you modify them and see the results. -The Visual Basic or Delphi development environment are good -examples of this kind of thing. -In a batch charting application, a file could list all the -properties of all the components in a chart, and be merged -with a database query to make a batch of charts. -""") - -disc(""" -To support these applications we have two interfaces, $getProperties$ -and $setProperties$, as well as a convenience method $dumpProperties$. -The first returns a dictionary of the editable properties of an -object; the second sets them en masse. -If an object has publicly exposed 'children' then one can recursively -set and get their properties too. -This will make much more sense when we look at Widgets later on, -but we need to put the support into the base of the framework. -""") - -eg(""" ->>> r = shapes.Rect(0,0,200,100) ->>> import pprint ->>> pprint.pprint(r.getProperties()) -{'fillColor': Color(0.00,0.00,0.00), - 'height': 100, - 'rx': 0, - 'ry': 0, - 'strokeColor': Color(0.00,0.00,0.00), - 'strokeDashArray': None, - 'strokeLineCap': 0, - 'strokeLineJoin': 0, - 'strokeMiterLimit': 0, - 'strokeWidth': 1, - 'width': 200, - 'x': 0, - 'y': 0} ->>> r.setProperties({'x':20, 'y':30, 'strokeColor': colors.red}) ->>> r.dumpProperties() -fillColor = Color(0.00,0.00,0.00) -height = 100 -rx = 0 -ry = 0 -strokeColor = Color(1.00,0.00,0.00) -strokeDashArray = None -strokeLineCap = 0 -strokeLineJoin = 0 -strokeMiterLimit = 0 -strokeWidth = 1 -width = 200 -x = 20 -y = 30 ->>> """) - -disc(""" -Note: $pprint$ is the standard Python library module that allows -you to 'pretty print' output over multiple lines rather than having -one very long line. -""") - -disc(""" -These three methods don't seem to do much here, but as we will see -they make our widgets framework much more powerful when dealing with -non-primitive objects. -""") - - -heading2("Naming Children") - -disc(""" -You can add objects to the $Drawing$ and $Group$ objects. -These normally go into a list of contents. -However, you may also give objects a name when adding them. -This allows you to refer to and possibly change any element -of a drawing after constructing it. -""") - -eg(""" ->>> d = shapes.Drawing(400, 200) ->>> s = shapes.String(10, 10, 'Hello World') ->>> d.add(s, 'caption') ->>> s.caption.text -'Hello World' ->>> -""") - -disc(""" -Note that you can use the same shape instance in several contexts -in a drawing; if you choose to use the same $Circle$ object in many -locations (e.g. a scatter plot) and use different names to access -it, it will still be a shared object and the changes will be -global. -""") - -disc(""" -This provides one paradigm for creating and modifying interactive -drawings. -""") \ No newline at end of file diff --git a/bin/reportlab/docs/graphguide/ch3_shapes.py b/bin/reportlab/docs/graphguide/ch3_shapes.py deleted file mode 100644 index 375513a037e..00000000000 --- a/bin/reportlab/docs/graphguide/ch3_shapes.py +++ /dev/null @@ -1,416 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch3_shapes.py - -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.graphics.shapes import * - -heading1("Shapes") - -disc(""" -This chapter describes the concept of shapes and their importance -as building blocks for all output generated by the graphics library. -Some properties of existing shapes and their relationship to -diagrams are presented and the notion of having different renderers -for different output formats is briefly introduced. -""") - -heading2("Available Shapes") - -disc(""" -Drawings are made up of Shapes. -Absolutely anything can be built up by combining the same set of -primitive shapes. -The module $shapes.py$ supplies a number of primitive shapes and -constructs which can be added to a drawing. -They are: -""") - -bullet("Rect") -bullet("Circle") -bullet("Ellipse") -bullet("Wedge (a pie slice)") -bullet("Polygon") -bullet("Line") -bullet("PolyLine") -bullet("String") -bullet("Group") -bullet("Path (not implemented yet, but will be added in the future)") - -disc(""" -The following drawing, taken from our test suite, shows most of the -basic shapes (except for groups). -Those with a filled green surface are also called solid shapes -(these are $Rect$, $Circle$, $Ellipse$, $Wedge$ and $Polygon$). -""") - -from reportlab.graphics import testshapes - -t = testshapes.getDrawing06() -draw(t, "Basic shapes") - - -heading2("Shape Properties") - -disc(""" -Shapes have two kinds of properties - some to define their geometry -and some to define their style. -Let's create a red rectangle with 3-point thick green borders: -""") - -eg(""" ->>> from reportlab.graphics.shapes import Rect ->>> from reportlab.lib.colors import red, green ->>> r = Rect(5, 5, 200, 100) ->>> r.fillColor = red ->>> r.strokeColor = green ->>> r.strokeWidth = 3 ->>> -""") - -from reportlab.graphics.shapes import Rect -from reportlab.lib.colors import red, green -d = Drawing(220, 120) -r = Rect(5, 5, 200, 100) -r.fillColor = red -r.strokeColor = green -r.strokeWidth = 3 -d.add(r) -draw(d, "red rectangle with green border") - -disc(""" -Note: In future examples we will omit the import statements. -""") - -disc(""" -All shapes have a number of properties which can be set. -At an interactive prompt, we can use their dumpProperties() -method to list these. -Here's what you can use to configure a Rect: -""") - -eg(""" ->>> r.dumpProperties() -fillColor = Color(1.00,0.00,0.00) -height = 100 -rx = 0 -ry = 0 -strokeColor = Color(0.00,0.50,0.00) -strokeDashArray = None -strokeLineCap = 0 -strokeLineJoin = 0 -strokeMiterLimit = 0 -strokeWidth = 3 -width = 200 -x = 5 -y = 5 ->>> -""") - -disc(""" -Shapes generally have style properties and geometry -properties. -$x$, $y$, $width$ and $height$ are part of the geometry and must -be provided when creating the rectangle, since it does not make -much sense without those properties. -The others are optional and come with sensible defaults. -""") - -disc(""" -You may set other properties on subsequent lines, or by passing them -as optional arguments to the constructor. -We could also have created our rectangle this way: -""") - -eg(""" ->>> r = Rect(5, 5, 200, 100, - fillColor=red, - strokeColor=green, - strokeWidth=3) -""") - -disc(""" -Let's run through the style properties. $fillColor$ is obvious. -$stroke$ is publishing terminology for the edge of a shape; -the stroke has a color, width, possibly a dash pattern, and -some (rarely used) features for what happens when a line turns -a corner. -$rx$ and $ry$ are optional geometric properties and are used to -define the corner radius for a rounded rectangle. -""") - -disc("All the other solid shapes share the same style properties.") - - -heading2("Lines") - -disc(""" -We provide single straight lines, PolyLines and curves. -Lines have all the $stroke*$ properties, but no $fillColor$. -Here are a few Line and PolyLine examples and the corresponding -graphics output: -""") - -eg(""" - Line(50,50, 300,100, - strokeColor=colors.blue, strokeWidth=5) - Line(50,100, 300,50, - strokeColor=colors.red, - strokeWidth=10, - strokeDashArray=[10, 20]) - PolyLine([120,110, 130,150, 140,110, 150,150, 160,110, - 170,150, 180,110, 190,150, 200,110], - strokeWidth=2, - strokeColor=colors.purple) -""") - -d = Drawing(400, 200) -d.add(Line(50,50, 300,100,strokeColor=colors.blue, strokeWidth=5)) -d.add(Line(50,100, 300,50, - strokeColor=colors.red, - strokeWidth=10, - strokeDashArray=[10, 20])) -d.add(PolyLine([120,110, 130,150, 140,110, 150,150, 160,110, - 170,150, 180,110, 190,150, 200,110], - strokeWidth=2, - strokeColor=colors.purple)) -draw(d, "Line and PolyLine examples") - - -heading2("Strings") - -disc(""" -The ReportLab Graphics package is not designed for fancy text -layout, but it can place strings at desired locations and with -left/right/center alignment. -Let's specify a $String$ object and look at its properties: -""") - -eg(""" ->>> s = String(10, 50, 'Hello World') ->>> s.dumpProperties() -fillColor = Color(0.00,0.00,0.00) -fontName = Times-Roman -fontSize = 10 -text = Hello World -textAnchor = start -x = 10 -y = 50 ->>> -""") - -disc(""" -Strings have a textAnchor property, which may have one of the -values 'start', 'middle', 'end'. -If this is set to 'start', x and y relate to the start of the -string, and so on. -This provides an easy way to align text. -""") - -disc(""" -Strings use a common font standard: the Type 1 Postscript fonts -present in Acrobat Reader. -We can thus use the basic 14 fonts in ReportLab and get accurate -metrics for them. -We have recently also added support for extra Type 1 fonts -and the renderers all know how to render Type 1 fonts. -""") - -##Until now we have worked with bitmap renderers which have to use -##TrueType fonts and which make some substitutions; this could lead -##to differences in text wrapping or even the number of labels on -##a chart between renderers. - -disc(""" -Here is a more fancy example using the code snippet below. -Please consult the ReportLab User Guide to see how non-standard -like 'LettErrorRobot-Chrome' fonts are being registered! -""") - -eg(""" - d = Drawing(400, 200) - for size in range(12, 36, 4): - d.add(String(10+size*2, 10+size*2, 'Hello World', - fontName='Times-Roman', - fontSize=size)) - - d.add(String(130, 120, 'Hello World', - fontName='Courier', - fontSize=36)) - - d.add(String(150, 160, 'Hello World', - fontName='LettErrorRobot-Chrome', - fontSize=36)) -""") - -from reportlab.pdfbase import pdfmetrics -from reportlab import rl_config -rl_config.warnOnMissingFontGlyphs = 0 -afmFile, pfbFile = getJustFontPaths() -T1face = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile) -T1faceName = 'LettErrorRobot-Chrome' -pdfmetrics.registerTypeFace(T1face) -T1font = pdfmetrics.Font(T1faceName, T1faceName, 'WinAnsiEncoding') -pdfmetrics.registerFont(T1font) - -d = Drawing(400, 200) -for size in range(12, 36, 4): - d.add(String(10+size*2, 10+size*2, 'Hello World', - fontName='Times-Roman', - fontSize=size)) - -d.add(String(130, 120, 'Hello World', - fontName='Courier', - fontSize=36)) - -d.add(String(150, 160, 'Hello World', - fontName='LettErrorRobot-Chrome', - fontSize=36)) - -draw(d, 'fancy font example') - - -heading2("""Paths""") - -disc(""" -Postscript paths are a widely understood concept in graphics. -They are not implemented in $reportlab/graphics$ as yet, but they -will be, soon. -""") - -# NB This commented out section is for 'future compatibility' - paths haven't -# been implemented yet, but when they are we can uncomment this back in. - - ##disc("""Postscript paths are a widely understood concept in graphics. A Path - ## is a way of defining a region in space. You put an imaginary pen down, - ## draw straight and curved segments, and even pick the pen up and move - ## it. At the end of this you have described a region, which may consist - ## of several distinct close shapes or unclosed lines. At the end, this - ## 'path' is 'stroked and filled' according to its properties. A Path has - ## the same style properties as a solid shape. It can be used to create - ## any irregular shape.""") - ## - ##disc("""In Postscript-based imaging models such as PDF, Postscript and SVG, - ## everything is done with paths. All the specific shapes covered above - ## are instances of paths; even text strings (which are shapes in which - ## each character is an outline to be filled). Here we begin creating a - ## path with a straight line and a bezier curve:""") - ## - ##eg(""" - ##>>> P = Path(0,0, strokeWidth=3, strokeColor=red) - ##>>> P.lineTo(0, 50) - ##>>> P.curveTo(10,50,80,80,100,30) - ##>>> - ##""") - - ##disc("""As well as being the only way to draw complex shapes, paths offer some - ## performance advantages in renderers which support them. If you want to - ## create a scatter plot with 5000 blue circles of different sizes, you - ## can create 5000 circles, or one path object. With the latter, you only - ## need to set the color and line width once. PINGO just remembers the - ## drawing sequence, and writes it out into the file. In renderers which - ## do not support paths, the renderer will still have to decompose it - ## into 5000 circles so you won't save anything.""") - ## - ##disc("""Note that our current path implementation is an approximation; it - ## should be finished off accurately for PDF and PS.""") - - -heading2("Groups") - -disc(""" -Finally, we have Group objects. -A group has a list of contents, which are other nodes. -It can also apply a transformation - its contents can be rotated, -scaled or shifted. -If you know the math, you can set the transform directly. -Otherwise it provides methods to rotate, scale and so on. -Here we make a group which is rotated and translated: -""") - -eg(""" ->>> g =Group(shape1, shape2, shape3) ->>> g.rotate(30) ->>> g.translate(50, 200) -""") - -disc(""" -Groups provide a tool for reuse. -You can make a bunch of shapes to represent some component - say, -a coordinate system - and put them in one group called "Axis". -You can then put that group into other groups, each with a different -translation and rotation, and you get a bunch of axis. -It is still the same group, being drawn in different places. -""") - -disc("""Let's do this with some only slightly more code:""") - -eg(""" - d = Drawing(400, 200) - - Axis = Group( - Line(0,0,100,0), # x axis - Line(0,0,0,50), # y axis - Line(0,10,10,10), # ticks on y axis - Line(0,20,10,20), - Line(0,30,10,30), - Line(0,40,10,40), - Line(10,0,10,10), # ticks on x axis - Line(20,0,20,10), - Line(30,0,30,10), - Line(40,0,40,10), - Line(50,0,50,10), - Line(60,0,60,10), - Line(70,0,70,10), - Line(80,0,80,10), - Line(90,0,90,10), - String(20, 35, 'Axes', fill=colors.black) - ) - - firstAxisGroup = Group(Axis) - firstAxisGroup.translate(10,10) - d.add(firstAxisGroup) - - secondAxisGroup = Group(Axis) - secondAxisGroup.translate(150,10) - secondAxisGroup.rotate(15) - - d.add(secondAxisGroup) - - thirdAxisGroup = Group(Axis, - transform=mmult(translate(300,10), - rotate(30))) - d.add(thirdAxisGroup) -""") - -d = Drawing(400, 200) -Axis = Group( - Line(0,0,100,0), # x axis - Line(0,0,0,50), # y axis - Line(0,10,10,10), # ticks on y axis - Line(0,20,10,20), - Line(0,30,10,30), - Line(0,40,10,40), - Line(10,0,10,10), # ticks on x axis - Line(20,0,20,10), - Line(30,0,30,10), - Line(40,0,40,10), - Line(50,0,50,10), - Line(60,0,60,10), - Line(70,0,70,10), - Line(80,0,80,10), - Line(90,0,90,10), - String(20, 35, 'Axes', fill=colors.black) - ) -firstAxisGroup = Group(Axis) -firstAxisGroup.translate(10,10) -d.add(firstAxisGroup) -secondAxisGroup = Group(Axis) -secondAxisGroup.translate(150,10) -secondAxisGroup.rotate(15) -d.add(secondAxisGroup) -thirdAxisGroup = Group(Axis, - transform=mmult(translate(300,10), - rotate(30))) -d.add(thirdAxisGroup) -draw(d, "Groups examples") \ No newline at end of file diff --git a/bin/reportlab/docs/graphguide/ch4_widgets.py b/bin/reportlab/docs/graphguide/ch4_widgets.py deleted file mode 100644 index eed115d6a86..00000000000 --- a/bin/reportlab/docs/graphguide/ch4_widgets.py +++ /dev/null @@ -1,422 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch4_widgets.py - -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.graphics.shapes import * -from reportlab.graphics.widgets import signsandsymbols - -heading1("Widgets") - -disc(""" -We now describe widgets and how they relate to shapes. -Using many examples it is shown how widgets make reusable -graphics components. -""") - - -heading2("Shapes vs. Widgets") - -disc("""Up until now, Drawings have been 'pure data'. There is no code in them - to actually do anything, except assist the programmer in checking and - inspecting the drawing. In fact, that's the cornerstone of the whole - concept and is what lets us achieve portability - a renderer only - needs to implement the primitive shapes.""") - -disc("""We want to build reusable graphic objects, including a powerful chart - library. To do this we need to reuse more tangible things than - rectangles and circles. We should be able to write objects for other - to reuse - arrows, gears, text boxes, UML diagram nodes, even fully - fledged charts.""") - -disc(""" -The Widget standard is a standard built on top of the shapes module. -Anyone can write new widgets, and we can build up libraries of them. -Widgets support the $getProperties()$ and $setProperties()$ methods, -so you can inspect and modify as well as document them in a uniform -way. -""") - -bullet("A widget is a reusable shape ") -bullet("""it can be initialized with no arguments - when its $draw()$ method is called it creates a primitive Shape or a - Group to represent itself""") -bullet("""It can have any parameters you want, and they can drive the way it is - drawn""") -bullet("""it has a $demo()$ method which should return an attractively drawn - example of itself in a 200x100 rectangle. This is the cornerstone of - the automatic documentation tools. The $demo()$ method should also have - a well written docstring, since that is printed too!""") - -disc("""Widgets run contrary to the idea that a drawing is just a bundle of - shapes; surely they have their own code? The way they work is that a - widget can convert itself to a group of primitive shapes. If some of - its components are themselves widgets, they will get converted too. - This happens automatically during rendering; the renderer will not see - your chart widget, but just a collection of rectangles, lines and - strings. You can also explicitly 'flatten out' a drawing, causing all - widgets to be converted to primitives.""") - - -heading2("Using a Widget") - -disc(""" -Let's imagine a simple new widget. -We will use a widget to draw a face, then show how it was implemented.""") - -eg(""" ->>> from reportlab.lib import colors ->>> from reportlab.graphics import shapes ->>> from reportlab.graphics import widgetbase ->>> from reportlab.graphics import renderPDF ->>> d = shapes.Drawing(200, 100) ->>> f = widgetbase.Face() ->>> f.skinColor = colors.yellow ->>> f.mood = "sad" ->>> d.add(f) ->>> renderPDF.drawToFile(d, 'face.pdf', 'A Face') -""") - -from reportlab.graphics import widgetbase -d = Drawing(200, 120) -f = widgetbase.Face() -f.x = 50 -f.y = 10 -f.skinColor = colors.yellow -f.mood = "sad" -d.add(f) -draw(d, 'A sample widget') - -disc(""" -Let's see what properties it has available, using the $setProperties()$ -method we have seen earlier: -""") - -eg(""" ->>> f.dumpProperties() -eyeColor = Color(0.00,0.00,1.00) -mood = sad -size = 80 -skinColor = Color(1.00,1.00,0.00) -x = 10 -y = 10 ->>> -""") - -disc(""" -One thing which seems strange about the above code is that we did not -set the size or position when we made the face. -This is a necessary trade-off to allow a uniform interface for -constructing widgets and documenting them - they cannot require -arguments in their $__init__()$ method. -Instead, they are generally designed to fit in a 200 x 100 -window, and you move or resize them by setting properties such as -x, y, width and so on after creation. -""") - -disc(""" -In addition, a widget always provides a $demo()$ method. -Simple ones like this always do something sensible before setting -properties, but more complex ones like a chart would not have any -data to plot. -The documentation tool calls $demo()$ so that your fancy new chart -class can create a drawing showing what it can do. -""") - -disc(""" -Here are a handful of simple widgets available in the module -signsandsymbols.py: -""") - -from reportlab.graphics.shapes import Drawing -from reportlab.graphics.widgets import signsandsymbols - -d = Drawing(230, 230) - -ne = signsandsymbols.NoEntry() -ds = signsandsymbols.DangerSign() -fd = signsandsymbols.FloppyDisk() -ns = signsandsymbols.NoSmoking() - -ne.x, ne.y = 10, 10 -ds.x, ds.y = 120, 10 -fd.x, fd.y = 10, 120 -ns.x, ns.y = 120, 120 - -d.add(ne) -d.add(ds) -d.add(fd) -d.add(ns) - -draw(d, 'A few samples from signsandsymbols.py') - -disc(""" -And this is the code needed to generate them as seen in the drawing above: -""") - -eg(""" -from reportlab.graphics.shapes import Drawing -from reportlab.graphics.widgets import signsandsymbols - -d = Drawing(230, 230) - -ne = signsandsymbols.NoEntry() -ds = signsandsymbols.DangerSign() -fd = signsandsymbols.FloppyDisk() -ns = signsandsymbols.NoSmoking() - -ne.x, ne.y = 10, 10 -ds.x, ds.y = 120, 10 -fd.x, fd.y = 10, 120 -ns.x, ns.y = 120, 120 - -d.add(ne) -d.add(ds) -d.add(fd) -d.add(ns) -""") - - -heading2("Compound Widgets") - -disc("""Let's imagine a compound widget which draws two faces side by side. - This is easy to build when you have the Face widget.""") - -eg(""" ->>> tf = widgetbase.TwoFaces() ->>> tf.faceOne.mood -'happy' ->>> tf.faceTwo.mood -'sad' ->>> tf.dumpProperties() -faceOne.eyeColor = Color(0.00,0.00,1.00) -faceOne.mood = happy -faceOne.size = 80 -faceOne.skinColor = None -faceOne.x = 10 -faceOne.y = 10 -faceTwo.eyeColor = Color(0.00,0.00,1.00) -faceTwo.mood = sad -faceTwo.size = 80 -faceTwo.skinColor = None -faceTwo.x = 100 -faceTwo.y = 10 ->>> -""") - -disc("""The attributes 'faceOne' and 'faceTwo' are deliberately exposed so you - can get at them directly. There could also be top-level attributes, - but there aren't in this case.""") - - -heading2("Verifying Widgets") - -disc("""The widget designer decides the policy on verification, but by default - they work like shapes - checking every assignment - if the designer - has provided the checking information.""") - - -heading2("Implementing Widgets") - -disc("""We tried to make it as easy to implement widgets as possible. Here's - the code for a Face widget which does not do any type checking:""") - -eg(""" -class Face(Widget): - \"\"\"This draws a face with two eyes, mouth and nose.\"\"\" - - def __init__(self): - self.x = 10 - self.y = 10 - self.size = 80 - self.skinColor = None - self.eyeColor = colors.blue - self.mood = 'happy' - - def draw(self): - s = self.size # abbreviate as we will use this a lot - g = shapes.Group() - g.transform = [1,0,0,1,self.x, self.y] - # background - g.add(shapes.Circle(s * 0.5, s * 0.5, s * 0.5, - fillColor=self.skinColor)) - # CODE OMITTED TO MAKE MORE SHAPES - return g -""") - -disc("""We left out all the code to draw the shapes in this document, but you - can find it in the distribution in $widgetbase.py$.""") - -disc("""By default, any attribute without a leading underscore is returned by - setProperties. This is a deliberate policy to encourage consistent - coding conventions.""") - -disc("""Once your widget works, you probably want to add support for - verification. This involves adding a dictionary to the class called - $_verifyMap$, which map from attribute names to 'checking functions'. - The $widgetbase.py$ module defines a bunch of checking functions with names - like $isNumber$, $isListOfShapes$ and so on. You can also simply use $None$, - which means that the attribute must be present but can have any type. - And you can and should write your own checking functions. We want to - restrict the "mood" custom attribute to the values "happy", "sad" or - "ok". So we do this:""") - -eg(""" -class Face(Widget): - \"\"\"This draws a face with two eyes. It exposes a - couple of properties to configure itself and hides - all other details\"\"\" - def checkMood(moodName): - return (moodName in ('happy','sad','ok')) - _verifyMap = { - 'x': shapes.isNumber, - 'y': shapes.isNumber, - 'size': shapes.isNumber, - 'skinColor':shapes.isColorOrNone, - 'eyeColor': shapes.isColorOrNone, - 'mood': checkMood - } -""") - -disc("""This checking will be performed on every attribute assignment; or, if - $config.shapeChecking$ is off, whenever you call $myFace.verify()$.""") - - -heading2("Documenting Widgets") - -disc(""" -We are working on a generic tool to document any Python package or -module; this is already checked into ReportLab and will be used to -generate a reference for the ReportLab package. -When it encounters widgets, it adds extra sections to the -manual including:""") - -bullet("the doc string for your widget class ") -bullet("the code snippet from your demo() method, so people can see how to use it") -bullet("the drawing produced by the demo() method ") -bullet("the property dump for the widget in the drawing. ") - -disc(""" -This tool will mean that we can have guaranteed up-to-date -documentation on our widgets and charts, both on the web site -and in print; and that you can do the same for your own widgets, -too! -""") - - -heading2("Widget Design Strategies") - -disc("""We could not come up with a consistent architecture for designing - widgets, so we are leaving that problem to the authors! If you do not - like the default verification strategy, or the way - $setProperties/getProperties$ works, you can override them yourself.""") - -disc("""For simple widgets it is recommended that you do what we did above: - select non-overlapping properties, initialize every property on - $__init__$ and construct everything when $draw()$ is called. You can - instead have $__setattr__$ hooks and have things updated when certain - attributes are set. Consider a pie chart. If you want to expose the - individual wedges, you might write code like this:""") - -eg(""" -from reportlab.graphics.charts import piecharts -pc = piecharts.Pie() -pc.defaultColors = [navy, blue, skyblue] #used in rotation -pc.data = [10,30,50,25] -pc.slices[7].strokeWidth = 5 -""") -#removed 'pc.backColor = yellow' from above code example - -disc("""The last line is problematic as we have only created four wedges - in - fact we might not have created them yet. Does $pc.wedges[7]$ raise an - error? Is it a prescription for what should happen if a seventh wedge - is defined, used to override the default settings? We dump this - problem squarely on the widget author for now, and recommend that you - get a simple one working before exposing 'child objects' whose - existence depends on other properties' values :-)""") - -disc("""We also discussed rules by which parent widgets could pass properties - to their children. There seems to be a general desire for a global way - to say that 'all wedges get their lineWidth from the lineWidth of - their parent' without a lot of repetitive coding. We do not have a - universal solution, so again leave that to widget authors. We hope - people will experiment with push-down, pull-down and pattern-matching - approaches and come up with something nice. In the meantime, we - certainly can write monolithic chart widgets which work like the ones - in, say, Visual Basic and Delphi.""") - -disc("""For now have a look at the following sample code using an early - version of a pie chart widget and the output it generates:""") - -eg(""" -from reportlab.lib.colors import * -from reportlab.graphics import shapes,renderPDF -from reportlab.graphics.charts.piecharts import Pie - -d = Drawing(400,200) -d.add(String(100,175,"Without labels", textAnchor="middle")) -d.add(String(300,175,"With labels", textAnchor="middle")) - -pc = Pie() -pc.x = 25 -pc.y = 50 -pc.data = [10,20,30,40,50,60] -pc.slices[0].popout = 5 -d.add(pc, 'pie1') - -pc2 = Pie() -pc2.x = 150 -pc2.y = 50 -pc2.data = [10,20,30,40,50,60] -pc2.labels = ['a','b','c','d','e','f'] -d.add(pc2, 'pie2') - -pc3 = Pie() -pc3.x = 275 -pc3.y = 50 -pc3.data = [10,20,30,40,50,60] -pc3.labels = ['a','b','c','d','e','f'] -pc3.wedges.labelRadius = 0.65 -pc3.wedges.fontName = "Helvetica-Bold" -pc3.wedges.fontSize = 16 -pc3.wedges.fontColor = colors.yellow -d.add(pc3, 'pie3') -""") - -# Hack to force a new paragraph before the todo() :-( -disc("") - -from reportlab.lib.colors import * -from reportlab.graphics import shapes,renderPDF -from reportlab.graphics.charts.piecharts import Pie - -d = Drawing(400,200) -d.add(String(100,175,"Without labels", textAnchor="middle")) -d.add(String(300,175,"With labels", textAnchor="middle")) - -pc = Pie() -pc.x = 25 -pc.y = 50 -pc.data = [10,20,30,40,50,60] -pc.slices[0].popout = 5 -d.add(pc, 'pie1') - -pc2 = Pie() -pc2.x = 150 -pc2.y = 50 -pc2.data = [10,20,30,40,50,60] -pc2.labels = ['a','b','c','d','e','f'] -d.add(pc2, 'pie2') - -pc3 = Pie() -pc3.x = 275 -pc3.y = 50 -pc3.data = [10,20,30,40,50,60] -pc3.labels = ['a','b','c','d','e','f'] -pc3.slices.labelRadius = 0.65 -pc3.slices.fontName = "Helvetica-Bold" -pc3.slices.fontSize = 16 -pc3.slices.fontColor = colors.yellow -d.add(pc3, 'pie3') - -draw(d, 'Some sample Pies') \ No newline at end of file diff --git a/bin/reportlab/docs/graphguide/ch5_charts.py b/bin/reportlab/docs/graphguide/ch5_charts.py deleted file mode 100644 index d37df78d094..00000000000 --- a/bin/reportlab/docs/graphguide/ch5_charts.py +++ /dev/null @@ -1,1229 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/ch5_charts.py - -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.graphics.shapes import * - -heading1("Charts") - -disc(""" -The motivation for much of this is to create a flexible chart -package. -This chapter presents a treatment of the ideas behind our charting -model, what the design goals are and what components of the chart -package already exist. -""") - - -heading2("Design Goals") - -disc("Here are some of the design goals: ") - -disc("Make simple top-level use really simple ") -disc("""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.""") - -disc("Allow precise positioning ") -disc("""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.""") - -disc("""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.""") - -disc("Control child elements individually or as a group") -disc("""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:""") - -eg(""" -d = Drawing(400,200) -pc = Pie() -pc.x = 150 -pc.y = 50 -pc.data = [10,20,30,40,50,60] -pc.labels = ['a','b','c','d','e','f'] -pc.slices.strokeWidth=0.5 -pc.slices[3].popout = 20 -pc.slices[3].strokeWidth = 2 -pc.slices[3].strokeDashArray = [2,2] -pc.slices[3].labelRadius = 1.75 -pc.slices[3].fontColor = colors.red -d.add(pc, '') -""") - -disc("""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.""") - -disc("Only expose things you should change ") -disc("""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.""") - -disc("Composition and component based ") -disc("""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...""") - -eg(""" -c = MyChartWithLegend() -c.legend = MyNewLegendClass() # just change it -c.legend.swatchRadius = 5 # set a property only relevant to the new one -c.data = [10,20,30] # and then configure as usual... -""") - -disc("""...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.""") - -disc("""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.""") - -disc("Multiples ") -disc("""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("""(If you want to see the image, it is available on our website at -http://www.reportlab.com/demos/provencio.pdf)""") - - -##heading2("Key Concepts and Components") -heading2("Overview") - -disc("""A chart or plot is an object which is placed on a drawing; it is not - itself a drawing. You can thus control where it goes, put several on - the same drawing, or add annotations.""") - -disc("""Charts have two axes; axes may be Value or Category axes. Axes in turn - have a Labels property which lets you configure all text labels or - each one individually. Most of the configuration details which vary - from chart to chart relate to axis properties, or axis labels.""") - -disc("""Objects expose properties through the interfaces discussed in the - previous section; these are all optional and are there to let the end - user configure the appearance. Things which must be set for a chart to - work, and essential communication between a chart and its components, - are handled through methods.""") - -disc("""You can subclass any chart component and use your replacement instead - of the original provided you implement the essential methods and - properties.""") - - -heading2("Labels") - -disc(""" -A label is a string of text attached to some chart element. -They are used on axes, for titles or alongside axes, or attached -to individual data points. -Labels may contain newline characters, but only one font. -""") - -disc("""The text and 'origin' of a label are typically set by its parent - object. They are accessed by methods rather than properties. Thus, the - X axis decides the 'reference point' for each tickmark label and the - numeric or date text for each label. However, the end user can set - properties of the label (or collection of labels) directly to affect - its position relative to this origin and all of its formatting.""") - -eg(""" -from reportlab.graphics import shapes -from reportlab.graphics.charts.textlabels import Label - -d = Drawing(200, 100) - -# mark the origin of the label -d.add(Circle(100,90, 5, fillColor=colors.green)) - -lab = Label() -lab.setOrigin(100,90) -lab.boxAnchor = 'ne' -lab.angle = 45 -lab.dx = 0 -lab.dy = -20 -lab.boxStrokeColor = colors.green -lab.setText('Some\nMulti-Line\nLabel') - -d.add(lab) -""") - - -from reportlab.graphics import shapes -from reportlab.graphics.charts.textlabels import Label - -d = Drawing(200, 100) - -# mark the origin of the label -d.add(Circle(100,90, 5, fillColor=colors.green)) - -lab = Label() -lab.setOrigin(100,90) -lab.boxAnchor = 'ne' -lab.angle = 45 -lab.dx = 0 -lab.dy = -20 -lab.boxStrokeColor = colors.green -lab.setText('Some\nMulti-Line\nLabel') - -d.add(lab) - -draw(d, 'Label example') - - - -disc(""" -In the drawing above, the label is defined relative to the green blob. -The text box should have its north-east corner ten points down from -the origin, and be rotated by 45 degrees about that corner. -""") - -disc(""" -At present labels have the following properties, which we believe are -sufficient for all charts we have seen to date: -""") - -disc("") - -data=[["Property", "Meaning"], - ["dx", """The label's x displacement."""], - ["dy", """The label's y displacement."""], - ["angle", """The angle of rotation (counterclockwise) applied to the label."""], - ["boxAnchor", "The label's box anchor, one of 'n', 'e', 'w', 's', 'ne', 'nw', 'se', 'sw'."], - ["textAnchor", """The place where to anchor the label's text, one of 'start', 'middle', 'end'."""], - ["boxFillColor", """The fill color used in the label's box."""], - ["boxStrokeColor", "The stroke color used in the label's box."], - ["boxStrokeWidth", """The line width of the label's box."""], - ["fontName", """The label's font name."""], - ["fontSize", """The label's font size."""], - ["leading", """The leading value of the label's text lines."""], - ["x", """The X-coordinate of the reference point."""], - ["y", """The Y-coordinate of the reference point."""], - ["width", """The label's width."""], - ["height", """The label's height."""] - ] -t=Table(data, colWidths=(100,330)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,0),'Times-Bold',10,12), - ('FONT',(0,1),(0,-1),'Courier',8,8), - ('FONT',(1,1),(1,-1),'Times-Roman',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - Label properties""") - -disc(""" -To see many more examples of $Label$ objects with different -combinations of properties, please have a look into the -ReportLab test suite in the folder $reportlab/test$, run the -script $test_charts_textlabels.py$ and look at the PDF document -it generates! -""") - - - -heading2("Axes") - -disc(""" -We identify two basic kinds of axes - Value and Category -ones. -Both come in horizontal and vertical flavors. -Both can be subclassed to make very specific kinds of axis. -For example, if you have complex rules for which dates to display -in a time series application, or want irregular scaling, you override -the axis and make a new one. -""") - -disc(""" -Axes are responsible for determining the mapping from data to image -coordinates; transforming points on request from the chart; drawing -themselves and their tickmarks, gridlines and axis labels. -""") - -disc(""" -This drawing shows two axes, one of each kind, which have been created -directly without reference to any chart: -""") - - -from reportlab.graphics import shapes -from reportlab.graphics.charts.axes import XCategoryAxis,YValueAxis - -drawing = Drawing(400, 200) - -data = [(10, 20, 30, 40), (15, 22, 37, 42)] - -xAxis = XCategoryAxis() -xAxis.setPosition(75, 75, 300) -xAxis.configure(data) -xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -xAxis.labels.boxAnchor = 'n' -xAxis.labels[3].dy = -15 -xAxis.labels[3].angle = 30 -xAxis.labels[3].fontName = 'Times-Bold' - -yAxis = YValueAxis() -yAxis.setPosition(50, 50, 125) -yAxis.configure(data) - -drawing.add(xAxis) -drawing.add(yAxis) - -draw(drawing, 'Two isolated axes') - - -disc("Here is the code that created them: ") - -eg(""" -from reportlab.graphics import shapes -from reportlab.graphics.charts.axes import XCategoryAxis,YValueAxis - -drawing = Drawing(400, 200) - -data = [(10, 20, 30, 40), (15, 22, 37, 42)] - -xAxis = XCategoryAxis() -xAxis.setPosition(75, 75, 300) -xAxis.configure(data) -xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -xAxis.labels.boxAnchor = 'n' -xAxis.labels[3].dy = -15 -xAxis.labels[3].angle = 30 -xAxis.labels[3].fontName = 'Times-Bold' - -yAxis = YValueAxis() -yAxis.setPosition(50, 50, 125) -yAxis.configure(data) - -drawing.add(xAxis) -drawing.add(yAxis) -""") - -disc(""" -Remember that, usually, you won't have to create axes directly; -when using a standard chart, it comes with ready-made axes. -The methods are what the chart uses to configure it and take care -of the geometry. -However, we will talk through them in detail below. -The orthogonally dual axes to those we describe have essentially -the same properties, except for those refering to ticks. -""") - - -heading3("XCategoryAxis class") - -disc(""" -A Category Axis doesn't really have a scale; it just divides itself -into equal-sized buckets. -It is simpler than a value axis. -The chart (or programmer) sets its location with the method -$setPosition(x, y, length)$. -The next stage is to show it the data so that it can configure -itself. -This is easy for a category axis - it just counts the number of -data points in one of the data series. The $reversed$ attribute (if 1) -indicates that the categories should be reversed. -When the drawing is drawn, the axis can provide some help to the -chart with its $scale()$ method, which tells the chart where -a given category begins and ends on the page. -We have not yet seen any need to let people override the widths -or positions of categories. -""") - -disc("An XCategoryAxis has the following editable properties:") - -disc("") - -data=[["Property", "Meaning"], - ["visible", """Should the axis be drawn at all? Sometimes you don't want -to display one or both axes, but they still need to be there as -they manage the scaling of points."""], - ["strokeColor", "Color of the axis"], - ["strokeDashArray", """Whether to draw axis with a dash and, if so, what kind. -Defaults to None"""], - ["strokeWidth", "Width of axis in points"], - ["tickUp", """How far above the axis should the tick marks protrude? -(Note that making this equal to chart height gives you a gridline)"""], - ["tickDown", """How far below the axis should the tick mark protrude?"""], - ["categoryNames", """Either None, or a list of strings. This should have the -same length as each data series."""], - ["labels", """A collection of labels for the tick marks. By default the 'north' -of each text label (i.e top centre) is positioned 5 points down -from the centre of each category on the axis. You may redefine -any property of the whole label group or of any one label. If -categoryNames=None, no labels are drawn."""], - ["title", """Not Implemented Yet. This needs to be like a label, but also -lets you set the text directly. It would have a default -location below the axis."""]] -t=Table(data, colWidths=(100,330)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,0),'Times-Bold',10,12), - ('FONT',(0,1),(0,-1),'Courier',8,8), - ('FONT',(1,1),(1,-1),'Times-Roman',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - XCategoryAxis properties""") - - -heading3("YValueAxis") - -disc(""" -The left axis in the diagram is a YValueAxis. -A Value Axis differs from a Category Axis in that each point along -its length corresponds to a y value in chart space. -It is the job of the axis to configure itself, and to convert Y values -from chart space to points on demand to assist the parent chart in -plotting. -""") - -disc(""" -$setPosition(x, y, length)$ and $configure(data)$ work exactly as -for a category axis. -If you have not fully specified the maximum, minimum and tick -interval, then $configure()$ results in the axis choosing suitable -values. -Once configured, the value axis can convert y data values to drawing -space with the $scale()$ method. -Thus: -""") - -eg(""" ->>> yAxis = YValueAxis() ->>> yAxis.setPosition(50, 50, 125) ->>> data = [(10, 20, 30, 40),(15, 22, 37, 42)] ->>> yAxis.configure(data) ->>> yAxis.scale(10) # should be bottom of chart -50.0 ->>> yAxis.scale(40) # should be near the top -167.1875 ->>> -""") - -disc("""By default, the highest data point is aligned with the top of the - axis, the lowest with the bottom of the axis, and the axis choose - 'nice round numbers' for its tickmark points. You may override these - settings with the properties below. """) - -disc("") - -data=[["Property", "Meaning"], - ["visible", """Should the axis be drawn at all? Sometimes you don't want -to display one or both axes, but they still need to be there as -they manage the scaling of points."""], - ["strokeColor", "Color of the axis"], - ["strokeDashArray", """Whether to draw axis with a dash and, if so, what kind. -Defaults to None"""], - ["strokeWidth", "Width of axis in points"], - ["tickLeft", """How far to the left of the axis should the tick marks protrude? -(Note that making this equal to chart height gives you a gridline)"""], - ["tickRight", """How far to the right of the axis should the tick mark protrude?"""], - - ["valueMin", """The y value to which the bottom of the axis should correspond. -Default value is None in which case the axis sets it to the lowest -actual data point (e.g. 10 in the example above). It is common to set -this to zero to avoid misleading the eye."""], - ["valueMax", """The y value to which the top of the axis should correspond. -Default value is None in which case the axis sets it to the highest -actual data point (e.g. 42 in the example above). It is common to set -this to a 'round number' so data bars do not quite reach the top."""], - ["valueStep", """The y change between tick intervals. By default this is -None, and the chart tries to pick 'nice round numbers' which are -just wider than the minimumTickSpacing below."""], - - ["valueSteps", """A list of numbers at which to place ticks."""], - - ["minimumTickSpacing", """This is used when valueStep is set to None, and ignored -otherwise. The designer specified that tick marks should be no -closer than X points apart (based, presumably, on considerations -of the label font size and angle). The chart tries values of the -type 1,2,5,10,20,50,100... (going down below 1 if necessary) until -it finds an interval which is greater than the desired spacing, and -uses this for the step."""], - ["labelTextFormat", """This determines what goes in the labels. Unlike a category -axis which accepts fixed strings, the labels on a ValueAxis are -supposed to be numbers. You may provide either a 'format string' -like '%0.2f' (show two decimal places), or an arbitrary function -which accepts a number and returns a string. One use for the -latter is to convert a timestamp to a readable year-month-day -format."""], - ["title", """Not Implemented Yet. This needs to be like a label, but also -lets you set the text directly. It would have a default -location below the axis."""]] -t=Table(data, colWidths=(100,330)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,0),'Times-Bold',10,12), - ('FONT',(0,1),(0,-1),'Courier',8,8), - ('FONT',(1,1),(1,-1),'Times-Roman',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - YValueAxis properties""") - -disc(""" -The $valueSteps$ property lets you explicitly specify the -tick mark locations, so you don't have to follow regular intervals. -Hence, you can plot month ends and month end dates with a couple of -helper functions, and without needing special time series chart -classes. -The following code show how to create a simple $XValueAxis$ with special -tick intervals. Make sure to set the $valueSteps$ attribute before calling -the configure method! -""") - -eg(""" -from reportlab.graphics.shapes import Drawing -from reportlab.graphics.charts.axes import XValueAxis - -drawing = Drawing(400, 100) - -data = [(10, 20, 30, 40)] - -xAxis = XValueAxis() -xAxis.setPosition(75, 50, 300) -xAxis.valueSteps = [10, 15, 20, 30, 35, 40] -xAxis.configure(data) -xAxis.labels.boxAnchor = 'n' - -drawing.add(xAxis) -""") - - -from reportlab.graphics import shapes -from reportlab.graphics.charts.axes import XValueAxis - -drawing = Drawing(400, 100) - -data = [(10, 20, 30, 40)] - -xAxis = XValueAxis() -xAxis.setPosition(75, 50, 300) -xAxis.valueSteps = [10, 15, 20, 30, 35, 40] -xAxis.configure(data) -xAxis.labels.boxAnchor = 'n' - -drawing.add(xAxis) - -draw(drawing, 'An axis with non-equidistant tick marks') - - -disc(""" -In addition to these properties, all axes classes have three -properties describing how to join two of them to each other. -Again, this is interesting only if you define your own charts -or want to modify the appearance of an existing chart using -such axes. -These properties are listed here only very briefly for now, -but you can find a host of sample functions in the module -$reportlab/graphics/axes.py$ which you can examine... -""") - -disc(""" -One axis is joined to another, by calling the method -$joinToAxis(otherAxis, mode, pos)$ on the first axis, -with $mode$ and $pos$ being the properties described by -$joinAxisMode$ and $joinAxisPos$, respectively. -$'points'$ means to use an absolute value, and $'value'$ -to use a relative value (both indicated by the the -$joinAxisPos$ property) along the axis. -""") - -disc("") - -data=[["Property", "Meaning"], - ["joinAxis", """Join both axes if true."""], - ["joinAxisMode", """Mode used for connecting axis ('bottom', 'top', 'left', 'right', 'value', 'points', None)."""], - ["joinAxisPos", """Position at which to join with other axis."""], - ] -t=Table(data, colWidths=(100,330)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,0),'Times-Bold',10,12), - ('FONT',(0,1),(0,-1),'Courier',8,8), - ('FONT',(1,1),(1,-1),'Times-Roman',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - Axes joining properties""") - - -heading2("Bar Charts") - -disc(""" -This describes our current $VerticalBarChart$ class, which uses the -axes and labels above. -We think it is step in the right direction but is is -far from final. -Note that people we speak to are divided about 50/50 on whether to -call this a 'Vertical' or 'Horizontal' bar chart. -We chose this name because 'Vertical' appears next to 'Bar', so -we take it to mean that the bars rather than the category axis -are vertical. -""") - -disc(""" -As usual, we will start with an example: -""") - -from reportlab.graphics.shapes import Drawing -from reportlab.graphics.charts.barcharts import VerticalBarChart - -drawing = Drawing(400, 200) - -data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 6, 21, 23, 38, 46, 20, 5) - ] - -bc = VerticalBarChart() -bc.x = 50 -bc.y = 50 -bc.height = 125 -bc.width = 300 -bc.data = data -bc.strokeColor = colors.black - -bc.valueAxis.valueMin = 0 -bc.valueAxis.valueMax = 50 -bc.valueAxis.valueStep = 10 - -bc.categoryAxis.labels.boxAnchor = 'ne' -bc.categoryAxis.labels.dx = 8 -bc.categoryAxis.labels.dy = -2 -bc.categoryAxis.labels.angle = 30 -bc.categoryAxis.categoryNames = ['Jan-99','Feb-99','Mar-99', - 'Apr-99','May-99','Jun-99','Jul-99','Aug-99'] - -drawing.add(bc) - -draw(drawing, 'Simple bar chart with two data series') - - -eg(""" - # code to produce the above chart - - from reportlab.graphics.shapes import Drawing - from reportlab.graphics.charts.barcharts import VerticalBarChart - - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 6, 21, 23, 38, 46, 20, 5) - ] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 50 - bc.valueAxis.valueStep = 10 - - bc.categoryAxis.labels.boxAnchor = 'ne' - bc.categoryAxis.labels.dx = 8 - bc.categoryAxis.labels.dy = -2 - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = ['Jan-99','Feb-99','Mar-99', - 'Apr-99','May-99','Jun-99','Jul-99','Aug-99'] - - drawing.add(bc) -""") - -disc(""" -Most of this code is concerned with setting up the axes and -labels, which we have already covered. -Here are the top-level properties of the $VerticalBarChart$ class: -""") - -disc("") - -data=[["Property", "Meaning"], - ["data", """This should be a "list of lists of numbers" or "list of -tuples of numbers". If you have just one series, write it as -data = [(10,20,30,42),]"""], - ["x, y, width, height", """These define the inner 'plot rectangle'. We -highlighted this with a yellow border above. Note that it is -your job to place the chart on the drawing in a way which leaves -room for all the axis labels and tickmarks. We specify this 'inner -rectangle' because it makes it very easy to lay out multiple charts -in a consistent manner."""], - ["strokeColor", """Defaults to None. This will draw a border around the -plot rectangle, which may be useful in debugging. Axes will -overwrite this."""], - ["fillColor", """Defaults to None. This will fill the plot rectangle with -a solid color. (Note that we could implement dashArray etc. -as for any other solid shape)"""], - ["barLabelFormat", """This is a format string or function used for displaying -labels above each bar. They are positioned automatically -above the bar for positive values and below for negative ones."""], - ["useAbsolute", """Defaults to 0. If 1, the three properties below are -absolute values in points (which means you can make a chart -where the bars stick out from the plot rectangle); if 0, -they are relative quantities and indicate the proportional -widths of the elements involved."""], - ["barWidth", """As it says. Defaults to 10."""], - ["groupSpacing", """Defaults to 5. This is the space between each group of -bars. If you have only one series, use groupSpacing and not -barSpacing to split them up. Half of the groupSpacing is used -before the first bar in the chart, and another half at the end."""], - ["barSpacing", """Defaults to 0. This is the spacing between bars in each -group. If you wanted a little gap between green and red bars in -the example above, you would make this non-zero."""], - ["barLabelFormat", """Defaults to None. As with the YValueAxis, if you supply -a function or format string then labels will be drawn next -to each bar showing the numeric value."""], - ["barLabels", """A collection of labels used to format all bar labels. Since -this is a two-dimensional array, you may explicitly format the -third label of the second series using this syntax: - chart.barLabels[(1,2)].fontSize = 12"""], - ["valueAxis", """The value axis, which may be formatted as described -previously."""], - ["categoryAxis", """The category axis, which may be formatted as described -previously."""], - - ["title", """Not Implemented Yet. This needs to be like a label, but also -lets you set the text directly. It would have a default -location below the axis."""]] -t=Table(data, colWidths=(100,330)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,0),'Times-Bold',10,12), - ('FONT',(0,1),(0,-1),'Courier',8,8), - ('FONT',(1,1),(1,-1),'Times-Roman',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - VerticalBarChart properties""") - - -disc(""" -From this table we deduce that adding the following lines to our code -above should double the spacing between bar groups (the $groupSpacing$ -attribute has a default value of five points) and we should also see -some tiny space between bars of the same group ($barSpacing$). -""") - -eg(""" - bc.groupSpacing = 10 - bc.barSpacing = 2.5 -""") - -disc(""" -And, in fact, this is exactly what we can see after adding these -lines to the code above. -Notice how the width of the individual bars has changed as well. -This is because the space added between the bars has to be 'taken' -from somewhere as the total chart width stays unchanged. -""") - -from reportlab.graphics.shapes import Drawing -from reportlab.graphics.charts.barcharts import VerticalBarChart - -drawing = Drawing(400, 200) - -data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 6, 21, 23, 38, 46, 20, 5) - ] - -bc = VerticalBarChart() -bc.x = 50 -bc.y = 50 -bc.height = 125 -bc.width = 300 -bc.data = data -bc.strokeColor = colors.black - -bc.groupSpacing = 10 -bc.barSpacing = 2.5 - -bc.valueAxis.valueMin = 0 -bc.valueAxis.valueMax = 50 -bc.valueAxis.valueStep = 10 - -bc.categoryAxis.labels.boxAnchor = 'ne' -bc.categoryAxis.labels.dx = 8 -bc.categoryAxis.labels.dy = -2 -bc.categoryAxis.labels.angle = 30 -bc.categoryAxis.categoryNames = ['Jan-99','Feb-99','Mar-99', - 'Apr-99','May-99','Jun-99','Jul-99','Aug-99'] - -drawing.add(bc) - -draw(drawing, 'Like before, but with modified spacing') - -disc(""" -Bars labels are automatically displayed for negative values -below the lower end of the bar for positive values -above the upper end of the other ones. -""") - - -##Property Value -##data This should be a "list of lists of numbers" or "list of tuples of numbers". If you have just one series, write it as -##data = [(10,20,30,42),] -## -##x, y, width, height These define the inner 'plot rectangle'. We highlighted this with a yellow border above. Note that it is your job to place the chart on the drawing in a way which leaves room for all the axis labels and tickmarks. We specify this 'inner rectangle' because it makes it very easy to lay out multiple charts in a consistent manner. -##strokeColor Defaults to None. This will draw a border around the plot rectangle, which may be useful in debugging. Axes will overwrite this. -##fillColor Defaults to None. This will fill the plot rectangle with a solid color. (Note that we could implement dashArray etc. as for any other solid shape) -##barLabelFormat This is a format string or function used for displaying labels above each bar. We're working on ways to position these labels so that they work for positive and negative bars. -##useAbsolute Defaults to 0. If 1, the three properties below are absolute values in points (which means you can make a chart where the bars stick out from the plot rectangle); if 0, they are relative quantities and indicate the proportional widths of the elements involved. -##barWidth As it says. Defaults to 10. -##groupSpacing Defaults to 5. This is the space between each group of bars. If you have only one series, use groupSpacing and not barSpacing to split them up. Half of the groupSpacing is used before the first bar in the chart, and another half at the end. -##barSpacing Defaults to 0. This is the spacing between bars in each group. If you wanted a little gap between green and red bars in the example above, you would make this non-zero. -##barLabelFormat Defaults to None. As with the YValueAxis, if you supply a function or format string then labels will be drawn next to each bar showing the numeric value. -##barLabels A collection of labels used to format all bar labels. Since this is a two-dimensional array, you may explicitly format the third label of the second series using this syntax: -## chart.barLabels[(1,2)].fontSize = 12 -## -##valueAxis The value axis, which may be formatted as described previously -##categoryAxis The categoryAxis, which may be formatted as described previously -##title, subTitle Not implemented yet. These would be label-like objects whose text could be set directly and which would appear in sensible locations. For now, you can just place extra strings on the drawing. - - -heading2("Line Charts") - -disc(""" -We consider "Line Charts" to be essentially the same as -"Bar Charts", but with lines instead of bars. -Both share the same pair of Category/Value axes pairs. -This is in contrast to "Line Plots", where both axes are -Value axes. -""") - -disc(""" -The following code and its output shall serve as a simple -example. -More explanation will follow. -For the time being you can also study the output of running -the tool $reportlab/lib/graphdocpy.py$ withough any arguments -and search the generated PDF document for examples of -Line Charts. -""") - -eg(""" -from reportlab.graphics.charts.linecharts import HorizontalLineChart - -drawing = Drawing(400, 200) - -data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) -] - -lc = HorizontalLineChart() -lc.x = 50 -lc.y = 50 -lc.height = 125 -lc.width = 300 -lc.data = data -lc.joinedLines = 1 -catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') -lc.categoryAxis.categoryNames = catNames -lc.categoryAxis.labels.boxAnchor = 'n' -lc.valueAxis.valueMin = 0 -lc.valueAxis.valueMax = 60 -lc.valueAxis.valueStep = 15 -lc.lines[0].strokeWidth = 2 -lc.lines[1].strokeWidth = 1.5 -drawing.add(lc) -""") - -from reportlab.graphics.charts.linecharts import HorizontalLineChart - -drawing = Drawing(400, 200) - -data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) -] - -lc = HorizontalLineChart() -lc.x = 50 -lc.y = 50 -lc.height = 125 -lc.width = 300 -lc.data = data -lc.joinedLines = 1 -catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') -lc.categoryAxis.categoryNames = catNames -lc.categoryAxis.labels.boxAnchor = 'n' -lc.valueAxis.valueMin = 0 -lc.valueAxis.valueMax = 60 -lc.valueAxis.valueStep = 15 -lc.lines[0].strokeWidth = 2 -lc.lines[1].strokeWidth = 1.5 -drawing.add(lc) - -draw(drawing, 'HorizontalLineChart sample') - - -disc("") -todo("Add properties table.") - - -heading2("Line Plots") - -disc(""" -Below we show a more complex example of a Line Plot that -also uses some experimental features like line markers -placed at each data point. -""") - -eg(""" -from reportlab.graphics.charts.lineplots import LinePlot -from reportlab.graphics.widgets.markers import makeMarker - -drawing = Drawing(400, 200) - -data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) -] - -lp = LinePlot() -lp.x = 50 -lp.y = 50 -lp.height = 125 -lp.width = 300 -lp.data = data -lp.joinedLines = 1 -lp.lines[0].symbol = makeMarker('FilledCircle') -lp.lines[1].symbol = makeMarker('Circle') -lp.lineLabelFormat = '%2.0f' -lp.strokeColor = colors.black -lp.xValueAxis.valueMin = 0 -lp.xValueAxis.valueMax = 5 -lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5] -lp.xValueAxis.labelTextFormat = '%2.1f' -lp.yValueAxis.valueMin = 0 -lp.yValueAxis.valueMax = 7 -lp.yValueAxis.valueSteps = [1, 2, 3, 5, 6] - -drawing.add(lp) -""") - - -from reportlab.graphics.charts.lineplots import LinePlot -from reportlab.graphics.widgets.markers import makeMarker - -drawing = Drawing(400, 200) - -data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) -] - -lp = LinePlot() -lp.x = 50 -lp.y = 50 -lp.height = 125 -lp.width = 300 -lp.data = data -lp.joinedLines = 1 -lp.lines[0].symbol = makeMarker('FilledCircle') -lp.lines[1].symbol = makeMarker('Circle') -lp.lineLabelFormat = '%2.0f' -lp.strokeColor = colors.black -lp.xValueAxis.valueMin = 0 -lp.xValueAxis.valueMax = 5 -lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5] -lp.xValueAxis.labelTextFormat = '%2.1f' -lp.yValueAxis.valueMin = 0 -lp.yValueAxis.valueMax = 7 -lp.yValueAxis.valueSteps = [1, 2, 3, 5, 6] - -drawing.add(lp) - -draw(drawing, 'LinePlot sample') - - - -disc("") -todo("Add properties table.") - - - -heading2("Pie Charts") - -disc(""" -We've already seen a pie chart example above. -This is provisional but seems to do most things. -At the very least we need to change the name. -For completeness we will cover it here. -""") - -eg(""" -from reportlab.graphics.charts.piecharts import Pie - -d = Drawing(200, 100) - -pc = Pie() -pc.x = 65 -pc.y = 15 -pc.width = 70 -pc.height = 70 -pc.data = [10,20,30,40,50,60] -pc.labels = ['a','b','c','d','e','f'] - -pc.slices.strokeWidth=0.5 -pc.slices[3].popout = 10 -pc.slices[3].strokeWidth = 2 -pc.slices[3].strokeDashArray = [2,2] -pc.slices[3].labelRadius = 1.75 -pc.slices[3].fontColor = colors.red - -d.add(pc) -""") - -from reportlab.graphics.charts.piecharts import Pie - -d = Drawing(200, 100) - -pc = Pie() -pc.x = 65 -pc.y = 15 -pc.width = 70 -pc.height = 70 -pc.data = [10,20,30,40,50,60] -pc.labels = ['a','b','c','d','e','f'] - -pc.slices.strokeWidth=0.5 -pc.slices[3].popout = 10 -pc.slices[3].strokeWidth = 2 -pc.slices[3].strokeDashArray = [2,2] -pc.slices[3].labelRadius = 1.75 -pc.slices[3].fontColor = colors.red - -d.add(pc) - -draw(d, 'A bare bones pie chart') - -disc(""" -Properties are covered below. -The pie has a 'wedges' collection and we document wedge properties -in the same table. -This was invented before we finished the $Label$ class and will -probably be reworked to use such labels shortly. -""") - -disc("") -todo("Add properties table.") - -##Property Value -##data a list or tuple of numbers -##x, y, width, height Bounding box of the pie. Note that x and y do NOT specify the centre but the bottom left corner, and that width and height do not have to be equal; pies may be elliptical and wedges will be drawn correctly. -##labels None, or a list of strings. Make it None if you don't want labels around the edge of the pie. Since it is impossible to know the size of slices, we generally discourage placing labels in or around pies; it is much better to put them in a legend alongside. -##startAngle Where is the start angle of the first pie slice? The default is '90' which is twelve o'clock. -##direction Which direction do slices progress in? The default is 'clockwise'. -##wedges Collection of wedges. This lets you customise each wedge, or individual ones. See below -##wedges.strokeWidth Border width for wedge -##wedges.strokeColor Border color -##wedges.strokeDashArray Solid or dashed line configuration for -##wedges.popout How far out should the slice(s) stick from the centre of -##the pie? default is zero. -##wedges.fontName -##wedges.fontSize -##wedges.fontColor Used for text labels -##wedges.labelRadius This controls the anchor point for a text label. It -##is a fraction of the radius; 0.7 will place the text inside the pie, -##1.2 will place it slightly outside. (note that if we add labels, we -##will keep this to specify their anchor point) -## - - -heading2("Legends") - -disc(""" -Various preliminary legend classes can be found but need a -cleanup to be consistent with the rest of the charting -model. -Legends are the natural place to specify the colors and line -styles of charts; we propose that each chart is created with -a $legend$ attribute which is invisible. -One would then do the following to specify colors: -""") - -eg(""" -myChart.legend.defaultColors = [red, green, blue] -""") - -disc(""" -One could also define a group of charts sharing the same legend: -""") - -eg(""" -myLegend = Legend() -myLegend.defaultColor = [red, green.....] #yuck! -myLegend.columns = 2 -# etc. -chart1.legend = myLegend -chart2.legend = myLegend -chart3.legend = myLegend -""") - -# Hack to force a new paragraph before the todo() :-( -disc("") - -todo("""Does this work? Is it an acceptable complication over specifying chart -colors directly?""") - - - -heading2("Remaining Issues") - -disc(""" -There are several issues that are almost solved, but for which -is is a bit too early to start making them really public. -Nevertheless, here is a list of things that are under way: -""") - -list(""" -Color specification - right now the chart has an undocumented property -$defaultColors$, which provides a list of colors to cycle through, -such that each data series gets its own color. -Right now, if you introduce a legend, you need to make sure it shares -the same list of colors. -Most likely, this will be replaced with a scheme to specify a kind -of legend containing attributes with different values for each data -series. -This legend can then also be shared by several charts, but need not -be visible itself. -""") - -list(""" -Additional chart types - when the current design will have become -more stable, we expect to add variants of bar charts to deal with stacked -and percentile bars as well as the side-by-side variant seen here. -""") - - -heading2("Outlook") - -disc(""" -It will take some time to deal with the full range of chart types. -We expect to finalize bars and pies first and to produce trial -implementations of more general plots, thereafter. -""") - - -heading3("X-Y Plots") - -disc(""" -Most other plots involve two value axes and directly plotting -x-y data in some form. -The series can be plotted as lines, marker symbols, both, or -custom graphics such as open-high-low-close graphics. -All share the concepts of scaling and axis/title formatting. -At a certain point, a routine will loop over the data series and -'do something' with the data points at given x-y locations. -Given a basic line plot, it should be very easy to derive a -custom chart type just by overriding a single method - say, -$drawSeries()$. -""") - - -heading3("Marker customisation and custom shapes") - -disc(""" -Well known plotting packages such as excel, Mathematica and Excel -offer ranges of marker types to add to charts. -We can do better - you can write any kind of chart widget you -want and just tell the chart to use it as an example. -""") - - -heading4("Combination plots") - -disc(""" -Combining multiple plot types is really easy. -You can just draw several charts (bar, line or whatever) in -the same rectangle, suppressing axes as needed. -So a chart could correlate a line with Scottish typhoid cases -over a 15 year period on the left axis with a set of bars showing -inflation rates on the right axis. -If anyone can remind us where this example came from we'll -attribute it, and happily show the well-known graph as an -example. -""") - - -heading3("Interactive editors") - -disc(""" -One principle of the Graphics package is to make all 'interesting' -properties of its graphic components accessible and changeable by -setting apropriate values of corresponding public attributes. -This makes it very tempting to build a tool like a GUI editor that -that helps you with doing that interactively. -""") - -disc(""" -ReportLab has built such a tool using the Tkinter toolkit that -loads pure Python code describing a drawing and records your -property editing operations. -This "change history" is then used to create code for a subclass -of that chart, say, that can be saved and used instantly just -like any other chart or as a new starting point for another -interactive editing session. -""") - -disc(""" -This is still work in progress, though, and the conditions for -releasing this need to be further elaborated. -""") - - -heading3("Misc.") - -disc(""" -This has not been an exhaustive look at all the chart classes. -Those classes are constantly being worked on. -To see exactly what is in the current distribution, use the -$graphdocpy.py$ utility. -By default, it will run on reportlab/graphics, and produce a full -report. -(If you want to run it on other modules or packages, -$graphdocpy.py -h$ prints a help message that will tell you -how.) -""") - -disc(""" -This is the tool that was mentioned in the section on 'Documenting -Widgets'. -""") \ No newline at end of file diff --git a/bin/reportlab/docs/graphguide/gengraphguide.py b/bin/reportlab/docs/graphguide/gengraphguide.py deleted file mode 100644 index 541bbcedbac..00000000000 --- a/bin/reportlab/docs/graphguide/gengraphguide.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/graphguide/gengraphguide.py -__version__=''' $Id: gengraphguide.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__ = """ -This module contains the script for building the graphics guide. -""" -def run(pagesize=None, verbose=1, outDir=None): - import os - from reportlab.tools.docco.rl_doc_utils import setStory, getStory, RLDocTemplate, defaultPageSize - from reportlab.tools.docco import rl_doc_utils - from reportlab.lib.utils import open_and_read, _RL_DIR - if not outDir: outDir = os.path.join(_RL_DIR,'docs') - destfn = os.path.join(outDir,'graphguide.pdf') - doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize) - - #this builds the story - setStory() - G = {} - exec 'from reportlab.tools.docco.rl_doc_utils import *' in G, G - doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize) - for f in ( - 'ch1_intro', - 'ch2_concepts', - 'ch3_shapes', - 'ch4_widgets', - 'ch5_charts', - ): - exec open_and_read(f+'.py',mode='t') in G, G - del G - - story = getStory() - if verbose: print 'Built story contains %d flowables...' % len(story) - doc.build(story) - if verbose: print 'Saved "%s"' % destfn - -def makeSuite(): - "standard test harness support - run self as separate process" - from reportlab.test.utils import ScriptThatMakesFileTest - return ScriptThatMakesFileTest('../docs/graphguide', 'gengraphguide.py', 'graphguide.pdf') - -def main(): - import sys - verbose = '-s' not in sys.argv - if not verbose: sys.argv.remove('-s') - if len(sys.argv) > 1: - try: - pagesize = eval(sys.argv[1]) - except: - print 'Expected page size in argument 1', sys.argv[1] - raise - print 'set page size to',sys.argv[1] - else: - pagesize = None - run(pagesize,verbose) - -if __name__=="__main__": - main() diff --git a/bin/reportlab/docs/images/Edit_Prefs.gif b/bin/reportlab/docs/images/Edit_Prefs.gif deleted file mode 100644 index ebcfa34987d8ca83cf51cf4206c79dd4ba3c6ffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9685 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 diff --git a/bin/reportlab/docs/images/Python_21.gif b/bin/reportlab/docs/images/Python_21.gif deleted file mode 100644 index 1d6892e074fa69f1d5154b9c0f7eeb7a856f47c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28106 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 Xyp_|Kb(Bip#`3#^#^p>B0HFOpRX?{W diff --git a/bin/reportlab/docs/images/fileExchange.gif b/bin/reportlab/docs/images/fileExchange.gif deleted file mode 100644 index bb5184a833da3808580122b6b127944f230dfd50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37812 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 VwNZQZS0}XrST$K2HCYD)06Vt5BFq2) diff --git a/bin/reportlab/docs/images/jpn.gif b/bin/reportlab/docs/images/jpn.gif deleted file mode 100644 index d1ecbf0f487805676b1c50341fafdbae41654e3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10513 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 KV=9ygc=A8`*+i`X diff --git a/bin/reportlab/docs/images/jpnchars.jpg b/bin/reportlab/docs/images/jpnchars.jpg deleted file mode 100644 index 07c8ba6d2765a069aaf399f905a1e6c6156437f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33898 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 z zfQH6@nwHZZ>HNi7&cA-dkp(%#^W|&kq$N+u3$H1B;d+oYHQ04AS&gLjNF&srX$`f~k&V6=O*%MSD@_GKQk)b`{ zV9~#P z`)4OL4Dz)eYgNoK0du>rDvBd7sDc5jo`$Q|Lc84|56A%X_QEoE>|a(EYt_O?FTTpH z#a}ArAQb0RDeBr%dsk0}rGfZG6)V?rI~`ugE5_!CI1NsZnOQs$$@LvIc&Nk4zBUy9UZ5Bz_|q;vKE!(#&fpWIv~wBg zK@C$+g#67wZ?AQ)3Z%h8<>Fb48U*hWIg#zUwq(M09gKW_jFBX6x93y?ajCGx7wOyb zU3Wdm*+Hbb;q!H*n_^u^zHHg;w&c6le(?F?6!$$!e_2WA=+?ljM0&I$*;CvemorkE61I|D-zQ}-{Z&58EA^)MkC%WDQ!eNl3DwlC~UYG*&EH@J%HJe!lqA8kbBV z`_fIbdmqa07H|&?5c5zY#e9-ZVRNx4m?xPos_OAw$+dUKInZZ0rW97Dk}N{{A2V!N zw1h*%*FehWW>81uB?(TU+4)AAtCCfrZ;1x#SMKc2k#+Nmsmbkt=p3`{{ih`C331|l zEA=n3>g((~% z_ssKS7VVd1)fFQy`Q^L{HRrJu?e~N@1{)~5Y;I>nV1n2Xt(J`urIDRN?RUyZA#3E& z$W4cPJ4YifV_Qv|HlB&0ovFCeghpwq6;J{ySIcN(PS3e>p^&e^GN^tm_Fh%+cYy8Y z-{SDLCjLYX@rR%8U?$xDLf`9Ast97 z1#n7Evc)=lE5pP4@_F1FIyiOxenMxLeB1q>Exp#mYx#Itv{zD401g6p>aASYt(W4= zaCtSa3QOOqj#l+mLF~UcpJw6~MtBNydK`saWp6op(^c=;k=y}MK2WgK8i&i4}SMp53+l*L^M|p8-<(&&0dc8ECz$@p}TE$X#>H$Ei?Q-HnILm)&`NQ z=Gq5vl(p;96bA>VcF(!kibJfR))(pGK)U0fr(F#-2=Sb@=dZk_-w`yA7VD$#eCwF- zipUIAUr*`RV!_P8u?ApDEwo-;Ut!@ku{fA&t4I*#WfL2vFED;HL-=|<p4{YXup=L@lbLY@$hml49i@COu3dsvZFF;U<6LMJ%o1*-=5_&a<%X7V2$N+0 zfh5JW`XVK%v2#gz3im{U`F4v<9537dQchsCD8mfhiM3N*>66Thc-1>rX3qHwZ_~RF zBTH>~ikecZO0V(Kr;o{-S~Hx!4r16CdkMBqFNZs`aA7Bf)18cjm)O&=M)7<)b;q*B zi)v^0<8RLm;wkxsm0*6=b3oCR+S`L1^GXrJA?v(`pNMIND*FsUy=;4x4gXQ9acCBe zU4=rBb9Wm;Udm`m)(dNq7@xk5zZA#VZCUML@VnUYvKfVWN7|wgpzPuaZRpj`p0s7 zy!puX=;2oUHL)8c8>gL;*pq@XbL$}#U$jmTd6mraqjhTrq0}XRah3+ILS?qQP0#j{ z_}?4c_lU7(+N*{+V@vdjHEgcZ1%eJJJ`FEqmaxpjXYfqOsDVOr#L>mw?aQ8;p^f6) z<#lRw$*f1iU)39rNEcS+KQpRD;ar>JYX*r_TyJ=v^tP#Rr)0ZG}z}3%z#FC_jLaD+;EV6oNGFTKm^- zAYRRPza0aXp2y`;s%X9TwvZk;j6Mq*@3xLUNyY~bJd1(Jm-p9O6Ge@bmvoaJm6$S% z&`-1kU>Ylfsc|G8YswL66awHrR~GYuw+5-Wvx%_(j2%!`6&I>A30FX(%%!`XmtWiEbx zY)nfU=!dRO4!MG<>p2~yt30*hS?r+lqk_#4P$8qr=)Cvq958Vl$^+J)AFnMsQ%e5rlN}v1k2K;WwZI+v(uBRcD8#=Mc#N@qjZ*&R5HLiH#t(@o|@D+^mQXg>_OVR$&^K*pZ=EO$;rGaZCo(4yw8@Eo0s8YosR`0ha z!0wi+3unVakH6V^o@md+d-u5*zS{zFF+gu4U(U_(Q4okAG_b*0UtEbvTO1T8XM8ey z7Q1qhai(~5AQ__{KOi_59vTpzD89AAe3IRCkqfuW52aIx^Y~)9!%I57QG4zY{ij2a zLnO9$?bL47qECpn)vh;{h4$VMAjEMVnu$=-it*(Mo+Flc7$lFm{us8VfT+<7NsWdY zf8q8=2$qKiD1Vs&&eR7txepQfF&TI8a3FaIrvTeaJr3hxu&)K`2tv^AWvL49e12^RDP>g+-vJ>vQ32qKs(z+xfN8k>$c z*Tmhd!&-$)MpnIInFtFyscm!oQ0nd{6DhmTjL=(Go0A7h{#Yfj>?`x)r41BS+hdD78ZHvXCRNH z>K$*CdGg)t<67|g&S(rC5`I7%N1*qL0E+*KzUeQy3)>fXEN0OhBq zCNH^hgEvTK+)u#iutUdJ6b0DAUYVNcDwxU_r!}T_)5=Aa)2M3S3fF|zyxP~ep&Lw^ zBYnSKA5w}>{7$l-ApV2i!W96SvNBW(Blc5!%~nS3W#PRdPB{)Sv4dT5Li~&gh_LzW zqx?wlb+99x3MTtQ{*V{1fl7>WxRk+(W>FNNCB=1{`_~@N*6+797y}9B~>&?S9!sL#ivG-1yPc^qV#-ARij-E3Cpm_4f zTOo1}Tr9ax-0;f4eGbf$a#zIVHBObx@CKT}s!i;Ea_Gv>w%Lbu5qI~E>?7}dzoVTX zoBKHg1ns6$(e_!A8M!4mwF~EaMsZiOymE5f_q0ddx`rA9kv$dx z9~~MlX`BWS`E~hGRnorqr5n#{ZopJJy6W9HgA~v270-s*^3+qCwlA;{+o^eKY7!=U zYA=kN%wTsfK1dU8RpDdTP-_TNIm;WXR{$pqc<)`i&&#Gm68;jyHsrVE3<&c*WL~L7 z%)g7MQMbb59uU6}f2W41Dq`Y{aQ3 z@6Pv!eQDVj$=0aX`8V=(WszVqX^=W^aamu<5>%`Ajq_Q1EXzQao6PF45udR30wITU(Q=?40v}MhH;8efraE zJIT6=(xFBsvdRAnzyk7jRgJk3!?o6r!&jLt%f-hQ_lRfMlYzc+&naW_BZvhNGOQ4K zAf{6QnxoZ`W`eaJEi_wN8WGtTtM&fmobB!KriXpR*I_V47?$-cN`%mGs7gzq!L!(6 zx1P^{^s%R?y}R&UOSC!r!Epj>H=x!v6Ng|3V0crD{8Ev}0^lY|4AaGW?B!Hixn%ft zGU2sOFvoCIq2#_~^~?rMiKEkuwc7^ia6|jOe5<q->{?yS6?; zs_HffrHFlm(zV&uoBbon+7~*)QaKMT zNw{a1qEt@KviIn1#pNiNq)MJkMZqK73ZOWrBV4ORB*m%}sq*FkG&ex8)FKF_#;bFF zYx^WaRRjWL8>$|va8?A`qo6Ru0^OqJ{&J5K|079^x{aVY^e&A#bE9ahjbXL@^L(5p z_2xzRb)@gm)52oWXn4)cgXp-E+031d@wghjofs-3TDMZZTf(omI5^fXX<%gdTaMT^q}df9 z`jQLJ`1;Q{BffNBDY5X=LV9y!^K8we*C^Wv_nZD3^rT-xC;*>;^0+HN2*^ALmvL%^ z^{Xny4?O(JG!G4X)->%Kx6rT)y{_)&e6+KBQS43429z+`5Y{}0G`Ptl^>zQy=YD~p zr4zwa$22*`?HPyNyaL(gWqP1jRz=zC5ie7pkOGMS>D4gomMInK@WB^;y zqraWW%Yvi((IkY(aHSb#*^IAk6Uz+U5qU4}$E2W&8>Ea_hSKYrt9_jf=Eqw}dg!$T z2_c&DZszoo{ymn9sBvuRh|*5IFJ+tU8uyLj@Ma8?(Cu)Y2YF|U2uSTkYM1l8PXe`E zmEzOMMoa0Z*?QOWftGp>axcCiGsbD98{IvmA7`kk`tf@~-WHGd)-04Dv}fgYRN0{< zuNxB7oc#?3P&CB&`$UqHN^ptf_fHuJx3bMDY`O2U)>M;nU-n#%z%v1N1je=|=3w)C zINgiwQ7UFTvj=mCj5njS<&NUwg_W<;)87-T&TrH|(RM9{Aruxf6XnevCTj@#pIuei zTEEWvtT9@;0#Oa^8G$6PWT^`Clx?|crGnqr0g;5pfgB!WjOY^wU#5UELk<=J&y&|{^>f`^bZ}kl z{&;T^fX-UKIqzU`?vfFrAcLTJD3T^l&tg~_&gA($_Wr2jHZjd}=z#b6Uvf@gauF22 z%~kGyYRj7i(=IU8jyWI|mPm+K8Ch$F+Ug}p!wjM(B zJf;Ib`PQ8FVA3XIaR=?{8j*OPRnwv}FplNfpt=0-MLZ?)ZFSDiRhZDkF<*Gb`JT^A zl*!C@&t<^FQ*m$B_rBBuEwOgd%Xf}UCG3IXS~3B7IFxm$H5p9y3*qf70fHUx5mLvl zMccj}Mdg^kbAFvopRQb_EhyOxOKnLY5&5`o|242Bm?3yncH8ePJrj{1*_xQ|S*BI2 zEDs{n+kX!Ir*^rj(1~y@5ciniV)E$$8)U#U^p9@{^gA6B_BTYRnV41uf6N& zwXi6vcRxb!EWJnxRom>;z`tXL8Q~}s1%EVpu$bxE+hgE#3!|>64ugZql6-@}zKFoYouSANOhn&6B7b)fCCEE##octs6?;HT=LEu18Eu!Nh7-AP#vv^$bJ8#@RAj zLi|az!}X7Ed`xFjxZ4u81noM3UF5^4QI?L#w_Cj)Q72|nNsjUt`{M*bztn&`4$$rQhaN@AFI%~Y~GGmdKXos?*32^;ae#h~L=A8EuSvCGZy<>vAS zN4k!*kv{xB)_7f^_*@=lJ3|n`9-?ralOS+a@7$tMn2qSyj^@TpufvDmy)}n{&qj{& zWa!+8x{BKC!@3yju@V!-l{$pOV)$GWnD%E^VtOO=RTD2O$HY)PTPp z>s9UfRB)#@bKs3}-h^`JOWCiXlHxD$IjZHJ+?m;rir-7J=1R6U&KhkEv2nAfxdRi>wZBGiDzBc?grWwg+R(GS%FIrq$9t{%u#d%w@+&nHY`k^-~qxMnnHn;o1 zk|Bh4{QBbMaIg}NVL}cbZYI1~x_GUyc%V0@!esqJf8A|}L=z3SmTh4$E{eXifTJEe zed2{zSiOs%@&!iZQD+Yj+qydBUU-vi#%~h@4fP7bOnNX~kb;v%mCtP;8reuUCh&FrlZJ>uj!f zZ6Oo!79Q^53y$of-*W?*26>b^)s3Pc583=>`=>P6zk|h<3T=b$_gWLcc6Zwwk-)hDrT0cB)mKiQ#A1^95;^jLxA}p;}V2wJ1Jidl+Lw`28dMI4JS}y7v1z8WAnUC`PZ$r9lp-k@pd?(X zj>pLs`bsRE+0NkezI0@Jb8Oxg?XuyC$|B3X%E~q=5A;z1oX?g@9l)=lb122VW^C8q zEeszeOaNQnv38=jzakB3kPTmIFU_uKbKe+Ka<|sED2-x1xrqzMnr*XMlT{x|xv%!p zsM{A6{eDvS!Eb=;rvLKp5evEY^*UA~12t)A_O3}2c5Pp|6#sVk;2dc-9#b&c(AO9; zEcmSzdy?S6f@~odW|Rxn;l`G8ePoVMRm6GpW*ESX6#TPUA~rhX{3|}2Lu*ipwdjgl z8T)c+9&)6M0<8N@-eLTi;;C))N5<3bVp!#M$HHuSK|9Be5@c$=Q_q zy0Q>^ndyBq0aPMD5>T|jzL6WvVVl=A0H=XFfFr$~81R{>8pNoAuD?<9M8mNvQ)BW- z;5~;(K_-e@EkZnp;p`icnripQ$v*72vz_1GPBwL!on7RME}AF^a@7q|*xY|jxL;{@ zQMxz9CnNm6*X~gou=ahjtQGUQ6+tm_see@wSDl8H?%`a0=UhN+L2+i(7r8uR_I0<* z4k$K}uw9eemxYZg_m4P#D}MLU{WL!oUhmj5V$BOArp)kfhYZki z6UWYe+N|K*hspg*Pc?u2z}%)f)Z6=euA(7P3-7JNJEbC*MB<@vy%mnJJBSfX-u**0 zZjU|Z^?RM13$ED~Ye107`qFtRo47w}MEnn%zK4xq2Bd;3Sa6jTC}W^;f3QaqvEXba zgj?HNJHG`L>wUXgh`M-uu1%bAq#3c^XJ1cx8l{n9F)o?jS0ACsw0OI8QAlF1l(uaC zc$^9by!d#@?|jF_iVts%-PLc%lUBHIi|t5OhOm&AN8cHuEZeuz#d>$I)k}wi9h1V@ z@)-y!*nI8T#(ZjL|2%liYq_?-xH(sC$WZG`PECmghoK?cv{8V+Dg|+0AP5yhJBG{u z4HkjBIkg7;JpAIE?}np1dqQXL&2-az#pZ+!d8CHFTkuy#nCr2M2dk-i6<+J4X2M-+ zwtM#s{fl*uNorlnqy2v2qFRBMB5MY;+iD|Z*AOHP+%v_vR=7$sA=VQ%E@pO#{by&} zqT=tTq_$MrXnXqekFTzW3STp&5x*rzG&$pSj|h2>#MYXaV0QeSU#q-0xtmeMj3qG5 z)!j$swvo0?BJ1mbH0>ou9Z4Z>rv4CBCu}hx`-YOlnaDu_apOlV`g!%%R6|fuzP>4W z27`Iwqs^i@|I{IJriL8BHU;d8c~X7dnH*V^~IW-E{D@ll8e@Z zzx#Y8%q6qCAMZ@gTQ+Sx`1D{Y8l$ie;=slbT(Nxp`LykfpP>Hx!7@L5wmPQ?a;=Y0 zj82O`p7-3AyF1+G(9B<0_P3Cdn@o)}PvHuXR)+Kculbs9DtZHnAj9%)qwT6Cshka+ zHyHKSmgbk^WPG=Mql>Z(G?9M3GCJv`;MDPc2$0n}_{ZwMIbB=x$p!~&ieGT&%eg6$ zALL>Ci~XZ{mOr@eMvnPA3b7B||JP@O8H<6j*?3u@E`q=uoY3%z8sw|8KM)22N)>yE zG<~|fjm@#pxSIs?%60EV3$ZVOLp8_K_yIeP|+I1$HHUQH2H-C@=!ns5(R zsOK{+6r^nUyKl~2=2TzN;mQb(Oh8@avq9}we+&rn`0h@zOlw{+mhI$y6XfL>j4r65 zw|b0XZU1A?7U+C}a(u1UDC*T`f?-R`B#2~vpK^He6Si@+phVXzGvS2w!-#M5x#;RG z;cr=}--BY@#xMxy(W?k(d-dL||FV+jUL(=`#k z?A7~@ZVs2?F`rtRX9b{;e7Rc&Jhtx({732meyh~8!J;d{Q>)5THT2=LtY>0Tn|U)H z2YKu^8JOol;p@+NU8x3NhfpO7e5Ab&X3u{X*Js@TarQPh--*K(K2d09t3%K-j&>o< zIki4Sr;yo73Q$o%xXDb2##8GbU)AVZWdx4#o7l;G)92wGz`ZKTtjlfx-HF)kqE^^^ zAv64f$#e5Q`?4zpvC59`xB}3zVO*gE&-J8zc9t{ z+`WPp2z1Lr0#ahvt zKf6iWWR>k#7CLfz;WVmJP;sq%-6?|qI{SV}XZsIRzV6fgtm^9G)(^ic|HzCAkbR;g zD-KBN14h7^gLw!d({dFaoOsDU{kXB1w_g$K=RZ9C{n4)Gp&M&PX*Z=DrJj@BzAi4R zag#!8pTn2v>i+wZ{^*4U-8UxNRsbA*p{J)Wx-Hu6kV$x2?VRp2L>L}a7~LbNR;S%> zz*88LH@DY=3JOGYv46sjmd457V(#hWZNL)2$Yt%hu~PNrwwiD2_ldoC5Pw6r`HA#< zuYgQpnX|4xujoZKCrX&SN42`wmlWapzG$p>K=!?bkqy0@wpE91t9$ky4IHvk z!sk2e057w{@EVZq`MtFT=WLeMii*-Cn8nFrMEVb-@xqFoGo&J0+0-agz=*5-u*SF8 z%A;QMm(K+QqM{B!<~X6AGxRb(49OWmtlQ*nrZ|^CAx~v@Sz8lpDiLmy6G?iG5yG_% z^|rze%c3U^x0jQi;61S(XkZ^WqWR3K23nx= z%h8FRGCZyhxk0xnLk+Y?A1;)l#S%c;^1Fx8!Myb|qeVtCmcLq`b`>{FAg4eO{stWR z1KldQBDNu3+uvcafL)MIv6=Kf3-<8Iqf})eUL6E^&3d0(Tj<;>{F8WC3pbqM{^__p zE*~*uKP`1AM;!1#U{=^nBH?r{oSBiR4)amtr!zHNChe^zY%fw!#z*cF3*U47`R-7k zMx3_6BlDTtIdGY%%!q~>4!r0ronrUmN@UgsUiEyasV)-pFuwQ2&Zd?PD7P*kq%5J1 zyTxvP4r~wR*lm@HC;}&dBulo>gs1duSrb0*E;^eQxlQQ_nY-c|re)o2B_cl74=&XU zrIHLn-KvYO0HFb}H-We^n$xg>63n@YdEB_{{@cVuFtNDx?YDRCs`0_8qvJ>{*lC;$ ziZHk+Y)CL#+soFA%#c*wZdZq+?^j|PA{Q@54_}z~$5(ZNG-163?aTS;GSAUT-EdwQ z&qe0t-KFh3dF+ofmbEC~Dh-4B{719yB`uCkd62Lhb8nX~pQN(+NwK{1TOHoIe?ag& zmw{HG?dH7jP3I_Z%vM{{NPw?Ll%hW!=N|QW-C56Fp2Et&rvba80IQ4aU`D*_Io~tf zlMH`md?Fw7?g>rVc-iuZxcQ>1KXwuMH#y3rkjv17;r_Aw<{HDF^O5ld=@@rF zH|%b?me`UcK`Y?qdk94>^R@Gb!7gIhkXX~<%$GnJtXL>aH^3GZ$2BTzfqOJFn~pZ| zqrgQ^YlRpDRw$NO)tBAcN0Z&yQcDX%{bsc6Fc3wpzdQZKJyj zMgcohsU%P>lY)2ZbSmoG82A@A)!%U)7)x;gRD-Y?ZJ(qgkI67p1j#Zdk1qpdnr9I| zGn4g-mAB`NJP(N5^jHY=B~EJdJm7csKHQCnf9#pbN0u>~VF|4%UwzE%c_ofzOE6DmwyPC9R4LBaeD?;nHIR@rCm zW~xcQ+9#s_Hex(I=?&FGoq1)xe0fm@0C<5pV7GkH9YD-u^O35E=}vLH!=<(yiO(tX z!wf54rUP^RlgJmE&Rt$ckDT=1Z#)Wc?1o75Kyy%I5!h8d+R;j@9nM1N=N;*2v%J!h z{d1O*@Hre%bv7WYrw{e*|mbt%+OZrltIDoY(f;B2F&S&fPrILb4 zmQ@bm7?4!dd6n1p1*%Ljg#G`>U`(ecTd!f&*jD}-9 z=9u}-U#ETHl+KZjzYfM^BRw@1b$D-_Z1);xFe~0#3kQF<@AfC3bk~T}gqNYy$*r=W zWm4#_OlS4D5Vv~c_b^gL^80Uw+`;o0i>7bW3Au;8RZ_jf#vgy3StFK4RFrlMY{eyK zD$RQPytX$4G-NvaxiV?V0JP>E8d^B5J#$=h-=gHFX}L89ys}aI^y}jGyug%F8m{nA z)9|`MGVNVZ>FXz^npI7p#%H1PSr&HB1`-I26_yK?nl$3$)H`+C*deYg zp{&0un^F|)wjsbgxLpk|9^QWm!XCzap73oGuNacschzqr&Rt2Nr_nP9 zJ~g#ErE?OHaMc!{#5bj|!`>Gs?(->T@KjX$jUrD2*UvzfwTd#+;9&EW*#~20p3SWeV?Lo~wb#<8Hz>G%+|Y<}Rb6 z+0Gfw_V=<;84M$w?av}!Y*=6tIZU(6*II|iVgQ!TZZ#!($M zPO1J+IVO3Y)k+Mwo94#YrlWxiz)B z%u2ETbtT)kO`DohJ&#)`8F5-qXD*px52t*Bo+I2tkKb^@6NOMsNPGl%o9y~AEt|?4^<&aJjG2{?LY6tDbk-S z(r|adYUIZA7-=z0-}C2NHU+dMY{|D%W=i)Em@L9Gkop(sKu@sH58KYmsV1WFg&{cm zORpu2f3P!kO06W&zPh6d9>n;*{+?$SQr;p9WCvAK+H z?xdMeVM5&mADkCx2D~x!WVjnOMDdV2iRU7N;E&N`Bp6M$OBU*tS(unESnseIwRXkG z=lHe+x;>rOd2{Gltj6n1_p|EuHBSMWZ*J6rdZoVLP%zW+3ULt8Xa;1BJ$%vDIW-pZ zrg@dTE006MG^;42cX2Z5bwnpj%I^BRW^%P}-oc}J;MBkvem0FY-R`p< zVf3Tqj0pMl2%66sN$&kyZ}^kd?tznx;sUUc6Qt&}ujRv%Qn?AmcRF-jOA`wm5 zXW?^Bne`3N+Nzp<+MpE~^@KQ#rzL`cFdaNm8+YYac+`tdgr-p1U(qZ^6Jsn1L*EX;D&*hlPA zL6rxLg{kOUsfI~j5-@RY#@$X~yg5!BHCBU_MYLMS5f~}*-O&F}W$zW#RQrZ|qadJw zq4y?LsnU@uN;M!&Y6L_?N(4k&&_JYj5D*ZMB3K5n;+X82oM^kkUH_k+HExJwDn z>S$zZlFilXz0qWx2A_4GA1q^L%`KREPU2K;z6*Rd=FM7HH)y*fbLZ~n?kBAtG8Rqe zNze#7FAl-zi}U&Rd{T`wxW(5OEEKzO#c$xosC^LqOkn@1;Rk(5#p7iO@e~J8)%Q!F z9noBy3KVjeh&PJ<+OFq(KltZZA8y@0VTyg&O(;Z>PYVk=I>kw&8a|T%8trPVEg(8eP5Bl(`Gn*_d~~fT?@(`C!{{dJMUaTQpSNb9|~3{Fkm58 z?KtTWFNq4Zd&S`5{noBEz2z4$yDid^zEE?LI|dGCbZ!%Rr{~q5nNqSc5}iovXvLt)ZnXA-#9hI!^~jJ1TNk5{go6 zHQ-#Gs1BZ{+~C&n5^^!a4uY!Tax;>?bfTLj*-i|hqqBg6GhT8tyk2p5TFoR@b~Tsz zU0_wc`Be@{5E@Vdd^!Y}OsLG7=26$sDjTkH(IXfE zUkvM%Akd+&j0ZS*yUmToNzQL;XM(Wk2A)%W3NmigzmXTMbXX1p_hbye6!*bw-2AB) zqt@RC^`}*Aq(q1HbPp4uDTFPYYJV%+u?Jx6abQGxXzC!lAen#u2IqJi0&@K3p&GDDQH+kz{ZCm-3 zt*$(a&`S4-FtPp^#58CxH+4#qb!bmV+VC^eZgf4=Bd+B$n~Ozpnl-BFyIo8BbtBHwt( zQxi+NTL^pQ)ghQNCg021#+Hy25ljoFtBzGOMJGH(aKTz(e7z_mNK-xP{b z-Zwu^dsB=nPkz!xk^=;$yX~dvR?V}jD|Ayj6n=jb%%#1_wdR{J+%21D!Ot)i zi=D^m7W(0s(4>~~G2xv|^p(Ab?v5nWWza9Txg&P&WJ7}@TXiy1&0sx#iEuGE};W_JDcWwUM&n$n3A zsl{w8Pa4cCw2gS3I1|2=4&CVg!Hld3R*F=8?m@@Y&HLV3&xlY=7i4cg-GAp z_aSO^0u{7)qO@X^V>yBRdH6o73{=I3OQ9uYienz$qL%+_G@#<)u>UQxKYTG=^nM-2 zTY1sG*?f&CS63-sw zW=ufVmOlJuwGn;j#-OZ2D{Jh?P?lp%hGUP7hThWSmyyL%wPl1RLmUH+8M|Q%?dwnZ z0jI!y(DQpQzc_30%c}K_N0T%8>&dLUOR1sMRGsig3xK|&K{U+It@5PWY^dA*Uh9V- zjkTTTFT(5L121FVP3*Ei;}H5pX;A$I?uPTi_@`lA`eyaE)NlG-d45RZ%DXiJ_yOG& z71Nu{+%LQpQ0}*cn;e8Iwh|mo&|6XO?geHndTMC6#^El}<2&D+F_-0W#+1;;rgtlF z)y2y8{q;j_VKfrz@>%aop0bqSYV2f&(Rx6^{0rH5XNv!gH3!5uX6dY9SNjtuL1&-Wqaaz z!{2Jk(s!N>C25k!teB;rxJS4jhQc(RfuCLtVwyL235z;FaM6Ox>t(glP?>`(DMcc! z)fAk*6IB)^pT@nx%a%2yoPjM8Dz}`XQ4e#Uy%2AUSXJy)TqS_F5D5)`k>Us*Ti6R2 z)MVPrRD&JLX5L!VPP&CzTiv-T%nI zO|w-lbKg3#UmHgq#uFkh!+xw%d%kib4mgNI2U9(6J-ZLJD=zIGzOi2EUFbFgH^4pY z0(2Ku^4`Bn>MQiuomX$CJmz&W`^gnS^?(PyyE)lw9GOR{A(Y4LmWQpmwPXOodv1iK z=)IkeLx`D5OcR2Hj8?ud(MC_)l(6}-3+?-ljA`*X!5r#^PQ$V0RJj3KccNB>UH}4n z*sqdMlYq=I`uM7;(QLBu1?_8X!2XII9(l+M#ujd|WF!FnXs^axf>jgIn zk|uQL#htm$#>TTSLyE*{{bNRNd<|{v3yY=T41(y70}cK;&DWJ+(~jo$`RZWMffAci z)4PUxuw35l`r(shiGdiQuPFwi3Ol(Boj9vdsXsK8O3y`hb?WEdyhz{LjEdiO)4BSo zWR)^H{uVqYw6M25>5s5oCOF}wbqIc#ypJf#LMP1M6QUe;(rl{QcBQ)&Z|@s)lV*Tx zsiK&m++g&ae$^cgly1fI&hjDK&EyGMnP))MxN zLhn}6%vk%w8|uQJ<%5iHr7?b8x7-}~q~Yq72Iw_2L`*Qxtd)7<-JHsqgUP=1nHt6c zF_*DStgYgpPgv~V-`2ROg!Z?cd>U$m@d|>MQR4#A`s8U$xwR1j>f%*=ST2c`am6Qa zD%beXY+n>b^cF=ItG|?vMo}dyOvbU^WQ#v?LPTdNc5n@0FYH>K@RCZ}xyrw^k(2_n zwo;E11{GJ?ZYMoYjKiL4&o;QV%ZapY{YFO@*0#0iLz5vwBX{ zGHnkRW&ci0{P1|W!Mw?*ySH$WzQG+#uTy5{9uP1B1U*cXl@j9bC#2*AhF|TEy zC|>GG3S?}zvpBh-gWRc4;W{`aliA_cj#TqHEGXtp)$wUbGqN>fBo)cbGOtlieBI@)GEP9=+ANE-ZHLw_?UJNmO8vurOM z*HXZ^`l&8?o0n=xLjt=S9(;>5e|_U2jyKI~OVz{5boRKioxBRQ($A$6yIkCyZlrM+ z6DSgCWB28lYfSEIEpI)>E4G!j9>B}2-)owMyEm!?L?SB%v=LFAf{d9}C-Tbz&I6H( zH|jpitb%@ip%VHCV!2AtxKDhD(@SUck$ z4IW3(!AG51#L79z878MSZeMEKo*pye&!|TUKLo`oldW~t9u#>lP1n_Rs@#8}wJX^4 zbEBRV#n-du)t6N{8R5CUGxTt}0UXtUWsBp~{$Q+T?8n6;6QI`nE1~GxW%2pejIVM0 z0%u7fZemO~buf!%rxJ6h6lY=_r7}+Zv;q)@_HYxlA|vy{uM$n-Wa*U#?&#^7bo2TZ z6Se0HTaXJUOal?WI^3%L7%&~mBN|_wo6{F2N0J;~n}SfLU;+6$32l129*6H5qB?$F zTcD4h)|ITq`*w^uTl&iRHyjz-E;*6AVYanJxO#U#Uj>@2 zddQ`rCFr|CzbDXhn7@9&_4-WE(WhRl3S)T9z6;}(kj`qG(OnK8yf~r%O42ap87=dC z=*A{TVHDz}JFZZIa_(;mM*pHh-CHPBdFzG=`H*}bA0fk%(EH{!;2ORAq79e$U7pQ9 zS8RGB!R;0_dEj-W`|h!{yXAjmPk6!l3G!B&Etb*WH8LyQ*A*5uT{ciNy92gI7r^xo zy6$@MR-p6iDLRg0o!`ucL9t zgmD0IQQMyMLf9)$BzxcxA)xO9?=ob| zw4c0Mx{`s8imsCO_!**?>C!pX^K?pU2RD>+TAN$^>&Sc5UMhio87TovN7Rx9Xn_9F{38r6>$%-k8T*dGkQ$K1BO*x?QYV{D|6C`P(pTs(nFX9oEp z2mBa2Pbw~T&k8qr0g1~FQrTXAG%*5{28_7h4L(B|uB2;v0Tt@J8&HxkVUn^uA2#@J z{cw=gHlo33lgHni`_o>l-sdyjM#GoBS=R*0BZ37#(aOsFmQ+G(e&o_8m{z%`uWNd7 z5kP%eEaCo*4!8YY)i$@!a_lBui5Rhhzr0Kd1g~pR+}ugZ&cF#tN3M3({E!)R|#lN|LDb37G{1w%)F~jYw(0 zYs3bqKEh95)OEs$H>(AnUhy3leq={fa|H$cbly-UF z6$^hnLuB&*r;+rbOwSLaRcxe~EB_!>)ylLc{n_PX2sX8fU7Ws$DaT~7Nj0-rb z#v1pwQmE`?HFEMrNmnaCtE1Kh=gh=&)GMU&>5Dt^6L&tbVa*16VAWe`I z=OuuCrm9NPV1l?!Q$~MQ1zW2(ZogHDwQeYgH;*q>qzhK>87L8|-MH?alU?ku8}LuF zpYh-=K@{H|#AZSoUa%FP3vbC7hbhzJew##zaq7mQ$U@6z5Mn43ki8Of!WTK=mHn+6>2_zYZF z-@ksKitKS`MlDc%uoMIN5T{&0RKj>DKHC4bFBT&?o|f6%FxEHR?ZZX6G$L?9cJ}P5 z(03$Q&@H8DVEnc^=O9Oe>Jryr3iXN9Hds8ms++S1#K@m3c>gV{o-b-iqU#aN)8olj z4__VAp~|NPg|ITnRhtRh2wcij6U{OYU)GpPJXCb>{!M=0Gao1s@HF)yC`QmhHdV;# zxGBQSrA3mpk>z4vuEwQg`*5;$EC5&>LHJR}6uE^L+l9*W>(=5)%s9VPLfj@Ol2em; zq!L2t{C9A03Yl)#p=-~>2`n(PpD6fB(NOBr2Uc0_-m)>NCEFvu(cBVKa{v$sEY_lEFrn#g%+^d1vUv*n zKIDWyc5f-qi(FW=7wMYMMWeXd(mH}3Oy{nHKeK8W^~e775z_6U{h%mzDirxZ0>HAx z$_;5mq22BzJI5Nc6D+2;9$1-IEzx&hP&HKs1WR8Pp0wY??eI~o)3MI#K)7-Ys*ZDi zo6Vh2XgXUqvYK{hi}<&hb0nN7QCq*E-2|!Pu2QXG<~@xGf@=mIn2lSy7qYrPW|FI{ zuOE?-F$VH`r`l?Akk}55F7#j1u%hdw+$74q1}4}rHSv61(O=KWEnX)aE9w3hwO zrr-1Mvid#)Gk%+}0U}y86HKuoo{|l1<(Yoc(ZQk((1F^l9n>@^8hM@ca^SL0D3`D)|?Pd1}$U0%WFT={1-MMmj0p=5T{ z@DaHl+Mb%Tzh8HdXgj@rdm3!eU|z{VeMtt++_pLBF?e3eBcUvHumtt&%rTQ9*SX^2 zV_RV~#^CRxzx+I3yBaAAf54yA%=m(6?cHq|77DP(|Bs zskGf8C?AR(A3Tk`bgkrXjvMOaYHUt2Y)$z$%uY0Q=9ITGqJ3n7D==tsue)hsXVF|G z*UMM`y3Kp@(s#;tk3Z?jf|op$ChiJhT|ZaxXUr)0pYk_IbQKFqX!a-6<2PM+*E%?= z@=CCeb0|{tbrPLaV}@7RI$@|+X?D$B>9^h5^C$mES$yMx%SEegWWTmPk&&5DHXYx) zd|?NtBP5p*oIU>3a`W;a7xDpf8@2t8&Fl8~fw)TD>1e)Jk1s!GcER42pGD>2VOLe2 z8j3`dMr>0?(YTjV)Tx47ac$e|E=(x$gL3~Xn**yav{I|2b4RbWc*?(_ehwVIlgx(* z4kAtQUO-m7Ej;UXo1Y&~<2N4{a{UYyv)<9PL>3!_r7@?wy--MLfB78H>ug2g&$l6n;AZwGt5FAGkvg(uOVhwh4LNW;Ngb zh(x*Il35G{UN7i7((5JHOSxRee@ihGjOh<7p{c2sI|b*FG@40#AS9$i@YuEi0fK{l z6w?YY18vh>|bdnL~g`o;jlkd8r=0UqIkag;Z zT+OmVy$YrA_lE{$g&0;OTyM%6ZvNyUF<{YZ1U6i(3|4^4)_A;JIPIS%ssOOC3Zs|3 z>tMh_Tb;XbDAw4=zxt9fEUXE3Igfr%6{_aA1(be>Z>ALVHSR#IIM4BErF&iUA~S_E z#WM*NGC!O2Q`HND(%1jy>co-_bUR)49-Uk}&f`{t25u!SGi_OHUxc4`QH`@jC^WN9 z+Cdewj2aoY7mu2lL{?MUYaW;nJ%VkZ9Pr<9Q9XH_hqj(E03grAZJo1i{@bkA)2zje zNtUADG+)w?(VoHMTzZ!Ste(FhlQlJBmJW=l&X`LO++|7x%Yo(Di9*}C4`NUp3p)!} z|B==I`n0~Xn!59RNIJ89?7mH0CPf(R&AGiaf}!bHvtShkelMR*16H=3QTB^!Y_{oK z5K2aI_1BnvaX81HX^K6j6`2CD5I~n+gcVH2P!3a1Kn4laF>|WW(y)I!)5{qj86uz2 z@%!`2VN=wVdvsl`0jn(Sh?JuPjG$WX{*SLGkKUd0b{J5_;4KCvs2Of7C$AMZqz(aZ z2xBwGQVj2W(zQ#oOSbuFw#Qy`h6qJD^arUJFaJ1&%&QssNPU0PWV;%m()rEbR{BUh z(&qzrGBsx4XZn9+hunDzpXdhO$)9_OT=r|$b;UPyfJlrMxF8_zbK#2w$kyzgZS`N= z5i2T9%P66_EjqEM_mfE8s^fu+YrJ0v6FFUUm5B0GF zH4&15F^(qjA9@3KCH$e+%|p)fqYM?E;>IQqx7(SLU&~%LhZ7u==SH8ou#vw>9&~~$ zOkK*(LU`{J+OVbObCsTsN*lL{2EuFgT0Y$0vJM~pW-!xt{PMf`x?@6g4|Pj7S<+V2 z;A9*hof_>0FCRh24`?1TjJteqO<>(yhb)7cek{u;C)1`14cqGKclzF|r?pm)1HO}> zfZ!$#Zm=VHyn3@TLQlKUphdPzQ48M`KP?w!E0-jhlpddm*WH4mBAqV?VfoguQgcFJ zF>JSwkNH@>QlZ`0=f-();`c^Xp2y~pxEm=uDAkz$7!O)kk5}=so~C!A2U*(kD4)A! z1ss`P>Joemr{Q)*CE4E&Es& zOE95+%f90VGAi^>ToAW>$*u*Y!(TYTF?%HQCA8xU!3d1bsFk2&Bl1B-YE&4A zCKW7o3t#zx@|(At$CmiR>HYT}gWui;xN?fo)j)=Xs>f)LABUv8y(XAA)8(-yf`vK# z#16&IUK$VBHb!~Z_xzjvYuVU^(s)*jnZ2mLUmpdh$w>N{@h(XizlU60;=)I_kWe`3 zsP;ULrQ|>~kGwl)`qPMl8~T;w9fMZ`A2nN*%?!k@IqvXsUwv&4a`eEaor9w7HDuySceBO(oACD`7XArc0G``J= z>!;nF0=e>SO)01s)gSUd@!qqrSM1s5d36-slUmI%mw!%y z{Q`Ahl_m7iTBiM84&ZG)fvB5s%8*Y4Y~kOkx=P z7n%Q(X+j4dk&!y4ZJ2v$s+Z4#v6sbqcHvSnrP0#9(v$`slIU#XIj&2f|E)Far|+c7 zPg-ehW&oGTst+<`dG7X*Mt)Q-FoqEjL`nhgt zQX2I})I?CRGKG=F`sbZT@}{4Zd=U+KeFz@dC1k$3qG64Lc+U&XI$h#y`fx=2JwA+wJ+Y41ZU0u=Z8)`8Z?zauGB>L zeN~*o)guB>mxRj>k!$o;deO8~Zx|DK-oSMThsnT$Iec)%beRof}Al4qd#Q~Oop9?jJA@%mJ05^Y5hX^V5j#~5HT z1z-!B$XYMFoq7XkaK%-5K68ElZSKfc1WRgonZlLIZ11k}27W}o(>;7GX~b%Sx>M6l zvu=(P-LkMfCyZ?nvROWuHZH#^fH+&2L-`_6zl?V7)6y;c;m>HcD>21a4+^i~$Tv;2 zX#n&_&IFnA|Df+5Y%m|yHu?~a&iHEFr8&W`jKCShh@q;jNiyzOY_L98uKHz}8{mp) zpfMtAOrOzF+!u6qF;Bmyr&LLV;n;?}mX)^bi+{iz45#F0u^79j>mG8DAbqcnnb$3s z5-XMYXC;Yj$mGvd>@9{eKX~a0v|XTJ#r4Tzw@&^fz+DoNaATmo?x6K+&03)=>!kMw z3E_nHpR*0yRnOUEDeX4ynkG<%z>U#fOTPyK7CE&wc&2s;?CI}6Gq%xWUp?jFFcopV zua+JwiPfLH98r8&N^^aRYSvzXkc=4zZGj@ySPBS`C-V*C4o_O=U(b2^jl9_nRfS}` zR$1JE*syG{H6HUi$ICEMx?(HVA8%H#SefEn667USjHw+^ZdvV|+96UNS*Wv7@Q9!K zh-{nFuf$Oiu5hh+mt;<(g=xbLRQ={LpoLO|2@WX!vpBy(CZ-6@NmIz#^*GU`cNA>AF)=crN{9 zX9gd^gmP%`r6&3%@#X;)QN+pyNYP~R_0K^=4ybBdf|Flh@%}mk<3S@TE&e7xd#Q*C zVi3S+bT>xmw&vS2m6W&5v|FwMt$b`Su7?D#D$-;wH+C@9t-#ekSg!q2BhQgNmc40D zWiAP+_t0)RX>Ku+`06UB{T^C6wq8G%ZrV>>)h71Bx@{hr6cLbnKOIBbHOUR6A1VE7 z%sP-}p*9JMK(v>_3yP#IC2sdS684hLT2P2sF!p+?Q zbL?+?AmJMMGLO~MW~^e4RO#(6XR8N|Z{x(BfpmiKGO)_=x1E50G!v5%7t39{!X&&S z!C=!)d{CB9uZ8s`aV^RptJrbwnj*2Ua#?53O}&NWa?-8Hk8gatly~RpeEy_cKB2vo z0i9X2fN+rxXXRT0NDnZ4sUD@EK`7fQ; zQ|?kB0GE0vk`Ms^&RcD2<|WZMs;Cu{mUO;bhemYcD`5BNH@hD!9=q`6+)2kgETg~| zY0zxd5P}p-p_n8TuLtYIhRJ*(?wPTRo8MaxOqL^hoV!3Rgu5WUTM}8j2y6ox*x#nO zi`d{hMDZD-0jS?jNeg?@#J7|?+ikvIYrPV=n4h4X>^3YA-rag#g;8)>E#%{}XvQCW zZ0oasQO7jA^`!^4Rk`aj^&pqR0o66bp-0o##i)b5f2WNMTX0BOkkelqA`IbY<{CB_ z-QRnLY?iOR1Cvu`3K#iv7X3On`svBfrkL*2J&pRocVC=?>s1d+yu0upV_rha&;aS~ zRi0mt!cI!>??E=%N1l?uno{|&Nl-yL?%1j*{%spAo&k7Y@jmL0r9E3x|9eE@A1?-i zlh*8;>O1u{IQ5x>f9>8jdfq;g18HABiDvM0{Jbe4{ou2mDys?Qe`IfG&Xv#acDY0( zJ!zXZ+Zg;8e$yrJc79@QRaE8QH%3JIRhd^}135CyHMHHH{M?I!phyxU0Ao2e#YhvL z5G4y?pKV~$k68OGYhw4bIxnr)>VKLysj?{_hFBobU;l^yo|eRVk*Fm#32T1qz^43v z*g%`xHW&!$D}zmV-36RHJJ?v{+l~0t=qcovSYea4q5w?~s?m)aX6YueyW|;!>`P6e zMD0ww+Cmo{T-g1LP~Vq6F}3Ia!}gwEwsP^~=)YEa&dzEqFRA&H?dPkAidSFMWH2mK z3WzAa+eb(tWJ^cpD{NZ14%EYob4=}miJJDUb~G;KtWL@~-o>X(jW0>KJ-cU}I_MEw z=-+k=v|{c9TIZR^l}l`vGQ{vu{ZV4O_rN<~yUeY|(23(?anjL}745)QMokLAZVfPH z0<{m{nct6Z=gCjLp@O^$Or4%!D7PaiUJ~ zevR7H3N)ZL8uOc8uEI3>li05Nk?E4g)ZHf3 ztyRBxt)FgFs`+#%=qm5>5z(v-LX+Uyx2bu1v^6L{p`=~l)Is2Ge|(t@X>WD3MNo(~ zi)^!W2H=5$JsuDFGr~mCb!#wz1=Y1+O@emi{OH2EMU%WS~Z{pT-aI9lP z?|bSL{seR=X!OncG?FW(nD%;nA zu_;Oc6sZX+Tueu)ey-JEK)Si;K{8CK>GK}om+fQ{)h8$d`91CC|8zxc)LSBb3Q@~M zNvw2cx!v=nWezwSh3UADrwbeFC2Cb{H1Ac*8W(-{qak*V>g$A5G!O+`6=>X>ysF;n zxVcM0WvUt7>2ZPdhme!CROUav#;9MjvVSwk0!BUHdv`j zzcTne?9*?ugzElsin1EN=^KATXAD%eltxgL$lBile%r6sjP3|-? zDSc0;F+pM{d`H7*aNv!PpB{~T5F6uqAX`0rG&;dfzIgs0*>WkGg0CSX zy;EjtYXDwM?!Fyunj(kl5bQVlkBmbHdt9GHC?Gz0*GJ0pIJ(>l1n7Nwn+rf} z%>-nZW0JBPeWv5+O3F#k;*$Gk43;O45%-0!H@s1cu3II35qqez0hsVKUA)*xV|lFY z2Ad!%xa@*vUAtU)ph{QdpAHpos1MKQl)EED6Qu9VNC_cB z7fJMMk7(7G;(Oy~^kW63_&zO>Q-0J4OWGVdTJ$Tk**^rRJt-Rw`Y#$n9d0j)uj(^= zEqXCXQ}fKf_jiQT(xM<%6z@+^((Lo_F`p-~62$i35REb6fhX_>ZgNHUp#jr_g%{e# z`h^x2+&4_fHmy=&Pb6Q?;=`~kq(Y1_JCxHUmc&0&xzBj0s3`7Mh8jR$j)9PB=?lqk z@~-GOnBEnxJnu&HYRTyxCSg4Edh%+c28|HL+%t}L-7;yCCzDfJ}UI1-VcWSN>QNwNonmf zZojDcx;h-#I~$@xe3wWp1QB&v#4SSP0Yn(P{Uu^M_P4I}uUBVS8vyO-nXlg+w3U~A zP6t7YM&>73VAFQ{6mXIW1MUBOvz#-+ezkx@#F%BMIf~aok3QB6>Irl%E-wpIjuBJZfQ@wH)Oo^4+Z}7+8pnQiYBf4miXq zpcw2;*>XQx{Y)ubg2P#WeJh@oO8J@T(OvRoBqD^bekhU`1DrOZubjc7bo|Gvkq@@89U(8vOY;SU#ba zf&QMvKs4ZN)E|TncX^IK0@1(@he8i7`&nr)d6eUhrC^O}1Z(K!_LD15nUDx1p80iF5UcA~3EFf*iCt%7U&)OE{gEYj!?0Gsf z*8G9=5&(I*riHA%l)H8k0Eu(q!MN8+RqiOh$WA(FmVFdi^Sqb*xayOZ$X;$as+?r~ z1aIyr$&hfb$42cMQk+)DN}!LG_FIE&ymyP}`9;i)iOuDMFDLp`7FEGN zyQuG66{~?tN_+DthDx3jjQiQlmZhU4`eng9JMqbF|0fb`Rtp=3cWJ@BZbCfMOln?W&-x!P28#$?$WWN=btBAKNO}8ojJpAZ1 zrn08gEyXxrPJe<~5=4W$E z?H=Lh4d`*Wu0gezwK0@n+^4I?E2M~ymd3;pag|>s`%QUymRyI9X@1N!RKuH%M=8?{Flk3Tr z`)Y=|bbsDoL0~a=Q6-mwz5{g>Jwwb#m18}$=+StgbTr*=Uv*^q82U1xW!{?L*&ipL zct<{DIF2ht#DIg+#!P?dqtK((#R0zMw$1bE1Pz)YCv1I>;(|(EY)R+=HC)Lx#+!Z$ zX%qUS)8S{^Rt!G5Parqn=@&pSW+mMK1W;Tw4h#waK+6kL_R2_gz}9Ldwj?>oN9_5& zL2-GUn@)``LJ1y@8a~sXg2+3-h+t)$!_mc366+HXFf>dsYk(}!5Vd%u3N6f#d5lrUf)j(iL!y zGI#IiuC_WKJ8NFoWulYkO~79KV*0lk=0C+>FXbMJJI_?(0s0=nZGl@xdfF{>d`Gdl z0=VYcBTvco3v(OCrq;ID9k8AwE#zB+e&Wgp7lUiRuRBb&2NuAR@alx!;{8sESy9pi z*0PqRDZC+%#TvTM>Ri_FRTn!(*Dfa@qs%bUVyf*P;$8btJqWmi!)imTvDw|@lN{LX z%q`NDS=Uhy)}zoFC^CUJJ)=saB!);c)yAo(=Ab8JfBVpgqg`yl;DOtIM{ zJ>b;=uLSvzY(GlB+AtrKO#%+O>*k;T$hhHxjg16{%@rsVh-D_d{FpWP8gz1H3Jy}j z-wJYNz`Yx1lRulsmkCa5qQ7X+A*O|}(j+E-2cmm6&WrQ})MI?fRiz0`1zRjNp~`Fm zP<=j*13aStk?CXdZ+I<-ZBCnMqRj~GF0+09 z2%>xY1#pFMK%)kiH_-NBGLo0JHL?%znb{6WM^!Aph~8^`RV>KsFxs}AT%+2?Ta@)r z`A@YhLFYd*!PCL6ORmcQ%`+=9A@4$e38qW>Ta-%)T#{r@U=K{NQV+xipfCD168I?2 zf&GZBFPLMl2&c2ww(g2DWKC7m)0(#_e4X$tO_+Z3!t^-FqgoTUOH4}&>B1|n{zrD8 zSz$t%(T@lTmc;Q5h;MKJ^;;FG2>dbUn0`y$f||NwabCS~pqPFV&vEDK6*9!E@`4iN zKCF42B*ELosK$yo(QW(RI96t0+*)+k>#kT{VPswCE&$kK@K8{tvz22Dl?rjIl)|(| za~}+COJ9#XdDcj+ENDE0AR`G8_0t0u+r2iy-!!7W!~AuOZ@4p@)ODU|%;ZTm+=m=Z zp`!$;C|<|jy{<`1ngsq&@JEi`Q`};h+3s;b)2Ilxu@bN;tWK(rNk~?GS%jywy)EQw z9(Y_DJjur-57?-myY3EkQZ*-nuOMcze~B#cj7A@KB{rrB{OnMLV0ZW0lQLYIpBW|Q zU4Owxu?^A~uf#l@$qTL85rFPTNp_s#zY3D*6ZGx$KBSABdqy_x(p1#*HF*67%LvyQ zRE3VAL4!0Po08e5lJN){YEs39VaS$SG1{ z$h?d?xE4ZT4V-q^yB$@L|$@@CQ%CTX@GbB>>Sq%j@z3D@4QthQA ztgGL4qWH&wJ-{s=#}|LMKDeku^a}ncYi_N->rv#(xoO5U7Vj}Q!|<5$)(uX+qgkBj z!_?k7EgV(P%m{k?5#W7okb^xo@sh=3CtO-GNTPhId5w1HV9XVOQKxAr_{br8G=O6> z!5Vfl&yn~E<|flF^VR2ZaR`DApvh}aIXl4Yz+TfsO=Bv@?DRVgLy|w=ef#Ux$X3^K z(oh$Jv~Tvf`5=k0p>JOcdf$OoeOy-O6k}lT0r9OtGWxh6M(}$c`I1{1P z4bw$;xUfOrPUE2z^#?kBdvr$n`g-7X6b7vHqWkbhob;08t;b~7;E}@uKf9t{BGq2ZI76*`m?XDBq2+d|PBceiMat2FIO*O}@Vo+@o@onA?cR6W$xb{T zGE!*sV!_GJ==X3T#eGBC)O2BHGT`2*PwJ=nkBri7on1o)tKOMYnzK-E1GBP1c0`P* z{(=l6=bIS~TG~g{#%H{OwwoHgbg+;u+F(HT9Sy+*gl%_C`FhBW-$I#9zHe!1-k4dC;SO<_bSh@}VEb-@RU~ zDG=KgQ

}+gIY@MCp|H)j~!qLRY+0N0O{=dBb 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 z
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 qRuzuk(EngR3h#6~( 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 diff --git a/bin/reportlab/fonts/luxiserif_license.txt b/bin/reportlab/fonts/luxiserif_license.txt deleted file mode 100644 index e8b344b8542..00000000000 --- a/bin/reportlab/fonts/luxiserif_license.txt +++ /dev/null @@ -1,40 +0,0 @@ -Bigelow & Holmes Inc and URW++ GmbH Luxi font license - -Luxi fonts copyright (c) 2001 by Bigelow & Holmes Inc. Luxi font instruction -code copyright (c) 2001 by URW++ GmbH. All Rights Reserved. Luxi is a -registered trademark of Bigelow & Holmes Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of these Fonts and associated documentation files (the "Font Software"), to -deal in the Font Software, including without limitation the rights to use, -copy, merge, publish, distribute, sublicense, and/or sell copies of the Font -Software, and to permit persons to whom the Font Software is furnished to do -so, subject to the following conditions: - -The above copyright and trademark notices and this permission notice shall be -included in all copies of one or more of the Font Software. - -The Font Software may not be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may not be -modified nor may additional glyphs or characters be added to the Fonts. This -License becomes null and void when the Fonts or Font Software have been -modified. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BIGELOW & HOLMES INC. OR URW++ -GMBH. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY -GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN -AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR -INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT -SOFTWARE. - -Except as contained in this notice, the names of Bigelow & Holmes Inc. and -URW++ GmbH. shall not be used in advertising or otherwise to promote the -sale, use or other dealings in this Font Software without prior written -authorization from Bigelow & Holmes Inc. and URW++ GmbH. - -For further information, contact: - -info@urwpp.de or design@bigelowandholmes.com diff --git a/bin/reportlab/fonts/rina.ttf b/bin/reportlab/fonts/rina.ttf deleted file mode 100644 index f21f0bae3a398b4b17e51158f48aa96bbd95cd05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36176 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 -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -__version__ = '0.9' - -def getCodes(): - """Returns a dict mapping code names to widgets""" - - from widgets import (BarcodeI2of5, - BarcodeCode128, - BarcodeStandard93, - BarcodeExtended93, - BarcodeStandard39, - BarcodeExtended39, - BarcodeMSI, - BarcodeCodabar, - BarcodeCode11, - BarcodeFIM, - BarcodePOSTNET) - - #newer codes will typically get their own module - from eanbc import Ean13BarcodeWidget, Ean8BarcodeWidget - - - #the module exports a dictionary of names to widgets, to make it easy for - #apps and doc tools to display information about them. - codes = {} - for widget in ( - BarcodeI2of5, - BarcodeCode128, - BarcodeStandard93, - BarcodeExtended93, - BarcodeStandard39, - BarcodeExtended39, - BarcodeMSI, - BarcodeCodabar, - BarcodeCode11, - BarcodeFIM, - BarcodePOSTNET, - Ean13BarcodeWidget, - Ean8BarcodeWidget, - ): - codeName = widget.codeName - codes[codeName] = widget - - return codes - -def getCodeNames(): - """Returns sorted list of supported bar code names""" - return sorted(getCodes().keys()) - -def createBarcodeDrawing(codeName, **options): - """This creates and returns a drawing with a barcode. - """ - from reportlab.graphics.shapes import Drawing, Group - - codes = getCodes() - bcc = codes[codeName] - width = options.pop('width',None) - height = options.pop('height',None) - isoScale = options.pop('isoScale',0) - kw = {} - for k,v in options.iteritems(): - if k.startswith('_') or k in bcc._attrMap: kw[k] = v - bc = bcc(**kw) - - #size it after setting the data - x1, y1, x2, y2 = bc.getBounds() - w = float(x2 - x1) - h = float(y2 - y1) - sx = width not in ('auto',None) - sy = height not in ('auto',None) - if sx or sy: - sx = sx and width/w or 1.0 - sy = sy and height/h or 1.0 - if isoScale: - if sx<1.0 and sy<1.0: - sx = sy = max(sx,sy) - else: - sx = sy = min(sx,sy) - - w *= sx - h *= sy - else: - sx = sy = 1 - - #bc.x = -sx*x1 - #bc.y = -sy*y1 - d = Drawing(width=w,height=h,transform=[sx,0,0,sy,-sx*x1,-sy*y1]) - d.add(bc, "_bc") - return d - -def createBarcodeImageInMemory(codeName, **options): - """This creates and returns barcode as an image in memory. - """ - d = createBarcodeDrawing(codeName, **options) - return d.asString(format) diff --git a/bin/reportlab/graphics/barcode/code128.py b/bin/reportlab/graphics/barcode/code128.py deleted file mode 100644 index 443edb06c84..00000000000 --- a/bin/reportlab/graphics/barcode/code128.py +++ /dev/null @@ -1,322 +0,0 @@ -# -# Copyright (c) 2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import MultiWidthBarcode -from string import digits - -_patterns = { - 0 : 'BaBbBb', 1 : 'BbBaBb', 2 : 'BbBbBa', - 3 : 'AbAbBc', 4 : 'AbAcBb', 5 : 'AcAbBb', - 6 : 'AbBbAc', 7 : 'AbBcAb', 8 : 'AcBbAb', - 9 : 'BbAbAc', 10 : 'BbAcAb', 11 : 'BcAbAb', - 12 : 'AaBbCb', 13 : 'AbBaCb', 14 : 'AbBbCa', - 15 : 'AaCbBb', 16 : 'AbCaBb', 17 : 'AbCbBa', - 18 : 'BbCbAa', 19 : 'BbAaCb', 20 : 'BbAbCa', - 21 : 'BaCbAb', 22 : 'BbCaAb', 23 : 'CaBaCa', - 24 : 'CaAbBb', 25 : 'CbAaBb', 26 : 'CbAbBa', - 27 : 'CaBbAb', 28 : 'CbBaAb', 29 : 'CbBbAa', - 30 : 'BaBaBc', 31 : 'BaBcBa', 32 : 'BcBaBa', - 33 : 'AaAcBc', 34 : 'AcAaBc', 35 : 'AcAcBa', - 36 : 'AaBcAc', 37 : 'AcBaAc', 38 : 'AcBcAa', - 39 : 'BaAcAc', 40 : 'BcAaAc', 41 : 'BcAcAa', - 42 : 'AaBaCc', 43 : 'AaBcCa', 44 : 'AcBaCa', - 45 : 'AaCaBc', 46 : 'AaCcBa', 47 : 'AcCaBa', - 48 : 'CaCaBa', 49 : 'BaAcCa', 50 : 'BcAaCa', - 51 : 'BaCaAc', 52 : 'BaCcAa', 53 : 'BaCaCa', - 54 : 'CaAaBc', 55 : 'CaAcBa', 56 : 'CcAaBa', - 57 : 'CaBaAc', 58 : 'CaBcAa', 59 : 'CcBaAa', - 60 : 'CaDaAa', 61 : 'BbAdAa', 62 : 'DcAaAa', - 63 : 'AaAbBd', 64 : 'AaAdBb', 65 : 'AbAaBd', - 66 : 'AbAdBa', 67 : 'AdAaBb', 68 : 'AdAbBa', - 69 : 'AaBbAd', 70 : 'AaBdAb', 71 : 'AbBaAd', - 72 : 'AbBdAa', 73 : 'AdBaAb', 74 : 'AdBbAa', - 75 : 'BdAbAa', 76 : 'BbAaAd', 77 : 'DaCaAa', - 78 : 'BdAaAb', 79 : 'AcDaAa', 80 : 'AaAbDb', - 81 : 'AbAaDb', 82 : 'AbAbDa', 83 : 'AaDbAb', - 84 : 'AbDaAb', 85 : 'AbDbAa', 86 : 'DaAbAb', - 87 : 'DbAaAb', 88 : 'DbAbAa', 89 : 'BaBaDa', - 90 : 'BaDaBa', 91 : 'DaBaBa', 92 : 'AaAaDc', - 93 : 'AaAcDa', 94 : 'AcAaDa', 95 : 'AaDaAc', - 96 : 'AaDcAa', 97 : 'DaAaAc', 98 : 'DaAcAa', - 99 : 'AaCaDa', 100 : 'AaDaCa', 101 : 'CaAaDa', - 102 : 'DaAaCa', 103 : 'BaAdAb', 104 : 'BaAbAd', - 105 : 'BaAbCb', 106 : 'BcCaAaB' -} - -starta, startb, startc, stop = 103, 104, 105, 106 - -seta = { - ' ' : 0, '!' : 1, '"' : 2, '#' : 3, - '$' : 4, '%' : 5, '&' : 6, '\'' : 7, - '(' : 8, ')' : 9, '*' : 10, '+' : 11, - ',' : 12, '-' : 13, '.' : 14, '/' : 15, - '0' : 16, '1' : 17, '2' : 18, '3' : 19, - '4' : 20, '5' : 21, '6' : 22, '7' : 23, - '8' : 24, '9' : 25, ':' : 26, ';' : 27, - '<' : 28, '=' : 29, '>' : 30, '?' : 31, - '@' : 32, 'A' : 33, 'B' : 34, 'C' : 35, - 'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39, - 'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43, - 'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47, - 'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51, - 'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55, - 'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59, - '\\' : 60, ']' : 61, '^' : 62, '_' : 63, - '\x00' : 64, '\x01' : 65, '\x02' : 66, '\x03' : 67, - '\x04' : 68, '\x05' : 69, '\x06' : 70, '\x07' : 71, - '\x08' : 72, '\x09' : 73, '\x0a' : 74, '\x0b' : 75, - '\x0c' : 76, '\x0d' : 77, '\x0e' : 78, '\x0f' : 79, - '\x10' : 80, '\x11' : 81, '\x12' : 82, '\x13' : 83, - '\x14' : 84, '\x15' : 85, '\x16' : 86, '\x17' : 87, - '\x18' : 88, '\x19' : 89, '\x1a' : 90, '\x1b' : 91, - '\x1c' : 92, '\x1d' : 93, '\x1e' : 94, '\x1f' : 95, - '\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99, - 'TO_B' : 100, '\xf4' : 101, '\xf1' : 102 -} - -setb = { - ' ' : 0, '!' : 1, '"' : 2, '#' : 3, - '$' : 4, '%' : 5, '&' : 6, '\'' : 7, - '(' : 8, ')' : 9, '*' : 10, '+' : 11, - ',' : 12, '-' : 13, '.' : 14, '/' : 15, - '0' : 16, '1' : 17, '2' : 18, '3' : 19, - '4' : 20, '5' : 21, '6' : 22, '7' : 23, - '8' : 24, '9' : 25, ':' : 26, ';' : 27, - '<' : 28, '=' : 29, '>' : 30, '?' : 31, - '@' : 32, 'A' : 33, 'B' : 34, 'C' : 35, - 'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39, - 'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43, - 'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47, - 'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51, - 'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55, - 'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59, - '\\' : 60, ']' : 61, '^' : 62, '_' : 63, - '`' : 64, 'a' : 65, 'b' : 66, 'c' : 67, - 'd' : 68, 'e' : 69, 'f' : 70, 'g' : 71, - 'h' : 72, 'i' : 73, 'j' : 74, 'k' : 75, - 'l' : 76, 'm' : 77, 'n' : 78, 'o' : 79, - 'p' : 80, 'q' : 81, 'r' : 82, 's' : 83, - 't' : 84, 'u' : 85, 'v' : 86, 'w' : 87, - 'x' : 88, 'y' : 89, 'z' : 90, '{' : 91, - '|' : 92, '}' : 93, '~' : 94, '\x7f' : 95, - '\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99, - '\xf4' : 100, 'TO_A' : 101, '\xf1' : 102 -} - -setc = { - '00': 0, '01': 1, '02': 2, '03': 3, '04': 4, - '05': 5, '06': 6, '07': 7, '08': 8, '09': 9, - '10':10, '11':11, '12':12, '13':13, '14':14, - '15':15, '16':16, '17':17, '18':18, '19':19, - '20':20, '21':21, '22':22, '23':23, '24':24, - '25':25, '26':26, '27':27, '28':28, '29':29, - '30':30, '31':31, '32':32, '33':33, '34':34, - '35':35, '36':36, '37':37, '38':38, '39':39, - '40':40, '41':41, '42':42, '43':43, '44':44, - '45':45, '46':46, '47':47, '48':48, '49':49, - '50':50, '51':51, '52':52, '53':53, '54':54, - '55':55, '56':56, '57':57, '58':58, '59':59, - '60':60, '61':61, '62':62, '63':63, '64':64, - '65':65, '66':66, '67':67, '68':68, '69':69, - '70':70, '71':71, '72':72, '73':73, '74':74, - '75':75, '76':76, '77':77, '78':78, '79':79, - '80':80, '81':81, '82':82, '83':83, '84':84, - '85':85, '86':86, '87':87, '88':88, '89':89, - '90':90, '91':91, '92':92, '93':93, '94':94, - '95':95, '96':96, '97':97, '98':98, '99':99, - - 'TO_B' : 100, 'TO_A' : 101, '\xf1' : 102 -} - -setmap = { - 'TO_A' : (seta, setb), - 'TO_B' : (setb, seta), - 'TO_C' : (setc, None), - 'START_A' : (starta, seta, setb), - 'START_B' : (startb, setb, seta), - 'START_C' : (startc, setc, None), -} -tos = setmap.keys() - -class Code128(MultiWidthBarcode): - """ - Code 128 is a very compact symbology that can encode the entire - 128 character ASCII set, plus 4 special control codes, - (FNC1-FNC4, expressed in the input string as \xf1 to \xf4). - Code 128 can also encode digits at double density (2 per byte) - and has a mandatory checksum. Code 128 is well supported and - commonly used -- for example, by UPS for tracking labels. - - Because of these qualities, Code 128 is probably the best choice - for a linear symbology today (assuming you have a choice). - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 barWidth - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 128: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_128.html - http://www.adams1.com/pub/russadam/128code.html - http://www.barcodeman.com/c128.html - - Official Spec, "ANSI/AIM BC4-1999, ISS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - barWidth = inch * 0.0075 - lquiet = None - rquiet = None - quiet = 1 - barHeight = None - def __init__(self, value='', **args): - - if type(value) is type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.barWidth * 10.0) - if self.rquiet is None: - self.rquiet = max(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - MultiWidthBarcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if ord(c) > 127 and c not in '\xf1\xf2\xf3\xf4': - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def _trailingDigitsToC(self, l): - # Optimization: trailing digits -> set C double-digits - c = 1 - savings = -1 # the TO_C costs one character - rl = ['STOP'] - while c < len(l): - i = (-c - 1) - if l[i] == '\xf1': - c = c + 1 - rl.insert(0, '\xf1') - continue - elif len(l[i]) == 1 and l[i] in digits \ - and len(l[i-1]) == 1 and l[i-1] in digits: - c += 2 - savings += 1 - rl.insert(0, l[i-1] + l[i]) - continue - else: - break - if savings > 0: - return l[:-c] + ['TO_C'] + rl - else: - return l - - def encode(self): - # First, encode using only B - s = self.validated - l = ['START_B'] - for c in s: - if not setb.has_key(c): - l = l + ['TO_A', c, 'TO_B'] - else: - l.append(c) - l.append('STOP') - - l = self._trailingDigitsToC(l) - - # Finally, replace START_X,TO_Y with START_Y - if l[1] in tos: - l[:2] = ['START_' + l[1][-1]] - -# print `l` - - # encode into numbers - start, set, shset = setmap[l[0]] - e = [start] - - l = l[1:-1] - while l: - c = l[0] - if c == 'SHIFT': - e = e + [set[c], shset[l[1]]] - l = l[2:] - elif c in tos: - e.append(set[c]) - set, shset = setmap[c] - l = l[1:] - else: - e.append(set[c]) - l = l[1:] - - c = e[0] - for i in range(1, len(e)): - c = c + i * e[i] - self.encoded = e + [c % 103, stop] - return self.encoded - - def decompose(self): - self.decomposed = ''.join([_patterns[c] for c in self.encoded]) - return self.decomposed - - def _humanText(self): - return self.value diff --git a/bin/reportlab/graphics/barcode/code39.py b/bin/reportlab/graphics/barcode/code39.py deleted file mode 100644 index 697b54045b7..00000000000 --- a/bin/reportlab/graphics/barcode/code39.py +++ /dev/null @@ -1,248 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -_patterns = { - '0': ("bsbSBsBsb", 0), '1': ("BsbSbsbsB", 1), - '2': ("bsBSbsbsB", 2), '3': ("BsBSbsbsb", 3), - '4': ("bsbSBsbsB", 4), '5': ("BsbSBsbsb", 5), - '6': ("bsBSBsbsb", 6), '7': ("bsbSbsBsB", 7), - '8': ("BsbSbsBsb", 8), '9': ("bsBSbsBsb", 9), - 'A': ("BsbsbSbsB", 10), 'B': ("bsBsbSbsB", 11), - 'C': ("BsBsbSbsb", 12), 'D': ("bsbsBSbsB", 13), - 'E': ("BsbsBSbsb", 14), 'F': ("bsBsBSbsb", 15), - 'G': ("bsbsbSBsB", 16), 'H': ("BsbsbSBsb", 17), - 'I': ("bsBsbSBsb", 18), 'J': ("bsbsBSBsb", 19), - 'K': ("BsbsbsbSB", 20), 'L': ("bsBsbsbSB", 21), - 'M': ("BsBsbsbSb", 22), 'N': ("bsbsBsbSB", 23), - 'O': ("BsbsBsbSb", 24), 'P': ("bsBsBsbSb", 25), - 'Q': ("bsbsbsBSB", 26), 'R': ("BsbsbsBSb", 27), - 'S': ("bsBsbsBSb", 28), 'T': ("bsbsBsBSb", 29), - 'U': ("BSbsbsbsB", 30), 'V': ("bSBsbsbsB", 31), - 'W': ("BSBsbsbsb", 32), 'X': ("bSbsBsbsB", 33), - 'Y': ("BSbsBsbsb", 34), 'Z': ("bSBsBsbsb", 35), - '-': ("bSbsbsBsB", 36), '.': ("BSbsbsBsb", 37), - ' ': ("bSBsbsBsb", 38), '*': ("bSbsBsBsb", 39), - '$': ("bSbSbSbsb", 40), '/': ("bSbSbsbSb", 41), - '+': ("bSbsbSbSb", 42), '%': ("bsbSbSbSb", 43) -} - -_valchars = [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', - 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%' -] - -_extended = { - '\0': "%U", '\01': "$A", '\02': "$B", '\03': "$C", - '\04': "$D", '\05': "$E", '\06': "$F", '\07': "$G", - '\010': "$H", '\011': "$I", '\012': "$J", '\013': "$K", - '\014': "$L", '\015': "$M", '\016': "$N", '\017': "$O", - '\020': "$P", '\021': "$Q", '\022': "$R", '\023': "$S", - '\024': "$T", '\025': "$U", '\026': "$V", '\027': "$W", - '\030': "$X", '\031': "$Y", '\032': "$Z", '\033': "%A", - '\034': "%B", '\035': "%C", '\036': "%D", '\037': "%E", - '!': "/A", '"': "/B", '#': "/C", '$': "/D", - '%': "/E", '&': "/F", '\'': "/G", '(': "/H", - ')': "/I", '*': "/J", '+': "/K", ',': "/L", - '/': "/O", ':': "/Z", ';': "%F", '<': "%G", - '=': "%H", '>': "%I", '?': "%J", '@': "%V", - '[': "%K", '\\': "%L", ']': "%M", '^': "%N", - '_': "%O", '`': "%W", 'a': "+A", 'b': "+B", - 'c': "+C", 'd': "+D", 'e': "+E", 'f': "+F", - 'g': "+G", 'h': "+H", 'i': "+I", 'j': "+J", - 'k': "+K", 'l': "+L", 'm': "+M", 'n': "+N", - 'o': "+O", 'p': "+P", 'q': "+Q", 'r': "+R", - 's': "+S", 't': "+T", 'u': "+U", 'v': "+V", - 'w': "+W", 'x': "+X", 'y': "+Y", 'z': "+Z", - '{': "%P", '|': "%Q", '}': "%R", '~': "%S", - '\177': "%T" -} - - -_stdchrs = string.digits + string.uppercase + "-. *$/+%" -_extchrs = _stdchrs + string.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" - -def _encode39(value, cksum, stop): - v = sum([_patterns[c][1] for c in value]) % 43 - if cksum: - value += _valchars[v] - if stop: value = '*'+value+'*' - return value - -class _Code39Base(Barcode): - barWidth = inch * 0.0075 - lquiet = None - rquiet = None - quiet = 1 - gap = None - barHeight = None - ratio = 2.2 - checksum = 1 - bearers = 0.0 - stop = 1 - def __init__(self, value = "", **args): - for k, v in args.iteritems(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.barWidth * 10.0) - self.rquiet = max(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def decompose(self): - dval = "" - for c in self.encoded: - dval = dval + _patterns[c][0] + 'i' - self.decomposed = dval[:-1] - return self.decomposed - - def _humanText(self): - return self.stop and self.encoded[1:-1] or self.encoded - -class Standard39(_Code39Base): - """ - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the - barWidth is greater than 20 mils (.02 inch)) - - gap (float or None, default None): - width of intercharacter gap. None means "use barWidth". - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 1): - Wether to compute and include the check digit - - bearers (float, in units of barWidth. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - stop (bool, default 1): - Whether to include start/stop symbols. - - Sources of Information on Code 39: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_39.html - http://www.adams1.com/pub/russadam/39code.html - http://www.barcodeman.com/c39_1.html - - Official Spec, "ANSI/AIM BC1-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c in string.lowercase: - c = string.upper(c) - if c not in _stdchrs: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = _encode39(self.validated, self.checksum, self.stop) - return self.encoded - -class Extended39(_Code39Base): - """ - Extended Code 39 is a convention for encoding additional characters - not present in stanmdard Code 39 by using pairs of characters to - represent the characters missing in Standard Code 39. - - See Standard39 for arguments. - - Sources of Information on Extended Code 39: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/xcode_39.html - http://www.barcodeman.com/c39_ext.html - """ - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c not in _extchrs: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = "" - for c in self.validated: - if _extended.has_key(c): - self.encoded = self.encoded + _extended[c] - elif c in _stdchrs: - self.encoded = self.encoded + c - else: - raise ValueError - self.encoded = _encode39(self.encoded, self.checksum,self.stop) - return self.encoded diff --git a/bin/reportlab/graphics/barcode/code93.py b/bin/reportlab/graphics/barcode/code93.py deleted file mode 100644 index 0ab52e945e3..00000000000 --- a/bin/reportlab/graphics/barcode/code93.py +++ /dev/null @@ -1,236 +0,0 @@ -# -# Copyright (c) 2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import MultiWidthBarcode -import string - -_patterns = { - '0' : ('AcAaAb', 0), '1' : ('AaAbAc', 1), '2' : ('AaAcAb', 2), - '3' : ('AaAdAa', 3), '4' : ('AbAaAc', 4), '5' : ('AbAbAb', 5), - '6' : ('AbAcAa', 6), '7' : ('AaAaAd', 7), '8' : ('AcAbAa', 8), - '9' : ('AdAaAa', 9), 'A' : ('BaAaAc', 10), 'B' : ('BaAbAb', 11), - 'C' : ('BaAcAa', 12), 'D' : ('BbAaAb', 13), 'E' : ('BbAbAa', 14), - 'F' : ('BcAaAa', 15), 'G' : ('AaBaAc', 16), 'H' : ('AaBbAb', 17), - 'I' : ('AaBcAa', 18), 'J' : ('AbBaAb', 19), 'K' : ('AcBaAa', 20), - 'L' : ('AaAaBc', 21), 'M' : ('AaAbBb', 22), 'N' : ('AaAcBa', 23), - 'O' : ('AbAaBb', 24), 'P' : ('AcAaBa', 25), 'Q' : ('BaBaAb', 26), - 'R' : ('BaBbAa', 27), 'S' : ('BaAaBb', 28), 'T' : ('BaAbBa', 29), - 'U' : ('BbAaBa', 30), 'V' : ('BbBaAa', 31), 'W' : ('AaBaBb', 32), - 'X' : ('AaBbBa', 33), 'Y' : ('AbBaBa', 34), 'Z' : ('AbCaAa', 35), - '-' : ('AbAaCa', 36), '.' : ('CaAaAb', 37), ' ' : ('CaAbAa', 38), - '$' : ('CbAaAa', 39), '/' : ('AaBaCa', 40), '+' : ('AaCaBa', 41), - '%' : ('BaAaCa', 42), '#' : ('AbAbBa', 43), '!' : ('CaBaAa', 44), - '=' : ('CaAaBa', 45), '&' : ('AbBbAa', 46), - 'start' : ('AaAaDa', -1), 'stop' : ('AaAaDaA', -2) -} - -_charsbyval = {} -for k, v in _patterns.items(): - _charsbyval[v[1]] = k - -_extended = { - '\x00' : '!U', '\x01' : '#A', '\x02' : '#B', '\x03' : '#C', - '\x04' : '#D', '\x05' : '#E', '\x06' : '#F', '\x07' : '#G', - '\x08' : '#H', '\x09' : '#I', '\x0a' : '#J', '\x0b' : '#K', - '\x0c' : '#L', '\x0d' : '#M', '\x0e' : '#N', '\x0f' : '#O', - '\x10' : '#P', '\x11' : '#Q', '\x12' : '#R', '\x13' : '#S', - '\x14' : '#T', '\x15' : '#U', '\x16' : '#V', '\x17' : '#W', - '\x18' : '#X', '\x19' : '#Y', '\x1a' : '#Z', '\x1b' : '!A', - '\x1c' : '!B', '\x1d' : '!C', '\x1e' : '!D', '\x1f' : '!E', - '!' : '=A', '"' : '=B', '#' : '=C', '$' : '=D', - '%' : '=E', '&' : '=F', '\'' : '=G', '(' : '=H', - ')' : '=I', '*' : '=J', '+' : '=K', ',' : '=L', - '/' : '=O', ':' : '=Z', ';' : '!F', '<' : '!G', - '=' : '!H', '>' : '!I', '?' : '!J', '@' : '!V', - '[' : '!K', '\\' : '!L', ']' : '!M', '^' : '!N', - '_' : '!O', '`' : '!W', 'a' : '&A', 'b' : '&B', - 'c' : '&C', 'd' : '&D', 'e' : '&E', 'f' : '&F', - 'g' : '&G', 'h' : '&H', 'i' : '&I', 'j' : '&J', - 'k' : '&K', 'l' : '&L', 'm' : '&M', 'n' : '&N', - 'o' : '&O', 'p' : '&P', 'q' : '&Q', 'r' : '&R', - 's' : '&S', 't' : '&T', 'u' : '&U', 'v' : '&V', - 'w' : '&W', 'x' : '&X', 'y' : '&Y', 'z' : '&Z', - '{' : '!P', '|' : '!Q', '}' : '!R', '~' : '!S', - '\x7f' : '!T' -} - -def _encode93(str): - s = map(None, str) - s.reverse() - - # compute 'C' checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * _patterns[s[i]][1] - i = i + 1; v = v + 1 - if v > 20: - v = 1 - s.insert(0, _charsbyval[c % 47]) - - # compute 'K' checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * _patterns[s[i]][1] - i = i + 1; v = v + 1 - if v > 15: - v = 1 - s.insert(0, _charsbyval[c % 47]) - - s.reverse() - - return string.join(s, '') - -class _Code93Base(MultiWidthBarcode): - barWidth = inch * 0.0075 - lquiet = None - rquiet = None - quiet = 1 - barHeight = None - stop = 1 - def __init__(self, value='', **args): - - if type(value) is type(1): - value = str(value) - - for (k, v) in args.iteritems(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.barWidth * 10.0) - self.rquiet = max(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - MultiWidthBarcode.__init__(self, value) - - def decompose(self): - dval = self.stop and [_patterns['start'][0]] or [] - dval += [_patterns[c][0] for c in self.encoded] - if self.stop: dval.append(_patterns['stop'][0]) - self.decomposed = ''.join(dval) - return self.decomposed - -class Standard93(_Code93Base): - """ - Code 93 is a Uppercase alphanumeric symbology with some punctuation. - See Extended Code 93 for a variant that can represent the entire - 128 characrter ASCII set. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 barWidth - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - stop (bool, default 1): - Whether to include start/stop symbols. - - Sources of Information on Code 93: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_93.html - - Official Spec, "NSI/AIM BC5-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c in string.lowercase: - c = string.upper(c) - if not _patterns.has_key(c): - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = _encode93(self.validated) - return self.encoded - - -class Extended93(_Code93Base): - """ - Extended Code 93 is a convention for encoding the entire 128 character - set using pairs of characters to represent the characters missing in - Standard Code 93. It is very much like Extended Code 39 in that way. - - See Standard93 for arguments. - """ - - def validate(self): - vval = [] - self.valid = 1 - a = vval.append - for c in self.value: - if not _patterns.has_key(c) and not _extended.has_key(c): - self.valid = 0 - continue - a(c) - self.validated = ''.join(vval) - return self.validated - - def encode(self): - self.encoded = "" - for c in self.validated: - if _patterns.has_key(c): - self.encoded = self.encoded + c - elif _extended.has_key(c): - self.encoded = self.encoded + _extended[c] - else: - raise ValueError - self.encoded = _encode93(self.encoded) - return self.encoded - - def _humanText(self): - return self.validated+self.encoded[-2:] diff --git a/bin/reportlab/graphics/barcode/common.py b/bin/reportlab/graphics/barcode/common.py deleted file mode 100644 index 70dbcba72b6..00000000000 --- a/bin/reportlab/graphics/barcode/common.py +++ /dev/null @@ -1,748 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.platypus.flowables import Flowable -from reportlab.lib.units import inch -import string - -class Barcode(Flowable): - """Abstract Base for barcodes. Includes implementations of - some methods suitable for the more primitive barcode types""" - - fontName = 'Courier' - fontSize = 12 - humanReadable = 0 - - def _humanText(self): - return self.encoded - - def __init__(self, value='',**args): - self.value = value - - for (k, v) in args.items(): - setattr(self, k, v) - - - if not hasattr(self, 'gap'): - self.gap = None - - self.validate() - self.encode() - self.decompose() - self.computeSize() - - def validate(self): - self.valid = 1 - self.validated = self.value - - def encode(self): - self.encoded = self.validated - - def decompose(self): - self.decomposed = self.encoded - - def computeSize(self, *args): - barWidth = self.barWidth - wx = barWidth * self.ratio - - if self.gap == None: - self.gap = barWidth - - w = 0.0 - - for c in self.decomposed: - if c in 'sb': - w = w + barWidth - elif c in 'SB': - w = w + wx - else: # 'i' - w = w + self.gap - - if self.barHeight is None: - self.barHeight = w * 0.15 - self.barHeight = max(0.25 * inch, self.barHeight) - if self.bearers: - self.barHeight = self.barHeight + self.bearers * 2.0 * barWidth - - if self.quiet: - w += self.lquiet + self.rquiet - - self.height = self.barHeight - self.width = w - - def draw(self): - barWidth = self.barWidth - wx = barWidth * self.ratio - - left = self.quiet and self.lquiet or 0 - b = self.bearers * barWidth - bb = b * 0.5 - tb = self.barHeight - (b * 1.5) - - for c in self.decomposed: - if c == 'i': - left = left + self.gap - elif c == 's': - left = left + barWidth - elif c == 'S': - left = left + wx - elif c == 'b': - self.rect(left, bb, barWidth, tb) - left = left + barWidth - elif c == 'B': - self.rect(left, bb, wx, tb) - left = left + wx - - if self.bearers: - self.rect(self.lquiet, 0, \ - self.width - (self.lquiet + self.rquiet), b) - self.rect(self.lquiet, self.barHeight - b, \ - self.width - (self.lquiet + self.rquiet), b) - - self.drawHumanReadable() - - def drawHumanReadable(self): - if self.humanReadable: - #we have text - from reportlab.pdfbase.pdfmetrics import getAscent, stringWidth - s = str(self._humanText()) - fontSize = self.fontSize - fontName = self.fontName - w = stringWidth(s,fontName,fontSize) - width = self.width - if self.quiet: - width -= self.lquiet+self.rquiet - x = self.lquiet - else: - x = 0 - if w>width: fontSize *= width/float(w) - y = 1.07*getAscent(fontName)*fontSize/1000. - self.annotate(x+width/2.,-y,s,fontName,fontSize) - - def rect(self, x, y, w, h): - self.canv.rect(x, y, w, h, stroke=0, fill=1) - - def annotate(self,x,y,text,fontName,fontSize,anchor='middle'): - canv = self.canv - canv.saveState() - canv.setFont(self.fontName,fontSize) - if anchor=='middle': func = 'drawCentredString' - elif anchor=='end': func = 'drawRightString' - else: func = 'drawString' - getattr(canv,func)(text,x,y) - canv.restoreState() - -class MultiWidthBarcode(Barcode): - """Base for variable-bar-width codes like Code93 and Code128""" - - def computeSize(self, *args): - barWidth = self.barWidth - oa, oA = ord('a') - 1, ord('A') - 1 - - w = 0.0 - - for c in self.decomposed: - oc = ord(c) - if c in string.lowercase: - w = w + barWidth * (oc - oa) - elif c in string.uppercase: - w = w + barWidth * (oc - oA) - - if self.barHeight is None: - self.barHeight = w * 0.15 - self.barHeight = max(0.25 * inch, self.barHeight) - - if self.quiet: - w += self.lquiet + self.rquiet - - self.height = self.barHeight - self.width = w - - def draw(self): - oa, oA = ord('a') - 1, ord('A') - 1 - barWidth = self.barWidth - left = self.quiet and self.lquiet or 0 - - for c in self.decomposed: - oc = ord(c) - if c in string.lowercase: - left = left + (oc - oa) * barWidth - elif c in string.uppercase: - w = (oc - oA) * barWidth - self.rect(left, 0, w, self.barHeight) - left += w - self.drawHumanReadable() - -class I2of5(Barcode): - """ - Interleaved 2 of 5 is a numeric-only barcode. It encodes an even - number of digits; if an odd number is given, a 0 is prepended. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the - barWidth is greater than 20 mils (.02 inch)) - - gap (float or None, default None): - width of intercharacter gap. None means "use barWidth". - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 1): - Whether to compute and include the check digit - - bearers (float, in units of barWidth. default 3.0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 3 x-dimensions. - Set to zero for no bearer bars. (Bearer bars help detect - misscans, so it is suggested to leave them on). - - quiet (bool, default 1): - Whether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - stop (bool, default 1): - Whether to include start/stop symbols. - - Sources of Information on Interleaved 2 of 5: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/i_25.html - http://www.adams1.com/pub/russadam/i25code.html - - Official Spec, "ANSI/AIM BC2-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - patterns = { - 'start' : 'bsbs', - 'stop' : 'Bsb', - - 'B0' : 'bbBBb', 'S0' : 'ssSSs', - 'B1' : 'BbbbB', 'S1' : 'SsssS', - 'B2' : 'bBbbB', 'S2' : 'sSssS', - 'B3' : 'BBbbb', 'S3' : 'SSsss', - 'B4' : 'bbBbB', 'S4' : 'ssSsS', - 'B5' : 'BbBbb', 'S5' : 'SsSss', - 'B6' : 'bBBbb', 'S6' : 'sSSss', - 'B7' : 'bbbBB', 'S7' : 'sssSS', - 'B8' : 'BbbBb', 'S8' : 'SssSs', - 'B9' : 'bBbBb', 'S9' : 'sSsSs' - } - - barHeight = None - barWidth = inch * 0.0075 - ratio = 2.2 - checksum = 1 - bearers = 3.0 - quiet = 1 - lquiet = None - rquiet = None - stop = 1 - - def __init__(self, value='', **args): - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.barWidth * 10.0) - self.rquiet = min(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in string.strip(self.value): - if c not in string.digits: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - s = self.validated - - # make sure result will be a multiple of 2 digits long, - # checksum included - if ((len(self.validated) % 2 == 0) and self.checksum) \ - or ((len(self.validated) % 2 == 1) and not self.checksum): - s = '0' + s - - if self.checksum: - c = 0 - cm = 3 - - for d in s: - c = c + cm * int(d) - if cm == 3: - cm = 1 - else: - cm = 3 - - d = 10 - (int(d) % 10) - s = s + `d` - - self.encoded = s - - def decompose(self): - dval = self.stop and [self.patterns['start']] or [] - a = dval.append - - for i in xrange(0, len(self.encoded), 2): - b = self.patterns['B' + self.encoded[i]] - s = self.patterns['S' + self.encoded[i+1]] - - for i in range(0, len(b)): - a(b[i] + s[i]) - - if self.stop: a(self.patterns['stop']) - self.decomposed = ''.join(dval) - return self.decomposed - -class MSI(Barcode): - """ - MSI is a numeric-only barcode. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use barWidth". - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 1): - Wether to compute and include the check digit - - bearers (float, in units of barWidth. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 barWidths. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - stop (bool, default 1): - Whether to include start/stop symbols. - - Sources of Information on MSI Bar Code: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/msi_code.html - http://www.adams1.com/pub/russadam/plessy.html - """ - - patterns = { - 'start' : 'Bs', 'stop' : 'bSb', - - '0' : 'bSbSbSbS', '1' : 'bSbSbSBs', - '2' : 'bSbSBsbS', '3' : 'bSbSBsBs', - '4' : 'bSBsbSbS', '5' : 'bSBsbSBs', - '6' : 'bSBsBsbS', '7' : 'bSBsBsBs', - '8' : 'BsbSbSbS', '9' : 'BsbSbSBs' - } - - stop = 1 - barHeight = None - barWidth = inch * 0.0075 - ratio = 2.2 - checksum = 1 - bearers = 0.0 - quiet = 1 - lquiet = None - rquiet = None - - def __init__(self, value="", **args): - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.barWidth * 10.0) - self.rquiet = max(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in string.strip(self.value): - if c not in string.digits: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum: - c = '' - for i in range(1, len(s), 2): - c = c + s[i] - d = str(int(c) * 2) - t = 0 - for c in d: - t = t + int(c) - for i in range(0, len(s), 2): - t = t + int(s[i]) - c = 10 - (t % 10) - - s = s + str(c) - - self.encoded = s - - def decompose(self): - dval = self.stop and [self.patterns['start']] or [] - dval += [self.patterns[c] for c in self.encoded] - if self.stop: dval.append(self.patterns['stop']) - self.decomposed = ''.join(dval) - return self.decomposed - -class Codabar(Barcode): - """ - Codabar is a numeric plus some puntuation ("-$:/.+") barcode - with four start/stop characters (A, B, C, and D). - - Options that may be passed to constructor: - - value (string. required.): - The value to encode. - - barWidth (float, default .0065): - X-Dimension, or width of the smallest element - minimum is 6.5 mils (.0065 inch) - - ratio (float, default 2.0): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use barWidth". - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 0): - Whether to compute and include the check digit - - bearers (float, in units of barWidth. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Whether to include quiet zones in the symbol. - - stop (bool, default 1): - Whether to include start/stop symbols. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 barWidth - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Codabar - - http://www.semiconductor.agilent.com/barcode/sg/Misc/codabar.html - http://www.barcodeman.com/codabar.html - - Official Spec, "ANSI/AIM BC3-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - patterns = { - '0': 'bsbsbSB', '1': 'bsbsBSb', '2': 'bsbSbsB', - '3': 'BSbsbsb', '4': 'bsBsbSb', '5': 'BsbsbSb', - '6': 'bSbsbsB', '7': 'bSbsBsb', '8': 'bSBsbsb', - '9': 'BsbSbsb', '-': 'bsbSBsb', '$': 'bsBSbsb', - ':': 'BsbsBsB', '/': 'BsBsbsB', '.': 'BsBsBsb', - '+': 'bsBsBsB', 'A': 'bsBSbSb', 'B': 'bSbSbsB', - 'C': 'bsbSbSB', 'D': 'bsbSBSb' - } - - values = { - '0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4, - '5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9, - '-' : 10, '$' : 11, ':' : 12, '/' : 13, '.' : 14, - '+' : 15, 'A' : 16, 'B' : 17, 'C' : 18, 'D' : 19 - } - - chars = string.digits + "-$:/.+" - - stop = 1 - barHeight = None - barWidth = inch * 0.0065 - ratio = 2.0 # XXX ? - checksum = 0 - bearers = 0.0 - quiet = 1 - lquiet = None - rquiet = None - - def __init__(self, value='', **args): - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.barWidth * 10.0) - self.rquiet = min(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - s = string.strip(self.value) - for i in range(0, len(s)): - c = s[i] - if c not in self.chars: - if ((i != 0) and (i != len(s) - 1)) or (c not in 'ABCD'): - self.Valid = 0 - continue - vval = vval + c - - if self.stop: - if vval[0] not in 'ABCD': - vval = 'A' + vval - if vval[-1] not in 'ABCD': - vval = vval + vval[0] - - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum: - v = sum([self.values[c] for c in s]) - s += self.chars[v % 16] - - self.encoded = s - - def decompose(self): - dval = ''.join([self.patterns[c]+'i' for c in self.encoded]) - self.decomposed = dval[:-1] - return self.decomposed - -class Code11(Barcode): - """ - 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.): - The value to encode. - - barWidth (float, default .0075): - X-Dimension, or width of the smallest element - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use barWidth". - - barHeight (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (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. - - bearers (float, in units of barWidth. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 barWidth - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 11: - - http://www.cwi.nl/people/dik/english/codes/barcodes.html - """ - - chars = string.digits + '-' - - patterns = { - '0' : 'bsbsB', '1' : 'BsbsB', '2' : 'bSbsB', - '3' : 'BSbsb', '4' : 'bsBsB', '5' : 'BsBsb', - '6' : 'bSBsb', '7' : 'bsbSB', '8' : 'BsbSb', - '9' : 'Bsbsb', '-' : 'bsBsb', 'S' : 'bsBSb' # Start/Stop - } - - values = { - '0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4, - '5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9, - '-' : 10, - } - - stop = 1 - barHeight = None - barWidth = inch * 0.0075 - ratio = 2.2 # XXX ? - checksum = -1 # Auto - bearers = 0.0 - quiet = 1 - lquiet = None - rquiet = None - def __init__(self, value='', **args): - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.barWidth * 10.0) - self.rquiet = min(inch * 0.25, self.barWidth * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - s = string.strip(self.value) - for i in range(0, len(s)): - c = s[i] - if c not in self.chars: - self.Valid = 0 - continue - vval = vval + c - - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum == -1: - if len(s) <= 10: - self.checksum = 1 - else: - self.checksum = 2 - - if self.checksum > 0: - # compute first checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * string.index(self.chars, s[-(i+1)]) - i = i + 1; v = v + 1 - if v > 10: - v = 1 - s = s + self.chars[c % 11] - - if self.checksum > 1: - # compute second checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * string.index(self.chars, s[-(i+1)]) - i = i + 1; v = v + 1 - if v > 9: - v = 1 - s = s + self.chars[c % 10] - - self.encoded = self.stop and ('S' + s + 'S') or s - - def decompose(self): - dval = [self.patterns[c]+'i' for c in self.encoded] - self.decomposed = ''.join(dval[:-1]) - return self.decomposed - - def _humanText(self): - return self.stop and self.encoded[1:-1] or self.encoded diff --git a/bin/reportlab/graphics/barcode/eanbc.py b/bin/reportlab/graphics/barcode/eanbc.py deleted file mode 100644 index f9fbc73b61c..00000000000 --- a/bin/reportlab/graphics/barcode/eanbc.py +++ /dev/null @@ -1,339 +0,0 @@ -__all__=( - 'Ean13BarcodeWidget','isEanString', - ) -from reportlab.graphics.shapes import Group, String, Rect -from reportlab.lib import colors -from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.lib.validators import isNumber, isColor, isString, Validator, isBoolean -from reportlab.lib.attrmap import * -from reportlab.graphics.charts.areas import PlotArea -from reportlab.lib.units import mm - -#work out a list of manufacturer codes.... -_eanNumberSystems = [ - ('00-13', 'USA & Canada'), - ('20-29', 'In-Store Functions'), - ('30-37', 'France'), - ('40-44', 'Germany'), - ('45', 'Japan (also 49)'), - ('46', 'Russian Federation'), - ('471', 'Taiwan'), - ('474', 'Estonia'), - ('475', 'Latvia'), - ('477', 'Lithuania'), - ('479', 'Sri Lanka'), - ('480', 'Philippines'), - ('482', 'Ukraine'), - ('484', 'Moldova'), - ('485', 'Armenia'), - ('486', 'Georgia'), - ('487', 'Kazakhstan'), - ('489', 'Hong Kong'), - ('49', 'Japan (JAN-13)'), - ('50', 'United Kingdom'), - ('520', 'Greece'), - ('528', 'Lebanon'), - ('529', 'Cyprus'), - ('531', 'Macedonia'), - ('535', 'Malta'), - ('539', 'Ireland'), - ('54', 'Belgium & Luxembourg'), - ('560', 'Portugal'), - ('569', 'Iceland'), - ('57', 'Denmark'), - ('590', 'Poland'), - ('594', 'Romania'), - ('599', 'Hungary'), - ('600-601', 'South Africa'), - ('609', 'Mauritius'), - ('611', 'Morocco'), - ('613', 'Algeria'), - ('619', 'Tunisia'), - ('622', 'Egypt'), - ('625', 'Jordan'), - ('626', 'Iran'), - ('64', 'Finland'), - ('690-692', 'China'), - ('70', 'Norway'), - ('729', 'Israel'), - ('73', 'Sweden'), - ('740', 'Guatemala'), - ('741', 'El Salvador'), - ('742', 'Honduras'), - ('743', 'Nicaragua'), - ('744', 'Costa Rica'), - ('746', 'Dominican Republic'), - ('750', 'Mexico'), - ('759', 'Venezuela'), - ('76', 'Switzerland'), - ('770', 'Colombia'), - ('773', 'Uruguay'), - ('775', 'Peru'), - ('777', 'Bolivia'), - ('779', 'Argentina'), - ('780', 'Chile'), - ('784', 'Paraguay'), - ('785', 'Peru'), - ('786', 'Ecuador'), - ('789', 'Brazil'), - ('80-83', 'Italy'), - ('84', 'Spain'), - ('850', 'Cuba'), - ('858', 'Slovakia'), - ('859', 'Czech Republic'), - ('860', 'Yugloslavia'), - ('869', 'Turkey'), - ('87', 'Netherlands'), - ('880', 'South Korea'), - ('885', 'Thailand'), - ('888', 'Singapore'), - ('890', 'India'), - ('893', 'Vietnam'), - ('899', 'Indonesia'), - ('90-91', 'Austria'), - ('93', 'Australia'), - ('94', 'New Zealand'), - ('955', 'Malaysia'), - ('977', 'International Standard Serial Number for Periodicals (ISSN)'), - ('978', 'International Standard Book Numbering (ISBN)'), - ('979', 'International Standard Music Number (ISMN)'), - ('980', 'Refund receipts'), - ('981-982', 'Common Currency Coupons'), - ('99', 'Coupons') - ] - -manufacturerCodes = {} -for (k, v) in _eanNumberSystems: - words = k.split('-') - if len(words)==2: - fromCode = int(words[0]) - toCode = int(words[1]) - for code in range(fromCode, toCode+1): - manufacturerCodes[code] = v - else: - manufacturerCodes[int(k)] = v - -class isEan13String(Validator): - def test(self,x): - return type(x) is str and len(x)<=12 and len([c for c in x if c in "0123456789"])==12 -isEan13String = isEan13String() - -class Ean13BarcodeWidget(PlotArea): - codeName = "EAN13" - _attrMap = AttrMap(BASE=PlotArea, - value = AttrMapValue(isEan13String, desc='the number'), - fontName = AttrMapValue(isString, desc='fontName'), - fontSize = AttrMapValue(isNumber, desc='font size'), - x = AttrMapValue(isNumber, desc='x-coord'), - y = AttrMapValue(isNumber, desc='y-coord'), - barFillColor = AttrMapValue(isColor, desc='bar color'), - barHeight = AttrMapValue(isNumber, desc='Height of bars.'), - barWidth = AttrMapValue(isNumber, desc='Width of bars.'), - barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'), - barStrokeColor = AttrMapValue(isColor, desc='Color of bar borders.'), - textColor = AttrMapValue(isColor, desc='human readable text color'), - humanReadable = AttrMapValue(isBoolean, desc='if human readable'), - quiet = AttrMapValue(isBoolean, desc='if quiet zone to be used'), - lquiet = AttrMapValue(isBoolean, desc='left quiet zone length'), - rquiet = AttrMapValue(isBoolean, desc='right quiet zone length'), - ) - _digits=12 - _start_right = 7 #for ean-13 left = [0:7] right=[7:13] - _nbars = 113 - barHeight = 25.93*mm #millimeters - barWidth = (37.29/_nbars)*mm - humanReadable = 1 - _0csw = 1 - _1csw = 3 - - #Left Hand Digits. - _left = ( ("0001101", "0011001", "0010011", "0111101", - "0100011", "0110001", "0101111", "0111011", - "0110111", "0001011", - ), #odd left hand digits - ("0100111", "0110011", "0011011", "0100001", - "0011101", "0111001", "0000101", "0010001", - "0001001", "0010111"), #even left hand digits - ) - - _right = ("1110010", "1100110", "1101100", "1000010", - "1011100", "1001110", "1010000", "1000100", - "1001000", "1110100") - - quiet = 1 - rquiet = lquiet = None - _tail = "101" - _sep = "01010" - - _lhconvert={ - "0": (0,0,0,0,0,0), - "1": (0,0,1,0,1,1), - "2": (0,0,1,1,0,1), - "3": (0,0,1,1,1,0), - "4": (0,1,0,0,1,1), - "5": (0,1,1,0,0,1), - "6": (0,1,1,1,0,0), - "7": (0,1,0,1,0,1), - "8": (0,1,0,1,1,0), - "9": (0,1,1,0,1,0) - } - fontSize = 8 #millimeters - fontName = 'Helvetica' - textColor = barFillColor = barStrokeColor = colors.black - barStrokeWidth = 0 - x = 0 - y = 0 - def __init__(self,value='123456789012',**kw): - self.value=max(self._digits-len(value),0)*'0'+value[:self._digits] - for k, v in kw.iteritems(): - setattr(self, k, v) - - width = property(lambda self: self.barWidth*(self._nbars-18+self._calc_quiet(self.lquiet)+self._calc_quiet(self.rquiet))) - - def wrap(self,aW,aH): - return self.width,self.barHeight - - def _encode_left(self,s,a): - cp = self._lhconvert[s[0]] #convert the left hand numbers - _left = self._left - z = ord('0') - for i,c in enumerate(s[1:self._start_right]): - a(_left[cp[i]][ord(c)-z]) - - def _short_bar(self,i): - i += 9 - self._lquiet - return self.humanReadable and ((120: v += 1 - else: - v = 0 - return v - - def draw(self): - g = Group() - gAdd = g.add - barWidth = self.barWidth - width = self.width - barHeight = self.barHeight - x = self.x - y = self.y - gAdd(Rect(x,y,width,barHeight,fillColor=None,strokeColor=None,strokeWidth=0)) - s = self.value+self._checkdigit(self.value) - self._lquiet = lquiet = self._calc_quiet(self.lquiet) - rquiet = self._calc_quiet(self.rquiet) - b = [lquiet*'0',self._tail] #the signal string - a = b.append - self._encode_left(s,a) - a(self._sep) - - z = ord('0') - _right = self._right - for c in s[self._start_right:]: - a(_right[ord(c)-z]) - a(self._tail) - a(rquiet*'0') - - fontSize = self.fontSize - barFillColor = self.barFillColor - barStrokeWidth = self.barStrokeWidth - - fth = fontSize*1.2 - b = ''.join(b) - - lrect = None - for i,c in enumerate(b): - if c=="1": - dh = self._short_bar(i) and fth or 0 - yh = y+dh - if lrect and lrect.y==yh: - lrect.width += barWidth - else: - lrect = Rect(x,yh,barWidth,barHeight-dh,fillColor=barFillColor,strokeWidth=barStrokeWidth,strokeColor=barFillColor) - gAdd(lrect) - else: - lrect = None - x += barWidth - - if self.humanReadable: self._add_human_readable(s,gAdd) - return g - - def _add_human_readable(self,s,gAdd): - barWidth = self.barWidth - fontSize = self.fontSize - textColor = self.textColor - fontName = self.fontName - fth = fontSize*1.2 - # draw the num below the line. - c = s[0] - w = stringWidth(c,fontName,fontSize) - x = self.x+barWidth*(self._lquiet-8) - y = self.y + 0.2*fth - - gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor)) - x = self.x + (33-9+self._lquiet)*barWidth - - c = s[1:7] - gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle')) - - x += 47*barWidth - c = s[7:] - gAdd(String(x,y,c,fontName=fontName,fontSize=fontSize,fillColor=textColor,textAnchor='middle')) - - @classmethod - def _checkdigit(cls,num): - z = ord('0') - iSum = cls._0csw*sum([(ord(x)-z) for x in num[::2]]) \ - + cls._1csw*sum([(ord(x)-z) for x in num[1::2]]) - return chr(z+((10-(iSum%10))%10)) - -class isEan8String(Validator): - def test(self,x): - return type(x) is str and len(x)<=7 and len([c for c in x if c in "0123456789"])==7 -isEan8String = isEan8String() - -class Ean8BarcodeWidget(Ean13BarcodeWidget): - codeName = "EAN8" - _attrMap = AttrMap(BASE=Ean13BarcodeWidget, - value = AttrMapValue(isEan8String, desc='the number'), - ) - _start_right = 4 #for ean-13 left = [0:7] right=[7:13] - _nbars = 85 - _digits=7 - _0csw = 3 - _1csw = 1 - - def _encode_left(self,s,a): - cp = self._lhconvert[s[0]] #convert the left hand numbers - _left = self._left[0] - z = ord('0') - for i,c in enumerate(s[0:self._start_right]): - a(_left[ord(c)-z]) - - def _short_bar(self,i): - i += 9 - self._lquiet - return self.humanReadable and ((12 -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -# . 3 T Tracker -# , 2 D Descender -# ' 1 A Ascender -# | 0 H Ascender/Descender - -_rm_patterns = { - "0" : "--||", "1" : "-',|", "2" : "-'|,", "3" : "'-,|", - "4" : "'-|,", "5" : "'',,", "6" : "-,'|", "7" : "-|-|", - "8" : "-|',", "9" : "',-|", "A" : "',',", "B" : "'|-,", - "C" : "-,|'", "D" : "-|,'", "E" : "-||-", "F" : "',,'", - "G" : "',|-", "H" : "'|,-", "I" : ",-'|", "J" : ",'-|", - "K" : ",'',", "L" : "|--|", "M" : "|-',", "N" : "|'-,", - "O" : ",-|'", "P" : ",','", "Q" : ",'|-", "R" : "|-,'", - "S" : "|-|-", "T" : "|',-", "U" : ",,''", "V" : ",|-'", - "W" : ",|'-", "X" : "|,-'", "Y" : "|,'-", "Z" : "||--", - - # start, stop - "(" : "'-,'", ")" : "'|,|" -} - -_ozN_patterns = { - "0" : "||", "1" : "|'", "2" : "|,", "3" : "'|", "4" : "''", - "5" : "',", "6" : ",|", "7" : ",'", "8" : ",,", "9" : ".|" -} - -_ozC_patterns = { - "A" : "|||", "B" : "||'", "C" : "||,", "D" : "|'|", - "E" : "|''", "F" : "|',", "G" : "|,|", "H" : "|,'", - "I" : "|,,", "J" : "'||", "K" : "'|'", "L" : "'|,", - "M" : "''|", "N" : "'''", "O" : "'',", "P" : "',|", - "Q" : "','", "R" : "',,", "S" : ",||", "T" : ",|'", - "U" : ",|,", "V" : ",'|", "W" : ",''", "X" : ",',", - "Y" : ",,|", "Z" : ",,'", "a" : "|,.", "b" : "|.|", - "c" : "|.'", "d" : "|.,", "e" : "|..", "f" : "'|.", - "g" : "''.", "h" : "',.", "i" : "'.|", "j" : "'.'", - "k" : "'.,", "l" : "'..", "m" : ",|.", "n" : ",'.", - "o" : ",,.", "p" : ",.|", "q" : ",.'", "r" : ",.,", - "s" : ",..", "t" : ".|.", "u" : ".'.", "v" : ".,.", - "w" : "..|", "x" : "..'", "y" : "..,", "z" : "...", - "0" : ",,,", "1" : ".||", "2" : ".|'", "3" : ".|,", - "4" : ".'|", "5" : ".''", "6" : ".',", "7" : ".,|", - "8" : ".,'", "9" : ".,,", " " : "||.", "#" : "|'.", -} - -#http://www.auspost.com.au/futurepost/ diff --git a/bin/reportlab/graphics/barcode/test.py b/bin/reportlab/graphics/barcode/test.py deleted file mode 100644 index 3342947672e..00000000000 --- a/bin/reportlab/graphics/barcode/test.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/pkg/bin/python - -import os, sys, time - -from reportlab.graphics.barcode.common import * -from reportlab.graphics.barcode.code39 import * -from reportlab.graphics.barcode.code93 import * -from reportlab.graphics.barcode.code128 import * -from reportlab.graphics.barcode.usps import * - - -from reportlab.test import unittest -from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation -from reportlab.platypus import Spacer, SimpleDocTemplate, Table, TableStyle, Preformatted, PageBreak -from reportlab.lib.units import inch, cm -from reportlab.lib import colors - -from reportlab.pdfgen.canvas import Canvas -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.platypus.paragraph import Paragraph -from reportlab.platypus.frames import Frame -from reportlab.platypus.flowables import XBox, KeepTogether -from reportlab.graphics.shapes import Drawing - -from reportlab.graphics.barcode import getCodes, getCodeNames, createBarcodeDrawing -def run(): - styles = getSampleStyleSheet() - styleN = styles['Normal'] - styleH = styles['Heading1'] - story = [] - - #for codeNames in code - story.append(Paragraph('I2of5', styleN)) - story.append(I2of5(1234, barWidth = inch*0.02, checksum=0)) - story.append(Paragraph('MSI', styleN)) - story.append(MSI(1234)) - story.append(Paragraph('Codabar', styleN)) - story.append(Codabar("A012345B", barWidth = inch*0.02)) - story.append(Paragraph('Code 11', styleN)) - story.append(Code11("01234545634563")) - story.append(Paragraph('Code 39', styleN)) - story.append(Standard39("A012345B%R")) - story.append(Paragraph('Extended Code 39', styleN)) - story.append(Extended39("A012345B}")) - story.append(Paragraph('Code93', styleN)) - story.append(Standard93("CODE 93")) - story.append(Paragraph('Extended Code93', styleN)) - story.append(Extended93("L@@K! Code 93 :-)")) #, barWidth=0.005 * inch)) - story.append(Paragraph('Code 128', styleN)) - c=Code128("AB-12345678") #, barWidth=0.005 * inch) - #print 'WIDTH =', (c.width / inch), 'barWidth =', (c.barWidth / inch) - #print 'LQ =', (c.lquiet / inch), 'RQ =', (c.rquiet / inch) - story.append(c) - story.append(Paragraph('USPS FIM', styleN)) - story.append(FIM("A")) - story.append(Paragraph('USPS POSTNET', styleN)) - story.append(POSTNET('78247-1043')) - - from reportlab.graphics.barcode import createBarcodeDrawing - story.append(Paragraph('EAN13', styleN)) - bcd = createBarcodeDrawing('EAN13', value='123456789012') - story.append(bcd) - story.append(Paragraph('EAN8', styleN)) - bcd = createBarcodeDrawing('EAN8', value='1234567') - story.append(bcd) - - story.append(Paragraph('Label Size', styleN)) - story.append(XBox((2.0 + 5.0/8.0)*inch, 1 * inch, '1x2-5/8"')) - story.append(Paragraph('Label Size', styleN)) - story.append(XBox((1.75)*inch, .5 * inch, '1/2x1-3/4"')) - c = Canvas('out.pdf') - f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1) - f.addFromList(story, c) - c.save() - print 'saved out.pdf' - -def fullTest(fileName="test_full.pdf"): - """Creates large-ish test document with a variety of parameters""" - - story = [] - - styles = getSampleStyleSheet() - styleN = styles['Normal'] - styleH = styles['Heading1'] - styleH2 = styles['Heading2'] - story = [] - - story.append(Paragraph('ReportLab Barcode Test Suite - full output', styleH)) - story.append(Paragraph('Generated on %s' % time.ctime(time.time()), styleN)) - - story.append(Paragraph('', styleN)) - story.append(Paragraph('Repository information for this build:', styleN)) - #see if we can figure out where it was built, if we're running in source - if os.path.split(os.getcwd())[-1] == 'barcode' and os.path.isdir('.svn'): - #runnning in a filesystem svn copy - infoLines = os.popen('svn info').read() - story.append(Preformatted(infoLines, styles["Code"])) - - story.append(Paragraph('About this document', styleH2)) - story.append(Paragraph('History and Status', styleH2)) - - story.append(Paragraph(""" - This is the test suite and docoumentation for the ReportLab open source barcode API, - being re-released as part of the forthcoming ReportLab 2.0 release. - """, styleN)) - - story.append(Paragraph(""" - Several years ago Ty Sarna contributed a barcode module to the ReportLab community. - Several of the codes were used by him in hiw work and to the best of our knowledge - this was correct. These were written as flowable objects and were available in PDFs, - but not in our graphics framework. However, we had no knowledge of barcodes ourselves - and did not advertise or extend the package. - """, styleN)) - - story.append(Paragraph(""" - We "wrapped" the barcodes to be usable within our graphics framework; they are now available - as Drawing objects which can be rendered to EPS files or bitmaps. For the last 2 years this - has been available in our Diagra and Report Markup Language products. However, we did not - charge separately and use was on an "as is" basis. - """, styleN)) - - story.append(Paragraph(""" - A major licensee of our technology has kindly agreed to part-fund proper productisation - of this code on an open source basis in Q1 2006. This has involved addition of EAN codes - as well as a proper testing program. Henceforth we intend to publicise the code more widely, - gather feedback, accept contributions of code and treat it as "supported". - """, styleN)) - - story.append(Paragraph(""" - This involved making available both downloads and testing resources. This PDF document - is the output of the current test suite. It contains codes you can scan (if you use a nice sharp - laser printer!), and will be extended over coming weeks to include usage examples and notes on - each barcode and how widely tested they are. This is being done through documentation strings in - the barcode objects themselves so should always be up to date. - """, styleN)) - - story.append(Paragraph('Usage examples', styleH2)) - story.append(Paragraph(""" - To be completed - """, styleN)) - - story.append(Paragraph('The codes', styleH2)) - story.append(Paragraph(""" - Below we show a scannable code from each barcode, with and without human-readable text. - These are magnified about 2x from the natural size done by the original author to aid - inspection. This will be expanded to include several test cases per code, and to add - explanations of checksums. Be aware that (a) if you enter numeric codes which are too - short they may be prefixed for you (e.g. "123" for an 8-digit code becomes "00000123"), - and that the scanned results and readable text will generally include extra checksums - at the end. - """, styleN)) - - codeNames = getCodeNames() - from reportlab.lib.utils import flatten - width = [float(x[8:]) for x in sys.argv if x.startswith('--width=')] - height = [float(x[9:]) for x in sys.argv if x.startswith('--height=')] - isoScale = [int(x[11:]) for x in sys.argv if x.startswith('--isoscale=')] - options = {} - if width: options['width'] = width[0] - 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]) - for scale in scales: - story.append(PageBreak()) - story.append(Paragraph('Scale = %.1f'%scale, styleH2)) - story.append(Spacer(36, 12)) - for codeName in codeNames: - s = [Paragraph('Code: ' + codeName, styleH2)] - for hr in (0,1): - s.append(Spacer(36, 12)) - dr = createBarcodeDrawing(codeName, humanReadable=hr,**options) - dr.renderScale = scale - s.append(dr) - s.append(Spacer(36, 12)) - s.append(Paragraph('Barcode should say: ' + dr._bc.value, styleN)) - story.append(KeepTogether(s)) - - SimpleDocTemplate(fileName).build(story) - print 'created', fileName - -if __name__=='__main__': - run() - fullTest() diff --git a/bin/reportlab/graphics/barcode/usps.py b/bin/reportlab/graphics/barcode/usps.py deleted file mode 100644 index 999dd9ca340..00000000000 --- a/bin/reportlab/graphics/barcode/usps.py +++ /dev/null @@ -1,228 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -_fim_patterns = { - 'A' : "|| | ||", - 'B' : "| || || |", - 'C' : "|| | | ||", - 'D' : "||| | |||", - # XXX There is an E. - # The below has been seen, but dunno if it is E or not: - # 'E' : '|||| ||||' -} - -_postnet_patterns = { - '1' : "...||", '2' : "..|.|", '3' : "..||.", '4' : ".|..|", - '5' : ".|.|.", '6' : ".||..", '7' : "|...|", '8' : "|..|.", - '9' : "|.|..", '0' : "||...", 'S' : "|", -} - -class FIM(Barcode): - """" - FIM (Facing ID Marks) encode only one letter. - There are currently four defined: - - A Courtesy reply mail with pre-printed POSTNET - B Business reply mail without pre-printed POSTNET - C Business reply mail with pre-printed POSTNET - D OCR Readable mail without pre-printed POSTNET - - Options that may be passed to constructor: - - value (single character string from the set A - D. required.): - The value to encode. - - quiet (bool, default 0): - Whether to include quiet zones in the symbol. - - The following may also be passed, but doing so will generate nonstandard - symbols which should not be used. This is mainly documented here to - show the defaults: - - barHeight (float, default 5/8 inch): - Height of the code. This might legitimately be overriden to make - a taller symbol that will 'bleed' off the edge of the paper, - leaving 5/8 inch remaining. - - lquiet (float, default 1/4 inch): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, default 15/32 inch): - Quiet zone size to right left of code, if quiet is true. - - Sources of information on FIM: - - USPS Publication 25, A Guide to Business Mail Preparation - http://new.usps.com/cpim/ftp/pubs/pub25.pdf - """ - barWidth = inch * (1.0/32.0) - spaceWidth = inch * (1.0/16.0) - barHeight = inch * (5.0/8.0) - rquiet = inch * (0.25) - lquiet = inch * (15.0/32.0) - quiet = 0 - def __init__(self, value='', **args): - for (k, v) in args.items(): - setattr(self, k, v) - - Barcode.__init__(self, value) - - def validate(self): - self.valid = 1 - self.validated = '' - for c in self.value: - if c in string.whitespace: - continue - elif c in "abcdABCD": - self.validated = self.validated + string.upper(c) - else: - self.valid = 0 - - if len(self.validated) != 1: - raise ValueError, "Input must be exactly one character" - - return self.validated - - def decompose(self): - self.decomposed = '' - for c in self.encoded: - self.decomposed = self.decomposed + _fim_patterns[c] - - return self.decomposed - - def computeSize(self): - self.width = (len(self.decomposed) - 1) * self.spaceWidth + self.barWidth - if self.quiet: - self.width += self.lquiet + self.rquiet - self.height = self.barHeight - - def draw(self): - left = self.quiet and self.lquiet or 0 - for c in self.decomposed: - if c == '|': - self.rect(left, 0.0, self.barWidth, self.barHeight) - left += self.spaceWidth - self.drawHumanReadable() - - def _humanText(self): - return self.value - -class POSTNET(Barcode): - """" - POSTNET is used in the US to encode "zip codes" (postal codes) on - mail. It can encode 5, 9, or 11 digit codes. I've read that it's - pointless to do 5 digits, since USPS will just have to re-print - them with 9 or 11 digits. - - Sources of information on POSTNET: - - USPS Publication 25, A Guide to Business Mail Preparation - http://new.usps.com/cpim/ftp/pubs/pub25.pdf - """ - quiet = 0 - shortHeight = inch * 0.050 - barHeight = inch * 0.125 - barWidth = inch * 0.018 - spaceWidth = inch * 0.0275 - def __init__(self, value='', **args): - - for (k, v) in args.items(): - setattr(self, k, v) - - Barcode.__init__(self, value) - - def validate(self): - self.validated = '' - self.valid = 1 - count = 0 - for c in self.value: - if c in (string.whitespace + '-'): - pass - elif c in string.digits: - count = count + 1 - if count == 6: - self.validated = self.validated + '-' - self.validated = self.validated + c - else: - self.valid = 0 - - if len(self.validated) not in [5, 10, 12]: - self.valid = 0 - - return self.validated - - def encode(self): - self.encoded = "S" - check = 0 - for c in self.validated: - if c in string.digits: - self.encoded = self.encoded + c - check = check + string.atoi(c) - elif c == '-': - pass - else: - raise ValueError, "Invalid character in input" - check = (10 - (check % 10)) % 10 - self.encoded = self.encoded + `check` + 'S' - return self.encoded - - def decompose(self): - self.decomposed = '' - for c in self.encoded: - self.decomposed = self.decomposed + _postnet_patterns[c] - return self.decomposed - - def computeSize(self): - self.width = len(self.decomposed) * self.barWidth + (len(self.decomposed) - 1) * self.spaceWidth - self.height = self.barHeight - - def draw(self): - sdown = self.barHeight - self.shortHeight - left = 0 - - for c in self.decomposed: - if c == '.': - h = self.shortHeight - else: - h = self.barHeight - self.rect(left, 0.0, self.barWidth, h) - left = left + self.barWidth + self.spaceWidth - self.drawHumanReadable() - - def _humanText(self): - return self.encoded[1:-1] diff --git a/bin/reportlab/graphics/barcode/widgets.py b/bin/reportlab/graphics/barcode/widgets.py deleted file mode 100644 index b350d513157..00000000000 --- a/bin/reportlab/graphics/barcode/widgets.py +++ /dev/null @@ -1,304 +0,0 @@ -#copyright ReportLab Europe Limited. 2000-2006 -#see license.txt for license details -__version__=''' $Id: widgets.py 2851 2006-05-08 14:34:45Z rgbecker $ ''' -__all__= ( - 'BarcodeI2of5', - 'BarcodeCode128', - 'BarcodeStandard93', - 'BarcodeExtended93', - 'BarcodeStandard39', - 'BarcodeExtended39', - 'BarcodeMSI', - 'BarcodeCodabar', - 'BarcodeCode11', - 'BarcodeFIM', - 'BarcodePOSTNET', - ) - -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.graphics.shapes import Line, Rect, Group, NotImplementedError, String -from reportlab.graphics.charts.areas import PlotArea - -''' -#snippet - -#first make your Drawing -from reportlab.graphics.shapes import Drawing -d= Drawing(100,50) - -#create and set up the widget -from reportlab.graphics.barcode.widgets import BarcodeStandard93 -bc = BarcodeStandard93() -bc.value = 'RGB-123456' - -#add to the drawing and save -d.add(bc) -# d.save(formats=['gif','pict'],fnRoot='bc_sample') -''' - -class _BarcodeWidget(PlotArea): - _attrMap = AttrMap(BASE=PlotArea, - barStrokeColor = AttrMapValue(isColorOrNone, desc='Color of bar borders.'), - barFillColor = AttrMapValue(isColorOrNone, desc='Color of bar interior areas.'), - barStrokeWidth = AttrMapValue(isNumber, desc='Width of bar borders.'), - value = AttrMapValue(EitherOr((isString,isNumber)), desc='Value.'), - textColor = AttrMapValue(isColorOrNone, desc='Color of human readable text.'), - valid = AttrMapValue(isBoolean), - validated = AttrMapValue(isString,desc="validated form of input"), - encoded = AttrMapValue(None,desc="encoded form of input"), - decomposed = AttrMapValue(isString,desc="decomposed form of input"), - canv = AttrMapValue(None,desc="temporarily used for internal methods"), - gap = AttrMapValue(isNumberOrNone, desc='Width of inter character gaps.'), - ) - - barStrokeColor = barFillColor = textColor = black - 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 - PlotArea.__init__(self) - self.x = self.y = 0 - kw.setdefault('value',_value) - BCC.__init__(self,**kw) - - def rect(self,x,y,w,h,**kw): - self._Gadd(Rect(self.x+x,self.y+y,w,h, - strokeColor=self.barStrokeColor,strokeWidth=self.barStrokeWidth, fillColor=self.barFillColor)) - - def draw(self): - if not self._BCC: raise NotImplementedError("Abstract class %s cannot be drawn" % self.__class__.__name__) - self.canv = self - G = Group() - self._Gadd = G.add - self._Gadd(Rect(self.x,self.y,self.width,self.height,fillColor=None,strokeColor=None,strokeWidth=0.0001)) - self._BCC.draw(self) - del self.canv, self._Gadd - return G - - def annotate(self,x,y,text,fontName,fontSize,anchor='middle'): - self._Gadd(String(self.x+x,self.y+y,text,fontName=fontName,fontSize=fontSize, - textAnchor=anchor,fillColor=self.textColor)) - -class BarcodeI2of5(_BarcodeWidget): - """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, - barWidth = AttrMapValue(isNumber,'''(float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils).'''), - ratio = AttrMapValue(isNumber,'''(float, default 2.2): - The ratio of wide elements to narrow elements. - Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the - barWidth is greater than 20 mils (.02 inch))'''), - gap = AttrMapValue(isNumberOrNone,'''(float or None, default None): - width of intercharacter gap. None means "use barWidth".'''), - barHeight = AttrMapValue(isNumber,'''(float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length.'''), - checksum = AttrMapValue(isBoolean,'''(bool, default 1): - Whether to compute and include the check digit'''), - bearers = AttrMapValue(isNumber,'''(float, in units of barWidth. default 3.0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 3 x-dimensions. - Set to zero for no bearer bars. (Bearer bars help detect - misscans, so it is suggested to leave them on).'''), - quiet = AttrMapValue(isBoolean,'''(bool, default 1): - Whether to include quiet zones in the symbol.'''), - - lquiet = AttrMapValue(isNumber,'''(float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length.'''), - - rquiet = AttrMapValue(isNumber,'''(float, defaults as above): - 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'), - 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. - """ - _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) - -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) - -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) - -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) - -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) - -class BarcodeMSI(BarcodeI2of5): - """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) - -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) - -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) - -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. - - 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) - -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) - -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') - map(os.remove,glob.glob(os.path.join('out','*'))) - html = [''] - a = html.append - for C in (BarcodeI2of5, - BarcodeCode128, - BarcodeStandard93, - BarcodeExtended93, - BarcodeStandard39, - BarcodeExtended39, - BarcodeMSI, - BarcodeCodabar, - BarcodeCode11, - BarcodeFIM, - BarcodePOSTNET, - ): - name = C.__name__ - i = C() - D = Drawing(100,50) - D.add(i) - D.save(formats=['gif','pict'],outDir='out',fnRoot=name) - a('

%s


' % (name, name)) - a('') - open(os.path.join('out','index.html'),'w').write('\n'.join(html)) diff --git a/bin/reportlab/graphics/charts/__init__.py b/bin/reportlab/graphics/charts/__init__.py deleted file mode 100644 index 6b2a42748cc..00000000000 --- a/bin/reportlab/graphics/charts/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/__init__.py -__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' \ No newline at end of file diff --git a/bin/reportlab/graphics/charts/areas.py b/bin/reportlab/graphics/charts/areas.py deleted file mode 100644 index 7588d924009..00000000000 --- a/bin/reportlab/graphics/charts/areas.py +++ /dev/null @@ -1,92 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/areas.py -"""This module defines a Area mixin classes -""" -__version__=''' $Id: areas.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isNoneOrShape -from reportlab.graphics.widgetbase import Widget -from reportlab.graphics.shapes import Rect, Group, Line, Polygon -from reportlab.lib.attrmap import AttrMap, AttrMapValue - -class PlotArea(Widget): - "Abstract base class representing a chart's plot area, pretty unusable by itself." - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc='X position of the lower-left corner of the chart.'), - y = AttrMapValue(isNumber, desc='Y position of the lower-left corner of the chart.'), - width = AttrMapValue(isNumber, desc='Width of the chart.'), - height = AttrMapValue(isNumber, desc='Height of the chart.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color of the plot area border.'), - strokeWidth = AttrMapValue(isNumber, desc='Width plot area border.'), - fillColor = AttrMapValue(isColorOrNone, desc='Color of the plot area interior.'), - background = AttrMapValue(isNoneOrShape, desc='Handle to background object.'), - debug = AttrMapValue(isNumber, desc='Used only for debugging.'), - ) - - def __init__(self): - self.x = 20 - self.y = 10 - self.height = 85 - self.width = 180 - self.strokeColor = None - self.strokeWidth = 1 - self.fillColor = None - self.background = None - self.debug = 0 - - def makeBackground(self): - if self.background is not None: - BG = self.background - if isinstance(BG,Group): - g = BG - for bg in g.contents: - bg.x = self.x - bg.y = self.y - bg.width = self.width - bg.height = self.height - else: - g = Group() - if type(BG) not in (type(()),type([])): BG=(BG,) - for bg in BG: - bg.x = self.x - bg.y = self.y - bg.width = self.width - bg.height = self.height - g.add(bg) - return g - else: - strokeColor,strokeWidth,fillColor=self.strokeColor, self.strokeWidth, self.fillColor - if (strokeWidth and strokeColor) or fillColor: - g = Group() - _3d_dy = getattr(self,'_3d_dy',None) - x = self.x - y = self.y - h = self.height - w = self.width - if _3d_dy is not None: - _3d_dx = self._3d_dx - if fillColor and not strokeColor: - from reportlab.lib.colors import Blacker - c = Blacker(fillColor, getattr(self,'_3d_blacken',0.7)) - else: - c = strokeColor - if not strokeWidth: strokeWidth = 0.5 - if fillColor or strokeColor or c: - bg = Polygon([x,y,x,y+h,x+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y], - strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fillColor) - g.add(bg) - g.add(Line(x,y,x+_3d_dx,y+_3d_dy, strokeWidth=0.5, strokeColor=c)) - g.add(Line(x+_3d_dx,y+_3d_dy, x+_3d_dx,y+h+_3d_dy,strokeWidth=0.5, strokeColor=c)) - fc = Blacker(c, getattr(self,'_3d_blacken',0.8)) - g.add(Polygon([x,y,x+_3d_dx,y+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y], - strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fc)) - bg = Line(x+_3d_dx,y+_3d_dy, x+w+_3d_dx,y+_3d_dy,strokeWidth=0.5, strokeColor=c) - else: - bg = None - else: - bg = Rect(x, y, w, h, - strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor) - if bg: g.add(bg) - return g - else: - return None diff --git a/bin/reportlab/graphics/charts/axes.py b/bin/reportlab/graphics/charts/axes.py deleted file mode 100644 index ab0be2ad060..00000000000 --- a/bin/reportlab/graphics/charts/axes.py +++ /dev/null @@ -1,1979 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/axes.py -"""Collection of axes for charts. - -The current collection comprises axes for charts using cartesian -coordinate systems. All axes might have tick marks and labels. -There are two dichotomies for axes: one of X and Y flavours and -another of category and value flavours. - -Category axes have an ordering but no metric. They are divided -into a number of equal-sized buckets. Their tick marks or labels, -if available, go BETWEEN the buckets, and the labels are placed -below to/left of the X/Y-axis, respectively. - - Value axes have an ordering AND metric. They correspond to a nu- - meric quantity. Value axis have a real number quantity associated - with it. The chart tells it where to go. - The most basic axis divides the number line into equal spaces - and has tickmarks and labels associated with each; later we - will add variants where you can specify the sampling - interval. - -The charts using axis tell them where the labels should be placed. - -Axes of complementary X/Y flavours can be connected to each other -in various ways, i.e. with a specific reference point, like an -x/value axis to a y/value (or category) axis. In this case the -connection can be either at the top or bottom of the former or -at any absolute value (specified in points) or at some value of -the former axes in its own coordinate system. -""" -__version__=''' $Id: axes.py 2838 2006-04-18 17:47:54Z rgbecker $ ''' - -import string -from types import FunctionType, StringType, TupleType, ListType - -from reportlab.lib.validators import isNumber, isNumberOrNone, isListOfStringsOrNone, isListOfNumbers, \ - isListOfNumbersOrNone, isColorOrNone, OneOf, isBoolean, SequenceOf, \ - isString, EitherOr -from reportlab.lib.attrmap import * -from reportlab.lib import normalDate -from reportlab.graphics.shapes import Drawing, Line, PolyLine, Group, STATE_DEFAULTS, _textBoxLimits, _rotatedBoxLimits -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.charts.utils import nextRoundNumber - - -# Helpers. - -def _findMinMaxValue(V, x, default, func, special=None): - if type(V[0][0]) in (TupleType,ListType): - 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)) - if len(V)==0: return default - return func(map(func,V)) - -def _findMin(V, x, default,special=None): - '''find minimum over V[i][x]''' - return _findMinMaxValue(V,x,default,min,special=special) - -def _findMax(V, x, default,special=None): - '''find maximum over V[i][x]''' - return _findMinMaxValue(V,x,default,max,special=special) - -def _allInt(values): - '''true if all values are int''' - for v in values: - try: - if int(v)!=v: return 0 - except: - return 0 - return 1 - -class _AxisG(Widget): - def _get_line_pos(self,v): - v = self.scale(v) - try: - v = v[0] - except: - pass - return v - - def _cxLine(self,x,start,end): - x = self._get_line_pos(x) - return Line(x, self._y + start, x, self._y + end) - - def _cyLine(self,y,start,end): - y = self._get_line_pos(y) - return Line(self._x + start, y, self._x + end, y) - - def _cxLine3d(self,x,start,end,_3d_dx,_3d_dy): - x = self._get_line_pos(x) - y0 = self._y + start - y1 = self._y + end - y0, y1 = min(y0,y1),max(y0,y1) - x1 = x + _3d_dx - return PolyLine([x,y0,x1,y0+_3d_dy,x1,y1+_3d_dy],strokeLineJoin=1) - - def _cyLine3d(self,y,start,end,_3d_dx,_3d_dy): - y = self._get_line_pos(y) - x0 = self._x + start - x1 = self._x + end - x0, x1 = min(x0,x1),max(x0,x1) - y1 = y + _3d_dy - return PolyLine([x0,y,x0+_3d_dx,y1,x1+_3d_dx,y1],strokeLineJoin=1) - - def _getLineFunc(self, start, end, parent=None): - _3d_dx = getattr(parent,'_3d_dx',None) - if _3d_dx is not None: - _3d_dy = getattr(parent,'_3d_dy',None) - f = self._dataIndex and self._cyLine3d or self._cxLine3d - return lambda v, s=start, e=end, f=f,_3d_dx=_3d_dx,_3d_dy=_3d_dy: f(v,s,e,_3d_dx=_3d_dx,_3d_dy=_3d_dy) - else: - f = self._dataIndex and self._cyLine or self._cxLine - return lambda v, s=start, e=end, f=f: f(v,s,e) - - def _makeLines(self,g,start,end,strokeColor,strokeWidth,strokeDashArray,parent=None): - func = self._getLineFunc(start,end,parent) - for t in self._tickValues: - L = func(t) - L.strokeColor = strokeColor - L.strokeWidth = strokeWidth - L.strokeDashArray = strokeDashArray - g.add(L) - - def makeGrid(self,g,dim=None,parent=None): - '''this is only called by a container object''' - s = self.gridStart - e = self.gridEnd - if dim: - s = s is None and dim[0] - e = e is None and dim[0]+dim[1] - c = self.gridStrokeColor - if self.visibleGrid and (s or e) and c is not None: - if self._dataIndex: offs = self._x - else: offs = self._y - self._makeLines(g,s-offs,e-offs,c,self.gridStrokeWidth,self.gridStrokeDashArray,parent=parent) - -# Category axes. -class CategoryAxis(_AxisG): - "Abstract category axis, unusable in itself." - _nodoc = 1 - _attrMap = AttrMap( - visible = AttrMapValue(isBoolean, desc='Display entire object, if true.'), - visibleAxis = AttrMapValue(isBoolean, desc='Display axis line, if true.'), - visibleTicks = AttrMapValue(isBoolean, desc='Display axis ticks, if true.'), - visibleLabels = AttrMapValue(isBoolean, desc='Display axis labels, if true.'), - visibleGrid = AttrMapValue(isBoolean, desc='Display axis grid, if true.'), - strokeWidth = AttrMapValue(isNumber, desc='Width of axis line and ticks.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color of axis line and ticks.'), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array used for axis line.'), - gridStrokeWidth = AttrMapValue(isNumber, desc='Width of grid lines.'), - gridStrokeColor = AttrMapValue(isColorOrNone, desc='Color of grid lines.'), - gridStrokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array used for grid lines.'), - gridStart = AttrMapValue(isNumberOrNone, desc='Start of grid lines wrt axis origin'), - gridEnd = AttrMapValue(isNumberOrNone, desc='End of grid lines wrt axis origin'), - labels = AttrMapValue(None, desc='Handle of the axis labels.'), - categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'), - joinAxis = AttrMapValue(None, desc='Join both axes if true.'), - joinAxisPos = AttrMapValue(isNumberOrNone, desc='Position at which to join with other axis.'), - reverseDirection = AttrMapValue(isBoolean, desc='If true reverse category direction.'), - style = AttrMapValue(OneOf('parallel','stacked','parallel_3d'),"How common category bars are plotted"), - labelAxisMode = AttrMapValue(OneOf('high','low','axis'), desc="Like joinAxisMode, but for the axis labels"), - tickShift = AttrMapValue(isBoolean, desc='Tick shift typically'), - ) - - def __init__(self): - assert self.__class__.__name__!='CategoryAxis', "Abstract Class CategoryAxis Instantiated" - # private properties set by methods. The initial values - # here are to make demos easy; they would always be - # overridden in real life. - self._x = 50 - self._y = 50 - self._length = 100 - self._catCount = 0 - - # public properties - self.visible = 1 - self.visibleAxis = 1 - self.visibleTicks = 1 - self.visibleLabels = 1 - self.visibleGrid = 0 - - self.strokeWidth = 1 - self.strokeColor = STATE_DEFAULTS['strokeColor'] - self.strokeDashArray = STATE_DEFAULTS['strokeDashArray'] - self.gridStrokeWidth = 0.25 - self.gridStrokeColor = STATE_DEFAULTS['strokeColor'] - self.gridStrokeDashArray = STATE_DEFAULTS['strokeDashArray'] - self.gridStart = self.gridEnd = 0 - self.labels = TypedPropertyCollection(Label) - # if None, they don't get labels. If provided, - # you need one name per data point and they are - # used for label text. - self.categoryNames = None - self.joinAxis = None - self.joinAxisPos = None - self.joinAxisMode = None - self.labelAxisMode = 'axis' - self.reverseDirection = 0 - self.style = 'parallel' - - #various private things which need to be initialized - self._labelTextFormat = None - self.tickShift = 0 - - def setPosition(self, x, y, length): - # ensure floating point - self._x = x - self._y = y - self._length = length - - - def configure(self, multiSeries,barWidth=None): - self._catCount = max(map(len,multiSeries)) - self._barWidth = barWidth or (self._length/float(self._catCount or 1)) - self._calcTickmarkPositions() - - def _calcTickmarkPositions(self): - n = self._catCount - if self.tickShift: - self._tickValues = [t+0.5 for t in xrange(n)] - else: - self._tickValues = range(n+1) - - def draw(self): - g = Group() - - if not self.visible: - return g - - g.add(self.makeAxis()) - g.add(self.makeTicks()) - g.add(self.makeTickLabels()) - - return g - - def _scale(self,idx): - if self.reverseDirection: idx = self._catCount-idx-1 - return idx - -def _assertYAxis(axis): - acn = axis.__class__.__name__ - assert acn[0]=='Y' or acn[:4]=='AdjY', "Cannot connect to other axes (%s), but Y- ones." % acn -def _assertXAxis(axis): - acn = axis.__class__.__name__ - assert acn[0]=='X' or acn[:11]=='NormalDateX', "Cannot connect to other axes (%s), but X- ones." % acn - -class XCategoryAxis(CategoryAxis): - "X/category axis" - - _attrMap = AttrMap(BASE=CategoryAxis, - tickUp = AttrMapValue(isNumber, - desc='Tick length up the axis.'), - tickDown = AttrMapValue(isNumber, - desc='Tick length down the axis.'), - joinAxisMode = AttrMapValue(OneOf('bottom', 'top', 'value', 'points', None), - desc="Mode used for connecting axis ('bottom', 'top', 'value', 'points', None)."), - ) - - _dataIndex = 0 - - def __init__(self): - CategoryAxis.__init__(self) - self.labels.boxAnchor = 'n' #north - top edge - self.labels.dy = -5 - # ultra-simple tick marks for now go between categories - # and have same line style as axis - need more - self.tickUp = 0 # how far into chart does tick go? - self.tickDown = 5 # how far below axis does tick go? - - - def demo(self): - self.setPosition(30, 70, 140) - self.configure([(10,20,30,40,50)]) - - self.categoryNames = ['One','Two','Three','Four','Five'] - # all labels top-centre aligned apart from the last - self.labels.boxAnchor = 'n' - self.labels[4].boxAnchor = 'e' - self.labels[4].angle = 90 - - d = Drawing(200, 100) - d.add(self) - return d - - - def joinToAxis(self, yAxis, mode='bottom', pos=None): - "Join with y-axis using some mode." - - _assertYAxis(yAxis) - if mode == 'bottom': - self._x = yAxis._x - self._y = yAxis._y - elif mode == 'top': - self._x = yAxis._x - self._y = yAxis._y + yAxis._length - elif mode == 'value': - self._x = yAxis._x - self._y = yAxis.scale(pos) - elif mode == 'points': - self._x = yAxis._x - self._y = pos - - def _joinToAxis(self): - ja = self.joinAxis - if ja: - jam = self.joinAxisMode - jap = self.joinAxisPos - jta = self.joinToAxis - if jam in ('bottom', 'top'): - jta(ja, mode=jam) - elif jam in ('value', 'points'): - jta(ja, mode=jam, pos=jap) - - def scale(self, idx): - """returns the x position and width in drawing units of the slice""" - return (self._x + self._scale(idx)*self._barWidth, self._barWidth) - - def makeAxis(self): - g = Group() - self._joinToAxis() - if not self.visibleAxis: return g - - axis = Line(self._x, self._y, self._x + self._length, self._y) - axis.strokeColor = self.strokeColor - axis.strokeWidth = self.strokeWidth - axis.strokeDashArray = self.strokeDashArray - g.add(axis) - - return g - - def makeTicks(self): - g = Group() - if not self.visibleTicks: return g - if self.tickUp or self.tickDown: - self._makeLines(g,self.tickUp,-self.tickDown,self.strokeColor,self.strokeWidth,self.strokeDashArray) - return g - - def _labelAxisPos(self): - axis = self.joinAxis - if axis: - mode = self.labelAxisMode - if mode == 'low': - return axis._y - elif mode == 'high': - return axis._y + axis._length - return self._y - - def makeTickLabels(self): - g = Group() - - if not self.visibleLabels: return g - - categoryNames = self.categoryNames - if categoryNames is not None: - catCount = self._catCount - n = len(categoryNames) - reverseDirection = self.reverseDirection - barWidth = self._barWidth - _y = self._labelAxisPos() - _x = self._x - - for i in xrange(catCount): - if reverseDirection: ic = catCount-i-1 - else: ic = i - if ic>=n: continue - x = _x + (i+0.5) * barWidth - label = self.labels[i] - label.setOrigin(x, _y) - label.setText(categoryNames[ic] or '') - g.add(label) - - return g - - -class YCategoryAxis(CategoryAxis): - "Y/category axis" - - _attrMap = AttrMap(BASE=CategoryAxis, - tickLeft = AttrMapValue(isNumber, - desc='Tick length left of the axis.'), - tickRight = AttrMapValue(isNumber, - desc='Tick length right of the axis.'), - joinAxisMode = AttrMapValue(OneOf(('left', 'right', 'value', 'points', None)), - desc="Mode used for connecting axis ('left', 'right', 'value', 'points', None)."), - ) - - _dataIndex = 1 - - - def __init__(self): - CategoryAxis.__init__(self) - self.labels.boxAnchor = 'e' #east - right edge - self.labels.dx = -5 - # ultra-simple tick marks for now go between categories - # and have same line style as axis - need more - self.tickLeft = 5 # how far left of axis does tick go? - self.tickRight = 0 # how far right of axis does tick go? - - - def demo(self): - self.setPosition(50, 10, 80) - self.configure([(10,20,30)]) - self.categoryNames = ['One','Two','Three'] - # all labels top-centre aligned apart from the last - self.labels.boxAnchor = 'e' - self.labels[2].boxAnchor = 's' - self.labels[2].angle = 90 - - d = Drawing(200, 100) - d.add(self) - return d - - - def joinToAxis(self, xAxis, mode='left', pos=None): - "Join with x-axis using some mode." - - _assertXAxis(xAxis) - - if mode == 'left': - self._x = xAxis._x * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'right': - self._x = (xAxis._x + xAxis._length) * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'value': - self._x = xAxis.scale(pos) * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'points': - self._x = pos * 1.0 - self._y = xAxis._y * 1.0 - - def _joinToAxis(self): - ja = self.joinAxis - if ja: - jam = self.joinAxisMode - jap = self.joinAxisPos - jta = self.joinToAxis - if jam in ('left', 'right'): - jta(ja, mode=jam) - elif jam in ('value', 'points'): - jta(ja, mode=jam, pos=jap) - - def scale(self, idx): - "Returns the y position and width in drawing units of the slice." - return (self._y + self._scale(idx)*self._barWidth, self._barWidth) - - def makeAxis(self): - g = Group() - self._joinToAxis() - if not self.visibleAxis: return g - - axis = Line(self._x, self._y, self._x, self._y + self._length) - axis.strokeColor = self.strokeColor - axis.strokeWidth = self.strokeWidth - axis.strokeDashArray = self.strokeDashArray - g.add(axis) - - return g - - def makeTicks(self): - g = Group() - if not self.visibleTicks: return g - if self.tickLeft or self.tickRight: - self._makeLines(g,-self.tickLeft,self.tickRight,self.strokeColor,self.strokeWidth,self.strokeDashArray) - return g - - def _labelAxisPos(self): - axis = self.joinAxis - if axis: - mode = self.labelAxisMode - if mode == 'low': - return axis._x - elif mode == 'high': - return axis._x + axis._length - return self._x - - def makeTickLabels(self): - g = Group() - - if not self.visibleTicks: return g - - categoryNames = self.categoryNames - if categoryNames is not None: - catCount = self._catCount - n = len(categoryNames) - reverseDirection = self.reverseDirection - barWidth = self._barWidth - labels = self.labels - _x = self._labelAxisPos() - _y = self._y - for i in xrange(catCount): - if reverseDirection: ic = catCount-i-1 - else: ic = i - if ic>=n: continue - y = _y + (i+0.5) * barWidth - label = labels[i] - label.setOrigin(_x, y) - label.setText(categoryNames[ic] or '') - g.add(label) - return g - - -# Value axes. -class ValueAxis(_AxisG): - "Abstract value axis, unusable in itself." - - _attrMap = AttrMap( - forceZero = AttrMapValue(EitherOr((isBoolean,OneOf('near'))), desc='Ensure zero in range if true.'), - visible = AttrMapValue(isBoolean, desc='Display entire object, if true.'), - visibleAxis = AttrMapValue(isBoolean, desc='Display axis line, if true.'), - visibleLabels = AttrMapValue(isBoolean, desc='Display axis labels, if true.'), - visibleTicks = AttrMapValue(isBoolean, desc='Display axis ticks, if true.'), - visibleGrid = AttrMapValue(isBoolean, desc='Display axis grid, if true.'), - strokeWidth = AttrMapValue(isNumber, desc='Width of axis line and ticks.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color of axis line and ticks.'), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array used for axis line.'), - gridStrokeWidth = AttrMapValue(isNumber, desc='Width of grid lines.'), - gridStrokeColor = AttrMapValue(isColorOrNone, desc='Color of grid lines.'), - gridStrokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array used for grid lines.'), - gridStart = AttrMapValue(isNumberOrNone, desc='Start of grid lines wrt axis origin'), - gridEnd = AttrMapValue(isNumberOrNone, desc='End of grid lines wrt axis origin'), - minimumTickSpacing = AttrMapValue(isNumber, desc='Minimum value for distance between ticks.'), - maximumTicks = AttrMapValue(isNumber, desc='Maximum number of ticks.'), - labels = AttrMapValue(None, desc='Handle of the axis labels.'), - labelTextFormat = AttrMapValue(None, desc='Formatting string or function used for axis labels.'), - labelTextPostFormat = AttrMapValue(None, desc='Extra Formatting string.'), - labelTextScale = AttrMapValue(isNumberOrNone, desc='Scaling for label tick values.'), - valueMin = AttrMapValue(isNumberOrNone, desc='Minimum value on axis.'), - valueMax = AttrMapValue(isNumberOrNone, desc='Maximum value on axis.'), - valueStep = AttrMapValue(isNumberOrNone, desc='Step size used between ticks.'), - valueSteps = AttrMapValue(isListOfNumbersOrNone, desc='List of step sizes used between ticks.'), - avoidBoundFrac = AttrMapValue(EitherOr((isNumberOrNone,SequenceOf(isNumber,emptyOK=0,lo=2,hi=2))), desc='Fraction of interval to allow above and below.'), - rangeRound=AttrMapValue(OneOf('none','both','ceiling','floor'),'How to round the axis limits'), - zrangePref = AttrMapValue(isNumberOrNone, desc='Zero range axis limit preference.'), - style = AttrMapValue(OneOf('normal','stacked','parallel_3d'),"How values are plotted!"), - ) - - def __init__(self): - assert self.__class__.__name__!='ValueAxis', 'Abstract Class ValueAxis Instantiated' - self._configured = 0 - # private properties set by methods. The initial values - # here are to make demos easy; they would always be - # overridden in real life. - self._x = 50 - self._y = 50 - self._length = 100 - - # public properties - self.visible = 1 - self.visibleAxis = 1 - self.visibleLabels = 1 - self.visibleTicks = 1 - self.visibleGrid = 0 - self.forceZero = 0 - - self.strokeWidth = 1 - self.strokeColor = STATE_DEFAULTS['strokeColor'] - self.strokeDashArray = STATE_DEFAULTS['strokeDashArray'] - self.gridStrokeWidth = 0.25 - self.gridStrokeColor = STATE_DEFAULTS['strokeColor'] - self.gridStrokeDashArray = STATE_DEFAULTS['strokeDashArray'] - self.gridStart = self.gridEnd = 0 - - self.labels = TypedPropertyCollection(Label) - self.labels.angle = 0 - - # how close can the ticks be? - self.minimumTickSpacing = 10 - self.maximumTicks = 7 - - # a format string like '%0.2f' - # or a function which takes the value as an argument and returns a string - self._labelTextFormat = self.labelTextFormat = self.labelTextPostFormat = self.labelTextScale = None - - # if set to None, these will be worked out for you. - # if you override any or all of them, your values - # will be used. - self.valueMin = None - self.valueMax = None - self.valueStep = None - self.avoidBoundFrac = None - self.rangeRound = 'none' - self.zrangePref = 0 - self.style = 'normal' - - def setPosition(self, x, y, length): - # ensure floating point - self._x = x * 1.0 - self._y = y * 1.0 - self._length = length * 1.0 - - def configure(self, dataSeries): - """Let the axis configure its scale and range based on the data. - - Called after setPosition. Let it look at a list of lists of - numbers determine the tick mark intervals. If valueMin, - valueMax and valueStep are configured then it - will use them; if any of them are set to None it - will look at the data and make some sensible decision. - You may override this to build custom axes with - irregular intervals. It creates an internal - variable self._values, which is a list of numbers - to use in plotting. - """ - self._setRange(dataSeries) - self._calcTickmarkPositions() - self._calcScaleFactor() - self._configured = 1 - - def _getValueStepAndTicks(self, valueMin, valueMax,cache={}): - try: - K = (valueMin,valueMax) - r = cache[K] - except: - self._valueMin = valueMin - self._valueMax = valueMax - T = self._calcTickPositions() - if len(T)>1: - valueStep = T[1]-T[0] - else: - oVS = self.valueStep - self.valueStep = None - T = self._calcTickPositions() - self.valueStep = oVS - if len(T)>1: - valueStep = T[1]-T[0] - else: - valueStep = self._valueStep - r = cache[K] = valueStep, T, valueStep*1e-8 - return r - - def _setRange(self, dataSeries): - """Set minimum and maximum axis values. - - The dataSeries argument is assumed to be a list of data - vectors. Each vector is itself a list or tuple of numbers. - - Returns a min, max tuple. - """ - - oMin = valueMin = self.valueMin - oMax = valueMax = self.valueMax - rangeRound = self.rangeRound - if valueMin is None: valueMin = self._cValueMin = _findMin(dataSeries,self._dataIndex,0) - if valueMax is None: valueMax = self._cValueMax = _findMax(dataSeries,self._dataIndex,0) - if valueMin == valueMax: - if valueMax==0: - if oMin is None and oMax is None: - zrp = getattr(self,'zrangePref',0) - if zrp>0: - valueMax = zrp - valueMin = 0 - elif zrp<0: - valueMax = 0 - valueMin = zrp - else: - valueMax = 0.01 - valueMin = -0.01 - elif self.valueMin is None: - valueMin = -0.01 - else: - valueMax = 0.01 - else: - if valueMax>0: - valueMax = 1.2*valueMax - valueMin = 0.0 - else: - valueMax = 0.0 - valueMin = 1.2*valueMin - - if getattr(self,'_bubblePlot',None): - bubbleMax = float(_findMax(dataSeries,2,0)) - frac=.25 - bubbleV=frac*(valueMax-valueMin) - self._bubbleV = bubbleV - self._bubbleMax = bubbleMax - self._bubbleRadius = frac*self._length - def special(T,x,func,bubbleV=bubbleV,bubbleMax=bubbleMax): - try: - v = T[2] - except IndexError: - v = bubbleMAx*0.1 - bubbleV *= (v/bubbleMax)**0.5 - return func(T[x]+bubbleV,T[x]-bubbleV) - if oMin is None: valueMin = self._cValueMin = _findMin(dataSeries,self._dataIndex,0,special=special) - if oMax is None: valueMax = self._cValueMax = _findMax(dataSeries,self._dataIndex,0,special=special) - - forceZero = self.forceZero - if forceZero: - if forceZero=='near': - forceZero = min(abs(valueMin),abs(valueMax)) <= 5*(valueMax-valueMin) - if forceZero: - if valueMax<0: valueMax=0 - elif valueMin>0: valueMin = 0 - - abf = self.avoidBoundFrac - do_rr = not getattr(self,'valueSteps',None) - do_abf = abf and do_rr - if type(abf) not in (TupleType,ListType): - abf = abf, abf - do_rr = rangeRound is not 'none' and do_rr - if do_rr: - rrn = rangeRound in ['both','floor'] - rrx = rangeRound in ['both','ceiling'] - else: - rrn = rrx = 0 - - go = do_rr or do_abf - cache = {} - cMin = valueMin - cMax = valueMax - iter = 0 - while go and iter<=10: - iter += 1 - go = 0 - if do_abf: - valueStep, T, fuzz = self._getValueStepAndTicks(valueMin, valueMax, cache) - i0 = valueStep*abf[0] - i1 = valueStep*abf[1] - if rrn: v = T[0] - else: v = valueMin - u = cMin-i0 - if abs(v)>fuzz and v>=u+fuzz: - valueMin = u - go = 1 - if rrx: v = T[-1] - else: v = valueMax - u = cMax+i1 - if abs(v)>fuzz and v<=u-fuzz: - valueMax = u - go = 1 - - if do_rr: - valueStep, T, fuzz = self._getValueStepAndTicks(valueMin, valueMax, cache) - if rrn: - if valueMin=T[0]+fuzz - valueMin = T[0] - if rrx: - if valueMax>T[-1]+fuzz: - valueMax = T[-1]+valueStep - go = 1 - else: - go = valueMax<=T[-1]-fuzz - valueMax = T[-1] - - self._valueMin, self._valueMax = valueMin, valueMax - self._rangeAdjust() - - def _rangeAdjust(self): - """Override this if you want to alter the calculated range. - - E.g. if want a minumamum range of 30% or don't want 100% - as the first point. - """ - pass - - def _adjustAxisTicks(self): - '''Override if you want to put slack at the ends of the axis - eg if you don't want the last tick to be at the bottom etc - ''' - pass - - def _calcScaleFactor(self): - """Calculate the axis' scale factor. - This should be called only *after* the axis' range is set. - Returns a number. - """ - self._scaleFactor = self._length / float(self._valueMax - self._valueMin) - return self._scaleFactor - - def _calcTickPositions(self): - self._calcValueStep() - valueMin, valueMax, valueStep = self._valueMin, self._valueMax, self._valueStep - fuzz = 1e-8*valueStep - rangeRound = self.rangeRound - i0 = int(float(valueMin)/valueStep) - v = i0*valueStep - if rangeRound in ('both','floor'): - if v>valueMin+fuzz: i0 -= 1 - elif vvalueMax+fuzz: i1 -= 1 - return [i*valueStep for i in xrange(i0,i1+1)] - - def _calcTickmarkPositions(self): - """Calculate a list of tick positions on the axis. Returns a list of numbers.""" - self._tickValues = getattr(self,'valueSteps',None) - if self._tickValues: return self._tickValues - self._tickValues = self._calcTickPositions() - self._adjustAxisTicks() - return self._tickValues - - def _calcValueStep(self): - '''Calculate _valueStep for the axis or get from valueStep.''' - if self.valueStep is None: - rawRange = self._valueMax - self._valueMin - rawInterval = rawRange / min(float(self.maximumTicks-1),(float(self._length)/self.minimumTickSpacing)) - self._valueStep = nextRoundNumber(rawInterval) - else: - self._valueStep = self.valueStep - - def _allIntTicks(self): - return _allInt(self._tickValues) - - def makeTickLabels(self): - g = Group() - if not self.visibleLabels: return g - - f = self._labelTextFormat # perhaps someone already set it - if f is None: - f = self.labelTextFormat or (self._allIntTicks() and '%.0f' or str) - elif f is str and self._allIntTicks(): f = '%.0f' - post = self.labelTextPostFormat - scl = self.labelTextScale - pos = [self._x, self._y] - d = self._dataIndex - labels = self.labels - - i = 0 - for tick in self._tickValues: - if f and labels[i].visible: - v = self.scale(tick) - if scl is not None: - t = tick*scl - else: - t = tick - if type(f) is StringType: txt = f % t - elif type(f) in (TupleType,ListType): - #it's a list, use as many items as we get - if i < len(f): - txt = f[i] - else: - txt = '' - elif callable(f): - txt = f(t) - else: - raise ValueError, 'Invalid labelTextFormat %s' % f - if post: txt = post % txt - label = labels[i] - pos[d] = v - apply(label.setOrigin,pos) - label.setText(txt) - g.add(label) - i = i + 1 - - return g - - def draw(self): - g = Group() - - if not self.visible: - return g - - g.add(self.makeAxis()) - g.add(self.makeTicks()) - g.add(self.makeTickLabels()) - - return g - - -class XValueAxis(ValueAxis): - "X/value axis" - - _attrMap = AttrMap(BASE=ValueAxis, - tickUp = AttrMapValue(isNumber, - desc='Tick length up the axis.'), - tickDown = AttrMapValue(isNumber, - desc='Tick length down the axis.'), - joinAxis = AttrMapValue(None, - desc='Join both axes if true.'), - joinAxisMode = AttrMapValue(OneOf('bottom', 'top', 'value', 'points', None), - desc="Mode used for connecting axis ('bottom', 'top', 'value', 'points', None)."), - joinAxisPos = AttrMapValue(isNumberOrNone, - desc='Position at which to join with other axis.'), - ) - - # Indicate the dimension of the data we're interested in. - _dataIndex = 0 - - def __init__(self): - ValueAxis.__init__(self) - - self.labels.boxAnchor = 'n' - self.labels.dx = 0 - self.labels.dy = -5 - - self.tickUp = 0 - self.tickDown = 5 - - self.joinAxis = None - self.joinAxisMode = None - self.joinAxisPos = None - - - def demo(self): - self.setPosition(20, 50, 150) - self.configure([(10,20,30,40,50)]) - - d = Drawing(200, 100) - d.add(self) - return d - - - def joinToAxis(self, yAxis, mode='bottom', pos=None): - "Join with y-axis using some mode." - _assertYAxis(yAxis) - if mode == 'bottom': - self._x = yAxis._x * 1.0 - self._y = yAxis._y * 1.0 - elif mode == 'top': - self._x = yAxis._x * 1.0 - self._y = (yAxis._y + yAxis._length) * 1.0 - elif mode == 'value': - self._x = yAxis._x * 1.0 - self._y = yAxis.scale(pos) * 1.0 - elif mode == 'points': - self._x = yAxis._x * 1.0 - self._y = pos * 1.0 - - def _joinToAxis(self): - ja = self.joinAxis - if ja: - jam = self.joinAxisMode - jap = self.joinAxisPos - jta = self.joinToAxis - if jam in ('bottom', 'top'): - jta(ja, mode=jam) - elif jam in ('value', 'points'): - jta(ja, mode=jam, pos=jap) - - def scale(self, value): - """Converts a numeric value to a Y position. - - The chart first configures the axis, then asks it to - work out the x value for each point when plotting - lines or bars. You could override this to do - logarithmic axes. - """ - - msg = "Axis cannot scale numbers before it is configured" - assert self._configured, msg - if value is None: - value = 0 - return self._x + self._scaleFactor * (value - self._valueMin) - - - def makeAxis(self): - g = Group() - self._joinToAxis() - if not self.visibleAxis: return g - - axis = Line(self._x, self._y, self._x + self._length, self._y) - axis.strokeColor = self.strokeColor - axis.strokeWidth = self.strokeWidth - axis.strokeDashArray = self.strokeDashArray - g.add(axis) - - return g - - def makeTicks(self): - g = Group() - if self.visibleTicks and (self.tickUp or self.tickDown): - self._makeLines(g,-self.tickDown,self.tickUp,self.strokeColor,self.strokeWidth,self.strokeDashArray) - return g - -class NormalDateXValueAxis(XValueAxis): - """An X axis applying additional rules. - - Depending on the data and some built-in rules, the axis - displays normalDate values as nicely formatted dates. - - The client chart should have NormalDate X values. - """ - - _attrMap = AttrMap(BASE = XValueAxis, - bottomAxisLabelSlack = AttrMapValue(isNumber, desc="Fractional amount used to adjust label spacing"), - niceMonth = AttrMapValue(isBoolean, desc="Flag for displaying months 'nicely'."), - forceEndDate = AttrMapValue(isBoolean, desc='Flag for enforced displaying of last date value.'), - forceFirstDate = AttrMapValue(isBoolean, desc='Flag for enforced displaying of first date value.'), - xLabelFormat = AttrMapValue(None, desc="Label format string (e.g. '{mm}/{yy}') or function."), - dayOfWeekName = AttrMapValue(SequenceOf(isString,emptyOK=0,lo=7,hi=7), desc='Weekday names.'), - monthName = AttrMapValue(SequenceOf(isString,emptyOK=0,lo=12,hi=12), desc='Month names.'), - dailyFreq = AttrMapValue(isBoolean, desc='True if we are to assume daily data to be ticked at end of month.'), - ) - - _valueClass = normalDate.ND - - def __init__(self, **kw): - apply(XValueAxis.__init__, (self,)) - - # some global variables still used... - self.bottomAxisLabelSlack = 0.1 - self.niceMonth = 1 - self.forceEndDate = 0 - self.forceFirstDate = 0 - self.dailyFreq = 0 - self.xLabelFormat = "{mm}/{yy}" - self.dayOfWeekName = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - self.monthName = ['January', 'February', 'March', 'April', 'May', 'June', 'July', - 'August', 'September', 'October', 'November', 'December'] - self.valueSteps = None - - def _scalar2ND(self, x): - "Convert a scalar to a NormalDate value." - d = self._valueClass() - d.normalize(x) - return d - - def _dateFormatter(self, v): - "Create a formatted label for some value." - if not isinstance(v,normalDate.NormalDate): - v = self._scalar2ND(v) - d, m = normalDate._dayOfWeekName, normalDate._monthName - try: - normalDate._dayOfWeekName, normalDate._monthName = self.dayOfWeekName, self.monthName - return v.formatMS(self.xLabelFormat) - finally: - normalDate._dayOfWeekName, normalDate._monthName = d, m - - def _xAxisTicker(self, xVals): - """Complex stuff... - - Needs explanation... - """ - axisLength = self._length - formatter = self._dateFormatter - labels = self.labels - fontName, fontSize, leading = labels.fontName, labels.fontSize, labels.leading - textAnchor, boxAnchor, angle = labels.textAnchor, labels.boxAnchor, labels.angle - RBL = _textBoxLimits(string.split(formatter(xVals[0]),'\n'),fontName, - fontSize,leading or 1.2*fontSize,textAnchor,boxAnchor) - RBL = _rotatedBoxLimits(RBL[0],RBL[1],RBL[2],RBL[3], angle) - xLabelW = RBL[1]-RBL[0] - xLabelH = RBL[3]-RBL[2] - w = max(xLabelW,labels.width,self.minimumTickSpacing) - - W = w+w*self.bottomAxisLabelSlack - n = len(xVals) - ticks = [] - labels = [] - maximumTicks = self.maximumTicks - - def addTick(i, xVals=xVals, formatter=formatter, ticks=ticks, labels=labels): - ticks.insert(0,xVals[i]) - labels.insert(0,formatter(xVals[i])) - - for d in (1,2,3,6,12,24,60,120): - k = n/d - if k<=maximumTicks and k*W <= axisLength: - i = n-1 - if self.niceMonth: - j = xVals[-1].month() % (d<=12 and d or 12) - if j: - if self.forceEndDate: addTick(i) - i = i - j - - #weird first date ie not at end of month - try: - wfd = xVals[0].month() == xVals[1].month() - except: - wfd = 0 - - while i>=wfd: - addTick(i) - i = i - d - - if self.forceFirstDate and ticks[0] != xVals[0]: - addTick(0) - if (axisLength/(ticks[-1]-ticks[0]))*(ticks[1]-ticks[0])<=w: - del ticks[1], labels[1] - if self.forceEndDate and self.niceMonth and j: - if (axisLength/(ticks[-1]-ticks[0]))*(ticks[-1]-ticks[-2])<=w: - del ticks[-2], labels[-2] - try: - if labels[0] and labels[0]==labels[1]: - del ticks[1], labels[1] - except IndexError: - pass - - return ticks, labels - - def _convertXV(self,data): - '''Convert all XValues to a standard normalDate type''' - - VC = self._valueClass - for D in data: - for i in xrange(len(D)): - x, y = D[i] - if not isinstance(x,VC): - D[i] = (VC(x),y) - - def _getStepsAndLabels(self,xVals): - if self.dailyFreq: - xEOM = [] - pm = 0 - px = xVals[0] - for x in xVals: - m = x.month() - if pm!=m: - if pm: xEOM.append(px) - pm = m - px = x - px = xVals[-1] - if xEOM[-1]!=x: xEOM.append(px) - steps, labels = self._xAxisTicker(xEOM) - else: - steps, labels = self._xAxisTicker(xVals) - return steps, labels - - 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.sort() - steps,labels = self._getStepsAndLabels(xVals) - valueMin, valueMax = self.valueMin, self.valueMax - if valueMin is None: valueMin = xVals[0] - if valueMax is None: valueMax = xVals[-1] - self._valueMin, self._valueMax = valueMin, valueMax - self._tickValues = steps - self._labelTextFormat = labels - - self._scaleFactor = self._length / float(valueMax - valueMin) - self._tickValues = steps - self._configured = 1 - -class YValueAxis(ValueAxis): - "Y/value axis" - - _attrMap = AttrMap(BASE=ValueAxis, - tickLeft = AttrMapValue(isNumber, - desc='Tick length left of the axis.'), - tickRight = AttrMapValue(isNumber, - desc='Tick length right of the axis.'), - joinAxis = AttrMapValue(None, - desc='Join both axes if true.'), - joinAxisMode = AttrMapValue(OneOf(('left', 'right', 'value', 'points', None)), - desc="Mode used for connecting axis ('left', 'right', 'value', 'points', None)."), - joinAxisPos = AttrMapValue(isNumberOrNone, - desc='Position at which to join with other axis.'), - ) - - # Indicate the dimension of the data we're interested in. - _dataIndex = 1 - - def __init__(self): - ValueAxis.__init__(self) - - self.labels.boxAnchor = 'e' - self.labels.dx = -5 - self.labels.dy = 0 - - self.tickRight = 0 - self.tickLeft = 5 - - self.joinAxis = None - self.joinAxisMode = None - self.joinAxisPos = None - - - def demo(self): - data = [(10, 20, 30, 42)] - self.setPosition(100, 10, 80) - self.configure(data) - - drawing = Drawing(200, 100) - drawing.add(self) - return drawing - - - - def joinToAxis(self, xAxis, mode='left', pos=None): - "Join with x-axis using some mode." - _assertXAxis(xAxis) - if mode == 'left': - self._x = xAxis._x * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'right': - self._x = (xAxis._x + xAxis._length) * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'value': - self._x = xAxis.scale(pos) * 1.0 - self._y = xAxis._y * 1.0 - elif mode == 'points': - self._x = pos * 1.0 - self._y = xAxis._y * 1.0 - - def _joinToAxis(self): - ja = self.joinAxis - if ja: - jam = self.joinAxisMode - jap = self.joinAxisPos - jta = self.joinToAxis - if jam in ('left', 'right'): - jta(ja, mode=jam) - elif jam in ('value', 'points'): - jta(ja, mode=jam, pos=jap) - - def scale(self, value): - """Converts a numeric value to a Y position. - - The chart first configures the axis, then asks it to - work out the x value for each point when plotting - lines or bars. You could override this to do - logarithmic axes. - """ - - msg = "Axis cannot scale numbers before it is configured" - assert self._configured, msg - - if value is None: - value = 0 - return self._y + self._scaleFactor * (value - self._valueMin) - - - def makeAxis(self): - g = Group() - self._joinToAxis() - if not self.visibleAxis: return g - - axis = Line(self._x, self._y, self._x, self._y + self._length) - axis.strokeColor = self.strokeColor - axis.strokeWidth = self.strokeWidth - axis.strokeDashArray = self.strokeDashArray - g.add(axis) - - return g - - def makeTicks(self): - g = Group() - if self.visibleTicks and (self.tickLeft or self.tickRight): - self._makeLines(g,-self.tickLeft,self.tickRight,self.strokeColor,self.strokeWidth,self.strokeDashArray) - return g - -class AdjYValueAxis(YValueAxis): - """A Y-axis applying additional rules. - - Depending on the data and some built-in rules, the axis - may choose to adjust its range and origin. - """ - _attrMap = AttrMap(BASE = YValueAxis, - requiredRange = AttrMapValue(isNumberOrNone, desc='Minimum required value range.'), - leftAxisPercent = AttrMapValue(isBoolean, desc='When true add percent sign to label values.'), - leftAxisOrigShiftIPC = AttrMapValue(isNumber, desc='Lowest label shift interval ratio.'), - leftAxisOrigShiftMin = AttrMapValue(isNumber, desc='Minimum amount to shift.'), - leftAxisSkipLL0 = AttrMapValue(EitherOr((isBoolean,isListOfNumbers)), desc='Skip/Keep lowest tick label when true/false.\nOr skiplist') - ) - - def __init__(self): - apply(YValueAxis.__init__, (self,)) - self.requiredRange = 30 - self.leftAxisPercent = 1 - self.leftAxisOrigShiftIPC = 0.15 - self.leftAxisOrigShiftMin = 12 - self.leftAxisSkipLL0 = 0 - self.valueSteps = None - - def _rangeAdjust(self): - "Adjusts the value range of the axis." - - 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]) - if not n: n = [m] - - valueStep, requiredRange = self.valueStep, self.requiredRange - if requiredRange and y_max - y_min < requiredRange: - y1, y2 = find_good_grid(y_min, y_max,n=n,grid=valueStep)[:2] - if y2 - y1 < requiredRange: - ym = (y1+y2)*0.5 - y1 = min(ym-requiredRange*0.5,y_min) - y2 = max(ym+requiredRange*0.5,y_max) - if y_min>=100 and y1<100: - y2 = y2 + 100 - y1 - y1 = 100 - elif y_min>=0 and y1<0: - y2 = y2 - y1 - y1 = 0 - self._valueMin, self._valueMax = y1, y2 - - T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep) - abf = self.avoidBoundFrac - if abf: - i1 = (T[1]-T[0]) - if type(abf) not in (TupleType,ListType): - i0 = i1 = i1*abf - else: - i0 = i1*abf[0] - i1 = i1*abf[1] - _n = getattr(self,'_cValueMin',T[0]) - _x = getattr(self,'_cValueMax',T[-1]) - if _n - T[0] < i0: self._valueMin = self._valueMin - i0 - if T[-1]-_x < i1: self._valueMax = self._valueMax + i1 - T, L = ticks(self._valueMin, self._valueMax, split=1, n=n, percent=self.leftAxisPercent,grid=valueStep) - - self._valueMin = T[0] - self._valueMax = T[-1] - self._tickValues = self.valueSteps = T - if self.labelTextFormat is None: - self._labelTextFormat = L - else: - self._labelTextFormat = self.labelTextFormat - - if abs(self._valueMin-100)<1e-6: - self._calcValueStep() - vMax, vMin = self._valueMax, self._valueMin - m = max(self.leftAxisOrigShiftIPC*self._valueStep, - (vMax-vMin)*self.leftAxisOrigShiftMin/self._length) - self._valueMin = self._valueMin - m - - if self.leftAxisSkipLL0: - if type(self.leftAxisSkipLL0) in (ListType,TupleType): - for x in self.leftAxisSkipLL0: - try: - L[x] = '' - except IndexError: - pass - L[0] = '' - -# Sample functions. -def sample0a(): - "Sample drawing with one xcat axis and two buckets." - - drawing = Drawing(400, 200) - - data = [(10, 20)] - - xAxis = XCategoryAxis() - xAxis.setPosition(75, 75, 300) - xAxis.configure(data) - xAxis.categoryNames = ['Ying', 'Yang'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - - return drawing - - -def sample0b(): - "Sample drawing with one xcat axis and one bucket only." - - drawing = Drawing(400, 200) - - data = [(10,)] - - xAxis = XCategoryAxis() - xAxis.setPosition(75, 75, 300) - xAxis.configure(data) - xAxis.categoryNames = ['Ying'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - - return drawing - - -def sample1(): - "Sample drawing containing two unconnected axes." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XCategoryAxis() - xAxis.setPosition(75, 75, 300) - xAxis.configure(data) - xAxis.categoryNames = ['Beer','Wine','Meat','Cannelloni'] - xAxis.labels.boxAnchor = 'n' - xAxis.labels[3].dy = -15 - xAxis.labels[3].angle = 30 - xAxis.labels[3].fontName = 'Times-Bold' - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -##def sample2a(): -## "Make sample drawing with two axes, x connected at top of y." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.joinToAxis(yAxis, mode='top') -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample2b(): -## "Make two axes, x connected at bottom of y." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.joinToAxis(yAxis, mode='bottom') -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample2c(): -## "Make two axes, x connected at fixed value (in points) of y." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.joinToAxis(yAxis, mode='points', pos=100) -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample2d(): -## "Make two axes, x connected at fixed value (of y-axes) of y." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.joinToAxis(yAxis, mode='value', pos=20) -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample3a(): -## "Make sample drawing with two axes, y connected at left of x." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## yAxis.joinToAxis(xAxis, mode='left') -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample3b(): -## "Make sample drawing with two axes, y connected at right of x." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## xAxis = XCategoryAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] -## xAxis.labels.boxAnchor = 'n' -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## yAxis.joinToAxis(xAxis, mode='right') -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing -## -## -##def sample3c(): -## "Make two axes, y connected at fixed value (in points) of x." -## -## drawing = Drawing(400, 200) -## -## data = [(10, 20, 30, 42)] -## -## yAxis = YValueAxis() -## yAxis.setPosition(50, 50, 125) -## yAxis.configure(data) -## -## xAxis = XValueAxis() -## xAxis._length = 300 -## xAxis.configure(data) -## xAxis.joinToAxis(yAxis, mode='points', pos=100) -## -## drawing.add(xAxis) -## drawing.add(yAxis) -## -## return drawing - - -def sample4a(): - "Sample drawing, xvalue/yvalue axes, y connected at 100 pts to x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'points' - xAxis.joinAxisPos = 100 - xAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample4b(): - "Sample drawing, xvalue/yvalue axes, y connected at value 35 of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'value' - xAxis.joinAxisPos = 35 - xAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample4c(): - "Sample drawing, xvalue/yvalue axes, y connected to bottom of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'bottom' - xAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample4c1(): - "xvalue/yvalue axes, without drawing axis lines/ticks." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - yAxis.visibleAxis = 0 - yAxis.visibleTicks = 0 - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'bottom' - xAxis.configure(data) - xAxis.visibleAxis = 0 - xAxis.visibleTicks = 0 - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample4d(): - "Sample drawing, xvalue/yvalue axes, y connected to top of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'top' - xAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample5a(): - "Sample drawing, xvalue/yvalue axes, y connected at 100 pts to x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis.setPosition(50, 50, 300) - xAxis.configure(data) - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'points' - yAxis.joinAxisPos = 100 - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample5b(): - "Sample drawing, xvalue/yvalue axes, y connected at value 35 of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis.setPosition(50, 50, 300) - xAxis.configure(data) - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'value' - yAxis.joinAxisPos = 35 - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample5c(): - "Sample drawing, xvalue/yvalue axes, y connected at right of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis.setPosition(50, 50, 300) - xAxis.configure(data) - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'right' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample5d(): - "Sample drawing, xvalue/yvalue axes, y connected at left of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis.setPosition(50, 50, 300) - xAxis.configure(data) - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'left' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample6a(): - "Sample drawing, xcat/yvalue axes, x connected at top of y." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XCategoryAxis() - xAxis._length = 300 - xAxis.configure(data) - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'top' - xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample6b(): - "Sample drawing, xcat/yvalue axes, x connected at bottom of y." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XCategoryAxis() - xAxis._length = 300 - xAxis.configure(data) - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'bottom' - xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample6c(): - "Sample drawing, xcat/yvalue axes, x connected at 100 pts to y." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XCategoryAxis() - xAxis._length = 300 - xAxis.configure(data) - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'points' - xAxis.joinAxisPos = 100 - xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample6d(): - "Sample drawing, xcat/yvalue axes, x connected at value 20 of y." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - yAxis = YValueAxis() - yAxis.setPosition(50, 50, 125) - yAxis.configure(data) - - xAxis = XCategoryAxis() - xAxis._length = 300 - xAxis.configure(data) - xAxis.joinAxis = yAxis - xAxis.joinAxisMode = 'value' - xAxis.joinAxisPos = 20 - xAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - xAxis.labels.boxAnchor = 'n' - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample7a(): - "Sample drawing, xvalue/ycat axes, y connected at right of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.configure(data) - - yAxis = YCategoryAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'right' - yAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - yAxis.labels.boxAnchor = 'e' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample7b(): - "Sample drawing, xvalue/ycat axes, y connected at left of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.configure(data) - - yAxis = YCategoryAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'left' - yAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - yAxis.labels.boxAnchor = 'e' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample7c(): - "Sample drawing, xvalue/ycat axes, y connected at value 30 of x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.configure(data) - - yAxis = YCategoryAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'value' - yAxis.joinAxisPos = 30 - yAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - yAxis.labels.boxAnchor = 'e' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing - - -def sample7d(): - "Sample drawing, xvalue/ycat axes, y connected at 200 pts to x." - - drawing = Drawing(400, 200) - - data = [(10, 20, 30, 42)] - - xAxis = XValueAxis() - xAxis._length = 300 - xAxis.configure(data) - - yAxis = YCategoryAxis() - yAxis.setPosition(50, 50, 125) - yAxis.joinAxis = xAxis - yAxis.joinAxisMode = 'points' - yAxis.joinAxisPos = 200 - yAxis.categoryNames = ['Beer', 'Wine', 'Meat', 'Cannelloni'] - yAxis.labels.boxAnchor = 'e' - yAxis.configure(data) - - drawing.add(xAxis) - drawing.add(yAxis) - - return drawing diff --git a/bin/reportlab/graphics/charts/barcharts.py b/bin/reportlab/graphics/charts/barcharts.py deleted file mode 100644 index 0ca1ae3ed57..00000000000 --- a/bin/reportlab/graphics/charts/barcharts.py +++ /dev/null @@ -1,1972 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/barcharts.py -"""This module defines a variety of Bar Chart components. - -The basic flavors are Side-by-side, available in horizontal and -vertical versions. - -Stacked and percentile bar charts to follow... -""" -__version__=''' $Id: barcharts.py 2647 2005-07-26 13:47:51Z rgbecker $ ''' - -import string, copy -from types import FunctionType, StringType - -from reportlab.lib import colors -from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isString,\ - isListOfStrings, SequenceOf, isBoolean, isNoneOrShape, isStringOrNone,\ - NoneOr -from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol -from reportlab.lib.formatters import Formatter -from reportlab.lib.attrmap import AttrMap, AttrMapValue -from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.shapes import Line, Rect, Group, Drawing, NotImplementedError -from reportlab.graphics.charts.axes import XCategoryAxis, YValueAxis, YCategoryAxis, XValueAxis -from reportlab.graphics.charts.textlabels import BarChartLabel, NA_Label, NoneOrInstanceOfNA_Label -from reportlab.graphics.charts.areas import PlotArea - -class BarChartProperties(PropHolder): - _attrMap = AttrMap( - strokeColor = AttrMapValue(isColorOrNone, desc='Color of the bar border.'), - fillColor = AttrMapValue(isColorOrNone, desc='Color of the bar interior area.'), - strokeWidth = AttrMapValue(isNumber, desc='Width of the bar border.'), - symbol = AttrMapValue(None, desc='A widget to be used instead of a normal bar.'), - name = AttrMapValue(isString, desc='Text to be associated with a bar (eg seriesname)'), - swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ..."), - ) - - def __init__(self): - self.strokeColor = None - self.fillColor = colors.blue - self.strokeWidth = 0.5 - self.symbol = None - -# Bar chart classes. -class BarChart(PlotArea): - "Abstract base class, unusable by itself." - - _attrMap = AttrMap(BASE=PlotArea, - useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'), - barWidth = AttrMapValue(isNumber, desc='The width of an individual bar.'), - groupSpacing = AttrMapValue(isNumber, desc='Width between groups of bars.'), - barSpacing = AttrMapValue(isNumber, desc='Width between individual bars.'), - bars = AttrMapValue(None, desc='Handle of the individual bars.'), - valueAxis = AttrMapValue(None, desc='Handle of the value axis.'), - categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'), - data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'), - barLabels = AttrMapValue(None, desc='Handle to the list of bar labels.'), - barLabelFormat = AttrMapValue(None, desc='Formatting string or function used for bar labels.'), - barLabelCallOut = AttrMapValue(None, desc='Callout function(label)\nlabel._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0)'), - barLabelArray = AttrMapValue(None, desc='explicit array of bar label values, must match size of data if present.'), - reversePlotOrder = AttrMapValue(isBoolean, desc='If true, reverse common category plot order.'), - naLabel = AttrMapValue(NoneOrInstanceOfNA_Label, desc='Label to use for N/A values.'), - annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'), - ) - - def makeSwatchSample(self, rowNo, x, y, width, height): - baseStyle = self.bars - styleIdx = rowNo % len(baseStyle) - style = baseStyle[styleIdx] - strokeColor = getattr(style, 'strokeColor', getattr(baseStyle,'strokeColor',None)) - fillColor = getattr(style, 'fillColor', getattr(baseStyle,'fillColor',None)) - strokeDashArray = getattr(style, 'strokeDashArray', getattr(baseStyle,'strokeDashArray',None)) - strokeWidth = getattr(style, 'strokeWidth', getattr(style, 'strokeWidth',None)) - swatchMarker = getattr(style, 'swatchMarker', getattr(baseStyle, 'swatchMarker',None)) - if swatchMarker: - return uSymbol2Symbol(swatchMarker,x+width/2.,y+height/2.,fillColor) - return Rect(x,y,width,height,strokeWidth=strokeWidth,strokeColor=strokeColor, - strokeDashArray=strokeDashArray,fillColor=fillColor) - - def getSeriesName(self,i,default=None): - '''return series name i or default''' - return getattr(self.bars[i],'name',default) - - def __init__(self): - assert self.__class__.__name__ not in ('BarChart','BarChart3D'), 'Abstract Class %s Instantiated' % self.__class__.__name__ - - if self._flipXY: - self.categoryAxis = YCategoryAxis() - self.valueAxis = XValueAxis() - else: - self.categoryAxis = XCategoryAxis() - self.valueAxis = YValueAxis() - - PlotArea.__init__(self) - self.barSpacing = 0 - self.reversePlotOrder = 0 - - - # this defines two series of 3 points. Just an example. - self.data = [(100,110,120,130), - (70, 80, 85, 90)] - - # control bar spacing. is useAbsolute = 1 then - # the next parameters are in points; otherwise - # they are 'proportions' and are normalized to - # fit the available space. Half a barSpacing - # is allocated at the beginning and end of the - # chart. - self.useAbsolute = 0 #- not done yet - self.barWidth = 10 - self.groupSpacing = 5 - self.barSpacing = 0 - - self.barLabels = TypedPropertyCollection(BarChartLabel) - self.barLabels.boxAnchor = 'c' - self.barLabels.textAnchor = 'middle' - self.barLabelFormat = None - self.barLabelArray = None - # this says whether the origin is inside or outside - # the bar - +10 means put the origin ten points - # above the tip of the bar if value > 0, or ten - # points inside if bar value < 0. This is different - # to label dx/dy which are not dependent on the - # sign of the data. - self.barLabels.nudge = 0 - - # if you have multiple series, by default they butt - # together. - - # we really need some well-designed default lists of - # colors e.g. from Tufte. These will be used in a - # cycle to set the fill color of each series. - self.bars = TypedPropertyCollection(BarChartProperties) -## self.bars.symbol = None - self.bars.strokeWidth = 1 - self.bars.strokeColor = colors.black - - self.bars[0].fillColor = colors.red - self.bars[1].fillColor = colors.green - self.bars[2].fillColor = colors.blue - self.naLabel = None#NA_Label() - - - def demo(self): - """Shows basic use of a bar chart""" - if self.__class__.__name__=='BarChart': - raise NotImplementedError, 'Abstract Class BarChart has no demo' - drawing = Drawing(200, 100) - bc = self.__class__() - drawing.add(bc) - return drawing - - def _getConfigureData(self): - cA = self.categoryAxis - data = self.data - if cA.style not in ('parallel','parallel_3d'): - _data = data - data = max(map(len,_data))*[0] - for d in _data: - for i in xrange(len(d)): - data[i] = data[i] + (d[i] or 0) - data = list(_data) + [data] - self._configureData = data - - def _getMinMax(self): - '''Attempt to return the data range''' - self._getConfigureData() - self.valueAxis._setRange(self._configureData) - return self.valueAxis._valueMin, self.valueAxis._valueMax - - def _drawBegin(self,org,length): - '''Position and configure value axis, return crossing value''' - vA = self.valueAxis - vA.setPosition(self.x, self.y, length) - self._getConfigureData() - vA.configure(self._configureData) - - # if zero is in chart, put the other axis there, otherwise use low - crossesAt = vA.scale(0) - if crossesAt > org+length or crossesAt=0 and 1 or -1)*nudge, y + 0.5*height - else: - value = height - if anti: value = 0 - return x + 0.5*width, y + value + (height>=0 and 1 or -1)*nudge - - def _addBarLabel(self, g, rowNo, colNo, x, y, width, height): - text = self._getLabelText(rowNo,colNo) - if text: - self._addLabel(text, self.barLabels[(rowNo, colNo)], g, rowNo, colNo, x, y, width, height) - - def _addNABarLabel(self, g, rowNo, colNo, x, y, width, height): - na = self.naLabel - if na and na.text: - na = copy.copy(na) - v = self.valueAxis._valueMax<=0 and -1e-8 or 1e-8 - if width is None: width = v - if height is None: height = v - self._addLabel(na.text, na, g, rowNo, colNo, x, y, width, height) - - def _addLabel(self, text, label, g, rowNo, colNo, x, y, width, height): - if label.visible: - labelWidth = stringWidth(text, label.fontName, label.fontSize) - x0, y0 = self._labelXY(label,x,y,width,height) - flipXY = self._flipXY - if flipXY: - pm = width - else: - pm = height - label._pmv = pm #the plus minus val - fixedEnd = getattr(label,'fixedEnd', None) - if fixedEnd is not None: - v = fixedEnd._getValue(self,pm) - x00, y00 = x0, y0 - if flipXY: - x0 = v - else: - y0 = v - else: - if flipXY: - x00 = x0 - y00 = y+height/2.0 - else: - x00 = x+width/2.0 - y00 = y0 - fixedStart = getattr(label,'fixedStart', None) - if fixedStart is not None: - v = fixedStart._getValue(self,pm) - if flipXY: - x00 = v - else: - y00 = v - - if pm<0: - if flipXY: - dx = -2*label.dx - dy = 0 - else: - dy = -2*label.dy - dx = 0 - else: - dy = dx = 0 - label.setOrigin(x0+dx, y0+dy) - label.setText(text) - sC, sW = label.lineStrokeColor, label.lineStrokeWidth - if sC and sW: g.insert(0,Line(x00,y00,x0,y0, strokeColor=sC, strokeWidth=sW)) - g.add(label) - alx = getattr(self,'barLabelCallOut',None) - if alx: - label._callOutInfo = (self,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0) - alx(label) - del label._callOutInfo - - def _makeBar(self,g,x,y,width,height,rowNo,style): - r = Rect(x, y, width, height) - r.strokeWidth = style.strokeWidth - r.fillColor = style.fillColor - r.strokeColor = style.strokeColor - g.add(r) - - def _makeBars(self,g,lg): - lenData = len(self.data) - bars = self.bars - for rowNo in range(lenData): - row = self._barPositions[rowNo] - styleCount = len(bars) - styleIdx = rowNo % styleCount - rowStyle = bars[styleIdx] - for colNo in range(len(row)): - barPos = row[colNo] - style = bars.has_key((styleIdx,colNo)) and bars[(styleIdx,colNo)] or rowStyle - (x, y, width, height) = barPos - if None in (width,height): - self._addNABarLabel(lg,rowNo,colNo,x,y,width,height) - continue - - # Draw a rectangular symbol for each data item, - # or a normal colored rectangle. - symbol = None - if hasattr(style, 'symbol'): - symbol = copy.deepcopy(style.symbol) - elif hasattr(self.bars, 'symbol'): - symbol = self.bars.symbol - - if symbol: - symbol.x = x - symbol.y = y - symbol.width = width - symbol.height = height - g.add(symbol) - elif abs(width)>1e-7 and abs(height)>=1e-7 and (style.fillColor is not None or style.strokeColor is not None): - self._makeBar(g,x,y,width,height,rowNo,style) - - self._addBarLabel(lg,rowNo,colNo,x,y,width,height) - - def makeBars(self): - g = Group() - lg = Group() - self._makeBars(g,lg) - g.add(lg) - return g - - def _desiredCategoryAxisLength(self): - '''for dynamically computing the desired category axis length''' - style = self.categoryAxis.style - data = self.data - n = len(data) - m = max(map(len,data)) - if style=='parallel': - groupWidth = (n-1)*self.barSpacing+n*self.barWidth - else: - groupWidth = self.barWidth - return m*(self.groupSpacing+groupWidth) - - def draw(self): - cA, vA = self.categoryAxis, self.valueAxis - if vA: ovAjA, vA.joinAxis = vA.joinAxis, cA - if cA: ocAjA, cA.joinAxis = cA.joinAxis, vA - if self._flipXY: - cA.setPosition(self._drawBegin(self.x,self.width), self.y, self.height) - else: - cA.setPosition(self.x, self._drawBegin(self.y,self.height), self.width) - return self._drawFinish() - -class VerticalBarChart(BarChart): - "Vertical bar chart with multiple side-by-side bars." - _flipXY = 0 - -class HorizontalBarChart(BarChart): - "Horizontal bar chart with multiple side-by-side bars." - _flipXY = 1 - -class _FakeGroup: - def __init__(self, cmp=None): - self._data = [] - self._cmp = cmp - - def add(self,what): - self._data.append(what) - - def value(self): - return self._data - - def sort(self): - self._data.sort(self._cmp) - -class BarChart3D(BarChart): - _attrMap = AttrMap(BASE=BarChart, - theta_x = AttrMapValue(isNumber, desc='dx/dz'), - theta_y = AttrMapValue(isNumber, desc='dy/dz'), - zDepth = AttrMapValue(isNumber, desc='depth of an individual series'), - zSpace = AttrMapValue(isNumber, desc='z gap around series'), - ) - theta_x = .5 - theta_y = .5 - zDepth = None - zSpace = None - - def calcBarPositions(self): - BarChart.calcBarPositions(self) - seriesCount = self._seriesCount - zDepth = self.zDepth - if zDepth is None: zDepth = self.barWidth - zSpace = self.zSpace - if zSpace is None: zSpace = self.barSpacing - if self.categoryAxis.style=='parallel_3d': - _3d_depth = seriesCount*zDepth+(seriesCount+1)*zSpace - else: - _3d_depth = zDepth + 2*zSpace - _3d_depth *= self._normFactor - self._3d_dx = self.theta_x*_3d_depth - self._3d_dy = self.theta_y*_3d_depth - - def _calc_z0(self,rowNo): - zDepth = self.zDepth - if zDepth is None: zDepth = self.barWidth - zSpace = self.zSpace - if zSpace is None: zSpace = self.barSpacing - if self.categoryAxis.style=='parallel_3d': - z0 = self._normFactor*(rowNo*(zDepth+zSpace)+zSpace) - else: - z0 = self._normFactor*zSpace - return z0 - - def _makeBar(self,g,x,y,width,height,rowNo,style): - zDepth = self.zDepth - if zDepth is None: zDepth = self.barWidth - zSpace = self.zSpace - if zSpace is None: zSpace = self.barSpacing - z0 = self._calc_z0(rowNo) - z1 = z0 + zDepth*self._normFactor - if width<0: - x += width - width = -width - x += z0*self.theta_x - y += z0*self.theta_y - if self._flipXY: - y += zSpace - else: - x += zSpace - g.add((0,z0,z1,x,y,width,height,rowNo,style)) - - def _addBarLabel(self, g, rowNo, colNo, x, y, width, height): - z0 = self._calc_z0(rowNo) - zSpace = self.zSpace - if zSpace is None: zSpace = self.barSpacing - z1 = z0 - x += z0*self.theta_x - y += z0*self.theta_y - if self._flipXY: - y += zSpace - else: - x += zSpace - g.add((1,z0,z1,x,y,width,height,rowNo,colNo)) - - def makeBars(self): - from utils3d import _draw_3d_bar - fg = _FakeGroup(cmp=self._cmpZ) - self._makeBars(fg,fg) - fg.sort() - g = Group() - theta_x = self.theta_x - theta_y = self.theta_y - for t in fg.value(): - if t[0]==1: - z0,z1,x,y,width,height,rowNo,colNo = t[1:] - BarChart._addBarLabel(self,g,rowNo,colNo,x,y,width,height) - elif t[0]==0: - z0,z1,x,y,width,height,rowNo,style = t[1:] - dz = z1 - z0 - _draw_3d_bar(g, x, x+width, y, y+height, dz*theta_x, dz*theta_y, - fillColor=style.fillColor, fillColorShaded=None, - strokeColor=style.strokeColor, strokeWidth=style.strokeWidth, - shading=0.45) - return g - -class VerticalBarChart3D(BarChart3D,VerticalBarChart): - _cmpZ=lambda self,a,b:cmp((-a[1],a[3],a[0],-a[4]),(-b[1],b[3],b[0],-b[4])) - -class HorizontalBarChart3D(BarChart3D,HorizontalBarChart): - _cmpZ = lambda self,a,b: cmp((-a[1],a[4],a[0],-a[3]),(-b[1],b[4],b[0],-b[3])) #t, z0, z1, x, y = a[:5] - -# Vertical samples. -def sampleV0a(): - "A slightly pathologic bar chart with only TWO data items." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'ne' - bc.categoryAxis.labels.dx = 8 - bc.categoryAxis.labels.dy = -2 - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV0b(): - "A pathologic bar chart with only ONE data item." - - drawing = Drawing(400, 200) - - data = [(42,)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 50 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'ne' - bc.categoryAxis.labels.dx = 8 - bc.categoryAxis.labels.dy = -2 - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = ['Jan-99'] - - drawing.add(bc) - - return drawing - - -def sampleV0c(): - "A really pathologic bar chart with NO data items at all!" - - drawing = Drawing(400, 200) - - data = [()] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'ne' - bc.categoryAxis.labels.dx = 8 - bc.categoryAxis.labels.dy = -2 - bc.categoryAxis.categoryNames = [] - - drawing.add(bc) - - return drawing - - -def sampleV1(): - "Sample of multi-series bar chart." - - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 6, 21, 23, 38, 46, 20, 5) - ] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'ne' - bc.categoryAxis.labels.dx = 8 - bc.categoryAxis.labels.dy = -2 - bc.categoryAxis.labels.angle = 30 - - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - catNames = map(lambda n:n+'-99', catNames) - bc.categoryAxis.categoryNames = catNames - drawing.add(bc) - - return drawing - - -def sampleV2a(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.2), - (0.6, -4.9, -3, 4, 6.8) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 0 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.valueAxis.labels.textAnchor = 'middle' - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.categoryAxis.labels.dy = -60 - - drawing.add(bc) - - return drawing - - -def sampleV2b(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.2), - (0.6, -4.9, -3, 4, 6.8) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 5 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.valueAxis.labels.textAnchor = 'middle' - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.categoryAxis.labels.dy = -60 - - drawing.add(bc) - - return drawing - - -def sampleV2c(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.99), - (0.6, -4.9, -3, 4, 9.99) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 2 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' - bc.valueAxis.labels.textAnchor = 'middle' - bc.categoryAxis.labels.dy = -60 - - bc.barLabels.nudge = 10 - - bc.barLabelFormat = '%0.2f' - bc.barLabels.dx = 0 - bc.barLabels.dy = 0 - bc.barLabels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.barLabels.fontName = 'Helvetica' - bc.barLabels.fontSize = 6 - - drawing.add(bc) - - return drawing - - -def sampleV3(): - "Faked horizontal bar chart using a vertical real one (deprecated)." - - names = ("UK Equities", "US Equities", "European Equities", "Japanese Equities", - "Pacific (ex Japan) Equities", "Emerging Markets Equities", - "UK Bonds", "Overseas Bonds", "UK Index-Linked", "Cash") - - series1 = (-1.5, 0.3, 0.5, 1.0, 0.8, 0.7, 0.4, 0.1, 1.0, 0.3) - series2 = (0.0, 0.33, 0.55, 1.1, 0.88, 0.77, 0.44, 0.11, 1.10, 0.33) - - assert len(names) == len(series1), "bad data" - assert len(names) == len(series2), "bad data" - - drawing = Drawing(400, 200) - - bc = VerticalBarChart() - bc.x = 0 - bc.y = 0 - bc.height = 100 - bc.width = 150 - bc.data = (series1,) - bc.bars.fillColor = colors.green - - bc.barLabelFormat = '%0.2f' - bc.barLabels.dx = 0 - bc.barLabels.dy = 0 - bc.barLabels.boxAnchor = 'w' # irrelevant (becomes 'c') - bc.barLabels.angle = 90 - bc.barLabels.fontName = 'Helvetica' - bc.barLabels.fontSize = 6 - bc.barLabels.nudge = 10 - - bc.valueAxis.visible = 0 - bc.valueAxis.valueMin = -2 - bc.valueAxis.valueMax = +2 - bc.valueAxis.valueStep = 1 - - bc.categoryAxis.tickUp = 0 - bc.categoryAxis.tickDown = 0 - bc.categoryAxis.categoryNames = names - bc.categoryAxis.labels.angle = 90 - bc.categoryAxis.labels.boxAnchor = 'w' - bc.categoryAxis.labels.dx = 0 - bc.categoryAxis.labels.dy = -125 - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 6 - - g = Group(bc) - g.translate(100, 175) - g.rotate(-90) - - drawing.add(g) - - return drawing - - -def sampleV4a(): - "A bar chart showing value axis region starting at *exactly* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV4b(): - "A bar chart showing value axis region starting *below* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = -10 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV4c(): - "A bar chart showing value axis region staring *above* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 10 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV4d(): - "A bar chart showing value axis region entirely *below* zero." - - drawing = Drawing(400, 200) - - data = [(-13, -20)] - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = -30 - bc.valueAxis.valueMax = -10 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -### - -##dataSample5 = [(10, 20), (20, 30), (30, 40), (40, 50), (50, 60)] -##dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30), (50, 20)] -dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30)] - -def sampleV5a(): - "A simple bar chart with no expressed spacing attributes." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV5b(): - "A simple bar chart with proportional spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 0 - bc.barWidth = 40 - bc.groupSpacing = 20 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV5c1(): - "Make sampe simple bar chart but with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 40 - bc.groupSpacing = 0 - bc.barSpacing = 0 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV5c2(): - "Make sampe simple bar chart but with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 40 - bc.groupSpacing = 20 - bc.barSpacing = 0 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV5c3(): - "Make sampe simple bar chart but with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 40 - bc.groupSpacing = 0 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleV5c4(): - "Make sampe simple bar chart but with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 40 - bc.groupSpacing = 20 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'n' - bc.categoryAxis.labels.dy = -5 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -# Horizontal samples - -def sampleH0a(): - "Make a slightly pathologic bar chart with only TWO data items." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'se' - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH0b(): - "Make a pathologic bar chart with only ONE data item." - - drawing = Drawing(400, 200) - - data = [(42,)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 50 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'se' - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = ['Jan-99'] - - drawing.add(bc) - - return drawing - - -def sampleH0c(): - "Make a really pathologic bar chart with NO data items at all!" - - drawing = Drawing(400, 200) - - data = [()] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'se' - bc.categoryAxis.labels.angle = 30 - bc.categoryAxis.categoryNames = [] - - drawing.add(bc) - - return drawing - - -def sampleH1(): - "Sample of multi-series bar chart." - - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 6, 21, 23, 38, 46, 20, 5) - ] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - catNames = map(lambda n:n+'-99', catNames) - bc.categoryAxis.categoryNames = catNames - drawing.add(bc, 'barchart') - - return drawing - - -def sampleH2a(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.2), - (0.6, -4.9, -3, 4, 6.8) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = HorizontalBarChart() - bc.x = 80 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 0 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.valueAxis.labels.textAnchor = 'middle' - bc.valueAxis.configure(bc.data) - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.categoryAxis.labels.dx = -150 - - drawing.add(bc) - - return drawing - - -def sampleH2b(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.2), - (0.6, -4.9, -3, 4, 6.8) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = HorizontalBarChart() - bc.x = 80 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 5 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.valueAxis.labels.textAnchor = 'middle' - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.categoryAxis.labels.dx = -150 - - drawing.add(bc) - - return drawing - - -def sampleH2c(): - "Sample of multi-series bar chart." - - data = [(2.4, -5.7, 2, 5, 9.99), - (0.6, -4.9, -3, 4, 9.99) - ] - - labels = ("Q3 2000", "Year to Date", "12 months", - "Annualised\n3 years", "Since 07.10.99") - - drawing = Drawing(400, 200) - - bc = HorizontalBarChart() - bc.x = 80 - bc.y = 50 - bc.height = 120 - bc.width = 300 - bc.data = data - - bc.barSpacing = 2 - bc.groupSpacing = 10 - bc.barWidth = 10 - - bc.valueAxis.valueMin = -15 - bc.valueAxis.valueMax = +15 - bc.valueAxis.valueStep = 5 - bc.valueAxis.labels.fontName = 'Helvetica' - bc.valueAxis.labels.fontSize = 8 - bc.valueAxis.labels.boxAnchor = 'n' - bc.valueAxis.labels.textAnchor = 'middle' - - bc.categoryAxis.categoryNames = labels - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 8 - bc.categoryAxis.labels.dx = -150 - - bc.barLabels.nudge = 10 - - bc.barLabelFormat = '%0.2f' - bc.barLabels.dx = 0 - bc.barLabels.dy = 0 - bc.barLabels.boxAnchor = 'n' # irrelevant (becomes 'c') - bc.barLabels.fontName = 'Helvetica' - bc.barLabels.fontSize = 6 - - drawing.add(bc) - - return drawing - - -def sampleH3(): - "A really horizontal bar chart (compared to the equivalent faked one)." - - names = ("UK Equities", "US Equities", "European Equities", "Japanese Equities", - "Pacific (ex Japan) Equities", "Emerging Markets Equities", - "UK Bonds", "Overseas Bonds", "UK Index-Linked", "Cash") - - series1 = (-1.5, 0.3, 0.5, 1.0, 0.8, 0.7, 0.4, 0.1, 1.0, 0.3) - series2 = (0.0, 0.33, 0.55, 1.1, 0.88, 0.77, 0.44, 0.11, 1.10, 0.33) - - assert len(names) == len(series1), "bad data" - assert len(names) == len(series2), "bad data" - - drawing = Drawing(400, 200) - - bc = HorizontalBarChart() - bc.x = 100 - bc.y = 20 - bc.height = 150 - bc.width = 250 - bc.data = (series1,) - bc.bars.fillColor = colors.green - - bc.barLabelFormat = '%0.2f' - bc.barLabels.dx = 0 - bc.barLabels.dy = 0 - bc.barLabels.boxAnchor = 'w' # irrelevant (becomes 'c') - bc.barLabels.fontName = 'Helvetica' - bc.barLabels.fontSize = 6 - bc.barLabels.nudge = 10 - - bc.valueAxis.visible = 0 - bc.valueAxis.valueMin = -2 - bc.valueAxis.valueMax = +2 - bc.valueAxis.valueStep = 1 - - bc.categoryAxis.tickLeft = 0 - bc.categoryAxis.tickRight = 0 - bc.categoryAxis.categoryNames = names - bc.categoryAxis.labels.boxAnchor = 'w' - bc.categoryAxis.labels.dx = -170 - bc.categoryAxis.labels.fontName = 'Helvetica' - bc.categoryAxis.labels.fontSize = 6 - - g = Group(bc) - drawing.add(g) - - return drawing - - -def sampleH4a(): - "A bar chart showing value axis region starting at *exactly* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH4b(): - "A bar chart showing value axis region starting *below* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = -10 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH4c(): - "A bar chart showing value axis region starting *above* zero." - - drawing = Drawing(400, 200) - - data = [(13, 20)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 10 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH4d(): - "A bar chart showing value axis region entirely *below* zero." - - drawing = Drawing(400, 200) - - data = [(-13, -20)] - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = -30 - bc.valueAxis.valueMax = -10 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -dataSample5 = [(10, 60), (20, 50), (30, 40), (40, 30)] - -def sampleH5a(): - "A simple bar chart with no expressed spacing attributes." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH5b(): - "A simple bar chart with proportional spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 0 - bc.barWidth = 40 - bc.groupSpacing = 20 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH5c1(): - "A simple bar chart with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 10 - bc.groupSpacing = 0 - bc.barSpacing = 0 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH5c2(): - "Simple bar chart with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 10 - bc.groupSpacing = 20 - bc.barSpacing = 0 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH5c3(): - "Simple bar chart with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 20 - bc.height = 155 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 10 - bc.groupSpacing = 0 - bc.barSpacing = 2 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - - -def sampleH5c4(): - "Simple bar chart with absolute spacing." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 10 - bc.groupSpacing = 20 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - drawing.add(bc) - - return drawing - -def sampleSymbol1(): - "Simple bar chart using symbol attribute." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.barWidth = 10 - bc.groupSpacing = 15 - bc.barSpacing = 3 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - from reportlab.graphics.widgets.grids import ShadedRect - sym1 = ShadedRect() - sym1.fillColorStart = colors.black - sym1.fillColorEnd = colors.blue - sym1.orientation = 'horizontal' - sym1.strokeWidth = 0 - - sym2 = ShadedRect() - sym2.fillColorStart = colors.black - sym2.fillColorEnd = colors.pink - sym2.orientation = 'horizontal' - sym2.strokeWidth = 0 - - sym3 = ShadedRect() - sym3.fillColorStart = colors.blue - sym3.fillColorEnd = colors.white - sym3.orientation = 'vertical' - sym3.cylinderMode = 1 - sym3.strokeWidth = 0 - - bc.bars.symbol = sym1 - bc.bars[2].symbol = sym2 - bc.bars[3].symbol = sym3 - - drawing.add(bc) - - return drawing - -def sampleStacked1(): - "Simple bar chart using symbol attribute." - - drawing = Drawing(400, 200) - - data = dataSample5 - - bc = VerticalBarChart() - bc.categoryAxis.style = 'stacked' - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = data - bc.strokeColor = colors.black - - bc.barWidth = 10 - bc.groupSpacing = 15 - bc.valueAxis.valueMin = 0 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - from reportlab.graphics.widgets.grids import ShadedRect - bc.bars.symbol = ShadedRect() - bc.bars.symbol.fillColorStart = colors.red - bc.bars.symbol.fillColorEnd = colors.white - bc.bars.symbol.orientation = 'vertical' - bc.bars.symbol.cylinderMode = 1 - bc.bars.symbol.strokeWidth = 0 - - bc.bars[1].symbol = ShadedRect() - bc.bars[1].symbol.fillColorStart = colors.magenta - bc.bars[1].symbol.fillColorEnd = colors.white - bc.bars[1].symbol.orientation = 'vertical' - bc.bars[1].symbol.cylinderMode = 1 - bc.bars[1].symbol.strokeWidth = 0 - - bc.bars[2].symbol = ShadedRect() - bc.bars[2].symbol.fillColorStart = colors.green - bc.bars[2].symbol.fillColorEnd = colors.white - bc.bars[2].symbol.orientation = 'vertical' - bc.bars[2].symbol.cylinderMode = 1 - bc.bars[2].symbol.strokeWidth = 0 - - bc.bars[3].symbol = ShadedRect() - bc.bars[3].symbol.fillColorStart = colors.blue - bc.bars[3].symbol.fillColorEnd = colors.white - bc.bars[3].symbol.orientation = 'vertical' - bc.bars[3].symbol.cylinderMode = 1 - bc.bars[3].symbol.strokeWidth = 0 - - drawing.add(bc) - - return drawing - -#class version of function sampleH5c4 above -class SampleH5c4(Drawing): - "Simple bar chart with absolute spacing." - - def __init__(self,width=400,height=200,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - bc = HorizontalBarChart() - bc.x = 50 - bc.y = 50 - bc.height = 125 - bc.width = 300 - bc.data = dataSample5 - bc.strokeColor = colors.black - - bc.useAbsolute = 1 - bc.barWidth = 10 - bc.groupSpacing = 20 - bc.barSpacing = 10 - - bc.valueAxis.valueMin = 0 - bc.valueAxis.valueMax = 60 - bc.valueAxis.valueStep = 15 - - bc.categoryAxis.labels.boxAnchor = 'e' - bc.categoryAxis.categoryNames = ['Ying', 'Yang'] - - self.add(bc,name='HBC') diff --git a/bin/reportlab/graphics/charts/dotbox.py b/bin/reportlab/graphics/charts/dotbox.py deleted file mode 100644 index 6962d278822..00000000000 --- a/bin/reportlab/graphics/charts/dotbox.py +++ /dev/null @@ -1,165 +0,0 @@ -from reportlab.lib.colors import blue, _PCMYK_black -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.shapes import Circle, Drawing, Group, Line, Rect, String -from reportlab.graphics.widgetbase import Widget -from reportlab.lib.attrmap import * -from reportlab.lib.validators import * -from reportlab.lib.units import cm -from reportlab.pdfbase.pdfmetrics import getFont -from reportlab.graphics.charts.lineplots import _maxWidth - -class DotBox(Widget): - """Returns a dotbox widget.""" - - #Doesn't use TypedPropertyCollection for labels - this can be a later improvement - _attrMap = AttrMap( - xlabels = AttrMapValue(isNoneOrListOfNoneOrStrings, - desc="List of text labels for boxes on left hand side"), - ylabels = AttrMapValue(isNoneOrListOfNoneOrStrings, - desc="Text label for second box on left hand side"), - labelFontName = AttrMapValue(isString, - desc="Name of font used for the labels"), - labelFontSize = AttrMapValue(isNumber, - desc="Size of font used for the labels"), - labelOffset = AttrMapValue(isNumber, - desc="Space between label text and grid edge"), - strokeWidth = AttrMapValue(isNumber, - desc='Width of the grid and dot outline'), - gridDivWidth = AttrMapValue(isNumber, - desc="Width of each 'box'"), - gridColor = AttrMapValue(isColor, - desc='Colour for the box and gridding'), - dotDiameter = AttrMapValue(isNumber, - desc="Diameter of the circle used for the 'dot'"), - dotColor = AttrMapValue(isColor, - desc='Colour of the circle on the box'), - dotXPosition = AttrMapValue(isNumber, - desc='X Position of the circle'), - dotYPosition = AttrMapValue(isNumber, - desc='X Position of the circle'), - x = AttrMapValue(isNumber, - desc='X Position of dotbox'), - y = AttrMapValue(isNumber, - desc='Y Position of dotbox'), - ) - - def __init__(self): - self.xlabels=["Value", "Blend", "Growth"] - self.ylabels=["Small", "Medium", "Large"] - self.labelFontName = "Helvetica" - self.labelFontSize = 6 - self.labelOffset = 5 - self.strokeWidth = 0.5 - self.gridDivWidth=0.5*cm - self.gridColor=colors.Color(25/255.0,77/255.0,135/255.0) - self.dotDiameter=0.4*cm - self.dotColor=colors.Color(232/255.0,224/255.0,119/255.0) - self.dotXPosition = 1 - self.dotYPosition = 1 - self.x = 30 - self.y = 5 - - - def _getDrawingDimensions(self): - leftPadding=rightPadding=topPadding=bottomPadding=5 - #find width of grid - tx=len(self.xlabels)*self.gridDivWidth - #add padding (and offset) - tx=tx+leftPadding+rightPadding+self.labelOffset - #add in maximum width of text - tx=tx+_maxWidth(self.xlabels, self.labelFontName, self.labelFontSize) - #find height of grid - ty=len(self.ylabels)*self.gridDivWidth - #add padding (and offset) - ty=ty+topPadding+bottomPadding+self.labelOffset - #add in maximum width of text - ty=ty+_maxWidth(self.ylabels, self.labelFontName, self.labelFontSize) - #print (tx, ty) - return (tx,ty) - - def demo(self,drawing=None): - if not drawing: - tx,ty=self._getDrawingDimensions() - drawing = Drawing(tx,ty) - drawing.add(self.draw()) - return drawing - - def draw(self): - g = Group() - - #box - g.add(Rect(self.x,self.y,len(self.xlabels)*self.gridDivWidth,len(self.ylabels)*self.gridDivWidth, - strokeColor=self.gridColor, - strokeWidth=self.strokeWidth, - fillColor=None)) - - #internal gridding - for f in range (1,len(self.ylabels)): - #horizontal - g.add(Line(strokeColor=self.gridColor, - strokeWidth=self.strokeWidth, - x1 = self.x, - y1 = self.y+f*self.gridDivWidth, - x2 = self.x+len(self.xlabels)*self.gridDivWidth, - y2 = self.y+f*self.gridDivWidth)) - for f in range (1,len(self.xlabels)): - #vertical - g.add(Line(strokeColor=self.gridColor, - strokeWidth=self.strokeWidth, - x1 = self.x+f*self.gridDivWidth, - y1 = self.y, - x2 = self.x+f*self.gridDivWidth, - y2 = self.y+len(self.ylabels)*self.gridDivWidth)) - - # draw the 'dot' - g.add(Circle(strokeColor=self.gridColor, - strokeWidth=self.strokeWidth, - fillColor=self.dotColor, - cx = self.x+(self.dotXPosition*self.gridDivWidth), - cy = self.y+(self.dotYPosition*self.gridDivWidth), - r = self.dotDiameter/2.0)) - - #used for centering y-labels (below) - ascent=getFont(self.labelFontName).face.ascent - if ascent==0: - ascent=0.718 # default (from helvetica) - ascent=ascent*self.labelFontSize # normalize - - #do y-labels - if self.ylabels != None: - for f in range (len(self.ylabels)-1,-1,-1): - if self.ylabels[f]!= None: - g.add(String(strokeColor=self.gridColor, - text = self.ylabels[f], - fontName = self.labelFontName, - fontSize = self.labelFontSize, - fillColor=_PCMYK_black, - x = self.x-self.labelOffset, - y = self.y+(f*self.gridDivWidth+(self.gridDivWidth-ascent)/2.0), - textAnchor = 'end')) - - #do x-labels - if self.xlabels != None: - for f in range (0,len(self.xlabels)): - if self.xlabels[f]!= None: - l=Label() - l.x=self.x+(f*self.gridDivWidth)+(self.gridDivWidth+ascent)/2.0 - l.y=self.y+(len(self.ylabels)*self.gridDivWidth)+self.labelOffset - l.angle=90 - l.textAnchor='start' - l.fontName = self.labelFontName - l.fontSize = self.labelFontSize - l.fillColor = _PCMYK_black - l.setText(self.xlabels[f]) - l.boxAnchor = 'sw' - l.draw() - g.add(l) - - return g - - - - -if __name__ == "__main__": - d = DotBox() - d.demo().save(fnRoot="dotbox") \ No newline at end of file diff --git a/bin/reportlab/graphics/charts/doughnut.py b/bin/reportlab/graphics/charts/doughnut.py deleted file mode 100644 index f365f9a3fb8..00000000000 --- a/bin/reportlab/graphics/charts/doughnut.py +++ /dev/null @@ -1,349 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/doughnut.py -# doughnut chart - -"""Doughnut chart - -Produces a circular chart like the doughnut charts produced by Excel. -Can handle multiple series (which produce concentric 'rings' in the chart). - -""" -__version__=''' $Id: doughnut.py 2499 2004-12-29 17:12:34Z rgbecker $ ''' - -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,\ - isListOfStringsOrNone, OneOf, SequenceOf,\ - isBoolean, isListOfColors,\ - isNoneOrListOfNoneOrStrings,\ - isNoneOrListOfNoneOrNumbers,\ - isNumberOrNone -from reportlab.lib.attrmap import * -from reportlab.pdfgen.canvas import Canvas -from reportlab.graphics.shapes import Group, Drawing, Line, Rect, Polygon, Ellipse, \ - Wedge, String, SolidShape, UserNode, STATE_DEFAULTS -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.charts.piecharts import AbstractPieChart, WedgeProperties, _addWedgeLabel -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.widgets.markers import Marker - -class SectorProperties(WedgeProperties): - """This holds descriptive information about the sectors in a doughnut chart. - - It is not to be confused with the 'sector itself'; this just holds - a recipe for how to format one, and does not allow you to hack the - angles. It can format a genuine Sector object for you with its - format method. - """ - _attrMap = AttrMap(BASE=WedgeProperties, - ) - -class Doughnut(AbstractPieChart): - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc='X position of the chart within its container.'), - y = AttrMapValue(isNumber, desc='Y position of the chart within its container.'), - width = AttrMapValue(isNumber, desc='width of doughnut bounding box. Need not be same as width.'), - height = AttrMapValue(isNumber, desc='height of doughnut bounding box. Need not be same as height.'), - data = AttrMapValue(None, desc='list of numbers defining sector sizes; need not sum to 1'), - labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"), - startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"), - direction = AttrMapValue(OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"), - slices = AttrMapValue(None, desc="collection of sector descriptor objects"), - simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel"), - ) - - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 100 - self.height = 100 - self.data = [1,1] - self.labels = None # or list of strings - self.startAngle = 90 - self.direction = "clockwise" - self.simpleLabels = 1 - - self.slices = TypedPropertyCollection(SectorProperties) - self.slices[0].fillColor = colors.darkcyan - self.slices[1].fillColor = colors.blueviolet - self.slices[2].fillColor = colors.blue - self.slices[3].fillColor = colors.cyan - - def demo(self): - d = Drawing(200, 100) - - dn = Doughnut() - dn.x = 50 - dn.y = 10 - dn.width = 100 - dn.height = 80 - dn.data = [10,20,30,40,50,60] - dn.labels = ['a','b','c','d','e','f'] - - dn.slices.strokeWidth=0.5 - dn.slices[3].popout = 10 - dn.slices[3].strokeWidth = 2 - dn.slices[3].strokeDashArray = [2,2] - dn.slices[3].labelRadius = 1.75 - dn.slices[3].fontColor = colors.red - dn.slices[0].fillColor = colors.darkcyan - dn.slices[1].fillColor = colors.blueviolet - dn.slices[2].fillColor = colors.blue - dn.slices[3].fillColor = colors.cyan - dn.slices[4].fillColor = colors.aquamarine - dn.slices[5].fillColor = colors.cadetblue - dn.slices[6].fillColor = colors.lightcoral - - d.add(dn) - return d - - 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] - - def makeSectors(self): - # normalize slice data - if type(self.data) in (ListType, TupleType) and type(self.data[0]) in (ListType, TupleType): - #it's a nested list, more than one sequence - normData = [] - n = [] - for l in self.data: - t = self.normalizeData(l) - normData.append(t) - n.append(len(t)) - self._seriesCount = max(n) - else: - normData = self.normalizeData(self.data) - n = len(normData) - self._seriesCount = n - - #labels - if self.labels is None: - labels = [] - if type(n) not in (ListType,TupleType): - labels = [''] * n - else: - for m in n: - labels = list(labels) + [''] * m - else: - 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): - i = n-len(labels) - if i>0: - labels = list(labels) + [''] * i - else: - tlab = 0 - for m in n: - tlab = tlab+m - i = tlab-len(labels) - if i>0: - labels = list(labels) + [''] * i - - xradius = self.width/2.0 - yradius = self.height/2.0 - centerx = self.x + xradius - centery = self.y + yradius - - if self.direction == "anticlockwise": - whichWay = 1 - else: - whichWay = -1 - - g = Group() - sn = 0 - - startAngle = self.startAngle #% 360 - styleCount = len(self.slices) - if type(self.data[0]) in (ListType, TupleType): - #multi-series doughnut - iradius = (self.height/5.0)/len(self.data) - for series in normData: - i = 0 - for angle in series: - endAngle = (startAngle + (angle * whichWay)) #% 360 - if abs(startAngle-endAngle)>=1e-5: - if startAngle < endAngle: - a1 = startAngle - a2 = endAngle - else: - a1 = endAngle - a2 = startAngle - - #if we didn't use %stylecount here we'd end up with the later sectors - #all having the default style - sectorStyle = self.slices[i%styleCount] - - # is it a popout? - cx, cy = centerx, centery - if sectorStyle.popout != 0: - # pop out the sector - averageAngle = (a1+a2)/2.0 - aveAngleRadians = averageAngle * pi/180.0 - popdistance = sectorStyle.popout - cx = centerx + popdistance * cos(aveAngleRadians) - cy = centery + popdistance * sin(aveAngleRadians) - - if type(n) in (ListType,TupleType): - 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) - - theSector.fillColor = sectorStyle.fillColor - theSector.strokeColor = sectorStyle.strokeColor - theSector.strokeWidth = sectorStyle.strokeWidth - theSector.strokeDashArray = sectorStyle.strokeDashArray - - g.add(theSector) - startAngle = endAngle - - text = self.getSeriesName(i,'') - if text: - averageAngle = (a1+a2)/2.0 - aveAngleRadians = averageAngle*pi/180.0 - labelRadius = sectorStyle.labelRadius - labelX = centerx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius) - labelY = centery + (0.5 * self.height * sin(aveAngleRadians) * labelRadius) - _addWedgeLabel(self,text,g.add,averageAngle,labelX,labelY,sectorStyle) - i = i + 1 - sn = sn + 1 - - else: - i = 0 - #single series doughnut - iradius = self.height/5.0 - for angle in normData: - endAngle = (startAngle + (angle * whichWay)) #% 360 - if abs(startAngle-endAngle)>=1e-5: - if startAngle < endAngle: - a1 = startAngle - a2 = endAngle - else: - a1 = endAngle - a2 = startAngle - - #if we didn't use %stylecount here we'd end up with the later sectors - #all having the default style - sectorStyle = self.slices[i%styleCount] - - # is it a popout? - cx, cy = centerx, centery - if sectorStyle.popout != 0: - # pop out the sector - averageAngle = (a1+a2)/2.0 - aveAngleRadians = averageAngle * pi/180.0 - popdistance = sectorStyle.popout - cx = centerx + popdistance * cos(aveAngleRadians) - cy = centery + popdistance * sin(aveAngleRadians) - - if n > 1: - theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, radius1=iradius) - elif n==1: - theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, iradius=iradius) - - theSector.fillColor = sectorStyle.fillColor - theSector.strokeColor = sectorStyle.strokeColor - theSector.strokeWidth = sectorStyle.strokeWidth - theSector.strokeDashArray = sectorStyle.strokeDashArray - - g.add(theSector) - - # now draw a label - if labels[i] != "": - averageAngle = (a1+a2)/2.0 - aveAngleRadians = averageAngle*pi/180.0 - labelRadius = sectorStyle.labelRadius - labelX = centerx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius) - labelY = centery + (0.5 * self.height * sin(aveAngleRadians) * labelRadius) - - theLabel = String(labelX, labelY, labels[i]) - theLabel.textAnchor = "middle" - theLabel.fontSize = sectorStyle.fontSize - theLabel.fontName = sectorStyle.fontName - theLabel.fillColor = sectorStyle.fontColor - - g.add(theLabel) - - startAngle = endAngle - i = i + 1 - - return g - - def draw(self): - g = Group() - g.add(self.makeSectors()) - return g - - -def sample1(): - "Make up something from the individual Sectors" - - d = Drawing(400, 400) - g = Group() - - s1 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=0, endangledegrees=120, radius1=100) - s1.fillColor=colors.red - s1.strokeColor=None - d.add(s1) - s2 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=120, endangledegrees=240, radius1=100) - s2.fillColor=colors.green - s2.strokeColor=None - d.add(s2) - s3 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=240, endangledegrees=260, radius1=100) - s3.fillColor=colors.blue - s3.strokeColor=None - d.add(s3) - s4 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=260, endangledegrees=360, radius1=100) - s4.fillColor=colors.gray - s4.strokeColor=None - d.add(s4) - - return d - -def sample2(): - "Make a simple demo" - - d = Drawing(400, 400) - - dn = Doughnut() - dn.x = 50 - dn.y = 50 - dn.width = 300 - dn.height = 300 - dn.data = [10,20,30,40,50,60] - - d.add(dn) - - return d - -def sample3(): - "Make a more complex demo" - - d = Drawing(400, 400) - dn = Doughnut() - dn.x = 50 - dn.y = 50 - dn.width = 300 - dn.height = 300 - dn.data = [[10,20,30,40,50,60], [10,20,30,40]] - dn.labels = ['a','b','c','d','e','f'] - - d.add(dn) - - return d - -if __name__=='__main__': - - from reportlab.graphics.renderPDF import drawToFile - d = sample1() - drawToFile(d, 'doughnut1.pdf') - d = sample2() - drawToFile(d, 'doughnut2.pdf') - d = sample3() - drawToFile(d, 'doughnut3.pdf') diff --git a/bin/reportlab/graphics/charts/legends.py b/bin/reportlab/graphics/charts/legends.py deleted file mode 100644 index fae4d2b4fec..00000000000 --- a/bin/reportlab/graphics/charts/legends.py +++ /dev/null @@ -1,611 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/legends.py -"""This will be a collection of legends to be used with charts. -""" -__version__=''' $Id: legends.py 2604 2005-06-08 10:12:46Z rgbecker $ ''' - -import copy, operator - -from reportlab.lib import colors -from reportlab.lib.validators import isNumber, OneOf, isString, isColorOrNone,\ - isNumberOrNone, isListOfNumbersOrNone, isStringOrNone, isBoolean,\ - NoneOr, AutoOr, isAuto, Auto, isBoxAnchor, SequenceOf -from reportlab.lib.attrmap import * -from reportlab.pdfbase.pdfmetrics import stringWidth, getFont -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -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 - - -def _getStr(s): - if isSeqType(s): - return map(str,s) - else: - return str(s) - -def _getLines(s): - if isSeqType(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): - return max([len(x) for x in T]) - else: - return len(T) - -def _getWidth(s,fontName, fontSize, sepSpace=0): - if isSeqType(s): - sum = 0 - for t in s: - m = [stringWidth(x, fontName, fontSize) for x in t.split('\n')] - sum += m and max(m) or 0 - sum += (len(s)-1)*sepSpace - return sum - m = [stringWidth(x, fontName, fontSize) for x in s.split('\n')] - return m and max(m) or 0 - -class Legend(Widget): - """A simple legend containing rectangular swatches and strings. - - The swatches are filled rectangles whenever the respective - color object in 'colorNamePairs' is a subclass of Color in - reportlab.lib.colors. Otherwise the object passed instead is - assumed to have 'x', 'y', 'width' and 'height' attributes. - A legend then tries to set them or catches any error. This - lets you plug-in any widget you like as a replacement for - the default rectangular swatches. - - Strings can be nicely aligned left or right to the swatches. - """ - - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc="x-coordinate of upper-left reference point"), - y = AttrMapValue(isNumber, desc="y-coordinate of upper-left reference point"), - deltax = AttrMapValue(isNumberOrNone, desc="x-distance between neighbouring swatches"), - deltay = AttrMapValue(isNumberOrNone, desc="y-distance between neighbouring swatches"), - dxTextSpace = AttrMapValue(isNumber, desc="Distance between swatch rectangle and text"), - autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None"), - autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None"), - yGap = AttrMapValue(isNumber, desc="Additional gap between rows"), - dx = AttrMapValue(isNumber, desc="Width of swatch rectangle"), - dy = AttrMapValue(isNumber, desc="Height of swatch rectangle"), - columnMaximum = AttrMapValue(isNumber, desc="Max. number of items per column"), - alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text with respect to swatches"), - colorNamePairs = AttrMapValue(None, desc="List of color/name tuples (color can also be widget)"), - fontName = AttrMapValue(isString, desc="Font name of the strings"), - fontSize = AttrMapValue(isNumber, desc="Font size of the strings"), - fillColor = AttrMapValue(isColorOrNone, desc=""), - strokeColor = AttrMapValue(isColorOrNone, desc="Border color of the swatches"), - strokeWidth = AttrMapValue(isNumber, desc="Width of the border color of the swatches"), - swatchMarker = AttrMapValue(NoneOr(AutoOr(isSymbol)), desc="None, Auto() or makeMarker('Diamond') ..."), - callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))"), - boxAnchor = AttrMapValue(isBoxAnchor,'Anchor point for the legend area'), - variColumn = AttrMapValue(isBoolean,'If true column widths may vary (default is false)'), - dividerLines = AttrMapValue(OneOf(0,1,2,3,4,5,6,7),'If 1 we have dividers between the rows | 2 for extra top | 4 for bottom'), - dividerWidth = AttrMapValue(isNumber, desc="dividerLines width"), - dividerColor = AttrMapValue(isColorOrNone, desc="dividerLines color"), - dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'), - dividerOffsX = AttrMapValue(SequenceOf(isNumber,emptyOK=0,lo=2,hi=2), desc='divider lines X offsets'), - dividerOffsY = AttrMapValue(isNumber, desc="dividerLines Y offset"), - sepSpace = AttrMapValue(isNumber, desc="separator spacing"), - colEndCallout = AttrMapValue(None, desc="a user callout(self,g, x, xt, y,width, lWidth)"), - ) - - def __init__(self): - # Upper-left reference point. - self.x = 0 - self.y = 0 - - # Alginment of text with respect to swatches. - self.alignment = "left" - - # x- and y-distances between neighbouring swatches. - self.deltax = 75 - self.deltay = 20 - self.autoXPadding = 5 - self.autoYPadding = 2 - - # Size of swatch rectangle. - self.dx = 10 - self.dy = 10 - - # Distance between swatch rectangle and text. - self.dxTextSpace = 10 - - # Max. number of items per column. - self.columnMaximum = 3 - - # Color/name pairs. - self.colorNamePairs = [ (colors.red, "red"), - (colors.blue, "blue"), - (colors.green, "green"), - (colors.pink, "pink"), - (colors.yellow, "yellow") ] - - # Font name and size of the labels. - self.fontName = STATE_DEFAULTS['fontName'] - self.fontSize = STATE_DEFAULTS['fontSize'] - self.fillColor = STATE_DEFAULTS['fillColor'] - self.strokeColor = STATE_DEFAULTS['strokeColor'] - self.strokeWidth = STATE_DEFAULTS['strokeWidth'] - self.swatchMarker = None - self.boxAnchor = 'nw' - self.yGap = 0 - self.variColumn = 0 - self.dividerLines = 0 - self.dividerWidth = 0.5 - self.dividerDashArray = None - self.dividerColor = colors.black - self.dividerOffsX = (0,0) - self.dividerOffsY = 0 - self.sepSpace = 0 - self.colEndCallout = None - - def _getChartStyleName(self,chart): - for a in 'lines', 'bars', 'slices', 'strands': - if hasattr(chart,a): return a - return None - - def _getChartStyle(self,chart): - return getattr(chart,self._getChartStyleName(chart),None) - - def _getTexts(self,colorNamePairs): - if not isAuto(colorNamePairs): - texts = [_getStr(p[1]) for p in colorNamePairs] - else: - chart = colorNamePairs.chart - texts = [str(chart.getSeriesName(i,'series %d' % i)) for i in xrange(chart._seriesCount)] - return texts - - def _calculateMaxWidth(self, colorNamePairs): - "Calculate the maximum width of some given strings." - M = [] - a = M.append - for t in self._getTexts(colorNamePairs): - M.append(_getWidth(t, self.fontName, self.fontSize,self.sepSpace)) - if not M: return 0 - if self.variColumn: - columnMaximum = self.columnMaximum - return [max(M[r:r+columnMaximum]) for r in range(0,len(M),self.columnMaximum)] - else: - return max(M) - - def _calcHeight(self): - dy = self.dy - yGap = self.yGap - thisy = upperlefty = self.y - dy - fontSize = self.fontSize - ascent=getFont(self.fontName).face.ascent/1000. - if ascent==0: ascent=0.718 # default (from helvetica) - ascent *= fontSize - leading = fontSize*1.2 - deltay = self.deltay - if not deltay: deltay = max(dy,leading)+self.autoYPadding - columnCount = 0 - count = 0 - lowy = upperlefty - lim = self.columnMaximum - 1 - for name in self._getTexts(self.colorNamePairs): - y0 = thisy+(dy-ascent)*0.5 - y = y0 - _getLineCount(name)*leading - leadingMove = 2*y0-y-thisy - newy = thisy-max(deltay,leadingMove)-yGap - lowy = min(y,newy,lowy) - if count==lim: - count = 0 - thisy = upperlefty - columnCount = columnCount + 1 - else: - thisy = newy - count = count+1 - return upperlefty - lowy - - def _defaultSwatch(self,x,thisy,dx,dy,fillColor,strokeWidth,strokeColor): - return Rect(x, thisy, dx, dy, - fillColor = fillColor, - strokeColor = strokeColor, - strokeWidth = strokeWidth, - ) - - def draw(self): - colorNamePairs = self.colorNamePairs - autoCP = isAuto(colorNamePairs) - if autoCP: - chart = getattr(colorNamePairs,'chart',getattr(colorNamePairs,'obj',None)) - swatchMarker = None - autoCP = Auto(obj=chart) - n = chart._seriesCount - chartTexts = self._getTexts(colorNamePairs) - else: - swatchMarker = getattr(self,'swatchMarker',None) - if isAuto(swatchMarker): - chart = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None)) - swatchMarker = Auto(obj=chart) - n = len(colorNamePairs) - dx = self.dx - dy = self.dy - alignment = self.alignment - columnMaximum = self.columnMaximum - deltax = self.deltax - deltay = self.deltay - dxTextSpace = self.dxTextSpace - fontName = self.fontName - fontSize = self.fontSize - fillColor = self.fillColor - strokeWidth = self.strokeWidth - strokeColor = self.strokeColor - leading = fontSize*1.2 - yGap = self.yGap - if not deltay: - deltay = max(dy,leading)+self.autoYPadding - ba = self.boxAnchor - baw = ba not in ('nw','w','sw','autox') - maxWidth = self._calculateMaxWidth(colorNamePairs) - nCols = int((n+columnMaximum-1)/columnMaximum) - xW = dx+dxTextSpace+self.autoXPadding - variColumn = self.variColumn - if variColumn: - width = reduce(operator.add,maxWidth,0)+xW*nCols - else: - deltax = max(maxWidth+xW,deltax) - width = nCols*deltax - maxWidth = nCols*[maxWidth] - - thisx = self.x - thisy = self.y - self.dy - if ba not in ('ne','n','nw','autoy'): - height = self._calcHeight() - if ba in ('e','c','w'): - thisy += height/2. - else: - thisy += height - if baw: - if ba in ('n','c','s'): - thisx -= width/2 - else: - thisx -= width - upperlefty = thisy - - g = Group() - def gAdd(t,g=g,fontName=fontName,fontSize=fontSize,fillColor=fillColor): - t.fontName = fontName - t.fontSize = fontSize - t.fillColor = fillColor - return g.add(t) - - ascent=getFont(fontName).face.ascent/1000. - if ascent==0: ascent=0.718 # default (from helvetica) - ascent *= fontSize # normalize - - lim = columnMaximum - 1 - callout = getattr(self,'callout',None) - dividerLines = self.dividerLines - if dividerLines: - dividerWidth = self.dividerWidth - dividerColor = self.dividerColor - dividerDashArray = self.dividerDashArray - dividerOffsX = self.dividerOffsX - dividerOffsY = self.dividerOffsY - - for i in xrange(n): - if autoCP: - col = autoCP - col.index = i - name = chartTexts[i] - else: - col, name = colorNamePairs[i] - if isAuto(swatchMarker): - col = swatchMarker - col.index = i - if isAuto(name): - name = getattr(swatchMarker,'chart',getattr(swatchMarker,'obj',None)).getSeriesName(i,'series %d' % i) - T = _getLines(name) - S = [] - j = int(i/columnMaximum) - - # thisy+dy/2 = y+leading/2 - y = y0 = thisy+(dy-ascent)*0.5 - - if callout: callout(self,g,thisx,y,(col,name)) - if alignment == "left": - if isSeqType(name): - for t in T[0]: - S.append(String(thisx,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "start")) - y -= leading - yd = y - y = y0 - for t in T[1]: - S.append(String(thisx+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "end")) - y -= leading - y = min(yd,y) - else: - for t in T: - # align text to left - S.append(String(thisx+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "end")) - y -= leading - x = thisx+maxWidth[j]+dxTextSpace - elif alignment == "right": - if isSeqType(name): - y0 = y - for t in T[0]: - S.append(String(thisx+dx+dxTextSpace,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "start")) - y -= leading - yd = y - y = y0 - for t in T[1]: - S.append(String(thisx+dx+dxTextSpace+maxWidth[j],y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "end")) - y -= leading - y = min(yd,y) - else: - for t in T: - # align text to right - S.append(String(thisx+dx+dxTextSpace,y,t,fontName=fontName,fontSize=fontSize,fillColor=fillColor, - textAnchor = "start")) - y -= leading - x = thisx - else: - raise ValueError, "bad alignment" - leadingMove = 2*y0-y-thisy - - if dividerLines: - xd = thisx+dx+dxTextSpace+maxWidth[j]+dividerOffsX[1] - yd = thisy+dy*0.5+dividerOffsY - if ((dividerLines&1) and i%columnMaximum) or ((dividerLines&2) and not i%columnMaximum): - g.add(Line(thisx+dividerOffsX[0],yd,xd,yd, - strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray)) - - if (dividerLines&4) and (i%columnMaximum==lim or i==(n-1)): - yd -= max(deltay,leadingMove)+yGap - g.add(Line(thisx+dividerOffsX[0],yd,xd,yd, - strokeColor=dividerColor, strokeWidth=dividerWidth, strokeDashArray=dividerDashArray)) - - # Make a 'normal' color swatch... - if isAuto(col): - chart = getattr(col,'chart',getattr(col,'obj',None)) - g.add(chart.makeSwatchSample(getattr(col,'index',i),x,thisy,dx,dy)) - elif isinstance(col, colors.Color): - if isSymbol(swatchMarker): - g.add(uSymbol2Symbol(swatchMarker,x+dx/2.,thisy+dy/2.,col)) - else: - g.add(self._defaultSwatch(x,thisy,dx,dy,fillColor=col,strokeWidth=strokeWidth,strokeColor=strokeColor)) - else: - try: - c = copy.deepcopy(col) - c.x = x - c.y = thisy - c.width = dx - c.height = dy - g.add(c) - except: - pass - - map(gAdd,S) - if self.colEndCallout and (i%columnMaximum==lim or i==(n-1)): - if alignment == "left": - xt = thisx - else: - xt = thisx+dx+dxTextSpace - yd = thisy+dy*0.5+dividerOffsY - (max(deltay,leadingMove)+yGap) - self.colEndCallout(self, g, thisx, xt, yd, maxWidth[j], maxWidth[j]+dx+dxTextSpace) - - if i%columnMaximum==lim: - if variColumn: - thisx += maxWidth[j]+xW - else: - thisx = thisx+deltax - thisy = upperlefty - else: - thisy = thisy-max(deltay,leadingMove)-yGap - - return g - - def demo(self): - "Make sample legend." - - d = Drawing(200, 100) - - legend = Legend() - legend.alignment = 'left' - legend.x = 0 - legend.y = 100 - legend.dxTextSpace = 5 - items = 'red green blue yellow pink black white'.split() - items = map(lambda i:(getattr(colors, i), i), items) - legend.colorNamePairs = items - - d.add(legend, 'legend') - - return d - -class TotalAnnotator: - def __init__(self, lText='Total', rText='0.0', fontName='Times-Roman', fontSize=10, - fillColor=colors.black, strokeWidth=0.5, strokeColor=colors.black, strokeDashArray=None, - dx=0, dy=0, dly=0, dlx=(0,0)): - self.lText = lText - self.rText = rText - self.fontName = fontName - self.fontSize = fontSize - self.fillColor = fillColor - self.dy = dy - self.dx = dx - self.dly = dly - self.dlx = dlx - self.strokeWidth = strokeWidth - self.strokeColor = strokeColor - self.strokeDashArray = strokeDashArray - - def __call__(self,legend, g, x, xt, y, width, lWidth): - from reportlab.graphics.shapes import String, Line - fontSize = self.fontSize - fontName = self.fontName - fillColor = self.fillColor - strokeColor = self.strokeColor - strokeWidth = self.strokeWidth - ascent=getFont(fontName).face.ascent/1000. - if ascent==0: ascent=0.718 # default (from helvetica) - ascent *= fontSize - leading = fontSize*1.2 - yt = y+self.dy-ascent*1.3 - if self.lText and fillColor: - g.add(String(xt,yt,self.lText, - fontName=fontName, - fontSize=fontSize, - fillColor=fillColor, - textAnchor = "start")) - if self.rText: - g.add(String(xt+width,yt,self.rText, - fontName=fontName, - fontSize=fontSize, - fillColor=fillColor, - textAnchor = "end")) - if strokeWidth and strokeColor: - yL = y+self.dly-leading - g.add(Line(x+self.dlx[0],yL,x+self.dlx[1]+lWidth,yL, - strokeColor=strokeColor, strokeWidth=strokeWidth, - strokeDashArray=self.strokeDashArray)) - -class LineSwatch(Widget): - """basically a Line with properties added so it can be used in a LineLegend""" - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc="x-coordinate for swatch line start point"), - y = AttrMapValue(isNumber, desc="y-coordinate for swatch line start point"), - width = AttrMapValue(isNumber, desc="length of swatch line"), - height = AttrMapValue(isNumber, desc="used for line strokeWidth"), - strokeColor = AttrMapValue(isColorOrNone, desc="color of swatch line"), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc="dash array for swatch line"), - ) - - def __init__(self): - from reportlab.lib.colors import red - from reportlab.graphics.shapes import Line - self.x = 0 - self.y = 0 - self.width = 20 - self.height = 1 - self.strokeColor = red - self.strokeDashArray = None - - def draw(self): - l = Line(self.x,self.y,self.x+self.width,self.y) - l.strokeColor = self.strokeColor - l.strokeDashArray = self.strokeDashArray - l.strokeWidth = self.height - return l - -class LineLegend(Legend): - """A subclass of Legend for drawing legends with lines as the - swatches rather than rectangles. Useful for lineCharts and - linePlots. Should be similar in all other ways the the standard - Legend class. - """ - - def __init__(self): - Legend.__init__(self) - - # Size of swatch rectangle. - self.dx = 10 - self.dy = 2 - - def _defaultSwatch(self,x,thisy,dx,dy,fillColor,strokeWidth,strokeColor): - l = LineSwatch() - l.x = x - l.y = thisy - l.width = dx - l.height = dy - l.strokeColor = fillColor - return l - -def sample1c(): - "Make sample legend." - - d = Drawing(200, 100) - - legend = Legend() - legend.alignment = 'right' - legend.x = 0 - legend.y = 100 - legend.dxTextSpace = 5 - items = 'red green blue yellow pink black white'.split() - items = map(lambda i:(getattr(colors, i), i), items) - legend.colorNamePairs = items - - d.add(legend, 'legend') - - return d - - -def sample2c(): - "Make sample legend." - - d = Drawing(200, 100) - - legend = Legend() - legend.alignment = 'right' - legend.x = 20 - legend.y = 90 - legend.deltax = 60 - legend.dxTextSpace = 10 - legend.columnMaximum = 4 - items = 'red green blue yellow pink black white'.split() - items = map(lambda i:(getattr(colors, i), i), items) - legend.colorNamePairs = items - - d.add(legend, 'legend') - - return d - -def sample3(): - "Make sample legend with line swatches." - - d = Drawing(200, 100) - - legend = LineLegend() - legend.alignment = 'right' - legend.x = 20 - legend.y = 90 - legend.deltax = 60 - legend.dxTextSpace = 10 - legend.columnMaximum = 4 - items = 'red green blue yellow pink black white'.split() - items = map(lambda i:(getattr(colors, i), i), items) - legend.colorNamePairs = items - d.add(legend, 'legend') - - return d - - -def sample3a(): - "Make sample legend with line swatches and dasharrays on the lines." - - d = Drawing(200, 100) - - legend = LineLegend() - legend.alignment = 'right' - legend.x = 20 - legend.y = 90 - legend.deltax = 60 - legend.dxTextSpace = 10 - legend.columnMaximum = 4 - items = 'red green blue yellow pink black white'.split() - darrays = ([2,1], [2,5], [2,2,5,5], [1,2,3,4], [4,2,3,4], [1,2,3,4,5,6], [1]) - cnp = [] - for i in range(0, len(items)): - l = LineSwatch() - l.strokeColor = getattr(colors, items[i]) - l.strokeDashArray = darrays[i] - cnp.append((l, items[i])) - legend.colorNamePairs = cnp - d.add(legend, 'legend') - - return d diff --git a/bin/reportlab/graphics/charts/linecharts.py b/bin/reportlab/graphics/charts/linecharts.py deleted file mode 100644 index 99ef982a644..00000000000 --- a/bin/reportlab/graphics/charts/linecharts.py +++ /dev/null @@ -1,695 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/linecharts.py -""" -This modules defines a very preliminary Line Chart example. -""" -__version__=''' $Id: linecharts.py 2493 2004-12-22 16:14:25Z rgbecker $ ''' - -import string -from types import FunctionType, StringType - -from reportlab.lib import colors -from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isListOfStrings, \ - isListOfStringsOrNone, SequenceOf, isBoolean, NoneOr, \ - isListOfNumbersOrNone, isStringOrNone -from reportlab.lib.attrmap import * -from reportlab.lib.formatters import Formatter -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.shapes import Line, Rect, Group, Drawing, Polygon, PolyLine -from reportlab.graphics.widgets.signsandsymbols import NoEntry -from reportlab.graphics.charts.axes import XCategoryAxis, YValueAxis -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol, makeMarker -from reportlab.graphics.charts.areas import PlotArea - -class LineChartProperties(PropHolder): - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'), - symbol = AttrMapValue(NoneOr(isSymbol), desc='Widget placed at data points.'), - shader = AttrMapValue(None, desc='Shader Class.'), - filler = AttrMapValue(None, desc='Filler Class.'), - name = AttrMapValue(isStringOrNone, desc='Name of the line.'), - ) - -class AbstractLineChart(PlotArea): - - def makeSwatchSample(self,rowNo, x, y, width, height): - baseStyle = self.lines - styleIdx = rowNo % len(baseStyle) - style = baseStyle[styleIdx] - color = style.strokeColor - y = y+height/2. - if self.joinedLines: - dash = getattr(style, 'strokeDashArray', getattr(baseStyle,'strokeDashArray',None)) - strokeWidth= getattr(style, 'strokeWidth', getattr(style, 'strokeWidth',None)) - L = Line(x,y,x+width,y,strokeColor=color,strokeLineCap=0) - if strokeWidth: L.strokeWidth = strokeWidth - if dash: L.strokeDashArray = dash - else: - L = None - - if hasattr(style, 'symbol'): - S = style.symbol - elif hasattr(baseStyle, 'symbol'): - S = baseStyle.symbol - else: - S = None - - if S: S = uSymbol2Symbol(S,x+width/2.,y,color) - if S and L: - g = Group() - g.add(L) - g.add(S) - return g - return S or L - - def getSeriesName(self,i,default=None): - '''return series name i or default''' - return getattr(self.lines[i],'name',default) - -class LineChart(AbstractLineChart): - pass - -# This is conceptually similar to the VerticalBarChart. -# Still it is better named HorizontalLineChart... :-/ - -class HorizontalLineChart(LineChart): - """Line chart with multiple lines. - - A line chart is assumed to have one category and one value axis. - Despite its generic name this particular line chart class has - a vertical value axis and a horizontal category one. It may - evolve into individual horizontal and vertical variants (like - with the existing bar charts). - - Available attributes are: - - x: x-position of lower-left chart origin - y: y-position of lower-left chart origin - width: chart width - height: chart height - - useAbsolute: disables auto-scaling of chart elements (?) - lineLabelNudge: distance of data labels to data points - lineLabels: labels associated with data values - lineLabelFormat: format string or callback function - groupSpacing: space between categories - - joinedLines: enables drawing of lines - - strokeColor: color of chart lines (?) - fillColor: color for chart background (?) - lines: style list, used cyclically for data series - - valueAxis: value axis object - categoryAxis: category axis object - categoryNames: category names - - data: chart data, a list of data series of equal length - """ - - _attrMap = AttrMap(BASE=LineChart, - useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'), - lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.'), - lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'), - lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'), - lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'), - groupSpacing = AttrMapValue(isNumber, desc='? - Likely to disappear.'), - joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'), - lines = AttrMapValue(None, desc='Handle of the lines.'), - valueAxis = AttrMapValue(None, desc='Handle of the value axis.'), - categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'), - categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'), - data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'), - inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.'), - reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.'), - annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'), - ) - - def __init__(self): - LineChart.__init__(self) - - # Allow for a bounding rectangle. - self.strokeColor = None - self.fillColor = None - - # Named so we have less recoding for the horizontal one :-) - self.categoryAxis = XCategoryAxis() - self.valueAxis = YValueAxis() - - # This defines two series of 3 points. Just an example. - self.data = [(100,110,120,130), - (70, 80, 80, 90)] - self.categoryNames = ('North','South','East','West') - - self.lines = TypedPropertyCollection(LineChartProperties) - self.lines.strokeWidth = 1 - self.lines[0].strokeColor = colors.red - self.lines[1].strokeColor = colors.green - self.lines[2].strokeColor = colors.blue - - # control spacing. if useAbsolute = 1 then - # the next parameters are in points; otherwise - # they are 'proportions' and are normalized to - # fit the available space. - self.useAbsolute = 0 #- not done yet - self.groupSpacing = 1 #5 - - self.lineLabels = TypedPropertyCollection(Label) - self.lineLabelFormat = None - self.lineLabelArray = None - - # This says whether the origin is above or below - # the data point. +10 means put the origin ten points - # above the data point if value > 0, or ten - # points below if data value < 0. This is different - # to label dx/dy which are not dependent on the - # sign of the data. - self.lineLabelNudge = 10 - # If you have multiple series, by default they butt - # together. - - # New line chart attributes. - self.joinedLines = 1 # Connect items with straight lines. - self.inFill = 0 - self.reversePlotOrder = 0 - - - def demo(self): - """Shows basic use of a line chart.""" - - drawing = Drawing(200, 100) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 10, 21, 28, 38, 46, 25, 5) - ] - - lc = HorizontalLineChart() - - lc.x = 20 - lc.y = 10 - lc.height = 85 - lc.width = 170 - lc.data = data - lc.lines.symbol = makeMarker('Circle') - - drawing.add(lc) - - return drawing - - - def calcPositions(self): - """Works out where they go. - - Sets an attribute _positions which is a list of - lists of (x, y) matching the data. - """ - - self._seriesCount = len(self.data) - self._rowLength = max(map(len,self.data)) - - if self.useAbsolute: - # Dimensions are absolute. - normFactor = 1.0 - else: - # Dimensions are normalized to fit. - normWidth = self.groupSpacing - availWidth = self.categoryAxis.scale(0)[1] - normFactor = availWidth / normWidth - - self._positions = [] - for rowNo in range(len(self.data)): - lineRow = [] - for colNo in range(len(self.data[rowNo])): - datum = self.data[rowNo][colNo] - if datum is not None: - (groupX, groupWidth) = self.categoryAxis.scale(colNo) - x = groupX + (0.5 * self.groupSpacing * normFactor) - y = self.valueAxis.scale(0) - height = self.valueAxis.scale(datum) - y - lineRow.append((x, y+height)) - self._positions.append(lineRow) - - - def _innerDrawLabel(self, rowNo, colNo, x, y): - "Draw a label for a given item in the list." - - labelFmt = self.lineLabelFormat - labelValue = self.data[rowNo][colNo] - - if labelFmt is None: - labelText = None - elif type(labelFmt) is StringType: - if labelFmt == 'values': - labelText = self.lineLabelArray[rowNo][colNo] - else: - labelText = labelFmt % labelValue - elif type(labelFmt) is FunctionType: - labelText = labelFmt(labelValue) - elif isinstance(labelFmt, Formatter): - labelText = labelFmt(labelValue) - else: - msg = "Unknown formatter type %s, expected string or function" - raise Exception, msg % labelFmt - - if labelText: - label = self.lineLabels[(rowNo, colNo)] - # Make sure labels are some distance off the data point. - if y > 0: - label.setOrigin(x, y + self.lineLabelNudge) - else: - label.setOrigin(x, y - self.lineLabelNudge) - label.setText(labelText) - else: - label = None - return label - - def drawLabel(self, G, rowNo, colNo, x, y): - '''Draw a label for a given item in the list. - G must have an add method''' - G.add(self._innerDrawLabel(rowNo,colNo,x,y)) - - def makeLines(self): - g = Group() - - labelFmt = self.lineLabelFormat - P = range(len(self._positions)) - if self.reversePlotOrder: P.reverse() - inFill = self.inFill - if inFill: - inFillY = self.categoryAxis._y - inFillX0 = self.valueAxis._x - inFillX1 = inFillX0 + self.categoryAxis._length - inFillG = getattr(self,'_inFillG',g) - - # Iterate over data rows. - for rowNo in P: - row = self._positions[rowNo] - styleCount = len(self.lines) - styleIdx = rowNo % styleCount - rowStyle = self.lines[styleIdx] - rowColor = rowStyle.strokeColor - dash = getattr(rowStyle, 'strokeDashArray', None) - - if hasattr(self.lines[styleIdx], 'strokeWidth'): - strokeWidth = self.lines[styleIdx].strokeWidth - elif hasattr(self.lines, 'strokeWidth'): - strokeWidth = self.lines.strokeWidth - else: - strokeWidth = None - - # Iterate over data columns. - if self.joinedLines: - points = [] - for colNo in range(len(row)): - points += row[colNo] - if inFill: - points = points + [inFillX1,inFillY,inFillX0,inFillY] - inFillG.add(Polygon(points,fillColor=rowColor,strokeColor=rowColor,strokeWidth=0.1)) - else: - line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1) - if strokeWidth: - line.strokeWidth = strokeWidth - if dash: - line.strokeDashArray = dash - g.add(line) - - if hasattr(self.lines[styleIdx], 'symbol'): - uSymbol = self.lines[styleIdx].symbol - elif hasattr(self.lines, 'symbol'): - uSymbol = self.lines.symbol - else: - uSymbol = None - - if uSymbol: - for colNo in range(len(row)): - x1, y1 = row[colNo] - symbol = uSymbol2Symbol(uSymbol,x1,y1,rowStyle.strokeColor) - if symbol: g.add(symbol) - - # Draw item labels. - for colNo in range(len(row)): - x1, y1 = row[colNo] - self.drawLabel(g, rowNo, colNo, x1, y1) - - return g - - def draw(self): - "Draws itself." - - vA, cA = self.valueAxis, self.categoryAxis - vA.setPosition(self.x, self.y, self.height) - if vA: vA.joinAxis = cA - if cA: cA.joinAxis = vA - vA.configure(self.data) - - # If zero is in chart, put x axis there, otherwise - # use bottom. - xAxisCrossesAt = vA.scale(0) - if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)): - y = self.y - else: - y = xAxisCrossesAt - - cA.setPosition(self.x, y, self.width) - cA.configure(self.data) - - self.calcPositions() - - g = Group() - g.add(self.makeBackground()) - if self.inFill: - self._inFillG = Group() - g.add(self._inFillG) - - g.add(cA) - g.add(vA) - vA.gridStart = cA._x - vA.gridEnd = cA._x+cA._length - cA.gridStart = vA._y - cA.gridEnd = vA._y+vA._length - cA.makeGrid(g,parent=self) - vA.makeGrid(g,parent=self) - g.add(self.makeLines()) - for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale)) - return g - -def _cmpFakeItem(a,b): - '''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])) - -class _FakeGroup: - def __init__(self): - self._data = [] - - def add(self,what): - if what: self._data.append(what) - - def value(self): - return self._data - - def sort(self): - self._data.sort(_cmpFakeItem) - #for t in self._data: print t - -class HorizontalLineChart3D(HorizontalLineChart): - _attrMap = AttrMap(BASE=HorizontalLineChart, - theta_x = AttrMapValue(isNumber, desc='dx/dz'), - theta_y = AttrMapValue(isNumber, desc='dy/dz'), - zDepth = AttrMapValue(isNumber, desc='depth of an individual series'), - zSpace = AttrMapValue(isNumber, desc='z gap around series'), - ) - theta_x = .5 - theta_y = .5 - zDepth = 10 - zSpace = 3 - - def calcPositions(self): - HorizontalLineChart.calcPositions(self) - nSeries = self._seriesCount - zSpace = self.zSpace - zDepth = self.zDepth - if self.categoryAxis.style=='parallel_3d': - _3d_depth = nSeries*zDepth+(nSeries+1)*zSpace - else: - _3d_depth = zDepth + 2*zSpace - self._3d_dx = self.theta_x*_3d_depth - self._3d_dy = self.theta_y*_3d_depth - - def _calc_z0(self,rowNo): - zSpace = self.zSpace - if self.categoryAxis.style=='parallel_3d': - z0 = rowNo*(self.zDepth+zSpace)+zSpace - else: - z0 = zSpace - return z0 - - def _zadjust(self,x,y,z): - return x+z*self.theta_x, y+z*self.theta_y - - def makeLines(self): - labelFmt = self.lineLabelFormat - P = range(len(self._positions)) - if self.reversePlotOrder: P.reverse() - inFill = self.inFill - assert not inFill, "inFill not supported for 3d yet" - #if inFill: - #inFillY = self.categoryAxis._y - #inFillX0 = self.valueAxis._x - #inFillX1 = inFillX0 + self.categoryAxis._length - #inFillG = getattr(self,'_inFillG',g) - zDepth = self.zDepth - _zadjust = self._zadjust - theta_x = self.theta_x - theta_y = self.theta_y - F = _FakeGroup() - from utils3d import _make_3d_line_info - tileWidth = getattr(self,'_3d_tilewidth',None) - if not tileWidth and self.categoryAxis.style!='parallel_3d': tileWidth = 1 - - # Iterate over data rows. - for rowNo in P: - row = self._positions[rowNo] - n = len(row) - styleCount = len(self.lines) - styleIdx = rowNo % styleCount - rowStyle = self.lines[styleIdx] - rowColor = rowStyle.strokeColor - dash = getattr(rowStyle, 'strokeDashArray', None) - z0 = self._calc_z0(rowNo) - z1 = z0 + zDepth - - if hasattr(self.lines[styleIdx], 'strokeWidth'): - strokeWidth = self.lines[styleIdx].strokeWidth - elif hasattr(self.lines, 'strokeWidth'): - strokeWidth = self.lines.strokeWidth - else: - strokeWidth = None - - # Iterate over data columns. - if self.joinedLines: - if n: - x0, y0 = row[0] - for colNo in xrange(1,n): - x1, y1 = row[colNo] - _make_3d_line_info( F, x0, x1, y0, y1, z0, z1, - theta_x, theta_y, - rowColor, fillColorShaded=None, tileWidth=tileWidth, - strokeColor=None, strokeWidth=None, strokeDashArray=None, - shading=0.1) - x0, y0 = x1, y1 - - if hasattr(self.lines[styleIdx], 'symbol'): - uSymbol = self.lines[styleIdx].symbol - elif hasattr(self.lines, 'symbol'): - uSymbol = self.lines.symbol - else: - uSymbol = None - - if uSymbol: - for colNo in xrange(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): - x1, y1 = row[colNo] - x1, y1 = _zadjust(x1,y1,z0) - L = self._innerDrawLabel(rowNo, colNo, x1, y1) - if L: F.add((2,z0,z0,x1,y1,L)) - - F.sort() - g = Group() - map(lambda x,a=g.add: a(x[-1]),F.value()) - return g - -class VerticalLineChart(LineChart): - pass - - -def sample1(): - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) - ] - - lc = HorizontalLineChart() - - lc.x = 50 - lc.y = 50 - lc.height = 125 - lc.width = 300 - lc.data = data - lc.joinedLines = 1 - lc.lines.symbol = makeMarker('FilledDiamond') - lc.lineLabelFormat = '%2.0f' - - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - lc.categoryAxis.categoryNames = catNames - lc.categoryAxis.labels.boxAnchor = 'n' - - lc.valueAxis.valueMin = 0 - lc.valueAxis.valueMax = 60 - lc.valueAxis.valueStep = 15 - - drawing.add(lc) - - return drawing - - -class SampleHorizontalLineChart(HorizontalLineChart): - "Sample class overwriting one method to draw additional horizontal lines." - - def demo(self): - """Shows basic use of a line chart.""" - - drawing = Drawing(200, 100) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (14, 10, 21, 28, 38, 46, 25, 5) - ] - - lc = SampleHorizontalLineChart() - - lc.x = 20 - lc.y = 10 - lc.height = 85 - lc.width = 170 - lc.data = data - lc.strokeColor = colors.white - lc.fillColor = colors.HexColor(0xCCCCCC) - - drawing.add(lc) - - return drawing - - - def makeBackground(self): - g = Group() - - g.add(HorizontalLineChart.makeBackground(self)) - - valAxis = self.valueAxis - valTickPositions = valAxis._tickValues - - for y in valTickPositions: - y = valAxis.scale(y) - g.add(Line(self.x, y, self.x+self.width, y, - strokeColor = self.strokeColor)) - - return g - - - -def sample1a(): - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) - ] - - lc = SampleHorizontalLineChart() - - lc.x = 50 - lc.y = 50 - lc.height = 125 - lc.width = 300 - lc.data = data - lc.joinedLines = 1 - lc.strokeColor = colors.white - lc.fillColor = colors.HexColor(0xCCCCCC) - lc.lines.symbol = makeMarker('FilledDiamond') - lc.lineLabelFormat = '%2.0f' - - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - lc.categoryAxis.categoryNames = catNames - lc.categoryAxis.labels.boxAnchor = 'n' - - lc.valueAxis.valueMin = 0 - lc.valueAxis.valueMax = 60 - lc.valueAxis.valueStep = 15 - - drawing.add(lc) - - return drawing - - -def sample2(): - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) - ] - - lc = HorizontalLineChart() - - lc.x = 50 - lc.y = 50 - lc.height = 125 - lc.width = 300 - lc.data = data - lc.joinedLines = 1 - lc.lines.symbol = makeMarker('Smiley') - lc.lineLabelFormat = '%2.0f' - lc.strokeColor = colors.black - lc.fillColor = colors.lightblue - - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - lc.categoryAxis.categoryNames = catNames - lc.categoryAxis.labels.boxAnchor = 'n' - - lc.valueAxis.valueMin = 0 - lc.valueAxis.valueMax = 60 - lc.valueAxis.valueStep = 15 - - drawing.add(lc) - - return drawing - - -def sample3(): - drawing = Drawing(400, 200) - - data = [ - (13, 5, 20, 22, 37, 45, 19, 4), - (5, 20, 46, 38, 23, 21, 6, 14) - ] - - lc = HorizontalLineChart() - - lc.x = 50 - lc.y = 50 - lc.height = 125 - lc.width = 300 - lc.data = data - lc.joinedLines = 1 - lc.lineLabelFormat = '%2.0f' - lc.strokeColor = colors.black - - lc.lines[0].symbol = makeMarker('Smiley') - lc.lines[1].symbol = NoEntry - lc.lines[0].strokeWidth = 2 - lc.lines[1].strokeWidth = 4 - - catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ') - lc.categoryAxis.categoryNames = catNames - lc.categoryAxis.labels.boxAnchor = 'n' - - lc.valueAxis.valueMin = 0 - lc.valueAxis.valueMax = 60 - lc.valueAxis.valueStep = 15 - - drawing.add(lc) - - return drawing diff --git a/bin/reportlab/graphics/charts/lineplots.py b/bin/reportlab/graphics/charts/lineplots.py deleted file mode 100644 index 42732dddde3..00000000000 --- a/bin/reportlab/graphics/charts/lineplots.py +++ /dev/null @@ -1,1083 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/lineplots.py -"""This module defines a very preliminary Line Plot example. -""" -__version__=''' $Id: lineplots.py 2659 2005-08-18 10:28:12Z rgbecker $ ''' - -import string, time -from types import FunctionType - -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -from reportlab.graphics.shapes import Drawing, Group, Rect, Line, PolyLine, Polygon, _SetKeyWordArgs -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.charts.axes import XValueAxis, YValueAxis, AdjYValueAxis, NormalDateXValueAxis -from reportlab.graphics.charts.utils import * -from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol, makeMarker -from reportlab.graphics.widgets.grids import Grid, DoubleGrid, ShadedRect, ShadedPolygon -from reportlab.pdfbase.pdfmetrics import stringWidth, getFont -from reportlab.graphics.charts.areas import PlotArea - -# This might be moved again from here... -class LinePlotProperties(PropHolder): - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'), - symbol = AttrMapValue(None, desc='Widget placed at data points.'), - shader = AttrMapValue(None, desc='Shader Class.'), - filler = AttrMapValue(None, desc='Filler Class.'), - name = AttrMapValue(isStringOrNone, desc='Name of the line.'), - ) - -class Shader(_SetKeyWordArgs): - _attrMap = AttrMap(BASE=PlotArea, - vertical = AttrMapValue(isBoolean, desc='If true shade to x axis'), - colors = AttrMapValue(SequenceOf(isColorOrNone,lo=2,hi=2), desc='(AxisColor, LineColor)'), - ) - - def shade(self, lp, g, rowNo, rowColor, row): - c = [None,None] - c = getattr(self,'colors',c) or c - if not c[0]: c[0] = getattr(lp,'fillColor',colors.white) - if not c[1]: c[1] = rowColor - -class NoFiller: - def fill(self, lp, g, rowNo, rowColor, points): - pass - -class Filler: - '''mixin providing simple polygon fill''' - _attrMap = AttrMap( - fillColor = AttrMapValue(isColorOrNone, desc='filler interior color'), - strokeColor = AttrMapValue(isColorOrNone, desc='filler edge color'), - strokeWidth = AttrMapValue(isNumberOrNone, desc='filler edge width'), - ) - def __init__(self,**kw): - self.__dict__ = kw - - def fill(self, lp, g, rowNo, rowColor, points): - g.add(Polygon(points, - fillColor=getattr(self,'fillColor',rowColor), - strokeColor=getattr(self,'strokeColor',rowColor), - strokeWidth=getattr(self,'strokeWidth',0.1))) - -class ShadedPolyFiller(Filler,ShadedPolygon): - pass - -class PolyFiller(Filler,Polygon): - pass - -from linecharts import AbstractLineChart -class LinePlot(AbstractLineChart): - """Line plot with multiple lines. - - Both x- and y-axis are value axis (so there are no seperate - X and Y versions of this class). - """ - _attrMap = AttrMap(BASE=PlotArea, - reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.'), - lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.'), - lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'), - lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'), - lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'), - joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color used for background border of plot area.'), - fillColor = AttrMapValue(isColorOrNone, desc='Color used for background interior of plot area.'), - lines = AttrMapValue(None, desc='Handle of the lines.'), - xValueAxis = AttrMapValue(None, desc='Handle of the x axis.'), - yValueAxis = AttrMapValue(None, desc='Handle of the y axis.'), - data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) x/y tuples.'), - annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'), - ) - - def __init__(self): - PlotArea.__init__(self) - self.reversePlotOrder = 0 - - self.xValueAxis = XValueAxis() - self.yValueAxis = YValueAxis() - - # this defines two series of 3 points. Just an example. - self.data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3,4), (4,6)) - ] - - self.lines = TypedPropertyCollection(LinePlotProperties) - self.lines.strokeWidth = 1 - self.lines[0].strokeColor = colors.red - self.lines[1].strokeColor = colors.blue - - self.lineLabels = TypedPropertyCollection(Label) - self.lineLabelFormat = None - self.lineLabelArray = None - - # this says whether the origin is inside or outside - # the bar - +10 means put the origin ten points - # above the tip of the bar if value > 0, or ten - # points inside if bar value < 0. This is different - # to label dx/dy which are not dependent on the - # sign of the data. - self.lineLabelNudge = 10 - # if you have multiple series, by default they butt - # together. - - # New line chart attributes. - self.joinedLines = 1 # Connect items with straight lines. - - #private attributes - self._inFill = None - - def demo(self): - """Shows basic use of a line chart.""" - - drawing = Drawing(400, 200) - - data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) - ] - - lp = LinePlot() - - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = data - lp.joinedLines = 1 - lp.lineLabelFormat = '%2.0f' - lp.strokeColor = colors.black - - lp.lines[0].strokeColor = colors.red - lp.lines[0].symbol = makeMarker('FilledCircle') - lp.lines[1].strokeColor = colors.blue - lp.lines[1].symbol = makeMarker('FilledDiamond') - - lp.xValueAxis.valueMin = 0 - lp.xValueAxis.valueMax = 5 - lp.xValueAxis.valueStep = 1 - - lp.yValueAxis.valueMin = 0 - lp.yValueAxis.valueMax = 7 - lp.yValueAxis.valueStep = 1 - - drawing.add(lp) - - return drawing - - - def calcPositions(self): - """Works out where they go. - - Sets an attribute _positions which is a list of - lists of (x, y) matching the data. - """ - - self._seriesCount = len(self.data) - self._rowLength = max(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(''): - x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0]))) - else: - x = self.xValueAxis.scale(datum[0]) - y = self.yValueAxis.scale(datum[1]) - line.append((x, y)) - self._positions.append(line) - - def _innerDrawLabel(self, rowNo, colNo, x, y): - "Draw a label for a given item in the list." - - labelFmt = self.lineLabelFormat - labelValue = self.data[rowNo][colNo][1] ### - - if labelFmt is None: - labelText = None - elif type(labelFmt) is StringType: - if labelFmt == 'values': - labelText = self.lineLabelArray[rowNo][colNo] - else: - labelText = labelFmt % labelValue - elif type(labelFmt) is FunctionType: - labelText = labelFmt(labelValue) - elif isinstance(labelFmt, Formatter): - labelText = labelFmt(labelValue) - else: - msg = "Unknown formatter type %s, expected string or function" - raise Exception, msg % labelFmt - - if labelText: - label = self.lineLabels[(rowNo, colNo)] - #hack to make sure labels are outside the bar - if y > 0: - label.setOrigin(x, y + self.lineLabelNudge) - else: - label.setOrigin(x, y - self.lineLabelNudge) - label.setText(labelText) - else: - label = None - return label - - def drawLabel(self, G, rowNo, colNo, x, y): - '''Draw a label for a given item in the list. - G must have an add method''' - G.add(self._innerDrawLabel(rowNo,colNo,x,y)) - - def makeLines(self): - g = Group() - bubblePlot = getattr(self,'_bubblePlot',None) - if bubblePlot: - yA = self.yValueAxis - xA = self.xValueAxis - bubbleR = min(yA._bubbleRadius,xA._bubbleRadius) - bubbleMax = xA._bubbleMax - - labelFmt = self.lineLabelFormat - - P = range(len(self._positions)) - if self.reversePlotOrder: P.reverse() - inFill = getattr(self,'_inFill',None) - if inFill: - inFillY = self.xValueAxis._y - inFillX0 = self.yValueAxis._x - inFillX1 = inFillX0 + self.xValueAxis._length - inFillG = getattr(self,'_inFillG',g) - # Iterate over data rows. - styleCount = len(self.lines) - for rowNo in P: - row = self._positions[rowNo] - rowStyle = self.lines[rowNo % styleCount] - rowColor = rowStyle.strokeColor - dash = getattr(rowStyle, 'strokeDashArray', None) - - if hasattr(rowStyle, 'strokeWidth'): - width = rowStyle.strokeWidth - elif hasattr(self.lines, 'strokeWidth'): - width = self.lines.strokeWidth - else: - width = None - - # Iterate over data columns. - if self.joinedLines: - points = [] - for xy in row: - points = points + [xy[0], xy[1]] - if inFill: - fpoints = [inFillX0,inFillY] + points + [inFillX1,inFillY] - filler = getattr(rowStyle, 'filler', None) - if filler: - filler.fill(self,inFillG,rowNo,rowColor,fpoints) - else: - inFillG.add(Polygon(fpoints,fillColor=rowColor,strokeColor=rowColor,strokeWidth=width or 0.1)) - if inFill in (None,0,2): - line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1) - if width: - line.strokeWidth = width - if dash: - line.strokeDashArray = dash - g.add(line) - - if hasattr(rowStyle, 'symbol'): - uSymbol = rowStyle.symbol - elif hasattr(self.lines, 'symbol'): - uSymbol = self.lines.symbol - else: - uSymbol = None - - if uSymbol: - j = -1 - if bubblePlot: drow = self.data[rowNo] - for xy in row: - j += 1 - symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor) - if symbol: - if bubblePlot: - symbol.size = bubbleR*(drow[j][2]/bubbleMax)**0.5 - g.add(symbol) - - # Draw data labels. - for colNo in range(len(row)): - x1, y1 = row[colNo] - self.drawLabel(g, rowNo, colNo, x1, y1) - - shader = getattr(rowStyle, 'shader', None) - if shader: shader.shade(self,g,rowNo,rowColor,row) - - return g - - def draw(self): - yA = self.yValueAxis - xA = self.xValueAxis - if getattr(self,'_bubblePlot',None): - yA._bubblePlot = xA._bubblePlot = 1 - yA.setPosition(self.x, self.y, self.height) - if yA: yA.joinAxis = xA - if xA: xA.joinAxis = yA - yA.configure(self.data) - - # if zero is in chart, put x axis there, otherwise use bottom. - xAxisCrossesAt = yA.scale(0) - if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)): - y = self.y - else: - y = xAxisCrossesAt - - xA.setPosition(self.x, y, self.width) - xA.configure(self.data) - self.calcPositions() - g = Group() - g.add(self.makeBackground()) - if self._inFill: - xA._joinToAxis() - self._inFillG = Group() - g.add(self._inFillG) - g.add(xA) - g.add(yA) - yA.gridStart = xA._x - yA.gridEnd = xA._x+xA._length - xA.gridStart = yA._y - xA.gridEnd = yA._y+yA._length - xA.makeGrid(g,parent=self) - yA.makeGrid(g,parent=self) - g.add(self.makeLines()) - for a in getattr(self,'annotations',()): g.add(a(self,xA.scale,yA.scale)) - return g - -class LinePlot3D(LinePlot): - _attrMap = AttrMap(BASE=LinePlot, - theta_x = AttrMapValue(isNumber, desc='dx/dz'), - theta_y = AttrMapValue(isNumber, desc='dy/dz'), - zDepth = AttrMapValue(isNumber, desc='depth of an individual series'), - zSpace = AttrMapValue(isNumber, desc='z gap around series'), - ) - theta_x = .5 - theta_y = .5 - zDepth = 10 - zSpace = 3 - - def calcPositions(self): - LinePlot.calcPositions(self) - nSeries = self._seriesCount - zSpace = self.zSpace - zDepth = self.zDepth - if self.xValueAxis.style=='parallel_3d': - _3d_depth = nSeries*zDepth+(nSeries+1)*zSpace - else: - _3d_depth = zDepth + 2*zSpace - self._3d_dx = self.theta_x*_3d_depth - self._3d_dy = self.theta_y*_3d_depth - - def _calc_z0(self,rowNo): - zSpace = self.zSpace - if self.xValueAxis.style=='parallel_3d': - z0 = rowNo*(self.zDepth+zSpace)+zSpace - else: - z0 = zSpace - return z0 - - def _zadjust(self,x,y,z): - return x+z*self.theta_x, y+z*self.theta_y - - def makeLines(self): - bubblePlot = getattr(self,'_bubblePlot',None) - assert not bubblePlot, "_bubblePlot not supported for 3d yet" - #if bubblePlot: - # yA = self.yValueAxis - # xA = self.xValueAxis - # bubbleR = min(yA._bubbleRadius,xA._bubbleRadius) - # bubbleMax = xA._bubbleMax - - labelFmt = self.lineLabelFormat - positions = self._positions - - P = range(len(positions)) - if self.reversePlotOrder: P.reverse() - inFill = getattr(self,'_inFill',None) - assert not inFill, "inFill not supported for 3d yet" - #if inFill: - # inFillY = self.xValueAxis._y - # inFillX0 = self.yValueAxis._x - # inFillX1 = inFillX0 + self.xValueAxis._length - # inFillG = getattr(self,'_inFillG',g) - zDepth = self.zDepth - _zadjust = self._zadjust - theta_x = self.theta_x - theta_y = self.theta_y - from linecharts import _FakeGroup - F = _FakeGroup() - - from 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) - I = find_intersections(fpositions,small=tileWidth) - ic = None - for i,j,x,y in I: - if ic!=i: - ic = i - jc = 0 - else: - jc+=1 - fpositions[i].insert(j+jc,(x,y)) - tileWidth = None - else: - fpositions = positions - else: - tileWidth = None - fpositions = positions - - # Iterate over data rows. - styleCount = len(self.lines) - for rowNo in P: - row = positions[rowNo] - n = len(row) - rowStyle = self.lines[rowNo % styleCount] - rowColor = rowStyle.strokeColor - dash = getattr(rowStyle, 'strokeDashArray', None) - z0 = self._calc_z0(rowNo) - z1 = z0 + zDepth - - if hasattr(rowStyle, 'strokeWidth'): - width = rowStyle.strokeWidth - elif hasattr(self.lines, 'strokeWidth'): - width = self.lines.strokeWidth - else: - width = None - - # Iterate over data columns. - if self.joinedLines: - if n: - frow = fpositions[rowNo] - x0, y0 = frow[0] - for colNo in xrange(1,len(frow)): - x1, y1 = frow[colNo] - _make_3d_line_info( F, x0, x1, y0, y1, z0, z1, - theta_x, theta_y, - rowColor, fillColorShaded=None, tileWidth=tileWidth, - strokeColor=None, strokeWidth=None, strokeDashArray=None, - shading=0.1) - x0, y0 = x1, y1 - - if hasattr(rowStyle, 'symbol'): - uSymbol = rowStyle.symbol - elif hasattr(self.lines, 'symbol'): - uSymbol = self.lines.symbol - else: - uSymbol = None - - if uSymbol: - for xy in row: - x1, y1 = row[colNo] - x1, y1 = _zadjust(x1,y1,z0) - symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor) - if symbol: F.add((1,z0,z0,x1,y1,symbol)) - - # Draw data labels. - for colNo in xrange(n): - x1, y1 = row[colNo] - x1, y1 = _zadjust(x1,y1,z0) - L = self._innerDrawLabel(rowNo, colNo, x1, y1) - if L: F.add((2,z0,z0,x1,y1,L)) - - F.sort() - g = Group() - map(lambda x,a=g.add: a(x[-1]),F.value()) - return g - -_monthlyIndexData = [[(19971202, 100.0), - (19971231, 100.1704367), - (19980131, 101.5639577), - (19980228, 102.1879927), - (19980331, 101.6337257), - (19980430, 102.7640446), - (19980531, 102.9198038), - (19980630, 103.25938789999999), - (19980731, 103.2516421), - (19980831, 105.4744329), - (19980930, 109.3242705), - (19981031, 111.9859291), - (19981130, 110.9184642), - (19981231, 110.9184642), - (19990131, 111.9882532), - (19990228, 109.7912614), - (19990331, 110.24189629999999), - (19990430, 110.4279321), - (19990531, 109.33955469999999), - (19990630, 108.2341748), - (19990731, 110.21294469999999), - (19990831, 110.9683062), - (19990930, 112.4425371), - (19991031, 112.7314032), - (19991130, 112.3509645), - (19991231, 112.3660659), - (20000131, 110.9255248), - (20000229, 110.5266306), - (20000331, 113.3116101), - (20000430, 111.0449133), - (20000531, 111.702717), - (20000630, 113.5832178)], - [(19971202, 100.0), - (19971231, 100.0), - (19980131, 100.8), - (19980228, 102.0), - (19980331, 101.9), - (19980430, 103.0), - (19980531, 103.0), - (19980630, 103.1), - (19980731, 103.1), - (19980831, 102.8), - (19980930, 105.6), - (19981031, 108.3), - (19981130, 108.1), - (19981231, 111.9), - (19990131, 113.1), - (19990228, 110.2), - (19990331, 111.8), - (19990430, 112.3), - (19990531, 110.1), - (19990630, 109.3), - (19990731, 111.2), - (19990831, 111.7), - (19990930, 112.6), - (19991031, 113.2), - (19991130, 113.9), - (19991231, 115.4), - (20000131, 112.7), - (20000229, 113.9), - (20000331, 115.8), - (20000430, 112.2), - (20000531, 112.6), - (20000630, 114.6)]] - -class GridLinePlot(LinePlot): - """A customized version of LinePlot. - It uses NormalDateXValueAxis() and AdjYValueAxis() for the X and Y axes. - The chart has a default grid background with thin horizontal lines - aligned with the tickmarks (and labels). You can change the back- - ground to be any Grid or ShadedRect, or scale the whole chart. - If you do provide a background, you can specify the colours of the - stripes with 'background.stripeColors'. - """ - - _attrMap = AttrMap(BASE=LinePlot, - background = AttrMapValue(None, desc='Background for chart area (now Grid or ShadedRect).'), - scaleFactor = AttrMapValue(isNumberOrNone, desc='Scalefactor to apply to whole drawing.'), - ) - - def __init__(self): - from reportlab.lib import colors - LinePlot.__init__(self) - self.xValueAxis = NormalDateXValueAxis() - self.yValueAxis = AdjYValueAxis() - self.scaleFactor = None - self.background = Grid() - self.background.orientation = 'horizontal' - self.background.useRects = 0 - self.background.useLines = 1 - self.background.strokeWidth = 0.5 - self.background.strokeColor = colors.black - self.data = _monthlyIndexData - - def demo(self,drawing=None): - from reportlab.lib import colors - if not drawing: - drawing = Drawing(400, 200) - lp = AdjLinePlot() - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = _monthlyIndexData - lp.joinedLines = 1 - lp.strokeColor = colors.black - c0 = colors.PCMYKColor(100,65,0,30, spotName='PANTONE 288 CV', density=100) - lp.lines[0].strokeColor = c0 - lp.lines[0].strokeWidth = 2 - lp.lines[0].strokeDashArray = None - c1 = colors.PCMYKColor(0,79,91,0, spotName='PANTONE Wm Red CV', density=100) - lp.lines[1].strokeColor = c1 - lp.lines[1].strokeWidth = 1 - lp.lines[1].strokeDashArray = [3,1] - lp.xValueAxis.labels.fontSize = 10 - lp.xValueAxis.labels.textAnchor = 'start' - lp.xValueAxis.labels.boxAnchor = 'w' - lp.xValueAxis.labels.angle = -45 - lp.xValueAxis.labels.dx = 0 - lp.xValueAxis.labels.dy = -8 - lp.xValueAxis.xLabelFormat = '{mm}/{yy}' - lp.yValueAxis.labelTextFormat = '%5d%% ' - lp.yValueAxis.tickLeft = 5 - lp.yValueAxis.labels.fontSize = 10 - lp.background = Grid() - lp.background.stripeColors = [colors.pink, colors.lightblue] - lp.background.orientation = 'vertical' - drawing.add(lp,'plot') - return drawing - - def draw(self): - xva, yva = self.xValueAxis, self.yValueAxis - if xva: xva.joinAxis = yva - if yva: yva.joinAxis = xva - - yva.setPosition(self.x, self.y, self.height) - yva.configure(self.data) - - # if zero is in chart, put x axis there, otherwise - # use bottom. - xAxisCrossesAt = yva.scale(0) - if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)): - y = self.y - else: - y = xAxisCrossesAt - - xva.setPosition(self.x, y, self.width) - xva.configure(self.data) - - back = self.background - if isinstance(back, Grid): - if back.orientation == 'vertical' and xva._tickValues: - xpos = 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) - steps = [] - for i in range(len(ypos)-1): - steps.append(ypos[i+1] - ypos[i]) - back.deltaSteps = steps - elif isinstance(back, DoubleGrid): - # Ideally, these lines would not be needed... - back.grid0.x = self.x - back.grid0.y = self.y - back.grid0.width = self.width - back.grid0.height = self.height - back.grid1.x = self.x - back.grid1.y = self.y - back.grid1.width = self.width - back.grid1.height = self.height - - # some room left for optimization... - if back.grid0.orientation == 'vertical' and xva._tickValues: - xpos = 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) - 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) - 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) - steps = [] - for i in range(len(ypos)-1): - steps.append(ypos[i+1] - ypos[i]) - back.grid1.deltaSteps = steps - - self.calcPositions() - - width, height, scaleFactor = self.width, self.height, self.scaleFactor - if scaleFactor and scaleFactor!=1: - #g = Drawing(scaleFactor*width, scaleFactor*height) - g.transform = (scaleFactor, 0, 0, scaleFactor,0,0) - else: - g = Group() - - g.add(self.makeBackground()) - g.add(self.xValueAxis) - g.add(self.yValueAxis) - g.add(self.makeLines()) - - return g - -class AreaLinePlot(LinePlot): - '''we're given data in the form [(X1,Y11,..Y1M)....(Xn,Yn1,...YnM)]'''#' - def __init__(self): - LinePlot.__init__(self) - self._inFill = 1 - self.reversePlotOrder = 1 - self.data = [(1,20,100,30),(2,11,50,15),(3,15,70,40)] - - def draw(self): - try: - odata = self.data - n = len(odata) - m = len(odata[0]) - S = n*[0] - self.data = [] - for i in xrange(1,m): - D = [] - for j in xrange(n): - S[j] = S[j] + odata[j][i] - D.append((odata[j][0],S[j])) - self.data.append(D) - return LinePlot.draw(self) - finally: - self.data = odata - -class SplitLinePlot(AreaLinePlot): - def __init__(self): - AreaLinePlot.__init__(self) - self.xValueAxis = NormalDateXValueAxis() - self.yValueAxis = AdjYValueAxis() - self.data=[(20030601,0.95,0.05,0.0),(20030701,0.95,0.05,0.0),(20030801,0.95,0.05,0.0),(20030901,0.95,0.05,0.0),(20031001,0.95,0.05,0.0),(20031101,0.95,0.05,0.0),(20031201,0.95,0.05,0.0),(20040101,0.95,0.05,0.0),(20040201,0.95,0.05,0.0),(20040301,0.95,0.05,0.0),(20040401,0.95,0.05,0.0),(20040501,0.95,0.05,0.0),(20040601,0.95,0.05,0.0),(20040701,0.95,0.05,0.0),(20040801,0.95,0.05,0.0),(20040901,0.95,0.05,0.0),(20041001,0.95,0.05,0.0),(20041101,0.95,0.05,0.0),(20041201,0.95,0.05,0.0),(20050101,0.95,0.05,0.0),(20050201,0.95,0.05,0.0),(20050301,0.95,0.05,0.0),(20050401,0.95,0.05,0.0),(20050501,0.95,0.05,0.0),(20050601,0.95,0.05,0.0),(20050701,0.95,0.05,0.0),(20050801,0.95,0.05,0.0),(20050901,0.95,0.05,0.0),(20051001,0.95,0.05,0.0),(20051101,0.95,0.05,0.0),(20051201,0.95,0.05,0.0),(20060101,0.95,0.05,0.0),(20060201,0.95,0.05,0.0),(20060301,0.95,0.05,0.0),(20060401,0.95,0.05,0.0),(20060501,0.95,0.05,0.0),(20060601,0.95,0.05,0.0),(20060701,0.95,0.05,0.0),(20060801,0.95,0.05,0.0),(20060901,0.95,0.05,0.0),(20061001,0.95,0.05,0.0),(20061101,0.95,0.05,0.0),(20061201,0.95,0.05,0.0),(20070101,0.95,0.05,0.0),(20070201,0.95,0.05,0.0),(20070301,0.95,0.05,0.0),(20070401,0.95,0.05,0.0),(20070501,0.95,0.05,0.0),(20070601,0.95,0.05,0.0),(20070701,0.95,0.05,0.0),(20070801,0.95,0.05,0.0),(20070901,0.95,0.05,0.0),(20071001,0.95,0.05,0.0),(20071101,0.95,0.05,0.0),(20071201,0.95,0.05,0.0),(20080101,0.95,0.05,0.0),(20080201,0.95,0.05,0.0),(20080301,0.95,0.05,0.0),(20080401,0.95,0.05,0.0),(20080501,0.95,0.05,0.0),(20080601,0.95,0.05,0.0),(20080701,0.95,0.05,0.0),(20080801,0.95,0.05,0.0),(20080901,0.95,0.05,0.0),(20081001,0.95,0.05,0.0),(20081101,0.95,0.05,0.0),(20081201,0.95,0.05,0.0),(20090101,0.95,0.05,0.0),(20090201,0.91,0.09,0.0),(20090301,0.91,0.09,0.0),(20090401,0.91,0.09,0.0),(20090501,0.91,0.09,0.0),(20090601,0.91,0.09,0.0),(20090701,0.91,0.09,0.0),(20090801,0.91,0.09,0.0),(20090901,0.91,0.09,0.0),(20091001,0.91,0.09,0.0),(20091101,0.91,0.09,0.0),(20091201,0.91,0.09,0.0),(20100101,0.91,0.09,0.0),(20100201,0.81,0.19,0.0),(20100301,0.81,0.19,0.0),(20100401,0.81,0.19,0.0),(20100501,0.81,0.19,0.0),(20100601,0.81,0.19,0.0),(20100701,0.81,0.19,0.0),(20100801,0.81,0.19,0.0),(20100901,0.81,0.19,0.0),(20101001,0.81,0.19,0.0),(20101101,0.81,0.19,0.0),(20101201,0.81,0.19,0.0),(20110101,0.81,0.19,0.0),(20110201,0.72,0.28,0.0),(20110301,0.72,0.28,0.0),(20110401,0.72,0.28,0.0),(20110501,0.72,0.28,0.0),(20110601,0.72,0.28,0.0),(20110701,0.72,0.28,0.0),(20110801,0.72,0.28,0.0),(20110901,0.72,0.28,0.0),(20111001,0.72,0.28,0.0),(20111101,0.72,0.28,0.0),(20111201,0.72,0.28,0.0),(20120101,0.72,0.28,0.0),(20120201,0.53,0.47,0.0),(20120301,0.53,0.47,0.0),(20120401,0.53,0.47,0.0),(20120501,0.53,0.47,0.0),(20120601,0.53,0.47,0.0),(20120701,0.53,0.47,0.0),(20120801,0.53,0.47,0.0),(20120901,0.53,0.47,0.0),(20121001,0.53,0.47,0.0),(20121101,0.53,0.47,0.0),(20121201,0.53,0.47,0.0),(20130101,0.53,0.47,0.0),(20130201,0.44,0.56,0.0),(20130301,0.44,0.56,0.0),(20130401,0.44,0.56,0.0),(20130501,0.44,0.56,0.0),(20130601,0.44,0.56,0.0),(20130701,0.44,0.56,0.0),(20130801,0.44,0.56,0.0),(20130901,0.44,0.56,0.0),(20131001,0.44,0.56,0.0),(20131101,0.44,0.56,0.0),(20131201,0.44,0.56,0.0),(20140101,0.44,0.56,0.0),(20140201,0.36,0.5,0.14),(20140301,0.36,0.5,0.14),(20140401,0.36,0.5,0.14),(20140501,0.36,0.5,0.14),(20140601,0.36,0.5,0.14),(20140701,0.36,0.5,0.14),(20140801,0.36,0.5,0.14),(20140901,0.36,0.5,0.14),(20141001,0.36,0.5,0.14),(20141101,0.36,0.5,0.14),(20141201,0.36,0.5,0.14),(20150101,0.36,0.5,0.14),(20150201,0.3,0.41,0.29),(20150301,0.3,0.41,0.29),(20150401,0.3,0.41,0.29),(20150501,0.3,0.41,0.29),(20150601,0.3,0.41,0.29),(20150701,0.3,0.41,0.29),(20150801,0.3,0.41,0.29),(20150901,0.3,0.41,0.29),(20151001,0.3,0.41,0.29),(20151101,0.3,0.41,0.29),(20151201,0.3,0.41,0.29),(20160101,0.3,0.41,0.29),(20160201,0.26,0.36,0.38),(20160301,0.26,0.36,0.38),(20160401,0.26,0.36,0.38),(20160501,0.26,0.36,0.38),(20160601,0.26,0.36,0.38),(20160701,0.26,0.36,0.38),(20160801,0.26,0.36,0.38),(20160901,0.26,0.36,0.38),(20161001,0.26,0.36,0.38),(20161101,0.26,0.36,0.38),(20161201,0.26,0.36,0.38),(20170101,0.26,0.36,0.38),(20170201,0.2,0.3,0.5),(20170301,0.2,0.3,0.5),(20170401,0.2,0.3,0.5),(20170501,0.2,0.3,0.5),(20170601,0.2,0.3,0.5),(20170701,0.2,0.3,0.5),(20170801,0.2,0.3,0.5),(20170901,0.2,0.3,0.5),(20171001,0.2,0.3,0.5),(20171101,0.2,0.3,0.5),(20171201,0.2,0.3,0.5),(20180101,0.2,0.3,0.5),(20180201,0.13,0.37,0.5),(20180301,0.13,0.37,0.5),(20180401,0.13,0.37,0.5),(20180501,0.13,0.37,0.5),(20180601,0.13,0.37,0.5),(20180701,0.13,0.37,0.5),(20180801,0.13,0.37,0.5),(20180901,0.13,0.37,0.5),(20181001,0.13,0.37,0.5),(20181101,0.13,0.37,0.5),(20181201,0.13,0.37,0.5),(20190101,0.13,0.37,0.5),(20190201,0.1,0.4,0.5),(20190301,0.1,0.4,0.5),(20190401,0.1,0.4,0.5),(20190501,0.1,0.4,0.5),(20190601,0.1,0.4,0.5),(20190701,0.1,0.4,0.5),(20190801,0.1,0.4,0.5),(20190901,0.1,0.4,0.5),(20191001,0.1,0.4,0.5),(20191101,0.1,0.4,0.5),(20191201,0.1,0.4,0.5),(20200101,0.1,0.4,0.5)] - self.yValueAxis.requiredRange = None - self.yValueAxis.leftAxisPercent = 0 - self.yValueAxis.leftAxisOrigShiftMin = 0 - self.yValueAxis.leftAxisOrigShiftIPC = 0 - self.lines[0].strokeColor = colors.toColor(0x0033cc) - self.lines[1].strokeColor = colors.toColor(0x99c3ff) - self.lines[2].strokeColor = colors.toColor(0xCC0033) - -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 - -class ScatterPlot(LinePlot): - """A scatter plot widget""" - - _attrMap = AttrMap(BASE=LinePlot, - width = AttrMapValue(isNumber, desc="Width of the area inside the axes"), - height = AttrMapValue(isNumber, desc="Height of the area inside the axes"), - outerBorderOn = AttrMapValue(isBoolean, desc="Is there an outer border (continuation of axes)"), - outerBorderColor = AttrMapValue(isColorOrNone, desc="Color of outer border (if any)"), - background = AttrMapValue(isColorOrNone, desc="Background color (if any)"), - labelOffset = AttrMapValue(isNumber, desc="Space between label and Axis (or other labels)"), - axisTickLengths = AttrMapValue(isNumber, desc="Lenth of the ticks on both axes"), - axisStrokeWidth = AttrMapValue(isNumber, desc="Stroke width for both axes"), - xLabel = AttrMapValue(isString, desc="Label for the whole X-Axis"), - yLabel = AttrMapValue(isString, desc="Label for the whole Y-Axis"), - data = AttrMapValue(isAnything, desc='Data points - a list of x/y tuples.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color used for border of plot area.'), - fillColor = AttrMapValue(isColorOrNone, desc='Color used for background interior of plot area.'), - leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'), - rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'), - topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'), - bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'), - ) - - def __init__(self): - LinePlot.__init__(self) - self.width = 142 - self.height = 77 - self.outerBorderOn = 1 - self.outerBorderColor = colors.black - self.background = None - - _labelOffset = 3 - _axisTickLengths = 2 - _axisStrokeWidth = 0.5 - - self.yValueAxis.valueMin = None - self.yValueAxis.valueMax = None - self.yValueAxis.valueStep = None - self.yValueAxis.labelTextFormat = '%s' - - self.xLabel="X Lable" - self.xValueAxis.labels.fontSize = 6 - - self.yLabel="Y Lable" - self.yValueAxis.labels.fontSize = 6 - - self.data =[((0.030, 62.73), - (0.074, 54.363), - (1.216, 17.964)), - - ((1.360, 11.621), - (1.387, 50.011), - (1.428, 68.953)), - - ((1.444, 86.888), - (1.754, 35.58), - (1.766, 36.05))] - - #values for lineplot - self.joinedLines = 0 - self.fillColor = self.background - - self.leftPadding=5 - self.rightPadding=10 - self.topPadding=5 - self.bottomPadding=5 - - self.x = self.leftPadding+_axisTickLengths+(_labelOffset*2) - self.x=self.x+_maxWidth(str(self.yValueAxis.valueMax), self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize) - self.y = self.bottomPadding+_axisTickLengths+_labelOffset+self.xValueAxis.labels.fontSize - - self.xValueAxis.labels.dy = -_labelOffset - self.xValueAxis.tickDown = _axisTickLengths - self.xValueAxis.strokeWidth = _axisStrokeWidth - self.xValueAxis.rangeRound='both' - self.yValueAxis.labels.dx = -_labelOffset - self.yValueAxis.tickLeft = _axisTickLengths - self.yValueAxis.strokeWidth = _axisStrokeWidth - self.yValueAxis.rangeRound='both' - - self.lineLabelFormat="%.2f" - self.lineLabels.fontSize = 5 - self.lineLabels.boxAnchor = 'e' - self.lineLabels.dx = -2 - self.lineLabelNudge = 0 - self.lines.symbol=makeMarker('FilledCircle',size=3) - self.lines[1].symbol=makeMarker('FilledDiamond',size=3) - self.lines[2].symbol=makeMarker('FilledSquare',size=3) - self.lines[2].strokeColor = colors.green - - def _getDrawingDimensions(self): - tx = self.leftPadding+self.yValueAxis.tickLeft+(self.yValueAxis.labels.dx*2)+self.xValueAxis.labels.fontSize - tx=tx+(5*_maxWidth(str(self.yValueAxis.valueMax), self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize)) - tx=tx+self.width+self.rightPadding - t=('%.2f%%'%self.xValueAxis.valueMax) - tx=tx+(_maxWidth(t, self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize)) - ty = self.bottomPadding+self.xValueAxis.tickDown+(self.xValueAxis.labels.dy*2)+(self.xValueAxis.labels.fontSize*2) - ty=ty+self.yValueAxis.labels.fontSize+self.height+self.topPadding - #print (tx, ty) - return (tx,ty) - - def demo(self,drawing=None): - if not drawing: - tx,ty=self._getDrawingDimensions() - drawing = Drawing(tx,ty) - drawing.add(self.draw()) - return drawing - - def draw(self): - ascent=getFont(self.xValueAxis.labels.fontName).face.ascent - if ascent==0: - ascent=0.718 # default (from helvetica) - ascent=ascent*self.xValueAxis.labels.fontSize # normalize - - #basic LinePlot - does the Axes, Ticks etc - lp = LinePlot.draw(self) - - xLabel = self.xLabel - if xLabel: #Overall label for the X-axis - xl=Label() - xl.x = (self.x+self.width)/2.0 - xl.y = 0 - xl.fontName = self.xValueAxis.labels.fontName - xl.fontSize = self.xValueAxis.labels.fontSize - xl.setText(xLabel) - lp.add(xl) - - yLabel = self.yLabel - if yLabel: #Overall label for the Y-axis - yl=Label() - yl.angle = 90 - yl.x = 0 - yl.y = (self.y+self.height/2.0) - yl.fontName = self.yValueAxis.labels.fontName - yl.fontSize = self.yValueAxis.labels.fontSize - yl.setText(yLabel) - lp.add(yl) - - # do a bounding box - in the same style as the axes - if self.outerBorderOn: - lp.add(Rect(self.x, self.y, self.width, self.height, - strokeColor = self.outerBorderColor, - strokeWidth = self.yValueAxis.strokeWidth, - fillColor = None)) - - lp.shift(self.leftPadding, self.bottomPadding) - - return lp - -def sample1a(): - "A line plot with non-equidistant points in x-axis." - - drawing = Drawing(400, 200) - - data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) - ] - - lp = LinePlot() - - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = data - lp.joinedLines = 1 - lp.strokeColor = colors.black - - lp.lines.symbol = makeMarker('UK_Flag') - - lp.lines[0].strokeWidth = 2 - lp.lines[1].strokeWidth = 4 - - lp.xValueAxis.valueMin = 0 - lp.xValueAxis.valueMax = 5 - lp.xValueAxis.valueStep = 1 - - lp.yValueAxis.valueMin = 0 - lp.yValueAxis.valueMax = 7 - lp.yValueAxis.valueStep = 1 - - drawing.add(lp) - - return drawing - - -def sample1b(): - "A line plot with non-equidistant points in x-axis." - - drawing = Drawing(400, 200) - - data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) - ] - - lp = LinePlot() - - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = data - lp.joinedLines = 1 - lp.lines.symbol = makeMarker('Circle') - lp.lineLabelFormat = '%2.0f' - lp.strokeColor = colors.black - - lp.xValueAxis.valueMin = 0 - lp.xValueAxis.valueMax = 5 - lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5] - lp.xValueAxis.labelTextFormat = '%2.1f' - - lp.yValueAxis.valueMin = 0 - lp.yValueAxis.valueMax = 7 - lp.yValueAxis.valueStep = 1 - - drawing.add(lp) - - return drawing - - -def sample1c(): - "A line plot with non-equidistant points in x-axis." - - drawing = Drawing(400, 200) - - data = [ - ((1,1), (2,2), (2.5,1), (3,3), (4,5)), - ((1,2), (2,3), (2.5,2), (3.5,5), (4,6)) - ] - - lp = LinePlot() - - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = data - lp.joinedLines = 1 - lp.lines[0].symbol = makeMarker('FilledCircle') - lp.lines[1].symbol = makeMarker('Circle') - lp.lineLabelFormat = '%2.0f' - lp.strokeColor = colors.black - - lp.xValueAxis.valueMin = 0 - lp.xValueAxis.valueMax = 5 - lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5] - lp.xValueAxis.labelTextFormat = '%2.1f' - - lp.yValueAxis.valueMin = 0 - lp.yValueAxis.valueMax = 7 - lp.yValueAxis.valueSteps = [1, 2, 3, 5, 6] - - drawing.add(lp) - - return drawing - - -def preprocessData(series): - "Convert date strings into seconds and multiply values by 100." - - return map(lambda x: (str2seconds(x[0]), x[1]*100), series) - - -def sample2(): - "A line plot with non-equidistant points in x-axis." - - drawing = Drawing(400, 200) - - data = [ - (('25/11/1991',1), - ('30/11/1991',1.000933333), - ('31/12/1991',1.0062), - ('31/01/1992',1.0112), - ('29/02/1992',1.0158), - ('31/03/1992',1.020733333), - ('30/04/1992',1.026133333), - ('31/05/1992',1.030266667), - ('30/06/1992',1.034466667), - ('31/07/1992',1.038733333), - ('31/08/1992',1.0422), - ('30/09/1992',1.045533333), - ('31/10/1992',1.049866667), - ('30/11/1992',1.054733333), - ('31/12/1992',1.061), - ), - ] - - data[0] = preprocessData(data[0]) - - lp = LinePlot() - - lp.x = 50 - lp.y = 50 - lp.height = 125 - lp.width = 300 - lp.data = data - lp.joinedLines = 1 - lp.lines.symbol = makeMarker('FilledDiamond') - lp.strokeColor = colors.black - - start = mktime(mkTimeTuple('25/11/1991')) - t0 = mktime(mkTimeTuple('30/11/1991')) - t1 = mktime(mkTimeTuple('31/12/1991')) - t2 = mktime(mkTimeTuple('31/03/1992')) - t3 = mktime(mkTimeTuple('30/06/1992')) - t4 = mktime(mkTimeTuple('30/09/1992')) - end = mktime(mkTimeTuple('31/12/1992')) - lp.xValueAxis.valueMin = start - lp.xValueAxis.valueMax = end - lp.xValueAxis.valueSteps = [start, t0, t1, t2, t3, t4, end] - lp.xValueAxis.labelTextFormat = seconds2str - lp.xValueAxis.labels[1].dy = -20 - lp.xValueAxis.labels[2].dy = -35 - - lp.yValueAxis.labelTextFormat = '%4.2f' - lp.yValueAxis.valueMin = 100 - lp.yValueAxis.valueMax = 110 - lp.yValueAxis.valueStep = 2 - - drawing.add(lp) - - return drawing diff --git a/bin/reportlab/graphics/charts/markers.py b/bin/reportlab/graphics/charts/markers.py deleted file mode 100644 index cb3493cb36b..00000000000 --- a/bin/reportlab/graphics/charts/markers.py +++ /dev/null @@ -1,81 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/markers.py -""" -This modules defines a collection of markers used in charts. - -The make* functions return a simple shape or a widget as for -the smiley. -""" -__version__=''' $Id: markers.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -from reportlab.lib import colors -from reportlab.graphics.shapes import Rect, Line, Circle, Polygon -from reportlab.graphics.widgets.signsandsymbols import SmileyFace - - -def makeEmptySquare(x, y, size, color): - "Make an empty square marker." - - d = size/2.0 - rect = Rect(x-d, y-d, 2*d, 2*d) - rect.strokeColor = color - rect.fillColor = None - - return rect - - -def makeFilledSquare(x, y, size, color): - "Make a filled square marker." - - d = size/2.0 - rect = Rect(x-d, y-d, 2*d, 2*d) - rect.strokeColor = color - rect.fillColor = color - - return rect - - -def makeFilledDiamond(x, y, size, color): - "Make a filled diamond marker." - - d = size/2.0 - poly = Polygon((x-d,y, x,y+d, x+d,y, x,y-d)) - poly.strokeColor = color - poly.fillColor = color - - return poly - - -def makeEmptyCircle(x, y, size, color): - "Make a hollow circle marker." - - d = size/2.0 - circle = Circle(x, y, d) - circle.strokeColor = color - circle.fillColor = colors.white - - return circle - - -def makeFilledCircle(x, y, size, color): - "Make a hollow circle marker." - - d = size/2.0 - circle = Circle(x, y, d) - circle.strokeColor = color - circle.fillColor = color - - return circle - - -def makeSmiley(x, y, size, color): - "Make a smiley marker." - - d = size - s = SmileyFace() - s.fillColor = color - s.x = x-d - s.y = y-d - s.size = d*2 - - return s \ No newline at end of file diff --git a/bin/reportlab/graphics/charts/piecharts.py b/bin/reportlab/graphics/charts/piecharts.py deleted file mode 100644 index f8646086b8e..00000000000 --- a/bin/reportlab/graphics/charts/piecharts.py +++ /dev/null @@ -1,1279 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/piecharts.py -# experimental pie chart script. Two types of pie - one is a monolithic -#widget with all top-level properties, the other delegates most stuff to -#a wedges collection whic lets you customize the group or every individual -#wedge. - -"""Basic Pie Chart class. - -This permits you to customize and pop out individual wedges; -supports elliptical and circular pies. -""" -__version__=''' $Id: piecharts.py 2743 2005-12-12 15:51:29Z rgbecker $ ''' - -import copy -from math import sin, cos, pi - -from reportlab.lib import colors -from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\ - isListOfNumbers, isColorOrNone, isString,\ - isListOfStringsOrNone, OneOf, SequenceOf,\ - isBoolean, isListOfColors, isNumberOrNone,\ - isNoneOrListOfNoneOrStrings, isTextAnchor,\ - isNoneOrListOfNoneOrNumbers, isBoxAnchor,\ - isStringOrNone, NoneOr -from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol -from reportlab.lib.attrmap import * -from reportlab.pdfgen.canvas import Canvas -from reportlab.graphics.shapes import Group, Drawing, Ellipse, Wedge, String, STATE_DEFAULTS, ArcPath, Polygon, Rect, PolyLine -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.charts.areas import PlotArea -from textlabels import Label - -_ANGLE2BOXANCHOR={0:'w', 45:'sw', 90:'s', 135:'se', 180:'e', 225:'ne', 270:'n', 315: 'nw', -45: 'nw'} -_ANGLE2RBOXANCHOR={0:'e', 45:'ne', 90:'n', 135:'nw', 180:'w', 225:'sw', 270:'s', 315: 'se', -45: 'se'} -class WedgeLabel(Label): - def _checkDXY(self,ba): - pass - def _getBoxAnchor(self): - na = (int((self._pmv%360)/45.)*45)%360 - if not (na % 90): # we have a right angle case - da = (self._pmv - na) % 360 - if abs(da)>5: - na = na + (da>0 and 45 or -45) - ba = (getattr(self,'_anti',None) and _ANGLE2RBOXANCHOR or _ANGLE2BOXANCHOR)[na] - self._checkDXY(ba) - return ba - -class WedgeProperties(PropHolder): - """This holds descriptive information about the wedges in a pie chart. - - It is not to be confused with the 'wedge itself'; this just holds - a recipe for how to format one, and does not allow you to hack the - angles. It can format a genuine Wedge object for you with its - format method. - """ - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - popout = AttrMapValue(isNumber), - fontName = AttrMapValue(isString), - fontSize = AttrMapValue(isNumber), - fontColor = AttrMapValue(isColorOrNone), - labelRadius = AttrMapValue(isNumber), - label_dx = AttrMapValue(isNumber), - label_dy = AttrMapValue(isNumber), - label_angle = AttrMapValue(isNumber), - label_boxAnchor = AttrMapValue(isBoxAnchor), - label_boxStrokeColor = AttrMapValue(isColorOrNone), - label_boxStrokeWidth = AttrMapValue(isNumber), - label_boxFillColor = AttrMapValue(isColorOrNone), - label_strokeColor = AttrMapValue(isColorOrNone), - label_strokeWidth = AttrMapValue(isNumber), - label_text = AttrMapValue(isStringOrNone), - label_leading = AttrMapValue(isNumberOrNone), - label_width = AttrMapValue(isNumberOrNone), - label_maxWidth = AttrMapValue(isNumberOrNone), - label_height = AttrMapValue(isNumberOrNone), - label_textAnchor = AttrMapValue(isTextAnchor), - label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"), - label_topPadding = AttrMapValue(isNumber,'padding at top of box'), - label_leftPadding = AttrMapValue(isNumber,'padding at left of box'), - label_rightPadding = AttrMapValue(isNumber,'padding at right of box'), - label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'), - label_pointer_strokeColor = AttrMapValue(isColorOrNone,desc='Color of indicator line'), - label_pointer_strokeWidth = AttrMapValue(isNumber,desc='StrokeWidth of indicator line'), - label_pointer_elbowLength = AttrMapValue(isNumber,desc='length of final indicator line segment'), - label_pointer_edgePad = AttrMapValue(isNumber,desc='pad between pointer label and box'), - label_pointer_piePad = AttrMapValue(isNumber,desc='pad between pointer label and pie'), - swatchMarker = AttrMapValue(NoneOr(isSymbol), desc="None or makeMarker('Diamond') ..."), - ) - - def __init__(self): - self.strokeWidth = 0 - self.fillColor = None - self.strokeColor = STATE_DEFAULTS["strokeColor"] - self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"] - self.popout = 0 - self.fontName = STATE_DEFAULTS["fontName"] - self.fontSize = STATE_DEFAULTS["fontSize"] - self.fontColor = STATE_DEFAULTS["fillColor"] - self.labelRadius = 1.2 - self.label_dx = self.label_dy = self.label_angle = 0 - self.label_text = None - self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0 - self.label_boxAnchor = 'c' - self.label_boxStrokeColor = None #boxStroke - self.label_boxStrokeWidth = 0.5 #boxStrokeWidth - self.label_boxFillColor = None - self.label_strokeColor = None - self.label_strokeWidth = 0.1 - self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None - self.label_textAnchor = 'start' - self.label_visible = 1 - self.label_pointer_strokeColor = colors.black - self.label_pointer_strokeWidth = 0.5 - self.label_pointer_elbowLength = 3 - self.label_pointer_edgePad = 2 - self.label_pointer_piePad = 3 - -def _addWedgeLabel(self,text,add,angle,labelX,labelY,wedgeStyle,labelClass=WedgeLabel): - # now draw a label - if self.simpleLabels: - theLabel = String(labelX, labelY, text) - theLabel.textAnchor = "middle" - theLabel._pmv = angle - else: - theLabel = labelClass() - theLabel._pmv = angle - theLabel.x = labelX - theLabel.y = labelY - theLabel.dx = wedgeStyle.label_dx - theLabel.dy = wedgeStyle.label_dy - theLabel.angle = wedgeStyle.label_angle - theLabel.boxAnchor = wedgeStyle.label_boxAnchor - theLabel.boxStrokeColor = wedgeStyle.label_boxStrokeColor - theLabel.boxStrokeWidth = wedgeStyle.label_boxStrokeWidth - theLabel.boxFillColor = wedgeStyle.label_boxFillColor - theLabel.strokeColor = wedgeStyle.label_strokeColor - theLabel.strokeWidth = wedgeStyle.label_strokeWidth - _text = wedgeStyle.label_text - if _text is None: _text = text - theLabel._text = _text - theLabel.leading = wedgeStyle.label_leading - theLabel.width = wedgeStyle.label_width - theLabel.maxWidth = wedgeStyle.label_maxWidth - theLabel.height = wedgeStyle.label_height - theLabel.textAnchor = wedgeStyle.label_textAnchor - theLabel.visible = wedgeStyle.label_visible - theLabel.topPadding = wedgeStyle.label_topPadding - theLabel.leftPadding = wedgeStyle.label_leftPadding - theLabel.rightPadding = wedgeStyle.label_rightPadding - theLabel.bottomPadding = wedgeStyle.label_bottomPadding - theLabel.fontSize = wedgeStyle.fontSize - theLabel.fontName = wedgeStyle.fontName - theLabel.fillColor = wedgeStyle.fontColor - add(theLabel) - -def _fixLabels(labels,n): - if labels is None: - labels = [''] * n - else: - i = n-len(labels) - if i>0: labels = labels + ['']*i - return labels - -class AbstractPieChart(PlotArea): - - def makeSwatchSample(self, rowNo, x, y, width, height): - baseStyle = self.slices - styleIdx = rowNo % len(baseStyle) - style = baseStyle[styleIdx] - strokeColor = getattr(style, 'strokeColor', getattr(baseStyle,'strokeColor',None)) - fillColor = getattr(style, 'fillColor', getattr(baseStyle,'fillColor',None)) - strokeDashArray = getattr(style, 'strokeDashArray', getattr(baseStyle,'strokeDashArray',None)) - strokeWidth = getattr(style, 'strokeWidth', getattr(baseStyle, 'strokeWidth',None)) - swatchMarker = getattr(style, 'swatchMarker', getattr(baseStyle, 'swatchMarker',None)) - if swatchMarker: - return uSymbol2Symbol(swatchMarker,x+width/2.,y+height/2.,fillColor) - return Rect(x,y,width,height,strokeWidth=strokeWidth,strokeColor=strokeColor, - strokeDashArray=strokeDashArray,fillColor=fillColor) - - def getSeriesName(self,i,default=None): - '''return series name i or default''' - try: - text = str(self.labels[i]) - except: - text = default - if not self.simpleLabels: - _text = getattr(self.slices[i],'label_text','') - if _text is not None: text = _text - return text - -def boundsOverlap(P,Q): - return not(P[0]>Q[2]-1e-2 or Q[0]>P[2]-1e-2 or P[1]>Q[3]-1e-2 or Q[1]>P[3]-1e-2) - -def _findOverlapRun(B,i,wrap): - '''find overlap run containing B[i]''' - n = len(B) - R = [i] - while 1: - i = R[-1] - j = (i+1)%n - if j in R or not boundsOverlap(B[i],B[j]): break - R.append(j) - while 1: - i = R[0] - j = (i-1)%n - if j in R or not boundsOverlap(B[i],B[j]): break - R.insert(0,j) - return R - -def findOverlapRun(B,wrap=1): - '''determine a set of overlaps in bounding boxes B or return None''' - n = len(B) - if n>1: - for i in xrange(n-1): - R = _findOverlapRun(B,i,wrap) - if len(R)>1: return R - return None - -def fixLabelOverlaps(L): - nL = len(L) - if nL<2: return - B = [l._origdata['bounds'] for l in L] - OK = 1 - RP = [] - iter = 0 - mult = 1. - - while iter<30: - R = findOverlapRun(B) - if not R: break - nR = len(R) - if nR==nL: break - if not [r for r in RP if r in R]: - mult = 1.0 - da = 0 - r0 = R[0] - rL = R[-1] - bi = B[r0] - taa = aa = _360(L[r0]._pmv) - for r in R[1:]: - b = B[r] - da = max(da,min(b[3]-bi[1],bi[3]-b[1])) - bi = b - aa += L[r]._pmv - aa = aa/float(nR) - utaa = abs(L[rL]._pmv-taa) - ntaa = _360(utaa) - da *= mult*(nR-1)/ntaa - - for r in R: - l = L[r] - orig = l._origdata - angle = l._pmv = _360(l._pmv+da*(_360(l._pmv)-aa)) - rad = angle/_180_pi - l.x = orig['cx'] + orig['rx']*cos(rad) - l.y = orig['cy'] + orig['ry']*sin(rad) - B[r] = l.getBounds() - RP = R - mult *= 1.05 - iter += 1 - -def intervalIntersection(A,B): - x,y = max(min(A),min(B)),min(max(A),max(B)) - if x>=y: return None - return x,y - -def _makeSideArcDefs(sa,direction): - sa %= 360 - if 90<=sa<270: - if direction=='clockwise': - a = (0,90,sa),(1,-90,90),(0,-360+sa,-90) - else: - a = (0,sa,270),(1,270,450),(0,450,360+sa) - else: - offs = sa>=270 and 360 or 0 - if direction=='clockwise': - a = (1,offs-90,sa),(0,offs-270,offs-90),(1,-360+sa,offs-270) - else: - 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]1: a.sort(lambda x,y: cmp(y[1]-y[0],x[1]-x[0])) - return a[0] - -def _fPLSide(l,width,side=None): - data = l._origdata - if side is None: - li = data['li'] - ri = data['ri'] - if li is None: - side = 1 - i = ri - elif ri is None: - side = 0 - i = li - elif li[1]-li[0]>ri[1]-ri[0]: - side = 0 - i = li - else: - side = 1 - i = ri - w = data['width'] - edgePad = data['edgePad'] - if not side: #on left - l._pmv = 180 - l.x = edgePad+w - i = data['li'] - else: - l._pmv = 0 - l.x = width - w - edgePad - i = data['ri'] - mid = data['mid'] = (i[0]+i[1])*0.5 - data['smid'] = sin(mid/_180_pi) - data['cmid'] = cos(mid/_180_pi) - data['side'] = side - return side,w - -def _fPLCF(a,b): - return cmp(b._origdata['smid'],a._origdata['smid']) - -def _arcCF(a,b): - return cmp(a[1],b[1]) - -def _fixPointerLabels(n,L,x,y,width,height,side=None): - LR = [],[] - mlr = [0,0] - for l in L: - i,w = _fPLSide(l,width,side) - LR[i].append(l) - mlr[i] = max(w,mlr[i]) - mul = 1 - G = n*[None] - mel = 0 - hh = height*0.5 - yhh = y+hh - m = max(mlr) - for i in (0,1): - T = LR[i] - if T: - B = [] - aB = B.append - S = [] - aS = S.append - T.sort(_fPLCF) - p = 0 - yh = y+height - for l in T: - data = l._origdata - inc = x+mul*(m-data['width']) - l.x += inc - G[data['index']] = l - ly = yhh+data['smid']*hh - b = data['bounds'] - b2 = (b[3]-b[1])*0.5 - if ly+b2>yh: ly = yh-b2 - if ly-b2sFree: break - yh = B[j0][3]+sAbove*sNeed/sFree - for r in R: - l = T[r] - data = l._origdata - b = data['bounds'] - b2 = (b[3]-b[1])*0.5 - yh -= 0.5 - ly = l.y = yh-b2 - B[r] = data['bounds'] = (b[0],ly-b2,b[2],yh) - yh = ly - b2 - 0.5 - mlr[i] = m+p - mul = -1 - return G, mlr[0], mlr[1], mel - -class Pie(AbstractPieChart): - _attrMap = AttrMap(BASE=AbstractPieChart, - data = AttrMapValue(isListOfNumbers, desc='list of numbers defining wedge sizes; need not sum to 1'), - labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"), - startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"), - direction = AttrMapValue(OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"), - slices = AttrMapValue(None, desc="collection of wedge descriptor objects"), - simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel"), - other_threshold = AttrMapValue(isNumber, desc='A value for doing threshholding, not used yet.'), - checkLabelOverlap = AttrMapValue(isBoolean, desc="If true check and attempt to fix standard label overlaps(default off)"), - pointerLabelMode = AttrMapValue(OneOf(None,'LeftRight','LeftAndRight'), desc=""), - sameRadii = AttrMapValue(isBoolean, desc="If true make x/y radii the same(default off)"), - orderMode = AttrMapValue(OneOf('fixed','alternate')), - xradius = AttrMapValue(isNumberOrNone, desc="X direction Radius"), - yradius = AttrMapValue(isNumberOrNone, desc="Y direction Radius"), - ) - other_threshold=None - - def __init__(self,**kwd): - PlotArea.__init__(self) - self.x = 0 - self.y = 0 - self.width = 100 - self.height = 100 - self.data = [1,2.3,1.7,4.2] - self.labels = None # or list of strings - self.startAngle = 90 - self.direction = "clockwise" - self.simpleLabels = 1 - self.checkLabelOverlap = 0 - self.pointerLabelMode = None - self.sameRadii = False - self.orderMode = 'fixed' - self.xradius = self.yradius = None - - self.slices = TypedPropertyCollection(WedgeProperties) - self.slices[0].fillColor = colors.darkcyan - self.slices[1].fillColor = colors.blueviolet - self.slices[2].fillColor = colors.blue - self.slices[3].fillColor = colors.cyan - self.slices[4].fillColor = colors.pink - self.slices[5].fillColor = colors.magenta - self.slices[6].fillColor = colors.yellow - - def demo(self): - d = Drawing(200, 100) - - pc = Pie() - pc.x = 50 - pc.y = 10 - pc.width = 100 - pc.height = 80 - pc.data = [10,20,30,40,50,60] - pc.labels = ['a','b','c','d','e','f'] - - pc.slices.strokeWidth=0.5 - pc.slices[3].popout = 10 - pc.slices[3].strokeWidth = 2 - pc.slices[3].strokeDashArray = [2,2] - pc.slices[3].labelRadius = 1.75 - pc.slices[3].fontColor = colors.red - pc.slices[0].fillColor = colors.darkcyan - pc.slices[1].fillColor = colors.blueviolet - pc.slices[2].fillColor = colors.blue - pc.slices[3].fillColor = colors.cyan - pc.slices[4].fillColor = colors.aquamarine - pc.slices[5].fillColor = colors.cadetblue - pc.slices[6].fillColor = colors.lightcoral - - d.add(pc) - return d - - def makePointerLabels(self,angles,plMode): - class PL: - def __init__(self,centerx,centery,xradius,yradius,data,lu=0,ru=0): - self.centerx = centerx - self.centery = centery - self.xradius = xradius - self.yradius = yradius - self.data = data - self.lu = lu - self.ru = ru - - labelX = self.width-2 - labelY = self.height - n = nr = nl = maxW = sumH = 0 - styleCount = len(self.slices) - L=[] - L_add = L.append - refArcs = _makeSideArcDefs(self.startAngle,self.direction) - for i, A in angles: - if A[1] is None: continue - sn = self.getSeriesName(i,'') - if not sn: continue - n += 1 - style = self.slices[i%styleCount] - _addWedgeLabel(self,sn,L_add,180,labelX,labelY,style,labelClass=WedgeLabel) - l = L[-1] - b = l.getBounds() - w = b[2]-b[0] - h = b[3]-b[1] - ri = [(a[0],intervalIntersection(A,(a[1],a[2]))) for a in refArcs] - li = _findLargestArc(ri,0) - ri = _findLargestArc(ri,1) - if li and ri: - if plMode=='LeftAndRight': - if li[1]-li[0]ri[1]-ri[0]: - ri = None - if ri: nr += 1 - if li: nl += 1 - l._origdata = dict(bounds=b,width=w,height=h,li=li,ri=ri,index=i,edgePad=style.label_pointer_edgePad,piePad=style.label_pointer_piePad,elbowLength=style.label_pointer_elbowLength) - maxW = max(w,maxW) - sumH += h+2 - - if not n: #we have no labels - xradius = self.width*0.5 - yradius = self.height*0.5 - centerx = self.x+xradius - centery = self.y+yradius - if self.xradius: xradius = self.xradius - if self.yradius: yradius = self.yradius - if self.sameRadii: xradius=yradius=min(xradius,yradius) - return PL(centerx,centery,xradius,yradius,[]) - - aonR = nr==n - if sumH=1e-8 and map(lambda x,f=360./sum: f*x, data) or len(data)*[0] - - def makeAngles(self): - startAngle = self.startAngle % 360 - whichWay = self.direction == "clockwise" and -1 or 1 - D = [a for a in enumerate(self.normalizeData())] - if self.orderMode=='alternate': - W = [a for a in D if abs(a[1])>=1e-5] - W.sort(_arcCF) - T = [[],[]] - i = 0 - while W: - if i<2: - a = W.pop(0) - else: - a = W.pop(-1) - T[i%2].append(a) - i += 1 - i %= 4 - T[1].reverse() - D = T[0]+T[1] + [a for a in D if abs(a[1])<1e-5] - A = [] - a = A.append - for i, angle in D: - endAngle = (startAngle + (angle * whichWay)) - if abs(angle)>=1e-5: - if startAngle >= endAngle: - aa = endAngle,startAngle - else: - aa = startAngle,endAngle - else: - aa = startAngle, None - startAngle = endAngle - a((i,aa)) - return A - - def makeWedges(self): - angles = self.makeAngles() - n = len(angles) - labels = _fixLabels(self.labels,n) - - self._seriesCount = n - styleCount = len(self.slices) - - plMode = self.pointerLabelMode - if plMode: - checkLabelOverlap = False - PL=self.makePointerLabels(angles,plMode) - xradius = PL.xradius - yradius = PL.yradius - centerx = PL.centerx - centery = PL.centery - PL_data = PL.data - gSN = lambda i: '' - else: - xradius = self.width*0.5 - yradius = self.height*0.5 - centerx = self.x + xradius - centery = self.y + yradius - if self.xradius: xradius = self.xradius - if self.yradius: yradius = self.yradius - if self.sameRadii: xradius=yradius=min(xradius,yradius) - checkLabelOverlap = self.checkLabelOverlap - gSN = lambda i: self.getSeriesName(i,'') - - g = Group() - g_add = g.add - if checkLabelOverlap: - L = [] - L_add = L.append - else: - L_add = g_add - - for i,(a1,a2) in angles: - if a2 is None: continue - #if we didn't use %stylecount here we'd end up with the later wedges - #all having the default style - wedgeStyle = self.slices[i%styleCount] - - # is it a popout? - cx, cy = centerx, centery - text = gSN(i) - popout = wedgeStyle.popout - if text or popout: - averageAngle = (a1+a2)/2.0 - aveAngleRadians = averageAngle/_180_pi - cosAA = cos(aveAngleRadians) - sinAA = sin(aveAngleRadians) - if popout: - # pop out the wedge - cx = centerx + popout*cosAA - cy = centery + popout*sinAA - - if n > 1: - theWedge = Wedge(cx, cy, xradius, a1, a2, yradius=yradius) - elif n==1: - theWedge = Ellipse(cx, cy, xradius, yradius) - - theWedge.fillColor = wedgeStyle.fillColor - theWedge.strokeColor = wedgeStyle.strokeColor - theWedge.strokeWidth = wedgeStyle.strokeWidth - theWedge.strokeDashArray = wedgeStyle.strokeDashArray - - g_add(theWedge) - if text: - labelRadius = wedgeStyle.labelRadius - rx = xradius*labelRadius - ry = yradius*labelRadius - labelX = cx + rx*cosAA - labelY = cy + ry*sinAA - _addWedgeLabel(self,text,L_add,averageAngle,labelX,labelY,wedgeStyle) - if checkLabelOverlap: - l = L[-1] - l._origdata = { 'x': labelX, 'y':labelY, 'angle': averageAngle, - 'rx': rx, 'ry':ry, 'cx':cx, 'cy':cy, - 'bounds': l.getBounds(), - } - elif plMode and PL_data: - l = PL_data[i] - if l: - data = l._origdata - sinM = data['smid'] - cosM = data['cmid'] - lX = cx + xradius*cosM - lY = cy + yradius*sinM - lpel = wedgeStyle.label_pointer_elbowLength - lXi = lX + lpel*cosM - lYi = lY + lpel*sinM - L_add(PolyLine((lX,lY,lXi,lYi,l.x,l.y), - strokeWidth=wedgeStyle.label_pointer_strokeWidth, - strokeColor=wedgeStyle.label_pointer_strokeColor)) - L_add(l) - - if checkLabelOverlap: - fixLabelOverlaps(L) - map(g_add,L) - - return g - - def draw(self): - G = self.makeBackground() - w = self.makeWedges() - if G: return Group(G,w) - return w - -class LegendedPie(Pie): - """Pie with a two part legend (one editable with swatches, one hidden without swatches).""" - - _attrMap = AttrMap(BASE=Pie, - drawLegend = AttrMapValue(isBoolean, desc="If true then create and draw legend"), - legend1 = AttrMapValue(None, desc="Handle to legend for pie"), - legendNumberFormat = AttrMapValue(None, desc="Formatting routine for number on right hand side of legend."), - legendNumberOffset = AttrMapValue(isNumber, desc="Horizontal space between legend and numbers on r/hand side"), - pieAndLegend_colors = AttrMapValue(isListOfColors, desc="Colours used for both swatches and pie"), - legend_names = AttrMapValue(isNoneOrListOfNoneOrStrings, desc="Names used in legend (or None)"), - legend_data = AttrMapValue(isNoneOrListOfNoneOrNumbers, desc="Numbers used on r/hand side of legend (or None)"), - leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'), - rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'), - topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'), - bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'), - ) - - def __init__(self): - Pie.__init__(self) - self.x = 0 - self.y = 0 - self.height = 100 - self.width = 100 - self.data = [38.4, 20.7, 18.9, 15.4, 6.6] - self.labels = None - self.direction = 'clockwise' - PCMYKColor, black = colors.PCMYKColor, colors.black - self.pieAndLegend_colors = [PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV'), - PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV'), - PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=75), - PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=75), - PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=50), - PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=50)] - - #Allows us up to six 'wedges' to be coloured - self.slices[0].fillColor=self.pieAndLegend_colors[0] - self.slices[1].fillColor=self.pieAndLegend_colors[1] - self.slices[2].fillColor=self.pieAndLegend_colors[2] - self.slices[3].fillColor=self.pieAndLegend_colors[3] - self.slices[4].fillColor=self.pieAndLegend_colors[4] - self.slices[5].fillColor=self.pieAndLegend_colors[5] - - self.slices.strokeWidth = 0.75 - self.slices.strokeColor = black - - legendOffset = 17 - self.legendNumberOffset = 51 - self.legendNumberFormat = '%.1f%%' - self.legend_data = self.data - - #set up the legends - from reportlab.graphics.charts.legends import Legend - self.legend1 = Legend() - self.legend1.x = self.width+legendOffset - self.legend1.y = self.height - self.legend1.deltax = 5.67 - self.legend1.deltay = 14.17 - self.legend1.dxTextSpace = 11.39 - self.legend1.dx = 5.67 - self.legend1.dy = 5.67 - self.legend1.columnMaximum = 7 - self.legend1.alignment = 'right' - self.legend_names = ['AAA:','AA:','A:','BBB:','NR:'] - for f in range(0,len(self.data)): - self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f])) - self.legend1.fontName = "Helvetica-Bold" - self.legend1.fontSize = 6 - self.legend1.strokeColor = black - self.legend1.strokeWidth = 0.5 - - self._legend2 = Legend() - self._legend2.dxTextSpace = 0 - self._legend2.dx = 0 - self._legend2.alignment = 'right' - self._legend2.fontName = "Helvetica-Oblique" - self._legend2.fontSize = 6 - self._legend2.strokeColor = self.legend1.strokeColor - - self.leftPadding = 5 - self.rightPadding = 5 - self.topPadding = 5 - self.bottomPadding = 5 - self.drawLegend = 1 - - def draw(self): - if self.drawLegend: - self.legend1.colorNamePairs = [] - self._legend2.colorNamePairs = [] - for f in range(0,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)) - else: - try: - self.slices[f].fillColor = self.pieAndLegend_colors[f] - self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f])) - except IndexError: - self.slices[f].fillColor = self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)] - self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)], self.legend_names[f])) - 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: - ldf = lNF % ldf - elif callable(lNF): - 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 callable(lNF): - ldf = lNF(ldf) - else: - msg = "Unknown formatter type %s, expected string or function" % self.legendNumberFormat - raise Exception, msg - self._legend2.colorNamePairs.append((None,ldf)) - p = Pie.draw(self) - if self.drawLegend: - p.add(self.legend1) - #hide from user - keeps both sides lined up! - self._legend2.x = self.legend1.x+self.legendNumberOffset - self._legend2.y = self.legend1.y - self._legend2.deltax = self.legend1.deltax - self._legend2.deltay = self.legend1.deltay - self._legend2.dy = self.legend1.dy - self._legend2.columnMaximum = self.legend1.columnMaximum - p.add(self._legend2) - p.shift(self.leftPadding, self.bottomPadding) - return p - - def _getDrawingDimensions(self): - tx = self.rightPadding - if self.drawLegend: - tx = tx+self.legend1.x+self.legendNumberOffset #self._legend2.x - tx = tx + self._legend2._calculateMaxWidth(self._legend2.colorNamePairs) - ty = self.bottomPadding+self.height+self.topPadding - return (tx,ty) - - def demo(self, drawing=None): - if not drawing: - tx,ty = self._getDrawingDimensions() - drawing = Drawing(tx, ty) - drawing.add(self.draw()) - return drawing - -from utils3d import _getShaded, _2rad, _360, _pi_2, _2pi, _180_pi -class Wedge3dProperties(PropHolder): - """This holds descriptive information about the wedges in a pie chart. - - It is not to be confused with the 'wedge itself'; this just holds - a recipe for how to format one, and does not allow you to hack the - angles. It can format a genuine Wedge object for you with its - format method. - """ - _attrMap = AttrMap( - fillColor = AttrMapValue(isColorOrNone), - fillColorShaded = AttrMapValue(isColorOrNone), - fontColor = AttrMapValue(isColorOrNone), - fontName = AttrMapValue(isString), - fontSize = AttrMapValue(isNumber), - label_angle = AttrMapValue(isNumber), - label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'), - label_boxAnchor = AttrMapValue(isBoxAnchor), - label_boxFillColor = AttrMapValue(isColorOrNone), - label_boxStrokeColor = AttrMapValue(isColorOrNone), - label_boxStrokeWidth = AttrMapValue(isNumber), - label_dx = AttrMapValue(isNumber), - label_dy = AttrMapValue(isNumber), - label_height = AttrMapValue(isNumberOrNone), - label_leading = AttrMapValue(isNumberOrNone), - label_leftPadding = AttrMapValue(isNumber,'padding at left of box'), - label_maxWidth = AttrMapValue(isNumberOrNone), - label_rightPadding = AttrMapValue(isNumber,'padding at right of box'), - label_strokeColor = AttrMapValue(isColorOrNone), - label_strokeWidth = AttrMapValue(isNumber), - label_text = AttrMapValue(isStringOrNone), - label_textAnchor = AttrMapValue(isTextAnchor), - label_topPadding = AttrMapValue(isNumber,'padding at top of box'), - label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"), - label_width = AttrMapValue(isNumberOrNone), - labelRadius = AttrMapValue(isNumber), - popout = AttrMapValue(isNumber), - shading = AttrMapValue(isNumber), - strokeColor = AttrMapValue(isColorOrNone), - strokeColorShaded = AttrMapValue(isColorOrNone), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - strokeWidth = AttrMapValue(isNumber), - visible = AttrMapValue(isBoolean,'set to false to skip displaying'), - ) - - def __init__(self): - self.strokeWidth = 0 - self.shading = 0.3 - self.visible = 1 - self.strokeColorShaded = self.fillColorShaded = self.fillColor = None - self.strokeColor = STATE_DEFAULTS["strokeColor"] - self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"] - self.popout = 0 - self.fontName = STATE_DEFAULTS["fontName"] - self.fontSize = STATE_DEFAULTS["fontSize"] - self.fontColor = STATE_DEFAULTS["fillColor"] - self.labelRadius = 1.2 - self.label_dx = self.label_dy = self.label_angle = 0 - self.label_text = None - self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0 - self.label_boxAnchor = 'c' - self.label_boxStrokeColor = None #boxStroke - self.label_boxStrokeWidth = 0.5 #boxStrokeWidth - self.label_boxFillColor = None - self.label_strokeColor = None - self.label_strokeWidth = 0.1 - self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None - self.label_textAnchor = 'start' - self.label_visible = 1 - -class _SL3D: - def __init__(self,lo,hi): - if lo<0: - lo += 360 - hi += 360 - self.lo = lo - self.hi = hi - self.mid = (lo+hi)*0.5 - - def __str__(self): - return '_SL3D(%.2f,%.2f)' % (self.lo,self.hi) - -_270r = _2rad(270) -class Pie3d(Pie): - _attrMap = AttrMap(BASE=Pie, - perspective = AttrMapValue(isNumber, desc='A flattening parameter.'), - depth_3d = AttrMapValue(isNumber, desc='depth of the pie.'), - angle_3d = AttrMapValue(isNumber, desc='The view angle.'), - ) - perspective = 70 - depth_3d = 25 - angle_3d = 180 - - def _popout(self,i): - return self.slices[i].popout or 0 - - def CX(self, i,d ): - return self._cx+(d and self._xdepth_3d or 0)+self._popout(i)*cos(_2rad(self._sl3d[i].mid)) - def CY(self,i,d): - return self._cy+(d and self._ydepth_3d or 0)+self._popout(i)*sin(_2rad(self._sl3d[i].mid)) - def OX(self,i,o,d): - return self.CX(i,d)+self._radiusx*cos(_2rad(o)) - def OY(self,i,o,d): - return self.CY(i,d)+self._radiusy*sin(_2rad(o)) - - def rad_dist(self,a): - _3dva = self._3dva - return min(abs(a-_3dva),abs(a-_3dva+360)) - - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 300 - self.height = 200 - self.data = [12.50,20.10,2.00,22.00,5.00,18.00,13.00] - self.labels = None # or list of strings - self.startAngle = 90 - self.direction = "clockwise" - self.simpleLabels = 1 - self.slices = TypedPropertyCollection(Wedge3dProperties) - self.slices[0].fillColor = colors.darkcyan - self.slices[1].fillColor = colors.blueviolet - self.slices[2].fillColor = colors.blue - self.slices[3].fillColor = colors.cyan - self.slices[4].fillColor = colors.azure - self.slices[5].fillColor = colors.crimson - self.slices[6].fillColor = colors.darkviolet - self.checkLabelOverlap = 0 - - def _fillSide(self,L,i,angle,strokeColor,strokeWidth,fillColor): - rd = self.rad_dist(angle) - if rd0: angle0, angle1 = angle1, angle0 - _sl3d.append(_SL3D(angle0,angle1)) - - labels = _fixLabels(self.labels,n) - a0 = _3d_angle - a1 = _3d_angle+180 - T = [] - S = [] - L = [] - - class WedgeLabel3d(WedgeLabel): - _ydepth_3d = self._ydepth_3d - def _checkDXY(self,ba): - if ba[0]=='n': - if not hasattr(self,'_ody'): - self._ody = self.dy - self.dy = -self._ody + self._ydepth_3d - - checkLabelOverlap = self.checkLabelOverlap - - for i in xrange(n): - style = slices[i] - if not style.visible: continue - sl = _sl3d[i] - lo = angle0 = sl.lo - hi = angle1 = sl.hi - if abs(hi-lo)<=1e-7: continue - fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading) - strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor - strokeWidth = style.strokeWidth - cx0 = CX(i,0) - cy0 = CY(i,0) - cx1 = CX(i,1) - cy1 = CY(i,1) - #background shaded pie bottom - g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy, - strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, - strokeLineJoin=1)) - #connect to top - if lo < a0 < hi: angle0 = a0 - if lo < a1 < hi: angle1 = a1 - if 1: - p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1) - p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1) - p.lineTo(OX(i,angle1,0),OY(i,angle1,0)) - p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1) - p.closePath() - if angle0<=_3dva and angle1>=_3dva: - rd = 0 - else: - rd = min(rad_dist(angle0),rad_dist(angle1)) - S.append((rd,p)) - _fillSide(S,i,lo,strokeColor,strokeWidth,fillColor) - _fillSide(S,i,hi,strokeColor,strokeWidth,fillColor) - - #bright shaded top - fillColor = style.fillColor - strokeColor = style.strokeColor or fillColor - T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy, - strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1)) - - text = labels[i] - if text: - rat = style.labelRadius - self._radiusx *= rat - self._radiusy *= rat - mid = sl.mid - labelX = OX(i,mid,0) - labelY = OY(i,mid,0) - _addWedgeLabel(self,text,L.append,mid,labelX,labelY,style,labelClass=WedgeLabel3d) - if checkLabelOverlap: - l = L[-1] - l._origdata = { 'x': labelX, 'y':labelY, 'angle': mid, - 'rx': self._radiusx, 'ry':self._radiusy, 'cx':CX(i,0), 'cy':CY(i,0), - 'bounds': l.getBounds(), - } - self._radiusx = radiusx - self._radiusy = radiusy - - S.sort(lambda a,b: -cmp(a[0],b[0])) - if checkLabelOverlap: - fixLabelOverlaps(L) - map(g.add,map(lambda x:x[1],S)+T+L) - return g - - def demo(self): - d = Drawing(200, 100) - - pc = Pie() - pc.x = 50 - pc.y = 10 - pc.width = 100 - pc.height = 80 - pc.data = [10,20,30,40,50,60] - pc.labels = ['a','b','c','d','e','f'] - - pc.slices.strokeWidth=0.5 - pc.slices[3].popout = 10 - pc.slices[3].strokeWidth = 2 - pc.slices[3].strokeDashArray = [2,2] - pc.slices[3].labelRadius = 1.75 - pc.slices[3].fontColor = colors.red - pc.slices[0].fillColor = colors.darkcyan - pc.slices[1].fillColor = colors.blueviolet - pc.slices[2].fillColor = colors.blue - pc.slices[3].fillColor = colors.cyan - pc.slices[4].fillColor = colors.aquamarine - pc.slices[5].fillColor = colors.cadetblue - pc.slices[6].fillColor = colors.lightcoral - self.slices[1].visible = 0 - self.slices[3].visible = 1 - self.slices[4].visible = 1 - self.slices[5].visible = 1 - self.slices[6].visible = 0 - - d.add(pc) - return d - - -def sample0a(): - "Make a degenerated pie chart with only one slice." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 150 - pc.y = 50 - pc.data = [10] - pc.labels = ['a'] - pc.slices.strokeWidth=1#0.5 - - d.add(pc) - - return d - - -def sample0b(): - "Make a degenerated pie chart with only one slice." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 150 - pc.y = 50 - pc.width = 120 - pc.height = 100 - pc.data = [10] - pc.labels = ['a'] - pc.slices.strokeWidth=1#0.5 - - d.add(pc) - - return d - - -def sample1(): - "Make a typical pie chart with with one slice treated in a special way." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 150 - pc.y = 50 - pc.data = [10, 20, 30, 40, 50, 60] - pc.labels = ['a', 'b', 'c', 'd', 'e', 'f'] - - pc.slices.strokeWidth=1#0.5 - pc.slices[3].popout = 20 - pc.slices[3].strokeWidth = 2 - pc.slices[3].strokeDashArray = [2,2] - pc.slices[3].labelRadius = 1.75 - pc.slices[3].fontColor = colors.red - - d.add(pc) - - return d - - -def sample2(): - "Make a pie chart with nine slices." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 125 - pc.y = 25 - pc.data = [0.31, 0.148, 0.108, - 0.076, 0.033, 0.03, - 0.019, 0.126, 0.15] - pc.labels = ['1', '2', '3', '4', '5', '6', '7', '8', 'X'] - - pc.width = 150 - pc.height = 150 - pc.slices.strokeWidth=1#0.5 - - pc.slices[0].fillColor = colors.steelblue - pc.slices[1].fillColor = colors.thistle - pc.slices[2].fillColor = colors.cornflower - pc.slices[3].fillColor = colors.lightsteelblue - pc.slices[4].fillColor = colors.aquamarine - pc.slices[5].fillColor = colors.cadetblue - pc.slices[6].fillColor = colors.lightcoral - pc.slices[7].fillColor = colors.tan - pc.slices[8].fillColor = colors.darkseagreen - - d.add(pc) - - return d - - -def sample3(): - "Make a pie chart with a very slim slice." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 125 - pc.y = 25 - - pc.data = [74, 1, 25] - - pc.width = 150 - pc.height = 150 - pc.slices.strokeWidth=1#0.5 - pc.slices[0].fillColor = colors.steelblue - pc.slices[1].fillColor = colors.thistle - pc.slices[2].fillColor = colors.cornflower - - d.add(pc) - - return d - - -def sample4(): - "Make a pie chart with several very slim slices." - - d = Drawing(400, 200) - - pc = Pie() - pc.x = 125 - pc.y = 25 - - pc.data = [74, 1, 1, 1, 1, 22] - - pc.width = 150 - pc.height = 150 - pc.slices.strokeWidth=1#0.5 - pc.slices[0].fillColor = colors.steelblue - pc.slices[1].fillColor = colors.thistle - pc.slices[2].fillColor = colors.cornflower - pc.slices[3].fillColor = colors.lightsteelblue - pc.slices[4].fillColor = colors.aquamarine - pc.slices[5].fillColor = colors.cadetblue - - d.add(pc) - - return d diff --git a/bin/reportlab/graphics/charts/slidebox.py b/bin/reportlab/graphics/charts/slidebox.py deleted file mode 100644 index 309fb07ba0d..00000000000 --- a/bin/reportlab/graphics/charts/slidebox.py +++ /dev/null @@ -1,186 +0,0 @@ -from reportlab.lib.colors import Color, white, black -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.shapes import Polygon, Line, Circle, String, Drawing, PolyLine, Group, Rect -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection -from reportlab.lib.attrmap import * -from reportlab.lib.validators import * -from reportlab.lib.units import cm -from reportlab.pdfbase.pdfmetrics import stringWidth, getFont -from reportlab.graphics.widgets.grids import ShadedRect, Grid - -class SlideBox(Widget): - """Returns a slidebox widget""" - _attrMap = AttrMap( - labelFontName = AttrMapValue(isString, desc="Name of font used for the labels"), - labelFontSize = AttrMapValue(isNumber, desc="Size of font used for the labels"), - labelStrokeColor = AttrMapValue(isColorOrNone, desc="Colour for for number outlines"), - labelFillColor = AttrMapValue(isColorOrNone, desc="Colour for number insides"), - startColor = AttrMapValue(isColor, desc='Color of first box'), - endColor = AttrMapValue(isColor, desc='Color of last box'), - numberOfBoxes = AttrMapValue(isInt, desc='How many boxes there are'), - trianglePosition = AttrMapValue(isInt, desc='Which box is highlighted by the triangles'), - triangleHeight = AttrMapValue(isNumber, desc="Height of indicator triangles"), - triangleWidth = AttrMapValue(isNumber, desc="Width of indicator triangles"), - triangleFillColor = AttrMapValue(isColor, desc="Colour of indicator triangles"), - triangleStrokeColor = AttrMapValue(isColorOrNone, desc="Colour of indicator triangle outline"), - triangleStrokeWidth = AttrMapValue(isNumber, desc="Colour of indicator triangle outline"), - boxHeight = AttrMapValue(isNumber, desc="Height of the boxes"), - boxWidth = AttrMapValue(isNumber, desc="Width of the boxes"), - boxSpacing = AttrMapValue(isNumber, desc="Space between the boxes"), - boxOutlineColor = AttrMapValue(isColorOrNone, desc="Colour used to outline the boxes (if any)"), - boxOutlineWidth = AttrMapValue(isNumberOrNone, desc="Width of the box outline (if any)"), - leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'), - rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'), - topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'), - bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'), - background = AttrMapValue(isColorOrNone, desc='Colour of the background to the drawing (if any)'), - sourceLabelText = AttrMapValue(isNoneOrString, desc="Text used for the 'source' label (can be empty)"), - sourceLabelOffset = AttrMapValue(isNumber, desc='Padding at bottom of drawing'), - sourceLabelFontName = AttrMapValue(isString, desc="Name of font used for the 'source' label"), - sourceLabelFontSize = AttrMapValue(isNumber, desc="Font size for the 'source' label"), - sourceLabelFillColor = AttrMapValue(isColorOrNone, desc="Colour ink for the 'source' label (bottom right)"), - ) - - def __init__(self): - self.labelFontName = "Helvetica-Bold" - self.labelFontSize = 10 - self.labelStrokeColor = black - self.labelFillColor = white - self.startColor = colors.Color(232/255.0,224/255.0,119/255.0) - self.endColor = colors.Color(25/255.0,77/255.0,135/255.0) - self.numberOfBoxes = 7 - self.trianglePosition = 7 - self.triangleHeight = 0.12*cm - self.triangleWidth = 0.38*cm - self.triangleFillColor = white - self.triangleStrokeColor = black - self.triangleStrokeWidth = 0.58 - self.boxHeight = 0.55*cm - self.boxWidth = 0.73*cm - self.boxSpacing = 0.075*cm - self.boxOutlineColor = black - self.boxOutlineWidth = 0.58 - self.leftPadding=5 - self.rightPadding=5 - self.topPadding=5 - self.bottomPadding=5 - self.background=None - self.sourceLabelText = "Source: ReportLab" - self.sourceLabelOffset = 0.2*cm - self.sourceLabelFontName = "Helvetica-Oblique" - self.sourceLabelFontSize = 6 - self.sourceLabelFillColor = black - - def _getDrawingDimensions(self): - tx=(self.numberOfBoxes*self.boxWidth) - if self.numberOfBoxes>1: tx=tx+((self.numberOfBoxes-1)*self.boxSpacing) - tx=tx+self.leftPadding+self.rightPadding - ty=self.boxHeight+self.triangleHeight - ty=ty+self.topPadding+self.bottomPadding+self.sourceLabelOffset+self.sourceLabelFontSize - return (tx,ty) - - def _getColors(self): - # for calculating intermediate colors... - numShades = self.numberOfBoxes+1 - fillColorStart = self.startColor - fillColorEnd = self.endColor - colorsList =[] - - for i in range(0,numShades): - colorsList.append(colors.linearlyInterpolatedColor(fillColorStart, fillColorEnd, 0, numShades-1, i)) - return colorsList - - def demo(self,drawing=None): - from reportlab.lib import colors - if not drawing: - tx,ty=self._getDrawingDimensions() - drawing = Drawing(tx,ty) - drawing.add(self.draw()) - return drawing - - def draw(self): - g = Group() - ys = self.bottomPadding+(self.triangleHeight/2)+self.sourceLabelOffset+self.sourceLabelFontSize - if self.background: - x,y = self._getDrawingDimensions() - g.add(Rect(-self.leftPadding,-ys,x,y, - strokeColor=None, - strokeWidth=0, - fillColor=self.background)) - - ascent=getFont(self.labelFontName).face.ascent/1000. - if ascent==0: ascent=0.718 # default (from helvetica) - ascent=ascent*self.labelFontSize # normalize - - colorsList = self._getColors() - - # Draw the boxes - now uses ShadedRect from grids - x=0 - for f in range (0,self.numberOfBoxes): - sr=ShadedRect() - sr.x=x - sr.y=0 - sr.width=self.boxWidth - sr.height=self.boxHeight - sr.orientation = 'vertical' - sr.numShades = 30 - sr.fillColorStart = colorsList[f] - sr.fillColorEnd = colorsList[f+1] - sr.strokeColor = None - sr.strokeWidth = 0 - - g.add(sr) - - g.add(Rect(x,0,self.boxWidth,self.boxHeight, - strokeColor=self.boxOutlineColor, - strokeWidth=self.boxOutlineWidth, - fillColor=None)) - - g.add(String(x+self.boxWidth/2.,(self.boxHeight-ascent)/2., - text = str(f+1), - fillColor = self.labelFillColor, - strokeColor=self.labelStrokeColor, - textAnchor = 'middle', - fontName = self.labelFontName, - fontSize = self.labelFontSize)) - x=x+self.boxWidth+self.boxSpacing - - #do triangles - xt = (self.trianglePosition*self.boxWidth) - if self.trianglePosition>1: - xt = xt+(self.trianglePosition-1)*self.boxSpacing - xt = xt-(self.boxWidth/2) - g.add(Polygon( - strokeColor = self.triangleStrokeColor, - strokeWidth = self.triangleStrokeWidth, - fillColor = self.triangleFillColor, - points=[xt,self.boxHeight-(self.triangleHeight/2), - xt-(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2), - xt+(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2), - xt,self.boxHeight-(self.triangleHeight/2)])) - g.add(Polygon( - strokeColor = self.triangleStrokeColor, - strokeWidth = self.triangleStrokeWidth, - fillColor = self.triangleFillColor, - points=[xt,0+(self.triangleHeight/2), - xt-(self.triangleWidth/2),0-(self.triangleHeight/2), - xt+(self.triangleWidth/2),0-(self.triangleHeight/2), - xt,0+(self.triangleHeight/2)])) - - #source label - if self.sourceLabelText != None: - g.add(String(x-self.boxSpacing,0-(self.triangleHeight/2)-self.sourceLabelOffset-(self.sourceLabelFontSize), - text = self.sourceLabelText, - fillColor = self.sourceLabelFillColor, - textAnchor = 'end', - fontName = self.sourceLabelFontName, - fontSize = self.sourceLabelFontSize)) - - g.shift(self.leftPadding, ys) - - return g - - -if __name__ == "__main__": - d = SlideBox() - d.demo().save(fnRoot="slidebox") diff --git a/bin/reportlab/graphics/charts/spider.py b/bin/reportlab/graphics/charts/spider.py deleted file mode 100644 index 6de92724390..00000000000 --- a/bin/reportlab/graphics/charts/spider.py +++ /dev/null @@ -1,407 +0,0 @@ - #Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/spider.py -# spider chart, also known as radar chart - -"""Spider Chart - -Normal use shows variation of 5-10 parameters against some 'norm' or target. -When there is more than one series, place the series with the largest -numbers first, as it will be overdrawn by each successive one. -""" -__version__=''' $Id: spider.py 2676 2005-09-06 10:25:00Z rgbecker $ ''' - -import copy -from math import sin, cos, pi - -from reportlab.lib import colors -from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\ - isListOfNumbers, isColorOrNone, isString,\ - isListOfStringsOrNone, OneOf, SequenceOf,\ - isBoolean, isListOfColors, isNumberOrNone,\ - isNoneOrListOfNoneOrStrings, isTextAnchor,\ - isNoneOrListOfNoneOrNumbers, isBoxAnchor,\ - isStringOrNone, isStringOrNone, EitherOr,\ - isCallable -from reportlab.lib.attrmap import * -from reportlab.pdfgen.canvas import Canvas -from reportlab.graphics.shapes import Group, Drawing, Line, Rect, Polygon, PolyLine, Ellipse, \ - Wedge, String, STATE_DEFAULTS -from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder -from reportlab.graphics.charts.areas import PlotArea -from piecharts import WedgeLabel -from reportlab.graphics.widgets.markers import makeMarker, uSymbol2Symbol, isSymbol - -class StrandProperty(PropHolder): - - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - symbol = AttrMapValue(EitherOr((isStringOrNone,isSymbol)), desc='Widget placed at data points.'), - symbolSize= AttrMapValue(isNumber, desc='Symbol size.'), - name = AttrMapValue(isStringOrNone, desc='Name of the strand.'), - ) - - def __init__(self): - self.strokeWidth = 1 - self.fillColor = None - self.strokeColor = STATE_DEFAULTS["strokeColor"] - self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"] - self.symbol = None - self.symbolSize = 5 - self.name = None - -class SpokeProperty(PropHolder): - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - labelRadius = AttrMapValue(isNumber), - visible = AttrMapValue(isBoolean,desc="True if the spoke line is to be drawn"), - ) - - def __init__(self,**kw): - self.strokeWidth = 0.5 - self.fillColor = None - self.strokeColor = STATE_DEFAULTS["strokeColor"] - self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"] - self.visible = 1 - self.labelRadius = 1.05 - -class SpokeLabel(WedgeLabel): - def __init__(self,**kw): - WedgeLabel.__init__(self,**kw) - if '_text' not in kw.keys(): self._text = '' - -class StrandLabel(SpokeLabel): - _attrMap = AttrMap(BASE=SpokeLabel, - format = AttrMapValue(EitherOr((isStringOrNone,isCallable)),"Format for the label"), - dR = AttrMapValue(isNumberOrNone,"radial shift for label"), - ) - def __init__(self,**kw): - self.format = '' - self.dR = 0 - SpokeLabel.__init__(self,**kw) - -def _setupLabel(labelClass, text, radius, cx, cy, angle, car, sar, sty): - L = labelClass() - L._text = text - L.x = cx + radius*car - L.y = cy + radius*sar - L._pmv = angle*180/pi - L.boxAnchor = sty.boxAnchor - L.dx = sty.dx - L.dy = sty.dy - L.angle = sty.angle - L.boxAnchor = sty.boxAnchor - L.boxStrokeColor = sty.boxStrokeColor - L.boxStrokeWidth = sty.boxStrokeWidth - L.boxFillColor = sty.boxFillColor - L.strokeColor = sty.strokeColor - L.strokeWidth = sty.strokeWidth - L.leading = sty.leading - L.width = sty.width - L.maxWidth = sty.maxWidth - L.height = sty.height - L.textAnchor = sty.textAnchor - L.visible = sty.visible - L.topPadding = sty.topPadding - L.leftPadding = sty.leftPadding - L.rightPadding = sty.rightPadding - L.bottomPadding = sty.bottomPadding - L.fontName = sty.fontName - L.fontSize = sty.fontSize - L.fillColor = sty.fillColor - return L - -class SpiderChart(PlotArea): - _attrMap = AttrMap(BASE=PlotArea, - data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'), - labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"), - startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"), - direction = AttrMapValue( OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"), - strands = AttrMapValue(None, desc="collection of strand descriptor objects"), - spokes = AttrMapValue(None, desc="collection of spoke descriptor objects"), - strandLabels = AttrMapValue(None, desc="collection of strand label descriptor objects"), - spokeLabels = AttrMapValue(None, desc="collection of spoke label descriptor objects"), - ) - - def makeSwatchSample(self, rowNo, x, y, width, height): - baseStyle = self.strands - styleIdx = rowNo % len(baseStyle) - style = baseStyle[styleIdx] - strokeColor = getattr(style, 'strokeColor', getattr(baseStyle,'strokeColor',None)) - fillColor = getattr(style, 'fillColor', getattr(baseStyle,'fillColor',None)) - strokeDashArray = getattr(style, 'strokeDashArray', getattr(baseStyle,'strokeDashArray',None)) - strokeWidth = getattr(style, 'strokeWidth', getattr(baseStyle, 'strokeWidth',0)) - symbol = getattr(style, 'symbol', getattr(baseStyle, 'symbol',None)) - ym = y+height/2.0 - if fillColor is None and strokeColor is not None and strokeWidth>0: - bg = Line(x,ym,x+width,ym,strokeWidth=strokeWidth,strokeColor=strokeColor, - strokeDashArray=strokeDashArray) - elif fillColor is not None: - bg = Rect(x,y,width,height,strokeWidth=strokeWidth,strokeColor=strokeColor, - strokeDashArray=strokeDashArray,fillColor=fillColor) - else: - bg = None - if symbol: - symbol = uSymbol2Symbol(symbol,x+width/2.,ym,color) - if bg: - g = Group() - g.add(bg) - g.add(symbol) - return g - return symbol or bg - - def getSeriesName(self,i,default=None): - '''return series name i or default''' - return getattr(self.strands[i],'name',default) - - def __init__(self): - PlotArea.__init__(self) - - self.data = [[10,12,14,16,14,12], [6,8,10,12,9,11]] - self.labels = None # or list of strings - self.labels = ['a','b','c','d','e','f'] - self.startAngle = 90 - self.direction = "clockwise" - - self.strands = TypedPropertyCollection(StrandProperty) - self.spokes = TypedPropertyCollection(SpokeProperty) - self.spokeLabels = TypedPropertyCollection(SpokeLabel) - self.spokeLabels._text = None - self.strandLabels = TypedPropertyCollection(StrandLabel) - self.x = 10 - self.y = 10 - self.width = 180 - self.height = 180 - - def demo(self): - d = Drawing(200, 200) - d.add(SpiderChart()) - return d - - def normalizeData(self, outer = 0.0): - """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)) - norm *= (1.0+outer) - if norm<1e-9: norm = 1.0 - self._norm = norm - return [[e/norm for e in row] for row in data] - - def _innerDrawLabel(self, sty, radius, cx, cy, angle, car, sar, labelClass=StrandLabel): - "Draw a label for a given item in the list." - fmt = sty.format - value = radius*self._norm - if not fmt: - text = None - elif isinstance(fmt,str): - if fmt == 'values': - text = sty._text - else: - text = fmt % value - elif callable(fmt): - text = fmt(value) - else: - raise ValueError("Unknown formatter type %s, expected string or function" % fmt) - - if text: - dR = sty.dR - if dR: - radius += dR/self._radius - L = _setupLabel(labelClass, text, radius, cx, cy, angle, car, sar, sty) - if dR<0: L._anti = 1 - else: - L = None - return L - - def draw(self): - # normalize slice data - g = self.makeBackground() or Group() - - xradius = self.width/2.0 - yradius = self.height/2.0 - self._radius = radius = min(xradius, yradius) - cx = self.x + xradius - cy = self.y + yradius - - data = self.normalizeData() - - self._seriesCount = len(data) - n = len(data[0]) - - #labels - if self.labels is None: - labels = [''] * n - else: - labels = self.labels - #there's no point in raising errors for less than enough errors if - #we silently create all for the extreme case of no labels. - i = n-len(labels) - if i>0: - labels = labels + ['']*i - - S = [] - STRANDS = [] - STRANDAREAS = [] - syms = [] - labs = [] - csa = [] - angle = self.startAngle*pi/180 - direction = self.direction == "clockwise" and -1 or 1 - angleBetween = direction*(2 * pi)/float(n) - spokes = self.spokes - spokeLabels = self.spokeLabels - for i in xrange(n): - car = cos(angle)*radius - sar = sin(angle)*radius - csa.append((car,sar,angle)) - si = self.spokes[i] - if si.visible: - spoke = Line(cx, cy, cx + car, cy + sar, strokeWidth = si.strokeWidth, strokeColor=si.strokeColor, strokeDashArray=si.strokeDashArray) - S.append(spoke) - sli = spokeLabels[i] - text = sli._text - if not text: text = labels[i] - if text: - S.append(_setupLabel(WedgeLabel, text, si.labelRadius, cx, cy, angle, car, sar, sli)) - angle += angleBetween - - # now plot the polygons - rowIdx = 0 - strands = self.strands - strandLabels = self.strandLabels - for row in data: - # series plot - rsty = strands[rowIdx] - points = [] - car, sar = csa[-1][:2] - r = row[-1] - points.append(cx+car*r) - points.append(cy+sar*r) - for i in xrange(n): - car, sar, angle = csa[i] - r = row[i] - points.append(cx+car*r) - points.append(cy+sar*r) - L = self._innerDrawLabel(strandLabels[(rowIdx,i)], r, cx, cy, angle, car, sar, labelClass=StrandLabel) - if L: labs.append(L) - sty = strands[(rowIdx,i)] - uSymbol = sty.symbol - - # put in a marker, if it needs one - if uSymbol: - s_x = cx+car*r - s_y = cy+sar*r - s_fillColor = sty.fillColor - s_strokeColor = sty.strokeColor - s_strokeWidth = sty.strokeWidth - s_angle = 0 - s_size = sty.symbolSize - if type(uSymbol) is type(''): - symbol = makeMarker(uSymbol, - size = s_size, - x = s_x, - y = s_y, - fillColor = s_fillColor, - strokeColor = s_strokeColor, - strokeWidth = s_strokeWidth, - angle = s_angle, - ) - else: - symbol = uSymbol2Symbol(uSymbol,s_x,s_y,s_fillColor) - for k,v in (('size', s_size), ('fillColor', s_fillColor), - ('x', s_x), ('y', s_y), - ('strokeColor',s_strokeColor), ('strokeWidth',s_strokeWidth), - ('angle',s_angle),): - if getattr(symbol,k,None) is None: - try: - setattr(symbol,k,v) - except: - pass - syms.append(symbol) - - # make up the 'strand' - if rsty.fillColor: - strand = Polygon(points) - strand.fillColor = rsty.fillColor - strand.strokeColor = None - strand.strokeWidth = 0 - STRANDAREAS.append(strand) - if rsty.strokeColor and rsty.strokeWidth: - strand = PolyLine(points) - strand.strokeColor = rsty.strokeColor - strand.strokeWidth = rsty.strokeWidth - strand.strokeDashArray = rsty.strokeDashArray - STRANDS.append(strand) - rowIdx += 1 - - map(g.add,STRANDAREAS+STRANDS+syms+S+labs) - return g - -def sample1(): - "Make a simple spider chart" - d = Drawing(400, 400) - sp = SpiderChart() - sp.x = 50 - sp.y = 50 - sp.width = 300 - sp.height = 300 - sp.data = [[10,12,14,16,14,12], [6,8,10,12,9,15],[7,8,17,4,12,8]] - sp.labels = ['a','b','c','d','e','f'] - sp.strands[0].strokeColor = colors.cornsilk - sp.strands[1].strokeColor = colors.cyan - sp.strands[2].strokeColor = colors.palegreen - sp.strands[0].fillColor = colors.cornsilk - sp.strands[1].fillColor = colors.cyan - sp.strands[2].fillColor = colors.palegreen - sp.spokes.strokeDashArray = (2,2) - d.add(sp) - return d - - -def sample2(): - "Make a spider chart with markers, but no fill" - d = Drawing(400, 400) - sp = SpiderChart() - sp.x = 50 - sp.y = 50 - sp.width = 300 - sp.height = 300 - sp.data = [[10,12,14,16,14,12], [6,8,10,12,9,15],[7,8,17,4,12,8]] - sp.labels = ['U','V','W','X','Y','Z'] - sp.strands.strokeWidth = 1 - sp.strands[0].fillColor = colors.pink - sp.strands[1].fillColor = colors.lightblue - sp.strands[2].fillColor = colors.palegreen - sp.strands[0].strokeColor = colors.red - sp.strands[1].strokeColor = colors.blue - sp.strands[2].strokeColor = colors.green - sp.strands.symbol = "FilledDiamond" - sp.strands[1].symbol = makeMarker("Circle") - sp.strands[1].symbol.strokeWidth = 0.5 - sp.strands[1].symbol.fillColor = colors.yellow - sp.strands.symbolSize = 6 - sp.strandLabels[0,3]._text = 'special' - sp.strandLabels[0,1]._text = 'one' - sp.strandLabels[0,0]._text = 'zero' - sp.strandLabels[1,0]._text = 'Earth' - sp.strandLabels[2,2]._text = 'Mars' - sp.strandLabels.format = 'values' - sp.strandLabels.dR = -5 - d.add(sp) - return d - - -if __name__=='__main__': - d = sample1() - from reportlab.graphics.renderPDF import drawToFile - drawToFile(d, 'spider.pdf') - d = sample2() - drawToFile(d, 'spider2.pdf') diff --git a/bin/reportlab/graphics/charts/textlabels.py b/bin/reportlab/graphics/charts/textlabels.py deleted file mode 100644 index 5bcd0250e79..00000000000 --- a/bin/reportlab/graphics/charts/textlabels.py +++ /dev/null @@ -1,442 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/textlabels.py -__version__=''' $Id: textlabels.py 2647 2005-07-26 13:47:51Z rgbecker $ ''' -import string - -from reportlab.lib import colors -from reportlab.lib.validators import isNumber, isNumberOrNone, OneOf, isColorOrNone, isString, \ - isTextAnchor, isBoxAnchor, isBoolean, NoneOr, isInstanceOf, isNoneOrString -from reportlab.lib.attrmap import * -from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.graphics.shapes import Drawing, Group, Circle, Rect, String, STATE_DEFAULTS -from reportlab.graphics.shapes import _PATH_OP_ARG_COUNT, _PATH_OP_NAMES, definePath -from reportlab.graphics.widgetbase import Widget, PropHolder - -_gs = None -_A2BA= { - 'x': {0:'n', 45:'ne', 90:'e', 135:'se', 180:'s', 225:'sw', 270:'w', 315: 'nw', -45: 'nw'}, - 'y': {0:'e', 45:'se', 90:'s', 135:'sw', 180:'w', 225:'nw', 270:'n', 315: 'ne', -45: 'ne'}, - } -def _simpleSplit(txt,mW,SW): - L = [] - ws = SW(' ') - O = [] - w = -ws - for t in string.split(txt): - lt = SW(t) - if w+ws+lt<=mW or O==[]: - O.append(t) - w = w + ws + lt - else: - L.append(string.join(O,' ')) - O = [t] - w = lt - if O!=[]: L.append(string.join(O,' ')) - return L - -def _pathNumTrunc(n): - if int(n)==n: return int(n) - return round(n,5) - -def _processGlyph(G, truncate=1, pathReverse=0): - O = [] - P = [] - R = [] - 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): - 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) - for o in O: - j = i + _PATH_OP_ARG_COUNT[_PATH_OP_NAMES.index(o)] - if o=='closePath': - R.append(o) - else: - R.append((o,)+ tuple(P[i:j])) - i = j - O = [] - P = [] - O.append(op) - P.extend(g[1:]) - return R - -def _text2PathDescription(text, x=0, y=0, fontName='Times-Roman', 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 - renderPM._setFont(_gs,fontName,fontSize) - P = [] - if not anchor =='start': - textLen = stringWidth(text, fontName,fontSize) - if text_anchor=='end': - x = x-textLen - elif text_anchor=='middle': - x = x - textLen/2. - for g in _gs._stringPath(text,x,y): - P.extend(_processGlyph(g,truncate=truncate,pathReverse=pathReverse)) - return P - -def _text2Path(text, x=0, y=0, fontName='Times-Roman', fontSize=1000, - anchor='start', truncate=1, pathReverse=0): - return definePath(_text2PathDescription(text,x=x,y=y,fontName=fontName, - fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse)) - -_BA2TA={'w':'start','nw':'start','sw':'start','e':'end', 'ne': 'end', 'se':'end', 'n':'middle','s':'middle','c':'middle'} -class Label(Widget): - """A text label to attach to something else, such as a chart axis. - - This allows you to specify an offset, angle and many anchor - properties relative to the label's origin. It allows, for example, - angled multiline axis labels. - """ - # fairly straight port of Robin Becker's textbox.py to new widgets - # framework. - - _attrMap = AttrMap( - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - dx = AttrMapValue(isNumber), - dy = AttrMapValue(isNumber), - angle = AttrMapValue(isNumber), - boxAnchor = AttrMapValue(isBoxAnchor), - boxStrokeColor = AttrMapValue(isColorOrNone), - boxStrokeWidth = AttrMapValue(isNumber), - boxFillColor = AttrMapValue(isColorOrNone), - boxTarget = AttrMapValue(isString), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeWidth = AttrMapValue(isNumber), - text = AttrMapValue(isString), - fontName = AttrMapValue(isString), - fontSize = AttrMapValue(isNumber), - leading = AttrMapValue(isNumberOrNone), - width = AttrMapValue(isNumberOrNone), - maxWidth = AttrMapValue(isNumberOrNone), - height = AttrMapValue(isNumberOrNone), - textAnchor = AttrMapValue(isTextAnchor), - visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"), - topPadding = AttrMapValue(isNumber,'padding at top of box'), - leftPadding = AttrMapValue(isNumber,'padding at left of box'), - rightPadding = AttrMapValue(isNumber,'padding at right of box'), - bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'), - ) - - def __init__(self,**kw): - self._setKeywords(**kw) - self._setKeywords( - _text = 'Multi-Line\nString', - boxAnchor = 'c', - angle = 0, - x = 0, - y = 0, - dx = 0, - dy = 0, - topPadding = 0, - leftPadding = 0, - rightPadding = 0, - bottomPadding = 0, - boxStrokeWidth = 0.5, - boxStrokeColor = None, - boxTarget = 'normal', - strokeColor = None, - boxFillColor = None, - leading = None, - width = None, - maxWidth = None, - height = None, - fillColor = STATE_DEFAULTS['fillColor'], - fontName = STATE_DEFAULTS['fontName'], - fontSize = STATE_DEFAULTS['fontSize'], - strokeWidth = 0.1, - textAnchor = 'start', - visible = 1, - ) - - def setText(self, text): - """Set the text property. May contain embedded newline characters. - Called by the containing chart or axis.""" - self._text = text - - - def setOrigin(self, x, y): - """Set the origin. This would be the tick mark or bar top relative to - which it is defined. Called by the containing chart or axis.""" - self.x = x - self.y = y - - - def demo(self): - """This shows a label positioned with its top right corner - at the top centre of the drawing, and rotated 45 degrees.""" - - d = Drawing(200, 100) - - # mark the origin of the label - d.add(Circle(100,90, 5, fillColor=colors.green)) - - lab = Label() - lab.setOrigin(100,90) - lab.boxAnchor = 'ne' - lab.angle = 45 - lab.dx = 0 - lab.dy = -20 - lab.boxStrokeColor = colors.green - lab.setText('Another\nMulti-Line\nString') - d.add(lab) - - return d - - def _getBoxAnchor(self): - '''hook for allowing special box anchor effects''' - ba = self.boxAnchor - if ba in ('autox', 'autoy'): - angle = self.angle - na = (int((angle%360)/45.)*45)%360 - if not (na % 90): # we have a right angle case - da = (angle - na) % 360 - if abs(da)>5: - na = na + (da>0 and 45 or -45) - ba = _A2BA[ba[-1]][na] - return ba - - def computeSize(self): - # the thing will draw in its own coordinate system - self._lines = string.split(self._text, '\n') - self._lineWidths = [] - topPadding = self.topPadding - leftPadding = self.leftPadding - rightPadding = self.rightPadding - bottomPadding = self.bottomPadding - SW = lambda text, fN=self.fontName, fS=self.fontSize: stringWidth(text, fN, fS) - if self.maxWidth: - L = [] - for l in self._lines: - L[-1:-1] = _simpleSplit(l,self.maxWidth,SW) - self._lines = L - if not self.width: - w = 0 - for line in self._lines: - thisWidth = SW(line) - self._lineWidths.append(thisWidth) - w = max(w,thisWidth) - self._width = w+leftPadding+rightPadding - else: - self._width = self.width - self._height = self.height or ((self.leading or 1.2*self.fontSize) * len(self._lines)+topPadding+bottomPadding) - self._ewidth = (self._width-leftPadding-rightPadding) - self._eheight = (self._height-topPadding-bottomPadding) - boxAnchor = self._getBoxAnchor() - if boxAnchor in ['n','ne','nw']: - self._top = -topPadding - elif boxAnchor in ['s','sw','se']: - self._top = self._height-topPadding - else: - self._top = 0.5*self._eheight - self._bottom = self._top - self._eheight - - if boxAnchor in ['ne','e','se']: - self._left = leftPadding - self._width - elif boxAnchor in ['nw','w','sw']: - self._left = leftPadding - else: - self._left = -self._ewidth*0.5 - self._right = self._left+self._ewidth - - def _getTextAnchor(self): - '''This can be overridden to allow special effects''' - ta = self.textAnchor - if ta=='boxauto': ta = _BA2TA[self._getBoxAnchor()] - return ta - - def draw(self): - _text = self._text - self._text = _text or '' - self.computeSize() - self._text = _text - g = Group() - g.translate(self.x + self.dx, self.y + self.dy) - g.rotate(self.angle) - - y = self._top - self.fontSize - textAnchor = self._getTextAnchor() - if textAnchor == 'start': - x = self._left - elif textAnchor == 'middle': - x = self._left + self._ewidth*0.5 - else: - x = self._right - - # paint box behind text just in case they - # fill it - if self.boxFillColor or (self.boxStrokeColor and self.boxStrokeWidth): - g.add(Rect( self._left-self.leftPadding, - self._bottom-self.bottomPadding, - self._width, - self._height, - strokeColor=self.boxStrokeColor, - strokeWidth=self.boxStrokeWidth, - fillColor=self.boxFillColor) - ) - - fillColor, fontName, fontSize = self.fillColor, self.fontName, self.fontSize - strokeColor, strokeWidth, leading = self.strokeColor, self.strokeWidth, (self.leading or 1.2*fontSize) - if strokeColor: - for line in self._lines: - s = _text2Path(line, x, y, fontName, fontSize, textAnchor) - s.fillColor = fillColor - s.strokeColor = strokeColor - s.strokeWidth = strokeWidth - g.add(s) - y = y - leading - else: - for line in self._lines: - s = String(x, y, line) - s.textAnchor = textAnchor - s.fontName = fontName - s.fontSize = fontSize - s.fillColor = fillColor - g.add(s) - y = y - leading - - return g - -class LabelDecorator: - _attrMap = AttrMap( - x = AttrMapValue(isNumberOrNone), - y = AttrMapValue(isNumberOrNone), - dx = AttrMapValue(isNumberOrNone), - dy = AttrMapValue(isNumberOrNone), - angle = AttrMapValue(isNumberOrNone), - boxAnchor = AttrMapValue(isBoxAnchor), - boxStrokeColor = AttrMapValue(isColorOrNone), - boxStrokeWidth = AttrMapValue(isNumberOrNone), - boxFillColor = AttrMapValue(isColorOrNone), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeWidth = AttrMapValue(isNumberOrNone), - fontName = AttrMapValue(isNoneOrString), - fontSize = AttrMapValue(isNumberOrNone), - leading = AttrMapValue(isNumberOrNone), - width = AttrMapValue(isNumberOrNone), - maxWidth = AttrMapValue(isNumberOrNone), - height = AttrMapValue(isNumberOrNone), - textAnchor = AttrMapValue(isTextAnchor), - visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"), - ) - - def __init__(self): - self.textAnchor = 'start' - self.boxAnchor = 'w' - for a in self._attrMap.keys(): - if not hasattr(self,a): setattr(self,a,None) - - def decorate(self,l,L): - chart,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0 = l._callOutInfo - L.setText(chart.categoryAxis.categoryNames[colNo]) - g.add(L) - - def __call__(self,l): - from copy import deepcopy - L = Label() - for a,v in self.__dict__.items(): - if v is None: v = getattr(l,a,None) - setattr(L,a,v) - self.decorate(l,L) - -isOffsetMode=OneOf('high','low','bar','axis') -class LabelOffset(PropHolder): - _attrMap = AttrMap( - posMode = AttrMapValue(isOffsetMode,desc="Where to base +ve offset"), - pos = AttrMapValue(isNumber,desc='Value for positive elements'), - negMode = AttrMapValue(isOffsetMode,desc="Where to base -ve offset"), - neg = AttrMapValue(isNumber,desc='Value for negative elements'), - ) - def __init__(self): - self.posMode=self.negMode='axis' - self.pos = self.neg = 0 - - def _getValue(self, chart, val): - flipXY = chart._flipXY - A = chart.categoryAxis - jA = A.joinAxis - if val>=0: - mode = self.posMode - delta = self.pos - else: - mode = self.negMode - delta = self.neg - if flipXY: - v = A._x - else: - v = A._y - if jA: - if flipXY: - _v = jA._x - else: - _v = jA._y - if mode=='high': - v = _v + jA._length - elif mode=='low': - v = _v - elif mode=='bar': - v = _v+val - return v+delta - -NoneOrInstanceOfLabelOffset=NoneOr(isInstanceOf(LabelOffset)) - -class BarChartLabel(Label): - """ - An extended Label allowing for nudging, lines visibility etc - """ - _attrMap = AttrMap( - BASE=Label, - lineStrokeWidth = AttrMapValue(isNumberOrNone, desc="Non-zero for a drawn line"), - lineStrokeColor = AttrMapValue(isColorOrNone, desc="Color for a drawn line"), - fixedEnd = AttrMapValue(NoneOrInstanceOfLabelOffset, desc="None or fixed draw ends +/-"), - fixedStart = AttrMapValue(NoneOrInstanceOfLabelOffset, desc="None or fixed draw starts +/-"), - nudge = AttrMapValue(isNumber, desc="Non-zero sign dependent nudge"), - ) - - def __init__(self): - Label.__init__(self) - self.lineStrokeWidth = 0 - self.lineStrokeColor = None - self.nudge = 0 - self.fixedStart = self.fixedEnd = None - self._pmv = 0 - - def _getBoxAnchor(self): - a = self.boxAnchor - if self._pmv<0: a = {'nw':'se','n':'s','ne':'sw','w':'e','c':'c','e':'w','sw':'ne','s':'n','se':'nw'}[a] - return a - - def _getTextAnchor(self): - a = self.textAnchor - if self._pmv<0: a = {'start':'end', 'middle':'middle', 'end':'start'}[a] - return a - -class NA_Label(BarChartLabel): - """ - An extended Label allowing for nudging, lines visibility etc - """ - _attrMap = AttrMap( - BASE=BarChartLabel, - text = AttrMapValue(isNoneOrString, desc="Text to be used for N/A values"), - ) - def __init__(self): - BarChartLabel.__init__(self) - self.text = 'n/a' -NoneOrInstanceOfNA_Label=NoneOr(isInstanceOf(NA_Label)) diff --git a/bin/reportlab/graphics/charts/utils.py b/bin/reportlab/graphics/charts/utils.py deleted file mode 100644 index c654428f5c1..00000000000 --- a/bin/reportlab/graphics/charts/utils.py +++ /dev/null @@ -1,191 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/utils.py -"Utilities used here and there." -__version__=''' $Id: utils.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from time import mktime, gmtime, strftime -import string - - -### Dinu's stuff used in some line plots (likely to vansih). - -def mkTimeTuple(timeString): - "Convert a 'dd/mm/yyyy' formatted string to a tuple for use in the time module." - - list = [0] * 9 - dd, mm, yyyy = map(int, string.split(timeString, '/')) - list[:3] = [yyyy, mm, dd] - - return tuple(list) - - -def str2seconds(timeString): - "Convert a number of seconds since the epoch into a date string." - - return mktime(mkTimeTuple(timeString)) - - -def seconds2str(seconds): - "Convert a date string into the number of seconds since the epoch." - - return strftime('%Y-%m-%d', gmtime(seconds)) - - -### Aaron's rounding function for making nice values on axes. - -from math import log10 - -def nextRoundNumber(x): - """Return the first 'nice round number' greater than or equal to x - - Used in selecting apropriate tick mark intervals; we say we want - an interval which places ticks at least 10 points apart, work out - what that is in chart space, and ask for the nextRoundNumber(). - Tries the series 1,2,5,10,20,50,100.., going up or down as needed. - """ - - #guess to nearest order of magnitude - if x in (0, 1): - return x - - if x < 0: - return -1.0 * nextRoundNumber(-x) - else: - lg = int(log10(x)) - - if lg == 0: - if x < 1: - base = 0.1 - else: - base = 1.0 - elif lg < 0: - base = 10.0 ** (lg - 1) - else: - base = 10.0 ** lg # e.g. base(153) = 100 - # base will always be lower than x - - if base >= x: - return base * 1.0 - elif (base * 2) >= x: - return base * 2.0 - elif (base * 5) >= x: - return base * 5.0 - else: - return base * 10.0 - - -### Robin's stuff from rgb_ticks. - -from math import log10, floor - -_intervals=(.1, .2, .25, .5) -_j_max=len(_intervals)-1 - - -def find_interval(lo,hi,I=5): - 'determine tick parameters for range [lo, hi] using I intervals' - - if lo >= hi: - if lo==hi: - if lo==0: - lo = -.1 - hi = .1 - else: - lo = 0.9*lo - hi = 1.1*hi - else: - 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 - while 1: - a = x/b - if a<=_intervals[-1]: break - b = b*10 - - j = 0 - while a>_intervals[j]: j = j + 1 - - while 1: - ss = _intervals[j]*b - n = lo/ss - l = int(n)-(n<0) - n = ss*l - x = ss*(l+I) - a = I*ss - if n>0: - if a>=hi: - n = 0.0 - x = a - elif hi<0: - a = -a - if lo>a: - n = a - x = 0 - if hi<=x and n<=lo: break - j = j + 1 - if j>_j_max: - j = 0 - b = b*10 - return n, x, ss, lo - n + x - hi - - -def find_good_grid(lower,upper,n=(4,5,6,7,8,9), grid=None): - if grid: - t = divmod(lower,grid)[0] * grid - hi, z = divmod(upper,grid) - if z>1e-8: hi = hi+1 - hi = hi*grid - else: - try: - n[0] - except TypeError: - n = xrange(max(1,n-2),max(n+3,2)) - - w = 1e308 - for i in n: - z=find_interval(lower,upper,i) - if z[3] 3 or power < -3: - format = '%+'+`w+7`+'.0e' - else: - if power >= 0: - digits = int(power)+w - format = '%' + `digits`+'.0f' - else: - digits = w-int(power) - format = '%'+`digits+2`+'.'+`digits`+'f' - - if percent: format=format+'%%' - T = [] - n = int(float(hi-t)/grid+0.1)+1 - if split: - labels = [] - for i in xrange(n): - v = t+grid*i - T.append(v) - labels.append(format % v) - return T, labels - else: - for i in xrange(n): - v = t+grid*i - T.append((v, format % v)) - return T \ No newline at end of file diff --git a/bin/reportlab/graphics/charts/utils3d.py b/bin/reportlab/graphics/charts/utils3d.py deleted file mode 100644 index ca6d0f8d3d5..00000000000 --- a/bin/reportlab/graphics/charts/utils3d.py +++ /dev/null @@ -1,233 +0,0 @@ -from reportlab.lib import colors -from reportlab.lib.attrmap import * -from reportlab.pdfgen.canvas import Canvas -from reportlab.graphics.shapes import Group, Drawing, Ellipse, Wedge, String, STATE_DEFAULTS, Polygon, Line - -def _getShaded(col,shd=None,shading=0.1): - if shd is None: - from reportlab.lib.colors import Blacker - if col: shd = Blacker(col,1-shading) - return shd - -def _getLit(col,shd=None,lighting=0.1): - if shd is None: - from reportlab.lib.colors import Whiter - if col: shd = Whiter(col,1-lighting) - return shd - - -def _draw_3d_bar(G, x1, x2, y0, yhigh, xdepth, ydepth, - fillColor=None, fillColorShaded=None, - strokeColor=None, strokeWidth=1, shading=0.1): - fillColorShaded = _getShaded(fillColor,None,shading) - fillColorShadedTop = _getShaded(fillColor,None,shading/2.0) - - def _add_3d_bar(x1, x2, y1, y2, xoff, yoff, - G=G,strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor): - G.add(Polygon((x1,y1, x1+xoff,y1+yoff, x2+xoff,y2+yoff, x2,y2), - strokeWidth=strokeWidth, strokeColor=strokeColor, fillColor=fillColor,strokeLineJoin=1)) - - usd = max(y0, yhigh) - if xdepth or ydepth: - if y0!=yhigh: #non-zero height - _add_3d_bar( x2, x2, y0, yhigh, xdepth, ydepth, fillColor=fillColorShaded) #side - - _add_3d_bar(x1, x2, usd, usd, xdepth, ydepth, fillColor=fillColorShadedTop) #top - - G.add(Polygon((x1,y0,x2,y0,x2,yhigh,x1,yhigh), - strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor,strokeLineJoin=1)) #front - - if xdepth or ydepth: - G.add(Line( x1, usd, x2, usd, strokeWidth=strokeWidth, strokeColor=strokeColor or fillColorShaded)) - -class _YStrip: - def __init__(self,y0,y1, slope, fillColor, fillColorShaded, shading=0.1): - self.y0 = y0 - self.y1 = y1 - self.slope = slope - self.fillColor = fillColor - self.fillColorShaded = _getShaded(fillColor,fillColorShaded,shading) - -def _ystrip_poly( x0, x1, y0, y1, xoff, yoff): - return [x0,y0,x0+xoff,y0+yoff,x1+xoff,y1+yoff,x1,y1] - - -def _make_3d_line_info( G, x0, x1, y0, y1, z0, z1, - theta_x, theta_y, - fillColor, fillColorShaded=None, tileWidth=1, - strokeColor=None, strokeWidth=None, strokeDashArray=None, - shading=0.1): - zwidth = abs(z1-z0) - xdepth = zwidth*theta_x - ydepth = zwidth*theta_y - depth_slope = xdepth==0 and 1e150 or -ydepth/float(xdepth) - - x = float(x1-x0) - slope = x==0 and 1e150 or (y1-y0)/x - - c = slope>depth_slope and _getShaded(fillColor,fillColorShaded,shading) or fillColor - zy0 = z0*theta_y - zx0 = z0*theta_x - - tileStrokeWidth = 0.6 - if tileWidth is None: - D = [(x1,y1)] - else: - T = ((y1-y0)**2+(x1-x0)**2)**0.5 - tileStrokeWidth *= tileWidth - if Tself.x1: return 1 - if o.s==self.s and o.i in (self.i-1,self.i+1): return - a = self.a - b = self.b - oa = o.a - ob = o.b - det = ob*a - oa*b - if -1e-81 or ou<0 or ou>1: return - x = x0 + u*a - y = self.y0 + u*b - if _ZERO=small: a(seg) - S.sort(_segCmp) - I = [] - n = len(S) - for i in xrange(0,n-1): - s = S[i] - for j in xrange(i+1,n): - if s.intersect(S[j],I)==1: break - I.sort() - return I - -if __name__=='__main__': - from reportlab.graphics.shapes import Drawing - from reportlab.lib.colors import lightgrey, pink - D = Drawing(300,200) - _draw_3d_bar(D, 10, 20, 10, 50, 5, 5, fillColor=lightgrey, strokeColor=pink) - _draw_3d_bar(D, 30, 40, 10, 45, 5, 5, fillColor=lightgrey, strokeColor=pink) - - 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)]]) diff --git a/bin/reportlab/graphics/renderPDF.py b/bin/reportlab/graphics/renderPDF.py deleted file mode 100644 index e1da60c0896..00000000000 --- a/bin/reportlab/graphics/renderPDF.py +++ /dev/null @@ -1,360 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/renderPDF.py -# renderPDF - draws Drawings onto a canvas -"""Usage: - import renderpdf - renderpdf.draw(drawing, canvas, x, y) -Execute the script to see some test drawings. -changed -""" -__version__=''' $Id: renderPDF.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' - -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 import rl_config -from renderbase import Renderer, StateTracker, getStateDelta, renderScaledDrawing - -# the main entry point for users... -def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_): - """As it says""" - R = _PDFRenderer() - R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary) - -class _PDFRenderer(Renderer): - """This draws onto a PDF document. It needs to be a class - rather than a function, as some PDF-specific state tracking is - needed outside of the state info in the SVG model.""" - - def __init__(self): - self._stroke = 0 - self._fill = 0 - self._tracker = StateTracker() - - def drawNode(self, node): - """This is the recursive method called for each node - in the tree""" - #print "pdf:drawNode", self - #if node.__class__ is Wedge: stop - if not (isinstance(node, Path) and node.isClipPath): - self._canvas.saveState() - - #apply state changes - deltas = getStateDelta(node) - self._tracker.push(deltas) - self.applyStateChanges(deltas, {}) - - #draw the object, or recurse - self.drawNodeDispatcher(node) - - self._tracker.pop() - if not (isinstance(node, Path) and node.isClipPath): - self._canvas.restoreState() - - def drawRect(self, rect): - if rect.rx == rect.ry == 0: - #plain old rectangle - self._canvas.rect( - rect.x, rect.y, - rect.width, rect.height, - stroke=self._stroke, - fill=self._fill - ) - else: - #cheat and assume ry = rx; better to generalize - #pdfgen roundRect function. TODO - self._canvas.roundRect( - rect.x, rect.y, - rect.width, rect.height, rect.rx, - fill=self._fill, - stroke=self._stroke - ) - - def drawImage(self, image): - # currently not implemented in other renderers - if image.path and os.path.exists(image.path): - self._canvas.drawInlineImage( - image.path, - image.x, image.y, - image.width, image.height - ) - - def drawLine(self, line): - if self._stroke: - self._canvas.line(line.x1, line.y1, line.x2, line.y2) - - def drawCircle(self, circle): - self._canvas.circle( - circle.cx, circle.cy, circle.r, - fill=self._fill, - stroke=self._stroke - ) - - def drawPolyLine(self, polyline): - if self._stroke: - assert len(polyline.points) >= 2, 'Polyline must have 2 or more points' - head, tail = polyline.points[0:2], polyline.points[2:], - path = self._canvas.beginPath() - path.moveTo(head[0], head[1]) - for i in range(0, len(tail), 2): - path.lineTo(tail[i], tail[i+1]) - self._canvas.drawPath(path) - - def drawWedge(self, wedge): - centerx, centery, radius, startangledegrees, endangledegrees = \ - wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees - yradius, radius1, yradius1 = wedge._xtraRadii() - if yradius is None: yradius = radius - angle = endangledegrees-startangledegrees - path = self._canvas.beginPath() - if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None): - path.moveTo(centerx, centery) - path.arcTo(centerx-radius, centery-yradius, centerx+radius, centery+yradius, - startangledegrees, angle) - else: - path.arc(centerx-radius, centery-yradius, centerx+radius, centery+yradius, - startangledegrees, angle) - path.arcTo(centerx-radius1, centery-yradius1, centerx+radius1, centery+yradius1, - endangledegrees, -angle) - path.close() - self._canvas.drawPath(path, - fill=self._fill, - stroke=self._stroke) - - def drawEllipse(self, ellipse): - #need to convert to pdfgen's bounding box representation - x1 = ellipse.cx - ellipse.rx - x2 = ellipse.cx + ellipse.rx - y1 = ellipse.cy - ellipse.ry - y2 = ellipse.cy + ellipse.ry - self._canvas.ellipse(x1,y1,x2,y2,fill=self._fill,stroke=self._stroke) - - def drawPolygon(self, polygon): - assert len(polygon.points) >= 2, 'Polyline must have 2 or more points' - head, tail = polygon.points[0:2], polygon.points[2:], - path = self._canvas.beginPath() - path.moveTo(head[0], head[1]) - for i in range(0, len(tail), 2): - path.lineTo(tail[i], tail[i+1]) - path.close() - self._canvas.drawPath( - path, - stroke=self._stroke, - fill=self._fill - ) - - def drawString(self, stringObj): - if self._fill: - S = self._tracker.getState() - text_anchor, x, y, text, enc = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text, stringObj.encoding - if not text_anchor in ['start','inherited']: - font, font_size = S['fontName'], S['fontSize'] - textLen = stringWidth(text, font, font_size, enc) - if text_anchor=='end': - x = x-textLen - elif text_anchor=='middle': - x = x - textLen/2 - else: - raise ValueError, 'bad value for textAnchor '+str(text_anchor) - t = self._canvas.beginText(x,y) - t.textLine(text) - self._canvas.drawText(t) - - def drawPath(self, path): - from reportlab.graphics.shapes import _renderPath - pdfPath = self._canvas.beginPath() - drawFuncs = (pdfPath.moveTo, pdfPath.lineTo, pdfPath.curveTo, pdfPath.close) - isClosed = _renderPath(path, drawFuncs) - if isClosed: - fill = self._fill - else: - fill = 0 - if path.isClipPath: - self._canvas.clipPath(pdfPath, fill=fill, stroke=self._stroke) - else: - self._canvas.drawPath(pdfPath, - fill=fill, - stroke=self._stroke) - - def applyStateChanges(self, delta, newState): - """This takes a set of states, and outputs the PDF operators - needed to set those properties""" - for key, value in delta.items(): - if key == 'transform': - self._canvas.transform(value[0], value[1], value[2], - value[3], value[4], value[5]) - elif key == 'strokeColor': - #this has different semantics in PDF to SVG; - #we always have a color, and either do or do - #not apply it; in SVG one can have a 'None' color - if value is None: - self._stroke = 0 - else: - self._stroke = 1 - self._canvas.setStrokeColor(value) - elif key == 'strokeWidth': - self._canvas.setLineWidth(value) - elif key == 'strokeLineCap': #0,1,2 - self._canvas.setLineCap(value) - elif key == 'strokeLineJoin': - self._canvas.setLineJoin(value) -# elif key == 'stroke_dasharray': -# self._canvas.setDash(array=value) - elif key == 'strokeDashArray': - if value: - self._canvas.setDash(value) - else: - self._canvas.setDash() - elif key == 'fillColor': - #this has different semantics in PDF to SVG; - #we always have a color, and either do or do - #not apply it; in SVG one can have a 'None' color - if value is None: - self._fill = 0 - else: - self._fill = 1 - self._canvas.setFillColor(value) - elif key in ['fontSize', 'fontName']: - # both need setting together in PDF - # one or both might be in the deltas, - # so need to get whichever is missing - fontname = delta.get('fontName', self._canvas._fontname) - fontsize = delta.get('fontSize', self._canvas._fontsize) - self._canvas.setFont(fontname, fontsize) - -from reportlab.platypus import Flowable -class GraphicsFlowable(Flowable): - """Flowable wrapper around a Pingo drawing""" - def __init__(self, drawing): - self.drawing = drawing - self.width = self.drawing.width - self.height = self.drawing.height - - def draw(self): - draw(self.drawing, self.canv, 0, 0) - -def drawToFile(d, fn, msg="", showBoundary=rl_config._unset_, autoSize=1): - """Makes a one-page PDF with just the drawing. - - If autoSize=1, the PDF will be the same size as - the drawing; if 0, it will place the drawing on - an A4 page with a title above it - possibly overflowing - if too big.""" - d = renderScaledDrawing(d) - c = Canvas(fn) - c.setFont('Times-Roman', 36) - c.drawString(80, 750, msg) - c.setTitle(msg) - - if autoSize: - c.setPageSize((d.width, d.height)) - draw(d, c, 0, 0, showBoundary=showBoundary) - else: - #show with a title - c.setFont('Times-Roman', 12) - y = 740 - i = 1 - y = y - d.height - draw(d, c, 80, y, showBoundary=showBoundary) - - c.showPage() - c.save() - if sys.platform=='mac' and not hasattr(fn, "write"): - try: - import macfs, macostools - macfs.FSSpec(fn).SetCreatorType("CARO", "PDF ") - macostools.touched(fn) - except: - pass - - -def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1): - "Returns a PDF as a string in memory, without touching the disk" - s = getStringIO() - drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize) - return s.getvalue() - - -######################################################### -# -# test code. First, defin a bunch of drawings. -# Routine to draw them comes at the end. -# -######################################################### - - -def test(): - c = Canvas('renderPDF.pdf') - c.setFont('Times-Roman', 36) - c.drawString(80, 750, 'Graphics Test') - - # print all drawings and their doc strings from the test - # file - - #grab all drawings from the test module - from reportlab.graphics import testshapes - drawings = [] - 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)) - - #print in a loop, with their doc strings - c.setFont('Times-Roman', 12) - y = 740 - i = 1 - for (drawing, docstring) in drawings: - assert (docstring is not None), "Drawing %d has no docstring!" % i - if y < 300: #allows 5-6 lines of text - c.showPage() - y = 740 - # draw a title - y = y - 30 - c.setFont('Times-BoldItalic',12) - c.drawString(80, y, 'Drawing %d' % i) - c.setFont('Times-Roman',12) - y = y - 14 - textObj = c.beginText(80, y) - textObj.textLines(docstring) - c.drawText(textObj) - y = textObj.getY() - y = y - drawing.height - draw(drawing, c, 80, y) - i = i + 1 - if y!=740: c.showPage() - - c.save() - print 'saved renderPDF.pdf' - -##def testFlowable(): -## """Makes a platypus document""" -## from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer -## from reportlab.lib.styles import getSampleStyleSheet -## styles = getSampleStyleSheet() -## styNormal = styles['Normal'] -## -## doc = SimpleDocTemplate('test_flowable.pdf') -## story = [] -## story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal)) -## -## import testdrawings -## drawings = [] -## -## for funcname in dir(testdrawings): -## if funcname[0:10] == 'getDrawing': -## drawing = eval('testdrawings.' + funcname + '()') #execute it -## docstring = eval('testdrawings.' + funcname + '.__doc__') -## story.append(Paragraph(docstring, styNormal)) -## story.append(Spacer(18,18)) -## story.append(drawing) -## story.append(Spacer(36,36)) -## -## doc.build(story) -## print 'saves test_flowable.pdf' - -if __name__=='__main__': - test() - #testFlowable() diff --git a/bin/reportlab/graphics/renderPM.py b/bin/reportlab/graphics/renderPM.py deleted file mode 100644 index 35960a25b75..00000000000 --- a/bin/reportlab/graphics/renderPM.py +++ /dev/null @@ -1,663 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/graphics/Csrc/renderPM/renderP.py -__version__=''' $Id: renderPM.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' -"""Usage: - from reportlab.graphics import renderPM - renderPM.drawToFile(drawing,filename,fmt='GIF',configPIL={....}) -Other functions let you create a PM drawing as string or into a PM buffer. -Execute the script to see some test drawings.""" - -from reportlab.graphics.shapes import * -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 import rl_config - -class RenderPMError(Exception): - pass - -import string, os, sys - -try: - import _renderPM -except ImportError, errMsg: - raise ImportError, "No module named _renderPM\n" + \ - (str(errMsg)!='No module named _renderPM' and "it may be the wrong version or badly installed!" or - "see http://www.reportlab.org/rl_addons.html") - -from types import TupleType, ListType -_SeqTypes = (TupleType,ListType) - -def _getImage(): - try: - from PIL import Image - except ImportError: - import Image - return Image - -def Color2Hex(c): - #assert isinstance(colorobj, colors.Color) #these checks don't work well RGB - if c: return ((0xFF&int(255*c.red)) << 16) | ((0xFF&int(255*c.green)) << 8) | (0xFF&int(255*c.blue)) - return c - -# the main entry point for users... -def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_): - """As it says""" - R = _PMRenderer() - R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary) - -from reportlab.graphics.renderbase import Renderer -class _PMRenderer(Renderer): - """This draws onto a pix map image. It needs to be a class - rather than a function, as some image-specific state tracking is - needed outside of the state info in the SVG model.""" - - def __init__(self): - self._tracker = StateTracker() - - def pop(self): - self._tracker.pop() - self.applyState() - - def push(self,node): - deltas = getStateDelta(node) - self._tracker.push(deltas) - self.applyState() - - def applyState(self): - s = self._tracker.getState() - self._canvas.ctm = s['ctm'] - self._canvas.strokeWidth = s['strokeWidth'] - self._canvas.strokeColor = Color2Hex(s['strokeColor']) - self._canvas.lineCap = s['strokeLineCap'] - self._canvas.lineJoin = s['strokeLineJoin'] - da = s['strokeDashArray'] - da = da and (0,da) or None - self._canvas.dashArray = da - self._canvas.fillColor = Color2Hex(s['fillColor']) - self._canvas.setFont(s['fontName'], s['fontSize']) - - def initState(self,x,y): - deltas = STATE_DEFAULTS.copy() - deltas['transform'] = self._canvas._baseCTM[0:4]+(x,y) - self._tracker.push(deltas) - self.applyState() - - def drawNode(self, node): - """This is the recursive method called for each node - in the tree""" - - #apply state changes - self.push(node) - - #draw the object, or recurse - self.drawNodeDispatcher(node) - - # restore the state - self.pop() - - def drawRect(self, rect): - c = self._canvas - if rect.rx == rect.ry == 0: - #plain old rectangle, draw clockwise (x-axis to y-axis) direction - c.rect(rect.x,rect.y, rect.width, rect.height) - else: - c.roundRect(rect.x,rect.y, rect.width, rect.height, rect.rx, rect.ry) - - def drawLine(self, line): - self._canvas.line(line.x1,line.y1,line.x2,line.y2) - - def drawImage(self, image): - if image.path and os.path.exists(image.path): - if type(image.path) is type(''): - im = _getImage().open(image.path).convert('RGB') - else: - im = image.path.convert('RGB') - srcW, srcH = im.size - dstW, dstH = image.width, image.height - if dstW is None: dstW = srcW - if dstH is None: dstH = srcH - self._canvas._aapixbuf( - image.x, image.y, dstW, dstH, - im.tostring(), srcW, srcH, 3, - ) - - def drawCircle(self, circle): - c = self._canvas - c.circle(circle.cx,circle.cy, circle.r) - c.fillstrokepath() - - def drawPolyLine(self, polyline, _doClose=0): - P = polyline.points - assert len(P) >= 2, 'Polyline must have 1 or more points' - c = self._canvas - c.pathBegin() - c.moveTo(P[0], P[1]) - for i in range(2, len(P), 2): - c.lineTo(P[i], P[i+1]) - if _doClose: - c.pathClose() - c.pathFill() - c.pathStroke() - - def drawEllipse(self, ellipse): - c=self._canvas - c.ellipse(ellipse.cx, ellipse.cy, ellipse.rx,ellipse.ry) - c.fillstrokepath() - - def drawPolygon(self, polygon): - self.drawPolyLine(polygon,_doClose=1) - - def drawString(self, stringObj): - canv = self._canvas - fill = canv.fillColor - if fill is not None: - S = self._tracker.getState() - text_anchor = S['textAnchor'] - fontName = S['fontName'] - fontSize = S['fontSize'] - font = getFont(fontName) - text = stringObj.text - x = stringObj.x - y = stringObj.y - if not text_anchor in ['start','inherited']: - textLen = stringWidth(text, fontName,fontSize) - if text_anchor=='end': - x = x-textLen - elif text_anchor=='middle': - x = x - textLen/2 - else: - raise ValueError, 'bad value for textAnchor '+str(text_anchor) - if getattr(font,'_dynamicFont',None): - if isinstance(text,unicode): text = text.encode('utf8') - canv.drawString(x,y,text) - else: - fc = font - if not isinstance(text,unicode): - try: - text = text.decode('utf8') - except UnicodeDecodeError,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): - f, t = FT[i] - if f!=fc: - canv.setFont(f.fontName,fontSize) - fc = f - canv.drawString(x,y,t) - if i!=nm1: - x += wscale*sum(map(f.widths.__getitem__,map(ord,t))) - if font!=fc: - canv.setFont(fontName,fontSize) - - def drawPath(self, path): - c = self._canvas - if path is EmptyClipPath: - del c._clipPaths[-1] - if c._clipPaths: - P = c._clipPaths[-1] - icp = P.isClipPath - P.isClipPath = 1 - self.drawPath(P) - P.isClipPath = icp - else: - c.clipPathClear() - return - c.pathBegin() - drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.pathClose) - from reportlab.graphics.shapes import _renderPath - isClosed = _renderPath(path, drawFuncs) - if path.isClipPath: - c.clipPathSet() - c._clipPaths.append(path) - else: - if isClosed: c.pathFill() - c.pathStroke() - -def _setFont(gs,fontName,fontSize): - try: - gs.setFont(fontName,fontSize) - except _renderPM.Error, errMsg: - if errMsg.args[0]!="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) - 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) - gs.setFont(fontName,fontSize) - -def _convert2pilp(im): - Image = _getImage() - return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE) - -def _saveAsPICT(im,fn,fmt,transparent=None): - im = _convert2pilp(im) - cols, rows = im.size - #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) - if os.name=='mac': - from reportlab.lib.utils import markfilename - markfilename(fn,ext='PICT') - else: - fn.write(s) - -BEZIER_ARC_MAGIC = 0.5522847498 #constant for drawing circular arcs w/ Beziers -class PMCanvas: - def __init__(self,w,h,dpi=72,bg=0xffffff,configPIL=None): - '''configPIL dict is passed to image save method''' - scale = dpi/72.0 - w = int(w*scale+0.5) - h = int(h*scale+0.5) - self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg) - self.__dict__['_bg'] = bg - self.__dict__['_baseCTM'] = (scale,0,0,scale,0,0) - self.__dict__['_clipPaths'] = [] - self.__dict__['configPIL'] = configPIL - self.__dict__['_dpi'] = dpi - self.ctm = self._baseCTM - - def _drawTimeResize(self,w,h,bg=None): - if bg is None: bg = self._bg - self._drawing.width, self._drawing.height = w, h - A = {'ctm':None, 'strokeWidth':None, 'strokeColor':None, 'lineCap':None, 'lineJoin':None, 'dashArray':None, 'fillColor':None} - gs = self._gs - fN,fS = gs.fontName, gs.fontSize - for k in A.keys(): - A[k] = getattr(gs,k) - del gs, self._gs - gs = self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg) - for k in A.keys(): - setattr(self,k,A[k]) - gs.setFont(fN,fS) - - def toPIL(self): - im = _getImage().new('RGB', size=(self._gs.width, self._gs.height)) - im.fromstring(self._gs.pixBuf) - return im - - 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) - fmt = os.path.splitext(fn)[1] - if fmt.startswith('.'): fmt = fmt[1:] - configPIL = self.configPIL or {} - fmt = string.upper(fmt) - if fmt in ['GIF']: - im = _convert2pilp(im) - elif fmt in ['PCT','PICT']: - return _saveAsPICT(im,fn,fmt,transparent=configPIL.get('transparent',None)) - elif fmt in ['PNG','TIFF','BMP', 'PPM', 'TIF']: - if fmt=='TIF': fmt = 'TIFF' - if fmt=='PNG': - try: - from PIL import PngImagePlugin - except ImportError: - import PngImagePlugin - elif fmt=='BMP': - try: - from PIL import BmpImagePlugin - except ImportError: - import BmpImagePlugin - elif fmt in ('JPG','JPEG'): - fmt = 'JPEG' - else: - raise RenderPMError,"Unknown image kind %s" % fmt - if fmt=='TIFF': - tc = configPIL.get('transparent',None) - if tc: - from PIL import ImageChops, Image - 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') - 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') - for a,d in ('resolution',self._dpi),('resolution unit','inch'): - configPIL[a] = configPIL.get(a,d) - apply(im.save,(fn,fmt),configPIL) - if not hasattr(fn,'write') and os.name=='mac': - from reportlab.lib.utils import markfilename - markfilename(fn,ext=fmt) - - def saveToString(self,fmt='GIF'): - s = getStringIO() - self.saveToFile(s,fmt=fmt) - return s.getvalue() - - def _saveToBMP(self,f): - ''' - Niki Spahiev, , asserts that this is a respectable way to get BMP without PIL - f is a file like object to which the BMP is written - ''' - import struct - gs = self._gs - pix, width, height = gs.pixBuf, gs.width, gs.height - f.write(struct.pack('=2sLLLLLLhh24x','BM',len(pix)+54,0,54,40,width,height,1,24)) - rowb = width * 3 - for o in range(len(pix),0,-rowb): - f.write(pix[o-rowb:o]) - f.write( '\0' * 14 ) - - def setFont(self,fontName,fontSize,leading=None): - _setFont(self._gs,fontName,fontSize) - - def __setattr__(self,name,value): - setattr(self._gs,name,value) - - def __getattr__(self,name): - return getattr(self._gs,name) - - def fillstrokepath(self,stroke=1,fill=1): - if fill: self.pathFill() - if stroke: self.pathStroke() - - def _bezierArcSegmentCCW(self, cx,cy, rx,ry, theta0, theta1): - """compute the control points for a bezier arc with theta1-theta0 <= 90. - Points are computed for an arc with angle theta increasing in the - counter-clockwise (CCW) direction. returns a tuple with starting point - and 3 control points of a cubic bezier curve for the curvto opertator""" - - # Requires theta1 - theta0 <= 90 for a good approximation - assert abs(theta1 - theta0) <= 90 - cos0 = cos(pi*theta0/180.0) - sin0 = sin(pi*theta0/180.0) - x0 = cx + rx*cos0 - y0 = cy + ry*sin0 - - cos1 = cos(pi*theta1/180.0) - sin1 = sin(pi*theta1/180.0) - - x3 = cx + rx*cos1 - y3 = cy + ry*sin1 - - dx1 = -rx * sin0 - dy1 = ry * cos0 - - #from pdfgeom - halfAng = pi*(theta1-theta0)/(2.0 * 180.0) - k = abs(4.0 / 3.0 * (1.0 - cos(halfAng) ) /(sin(halfAng)) ) - x1 = x0 + dx1 * k - y1 = y0 + dy1 * k - - dx2 = -rx * sin1 - dy2 = ry * cos1 - - x2 = x3 - dx2 * k - y2 = y3 - dy2 * k - return ((x0,y0), ((x1,y1), (x2,y2), (x3,y3)) ) - - def bezierArcCCW(self, cx,cy, rx,ry, theta0, theta1): - """return a set of control points for Bezier approximation to an arc - with angle increasing counter clockwise. No requirement on |theta1-theta0| <= 90 - However, it must be true that theta1-theta0 > 0.""" - - # I believe this is also clockwise - # pretty much just like Robert Kern's pdfgeom.BezierArc - angularExtent = theta1 - theta0 - # break down the arc into fragments of <=90 degrees - if abs(angularExtent) <= 90.0: # we just need one fragment - angleList = [(theta0,theta1)] - else: - Nfrag = int( ceil( abs(angularExtent)/90.) ) - fragAngle = float(angularExtent)/ Nfrag # this could be negative - angleList = [] - for ii in range(Nfrag): - a = theta0 + ii * fragAngle - b = a + fragAngle # hmm.. is I wonder if this is precise enought - angleList.append((a,b)) - - ctrlpts = [] - for (a,b) in angleList: - if not ctrlpts: # first time - [(x0,y0), pts] = self._bezierArcSegmentCCW(cx,cy, rx,ry, a,b) - ctrlpts.append(pts) - else: - [(tmpx,tmpy), pts] = self._bezierArcSegmentCCW(cx,cy, rx,ry, a,b) - ctrlpts.append(pts) - return ((x0,y0), ctrlpts) - - def addEllipsoidalArc(self, cx,cy, rx, ry, ang1, ang2): - """adds an ellisesoidal arc segment to a path, with an ellipse centered - on cx,cy and with radii (major & minor axes) rx and ry. The arc is - drawn in the CCW direction. Requires: (ang2-ang1) > 0""" - - ((x0,y0), ctrlpts) = self.bezierArcCCW(cx,cy, rx,ry,ang1,ang2) - - self.lineTo(x0,y0) - for ((x1,y1), (x2,y2),(x3,y3)) in ctrlpts: - self.curveTo(x1,y1,x2,y2,x3,y3) - - def drawCentredString(self, x, y, text, text_anchor='middle'): - if self.fillColor is not None: - textLen = stringWidth(text, self.fontName,self.fontSize) - if text_anchor=='end': - x -= textLen - elif text_anchor=='middle': - x -= textLen/2. - self.drawString(x,y,text) - - def drawRightString(self, text, x, y): - self.drawCentredString(text,x,y,text_anchor='end') - - def line(self,x1,y1,x2,y2): - if self.strokeColor is not None: - self.pathBegin() - self.moveTo(x1,y1) - self.lineTo(x2,y2) - self.pathStroke() - - def rect(self,x,y,width,height,stroke=1,fill=1): - self.pathBegin() - self.moveTo(x, y) - self.lineTo(x+width, y) - self.lineTo(x+width, y + height) - self.lineTo(x, y + height) - self.pathClose() - self.fillstrokepath(stroke=stroke,fill=fill) - - def roundRect(self, x, y, width, height, rx,ry): - """rect(self, x, y, width, height, rx,ry): - Draw a rectangle if rx or rx and ry are specified the corners are - rounded with ellipsoidal arcs determined by rx and ry - (drawn in the counter-clockwise direction)""" - if rx==0: rx = ry - if ry==0: ry = rx - x2 = x + width - y2 = y + height - self.pathBegin() - self.moveTo(x+rx,y) - self.addEllipsoidalArc(x2-rx, y+ry, rx, ry, 270, 360 ) - self.addEllipsoidalArc(x2-rx, y2-ry, rx, ry, 0, 90) - self.addEllipsoidalArc(x+rx, y2-ry, rx, ry, 90, 180) - self.addEllipsoidalArc(x+rx, y+ry, rx, ry, 180, 270) - self.pathClose() - self.fillstrokepath() - - def circle(self, cx, cy, r): - "add closed path circle with center cx,cy and axes r: counter-clockwise orientation" - self.ellipse(cx,cy,r,r) - - def ellipse(self, cx,cy,rx,ry): - """add closed path ellipse with center cx,cy and axes rx,ry: counter-clockwise orientation - (remember y-axis increases downward) """ - self.pathBegin() - # first segment - x0 = cx + rx # (x0,y0) start pt - y0 = cy - - x3 = cx # (x3,y3) end pt of arc - y3 = cy-ry - - x1 = cx+rx - y1 = cy-ry*BEZIER_ARC_MAGIC - - x2 = x3 + rx*BEZIER_ARC_MAGIC - y2 = y3 - self.moveTo(x0, y0) - self.curveTo(x1,y1,x2,y2,x3,y3) - # next segment - x0 = x3 - y0 = y3 - - x3 = cx-rx - y3 = cy - - x1 = cx-rx*BEZIER_ARC_MAGIC - y1 = cy-ry - - x2 = x3 - y2 = cy- ry*BEZIER_ARC_MAGIC - self.curveTo(x1,y1,x2,y2,x3,y3) - # next segment - x0 = x3 - y0 = y3 - - x3 = cx - y3 = cy+ry - - x1 = cx-rx - y1 = cy+ry*BEZIER_ARC_MAGIC - - x2 = cx -rx*BEZIER_ARC_MAGIC - y2 = cy+ry - self.curveTo(x1,y1,x2,y2,x3,y3) - #last segment - x0 = x3 - y0 = y3 - - x3 = cx+rx - y3 = cy - - x1 = cx+rx*BEZIER_ARC_MAGIC - y1 = cy+ry - - x2 = cx+rx - y2 = cy+ry*BEZIER_ARC_MAGIC - self.curveTo(x1,y1,x2,y2,x3,y3) - self.pathClose() - - def saveState(self): - '''do nothing for compatibility''' - pass - - def setFillColor(self,aColor): - self.fillColor = Color2Hex(aColor) - - def setStrokeColor(self,aColor): - self.strokeColor = Color2Hex(aColor) - - restoreState = saveState - - # compatibility routines - def setLineCap(self,cap): - self.lineCap = cap - - def setLineWidth(self,width): - self.strokeWidth = width - -def drawToPMCanvas(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_): - d = renderScaledDrawing(d) - c = PMCanvas(d.width, d.height, dpi=dpi, bg=bg, configPIL=configPIL) - draw(d, c, 0, 0, showBoundary=showBoundary) - return c - -def drawToPIL(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_): - return drawToPMCanvas(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary).toPIL() - -def drawToPILP(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_): - Image = _getImage() - im = drawToPIL(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary) - return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE) - -def drawToFile(d,fn,fmt='GIF', dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_): - '''create a pixmap and draw drawing, d to it then save as a file - configPIL dict is passed to image save method''' - c = drawToPMCanvas(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary) - c.saveToFile(fn,fmt) - -def drawToString(d,fmt='GIF', dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_): - s = getStringIO() - drawToFile(d,s,fmt=fmt, dpi=dpi, bg=bg, configPIL=configPIL) - return s.getvalue() - -save = drawToFile - -def test(): - def ext(x): - if x=='tiff': x='tif' - return x - #grab all drawings from the test module and write out. - #make a page of links in HTML to assist viewing. - import os - from reportlab.graphics import testshapes - getAllTestDrawings = testshapes.getAllTestDrawings - drawings = [] - if not os.path.isdir('pmout'): - os.mkdir('pmout') - htmlTop = """renderPM output results - -

renderPM results of output

- """ - htmlBottom = """ - - """ - html = [htmlTop] - - i = 0 - #print in a loop, with their doc strings - for (drawing, docstring, name) in getAllTestDrawings(doTTF=hasattr(_renderPM,'ft_get_face')): - fnRoot = 'renderPM%d' % i - if 1 or i==10: - w = int(drawing.width) - h = int(drawing.height) - html.append('

Drawing %s %d

\n
%s
' % (name, i, docstring)) - - for k in ['gif','tiff', 'png', 'jpg', 'pct']: - if k in ['gif','png','jpg','pct']: - html.append('

%s format

\n' % string.upper(k)) - try: - filename = '%s.%s' % (fnRoot, ext(k)) - fullpath = os.path.join('pmout', filename) - if os.path.isfile(fullpath): - os.remove(fullpath) - if k=='pct': - from reportlab.lib.colors import white - drawToFile(drawing,fullpath,fmt=k,configPIL={'transparent':white}) - else: - drawToFile(drawing,fullpath,fmt=k) - if k in ['gif','png','jpg']: - html.append('
\n' % filename) - print 'wrote',fullpath - except AttributeError: - print 'Problem drawing %s file'%k - raise - if os.environ.get('RL_NOEPSPREVIEW','0')=='1': drawing.__dict__['preview'] = 0 - drawing.save(formats=['eps','pdf'],outDir='pmout',fnRoot=fnRoot) - i = i + 1 - #if i==10: break - html.append(htmlBottom) - htmlFileName = os.path.join('pmout', 'index.html') - open(htmlFileName, 'w').writelines(html) - if sys.platform=='mac': - from reportlab.lib.utils import markfilename - markfilename(htmlFileName,ext='HTML') - print 'wrote %s' % htmlFileName - -if __name__=='__main__': - test() diff --git a/bin/reportlab/graphics/renderPS.py b/bin/reportlab/graphics/renderPS.py deleted file mode 100644 index b6e11828fc3..00000000000 --- a/bin/reportlab/graphics/renderPS.py +++ /dev/null @@ -1,871 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/renderPS.py -__version__=''' $Id: renderPS.py 2808 2006-03-15 16:47:27Z rgbecker $ ''' -import string, types -from reportlab.pdfbase.pdfmetrics import getFont, stringWidth # for font info -from reportlab.lib.utils import fp_str, getStringIO -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 - - -# 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: - def __init__(self,size=(300,300), PostScriptLevel=2): - self.width, self.height = size - xtraState = [] - self._xtraState_push = xtraState.append - self._xtraState_pop = xtraState.pop - self.comments = 0 - self.code = [] - self._sep = '\n' - self._strokeColor = self._fillColor = self._lineWidth = \ - self._font = self._fontSize = self._lineCap = \ - self._lineJoin = self._color = None - - - self._fontsUsed = [] # track them as we go - - self.setFont(STATE_DEFAULTS['fontName'],STATE_DEFAULTS['fontSize']) - self.setStrokeColor(STATE_DEFAULTS['strokeColor']) - self.setLineCap(2) - self.setLineJoin(0) - self.setLineWidth(1) - - self.PostScriptLevel=PostScriptLevel - - def comment(self,msg): - if self.comments: self.code.append('%'+msg) - - def drawImage(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version - # select between postscript level 1 or level 2 - if PostScriptLevel==1: - self._drawImageLevel1(image, x1,y1, x2=None,y2=None) - elif PostScriptLevel == 2 : - self._drawImageLevel2(image, x1,y1, x2=None,y2=None) - else : - raise 'PostScriptLevelException' - - def clear(self): - self.code.append('showpage') # ugh, this makes no sense oh well. - - def save(self,f=None): - if not hasattr(f,'write'): - file = open(f,'wb') - else: - file = f - if self.code[-1]!='showpage': self.clear() - self.code.insert(0,'''\ -%%!PS-Adobe-3.0 EPSF-3.0 -%%%%BoundingBox: 0 0 %d %d -%%%% Initialization: -/m {moveto} bind def -/l {lineto} bind def -/c {curveto} bind def - -%s -''' % (self.width,self.height, PS_WinAnsiEncoding)) - - # for each font used, reencode the vectors - fontReencode = [] - for fontName in self._fontsUsed: - fontReencode.append('WinAnsiEncoding /%s /%s RE' % (fontName, fontName)) - self.code.insert(1, string.join(fontReencode, self._sep)) - - file.write(string.join(self.code,self._sep)) - if file is not f: - file.close() - from reportlab.lib.utils import markfilename - markfilename(f,creatorcode='XPR3',filetype='EPSF') - - def saveState(self): - self._xtraState_push((self._fontCodeLoc,)) - self.code.append('gsave') - - def restoreState(self): - self.code.append('grestore') - self._fontCodeLoc, = self._xtraState_pop() - - def stringWidth(self, s, font=None, fontSize=None): - """Return the logical width of the string if it were drawn - in the current font (defaults to self.font).""" - font = font or self._font - fontSize = fontSize or self._fontSize - return stringWidth(s, font, fontSize) - - def setLineCap(self,v): - if self._lineCap!=v: - self._lineCap = v - self.code.append('%d setlinecap'%v) - - def setLineJoin(self,v): - if self._lineJoin!=v: - self._lineJoin = v - self.code.append('%d setlinejoin'%v) - - def setDash(self, array=[], phase=0): - """Two notations. pass two numbers, or an array and phase""" - # copied and modified from reportlab.canvas - psoperation = "setdash" - if type(array) == types.IntType or type(array) == types.FloatType: - self._code.append('[%s %s] 0 %s' % (array, phase, psoperation)) - elif type(array) == types.ListType or type(array) == types.TupleType: - assert phase >= 0, "phase is a length in user space" - textarray = string.join(map(str, array)) - self.code.append('[%s] %s %s' % (textarray, phase, psoperation)) - - def setStrokeColor(self, color): - self._strokeColor = color - self.setColor(color) - - def setColor(self, color): - if self._color!=color: - self._color = color - if color: - if hasattr(color, "cyan"): - self.code.append('%s setcmykcolor' % fp_str(color.cyan, color.magenta, color.yellow, color.black)) - else: - self.code.append('%s setrgbcolor' % fp_str(color.red, color.green, color.blue)) - - def setFillColor(self, color): - self._fillColor = color - self.setColor(color) - - def setLineWidth(self, width): - if width != self._lineWidth: - self._lineWidth = width - self.code.append('%s setlinewidth' % width) - - def setFont(self,font,fontSize,leading=None): - if self._font!=font or self._fontSize!=fontSize: - self._fontCodeLoc = len(self.code) - self._font = font - self._fontSize = fontSize - self.code.append('') - - def line(self, x1, y1, x2, y2): - if self._strokeColor != None: - self.setColor(self._strokeColor) - self.code.append('%s m %s l stroke' % (fp_str(x1, y1), fp_str(x2, y2))) - - def _escape(self, s): - ''' - return a copy of string s with special characters in postscript strings - escaped with backslashes. - Have not handled characters that are converted normally in python strings - i.e. \n -> newline - ''' - str = string.replace(s, chr(0x5C), r'\\' ) - str = string.replace(str, '(', '\(' ) - str = string.replace(str, ')', '\)') - return str - - def drawString(self, x, y, s, angle=0): - if self._fillColor != None: - if not self.code[self._fontCodeLoc]: - psName = getFont(self._font).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) - self.setColor(self._fillColor) - s = self._escape(s) -## before inverting... -## if angle == 0 : # do special case of angle = 0 first. Avoids a bunch of gsave/grestore ops -## self.code.append('%s m 1 -1 scale (%s) show 1 -1 scale' % (fp_str(x,y),s)) -## else : # general case, rotated text -## self.code.append('gsave %s %s translate %s rotate' % (x,y,angle)) -## self.code.append('0 0 m 1 -1 scale (%s) show' % s) -## self.code.append('grestore') - if angle == 0 : # do special case of angle = 0 first. Avoids a bunch of gsave/grestore ops - self.code.append('%s m (%s) show ' % (fp_str(x,y),s)) - else : # general case, rotated text - self.code.append('gsave %s %s translate %s rotate' % (x,y,angle)) - self.code.append('0 0 m (%s) show' % s) - self.code.append('grestore') - - def drawCentredString(self, x, y, text, text_anchor='middle'): - if self._fillColor is not None: - textLen = stringWidth(text, self._font,self._fontSize) - if text_anchor=='end': - x -= textLen - elif text_anchor=='middle': - x -= textLen/2. - self.drawString(x,y,text) - - def drawRightString(self, text, x, y): - self.drawCentredString(text,x,y,text_anchor='end') - - def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0): - codeline = '%s m %s curveto' - data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4)) - if self._fillColor != None: - self.setColor(self._fillColor) - self.code.append((codeline % data) + ' eofill') - if self._strokeColor != None: - self.setColor(self._strokeColor) - self.code.append((codeline % data) - + ((closed and ' closepath') or '') - + ' stroke') - - ######################################################################################## - - def rect(self, x1,y1, x2,y2, stroke=1, fill=1): - "Draw a rectangle between x1,y1, and x2,y2" - # Path is drawn in counter-clockwise direction" - - x1, x2 = min(x1,x2), max(x1, x2) # from piddle.py - y1, y2 = min(y1,y2), max(y1, y2) - self.polygon(((x1,y1),(x2,y1),(x2,y2),(x1,y2)), closed=1, stroke=stroke, fill = fill) - - def roundRect(self, x1,y1, x2,y2, rx=8, ry=8): - """Draw a rounded rectangle between x1,y1, and x2,y2, - with corners inset as ellipses with x radius rx and y radius ry. - These should have x10, and ry>0.""" - # Path is drawn in counter-clockwise direction - - x1, x2 = min(x1,x2), max(x1, x2) # from piddle.py - y1, y2 = min(y1,y2), max(y1, y2) - - # Note: arcto command draws a line from current point to beginning of arc - # save current matrix, translate to center of ellipse, scale by rx ry, and draw - # a circle of unit radius in counterclockwise dir, return to original matrix - # arguments are (cx, cy, rx, ry, startAngle, endAngle) - ellipsePath = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s arc setmatrix' - - # choice between newpath and moveTo beginning of arc - # go with newpath for precision, does this violate any assumptions in code??? - rrCode = ['newpath'] # Round Rect code path - # upper left corner ellipse is first - rrCode.append(ellipsePath % (x1+rx, y1+ry, rx, -ry, 90, 180)) - rrCode.append(ellipsePath % (x1+rx, y2-ry, rx, -ry, 180, 270)) - rrCode.append(ellipsePath % (x2-rx, y2-ry, rx, -ry, 270, 360)) - rrCode.append(ellipsePath % (x2-rx, y1+ry, rx, -ry, 0, 90) ) - rrCode.append('closepath') - - self._fillAndStroke(rrCode) - - def ellipse(self, x1,y1, x2,y2): - """Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2. - These should have x1= 0: - arc='arc' - else: - arc='arcn' - data = (x,y, xScale, yScale, startAng, startAng+extent, arc) - - return codeline % data - - def polygon(self, p, closed=0, stroke=1, fill=1): - assert len(p) >= 2, 'Polygon must have 2 or more points' - - start = p[0] - p = p[1:] - - polyCode = [] - polyCode.append("%s m" % fp_str(start)) - for point in p: - polyCode.append("%s l" % fp_str(point)) - if closed: - polyCode.append("closepath") - - self._fillAndStroke(polyCode,stroke=stroke,fill=fill) - - def lines(self, lineList, color=None, width=None): - if self._strokeColor != None: - self._setColor(self._strokeColor) - codeline = '%s m %s l stroke' - for line in lineList: - self.code.append(codeline % (fp_str(line[0]),fp_str(line[1]))) - - def moveTo(self,x,y): - self.code.append('%s m' % fp_str(x, y)) - - def lineTo(self,x,y): - self.code.append('%s l' % fp_str(x, y)) - - def curveTo(self,x1,y1,x2,y2,x3,y3): - self.code.append('%s c' % fp_str(x1,y1,x2,y2,x3,y3)) - - def closePath(self): - self.code.append('closepath') - - def polyLine(self, p): - assert len(p) >= 1, 'Polyline must have 1 or more points' - if self._strokeColor != None: - self.setColor(self._strokeColor) - self.moveTo(p[0][0], p[0][1]) - for t in p[1:]: - self.lineTo(t[0], t[1]) - self.code.append('stroke') - - - def drawFigure(self, partList, closed=0): - figureCode = [] - first = 1 - - for part in partList: - op = part[0] - args = list(part[1:]) - - if op == figureLine: - if first: - first = 0 - figureCode.append("%s m" % fp_str(args[:2])) - else: - figureCode.append("%s l" % fp_str(args[:2])) - figureCode.append("%s l" % fp_str(args[2:])) - - elif op == figureArc: - first = 0 - x1,y1,x2,y2,startAngle,extent = args[:6] - figureCode.append(self._genArcCode(x1,y1,x2,y2,startAngle,extent)) - - elif op == figureCurve: - if first: - first = 0 - figureCode.append("%s m" % fp_str(args[:2])) - else: - figureCode.append("%s l" % fp_str(args[:2])) - figureCode.append("%s curveto" % fp_str(args[2:])) - else: - raise TypeError, "unknown figure operator: "+op - - if closed: - figureCode.append("closepath") - self._fillAndStroke(figureCode) - - def _fillAndStroke(self,code,clip=0,fill=1,stroke=1): - fill = self._fillColor and fill - stroke = self._strokeColor and stroke - if fill or stroke or clip: - self.code.extend(code) - if fill: - if stroke or clip: self.code.append("gsave") - self.setColor(self._fillColor) - self.code.append("eofill") - if stroke or clip: self.code.append("grestore") - if stroke: - if clip: self.code.append("gsave") - self.setColor(self._strokeColor) - self.code.append("stroke") - if clip: self.code.append("grestore") - if clip: - self.code.append("clip") - self.code.append("newpath") - - - def translate(self,x,y): - self.code.append('%s translate' % fp_str(x,y)) - - def scale(self,x,y): - self.code.append('%s scale' % fp_str(x,y)) - - def transform(self,a,b,c,d,e,f): - self.code.append('[%s] concat' % fp_str(a,b,c,d,e,f)) - - def _drawTimeResize(self,w,h): - '''if this is used we're probably in the wrong world''' - self.width, self.height = w, h - - ############################################################################################ - # drawImage(self. image, x1, y1, x2=None, y2=None) is now defined by either _drawImageLevel1 - # ._drawImageLevel2, the choice is made in .__init__ depending on option - def _drawImageLevel1(self, image, x1, y1, x2=None,y2=None): - # Postscript Level1 version available for fallback mode when Level2 doesn't work - """drawImage(self,image,x1,y1,x2=None,y2=None) : If x2 and y2 are ommitted, they are - calculated from image size. (x1,y1) is upper left of image, (x2,y2) is lower right of - image in piddle coordinates.""" - # For now let's start with 24 bit RGB images (following piddlePDF again) - component_depth = 8 - myimage = image.convert('RGB') - imgwidth, imgheight = myimage.size - if not x2: - x2 = imgwidth + x1 - if not y2: - y2 = y1 + imgheight - drawwidth = x2 - x1 - drawheight = y2 - y1 - #print 'Image size (%d, %d); Draw size (%d, %d)' % (imgwidth, imgheight, drawwidth, drawheight) - # now I need to tell postscript how big image is - - # "image operators assume that they receive sample data from - # their data source in x-axis major index order. The coordinate - # of the lower-left corner of the first sample is (0,0), of the - # second (1,0) and so on" -PS2 ref manual p. 215 - # - # The ImageMatrix maps unit squre of user space to boundary of the source image - # - - # The CurrentTransformationMatrix (CTM) maps the unit square of - # user space to the rect...on the page that is to receive the - # image. A common ImageMatrix is [width 0 0 -height 0 height] - # (for a left to right, top to bottom image ) - - # first let's map the user coordinates start at offset x1,y1 on page - - self.code.extend([ - 'gsave', - '%s %s translate' % (x1,-y1 - drawheight), # need to start are lower left of image - '%s %s scale' % (drawwidth,drawheight), - '/scanline %d 3 mul string def' % imgwidth # scanline by multiples of image width - ]) - - # now push the dimensions and depth info onto the stack - # and push the ImageMatrix to map the source to the target rectangle (see above) - # finally specify source (PS2 pp. 225 ) and by exmample - self.code.extend([ - '%s %s %s' % (imgwidth, imgheight, component_depth), - '[%s %s %s %s %s %s]' % (imgwidth, 0, 0, -imgheight, 0, imgheight), - '{ currentfile scanline readhexstring pop } false 3', - 'colorimage ' - ]) - - # data source output--now we just need to deliver a hex encode - # series of lines of the right overall size can follow - # piddlePDF again - - rawimage = myimage.tostring() - assert(len(rawimage) == imgwidth*imgheight, 'Wrong amount of data for image') - #compressed = zlib.compress(rawimage) # no zlib at moment - hex_encoded = self._AsciiHexEncode(rawimage) - - # write in blocks of 78 chars per line - outstream = getStringIO(hex_encoded) - - dataline = outstream.read(78) - while dataline <> "": - self.code.append(dataline) - dataline= outstream.read(78) - self.code.append('% end of image data') # for clarity - self.code.append('grestore') # return coordinates to normal - - # end of drawImage - def _AsciiHexEncode(self, input): # also based on piddlePDF - "Helper function used by images" - output = getStringIO() - for char in input: - output.write('%02x' % ord(char)) - return output.getvalue() - - def _drawImageLevel2(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version - '''At present we're handling only PIL''' - ### what sort of image are we to draw - if image.mode=='L' : - imBitsPerComponent = 8 - imNumComponents = 1 - myimage = image - elif image.mode == '1': - myimage = image.convert('L') - imNumComponents = 1 - myimage = image - else : - myimage = image.convert('RGB') - imNumComponents = 3 - imBitsPerComponent = 8 - - imwidth, imheight = myimage.size - if not x2: - x2 = imwidth + x1 - if not y2: - y2 = y1 + imheight - drawwidth = x2 - x1 - drawheight = y2 - y1 - self.code.extend([ - 'gsave', - '%s %s translate' % (x1,-y1 - drawheight), # need to start are lower left of image - '%s %s scale' % (drawwidth,drawheight)]) - - if imNumComponents == 3 : - self.code.append('/DeviceRGB setcolorspace') - elif imNumComponents == 1 : - self.code.append('/DeviceGray setcolorspace') - # create the image dictionary - self.code.append(""" -<< -/ImageType 1 -/Width %d /Height %d %% dimensions of source image -/BitsPerComponent %d""" % (imwidth, imheight, imBitsPerComponent) ) - - if imNumComponents == 1: - self.code.append('/Decode [0 1]') - if imNumComponents == 3: - self.code.append('/Decode [0 1 0 1 0 1] %% decode color values normally') - - self.code.extend([ '/ImageMatrix [%s 0 0 %s 0 %s]' % (imwidth, -imheight, imheight), - '/DataSource currentfile /ASCIIHexDecode filter', - '>> % End image dictionary', - 'image']) - # after image operator just need to dump image dat to file as hexstring - rawimage = myimage.tostring() - assert(len(rawimage) == imwidth*imheight, 'Wrong amount of data for image') - #compressed = zlib.compress(rawimage) # no zlib at moment - hex_encoded = self._AsciiHexEncode(rawimage) - - # write in blocks of 78 chars per line - outstream = getStringIO(hex_encoded) - - dataline = outstream.read(78) - while dataline <> "": - self.code.append(dataline) - dataline= outstream.read(78) - self.code.append('> % end of image data') # > is EOD for hex encoded filterfor clarity - self.code.append('grestore') # return coordinates to normal - -# renderpdf - draws them onto a canvas -"""Usage: - from reportlab.graphics import renderPS - renderPS.draw(drawing, canvas, x, y) -Execute the script to see some test drawings.""" -from shapes import * - -# hack so we only get warnings once each -#warnOnce = WarnOnce() - -# the main entry point for users... -def draw(drawing, canvas, x=0, y=0, showBoundary=rl_config.showBoundary): - """As it says""" - R = _PSRenderer() - R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary) - -def _pointsFromList(L): - ''' - given a list of coordinates [x0, y0, x1, y1....] - produce a list of points [(x0,y0), (y1,y0),....] - ''' - P=[] - for i in range(0,len(L),2): - P.append((L[i],L[i+1])) - return P - -class _PSRenderer(Renderer): - """This draws onto a EPS document. It needs to be a class - rather than a function, as some EPS-specific state tracking is - needed outside of the state info in the SVG model.""" - - def __init__(self): - self._tracker = StateTracker() - - def drawNode(self, node): - """This is the recursive method called for each node - in the tree""" - self._canvas.comment('begin node %s'%`node`) - color = self._canvas._color - if not (isinstance(node, Path) and node.isClipPath): - self._canvas.saveState() - - #apply state changes - deltas = getStateDelta(node) - self._tracker.push(deltas) - self.applyStateChanges(deltas, {}) - - #draw the object, or recurse - self.drawNodeDispatcher(node) - - rDeltas = self._tracker.pop() - if not (isinstance(node, Path) and node.isClipPath): - self._canvas.restoreState() - self._canvas.comment('end node %s'%`node`) - self._canvas._color = color - - #restore things we might have lost (without actually doing anything). - for k, v in rDeltas.items(): - if self._restores.has_key(k): - setattr(self._canvas,self._restores[k],v) - -## _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap', -## 'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font', -## 'font_size':'_fontSize'} - _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', - 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', - 'fontSize':'_fontSize'} - - def drawRect(self, rect): - if rect.rx == rect.ry == 0: - #plain old rectangle - self._canvas.rect( - rect.x, rect.y, - rect.x+rect.width, rect.y+rect.height) - else: - #cheat and assume ry = rx; better to generalize - #pdfgen roundRect function. TODO - self._canvas.roundRect( - rect.x, rect.y, - rect.x+rect.width, rect.y+rect.height, rect.rx, rect.ry - ) - - def drawLine(self, line): - if self._canvas._strokeColor: - self._canvas.line(line.x1, line.y1, line.x2, line.y2) - - def drawCircle(self, circle): - self._canvas.circle( circle.cx, circle.cy, circle.r) - - def drawWedge(self, wedge): - yradius, radius1, yradius1 = wedge._xtraRadii() - if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None): - startangledegrees = wedge.startangledegrees - endangledegrees = wedge.endangledegrees - centerx= wedge.centerx - centery = wedge.centery - radius = wedge.radius - extent = endangledegrees - startangledegrees - self._canvas.drawArc(centerx-radius, centery-yradius, centerx+radius, centery+yradius, - startangledegrees, extent, fromcenter=1) - else: - self.drawPolygon(wedge.asPolygon()) - - def drawPolyLine(self, p): - if self._canvas._strokeColor: - self._canvas.polyLine(_pointsFromList(p.points)) - - def drawEllipse(self, ellipse): - #need to convert to pdfgen's bounding box representation - x1 = ellipse.cx - ellipse.rx - x2 = ellipse.cx + ellipse.rx - y1 = ellipse.cy - ellipse.ry - y2 = ellipse.cy + ellipse.ry - self._canvas.ellipse(x1,y1,x2,y2) - - def drawPolygon(self, p): - self._canvas.polygon(_pointsFromList(p.points), closed=1) - - def drawString(self, stringObj): - if self._canvas._fillColor: - S = self._tracker.getState() - text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text - if not text_anchor in ['start','inherited']: - font, fontSize = S['fontName'], S['fontSize'] - textLen = stringWidth(text, font,fontSize) - if text_anchor=='end': - x = x-textLen - elif text_anchor=='middle': - x = x - textLen/2 - else: - raise ValueError, 'bad value for text_anchor '+str(text_anchor) - self._canvas.drawString(x,y,text) - - def drawPath(self, path): - from reportlab.graphics.shapes import _renderPath - c = self._canvas - drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) - isClosed = _renderPath(path, drawFuncs) - if not isClosed: - c._fillColor = None - c._fillAndStroke([], clip=path.isClipPath) - - def applyStateChanges(self, delta, newState): - """This takes a set of states, and outputs the operators - needed to set those properties""" - for key, value in delta.items(): - if key == 'transform': - self._canvas.transform(value[0], value[1], value[2], - value[3], value[4], value[5]) - elif key == 'strokeColor': - #this has different semantics in PDF to SVG; - #we always have a color, and either do or do - #not apply it; in SVG one can have a 'None' color - self._canvas.setStrokeColor(value) - elif key == 'strokeWidth': - self._canvas.setLineWidth(value) - elif key == 'strokeLineCap': #0,1,2 - self._canvas.setLineCap(value) - elif key == 'strokeLineJoin': - self._canvas.setLineJoin(value) - elif key == 'strokeDashArray': - if value: - self._canvas.setDash(value) - else: - self._canvas.setDash() -## elif key == 'stroke_opacity': -## warnOnce('Stroke Opacity not supported yet') - elif key == 'fillColor': - #this has different semantics in PDF to SVG; - #we always have a color, and either do or do - #not apply it; in SVG one can have a 'None' color - self._canvas.setFillColor(value) -## elif key == 'fill_rule': -## warnOnce('Fill rules not done yet') -## elif key == 'fill_opacity': -## warnOnce('Fill opacity not done yet') - elif key in ['fontSize', 'fontName']: - # both need setting together in PDF - # one or both might be in the deltas, - # so need to get whichever is missing - fontname = delta.get('fontName', self._canvas._font) - fontsize = delta.get('fontSize', self._canvas._fontSize) - self._canvas.setFont(fontname, fontsize) - - def drawImage(self, image): - from reportlab.lib.utils import ImageReader - im = ImageReader(image.path) - x0 = image.x - y0 = image.y - x1 = image.width - if x1 is not None: x1 += x0 - y1 = image.height - if y1 is not None: y1 += y0 - self._canvas.drawImage(im._image,x0,y0,x1,y1) - -def drawToFile(d,fn, showBoundary=rl_config.showBoundary): - d = renderScaledDrawing(d) - c = PSCanvas((d.width,d.height)) - draw(d, c, 0, 0, showBoundary=showBoundary) - c.save(fn) - -def drawToString(d, showBoundary=rl_config.showBoundary): - "Returns a PS as a string in memory, without touching the disk" - s = getStringIO() - drawToFile(d, s, showBoundary=showBoundary) - return s.getvalue() - -######################################################### -# -# test code. First, defin a bunch of drawings. -# 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 = [] - - 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)) - - i = 0 - for (d, docstring) in drawings: - filename = outdir + os.sep + 'renderPS_%d.eps'%i - drawToFile(d,filename) - print 'saved', filename - i = i + 1 - -if __name__=='__main__': - import sys - if len(sys.argv)>1: - outdir = sys.argv[1] - else: - outdir = 'epsout' - test(outdir) diff --git a/bin/reportlab/graphics/renderSVG.py b/bin/reportlab/graphics/renderSVG.py deleted file mode 100644 index 73aaddfc16d..00000000000 --- a/bin/reportlab/graphics/renderSVG.py +++ /dev/null @@ -1,828 +0,0 @@ -"""An experimental SVG renderer for the ReportLab graphics framework. - -This will create SVG code from the ReportLab Graphics API (RLG). -To read existing SVG code and convert it into ReportLab graphics -objects download the svglib module here: - - http://python.net/~gherman/#svglib -""" - -import math, string, types, sys, os -from types import StringType -from operator import getitem - -from reportlab.pdfbase.pdfmetrics import stringWidth # for font info -from reportlab.lib.utils import fp_str -from reportlab.lib.colors import black -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 - -from xml.dom import getDOMImplementation - - -### some constants ### - -sin = math.sin -cos = math.cos -pi = math.pi - -LINE_STYLES = 'stroke-width stroke-linecap stroke fill stroke-dasharray' -TEXT_STYLES = 'font-family font-size' - - -### top-level user function ### - -def drawToString(d, showBoundary=rl_config.showBoundary): - "Returns a SVG as a string in memory, without touching the disk" - s = getStringIO() - drawToFile(d, s, showBoundary=showBoundary) - return s.getvalue() - -def drawToFile(d, fn, showBoundary=rl_config.showBoundary): - d = renderScaledDrawing(d) - c = SVGCanvas((d.width, d.height)) - draw(d, c, 0, 0, showBoundary=showBoundary) - c.save(fn) - - -def draw(drawing, canvas, x=0, y=0, showBoundary=rl_config.showBoundary): - """As it says.""" - r = _SVGRenderer() - r.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary) - - -### helper functions ### - -def _pointsFromList(L): - """ - given a list of coordinates [x0, y0, x1, y1....] - produce a list of points [(x0,y0), (y1,y0),....] - """ - - P=[] - for i in range(0,len(L), 2): - P.append((L[i], L[i+1])) - - return P - - -def transformNode(doc, newTag, node=None, **attrDict): - """Transform a DOM node into new node and copy selected attributes. - - Creates a new DOM node with tag name 'newTag' for document 'doc' - and copies selected attributes from an existing 'node' as provided - in 'attrDict'. The source 'node' can be None. Attribute values will - be converted to strings. - - E.g. - - n = transformNode(doc, "node1", x="0", y="1") - -> DOM node for - - n = transformNode(doc, "node1", x=0, y=1+1) - -> DOM node for - - n = transformNode(doc, "node1", node0, x="x0", y="x0", zoo=bar()) - -> DOM node for - """ - - newNode = doc.createElement(newTag) - for newAttr, attr in attrDict.items(): - sattr = str(attr) - if not node: - newNode.setAttribute(newAttr, sattr) - else: - attrVal = node.getAttribute(sattr) - newNode.setAttribute(newAttr, attrVal or sattr) - - return newNode - - -### classes ### - -class SVGCanvas: - def __init__(self, size=(300,300)): - self.verbose = 0 - self.width, self.height = self.size = size - # self.height = size[1] - self.code = [] - self.style = {} - self.path = '' - self._strokeColor = self._fillColor = self._lineWidth = \ - self._font = self._fontSize = self._lineCap = \ - self._lineJoin = self._color = None - - implementation = getDOMImplementation('minidom') - self.doc = implementation.createDocument(None, "svg", None) - self.svg = self.doc.documentElement - self.svg.setAttribute("width", str(size[0])) - self.svg.setAttribute("height", str(self.height)) - - title = self.doc.createElement('title') - text = self.doc.createTextNode('...') - title.appendChild(text) - self.svg.appendChild(title) - - desc = self.doc.createElement('desc') - text = self.doc.createTextNode('...') - desc.appendChild(text) - self.svg.appendChild(desc) - - self.setFont(STATE_DEFAULTS['fontName'], STATE_DEFAULTS['fontSize']) - self.setStrokeColor(STATE_DEFAULTS['strokeColor']) - self.setLineCap(2) - self.setLineJoin(0) - self.setLineWidth(1) - - # Add a rectangular clipping path identical to view area. - clipPath = transformNode(self.doc, "clipPath", id="clip") - clipRect = transformNode(self.doc, "rect", x=0, y=0, - width=self.width, height=self.height) - clipPath.appendChild(clipRect) - self.svg.appendChild(clipPath) - - self.groupTree = transformNode(self.doc, "g", - id="group", - transform="scale(1,-1) translate(0,-%d)" % self.height, - style="clip-path: url(#clip)") - self.svg.appendChild(self.groupTree) - self.currGroup = self.groupTree - - - def save(self, f=None): - if type(f) is StringType: - file = open(f, 'w') - else: - file = f - - file.write("""\ - -\n""") - - # use = self.doc.createElement('use') - # use.setAttribute("xlink:href", "#group") - # use.setAttribute("transform", "scale(1, -1)") - # self.svg.appendChild(use) - - result = self.svg.toprettyxml(indent=" ") - file.write(result) - - if file is not f: - file.close() - - - ### helpers ### - - def NOTUSED_stringWidth(self, s, font=None, fontSize=None): - """Return the logical width of the string if it were drawn - in the current font (defaults to self.font). - """ - - font = font or self._font - fontSize = fontSize or self._fontSize - - return stringWidth(s, font, fontSize) - - - def _formatStyle(self, include=''): - str = '' - include = string.split(include) - keys = self.style.keys() - if include: - #2.1-safe version of the line below follows: - #keys = filter(lambda k: k in include, keys) - tmp = [] - for word in keys: - if word in include: - tmp.append(word) - keys = tmp - - items = [] - for k in keys: - items.append((k, self.style[k])) - items = map(lambda i: "%s: %s"%(i[0], i[1]), items) - str = string.join(items, '; ') + ';' - - return str - - - def _escape(self, s): - """ - return a copy of string s with special characters in postscript strings - escaped with backslashes. - Have not handled characters that are converted normally in python strings - i.e. \n -> newline - """ - - str = string.replace(s, chr(0x5C), r'\\' ) - str = string.replace(str, '(', '\(' ) - str = string.replace(str, ')', '\)') - return str - - - def _genArcCode(self, x1, y1, x2, y2, startAng, extent): - """Calculate the path for an arc inscribed in rectangle defined - by (x1,y1),(x2,y2).""" - - return - - #calculate semi-minor and semi-major axes of ellipse - xScale = abs((x2-x1)/2.0) - yScale = abs((y2-y1)/2.0) - #calculate centre of ellipse - x, y = (x1+x2)/2.0, (y1+y2)/2.0 - - codeline = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s %s setmatrix' - - if extent >= 0: - arc='arc' - else: - arc='arcn' - data = (x,y, xScale, yScale, startAng, startAng+extent, arc) - - return codeline % data - - - def _fillAndStroke(self, code, clip=0): - path = transformNode(self.doc, "path", - d=self.path, style=self._formatStyle(LINE_STYLES)) - self.currGroup.appendChild(path) - self.path = '' - - return - - """ - if self._fillColor or self._strokeColor or clip: - self.code.extend(code) - if self._fillColor: - if self._strokeColor or clip: - self.code.append("gsave") - self.setColor(self._fillColor) - self.code.append("eofill") - if self._strokeColor or clip: - self.code.append("grestore") - if self._strokeColor != None: - if clip: self.code.append("gsave") - self.setColor(self._strokeColor) - self.code.append("stroke") - if clip: self.code.append("grestore") - if clip: - self.code.append("clip") - self.code.append("newpath") - """ - - - ### styles ### - - def setLineCap(self, v): - vals = {0:'butt', 1:'round', 2:'square'} - if self._lineCap != v: - self._lineCap = v - self.style['stroke-linecap'] = vals[v] - - - def setLineJoin(self, v): - vals = {0:'miter', 1:'round', 2:'bevel'} - if self._lineJoin != v: - self._lineJoin = v - self.style['stroke-linecap'] = vals[v] - - - def setDash(self, array=[], phase=0): - """Two notations. Pass two numbers, or an array and phase.""" - - join = string.join - if type(array) in (types.IntType, types.FloatType): - self.style['stroke-dasharray'] = join(map(str, ([array, phase])), ', ') - elif type(array) in (types.ListType, types.TupleType) and len(array) > 0: - assert phase >= 0, "phase is a length in user space" - self.style['stroke-dasharray'] = join(map(str, (array+[phase])), ', ') - - - def setStrokeColor(self, color): - self._strokeColor = color - self.setColor(color) - if color == None: - self.style['stroke'] = 'none' - else: - r, g, b = color.red, color.green, color.blue - self.style['stroke'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100) - - - def setColor(self, color): - if self._color != color: - self._color = color - - - def setFillColor(self, color): - self._fillColor = color - self.setColor(color) - if color == None: - self.style['fill'] = 'none' - else: - r, g, b = color.red, color.green, color.blue - self.style['fill'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100) - - - def setLineWidth(self, width): - if width != self._lineWidth: - self._lineWidth = width - self.style['stroke-width'] = width - - - def setFont(self, font, fontSize): - if self._font != font or self._fontSize != fontSize: - self._font, self._fontSize = (font, fontSize) - self.style['font-family'] = font - self.style['font-size'] = fontSize - - - ### shapes ### - - def rect(self, x1,y1, x2,y2, rx=8, ry=8): - "Draw a rectangle between x1,y1 and x2,y2." - - if self.verbose: print "+++ SVGCanvas.rect" - - rect = transformNode(self.doc, "rect", - x=x1, y=y1, width=x2-x1, height=y2-y1, - style=self._formatStyle(LINE_STYLES)) - - self.currGroup.appendChild(rect) - - - def roundRect(self, x1,y1, x2,y2, rx=8, ry=8): - """Draw a rounded rectangle between x1,y1 and x2,y2. - - Corners inset as ellipses with x-radius rx and y-radius ry. - These should have x10, and ry>0. - """ - - rect = transformNode(self.doc, "rect", - x=x1, y=y1, width=x2-x1, height=y2-y1, rx=rx, ry=ry, - style=self._formatStyle(LINE_STYLES)) - - self.currGroup.appendChild(rect) - - - def drawString(self, s, x, y, angle=0): - if self.verbose: print "+++ SVGCanvas.drawString" - - if self._fillColor != None: - self.setColor(self._fillColor) - s = self._escape(s) - st = self._formatStyle(TEXT_STYLES) - if angle != 0: - st = st + " rotate(%f %f %f);" % (angle, x, y) - st = st + " fill: %s;" % self.style['fill'] - text = transformNode(self.doc, "text", - x=x, y=y, style=st, - transform="translate(0,%d) scale(1,-1)" % (2*y)) - content = self.doc.createTextNode(s) - text.appendChild(content) - - self.currGroup.appendChild(text) - - def drawCentredString(self, s, x, y, angle=0,text_anchor='middle'): - if self.verbose: print "+++ SVGCanvas.drawCentredString" - - if self._fillColor != None: - if not text_anchor in ['start', 'inherited']: - textLen = stringWidth(s,self._font,self._fontSize) - if text_anchor=='end': - x -= textLen - elif text_anchor=='middle': - x -= textLen/2. - else: - raise ValueError, 'bad value for text_anchor ' + str(text_anchor) - self.drawString(x,y,text,angle=angle) - - def drawRightString(self, text, x, y, angle=0): - self.drawCentredString(text,x,y,angle=angle,text_anchor='end') - - def comment(self, data): - "Add a comment." - - comment = self.doc.createComment(data) - # self.currGroup.appendChild(comment) - - - def drawImage(self, image, x1, y1, x2=None, y2=None): - pass - - - def line(self, x1, y1, x2, y2): - if self._strokeColor != None: - if 0: # something is wrong with line in my SVG viewer... - line = transformNode(self.doc, "line", - x=x1, y=y1, x2=x2, y2=y2, - style=self._formatStyle(LINE_STYLES)) - self.currGroup.appendChild(line) - path = transformNode(self.doc, "path", - d="M %f,%f L %f,%f Z" % (x1,y1,x2,y2), - style=self._formatStyle(LINE_STYLES)) - self.currGroup.appendChild(path) - - - def ellipse(self, x1, y1, x2, y2): - """Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2. - - These should have x1=180, 0, mx, my) - else: - str = str + "M %f, %f A %f, %f %d %d %d %f, %f Z " % \ - (mx, my, rx, ry, 0, extent>=180, 0, mx, my) - - if fromcenter: - str = str + "L %f, %f Z " % (cx, cy) - - path = transformNode(self.doc, "path", - d=str, style=self._formatStyle()) - self.currGroup.appendChild(path) - - - def polygon(self, points, closed=0): - assert len(points) >= 2, 'Polygon must have 2 or more points' - - if self._strokeColor != None: - self.setColor(self._strokeColor) - pairs = [] - for i in xrange(len(points)): - pairs.append("%f %f" % (points[i])) - pts = string.join(pairs, ', ') - polyline = transformNode(self.doc, "polygon", - points=pts, style=self._formatStyle(LINE_STYLES)) - self.currGroup.appendChild(polyline) - - # self._fillAndStroke(polyCode) - - - def lines(self, lineList, color=None, width=None): - # print "### lineList", lineList - return - - if self._strokeColor != None: - self._setColor(self._strokeColor) - codeline = '%s m %s l stroke' - for line in lineList: - self.code.append(codeline % (fp_str(line[0]), fp_str(line[1]))) - - - def polyLine(self, points): - assert len(points) >= 1, 'Polyline must have 1 or more points' - - if self._strokeColor != None: - self.setColor(self._strokeColor) - pairs = [] - for i in xrange(len(points)): - pairs.append("%f %f" % (points[i])) - pts = string.join(pairs, ', ') - polyline = transformNode(self.doc, "polyline", - points=pts, style=self._formatStyle(LINE_STYLES)) - self.currGroup.appendChild(polyline) - - - ### groups ### - - def startGroup(self): - 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" - return currGroup - - def endGroup(self,currGroup): - if self.verbose: print "+++ begin SVGCanvas.endGroup" - self.currGroup = currGroup - 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 - 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): - self.currGroup.setAttribute("transform", "%s %s" % (tr, t)) - - - def translate(self, x, y): - # probably never used - print "!!! begin SVGCanvas.translate" - return - - tr = self.currGroup.getAttribute("transform") - t = 'translate(%f, %f)' % (x, y) - self.currGroup.setAttribute("transform", "%s %s" % (tr, t)) - - - def scale(self, x, y): - # probably never used - print "!!! begin SVGCanvas.scale" - return - - tr = self.groups[-1].getAttribute("transform") - t = 'scale(%f, %f)' % (x, y) - self.currGroup.setAttribute("transform", "%s %s" % (tr, t)) - - - ### paths ### - - def moveTo(self, x, y): - self.path = self.path + 'M %f %f ' % (x, y) - - - def lineTo(self, x, y): - self.path = self.path + 'L %f %f ' % (x, y) - - - def curveTo(self, x1, y1, x2, y2, x3, y3): - self.path = self.path + 'C %f %f %f %f %f %f ' % (x1, y1, x2, y2, x3, y3) - - - def closePath(self): - self.path = self.path + 'Z ' - - def saveState(self): - pass - - def restoreState(self): - pass - -class _SVGRenderer(Renderer): - """This draws onto an SVG document. - """ - - def __init__(self): - self._tracker = StateTracker() - self.verbose = 0 - - def drawNode(self, node): - """This is the recursive method called for each node in the tree. - """ - - if self.verbose: print "### begin _SVGRenderer.drawNode" - - self._canvas.comment('begin node %s'%`node`) - color = self._canvas._color - if not (isinstance(node, Path) and node.isClipPath): - pass # self._canvas.saveState() - - #apply state changes - deltas = getStateDelta(node) - self._tracker.push(deltas) - self.applyStateChanges(deltas, {}) - - #draw the object, or recurse - self.drawNodeDispatcher(node) - - rDeltas = self._tracker.pop() - if not (isinstance(node, Path) and node.isClipPath): - pass # self._canvas.restoreState() - self._canvas.comment('end node %s'%`node`) - self._canvas._color = color - - #restore things we might have lost (without actually doing anything). - for k, v in rDeltas.items(): - if self._restores.has_key(k): - setattr(self._canvas,self._restores[k],v) - - if self.verbose: print "### end _SVGRenderer.drawNode" - - _restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap', - 'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font', - 'fontSize':'_fontSize'} - - - def drawGroup(self, group): - if self.verbose: print "### begin _SVGRenderer.drawGroup" - - currGroup = self._canvas.startGroup() - a, b, c, d, e, f = self._tracker.getCTM() - for childNode in group.getContents(): - if isinstance(childNode, UserNode): - node2 = childNode.provideNode() - else: - node2 = childNode - self.drawNode(node2) - self._canvas.transform(a, b, c, d, e, f) - self._canvas.endGroup(currGroup) - - if self.verbose: print "### end _SVGRenderer.drawGroup" - - - def drawRect(self, rect): - if rect.rx == rect.ry == 0: - #plain old rectangle - self._canvas.rect( - rect.x, rect.y, - rect.x+rect.width, rect.y+rect.height) - else: - #cheat and assume ry = rx; better to generalize - #pdfgen roundRect function. TODO - self._canvas.roundRect( - rect.x, rect.y, - rect.x+rect.width, rect.y+rect.height, - rect.rx, rect.ry - ) - - - def drawString(self, stringObj): - if self._canvas._fillColor: - S = self._tracker.getState() - text_anchor, x, y, text = S['textAnchor'], stringObj.x, stringObj.y, stringObj.text - if not text_anchor in ['start', 'inherited']: - font, fontSize = S['fontName'], S['fontSize'] - textLen = stringWidth(text, font,fontSize) - if text_anchor=='end': - x = x-textLen - elif text_anchor=='middle': - x = x - textLen/2 - else: - raise ValueError, 'bad value for text_anchor ' + str(text_anchor) - self._canvas.drawString(text,x,y) - - - def drawLine(self, line): - if self._canvas._strokeColor: - self._canvas.line(line.x1, line.y1, line.x2, line.y2) - - - def drawCircle(self, circle): - self._canvas.circle( circle.cx, circle.cy, circle.r) - - - def drawWedge(self, wedge): - centerx, centery, radius, startangledegrees, endangledegrees = \ - wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees - yradius = wedge.yradius or wedge.radius - (x1, y1) = (centerx-radius, centery-yradius) - (x2, y2) = (centerx+radius, centery+yradius) - extent = endangledegrees - startangledegrees - self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1) - - - def drawPolyLine(self, p): - if self._canvas._strokeColor: - self._canvas.polyLine(_pointsFromList(p.points)) - - - def drawEllipse(self, ellipse): - #need to convert to pdfgen's bounding box representation - x1 = ellipse.cx - ellipse.rx - x2 = ellipse.cx + ellipse.rx - y1 = ellipse.cy - ellipse.ry - y2 = ellipse.cy + ellipse.ry - self._canvas.ellipse(x1,y1,x2,y2) - - - def drawPolygon(self, p): - self._canvas.polygon(_pointsFromList(p.points), closed=1) - - - def drawPath(self, path): - # print "### drawPath", path.points - from reportlab.graphics.shapes import _renderPath - c = self._canvas - drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath) - isClosed = _renderPath(path, drawFuncs) - if not isClosed: - c._fillColor = None - c._fillAndStroke([], clip=path.isClipPath) - - - def applyStateChanges(self, delta, newState): - """This takes a set of states, and outputs the operators - needed to set those properties""" - - for key, value in delta.items(): - if key == 'transform': - pass - #self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5]) - elif key == 'strokeColor': - self._canvas.setStrokeColor(value) - elif key == 'strokeWidth': - self._canvas.setLineWidth(value) - elif key == 'strokeLineCap': #0,1,2 - self._canvas.setLineCap(value) - elif key == 'strokeLineJoin': - self._canvas.setLineJoin(value) - elif key == 'strokeDashArray': - if value: - self._canvas.setDash(value) - else: - self._canvas.setDash() - elif key == 'fillColor': - self._canvas.setFillColor(value) - elif key in ['fontSize', 'fontName']: - fontname = delta.get('fontName', self._canvas._font) - fontsize = delta.get('fontSize', self._canvas._fontSize) - self._canvas.setFont(fontname, fontsize) - - - - -def test0(outdir='svgout'): - # 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 - from reportlab.graphics import testshapes - 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 + '()') - 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 - drawToFile(d, filename) - # print 'saved', filename - i = i + 1 - - -def test1(): - from reportlab.graphics.testshapes import getDrawing01 - d = getDrawing01() - drawToFile(d, "svgout/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") - - -if __name__=='__main__': - test0() - test1() - test2() diff --git a/bin/reportlab/graphics/renderbase.py b/bin/reportlab/graphics/renderbase.py deleted file mode 100644 index 10282e15af4..00000000000 --- a/bin/reportlab/graphics/renderbase.py +++ /dev/null @@ -1,351 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/renderbase.py -""" -Superclass for renderers to factor out common functionality and default implementations. -""" - - -__version__=''' $Id $ ''' - -from reportlab.graphics.shapes import * -from reportlab.lib.validators import DerivedValue -from reportlab import rl_config - -def inverse(A): - "For A affine 2D represented as 6vec return 6vec version of A**(-1)" - # I checked this RGB - det = float(A[0]*A[3] - A[2]*A[1]) - R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det] - return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]]) - -def mmult(A, B): - "A postmultiplied by B" - # I checked this RGB - # [a0 a2 a4] [b0 b2 b4] - # [a1 a3 a5] * [b1 b3 b5] - # [ 1 ] [ 1 ] - # - return (A[0]*B[0] + A[2]*B[1], - A[1]*B[0] + A[3]*B[1], - A[0]*B[2] + A[2]*B[3], - A[1]*B[2] + A[3]*B[3], - A[0]*B[4] + A[2]*B[5] + A[4], - A[1]*B[4] + A[3]*B[5] + A[5]) - - -def getStateDelta(shape): - """Used to compute when we need to change the graphics state. - For example, if we have two adjacent red shapes we don't need - 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(): - if STATE_DEFAULTS.has_key(prop): - delta[prop] = value - return delta - - -class StateTracker: - """Keeps a stack of transforms and state - properties. It can contain any properties you - want, but the keys 'transform' and 'ctm' have - special meanings. The getCTM() - method returns the current transformation - matrix at any point, without needing to - invert matrixes when you pop.""" - def __init__(self, defaults=None): - # one stack to keep track of what changes... - self._deltas = [] - - # and another to keep track of cumulative effects. Last one in - # list is the current graphics state. We put one in to simplify - # loops below. - self._combined = [] - if defaults is None: - defaults = STATE_DEFAULTS.copy() - #ensure that if we have a transform, we have a CTM - if defaults.has_key('transform'): - defaults['ctm'] = defaults['transform'] - self._combined.append(defaults) - - def push(self,delta): - """Take a new state dictionary of changes and push it onto - the stack. After doing this, the combined state is accessible - through getState()""" - - newstate = self._combined[-1].copy() - 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']) - #print 'statetracker transform = (%0.2f, %0.2f, %0.2f, %0.2f, %0.2f, %0.2f)' % tuple(newstate['transform']) - #print 'statetracker ctm = (%0.2f, %0.2f, %0.2f, %0.2f, %0.2f, %0.2f)' % tuple(newstate['ctm']) - - else: #just overwrite it - newstate[key] = value - - self._combined.append(newstate) - self._deltas.append(delta) - - def pop(self): - """steps back one, and returns a state dictionary with the - deltas to reverse out of wherever you are. Depending - on your back end, you may not need the return value, - since you can get the complete state afterwards with getState()""" - del self._combined[-1] - newState = self._combined[-1] - lastDelta = self._deltas[-1] - del self._deltas[-1] - #need to diff this against the last one in the state - reverseDelta = {} - #print 'pop()...' - for key, curValue in lastDelta.items(): - #print ' key=%s, value=%s' % (key, curValue) - prevValue = newState[key] - if prevValue <> curValue: - #print ' state popping "%s"="%s"' % (key, curValue) - if key == 'transform': - reverseDelta[key] = inverse(lastDelta['transform']) - else: #just return to previous state - reverseDelta[key] = prevValue - return reverseDelta - - def getState(self): - "returns the complete graphics state at this point" - return self._combined[-1] - - def getCTM(self): - "returns the current transformation matrix at this point""" - return self._combined[-1]['ctm'] - - def __getitem__(self,key): - "returns the complete graphics state value of key at this point" - return self._combined[-1][key] - - def __setitem__(self,key,value): - "sets the complete graphics state value of key to value" - self._combined[-1][key] = value - -def testStateTracker(): - print 'Testing state tracker' - defaults = {'fillColor':None, 'strokeColor':None,'fontName':None, 'transform':[1,0,0,1,0,0]} - deltas = [ - {'fillColor':'red'}, - {'fillColor':'green', 'strokeColor':'blue','fontName':'Times-Roman'}, - {'transform':[0.5,0,0,0.5,0,0]}, - {'transform':[0.5,0,0,0.5,2,3]}, - {'strokeColor':'red'} - ] - - st = StateTracker(defaults) - print 'initial:', st.getState() - print - for delta in deltas: - print 'pushing:', delta - st.push(delta) - print 'state: ',st.getState(),'\n' - - for delta in deltas: - print 'popping:',st.pop() - print 'state: ',st.getState(),'\n' - - -def _expandUserNode(node,canvas): - if isinstance(node, UserNode): - try: - if hasattr(node,'_canvas'): - ocanvas = 1 - else: - node._canvas = canvas - ocanvas = None - onode = node - node = node.provideNode() - finally: - if not ocanvas: del onode._canvas - return node - -def renderScaledDrawing(d): - renderScale = d.renderScale - if renderScale!=1.0: - d = d.copy() - d.width *= renderScale - d.height *= renderScale - d.scale(renderScale,renderScale) - d.renderScale = 1.0 - return d - -class Renderer: - """Virtual superclass for graphics renderers.""" - - def __init__(self): - self._tracker = StateTracker() - self._nodeStack = [] #track nodes visited - - def undefined(self, operation): - 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 - location. The recursive part is handled by drawNode.""" - #stash references for ease of communication - if showBoundary is rl_config._unset_: showBoundary=rl_config.showBoundary - self._canvas = canvas - canvas.__dict__['_drawing'] = self._drawing = drawing - drawing._parent = None - try: - #bounding box - if showBoundary: canvas.rect(x, y, drawing.width, drawing.height) - canvas.saveState() - self.initState(x,y) #this is the push() - self.drawNode(drawing) - self.pop() - canvas.restoreState() - finally: - #remove any circular references - del self._canvas, self._drawing, canvas._drawing, drawing._parent - - def initState(self,x,y): - deltas = STATE_DEFAULTS.copy() - deltas['transform'] = [1,0,0,1,x,y] - self._tracker.push(deltas) - self.applyStateChanges(deltas, {}) - - def pop(self): - self._tracker.pop() - - def drawNode(self, node): - """This is the recursive method called for each node - in the tree""" - # Undefined here, but with closer analysis probably can be handled in superclass - self.undefined("drawNode") - - def getStateValue(self, key): - """Return current state parameter for given key""" - currentState = self._tracker._combined[-1] - return currentState[key] - - def fillDerivedValues(self, node): - """Examine a node for any values which are Derived, - and replace them with their calculated values. - Generally things may look at the drawing or their - parent. - - """ - for (key, value) in node.__dict__.items(): - if isinstance(value, DerivedValue): - #just replace with default for key? - #print ' fillDerivedValues(%s)' % key - newValue = value.getValue(self, key) - #print ' got value of %s' % newValue - node.__dict__[key] = newValue - - def drawNodeDispatcher(self, node): - """dispatch on the node's (super) class: shared code""" - - canvas = getattr(self,'_canvas',None) - # replace UserNode with its contents - - try: - node = _expandUserNode(node,canvas) - if hasattr(node,'_canvas'): - ocanvas = 1 - else: - node._canvas = canvas - ocanvas = None - - self.fillDerivedValues(node) - #draw the object, or recurse - if isinstance(node, Line): - self.drawLine(node) - elif isinstance(node, Image): - self.drawImage(node) - elif isinstance(node, Rect): - self.drawRect(node) - elif isinstance(node, Circle): - self.drawCircle(node) - elif isinstance(node, Ellipse): - self.drawEllipse(node) - elif isinstance(node, PolyLine): - self.drawPolyLine(node) - elif isinstance(node, Polygon): - self.drawPolygon(node) - elif isinstance(node, Path): - self.drawPath(node) - elif isinstance(node, String): - self.drawString(node) - elif isinstance(node, Group): - self.drawGroup(node) - elif isinstance(node, Wedge): - self.drawWedge(node) - else: - print 'DrawingError','Unexpected element %s in drawing!' % str(node) - finally: - if not ocanvas: del node._canvas - - _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap', - 'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font', - 'font_size':'_fontSize'} - - def drawGroup(self, group): - # just do the contents. Some renderers might need to override this - # if they need a flipped transform - canvas = getattr(self,'_canvas',None) - for node in group.getContents(): - node = _expandUserNode(node,canvas) - - #here is where we do derived values - this seems to get everything. Touch wood. - self.fillDerivedValues(node) - try: - if hasattr(node,'_canvas'): - ocanvas = 1 - else: - node._canvas = canvas - ocanvas = None - node._parent = group - self.drawNode(node) - finally: - del node._parent - if not ocanvas: del node._canvas - - def drawWedge(self, wedge): - # by default ask the wedge to make a polygon of itself and draw that! - #print "drawWedge" - polygon = wedge.asPolygon() - self.drawPolygon(polygon) - - def drawPath(self, path): - polygons = path.asPolygons() - for polygon in polygons: - self.drawPolygon(polygon) - - def drawRect(self, rect): - # could be implemented in terms of polygon - self.undefined("drawRect") - - def drawLine(self, line): - self.undefined("drawLine") - - def drawCircle(self, circle): - self.undefined("drawCircle") - - def drawPolyLine(self, p): - self.undefined("drawPolyLine") - - def drawEllipse(self, ellipse): - self.undefined("drawEllipse") - - def drawPolygon(self, p): - self.undefined("drawPolygon") - - def drawString(self, stringObj): - self.undefined("drawString") - - def applyStateChanges(self, delta, newState): - """This takes a set of states, and outputs the operators - needed to set those properties""" - self.undefined("applyStateChanges") - -if __name__=='__main__': - print "this file has no script interpretation" - print __doc__ diff --git a/bin/reportlab/graphics/samples/__init__.py b/bin/reportlab/graphics/samples/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/bin/reportlab/graphics/samples/bubble.py b/bin/reportlab/graphics/samples/bubble.py deleted file mode 100644 index 64bf3137912..00000000000 --- a/bin/reportlab/graphics/samples/bubble.py +++ /dev/null @@ -1,73 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class Bubble(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.lines.symbol.kind ='Circle' - self.chart.lines.symbol.size = 15 - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((100,100), (200,200), (250,210), (300,300), (350,450))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.lineLabelFormat = None - self.chart.xLabel = 'X Axis' - self.chart.y = 30 - self.chart.yLabel = 'Y Axis' - self.chart.yValueAxis.labelTextFormat = '%d' - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - - - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - Bubble().save(formats=['pdf'],outDir=None,fnRoot='bubble') diff --git a/bin/reportlab/graphics/samples/clustered_bar.py b/bin/reportlab/graphics/samples/clustered_bar.py deleted file mode 100644 index 4d8c363eb29..00000000000 --- a/bin/reportlab/graphics/samples/clustered_bar.py +++ /dev/null @@ -1,84 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -from excelcolors import * -from reportlab.graphics.charts.barcharts import HorizontalBarChart -from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String -from reportlab.graphics.charts.textlabels import Label - -class ClusteredBar(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.bars[0].fillColor = color01 - self.chart.bars[1].fillColor = color02 - self.chart.bars[2].fillColor = color03 - self.chart.bars[3].fillColor = color04 - self.chart.bars[4].fillColor = color05 - self.chart.bars[5].fillColor = color06 - self.chart.bars[6].fillColor = color07 - self.chart.bars[7].fillColor = color08 - self.chart.bars[8].fillColor = color09 - self.chart.bars[9].fillColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.barLabels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontSize = 6 - self.chart.valueAxis.forceZero = 1 - self.chart.data = [(100, 150, 180), (125, 180, 200)] - self.chart.groupSpacing = 15 - self.chart.valueAxis.avoidBoundFrac = 1 - self.chart.valueAxis.gridEnd = 80 - self.chart.valueAxis.tickDown = 3 - self.chart.valueAxis.visibleGrid = 1 - self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central'] - self.chart.categoryAxis.tickLeft = 3 - self.chart.categoryAxis.labels.fontName = 'Helvetica' - self.chart.categoryAxis.labels.fontSize = 6 - self.chart.categoryAxis.labels.dx = -3 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - ClusteredBar().save(formats=['pdf'],outDir=None,fnRoot='clustered_bar') \ No newline at end of file diff --git a/bin/reportlab/graphics/samples/clustered_column.py b/bin/reportlab/graphics/samples/clustered_column.py deleted file mode 100644 index 8ea9542eadc..00000000000 --- a/bin/reportlab/graphics/samples/clustered_column.py +++ /dev/null @@ -1,83 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -from excelcolors import * -from reportlab.graphics.charts.barcharts import VerticalBarChart -from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String -from reportlab.graphics.charts.textlabels import Label - -class ClusteredColumn(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.bars[0].fillColor = color01 - self.chart.bars[1].fillColor = color02 - self.chart.bars[2].fillColor = color03 - self.chart.bars[3].fillColor = color04 - self.chart.bars[4].fillColor = color05 - self.chart.bars[5].fillColor = color06 - self.chart.bars[6].fillColor = color07 - self.chart.bars[7].fillColor = color08 - self.chart.bars[8].fillColor = color09 - self.chart.bars[9].fillColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.barLabels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontSize = 7 - self.chart.valueAxis.forceZero = 1 - self.chart.data = [(100, 150, 180), (125, 180, 200)] - self.chart.groupSpacing = 15 - self.chart.valueAxis.avoidBoundFrac = 1 - self.chart.valueAxis.gridEnd = 115 - self.chart.valueAxis.tickLeft = 3 - self.chart.valueAxis.visibleGrid = 1 - self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central'] - self.chart.categoryAxis.tickDown = 3 - self.chart.categoryAxis.labels.fontName = 'Helvetica' - self.chart.categoryAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - ClusteredColumn().save(formats=['pdf'],outDir=None,fnRoot='clustered_column') diff --git a/bin/reportlab/graphics/samples/excelcolors.py b/bin/reportlab/graphics/samples/excelcolors.py deleted file mode 100644 index f3f4f5b6950..00000000000 --- a/bin/reportlab/graphics/samples/excelcolors.py +++ /dev/null @@ -1,45 +0,0 @@ -# define standard colors to mimic those used by Microsoft Excel -from reportlab.lib.colors import CMYKColor, PCMYKColor - -#colour names as comments at the end of each line are as a memory jogger ONLY -#NOT HTML named colours! - -#Main colours as used for bars etc -color01 = PCMYKColor(40,40,0,0) # Lavender -color02 = PCMYKColor(0,66,33,39) # Maroon -color03 = PCMYKColor(0,0,20,0) # Yellow -color04 = PCMYKColor(20,0,0,0) # Cyan -color05 = PCMYKColor(0,100,0,59) # Purple -color06 = PCMYKColor(0,49,49,0) # Salmon -color07 = PCMYKColor(100,49,0,19) # Blue -color08 = PCMYKColor(20,20,0,0) # PaleLavender -color09 = PCMYKColor(100,100,0,49) # NavyBlue -color10 = PCMYKColor(0,100,0,0) # Purple - -#Highlight colors - eg for the tops of bars -color01Light = PCMYKColor(39,39,0,25) # Light Lavender -color02Light = PCMYKColor(0,66,33,54) # Light Maroon -color03Light = PCMYKColor(0,0,19,25) # Light Yellow -color04Light = PCMYKColor(19,0,0,25) # Light Cyan -color05Light = PCMYKColor(0,100,0,69) # Light Purple -color06Light = PCMYKColor(0,49,49,25) # Light Salmon -color07Light = PCMYKColor(100,49,0,39) # Light Blue -color08Light = PCMYKColor(19,19,0,25) # Light PaleLavender -color09Light = PCMYKColor(100,100,0,62) # Light NavyBlue -color10Light = PCMYKColor(0,100,0,25) # Light Purple - -#Lowlight colors - eg for the sides of bars -color01Dark = PCMYKColor(39,39,0,49) # Dark Lavender -color02Dark = PCMYKColor(0,66,33,69) # Dark Maroon -color03Dark = PCMYKColor(0,0,20,49) # Dark Yellow -color04Dark = PCMYKColor(20,0,0,49) # Dark Cyan -color05Dark = PCMYKColor(0,100,0,80) # Dark Purple -color06Dark = PCMYKColor(0,50,50,49) # Dark Salmon -color07Dark = PCMYKColor(100,50,0,59) # Dark Blue -color08Dark = PCMYKColor(20,20,0,49) # Dark PaleLavender -color09Dark = PCMYKColor(100,100,0,79) # Dark NavyBlue -color10Dark = PCMYKColor(0,100,0,49) # Dark Purple - -#for standard grey backgrounds -backgroundGrey = PCMYKColor(0,0,0,24) - diff --git a/bin/reportlab/graphics/samples/exploded_pie.py b/bin/reportlab/graphics/samples/exploded_pie.py deleted file mode 100644 index 8076493bd82..00000000000 --- a/bin/reportlab/graphics/samples/exploded_pie.py +++ /dev/null @@ -1,65 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.piecharts import Pie -from excelcolors import * -from reportlab.graphics.widgets.grids import ShadedRect -from reportlab.graphics.charts.legends import Legend -from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String -from reportlab.graphics.charts.textlabels import Label - -class ExplodedPie(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,Pie(),name='chart',validate=None,desc="The main chart") - self.chart.width = 100 - self.chart.height = 100 - self.chart.x = 25 - self.chart.y = 25 - self.chart.slices[0].fillColor = color01 - self.chart.slices[1].fillColor = color02 - self.chart.slices[2].fillColor = color03 - self.chart.slices[3].fillColor = color04 - self.chart.slices[4].fillColor = color05 - self.chart.slices[5].fillColor = color06 - self.chart.slices[6].fillColor = color07 - self.chart.slices[7].fillColor = color08 - self.chart.slices[8].fillColor = color09 - self.chart.slices[9].fillColor = color10 - self.chart.data = (100, 150, 180) - self.chart.startAngle = -90 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'North'), (color02, 'South'), (color03, 'Central')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 160 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.Legend.columnMaximum = 10 - self.chart.slices.strokeWidth = 1 - self.chart.slices.fontName = 'Helvetica' - self.background = ShadedRect() - self.background.fillColorStart = backgroundGrey - self.background.fillColorEnd = backgroundGrey - self.background.numShades = 1 - self.background.strokeWidth = 0.5 - self.background.x = 20 - self.background.y = 20 - self.chart.slices.popout = 5 - self.background.height = 110 - self.background.width = 110 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - ExplodedPie().save(formats=['pdf'],outDir=None,fnRoot='exploded_pie') \ No newline at end of file diff --git a/bin/reportlab/graphics/samples/filled_radar.py b/bin/reportlab/graphics/samples/filled_radar.py deleted file mode 100644 index 8439aa235c2..00000000000 --- a/bin/reportlab/graphics/samples/filled_radar.py +++ /dev/null @@ -1,54 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class FilledRadarChart(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 90 - self.chart.height = 90 - self.chart.x = 45 - self.chart.y = 25 - self.chart.strands[0].fillColor = color01 - self.chart.strands[1].fillColor = color02 - self.chart.strands[2].fillColor = color03 - self.chart.strands[3].fillColor = color04 - self.chart.strands[4].fillColor = color05 - self.chart.strands[5].fillColor = color06 - self.chart.strands[6].fillColor = color07 - self.chart.strands[7].fillColor = color08 - self.chart.strands[8].fillColor = color09 - self.chart.strands[9].fillColor = color10 - self.chart.strandLabels.fontName = 'Helvetica' - self.chart.strandLabels.fontSize = 6 - self.chart.fillColor = backgroundGrey - self.chart.data = [(125, 180, 200), (100, 150, 180)] - self.chart.labels = ['North', 'South', 'Central'] - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - FilledRadarChart().save(formats=['pdf'],outDir=None,fnRoot='filled_radar') diff --git a/bin/reportlab/graphics/samples/line_chart.py b/bin/reportlab/graphics/samples/line_chart.py deleted file mode 100644 index 49563a94766..00000000000 --- a/bin/reportlab/graphics/samples/line_chart.py +++ /dev/null @@ -1,83 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class LineChart(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((0, 50), (100,100), (200,200), (250,210), (300,300), (400,500)), ((0, 150), (100,200), (200,300), (250,200), (300,400), (400, 600))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - LineChart().save(formats=['pdf'],outDir=None,fnRoot='line_chart') diff --git a/bin/reportlab/graphics/samples/linechart_with_markers.py b/bin/reportlab/graphics/samples/linechart_with_markers.py deleted file mode 100644 index 2875cecb5d7..00000000000 --- a/bin/reportlab/graphics/samples/linechart_with_markers.py +++ /dev/null @@ -1,94 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -from reportlab.graphics.charts.lineplots import LinePlot -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 * - -class LineChartWithMarkers(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.lines[0].symbol = makeMarker('FilledSquare') - self.chart.lines[1].symbol = makeMarker('FilledDiamond') - self.chart.lines[2].symbol = makeMarker('FilledStarFive') - self.chart.lines[3].symbol = makeMarker('FilledTriangle') - self.chart.lines[4].symbol = makeMarker('FilledCircle') - self.chart.lines[5].symbol = makeMarker('FilledPentagon') - self.chart.lines[6].symbol = makeMarker('FilledStarSix') - self.chart.lines[7].symbol = makeMarker('FilledHeptagon') - self.chart.lines[8].symbol = makeMarker('FilledOctagon') - self.chart.lines[9].symbol = makeMarker('FilledCross') - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((0, 50), (100,100), (200,200), (250,210), (300,300), (400,500)), ((0, 150), (100,200), (200,300), (250,200), (300,400), (400, 600))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - LineChartWithMarkers().save(formats=['pdf'],outDir=None,fnRoot='linechart_with_markers') diff --git a/bin/reportlab/graphics/samples/radar.py b/bin/reportlab/graphics/samples/radar.py deleted file mode 100644 index 6d4d8d7b8e7..00000000000 --- a/bin/reportlab/graphics/samples/radar.py +++ /dev/null @@ -1,66 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -from excelcolors import * -from reportlab.graphics.charts.spider import SpiderChart -from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String -from reportlab.graphics.charts.textlabels import Label - -class RadarChart(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 90 - self.chart.height = 90 - self.chart.x = 45 - self.chart.y = 25 - self.chart.strands[0].strokeColor= color01 - self.chart.strands[1].strokeColor= color02 - self.chart.strands[2].strokeColor= color03 - self.chart.strands[3].strokeColor= color04 - self.chart.strands[4].strokeColor= color05 - self.chart.strands[5].strokeColor= color06 - self.chart.strands[6].strokeColor= color07 - self.chart.strands[7].strokeColor= color08 - self.chart.strands[8].strokeColor= color09 - self.chart.strands[9].strokeColor= color10 - self.chart.strands[0].fillColor = None - self.chart.strands[1].fillColor = None - self.chart.strands[2].fillColor = None - self.chart.strands[3].fillColor = None - self.chart.strands[4].fillColor = None - self.chart.strands[5].fillColor = None - self.chart.strands[6].fillColor = None - self.chart.strands[7].fillColor = None - self.chart.strands[8].fillColor = None - self.chart.strands[9].fillColor = None - self.chart.strands.strokeWidth = 1 - self.chart.strandLabels.fontName = 'Helvetica' - self.chart.strandLabels.fontSize = 6 - self.chart.fillColor = backgroundGrey - self.chart.data = [(125, 180, 200), (100, 150, 180)] - self.chart.labels = ['North', 'South', 'Central'] - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.strands.strokeWidth = 1 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - RadarChart().save(formats=['pdf'],outDir=None,fnRoot='radar') diff --git a/bin/reportlab/graphics/samples/runall.py b/bin/reportlab/graphics/samples/runall.py deleted file mode 100644 index 938ab8fec42..00000000000 --- a/bin/reportlab/graphics/samples/runall.py +++ /dev/null @@ -1,59 +0,0 @@ -# runs all the GUIedit charts in this directory - -# 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): - return (type(obj)==CT and obj.__module__==m) - try: - return inspect.getmembers(mod, P)[0][1] - except: - return None - -def getclass(f): - return moduleClasses(__import__(f)) - -def run(format, VERBOSE=0): - formats = string.split(format, ',') - for i in range(0, len(formats)): - formats[i] == string.lower(string.strip(formats[i])) - allfiles = glob.glob('*.py') - allfiles.sort() - for fn in allfiles: - f = string.split(fn, '.')[0] - c = getclass(f) - if c != None: - 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) - except: - print " COULDN'T CREATE '%s.%s'!" % (c.__name__, format) - -if __name__ == "__main__": - if len(sys.argv) == 1: - run('pdf,pict,png') - 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' - 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' - raise diff --git a/bin/reportlab/graphics/samples/scatter.py b/bin/reportlab/graphics/samples/scatter.py deleted file mode 100644 index ea1a9991d3e..00000000000 --- a/bin/reportlab/graphics/samples/scatter.py +++ /dev/null @@ -1,71 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class Scatter(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.lineLabelFormat = None - self.chart.xLabel = 'X Axis' - self.chart.y = 30 - self.chart.yLabel = 'Y Axis' - self.chart.yValueAxis.labelTextFormat = '%d' - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - - - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - Scatter().save(formats=['pdf'],outDir=None,fnRoot='scatter') diff --git a/bin/reportlab/graphics/samples/scatter_lines.py b/bin/reportlab/graphics/samples/scatter_lines.py deleted file mode 100644 index 16ed718b2fd..00000000000 --- a/bin/reportlab/graphics/samples/scatter_lines.py +++ /dev/null @@ -1,82 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class ScatterLines(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.lines[0].symbol = None - self.chart.lines[1].symbol = None - self.chart.lines[2].symbol = None - self.chart.lines[3].symbol = None - self.chart.lines[4].symbol = None - self.chart.lines[5].symbol = None - self.chart.lines[6].symbol = None - self.chart.lines[7].symbol = None - self.chart.lines[8].symbol = None - self.chart.lines[9].symbol = None - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.lineLabelFormat = None - self.chart.xLabel = 'X Axis' - self.chart.y = 30 - self.chart.yLabel = 'Y Axis' - self.chart.yValueAxis.gridEnd = 115 - self.chart.yValueAxis.visibleGrid = 1 - self.chart.yValueAxis.labelTextFormat = '%d' - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - self.chart.joinedLines = 1 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - ScatterLines().save(formats=['pdf'],outDir=None,fnRoot='scatter_lines') diff --git a/bin/reportlab/graphics/samples/scatter_lines_markers.py b/bin/reportlab/graphics/samples/scatter_lines_markers.py deleted file mode 100644 index 34f8ff220f5..00000000000 --- a/bin/reportlab/graphics/samples/scatter_lines_markers.py +++ /dev/null @@ -1,72 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class ScatterLinesMarkers(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.lines[0].strokeColor = color01 - self.chart.lines[1].strokeColor = color02 - self.chart.lines[2].strokeColor = color03 - self.chart.lines[3].strokeColor = color04 - self.chart.lines[4].strokeColor = color05 - self.chart.lines[5].strokeColor = color06 - self.chart.lines[6].strokeColor = color07 - self.chart.lines[7].strokeColor = color08 - self.chart.lines[8].strokeColor = color09 - self.chart.lines[9].strokeColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.lineLabels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontName = 'Helvetica' - self.chart.xValueAxis.labels.fontSize = 7 - self.chart.xValueAxis.forceZero = 0 - self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))] - self.chart.xValueAxis.avoidBoundFrac = 1 - self.chart.xValueAxis.gridEnd = 115 - self.chart.xValueAxis.tickDown = 3 - self.chart.xValueAxis.visibleGrid = 1 - self.chart.yValueAxis.tickLeft = 3 - self.chart.yValueAxis.labels.fontName = 'Helvetica' - self.chart.yValueAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.lineLabelFormat = None - self.chart.xLabel = 'X Axis' - self.chart.y = 30 - self.chart.yLabel = 'Y Axis' - self.chart.yValueAxis.gridEnd = 115 - self.chart.yValueAxis.visibleGrid = 1 - self.chart.yValueAxis.labelTextFormat = '%d' - self.chart.yValueAxis.forceZero = 1 - self.chart.xValueAxis.forceZero = 1 - self.chart.joinedLines = 1 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - ScatterLinesMarkers().save(formats=['pdf'],outDir=None,fnRoot='scatter_lines_markers') diff --git a/bin/reportlab/graphics/samples/simple_pie.py b/bin/reportlab/graphics/samples/simple_pie.py deleted file mode 100644 index 7542607f6ae..00000000000 --- a/bin/reportlab/graphics/samples/simple_pie.py +++ /dev/null @@ -1,61 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.piecharts import Pie -from reportlab.graphics.widgets.grids import ShadedRect -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 * - -class SimplePie(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,Pie(),name='chart',validate=None,desc="The main chart") - self.chart.width = 100 - self.chart.height = 100 - self.chart.x = 25 - self.chart.y = 25 - self.chart.slices[0].fillColor = color01 - self.chart.slices[1].fillColor = color02 - self.chart.slices[2].fillColor = color03 - self.chart.slices[3].fillColor = color04 - self.chart.slices[4].fillColor = color05 - self.chart.slices[5].fillColor = color06 - self.chart.slices[6].fillColor = color07 - self.chart.slices[7].fillColor = color08 - self.chart.slices[8].fillColor = color09 - self.chart.slices[9].fillColor = color10 - self.chart.data = (100, 150, 180) - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'North'), (color02, 'South'),(color03, 'Central')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 160 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self.chart.slices.strokeWidth = 1 - self.chart.slices.fontName = 'Helvetica' - self.background = ShadedRect() - self.background.fillColorStart = backgroundGrey - self.background.fillColorEnd = backgroundGrey - self.background.numShades = 1 - self.background.strokeWidth = 0.5 - self.background.x = 25 - self.background.y = 25 - self.Legend.columnMaximum = 10 - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - SimplePie().save(formats=['pdf'],outDir=None,fnRoot=None) \ No newline at end of file diff --git a/bin/reportlab/graphics/samples/stacked_bar.py b/bin/reportlab/graphics/samples/stacked_bar.py deleted file mode 100644 index 9ba2a962f82..00000000000 --- a/bin/reportlab/graphics/samples/stacked_bar.py +++ /dev/null @@ -1,85 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class StackedBar(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.bars[0].fillColor = color01 - self.chart.bars[1].fillColor = color02 - self.chart.bars[2].fillColor = color03 - self.chart.bars[3].fillColor = color04 - self.chart.bars[4].fillColor = color05 - self.chart.bars[5].fillColor = color06 - self.chart.bars[6].fillColor = color07 - self.chart.bars[7].fillColor = color08 - self.chart.bars[8].fillColor = color09 - self.chart.bars[9].fillColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.barLabels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontSize = 6 - self.chart.valueAxis.forceZero = 1 - self.chart.data = [(100, 150, 180), (125, 180, 200)] - self.chart.groupSpacing = 15 - self.chart.valueAxis.avoidBoundFrac = 1 - self.chart.valueAxis.gridEnd = 80 - self.chart.valueAxis.tickDown = 3 - self.chart.valueAxis.visibleGrid = 1 - self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central'] - self.chart.categoryAxis.tickLeft = 3 - self.chart.categoryAxis.labels.fontName = 'Helvetica' - self.chart.categoryAxis.labels.fontSize = 6 - self.chart.categoryAxis.labels.dx = -3 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self.chart.categoryAxis.style='stacked' - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - StackedBar().save(formats=['pdf'],outDir=None,fnRoot='stacked_bar') \ No newline at end of file diff --git a/bin/reportlab/graphics/samples/stacked_column.py b/bin/reportlab/graphics/samples/stacked_column.py deleted file mode 100644 index a50a1598c90..00000000000 --- a/bin/reportlab/graphics/samples/stacked_column.py +++ /dev/null @@ -1,84 +0,0 @@ -#Autogenerated by ReportLab guiedit do not edit -from reportlab.graphics.charts.legends import Legend -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 * - -class StackedColumn(_DrawingEditorMixin,Drawing): - def __init__(self,width=200,height=150,*args,**kw): - apply(Drawing.__init__,(self,width,height)+args,kw) - self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart") - self.chart.width = 115 - self.chart.height = 80 - self.chart.x = 30 - self.chart.y = 40 - self.chart.bars[0].fillColor = color01 - self.chart.bars[1].fillColor = color02 - self.chart.bars[2].fillColor = color03 - self.chart.bars[3].fillColor = color04 - self.chart.bars[4].fillColor = color05 - self.chart.bars[5].fillColor = color06 - self.chart.bars[6].fillColor = color07 - self.chart.bars[7].fillColor = color08 - self.chart.bars[8].fillColor = color09 - self.chart.bars[9].fillColor = color10 - self.chart.fillColor = backgroundGrey - self.chart.barLabels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontName = 'Helvetica' - self.chart.valueAxis.labels.fontSize = 7 - self.chart.valueAxis.forceZero = 1 - self.chart.data = [(100, 150, 180), (125, 180, 200)] - self.chart.groupSpacing = 15 - self.chart.valueAxis.avoidBoundFrac = 1 - self.chart.valueAxis.gridEnd = 115 - self.chart.valueAxis.tickLeft = 3 - self.chart.valueAxis.visibleGrid = 1 - self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central'] - self.chart.categoryAxis.tickDown = 3 - self.chart.categoryAxis.labels.fontName = 'Helvetica' - self.chart.categoryAxis.labels.fontSize = 7 - self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart") - self.Title.fontName = 'Helvetica-Bold' - self.Title.fontSize = 7 - self.Title.x = 100 - self.Title.y = 135 - self.Title._text = 'Chart Title' - self.Title.maxWidth = 180 - self.Title.height = 20 - self.Title.textAnchor ='middle' - self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart") - self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')] - self.Legend.fontName = 'Helvetica' - self.Legend.fontSize = 7 - self.Legend.x = 153 - self.Legend.y = 85 - self.Legend.dxTextSpace = 5 - self.Legend.dy = 5 - self.Legend.dx = 5 - self.Legend.deltay = 5 - self.Legend.alignment ='right' - self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis") - self.XLabel.fontName = 'Helvetica' - self.XLabel.fontSize = 7 - self.XLabel.x = 85 - self.XLabel.y = 10 - self.XLabel.textAnchor ='middle' - self.XLabel.maxWidth = 100 - self.XLabel.height = 20 - self.XLabel._text = "X Axis" - self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis") - self.YLabel.fontName = 'Helvetica' - self.YLabel.fontSize = 7 - self.YLabel.x = 12 - self.YLabel.y = 80 - self.YLabel.angle = 90 - self.YLabel.textAnchor ='middle' - self.YLabel.maxWidth = 100 - self.YLabel.height = 20 - self.YLabel._text = "Y Axis" - self.chart.categoryAxis.style='stacked' - self._add(self,0,name='preview',validate=None,desc=None) - -if __name__=="__main__": #NORUNTESTS - StackedColumn().save(formats=['pdf'],outDir=None,fnRoot='stacked_column') diff --git a/bin/reportlab/graphics/shapes.py b/bin/reportlab/graphics/shapes.py deleted file mode 100644 index 896385e8bee..00000000000 --- a/bin/reportlab/graphics/shapes.py +++ /dev/null @@ -1,1289 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/shapes.py -""" -core of the graphics library - defines Drawing and Shapes -""" -__version__=''' $Id: shapes.py 2845 2006-05-03 12:24:35Z rgbecker $ ''' - -import string, os, sys -from math import pi, cos, sin, tan -from types import FloatType, IntType, ListType, TupleType, StringType, InstanceType -from pprint import pprint - -from reportlab.platypus import Flowable -from reportlab.rl_config import shapeChecking, verbose, defaultGraphicsFontName, _unset_ -from reportlab.lib import logger -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -from reportlab.lib.utils import fp_str -from reportlab.pdfbase.pdfmetrics import stringWidth - -class NotImplementedError(Exception): - pass - -# two constants for filling rules -NON_ZERO_WINDING = 'Non-Zero Winding' -EVEN_ODD = 'Even-Odd' - -## these can be overridden at module level before you start -#creating shapes. So, if using a special color model, -#this provides support for the rendering mechanism. -#you can change defaults globally before you start -#making shapes; one use is to substitute another -#color model cleanly throughout the drawing. - -STATE_DEFAULTS = { # sensible defaults for all - 'transform': (1,0,0,1,0,0), - - # styles follow SVG naming - 'strokeColor': colors.black, - 'strokeWidth': 1, - 'strokeLineCap': 0, - 'strokeLineJoin': 0, - 'strokeMiterLimit' : 'TBA', # don't know yet so let bomb here - 'strokeDashArray': None, - 'strokeOpacity': 1.0, #100% - - 'fillColor': colors.black, #...or text will be invisible - #'fillRule': NON_ZERO_WINDING, - these can be done later - #'fillOpacity': 1.0, #100% - can be done later - - 'fontSize': 10, - 'fontName': defaultGraphicsFontName, - 'textAnchor': 'start' # can be start, middle, end, inherited - } - - -#################################################################### -# math utilities. These could probably be moved into lib -# somewhere. -#################################################################### - -# constructors for matrices: -def nullTransform(): - return (1, 0, 0, 1, 0, 0) - -def translate(dx, dy): - return (1, 0, 0, 1, dx, dy) - -def scale(sx, sy): - return (sx, 0, 0, sy, 0, 0) - -def rotate(angle): - a = angle * pi/180 - return (cos(a), sin(a), -sin(a), cos(a), 0, 0) - -def skewX(angle): - a = angle * pi/180 - return (1, 0, tan(a), 1, 0, 0) - -def skewY(angle): - a = angle * pi/180 - return (1, tan(a), 0, 1, 0, 0) - -def mmult(A, B): - "A postmultiplied by B" - # I checked this RGB - # [a0 a2 a4] [b0 b2 b4] - # [a1 a3 a5] * [b1 b3 b5] - # [ 1 ] [ 1 ] - # - return (A[0]*B[0] + A[2]*B[1], - A[1]*B[0] + A[3]*B[1], - A[0]*B[2] + A[2]*B[3], - A[1]*B[2] + A[3]*B[3], - A[0]*B[4] + A[2]*B[5] + A[4], - A[1]*B[4] + A[3]*B[5] + A[5]) - -def inverse(A): - "For A affine 2D represented as 6vec return 6vec version of A**(-1)" - # I checked this RGB - det = float(A[0]*A[3] - A[2]*A[1]) - R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det] - return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]]) - -def zTransformPoint(A,v): - "Apply the homogenous part of atransformation a to vector v --> A*v" - return (A[0]*v[0]+A[2]*v[1],A[1]*v[0]+A[3]*v[1]) - -def transformPoint(A,v): - "Apply transformation a to vector v --> A*v" - 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) - -def zTransformPoints(matrix, V): - return map(lambda x,matrix=matrix: zTransformPoint(matrix,x), V) - -def _textBoxLimits(text, font, fontSize, leading, textAnchor, boxAnchor): - w = 0 - for t in text: - w = max(w,stringWidth(t,font, fontSize)) - - h = len(text)*leading - yt = fontSize - if boxAnchor[0]=='s': - yb = -h - yt = yt - h - elif boxAnchor[0]=='n': - yb = 0 - else: - yb = -h/2.0 - yt = yt + yb - - if boxAnchor[-1]=='e': - xb = -w - if textAnchor=='end': xt = 0 - elif textAnchor=='start': xt = -w - else: xt = -w/2.0 - elif boxAnchor[-1]=='w': - xb = 0 - if textAnchor=='end': xt = w - elif textAnchor=='start': xt = 0 - else: xt = w/2.0 - else: - xb = -w/2.0 - if textAnchor=='end': xt = -xb - elif textAnchor=='start': xt = xb - else: xt = 0 - - return xb, yb, w, h, xt, yt - -def _rotatedBoxLimits( x, y, w, h, angle): - ''' - Find the corner points of the rotated w x h sized box at x,y - 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) - return min(X), max(X), min(Y), max(Y), C - - -class _DrawTimeResizeable: - '''Addin class to provide the horribleness of _drawTimeResize''' - def _drawTimeResize(self,w,h): - if hasattr(self,'_canvas'): - canvas = self._canvas - drawing = canvas._drawing - drawing.width, drawing.height = w, h - if hasattr(canvas,'_drawTimeResize'): - canvas._drawTimeResize(w,h) - -class _SetKeyWordArgs: - def __init__(self, keywords={}): - """In general properties may be supplied to the constructor.""" - for key, value in keywords.items(): - setattr(self, key, value) - - -################################################################# -# -# Helper functions for working out bounds -# -################################################################# - -def getRectsBounds(rectList): - # filter out any None objects, e.g. empty groups - L = filter(lambda x: x is not None, rectList) - if not L: return None - - xMin, yMin, xMax, yMax = L[0] - for (x1, y1, x2, y2) in L[1:]: - if x1 < xMin: - xMin = x1 - if x2 > xMax: - xMax = x2 - if y1 < yMin: - yMin = y1 - if y2 > yMax: - yMax = y2 - return (xMin, yMin, xMax, yMax) - -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)) - 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) - return (min(xs), min(ys), max(xs), max(ys)) - else: - return getPathBounds(pointList) - -################################################################# -# -# And now the shapes themselves.... -# -################################################################# -class Shape(_SetKeyWordArgs,_DrawTimeResizeable): - """Base class for all nodes in the tree. Nodes are simply - packets of data to be created, stored, and ultimately - rendered - they don't do anything active. They provide - convenience methods for verification but do not - check attribiute assignments or use any clever setattr - tricks this time.""" - _attrMap = AttrMap() - - def copy(self): - """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__ - - def getProperties(self,recur=1): - """Interface to make it easy to extract automatic - documentation""" - - #basic nodes have no children so this is easy. - #for more complex objects like widgets you - #may need to override this. - props = {} - for key, value in self.__dict__.items(): - if key[0:1] <> '_': - props[key] = value - return props - - def setProperties(self, props): - """Supports the bulk setting if properties from, - for example, a GUI application or a config file.""" - - self.__dict__.update(props) - #self.verify() - - def dumpProperties(self, prefix=""): - """Convenience. Lists them on standard output. You - may provide a prefix - mostly helps to generate code - samples for documentation.""" - - propList = self.getProperties().items() - propList.sort() - if prefix: - prefix = prefix + '.' - for (name, value) in propList: - print '%s%s = %s' % (prefix, name, value) - - def verify(self): - """If the programmer has provided the optional - _attrMap attribute, this checks all expected - attributes are present; no unwanted attributes - are present; and (if a checking function is found) - checks each attribute. Either succeeds or raises - an informative exception.""" - - if self._attrMap is not None: - for key in self.__dict__.keys(): - if key[0] <> '_': - assert self._attrMap.has_key(key), "Unexpected attribute %s found in %s" % (key, self) - 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__) - - if shapeChecking: - """This adds the ability to check every attribute assignment as it is made. - It slows down shapes but is a big help when developing. It does not - get defined if rl_config.shapeChecking = 0""" - def __setattr__(self, attr, value): - """By default we verify. This could be off - in some parallel base classes.""" - validateSetattr(self,attr,value) #from reportlab.lib.attrmap - - def getBounds(self): - "Returns bounding rectangle of object as (x1,y1,x2,y2)" - raise NotImplementedError("Shapes and widgets must implement getBounds") - -class Group(Shape): - """Groups elements together. May apply a transform - to its contents. Has a publicly accessible property - 'contents' which may be used to iterate over contents. - In addition, child nodes may be given a name in which - case they are subsequently accessible as properties.""" - - _attrMap = AttrMap( - transform = AttrMapValue(isTransform,desc="Coordinate transformation to apply"), - contents = AttrMapValue(isListOfShapes,desc="Contained drawable elements"), - ) - - def __init__(self, *elements, **keywords): - """Initial lists of elements may be provided to allow - compact definitions in literal Python code. May or - may not be useful.""" - - # Groups need _attrMap to be an instance rather than - # a class attribute, as it may be extended at run time. - self._attrMap = self._attrMap.clone() - self.contents = [] - self.transform = (1,0,0,1,0,0) - for elt in elements: - self.add(elt) - # this just applies keywords; do it at the end so they - #don;t get overwritten - _SetKeyWordArgs.__init__(self, keywords) - - 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(): - self._attrMap[name] = AttrMapValue(isValidChild) - setattr(self, name, node) - - def add(self, node, name=None): - """Appends non-None child node to the 'contents' attribute. In addition, - if a name is provided, it is subsequently accessible by name - """ - # propagates properties down - if node is not None: - assert isValidChild(node), "Can only add Shape or UserNode objects to a Group" - self.contents.append(node) - self._addNamedNode(name,node) - - def _nn(self,node): - self.add(node) - return self.contents[-1] - - def insert(self, i, n, name=None): - 'Inserts sub-node n in contents at specified location' - if n is not None: - assert isValidChild(n), "Can only insert Shape or UserNode objects in a Group" - if i<0: - self.contents[i:i] =[n] - else: - self.contents.insert(i,n) - self._addNamedNode(name,n) - - def expandUserNodes(self): - """Return a new object which only contains primitive shapes.""" - - # many limitations - shared nodes become multiple ones, - obj = isinstance(self,Drawing) and Drawing(self.width,self.height) or Group() - obj._attrMap = self._attrMap.clone() - if hasattr(obj,'transform'): obj.transform = self.transform[:] - - self_contents = self.contents - a = obj.contents.append - for child in self_contents: - if isinstance(child, UserNode): - newChild = child.provideNode() - elif isinstance(child, Group): - newChild = child.expandUserNodes() - else: - newChild = child.copy() - a(newChild) - - self._copyNamedContents(obj) - return obj - - def _explode(self): - ''' return a fully expanded object''' - from reportlab.graphics.widgetbase import Widget - obj = Group() - if hasattr(obj,'transform'): obj.transform = self.transform[:] - P = self.contents[:] # pending nodes - while P: - n = P.pop(0) - if isinstance(n, UserNode): - P.append(n.provideNode()) - elif isinstance(n, Group): - n = n._explode() - if n.transform==(1,0,0,1,0,0): - obj.contents.extend(n.contents) - else: - obj.add(n) - else: - obj.add(n) - return obj - - def _copyContents(self,obj): - for child in self.contents: - obj.contents.append(child) - - 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 v in self_contents: - pos = self_contents.index(v) - setattr(obj, k, obj.contents[pos]) - elif k in aKeys and k not in noCopy: - setattr(obj, k, copy(v)) - - def _copy(self,obj): - """copies to obj""" - obj._attrMap = self._attrMap.clone() - self._copyContents(obj) - self._copyNamedContents(obj) - return obj - - def copy(self): - """returns a copy""" - return self._copy(self.__class__()) - - def rotate(self, theta): - """Convenience to help you set transforms""" - self.transform = mmult(self.transform, rotate(theta)) - - def translate(self, dx, dy): - """Convenience to help you set transforms""" - self.transform = mmult(self.transform, translate(dx, dy)) - - def scale(self, sx, sy): - """Convenience to help you set transforms""" - self.transform = mmult(self.transform, scale(sx, sy)) - - - def skew(self, kx, ky): - """Convenience to help you set transforms""" - self.transform = mmult(mmult(self.transform, skewX(kx)),skewY(ky)) - - def shift(self, x, y): - '''Convenience function to set the origin arbitrarily''' - self.transform = self.transform[:-2]+(x,y) - - def asDrawing(self, width, height): - """ Convenience function to make a drawing from a group - After calling this the instance will be a drawing! - """ - self.__class__ = Drawing - self._attrMap.update(self._xtraAttrMap) - self.width = width - self.height = height - - def getContents(self): - '''Return the list of things to be rendered - override to get more complicated behaviour''' - b = getattr(self,'background',None) - C = self.contents - if b and b not in C: C = [b]+C - return C - - def getBounds(self): - if self.contents: - b = [] - for elem in self.contents: - b.append(elem.getBounds()) - x1 = getRectsBounds(b) - if x1 is None: return None - x1, y1, x2, y2 = x1 - trans = self.transform - corners = [[x1,y1], [x1, y2], [x2, y1], [x2,y2]] - newCorners = [] - for corner in corners: - newCorners.append(transformPoint(trans, corner)) - return getPointsBounds(newCorners) - else: - #empty group needs a sane default; this - #will happen when interactively creating a group - #nothing has been added to yet. The alternative is - #to handle None as an allowed return value everywhere. - return None - -def _addObjImport(obj,I,n=None): - '''add an import of obj's class to a dictionary of imports''' #' - from inspect import getmodule - c = obj.__class__ - m = getmodule(c).__name__ - n = n or c.__name__ - if not I.has_key(m): - I[m] = [n] - elif n not in I[m]: - I[m].append(n) - -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: - return fp_str(self) - elif type(self) in (ListType,TupleType): - s = '' - for v in self: - s = s + '%s,' % _repr(v,I) - if type(self) is ListType: - return '[%s]' % s[:-1] - else: - return '(%s%s)' % (s[:-1],len(self)==1 and ',' or '') - else: - return repr(self) - -def _renderGroupPy(G,pfx,I,i=0,indent='\t\t'): - s = '' - C = getattr(G,'transform',None) - if C: s = s + ('%s%s.transform = %s\n' % (indent,pfx,_repr(C))) - C = G.contents - for n in C: - if isinstance(n, Group): - npfx = 'v%d' % i - i = i + 1 - s = s + '%s%s=%s._nn(Group())\n' % (indent,npfx,pfx) - s = s + _renderGroupPy(n,npfx,I,i,indent) - i = i - 1 - else: - s = s + '%s%s.add(%s)\n' % (indent,pfx,_repr(n,I)) - return s - -def _extraKW(self,pfx,**kw): - kw.update(self.__dict__) - R = {} - n = len(pfx) - for k in kw.keys(): - if k.startswith(pfx): - R[k[n:]] = kw[k] - return R - -class Drawing(Group, Flowable): - """Outermost container; the thing a renderer works on. - This has no properties except a height, width and list - of contents.""" - - _xtraAttrMap = AttrMap( - width = AttrMapValue(isNumber,desc="Drawing width in points."), - height = AttrMapValue(isNumber,desc="Drawing height in points."), - canv = AttrMapValue(None), - background = AttrMapValue(isValidChildOrNone,desc="Background widget for the drawing"), - hAlign = AttrMapValue(OneOf("LEFT", "RIGHT", "CENTER", "CENTRE"), desc="Horizontal alignment within parent document"), - vAlign = AttrMapValue(OneOf("TOP", "BOTTOM", "CENTER", "CENTRE"), desc="Vertical alignment within parent document"), - #AR temporary hack to track back up. - #fontName = AttrMapValue(isStringOrNone), - renderScale = AttrMapValue(isNumber,desc="Global scaling for rendering"), - ) - - _attrMap = AttrMap(BASE=Group) - _attrMap.update(_xtraAttrMap) - - def __init__(self, width=400, height=200, *nodes, **keywords): - self.background = None - apply(Group.__init__,(self,)+nodes,keywords) - self.width = width - self.height = height - self.hAlign = 'LEFT' - self.vAlign = 'BOTTOM' - self.renderScale = 1.0 - - def _renderPy(self): - I = {'reportlab.graphics.shapes': ['_DrawingEditorMixin','Drawing','Group']} - G = _renderGroupPy(self._explode(),'self',I) - 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 + '\nclass %s(_DrawingEditorMixin,Drawing):\n' % n - s = s + '\tdef __init__(self,width=%s,height=%s,*args,**kw):\n' % (self.width,self.height) - s = s + '\t\tapply(Drawing.__init__,(self,width,height)+args,kw)\n' - s = s + G - s = s + '\n\nif __name__=="__main__": #NORUNTESTS\n\t%s().save(formats=[\'pdf\'],outDir=\'.\',fnRoot=None)\n' % n - return s - - def draw(self,showBoundary=_unset_): - """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 - renderPDF.draw(self, self.canv, 0, 0, showBoundary=showBoundary) - - def wrap(self, availWidth, availHeight): - width = self.width - height = self.height - renderScale = self.renderScale - if renderScale!=1.0: - width *= renderScale - height *= renderScale - return width, height - - def expandUserNodes(self): - """Return a new drawing which only contains primitive shapes.""" - obj = Group.expandUserNodes(self) - obj.width = self.width - obj.height = self.height - return obj - - def copy(self): - """Returns a copy""" - return self._copy(self.__class__(self.width, self.height)) - - def asGroup(self,*args,**kw): - return self._copy(apply(Group,args,kw)) - - def save(self, formats=None, verbose=None, fnRoot=None, outDir=None, title='', **kw): - """Saves copies of self in desired location and formats. - Multiple formats can be supported in one call - - the extra keywords can be of the form - _renderPM_dpi=96 (which passes dpi=96 to renderPM) - """ - from reportlab import rl_config - ext = '' - if not fnRoot: - fnRoot = getattr(self,'fileNamePattern',(self.__class__.__name__+'%03d')) - chartId = getattr(self,'chartId',0) - if callable(fnRoot): - fnRoot = fnRoot(chartId) - else: - try: - fnRoot = fnRoot % getattr(self,'chartId',0) - except TypeError, 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 - - if os.path.isabs(fnRoot): - outDir, fnRoot = os.path.split(fnRoot) - else: - outDir = outDir or getattr(self,'outDir','.') - outDir = outDir.rstrip().rstrip(os.sep) - if not outDir: outDir = '.' - if not os.path.isabs(outDir): outDir = os.path.join(getattr(self,'_override_CWD',os.path.dirname(sys.argv[0])),outDir) - 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 ['pdf','ps','eps','gif','png','jpg','jpeg','pct','pict','tiff','tif','py','bmp','svg']: - fnroot = plotMode[0] - - plotMode, verbose = formats or getattr(self,'formats',['pdf']), (verbose is not None and (verbose,) or (getattr(self,'verbose',verbose),))[0] - _saved = logger.warnOnce.enabled, logger.infoOnce.enabled - logger.warnOnce.enabled = logger.infoOnce.enabled = verbose - if 'pdf' in plotMode: - from reportlab.graphics import renderPDF - filename = fnroot+'.pdf' - if verbose: print "generating PDF file %s" % filename - renderPDF.drawToFile(self, filename, title, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPDF_',**kw)) - ext = ext + '/.pdf' - if sys.platform=='mac': - import macfs, macostools - macfs.FSSpec(filename).SetCreatorType("CARO", "PDF ") - macostools.touched(filename) - - for bmFmt in ['gif','png','tif','jpg','tiff','pct','pict', 'bmp']: - if bmFmt in plotMode: - from reportlab.graphics import renderPM - filename = '%s.%s' % (fnroot,bmFmt) - if verbose: print "generating %s file %s" % (bmFmt,filename) - renderPM.drawToFile(self, filename,fmt=bmFmt,showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPM_',**kw)) - ext = ext + '/.' + bmFmt - - if 'eps' in plotMode: - from rlextra.graphics import renderPS_SEP - filename = fnroot+'.eps' - if verbose: print "generating EPS file %s" % filename - renderPS_SEP.drawToFile(self, - filename, - title = fnroot, - dept = getattr(self,'EPS_info',['Testing'])[0], - company = getattr(self,'EPS_info',['','ReportLab'])[1], - preview = getattr(self,'preview',1), - showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPS_',**kw)) - ext = ext + '/.eps' - - if 'svg' in plotMode: - from reportlab.graphics import renderSVG - filename = fnroot+'.svg' - if verbose: print "generating EPS file %s" % filename - renderSVG.drawToFile(self, - filename, - showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderSVG_',**kw)) - ext = ext + '/.svg' - - if 'ps' in plotMode: - from reportlab.graphics import renderPS - filename = fnroot+'.ps' - if verbose: print "generating EPS file %s" % filename - renderPS.drawToFile(self, filename, showBoundary=getattr(self,'showBorder',rl_config.showBoundary),**_extraKW(self,'_renderPS_',**kw)) - ext = ext + '/.ps' - - if 'py' in plotMode: - filename = fnroot+'.py' - if verbose: print "generating py file %s" % filename - open(filename,'w').write(self._renderPy()) - ext = ext + '/.py' - - logger.warnOnce.enabled, logger.infoOnce.enabled = _saved - if hasattr(self,'saveLogger'): - self.saveLogger(fnroot,ext) - return ext and fnroot+ext[1:] or '' - - - def asString(self, format, verbose=None, preview=0): - """Converts to an 8 bit string in given format.""" - assert format in ['pdf','ps','eps','gif','png','jpg','jpeg','bmp','ppm','tiff','tif','py','pict','pct'], 'Unknown file format "%s"' % format - from reportlab import rl_config - #verbose = verbose is not None and (verbose,) or (getattr(self,'verbose',verbose),)[0] - if format == 'pdf': - from reportlab.graphics import renderPDF - return renderPDF.drawToString(self) - elif format in ['gif','png','tif','jpg','pct','pict','bmp','ppm']: - from reportlab.graphics import renderPM - return renderPM.drawToString(self, fmt=format) - elif format == 'eps': - from rlextra.graphics import renderPS_SEP - return renderPS_SEP.drawToString(self, - preview = preview, - showBoundary=getattr(self,'showBorder',rl_config.showBoundary)) - elif format == 'ps': - from reportlab.graphics import renderPS - return renderPS.drawToString(self, showBoundary=getattr(self,'showBorder',rl_config.showBoundary)) - elif format == 'py': - return self._renderPy() - -class _DrawingEditorMixin: - '''This is a mixin to provide functionality for edited drawings''' - def _add(self,obj,value,name=None,validate=None,desc=None,pos=None): - ''' - effectively setattr(obj,name,value), but takes care of things with _attrMaps etc - ''' - ivc = isValidChild(value) - if name and hasattr(obj,'_attrMap'): - if not obj.__dict__.has_key('_attrMap'): - obj._attrMap = obj._attrMap.clone() - if ivc and validate is None: validate = isValidChild - obj._attrMap[name] = AttrMapValue(validate,desc) - if hasattr(obj,'add') and ivc: - if pos: - obj.insert(pos,value,name) - else: - obj.add(value,name) - elif name: - setattr(obj,name,value) - else: - raise ValueError, "Can't add, need name" - -class LineShape(Shape): - # base for types of lines - - _attrMap = AttrMap( - strokeColor = AttrMapValue(isColorOrNone), - strokeWidth = AttrMapValue(isNumber), - strokeLineCap = AttrMapValue(None), - strokeLineJoin = AttrMapValue(None), - strokeMiterLimit = AttrMapValue(isNumber), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - ) - - def __init__(self, kw): - self.strokeColor = STATE_DEFAULTS['strokeColor'] - self.strokeWidth = 1 - self.strokeLineCap = 0 - self.strokeLineJoin = 0 - self.strokeMiterLimit = 0 - self.strokeDashArray = None - self.setProperties(kw) - - -class Line(LineShape): - _attrMap = AttrMap(BASE=LineShape, - x1 = AttrMapValue(isNumber), - y1 = AttrMapValue(isNumber), - x2 = AttrMapValue(isNumber), - y2 = AttrMapValue(isNumber), - ) - - def __init__(self, x1, y1, x2, y2, **kw): - LineShape.__init__(self, kw) - self.x1 = x1 - self.y1 = y1 - self.x2 = x2 - self.y2 = y2 - - def getBounds(self): - "Returns bounding rectangle of object as (x1,y1,x2,y2)" - return (self.x1, self.y1, self.x2, self.y2) - - -class SolidShape(LineShape): - # base for anything with outline and content - - _attrMap = AttrMap(BASE=LineShape, - fillColor = AttrMapValue(isColorOrNone), - ) - - def __init__(self, kw): - self.fillColor = STATE_DEFAULTS['fillColor'] - # do this at the end so keywords overwrite - #the above settings - LineShape.__init__(self, kw) - - -# path operator constants -_MOVETO, _LINETO, _CURVETO, _CLOSEPATH = range(4) -_PATH_OP_ARG_COUNT = (2, 2, 6, 0) # [moveTo, lineTo, curveTo, closePath] -_PATH_OP_NAMES=['moveTo','lineTo','curveTo','closePath'] - -def _renderPath(path, drawFuncs): - """Helper function for renderers.""" - # this could be a method of Path... - points = path.points - i = 0 - hadClosePath = 0 - hadMoveTo = 0 - for op in path.operators: - nArgs = _PATH_OP_ARG_COUNT[op] - func = drawFuncs[op] - j = i + nArgs - apply(func, points[i:j]) - i = j - if op == _CLOSEPATH: - hadClosePath = hadClosePath + 1 - if op == _MOVETO: - hadMoveTo = hadMoveTo + 1 - return hadMoveTo == hadClosePath - -class Path(SolidShape): - """Path, made up of straight lines and bezier curves.""" - - _attrMap = AttrMap(BASE=SolidShape, - points = AttrMapValue(isListOfNumbers), - operators = AttrMapValue(isListOfNumbers), - isClipPath = AttrMapValue(isBoolean), - ) - - def __init__(self, points=None, operators=None, isClipPath=0, **kw): - SolidShape.__init__(self, kw) - if points is None: - points = [] - if operators is None: - operators = [] - assert len(points) % 2 == 0, 'Point list must have even number of elements!' - self.points = points - self.operators = operators - self.isClipPath = isClipPath - - def copy(self): - new = self.__class__(self.points[:], self.operators[:]) - new.setProperties(self.getProperties()) - return new - - def moveTo(self, x, y): - self.points.extend([x, y]) - self.operators.append(_MOVETO) - - def lineTo(self, x, y): - self.points.extend([x, y]) - self.operators.append(_LINETO) - - def curveTo(self, x1, y1, x2, y2, x3, y3): - self.points.extend([x1, y1, x2, y2, x3, y3]) - self.operators.append(_CURVETO) - - def closePath(self): - self.operators.append(_CLOSEPATH) - - def getBounds(self): - return getPathBounds(self.points) - -EmptyClipPath=Path() #special path - -def getArcPoints(centerx, centery, radius, startangledegrees, endangledegrees, yradius=None, degreedelta=None, reverse=None): - if yradius is None: yradius = radius - points = [] - from math import sin, cos, pi - degreestoradians = pi/180.0 - startangle = startangledegrees*degreestoradians - endangle = endangledegrees*degreestoradians - while endangle.001: - degreedelta = min(angle,degreedelta or 1.) - radiansdelta = degreedelta*degreestoradians - n = max(int(angle/radiansdelta+0.5),1) - radiansdelta = angle/n - n += 1 - else: - n = 1 - radiansdelta = 0 - - for angle in xrange(n): - angle = startangle+angle*radiansdelta - a((centerx+radius*cos(angle),centery+yradius*sin(angle))) - - if reverse: points.reverse() - return points - -class ArcPath(Path): - '''Path with an addArc method''' - def addArc(self, centerx, centery, radius, startangledegrees, endangledegrees, yradius=None, degreedelta=None, moveTo=None, reverse=None): - P = getArcPoints(centerx, centery, radius, startangledegrees, endangledegrees, yradius=yradius, degreedelta=degreedelta, reverse=reverse) - if moveTo or not len(self.operators): - self.moveTo(P[0][0],P[0][1]) - del P[0] - for x, y in P: self.lineTo(x,y) - -def definePath(pathSegs=[],isClipPath=0, dx=0, dy=0, **kw): - O = [] - P = [] - for seg in pathSegs: - if type(seg) not in [ListType,TupleType]: - opName = seg - args = [] - else: - opName = seg[0] - args = seg[1:] - if opName not in _PATH_OP_NAMES: - 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)) - O.append(op) - P.extend(list(args)) - for d,o in (dx,0), (dy,1): - for i in xrange(o,len(P),2): - P[i] = P[i]+d - return apply(Path,(P,O,isClipPath),kw) - -class Rect(SolidShape): - """Rectangle, possibly with rounded corners.""" - - _attrMap = AttrMap(BASE=SolidShape, - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - width = AttrMapValue(isNumber), - height = AttrMapValue(isNumber), - rx = AttrMapValue(isNumber), - ry = AttrMapValue(isNumber), - ) - - def __init__(self, x, y, width, height, rx=0, ry=0, **kw): - SolidShape.__init__(self, kw) - self.x = x - self.y = y - self.width = width - self.height = height - self.rx = rx - self.ry = ry - - def copy(self): - new = self.__class__(self.x, self.y, self.width, self.height) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return (self.x, self.y, self.x + self.width, self.y + self.height) - - -class Image(SolidShape): - """Bitmap image.""" - - _attrMap = AttrMap(BASE=SolidShape, - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - width = AttrMapValue(isNumberOrNone), - height = AttrMapValue(isNumberOrNone), - path = AttrMapValue(None), - ) - - def __init__(self, x, y, width, height, path, **kw): - SolidShape.__init__(self, kw) - self.x = x - self.y = y - self.width = width - self.height = height - self.path = path - - def copy(self): - new = self.__class__(self.x, self.y, self.width, self.height, self.path) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return (self.x, self.y, self.x + width, self.y + width) - -class Circle(SolidShape): - - _attrMap = AttrMap(BASE=SolidShape, - cx = AttrMapValue(isNumber), - cy = AttrMapValue(isNumber), - r = AttrMapValue(isNumber), - ) - - def __init__(self, cx, cy, r, **kw): - SolidShape.__init__(self, kw) - self.cx = cx - self.cy = cy - self.r = r - - def copy(self): - new = self.__class__(self.cx, self.cy, self.r) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return (self.cx - self.r, self.cy - self.r, self.cx + self.r, self.cy + self.r) - -class Ellipse(SolidShape): - _attrMap = AttrMap(BASE=SolidShape, - cx = AttrMapValue(isNumber), - cy = AttrMapValue(isNumber), - rx = AttrMapValue(isNumber), - ry = AttrMapValue(isNumber), - ) - - def __init__(self, cx, cy, rx, ry, **kw): - SolidShape.__init__(self, kw) - self.cx = cx - self.cy = cy - self.rx = rx - self.ry = ry - - def copy(self): - new = self.__class__(self.cx, self.cy, self.rx, self.ry) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return (self.cx - self.rx, self.cy - self.ry, self.cx + self.rx, self.cy + self.ry) - -class Wedge(SolidShape): - """A "slice of a pie" by default translates to a polygon moves anticlockwise - from start angle to end angle""" - - _attrMap = AttrMap(BASE=SolidShape, - centerx = AttrMapValue(isNumber), - centery = AttrMapValue(isNumber), - radius = AttrMapValue(isNumber), - startangledegrees = AttrMapValue(isNumber), - endangledegrees = AttrMapValue(isNumber), - yradius = AttrMapValue(isNumberOrNone), - radius1 = AttrMapValue(isNumberOrNone), - yradius1 = AttrMapValue(isNumberOrNone), - ) - - degreedelta = 1 # jump every 1 degrees - - def __init__(self, centerx, centery, radius, startangledegrees, endangledegrees, yradius=None, **kw): - SolidShape.__init__(self, kw) - while endangledegrees0.001: - degreedelta = min(self.degreedelta or 1.,angle) - radiansdelta = degreedelta*degreestoradians - n = max(1,int(angle/radiansdelta+0.5)) - radiansdelta = angle/n - n += 1 - else: - n = 1 - radiansdelta = 0 - CA = [] - CAA = CA.append - a = points.append - for angle in xrange(n): - angle = startangle+angle*radiansdelta - CAA((cos(angle),sin(angle))) - for c,s in CA: - a(centerx+radius*c) - a(centery+yradius*s) - if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None): - a(centerx); a(centery) - else: - CA.reverse() - for c,s in CA: - a(centerx+radius1*c) - a(centery+yradius1*s) - return Polygon(points) - - def copy(self): - new = self.__class__(self.centerx, - self.centery, - self.radius, - self.startangledegrees, - self.endangledegrees) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return self.asPolygon().getBounds() - -class Polygon(SolidShape): - """Defines a closed shape; Is implicitly - joined back to the start for you.""" - - _attrMap = AttrMap(BASE=SolidShape, - points = AttrMapValue(isListOfNumbers), - ) - - def __init__(self, points=[], **kw): - SolidShape.__init__(self, kw) - assert len(points) % 2 == 0, 'Point list must have even number of elements!' - self.points = points - - def copy(self): - new = self.__class__(self.points) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return getPointsBounds(self.points) - -class PolyLine(LineShape): - """Series of line segments. Does not define a - closed shape; never filled even if apparently joined. - Put the numbers in the list, not two-tuples.""" - - _attrMap = AttrMap(BASE=LineShape, - points = AttrMapValue(isListOfNumbers), - ) - - def __init__(self, points=[], **kw): - LineShape.__init__(self, kw) - lenPoints = len(points) - if lenPoints: - if type(points[0]) in (ListType,TupleType): - L = [] - for (x,y) in points: - L.append(x) - L.append(y) - points = L - else: - assert len(points) % 2 == 0, 'Point list must have even number of elements!' - self.points = points - - def copy(self): - new = self.__class__(self.points) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - return getPointsBounds(self.points) - -class String(Shape): - """Not checked against the spec, just a way to make something work. - Can be anchored left, middle or end.""" - - # to do. - _attrMap = AttrMap( - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - text = AttrMapValue(isString), - fontName = AttrMapValue(None), - fontSize = AttrMapValue(isNumber), - fillColor = AttrMapValue(isColorOrNone), - textAnchor = AttrMapValue(isTextAnchor), - encoding = AttrMapValue(isString), - ) - - def __init__(self, x, y, text, **kw): - self.x = x - self.y = y - self.text = text - self.textAnchor = 'start' - self.fontName = STATE_DEFAULTS['fontName'] - self.fontSize = STATE_DEFAULTS['fontSize'] - self.fillColor = STATE_DEFAULTS['fillColor'] - self.setProperties(kw) - self.encoding = 'cp1252' #matches only fonts we have! - - def getEast(self): - return self.x + stringWidth(self.text,self.fontName,self.fontSize, self.encoding) - - def copy(self): - new = self.__class__(self.x, self.y, self.text) - new.setProperties(self.getProperties()) - return new - - def getBounds(self): - # assumes constant drop of 0.2*size to baseline - w = stringWidth(self.text,self.fontName,self.fontSize, self.encoding) - if self.textAnchor == 'start': - x = self.x - elif self.textAnchor == 'middle': - x = self.x - 0.5*w - elif self.textAnchor == 'end': - x = self.x - w - return (x, self.y - 0.2 * self.fontSize, x+w, self.y + self.fontSize) - -class UserNode(_DrawTimeResizeable): - """A simple template for creating a new node. The user (Python - programmer) may subclasses this. provideNode() must be defined to - provide a Shape primitive when called by a renderer. It does - NOT inherit from Shape, as the renderer always replaces it, and - your own classes can safely inherit from it without getting - lots of unintended behaviour.""" - - def provideNode(self): - """Override this to create your own node. This lets widgets be - 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" - - -def test(): - r = Rect(10,10,200,50) - import pprint - pp = pprint.pprint - print 'a Rectangle:' - pp(r.getProperties()) - print - print 'verifying...', - r.verify() - print 'OK' - #print 'setting rect.z = "spam"' - #r.z = 'spam' - print 'deleting rect.width' - del r.width - print 'verifying...', - r.verify() - - -if __name__=='__main__': - test() diff --git a/bin/reportlab/graphics/testdrawings.py b/bin/reportlab/graphics/testdrawings.py deleted file mode 100644 index d5c52fc57eb..00000000000 --- a/bin/reportlab/graphics/testdrawings.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/testdrawings.py -__version__=''' $Id $ ''' -"""This contains a number of routines to generate test drawings -for reportlab/graphics. For now they are contrived, but we will expand them -to try and trip up any parser. Feel free to add more. - -""" - -from reportlab.graphics.shapes import * -from reportlab.lib import colors - -def getDrawing1(): - """Hello World, on a rectangular background""" - - D = Drawing(400, 200) - D.add(Rect(50, 50, 300, 100, fillColor=colors.yellow)) #round corners - D.add(String(180,100, 'Hello World', fillColor=colors.red)) - - - return D - - -def getDrawing2(): - """This demonstrates the basic shapes. There are - no groups or references. Each solid shape should have - a purple fill.""" - D = Drawing(400, 200) #, fillColor=colors.purple) - - D.add(Line(10,10,390,190)) - D.add(Circle(100,100,20, fillColor=colors.purple)) - D.add(Circle(200,100,20, fillColor=colors.purple)) - D.add(Circle(300,100,20, fillColor=colors.purple)) - - D.add(Wedge(330,100,40, -10,40, fillColor=colors.purple)) - - D.add(PolyLine([120,10,130,20,140,10,150,20,160,10, - 170,20,180,10,190,20,200,10])) - - D.add(Polygon([300,20,350,20,390,80,300,75, 330, 40])) - - D.add(Ellipse(50, 150, 40, 20)) - - D.add(Rect(120, 150, 60, 30, - strokeWidth=10, - strokeColor=colors.red, - fillColor=colors.yellow)) #square corners - - D.add(Rect(220, 150, 60, 30, 10, 10)) #round corners - - D.add(String(10,50, 'Basic Shapes', fillColor=colors.black)) - - return D - - -##def getDrawing2(): -## """This drawing uses groups. Each group has two circles and a comment. -## The line style is set at group level and should be red for the left, -## bvlue for the right.""" -## D = Drawing(400, 200) -## -## Group1 = Group() -## -## Group1.add(String(50, 50, 'Group 1', fillColor=colors.black)) -## Group1.add(Circle(75,100,25)) -## Group1.add(Circle(125,100,25)) -## D.add(Group1) -## -## Group2 = Group( -## String(250, 50, 'Group 2', fillColor=colors.black), -## Circle(275,100,25), -## Circle(325,100,25)#, - - -##def getDrawing2(): -## """This drawing uses groups. Each group has two circles and a comment. -## The line style is set at group level and should be red for the left, -## bvlue for the right.""" -## D = Drawing(400, 200) -## -## Group1 = Group() -## -## Group1.add(String(50, 50, 'Group 1', fillColor=colors.black)) -## Group1.add(Circle(75,100,25)) -## Group1.add(Circle(125,100,25)) -## D.add(Group1) -## -## Group2 = Group( -## String(250, 50, 'Group 2', fillColor=colors.black), -## Circle(275,100,25), -## Circle(325,100,25)#, -## -## #group attributes -## #strokeColor=colors.blue -## ) -## D.add(Group2) - -## return D -## -## -##def getDrawing3(): -## """This uses a named reference object. The house is a 'subroutine' -## the basic brick colored walls are defined, but the roof and window -## color are undefined and may be set by the container.""" -## -## D = Drawing(400, 200, fill=colors.bisque) -## -## -## House = Group( -## Rect(2,20,36,30, fill=colors.bisque), #walls -## Polygon([0,20,40,20,20,5]), #roof -## Rect(8, 38, 8, 12), #door -## Rect(25, 38, 8, 7), #window -## Rect(8, 25, 8, 7), #window -## Rect(25, 25, 8, 7) #window -## -## ) -## D.addDef('MyHouse', House) -## -## # one row all the same color -## D.add(String(20, 40, 'British Street...',fill=colors.black)) -## for i in range(6): -## x = i * 50 -## D.add(NamedReference('MyHouse', -## House, -## transform=translate(x, 40), -## fill = colors.brown -## ) -## ) -## -## # now do a row all different -## D.add(String(20, 120, 'Mediterranean Street...',fill=colors.black)) -## x = 0 -## for color in (colors.blue, colors.yellow, colors.orange, -## colors.red, colors.green, colors.chartreuse): -## D.add(NamedReference('MyHouse', -## House, -## transform=translate(x,120), -## fill = color, -## ) -## ) -## x = x + 50 -## #..by popular demand, the mayor gets a big one at the end -## D.add(NamedReference('MyHouse', -## House, -## transform=mmult(translate(x,110), scale(1.2,1.2)), -## fill = color, -## ) -## ) -## -## -## return D -## -##def getDrawing4(): -## """This tests that attributes are 'unset' correctly when -## one steps back out of a drawing node. All the circles are part of a -## group setting the line color to blue; the second circle explicitly -## sets it to red. Ideally, the third circle should go back to blue.""" -## D = Drawing(400, 200) -## -## -## G = Group( -## Circle(100,100,20), -## Circle(200,100,20, stroke=colors.blue), -## Circle(300,100,20), -## stroke=colors.red, -## stroke_width=3, -## fill=colors.aqua -## ) -## D.add(G) -## -## -## D.add(String(10,50, 'Stack Unwinding - should be red, blue, red')) -## -## return D -## -## -##def getDrawing5(): -## """This Rotates Coordinate Axes""" -## D = Drawing(400, 200) -## -## -## -## Axis = Group( -## Line(0,0,100,0), #x axis -## Line(0,0,0,50), # y axis -## Line(0,10,10,10), #ticks on y axis -## Line(0,20,10,20), -## Line(0,30,10,30), -## Line(0,40,10,40), -## Line(10,0,10,10), #ticks on x axis -## Line(20,0,20,10), -## Line(30,0,30,10), -## Line(40,0,40,10), -## Line(50,0,50,10), -## Line(60,0,60,10), -## Line(70,0,70,10), -## Line(80,0,80,10), -## Line(90,0,90,10), -## String(20, 35, 'Axes', fill=colors.black) -## ) -## -## D.addDef('Axes', Axis) -## -## D.add(NamedReference('Axis', Axis, -## transform=translate(10,10))) -## D.add(NamedReference('Axis', Axis, -## transform=mmult(translate(150,10),rotate(15))) -## ) -## return D -## -##def getDrawing6(): -## """This Rotates Text""" -## D = Drawing(400, 300, fill=colors.black) -## -## xform = translate(200,150) -## C = (colors.black,colors.red,colors.green,colors.blue,colors.brown,colors.gray, colors.pink, -## colors.lavender,colors.lime, colors.mediumblue, colors.magenta, colors.limegreen) -## -## for i in range(12): -## D.add(String(0, 0, ' - - Rotated Text', fill=C[i%len(C)], transform=mmult(xform, rotate(30*i)))) -## -## return D -## -##def getDrawing7(): -## """This defines and tests a simple UserNode0 (the trailing zero denotes -## an experimental method which is not part of the supported API yet). -## Each of the four charts is a subclass of UserNode which generates a random -## series when rendered.""" -## -## class MyUserNode(UserNode0): -## import whrandom, math -## -## -## def provideNode(self, sender): -## """draw a simple chart that changes everytime it's drawn""" -## # print "here's a random number %s" % self.whrandom.random() -## #print "MyUserNode.provideNode being called by %s" % sender -## g = Group() -## #g._state = self._state # this is naughty -## PingoNode.__init__(g, self._state) # is this less naughty ? -## w = 80.0 -## h = 50.0 -## g.add(Rect(0,0, w, h, stroke=colors.black)) -## N = 10.0 -## x,y = (0,h) -## dx = w/N -## for ii in range(N): -## dy = (h/N) * self.whrandom.random() -## g.add(Line(x,y,x+dx, y-dy)) -## x = x + dx -## y = y - dy -## return g -## -## D = Drawing(400,200, fill=colors.white) # AR - same size as others -## -## D.add(MyUserNode()) -## -## graphcolor= [colors.green, colors.red, colors.brown, colors.purple] -## for ii in range(4): -## D.add(Group( MyUserNode(stroke=graphcolor[ii], stroke_width=2), -## transform=translate(ii*90,0) )) -## -## #un = MyUserNode() -## #print un.provideNode() -## return D -## -##def getDrawing8(): -## """Test Path operations--lineto, curveTo, etc.""" -## D = Drawing(400, 200, fill=None, stroke=colors.purple, stroke_width=2) -## -## xform = translate(200,100) -## C = (colors.black,colors.red,colors.green,colors.blue,colors.brown,colors.gray, colors.pink, -## colors.lavender,colors.lime, colors.mediumblue, colors.magenta, colors.limegreen) -## p = Path(50,50) -## p.lineTo(100,100) -## p.moveBy(-25,25) -## p.curveTo(150,125, 125,125, 200,50) -## p.curveTo(175, 75, 175, 98, 62, 87) -## -## -## D.add(p) -## D.add(String(10,30, 'Tests of path elements-lines and bezier curves-and text formating')) -## D.add(Line(220,150, 220,200, stroke=colors.red)) -## D.add(String(220,180, "Text should be centered", text_anchor="middle") ) -## -## -## return D - - -if __name__=='__main__': - print __doc__ \ No newline at end of file diff --git a/bin/reportlab/graphics/testshapes.py b/bin/reportlab/graphics/testshapes.py deleted file mode 100644 index 261d7d73d34..00000000000 --- a/bin/reportlab/graphics/testshapes.py +++ /dev/null @@ -1,574 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/testshapes.py - -# testshapes.py - draws shapes onto a PDF canvas. - -""" -Execute the script to see some test drawings. - -This contains a number of routines to generate test drawings -for reportlab/graphics. For now many of them are contrived, -but we will expand them to try and trip up any parser. -Feel free to add more. -""" - -__version__ = ''' $Id $ ''' - - -import os, sys - -from reportlab.lib import colors -from reportlab.lib.units import cm -from reportlab.pdfgen.canvas import Canvas -from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.platypus import Flowable -from reportlab.graphics.shapes import * -from reportlab.graphics.renderPDF import _PDFRenderer -import unittest - -_FONTS = ['Times-Roman','Courier','Times-BoldItalic',] - -######################################################### -# -# Collections of shape drawings. -# -######################################################### - - -def getFailedDrawing(funcName): - """Generate a drawing in case something goes really wrong. - - This will create a drawing to be displayed whenever some - other drawing could not be executed, because the generating - function does something terribly wrong! The box contains - an attention triangle, plus some error message. - """ - - D = Drawing(400, 200) - - points = [200,170, 140,80, 260,80] - D.add(Polygon(points, - strokeWidth=0.5*cm, - strokeColor=colors.red, - fillColor=colors.yellow)) - - s = String(200, 40, - "Error in generating function '%s'!" % funcName, - textAnchor='middle') - D.add(s) - - return D - - -# These are the real drawings to be eye-balled. - -def getDrawing01(): - """Hello World, on a rectangular background. - - The rectangle's fillColor is yellow. - The string's fillColor is red. - """ - - 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, 'Some special characters \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', fillColor=colors.red)) - - return D - - -def getDrawing02(): - """Various Line shapes. - - The lines are blue and their strokeWidth is 5 mm. - One line has a strokeDashArray set to [5, 10, 15]. - """ - - D = Drawing(400, 200) - D.add(Line(50,50, 300,100, - strokeColor=colors.blue, - strokeWidth=0.5*cm, - )) - D.add(Line(50,100, 300,50, - strokeColor=colors.blue, - strokeWidth=0.5*cm, - strokeDashArray=[5, 10, 15], - )) - - #x = 1/0 # Comment this to see the actual drawing! - - return D - - -def getDrawing03(): - """Text strings in various sizes and different fonts. - - Font size increases from 12 to 36 and from bottom left - to upper right corner. The first ones should be in - Times-Roman. Finally, a solitary Courier string at - the top right corner. - """ - - D = Drawing(400, 200) - for size in range(12, 36, 4): - D.add(String(10+size*2, - 10+size*2, - 'Hello World', - fontName=_FONTS[0], - fontSize=size)) - - D.add(String(150, 150, - 'Hello World', - fontName=_FONTS[1], - fontSize=36)) - return D - - -def getDrawing04(): - """Text strings in various colours. - - Colours are blue, yellow and red from bottom left - to upper right. - """ - - D = Drawing(400, 200) - i = 0 - for color in (colors.blue, colors.yellow, colors.red): - D.add(String(50+i*30, 50+i*30, - 'Hello World', fillColor=color)) - i = i + 1 - - return D - - -def getDrawing05(): - """Text strings with various anchors (alignments). - - Text alignment conforms to the anchors in the left column. - """ - - D = Drawing(400, 200) - - lineX = 250 - D.add(Line(lineX,10, lineX,190, strokeColor=colors.gray)) - - y = 130 - for anchor in ('start', 'middle', 'end'): - D.add(String(lineX, y, 'Hello World', textAnchor=anchor)) - D.add(String(50, y, anchor + ':')) - y = y - 30 - - return D - - -def getDrawing06(): - """This demonstrates all the basic shapes at once. - - There are no groups or references. - Each solid shape should have a purple fill. - """ - - purple = colors.purple - purple = colors.green - - D = Drawing(400, 200) #, fillColor=purple) - - D.add(Line(10,10, 390,190)) - - D.add(Circle(100,100,20, fillColor=purple)) - D.add(Circle(200,100,40, fillColor=purple)) - D.add(Circle(300,100,30, fillColor=purple)) - - D.add(Wedge(330,100,40, -10,40, fillColor=purple)) - - D.add(PolyLine([120,10, 130,20, 140,10, 150,20, 160,10, - 170,20, 180,10, 190,20, 200,10], fillColor=purple)) - - D.add(Polygon([300,20, 350,20, 390,80, 300,75, 330,40], fillColor=purple)) - - D.add(Ellipse(50,150, 40, 20, fillColor=purple)) - - D.add(Rect(120,150, 60,30, - strokeWidth=10, - strokeColor=colors.yellow, - fillColor=purple)) #square corners - - D.add(Rect(220, 150, 60, 30, 10, 10, fillColor=purple)) #round corners - - from reportlab.lib.validators import inherit - D.add(String(10,50, 'Basic Shapes', fillColor=colors.black, fontName=inherit)) - - return D - -def getDrawing07(): - """This tests the ability to translate and rotate groups. The first set of axes should be - near the bottom left of the drawing. The second should be rotated counterclockwise - by 15 degrees. The third should be rotated by 30 degrees.""" - D = Drawing(400, 200) - - Axis = Group( - Line(0,0,100,0), #x axis - Line(0,0,0,50), # y axis - Line(0,10,10,10), #ticks on y axis - Line(0,20,10,20), - Line(0,30,10,30), - Line(0,40,10,40), - Line(10,0,10,10), #ticks on x axis - Line(20,0,20,10), - Line(30,0,30,10), - Line(40,0,40,10), - Line(50,0,50,10), - Line(60,0,60,10), - Line(70,0,70,10), - Line(80,0,80,10), - Line(90,0,90,10), - String(20, 35, 'Axes', fill=colors.black) - ) - - firstAxisGroup = Group(Axis) - firstAxisGroup.translate(10,10) - D.add(firstAxisGroup) - - secondAxisGroup = Group(Axis) - secondAxisGroup.translate(150,10) - secondAxisGroup.rotate(15) - - D.add(secondAxisGroup) - - - thirdAxisGroup = Group(Axis, transform=mmult(translate(300,10), rotate(30))) - D.add(thirdAxisGroup) - - return D - - -def getDrawing08(): - """This tests the ability to scale coordinates. The bottom left set of axes should be - near the bottom left of the drawing. The bottom right should be stretched vertically - by a factor of 2. The top left one should be stretched horizontally by a factor of 2. - The top right should have the vertical axiss leaning over to the right by 30 degrees.""" - D = Drawing(400, 200) - - Axis = Group( - Line(0,0,100,0), #x axis - Line(0,0,0,50), # y axis - Line(0,10,10,10), #ticks on y axis - Line(0,20,10,20), - Line(0,30,10,30), - Line(0,40,10,40), - Line(10,0,10,10), #ticks on x axis - Line(20,0,20,10), - Line(30,0,30,10), - Line(40,0,40,10), - Line(50,0,50,10), - Line(60,0,60,10), - Line(70,0,70,10), - Line(80,0,80,10), - Line(90,0,90,10), - String(20, 35, 'Axes', fill=colors.black) - ) - - firstAxisGroup = Group(Axis) - firstAxisGroup.translate(10,10) - D.add(firstAxisGroup) - - secondAxisGroup = Group(Axis) - secondAxisGroup.translate(150,10) - secondAxisGroup.scale(1,2) - D.add(secondAxisGroup) - - thirdAxisGroup = Group(Axis) - thirdAxisGroup.translate(10,125) - thirdAxisGroup.scale(2,1) - D.add(thirdAxisGroup) - - fourthAxisGroup = Group(Axis) - fourthAxisGroup.translate(250,125) - fourthAxisGroup.skew(30,0) - D.add(fourthAxisGroup) - - - return D - -def getDrawing09(): - """This tests rotated strings - - Some renderers will have a separate mechanism for font drawing. This test - just makes sure strings get transformed the same way as regular graphics.""" - D = Drawing(400, 200) - - fontName = _FONTS[0] - fontSize = 12 - text = "I should be totally horizontal and enclosed in a box" - textWidth = stringWidth(text, fontName, fontSize) - - - g1 = Group( - String(20, 20, text, fontName=fontName, fontSize = fontSize), - Rect(18, 18, textWidth + 4, fontSize + 4, fillColor=None) - ) - D.add(g1) - - text = "I should slope up by 15 degrees, so my right end is higher than my left" - textWidth = stringWidth(text, fontName, fontSize) - g2 = Group( - String(20, 20, text, fontName=fontName, fontSize = fontSize), - Rect(18, 18, textWidth + 4, fontSize + 4, fillColor=None) - ) - g2.translate(0, 50) - g2.rotate(15) - D.add(g2) - - return D - -def getDrawing10(): - """This tests nested groups with multiple levels of coordinate transformation. - Each box should be staggered up and to the right, moving by 25 points each time.""" - D = Drawing(400, 200) - - fontName = _FONTS[0] - fontSize = 12 - - g1 = Group( - Rect(0, 0, 100, 20, fillColor=colors.yellow), - String(5, 5, 'Text in the box', fontName=fontName, fontSize = fontSize) - ) - D.add(g1) - - g2 = Group(g1, transform = translate(25,25)) - D.add(g2) - - g3 = Group(g2, transform = translate(25,25)) - D.add(g3) - - g4 = Group(g3, transform = translate(25,25)) - D.add(g4) - - - return D - -from widgets.signsandsymbols import SmileyFace -def getDrawing11(): - '''test of anchoring''' - def makeSmiley(x, y, size, color): - "Make a smiley data item representation." - d = size - s = SmileyFace() - s.fillColor = color - s.x = x-d - s.y = y-d - s.size = d*2 - return s - - D = Drawing(400, 200) #, fillColor=colors.purple) - g = Group(transform=(1,0,0,1,0,0)) - g.add(makeSmiley(100,100,10,colors.red)) - g.add(Line(90,100,110,100,strokeColor=colors.green)) - g.add(Line(100,90,100,110,strokeColor=colors.green)) - D.add(g) - g = Group(transform=(2,0,0,2,100,-100)) - g.add(makeSmiley(100,100,10,colors.blue)) - g.add(Line(90,100,110,100,strokeColor=colors.green)) - g.add(Line(100,90,100,110,strokeColor=colors.green)) - D.add(g) - g = Group(transform=(2,0,0,2,0,0)) - return D - - -def getDrawing12(): - """Text strings in a non-standard font. - All that is required is to place the .afm and .pfb files - on the font patch given in rl_config.py, - for example in reportlab/lib/fonts/. - """ - faceName = "LettErrorRobot-Chrome" - D = Drawing(400, 200) - for size in range(12, 36, 4): - D.add(String(10+size*2, - 10+size*2, - 'Hello World', - fontName=faceName, - fontSize=size)) - return D - -def getDrawing13(): - 'Test Various TTF Fonts' - from reportlab.pdfbase import pdfmetrics, ttfonts - pdfmetrics.registerFont(ttfonts.TTFont("LuxiSerif", "luxiserif.ttf")) - pdfmetrics.registerFont(ttfonts.TTFont("Rina", "rina.ttf")) - _FONTS[1] = 'LuxiSerif' - _FONTS[2] = 'Rina' - F = ['Times-Roman','Courier','Helvetica','LuxiSerif', 'Rina'] - if sys.platform=='win32': - for name, ttf in [('Adventurer Light SF','Advlit.ttf'),('ArialMS','ARIAL.TTF'), - ('Arial Unicode MS', 'ARIALUNI.TTF'), - ('Book Antiqua','BKANT.TTF'), - ('Century Gothic','GOTHIC.TTF'), - ('Comic Sans MS', 'COMIC.TTF'), - ('Elementary Heavy SF Bold','Vwagh.ttf'), - ('Firenze SF','flot.ttf'), - ('Garamond','GARA.TTF'), - ('Jagger','Rols.ttf'), - ('Monotype Corsiva','MTCORSVA.TTF'), - ('Seabird SF','seag.ttf'), - ('Tahoma','TAHOMA.TTF'), - ('VerdanaMS','VERDANA.TTF'), - ]: - for D in ('c:\WINNT','c:\Windows'): - fn = os.path.join(D,'Fonts',ttf) - if os.path.isfile(fn): - try: - f = ttfonts.TTFont(name, fn) - pdfmetrics.registerFont(f) - F.append(name) - except: - pass - - def drawit(F,w=400,h=200,fontSize=12,slack=2,gap=5): - D = Drawing(w,h) - th = 2*gap + fontSize*1.2 - gh = gap + .2*fontSize - y = h - 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" - textWidth = stringWidth(text, fontName, fontSize) - maxx = max(maxx,textWidth+20) - D.add( - Group(Rect(8, y-gh, textWidth + 4, th, strokeColor=colors.red, strokeWidth=.5, fillColor=colors.lightgrey), - String(10, y, text, fontName=fontName, fontSize = fontSize))) - y -= 5 - return maxx, h-y+gap, D - maxx, maxy, D = drawit(F) - if maxx>400 or maxy>200: _,_,D = drawit(F,maxx,maxy) - return D - -##def getDrawing14(): -## """This tests inherited properties. Each font should be as it says.""" -## D = Drawing(400, 200) -## -## fontSize = 12 -## D.fontName = 'Courier' -## -## g1 = Group( -## Rect(0, 0, 150, 20, fillColor=colors.yellow), -## String(5, 5, 'Inherited Courier', fontName=inherit, fontSize = fontSize) -## ) -## D.add(g1) -## -## g2 = Group(g1, transform = translate(25,25)) -## D.add(g2) -## -## g3 = Group(g2, transform = translate(25,25)) -## D.add(g3) -## -## g4 = Group(g3, transform = translate(25,25)) -## D.add(g4) -## -## -## return D -def getAllFunctionDrawingNames(doTTF=1): - "Get a list of drawing function names from somewhere." - - funcNames = [] - - # Here we get the names from the global name space. - symbols = globals().keys() - symbols.sort() - for funcName in symbols: - if funcName[0:10] == 'getDrawing': - if doTTF or funcName!='getDrawing13': - funcNames.append(funcName) - - return funcNames - -def _evalFuncDrawing(name, D, l=None, g=None): - try: - d = eval(name + '()', g or globals(), l or locals()) - except: - d = getFailedDrawing(name) - D.append((d, eval(name + '.__doc__'), name[3:])) - -def getAllTestDrawings(doTTF=1): - D = [] - for f in getAllFunctionDrawingNames(doTTF=doTTF): - _evalFuncDrawing(f,D) - return D - -def writePDF(drawings): - "Create and save a PDF file containing some drawings." - - pdfPath = os.path.splitext(sys.argv[0])[0] + '.pdf' - c = Canvas(pdfPath) - c.setFont(_FONTS[0], 32) - c.drawString(80, 750, 'ReportLab Graphics-Shapes Test') - - # Print drawings in a loop, with their doc strings. - c.setFont(_FONTS[0], 12) - y = 740 - i = 1 - for (drawing, docstring, funcname) in drawings: - if y < 300: # Allows 5-6 lines of text. - c.showPage() - y = 740 - # Draw a title. - y = y - 30 - c.setFont(_FONTS[2],12) - c.drawString(80, y, '%s (#%d)' % (funcname, i)) - c.setFont(_FONTS[0],12) - y = y - 14 - textObj = c.beginText(80, y) - textObj.textLines(docstring) - c.drawText(textObj) - y = textObj.getY() - y = y - drawing.height - drawing.drawOn(c, 80, y) - i = i + 1 - - c.save() - print 'wrote %s ' % pdfPath - - -class ShapesTestCase(unittest.TestCase): - "Test generating all kinds of shapes." - - def setUp(self): - "Prepare some things before the tests start." - - self.funcNames = getAllFunctionDrawingNames() - self.drawings = [] - - - def tearDown(self): - "Do what has to be done after the tests are over." - - writePDF(self.drawings) - - - # This should always succeed. If each drawing would be - # wrapped in a dedicated test method like this one, it - # would be possible to have a count for wrong tests - # as well... Something like this is left for later... - def testAllDrawings(self): - "Make a list of drawings." - - for f in self.funcNames: - if f[0:10] == 'getDrawing': - # Make an instance and get its doc string. - # If that fails, use a default error drawing. - _evalFuncDrawing(f,self.drawings) - - -def makeSuite(): - "Make a test suite for unit testing." - - suite = unittest.TestSuite() - suite.addTest(ShapesTestCase('testAllDrawings')) - return suite - - -if __name__ == "__main__": - unittest.TextTestRunner().run(makeSuite()) diff --git a/bin/reportlab/graphics/widgetbase.py b/bin/reportlab/graphics/widgetbase.py deleted file mode 100644 index c529c0ded88..00000000000 --- a/bin/reportlab/graphics/widgetbase.py +++ /dev/null @@ -1,502 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgetbase.py -__version__=''' $Id: widgetbase.py 2668 2005-09-05 10:23:51Z rgbecker $ ''' -import string - -from reportlab.graphics import shapes -from reportlab import rl_config -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * - - -class PropHolder: - '''Base for property holders''' - - _attrMap = None - - def verify(self): - """If the _attrMap attribute is not None, this - checks all expected attributes are present; no - unwanted attributes are present; and (if a - checking function is found) checks each - attribute has a valid value. Either succeeds - or raises an informative exception. - """ - - if self._attrMap is not None: - for key in self.__dict__.keys(): - if key[0] <> '_': - msg = "Unexpected attribute %s found in %s" % (key, self) - assert self._attrMap.has_key(key), msg - for (attr, metavalue) in self._attrMap.items(): - msg = "Missing attribute %s from %s" % (attr, self) - assert hasattr(self, attr), msg - value = getattr(self, attr) - args = (value, attr, self.__class__.__name__) - assert metavalue.validate(value), "Invalid value %s for attribute %s in class %s" % args - - if rl_config.shapeChecking: - """This adds the ability to check every attribute assignment - as it is made. It slows down shapes but is a big help when - developing. It does not get defined if rl_config.shapeChecking = 0. - """ - - def __setattr__(self, name, value): - """By default we verify. This could be off - in some parallel base classes.""" - validateSetattr(self,name,value) - - - def getProperties(self,recur=1): - """Returns a list of all properties which can be edited and - which are not marked as private. This may include 'child - widgets' or 'primitive shapes'. You are free to override - this and provide alternative implementations; the default - one simply returns everything without a leading underscore. - """ - - from reportlab.lib.validators import isValidChild - - # TODO when we need it, but not before - - # expose sequence contents? - - props = {} - for name in self.__dict__.keys(): - if name[0:1] <> '_': - component = getattr(self, name) - - if recur and isValidChild(component): - # child object, get its properties too - childProps = component.getProperties(recur=recur) - 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. - if childKey[0] == '[': - props['%s%s' % (name, childKey)] = childValue - else: - props['%s.%s' % (name, childKey)] = childValue - else: - props[name] = component - - return props - - - def setProperties(self, propDict): - """Permits bulk setting of properties. These may include - child objects e.g. "chart.legend.width = 200". - - All assignments will be validated by the object as if they - were set individually in python code. - - All properties of a top-level object are guaranteed to be - set before any of the children, which may be helpful to - widget designers. - """ - - childPropDicts = {} - for (name, value) in propDict.items(): - parts = string.split(name, '.', 1) - if len(parts) == 1: - #simple attribute, set it now - setattr(self, name, value) - else: - (childName, remains) = parts - try: - childPropDicts[childName][remains] = value - except KeyError: - childPropDicts[childName] = {remains: value} - - # now assign to children - for (childName, childPropDict) in childPropDicts.items(): - child = getattr(self, childName) - child.setProperties(childPropDict) - - - def dumpProperties(self, prefix=""): - """Convenience. Lists them on standard output. You - may provide a prefix - mostly helps to generate code - samples for documentation. - """ - - propList = self.getProperties().items() - propList.sort() - if prefix: - prefix = prefix + '.' - for (name, value) in propList: - print '%s%s = %s' % (prefix, name, value) - - -class Widget(PropHolder, shapes.UserNode): - """Base for all user-defined widgets. Keep as simple as possible. Does - not inherit from Shape so that we can rewrite shapes without breaking - widgets and vice versa.""" - - def _setKeywords(self,**kw): - for k,v in kw.items(): - if not self.__dict__.has_key(k): - setattr(self,k,v) - - def draw(self): - msg = "draw() must be implemented for each Widget!" - raise shapes.NotImplementedError, msg - - def demo(self): - msg = "demo() must be implemented for each Widget!" - raise shapes.NotImplementedError, msg - - def provideNode(self): - return self.draw() - - def getBounds(self): - "Return outer boundary as x1,y1,x2,y2. Can be overridden for efficiency" - return self.draw().getBounds() - -_ItemWrapper={} - -class TypedPropertyCollection(PropHolder): - """A container with properties for objects of the same kind. - - This makes it easy to create lists of objects. You initialize - it with a class of what it is to contain, and that is all you - can add to it. You can assign properties to the collection - as a whole, or to a numeric index within it; if so it creates - a new child object to hold that data. - - So: - wedges = TypedPropertyCollection(WedgeProperties) - wedges.strokeWidth = 2 # applies to all - wedges.strokeColor = colors.red # applies to all - wedges[3].strokeColor = colors.blue # only to one - - The last line should be taken as a prescription of how to - create wedge no. 3 if one is needed; no error is raised if - there are only two data points. - - We try and make sensible use of tuple indeces. - line[(3,x)] is backed by line[(3,)], line[3] & line - """ - - def __init__(self, exampleClass): - #give it same validation rules as what it holds - self.__dict__['_value'] = exampleClass() - self.__dict__['_children'] = {} - - def wKlassFactory(self,Klass): - class WKlass(Klass): - def __getattr__(self,name): - try: - return self.__class__.__bases__[0].__getattr__(self,name) - except: - i = self._index - if i: - c = self._parent._children - if c.has_key(i) and c[i].__dict__.has_key(name): - return getattr(c[i],name) - elif len(i)==1: - i = i[0] - if c.has_key(i) and c[i].__dict__.has_key(name): - return getattr(c[i],name) - return getattr(self._parent,name) - return WKlass - - def __getitem__(self, index): - try: - return self._children[index] - except KeyError: - Klass = self._value.__class__ - if _ItemWrapper.has_key(Klass): - WKlass = _ItemWrapper[Klass] - else: - _ItemWrapper[Klass] = WKlass = self.wKlassFactory(Klass) - - child = WKlass() - child._parent = self - if type(index) in (type(()),type([])): - index = tuple(index) - if len(index)>1: - child._index = tuple(index[:-1]) - else: - child._index = None - else: - child._index = None - for i in filter(lambda x,K=child.__dict__.keys(): x in K,child._attrMap.keys()): - del child.__dict__[i] - - self._children[index] = child - return child - - def has_key(self,key): - if type(key) in (type(()),type([])): key = tuple(key) - return self._children.has_key(key) - - def __setitem__(self, key, value): - msg = "This collection can only hold objects of type %s" % self._value.__class__.__name__ - assert isinstance(value, self._value.__class__), msg - - def __len__(self): - return len(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(): - props['%s' % key] = value - - for idx in self._children.keys(): - childProps = self._children[idx].getProperties(recur=recur) - for (key, value) in childProps.items(): - if not hasattr(self,key) or getattr(self, key)<>value: - newKey = '[%s].%s' % (idx, key) - props[newKey] = value - return props - - def setVector(self,**kw): - for name, value in kw.items(): - for i in xrange(len(value)): - setattr(self[i],name,value[i]) - - def __getattr__(self,name): - return getattr(self._value,name) - - def __setattr__(self,name,value): - return setattr(self._value,name,value) - -## No longer needed! -class StyleProperties(PropHolder): - """A container class for attributes used in charts and legends. - - Attributes contained can be those for any graphical element - (shape?) in the ReportLab graphics package. The idea for this - container class is to be useful in combination with legends - and/or the individual appearance of data series in charts. - - A legend could be as simple as a wrapper around a list of style - properties, where the 'desc' attribute contains a descriptive - string and the rest could be used by the legend e.g. to draw - something like a color swatch. The graphical presentation of - the legend would be its own business, though. - - A chart could be inspecting a legend or, more directly, a list - of style properties to pick individual attributes that it knows - about in order to render a particular row of the data. A bar - chart e.g. could simply use 'strokeColor' and 'fillColor' for - drawing the bars while a line chart could also use additional - ones like strokeWidth. - """ - - _attrMap = AttrMap( - strokeWidth = AttrMapValue(isNumber), - strokeLineCap = AttrMapValue(isNumber), - strokeLineJoin = AttrMapValue(isNumber), - strokeMiterLimit = AttrMapValue(None), - strokeDashArray = AttrMapValue(isListOfNumbersOrNone), - strokeOpacity = AttrMapValue(isNumber), - strokeColor = AttrMapValue(isColorOrNone), - fillColor = AttrMapValue(isColorOrNone), - desc = AttrMapValue(isString), - ) - - def __init__(self, **kwargs): - "Initialize with attributes if any." - - for k, v in kwargs.items(): - setattr(self, k, v) - - - def __setattr__(self, name, value): - "Verify attribute name and value, before setting it." - validateSetattr(self,name,value) - - -class TwoCircles(Widget): - def __init__(self): - self.leftCircle = shapes.Circle(100,100,20, fillColor=colors.red) - self.rightCircle = shapes.Circle(300,100,20, fillColor=colors.red) - - def draw(self): - return shapes.Group(self.leftCircle, self.rightCircle) - - -class Face(Widget): - """This draws a face with two eyes. - - It exposes a couple of properties - to configure itself and hides all other details. - """ - - _attrMap = AttrMap( - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - size = AttrMapValue(isNumber), - skinColor = AttrMapValue(isColorOrNone), - eyeColor = AttrMapValue(isColorOrNone), - mood = AttrMapValue(OneOf('happy','sad','ok')), - ) - - def __init__(self): - self.x = 10 - self.y = 10 - self.size = 80 - self.skinColor = None - self.eyeColor = colors.blue - self.mood = 'happy' - - def demo(self): - pass - - def draw(self): - s = self.size # abbreviate as we will use this a lot - g = shapes.Group() - g.transform = [1,0,0,1,self.x, self.y] - - # background - g.add(shapes.Circle(s * 0.5, s * 0.5, s * 0.5, fillColor=self.skinColor)) - - # left eye - g.add(shapes.Circle(s * 0.35, s * 0.65, s * 0.1, fillColor=colors.white)) - g.add(shapes.Circle(s * 0.35, s * 0.65, s * 0.05, fillColor=self.eyeColor)) - - # right eye - g.add(shapes.Circle(s * 0.65, s * 0.65, s * 0.1, fillColor=colors.white)) - g.add(shapes.Circle(s * 0.65, s * 0.65, s * 0.05, fillColor=self.eyeColor)) - - # nose - g.add(shapes.Polygon( - points=[s * 0.5, s * 0.6, s * 0.4, s * 0.3, s * 0.6, s * 0.3], - fillColor=None)) - - # mouth - if self.mood == 'happy': - offset = -0.05 - elif self.mood == 'sad': - offset = +0.05 - else: - offset = 0 - - g.add(shapes.Polygon( - points = [ - s * 0.3, s * 0.2, #left of mouth - s * 0.7, s * 0.2, #right of mouth - s * 0.6, s * (0.2 + offset), # the bit going up or down - s * 0.4, s * (0.2 + offset) # the bit going up or down - ], - fillColor = colors.pink, - strokeColor = colors.red, - strokeWidth = s * 0.03 - )) - - return g - - -class TwoFaces(Widget): - def __init__(self): - self.faceOne = Face() - self.faceOne.mood = "happy" - self.faceTwo = Face() - self.faceTwo.x = 100 - self.faceTwo.mood = "sad" - - def draw(self): - """Just return a group""" - return shapes.Group(self.faceOne, self.faceTwo) - - def demo(self): - """The default case already looks good enough, - no implementation needed here""" - pass - -class Sizer(Widget): - "Container to show size of all enclosed objects" - - _attrMap = AttrMap(BASE=shapes.SolidShape, - contents = AttrMapValue(isListOfShapes,desc="Contained drawable elements"), - ) - def __init__(self, *elements): - self.contents = [] - self.fillColor = colors.cyan - self.strokeColor = colors.magenta - - for elem in elements: - self.add(elem) - - 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(): - self._attrMap[name] = AttrMapValue(isValidChild) - setattr(self, name, node) - - def add(self, node, name=None): - """Appends non-None child node to the 'contents' attribute. In addition, - if a name is provided, it is subsequently accessible by name - """ - # propagates properties down - if node is not None: - assert isValidChild(node), "Can only add Shape or UserNode objects to a Group" - self.contents.append(node) - self._addNamedNode(name,node) - - def getBounds(self): - # get bounds of each object - if self.contents: - b = [] - for elem in self.contents: - b.append(elem.getBounds()) - return shapes.getRectsBounds(b) - else: - return (0,0,0,0) - - def draw(self): - g = shapes.Group() - (x1, y1, x2, y2) = self.getBounds() - r = shapes.Rect( - x = x1, - y = y1, - width = x2-x1, - height = y2-y1, - fillColor = self.fillColor, - strokeColor = self.strokeColor - ) - g.add(r) - for elem in self.contents: - g.add(elem) - return g - -def test(): - from reportlab.graphics.charts.piecharts import WedgeProperties - wedges = TypedPropertyCollection(WedgeProperties) - wedges.fillColor = colors.red - wedges.setVector(fillColor=(colors.blue,colors.green,colors.white)) - print len(_ItemWrapper) - - d = shapes.Drawing(400, 200) - tc = TwoCircles() - d.add(tc) - import renderPDF - renderPDF.drawToFile(d, 'sample_widget.pdf', 'A Sample Widget') - 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:' - d.dumpProperties() - renderPDF.drawToFile(d, 'face.pdf', 'A Sample Widget') - 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:' - d2.dumpProperties() - - -if __name__=='__main__': - test() diff --git a/bin/reportlab/graphics/widgets/__init__.py b/bin/reportlab/graphics/widgets/__init__.py deleted file mode 100644 index 8558748139b..00000000000 --- a/bin/reportlab/graphics/widgets/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/__init__.py -__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' \ No newline at end of file diff --git a/bin/reportlab/graphics/widgets/eventcal.py b/bin/reportlab/graphics/widgets/eventcal.py deleted file mode 100644 index 20f0b817431..00000000000 --- a/bin/reportlab/graphics/widgets/eventcal.py +++ /dev/null @@ -1,303 +0,0 @@ -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/eventcal.py -# Event Calendar widget -# author: Andy Robinson -"""This file is a -""" -__version__=''' $Id: eventcal.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -from reportlab.graphics.shapes import Line, Rect, Polygon, Drawing, Group, String, Circle, Wedge -from reportlab.graphics.charts.textlabels import Label -from reportlab.graphics.widgetbase import Widget -from reportlab.graphics import renderPDF - - - - -class EventCalendar(Widget): - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 300 - self.height = 150 - self.timeColWidth = None # if declared, use it; otherwise auto-size. - self.trackRowHeight = 20 - self.data = [] # list of Event objects - self.trackNames = None - - self.startTime = None #displays ALL data on day if not set - self.endTime = None # displays ALL data on day if not set - self.day = 0 - - - # we will keep any internal geometry variables - # here. These are computed by computeSize(), - # which is the first thing done when drawing. - self._talksVisible = [] # subset of data which will get plotted, cache - self._startTime = None - self._endTime = None - self._trackCount = 0 - self._colWidths = [] - self._colLeftEdges = [] # left edge of each column - - def computeSize(self): - "Called at start of draw. Sets various column widths" - self._talksVisible = self.getRelevantTalks(self.data) - self._trackCount = len(self.getAllTracks()) - self.computeStartAndEndTimes() - self._colLeftEdges = [self.x] - if self.timeColWidth is None: - w = self.width / (1 + self._trackCount) - self._colWidths = [w] * (1+ self._trackCount) - for i in range(self._trackCount): - self._colLeftEdges.append(self._colLeftEdges[-1] + w) - else: - self._colWidths = [self.timeColWidth] - w = (self.width - self.timeColWidth) / self._trackCount - for i in range(self._trackCount): - self._colWidths.append(w) - self._colLeftEdges.append(self._colLeftEdges[-1] + w) - - - - def computeStartAndEndTimes(self): - "Work out first and last times to display" - if self.startTime: - self._startTime = self.startTime - else: - for (title, speaker, trackId, day, start, duration) in self._talksVisible: - - if self._startTime is None: #first one - self._startTime = start - else: - if start < self._startTime: - self._startTime = start - - if self.endTime: - self._endTime = self.endTime - else: - for (title, speaker, trackId, day, start, duration) in self._talksVisible: - if self._endTime is None: #first one - self._endTime = start + duration - else: - if start + duration > self._endTime: - self._endTime = start + duration - - - - - def getAllTracks(self): - tracks = [] - for (title, speaker, trackId, day, hours, duration) in self.data: - if trackId is not None: - if trackId not in tracks: - tracks.append(trackId) - tracks.sort() - return tracks - - def getRelevantTalks(self, talkList): - "Scans for tracks actually used" - used = [] - for talk in talkList: - (title, speaker, trackId, day, hours, duration) = talk - assert trackId <> 0, "trackId must be None or 1,2,3... zero not allowed!" - if day == self.day: - if (((self.startTime is None) or ((hours + duration) >= self.startTime)) - and ((self.endTime is None) or (hours <= self.endTime))): - used.append(talk) - return used - - def scaleTime(self, theTime): - "Return y-value corresponding to times given" - axisHeight = self.height - self.trackRowHeight - # compute fraction between 0 and 1, 0 is at start of period - proportionUp = ((theTime - self._startTime) / (self._endTime - self._startTime)) - y = self.y + axisHeight - (axisHeight * proportionUp) - return y - - - def getTalkRect(self, startTime, duration, trackId, text): - "Return shapes for a specific talk" - g = Group() - y_bottom = self.scaleTime(startTime + duration) - y_top = self.scaleTime(startTime) - y_height = y_top - y_bottom - - if trackId is None: - #spans all columns - x = self._colLeftEdges[1] - width = self.width - self._colWidths[0] - else: - #trackId is 1-based and these arrays have the margin info in column - #zero, so no need to add 1 - x = self._colLeftEdges[trackId] - width = self._colWidths[trackId] - - lab = Label() - lab.setText(text) - lab.setOrigin(x + 0.5*width, y_bottom+0.5*y_height) - lab.boxAnchor = 'c' - lab.width = width - lab.height = y_height - lab.fontSize = 6 - - r = Rect(x, y_bottom, width, y_height, fillColor=colors.cyan) - g.add(r) - g.add(lab) - - #now for a label - # would expect to color-code and add text - return g - - def draw(self): - self.computeSize() - g = Group() - - # time column - g.add(Rect(self.x, self.y, self._colWidths[0], self.height - self.trackRowHeight, fillColor=colors.cornsilk)) - - # track headers - x = self.x + self._colWidths[0] - y = self.y + self.height - self.trackRowHeight - for trk in range(self._trackCount): - wid = self._colWidths[trk+1] - r = Rect(x, y, wid, self.trackRowHeight, fillColor=colors.yellow) - s = String(x + 0.5*wid, y, 'Track %d' % trk, align='middle') - g.add(r) - g.add(s) - x = x + wid - - for talk in self._talksVisible: - (title, speaker, trackId, day, start, duration) = talk - r = self.getTalkRect(start, duration, trackId, title + '\n' + speaker) - g.add(r) - - - return g - - - - -def test(): - "Make a conference event for day 1 of UP Python 2003" - - - d = Drawing(400,200) - - cal = EventCalendar() - cal.x = 50 - cal.y = 25 - cal.data = [ - # these might be better as objects instead of tuples, since I - # predict a large number of "optionsl" variables to affect - # formatting in future. - - #title, speaker, track id, day, start time (hrs), duration (hrs) - # track ID is 1-based not zero-based! - ('Keynote: Why design another programming language?', 'Guido van Rossum', None, 1, 9.0, 1.0), - - ('Siena Web Service Architecture', 'Marc-Andre Lemburg', 1, 1, 10.5, 1.5), - ('Extreme Programming in Python', 'Chris Withers', 2, 1, 10.5, 1.5), - ('Pattern Experiences in C++', 'Mark Radford', 3, 1, 10.5, 1.5), - ('What is the Type of std::toupper()', 'Gabriel Dos Reis', 4, 1, 10.5, 1.5), - ('Linguistic Variables: Clear Thinking with Fuzzy Logic ', 'Walter Banks', 5, 1, 10.5, 1.5), - - ('lunch, short presentations, vendor presentations', '', None, 1, 12.0, 2.0), - - ("CORBA? Isn't that obsolete", 'Duncan Grisby', 1, 1, 14.0, 1.5), - ("Python Design Patterns", 'Duncan Booth', 2, 1, 14.0, 1.5), - ("Inside Security Checks and Safe Exceptions", 'Brandon Bray', 3, 1, 14.0, 1.5), - ("Studying at a Distance", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 1, 14.0, 1.5), - ("Coding Standards - Given the ANSI C Standard why do I still need a coding Standard", 'Randy Marques', 5, 1, 14.0, 1.5), - - ("RESTful Python", 'Hamish Lawson', 1, 1, 16.0, 1.5), - ("Parsing made easier - a radical old idea", 'Andrew Koenig', 2, 1, 16.0, 1.5), - ("C++ & Multimethods", 'Julian Smith', 3, 1, 16.0, 1.5), - ("C++ Threading", 'Kevlin Henney', 4, 1, 16.0, 1.5), - ("The Organisation Strikes Back", 'Alan Griffiths & Sarah Lees', 5, 1, 16.0, 1.5), - - ('Birds of a Feather meeting', '', None, 1, 17.5, 2.0), - - ('Keynote: In the Spirit of C', 'Greg Colvin', None, 2, 9.0, 1.0), - - ('The Infinite Filing Cabinet - object storage in Python', 'Jacob Hallen', 1, 2, 10.5, 1.5), - ('Introduction to Python and Jython for C++ and Java Programmers', 'Alex Martelli', 2, 2, 10.5, 1.5), - ('Template metaprogramming in Haskell', 'Simon Peyton Jones', 3, 2, 10.5, 1.5), - ('Plenty People Programming: C++ Programming in a Group, Workshop with a difference', 'Nico Josuttis', 4, 2, 10.5, 1.5), - ('Design and Implementation of the Boost Graph Library', 'Jeremy Siek', 5, 2, 10.5, 1.5), - - ('lunch, short presentations, vendor presentations', '', None, 2, 12.0, 2.0), - - ("Building GUI Applications with PythonCard and PyCrust", 'Andy Todd', 1, 2, 14.0, 1.5), - ("Integrating Python, C and C++", 'Duncan Booth', 2, 2, 14.0, 1.5), - ("Secrets and Pitfalls of Templates", 'Nicolai Josuttis & David Vandevoorde', 3, 2, 14.0, 1.5), - ("Being a Mentor", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 2, 14.0, 1.5), - ("The Embedded C Extensions to C", 'Willem Wakker', 5, 2, 14.0, 1.5), - - ("Lightning Talks", 'Paul Brian', 1, 2, 16.0, 1.5), - ("Scripting Java Applications with Jython", 'Anthony Eden', 2, 2, 16.0, 1.5), - ("Metaprogramming and the Boost Metaprogramming Library", 'David Abrahams', 3, 2, 16.0, 1.5), - ("A Common Vendor ABI for C++ -- GCC's why, what and not", 'Nathan Sidwell & Gabriel Dos Reis', 4, 2, 16.0, 1.5), - ("The Timing and Cost of Choices", 'Hubert Matthews', 5, 2, 16.0, 1.5), - - ('Birds of a Feather meeting', '', None, 2, 17.5, 2.0), - - ('Keynote: The Cost of C & C++ Compatibility', 'Andy Koenig', None, 3, 9.0, 1.0), - - ('Prying Eyes: Generic Observer Implementations in C++', 'Andrei Alexandrescu', 1, 2, 10.5, 1.5), - ('The Roadmap to Generative Programming With C++', 'Ulrich Eisenecker', 2, 2, 10.5, 1.5), - ('Design Patterns in C++ and C# for the Common Language Runtime', 'Brandon Bray', 3, 2, 10.5, 1.5), - ('Extreme Hour (XH): (workshop) - Jutta Eckstein and Nico Josuttis', 'Jutta Ecstein', 4, 2, 10.5, 1.5), - ('The Lambda Library : Unnamed Functions for C++', 'Jaako Jarvi', 5, 2, 10.5, 1.5), - - ('lunch, short presentations, vendor presentations', '', None, 3, 12.0, 2.0), - - ('Reflective Metaprogramming', 'Daveed Vandevoorde', 1, 3, 14.0, 1.5), - ('Advanced Template Issues and Solutions (double session)', 'Herb Sutter',2, 3, 14.0, 3), - ('Concurrent Programming in Java (double session)', 'Angelika Langer', 3, 3, 14.0, 3), - ('What can MISRA-C (2nd Edition) do for us?', 'Chris Hills', 4, 3, 14.0, 1.5), - ('C++ Metaprogramming Concepts and Results', 'Walter E Brown', 5, 3, 14.0, 1.5), - - ('Binding C++ to Python with the Boost Python Library', 'David Abrahams', 1, 3, 16.0, 1.5), - ('Using Aspect Oriented Programming for Enterprise Application Integration', 'Arno Schmidmeier', 4, 3, 16.0, 1.5), - ('Defective C++', 'Marc Paterno', 5, 3, 16.0, 1.5), - - ("Speakers' Banquet & Birds of a Feather meeting", '', None, 3, 17.5, 2.0), - - ('Keynote: The Internet, Software and Computers - A Report Card', 'Alan Lenton', None, 4, 9.0, 1.0), - - ('Multi-Platform Software Development; Lessons from the Boost libraries', 'Beman Dawes', 1, 5, 10.5, 1.5), - ('The Stability of the C++ ABI', 'Steve Clamage', 2, 5, 10.5, 1.5), - ('Generic Build Support - A Pragmatic Approach to the Software Build Process', 'Randy Marques', 3, 5, 10.5, 1.5), - ('How to Handle Project Managers: a survival guide', 'Barb Byro', 4, 5, 10.5, 1.5), - - ('lunch, ACCU AGM', '', None, 5, 12.0, 2.0), - - ('Sauce: An OO recursive descent parser; its design and implementation.', 'Jon Jagger', 1, 5, 14.0, 1.5), - ('GNIRTS ESAC REWOL - Bringing the UNIX filters to the C++ iostream library.', 'JC van Winkel', 2, 5, 14.0, 1.5), - ('Pattern Writing: Live and Direct', 'Frank Buschmann & Kevlin Henney', 3, 5, 14.0, 3.0), - ('The Future of Programming Languages - A Goldfish Bowl', 'Francis Glassborow and friends', 3, 5, 14.0, 1.5), - - ('Honey, I Shrunk the Threads: Compile-time checked multithreaded transactions in C++', 'Andrei Alexandrescu', 1, 5, 16.0, 1.5), - ('Fun and Functionality with Functors', 'Lois Goldthwaite', 2, 5, 16.0, 1.5), - ('Agile Enough?', 'Alan Griffiths', 4, 5, 16.0, 1.5), - ("Conference Closure: A brief plenary session", '', None, 5, 17.5, 0.5), - - ] - - #return cal - cal.day = 1 - - d.add(cal) - - - for format in ['pdf']:#,'gif','png']: - out = d.asString(format) - open('eventcal.%s' % format, 'wb').write(out) - print 'saved eventcal.%s' % format - -if __name__=='__main__': - test() diff --git a/bin/reportlab/graphics/widgets/flags.py b/bin/reportlab/graphics/widgets/flags.py deleted file mode 100644 index d83541c0404..00000000000 --- a/bin/reportlab/graphics/widgets/flags.py +++ /dev/null @@ -1,879 +0,0 @@ -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/flags.py -# Flag Widgets - a collection of flags as widgets -# author: John Precedo (johnp@reportlab.com) -"""This file is a collection of flag graphics as widgets. - -All flags are represented at the ratio of 1:2, even where the official ratio for the flag is something else -(such as 3:5 for the German national flag). The only exceptions are for where this would look _very_ wrong, -such as the Danish flag whose (ratio is 28:37), or the Swiss flag (which is square). - -Unless otherwise stated, these flags are all the 'national flags' of the countries, rather than their -state flags, naval flags, ensigns or any other variants. (National flags are the flag flown by civilians -of a country and the ones usually used to represent a country abroad. State flags are the variants used by -the government and by diplomatic missions overseas). - -To check on how close these are to the 'official' representations of flags, check the World Flag Database at -http://www.flags.ndirect.co.uk/ - -The flags this file contains are: - -EU Members: -United Kingdom, Austria, Belgium, Denmark, Finland, France, Germany, Greece, Ireland, Italy, Luxembourg, -Holland (The Netherlands), Spain, Sweden - -Others: -USA, Czech Republic, European Union, Switzerland, Turkey, Brazil - -(Brazilian flag contributed by Publio da Costa Melo [publio@planetarium.com.br]). -""" -__version__=''' $Id: flags.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -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 -import copy -from math import sin, cos, pi - -validFlag=OneOf(None, - 'UK', - 'USA', - 'Afghanistan', - 'Austria', - 'Belgium', - 'China', - 'Cuba', - 'Denmark', - 'Finland', - 'France', - 'Germany', - 'Greece', - 'Ireland', - 'Italy', - 'Japan', - 'Luxembourg', - 'Holland', - 'Palestine', - 'Portugal', - 'Russia', - 'Spain', - 'Sweden', - 'Norway', - 'CzechRepublic', - 'Turkey', - 'Switzerland', - 'EU', - 'Brazil' - ) - -_size = 100. - -class Star(_Symbol): - """This draws a 5-pointed star. - - possible attributes: - 'x', 'y', 'size', 'fillColor', 'strokeColor' - - """ - _attrMap = AttrMap(BASE=_Symbol, - angle = AttrMapValue(isNumber, desc='angle in degrees'), - ) - _size = 100. - - def __init__(self): - _Symbol.__init__(self) - self.size = 100 - self.fillColor = colors.yellow - self.strokeColor = None - self.angle = 0 - - def demo(self): - D = Drawing(200, 100) - et = Star() - et.x=50 - et.y=0 - D.add(et) - labelFontSize = 10 - D.add(String(et.x+(et.size/2.0),(et.y-(1.2*labelFontSize)), - et.__class__.__name__, fillColor=colors.black, textAnchor='middle', - fontSize=labelFontSize)) - return D - - def draw(self): - s = float(self.size) #abbreviate as we will use this a lot - g = Group() - - # new algorithm from markers.StarFive - R = float(self.size)/2 - r = R*sin(18*(pi/180.0))/cos(36*(pi/180.0)) - P = [] - angle = 90 - for i in xrange(5): - for radius in R, r: - theta = angle*(pi/180.0) - P.append(radius*cos(theta)) - P.append(radius*sin(theta)) - angle = angle + 36 - # star specific bits - star = Polygon(P, - fillColor = self.fillColor, - strokeColor = self.strokeColor, - strokeWidth=s/50) - g.rotate(self.angle) - g.shift(self.x+self.dx,self.y+self.dy) - g.add(star) - - return g - -class Flag(_Symbol): - """This is a generic flag class that all the flags in this file use as a basis. - - This class basically provides edges and a tidy-up routine to hide any bits of - line that overlap the 'outside' of the flag - - possible attributes: - 'x', 'y', 'size', 'fillColor' - """ - - _attrMap = AttrMap(BASE=_Symbol, - fillColor = AttrMapValue(isColor, desc='Background color'), - border = AttrMapValue(isBoolean, 'Whether a background is drawn'), - kind = AttrMapValue(validFlag, desc='Which flag'), - ) - - _cache = {} - - def __init__(self,**kw): - _Symbol.__init__(self) - self.kind = None - self.size = 100 - self.fillColor = colors.white - self.border=1 - self.setProperties(kw) - - 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) - - def _Flag_None(self): - s = _size # abbreviate as we will use this a lot - g = Group() - g.add(Rect(0, 0, s*2, s, fillColor = colors.purple, strokeColor = colors.black, strokeWidth=0)) - return g - - def _borderDraw(self,f): - s = self.size # abbreviate as we will use this a lot - g = Group() - g.add(f) - x, y, sW = self.x+self.dx, self.y+self.dy, self.strokeWidth/2. - g.insert(0,Rect(-sW, -sW, width=getattr(self,'_width',2*s)+3*sW, height=getattr(self,'_height',s)+2*sW, - fillColor = None, strokeColor = self.strokeColor, strokeWidth=sW*2)) - g.shift(x,y) - g.scale(s/_size, s/_size) - return g - - def draw(self): - kind = self.kind or 'None' - f = self._cache.get(kind) - if not f: - f = getattr(self,'_Flag_'+kind)() - self._cache[kind] = f._explode() - return self._borderDraw(f) - - def clone(self): - return copy.copy(self) - - def demo(self): - D = Drawing(200, 100) - name = self.availableFlagNames() - import time - name = name[int(time.time()) % len(name)] - fx = Flag() - fx.kind = name - fx.x = 0 - fx.y = 0 - D.add(fx) - labelFontSize = 10 - D.add(String(fx.x+(fx.size/2),(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))), - "SAMPLE", fillColor=colors.gold, textAnchor='middle', - fontSize=labelFontSize, fontName="Helvetica-Bold")) - return D - - def _Flag_UK(self): - s = _size - g = Group() - w = s*2 - 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(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)) - g.add(Rect(0, (s*0.4), width=w, height=s*0.2, fillColor = colors.red, strokeColor = None, strokeWidth=0)) - return g - - def _Flag_USA(self): - s = _size # abbreviate as we will use this a lot - g = Group() - - box = Rect(0, 0, s*2, s, fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - for stripecounter in range (13,0, -1): - stripeheight = s/13.0 - if not (stripecounter%2 == 0): - stripecolor = colors.red - else: - stripecolor = colors.mintcream - redorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight, - fillColor = stripecolor, strokeColor = None, strokeWidth=20) - g.add(redorwhiteline) - - bluebox = Rect(0, (s-(stripeheight*7)), width=0.8*s, height=stripeheight*7, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(bluebox) - - lss = s*0.045 - lss2 = lss/2 - s9 = s/9 - s7 = s/7 - 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.fillColor = colors.mintcream - ls.y = s-(starycounter+1)*s9+lss2 - g.add(ls) - - for starxcounter in range(6): - for starycounter in range(5): - ls = Star() - ls.size = lss - ls.x = 0-(s/22)+lss/2+s/14+starxcounter*s7 - ls.fillColor = colors.mintcream - ls.y = s-(starycounter+1)*s9+(s/18)+lss2 - g.add(ls) - return g - - def _Flag_Afghanistan(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - greenbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0, - fillColor = colors.limegreen, strokeColor = None, strokeWidth=0) - g.add(greenbox) - - blackbox = Rect(0, 0, width=s*2.0, height=s/3.0, - fillColor = colors.black, strokeColor = None, strokeWidth=0) - g.add(blackbox) - return g - - def _Flag_Austria(self): - s = _size # abbreviate as we will use this a lot - g = Group() - - box = Rect(0, 0, s*2, s, fillColor = colors.mintcream, - strokeColor = colors.black, strokeWidth=0) - g.add(box) - - - redbox1 = Rect(0, 0, width=s*2.0, height=s/3.0, - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(redbox1) - - redbox2 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0, - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(redbox2) - return g - - def _Flag_Belgium(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.black, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - - box1 = Rect(0, 0, width=(s/3.0)*2.0, height=s, - fillColor = colors.black, strokeColor = None, strokeWidth=0) - g.add(box1) - - box2 = Rect(((s/3.0)*2.0), 0, width=(s/3.0)*2.0, height=s, - fillColor = colors.gold, strokeColor = None, strokeWidth=0) - g.add(box2) - - box3 = Rect(((s/3.0)*4.0), 0, width=(s/3.0)*2.0, height=s, - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(box3) - return g - - def _Flag_China(self): - s = _size - g = Group() - 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): - s = Star() - s.fillColor=colors.yellow - s.angle = angle - s.size = size*w*2 - s.x = x*w+x0 - s.y = y*w+y0 - g.add(s) - - addStar(5,5,3, 0) - addStar(10,1,1,36.86989765) - addStar(12,3,1,8.213210702) - addStar(12,6,1,16.60154960) - addStar(10,8,1,53.13010235) - return g - - def _Flag_Cuba(self): - s = _size - g = Group() - - for i in range(5): - stripe = Rect(0, i*s/5, width=s*2, height=s/5, - 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], - 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.fillColor = colors.white - g.add(star) - - box = Rect(0, 0, s*2, s, - fillColor = None, - strokeColor = colors.black, - strokeWidth=0) - g.add(box) - - return g - - def _Flag_Denmark(self): - s = _size - g = Group() - self._width = w = s*1.4 - - box = Rect(0, 0, w, s, - fillColor = colors.red, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - whitebox1 = Rect(((s/5)*2), 0, width=s/6, height=s, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whitebox1) - - whitebox2 = Rect(0, ((s/2)-(s/12)), width=w, height=s/6, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whitebox2) - return g - - def _Flag_Finland(self): - s = _size - g = Group() - - # crossbox specific bits - box = Rect(0, 0, s*2, s, - fillColor = colors.ghostwhite, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - blueline1 = Rect((s*0.6), 0, width=0.3*s, height=s, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(blueline1) - - blueline2 = Rect(0, (s*0.4), width=s*2, height=s*0.3, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(blueline2) - return g - - def _Flag_France(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - bluebox = Rect(0, 0, width=((s/3.0)*2.0), height=s, - fillColor = colors.blue, strokeColor = None, strokeWidth=0) - g.add(bluebox) - - whitebox = Rect(((s/3.0)*2.0), 0, width=((s/3.0)*2.0), height=s, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whitebox) - - redbox = Rect(((s/3.0)*4.0), 0, width=((s/3.0)*2.0), height=s, - fillColor = colors.red, - strokeColor = None, - strokeWidth=0) - g.add(redbox) - return g - - def _Flag_Germany(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.gold, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - blackbox1 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0, - fillColor = colors.black, strokeColor = None, strokeWidth=0) - g.add(blackbox1) - - redbox1 = Rect(0, (s/3.0), width=s*2.0, height=s/3.0, - fillColor = colors.orangered, strokeColor = None, strokeWidth=0) - g.add(redbox1) - return g - - def _Flag_Greece(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, fillColor = colors.gold, - strokeColor = colors.black, strokeWidth=0) - g.add(box) - - for stripecounter in range (9,0, -1): - stripeheight = s/9.0 - if not (stripecounter%2 == 0): - stripecolor = colors.deepskyblue - else: - stripecolor = colors.mintcream - - blueorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight, - fillColor = stripecolor, strokeColor = None, strokeWidth=20) - g.add(blueorwhiteline) - - bluebox1 = Rect(0, ((s)-stripeheight*5), width=(stripeheight*5), height=stripeheight*5, - fillColor = colors.deepskyblue, strokeColor = None, strokeWidth=0) - g.add(bluebox1) - - whiteline1 = Rect(0, ((s)-stripeheight*3), width=stripeheight*5, height=stripeheight, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whiteline1) - - whiteline2 = Rect((stripeheight*2), ((s)-stripeheight*5), width=stripeheight, height=stripeheight*5, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whiteline2) - - return g - - def _Flag_Ireland(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.forestgreen, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - whitebox = Rect(((s*2.0)/3.0), 0, width=(2.0*(s*2.0)/3.0), height=s, - fillColor = colors.mintcream, strokeColor = None, strokeWidth=0) - g.add(whitebox) - - orangebox = Rect(((2.0*(s*2.0)/3.0)), 0, width=(s*2.0)/3.0, height=s, - fillColor = colors.darkorange, strokeColor = None, strokeWidth=0) - g.add(orangebox) - return g - - def _Flag_Italy(self): - 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)) - return g - - def _Flag_Japan(self): - s = _size - 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)) - return g - - def _Flag_Luxembourg(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0, - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(redbox) - - bluebox = Rect(0, 0, width=s*2.0, height=s/3.0, - fillColor = colors.dodgerblue, strokeColor = None, strokeWidth=0) - g.add(bluebox) - return g - - def _Flag_Holland(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0, - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(redbox) - - bluebox = Rect(0, 0, width=s*2.0, height=s/3.0, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(bluebox) - return g - - def _Flag_Portugal(self): - return Group() - - def _Flag_Russia(self): - s = _size - g = Group() - w = self._width = s*1.5 - t = s/3 - 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)) - return g - - def _Flag_Spain(self): - s = _size - 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)) - return g - - def _Flag_Sweden(self): - s = _size - g = Group() - self._width = s*1.4 - box = Rect(0, 0, self._width, s, - fillColor = colors.dodgerblue, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - box1 = Rect(((s/5)*2), 0, width=s/6, 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, - fillColor = colors.gold, - strokeColor = None, - strokeWidth=0) - g.add(box2) - return g - - def _Flag_Norway(self): - s = _size - g = Group() - self._width = s*1.4 - - box = Rect(0, 0, self._width, s, - fillColor = colors.red, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - box = Rect(0, 0, self._width, s, - fillColor = colors.red, strokeColor = colors.black, strokeWidth=0) - g.add(box) - - whiteline1 = Rect(((s*0.2)*2), 0, width=s*0.2, height=s, - fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0) - g.add(whiteline1) - - whiteline2 = Rect(0, (s*0.4), width=self._width, height=s*0.2, - fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0) - g.add(whiteline2) - - blueline1 = Rect(((s*0.225)*2), 0, width=0.1*s, height=s, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(blueline1) - - blueline2 = Rect(0, (s*0.45), width=self._width, height=s*0.1, - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(blueline2) - return g - - def _Flag_CzechRepublic(self): - s = _size - g = Group() - box = Rect(0, 0, s*2, s, - fillColor = colors.mintcream, - strokeColor = colors.black, - strokeWidth=0) - g.add(box) - - redbox = Rect(0, 0, width=s*2, height=s/2, - fillColor = colors.red, - strokeColor = None, - strokeWidth=0) - g.add(redbox) - - bluewedge = Polygon(points = [ 0, 0, s, (s/2), 0, s], - fillColor = colors.darkblue, strokeColor = None, strokeWidth=0) - g.add(bluewedge) - return g - - def _Flag_Palestine(self): - s = _size - g = Group() - box = Rect(0, s/3, s*2, s/3, - fillColor = colors.mintcream, - strokeColor = None, - strokeWidth=0) - g.add(box) - - greenbox = Rect(0, 0, width=s*2, height=s/3, - fillColor = colors.limegreen, - strokeColor = None, - strokeWidth=0) - g.add(greenbox) - - blackbox = Rect(0, 2*s/3, width=s*2, height=s/3, - fillColor = colors.black, - strokeColor = None, - strokeWidth=0) - g.add(blackbox) - - redwedge = Polygon(points = [ 0, 0, 2*s/3, (s/2), 0, s], - fillColor = colors.red, strokeColor = None, strokeWidth=0) - g.add(redwedge) - return g - - def _Flag_Turkey(self): - s = _size - g = Group() - - box = Rect(0, 0, s*2, s, - fillColor = colors.red, - strokeColor = colors.black, - strokeWidth=0) - g.add(box) - - whitecircle = Circle(cx=((s*0.35)*2), cy=s/2, 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, - fillColor = colors.red, - strokeColor = None, - strokeWidth=0) - g.add(redcircle) - - ws = Star() - ws.angle = 15 - ws.size = s/5 - ws.x = (s*0.5)*2+ws.size/2 - ws.y = (s*0.5) - ws.fillColor = colors.mintcream - ws.strokeColor = None - g.add(ws) - return g - - def _Flag_Switzerland(self): - s = _size - g = Group() - 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)) - return g - - def _Flag_EU(self): - s = _size - g = Group() - 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 - yradius = radius - xradius = radius - nStars = 12 - delta = 2*pi/nStars - for i in range(nStars): - rad = i*delta - gs = Star() - gs.x=cos(rad)*radius+centerx - gs.y=sin(rad)*radius+centery - gs.size=s/10 - gs.fillColor=colors.gold - g.add(gs) - return g - - def _Flag_Brazil(self): - s = _size # abbreviate as we will use this a lot - g = Group() - - m = s/14 - 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)) - 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], - fillColor = colors.yellow, strokeColor = None, strokeWidth=0)) - g.add(Circle(cx=w/2, cy=s/2, 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, - fillColor=colors.mintcream,strokeColor=None, strokeWidth=0)) - g.add(Wedge((w/2), (s/2), 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, - fillColor=colors.blue,strokeColor=None, strokeWidth=0)) - g.add(Rect(0, 0, w, (s/4) + 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], - fillColor = colors.yellow, strokeColor = None, strokeWidth=0)) - g.add(Wedge(w/2, s/2, 3.502*m, 166, 342.1, 3.502*m, - fillColor=colors.blue,strokeColor=None, strokeWidth=0)) - - addStar(3.2,3.5,0.3) - addStar(-8.5,1.5,0.3) - addStar(-7.5,-3,0.3) - addStar(-4,-5.5,0.3) - addStar(0,-4.5,0.3) - addStar(7,-3.5,0.3) - addStar(-3.5,-0.5,0.25) - addStar(0,-1.5,0.25) - addStar(1,-2.5,0.25) - addStar(3,-7,0.25) - addStar(5,-6.5,0.25) - addStar(6.5,-5,0.25) - addStar(7,-4.5,0.25) - addStar(-5.5,-3.2,0.25) - addStar(-6,-4.2,0.25) - addStar(-1,-2.75,0.2) - addStar(2,-5.5,0.2) - addStar(4,-5.5,0.2) - addStar(5,-7.5,0.2) - addStar(5,-5.5,0.2) - addStar(6,-5.5,0.2) - addStar(-8.8,-3.2,0.2) - addStar(2.5,0.5,0.2) - addStar(-0.2,-3.2,0.14) - addStar(-7.2,-2,0.14) - addStar(0,-8,0.1) - - sTmp = "ORDEM E PROGRESSO" - nTmp = len(sTmp) - delta = 0.850848010347/nTmp - radius = 7.9 *m - centerx = (w/2)-(2*m) - centery = 0 - for i in range(nTmp): - rad = 2*pi - i*delta -4.60766922527 - x=cos(rad)*radius+centerx - y=sin(rad)*radius+centery - if i == 6: - z = 0.35*m - else: - z= 0.45*m - g2 = Group(String(x, y, sTmp[i], fontName='Helvetica-Bold', - fontSize = z,strokeColor=None,fillColor=colors.green)) - g2.rotate(rad) - g.add(g2) - return g - -def makeFlag(name): - flag = Flag() - flag.kind = name - return flag - -def test(): - """This function produces three pdf files with examples of all the signs and symbols from this file. - """ -# page 1 - - labelFontSize = 10 - - X = (20,245) - - flags = [ - 'UK', - 'USA', - 'Afghanistan', - 'Austria', - 'Belgium', - 'Denmark', - 'Cuba', - 'Finland', - 'France', - 'Germany', - 'Greece', - 'Ireland', - 'Italy', - 'Luxembourg', - 'Holland', - 'Palestine', - 'Portugal', - 'Spain', - 'Sweden', - 'Norway', - 'CzechRepublic', - 'Turkey', - 'Switzerland', - 'EU', - 'Brazil', - ] - y = Y0 = 530 - f = 0 - D = None - for name in flags: - if not D: D = Drawing(450,650) - flag = makeFlag(name) - i = flags.index(name) - flag.x = X[i%2] - flag.y = y - D.add(flag) - D.add(String(flag.x+(flag.size/2),(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]: - renderPDF.drawToFile(D, 'flags%02d.pdf'%f, 'flags.py - Page #%d'%(f+1)) - y = Y0 - f = f+1 - D = None - -if __name__=='__main__': - test() diff --git a/bin/reportlab/graphics/widgets/grids.py b/bin/reportlab/graphics/widgets/grids.py deleted file mode 100644 index d3072728a89..00000000000 --- a/bin/reportlab/graphics/widgets/grids.py +++ /dev/null @@ -1,504 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/grids.py -__version__=''' $Id: grids.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from reportlab.lib import colors -from reportlab.lib.validators import isNumber, isColorOrNone, isBoolean, isListOfNumbers, OneOf, isListOfColors -from reportlab.lib.attrmap import AttrMap, AttrMapValue -from reportlab.graphics.shapes import Drawing, Group, Line, Rect, LineShape, definePath, EmptyClipPath -from reportlab.graphics.widgetbase import Widget - -def frange(start, end=None, inc=None): - "A range function, that does accept float increments..." - - if end == None: - end = start + 0.0 - start = 0.0 - - if inc == None: - inc = 1.0 - - L = [] - end = end - inc*0.0001 #to avoid numrical problems - while 1: - next = start + len(L) * inc - if inc > 0 and next >= end: - break - elif inc < 0 and next <= end: - break - L.append(next) - - return L - - -def makeDistancesList(list): - """Returns a list of distances between adjacent numbers in some input list. - - E.g. [1, 1, 2, 3, 5, 7] -> [0, 1, 1, 2, 2] - """ - - d = [] - for i in range(len(list[:-1])): - d.append(list[i+1] - list[i]) - - return d - - -class Grid(Widget): - """This makes a rectangular grid of equidistant stripes. - - The grid contains an outer border rectangle, and stripes - inside which can be drawn with lines and/or as solid tiles. - The drawing order is: outer rectangle, then lines and tiles. - - The stripes' width is indicated as 'delta'. The sequence of - stripes can have an offset named 'delta0'. Both values need - to be positive! - """ - - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc="The grid's lower-left x position."), - y = AttrMapValue(isNumber, desc="The grid's lower-left y position."), - width = AttrMapValue(isNumber, desc="The grid's width."), - height = AttrMapValue(isNumber, desc="The grid's height."), - orientation = AttrMapValue(OneOf(('vertical', 'horizontal')), - desc='Determines if stripes are vertical or horizontal.'), - useLines = AttrMapValue(OneOf((0, 1)), - desc='Determines if stripes are drawn with lines.'), - useRects = AttrMapValue(OneOf((0, 1)), - desc='Determines if stripes are drawn with solid rectangles.'), - delta = AttrMapValue(isNumber, - desc='Determines the width/height of the stripes.'), - delta0 = AttrMapValue(isNumber, - desc='Determines the stripes initial width/height offset.'), - deltaSteps = AttrMapValue(isListOfNumbers, - desc='List of deltas to be used cyclically.'), - stripeColors = AttrMapValue(isListOfColors, - desc='Colors applied cyclically in the right or upper direction.'), - fillColor = AttrMapValue(isColorOrNone, - desc='Background color for entire rectangle.'), - strokeColor = AttrMapValue(isColorOrNone, - desc='Color used for lines.'), - strokeWidth = AttrMapValue(isNumber, - desc='Width used for lines.'), - rectStrokeColor = AttrMapValue(isColorOrNone, desc='Color for outer rect stroke.'), - rectStrokeWidth = AttrMapValue(isColorOrNone, desc='Width for outer rect stroke.'), - ) - - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 100 - self.height = 100 - self.orientation = 'vertical' - self.useLines = 0 - self.useRects = 1 - self.delta = 20 - self.delta0 = 0 - self.deltaSteps = [] - self.fillColor = colors.white - self.stripeColors = [colors.red, colors.green, colors.blue] - self.strokeColor = colors.black - self.strokeWidth = 2 - - - def demo(self): - D = Drawing(100, 100) - - g = Grid() - D.add(g) - - return D - - def makeOuterRect(self): - strokeColor = getattr(self,'rectStrokeColor',self.strokeColor) - strokeWidth = getattr(self,'rectStrokeWidth',self.strokeWidth) - if self.fillColor or (strokeColor and strokeWidth): - rect = Rect(self.x, self.y, self.width, self.height) - rect.fillColor = self.fillColor - rect.strokeColor = strokeColor - rect.strokeWidth = strokeWidth - return rect - else: - return None - - def makeLinePosList(self, start, isX=0): - "Returns a list of positions where to place lines." - - w, h = self.width, self.height - if isX: - length = w - else: - length = h - if self.deltaSteps: - r = [start + self.delta0] - i = 0 - while 1: - if r[-1] > start + length: - del r[-1] - break - r.append(r[-1] + self.deltaSteps[i % len(self.deltaSteps)]) - i = i + 1 - else: - r = frange(start + self.delta0, start + length, self.delta) - - r.append(start + length) - if self.delta0 != 0: - r.insert(0, start) - #print 'Grid.makeLinePosList() -> %s' % r - return r - - - def makeInnerLines(self): - # inner grid lines - group = Group() - - w, h = self.width, self.height - - if self.useLines == 1: - if self.orientation == 'vertical': - r = self.makeLinePosList(self.x, isX=1) - for x in r: - line = Line(x, self.y, x, self.y + h) - line.strokeColor = self.strokeColor - line.strokeWidth = self.strokeWidth - group.add(line) - elif self.orientation == 'horizontal': - r = self.makeLinePosList(self.y, isX=0) - for y in r: - line = Line(self.x, y, self.x + w, y) - line.strokeColor = self.strokeColor - line.strokeWidth = self.strokeWidth - group.add(line) - - return group - - - def makeInnerTiles(self): - # inner grid lines - group = Group() - - w, h = self.width, self.height - - # inner grid stripes (solid rectangles) - if self.useRects == 1: - cols = self.stripeColors - - if self.orientation == 'vertical': - r = self.makeLinePosList(self.x, isX=1) - elif self.orientation == 'horizontal': - r = self.makeLinePosList(self.y, isX=0) - - dist = makeDistancesList(r) - - i = 0 - for j in range(len(dist)): - if self.orientation == 'vertical': - x = r[j] - stripe = Rect(x, self.y, dist[j], h) - elif self.orientation == 'horizontal': - y = r[j] - stripe = Rect(self.x, y, w, dist[j]) - stripe.fillColor = cols[i % len(cols)] - stripe.strokeColor = None - group.add(stripe) - i = i + 1 - - return group - - - def draw(self): - # general widget bits - group = Group() - - group.add(self.makeOuterRect()) - group.add(self.makeInnerTiles()) - group.add(self.makeInnerLines(),name='_gridLines') - - return group - - -class DoubleGrid(Widget): - """This combines two ordinary Grid objects orthogonal to each other. - """ - - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc="The grid's lower-left x position."), - y = AttrMapValue(isNumber, desc="The grid's lower-left y position."), - width = AttrMapValue(isNumber, desc="The grid's width."), - height = AttrMapValue(isNumber, desc="The grid's height."), - grid0 = AttrMapValue(None, desc="The first grid component."), - grid1 = AttrMapValue(None, desc="The second grid component."), - ) - - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 100 - self.height = 100 - - g0 = Grid() - g0.x = self.x - g0.y = self.y - g0.width = self.width - g0.height = self.height - g0.orientation = 'vertical' - g0.useLines = 1 - g0.useRects = 0 - g0.delta = 20 - g0.delta0 = 0 - g0.deltaSteps = [] - g0.fillColor = colors.white - g0.stripeColors = [colors.red, colors.green, colors.blue] - g0.strokeColor = colors.black - g0.strokeWidth = 1 - - g1 = Grid() - g1.x = self.x - g1.y = self.y - g1.width = self.width - g1.height = self.height - g1.orientation = 'horizontal' - g1.useLines = 1 - g1.useRects = 0 - g1.delta = 20 - g1.delta0 = 0 - g1.deltaSteps = [] - g1.fillColor = colors.white - g1.stripeColors = [colors.red, colors.green, colors.blue] - g1.strokeColor = colors.black - g1.strokeWidth = 1 - - self.grid0 = g0 - self.grid1 = g1 - - -## # This gives an AttributeError: -## # DoubleGrid instance has no attribute 'grid0' -## def __setattr__(self, name, value): -## if name in ('x', 'y', 'width', 'height'): -## setattr(self.grid0, name, value) -## setattr(self.grid1, name, value) - - - def demo(self): - D = Drawing(100, 100) - g = DoubleGrid() - D.add(g) - return D - - - def draw(self): - group = Group() - g0, g1 = self.grid0, self.grid1 - # Order groups to make sure both v and h lines - # are visible (works only when there is only - # one kind of stripes, v or h). - G = g0.useRects == 1 and g1.useRects == 0 and (g0,g1) or (g1,g0) - for g in G: - group.add(g.makeOuterRect()) - for g in G: - group.add(g.makeInnerTiles()) - group.add(g.makeInnerLines(),name='_gridLines') - - return group - - -class ShadedRect(Widget): - """This makes a rectangle with shaded colors between two colors. - - Colors are interpolated linearly between 'fillColorStart' - and 'fillColorEnd', both of which appear at the margins. - If 'numShades' is set to one, though, only 'fillColorStart' - is used. - """ - - _attrMap = AttrMap( - x = AttrMapValue(isNumber, desc="The grid's lower-left x position."), - y = AttrMapValue(isNumber, desc="The grid's lower-left y position."), - width = AttrMapValue(isNumber, desc="The grid's width."), - height = AttrMapValue(isNumber, desc="The grid's height."), - orientation = AttrMapValue(OneOf(('vertical', 'horizontal')), desc='Determines if stripes are vertical or horizontal.'), - numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'), - fillColorStart = AttrMapValue(isColorOrNone, desc='Start value of the color shade.'), - fillColorEnd = AttrMapValue(isColorOrNone, desc='End value of the color shade.'), - strokeColor = AttrMapValue(isColorOrNone, desc='Color used for border line.'), - strokeWidth = AttrMapValue(isNumber, desc='Width used for lines.'), - cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'), - ) - - def __init__(self,**kw): - self.x = 0 - self.y = 0 - self.width = 100 - self.height = 100 - self.orientation = 'vertical' - self.numShades = 20 - self.fillColorStart = colors.pink - self.fillColorEnd = colors.black - self.strokeColor = colors.black - self.strokeWidth = 2 - self.cylinderMode = 0 - self.setProperties(kw) - - def demo(self): - D = Drawing(100, 100) - g = ShadedRect() - D.add(g) - - return D - - def _flipRectCorners(self): - "Flip rectangle's corners if width or height is negative." - x, y, width, height, fillColorStart, fillColorEnd = self.x, self.y, self.width, self.height, self.fillColorStart, self.fillColorEnd - if width < 0 and height > 0: - x = x + width - width = -width - if self.orientation=='vertical': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart - elif height<0 and width>0: - y = y + height - height = -height - if self.orientation=='horizontal': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart - elif height < 0 and height < 0: - x = x + width - width = -width - y = y + height - height = -height - return x, y, width, height, fillColorStart, fillColorEnd - - def draw(self): - # general widget bits - group = Group() - x, y, w, h, c0, c1 = self._flipRectCorners() - numShades = self.numShades - if self.cylinderMode: - if not numShades%2: numShades = numShades+1 - halfNumShades = (numShades-1)/2 + 1 - num = float(numShades) # must make it float! - vertical = self.orientation == 'vertical' - if vertical: - if numShades == 1: - V = [x] - else: - V = frange(x, x + w, w/num) - else: - if numShades == 1: - V = [y] - else: - V = frange(y, y + h, h/num) - - for v in V: - stripe = vertical and Rect(v, y, w/num, h) or Rect(x, v, w, h/num) - if self.cylinderMode: - if V.index(v)>=halfNumShades: - col = colors.linearlyInterpolatedColor(c1,c0,V[halfNumShades],V[-1], v) - else: - col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[halfNumShades], v) - else: - col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[-1], v) - stripe.fillColor = col - stripe.strokeColor = col - stripe.strokeWidth = 1 - group.add(stripe) - if self.strokeColor and self.strokeWidth>=0: - rect = Rect(x, y, w, h) - rect.strokeColor = self.strokeColor - rect.strokeWidth = self.strokeWidth - rect.fillColor = None - group.add(rect) - return group - - -def colorRange(c0, c1, n): - "Return a range of intermediate colors between c0 and c1" - if n==1: return [c0] - - C = [] - if n>1: - lim = n-1 - for i in range(n): - C.append(colors.linearlyInterpolatedColor(c0,c1,0,lim, i)) - return C - - -def centroid(P): - '''compute average point of a set of points''' - return reduce(lambda x,y, fn=float(len(P)): (x[0]+y[0]/fn,x[1]+y[1]/fn),P,(0,0)) - -def rotatedEnclosingRect(P, angle, rect): - ''' - given P a sequence P of x,y coordinate pairs and an angle in degrees - find the centroid of P and the axis at angle theta through it - find the extreme points of P wrt axis parallel distance and axis - orthogonal distance. Then compute the least rectangle that will still - enclose P when rotated by angle. - - The class R - ''' - from math import pi, cos, sin, tan - x0, y0 = centroid(P) - theta = (angle/180.)*pi - s,c=sin(theta),cos(theta) - def parallelAxisDist((x,y),s=s,c=c,x0=x0,y0=y0): - return (s*(y-y0)+c*(x-x0)) - def orthogonalAxisDist((x,y),s=s,c=c,x0=x0,y0=y0): - return (c*(y-y0)+s*(x-x0)) - L = map(parallelAxisDist,P) - L.sort() - a0, a1 = L[0], L[-1] - L = map(orthogonalAxisDist,P) - L.sort() - b0, b1 = L[0], L[-1] - rect.x, rect.width = a0, a1-a0 - rect.y, rect.height = b0, b1-b0 - g = Group(transform=(c,s,-s,c,x0,y0)) - g.add(rect) - return g - -class ShadedPolygon(Widget,LineShape): - _attrMap = AttrMap(BASE=LineShape, - angle = AttrMapValue(isNumber,desc="Shading angle"), - fillColorStart = AttrMapValue(isColorOrNone), - fillColorEnd = AttrMapValue(isColorOrNone), - numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'), - cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'), - points = AttrMapValue(isListOfNumbers), - ) - - def __init__(self,**kw): - self.angle = 90 - self.fillColorStart = colors.red - self.fillColorEnd = colors.green - self.cylinderMode = 0 - self.numShades = 50 - self.points = [-1,-1,2,2,3,-1] - LineShape.__init__(self,kw) - - 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'], - fillColor=None, strokeColor=None) - path.isClipPath = 1 - g = Group() - g.add(path) - rect = ShadedRect(strokeWidth=0,strokeColor=None) - for k in 'fillColorStart', 'fillColorEnd', 'numShades', 'cylinderMode': - setattr(rect,k,getattr(self,k)) - g.add(rotatedEnclosingRect(P, self.angle, rect)) - g.add(EmptyClipPath) - path = path.copy() - path.isClipPath = 0 - path.strokeColor = self.strokeColor - path.strokeWidth = self.strokeWidth - g.add(path) - return g - -if __name__=='__main__': #noruntests - from reportlab.lib.colors import blue - from reportlab.graphics.shapes import Drawing - angle=45 - D = Drawing(120,120) - D.add(ShadedPolygon(points=(10,10,60,60,110,10),strokeColor=None,strokeWidth=1,angle=90,numShades=50,cylinderMode=0)) - D.save(formats=['gif'],fnRoot='shobj',outDir='/tmp') diff --git a/bin/reportlab/graphics/widgets/markers.py b/bin/reportlab/graphics/widgets/markers.py deleted file mode 100644 index ed700773ab2..00000000000 --- a/bin/reportlab/graphics/widgets/markers.py +++ /dev/null @@ -1,228 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/markers.py -""" -This modules defines a collection of markers used in charts. -""" -__version__=''' $Id: markers.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -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.graphics.widgets.flags import Flag -from math import sin, cos, pi -import copy, new -_toradians = pi/180.0 - -class Marker(Widget): - '''A polymorphic class of markers''' - _attrMap = AttrMap(BASE=Widget, - kind = AttrMapValue( - OneOf(None, 'Square', 'Diamond', 'Circle', 'Cross', 'Triangle', 'StarSix', - 'Pentagon', 'Hexagon', 'Heptagon', 'Octagon', 'StarFive', - 'FilledSquare', 'FilledCircle', 'FilledDiamond', 'FilledCross', - 'FilledTriangle','FilledStarSix', 'FilledPentagon', 'FilledHexagon', - 'FilledHeptagon', 'FilledOctagon', 'FilledStarFive', - 'Smiley'), - desc='marker type name'), - size = AttrMapValue(isNumber,desc='marker size'), - x = AttrMapValue(isNumber,desc='marker x coordinate'), - y = AttrMapValue(isNumber,desc='marker y coordinate'), - dx = AttrMapValue(isNumber,desc='marker x coordinate adjustment'), - dy = AttrMapValue(isNumber,desc='marker y coordinate adjustment'), - angle = AttrMapValue(isNumber,desc='marker rotation'), - fillColor = AttrMapValue(isColorOrNone, desc='marker fill colour'), - strokeColor = AttrMapValue(isColorOrNone, desc='marker stroke colour'), - strokeWidth = AttrMapValue(isNumber, desc='marker stroke width'), - ) - - def __init__(self,*args,**kw): - self.kind = None - self.strokeColor = black - self.strokeWidth = 0.1 - self.fillColor = None - self.size = 5 - self.x = self.y = self.dx = self.dy = self.angle = 0 - self.setProperties(kw) - - def clone(self): - return new.instance(self.__class__,self.__dict__.copy()) - - def _Smiley(self): - x, y = self.x+self.dx, self.y+self.dy - d = self.size/2.0 - s = SmileyFace() - s.fillColor = self.fillColor - s.strokeWidth = self.strokeWidth - s.strokeColor = self.strokeColor - s.x = x-d - s.y = y-d - s.size = d*2 - return s - - def _Square(self): - x, y = self.x+self.dx, self.y+self.dy - d = self.size/2.0 - s = Rect(x-d,y-d,2*d,2*d,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth) - return s - - def _Diamond(self): - d = self.size/2.0 - return self._doPolygon((-d,0,0,d,d,0,0,-d)) - - def _Circle(self): - x, y = self.x+self.dx, self.y+self.dy - s = Circle(x,y,self.size/2.0,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth) - return s - - def _Cross(self): - x, y = self.x+self.dx, self.y+self.dy - s = float(self.size) - h, s = s/2, s/6 - return self._doPolygon((-s,-h,-s,-s,-h,-s,-h,s,-s,s,-s,h,s,h,s,s,h,s,h,-s,s,-s,s,-h)) - - def _Triangle(self): - x, y = self.x+self.dx, self.y+self.dy - r = float(self.size)/2 - c = 30*_toradians - s = sin(30*_toradians)*r - c = cos(c)*r - return self._doPolygon((0,r,-c,-s,c,-s)) - - def _StarSix(self): - r = float(self.size)/2 - c = 30*_toradians - s = sin(c)*r - c = cos(c)*r - z = s/2 - g = c/2 - return self._doPolygon((0,r,-z,s,-c,s,-s,0,-c,-s,-z,-s,0,-r,z,-s,c,-s,s,0,c,s,z,s)) - - def _StarFive(self): - R = float(self.size)/2 - r = R*sin(18*_toradians)/cos(36*_toradians) - P = [] - angle = 90 - for i in xrange(5): - for radius in R, r: - theta = angle*_toradians - P.append(radius*cos(theta)) - P.append(radius*sin(theta)) - angle = angle + 36 - return self._doPolygon(P) - - def _Pentagon(self): - return self._doNgon(5) - - def _Hexagon(self): - return self._doNgon(6) - - def _Heptagon(self): - return self._doNgon(7) - - def _Octagon(self): - return self._doNgon(8) - - 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))) - return Polygon(P, strokeWidth =self.strokeWidth, strokeColor=self.strokeColor, fillColor=self.fillColor) - - def _doFill(self): - old = self.fillColor - if old is None: - self.fillColor = self.strokeColor - r = (self.kind and getattr(self,'_'+self.kind[6:]) or Group)() - self.fillColor = old - return r - - def _doNgon(self,n): - P = [] - size = float(self.size)/2 - for i in xrange(n): - r = (2.*i/n+0.5)*pi - P.append(size*cos(r)) - P.append(size*sin(r)) - return self._doPolygon(P) - - _FilledCircle = _doFill - _FilledSquare = _doFill - _FilledDiamond = _doFill - _FilledCross = _doFill - _FilledTriangle = _doFill - _FilledStarSix = _doFill - _FilledPentagon = _doFill - _FilledHexagon = _doFill - _FilledHeptagon = _doFill - _FilledOctagon = _doFill - _FilledStarFive = _doFill - - def draw(self): - if self.kind: - m = getattr(self,'_'+self.kind) - if self.angle: - _x, _dx, _y, _dy = self.x, self.dx, self.y, self.dy - self.x, self.dx, self.y, self.dy = 0,0,0,0 - try: - m = m() - finally: - self.x, self.dx, self.y, self.dy = _x, _dx, _y, _dy - if not isinstance(m,Group): - _m, m = m, Group() - m.add(_m) - if self.angle: m.rotate(self.angle) - x, y = _x+_dx, _y+_dy - if x or y: m.shift(x,y) - else: - m = m() - else: - m = Group() - return m - -def uSymbol2Symbol(uSymbol,x,y,color): - if type(uSymbol) == FunctionType: - symbol = uSymbol(x, y, 5, color) - elif type(uSymbol) == ClassType and issubclass(uSymbol,Widget): - size = 10. - symbol = uSymbol() - symbol.x = x - (size/2) - symbol.y = y - (size/2) - try: - symbol.size = size - symbol.color = color - except: - pass - elif isinstance(uSymbol,Marker) or isinstance(uSymbol,Flag): - symbol = uSymbol.clone() - if isinstance(uSymbol,Marker): symbol.fillColor = symbol.fillColor or color - symbol.x, symbol.y = x, y - else: - symbol = None - return symbol - -class _isSymbol(Validator): - def test(self,x): - return callable(x) or isinstance(x,Marker) or isinstance(x,Flag) \ - or (type(x)==ClassType and issubclass(x,Widget)) - -isSymbol = _isSymbol() - -def makeMarker(name,**kw): - if Marker._attrMap['kind'].validate(name): - m = apply(Marker,(),kw) - m.kind = name - elif name[-5:]=='_Flag' and Flag._attrMap['kind'].validate(name[:-5]): - m = apply(Flag,(),kw) - m.kind = name[:-5] - m.size = 10 - else: - raise ValueError, "Invalid marker name %s" % name - return m - -if __name__=='__main__': - D = Drawing() - D.add(Marker()) - D.save(fnRoot='Marker',formats=['pdf'], outDir='/tmp') diff --git a/bin/reportlab/graphics/widgets/signsandsymbols.py b/bin/reportlab/graphics/widgets/signsandsymbols.py deleted file mode 100644 index f7f193cf536..00000000000 --- a/bin/reportlab/graphics/widgets/signsandsymbols.py +++ /dev/null @@ -1,919 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/signsandsymbols.py -# signsandsymbols.py -# A collection of new widgets -# author: John Precedo (johnp@reportlab.com) -"""This file is a collection of widgets to produce some common signs and symbols. - -Widgets include: -- ETriangle (an equilateral triangle), -- RTriangle (a right angled triangle), -- Octagon, -- Crossbox, -- Tickbox, -- SmileyFace, -- StopSign, -- NoEntry, -- NotAllowed (the red roundel from 'no smoking' signs), -- NoSmoking, -- DangerSign (a black exclamation point in a yellow triangle), -- YesNo (returns a tickbox or a crossbox depending on a testvalue), -- FloppyDisk, -- ArrowOne, and -- ArrowTwo -""" -__version__=''' $Id: signsandsymbols.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from reportlab.lib import colors -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -from reportlab.graphics import shapes -from reportlab.graphics.widgetbase import Widget -from reportlab.graphics import renderPDF - - -class _Symbol(Widget): - """Abstract base widget - possible attributes: - 'x', 'y', 'size', 'fillColor', 'strokeColor' - """ - _nodoc = 1 - _attrMap = AttrMap( - x = AttrMapValue(isNumber,desc='symbol x coordinate'), - y = AttrMapValue(isNumber,desc='symbol y coordinate'), - dx = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'), - dy = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'), - size = AttrMapValue(isNumber), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - strokeWidth = AttrMapValue(isNumber), - ) - def __init__(self): - assert self.__class__.__name__!='_Symbol', 'Abstract class _Symbol instantiated' - self.x = self.y = self.dx = self.dy = 0 - self.size = 100 - self.fillColor = colors.red - self.strokeColor = None - self.strokeWidth = 0.1 - - def demo(self): - D = shapes.Drawing(200, 100) - s = float(self.size) - ob = self.__class__() - ob.x=50 - ob.y=0 - ob.draw() - D.add(ob) - D.add(shapes.String(ob.x+(s/2),(ob.y-12), - ob.__class__.__name__, fillColor=colors.black, textAnchor='middle', - fontSize=10)) - return D - -class ETriangle(_Symbol): - """This draws an equilateral triangle.""" - - def __init__(self): - pass #AbstractSymbol - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # Triangle specific bits - ae = s*0.125 #(ae = 'an eighth') - triangle = shapes.Polygon(points = [ - self.x, self.y, - self.x+s, self.y, - self.x+(s/2),self.y+s], - fillColor = self.fillColor, - strokeColor = self.strokeColor, - strokeWidth=s/50.) - g.add(triangle) - return g - -class RTriangle(_Symbol): - """This draws a right-angled triangle. - - possible attributes: - 'x', 'y', 'size', 'fillColor', 'strokeColor' - - """ - - def __init__(self): - self.x = 0 - self.y = 0 - self.size = 100 - self.fillColor = colors.green - self.strokeColor = None - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # Triangle specific bits - ae = s*0.125 #(ae = 'an eighth') - triangle = shapes.Polygon(points = [ - self.x, self.y, - self.x+s, self.y, - self.x,self.y+s], - fillColor = self.fillColor, - strokeColor = self.strokeColor, - strokeWidth=s/50.) - g.add(triangle) - return g - -class Octagon(_Symbol): - """This widget draws an Octagon. - - possible attributes: - 'x', 'y', 'size', 'fillColor', 'strokeColor' - - """ - - def __init__(self): - self.x = 0 - self.y = 0 - self.size = 100 - self.fillColor = colors.yellow - self.strokeColor = None - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # Octagon specific bits - athird=s/3 - - octagon = shapes.Polygon(points=[self.x+athird, self.y, - self.x, self.y+athird, - self.x, self.y+(athird*2), - self.x+athird, self.y+s, - self.x+(athird*2), self.y+s, - self.x+s, self.y+(athird*2), - self.x+s, self.y+athird, - self.x+(athird*2), self.y], - strokeColor = self.strokeColor, - fillColor = self.fillColor, - strokeWidth=10) - g.add(octagon) - return g - -class Crossbox(_Symbol): - """This draws a black box with a red cross in it - a 'checkbox'. - - possible attributes: - 'x', 'y', 'size', 'crossColor', 'strokeColor', 'crosswidth' - - """ - - _attrMap = AttrMap(BASE=_Symbol, - crossColor = AttrMapValue(isColorOrNone), - crosswidth = AttrMapValue(isNumber), - ) - - def __init__(self): - self.x = 0 - self.y = 0 - self.size = 100 - self.fillColor = colors.white - self.crossColor = colors.red - self.strokeColor = colors.black - self.crosswidth = 10 - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # crossbox specific bits - box = shapes.Rect(self.x+1, self.y+1, s-2, s-2, - fillColor = self.fillColor, - strokeColor = self.strokeColor, - strokeWidth=2) - g.add(box) - - crossLine1 = shapes.Line(self.x+(s*0.15), self.y+(s*0.15), self.x+(s*0.85), self.y+(s*0.85), - fillColor = self.crossColor, - strokeColor = self.crossColor, - strokeWidth = self.crosswidth) - g.add(crossLine1) - - crossLine2 = shapes.Line(self.x+(s*0.15), self.y+(s*0.85), self.x+(s*0.85) ,self.y+(s*0.15), - fillColor = self.crossColor, - strokeColor = self.crossColor, - strokeWidth = self.crosswidth) - g.add(crossLine2) - - return g - - -class Tickbox(_Symbol): - """This draws a black box with a red tick in it - another 'checkbox'. - - possible attributes: - 'x', 'y', 'size', 'tickColor', 'strokeColor', 'tickwidth' - -""" - - _attrMap = AttrMap(BASE=_Symbol, - tickColor = AttrMapValue(isColorOrNone), - tickwidth = AttrMapValue(isNumber), - ) - - def __init__(self): - self.x = 0 - self.y = 0 - self.size = 100 - self.tickColor = colors.red - self.strokeColor = colors.black - self.fillColor = colors.white - self.tickwidth = 10 - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # tickbox specific bits - box = shapes.Rect(self.x+1, self.y+1, s-2, s-2, - fillColor = self.fillColor, - strokeColor = self.strokeColor, - strokeWidth=2) - g.add(box) - - tickLine = shapes.PolyLine(points = [self.x+(s*0.15), self.y+(s*0.35), self.x+(s*0.35), self.y+(s*0.15), - self.x+(s*0.35), self.y+(s*0.15), self.x+(s*0.85) ,self.y+(s*0.85)], - fillColor = self.tickColor, - strokeColor = self.tickColor, - strokeWidth = self.tickwidth) - g.add(tickLine) - - return g - -class SmileyFace(_Symbol): - """This draws a classic smiley face. - - possible attributes: - 'x', 'y', 'size', 'fillColor' - - """ - - def __init__(self): - _Symbol.__init__(self) - self.x = 0 - self.y = 0 - self.size = 100 - self.fillColor = colors.yellow - self.strokeColor = colors.black - - def draw(self): - # general widget bits - s = float(self.size) # abbreviate as we will use this a lot - g = shapes.Group() - - # SmileyFace specific bits - g.add(shapes.Circle(cx=self.x+(s/2), cy=self.y+(s/2), r=s/2, - fillColor=self.fillColor, strokeColor=self.strokeColor, - strokeWidth=max(s/38.,self.strokeWidth))) - - for i in (1,2): - g.add(shapes.Ellipse(self.x+(s/3)*i,self.y+(s/3)*2, s/30, s/10, - fillColor=self.strokeColor, strokeColor = self.strokeColor, - strokeWidth=max(s/38.,self.strokeWidth))) - - # calculate a pointslist for the mouth - # THIS IS A HACK! - don't use if there is a 'shapes.Arc' - centerx=self.x+(s/2) - centery=self.y+(s/2) - radius=s/3 - yradius = radius - xradius = radius - startangledegrees=200 - endangledegrees=340 - degreedelta = 1 - pointslist = [] - a = pointslist.append - from math import sin, cos, pi - degreestoradians = pi/180.0 - radiansdelta = degreedelta*degreestoradians - startangle = startangledegrees*degreestoradians - endangle = endangledegrees*degreestoradians - while endangle= searchto: - break # EXIT LOOP - match = m.group(0) - end = start + len(match) - c = match[0] - if c not in "#'\"": - # Must have matched a keyword. - if start <> searchfrom: - # there's still a redundant char before and after it, strip! - match = match[1:-1] - start = start + 1 - else: - # this is the first keyword in the text. - # Only a space at the end. - match = match[:-1] - end = end - 1 - tags_append((keywordTag, start, end, None)) - # If this was a defining keyword, look ahead to the - # following identifier. - if match in ["def", "class"]: - m = idSearch(pytext, end) - if m is not None: - start = m.start() - if start == end: - match = m.group(0) - end = start + len(match) - tags_append((identifierTag, start, end, None)) - elif c == "#": - tags_append((commentTag, start, end, None)) - else: - tags_append((stringTag, start, end, None)) - return tags - - -def test(path): - f = open(path) - text = f.read() - f.close() - tags = fontify(text) - for tag, start, end, sublist in tags: - print tag, `text[start:end]` \ No newline at end of file diff --git a/bin/reportlab/lib/__init__.py b/bin/reportlab/lib/__init__.py deleted file mode 100644 index 22d3f38c1b7..00000000000 --- a/bin/reportlab/lib/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/__init__.py -__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -import os -RL_DEBUG = os.environ.has_key('RL_DEBUG') \ No newline at end of file diff --git a/bin/reportlab/lib/abag.py b/bin/reportlab/lib/abag.py deleted file mode 100644 index 4c3c029a8d0..00000000000 --- a/bin/reportlab/lib/abag.py +++ /dev/null @@ -1,44 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/abag.py -__version__=''' $Id: abag.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -class ABag: - """ - 'Attribute Bag' - a trivial BAG class for holding attributes. - - You may initialize with keyword arguments. - a = ABag(k0=v0,....,kx=vx,....) ==> getattr(a,'kx')==vx - - c = a.clone(ak0=av0,.....) copy with optional additional attributes. - """ - def __init__(self,**attr): - for k,v in attr.items(): - setattr(self,k,v) - - def clone(self,**attr): - n = apply(ABag,(),self.__dict__) - if attr != {}: apply(ABag.__init__,(n,),attr) - return n - - def __repr__(self): - import string - n = self.__class__.__name__ - L = [n+"("] - keys = self.__dict__.keys() - for k in keys: - v = getattr(self, k) - rk = repr(k) - rv = repr(v) - rk = " "+string.replace(rk, "\n", "\n ") - rv = " "+string.replace(rv, "\n", "\n ") - L.append(rk) - L.append(rv) - L.append(") #"+n) - return string.join(L, "\n") - -if __name__=="__main__": - AB = ABag(a=1, c="hello") - CD = AB.clone() - print AB - print CD \ No newline at end of file diff --git a/bin/reportlab/lib/attrmap.py b/bin/reportlab/lib/attrmap.py deleted file mode 100644 index 2f8e89c13f8..00000000000 --- a/bin/reportlab/lib/attrmap.py +++ /dev/null @@ -1,138 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/attrmap.py -__version__=''' $Id: attrmap.py 2741 2005-12-07 21:52:33Z andy $ ''' -from UserDict import UserDict -from reportlab.lib.validators import isAnything, _SequenceTypes, DerivedValue -from reportlab import rl_config - -class CallableValue: - '''a class to allow callable initial values''' - def __init__(self,func,*args,**kw): - #assert iscallable(func) - self.func = func - self.args = args - self.kw = kw - - def __call__(self): - return apply(self.func,self.args,self.kw) - -class AttrMapValue: - '''Simple multi-value holder for attribute maps''' - def __init__(self,validate=None,desc=None,initial=None, **kw): - self.validate = validate or isAnything - self.desc = desc - self._initial = initial - for k,v in kw.items(): - setattr(self,k,v) - - def __getattr__(self,name): - #hack to allow callable initial values - if name=='initial': - if isinstance(self._initial,CallableValue): return self._initial() - return self._initial - elif name=='hidden': - return 0 - raise AttributeError, name - -class AttrMap(UserDict): - def __init__(self,BASE=None,UNWANTED=[],**kw): - data = {} - if BASE: - if isinstance(BASE,AttrMap): - data = BASE.data #they used BASECLASS._attrMap - else: - if type(BASE) not in (type(()),type([])): BASE = (BASE,) - for B in BASE: - if hasattr(B,'_attrMap'): - data.update(getattr(B._attrMap,'data',{})) - else: - raise ValueError, 'BASE=%s has wrong kind of value' % str(B) - - UserDict.__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) - - def remove(self,unwanted): - for k in unwanted: - try: - del self[k] - except KeyError: - pass - - def clone(self,UNWANTED=[],**kw): - c = AttrMap(BASE=self,UNWANTED=UNWANTED) - c.update(kw) - return c - -def validateSetattr(obj,name,value): - '''validate setattr(obj,name,value)''' - if rl_config.shapeChecking: - map = obj._attrMap - if map and name[0]!= '_': - #we always allow the inherited values; they cannot - #be checked until draw time. - if isinstance(value, DerivedValue): - #let it through - pass - else: - 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__) - except KeyError: - raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__) - obj.__dict__[name] = value - -def _privateAttrMap(obj,ret=0): - '''clone obj._attrMap if required''' - A = obj._attrMap - oA = getattr(obj.__class__,'_attrMap',None) - if ret: - if oA is A: - return A.clone(), oA - else: - return A, None - else: - if oA is A: - obj._attrMap = A.clone() - -def _findObjectAndAttr(src, P): - '''Locate the object src.P for P a string, return parent and name of attribute - ''' - P = string.split(P, '.') - if len(P) == 0: - return None, None - else: - for p in P[0:-1]: - src = getattr(src, p) - return src, P[-1] - -def hook__setattr__(obj): - if not hasattr(obj,'__attrproxy__'): - C = obj.__class__ - import new - obj.__class__=new.classobj(C.__name__,(C,)+C.__bases__, - {'__attrproxy__':[], - '__setattr__':lambda self,k,v,osa=getattr(obj,'__setattr__',None),hook=hook: hook(self,k,v,osa)}) - -def addProxyAttribute(src,name,validate=None,desc=None,initial=None,dst=None): - ''' - Add a proxy attribute 'name' to src with targets dst - ''' - #sanity - assert hasattr(src,'_attrMap'), 'src object has no _attrMap' - A, oA = _privateAttrMap(src,1) - if type(dst) not in _SequenceTypes: dst = dst, - D = [] - DV = [] - for d in dst: - if type(d) in _SequenceTypes: - d, e = d[0], d[1:] - obj, attr = _findObjectAndAttr(src,d) - if obj: - dA = getattr(obj,'_attrMap',None) diff --git a/bin/reportlab/lib/codecharts.py b/bin/reportlab/lib/codecharts.py deleted file mode 100644 index 3d8630364b8..00000000000 --- a/bin/reportlab/lib/codecharts.py +++ /dev/null @@ -1,365 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/codecharts.py -#$Header $ -__version__=''' $Id ''' -__doc__="""Routines to print code page (character set) drawings. - -To be sure we can accurately represent characters in various encodings -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 -from reportlab.platypus import Flowable -from reportlab.pdfbase import pdfmetrics, cidfonts -from reportlab.graphics.shapes import Drawing, Group, String, Circle, Rect -from reportlab.graphics.widgetbase import Widget -from reportlab.lib import colors - -adobe2codec = { - 'WinAnsiEncoding':'winansi', - 'MacRomanEncoding':'macroman', - 'MacExpert':'macexpert', - 'PDFDoc':'pdfdoc', - - } - -class CodeChartBase(Flowable): - """Basic bits of drawing furniture used by - single and multi-byte versions: ability to put letters - into boxes.""" - - def calcLayout(self): - "Work out x and y positions for drawing" - - - rows = self.codePoints * 1.0 / self.charsPerRow - if rows == int(rows): - self.rows = int(rows) - else: - self.rows = int(rows) + 1 - # size allows for a gray column of labels - self.width = self.boxSize * (1+self.charsPerRow) - self.height = self.boxSize * (1+self.rows) - - #handy lists - self.ylist = [] - for row in range(self.rows + 2): - self.ylist.append(row * self.boxSize) - self.xlist = [] - for col in range(self.charsPerRow + 2): - self.xlist.append(col * self.boxSize) - - def formatByte(self, byt): - if self.hex: - return '%02X' % byt - else: - return '%d' % byt - - def drawChars(self, charList): - """Fills boxes in order. None means skip a box. - Empty boxes at end get filled with gray""" - extraNeeded = (self.rows * self.charsPerRow - len(charList)) - for i in range(extraNeeded): - charList.append(None) - #charList.extend([None] * extraNeeded) - row = 0 - col = 0 - self.canv.setFont(self.fontName, self.boxSize * 0.75) - for ch in charList: # may be 2 bytes or 1 - if ch is None: - self.canv.setFillGray(0.9) - self.canv.rect((1+col) * self.boxSize, (self.rows - row - 1) * self.boxSize, - self.boxSize, self.boxSize, stroke=0, fill=1) - self.canv.setFillGray(0.0) - else: - try: - self.canv.drawCentredString( - (col+1.5) * self.boxSize, - (self.rows - row - 0.875) * self.boxSize, - ch, - ) - except: - self.canv.setFillGray(0.9) - self.canv.rect((1+col) * self.boxSize, (self.rows - row - 1) * self.boxSize, - self.boxSize, self.boxSize, stroke=0, fill=1) - self.canv.drawCentredString( - (col+1.5) * self.boxSize, - (self.rows - row - 0.875) * self.boxSize, - '?', - ) - self.canv.setFillGray(0.0) - col = col + 1 - if col == self.charsPerRow: - row = row + 1 - col = 0 - - def drawLabels(self, topLeft = ''): - """Writes little labels in the top row and first column""" - self.canv.setFillGray(0.8) - self.canv.rect(0, self.ylist[-2], self.width, self.boxSize, fill=1, stroke=0) - self.canv.rect(0, 0, self.boxSize, self.ylist[-2], fill=1, stroke=0) - self.canv.setFillGray(0.0) - - #label each row and column - self.canv.setFont('Helvetica-Oblique',0.375 * self.boxSize) - byt = 0 - for row in range(self.rows): - if self.rowLabels: - label = self.rowLabels[row] - else: # format start bytes as hex or decimal - label = self.formatByte(row * self.charsPerRow) - self.canv.drawCentredString(0.5 * self.boxSize, - (self.rows - row - 0.75) * self.boxSize, - label - ) - for col in range(self.charsPerRow): - self.canv.drawCentredString((col + 1.5) * self.boxSize, - (self.rows + 0.25) * self.boxSize, - self.formatByte(col) - ) - - if topLeft: - self.canv.setFont('Helvetica-BoldOblique',0.5 * self.boxSize) - self.canv.drawCentredString(0.5 * self.boxSize, - (self.rows + 0.25) * self.boxSize, - topLeft - ) - -class SingleByteEncodingChart(CodeChartBase): - def __init__(self, faceName='Helvetica', encodingName='WinAnsiEncoding', - charsPerRow=16, boxSize=14, hex=1): - self.codePoints = 256 - self.faceName = faceName - self.encodingName = encodingName - self.fontName = self.faceName + '-' + self.encodingName - self.charsPerRow = charsPerRow - self.boxSize = boxSize - self.hex = hex - self.rowLabels = None - pdfmetrics.registerFont(pdfmetrics.Font(self.fontName, - self.faceName, - self.encodingName) - ) - - self.calcLayout() - - - def draw(self): - self.drawLabels() - charList = [None] * 32 + map(chr, range(32, 256)) - - #we need to convert these to Unicode, since ReportLab - #2.0 can only draw in Unicode. - - encName = self.encodingName - #apply some common translations - encName = adobe2codec.get(encName, encName) - decoder = codecs.lookup(encName)[1] - def decodeFunc(txt): - if txt is None: - return None - else: - return decoder(txt, errors='replace')[0] - - charList = [decodeFunc(ch) for ch in charList] - - - - self.drawChars(charList) - self.canv.grid(self.xlist, self.ylist) - - -class KutenRowCodeChart(CodeChartBase): - """Formats one 'row' of the 94x94 space used in many Asian encodings.aliases - - These deliberately resemble the code charts in Ken Lunde's "Understanding - CJKV Information Processing", to enable manual checking. Due to the large - numbers of characters, we don't try to make one graphic with 10,000 characters, - but rather output a sequence of these.""" - #would be cleaner if both shared one base class whose job - #was to draw the boxes, but never mind... - def __init__(self, row, faceName, encodingName): - self.row = row - self.codePoints = 94 - self.boxSize = 18 - self.charsPerRow = 20 - self.rows = 5 - self.rowLabels = ['00','20','40','60','80'] - self.hex = 0 - self.faceName = faceName - self.encodingName = encodingName - - try: - # the dependent files might not be available - font = cidfonts.CIDFont(self.faceName, self.encodingName) - pdfmetrics.registerFont(font) - except: - # fall back to English and at least shwo we can draw the boxes - self.faceName = 'Helvetica' - self.encodingName = 'WinAnsiEncoding' - self.fontName = self.faceName + '-' + self.encodingName - self.calcLayout() - - def makeRow(self, row): - """Works out the character values for this kuten row""" - cells = [] - if string.find(self.encodingName, 'EUC') > -1: - # it is an EUC family encoding. - for col in range(1, 95): - ch = chr(row + 160) + chr(col+160) - cells.append(ch) -## elif string.find(self.encodingName, 'GB') > -1: -## # it is an EUC family encoding. -## for col in range(1, 95): -## ch = chr(row + 160) + chr(col+160) - else: - cells.append([None] * 94) - return cells - - def draw(self): - 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 - - # pad out by 1 to match Ken Lunde's tables - charList = [None] + self.makeRow(self.row) - self.drawChars(charList) - self.canv.grid(self.xlist, self.ylist) - - -class Big5CodeChart(CodeChartBase): - """Formats one 'row' of the 94x160 space used in Big 5 - - These deliberately resemble the code charts in Ken Lunde's "Understanding - CJKV Information Processing", to enable manual checking.""" - def __init__(self, row, faceName, encodingName): - self.row = row - self.codePoints = 160 - self.boxSize = 18 - self.charsPerRow = 16 - self.rows = 10 - self.hex = 1 - self.faceName = faceName - self.encodingName = encodingName - self.rowLabels = ['4','5','6','7','A','B','C','D','E','F'] - try: - # the dependent files might not be available - font = cidfonts.CIDFont(self.faceName, self.encodingName) - pdfmetrics.registerFont(font) - except: - # fall back to English and at least shwo we can draw the boxes - self.faceName = 'Helvetica' - self.encodingName = 'WinAnsiEncoding' - self.fontName = self.faceName + '-' + self.encodingName - self.calcLayout() - - def makeRow(self, row): - """Works out the character values for this Big5 row. - Rows start at 0xA1""" - cells = [] - if string.find(self.encodingName, '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) - cells.append(ch) - - else: - cells.append([None] * 160) - return cells - - def draw(self): - self.drawLabels(topLeft='%02X' % self.row) - - charList = self.makeRow(self.row) - self.drawChars(charList) - self.canv.grid(self.xlist, self.ylist) - - -def hBoxText(msg, canvas, x, y, fontName): - """Helper for stringwidth tests on Asian fonts. - - Registers font if needed. Then draws the string, - and a box around it derived from the stringWidth function""" - canvas.saveState() - try: - font = pdfmetrics.getFont(fontName) - except KeyError: - font = cidfonts.UnicodeCIDFont(fontName) - pdfmetrics.registerFont(font) - - canvas.setFillGray(0.8) - canvas.rect(x,y,pdfmetrics.stringWidth(msg, fontName, 16),16,stroke=0,fill=1) - canvas.setFillGray(0) - canvas.setFont(fontName, 16,16) - canvas.drawString(x,y,msg) - canvas.restoreState() - - -class CodeWidget(Widget): - """Block showing all the characters""" - def __init__(self): - self.x = 0 - self.y = 0 - self.width = 160 - self.height = 160 - - def draw(self): - dx = self.width / 16.0 - dy = self.height / 16.0 - g = Group() - g.add(Rect(self.x, self.y, self.width, self.height, - fillColor=None, strokeColor=colors.black)) - for x in range(16): - for y in range(16): - charValue = y * 16 + x - if charValue > 32: - s = String(self.x + x * dx, - self.y + (self.height - y*dy), chr(charValue)) - g.add(s) - return g - - - - - - -def test(): - c = Canvas('codecharts.pdf') - c.setFont('Helvetica-Bold', 24) - c.drawString(72, 750, 'Testing code page charts') - cc1 = SingleByteEncodingChart() - cc1.drawOn(c, 72, 500) - - cc2 = SingleByteEncodingChart(charsPerRow=32) - cc2.drawOn(c, 72, 300) - - cc3 = SingleByteEncodingChart(charsPerRow=25, hex=0) - cc3.drawOn(c, 72, 100) - -## c.showPage() -## -## c.setFont('Helvetica-Bold', 24) -## c.drawString(72, 750, 'Multi-byte Kuten code chart examples') -## KutenRowCodeChart(1, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 600) -## KutenRowCodeChart(16, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 450) -## KutenRowCodeChart(84, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 300) -## -## c.showPage() -## c.setFont('Helvetica-Bold', 24) -## c.drawString(72, 750, 'Big5 Code Chart Examples') -## #Big5CodeChart(0xA1, 'MSungStd-Light-Acro','ETenms-B5-H').drawOn(c, 72, 500) - - c.save() - print 'saved codecharts.pdf' - -if __name__=='__main__': - test() - - diff --git a/bin/reportlab/lib/colors.py b/bin/reportlab/lib/colors.py deleted file mode 100644 index ed537259e06..00000000000 --- a/bin/reportlab/lib/colors.py +++ /dev/null @@ -1,565 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/colors.py -__version__=''' $Id: colors.py 2587 2005-05-03 11:41:54Z rgbecker $ ''' - -import string, math -from types import StringType, ListType, TupleType -from reportlab.lib.utils import fp_str -_SeqTypes = (ListType,TupleType) - -class Color: - """This class is used to represent color. Components red, green, blue - are in the range 0 (dark) to 1 (full intensity).""" - - def __init__(self, red=0, green=0, blue=0): - "Initialize with red, green, blue in range [0-1]." - self.red, self.green, self.blue = red,green,blue - - def __repr__(self): - return "Color(%s)" % string.replace(fp_str(self.red, self.green, self.blue),' ',',') - - def __hash__(self): - return hash( (self.red, self.green, self.blue) ) - - def __cmp__(self,other): - try: - dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue - except: - return -1 - if dsum > 0: return 1 - if dsum < 0: return -1 - return 0 - - def rgb(self): - "Returns a three-tuple of components" - return (self.red, self.green, self.blue) - - def bitmap_rgb(self): - return tuple(map(lambda x: int(x*255)&255, self.rgb())) - - def hexval(self): - return '0x%02x%02x%02x' % self.bitmap_rgb() - -class CMYKColor(Color): - """This represents colors using the CMYK (cyan, magenta, yellow, black) - model commonly used in professional printing. This is implemented - as a derived class so that renderers which only know about RGB "see it" - as an RGB color through its 'red','green' and 'blue' attributes, according - to an approximate function. - - The RGB approximation is worked out when the object in constructed, so - the color attributes should not be changed afterwards. - - Extra attributes may be attached to the class to support specific ink models, - and renderers may look for these.""" - - def __init__(self, cyan=0, magenta=0, yellow=0, black=0, - spotName=None, density=1, knockout=None): - """ - Initialize with four colors in range [0-1]. the optional - spotName, density & knockout may be of use to specific renderers. - spotName is intended for use as an identifier to the renderer not client programs. - density is used to modify the overall amount of ink. - knockout is a renderer dependent option that determines whether the applied colour - knocksout (removes) existing colour; None means use the global default. - """ - self.cyan = cyan - self.magenta = magenta - self.yellow = yellow - self.black = black - self.spotName = spotName - self.density = max(min(density,1),0) # force into right range - self.knockout = knockout - - # now work out the RGB approximation. override - self.red, self.green, self.blue = cmyk2rgb( (cyan, magenta, yellow, black) ) - - if density<1: - #density adjustment of rgb approximants, effectively mix with white - r, g, b = self.red, self.green, self.blue - r = density*(r-1)+1 - g = density*(g-1)+1 - b = density*(b-1)+1 - self.red, self.green, self.blue = (r,g,b) - - def __repr__(self): - return "CMYKColor(%s%s%s%s)" % ( - string.replace(fp_str(self.cyan, self.magenta, self.yellow, self.black),' ',','), - (self.spotName and (',spotName='+repr(self.spotName)) or ''), - (self.density!=1 and (',density='+fp_str(self.density)) or ''), - (self.knockout is not None and (',knockout=%d' % self.knockout) or ''), - ) - - def __hash__(self): - return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName) ) - - def __cmp__(self,other): - """Partial ordering of colors according to a notion of distance. - - Comparing across the two color models is of limited use.""" - # why the try-except? What can go wrong? - if isinstance(other, CMYKColor): - dsum = (((( (self.cyan-other.cyan)*2 + - (self.magenta-other.magenta))*2+ - (self.yellow-other.yellow))*2+ - (self.black-other.black))*2+ - (self.density-other.density))*2 + cmp(self.spotName or '',other.spotName or '') - else: # do the RGB comparison - try: - dsum = ((self.red-other.red)*2+(self.green-other.green))*2+(self.blue-other.blue) - except: # or just return 'not equal' if not a color - return -1 - if dsum >= 0: - return dsum>0 - else: - return -1 - - def cmyk(self): - "Returns a tuple of four color components - syntactic sugar" - return (self.cyan, self.magenta, self.yellow, self.black) - - def _density_str(self): - return fp_str(self.density) - -class PCMYKColor(CMYKColor): - '''100 based CMYKColor with density and a spotName; just like Rimas uses''' - def __init__(self,cyan,magenta,yellow,black,density=100,spotName=None,knockout=None): - CMYKColor.__init__(self,cyan/100.,magenta/100.,yellow/100.,black/100.,spotName,density/100.,knockout=knockout) - - def __repr__(self): - return "PCMYKColor(%s%s%s%s)" % ( - string.replace(fp_str(self.cyan*100, self.magenta*100, self.yellow*100, self.black*100),' ',','), - (self.spotName and (',spotName='+repr(self.spotName)) or ''), - (self.density!=1 and (',density='+fp_str(self.density*100)) or ''), - (self.knockout is not None and (',knockout=%d' % self.knockout) or ''), - ) - -def cmyk2rgb((c,m,y,k),density=1): - "Convert from a CMYK color tuple to an RGB color tuple" - # From the Adobe Postscript Ref. Manual 2nd ed. - r = 1.0 - min(1.0, c + k) - g = 1.0 - min(1.0, m + k) - b = 1.0 - min(1.0, y + k) - return (r,g,b) - -def rgb2cmyk(r,g,b): - '''one way to get cmyk from rgb''' - c = 1 - r - m = 1 - g - y = 1 - b - k = min(c,m,y) - c = min(1,max(0,c-k)) - m = min(1,max(0,m-k)) - y = min(1,max(0,y-k)) - k = min(1,max(0,k)) - return (c,m,y,k) - -def color2bw(colorRGB): - "Transform an RGB color to a black and white equivalent." - - col = colorRGB - r, g, b = col.red, col.green, col.blue - n = (r + g + b) / 3.0 - bwColorRGB = Color(n, n, n) - return bwColorRGB - -def HexColor(val): - """This function converts a hex string, or an actual integer number, - into the corresponding color. E.g., in "AABBCC" or 0xAABBCC, - AA is the red, BB is the green, and CC is the blue (00-FF). - - HTML uses a hex string with a preceding hash; if this is present, - it is stripped off. (AR, 3-3-2000) - - For completeness I assume that #aabbcc or 0xaabbcc are hex numbers - otherwise a pure integer is converted as decimal rgb - """ - - if type(val) == StringType: - b = 10 - if val[:1] == '#': - val = val[1:] - b = 16 - elif string.lower(val[:2]) == '0x': - b = 16 - val = val[2:] - val = string.atoi(val,b) - return Color(((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0) - -def linearlyInterpolatedColor(c0, c1, x0, x1, x): - """ - Linearly interpolates colors. Can handle RGB, CMYK and PCMYK - colors - give ValueError if colours aren't the same. - Doesn't currently handle 'Spot Color Interpolation'. - """ - - if c0.__class__ != c1.__class__: - raise ValueError, "Color classes must be the same for interpolation!" - if x1x0 - if xx1+1e-8: # fudge factor for numerical problems - raise ValueError, "Can't interpolate: x=%f is not between %f and %f!" % (x,x0,x1) - if x<=x0: - return c0 - elif x>=x1: - return c1 - - cname = c0.__class__.__name__ - dx = float(x1-x0) - x = x-x0 - - if cname == 'Color': # RGB - r = c0.red+x*(c1.red - c0.red)/dx - g = c0.green+x*(c1.green- c0.green)/dx - b = c0.blue+x*(c1.blue - c0.blue)/dx - return Color(r,g,b) - elif cname == 'CMYKColor': - c = c0.cyan+x*(c1.cyan - c0.cyan)/dx - m = c0.magenta+x*(c1.magenta - c0.magenta)/dx - y = c0.yellow+x*(c1.yellow - c0.yellow)/dx - k = c0.black+x*(c1.black - c0.black)/dx - d = c0.density+x*(c1.density - c0.density)/dx - return CMYKColor(c,m,y,k, density=d) - elif cname == 'PCMYKColor': - if cmykDistance(c0,c1)<1e-8: - #colors same do density and preserve spotName if any - assert c0.spotName == c1.spotName, "Identical cmyk, but different spotName" - c = c0.cyan - m = c0.magenta - y = c0.yellow - k = c0.black - d = c0.density+x*(c1.density - c0.density)/dx - return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName) - elif cmykDistance(c0,_CMYK_white)<1e-8: - #special c0 is white - c = c1.cyan - m = c1.magenta - y = c1.yellow - k = c1.black - d = x*c1.density/dx - return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c1.spotName) - elif cmykDistance(c1,_CMYK_white)<1e-8: - #special c1 is white - c = c0.cyan - m = c0.magenta - y = c0.yellow - k = c0.black - d = x*c0.density/dx - d = c0.density*(1-x/dx) - return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName) - else: - c = c0.cyan+x*(c1.cyan - c0.cyan)/dx - m = c0.magenta+x*(c1.magenta - c0.magenta)/dx - y = c0.yellow+x*(c1.yellow - c0.yellow)/dx - k = c0.black+x*(c1.black - c0.black)/dx - d = c0.density+x*(c1.density - c0.density)/dx - return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100) - else: - raise ValueError, "Can't interpolate: Unknown color class %s!" % cname - -# special case -- indicates no drawing should be done -# this is a hangover from PIDDLE - suggest we ditch it since it is not used anywhere -#transparent = Color(-1, -1, -1) - -_CMYK_white=CMYKColor(0,0,0,0) -_PCMYK_white=PCMYKColor(0,0,0,0) -_CMYK_black=CMYKColor(0,0,0,1) -_PCMYK_black=PCMYKColor(0,0,0,100) - -# Special colors -ReportLabBlueOLD = HexColor(0x4e5688) -ReportLabBlue = HexColor(0x00337f) -ReportLabBluePCMYK = PCMYKColor(100,65,0,30,spotName='Pantone 288U') -ReportLabLightBlue = HexColor(0xb7b9d3) -ReportLabFidBlue=HexColor(0x3366cc) -ReportLabFidRed=HexColor(0xcc0033) -ReportLabGreen = HexColor(0x336600) -ReportLabLightGreen = HexColor(0x339933) - -# color constants -- mostly from HTML standard -aliceblue = HexColor(0xF0F8FF) -antiquewhite = HexColor(0xFAEBD7) -aqua = HexColor(0x00FFFF) -aquamarine = HexColor(0x7FFFD4) -azure = HexColor(0xF0FFFF) -beige = HexColor(0xF5F5DC) -bisque = HexColor(0xFFE4C4) -black = HexColor(0x000000) -blanchedalmond = HexColor(0xFFEBCD) -blue = HexColor(0x0000FF) -blueviolet = HexColor(0x8A2BE2) -brown = HexColor(0xA52A2A) -burlywood = HexColor(0xDEB887) -cadetblue = HexColor(0x5F9EA0) -chartreuse = HexColor(0x7FFF00) -chocolate = HexColor(0xD2691E) -coral = HexColor(0xFF7F50) -cornflowerblue = cornflower = HexColor(0x6495ED) -cornsilk = HexColor(0xFFF8DC) -crimson = HexColor(0xDC143C) -cyan = HexColor(0x00FFFF) -darkblue = HexColor(0x00008B) -darkcyan = HexColor(0x008B8B) -darkgoldenrod = HexColor(0xB8860B) -darkgray = HexColor(0xA9A9A9) -darkgreen = HexColor(0x006400) -darkkhaki = HexColor(0xBDB76B) -darkmagenta = HexColor(0x8B008B) -darkolivegreen = HexColor(0x556B2F) -darkorange = HexColor(0xFF8C00) -darkorchid = HexColor(0x9932CC) -darkred = HexColor(0x8B0000) -darksalmon = HexColor(0xE9967A) -darkseagreen = HexColor(0x8FBC8B) -darkslateblue = HexColor(0x483D8B) -darkslategray = HexColor(0x2F4F4F) -darkturquoise = HexColor(0x00CED1) -darkviolet = HexColor(0x9400D3) -deeppink = HexColor(0xFF1493) -deepskyblue = HexColor(0x00BFFF) -dimgray = HexColor(0x696969) -dodgerblue = HexColor(0x1E90FF) -firebrick = HexColor(0xB22222) -floralwhite = HexColor(0xFFFAF0) -forestgreen = HexColor(0x228B22) -fuchsia = HexColor(0xFF00FF) -gainsboro = HexColor(0xDCDCDC) -ghostwhite = HexColor(0xF8F8FF) -gold = HexColor(0xFFD700) -goldenrod = HexColor(0xDAA520) -gray = HexColor(0x808080) -grey = gray -green = HexColor(0x008000) -greenyellow = HexColor(0xADFF2F) -honeydew = HexColor(0xF0FFF0) -hotpink = HexColor(0xFF69B4) -indianred = HexColor(0xCD5C5C) -indigo = HexColor(0x4B0082) -ivory = HexColor(0xFFFFF0) -khaki = HexColor(0xF0E68C) -lavender = HexColor(0xE6E6FA) -lavenderblush = HexColor(0xFFF0F5) -lawngreen = HexColor(0x7CFC00) -lemonchiffon = HexColor(0xFFFACD) -lightblue = HexColor(0xADD8E6) -lightcoral = HexColor(0xF08080) -lightcyan = HexColor(0xE0FFFF) -lightgoldenrodyellow = HexColor(0xFAFAD2) -lightgreen = HexColor(0x90EE90) -lightgrey = HexColor(0xD3D3D3) -lightpink = HexColor(0xFFB6C1) -lightsalmon = HexColor(0xFFA07A) -lightseagreen = HexColor(0x20B2AA) -lightskyblue = HexColor(0x87CEFA) -lightslategray = HexColor(0x778899) -lightsteelblue = HexColor(0xB0C4DE) -lightyellow = HexColor(0xFFFFE0) -lime = HexColor(0x00FF00) -limegreen = HexColor(0x32CD32) -linen = HexColor(0xFAF0E6) -magenta = HexColor(0xFF00FF) -maroon = HexColor(0x800000) -mediumaquamarine = HexColor(0x66CDAA) -mediumblue = HexColor(0x0000CD) -mediumorchid = HexColor(0xBA55D3) -mediumpurple = HexColor(0x9370DB) -mediumseagreen = HexColor(0x3CB371) -mediumslateblue = HexColor(0x7B68EE) -mediumspringgreen = HexColor(0x00FA9A) -mediumturquoise = HexColor(0x48D1CC) -mediumvioletred = HexColor(0xC71585) -midnightblue = HexColor(0x191970) -mintcream = HexColor(0xF5FFFA) -mistyrose = HexColor(0xFFE4E1) -moccasin = HexColor(0xFFE4B5) -navajowhite = HexColor(0xFFDEAD) -navy = HexColor(0x000080) -oldlace = HexColor(0xFDF5E6) -olive = HexColor(0x808000) -olivedrab = HexColor(0x6B8E23) -orange = HexColor(0xFFA500) -orangered = HexColor(0xFF4500) -orchid = HexColor(0xDA70D6) -palegoldenrod = HexColor(0xEEE8AA) -palegreen = HexColor(0x98FB98) -paleturquoise = HexColor(0xAFEEEE) -palevioletred = HexColor(0xDB7093) -papayawhip = HexColor(0xFFEFD5) -peachpuff = HexColor(0xFFDAB9) -peru = HexColor(0xCD853F) -pink = HexColor(0xFFC0CB) -plum = HexColor(0xDDA0DD) -powderblue = HexColor(0xB0E0E6) -purple = HexColor(0x800080) -red = HexColor(0xFF0000) -rosybrown = HexColor(0xBC8F8F) -royalblue = HexColor(0x4169E1) -saddlebrown = HexColor(0x8B4513) -salmon = HexColor(0xFA8072) -sandybrown = HexColor(0xF4A460) -seagreen = HexColor(0x2E8B57) -seashell = HexColor(0xFFF5EE) -sienna = HexColor(0xA0522D) -silver = HexColor(0xC0C0C0) -skyblue = HexColor(0x87CEEB) -slateblue = HexColor(0x6A5ACD) -slategray = HexColor(0x708090) -snow = HexColor(0xFFFAFA) -springgreen = HexColor(0x00FF7F) -steelblue = HexColor(0x4682B4) -tan = HexColor(0xD2B48C) -teal = HexColor(0x008080) -thistle = HexColor(0xD8BFD8) -tomato = HexColor(0xFF6347) -turquoise = HexColor(0x40E0D0) -violet = HexColor(0xEE82EE) -wheat = HexColor(0xF5DEB3) -white = HexColor(0xFFFFFF) -whitesmoke = HexColor(0xF5F5F5) -yellow = HexColor(0xFFFF00) -yellowgreen = HexColor(0x9ACD32) -fidblue=HexColor(0x3366cc) -fidred=HexColor(0xcc0033) -fidlightblue=HexColor("#d6e0f5") - -ColorType=type(black) - - ################################################################ - # - # Helper functions for dealing with colors. These tell you - # which are predefined, so you can print color charts; - # and can give the nearest match to an arbitrary color object - # - ################################################################# - -def colorDistance(col1, col2): - """Returns a number between 0 and root(3) stating how similar - two colours are - distance in r,g,b, space. Only used to find - names for things.""" - return math.sqrt( - (col1.red - col2.red)**2 + - (col1.green - col2.green)**2 + - (col1.blue - col2.blue)**2 - ) - -def cmykDistance(col1, col2): - """Returns a number between 0 and root(4) stating how similar - two colours are - distance in r,g,b, space. Only used to find - names for things.""" - return math.sqrt( - (col1.cyan - col2.cyan)**2 + - (col1.magenta - col2.magenta)**2 + - (col1.yellow - col2.yellow)**2 + - (col1.black - col2.black)**2 - ) - -_namedColors = None - -def getAllNamedColors(): - #returns a dictionary of all the named ones in the module - # uses a singleton for efficiency - global _namedColors - if _namedColors is not None: return _namedColors - import colors - _namedColors = {} - for (name, value) in colors.__dict__.items(): - if isinstance(value, Color): - _namedColors[name] = value - - return _namedColors - -def describe(aColor,mode=0): - '''finds nearest colour match to aColor. - mode=0 print a string desription - mode=1 return a string description - mode=2 return (distance, colorName) - ''' - namedColors = getAllNamedColors() - closest = (10, None, None) #big number, name, color - 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 - else: return s - elif mode==2: - return (closest[1], closest[0]) - else: - raise ValueError, "Illegal value for mode "+str(mode) - -def toColor(arg,default=None): - '''try to map an arbitrary arg to a color instance''' - if isinstance(arg,Color): return arg - tArg = type(arg) - if tArg in _SeqTypes: - 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 tArg == StringType: - C = getAllNamedColors() - s = string.lower(arg) - if C.has_key(s): return C[s] - try: - return toColor(eval(arg)) - except: - pass - - try: - return HexColor(arg) - except: - if default is None: - raise 'Invalid color value', str(arg) - return default - -def toColorOrNone(arg,default=None): - '''as above but allows None as a legal value''' - if arg is None: - return None - else: - return toColor(arg, default) - -def setColors(**kw): - UNDEF = [] - progress = 1 - assigned = {} - while kw and progress: - progress = 0 - for k, v in kw.items(): - if type(v) in (type(()),type([])): - c = map(lambda x,UNDEF=UNDEF: toColor(x,UNDEF),v) - if type(v) is type(()): c = tuple(c) - ok = UNDEF not in c - else: - c = toColor(v,UNDEF) - ok = c is not UNDEF - if ok: - assigned[k] = c - del kw[k] - progress = 1 - - if kw: raise ValueError("Can't convert\n%s" % str(kw)) - getAllNamedColors() - for k, c in assigned.items(): - globals()[k] = c - if isinstance(c,Color): _namedColors[k] = c - -def Whiter(c,f): - '''given a color combine with white as c*f w*(1-f) 0<=f<=1''' - c = toColor(c) - if isinstance(c,PCMYKColor): - w = _PCMYK_white - elif isinstance(c,CMYKColor): w = _CMYK_white - else: w = white - return linearlyInterpolatedColor(w, c, 0, 1, f) - -def Blacker(c,f): - '''given a color combine with black as c*f+b*(1-f) 0<=f<=1''' - c = toColor(c) - if isinstance(c,PCMYKColor): - b = _PCMYK_black - elif isinstance(c,CMYKColor): b = _CMYK_black - else: b = black - return linearlyInterpolatedColor(b, c, 0, 1, f) diff --git a/bin/reportlab/lib/corp.py b/bin/reportlab/lib/corp.py deleted file mode 100644 index 0904c4d3a9c..00000000000 --- a/bin/reportlab/lib/corp.py +++ /dev/null @@ -1,452 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/corp.py -""" This module includes some reusable routines for ReportLab's - 'Corporate Image' - the logo, standard page backdrops and - so on - you are advised to do the same for your own company!""" -__version__=''' $Id: corp.py 2484 2004-12-10 07:27:50Z andy $ ''' - -from reportlab.lib.units import inch,cm -from reportlab.lib.validators import * -from reportlab.lib.attrmap import * -from reportlab.graphics.shapes import definePath, Group, Drawing, Rect, PolyLine, String -from reportlab.graphics.widgetbase import Widget -from reportlab.lib.colors import Color, black, white, ReportLabBlue -from reportlab.pdfbase.pdfmetrics import stringWidth -from math import sin, pi - -class RL_CorpLogo(Widget): - '''Dinu's fat letter logo as hacked into decent paths by Robin''' - _attrMap = AttrMap( - x = AttrMapValue(isNumber,'Logo x-coord'), - y = AttrMapValue(isNumber,'Logo y-coord'), - angle = AttrMapValue(isNumber,'Logo rotation'), - strokeColor = AttrMapValue(isColorOrNone, 'Logo lettering stroke color'), - fillColor = AttrMapValue(isColorOrNone, 'Logo lettering fill color'), - strokeWidth = AttrMapValue(isNumber,'Logo lettering stroke width'), - background = AttrMapValue(isColorOrNone,desc="Logo background color"), - border = AttrMapValue(isColorOrNone,desc="Logo border color"), - borderWidth = AttrMapValue(isNumber,desc="Logo border width (1)"), - shadow = AttrMapValue(isNumberOrNone,desc="None or fraction of background for shadowing" ), - width = AttrMapValue(isNumber, desc="width in points of the logo (default 129)"), - height = AttrMapValue(isNumber, desc="height in points of the logo (default 86)"), - skewX = AttrMapValue(isNumber, desc="x-skew of the logo (default 10)"), - skewY = AttrMapValue(isNumber, desc="y-skew of the logo (default 0)"), - showPage = AttrMapValue(isBoolean, desc="If true show the page lines"), - xFlip = AttrMapValue(isBoolean, desc="If true do x reversal"), - yFlip = AttrMapValue(isBoolean, desc="If true do y reversal"), - ) - - def __init__(self): - self.fillColor = white - self.strokeColor = None - self.strokeWidth = 0.1 - self.background = ReportLabBlue - self.border = None - self.borderWidth = 1 - self.shadow = 0.5 - self.height = 86 - self.width = 130 - self.x = self.y = self.angle = self.skewY = self._dx = 0 - self.skewX = 10 - self._dy = 35.5 - self.showPage = 1 - - def demo(self): - D = Drawing(self.width, self.height) - D.add(self) - return D - - def _paintLogo(self, g, dx=0, dy=0, strokeColor=None, strokeWidth=0.1, fillColor=white): - P = [ - ('moveTo' ,15.7246,0 ), ('lineTo' ,9.49521,0 ), ('lineTo' ,6.64988,6.83711 ), ('curveTo' ,6.62224,6.95315 ,6.57391,7.10646 ,6.50485,7.29708 ), ('curveTo' ,6.43578,7.48767 ,6.35059,7.71559 ,6.24931,7.98079 ), ('lineTo' ,6.29074,6.71282 ), ('lineTo' ,6.29074,0 ), ('lineTo' ,0.55862,0 ), ('lineTo' ,0.55862,19.19365 ), ('lineTo' ,6.45649,19.19365 ), ('curveTo' ,9.05324,19.19365 ,10.99617,18.73371 ,12.28532,17.8138 ), ('curveTo' ,13.92439,16.63697 ,14.7439,14.96293 ,14.7439,12.79161 ), ('curveTo' ,14.7439,10.47114 ,13.64354,8.86755 ,11.44276,7.98079 ), 'closePath', ('moveTo' ,6.31838,10.30542 ), ('lineTo' ,6.70513,10.30542 ), ('curveTo' ,7.36812,10.30542 ,7.92062,10.53331 ,8.36261,10.98912 ), ('curveTo' ,8.80461,11.44491 ,9.0256,12.02504 ,9.0256,12.72947 ), ('curveTo' ,9.0256,14.16321 ,8.19227,14.88004 ,6.52556,14.88004 ), ('lineTo' ,6.31838,14.88004 ), 'closePath', - ('moveTo' ,25.06173,4.54978 ), ('lineTo' ,30.47611,4.45033 ), ('curveTo' ,30.08951,2.88402 ,29.33668,1.70513 ,28.21787,0.91369 ), ('curveTo' ,27.09906,0.12223 ,25.63726,-0.27348 ,23.83245,-0.27348 ), ('curveTo' ,21.69611,-0.27348 ,20.02024,0.32322 ,18.80475,1.5166 ), ('curveTo' ,17.59846,2.72658 ,16.99531,4.37988 ,16.99531,6.47662 ), ('curveTo' ,16.99531,8.6065 ,17.64451,10.34269 ,18.94286,11.68527 ), ('curveTo' ,20.24124,13.03612 ,21.91711,13.71152 ,23.97056,13.71152 ), ('curveTo' ,26.01482,13.71152 ,27.64466,13.06096 ,28.86015,11.75985 ), ('curveTo' ,30.07566,10.45042 ,30.68326,8.71423 ,30.68326,6.5512 ), ('lineTo' ,30.65586,5.66859 ), ('lineTo' ,22.53407,5.66859 ), ('curveTo' ,22.59855,4.29287 ,23.03132,3.60503 ,23.83245,3.60503 ), ('curveTo' ,24.45861,3.60503 ,24.86837,3.91994 ,25.06173,4.54978 ), 'closePath', ('moveTo' ,25.18604,8.35371 ), ('curveTo' ,25.18604,8.60235 ,25.15384,8.83024 ,25.08937,9.03742 ), ('curveTo' ,25.02489,9.24463 ,24.93514,9.42278 ,24.82001,9.57197 ), ('curveTo' ,24.70492,9.72113 ,24.56911,9.83923 ,24.41255,9.92624 ), ('curveTo' ,24.25603,10.01326 ,24.08568,10.05678 ,23.90152,10.05678 ), ('curveTo' ,23.51474,10.05678 ,23.20169,9.89725 ,22.96225,9.57819 ), ('curveTo' ,22.72283,9.25913 ,22.60314,8.85096 ,22.60314,8.35371 ), 'closePath', - ('moveTo' ,38.36308,-5.99181 ), ('lineTo' ,32.82428,-5.99181 ), ('lineTo' ,32.82428,13.43804 ), ('lineTo' ,38.36308,13.43804 ), ('lineTo' ,38.23873,11.53608 ), ('curveTo' ,38.46886,11.93387 ,38.70371,12.27159 ,38.94327,12.54922 ), ('curveTo' ,39.18254,12.82685 ,39.44037,13.05268 ,39.71676,13.22671 ), ('curveTo' ,39.99286,13.40074 ,40.28988,13.52712 ,40.60753,13.60585 ), ('curveTo' ,40.92518,13.68459 ,41.27759,13.72396 ,41.66419,13.72396 ), ('curveTo' ,43.10068,13.72396 ,44.2702,13.07755 ,45.17246,11.78472 ), ('curveTo' ,46.06588,10.50844 ,46.51229,8.81368 ,46.51229,6.70038 ), ('curveTo' ,46.51229,4.55394 ,46.08415,2.85502 ,45.22785,1.60362 ), ('curveTo' ,44.38983,0.35221 ,43.23416,-0.27348 ,41.76084,-0.27348 ), ('curveTo' ,40.41659,-0.27348 ,39.24235,0.42679 ,38.23873,1.82739 ), ('curveTo' ,38.2847,1.40472 ,38.31239,1.04007 ,38.32153,0.73345 ), ('curveTo' ,38.34923,0.41851 ,38.36308,0.04146 ,38.36308,-0.3978 ), 'closePath', ('moveTo' ,40.7802,6.84954 ), ('curveTo' ,40.7802,7.72802 ,40.66734,8.40964 ,40.44193,8.89448 ), ('curveTo' ,40.21621,9.37929 ,39.89621,9.62168 ,39.48191,9.62168 ), ('curveTo' ,38.62533,9.62168 ,38.19718,8.68108 ,38.19718,6.79983 ), ('curveTo' ,38.19718,4.87712 ,38.61177,3.91581 ,39.44037,3.91581 ), ('curveTo' ,39.85466,3.91581 ,40.18174,4.1727 ,40.42101,4.68654 ), ('curveTo' ,40.66057,5.20037 ,40.7802,5.92135 ,40.7802,6.84954 ), 'closePath', - ('moveTo' ,62.10648,6.51392 ), ('curveTo' ,62.10648,4.44205 ,61.47118,2.79288 ,60.2003,1.56631 ), ('curveTo' ,58.92971,0.33978 ,57.22626,-0.27348 ,55.08965,-0.27348 ), ('curveTo' ,52.99018,-0.27348 ,51.31914,0.35221 ,50.07595,1.60362 ), ('curveTo' ,48.8419,2.8633 ,48.22517,4.55394 ,48.22517,6.67551 ), ('curveTo' ,48.22517,8.79709 ,48.85575,10.50016 ,50.1175,11.78472 ), ('curveTo' ,51.36982,13.07755 ,53.03172,13.72396 ,55.1035,13.72396 ), ('curveTo' ,57.28608,13.72396 ,58.99866,13.08168 ,60.24185,11.79712 ), ('curveTo' ,61.48503,10.51259 ,62.10648,8.75154 ,62.10648,6.51392 ), 'closePath', ('moveTo' ,56.73358,6.67551 ), ('curveTo' ,56.73358,7.17276 ,56.69675,7.62236 ,56.62308,8.02428 ), ('curveTo' ,56.54942,8.42623 ,56.44334,8.77016 ,56.30544,9.05607 ), ('curveTo' ,56.16724,9.34198 ,56.00134,9.56369 ,55.80804,9.72113 ), ('curveTo' ,55.61474,9.8786 ,55.39817,9.95733 ,55.1589,9.95733 ), ('curveTo' ,54.68921,9.95733 ,54.31174,9.65898 ,54.02621,9.06229 ), ('curveTo' ,53.74068,8.54018 ,53.59807,7.75702 ,53.59807,6.71282 ), ('curveTo' ,53.59807,5.68515 ,53.74068,4.90202 ,54.02621,4.36332 ), ('curveTo' ,54.31174,3.76663 ,54.69392,3.46828 ,55.17275,3.46828 ), ('curveTo' ,55.62388,3.46828 ,55.99692,3.7625 ,56.29159,4.35088 ), ('curveTo' ,56.58625,5.0056 ,56.73358,5.78047 ,56.73358,6.67551 ), 'closePath', - ('moveTo' ,69.78629,0 ), ('lineTo' ,64.2475,0 ), ('lineTo' ,64.2475,13.43804 ), ('lineTo' ,69.78629,13.43804 ), ('lineTo' ,69.49605,10.81507 ), ('curveTo' ,70.33407,12.77921 ,71.71988,13.76126 ,73.65346,13.76126 ), ('lineTo' ,73.65346,8.16725 ), ('curveTo' ,73.04586,8.4656 ,72.5302,8.61478 ,72.10647,8.61478 ), ('curveTo' ,71.36068,8.61478 ,70.78756,8.37236 ,70.38711,7.88755 ), ('curveTo' ,69.98637,7.40274 ,69.78629,6.69623 ,69.78629,5.76804 ), 'closePath', - ('moveTo' ,81.55427,0 ), ('lineTo' ,76.00163,0 ), ('lineTo' ,76.00163,9.42278 ), ('lineTo' ,74.42725,9.42278 ), ('lineTo' ,74.42725,13.43804 ), ('lineTo' ,76.00163,13.43804 ), ('lineTo' ,76.00163,17.39113 ), ('lineTo' ,81.55427,17.39113 ), ('lineTo' ,81.55427,13.43804 ), ('lineTo' ,83.39121,13.43804 ), ('lineTo' ,83.39121,9.42278 ), ('lineTo' ,81.55427,9.42278 ), 'closePath', - ('moveTo' ,95.17333,0 ), ('lineTo' ,85.09024,0 ), ('lineTo' ,85.09024,19.19365 ), ('lineTo' ,90.85002,19.19365 ), ('lineTo' ,90.85002,4.61196 ), ('lineTo' ,95.17333,4.61196 ), 'closePath', - ('moveTo' ,110.00787,0 ), ('lineTo' ,104.45523,0 ), ('curveTo' ,104.5012,0.44754 ,104.53803,0.87433 ,104.56573,1.2804 ), ('curveTo' ,104.59313,1.68651 ,104.62083,2.01385 ,104.64853,2.26246 ), ('curveTo' ,103.69087,0.57182 ,102.40644,-0.27348 ,100.79492,-0.27348 ), ('curveTo' ,99.39527,-0.27348 ,98.28557,0.35637 ,97.46611,1.61605 ), ('curveTo' ,96.65578,2.86746 ,96.25062,4.59952 ,96.25062,6.81227 ), ('curveTo' ,96.25062,8.95041 ,96.66963,10.63276 ,97.50765,11.8593 ), ('curveTo' ,98.34538,13.10242 ,99.4872,13.72396 ,100.93312,13.72396 ), ('curveTo' ,102.41557,13.72396 ,103.61249,12.92008 ,104.52418,11.31231 ), ('curveTo' ,104.50591,11.47806 ,104.49206,11.62309 ,104.48293,11.74741 ), ('curveTo' ,104.4735,11.87173 ,104.46437,11.9753 ,104.45523,12.05819 ), ('lineTo' ,104.39983,12.84135 ), ('lineTo' ,104.35858,13.43804 ), ('lineTo' ,110.00787,13.43804 ), 'closePath', ('moveTo' ,104.39983,6.88685 ), ('curveTo' ,104.39983,7.38409 ,104.37921,7.80676 ,104.33766,8.15481 ), ('curveTo' ,104.29641,8.5029 ,104.22952,8.78672 ,104.13758,9.00636 ), ('curveTo' ,104.04535,9.22598 ,103.92572,9.38341 ,103.77839,9.47874 ), ('curveTo' ,103.63106,9.57403 ,103.45161,9.62168 ,103.23974,9.62168 ), ('curveTo' ,102.30036,9.62168 ,101.83096,8.49875 ,101.83096,6.25285 ), ('curveTo' ,101.83096,4.64508 ,102.24967,3.8412 ,103.0877,3.8412 ), ('curveTo' ,103.96255,3.8412 ,104.39983,4.85641 ,104.39983,6.88685 ), 'closePath', - ('moveTo' ,118.22604,0 ), ('lineTo' ,112.5629,0 ), ('lineTo' ,112.5629,20.99616 ), ('lineTo' ,118.10169,20.99616 ), ('lineTo' ,118.10169,13.63694 ), ('curveTo' ,118.10169,13.01538 ,118.07399,12.30268 ,118.01889,11.49877 ), ('curveTo' ,118.52542,12.31096 ,119.03636,12.88693 ,119.55202,13.22671 ), ('curveTo' ,120.08625,13.55821 ,120.75838,13.72396 ,121.5687,13.72396 ), ('curveTo' ,123.07885,13.72396 ,124.24837,13.09827 ,125.07697,11.84686 ), ('curveTo' ,125.90586,10.60373 ,126.32015,8.85099 ,126.32015,6.5885 ), ('curveTo' ,126.32015,4.42546 ,125.89201,2.74314 ,125.03571,1.54147 ), ('curveTo' ,124.18826,0.3315 ,123.01432,-0.27348 ,121.51331,-0.27348 ), ('curveTo' ,120.78608,-0.27348 ,120.16905,-0.12432 ,119.66252,0.17403 ), ('curveTo' ,119.41383,0.3315 ,119.15835,0.54283 ,118.8961,0.80803 ), ('curveTo' ,118.63356,1.07322 ,118.36866,1.40472 ,118.10169,1.80252 ), ('curveTo' ,118.11112,1.64505 ,118.12025,1.51039 ,118.12939,1.3985 ), ('curveTo' ,118.13852,1.28662 ,118.14766,1.19339 ,118.15709,1.11881 ), 'closePath', ('moveTo' ,120.58806,6.70038 ), ('curveTo' ,120.58806,8.62306 ,120.11837,9.5844 ,119.17898,9.5844 ), ('curveTo' ,118.35039,9.5844 ,117.93609,8.67693 ,117.93609,6.86198 ), ('curveTo' ,117.93609,4.96417 ,118.36424,4.01526 ,119.22053,4.01526 ), ('curveTo' ,120.13222,4.01526 ,120.58806,4.91027 ,120.58806,6.70038 ), 'closePath', - ] + (self.showPage and [ - ('moveTo',38.30626,-7.28346),('lineTo',38.30626,-25.55261),('lineTo',85.15777,-25.55261),('lineTo',85.15777,-1.39019),('lineTo',90.46172,-1.39019),('lineTo',90.46172,-31.15121),('lineTo',32.70766,-31.15121),('lineTo',32.70766,-7.28346), 'closePath', - ('moveTo' ,32.70766,14.52164 ), ('lineTo' ,32.70766,47.81862 ), ('lineTo' ,80.14849,47.81862 ), ('lineTo' ,90.46172,37.21073 ), ('lineTo' ,90.46172,20.12025 ), ('lineTo' ,85.15777,20.12025 ), ('lineTo' ,85.15777,30.72814 ), ('lineTo' ,73.66589,30.72814 ), ('lineTo' ,73.66589,42.22002 ), ('lineTo' ,38.30626,42.22002 ), ('lineTo' ,38.30626,14.52164 ), 'closePath', ('moveTo' ,79.2645,36.32674 ), ('lineTo' ,85.15777,36.32674 ), ('lineTo' ,79.2645,42.22002 ), 'closePath', - ] or []) - g.add(definePath(P,strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, dx=dx, dy=dy)) - - def draw(self): - fillColor = self.fillColor - strokeColor = self.strokeColor - g = Group() - bg = self.background - bd = self.border - bdw = self.borderWidth - shadow = self.shadow - x, y = self.x, self.y - if bg: - if shadow is not None and 0<=shadow<1: - shadow = Color(bg.red*shadow,bg.green*shadow,bg.blue*shadow) - self._paintLogo(g,dy=-2.5, dx=2,fillColor=shadow) - self._paintLogo(g,fillColor=fillColor,strokeColor=strokeColor) - g.skew(kx=self.skewX, ky=self.skewY) - g.shift(self._dx,self._dy) - G = Group() - G.add(g) - _w, _h = 130, 86 - w, h = self.width, self.height - if bg or (bd and bdw): - G.insert(0,Rect(0,0,_w,_h,fillColor=bg,strokeColor=bd,strokeWidth=bdw)) - if w!=_w or h!=_h: G.scale(w/float(_w),h/float(_h)) - - angle = self.angle - if self.angle: - w, h = w/2., h/2. - G.shift(-w,-h) - G.rotate(angle) - G.shift(w,h) - xFlip = getattr(self,'xFlip',0) and -1 or 0 - yFlip = getattr(self,'yFlip',0) and -1 or 0 - if xFlip or yFlip: - sx = xFlip or 1 - sy = yFlip or 1 - G.shift(sx*x+w*xFlip,sy*y+yFlip*h) - G = Group(G,transform=(sx,0,0,sy,0,0)) - else: - G.shift(x,y) - return G - -class RL_CorpLogoReversed(RL_CorpLogo): - def __init__(self): - RL_CorpLogo.__init__(self) - self.background = white - self.fillColor = ReportLabBlue - -class RL_CorpLogoThin(Widget): - """The ReportLab Logo. - - New version created by John Precedo on 7-8 August 2001. - Based on bitmapped imaged from E-Id. - Improved by Robin Becker.""" - - _attrMap = AttrMap( - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - height = AttrMapValue(isNumberOrNone), - width = AttrMapValue(isNumberOrNone), - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue( isColorOrNone) - ) - - _h = 90.5 - _w = 136.5 - _text='R e p o r t L a b' - _fontName = 'Helvetica-Bold' - _fontSize = 16 - - def __init__(self): - self.fillColor = ReportLabBlue - self.strokeColor = white - self.x = 0 - self.y = 0 - self.height = self._h - self.width = self._w - - def demo(self): - D = Drawing(self.width, self.height) - D.add(self) - return D - - def _getText(self, x=0, y=0, color=None): - return String(x,y, self._text, fontName=self._fontName, fontSize=self._fontSize, fillColor=color) - - def _sw(self,f=None,l=None): - text = self._text - if f is None: f = 0 - if l is None: l = len(text) - return stringWidth(text[f:l],self._fontName,self._fontSize) - - def _addPage(self, g, strokeWidth=3, color=None, dx=0, dy=0): - x1, x2 = 31.85+dx, 80.97+dx - fL = 10 # fold length - y1, y2 = dy-34, dy+50.5 - L = [[x1,dy-4,x1,y1, x2, y1, x2, dy-1], - [x1,dy+11,x1,y2,x2-fL,y2,x2,y2-fL,x2,dy+14], - [x2-10,y2,x2-10,y2-fL,x2,y2-fL]] - - for l in L: - g.add(PolyLine(l, strokeWidth=strokeWidth, strokeColor=color, strokeLineJoin=0)) - - def draw(self): - sx = 0.5 - fillColor = self.fillColor - strokeColor = self.strokeColor - shadow = Color(fillColor.red*sx,fillColor.green*sx,fillColor.blue*sx) - g = Group() - g2= Group() - g.add(Rect(fillColor=fillColor, strokeColor=fillColor, x=0, y=0, width=self._w, height=self._h)) - sx = (self._w-2)/self._sw() - g2.scale(sx,1) - self._addPage(g2,strokeWidth=3,dx=2,dy=-2.5,color=shadow) - self._addPage(g2,strokeWidth=3,color=strokeColor) - g2.scale(1/sx,1) - g2.add(self._getText(x=1,y=0,color=shadow)) - g2.add(self._getText(x=0,y=1,color=strokeColor)) - g2.scale(sx,1) - g2.skew(kx=10, ky=0) - g2.shift(0,38) - g.add(g2) - g.scale(self.width/self._w,self.height/self._h) - g.shift(self.x,self.y) - return g - -class ReportLabLogo: - """vector reportlab logo centered in a 250x by 150y rectangle""" - - def __init__(self, atx=0, aty=0, width=2.5*inch, height=1.5*inch, powered_by=0): - self.origin = (atx, aty) - self.dimensions = (width, height) - self.powered_by = powered_by - - def draw(self, canvas): - from reportlab.graphics import renderPDF - canvas.saveState() - (atx,aty) = self.origin - (width, height) = self.dimensions - logo = RL_CorpLogo() - logo.width, logo.height = width, height - renderPDF.draw(logo.demo(),canvas,atx,aty,0) - canvas.restoreState() - -class RL_BusinessCard(Widget): - """Widget that creates a single business card. - Uses RL_CorpLogo for the logo. - - For a black border around your card, set self.border to 1. - To change the details on the card, over-ride the following properties: - self.name, self.position, self.telephone, self.mobile, self.fax, self.email, self.web - The office locations are set in self.rh_blurb_top ("London office" etc), and - self.rh_blurb_bottom ("New York office" etc). - """ - # for items where it 'isString' the string can be an empty one... - _attrMap = AttrMap( - fillColor = AttrMapValue(isColorOrNone), - strokeColor = AttrMapValue(isColorOrNone), - altStrokeColor = AttrMapValue(isColorOrNone), - x = AttrMapValue(isNumber), - y = AttrMapValue(isNumber), - height = AttrMapValue(isNumber), - width = AttrMapValue(isNumber), - borderWidth = AttrMapValue(isNumber), - bleed=AttrMapValue(isNumberOrNone), - cropMarks=AttrMapValue(isBoolean), - border=AttrMapValue(isBoolean), - name=AttrMapValue(isString), - position=AttrMapValue(isString), - telephone=AttrMapValue(isString), - mobile=AttrMapValue(isString), - fax=AttrMapValue(isString), - email=AttrMapValue(isString), - web=AttrMapValue(isString), - rh_blurb_top=AttrMapValue(isListOfStringsOrNone), - rh_blurb_bottom=AttrMapValue(isListOfStringsOrNone) - ) - - _h = 5.35*cm - _w = 8.5*cm - _fontName = 'Helvetica-Bold' - _strapline = "strategic reporting solutions for e-business" - - - def __init__(self): - self.fillColor = ReportLabBlue - self.strokeColor = black - self.altStrokeColor = white - self.x = 0 - self.y = 0 - self.height = self._h - self.width = self._w - self.borderWidth = self.width/6.15 - self.bleed=0.2*cm - self.cropMarks=1 - self.border=0 - #Over-ride these with your own info - self.name="Joe Cool" - self.position="Freelance Demonstrator" - self.telephone="020 8545 7271" - self.mobile="-" - self.fax="020 8544 1311" - self.email="info@reportlab.com" - self.web="www.reportlab.com" - self.rh_blurb_top = ["London office:", - "ReportLab Europe Ltd", - "Lombard Business Park", - "8 Lombard Road", - "Wimbledon", - "London SW19 3TZ", - "United Kingdom"] - self.rh_blurb_bottom = ["New York office:", - "ReportLab Inc", - "219 Harper Street", - "Highland Park", - "New Jersey 08904", - "USA"] - - def demo(self): - D = Drawing(self.width, self.height) - D.add(self) - return D - - def draw(self): - fillColor = self.fillColor - strokeColor = self.strokeColor - - g = Group() - g.add(Rect(x = 0, y = 0, - fillColor = self.fillColor, - strokeColor = self.fillColor, - width = self.borderWidth, - height = self.height)) - g.add(Rect(x = 0, y = self.height-self.borderWidth, - fillColor = self.fillColor, - strokeColor = self.fillColor, - width = self.width, - height = self.borderWidth)) - - g2 = Group() - rl=RL_CorpLogo() - rl.height = 1.25*cm - rl.width = 1.9*cm - rl.draw() - g2.add(rl) - g.add(g2) - g2.shift(x=(self.width-(rl.width+(self.width/42))), - y=(self.height - (rl.height+(self.height/42)))) - - g.add(String(x = self.borderWidth/5.0, - y = ((self.height - (rl.height+(self.height/42)))+((38/90.5)*rl.height)), - fontSize = 6, - fillColor = self.altStrokeColor, - fontName = "Helvetica-BoldOblique", - textAnchor = 'start', - text = self._strapline)) - - leftText=["Tel:", "Mobile:", "Fax:", "Email:", "Web:"] - leftDetails=[self.telephone,self.mobile,self.fax,self.email,self.web] - leftText.reverse() - leftDetails.reverse() - for f in range(len(leftText),0,-1): - g.add(String(x = self.borderWidth+(self.borderWidth/5.0), - y = (self.borderWidth/5.0)+((f-1)*(5*1.2)), - fontSize = 5, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'start', - text = leftText[f-1])) - g.add(String(x = self.borderWidth+(self.borderWidth/5.0)+self.borderWidth, - y = (self.borderWidth/5.0)+((f-1)*(5*1.2)), - fontSize = 5, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'start', - text = leftDetails[f-1])) - - rightText=self.rh_blurb_bottom - rightText.reverse() - for f in range(len(rightText),0,-1): - g.add(String(x = self.width-((self.borderWidth/5.0)), - y = (self.borderWidth/5.0)+((f-1)*(5*1.2)), - fontSize = 5, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'end', - text = rightText[f-1])) - - ty = (self.height-self.borderWidth-(self.borderWidth/5.0)+2) -# g.add(Line(self.borderWidth, ty, self.borderWidth+(self.borderWidth/5.0), ty)) -# g.add(Line(self.borderWidth+(self.borderWidth/5.0), ty, self.borderWidth+(self.borderWidth/5.0), -# ty+(self.borderWidth/5.0))) -# g.add(Line(self.borderWidth, ty-10, -# self.borderWidth+(self.borderWidth/5.0), ty-10)) - - rightText=self.rh_blurb_top - for f in range(1,(len(rightText)+1)): - g.add(String(x = self.width-(self.borderWidth/5.0), - y = ty-((f)*(5*1.2)), - fontSize = 5, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'end', - text = rightText[f-1])) - - g.add(String(x = self.borderWidth+(self.borderWidth/5.0), - y = ty-10, - fontSize = 10, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'start', - text = self.name)) - - ty1 = ty-10*1.2 - - g.add(String(x = self.borderWidth+(self.borderWidth/5.0), - y = ty1-8, - fontSize = 8, - fillColor = self.strokeColor, - fontName = "Helvetica", - textAnchor = 'start', - text = self.position)) - if self.border: - g.add(Rect(x = 0, y = 0, - fillColor=None, - strokeColor = black, - width = self.width, - height = self.height)) - g.shift(self.x,self.y) - return g - - -def test(): - """This function produces a pdf with examples. """ - - #white on blue - rl = RL_CorpLogo() - rl.width = 129 - rl.height = 86 - D = Drawing(rl.width,rl.height) - D.add(rl) - D.__dict__['verbose'] = 1 - D.save(fnRoot='corplogo_whiteonblue',formats=['pdf','eps','jpg','gif']) - - - #blue on white - rl = RL_CorpLogoReversed() - rl.width = 129 - rl.height = 86 - D = Drawing(rl.width,rl.height) - D.add(rl) - D.__dict__['verbose'] = 1 - D.save(fnRoot='corplogo_blueonwhite',formats=['pdf','eps','jpg','gif']) - - #gray on white - rl = RL_CorpLogoReversed() - rl.fillColor = Color(0.2, 0.2, 0.2) - rl.width = 129 - rl.height = 86 - D = Drawing(rl.width,rl.height) - D.add(rl) - D.__dict__['verbose'] = 1 - D.save(fnRoot='corplogo_grayonwhite',formats=['pdf','eps','jpg','gif']) - - - rl = RL_BusinessCard() - rl.x=25 - rl.y=25 - rl.border=1 - D = Drawing(rl.width+50,rl.height+50) - D.add(rl) - D.__dict__['verbose'] = 1 - D.save(fnRoot='RL_BusinessCard',formats=['pdf']) - -if __name__=='__main__': - test() diff --git a/bin/reportlab/lib/enums.py b/bin/reportlab/lib/enums.py deleted file mode 100644 index f28897a929c..00000000000 --- a/bin/reportlab/lib/enums.py +++ /dev/null @@ -1,11 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/enums.py -__version__=''' $Id: enums.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__=""" -holder for all reportlab's enumerated types -""" -TA_LEFT = 0 -TA_CENTER = 1 -TA_RIGHT = 2 -TA_JUSTIFY = 4 \ No newline at end of file diff --git a/bin/reportlab/lib/extformat.py b/bin/reportlab/lib/extformat.py deleted file mode 100644 index f3450596081..00000000000 --- a/bin/reportlab/lib/extformat.py +++ /dev/null @@ -1,81 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/extformat.py -from tokenize import tokenprog -import sys - -def _matchorfail(text, pos): - match = tokenprog.match(text, pos) - if match is None: raise ValueError(text, pos) - return match, match.end() - -''' - Extended dictionary formatting - We allow expressions in the parentheses instead of - just a simple variable. -''' -def dictformat(_format, L={}, G={}): - format = _format - - S = {} - chunks = [] - pos = 0 - n = 0 - - while 1: - pc = format.find("%", pos) - if pc < 0: break - nextchar = format[pc+1] - - if nextchar == "(": - chunks.append(format[pos:pc]) - pos, level = pc+2, 1 - while level: - match, pos = _matchorfail(format, pos) - tstart, tend = match.regs[3] - token = format[tstart:tend] - if token == "(": level = level+1 - elif token == ")": level = level-1 - vname = '__superformat_%d' % n - n += 1 - S[vname] = eval(format[pc+2:pos-1],L,G) - chunks.append('%%(%s)' % vname) - else: - nc = pc+1+(nextchar=="%") - chunks.append(format[pos:nc]) - pos = nc - - if pos < len(format): chunks.append(format[pos:]) - return (''.join(chunks)) % S - -def magicformat(format): - """Evaluate and substitute the appropriate parts of the string.""" - try: 1/0 - except: frame = sys.exc_traceback.tb_frame - while frame.f_globals["__name__"] == __name__: frame = frame.f_back - return dictformat(format,frame.f_locals, frame.f_globals) - -if __name__=='__main__': - from reportlab.lib.formatters import DecimalFormatter - _DF={} - def df(n,dp=2,ds='.',ts=','): - try: - _df = _DF[dp,ds] - except KeyError: - _df = _DF[dp,ds] = DecimalFormatter(places=dp,decimalSep=ds,thousandSep=ts) - return _df(n) - - from reportlab.lib.extformat import magicformat - - Z={'abc': ('ab','c')} - x = 300000.23 - percent=79.2 - class dingo: - a=3 - 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 -''') diff --git a/bin/reportlab/lib/fonts.py b/bin/reportlab/lib/fonts.py deleted file mode 100644 index 7793530fbc8..00000000000 --- a/bin/reportlab/lib/fonts.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/fonts.py -__version__=''' $Id: fonts.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -import string, sys, os -############################################################################### -# A place to put useful font stuff -############################################################################### -# -# Font Mappings -# The brute force approach to finding the correct postscript font name; -# much safer than the rule-based ones we tried. -# preprocessor to reduce font face names to the shortest list -# possible. Add any aliases you wish; it keeps looking up -# until it finds no more translations to do. Any input -# will be lowercased before checking. -_family_alias = { - 'serif':'times', - 'sansserif':'helvetica', - 'monospaced':'courier', - 'arial':'helvetica' - } -#maps a piddle font to a postscript one. -_tt2ps_map = { - #face, bold, italic -> ps name - ('times', 0, 0) :'Times-Roman', - ('times', 1, 0) :'Times-Bold', - ('times', 0, 1) :'Times-Italic', - ('times', 1, 1) :'Times-BoldItalic', - - ('courier', 0, 0) :'Courier', - ('courier', 1, 0) :'Courier-Bold', - ('courier', 0, 1) :'Courier-Oblique', - ('courier', 1, 1) :'Courier-BoldOblique', - - ('helvetica', 0, 0) :'Helvetica', - ('helvetica', 1, 0) :'Helvetica-Bold', - ('helvetica', 0, 1) :'Helvetica-Oblique', - ('helvetica', 1, 1) :'Helvetica-BoldOblique', - - - # there is only one Symbol font - ('symbol', 0, 0) :'Symbol', - ('symbol', 1, 0) :'Symbol', - ('symbol', 0, 1) :'Symbol', - ('symbol', 1, 1) :'Symbol', - - # ditto for dingbats - ('zapfdingbats', 0, 0) :'ZapfDingbats', - ('zapfdingbats', 1, 0) :'ZapfDingbats', - ('zapfdingbats', 0, 1) :'ZapfDingbats', - ('zapfdingbats', 1, 1) :'ZapfDingbats', - - - } - -_ps2tt_map={} -for k,v in _tt2ps_map.items(): - if not _ps2tt_map.has_key(k): - _ps2tt_map[string.lower(v)] = k - -def ps2tt(psfn): - 'ps fontname to family name, bold, italic' - psfn = string.lower(psfn) - if _ps2tt_map.has_key(psfn): - return _ps2tt_map[psfn] - raise ValueError, "Can't map determine family/bold/italic for %s" % psfn - -def tt2ps(fn,b,i): - 'family name + bold & italic to ps font name' - K = (string.lower(fn),b,i) - if _tt2ps_map.has_key(K): - return _tt2ps_map[K] - else: - fn, b1, i1 = ps2tt(K[0]) - K = fn, b1|b, i1|i - if _tt2ps_map.has_key(K): - return _tt2ps_map[K] - raise ValueError, "Can't find concrete font for family=%s, bold=%d, italic=%d" % (fn, b, i) - -def addMapping(face, bold, italic, psname): - 'allow a custom font to be put in the mapping' - k = (string.lower(face), bold, italic) - _tt2ps_map[k] = psname - # rebuild inverse - inefficient - for k,v in _tt2ps_map.items(): - if not _ps2tt_map.has_key(k): - _ps2tt_map[string.lower(v)] = k diff --git a/bin/reportlab/lib/formatters.py b/bin/reportlab/lib/formatters.py deleted file mode 100644 index 19230f3c375..00000000000 --- a/bin/reportlab/lib/formatters.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/formatters.py -__version__=''' $Id: formatters.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__=""" -These help format numbers and dates in a user friendly way. - -Used by the graphics framework. -""" -import string, sys, os - -class Formatter: - "Base formatter - simply applies python format strings" - def __init__(self, pattern): - self.pattern = pattern - def format(self, obj): - return self.pattern % obj - def __repr__(self): - return "%s('%s')" % (self.__class__.__name__, self.pattern) - def __call__(self, x): - return self.format(x) - - -class DecimalFormatter(Formatter): - """lets you specify how to build a decimal. - - A future NumberFormatter class will take Microsoft-style patterns - instead - "$#,##0.00" is WAY easier than this.""" - def __init__(self, places=2, decimalSep='.', thousandSep=None, prefix=None, suffix=None): - self.places = places - self.dot = decimalSep - self.comma = thousandSep - self.prefix = prefix - self.suffix = suffix - - def format(self, num): - # positivize the numbers - sign=num<0 - if sign: - num = -num - places, sep = self.places, self.dot - strip = places<=0 - if places and strip: places = -places - strInt = ('%.' + str(places) + 'f') % num - if places: - strInt, strFrac = strInt.split('.') - strFrac = sep + strFrac - if strip: - while strFrac and strFrac[-1] in ['0',sep]: strFrac = strFrac[:-1] - else: - strFrac = '' - - if self.comma is not None: - strNew = '' - while strInt: - left, right = strInt[0:-3], strInt[-3:] - if left == '': - #strNew = self.comma + right + strNew - strNew = right + strNew - else: - strNew = self.comma + right + strNew - strInt = left - strInt = strNew - - strBody = strInt + strFrac - if sign: strBody = '-' + strBody - if self.prefix: - strBody = self.prefix + strBody - if self.suffix: - strBody = strBody + self.suffix - return strBody - - def __repr__(self): - return "%s(places=%d, decimalSep=%s, thousandSep=%s, prefix=%s, suffix=%s)" % ( - self.__class__.__name__, - self.places, - repr(self.dot), - repr(self.comma), - repr(self.prefix), - repr(self.suffix) - ) - -if __name__=='__main__': - 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') - t(1000.9,'1,000.9',1,thousandSep=',') - t(1000.95,'1,001.0',1,thousandSep=',') - t(1000.95,'1,001',-1,thousandSep=',') - t(1000.9,'1,001',0,thousandSep=',') - t(1000.9,'1000.9',1) - t(1000.95,'1001.0',1) - t(1000.95,'1001',-1) - t(1000.9,'1001',0) - t(1000.1,'1000.1',1) - t(1000.55,'1000.6',1) - t(1000.449,'1000.4',-1) - t(1000.45,'1000',0) \ No newline at end of file diff --git a/bin/reportlab/lib/logger.py b/bin/reportlab/lib/logger.py deleted file mode 100644 index 35555873a3e..00000000000 --- a/bin/reportlab/lib/logger.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/logger.py -__version__=''' $Id: logger.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from sys import stderr -class Logger: - ''' - An extended file type thing initially equivalent to sys.stderr - You can add/remove file type things; it has a write method - ''' - def __init__(self): - self._fps = [stderr] - self._fns = {} - - def add(self,fp): - '''add the file/string fp to the destinations''' - if type(fp) is StringType: - if fp in self._fns: return - fp = open(fn,'wb') - self._fns[fn] = fp - self._fps.append(fp) - - def remove(self,fp): - '''remove the file/string fp from the destinations''' - if type(fp) is StringType: - if fp not in self._fns: return - fn = fp - fp = self._fns[fn] - del self.fns[fn] - if fp in self._fps: - del self._fps[self._fps.index(fp)] - - def write(self,text): - '''write text to all the destinations''' - if text[-1]!='\n': text=text+'\n' - map(lambda fp,t=text: fp.write(t),self._fps) - - def __call__(self,text): - self.write(text) - -logger=Logger() - -class WarnOnce: - - def __init__(self,kind='Warn'): - self.uttered = {} - self.pfx = '%s: '%kind - self.enabled = 1 - - def once(self,warning): - if not self.uttered.has_key(warning): - if self.enabled: logger.write(self.pfx + warning) - self.uttered[warning] = 1 - - def __call__(self,warning): - self.once(warning) - -warnOnce=WarnOnce() -infoOnce=WarnOnce('Info') \ No newline at end of file diff --git a/bin/reportlab/lib/normalDate.py b/bin/reportlab/lib/normalDate.py deleted file mode 100644 index efcea715013..00000000000 --- a/bin/reportlab/lib/normalDate.py +++ /dev/null @@ -1,605 +0,0 @@ -#!/usr/bin/env python -# normalDate.py - version 1.0 - 20000717 -#hacked by Robin Becker 10/Apr/2001 -#major changes include -# using Types instead of type(0) etc -# BusinessDate class -# __radd__, __rsub__ methods -# formatMS stuff - -# derived from an original version created -# by Jeff Bauer of Rubicon Research and used -# with his kind permission -__version__=''' $Id: normalDate.py 2742 2005-12-12 11:58:08Z oualid $ ''' - - - -_bigBangScalar = -4345732 # based on (-9999, 1, 1) BC/BCE minimum -_bigCrunchScalar = 2958463 # based on (9999,12,31) AD/CE maximum -_daysInMonthNormal = [31,28,31,30,31,30,31,31,30,31,30,31] -_daysInMonthLeapYear = [31,29,31,30,31,30,31,31,30,31,30,31] -_dayOfWeekName = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', - 'Friday', 'Saturday', 'Sunday'] -_monthName = ['January', 'February', 'March', 'April', 'May', 'June', - 'July','August','September','October','November','December'] - -from types import IntType, StringType, ListType, TupleType -import string, re, time, datetime -if hasattr(time,'struct_time'): - _DateSeqTypes = (ListType,TupleType,time.struct_time) -else: - _DateSeqTypes = (ListType,TupleType) - -_fmtPat = re.compile('\\{(m{1,5}|yyyy|yy|d{1,4})\\}',re.MULTILINE|re.IGNORECASE) -_iso_re = re.compile(r'(\d\d\d\d|\d\d)-(\d\d)-(\d\d)') - -def getStdMonthNames(): - return map(string.lower,_monthName) - -def getStdShortMonthNames(): - return map(lambda x: x[:3],getStdMonthNames()) - -def getStdDayNames(): - return map(string.lower,_dayOfWeekName) - -def getStdShortDayNames(): - return map(lambda x: x[:3],getStdDayNames()) - -def isLeapYear(year): - """determine if specified year is leap year, returns Python boolean""" - if year < 1600: - if year % 4: - return 0 - else: - return 1 - elif year % 4 != 0: - return 0 - elif year % 100 != 0: - return 1 - elif year % 400 != 0: - return 0 - else: - return 1 - -class NormalDateException(Exception): - """Exception class for NormalDate""" - pass - -class NormalDate: - """ - NormalDate is a specialized class to handle dates without - all the excess baggage (time zones, daylight savings, leap - seconds, etc.) of other date structures. The minimalist - strategy greatly simplifies its implementation and use. - - Internally, NormalDate is stored as an integer with values - in a discontinuous range of -99990101 to 99991231. The - integer value is used principally for storage and to simplify - the user interface. Internal calculations are performed by - a scalar based on Jan 1, 1900. - - Valid NormalDate ranges include (-9999,1,1) B.C.E. through - (9999,12,31) C.E./A.D. - - 1.0 - No changes, except the version number. After 3 years of use - by various parties I think we can consider it stable. - 0.8 - added Prof. Stephen Walton's suggestion for a range method - - module author resisted the temptation to use lambda <0.5 wink> - 0.7 - added Dan Winkler's suggestions for __add__, __sub__ methods - 0.6 - modifications suggested by Kevin Digweed to fix: - - dayOfWeek, dayOfWeekAbbrev, clone methods - - permit NormalDate to be a better behaved superclass - 0.5 - minor tweaking - 0.4 - added methods __cmp__, __hash__ - - added Epoch variable, scoped to the module - - added setDay, setMonth, setYear methods - 0.3 - minor touch-ups - 0.2 - fixed bug for certain B.C.E leap years - - added Jim Fulton's suggestions for short alias class name =ND - and __getstate__, __setstate__ methods - - Special thanks: Roedy Green - """ - def __init__(self, normalDate=None): - """ - Accept 1 of 4 values to initialize a NormalDate: - 1. None - creates a NormalDate for the current day - 2. integer in yyyymmdd format - 3. string in yyyymmdd format - 4. tuple in (yyyy, mm, dd) - localtime/gmtime can also be used - """ - if normalDate is None: - self.setNormalDate(time.localtime(time.time())) - else: - self.setNormalDate(normalDate) - - def add(self, days): - """add days to date; use negative integers to subtract""" - if not type(days) is IntType: - raise NormalDateException( \ - 'add method parameter must be integer type') - self.normalize(self.scalar() + days) - - def __add__(self, days): - """add integer to normalDate and return a new, calculated value""" - if not type(days) is IntType: - raise NormalDateException( \ - '__add__ parameter must be integer type') - cloned = self.clone() - cloned.add(days) - return cloned - - def __radd__(self,days): - '''for completeness''' - return self.__add__(days) - - def clone(self): - """return a cloned instance of this normalDate""" - return self.__class__(self.normalDate) - - def __cmp__(self, target): - if target is None: - return 1 - elif not hasattr(target, 'normalDate'): - return 1 - else: - return cmp(self.normalDate, target.normalDate) - - def day(self): - """return the day as integer 1-31""" - return int(repr(self.normalDate)[-2:]) - - def dayOfWeek(self): - """return integer representing day of week, Mon=0, Tue=1, etc.""" - return apply(dayOfWeek, self.toTuple()) - - def dayOfWeekAbbrev(self): - """return day of week abbreviation for current date: Mon, Tue, etc.""" - return _dayOfWeekName[self.dayOfWeek()][:3] - - def dayOfWeekName(self): - """return day of week name for current date: Monday, Tuesday, etc.""" - return _dayOfWeekName[self.dayOfWeek()] - - def dayOfYear(self): - """day of year""" - if self.isLeapYear(): - daysByMonth = _daysInMonthLeapYear - else: - daysByMonth = _daysInMonthNormal - priorMonthDays = 0 - for m in xrange(self.month() - 1): - priorMonthDays = priorMonthDays + daysByMonth[m] - return self.day() + priorMonthDays - - def daysBetweenDates(self, normalDate): - """ - return value may be negative, since calculation is - self.scalar() - arg - """ - if type(normalDate) is _NDType: - return self.scalar() - normalDate.scalar() - else: - return self.scalar() - NormalDate(normalDate).scalar() - - def equals(self, target): - if type(target) is _NDType: - if target is None: - return self.normalDate is None - else: - return self.normalDate == target.normalDate - else: - return 0 - - def endOfMonth(self): - """returns (cloned) last day of month""" - return self.__class__(self.__repr__()[-8:-2]+str(self.lastDayOfMonth())) - - def firstDayOfMonth(self): - """returns (cloned) first day of month""" - return self.__class__(self.__repr__()[-8:-2]+"01") - - def formatUS(self): - """return date as string in common US format: MM/DD/YY""" - d = self.__repr__() - return "%s/%s/%s" % (d[-4:-2], d[-2:], d[-6:-4]) - - def formatUSCentury(self): - """return date as string in 4-digit year US format: MM/DD/YYYY""" - d = self.__repr__() - return "%s/%s/%s" % (d[-4:-2], d[-2:], d[-8:-4]) - - def _fmtM(self): - return str(self.month()) - - def _fmtMM(self): - return '%02d' % self.month() - - def _fmtMMM(self): - return self.monthAbbrev() - - def _fmtMMMM(self): - return self.monthName() - - def _fmtMMMMM(self): - return self.monthName()[0] - - def _fmtD(self): - return str(self.day()) - - def _fmtDD(self): - return '%02d' % self.day() - - def _fmtDDD(self): - return self.dayOfWeekAbbrev() - - def _fmtDDDD(self): - return self.dayOfWeekName() - - def _fmtYY(self): - return '%02d' % (self.year()%100) - - def _fmtYYYY(self): - return str(self.year()) - - def formatMS(self,fmt): - '''format like MS date using the notation - {YY} --> 2 digit year - {YYYY} --> 4 digit year - {M} --> month as digit - {MM} --> 2 digit month - {MMM} --> abbreviated month name - {MMMM} --> monthname - {MMMMM} --> first character of monthname - {D} --> day of month as digit - {DD} --> 2 digit day of month - {DDD} --> abrreviated weekday name - {DDDD} --> weekday name - ''' - r = fmt[:] - f = 0 - while 1: - m = _fmtPat.search(r,f) - if m: - y = getattr(self,'_fmt'+string.upper(m.group()[1:-1]))() - i, j = m.span() - r = (r[0:i] + y) + r[j:] - f = i + len(y) - else: - return r - - def __getstate__(self): - """minimize persistent storage requirements""" - return self.normalDate - - def __hash__(self): - return hash(self.normalDate) - - def __int__(self): - return self.normalDate - - def isLeapYear(self): - """ - determine if specified year is leap year, returning true (1) or - false (0) - """ - return isLeapYear(self.year()) - - def _isValidNormalDate(self, normalDate): - """checks for date validity in [-]yyyymmdd format""" - if type(normalDate) is not IntType: - return 0 - if len(repr(normalDate)) > 9: - return 0 - if normalDate < 0: - dateStr = "%09d" % normalDate - else: - dateStr = "%08d" % normalDate - if len(dateStr) < 8: - return 0 - elif len(dateStr) == 9: - if (dateStr[0] != '-' and dateStr[0] != '+'): - return 0 - year = int(dateStr[:-4]) - if year < -9999 or year > 9999 or year == 0: - return 0 # note: zero (0) is not a valid year - month = int(dateStr[-4:-2]) - if month < 1 or month > 12: - return 0 - if isLeapYear(year): - maxDay = _daysInMonthLeapYear[month - 1] - else: - maxDay = _daysInMonthNormal[month - 1] - day = int(dateStr[-2:]) - if day < 1 or day > maxDay: - return 0 - if year == 1582 and month == 10 and day > 4 and day < 15: - return 0 # special case of 10 days dropped: Oct 5-14, 1582 - return 1 - - def lastDayOfMonth(self): - """returns last day of the month as integer 28-31""" - if self.isLeapYear(): - return _daysInMonthLeapYear[self.month() - 1] - else: - return _daysInMonthNormal[self.month() - 1] - - def localeFormat(self): - """override this method to use your preferred locale format""" - return self.formatUS() - - def month(self): - """returns month as integer 1-12""" - return int(repr(self.normalDate)[-4:-2]) - - def monthAbbrev(self): - """returns month as a 3-character abbreviation, i.e. Jan, Feb, etc.""" - return _monthName[self.month() - 1][:3] - - def monthName(self): - """returns month name, i.e. January, February, etc.""" - return _monthName[self.month() - 1] - - def normalize(self, scalar): - """convert scalar to normalDate""" - if scalar < _bigBangScalar: - msg = "normalize(%d): scalar below minimum" % \ - _bigBangScalar - raise NormalDateException(msg) - if scalar > _bigCrunchScalar: - msg = "normalize(%d): scalar exceeds maximum" % \ - _bigCrunchScalar - raise NormalDateException(msg) - from math import floor - if scalar >= -115860: - year = 1600 + int(floor((scalar + 109573) / 365.2425)) - elif scalar >= -693597: - year = 4 + int(floor((scalar + 692502) / 365.2425)) - else: - year = -4 + int(floor((scalar + 695058) / 365.2425)) - days = scalar - firstDayOfYear(year) + 1 - if days <= 0: - year = year - 1 - days = scalar - firstDayOfYear(year) + 1 - daysInYear = 365 - if isLeapYear(year): - daysInYear = daysInYear + 1 - if days > daysInYear: - year = year + 1 - days = scalar - firstDayOfYear(year) + 1 - # add 10 days if between Oct 15, 1582 and Dec 31, 1582 - if (scalar >= -115860 and scalar <= -115783): - days = days + 10 - if isLeapYear(year): - daysByMonth = _daysInMonthLeapYear - else: - daysByMonth = _daysInMonthNormal - dc = 0; month = 12 - for m in xrange(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): - priorMonthDays = priorMonthDays + daysByMonth[m] - day = days - priorMonthDays - self.setNormalDate((year, month, day)) - - def range(self, days): - """Return a range of normalDates as a list. Parameter - may be an int or normalDate.""" - if type(days) is not IntType: - days = days - self # if not int, assume arg is normalDate type - r = [] - for i in range(days): - r.append(self + i) - return r - - def __repr__(self): - """print format: [-]yyyymmdd""" - # Note: When disassembling a NormalDate string, be sure to - # count from the right, i.e. epochMonth = int(`Epoch`[-4:-2]), - # or the slice won't work for dates B.C. - if self.normalDate < 0: - return "%09d" % self.normalDate - else: - return "%08d" % self.normalDate - - def scalar(self): - """days since baseline date: Jan 1, 1900""" - (year, month, day) = self.toTuple() - days = firstDayOfYear(year) + day - 1 - if self.isLeapYear(): - for m in xrange(month - 1): - days = days + _daysInMonthLeapYear[m] - else: - for m in xrange(month - 1): - days = days + _daysInMonthNormal[m] - if year == 1582: - if month > 10 or (month == 10 and day > 4): - days = days - 10 - return days - - def setDay(self, day): - """set the day of the month""" - maxDay = self.lastDayOfMonth() - if day < 1 or day > maxDay: - msg = "day is outside of range 1 to %d" % maxDay - raise NormalDateException(msg) - (y, m, d) = self.toTuple() - self.setNormalDate((y, m, day)) - - def setMonth(self, month): - """set the month [1-12]""" - if month < 1 or month > 12: - raise NormalDateException('month is outside range 1 to 12') - (y, m, d) = self.toTuple() - self.setNormalDate((y, month, d)) - - def setNormalDate(self, normalDate): - """ - accepts date as scalar string/integer (yyyymmdd) or tuple - (year, month, day, ...)""" - tn=type(normalDate) - if tn is IntType: - self.normalDate = normalDate - elif tn is StringType: - try: - self.normalDate = int(normalDate) - except: - m = _iso_re.match(normalDate) - if m: - self.setNormalDate(m.group(1)+m.group(2)+m.group(3)) - else: - raise NormalDateException("unable to setNormalDate(%s)" % `normalDate`) - elif tn in _DateSeqTypes: - self.normalDate = int("%04d%02d%02d" % normalDate[:3]) - elif tn is _NDType: - self.normalDate = normalDate.normalDate - elif isinstance(normalDate,(datetime.datetime,datetime.date)): - self.normalDate = (normalDate.year*100+normalDate.month)*100+normalDate.day - if not self._isValidNormalDate(self.normalDate): - raise NormalDateException("unable to setNormalDate(%s)" % `normalDate`) - - def setYear(self, year): - if year == 0: - raise NormalDateException('cannot set year to zero') - elif year < -9999: - raise NormalDateException('year cannot be less than -9999') - elif year > 9999: - raise NormalDateException('year cannot be greater than 9999') - (y, m, d) = self.toTuple() - self.setNormalDate((year, m, d)) - - __setstate__ = setNormalDate - - def __sub__(self, v): - if type(v) is IntType: - return self.__add__(-v) - return self.scalar() - v.scalar() - - def __rsub__(self,v): - if type(v) is IntType: - return NormalDate(v) - self - else: - return v.scalar() - self.scalar() - - def toTuple(self): - """return date as (year, month, day) tuple""" - return (self.year(), self.month(), self.day()) - - def year(self): - """return year in yyyy format, negative values indicate B.C.""" - return int(repr(self.normalDate)[:-4]) - -################# Utility functions ################# - -def bigBang(): - """return lower boundary as a NormalDate""" - return NormalDate((-9999, 1, 1)) - -def bigCrunch(): - """return upper boundary as a NormalDate""" - return NormalDate((9999, 12, 31)) - -def dayOfWeek(y, m, d): - """return integer representing day of week, Mon=0, Tue=1, etc.""" - if m == 1 or m == 2: - m = m + 12 - y = y - 1 - return (d + 2*m + 3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7 - -def firstDayOfYear(year): - """number of days to the first of the year, relative to Jan 1, 1900""" - if type(year) is not IntType: - msg = "firstDayOfYear() expected integer, got %s" % type(year) - raise NormalDateException(msg) - if year == 0: - raise NormalDateException('first day of year cannot be zero (0)') - elif year < 0: # BCE calculation - firstDay = (year * 365) + int((year - 1) / 4) - 693596 - else: # CE calculation - leapAdjust = int((year + 3) / 4) - if year > 1600: - leapAdjust = leapAdjust - int((year + 99 - 1600) / 100) + \ - int((year + 399 - 1600) / 400) - firstDay = year * 365 + leapAdjust - 693963 - if year > 1582: - firstDay = firstDay - 10 - return firstDay - -def FND(d): - '''convert to ND if required''' - return (type(d) is _NDType) and d or ND(d) - -Epoch=bigBang() -ND=NormalDate -_NDType = type(Epoch) -BDEpoch=ND(15821018) -BDEpochScalar = -115857 - -class BusinessDate(NormalDate): - """ - Specialised NormalDate - """ - def add(self, days): - """add days to date; use negative integers to subtract""" - if not type(days) is IntType: - raise NormalDateException('add method parameter must be integer type') - self.normalize(self.scalar() + days) - - def __add__(self, days): - """add integer to BusinessDate and return a new, calculated value""" - if not type(days) is IntType: - raise NormalDateException('__add__ parameter must be integer type') - cloned = self.clone() - cloned.add(days) - return cloned - - def __sub__(self, v): - return type(v) is IntType and self.__add__(-v) or self.scalar() - v.scalar() - - def asNormalDate(self): - return ND(self.normalDate) - - def daysBetweenDates(self, normalDate): - return self.asNormalDate.daysBetweenDates(normalDate) - - def _checkDOW(self): - if self.dayOfWeek()>4: raise NormalDateException("%s isn't a business day" % `self.normalDate`) - - def normalize(self, i): - i = int(i) - NormalDate.normalize(self,(i/5)*7+i%5+BDEpochScalar) - - def scalar(self): - d = self.asNormalDate() - i = d - BDEpoch #luckily BDEpoch is a Monday so we don't have a problem - #concerning the relative weekday - return 5*(i/7) + i%7 - - def setNormalDate(self, normalDate): - NormalDate.setNormalDate(self,normalDate) - self._checkDOW() - -if __name__ == '__main__': - today = NormalDate() - 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()) - 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}') - b = BusinessDate('20010116') - print 'b=',b,'b.scalar()', b.scalar() diff --git a/bin/reportlab/lib/pagesizes.py b/bin/reportlab/lib/pagesizes.py deleted file mode 100644 index 75fb8ddd588..00000000000 --- a/bin/reportlab/lib/pagesizes.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/pagesizes.py - -"""This module defines a few common page sizes in points (1/72 inch). -To be expanded to include things like label sizes, envelope windows -etc.""" -__version__=''' $Id: pagesizes.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -from reportlab.lib.units import cm, inch - -_W, _H = (21*cm, 29.7*cm) - -A6 = (_W*.5, _H*.5) -A5 = (_H*.5, _W) -A4 = (_W, _H) -A3 = (_H, _W*2) -A2 = (_W*2, _H*2) -A1 = (_H*2, _W*4) -A0 = (_W*4, _H*4) - -LETTER = (8.5*inch, 11*inch) -LEGAL = (8.5*inch, 14*inch) -ELEVENSEVENTEEN = (11*inch, 17*inch) -# lower case is deprecated as of 12/2001, but here -# for compatability -letter=LETTER -legal=LEGAL -elevenSeventeen = ELEVENSEVENTEEN - -_BW, _BH = (25*cm, 35.3*cm) -B6 = (_BW*.5, _BH*.5) -B5 = (_BH*.5, _BW) -B4 = (_BW, _BH) -B3 = (_BH*2, _BW) -B2 = (_BW*2, _BH*2) -B1 = (_BH*4, _BW*2) -B0 = (_BW*4, _BH*4) - -def landscape(pagesize): - """Use this to get page orientation right""" - a, b = pagesize - if a < b: - return (b, a) - else: - return (a, b) - -def portrait(pagesize): - """Use this to get page orientation right""" - a, b = pagesize - if a >= b: - return (b, a) - else: - return (a, b) \ No newline at end of file diff --git a/bin/reportlab/lib/randomtext.py b/bin/reportlab/lib/randomtext.py deleted file mode 100644 index 075b9d711c7..00000000000 --- a/bin/reportlab/lib/randomtext.py +++ /dev/null @@ -1,348 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/randomtext.py - -__version__=''' $Id: randomtext.py 2436 2004-09-11 14:18:33Z rgbecker $ ''' - -############################################################################### -# generates so-called 'Greek Text' for use in filling documents. -############################################################################### -""" -This module exposes a function randomText() which generates paragraphs. -These can be used when testing out document templates and stylesheets. -A number of 'themes' are provided - please contribute more! -We need some real Greek text too. - -There are currently six themes provided: - STARTUP (words suitable for a business plan - or not as the case may be), - COMPUTERS (names of programming languages and operating systems etc), - BLAH (variations on the word 'blah'), - BUZZWORD (buzzword bingo), - STARTREK (Star Trek), - PRINTING (print-related terms) - PYTHON (snippets and quotes from Monty Python) - CHOMSKY (random lingusitic nonsense) - -EXAMPLE USAGE: - from reportlab.lib import randomtext - print randomtext.randomText(randomtext.PYTHON, 10) - - This prints a random number of random sentences (up to a limit - of ten) using the theme 'PYTHON'. - -""" - -#theme one :-) -STARTUP = ['strategic', 'direction', 'proactive', 'venture capital', - 'reengineering', 'forecast', 'resources', 'SWOT analysis', - 'forward-thinking', 'profit', 'growth', 'doubletalk', 'B2B', 'B2C', - 'venture capital', 'IPO', "NASDAQ meltdown - we're all doomed!"] - -#theme two - computery things. -COMPUTERS = ['Python', 'Perl', 'Pascal', 'Java', 'Javascript', - 'VB', 'Basic', 'LISP', 'Fortran', 'ADA', 'APL', 'C', 'C++', - 'assembler', 'Larry Wall', 'Guido van Rossum', 'XML', 'HTML', - 'cgi', 'cgi-bin', 'Amiga', 'Macintosh', 'Dell', 'Microsoft', - 'firewall', 'server', 'Linux', 'Unix', 'MacOS', 'BeOS', 'AS/400', - 'sendmail', 'TCP/IP', 'SMTP', 'RFC822-compliant', 'dynamic', - 'Internet', 'A/UX', 'Amiga OS', 'BIOS', 'boot managers', 'CP/M', - 'DOS', 'file system', 'FreeBSD', 'Freeware', 'GEOS', 'GNU', - 'Hurd', 'Linux', 'Mach', 'Macintosh OS', 'mailing lists', 'Minix', - 'Multics', 'NetWare', 'NextStep', 'OS/2', 'Plan 9', 'Realtime', - 'UNIX', 'VMS', 'Windows', 'X Windows', 'Xinu', 'security', 'Intel', - 'encryption', 'PGP' , 'software', 'ActiveX', 'AppleScript', 'awk', - 'BETA', 'COBOL', 'Delphi', 'Dylan', 'Eiffel', 'extreme programming', - 'Forth', 'Fortran', 'functional languages', 'Guile', 'format your hard drive', - 'Icon', 'IDL', 'Infer', 'Intercal', 'J', 'Java', 'JavaScript', 'CD-ROM', - 'JCL', 'Lisp', '"literate programming"', 'Logo', 'MUMPS', 'C: drive', - 'Modula-2', 'Modula-3', 'Oberon', 'Occam', 'OpenGL', 'parallel languages', - 'Pascal', 'Perl', 'PL/I', 'PostScript', 'Prolog', 'hardware', 'Blue Screen of Death', - 'Rexx', 'RPG', 'Scheme', 'scripting languages', 'Smalltalk', 'crash!', 'disc crash', - 'Spanner', 'SQL', 'Tcl/Tk', 'TeX', 'TOM', 'Visual', 'Visual Basic', '4GL', - 'VRML', 'Virtual Reality Modeling Language', 'difference engine', '...went into "yo-yo mode"', - 'Sun', 'Sun Microsystems', 'Hewlett Packard', 'output device', - 'CPU', 'memory', 'registers', 'monitor', 'TFT display', 'plasma screen', - 'bug report', '"mis-feature"', '...millions of bugs!', 'pizza', - '"illiterate programming"','...lots of pizza!', 'pepperoni pizza', - 'coffee', 'Jolt Cola[TM]', 'beer', 'BEER!'] - -#theme three - 'blah' - for when you want to be subtle. :-) -BLAH = ['Blah', 'BLAH', 'blahblah', 'blahblahblah', 'blah-blah', - 'blah!', '"Blah Blah Blah"', 'blah-de-blah', 'blah?', 'blah!!!', - 'blah...', 'Blah.', 'blah;', 'blah, Blah, BLAH!', 'Blah!!!'] - -#theme four - 'buzzword bingo' time! -BUZZWORD = ['intellectual capital', 'market segment', 'flattening', - 'regroup', 'platform', 'client-based', 'long-term', 'proactive', - 'quality vector', 'out of the loop', 'implement', - 'streamline', 'cost-centered', 'phase', 'synergy', - 'synergize', 'interactive', 'facilitate', - 'appropriate', 'goal-setting', 'empowering', 'low-risk high-yield', - 'peel the onion', 'goal', 'downsize', 'result-driven', - 'conceptualize', 'multidisciplinary', 'gap analysis', 'dysfunctional', - 'networking', 'knowledge management', 'goal-setting', - 'mastery learning', 'communication', 'real-estate', 'quarterly', - 'scalable', 'Total Quality Management', 'best of breed', - 'nimble', 'monetize', 'benchmark', 'hardball', - 'client-centered', 'vision statement', 'empowerment', - 'lean & mean', 'credibility', 'synergistic', - 'backward-compatible', 'hardball', 'stretch the envelope', - 'bleeding edge', 'networking', 'motivation', 'best practice', - 'best of breed', 'implementation', 'Total Quality Management', - 'undefined', 'disintermediate', 'mindset', 'architect', - 'gap analysis', 'morale', 'objective', 'projection', - 'contribution', 'proactive', 'go the extra mile', 'dynamic', - 'world class', 'real estate', 'quality vector', 'credibility', - 'appropriate', 'platform', 'projection', 'mastery learning', - 'recognition', 'quality', 'scenario', 'performance based', - 'solutioning', 'go the extra mile', 'downsize', 'phase', - 'networking', 'experiencing slippage', 'knowledge management', - 'high priority', 'process', 'ethical', 'value-added', 'implement', - 're-factoring', 're-branding', 'embracing change'] - -#theme five - Star Trek -STARTREK = ['Starfleet', 'Klingon', 'Romulan', 'Cardassian', 'Vulcan', - 'Benzite', 'IKV Pagh', 'emergency transponder', 'United Federation of Planets', - 'Bolian', "K'Vort Class Bird-of-Prey", 'USS Enterprise', 'USS Intrepid', - 'USS Reliant', 'USS Voyager', 'Starfleet Academy', 'Captain Picard', - 'Captain Janeway', 'Tom Paris', 'Harry Kim', 'Counsellor Troi', - 'Lieutenant Worf', 'Lieutenant Commander Data', 'Dr. Beverly Crusher', - 'Admiral Nakamura', 'Irumodic Syndrome', 'Devron system', 'Admiral Pressman', - 'asteroid field', 'sensor readings', 'Binars', 'distress signal', 'shuttlecraft', - 'cloaking device', 'shuttle bay 2', 'Dr. Pulaski', 'Lwaxana Troi', 'Pacifica', - 'William Riker', "Chief O'Brian", 'Soyuz class science vessel', 'Wolf-359', - 'Galaxy class vessel', 'Utopia Planitia yards', 'photon torpedo', 'Archer IV', - 'quantum flux', 'spacedock', 'Risa', 'Deep Space Nine', 'blood wine', - 'quantum torpedoes', 'holodeck', 'Romulan Warbird', 'Betazoid', 'turbolift', 'battle bridge', - 'Memory Alpha', '...with a phaser!', 'Romulan ale', 'Ferrengi', 'Klingon opera', - 'Quark', 'wormhole', 'Bajoran', 'cruiser', 'warship', 'battlecruiser', '"Intruder alert!"', - 'scout ship', 'science vessel', '"Borg Invasion imminent!" ', '"Abandon ship!"', - 'Red Alert!', 'warp-core breech', '"All hands abandon ship! This is not a drill!"'] - -#theme six - print-related terms -PRINTING = ['points', 'picas', 'leading', 'kerning', 'CMYK', 'offset litho', - 'type', 'font family', 'typography', 'type designer', - 'baseline', 'white-out type', 'WOB', 'bicameral', 'bitmap', - 'blockletter', 'bleed', 'margin', 'body', 'widow', 'orphan', - 'cicero', 'cursive', 'letterform', 'sidehead', 'dingbat', 'leader', - 'DPI', 'drop-cap', 'paragraph', 'En', 'Em', 'flush left', 'left justified', - 'right justified', 'centered', 'italic', 'Latin letterform', 'ligature', - 'uppercase', 'lowercase', 'serif', 'sans-serif', 'weight', 'type foundry', - 'fleuron', 'folio', 'gutter', 'whitespace', 'humanist letterform', 'caption', - 'page', 'frame', 'ragged setting', 'flush-right', 'rule', 'drop shadows', - 'prepress', 'spot-colour', 'duotones', 'colour separations', 'four-colour printing', - 'Pantone[TM]', 'service bureau', 'imagesetter'] - -#it had to be done!... -#theme seven - the "full Monty"! -PYTHON = ['Good evening ladies and Bruces','I want to buy some cheese', 'You do have some cheese, do you?', - "Of course sir, it's a cheese shop sir, we've got...",'discipline?... naked? ... With a melon!?', - 'The Church Police!!' , "There's a dead bishop on the landing", 'Would you like a twist of lemming sir?', - '"Conquistador Coffee brings a new meaning to the word vomit"','Your lupins please', - 'Crelm Toothpaste, with the miracle ingredient Fraudulin', - "Well there's the first result and the Silly Party has held Leicester.", - 'Hello, I would like to buy a fish license please', "Look, it's people like you what cause unrest!", - "When we got home, our Dad would thrash us to sleep with his belt!", 'Luxury', "Gumby Brain Specialist", - "My brain hurts!!!", "My brain hurts too.", "How not to be seen", - "In this picture there are 47 people. None of them can be seen", - "Mrs Smegma, will you stand up please?", - "Mr. Nesbitt has learned the first lesson of 'Not Being Seen', not to stand up.", - "My hovercraft is full of eels", "Ah. You have beautiful thighs.", "My nipples explode with delight", - "Drop your panties Sir William, I cannot wait 'til lunchtime", - "I'm a completely self-taught idiot.", "I always wanted to be a lumberjack!!!", - "Told you so!! Oh, coitus!!", "", - "Nudge nudge?", "Know what I mean!", "Nudge nudge, nudge nudge?", "Say no more!!", - "Hello, well it's just after 8 o'clock, and time for the penguin on top of your television set to explode", - "Oh, intercourse the penguin!!", "Funny that penguin being there, isn't it?", - "I wish to register a complaint.", "Now that's what I call a dead parrot", "Pining for the fjords???", - "No, that's not dead, it's ,uhhhh, resting", "This is an ex-parrot!!", - "That parrot is definitely deceased.", "No, no, no - it's spelt Raymond Luxury Yach-t, but it's pronounced 'Throatwobbler Mangrove'.", - "You're a very silly man and I'm not going to interview you.", "No Mungo... never kill a customer." - "And I'd like to conclude by putting my finger up my nose", - "egg and Spam", "egg bacon and Spam", "egg bacon sausage and Spam", "Spam bacon sausage and Spam", - "Spam egg Spam Spam bacon and Spam", "Spam sausage Spam Spam Spam bacon Spam tomato and Spam", - "Spam Spam Spam egg and Spam", "Spam Spam Spam Spam Spam Spam baked beans Spam Spam Spam", - "Spam!!", "I don't like Spam!!!", "You can't have egg, bacon, Spam and sausage without the Spam!", - "I'll have your Spam. I Love it!", - "I'm having Spam Spam Spam Spam Spam Spam Spam baked beans Spam Spam Spam and Spam", - "Have you got anything without Spam?", "There's Spam egg sausage and Spam, that's not got much Spam in it.", - "No one expects the Spanish Inquisition!!", "Our weapon is surprise, surprise and fear!", - "Get the comfy chair!", "Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn!", - "Nobody expects the... Oh bugger!", "What swims in the sea and gets caught in nets? Henri Bergson?", - "Goats. Underwater goats with snorkels and flippers?", "A buffalo with an aqualung?", - "Dinsdale was a looney, but he was a happy looney.", "Dinsdale!!", - "The 127th Upper-Class Twit of the Year Show", "What a great Twit!", - "thought by many to be this year's outstanding twit", - "...and there's a big crowd here today to see these prize idiots in action.", - "And now for something completely different.", "Stop that, it's silly", - "We interrupt this program to annoy you and make things generally irritating", - "This depraved and degrading spectacle is going to stop right now, do you hear me?", - "Stop right there!", "This is absolutely disgusting and I'm not going to stand for it", - "I object to all this sex on the television. I mean, I keep falling off", - "Right! Stop that, it's silly. Very silly indeed", "Very silly indeed", "Lemon curry?", - "And now for something completely different, a man with 3 buttocks", - "I've heard of unisex, but I've never had it", "That's the end, stop the program! Stop it!"] -leadins=[ - "To characterize a linguistic level L,", - "On the other hand,", - "This suggests that", - "It appears that", - "Furthermore,", - "We will bring evidence in favor of the following thesis: ", - "To provide a constituent structure for T(Z,K),", - "From C1, it follows that", - "For any transformation which is sufficiently diversified in application to be of any interest,", - "Analogously,", - "Clearly,", - "Note that", - "Of course,", - "Suppose, for instance, that", - "Thus", - "With this clarification,", - "Conversely,", - "We have already seen that", - "By combining adjunctions and certain deformations,", - "I suggested that these results would follow from the assumption that", - "If the position of the trace in (99c) were only relatively inaccessible to movement,", - "However, this assumption is not correct, since", - "Comparing these examples with their parasitic gap counterparts in (96) and (97), we see that", - "In the discussion of resumptive pronouns following (81),", - "So far,", - "Nevertheless,", - "For one thing,", - "Summarizing, then, we assume that", - "A consequence of the approach just outlined is that", - "Presumably,", - "On our assumptions,", - "It may be, then, that", - "It must be emphasized, once again, that", - "Let us continue to suppose that", - "Notice, incidentally, that", - "A majority of informed linguistic specialists agree that", - ] - -subjects = [ - "the notion of level of grammaticalness", - "a case of semigrammaticalness of a different sort", - "most of the methodological work in modern linguistics", - "a subset of English sentences interesting on quite independent grounds", - "the natural general principle that will subsume this case", - "an important property of these three types of EC", - "any associated supporting element", - "the appearance of parasitic gaps in domains relatively inaccessible to ordinary extraction", - "the speaker-hearer's linguistic intuition", - "the descriptive power of the base component", - "the earlier discussion of deviance", - "this analysis of a formative as a pair of sets of features", - "this selectionally introduced contextual feature", - "a descriptively adequate grammar", - "the fundamental error of regarding functional notions as categorial", - "relational information", - "the systematic use of complex symbols", - "the theory of syntactic features developed earlier", - ] - -verbs= [ - "can be defined in such a way as to impose", - "delimits", - "suffices to account for", - "cannot be arbitrary in", - "is not subject to", - "does not readily tolerate", - "raises serious doubts about", - "is not quite equivalent to", - "does not affect the structure of", - "may remedy and, at the same time, eliminate", - "is not to be considered in determining", - "is to be regarded as", - "is unspecified with respect to", - "is, apparently, determined by", - "is necessary to impose an interpretation on", - "appears to correlate rather closely with", - "is rather different from", - ] - -objects = [ - "problems of phonemic and morphological analysis.", - "a corpus of utterance tokens upon which conformity has been defined by the paired utterance test.", - "the traditional practice of grammarians.", - "the levels of acceptability from fairly high (e.g. (99a)) to virtual gibberish (e.g. (98d)).", - "a stipulation to place the constructions into these various categories.", - "a descriptive fact.", - "a parasitic gap construction.", - "the extended c-command discussed in connection with (34).", - "the ultimate standard that determines the accuracy of any proposed grammar.", - "the system of base rules exclusive of the lexicon.", - "irrelevant intervening contexts in selectional rules.", - "nondistinctness in the sense of distinctive feature theory.", - "a general convention regarding the forms of the grammar.", - "an abstract underlying order.", - "an important distinction in language use.", - "the requirement that branching is not tolerated within the dominance scope of a complex symbol.", - "the strong generative capacity of the theory.", - ] - -def format_wisdom(text,line_length=72): - try: - import textwrap - return textwrap.fill(text, line_length) - except: - return text - -def chomsky(times = 1): - if not isinstance(times, int): - return format_wisdom(__doc__) - import random - prevparts = [] - newparts = [] - output = [] - for i in xrange(times): - for partlist in (leadins, subjects, verbs, objects): - while 1: - part = random.choice(partlist) - if part not in prevparts: - break - newparts.append(part) - output.append(' '.join(newparts)) - prevparts = newparts - newparts = [] - return format_wisdom(' '.join(output)) - -from reportlab import rl_config -if rl_config.invariant: - if not getattr(rl_config,'_random',None): - rl_config._random = 1 - import random - random.seed(2342471922L) - del random -del rl_config - -def randomText(theme=STARTUP, sentences=5): - #this may or may not be appropriate in your company - if type(theme)==type(''): - if theme.lower()=='chomsky': return chomsky(sentences) - elif theme.upper() in ('STARTUP','COMPUTERS','BLAH','BUZZWORD','STARTREK','PRINTING','PYTHON'): - theme = globals()[theme] - else: - raise ValueError('Unknown theme "%s"' % theme) - - from random import randint, choice - - RANDOMWORDS = theme - - #sentences = 5 - output = "" - for sentenceno in range(randint(1,sentences)): - output = output + 'Blah' - for wordno in range(randint(10,25)): - if randint(0,4)==0: - word = choice(RANDOMWORDS) - else: - word = 'blah' - output = output + ' ' +word - output = output+'. ' - return output - -if __name__=='__main__': - print chomsky(5) diff --git a/bin/reportlab/lib/rltempfile.py b/bin/reportlab/lib/rltempfile.py deleted file mode 100644 index d4e58aab507..00000000000 --- a/bin/reportlab/lib/rltempfile.py +++ /dev/null @@ -1,29 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2006 -#see license.txt for license details -# $URI:$ -__version__=''' $Id: rltempfile.py 2892 2006-05-19 14:16:02Z rgbecker $ ''' -_rl_tempdir=None -__all__ = ('get_rl_tempdir', 'get_rl_tempdir') -import os, tempfile -def _rl_getuid(): - if hasattr(os,'getuid'): - return os.getuid() - else: - return '' - -def get_rl_tempdir(*subdirs): - global _rl_tempdir - if _rl_tempdir is None: - _rl_tempdir = os.path.join(tempfile.gettempdir(),'ReportLab_tmp%s' % str(_rl_getuid())) - d = _rl_tempdir - if subdirs: d = os.path.join(*((d,)+subdirs)) - try: - os.makedirs(d) - except: - pass - return d - -def get_rl_tempfile(fn=None): - if not fn: - fn = tempfile.mktemp() - return os.path.join(get_rl_tempdir(),fn) diff --git a/bin/reportlab/lib/rparsexml.py b/bin/reportlab/lib/rparsexml.py deleted file mode 100644 index 37982050ee2..00000000000 --- a/bin/reportlab/lib/rparsexml.py +++ /dev/null @@ -1,431 +0,0 @@ -"""Radically simple xml parsing - -Example parse - -text in xml - -( "this", - {"type": "xml"}, - [ "text ", - ("b", None, ["in"], None), - " xml" - ] - None ) - -{ 0: "this" - "type": "xml" - 1: ["text ", - {0: "b", 1:["in"]}, - " xml"] -} - -Ie, xml tag translates to a tuple: - (name, dictofattributes, contentlist, miscellaneousinfo) - -where miscellaneousinfo can be anything, (but defaults to None) -(with the intention of adding, eg, line number information) - -special cases: name of "" means "top level, no containing tag". -Top level parse always looks like this - - ("", list, None, None) - - contained text of None means - -In order to support stuff like - - - -AT THE MOMENT & ETCETERA ARE IGNORED. THEY MUST BE PROCESSED -IN A POST-PROCESSING STEP. - -PROLOGUES ARE NOT UNDERSTOOD. OTHER STUFF IS PROBABLY MISSING. -""" - -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 - pyRXP_parser = pyRXPU.Parser( - ErrorOnValidityErrors=1, - NoNoDTDWarning=1, - ExpandCharacterEntities=1, - ExpandGeneralEntities=1, - warnCB = warnCB, - srcName='string input', - ReturnUTF8 = 1, - ) - def parsexml(xmlText, oneOutermostTag=0,eoCB=None,entityReplacer=None): - pyRXP_parser.eoCB = eoCB - p = pyRXP_parser.parse(xmlText) - return oneOutermostTag and p or ('',None,[p],None) -except ImportError: - simpleparse = 1 - -NONAME = "" -NAMEKEY = 0 -CONTENTSKEY = 1 -CDATAMARKER = "" -replacelist = [("<", "<"), (">", ">"), ("&", "&")] # amp must be last -#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) - 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" - (result, cursor) = parsexml0(xmltext,entityReplacer=entityReplacer) - if oneOutermostTag: - return result[2][0] - else: - return result - -if simpleparse: - parsexml = parsexmlSimple - -def parseFile(filename): - raw = open(filename, 'r').read() - return parsexml(raw) - -verbose = 0 - -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) - if openbracket<0: break - past = openbracket+1 - found = None - for e in prologue_elements: - le = len(e) - if text[past:past+le]==e: - found = 1 - cursor = find(text, ">", past) - if cursor<0: - raise ValueError, "can't close prologue %s" % `e` - cursor = cursor+1 - if found is None: - done=1 - #print "done skipping" - return cursor - -def parsexml0(xmltext, startingat=0, toplevel=1, - # snarf in some globals - strip=string.strip, split=string.split, find=string.find, 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", `xmltext[startingat: startingat+10]` - # DEFAULTS - NameString = NONAME - ContentList = AttDict = ExtraStuff = None - if toplevel is not None: - #if verbose: print "at top level" - #if startingat!=0: - # raise ValueError, "have to start at 0 for top level!" - xmltext = strip(xmltext) - cursor = startingat - #look for interesting starting points - firstbracket = find(xmltext, "<", cursor) - afterbracket2char = xmltext[firstbracket+1:firstbracket+3] - #print "a", `afterbracket2char` - #firstampersand = find(xmltext, "&", cursor) - #if firstampersand>0 and firstampersand0: - #afterbracket2char = xmltext[firstbracket:firstbracket+2] - if toplevel is not None: - #print "toplevel with no outer tag" - NameString = name = NONAME - cursor = skip_prologue(xmltext, cursor) - #break - elif firstbracket<0: - 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]=="": - 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) - noclose = closebracket<0 - startsearch = closebracket+1 - pastfirstbracket = firstbracket+1 - tagcontent = xmltext[pastfirstbracket:closebracket] - # shortcut, no equal means nothing but name in the tag content - if '=' not in tagcontent: - if tagcontent[-1]=="/": - # simple case - #print "simple case", tagcontent - tagcontent = tagcontent[:-1] - docontents = None - name = strip(tagcontent) - NameString = name - cursor = startsearch - else: - if '"' in tagcontent: - # check double quotes - stop = None - # not inside double quotes! (the split should have odd length) - if noclose or len(split(tagcontent+".", '"'))% 2: - stop=1 - while stop is None: - closebracket = find(xmltext, ">", 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: - stop=1 - if noclose: - raise ValueError, "unclosed start tag %s" % repr(xmltext[firstbracket:firstbracket+20]) - cursor = startsearch - #cursor = closebracket+1 - # handle simple tag /> syntax - if xmltext[closebracket-1]=="/": - #if verbose: print "it's a simple tag" - closebracket = closebracket-1 - tagcontent = tagcontent[:-1] - docontents = None - #tagcontent = xmltext[firstbracket+1:closebracket] - tagcontent = strip(tagcontent) - taglist = split(tagcontent, "=") - #if not taglist: - # raise ValueError, "tag with no name %s" % repr(xmltext[firstbracket:firstbracket+20]) - taglist0 = taglist[0] - taglist0list = split(taglist0) - #if len(taglist0list)>2: - # raise ValueError, "bad tag head %s" % repr(taglist0) - name = taglist0list[0] - #print "tag name is", name - NameString = name - # now parse the attributes - attributename = taglist0list[-1] - # put a fake att name at end of last taglist entry for consistent parsing - taglist[-1] = taglist[-1]+" f" - AttDict = D = {} - taglistindex = 1 - lasttaglistindex = len(taglist) - #for attentry in taglist[1:]: - while taglistindexlasttaglistindex: - 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) - nextattname = attlist[-1] - attvalue = attentry[:-len(nextattname)] - attvalue = strip(attvalue) - try: - first = attvalue[0]; last=attvalue[-1] - except: - raise ValueError, "attvalue,attentry,attlist="+repr((attvalue, attentry,attlist)) - if first==last=='"' or first==last=="'": - attvalue = attvalue[1:-1] - #print attributename, "=", attvalue - D[attributename] = attvalue - attributename = nextattname - # pass over other tags and content looking for end tag - if docontents is not None: - #print "now looking for end tag" - ContentList = L - while docontents is not None: - nextopenbracket = find(xmltext, "<", cursor) - if nextopenbracket", nextopenbracket) - if nextclosebracket\n%s\n" % (name, attributes, textpprint, name) - # otherwise must be a simple tag - return "<%s %s/>" % (name, attributes) - -dump = 0 -def testparse(s): - from time import time - from pprint import pprint - now = time() - D = parsexmlSimple(s) - print "DONE", time()-now - if dump&4: - pprint(D) - #pprint(D) - if dump&1: - print "============== reformatting" - p = pprettyprint(D) - print p - -def test(): - testparse("""text <>in xml - - text in xml ]]> - just testing brackets feature - """) - -filenames = [ #"../../reportlab/demos/pythonpoint/pythonpoint.xml", - "samples/hamlet.xml"] - -#filenames = ["moa.xml"] - -dump=1 -if __name__=="__main__": - test() - from time import time - now = time() - for f in filenames: - t = open(f).read() - print "parsing", f - testparse(t) - print "elapsed", time()-now diff --git a/bin/reportlab/lib/sequencer.py b/bin/reportlab/lib/sequencer.py deleted file mode 100644 index c9d9274d2d7..00000000000 --- a/bin/reportlab/lib/sequencer.py +++ /dev/null @@ -1,284 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/sequencer.py -__version__=''' $Id: sequencer.py 2423 2004-08-24 11:36:22Z rgbecker $ ''' -"""This module defines a single public class, Sequencer, which aids in -numbering and formatting lists.""" -# -# roman numbers conversion thanks to -# -# fredrik lundh, november 1996 (based on a C hack from 1984) -# -# fredrik@pythonware.com -# http://www.pythonware.com - -_RN_TEMPLATES = [ 0, 01, 011, 0111, 012, 02, 021, 0211, 02111, 013 ] -_RN_LETTERS = "IVXLCDM" - -from string import lower - - -def _format_I(value): - if value < 0 or value > 3999: - raise ValueError, "illegal value" - str = "" - base = -1 - while value: - value, index = divmod(value, 10) - tmp = _RN_TEMPLATES[index] - while tmp: - tmp, index = divmod(tmp, 8) - str = _RN_LETTERS[index+base] + str - base = base + 2 - return str - -def _format_i(num): - return lower(_format_I(num)) - -def _format_123(num): - """The simplest formatter""" - return str(num) - -def _format_ABC(num): - """Uppercase. Wraps around at 26.""" - n = (num -1) % 26 - return chr(n+65) - -def _format_abc(num): - """Lowercase. Wraps around at 26.""" - n = (num -1) % 26 - return chr(n+97) - - -class _Counter: - """Private class used by Sequencer. Each counter - knows its format, and the IDs of anything it - resets, as well as its value. Starts at zero - and increments just before you get the new value, - so that it is still 'Chapter 5' and not 'Chapter 6' - when you print 'Figure 5.1'""" - - def __init__(self): - self._base = 0 - self._value = self._base - self._formatter = _format_123 - self._resets = [] - - def setFormatter(self, formatFunc): - self._formatter = formatFunc - - def reset(self, value=None): - if value: - self._value = value - else: - self._value = self._base - - def next(self): - self._value = self._value + 1 - v = self._value - for counter in self._resets: - counter.reset() - return v - - def _this(self): - return self._value - - def nextf(self): - """Returns next value formatted""" - return self._formatter(self.next()) - - def thisf(self): - return self._formatter(self._this()) - - def chain(self, otherCounter): - if not otherCounter in self._resets: - self._resets.append(otherCounter) - - -class Sequencer: - """Something to make it easy to number paragraphs, sections, - images and anything else. The features include registering - new string formats for sequences, and 'chains' whereby - some counters are reset when their parents. - It keeps track of a number of - 'counters', which are created on request: - Usage: - >>> seq = layout.Sequencer() - >>> seq.next('Bullets') - 1 - >>> seq.next('Bullets') - 2 - >>> seq.next('Bullets') - 3 - >>> seq.reset('Bullets') - >>> seq.next('Bullets') - 1 - >>> seq.next('Figures') - 1 - >>> - """ - - def __init__(self): - self._counters = {} #map key to current number - self._defaultCounter = None - - self._formatters = { - # the formats it knows initially - '1':_format_123, - 'A':_format_ABC, - 'a':_format_abc, - 'I':_format_I, - 'i':_format_i, - } - - def _getCounter(self, counter=None): - """Creates one if not present""" - try: - return self._counters[counter] - except KeyError: - cnt = _Counter() - self._counters[counter] = cnt - return cnt - - def _this(self, counter=None): - """Retrieves counter value but does not increment. For - new counters, sets base value to 1.""" - if not counter: - 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() - - def thisf(self, counter=None): - if not counter: - counter = self._defaultCounter - return self._getCounter(counter).thisf() - - def nextf(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).nextf() - - def setDefaultCounter(self, default=None): - """Changes the key used for the default""" - self._defaultCounter = default - - def registerFormat(self, format, func): - """Registers a new formatting function. The funtion - must take a number as argument and return a string; - fmt is a short menmonic string used to access it.""" - self._formatters[format] = func - - def setFormat(self, counter, format): - """Specifies that the given counter should use - the given format henceforth.""" - func = self._formatters[format] - self._getCounter(counter).setFormatter(func) - - def reset(self, counter=None, base=0): - if not counter: - counter = self._defaultCounter - self._getCounter(counter)._value = base - - def chain(self, parent, child): - p = self._getCounter(parent) - c = self._getCounter(child) - p.chain(c) - - def __getitem__(self, key): - """Allows compact notation to support the format function. - s['key'] gets current value, s['key+'] increments.""" - if key[-1:] == '+': - counter = key[:-1] - return self.nextf(counter) - else: - return self.thisf(key) - - def format(self, template): - """The crowning jewels - formats multi-level lists.""" - return template % self - - def dump(self): - """Write current state to stdout for diagnostics""" - counters = self._counters.items() - counters.sort() - print 'Sequencer dump:' - for (key, counter) in counters: - 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 - -def getSequencer(): - global _sequencer - if _sequencer is None: - _sequencer = Sequencer() - return _sequencer - -def setSequencer(seq): - global _sequencer - s = _sequencer - _sequencer = seq - return s - -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() - s.setDefaultCounter('list1') - print 'Set default to list1: %d %d %d' % (s.next(),s.next(), s.next()) - 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...' - s.setFormat('Appendix', 'A') - 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:' - 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' - 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 - - -if __name__=='__main__': - test() diff --git a/bin/reportlab/lib/set_ops.py b/bin/reportlab/lib/set_ops.py deleted file mode 100644 index eb44fb2abbc..00000000000 --- a/bin/reportlab/lib/set_ops.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/set_ops.py -import types -import string - -def __set_coerce(t, S): - if t is types.ListType: - return list(S) - elif t is types.TupleType: - return tuple(S) - elif t is types.StringType: - return string.join(S, '') - return S - -def unique(seq): - result = [] - for i in seq: - if i not in result: - result.append(i) - return __set_coerce(type(seq), result) - -def intersect(seq1, seq2): - result = [] - if type(seq1) != type(seq2) and type(seq2) == types.StringType: 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) - -def union(seq1, seq2): - if type(seq1) == type(seq2): - return unique(seq1 + seq2) - if type(seq1) == types.ListType or type(seq2) == types.ListType: - return unique(list(seq1) + list(seq2)) - if type(seq1) == types.TupleType or type(seq2) == types.TupleType: - return unique(tuple(seq1) + tuple(seq2)) - return unique(list(seq1) + list(seq2)) diff --git a/bin/reportlab/lib/styles.py b/bin/reportlab/lib/styles.py deleted file mode 100644 index 4da775eaaf5..00000000000 --- a/bin/reportlab/lib/styles.py +++ /dev/null @@ -1,257 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/styles.py -__version__=''' $Id: styles.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' - -from reportlab.lib.colors import white, black -from reportlab.lib.enums import TA_LEFT, TA_CENTER - -########################################################### -# This class provides an 'instance inheritance' -# mechanism for its descendants, simpler than acquisition -# but not as far-reaching -########################################################### -class PropertySet: - defaults = {} - - def __init__(self, name, parent=None, **kw): - """When initialized, it copies the class defaults; - then takes a copy of the attributes of the parent - if any. All the work is done in init - styles - should cost little to use at runtime.""" - # step one - validate the hell out of it - assert not self.defaults.has_key('name'), "Class Defaults may not contain a 'name' attribute" - assert not self.defaults.has_key('parent'), "Class Defaults may not contain a 'parent' attribute" - if parent: - assert parent.__class__ == self.__class__, "Parent style must have same class as new style" - - #step two - self.name = name - self.parent = parent - self.__dict__.update(self.defaults) - - #step two - copy from parent if any. Try to be - # very strict that only keys in class defaults are - # allowed, so they cannot inherit - self.refresh() - - #step three - copy keywords if any - for (key, value) in kw.items(): - self.__dict__[key] = value - - - def __repr__(self): - return "<%s '%s'>" % (self.__class__.__name__, self.name) - - def refresh(self): - """re-fetches attributes from the parent on demand; - use if you have been hacking the styles. This is - used by __init__""" - if self.parent: - 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() - keylist.sort() - keylist.remove('name') - keylist.remove('parent') - for key in keylist: - value = self.__dict__.get(key, None) - print indent + '%s = %s' % (key, value) - -class ParagraphStyle(PropertySet): - defaults = { - 'fontName':'Times-Roman', - 'fontSize':10, - 'leading':12, - 'leftIndent':0, - 'rightIndent':0, - 'firstLineIndent':0, - 'alignment':TA_LEFT, - 'spaceBefore':0, - 'spaceAfter':0, - 'bulletFontName':'Times-Roman', - 'bulletFontSize':10, - 'bulletIndent':0, - 'textColor': black, - 'backColor':None, - 'wordWrap':None, - } - -class LineStyle(PropertySet): - defaults = { - 'width':1, - 'color': black - } - def prepareCanvas(self, canvas): - """You can ask a LineStyle to set up the canvas for drawing - the lines.""" - canvas.setLineWidth(1) - #etc. etc. - -class StyleSheet1: - """This may or may not be used. The idea is to - 1. slightly simplify construction of stylesheets; - 2. enforce rules to validate styles when added - (e.g. we may choose to disallow having both - 'heading1' and 'Heading1' - actual rules are - open to discussion); - 3. allow aliases and alternate style lookup - mechanisms - 4. Have a place to hang style-manipulation - methods (save, load, maybe support a GUI - editor) - Access is via getitem, so they can be - compatible with plain old dictionaries. - """ - def __init__(self): - self.byName = {} - self.byAlias = {} - - - def __getitem__(self, key): - try: - return self.byAlias[key] - except KeyError: - try: - return self.byName[key] - except KeyError: - raise KeyError, "Style '%s' not found in stylesheet" % key - - def has_key(self, key): - if self.byAlias.has_key(key): - return 1 - elif self.byName.has_key(key): - return 1 - else: - return 0 - - def add(self, style, alias=None): - key = style.name - if self.byName.has_key(key): - raise KeyError, "Style '%s' already defined in stylesheet" % key - if self.byAlias.has_key(key): - raise KeyError, "Style name '%s' is already an alias in stylesheet" % key - - if alias: - if self.byName.has_key(alias): - raise KeyError, "Style '%s' already defined in stylesheet" % alias - if self.byAlias.has_key(alias): - raise KeyError, "Alias name '%s' is already an alias in stylesheet" % alias - #passed all tests? OK, add it - self.byName[key] = style - if alias: - self.byAlias[alias] = style - - def list(self): - styles = self.byName.items() - styles.sort() - alii = {} - for (alias, style) in self.byAlias.items(): - alii[style] = alias - for (name, style) in styles: - alias = alii.get(style, None) - print name, alias - style.listAttrs(' ') - print - - - - -def testStyles(): - pNormal = ParagraphStyle('Normal',None) - pNormal.fontName = 'Times-Roman' - pNormal.fontSize = 12 - pNormal.leading = 14.4 - - pNormal.listAttrs() - print - pPre = ParagraphStyle('Literal', pNormal) - pPre.fontName = 'Courier' - pPre.listAttrs() - return pNormal, pPre - -def getSampleStyleSheet(): - """Returns a stylesheet object""" - stylesheet = StyleSheet1() - - stylesheet.add(ParagraphStyle(name='Normal', - fontName='Times-Roman', - fontSize=10, - leading=12) - ) - - stylesheet.add(ParagraphStyle(name='BodyText', - parent=stylesheet['Normal'], - spaceBefore=6) - ) - stylesheet.add(ParagraphStyle(name='Italic', - parent=stylesheet['BodyText'], - fontName = 'Times-Italic') - ) - - stylesheet.add(ParagraphStyle(name='Heading1', - parent=stylesheet['Normal'], - fontName = 'Times-Bold', - fontSize=18, - leading=22, - spaceAfter=6), - alias='h1') - - stylesheet.add(ParagraphStyle(name='Title', - parent=stylesheet['Normal'], - fontName = 'Times-Bold', - fontSize=18, - leading=22, - alignment=TA_CENTER, - spaceAfter=6), - alias='title') - - stylesheet.add(ParagraphStyle(name='Heading2', - parent=stylesheet['Normal'], - fontName = 'Times-Bold', - fontSize=14, - leading=18, - spaceBefore=12, - spaceAfter=6), - alias='h2') - - stylesheet.add(ParagraphStyle(name='Heading3', - parent=stylesheet['Normal'], - fontName = 'Times-BoldItalic', - fontSize=12, - leading=14, - spaceBefore=12, - spaceAfter=6), - alias='h3') - - stylesheet.add(ParagraphStyle(name='Bullet', - parent=stylesheet['Normal'], - firstLineIndent=0, - spaceBefore=3), - alias='bu') - - stylesheet.add(ParagraphStyle(name='Definition', - parent=stylesheet['Normal'], - firstLineIndent=0, - leftIndent=36, - bulletIndent=0, - spaceBefore=6, - bulletFontName='Times-BoldItalic'), - alias='df') - - stylesheet.add(ParagraphStyle(name='Code', - parent=stylesheet['Normal'], - fontName='Courier', - fontSize=8, - leading=8.8, - firstLineIndent=0, - leftIndent=36)) - - - return stylesheet diff --git a/bin/reportlab/lib/textsplit.py b/bin/reportlab/lib/textsplit.py deleted file mode 100644 index 5c6733c788a..00000000000 --- a/bin/reportlab/lib/textsplit.py +++ /dev/null @@ -1,210 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2006 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/textsplit.py - -"""Helpers for text wrapping, hyphenation, Asian text splitting and kinsoku shori. - -How to split a 'big word' depends on the language and the writing system. This module -works on a Unicode string. It ought to grow by allowing ore algoriths to be plugged -in based on possible knowledge of the language and desirable 'niceness' of the algorithm. - -""" - -__version__=''' $Id: textsplit.py 2833 2006-04-05 16:01:20Z rgbecker $ ''' - -from types import StringType, UnicodeType -import unicodedata -from reportlab.pdfbase.pdfmetrics import stringWidth -from reportlab.rl_config import _FUZZ - -CANNOT_START_LINE = [ - #strongly prohibited e.g. end brackets, stop, exclamation... - u'!\',.:;?!")]\u3001\u3002\u300d\u300f\u3011\u3015\uff3d\u3011\uff09', - #middle priority e.g. continuation small vowels - wrapped on two lines but one string... - u'\u3005\u2015\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308e\u30a1\u30a3' - u'\u30a5\u30a7\u30a9\u30c3\u30e3\u30e5\u30e7\u30ee\u30fc\u30f5\u30f6', - #weakly prohibited - continuations, celsius symbol etc. - u'\u309b\u309c\u30fb\u30fd\u30fe\u309d\u309e\u2015\u2010\xb0\u2032\u2033\u2103\uffe0\uff05\u2030' - ] - -ALL_CANNOT_START = u''.join(CANNOT_START_LINE) -CANNOT_END_LINE = [ - #strongly prohibited - u'\u2018\u201c\uff08[{\uff08\u3014\uff3b\uff5b\u3008\u300a\u300c\u300e\u3010', - #weaker - currency symbols, hash, postcode - prefixes - u'$\u00a3@#\uffe5\uff04\uffe1\uff20\u3012\u00a7' - ] -ALL_CANNOT_END = u''.join(CANNOT_END_LINE) -def getCharWidths(word, fontName, fontSize): - """Returns a list of glyph widths. Should be easy to optimize in _rl_accel - - >>> getCharWidths('Hello', 'Courier', 10) - [6.0, 6.0, 6.0, 6.0, 6.0] - >>> from reportlab.pdfbase.cidfonts import UnicodeCIDFont - >>> from reportlab.pdfbase.pdfmetrics import registerFont - >>> registerFont(UnicodeCIDFont('HeiseiMin-W3')) - >>> getCharWidths(u'\u6771\u4EAC', 'HeiseiMin-W3', 10) #most kanji are 100 ems - [10.0, 10.0] - """ - #character-level function call; the performance is going to SUCK - - return [stringWidth(uChar, fontName, fontSize) for uChar in word] - -def wordSplit(word, availWidth, fontName, fontSize, encoding='utf8'): - """Attempts to break a word which lacks spaces into two parts, the first of which - fits in the remaining space. It is allowed to add hyphens or whatever it wishes. - - This is intended as a wrapper for some language- and user-choice-specific splitting - algorithms. It should only be called after line breaking on spaces, which covers western - languages and is highly optimised already. It works on the 'last unsplit word'. - - Presumably with further study one could write a Unicode splitting algorithm for text - fragments whick was much faster. - - Courier characters should be 6 points wide. - >>> wordSplit('HelloWorld', 30, 'Courier', 10) - [[0.0, 'Hello'], [0.0, 'World']] - >>> wordSplit('HelloWorld', 31, 'Courier', 10) - [[1.0, 'Hello'], [1.0, 'World']] - """ - if type(word) is not UnicodeType: - uword = word.decode(encoding) - else: - uword = word - - charWidths = getCharWidths(uword, fontName, fontSize) - lines = dumbSplit(uword, charWidths, availWidth) - - if type(word) is not UnicodeType: - lines2 = [] - #convert back - for (extraSpace, text) in lines: - lines2.append([extraSpace, text.encode(encoding)]) - lines = lines2 - - return lines - -def dumbSplit(word, widths, availWidth): - """This function attempts to fit as many characters as possible into the available - space, cutting "like a knife" between characters. This would do for Chinese. - It returns a list of (text, extraSpace) items where text is a Unicode string, - and extraSpace is the points of unused space available on the line. This is a - structure which is fairly easy to display, and supports 'backtracking' approaches - after the fact. - - Test cases assume each character is ten points wide... - - >>> dumbSplit(u'Hello', [10]*5, 60) - [[10.0, u'Hello']] - >>> dumbSplit(u'Hello', [10]*5, 50) - [[0.0, u'Hello']] - >>> dumbSplit(u'Hello', [10]*5, 40) - [[0.0, u'Hell'], [30, u'o']] - """ - - _more = """ - #>>> dumbSplit(u'Hello', [10]*5, 4) # less than one character - #(u'', u'Hello') - # this says 'Nihongo wa muzukashii desu ne!' (Japanese is difficult isn't it?) in 12 characters - >>> jtext = u'\u65e5\u672c\u8a9e\u306f\u96e3\u3057\u3044\u3067\u3059\u306d\uff01' - >>> dumbSplit(jtext, [10]*11, 30) # - (u'\u65e5\u672c\u8a9e', u'\u306f\u96e3\u3057\u3044\u3067\u3059\u306d\uff01') - """ - assert type(word) is UnicodeType - lines = [] - widthUsed = 0.0 - lineStartPos = 0 - for (i, w) in enumerate(widths): - widthUsed += w - if widthUsed > availWidth + _FUZZ: - #used more than can fit... - #ping out with previous cut, then set up next line with one character - - extraSpace = availWidth - widthUsed + w - #print 'ending a line; used %d, available %d' % (widthUsed, availWidth) - selected = word[lineStartPos:i] - - - #This is the most important of the Japanese typography rules. - #if next character cannot start a line, wrap it up to this line so it hangs - #in the right margin. We won't do two or more though - that's unlikely and - #would result in growing ugliness. - nextChar = word[i] - if nextChar in ALL_CANNOT_START: - #it's punctuation or a closing bracket of some kind. 'wrap up' - #so it stays on the line above, slightly exceeding our target width. - #print 'wrapping up', repr(nextChar) - selected += nextChar - extraSpace -= w - i += 1 - - - - lines.append([extraSpace, selected]) - lineStartPos = i - widthUsed = w - i -= 1 - #any characters left? - if widthUsed > 0: - extraSpace = availWidth - widthUsed - lines.append([extraSpace, word[lineStartPos:]]) - - return lines - -def kinsokuShoriSplit(word, widths, availWidth): - #NOT USED OR FINISHED YET! - """Split according to Japanese rules according to CJKV (Lunde). - - Essentially look for "nice splits" so that we don't end a line - with an open bracket, or start one with a full stop, or stuff like - that. There is no attempt to try to split compound words into - constituent kanji. It currently uses wrap-down: packs as much - on a line as possible, then backtracks if needed - - - This returns a number of words each of which should just about fit - on a line. If you give it a whole paragraph at once, it will - do all the splits. - - It's possible we might slightly step over the width limit - if we do hanging punctuation marks in future (e.g. dangle a Japanese - full stop in the right margin rather than using a whole character - box. - - """ - lines = [] - assert len(word) == len(widths) - curWidth = 0.0 - curLine = [] - i = 0 #character index - we backtrack at times so cannot use for loop - while 1: - ch = word[i] - w = widths[i] - if curWidth + w < availWidth: - curLine.append(ch) - curWidth += w - else: - #end of line. check legality - if ch in CANNOT_END_LINE[0]: - pass - #to be completed - - -# This recipe refers: -# -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 -import re -rx=re.compile(u"([\u2e80-\uffff])", re.UNICODE) -def cjkwrap(text, width, encoding="utf8"): - return reduce(lambda line, word, width=width: '%s%s%s' % - (line, - [' ','\n', ''][(len(line)-line.rfind('\n')-1 - + len(word.split('\n',1)[0] ) >= width) or - line[-1:] == '\0' and 2], - word), - rx.sub(r'\1\0 ', unicode(text,encoding)).split(' ') - ).replace('\0', '').encode(encoding) - -if __name__=='__main__': - import doctest, textsplit - doctest.testmod(textsplit) diff --git a/bin/reportlab/lib/tocindex.py b/bin/reportlab/lib/tocindex.py deleted file mode 100644 index d94189fa630..00000000000 --- a/bin/reportlab/lib/tocindex.py +++ /dev/null @@ -1,294 +0,0 @@ -# Tables of Contents and Indices -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/tocindex.py -__version__=''' $Id: tocindex.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__='' -""" -This module will contain standard Table of Contents and Index objects. -under development, and pending certain hooks adding in DocTemplate -As of today, it onyl handles the formatting aspect of TOCs -""" - -import string - -from reportlab.platypus import Flowable, BaseDocTemplate, Paragraph, \ - PageBreak, Frame, PageTemplate, NextPageTemplate -from reportlab.platypus.doctemplate import IndexingFlowable -from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet -from reportlab.platypus import tables -from reportlab.lib import enums -from reportlab.lib import colors -from reportlab.lib.units import inch, cm -from reportlab.rl_config import defaultPageSize - - ############################################################## - # - # we first define a paragraph style for each level of the - # table, and one for the table as whole; you can supply your - # own. - # - ############################################################## - - -levelZeroParaStyle = ParagraphStyle(name='LevelZero', - fontName='Times-Roman', - fontSize=10, - leading=12) -levelOneParaStyle = ParagraphStyle(name='LevelOne', - parent = levelZeroParaStyle, - firstLineIndent = 0, - leftIndent = 18) -levelTwoParaStyle = ParagraphStyle(name='LevelTwo', - parent = levelOneParaStyle, - firstLineIndent = 0, - leftIndent = 36) -levelThreeParaStyle = ParagraphStyle(name='LevelThree', - parent = levelTwoParaStyle, - firstLineIndent = 0, - leftIndent = 54) - -defaultTableStyle = tables.TableStyle([ - ('VALIGN',(0,0),(-1,-1),'TOP'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ]) - - - - - - -class TableOfContents0(IndexingFlowable): - """This creates a formatted table of contents. It presumes - a correct block of data is passed in. The data block contains - a list of (level, text, pageNumber) triplets. You can supply - a paragraph style for each level (starting at zero).""" - def __init__(self): - self.entries = [] - self.rightColumnWidth = 72 - self.levelStyles = [levelZeroParaStyle, - levelOneParaStyle, - levelTwoParaStyle, - levelThreeParaStyle] - self.tableStyle = defaultTableStyle - self._table = None - self._entries = [] - self._lastEntries = [] - - def beforeBuild(self): - # keep track of the last run - self._lastEntries = self._entries[:] - self.clearEntries() - - def isIndexing(self): - return 1 - - def isSatisfied(self): - return (self._entries == self._lastEntries) - - def notify(self, kind, stuff): - """DocTemplate framework can call this with all kinds - of events; we say we are interested in 'TOCEntry' - events.""" - if kind == 'TOCEntry': - (level, text, pageNum) = stuff - self.addEntry(level, text, pageNum) - #print 'TOC notified of ', stuff -## elif kind == 'debug': -## # hack to watch its state -## import pprint -## print 'Last Entries first 5:' -## for (level, text, pageNum) in self._lastEntries[0:5]: -## print (level, text[0:30], pageNum), -## print -## print 'Current Entries first 5:' -## for (level, text, pageNum) in self._lastEntries[0:5]: -## print (level, text[0:30], pageNum), - - - def clearEntries(self): - self._entries = [] - - def addEntry(self, level, text, pageNum): - """Adds one entry; allows incremental buildup by a doctemplate. - Requires that enough styles are defined.""" - assert type(level) == type(1), "Level must be an integer" - assert level < len(self.levelStyles), \ - "Table of contents must have a style defined " \ - "for paragraph level %d before you add an entry" % level - self._entries.append((level, text, pageNum)) - - def addEntries(self, listOfEntries): - """Bulk creation. If you knew the titles but - not the page numbers, you could supply them to - get sensible output on the first run.""" - for (level, text, pageNum) in listOfEntries: - self.addEntry(level, text, pageNum) - - def wrap(self, availWidth, availHeight): - """All table properties should be known by now.""" - widths = (availWidth - self.rightColumnWidth, - self.rightColumnWidth) - - # makes an internal table which does all the work. - # we draw the LAST RUN's entries! If there are - # none, we make some dummy data to keep the table - # from complaining - if len(self._lastEntries) == 0: - _tempEntries = [(0,'Placeholder for table of contents',0)] - else: - _tempEntries = self._lastEntries - tableData = [] - for (level, text, pageNum) in _tempEntries: - leftColStyle = self.levelStyles[level] - #right col style is right aligned - rightColStyle = ParagraphStyle(name='leftColLevel%d' % level, - parent=leftColStyle, - leftIndent=0, - alignment=enums.TA_RIGHT) - leftPara = Paragraph(text, leftColStyle) - rightPara = Paragraph(str(pageNum), rightColStyle) - tableData.append([leftPara, rightPara]) - self._table = tables.Table(tableData, colWidths=widths, - style=self.tableStyle) - self.width, self.height = self._table.wrap(availWidth, availHeight) - return (self.width, self.height) - - def split(self, availWidth, availHeight): - """At this stage we do not care about splitting the entries, - we wil just return a list of platypus tables. Presumably the - calling app has a pointer to the original TableOfContents object; - Platypus just sees tables.""" - return self._table.split(availWidth, availHeight) - - def drawOn(self, canvas, x, y, _sW=0): - """Don't do this at home! The standard calls for implementing - draw(); we are hooking this in order to delegate ALL the drawing - work to the embedded table object""" - self._table.drawOn(canvas, x, y, _sW) - - - - ################################################################################# - # - # everything from here down is concerned with creating a good example document - # i.e. test code as well as tutorial -PAGE_HEIGHT = defaultPageSize[1] -def getSampleTOCData(depth=3): - """Returns a longish block of page numbers and headings over 3 levels""" - from random import randint - pgNum = 2 - data = [] - for chapter in range(1,8): - data.append((0, """Chapter %d with a really long name which will hopefully - wrap onto a second line, fnding out if the right things happen with - full paragraphs n the table of contents""" % chapter, pgNum)) - pgNum = pgNum + randint(0,2) - if depth > 1: - for section in range(1,5): - data.append((1, 'Chapter %d Section %d' % (chapter, section), pgNum)) - pgNum = pgNum + randint(0,2) - if depth > 2: - for subSection in range(1,6): - data.append(2, 'Chapter %d Section %d Subsection %d' % - (chapter, section, subSection), - pgNum) - pgNum = pgNum + randint(0,1) - from pprint import pprint as pp - pp(data) - return data - - -def getSampleStory(depth=3): - """Makes a story with lots of paragraphs. Uses the random - TOC data and makes paragraphs to correspond to each.""" - from reportlab.platypus.doctemplate import randomText - from random import randint - - styles = getSampleStyleSheet() - TOCData = getSampleTOCData(depth) - - story = [Paragraph("This is a demo of the table of contents object", - styles['Heading1'])] - - toc = TableOfContents0() # empty on first pass - #toc.addEntries(TOCData) # init with random page numbers - story.append(toc) - - # the next full page should switch to internal page style - story.append(NextPageTemplate("Body")) - - # now make some paragraphs to correspond to it - for (level, text, pageNum) in TOCData: - if level == 0: - #page break before chapter - story.append(PageBreak()) - - headingStyle = (styles['Heading1'], styles['Heading2'], styles['Heading3'])[level] - headingPara = Paragraph(text, headingStyle) - story.append(headingPara) - # now make some body text - for i in range(randint(1,6)): - bodyPara = Paragraph(randomText(), - styles['Normal']) - story.append(bodyPara) - - return story - -class MyDocTemplate(BaseDocTemplate): - """Example of how to do the indexing. Need the onDraw hook - to find out which things are drawn on which pages""" - def afterInit(self): - """Set up the page templates""" - frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal') - self.addPageTemplates([PageTemplate(id='Front',frames=frameT), - PageTemplate(id='Body',frames=frameT) - ]) - # just need a unique key generator for outline entries; - # easiest is to count all flowables in afterFlowable - # and set up a counter variable here - self._uniqueKey = 0 - - - def afterFlowable(self, flowable): - """Our rule for the table of contents is simply to take - the text of H1, H2 and H3 elements. We broadcast a - notification to the DocTemplate, which should inform - the TOC and let it pull them out. Also build an outline""" - self._uniqueKey = self._uniqueKey + 1 - - if hasattr(flowable, 'style'): - if flowable.style.name == 'Heading1': - self.notify('TOCEntry', (0, flowable.getPlainText(), self.page)) - self.canv.bookmarkPage(str(self._uniqueKey)) - self.canv.addOutlineEntry(flowable.getPlainText()[0:10], str(self._uniqueKey), 0) - - elif flowable.style.name == 'Heading2': - self.notify('TOCEntry', (1, flowable.getPlainText(), self.page)) - self.canv.bookmarkPage(str(self._uniqueKey)) - self.canv.addOutlineEntry(flowable.getPlainText(), str(self._uniqueKey), 1) - - elif flowable.style.name == 'Heading3': - self.notify('TOCEntry', (2, flowable.getPlainText(), self.page)) - self.canv.bookmarkPage(str(self._uniqueKey)) - self.canv.addOutlineEntry(flowable.getPlainText(), str(self._uniqueKey), 2) - - def beforePage(self): - """decorate the page""" - self.canv.saveState() - self.canv.setStrokeColor(colors.red) - self.canv.setLineWidth(5) - self.canv.line(66,72,66,PAGE_HEIGHT-72) - self.canv.setFont('Times-Roman',12) - self.canv.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page) - self.canv.restoreState() - -if __name__=='__main__': - from reportlab.platypus import SimpleDocTemplate - doc = MyDocTemplate('tocindex.pdf') - - #change this to depth=3 for a BIG document - story = getSampleStory(depth=2) - - doc.multiBuild(story, 'tocindex.pdf') \ No newline at end of file diff --git a/bin/reportlab/lib/units.py b/bin/reportlab/lib/units.py deleted file mode 100644 index 79067d8462f..00000000000 --- a/bin/reportlab/lib/units.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/units.py -__version__=''' $Id: units.py 2524 2005-02-17 08:43:01Z rgbecker $ ''' - -inch = 72.0 -cm = inch / 2.54 -mm = cm * 0.1 -pica = 12.0 - -def toLength(s): - '''convert a string to a length''' - try: - if s[-2:]=='cm': return float(s[:-2])*cm - if s[-2:]=='in': return float(s[:-2])*inch - if s[-2:]=='pt': return float(s[:-2]) - if s[-1:]=='i': return float(s[:-1])*inch - if s[-2:]=='mm': return float(s[:-2])*mm - if s[-4:]=='pica': return float(s[:-4])*pica - return float(s) - except: - raise ValueError, "Can't convert '%s' to length" % s diff --git a/bin/reportlab/lib/utils.py b/bin/reportlab/lib/utils.py deleted file mode 100644 index c61dc8710a6..00000000000 --- a/bin/reportlab/lib/utils.py +++ /dev/null @@ -1,827 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2006 -#see license.txt for license details -# $URI:$ -__version__=''' $Id: utils.py 2892 2006-05-19 14:16:02Z rgbecker $ ''' - -import string, os, sys, imp -from reportlab.lib.logger import warnOnce -from types import * -from rltempfile import get_rl_tempfile, get_rl_tempdir, _rl_getuid -SeqTypes = (ListType,TupleType) -if sys.hexversion<0x2020000: - def isSeqType(v): - return type(v) in SeqTypes -else: - def isSeqType(v): - return isinstance(v,(tuple,list)) - -if sys.hexversion<0x2030000: - True = 1 - False = 0 - -def _findFiles(dirList,ext='.ttf'): - from os.path import isfile, isdir, join as path_join - from os import listdir - ext = ext.lower() - R = [] - A = R.append - for D in dirList: - if not isdir(D): continue - for fn in listdir(D): - fn = path_join(D,fn) - 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): - def __init__(self,*a,**kw): - map(self.update, a) - self.update(kw) - - def update(self,D): - for k,v in D.items(): self[k] = v - - def __setitem__(self,k,v): - try: - k = k.lower() - except: - pass - _UserDict.__setitem__(self,k,v) - - def __getitem__(self,k): - try: - k = k.lower() - except: - pass - return _UserDict.__getitem__(self,k) - - def __delitem__(self,k): - try: - k = k.lower() - except: - pass - return _UserDict.__delitem__(self,k) - - def get(self,k,dv=None): - try: - return self[k] - except KeyError: - return dv - - def has_key(self,k): - try: - self[k] - return True - except: - return False - - def pop(self,k,*a): - try: - k = k.lower() - except: - pass - return _UserDict.pop(*((self,k)+a)) - - def setdefault(self,k,*a): - try: - k = k.lower() - except: - pass - return _UserDict.setdefault(*((self,k)+a)) - -if os.name == 'mac': - #with the Mac, we need to tag the file in a special - #way so the system knows it is a PDF file. - #This supplied by Joe Strout - import macfs, macostools - _KNOWN_MAC_EXT = { - 'BMP' : ('ogle','BMP '), - 'EPS' : ('ogle','EPSF'), - 'EPSF': ('ogle','EPSF'), - 'GIF' : ('ogle','GIFf'), - 'JPG' : ('ogle','JPEG'), - 'JPEG': ('ogle','JPEG'), - 'PCT' : ('ttxt','PICT'), - 'PICT': ('ttxt','PICT'), - 'PNG' : ('ogle','PNGf'), - 'PPM' : ('ogle','.PPM'), - 'TIF' : ('ogle','TIFF'), - 'TIFF': ('ogle','TIFF'), - 'PDF' : ('CARO','PDF '), - 'HTML': ('MSIE','TEXT'), - } - def markfilename(filename,creatorcode=None,filetype=None,ext='PDF'): - try: - if creatorcode is None or filetype is None and ext is not None: - try: - creatorcode, filetype = _KNOWN_MAC_EXT[string.upper(ext)] - except: - return - macfs.FSSpec(filename).SetCreatorType(creatorcode,filetype) - macostools.touched(filename) - except: - pass -else: - def markfilename(filename,creatorcode=None,filetype=None): - pass - -import reportlab -__RL_DIR=os.path.dirname(reportlab.__file__) #possibly relative -_RL_DIR=os.path.isabs(__RL_DIR) and __RL_DIR or os.path.abspath(__RL_DIR) -del reportlab - -#Attempt to detect if this copy of reportlab is running in a -#file system (as opposed to mostly running in a zip or McMillan -#archive or Jar file). This is used by test cases, so that -#we can write test cases that don't get activated in a compiled -try: - __file__ -except: - __file__ = sys.argv[0] -import glob, fnmatch -try: - _isFSD = not __loader__ - _archive = os.path.normcase(os.path.normpath(__loader__.archive)) - _archivepfx = _archive + os.sep - _archivedir = os.path.dirname(_archive) - _archivedirpfx = _archivedir + os.sep - _archivepfxlen = len(_archivepfx) - _archivedirpfxlen = len(_archivedirpfx) - def __startswith_rl(fn, - _archivepfx=_archivepfx, - _archivedirpfx=_archivedirpfx, - _archive=_archive, - _archivedir=_archivedir, - os_path_normpath=os.path.normpath, - os_path_normcase=os.path.normcase, - os_getcwd=os.getcwd, - os_sep=os.sep, - os_sep_len = len(os.sep)): - '''if the name starts with a known prefix strip it off''' - fn = os_path_normpath(fn.replace('/',os_sep)) - nfn = os_path_normcase(fn) - if nfn in (_archivedir,_archive): return 1,'' - if nfn.startswith(_archivepfx): return 1,fn[_archivepfxlen:] - if nfn.startswith(_archivedirpfx): return 1,fn[_archivedirpfxlen:] - cwd = os_path_normcase(os_getcwd()) - n = len(cwd) - if nfn.startswith(cwd): - if fn[n:].startswith(os_sep): return 1, fn[n+os_sep_len:] - if n==len(fn): return 1,'' - return not os.path.isabs(fn),fn - - def _startswith_rl(fn): - return __startswith_rl(fn)[1] - - def rl_glob(pattern,glob=glob.glob,fnmatch=fnmatch.fnmatch, _RL_DIR=_RL_DIR,pjoin=os.path.join): - 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())) - return r -except: - _isFSD = os.path.isfile(__file__) #slight risk of wrong path - __loader__ = None - def _startswith_rl(fn): - return fn - def rl_glob(pattern,glob=glob.glob): - return glob(pattern) -del glob, fnmatch -_isFSSD = _isFSD and os.path.isfile(os.path.splitext(__file__)[0] +'.py') - -def isFileSystemDistro(): - '''return truth if a file system distribution''' - return _isFSD - -def isCompactDistro(): - '''return truth if not a file system distribution''' - return not _isFSD - -def isSourceDistro(): - '''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): - 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 string.join(s) - -#hack test for comma users -if ',' in fp_str(0.25): - _FP_STR = fp_str - def fp_str(*a): - return string.replace(apply(_FP_STR,a),',','.') - -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) - if baseDir: - if not isSeqType(baseDir): - tp = [baseDir] - else: - tp = filter(None,list(baseDir)) - for p in tp: - p = normalize(p) - if p not in path: path.insert(0,p) - - if noCWD: - for p in ('','.',normalize('.')): - while p in path: - if debug: print 'removed "%s" from path' % p - path.remove(p) - elif '.' not in path: - path.insert(0,'.') - - if debug: - import pprint - pp = pprint.pprint - 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: - sys.path = opath - msg = "recursiveimport(%s,baseDir=%s) failed" % (modulename,baseDir) - if baseDir: - msg = msg + " under paths '%s'" % `path` - raise ImportError, msg - -def recursiveGetAttr(obj, name): - "Can call down into e.g. object1.object2[4].attr" - return eval(name, obj.__dict__) - -def recursiveSetAttr(obj, name, value): - "Can call down into e.g. object1.object2[4].attr = value" - #get the thing above last. - tokens = string.split(name, '.') - if len(tokens) == 1: - setattr(obj, name, value) - else: - most = string.join(tokens[:-1], '.') - last = tokens[-1] - parent = recursiveGetAttr(obj, most) - setattr(parent, last, value) - -def import_zlib(): - try: - import zlib - except ImportError: - zlib = None - from reportlab.rl_config import ZLIB_WARNINGS - if ZLIB_WARNINGS: warnOnce('zlib not available') - return zlib - - -# Image Capability Detection. Set a flag haveImages -# to tell us if either PIL or Java imaging libraries present. -# define PIL_Image as either None, or an alias for the PIL.Image -# module, as there are 2 ways to import it - -if sys.platform[0:4] == 'java': - try: - import javax.imageio - import java.awt.image - haveImages = 1 - except: - haveImages = 0 -else: - try: - from PIL import Image - except ImportError: - try: - import Image - except ImportError: - Image = None - haveImages = Image is not None - if haveImages: del Image - - -__StringIO=None -def getStringIO(buf=None): - '''unified StringIO instance interface''' - global __StringIO - if not __StringIO: - try: - from cStringIO import StringIO - except ImportError: - from StringIO import StringIO - __StringIO = StringIO - return buf is not None and __StringIO(buf) or __StringIO() - -class ArgvDictValue: - '''A type to allow clients of getArgvDict to specify a conversion function''' - def __init__(self,value,func): - self.value = value - self.func = func - -def getArgvDict(**kw): - ''' Builds a dictionary from its keyword arguments with overrides from sys.argv. - Attempts to be smart about conversions, but the value can be an instance - of ArgDictValue to allow specifying a conversion function. - ''' - def handleValue(v,av,func): - if func: - v = func(av) - else: - t = type(v) - if t is StringType: - v = av - elif t is FloatType: - v = float(av) - elif t is IntType: - v = int(av) - elif t is ListType: - v = list(eval(av)) - elif t is TupleType: - v = tuple(eval(av)) - else: - raise TypeError, "Can't convert string '%s' to %s" % (av,str(t)) - return v - - A = sys.argv[1:] - R = {} - for k, v in kw.items(): - if isinstance(v,ArgvDictValue): - v, func = v.value, v.func - else: - func = None - handled = 0 - ke = k+'=' - for a in A: - if string.find(a,ke)==0: - av = a[len(ke):] - A.remove(a) - R[k] = handleValue(v,av,func) - handled = 1 - break - - if not handled: R[k] = handleValue(v,v,func) - - return R - -def getHyphenater(hDict=None): - try: - 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: - if str(errMsg)!='No module named pyHnj': raise - return None - -def _className(self): - '''Return a shortened class name''' - try: - name = self.__class__.__name__ - i=string.rfind(name,'.') - if i>=0: return name[i+1:] - return name - except AttributeError: - return str(self) - -def open_for_read_by_name(name,mode='b'): - if 'r' not in mode: mode = 'r'+mode - try: - return open(name,mode) - except IOError: - if _isFSD or __loader__ is None: raise - #we have a __loader__, perhaps the filename starts with - #the dirname(reportlab.__file__) or is relative - 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) - -import urllib -def open_for_read(name,mode='b', urlopen=urllib.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()) - except: - raise IOError('Cannot open resource "%s"' % name) -del urllib - -def open_and_read(name,mode='b'): - return open_for_read(name,mode).read() - -def open_and_readlines(name,mode='t'): - return open_and_read(name,mode).split('\n') - -def rl_isfile(fn,os_path_isfile=os.path.isfile): - if hasattr(fn,'read'): return True - 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() - -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 - -def rl_get_module(name,dir): - if sys.modules.has_key(name): - om = sys.modules[name] - del sys.modules[name] - else: - om = None - try: - f = None - try: - f, p, desc= imp.find_module(name,[dir]) - return imp.load_module(name,f,p,desc) - except: - if isCompactDistro(): - #attempt a load from inside the zip archive - import zipimport - dir = _startswith_rl(dir) - dir = (dir=='.' or not dir) and _archive or os.path.join(_archive,dir.replace('/',os.sep)) - zi = zipimport.zipimporter(dir) - return zi.load_module(name) - raise ImportError('%s[%s]' % (name,dir)) - finally: - if om: sys.modules[name] = om - del om - if f: f.close() - -def _isPILImage(im): - try: - from PIL.Image import Image - return isinstance(im,Image) - except ImportError: - return 0 - -class ImageReader: - "Wraps up either PIL or Java to get data from bitmaps" - def __init__(self, fileName): - if isinstance(fileName,ImageReader): - self.__dict__ = fileName.__dict__ #borgize - return - if not haveImages: - raise RuntimeError('Imaging Library not available, unable to import bitmaps') - #start wih lots of null private fields, to be populated by - #the relevant engine. - self.fileName = fileName - self._image = None - self._width = None - self._height = None - self._transparent = None - self._data = None - if _isPILImage(fileName): - self._image = fileName - self.fp = fileName.fp - try: - self.fileName = im.fileName - except AttributeError: - self.fileName = 'PILIMAGE_%d' % id(self) - else: - try: - self.fp = open_for_read(fileName,'b') - #detect which library we are using and open the image - if sys.platform[0:4] == 'java': - from javax.imageio import ImageIO - self._image = ImageIO.read(self.fp) - else: - import PIL.Image - self._image = PIL.Image.open(self.fp) - if self._image=='JPEG': self.jpeg_fh = self._jpeg_fp - except: - et,ev,tb = sys.exc_info() - if hasattr(ev,'args'): - a = str(ev.args[-1])+(' fileName='+fileName) - ev.args= ev.args[:-1]+(a,) - raise et,ev,tb - else: - raise - - - def _jpeg_fh(self): - fp = self.fp - fp.seek(0) - return fp - - def jpeg_fh(self): - return None - - def getSize(self): - if (self._width is None or self._height is None): - if sys.platform[0:4] == 'java': - self._width = self._image.getWidth() - self._height = self._image.getHeight() - else: - self._width, self._height = self._image.size - return (self._width, self._height) - - def getRGBData(self): - "Return byte array of RGB data as string" - if self._data is None: - if sys.platform[0:4] == 'java': - import jarray - from java.awt.image import PixelGrabber - width, height = self.getSize() - buffer = jarray.zeros(width*height, 'i') - pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width) - pg.grabPixels() - # there must be a way to do this with a cast not a byte-level loop, - # I just haven't found it yet... - pixels = [] - a = pixels.append - for i in range(len(buffer)): - rgb = buffer[i] - a(chr((rgb>>16)&0xff)) - a(chr((rgb>>8)&0xff)) - a(chr(rgb&0xff)) - self._data = ''.join(pixels) - self.mode = 'RGB' - else: - im = self._image - mode = self.mode = im.mode - if mode not in ('L','RGB','CMYK'): - im = im.convert('RGB') - self.mode = 'RGB' - self._data = im.tostring() - return self._data - - def getImageData(self): - width, height = self.getSize() - return width, height, self.getRGBData() - - def getTransparent(self): - if sys.platform[0:4] == 'java': - return None - else: - if self._image.info.has_key("transparency"): - transparency = self._image.info["transparency"] * 3 - palette = self._image.palette - try: - palette = palette.palette - except: - palette = palette.data - return map(ord, palette[transparency:transparency+3]) - else: - return None - -def getImageData(imageFileName): - "Get width, height and RGB pixels from image file. Wraps Java/PIL" - try: - return imageFileName.getImageData() - except AttributeError: - return ImageReader(imageFileName).getImageData() - -class DebugMemo: - '''Intended as a simple report back encapsulator - - Typical usages - 1) To record error data - dbg = DebugMemo(fn='dbgmemo.dbg',myVar=value) - dbg.add(anotherPayload='aaaa',andagain='bbb') - dbg.dump() - - 2) To show the recorded info - dbg = DebugMemo(fn='dbgmemo.dbg',mode='r') - dbg.load() - dbg.show() - - 3) To re-use recorded information - dbg = DebugMemo(fn='dbgmemo.dbg',mode='r') - dbg.load() - myTestFunc(dbg.payload('myVar'),dbg.payload('andagain')) - - in addition to the payload variables the dump records many useful bits - of information which are also printed in the show() method. - ''' - def __init__(self,fn='rl_dbgmemo.dbg',mode='w',getScript=1,modules=(),capture_traceback=1, stdout=None, **kw): - import time, socket - self.fn = fn - if mode!='w': return - if not stdout: - self.stdout = sys.stdout - else: - if hasattr(stdout,'write'): - self.stdout = stdout - else: - self.stdout = open(stdout,'w') - self.store = store = {} - if capture_traceback and sys.exc_info() != (None,None,None): - import traceback - s = getStringIO() - traceback.print_exc(None,s) - store['__traceback'] = s.getvalue() - cwd=os.getcwd() - lcwd = os.listdir(cwd) - exed = os.path.abspath(os.path.dirname(sys.argv[0])) - store.update({ 'gmt': time.asctime(time.gmtime(time.time())), - 'platform': sys.platform, - 'version': sys.version, - 'hexversion': hex(sys.hexversion), - 'executable': sys.executable, - 'exec_prefix': sys.exec_prefix, - 'prefix': sys.prefix, - 'path': sys.path, - 'argv': sys.argv, - 'cwd': cwd, - 'hostname': socket.gethostname(), - 'lcwd': lcwd, - 'byteorder': sys.byteorder, - 'maxint': sys.maxint, - 'maxint': getattr(sys,'maxunicode','????'), - 'api_version': getattr(sys,'api_version','????'), - 'version_info': getattr(sys,'version_info','????'), - 'winver': getattr(sys,'winver','????'), - 'environment': os.environ, - }) - for M,A in ( - (sys,('getwindowsversion','getfilesystemencoding')), - (os,('uname', 'ctermid', 'getgid', 'getuid', 'getegid', - 'geteuid', 'getlogin', 'getgroups', 'getpgrp', 'getpid', 'getppid', - )), - ): - for a in A: - if hasattr(M,a): - try: - store[a] = getattr(M,a)() - except: - pass - if exed!=cwd: - try: - store.update({'exed': exed, 'lexed': os.listdir(exed),}) - except: - pass - if getScript: - fn = os.path.abspath(sys.argv[0]) - if os.path.isfile(fn): - try: - store['__script'] = (fn,open(fn,'r').read()) - except: - pass - module_versions = {} - for n,m in sys.modules.items(): - if n=='reportlab' or n=='rlextra' or n[:10]=='reportlab.' or n[:8]=='rlextra.': - v = getattr(m,'__version__',None) - if v: module_versions[n] = v - store['__module_versions'] = module_versions - self.store['__payload'] = {} - self._add(kw) - - def _add(self,D): - payload = self.store['__payload'] - for k, v in D.items(): - payload[k] = v - - def add(self,**kw): - self._add(kw) - - def dump(self): - import pickle - pickle.dump(self.store,open(self.fn,'wb')) - - def load(self): - import pickle - self.store = pickle.load(open(self.fn,'rb')) - - def _show_module_versions(self,k,v): - self._writeln(k[2:]) - K = v.keys() - K.sort() - for k in K: - vk = v[k] - try: - m = recursiveImport(k,sys.path[:],1) - d = getattr(m,'__version__',None)==vk and 'SAME' or 'DIFFERENT' - except: - m = None - d = '??????unknown??????' - self._writeln(' %s = %s (%s)' % (k,vk,d)) - - def _banner(self,k,what): - self._writeln('###################%s %s##################' % (what,k[2:])) - - def _start(self,k): - self._banner(k,'Start ') - - def _finish(self,k): - self._banner(k,'Finish ') - - def _show_lines(self,k,v): - self._start(k) - self._writeln(v) - self._finish(k) - - def _show_file(self,k,v): - k = '%s %s' % (k,os.path.basename(v[0])) - self._show_lines(k,v[1]) - - def _show_payload(self,k,v): - if v: - import pprint - self._start(k) - pprint.pprint(v,self.stdout) - self._finish(k) - - specials = {'__module_versions': _show_module_versions, - '__payload': _show_payload, - '__traceback': _show_lines, - '__script': _show_file, - } - def show(self): - K = self.store.keys() - K.sort() - for k in K: - if k not in self.specials.keys(): self._writeln('%-15s = %s' % (k,self.store[k])) - for k in K: - if k in self.specials.keys(): apply(self.specials[k],(self,k,self.store[k])) - - def payload(self,name): - return self.store['__payload'][name] - - def __setitem__(self,name,value): - self.store['__payload'][name] = value - - def __getitem__(self,name): - return self.store['__payload'][name] - - def _writeln(self,msg): - self.stdout.write(msg+'\n') - -def _flatten(L,a): - for x in L: - if isSeqType(x): _flatten(x,a) - else: a(x) - -def flatten(L): - '''recursively flatten the list or tuple L''' - R = [] - _flatten(L,R.append) - return R - -def find_locals(func,depth=0): - '''apply func to the locals at each stack frame till func returns a non false value''' - while 1: - _ = func(sys._getframe(depth).f_locals) - if _: return _ - depth += 1 - -class _FmtSelfDict: - def __init__(self,obj,overrideArgs): - self.obj = obj - self._overrideArgs = overrideArgs - def __getitem__(self,k): - try: - return self._overrideArgs[k] - except KeyError: - try: - return self.obj.__dict__[k] - except KeyError: - return getattr(self.obj,k) - -class FmtSelfDict: - '''mixin to provide the _fmt method''' - def _fmt(self,fmt,**overrideArgs): - D = _FmtSelfDict(self, overrideArgs) - return fmt % D diff --git a/bin/reportlab/lib/validators.py b/bin/reportlab/lib/validators.py deleted file mode 100644 index 3f7f06edb79..00000000000 --- a/bin/reportlab/lib/validators.py +++ /dev/null @@ -1,324 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/validators.py -__version__=''' $Id: validators.py 2875 2006-05-18 07:00:16Z andy $ ''' -""" -This module contains some standard verifying functions which can be -used in an attribute map. -""" - -import string, sys, codecs -from types import * -_SequenceTypes = (ListType,TupleType) -_NumberTypes = (FloatType,IntType) -from reportlab.lib import colors -if sys.hexversion<0x2030000: - True = 1 - False = 0 - -class Validator: - "base validator class" - def __call__(self,x): - return self.test(x) - - def __str__(self): - return getattr(self,'_str',self.__class__.__name__) - - def normalize(self,x): - return x - - def normalizeTest(self,x): - try: - self.normalize(x) - return True - except: - return False - -class _isAnything(Validator): - def test(self,x): - return True - -class _isNothing(Validator): - def test(self,x): - 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 normalize(self,x): - if x in (0,1): return x - try: - S = string.upper(x) - except: - raise ValueError, 'Must be boolean' - if S in ('YES','TRUE'): return True - if S in ('NO','FALSE',None): return False - raise ValueError, 'Must be boolean' - -class _isString(Validator): - def test(self,x): - return type(x) in (StringType, UnicodeType) - -class _isCodec(Validator): - def test(self,x): - if type(x) not in (StringType, UnicodeType): - return False - try: - a,b,c,d = codecs.lookup(x) - return True - except LookupError: - return False - -class _isNumber(Validator): - def test(self,x): - if type(x) in _NumberTypes: return True - return self.normalizeTest(x) - - def normalize(self,x): - try: - return float(x) - except: - return int(x) - -class _isInt(Validator): - def test(self,x): - if type(x) not in (IntType,StringType): return False - return self.normalizeTest(x) - - def normalize(self,x): - return int(x) - -class _isNumberOrNone(_isNumber): - def test(self,x): - return x is None or isNumber(x) - - def normalize(self,x): - if x is None: return x - return _isNumber.normalize(x) - -class _isListOfNumbersOrNone(Validator): - "ListOfNumbersOrNone validator class." - def test(self, x): - if x is None: return True - return isListOfNumbers(x) - -class _isListOfShapes(Validator): - "ListOfShapes validator class." - def test(self, x): - from reportlab.graphics.shapes import Shape - if type(x) in _SequenceTypes: - answer = 1 - for element in x: - if not isinstance(x, Shape): - answer = 0 - return answer - else: - return False - -class _isListOfStringsOrNone(Validator): - "ListOfStringsOrNone validator class." - - def test(self, x): - if x is None: return True - return isListOfStrings(x) - -class _isTransform(Validator): - "Transform validator class." - def test(self, x): - if type(x) in _SequenceTypes: - if len(x) == 6: - for element in x: - if not isNumber(element): - return False - return True - else: - return False - else: - return False - -class _isColor(Validator): - "Color validator class." - def test(self, x): - return isinstance(x, colors.Color) - -class _isColorOrNone(Validator): - "ColorOrNone validator class." - def test(self, x): - if x is None: return True - return isColor(x) - -class _isValidChild(Validator): - "ValidChild validator class." - def test(self, x): - """Is this child allowed in a drawing or group? - I.e. does it descend from Shape or UserNode? - """ - - from reportlab.graphics.shapes import UserNode, Shape - return isinstance(x, UserNode) or isinstance(x, Shape) - -class _isValidChildOrNone(_isValidChild): - def test(self,x): - return _isValidChild.test(self,x) or x is None - -class _isCallable(Validator): - def test(self, x): - return callable(x) - -class OneOf(Validator): - """Make validator functions for list of choices. - - Usage: - f = reportlab.lib.validators.OneOf('happy','sad') - or - f = reportlab.lib.validators.OneOf(('happy','sad')) - f('sad'),f('happy'), f('grumpy') - (1,1,0) - """ - def __init__(self, enum,*args): - if type(enum) in [ListType,TupleType]: - if args!=(): - raise ValueError, "Either all singleton args or a single sequence argument" - self._enum = tuple(enum)+args - else: - self._enum = (enum,)+args - - def test(self, x): - return x in self._enum - -class SequenceOf(Validator): - def __init__(self,elemTest,name=None,emptyOK=1, NoneOK=0, lo=0,hi=0x7fffffff): - self._elemTest = elemTest - self._emptyOK = emptyOK - self._NoneOK = NoneOK - self._lo, self._hi = lo, hi - if name: self._str = name - - def test(self, x): - if type(x) not in _SequenceTypes: - if x is None: return self._NoneOK - return False - if x==[] or x==(): - return self._emptyOK - elif not self._lo<=len(x)<=self._hi: return False - for e in x: - if not self._elemTest(e): return False - return True - -class EitherOr(Validator): - def __init__(self,tests,name=None): - if type(tests) not in _SequenceTypes: tests = (tests,) - self._tests = tests - if name: self._str = name - - def test(self, x): - for t in self._tests: - if t(x): return True - return False - -class NoneOr(Validator): - def __init__(self,elemTest,name=None): - self._elemTest = elemTest - if name: self._str = name - - def test(self, x): - if x is None: return True - return self._elemTest(x) - -class Auto(Validator): - def __init__(self,**kw): - self.__dict__.update(kw) - - def test(self,x): - return x is self.__class__ or isinstance(x,self.__class__) - -class AutoOr(NoneOr): - def test(self,x): - return isAuto(x) or self._elemTest(x) - -class isInstanceOf(Validator): - def __init__(self,klass=None): - self._klass = klass - def test(self,x): - return isinstance(x,self._klass) - -class matchesPattern(Validator): - """Matches value, or its string representation, against regex""" - def __init__(self, pattern): - 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) - -class DerivedValue: - """This is used for magic values which work themselves out. - An example would be an "inherit" property, so that one can have - - drawing.chart.categoryAxis.labels.fontName = inherit - - and pick up the value from the top of the drawing. - Validators will permit this provided that a value can be pulled - in which satisfies it. And the renderer will have special - knowledge of these so they can evaluate themselves. - """ - def getValue(self, renderer, attr): - """Override this. The renderers will pass the renderer, - and the attribute name. Algorithms can then backtrack up - through all the stuff the renderer provides, including - a correct stack of parent nodes.""" - return None - -class Inherit(DerivedValue): - def __repr__(self): - return "inherit" - - def getValue(self, renderer, attr): - return renderer.getStateValue(attr) - -inherit = Inherit() - - -isAuto = Auto() -isBoolean = _isBoolean() -isString = _isString() -isCodec = _isCodec() -isNumber = _isNumber() -isInt = _isInt() -isNoneOrInt = NoneOr(isInt,'isNoneOrInt') -isNumberOrNone = _isNumberOrNone() -isTextAnchor = OneOf('start','middle','end','boxauto') -isListOfNumbers = SequenceOf(isNumber,'isListOfNumbers') -isListOfNumbersOrNone = _isListOfNumbersOrNone() -isListOfShapes = _isListOfShapes() -isListOfStrings = SequenceOf(isString,'isListOfStrings') -isListOfStringsOrNone = _isListOfStringsOrNone() -isTransform = _isTransform() -isColor = _isColor() -isListOfColors = SequenceOf(isColor,'isListOfColors') -isColorOrNone = _isColorOrNone() -isShape = isValidChild = _isValidChild() -isNoneOrShape = isValidChildOrNone = _isValidChildOrNone() -isAnything = _isAnything() -isNothing = _isNothing() -isXYCoord = SequenceOf(isNumber,lo=2,hi=2,emptyOK=0) -isBoxAnchor = OneOf('nw','n','ne','w','c','e','sw','s','se', 'autox', 'autoy') -isNoneOrString = NoneOr(isString,'NoneOrString') -isNoneOrListOfNoneOrStrings=SequenceOf(isNoneOrString,'isNoneOrListOfNoneOrStrings',NoneOK=1) -isListOfNoneOrString=SequenceOf(isNoneOrString,'isListOfNoneOrString',NoneOK=0) -isNoneOrListOfNoneOrNumbers=SequenceOf(isNumberOrNone,'isNoneOrListOfNoneOrNumbers',NoneOK=1) -isCallable = _isCallable() -isStringOrCallable=EitherOr((isString,isCallable),'isStringOrCallable') -isStringOrCallableOrNone=NoneOr(isStringOrCallable,'isStringOrCallableNone') -isStringOrNone=NoneOr(isString,'isStringOrNone') diff --git a/bin/reportlab/lib/xmllib.py b/bin/reportlab/lib/xmllib.py deleted file mode 100644 index 041646fbb42..00000000000 --- a/bin/reportlab/lib/xmllib.py +++ /dev/null @@ -1,769 +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) - -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 -# and , 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 + ')[^-a-zA-Z0-9._:]') -charref = re.compile('&#(?P[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('/?)>') -endbracket = re.compile('>') -tagfind = re.compile(_Name) -cdataopen = re.compile('') -special = re.compile('[^<>]*)>') -procopen = re.compile('<\?(?P' + _Name + ')' + _S) -procclose = re.compile('\?>') -commentopen = re.compile('') -doubledash = re.compile('--') -attrfind = re.compile( - _opS + '(?P' + _Name + ')' - '(' + _opS + '=' + _opS + - '(?P\'[^\']*\'|"[^"]*"|[-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) - self.lineno = self.lineno + string.count(data, '\n') - i = i+1 - continue - k = self.parse_comment(i) - if k < 0: break - self.lineno = self.lineno + string.count(rawdata[i:k], '\n') - i = k - continue - if cdataopen.match(rawdata, i): - k = self.parse_cdata(i) - if k < 0: break - self.lineno = self.lineno + string.count(rawdata[i:i], '\n') - i = k - continue - res = procopen.match(rawdata, i) - if res: - k = self.parse_proc(i, res) - if k < 0: break - self.lineno = self.lineno + string.count(rawdata[i:k], '\n') - i = k - continue - res = special.match(rawdata, i) - if res: - if self.literal: - data = rawdata[i] - self.handle_data(data) - self.lineno = self.lineno + string.count(data, '\n') - i = i+1 - continue - self.handle_special(res.group('special')) - self.lineno = self.lineno + string.count(res.group(0), '\n') - i = res.end(0) - continue - elif rawdata[i] == '&': - res = charref.match(rawdata, i) - if res is not None: - i = res.end(0) - if rawdata[i-1] != ';': - self.syntax_error(self.lineno, '; missing in charref') - i = i-1 - self.handle_charref(res.group('char')[:-1]) - self.lineno = self.lineno + string.count(res.group(0), '\n') - continue - res = entityref.match(rawdata, i) - if res is not None: - i = res.end(0) - if rawdata[i-1] != ';': - self.syntax_error(self.lineno, '; missing in entityref') - i = i-1 - self.handle_entityref(res.group('name')) - self.lineno = self.lineno + string.count(res.group(0), '\n') - continue - else: - raise RuntimeError, 'neither < nor & ??' - # We get here only if incomplete matches but - # nothing else - res = incomplete.match(rawdata, i) - if not res: - data = rawdata[i] - self.handle_data(data) - self.lineno = self.lineno + string.count(data, '\n') - i = i+1 - continue - j = res.end(0) - if j == n: - break # Really incomplete - self.syntax_error(self.lineno, 'bogus < or &') - data = res.group(0) - self.handle_data(data) - self.lineno = self.lineno + string.count(data, '\n') - i = j - # end while - if end and i < n: - data = rawdata[i:n] - self.handle_data(data) - self.lineno = self.lineno + string.count(data, '\n') - i = n - self.rawdata = rawdata[i:] - # XXX if end: check for empty stack - - # Internal -- parse comment, return length or -1 if not terminated - def parse_comment(self, i): - rawdata = self.rawdata - if rawdata[i:i+4] <> ' width of glyph - widthVectorsByFont fontName -> vector of widths -""" -import string, UserDict, os, sys - -# mapping of name to width vector, starts empty until fonts are added -# e.g. widths['Courier'] = [...600,600,600,...] -widthVectorsByFont = {} -fontsByName = {} -fontsByBaseEnc = {} -# this is a list of the standard 14 font names in Acrobat Reader -standardFonts = ( - 'Courier', 'Courier-Bold', 'Courier-Oblique', 'Courier-BoldOblique', - 'Helvetica', 'Helvetica-Bold', 'Helvetica-Oblique', 'Helvetica-BoldOblique', - 'Times-Roman', 'Times-Bold', 'Times-Italic', 'Times-BoldItalic', - 'Symbol','ZapfDingbats') - -standardFontAttributes = { - #family, bold, italic defined for basic ones - 'Courier':('Courier',0,0), - 'Courier-Bold':('Courier',1,0), - 'Courier-Oblique':('Courier',0,1), - 'Courier-BoldOblique':('Courier',1,1), - - 'Helvetica':('Helvetica',0,0), - 'Helvetica-Bold':('Helvetica',1,0), - 'Helvetica-Oblique':('Helvetica',0,1), - 'Helvetica-BoldOblique':('Helvetica',1,1), - - 'Times-Roman':('Times-Roman',0,0), - 'Times-Bold':('Times-Roman',1,0), - 'Times-Italic':('Times-Roman',0,1), - 'Times-BoldItalic':('Times-Roman',1,1), - - 'Symbol':('Symbol',0,0), - 'ZapfDingbats':('ZapfDingbats',0,0) - - } - -#this maps fontnames to the equivalent filename root. -_font2fnrMapWin32 = { - 'symbol': 'Sy______', - 'zapfdingbats': 'Zd______', - 'helvetica': '_a______', - 'helvetica-bold': '_ab_____', - 'helvetica-boldoblique': '_abi____', - 'helvetica-oblique': '_ai_____', - 'times-bold': '_eb_____', - 'times-bolditalic': '_ebi____', - 'times-italic': '_ei_____', - 'times-roman': '_er_____', - 'courier-bold': 'cob_____', - 'courier-boldoblique': 'cobo____', - 'courier': 'com_____', - 'courier-oblique': 'coo_____', - } -if sys.platform in ('linux2',): - _font2fnrMapLinux2 ={ - 'symbol': 'Symbol', - 'zapfdingbats': 'ZapfDingbats', - 'helvetica': 'Arial', - 'helvetica-bold': 'Arial-Bold', - 'helvetica-boldoblique': 'Arial-BoldItalic', - 'helvetica-oblique': 'Arial-Italic', - 'times-bold': 'TimesNewRoman-Bold', - 'times-bolditalic':'TimesNewRoman-BoldItalic', - 'times-italic': 'TimesNewRoman-Italic', - 'times-roman': 'TimesNewRoman', - 'courier-bold': 'Courier-Bold', - 'courier-boldoblique': 'Courier-BoldOblique', - 'courier': 'Courier', - 'courier-oblique': 'Courier-Oblique', - } - _font2fnrMap = _font2fnrMapLinux2 - _revmap = None -else: - _font2fnrMap = _font2fnrMapWin32 - -def _findFNR(fontName): - return _font2fnrMap[string.lower(fontName)] - -from reportlab.rl_config import T1SearchPath -from reportlab.lib.utils import rl_isfile -def _searchT1Dirs(n,rl_isfile=rl_isfile,T1SearchPath=T1SearchPath): - assert T1SearchPath!=[], "No Type-1 font search path" - for d in T1SearchPath: - f = os.path.join(d,n) - if rl_isfile(f): return f - return None -del T1SearchPath, rl_isfile - -def findT1File(fontName,ext='.pfb'): - if sys.platform in ('linux2',) and ext=='.pfb': - try: - f = _searchT1Dirs(_findFNR(fontName)) - if f: return f - except: - pass - global _revmap - if not _revmap: - for k, v in _font2fnrMap.items(): - if k in _font2fnrMapWin32.keys(): - _font2fnrMapWin32[string.lower(v)] = _font2fnrMapWin32[k] - revmap = 1 - try: - f = _searchT1Dirs(_font2fnrMapWin32[string.lower(fontName)]+ext) - if f: return f - except: - pass - - return _searchT1Dirs(_findFNR(fontName)+ext) - -# this lists the predefined font encodings - WinAnsi and MacRoman. We have -# not added MacExpert - it's possible, but would complicate life and nobody -# is asking. StandardEncoding means something special. -standardEncodings = ('WinAnsiEncoding','MacRomanEncoding','StandardEncoding','SymbolEncoding','ZapfDingbatsEncoding','PDFDocEncoding', 'MacExpertEncoding') - -#this is the global mapping of standard encodings to name vectors -class _Name2StandardEncodingMap(UserDict.UserDict): - '''Trivial fake dictionary with some [] magic''' - _XMap = {'winansi':'WinAnsiEncoding','macroman': 'MacRomanEncoding','standard':'StandardEncoding','symbol':'SymbolEncoding', 'zapfdingbats':'ZapfDingbatsEncoding','pdfdoc':'PDFDocEncoding', 'macexpert':'MacExpertEncoding'} - def __setitem__(self,x,v): - y = string.lower(x) - if y[-8:]=='encoding': y = y[:-8] - y = self._XMap[y] - if y in self.keys(): raise IndexError, 'Encoding %s is already set' % y - self.data[y] = v - - def __getitem__(self,x): - y = string.lower(x) - if y[-8:]=='encoding': y = y[:-8] - y = self._XMap[y] - return self.data[y] - -encodings = _Name2StandardEncodingMap() -encodings['WinAnsiEncoding'] = ( - None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, 'space', 'exclam', - 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', - 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', - 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', - 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', - 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', - 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', - 'asciitilde', 'bullet', 'Euro', 'bullet', 'quotesinglbase', 'florin', - 'quotedblbase', 'ellipsis', 'dagger', 'daggerdbl', 'circumflex', - 'perthousand', 'Scaron', 'guilsinglleft', 'OE', 'bullet', 'Zcaron', - 'bullet', 'bullet', 'quoteleft', 'quoteright', 'quotedblleft', - 'quotedblright', 'bullet', 'endash', 'emdash', 'tilde', 'trademark', - 'scaron', 'guilsinglright', 'oe', 'bullet', 'zcaron', 'Ydieresis', - 'space', 'exclamdown', 'cent', 'sterling', 'currency', 'yen', 'brokenbar', - 'section', 'dieresis', 'copyright', 'ordfeminine', 'guillemotleft', - 'logicalnot', 'hyphen', 'registered', 'macron', 'degree', 'plusminus', - 'twosuperior', 'threesuperior', 'acute', 'mu', 'paragraph', 'periodcentered', - 'cedilla', 'onesuperior', 'ordmasculine', 'guillemotright', 'onequarter', - 'onehalf', 'threequarters', 'questiondown', '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') - -encodings['MacRomanEncoding'] = ( - None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, 'space', 'exclam', - 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', - 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', - 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', - 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', - 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', - 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', - 'asciitilde', None, 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', - 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', 'agrave', 'acircumflex', - 'adieresis', 'atilde', 'aring', 'ccedilla', 'eacute', 'egrave', - 'ecircumflex', 'edieresis', 'iacute', 'igrave', 'icircumflex', - 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', 'odieresis', - 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', 'dagger', - 'degree', 'cent', 'sterling', 'section', 'bullet', 'paragraph', - 'germandbls', 'registered', 'copyright', 'trademark', 'acute', - 'dieresis', None, 'AE', 'Oslash', None, 'plusminus', None, None, 'yen', - 'mu', None, None, None, None, None, 'ordfeminine', 'ordmasculine', None, - 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', None, 'florin', - None, None, 'guillemotleft', 'guillemotright', 'ellipsis', 'space', 'Agrave', - 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', - 'quotedblright', 'quoteleft', 'quoteright', 'divide', None, 'ydieresis', - 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'guilsinglright', - 'fi', 'fl', 'daggerdbl', 'periodcentered', 'quotesinglbase', - 'quotedblbase', 'perthousand', 'Acircumflex', 'Ecircumflex', 'Aacute', - 'Edieresis', 'Egrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Igrave', - 'Oacute', 'Ocircumflex', None, 'Ograve', 'Uacute', 'Ucircumflex', - 'Ugrave', 'dotlessi', 'circumflex', 'tilde', 'macron', 'breve', - 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', 'ogonek', 'caron') -encodings['SymbolEncoding']=(None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, 'space', - 'exclam', 'universal', 'numbersign', 'existential', 'percent', 'ampersand', 'suchthat', - 'parenleft', 'parenright', 'asteriskmath', 'plus', 'comma', 'minus', 'period', 'slash', 'zero', - 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', - 'less', 'equal', 'greater', 'question', 'congruent', 'Alpha', 'Beta', 'Chi', 'Delta', 'Epsilon', - 'Phi', 'Gamma', 'Eta', 'Iota', 'theta1', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Omicron', 'Pi', 'Theta', - 'Rho', 'Sigma', 'Tau', 'Upsilon', 'sigma1', 'Omega', 'Xi', 'Psi', 'Zeta', 'bracketleft', - 'therefore', 'bracketright', 'perpendicular', 'underscore', 'radicalex', 'alpha', 'beta', 'chi', - 'delta', 'epsilon', 'phi', 'gamma', 'eta', 'iota', 'phi1', 'kappa', 'lambda', 'mu', 'nu', - 'omicron', 'pi', 'theta', 'rho', 'sigma', 'tau', 'upsilon', 'omega1', 'omega', 'xi', 'psi', 'zeta', - 'braceleft', 'bar', 'braceright', 'similar', None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, 'Euro', 'Upsilon1', 'minute', 'lessequal', - 'fraction', 'infinity', 'florin', 'club', 'diamond', 'heart', 'spade', 'arrowboth', 'arrowleft', - 'arrowup', 'arrowright', 'arrowdown', 'degree', 'plusminus', 'second', 'greaterequal', 'multiply', - 'proportional', 'partialdiff', 'bullet', 'divide', 'notequal', 'equivalence', 'approxequal', - 'ellipsis', 'arrowvertex', 'arrowhorizex', 'carriagereturn', 'aleph', 'Ifraktur', 'Rfraktur', - 'weierstrass', 'circlemultiply', 'circleplus', 'emptyset', 'intersection', 'union', - 'propersuperset', 'reflexsuperset', 'notsubset', 'propersubset', 'reflexsubset', 'element', - 'notelement', 'angle', 'gradient', 'registerserif', 'copyrightserif', 'trademarkserif', 'product', - 'radical', 'dotmath', 'logicalnot', 'logicaland', 'logicalor', 'arrowdblboth', 'arrowdblleft', - 'arrowdblup', 'arrowdblright', 'arrowdbldown', 'lozenge', 'angleleft', 'registersans', - 'copyrightsans', 'trademarksans', 'summation', 'parenlefttp', 'parenleftex', 'parenleftbt', - 'bracketlefttp', 'bracketleftex', 'bracketleftbt', 'bracelefttp', 'braceleftmid', 'braceleftbt', - 'braceex', None, 'angleright', 'integral', 'integraltp', 'integralex', 'integralbt', - 'parenrighttp', 'parenrightex', 'parenrightbt', 'bracketrighttp', 'bracketrightex', - 'bracketrightbt', 'bracerighttp', 'bracerightmid', 'bracerightbt', None) -encodings['ZapfDingbatsEncoding'] = ( None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - 'space', 'a1', 'a2', 'a202', 'a3', 'a4', 'a5', 'a119', 'a118', 'a117', 'a11', 'a12', 'a13', 'a14', - 'a15', 'a16', 'a105', 'a17', 'a18', 'a19', 'a20', 'a21', 'a22', 'a23', 'a24', 'a25', 'a26', 'a27', - 'a28', 'a6', 'a7', 'a8', 'a9', 'a10', 'a29', 'a30', 'a31', 'a32', 'a33', 'a34', 'a35', 'a36', - 'a37', 'a38', 'a39', 'a40', 'a41', 'a42', 'a43', 'a44', 'a45', 'a46', 'a47', 'a48', 'a49', 'a50', - 'a51', 'a52', 'a53', 'a54', 'a55', 'a56', 'a57', 'a58', 'a59', 'a60', 'a61', 'a62', 'a63', 'a64', - 'a65', 'a66', 'a67', 'a68', 'a69', 'a70', 'a71', 'a72', 'a73', 'a74', 'a203', 'a75', 'a204', 'a76', - 'a77', 'a78', 'a79', 'a81', 'a82', 'a83', 'a84', 'a97', 'a98', 'a99', 'a100', None, 'a89', 'a90', - 'a93', 'a94', 'a91', 'a92', 'a205', 'a85', 'a206', 'a86', 'a87', 'a88', 'a95', 'a96', None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, 'a101', 'a102', 'a103', 'a104', 'a106', 'a107', 'a108', 'a112', 'a111', 'a110', 'a109', - 'a120', 'a121', 'a122', 'a123', 'a124', 'a125', 'a126', 'a127', 'a128', 'a129', 'a130', 'a131', - 'a132', 'a133', 'a134', 'a135', 'a136', 'a137', 'a138', 'a139', 'a140', 'a141', 'a142', 'a143', - 'a144', 'a145', 'a146', 'a147', 'a148', 'a149', 'a150', 'a151', 'a152', 'a153', 'a154', 'a155', - 'a156', 'a157', 'a158', 'a159', 'a160', 'a161', 'a163', 'a164', 'a196', 'a165', 'a192', 'a166', - 'a167', 'a168', 'a169', 'a170', 'a171', 'a172', 'a173', 'a162', 'a174', 'a175', 'a176', 'a177', - 'a178', 'a179', 'a193', 'a180', 'a199', 'a181', 'a200', 'a182', None, 'a201', 'a183', 'a184', - 'a197', 'a185', 'a194', 'a198', 'a186', 'a195', 'a187', 'a188', 'a189', 'a190', 'a191', None) -encodings['StandardEncoding']=(None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,"space","exclam", - "quotedbl","numbersign","dollar","percent","ampersand","quoteright","parenleft","parenright","asterisk","plus", - "comma","hyphen","period","slash","zero","one","two","three","four","five","six","seven","eight","nine","colon", - "semicolon","less","equal","greater","question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O", - "P","Q","R","S","T","U","V","W","X","Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore", - "quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y", - "z","braceleft","bar","braceright","asciitilde",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None, - None,None,None,"exclamdown","cent","sterling","fraction","yen","florin","section","currency","quotesingle","quotedblleft", - "guillemotleft","guilsinglleft","guilsinglright","fi","fl",None,"endash","dagger","daggerdbl","periodcentered",None, - "paragraph","bullet","quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis","perthousand", - None,"questiondown",None,"grave","acute","circumflex","tilde","macron","breve","dotaccent","dieresis",None,"ring", - "cedilla",None,"hungarumlaut","ogonek","caron","emdash",None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,"AE",None,"ordfeminine", - None,None,None,None,"Lslash","Oslash","OE","ordmasculine",None,None,None,None,None,"ae",None,None,None,"dotlessi",None,None,"lslash","oslash", - "oe","germandbls",None,None,None,None) -encodings['PDFDocEncoding']=(None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None,None, - None,None,None,None,None,"breve","caron","circumflex", - "dotaccent","hungarumlaut","ogonek","ring","tilde","space","exclam","quotedbl","numbersign","dollar","percent", - "ampersand","quotesingle","parenleft","parenright","asterisk","plus","comma","hyphen","period","slash","zero", - "one","two","three","four","five","six","seven","eight","nine","colon","semicolon","less","equal","greater", - "question","at","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X", - "Y","Z","bracketleft","backslash","bracketright","asciicircum","underscore","grave","a","b","c","d","e","f","g", - "h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","braceleft","bar","braceright", - "asciitilde",None,"bullet","dagger","daggerdbl","ellipsis","emdash","endash","florin","fraction","guilsinglleft", - "guilsinglright","minus","perthousand","quotedblbase","quotedblleft","quotedblright","quoteleft","quoteright", - "quotesinglbase","trademark","fi","fl","Lslash","OE","Scaron","Ydieresis","Zcaron","dotlessi","lslash","oe", - "scaron","zcaron",None,"Euro","exclamdown","cent","sterling","currency","yen","brokenbar","section","dieresis", - "copyright","ordfeminine","guillemotleft","logicalnot",None,"registered","macron","degree","plusminus","twosuperior", - "threesuperior","acute","mu","paragraph","periodcentered","cedilla","onesuperior","ordmasculine","guillemotright", - "onequarter","onehalf","threequarters","questiondown","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") -encodings['MacExpertEncoding'] = (None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, - 'space', 'exclamsmall', 'Hungarumlautsmall', 'centoldstyle', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', - 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', - 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', - 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', None, - 'threequartersemdash', None, 'questionsmall', None, None, None, None, 'Ethsmall', None, None, 'onequarter', - 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', 'twothirds', - None, None, None, None, None, None, 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', None, - 'parenrightinferior', 'Circumflexsmall', 'hypheninferior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', - 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', - 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', - 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', None, None, 'asuperior', 'centsuperior', None, None, None, - None, 'Aacutesmall', 'Agravesmall', 'Acircumflexsmall', 'Adieresissmall', 'Atildesmall', 'Aringsmall', - 'Ccedillasmall', 'Eacutesmall', 'Egravesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Iacutesmall', 'Igravesmall', - 'Icircumflexsmall', 'Idieresissmall', 'Ntildesmall', 'Oacutesmall', 'Ogravesmall', 'Ocircumflexsmall', - 'Odieresissmall', 'Otildesmall', 'Uacutesmall', 'Ugravesmall', 'Ucircumflexsmall', 'Udieresissmall', None, - 'eightsuperior', 'fourinferior', 'threeinferior', 'sixinferior', 'eightinferior', 'seveninferior', 'Scaronsmall', - None, 'centinferior', 'twoinferior', None, 'Dieresissmall', None, 'Caronsmall', 'osuperior', 'fiveinferior', None, - 'commainferior', 'periodinferior', 'Yacutesmall', None, 'dollarinferior', None, None, 'Thornsmall', None, - 'nineinferior', 'zeroinferior', 'Zcaronsmall', 'AEsmall', 'Oslashsmall', 'questiondownsmall', 'oneinferior', - 'Lslashsmall', None, None, None, None, None, None, 'Cedillasmall', None, None, None, None, None, 'OEsmall', - 'figuredash', 'hyphensuperior', None, None, None, None, 'exclamdownsmall', None, 'Ydieresissmall', None, - 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', - 'ninesuperior', 'zerosuperior', None, 'esuperior', 'rsuperior', 'tsuperior', None, None, 'isuperior', 'ssuperior', - 'dsuperior', None, None, None, None, None, 'lsuperior', 'Ogoneksmall', 'Brevesmall', 'Macronsmall', 'bsuperior', - 'nsuperior', 'msuperior', 'commasuperior', 'periodsuperior', 'Dotaccentsmall', 'Ringsmall', None, None, None, None) -ascent_descent = { - 'Courier': (629, -157), - 'Courier-Bold': (626, -142), - 'Courier-BoldOblique': (626, -142), - 'Courier-Oblique': (629, -157), - 'Helvetica': (718, -207), - 'Helvetica-Bold': (718, -207), - 'Helvetica-BoldOblique': (718, -207), - 'Helvetica-Oblique': (718, -207), - 'Times-Roman': (683, -217), - 'Times-Bold': (676, -205), - 'Times-BoldItalic': (699, -205), - 'Times-Italic': (683, -205), - 'Symbol': (0, 0), - 'ZapfDingbats': (0, 0) - } - -# nuild this up one entry at a time to stay under JPython's 64k limit. -widthsByFontGlyph = {} -widthsByFontGlyph['Helvetica'] = {'A': 667, - 'AE': 1000, - 'Aacute': 667, - 'Acircumflex': 667, - 'Adieresis': 667, - 'Agrave': 667, - 'Aring': 667, - 'Atilde': 667, - 'B': 667, - 'C': 722, - 'Ccedilla': 722, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 556, - 'F': 611, - 'G': 778, - 'H': 722, - 'I': 278, - 'Iacute': 278, - 'Icircumflex': 278, - 'Idieresis': 278, - 'Igrave': 278, - 'J': 500, - 'K': 667, - 'L': 556, - 'Lslash': 556, - 'M': 833, - 'N': 722, - 'Ntilde': 722, - 'O': 778, - 'OE': 1000, - 'Oacute': 778, - 'Ocircumflex': 778, - 'Odieresis': 778, - 'Ograve': 778, - 'Oslash': 778, - 'Otilde': 778, - 'P': 667, - 'Q': 778, - 'R': 722, - 'S': 667, - 'Scaron': 667, - 'T': 611, - 'Thorn': 667, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 667, - 'W': 944, - 'X': 667, - 'Y': 667, - 'Yacute': 667, - 'Ydieresis': 667, - 'Z': 611, - 'Zcaron': 611, - 'a': 556, - 'aacute': 556, - 'acircumflex': 556, - 'acute': 333, - 'adieresis': 556, - 'ae': 889, - 'agrave': 556, - 'ampersand': 667, - 'aring': 556, - 'asciicircum': 469, - 'asciitilde': 584, - 'asterisk': 389, - 'at': 1015, - 'atilde': 556, - 'b': 556, - 'backslash': 278, - 'bar': 260, - 'braceleft': 334, - 'braceright': 334, - 'bracketleft': 278, - 'bracketright': 278, - 'breve': 333, - 'brokenbar': 260, - 'bullet': 350, - 'c': 500, - 'caron': 333, - 'ccedilla': 500, - 'cedilla': 333, - 'cent': 556, - 'circumflex': 333, - 'colon': 278, - 'comma': 278, - 'copyright': 737, - 'currency': 556, - 'd': 556, - 'dagger': 556, - 'daggerdbl': 556, - 'degree': 400, - 'dieresis': 333, - 'divide': 584, - 'dollar': 556, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 556, - 'eacute': 556, - 'ecircumflex': 556, - 'edieresis': 556, - 'egrave': 556, - 'eight': 556, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 556, - 'equal': 584, - 'eth': 556, - 'exclam': 278, - 'exclamdown': 333, - 'f': 278, - 'fi': 500, - 'five': 556, - 'fl': 500, - 'florin': 556, - 'four': 556, - 'fraction': 167, - 'g': 556, - 'germandbls': 611, - 'grave': 333, - 'greater': 584, - 'guillemotleft': 556, - 'guillemotright': 556, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 556, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 222, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 222, - 'k': 500, - 'l': 222, - 'less': 584, - 'logicalnot': 584, - 'lslash': 222, - 'm': 833, - 'macron': 333, - 'minus': 584, - 'mu': 556, - 'multiply': 584, - 'n': 556, - 'nine': 556, - 'ntilde': 556, - 'numbersign': 556, - 'o': 556, - 'oacute': 556, - 'ocircumflex': 556, - 'odieresis': 556, - 'oe': 944, - 'ogonek': 333, - 'ograve': 556, - 'one': 556, - 'onehalf': 834, - 'onequarter': 834, - 'onesuperior': 333, - 'ordfeminine': 370, - 'ordmasculine': 365, - 'oslash': 611, - 'otilde': 556, - 'p': 556, - 'paragraph': 537, - 'parenleft': 333, - 'parenright': 333, - 'percent': 889, - 'period': 278, - 'periodcentered': 278, - 'perthousand': 1000, - 'plus': 584, - 'plusminus': 584, - 'q': 556, - 'question': 556, - 'questiondown': 611, - 'quotedbl': 355, - 'quotedblbase': 333, - 'quotedblleft': 333, - 'quotedblright': 333, - 'quoteleft': 222, - 'quoteright': 222, - 'quotesinglbase': 222, - 'quotesingle': 191, - 'r': 333, - 'registered': 737, - 'ring': 333, - 's': 500, - 'scaron': 500, - 'section': 556, - 'semicolon': 278, - 'seven': 556, - 'six': 556, - 'slash': 278, - 'space': 278, - 'sterling': 556, - 't': 278, - 'thorn': 556, - 'three': 556, - 'threequarters': 834, - 'threesuperior': 333, - 'tilde': 333, - 'trademark': 1000, - 'two': 556, - 'twosuperior': 333, - 'u': 556, - 'uacute': 556, - 'ucircumflex': 556, - 'udieresis': 556, - 'ugrave': 556, - 'underscore': 556, - 'v': 500, - 'w': 722, - 'x': 500, - 'y': 500, - 'yacute': 500, - 'ydieresis': 500, - 'yen': 556, - 'z': 500, - 'zcaron': 500, - 'zero': 556} - -widthsByFontGlyph['Helvetica-Bold'] = {'A': 722, - 'AE': 1000, - 'Aacute': 722, - 'Acircumflex': 722, - 'Adieresis': 722, - 'Agrave': 722, - 'Aring': 722, - 'Atilde': 722, - 'B': 722, - 'C': 722, - 'Ccedilla': 722, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 556, - 'F': 611, - 'G': 778, - 'H': 722, - 'I': 278, - 'Iacute': 278, - 'Icircumflex': 278, - 'Idieresis': 278, - 'Igrave': 278, - 'J': 556, - 'K': 722, - 'L': 611, - 'Lslash': 611, - 'M': 833, - 'N': 722, - 'Ntilde': 722, - 'O': 778, - 'OE': 1000, - 'Oacute': 778, - 'Ocircumflex': 778, - 'Odieresis': 778, - 'Ograve': 778, - 'Oslash': 778, - 'Otilde': 778, - 'P': 667, - 'Q': 778, - 'R': 722, - 'S': 667, - 'Scaron': 667, - 'T': 611, - 'Thorn': 667, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 667, - 'W': 944, - 'X': 667, - 'Y': 667, - 'Yacute': 667, - 'Ydieresis': 667, - 'Z': 611, - 'Zcaron': 611, - 'a': 556, - 'aacute': 556, - 'acircumflex': 556, - 'acute': 333, - 'adieresis': 556, - 'ae': 889, - 'agrave': 556, - 'ampersand': 722, - 'aring': 556, - 'asciicircum': 584, - 'asciitilde': 584, - 'asterisk': 389, - 'at': 975, - 'atilde': 556, - 'b': 611, - 'backslash': 278, - 'bar': 280, - 'braceleft': 389, - 'braceright': 389, - 'bracketleft': 333, - 'bracketright': 333, - 'breve': 333, - 'brokenbar': 280, - 'bullet': 350, - 'c': 556, - 'caron': 333, - 'ccedilla': 556, - 'cedilla': 333, - 'cent': 556, - 'circumflex': 333, - 'colon': 333, - 'comma': 278, - 'copyright': 737, - 'currency': 556, - 'd': 611, - 'dagger': 556, - 'daggerdbl': 556, - 'degree': 400, - 'dieresis': 333, - 'divide': 584, - 'dollar': 556, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 556, - 'eacute': 556, - 'ecircumflex': 556, - 'edieresis': 556, - 'egrave': 556, - 'eight': 556, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 556, - 'equal': 584, - 'eth': 611, - 'exclam': 333, - 'exclamdown': 333, - 'f': 333, - 'fi': 611, - 'five': 556, - 'fl': 611, - 'florin': 556, - 'four': 556, - 'fraction': 167, - 'g': 611, - 'germandbls': 611, - 'grave': 333, - 'greater': 584, - 'guillemotleft': 556, - 'guillemotright': 556, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 611, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 278, - 'k': 556, - 'l': 278, - 'less': 584, - 'logicalnot': 584, - 'lslash': 278, - 'm': 889, - 'macron': 333, - 'minus': 584, - 'mu': 611, - 'multiply': 584, - 'n': 611, - 'nine': 556, - 'ntilde': 611, - 'numbersign': 556, - 'o': 611, - 'oacute': 611, - 'ocircumflex': 611, - 'odieresis': 611, - 'oe': 944, - 'ogonek': 333, - 'ograve': 611, - 'one': 556, - 'onehalf': 834, - 'onequarter': 834, - 'onesuperior': 333, - 'ordfeminine': 370, - 'ordmasculine': 365, - 'oslash': 611, - 'otilde': 611, - 'p': 611, - 'paragraph': 556, - 'parenleft': 333, - 'parenright': 333, - 'percent': 889, - 'period': 278, - 'periodcentered': 278, - 'perthousand': 1000, - 'plus': 584, - 'plusminus': 584, - 'q': 611, - 'question': 611, - 'questiondown': 611, - 'quotedbl': 474, - 'quotedblbase': 500, - 'quotedblleft': 500, - 'quotedblright': 500, - 'quoteleft': 278, - 'quoteright': 278, - 'quotesinglbase': 278, - 'quotesingle': 238, - 'r': 389, - 'registered': 737, - 'ring': 333, - 's': 556, - 'scaron': 556, - 'section': 556, - 'semicolon': 333, - 'seven': 556, - 'six': 556, - 'slash': 278, - 'space': 278, - 'sterling': 556, - 't': 333, - 'thorn': 611, - 'three': 556, - 'threequarters': 834, - 'threesuperior': 333, - 'tilde': 333, - 'trademark': 1000, - 'two': 556, - 'twosuperior': 333, - 'u': 611, - 'uacute': 611, - 'ucircumflex': 611, - 'udieresis': 611, - 'ugrave': 611, - 'underscore': 556, - 'v': 556, - 'w': 778, - 'x': 556, - 'y': 556, - 'yacute': 556, - 'ydieresis': 556, - 'yen': 556, - 'z': 500, - 'zcaron': 500, - 'zero': 556} - -widthsByFontGlyph['Helvetica-Oblique'] = {'A': 667, - 'AE': 1000, - 'Aacute': 667, - 'Acircumflex': 667, - 'Adieresis': 667, - 'Agrave': 667, - 'Aring': 667, - 'Atilde': 667, - 'B': 667, - 'C': 722, - 'Ccedilla': 722, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 556, - 'F': 611, - 'G': 778, - 'H': 722, - 'I': 278, - 'Iacute': 278, - 'Icircumflex': 278, - 'Idieresis': 278, - 'Igrave': 278, - 'J': 500, - 'K': 667, - 'L': 556, - 'Lslash': 556, - 'M': 833, - 'N': 722, - 'Ntilde': 722, - 'O': 778, - 'OE': 1000, - 'Oacute': 778, - 'Ocircumflex': 778, - 'Odieresis': 778, - 'Ograve': 778, - 'Oslash': 778, - 'Otilde': 778, - 'P': 667, - 'Q': 778, - 'R': 722, - 'S': 667, - 'Scaron': 667, - 'T': 611, - 'Thorn': 667, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 667, - 'W': 944, - 'X': 667, - 'Y': 667, - 'Yacute': 667, - 'Ydieresis': 667, - 'Z': 611, - 'Zcaron': 611, - 'a': 556, - 'aacute': 556, - 'acircumflex': 556, - 'acute': 333, - 'adieresis': 556, - 'ae': 889, - 'agrave': 556, - 'ampersand': 667, - 'aring': 556, - 'asciicircum': 469, - 'asciitilde': 584, - 'asterisk': 389, - 'at': 1015, - 'atilde': 556, - 'b': 556, - 'backslash': 278, - 'bar': 260, - 'braceleft': 334, - 'braceright': 334, - 'bracketleft': 278, - 'bracketright': 278, - 'breve': 333, - 'brokenbar': 260, - 'bullet': 350, - 'c': 500, - 'caron': 333, - 'ccedilla': 500, - 'cedilla': 333, - 'cent': 556, - 'circumflex': 333, - 'colon': 278, - 'comma': 278, - 'copyright': 737, - 'currency': 556, - 'd': 556, - 'dagger': 556, - 'daggerdbl': 556, - 'degree': 400, - 'dieresis': 333, - 'divide': 584, - 'dollar': 556, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 556, - 'eacute': 556, - 'ecircumflex': 556, - 'edieresis': 556, - 'egrave': 556, - 'eight': 556, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 556, - 'equal': 584, - 'eth': 556, - 'exclam': 278, - 'exclamdown': 333, - 'f': 278, - 'fi': 500, - 'five': 556, - 'fl': 500, - 'florin': 556, - 'four': 556, - 'fraction': 167, - 'g': 556, - 'germandbls': 611, - 'grave': 333, - 'greater': 584, - 'guillemotleft': 556, - 'guillemotright': 556, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 556, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 222, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 222, - 'k': 500, - 'l': 222, - 'less': 584, - 'logicalnot': 584, - 'lslash': 222, - 'm': 833, - 'macron': 333, - 'minus': 584, - 'mu': 556, - 'multiply': 584, - 'n': 556, - 'nine': 556, - 'ntilde': 556, - 'numbersign': 556, - 'o': 556, - 'oacute': 556, - 'ocircumflex': 556, - 'odieresis': 556, - 'oe': 944, - 'ogonek': 333, - 'ograve': 556, - 'one': 556, - 'onehalf': 834, - 'onequarter': 834, - 'onesuperior': 333, - 'ordfeminine': 370, - 'ordmasculine': 365, - 'oslash': 611, - 'otilde': 556, - 'p': 556, - 'paragraph': 537, - 'parenleft': 333, - 'parenright': 333, - 'percent': 889, - 'period': 278, - 'periodcentered': 278, - 'perthousand': 1000, - 'plus': 584, - 'plusminus': 584, - 'q': 556, - 'question': 556, - 'questiondown': 611, - 'quotedbl': 355, - 'quotedblbase': 333, - 'quotedblleft': 333, - 'quotedblright': 333, - 'quoteleft': 222, - 'quoteright': 222, - 'quotesinglbase': 222, - 'quotesingle': 191, - 'r': 333, - 'registered': 737, - 'ring': 333, - 's': 500, - 'scaron': 500, - 'section': 556, - 'semicolon': 278, - 'seven': 556, - 'six': 556, - 'slash': 278, - 'space': 278, - 'sterling': 556, - 't': 278, - 'thorn': 556, - 'three': 556, - 'threequarters': 834, - 'threesuperior': 333, - 'tilde': 333, - 'trademark': 1000, - 'two': 556, - 'twosuperior': 333, - 'u': 556, - 'uacute': 556, - 'ucircumflex': 556, - 'udieresis': 556, - 'ugrave': 556, - 'underscore': 556, - 'v': 500, - 'w': 722, - 'x': 500, - 'y': 500, - 'yacute': 500, - 'ydieresis': 500, - 'yen': 556, - 'z': 500, - 'zcaron': 500, - 'zero': 556} - - - -widthsByFontGlyph['Helvetica-BoldOblique'] = {'A': 722, - 'AE': 1000, - 'Aacute': 722, - 'Acircumflex': 722, - 'Adieresis': 722, - 'Agrave': 722, - 'Aring': 722, - 'Atilde': 722, - 'B': 722, - 'C': 722, - 'Ccedilla': 722, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 556, - 'F': 611, - 'G': 778, - 'H': 722, - 'I': 278, - 'Iacute': 278, - 'Icircumflex': 278, - 'Idieresis': 278, - 'Igrave': 278, - 'J': 556, - 'K': 722, - 'L': 611, - 'Lslash': 611, - 'M': 833, - 'N': 722, - 'Ntilde': 722, - 'O': 778, - 'OE': 1000, - 'Oacute': 778, - 'Ocircumflex': 778, - 'Odieresis': 778, - 'Ograve': 778, - 'Oslash': 778, - 'Otilde': 778, - 'P': 667, - 'Q': 778, - 'R': 722, - 'S': 667, - 'Scaron': 667, - 'T': 611, - 'Thorn': 667, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 667, - 'W': 944, - 'X': 667, - 'Y': 667, - 'Yacute': 667, - 'Ydieresis': 667, - 'Z': 611, - 'Zcaron': 611, - 'a': 556, - 'aacute': 556, - 'acircumflex': 556, - 'acute': 333, - 'adieresis': 556, - 'ae': 889, - 'agrave': 556, - 'ampersand': 722, - 'aring': 556, - 'asciicircum': 584, - 'asciitilde': 584, - 'asterisk': 389, - 'at': 975, - 'atilde': 556, - 'b': 611, - 'backslash': 278, - 'bar': 280, - 'braceleft': 389, - 'braceright': 389, - 'bracketleft': 333, - 'bracketright': 333, - 'breve': 333, - 'brokenbar': 280, - 'bullet': 350, - 'c': 556, - 'caron': 333, - 'ccedilla': 556, - 'cedilla': 333, - 'cent': 556, - 'circumflex': 333, - 'colon': 333, - 'comma': 278, - 'copyright': 737, - 'currency': 556, - 'd': 611, - 'dagger': 556, - 'daggerdbl': 556, - 'degree': 400, - 'dieresis': 333, - 'divide': 584, - 'dollar': 556, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 556, - 'eacute': 556, - 'ecircumflex': 556, - 'edieresis': 556, - 'egrave': 556, - 'eight': 556, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 556, - 'equal': 584, - 'eth': 611, - 'exclam': 333, - 'exclamdown': 333, - 'f': 333, - 'fi': 611, - 'five': 556, - 'fl': 611, - 'florin': 556, - 'four': 556, - 'fraction': 167, - 'g': 611, - 'germandbls': 611, - 'grave': 333, - 'greater': 584, - 'guillemotleft': 556, - 'guillemotright': 556, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 611, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 278, - 'k': 556, - 'l': 278, - 'less': 584, - 'logicalnot': 584, - 'lslash': 278, - 'm': 889, - 'macron': 333, - 'minus': 584, - 'mu': 611, - 'multiply': 584, - 'n': 611, - 'nine': 556, - 'ntilde': 611, - 'numbersign': 556, - 'o': 611, - 'oacute': 611, - 'ocircumflex': 611, - 'odieresis': 611, - 'oe': 944, - 'ogonek': 333, - 'ograve': 611, - 'one': 556, - 'onehalf': 834, - 'onequarter': 834, - 'onesuperior': 333, - 'ordfeminine': 370, - 'ordmasculine': 365, - 'oslash': 611, - 'otilde': 611, - 'p': 611, - 'paragraph': 556, - 'parenleft': 333, - 'parenright': 333, - 'percent': 889, - 'period': 278, - 'periodcentered': 278, - 'perthousand': 1000, - 'plus': 584, - 'plusminus': 584, - 'q': 611, - 'question': 611, - 'questiondown': 611, - 'quotedbl': 474, - 'quotedblbase': 500, - 'quotedblleft': 500, - 'quotedblright': 500, - 'quoteleft': 278, - 'quoteright': 278, - 'quotesinglbase': 278, - 'quotesingle': 238, - 'r': 389, - 'registered': 737, - 'ring': 333, - 's': 556, - 'scaron': 556, - 'section': 556, - 'semicolon': 333, - 'seven': 556, - 'six': 556, - 'slash': 278, - 'space': 278, - 'sterling': 556, - 't': 333, - 'thorn': 611, - 'three': 556, - 'threequarters': 834, - 'threesuperior': 333, - 'tilde': 333, - 'trademark': 1000, - 'two': 556, - 'twosuperior': 333, - 'u': 611, - 'uacute': 611, - 'ucircumflex': 611, - 'udieresis': 611, - 'ugrave': 611, - 'underscore': 556, - 'v': 556, - 'w': 778, - 'x': 556, - 'y': 556, - 'yacute': 556, - 'ydieresis': 556, - 'yen': 556, - 'z': 500, - 'zcaron': 500, - 'zero': 556} - -# Courier can be expressed more compactly! -_w = {} -for charname in widthsByFontGlyph['Helvetica'].keys(): - _w[charname] = 600 -widthsByFontGlyph['Courier'] = _w -widthsByFontGlyph['Courier-Bold'] = _w -widthsByFontGlyph['Courier-Oblique'] = _w -widthsByFontGlyph['Courier-BoldOblique'] = _w - -widthsByFontGlyph['Times-Roman'] = {'A': 722, - 'AE': 889, - 'Aacute': 722, - 'Acircumflex': 722, - 'Adieresis': 722, - 'Agrave': 722, - 'Aring': 722, - 'Atilde': 722, - 'B': 667, - 'C': 667, - 'Ccedilla': 667, - 'D': 722, - 'E': 611, - 'Eacute': 611, - 'Ecircumflex': 611, - 'Edieresis': 611, - 'Egrave': 611, - 'Eth': 722, - 'Euro': 500, - 'F': 556, - 'G': 722, - 'H': 722, - 'I': 333, - 'Iacute': 333, - 'Icircumflex': 333, - 'Idieresis': 333, - 'Igrave': 333, - 'J': 389, - 'K': 722, - 'L': 611, - 'Lslash': 611, - 'M': 889, - 'N': 722, - 'Ntilde': 722, - 'O': 722, - 'OE': 889, - 'Oacute': 722, - 'Ocircumflex': 722, - 'Odieresis': 722, - 'Ograve': 722, - 'Oslash': 722, - 'Otilde': 722, - 'P': 556, - 'Q': 722, - 'R': 667, - 'S': 556, - 'Scaron': 556, - 'T': 611, - 'Thorn': 556, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 722, - 'W': 944, - 'X': 722, - 'Y': 722, - 'Yacute': 722, - 'Ydieresis': 722, - 'Z': 611, - 'Zcaron': 611, - 'a': 444, - 'aacute': 444, - 'acircumflex': 444, - 'acute': 333, - 'adieresis': 444, - 'ae': 667, - 'agrave': 444, - 'ampersand': 778, - 'aring': 444, - 'asciicircum': 469, - 'asciitilde': 541, - 'asterisk': 500, - 'at': 921, - 'atilde': 444, - 'b': 500, - 'backslash': 278, - 'bar': 200, - 'braceleft': 480, - 'braceright': 480, - 'bracketleft': 333, - 'bracketright': 333, - 'breve': 333, - 'brokenbar': 200, - 'bullet': 350, - 'c': 444, - 'caron': 333, - 'ccedilla': 444, - 'cedilla': 333, - 'cent': 500, - 'circumflex': 333, - 'colon': 278, - 'comma': 250, - 'copyright': 760, - 'currency': 500, - 'd': 500, - 'dagger': 500, - 'daggerdbl': 500, - 'degree': 400, - 'dieresis': 333, - 'divide': 564, - 'dollar': 500, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 444, - 'eacute': 444, - 'ecircumflex': 444, - 'edieresis': 444, - 'egrave': 444, - 'eight': 500, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 500, - 'equal': 564, - 'eth': 500, - 'exclam': 333, - 'exclamdown': 333, - 'f': 333, - 'fi': 556, - 'five': 500, - 'fl': 556, - 'florin': 500, - 'four': 500, - 'fraction': 167, - 'g': 500, - 'germandbls': 500, - 'grave': 333, - 'greater': 564, - 'guillemotleft': 500, - 'guillemotright': 500, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 500, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 278, - 'k': 500, - 'l': 278, - 'less': 564, - 'logicalnot': 564, - 'lslash': 278, - 'm': 778, - 'macron': 333, - 'minus': 564, - 'mu': 500, - 'multiply': 564, - 'n': 500, - 'nine': 500, - 'ntilde': 500, - 'numbersign': 500, - 'o': 500, - 'oacute': 500, - 'ocircumflex': 500, - 'odieresis': 500, - 'oe': 722, - 'ogonek': 333, - 'ograve': 500, - 'one': 500, - 'onehalf': 750, - 'onequarter': 750, - 'onesuperior': 300, - 'ordfeminine': 276, - 'ordmasculine': 310, - 'oslash': 500, - 'otilde': 500, - 'p': 500, - 'paragraph': 453, - 'parenleft': 333, - 'parenright': 333, - 'percent': 833, - 'period': 250, - 'periodcentered': 250, - 'perthousand': 1000, - 'plus': 564, - 'plusminus': 564, - 'q': 500, - 'question': 444, - 'questiondown': 444, - 'quotedbl': 408, - 'quotedblbase': 444, - 'quotedblleft': 444, - 'quotedblright': 444, - 'quoteleft': 333, - 'quoteright': 333, - 'quotesinglbase': 333, - 'quotesingle': 180, - 'r': 333, - 'registered': 760, - 'ring': 333, - 's': 389, - 'scaron': 389, - 'section': 500, - 'semicolon': 278, - 'seven': 500, - 'six': 500, - 'slash': 278, - 'space': 250, - 'sterling': 500, - 't': 278, - 'thorn': 500, - 'three': 500, - 'threequarters': 750, - 'threesuperior': 300, - 'tilde': 333, - 'trademark': 980, - 'two': 500, - 'twosuperior': 300, - 'u': 500, - 'uacute': 500, - 'ucircumflex': 500, - 'udieresis': 500, - 'ugrave': 500, - 'underscore': 500, - 'v': 500, - 'w': 722, - 'x': 500, - 'y': 500, - 'yacute': 500, - 'ydieresis': 500, - 'yen': 500, - 'z': 444, - 'zcaron': 444, - 'zero': 500} - -widthsByFontGlyph['Times-Bold'] = {'A': 722, - 'AE': 1000, - 'Aacute': 722, - 'Acircumflex': 722, - 'Adieresis': 722, - 'Agrave': 722, - 'Aring': 722, - 'Atilde': 722, - 'B': 667, - 'C': 722, - 'Ccedilla': 722, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 500, - 'F': 611, - 'G': 778, - 'H': 778, - 'I': 389, - 'Iacute': 389, - 'Icircumflex': 389, - 'Idieresis': 389, - 'Igrave': 389, - 'J': 500, - 'K': 778, - 'L': 667, - 'Lslash': 667, - 'M': 944, - 'N': 722, - 'Ntilde': 722, - 'O': 778, - 'OE': 1000, - 'Oacute': 778, - 'Ocircumflex': 778, - 'Odieresis': 778, - 'Ograve': 778, - 'Oslash': 778, - 'Otilde': 778, - 'P': 611, - 'Q': 778, - 'R': 722, - 'S': 556, - 'Scaron': 556, - 'T': 667, - 'Thorn': 611, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 722, - 'W': 1000, - 'X': 722, - 'Y': 722, - 'Yacute': 722, - 'Ydieresis': 722, - 'Z': 667, - 'Zcaron': 667, - 'a': 500, - 'aacute': 500, - 'acircumflex': 500, - 'acute': 333, - 'adieresis': 500, - 'ae': 722, - 'agrave': 500, - 'ampersand': 833, - 'aring': 500, - 'asciicircum': 581, - 'asciitilde': 520, - 'asterisk': 500, - 'at': 930, - 'atilde': 500, - 'b': 556, - 'backslash': 278, - 'bar': 220, - 'braceleft': 394, - 'braceright': 394, - 'bracketleft': 333, - 'bracketright': 333, - 'breve': 333, - 'brokenbar': 220, - 'bullet': 350, - 'c': 444, - 'caron': 333, - 'ccedilla': 444, - 'cedilla': 333, - 'cent': 500, - 'circumflex': 333, - 'colon': 333, - 'comma': 250, - 'copyright': 747, - 'currency': 500, - 'd': 556, - 'dagger': 500, - 'daggerdbl': 500, - 'degree': 400, - 'dieresis': 333, - 'divide': 570, - 'dollar': 500, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 444, - 'eacute': 444, - 'ecircumflex': 444, - 'edieresis': 444, - 'egrave': 444, - 'eight': 500, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 500, - 'equal': 570, - 'eth': 500, - 'exclam': 333, - 'exclamdown': 333, - 'f': 333, - 'fi': 556, - 'five': 500, - 'fl': 556, - 'florin': 500, - 'four': 500, - 'fraction': 167, - 'g': 500, - 'germandbls': 556, - 'grave': 333, - 'greater': 570, - 'guillemotleft': 500, - 'guillemotright': 500, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 556, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 333, - 'k': 556, - 'l': 278, - 'less': 570, - 'logicalnot': 570, - 'lslash': 278, - 'm': 833, - 'macron': 333, - 'minus': 570, - 'mu': 556, - 'multiply': 570, - 'n': 556, - 'nine': 500, - 'ntilde': 556, - 'numbersign': 500, - 'o': 500, - 'oacute': 500, - 'ocircumflex': 500, - 'odieresis': 500, - 'oe': 722, - 'ogonek': 333, - 'ograve': 500, - 'one': 500, - 'onehalf': 750, - 'onequarter': 750, - 'onesuperior': 300, - 'ordfeminine': 300, - 'ordmasculine': 330, - 'oslash': 500, - 'otilde': 500, - 'p': 556, - 'paragraph': 540, - 'parenleft': 333, - 'parenright': 333, - 'percent': 1000, - 'period': 250, - 'periodcentered': 250, - 'perthousand': 1000, - 'plus': 570, - 'plusminus': 570, - 'q': 556, - 'question': 500, - 'questiondown': 500, - 'quotedbl': 555, - 'quotedblbase': 500, - 'quotedblleft': 500, - 'quotedblright': 500, - 'quoteleft': 333, - 'quoteright': 333, - 'quotesinglbase': 333, - 'quotesingle': 278, - 'r': 444, - 'registered': 747, - 'ring': 333, - 's': 389, - 'scaron': 389, - 'section': 500, - 'semicolon': 333, - 'seven': 500, - 'six': 500, - 'slash': 278, - 'space': 250, - 'sterling': 500, - 't': 333, - 'thorn': 556, - 'three': 500, - 'threequarters': 750, - 'threesuperior': 300, - 'tilde': 333, - 'trademark': 1000, - 'two': 500, - 'twosuperior': 300, - 'u': 556, - 'uacute': 556, - 'ucircumflex': 556, - 'udieresis': 556, - 'ugrave': 556, - 'underscore': 500, - 'v': 500, - 'w': 722, - 'x': 500, - 'y': 500, - 'yacute': 500, - 'ydieresis': 500, - 'yen': 500, - 'z': 444, - 'zcaron': 444, - 'zero': 500} - -widthsByFontGlyph['Times-Italic'] = {'A': 611, - 'AE': 889, - 'Aacute': 611, - 'Acircumflex': 611, - 'Adieresis': 611, - 'Agrave': 611, - 'Aring': 611, - 'Atilde': 611, - 'B': 611, - 'C': 667, - 'Ccedilla': 667, - 'D': 722, - 'E': 611, - 'Eacute': 611, - 'Ecircumflex': 611, - 'Edieresis': 611, - 'Egrave': 611, - 'Eth': 722, - 'Euro': 500, - 'F': 611, - 'G': 722, - 'H': 722, - 'I': 333, - 'Iacute': 333, - 'Icircumflex': 333, - 'Idieresis': 333, - 'Igrave': 333, - 'J': 444, - 'K': 667, - 'L': 556, - 'Lslash': 556, - 'M': 833, - 'N': 667, - 'Ntilde': 667, - 'O': 722, - 'OE': 944, - 'Oacute': 722, - 'Ocircumflex': 722, - 'Odieresis': 722, - 'Ograve': 722, - 'Oslash': 722, - 'Otilde': 722, - 'P': 611, - 'Q': 722, - 'R': 611, - 'S': 500, - 'Scaron': 500, - 'T': 556, - 'Thorn': 611, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 611, - 'W': 833, - 'X': 611, - 'Y': 556, - 'Yacute': 556, - 'Ydieresis': 556, - 'Z': 556, - 'Zcaron': 556, - 'a': 500, - 'aacute': 500, - 'acircumflex': 500, - 'acute': 333, - 'adieresis': 500, - 'ae': 667, - 'agrave': 500, - 'ampersand': 778, - 'aring': 500, - 'asciicircum': 422, - 'asciitilde': 541, - 'asterisk': 500, - 'at': 920, - 'atilde': 500, - 'b': 500, - 'backslash': 278, - 'bar': 275, - 'braceleft': 400, - 'braceright': 400, - 'bracketleft': 389, - 'bracketright': 389, - 'breve': 333, - 'brokenbar': 275, - 'bullet': 350, - 'c': 444, - 'caron': 333, - 'ccedilla': 444, - 'cedilla': 333, - 'cent': 500, - 'circumflex': 333, - 'colon': 333, - 'comma': 250, - 'copyright': 760, - 'currency': 500, - 'd': 500, - 'dagger': 500, - 'daggerdbl': 500, - 'degree': 400, - 'dieresis': 333, - 'divide': 675, - 'dollar': 500, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 444, - 'eacute': 444, - 'ecircumflex': 444, - 'edieresis': 444, - 'egrave': 444, - 'eight': 500, - 'ellipsis': 889, - 'emdash': 889, - 'endash': 500, - 'equal': 675, - 'eth': 500, - 'exclam': 333, - 'exclamdown': 389, - 'f': 278, - 'fi': 500, - 'five': 500, - 'fl': 500, - 'florin': 500, - 'four': 500, - 'fraction': 167, - 'g': 500, - 'germandbls': 500, - 'grave': 333, - 'greater': 675, - 'guillemotleft': 500, - 'guillemotright': 500, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 500, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 278, - 'k': 444, - 'l': 278, - 'less': 675, - 'logicalnot': 675, - 'lslash': 278, - 'm': 722, - 'macron': 333, - 'minus': 675, - 'mu': 500, - 'multiply': 675, - 'n': 500, - 'nine': 500, - 'ntilde': 500, - 'numbersign': 500, - 'o': 500, - 'oacute': 500, - 'ocircumflex': 500, - 'odieresis': 500, - 'oe': 667, - 'ogonek': 333, - 'ograve': 500, - 'one': 500, - 'onehalf': 750, - 'onequarter': 750, - 'onesuperior': 300, - 'ordfeminine': 276, - 'ordmasculine': 310, - 'oslash': 500, - 'otilde': 500, - 'p': 500, - 'paragraph': 523, - 'parenleft': 333, - 'parenright': 333, - 'percent': 833, - 'period': 250, - 'periodcentered': 250, - 'perthousand': 1000, - 'plus': 675, - 'plusminus': 675, - 'q': 500, - 'question': 500, - 'questiondown': 500, - 'quotedbl': 420, - 'quotedblbase': 556, - 'quotedblleft': 556, - 'quotedblright': 556, - 'quoteleft': 333, - 'quoteright': 333, - 'quotesinglbase': 333, - 'quotesingle': 214, - 'r': 389, - 'registered': 760, - 'ring': 333, - 's': 389, - 'scaron': 389, - 'section': 500, - 'semicolon': 333, - 'seven': 500, - 'six': 500, - 'slash': 278, - 'space': 250, - 'sterling': 500, - 't': 278, - 'thorn': 500, - 'three': 500, - 'threequarters': 750, - 'threesuperior': 300, - 'tilde': 333, - 'trademark': 980, - 'two': 500, - 'twosuperior': 300, - 'u': 500, - 'uacute': 500, - 'ucircumflex': 500, - 'udieresis': 500, - 'ugrave': 500, - 'underscore': 500, - 'v': 444, - 'w': 667, - 'x': 444, - 'y': 444, - 'yacute': 444, - 'ydieresis': 444, - 'yen': 500, - 'z': 389, - 'zcaron': 389, - 'zero': 500} - -widthsByFontGlyph['Times-BoldItalic'] = {'A': 667, - 'AE': 944, - 'Aacute': 667, - 'Acircumflex': 667, - 'Adieresis': 667, - 'Agrave': 667, - 'Aring': 667, - 'Atilde': 667, - 'B': 667, - 'C': 667, - 'Ccedilla': 667, - 'D': 722, - 'E': 667, - 'Eacute': 667, - 'Ecircumflex': 667, - 'Edieresis': 667, - 'Egrave': 667, - 'Eth': 722, - 'Euro': 500, - 'F': 667, - 'G': 722, - 'H': 778, - 'I': 389, - 'Iacute': 389, - 'Icircumflex': 389, - 'Idieresis': 389, - 'Igrave': 389, - 'J': 500, - 'K': 667, - 'L': 611, - 'Lslash': 611, - 'M': 889, - 'N': 722, - 'Ntilde': 722, - 'O': 722, - 'OE': 944, - 'Oacute': 722, - 'Ocircumflex': 722, - 'Odieresis': 722, - 'Ograve': 722, - 'Oslash': 722, - 'Otilde': 722, - 'P': 611, - 'Q': 722, - 'R': 667, - 'S': 556, - 'Scaron': 556, - 'T': 611, - 'Thorn': 611, - 'U': 722, - 'Uacute': 722, - 'Ucircumflex': 722, - 'Udieresis': 722, - 'Ugrave': 722, - 'V': 667, - 'W': 889, - 'X': 667, - 'Y': 611, - 'Yacute': 611, - 'Ydieresis': 611, - 'Z': 611, - 'Zcaron': 611, - 'a': 500, - 'aacute': 500, - 'acircumflex': 500, - 'acute': 333, - 'adieresis': 500, - 'ae': 722, - 'agrave': 500, - 'ampersand': 778, - 'aring': 500, - 'asciicircum': 570, - 'asciitilde': 570, - 'asterisk': 500, - 'at': 832, - 'atilde': 500, - 'b': 500, - 'backslash': 278, - 'bar': 220, - 'braceleft': 348, - 'braceright': 348, - 'bracketleft': 333, - 'bracketright': 333, - 'breve': 333, - 'brokenbar': 220, - 'bullet': 350, - 'c': 444, - 'caron': 333, - 'ccedilla': 444, - 'cedilla': 333, - 'cent': 500, - 'circumflex': 333, - 'colon': 333, - 'comma': 250, - 'copyright': 747, - 'currency': 500, - 'd': 500, - 'dagger': 500, - 'daggerdbl': 500, - 'degree': 400, - 'dieresis': 333, - 'divide': 570, - 'dollar': 500, - 'dotaccent': 333, - 'dotlessi': 278, - 'e': 444, - 'eacute': 444, - 'ecircumflex': 444, - 'edieresis': 444, - 'egrave': 444, - 'eight': 500, - 'ellipsis': 1000, - 'emdash': 1000, - 'endash': 500, - 'equal': 570, - 'eth': 500, - 'exclam': 389, - 'exclamdown': 389, - 'f': 333, - 'fi': 556, - 'five': 500, - 'fl': 556, - 'florin': 500, - 'four': 500, - 'fraction': 167, - 'g': 500, - 'germandbls': 500, - 'grave': 333, - 'greater': 570, - 'guillemotleft': 500, - 'guillemotright': 500, - 'guilsinglleft': 333, - 'guilsinglright': 333, - 'h': 556, - 'hungarumlaut': 333, - 'hyphen': 333, - 'i': 278, - 'iacute': 278, - 'icircumflex': 278, - 'idieresis': 278, - 'igrave': 278, - 'j': 278, - 'k': 500, - 'l': 278, - 'less': 570, - 'logicalnot': 606, - 'lslash': 278, - 'm': 778, - 'macron': 333, - 'minus': 606, - 'mu': 576, - 'multiply': 570, - 'n': 556, - 'nine': 500, - 'ntilde': 556, - 'numbersign': 500, - 'o': 500, - 'oacute': 500, - 'ocircumflex': 500, - 'odieresis': 500, - 'oe': 722, - 'ogonek': 333, - 'ograve': 500, - 'one': 500, - 'onehalf': 750, - 'onequarter': 750, - 'onesuperior': 300, - 'ordfeminine': 266, - 'ordmasculine': 300, - 'oslash': 500, - 'otilde': 500, - 'p': 500, - 'paragraph': 500, - 'parenleft': 333, - 'parenright': 333, - 'percent': 833, - 'period': 250, - 'periodcentered': 250, - 'perthousand': 1000, - 'plus': 570, - 'plusminus': 570, - 'q': 500, - 'question': 500, - 'questiondown': 500, - 'quotedbl': 555, - 'quotedblbase': 500, - 'quotedblleft': 500, - 'quotedblright': 500, - 'quoteleft': 333, - 'quoteright': 333, - 'quotesinglbase': 333, - 'quotesingle': 278, - 'r': 389, - 'registered': 747, - 'ring': 333, - 's': 389, - 'scaron': 389, - 'section': 500, - 'semicolon': 333, - 'seven': 500, - 'six': 500, - 'slash': 278, - 'space': 250, - 'sterling': 500, - 't': 278, - 'thorn': 500, - 'three': 500, - 'threequarters': 750, - 'threesuperior': 300, - 'tilde': 333, - 'trademark': 1000, - 'two': 500, - 'twosuperior': 300, - 'u': 556, - 'uacute': 556, - 'ucircumflex': 556, - 'udieresis': 556, - 'ugrave': 556, - 'underscore': 500, - 'v': 444, - 'w': 667, - 'x': 500, - 'y': 444, - 'yacute': 444, - 'ydieresis': 444, - 'yen': 500, - 'z': 389, - 'zcaron': 389, - 'zero': 500} - -widthsByFontGlyph['Symbol'] = {'Alpha': 722, - 'Beta': 667, - 'Chi': 722, - 'Delta': 612, - 'Epsilon': 611, - 'Eta': 722, - 'Euro': 750, - 'Gamma': 603, - 'Ifraktur': 686, - 'Iota': 333, - 'Kappa': 722, - 'Lambda': 686, - 'Mu': 889, - 'Nu': 722, - 'Omega': 768, - 'Omicron': 722, - 'Phi': 763, - 'Pi': 768, - 'Psi': 795, - 'Rfraktur': 795, - 'Rho': 556, - 'Sigma': 592, - 'Tau': 611, - 'Theta': 741, - 'Upsilon': 690, - 'Upsilon1': 620, - 'Xi': 645, - 'Zeta': 611, - 'aleph': 823, - 'alpha': 631, - 'ampersand': 778, - 'angle': 768, - 'angleleft': 329, - 'angleright': 329, - 'apple': 790, - 'approxequal': 549, - 'arrowboth': 1042, - 'arrowdblboth': 1042, - 'arrowdbldown': 603, - 'arrowdblleft': 987, - 'arrowdblright': 987, - 'arrowdblup': 603, - 'arrowdown': 603, - 'arrowhorizex': 1000, - 'arrowleft': 987, - 'arrowright': 987, - 'arrowup': 603, - 'arrowvertex': 603, - 'asteriskmath': 500, - 'bar': 200, - 'beta': 549, - 'braceex': 494, - 'braceleft': 480, - 'braceleftbt': 494, - 'braceleftmid': 494, - 'bracelefttp': 494, - 'braceright': 480, - 'bracerightbt': 494, - 'bracerightmid': 494, - 'bracerighttp': 494, - 'bracketleft': 333, - 'bracketleftbt': 384, - 'bracketleftex': 384, - 'bracketlefttp': 384, - 'bracketright': 333, - 'bracketrightbt': 384, - 'bracketrightex': 384, - 'bracketrighttp': 384, - 'bullet': 460, - 'carriagereturn': 658, - 'chi': 549, - 'circlemultiply': 768, - 'circleplus': 768, - 'club': 753, - 'colon': 278, - 'comma': 250, - 'congruent': 549, - 'copyrightsans': 790, - 'copyrightserif': 790, - 'degree': 400, - 'delta': 494, - 'diamond': 753, - 'divide': 549, - 'dotmath': 250, - 'eight': 500, - 'element': 713, - 'ellipsis': 1000, - 'emptyset': 823, - 'epsilon': 439, - 'equal': 549, - 'equivalence': 549, - 'eta': 603, - 'exclam': 333, - 'existential': 549, - 'five': 500, - 'florin': 500, - 'four': 500, - 'fraction': 167, - 'gamma': 411, - 'gradient': 713, - 'greater': 549, - 'greaterequal': 549, - 'heart': 753, - 'infinity': 713, - 'integral': 274, - 'integralbt': 686, - 'integralex': 686, - 'integraltp': 686, - 'intersection': 768, - 'iota': 329, - 'kappa': 549, - 'lambda': 549, - 'less': 549, - 'lessequal': 549, - 'logicaland': 603, - 'logicalnot': 713, - 'logicalor': 603, - 'lozenge': 494, - 'minus': 549, - 'minute': 247, - 'mu': 576, - 'multiply': 549, - 'nine': 500, - 'notelement': 713, - 'notequal': 549, - 'notsubset': 713, - 'nu': 521, - 'numbersign': 500, - 'omega': 686, - 'omega1': 713, - 'omicron': 549, - 'one': 500, - 'parenleft': 333, - 'parenleftbt': 384, - 'parenleftex': 384, - 'parenlefttp': 384, - 'parenright': 333, - 'parenrightbt': 384, - 'parenrightex': 384, - 'parenrighttp': 384, - 'partialdiff': 494, - 'percent': 833, - 'period': 250, - 'perpendicular': 658, - 'phi': 521, - 'phi1': 603, - 'pi': 549, - 'plus': 549, - 'plusminus': 549, - 'product': 823, - 'propersubset': 713, - 'propersuperset': 713, - 'proportional': 713, - 'psi': 686, - 'question': 444, - 'radical': 549, - 'radicalex': 500, - 'reflexsubset': 713, - 'reflexsuperset': 713, - 'registersans': 790, - 'registerserif': 790, - 'rho': 549, - 'second': 411, - 'semicolon': 278, - 'seven': 500, - 'sigma': 603, - 'sigma1': 439, - 'similar': 549, - 'six': 500, - 'slash': 278, - 'space': 250, - 'spade': 753, - 'suchthat': 439, - 'summation': 713, - 'tau': 439, - 'therefore': 863, - 'theta': 521, - 'theta1': 631, - 'three': 500, - 'trademarksans': 786, - 'trademarkserif': 890, - 'two': 500, - 'underscore': 500, - 'union': 768, - 'universal': 713, - 'upsilon': 576, - 'weierstrass': 987, - 'xi': 493, - 'zero': 500, - 'zeta': 494} - -widthsByFontGlyph['ZapfDingbats'] = {'a1': 974, - 'a10': 692, - 'a100': 668, - 'a101': 732, - 'a102': 544, - 'a103': 544, - 'a104': 910, - 'a105': 911, - 'a106': 667, - 'a107': 760, - 'a108': 760, - 'a109': 626, - 'a11': 960, - 'a110': 694, - 'a111': 595, - 'a112': 776, - 'a117': 690, - 'a118': 791, - 'a119': 790, - 'a12': 939, - 'a120': 788, - 'a121': 788, - 'a122': 788, - 'a123': 788, - 'a124': 788, - 'a125': 788, - 'a126': 788, - 'a127': 788, - 'a128': 788, - 'a129': 788, - 'a13': 549, - 'a130': 788, - 'a131': 788, - 'a132': 788, - 'a133': 788, - 'a134': 788, - 'a135': 788, - 'a136': 788, - 'a137': 788, - 'a138': 788, - 'a139': 788, - 'a14': 855, - 'a140': 788, - 'a141': 788, - 'a142': 788, - 'a143': 788, - 'a144': 788, - 'a145': 788, - 'a146': 788, - 'a147': 788, - 'a148': 788, - 'a149': 788, - 'a15': 911, - 'a150': 788, - 'a151': 788, - 'a152': 788, - 'a153': 788, - 'a154': 788, - 'a155': 788, - 'a156': 788, - 'a157': 788, - 'a158': 788, - 'a159': 788, - 'a16': 933, - 'a160': 894, - 'a161': 838, - 'a162': 924, - 'a163': 1016, - 'a164': 458, - 'a165': 924, - 'a166': 918, - 'a167': 927, - 'a168': 928, - 'a169': 928, - 'a17': 945, - 'a170': 834, - 'a171': 873, - 'a172': 828, - 'a173': 924, - 'a174': 917, - 'a175': 930, - 'a176': 931, - 'a177': 463, - 'a178': 883, - 'a179': 836, - 'a18': 974, - 'a180': 867, - 'a181': 696, - 'a182': 874, - 'a183': 760, - 'a184': 946, - 'a185': 865, - 'a186': 967, - 'a187': 831, - 'a188': 873, - 'a189': 927, - 'a19': 755, - 'a190': 970, - 'a191': 918, - 'a192': 748, - 'a193': 836, - 'a194': 771, - 'a195': 888, - 'a196': 748, - 'a197': 771, - 'a198': 888, - 'a199': 867, - 'a2': 961, - 'a20': 846, - 'a200': 696, - 'a201': 874, - 'a202': 974, - 'a203': 762, - 'a204': 759, - 'a205': 509, - 'a206': 410, - 'a21': 762, - 'a22': 761, - 'a23': 571, - 'a24': 677, - 'a25': 763, - 'a26': 760, - 'a27': 759, - 'a28': 754, - 'a29': 786, - 'a3': 980, - 'a30': 788, - 'a31': 788, - 'a32': 790, - 'a33': 793, - 'a34': 794, - 'a35': 816, - 'a36': 823, - 'a37': 789, - 'a38': 841, - 'a39': 823, - 'a4': 719, - 'a40': 833, - 'a41': 816, - 'a42': 831, - 'a43': 923, - 'a44': 744, - 'a45': 723, - 'a46': 749, - 'a47': 790, - 'a48': 792, - 'a49': 695, - 'a5': 789, - 'a50': 776, - 'a51': 768, - 'a52': 792, - 'a53': 759, - 'a54': 707, - 'a55': 708, - 'a56': 682, - 'a57': 701, - 'a58': 826, - 'a59': 815, - 'a6': 494, - 'a60': 789, - 'a61': 789, - 'a62': 707, - 'a63': 687, - 'a64': 696, - 'a65': 689, - 'a66': 786, - 'a67': 787, - 'a68': 713, - 'a69': 791, - 'a7': 552, - 'a70': 785, - 'a71': 791, - 'a72': 873, - 'a73': 761, - 'a74': 762, - 'a75': 759, - 'a76': 892, - 'a77': 892, - 'a78': 788, - 'a79': 784, - 'a8': 537, - 'a81': 438, - 'a82': 138, - 'a83': 277, - 'a84': 415, - 'a85': 509, - 'a86': 410, - 'a87': 234, - 'a88': 234, - 'a89': 390, - 'a9': 577, - 'a90': 390, - 'a91': 276, - 'a92': 276, - 'a93': 317, - 'a94': 317, - 'a95': 334, - 'a96': 334, - 'a97': 392, - 'a98': 392, - 'a99': 668, - 'space': 278} diff --git a/bin/reportlab/pdfbase/cidfonts.py b/bin/reportlab/pdfbase/cidfonts.py deleted file mode 100644 index ba438c40f53..00000000000 --- a/bin/reportlab/pdfbase/cidfonts.py +++ /dev/null @@ -1,516 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfbase/cidfonts.py -#$Header $ -__version__=''' $Id: cidfonts.py 2905 2006-05-23 14:49:28Z andy $ ''' -__doc__="""CID (Asian multi-byte) font support. - -This defines classes to represent CID fonts. They know how to calculate -their own width and how to write themselves into PDF files.""" - -import os -from types import ListType, TupleType, DictType -from string import find, split, strip -import marshal -import md5 -import time - -import reportlab -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfbase._cidfontdata import allowedTypeFaces, allowedEncodings, CIDFontInfo, \ - defaultUnicodeEncodings, widthsByUnichar -from reportlab.pdfgen.canvas import Canvas -from reportlab.pdfbase import pdfdoc -from reportlab.pdfbase.pdfutils import _escape -from reportlab.rl_config import CMapSearchPath - - -#quick hackery for 2.0 release. Now we always do unicode, and have built in -#the CMAP data, any code to load CMap files is not needed. -DISABLE_CMAP = True - - -def findCMapFile(name): - "Returns full filename, or raises error" - for dirname in CMapSearchPath: - cmapfile = dirname + os.sep + name - if os.path.isfile(cmapfile): - #print "found", cmapfile - return cmapfile - raise IOError, 'CMAP file for encodings "%s" not found!' % name - -def structToPDF(structure): - "Converts deeply nested structure to PDFdoc dictionary/array objects" - if type(structure) is DictType: - newDict = {} - for k, v in structure.items(): - newDict[k] = structToPDF(v) - return pdfdoc.PDFDictionary(newDict) - elif type(structure) in (ListType, TupleType): - newList = [] - for elem in structure: - newList.append(structToPDF(elem)) - return pdfdoc.PDFArray(newList) - else: - return structure - -class CIDEncoding(pdfmetrics.Encoding): - """Multi-byte encoding. These are loaded from CMAP files. - - A CMAP file is like a mini-codec. It defines the correspondence - between code points in the (multi-byte) input data and Character - IDs. """ - # aims to do similar things to Brian Hooper's CMap class, - # but I could not get it working and had to rewrite. - # also, we should really rearrange our current encoding - # into a SingleByteEncoding since many of its methods - # should not apply here. - - def __init__(self, name, useCache=1): - self.name = name - self._mapFileHash = None - self._codeSpaceRanges = [] - self._notDefRanges = [] - self._cmap = {} - self.source = None - if not DISABLE_CMAP: - if useCache: - from reportlab.lib.utils import get_rl_tempdir - fontmapdir = get_rl_tempdir('FastCMAPS') - if os.path.isfile(fontmapdir + os.sep + name + '.fastmap'): - self.fastLoad(fontmapdir) - self.source = fontmapdir + os.sep + name + '.fastmap' - else: - self.parseCMAPFile(name) - self.source = 'CMAP: ' + name - self.fastSave(fontmapdir) - else: - self.parseCMAPFile(name) - - def _hash(self, text): - hasher = md5.new() - hasher.update(text) - return hasher.digest() - - def parseCMAPFile(self, name): - """This is a tricky one as CMAP files are Postscript - ones. Some refer to others with a 'usecmap' - command""" - #started = time.clock() - cmapfile = findCMapFile(name) - # this will CRAWL with the unicode encodings... - rawdata = open(cmapfile, 'r').read() - - self._mapFileHash = self._hash(rawdata) - #if it contains the token 'usecmap', parse the other - #cmap file first.... - usecmap_pos = find(rawdata, 'usecmap') - if usecmap_pos > -1: - #they tell us to look in another file - #for the code space ranges. The one - # to use will be the previous word. - chunk = rawdata[0:usecmap_pos] - words = split(chunk) - otherCMAPName = words[-1] - #print 'referred to another CMAP %s' % otherCMAPName - self.parseCMAPFile(otherCMAPName) - # now continue parsing this, as it may - # override some settings - - - words = split(rawdata) - while words <> []: - if words[0] == 'begincodespacerange': - words = words[1:] - while words[0] <> 'endcodespacerange': - strStart, strEnd, words = words[0], words[1], words[2:] - start = int(strStart[1:-1], 16) - end = int(strEnd[1:-1], 16) - self._codeSpaceRanges.append((start, end),) - elif words[0] == 'beginnotdefrange': - words = words[1:] - while words[0] <> 'endnotdefrange': - strStart, strEnd, strValue = words[0:3] - start = int(strStart[1:-1], 16) - end = int(strEnd[1:-1], 16) - value = int(strValue) - self._notDefRanges.append((start, end, value),) - words = words[3:] - elif words[0] == 'begincidrange': - words = words[1:] - while words[0] <> 'endcidrange': - strStart, strEnd, strValue = words[0:3] - start = int(strStart[1:-1], 16) - end = int(strEnd[1:-1], 16) - value = int(strValue) - # this means that 'start' corresponds to 'value', - # start+1 corresponds to value+1 and so on up - # to end - offset = 0 - while start + offset <= end: - self._cmap[start + offset] = value + offset - offset = offset + 1 - words = words[3:] - - else: - words = words[1:] - #finished = time.clock() - #print 'parsed CMAP %s in %0.4f seconds' % (self.name, finished - started) - - def translate(self, text): - "Convert a string into a list of CIDs" - output = [] - cmap = self._cmap - lastChar = '' - for char in text: - if lastChar <> '': - #print 'convert character pair "%s"' % (lastChar + char) - num = ord(lastChar) * 256 + ord(char) - else: - #print 'convert character "%s"' % char - num = ord(char) - lastChar = char - found = 0 - for low, high in self._codeSpaceRanges: - if low < num < high: - try: - cid = cmap[num] - #print '%d -> %d' % (num, cid) - except KeyError: - #not defined. Try to find the appropriate - # notdef character, or failing that return - # zero - cid = 0 - for low2, high2, notdef in self._notDefRanges: - if low2 < num < high2: - cid = notdef - break - output.append(cid) - found = 1 - break - if found: - lastChar = '' - else: - lastChar = char - return output - - def fastSave(self, directory): - f = open(os.path.join(directory, self.name + '.fastmap'), 'wb') - marshal.dump(self._mapFileHash, f) - marshal.dump(self._codeSpaceRanges, f) - marshal.dump(self._notDefRanges, f) - marshal.dump(self._cmap, f) - f.close() - - def fastLoad(self, directory): - started = time.clock() - f = open(os.path.join(directory, self.name + '.fastmap'), 'rb') - self._mapFileHash = marshal.load(f) - self._codeSpaceRanges = marshal.load(f) - self._notDefRanges = marshal.load(f) - self._cmap = marshal.load(f) - f.close() - finished = time.clock() - #print 'loaded %s in %0.4f seconds' % (self.name, finished - started) - - def getData(self): - """Simple persistence helper. Return a dict with all that matters.""" - return { - 'mapFileHash': self._mapFileHash, - 'codeSpaceRanges': self._codeSpaceRanges, - 'notDefRanges': self._notDefRanges, - 'cmap': self._cmap, - - } - - -class CIDTypeFace(pdfmetrics.TypeFace): - """Multi-byte type face. - - Conceptually similar to a single byte typeface, - but the glyphs are identified by a numeric Character - ID (CID) and not a glyph name. """ - def __init__(self, name): - """Initialised from one of the canned dictionaries in allowedEncodings - - Or rather, it will be shortly...""" - pdfmetrics.TypeFace.__init__(self, name) - self._extractDictInfo(name) - def _extractDictInfo(self, name): - try: - fontDict = CIDFontInfo[name] - except KeyError: - raise KeyError, ("Unable to find information on CID typeface '%s'" % name + - "Only the following font names work:" + repr(allowedTypeFaces) - ) - descFont = fontDict['DescendantFonts'][0] - self.ascent = descFont['FontDescriptor']['Ascent'] - self.descent = descFont['FontDescriptor']['Descent'] - self._defaultWidth = descFont['DW'] - self._explicitWidths = self._expandWidths(descFont['W']) - - # should really support self.glyphWidths, self.glyphNames - # but not done yet. - - - def _expandWidths(self, compactWidthArray): - """Expands Adobe nested list structure to get a dictionary of widths. - - Here is an example of such a structure. - ( - # starting at character ID 1, next n characters have the widths given. - 1, (277,305,500,668,668,906,727,305,445,445,508,668,305,379,305,539), - # all Characters from ID 17 to 26 are 668 em units wide - 17, 26, 668, - 27, (305, 305, 668, 668, 668, 566, 871, 727, 637, 652, 699, 574, 555, - 676, 687, 242, 492, 664, 582, 789, 707, 734, 582, 734, 605, 605, - 641, 668, 727, 945, 609, 609, 574, 445, 668, 445, 668, 668, 590, - 555, 609, 547, 602, 574, 391, 609, 582, 234, 277, 539, 234, 895, - 582, 605, 602, 602, 387, 508, 441, 582, 562, 781, 531, 570, 555, - 449, 246, 449, 668), - # these must be half width katakana and the like. - 231, 632, 500 - ) - """ - data = compactWidthArray[:] - widths = {} - while data: - start, data = data[0], data[1:] - if type(data[0]) in (ListType, TupleType): - items, data = data[0], data[1:] - for offset in range(len(items)): - widths[start + offset] = items[offset] - else: - end, width, data = data[0], data[1], data[2:] - for idx in range(start, end+1): - widths[idx] = width - return widths - - def getCharWidth(self, characterId): - return self._explicitWidths.get(characterId, self._defaultWidth) - -class CIDFont(pdfmetrics.Font): - "Represents a built-in multi-byte font" - def __init__(self, face, encoding): - - self._multiByte = 1 - assert face in allowedTypeFaces, "TypeFace '%s' not supported! Use any of these instead: %s" % (face, allowedTypeFaces) - self.faceName = face - #should cache in registry... - self.face = CIDTypeFace(face) - - assert encoding in allowedEncodings, "Encoding '%s' not supported! Use any of these instead: %s" % (encoding, allowedEncodings) - self.encodingName = encoding - self.encoding = CIDEncoding(encoding) - - #legacy hack doing quick cut and paste. - self.fontName = self.faceName + '-' + self.encodingName - self.name = self.fontName - - # need to know if it is vertical or horizontal - self.isVertical = (self.encodingName[-1] == 'V') - - - #no substitutes initially - self.substitutionFonts = [] - - def formatForPdf(self, text): - encoded = _escape(text) - #print 'encoded CIDFont:', encoded - return encoded - - def stringWidth(self, text, size, encoding=None): - """This presumes non-Unicode input. UnicodeCIDFont wraps it for that context""" - cidlist = self.encoding.translate(text) - if self.isVertical: - #this part is "not checked!" but seems to work. - #assume each is 1000 ems high - return len(cidlist) * size - else: - w = 0 - for cid in cidlist: - w = w + self.face.getCharWidth(cid) - return 0.001 * w * size - - - def addObjects(self, doc): - """The explicit code in addMinchoObjects and addGothicObjects - will be replaced by something that pulls the data from - _cidfontdata.py in the next few days.""" - internalName = 'F' + repr(len(doc.fontMapping)+1) - - bigDict = CIDFontInfo[self.face.name] - bigDict['Name'] = '/' + internalName - bigDict['Encoding'] = '/' + self.encodingName - - #convert to PDF dictionary/array objects - cidObj = structToPDF(bigDict) - - # link into document, and add to font map - r = doc.Reference(cidObj, internalName) - fontDict = doc.idToObject['BasicFonts'].dict - fontDict[internalName] = r - doc.fontMapping[self.name] = '/' + internalName - - -class UnicodeCIDFont(CIDFont): - """Wraps up CIDFont to hide explicit encoding choice; - encodes text for output as UTF16. - - lang should be one of 'jpn',chs','cht','kor' for now. - if vertical is set, it will select a different widths array - and possibly glyphs for some punctuation marks. - - halfWidth is only for Japanese. - - - >>> dodgy = UnicodeCIDFont('nonexistent') - Traceback (most recent call last): - ... - KeyError: "don't know anything about CID font nonexistent" - >>> heisei = UnicodeCIDFont('HeiseiMin-W3') - >>> heisei.name - 'HeiseiMin-W3' - >>> heisei.language - 'jpn' - >>> heisei.encoding.name - 'UniJIS-UCS2-H' - >>> #This is how PDF data gets encoded. - >>> print heisei.formatForPdf('hello') - \\377\\376h\\000e\\000l\\000l\\000o\\000 - >>> tokyo = u'\u6771\u4AEC' - >>> print heisei.formatForPdf(tokyo) - \\377\\376qg\\354J - - """ - - def __init__(self, face, isVertical=False, isHalfWidth=False): - #pass - try: - lang, defaultEncoding = defaultUnicodeEncodings[face] - except KeyError: - raise KeyError("don't know anything about CID font %s" % face) - - #we know the languages now. - self.language = lang - - #rebuilt encoding string. They follow rules which work - #for the 7 fonts provided. - enc = defaultEncoding[:-1] - if isHalfWidth: - enc = enc + 'HW-' - if isVertical: - enc = enc + 'V' - else: - enc = enc + 'H' - - #now we can do the more general case - CIDFont.__init__(self, face, enc) - #self.encName = 'utf_16_le' - #it's simpler for unicode, just use the face name - self.name = self.fontName = face - self.vertical = isVertical - self.isHalfWidth = isHalfWidth - - self.unicodeWidths = widthsByUnichar[self.name] - - - def formatForPdf(self, text): - #these ones should be encoded asUTF16 minus the BOM - from codecs import utf_16_be_encode - #print 'formatting %s: %s' % (type(text), repr(text)) - if type(text) is not unicode: - text = text.decode('utf8') - utfText = utf_16_be_encode(text)[0] - encoded = _escape(utfText) - #print ' encoded:',encoded - return encoded - # - #result = _escape(encoded) - #print ' -> %s' % repr(result) - #return result - - - def stringWidth(self, text, size, encoding=None): - "Just ensure we do width test on characters, not bytes..." - if type(text) is type(''): - text = text.decode('utf8') - - widths = self.unicodeWidths - return size * 0.001 * sum([widths.get(uch, 1000) for uch in text]) - #return CIDFont.stringWidth(self, text, size, encoding) - - -def precalculate(cmapdir): - # crunches through all, making 'fastmap' files - import os - files = os.listdir(cmapdir) - for file in files: - if os.path.isfile(cmapdir + os.sep + self.name + '.fastmap'): - continue - try: - enc = CIDEncoding(file) - except: - print 'cannot parse %s, skipping' % enc - continue - enc.fastSave(cmapdir) - print 'saved %s.fastmap' % file - -def test(): - # only works if you have cirrect encodings on your box! - c = Canvas('test_japanese.pdf') - c.setFont('Helvetica', 30) - c.drawString(100,700, 'Japanese Font Support') - - pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','90ms-RKSJ-H')) - pdfmetrics.registerFont(CIDFont('HeiseiKakuGo-W5','90ms-RKSJ-H')) - - - # the two typefaces - c.setFont('HeiseiMin-W3-90ms-RKSJ-H', 16) - # this says "This is HeiseiMincho" in shift-JIS. Not all our readers - # have a Japanese PC, so I escaped it. On a Japanese-capable - # system, print the string to see Kanji - message1 = '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B' - c.drawString(100, 675, message1) - c.save() - print 'saved test_japanese.pdf' - - -## print 'CMAP_DIR = ', CMAP_DIR -## tf1 = CIDTypeFace('HeiseiMin-W3') -## print 'ascent = ',tf1.ascent -## print 'descent = ',tf1.descent -## for cid in [1,2,3,4,5,18,19,28,231,1742]: -## print 'width of cid %d = %d' % (cid, tf1.getCharWidth(cid)) - - encName = '90ms-RKSJ-H' - enc = CIDEncoding(encName) - print message1, '->', enc.translate(message1) - - f = CIDFont('HeiseiMin-W3','90ms-RKSJ-H') - print 'width = %0.2f' % f.stringWidth(message1, 10) - - - #testing all encodings -## import time -## started = time.time() -## import glob -## for encName in _cidfontdata.allowedEncodings: -## #encName = '90ms-RKSJ-H' -## enc = CIDEncoding(encName) -## print 'encoding %s:' % encName -## print ' codeSpaceRanges = %s' % enc._codeSpaceRanges -## print ' notDefRanges = %s' % enc._notDefRanges -## print ' mapping size = %d' % len(enc._cmap) -## finished = time.time() -## print 'constructed all encodings in %0.2f seconds' % (finished - started) - -if __name__=='__main__': - import doctest, cidfonts - doctest.testmod(cidfonts) - #test() - - - - diff --git a/bin/reportlab/pdfbase/pdfdoc.py b/bin/reportlab/pdfbase/pdfdoc.py deleted file mode 100755 index e3a12ff4d1e..00000000000 --- a/bin/reportlab/pdfbase/pdfdoc.py +++ /dev/null @@ -1,1892 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfbase/pdfdoc.py -__version__=''' $Id: pdfdoc.py 2854 2006-05-10 12:57:21Z rgbecker $ ''' -__doc__=""" -The module pdfdoc.py handles the 'outer structure' of PDF documents, ensuring that -all objects are properly cross-referenced and indexed to the nearest byte. The -'inner structure' - the page descriptions - are presumed to be generated before -each page is saved. -pdfgen.py calls this and provides a 'canvas' object to handle page marking operators. -piddlePDF calls pdfgen and offers a high-level interface. - -The classes within this generally mirror structures in the PDF file -and are not part of any public interface. Instead, canvas and font -classes are made available elsewhere for users to manipulate. -""" - -import string, types, binascii, codecs -from reportlab.pdfbase import pdfutils -from reportlab.pdfbase.pdfutils import LINEEND # this constant needed in both -from reportlab import rl_config -from reportlab.lib.utils import import_zlib, open_for_read, fp_str -from reportlab.pdfbase import pdfmetrics - -from sys import platform -try: - from sys import version_info -except: # pre-2.0 - # may be inaccurate but will at least - #work in anything which seeks to format - # version_info into a string - version_info = (1,5,2,'unknown',0) - -if platform[:4] == 'java' and version_info[:2] == (2, 1): - # workaround for list()-bug in Jython 2.1 (should be fixed in 2.2) - def list(sequence): - def f(x): - return x - return map(f, sequence) - -class PDFError(Exception): - pass - - -# set this flag to get more vertical whitespace (and larger files) -LongFormat = 1 -##if LongFormat: (doesn't work) -## pass -##else: -## LINEEND = "\n" # no wasteful carriage returns! - -# __InternalName__ is a special attribute that can only be set by the Document arbitrator -__InternalName__ = "__InternalName__" - -# __RefOnly__ marks reference only elements that must be formatted on top level -__RefOnly__ = "__RefOnly__" - -# __Comment__ provides a (one line) comment to inline with an object ref, if present -# if it is more than one line then percentize it... -__Comment__ = "__Comment__" - -# If DoComments is set then add helpful (space wasting) comment lines to PDF files -DoComments = 1 -if not LongFormat: - DoComments = 0 - -# name for standard font dictionary -BasicFonts = "BasicFonts" - -# name for the pages object -Pages = "Pages" - -### generic utilities - - -# for % substitutions -LINEENDDICT = {"LINEEND": LINEEND, "PERCENT": "%"} - -from types import InstanceType -def format(element, document, toplevel=0, InstanceType=InstanceType): - """Indirection step for formatting. - Ensures that document parameters alter behaviour - of formatting for all elements. - """ - t=type(element) - if t is InstanceType: - if not toplevel and hasattr(element, __RefOnly__): - # the object cannot be a component at non top level. - # make a reference to it and return it's format - return document.Reference(element).format(document) - else: - f = element.format(document) - if not rl_config.invariant and DoComments and hasattr(element, __Comment__): - f = "%s%s%s%s" % ("% ", element.__Comment__, LINEEND, f) - return f - elif t in (float, int): - #use a controlled number formatting routine - #instead of str, so Jython/Python etc do not differ - return fp_str(element) - else: - return str(element) - -def xObjectName(externalname): - return "FormXob.%s" % externalname - -# backwards compatibility -formName = xObjectName - - -# no encryption -class NoEncryption: - def encode(self, t): - "encode a string, stream, text" - return t - def prepare(self, document): - # get ready to do encryption - pass - def register(self, objnum, version): - # enter a new direct object - pass - def info(self): - # the representation of self in file if any (should be None or PDFDict) - return None - -class DummyDoc: - "used to bypass encryption when required" - encrypt = NoEncryption() - -### the global document structure manager - -class PDFDocument: - _ID = None - objectcounter = 0 - inObject = None - # set this to define filters - defaultStreamFilters = None - encrypt = NoEncryption() # default no encryption - pageCounter = 1 - def __init__(self, - dummyoutline=0, - compression=rl_config.pageCompression, - invariant=rl_config.invariant, - filename=None): - - # allow None value to be passed in to mean 'give system defaults' - if invariant is None: - self.invariant = rl_config.invariant - else: - self.invariant = invariant - self.setCompression(compression) - # signature for creating PDF ID - import md5 - sig = self.signature = md5.new() - sig.update("a reportlab document") - if not self.invariant: - cat = _getTimeStamp() - else: - cat = 946684800.0 - sig.update(repr(cat)) # initialize with timestamp digest - # mapping of internal identifier ("Page001") to PDF objectnumber and generation number (34, 0) - self.idToObjectNumberAndVersion = {} - # mapping of internal identifier ("Page001") to PDF object (PDFPage instance) - self.idToObject = {} - # internal id to file location - self.idToOffset = {} - # number to id - self.numberToId = {} - cat = self.Catalog = self._catalog = PDFCatalog() - pages = self.Pages = PDFPages() - cat.Pages = pages - if dummyoutline: - outlines = PDFOutlines0() - else: - outlines = PDFOutlines() - self.Outlines = self.outline = outlines - cat.Outlines = outlines - self.info = PDFInfo() - self.info.invariant = self.invariant - #self.Reference(self.Catalog) - #self.Reference(self.Info) - self.fontMapping = {} - #make an empty font dictionary - DD = PDFDictionary({}) - DD.__Comment__ = "The standard fonts dictionary" - DDR = self.Reference(DD, BasicFonts) - self.delayedFonts = [] - - def setCompression(self, onoff): - # XXX: maybe this should also set self.defaultStreamFilters? - self.compression = onoff - - def updateSignature(self, thing): - "add information to the signature" - if self._ID: return # but not if its used already! - self.signature.update(str(thing)) - - def ID(self): - "A unique fingerprint for the file (unless in invariant mode)" - if self._ID: - return self._ID - digest = self.signature.digest() - doc = DummyDoc() - ID = PDFString(digest,enc='raw') - IDs = ID.format(doc) - self._ID = "%s %% ReportLab generated PDF document -- digest (http://www.reportlab.com) %s [%s %s] %s" % ( - LINEEND, LINEEND, IDs, IDs, LINEEND) - return self._ID - - def SaveToFile(self, filename, canvas): - if callable(getattr(filename, "write",None)): - myfile = 0 - f = filename - filename = str(getattr(filename,'name','')) - else : - myfile = 1 - filename = str(filename) - f = open(filename, "wb") - f.write(self.GetPDFData(canvas)) - if myfile: - f.close() - import os - if os.name=='mac': - from reportlab.lib.utils import markfilename - markfilename(filename) # do platform specific file junk - if getattr(canvas,'_verbosity',None): print 'saved', filename - - def GetPDFData(self, canvas): - # realize delayed fonts - for fnt in self.delayedFonts: - fnt.addObjects(self) - # add info stuff to signature - self.info.invariant = self.invariant - self.info.digest(self.signature) - ### later: maybe add more info to sig? - # prepare outline - self.Reference(self.Catalog) - self.Reference(self.info) - outline = self.outline - outline.prepare(self, canvas) - return self.format() - - def inPage(self): - """specify the current object as a page (enables reference binding and other page features)""" - if self.inObject is not None: - if self.inObject=="page": return - raise ValueError, "can't go in page already in object %s" % self.inObject - self.inObject = "page" - - def inForm(self): - """specify that we are in a form xobject (disable page features, etc)""" - # don't need this check anymore since going in a form pushes old context at canvas level. - #if self.inObject not in ["form", None]: - # raise ValueError, "can't go in form already in object %s" % self.inObject - self.inObject = "form" - # don't need to do anything else, I think... - - def getInternalFontName(self, psfontname): - fm = self.fontMapping - if fm.has_key(psfontname): - return fm[psfontname] - else: - try: - # does pdfmetrics know about it? if so, add - fontObj = pdfmetrics.getFont(psfontname) - if getattr(fontObj, '_dynamicFont', 0): - raise PDFError, "getInternalFontName(%s) called for a dynamic font" % repr(psfontname) - fontObj.addObjects(self) - #self.addFont(fontObj) - return fm[psfontname] - except KeyError: - raise PDFError, "Font %s not known!" % repr(psfontname) - - def thisPageName(self): - return "Page"+repr(self.pageCounter) - - def thisPageRef(self): - return PDFObjectReference(self.thisPageName()) - - def addPage(self, page): - name = self.thisPageName() - self.Reference(page, name) - self.Pages.addPage(page) - self.pageCounter = self.pageCounter+1 - self.inObject = None - - - def addForm(self, name, form): - """add a Form XObject.""" - # XXX should check that name is a legal PDF name - if self.inObject != "form": - self.inForm() - self.Reference(form, xObjectName(name)) - self.inObject = None - - def annotationName(self, externalname): - return "Annot.%s"%externalname - - def addAnnotation(self, name, annotation): - self.Reference(annotation, self.annotationName(name)) - - def refAnnotation(self, name): - internalname = self.annotationName(name) - return PDFObjectReference(internalname) - - def setTitle(self, title): - "embeds in PDF file" - self.info.title = title - - def setAuthor(self, author): - "embedded in PDF file" - self.info.author = author - - def setSubject(self, subject): - "embeds in PDF file" - self.info.subject = subject - - def setDateFormatter(self, dateFormatter): - self.info._dateFormatter = dateFormatter - - def getAvailableFonts(self): - fontnames = self.fontMapping.keys() - # the standard 14 are also always available! (even if not initialized yet) - import _fontdata - for name in _fontdata.standardFonts: - if name not in fontnames: - fontnames.append(name) - fontnames.sort() - return fontnames - - def format(self): - # register the Catalog/INfo and then format the objects one by one until exhausted - # (possible infinite loop if there is a bug that continually makes new objects/refs...) - # Prepare encryption - self.encrypt.prepare(self) - cat = self.Catalog - info = self.info - self.Reference(self.Catalog) - self.Reference(self.info) - # register the encryption dictionary if present - encryptref = None - encryptinfo = self.encrypt.info() - if encryptinfo: - encryptref = self.Reference(encryptinfo) - # make std fonts (this could be made optional - counter = 0 # start at first object (object 1 after preincrement) - ids = [] # the collection of object ids in object number order - numbertoid = self.numberToId - idToNV = self.idToObjectNumberAndVersion - idToOb = self.idToObject - idToOf = self.idToOffset - ### note that new entries may be "appended" DURING FORMATTING - done = None - File = PDFFile() # output collector - while done is None: - counter += 1 # do next object... - if numbertoid.has_key(counter): - id = numbertoid[counter] - #printidToOb - obj = idToOb[id] - IO = PDFIndirectObject(id, obj) - # register object number and version - #encrypt.register(id, - IOf = IO.format(self) - # add a comment to the PDF output - if not rl_config.invariant and DoComments: - try: - classname = obj.__class__.__name__ - except: - classname = repr(obj) - File.add("%% %s: class %s %s" % (repr(id), classname[:50], LINEEND)) - offset = File.add(IOf) - idToOf[id] = offset - ids.append(id) - else: - done = 1 - # sanity checks (must happen AFTER formatting) - lno = len(numbertoid) - if counter-1!=lno: - raise ValueError, "counter %s doesn't match number to id dictionary %s" %(counter, lno) - # now add the xref - xref = PDFCrossReferenceTable() - xref.addsection(0, ids) - xreff = xref.format(self) - xrefoffset = File.add(xreff) - # now add the trailer - trailer = PDFTrailer( - startxref = xrefoffset, - Size = lno+1, - Root = self.Reference(cat), - Info = self.Reference(info), - Encrypt = encryptref, - ID = self.ID(), - ) - trailerf = trailer.format(self) - File.add(trailerf) - # return string format for pdf file - return File.format(self) - - def hasForm(self, name): - """test for existence of named form""" - internalname = xObjectName(name) - return self.idToObject.has_key(internalname) - - def getFormBBox(self, name): - "get the declared bounding box of the form as a list" - internalname = xObjectName(name) - if self.idToObject.has_key(internalname): - theform = self.idToObject[internalname] - if isinstance(theform, PDFFormXObject): - # internally defined form - return theform.BBoxList() - elif isinstance(theform, PDFStream): - # externally defined form - return list(theform.dictionary.dict["BBox"].sequence) - else: - raise ValueError, "I don't understand the form instance %s" % repr(name) - - def getXObjectName(self, name): - """Lets canvas find out what form is called internally. - Never mind whether it is defined yet or not.""" - return xObjectName(name) - - def xobjDict(self, formnames): - """construct an xobject dict (for inclusion in a resource dict, usually) - from a list of form names (images not yet supported)""" - D = {} - for name in formnames: - internalname = xObjectName(name) - reference = PDFObjectReference(internalname) - D[internalname] = reference - #print "xobjDict D", D - return PDFDictionary(D) - - def Reference(self, object, name=None, InstanceType=InstanceType): - ### note references may "grow" during the final formatting pass: don't use d.keys()! - # don't make references to other references, or non instances, unless they are named! - #print"object type is ", type(object) - tob = type(object) - idToObject = self.idToObject - if name is None and ( - (tob is not InstanceType) or (tob is InstanceType and object.__class__ is PDFObjectReference)): - return object - if hasattr(object, __InternalName__): - # already registered - intname = object.__InternalName__ - if name is not None and name!=intname: - raise ValueError, "attempt to reregister object %s with new name %s" % ( - repr(intname), repr(name)) - if not idToObject.has_key(intname): - raise ValueError, "object named but not registered" - return PDFObjectReference(intname) - # otherwise register the new object - objectcounter = self.objectcounter = self.objectcounter+1 - if name is None: - name = "R"+repr(objectcounter) - if idToObject.has_key(name): - other = idToObject[name] - if other!=object: - raise ValueError, "redefining named object: "+repr(name) - return PDFObjectReference(name) - if tob is InstanceType: - object.__InternalName__ = name - #print "name", name, "counter", objectcounter - self.idToObjectNumberAndVersion[name] = (objectcounter, 0) - self.numberToId[objectcounter] = name - idToObject[name] = object - return PDFObjectReference(name) - -### chapter 4 Objects - -PDFtrue = "true" -PDFfalse = "false" -PDFnull = "null" - -class PDFText: - def __init__(self, t): - self.t = t - def format(self, document): - result = binascii.hexlify(document.encrypt.encode(self.t)) - return "<%s>" % result - def __str__(self): - dummydoc = DummyDoc() - return self.format(dummydoc) - -def PDFnumber(n): - return n - -import re -_re_cleanparens=re.compile('[^()]') -del re -def _isbalanced(s): - '''test whether a string is balanced in parens''' - s = _re_cleanparens.sub('',s) - n = 0 - for c in s: - if c=='(': n+=1 - else: - n -= 1 - if n<0: return 0 - return not n and 1 or 0 - -def _checkPdfdoc(utext): - '''return true if no Pdfdoc encoding errors''' - try: - utext.encode('pdfdoc') - return 1 - except UnicodeEncodeError, e: - return 0 - -class PDFString: - def __init__(self, s, escape=1, enc='auto'): - '''s can be unicode/utf8 or a PDFString - if escape is true then the output will be passed through escape - if enc is raw then the string will be left alone - if enc is auto we'll try and automatically adapt to utf_16_be if the - effective string is not entirely in pdfdoc - ''' - if isinstance(s,PDFString): - self.s = s.s - self.escape = s.escape - self.enc = s.enc - else: - self.s = s - self.escape = escape - self.enc = enc - def format(self, document): - s = self.s - enc = getattr(self,'enc','auto') - if type(s) is str: - if enc is 'auto': - try: - u = s.decode('utf8') - except: - print s - raise - if _checkPdfdoc(u): - s = u.encode('pdfdoc') - else: - s = codecs.BOM_UTF16_BE+u.encode('utf_16_be') - elif type(s) is unicode: - if enc is 'auto': - if _checkPdfdoc(s): - s = s.encode('pdfdoc') - else: - s = codecs.BOM_UTF16_BE+s.encode('utf_16_be') - else: - s = codecs.BOM_UTF16_BE+s.encode('utf_16_be') - else: - raise ValueError('PDFString argument must be str/unicode not %s' % type(s)) - - escape = getattr(self,'escape',1) - if not isinstance(document.encrypt,NoEncryption): - s = document.encrypt.encode(s) - escape = 1 - if escape: - try: - es = "(%s)" % pdfutils._escape(s) - except: - raise ValueError("cannot escape %s %s" % (s, repr(s))) - if escape&2: - es = es.replace('\\012','\n') - if escape&4 and _isbalanced(s): - es = es.replace('\\(','(').replace('\\)',')') - return es - else: - return '(%s)' % s - def __str__(self): - return "(%s)" % pdfutils._escape(self.s) - -def PDFName(data,lo=chr(0x21),hi=chr(0x7e)): - # might need to change this to class for encryption - # NOTE: RESULT MUST ALWAYS SUPPORT MEANINGFUL COMPARISONS (EQUALITY) AND HASH - # first convert the name - L = list(data) - for i,c in enumerate(L): - if chi or c in "%()<>{}[]#": - L[i] = "#"+hex(ord(c))[2:] # forget the 0x thing... - return "/"+(''.join(L)) - -class PDFDictionary: - - multiline = LongFormat - def __init__(self, dict=None): - """dict should be namestring to value eg "a": 122 NOT pdfname to value NOT "/a":122""" - if dict is None: - self.dict = {} - else: - self.dict = dict.copy() - def __setitem__(self, name, value): - self.dict[name] = value - def Reference(name, document): - self.dict[name] = document.Reference(self.dict[name]) - def format(self, document,IND=LINEEND+' '): - dict = self.dict - keys = dict.keys() - keys.sort() - L = [(format(PDFName(k),document)+" "+format(dict[k],document)) for k in keys] - if self.multiline: - L = IND.join(L) - else: - # break up every 6 elements anyway - t=L.insert - for i in xrange(6, len(L), 6): - t(i,LINEEND) - L = " ".join(L) - return "<< %s >>" % L - -# stream filters are objects to support round trip and -# possibly in the future also support parameters -class PDFStreamFilterZCompress: - pdfname = "FlateDecode" - def encode(self, text): - from reportlab.lib.utils import import_zlib - zlib = import_zlib() - if not zlib: raise ImportError, "cannot z-compress zlib unavailable" - return zlib.compress(text) - def decode(self, encoded): - from reportlab.lib.utils import import_zlib - zlib = import_zlib() - if not zlib: raise ImportError, "cannot z-decompress zlib unavailable" - return zlib.decompress(encoded) - -# need only one of these, unless we implement parameters later -PDFZCompress = PDFStreamFilterZCompress() - -class PDFStreamFilterBase85Encode: - pdfname = "ASCII85Decode" - def encode(self, text): - from pdfutils import _AsciiBase85Encode, _wrap - return _wrap(_AsciiBase85Encode(text)) - def decode(self, text): - from pdfutils import _AsciiBase85Decode - return _AsciiBase85Decode(text) - -# need only one of these too -PDFBase85Encode = PDFStreamFilterBase85Encode() - -STREAMFMT = ("%(dictionary)s%(LINEEND)s" # dictionary - "stream" # stream keyword - "%(LINEEND)s" # a line end (could be just a \n) - "%(content)s" # the content, with no lineend - "endstream%(LINEEND)s" # the endstream keyword - ) -class PDFStream: - '''set dictionary elements explicitly stream.dictionary[name]=value''' - ### compression stuff not implemented yet - __RefOnly__ = 1 # must be at top level - def __init__(self, dictionary=None, content=None): - if dictionary is None: - dictionary = PDFDictionary() - self.dictionary = dictionary - self.content = content - self.filters = None - def format(self, document): - dictionary = self.dictionary - # copy it for modification - dictionary = PDFDictionary(dictionary.dict.copy()) - content = self.content - filters = self.filters - if self.content is None: - raise ValueError, "stream content not set" - if filters is None: - filters = document.defaultStreamFilters - # only apply filters if they haven't been applied elsewhere - if filters is not None and not dictionary.dict.has_key("Filter"): - # apply filters in reverse order listed - rf = list(filters) - rf.reverse() - fnames = [] - for f in rf: - #print "*****************content:"; print repr(content[:200]) - #print "*****************filter", f.pdfname - content = f.encode(content) - fnames.insert(0, PDFName(f.pdfname)) - #print "*****************finally:"; print content[:200] - #print "****** FILTERS", fnames - #stop - dictionary["Filter"] = PDFArray(fnames) - # "stream encoding is done after all filters have been applied" - content = document.encrypt.encode(content) - fc = format(content, document) - #print "type(content)", type(content), len(content), type(self.dictionary) - lc = len(content) - #if fc!=content: burp - # set dictionary length parameter - dictionary["Length"] = lc - fd = format(dictionary, document) - sdict = LINEENDDICT.copy() - sdict["dictionary"] = fd - sdict["content"] = fc - return STREAMFMT % sdict - -def teststream(content=None): - #content = "" # test - if content is None: - content = teststreamcontent - content = string.strip(content) - content = string.replace(content, "\n", LINEEND) + LINEEND - S = PDFStream() - S.content = content - S.filters = [PDFBase85Encode, PDFZCompress] - # nothing else needed... - S.__Comment__ = "test stream" - return S - -teststreamcontent = """ -1 0 0 1 0 0 cm BT /F9 12 Tf 14.4 TL ET -1.00 0.00 1.00 rg -n 72.00 72.00 432.00 648.00 re B* -""" -class PDFArray: - multiline = LongFormat - def __init__(self, sequence): - self.sequence = list(sequence) - def References(self, document): - """make all objects in sequence references""" - self.sequence = map(document.Reference, self.sequence) - def format(self, document, IND=LINEEND+' '): - L = [format(e, document) for e in self.sequence] - if self.multiline: - L = IND.join(L) - else: - # break up every 10 elements anyway - breakline = LINEEND+" " - for i in xrange(10, len(L), 10): - L.insert(i,breakline) - L = ' '.join(L) - return "[ %s ]" % L - -INDIRECTOBFMT = ("%(n)s %(v)s obj%(LINEEND)s" - "%(content)s" "%(LINEEND)s" - "endobj" "%(LINEEND)s") - -class PDFIndirectObject: - __RefOnly__ = 1 - def __init__(self, name, content): - self.name = name - self.content = content - def format(self, document): - name = self.name - (n, v) = document.idToObjectNumberAndVersion[name] - # set encryption parameters - document.encrypt.register(n, v) - content = self.content - fcontent = format(content, document, toplevel=1) # yes this is at top level - sdict = LINEENDDICT.copy() - sdict["n"] = n - sdict["v"] = v - sdict["content"] = fcontent - return INDIRECTOBFMT % sdict - -class PDFObjectReference: - def __init__(self, name): - self.name = name - def format(self, document): - try: - return "%s %s R" % document.idToObjectNumberAndVersion[self.name] - except: - raise KeyError, "forward reference to %s not resolved upon final formatting" % repr(self.name) - -### chapter 5 -# Following Ken Lunde's advice and the PDF spec, this includes -# some high-order bytes. I chose the characters for Tokyo -# in Shift-JIS encoding, as these cannot be mistaken for -# any other encoding, and we'll be able to tell if something -# has run our PDF files through a dodgy Unicode conversion. -PDFHeader = ( -"%PDF-1.3"+LINEEND+ -"%\223\214\213\236 ReportLab Generated PDF document http://www.reportlab.com"+LINEEND) - -class PDFFile: - ### just accumulates strings: keeps track of current offset - def __init__(self): - self.strings = [] - self.write = self.strings.append - self.offset = 0 - self.add(PDFHeader) - - def closeOrReset(self): - pass - - def add(self, s): - """should be constructed as late as possible, return position where placed""" - result = self.offset - self.offset = result+len(s) - self.write(s) - return result - def format(self, document): - strings = map(str, self.strings) # final conversion, in case of lazy objects - return string.join(strings, "") - -XREFFMT = '%0.10d %0.5d n' - -class PDFCrossReferenceSubsection: - def __init__(self, firstentrynumber, idsequence): - self.firstentrynumber = firstentrynumber - self.idsequence = idsequence - def format(self, document): - """id sequence should represent contiguous object nums else error. free numbers not supported (yet)""" - firstentrynumber = self.firstentrynumber - idsequence = self.idsequence - entries = list(idsequence) - nentries = len(idsequence) - # special case: object number 0 is always free - taken = {} - if firstentrynumber==0: - taken[0] = "standard free entry" - nentries = nentries+1 - entries.insert(0, "0000000000 65535 f") - idToNV = document.idToObjectNumberAndVersion - idToOffset = document.idToOffset - lastentrynumber = firstentrynumber+nentries-1 - for id in idsequence: - (num, version) = idToNV[id] - if taken.has_key(num): - raise ValueError, "object number collision %s %s %s" % (num, repr(id), repr(taken[id])) - if num>lastentrynumber or num>""" - -class PDFOutlines0: - __Comment__ = "TEST OUTLINE!" - text = string.replace(DUMMYOUTLINE, "\n", LINEEND) - __RefOnly__ = 1 - def format(self, document): - return self.text - - -class OutlineEntryObject: - "an entry in an outline" - Title = Dest = Parent = Prev = Next = First = Last = Count = None - def format(self, document): - D = {} - D["Title"] = PDFString(self.Title) - D["Parent"] = self.Parent - D["Dest"] = self.Dest - for n in ("Prev", "Next", "First", "Last", "Count"): - v = getattr(self, n) - if v is not None: - D[n] = v - PD = PDFDictionary(D) - return PD.format(document) - - -class PDFOutlines: - """takes a recursive list of outline destinations - like - out = PDFOutline1() - out.setNames(canvas, # requires canvas for name resolution - "chapter1dest", - ("chapter2dest", - ["chapter2section1dest", - "chapter2section2dest", - "chapter2conclusiondest"] - ), # end of chapter2 description - "chapter3dest", - ("chapter4dest", ["c4s1", "c4s2"]) - ) - Higher layers may build this structure incrementally. KISS at base level. - """ - # first attempt, many possible features missing. - #no init for now - mydestinations = ready = None - counter = 0 - currentlevel = -1 # ie, no levels yet - - def __init__(self): - self.destinationnamestotitles = {} - self.destinationstotitles = {} - self.levelstack = [] - self.buildtree = [] - self.closedict = {} # dictionary of "closed" destinations in the outline - - def addOutlineEntry(self, destinationname, level=0, title=None, closed=None): - """destinationname of None means "close the tree" """ - from types import IntType, TupleType - if destinationname is None and level!=0: - raise ValueError, "close tree must have level of 0" - if type(level) is not IntType: raise ValueError, "level must be integer, got %s" % type(level) - if level<0: raise ValueError, "negative levels not allowed" - if title is None: title = destinationname - currentlevel = self.currentlevel - stack = self.levelstack - tree = self.buildtree - # adjust currentlevel and stack to match level - if level>currentlevel: - if level>currentlevel+1: - raise ValueError, "can't jump from outline level %s to level %s, need intermediates" %(currentlevel, level) - level = currentlevel = currentlevel+1 - stack.append([]) - while levelref - if Ot is ListType or Ot is TupleType: - L = [] - for o in object: - L.append(self.translateNames(canvas, o)) - if Ot is TupleType: - return tuple(L) - return L - raise "in outline, destination name must be string: got a %s" % Ot - - def prepare(self, document, canvas): - """prepare all data structures required for save operation (create related objects)""" - if self.mydestinations is None: - if self.levelstack: - self.addOutlineEntry(None) # close the tree - destnames = self.levelstack[0] - #from pprint import pprint; pprint(destnames); stop - self.mydestinations = self.translateNames(canvas, destnames) - else: - self.first = self.last = None - self.count = 0 - self.ready = 1 - return - #self.first = document.objectReference("Outline.First") - #self.last = document.objectReference("Outline.Last") - # XXXX this needs to be generalized for closed entries! - self.count = count(self.mydestinations, self.closedict) - (self.first, self.last) = self.maketree(document, self.mydestinations, toplevel=1) - self.ready = 1 - - def maketree(self, document, destinationtree, Parent=None, toplevel=0): - from types import ListType, TupleType, DictType - tdestinationtree = type(destinationtree) - if toplevel: - levelname = "Outline" - Parent = document.Reference(document.Outlines) - else: - self.count = self.count+1 - levelname = "Outline.%s" % self.count - if Parent is None: - raise ValueError, "non-top level outline elt parent must be specified" - if tdestinationtree is not ListType and tdestinationtree is not TupleType: - raise ValueError, "destinationtree must be list or tuple, got %s" - nelts = len(destinationtree) - lastindex = nelts-1 - lastelt = firstref = lastref = None - destinationnamestotitles = self.destinationnamestotitles - closedict = self.closedict - for index in range(nelts): - eltobj = OutlineEntryObject() - eltobj.Parent = Parent - eltname = "%s.%s" % (levelname, index) - eltref = document.Reference(eltobj, eltname) - #document.add(eltname, eltobj) - if lastelt is not None: - lastelt.Next = eltref - eltobj.Prev = lastref - if firstref is None: - firstref = eltref - lastref = eltref - lastelt = eltobj # advance eltobj - lastref = eltref - elt = destinationtree[index] - te = type(elt) - if te is DictType: - # simple leaf {name: dest} - leafdict = elt - elif te is TupleType: - # leaf with subsections: ({name: ref}, subsections) XXXX should clean up (see count(...)) - try: - (leafdict, subsections) = elt - except: - raise ValueError, "destination tree elt tuple should have two elts, got %s" % len(elt) - eltobj.Count = count(subsections, closedict) - (eltobj.First, eltobj.Last) = self.maketree(document, subsections, eltref) - else: - raise ValueError, "destination tree elt should be dict or tuple, got %s" % te - try: - [(Title, Dest)] = leafdict.items() - except: - raise ValueError, "bad outline leaf dictionary, should have one entry "+str(elt) - eltobj.Title = destinationnamestotitles[Title] - eltobj.Dest = Dest - if te is TupleType and closedict.has_key(Dest): - # closed subsection, count should be negative - eltobj.Count = -eltobj.Count - return (firstref, lastref) - -def count(tree, closedict=None): - """utility for outline: recursively count leaves in a tuple/list tree""" - from operator import add - from types import TupleType, ListType - tt = type(tree) - if tt is TupleType: - # leaf with subsections XXXX should clean up this structural usage - (leafdict, subsections) = tree - [(Title, Dest)] = leafdict.items() - if closedict and closedict.has_key(Dest): - return 1 # closed tree element - if tt is TupleType or tt is ListType: - #return reduce(add, map(count, tree)) - counts = [] - for e in tree: - counts.append(count(e, closedict)) - return reduce(add, counts) - return 1 - -class PDFInfo: - """PDF documents can have basic information embedded, viewable from - File | Document Info in Acrobat Reader. If this is wrong, you get - Postscript errors while printing, even though it does not print.""" - producer = "ReportLab http://www.reportlab.com" - title = "untitled" - author = "anonymous" - subject = "unspecified" - _dateFormatter = None - - def __init__(self): - self.invariant = rl_config.invariant - - def digest(self, md5object): - # add self information to signature - for x in (self.title, self.author, self.subject): - md5object.update(str(x)) - - def format(self, document): - D = {} - D["Title"] = PDFString(self.title) - D["Author"] = PDFString(self.author) - D["CreationDate"] = PDFDate(invariant=self.invariant,dateFormatter=self._dateFormatter) - D["Producer"] = PDFString(self.producer) - D["Subject"] = PDFString(self.subject) - PD = PDFDictionary(D) - return PD.format(document) - -# skipping thumbnails, etc - - -class Annotation: - """superclass for all annotations.""" - defaults = [("Type", PDFName("Annot"),)] - required = ("Type", "Rect", "Contents", "Subtype") - permitted = required+( - "Border", "C", "T", "M", "F", "H", "BS", "AA", "AS", "Popup", "P", "AP") - def cvtdict(self, d, escape=1): - """transform dict args from python form to pdf string rep as needed""" - Rect = d["Rect"] - if type(Rect) is not types.StringType: - d["Rect"] = PDFArray(Rect) - d["Contents"] = PDFString(d["Contents"],escape) - return d - def AnnotationDict(self, **kw): - if 'escape' in kw: - escape = kw['escape'] - del kw['escape'] - else: - escape = 1 - d = {} - for (name,val) in self.defaults: - d[name] = val - d.update(kw) - for name in self.required: - if not d.has_key(name): - raise ValueError, "keyword argument %s missing" % name - d = self.cvtdict(d,escape=escape) - permitted = self.permitted - for name in d.keys(): - if name not in permitted: - raise ValueError, "bad annotation dictionary name %s" % name - return PDFDictionary(d) - def Dict(self): - raise ValueError, "DictString undefined for virtual superclass Annotation, must overload" - # but usually - #return self.AnnotationDict(self, Rect=(a,b,c,d)) or whatever - def format(self, document): - D = self.Dict() - return D.format(document) - -class TextAnnotation(Annotation): - permitted = Annotation.permitted + ( - "Open", "Name") - def __init__(self, Rect, Contents, **kw): - self.Rect = Rect - self.Contents = Contents - self.otherkw = kw - def Dict(self): - d = {} - d.update(self.otherkw) - d["Rect"] = self.Rect - d["Contents"] = self.Contents - d["Subtype"] = "/Text" - return self.AnnotationDict(**d) - -class FreeTextAnnotation(Annotation): - permitted = Annotation.permitted + ("DA",) - def __init__(self, Rect, Contents, DA, **kw): - self.Rect = Rect - self.Contents = Contents - self.DA = DA - self.otherkw = kw - def Dict(self): - d = {} - d.update(self.otherkw) - d["Rect"] = self.Rect - d["Contents"] = self.Contents - d["DA"] = self.DA - d["Subtype"] = "/FreeText" - return self.AnnotationDict(**d) - -class LinkAnnotation(Annotation): - - permitted = Annotation.permitted + ( - "Dest", "A", "PA") - def __init__(self, Rect, Contents, Destination, Border="[0 0 1]", **kw): - self.Border = Border - self.Rect = Rect - self.Contents = Contents - self.Destination = Destination - self.otherkw = kw - - def dummyDictString(self): # old, testing - return """ - << /Type /Annot /Subtype /Link /Rect [71 717 190 734] /Border [16 16 1] - /Dest [23 0 R /Fit] >> - """ - - def Dict(self): - d = {} - d.update(self.otherkw) - d["Border"] = self.Border - d["Rect"] = self.Rect - d["Contents"] = self.Contents - d["Subtype"] = "/Link" - d["Dest"] = self.Destination - return apply(self.AnnotationDict, (), d) - - -# skipping names tree - -# skipping actions - -# skipping names trees - -# skipping to chapter 7 - -class PDFRectangle: - def __init__(self, llx, lly, urx, ury): - self.llx, self.lly, self.ulx, self.ury = llx, lly, urx, ury - def format(self, document): - A = PDFArray([self.llx, self.lly, self.ulx, self.ury]) - return format(A, document) - -_NOWT=None -def _getTimeStamp(): - global _NOWT - if not _NOWT: - import time - _NOWT = time.time() - return _NOWT - -class PDFDate: - # gmt offset not yet suppported - def __init__(self, yyyy=None, mm=None, dd=None, hh=None, m=None, s=None, - invariant=rl_config.invariant,dateFormatter=None): - if None in (yyyy, mm, dd, hh, m, s): - if invariant: - now = (2000,01,01,00,00,00,0) - else: - import time - now = tuple(time.localtime(_getTimeStamp())[:6]) - if yyyy is None: yyyy=now[0] - if mm is None: mm=now[1] - if dd is None: dd=now[2] - if hh is None: hh=now[3] - if m is None: m=now[4] - if s is None: s=now[5] - self.date=(yyyy,mm,dd,hh,m,s) - self.dateFormatter = dateFormatter - - def format(self, doc): - dfmt = self.dateFormatter or ( - lambda yyyy,mm,dd,hh,m,s: '%04d%02d%02d%02d%02d%02d' % (yyyy,mm,dd,hh,m,s)) - return format(PDFString(dfmt(*self.date)), doc) - -class Destination: - """not a pdfobject! This is a placeholder that can delegates - to a pdf object only after it has been defined by the methods - below. EG a Destination can refer to Appendix A before it has been - defined, but only if Appendix A is explicitly noted as a destination - and resolved before the document is generated... - For example the following sequence causes resolution before doc generation. - d = Destination() - d.fit() # or other format defining method call - d.setPage(p) - (at present setPageRef is called on generation of the page). - """ - representation = format = page = None - def __init__(self,name): - self.name = name - self.fmt = self.page = None - def format(self, document): - f = self.fmt - if f is None: raise ValueError, "format not resolved %s" % self.name - p = self.page - if p is None: raise ValueError, "Page reference unbound %s" % self.name - f.page = p - return f.format(document) - def xyz(self, left, top, zoom): # see pdfspec mar 11 99 pp184+ - self.fmt = PDFDestinationXYZ(None, left, top, zoom) - def fit(self): - self.fmt = PDFDestinationFit(None) - def fitb(self): - self.fmt = PDFDestinationFitB(None) - def fith(self, top): - self.fmt = PDFDestinationFitH(None,top) - def fitv(self, left): - self.fmt = PDFDestinationFitV(None, left) - def fitbh(self, top): - self.fmt = PDFDestinationFitBH(None, top) - def fitbv(self, left): - self.fmt = PDFDestinationFitBV(None, left) - def fitr(self, left, bottom, right, top): - self.fmt = PDFDestinationFitR(None, left, bottom, right, top) - def setPage(self, page): - self.page = page - #self.fmt.page = page # may not yet be defined! - -class PDFDestinationXYZ: - typename = "XYZ" - def __init__(self, page, left, top, zoom): - self.page = page - self.top = top - self.zoom = zoom - self.left = left - def format(self, document): - pageref = document.Reference(self.page) - A = PDFArray( [ pageref, PDFName(self.typename), self.left, self.top, self.zoom ] ) - return format(A, document) - -class PDFDestinationFit: - typename = "Fit" - def __init__(self, page): - self.page = page - def format(self, document): - pageref = document.Reference(self.page) - A = PDFArray( [ pageref, PDFName(self.typename) ] ) - return format(A, document) - -class PDFDestinationFitB(PDFDestinationFit): - typename = "FitB" - -class PDFDestinationFitH: - typename = "FitH" - def __init__(self, page, top): - self.page = page; self.top=top - def format(self, document): - pageref = document.Reference(self.page) - A = PDFArray( [ pageref, PDFName(self.typename), self.top ] ) - return format(A, document) - -class PDFDestinationFitBH(PDFDestinationFitH): - typename = "FitBH" - -class PDFDestinationFitV: - typename = "FitV" - def __init__(self, page, left): - self.page = page; self.left=left - def format(self, document): - pageref = document.Reference(self.page) - A = PDFArray( [ pageref, PDFName(self.typename), self.left ] ) - return format(A, document) - -class PDFDestinationBV(PDFDestinationFitV): - typename = "FitBV" - -class PDFDestinationFitR: - typename = "FitR" - def __init__(self, page, left, bottom, right, top): - self.page = page; self.left=left; self.bottom=bottom; self.right=right; self.top=top - def format(self, document): - pageref = document.Reference(self.page) - A = PDFArray( [ pageref, PDFName(self.typename), self.left, self.bottom, self.right, self.top] ) - return format(A, document) - -# named destinations need nothing - -# skipping filespecs - -class PDFResourceDictionary: - """each element *could* be reset to a reference if desired""" - def __init__(self): - self.ColorSpace = {} - self.XObject = {} - self.ExtGState = {} - self.Font = {} - self.Pattern = {} - self.ProcSet = [] - self.Properties = {} - self.Shading = {} - # ?by default define the basicprocs - self.basicProcs() - stdprocs = map(PDFName, string.split("PDF Text ImageB ImageC ImageI")) - dict_attributes = ("ColorSpace", "XObject", "ExtGState", "Font", "Pattern", "Properties", "Shading") - def allProcs(self): - # define all standard procsets - self.ProcSet = self.stdprocs - def basicProcs(self): - self.ProcSet = self.stdprocs[:2] # just PDF and Text - def basicFonts(self): - self.Font = PDFObjectReference(BasicFonts) - def format(self, document): - D = {} - from types import ListType, DictType - for dname in self.dict_attributes: - v = getattr(self, dname) - if type(v) is DictType: - if v: - dv = PDFDictionary(v) - D[dname] = dv - else: - D[dname] = v - v = self.ProcSet - dname = "ProcSet" - if type(v) is ListType: - if v: - dv = PDFArray(v) - D[dname] = dv - else: - D[dname] = v - DD = PDFDictionary(D) - return format(DD, document) - - ############################################################################## - # - # Font objects - the PDFDocument.addFont() method knows which of these - # to construct when given a user-facing Font object - # - ############################################################################## - - -class PDFType1Font: - """no init: set attributes explicitly""" - __RefOnly__ = 1 - # note! /Name appears to be an undocumented attribute.... - name_attributes = string.split("Type Subtype BaseFont Name") - Type = "Font" - Subtype = "Type1" - # these attributes are assumed to already be of the right type - local_attributes = string.split("FirstChar LastChar Widths Encoding ToUnicode FontDescriptor") - def format(self, document): - D = {} - for name in self.name_attributes: - if hasattr(self, name): - value = getattr(self, name) - D[name] = PDFName(value) - for name in self.local_attributes: - if hasattr(self, name): - value = getattr(self, name) - D[name] = value - #print D - PD = PDFDictionary(D) - return PD.format(document) - -## These attribute listings will be useful in future, even if we -## put them elsewhere - -class PDFTrueTypeFont(PDFType1Font): - Subtype = "TrueType" - #local_attributes = string.split("FirstChar LastChar Widths Encoding ToUnicode FontDescriptor") #same - -##class PDFMMType1Font(PDFType1Font): -## Subtype = "MMType1" -## -##class PDFType3Font(PDFType1Font): -## Subtype = "Type3" -## local_attributes = string.split( -## "FirstChar LastChar Widths CharProcs FontBBox FontMatrix Resources Encoding") -## -##class PDFType0Font(PDFType1Font): -## Subtype = "Type0" -## local_attributes = string.split( -## "DescendantFonts Encoding") -## -##class PDFCIDFontType0(PDFType1Font): -## Subtype = "CIDFontType0" -## local_attributes = string.split( -## "CIDSystemInfo FontDescriptor DW W DW2 W2 Registry Ordering Supplement") -## -##class PDFCIDFontType0(PDFType1Font): -## Subtype = "CIDFontType2" -## local_attributes = string.split( -## "BaseFont CIDToGIDMap CIDSystemInfo FontDescriptor DW W DW2 W2") -## -##class PDFEncoding(PDFType1Font): -## Type = "Encoding" -## name_attributes = string.split("Type BaseEncoding") -## # these attributes are assumed to already be of the right type -## local_attributes = ["Differences"] -## - -# UGLY ALERT - this needs turning into something O-O, it was hacked -# across from the pdfmetrics.Encoding class to avoid circularity - -# skipping CMaps - -class PDFFormXObject: - # like page requires .info set by some higher level (doc) - # XXXX any resource used in a form must be propagated up to the page that (recursively) uses - # the form!! (not implemented yet). - XObjects = Annots = BBox = Matrix = Contents = stream = Resources = None - hasImages = 1 # probably should change - compression = 0 - def __init__(self, lowerx, lowery, upperx, uppery): - #not done - self.lowerx = lowerx; self.lowery=lowery; self.upperx=upperx; self.uppery=uppery - - def setStreamList(self, data): - if type(data) is types.ListType: - data = string.join(data, LINEEND) - self.stream = data - - def BBoxList(self): - "get the declared bounding box for the form as a list" - if self.BBox: - return list(self.BBox.sequence) - else: - return [self.lowerx, self.lowery, self.upperx, self.uppery] - - def format(self, document): - self.BBox = self.BBox or PDFArray([self.lowerx, self.lowery, self.upperx, self.uppery]) - self.Matrix = self.Matrix or PDFArray([1, 0, 0, 1, 0, 0]) - if not self.Annots: - self.Annots = None - else: - #these must be transferred to the page when the form is used - raise ValueError, "annotations not reimplemented yet" - if not self.Contents: - stream = self.stream - if not stream: - self.Contents = teststream() - else: - S = PDFStream() - S.content = stream - # need to add filter stuff (?) - S.__Comment__ = "xobject form stream" - self.Contents = S - if not self.Resources: - resources = PDFResourceDictionary() - # fonts! - resources.basicFonts() - if self.hasImages: - resources.allProcs() - else: - resources.basicProcs() - if self.XObjects: - #print "XObjects", self.XObjects.dict - resources.XObject = self.XObjects - if self.compression: - self.Contents.filters = [PDFBase85Encode, PDFZCompress] - sdict = self.Contents.dictionary - sdict["Type"] = PDFName("XObject") - sdict["Subtype"] = PDFName("Form") - sdict["FormType"] = 1 - sdict["BBox"] = self.BBox - sdict["Matrix"] = self.Matrix - sdict["Resources"] = resources - return self.Contents.format(document) - -class PDFPostScriptXObject: - "For embedding PD (e.g. tray commands) in PDF" - def __init__(self, content=None): - self.content = content - - def format(self, document): - S = PDFStream() - S.content = self.content - S.__Comment__ = "xobject postscript stream" - sdict = S.dictionary - sdict["Type"] = PDFName("XObject") - sdict["Subtype"] = PDFName("PS") - return S.format(document) - -_mode2CS={'RGB':'DeviceRGB', 'L':'DeviceGray', 'CMYK':'DeviceCMYK'} -class PDFImageXObject: - # first attempts at a hard-coded one - # in the file, Image XObjects are stream objects. We already - # have a PDFStream object with 3 attributes: dictionary, content - # and filters. So the job of this thing is to construct the - # right PDFStream instance and ask it to format itself. - def __init__(self, name, source=None, mask=None): - self.name = name - self.width = 24 - self.height = 23 - self.bitsPerComponent = 1 - self.colorSpace = 'DeviceGray' - self._filters = 'ASCII85Decode', - self.streamContent = """ - 003B00 002700 002480 0E4940 114920 14B220 3CB650 - 75FE88 17FF8C 175F14 1C07E2 3803C4 703182 F8EDFC - B2BBC2 BB6F84 31BFC2 18EA3C 0E3E00 07FC00 03F800 - 1E1800 1FF800> - """ - self.mask = mask - - if source is None: - pass # use the canned one. - elif type(source)==type(''): - # it is a filename - import os - ext = string.lower(os.path.splitext(source)[1]) - src = open_for_read(source) - if not(ext in ('.jpg', '.jpeg') and self.loadImageFromJPEG(src)): - self.loadImageFromA85(src) - else: # it is already a PIL Image - self.loadImageFromSRC(source) - - def loadImageFromA85(self,source): - IMG=[] - imagedata = map(string.strip,pdfutils.makeA85Image(source,IMG=IMG)) - words = string.split(imagedata[1]) - self.width, self.height = map(string.atoi,(words[1],words[3])) - self.colorSpace = {'/RGB':'DeviceRGB', '/G':'DeviceGray', '/CMYK':'DeviceCMYK'}[words[7]] - self.bitsPerComponent = 8 - self._filters = 'ASCII85Decode','FlateDecode' #'A85','Fl' - if IMG: self._checkTransparency(IMG[0]) - elif self.mask=='auto': self.mask = None - self.streamContent = string.join(imagedata[3:-1],'') - - def loadImageFromJPEG(self,imageFile): - try: - try: - info = pdfutils.readJPEGInfo(imageFile) - finally: - imageFile.seek(0) #reset file pointer - except: - return False - self.width, self.height = info[0], info[1] - self.bitsPerComponent = 8 - if info[2] == 1: - self.colorSpace = 'DeviceGray' - elif info[2] == 3: - self.colorSpace = 'DeviceRGB' - else: #maybe should generate an error, is this right for CMYK? - self.colorSpace = 'DeviceCMYK' - self._dotrans = 1 - self.streamContent = pdfutils._AsciiBase85Encode(imageFile.read()) - self._filters = 'ASCII85Decode','DCTDecode' #'A85','DCT' - self.mask = None - return True - - def _checkTransparency(self,im): - if self.mask=='auto': - tc = im.getTransparent() - if tc: - self.mask = (tc[0], tc[0], tc[1], tc[1], tc[2], tc[2]) - else: - self.mask = None - elif hasattr(self.mask,'rgb'): - _ = self.mask.rgb() - self.mask = _[0],_[0],_[1],_[1],_[2],_[2] - - def loadImageFromSRC(self, im): - "Extracts the stream, width and height" - fp = im.jpeg_fh() - if fp: - self.loadImageFromJPEG(fp) - else: - zlib = import_zlib() - if not zlib: return - self.width, self.height = im.getSize() - raw = im.getRGBData() - assert(len(raw) == self.width*self.height, "Wrong amount of data for image") - self.streamContent = pdfutils._AsciiBase85Encode(zlib.compress(raw)) - self.colorSpace= _mode2CS[im.mode] - self.bitsPerComponent = 8 - self._filters = 'ASCII85Decode','FlateDecode' #'A85','Fl' - self._checkTransparency(im) - - def format(self, document): - S = PDFStream() - S.content = self.streamContent - dict = S.dictionary - dict["Type"] = PDFName("XObject") - dict["Subtype"] = PDFName("Image") - dict["Width"] = self.width - dict["Height"] = self.height - dict["BitsPerComponent"] = self.bitsPerComponent - dict["ColorSpace"] = PDFName(self.colorSpace) - if self.colorSpace=='DeviceCMYK' and getattr(self,'_dotrans',0): - dict["Decode"] = PDFArray([1,0,1,0,1,0,1,0]) - dict["Filter"] = PDFArray(map(PDFName,self._filters)) - dict["Length"] = len(self.streamContent) - if self.mask: dict["Mask"] = PDFArray(self.mask) - return S.format(document) - -if __name__=="__main__": - print "There is no script interpretation for pdfdoc." diff --git a/bin/reportlab/pdfbase/pdfform.py b/bin/reportlab/pdfbase/pdfform.py deleted file mode 100644 index 9fca917021e..00000000000 --- a/bin/reportlab/pdfbase/pdfform.py +++ /dev/null @@ -1,632 +0,0 @@ - -"""Support for Acrobat Forms in ReportLab documents - -This module is somewhat experimental at this time. - -Includes basic support for - textfields, - select fields (drop down lists), and - check buttons. - -The public interface consists of functions at the moment. -At some later date these operations may be made into canvas -methods. (comments?) - -The ...Absolute(...) functions position the fields with respect -to the absolute canvas coordinate space -- that is, they do not -respect any coordinate transforms in effect for the canvas. - -The ...Relative(...) functions position the ONLY THE LOWER LEFT -CORNER of the field using the coordinate transform in effect for -the canvas. THIS WILL ONLY WORK CORRECTLY FOR TRANSLATED COORDINATES --- THE SHAPE, SIZE, FONTSIZE, AND ORIENTATION OF THE FIELD WILL NOT BE EFFECTED -BY SCALING, ROTATION, SKEWING OR OTHER NON-TRANSLATION COORDINATE -TRANSFORMS. - -Please note that all field names (titles) in a given document must be unique. -Textfields and select fields only support the "base 14" canvas fonts -at this time. - -See individual function docstrings below for more information. - -The function test1(...) generates a simple test file. - -THIS CONTRIBUTION WAS COMMISSIONED BY REPORTLAB USERS -WHO WISH TO REMAIN ANONYMOUS. -""" - -### NOTE: MAKE THE STRING FORMATS DYNAMIC IN PATTERNS TO SUPPORT ENCRYPTION XXXX - -import string -from reportlab.pdfbase.pdfdoc import LINEEND, PDFString, PDFStream, PDFDictionary, PDFName - -#==========================public interfaces - -def textFieldAbsolute(canvas, title, x, y, width, height, value="", maxlen=1000000, multiline=0): - """Place a text field on the current page - with name title at ABSOLUTE position (x,y) with - dimensions (width, height), using value as the default value and - maxlen as the maximum permissible length. If multiline is set make - it a multiline field. - """ - theform = getForm(canvas) - return theform.textField(canvas, title, x, y, x+width, y+height, value, maxlen, multiline) - -def textFieldRelative(canvas, title, xR, yR, width, height, value="", maxlen=1000000, multiline=0): - "same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform" - (xA, yA) = canvas.absolutePosition(xR,yR) - return textFieldAbsolute(canvas, title, xA, yA, width, height, value, maxlen, multiline) - -def buttonFieldAbsolute(canvas, title, value, x, y): - """Place a check button field on the current page - with name title and default value value (one of "Yes" or "Off") - at ABSOLUTE position (x,y). - """ - theform = getForm(canvas) - return theform.buttonField(canvas, title, value, x, y) - -def buttonFieldRelative(canvas, title, value, xR, yR): - "same as buttonFieldAbsolute except the x and y are relative to the canvas coordinate transform" - (xA, yA) = canvas.absolutePosition(xR,yR) - return buttonFieldAbsolute(canvas, title, value, xA, yA) - -def selectFieldAbsolute(canvas, title, value, options, x, y, width, height): - """Place a select field (drop down list) on the current page - with name title and - with options listed in the sequence options - default value value (must be one of options) - at ABSOLUTE position (x,y) with dimensions (width, height).""" - theform = getForm(canvas) - theform.selectField(canvas, title, value, options, x, y, x+width, y+height) - -def selectFieldRelative(canvas, title, value, options, xR, yR, width, height): - "same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform" - (xA, yA) = canvas.absolutePosition(xR,yR) - return selectFieldAbsolute(canvas, title, value, options, xA, yA, width, height) - -def test1(): - from reportlab.pdfgen import canvas - fn = "formtest1.pdf" - c = canvas.Canvas(fn) - # first page - c.setFont("Courier", 10) - c.drawString(100, 500, "hello world") - textFieldAbsolute(c, "fieldA", 100, 600, 100, 20, "default value") - textFieldAbsolute(c, "fieldB", 100, 300, 100, 50, "another default value", multiline=1) - selectFieldAbsolute(c, "fieldC", "France", ["Canada", "France", "China"], 100, 200, 100, 20) - c.rect(100, 600, 100, 20) - buttonFieldAbsolute(c, "field2", "Yes", 100, 700) - c.rect(100, 700, 20, 20) - buttonFieldAbsolute(c, "field3", "Off", 100, 800) - c.rect(100, 800, 20, 20) - # second page - c.showPage() - c.setFont("Helvetica", 7) - c.translate(50, 20) - c.drawString(100, 500, "hello world") - textFieldRelative(c, "fieldA_1", 100, 600, 100, 20, "default value 2") - c.setStrokeColorRGB(1,0,0) - c.setFillColorRGB(0,1,0.5) - textFieldRelative(c, "fieldB_1", 100, 300, 100, 50, "another default value 2", multiline=1) - selectFieldRelative(c, "fieldC_1", "France 1", ["Canada 0", "France 1", "China 2"], 100, 200, 100, 20) - c.rect(100, 600, 100, 20) - buttonFieldRelative(c, "field2_1", "Yes", 100, 700) - c.rect(100, 700, 20, 20) - buttonFieldRelative(c, "field3_1", "Off", 100, 800) - c.rect(100, 800, 20, 20) - c.save() - print "wrote", fn - -#==========================end of public interfaces - -from pdfpattern import PDFPattern - -def getForm(canvas): - "get form from canvas, create the form if needed" - try: - return canvas.AcroForm - except AttributeError: - theform = canvas.AcroForm = AcroForm() - # install the form in the document - d = canvas._doc - cat = d._catalog - cat.AcroForm = theform - return theform - -class AcroForm: - def __init__(self): - self.fields = [] - def textField(self, canvas, title, xmin, ymin, xmax, ymax, value="", maxlen=1000000, multiline=0): - # determine the page ref - doc = canvas._doc - page = doc.thisPageRef() - # determine text info - (R,G,B) = canvas._fillColorRGB - #print "rgb", (R,G,B) - font = canvas. _fontname - fontsize = canvas. _fontsize - field = TextField(title, value, xmin, ymin, xmax, ymax, page, maxlen, - font, fontsize, R, G, B, multiline) - self.fields.append(field) - canvas._addAnnotation(field) - def selectField(self, canvas, title, value, options, xmin, ymin, xmax, ymax): - # determine the page ref - doc = canvas._doc - page = doc.thisPageRef() - # determine text info - (R,G,B) = canvas._fillColorRGB - #print "rgb", (R,G,B) - font = canvas. _fontname - fontsize = canvas. _fontsize - field = SelectField(title, value, options, xmin, ymin, xmax, ymax, page, - font=font, fontsize=fontsize, R=R, G=G, B=B) - self.fields.append(field) - canvas._addAnnotation(field) - def buttonField(self, canvas, title, value, xmin, ymin): - # determine the page ref - doc = canvas._doc - page = doc.thisPageRef() - field = ButtonField(title, value, xmin, ymin, page) - self.fields.append(field) - canvas._addAnnotation(field) - def format(self, document): - from reportlab.pdfbase.pdfdoc import PDFArray - proxy = PDFPattern(FormPattern, Resources=GLOBALRESOURCES, fields=PDFArray(self.fields)) - return proxy.format(document) - -FormPattern = [ -'<<', LINEEND, -' /NeedAppearances true ', LINEEND, -' /DA ', PDFString('/Helv 0 Tf 0 g '), LINEEND, -' /DR ', LINEEND, -["Resources"], -' /Fields ', LINEEND, -["fields"], -'>>' -] - -def FormFontsDictionary(): - from reportlab.pdfbase.pdfdoc import PDFDictionary - fontsdictionary = PDFDictionary() - fontsdictionary.__RefOnly__ = 1 - for (fullname, shortname) in FORMFONTNAMES.items(): - fontsdictionary[shortname] = FormFont(fullname, shortname) - fontsdictionary["ZaDb"] = ZADB - return fontsdictionary - -def FormResources(): - return PDFPattern(FormResourcesDictionaryPattern, - Encoding=ENCODING, Font=GLOBALFONTSDICTIONARY) - -ZaDbPattern = [ -' <<' -' /BaseFont' -' /ZapfDingbats' -' /Name' -' /ZaDb' -' /Subtype' -' /Type1' -' /Type' -' /Font' -'>>'] - -ZADB = PDFPattern(ZaDbPattern) - -FormResourcesDictionaryPattern = [ -'<<', -' /Encoding ', -["Encoding"], LINEEND, -' /Font ', -["Font"], LINEEND, -'>>' -] - -FORMFONTNAMES = { - "Helvetica": "Helv", - "Helvetica-Bold": "HeBo", - 'Courier': "Cour", - 'Courier-Bold': "CoBo", - 'Courier-Oblique': "CoOb", - 'Courier-BoldOblique': "CoBO", - 'Helvetica-Oblique': "HeOb", - 'Helvetica-BoldOblique': "HeBO", - 'Times-Roman': "Time", - 'Times-Bold': "TiBo", - 'Times-Italic': "TiIt", - 'Times-BoldItalic': "TiBI", - } - -EncodingPattern = [ -'<<', -' /PDFDocEncoding ', -["PDFDocEncoding"], LINEEND, -'>>', -] - -PDFDocEncodingPattern = [ -'<<' -' /Differences' -' [' -' 24' -' /breve' -' /caron' -' /circumflex' -' /dotaccent' -' /hungarumlaut' -' /ogonek' -' /ring' -' /tilde' -' 39' -' /quotesingle' -' 96' -' /grave' -' 128' -' /bullet' -' /dagger' -' /daggerdbl' -' /ellipsis' -' /emdash' -' /endash' -' /florin' -' /fraction' -' /guilsinglleft' -' /guilsinglright' -' /minus' -' /perthousand' -' /quotedblbase' -' /quotedblleft' -' /quotedblright' -' /quoteleft' -' /quoteright' -' /quotesinglbase' -' /trademark' -' /fi' -' /fl' -' /Lslash' -' /OE' -' /Scaron' -' /Ydieresis' -' /Zcaron' -' /dotlessi' -' /lslash' -' /oe' -' /scaron' -' /zcaron' -' 160' -' /Euro' -' 164' -' /currency' -' 166' -' /brokenbar' -' 168' -' /dieresis' -' /copyright' -' /ordfeminine' -' 172' -' /logicalnot' -' /.notdef' -' /registered' -' /macron' -' /degree' -' /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' -' ]' -' /Type' -' /Encoding' -'>>'] - -# global constant -PDFDOCENC = PDFPattern(PDFDocEncodingPattern) -# global constant -ENCODING = PDFPattern(EncodingPattern, PDFDocEncoding=PDFDOCENC) - - -def FormFont(BaseFont, Name): - from reportlab.pdfbase.pdfdoc import PDFName - return PDFPattern(FormFontPattern, BaseFont=PDFName(BaseFont), Name=PDFName(Name), Encoding=PDFDOCENC) - -FormFontPattern = [ -'<<', -' /BaseFont ', -["BaseFont"], LINEEND, -' /Encoding ', -["Encoding"], LINEEND, -' /Name ', -["Name"], LINEEND, -' /Subtype ' -' /Type1 ' -' /Type ' -' /Font ' -'>>' ] - -# global constants -GLOBALFONTSDICTIONARY = FormFontsDictionary() -GLOBALRESOURCES = FormResources() - - -def TextField(title, value, xmin, ymin, xmax, ymax, page, - maxlen=1000000, font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627, multiline=0): - from reportlab.pdfbase.pdfdoc import PDFString, PDFName - Flags = 0 - if multiline: - Flags = Flags | (1<<12) # bit 13 is at position 12 :) - fontname = FORMFONTNAMES[font] - return PDFPattern(TextFieldPattern, - value=PDFString(value), maxlen=maxlen, page=page, - title=PDFString(title), - xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax, - fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B, Flags=Flags) - - -TextFieldPattern = [ -'<<' -' /DA' -' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)' -' /DV ', -["value"], LINEEND, -' /F 4 /FT /Tx' -'/MK << /BC [ 0 0 0 ] >>' -' /MaxLen ', -["maxlen"], LINEEND, -' /P ', -["page"], LINEEND, -' /Rect ' -' [', ["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"], ' ]' -'/Subtype /Widget' -' /T ', -["title"], LINEEND, -' /Type' -' /Annot' -' /V ', -["value"], LINEEND, -' /Ff ', -["Flags"],LINEEND, -'>>'] - -def SelectField(title, value, options, xmin, ymin, xmax, ymax, page, - font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627): - #print "ARGS", (title, value, options, xmin, ymin, xmax, ymax, page, font, fontsize, R, G, B) - from reportlab.pdfbase.pdfdoc import PDFString, PDFName, PDFArray - if value not in options: - raise ValueError, "value %s must be one of options %s" % (repr(value), repr(options)) - fontname = FORMFONTNAMES[font] - optionstrings = map(PDFString, options) - optionarray = PDFArray(optionstrings) - return PDFPattern(SelectFieldPattern, - Options=optionarray, - Selected=PDFString(value), Page=page, - Name=PDFString(title), - xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax, - fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B) - -SelectFieldPattern = [ -'<< % a select list',LINEEND, -' /DA ', -' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)',LINEEND, -#' (/Helv 12 Tf 0 g)',LINEEND, -' /DV ', -["Selected"],LINEEND, -' /F ', -' 4',LINEEND, -' /FT ', -' /Ch',LINEEND, -' /MK ', -' <<', -' /BC', -' [', -' 0', -' 0', -' 0', -' ]', -' /BG', -' [', -' 1', -' 1', -' 1', -' ]', -' >>',LINEEND, -' /Opt ', -["Options"],LINEEND, -' /P ', -["Page"],LINEEND, -'/Rect', -' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"], -' ] ',LINEEND, -'/Subtype', -' /Widget',LINEEND, -' /T ', -["Name"],LINEEND, -' /Type ', -' /Annot', -' /V ', -["Selected"],LINEEND, -'>>'] - -def ButtonField(title, value, xmin, ymin, page): - if value not in ("Yes", "Off"): - raise ValueError, "button value must be 'Yes' or 'Off': "+repr(value) - (dx, dy) = (16.77036, 14.90698) - return PDFPattern(ButtonFieldPattern, - Name=PDFString(title), - xmin=xmin, ymin=ymin, xmax=xmin+dx, ymax=ymin+dy, - Hide=HIDE, - APDOff=APDOFF, - APDYes=APDYES, - APNYes=APNYES, - Value=PDFName(value), - Page=page) - -ButtonFieldPattern = ['<< ', -'/AA', -' <<', -' /D ', -["Hide"], LINEEND, -#' %(imported.18.0)s', -' >> ', -'/AP ', -' <<', -' /D', -' <<', -' /Off ', -#' %(imported.40.0)s', -["APDOff"], LINEEND, -' /Yes ', -#' %(imported.39.0)s', -["APDYes"], LINEEND, -' >>', LINEEND, -' /N', -' << ', -' /Yes ', -#' %(imported.38.0)s', -["APNYes"], LINEEND, -' >>', -' >>', LINEEND, -' /AS ', -["Value"], LINEEND, -' /DA ', -PDFString('/ZaDb 0 Tf 0 g'), LINEEND, -'/DV ', -["Value"], LINEEND, -'/F ', -' 4 ', -'/FT ', -' /Btn ', -'/H ', -' /T ', -'/MK ', -' <<', -' /AC (\\376\\377)', -#PDFString('\376\377'), -' /CA ', -PDFString('4'), -' /RC ', -PDFString('\376\377'), -' >> ',LINEEND, -'/P ', -["Page"], LINEEND, -'/Rect', -' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"], -' ] ',LINEEND, -'/Subtype', -' /Widget ', -'/T ', -["Name"], LINEEND, -'/Type', -' /Annot ', -'/V ', -["Value"], LINEEND, -' >>'] - -HIDE = PDFPattern([ -'<< ' -'/S ' -' /Hide ' -'>>']) - -def buttonStreamDictionary(): - "everything except the length for the button appearance streams" - result = PDFDictionary() - result["SubType"] = "/Form" - result["BBox"] = "[0 0 16.77036 14.90698]" - font = PDFDictionary() - font["ZaDb"] = ZADB - resources = PDFDictionary() - resources["ProcSet"] = "[ /PDF /Text ]" - resources["Font"] = font - result["Resources"] = resources - return result - -def ButtonStream(content): - dict = buttonStreamDictionary() - result = PDFStream(dict, content) - result.filters = [] - return result - -APDOFF = ButtonStream('0.749 g 0 0 16.7704 14.907 re f'+LINEEND) -APDYES = ButtonStream( -'0.749 g 0 0 16.7704 14.907 re f q 1 1 14.7704 12.907 re W '+ -'n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET'+LINEEND) -APNYES = ButtonStream( -'q 1 1 14.7704 12.907 re W n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET Q'+LINEEND) - -#==== script interpretation - -if __name__=="__main__": - test1() \ No newline at end of file diff --git a/bin/reportlab/pdfbase/pdfmetrics.py b/bin/reportlab/pdfbase/pdfmetrics.py deleted file mode 100755 index 58cf3f051b6..00000000000 --- a/bin/reportlab/pdfbase/pdfmetrics.py +++ /dev/null @@ -1,796 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfbase/pdfmetrics.py -#$Header $ -__version__=''' $Id: pdfmetrics.py 2873 2006-05-17 10:59:59Z rgbecker $ ''' -__doc__=""" -This provides a database of font metric information and -efines Font, Encoding and TypeFace classes aimed at end users. - -There are counterparts to some of these in pdfbase/pdfdoc.py, but -the latter focus on constructing the right PDF objects. These -classes are declarative and focus on letting the user construct -and query font objects. - -The module maintains a registry of font objects at run time. - -It is independent of the canvas or any particular context. It keeps -a registry of Font, TypeFace and Encoding objects. Ideally these -would be pre-loaded, but due to a nasty circularity problem we -trap attempts to access them and do it on first access. -""" -import string, os -from types import StringType, ListType, TupleType -from reportlab.pdfbase import _fontdata -from reportlab.lib.logger import warnOnce -from reportlab.lib.utils import rl_isfile, rl_glob, rl_isdir, open_and_read, open_and_readlines -from reportlab.rl_config import defaultEncoding -import rl_codecs - -rl_codecs.RL_Codecs.register() -standardFonts = _fontdata.standardFonts -standardEncodings = _fontdata.standardEncodings - -_typefaces = {} -_encodings = {} -_fonts = {} - -def _py_unicode2T1(utext,fonts): - '''return a list of (font,string) pairs representing the unicode text''' - #print 'unicode2t1(%s, %s): %s' % (utext, fonts, type(utext)) - #if type(utext) - R = [] - font, fonts = fonts[0], fonts[1:] - enc = font.encName - if 'UCS-2' in enc: - enc = 'UTF16' - while utext: - try: - R.append((font,utext.encode(enc))) - break - except UnicodeEncodeError, e: - i0, il = e.args[2:4] - if i0: - R.append((font,utext[:i0].encode(enc))) - if fonts: - R.extend(_py_unicode2T1(utext[i0:il],fonts)) - else: - R.append((_notdefFont,_notdefChar*(il-i0))) - utext = utext[il:] - return R - -try: - from _rl_accel import unicode2T1 -except ImportError: - unicode2T1 = _py_unicode2T1 - -class FontError(Exception): - pass -class FontNotFoundError(Exception): - pass - -def parseAFMFile(afmFileName): - """Quick and dirty - gives back a top-level dictionary - with top-level items, and a 'widths' key containing - a dictionary of glyph names and widths. Just enough - needed for embedding. A better parser would accept - options for what data you wwanted, and preserve the - order.""" - - lines = open_and_readlines(afmFileName, 'r') - if len(lines)<=1: - #likely to be a MAC file - if lines: lines = string.split(lines[0],'\r') - if len(lines)<=1: - raise ValueError, 'AFM file %s hasn\'t enough data' % afmFileName - topLevel = {} - glyphLevel = [] - - lines = map(string.strip, lines) - #pass 1 - get the widths - inMetrics = 0 # os 'TOP', or 'CHARMETRICS' - for line in lines: - if line[0:16] == 'StartCharMetrics': - inMetrics = 1 - elif line[0:14] == 'EndCharMetrics': - inMetrics = 0 - elif inMetrics: - chunks = string.split(line, ';') - chunks = map(string.strip, chunks) - cidChunk, widthChunk, nameChunk = chunks[0:3] - - # character ID - l, r = string.split(cidChunk) - assert l == 'C', 'bad line in font file %s' % line - cid = string.atoi(r) - - # width - l, r = string.split(widthChunk) - assert l == 'WX', 'bad line in font file %s' % line - width = string.atoi(r) - - # name - l, r = string.split(nameChunk) - assert l == 'N', 'bad line in font file %s' % line - name = r - - glyphLevel.append((cid, width, name)) - - # pass 2 font info - inHeader = 0 - for line in lines: - if line[0:16] == 'StartFontMetrics': - inHeader = 1 - if line[0:16] == 'StartCharMetrics': - inHeader = 0 - elif inHeader: - if line[0:7] == 'Comment': pass - try: - left, right = string.split(line,' ',1) - except: - raise ValueError, "Header information error in afm %s: line='%s'" % (afmFileName, line) - try: - right = string.atoi(right) - except: - pass - topLevel[left] = right - - - return (topLevel, glyphLevel) - -class TypeFace: - def __init__(self, name): - self.name = name - self.glyphNames = [] - self.glyphWidths = {} - self.ascent = 0 - self.descent = 0 - - - # all typefaces of whatever class should have these 3 attributes. - # these are the basis for family detection. - self.familyName = None # should set on load/construction if possible - self.bold = 0 # bold faces should set this - self.italic = 0 #italic faces should set this - - - if name == 'ZapfDingbats': - self.requiredEncoding = 'ZapfDingbatsEncoding' - elif name == 'Symbol': - self.requiredEncoding = 'SymbolEncoding' - else: - self.requiredEncoding = None - if name in standardFonts: - self.builtIn = 1 - self._loadBuiltInData(name) - else: - self.builtIn = 0 - - def _loadBuiltInData(self, name): - """Called for the built in 14 fonts. Gets their glyph data. - We presume they never change so this can be a shared reference.""" - name = str(name) #needed for pycanvas&jython/2.1 compatibility - self.glyphWidths = _fontdata.widthsByFontGlyph[name] - self.glyphNames = self.glyphWidths.keys() - self.ascent,self.descent = _fontdata.ascent_descent[name] - - def getFontFiles(self): - "Info function, return list of the font files this depends on." - return [] - - def findT1File(self, ext='.pfb'): - possible_exts = (string.lower(ext), string.upper(ext)) - if hasattr(self,'pfbFileName'): - r_basename = os.path.splitext(self.pfbFileName)[0] - for e in possible_exts: - if rl_isfile(r_basename + e): - return r_basename + e - try: - r = _fontdata.findT1File(self.name) - except: - afm = bruteForceSearchForAFM(self.name) - if afm: - if string.lower(ext) == '.pfb': - for e in possible_exts: - pfb = os.path.splitext(afm)[0] + e - if rl_isfile(pfb): - r = pfb - else: - r = None - elif string.lower(ext) == '.afm': - r = afm - else: - r = None - if r is None: - warnOnce("Can't find %s for face '%s'" % (ext, self.name)) - return r - -def bruteForceSearchForFile(fn,searchPath=None): - if searchPath is None: from reportlab.rl_config import T1SearchPath as searchPath - if rl_isfile(fn): return fn - bfn = os.path.basename(fn) - for dirname in searchPath: - if not rl_isdir(dirname): continue - tfn = os.path.join(dirname,bfn) - if rl_isfile(tfn): return tfn - return fn - -def bruteForceSearchForAFM(faceName): - """Looks in all AFM files on path for face with given name. - - Returns AFM file name or None. Ouch!""" - from reportlab.rl_config import T1SearchPath - - for dirname in T1SearchPath: - if not rl_isdir(dirname): continue - possibles = rl_glob(dirname + os.sep + '*.[aA][fF][mM]') - for possible in possibles: - (topDict, glyphDict) = parseAFMFile(possible) - if topDict['FontName'] == faceName: - return possible - return None - -#for faceName in standardFonts: -# registerTypeFace(TypeFace(faceName)) - - -class Encoding: - """Object to help you create and refer to encodings.""" - def __init__(self, name, base=None): - self.name = name - self.frozen = 0 - if name in standardEncodings: - assert base is None, "Can't have a base encoding for a standard encoding" - self.baseEncodingName = name - self.vector = _fontdata.encodings[name] - elif base == None: - # assume based on the usual one - self.baseEncodingName = defaultEncoding - self.vector = _fontdata.encodings[defaultEncoding] - elif type(base) is StringType: - baseEnc = getEncoding(base) - self.baseEncodingName = baseEnc.name - self.vector = baseEnc.vector[:] - elif type(base) in (ListType, TupleType): - self.baseEncodingName = defaultEncoding - self.vector = base[:] - elif isinstance(base, Encoding): - # accept a vector - self.baseEncodingName = base.name - self.vector = base.vector[:] - - def __getitem__(self, index): - "Return glyph name for that code point, or None" - # THIS SHOULD BE INLINED FOR SPEED - return self.vector[index] - - def __setitem__(self, index, value): - # should fail if they are frozen - assert self.frozen == 0, 'Cannot modify a frozen encoding' - if self.vector[index]!=value: - L = list(self.vector) - L[index] = value - self.vector = tuple(L) - - def freeze(self): - self.vector = tuple(self.vector) - self.frozen = 1 - - def isEqual(self, other): - return ((self.name == other.name) and (self.vector == other.vector)) - - def modifyRange(self, base, newNames): - """Set a group of character names starting at the code point 'base'.""" - assert self.frozen == 0, 'Cannot modify a frozen encoding' - idx = base - for name in newNames: - self.vector[idx] = name - idx = idx + 1 - - def getDifferences(self, otherEnc): - """Return a compact list of the code points differing between two encodings - - This is in the Adobe format: list of - [[b1, name1, name2, name3], - [b2, name4]] - where b1...bn is the starting code point, and the glyph names following - are assigned consecutive code points.""" - - ranges = [] - curRange = None - for i in xrange(len(self.vector)): - glyph = self.vector[i] - if glyph==otherEnc.vector[i]: - if curRange: - ranges.append(curRange) - curRange = [] - else: - if curRange: - curRange.append(glyph) - elif glyph: - curRange = [i, glyph] - if curRange: - ranges.append(curRange) - return ranges - - def makePDFObject(self): - "Returns a PDF Object representing self" - # avoid circular imports - this cannot go at module level - from reportlab.pdfbase import pdfdoc - - D = {} - baseEnc = getEncoding(self.baseEncodingName) - differences = self.getDifferences(baseEnc) #[None] * 256) - - # if no differences, we just need the base name - if differences == []: - return pdfdoc.PDFName(self.baseEncodingName) - else: - #make up a dictionary describing the new encoding - diffArray = [] - for range in differences: - diffArray.append(range[0]) # numbers go 'as is' - for glyphName in range[1:]: - if glyphName is not None: - # there is no way to 'unset' a character in the base font. - diffArray.append('/' + glyphName) - - #print 'diffArray = %s' % diffArray - D["Differences"] = pdfdoc.PDFArray(diffArray) - D["BaseEncoding"] = pdfdoc.PDFName(self.baseEncodingName) - D["Type"] = pdfdoc.PDFName("Encoding") - PD = pdfdoc.PDFDictionary(D) - return PD - -#for encName in standardEncodings: -# registerEncoding(Encoding(encName)) - -standardT1SubstitutionFonts = [] -class Font: - """Represents a font (i.e combination of face and encoding). - - Defines suitable machinery for single byte fonts. This is - a concrete class which can handle the basic built-in fonts; - not clear yet if embedded ones need a new font class or - just a new typeface class (which would do the job through - composition)""" - def __init__(self, name, faceName, encName): - self.fontName = name - face = self.face = getTypeFace(faceName) - self.encoding= getEncoding(encName) - self.encName = encName - if face.builtIn and face.requiredEncoding is None: - _ = standardT1SubstitutionFonts - else: - _ = [] - self.substitutionFonts = _ - self._calcWidths() - - # multi byte fonts do their own stringwidth calculations. - # signal this here. - self._multiByte = 0 - - - def __repr__(self): - return "<%s %s>" % (self.__class__.__name__, self.face.name) - - def _calcWidths(self): - """Vector of widths for stringWidth function""" - #synthesize on first request - w = [0] * 256 - gw = self.face.glyphWidths - vec = self.encoding.vector - for i in range(256): - glyphName = vec[i] - if glyphName is not None: - try: - width = gw[glyphName] - w[i] = width - except KeyError: - import reportlab.rl_config - if reportlab.rl_config.warnOnMissingFontGlyphs: - print 'typeface "%s" does not have a glyph "%s", bad font!' % (self.face.name, glyphName) - else: - pass - self.widths = w - - def _py_stringWidth(self, text, size, encoding='utf8'): - """This is the "purist" approach to width. The practical approach - is to use the stringWidth function, which may be swapped in for one - written in C.""" - if not isinstance(text,unicode): text = text.decode(encoding) - return sum([sum(map(f.widths.__getitem__,map(ord,t))) for f, t in unicode2T1(text,[self]+self.substitutionFonts)])*0.001*size - stringWidth = _py_stringWidth - - def _formatWidths(self): - "returns a pretty block in PDF Array format to aid inspection" - text = '[' - for i in range(256): - text = text + ' ' + str(self.widths[i]) - if i == 255: - text = text + ' ]' - if i % 16 == 15: - text = text + '\n' - return text - - def addObjects(self, doc): - """Makes and returns one or more PDF objects to be added - to the document. The caller supplies the internal name - to be used (typically F1, F2... in sequence) """ - # avoid circular imports - this cannot go at module level - from reportlab.pdfbase import pdfdoc - - # construct a Type 1 Font internal object - internalName = 'F' + repr(len(doc.fontMapping)+1) - pdfFont = pdfdoc.PDFType1Font() - pdfFont.Name = internalName - pdfFont.BaseFont = self.face.name - pdfFont.__Comment__ = 'Font %s' % self.fontName - pdfFont.Encoding = self.encoding.makePDFObject() - - # is it a built-in one? if not, need more stuff. - if not self.face.name in standardFonts: - pdfFont.FirstChar = 0 - pdfFont.LastChar = 255 - pdfFont.Widths = pdfdoc.PDFArray(self.widths) - pdfFont.FontDescriptor = self.face.addObjects(doc) - # now link it in - ref = doc.Reference(pdfFont, internalName) - - # also refer to it in the BasicFonts dictionary - fontDict = doc.idToObject['BasicFonts'].dict - fontDict[internalName] = pdfFont - - # and in the font mappings - doc.fontMapping[self.fontName] = '/' + internalName - -PFB_MARKER=chr(0x80) -PFB_ASCII=chr(1) -PFB_BINARY=chr(2) -PFB_EOF=chr(3) -def _pfbSegLen(p,d): - '''compute a pfb style length from the first 4 bytes of string d''' - return ((((ord(d[p+3])<<8)|ord(d[p+2])<<8)|ord(d[p+1]))<<8)|ord(d[p]) - -def _pfbCheck(p,d,m,fn): - if d[p]!=PFB_MARKER or d[p+1]!=m: - raise ValueError, 'Bad pfb file\'%s\' expected chr(%d)chr(%d) at char %d, got chr(%d)chr(%d)' % (fn,ord(PFB_MARKER),ord(m),p,ord(d[p]),ord(d[p+1])) - if m==PFB_EOF: return - p = p + 2 - l = _pfbSegLen(p,d) - p = p + 4 - if p+l>len(d): - raise ValueError, 'Bad pfb file\'%s\' needed %d+%d bytes have only %d!' % (fn,p,l,len(d)) - return p, p+l - - -class EmbeddedType1Face(TypeFace): - """A Type 1 font other than one of the basic 14. - - Its glyph data will be embedded in the PDF file.""" - def __init__(self, afmFileName, pfbFileName): - # ignore afm file for now - TypeFace.__init__(self, None) - #None is a hack, name will be supplied by AFM parse lower done - #in this __init__ method. - self.afmFileName = os.path.abspath(afmFileName) - self.pfbFileName = os.path.abspath(pfbFileName) - self.requiredEncoding = None - self._loadGlyphs(pfbFileName) - self._loadMetrics(afmFileName) - - def getFontFiles(self): - return [self.afmFileName, self.pfbFileName] - - def _loadGlyphs(self, pfbFileName): - """Loads in binary glyph data, and finds the four length - measurements needed for the font descriptor""" - pfbFileName = bruteForceSearchForFile(pfbFileName) - assert rl_isfile(pfbFileName), 'file %s not found' % pfbFileName - d = open_and_read(pfbFileName, 'b') - s1, l1 = _pfbCheck(0,d,PFB_ASCII,pfbFileName) - s2, l2 = _pfbCheck(l1,d,PFB_BINARY,pfbFileName) - s3, l3 = _pfbCheck(l2,d,PFB_ASCII,pfbFileName) - _pfbCheck(l3,d,PFB_EOF,pfbFileName) - self._binaryData = d[s1:l1]+d[s2:l2]+d[s3:l3] - - self._length = len(self._binaryData) - self._length1 = l1-s1 - self._length2 = l2-s2 - self._length3 = l3-s3 - - - def _loadMetrics(self, afmFileName): - """Loads in and parses font metrics""" - #assert os.path.isfile(afmFileName), "AFM file %s not found" % afmFileName - afmFileName = bruteForceSearchForFile(afmFileName) - (topLevel, glyphData) = parseAFMFile(afmFileName) - - self.name = topLevel['FontName'] - self.familyName = topLevel['FamilyName'] - self.ascent = topLevel.get('Ascender', 1000) - self.descent = topLevel.get('Descender', 0) - self.capHeight = topLevel.get('CapHeight', 1000) - self.italicAngle = topLevel.get('ItalicAngle', 0) - self.stemV = topLevel.get('stemV', 0) - self.xHeight = topLevel.get('XHeight', 1000) - - strBbox = topLevel.get('FontBBox', [0,0,1000,1000]) - tokens = string.split(strBbox) - self.bbox = [] - for tok in tokens: - self.bbox.append(string.atoi(tok)) - - glyphWidths = {} - for (cid, width, name) in glyphData: - glyphWidths[name] = width - self.glyphWidths = glyphWidths - self.glyphNames = glyphWidths.keys() - self.glyphNames.sort() - - # for font-specific encodings like Symbol, Dingbats, Carta we - # need to make a new encoding as well.... - if topLevel.get('EncodingScheme', None) == 'FontSpecific': - names = [None] * 256 - for (code, width, name) in glyphData: - if code >=0 and code <=255: - names[code] = name - encName = self.name + 'Encoding' - self.requiredEncoding = encName - enc = Encoding(encName, names) - registerEncoding(enc) - - def addObjects(self, doc): - """Add whatever needed to PDF file, and return a FontDescriptor reference""" - from reportlab.pdfbase import pdfdoc - - fontFile = pdfdoc.PDFStream() - fontFile.content = self._binaryData - #fontFile.dictionary['Length'] = self._length - fontFile.dictionary['Length1'] = self._length1 - fontFile.dictionary['Length2'] = self._length2 - fontFile.dictionary['Length3'] = self._length3 - #fontFile.filters = [pdfdoc.PDFZCompress] - - fontFileRef = doc.Reference(fontFile, 'fontFile:' + self.pfbFileName) - - fontDescriptor = pdfdoc.PDFDictionary({ - 'Type': '/FontDescriptor', - 'Ascent':self.ascent, - 'CapHeight':self.capHeight, - 'Descent':self.descent, - 'Flags': 34, - 'FontBBox':pdfdoc.PDFArray(self.bbox), - 'FontName':pdfdoc.PDFName(self.name), - 'ItalicAngle':self.italicAngle, - 'StemV':self.stemV, - 'XHeight':self.xHeight, - 'FontFile': fontFileRef, - }) - fontDescriptorRef = doc.Reference(fontDescriptor, 'fontDescriptor:' + self.name) - return fontDescriptorRef - -def registerTypeFace(face): - assert isinstance(face, TypeFace), 'Not a TypeFace: %s' % face - _typefaces[face.name] = face - # HACK - bold/italic do not apply for type 1, so egister - # all combinations of mappings. - from reportlab.lib import fonts - ttname = string.lower(face.name) - if not face.name in standardFonts: - fonts.addMapping(ttname, 0, 0, face.name) - fonts.addMapping(ttname, 1, 0, face.name) - fonts.addMapping(ttname, 0, 1, face.name) - fonts.addMapping(ttname, 1, 1, face.name) - -def registerEncoding(enc): - assert isinstance(enc, Encoding), 'Not an Encoding: %s' % enc - if _encodings.has_key(enc.name): - # already got one, complain if they are not the same - if enc.isEqual(_encodings[enc.name]): - enc.freeze() - else: - raise FontError('Encoding "%s" already registered with a different name vector!' % enc.Name) - else: - _encodings[enc.name] = enc - enc.freeze() - # have not yet dealt with immutability! - -def registerFont(font): - "Registers a font, including setting up info for accelerated stringWidth" - #assert isinstance(font, Font), 'Not a Font: %s' % font - fontName = font.fontName - _fonts[fontName] = font - if font._multiByte: - # CID fonts don't need to have typeface registered. - #need to set mappings so it can go in a paragraph even if within - # bold tags - from reportlab.lib import fonts - ttname = string.lower(font.fontName) - fonts.addMapping(ttname, 0, 0, font.fontName) - fonts.addMapping(ttname, 1, 0, font.fontName) - fonts.addMapping(ttname, 0, 1, font.fontName) - fonts.addMapping(ttname, 1, 1, font.fontName) - - -def getTypeFace(faceName): - """Lazily construct known typefaces if not found""" - try: - return _typefaces[faceName] - except KeyError: - # not found, construct it if known - if faceName in standardFonts: - face = TypeFace(faceName) - (face.familyName, face.bold, face.italic) = _fontdata.standardFontAttributes[faceName] - registerTypeFace(face) -## print 'auto-constructing type face %s with family=%s, bold=%d, italic=%d' % ( -## face.name, face.familyName, face.bold, face.italic) - return face - else: - #try a brute force search - afm = bruteForceSearchForAFM(faceName) - if afm: - for e in ('.pfb', '.PFB'): - pfb = os.path.splitext(afm)[0] + e - if rl_isfile(pfb): break - assert rl_isfile(pfb), 'file %s not found!' % pfb - face = EmbeddedType1Face(afm, pfb) - registerTypeFace(face) - return face - else: - raise - -def getEncoding(encName): - """Lazily construct known encodings if not found""" - try: - return _encodings[encName] - except KeyError: - if encName in standardEncodings: - enc = Encoding(encName) - registerEncoding(enc) - #print 'auto-constructing encoding %s' % encName - return enc - else: - raise - -def findFontAndRegister(fontName): - '''search for and register a font given it's name''' - #it might have a font-specific encoding e.g. Symbol - # or Dingbats. If not, take the default. - face = getTypeFace(fontName) - if face.requiredEncoding: - font = Font(fontName, fontName, face.requiredEncoding) - else: - font = Font(fontName, fontName, defaultEncoding) - registerFont(font) - return font - -def _py_getFont(fontName): - """Lazily constructs known fonts if not found. - - Names of form 'face-encoding' will be built if - face and encoding are known. Also if the name is - just one of the standard 14, it will make up a font - in the default encoding.""" - try: - return _fonts[fontName] - except KeyError: - return findFontAndRegister(fontName) - -try: - from _rl_accel import getFontU as getFont -except ImportError: - getFont = _py_getFont - -_notdefFont,_notdefChar = getFont('ZapfDingbats'),chr(110) -standardT1SubstitutionFonts.extend([getFont('Symbol'),getFont('ZapfDingbats')]) - -def getAscentDescent(fontName): - font = getFont(fontName) - try: - return font.ascent,font.descent - except: - return font.face.ascent,font.face.descent - -def getAscent(fontName): - return getAscentDescent(fontName)[0] - -def getDescent(fontName): - return getAscentDescent(fontName)[1] - -def getRegisteredFontNames(): - "Returns what's in there" - reg = _fonts.keys() - reg.sort() - return reg - -def _py_stringWidth(text, fontName, fontSize, encoding='utf8'): - """Define this anyway so it can be tested, but whether it is used or not depends on _rl_accel""" - return getFont(fontName).stringWidth(text, fontSize, encoding=encoding) - -try: - from _rl_accel import stringWidthU as stringWidth -except ImportError: - stringWidth = _py_stringWidth - -try: - from _rl_accel import _instanceStringWidthU - import new - Font.stringWidth = new.instancemethod(_instanceStringWidthU,None,Font) -except ImportError: - pass - -def dumpFontData(): - print 'Registered Encodings:' - keys = _encodings.keys() - keys.sort() - for encName in keys: - print ' ',encName - - print - print 'Registered Typefaces:' - faces = _typefaces.keys() - faces.sort() - for faceName in faces: - print ' ',faceName - - - print - print 'Registered Fonts:' - k = _fonts.keys() - k.sort() - for key in k: - font = _fonts[key] - print ' %s (%s/%s)' % (font.fontName, font.face.name, font.encoding.name) - -def test3widths(texts): - # checks all 3 algorithms give same answer, note speed - import time - for fontName in standardFonts[0:1]: -## t0 = time.time() -## for text in texts: -## l1 = stringWidth(text, fontName, 10) -## t1 = time.time() -## print 'fast stringWidth took %0.4f' % (t1 - t0) - - t0 = time.time() - w = getFont(fontName).widths - for text in texts: - l2 = 0 - for ch in text: - l2 = l2 + w[ord(ch)] - t1 = time.time() - print 'slow stringWidth took %0.4f' % (t1 - t0) - - t0 = time.time() - for text in texts: - l3 = getFont(fontName).stringWidth(text, 10) - t1 = time.time() - print 'class lookup and stringWidth took %0.4f' % (t1 - t0) - print - -def testStringWidthAlgorithms(): - rawdata = open('../../rlextra/rml2pdf/doc/rml_user_guide.prep').read() - print 'rawdata length %d' % len(rawdata) - print 'test one huge string...' - test3widths([rawdata]) - print - words = string.split(rawdata) - print 'test %d shorter strings (average length %0.2f chars)...' % (len(words), 1.0*len(rawdata)/len(words)) - test3widths(words) - - -def test(): - helv = TypeFace('Helvetica') - registerTypeFace(helv) - print helv.glyphNames[0:30] - - wombat = TypeFace('Wombat') - print wombat.glyphNames - registerTypeFace(wombat) - - dumpFontData() - -if __name__=='__main__': - test() - testStringWidthAlgorithms() diff --git a/bin/reportlab/pdfbase/pdfpattern.py b/bin/reportlab/pdfbase/pdfpattern.py deleted file mode 100644 index 0ba62c9bed6..00000000000 --- a/bin/reportlab/pdfbase/pdfpattern.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -helper for importing pdf structures into a ReportLab generated document -""" -from reportlab.pdfbase.pdfdoc import format - -import string - -class PDFPattern: - __RefOnly__ = 1 - def __init__(self, pattern_sequence, **keywordargs): - """ - Description of a kind of PDF object using a pattern. - - Pattern sequence should contain strings or singletons of form [string]. - Strings are literal strings to be used in the object. - Singletons are names of keyword arguments to include. - Keyword arguments can be non-instances which are substituted directly in string conversion, - or they can be object instances in which case they should be pdfdoc.* style - objects with a x.format(doc) method. - Keyword arguments may be set on initialization or subsequently using __setitem__, before format. - "constant object" instances can also be inserted in the patterns. - """ - self.pattern = pattern_sequence - self.arguments = keywordargs - from types import StringType, InstanceType - toptypes = (StringType, InstanceType) - for x in pattern_sequence: - if type(x) not in toptypes: - if len(x)!=1: - raise ValueError, "sequence elts must be strings or singletons containing strings: "+repr(x) - if type(x[0]) is not StringType: - raise ValueError, "Singletons must contain strings or instances only: "+repr(x[0]) - def __setitem__(self, item, value): - self.arguments[item] = value - def __getitem__(self, item): - return self.arguments[item] - def format(self, document): - L = [] - arguments = self.arguments - from types import StringType, InstanceType - for x in self.pattern: - tx = type(x) - if tx is StringType: - L.append(x) - elif tx is InstanceType: - L.append( x.format(document) ) - else: - name = x[0] - value = arguments.get(name, None) - if value is None: - raise ValueError, "%s value not defined" % repr(name) - if type(value) is InstanceType: - #L.append( value.format(document) ) - L.append(format(value, document)) - else: - L.append( str(value) ) - return string.join(L, "") - - diff --git a/bin/reportlab/pdfbase/pdfutils.py b/bin/reportlab/pdfbase/pdfutils.py deleted file mode 100755 index 8a13c894d76..00000000000 --- a/bin/reportlab/pdfbase/pdfutils.py +++ /dev/null @@ -1,460 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfbase/pdfutils.py -__version__=''' $Id: pdfutils.py 2765 2006-02-02 18:48:12Z rgbecker $ ''' -__doc__='' -# pdfutils.py - everything to do with images, streams, -# compression, and some constants - -import os -from reportlab import rl_config -from string import join, replace, strip, split -from reportlab.lib.utils import getStringIO, ImageReader - -LINEEND = '\015\012' - -def _chunker(src,dst=[],chunkSize=60): - for i in xrange(0,len(src),chunkSize): - dst.append(src[i:i+chunkSize]) - return dst - -########################################################## -# -# Image compression helpers. Preprocessing a directory -# of images will offer a vast speedup. -# -########################################################## - -_mode2cs = {'RGB':'RGB', 'CMYK': 'CMYK', 'L': 'G'} - -def makeA85Image(filename,IMG=None): - import zlib - img = ImageReader(filename) - if IMG is not None: IMG.append(img) - - imgwidth, imgheight = img.getSize() - raw = img.getRGBData() - - code = [] - append = code.append - # this describes what is in the image itself - append('BI') - append('/W %s /H %s /BPC 8 /CS /%s /F [/A85 /Fl]' % (imgwidth, imgheight,_mode2cs[img.mode])) - append('ID') - #use a flate filter and Ascii Base 85 - assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image") - compressed = zlib.compress(raw) #this bit is very fast... - encoded = _AsciiBase85Encode(compressed) #...sadly this may not be - - #append in blocks of 60 characters - _chunker(encoded,code) - - append('EI') - return code - -def cacheImageFile(filename, returnInMemory=0, IMG=None): - "Processes image as if for encoding, saves to a file with .a85 extension." - - cachedname = os.path.splitext(filename)[0] + '.a85' - if filename==cachedname: - if cachedImageExists(filename): - from reportlab.lib.utils import open_for_read - if returnInMemory: return split(open_for_read(cachedname).read(),LINEEND)[:-1] - else: - raise IOError, 'No such cached image %s' % filename - else: - code = makeA85Image(filename,IMG) - if returnInMemory: return code - - #save it to a file - f = open(cachedname,'wb') - f.write(join(code, LINEEND)+LINEEND) - f.close() - if rl_config.verbose: - print 'cached image as %s' % cachedname - - -def preProcessImages(spec): - """Preprocesses one or more image files. - - Accepts either a filespec ('C:\mydir\*.jpg') or a list - of image filenames, crunches them all to save time. Run this - to save huge amounts of time when repeatedly building image - documents.""" - - import types, glob - - if type(spec) is types.StringType: - filelist = glob.glob(spec) - else: #list or tuple OK - filelist = spec - - for filename in filelist: - if cachedImageExists(filename): - if rl_config.verbose: - print 'cached version of %s already exists' % filename - else: - cacheImageFile(filename) - - -def cachedImageExists(filename): - """Determines if a cached image already exists for a given file. - - Determines if a cached image exists which has the same name - and equal or newer date to the given file.""" - cachedname = os.path.splitext(filename)[0] + '.a85' - if os.path.isfile(cachedname): - #see if it is newer - original_date = os.stat(filename)[8] - cached_date = os.stat(cachedname)[8] - if original_date > cached_date: - return 0 - else: - return 1 - else: - return 0 - - -############################################################## -# -# PDF Helper functions -# -############################################################## - -try: - from _rl_accel import escapePDF, _instanceEscapePDF - _escape = escapePDF -except ImportError: - try: - from reportlab.lib._rl_accel import escapePDF, _instanceEscapePDF - _escape = escapePDF - except ImportError: - _instanceEscapePDF=None - if rl_config.sys_version>='2.1': - _ESCAPEDICT={} - for c in range(0,256): - if c<32 or c>=127: - _ESCAPEDICT[chr(c)]= '\\%03o' % c - elif c in (ord('\\'),ord('('),ord(')')): - _ESCAPEDICT[chr(c)] = '\\'+chr(c) - else: - _ESCAPEDICT[chr(c)] = chr(c) - del c - #Michael Hudson donated this - def _escape(s): - return join(map(lambda c, d=_ESCAPEDICT: d[c],s),'') - else: - def _escape(s): - """Escapes some PDF symbols (in fact, parenthesis). - PDF escapes are almost like Python ones, but brackets - need slashes before them too. Uses Python's repr function - and chops off the quotes first.""" - s = repr(s)[1:-1] - s = replace(s, '(','\(') - s = replace(s, ')','\)') - return s - -def _normalizeLineEnds(text,desired=LINEEND): - """Normalizes different line end character(s). - - Ensures all instances of CR, LF and CRLF end up as - the specified one.""" - unlikely = '\000\001\002\003' - text = replace(text, '\015\012', unlikely) - text = replace(text, '\015', unlikely) - text = replace(text, '\012', unlikely) - text = replace(text, unlikely, desired) - return text - - -def _AsciiHexEncode(input): - """Encodes input using ASCII-Hex coding. - - This is a verbose encoding used for binary data within - a PDF file. One byte binary becomes two bytes of ASCII. - Helper function used by images.""" - output = getStringIO() - for char in input: - output.write('%02x' % ord(char)) - output.write('>') - return output.getvalue() - - -def _AsciiHexDecode(input): - """Decodes input using ASCII-Hex coding. - - Not used except to provide a test of the inverse function.""" - - #strip out all whitespace - stripped = join(split(input),'') - assert stripped[-1] == '>', 'Invalid terminator for Ascii Hex Stream' - stripped = stripped[:-1] #chop off terminator - assert len(stripped) % 2 == 0, 'Ascii Hex stream has odd number of bytes' - - i = 0 - output = getStringIO() - while i < len(stripped): - twobytes = stripped[i:i+2] - output.write(chr(eval('0x'+twobytes))) - i = i + 2 - return output.getvalue() - - -if 1: # for testing always define this - def _AsciiBase85EncodePYTHON(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.""" - outstream = getStringIO() - # 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:] - - for i in range(whole_word_count): - offset = i*4 - b1 = ord(body[offset]) - b2 = ord(body[offset+1]) - b3 = ord(body[offset+2]) - b4 = ord(body[offset+3]) - - if b1<128: - num = (((((b1<<8)|b2)<<8)|b3)<<8)|b4 - else: - num = 16777216L * b1 + 65536 * b2 + 256 * b3 + b4 - - if num == 0: - #special case - outstream.write('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!' - outstream.write(chr(c1+33)) - outstream.write(chr(c2+33)) - outstream.write(chr(c3+33)) - outstream.write(chr(c4+33)) - outstream.write(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: - while len(lastbit) < 4: - lastbit = lastbit + '\000' - b1 = ord(lastbit[0]) - b2 = ord(lastbit[1]) - b3 = ord(lastbit[2]) - b4 = ord(lastbit[3]) - - num = 16777216L * 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. - outstream.write(lastword[0:remainder_size + 1]) - - #terminator code for ascii 85 - outstream.write('~>') - return outstream.getvalue() - - def _AsciiBase85DecodePYTHON(input): - """Decodes input using ASCII-Base85 coding. - - This is not used - Acrobat Reader decodes for you - - but a round trip is essential for testing.""" - outstream = getStringIO() - #strip all whitespace - stripped = join(split(input),'') - #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 = replace(stripped,'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:] - - 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 = ((85L**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!' - outstream.write(chr(b1)) - outstream.write(chr(b2)) - outstream.write(chr(b3)) - outstream.write(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)*85L + (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 = '' - outstream.write(lastword) - - #terminator code for ascii 85 - return outstream.getvalue() - -try: - from _rl_accel import _AsciiBase85Encode # builtin or on the path -except ImportError: - try: - from reportlab.lib._rl_accel import _AsciiBase85Encode # where we think it should be - except ImportError: - _AsciiBase85Encode = _AsciiBase85EncodePYTHON - -try: - from _rl_accel import _AsciiBase85Decode # builtin or on the path -except ImportError: - try: - from reportlab.lib._rl_accel import _AsciiBase85Decode # where we think it should be - except ImportError: - _AsciiBase85Decode = _AsciiBase85DecodePYTHON - -def _wrap(input, columns=60): - "Wraps input at a given column size by inserting LINEEND characters." - - output = [] - length = len(input) - i = 0 - pos = columns * i - while pos < length: - output.append(input[pos:pos+columns]) - i = i + 1 - pos = columns * i - - return join(output, LINEEND) - - -######################################################################### -# -# JPEG processing code - contributed by Eric Johnson -# -######################################################################### - -# Read data from the JPEG file. We should probably be using PIL to -# get this information for us -- but this way is more fun! -# Returns (width, height, color components) as a triple -# This is based on Thomas Merz's code from GhostScript (viewjpeg.ps) -def readJPEGInfo(image): - "Read width, height and number of components from open JPEG file." - - import struct - from pdfdoc import PDFError - - #Acceptable JPEG Markers: - # SROF0=baseline, SOF1=extended sequential or SOF2=progressive - validMarkers = [0xC0, 0xC1, 0xC2] - - #JPEG markers without additional parameters - noParamMarkers = \ - [ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01 ] - - #Unsupported JPEG Markers - unsupportedMarkers = \ - [ 0xC3, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF ] - - #read JPEG marker segments until we find SOFn marker or EOF - done = 0 - while not done: - x = struct.unpack('B', image.read(1)) - if x[0] == 0xFF: #found marker - x = struct.unpack('B', image.read(1)) - #print "Marker: ", '%0.2x' % x[0] - #check marker type is acceptable and process it - if x[0] in validMarkers: - image.seek(2, 1) #skip segment length - x = struct.unpack('B', image.read(1)) #data precision - if x[0] != 8: - raise PDFError('JPEG must have 8 bits per component') - y = struct.unpack('BB', image.read(2)) - height = (y[0] << 8) + y[1] - y = struct.unpack('BB', image.read(2)) - width = (y[0] << 8) + y[1] - y = struct.unpack('B', image.read(1)) - color = y[0] - return width, height, color - done = 1 - elif x[0] in unsupportedMarkers: - raise PDFError('JPEG Unsupported JPEG marker: %0.2x' % x[0]) - elif x[0] not in noParamMarkers: - #skip segments with parameters - #read length and skip the data - x = struct.unpack('BB', image.read(2)) - image.seek( (x[0] << 8) + x[1] - 2, 1) - -class _fusc: - def __init__(self,k, n): - assert k, 'Argument k should be a non empty string' - self._k = k - self._klen = len(k) - self._n = int(n) or 7 - - def encrypt(self,s): - return self.__rotate(_AsciiBase85Encode(''.join(map(chr,self.__fusc(map(ord,s))))),self._n) - - def decrypt(self,s): - return ''.join(map(chr,self.__fusc(map(ord,_AsciiBase85Decode(self.__rotate(s,-self._n)))))) - - def __rotate(self,s,n): - l = len(s) - if n<0: n = l+n - n %= l - if not n: return s - return s[-n:]+s[:l-n] - - def __fusc(self,s): - slen = len(s) - return map(lambda x,y: x ^ y,s,map(ord,((int(slen/self._klen)+1)*self._k)[:slen])) diff --git a/bin/reportlab/pdfbase/rl_codecs.py b/bin/reportlab/pdfbase/rl_codecs.py deleted file mode 100644 index 326ed09fbd2..00000000000 --- a/bin/reportlab/pdfbase/rl_codecs.py +++ /dev/null @@ -1,1027 +0,0 @@ -#codecs support -__all__=['RL_Codecs'] -class RL_Codecs: - __rl_codecs_data = { - 'winansi':({ - 0x007f: 0x2022, # BULLET - 0x0080: 0x20ac, # EURO SIGN - 0x0081: 0x2022, # BULLET - 0x0082: 0x201a, # SINGLE LOW-9 QUOTATION MARK - 0x0083: 0x0192, # LATIN SMALL LETTER F WITH HOOK - 0x0084: 0x201e, # DOUBLE LOW-9 QUOTATION MARK - 0x0085: 0x2026, # HORIZONTAL ELLIPSIS - 0x0086: 0x2020, # DAGGER - 0x0087: 0x2021, # DOUBLE DAGGER - 0x0088: 0x02c6, # MODIFIER LETTER CIRCUMFLEX ACCENT - 0x0089: 0x2030, # PER MILLE SIGN - 0x008a: 0x0160, # LATIN CAPITAL LETTER S WITH CARON - 0x008b: 0x2039, # SINGLE LEFT-POINTING ANGLE QUOTATION MARK - 0x008c: 0x0152, # LATIN CAPITAL LIGATURE OE - 0x008d: 0x2022, # BULLET - 0x008e: 0x017d, # LATIN CAPITAL LETTER Z WITH CARON - 0x008f: 0x2022, # BULLET - 0x0090: 0x2022, # BULLET - 0x0091: 0x2018, # LEFT SINGLE QUOTATION MARK - 0x0092: 0x2019, # RIGHT SINGLE QUOTATION MARK - 0x0093: 0x201c, # LEFT DOUBLE QUOTATION MARK - 0x0094: 0x201d, # RIGHT DOUBLE QUOTATION MARK - 0x0095: 0x2022, # BULLET - 0x0096: 0x2013, # EN DASH - 0x0097: 0x2014, # EM DASH - 0x0098: 0x02dc, # SMALL TILDE - 0x0099: 0x2122, # TRADE MARK SIGN - 0x009a: 0x0161, # LATIN SMALL LETTER S WITH CARON - 0x009b: 0x203a, # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - 0x009c: 0x0153, # LATIN SMALL LIGATURE OE - 0x009d: 0x2022, # BULLET - 0x009e: 0x017e, # LATIN SMALL LETTER Z WITH CARON - 0x009f: 0x0178, # LATIN CAPITAL LETTER Y WITH DIAERESIS - 0x00a0: 0x0020, # SPACE - 0x00ad: 0x002d, # HYPHEN-MINUS - }, {0x2022:0x7f,0x20:0x20,0x2d:0x2d,0xa0:0x20}), - 'macroman':({ - 0x007f: None, # UNDEFINED - 0x0080: 0x00c4, # LATIN CAPITAL LETTER A WITH DIAERESIS - 0x0081: 0x00c5, # LATIN CAPITAL LETTER A WITH RING ABOVE - 0x0082: 0x00c7, # LATIN CAPITAL LETTER C WITH CEDILLA - 0x0083: 0x00c9, # LATIN CAPITAL LETTER E WITH ACUTE - 0x0084: 0x00d1, # LATIN CAPITAL LETTER N WITH TILDE - 0x0085: 0x00d6, # LATIN CAPITAL LETTER O WITH DIAERESIS - 0x0086: 0x00dc, # LATIN CAPITAL LETTER U WITH DIAERESIS - 0x0087: 0x00e1, # LATIN SMALL LETTER A WITH ACUTE - 0x0088: 0x00e0, # LATIN SMALL LETTER A WITH GRAVE - 0x0089: 0x00e2, # LATIN SMALL LETTER A WITH CIRCUMFLEX - 0x008a: 0x00e4, # LATIN SMALL LETTER A WITH DIAERESIS - 0x008b: 0x00e3, # LATIN SMALL LETTER A WITH TILDE - 0x008c: 0x00e5, # LATIN SMALL LETTER A WITH RING ABOVE - 0x008d: 0x00e7, # LATIN SMALL LETTER C WITH CEDILLA - 0x008e: 0x00e9, # LATIN SMALL LETTER E WITH ACUTE - 0x008f: 0x00e8, # LATIN SMALL LETTER E WITH GRAVE - 0x0090: 0x00ea, # LATIN SMALL LETTER E WITH CIRCUMFLEX - 0x0091: 0x00eb, # LATIN SMALL LETTER E WITH DIAERESIS - 0x0092: 0x00ed, # LATIN SMALL LETTER I WITH ACUTE - 0x0093: 0x00ec, # LATIN SMALL LETTER I WITH GRAVE - 0x0094: 0x00ee, # LATIN SMALL LETTER I WITH CIRCUMFLEX - 0x0095: 0x00ef, # LATIN SMALL LETTER I WITH DIAERESIS - 0x0096: 0x00f1, # LATIN SMALL LETTER N WITH TILDE - 0x0097: 0x00f3, # LATIN SMALL LETTER O WITH ACUTE - 0x0098: 0x00f2, # LATIN SMALL LETTER O WITH GRAVE - 0x0099: 0x00f4, # LATIN SMALL LETTER O WITH CIRCUMFLEX - 0x009a: 0x00f6, # LATIN SMALL LETTER O WITH DIAERESIS - 0x009b: 0x00f5, # LATIN SMALL LETTER O WITH TILDE - 0x009c: 0x00fa, # LATIN SMALL LETTER U WITH ACUTE - 0x009d: 0x00f9, # LATIN SMALL LETTER U WITH GRAVE - 0x009e: 0x00fb, # LATIN SMALL LETTER U WITH CIRCUMFLEX - 0x009f: 0x00fc, # LATIN SMALL LETTER U WITH DIAERESIS - 0x00a0: 0x2020, # DAGGER - 0x00a1: 0x00b0, # DEGREE SIGN - 0x00a4: 0x00a7, # SECTION SIGN - 0x00a5: 0x2022, # BULLET - 0x00a6: 0x00b6, # PILCROW SIGN - 0x00a7: 0x00df, # LATIN SMALL LETTER SHARP S - 0x00a8: 0x00ae, # REGISTERED SIGN - 0x00aa: 0x2122, # TRADE MARK SIGN - 0x00ab: 0x00b4, # ACUTE ACCENT - 0x00ac: 0x00a8, # DIAERESIS - 0x00ad: None, # UNDEFINED - 0x00ae: 0x00c6, # LATIN CAPITAL LETTER AE - 0x00af: 0x00d8, # LATIN CAPITAL LETTER O WITH STROKE - 0x00b0: None, # UNDEFINED - 0x00b2: None, # UNDEFINED - 0x00b3: None, # UNDEFINED - 0x00b4: 0x00a5, # YEN SIGN - 0x00b6: None, # UNDEFINED - 0x00b7: None, # UNDEFINED - 0x00b8: None, # UNDEFINED - 0x00b9: None, # UNDEFINED - 0x00ba: None, # UNDEFINED - 0x00bb: 0x00aa, # FEMININE ORDINAL INDICATOR - 0x00bc: 0x00ba, # MASCULINE ORDINAL INDICATOR - 0x00bd: None, # UNDEFINED - 0x00be: 0x00e6, # LATIN SMALL LETTER AE - 0x00bf: 0x00f8, # LATIN SMALL LETTER O WITH STROKE - 0x00c0: 0x00bf, # INVERTED QUESTION MARK - 0x00c1: 0x00a1, # INVERTED EXCLAMATION MARK - 0x00c2: 0x00ac, # NOT SIGN - 0x00c3: None, # UNDEFINED - 0x00c4: 0x0192, # LATIN SMALL LETTER F WITH HOOK - 0x00c5: None, # UNDEFINED - 0x00c6: None, # UNDEFINED - 0x00c7: 0x00ab, # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK - 0x00c8: 0x00bb, # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - 0x00c9: 0x2026, # HORIZONTAL ELLIPSIS - 0x00ca: 0x0020, # SPACE - 0x00cb: 0x00c0, # LATIN CAPITAL LETTER A WITH GRAVE - 0x00cc: 0x00c3, # LATIN CAPITAL LETTER A WITH TILDE - 0x00cd: 0x00d5, # LATIN CAPITAL LETTER O WITH TILDE - 0x00ce: 0x0152, # LATIN CAPITAL LIGATURE OE - 0x00cf: 0x0153, # LATIN SMALL LIGATURE OE - 0x00d0: 0x2013, # EN DASH - 0x00d1: 0x2014, # EM DASH - 0x00d2: 0x201c, # LEFT DOUBLE QUOTATION MARK - 0x00d3: 0x201d, # RIGHT DOUBLE QUOTATION MARK - 0x00d4: 0x2018, # LEFT SINGLE QUOTATION MARK - 0x00d5: 0x2019, # RIGHT SINGLE QUOTATION MARK - 0x00d6: 0x00f7, # DIVISION SIGN - 0x00d7: None, # UNDEFINED - 0x00d8: 0x00ff, # LATIN SMALL LETTER Y WITH DIAERESIS - 0x00d9: 0x0178, # LATIN CAPITAL LETTER Y WITH DIAERESIS - 0x00da: 0x2044, # FRACTION SLASH - 0x00db: 0x00a4, # CURRENCY SIGN - 0x00dc: 0x2039, # SINGLE LEFT-POINTING ANGLE QUOTATION MARK - 0x00dd: 0x203a, # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - 0x00de: 0xfb01, # LATIN SMALL LIGATURE FI - 0x00df: 0xfb02, # LATIN SMALL LIGATURE FL - 0x00e0: 0x2021, # DOUBLE DAGGER - 0x00e1: 0x00b7, # MIDDLE DOT - 0x00e2: 0x201a, # SINGLE LOW-9 QUOTATION MARK - 0x00e3: 0x201e, # DOUBLE LOW-9 QUOTATION MARK - 0x00e4: 0x2030, # PER MILLE SIGN - 0x00e5: 0x00c2, # LATIN CAPITAL LETTER A WITH CIRCUMFLEX - 0x00e6: 0x00ca, # LATIN CAPITAL LETTER E WITH CIRCUMFLEX - 0x00e7: 0x00c1, # LATIN CAPITAL LETTER A WITH ACUTE - 0x00e8: 0x00cb, # LATIN CAPITAL LETTER E WITH DIAERESIS - 0x00e9: 0x00c8, # LATIN CAPITAL LETTER E WITH GRAVE - 0x00ea: 0x00cd, # LATIN CAPITAL LETTER I WITH ACUTE - 0x00eb: 0x00ce, # LATIN CAPITAL LETTER I WITH CIRCUMFLEX - 0x00ec: 0x00cf, # LATIN CAPITAL LETTER I WITH DIAERESIS - 0x00ed: 0x00cc, # LATIN CAPITAL LETTER I WITH GRAVE - 0x00ee: 0x00d3, # LATIN CAPITAL LETTER O WITH ACUTE - 0x00ef: 0x00d4, # LATIN CAPITAL LETTER O WITH CIRCUMFLEX - 0x00f0: None, # UNDEFINED - 0x00f1: 0x00d2, # LATIN CAPITAL LETTER O WITH GRAVE - 0x00f2: 0x00da, # LATIN CAPITAL LETTER U WITH ACUTE - 0x00f3: 0x00db, # LATIN CAPITAL LETTER U WITH CIRCUMFLEX - 0x00f4: 0x00d9, # LATIN CAPITAL LETTER U WITH GRAVE - 0x00f5: 0x0131, # LATIN SMALL LETTER DOTLESS I - 0x00f6: 0x02c6, # MODIFIER LETTER CIRCUMFLEX ACCENT - 0x00f7: 0x02dc, # SMALL TILDE - 0x00f8: 0x00af, # MACRON - 0x00f9: 0x02d8, # BREVE - 0x00fa: 0x02d9, # DOT ABOVE - 0x00fb: 0x02da, # RING ABOVE - 0x00fc: 0x00b8, # CEDILLA - 0x00fd: 0x02dd, # DOUBLE ACUTE ACCENT - 0x00fe: 0x02db, # OGONEK - 0x00ff: 0x02c7, # CARON - },None), - 'standard':({ - 0x0027: 0x2019, # RIGHT SINGLE QUOTATION MARK - 0x0060: 0x2018, # LEFT SINGLE QUOTATION MARK - 0x007f: None, # UNDEFINED - 0x0080: None, # UNDEFINED - 0x0081: None, # UNDEFINED - 0x0082: None, # UNDEFINED - 0x0083: None, # UNDEFINED - 0x0084: None, # UNDEFINED - 0x0085: None, # UNDEFINED - 0x0086: None, # UNDEFINED - 0x0087: None, # UNDEFINED - 0x0088: None, # UNDEFINED - 0x0089: None, # UNDEFINED - 0x008a: None, # UNDEFINED - 0x008b: None, # UNDEFINED - 0x008c: None, # UNDEFINED - 0x008d: None, # UNDEFINED - 0x008e: None, # UNDEFINED - 0x008f: None, # UNDEFINED - 0x0090: None, # UNDEFINED - 0x0091: None, # UNDEFINED - 0x0092: None, # UNDEFINED - 0x0093: None, # UNDEFINED - 0x0094: None, # UNDEFINED - 0x0095: None, # UNDEFINED - 0x0096: None, # UNDEFINED - 0x0097: None, # UNDEFINED - 0x0098: None, # UNDEFINED - 0x0099: None, # UNDEFINED - 0x009a: None, # UNDEFINED - 0x009b: None, # UNDEFINED - 0x009c: None, # UNDEFINED - 0x009d: None, # UNDEFINED - 0x009e: None, # UNDEFINED - 0x009f: None, # UNDEFINED - 0x00a0: None, # UNDEFINED - 0x00a4: 0x2044, # FRACTION SLASH - 0x00a6: 0x0192, # LATIN SMALL LETTER F WITH HOOK - 0x00a8: 0x00a4, # CURRENCY SIGN - 0x00a9: 0x0027, # APOSTROPHE - 0x00aa: 0x201c, # LEFT DOUBLE QUOTATION MARK - 0x00ac: 0x2039, # SINGLE LEFT-POINTING ANGLE QUOTATION MARK - 0x00ad: 0x203a, # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - 0x00ae: 0xfb01, # LATIN SMALL LIGATURE FI - 0x00af: 0xfb02, # LATIN SMALL LIGATURE FL - 0x00b0: None, # UNDEFINED - 0x00b1: 0x2013, # EN DASH - 0x00b2: 0x2020, # DAGGER - 0x00b3: 0x2021, # DOUBLE DAGGER - 0x00b4: 0x00b7, # MIDDLE DOT - 0x00b5: None, # UNDEFINED - 0x00b7: 0x2022, # BULLET - 0x00b8: 0x201a, # SINGLE LOW-9 QUOTATION MARK - 0x00b9: 0x201e, # DOUBLE LOW-9 QUOTATION MARK - 0x00ba: 0x201d, # RIGHT DOUBLE QUOTATION MARK - 0x00bc: 0x2026, # HORIZONTAL ELLIPSIS - 0x00bd: 0x2030, # PER MILLE SIGN - 0x00be: None, # UNDEFINED - 0x00c0: None, # UNDEFINED - 0x00c1: 0x0060, # GRAVE ACCENT - 0x00c2: 0x00b4, # ACUTE ACCENT - 0x00c3: 0x02c6, # MODIFIER LETTER CIRCUMFLEX ACCENT - 0x00c4: 0x02dc, # SMALL TILDE - 0x00c5: 0x00af, # MACRON - 0x00c6: 0x02d8, # BREVE - 0x00c7: 0x02d9, # DOT ABOVE - 0x00c8: 0x00a8, # DIAERESIS - 0x00c9: None, # UNDEFINED - 0x00ca: 0x02da, # RING ABOVE - 0x00cb: 0x00b8, # CEDILLA - 0x00cc: None, # UNDEFINED - 0x00cd: 0x02dd, # DOUBLE ACUTE ACCENT - 0x00ce: 0x02db, # OGONEK - 0x00cf: 0x02c7, # CARON - 0x00d0: 0x2014, # EM DASH - 0x00d1: None, # UNDEFINED - 0x00d2: None, # UNDEFINED - 0x00d3: None, # UNDEFINED - 0x00d4: None, # UNDEFINED - 0x00d5: None, # UNDEFINED - 0x00d6: None, # UNDEFINED - 0x00d7: None, # UNDEFINED - 0x00d8: None, # UNDEFINED - 0x00d9: None, # UNDEFINED - 0x00da: None, # UNDEFINED - 0x00db: None, # UNDEFINED - 0x00dc: None, # UNDEFINED - 0x00dd: None, # UNDEFINED - 0x00de: None, # UNDEFINED - 0x00df: None, # UNDEFINED - 0x00e0: None, # UNDEFINED - 0x00e1: 0x00c6, # LATIN CAPITAL LETTER AE - 0x00e2: None, # UNDEFINED - 0x00e3: 0x00aa, # FEMININE ORDINAL INDICATOR - 0x00e4: None, # UNDEFINED - 0x00e5: None, # UNDEFINED - 0x00e6: None, # UNDEFINED - 0x00e7: None, # UNDEFINED - 0x00e8: 0x0141, # LATIN CAPITAL LETTER L WITH STROKE - 0x00e9: 0x00d8, # LATIN CAPITAL LETTER O WITH STROKE - 0x00ea: 0x0152, # LATIN CAPITAL LIGATURE OE - 0x00eb: 0x00ba, # MASCULINE ORDINAL INDICATOR - 0x00ec: None, # UNDEFINED - 0x00ed: None, # UNDEFINED - 0x00ee: None, # UNDEFINED - 0x00ef: None, # UNDEFINED - 0x00f0: None, # UNDEFINED - 0x00f1: 0x00e6, # LATIN SMALL LETTER AE - 0x00f2: None, # UNDEFINED - 0x00f3: None, # UNDEFINED - 0x00f4: None, # UNDEFINED - 0x00f5: 0x0131, # LATIN SMALL LETTER DOTLESS I - 0x00f6: None, # UNDEFINED - 0x00f7: None, # UNDEFINED - 0x00f8: 0x0142, # LATIN SMALL LETTER L WITH STROKE - 0x00f9: 0x00f8, # LATIN SMALL LETTER O WITH STROKE - 0x00fa: 0x0153, # LATIN SMALL LIGATURE OE - 0x00fb: 0x00df, # LATIN SMALL LETTER SHARP S - 0x00fc: None, # UNDEFINED - 0x00fd: None, # UNDEFINED - 0x00fe: None, # UNDEFINED - 0x00ff: None, # UNDEFINED - },None), - 'symbol':({ - 0x0022: 0x2200, # FOR ALL - 0x0024: 0x2203, # THERE EXISTS - 0x0027: 0x220b, # CONTAINS AS MEMBER - 0x002a: 0x2217, # ASTERISK OPERATOR - 0x002d: 0x2212, # MINUS SIGN - 0x0040: 0x2245, # APPROXIMATELY EQUAL TO - 0x0041: 0x0391, # GREEK CAPITAL LETTER ALPHA - 0x0042: 0x0392, # GREEK CAPITAL LETTER BETA - 0x0043: 0x03a7, # GREEK CAPITAL LETTER CHI - 0x0044: 0x2206, # INCREMENT - 0x0045: 0x0395, # GREEK CAPITAL LETTER EPSILON - 0x0046: 0x03a6, # GREEK CAPITAL LETTER PHI - 0x0047: 0x0393, # GREEK CAPITAL LETTER GAMMA - 0x0048: 0x0397, # GREEK CAPITAL LETTER ETA - 0x0049: 0x0399, # GREEK CAPITAL LETTER IOTA - 0x004a: 0x03d1, # GREEK THETA SYMBOL - 0x004b: 0x039a, # GREEK CAPITAL LETTER KAPPA - 0x004c: 0x039b, # GREEK CAPITAL LETTER LAMDA - 0x004d: 0x039c, # GREEK CAPITAL LETTER MU - 0x004e: 0x039d, # GREEK CAPITAL LETTER NU - 0x004f: 0x039f, # GREEK CAPITAL LETTER OMICRON - 0x0050: 0x03a0, # GREEK CAPITAL LETTER PI - 0x0051: 0x0398, # GREEK CAPITAL LETTER THETA - 0x0052: 0x03a1, # GREEK CAPITAL LETTER RHO - 0x0053: 0x03a3, # GREEK CAPITAL LETTER SIGMA - 0x0054: 0x03a4, # GREEK CAPITAL LETTER TAU - 0x0055: 0x03a5, # GREEK CAPITAL LETTER UPSILON - 0x0056: 0x03c2, # GREEK SMALL LETTER FINAL SIGMA - 0x0057: 0x2126, # OHM SIGN - 0x0058: 0x039e, # GREEK CAPITAL LETTER XI - 0x0059: 0x03a8, # GREEK CAPITAL LETTER PSI - 0x005a: 0x0396, # GREEK CAPITAL LETTER ZETA - 0x005c: 0x2234, # THEREFORE - 0x005e: 0x22a5, # UP TACK - 0x0060: 0xf8e5, # [unknown unicode name for radicalex] - 0x0061: 0x03b1, # GREEK SMALL LETTER ALPHA - 0x0062: 0x03b2, # GREEK SMALL LETTER BETA - 0x0063: 0x03c7, # GREEK SMALL LETTER CHI - 0x0064: 0x03b4, # GREEK SMALL LETTER DELTA - 0x0065: 0x03b5, # GREEK SMALL LETTER EPSILON - 0x0066: 0x03c6, # GREEK SMALL LETTER PHI - 0x0067: 0x03b3, # GREEK SMALL LETTER GAMMA - 0x0068: 0x03b7, # GREEK SMALL LETTER ETA - 0x0069: 0x03b9, # GREEK SMALL LETTER IOTA - 0x006a: 0x03d5, # GREEK PHI SYMBOL - 0x006b: 0x03ba, # GREEK SMALL LETTER KAPPA - 0x006c: 0x03bb, # GREEK SMALL LETTER LAMDA - 0x006d: 0x00b5, # MICRO SIGN - 0x006e: 0x03bd, # GREEK SMALL LETTER NU - 0x006f: 0x03bf, # GREEK SMALL LETTER OMICRON - 0x0070: 0x03c0, # GREEK SMALL LETTER PI - 0x0071: 0x03b8, # GREEK SMALL LETTER THETA - 0x0072: 0x03c1, # GREEK SMALL LETTER RHO - 0x0073: 0x03c3, # GREEK SMALL LETTER SIGMA - 0x0074: 0x03c4, # GREEK SMALL LETTER TAU - 0x0075: 0x03c5, # GREEK SMALL LETTER UPSILON - 0x0076: 0x03d6, # GREEK PI SYMBOL - 0x0077: 0x03c9, # GREEK SMALL LETTER OMEGA - 0x0078: 0x03be, # GREEK SMALL LETTER XI - 0x0079: 0x03c8, # GREEK SMALL LETTER PSI - 0x007a: 0x03b6, # GREEK SMALL LETTER ZETA - 0x007e: 0x223c, # TILDE OPERATOR - 0x007f: None, # UNDEFINED - 0x0080: None, # UNDEFINED - 0x0081: None, # UNDEFINED - 0x0082: None, # UNDEFINED - 0x0083: None, # UNDEFINED - 0x0084: None, # UNDEFINED - 0x0085: None, # UNDEFINED - 0x0086: None, # UNDEFINED - 0x0087: None, # UNDEFINED - 0x0088: None, # UNDEFINED - 0x0089: None, # UNDEFINED - 0x008a: None, # UNDEFINED - 0x008b: None, # UNDEFINED - 0x008c: None, # UNDEFINED - 0x008d: None, # UNDEFINED - 0x008e: None, # UNDEFINED - 0x008f: None, # UNDEFINED - 0x0090: None, # UNDEFINED - 0x0091: None, # UNDEFINED - 0x0092: None, # UNDEFINED - 0x0093: None, # UNDEFINED - 0x0094: None, # UNDEFINED - 0x0095: None, # UNDEFINED - 0x0096: None, # UNDEFINED - 0x0097: None, # UNDEFINED - 0x0098: None, # UNDEFINED - 0x0099: None, # UNDEFINED - 0x009a: None, # UNDEFINED - 0x009b: None, # UNDEFINED - 0x009c: None, # UNDEFINED - 0x009d: None, # UNDEFINED - 0x009e: None, # UNDEFINED - 0x009f: None, # UNDEFINED - 0x00a0: 0x20ac, # EURO SIGN - 0x00a1: 0x03d2, # GREEK UPSILON WITH HOOK SYMBOL - 0x00a2: 0x2032, # PRIME - 0x00a3: 0x2264, # LESS-THAN OR EQUAL TO - 0x00a4: 0x2044, # FRACTION SLASH - 0x00a5: 0x221e, # INFINITY - 0x00a6: 0x0192, # LATIN SMALL LETTER F WITH HOOK - 0x00a7: 0x2663, # BLACK CLUB SUIT - 0x00a8: 0x2666, # BLACK DIAMOND SUIT - 0x00a9: 0x2665, # BLACK HEART SUIT - 0x00aa: 0x2660, # BLACK SPADE SUIT - 0x00ab: 0x2194, # LEFT RIGHT ARROW - 0x00ac: 0x2190, # LEFTWARDS ARROW - 0x00ad: 0x2191, # UPWARDS ARROW - 0x00ae: 0x2192, # RIGHTWARDS ARROW - 0x00af: 0x2193, # DOWNWARDS ARROW - 0x00b2: 0x2033, # DOUBLE PRIME - 0x00b3: 0x2265, # GREATER-THAN OR EQUAL TO - 0x00b4: 0x00d7, # MULTIPLICATION SIGN - 0x00b5: 0x221d, # PROPORTIONAL TO - 0x00b6: 0x2202, # PARTIAL DIFFERENTIAL - 0x00b7: 0x2022, # BULLET - 0x00b8: 0x00f7, # DIVISION SIGN - 0x00b9: 0x2260, # NOT EQUAL TO - 0x00ba: 0x2261, # IDENTICAL TO - 0x00bb: 0x2248, # ALMOST EQUAL TO - 0x00bc: 0x2026, # HORIZONTAL ELLIPSIS - 0x00bd: 0xf8e6, # [unknown unicode name for arrowvertex] - 0x00be: 0xf8e7, # [unknown unicode name for arrowhorizex] - 0x00bf: 0x21b5, # DOWNWARDS ARROW WITH CORNER LEFTWARDS - 0x00c0: 0x2135, # ALEF SYMBOL - 0x00c1: 0x2111, # BLACK-LETTER CAPITAL I - 0x00c2: 0x211c, # BLACK-LETTER CAPITAL R - 0x00c3: 0x2118, # SCRIPT CAPITAL P - 0x00c4: 0x2297, # CIRCLED TIMES - 0x00c5: 0x2295, # CIRCLED PLUS - 0x00c6: 0x2205, # EMPTY SET - 0x00c7: 0x2229, # INTERSECTION - 0x00c8: 0x222a, # UNION - 0x00c9: 0x2283, # SUPERSET OF - 0x00ca: 0x2287, # SUPERSET OF OR EQUAL TO - 0x00cb: 0x2284, # NOT A SUBSET OF - 0x00cc: 0x2282, # SUBSET OF - 0x00cd: 0x2286, # SUBSET OF OR EQUAL TO - 0x00ce: 0x2208, # ELEMENT OF - 0x00cf: 0x2209, # NOT AN ELEMENT OF - 0x00d0: 0x2220, # ANGLE - 0x00d1: 0x2207, # NABLA - 0x00d2: 0xf6da, # [unknown unicode name for registerserif] - 0x00d3: 0xf6d9, # [unknown unicode name for copyrightserif] - 0x00d4: 0xf6db, # [unknown unicode name for trademarkserif] - 0x00d5: 0x220f, # N-ARY PRODUCT - 0x00d6: 0x221a, # SQUARE ROOT - 0x00d7: 0x22c5, # DOT OPERATOR - 0x00d8: 0x00ac, # NOT SIGN - 0x00d9: 0x2227, # LOGICAL AND - 0x00da: 0x2228, # LOGICAL OR - 0x00db: 0x21d4, # LEFT RIGHT DOUBLE ARROW - 0x00dc: 0x21d0, # LEFTWARDS DOUBLE ARROW - 0x00dd: 0x21d1, # UPWARDS DOUBLE ARROW - 0x00de: 0x21d2, # RIGHTWARDS DOUBLE ARROW - 0x00df: 0x21d3, # DOWNWARDS DOUBLE ARROW - 0x00e0: 0x25ca, # LOZENGE - 0x00e1: 0x2329, # LEFT-POINTING ANGLE BRACKET - 0x00e2: 0xf8e8, # [unknown unicode name for registersans] - 0x00e3: 0xf8e9, # [unknown unicode name for copyrightsans] - 0x00e4: 0xf8ea, # [unknown unicode name for trademarksans] - 0x00e5: 0x2211, # N-ARY SUMMATION - 0x00e6: 0xf8eb, # [unknown unicode name for parenlefttp] - 0x00e7: 0xf8ec, # [unknown unicode name for parenleftex] - 0x00e8: 0xf8ed, # [unknown unicode name for parenleftbt] - 0x00e9: 0xf8ee, # [unknown unicode name for bracketlefttp] - 0x00ea: 0xf8ef, # [unknown unicode name for bracketleftex] - 0x00eb: 0xf8f0, # [unknown unicode name for bracketleftbt] - 0x00ec: 0xf8f1, # [unknown unicode name for bracelefttp] - 0x00ed: 0xf8f2, # [unknown unicode name for braceleftmid] - 0x00ee: 0xf8f3, # [unknown unicode name for braceleftbt] - 0x00ef: 0xf8f4, # [unknown unicode name for braceex] - 0x00f0: None, # UNDEFINED - 0x00f1: 0x232a, # RIGHT-POINTING ANGLE BRACKET - 0x00f2: 0x222b, # INTEGRAL - 0x00f3: 0x2320, # TOP HALF INTEGRAL - 0x00f4: 0xf8f5, # [unknown unicode name for integralex] - 0x00f5: 0x2321, # BOTTOM HALF INTEGRAL - 0x00f6: 0xf8f6, # [unknown unicode name for parenrighttp] - 0x00f7: 0xf8f7, # [unknown unicode name for parenrightex] - 0x00f8: 0xf8f8, # [unknown unicode name for parenrightbt] - 0x00f9: 0xf8f9, # [unknown unicode name for bracketrighttp] - 0x00fa: 0xf8fa, # [unknown unicode name for bracketrightex] - 0x00fb: 0xf8fb, # [unknown unicode name for bracketrightbt] - 0x00fc: 0xf8fc, # [unknown unicode name for bracerighttp] - 0x00fd: 0xf8fd, # [unknown unicode name for bracerightmid] - 0x00fe: 0xf8fe, # [unknown unicode name for bracerightbt] - 0x00ff: None, # UNDEFINED - },None), - 'zapfdingbats':({ - 0x0021: 0x2701, # UPPER BLADE SCISSORS - 0x0022: 0x2702, # BLACK SCISSORS - 0x0023: 0x2703, # LOWER BLADE SCISSORS - 0x0024: 0x2704, # WHITE SCISSORS - 0x0025: 0x260e, # BLACK TELEPHONE - 0x0026: 0x2706, # TELEPHONE LOCATION SIGN - 0x0027: 0x2707, # TAPE DRIVE - 0x0028: 0x2708, # AIRPLANE - 0x0029: 0x2709, # ENVELOPE - 0x002a: 0x261b, # BLACK RIGHT POINTING INDEX - 0x002b: 0x261e, # WHITE RIGHT POINTING INDEX - 0x002c: 0x270c, # VICTORY HAND - 0x002d: 0x270d, # WRITING HAND - 0x002e: 0x270e, # LOWER RIGHT PENCIL - 0x002f: 0x270f, # PENCIL - 0x0030: 0x2710, # UPPER RIGHT PENCIL - 0x0031: 0x2711, # WHITE NIB - 0x0032: 0x2712, # BLACK NIB - 0x0033: 0x2713, # CHECK MARK - 0x0034: 0x2714, # HEAVY CHECK MARK - 0x0035: 0x2715, # MULTIPLICATION X - 0x0036: 0x2716, # HEAVY MULTIPLICATION X - 0x0037: 0x2717, # BALLOT X - 0x0038: 0x2718, # HEAVY BALLOT X - 0x0039: 0x2719, # OUTLINED GREEK CROSS - 0x003a: 0x271a, # HEAVY GREEK CROSS - 0x003b: 0x271b, # OPEN CENTRE CROSS - 0x003c: 0x271c, # HEAVY OPEN CENTRE CROSS - 0x003d: 0x271d, # LATIN CROSS - 0x003e: 0x271e, # SHADOWED WHITE LATIN CROSS - 0x003f: 0x271f, # OUTLINED LATIN CROSS - 0x0040: 0x2720, # MALTESE CROSS - 0x0041: 0x2721, # STAR OF DAVID - 0x0042: 0x2722, # FOUR TEARDROP-SPOKED ASTERISK - 0x0043: 0x2723, # FOUR BALLOON-SPOKED ASTERISK - 0x0044: 0x2724, # HEAVY FOUR BALLOON-SPOKED ASTERISK - 0x0045: 0x2725, # FOUR CLUB-SPOKED ASTERISK - 0x0046: 0x2726, # BLACK FOUR POINTED STAR - 0x0047: 0x2727, # WHITE FOUR POINTED STAR - 0x0048: 0x2605, # BLACK STAR - 0x0049: 0x2729, # STRESS OUTLINED WHITE STAR - 0x004a: 0x272a, # CIRCLED WHITE STAR - 0x004b: 0x272b, # OPEN CENTRE BLACK STAR - 0x004c: 0x272c, # BLACK CENTRE WHITE STAR - 0x004d: 0x272d, # OUTLINED BLACK STAR - 0x004e: 0x272e, # HEAVY OUTLINED BLACK STAR - 0x004f: 0x272f, # PINWHEEL STAR - 0x0050: 0x2730, # SHADOWED WHITE STAR - 0x0051: 0x2731, # HEAVY ASTERISK - 0x0052: 0x2732, # OPEN CENTRE ASTERISK - 0x0053: 0x2733, # EIGHT SPOKED ASTERISK - 0x0054: 0x2734, # EIGHT POINTED BLACK STAR - 0x0055: 0x2735, # EIGHT POINTED PINWHEEL STAR - 0x0056: 0x2736, # SIX POINTED BLACK STAR - 0x0057: 0x2737, # EIGHT POINTED RECTILINEAR BLACK STAR - 0x0058: 0x2738, # HEAVY EIGHT POINTED RECTILINEAR BLACK STAR - 0x0059: 0x2739, # TWELVE POINTED BLACK STAR - 0x005a: 0x273a, # SIXTEEN POINTED ASTERISK - 0x005b: 0x273b, # TEARDROP-SPOKED ASTERISK - 0x005c: 0x273c, # OPEN CENTRE TEARDROP-SPOKED ASTERISK - 0x005d: 0x273d, # HEAVY TEARDROP-SPOKED ASTERISK - 0x005e: 0x273e, # SIX PETALLED BLACK AND WHITE FLORETTE - 0x005f: 0x273f, # BLACK FLORETTE - 0x0060: 0x2740, # WHITE FLORETTE - 0x0061: 0x2741, # EIGHT PETALLED OUTLINED BLACK FLORETTE - 0x0062: 0x2742, # CIRCLED OPEN CENTRE EIGHT POINTED STAR - 0x0063: 0x2743, # HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK - 0x0064: 0x2744, # SNOWFLAKE - 0x0065: 0x2745, # TIGHT TRIFOLIATE SNOWFLAKE - 0x0066: 0x2746, # HEAVY CHEVRON SNOWFLAKE - 0x0067: 0x2747, # SPARKLE - 0x0068: 0x2748, # HEAVY SPARKLE - 0x0069: 0x2749, # BALLOON-SPOKED ASTERISK - 0x006a: 0x274a, # EIGHT TEARDROP-SPOKED PROPELLER ASTERISK - 0x006b: 0x274b, # HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK - 0x006c: 0x25cf, # BLACK CIRCLE - 0x006d: 0x274d, # SHADOWED WHITE CIRCLE - 0x006e: 0x25a0, # BLACK SQUARE - 0x006f: 0x274f, # LOWER RIGHT DROP-SHADOWED WHITE SQUARE - 0x0070: 0x2750, # UPPER RIGHT DROP-SHADOWED WHITE SQUARE - 0x0071: 0x2751, # LOWER RIGHT SHADOWED WHITE SQUARE - 0x0072: 0x2752, # UPPER RIGHT SHADOWED WHITE SQUARE - 0x0073: 0x25b2, # BLACK UP-POINTING TRIANGLE - 0x0074: 0x25bc, # BLACK DOWN-POINTING TRIANGLE - 0x0075: 0x25c6, # BLACK DIAMOND - 0x0076: 0x2756, # BLACK DIAMOND MINUS WHITE X - 0x0077: 0x25d7, # RIGHT HALF BLACK CIRCLE - 0x0078: 0x2758, # LIGHT VERTICAL BAR - 0x0079: 0x2759, # MEDIUM VERTICAL BAR - 0x007a: 0x275a, # HEAVY VERTICAL BAR - 0x007b: 0x275b, # HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT - 0x007c: 0x275c, # HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT - 0x007d: 0x275d, # HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT - 0x007e: 0x275e, # HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT - 0x007f: None, # UNDEFINED - 0x0080: 0x2768, # MEDIUM LEFT PARENTHESIS ORNAMENT - 0x0081: 0x2769, # MEDIUM RIGHT PARENTHESIS ORNAMENT - 0x0082: 0x276a, # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT - 0x0083: 0x276b, # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT - 0x0084: 0x276c, # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT - 0x0085: 0x276d, # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT - 0x0086: 0x276e, # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT - 0x0087: 0x276f, # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT - 0x0088: 0x2770, # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT - 0x0089: 0x2771, # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT - 0x008a: 0x2772, # LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT - 0x008b: 0x2773, # LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT - 0x008c: 0x2774, # MEDIUM LEFT CURLY BRACKET ORNAMENT - 0x008d: 0x2775, # MEDIUM RIGHT CURLY BRACKET ORNAMENT - 0x008e: None, # UNDEFINED - 0x008f: None, # UNDEFINED - 0x0090: None, # UNDEFINED - 0x0091: None, # UNDEFINED - 0x0092: None, # UNDEFINED - 0x0093: None, # UNDEFINED - 0x0094: None, # UNDEFINED - 0x0095: None, # UNDEFINED - 0x0096: None, # UNDEFINED - 0x0097: None, # UNDEFINED - 0x0098: None, # UNDEFINED - 0x0099: None, # UNDEFINED - 0x009a: None, # UNDEFINED - 0x009b: None, # UNDEFINED - 0x009c: None, # UNDEFINED - 0x009d: None, # UNDEFINED - 0x009e: None, # UNDEFINED - 0x009f: None, # UNDEFINED - 0x00a0: None, # UNDEFINED - 0x00a1: 0x2761, # CURVED STEM PARAGRAPH SIGN ORNAMENT - 0x00a2: 0x2762, # HEAVY EXCLAMATION MARK ORNAMENT - 0x00a3: 0x2763, # HEAVY HEART EXCLAMATION MARK ORNAMENT - 0x00a4: 0x2764, # HEAVY BLACK HEART - 0x00a5: 0x2765, # ROTATED HEAVY BLACK HEART BULLET - 0x00a6: 0x2766, # FLORAL HEART - 0x00a7: 0x2767, # ROTATED FLORAL HEART BULLET - 0x00a8: 0x2663, # BLACK CLUB SUIT - 0x00a9: 0x2666, # BLACK DIAMOND SUIT - 0x00aa: 0x2665, # BLACK HEART SUIT - 0x00ab: 0x2660, # BLACK SPADE SUIT - 0x00ac: 0x2460, # CIRCLED DIGIT ONE - 0x00ad: 0x2461, # CIRCLED DIGIT TWO - 0x00ae: 0x2462, # CIRCLED DIGIT THREE - 0x00af: 0x2463, # CIRCLED DIGIT FOUR - 0x00b0: 0x2464, # CIRCLED DIGIT FIVE - 0x00b1: 0x2465, # CIRCLED DIGIT SIX - 0x00b2: 0x2466, # CIRCLED DIGIT SEVEN - 0x00b3: 0x2467, # CIRCLED DIGIT EIGHT - 0x00b4: 0x2468, # CIRCLED DIGIT NINE - 0x00b5: 0x2469, # CIRCLED NUMBER TEN - 0x00b6: 0x2776, # DINGBAT NEGATIVE CIRCLED DIGIT ONE - 0x00b7: 0x2777, # DINGBAT NEGATIVE CIRCLED DIGIT TWO - 0x00b8: 0x2778, # DINGBAT NEGATIVE CIRCLED DIGIT THREE - 0x00b9: 0x2779, # DINGBAT NEGATIVE CIRCLED DIGIT FOUR - 0x00ba: 0x277a, # DINGBAT NEGATIVE CIRCLED DIGIT FIVE - 0x00bb: 0x277b, # DINGBAT NEGATIVE CIRCLED DIGIT SIX - 0x00bc: 0x277c, # DINGBAT NEGATIVE CIRCLED DIGIT SEVEN - 0x00bd: 0x277d, # DINGBAT NEGATIVE CIRCLED DIGIT EIGHT - 0x00be: 0x277e, # DINGBAT NEGATIVE CIRCLED DIGIT NINE - 0x00bf: 0x277f, # DINGBAT NEGATIVE CIRCLED NUMBER TEN - 0x00c0: 0x2780, # DINGBAT CIRCLED SANS-SERIF DIGIT ONE - 0x00c1: 0x2781, # DINGBAT CIRCLED SANS-SERIF DIGIT TWO - 0x00c2: 0x2782, # DINGBAT CIRCLED SANS-SERIF DIGIT THREE - 0x00c3: 0x2783, # DINGBAT CIRCLED SANS-SERIF DIGIT FOUR - 0x00c4: 0x2784, # DINGBAT CIRCLED SANS-SERIF DIGIT FIVE - 0x00c5: 0x2785, # DINGBAT CIRCLED SANS-SERIF DIGIT SIX - 0x00c6: 0x2786, # DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN - 0x00c7: 0x2787, # DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT - 0x00c8: 0x2788, # DINGBAT CIRCLED SANS-SERIF DIGIT NINE - 0x00c9: 0x2789, # DINGBAT CIRCLED SANS-SERIF NUMBER TEN - 0x00ca: 0x278a, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE - 0x00cb: 0x278b, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO - 0x00cc: 0x278c, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE - 0x00cd: 0x278d, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR - 0x00ce: 0x278e, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE - 0x00cf: 0x278f, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX - 0x00d0: 0x2790, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN - 0x00d1: 0x2791, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT - 0x00d2: 0x2792, # DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE - 0x00d3: 0x2793, # DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN - 0x00d4: 0x2794, # HEAVY WIDE-HEADED RIGHTWARDS ARROW - 0x00d5: 0x2192, # RIGHTWARDS ARROW - 0x00d6: 0x2194, # LEFT RIGHT ARROW - 0x00d7: 0x2195, # UP DOWN ARROW - 0x00d8: 0x2798, # HEAVY SOUTH EAST ARROW - 0x00d9: 0x2799, # HEAVY RIGHTWARDS ARROW - 0x00da: 0x279a, # HEAVY NORTH EAST ARROW - 0x00db: 0x279b, # DRAFTING POINT RIGHTWARDS ARROW - 0x00dc: 0x279c, # HEAVY ROUND-TIPPED RIGHTWARDS ARROW - 0x00dd: 0x279d, # TRIANGLE-HEADED RIGHTWARDS ARROW - 0x00de: 0x279e, # HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW - 0x00df: 0x279f, # DASHED TRIANGLE-HEADED RIGHTWARDS ARROW - 0x00e0: 0x27a0, # HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW - 0x00e1: 0x27a1, # BLACK RIGHTWARDS ARROW - 0x00e2: 0x27a2, # THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD - 0x00e3: 0x27a3, # THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD - 0x00e4: 0x27a4, # BLACK RIGHTWARDS ARROWHEAD - 0x00e5: 0x27a5, # HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW - 0x00e6: 0x27a6, # HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW - 0x00e7: 0x27a7, # SQUAT BLACK RIGHTWARDS ARROW - 0x00e8: 0x27a8, # HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW - 0x00e9: 0x27a9, # RIGHT-SHADED WHITE RIGHTWARDS ARROW - 0x00ea: 0x27aa, # LEFT-SHADED WHITE RIGHTWARDS ARROW - 0x00eb: 0x27ab, # BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW - 0x00ec: 0x27ac, # FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW - 0x00ed: 0x27ad, # HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW - 0x00ee: 0x27ae, # HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW - 0x00ef: 0x27af, # NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW - 0x00f0: None, # UNDEFINED - 0x00f1: 0x27b1, # NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW - 0x00f2: 0x27b2, # CIRCLED HEAVY WHITE RIGHTWARDS ARROW - 0x00f3: 0x27b3, # WHITE-FEATHERED RIGHTWARDS ARROW - 0x00f4: 0x27b4, # BLACK-FEATHERED SOUTH EAST ARROW - 0x00f5: 0x27b5, # BLACK-FEATHERED RIGHTWARDS ARROW - 0x00f6: 0x27b6, # BLACK-FEATHERED NORTH EAST ARROW - 0x00f7: 0x27b7, # HEAVY BLACK-FEATHERED SOUTH EAST ARROW - 0x00f8: 0x27b8, # HEAVY BLACK-FEATHERED RIGHTWARDS ARROW - 0x00f9: 0x27b9, # HEAVY BLACK-FEATHERED NORTH EAST ARROW - 0x00fa: 0x27ba, # TEARDROP-BARBED RIGHTWARDS ARROW - 0x00fb: 0x27bb, # HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW - 0x00fc: 0x27bc, # WEDGE-TAILED RIGHTWARDS ARROW - 0x00fd: 0x27bd, # HEAVY WEDGE-TAILED RIGHTWARDS ARROW - 0x00fe: 0x27be, # OPEN-OUTLINED RIGHTWARDS ARROW - 0x00ff: None, # UNDEFINED - },None), - 'pdfdoc':({ - 0x007f: None, # UNDEFINED - 0x0080: 0x2022, # BULLET - 0x0081: 0x2020, # DAGGER - 0x0082: 0x2021, # DOUBLE DAGGER - 0x0083: 0x2026, # HORIZONTAL ELLIPSIS - 0x0084: 0x2014, # EM DASH - 0x0085: 0x2013, # EN DASH - 0x0086: 0x0192, # LATIN SMALL LETTER F WITH HOOK - 0x0087: 0x2044, # FRACTION SLASH - 0x0088: 0x2039, # SINGLE LEFT-POINTING ANGLE QUOTATION MARK - 0x0089: 0x203a, # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - 0x008a: 0x2212, # MINUS SIGN - 0x008b: 0x2030, # PER MILLE SIGN - 0x008c: 0x201e, # DOUBLE LOW-9 QUOTATION MARK - 0x008d: 0x201c, # LEFT DOUBLE QUOTATION MARK - 0x008e: 0x201d, # RIGHT DOUBLE QUOTATION MARK - 0x008f: 0x2018, # LEFT SINGLE QUOTATION MARK - 0x0090: 0x2019, # RIGHT SINGLE QUOTATION MARK - 0x0091: 0x201a, # SINGLE LOW-9 QUOTATION MARK - 0x0092: 0x2122, # TRADE MARK SIGN - 0x0093: 0xfb01, # LATIN SMALL LIGATURE FI - 0x0094: 0xfb02, # LATIN SMALL LIGATURE FL - 0x0095: 0x0141, # LATIN CAPITAL LETTER L WITH STROKE - 0x0096: 0x0152, # LATIN CAPITAL LIGATURE OE - 0x0097: 0x0160, # LATIN CAPITAL LETTER S WITH CARON - 0x0098: 0x0178, # LATIN CAPITAL LETTER Y WITH DIAERESIS - 0x0099: 0x017d, # LATIN CAPITAL LETTER Z WITH CARON - 0x009a: 0x0131, # LATIN SMALL LETTER DOTLESS I - 0x009b: 0x0142, # LATIN SMALL LETTER L WITH STROKE - 0x009c: 0x0153, # LATIN SMALL LIGATURE OE - 0x009d: 0x0161, # LATIN SMALL LETTER S WITH CARON - 0x009e: 0x017e, # LATIN SMALL LETTER Z WITH CARON - 0x009f: None, # UNDEFINED - 0x00a0: 0x20ac, # EURO SIGN - 0x00ad: None, # UNDEFINED - 24: 0x02d8, #breve - 25: 0x02c7, #caron - 26: 0x02c6, #circumflex - 27: 0x02d9, #dotaccent - 28: 0x02dd, #hungarumlaut - 29: 0x02db, #ogonek - 30: 0x02da, #ring - 31: 0x02dc, #tilde - },None), - 'macexpert':({ - 0x0021: 0xf721, # [unknown unicode name for exclamsmall] - 0x0022: 0xf6f8, # [unknown unicode name for Hungarumlautsmall] - 0x0023: 0xf7a2, # [unknown unicode name for centoldstyle] - 0x0024: 0xf724, # [unknown unicode name for dollaroldstyle] - 0x0025: 0xf6e4, # [unknown unicode name for dollarsuperior] - 0x0026: 0xf726, # [unknown unicode name for ampersandsmall] - 0x0027: 0xf7b4, # [unknown unicode name for Acutesmall] - 0x0028: 0x207d, # SUPERSCRIPT LEFT PARENTHESIS - 0x0029: 0x207e, # SUPERSCRIPT RIGHT PARENTHESIS - 0x002a: 0x2025, # TWO DOT LEADER - 0x002b: 0x2024, # ONE DOT LEADER - 0x002f: 0x2044, # FRACTION SLASH - 0x0030: 0xf730, # [unknown unicode name for zerooldstyle] - 0x0031: 0xf731, # [unknown unicode name for oneoldstyle] - 0x0032: 0xf732, # [unknown unicode name for twooldstyle] - 0x0033: 0xf733, # [unknown unicode name for threeoldstyle] - 0x0034: 0xf734, # [unknown unicode name for fouroldstyle] - 0x0035: 0xf735, # [unknown unicode name for fiveoldstyle] - 0x0036: 0xf736, # [unknown unicode name for sixoldstyle] - 0x0037: 0xf737, # [unknown unicode name for sevenoldstyle] - 0x0038: 0xf738, # [unknown unicode name for eightoldstyle] - 0x0039: 0xf739, # [unknown unicode name for nineoldstyle] - 0x003c: None, # UNDEFINED - 0x003d: 0xf6de, # [unknown unicode name for threequartersemdash] - 0x003e: None, # UNDEFINED - 0x003f: 0xf73f, # [unknown unicode name for questionsmall] - 0x0040: None, # UNDEFINED - 0x0041: None, # UNDEFINED - 0x0042: None, # UNDEFINED - 0x0043: None, # UNDEFINED - 0x0044: 0xf7f0, # [unknown unicode name for Ethsmall] - 0x0045: None, # UNDEFINED - 0x0046: None, # UNDEFINED - 0x0047: 0x00bc, # VULGAR FRACTION ONE QUARTER - 0x0048: 0x00bd, # VULGAR FRACTION ONE HALF - 0x0049: 0x00be, # VULGAR FRACTION THREE QUARTERS - 0x004a: 0x215b, # VULGAR FRACTION ONE EIGHTH - 0x004b: 0x215c, # VULGAR FRACTION THREE EIGHTHS - 0x004c: 0x215d, # VULGAR FRACTION FIVE EIGHTHS - 0x004d: 0x215e, # VULGAR FRACTION SEVEN EIGHTHS - 0x004e: 0x2153, # VULGAR FRACTION ONE THIRD - 0x004f: 0x2154, # VULGAR FRACTION TWO THIRDS - 0x0050: None, # UNDEFINED - 0x0051: None, # UNDEFINED - 0x0052: None, # UNDEFINED - 0x0053: None, # UNDEFINED - 0x0054: None, # UNDEFINED - 0x0055: None, # UNDEFINED - 0x0056: 0xfb00, # LATIN SMALL LIGATURE FF - 0x0057: 0xfb01, # LATIN SMALL LIGATURE FI - 0x0058: 0xfb02, # LATIN SMALL LIGATURE FL - 0x0059: 0xfb03, # LATIN SMALL LIGATURE FFI - 0x005a: 0xfb04, # LATIN SMALL LIGATURE FFL - 0x005b: 0x208d, # SUBSCRIPT LEFT PARENTHESIS - 0x005c: None, # UNDEFINED - 0x005d: 0x208e, # SUBSCRIPT RIGHT PARENTHESIS - 0x005e: 0xf6f6, # [unknown unicode name for Circumflexsmall] - 0x005f: 0xf6e5, # [unknown unicode name for hypheninferior] - 0x0060: 0xf760, # [unknown unicode name for Gravesmall] - 0x0061: 0xf761, # [unknown unicode name for Asmall] - 0x0062: 0xf762, # [unknown unicode name for Bsmall] - 0x0063: 0xf763, # [unknown unicode name for Csmall] - 0x0064: 0xf764, # [unknown unicode name for Dsmall] - 0x0065: 0xf765, # [unknown unicode name for Esmall] - 0x0066: 0xf766, # [unknown unicode name for Fsmall] - 0x0067: 0xf767, # [unknown unicode name for Gsmall] - 0x0068: 0xf768, # [unknown unicode name for Hsmall] - 0x0069: 0xf769, # [unknown unicode name for Ismall] - 0x006a: 0xf76a, # [unknown unicode name for Jsmall] - 0x006b: 0xf76b, # [unknown unicode name for Ksmall] - 0x006c: 0xf76c, # [unknown unicode name for Lsmall] - 0x006d: 0xf76d, # [unknown unicode name for Msmall] - 0x006e: 0xf76e, # [unknown unicode name for Nsmall] - 0x006f: 0xf76f, # [unknown unicode name for Osmall] - 0x0070: 0xf770, # [unknown unicode name for Psmall] - 0x0071: 0xf771, # [unknown unicode name for Qsmall] - 0x0072: 0xf772, # [unknown unicode name for Rsmall] - 0x0073: 0xf773, # [unknown unicode name for Ssmall] - 0x0074: 0xf774, # [unknown unicode name for Tsmall] - 0x0075: 0xf775, # [unknown unicode name for Usmall] - 0x0076: 0xf776, # [unknown unicode name for Vsmall] - 0x0077: 0xf777, # [unknown unicode name for Wsmall] - 0x0078: 0xf778, # [unknown unicode name for Xsmall] - 0x0079: 0xf779, # [unknown unicode name for Ysmall] - 0x007a: 0xf77a, # [unknown unicode name for Zsmall] - 0x007b: 0x20a1, # COLON SIGN - 0x007c: 0xf6dc, # [unknown unicode name for onefitted] - 0x007d: 0xf6dd, # [unknown unicode name for rupiah] - 0x007e: 0xf6fe, # [unknown unicode name for Tildesmall] - 0x007f: None, # UNDEFINED - 0x0080: None, # UNDEFINED - 0x0081: 0xf6e9, # [unknown unicode name for asuperior] - 0x0082: 0xf6e0, # [unknown unicode name for centsuperior] - 0x0083: None, # UNDEFINED - 0x0084: None, # UNDEFINED - 0x0085: None, # UNDEFINED - 0x0086: None, # UNDEFINED - 0x0087: 0xf7e1, # [unknown unicode name for Aacutesmall] - 0x0088: 0xf7e0, # [unknown unicode name for Agravesmall] - 0x0089: 0xf7e2, # [unknown unicode name for Acircumflexsmall] - 0x008a: 0xf7e4, # [unknown unicode name for Adieresissmall] - 0x008b: 0xf7e3, # [unknown unicode name for Atildesmall] - 0x008c: 0xf7e5, # [unknown unicode name for Aringsmall] - 0x008d: 0xf7e7, # [unknown unicode name for Ccedillasmall] - 0x008e: 0xf7e9, # [unknown unicode name for Eacutesmall] - 0x008f: 0xf7e8, # [unknown unicode name for Egravesmall] - 0x0090: 0xf7ea, # [unknown unicode name for Ecircumflexsmall] - 0x0091: 0xf7eb, # [unknown unicode name for Edieresissmall] - 0x0092: 0xf7ed, # [unknown unicode name for Iacutesmall] - 0x0093: 0xf7ec, # [unknown unicode name for Igravesmall] - 0x0094: 0xf7ee, # [unknown unicode name for Icircumflexsmall] - 0x0095: 0xf7ef, # [unknown unicode name for Idieresissmall] - 0x0096: 0xf7f1, # [unknown unicode name for Ntildesmall] - 0x0097: 0xf7f3, # [unknown unicode name for Oacutesmall] - 0x0098: 0xf7f2, # [unknown unicode name for Ogravesmall] - 0x0099: 0xf7f4, # [unknown unicode name for Ocircumflexsmall] - 0x009a: 0xf7f6, # [unknown unicode name for Odieresissmall] - 0x009b: 0xf7f5, # [unknown unicode name for Otildesmall] - 0x009c: 0xf7fa, # [unknown unicode name for Uacutesmall] - 0x009d: 0xf7f9, # [unknown unicode name for Ugravesmall] - 0x009e: 0xf7fb, # [unknown unicode name for Ucircumflexsmall] - 0x009f: 0xf7fc, # [unknown unicode name for Udieresissmall] - 0x00a0: None, # UNDEFINED - 0x00a1: 0x2078, # SUPERSCRIPT EIGHT - 0x00a2: 0x2084, # SUBSCRIPT FOUR - 0x00a3: 0x2083, # SUBSCRIPT THREE - 0x00a4: 0x2086, # SUBSCRIPT SIX - 0x00a5: 0x2088, # SUBSCRIPT EIGHT - 0x00a6: 0x2087, # SUBSCRIPT SEVEN - 0x00a7: 0xf6fd, # [unknown unicode name for Scaronsmall] - 0x00a8: None, # UNDEFINED - 0x00a9: 0xf6df, # [unknown unicode name for centinferior] - 0x00aa: 0x2082, # SUBSCRIPT TWO - 0x00ab: None, # UNDEFINED - 0x00ac: 0xf7a8, # [unknown unicode name for Dieresissmall] - 0x00ad: None, # UNDEFINED - 0x00ae: 0xf6f5, # [unknown unicode name for Caronsmall] - 0x00af: 0xf6f0, # [unknown unicode name for osuperior] - 0x00b0: 0x2085, # SUBSCRIPT FIVE - 0x00b1: None, # UNDEFINED - 0x00b2: 0xf6e1, # [unknown unicode name for commainferior] - 0x00b3: 0xf6e7, # [unknown unicode name for periodinferior] - 0x00b4: 0xf7fd, # [unknown unicode name for Yacutesmall] - 0x00b5: None, # UNDEFINED - 0x00b6: 0xf6e3, # [unknown unicode name for dollarinferior] - 0x00b7: None, # UNDEFINED - 0x00b8: None, # UNDEFINED - 0x00b9: 0xf7fe, # [unknown unicode name for Thornsmall] - 0x00ba: None, # UNDEFINED - 0x00bb: 0x2089, # SUBSCRIPT NINE - 0x00bc: 0x2080, # SUBSCRIPT ZERO - 0x00bd: 0xf6ff, # [unknown unicode name for Zcaronsmall] - 0x00be: 0xf7e6, # [unknown unicode name for AEsmall] - 0x00bf: 0xf7f8, # [unknown unicode name for Oslashsmall] - 0x00c0: 0xf7bf, # [unknown unicode name for questiondownsmall] - 0x00c1: 0x2081, # SUBSCRIPT ONE - 0x00c2: 0xf6f9, # [unknown unicode name for Lslashsmall] - 0x00c3: None, # UNDEFINED - 0x00c4: None, # UNDEFINED - 0x00c5: None, # UNDEFINED - 0x00c6: None, # UNDEFINED - 0x00c7: None, # UNDEFINED - 0x00c8: None, # UNDEFINED - 0x00c9: 0xf7b8, # [unknown unicode name for Cedillasmall] - 0x00ca: None, # UNDEFINED - 0x00cb: None, # UNDEFINED - 0x00cc: None, # UNDEFINED - 0x00cd: None, # UNDEFINED - 0x00ce: None, # UNDEFINED - 0x00cf: 0xf6fa, # [unknown unicode name for OEsmall] - 0x00d0: 0x2012, # FIGURE DASH - 0x00d1: 0xf6e6, # [unknown unicode name for hyphensuperior] - 0x00d2: None, # UNDEFINED - 0x00d3: None, # UNDEFINED - 0x00d4: None, # UNDEFINED - 0x00d5: None, # UNDEFINED - 0x00d6: 0xf7a1, # [unknown unicode name for exclamdownsmall] - 0x00d7: None, # UNDEFINED - 0x00d8: 0xf7ff, # [unknown unicode name for Ydieresissmall] - 0x00d9: None, # UNDEFINED - 0x00da: 0x00b9, # SUPERSCRIPT ONE - 0x00db: 0x00b2, # SUPERSCRIPT TWO - 0x00dc: 0x00b3, # SUPERSCRIPT THREE - 0x00dd: 0x2074, # SUPERSCRIPT FOUR - 0x00de: 0x2075, # SUPERSCRIPT FIVE - 0x00df: 0x2076, # SUPERSCRIPT SIX - 0x00e0: 0x2077, # SUPERSCRIPT SEVEN - 0x00e1: 0x2079, # SUPERSCRIPT NINE - 0x00e2: 0x2070, # SUPERSCRIPT ZERO - 0x00e3: None, # UNDEFINED - 0x00e4: 0xf6ec, # [unknown unicode name for esuperior] - 0x00e5: 0xf6f1, # [unknown unicode name for rsuperior] - 0x00e6: 0xf6f3, # [unknown unicode name for tsuperior] - 0x00e7: None, # UNDEFINED - 0x00e8: None, # UNDEFINED - 0x00e9: 0xf6ed, # [unknown unicode name for isuperior] - 0x00ea: 0xf6f2, # [unknown unicode name for ssuperior] - 0x00eb: 0xf6eb, # [unknown unicode name for dsuperior] - 0x00ec: None, # UNDEFINED - 0x00ed: None, # UNDEFINED - 0x00ee: None, # UNDEFINED - 0x00ef: None, # UNDEFINED - 0x00f0: None, # UNDEFINED - 0x00f1: 0xf6ee, # [unknown unicode name for lsuperior] - 0x00f2: 0xf6fb, # [unknown unicode name for Ogoneksmall] - 0x00f3: 0xf6f4, # [unknown unicode name for Brevesmall] - 0x00f4: 0xf7af, # [unknown unicode name for Macronsmall] - 0x00f5: 0xf6ea, # [unknown unicode name for bsuperior] - 0x00f6: 0x207f, # SUPERSCRIPT LATIN SMALL LETTER N - 0x00f7: 0xf6ef, # [unknown unicode name for msuperior] - 0x00f8: 0xf6e2, # [unknown unicode name for commasuperior] - 0x00f9: 0xf6e8, # [unknown unicode name for periodsuperior] - 0x00fa: 0xf6f7, # [unknown unicode name for Dotaccentsmall] - 0x00fb: 0xf6fc, # [unknown unicode name for Ringsmall] - 0x00fc: None, # UNDEFINED - 0x00fd: None, # UNDEFINED - 0x00fe: None, # UNDEFINED - 0x00ff: None, # UNDEFINED - },None), - } - #for k,v in __rl_codecs_data.items(): - # __rl_codecs_data[k+'enc'] = __rl_codecs_data[k+'encoding'] = v - #del k,v - - def __init__(self): - raise NotImplementedError - - def _256_exception_codec((exceptions,rexceptions)): - import codecs - decoding_map = codecs.make_identity_dict(xrange(32,256)) - decoding_map.update(exceptions) - encoding_map = codecs.make_encoding_map(decoding_map) - if rexceptions: encoding_map.update(rexceptions) - ### Codec APIs - class Codec(codecs.Codec): - def encode(self,input,errors='strict',charmap_encode=codecs.charmap_encode,encoding_map=encoding_map): - return charmap_encode(input,errors,encoding_map) - - def decode(self,input,errors='strict',charmap_decode=codecs.charmap_decode,decoding_map=decoding_map): - return charmap_decode(input,errors,decoding_map) - - class StreamWriter(Codec,codecs.StreamWriter): - pass - - class StreamReader(Codec,codecs.StreamReader): - pass - C = Codec() - return (C.encode,C.decode,StreamReader,StreamWriter) - _256_exception_codec=staticmethod(_256_exception_codec) - - __rl_codecs_cache = {} - - def __rl_codecs(name,cache=__rl_codecs_cache,data=__rl_codecs_data): - try: - return cache[name] - except KeyError: - cache[name] = c = RL_Codecs._256_exception_codec( - data[name]) - return c - __rl_codecs=staticmethod(__rl_codecs) - - def _rl_codecs(name): - name = name.lower() - from pdfmetrics import standardEncodings - for e in standardEncodings: - e = e[:-8].lower() - if name.startswith(e): return RL_Codecs.__rl_codecs(e) - return None - _rl_codecs=staticmethod(_rl_codecs) - - def register(): - import codecs - codecs.register(RL_Codecs._rl_codecs) - register=staticmethod(register) diff --git a/bin/reportlab/pdfbase/ttfonts.py b/bin/reportlab/pdfbase/ttfonts.py deleted file mode 100644 index c6f9c922dd4..00000000000 --- a/bin/reportlab/pdfbase/ttfonts.py +++ /dev/null @@ -1,1113 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfbase/ttfonts.py -"""TrueType font support - -This defines classes to represent TrueType fonts. They know how to calculate -their own width and how to write themselves into PDF files. They support -subsetting and embedding and can represent all 16-bit Unicode characters. - -Note on dynamic fonts ---------------------- - -Usually a Font in ReportLab corresponds to a fixed set of PDF objects (Font, -FontDescriptor, Encoding). But with dynamic font subsetting a single TTFont -will result in a number of Font/FontDescriptor/Encoding object sets, and the -contents of those will depend on the actual characters used for printing. - -To support dynamic font subsetting a concept of "dynamic font" was introduced. -Dynamic Fonts have a _dynamicFont attribute set to 1. Since other Font object -may lack a this attribute, you should use constructs like - - if getattr(font, '_dynamicFont', 0): - # dynamic font - else: - # traditional static font - -Dynamic fonts have the following additional functions: - - def splitString(self, text, doc): - '''Splits text into a number of chunks, each of which belongs to a - single subset. Returns a list of tuples (subset, string). Use - subset numbers with getSubsetInternalName. Doc is used to identify - a document so that different documents may have different dynamically - constructed subsets.''' - - def getSubsetInternalName(self, subset, doc): - '''Returns the name of a PDF Font object corresponding to a given - subset of this dynamic font. Use this function instead of - PDFDocument.getInternalFontName.''' - -You must never call PDFDocument.getInternalFontName for dynamic fonts. - -If you have a traditional static font, mapping to PDF text output operators -is simple: - - '%s 14 Tf (%s) Tj' % (getInternalFontName(psfontname), text) - -If you have a dynamic font, use this instead: - - for subset, chunk in font.splitString(text, doc): - '%s 14 Tf (%s) Tj' % (font.getSubsetInternalName(subset, doc), chunk) - -(Tf is a font setting operator and Tj is a text ouput operator. You should -also escape invalid characters in Tj argument, see TextObject._formatText. -Oh, and that 14 up there is font size.) - -Canvas and TextObject have special support for dynamic fonts. -""" - -__version__ = '$Id: ttfonts.py 2865 2006-05-15 16:37:44Z rgbecker $' - -import string -from types import StringType, UnicodeType -from struct import pack, unpack -from cStringIO import StringIO -from reportlab.pdfbase import pdfmetrics, pdfdoc - -def _L2U32(L): - return unpack('l',pack('L',L))[0] - -class TTFError(pdfdoc.PDFError): - "TrueType font exception" - pass - - -def SUBSETN(n,table=string.maketrans('0123456789','ABCDEFGHIJ')): - return ('%6.6d'%n).translate(table) -# -# Helpers -# - -from codecs import utf_8_encode, utf_8_decode, latin_1_decode -parse_utf8=lambda x, decode=utf_8_decode: map(ord,decode(x)[0]) -parse_latin1 = lambda x, decode=latin_1_decode: map(ord,decode(x)[0]) -def latin1_to_utf8(text): - "helper to convert when needed from latin input" - return utf_8_encode(latin_1_decode(text)[0])[0] - -def makeToUnicodeCMap(fontname, subset): - """Creates a ToUnicode CMap for a given subset. See Adobe - _PDF_Reference (ISBN 0-201-75839-3) for more information.""" - cmap = [ - "/CIDInit /ProcSet findresource begin", - "12 dict begin", - "begincmap", - "/CIDSystemInfo", - "<< /Registry (%s)" % fontname, - "/Ordering (%s)" % fontname, - "/Supplement 0", - ">> def", - "/CMapName /%s def" % fontname, - "/CMapType 2 def", - "1 begincodespacerange", - "<00> <%02X>" % (len(subset) - 1), - "endcodespacerange", - "%d beginbfchar" % len(subset) - ] + map(lambda n, subset=subset: "<%02X> <%04X>" % (n, subset[n]), - xrange(len(subset))) + [ - "endbfchar", - "endcmap", - "CMapName currentdict /CMap defineresource pop", - "end", - "end" - ] - return string.join(cmap, "\n") - -def splice(stream, offset, value): - """Splices the given value into stream at the given offset and - returns the resulting stream (the original is unchanged)""" - return stream[:offset] + value + stream[offset + len(value):] - -def _set_ushort(stream, offset, value): - """Writes the given unsigned short value into stream at the given - offset and returns the resulting stream (the original is unchanged)""" - return splice(stream, offset, pack(">H", value)) - -import sys -try: - import _rl_accel -except ImportError: - try: - from reportlab.lib import _rl_accel - except ImportError: - _rl_accel = None - -try: - hex32 = _rl_accel.hex32 -except: - def hex32(i): - return '0X%8.8X' % (long(i)&0xFFFFFFFFL) -try: - add32 = _rl_accel.add32 -except: - if sys.hexversion>=0x02030000: - def add32(x, y): - "Calculate (x + y) modulo 2**32" - return _L2U32((long(x)+y) & 0xffffffffL) - else: - def add32(x, y): - "Calculate (x + y) modulo 2**32" - lo = (x & 0xFFFF) + (y & 0xFFFF) - hi = (x >> 16) + (y >> 16) + (lo >> 16) - return (hi << 16) | (lo & 0xFFFF) - -try: - calcChecksum = _rl_accel.calcChecksum -except: - def calcChecksum(data): - """Calculates PDF-style checksums""" - if len(data)&3: data = data + (4-(len(data)&3))*"\0" - sum = 0 - for n in unpack(">%dl" % (len(data)>>2), data): - sum = add32(sum,n) - return sum -del _rl_accel, sys -# -# TrueType font handling -# - -GF_ARG_1_AND_2_ARE_WORDS = 1 << 0 -GF_ARGS_ARE_XY_VALUES = 1 << 1 -GF_ROUND_XY_TO_GRID = 1 << 2 -GF_WE_HAVE_A_SCALE = 1 << 3 -GF_RESERVED = 1 << 4 -GF_MORE_COMPONENTS = 1 << 5 -GF_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6 -GF_WE_HAVE_A_TWO_BY_TWO = 1 << 7 -GF_WE_HAVE_INSTRUCTIONS = 1 << 8 -GF_USE_MY_METRICS = 1 << 9 -GF_OVERLAP_COMPOUND = 1 << 10 -GF_SCALED_COMPONENT_OFFSET = 1 << 11 -GF_UNSCALED_COMPONENT_OFFSET = 1 << 12 - -def TTFOpenFile(fn): - '''Opens a TTF file possibly after searching TTFSearchPath - returns (filename,file) - ''' - from reportlab.lib.utils import rl_isfile, open_for_read - try: - f = open_for_read(fn,'rb') - return fn, f - except IOError: - import os - if not os.path.isabs(fn): - from reportlab import rl_config - for D in rl_config.TTFSearchPath: - tfn = os.path.join(D,fn) - if rl_isfile(tfn): - f = open_for_read(tfn,'rb') - return tfn, f - raise TTFError('Can\'t open file "%s"' % fn) - -class TTFontParser: - "Basic TTF file parser" - ttfVersions = (0x00010000,0x74727565,0x74746366) - ttcVersions = (0x00010000,0x00020000) - fileKind='TTF' - - def __init__(self, file, validate=0,subfontIndex=0): - """Loads and parses a TrueType font file. file can be a filename or a - file object. If validate is set to a false values, skips checksum - validation. This can save time, especially if the font is large. - """ - self.validate = validate - self.readFile(file) - isCollection = self.readHeader() - if isCollection: - self.readTTCHeader() - self.getSubfont(subfontIndex) - else: - if self.validate: self.checksumFile() - self.readTableDirectory() - self.subfontNameX = '' - - def readTTCHeader(self): - self.ttcVersion = self.read_ulong() - self.fileKind = 'TTC' - self.ttfVersions = self.ttfVersions[:-1] - if self.ttcVersion not in self.ttcVersions: - raise TTFError('"%s" is not a %s file: can\'t read version 0x%8.8x' %(self.filename,self.fileKind,self.ttcVersion)) - self.numSubfonts = self.read_ulong() - self.subfontOffsets = [] - a = self.subfontOffsets.append - for i in xrange(self.numSubfonts): - a(self.read_ulong()) - - def getSubfont(self,subfontIndex): - if self.fileKind!='TTC': - raise TTFError('"%s" is not a TTC file: use this method' % (self.filename,self.fileKind)) - try: - pos = self.subfontOffsets[subfontIndex] - except IndexError: - raise TTFError('TTC file "%s": bad subfontIndex %s not in [0,%d]' % (self.filename,subfontIndex,self.numSubfonts-1)) - self.seek(pos) - self.readHeader() - self.readTableDirectory() - self.subfontNameX = '-'+str(subfontIndex) - - def readTableDirectory(self): - try: - self.numTables = self.read_ushort() - self.searchRange = self.read_ushort() - self.entrySelector = self.read_ushort() - self.rangeShift = self.read_ushort() - - # Read table directory - self.table = {} - self.tables = [] - for n in xrange(self.numTables): - record = {} - record['tag'] = self.read_tag() - record['checksum'] = self.read_ulong() - record['offset'] = self.read_ulong() - record['length'] = self.read_ulong() - self.tables.append(record) - self.table[record['tag']] = record - except: - raise TTFError('Corrupt %s file "%s" cannot read Table Directory' % (self.fileKind, self.filename)) - if self.validate: self.checksumTables() - - def readHeader(self): - '''read the sfnt header at the current position''' - try: - self.version = version = self.read_ulong() - except: - raise TTFError('"%s" is not a %s file: can\'t read version' %(self.filename,self.fileKind)) - - if version==0x4F54544F: - raise TTFError('%s file "%s": postscript outlines are not supported'%(self.fileKind,self.filename)) - - if version not in self.ttfVersions: - raise TTFError('Not a TrueType font: version=0x%8.8X' % version) - return version==self.ttfVersions[-1] - - def readFile(self,file): - if type(file) is StringType: - self.filename, file = TTFOpenFile(file) - else: - self.filename = '(ttf)' - - self._ttf_data = file.read() - self._pos = 0 - - def checksumTables(self): - # Check the checksums for all tables - for t in self.tables: - table = self.get_chunk(t['offset'], t['length']) - checkSum = calcChecksum(table) - if t['tag'] == 'head': - adjustment = unpack('>l', table[8:8+4])[0] - checkSum = add32(checkSum, -adjustment) - if t['checksum'] != checkSum: - raise TTFError('TTF file "%s": invalid checksum %s table: %s' % (self.filename,hex32(checkSum),t['tag'])) - - def checksumFile(self): - # Check the checksums for the whole file - checkSum = calcChecksum(self._ttf_data) - if add32(_L2U32(0xB1B0AFBAL), -checkSum) != 0: - raise TTFError('TTF file "%s": invalid checksum %s len: %d &3: %d' % (self.filename,hex32(checkSum),len(self._ttf_data),(len(self._ttf_data)&3))) - - def get_table_pos(self, tag): - "Returns the offset and size of a given TTF table." - offset = self.table[tag]['offset'] - length = self.table[tag]['length'] - return (offset, length) - - def seek(self, pos): - "Moves read pointer to a given offset in file." - self._pos = pos - - def skip(self, delta): - "Skip the given number of bytes." - self._pos = self._pos + delta - - def seek_table(self, tag, offset_in_table = 0): - """Moves read pointer to the given offset within a given table and - returns absolute offset of that position in the file.""" - self._pos = self.get_table_pos(tag)[0] + offset_in_table - return self._pos - - def read_tag(self): - "Read a 4-character tag" - self._pos += 4 - return self._ttf_data[self._pos - 4:self._pos] - - def read_ushort(self): - "Reads an unsigned short" - self._pos += 2 - return unpack('>H',self._ttf_data[self._pos-2:self._pos])[0] - - def read_ulong(self): - "Reads an unsigned long" - self._pos += 4 - return unpack('>l',self._ttf_data[self._pos - 4:self._pos])[0] - - def read_short(self): - "Reads a signed short" - self._pos += 2 - return unpack('>h',self._ttf_data[self._pos-2:self._pos])[0] - - def get_ushort(self, pos): - "Return an unsigned short at given position" - return unpack('>H',self._ttf_data[pos:pos+2])[0] - - def get_ulong(self, pos): - "Return an unsigned long at given position" - return unpack('>l',self._ttf_data[pos:pos+4])[0] - - def get_chunk(self, pos, length): - "Return a chunk of raw data at given position" - return self._ttf_data[pos:pos+length] - - def get_table(self, tag): - "Return the given TTF table" - pos, length = self.get_table_pos(tag) - return self._ttf_data[pos:pos+length] - -class TTFontMaker: - "Basic TTF file generator" - - def __init__(self): - "Initializes the generator." - self.tables = {} - - def add(self, tag, data): - "Adds a table to the TTF file." - if tag == 'head': - data = splice(data, 8, '\0\0\0\0') - self.tables[tag] = data - - def makeStream(self): - "Finishes the generation and returns the TTF file as a string" - stm = StringIO() - - numTables = len(self.tables) - searchRange = 1 - entrySelector = 0 - while searchRange * 2 <= numTables: - searchRange = searchRange * 2 - entrySelector = entrySelector + 1 - searchRange = searchRange * 16 - rangeShift = numTables * 16 - searchRange - - # Header - stm.write(pack(">lHHHH", 0x00010000, numTables, searchRange, - entrySelector, rangeShift)) - - # Table directory - tables = self.tables.items() - tables.sort() # XXX is this the correct order? - offset = 12 + numTables * 16 - for tag, data in tables: - if tag == 'head': - head_start = offset - checksum = calcChecksum(data) - stm.write(tag) - stm.write(pack(">LLL", checksum, offset, len(data))) - paddedLength = (len(data)+3)&~3 - offset = offset + paddedLength - - # Table data - for tag, data in tables: - data = data + "\0\0\0" - stm.write(data[:len(data)&~3]) - - checksum = calcChecksum(stm.getvalue()) - checksum = add32(_L2U32(0xB1B0AFBAL), -checksum) - stm.seek(head_start + 8) - stm.write(pack('>L', checksum)) - - return stm.getvalue() - -class TTFontFile(TTFontParser): - "TTF file parser and generator" - - def __init__(self, file, charInfo=1, validate=0,subfontIndex=0): - """Loads and parses a TrueType font file. - - file can be a filename or a file object. If validate is set to a false - values, skips checksum validation. This can save time, especially if - the font is large. See TTFontFile.extractInfo for more information. - """ - TTFontParser.__init__(self, file, validate=validate,subfontIndex=subfontIndex) - self.extractInfo(charInfo) - - def extractInfo(self, charInfo=1): - """Extract typographic information from the loaded font file. - - The following attributes will be set: - name - PostScript font name - flags - Font flags - ascent - Typographic ascender in 1/1000ths of a point - descent - Typographic descender in 1/1000ths of a point - capHeight - Cap height in 1/1000ths of a point (0 if not available) - bbox - Glyph bounding box [l,t,r,b] in 1/1000ths of a point - italicAngle - Italic angle in degrees ccw - stemV - stem weight in 1/1000ths of a point (approximate) - If charInfo is true, the following will also be set: - defaultWidth - default glyph width in 1/1000ths of a point - charWidths - dictionary of character widths for every supported - UCS character code - - This will only work if the font has a Unicode cmap (platform 3, - encoding 1, format 4 or platform 0 any encoding format 4). Setting - charInfo to false avoids this requirement. - """ - # name - Naming table - name_offset = self.seek_table("name") - format = self.read_ushort() - if format != 0: - raise TTFError, "Unknown name table format (%d)" % format - numRecords = self.read_ushort() - string_data_offset = name_offset + self.read_ushort() - names = {1:None,2:None,3:None,4:None,6:None} - K = names.keys() - nameCount = len(names) - for i in xrange(numRecords): - platformId = self.read_ushort() - encodingId = self.read_ushort() - languageId = self.read_ushort() - nameId = self.read_ushort() - length = self.read_ushort() - offset = self.read_ushort() - if nameId not in K: continue - N = None - if platformId == 3 and encodingId == 1 and languageId == 0x409: # Microsoft, Unicode, US English, PS Name - opos = self._pos - try: - self.seek(string_data_offset + offset) - if length % 2 != 0: - raise TTFError, "PostScript name is UTF-16BE string of odd length" - length /= 2 - N = [] - A = N.append - while length > 0: - char = self.read_ushort() - A(chr(char)) - length -= 1 - N = ''.join(N) - finally: - self._pos = opos - elif platformId == 1 and encodingId == 0 and languageId == 0: # Macintosh, Roman, English, PS Name - # According to OpenType spec, if PS name exists, it must exist - # both in MS Unicode and Macintosh Roman formats. Apparently, - # you can find live TTF fonts which only have Macintosh format. - N = self.get_chunk(string_data_offset + offset, length) - if N and names[nameId]==None: - names[nameId] = N - nameCount -= 1 - if nameCount==0: break - psName = names[6] - if not psName: - raise TTFError, "Could not find PostScript font name" - for c in psName: - oc = ord(c) - if oc<33 or oc>126 or c in ('[', ']', '(', ')', '{', '}', '<', '>', '/', '%'): - raise TTFError, "psName contains invalid character '%s' ie U+%04X" % (c,ord(c)) - self.name = psName - self.familyName = names[1] or psName - self.styleName = names[2] or 'Regular' - self.fullName = names[4] or psName - self.uniqueFontID = names[3] or psName - - # head - Font header table - self.seek_table("head") - ver_maj, ver_min = self.read_ushort(), self.read_ushort() - if ver_maj != 1: - raise TTFError, 'Unknown head table version %d.%04x' % (ver_maj, ver_min) - self.skip(8) - magic = self.read_ulong() - if magic != 0x5F0F3CF5: - raise TTFError, 'Invalid head table magic %04x' % magic - self.skip(2) - unitsPerEm = self.read_ushort() - scale = lambda x, unitsPerEm=unitsPerEm: x * 1000 / unitsPerEm - self.skip(16) - xMin = self.read_short() - yMin = self.read_short() - xMax = self.read_short() - yMax = self.read_short() - self.bbox = map(scale, [xMin, yMin, xMax, yMax]) - self.skip(3*2) - indexToLocFormat = self.read_ushort() - glyphDataFormat = self.read_ushort() - - # OS/2 - OS/2 and Windows metrics table - # (needs data from head table) - if self.table.has_key("OS/2"): - self.seek_table("OS/2") - version = self.read_ushort() - self.skip(2) - usWeightClass = self.read_ushort() - self.skip(2) - fsType = self.read_ushort() - if fsType == 0x0002 or (fsType & 0x0300) != 0: - raise TTFError, 'Font does not allow subsetting/embedding (%04X)' % fsType - self.skip(58) #11*2 + 10 + 4*4 + 4 + 3*2 - sTypoAscender = self.read_short() - sTypoDescender = self.read_short() - self.ascent = scale(sTypoAscender) # XXX: for some reason it needs to be multiplied by 1.24--1.28 - self.descent = scale(sTypoDescender) - - if version > 1: - self.skip(16) #3*2 + 2*4 + 2 - sCapHeight = self.read_short() - self.capHeight = scale(sCapHeight) - else: - self.capHeight = self.ascent - else: - # Microsoft TTFs require an OS/2 table; Apple ones do not. Try to - # cope. The data is not very important anyway. - usWeightClass = 500 - self.ascent = scale(yMax) - self.descent = scale(yMin) - self.capHeight = self.ascent - - # There's no way to get stemV from a TTF file short of analyzing actual outline data - # This fuzzy formula is taken from pdflib sources, but we could just use 0 here - self.stemV = 50 + int((usWeightClass / 65.0) ** 2) - - # post - PostScript table - # (needs data from OS/2 table) - self.seek_table("post") - ver_maj, ver_min = self.read_ushort(), self.read_ushort() - if ver_maj not in (1, 2, 3, 4): - # Adobe/MS documents 1, 2, 2.5, 3; Apple also has 4. - # From Apple docs it seems that we do not need to care - # about the exact version, so if you get this error, you can - # try to remove this check altogether. - raise TTFError, 'Unknown post table version %d.%04x' % (ver_maj, ver_min) - self.italicAngle = self.read_short() + self.read_ushort() / 65536.0 - self.skip(4) #2*2 - isFixedPitch = self.read_ulong() - - self.flags = FF_SYMBOLIC # All fonts that contain characters - # outside the original Adobe character - # set are considered "symbolic". - if self.italicAngle != 0: - self.flags = self.flags | FF_ITALIC - if usWeightClass >= 600: # FW_REGULAR == 500, FW_SEMIBOLD == 600 - self.flags = self.flags | FF_FORCEBOLD - if isFixedPitch: - self.flags = self.flags | FF_FIXED - # XXX: FF_SERIF? FF_SCRIPT? FF_ALLCAP? FF_SMALLCAP? - - # hhea - Horizontal header table - self.seek_table("hhea") - ver_maj, ver_min = self.read_ushort(), self.read_ushort() - if ver_maj != 1: - raise TTFError, 'Unknown hhea table version %d.%04x' % (ver_maj, ver_min) - self.skip(28) - metricDataFormat = self.read_ushort() - if metricDataFormat != 0: - raise TTFError, 'Unknown horizontal metric data format (%d)' % metricDataFormat - numberOfHMetrics = self.read_ushort() - if numberOfHMetrics == 0: - raise TTFError, 'Number of horizontal metrics is 0' - - # maxp - Maximum profile table - self.seek_table("maxp") - ver_maj, ver_min = self.read_ushort(), self.read_ushort() - if ver_maj != 1: - raise TTFError, 'Unknown maxp table version %d.%04x' % (ver_maj, ver_min) - numGlyphs = self.read_ushort() - - if not charInfo: - self.charToGlyph = None - self.defaultWidth = None - self.charWidths = None - return - - if glyphDataFormat != 0: - raise TTFError, 'Unknown glyph data format (%d)' % glyphDataFormat - - # cmap - Character to glyph index mapping table - cmap_offset = self.seek_table("cmap") - self.skip(2) - cmapTableCount = self.read_ushort() - unicode_cmap_offset = None - for n in xrange(cmapTableCount): - platformID = self.read_ushort() - encodingID = self.read_ushort() - offset = self.read_ulong() - if platformID == 3 and encodingID == 1: # Microsoft, Unicode - format = self.get_ushort(cmap_offset + offset) - if format == 4: - unicode_cmap_offset = cmap_offset + offset - break - elif platformID == 0: # Unicode -- assume all encodings are compatible - format = self.get_ushort(cmap_offset + offset) - if format == 4: - unicode_cmap_offset = cmap_offset + offset - break - if unicode_cmap_offset is None: - raise TTFError, 'Font does not have cmap for Unicode (platform 3, encoding 1, format 4 or platform 0 any encoding format 4)' - self.seek(unicode_cmap_offset + 2) - length = self.read_ushort() - limit = unicode_cmap_offset + length - self.skip(2) - segCount = self.read_ushort() / 2 - self.skip(6) - endCount = map(lambda x, self=self: self.read_ushort(), xrange(segCount)) - self.skip(2) - startCount = map(lambda x, self=self: self.read_ushort(), xrange(segCount)) - idDelta = map(lambda x, self=self: self.read_short(), xrange(segCount)) - idRangeOffset_start = self._pos - idRangeOffset = map(lambda x, self=self: self.read_ushort(), xrange(segCount)) - - # Now it gets tricky. - glyphToChar = {} - charToGlyph = {} - for n in xrange(segCount): - for unichar in xrange(startCount[n], endCount[n] + 1): - if idRangeOffset[n] == 0: - glyph = (unichar + idDelta[n]) & 0xFFFF - else: - offset = (unichar - startCount[n]) * 2 + idRangeOffset[n] - offset = idRangeOffset_start + 2 * n + offset - if offset >= limit: - # workaround for broken fonts (like Thryomanes) - glyph = 0 - else: - glyph = self.get_ushort(offset) - if glyph != 0: - glyph = (glyph + idDelta[n]) & 0xFFFF - charToGlyph[unichar] = glyph - if glyphToChar.has_key(glyph): - glyphToChar[glyph].append(unichar) - else: - glyphToChar[glyph] = [unichar] - self.charToGlyph = charToGlyph - - # hmtx - Horizontal metrics table - # (needs data from hhea, maxp, and cmap tables) - self.seek_table("hmtx") - aw = None - self.charWidths = {} - self.hmetrics = [] - for glyph in xrange(numberOfHMetrics): - # advance width and left side bearing. lsb is actually signed - # short, but we don't need it anyway (except for subsetting) - aw, lsb = self.read_ushort(), self.read_ushort() - self.hmetrics.append((aw, lsb)) - aw = scale(aw) - if glyph == 0: - self.defaultWidth = aw - if glyphToChar.has_key(glyph): - for char in glyphToChar[glyph]: - self.charWidths[char] = aw - for glyph in xrange(numberOfHMetrics, numGlyphs): - # the rest of the table only lists advance left side bearings. - # so we reuse aw set by the last iteration of the previous loop - lsb = self.read_ushort() - self.hmetrics.append((aw, lsb)) - if glyphToChar.has_key(glyph): - for char in glyphToChar[glyph]: - self.charWidths[char] = aw - - # loca - Index to location - self.seek_table('loca') - self.glyphPos = [] - if indexToLocFormat == 0: - for n in xrange(numGlyphs + 1): - self.glyphPos.append(self.read_ushort() << 1) - elif indexToLocFormat == 1: - for n in xrange(numGlyphs + 1): - self.glyphPos.append(self.read_ulong()) - else: - raise TTFError, 'Unknown location table format (%d)' % indexToLocFormat - - # Subsetting - - def makeSubset(self, subset): - """Create a subset of a TrueType font""" - output = TTFontMaker() - - # Build a mapping of glyphs in the subset to glyph numbers in - # the original font. Also build a mapping of UCS codes to - # glyph values in the new font. - - # Start with 0 -> 0: "missing character" - glyphMap = [0] # new glyph index -> old glyph index - glyphSet = {0:0} # old glyph index -> new glyph index - codeToGlyph = {} # unicode -> new glyph index - for code in subset: - if self.charToGlyph.has_key(code): - originalGlyphIdx = self.charToGlyph[code] - else: - originalGlyphIdx = 0 - if not glyphSet.has_key(originalGlyphIdx): - glyphSet[originalGlyphIdx] = len(glyphMap) - glyphMap.append(originalGlyphIdx) - codeToGlyph[code] = glyphSet[originalGlyphIdx] - - # Also include glyphs that are parts of composite glyphs - start = self.get_table_pos('glyf')[0] - n = 0 - while n < len(glyphMap): - originalGlyphIdx = glyphMap[n] - glyphPos = self.glyphPos[originalGlyphIdx] - glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos - self.seek(start + glyphPos) - numberOfContours = self.read_short() - if numberOfContours < 0: - # composite glyph - self.skip(8) - flags = GF_MORE_COMPONENTS - while flags & GF_MORE_COMPONENTS: - flags = self.read_ushort() - glyphIdx = self.read_ushort() - if not glyphSet.has_key(glyphIdx): - glyphSet[glyphIdx] = len(glyphMap) - glyphMap.append(glyphIdx) - if flags & GF_ARG_1_AND_2_ARE_WORDS: - self.skip(4) - else: - self.skip(2) - if flags & GF_WE_HAVE_A_SCALE: - self.skip(2) - elif flags & GF_WE_HAVE_AN_X_AND_Y_SCALE: - self.skip(4) - elif flags & GF_WE_HAVE_A_TWO_BY_TWO: - self.skip(8) - n = n + 1 - - numGlyphs = n = len(glyphMap) - while n > 1 and self.hmetrics[n][0] == self.hmetrics[n - 1][0]: - n = n - 1 - numberOfHMetrics = n - - # The following tables are simply copied from the original - for tag in ('name', 'OS/2', 'cvt ', 'fpgm', 'prep'): - try: - output.add(tag, self.get_table(tag)) - except KeyError: - # Apparently some of the tables are optional (cvt, fpgm, prep). - # The lack of the required ones (name, OS/2) would have already - # been caught before. - pass - - # post - PostScript - post = "\x00\x03\x00\x00" + self.get_table('post')[4:16] + "\x00" * 16 - output.add('post', post) - - # hhea - Horizontal Header - hhea = self.get_table('hhea') - hhea = _set_ushort(hhea, 34, numberOfHMetrics) - output.add('hhea', hhea) - - # maxp - Maximum Profile - maxp = self.get_table('maxp') - maxp = _set_ushort(maxp, 4, numGlyphs) - output.add('maxp', maxp) - - # cmap - Character to glyph mapping - # XXX maybe use format 0 if possible, not 6? - entryCount = len(subset) - length = 10 + entryCount * 2 - cmap = [0, 1, # version, number of tables - 1, 0, 0,12, # platform, encoding, offset (hi,lo) - 6, length, 0, # format, length, language - 0, - entryCount] + \ - map(codeToGlyph.get, subset) - cmap = apply(pack, [">%dH" % len(cmap)] + cmap) - output.add('cmap', cmap) - - # hmtx - Horizontal Metrics - hmtx = [] - for n in xrange(numGlyphs): - originalGlyphIdx = glyphMap[n] - aw, lsb = self.hmetrics[originalGlyphIdx] - if n < numberOfHMetrics: - hmtx.append(aw) - hmtx.append(lsb) - hmtx = apply(pack, [">%dH" % len(hmtx)] + hmtx) - output.add('hmtx', hmtx) - - # glyf - Glyph data - glyphData = self.get_table('glyf') - offsets = [] - glyf = [] - pos = 0 - for n in xrange(numGlyphs): - offsets.append(pos) - originalGlyphIdx = glyphMap[n] - glyphPos = self.glyphPos[originalGlyphIdx] - glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos - data = glyphData[glyphPos:glyphPos+glyphLen] - # Fix references in composite glyphs - if glyphLen > 2 and unpack(">h", data[:2])[0] < 0: - # composite glyph - pos_in_glyph = 10 - flags = GF_MORE_COMPONENTS - while flags & GF_MORE_COMPONENTS: - flags = unpack(">H", data[pos_in_glyph:pos_in_glyph+2])[0] - glyphIdx = unpack(">H", data[pos_in_glyph+2:pos_in_glyph+4])[0] - data = _set_ushort(data, pos_in_glyph + 2, glyphSet[glyphIdx]) - pos_in_glyph = pos_in_glyph + 4 - if flags & GF_ARG_1_AND_2_ARE_WORDS: - pos_in_glyph = pos_in_glyph + 4 - else: - pos_in_glyph = pos_in_glyph + 2 - if flags & GF_WE_HAVE_A_SCALE: - pos_in_glyph = pos_in_glyph + 2 - elif flags & GF_WE_HAVE_AN_X_AND_Y_SCALE: - pos_in_glyph = pos_in_glyph + 4 - elif flags & GF_WE_HAVE_A_TWO_BY_TWO: - pos_in_glyph = pos_in_glyph + 8 - glyf.append(data) - pos = pos + glyphLen - if pos % 4 != 0: - padding = 4 - pos % 4 - glyf.append('\0' * padding) - pos = pos + padding - offsets.append(pos) - output.add('glyf', string.join(glyf, "")) - - # loca - Index to location - loca = [] - if (pos + 1) >> 1 > 0xFFFF: - indexToLocFormat = 1 # long format - for offset in offsets: - loca.append(offset) - loca = apply(pack, [">%dL" % len(loca)] + loca) - else: - indexToLocFormat = 0 # short format - for offset in offsets: - loca.append(offset >> 1) - loca = apply(pack, [">%dH" % len(loca)] + loca) - output.add('loca', loca) - - # head - Font header - head = self.get_table('head') - head = _set_ushort(head, 50, indexToLocFormat) - output.add('head', head) - - return output.makeStream() - - -# -# TrueType font embedding -# - -# PDF font flags (see PDF Reference Guide table 5.19) -FF_FIXED = 1 << 1-1 -FF_SERIF = 1 << 2-1 -FF_SYMBOLIC = 1 << 3-1 -FF_SCRIPT = 1 << 4-1 -FF_NONSYMBOLIC = 1 << 6-1 -FF_ITALIC = 1 << 7-1 -FF_ALLCAP = 1 << 17-1 -FF_SMALLCAP = 1 << 18-1 -FF_FORCEBOLD = 1 << 19-1 - -class TTFontFace(TTFontFile, pdfmetrics.TypeFace): - """TrueType typeface. - - Conceptually similar to a single byte typeface, but the glyphs are - identified by UCS character codes instead of glyph names.""" - - def __init__(self, filename, validate=0, subfontIndex=0): - "Loads a TrueType font from filename." - pdfmetrics.TypeFace.__init__(self, None) - TTFontFile.__init__(self, filename, validate=validate, subfontIndex=subfontIndex) - - def getCharWidth(self, code): - "Returns the width of character U+" - return self.charWidths.get(code, self.defaultWidth) - - def addSubsetObjects(self, doc, fontname, subset): - """Generate a TrueType font subset and add it to the PDF document. - Returns a PDFReference to the new FontDescriptor object.""" - - fontFile = pdfdoc.PDFStream() - fontFile.content = self.makeSubset(subset) - fontFile.dictionary['Length1'] = len(fontFile.content) - if doc.compression: - fontFile.filters = [pdfdoc.PDFZCompress] - fontFileRef = doc.Reference(fontFile, 'fontFile:%s(%s)' % (self.filename, fontname)) - - flags = self.flags & ~ FF_NONSYMBOLIC - flags = flags | FF_SYMBOLIC - - fontDescriptor = pdfdoc.PDFDictionary({ - 'Type': '/FontDescriptor', - 'Ascent': self.ascent, - 'CapHeight': self.capHeight, - 'Descent': self.descent, - 'Flags': flags, - 'FontBBox': pdfdoc.PDFArray(self.bbox), - 'FontName': pdfdoc.PDFName(fontname), - 'ItalicAngle': self.italicAngle, - 'StemV': self.stemV, - 'FontFile2': fontFileRef, - }) - return doc.Reference(fontDescriptor, 'fontDescriptor:' + fontname) - -class TTEncoding: - """Encoding for TrueType fonts (always UTF-8). - - TTEncoding does not directly participate in PDF object creation, since - we need a number of different 8-bit encodings for every generated font - subset. TTFont itself cares about that.""" - - def __init__(self): - self.name = "UTF-8" - - -class TTFont: - """Represents a TrueType font. - - Its encoding is always UTF-8. - - Note: you cannot use the same TTFont object for different documents - at the same time. - - Example of usage: - - font = ttfonts.TTFont('PostScriptFontName', '/path/to/font.ttf') - pdfmetrics.registerFont(font) - - canvas.setFont('PostScriptFontName', size) - canvas.drawString(x, y, "Some text encoded in UTF-8") - """ - - class State: - def __init__(self): - self.assignments = {} - self.nextCode = 0 - self.internalName = None - self.frozen = 0 - - # Let's add the first 128 unicodes to the 0th subset, so ' ' - # always has code 32 (for word spacing to work) and the ASCII - # output is readable - subset0 = range(128) - self.subsets = [subset0] - for n in subset0: - self.assignments[n] = n - self.nextCode = 128 - - def __init__(self, name, filename, validate=0, subfontIndex=0): - """Loads a TrueType font from filename. - - If validate is set to a false values, skips checksum validation. This - can save time, especially if the font is large. - """ - self.fontName = name - self.face = TTFontFace(filename, validate=validate, subfontIndex=subfontIndex) - self.encoding = TTEncoding() - self._multiByte = 1 # We want our own stringwidth - self._dynamicFont = 1 # We want dynamic subsetting - self.state = {} - - def _py_stringWidth(self, text, size, encoding='utf-8'): - "Calculate text width" - if type(text) is not UnicodeType: - text = unicode(text, encoding or 'utf-8') # encoding defaults to utf-8 - g = self.face.charWidths.get - dw = self.face.defaultWidth - return 0.001*size*sum([g(ord(u),dw) for u in text]) - stringWidth = _py_stringWidth - - def splitString(self, text, doc, encoding='utf-8'): - """Splits text into a number of chunks, each of which belongs to a - single subset. Returns a list of tuples (subset, string). Use subset - numbers with getSubsetInternalName. Doc is needed for distinguishing - subsets when building different documents at the same time.""" - try: state = self.state[doc] - except KeyError: state = self.state[doc] = TTFont.State() - curSet = -1 - cur = [] - results = [] - if type(text) is not UnicodeType: - text = unicode(text, encoding or 'utf-8') # encoding defaults to utf-8 - for code in map(ord,text): - if state.assignments.has_key(code): - n = state.assignments[code] - else: - if state.frozen: - raise pdfdoc.PDFError, "Font %s is already frozen, cannot add new character U+%04X" % (self.fontName, code) - n = state.nextCode - if n & 0xFF == 32: - # make code 32 always be a space character - state.subsets[n >> 8].append(32) - state.nextCode += 1 - n = state.nextCode - state.nextCode += 1 - state.assignments[code] = n - if (n & 0xFF) == 0: - state.subsets.append([]) - state.subsets[n >> 8].append(code) - if (n >> 8) != curSet: - if cur: - results.append((curSet, string.join(map(chr, cur), ""))) - curSet = (n >> 8) - cur = [] - cur.append(n & 0xFF) - if cur: - results.append((curSet, string.join(map(chr, cur), ""))) - return results - - def getSubsetInternalName(self, subset, doc): - """Returns the name of a PDF Font object corresponding to a given - subset of this dynamic font. Use this function instead of - PDFDocument.getInternalFontName.""" - try: state = self.state[doc] - except KeyError: state = self.state[doc] = TTFont.State() - if subset < 0 or subset >= len(state.subsets): - raise IndexError, 'Subset %d does not exist in font %s' % (subset, self.fontName) - if state.internalName is None: - state.internalName = 'F%d' % (len(doc.fontMapping) + 1) - doc.fontMapping[self.fontName] = '/' + state.internalName - doc.delayedFonts.append(self) - return '/%s+%d' % (state.internalName, subset) - - def addObjects(self, doc): - """Makes one or more PDF objects to be added to the document. The - caller supplies the internal name to be used (typically F1, F2, ... in - sequence). - - This method creates a number of Font and FontDescriptor objects. Every - FontDescriptor is a (no more than) 256 character subset of the original - TrueType font.""" - try: state = self.state[doc] - except KeyError: state = self.state[doc] = TTFont.State() - state.frozen = 1 - for n in xrange(len(state.subsets)): - subset = state.subsets[n] - internalName = self.getSubsetInternalName(n, doc)[1:] - baseFontName = "%s+%s%s" % (SUBSETN(n),self.face.name,self.face.subfontNameX) - - pdfFont = pdfdoc.PDFTrueTypeFont() - pdfFont.__Comment__ = 'Font %s subset %d' % (self.fontName, n) - pdfFont.Name = internalName - pdfFont.BaseFont = baseFontName - - pdfFont.FirstChar = 0 - pdfFont.LastChar = len(subset) - 1 - - widths = map(self.face.getCharWidth, subset) - pdfFont.Widths = pdfdoc.PDFArray(widths) - - cmapStream = pdfdoc.PDFStream() - cmapStream.content = makeToUnicodeCMap(baseFontName, subset) - if doc.compression: - cmapStream.filters = [pdfdoc.PDFZCompress] - pdfFont.ToUnicode = doc.Reference(cmapStream, 'toUnicodeCMap:' + baseFontName) - - pdfFont.FontDescriptor = self.face.addSubsetObjects(doc, baseFontName, subset) - - # link it in - ref = doc.Reference(pdfFont, internalName) - fontDict = doc.idToObject['BasicFonts'].dict - fontDict[internalName] = pdfFont - del self.state[doc] -try: - from _rl_accel import _instanceStringWidthTTF - import new - TTFont.stringWidth = new.instancemethod(_instanceStringWidthTTF,None,TTFont) -except ImportError: - pass diff --git a/bin/reportlab/pdfgen/__init__.py b/bin/reportlab/pdfgen/__init__.py deleted file mode 100755 index 9dc4437c817..00000000000 --- a/bin/reportlab/pdfgen/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/__init__.py -__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__='' \ No newline at end of file diff --git a/bin/reportlab/pdfgen/canvas.py b/bin/reportlab/pdfgen/canvas.py deleted file mode 100755 index d06e9670997..00000000000 --- a/bin/reportlab/pdfgen/canvas.py +++ /dev/null @@ -1,1462 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/canvas.py -__version__=''' $Id: canvas.py 2854 2006-05-10 12:57:21Z rgbecker $ ''' -__doc__=""" -The Canvas object is the primary interface for creating PDF files. See -doc/userguide.pdf for copious examples. -""" -ENABLE_TRACKING = 1 # turn this off to do profile testing w/o tracking - -import os -import sys -import re -from string import join, split, strip, atoi, replace, upper, digits -import tempfile -from types import * -from math import sin, cos, tan, pi, ceil -import md5 - -from reportlab import rl_config -from reportlab.pdfbase import pdfutils -from reportlab.pdfbase import pdfdoc -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfgen import pdfgeom, pathobject, textobject -from reportlab.lib.utils import import_zlib -from reportlab.lib.utils import fp_str - - -digitPat = re.compile('\d') #used in decimal alignment - -zlib = import_zlib() -_SeqTypes=(TupleType,ListType) - -# Robert Kern -# Constants for closing paths. -# May be useful if one changes 'arc' and 'rect' to take a -# default argument that tells how to close the path. -# That way we can draw filled shapes. - -FILL_EVEN_ODD = 0 -FILL_NON_ZERO = 1 - #this is used by path-closing routines. - #map stroke, fill, fillmode -> operator - # fillmode: 1 = non-Zero (obviously), 0 = evenOdd -PATH_OPS = {(0, 0, FILL_EVEN_ODD) : 'n', #no op - (0, 0, FILL_NON_ZERO) : 'n', #no op - (1, 0, FILL_EVEN_ODD) : 'S', #stroke only - (1, 0, FILL_NON_ZERO) : 'S', #stroke only - (0, 1, FILL_EVEN_ODD) : 'f*', #Fill only - (0, 1, FILL_NON_ZERO) : 'f', #Fill only - (1, 1, FILL_EVEN_ODD) : 'B*', #Stroke and Fill - (1, 1, FILL_NON_ZERO) : 'B', #Stroke and Fill - } - -_escapePDF = pdfutils._escape -_instanceEscapePDF = pdfutils._instanceEscapePDF - -if sys.hexversion >= 0x02000000: - def _digester(s): - return md5.md5(s).hexdigest() -else: - # hexdigest not available in 1.5 - def _digester(s): - return join(map(lambda x : "%02x" % ord(x), md5.md5(s).digest()), '') - -def _annFormat(D,color,thickness,dashArray): - from reportlab.pdfbase.pdfdoc import PDFArray - if color: - D["C"] = PDFArray([color.red, color.green, color.blue]) - border = [0,0,0] - if thickness: - border[2] = thickness - if dashArray: - border.append(PDFArray(dashArray)) - D["Border"] = PDFArray(border) - -class Canvas(textobject._PDFColorSetter): - """This class is the programmer's interface to the PDF file format. Methods - are (or will be) provided here to do just about everything PDF can do. - - The underlying model to the canvas concept is that of a graphics state machine - that at any given point in time has a current font, fill color (for figure - interiors), stroke color (for figure borders), line width and geometric transform, among - many other characteristics. - - Canvas methods generally either draw something (like canvas.line) using the - current state of the canvas or change some component of the canvas - state (like canvas.setFont). The current state can be saved and restored - using the saveState/restoreState methods. - - Objects are "painted" in the order they are drawn so if, for example - two rectangles overlap the last draw will appear "on top". PDF form - objects (supported here) are used to draw complex drawings only once, - for possible repeated use. - - There are other features of canvas which are not visible when printed, - such as outlines and bookmarks which are used for navigating a document - in a viewer. - - Here is a very silly example usage which generates a Hello World pdf document. - - from reportlab.pdfgen import canvas - c = canvas.Canvas("hello.pdf") - from reportlab.lib.units import inch - # move the origin up and to the left - c.translate(inch,inch) - # define a large font - c.setFont("Helvetica", 80) - # choose some colors - c.setStrokeColorRGB(0.2,0.5,0.3) - c.setFillColorRGB(1,0,1) - # draw a rectangle - c.rect(inch,inch,6*inch,9*inch, fill=1) - # make text go straight up - c.rotate(90) - # change color - c.setFillColorRGB(0,0,0.77) - # say hello (note after rotate the y coord needs to be negative!) - c.drawString(3*inch, -3*inch, "Hello World") - c.showPage() - c.save() - """ - - def __init__(self,filename, - pagesize=None, - bottomup = 1, - pageCompression=None, - invariant = None, - verbosity=0): - """Create a canvas of a given size. etc. - - You may pass a file-like object to filename as an alternative to - a string. - - Most of the attributes are private - we will use set/get methods - as the preferred interface. Default page size is A4.""" - if pagesize is None: pagesize = rl_config.defaultPageSize - if invariant is None: invariant = rl_config.invariant - self._filename = filename - - self._doc = pdfdoc.PDFDocument(compression=pageCompression, - invariant=invariant, filename=filename) - - - #this only controls whether it prints 'saved ...' - 0 disables - self._verbosity = verbosity - - #this is called each time a page is output if non-null - self._onPage = None - - self._pagesize = pagesize - self._pageRotation = 0 - #self._currentPageHasImages = 0 - self._pageTransition = None - self._pageDuration = None - self._destinations = {} # dictionary of destinations for cross indexing. - - self.setPageCompression(pageCompression) - self._pageNumber = 1 # keep a count - #self3 = [] #where the current page's marking operators accumulate - # when we create a form we need to save operations not in the form - self._codeStack = [] - self._restartAccumulators() # restart all accumulation state (generalized, arw) - self._annotationCount = 0 - - self._outlines = [] # list for a name tree - self._psCommandsBeforePage = [] #for postscript tray/font commands - self._psCommandsAfterPage = [] #for postscript tray/font commands - - #PostScript has the origin at bottom left. It is easy to achieve a top- - #down coord system by translating to the top of the page and setting y - #scale to -1, but then text is inverted. So self.bottomup is used - #to also set the text matrix accordingly. You can now choose your - #drawing coordinates. - self.bottomup = bottomup - self.imageCaching = rl_config.defaultImageCaching - self._make_preamble() - self.init_graphics_state() - self.state_stack = [] - - def init_graphics_state(self): - #initial graphics state, never modify any of these in place - self._x = 0 - self._y = 0 - self._fontname = 'Times-Roman' - self._fontsize = 12 - - self._dynamicFont = 0 - self._textMode = 0 #track if between BT/ET - self._leading = 14.4 - self._currentMatrix = (1., 0., 0., 1., 0., 0.) - self._fillMode = 0 #even-odd - - #text state - self._charSpace = 0 - self._wordSpace = 0 - self._horizScale = 100 - self._textRenderMode = 0 - self._rise = 0 - self._textLineMatrix = (1., 0., 0., 1., 0., 0.) - self._textMatrix = (1., 0., 0., 1., 0., 0.) - - # line drawing - self._lineCap = 0 - self._lineJoin = 0 - self._lineDash = None #not done - self._lineWidth = 0 - self._mitreLimit = 0 - - self._fillColorRGB = (0,0,0) - self._strokeColorRGB = (0,0,0) - - def push_state_stack(self): - state = {} - d = self.__dict__ - for name in self.STATE_ATTRIBUTES: - state[name] = d[name] #getattr(self, name) - self.state_stack.append(state) - - def pop_state_stack(self): - state = self.state_stack[-1] - del self.state_stack[-1] - d = self.__dict__ - d.update(state) - - STATE_ATTRIBUTES = split(""" - _x _y _fontname _fontsize _dynamicFont _textMode _leading _currentMatrix _fillMode - _fillMode _charSpace _wordSpace _horizScale _textRenderMode _rise _textLineMatrix - _textMatrix _lineCap _lineJoin _lineDash _lineWidth _mitreLimit _fillColorRGB - _strokeColorRGB""") - STATE_RANGE = range(len(STATE_ATTRIBUTES)) - - #self._addStandardFonts() - - def _make_preamble(self): - # yuk - iName = self._doc.getInternalFontName('Helvetica') - if self.bottomup: - #must set an initial font - self._preamble = '1 0 0 1 0 0 cm BT %s 12 Tf 14.4 TL ET' % iName - else: - #switch coordinates, flip text and set font - self._preamble = '1 0 0 -1 0 %s cm BT %s 12 Tf 14.4 TL ET' % (fp_str(self._pagesize[1]), iName) - - if not _instanceEscapePDF: - def _escape(self, s): - return _escapePDF(s) - - #info functions - non-standard - def setAuthor(self, author): - """identify the author for invisible embedding inside the PDF document. - the author annotation will appear in the the text of the file but will - not automatically be seen when the document is viewed, but is visible - in document properties etc etc.""" - self._doc.setAuthor(author) - - def setDateFormatter(self, dateFormatter): - """accepts a func(yyyy,mm,dd,hh,m,s) used to create embedded formatted date""" - self._doc.setDateFormatter(dateFormatter) - - def addOutlineEntry(self, title, key, level=0, closed=None): - """Adds a new entry to the outline at given level. If LEVEL not specified, - entry goes at the top level. If level specified, it must be - no more than 1 greater than the outline level in the last call. - - The key must be the (unique) name of a bookmark. - the title is the (non-unique) name to be displayed for the entry. - - If closed is set then the entry should show no subsections by default - when displayed. - - Example - c.addOutlineEntry("first section", "section1") - c.addOutlineEntry("introduction", "s1s1", 1, closed=1) - c.addOutlineEntry("body", "s1s2", 1) - c.addOutlineEntry("detail1", "s1s2s1", 2) - c.addOutlineEntry("detail2", "s1s2s2", 2) - c.addOutlineEntry("conclusion", "s1s3", 1) - c.addOutlineEntry("further reading", "s1s3s1", 2) - c.addOutlineEntry("second section", "section1") - c.addOutlineEntry("introduction", "s2s1", 1) - c.addOutlineEntry("body", "s2s2", 1, closed=1) - c.addOutlineEntry("detail1", "s2s2s1", 2) - c.addOutlineEntry("detail2", "s2s2s2", 2) - c.addOutlineEntry("conclusion", "s2s3", 1) - c.addOutlineEntry("further reading", "s2s3s1", 2) - - generated outline looks like - - first section - |- introduction - |- body - | |- detail1 - | |- detail2 - |- conclusion - | |- further reading - - second section - |- introduction - |+ body - |- conclusion - | |- further reading - - Note that the second "body" is closed. - - Note that you can jump from level 5 to level 3 but not - from 3 to 5: instead you need to provide all intervening - levels going down (4 in this case). Note that titles can - collide but keys cannot. - """ - #to be completed - #self._outlines.append(title) - self._doc.outline.addOutlineEntry(key, level, title, closed=closed) - - def setOutlineNames0(self, *nametree): # keep this for now (?) - """nametree should can be a recursive tree like so - c.setOutlineNames( - "chapter1dest", - ("chapter2dest", - ["chapter2section1dest", - "chapter2section2dest", - "chapter2conclusiondest"] - ), # end of chapter2 description - "chapter3dest", - ("chapter4dest", ["c4s1", "c4s2"]) - ) - each of the string names inside must be bound to a bookmark - before the document is generated. - """ - self._doc.outline.setNames(*((self,)+nametree)) - - def setTitle(self, title): - """write a title into the PDF file that won't automatically display - in the document itself.""" - self._doc.setTitle(title) - - def setSubject(self, subject): - """write a subject into the PDF file that won't automatically display - in the document itself.""" - self._doc.setSubject(subject) - - def pageHasData(self): - "Info function - app can call it after showPage to see if it needs a save" - return len(self._code) == 0 - - def showOutline(self): - """Specify that Acrobat Reader should start with the outline tree visible. - showFullScreen() and showOutline() conflict; the one called last - wins.""" - self._doc._catalog.showOutline() - - def showFullScreen0(self): - """Specify that Acrobat Reader should start in full screen mode. - showFullScreen() and showOutline() conflict; the one called last - wins.""" - self._doc._catalog.showFullScreen() - - def showPage(self): - """Close the current page and possibly start on a new page.""" - - # ensure a space at the end of the stream - Acrobat does - # not mind, but Ghostscript dislikes 'Qendstream' even if - # the length marker finishes after 'Q' - self._code.append(' ') - page = pdfdoc.PDFPage() - page.pagewidth = self._pagesize[0] - page.pageheight = self._pagesize[1] - page.Rotate = self._pageRotation - page.hasImages = self._currentPageHasImages - page.setPageTransition(self._pageTransition) - page.setCompression(self._pageCompression) - if self._pageDuration is not None: - page.Dur = self._pageDuration - - strm = self._psCommandsBeforePage + [self._preamble] + self._code + self._psCommandsAfterPage - page.setStream(strm) - self._setXObjects(page) - self._setAnnotations(page) - self._doc.addPage(page) - - if self._onPage: self._onPage(self._pageNumber) - self._startPage() - - def _startPage(self): - #now get ready for the next one - self._pageNumber = self._pageNumber+1 - self._restartAccumulators() - self.init_graphics_state() - self.state_stack = [] - - def setPageCallBack(self, func): - """func(pageNum) will be called on each page end. - - This is mainly a hook for progress monitoring. - Call setPageCallback(None) to clear a callback.""" - self._onPage = func - - def _setAnnotations(self,page): - page.Annots = self._annotationrefs - - def _setXObjects(self, thing): - """for pages and forms, define the XObject dictionary for resources, if needed""" - forms = self._formsinuse - if forms: - xobjectsdict = self._doc.xobjDict(forms) - thing.XObjects = xobjectsdict - else: - thing.XObjects = None - - def _bookmarkReference(self, name): - """get a reference to a (possibly undefined, possibly unbound) bookmark""" - d = self._destinations - try: - return d[name] - except: - result = d[name] = pdfdoc.Destination(name) # newly defined, unbound - return result - - def bookmarkPage(self, key, - fit="Fit", - left=None, - top=None, - bottom=None, - right=None, - zoom=None - ): - """ - This creates a bookmark to the current page which can - be referred to with the given key elsewhere. - - PDF offers very fine grained control over how Acrobat - reader is zoomed when people link to this. The default - is to keep the user's current zoom settings. the last - arguments may or may not be needed depending on the - choice of 'fitType'. - - Fit types and the other arguments they use are: - /XYZ left top zoom - fine grained control. null - or zero for any of the parameters means 'leave - as is', so "0,0,0" will keep the reader's settings. - NB. Adobe Reader appears to prefer "null" to 0's. - - /Fit - entire page fits in window - - /FitH top - top coord at top of window, width scaled - to fit. - - /FitV left - left coord at left of window, height - scaled to fit - - /FitR left bottom right top - scale window to fit - the specified rectangle - - (question: do we support /FitB, FitBH and /FitBV - which are hangovers from version 1.1 / Acrobat 3.0?)""" - dest = self._bookmarkReference(key) - self._doc.inPage() # try to enable page-only features - pageref = self._doc.thisPageRef() - - #None = "null" for PDF - if left is None: - left = "null" - if top is None: - top = "null" - if bottom is None: - bottom = "null" - if right is None: - right = "null" - if zoom is None: - zoom = "null" - - if fit == "XYZ": - dest.xyz(left,top,zoom) - elif fit == "Fit": - dest.fit() - elif fit == "FitH": - dest.fith(top) - elif fit == "FitV": - dest.fitv(left) - elif fit == "FitR": - dest.fitr(left,bottom,right,top) - #Do we need these (version 1.1 / Acrobat 3 versions)? - elif fit == "FitB": - dest.fitb() - elif fit == "FitBH": - dest.fitbh(top) - elif fit == "FitBV": - dest.fitbv(left) - else: - raise "Unknown Fit type %s" % (fit,) - - dest.setPage(pageref) - return dest - - def bookmarkHorizontalAbsolute(self, key, top, left=0, fit='XYZ', **kw): - """Bind a bookmark (destination) to the current page at a horizontal position. - Note that the yhorizontal of the book mark is with respect to the default - user space (where the origin is at the lower left corner of the page) - and completely ignores any transform (translation, scale, skew, rotation, - etcetera) in effect for the current graphics state. The programmer is - responsible for making sure the bookmark matches an appropriate item on - the page.""" - #This method should probably be deprecated since it is just a sub-set of bookmarkPage - return self.bookmarkPage(key, fit=fit, top=top, left=left, zoom=0) - - def bookmarkHorizontal(self, key, relativeX, relativeY, **kw): - """w.r.t. the current transformation, bookmark this horizontal.""" - (left, top) = self.absolutePosition(relativeX,relativeY) - self.bookmarkHorizontalAbsolute(key, top, left=left, **kw) - - #def _inPage0(self): disallowed! - # """declare a page, enable page features""" - # self._doc.inPage() - - #def _inForm0(self): - # "deprecated in favore of beginForm...endForm" - # self._doc.inForm() - - def doForm(self, name): - """use a form XObj in current operation stream. - - The form should either have been defined previously using - beginForm ... endForm, or may be defined later. If it is not - defined at save time, an exception will be raised. The form - will be drawn within the context of the current graphics - state.""" - self._code.append("/%s Do" % self._doc.getXObjectName(name)) - self._formsinuse.append(name) - - def hasForm(self, name): - """Query whether form XObj really exists yet.""" - return self._doc.hasForm(name) - - - ###################################################### - # - # Image routines - # - ###################################################### - - def drawInlineImage(self, image, x,y, width=None,height=None): - """Draw an Image into the specified rectangle. If width and - height are omitted, they are calculated from the image size. - Also allow file names as well as images. The size in pixels - of the image is returned.""" - - self._currentPageHasImages = 1 - from pdfimages import PDFImage - img_obj = PDFImage(image, x,y, width, height) - img_obj.drawInlineImage(self) - return (img_obj.width, img_obj.height) - - def drawImage(self, image, x, y, width=None, height=None, mask=None): - """Draws the image (ImageReader object or filename) as specified. - - "image" may be an image filename or a ImageReader object. If width - and height are not given, the "natural" width and height in pixels - is used at a scale of 1 point to 1 pixel. - - The mask parameter takes 6 numbers and defines the range of - RGB values which will be masked out or treated as transparent. - For example with [0,2,40,42,136,139], it will mask out any - pixels with a Red value from 0-2, Green from 40-42 and - Blue from 136-139 (on a scale of 0-255) - - The method returns the width and height of the underlying image since - this is often useful for layout algorithms. - - Unlike drawInlineImage, this creates 'external images' which - are only stored once in the PDF file but can be drawn many times. - If you give it the same filename twice, even at different locations - and sizes, it will reuse the first occurrence. If you use ImageReader - objects, it tests whether the image content has changed before deciding - whether to reuse it. - - In general you should use drawImage in preference to drawInlineImage - unless you have read the PDF Spec and understand the tradeoffs.""" - self._currentPageHasImages = 1 - - # first, generate a unique name/signature for the image. If ANYTHING - # is different, even the mask, this should be different. - if type(image) == type(''): - #filename, use it - name = _digester('%s%s' % (image, mask)) - else: - rawdata = image.getRGBData() - name = _digester(rawdata+str(mask)) - - # in the pdf document, this will be prefixed with something to - # say it is an XObject. Does it exist yet? - regName = self._doc.getXObjectName(name) - imgObj = self._doc.idToObject.get(regName, None) - if not imgObj: - #first time seen, create and register the PDFImageXobject - imgObj = pdfdoc.PDFImageXObject(name, image, mask=mask) - imgObj.name = name - self._setXObjects(imgObj) - self._doc.Reference(imgObj, regName) - self._doc.addForm(name, imgObj) - - # ensure we have a size, as PDF will make it 1x1 pixel otherwise! - if width is None: - width = imgObj.width - if height is None: - height = imgObj.height - - # scale and draw - self.saveState() - self.translate(x, y) - self.scale(width, height) - self._code.append("/%s Do" % regName) - self.restoreState() - - # track what's been used on this page - self._formsinuse.append(name) - - return (imgObj.width, imgObj.height) - - def _restartAccumulators(self): - if self._codeStack: - # restore the saved code - saved = self._codeStack[-1] - del self._codeStack[-1] - (self._code, self._formsinuse, self._annotationrefs, self._formData) = saved - else: - self._code = [] # ready for more... - self._psCommandsAfterPage = [] - self._currentPageHasImages = 1 # for safety... - self._formsinuse = [] - self._annotationrefs = [] - self._formData = None - - def _pushAccumulators(self): - "when you enter a form, save accumulator info not related to the form for page (if any)" - saved = (self._code, self._formsinuse, self._annotationrefs, self._formData) - self._codeStack.append(saved) - self._code = [] # ready for more... - self._currentPageHasImages = 1 # for safety... - self._formsinuse = [] - self._annotationrefs = [] - self._formData = None - - def beginForm(self, name, lowerx=0, lowery=0, upperx=None, uppery=None): - """declare the current graphics stream to be a named form. - A graphics stream can either be a page or a form, not both. - Some operations (like bookmarking) are permitted for pages - but not forms. The form will not automatically be shown in the - document but must be explicitly referenced using doForm in pages - that require the form.""" - self.push_state_stack() - self.init_graphics_state() - if self._code: - # save the code that is not in the formf - self._pushAccumulators() - #self._codeStack.append(self._code) - #self._code = [] - self._formData = (name, lowerx, lowery, upperx, uppery) - self._doc.inForm() - #self._inForm0() - - def endForm(self): - """emit the current collection of graphics operations as a Form - as declared previously in beginForm.""" - (name, lowerx, lowery, upperx, uppery) = self._formData - #self.makeForm0(name, lowerx, lowery, upperx, uppery) - # fall through! makeForm0 disallowed - #def makeForm0(self, name, lowerx=0, lowery=0, upperx=None, uppery=None): - """Like showpage, but make a form using accumulated operations instead""" - # deprecated in favor or beginForm(...)... endForm() - (w,h) = self._pagesize - if upperx is None: upperx=w - if uppery is None: uppery=h - form = pdfdoc.PDFFormXObject(lowerx=lowerx, lowery=lowery, upperx=upperx, uppery=uppery) - form.compression = self._pageCompression - form.setStreamList([self._preamble] + self._code) # ??? minus preamble (seems to be needed!) - self._setXObjects(form) - self._setAnnotations(form) - self._doc.addForm(name, form) - self._restartAccumulators() - self.pop_state_stack() - - - def addPostScriptCommand(self, command, position=1): - """Embed literal Postscript in the document. - - With position=0, it goes at very beginning of page stream; - with position=1, at current point; and - with position=2, at very end of page stream. What that does - to the resulting Postscript depends on Adobe's header :-) - - Use with extreme caution, but sometimes needed for printer tray commands. - Acrobat 4.0 will export Postscript to a printer or file containing - the given commands. Adobe Reader 6.0 no longer does as this feature is - deprecated. 5.0, I don't know about (please let us know!). This was - funded by Bob Marshall of Vector.co.uk and tested on a Lexmark 750. - See test_pdfbase_postscript.py for 2 test cases - one will work on - any Postscript device, the other uses a 'setpapertray' command which - will error in Distiller but work on printers supporting it. - """ - #check if we've done this one already... - rawName = 'PS' + md5.md5(command).hexdigest() - regName = self._doc.getXObjectName(rawName) - psObj = self._doc.idToObject.get(regName, None) - if not psObj: - #first use of this chunk of Postscript, make an object - psObj = pdfdoc.PDFPostScriptXObject(command + '\r\n') - self._setXObjects(psObj) - self._doc.Reference(psObj, regName) - self._doc.addForm(rawName, psObj) - if position == 0: - self._psCommandsBeforePage.append("/%s Do" % regName) - elif position==1: - self._code.append("/%s Do" % regName) - else: - self._psCommandsAfterPage.append("/%s Do" % regName) - - self._formsinuse.append(rawName) - - def _absRect(self,rect,relative=0): - if not rect: - w,h = self._pagesize - rect = (0,0,w,h) - elif relative: - lx, ly, ux, uy = rect - xll,yll = self.absolutePosition(lx,ly) - xur,yur = self.absolutePosition(ux, uy) - xul,yul = self.absolutePosition(lx, uy) - xlr,ylr = self.absolutePosition(ux, ly) - xs = xll, xur, xul, xlr - ys = yll, yur, yul, ylr - xmin, ymin = min(xs), min(ys) - xmax, ymax = max(xs), max(ys) - rect = xmin, ymin, xmax, ymax - return rect - - def freeTextAnnotation(self, contents, DA, Rect=None, addtopage=1, name=None, relative=0, **kw): - """DA is the default appearance string???""" - Rect = self._absRect(Rect,relative) - self._addAnnotation(pdfdoc.FreeTextAnnotation(Rect, contents, DA, **kw), name, addtopage) - - def textAnnotation(self, contents, Rect=None, addtopage=1, name=None, relative=0, **kw): - """Experimental, but works. - """ - Rect = self._absRect(Rect,relative) - self._addAnnotation(pdfdoc.TextAnnotation(Rect, contents, **kw), name, addtopage) - textAnnotation0 = textAnnotation #deprecated - - def inkAnnotation(self, contents, InkList=None, Rect=None, addtopage=1, name=None, relative=0, **kw): - raise NotImplementedError - "Experimental" - Rect = self._absRect(Rect,relative) - if not InkList: - InkList = ((100,100,100,h-100,w-100,h-100,w-100,100),) - self._addAnnotation(pdfdoc.InkAnnotation(Rect, contents, InkList, **kw), name, addtopage) - inkAnnotation0 = inkAnnotation #deprecated - - def linkAbsolute(self, contents, destinationname, Rect=None, addtopage=1, name=None, - thickness=0, color=None, dashArray=None, **kw): - """rectangular link annotation positioned wrt the default user space. - The identified rectangle on the page becomes a "hot link" which - when clicked will send the viewer to the page and position identified - by the destination. - - Rect identifies (lowerx, lowery, upperx, uppery) for lower left - and upperright points of the rectangle. Translations and other transforms - are IGNORED (the rectangular position is given with respect - to the default user space. - destinationname should be the name of a bookmark (which may be defined later - but must be defined before the document is generated). - - You may want to use the keyword argument Border='[0 0 0]' to - suppress the visible rectangle around the during viewing link.""" - return self.linkRect(contents, destinationname, Rect, addtopage, name, relative=0, - thickness=thickness, color=color, dashArray=dashArray, **kw) - - def linkRect(self, contents, destinationname, Rect=None, addtopage=1, name=None, relative=0, - thickness=0, color=None, dashArray=None, **kw): - """rectangular link annotation w.r.t the current user transform. - if the transform is skewed/rotated the absolute rectangle will use the max/min x/y - """ - destination = self._bookmarkReference(destinationname) # permitted to be undefined... must bind later... - Rect = self._absRect(Rect,relative) - kw["Rect"] = Rect - kw["Contents"] = contents - kw["Destination"] = destination - _annFormat(kw,color,thickness,dashArray) - return self._addAnnotation(pdfdoc.LinkAnnotation(**kw), name, addtopage) - - def linkURL(self, url, rect, relative=0, thickness=0, color=None, dashArray=None, kind="URI", **kw): - """Create a rectangular URL 'hotspot' in the given rectangle. - - if relative=1, this is in the current coord system, otherwise - in absolute page space. - The remaining options affect the border appearance; the border is - drawn by Acrobat, not us. Set thickness to zero to hide it. - Any border drawn this way is NOT part of the page stream and - will not show when printed to a Postscript printer or distilled; - it is safest to draw your own.""" - from reportlab.pdfbase.pdfdoc import PDFDictionary, PDFName, PDFArray, PDFString - #tried the documented BS element in the pdf spec but it - #does not work, and Acrobat itself does not appear to use it! - - ann = PDFDictionary(dict=kw) - ann["Type"] = PDFName("Annot") - ann["Subtype"] = PDFName("Link") - ann["Rect"] = PDFArray(self._absRect(rect,relative)) # the whole page for testing - - # the action is a separate dictionary - A = PDFDictionary() - A["Type"] = PDFName("Action") # not needed? - uri = PDFString(url) - A['S'] = PDFName(kind) - if kind=="URI": - A["URI"] = uri - elif kind=='GoToR': - A["F"] = uri - A["D"] = "[ 0 /XYZ null null null ]" - else: - raise ValueError("Unknown linkURI kind '%s'" % kind) - - ann["A"] = A - _annFormat(ann,color,thickness,dashArray) - self._addAnnotation(ann) - - def _addAnnotation(self, annotation, name=None, addtopage=1): - count = self._annotationCount = self._annotationCount+1 - if not name: name="NUMBER"+repr(count) - self._doc.addAnnotation(name, annotation) - if addtopage: - self._annotatePage(name) - - def _annotatePage(self, name): - ref = self._doc.refAnnotation(name) - self._annotationrefs.append(ref) - - def getPageNumber(self): - "get the page number for the current page being generated." - return self._pageNumber - - def save(self): - """Saves and close the PDF document in the file. - If there is current data a ShowPage is executed automatically. - After this operation the canvas must not be used further.""" - if len(self._code): self.showPage() - self._doc.SaveToFile(self._filename, self) - - def getpdfdata(self): - """Returns the PDF data that would normally be written to a file. - If there is current data a ShowPage is executed automatically. - After this operation the canvas must not be used further.""" - if len(self._code): self.showPage() - return self._doc.GetPDFData(self) - - def setPageSize(self, size): - """accepts a 2-tuple in points for paper size for this - and subsequent pages""" - self._pagesize = size - self._make_preamble() - - def setPageRotation(self, rot): - """Instruct display device that this page is to be rotated""" - assert rot % 90.0 == 0.0, "Rotation must be a multiple of 90 degrees" - self._pageRotation = rot - - def addLiteral(self, s, escaped=1): - """introduce the literal text of PDF operations s into the current stream. - Only use this if you are an expert in the PDF file format.""" - s = str(s) # make sure its a string - if escaped==0: - s = self._escape(s) # convert to string for safety - self._code.append(s) - - ###################################################################### - # - # coordinate transformations - # - ###################################################################### - def resetTransforms(self): - """I want to draw something (eg, string underlines) w.r.t. the default user space. - Reset the matrix! This should be used usually as follows: - canv.saveState() - canv.resetTransforms() - ...draw some stuff in default space coords... - canv.restoreState() # go back! - """ - # we have to adjoin the inverse, since reset is not a basic operation (without save/restore) - (selfa, selfb, selfc, selfd, selfe, selff) = self._currentMatrix - det = selfa*selfd - selfc*selfb - resulta = selfd/det - resultc = -selfc/det - resulte = (selfc*selff - selfd*selfe)/det - resultd = selfa/det - resultb = -selfb/det - resultf = (selfe*selfb - selff*selfa)/det - self.transform(resulta, resultb, resultc, resultd, resulte, resultf) - - def transform(self, a,b,c,d,e,f): - """adjoin a mathematical transform to the current graphics state matrix. - Not recommended for beginners.""" - #"""How can Python track this?""" - if ENABLE_TRACKING: - a0,b0,c0,d0,e0,f0 = self._currentMatrix - self._currentMatrix = (a0*a+c0*b, b0*a+d0*b, - a0*c+c0*d, b0*c+d0*d, - a0*e+c0*f+e0, b0*e+d0*f+f0) - if self._code and self._code[-1][-3:]==' cm': - L = split(self._code[-1]) - a0, b0, c0, d0, e0, f0 = map(float,L[-7:-1]) - s = len(L)>7 and join(L)+ ' %s cm' or '%s cm' - self._code[-1] = s % fp_str(a0*a+c0*b,b0*a+d0*b,a0*c+c0*d,b0*c+d0*d,a0*e+c0*f+e0,b0*e+d0*f+f0) - else: - self._code.append('%s cm' % fp_str(a,b,c,d,e,f)) - ### debug -## (a,b,c,d,e,f) = self.Kolor -## self.Kolor = (f,a,b,c,d,e) -## self.setStrokeColorRGB(f,a,b) -## self.setFillColorRGB(f,a,b) -## self.line(-90,-1000,1,1); self.line(1000,-90,-1,1) -## self.drawString(0,0,"here") -## Kolor = (0, 0.5, 1, 0.25, 0.7, 0.3) - - def absolutePosition(self, x, y): - """return the absolute position of x,y in user space w.r.t. default user space""" - if not ENABLE_TRACKING: - raise ValueError, "tracking not enabled! (canvas.ENABLE_TRACKING=0)" - (a,b,c,d,e,f) = self._currentMatrix - xp = a*x + c*y + e - yp = b*x + d*y + f - return (xp, yp) - - def translate(self, dx, dy): - """move the origin from the current (0,0) point to the (dx,dy) point - (with respect to the current graphics state).""" - self.transform(1,0,0,1,dx,dy) - - def scale(self, x, y): - """Scale the horizontal dimension by x and the vertical by y - (with respect to the current graphics state). - For example canvas.scale(2.0, 0.5) will make everything short and fat.""" - self.transform(x,0,0,y,0,0) - - def rotate(self, theta): - """Canvas.rotate(theta) - - Rotate the canvas by the angle theta (in degrees).""" - c = cos(theta * pi / 180) - s = sin(theta * pi / 180) - self.transform(c, s, -s, c, 0, 0) - - def skew(self, alpha, beta): - tanAlpha = tan(alpha * pi / 180) - tanBeta = tan(beta * pi / 180) - self.transform(1, tanAlpha, tanBeta, 1, 0, 0) - - ###################################################################### - # - # graphics state management - # - ###################################################################### - - def saveState(self): - """Save the current graphics state to be restored later by restoreState. - - For example: - canvas.setFont("Helvetica", 20) - canvas.saveState() - ... - canvas.setFont("Courier", 9) - ... - canvas.restoreState() - # if the save/restore pairs match then font is Helvetica 20 again. - """ - self.push_state_stack() - self._code.append('q') - - def restoreState(self): - """restore the graphics state to the matching saved state (see saveState).""" - self._code.append('Q') - self.pop_state_stack() - - ############################################################### - # - # Drawing methods. These draw things directly without - # fiddling around with Path objects. We can add any geometry - # methods we wish as long as their meaning is precise and - # they are of general use. - # - # In general there are two patterns. Closed shapes - # have the pattern shape(self, args, stroke=1, fill=0); - # by default they draw an outline only. Line segments come - # in three flavours: line, bezier, arc (which is a segment - # of an elliptical arc, approximated by up to four bezier - # curves, one for each quadrant. - # - # In the case of lines, we provide a 'plural' to unroll - # the inner loop; it is useful for drawing big grids - ################################################################ - - #--------first the line drawing methods----------------------- - - def line(self, x1,y1, x2,y2): - """draw a line segment from (x1,y1) to (x2,y2) (with color, thickness and - other attributes determined by the current graphics state).""" - self._code.append('n %s m %s l S' % (fp_str(x1, y1), fp_str(x2, y2))) - - def lines(self, linelist): - """Like line(), permits many lines to be drawn in one call. - for example for the figure - | - -- -- - | - - crosshairs = [(20,0,20,10), (20,30,20,40), (0,20,10,20), (30,20,40,20)] - canvas.lines(crosshairs) - """ - self._code.append('n') - for (x1,y1,x2,y2) in linelist: - self._code.append('%s m %s l' % (fp_str(x1, y1), fp_str(x2, y2))) - self._code.append('S') - - def grid(self, xlist, ylist): - """Lays out a grid in current line style. Supply list of - x an y positions.""" - assert len(xlist) > 1, "x coordinate list must have 2+ items" - assert len(ylist) > 1, "y coordinate list must have 2+ items" - lines = [] - y0, y1 = ylist[0], ylist[-1] - x0, x1 = xlist[0], xlist[-1] - for x in xlist: - lines.append((x,y0,x,y1)) - for y in ylist: - lines.append((x0,y,x1,y)) - self.lines(lines) - - def bezier(self, x1, y1, x2, y2, x3, y3, x4, y4): - "Bezier curve with the four given control points" - self._code.append('n %s m %s c S' % - (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4)) - ) - def arc(self, x1,y1, x2,y2, startAng=0, extent=90): - """Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, - starting at startAng degrees and covering extent degrees. Angles - start with 0 to the right (+x) and increase counter-clockwise. - These should have x1.""" - - pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) - #move to first point - self._code.append('n %s m' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code.append('%s c' % fp_str(curve[2:])) - # stroke - self._code.append('S') - - #--------now the shape drawing methods----------------------- - - def rect(self, x, y, width, height, stroke=1, fill=0): - "draws a rectangle with lower left corner at (x,y) and width and height as given." - self._code.append('n %s re ' % fp_str(x, y, width, height) - + PATH_OPS[stroke, fill, self._fillMode]) - - def ellipse(self, x1, y1, x2, y2, stroke=1, fill=0): - """Draw an ellipse defined by an enclosing rectangle. - - Note that (x1,y1) and (x2,y2) are the corner points of - the enclosing rectangle. - - Uses bezierArc, which conveniently handles 360 degrees. - Special thanks to Robert Kern.""" - - pointList = pdfgeom.bezierArc(x1,y1, x2,y2, 0, 360) - #move to first point - self._code.append('n %s m' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code.append('%s c' % fp_str(curve[2:])) - #finish - self._code.append(PATH_OPS[stroke, fill, self._fillMode]) - - def wedge(self, x1,y1, x2,y2, startAng, extent, stroke=1, fill=0): - """Like arc, but connects to the centre of the ellipse. - Most useful for pie charts and PacMan!""" - - x_cen = (x1+x2)/2. - y_cen = (y1+y2)/2. - pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) - - self._code.append('n %s m' % fp_str(x_cen, y_cen)) - # Move the pen to the center of the rectangle - self._code.append('%s l' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code.append('%s c' % fp_str(curve[2:])) - # finish the wedge - self._code.append('%s l ' % fp_str(x_cen, y_cen)) - # final operator - self._code.append(PATH_OPS[stroke, fill, self._fillMode]) - - def circle(self, x_cen, y_cen, r, stroke=1, fill=0): - """draw a cirle centered at (x_cen,y_cen) with radius r (special case of ellipse)""" - - x1 = x_cen - r - x2 = x_cen + r - y1 = y_cen - r - y2 = y_cen + r - self.ellipse(x1, y1, x2, y2, stroke, fill) - - def roundRect(self, x, y, width, height, radius, stroke=1, fill=0): - """Draws a rectangle with rounded corners. The corners are - approximately quadrants of a circle, with the given radius.""" - #use a precomputed set of factors for the bezier approximation - #to a circle. There are six relevant points on the x axis and y axis. - #sketch them and it should all make sense! - t = 0.4472 * radius - - x0 = x - x1 = x0 + t - x2 = x0 + radius - x3 = x0 + width - radius - x4 = x0 + width - t - x5 = x0 + width - - y0 = y - y1 = y0 + t - y2 = y0 + radius - y3 = y0 + height - radius - y4 = y0 + height - t - y5 = y0 + height - - self._code.append('n %s m' % fp_str(x2, y0)) - self._code.append('%s l' % fp_str(x3, y0)) # bottom row - self._code.append('%s c' - % fp_str(x4, y0, x5, y1, x5, y2)) # bottom right - - self._code.append('%s l' % fp_str(x5, y3)) # right edge - self._code.append('%s c' - % fp_str(x5, y4, x4, y5, x3, y5)) # top right - - self._code.append('%s l' % fp_str(x2, y5)) # top row - self._code.append('%s c' - % fp_str(x1, y5, x0, y4, x0, y3)) # top left - - self._code.append('%s l' % fp_str(x0, y2)) # left edge - self._code.append('%s c' - % fp_str(x0, y1, x1, y0, x2, y0)) # bottom left - - self._code.append('h') #close off, although it should be where it started anyway - - self._code.append(PATH_OPS[stroke, fill, self._fillMode]) - - ################################################## - # - # Text methods - # - # As with graphics, a separate object ensures that - # everything is bracketed between text operators. - # The methods below are a high-level convenience. - # use PDFTextObject for multi-line text. - ################################################## - - def drawString(self, x, y, text): - """Draws a string in the current text styles.""" - #we could inline this for speed if needed - t = self.beginText(x, y) - t.textLine(text) - self.drawText(t) - - def drawRightString(self, x, y, text): - """Draws a string right-aligned with the x coordinate""" - width = self.stringWidth(text, self._fontname, self._fontsize) - t = self.beginText(x - width, y) - t.textLine(text) - self.drawText(t) - - def drawCentredString(self, x, y, text): - """Draws a string centred on the x coordinate.""" - width = self.stringWidth(text, self._fontname, self._fontsize) - t = self.beginText(x - 0.5*width, y) - t.textLine(text) - self.drawText(t) - - def drawAlignedString(self, x, y, text, pivotChar="."): - """Draws a string aligned on the first '.' (or other pivot character). - - The centre position of the pivot character will be used as x. - So, you could draw a straight line down through all the decimals in a - column of numbers, and anything without a decimal should be - optically aligned with those that have. - - There is one special rule to help with accounting formatting. Here's - how normal numbers should be aligned on the 'dot'. Look at the - LAST two: - 12,345,67 - 987.15 - 42 - -1,234.56 - (456.78) - (456) - 27 inches - 13cm - Since the last three do not contain a dot, a crude dot-finding - rule would place them wrong. So we test for the special case - where no pivot is found, digits are present, but the last character - is not a digit. We then work back from the end of the string - This case is a tad slower but hopefully rare. - - """ - parts = text.split(pivotChar,1) - pivW = self.stringWidth(pivotChar, self._fontname, self._fontsize) - - if len(parts) == 1 and digitPat.search(text) is not None and text[-1] not in digits: - #we have no decimal but it ends in a bracket, or 'in' or something. - #the cut should be after the last digit. - leftText = parts[0][0:-1] - rightText = parts[0][-1] - #any more? - while leftText[-1] not in digits: - rightText = leftText[-1] + rightText - leftText = leftText[0:-1] - - self.drawRightString(x-0.5*pivW, y, leftText) - self.drawString(x-0.5*pivW, y, rightText) - - else: - #normal case - leftText = parts[0] - self.drawRightString(x-0.5*pivW, y, leftText) - if len(parts) > 1: - rightText = pivotChar + parts[1] - self.drawString(x-0.5*pivW, y, rightText) - - def getAvailableFonts(self): - """Returns the list of PostScript font names available. - - Standard set now, but may grow in future with font embedding.""" - fontnames = self._doc.getAvailableFonts() - fontnames.sort() - return fontnames - - def addFont(self, fontObj): - "add a new font for subsequent use." - self._doc.addFont(fontObj) - - def _addStandardFonts(self): - """Ensures the standard 14 fonts are available in the system encoding. - Called by canvas on initialization""" - for fontName in pdfmetrics.standardFonts: - self.addFont(pdfmetrics.fontsByName[fontName]) - - def listLoadedFonts0(self): - "Convenience function to list all loaded fonts" - names = pdfmetrics.widths.keys() - names.sort() - return names - - def setFont(self, psfontname, size, leading = None): - """Sets the font. If leading not specified, defaults to 1.2 x - font size. Raises a readable exception if an illegal font - is supplied. Font names are case-sensitive! Keeps track - of font name and size for metrics.""" - self._fontname = psfontname - self._fontsize = size - if leading is None: - leading = size * 1.2 - self._leading = leading - font = pdfmetrics.getFont(self._fontname) - - self._dynamicFont = getattr(font, '_dynamicFont', 0) - if not self._dynamicFont: - pdffontname = self._doc.getInternalFontName(psfontname) - self._code.append('BT %s %s Tf %s TL ET' % (pdffontname, fp_str(size), fp_str(leading))) - - def setFontSize(self, size=None, leading=None): - '''Sets font size or leading without knowing the font face''' - if size is None: size = self._fontsize - if leading is None: leading = self._leading - self.setFont(self._fontname, size, leading) - - def stringWidth(self, text, fontName, fontSize): - "gets width of a string in the given font and size" - return pdfmetrics.stringWidth(text, fontName, fontSize) - - # basic graphics modes - - def setLineWidth(self, width): - self._lineWidth = width - self._code.append('%s w' % fp_str(width)) - - def setLineCap(self, mode): - """0=butt,1=round,2=square""" - assert mode in (0,1,2), "Line caps allowed: 0=butt,1=round,2=square" - self._lineCap = mode - self._code.append('%d J' % mode) - - def setLineJoin(self, mode): - """0=mitre, 1=round, 2=bevel""" - assert mode in (0,1,2), "Line Joins allowed: 0=mitre, 1=round, 2=bevel" - self._lineJoin = mode - self._code.append('%d j' % mode) - - def setMiterLimit(self, limit): - self._miterLimit = limit - self._code.append('%s M' % fp_str(limit)) - - def setDash(self, array=[], phase=0): - """Two notations. pass two numbers, or an array and phase""" - if type(array) == IntType or type(array) == FloatType: - self._code.append('[%s %s] 0 d' % (array, phase)) - elif type(array) == ListType or type(array) == TupleType: - assert phase >= 0, "phase is a length in user space" - textarray = ' '.join(map(str, array)) - self._code.append('[%s] %s d' % (textarray, phase)) - - # path stuff - the separate path object builds it - - def beginPath(self): - """Returns a fresh path object. Paths are used to draw - complex figures. The object returned follows the protocol - for a pathobject.PDFPathObject instance""" - return pathobject.PDFPathObject() - - def drawPath(self, aPath, stroke=1, fill=0): - "Draw the path object in the mode indicated" - gc = aPath.getCode(); pathops = PATH_OPS[stroke, fill, self._fillMode] - item = "%s %s" % (gc, pathops) # ENSURE STRING CONVERSION - self._code.append(item) - #self._code.append(aPath.getCode() + ' ' + PATH_OPS[stroke, fill, self._fillMode]) - - def clipPath(self, aPath, stroke=1, fill=0): - "clip as well as drawing" - gc = aPath.getCode(); pathops = PATH_OPS[stroke, fill, self._fillMode] - clip = (self._fillMode == FILL_EVEN_ODD and ' W* ' or ' W ') - item = "%s%s%s" % (gc, clip, pathops) # ensure string conversion - self._code.append(item) - #self._code.append( aPath.getCode() - # + (self._fillMode == FILL_EVEN_ODD and ' W* ' or ' W ') - # + PATH_OPS[stroke,fill,self._fillMode]) - - def beginText(self, x=0, y=0): - """Returns a fresh text object. Text objects are used - to add large amounts of text. See textobject.PDFTextObject""" - return textobject.PDFTextObject(self, x, y) - - def drawText(self, aTextObject): - """Draws a text object""" - self._code.append(str(aTextObject.getCode())) - - def setPageCompression(self, pageCompression=1): - """Possible values None, 1 or 0 - If None the value from rl_config will be used. - If on, the page data will be compressed, leading to much - smaller files, but takes a little longer to create the files. - This applies to all subsequent pages, or until setPageCompression() - is next called.""" - if pageCompression is None: pageCompression = rl_config.pageCompression - if pageCompression and not zlib: - self._pageCompression = 0 - else: - self._pageCompression = pageCompression - self._doc.setCompression(self._pageCompression) - - def setPageDuration(self, duration=None): - """Allows hands-off animation of presentations :-) - - If this is set to a number, in full screen mode, Acrobat Reader - will advance to the next page after this many seconds. The - duration of the transition itself (fade/flicker etc.) is controlled - by the 'duration' argument to setPageTransition; this controls - the time spent looking at the page. This is effective for all - subsequent pages.""" - - self._pageDuration = duration - - def setPageTransition(self, effectname=None, duration=1, - direction=0,dimension='H',motion='I'): - """PDF allows page transition effects for use when giving - presentations. There are six possible effects. You can - just guive the effect name, or supply more advanced options - to refine the way it works. There are three types of extra - argument permitted, and here are the allowed values: - direction_arg = [0,90,180,270] - dimension_arg = ['H', 'V'] - motion_arg = ['I','O'] (start at inside or outside) - - This table says which ones take which arguments: - - PageTransitionEffects = { - 'Split': [direction_arg, motion_arg], - 'Blinds': [dimension_arg], - 'Box': [motion_arg], - 'Wipe' : [direction_arg], - 'Dissolve' : [], - 'Glitter':[direction_arg] - } - Have fun! - """ - # This builds a Python dictionary with the right arguments - # for the Trans dictionary in the PDFPage object, - # and stores it in the variable _pageTransition. - # showPage later passes this to the setPageTransition method - # of the PDFPage object, which turns it to a PDFDictionary. - self._pageTransition = {} - if not effectname: - return - - #first check each optional argument has an allowed value - if direction in [0,90,180,270]: - direction_arg = ('Di', '/%d' % direction) - else: - raise 'PDFError', ' directions allowed are 0,90,180,270' - - if dimension in ['H', 'V']: - dimension_arg = ('Dm', '/' + dimension) - else: - raise'PDFError','dimension values allowed are H and V' - - if motion in ['I','O']: - motion_arg = ('M', '/' + motion) - else: - raise'PDFError','motion values allowed are I and O' - - # this says which effects require which argument types from above - PageTransitionEffects = { - 'Split': [direction_arg, motion_arg], - 'Blinds': [dimension_arg], - 'Box': [motion_arg], - 'Wipe' : [direction_arg], - 'Dissolve' : [], - 'Glitter':[direction_arg] - } - - try: - args = PageTransitionEffects[effectname] - except KeyError: - raise 'PDFError', 'Unknown Effect Name "%s"' % effectname - - # now build the dictionary - transDict = {} - transDict['Type'] = '/Trans' - transDict['D'] = '%d' % duration - transDict['S'] = '/' + effectname - for (key, value) in args: - transDict[key] = value - self._pageTransition = transDict - - def getCurrentPageContent(self): - """Return uncompressed contents of current page buffer. - - This is useful in creating test cases and assertions of what - got drawn, without necessarily saving pages to disk""" - return '\n'.join(self._code) - - - -if _instanceEscapePDF: - import new - Canvas._escape = new.instancemethod(_instanceEscapePDF,None,Canvas) - -if __name__ == '__main__': - print 'For test scripts, look in reportlab/test' diff --git a/bin/reportlab/pdfgen/pathobject.py b/bin/reportlab/pdfgen/pathobject.py deleted file mode 100755 index 2eaacc8d8e2..00000000000 --- a/bin/reportlab/pdfgen/pathobject.py +++ /dev/null @@ -1,101 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/pathobject.py -__version__=''' $Id: pathobject.py 2537 2005-03-15 14:19:29Z rgbecker $ ''' -__doc__=""" -PDFPathObject is an efficient way to draw paths on a Canvas. Do not -instantiate directly, obtain one from the Canvas instead. - -Progress Reports: -8.83, 2000-01-13, gmcm: - created from pdfgen.py -""" - -import string -from reportlab.pdfgen import pdfgeom -from reportlab.lib.utils import fp_str - - -class PDFPathObject: - """Represents a graphic path. There are certain 'modes' to PDF - drawing, and making a separate object to expose Path operations - ensures they are completed with no run-time overhead. Ask - the Canvas for a PDFPath with getNewPathObject(); moveto/lineto/ - curveto wherever you want; add whole shapes; and then add it back - into the canvas with one of the relevant operators. - - Path objects are probably not long, so we pack onto one line""" - - def __init__(self): - self._code = [] - #self._code.append('n') #newpath - self._code_append = self._init_code_append - - def _init_code_append(self,c): - assert c.endswith(' m') or c.endswith(' re'), 'path must start with a moveto or rect' - code_append = self._code.append - code_append('n') - code_append(c) - self._code_append = code_append - - def getCode(self): - "pack onto one line; used internally" - return string.join(self._code, ' ') - - def moveTo(self, x, y): - self._code_append('%s m' % fp_str(x,y)) - - def lineTo(self, x, y): - self._code_append('%s l' % fp_str(x,y)) - - def curveTo(self, x1, y1, x2, y2, x3, y3): - self._code_append('%s c' % fp_str(x1, y1, x2, y2, x3, y3)) - - def arc(self, x1,y1, x2,y2, startAng=0, extent=90): - """Contributed to piddlePDF by Robert Kern, 28/7/99. - Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, - starting at startAng degrees and covering extent degrees. Angles - start with 0 to the right (+x) and increase counter-clockwise. - These should have x1.""" - - pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) - #move to first point - self._code_append('%s m' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code_append('%s c' % fp_str(curve[2:])) - - def arcTo(self, x1,y1, x2,y2, startAng=0, extent=90): - """Like arc, but draws a line from the current point to - the start if the start is not the current point.""" - pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent) - self._code_append('%s l' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code_append('%s c' % fp_str(curve[2:])) - - def rect(self, x, y, width, height): - """Adds a rectangle to the path""" - self._code_append('%s re' % fp_str((x, y, width, height))) - - def ellipse(self, x, y, width, height): - """adds an ellipse to the path""" - pointList = pdfgeom.bezierArc(x, y, x + width,y + height, 0, 360) - self._code_append('%s m' % fp_str(pointList[0][:2])) - for curve in pointList: - self._code_append('%s c' % fp_str(curve[2:])) - - def circle(self, x_cen, y_cen, r): - """adds a circle to the path""" - x1 = x_cen - r - #x2 = x_cen + r - y1 = y_cen - r - #y2 = y_cen + r - width = height = 2*r - #self.ellipse(x_cen - r, y_cen - r, x_cen + r, y_cen + r) - self.ellipse(x1, y1, width, height) - - def close(self): - "draws a line back to where it started" - self._code_append('h') diff --git a/bin/reportlab/pdfgen/pdfgeom.py b/bin/reportlab/pdfgen/pdfgeom.py deleted file mode 100755 index 34ef826b0b8..00000000000 --- a/bin/reportlab/pdfgen/pdfgeom.py +++ /dev/null @@ -1,77 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/pdfgeom.py -__version__=''' $Id: pdfgeom.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__=""" -This module includes any mathematical methods needed for PIDDLE. -It should have no dependencies beyond the Python library. - -So far, just Robert Kern's bezierArc. -""" - -from math import sin, cos, pi, ceil - - -def bezierArc(x1,y1, x2,y2, startAng=0, extent=90): - """bezierArc(x1,y1, x2,y2, startAng=0, extent=90) --> List of Bezier -curve control points. - -(x1, y1) and (x2, y2) are the corners of the enclosing rectangle. The -coordinate system has coordinates that increase to the right and down. -Angles, measured in degress, start with 0 to the right (the positive X -axis) and increase counter-clockwise. The arc extends from startAng -to startAng+extent. I.e. startAng=0 and extent=180 yields an openside-down -semi-circle. - -The resulting coordinates are of the form (x1,y1, x2,y2, x3,y3, x4,y4) -such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and -(x3, y3) as their respective Bezier control points.""" - - x1,y1, x2,y2 = min(x1,x2), max(y1,y2), max(x1,x2), min(y1,y2) - - if abs(extent) <= 90: - arcList = [startAng] - fragAngle = float(extent) - Nfrag = 1 - else: - arcList = [] - Nfrag = int(ceil(abs(extent)/90.)) - fragAngle = float(extent) / Nfrag - - x_cen = (x1+x2)/2. - y_cen = (y1+y2)/2. - rx = (x2-x1)/2. - ry = (y2-y1)/2. - halfAng = fragAngle * pi / 360. - kappa = abs(4. / 3. * (1. - cos(halfAng)) / sin(halfAng)) - - if fragAngle < 0: - sign = -1 - else: - sign = 1 - - pointList = [] - - for i in range(Nfrag): - theta0 = (startAng + i*fragAngle) * pi / 180. - theta1 = (startAng + (i+1)*fragAngle) *pi / 180. - if fragAngle > 0: - pointList.append((x_cen + rx * cos(theta0), - y_cen - ry * sin(theta0), - x_cen + rx * (cos(theta0) - kappa * sin(theta0)), - y_cen - ry * (sin(theta0) + kappa * cos(theta0)), - x_cen + rx * (cos(theta1) + kappa * sin(theta1)), - y_cen - ry * (sin(theta1) - kappa * cos(theta1)), - x_cen + rx * cos(theta1), - y_cen - ry * sin(theta1))) - else: - pointList.append((x_cen + rx * cos(theta0), - y_cen - ry * sin(theta0), - x_cen + rx * (cos(theta0) + kappa * sin(theta0)), - y_cen - ry * (sin(theta0) - kappa * cos(theta0)), - x_cen + rx * (cos(theta1) - kappa * sin(theta1)), - y_cen - ry * (sin(theta1) + kappa * cos(theta1)), - x_cen + rx * cos(theta1), - y_cen - ry * sin(theta1))) - - return pointList \ No newline at end of file diff --git a/bin/reportlab/pdfgen/pdfimages.py b/bin/reportlab/pdfgen/pdfimages.py deleted file mode 100644 index f9e010314aa..00000000000 --- a/bin/reportlab/pdfgen/pdfimages.py +++ /dev/null @@ -1,188 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/pdfimages.py -__version__=''' $Id: pdfimages.py 2744 2005-12-15 12:28:18Z rgbecker $ ''' -__doc__=""" -Image functionality sliced out of canvas.py for generalization -""" - -import os -import string -from types import StringType -import reportlab -from reportlab.pdfbase import pdfutils -from reportlab.pdfbase import pdfdoc -from reportlab.lib.utils import fp_str, getStringIO -from reportlab.lib.utils import import_zlib, haveImages - - -class PDFImage: - """Wrapper around different "image sources". You can make images - from a PIL Image object, a filename (in which case it uses PIL), - an image we previously cached (optimisation, hardly used these - days) or a JPEG (which PDF supports natively).""" - - def __init__(self, image, x,y, width=None, height=None, caching=0): - self.image = image - self.point = (x,y) - self.dimensions = (width, height) - self.filename = None - self.imageCaching = caching - # the following facts need to be determined, - # whatever the source. Declare what they are - # here for clarity. - self.colorSpace = 'DeviceRGB' - self.bitsPerComponent = 8 - self.filters = [] - self.source = None # JPEG or PIL, set later - self.getImageData() - - def jpg_imagedata(self): - #directly process JPEG files - #open file, needs some error handling!! - fp = open(self.image, 'rb') - result = self._jpg_imagedata(fp) - fp.close() - return result - - def _jpg_imagedata(self,imageFile): - self.source = 'JPEG' - info = pdfutils.readJPEGInfo(imageFile) - imgwidth, imgheight = info[0], info[1] - if info[2] == 1: - colorSpace = 'DeviceGray' - elif info[2] == 3: - colorSpace = 'DeviceRGB' - else: #maybe should generate an error, is this right for CMYK? - colorSpace = 'DeviceCMYK' - imageFile.seek(0) #reset file pointer - imagedata = [] - #imagedata.append('BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /DCTDecode] ID' % (info[0], info[1], colorSpace)) - imagedata.append('BI /W %d /H %d /BPC 8 /CS /%s /F [/A85 /DCT] ID' % (imgwidth, imgheight, colorSpace)) - #write in blocks of (??) 60 characters per line to a list - compressed = imageFile.read() - encoded = pdfutils._AsciiBase85Encode(compressed) - pdfutils._chunker(encoded,imagedata) - imagedata.append('EI') - return (imagedata, imgwidth, imgheight) - - def cache_imagedata(self): - image = self.image - if not pdfutils.cachedImageExists(image): - zlib = import_zlib() - if not zlib: return - if not haveImages: return - pdfutils.cacheImageFile(image) - - #now we have one cached, slurp it in - cachedname = os.path.splitext(image)[0] + '.a85' - imagedata = open(cachedname,'rb').readlines() - #trim off newlines... - imagedata = map(string.strip, imagedata) - return imagedata - - def PIL_imagedata(self): - image = self.image - if image.format=='JPEG': - fp=image.fp - fp.seek(0) - return self._jpg_imagedata(fp) - self.source = 'PIL' - zlib = import_zlib() - if not zlib: return - myimage = image.convert('RGB') - imgwidth, imgheight = myimage.size - - # this describes what is in the image itself - # *NB* according to the spec you can only use the short form in inline images - #imagedata=['BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /FlateDecode] ID]' % (imgwidth, imgheight,'RGB')] - imagedata=['BI /W %d /H %d /BPC 8 /CS /RGB /F [/A85 /Fl] ID' % (imgwidth, imgheight)] - - #use a flate filter and Ascii Base 85 to compress - raw = myimage.tostring() - assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image") - compressed = zlib.compress(raw) #this bit is very fast... - encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this may not be - #append in blocks of 60 characters - pdfutils._chunker(encoded,imagedata) - imagedata.append('EI') - return (imagedata, imgwidth, imgheight) - - def getImageData(self): - "Gets data, height, width - whatever type of image" - image = self.image - (width, height) = self.dimensions - - if type(image) == StringType: - self.filename = image - if os.path.splitext(image)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']: - (imagedata, imgwidth, imgheight) = self.jpg_imagedata() - else: - if not self.imageCaching: - imagedata = pdfutils.cacheImageFile(image,returnInMemory=1) - else: - imagedata = self.cache_imagedata() - #parse line two for width, height - words = string.split(imagedata[1]) - imgwidth = string.atoi(words[1]) - imgheight = string.atoi(words[3]) - else: - import sys - if sys.platform[0:4] == 'java': - #jython, PIL not available - (imagedata, imgwidth, imgheight) = self.JAVA_imagedata() - else: - (imagedata, imgwidth, imgheight) = self.PIL_imagedata() - #now build the PDF for the image. - if not width: - width = imgwidth - if not height: - height = imgheight - self.width = width - self.height = height - self.imageData = imagedata - - def drawInlineImage(self, canvas): #, image, x,y, width=None,height=None): - """Draw an Image into the specified rectangle. If width and - height are omitted, they are calculated from the image size. - Also allow file names as well as images. This allows a - caching mechanism""" - if self.width<1e-6 or self.height<1e-6: return False - (x,y) = self.point - # this says where and how big to draw it - if not canvas.bottomup: y = y+self.height - canvas._code.append('q %s 0 0 %s cm' % (fp_str(self.width), fp_str(self.height, x, y))) - # self._code.extend(imagedata) if >=python-1.5.2 - for line in self.imageData: - canvas._code.append(line) - canvas._code.append('Q') - return True - - def format(self, document): - """Allow it to be used within pdfdoc framework. This only - defines how it is stored, not how it is drawn later.""" - - dict = pdfdoc.PDFDictionary() - dict['Type'] = '/XObject' - dict['Subtype'] = '/Image' - dict['Width'] = self.width - dict['Height'] = self.height - dict['BitsPerComponent'] = 8 - dict['ColorSpace'] = pdfdoc.PDFName(self.colorSpace) - content = string.join(self.imageData[3:-1], '\n') + '\n' - strm = pdfdoc.PDFStream(dictionary=dict, content=content) - return strm.format(document) - -if __name__=='__main__': - srcfile = os.path.join( - os.path.dirname(reportlab.__file__), - 'test', - 'pythonpowered.gif' - ) - assert os.path.isfile(srcfile), 'image not found' - pdfdoc.LongFormat = 1 - img = PDFImage(srcfile, 100, 100) - import pprint - doc = pdfdoc.PDFDocument() - print 'source=',img.source - print img.format(doc) diff --git a/bin/reportlab/pdfgen/pycanvas.py b/bin/reportlab/pdfgen/pycanvas.py deleted file mode 100644 index 849690eb1f2..00000000000 --- a/bin/reportlab/pdfgen/pycanvas.py +++ /dev/null @@ -1,309 +0,0 @@ -# a Pythonesque Canvas v0.8 -# Author : Jerome Alet - -# License : ReportLab's license -# -# $Id: pycanvas.py 1821 2002-11-06 17:11:31Z rgbecker $ -# -__doc__ = """pycanvas.Canvas : a Canvas class which can also output Python source code. - -pycanvas.Canvas class works exactly like canvas.Canvas, but you can -call str() on pycanvas.Canvas instances. Doing so will return the -Python source code equivalent to your own program, which would, when -run, produce the same PDF document as your original program. - -Generated Python source code defines a doIt() function which accepts -a filename or file-like object as its first parameter, and an -optional boolean parameter named "regenerate". - -The doIt() function will generate a PDF document and save it in the -file you specified in this argument. If the regenerate parameter is -set then it will also return an automatically generated equivalent -Python source code as a string of text, which you can run again to -produce the very same PDF document and the Python source code, which -you can run again... ad nauseam ! If the regenerate parameter is -unset or not used at all (it then defaults to being unset) then None -is returned and the doIt() function is much much faster, it is also -much faster than the original non-serialized program. - -the reportlab/test/test_pdfgen_pycanvas.py program is the test suite -for pycanvas, you can do the following to run it : - - First set verbose=1 in reportlab/rl_config.py - - then from the command interpreter : - - $ cd reportlab/test - $ python test_pdfgen_pycanvas.py >n1.py - - this will produce both n1.py and test_pdfgen_pycanvas.pdf - - then : - - $ python n1.py n1.pdf >n2.py - $ python n2.py n2.pdf >n3.py - $ ... - - n1.py, n2.py, n3.py and so on will be identical files. - they eventually may end being a bit different because of - rounding problems, mostly in the comments, but this - doesn't matter since the values really are the same - (e.g. 0 instead of 0.0, or .53 instead of 0.53) - - n1.pdf, n2.pdf, n3.pdf and so on will be PDF files - similar to test_pdfgen_pycanvas.pdf. - -Alternatively you can import n1.py (or n3.py, or n16384.py if you prefer) -in your own program, and then call its doIt function : - - import n1 - pythonsource = n1.doIt("myfile.pdf", regenerate=1) - -Or if you don't need the python source code and want a faster result : - - import n1 - n1.doIt("myfile.pdf") - -When the generated source code is run directly as an independant program, -then the equivalent python source code is printed to stdout, e.g. : - - python n1.py - - will print the python source code equivalent to n1.py - -Why would you want to use such a beast ? - - - To linearize (serialize?) a program : optimizing some complex - parts for example. - - - To debug : reading the generated Python source code may help you or - the ReportLab team to diagnose problems. The generated code is now - clearly commented and shows nesting levels, page numbers, and so - on. You can use the generated script when asking for support : we - can see the results you obtain without needing your datas or complete - application. - - - To create standalone scripts : say your program uses a high level - environment to generate its output (databases, RML, etc...), using - this class would give you an equivalent program but with complete - independance from the high level environment (e.g. if you don't - have Oracle). - - - To contribute some nice looking PDF documents to the ReportLab website - without having to send a complete application you don't want to - distribute. - - - ... Insert your own ideas here ... - - - For fun because you can do it ! -""" - -import cStringIO -from reportlab.pdfgen import canvas -from reportlab.pdfgen import pathobject -from reportlab.pdfgen import textobject - -PyHeader = '''#! /usr/bin/env python - -# -# This code was entirely generated by ReportLab (http://www.reportlab.com) -# - -import sys -from reportlab.pdfgen import pathobject -from reportlab.pdfgen import textobject -from reportlab.lib.colors import Color - -def doIt(file, regenerate=0) : - """Generates a PDF document, save it into file. - - file : either a filename or a file-like object. - - regenerate : if set then this function returns the Python source - code which when run will produce the same result. - if unset then this function returns None, and is - much faster. - """ - if regenerate : - from reportlab.pdfgen.pycanvas import Canvas - else : - from reportlab.pdfgen.canvas import Canvas -''' - -PyFooter = ''' - # if we want the equivalent Python source code, then send it back - if regenerate : - return str(c) - -if __name__ == "__main__" : - if len(sys.argv) != 2 : - # second argument must be the name of the PDF file to create - sys.stderr.write("%s needs one and only one argument\\n" % sys.argv[0]) - sys.exit(-1) - else : - # we've got a filename, we can proceed. - print doIt(sys.argv[1], regenerate=1) - sys.exit(0)''' - -def buildargs(*args, **kwargs) : - """Constructs a printable list of arguments suitable for use in source function calls.""" - arguments = "" - for arg in args : - arguments = arguments + ("%s, " % repr(arg)) - for (kw, val) in kwargs.items() : - arguments = arguments+ ("%s=%s, " % (kw, repr(val))) - if arguments[-2:] == ", " : - arguments = arguments[:-2] - return arguments - -class PDFAction : - """Base class to fake method calls or attributes on PDF objects (Canvas, PDFPathObject, PDFTextObject).""" - def __init__(self, parent, action) : - """Saves a pointer to the parent object, and the method name.""" - self._parent = parent - self._action = action - - def __getattr__(self, name) : - """Probably a method call on an attribute, returns the real one.""" - return getattr(getattr(self._parent._object, self._action), name) - - def __call__(self, *args, **kwargs) : - """The fake method is called, print it then call the real one.""" - if not self._parent._parent._in : - self._precomment() - self._parent._parent._PyWrite(" %s.%s(%s)" % (self._parent._name, self._action, apply(buildargs, args, kwargs))) - self._postcomment() - self._parent._parent._in = self._parent._parent._in + 1 - retcode = apply(getattr(self._parent._object, self._action), args, kwargs) - self._parent._parent._in = self._parent._parent._in - 1 - return retcode - - def __hash__(self) : - return hash(getattr(self._parent._object, self._action)) - - def __coerce__(self, other) : - """Needed.""" - return coerce(getattr(self._parent._object, self._action), other) - - def _precomment(self) : - """To be overriden.""" - pass - - def _postcomment(self) : - """To be overriden.""" - pass - -class PDFObject : - """Base class for PDF objects like PDFPathObject and PDFTextObject.""" - _number = 0 - def __init__(self, parent) : - """Saves a pointer to the parent Canvas.""" - self._parent = parent - self._initdone = 0 - - def __getattr__(self, name) : - """The user's programs wants to call one of our methods or get an attribute, fake it.""" - return PDFAction(self, name) - - def __repr__(self) : - """Returns the name used in the generated source code (e.g. 'p' or 't').""" - return self._name - - def __call__(self, *args, **kwargs) : - """Real object initialisation is made here, because now we've got the arguments.""" - if not self._initdone : - self.__class__._number = self.__class__._number + 1 - methodname = apply(self._postinit, args, kwargs) - self._parent._PyWrite("\n # create PDF%sObject number %i\n %s = %s.%s(%s)" % (methodname[5:], self.__class__._number, self._name, self._parent._name, methodname, apply(buildargs, args, kwargs))) - self._initdone = 1 - return self - -class Canvas : - """Our fake Canvas class, which will intercept each and every method or attribute access.""" - class TextObject(PDFObject) : - _name = "t" - def _postinit(self, *args, **kwargs) : - self._object = apply(textobject.PDFTextObject, (self._parent, ) + args, kwargs) - return "beginText" - - class PathObject(PDFObject) : - _name = "p" - def _postinit(self, *args, **kwargs) : - self._object = apply(pathobject.PDFPathObject, args, kwargs) - return "beginPath" - - class Action(PDFAction) : - """Class called for every Canvas method call.""" - def _precomment(self) : - """Outputs comments before the method call.""" - if self._action == "showPage" : - self._parent._PyWrite("\n # Ends page %i" % self._parent._pagenumber) - elif self._action == "saveState" : - state = {} - d = self._parent._object.__dict__ - for name in self._parent._object.STATE_ATTRIBUTES: - state[name] = d[name] - self._parent._PyWrite("\n # Saves context level %i %s" % (self._parent._contextlevel, state)) - self._parent._contextlevel = self._parent._contextlevel + 1 - elif self._action == "restoreState" : - self._parent._contextlevel = self._parent._contextlevel - 1 - self._parent._PyWrite("\n # Restores context level %i %s" % (self._parent._contextlevel, self._parent._object.state_stack[-1])) - elif self._action == "beginForm" : - self._parent._formnumber = self._parent._formnumber + 1 - self._parent._PyWrite("\n # Begins form %i" % self._parent._formnumber) - elif self._action == "endForm" : - self._parent._PyWrite("\n # Ends form %i" % self._parent._formnumber) - elif self._action == "save" : - self._parent._PyWrite("\n # Saves the PDF document to disk") - - def _postcomment(self) : - """Outputs comments after the method call.""" - if self._action == "showPage" : - self._parent._pagenumber = self._parent._pagenumber + 1 - self._parent._PyWrite("\n # Begins page %i" % self._parent._pagenumber) - elif self._action in [ "endForm", "drawPath", "clipPath" ] : - self._parent._PyWrite("") - - _name = "c" - def __init__(self, *args, **kwargs) : - """Initialize and begins source code.""" - self._parent = self # nice trick, isn't it ? - self._in = 0 - self._contextlevel = 0 - self._pagenumber = 1 - self._formnumber = 0 - self._footerpresent = 0 - self._object = apply(canvas.Canvas, args, kwargs) - self._pyfile = cStringIO.StringIO() - self._PyWrite(PyHeader) - try : - del kwargs["filename"] - except KeyError : - pass - self._PyWrite(" # create the PDF document\n %s = Canvas(file, %s)\n\n # Begins page 1" % (self._name, apply(buildargs, args[1:], kwargs))) - - def __nonzero__(self) : - """This is needed by platypus' tables.""" - return 1 - - def __str__(self) : - """Returns the equivalent Python source code.""" - if not self._footerpresent : - self._PyWrite(PyFooter) - self._footerpresent = 1 - return self._pyfile.getvalue() - - def __getattr__(self, name) : - """Method or attribute access.""" - if name == "beginPath" : - return self.PathObject(self) - elif name == "beginText" : - return self.TextObject(self) - else : - return self.Action(self, name) - - def _PyWrite(self, pycode) : - """Outputs the source code with a trailing newline.""" - self._pyfile.write("%s\n" % pycode) - -if __name__ == '__main__': - print 'For test scripts, look in reportlab/test' diff --git a/bin/reportlab/pdfgen/textobject.py b/bin/reportlab/pdfgen/textobject.py deleted file mode 100755 index 32affe605d3..00000000000 --- a/bin/reportlab/pdfgen/textobject.py +++ /dev/null @@ -1,398 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/pdfgen/textobject.py -__version__=''' $Id: textobject.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' -__doc__=""" -PDFTextObject is an efficient way to add text to a Canvas. Do not -instantiate directly, obtain one from the Canvas instead. - -Progress Reports: -8.83, 2000-01-13, gmcm: - created from pdfgen.py -""" - -import string -from types import * -from reportlab.lib.colors import Color, CMYKColor, toColor -from reportlab.lib.utils import fp_str -from reportlab.pdfbase import pdfmetrics - -_SeqTypes=(TupleType,ListType) - -class _PDFColorSetter: - '''Abstracts the color setting operations; used in Canvas and Textobject - asseumes we have a _code object''' - def setFillColorCMYK(self, c, m, y, k): - """set the fill color useing negative color values - (cyan, magenta, yellow and darkness value). - Takes 4 arguments between 0.0 and 1.0""" - self._fillColorCMYK = (c, m, y, k) - self._code.append('%s k' % fp_str(c, m, y, k)) - - def setStrokeColorCMYK(self, c, m, y, k): - """set the stroke color useing negative color values - (cyan, magenta, yellow and darkness value). - Takes 4 arguments between 0.0 and 1.0""" - self._strokeColorCMYK = (c, m, y, k) - self._code.append('%s K' % fp_str(c, m, y, k)) - - def setFillColorRGB(self, r, g, b): - """Set the fill color using positive color description - (Red,Green,Blue). Takes 3 arguments between 0.0 and 1.0""" - self._fillColorRGB = (r, g, b) - self._code.append('%s rg' % fp_str(r,g,b)) - - def setStrokeColorRGB(self, r, g, b): - """Set the stroke color using positive color description - (Red,Green,Blue). Takes 3 arguments between 0.0 and 1.0""" - self._strokeColorRGB = (r, g, b) - self._code.append('%s RG' % fp_str(r,g,b)) - - def setFillColor(self, aColor): - """Takes a color object, allowing colors to be referred to by name""" - if isinstance(aColor, CMYKColor): - d = aColor.density - c,m,y,k = (d*aColor.cyan, d*aColor.magenta, d*aColor.yellow, d*aColor.black) - self._fillColorCMYK = (c, m, y, k) - self._code.append('%s k' % fp_str(c, m, y, k)) - elif isinstance(aColor, Color): - rgb = (aColor.red, aColor.green, aColor.blue) - self._fillColorRGB = rgb - self._code.append('%s rg' % fp_str(rgb) ) - elif type(aColor) in _SeqTypes: - l = len(aColor) - if l==3: - self._fillColorRGB = aColor - self._code.append('%s rg' % fp_str(aColor) ) - elif l==4: - self.setFillColorCMYK(aColor[0], aColor[1], aColor[2], aColor[3]) - else: - raise 'Unknown color', str(aColor) - elif type(aColor) is StringType: - self.setFillColor(toColor(aColor)) - else: - raise 'Unknown color', str(aColor) - - def setStrokeColor(self, aColor): - """Takes a color object, allowing colors to be referred to by name""" - if isinstance(aColor, CMYKColor): - d = aColor.density - c,m,y,k = (d*aColor.cyan, d*aColor.magenta, d*aColor.yellow, d*aColor.black) - self._strokeColorCMYK = (c, m, y, k) - self._code.append('%s K' % fp_str(c, m, y, k)) - elif isinstance(aColor, Color): - rgb = (aColor.red, aColor.green, aColor.blue) - self._strokeColorRGB = rgb - self._code.append('%s RG' % fp_str(rgb) ) - elif type(aColor) in _SeqTypes: - l = len(aColor) - if l==3: - self._strokeColorRGB = aColor - self._code.append('%s RG' % fp_str(aColor) ) - elif l==4: - self.setStrokeColorCMYK(aColor[0], aColor[1], aColor[2], aColor[3]) - else: - raise 'Unknown color', str(aColor) - elif type(aColor) is StringType: - self.setStrokeColor(toColor(aColor)) - else: - raise 'Unknown color', str(aColor) - - def setFillGray(self, gray): - """Sets the gray level; 0.0=black, 1.0=white""" - self._fillColorRGB = (gray, gray, gray) - self._code.append('%s g' % fp_str(gray)) - - def setStrokeGray(self, gray): - """Sets the gray level; 0.0=black, 1.0=white""" - self._strokeColorRGB = (gray, gray, gray) - self._code.append('%s G' % fp_str(gray)) - -class PDFTextObject(_PDFColorSetter): - """PDF logically separates text and graphics drawing; text - operations need to be bracketed between BT (Begin text) and - ET operators. This class ensures text operations are - properly encapusalted. Ask the canvas for a text object - with beginText(x, y). Do not construct one directly. - Do not use multiple text objects in parallel; PDF is - not multi-threaded! - - It keeps track of x and y coordinates relative to its origin.""" - - def __init__(self, canvas, x=0,y=0): - self._code = ['BT'] #no point in [] then append RGB - self._canvas = canvas #canvas sets this so it has access to size info - self._fontname = self._canvas._fontname - self._fontsize = self._canvas._fontsize - self._leading = self._canvas._leading - font = pdfmetrics.getFont(self._fontname) - self._dynamicFont = getattr(font, '_dynamicFont', 0) - self._curSubset = -1 - - self.setTextOrigin(x, y) - - def getCode(self): - "pack onto one line; used internally" - self._code.append('ET') - return string.join(self._code, ' ') - - def setTextOrigin(self, x, y): - if self._canvas.bottomup: - self._code.append('1 0 0 1 %s Tm' % fp_str(x, y)) #bottom up - else: - self._code.append('1 0 0 -1 %s Tm' % fp_str(x, y)) #top down - - # The current cursor position is at the text origin - self._x0 = self._x = x - self._y0 = self._y = y - - def setTextTransform(self, a, b, c, d, e, f): - "Like setTextOrigin, but does rotation, scaling etc." - if not self._canvas.bottomup: - c = -c #reverse bottom row of the 2D Transform - d = -d - self._code.append('%s Tm' % fp_str(a, b, c, d, e, f)) - - # The current cursor position is at the text origin Note that - # we aren't keeping track of all the transform on these - # coordinates: they are relative to the rotations/sheers - # defined in the matrix. - self._x0 = self._x = e - self._y0 = self._y = f - - def moveCursor(self, dx, dy): - - """Starts a new line at an offset dx,dy from the start of the - current line. This does not move the cursor relative to the - current position, and it changes the current offset of every - future line drawn (i.e. if you next do a textLine() call, it - will move the cursor to a position one line lower than the - position specificied in this call. """ - - # Check if we have a previous move cursor call, and combine - # them if possible. - if self._code and self._code[-1][-3:]==' Td': - L = string.split(self._code[-1]) - if len(L)==3: - del self._code[-1] - else: - self._code[-1] = string.join(L[:-4]) - - # Work out the last movement - lastDx = float(L[-3]) - lastDy = float(L[-2]) - - # Combine the two movement - dx += lastDx - dy -= lastDy - - # We will soon add the movement to the line origin, so if - # we've already done this for lastDx, lastDy, remove it - # first (so it will be right when added back again). - self._x0 -= lastDx - self._y0 -= lastDy - - # Output the move text cursor call. - self._code.append('%s Td' % fp_str(dx, -dy)) - - # Keep track of the new line offsets and the cursor position - self._x0 += dx - self._y0 += dy - self._x = self._x0 - self._y = self._y0 - - def setXPos(self, dx): - """Starts a new line dx away from the start of the - current line - NOT from the current point! So if - you call it in mid-sentence, watch out.""" - self.moveCursor(dx,0) - - def getCursor(self): - """Returns current text position relative to the last origin.""" - return (self._x, self._y) - - def getStartOfLine(self): - """Returns a tuple giving the text position of the start of the - current line.""" - return (self._x0, self._y0) - - def getX(self): - """Returns current x position relative to the last origin.""" - return self._x - - def getY(self): - """Returns current y position relative to the last origin.""" - return self._y - - def _setFont(self, psfontname, size): - """Sets the font and fontSize - Raises a readable exception if an illegal font - is supplied. Font names are case-sensitive! Keeps track - of font anme and size for metrics.""" - self._fontname = psfontname - self._fontsize = size - font = pdfmetrics.getFont(self._fontname) - - self._dynamicFont = getattr(font, '_dynamicFont', 0) - if self._dynamicFont: - self._curSubset = -1 - else: - pdffontname = self._canvas._doc.getInternalFontName(psfontname) - self._code.append('%s %s Tf' % (pdffontname, fp_str(size))) - - def setFont(self, psfontname, size, leading = None): - """Sets the font. If leading not specified, defaults to 1.2 x - font size. Raises a readable exception if an illegal font - is supplied. Font names are case-sensitive! Keeps track - of font anme and size for metrics.""" - self._fontname = psfontname - self._fontsize = size - if leading is None: - leading = size * 1.2 - self._leading = leading - font = pdfmetrics.getFont(self._fontname) - - self._dynamicFont = getattr(font, '_dynamicFont', 0) - if self._dynamicFont: - self._curSubset = -1 - else: - pdffontname = self._canvas._doc.getInternalFontName(psfontname) - self._code.append('%s %s Tf %s TL' % (pdffontname, fp_str(size), fp_str(leading))) - - def setCharSpace(self, charSpace): - """Adjusts inter-character spacing""" - self._charSpace = charSpace - self._code.append('%s Tc' % fp_str(charSpace)) - - def setWordSpace(self, wordSpace): - """Adjust inter-word spacing. This can be used - to flush-justify text - you get the width of the - words, and add some space between them.""" - self._wordSpace = wordSpace - self._code.append('%s Tw' % fp_str(wordSpace)) - - def setHorizScale(self, horizScale): - "Stretches text out horizontally" - self._horizScale = 100 + horizScale - self._code.append('%s Tz' % fp_str(horizScale)) - - def setLeading(self, leading): - "How far to move down at the end of a line." - self._leading = leading - self._code.append('%s TL' % fp_str(leading)) - - def setTextRenderMode(self, mode): - """Set the text rendering mode. - - 0 = Fill text - 1 = Stroke text - 2 = Fill then stroke - 3 = Invisible - 4 = Fill text and add to clipping path - 5 = Stroke text and add to clipping path - 6 = Fill then stroke and add to clipping path - 7 = Add to clipping path""" - - assert mode in (0,1,2,3,4,5,6,7), "mode must be in (0,1,2,3,4,5,6,7)" - self._textRenderMode = mode - self._code.append('%d Tr' % mode) - - def setRise(self, rise): - "Move text baseline up or down to allow superscrip/subscripts" - self._rise = rise - self._y = self._y - rise # + ? _textLineMatrix? - self._code.append('%s Ts' % fp_str(rise)) - - def _formatText(self, text): - "Generates PDF text output operator(s)" - canv = self._canvas - font = pdfmetrics.getFont(self._fontname) - R = [] - if self._dynamicFont: - #it's a truetype font and should be utf8. If an error is raised, - for subset, t in font.splitString(text, canv._doc): - if subset != self._curSubset: - pdffontname = font.getSubsetInternalName(subset, canv._doc) - R.append("%s %s Tf %s TL" % (pdffontname, fp_str(self._fontsize), fp_str(self._leading))) - self._curSubset = subset - R.append("(%s) Tj" % canv._escape(t)) - elif font._multiByte: - #all the fonts should really work like this - let them know more about PDF... - R.append("%s %s Tf %s TL" % ( - canv._doc.getInternalFontName(font.fontName), - fp_str(self._fontsize), - fp_str(self._leading) - )) - R.append("(%s) Tj" % font.formatForPdf(text)) - - else: - #convert to T1 coding - fc = font - if not isinstance(text,unicode): - try: - text = text.decode('utf8') - except UnicodeDecodeError,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]),))) - - for f, t in pdfmetrics.unicode2T1(text,[font]+font.substitutionFonts): - if f!=fc: - R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(f.fontName), fp_str(self._fontsize), fp_str(self._leading))) - fc = f - R.append("(%s) Tj" % canv._escape(t)) - if font!=fc: - R.append("%s %s Tf %s TL" % (canv._doc.getInternalFontName(self._fontname), fp_str(self._fontsize), fp_str(self._leading))) - return ' '.join(R) - - def _textOut(self, text, TStar=0): - "prints string at current point, ignores text cursor" - self._code.append('%s%s' % (self._formatText(text), (TStar and ' T*' or ''))) - - def textOut(self, text): - """prints string at current point, text cursor moves across.""" - self._x = self._x + self._canvas.stringWidth(text, self._fontname, self._fontsize) - self._code.append(self._formatText(text)) - - def textLine(self, text=''): - """prints string at current point, text cursor moves down. - Can work with no argument to simply move the cursor down.""" - - # Update the coordinates of the cursor - self._x = self._x0 - if self._canvas.bottomup: - self._y = self._y - self._leading - else: - self._y = self._y + self._leading - - # Update the location of the start of the line - # self._x0 is unchanged - self._y0 = self._y - - # Output the text followed by a PDF newline command - self._code.append('%s T*' % self._formatText(text)) - - def textLines(self, stuff, trim=1): - """prints multi-line or newlined strings, moving down. One - comon use is to quote a multi-line block in your Python code; - since this may be indented, by default it trims whitespace - off each line and from the beginning; set trim=0 to preserve - whitespace.""" - if type(stuff) == StringType: - lines = string.split(string.strip(stuff), '\n') - if trim==1: - lines = map(string.strip,lines) - elif type(stuff) == ListType: - lines = stuff - elif type(stuff) == TupleType: - lines = stuff - else: - assert 1==0, "argument to textlines must be string,, list or tuple" - - # Output each line one at a time. This used to be a long-hand - # copy of the textLine code, now called as a method. - for line in lines: - self.textLine(line) - - def __nonzero__(self): - 'PDFTextObject is true if it has something done after the init' - return self._code != ['BT'] diff --git a/bin/reportlab/platypus/__init__.py b/bin/reportlab/platypus/__init__.py deleted file mode 100755 index 7a410633350..00000000000 --- a/bin/reportlab/platypus/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/__init__.py -__version__=''' $Id: __init__.py 2775 2006-02-15 12:17:24Z rgbecker $ ''' -__doc__='' -from reportlab.platypus.flowables import Flowable, Image, Macro, PageBreak, Preformatted, Spacer, XBox, \ - CondPageBreak, KeepTogether, TraceInfo, FailOnWrap, FailOnDraw, PTOContainer, \ - KeepInFrame, ParagraphAndImage, ImageAndFlowables -from reportlab.platypus.paragraph import Paragraph, cleanBlockQuotedText, ParaLines -from reportlab.platypus.paraparser import ParaFrag -from reportlab.platypus.tables import Table, TableStyle, CellStyle, LongTable -from reportlab.platypus.frames import Frame -from reportlab.platypus.doctemplate import BaseDocTemplate, NextPageTemplate, PageTemplate, ActionFlowable, \ - SimpleDocTemplate, FrameBreak, PageBegin, Indenter -from xpreformatted import XPreformatted diff --git a/bin/reportlab/platypus/doctemplate.py b/bin/reportlab/platypus/doctemplate.py deleted file mode 100644 index f38d6506b1f..00000000000 --- a/bin/reportlab/platypus/doctemplate.py +++ /dev/null @@ -1,953 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/doctemplate.py - -__version__=''' $Id: doctemplate.py 2852 2006-05-08 15:04:15Z rgbecker $ ''' - -__doc__=""" -This module contains the core structure of platypus. - -Platypus constructs documents. Document styles are determined by DocumentTemplates. - -Each DocumentTemplate contains one or more PageTemplates which defines the look of the -pages of the document. - -Each PageTemplate has a procedure for drawing the "non-flowing" part of the page -(for example the header, footer, page number, fixed logo graphic, watermark, etcetera) and -a set of Frames which enclose the flowing part of the page (for example the paragraphs, -tables, or non-fixed diagrams of the text). - -A document is built when a DocumentTemplate is fed a sequence of Flowables. -The action of the build consumes the flowables in order and places them onto -frames on pages as space allows. When a frame runs out of space the next frame -of the page is used. If no frame remains a new page is created. A new page -can also be created if a page break is forced. - -The special invisible flowable NextPageTemplate can be used to specify -the page template for the next page (which by default is the one being used -for the current frame). -""" - -from reportlab.platypus.flowables import * -from reportlab.lib.units import inch -from reportlab.platypus.paragraph import Paragraph -from reportlab.platypus.frames import Frame -from reportlab.rl_config import defaultPageSize, verbose -import reportlab.lib.sequencer -from reportlab.pdfgen import canvas - -from types import * -import sys -import logging -logger = logging.getLogger("reportlab.platypus") - -class LayoutError(Exception): - pass - -def _doNothing(canvas, doc): - "Dummy callback for onPage" - pass - - -class IndexingFlowable(Flowable): - """Abstract interface definition for flowables which might - hold references to other pages or themselves be targets - of cross-references. XRefStart, XRefDest, Table of Contents, - Indexes etc.""" - def isIndexing(self): - return 1 - - def isSatisfied(self): - return 1 - - def notify(self, kind, stuff): - """This will be called by the framework wherever 'stuff' happens. - 'kind' will be a value that can be used to decide whether to - pay attention or not.""" - pass - - def beforeBuild(self): - """Called by multiBuild before it starts; use this to clear - old contents""" - pass - - def afterBuild(self): - """Called after build ends but before isSatisfied""" - pass - -class ActionFlowable(Flowable): - '''This Flowable is never drawn, it can be used for data driven controls - For example to change a page template (from one column to two, for example) - use NextPageTemplate which creates an ActionFlowable. - ''' - def __init__(self,action=()): - #must call super init to ensure it has a width and height (of zero), - #as in some cases the packer might get called on it... - Flowable.__init__(self) - if type(action) not in (ListType, TupleType): - action = (action,) - self.action = tuple(action) - - def apply(self,doc): - ''' - This is called by the doc.build processing to allow the instance to - implement its behaviour - ''' - action = self.action[0] - args = tuple(self.action[1:]) - arn = 'handle_'+action - try: - apply(getattr(doc,arn), args) - except AttributeError, aerr: - if aerr.args[0]==arn: - raise NotImplementedError, "Can't handle ActionFlowable(%s)" % action - else: - raise - except "bogus": - t, v, unused = sys.exc_info() - raise t, "%s\n handle_%s args=%s"%(v,action,args) - - def __call__(self): - return self - - def identity(self, maxLen=None): - return "ActionFlowable: %s%s" % (str(self.action),self._frameName()) - -class LCActionFlowable(ActionFlowable): - locChanger = 1 #we cause a frame or page change - - def wrap(self, availWidth, availHeight): - '''Should never be called.''' - raise NotImplementedError - - def draw(self): - '''Should never be called.''' - raise NotImplementedError - -class NextFrameFlowable(ActionFlowable): - def __init__(self,ix,resume=0): - ActionFlowable.__init__(self,('nextFrame',ix,resume)) - -class CurrentFrameFlowable(LCActionFlowable): - def __init__(self,ix,resume=0): - ActionFlowable.__init__(self,('currentFrame',ix,resume)) - -class NullActionFlowable(ActionFlowable): - def apply(self): - pass - -class _FrameBreak(LCActionFlowable): - ''' - A special ActionFlowable that allows setting doc._nextFrameIndex - - eg story.append(FrameBreak('mySpecialFrame')) - ''' - def __call__(self,ix=None,resume=0): - r = self.__class__(self.action+(resume,)) - r._ix = ix - return r - - def apply(self,doc): - if getattr(self,'_ix',None): - doc.handle_nextFrame(self._ix) - ActionFlowable.apply(self,doc) - -FrameBreak = _FrameBreak('frameEnd') -PageBegin = LCActionFlowable('pageBegin') - -def _evalMeasurement(n): - if type(n) is type(''): - from paraparser import _num - n = _num(n) - if type(n) is type(()): n = n[1] - return n - -class FrameActionFlowable(Flowable): - def __init__(self,*arg,**kw): - raise NotImplementedError('Abstract Class') - - def frameAction(self,frame): - raise NotImplementedError('Abstract Class') - -class Indenter(FrameActionFlowable): - """Increases or decreases left and right margins of frame. - - This allows one to have a 'context-sensitive' indentation - and makes nested lists way easier. - """ - def __init__(self, left=0, right=0): - self.left = _evalMeasurement(left) - self.right = _evalMeasurement(right) - - def frameAction(self, frame): - frame._leftExtraIndent += self.left - frame._rightExtraIndent += self.right - -class NextPageTemplate(ActionFlowable): - """When you get to the next page, use the template specified (change to two column, for example) """ - def __init__(self,pt): - ActionFlowable.__init__(self,('nextPageTemplate',pt)) - - -class PageTemplate: - """ - essentially a list of Frames and an onPage routine to call at the start - of a page when this is selected. onPageEnd gets called at the end. - derived classes can also implement beforeDrawPage and afterDrawPage if they want - """ - def __init__(self,id=None,frames=[],onPage=_doNothing, onPageEnd=_doNothing, - pagesize=None): - if type(frames) not in (ListType,TupleType): frames = [frames] - assert filter(lambda x: not isinstance(x,Frame), frames)==[], "frames argument error" - self.id = id - self.frames = frames - self.onPage = onPage - self.onPageEnd = onPageEnd - self.pagesize = pagesize - - def beforeDrawPage(self,canv,doc): - """Override this if you want additional functionality or prefer - a class based page routine. Called before any flowables for - this page are processed.""" - pass - - def checkPageSize(self,canv,doc): - '''This gets called by the template framework - If canv size != template size then the canv size is set to - the template size or if that's not available to the - doc size. - ''' - #### NEVER EVER EVER COMPARE FLOATS FOR EQUALITY - #RGB converting pagesizes to ints means we are accurate to one point - #RGB I suggest we should be aiming a little better - cp = None - dp = None - sp = None - if canv._pagesize: cp = map(int, canv._pagesize) - if self.pagesize: sp = map(int, self.pagesize) - if doc.pagesize: dp = map(int, doc.pagesize) - if cp!=sp: - if sp: - canv.setPageSize(self.pagesize) - elif cp!=dp: - canv.setPageSize(doc.pagesize) - - def afterDrawPage(self, canv, doc): - """This is called after the last flowable for the page has - been processed. You might use this if the page header or - footer needed knowledge of what flowables were drawn on - this page.""" - pass - - -class BaseDocTemplate: - """ - First attempt at defining a document template class. - - The basic idea is simple. - 0) The document has a list of data associated with it - this data should derive from flowables. We'll have - special classes like PageBreak, FrameBreak to do things - like forcing a page end etc. - - 1) The document has one or more page templates. - - 2) Each page template has one or more frames. - - 3) The document class provides base methods for handling the - story events and some reasonable methods for getting the - story flowables into the frames. - - 4) The document instances can override the base handler routines. - - Most of the methods for this class are not called directly by the user, - but in some advanced usages they may need to be overridden via subclassing. - - EXCEPTION: doctemplate.build(...) must be called for most reasonable uses - since it builds a document using the page template. - - Each document template builds exactly one document into a file specified - by the filename argument on initialization. - - Possible keyword arguments for the initialization: - - pageTemplates: A list of templates. Must be nonempty. Names - assigned to the templates are used for referring to them so no two used - templates should have the same name. For example you might want one template - for a title page, one for a section first page, one for a first page of - a chapter and two more for the interior of a chapter on odd and even pages. - If this argument is omitted then at least one pageTemplate should be provided - using the addPageTemplates method before the document is built. - pageSize: a 2-tuple or a size constant from reportlab/lib/pagesizes.pu. - Used by the SimpleDocTemplate subclass which does NOT accept a list of - pageTemplates but makes one for you; ignored when using pageTemplates. - - showBoundary: if set draw a box around the frame boundaries. - leftMargin: - rightMargin: - topMargin: - bottomMargin: Margin sizes in points (default 1 inch) - These margins may be overridden by the pageTemplates. They are primarily of interest - for the SimpleDocumentTemplate subclass. - allowSplitting: If set flowables (eg, paragraphs) may be split across frames or pages - (default: 1) - title: Internal title for document (does not automatically display on any page) - author: Internal author for document (does not automatically display on any page) - """ - _initArgs = { 'pagesize':defaultPageSize, - 'pageTemplates':[], - 'showBoundary':0, - 'leftMargin':inch, - 'rightMargin':inch, - 'topMargin':inch, - 'bottomMargin':inch, - 'allowSplitting':1, - 'title':None, - 'author':None, - 'invariant':None, - 'pageCompression':None, - '_pageBreakQuick':1, - 'rotation':0, - '_debug':0} - _invalidInitArgs = () - _firstPageTemplateIndex = 0 - - def __init__(self, filename, **kw): - """create a document template bound to a filename (see class documentation for keyword arguments)""" - self.filename = filename - - for k in self._initArgs.keys(): - if not kw.has_key(k): - v = self._initArgs[k] - else: - if k in self._invalidInitArgs: - raise ValueError, "Invalid argument %s" % k - v = kw[k] - setattr(self,k,v) - - p = self.pageTemplates - self.pageTemplates = [] - self.addPageTemplates(p) - - # facility to assist multi-build and cross-referencing. - # various hooks can put things into here - key is what - # you want, value is a page number. This can then be - # passed to indexing flowables. - self._pageRefs = {} - self._indexingFlowables = [] - - - #callback facility for progress monitoring - self._onPage = None - self._onProgress = None - self._flowableCount = 0 # so we know how far to go - - - #infinite loop detection if we start doing lots of empty pages - self._curPageFlowableCount = 0 - self._emptyPages = 0 - self._emptyPagesAllowed = 10 - - #context sensitive margins - set by story, not from outside - self._leftExtraIndent = 0.0 - self._rightExtraIndent = 0.0 - - self._calc() - self.afterInit() - - def _calc(self): - self._rightMargin = self.pagesize[0] - self.rightMargin - self._topMargin = self.pagesize[1] - self.topMargin - self.width = self._rightMargin - self.leftMargin - self.height = self._topMargin - self.bottomMargin - - def setPageCallBack(self, func): - 'Simple progress monitor - func(pageNo) called on each new page' - self._onPage = func - - def setProgressCallBack(self, func): - '''Cleverer progress monitor - func(typ, value) called regularly''' - self._onProgress = func - - def clean_hanging(self): - 'handle internal postponed actions' - while len(self._hanging): - self.handle_flowable(self._hanging) - - def addPageTemplates(self,pageTemplates): - 'add one or a sequence of pageTemplates' - if type(pageTemplates) not in (ListType,TupleType): - pageTemplates = [pageTemplates] - #this test below fails due to inconsistent imports! - #assert filter(lambda x: not isinstance(x,PageTemplate), pageTemplates)==[], "pageTemplates argument error" - for t in pageTemplates: - self.pageTemplates.append(t) - - def handle_documentBegin(self): - '''implement actions at beginning of document''' - self._hanging = [PageBegin] - self.pageTemplate = self.pageTemplates[self._firstPageTemplateIndex] - self.page = 0 - self.beforeDocument() - - def handle_pageBegin(self): - '''Perform actions required at beginning of page. - shouldn't normally be called directly''' - self.page += 1 - if self._debug: logger.debug("beginning page %d" % self.page) - self.pageTemplate.beforeDrawPage(self.canv,self) - self.pageTemplate.checkPageSize(self.canv,self) - self.pageTemplate.onPage(self.canv,self) - for f in self.pageTemplate.frames: f._reset() - self.beforePage() - #keep a count of flowables added to this page. zero indicates bad stuff - self._curPageFlowableCount = 0 - if hasattr(self,'_nextFrameIndex'): - del self._nextFrameIndex - self.frame = self.pageTemplate.frames[0] - self.frame._debug = self._debug - self.handle_frameBegin() - - def handle_pageEnd(self): - ''' show the current page - check the next page template - hang a page begin - ''' - #detect infinite loops... - if self._curPageFlowableCount == 0: - self._emptyPages += 1 - else: - self._emptyPages = 0 - if self._emptyPages >= self._emptyPagesAllowed: - if 1: - ident = "More than %d pages generated without content - halting layout. Likely that a flowable is too large for any frame." % self._emptyPagesAllowed - #leave to keep apart from the raise - raise LayoutError(ident) - else: - pass #attempt to restore to good state - else: - if self._onProgress: - self._onProgress('PAGE', self.canv.getPageNumber()) - self.pageTemplate.afterDrawPage(self.canv, self) - self.pageTemplate.onPageEnd(self.canv, self) - self.afterPage() - if self._debug: logger.debug("ending page %d" % self.page) - self.canv.setPageRotation(getattr(self.pageTemplate,'rotation',self.rotation)) - self.canv.showPage() - - if hasattr(self,'_nextPageTemplateCycle'): - #they are cycling through pages'; we keep the index - cyc = self._nextPageTemplateCycle - idx = self._nextPageTemplateIndex - self.pageTemplate = cyc[idx] #which is one of the ones in the list anyway - #bump up by 1 - self._nextPageTemplateIndex = (idx + 1) % len(cyc) - elif hasattr(self,'_nextPageTemplateIndex'): - self.pageTemplate = self.pageTemplates[self._nextPageTemplateIndex] - del self._nextPageTemplateIndex - if self._emptyPages==0: - pass #store good state here - self._hanging.append(PageBegin) - - def handle_pageBreak(self,slow=None): - '''some might choose not to end all the frames''' - if self._pageBreakQuick and not slow: - self.handle_pageEnd() - else: - n = len(self._hanging) - while len(self._hanging)==n: - self.handle_frameEnd() - - def handle_frameBegin(self,resume=0): - '''What to do at the beginning of a frame''' - f = self.frame - if f._atTop: - if self.showBoundary or self.frame.showBoundary: - self.frame.drawBoundary(self.canv) - f._leftExtraIndent = self._leftExtraIndent - f._rightExtraIndent = self._rightExtraIndent - - def handle_frameEnd(self,resume=0): - ''' Handles the semantics of the end of a frame. This includes the selection of - the next frame or if this is the last frame then invoke pageEnd. - ''' - - self._leftExtraIndent = self.frame._leftExtraIndent - self._rightExtraIndent = self.frame._rightExtraIndent - - if hasattr(self,'_nextFrameIndex'): - self.frame = self.pageTemplate.frames[self._nextFrameIndex] - self.frame._debug = self._debug - del self._nextFrameIndex - self.handle_frameBegin(resume) - elif hasattr(self.frame,'lastFrame') or self.frame is self.pageTemplate.frames[-1]: - self.handle_pageEnd() - self.frame = None - else: - f = self.frame - self.frame = self.pageTemplate.frames[self.pageTemplate.frames.index(f) + 1] - self.frame._debug = self._debug - self.handle_frameBegin() - - def handle_nextPageTemplate(self,pt): - '''On endPage change to the page template with name or index pt''' - if type(pt) is StringType: - if hasattr(self, '_nextPageTemplateCycle'): del self._nextPageTemplateCycle - for t in self.pageTemplates: - if t.id == pt: - self._nextPageTemplateIndex = self.pageTemplates.index(t) - return - raise ValueError, "can't find template('%s')"%pt - elif type(pt) is IntType: - if hasattr(self, '_nextPageTemplateCycle'): del self._nextPageTemplateCycle - self._nextPageTemplateIndex = pt - elif type(pt) in (ListType, TupleType): - #used for alternating left/right pages - #collect the refs to the template objects, complain if any are bad - cycle = [] - for templateName in pt: - found = 0 - for t in self.pageTemplates: - if t.id == templateName: - cycle.append(t) - found = 1 - if not found: - raise ValueError("Cannot find page template called %s" % templateName) - #double-check all of them are there... - - first = cycle[0] - #ensure we start on the first one - self._nextPageTemplateCycle = cycle - self._nextPageTemplateIndex = 0 #indexes into the cycle - else: - raise TypeError, "argument pt should be string or integer or list" - - def handle_nextFrame(self,fx,resume=0): - '''On endFrame change to the frame with name or index fx''' - if type(fx) is StringType: - for f in self.pageTemplate.frames: - if f.id == fx: - self._nextFrameIndex = self.pageTemplate.frames.index(f) - return - raise ValueError, "can't find frame('%s')"%fx - elif type(fx) is IntType: - self._nextFrameIndex = fx - else: - raise TypeError, "argument fx should be string or integer" - - def handle_currentFrame(self,fx,resume=0): - '''change to the frame with name or index fx''' - self.handle_nextFrame(fx,resume) - self.handle_frameEnd(resume) - - def handle_breakBefore(self, flowables): - '''preprocessing step to allow pageBreakBefore and frameBreakBefore attributes''' - first = flowables[0] - # if we insert a page break before, we'll process that, see it again, - # and go in an infinite loop. So we need to set a flag on the object - # saying 'skip me'. This should be unset on the next pass - if hasattr(first, '_skipMeNextTime'): - delattr(first, '_skipMeNextTime') - return - # this could all be made much quicker by putting the attributes - # in to the flowables with a defult value of 0 - if hasattr(first,'pageBreakBefore') and first.pageBreakBefore == 1: - first._skipMeNextTime = 1 - first.insert(0, PageBreak()) - return - if hasattr(first,'style') and hasattr(first.style, 'pageBreakBefore') and first.style.pageBreakBefore == 1: - first._skipMeNextTime = 1 - flowables.insert(0, PageBreak()) - return - if hasattr(first,'frameBreakBefore') and first.frameBreakBefore == 1: - first._skipMeNextTime = 1 - flowables.insert(0, FrameBreak()) - return - if hasattr(first,'style') and hasattr(first.style, 'frameBreakBefore') and first.style.frameBreakBefore == 1: - first._skipMeNextTime = 1 - flowables.insert(0, FrameBreak()) - return - - def handle_keepWithNext(self, flowables): - "implements keepWithNext" - i = 0 - n = len(flowables) - while i maxPasses: - raise IndexError, "Index entries not resolved after %d passes" % maxPasses - - if verbose: print 'saved' - - #these are pure virtuals override in derived classes - #NB these get called at suitable places by the base class - #so if you derive and override the handle_xxx methods - #it's up to you to ensure that they maintain the needed consistency - def afterInit(self): - """This is called after initialisation of the base class.""" - pass - - def beforeDocument(self): - """This is called before any processing is - done on the document.""" - pass - - def beforePage(self): - """This is called at the beginning of page - processing, and immediately before the - beforeDrawPage method of the current page - template.""" - pass - - def afterPage(self): - """This is called after page processing, and - immediately after the afterDrawPage method - of the current page template.""" - pass - - def filterFlowables(self,flowables): - '''called to filter flowables at the start of the main handle_flowable method. - Upon return if flowables[0] has been set to None it is discarded and the main - method returns. - ''' - pass - - def afterFlowable(self, flowable): - '''called after a flowable has been rendered''' - pass - - -class SimpleDocTemplate(BaseDocTemplate): - """A special case document template that will handle many simple documents. - See documentation for BaseDocTemplate. No pageTemplates are required - for this special case. A page templates are inferred from the - margin information and the onFirstPage, onLaterPages arguments to the build method. - - A document which has all pages with the same look except for the first - page may can be built using this special approach. - """ - _invalidInitArgs = ('pageTemplates',) - - def handle_pageBegin(self): - '''override base method to add a change of page template after the firstpage. - ''' - self._handle_pageBegin() - self._handle_nextPageTemplate('Later') - - def build(self,flowables,onFirstPage=_doNothing, onLaterPages=_doNothing, canvasmaker=canvas.Canvas): - """build the document using the flowables. Annotate the first page using the onFirstPage - function and later pages using the onLaterPages function. The onXXX pages should follow - the signature - - def myOnFirstPage(canvas, document): - # do annotations and modify the document - ... - - The functions can do things like draw logos, page numbers, - footers, etcetera. They can use external variables to vary - the look (for example providing page numbering or section names). - """ - self._calc() #in case we changed margins sizes etc - frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal') - self.addPageTemplates([PageTemplate(id='First',frames=frameT, onPage=onFirstPage,pagesize=self.pagesize), - PageTemplate(id='Later',frames=frameT, onPage=onLaterPages,pagesize=self.pagesize)]) - if onFirstPage is _doNothing and hasattr(self,'onFirstPage'): - self.pageTemplates[0].beforeDrawPage = self.onFirstPage - if onLaterPages is _doNothing and hasattr(self,'onLaterPages'): - self.pageTemplates[1].beforeDrawPage = self.onLaterPages - BaseDocTemplate.build(self,flowables, canvasmaker=canvasmaker) - - -def progressCB(typ, value): - """Example prototype for progress monitoring. - - This aims to provide info about what is going on - during a big job. It should enable, for example, a reasonably - smooth progress bar to be drawn. We design the argument - signature to be predictable and conducive to programming in - other (type safe) languages. If set, this will be called - repeatedly with pairs of values. The first is a string - indicating the type of call; the second is a numeric value. - - typ 'STARTING', value = 0 - typ 'SIZE_EST', value = numeric estimate of job size - typ 'PASS', value = number of this rendering pass - typ 'PROGRESS', value = number between 0 and SIZE_EST - typ 'PAGE', value = page number of page - type 'FINISHED', value = 0 - - The sequence is - STARTING - always called once - SIZE_EST - always called once - PROGRESS - called often - PAGE - called often when page is emitted - FINISHED - called when really, really finished - - some juggling is needed to accurately estimate numbers of - pages in pageDrawing mode. - - NOTE: the SIZE_EST is a guess. It is possible that the - PROGRESS value may slightly exceed it, or may even step - back a little on rare occasions. The only way to be - really accurate would be to do two passes, and I don't - want to take that performance hit. - """ - print 'PROGRESS MONITOR: %-10s %d' % (typ, value) - -if __name__ == '__main__': - - def myFirstPage(canvas, doc): - canvas.saveState() - canvas.setStrokeColor(red) - canvas.setLineWidth(5) - canvas.line(66,72,66,PAGE_HEIGHT-72) - canvas.setFont('Times-Bold',24) - canvas.drawString(108, PAGE_HEIGHT-108, "TABLE OF CONTENTS DEMO") - canvas.setFont('Times-Roman',12) - canvas.drawString(4 * inch, 0.75 * inch, "First Page") - canvas.restoreState() - - def myLaterPages(canvas, doc): - canvas.saveState() - canvas.setStrokeColor(red) - canvas.setLineWidth(5) - canvas.line(66,72,66,PAGE_HEIGHT-72) - canvas.setFont('Times-Roman',12) - canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page) - canvas.restoreState() - - def run(): - objects_to_draw = [] - from reportlab.lib.styles import ParagraphStyle - #from paragraph import Paragraph - from doctemplate import SimpleDocTemplate - - #need a style - normal = ParagraphStyle('normal') - normal.firstLineIndent = 18 - normal.spaceBefore = 6 - from reportlab.lib.randomtext import randomText - import random - for i in range(15): - height = 0.5 + (2*random.random()) - box = XBox(6 * inch, height * inch, 'Box Number %d' % i) - objects_to_draw.append(box) - para = Paragraph(randomText(), normal) - objects_to_draw.append(para) - - SimpleDocTemplate('doctemplate.pdf').build(objects_to_draw, - onFirstPage=myFirstPage,onLaterPages=myLaterPages) - - run() diff --git a/bin/reportlab/platypus/figures.py b/bin/reportlab/platypus/figures.py deleted file mode 100644 index 00aeef7c898..00000000000 --- a/bin/reportlab/platypus/figures.py +++ /dev/null @@ -1,372 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/figures.py -"""This includes some demos of platypus for use in the API proposal""" -__version__=''' $Id: figures.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' - -import os - -from reportlab.lib import colors -from reportlab.pdfgen.canvas import Canvas -from reportlab.lib.styles import ParagraphStyle -from reportlab.lib.utils import recursiveImport -from reportlab.platypus import Frame -from reportlab.platypus import Flowable -from reportlab.platypus import Paragraph -from reportlab.lib.units import inch -from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER -from reportlab.lib.validators import isColor -from reportlab.lib.colors import toColor - -captionStyle = ParagraphStyle('Caption', fontName='Times-Italic', fontSize=10, alignment=TA_CENTER) - -class Figure(Flowable): - def __init__(self, width, height, caption="", - captionFont="Times-Italic", captionSize=12, - background=None, - captionTextColor=toColor('black'), - captionBackColor=None, - border=1): - Flowable.__init__(self) - self.width = width - self.figureHeight = height - self.caption = caption - self.captionFont = captionFont - self.captionSize = captionSize - self.captionTextColor = captionTextColor - self.captionBackColor = captionBackColor - self._captionData = None - self.captionHeight = 0 # work out later - self.background = background - self.border = border - self.spaceBefore = 12 - self.spaceAfter = 12 - - def _getCaptionPara(self): - caption = self.caption - captionFont = self.captionFont - captionSize = self.captionSize - captionTextColor = self.captionTextColor - captionBackColor = self.captionBackColor - if self._captionData!=(caption,captionFont,captionSize,captionTextColor,captionBackColor): - self._captionData = (caption,captionFont,captionSize,captionTextColor,captionBackColor) - self.captionStyle = ParagraphStyle( - 'Caption', - fontName=captionFont, - fontSize=captionSize, - leading=1.2*captionSize, - textColor = captionTextColor, - backColor = captionBackColor, - spaceBefore=captionSize * 0.5, - alignment=TA_CENTER) - #must build paragraph now to get sequencing in synch with rest of story - self.captionPara = Paragraph(self.caption, self.captionStyle) - - def wrap(self, availWidth, availHeight): - # try to get the caption aligned - if self.caption: - self._getCaptionPara() - (w, h) = self.captionPara.wrap(self.width, availHeight - self.figureHeight) - self.captionHeight = h - self.height = self.captionHeight + self.figureHeight - if w>self.width: self.width = w - else: - self.height = self.figureHeight - self.dx = 0.5 * (availWidth - self.width) - return (self.width, self.height) - - def draw(self): - self.canv.translate(self.dx, 0) - if self.caption: - self._getCaptionPara() - self.drawCaption() - self.canv.translate(0, self.captionHeight) - if self.background: - self.drawBackground() - if self.border: - self.drawBorder() - self.drawFigure() - - def drawBorder(self): - self.canv.rect(0, 0, self.width, self.figureHeight) - - def _doBackground(self, color): - self.canv.saveState() - self.canv.setFillColor(self.background) - self.canv.rect(0, 0, self.width, self.figureHeight, fill=1) - self.canv.restoreState() - - def drawBackground(self): - """For use when using a figure on a differently coloured background. - Allows you to specify a colour to be used as a background for the figure.""" - if isColor(self.background): - self._doBackground(self.background) - else: - try: - c = toColor(self.background) - self._doBackground(c) - except: - pass - - def drawCaption(self): - self.captionPara.drawOn(self.canv, 0, 0) - - def drawFigure(self): - pass - -def drawPage(canvas,x, y, width, height): - #draws something which looks like a page - pth = canvas.beginPath() - corner = 0.05*width - - # shaded backdrop offset a little - canvas.setFillColorRGB(0.5,0.5,0.5) - canvas.rect(x + corner, y - corner, width, height, stroke=0, fill=1) - - #'sheet of paper' in light yellow - canvas.setFillColorRGB(1,1,0.9) - canvas.setLineWidth(0) - canvas.rect(x, y, width, height, stroke=1, fill=1) - - #reset - canvas.setFillColorRGB(0,0,0) - canvas.setStrokeColorRGB(0,0,0) - -class PageFigure(Figure): - """Shows a blank page in a frame, and draws on that. Used in - illustrations of how PLATYPUS works.""" - def __init__(self, background=None): - Figure.__init__(self, 3*inch, 3*inch) - self.caption = 'Figure 1 - a blank page' - self.captionStyle = captionStyle - self.background = background - - def drawVirtualPage(self): - pass - - def drawFigure(self): - drawPage(self.canv, 0.625*inch, 0.25*inch, 1.75*inch, 2.5*inch) - self.canv.translate(0.625*inch, 0.25*inch) - self.canv.scale(1.75/8.27, 2.5/11.69) - self.drawVirtualPage() - -class PlatPropFigure1(PageFigure): - """This shows a page with a frame on it""" - def __init__(self): - PageFigure.__init__(self) - self.caption = "Figure 1 - a page with a simple frame" - def drawVirtualPage(self): - demo1(self.canv) - -class FlexFigure(Figure): - """Base for a figure class with a caption. Can grow or shrink in proportion""" - def __init__(self, width, height, caption, background=None): - Figure.__init__(self, width, height, caption, - captionFont="Helvetica-Oblique", captionSize=8, - background=None) - self.shrinkToFit = 1 #if set and wrap is too tight, shrinks - self.growToFit = 1 #if set and wrap is too tight, shrinks - self.scaleFactor = None - self.captionStyle = ParagraphStyle( - 'Caption', - fontName='Times', #'Helvetica-Oblique', - fontSize=4, #8, - spaceBefore=9, #3, - alignment=TA_CENTER - ) - self._scaleFactor = None - self.background = background - - def _scale(self,availWidth,availHeight): - "Rescale to fit according to the rules, but only once" - if self._scaleFactor is None or self.width>availWidth or self.height>availHeight: - w, h = Figure.wrap(self, availWidth, availHeight) - captionHeight = h - self.figureHeight - if self.scaleFactor is None: - #scale factor None means auto - self._scaleFactor = min(availWidth/self.width,(availHeight-captionHeight)/self.figureHeight) - else: #they provided a factor - self._scaleFactor = self.scaleFactor - if self._scaleFactor<1 and self.shrinkToFit: - self.width = self.width * self._scaleFactor - 0.0001 - self.figureHeight = self.figureHeight * self._scaleFactor - elif self._scaleFactor>1 and self.growToFit: - self.width = self.width*self._scaleFactor - 0.0001 - self.figureHeight = self.figureHeight * self._scaleFactor - - def wrap(self, availWidth, availHeight): - self._scale(availWidth,availHeight) - return Figure.wrap(self, availWidth, availHeight) - - def split(self, availWidth, availHeight): - self._scale(availWidth,availHeight) - return Figure.split(self, availWidth, availHeight) - -class ImageFigure(FlexFigure): - """Image with a caption below it""" - def __init__(self, filename, caption, background=None): - assert os.path.isfile(filename), 'image file %s not found' % filename - from reportlab.lib.utils import ImageReader - w, h = ImageReader(filename).getSize() - self.filename = filename - FlexFigure.__init__(self, w, h, caption, background) - - def drawFigure(self): - self.canv.drawInlineImage(self.filename, - 0, 0,self.width, self.figureHeight) - -class DrawingFigure(FlexFigure): - """Drawing with a caption below it. Clunky, scaling fails.""" - def __init__(self, modulename, classname, caption, baseDir=None, background=None): - module = recursiveImport(modulename, baseDir) - klass = getattr(module, classname) - self.drawing = klass() - FlexFigure.__init__(self, - self.drawing.width, - self.drawing.height, - caption, - background) - self.growToFit = 1 - - def drawFigure(self): - self.canv.scale(self._scaleFactor, self._scaleFactor) - self.drawing.drawOn(self.canv, 0, 0) - - -try: - from rlextra.pageCatcher.pageCatcher import restoreForms, storeForms, storeFormsInMemory, restoreFormsInMemory - _hasPageCatcher = 1 -except ImportError: - _hasPageCatcher = 0 -if _hasPageCatcher: - #################################################################### - # - # PageCatcher plugins - # These let you use our PageCatcher product to add figures - # to other documents easily. - #################################################################### - class PageCatcherCachingMixIn: - "Helper functions to cache pages for figures" - - def getFormName(self, pdfFileName, pageNo): - #naming scheme works within a directory only - dirname, filename = os.path.split(pdfFileName) - root, ext = os.path.splitext(filename) - return '%s_page%d' % (root, pageNo) - - - def needsProcessing(self, pdfFileName, pageNo): - "returns 1 if no forms or form is older" - formName = self.getFormName(pdfFileName, pageNo) - if os.path.exists(formName + '.frm'): - formModTime = os.stat(formName + '.frm')[8] - pdfModTime = os.stat(pdfFileName)[8] - return (pdfModTime > formModTime) - else: - return 1 - - def processPDF(self, pdfFileName, pageNo): - formName = self.getFormName(pdfFileName, pageNo) - storeForms(pdfFileName, formName + '.frm', - prefix= formName + '_', - pagenumbers=[pageNo]) - #print 'stored %s.frm' % formName - return formName + '.frm' - - class cachePageCatcherFigureNonA4(FlexFigure, PageCatcherCachingMixIn): - """PageCatcher page with a caption below it. Size to be supplied.""" - # This should merge with PageFigure into one class that reuses - # form information to determine the page orientation... - def __init__(self, filename, pageNo, caption, width, height, background=None): - self.dirname, self.filename = os.path.split(filename) - if self.dirname == '': - self.dirname = os.curdir - self.pageNo = pageNo - self.formName = self.getFormName(self.filename, self.pageNo) + '_' + str(pageNo) - FlexFigure.__init__(self, width, height, caption, background) - - def drawFigure(self): - self.canv.saveState() - if not self.canv.hasForm(self.formName): - restorePath = self.dirname + os.sep + self.filename - #does the form file exist? if not, generate it. - formFileName = self.getFormName(restorePath, self.pageNo) + '.frm' - if self.needsProcessing(restorePath, self.pageNo): - #print 'preprocessing PDF %s page %s' % (restorePath, self.pageNo) - self.processPDF(restorePath, self.pageNo) - names = restoreForms(formFileName, self.canv) - self.canv.scale(self._scaleFactor, self._scaleFactor) - self.canv.doForm(self.formName) - self.canv.restoreState() - - class cachePageCatcherFigure(cachePageCatcherFigureNonA4): - """PageCatcher page with a caption below it. Presumes A4, Portrait. - This needs our commercial PageCatcher product, or you'll get a blank.""" - def __init__(self, filename, pageNo, caption, width=595, height=842, background=None): - cachePageCatcherFigureNonA4.__init__(self, filename, pageNo, caption, width, height, background=background) - - class PageCatcherFigureNonA4(FlexFigure): - """PageCatcher page with a caption below it. Size to be supplied.""" - # This should merge with PageFigure into one class that reuses - # form information to determine the page orientation... - _cache = {} - def __init__(self, filename, pageNo, caption, width, height, background=None, caching=None): - fn = self.filename = filename - self.pageNo = pageNo - fn = fn.replace(os.sep,'_').replace('/','_').replace('\\','_').replace('-','_').replace(':','_') - self.prefix = fn.replace('.','_')+'_'+str(pageNo)+'_' - self.formName = self.prefix + str(pageNo) - self.caching = caching - FlexFigure.__init__(self, width, height, caption, background) - - def drawFigure(self): - if not self.canv.hasForm(self.formName): - if self.filename in self._cache: - f,data = self._cache[self.filename] - else: - f = open(self.filename,'rb') - pdf = f.read() - f.close() - f, data = storeFormsInMemory(pdf, pagenumbers=[self.pageNo], prefix=self.prefix) - if self.caching=='memory': - self._cache[self.filename] = f, data - f = restoreFormsInMemory(data, self.canv) - self.canv.saveState() - self.canv.scale(self._scaleFactor, self._scaleFactor) - self.canv.doForm(self.formName) - self.canv.restoreState() - - class PageCatcherFigure(PageCatcherFigureNonA4): - """PageCatcher page with a caption below it. Presumes A4, Portrait. - This needs our commercial PageCatcher product, or you'll get a blank.""" - def __init__(self, filename, pageNo, caption, width=595, height=842, background=None, caching=None): - PageCatcherFigureNonA4.__init__(self, filename, pageNo, caption, width, height, background=background, caching=caching) - -def demo1(canvas): - frame = Frame( - 2*inch, # x - 4*inch, # y at bottom - 4*inch, # width - 5*inch, # height - showBoundary = 1 # helps us see what's going on - ) - bodyStyle = ParagraphStyle('Body', fontName='Times-Roman', fontSize=24, leading=28, spaceBefore=6) - para1 = Paragraph('Spam spam spam spam. ' * 5, bodyStyle) - para2 = Paragraph('Eggs eggs eggs. ' * 5, bodyStyle) - mydata = [para1, para2] - - #this does the packing and drawing. The frame will consume - #items from the front of the list as it prints them - frame.addFromList(mydata,canvas) - -def test1(): - c = Canvas('figures.pdf') - f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1) - v = PlatPropFigure1() - v.captionTextColor = toColor('blue') - v.captionBackColor = toColor('lightyellow') - f.addFromList([v],c) - c.save() - -if __name__ == '__main__': - test1() diff --git a/bin/reportlab/platypus/flowables.py b/bin/reportlab/platypus/flowables.py deleted file mode 100644 index f22dd9ee696..00000000000 --- a/bin/reportlab/platypus/flowables.py +++ /dev/null @@ -1,1051 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/flowables.py -__version__=''' $Id: flowables.py 2830 2006-04-05 15:18:32Z rgbecker $ ''' -__doc__=""" -A flowable is a "floating element" in a document whose exact position is determined by the -other elements that precede it, such as a paragraph, a diagram interspersed between paragraphs, -a section header, etcetera. Examples of non-flowables include page numbering annotations, -headers, footers, fixed diagrams or logos, among others. - -Flowables are defined here as objects which know how to determine their size and which -can draw themselves onto a page with respect to a relative "origin" position determined -at a higher level. The object's draw() method should assume that (0,0) corresponds to the -bottom left corner of the enclosing rectangle that will contain the object. The attributes -vAlign and hAlign may be used by 'packers' as hints as to how the object should be placed. - -Some Flowables also know how to "split themselves". For example a -long paragraph might split itself between one page and the next. - -Packers should set the canv attribute during wrap, split & draw operations to allow -the flowable to work out sizes etc in the proper context. - -The "text" of a document usually consists mainly of a sequence of flowables which -flow into a document from top to bottom (with column and page breaks controlled by -higher level components). -""" -import os -import string -from copy import deepcopy -from types import ListType, TupleType, StringType - -from reportlab.lib.colors import red, gray, lightgrey -from reportlab.lib.utils import fp_str -from reportlab.pdfbase import pdfutils - -from reportlab.rl_config import _FUZZ, overlapAttachedSpace -__all__=('TraceInfo','Flowable','XBox','Preformatted','Image','Spacer','PageBreak','SlowPageBreak', - 'CondPageBreak','KeepTogether','Macro','CallerMacro','ParagraphAndImage', - 'FailOnWrap','HRFlowable','PTOContainer','KeepInFrame','UseUpSpace') - - -class TraceInfo: - "Holder for info about where an object originated" - def __init__(self): - self.srcFile = '(unknown)' - self.startLineNo = -1 - self.startLinePos = -1 - self.endLineNo = -1 - self.endLinePos = -1 - -############################################################# -# Flowable Objects - a base class and a few examples. -# One is just a box to get some metrics. We also have -# a paragraph, an image and a special 'page break' -# object which fills the space. -############################################################# -class Flowable: - """Abstract base class for things to be drawn. Key concepts: - 1. It knows its size - 2. It draws in its own coordinate system (this requires the - base API to provide a translate() function. - """ - _fixedWidth = 0 #assume wrap results depend on arguments? - _fixedHeight = 0 - - def __init__(self): - self.width = 0 - self.height = 0 - self.wrapped = 0 - - #these are hints to packers/frames as to how the floable should be positioned - self.hAlign = 'LEFT' #CENTER/CENTRE or RIGHT - self.vAlign = 'BOTTOM' #MIDDLE or TOP - - #optional holder for trace info - self._traceInfo = None - self._showBoundary = None - - #many flowables handle text and must be processed in the - #absence of a canvas. tagging them with their encoding - #helps us to get conversions right. Use Python codec names. - self.encoding = None - - - def _drawOn(self,canv): - '''ensure canv is set on and then draw''' - self.canv = canv - self.draw()#this is the bit you overload - del self.canv - - def drawOn(self, canvas, x, y, _sW=0): - "Tell it to draw itself on the canvas. Do not override" - if _sW and hasattr(self,'hAlign'): - a = self.hAlign - if a in ['CENTER','CENTRE']: - x = x + 0.5*_sW - elif a == 'RIGHT': - x = x + _sW - elif a != 'LEFT': - raise ValueError, "Bad hAlign value "+str(a) - canvas.saveState() - canvas.translate(x, y) - self._drawOn(canvas) - if hasattr(self, '_showBoundary') and self._showBoundary: - #diagnostic tool support - canvas.setStrokeColor(gray) - canvas.rect(0,0,self.width, self.height) - canvas.restoreState() - - def wrapOn(self, canv, aW, aH): - '''intended for use by packers allows setting the canvas on - during the actual wrap''' - self.canv = canv - w, h = self.wrap(aW,aH) - del self.canv - return w, h - - def wrap(self, availWidth, availHeight): - """This will be called by the enclosing frame before objects - are asked their size, drawn or whatever. It returns the - size actually used.""" - return (self.width, self.height) - - def minWidth(self): - """This should return the minimum required width""" - return getattr(self,'_minWidth',self.width) - - def splitOn(self, canv, aW, aH): - '''intended for use by packers allows setting the canvas on - during the actual split''' - self.canv = canv - S = self.split(aW,aH) - del self.canv - return S - - def split(self, availWidth, availheight): - """This will be called by more sophisticated frames when - wrap fails. Stupid flowables should return []. Clever flowables - should split themselves and return a list of flowables""" - return [] - - def getKeepWithNext(self): - """returns boolean determining whether the next flowable should stay with this one""" - if hasattr(self,'keepWithNext'): return self.keepWithNext - elif hasattr(self,'style') and hasattr(self.style,'keepWithNext'): return self.style.keepWithNext - else: return 0 - - def getSpaceAfter(self): - """returns how much space should follow this item if another item follows on the same page.""" - if hasattr(self,'spaceAfter'): return self.spaceAfter - elif hasattr(self,'style') and hasattr(self.style,'spaceAfter'): return self.style.spaceAfter - else: return 0 - - def getSpaceBefore(self): - """returns how much space should precede this item if another item precedess on the same page.""" - if hasattr(self,'spaceBefore'): return self.spaceBefore - elif hasattr(self,'style') and hasattr(self.style,'spaceBefore'): return self.style.spaceBefore - else: return 0 - - def isIndexing(self): - """Hook for IndexingFlowables - things which have cross references""" - return 0 - - def identity(self, maxLen=None): - ''' - This method should attempt to return a string that can be used to identify - a particular flowable uniquely. The result can then be used for debugging - and or error printouts - ''' - if hasattr(self, 'getPlainText'): - r = self.getPlainText(identify=1) - elif hasattr(self, 'text'): - r = str(self.text) - else: - r = '...' - if r and maxLen: - r = r[:maxLen] - return "<%s at %s%s>%s" % (self.__class__.__name__, hex(id(self)), self._frameName(), r) - - def _frameName(self): - f = getattr(self,'_frame',None) - if f and f.id: return ' frame=%s' % f.id - return '' - -class XBox(Flowable): - """Example flowable - a box with an x through it and a caption. - This has a known size, so does not need to respond to wrap().""" - def __init__(self, width, height, text = 'A Box'): - Flowable.__init__(self) - self.width = width - self.height = height - self.text = text - - def __repr__(self): - return "XBox(w=%s, h=%s, t=%s)" % (self.width, self.height, self.text) - - def draw(self): - self.canv.rect(0, 0, self.width, self.height) - self.canv.line(0, 0, self.width, self.height) - self.canv.line(0, self.height, self.width, 0) - - #centre the text - self.canv.setFont('Times-Roman',12) - self.canv.drawCentredString(0.5*self.width, 0.5*self.height, self.text) - -def _trimEmptyLines(lines): - #don't want the first or last to be empty - while len(lines) and string.strip(lines[0]) == '': - lines = lines[1:] - while len(lines) and string.strip(lines[-1]) == '': - lines = lines[:-1] - return lines - -def _dedenter(text,dedent=0): - ''' - tidy up text - carefully, it is probably code. If people want to - indent code within a source script, you can supply an arg to dedent - and it will chop off that many character, otherwise it leaves - left edge intact. - ''' - lines = string.split(text, '\n') - if dedent>0: - templines = _trimEmptyLines(lines) - lines = [] - for line in templines: - line = string.rstrip(line[dedent:]) - lines.append(line) - else: - lines = _trimEmptyLines(lines) - - return lines - -class Preformatted(Flowable): - """This is like the HTML
 tag.
-    It attempts to display text exactly as you typed it in a fixed width "typewriter" font.
-    The line breaks are exactly where you put
-    them, and it will not be wrapped."""
-    def __init__(self, text, style, bulletText = None, dedent=0):
-        """text is the text to display. If dedent is set then common leading space
-        will be chopped off the front (for example if the entire text is indented
-        6 spaces or more then each line will have 6 spaces removed from the front).
-        """
-        self.style = style
-        self.bulletText = bulletText
-        self.lines = _dedenter(text,dedent)
-
-    def __repr__(self):
-        bT = self.bulletText
-        H = "Preformatted("
-        if bT is not None:
-            H = "Preformatted(bulletText=%s," % repr(bT)
-        return "%s'''\\ \n%s''')" % (H, string.join(self.lines,'\n'))
-
-    def wrap(self, availWidth, availHeight):
-        self.width = availWidth
-        self.height = self.style.leading*len(self.lines)
-        return (self.width, self.height)
-
-    def split(self, availWidth, availHeight):
-        #returns two Preformatted objects
-
-        #not sure why they can be called with a negative height
-        if availHeight < self.style.leading:
-            return []
-
-        linesThatFit = int(availHeight * 1.0 / self.style.leading)
-
-        text1 = string.join(self.lines[0:linesThatFit], '\n')
-        text2 = string.join(self.lines[linesThatFit:], '\n')
-        style = self.style
-        if style.firstLineIndent != 0:
-            style = deepcopy(style)
-            style.firstLineIndent = 0
-        return [Preformatted(text1, self.style), Preformatted(text2, style)]
-
-
-    def draw(self):
-        #call another method for historical reasons.  Besides, I
-        #suspect I will be playing with alternate drawing routines
-        #so not doing it here makes it easier to switch.
-
-        cur_x = self.style.leftIndent
-        cur_y = self.height - self.style.fontSize
-        self.canv.addLiteral('%PreformattedPara')
-        if self.style.textColor:
-            self.canv.setFillColor(self.style.textColor)
-        tx = self.canv.beginText(cur_x, cur_y)
-        #set up the font etc.
-        tx.setFont( self.style.fontName,
-                    self.style.fontSize,
-                    self.style.leading)
-
-        for text in self.lines:
-            tx.textLine(text)
-        self.canv.drawText(tx)
-
-class Image(Flowable):
-    """an image (digital picture).  Formats supported by PIL/Java 1.4 (the Python/Java Imaging Library
-       are supported.  At the present time images as flowables are always centered horozontally
-       in the frame. We allow for two kinds of lazyness to allow for many images in a document
-       which could lead to file handle starvation.
-       lazy=1 don't open image until required.
-       lazy=2 open image when required then shut it.
-    """
-    _fixedWidth = 1
-    _fixedHeight = 1
-    def __init__(self, filename, width=None, height=None, kind='direct', mask="auto", lazy=1):
-        """If size to draw at not specified, get it from the image."""
-        self.hAlign = 'CENTER'
-        self._mask = mask
-        # if it is a JPEG, will be inlined within the file -
-        # but we still need to know its size now
-        fp = hasattr(filename,'read')
-        if fp:
-            self._file = filename
-            self.filename = `filename`
-        else:
-            self._file = self.filename = filename
-        if not fp and os.path.splitext(filename)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
-            from reportlab.lib.utils import open_for_read
-            f = open_for_read(filename, 'b')
-            info = pdfutils.readJPEGInfo(f)
-            f.close()
-            self.imageWidth = info[0]
-            self.imageHeight = info[1]
-            self._img = None
-            self._setup(width,height,kind,0)
-        elif fp:
-            self._setup(width,height,kind,0)
-        else:
-            self._setup(width,height,kind,lazy)
-
-    def _setup(self,width,height,kind,lazy):
-        self._lazy = lazy
-        self._width = width
-        self._height = height
-        self._kind = kind
-        if lazy<=0: self._setup_inner()
-
-    def _setup_inner(self):
-        width = self._width
-        height = self._height
-        kind = self._kind
-        img = self._img
-        if img: self.imageWidth, self.imageHeight = img.getSize()
-        if self._lazy>=2: del self._img
-        if kind in ['direct','absolute']:
-            self.drawWidth = width or self.imageWidth
-            self.drawHeight = height or self.imageHeight
-        elif kind in ['percentage','%']:
-            self.drawWidth = self.imageWidth*width*0.01
-            self.drawHeight = self.imageHeight*height*0.01
-        elif kind in ['bound','proportional']:
-            factor = min(float(width)/self.imageWidth,float(height)/self.imageHeight)
-            self.drawWidth = self.imageWidth*factor
-            self.drawHeight = self.imageHeight*factor
-
-    def __getattr__(self,a):
-        if a=='_img':
-            from reportlab.lib.utils import ImageReader  #this may raise an error
-            self._img = ImageReader(self._file)
-            del self._file
-            return self._img
-        elif a in ('drawWidth','drawHeight','imageWidth','imageHeight'):
-            self._setup_inner()
-            return self.__dict__[a]
-        raise AttributeError(a)
-
-    def wrap(self, availWidth, availHeight):
-        #the caller may decide it does not fit.
-        return (self.drawWidth, self.drawHeight)
-
-    def draw(self):
-        lazy = self._lazy
-        if lazy>=2: self._lazy = 1
-        self.canv.drawImage(    self._img or self.filename,
-                                0,
-                                0,
-                                self.drawWidth,
-                                self.drawHeight,
-                                mask=self._mask,
-                                )
-        if lazy>=2:
-            self._img = None
-            self._lazy = lazy
-
-    def identity(self,maxLen=None):
-        r = Flowable.identity(self,maxLen)
-        if r[-4:]=='>...' and type(self.filename) is StringType:
-            r = "%s filename=%s>" % (r[:-4],self.filename)
-        return r
-
-class Spacer(Flowable):
-    """A spacer just takes up space and doesn't draw anything - it guarantees
-       a gap between objects."""
-    _fixedWidth = 1
-    _fixedHeight = 1
-    def __init__(self, width, height):
-        self.width = width
-        self.height = height
-
-    def __repr__(self):
-        return "%s(%s, %s)" % (self.__class__.__name__,self.width, self.height)
-
-    def draw(self):
-        pass
-
-class UseUpSpace(Flowable):
-    def __init__(self):
-        pass
-
-    def __repr__(self):
-        return "%s()" % self.__class__.__name__
-
-    def wrap(self, availWidth, availHeight):
-        self.width = availWidth
-        self.height = availHeight
-        return (availWidth,availHeight-1e-8)  #step back a point
-
-    def draw(self):
-        pass
-
-class PageBreak(UseUpSpace):
-    """Move on to the next page in the document.
-       This works by consuming all remaining space in the frame!"""
-
-class SlowPageBreak(PageBreak):
-    pass
-
-class CondPageBreak(Spacer):
-    """Throw a page if not enough vertical space"""
-    def __init__(self, height):
-        self.height = height
-
-    def __repr__(self):
-        return "CondPageBreak(%s)" %(self.height,)
-
-    def wrap(self, availWidth, availHeight):
-        if availHeightaH and (not self._maxHeight or aH>self._maxHeight)
-        C1 = self._H0>aH
-        if C0 or C1:
-            if C0:
-                from doctemplate import FrameBreak
-                A = FrameBreak
-            else:
-                from doctemplate import NullActionFlowable
-                A = NullActionFlowable
-            S.insert(0,A())
-        return S
-
-    def identity(self, maxLen=None):
-        msg = " containing :%s" % (hex(id(self)),self._frameName(),"\n".join([f.identity() for f in self._content]))
-        if maxLen:
-            return msg[0:maxLen]
-        else:
-            return msg
-
-class Macro(Flowable):
-    """This is not actually drawn (i.e. it has zero height)
-    but is executed when it would fit in the frame.  Allows direct
-    access to the canvas through the object 'canvas'"""
-    def __init__(self, command):
-        self.command = command
-    def __repr__(self):
-        return "Macro(%s)" % repr(self.command)
-    def wrap(self, availWidth, availHeight):
-        return (0,0)
-    def draw(self):
-        exec self.command in globals(), {'canvas':self.canv}
-
-class CallerMacro(Flowable):
-    '''
-    like Macro, but with callable command(s)
-    drawCallable(self)
-    wrapCallable(self,aW,aH)
-    '''
-    def __init__(self, drawCallable=None, wrapCallable=None):
-        _ = lambda *args: None
-        self._drawCallable = drawCallable or _
-        self._wrapCallable = wrapCallable or _
-    def __repr__(self):
-        return "CallerMacro(%s)" % repr(self.command)
-    def wrap(self, aW, aH):
-        self._wrapCallable(self,aW,aH)
-        return (0,0)
-    def draw(self):
-        self._drawCallable(self)
-
-class ParagraphAndImage(Flowable):
-    '''combine a Paragraph and an Image'''
-    def __init__(self,P,I,xpad=3,ypad=3,side='right'):
-        self.P = P
-        self.I = I
-        self.xpad = xpad
-        self.ypad = ypad
-        self._side = side
-
-    def getSpaceBefore(self):
-        return max(self.P.getSpaceBefore(),self.I.getSpaceBefore())
-
-    def getSpaceAfter(self):
-        return max(self.P.getSpaceAfter(),self.I.getSpaceAfter())
-
-    def wrap(self,availWidth,availHeight):
-        wI, hI = self.I.wrap(availWidth,availHeight)
-        self.wI = wI
-        self.hI = hI
-        # work out widths array for breaking
-        self.width = availWidth
-        P = self.P
-        style = P.style
-        xpad = self.xpad
-        ypad = self.ypad
-        leading = style.leading
-        leftIndent = style.leftIndent
-        later_widths = availWidth - leftIndent - style.rightIndent
-        intermediate_widths = later_widths - xpad - wI
-        first_line_width = intermediate_widths - style.firstLineIndent
-        P.width = 0
-        nIW = int((hI+ypad)/leading)
-        P.blPara = P.breakLines([first_line_width] + nIW*[intermediate_widths]+[later_widths])
-        if self._side=='left':
-            self._offsets = [wI+xpad]*(1+nIW)+[0]
-        P.height = len(P.blPara.lines)*leading
-        self.height = max(hI,P.height)
-        return (self.width, self.height)
-
-    def split(self,availWidth, availHeight):
-        P, wI, hI, ypad = self.P, self.wI, self.hI, self.ypad
-        if hI+ypad>availHeight or len(P.frags)<=0: return []
-        S = P.split(availWidth,availHeight)
-        if not S: return S
-        P = self.P = S[0]
-        del S[0]
-        style = P.style
-        P.height = len(self.P.blPara.lines)*style.leading
-        self.height = max(hI,P.height)
-        return [self]+S
-
-    def draw(self):
-        canv = self.canv
-        if self._side=='left':
-            self.I.drawOn(canv,0,self.height-self.hI)
-            self.P._offsets = self._offsets
-            try:
-                self.P.drawOn(canv,0,0)
-            finally:
-                del self.P._offsets
-        else:
-            self.I.drawOn(canv,self.width-self.wI-self.xpad,self.height-self.hI)
-            self.P.drawOn(canv,0,0)
-
-class FailOnWrap(Flowable):
-    def wrap(self, availWidth, availHeight):
-        raise ValueError("FailOnWrap flowable wrapped and failing as ordered!")
-
-    def draw(self):
-        pass
-
-class FailOnDraw(Flowable):
-    def wrap(self, availWidth, availHeight):
-        return (0,0)
-
-    def draw(self):
-        raise ValueError("FailOnDraw flowable drawn, and failing as ordered!")
-
-class HRFlowable(Flowable):
-    '''Like the hr tag'''
-    def __init__(self,
-            width="80%",
-            thickness=1,
-            lineCap='round',
-            color=lightgrey,
-            spaceBefore=1, spaceAfter=1,
-            hAlign='CENTER', vAlign='BOTTOM',
-            dash=None):
-        Flowable.__init__(self)
-        self.width = width
-        self.lineWidth = thickness
-        self.lineCap=lineCap
-        self.spaceBefore = spaceBefore
-        self.spaceAfter = spaceAfter
-        self.color = color
-        self.hAlign = hAlign
-        self.vAlign = vAlign
-        self.dash = dash
-
-    def __repr__(self):
-        return "HRFlowable(width=%s, height=%s)" % (self.width, self.height)
-
-    def wrap(self, availWidth, availHeight):
-        w = self.width
-        if type(w) is type(''):
-            w = w.strip()
-            if w.endswith('%'): w = availWidth*float(w[:-1])*0.01
-            else: w = float(w)
-        w = min(w,availWidth)
-        self._width = w
-        return w, self.lineWidth
-
-    def draw(self):
-        canv = self.canv
-        canv.saveState()
-        canv.setLineWidth(self.lineWidth)
-        canv.setLineCap({'butt':0,'round':1, 'square': 2}[self.lineCap.lower()])
-        canv.setStrokeColor(self.color)
-        if self.dash: canv.setDash(self.dash)
-        canv.line(0, 0, self._width, self.height)
-        canv.restoreState()
-
-class _PTOInfo:
-    def __init__(self,trailer,header):
-        self.trailer = _flowableSublist(trailer)
-        self.header = _flowableSublist(header)
-
-class _Container(_ContainerSpace):  #Abstract some common container like behaviour
-    def drawOn(self, canv, x, y, _sW=0, scale=1.0, content=None, aW=None):
-        '''we simulate being added to a frame'''
-        pS = 0
-        if aW is None: aW = self.width
-        aW = scale*(aW+_sW)
-        if content is None:
-            content = self._content
-        y += self.height*scale
-        for c in content:
-            w, h = c.wrapOn(canv,aW,0xfffffff)
-            if w<_FUZZ or h<_FUZZ: continue
-            if c is not content[0]: h += max(c.getSpaceBefore()-pS,0)
-            y -= h
-            c.drawOn(canv,x,y,_sW=aW-w)
-            if c is not content[-1]:
-                pS = c.getSpaceAfter()
-                y -= pS
-
-class PTOContainer(_Container,Flowable):
-    '''PTOContainer(contentList,trailerList,headerList)
-    
-    A container for flowables decorated with trailer & header lists.
-    If the split operation would be called then the trailer and header
-    lists are injected before and after the split. This allows specialist
-    "please turn over" and "continued from previous" like behaviours.''' 
-    def __init__(self,content,trailer=None,header=None):
-        I = _PTOInfo(trailer,header)
-        self._content = C = []
-        for _ in _flowableSublist(content):
-            if isinstance(_,PTOContainer):
-                C.extend(_._content)
-            else:
-                C.append(_)
-                if not hasattr(_,'_ptoinfo'): _._ptoinfo = I
-
-    def wrap(self,availWidth,availHeight):
-        self.width, self.height = _listWrapOn(self._content,availWidth,self.canv)
-        return self.width,self.height
-
-    def split(self, availWidth, availHeight):
-        if availHeight<0: return []
-        canv = self.canv
-        C = self._content
-        x = i = H = pS = hx = 0
-        n = len(C)
-        I2W = {}
-        for x in xrange(n):
-            c = C[x]
-            I = c._ptoinfo
-            if I not in I2W.keys():
-                T = I.trailer
-                Hdr = I.header
-                tW, tH = _listWrapOn(T, availWidth, self.canv)
-                tSB = T[0].getSpaceBefore()
-                I2W[I] = T,tW,tH,tSB
-            else:
-                T,tW,tH,tSB = I2W[I]
-            _, h = c.wrapOn(canv,availWidth,0xfffffff)
-            if x:
-                hx = max(c.getSpaceBefore()-pS,0)
-                h += hx
-            pS = c.getSpaceAfter()
-            H += h+pS
-            tHS = tH+max(tSB,pS)
-            if H+tHS>=availHeight-_FUZZ: break
-            i += 1
-
-        #first retract last thing we tried
-        H -= (h+pS)
-
-        #attempt a sub split on the last one we have
-        aH = (availHeight-H-tHS-hx)*0.99999
-        if aH>=0.05*availHeight:
-            SS = c.splitOn(canv,availWidth,aH)
-        else:
-            SS = []
-        F = [UseUpSpace()]
-
-        if len(SS)>1:
-            R1 = C[:i] + SS[:1] + T + F
-            R2 = Hdr + SS[1:]+C[i+1:]
-        elif not i:
-            return []
-        else:
-            R1 = C[:i]+T+F
-            R2 = Hdr + C[i:]
-        T =  R1 + [PTOContainer(R2,deepcopy(I.trailer),deepcopy(I.header))]
-        return T
-
-#utility functions used by KeepInFrame
-def _hmodel(s0,s1,h0,h1):
-    # calculate the parameters in the model
-    # h = a/s**2 + b/s
-    a11 = 1./s0**2
-    a12 = 1./s0
-    a21 = 1./s1**2
-    a22 = 1./s1
-    det = a11*a22-a12*a21
-    b11 = a22/det
-    b12 = -a12/det
-    b21 = -a21/det
-    b22 = a11/det
-    a = b11*h0+b12*h1
-    b = b21*h0+b22*h1
-    return a,b
-
-def _qsolve(h,(a,b)):
-    '''solve the model v = a/s**2 + b/s for an s which gives us v==h'''
-    if abs(a)<=_FUZZ:
-        return b/h
-    t = 0.5*b/a
-    from math import sqrt
-    f = -h/a
-    r = t*t-f
-    if r<0: return None
-    r = sqrt(r)
-    if t>=0:
-        s1 = -t - r 
-    else:
-        s1 = -t + r
-    s2 = f/s1
-    return max(1./s1, 1./s2)
-
-class KeepInFrame(_Container,Flowable):
-    def __init__(self, maxWidth, maxHeight, content=[], mergeSpace=1, mode='shrink', name=''):
-        '''mode describes the action to take when overflowing
-            error       raise an error in the normal way
-            continue    ignore ie just draw it and report maxWidth, maxHeight
-            shrink      shrinkToFit
-            truncate    fit as much as possible
-        '''
-        self.name = name
-        self.maxWidth = maxWidth
-        self.maxHeight = maxHeight
-        self.mode = mode
-        assert mode in ('error','overflow','shrink','truncate'), '%s invalid mode value %s' % (self.identity(),mode)
-        assert maxHeight>=0,  '%s invalid maxHeight value %s' % (self.identity(),maxHeight)
-        if mergeSpace is None: mergeSpace = overlapAttachedSpace
-        self.mergespace = mergeSpace
-        self._content = content
-
-    def _getAvailableWidth(self):
-        return self.maxWidth - self._leftExtraIndent - self._rightExtraIndent
-
-    def identity(self, maxLen=None):
-        return "<%s at %s%s%s> size=%sx%s" % (self.__class__.__name__, hex(id(self)), self._frameName(),
-                getattr(self,'name','') and (' name="%s"'% getattr(self,'name','')) or '',
-                getattr(self,'maxWidth','') and (' maxWidth=%s'%fp_str(getattr(self,'maxWidth',0))) or '',
-                getattr(self,'maxHeight','')and (' maxHeight=%s' % fp_str(getattr(self,'maxHeight')))or '')
-
-    def wrap(self,availWidth,availHeight):
-        from doctemplate import LayoutError
-        mode = self.mode
-        maxWidth = float(self.maxWidth or availWidth)
-        maxHeight = float(self.maxHeight or availHeight)
-        W, H = _listWrapOn(self._content,availWidth,self.canv)
-        if (mode=='error' and (W>availWidth+_FUZZ or H>availHeight+_FUZZ)):
-            ident = 'content %sx%s too large for %s' % (W,H,self.identity(30))
-            #leave to keep apart from the raise
-            raise LayoutError(ident)
-        elif W<=availWidth+_FUZZ and H<=availHeight+_FUZZ:
-            self.width = W-_FUZZ      #we take what we get
-            self.height = H-_FUZZ
-        elif (maxWidth>=availWidth+_FUZZ or maxHeight>=availHeight+_FUZZ):
-            ident = 'Specified size too large for available space %sx%s in %s' % (availWidth,availHeight,self.identity(30))
-            #leave to keep apart from the raise
-            raise LayoutError(ident)
-        elif mode in ('overflow','truncate'):   #we lie
-            self.width = min(maxWidth,W)-_FUZZ
-            self.height = min(maxHeight,H)-_FUZZ
-        else:
-            def func(x):
-                W, H = _listWrapOn(self._content,x*availWidth,self.canv)
-                W /= x
-                H /= x
-                return W, H
-            W0 = W
-            H0 = H
-            s0 = 1
-            if W>maxWidth+_FUZZ:
-                #squeeze out the excess width and or Height
-                s1 = W/maxWidth
-                W, H = func(s1)
-                if H<=maxHeight+_FUZZ:
-                    self.width = W-_FUZZ
-                    self.height = H-_FUZZ
-                    self._scale = s1
-                    return W,H
-                s0 = s1
-                H0 = H
-                W0 = W
-            s1 = H/maxHeight
-            W, H = func(s1)
-            self.width = W-_FUZZ
-            self.height = H-_FUZZ
-            self._scale = s1
-            if H=maxHeight+_FUZZ:
-                #the standard case W should be OK, H is short we want
-                #to find the smallest s with H<=maxHeight
-                H1 = H
-                for f in 0, 0.01, 0.05, 0.10, 0.15:
-                    #apply the quadratic model
-                    s = _qsolve(maxHeight*(1-f),_hmodel(s0,s1,H0,H1))
-                    W, H = func(s)
-                    if H<=maxHeight+_FUZZ and W<=maxWidth+_FUZZ:
-                        self.width = W-_FUZZ
-                        self.height = H-_FUZZ
-                        self._scale = s
-                        break
-
-        return self.width, self.height
-
-    def drawOn(self, canv, x, y, _sW=0):
-        scale = getattr(self,'_scale',1.0)
-        truncate = self.mode=='truncate'
-        ss = scale!=1.0 or truncate
-        if ss:
-            canv.saveState()
-            if truncate:
-                p = canv.beginPath()
-                p.rect(x, y, self.width,self.height)
-                canv.clipPath(p,stroke=0)
-            else:
-                canv.translate(x,y)
-                x=y=0
-                canv.scale(1.0/scale, 1.0/scale)
-        _Container.drawOn(self, canv, x, y, _sW=_sW, scale=scale)
-        if ss: canv.restoreState()
-
-class ImageAndFlowables(_Container,Flowable):
-    '''combine a list of flowables and an Image'''
-    def __init__(self,I,F,imageLeftPadding=0,imageRightPadding=3,imageTopPadding=0,imageBottomPadding=3,
-                    imageSide='right'):
-        self._content = _flowableSublist(F)
-        self._I = I
-        self._irpad = imageRightPadding
-        self._ilpad = imageLeftPadding
-        self._ibpad = imageBottomPadding
-        self._itpad = imageTopPadding
-        self._side = imageSide
-
-    def getSpaceAfter(self):
-        if hasattr(self,'_C1'):
-            C = self._C1
-        elif hasattr(self,'_C0'):
-            C = self._C0
-        else:
-            C = self._content
-        return _Container.getSpaceAfter(self,C)
-
-    def getSpaceBefore(self):
-        return max(self._I.getSpaceBefore(),_Container.getSpaceBefore(self))
-
-    def _reset(self):
-        for a in ('_wrapArgs','_C0','_C1'):
-            try:
-                delattr(self,a)
-            except:
-                pass
-
-    def wrap(self,availWidth,availHeight):
-        canv = self.canv
-        if hasattr(self,'_wrapArgs'):
-            if self._wrapArgs==(availWidth,availHeight):
-                return self.width,self.height
-            self._reset()
-        self._wrapArgs = availWidth, availHeight
-        wI, hI = self._I.wrap(availWidth,availHeight)
-        self._wI = wI
-        self._hI = hI
-        ilpad = self._ilpad
-        irpad = self._irpad
-        ibpad = self._ibpad
-        itpad = self._itpad
-        self._iW = availWidth - irpad - wI - ilpad
-        aH = itpad + hI + ibpad
-        W,H0,self._C0,self._C1 = self._findSplit(canv,self._iW,aH)
-        self.width = availWidth
-        aH = self._aH = max(aH,H0)
-        if not self._C1:
-            self.height = aH
-        else:
-            W1,H1 = _listWrapOn(self._C1,availWidth,canv)
-            self.height = aH+H1
-        return self.width, self.height
-
-    def split(self,availWidth, availHeight):
-        if hasattr(self,'_wrapArgs'):
-            if self._wrapArgs!=(availWidth,availHeight):
-                self._reset()
-        W,H=self.wrap(availWidth,availHeight)
-        if self._aH>availHeight: return []
-        C1 = self._C1
-        if C1:
-            c0 = C1[0]
-            S = c0.split(availWidth,availHeight-self._aH)
-            if not S:
-                self._C1 = []
-                self.height = self._aH
-            else:
-                self._C1 = [S[0]]
-                self.height = self._aH + S[0].height
-                C1 = S[1:]+C1[1:]
-        else:
-            self._C1 = []
-            self.height = self._aH
-        return [self]+C1
-
-    def drawOn(self, canv, x, y, _sW=0):
-        if self._side=='left':
-            Ix = x + self._ilpad
-            Fx = Ix+ self._irpad + self._wI
-        else:
-            Ix = x + self.width-self._wI-self._irpad - self._ilpad
-            Fx = x
-        self._I.drawOn(canv,Ix,y+self.height-self._itpad-self._hI)
-        _Container.drawOn(self, canv, Fx, y, content=self._C0, aW=self._iW)
-        if self._C1:
-            _Container.drawOn(self, canv, x, y-self._aH,content=self._C1)
-
-    def _findSplit(self,canv,availWidth,availHeight,mergeSpace=1,obj=None):
-        '''return max width, required height for a list of flowables F'''
-        W = 0
-        H = 0
-        pS = sB = 0
-        atTop = 1
-        F = self._content
-        for i,f in enumerate(F):
-            w,h = f.wrapOn(canv,availWidth,0xfffffff)
-            if w<=_FUZZ or h<=_FUZZ: continue
-            W = max(W,w)
-            if not atTop:
-                s = f.getSpaceBefore()
-                if mergeSpace: s = max(s-pS,0)
-                H += s
-            else:
-                if obj is not None: obj._spaceBefore = f.getSpaceBefore()
-                atTop = 0
-            if H>=availHeight:
-                return W, availHeight, F[:i],F[i:]
-            H += h
-            if H>availHeight:
-                from paragraph import Paragraph
-                aH = availHeight-(H-h)
-                if isinstance(f,(Paragraph,Preformatted)):
-                    leading = f.style.leading
-                    nH = leading*int(aH/float(leading))+_FUZZ
-                    if nH0:
-            flowable._frame = self
-            flowable.canv = canv #so they can use stringWidth etc
-            w, h = flowable.wrap(aW, h)
-            del flowable.canv, flowable._frame
-        else:
-            return 0
-
-        h += s
-        y -= h
-
-        if y < p-_FUZZ:
-            if not rl_config.allowTableBoundsErrors and ((h>self._aH or w>aW) and not trySplit):
-                raise "LayoutError", "Flowable %s (%sx%s points) too large for frame (%sx%s points)." % (
-                    flowable.__class__, w,h, aW,self._aH)
-            return 0
-        else:
-            #now we can draw it, and update the current point.
-            flowable._frame = self
-            flowable.drawOn(canv, self._x + self._leftExtraIndent, y, _sW=aW-w)
-            if self._debug: logger.debug('drew %s' % flowable.identity())
-            del flowable._frame
-            s = flowable.getSpaceAfter()
-            y -= s
-            if self._oASpace: self._prevASpace = s
-            if y!=self._y: self._atTop = 0
-            self._y = y
-            return 1
-
-    add = _add
-
-    def split(self,flowable,canv):
-        '''Ask the flowable to split using up the available space.'''
-        y = self._y
-        p = self._y1p
-        s = 0
-        if not self._atTop: s = flowable.getSpaceBefore()
-        flowable.canv = canv    #some flowables might need this
-        r = flowable.split(self._aW, y-p-s)
-        del flowable.canv
-        return r
-
-    def drawBoundary(self,canv):
-        "draw the frame boundary as a rectangle (primarily for debugging)."
-        from reportlab.lib.colors import Color, CMYKColor, toColor
-        sb = self.showBoundary
-        isColor = type(sb) in (type(''),type(()),type([])) or isinstance(sb,Color)
-        if isColor:
-            sb = toColor(sb,self)
-            if sb is self: isColor = 0
-            else:
-                canv.saveState()
-                canv.setStrokeColor(sb)
-        canv.rect(
-                self._x1,
-                self._y1,
-                self._x2 - self._x1,
-                self._y2 - self._y1
-                )
-        if isColor: canv.restoreState()
-
-    def addFromList(self, drawlist, canv):
-        """Consumes objects from the front of the list until the
-        frame is full.  If it cannot fit one object, raises
-        an exception."""
-
-        if self._debug: logger.debug("enter Frame.addFromlist() for frame %s" % self.id)
-        if self.showBoundary:
-            self.drawBoundary(canv)
-
-        while len(drawlist) > 0:
-            head = drawlist[0]
-            if self.add(head,canv,trySplit=0):
-                del drawlist[0]
-            else:
-                #leave it in the list for later
-                break
diff --git a/bin/reportlab/platypus/para.py b/bin/reportlab/platypus/para.py
deleted file mode 100644
index 554c11003b0..00000000000
--- a/bin/reportlab/platypus/para.py
+++ /dev/null
@@ -1,2394 +0,0 @@
-"""new experimental paragraph implementation
-
-Intended to allow support for paragraphs in paragraphs, hotlinks,
-embedded flowables, and underlining.  The main entry point is the
-function
-
-def Paragraph(text, style, bulletText=None, frags=None)
-
-Which is intended to be plug compatible with the "usual" platypus
-paragraph except that it supports more functionality.
-
-In this implementation you may embed paragraphs inside paragraphs
-to create hierarchically organized documents.
-
-This implementation adds the following paragraph-like tags (which
-support the same attributes as paragraphs, for font specification, etc).
-
-- Unnumberred lists (ala html):
-
-    
    -
  • first one
  • -
  • second one
  • -
- -Also

eh$UOb@g41NZLrzF(hse0#(jsET?}rFt}5}rVRGiOig^OZDJT)r%1?&o z?Qm74`<4%!DeFqEQre9a2I6;Z2yf~E+uMQSOlhc}uxk~6rfBR#-KL3U!N*~ryhEvl z7-Pxy+tsm%EvhI5WA+UQ(Etk>!6X>@fqO(lz4S5!mRD>dqDyLut;QPS&kR-U0vl{w=EuWqLO+X zVr|natK3syr#&+6Sqs^e$sdB;0G2b#zcHc|mdq>ZZKz{=@QzouGUM>9>egNIY;3^M z_pMVhLRJt$mjI+THG1^)!# zohd6i6@>EcfX4K*1G&mVn6V`!ApIZ5i?)CWzwQ*aSd8C|o+aU+>;Oh{!>cR}p*{a~ zP~&Xp9fI!qMjvPYzqS0l-mIKosjEg%KaV@P&;)k_=Nr<(YMolo$d2ub3O@1!9IGT= zSkj~B8EGPP^R)i?#OxZllmNA!t&$p3t8NVR@EcyY?#VUO-TvefvR?0@>D0x&pL>fC zflkoGC`BDtsQI|5S5+%aL{uCwF-d^P$2r;-U(?*v>v_Yo{`^B21sSsmRRN;6fiOzO zhpkhieJhi?3Hb>`Hkc%WC4vL7S4dz3YDwTp(sNTCGDiYpQ+gnY?oi|2TxGB@L1qWX zT9j2hW3&mas~v7k$A5DgtfF?1T9BJ~ido(KsHBOug#*!8LDHrJ@s1bbueT2KwFnH~ zM+6HITmf@1YbB&@_BUhk1^;sL#?R6WPk>dO+Eu%-RpRii1*Wp%#51T+&7-9(g?uhR($f$AVxTqDY zzVZau!`NT3?dNwR`r%VD4$%VA1VTkeNQ@%EMM_09oC%9uUaUH+2!4 z%l>SH=Fv+76AZ*1a8`+Mr5|qV9CIRbGedgVj)Cy{QM{7n3yxQFqSqd+)d&4d`S|M9 LvA{_w%Dbhiaj`ZH8NfDHy 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,,kl8"&2NBd%Kaogum165&NYZBITmkcQDo:\lEnA -GJETphXmY&(BXdG&0O5g!!*-(#S8+DJ,fTO":,P]5_&h8!X&eu$f\(VI^5_q -jm)jbosgIBN01+GE&G/,0E`$J,.ogcq?_*e\?bYbO$E4*_M&A1cC@I!9Dd\' -+4?4QDr'3BbF=2qLTa,V3(b)tC;HhK!DHiEs= -#e!]_gC"9\WeUZ]%d_rZDm+pZHhMjerV*pJn(tTrkH8+&G'_fGA&n<1gUDI\ -)T%dl>;gEVr7uSSD=[2`\)2)Y\8d.Vp$:55>ISM;g9k_IXBE5_odUJ&4fhbj -+t4u*CCeUS/R,f.kn4Mb(GB+%B[KmgIC4%Q_Cr\%1,1M'_1Dj^kKfbZDr.!E -F(WairP]R[bk1DXB:hnj5(2_GWLnY9BgP/'PUTNpnM"tB`!I)(N#O]nba:,[ -7un]KC=L=e,=d\00l5LHTgOSF))>aLTKrYb%NI015l^i^qt9:L_$;(Sl-nh/ -pYP5l]D"DU][No_Y;b?do<#LYHN!Ng-V^$t+XoX`J[UDW6NstgiU^:#aXJYf -@T/-h#kbk9#UMApV<$^um2e#j'7`g:&J1#=2rAubQ**M!it6f3,*@pVZY%I6 -6o/H!J4.k`9lkR_-;U4ln3a&Bj2[3$3u/IFT]*eDlR2scpu.!iLu\C$^CPtn -H@SNS^AI?QDnk,W"$D00h*&FIlDn4&G3't(Uo5[]m!,=p/AQ+U,=dd\o,oLh -`[7$Kifm(afHdLC\5iol9&c'@AXlrl+e2TcVCt*"D43P1/tBd4IK''*Z:Y1g -&tsE#;eD`AXDo:8;HP?kPAJ]<++5EP`)3&A0_Gc=bj+0GNAo^=jcrnRI.>2- -?@.[$o*RI.f9aJ`&3E/CRVaf0!I//;.6.N@dhWJ":#_Dm4P7SLk/f_K3$fY" -@lB&>=ZT!#RpKMq"Z6@j)ODfZ2)o?9+0oW[2)q0V"P -i5SJT2Aq@18&2N,MORue((.$!o41l.*at,LGSoZG'&i)k+_>o\;RS\8iUnSbf.@["h4W^daDC< -[+g5DH18l\"e-&NZV?P&#\?tUg#Q>8N;^H/+c'&47a2<,ZndtDk/m0)o'Y9I -"Af)p(^a#(U5qJ$"NO<.;GoitP^.[:Han@R-"EJk':6\TnrLq,.c]R(i1M-HV6>$DWiJnWu`pbXf8^#tsp$Blf[o2f0&&E8Fj;UkH;2KPeTN'9<-` -_E>O(dS$eNYO@/]?Jm>IbNhuC)(3i7QGjt[o_n[]],>Y46C;2%ZOfU,n6RL6 -pQXGh.Ts48AWn&r=TE#APblWI!jfTH5qs'iZ7I([Rcm#)4]s3/IWG@.!hl8% -k"5Ai2hk31"fITC&?Wl%R3r7DZT<9r#jE`;DgM?VnOPTHJ6NEF=DX0oV=;jR -N"ee-'gN(,,1IMA,*\r!P!#ZJb/Yi%P$?h-bS)ApoF1$2S`.D2UXU#ifa/5t -gE^EM\-Jt.FMpZj7&UVY^aA_J@B/D`\QM+D5?Y%:r8SkQX)mILmG#,5Qa$@m -8\`%IN+JM>F2lkn-6bE9+'C"P4$:%88J]Hko.1njrgkG16n$[KbII#_p%Qu7 -AE+ku0`TXT)a,W-TbqN<#jU93V[&HNckm'+eCt(+$2Wka!3/NpDgDtsPjuY! -&Osl;iABPJ;B\[/"UG*'SNM&U8$BW">.&+qW`H6*^OH,o4LAeL -6:XCa%o6PiB$Hd`4)!?TBp0l=C<`"Pp/ -m\$/,\foSn:tn@>>.&+rXN:c=IYfebFG+3NbiMdW>Wh$%Tb9lJ,Z=l?0N&Gh -%hOq[K8PLeJg*I1A&jUDW1GZEB0I@H?K&bTn$qD<]C*82Gk'c!.Um,ORPB1S -X.(7fr^"6`JoT&6a&>eUf-b?fn#),:EYL950ZtXh\U7\pG(iO\9lOq# -XrZT?%Us\o*&sSo7H!XR3"?ul#`4f!Zb])99lfrHjSo#-^]+6,^OPUTa&(g] -,XjL"\_WnqMA+tu)_%J0.!LUQk"/iU(.j?`YBf'ePuh98R"@p`^A)0l[V`R2 -gPKuukg6#2UgK&_!SG#.Y9G=NE!Qjc;%hIeB-:05)Dr'p+snP*bJ%1&i@((p'?IgAp1[a)GE\GTFH2IL`l^S2o(A6m?6E<2l$L -;Y'R(,(@?ZKiF0"q<+@tT&PK,F%g#(oSZil5CQj_]O[q`Br_?MX^AjBUQ7T" -@`4'cHhKk=]U=,@T<#7>mE&],fEsJ/^mkC9$&OG7q&6HTG\$(lqac@H(VP3,j+0.uF2VMDhWa371^%^Af^3o`WX]fl=THUV8 -;J-N15UCZnZ$lNCS@!1iRd72#ac[r!.4H&c>e#3_/&6:Xk!k\T$ULAU46i)h -KH%IBj[)gcEr,Fm8/F9"JG-bqWJ=P0'Sa_a8O+\a'e<<9=gB:gLMs_TH*TS32)&7f!:AMCG&[J)1AofJl$\;g^ZSq!^Cknt;Dc -rq^D)=Ld1@AECt!d)V]q@7Ekg^RdT8Q7,V)DV_mO20\KDKS0K@4tMr8-me_+ -6ALSS;34Fo$;PSrC"&qrfWZNdMAT:eiJ/N9h/.>n:)McoCDR=g&EbB?cmQGOOBGCXu1L3)ObU2-@^S6cq05 -nc5;/mQ%?`p.hBGdaHQ]G81hMC-X9k5CORVD$e$*2eaH?-X<`[^HO@dZM4=l -hab9!R=Or%o^Ut^'0p7ELITD^<^"+ -Y!l5XU?A.u?="`9krB9aJ"#6+F6Cj4:db>5(9+:4@Z;6SRum`F3IA4r#nnWs&Oa4CR=Gqu:(/!q"AuW/k>2KVoY(R4ecMKT7N&8fNoIBR'&aTu -_%a8nlP$gt1hjg.A&aJ$sW.\U= -KOa]?e`5]MM*PY*DnCl6E32BB-7:0ra%\H4"(mN22(EhnjW*?28pfK@J`[[!MH!B8[HQK"!jf -o&R]D%nHJm"5FeBE%?@jo3S$g"8jar0l.N@RCY?!>0pJlFiV":, -&g1[(4*Jhq4I[qd=t#2]@%_3GZgHMV8%Q>l$=k!K4DE4hF[E))8cGMmOmSJk?qlY!GSG -'_ngSBXGW%WF4CmbBfH-3VQK-:?/N?Na1r%YB8a/6fP63Waj9$Pl!n -+l7j>8A\fLp2K7U;6T>E8cDG6!4WXc7"?Hb4Wk7fh,^l -Gu-2k?3!Y7cm+Skj[`5QE>n)0T9M@!8U^1;abaC7_SidkoJoUefH/9?E -m[7^Z+i%f8.H9CJ_lri9)WY8p75:$OAAe5ELDN`>?bUp7P;"RLL(age=oA'T -O+^%JPcaC(_^&i&\OMW$?*I^&jCHZ47FAA,!\WBYV+\4^X;sfD#_(f_`)u%D -ioZYkE\3NqppFke.Pj4>CKh1_F!5H&-r[]0L18NU;5m^/LH4?c?Q`Oc+.f> -e'lc1r:!(0,K&T&5#R9!O*0a;.9*5W'_;b]%M6T=E8Z8c8n+!r1V8tF-R:'I -+$4X^F::4AG[id#s#kMi'IFbKBbr[3@`!p_(!L,Yf9&7*"3cmV!eZ2bigph@OdFSrfZ>_)pa+!F0[?c:Jha3e$ -Wb9YLH1U1ceu]n+M;lM9Y4]K4f5O5c]&>)%37YT_leK$l5PsfR1M=uAFuG9k -S2bN*&eV9`ZAkZR>FRNf$5=o8Vb`p:>IHrQH3h7\I94<8qod?[6Y2su'8LFG -A#!QR2/8@+="a7_215o7IMTF;cpp)Z910VdA>cG-X&c)k)<>H!hS%ARq=Ku>CCY/RHP>@o_)Usb^#_d/YQ.l\.UBW-ac[tIp$9)0ldcnS -7'Q`g\Mef2ib4WcY?R`Dh*LdI@Sfe -'.EM&=(sh7GC/YXT4glkDrBMHTQ5bpueo<4-Jk7ZJI6i4snO\K1c? -oYqhgpuXipp4\#IjiWi=;P_CgDniu2oB(rc?XlqA[q3$bf`Gq?"eh]Y"fM.j -l)109b*/G:ZL[;4B'K.OcM)%!,a1qTu2iPK0LQMp$BO&AkR -/d?ud!>#gE>IRAG0akrjfr;0hqML2`"qA_A0-B`WJcuO6Y"?Od\T>#Gm\6$d -cGr?W:`VT=aI+hsh7@aLN>lMG0*e#)8m"T5H@!.X'&cYVJo?#.k^fU"@(uX: -lQ?`MW4E9K-9rP&dNX0:*P9Hb5c^2&>@u-K(!AdN`f,_TFEH?aGskaV*NJ8_ -acl#'21JE1amp(+#hr=aABE]AHY^aP8/M(<`0K13r&T_6?b_'DD+tcQ._2#c -o!)N7HFV*<4*ID<,IQUdD*b#^J?&%QJHP[iQI+[i9+RQQi,QW!3d'^eXBRT]Uk^G:S0iH -o&W5ZLp3V&4To+BADdNcV57$RIJ`_dl`IXBHI-,gm`DtYj)C0""m(6!\CCq3 -i@8gGr-=;eL(N$>3.:Y1m#fWIRhrr),tg)R[!1uqfWqG_nl-8MBbrl$iF5@C -NZC3iG3rI-+1#t+2O05B^=p>O^Up#Z`Q1X;6G,+9:,YQ -j,\EPgj,*AG8$(0Sj%Adh!*:cc^6EhkB;`8:HV=@30E-=bEaa7[r3X3;R5`7 -R58(f4aQaf0k3\3(kH$Q1^)QTJ3Rr+[Z,e(h7GW*].20N4aQaFrUndSGOM[u -BYh"Q0>-ekW)9=7_;s\Y7VGG1>&==R1G$g9n!4?>eAt*TB,# -jN'oV->hJDaXhe7gqRRPVJ16(:s-\.*_MYF;7q-n^;?q#m^_LoAV=2P?_iVp -:/O>E_@-qdlLW=ds5Jqi+OjX,MFZGE`PR+IS*G[1THSel[VQhbF8Yh'S)[bU -?b`1!;RQ`=.'j9,3I3Z'hRnge2s.e1kic&gZYpDE2fI9eC4`kuE;9[k[Vboc -1XGPBIYD+#'3"Ji%Yi\`6YP>h3IetT7EVpJR1Lnro[/!OO$uh@C\l>b5[ub)Ft?"Pa)^W6[TA$TKFo,u/Hu&4g%\!A:pA -@aL!>i[8[qI)UO2pANF:$>#.)cU3-+Sk@9YE8J?Zru=%>Zu=ini^MrU/8-`) -@XApFT'o-$9GJaQZY%HJK*GI;*3BO4ZtZqmKX/#1TiK-n*O,cBRP_iI7$Qa$S -X`VMIn3,=hshgs0.:8h45VC=NY[UjX;d -Y9?emeEPG[)`26$AA5J[JQ&jK!(([Qi.2,Rk0/Z'5S>9>3Urpn9UINUK*[0Z -JQ!QY!+i8H9"8C\;1=fB:]Bh+V.jiWkVW`,>B(2-I^H:gJB0&'oi -U-/,7f.U+%&2H_&*V!*/VLpfo',!ZHB.2C%':WAFd1Ff@e#!Qh,&/6=r8(^< -9P_X_W>;T\JH2aS!=Kb7^4oq*G#b_P*?Jqp*F(eL,uXuL0Me3Lc^qghiPK74 -c.e+T'5u[9ER^paY&0>IZ?V8%B_An@R4SHeQ!LW877>&l)_bY"TeV$BT).OQ -UN1Dj'dJWA&M>N929>m$:pk&g0k855HU^C4TqKE0@DfIiRd9+X6I>p`Ctc71 -JH?"J$,JCHi8"nb4S[K3Dn0Y;Bm3kM!=JUZBEe4m5-jMk,c)g-Nh:#!a -TBm94\@=MYf]Z&!2Vn9D)K=+oN#7,$L-\-'OA&e*V.""_0]12>](l\nEWS+- -!V#iM?@$>Ti5*>kmUcorgDi_D*20=L7ocQgOZu\A;Gb8,4RiQnYeL*mO7gY# -Uo(#(QYLA?BXCYS"e3#"]68&.Gu6peHGbIHBQ?`Jg.o7^/,=NGMi\'-Vrg00.2G^[*0]]nqE_tWNNF#2#%!mQAY;hhPea$M1s=X-9,IC)KVG`]rlZUg+VY*tq7"U!Q2&mO*aKaGEocZE -]Cl%L@$"bLo%eNIaktPon)BIib4Tb=1+0:`C7k[:)jL$U\D+`-TjE!&"oQ'& -2/=BKiQZRfWmQ7;7j!0OcpJ?>`^cH?#P]'$2/CbOdBtC_ju54b4oQLU6L*JC -[>2LbOlZKjOcD/jjs_l/'qISlRH#Z,5oIkN><%87U<.'bmUKa.I)$K8V'c`s -NoN(q4OAK&K^7UQ`8NZ6a&k\fBQ?B^.FQM444Sn2m+ATAqtKDBr/p063-!r5 -++C^MCTmbPM8mpW)-=XmJP_A1I+Enb$ISZrRV[V_Kk -CY&:UGkEq!P@Wt!4H#ktE+[Y#hR:C[H0(pTP_,\,hisWP5?b[YE^epm9ZqHjCn:o4n`c3rk#fQt@#VkiF-;9f"(drO> -[%^q;)`G@hN]Z[?l`MF#MELR];lC'_N0OdW:QY+nFK&$!SQDVfPZr"3"KL^n=&,J$7"O) -nB_rmGCrD03[>6d=;3@+Bi)o3iU=E@omid3JT0lY%NO-9It*oDVt7XfUu7S% -g:"$dg@1X`U):lX-\3*Albe/?aU]@N^n$=$D;?o!.@[B=/0BA`Pq,qJG3sOs -2,/%eEG7CWrTpl:@P=>D^^6a\.o861[VXpiW.-oR.SX,"Nq>"L"H&'^:fQLS -$A@tXOt;+rVi4c+otUY?'J!4CVmL?tRZJBtOH651Q,7bhKL:J&J_IK%`9EVp ->\hroV:n05puA&8L[lY%eEG;T,pD7a@Y=/Hk.C)rRhnBTnOrdp)cInqjb?jr -"`="nM_?u%d"!)++sI^&L@cYor.Vmi5F84YAaF#S^sD'ja?9$MIR=TnofuW4 -%.fs&*orQ7l9R$QD_u%J'Q0aXKi*[K9_O"2k"WBWOgnU"X']H>1Z!eB5 -56lus/g"=c6h`m1 ->dD.s:7XFC$-3+XQYNU&X]d"]qLNFXFMnN-;5O]pYB9$WYXo5Z(+N:^@)7GJ -=3q;nMfud7RsXhD5gU:R(IlOQ29(9F.C]HXGt4)?6A89e23M;-eWm`X$Y6]a -'IrhH`ujJFl:f.U,I_QJ@nm;W'Urp[PIS`ZjbTYQ0f5h$f1p2<\5@tbLj_=A -@N5tuatEt@i7b`$G2ihK.TOaBnZ4@tIt86j0@Z%_No9>).lAj?=XW!gB2N0; -=kCaSKM^Vp085dlImFbC`G[`fa?%_+qF@][H0,un8XV;;>i@+&Uja`F.A\B@ -+iT:?"&5A9qIp(t]CAb3:1fu3%5CT2Ppl4\/+!NUStNmc[VXV0qXB^b/QuNu -["C8c,:?2J4Se'%>j(>_%8PID:tl*B>)aFXGer[qX)irH'U8lsd8A1(4_TcW --^Jk[6m$qR%soug4o!rf]EK"u^a+A&Fj\R7kf`\thB:"g4NK"PN2P#fU6W/L -5IlenYmLp[/XUJXpBU+Oh9&0CRBf)l58i.;ml -r7mfGKeI>\gkHl8%7.[)lJ^jC`qsJk+.SLnW`?)8Q&iLGXbCu%h&rDIa(Z@> -QTPMA7_ZH=&[a'HldDV&[VXVn>kn5A01:1#:(pnf9@c]u8nC4 -FrOV33A)pZ':c7?l#7V\3lPn*o`"" -Fo80CW@o1/fL0R,9d01#gJQqZ!uJ4=K_];(%HnO3EE++`^\DB\NZGH:ab&&t -"ZEp,r'O>BF8hnRK4^qG/,MJ7UDW3-,SPr3]ioX^r'c[te#am)QCCt'7:"kb -@Zq.;IDPK4GTr0?U)p$YAK)=V$K@;q'g7t%)LEoDXcRhP!VaW#,t@_%+sF'T^p5)iE!d5o.IICs6m$qf -&o4/$lk/EjI%9-o@6q+rLje7!KddiG%CM7`#_T-XX_M:2`Dc&]&q,^8Z;rks -)#UQ!Ie2"U.h1+!(kX0HeS..?Y['G9R<`[BJ7qt4e5N9.\odU5h+Fmk -L$g(a,W;X#^2XiPSHT9NoLEdR`39suS$1>2+MPAEP"pB=0Gi$O."+K.+lmm\ -Qrh4Y)p5QHlXgpTG[sc/6?,EG&/_@a\$XQ'*]S^O>l;qNA8tm=BLSc>=s6Wh -C460/j0*h``GXkL_:O:KA6rX5il.T@V5!7@ -/R*u.c,AIRoFHs%Q)QgBo$u8#Xd!pJj(1j`MFXD(Gtf'ME9+H,l(*%TM.u*b -E("_!,NWqQ'>r'&'I`I_hu2u7Hg\^m(T1f5P\Mn-ORh=2:k86)f.1&28)4$6 -SbS^9;+Wc2Yts6qS+Ja*Y:#(7M -b3$MSbS,Ol/IRW(b-TJ-2?9^tkK^Se80``,cYTb>%sF([PFGrG=upmd"JmI+ -rY\*Y@oe3nPQh7)&r.,5;:VR>SK\-;i^#],*.m9#6n%AedVr?KSUaG#nIr%_ -'SPIH]H+gZ-LT/HS^?.K1aZP+?d,65Uth1fOqT6rA`=u'O]e+9!4de.[-ugp -hu/SnosAn]3%`_9Kanm;I4(EQ>84rG*mYD;2K)][&SP!BK=_\.]R5 -)TM)q<]j6)#'U?FSVU#2(qGLL,`,#,'Bodr&3M$1h07cHXhC]:,s)@%lWKaV -B$=G8H?N%(.M]16p$-\ceu:F\)6tlmP]#$XUr7?s=p1mW5k7d\$#6E^M$iDo -HXm/A#c50U5NBG=;C%Ypas>)"a[3Od;U>b/+fH)Z]..&WIP-@1HO?5"NWLU9 -agjMs=X?gf2#a'YVnNB>9lfsgmr="3pYUHbIn+="$8i5dWd:lLb(^sO*f$R@ -D#ChgRj!]7iJ(X0Bfi9Gm^6%u)#1oH^u'q;:K6QVmoQ.!;l -2C;_\=+um5q2nnRgL'sN(H=`=,!Q@2H"@b+=aCVq"W^%ne(i`B]C?i.,g4ni -)?MjKEG/=a^=:Y3JkG?4ZI$^d.m+`FL/WZ3Pl1D8J7rNWgn2BSIk=d,J/>Sr -XNS]o&/V=6kF[6qpmi'W.:KbNX!k!.![-G0(G8+n+OV#InWg\[e@Wa]-iAZ* -_&/kIX&lKb)PL$,=9O8BU)s(=V?GkUBuDGZVe8Z(d&+mal#!5PB&tVUKciH' -$Y@cqnTtWZ/4cUCG/JNrK1*gi&G8<(e_1+D.Z2WT@MXtGi>Y2CT$YWc&-d4R -A>(D$/g&5qlGDN@V'$7S."mbGP.tYRi^h:Y2/?6Qr)XSrKp(J/S._r#(S"=& -e2*OLZUSi1UnjZABK37g/ftOXkFeDjUj[auAc/>aJ`O`Y_\G,X=M,PO_V%<, -Cu;sTfT.d'T>4>]]Di/=]B9oJ3#8GPq5U -]Q@Hh*[qoa=t=lBYUOMI]G(@FaIPE%fOWj0W8o;L\Y>#cKj:rr%DaU'JQu@A -Uu](\-_71Z5dkL^po#a9AL*45ErQhI!N8^[CtuNTZf[N_51%:f+__]a?0>l] -V.9U[;N=(`Ym)YfrA)G&3fUVDm=+`/?h&:RFj$(&(VM!d;W\bib?A'F_RO4) -=-c1[n40-oBKO<.mC1uaSt>CX4:[K@0PEl>\QH+G-t+Mh@4W=;j^0B_X%Y1M -7VR:`E.<`BUVjVuTc^:e3lFo)%mdrb"/H2:)+,-8Ec/$pe.55=M5m)=oC'Be -?:t4[5Q;TMV>a>0MM@5^_hSb420a>)Dc'\6,:1j3CW#s-U?&iqKu=ee/%eU3 -&tTJHCr:Hoan"VF)T\6u+8Y0$YJ0QR_.9-R,TbD/S-'.i`%55_,P%p:i1A8A -BG:M&PWV38>uSt!J5Cbt5URX9GYhm"WdBhi13*D5X8?PUSm<@OmUTX!e0/E& -T%J]en2MnZ=3)p#779Ul;/]Xl)G[9@6A1MmOpf#HcFCCX6 -0D2!\a1[V1Ab7\nc=S^:DHbA8i!sH4I:r\N8b;8YX`&n8H]"Cc*E"A%O4&LU -p"*:N,F$k"=-@hFPUGJOVAI6W$L%dFME;J!;BG.GPKm#P_-K5qYp["LQ8!0q -15hOTKUrf$h#'rtqNEu>@)9-[b;?h0&N!"\'1cjlP4e+o:ik;@c?UG;*4hRh -P+8nd1tn;YQCi(/F_rL&:2nO@2\[eI,3/H-rquSj+Cu_?a0X!Bn6c4g?bB-b -!X0tn9gG,3Mm+bD]KT!bTqF)ME/1HG9l&@`jW+,1pk0h?QVdG4G9?S*R564J -Ab"d!Zp$t%VT,*pB1""j23*n+]2)inDr.!e(5c.Y#=Wa3)4F_/7k7lg"6>sh -62=W*Hq6Rt>pF\%(mX^Ihu4B9`>)@NlC[3k-C?L=d's.\5s[E7cF$8:*V_62 -bambCR9f%OW61(]8$ll^g7d:7XF7dtfI56ElCO(HI%F1`93- -X2u*`,_Kl/\2baTf3NUZT:j)_*'&U`9C\fs$mL`.O#TEH+;FS"5R7fF;?5ga -O&DZZ@0+mg"d%ngkK]V\H1Jb%"'2\i4D`c\N>@XJP!'Gf,!p:$B]6Ut[$-Z9 -Ukb.tPI6eRAQ*u6Up(>J&=Y2hGZgsj<'EKu3IB>XCBGi[;pYJ>XnlAA4ZMXNN.5Pd]WHBMMcZJ4>;jLleEV63ra%BLRjFASF=0E%*2n -9Pm1Tgh^7aL1qP-aY1F,dUe3%%uWiK)![6udj!\!s#lOE1q3]WD@EHWA=EqX -^`Eku/73&J>H.rDa'3%;&6(W#_d\&nSd:$2im(LY[hYB3H^5g6Rmm"uf!IUD -caY&=o7>2R6YKtkA?aRg9+.JOO[bp[agr3;AFUpV(!)GnN1dqZnjK386(E]d?rY7(m*00&l+s]*, -e]WE#l)3fC.9$JY#H_ZW@U9M&PY6OmiuS0[5Q1cRh1gS3#O!Ke?l?h3V*dmP -D^d+ane4hQW$@]a>I22>.)7SBe;WZ.h$"12Xp.Hqq5S(fZiZ<8GQSDDPs)DR -F`.,o'hX/aV>sd8e[gVh$Xk.Q0t6/_Zjr@O\Zp,^EcKp'"aU_EUE11'>e"qB -<\1-=2T?,KKEL>S!Q^lt58DB%1euB\k&s15B9Z)U/6Pg'G`ir*hF+0q!IMOH -jtmjT5r);']\refqt2V"C/+V@#SoR;mY6b7+I]aP#:-*^gU:tQ[,Hfq_P?/j -Zmdker,C%Q8#RM%VFk?XC=]K.I;nnaj&?iHBh(,0B0&u&'L.TP<82X@6F]f& -VeudSOgMWGE/2K]7^Or$BPDN"b/PG>Tf@)ms8CjTV/2AG;\lsB;5dn,P114O -E6XU!KS#qdjF0?QotP:RNTM7kOoR1N8u$h3mC.s`:S0h10;i?m0+>*_VF?@V -_.6JAq"#CWb!h=Q!Z%hnq24jB9;;FN"Y1^,NX#A_L -L+)9+L^,MifP\9\=\KMbh)\tfoi,F7fdJDXd[i"@(DUOTBn-=9d -X::cBqk?2ePRM.IaYSKfEpse+qA*dr>VI?S/e2BB;_@W$Z[]i!,u2jIIA.]B'X?[P__CIr_IX,:#)4.]3BaAH"saW:%%R6a.^)AS93 -i*!&T1tKrM)8-+!;4d:)k)=7J@]t_IJ)kL7"5&:0F(\1*E^jjA -7e2@'iPSK/(k[q\mUXY8SbhO(%3%3]K5Qa5%$MVPa[:-?,!42.+PCYnSA:Q4 -Q!:1>-Q+T7Z&Y'(Wm7(m0%YG`jR6,GR$:I??sZUu.NV_27Lfmkr#1ZZZi -WH,Q>k(iF\?bUnU7T9Yr%UfrV)4N0oY,NC;8SN5b,GBB\[ap*9pfVdW-?T1M -:Ob^G;>@hOpKeqi"Eu1)4su&^&uZ6g)>!\U39?YGTnIed1V)@r#1F'$JFL?H ->)J[NJ_/s`g_+/;bs/hGD;u1<36$WK%(hhE685!e!+YSujL&dTg9`(`Rk8>5 -Id-"[06UhOG?g7/"aL]=hu2te8A_ZDb75LOJCeGIDs"e`]carRNYE*H1br=u -gO]p,70W\a\%bAa:^EqJ`26t0JtaGO-o#:l1XMGdDB<>`ok`#0%4h`h6km0H -@W-$8'4mg^h0!1DGe%'CnVDAb&Z%EPr=#qsM)l08A4%pNq=sao4a-o*Vd"e*?r8"'e/OWt)a:8>=o -_UO1Ora01Be&Hft$FD="U9p:eP8:K'1,ku@\N^u`-Z`YtHc,t'#giZg8-@8F -#iUmj7kfN5X_g2njhShXRr.,-k.7.U$9#bjXS4sZo07a#b?oM+K*Mo"]\lVS -B.M-W\Tm1=-'#9$k.IZs-k)C:lX0Z1O.Dn?Cp#:hj+dA^6K"A'7m-QI)=f;c -1=U7J*P%r+C]G)GL\tO%[qs_A06-[MeJKW-KJ>[IYM& -V5[AB)mr)/LhM;P=Y]`!`\0uYbAnb'@[/aBLJ,-%Z@lDhDIIS6HVfpR7>!>ibS7@or+$PWaeC6TK`EYS] -CLe:+^0!UdA7!.d:g=Mnn,RiG+E+>63=TV#@W@O2HQX!#+:B8(,=LP5$9D4c/!gs -BSGNB_g1IoV+YM..S;/(C*EAb5'PsFXb3Ddb#/mjQ4J"Uo/lERZZ+m?";a!P -EPiT6V5C,)pkSDoW"CLICdR)WDq#q1c&38h?5sSlre5G45(dN[S<>8'@C5rp -Atl;Lm+@GlSrT815(,K'g'uI^ql5R?*-7;DAoo_)#T/hWV1r,dX]),mBm>Ap -<7-]kQ#Xc0)i`ElZi)L6\.q>Nf3NSLRAlTTfN,j;DGJ<5I=h"@HB)5H:2^8* -Pc;H4VrVebqA)!oU7$.F8!WBbFc3?L*/'MiBk\GAk^isGW-MU.`Hn:q[9/@t -.L8VCiNVQ]G:JeGe1,i&V(HDafnCW4ki;:2d-'d`J-Bq4EC^IKTTsdOacFG4 -fRS#hcY]=+Os\poaZURc@JN&M,&[Vl'L97.J)gi;Jifnm%$S8s1g!)D$&fHd -S%pOVkiAm+Te#U7Wpprp%';O8K43:_O<9r/> -s8)&bjXQXgd[1C;m2Th435p#Weq!0Lj^H]>-a$<6Srj0$AP.&'$ZKVgA::\R8/F/-,A:uDj?NY=,7uXR -%hFp`csJt[TFP*tJ.H\9UCh5<,4u3aEYlDa"Hj -4F-Me@p0YJ''A\-V=I#&Rh)?'asIrLC%uLbo5$UTfVF(Gb",o4W,m>i*,jn: -Cm*DJ'C4D,#BEq[$2[&i9_opc"BBmTG=1LhbP`LneG+f(H"ql-*q[U:-:AeRbta)]$@/, -?,Ifm^a:+XZI6N`=r)DY.C9D>1<_?m/M>LS3!*;j"MU8HkCu:^ef.W*j4*1$>=$q1$oni%QCPruZ!rEQaIkP,25QcV -3n_A$[bZ\^P8l\%UaSLa:HF&6G`maf(#j3mZ"M@PiCM4'TqG3V!$iF/H$tJ^ -A5puQrgng@10_D?[VXW9lF>gCj2X)rg^&`ZNfQeQr+*?OB_7%BkI1#Z)(A\k -fYjOB8/OPr%?S,:!ji$Z6?tEV=NodlG]CtBUF)rX'G2'H@!M%jYsX9V+ff:* -R_T3*G`',TF]Y"u".?#[R6jme7\OHqY$?fQjd3=mUiQdqR%H9![*g:dM5@!H -n%%U&C>drqmmU71?7Lc^Z=V57`O/qe:+dE6s35J2K/u=EXQR[qhTBt+!T%!I -TM(S%l)S?Kn9aot?*n8Ws&JR6D)G/.2+El6Q]oP;LjX1beTUlC`fY/mZq8G< -C-7,C=1<6gY3A(H-@F$ub:,!AL,%F,@+MNWc47"MY)=#F7WWtOh01=N"pLkP -FQsc2k7F;Lndc_fV6W__8FnTQC'K4ZLb#BN[OcPVNj_%1S7/_!fgT%6)8H8X0j3a8c(`s.m*^Md/>: -S'1Pi[GoT@numN8.rIOro&NJO4cC=X"/mFmYKT*u<\m6V+-#!=)1fU^8RpdC -5ROM*9u9tX4"6.-+](TU0j$e>:K;0P?;?Sa-jJoMTYX5'5_D)I\&DE>,L+8' -rqqoj/%%<>,WioGm -#@X9Ar.V"9ltI%?)<*rM-#tn4>sa?Z6`t)L\RYg]W!F6*um -,ndbj]?WFOjBGlC]C3HQ=+M0=Vn^S^HrtkT2HiWPj3qG0GZZBHUeu(I]f>uF -Dnd;WIQWn;=7Hd],R]ttfWfDS6bQp]*85ptL/!gKj<'MZeL7t3QACABP)R6q'3^1< -D*Bf)e>Z@i<)X$>2*uBF#cR-TnI(o#XtNX:rEZD -6#'(OrfXI)1CI:oW1ieu]]dn?6-0G/8thQ`fc?%Yl"b9od,Q -@?Q"`76$!&N97s56E\\cBQujRUqN<$Zn=ZK6(k$.TD@nBI'(3hQ6eb,GQM19P%6bj,gKBkpQe"Ojn#79*6M@WP>` -Rl@'BM7bYr_$=:ZqXj#iSQk9_eXj`A#%-&4$EY1@Lh_cChq^+!"N0NZnH9U$ -`>;n6-tTl[cGo&%+I:F#68\*js+9gUKJQ5]+qaD(q/@";!KBJ4i.6d&PV6=R -'ELHV\[OG!dSXdtQjQ9I_%_*6NDJCZ$q*:(n/u04S2kZ#8@-`$3"3/%mOMkA -l"A;3W>46=$"r_$-efQhJYSfa"_cM+o2tY*UIUC)!epZt]u"VBfR96Di7fhf -[Nm.F+eRsT>LuMhI^t9.Vi&?X)-7\(Q+B\,#b:GfqP]3>3?Q=24]=6WOkR73 -^JljMPq>)^NZP`/NU]&C9?]He#SESm7'e=G*2+t921<:>kdrXYLOuhCo$sOJ -2hs1D]O5!\Mt9r\misd#J[IDr"52.HP>nhG8XUM!kAUm2LYgs%1kET_$H\,G -Z$Z65bGkrM#[N3iM`u\m[&bTN;c2>9lP+t9L)8SOXQ=6-O4>2@X]o;omu&-8 -7.EoL#?M2]GnHXH=[J@(7rr=8`P:qOHbR,6gpepWNlu>3VsD'hKn$c%6f"[3 -K#@rKEC5XPEIn6^jD[sOhR%mC[e9,q=Bs/YhP6(S7jh]!0JV9+K.C-@s#MW. -;j1T)2bA#)7sC^/X4ZpV>K)>hr93+FQ$+lU"r*L%Rd?ctPi;X/>hs,)QI\u\ -*2(^b$lGZ)'sqjMpha;N^[+$[Z*Z&MTNZ#0g/Kq>H7?MZG%Z"GUO1B9PJ46U -7mee+Z&'-TcCgl76NAmU_iXs?pM)MrBU;d8ARB-T(C,mVNQIa:#bsNuOk08eg`bE2NXcGd+kUO+m$`+>< -%5EH5*$J:8$5HaQCl)5`nYi>c;#kS:-XV2hWD]Ps%NK08f)S49]mB@W%UA%V7RTKcY10CIUN#2/\9t>XXnq.&(_:RY -@K$&q]mKM)gdW6h4YR$:4m8cg+9DR&FW]Wd:B0KAs(V96GOhF'ft"&\%@ZU& -Xo!5F\4o7kUi!s[!6p4Fku+m,hnD@,!c?Yu=h&FE"rd]TkcL.e;_p7h>XpSP -*BJ;hEo/*3NkE2"X9!2`*'Sapo&Rp;Rl@g1!o3U=3bd0HR4Y+OnI!D$=Ms6q0iiPUF<-o:'FJ'\U)I!g=mXBAc8C,Kqd]YLhn%e)q'04(09 -R&FM:l_M$^@?f&Qe>SKkGAd%Npt2JEd>I#G -AF1>7;.jB+Rr[5WA>;VR;D18LZlq[8cpF+bo9cCb@ -if!NtgY39'X_4K<=d1ISh7@a3>e+V#XGQ1+FQcPsb8]AOi9].'++3R/(-&^e -:3A0NKtGdGH1R1F9;6QcUk5a@5oC!R0%&s'3jCQ&+B3`9HsBo]-E]u]i78.% -'/S^p]@Rt<2YF\+n(s<[="[UZ+CD6X^HNJ=.A2Kq2l6:cd#gClJeMr6(5h"B -Lk:RRN*tDKD6J<5RS35DCh45J;FK*tb*D;M^V=\"DCj9%^juTL8?"_R)I_rq -gQHPfbEa_ZW6hTK^8fSAOY1[)Rt6hTDh+;[/U;*N8.5FL_HGTj -,@A04/R#NK5(.a=1H3OI2c%&dBK9Wi+1:rSX&c?CkK[A8>.(%kLP@=R=>oSE -%/c2!,=ZeBg/ZmZ=Qj;K#sPe8h)^&br2AV/$Q!,`(RS;,F;QoP(c>erG*p$W -+.n#Ykq#uC1i:rgb*4=mmnXt^G!ri4r5i0$!..+9D2U5c!F)[VWqA7lJ,Ji2 -7>l36G+&\uSt8]j6(C]:_UdTH[;4C>Sio/9D!p)hQX)-d5SV"(X*sm=C&FPl -le-.f>4n,<=K%)l/!_eng3/p@<69&-*'8>F9:%:!16o9cm^(5'X.:caQS4D2 -V](mT?QLVi(r<4PWt5^,$u>0L)m][k'7ms_dA)?I#Gt:[PERS!ZHSn(En%\C -b"02Bm^jDsD@@4VN'O][?4K97PI@ta=7?E8jiU"7TN.`h_%;cmP&0;.gB5<[ -"Y`gX$PogI[Hc,46[.qO($Y8npncnY"HbRQeD7K&4L,unEF\"fiXhKq`5H'1 -OT@n(G=U14@WGs,*M7W>)>q0;N>bN]3&,e:oDha=E8u!4lrcY#/O6>pB=:a+ -3/%T>[r1$W\T?g+X^&F-qUXG[8rOTAJlkSk8\:$'N_ -,7qA^'cNhdLCYIrs/<7qCpK09(.hA;,taEceZ7DfY@F^lNi#aV3D(W/q3,;j5cW92?o_]Ua5Z;X`"1(mRh5,qO2X-aSKNXgYd#g6- -p?hSg+Bl$beLI@]4)!AVBnb1`'^%oC(4G8+8,DoE[r^`X8=0'O!O3n>>U_iU -)=+k`S=k]/kKf25>.8BT0^'oSnC_HH`Q-$B0H"\"APNdIr&p;3Y[PF#&;XIN -AM:c3djA1,Z')C(mZt$nW=g79pu$d4&bt"pi5(%cooY-p6BJf'^aZD4a>"h] -+UA2ZoB+:BJ84,;[0,\*MWN$JYB&aI,=ddL[r5WLb8]AOi7tka0>IFj'"g+@ -9bp>Z$\,?\X]egT4MV+.('N*Z%+(eQZY#LT[tM<3R=ch\RUjO'V.%8re##hI -k$pkDIL9rV.pXX*,YhKQXhiFsb*4>WY[FqXh3*qa@Q_VBTH8FQm&<.s73_$D -[S>q.!K\%mAF?:EQYoB,Go[Hi\b.fr-R,?VU'0qO4- -,pT7*TN)9=$0J3j[;&'`%,&=@GGNE/X'j\jD43mA7:\b1TT[M7(JBu@JM)97 -Xg_+p5t3%fOH>RnjHE@`$mlqQO`aS8-KN:4#LZ><4?;&I`@uf@r2*2XWno;G -f/s1"Em0k'hkQ5r3cO#m4aZlj]"A&mEQIJ3gUHVb:HT42*tN?B"99&IbYs,u -%3SW^G4"G;SSNk`$e;b,j)!DSTrc7o*NQT7K1_&J -S3(qt9:%7f3j"IWKiZc[,2#4\W`5rqW)00h??p,Xo&[^S"%3+frRKLBgLN!) -4oY6:EH-!FD8qMP_)/)-!La\>e>Q4h,EuuAA>)V\.TNcbakj^XJ,AWB_31_5 -KaS^.FmDMVM*'^ZC/!eg7(@VJE\/,M'I-.@9#<]I:ddl!P[LAg5_0!%.THi, -6Z@&;kYZ_8ji2][^E?M/Z=Sq//(UIcO4C:4#Dsqp+b)[!+e_Ab)FR0ildp(- -YT_5*kb56dIE\9G45Wf%1eARA+:nMl2f=kgOZbJ+DVd.m_Q9(S=0>oFITs:! -NHBFJr3EQK:jr67Khqp(cprcK]TEHHMEp,!qsT%&\`[SNSfIq8!qhkHQq704 -nR+4DFZgH@[Q]3?r3^=l*]7\38kM^OM]CYei;EEBGke[]eWPUf%6PaP10@(< -`JPCOjN,`F9:)JJQNe<6_oRrbW@3Zch7us4$;OH:>b='"C+N/"NY0?!*BJ9J -imar+LG6jsPkfd,hr;V5UM8V7qe^IM#6*IJYP*d2Pi?aS7p+oD,IRnFFm@H_ ->8FW/*'AK8aH7]op?ZMFY+9,l3Q?D2s1gCM>U4VF:nU)JmbLsH=qs_` -M@nd+i6aiha$YrdB>G;[JnUZ)5AVcX2bU6G!mcVZNjWihJCoZ)_+"ab%SG&S -$JSh-5$e]$oG;^)N6D4W*P88T_&5qhi!!*qb48[f_SB6lMA=SsW*g54fKnE/ -I4J9qJ9+Vl?G3pmlZ0r_;a0SLo1$DYWQ'4sT'I=nOMp-&u8NWFlC@hq2_%W\/Lk7U3o! -7:j`4PtF\?8-gs`0`n'X-r[Zsjm!n%G]9&s:-g;=JqBInfAI"4Ngkcr_+QE" ->QM8F9dZ1o(akq)nT!#(RZWE$H"EE_l$@78ML"bQD3l+^>CGZe+6D4e4l(#5 -\(DrI)9YiKEH-!rGM]uX_3/RgK%6o'=8#IuC=R(LWejP9*W\&mn-J$P\(=<= -6-N5NE4?Q0Qb(4?:>_g[s9OYrj-i"a1mUc07AZ+!9q]DB+82o)fusgpqKMmbBfO.4Np=4[q,RqXcOSb2DI#W@Ar1HgEJSAHo3$ -#=VahQ)3'L]7<2"&+'M0$t@f1JhR(#*u1BWGn46^AOaVPEnP)4 -\HB]#S`4\7kU+k"f3FApK:Rfs7Oqu7jX]*l.pNdR`3>;SfejF9Y*eSa+06FLqjHR: -)5cnZa\@Nb7b#&dTX*Ip*IAeAVF>/q,9PTuVc"$#oB/VH;8Y[h,'M>@CY+=_ -H:VA7b`dE==X]YkOfIdL:732;ek0mAjdu@*-?J<1LU'E6V_S0KR)s6ujE%0$gaL:26ZDFTBP%3d$g&5TCo*95[BmT`q<%-C -7JG\h5NgLm9fmL_<2?!E^s0Zg6\hm9\0ZgSO*Z=QYXuPN8$U1;Q0aa)VNC90 -H@VNt,;(O='#dP"9.%1dIQ,+0)F$$ajk;A[9lVaX3(GODYrngN6bYh3jgb1D -[m*V8ekbJc8hB"`.&`XrF1,l9DS^o._d[4=N,gQ,KZe -%[9VQOg8@%(`-F=(![(DO>jAhkNVdHQhkab^4'D?a(p.gCjhXTc(+SDQGX6Z -Hi&Q+\i4`[(XG),7CWYFZ\HJ,ISd=\aT;VQ]_^ooX&c?[F?9`;8_CCae&sDZ -$mSB2/mYhtd:+/SH[ocU0?&Pb8qg)&KSMmER"'`lR,Oe2~> -EI diff --git a/bin/reportlab/docs/images/replogo.gif b/bin/reportlab/docs/images/replogo.gif deleted file mode 100644 index e91e1ca03e0f4ed39857fc294d72b0ca9657db54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16583 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 e4D5Kkr@E@II;*$3tA}$y002A3mIKiM diff --git a/bin/reportlab/docs/reference/build.bat b/bin/reportlab/docs/reference/build.bat deleted file mode 100755 index 483af7b9b6c..00000000000 --- a/bin/reportlab/docs/reference/build.bat +++ /dev/null @@ -1,3 +0,0 @@ -del reference.pdf -python ..\tools\yaml2pdf.py reference.yml -start reference.pdf diff --git a/bin/reportlab/docs/reference/genreference.py b/bin/reportlab/docs/reference/genreference.py deleted file mode 100644 index c2b6afd7598..00000000000 --- a/bin/reportlab/docs/reference/genreference.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/reference/genreference.py -__version__=''' $Id: genreference.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__ = """ -This module contains the script for building the reference. -""" -def run(verbose=None, outDir=None): - import os, sys, shutil - from reportlab.tools.docco import yaml2pdf - from reportlab.lib.utils import _RL_DIR - if verbose is None: verbose=('-s' not in sys.argv) - yaml2pdf.run('reference.yml','reference.pdf') - if verbose: print 'Saved reference.pdf' - docdir = os.path.join(_RL_DIR,'docs') - if outDir: docDir = outDir - destfn = docdir + os.sep + 'reference.pdf' - shutil.copyfile('reference.pdf', destfn) - if verbose: print 'copied to %s' % destfn - -def makeSuite(): - "standard test harness support - run self as separate process" - from reportlab.test.utils import ScriptThatMakesFileTest - return ScriptThatMakesFileTest('../docs/reference', 'genreference.py', 'reference.pdf') - - -if __name__=='__main__': - run() diff --git a/bin/reportlab/docs/reference/reference.yml b/bin/reportlab/docs/reference/reference.yml deleted file mode 100644 index 65e4091af5c..00000000000 --- a/bin/reportlab/docs/reference/reference.yml +++ /dev/null @@ -1,217 +0,0 @@ -.t ReportLab API Reference - -.nextPageTemplate Normal - -.h1 Introduction - -This is the API reference for the ReportLab library. All public -classes, functions and methods are documented here. - -Most of the reference text is built automatically from the -documentation strings in each class, method and function. -That's why it uses preformatted text and doesn't look very -pretty. - -Please note the following points: -.bu ()Items with one leading underscore are considered private -to the modules they are defined in; they are not documented -here and we make no commitment to their maintenance. -.bu ()Items ending in a digit (usually zero) are experimental; -they are released to allow widespread testing, but are -guaranteed to be broken in future (if only by dropping the -zero). By all means play with these and give feedback, but -do not use them in production scripts. - -.h2 Package Architecture - -The reportlab package is broken into a number of subpackages. -These are as follows: - -.df reportlab.pdfgen -- this is the programming -interface to the PDF file format. The Canvas (and its co-workers, -TextObject and PathObject) provide everything you need to -create PDF output working at a low level - individual shapes -and lines of text. Internally, it constructs blocks of -page marking operators which match your drawing commands, -and hand them over to the pdfbase -package for drawing. - -.df reportlab.pdfbase -- this is not part of the -public interface. It contains code to handle the 'outer -structure' of PDF files, and utilities to handle text metrics -and compressed streams. - -.df reportlab.platypus -- PLATYPUS stands for -"Page Layout and Typography Using Scripts". It provides a -higher level of abstraction dealing with paragraphs, frames -on the page, and document templates. This is used for multi- -page documents such as this reference. - -.df reportlab.lib -- this contains code of -interest to application developers which cuts across -both of our libraries, such as standard colors, units, and -page sizes. It will also contain more drawable and flowable -objects in future. - -There is also a demos directory containing various demonstrations, -and a docs directory. These can be accessed with package -notation but should not be thought of as packages. - -Each package is documented in turn. - -.pageBreak -.h1 reportlab.pdfgen subpackage - -This package contains three modules, canvas.py, textobject.py -and pathobject.py, which define three classes of corresponding -names. The only class users should construct directly is -the Canvas, defined in reportlab.pdfgen.canvas; it provides -methods to obtain PathObjects and TextObjects. - - -.getClassDoc reportlab.pdfgen.canvas Canvas -.pageBreak -The method Canvas.beginPath allows users to construct -a PDFPathObject, which is defined in reportlab/pdfgen/pathobject.py. - -.getClassDoc reportlab.pdfgen.pathobject PDFPathObject -.pageBreak -The method Canvas.beginText allows users to construct -a PDFTextObject, which is defined in reportlab/pdfgen/textobject.py. - -.getClassDoc reportlab.pdfgen.textobject PDFTextObject - -.pageBreak -.h1 reportlab.platypus subpackage - -The platypus package defines our high-level page layout API. -The division into modules is far from final and has been -based more on balancing the module lengths than on any -particular programming interface. The __init__ module -imports the key classes into the top level of the package. - -.h2 Overall Structure - -Abstractly Platypus currently can be thought of has having four -levels: documents, pages, frames and flowables (things which can fit into frames in some way). -In practice there is a fifth level, the canvas, so that if you want -you can do anything that pdfgen's canvas allows. - -.h2 Document Templates -.h3 BaseDocTemplate - -The basic document template class; it provides for initialisation and -rendering of documents. A whole bunch of methods -handle_XXX handle document -rendering events. These event routines all contain some -significant semantics so while these may be overridden that -may require some detailed knowledge. Some other methods are -completely virtual and are designed to be overridden. -.h3 BaseDocTemplate -.getClassDoc reportlab.platypus.doctemplate BaseDocTemplate - -.pageBreak -A simple document processor can be made using derived class, -SimpleDocTemplate. - -.pageBreak -.h3 SimpleDocTemplate -.getClassDoc reportlab.platypus.doctemplate SimpleDocTemplate - -.pageBreak -.h2 Flowables -.getClassDoc reportlab.platypus.paragraph Paragraph -.getClassDoc reportlab.platypus.flowables Flowable -.getClassDoc reportlab.platypus.flowables XBox -.getClassDoc reportlab.platypus.flowables Preformatted -.getClassDoc reportlab.platypus.flowables Image -.getClassDoc reportlab.platypus.flowables Spacer -.getClassDoc reportlab.platypus.flowables PageBreak -.getClassDoc reportlab.platypus.flowables CondPageBreak -.getClassDoc reportlab.platypus.flowables KeepTogether -.getClassDoc reportlab.platypus.flowables Macro -.getClassDoc reportlab.platypus.xpreformatted XPreformatted -.getClassDoc reportlab.platypus.xpreformatted PythonPreformatted - -.pageBreak -.h1 reportlab.lib subpackage - -This package contains a number of modules which either add utility -to pdfgen and platypus, or which are of general use in graphics -applications. - -.h2 reportlab.lib.colors module -.getModuleDoc reportlab.lib.colors - -.h2 reportlab.lib.corp module -.getModuleDoc reportlab.lib.corp - -.h2 reportlab.lib.enums module -.getModuleDoc reportlab.lib.enums - -.h2 reportlab.lib.fonts module -.getModuleDoc reportlab.lib.fonts - -.h2 reportlab.lib.pagesizes module -.getModuleDoc reportlab.lib.pagesizes - -.h2 reportlab.lib.sequencer module -.getModuleDoc reportlab.lib.sequencer - - - - -.pageBreak -.h1 Appendix A - CVS Revision History -.beginPre Code -$Log: reference.yml,v $ -Revision 1.1 2001/10/05 12:33:33 rgbecker -Moved from original project docs, history lost - -Revision 1.13 2001/08/30 10:32:38 dinu_gherman -Added missing flowables. - -Revision 1.12 2001/07/11 09:21:27 rgbecker -Typo fix from Jerome Alet - -Revision 1.11 2000/07/10 23:56:09 andy_robinson -Paragraphs chapter pretty much complete. Fancy cover. - -Revision 1.10 2000/07/03 15:39:51 rgbecker -Documentation fixes - -Revision 1.9 2000/06/28 14:52:43 rgbecker -Documentation changes - -Revision 1.8 2000/06/19 23:52:31 andy_robinson -rltemplate now simple, based on UserDocTemplate - -Revision 1.7 2000/06/17 07:46:45 andy_robinson -Small text changes - -Revision 1.6 2000/06/14 21:22:52 andy_robinson -Added docs for library - -Revision 1.5 2000/06/12 11:26:34 andy_robinson -Numbered list added - -Revision 1.4 2000/06/12 11:13:09 andy_robinson -Added sequencer tags to paragraph parser - -Revision 1.3 2000/06/09 01:44:24 aaron_watters -added automatic generation for pathobject and textobject modules. - -Revision 1.2 2000/06/07 13:39:22 andy_robinson -Added some text to the first page of reference, and a build batch file - -Revision 1.1.1.1 2000/06/05 16:39:04 andy_robinson -initial import - -.endPre - - - diff --git a/bin/reportlab/docs/userguide/app_demos.py b/bin/reportlab/docs/userguide/app_demos.py deleted file mode 100644 index 6c1c156477c..00000000000 --- a/bin/reportlab/docs/userguide/app_demos.py +++ /dev/null @@ -1,113 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/app_demos.py -from reportlab.tools.docco.rl_doc_utils import * - -Appendix1("ReportLab Demos") -disc("""In the subdirectories of $reportlab/demos$ there are a number of working examples showing -almost all aspects of reportlab in use.""") - -heading2("""Odyssey""") -disc(""" -The three scripts odyssey.py, dodyssey.py and fodyssey.py all take the file odyssey.txt -and produce PDF documents. The included odyssey.txt is short; a longer and more testing version -can be found at ftp://ftp.reportlab.com/odyssey.full.zip. -""") -eg(""" -Windows -cd reportlab\\demos\\odyssey -python odyssey.py -start odyssey.pdf - -Linux -cd reportlab/demos/odyssey -python odyssey.py -acrord odyssey.pdf -""") -disc("""Simple formatting is shown by the odyssey.py script. It runs quite fast, -but all it does is gather the text and force it onto the canvas pages. It does no paragraph -manipulation at all so you get to see the XML < & > tags. -""") -disc("""The scripts fodyssey.py and dodyssey.py handle paragraph formatting so you get -to see colour changes etc. Both scripts -use the document template class and the dodyssey.py script shows the ability to do dual column -layout and uses multiple page templates. -""") - -heading2("""Standard Fonts and Colors""") -disc("""In $reportlab/demos/stdfonts$ the script stdfonts.py can be used to illustrate -ReportLab's standard fonts. Run the script using""") -eg(""" -cd reportlab\\demos\\stdfonts -python stdfonts.py -""") -disc(""" -to produce two PDF documents, StandardFonts_MacRoman.pdf & -StandardFonts_WinAnsi.pdf which show the two most common built in -font encodings. -""") -disc("""The colortest.py script in $reportlab/demos/colors$ demonstrates the different ways in which -reportlab can set up and use colors.""") -disc("""Try running the script and viewing the output document, colortest.pdf. This shows -different color spaces and a large selection of the colors which are named -in the $reportlab.lib.colors$ module. -""") -heading2("""Py2pdf""") -disc("""Dinu Gherman (<gherman@europemail.com>) contributed this useful script -which uses reportlab to produce nicely colorized PDF documents from Python -scripts including bookmarks for classes, methods and functions. -To get a nice version of the main script try""") -eg(""" -cd reportlab/demos/py2pdf -python py2pdf.py py2pdf.py -acrord py2pdf.pdf -""") -disc("""i.e. we used py2pdf to produce a nice version of py2pdf.py in -the document with the same rootname and a .pdf extension. -""") -disc(""" -The py2pdf.py script has many options which are beyond the scope of this -simple introduction; consult the comments at the start of the script. -""") -heading2("Gadflypaper") -disc(""" -The Python script, gfe.py, in $reportlab/demos/gadflypaper$ uses an inline style of -document preparation. The script almost entirely produced by Aaron Watters produces a document -describing Aaron's $gadfly$ in memory database for Python. To generate the document use -""") -eg(""" -cd reportlab\\gadflypaper -python gfe.py -start gfe.pdf -""") -disc(""" -everything in the PDF document was produced by the script which is why this is an inline style -of document production. So, to produce a header followed by some text the script uses functions -$header$ and $p$ which take some text and append to a global story list. -""") -eg(''' -header("Conclusion") - -p("""The revamped query engine design in Gadfly 2 supports -.......... -and integration.""") -''') -heading2("""Pythonpoint""") -disc("""Andy Robinson has refined the pythonpoint.py script (in $reportlab\\demos\\pythonpoint$) -until it is a really useful script. It takes an input file containing an XML markup -and uses an xmllib style parser to map the tags into PDF slides. When run in its own directory -pythonpoint.py takes as a default input the file pythonpoint.xml and produces pythonpoint.pdf -which is documentation for Pythonpoint! You can also see it in action with an older paper -""") -eg(""" -cd reportlab\\demos\\pythonpoint -python pythonpoint.py monterey.xml -start monterey.pdf -""") -disc(""" -Not only is pythonpoint self documenting, but it also demonstrates reportlab and PDF. It uses -many features of reportlab (document templates, tables etc). -Exotic features of PDF such as fadeins and bookmarks are also shown to good effect. The use of -an XML document can be contrasted with the inline style of the gadflypaper demo; the -content is completely separate from the formatting -""") \ No newline at end of file diff --git a/bin/reportlab/docs/userguide/ch1_intro.py b/bin/reportlab/docs/userguide/ch1_intro.py deleted file mode 100644 index 1aa99314d99..00000000000 --- a/bin/reportlab/docs/userguide/ch1_intro.py +++ /dev/null @@ -1,693 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch1_intro.py -from reportlab.tools.docco.rl_doc_utils import * -import reportlab - -title("ReportLab PDF Library") -title("User Guide") -centred('ReportLab Version ' + reportlab.Version) - -nextTemplate("Normal") - -######################################################################## -# -# Chapter 1 -# -######################################################################## - - -heading1("Introduction") - - -heading2("About this document") -disc("""This document is an introduction to the ReportLab PDF library. -Some previous programming experience -is presumed and familiarity with the Python Programming language is -recommended. If you are new to Python, we tell you in the next section -where to go for orientation. -""") - -disc(""" -This manual does not cover 100% of the features, but should explain all -the main concepts and help you get started, and point you at other -learning resources. -After working your way through this, you should be ready to begin -writing programs to produce sophisticated reports. -""") - -disc("""In this chapter, we will cover the groundwork:""") -bullet("What is ReportLab all about, and why should I use it?") -bullet("What is Python?") -bullet("How do I get everything set up and running?") - -todo(""" -We need your help to make sure this manual is complete and helpful. -Please send any feedback to our user mailing list, -which is signposted from www.reportlab.org. -""") - -heading2("What is the ReportLab PDF Library?") -disc("""This is a software library that lets you directly -create documents in Adobe's Portable Document Format (PDF) using -the Python programming language. It also creates charts and data graphics -in various bitmap and vector formats as well as PDF.""") - -disc("""PDF is the global standard for electronic documents. It -supports high-quality printing yet is totally portable across -platforms, thanks to the freely available Acrobat Reader. Any -application which previously generated hard copy reports or driving a printer -can benefit from making PDF documents instead; these can be archived, -emailed, placed on the web, or printed out the old-fashioned way. -However, the PDF file format is a complex -indexed binary format which is impossible to type directly. -The PDF format specification is more than 600 pages long and -PDF files must provide precise byte offsets -- a single extra -character placed anywhere in a valid PDF document can render it -invalid. This makes it harder to generate than HTML.""") - -disc("""Most of the world's PDF documents have been produced -by Adobe's Acrobat tools, or rivals such as JAWS PDF Creator, which act -as 'print drivers'. Anyone wanting to automate PDF production would -typically use a product like Quark, Word or Framemaker running in a loop -with macros or plugins, connected to Acrobat. Pipelines of several -languages and products can be slow and somewhat unwieldy. -""") - - -disc("""The ReportLab library directly creates PDF based on -your graphics commands. There are no intervening steps. Your applications -can generate reports extremely fast - sometimes orders -of magnitude faster than traditional report-writing -tools. This approach is shared by several other libraries - PDFlib for C, -iText for Java, iTextSharp for .NET and others. However, The ReportLab library -differs in that it can work at much higher levels, with a full featured engine -for laying out documents complete with tables and charts. """) - - -disc("""In addition, because you are writing a program -in a powerful general purpose language, there are no -restrictions at all on where you get your data from, -how you transform it, and the kind of output -you can create. And you can reuse code across -whole families of reports.""") - -disc("""The ReportLab library is expected to be useful -in at least the following contexts:""") -bullet("Dynamic PDF generation on the web") -bullet("High-volume corporate reporting and database publishing") -bullet("""An embeddable print engine for other applications, including -a 'report language' so that users can customize their own reports. -This is particularly relevant to cross-platform apps which cannot -rely on a consistent printing or previewing API on each operating -system.""") -bullet("""A 'build system' for complex documents with charts, tables -and text such as management accounts, statistical reports and -scientific papers """) -bullet("""Going from XML to PDF in one step!""") - - - - -heading2("What is Python?") -disc(""" -Python is an interpreted, interactive, object-oriented programming language. It is often compared to Tcl, Perl, -Scheme or Java. -""") - -disc(""" -Python combines remarkable power with very clear syntax. It has modules, classes, exceptions, very high level -dynamic data types, and dynamic typing. There are interfaces to many system calls and libraries, as well as to -various windowing systems (X11, Motif, Tk, Mac, MFC). New built-in modules are easily written in C or C++. -Python is also usable as an extension language for applications that need a programmable interface. -""") - - -disc(""" -Python is as old as Java and has been growing steadily in popularity for 13 years; since our -library first came out it has entered the mainstream. Many ReportLab library users are -already Python devotees, but if you are not, we feel that the language is an excellent -choice for document-generation apps because of its expressiveness and ability to get -data from anywhere. -""") - -disc(""" -Python is copyrighted but freely usable and distributable, even for commercial use. -""") - -heading2("Acknowledgements") -disc("""Many people have contributed to ReportLab. We would like to thank -in particular (in approximately chronological order) Chris Lee, Magnus Lie Hetland, -Robert Kern, Jeff Bauer (who contributed normalDate.py); Jerome Alet (numerous patches -and the rlzope demo), Andre Reitz, Max M, Albertas Agejevas, T Blatter, Ron Peleg, -Gary Poster, Steve Halasz, Andrew Mercer, Paul McNett, Chad Miller, Tim Roberts, -Jorge Godoy and Benn B.""") - -disc("""Special thanks go to Just van Rossum for his valuable assistance with -font technicalities and the LettErrorRobot-Chrome type 1 font.""") - -disc("""Marius Gedminas deserves a big hand for contributing the work on TrueType fonts and we -are glad to include these in the toolkit. Finally we thank Bigelow & Holmes Inc ($design@bigelowandholmes.com$) -for Luxi Serif Regular and Ray Larabie ($http://www.larabiefonts.com$) for the Rina TrueType font.""") - -heading2("Installation and Setup") - -disc(""" -Below we provide an abbreviated setup procedure for Python experts and a more -verbose procedure for people who are new to Python. -""") - -heading3("Installation for experts") -disc("""First of all, we'll give you the high-speed version for experienced -Python developers:""") -list("""Install Python 2.3 or later (2.4 recommended). ReportLab 2.x uses - Python 2.3 features and will use 2.4 going forwards. We also maintain - a 1.x branch which works back to Python 2.1. - """) -list("""If you want to produce compressed PDF files (recommended), -check that zlib is installed.""") -list("""If you want to work with bitmap images, install and -test the Python Imaging Library""") -list("""Unpack the reportlab package (reportlab.zip -or reportlab.tgz) into a directory on your path. (You can also use ^python setup.py install^ if you wish)""") -list("""Unpack the rl_addons package and build the C extensions with distutils; or grab the -corresponding .pyd files from our download page. """) - - -list("""$cd$ to ^reportlab/test^ and execute $runAll.py$. -This will create many PDF files. """) -list("""You may also want to download and run the ^rl_check.py^ on our site, which -health-checks an installation and reports on any missing options. """) -disc(" ") -disc("""If you have any problems, check the 'Detailed Instructions' section below.""") - -heading3("A note on available versions") -disc("""The $reportlab$ library can be found at $ftp.reportlab.com$ in -the top-level directory or at http://www.reportlab.com/ftp/. -Each successive version is stored in both zip -and tgz format, but the contents are identical apart from line endings. -Versions are numbered: $ReportLab_1_00.zip$, $ReportLab_1_01.zip$ and so on. The -latest stable version is also available as just $reportlab.zip$ (or -$reportlab.tgz$), which is actually a symbolic link to the latest -numbered version. Finally, daily snapshots off the trunk are available as -$current.zip$ (or $current.tgz$). -""") - - -heading3("Instructions for novices: Windows") - - - -disc("""This section assumes you -don't know much about Python. We cover all of the steps for three -common platforms, including how to verify that each one is complete. -While this may seem like a long list, everything takes 5 minutes if -you have the binaries at hand.""") - - -restartList() - -list("""Get and install Python from $http://www.python.org/.$ -Reportlab 2.x works with Python 2.3 upwards but we strongly recommend to use -the latest stable version of Python (2.4.3 at the time of writing). -Follow the links to 'Download' and get the latest -official version. This will install itself into $C:\Python24$ -After installing, you should be able to run the -'Python (command line)' option from the Start Menu.""") - -list("""If on Windows, we strongly recommend installing the Python Windows -Extensions, which let you use access all the Windows data sources, and provide -a very nice IDE. This can be found at ^http://sourceforge.net/projects/pywin32/^. -Once this is installed, you can start -Pythonwin from the Start Menu and get a GUI application.""") - -list("""The next step is optional and only necessary if you want to -include images in your reports; it can also be carried out later. However -we always recommend a full installation if time permits.""") - -list("""Install the Python Imaging Library ($PIL$) from $http://www.pythonware.com/products/pil/$. -""") - - -list("""Now you are ready to install reportlab itself. Unzip the archive straight into -your Python directory; it creates a subdirectory named -$reportlab$. You should now be able to go to a Python -command line interpreter and type $import reportlab$ without getting -an error message.""") - -list("""Download the zip file of precompiled DLLs for your Python version from -the bottom of the ^http://www.reportlab.org/downloads.html^ downloads page, and unzip -them into ^C:\Python24\lib\site-packages^ (or its equivalent for other Python versions""") - -list("""Open up a $MS-DOS$ command prompt and CD to -"$..\\reportlab\\test$". Enter "$runAll.py$". You should see lots of dots -and no error messages. This will also create many PDF files and generate -the manuals in ^reportlab/docs^ (including this one). """) - -list(""" -Finally, we recommend you download and run the script ^rl_check.py^ from -^^http://www.reportlab.org/ftp/^. This will health-check all the above -steps and warn you if anything is missing or mismatched.""") - -heading3("Instructions for Python novices: Unix") - -restartList() -list("""On a large number of Unix and Linux distributions, Python is already installed, -or is avaialable as a standard package you can install with the relevant package manager.""") - -list("""If you want to compile from -source download the latest -sources from http://www.python.org (currently the latest source is -in http://www.python.org/ftp/python/2.4.3/Python-2.4.3.tgz). If you wish to use -binaries -get the latest RPM or DEB or whatever package and install (or get your -super user (system administrator) to do the work).""") - -list("""If you are building Python yourself, unpack the sources into a -temporary directory using a tar command e.g. $tar xzvf Python-2.4.3.tgz$; -this will create a subdirectory called Python-2.4.3 (or whatever). cd -into this directory. Then read the file $README$! It contains the -latest information on how to install Python.""") - -list("""If your system has the gzip libz library installed -check that the zlib extension will be installed by default by editing -the file Modules/Setup.in and ensuring that (near line 405) the line -containing zlib zlibmodule.c is uncommented i.e. has no hash '#' character at the -beginning. You also need to decide if you will be installing in the default location -(/usr/local/) or in some other place. -The zlib module is needed if you want compressed PDF and for some images.""") - -list("""Invoke the command $./configure --prefix=/usr/local$ this should configure -the source directory for building. Then you can build the binaries with -a $make$ command. If your $make$ command is not up to it try building -with $make MAKE=make$. If all goes well install with $make install$.""") - -list("""If all has gone well and python is in the execution search path -you should now be able to type $python$ and see a Python prompt.""") - -list(""" -Once you can do that it's time to try and install ReportLab. -First get the latest reportlab.tgz. -If ReportLab is to be available to all then the reportlab archive should be unpacked in -the lib/site-python directory (typically /usr/local/lib/site-python) if necessary by -a superuser. -Otherwise unpack in a directory of your choice and arrange for that directory to be on your -$PYTHONPATH$ variable. -""") -eg(""" -#put something like this in your -#shell rcfile -PYTHONPATH=$HOME/mypythonpackages -export PYTHONPATH -""",after=0.1) - -list("""You should now be able to run python and execute the python statement -""",doBullet=0) -eg("""import reportlab""",after=0.1) -list("""If you want to use images you should certainly consider -getting & installing the Python Imaging Library - follow the -directions from -$http://www.python.org/sigs/image-sig/index.html$ or get it directly from -$http://www.pythonware.com/products/pil/$.""") - - -heading3("Instructions for Python novices: Mac") -disc(""" -This is much, much easier with Mac OS X since Python (usually 2.3) is installed on your -system as standard. Just follow the instructions for installing the ReportLab archive -above. -""") - - -heading3("Instructions for Jython (Java implementation of Python) users") - -disc(""" -A port to Java was done in 2004. This involved some changes to the framework -and creating Java equivalents of the C extensions. At the end of this work -the entire output of the test suite produced byte-for-byte identical output. -However, we have not been testng against Jython since, because (a) as far as -we know no one used it, and (b) Jython has not kept up with Python features -which we need to use. We suggest you use ReportLab v1.19 or v1.20 which -were Python-2.1 compatible. We'd welcome test reports and/or a volunteer to -refresh things now that Jython is progressing.""") - -disc(""" -The Jython version was tested under Sun's J2SDK 1.3.1. It is known that under -J2SDK 1.4.0_01 $test_pdfbase_ttfonts.py$ fails horribly with an outOfMemory -exception, probably caused by a JVM bug. -""") - - - - -restartList() - -list(""" -Before installing Jython, make sure you have a supported version of -Java Virtual Machine installed. For the list of supported JVM's see -$http://www.jython.org/platform.html$ -""") - -list(""" -To install Jython, download the setup package from $www.jython.org$ and -follow installation instructions. -""") - -list(""" -To set ReportLab toolkit under Jython PATH, edit $JYTHON_HOME/registry$ file -and include line that tells Jython where to look for packages. To include -ReportLab toolkit under Jython PATH, directory that contains Reportlab -should be included: $python.path=REPORTLAB_HOME_PARENT_DIR$ -For example, if your Reportlab toolkit is installed under $C:\code\\reportlab$ -the path line should be: $python.path=C:\\\\code$ (note two backslashes!) -""") - -heading3("Instructions for IronPython (Python for .NET) users") - -disc(""" -We haven't tackled this yet officially, but IronPython can apparently -run much of our code. We do need to go through the same exercises we did for Jython -- finding the .NET equivalents of _rl_accel, pyRXP, _renderPM and PIL - -to get 100% managed code. Hopefully this will happen soon and we'd be -delighted to work with anyone on this. -""") - - -heading2("Getting Involved") -disc("""ReportLab is an Open Source project. Although we are -a commercial company we provide the core PDF generation -sources freely, even for commercial purposes, and we make no income directly -from these modules. We also welcome help from the community -as much as any other Open Source project. There are many -ways in which you can help:""") - -bullet("""General feedback on the core API. Does it work for you? -Are there any rough edges? Does anything feel clunky and awkward?""") - -bullet("""New objects to put in reports, or useful utilities for the library. -We have an open standard for report objects, so if you have written a nice -chart or table class, why not contribute it?""") - -bullet("""Demonstrations and Case Studies: If you have produced some nice -output, send it to us (with or without scripts). If ReportLab solved a -problem for you at work, write a little 'case study' and send it in. -And if your web site uses our tools to make reports, let us link to it. -We will be happy to display your work (and credit it with your name -and company) on our site!""") - -bullet("""Working on the core code: we have a long list of things -to refine or to implement. If you are missing some features or -just want to help out, let us know!""") - -disc("""The first step for anyone wanting to learn more or -get involved is to join the mailing list. To Subscribe visit -$http://two.pairlist.net/mailman/listinfo/reportlab-users$. -From there you can also browse through the group's archives -and contributions. The mailing list is -the place to report bugs and get support. """) - - -heading2("Site Configuration") -disc("""There are a number of options which most likely need to be configured globally for a site. -The python script module $reportlab/rl_config.py$ may be edited to change the values of several -important sitewide properties.""") -bullet("""verbose: set to integer values to control diagnostic output.""") -bullet("""shapeChecking: set this to zero to turn off a lot of error checking in the graphics modules""") -bullet("""defaultEncoding: set this to WinAnsiEncoding or MacRomanEncoding.""") -bullet("""defaultPageSize: set this to one of the values defined in reportlab/lib/pagesizes.py; as delivered -it is set to pagesizes.A4; other values are pagesizes.letter etc.""") -bullet("""defaultImageCaching: set to zero to inhibit the creation of .a85 files on your -hard-drive. The default is to create these preprocessed PDF compatible image files for faster loading""") -bullet("""T1SearchPath: this is a python list of strings representing directories that -may be queried for information on Type 1 fonts""") -bullet("""TTFSearchPath: this is a python list of strings representing directories that -may be queried for information on TrueType fonts""") -bullet("""CMapSearchPath: this is a python list of strings representing directories that -may be queried for information on font code maps.""") -bullet("""showBoundary: set to non-zero to get boundary lines drawn.""") -bullet("""ZLIB_WARNINGS: set to non-zero to get warnings if the Python compression extension is not found.""") -bullet("""pageComression: set to non-zero to try and get compressed PDF.""") -bullet("""allowtableBoundsErrors: set to 0 to force an error on very large Platypus table elements""") -bullet("""emptyTableAction: Controls behaviour for empty tables, can be 'error' (default), 'indicate' or 'ignore'.""") - - - -heading2("Learning More About Python") - -disc(""" -If you are a total beginner to Python, you should check out one or more from the -growing number of resources on Python programming. The following are freely -available on the web: -""") - - -bullet("""Introductory Material on Python. -A list of tutorials on the Python.org web site. -$http://www.python.org/doc/Intros.html$ -""") - - -bullet("""Python Tutorial. -The official Python Tutorial by Guido van Rossum (edited by Fred L. Drake, Jr.) -$http://www.python.org/doc/tut/$ -""") - - -bullet("""Learning to Program. -A tutorial on programming by Alan Gauld. Has a heavy emphasis on -Python, but also uses other languages. -$http://www.freenetpages.co.uk/hp/alan.gauld/$ -""") - - -bullet("""How to think like a computer scientist (Python version). -$http://www.ibiblio.org/obp/thinkCSpy/$ -""") - - -bullet("""Instant Python. -A 6-page minimal crash course by Magnus Lie Hetland. -$http://www.hetland.org/python/instant-python.php$ -""") - - -bullet("""Dive Into Python. -A free Python tutorial for experienced programmers. -$http://diveintopython.org/$ -""") - - -from reportlab.lib.codecharts import SingleByteEncodingChart -from reportlab.tools.docco.stylesheet import getStyleSheet -styles = getStyleSheet() -indent0_style = styles['Indent0'] -indent1_style = styles['Indent1'] - -heading2("What's New in ReportLab 2.0") -disc(""" -Many new features have been added, foremost amongst which is the support -for unicode. This page documents what has changed since version 1.20.""") - -disc(""" -Adding full unicode support meant that we had to break backwards-compatibility, -so old code written for ReportLab 1 will sometimes need changes before it will -run correctly with ReportLab 2. Now that we have made the clean break to -introduce this important new feature, we intend to keep the API -backwards-compatible throughout the 2.* series. -""") -heading3("Goals for the 2.x series") -disc(""" -The main rationale for 2.0 was an incompatible change at the character level: -to properly support Unicode input. Now that it's out we will maintain compatibility -with 2.0. There are no pressing feature wishlists and new features will be driven, -as always, by contributions and the demands of projects.""") - -disc(""" -Our 1.x code base is still Python 2.1 compatible. The new version lets us move forwards -with a baseline of Python 2.4 (2.3 will work too, for the moment, but we don't promise -that going forwards) so we can use newer language features freely in our development.""") - -disc(""" -One area where we do want to make progress from release to release is with documentation -and installability. We'll be looking into better support for distutils, setuptools, -eggs and so on; and into better examples and tools to help people learn what's in the -(substantial) code base.""") - -disc(""" -Bigger ideas and more substantial rewrites are deferred to Version 3.0, with no particular -target dates. -""") - -heading3("Contributions") -disc("""Thanks to everybody who has contributed to the open-source toolkit in the run-up -to the 2.0 release, whether by reporting bugs, sending patches, or contributing to the -reportlab-users mailing list. Thanks especially to the following people, who contributed -code that has gone into 2.0: Andre Reitz, Max M, Albertas Agejevas, T Blatter, Ron Peleg, -Gary Poster, Steve Halasz, Andrew Mercer, Paul McNett, Chad Miller. -""") -todo("""If we missed you, please let us know!""") - -heading3("Unicode support") -disc(""" -This is the Big One, and the reason some apps may break. You must now pass in text either -in UTF-8 or as unicode string objects. The library will handle everything to do with output -encoding. There is more information on this below. -Since this is the biggest change, we'll start by reviewing how it worked in the past.""") - -disc(""" -In ReportLab 1.x, any string input you passed to our APIs was supposed to be in the same -encoding as the font you selected for output. If using the default fonts in Acrobat Reader -(Helvetica/Times/Courier), you would have implicitly used WinAnsi encoding, which is almost -exactly the same as Latin-1. However, if using TrueType fonts, you would have been using UTF-8.""") - -disc("""For Asian fonts, you had a wide choice of encodings but had to specify which one -(e.g Shift-JIS or EUC for Japanese). This state of affairs meant that you had -to make sure that every piece of text input was in the same encoding as the font used -to display it.""") - - - -disc("""Input text encoding is UTF-8 or Python Unicode strings""") -disc(""" -Any text you pass to a canvas API (drawString etc.), Paragraph or other flowable -constructor, into a table cell, or as an attribute of a graphic (e.g. chart.title.text), -is supposed to be unicode. If you use a traditional Python string, it is assumed to be UTF-8. -If you pass a Unicode object, we know it's unicode.""", style=indent1_style) - -disc("""Font encodings""") -disc(""" -Fonts still work in different ways, and the built-in ones will still use WinAnsi or MacRoman -internally while TrueType will use UTF-8. However, the library hides this from you; it converts -as it writes out the PDF file. As before, it's still your job to make sure the font you use has -the characters you need, or you may get either a traceback or a visible error character.""",style=indent1_style) - -disc("""Asian CID fonts""") -disc(""" -You no longer need to specify the encoding for the built-in Asian fonts, just the face name. -ReportLab knows about the standard fonts in Adobe's Asian Language Packs -""", style=indent1_style) - -disc("""Asian Truetype fonts""") -disc(""" -The standard Truetype fonts differ slightly for Asian languages (e.g msmincho.ttc). -These can now be read and used, albeit somewhat inefficiently. -""", style=indent1_style) - -disc("""Asian word wrapping""") -disc(""" -Previously we could display strings in Asian languages, but could not properly -wrap paragraphs as there are no gaps between the words. We now have a basic word wrapping -algorithm. -""", style=indent1_style) - -disc("""unichar tag""") -disc(""" -A convenience tag, <unichar/> has also been added. You can now do -or <unichar name='LATIN SMALL LETTER U WITH DIAERESIS'/> and -get a lowercase u umlaut. Names should be those in the Unicode Character Database. -""", style=indent1_style) - -disc("""Accents, greeks and symbols""") -disc(""" -The correct way to refer to all non-ASCII characters is to use their unicode representation. -This can be literal Unicode or UTF-8. Special symbols and Greek letters (collectively, "greeks") -inserted in paragraphs using the greek tag (e.g. <greek>lambda</greek>) or using the entity -references (e.g. λ) are now processed in a different way than in version 1.""", style=indent1_style) -disc(""" -Previously, these were always rendered using the Zapf Dingbats font. Now they are always output -in the font you specified, unless that font does not support that character. If the font does -not support the character, and the font you specified was an Adobe Type 1 font, Zapf Dingbats -is used as a fallback. However, at present there is no fallback in the case of TTF fonts. -Note that this means that documents that contain greeks and specify a TTF font may need -changing to explicitly specify the font to use for the greek character, or you will see a black -square in place of that character when you view your PDF output in Acrobat Reader. -""", style=indent1_style) - -# Other New Features Section ####################### -heading3("Other New Features") -disc("""PDF""") -disc("""Improved low-level annotation support for PDF "free text annotations" -""", style=indent0_style) -disc("""FreeTextAnnotation allows showing and hiding of an arbitrary PDF "form" -(reusable chunk of PDF content) depending on whether the document is printed or -viewed on-screen, or depending on whether the mouse is hovered over the content, etc. -""", style=indent1_style) - -disc("""TTC font collection files are now readable" -""", style=indent0_style) -disc("""ReportLab now supports using TTF fonts packaged in .TTC files""", style=indent1_style) - -disc("""East Asian font support (CID and TTF)""", style=indent0_style) -disc("""You no longer need to specify the encoding for the built-in Asian fonts, -just the face name. ReportLab knows about the standard fonts in Adobe's Asian Language Packs. -""", style=indent1_style) - -disc("""Native support for JPEG CMYK images""", style=indent0_style) -disc("""ReportLab now takes advantage of PDF's native JPEG CMYK image support, -so that JPEG CMYK images are no longer (lossily) converted to RGB format before including -them in PDF.""", style=indent1_style) - - -disc("""Platypus""") -disc("""Link support in paragraphs""", style=indent0_style) -disc(""" -Platypus paragraphs can now contain link elements, which support both internal links -to the same PDF document, links to other local PDF documents, and URL links to pages on -the web. Some examples:""", style=indent1_style) -disc("""Web links:""", style=indent1_style) -disc("""<link href="http://www.reportlab.com/">ReportLab<link>""", style=styles['Link']) - -disc("""Internal link to current PDF document:""", style=indent1_style) -disc("""<link href="summary">ReportLab<link>""", style=styles['Link']) - -disc("""External link to a PDF document on the local filesystem:""", style=indent1_style) -disc("""<link href="pdf:C:/john/report.pdf">ReportLab<link>""", style=styles['Link']) - -disc("""Improved wrapping support""", style=indent0_style) -disc("""Support for wrapping arbitrary sequence of flowables around an image, using -reportlab.platypus.flowables.ImageAndFlowables (similar to ParagraphAndImage).""" -,style=indent1_style) - -disc("""KeepInFrame""", style=indent0_style) -disc("""Sometimes the length of a piece of text you'd like to include in a fixed piece -of page "real estate" is not guaranteed to be constrained to a fixed maximum length. -In these cases, KeepInFrame allows you to specify an appropriate action to take when -the text is too long for the space allocated for it. In particular, it can shrink the text -to fit, mask (truncate) overflowing text, allow the text to overflow into the rest of the document, -or raise an error.""",style=indent1_style) - - -disc("""Improved convenience features for inserting unicode symbols and other characters -""", style=indent0_style) -disc(""" lets you conveniently insert unicode characters using the standard long name -or code point. Characters inserted with the <greek> tags (e.g. lambda) or corresponding -entity references (e.g. λ) support arbitrary fonts (rather than only Zapf Dingbats).""",style=indent1_style) - -disc("""Improvements to Legending""", style=indent0_style) -disc("""Instead of manual placement, there is now a attachment point (N, S, E, W, etc.), so that -the legend is always automatically positioned correctly relative to the chart. Swatches (the small -sample squares of colour / pattern fill sometimes displayed in the legend) can now be automatically -created from the graph data. Legends can now have automatically-computed totals (useful for -financial applications).""",style=indent1_style) - -disc("""More and better ways to place piechart labels""", style=indent0_style) -disc("""New smart algorithms for automatic pie chart label positioning have been added. -You can now produce nice-looking labels without manual positioning even for awkward cases in -big runs of charts.""",style=indent1_style) - -disc("""Adjustable piechart slice ordering""", style=indent0_style) -disc("""For example. pie charts with lots of small slices can be configured to alternate thin and -thick slices to help the lagel placememt algorithm work better.""",style=indent1_style) -disc("""Improved spiderplots""", style=indent0_style) - - -# Noteworthy bug fixes Section ####################### -heading3("Noteworthy bug fixes") -disc("""Fixes to TTF splitting (patch from Albertas Agejevas)""") -disc("""This affected some documents using font subsetting""", style=indent0_style) - -disc("""Tables with spans improved splitting""") -disc("""Splitting of tables across pages did not work correctly when the table had -row/column spans""", style=indent0_style) - -disc("""Fix runtime error affecting keepWithNext""") diff --git a/bin/reportlab/docs/userguide/ch2_graphics.py b/bin/reportlab/docs/userguide/ch2_graphics.py deleted file mode 100644 index 083b756db5b..00000000000 --- a/bin/reportlab/docs/userguide/ch2_graphics.py +++ /dev/null @@ -1,1180 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch2_graphics.py -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.lib.codecharts import SingleByteEncodingChart - -heading1("Graphics and Text with $pdfgen$") - -heading2("Basic Concepts") -disc(""" -The $pdfgen$ package is the lowest level interface for -generating PDF documents. A $pdfgen$ program is essentially -a sequence of instructions for "painting" a document onto -a sequence of pages. The interface object which provides the -painting operations is the $pdfgen canvas$. -""") - -disc(""" -The canvas should be thought of as a sheet of white paper -with points on the sheet identified using Cartesian ^(X,Y)^ coordinates -which by default have the ^(0,0)^ origin point at the lower -left corner of the page. Furthermore the first coordinate ^x^ -goes to the right and the second coordinate ^y^ goes up, by -default.""") - -disc(""" -A simple example -program that uses a canvas follows. -""") - -eg(""" - from reportlab.pdfgen import canvas - def hello(c): - c.drawString(100,100,"Hello World") - c = canvas.Canvas("hello.pdf") - hello(c) - c.showPage() - c.save() -""") - -disc(""" -The above code creates a $canvas$ object which will generate -a PDF file named $hello.pdf$ in the current working directory. -It then calls the $hello$ function passing the $canvas$ as an argument. -Finally the $showPage$ method saves the current page of the canvas -and the $save$ method stores the file and closes the canvas.""") - -disc(""" -The $showPage$ method causes the $canvas$ to stop drawing on the -current page and any further operations will draw on a subsequent -page (if there are any further operations -- if not no -new page is created). The $save$ method must be called after the -construction of the document is complete -- it generates the PDF -document, which is the whole purpose of the $canvas$ object. -""") - -heading2("More about the Canvas") -disc(""" -Before describing the drawing operations, we will digress to cover -some of the things which can be done to configure a canvas. There -are many different settings available. If you are new to Python -or can't wait to produce some output, you can skip ahead, but -come back later and read this!""") - -disc("""First of all, we will look at the constructor -arguments for the canvas:""") -eg(""" def __init__(self,filename, - pagesize=(595.27,841.89), - bottomup = 1, - pageCompression=0, - encoding=rl_config.defaultEncoding, - verbosity=0): - """) - -disc("""The $filename$ argument controls the -name of the final PDF file. You -may also pass in any open file object (such as $sys.stdout$, the python process standard output) -and the PDF document will be written to that. Since PDF -is a binary format, you should take care when writing other -stuff before or after it; you can't deliver PDF documents -inline in the middle of an HTML page!""") - -disc("""The $pagesize$ argument is a tuple of two numbers -in points (1/72 of an inch). The canvas defaults to $A4$ (an international standard -page size which differs from the American standard page size of $letter$), -but it is better to explicitly specify it. Most common page -sizes are found in the library module $reportlab.lib.pagesizes$, -so you can use expressions like""") - -eg("""from reportlab.lib.pagesizes import letter, A4 -myCanvas = Canvas('myfile.pdf', pagesize=letter) -width, height = letter #keep for later -""") - -pencilnote() - -disc("""If you have problems printing your document make sure you -are using the right page size (usually either $A4$ or $letter$). -Some printers do not work well with pages that are too large or too small.""") - -disc("""Very often, you will want to calculate things based on -the page size. In the example above we extracted the width and -height. Later in the program we may use the $width$ variable to -define a right margin as $width - inch$ rather than using -a constant. By using variables the margin will still make sense even -if the page size changes.""") - -disc("""The $bottomup$ argument -switches coordinate systems. Some graphics systems (like PDF -and PostScript) place (0,0) at the bottom left of the page -others (like many graphical user interfaces [GUI's]) place the origen at the top left. The -$bottomup$ argument is deprecated and may be dropped in future""") - -todo("""Need to see if it really works for all tasks, and if not - then get rid of it""") - -disc("""The $pageCompression$ option determines whether the stream -of PDF operations for each page is compressed. By default -page streams are not compressed, because the compression slows the file generation process. -If output size is important set $pageCompression=1$, but remember that, compressed documents will -be smaller, but slower to generate. Note that images are always compressed, and this option -will only save space if you have a very large amount of text and vector graphics -on each page.""") - -disc("""The $encoding$ argument is largely obsolete in version 2.0 and can -probably be omitted by 99% of users. Its default value is fine unless you -very specifically need to use one of the 25 or so characters which are present -in MacRoman and not in Winansi. A useful reference to these is here: -http://www.alanwood.net/demos/charsetdiffs.html. - -The parameter determines which font encoding is used for the -standard Type 1 fonts; this should correspond to the encoding on your system. -Note that this is the encoding used internally by the font; text you -pass to the ReportLab toolkit for rendering should always either be a Python -unicode string object or a UTF-8 encoded byte string (see the next chapter)! -The font encoding has two values at present: $'WinAnsiEncoding'$ or -$'MacRomanEncoding'$. The variable $rl_config.defaultEncoding$ above points -to the former, which is standard on Windows, Mac OS X and many Unices -(including Linux). If you are Mac user and don't have OS X, you may want to -make a global change: modify the line at the top of -reportlab/pdfbase/pdfdoc.py to switch it over. Otherwise, you can -probably just ignore this argument completely and never pass it. For all TTF -and the commonly-used CID fonts, the encoding you pass in here is ignored, -since the reportlab library itself knows the right encodings in those -cases.""") - -disc("""The demo script $reportlab/demos/stdfonts.py$ -will print out two test documents showing all code points -in all fonts, so you can look up characters. Special -characters can be inserted into string commands with -the usual Python escape sequences; for example \\101 = 'A'.""") - -disc("""The $verbosity$ argument determines how much log -information is printed. By default, it is zero to assist -applications which want to capture PDF from standard output. -With a value of 1, you will get a confirmation message -each time a document is generated. Higher numbers may -give more output in future.""") - -todo("to do - all the info functions and other non-drawing stuff") - - - - - -todo("""Cover all constructor arguments, and setAuthor etc.""") - -heading2("Drawing Operations") -disc(""" -Suppose the $hello$ function referenced above is implemented as -follows (we will not explain each of the operations in detail -yet). -""") - -eg(examples.testhello) - -disc(""" -Examining this code notice that there are essentially two types -of operations performed using a canvas. The first type draws something -on the page such as a text string or a rectangle or a line. The second -type changes the state of the canvas such as -changing the current fill or stroke color or changing the current font -type and size. -""") - -disc(""" -If we imagine the program as a painter working on -the canvas the "draw" operations apply paint to the canvas using -the current set of tools (colors, line styles, fonts, etcetera) -and the "state change" operations change one of the current tools -(changing the fill color from whatever it was to blue, or changing -the current font to $Times-Roman$ in 15 points, for example). -""") - -disc(""" -The document generated by the "hello world" program listed above would contain -the following graphics. -""") - -illust(examples.hello, '"Hello World" in pdfgen') - -heading3("About the demos in this document") - -disc(""" -This document contains demonstrations of the code discussed like the one shown -in the rectangle above. These demos are drawn on a "tiny page" embedded -within the real pages of the guide. The tiny pages are %s inches wide -and %s inches tall. The demo displays show the actual output of the demo code. -For convenience the size of the output has been reduced slightly. -""" % (examplefunctionxinches, examplefunctionyinches)) - -heading2('The tools: the "draw" operations') - -disc(""" -This section briefly lists the tools available to the program -for painting information onto a page using the canvas interface. -These will be discussed in detail in later sections. They are listed -here for easy reference and for summary purposes. -""") - -heading3("Line methods") - -eg("""canvas.line(x1,y1,x2,y2)""") -eg("""canvas.lines(linelist)""") - -disc(""" -The line methods draw straight line segments on the canvas. -""") - -heading3("Shape methods") - -eg("""canvas.grid(xlist, ylist) """) -eg("""canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)""") -eg("""canvas.arc(x1,y1,x2,y2) """) -eg("""canvas.rect(x, y, width, height, stroke=1, fill=0) """) -eg("""canvas.ellipse(x1,y1, x2,y2, stroke=1, fill=0)""") -eg("""canvas.wedge(x1,y1, x2,y2, startAng, extent, stroke=1, fill=0) """) -eg("""canvas.circle(x_cen, y_cen, r, stroke=1, fill=0)""") -eg("""canvas.roundRect(x, y, width, height, radius, stroke=1, fill=0) """) - -disc(""" -The shape methods draw common complex shapes on the canvas. -""") - -heading3("String drawing methods") - -eg("""canvas.drawString(x, y, text):""") -eg("""canvas.drawRightString(x, y, text) """) -eg("""canvas.drawCentredString(x, y, text)""") - -disc(""" -The draw string methods draw single lines of text on the canvas. -""") - -heading3("The text object methods") -eg("""textobject = canvas.beginText(x, y) """) -eg("""canvas.drawText(textobject) """) - -disc(""" -Text objects are used to format text in ways that -are not supported directly by the $canvas$ interface. -A program creates a text object from the $canvas$ using $beginText$ -and then formats text by invoking $textobject$ methods. -Finally the $textobject$ is drawn onto the canvas using -$drawText$. -""") - -heading3("The path object methods") - -eg("""path = canvas.beginPath() """) -eg("""canvas.drawPath(path, stroke=1, fill=0) """) -eg("""canvas.clipPath(path, stroke=1, fill=0) """) - -disc(""" -Path objects are similar to text objects: they provide dedicated control -for performing complex graphical drawing not directly provided by the -canvas interface. A program creates a path object using $beginPath$ -populates the path with graphics using the methods of the path object -and then draws the path on the canvas using $drawPath$.""") - -disc("""It is also possible -to use a path as a "clipping region" using the $clipPath$ method -- for example a circular path -can be used to clip away the outer parts of a rectangular image leaving -only a circular part of the image visible on the page. -""") - -heading3("Image methods") -pencilnote() -disc(""" -You need the Python Imaging Library (PIL) to use images with the ReportLab package. -Examnples of the techniques below can be found by running the script $test_pdfgen_general.py$ -in our $test$ subdirectory and looking at page 7 of the output. -""") - -disc(""" -There are two similar-sounding ways to draw images. The preferred one is -the $drawImage$ method. This implements a caching system so you can -define an image once and draw it many times; it will only be -stored once in the PDF file. $drawImage$ also exposes one advanced parameter, -a transparency mask, and will expose more in future. The older technique, -$drawInlineImage$, stores bitmaps within the page stream and is thus very -inefficient if you use the same image more than once in a document; but can result -in PDFs which render faster if the images are very small and not repeated. We'll -discuss the oldest one first: -""") - -eg("""canvas.drawInlineImage(self, image, x,y, width=None,height=None) """) - -disc(""" -The $drawInlineImage$ method places an image on the canvas. The $image$ -parameter may be either a PIL Image object or an image filename. Many common -file formats are accepted including GIF and JPEG. It returns the size of the actual -image in pixels as a (width, height) tuple. -""") - -eg("""canvas.drawImage(self, image, x,y, width=None,height=None,mask=None) """) -disc(""" -The arguments and return value work as for $drawInlineImage$. However, we use a caching -system; a given image will only be stored the first time it is used, and just referenced -on subsequent use. If you supply a filename, it assumes that the same filename -means the same image. If you supply a PIL image, it tests if the content -has actually changed before re-embedding.""") - -disc(""" -The $mask$ parameter lets you create transparent images. It takes 6 numbers -and defines the range of RGB values which will be masked out or treated as -transparent. For example with [0,2,40,42,136,139], it will mask out any pixels -with a Red value from 0 or 1, Green from 40 or 41 and Blue of 136, 137 or 138 -(on a scale of 0-255). It's currently your job to know which color is the -'transparent' or background one.""") - -disc("""PDF allows for many image features and we will expose more of the over -time, probably with extra keyword arguments to $drawImage$.""") - -heading3("Ending a page") - -eg("""canvas.showPage()""") - -disc("""The $showPage$ method finishes the current page. All additional drawing will -be done on another page.""") - -pencilnote() - -disc("""Warning! All state changes (font changes, color settings, geometry transforms, etcetera) -are FORGOTTEN when you advance to a new page in $pdfgen$. Any state settings you wish to preserve -must be set up again before the program proceeds with drawing!""") - -heading2('The toolbox: the "state change" operations') - -disc(""" -This section briefly lists the ways to switch the tools used by the -program -for painting information onto a page using the $canvas$ interface. -These too will be discussed in detail in later sections. -""") - -heading3("Changing Colors") -eg("""canvas.setFillColorCMYK(c, m, y, k) """) -eg("""canvas.setStrikeColorCMYK(c, m, y, k) """) -eg("""canvas.setFillColorRGB(r, g, b) """) -eg("""canvas.setStrokeColorRGB(r, g, b) """) -eg("""canvas.setFillColor(acolor) """) -eg("""canvas.setStrokeColor(acolor) """) -eg("""canvas.setFillGray(gray) """) -eg("""canvas.setStrokeGray(gray) """) - -disc(""" -PDF supports three different color models: gray level, additive (red/green/blue or RGB), and -subtractive with darkness parameter (cyan/magenta/yellow/darkness or CMYK). -The ReportLab packages also provide named colors such as $lawngreen$. There are two -basic color parameters in the graphics state: the $Fill$ color for the interior of graphic -figures and the $Stroke$ color for the boundary of graphic figures. The above methods -support setting the fill or stroke color using any of the four color specifications. -""") - -heading3("Changing Fonts") -eg("""canvas.setFont(psfontname, size, leading = None) """) - -disc(""" -The $setFont$ method changes the current text font to a given type and size. -The $leading$ parameter specifies the distance down to move when advancing from -one text line to the next. -""") - -heading3("Changing Graphical Line Styles") - -eg("""canvas.setLineWidth(width) """) -eg("""canvas.setLineCap(mode) """) -eg("""canvas.setLineJoin(mode) """) -eg("""canvas.setMiterLimit(limit) """) -eg("""canvas.setDash(self, array=[], phase=0) """) - -disc(""" -Lines drawn in PDF can be presented in a number of graphical styles. -Lines can have different widths, they can end in differing cap styles, -they can meet in different join styles, and they can be continuous or -they can be dotted or dashed. The above methods adjust these various parameters.""") - -heading3("Changing Geometry") - -eg("""canvas.setPageSize(pair) """) -eg("""canvas.transform(a,b,c,d,e,f): """) -eg("""canvas.translate(dx, dy) """) -eg("""canvas.scale(x, y) """) -eg("""canvas.rotate(theta) """) -eg("""canvas.skew(alpha, beta) """) - -disc(""" -All PDF drawings fit into a specified page size. Elements drawn outside of the specified -page size are not visible. Furthermore all drawn elements are passed through an affine -transformation which may adjust their location and/or distort their appearence. The -$setPageSize$ method adjusts the current page size. The $transform$, $translate$, $scale$, -$rotate$, and $skew$ methods add additional transformations to the current transformation. -It is important to remember that these transformations are incremental -- a new -transform modifies the current transform (but does not replace it). -""") - -heading3("State control") - -eg("""canvas.saveState() """) -eg("""canvas.restoreState() """) - -disc(""" -Very often it is important to save the current font, graphics transform, line styles and -other graphics state in order to restore them later. The $saveState$ method marks the -current graphics state for later restoration by a matching $restoreState$. Note that -the save and restore method invokation must match -- a restore call restores the state to -the most recently saved state which hasn't been restored yet. -You cannot save the state on one page and restore -it on the next, however -- no state is preserved between pages.""") - -heading2("Other $canvas$ methods.") - -disc(""" -Not all methods of the $canvas$ object fit into the "tool" or "toolbox" -categories. Below are some of the misfits, included here for completeness. -""") - -eg(""" - canvas.setAuthor() - canvas.addOutlineEntry(title, key, level=0, closed=None) - canvas.setTitle(title) - canvas.setSubject(subj) - canvas.pageHasData() - canvas.showOutline() - canvas.bookmarkPage(name) - canvas.bookmarkHorizontalAbsolute(name, yhorizontal) - canvas.doForm() - canvas.beginForm(name, lowerx=0, lowery=0, upperx=None, uppery=None) - canvas.endForm() - canvas.linkAbsolute(contents, destinationname, Rect=None, addtopage=1, name=None, **kw) - canvas.getPageNumber() - canvas.addLiteral() - canvas.getAvailableFonts() - canvas.stringWidth(self, text, fontName, fontSize, encoding=None) - canvas.setPageCompression(onoff=1) - canvas.setPageTransition(self, effectname=None, duration=1, - direction=0,dimension='H',motion='I') -""") - - -heading2('Coordinates (default user space)') - -disc(""" -By default locations on a page are identified by a pair of numbers. -For example the pair $(4.5*inch, 1*inch)$ identifies the location -found on the page by starting at the lower left corner and moving to -the right 4.5 inches and up one inch. -""") - -disc("""For example, the following function draws -a number of elements on a $canvas$.""") - -eg(examples.testcoords) - -disc("""In the default user space the "origin" ^(0,0)^ point is at the lower -left corner. Executing the $coords$ function in the default user space -(for the "demo minipage") we obtain the following.""") - -illust(examples.coords, 'The Coordinate System') - -heading3("Moving the origin: the $translate$ method") - -disc("""Often it is useful to "move the origin" to a new point off -the lower left corner. The $canvas.translate(^x,y^)$ method moves the origin -for the current page to the point currently identified by ^(x,y)^.""") - -disc("""For example the following translate function first moves -the origin before drawing the same objects as shown above.""") - -eg(examples.testtranslate) - -disc("""This produces the following.""") - -illust(examples.translate, "Moving the origin: the $translate$ method") - - -#illust(NOP) # execute some code - -pencilnote() - - -disc(""" -Note: As illustrated in the example it is perfectly possible to draw objects -or parts of objects "off the page". -In particular a common confusing bug is a translation operation that translates the -entire drawing off the visible area of the page. If a program produces a blank page -it is possible that all the drawn objects are off the page. -""") - -heading3("Shrinking and growing: the scale operation") - -disc("""Another important operation is scaling. The scaling operation $canvas.scale(^dx,dy^)$ -stretches or shrinks the ^x^ and ^y^ dimensions by the ^dx^, ^dy^ factors respectively. Often -^dx^ and ^dy^ are the same -- for example to reduce a drawing by half in all dimensions use -$dx = dy = 0.5$. However for the purposes of illustration we show an example where -$dx$ and $dy$ are different. -""") - -eg(examples.testscale) - -disc("""This produces a "short and fat" reduced version of the previously displayed operations.""") - -illust(examples.scale, "Scaling the coordinate system") - - -#illust(NOP) # execute some code - -pencilnote() - - -disc("""Note: scaling may also move objects or parts of objects off the page, -or may cause objects to "shrink to nothing." """) - -disc("""Scaling and translation can be combined, but the order of the -operations are important.""") - -eg(examples.testscaletranslate) - -disc("""This example function first saves the current $canvas$ state -and then does a $scale$ followed by a $translate$. Afterward the function -restores the state (effectively removing the effects of the scaling and -translation) and then does the same operations in a different order. -Observe the effect below.""") - -illust(examples.scaletranslate, "Scaling and Translating") - - -#illust(NOP) # execute some code - -pencilnote() - - -disc("""Note: scaling shrinks or grows everything including line widths -so using the canvas.scale method to render a microscopic drawing in -scaled microscopic units -may produce a blob (because all line widths will get expanded a huge amount). -Also rendering an aircraft wing in meters scaled to centimeters may cause the lines -to shrink to the point where they disappear. For engineering or scientific purposes -such as these scale and translate -the units externally before rendering them using the canvas.""") - -heading3("Saving and restoring the $canvas$ state: $saveState$ and $restoreState$") - -disc(""" -The $scaletranslate$ function used an important feature of the $canvas$ object: -the ability to save and restore the current parameters of the $canvas$. -By enclosing a sequence of operations in a matching pair of $canvas.saveState()$ -an $canvas.restoreState()$ operations all changes of font, color, line style, -scaling, translation, or other aspects of the $canvas$ graphics state can be -restored to the state at the point of the $saveState()$. Remember that the save/restore -calls must match: a stray save or restore operation may cause unexpected -and undesirable behavior. Also, remember that no $canvas$ state is -preserved across page breaks, and the save/restore mechanism does not work -across page breaks. -""") - -heading3("Mirror image") - -disc(""" -It is interesting although perhaps not terribly useful to note that -scale factors can be negative. For example the following function -""") - -eg(examples.testmirror) - -disc(""" -creates a mirror image of the elements drawn by the $coord$ function. -""") - -illust(examples.mirror, "Mirror Images") - -disc(""" -Notice that the text strings are painted backwards. -""") - -heading2("Colors") - -disc(""" -There are four ways to specify colors in $pdfgen$: by name (using the $color$ -module, by red/green/blue (additive, $RGB$) value, -by cyan/magenta/yellow/darkness (subtractive, $CMYK$), or by gray level. -The $colors$ function below exercises each of the four methods. -""") - -eg(examples.testcolors) - -disc(""" -The $RGB$ or additive color specification follows the way a computer -screen adds different levels of the red, green, or blue light to make -any color, where white is formed by turning all three lights on full -$(1,1,1)$.""") - -disc("""The $CMYK$ or subtractive method follows the way a printer -mixes three pigments (cyan, magenta, and yellow) to form colors. -Because mixing chemicals is more difficult than combining light there -is a fourth parameter for darkness. For example a chemical -combination of the $CMY$ pigments generally never makes a perfect -black -- instead producing a muddy color -- so, to get black printers -don't use the $CMY$ pigments but use a direct black ink. Because -$CMYK$ maps more directly to the way printer hardware works it may -be the case that colors specified in $CMYK$ will provide better fidelity -and better control when printed. -""") - -illust(examples.colors, "Color Models") - -heading2('Painting back to front') - -disc(""" -Objects may be painted over other objects to good effect in $pdfgen$. As -in painting with oils the object painted last will show up on top. For -example, the $spumoni$ function below paints up a base of colors and then -paints a white text over the base. -""") - -eg(examples.testspumoni) - -disc(""" -The word "SPUMONI" is painted in white over the colored rectangles, -with the apparent effect of "removing" the color inside the body of -the word. -""") - -illust(examples.spumoni, "Painting over colors") - -disc(""" -The last letters of the word are not visible because the default $canvas$ -background is white and painting white letters over a white background -leaves no visible effect. -""") - -disc(""" -This method of building up complex paintings in layers can be done -in very many layers in $pdfgen$ -- there are fewer physical limitations -than there are when dealing with physical paints. -""") - -eg(examples.testspumoni2) - -disc(""" -The $spumoni2$ function layers an ice cream cone over the -$spumoni$ drawing. Note that different parts of the cone -and scoops layer over eachother as well. -""") -illust(examples.spumoni2, "building up a drawing in layers") - - -heading2('Standard fonts and text objects') - -disc(""" -Text may be drawn in many different colors, fonts, and sizes in $pdfgen$. -The $textsize$ function demonstrates how to change the color and font and -size of text and how to place text on the page. -""") - -eg(examples.testtextsize) - -disc(""" -The $textsize$ function generates the following page. -""") - -illust(examples.textsize, "text in different fonts and sizes") - -disc(""" -A number of different fonts are always available in $pdfgen$. -""") - -eg(examples.testfonts) - -disc(""" -The $fonts$ function lists the fonts that are always available. -These don't need to be stored in a PDF document, since they -are guaranteed to be present in Acrobat Reader. -""") - -illust(examples.fonts, "the 14 standard fonts") - -disc(""" -For information on how to use arbitrary fonts, see the next chapter. -""") - - -heading2("Text object methods") - -disc(""" -For the dedicated presentation of text in a PDF document, use a text object. -The text object interface provides detailed control of text layout parameters -not available directly at the $canvas$ level. In addition, it results in smaller -PDF that will render faster than many separate calls to the $drawString$ methods. -""") - -eg("""textobject.setTextOrigin(x,y)""") -eg("""textobject.setTextTransform(a,b,c,d,e,f)""") -eg("""textobject.moveCursor(dx, dy) # from start of current LINE""") -eg("""(x,y) = textobject.getCursor()""") -eg("""x = textobject.getX(); y = textobject.getY()""") -eg("""textobject.setFont(psfontname, size, leading = None)""") -eg("""textobject.textOut(text)""") -eg("""textobject.textLine(text='')""") -eg("""textobject.textLines(stuff, trim=1)""") - -disc(""" -The text object methods shown above relate to basic text geometry. -""") - -disc(""" -A text object maintains a text cursor which moves about the page when -text is drawn. For example the $setTextOrigin$ places the cursor -in a known position and the $textLine$ and $textLines$ methods move -the text cursor down past the lines that have been missing. -""") - -eg(examples.testcursormoves1) - -disc(""" -The $cursormoves$ function relies on the automatic -movement of the text cursor for placing text after the origin -has been set. -""") - -illust(examples.cursormoves1, "How the text cursor moves") - -disc(""" -It is also possible to control the movement of the cursor -more explicitly by using the $moveCursor$ method (which moves -the cursor as an offset from the start of the current line -NOT the current cursor, and which also has positive ^y^ offsets -move down (in contrast to the normal geometry where -positive ^y^ usually moves up. -""") - -eg(examples.testcursormoves2) - -disc(""" -Here the $textOut$ does not move the down a line in contrast -to the $textLine$ function which does move down. -""") - -illust(examples.cursormoves2, "How the text cursor moves again") - -heading3("Character Spacing") - -eg("""textobject.setCharSpace(charSpace)""") - -disc("""The $setCharSpace$ method adjusts one of the parameters of text -- the inter-character -spacing.""") - -eg(examples.testcharspace) - -disc("""The -$charspace$ function exercises various spacing settings. -It produces the following page.""") - -illust(examples.charspace, "Adjusting inter-character spacing") - -heading3("Word Spacing") - -eg("""textobject.setWordSpace(wordSpace)""") - -disc("The $setWordSpace$ method adjusts the space between words.") - -eg(examples.testwordspace) - -disc("""The $wordspace$ function shows what various word space settings -look like below.""") - -illust(examples.wordspace, "Adjusting word spacing") - -heading3("Horizontal Scaling") - -eg("""textobject.setHorizScale(horizScale)""") - -disc("""Lines of text can be stretched or shrunken horizontally by the -$setHorizScale$ method.""") - -eg(examples.testhorizontalscale) - -disc("""The horizontal scaling parameter ^horizScale^ -is given in percentages (with 100 as the default), so the 80 setting -shown below looks skinny. -""") -illust(examples.horizontalscale, "adjusting horizontal text scaling") - -heading3("Interline spacing (Leading)") - -eg("""textobject.setLeading(leading)""") - -disc("""The vertical offset between the point at which one -line starts and where the next starts is called the leading -offset. The $setLeading$ method adjusts the leading offset. -""") - -eg(examples.testleading) - -disc("""As shown below if the leading offset is set too small -characters of one line my write over the bottom parts of characters -in the previous line.""") - -illust(examples.leading, "adjusting the leading") - -heading3("Other text object methods") - -eg("""textobject.setTextRenderMode(mode)""") - -disc("""The $setTextRenderMode$ method allows text to be used -as a forground for clipping background drawings, for example.""") - -eg("""textobject.setRise(rise)""") - -disc(""" -The $setRise$ method raises or lowers text on the line -(for creating superscripts or subscripts, for example). -""") - -eg("""textobject.setFillColor(aColor); -textobject.setStrokeColor(self, aColor) -# and similar""") - -disc(""" -These color change operations change the color of the text and are otherwise -similar to the color methods for the $canvas$ object.""") - -heading2('Paths and Lines') - -disc("""Just as textobjects are designed for the dedicated presentation -of text, path objects are designed for the dedicated construction of -graphical figures. When path objects are drawn onto a $canvas$ they -are drawn as one figure (like a rectangle) and the mode of drawing -for the entire figure can be adjusted: the lines of the figure can -be drawn (stroked) or not; the interior of the figure can be filled or -not; and so forth.""") - -disc(""" -For example the $star$ function uses a path object -to draw a star -""") - -eg(examples.teststar) - -disc(""" -The $star$ function has been designed to be useful in illustrating -various line style parameters supported by $pdfgen$. -""") - -illust(examples.star, "line style parameters") - -heading3("Line join settings") - -disc(""" -The $setLineJoin$ method can adjust whether line segments meet in a point -a square or a rounded vertex. -""") - -eg(examples.testjoins) - -disc(""" -The line join setting is only really of interest for thick lines because -it cannot be seen clearly for thin lines. -""") - -illust(examples.joins, "different line join styles") - -heading3("Line cap settings") - -disc("""The line cap setting, adjusted using the $setLineCap$ method, -determines whether a terminating line -ends in a square exactly at the vertex, a square over the vertex -or a half circle over the vertex. -""") - -eg(examples.testcaps) - -disc("""The line cap setting, like the line join setting, is only clearly -visible when the lines are thick.""") - -illust(examples.caps, "line cap settings") - -heading3("Dashes and broken lines") - -disc(""" -The $setDash$ method allows lines to be broken into dots or dashes. -""") - -eg(examples.testdashes) - -disc(""" -The patterns for the dashes or dots can be in a simple on/off repeating pattern -or they can be specified in a complex repeating pattern. -""") - -illust(examples.dashes, "some dash patterns") - -heading3("Creating complex figures with path objects") - -disc(""" -Combinations of lines, curves, arcs and other figures -can be combined into a single figure using path objects. -For example the function shown below constructs two path -objects using lines and curves. -This function will be used later on as part of a -pencil icon construction. -""") - -eg(examples.testpenciltip) - -disc(""" -Note that the interior of the pencil tip is filled -as one object even though it is constructed from -several lines and curves. The pencil lead is then -drawn over it using a new path object. -""") - -illust(examples.penciltip, "a pencil tip") - -heading2('Rectangles, circles, ellipses') - -disc(""" -The $pdfgen$ module supports a number of generally useful shapes -such as rectangles, rounded rectangles, ellipses, and circles. -Each of these figures can be used in path objects or can be drawn -directly on a $canvas$. For example the $pencil$ function below -draws a pencil icon using rectangles and rounded rectangles with -various fill colors and a few other annotations. -""") - -eg(examples.testpencil) - -pencilnote() - -disc(""" -Note that this function is used to create the "margin pencil" to the left. -Also note that the order in which the elements are drawn are important -because, for example, the white rectangles "erase" parts of a black rectangle -and the "tip" paints over part of the yellow rectangle. -""") - -illust(examples.pencil, "a whole pencil") - -heading2('Bezier curves') - -disc(""" -Programs that wish to construct figures with curving borders -generally use Bezier curves to form the borders. -""") - -eg(examples.testbezier) - -disc(""" -A Bezier curve is specified by four control points -$(x1,y1)$, $(x2,y2)$, $(x3,y3)$, $(x4,y4)$. -The curve starts at $(x1,y1)$ and ends at $(x4,y4)$ -and the line segment from $(x1,y1)$ to $(x2,y2)$ -and the line segment from $(x3,y3)$ to $(x4,y4)$ -both form tangents to the curve. Furthermore the -curve is entirely contained in the convex figure with vertices -at the control points. -""") - -illust(examples.bezier, "basic bezier curves") - -disc(""" -The drawing above (the output of $testbezier$) shows -a bezier curves, the tangent lines defined by the control points -and the convex figure with vertices at the control points. -""") - -heading3("Smoothly joining bezier curve sequences") - -disc(""" -It is often useful to join several bezier curves to form a -single smooth curve. To construct a larger smooth curve from -several bezier curves make sure that the tangent lines to adjacent -bezier curves that join at a control point lie on the same line. -""") - -eg(examples.testbezier2) - -disc(""" -The figure created by $testbezier2$ describes a smooth -complex curve because adjacent tangent lines "line up" as -illustrated below. -""") - -illust(examples.bezier2, "bezier curves") - -heading2("Path object methods") - -disc(""" -Path objects build complex graphical figures by setting -the "pen" or "brush" at a start point on the canvas and drawing -lines or curves to additional points on the canvas. Most operations -apply paint on the canvas starting at the end point of the last -operation and leave the brush at a new end point. -""") - -eg("""pathobject.moveTo(x,y)""") - -disc(""" -The $moveTo$ method lifts the brush (ending any current sequence -of lines or curves if there is one) and replaces the brush at the -new ^(x,y)^ location on the canvas to start a new path sequence. -""") - -eg("""pathobject.lineTo(x,y)""") - -disc(""" -The $lineTo$ method paints straight line segment from the current brush -location to the new ^(x,y)^ location. -""") - -eg("""pathobject.curveTo(x1, y1, x2, y2, x3, y3) """) - -disc(""" -The $curveTo$ method starts painting a Bezier curve beginning at -the current brush location, using ^(x1,y1)^, ^(x2,y2)^, and ^(x3,y3)^ -as the other three control points, leaving the brush on ^(x3,y3)^. -""") - -eg("""pathobject.arc(x1,y1, x2,y2, startAng=0, extent=90) """) - -eg("""pathobject.arcTo(x1,y1, x2,y2, startAng=0, extent=90) """) - -disc(""" -The $arc$ and $arcTo$ methods paint partial ellipses. The $arc$ method first "lifts the brush" -and starts a new shape sequence. The $arcTo$ method joins the start of -the partial ellipse to the current -shape sequence by line segment before drawing the partial ellipse. The points -^(x1,y1)^ and ^(x2,y2)^ define opposite corner points of a rectangle enclosing -the ellipse. The $startAng$ is an angle (in degrees) specifying where to begin -the partial ellipse where the 0 angle is the midpoint of the right border of the enclosing -rectangle (when ^(x1,y1)^ is the lower left corner and ^(x2,y2)^ is the upper -right corner). The $extent$ is the angle in degrees to traverse on the ellipse. -""") - -eg(examples.testarcs) - -disc("""The $arcs$ function above exercises the two partial ellipse methods. -It produces the following drawing.""") - -illust(examples.arcs, "arcs in path objects") - -eg("""pathobject.rect(x, y, width, height) """) - -disc("""The $rect$ method draws a rectangle with lower left corner -at ^(x,y)^ of the specified ^width^ and ^height^.""") - -eg("""pathobject.ellipse(x, y, width, height)""") - -disc("""The $ellipse$ method -draws an ellipse enclosed in the rectange with lower left corner -at ^(x,y)^ of the specified ^width^ and ^height^. -""") - -eg("""pathobject.circle(x_cen, y_cen, r) """) - -disc("""The $circle$ method -draws a circle centered at ^(x_cen, y_cen)^ with radius ^r^. -""") - -eg(examples.testvariousshapes) - -disc(""" -The $variousshapes$ function above shows a rectangle, circle and ellipse -placed in a frame of reference grid. -""") - -illust(examples.variousshapes, "rectangles, circles, ellipses in path objects") - -eg("""pathobject.close() """) - -disc(""" -The $close$ method closes the current graphical figure -by painting a line segment from the last point of the figure -to the starting point of the figure (the the most -recent point where the brush was placed on the paper by $moveTo$ -or $arc$ or other placement operations). -""") - -eg(examples.testclosingfigures) - -disc(""" -The $closingfigures$ function illustrates the -effect of closing or not closing figures including a line -segment and a partial ellipse. -""") - -illust(examples.closingfigures, "closing and not closing pathobject figures") - -disc(""" -Closing or not closing graphical figures effects only the stroked outline -of a figure, not the filling of the figure as illustrated above. -""") - - -disc(""" -For a more extensive example of drawing using a path object -examine the $hand$ function. -""") - -eg(examples.testhand) - -disc(""" -In debug mode (the default) the $hand$ function shows the tangent line segments -to the bezier curves used to compose the figure. Note that where the segments -line up the curves join smoothly, but where they do not line up the curves show -a "sharp edge". -""") - -illust(examples.hand, "an outline of a hand using bezier curves") - -disc(""" -Used in non-debug mode the $hand$ function only shows the -Bezier curves. With the $fill$ parameter set the figure is -filled using the current fill color. -""") - -eg(examples.testhand2) - -disc(""" -Note that the "stroking" of the border draws over the interior fill where -they overlap. -""") - -illust(examples.hand2, "the finished hand, filled") - - - -heading2("Further Reading: The ReportLab Graphics Library") - -disc(""" -So far the graphics we have seen was created on a fairly low level. -It should be noted, though, that there is another way of creating -much more sophisticated graphics using the emerging dedicated -high-level ReportLab Graphics Library. -""") - -disc(""" -It can be used to produce high-quality, platform-independant, -reusable graphics for different output formats (vector and bitmap) -like PDF, EPS and soon others like SVG. -""") - -disc(""" -A thorough description of its philsophy and features is beyond the -scope of this general user guide and the reader is recommended to -continue with the "ReportLab Graphics Guide". -There she will find information about the existing components and -how to create customized ones. -""") - -disc(""" -Also, the graphics guide contains a presentation of an emerging -charting package and its components (labels, axes, legends and -different types of charts like bar, line and pie charts) that -builds directly on the graphics library. -""") - - -##### FILL THEM IN diff --git a/bin/reportlab/docs/userguide/ch2a_fonts.py b/bin/reportlab/docs/userguide/ch2a_fonts.py deleted file mode 100644 index 460305d42a1..00000000000 --- a/bin/reportlab/docs/userguide/ch2a_fonts.py +++ /dev/null @@ -1,513 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch2a_fonts.py -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.lib.codecharts import SingleByteEncodingChart -from reportlab.platypus import Image -import reportlab - -heading1("Fonts and encodings") - -disc(""" -This chapter covers fonts, encodings and Asian language capabilities. -If you are purely concerned with generating PDFs for Western -European languages, you can just read the "Unicode is the default" section -below and skip the rest on a first reading. -We expect this section to grow considerably over time. We -hope that Open Source will enable us to give better support for -more of the world's languages than other tools, and we welcome -feedback and help in this area. -""") - -heading2("Unicode and UTF8 are the default input encodings") - -disc(""" -Starting with reportlab Version 2.0 (May 2006), all text input you -provide to our APIs should be in UTF8 or as Python Unicode objects. -This applies to arguments to canvas.drawString and related APIs, -table cell content, drawing object parameters, and paragraph source -text. -""") - - -disc(""" -We considered making the input encoding configurable or even locale-dependent, -but decided that "explicit is better than implicit".""") - -disc(""" -This simplifies many things we used to do previously regarding greek -letters, symbols and so on. To display any character, find out its -unicode code point, and make sure the font you are using is able -to display it.""") - -disc(""" -If you are adapting a ReportLab 1.x application, or reading data from -another source which contains single-byte data (e.g. latin-1 or WinAnsi), -you need to do a conversion into Unicode. The Python codecs package now -includes converters for all the common encodings, including Asian ones. -""") - - - -disc(u""" -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 -accent: ^Marc-Andr\u00e9 Lemburg^. The standard error is quite helpful and tells you -what character it doesn't like: -""") - -eg(u""" ->>> from reportlab.pdfgen.canvas import Canvas ->>> c = Canvas('temp.pdf') ->>> y = 700 ->>> for line in file('latin_python_gurus.txt','r'): -... c.drawString(100, y, line.strip()) -... -Traceback (most recent call last): -... -UnicodeDecodeError: 'utf8' codec can't decode bytes in position 9-11: invalid data --->\u00e9 L<--emburg ->>> -""") - - -disc(""" -The simplest fix is just to convert your data to unicode, saying which encoding -it comes from, like this:""") - -eg(""" ->>> for line in file('latin_input.txt','r'): -... uniLine = unicode(line, 'latin-1') -... c.drawString(100, y, uniLine.strip()) ->>> ->>> c.save() -""") - - -heading2("Changing the built-in fonts output encoding") - -disc(""" -There are still a number of places in the code, including the rl_config -defaultEncoding parameter, and arguments passed to various Font constructors. -These generally relate to the OUTPUT encoding used when we write data in the font -file. This affects which characters are actually available in the font if -you are using Type 1 fonts, since only 256 glyphs can be available at -one time. Unless you have a very specific need for -MacRoman or MacExpert encoding characters, we advise you to ignore -this. By default the standard fonts (Helvetica, Courier, Times Roman) -will offer the glyphs available in Latin-1. If you try to print a non-Latin-1 -character using the built-in Helvetica, you'll see a rectangle or blob. - -""") - - -heading2("Using non-standard Type 1 fonts") - -disc(""" -As discussed in the previous chapter, every copy of Acrobat Reader -comes with 14 standard fonts built in. Therefore, the ReportLab -PDF Library only needs to refer to these by name. If you want -to use other fonts, they must be available to your code and -will be embedded in the PDF document.""") - -disc(""" -You can use the mechanism described below to include arbitrary -fonts in your documents. Just van Rossum has kindly donated a Type 1 -font named LettErrorRobot-Chrome which we may -use for testing and/or documenting purposes (and which you may -use as well). It comes bundled with the ReportLab distribution in the -directory $reportlab/fonts$. -""") - -disc(""" -Right now font-embedding relies on font description files in the Adobe -AFM ('Adobe Font Metrics') and PFB ('Printer Font Binary') format. The -former is an ASCII file and contains information about the characters -('glyphs') in the font such as height, width, bounding box info and -other 'metrics', while the latter is a binary file that describes the -shapes of the font. The $reportlab/fonts$ directory contains the files -$'LeERC___.AFM'$ and $'LeERC___.PFB'$ that are used as an example -font. -""") - -disc(""" -In the following example locate the folder containing the test font and -register it for future use with the $pdfmetrics$ module, -after which we can use it like any other standard font. -""") - - -eg(""" -import os -import reportlab -folder = os.path.dirname(reportlab.__file__) + os.sep + 'fonts' -afmFile = os.path.join(folder, 'LeERC___.AFM') -pfbFile = os.path.join(folder, 'LeERC___.PFB') - -from reportlab.pdfbase import pdfmetrics -justFace = pdfmetrics.EmbeddedType1Face(afmFile, pfbFile) -faceName = 'LettErrorRobot-Chrome' # pulled from AFM file -pdfmetrics.registerTypeFace(justFace) -justFont = pdfmetrics.Font('LettErrorRobot-Chrome', - faceName, - 'WinAnsiEncoding') -pdfmetrics.registerFont(justFont) - -canvas.setFont('LettErrorRobot-Chrome', 32) -canvas.drawString(10, 150, 'This should be in') -canvas.drawString(10, 100, 'LettErrorRobot-Chrome') -""") - - -disc(""" -Note that the argument "WinAnsiEncoding" has nothing to do with the input; -it's to say which set of characters within the font file will be active -and available. -""") - -illust(examples.customfont1, "Using a very non-standard font") - -disc(""" -The font's facename comes from the AFM file's $FontName$ field. -In the example above we knew the name in advance, but quite -often the names of font description files are pretty cryptic -and then you might want to retrieve the name from an AFM file -automatically. -When lacking a more sophisticated method you can use some -code as simple as this: -""") - -eg(""" -class FontNameNotFoundError(Exception): - pass - - -def findFontName(path): - "Extract a font name from an AFM file." - - f = open(path) - - found = 0 - while not found: - line = f.readline()[:-1] - if not found and line[:16] == 'StartCharMetrics': - raise FontNameNotFoundError, path - if line[:8] == 'FontName': - fontName = line[9:] - found = 1 - - return fontName -""") - -disc(""" -In the LettErrorRobot-Chrome example we explicitely specified -the place of the font description files to be loaded. -In general, you'll prefer to store your fonts in some canonic -locations and make the embedding mechanism aware of them. -Using the same configuration mechanism we've already seen at the -beginning of this section we can indicate a default search path -for Type-1 fonts. -""") - -disc(""" -Unfortunately, there is no reliable standard yet for such -locations (not even on the same platform) and, hence, you might -have to edit the file $reportlab/rl_config.py$ to modify the -value of the $T1SearchPath$ identifier to contain additional -directories. Our own recommendation is to use the ^reportlab/fonts^ -folder in development; and to have any needed fonts as packaged parts of -your application in any kind of controlled server deployment. This insulates -you from fonts being installed and uninstalled by other software or system -administrator. -""") - -heading3("Warnings about missing glyphs") -disc("""If you specify an encoding, it is generally assumed that -the font designer has provided all the needed glyphs. However, -this is not always true. In the case of our example font, -the letters of the alphabet are present, but many symbols and -accents are missing. The default behaviour is for the font to -print a 'notdef' character - typically a blob, dot or space - -when passed a character it cannot draw. However, you can ask -the library to warn you instead; the code below (executed -before loading a font) will cause warnings to be generated -for any glyphs not in the font when you register it.""") - -eg(""" -import reportlab.rl_config -reportlab.rl_config.warnOnMissingFontGlyphs = 0 -""") - - - -heading2("Standard Single-Byte Font Encodings") -disc(""" -This section shows you the glyphs available in the common encodings. -""") - - -disc("""The code chart below shows the characters in the $WinAnsiEncoding$. -This is the standard encoding on Windows and many Unix systems in America -and Western Europe. It is also knows as Code Page 1252, and is practically -identical to ISO-Latin-1 (it contains one or two extra characters). This -is the default encoding used by the Reportlab PDF Library. It was generated from -a standard routine in $reportlab/lib$, $codecharts.py$, -which can be used to display the contents of fonts. The index numbers -along the edges are in hex.""") - -cht1 = SingleByteEncodingChart(encodingName='WinAnsiEncoding',charsPerRow=32, boxSize=12) -illust(lambda canv: cht1.drawOn(canv, 0, 0), "WinAnsi Encoding", cht1.width, cht1.height) - -disc("""The code chart below shows the characters in the $MacRomanEncoding$. -as it sounds, this is the standard encoding on Macintosh computers in -America and Western Europe. As usual with non-unicode encodings, the first -128 code points (top 4 rows in this case) are the ASCII standard and agree -with the WinAnsi code chart above; but the bottom 4 rows differ.""") -cht2 = SingleByteEncodingChart(encodingName='MacRomanEncoding',charsPerRow=32, boxSize=12) -illust(lambda canv: cht2.drawOn(canv, 0, 0), "MacRoman Encoding", cht2.width, cht2.height) - -disc("""These two encodings are available for the standard fonts (Helvetica, -Times-Roman and Courier and their variants) and will be available for most -commercial fonts including those from Adobe. However, some fonts contain non- -text glyphs and the concept does not really apply. For example, ZapfDingbats -and Symbol can each be treated as having their own encoding.""") - -cht3 = SingleByteEncodingChart(faceName='ZapfDingbats',encodingName='ZapfDingbatsEncoding',charsPerRow=32, boxSize=12) -illust(lambda canv: cht3.drawOn(canv, 0, 0), "ZapfDingbats and its one and only encoding", cht3.width, cht3.height) - -cht4 = SingleByteEncodingChart(faceName='Symbol',encodingName='SymbolEncoding',charsPerRow=32, boxSize=12) -illust(lambda canv: cht4.drawOn(canv, 0, 0), "Symbol and its one and only encoding", cht4.width, cht4.height) - - -CPage(5) -heading2("TrueType Font Support") -disc(""" -Marius Gedminas ($mgedmin@delfi.lt$) with the help of Viktorija Zaksiene ($vika@pov.lt$) -have contributed support for embedded TrueType fonts. TrueType fonts work in Unicode/UTF8 -and are not limited to 256 characters.""") - - -CPage(3) -disc("""We use $reportlab.pdfbase.ttfonts.TTFont$ to create a true type -font object and register using $reportlab.pdfbase.pdfmetrics.registerFont$. -In pdfgen drawing directly to the canvas we can do""") -eg(""" -# we know some glyphs are missing, suppress warnings -import reportlab.rl_config -reportlab.rl_config.warnOnMissingFontGlyphs = 0 - -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfbase.ttfonts import TTFont -pdfmetrics.registerFont(TTFont('Rina', 'rina.ttf')) -canvas.setFont(Rina, 32) -canvas.drawString(10, 150, "Some text encoded in UTF-8") -canvas.drawString(10, 100, "In the Rina TT Font!") -""") -illust(examples.ttffont1, "Using a the Rina TrueType Font") -disc("""In the above example the true type font object is created using""") -eg(""" - TTFont(name,filename) -""") -disc("""so that the ReportLab internal name is given by the first argument and the second argument -is a string(or file like object) denoting the font's TTF file. In Marius' original patch the filename -was supposed to be exactly correct, but we have modified things so that if the filename is relative -then a search for the corresponding file is done in the current directory and then in directories -specified by $reportlab.rl_config.TTFSearchpath$!""") - -from reportlab.lib.styles import ParagraphStyle - -from reportlab.lib.fonts import addMapping -addMapping('Rina', 0, 0, 'Rina') -addMapping('Rina', 0, 1, 'Rina') -addMapping('Rina', 1, 0, 'Rina') -addMapping('Rina', 1, 1, 'Rina') - -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 $$ and $$ attributes.""") - -eg(""" -from reportlab.lib.fonts import addMapping -addMapping('Rina', 0, 0, 'Rina') #normal -addMapping('Rina', 0, 1, 'Rina') #italic -addMapping('Rina', 1, 0, 'Rina') #bold -addMapping('Rina', 1, 1, 'Rina') #italic and bold -""") - -disc("""We only have a Rina regular font, no bold or italic, so we must map all to the -same internal fontname. ^<b>^ and ^<i>^ tags may now be used safely, but -have no effect. -After registering and mapping -the Rina font as above we can use paragraph text like""") -parabox2("""This is in Times-Roman -and this is in magenta Rina!""","Using TTF fonts in paragraphs") - - - - -heading2("Asian Font Support") -disc("""The Reportlab PDF Library aims to expose full support for Asian fonts. -PDF is the first really portable solution for Asian text handling. There are -two main approaches for this: Adobe's Asian Language Packs, or TrueType fonts. -""") - -heading3("Asian Language Packs") -disc(""" -This approach offers the best performance since nothing needs embedding in the PDF file; -as with the standard fonts, everything is on the reader.""") - -disc(""" -Adobe makes available add-ons for each main language. In Adobe Reader 6.0 and 7.0, you -will be prompted to download and install these as soon as you try to open a document -using them. In earlier versions, you would see an error message on opening an Asian document -and had to know what to do. -""") - -disc(""" -Japanese, Traditional Chinese (Taiwan/Hong Kong), Simplified Chinese (mainland China) -and Korean are all supported and our software knows about the following fonts: -""") -bullet(""" -$chs$ = Chinese Simplified (mainland): '$STSong-Light$' -""") -bullet(""" -$cht$ = Chinese Traditional (Taiwan): '$MSung-Light$', '$MHei-Medium$' -""") -bullet(""" -$kor$ = Korean: '$HYSMyeongJoStd-Medium$','$HYGothic-Medium$' -""") -bullet(""" -$jpn$ = Japanese: '$HeiseiMin-W3$', '$HeiseiKakuGo-W5$' -""") - - -disc("""Since many users will not have the font packs installed, we have included -a rather grainy ^bitmap^ of some Japanese characters. We will discuss below what is needed to -generate them.""") -# include a bitmap of some Asian text -I=os.path.join(os.path.dirname(reportlab.__file__),'docs','images','jpnchars.jpg') -try: - getStory().append(Image(I)) -except: - disc("""An image should have appeared here.""") - -disc("""Prior to Version 2.0, you had to specify one of many native encodings -when registering a CID Font. In version 2.0 you should a new UnicodeCIDFont -class.""") - -eg(""" -from reportlab.pdfbase import pdfmetrics -from reportlab.pdfbase.cidfonts import UnicodeCIDFont -pdfmetrics.registerFont(UnicodeCIDFont('HeiseiMin-W3')) -canvas.setFont('HeiseiMin-W3', 16) - -# the two unicode characters below are "Tokyo" -msg = u'\u6771\u4EAC : Unicode font, unicode input' -canvas.drawString(100, 675, msg) -""") -#had to double-escape the slashes above to get escapes into the PDF - -disc("""The old coding style with explicit encodings should still work, but is now -only relevant if you need to construct vertical text. We aim to add more readable options -for horizontal and vertical text to the UnicodeCIDFont constructor in future. -The following four test scripts generate samples in the corresponding languages:""") -eg("""reportlab/test/test_multibyte_jpn.py -reportlab/test/test_multibyte_kor.py -reportlab/test/test_multibyte_chs.py -reportlab/test/test_multibyte_cht.py""") - -## put back in when we have vertical text... -##disc("""The illustration below shows part of the first page -##of the Japanese output sample. It shows both horizontal and vertical -##writing, and illustrates the ability to mix variable-width Latin -##characters in Asian sentences. The choice of horizontal and vertical -##writing is determined by the encoding, which ends in 'H' or 'V'. -##Whether an encoding uses fixed-width or variable-width versions -##of Latin characters also depends on the encoding used; see the definitions -##below.""") -## -##Illustration(image("../images/jpn.gif", width=531*0.50, -##height=435*0.50), 'Output from test_multibyte_jpn.py') -## -##caption(""" -##Output from test_multibyte_jpn.py -##""") - - - - -disc("""In previous versions of the ReportLab PDF Library, we had to make -use of Adobe's CMap files (located near Acrobat Reader if the Asian Language -packs were installed). Now that we only have one encoding to deal with, the -character width data is embedded in the package, and CMap files are not needed -for generation. The CMap search path in ^rl_config.py^ is now deprecated -and has no effect if you restrict yourself to UnicodeCIDFont. -""") - - -heading3("TrueType fonts with Asian characters") -disc(""" -This is the easy way to do it. No special handling at all is needed to -work with Asian TrueType fonts. Windows users who have installed, for example, -Japanese as an option in Control Panel, will have a font "msmincho.ttf" which -can be used. However, be aware that it takes time to parse the fonts, and that -quite large subsets may need to be embedded in your PDFs. We can also now parse -files ending in .ttc, which are a slight variation of .ttf. - -""") - - -heading3("To Do") -disc("""We expect to be developing this area of the package for some time.accept2dyear -Here is an outline of the main priorities. We welcome help!""") - -bullet(""" -Ensure that we have accurate character metrics for all encodings in horizontal and -vertical writing.""") - -bullet(""" -Add options to ^UnicodeCIDFont^ to allow vertical and proportional variants where the font permits it.""") - - -bullet(""" -Improve the word wrapping code in paragraphs and allow vertical writing.""") - - - -CPage(5) -heading2("RenderPM tests") - -disc("""This may also be the best place to mention the test function of $reportlab/graphics/renderPM.py$, -which can be considered the cannonical place for tests which exercise renderPM (the "PixMap Renderer", -as opposed to renderPDF, renderPS or renderSVG).""") - -disc("""If you run this from the command line, you should see lots of output like the following.""") - -eg("""C:\\code\\reportlab\\graphics>renderPM.py -wrote pmout\\renderPM0.gif -wrote pmout\\renderPM0.tif -wrote pmout\\renderPM0.png -wrote pmout\\renderPM0.jpg -wrote pmout\\renderPM0.pct -... -wrote pmout\\renderPM12.gif -wrote pmout\\renderPM12.tif -wrote pmout\\renderPM12.png -wrote pmout\\renderPM12.jpg -wrote pmout\\renderPM12.pct -wrote pmout\\index.html""") - -disc("""This runs a number of tests progressing from a "Hello World" test, through various tests of -Lines; text strings in a number of sizes, fonts, colours and alignments; the basic shapes; translated -and rotated groups; scaled coordinates; rotated strings; nested groups; anchoring and non-standard fonts.""") - -disc("""It creates a subdirectory called $pmout$, writes the image files into it, and writes an -$index.html$ page which makes it easy to refer to all the results.""") - -disc("""The font-related tests which you may wish to look at are test #11 ('Text strings in a non-standard font') -and test #12 ('Test Various Fonts').""") - - - - -##### FILL THEM IN diff --git a/bin/reportlab/docs/userguide/ch3_pdffeatures.py b/bin/reportlab/docs/userguide/ch3_pdffeatures.py deleted file mode 100644 index 36fe8b8be07..00000000000 --- a/bin/reportlab/docs/userguide/ch3_pdffeatures.py +++ /dev/null @@ -1,271 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch3_pdffeatures.py -from reportlab.tools.docco.rl_doc_utils import * - - -heading1("Exposing PDF Special Capabilities") -disc("""PDF provides a number of features to make electronic - document viewing more efficient and comfortable, and - our library exposes a number of these.""") - -heading2("Forms") -disc("""The Form feature lets you create a block of graphics and text - once near the start of a PDF file, and then simply refer to it on - subsequent pages. If you are dealing with a run of 5000 repetitive - business forms - for example, one-page invoices or payslips - you - only need to store the backdrop once and simply draw the changing - text on each page. Used correctly, forms can dramatically cut - file size and production time, and apparently even speed things - up on the printer. - """) -disc("""Forms do not need to refer to a whole page; anything which - might be repeated often should be placed in a form.""") -disc("""The example below shows the basic sequence used. A real - program would probably define the forms up front and refer to - them from another location.""") - - -eg(examples.testforms) - -heading2("Links and Destinations") -disc("""PDF supports internal hyperlinks. There is a very wide - range of link types, destination types and events which - can be triggered by a click. At the moment we just - support the basic ability to jump from one part of a document - to another, and to control the zoom level of the window after - the jump. The bookmarkPage method defines a destination that - is the endpoint of a jump.""") -#todo("code example here...") - -eg(""" - canvas.bookmarkPage(name, - fitType="Fit", - left=None, - top=None, - bottom=None, - right=None, - zoom=None - ) -""") -disc(""" -By default the $bookmarkPage$ method defines the page itself as the -destination. After jumping to an endpoint defined by bookmarkPage, -the PDF browser will display the whole page, scaling it to fit the -screen:""") - -eg("""canvas.bookmarkPage(name)""") - -disc("""The $bookmarkPage$ method can be instructed to display the -page in a number of different ways by providing a $fitType$ -parameter.""") - -eg("") - -t = Table([ - ['fitType','Parameters Required','Meaning'], - ['Fit',None,'Entire page fits in window (the default)'], - ['FitH','top','Top coord at top of window, width scaled to fit'], - ['FitV','left','Left coord at left of window, height scaled to fit'], - ['FitR','left bottom right top','Scale window to fit the specified rectangle'], - ['XYZ','left top zoom','Fine grained control. If you omit a parameter\nthe PDF browser interprets it as "leave as is"'] - ]) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,1),'Times-Bold',10,12), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) - -getStory().append(t) -caption("""Table - Required attributes for different fit types""") - -disc(""" -Note : $fitType$ settings are case-sensitive so $fitType="FIT"$ is invalid$ -""") - - -disc(""" -Sometimes you want the destination of a jump to be some part of a page. -The $FitR$ fitType allows you to identify a particular rectangle, scaling -the area to fit the entire page. -""") - -disc(""" -To set the display to a particular x and y coordinate of the page and to -control the zoom directly use fitType="XYZ". -""") - -eg(""" -canvas.bookmarkPage('my_bookmark',fitType="XYZ",left=0,top=200) -""") - - - -disc(""" -This destination is at the leftmost of the page with the top of the screen -at position 200. Because $zoom$ was not set the zoom remains at whatever the -user had it set to. -""") - -eg(""" -canvas.bookmarkPage('my_bookmark',fitType="XYZ",left=0,top=200,zoom=2) -""") - -disc("""This time zoom is set to expand the page 2X its normal size.""") - -disc(""" -Note : Both $XYZ$ and $FitR$ fitTypes require that their positional parameters -($top, bottom, left, right$) be specified in terms of the default user space. -They ignore any geometric transform in effect in the canvas graphic state. -""") - - - -pencilnote() - -disc(""" -Note: Two previous bookmark methods are supported but deprecated now -that bookmarkPage is so general. These are $bookmarkHorizontalAbsolute$ -and $bookmarkHorizontal$. -""") - -heading3("Defining internal links") -eg(""" - canvas.linkAbsolute(contents, destinationname, Rect=None, addtopage=1, name=None, **kw) - """) - -disc(""" - The $linkAbsolute$ method defines a starting point for a jump. When the user - is browsing the generated document using a dynamic viewer (such as Acrobat Reader) - when the mouse is clicked when the pointer is within the rectangle specified - by $Rect$ the viewer will jump to the endpoint associated with $destinationname$. - As in the case with $bookmarkHorizontalAbsolute$ the rectangle $Rect$ must be - specified in terms of the default user space. The $contents$ parameter specifies - a chunk of text which displays in the viewer if the user left-clicks on the region. -""") - -disc(""" -The rectangle $Rect$ must be specified in terms of a tuple ^(x1,y1,x2,y2)^ identifying -the lower left and upper right points of the rectangle in default user space. -""") - -disc(""" -For example the code -""") - -eg(""" - canvas.bookmarkPage("Meaning_of_life") -""") - -disc(""" -defines a location as the whole of the current page with the identifier -$Meaning_of_life$. To create a rectangular link to it while drawing a possibly -different page, we would use this code: -""") - -eg(""" - canvas.linkAbsolute("Find the Meaning of Life", "Meaning_of_life", - (inch, inch, 6*inch, 2*inch)) -""") - -disc(""" -By default during interactive viewing a rectangle appears around the -link. Use the keyword argument $Border='[0 0 0]'$ to -suppress the visible rectangle around the during viewing link. -For example -""") - -eg(""" - canvas.linkAbsolute("Meaning of Life", "Meaning_of_life", - (inch, inch, 6*inch, 2*inch), Border='[0 0 0]') -""") - - -heading2("Outline Trees") -disc("""Acrobat Reader has a navigation page which can hold a - document outline; it should normally be visible when you - open this guide. We provide some simple methods to add - outline entries. Typically, a program to make a document - (such as this user guide) will call the method - $canvas.addOutlineEntry(^self, title, key, level=0, - closed=None^)$ as it reaches each heading in the document. - """) - -disc("""^title^ is the caption which will be displayed in - the left pane. The ^key^ must be a string which is - unique within the document and which names a bookmark, - as with the hyperlinks. The ^level^ is zero - the - uppermost level - unless otherwise specified, and - it is an error to go down more than one level at a time - (for example to follow a level 0 heading by a level 2 - heading). Finally, the ^closed^ argument specifies - whether the node in the outline pane is closed - or opened by default.""") - -disc("""The snippet below is taken from the document template - that formats this user guide. A central processor looks - at each paragraph in turn, and makes a new outline entry - when a new chapter occurs, taking the chapter heading text - as the caption text. The key is obtained from the - chapter number (not shown here), so Chapter 2 has the - key 'ch2'. The bookmark to which the - outline entry points aims at the whole page, but it could - as easily have been an individual paragraph. - """) - -eg(""" -#abridged code from our document template -if paragraph.style == 'Heading1': - self.chapter = paragraph.getPlainText() - key = 'ch%d' % self.chapterNo - self.canv.bookmarkPage(key) - self.canv.addOutlineEntry(paragraph.getPlainText(), - key, 0, 0) - """) - -heading2("Page Transition Effects") - - -eg(""" - canvas.setPageTransition(self, effectname=None, duration=1, - direction=0,dimension='H',motion='I') - """) - -disc(""" -The $setPageTransition$ method specifies how one page will be replaced with -the next. By setting the page transition effect to "dissolve" for example -the current page will appear to melt away when it is replaced by the next -page during interactive viewing. These effects are useful in spicing up -slide presentations, among other places. -Please see the reference manual for more detail on how to use this method. -""") - -heading2("Internal File Annotations") - -eg(""" - canvas.setAuthor(name) - canvas.setTitle(title) - canvas.setSubject(subj) - """) - -disc(""" -These methods have no automatically seen visible effect on the document. -They add internal annotations to the document. These annotations can be -viewed using the "Document Info" menu item of the browser and they also can -be used as a simple standard way of providing basic information about the -document to archiving software which need not parse the entire -file. To find the annotations view the $*.pdf$ output file using a standard -text editor (such as $notepad$ on MS/Windows or $vi$ or $emacs$ on unix) and look -for the string $/Author$ in the file contents. -""") - -eg(examples.testannotations) - -disc(""" -If you want the subject, title, and author to automatically display -in the document when viewed and printed you must paint them onto the -document like any other text. -""") - -illust(examples.annotations, "Setting document internal annotations") diff --git a/bin/reportlab/docs/userguide/ch4_platypus_concepts.py b/bin/reportlab/docs/userguide/ch4_platypus_concepts.py deleted file mode 100644 index b3b6d17a748..00000000000 --- a/bin/reportlab/docs/userguide/ch4_platypus_concepts.py +++ /dev/null @@ -1,509 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch4_platypus_concepts.py -from reportlab.tools.docco.rl_doc_utils import * - -#####################################################################################################3 - - -heading1("PLATYPUS - Page Layout and Typography Using Scripts") - -heading2("Design Goals") - -disc(""" -Platypus stands for "Page Layout and Typography Using Scripts". It is a high -level page layout library which lets you programmatically create complex -documents with a minimum of effort. -""") - - - -disc(""" -The design of Platypus seeks to separate "high level" layout decisions -from the document content as much as possible. Thus, for example, paragraphs -are constructed using paragraph styles and pages are constructed -using page templates with the intention that hundreds of -documents with thousands of pages can be reformatted to different -style specifications with the modifications of a few lines in a single -shared file which contains the paragraph styles and page layout specifications. -""") - - -disc(""" -The overall design of Platypus can be thought of has having -several layers, top down, these are""") - -disc("$DocTemplates$ the outermost container for the document;") - -disc("$PageTemplates$ specifications for layouts of pages of various kinds;") - -disc("$Frames$ specifications of regions in pages that can contain flowing text or graphics.") - -disc("""$Flowables$ text or graphic elements that should be "flowed - into the document (i.e. things like images, paragraphs and tables, but not things - like page footers or fixed page graphics).""") - -disc("""$pdfgen.Canvas$ the lowest level which ultimately receives the painting of the - document from the other layers.""") - -illust(examples.doctemplateillustration, "Illustration of DocTemplate structure") - -disc(""" - The illustration above graphically illustrates the concepts of $DocTemplates$, - $PageTemplates$ and $Flowables$. It is deceptive, however, because each - of the $PageTemplates$ actually may specify the format for any number of pages - (not just one as might be inferred from the diagram). -""") - -disc(""" -$DocTemplates$ contain one or more $PageTemplates$ each of which contain one or more -$Frames$. $Flowables$ are things which can be flowed into a $Frame$ e.g. -a $Paragraph$ or a $Table$. -""") - -disc(""" -To use platypus you create a document from a $DocTemplate$ class and pass -a list of $Flowable$s to its $build$ method. The document -$build$ method knows how to process the list of flowables -into something reasonable. -""") - -disc(""" -Internally the $DocTemplate$ class implements page layout and formatting -using various events. Each of the events has a corresponding handler method -called $handle_XXX$ where $XXX$ is the event name. A typical event is -$frameBegin$ which occurs when the machinery begins to use a frame for the -first time. -""") - -disc(""" -A Platypus story consists of a sequence of basic elements called $Flowables$ -and these elements drive the data driven Platypus formatting engine. -To modify the behavior of the engine -a special kind of flowable, $ActionFlowables$, tell the layout engine to, -for example, skip to the next -column or change to another $PageTemplate$. -""") - - -heading2("""Getting started""") - -disc("""Consider the following code sequence which provides -a very simple "hello world" example for Platypus.""") - -eg(examples.platypussetup) - -disc("""First we import some constructors, some paragraph styles -and other conveniences from other modules.""") - -eg(examples.platypusfirstpage) - -disc("""We define the fixed features of the first page of the document -with the function above.""") - -eg(examples.platypusnextpage) - -disc("""Since we want pages after the first to look different from the -first we define an alternate layout for the fixed features -of the other pages. Note that the two functions above use -the $pdfgen$ level canvas operations to paint the annotations for -the pages. -""") - -eg(examples.platypusgo) - -disc(""" -Finally, we create a story and build the document. -Note that we are using a "canned" document template here which -comes pre-built with page templates. We are also using a pre-built -paragraph style. We are only using two types of flowables here --- $Spacers$ and $Paragraphs$. The first $Spacer$ ensures that the -Paragraphs skip past the title string. -""") - -disc(""" -To see the output of this example program run the module -$docs/userguide/examples.py$ (from the ReportLab $docs$ distribution) -as a "top level script". The script interpretation $python examples.py$ will -generate the Platypus output $phello.pdf$. -""") - - -heading2("$Flowables$") -disc(""" -$Flowables$ are things which can be drawn and which have $wrap$, $draw$ and perhaps $split$ methods. -$Flowable$ is an abstract base class for things to be drawn and an instance knows its size -and draws in its own coordinate system (this requires the base API to provide an absolute coordinate -system when the $Flowable.draw$ method is called). To get an instance use $f=Flowable()$. -""") -disc(""" -It should be noted that the $Flowable$ class is an abstract class and is normally -only used as a base class. -""") -k=startKeep() -disc(""" -To illustrate the general way in which $Flowables$ are used we show how a derived class $Paragraph$ -is used and drawn on a canvas. $Paragraphs$ are so important they will get a whole chapter -to themselves. -""") -eg(""" - from reportlab.lib.styles import getSampleStyleSheet - from reportlab.platypus import Paragraph - from reportlab.pdfgen.canvas import Canvas - styleSheet = getSampleStyleSheet() - style = styleSheet['BodyText'] - P=Paragraph('This is a very silly example',style) - canv = Canvas('doc.pdf') - aW = 460 # available width and height - aH = 800 - w,h = P.wrap(aW, aH) # find required space - if w<=aW and h<=aH: - P.drawOn(canv,0,aH) - aH = aH - h # reduce the available height - canv.save() - else: - raise ValueError, "Not enough room" -""") -endKeep(k) -heading3("$Flowable$ User Methods") -eg(""" - Flowable.draw() -""") -disc("""This will be called to ask the flowable to actually render itself. -The $Flowable$ class does not implement $draw$. -The calling code should ensure that the flowable has an attribute $canv$ -which is the $pdfgen.Canvas$ which should be drawn to an that the $Canvas$ -is in an appropriate state (as regards translations rotations, etc). Normally -this method will only be called internally by the $drawOn$ method. Derived classes -must implement this method. -""") -eg(""" - Flowable.drawOn(canvas,x,y) -""") -disc(""" -This is the method which controlling programs use to render the flowable to a particular -canvas. It handles the translation to the canvas coordinate (x,y) and ensuring that -the flowable has a $canv$ attribute so that the -$draw$ method (which is not implemented in the base class) can render in an -absolute coordinate frame. -""") -eg(""" - Flowable.wrap(availWidth, availHeight) -""") -disc("""This will be called by the enclosing frame before objects -are asked their size, drawn or whatever. It returns the -size actually used.""") -eg(""" - Flowable.split(self, availWidth, availheight): -""") -disc("""This will be called by more sophisticated frames when - wrap fails. Stupid flowables should return [] meaning that they are unable to split. -Clever flowables should split themselves and return a list of flowables. It is up to -the client code to ensure that repeated attempts to split are avoided. -If the space is sufficient the split method should return [self]. -Otherwise -the flowable should rearrange itself and return a list $[f0,...]$ of flowables -which will be considered in order. The implemented split method should avoid -changing $self$ as this will allow sophisticated layout mechanisms to do multiple -passes over a list of flowables. -""") - -heading2("Guidelines for flowable positioning") - -disc("""Two methods, which by default return zero, provide guidance on vertical -spacing of flowables: -""") - -eg(""" - Flowable.getSpaceAfter(self): - Flowable.getSpaceBefore(self): -""") -disc("""These methods return how much space should follow or precede -the flowable. The space doesn't belong to the flowable itself i.e. the flowable's -$draw$ method shouldn't consider it when rendering. Controlling programs -will use the values returned in determining how much space is required by -a particular flowable in context. -""") - -disc("""All flowables have an $hAlign$ property: $('LEFT', 'RIGHT', 'CENTER' or 'CENTRE')$. -For paragraphs, which fill the full width of the frame, this has no effect. For tables, -images or other objects which are less than the width of the frame, this determines their -horizontal placement. - -""") - - -disc("""The chapters which follow will cover the most important -specific types of flowables: Paragraphs and Tables.""") - - -heading2("Frames") -disc(""" -$Frames$ are active containers which are themselves contained in $PageTemplates$. -$Frames$ have a location and size and maintain a concept of remaining drawable -space. The command -""") - -eg(""" - Frame(x1, y1, width,height, leftPadding=6, bottomPadding=6, - rightPadding=6, topPadding=6, id=None, showBoundary=0) -""") -disc("""creates a $Frame$ instance with lower left hand corner at coordinate $(x1,y1)$ -(relative to the canvas at use time) and with dimensions $width$ x $height$. The $Padding$ -arguments are positive quantities used to reduce the space available for drawing. -The $id$ argument is an identifier for use at runtime e.g. 'LeftColumn' or 'RightColumn' etc. -If the $showBoundary$ argument is non-zero then the boundary of the frame will get drawn -at run time (this is useful sometimes). -""") -heading3("$Frame$ User Methods") -eg(""" - Frame.addFromList(drawlist, canvas) -""") -disc("""consumes $Flowables$ from the front of $drawlist$ until the - frame is full. If it cannot fit one object, raises - an exception.""") - -eg(""" - Frame.split(flowable,canv) -""") -disc('''Asks the flowable to split using up the available space and return -the list of flowables. -''') - -eg(""" - Frame.drawBoundary(canvas) -""") -disc("draws the frame boundary as a rectangle (primarily for debugging).") -heading3("Using $Frames$") -disc(""" -$Frames$ can be used directly with canvases and flowables to create documents. -The $Frame.addFromList$ method handles the $wrap$ & $drawOn$ calls for you. -You don't need all of the Platypus machinery to get something useful into -PDF. -""") -eg(""" -from reportlab.pdfgen.canvas import Canvas -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib.units import inch -from reportlab.platypus import Paragraph, Frame -styles = getSampleStyleSheet() -styleN = styles['Normal'] -styleH = styles['Heading1'] -story = [] - -#add some flowables -story.append(Paragraph("This is a Heading",styleH)) -story.append(Paragraph("This is a paragraph in Normal style.", - styleN)) -c = Canvas('mydoc.pdf') -f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1) -f.addFromList(story,c) -c.save() -""") - -heading2("Documents and Templates") - -disc(""" -The $BaseDocTemplate$ class implements the basic machinery for document -formatting. An instance of the class contains a list of one or more -$PageTemplates$ that can be used to describe the layout of information -on a single page. The $build$ method can be used to process -a list of $Flowables$ to produce a PDF document. -""") - -CPage(3.0) -heading3("The $BaseDocTemplate$ class") - -eg(""" - BaseDocTemplate(self, filename, - pagesize=defaultPageSize, - pageTemplates=[], - showBoundary=0, - leftMargin=inch, - rightMargin=inch, - topMargin=inch, - bottomMargin=inch, - allowSplitting=1, - title=None, - author=None, - _pageBreakQuick=1) -""") - -disc(""" -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 PDF document; alternatively it -can be an object which has a $write$ method such as a $StringIO$ or $file$ or $socket$. -""") - -disc(""" -The allowed arguments should be self explanatory, but $showBoundary$ controls whether or -not $Frame$ boundaries are drawn which can be useful for debugging purposes. The -$allowSplitting$ argument determines whether the builtin methods should try to split -individual $Flowables$ across $Frames$. The $_pageBreakQuick$ argument determines whether -an attempt to do a page break should try to end all the frames on the page or not, before ending -the page. -""") - -heading4("User $BaseDocTemplate$ Methods") - -disc("""These are of direct interest to client programmers -in that they are normally expected to be used. -""") -eg(""" - BaseDocTemplate.addPageTemplates(self,pageTemplates) -""") -disc(""" -This method is used to add one or a list of $PageTemplates$ to an existing documents. -""") -eg(""" - BaseDocTemplate.build(self, flowables, filename=None, canvasmaker=canvas.Canvas) -""") -disc(""" -This is the main method which is of interest to the application -programmer. Assuming that the document instance is correctly set up the -$build$ method takes the story in the shape of the list of flowables -(the $flowables$ argument) and loops through the list forcing the flowables -one at a time through the formatting machinery. Effectively this causes -the $BaseDocTemplate$ instance to issue calls to the instance $handle_XXX$ methods -to process the various events. -""") -heading4("User Virtual $BaseDocTemplate$ Methods") -disc(""" -These have no semantics at all in the base class. They are intended as pure virtual hooks -into the layout machinery. Creators of immediately derived classes can override these -without worrying about affecting the properties of the layout engine. -""") -eg(""" - BaseDocTemplate.afterInit(self) -""") -disc(""" -This is called after initialisation of the base class; a derived class could overide -the method to add default $PageTemplates$. -""") - -eg(""" - BaseDocTemplate.afterPage(self) -""") -disc("""This is called after page processing, and - immediately after the afterDrawPage method - of the current page template. A derived class could -use this to do things which are dependent on information in the page -such as the first and last word on the page of a dictionary. -""") - -eg(""" - BaseDocTemplate.beforeDocument(self) -""") - -disc("""This is called before any processing is -done on the document, but after the processing machinery -is ready. It can therefore be used to do things to the instance\'s -$pdfgen.canvas$ and the like. -""") - -eg(""" - BaseDocTemplate.beforePage(self) -""") - -disc("""This is called at the beginning of page - processing, and immediately before the - beforeDrawPage method of the current page - template. It could be used to reset page specific - information holders.""") - -eg(""" - BaseDocTemplate.filterFlowables(self,flowables) -""") - -disc("""This is called to filter flowables at the start of the main handle_flowable method. - Upon return if flowables[0] has been set to None it is discarded and the main - method returns immediately. - """) - -eg(""" - BaseDocTemplate.afterFlowable(self, flowable) -""") - -disc("""Called after a flowable has been rendered. An interested class could use this -hook to gather information about what information is present on a particular page or frame.""") - -heading4("$BaseDocTemplate$ Event handler Methods") -disc(""" -These methods constitute the greater part of the layout engine. Programmers shouldn't -have to call or override these methods directly unless they are trying to modify the layout engine. -Of course, the experienced programmer who wants to intervene at a particular event, $XXX$, -which does not correspond to one of the virtual methods can always override and -call the base method from the drived class version. We make this easy by providing -a base class synonym for each of the handler methods with the same name prefixed by an underscore '_'. -""") - -eg(""" - def handle_pageBegin(self): - doStuff() - BaseDocTemplate.handle_pageBegin(self) - doMoreStuff() - - #using the synonym - def handle_pageEnd(self): - doStuff() - self._handle_pageEnd() - doMoreStuff() -""") -disc(""" -Here we list the methods only as an indication of the events that are being -handled. -Interested programmers can take a look at the source. -""") -eg(""" - handle_currentFrame(self,fx) - handle_documentBegin(self) - handle_flowable(self,flowables) - handle_frameBegin(self,*args) - handle_frameEnd(self) - handle_nextFrame(self,fx) - handle_nextPageTemplate(self,pt) - handle_pageBegin(self) - handle_pageBreak(self) - handle_pageEnd(self) -""") - -disc(""" -Using document templates can be very easy; $SimpleDoctemplate$ is a class derived from -$BaseDocTemplate$ which provides its own $PageTemplate$ and $Frame$ setup. -""") - -eg(""" -from reportlab.lib.styles import getSampleStyleSheet -from reportlab.lib.pagesizes import letter -from reportlab.platypus import Paragraph, SimpleDocTemplate -styles = getSampleStyleSheet() -styleN = styles['Normal'] -styleH = styles['Heading1'] -story = [] - -#add some flowables -story.append(Paragraph("This is a Heading",styleH)) -story.append(Paragraph("This is a paragraph in Normal style.", - styleN)) -doc = SimpleDocTemplate('mydoc.pdf',pagesize = letter) -doc.build(story) -""") -heading3("$PageTemplates$") -disc(""" -The $PageTemplate$ class is a container class with fairly minimal semantics. Each instance -contains a list of $Frames$ and has methods which should be called at the start and end -of each page. -""") -eg("PageTemplate(id=None,frames=[],onPage=_doNothing,onPageEnd=_doNothing)") -disc(""" -is used to initialize an instance, the $frames$ argument should be a list of $Frames$ -whilst the optional $onPage$ and $onPageEnd$ arguments are callables which should have signature -$def XXX(canvas,document)$ where $canvas$ and $document$ -are the canvas and document being drawn. These routines are intended to be used to paint non-flowing (i.e. standard) -parts of pages. These attribute functions are exactly parallel to the pure virtual methods -$PageTemplate.beforPage$ and $PageTemplate.afterPage$ which have signature -$beforPage(self,canvas,document)$. The methods allow class derivation to be used to define -standard behaviour, whilst the attributes allow instance changes. The $id$ argument is used at -run time to perform $PageTemplate$ switching so $id='FirstPage'$ or $id='TwoColumns'$ are typical. -""") diff --git a/bin/reportlab/docs/userguide/ch5_paragraphs.py b/bin/reportlab/docs/userguide/ch5_paragraphs.py deleted file mode 100644 index 3191f1f1d55..00000000000 --- a/bin/reportlab/docs/userguide/ch5_paragraphs.py +++ /dev/null @@ -1,319 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch5_paragraphs.py -from reportlab.tools.docco.rl_doc_utils import * - -#begin chapter oon paragraphs -heading1("Paragraphs") -disc(""" -The $reportlab.platypus.Paragraph$ class is one of the most useful of the Platypus $Flowables$; -it can format fairly arbitrary text and provides for inline font style and colour changes using -an XML style markup. The overall shape of the formatted text can be justified, right or left ragged -or centered. The XML markup can even be used to insert greek characters or to do subscripts. -""") -disc("""The following text creates an instance of the $Paragraph$ class:""") -eg("""Paragraph(text, style, bulletText=None)""") -disc("""The $text$ argument contains the text of the -paragraph; excess white space is removed from the text at the ends and internally after -linefeeds. This allows easy use of indented triple quoted text in Python scripts. -The $bulletText$ argument provides the text of a default bullet for the paragraph. -The font and other properties for the paragraph text and bullet are set using the style argument. -""") -disc(""" -The $style$ argument should be an instance of class $ParagraphStyle$ obtained typically -using""") -eg(""" -from reportlab.lib.styles import ParagraphStyle -""") -disc(""" -this container class provides for the setting of multiple default paragraph attributes -in a structured way. The styles are arranged in a dictionary style object called a $stylesheet$ -which allows for the styles to be accessed as $stylesheet['BodyText']$. A sample style -sheet is provided. -""") -eg(""" -from reportlab.lib.styles import getSampleStyleSheet -stylesheet=getSampleStyleSheet() -normalStyle = stylesheet['Normal'] -""") -disc(""" -The options which can be set for a $Paragraph$ can be seen from the $ParagraphStyle$ defaults. -""") -heading4("$class ParagraphStyle$") -eg(""" -class ParagraphStyle(PropertySet): - defaults = { - 'fontName':'Times-Roman', - 'fontSize':10, - 'leading':12, - 'leftIndent':0, - 'rightIndent':0, - 'firstLineIndent':0, - 'alignment':TA_LEFT, - 'spaceBefore':0, - 'spaceAfter':0, - 'bulletFontName':'Times-Roman', - 'bulletFontSize':10, - 'bulletIndent':0, - 'textColor': black - } -""") - -heading2("Using Paragraph Styles") - -#this will be used in the ParaBox demos. -sample = """You are hereby charged that on the 28th day of May, 1970, you did -willfully, unlawfully, and with malice of forethought, publish an -alleged English-Hungarian phrase book with intent to cause a breach -of the peace. How do you plead?""" - - -disc("""The $Paragraph$ and $ParagraphStyle$ classes together -handle most common formatting needs. The following examples -draw paragraphs in various styles, and add a bounding box -so that you can see exactly what space is taken up.""") - -s1 = ParagraphStyle('Normal') -parabox(sample, s1, 'The default $ParagraphStyle$') - -disc("""The two attributes $spaceBefore$ and $spaceAfter$ do what they -say, except at the top or bottom of a frame. At the top of a frame, -$spaceBefore$ is ignored, and at the bottom, $spaceAfter$ is ignored. -This means that you could specify that a 'Heading2' style had two -inches of space before when it occurs in mid-page, but will not -get acres of whitespace at the top of a page. These two attributes -should be thought of as 'requests' to the Frame and are not part -of the space occupied by the Paragraph itself.""") - -disc("""The $fontSize$ and $fontName$ tags are obvious, but it is -important to set the $leading$. This is the spacing between -adjacent lines of text; a good rule of thumb is to make this -20% larger than the point size. To get double-spaced text, -use a high $leading$.""") - -disc("""The figure below shows space before and after and an -increased leading:""") - -parabox(sample, - ParagraphStyle('Spaced', - spaceBefore=6, - spaceAfter=6, - leading=16), - 'Space before and after and increased leading' - ) - -disc("""The $leftIndent$ and $rightIndent$ attributes do exactly -what you would expect; $firstLineIndent$ is added to the $leftIndent$ of the -first line. If you want a straight left edge, remember -to set $firstLineIndent$ equal to 0.""") - -parabox(sample, - ParagraphStyle('indented', - firstLineIndent=+24, - leftIndent=24, - rightIndent=24), - 'one third inch indents at left and right, two thirds on first line' - ) - -disc("""Setting $firstLineIndent$ equal to a negative number, $leftIndent$ -much higher, and using a -different font (we'll show you how later!) can give you a -definition list:.""") - -parabox('Judge Pickles: ' + sample, - ParagraphStyle('dl', - leftIndent=36), - 'Definition Lists' - ) - -disc("""There are four possible values of $alignment$, defined as -constants in the module reportlab.lib.enums. These are -TA_LEFT, TA_CENTER or TA_CENTRE, TA_RIGHT and -TA_JUSTIFY, with values of 0, 1, 2 and 4 respectively. These -do exactly what you would expect.""") - - -heading2("Paragraph XML Markup Tags") -disc("""XML markup can be used to modify or specify the -overall paragraph style, and also to specify intra- -paragraph markup.""") - -heading3("The outermost < para > tag") - - -disc(""" -The paragraph text may optionally be surrounded by -<para attributes....> -</para> -tags. The attributes if any of the opening <para> tag affect the style that is used -with the $Paragraph$ $text$ and/or $bulletText$. -""") -disc(" ") - -from reportlab.platypus.paraparser import _addAttributeNames, _paraAttrMap, _bulletAttrMap - -def getAttrs(A): - _addAttributeNames(A) - S={} - for k, v in A.items(): - a = v[0] - if not S.has_key(a): - S[a] = k - else: - S[a] = "%s, %s" %(S[a],k) - - K = S.keys() - K.sort() - D=[('Attribute','Synonyms')] - for k in K: - D.append((k,S[k])) - cols=2*[None] - rows=len(D)*[None] - return D,cols,rows - -t=apply(Table,getAttrs(_paraAttrMap)) -t.setStyle(TableStyle([ - ('FONT',(0,0),(-1,1),'Times-Bold',10,12), - ('FONT',(0,1),(-1,-1),'Courier',8,8), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -getStory().append(t) -caption("""Table - Synonyms for style attributes""") - -disc("""Some useful synonyms have been provided for our Python attribute -names, including lowercase versions, and the equivalent properties -from the HTML standard where they exist. These additions make -it much easier to build XML-printing applications, since -much intra-paragraph markup may not need translating. The -table below shows the allowed attributes and synonyms in the -outermost paragraph tag.""") - - -heading2("Intra-paragraph markup") -disc("""'...) -and italic (...). It is also legal to use an underline -tag (... but it has no effect; PostScript fonts don't -support underlining, and neither do we, yet.]]>""") - -parabox2("""You are hereby charged that on the 28th day of May, 1970, you did -willfully, unlawfully, and with malice of forethought, publish an -alleged English-Hungarian phrase book with intent to cause a breach -of the peace. How do you plead?""", "Simple bold and italic tags") - -heading3("The $<font>$ tag") -disc("""The $<font>$ tag can be used to change the font name, -size and text color for any substring within the paragraph. -Legal attributes are $size$, $face$, $name$ (which is the same as $face$), -$color$, and $fg$ (which is the same as $color$). The $name$ is -the font family name, without any 'bold' or 'italic' suffixes. -Colors may be -HTML color names or a hex string encoded in a variety of ways; -see ^reportlab.lib.colors^ for the formats allowed.""") - -parabox2(""" -You are hereby charged that on the 28th day of May, 1970, you did -willfully, unlawfully, and with malice of forethought, -publish an -alleged English-Hungarian phrase book with intent to cause a breach -of the peace. How do you plead?""", "The $font$ tag") - -heading3("Superscripts and Subscripts") -disc("""Superscripts and subscripts are supported with the - and tags, which work exactly -as you might expect. In addition, most greek letters -can be accessed by using the -tag, or with mathML entity names.]]>""") - -##parabox2("""epsiloniota -##pi = -1""", "Greek letters and subscripts") - -parabox2("""Equation (α): e ip = -1""", - "Greek letters and superscripts") - -heading3("Numbering Paragraphs and Lists") -disc("""The $<seq>$ tag provides comprehensive support -for numbering lists, chapter headings and so on. It acts as -an interface to the $Sequencer$ class in ^reportlab.lib.sequencer^. -These are used to number headings and figures throughout this -document. -You may create as many separate 'counters' as you wish, accessed -with the $id$ attribute; these will be incremented by one each -time they are accessed. The $seqreset$ tag resets a counter. -If you want it to resume from a number other than 1, use -the syntax <seqreset id="mycounter" base="42">. -Let's have a go:""") - -parabox2(""", , . -Reset. , , -.""", "Basic sequences") - -disc("""You can save specifying an ID by designating a counter ID -as the default using the <seqdefault id="Counter"> -tag; it will then be used whenever a counter ID -is not specified. This saves some typing, especially when -doing multi-level lists; you just change counter ID when -stepping in or out a level.""") - -parabox2("""Continued... , -, , , , , .""", -"The default sequence") - -disc("""Finally, one can access multi-level sequences using -a variation of Python string formatting and the $template$ -attribute in a <seq> tags. This is used to do the -captions in all of the figures, as well as the level two -headings. The substring $%(counter)s$ extracts the current -value of a counter without incrementing it; appending a -plus sign as in $%(counter)s$ increments the counter. -The figure captions use a pattern like the one below:""") - -parabox2("""Figure - Multi-level templates""", -"Multi-level templates") - -disc("""We cheated a little - the real document used 'Figure', -but the text above uses 'FigureNo' - otherwise we would have -messed up our numbering!""") - -heading2("Bullets and Paragraph Numbering") -disc("""In addition to the three indent properties, some other -parameters are needed to correctly handle bulleted and numbered -lists. We discuss this here because you have now seen how -to handle numbering. A paragraph may have an optional -^bulletText^ argument passed to its constructor; alternatively, -bullet text may be placed in a $..]]>$ -tag at its head. The text will be drawn on the first line of -the paragraph, with its x origin determined by the $bulletIndent$ -attribute of the style, and in the font given in the -$bulletFontName$ attribute. For genuine bullets, a good -idea is to select the Times-Roman font in the style, and -use a character such as $\\xe2\\x80\\xa2)$:""") - -t=apply(Table,getAttrs(_bulletAttrMap)) -t.setStyle([ - ('FONT',(0,0),(-1,1),'Times-Bold',10,12), - ('FONT',(0,1),(-1,-1),'Courier',8,8), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ]) -getStory().append(t) - -caption("""Table - <bullet> attributes & synonyms""") -disc("""The <bullet> tag is only allowed once in a given paragraph and its use -overrides the implied bullet style and ^bulletText^ specified in the ^Paragraph^ -creation. -""") -parabox("""\xe2\x80\xa2this 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'], - 'Basic use of bullet points') - -disc("""Exactly the same technique is used for numbers, -except that a sequence tag is used. It is also possible -to put a multi-character string in the bullet; with a deep -indent and bold bullet font, you can make a compact -definition list.""") diff --git a/bin/reportlab/docs/userguide/ch6_tables.py b/bin/reportlab/docs/userguide/ch6_tables.py deleted file mode 100644 index 011423367ba..00000000000 --- a/bin/reportlab/docs/userguide/ch6_tables.py +++ /dev/null @@ -1,419 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch6_tables.py -from reportlab.tools.docco.rl_doc_utils import * -from reportlab.platypus import Image -import reportlab - -heading1("Tables and TableStyles") -disc(""" -The $Table$ and $LongTable$ classes derive from the $Flowable$ class and are intended -as a simple textual gridding mechanisms. The $longTable$ class uses a greedy algorithm -when calculating column widths and is intended for long tables where speed counts. -$Table$ cells can hold anything which can be converted to -a Python $string$ or $Flowables$ (or lists of $Flowables$). -""") - -disc(""" -Our present tables are a trade-off between efficient drawing and specification -and functionality. We assume the reader has some familiarity with HTML tables. -In brief, they have the following characteristics: -""") - -bullet("""They can contain anything convertible to a string; flowable -objects such as other tables; or entire sub-stories""") - -bullet("""They can work out the row heights to fit the data if you don't supply -the row height. (They can also work out the widths, but generally it is better -for a designer to set the width manually, and it draws faster).""") - -bullet("""They can split across pages if needed (see the canSplit attribute). -You can specify that a number of rows at the top and bottom should be -repeated after the split (e.g. show the headers again on page 2,3,4...)""") - -bullet("""For very wide tables, they can also split 'by column'. You can choose -whether tou want to split down-and-across or across-and-down""") - -bullet("""They have a simple and powerful notation for specifying shading and -gridlines which works well with financial or database tables, where you -don't know the number of rows up front. You can easily say 'make the last row -bold and put a line above it'""") - -bullet("""The style and data are separated, so you can declare a handful of table -styles and use them for a family of reports. Styes can also 'inherit', as with -paragraphs.""") - -disc("""There is however one main limitation compared to an HTML table. -They define a simple rectangular grid. There is no simple row or column -spanning; if you need to span cells, you must nest tables inside table cells instead or use a more -complex scheme in which the lead cell of a span contains the actual contents.""") - -disc(""" -$Tables$ are created by passing the constructor an optional sequence of column widths, -an optional sequence of row heights, and the data in row order. -Drawing of the table can be controlled by using a $TableStyle$ instance. This allows control of the -color and weight of the lines (if any), and the font, alignment and padding of the text. -A primitive automatic row height and or column width calculation mechanism is provided for. -""") - -heading2('$Table$ User Methods') -disc("""These are the main methods which are of interest to the client programmer.""") - -heading4("""$Table(data, colWidths=None, rowHeights=None, style=None, splitByRow=1, -repeatRows=0, repeatCols=0)$""") - -disc("""The $data$ argument is a sequence of sequences of cell values each of which -should be convertible to a string value using the $str$ function or should be a Flowable instance (such as a $Paragraph$) or a list (or tuple) of such instances. -If a cell value is a $Flowable$ or list of $Flowables$ these must either have a determined width -or the containing column must have a fixed width. -The first row of cell values -is in $data[0]$ i.e. the values are in row order. The $i$, $j$th. cell value is in -$data[i][j]$. Newline characters $'\\n'$ in cell values are treated as line split characters and -are used at draw time to format the cell into lines. -""") -disc("""The other arguments are fairly obvious, the $colWidths$ argument is a sequence -of numbers or possibly $None$, representing the widths of the columns. The number of elements -in $colWidths$ determines the number of columns in the table. -A value of $None$ means that the corresponding column width should be calculated automatically.""") - -disc("""The $rowHeights$ argument is a sequence -of numbers or possibly $None$, representing the heights of the rows. The number of elements -in $rowHeights$ determines the number of rows in the table. -A value of $None$ means that the corresponding row height should be calculated automatically.""") - -disc("""The $style$ argument can be an initial style for the table.""") -disc("""The $splitByRow$ argument is only needed for tables both too tall and too wide -to fit in the current context. In this case you must decide whether to 'tile' -down and across, or across and then down. This parameter is a Boolean indicating that the -$Table$ should split itself -by row before attempting to split itself by column when too little space is available in -the current drawing area and the caller wants the $Table$ to split.""") - -disc("""The $repeatRows$ and $repeatCols$ arguments specify the number of leading rows and columns -that should be repeated when the $Table$ is asked to split itself.""") -heading4('$Table.setStyle(tblStyle)$') -disc(""" -This method applies a particular instance of class $TableStyle$ (discussed below) -to the $Table$ instance. This is the only way to get $tables$ to appear -in a nicely formatted way. -""") -disc(""" -Successive uses of the $setStyle$ method apply the styles in an additive fashion. -That is, later applications override earlier ones where they overlap. -""") - -heading2('$TableStyle$') -disc(""" -This class is created by passing it a sequence of commands, each command -is a tuple identified by its first element which is a string; the remaining -elements of the command tuple represent the start and stop cell coordinates -of the command and possibly thickness and colors, etc. -""") -heading2("$TableStyle$ User Methods") -heading3("$TableStyle(commandSequence)$") -disc("""The creation method initializes the $TableStyle$ with the argument -command sequence as an example:""") -eg(""" - LIST_STYLE = TableStyle( - [('LINEABOVE', (0,0), (-1,0), 2, colors.green), - ('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black), - ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green), - ('ALIGN', (1,1), (-1,-1), 'RIGHT')] - ) -""") -heading3("$TableStyle.add(commandSequence)$") -disc("""This method allows you to add commands to an existing -$TableStyle$, i.e. you can build up $TableStyles$ in multiple statements. -""") -eg(""" - LIST_STYLE.add('BACKGROUND', (0,0), (-1,0), colors.Color(0,0.7,0.7)) -""") -heading3("$TableStyle.getCommands()$") -disc("""This method returns the sequence of commands of the instance.""") -eg(""" - cmds = LIST_STYLE.getCommands() -""") -heading2("$TableStyle$ Commands") -disc("""The commands passed to $TableStyles$ come in three main groups -which affect the table background, draw lines, or set cell styles. -""") -disc("""The first element of each command is its identifier, -the second and third arguments determine the cell coordinates of -the box of cells which are affected with negative coordinates -counting backwards from the limit values as in Python -indexing. The coordinates are given as -(column, row) which follows the spreadsheet 'A1' model, but not -the more natural (for mathematicians) 'RC' ordering. -The top left cell is (0, 0) the bottom right is (-1, -1). Depending on -the command various extra (???) occur at indices beginning at 3 on. -""") -heading3("""$TableStyle$ Cell Formatting Commands""") -disc("""The cell formatting commands all begin with an identifier, followed by -the start and stop cell definitions and the perhaps other arguments. -the cell formatting commands are:""") -eg(""" -FONT - takes fontname, optional fontsize and optional leading. -FONTNAME (or FACE) - takes fontname. -FONTSIZE (or SIZE) - takes fontsize in points; leading may get out of sync. -LEADING - takes leading in points. -TEXTCOLOR - takes a color name or (R,G,B) tuple. -ALIGNMENT (or ALIGN) - takes one of LEFT, RIGHT and CENTRE (or CENTER) or DECIMAL. -LEFTPADDING - takes an integer, defaults to 6. -RIGHTPADDING - takes an integer, defaults to 6. -BOTTOMPADDING - takes an integer, defaults to 3. -TOPPADDING - takes an integer, defaults to 3. -BACKGROUND - takes a color. -ROWBACKGROUNDS - takes a list of colors to be used cyclically. -COLBACKGROUNDS - takes a list of colors to be used cyclically. -VALIGN - takes one of TOP, MIDDLE or the default BOTTOM -""") -disc("""This sets the background cell color in the relevant cells. -The following example shows the $BACKGROUND$, and $TEXTCOLOR$ commands in action:""") -EmbeddedCode(""" -data= [['00', '01', '02', '03', '04'], - ['10', '11', '12', '13', '14'], - ['20', '21', '22', '23', '24'], - ['30', '31', '32', '33', '34']] -t=Table(data) -t.setStyle(TableStyle([('BACKGROUND',(1,1),(-2,-2),colors.green), - ('TEXTCOLOR',(0,0),(1,-1),colors.red)])) -""") -disc("""To see the effects of the alignment styles we need some widths -and a grid, but it should be easy to see where the styles come from.""") -EmbeddedCode(""" -data= [['00', '01', '02', '03', '04'], - ['10', '11', '12', '13', '14'], - ['20', '21', '22', '23', '24'], - ['30', '31', '32', '33', '34']] -t=Table(data,5*[0.4*inch], 4*[0.4*inch]) -t.setStyle(TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'), - ('TEXTCOLOR',(1,1),(-2,-2),colors.red), - ('VALIGN',(0,0),(0,-1),'TOP'), - ('TEXTCOLOR',(0,0),(0,-1),colors.blue), - ('ALIGN',(0,-1),(-1,-1),'CENTER'), - ('VALIGN',(0,-1),(-1,-1),'MIDDLE'), - ('TEXTCOLOR',(0,-1),(-1,-1),colors.green), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) -""") -heading3("""$TableStyle$ Line Commands""") -disc(""" - Line commands begin with the identifier, the start and stop cell coordinates - and always follow this with the thickness (in points) and color of the desired lines. Colors can be names, - or they can be specified as a (R, G, B) tuple, where R, G and B are floats and (0, 0, 0) is black. The line - command names are: GRID, BOX, OUTLINE, INNERGRID, LINEBELOW, LINEABOVE, LINEBEFORE - and LINEAFTER. BOX and OUTLINE are equivalent, and GRID is the equivalent of applying both BOX and - INNERGRID. -""") -CPage(4.0) -disc("""We can see some line commands in action with the following example. -""") -EmbeddedCode(""" -data= [['00', '01', '02', '03', '04'], - ['10', '11', '12', '13', '14'], - ['20', '21', '22', '23', '24'], - ['30', '31', '32', '33', '34']] -t=Table(data,style=[('GRID',(1,1),(-2,-2),1,colors.green), - ('BOX',(0,0),(1,-1),2,colors.red), - ('LINEABOVE',(1,2),(-2,2),1,colors.blue), - ('LINEBEFORE',(2,1),(2,-2),1,colors.pink), - ]) -""") -disc("""Line commands cause problems for tables when they split; the following example -shows a table being split in various positions""") -EmbeddedCode(""" -data= [['00', '01', '02', '03', '04'], - ['10', '11', '12', '13', '14'], - ['20', '21', '22', '23', '24'], - ['30', '31', '32', '33', '34']] -t=Table(data,style=[ - ('GRID',(0,0),(-1,-1),0.5,colors.grey), - ('GRID',(1,1),(-2,-2),1,colors.green), - ('BOX',(0,0),(1,-1),2,colors.red), - ('BOX',(0,0),(-1,-1),2,colors.black), - ('LINEABOVE',(1,2),(-2,2),1,colors.blue), - ('LINEBEFORE',(2,1),(2,-2),1,colors.pink), - ('BACKGROUND', (0, 0), (0, 1), colors.pink), - ('BACKGROUND', (1, 1), (1, 2), colors.lavender), - ('BACKGROUND', (2, 2), (2, 3), colors.orange), - ]) -""") -t=getStory()[-1] -getStory().append(Spacer(0,6)) -for s in t.split(4*inch,30): - getStory().append(s) - getStory().append(Spacer(0,6)) -getStory().append(Spacer(0,6)) -for s in t.split(4*inch,36): - getStory().append(s) - getStory().append(Spacer(0,6)) - -disc("""When unsplit and split at the first or second row.""") - -CPage(4.0) -heading3("""Complex Cell Values""") -disc(""" -As mentioned above we can have complicated cell values including $Paragraphs$, $Images$ and other $Flowables$ -or lists of the same. To see this in operation consider the following code and the table it produces. -Note that the $Image$ has a white background which will obscure any background you choose for the cell. -To get better results you should use a transparent background. -""") -import os, reportlab.platypus -I = '../images/replogo.gif' -EmbeddedCode(""" -I = Image('%s') -I.drawHeight = 1.25*inch*I.drawHeight / I.drawWidth -I.drawWidth = 1.25*inch -P0 = Paragraph(''' - A paragraph - 1''', - styleSheet["BodyText"]) -P = Paragraph(''' - The ReportLab Left - Logo - Image''', - styleSheet["BodyText"]) -data= [['A', 'B', 'C', P0, 'D'], - ['00', '01', '02', [I,P], '04'], - ['10', '11', '12', [P,I], '14'], - ['20', '21', '22', '23', '24'], - ['30', '31', '32', '33', '34']] - -t=Table(data,style=[('GRID',(1,1),(-2,-2),1,colors.green), - ('BOX',(0,0),(1,-1),2,colors.red), - ('LINEABOVE',(1,2),(-2,2),1,colors.blue), - ('LINEBEFORE',(2,1),(2,-2),1,colors.pink), - ('BACKGROUND', (0, 0), (0, 1), colors.pink), - ('BACKGROUND', (1, 1), (1, 2), colors.lavender), - ('BACKGROUND', (2, 2), (2, 3), colors.orange), - ('BOX',(0,0),(-1,-1),2,colors.black), - ('GRID',(0,0),(-1,-1),0.5,colors.black), - ('VALIGN',(3,0),(3,0),'BOTTOM'), - ('BACKGROUND',(3,0),(3,0),colors.limegreen), - ('BACKGROUND',(3,1),(3,1),colors.khaki), - ('ALIGN',(3,1),(3,1),'CENTER'), - ('BACKGROUND',(3,2),(3,2),colors.beige), - ('ALIGN',(3,2),(3,2),'LEFT'), - ]) - -t._argW[3]=1.5*inch -"""%I) -heading3("""$TableStyle$ Span Commands""") -disc("""Our $Table$ classes support the concept of spanning, but it isn't specified in the same way -as html. The style specification -""") -eg(""" -SPAN, (sc,sr), (ec,er) -""") -disc("""indicates that the cells in columns $sc$ - $ec$ and rows $sr$ - $er$ should be combined into a super cell -with contents determined by the cell $(sc, sr)$. The other cells should be present, but should contain empty strings -or you may get unexpected results. -""") -EmbeddedCode(""" -data= [['Top\\nLeft', '', '02', '03', '04'], - ['', '', '12', '13', '14'], - ['20', '21', '22', 'Bottom\\nRight', ''], - ['30', '31', '32', '', '']] -t=Table(data,style=[ - ('GRID',(0,0),(-1,-1),0.5,colors.grey), - ('BACKGROUND',(0,0),(1,1),colors.palegreen), - ('SPAN',(0,0),(1,1)), - ('BACKGROUND',(-2,-2),(-1,-1), colors.pink), - ('SPAN',(-2,-2),(-1,-1)), - ]) -""") - -disc("""notice that we don't need to be conservative with our $GRID$ command. The spanned cells are not drawn through. -""") -heading3("""Special $TableStyle$ Indeces""") -disc("""In any style command the first row index may be set to one of the special strings -$'splitlast'$ or $'splitfirst'$ to indicate that the style should be used only for the last row of -a split table, or the first row of a continuation. This allows splitting tables with nicer effects around the split.""") - -heading1("""Other Useful $Flowables$""") -heading2("""$Preformatted(text, style, bulletText = None, dedent=0)$""") -disc(""" -Creates a preformatted paragraph which does no wrapping, line splitting or other manipulations. -No $XML$ style tags are taken account of in the text. -If dedent is non zero $dedent$ common leading -spaces will be removed from the front of each line. -""") -heading2("""$XPreformatted(text, style, bulletText = None, dedent=0, frags=None)$""") -disc(""" -This is a non rearranging form of the $Paragraph$ class; $XML$ tags are allowed in -$text$ and have the same meanings as for the $Paragraph$ class. -As for $Preformatted$, if dedent is non zero $dedent$ common leading -spaces will be removed from the front of each line. -""") -EmbeddedCode(""" -from reportlab.lib.styles import getSampleStyleSheet -stylesheet=getSampleStyleSheet() -normalStyle = stylesheet['Normal'] -text=''' - - This is a non rearranging form of the Paragraph class; - XML tags are allowed in text and have the same - - meanings as for the Paragraph class. - As for Preformatted, if dedent is non zero dedent - common leading spaces will be removed from the - front of each line. - You can have &amp; style entities as well for & < > and ". - -''' -t=XPreformatted(text,normalStyle,dedent=3) -""") -heading2("""$Image(filename, width=None, height=None)$""") -disc("""Create a flowable which will contain the image defined by the data in file $filename$. -The default PDF image type jpeg is supported and if the PIL extension to Python -is installed the other image types can also be handled. If $width$ and or $height$ are specified -then they determine the dimension of the displayed image in points. If either dimension is -not specified (or specified as $None$) then the corresponding pixel dimension of the image is assumed -to be in points and used. -""") -I=os.path.join(os.path.dirname(reportlab.__file__),'docs','images','lj8100.jpg') -eg(""" -Image("lj8100.jpg") -""",after=0.1) -disc("""will display as""") -try: - getStory().append(Image(I)) -except: - disc("""An image should have appeared here.""") -disc("""whereas""") -eg(""" -im = Image("lj8100.jpg", width=2*inch, height=2*inch) -im.hAlign = 'CENTER' -""", after=0.1) -disc('produces') -try: - im = Image(I, width=2*inch, height=2*inch) - im.hAlign = 'CENTER' - getStory().append(Image(I, width=2*inch, height=2*inch)) -except: - disc("""An image should have appeared here.""") -heading2("""$Spacer(width, height)$""") -disc("""This does exactly as would be expected; it adds a certain amount of space into the story. -At present this only works for vertical space. -""") -CPage(1) -heading2("""$PageBreak()$""") -disc("""This $Flowable$ represents a page break. It works by effectively consuming all vertical -space given to it. This is sufficient for a single $Frame$ document, but would only be a -frame break for multiple frames so the $BaseDocTemplate$ mechanism -detects $pageBreaks$ internally and handles them specially. -""") -CPage(1) -heading2("""$CondPageBreak(height)$""") -disc("""This $Flowable$ attempts to force a $Frame$ break if insufficient vertical space remains -in the current $Frame$. It is thus probably wrongly named and should probably be renamed as -$CondFrameBreak$. -""") -CPage(1) -heading2("""$KeepTogether(flowables)$""") -disc(""" -This compound $Flowable$ takes a list of $Flowables$ and attempts to keep them in the same $Frame$. -If the total height of the $Flowables$ in the list $flowables$ exceeds the current frame's available -space then all the space is used and a frame break is forced. -""") diff --git a/bin/reportlab/docs/userguide/ch7_custom.py b/bin/reportlab/docs/userguide/ch7_custom.py deleted file mode 100644 index 176d042cd85..00000000000 --- a/bin/reportlab/docs/userguide/ch7_custom.py +++ /dev/null @@ -1,81 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch7_custom.py -from reportlab.tools.docco.rl_doc_utils import * - -heading1("Writing your own $Flowable$ Objects") -disc(""" -Flowables are intended to be an open standard for creating -reusable report content, and you can easily create your -own objects. We hope that over time we will build up -a library of contributions, giving reportlab users a -rich selection of charts, graphics and other "report -widgets" they can use in their own reports. This section -shows you how to create your own flowables.""") - -todo("""we should put the Figure class in the -standard library, as it is a very useful base.""") - - - - -heading2("A very simple $Flowable$") - -disc(""" -Recall the $hand$ function from the $pdfgen$ section of this user guide which -generated a drawing of a hand as a closed figure composed from Bezier curves. -""") -illust(examples.hand, "a hand") -disc(""" -To embed this or any other drawing in a Platypus flowable we must define a -subclass of $Flowable$ -with at least a $wrap$ method and a $draw$ method. -""") -eg(examples.testhandannotation) -disc(""" -The $wrap$ method must provide the size of the drawing -- it is used by -the Platypus mainloop to decide whether this element fits in the space remaining -on the current frame. The $draw$ method performs the drawing of the object after -the Platypus mainloop has translated the $(0,0)$ origin to an appropriate location -in an appropriate frame. -""") -disc(""" -Below are some example uses of the $HandAnnotation$ flowable. -""") - -from reportlab.lib.colors import blue, pink, yellow, cyan, brown -from reportlab.lib.units import inch - -handnote() - -disc("""The default.""") - -handnote(size=inch) - -disc("""Just one inch high.""") - -handnote(xoffset=3*inch, size=inch, strokecolor=blue, fillcolor=cyan) - -disc("""One inch high and shifted to the left with blue and cyan.""") - - -heading2("Modifying a Built in $Flowable$") -disc("""To modify an existing flowable, you should create a derived class -and override the methods you need to change to get the desired behaviour""") -disc("""As an example to create a rotated image you need to override the wrap -and draw methods of the existing Image class""") -import os -from reportlab.platypus import * -I = '../images/replogo.gif' - -EmbeddedCode(""" -class RotatedImage(Image): - def wrap(self,availWidth,availHeight): - h, w = Image.wrap(self,availHeight,availWidth) - return w, h - def draw(self): - self.canv.rotate(90) - Image.draw(self) -I = RotatedImage('%s') -I.hAlign = 'CENTER' -""" % I,'I') \ No newline at end of file diff --git a/bin/reportlab/docs/userguide/ch9_future.py b/bin/reportlab/docs/userguide/ch9_future.py deleted file mode 100644 index 6ac5b94e502..00000000000 --- a/bin/reportlab/docs/userguide/ch9_future.py +++ /dev/null @@ -1,35 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/ch9_future.py -from reportlab.tools.docco.rl_doc_utils import * - -heading1("Future Directions") - -disc("""We have a very long list of things we plan to do -and what we do first will most likely be inspired by customer -or user interest. -""") - -disc(""" -We plan to provide a large number of pre-designed Platypus example -document types -- brochure, newsletter, business letter, thesis, memo, -etcetera, to give our users a better boost towards the solutions they -desire. -""") - -disc(""" -We plan to fully support adding fonts and internationalization, which are -not well supported in the current release.""") - -disc(""" -We plan to fully support some of the more obscure features of PDF -such as general hyperlinks, which are not yet well supported. -""") - -disc(""" -We are also open for suggestions. Please let us know what you think -is missing. You can also offer patches or contributions. Please -look to $http://www.reportlab.com$ for the latest mailing list and -contact information.""") - -# this comment is a trivial test of SF checkin rights - delete it some time! AR 2001-04-17 \ No newline at end of file diff --git a/bin/reportlab/docs/userguide/genuserguide.py b/bin/reportlab/docs/userguide/genuserguide.py deleted file mode 100644 index 3166a6a6036..00000000000 --- a/bin/reportlab/docs/userguide/genuserguide.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/env python -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/docs/userguide/genuserguide.py -__version__=''' $Id: genuserguide.py 2900 2006-05-22 21:49:00Z andy $ ''' -__doc__ = """ -This module contains the script for building the user guide. -""" -def run(pagesize=None, verbose=0, outDir=None): - import os - from reportlab.tools.docco.rl_doc_utils import setStory, getStory, RLDocTemplate, defaultPageSize - from reportlab.tools.docco import rl_doc_utils - from reportlab.lib.utils import open_and_read, _RL_DIR - if not outDir: outDir = os.path.join(_RL_DIR,'docs') - destfn = os.path.join(outDir,'userguide.pdf') - doc = RLDocTemplate(destfn,pagesize = pagesize or defaultPageSize) - - #this builds the story - setStory() - G = {} - exec 'from reportlab.tools.docco.rl_doc_utils import *' in G, G - for f in ( - 'ch1_intro', - 'ch2_graphics', - 'ch2a_fonts', - 'ch3_pdffeatures', - 'ch4_platypus_concepts', - 'ch5_paragraphs', - 'ch6_tables', - 'ch7_custom', - 'ch9_future', - 'app_demos', - ): - exec open_and_read(f+'.py',mode='t') in G, G - del G - - story = getStory() - if verbose: print 'Built story contains %d flowables...' % len(story) - doc.build(story) - if verbose: print 'Saved "%s"' % destfn - -def makeSuite(): - "standard test harness support - run self as separate process" - from reportlab.test.utils import ScriptThatMakesFileTest - return ScriptThatMakesFileTest('../docs/userguide', 'genuserguide.py', 'userguide.pdf') - -def main(): - import sys - outDir = filter(lambda x: x[:9]=='--outdir=',sys.argv) - if outDir: - outDir = outDir[0] - sys.argv.remove(outDir) - outDir = outDir[9:] - else: - outDir = None - verbose = '-s' not in sys.argv - if not verbose: sys.argv.remove('-s') - timing = '-timing' in sys.argv - if timing: sys.argv.remove('-timing') - prof = '-prof' in sys.argv - if prof: sys.argv.remove('-prof') - - if len(sys.argv) > 1: - try: - pagesize = (w,h) = eval(sys.argv[1]) - except: - print 'Expected page size in argument 1', sys.argv[1] - raise - if verbose: - print 'set page size to',sys.argv[1] - else: - pagesize = None - if timing: - from time import time - t0 = time() - run(pagesize, verbose,outDir) - if verbose: - print 'Generation of userguide took %.2f seconds' % (time()-t0) - elif prof: - import profile - profile.run('run(pagesize,verbose,outDir)','genuserguide.stats') - else: - run(pagesize, verbose,outDir) -if __name__=="__main__": - main() diff --git a/bin/reportlab/docs/userguide/testfile.txt b/bin/reportlab/docs/userguide/testfile.txt deleted file mode 100644 index 020f3d7cbaf..00000000000 --- a/bin/reportlab/docs/userguide/testfile.txt +++ /dev/null @@ -1,2 +0,0 @@ -Test of ability to create new files in sourceforge CVS, which -seems not to be working. Can be removed any time. \ No newline at end of file diff --git a/bin/reportlab/extensions/README b/bin/reportlab/extensions/README deleted file mode 100644 index a0d9fc5ca28..00000000000 --- a/bin/reportlab/extensions/README +++ /dev/null @@ -1,25 +0,0 @@ -This directory is intended to act as a placeholder for extending ReportLab -with extra extensions. It has been packagised with an empty __init__.py. - -So a typical extender should add his package extension as - - reportlab/extensions/great_extension/__init__.py - /dingo.py - /etc etc - -and single modules as - - reportlab/extensions/my_module.py - -Then client code can do - - from reportlab.extensions.great_extension import dingo - -if you extend with just a single module it might be simpler to add that -into extensions so that you could do - - from reportlab.extensions import my_module. - - -ReportLab can take no responsibility for name clashes and problems caused by -modules and packages in reportlab/extensions. diff --git a/bin/reportlab/extensions/__init__.py b/bin/reportlab/extensions/__init__.py deleted file mode 100644 index 7e0ffa63afd..00000000000 --- a/bin/reportlab/extensions/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -#Copyright ReportLab Europe Ltd. 2000-2004 -#see license.txt for license details -#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/extensions/__init__.py -__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ ''' -__doc__=""" -""" -# No usable content. Do not add to this file! diff --git a/bin/reportlab/extensions/barcode/__init__.py b/bin/reportlab/extensions/barcode/__init__.py deleted file mode 100644 index a96948744f1..00000000000 --- a/bin/reportlab/extensions/barcode/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -__version__ = '0.9' diff --git a/bin/reportlab/extensions/barcode/code128.py b/bin/reportlab/extensions/barcode/code128.py deleted file mode 100644 index a69834c1ebc..00000000000 --- a/bin/reportlab/extensions/barcode/code128.py +++ /dev/null @@ -1,321 +0,0 @@ -# -# Copyright (c) 2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import MultiWidthBarcode -from string import digits - -_patterns = { - 0 : 'BaBbBb', 1 : 'BbBaBb', 2 : 'BbBbBa', - 3 : 'AbAbBc', 4 : 'AbAcBb', 5 : 'AcAbBb', - 6 : 'AbBbAc', 7 : 'AbBcAb', 8 : 'AcBbAb', - 9 : 'BbAbAc', 10 : 'BbAcAb', 11 : 'BcAbAb', - 12 : 'AaBbCb', 13 : 'AbBaCb', 14 : 'AbBbCa', - 15 : 'AaCbBb', 16 : 'AbCaBb', 17 : 'AbCbBa', - 18 : 'BbCbAa', 19 : 'BbAaCb', 20 : 'BbAbCa', - 21 : 'BaCbAb', 22 : 'BbCaAb', 23 : 'CaBaCa', - 24 : 'CaAbBb', 25 : 'CbAaBb', 26 : 'CbAbBa', - 27 : 'CaBbAb', 28 : 'CbBaAb', 29 : 'CbBbAa', - 30 : 'BaBaBc', 31 : 'BaBcBa', 32 : 'BcBaBa', - 33 : 'AaAcBc', 34 : 'AcAaBc', 35 : 'AcAcBa', - 36 : 'AaBcAc', 37 : 'AcBaAc', 38 : 'AcBcAa', - 39 : 'BaAcAc', 40 : 'BcAaAc', 41 : 'BcAcAa', - 42 : 'AaBaCc', 43 : 'AaBcCa', 44 : 'AcBaCa', - 45 : 'AaCaBc', 46 : 'AaCcBa', 47 : 'AcCaBa', - 48 : 'CaCaBa', 49 : 'BaAcCa', 50 : 'BcAaCa', - 51 : 'BaCaAc', 52 : 'BaCcAa', 53 : 'BaCaCa', - 54 : 'CaAaBc', 55 : 'CaAcBa', 56 : 'CcAaBa', - 57 : 'CaBaAc', 58 : 'CaBcAa', 59 : 'CcBaAa', - 60 : 'CaDaAa', 61 : 'BbAdAa', 62 : 'DcAaAa', - 63 : 'AaAbBd', 64 : 'AaAdBb', 65 : 'AbAaBd', - 66 : 'AbAdBa', 67 : 'AdAaBb', 68 : 'AdAbBa', - 69 : 'AaBbAd', 70 : 'AaBdAb', 71 : 'AbBaAd', - 72 : 'AbBdAa', 73 : 'AdBaAb', 74 : 'AdBbAa', - 75 : 'BdAbAa', 76 : 'BbAaAd', 77 : 'DaCaAa', - 78 : 'BdAaAb', 79 : 'AcDaAa', 80 : 'AaAbDb', - 81 : 'AbAaDb', 82 : 'AbAbDa', 83 : 'AaDbAb', - 84 : 'AbDaAb', 85 : 'AbDbAa', 86 : 'DaAbAb', - 87 : 'DbAaAb', 88 : 'DbAbAa', 89 : 'BaBaDa', - 90 : 'BaDaBa', 91 : 'DaBaBa', 92 : 'AaAaDc', - 93 : 'AaAcDa', 94 : 'AcAaDa', 95 : 'AaDaAc', - 96 : 'AaDcAa', 97 : 'DaAaAc', 98 : 'DaAcAa', - 99 : 'AaCaDa', 100 : 'AaDaCa', 101 : 'CaAaDa', - 102 : 'DaAaCa', 103 : 'BaAdAb', 104 : 'BaAbAd', - 105 : 'BaAbCb', 106 : 'BcCaAaB' -} - -starta, startb, startc, stop = 103, 104, 105, 106 - -seta = { - ' ' : 0, '!' : 1, '"' : 2, '#' : 3, - '$' : 4, '%' : 5, '&' : 6, '\'' : 7, - '(' : 8, ')' : 9, '*' : 10, '+' : 11, - ',' : 12, '-' : 13, '.' : 14, '/' : 15, - '0' : 16, '1' : 17, '2' : 18, '3' : 19, - '4' : 20, '5' : 21, '6' : 22, '7' : 23, - '8' : 24, '9' : 25, ':' : 26, ';' : 27, - '<' : 28, '=' : 29, '>' : 30, '?' : 31, - '@' : 32, 'A' : 33, 'B' : 34, 'C' : 35, - 'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39, - 'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43, - 'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47, - 'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51, - 'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55, - 'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59, - '\\' : 60, ']' : 61, '^' : 62, '_' : 63, - '\x00' : 64, '\x01' : 65, '\x02' : 66, '\x03' : 67, - '\x04' : 68, '\x05' : 69, '\x06' : 70, '\x07' : 71, - '\x08' : 72, '\x09' : 73, '\x0a' : 74, '\x0b' : 75, - '\x0c' : 76, '\x0d' : 77, '\x0e' : 78, '\x0f' : 79, - '\x10' : 80, '\x11' : 81, '\x12' : 82, '\x13' : 83, - '\x14' : 84, '\x15' : 85, '\x16' : 86, '\x17' : 87, - '\x18' : 88, '\x19' : 89, '\x1a' : 90, '\x1b' : 91, - '\x1c' : 92, '\x1d' : 93, '\x1e' : 94, '\x1f' : 95, - '\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99, - 'TO_B' : 100, '\xf4' : 101, '\xf1' : 102 -} - -setb = { - ' ' : 0, '!' : 1, '"' : 2, '#' : 3, - '$' : 4, '%' : 5, '&' : 6, '\'' : 7, - '(' : 8, ')' : 9, '*' : 10, '+' : 11, - ',' : 12, '-' : 13, '.' : 14, '/' : 15, - '0' : 16, '1' : 17, '2' : 18, '3' : 19, - '4' : 20, '5' : 21, '6' : 22, '7' : 23, - '8' : 24, '9' : 25, ':' : 26, ';' : 27, - '<' : 28, '=' : 29, '>' : 30, '?' : 31, - '@' : 32, 'A' : 33, 'B' : 34, 'C' : 35, - 'D' : 36, 'E' : 37, 'F' : 38, 'G' : 39, - 'H' : 40, 'I' : 41, 'J' : 42, 'K' : 43, - 'L' : 44, 'M' : 45, 'N' : 46, 'O' : 47, - 'P' : 48, 'Q' : 49, 'R' : 50, 'S' : 51, - 'T' : 52, 'U' : 53, 'V' : 54, 'W' : 55, - 'X' : 56, 'Y' : 57, 'Z' : 58, '[' : 59, - '\\' : 60, ']' : 61, '^' : 62, '_' : 63, - '`' : 64, 'a' : 65, 'b' : 66, 'c' : 67, - 'd' : 68, 'e' : 69, 'f' : 70, 'g' : 71, - 'h' : 72, 'i' : 73, 'j' : 74, 'k' : 75, - 'l' : 76, 'm' : 77, 'n' : 78, 'o' : 79, - 'p' : 80, 'q' : 81, 'r' : 82, 's' : 83, - 't' : 84, 'u' : 85, 'v' : 86, 'w' : 87, - 'x' : 88, 'y' : 89, 'z' : 90, '{' : 91, - '|' : 92, '}' : 93, '~' : 94, '\x7f' : 95, - '\xf3' : 96, '\xf2' : 97, 'SHIFT' : 98, 'TO_C' : 99, - '\xf4' : 100, 'TO_A' : 101, '\xf1' : 102 -} - -setc = { - '00': 0, '01': 1, '02': 2, '03': 3, '04': 4, - '05': 5, '06': 6, '07': 7, '08': 8, '09': 9, - '10':10, '11':11, '12':12, '13':13, '14':14, - '15':15, '16':16, '17':17, '18':18, '19':19, - '20':20, '21':21, '22':22, '23':23, '24':24, - '25':25, '26':26, '27':27, '28':28, '29':29, - '30':30, '31':31, '32':32, '33':33, '34':34, - '35':35, '36':36, '37':37, '38':38, '39':39, - '40':40, '41':41, '42':42, '43':43, '44':44, - '45':45, '46':46, '47':47, '48':48, '49':49, - '50':50, '51':51, '52':52, '53':53, '54':54, - '55':55, '56':56, '57':57, '58':58, '59':59, - '60':60, '61':61, '62':62, '63':63, '64':64, - '65':65, '66':66, '67':67, '68':68, '69':69, - '70':70, '71':71, '72':72, '73':73, '74':74, - '75':75, '76':76, '77':77, '78':78, '79':79, - '80':80, '81':81, '82':82, '83':83, '84':84, - '85':85, '86':86, '87':87, '88':88, '89':89, - '90':90, '91':91, '92':92, '93':93, '94':94, - '95':95, '96':96, '97':97, '98':98, '99':99, - - 'TO_B' : 100, 'TO_A' : 101, '\xf1' : 102 -} - -setmap = { - 'TO_A' : (seta, setb), - 'TO_B' : (setb, seta), - 'TO_C' : (setc, None), - 'START_A' : (starta, seta, setb), - 'START_B' : (startb, setb, seta), - 'START_C' : (startc, setc, None), -} -tos = setmap.keys() - -class Code128(MultiWidthBarcode): - """ - Code 128 is a very compact symbology that can encode the entire - 128 character ASCII set, plus 4 special control codes, - (FNC1-FNC4, expressed in the input string as \xf1 to \xf4). - Code 128 can also encode digits at double density (2 per byte) - and has a mandatory checksum. Code 128 is well supported and - commonly used -- for example, by UPS for tracking labels. - - Because of these qualities, Code 128 is probably the best choice - for a linear symbology today (assuming you have a choice). - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 xdim - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 128: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_128.html - http://www.adams1.com/pub/russadam/128code.html - http://www.barcodeman.com/c128.html - - Official Spec, "ANSI/AIM BC4-1999, ISS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - def __init__(self, value='', **args): - self.xdim = inch * 0.0275 - self.lquiet = None - self.rquiet = None - self.quiet = 1 - self.height = None - - if type(value) is type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.xdim * 10.0) - self.rquiet = max(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - MultiWidthBarcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if ord(c) > 127 and c not in '\xf1\xf2\xf3\xf4': - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def _trailingDigitsToC(self, l): - # Optimization: trailing digits -> set C double-digits - c = 1 - savings = -1 # the TO_C costs one character - rl = ['STOP'] - while c < len(l): - i = (-c - 1) - if l[i] == '\xf1': - c = c + 1 - rl.insert(0, '\xf1') - continue - elif len(l[i]) == 1 and l[i] in digits \ - and len(l[i-1]) == 1 and l[i-1] in digits: - c = c + 2 - savings = savings + 1 - rl.insert(0, l[i-1] + l[i]) - continue - else: - break - if savings > 0: - return l[:-c] + ['TO_C'] + rl - else: - return l - - def encode(self): - # First, encode using only B - s = self.validated - l = ['START_B'] - for c in s: - if not setb.has_key(c): - l = l + ['TO_A', c, 'TO_B'] - else: - l.append(c) - l.append('STOP') - - l = self._trailingDigitsToC(l) - - # Finally, replace START_X,TO_Y with START_Y - if l[1] in tos: - l[:2] = ['START_' + l[1][-1]] - -# print `l` - - # encode into numbers - start, set, shset = setmap[l[0]] - e = [start] - - l = l[1:-1] - while l: - c = l[0] - if c == 'SHIFT': - e = e + [set[c], shset[l[1]]] - l = l[2:] - elif c in tos: - e.append(set[c]) - set, shset = setmap[c] - l = l[1:] - else: - e.append(set[c]) - l = l[1:] - - c = e[0] - for i in range(1, len(e)): - c = c + i * e[i] - self.encoded = e + [c % 103, stop] - return self.encoded - - def decompose(self): - dval = '' - for c in self.encoded: - dval = dval + _patterns[c] - self.decomposed = dval - return self.decomposed diff --git a/bin/reportlab/extensions/barcode/code39.py b/bin/reportlab/extensions/barcode/code39.py deleted file mode 100644 index 08e0c9d4b5d..00000000000 --- a/bin/reportlab/extensions/barcode/code39.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -_patterns = { - '0': ("bsbSBsBsb", 0), '1': ("BsbSbsbsB", 1), - '2': ("bsBSbsbsB", 2), '3': ("BsBSbsbsb", 3), - '4': ("bsbSBsbsB", 4), '5': ("BsbSBsbsb", 5), - '6': ("bsBSBsbsb", 6), '7': ("bsbSbsBsB", 7), - '8': ("BsbSbsBsb", 8), '9': ("bsBSbsBsb", 9), - 'A': ("BsbsbSbsB", 10), 'B': ("bsBsbSbsB", 11), - 'C': ("BsBsbSbsb", 12), 'D': ("bsbsBSbsB", 13), - 'E': ("BsbsBSbsb", 14), 'F': ("bsBsBSbsb", 15), - 'G': ("bsbsbSBsB", 16), 'H': ("BsbsbSBsb", 17), - 'I': ("bsBsbSBsb", 18), 'J': ("bsbsBSBsb", 19), - 'K': ("BsbsbsbSB", 20), 'L': ("bsBsbsbSB", 21), - 'M': ("BsBsbsbSb", 22), 'N': ("bsbsBsbSB", 23), - 'O': ("BsbsBsbSb", 24), 'P': ("bsBsBsbSb", 25), - 'Q': ("bsbsbsBSB", 26), 'R': ("BsbsbsBSb", 27), - 'S': ("bsBsbsBSb", 28), 'T': ("bsbsBsBSb", 29), - 'U': ("BSbsbsbsB", 30), 'V': ("bSBsbsbsB", 31), - 'W': ("BSBsbsbsb", 32), 'X': ("bSbsBsbsB", 33), - 'Y': ("BSbsBsbsb", 34), 'Z': ("bSBsBsbsb", 35), - '-': ("bSbsbsBsB", 36), '.': ("BSbsbsBsb", 37), - ' ': ("bSBsbsBsb", 38), '*': ("bSbsBsBsb", 39), - '$': ("bSbSbSbsb", 40), '/': ("bSbSbsbSb", 41), - '+': ("bSbsbSbSb", 42), '%': ("bsbSbSbSb", 43) -} - -_valchars = [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', - 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '-', '.', ' ', '*', '$', '/', '+', '%' -] - -_extended = { - '\0': "%U", '\01': "$A", '\02': "$B", '\03': "$C", - '\04': "$D", '\05': "$E", '\06': "$F", '\07': "$G", - '\010': "$H", '\011': "$I", '\012': "$J", '\013': "$K", - '\014': "$L", '\015': "$M", '\016': "$N", '\017': "$O", - '\020': "$P", '\021': "$Q", '\022': "$R", '\023': "$S", - '\024': "$T", '\025': "$U", '\026': "$V", '\027': "$W", - '\030': "$X", '\031': "$Y", '\032': "$Z", '\033': "%A", - '\034': "%B", '\035': "%C", '\036': "%D", '\037': "%E", - '!': "/A", '"': "/B", '#': "/C", '$': "/D", - '%': "/E", '&': "/F", '\'': "/G", '(': "/H", - ')': "/I", '*': "/J", '+': "/K", ',': "/L", - '/': "/O", ':': "/Z", ';': "%F", '<': "%G", - '=': "%H", '>': "%I", '?': "%J", '@': "%V", - '[': "%K", '\\': "%L", ']': "%M", '^': "%N", - '_': "%O", '`': "%W", 'a': "+A", 'b': "+B", - 'c': "+C", 'd': "+D", 'e': "+E", 'f': "+F", - 'g': "+G", 'h': "+H", 'i': "+I", 'j': "+J", - 'k': "+K", 'l': "+L", 'm': "+M", 'n': "+N", - 'o': "+O", 'p': "+P", 'q': "+Q", 'r': "+R", - 's': "+S", 't': "+T", 'u': "+U", 'v': "+V", - 'w': "+W", 'x': "+X", 'y': "+Y", 'z': "+Z", - '{': "%P", '|': "%Q", '}': "%R", '~': "%S", - '\177': "%T" -} - - -_stdchrs = string.digits + string.uppercase + "-. *$/+%" -_extchrs = _stdchrs + string.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" - - -def _encode39(str, cksum): - newval = "*" - v = 0 - for c in str: - v = v + _patterns[c][1] - newval = newval + c - v = v % 43 - if cksum: - newval = newval + _valchars[v] - return newval + '*' - - -class _Code39Base(Barcode): - def __init__(self, value = "", **args): - self.xdim = inch * 0.0075 - self.lquiet = None - self.rquiet = None - self.quiet = 1 - self.gap = None - self.height = None - self.ratio = 2.2 - self.checksum = 0 - self.bearers = 0.0 - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.xdim * 10.0) - self.rquiet = max(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def decompose(self): - dval = "" - for c in self.encoded: - dval = dval + _patterns[c][0] + 'i' - self.decomposed = dval[:-1] - return self.decomposed - - -class Standard39(_Code39Base): - """ - Interleaved 2 of 5 is a numeric-only barcode. It encodes an even - number of digits; if an odd number is given, a 0 is prepended. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the - xdim is greater than 20 mils (.02 inch)) - - gap (float or None, default None): - width of intercharacter gap. None means "use xdim". - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 0): - Wether to compute and include the check digit - - bearers (float, in units of xdim. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 39: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_39.html - http://www.adams1.com/pub/russadam/39code.html - http://www.barcodeman.com/c39_1.html - - Official Spec, "ANSI/AIM BC1-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c in string.lowercase: - c = string.upper(c) - if c not in _stdchrs: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = _encode39(self.validated, self.checksum) - return self.encoded - - -class Extended39(_Code39Base): - """ - Extended Code 39 is a convention for encoding additional characters - not present in stanmdard Code 39 by using pairs of characters to - represent the characters missing in Standard Code 39. - - See Standard39 for arguments. - - Sources of Information on Extended Code 39: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/xcode_39.html - http://www.barcodeman.com/c39_ext.html - """ - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c not in _extchrs: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = "" - for c in self.validated: - if _extended.has_key(c): - self.encoded = self.encoded + _extended[c] - elif c in _stdchrs: - self.encoded = self.encoded + c - else: - raise ValueError - self.encoded = _encode39(self.encoded, self.checksum) - return self.encoded diff --git a/bin/reportlab/extensions/barcode/code93.py b/bin/reportlab/extensions/barcode/code93.py deleted file mode 100644 index fc19ee3ee98..00000000000 --- a/bin/reportlab/extensions/barcode/code93.py +++ /dev/null @@ -1,232 +0,0 @@ -# -# Copyright (c) 2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import MultiWidthBarcode -import string - -_patterns = { - '0' : ('AcAaAb', 0), '1' : ('AaAbAc', 1), '2' : ('AaAcAb', 2), - '3' : ('AaAdAa', 3), '4' : ('AbAaAc', 4), '5' : ('AbAbAb', 5), - '6' : ('AbAcAa', 6), '7' : ('AaAaAd', 7), '8' : ('AcAbAa', 8), - '9' : ('AdAaAa', 9), 'A' : ('BaAaAc', 10), 'B' : ('BaAbAb', 11), - 'C' : ('BaAcAa', 12), 'D' : ('BbAaAb', 13), 'E' : ('BbAbAa', 14), - 'F' : ('BcAaAa', 15), 'G' : ('AaBaAc', 16), 'H' : ('AaBbAb', 17), - 'I' : ('AaBcAa', 18), 'J' : ('AbBaAb', 19), 'K' : ('AcBaAa', 20), - 'L' : ('AaAaBc', 21), 'M' : ('AaAbBb', 22), 'N' : ('AaAcBa', 23), - 'O' : ('AbAaBb', 24), 'P' : ('AcAaBa', 25), 'Q' : ('BaBaAb', 26), - 'R' : ('BaBbAa', 27), 'S' : ('BaAaBb', 28), 'T' : ('BaAbBa', 29), - 'U' : ('BbAaBa', 30), 'V' : ('BbBaAa', 31), 'W' : ('AaBaBb', 32), - 'X' : ('AaBbBa', 33), 'Y' : ('AbBaBa', 34), 'Z' : ('AbCaAa', 35), - '-' : ('AbAaCa', 36), '.' : ('CaAaAb', 37), ' ' : ('CaAbAa', 38), - '$' : ('CbAaAa', 39), '/' : ('AaBaCa', 40), '+' : ('AaCaBa', 41), - '%' : ('BaAaCa', 42), '#' : ('AbAbBa', 43), '!' : ('CaBaAa', 44), - '=' : ('CaAaBa', 45), '&' : ('AbBbAa', 46), - 'start' : ('AaAaDa', -1), 'stop' : ('AaAaDaA', -2) -} - -_charsbyval = {} -for k, v in _patterns.items(): - _charsbyval[v[1]] = k - -_extended = { - '\x00' : '!U', '\x01' : '#A', '\x02' : '#B', '\x03' : '#C', - '\x04' : '#D', '\x05' : '#E', '\x06' : '#F', '\x07' : '#G', - '\x08' : '#H', '\x09' : '#I', '\x0a' : '#J', '\x0b' : '#K', - '\x0c' : '#L', '\x0d' : '#M', '\x0e' : '#N', '\x0f' : '#O', - '\x10' : '#P', '\x11' : '#Q', '\x12' : '#R', '\x13' : '#S', - '\x14' : '#T', '\x15' : '#U', '\x16' : '#V', '\x17' : '#W', - '\x18' : '#X', '\x19' : '#Y', '\x1a' : '#Z', '\x1b' : '!A', - '\x1c' : '!B', '\x1d' : '!C', '\x1e' : '!D', '\x1f' : '!E', - '!' : '=A', '"' : '=B', '#' : '=C', '$' : '=D', - '%' : '=E', '&' : '=F', '\'' : '=G', '(' : '=H', - ')' : '=I', '*' : '=J', '+' : '=K', ',' : '=L', - '/' : '=O', ':' : '=Z', ';' : '!F', '<' : '!G', - '=' : '!H', '>' : '!I', '?' : '!J', '@' : '!V', - '[' : '!K', '\\' : '!L', ']' : '!M', '^' : '!N', - '_' : '!O', '`' : '!W', 'a' : '&A', 'b' : '&B', - 'c' : '&C', 'd' : '&D', 'e' : '&E', 'f' : '&F', - 'g' : '&G', 'h' : '&H', 'i' : '&I', 'j' : '&J', - 'k' : '&K', 'l' : '&L', 'm' : '&M', 'n' : '&N', - 'o' : '&O', 'p' : '&P', 'q' : '&Q', 'r' : '&R', - 's' : '&S', 't' : '&T', 'u' : '&U', 'v' : '&V', - 'w' : '&W', 'x' : '&X', 'y' : '&Y', 'z' : '&Z', - '{' : '!P', '|' : '!Q', '}' : '!R', '~' : '!S', - '\x7f' : '!T' -} - - -def _encode93(str): - s = map(None, str) - s.reverse() - - # compute 'C' checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * _patterns[s[i]][1] - i = i + 1; v = v + 1 - if v > 20: - v = 1 - s.insert(0, _charsbyval[c % 47]) - - # compute 'K' checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * _patterns[s[i]][1] - i = i + 1; v = v + 1 - if v > 15: - v = 1 - s.insert(0, _charsbyval[c % 47]) - - s.reverse() - - return string.join(s, '') - - -class _Code93Base(MultiWidthBarcode): - def __init__(self, value='', **args): - self.xdim = inch * 0.0075 - self.lquiet = None - self.rquiet = None - self.quiet = 1 - self.height = None - - if type(value) is type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.xdim * 10.0) - self.rquiet = max(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - MultiWidthBarcode.__init__(self, value) - - def decompose(self): - dval = _patterns['start'][0] - for c in self.encoded: - dval = dval + _patterns[c][0] - self.decomposed = dval + _patterns['stop'][0] - return self.decomposed - - -class Standard93(_Code93Base): - """ - Code 93 is a Uppercase alphanumeric symbology with some punctuation. - See Extended Code 93 for a variant that can represent the entire - 128 characrter ASCII set. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 xdim - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 93: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/code_93.html - - Official Spec, "NSI/AIM BC5-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if c in string.lowercase: - c = string.upper(c) - if not _patterns.has_key(c): - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = _encode93(self.validated) - return self.encoded - - -class Extended93(_Code93Base): - """ - Extended Code 93 is a convention for encoding the entire 128 character - set using pairs of characters to represent the characters missing in - Standard Code 93. It is very much like Extended Code 39 in that way. - - See Standard93 for arguments. - """ - - def validate(self): - vval = "" - self.valid = 1 - for c in self.value: - if not _patterns.has_key(c) and not _extended.has_key(c): - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - self.encoded = "" - for c in self.validated: - if _patterns.has_key(c): - self.encoded = self.encoded + c - elif _extended.has_key(c): - self.encoded = self.encoded + _extended[c] - else: - raise ValueError - self.encoded = _encode93(self.encoded) - return self.encoded diff --git a/bin/reportlab/extensions/barcode/common.py b/bin/reportlab/extensions/barcode/common.py deleted file mode 100644 index a73ed7d73f5..00000000000 --- a/bin/reportlab/extensions/barcode/common.py +++ /dev/null @@ -1,705 +0,0 @@ -# -# Copyright (c) 1996-2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.platypus.flowables import Flowable -from reportlab.lib.units import inch -import string - -class Barcode(Flowable): - """Abstract Base for barcodes. Includes implementations of - some methods suitable for the more primitive barcode types""" - - def __init__(self, value = ''): - self.value = value - - if not hasattr(self, 'gap'): - self.gap = None - - self.validate() - self.encode() - #print self.encoded - self.decompose() - #print self.decomposed - self.computeSize() - - def validate(self): - self.valid = 1 - self.validated = self.value - - def encode(self): - self.encoded = self.validated - - def decompose(self): - self.decomposed = self.encoded - - def computeSize(self, *args): - xdim = self.xdim - wx = xdim * self.ratio - - if self.gap == None: - self.gap = xdim - - w = 0.0 - - for c in self.decomposed: - if c in 'sb': - w = w + xdim - elif c in 'SB': - w = w + wx - else: # 'i' - w = w + self.gap - - if self.height is None: - self.height = w * 0.15 - self.height = max(0.25 * inch, self.height) - if self.bearers: - self.height = self.height + self.bearers * 2.0 * xdim - - if self.quiet: - w = w + self.lquiet + self.rquiet - self.xo = self.lquiet - else: - self.xo = 0.0 - - self.width = w - - def draw(self): - xdim = self.xdim - wx = xdim * self.ratio - - left = self.xo - b = self.bearers * xdim - bb = b * 0.5 - tb = self.height - (b * 1.5) - - for c in self.decomposed: - if c == 'i': - left = left + self.gap - elif c == 's': - left = left + xdim - elif c == 'S': - left = left + wx - elif c == 'b': - self.rect(left, bb, xdim, tb) - left = left + xdim - elif c == 'B': - self.rect(left, bb, wx, tb) - left = left + wx - - if self.bearers: - self.rect(self.lquiet, 0.0, \ - self.width - (self.lquiet + self.rquiet), b) - self.rect(self.lquiet, self.height - b, \ - self.width - (self.lquiet + self.rquiet), b) - - def rect(self, x, y, w, h): - self.canv.rect(x, y, w, h, stroke=0, fill=1) - - -class MultiWidthBarcode(Barcode): - """Base for variable-bar-width codes like Code93 and Code128""" - - def computeSize(self, *args): - xdim = self.xdim - oa, oA = ord('a') - 1, ord('A') - 1 - - w = 0.0 - - for c in self.decomposed: - oc = ord(c) - if c in string.lowercase: - w = w + xdim * (oc - oa) - elif c in string.uppercase: - w = w + xdim * (oc - oA) - - if self.height is None: - self.height = w * 0.15 - self.height = max(0.25 * inch, self.height) - - if self.quiet: - w = w + self.lquiet + self.rquiet - self.xo = self.lquiet - else: - self.xo = 0.0 - - self.width = w - - def draw(self): - oa, oA = ord('a') - 1, ord('A') - 1 - xdim = self.xdim - left = self.xo - - for c in self.decomposed: - oc = ord(c) - if c in string.lowercase: - left = left + (oc - oa) * xdim - elif c in string.uppercase: - w = (oc - oA) * xdim - self.rect(left, 0.0, w, self.height) - left = left + w - - -class I2of5(Barcode): - """ - Interleaved 2 of 5 is a numeric-only barcode. It encodes an even - number of digits; if an odd number is given, a 0 is prepended. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - Minumum is .0075 inch (7.5 mils). - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the - xdim is greater than 20 mils (.02 inch)) - - gap (float or None, default None): - width of intercharacter gap. None means "use xdim". - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 1): - Wether to compute and include the check digit - - bearers (float, in units of xdim. default 3.0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 3 x-dimensions. - Set to zero for no bearer bars. (Bearer bars help detect - misscans, so it is suggested to leave them on). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Interleaved 2 of 5: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/i_25.html - http://www.adams1.com/pub/russadam/i25code.html - - Official Spec, "ANSI/AIM BC2-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - patterns = { - 'start' : 'bsbs', - 'stop' : 'Bsb', - - 'B0' : 'bbBBb', 'S0' : 'ssSSs', - 'B1' : 'BbbbB', 'S1' : 'SsssS', - 'B2' : 'bBbbB', 'S2' : 'sSssS', - 'B3' : 'BBbbb', 'S3' : 'SSsss', - 'B4' : 'bbBbB', 'S4' : 'ssSsS', - 'B5' : 'BbBbb', 'S5' : 'SsSss', - 'B6' : 'bBBbb', 'S6' : 'sSSss', - 'B7' : 'bbbBB', 'S7' : 'sssSS', - 'B8' : 'BbbBb', 'S8' : 'SssSs', - 'B9' : 'bBbBb', 'S9' : 'sSsSs' - } - - def __init__(self, value='', **args): - self.height = None - self.xdim = inch * 0.0075 - self.ratio = 2.2 - self.checksum = 1 - self.bearers = 3.0 - self.quiet = 1 - self.lquiet = self.rquiet = None - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.xdim * 10.0) - self.rquiet = min(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in string.strip(self.value): - if c not in string.digits: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - s = self.validated - - # make sure result will be a multiple of 2 digits long, - # checksum included - if ((len(self.validated) % 2 == 0) and self.checksum) \ - or ((len(self.validated) % 2 == 1) and not self.checksum): - s = '0' + s - - if self.checksum: - c = 0 - cm = 3 - - for d in s: - c = c + cm * int(d) - if cm == 3: - cm = 1 - else: - cm = 3 - - d = 10 - (int(d) % 10) - s = s + `d` - - self.encoded = s - - def decompose(self): - dval = self.patterns['start'] - - for i in range(0, len(self.encoded), 2): - b = self.patterns['B' + self.encoded[i]] - s = self.patterns['S' + self.encoded[i+1]] - - for i in range(0, len(b)): - dval = dval + b[i] + s[i] - - self.decomposed = dval + self.patterns['stop'] - return self.decomposed - - - -class MSI(Barcode): - """ - MSI is a numeric-only barcode. - - Options that may be passed to constructor: - - value (int, or numeric string. required.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use xdim". - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 1): - Wether to compute and include the check digit - - bearers (float, in units of xdim. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 xdims. - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on MSI Bar Code: - - http://www.semiconductor.agilent.com/barcode/sg/Misc/msi_code.html - http://www.adams1.com/pub/russadam/plessy.html - """ - - patterns = { - 'start' : 'Bs', 'stop' : 'bSb', - - '0' : 'bSbSbSbS', '1' : 'bSbSbSBs', - '2' : 'bSbSBsbS', '3' : 'bSbSBsBs', - '4' : 'bSBsbSbS', '5' : 'bSBsbSBs', - '6' : 'bSBsBsbS', '7' : 'bSBsBsBs', - '8' : 'BsbSbSbS', '9' : 'BsbSbSBs' - } - - def __init__(self, value="", **args): - self.height = None - self.xdim = inch * 0.0075 - self.ratio = 2.2 - self.checksum = 1 - self.bearers = 0.0 - self.quiet = 1 - self.lquiet = self.rquiet = None - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = max(inch * 0.25, self.xdim * 10.0) - self.rquiet = max(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - for c in string.strip(self.value): - if c not in string.digits: - self.valid = 0 - continue - vval = vval + c - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum: - c = '' - for i in range(1, len(s), 2): - c = c + s[i] - d = str(int(c) * 2) - t = 0 - for c in d: - t = t + int(c) - for i in range(0, len(s), 2): - t = t + int(s[i]) - c = 10 - (t % 10) - - s = s + str(c) - - self.encoded = s - - def decompose(self): - dval = self.patterns['start'] - - for c in self.encoded: - dval = dval + self.patterns[c] - - self.decomposed = dval + self.patterns['stop'] - return self.decomposed - - - -class Codabar(Barcode): - """ - Codabar is a numeric plus some puntuation ("-$:/.+") barcode - with four start/stop characters (A, B, C, and D). - - Options that may be passed to constructor: - - value (string. required.): - The value to encode. - - xdim (float, default .0065): - X-Dimension, or width of the smallest element - minimum is 6.5 mils (.0065 inch) - - ratio (float, default 2.0): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use xdim". - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (bool, default 0): - Wether to compute and include the check digit - - bearers (float, in units of xdim. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 xdim - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Codabar - - http://www.semiconductor.agilent.com/barcode/sg/Misc/codabar.html - http://www.barcodeman.com/codabar.html - - Official Spec, "ANSI/AIM BC3-1995, USS" is available for US$45 from - http://www.aimglobal.org/aimstore/ - """ - - patterns = { - '0': 'bsbsbSB', '1': 'bsbsBSb', '2': 'bsbSbsB', - '3': 'BSbsbsb', '4': 'bsBsbSb', '5': 'BsbsbSb', - '6': 'bSbsbsB', '7': 'bSbsBsb', '8': 'bSBsbsb', - '9': 'BsbSbsb', '-': 'bsbSBsb', '$': 'bsBSbsb', - ':': 'BsbsBsB', '/': 'BsBsbsB', '.': 'BsBsBsb', - '+': 'bsBsBsB', 'A': 'bsBSbSb', 'B': 'bSbSbsB', - 'C': 'bsbSbSB', 'D': 'bsbSBSb' - } - - values = { - '0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4, - '5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9, - '-' : 10, '$' : 11, ':' : 12, '/' : 13, '.' : 14, - '+' : 15, 'A' : 16, 'B' : 17, 'C' : 18, 'D' : 19 - } - - chars = string.digits + "-$:/.+" - - def __init__(self, value='', **args): - self.height = None - self.xdim = inch * 0.0065 - self.ratio = 2.0 # XXX ? - self.checksum = 0 - self.bearers = 0.0 - self.quiet = 1 - self.lquiet = self.rquiet = None - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.xdim * 10.0) - self.rquiet = min(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - s = string.strip(self.value) - for i in range(0, len(s)): - c = s[i] - if c not in self.chars: - if ((i != 0) and (i != len(s) - 1)) or (c not in 'ABCD'): - self.Valid = 0 - continue - vval = vval + c - - if vval[0] not in 'ABCD': - vval = 'A' + vval - if vval[-1] not in 'ABCD': - vval = vval + vval[0] - - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum: - v = 0 - for c in s: - v = v + self.values[v] - v = 16 - (v % 16) - s = s + self.chars[v] - - self.encoded = s - - def decompose(self): - dval = "" - for c in self.encoded: - dval = dval + self.patterns[c] + 'i' - self.decomposed = dval[:-1] - return self.decomposed - - - -class Code11(Barcode): - """ - 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.): - The value to encode. - - xdim (float, default .0075): - X-Dimension, or width of the smallest element - - ratio (float, default 2.2): - The ratio of wide elements to narrow elements. - - gap (float or None, default None): - width of intercharacter gap. None means "use xdim". - - height (float, see default below): - Height of the symbol. Default is the height of the two - bearer bars (if they exist) plus the greater of .25 inch - or .15 times the symbol's length. - - checksum (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. - - bearers (float, in units of xdim. default 0): - Height of bearer bars (horizontal bars along the top and - bottom of the barcode). Default is 0 (no bearers). - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - lquiet (float, see default below): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or 10 xdim - - rquiet (float, defaults as above): - Quiet zone size to right left of code, if quiet is true. - - Sources of Information on Code 11: - - http://www.cwi.nl/people/dik/english/codes/barcodes.html - """ - - chars = string.digits + '-' - - patterns = { - '0' : 'bsbsB', '1' : 'BsbsB', '2' : 'bSbsB', - '3' : 'BSbsb', '4' : 'bsBsB', '5' : 'BsBsb', - '6' : 'bSBsb', '7' : 'bsbSB', '8' : 'BsbSb', - '9' : 'Bsbsb', '-' : 'bsBsb', 'S' : 'bsBSb' # Start/Stop - } - - values = { - '0' : 0, '1' : 1, '2' : 2, '3' : 3, '4' : 4, - '5' : 5, '6' : 6, '7' : 7, '8' : 8, '9' : 9, - '-' : 10, - } - - def __init__(self, value='', **args): - self.height = None - self.xdim = inch * 0.0075 - self.ratio = 2.2 # XXX ? - self.checksum = -1 # Auto - self.bearers = 0.0 - self.quiet = 1 - self.lquiet = self.rquiet = None - - if type(value) == type(1): - value = str(value) - - for (k, v) in args.items(): - setattr(self, k, v) - - if self.quiet: - if self.lquiet is None: - self.lquiet = min(inch * 0.25, self.xdim * 10.0) - self.rquiet = min(inch * 0.25, self.xdim * 10.0) - else: - self.lquiet = self.rquiet = 0.0 - - Barcode.__init__(self, value) - - def validate(self): - vval = "" - self.valid = 1 - s = string.strip(self.value) - for i in range(0, len(s)): - c = s[i] - if c not in self.chars: - self.Valid = 0 - continue - vval = vval + c - - self.validated = vval - return vval - - def encode(self): - s = self.validated - - if self.checksum == -1: - if len(s) <= 10: - self.checksum = 1 - else: - self.checksum = 2 - - if self.checksum > 0: - # compute first checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * string.index(self.chars, s[-(i+1)]) - i = i + 1; v = v + 1 - if v > 10: - v = 1 - s = s + self.chars[c % 11] - - if self.checksum > 1: - # compute second checksum - i = 0; v = 1; c = 0 - while i < len(s): - c = c + v * string.index(self.chars, s[-(i+1)]) - i = i + 1; v = v + 1 - if v > 9: - v = 1 - s = s + self.chars[c % 10] - - self.encoded = 'S' + s + 'S' - - def decompose(self): - dval = "" - for c in self.encoded: - dval = dval + self.patterns[c] + 'i' - self.decomposed = dval[:-1] - return self.decomposed diff --git a/bin/reportlab/extensions/barcode/fourstate.py b/bin/reportlab/extensions/barcode/fourstate.py deleted file mode 100644 index 0751fba5c92..00000000000 --- a/bin/reportlab/extensions/barcode/fourstate.py +++ /dev/null @@ -1,81 +0,0 @@ -# -# Copyright (c) 2000 Tyler C. Sarna -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -# . 3 T Tracker -# , 2 D Descender -# ' 1 A Ascender -# | 0 H Ascender/Descender - -_rm_patterns = { - "0" : "--||", "1" : "-',|", "2" : "-'|,", "3" : "'-,|", - "4" : "'-|,", "5" : "'',,", "6" : "-,'|", "7" : "-|-|", - "8" : "-|',", "9" : "',-|", "A" : "',',", "B" : "'|-,", - "C" : "-,|'", "D" : "-|,'", "E" : "-||-", "F" : "',,'", - "G" : "',|-", "H" : "'|,-", "I" : ",-'|", "J" : ",'-|", - "K" : ",'',", "L" : "|--|", "M" : "|-',", "N" : "|'-,", - "O" : ",-|'", "P" : ",','", "Q" : ",'|-", "R" : "|-,'", - "S" : "|-|-", "T" : "|',-", "U" : ",,''", "V" : ",|-'", - "W" : ",|'-", "X" : "|,-'", "Y" : "|,'-", "Z" : "||--", - - # start, stop - "(" : "'-,'", ")" : "'|,|" -} - -_ozN_patterns = { - "0" : "||", "1" : "|'", "2" : "|,", "3" : "'|", "4" : "''", - "5" : "',", "6" : ",|", "7" : ",'", "8" : ",,", "9" : ".|" -} - -_ozC_patterns = { - "A" : "|||", "B" : "||'", "C" : "||,", "D" : "|'|", - "E" : "|''", "F" : "|',", "G" : "|,|", "H" : "|,'", - "I" : "|,,", "J" : "'||", "K" : "'|'", "L" : "'|,", - "M" : "''|", "N" : "'''", "O" : "'',", "P" : "',|", - "Q" : "','", "R" : "',,", "S" : ",||", "T" : ",|'", - "U" : ",|,", "V" : ",'|", "W" : ",''", "X" : ",',", - "Y" : ",,|", "Z" : ",,'", "a" : "|,.", "b" : "|.|", - "c" : "|.'", "d" : "|.,", "e" : "|..", "f" : "'|.", - "g" : "''.", "h" : "',.", "i" : "'.|", "j" : "'.'", - "k" : "'.,", "l" : "'..", "m" : ",|.", "n" : ",'.", - "o" : ",,.", "p" : ",.|", "q" : ",.'", "r" : ",.,", - "s" : ",..", "t" : ".|.", "u" : ".'.", "v" : ".,.", - "w" : "..|", "x" : "..'", "y" : "..,", "z" : "...", - "0" : ",,,", "1" : ".||", "2" : ".|'", "3" : ".|,", - "4" : ".'|", "5" : ".''", "6" : ".',", "7" : ".,|", - "8" : ".,'", "9" : ".,,", " " : "||.", "#" : "|'.", -} - -#http://www.auspost.com.au/futurepost/ diff --git a/bin/reportlab/extensions/barcode/out.pdf b/bin/reportlab/extensions/barcode/out.pdf deleted file mode 100644 index 52fafaae499d8b1830e02197cf98444f41cd4034..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7359 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 -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by Tyler C. Sarna. -# 4. Neither the name of the author nor the names of contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -from reportlab.lib.units import inch -from common import Barcode -import string - -_fim_patterns = { - 'A' : "|| | ||", - 'B' : "| || || |", - 'C' : "|| | | ||", - 'D' : "||| | |||", - # XXX There is an E. - # The below has been seen, but dunno if it is E or not: - # 'E' : '|||| ||||' -} - -_postnet_patterns = { - '1' : "...||", '2' : "..|.|", '3' : "..||.", '4' : ".|..|", - '5' : ".|.|.", '6' : ".||..", '7' : "|...|", '8' : "|..|.", - '9' : "|.|..", '0' : "||...", 'S' : "|", -} - - -class FIM(Barcode): - """" - FIM (Facing ID Marks) encode only one letter. - There are currently four defined: - - A Courtesy reply mail with pre-printed POSTNET - B Business reply mail without pre-printed POSTNET - C Business reply mail with pre-printed POSTNET - D OCR Readable mail without pre-printed POSTNET - - Interleaved 2 of 5 is a numeric-only barcode. It encodes an even - number of digits; if an odd number is given, a 0 is prepended. - - Options that may be passed to constructor: - - value (single character string from the set A - D. required.): - The value to encode. - - quiet (bool, default 1): - Wether to include quiet zones in the symbol. - - The following may also be passed, but doing so will generate nonstandard - symbols which should not be used. This is mainly documented here to - show the defaults: - - height (float, default 5/8 inch): - Height of the code. This might legitimately be overriden to make - a taller symbol that will 'bleed' off the edge of the paper, - leaving 5/8 inch remaining. - - lquiet (float, default 1/4 inch): - Quiet zone size to left of code, if quiet is true. - Default is the greater of .25 inch, or .15 times the symbol's - length. - - rquiet (float, default 15/32 inch): - Quiet zone size to right left of code, if quiet is true. - - Sources of information on FIM: - - USPS Publication 25, A Guide to Business Mail Preparation - http://new.usps.com/cpim/ftp/pubs/pub25.pdf - """ - - def __init__(self, value='', **args): - self.barwidth = inch * (1.0/32.0) - self.barspace = inch * (1.0/16.0) - self.height = inch * (5.0/8.0) - self.rquiet = inch * (0.25) - self.lquiet = inch * (15.0/32.0) - self.quiet = 0 - - for (k, v) in args.items(): - setattr(self, k, v) - - Barcode.__init__(self, value) - - def validate(self): - self.valid = 1 - self.validated = '' - for c in self.value: - if c in string.whitespace: - continue - elif c in "abcdABCD": - self.validated = self.validated + string.upper(c) - else: - self.valid = 0 - - if len(self.validated) != 1: - raise ValueError, "Input must be exactly one character" - - return self.validated - - def decompose(self): - self.decomposed = '' - for c in self.encoded: - self.decomposed = self.decomposed + _fim_patterns[c] - - return self.decomposed - - def computeSize(self): - self.width = (len(self.decomposed) - 1) * self.barspace + self.barwidth - if self.quiet: - self.xo = self.lquiet - self.width = self.lquiet + self.width + self.rquiet - else: - self.xo = 0.0 - - def draw(self): - left = self.xo - for c in self.decomposed: - if c == '|': - self.rect(left, 0.0, self.barwidth, self.height) - left = left + self.barspace - - -class POSTNET(Barcode): - """" - POSTNET is used in the US to encode "zip codes" (postal codes) on - mail. It can encode 5, 9, or 11 digit codes. I've read that it's - pointless to do 5 digits, since USPS will just have to re-print - them with 9 or 11 digits. - - Sources of information on POSTNET: - - USPS Publication 25, A Guide to Business Mail Preparation - http://new.usps.com/cpim/ftp/pubs/pub25.pdf - """ - - def __init__(self, value='', **args): - self.sbarheight = inch * 0.050 - self.fbarheight = inch * 0.125 - self.barwide = inch * 0.018 - self.spacewide = inch * 0.0275 - - for (k, v) in args.items(): - setattr(self, k, v) - - Barcode.__init__(self, value) - - def validate(self): - self.validated = '' - self.valid = 1 - count = 0 - for c in self.value: - if c in (string.whitespace + '-'): - pass - elif c in string.digits: - count = count + 1 - if count == 6: - self.validated = self.validated + '-' - self.validated = self.validated + c - else: - self.valid = 0 - - if len(self.validated) not in [5, 10, 12]: - self.valid = 0 - - return self.validated - - def encode(self): - self.encoded = "S" - check = 0 - for c in self.validated: - if c in string.digits: - self.encoded = self.encoded + c - check = check + string.atoi(c) - elif c == '-': - pass - else: - raise ValueError, "Invalid character in input" - check = (10 - (check % 10)) % 10 - self.encoded = self.encoded + `check` + 'S' - return self.encoded - - def decompose(self): - self.decomposed = '' - for c in self.encoded: - self.decomposed = self.decomposed + _postnet_patterns[c] - return self.decomposed - - def computeSize(self): - self.width = len(self.decomposed) * self.barwide - self.width = self.width + (len(self.decomposed) - 1) * self.spacewide - self.height = self.fbarheight - self.xo = 0.0 - - def draw(self): - sdown = self.fbarheight - self.sbarheight - left = self.xo - - for c in self.decomposed: - if c == '.': - h = self.sbarheight - else: - h = self.fbarheight - self.rect(left, 0.0, self.barwide, h) - left = left + self.barwide + self.spacewide diff --git a/bin/reportlab/fonts/00readme.txt b/bin/reportlab/fonts/00readme.txt deleted file mode 100644 index 08d545a8666..00000000000 --- a/bin/reportlab/fonts/00readme.txt +++ /dev/null @@ -1,8 +0,0 @@ -This directory is a convenient place to put -fonts where Reportlab will find them. If running -in a server environment, this is a good place to -put Type 1 fonts needed by your application. If -in a desktop environment, you might prefer to add -your font directories to the T1SearchPath in -reportlab/rl_config.py instead. - diff --git a/bin/reportlab/fonts/LeERC___.AFM b/bin/reportlab/fonts/LeERC___.AFM deleted file mode 100644 index fe491e473a2..00000000000 --- a/bin/reportlab/fonts/LeERC___.AFM +++ /dev/null @@ -1,93 +0,0 @@ -StartFontMetrics 2.0 -Comment Generated by RoboFog 08-06-2001 4:26:40 PM -FontName LettErrorRobot-Chrome -FullName LettErrorRobot-Chrome -FamilyName LettErrorRobot -Weight Medium -Notice (C) 1998-2001 LettError, Just van Rossum, Erik van Blokland, http://www.letterror.com/ -ItalicAngle 0 -IsFixedPitch false -UnderlinePosition -133 -UnderlineThickness 20 -Version 001.000 -EncodingScheme AdobeStandardEncoding -FontBBox -53 -252 1047 752 -CapHeight 647 -XHeight 548 -Descender -252 -Ascender 747 -StartCharMetrics 68 -C 32 ; WX 300 ; N space ; B 0 0 0 0 ; -C 33 ; WX 300 ; N exclam ; B 48 -52 253 652 ; -C 38 ; WX 700 ; N ampersand ; B 48 -47 653 647 ; -C 42 ; WX 700 ; N asterisk ; B 48 148 653 752 ; -C 48 ; WX 700 ; N zero ; B 53 -47 648 548 ; -C 49 ; WX 500 ; N one ; B 48 -47 453 553 ; -C 50 ; WX 600 ; N two ; B 48 -47 553 548 ; -C 51 ; WX 600 ; N three ; B 48 -147 553 548 ; -C 52 ; WX 700 ; N four ; B 48 -152 653 553 ; -C 53 ; WX 600 ; N five ; B 48 -147 553 548 ; -C 54 ; WX 600 ; N six ; B 53 -47 553 647 ; -C 55 ; WX 600 ; N seven ; B 48 -152 548 548 ; -C 56 ; WX 600 ; N eight ; B 48 -47 553 647 ; -C 57 ; WX 600 ; N nine ; B 48 -147 548 548 ; -C 63 ; WX 500 ; N question ; B 48 -52 448 647 ; -C 64 ; WX 800 ; N at ; B 53 -47 748 647 ; -C 65 ; WX 700 ; N A ; B 53 -52 648 652 ; -C 66 ; WX 600 ; N B ; B 53 -47 553 647 ; -C 67 ; WX 600 ; N C ; B 53 -47 553 647 ; -C 68 ; WX 700 ; N D ; B 53 -47 648 647 ; -C 69 ; WX 600 ; N E ; B 53 -47 553 647 ; -C 70 ; WX 600 ; N F ; B 53 -52 553 647 ; -C 71 ; WX 700 ; N G ; B 53 -47 653 647 ; -C 72 ; WX 700 ; N H ; B 53 -52 648 652 ; -C 73 ; WX 300 ; N I ; B 53 -52 248 652 ; -C 74 ; WX 300 ; N J ; B -53 -252 248 652 ; -C 75 ; WX 700 ; N K ; B 53 -52 653 652 ; -C 76 ; WX 600 ; N L ; B 53 -47 553 652 ; -C 77 ; WX 900 ; N M ; B 53 -52 848 652 ; -C 78 ; WX 700 ; N N ; B 53 -52 648 652 ; -C 79 ; WX 700 ; N O ; B 53 -47 648 647 ; -C 80 ; WX 600 ; N P ; B 53 -52 548 647 ; -C 81 ; WX 700 ; N Q ; B 53 -252 653 647 ; -C 82 ; WX 600 ; N R ; B 53 -52 653 647 ; -C 83 ; WX 600 ; N S ; B 48 -47 553 647 ; -C 84 ; WX 700 ; N T ; B 48 -52 653 647 ; -C 85 ; WX 700 ; N U ; B 53 -47 648 652 ; -C 86 ; WX 700 ; N V ; B 53 -52 648 652 ; -C 87 ; WX 1100 ; N W ; B 53 -52 1047 652 ; -C 88 ; WX 700 ; N X ; B 48 -52 653 652 ; -C 89 ; WX 700 ; N Y ; B 53 -52 648 652 ; -C 90 ; WX 700 ; N Z ; B 48 -47 653 647 ; -C 97 ; WX 600 ; N a ; B 48 -47 548 548 ; -C 98 ; WX 600 ; N b ; B 53 -47 548 752 ; -C 99 ; WX 600 ; N c ; B 53 -47 553 548 ; -C 100 ; WX 600 ; N d ; B 53 -47 548 752 ; -C 101 ; WX 600 ; N e ; B 53 -47 553 548 ; -C 102 ; WX 400 ; N f ; B -53 -52 453 747 ; -C 103 ; WX 600 ; N g ; B 48 -247 548 548 ; -C 104 ; WX 600 ; N h ; B 53 -52 548 752 ; -C 105 ; WX 300 ; N i ; B 48 -52 253 752 ; -C 106 ; WX 300 ; N j ; B -53 -252 253 752 ; -C 107 ; WX 600 ; N k ; B 53 -52 553 752 ; -C 108 ; WX 300 ; N l ; B 53 -52 248 752 ; -C 109 ; WX 900 ; N m ; B 53 -52 848 548 ; -C 110 ; WX 600 ; N n ; B 53 -52 548 548 ; -C 111 ; WX 600 ; N o ; B 53 -47 548 548 ; -C 112 ; WX 600 ; N p ; B 53 -252 548 548 ; -C 113 ; WX 600 ; N q ; B 53 -252 548 548 ; -C 114 ; WX 400 ; N r ; B 53 -52 453 553 ; -C 115 ; WX 600 ; N s ; B 48 -47 553 548 ; -C 116 ; WX 400 ; N t ; B -53 -47 453 652 ; -C 117 ; WX 600 ; N u ; B 53 -47 548 553 ; -C 118 ; WX 600 ; N v ; B 53 -52 548 553 ; -C 119 ; WX 900 ; N w ; B 53 -52 848 553 ; -C 120 ; WX 700 ; N x ; B 48 -52 653 553 ; -C 121 ; WX 600 ; N y ; B 48 -252 548 553 ; -C 122 ; WX 500 ; N z ; B -53 -47 553 548 ; -EndCharMetrics -StartKernData -StartKernPairs 0 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/bin/reportlab/fonts/LeERC___.PFB b/bin/reportlab/fonts/LeERC___.PFB deleted file mode 100644 index c1cf7ec189aa781e12da6269e10b314cb8cb99cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48590 zcmcG#V{|9qx-J^q=s5Ysw$rg~+qOEkZQD-Awr$(&*u4E;Yn^@W-uIl3chrZfdY^Z` zd}fUrbB^f;><$J5L`o>HL@QuyXJ{g7XX{MMNXNhhkT!947IAd6b5yi5v~#8vGIzAI zF##|z`~?{p;7Lh^98CEh%Ja5b>~tMBCGVnYKEakQ}dmq5_k&dS=r)|duh?(A&OMNj|h*DpHjzZsbP zlL4KPoelk8!vez6TmW_!4geD?6X2g5L~M