├── CHANGELOG.txt ├── LICENSE.txt ├── MANIFEST.in ├── PKG-INFO ├── README.txt ├── VERSION.txt ├── demo ├── cherrypy │ ├── demo-cherrypy.conf │ ├── demo-cherrypy.html │ └── demo-cherrypy.py ├── djangoproject │ ├── __init__.py │ ├── django-admin.py │ ├── ezpdf.py │ ├── manage.py │ ├── settings.py │ ├── templates │ │ ├── base.html │ │ └── entries.html │ ├── urls.py │ └── views.py ├── tgpisa │ ├── dev.cfg │ ├── sample-prod.cfg │ ├── setup.py │ ├── start-tgpisa.py │ ├── test.cfg │ └── tgpisa │ │ ├── __init__.py │ │ ├── commands.py │ │ ├── config │ │ ├── __init__.py │ │ ├── app.cfg │ │ └── log.cfg │ │ ├── controllers.py │ │ ├── json.py │ │ ├── model.py │ │ ├── release.py │ │ ├── static │ │ ├── css │ │ │ └── style.css │ │ └── images │ │ │ ├── favicon.ico │ │ │ ├── header_inner.png │ │ │ ├── info.png │ │ │ ├── ok.png │ │ │ ├── tg_under_the_hood.png │ │ │ └── under_the_hood_blue.png │ │ └── templates │ │ ├── __init__.py │ │ ├── login.kid │ │ ├── master.kid │ │ └── welcome.kid └── wsgi │ └── pisawsgidemo.py ├── doc ├── pisa-en.html ├── pisa-en.pdf ├── pisa.css └── screen.css ├── ez_setup.py ├── ho ├── __init__.py └── pisa │ └── __init__.py ├── pisa.py ├── setup.cfg ├── setup.py ├── setup_exe.py ├── setup_version.py ├── sx ├── __init__.py ├── pisa3 │ ├── __init__.py │ ├── pisa.py │ ├── pisa_context.py │ ├── pisa_default.py │ ├── pisa_document.py │ ├── pisa_paragraph.py │ ├── pisa_paragraph2.py │ ├── pisa_parser.py │ ├── pisa_pdf.py │ ├── pisa_reportlab.py │ ├── pisa_tables.py │ ├── pisa_tags.py │ ├── pisa_turbogears.py │ ├── pisa_util.py │ ├── pisa_version.py │ ├── pisa_wsgi.py │ └── reportlab_paragraph.py └── w3c │ ├── __init__.py │ ├── css.py │ ├── cssDOMElementInterface.py │ ├── cssParser.py │ └── cssSpecial.py ├── test ├── cookbook.html ├── cookbook.py ├── css │ ├── content.css │ ├── reset.css │ └── test-css-media-3.css ├── datauri.py ├── default.css ├── font │ └── README.txt ├── helloworld.py ├── img │ ├── beach.jpg │ ├── denker.png │ └── test.jpg ├── linkloading.py ├── pdf.css ├── pdf │ ├── background-sample.pdf │ └── test-invoice-bg.pdf ├── pdfform.html ├── pdfjoiner.py ├── simple-css.html ├── simple-test.html ├── simple.py ├── story2canvas.py ├── test-align.html ├── test-all.html ├── test-all │ ├── 01.jpg │ └── 02.jpg ├── test-background-img.html ├── test-background.html ├── test-barcode.html ├── test-bidirectional-text.html ├── test-blocks.html ├── test-cdata.html ├── test-cdata.xml ├── test-collapse.html ├── test-css-body.html ├── test-css-border.html ├── test-css-fontface.html ├── test-css-id.html ├── test-css-media-1.css ├── test-css-media-2.css ├── test-css-media-4.css ├── test-css-media.html ├── test-css.html ├── test-entities.html ├── test-error.html ├── test-font.html ├── test-helloworld.html ├── test-image-align.html ├── test-image.html ├── test-invoice.html ├── test-keep-with-next.html ├── test-linespacing.html ├── test-link.html ├── test-list.html ├── test-loremipsum.html ├── test-margins.html ├── test-page-box.html ├── test-para-border.html ├── test-pisa-linkfont.html ├── test-pisa-toc-entities.html ├── test-pisa-toc.html ├── test-pre.html ├── test-syntax.html ├── test-tables.html ├── test-template.html ├── test-unicode-all.html ├── test-unicode-greek.html ├── test-unicode-japanese.html ├── test-unicode-thaana.html ├── test-w3c-css.html ├── unicode.css ├── visualdiff.py └── witherror.py └── tests ├── __init__.py ├── runtests.py ├── samples ├── borders.html ├── font-and-styles.html ├── font-and-styles.pdf ├── font │ ├── CODE2000.TTF │ └── arialuni.ttf ├── images.html ├── images.pdf ├── img │ ├── denker.png │ └── tree.jpg ├── tables.html ├── tables.pdf ├── unicode.css ├── utf8.html └── utf8.pdf ├── test_parser.py ├── test_samples.py └── test_uri.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include pisa.py 2 | exclude doc/* 3 | include doc/pisa-en.pdf 4 | include doc/pisa-en.html 5 | include doc/pisa.css 6 | include doc/screen.css 7 | include demo/* 8 | exclude demo/tgproject/* 9 | recursive-include test *.html *.jpg *.py *.css *.txt 10 | include test/pdf/background-sample.pdf 11 | exclude test/archive/* 12 | global-exclude .ropeproject* *.odt *.bak *.pyc .svn* *.pyo *.ttf sitecustomize.py 13 | exclude license/* 14 | exclude dist/* 15 | exclude lib/* 16 | exclude test/font/* 17 | include test/font/README.txt 18 | exclude *.bat 19 | exclude check* 20 | exclude *.jar 21 | exclude tests/* 22 | exclude xhtml2pdf/* 23 | exclude setup_version.py 24 | exclude setup_exe.py 25 | exclude MANIFEST.in 26 | include README.txt 27 | include CHANGELOG.txt 28 | include LICENSE.txt 29 | recursive-include tests *.html *.jpg *.py *.css *.txt *.pdf 30 | exclude tests/tmp/* 31 | -------------------------------------------------------------------------------- /PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.1 2 | Name: pisa 3 | Version: 3.0.33 4 | Summary: PDF generator using HTML and CSS 5 | Home-page: http://www.xhtml2pdf.com 6 | Author: Dirk Holtwick 7 | Author-email: dirk.holtwick@gmail.com 8 | License: GNU General Public License (GPL) 9 | Download-URL: http://pypi.python.org/pypi/pisa/ 10 | Description: pisa is a html2pdf converter using the ReportLab Toolkit, 11 | the HTML5lib and pyPdf. It supports HTML 5 and CSS 2.1 (and some of CSS 3). 12 | It is completely written in pure Python so it is platform independent. 13 | The main benefit of this tool that a user with Web skills like HTML and CSS 14 | is able to generate PDF templates very quickly without learning new 15 | technologies. Easy integration into Python frameworks like CherryPy, 16 | KID Templating, TurboGears, Django, Zope, Plone, Google AppEngine (GAE) etc. 17 | (see 'demo' folder for examples) 18 | Keywords: PDF,HTML,XHTML,XML,CSS 19 | Platform: UNKNOWN 20 | Classifier: Development Status :: 5 - Production/Stable 21 | Classifier: Development Status :: 6 - Mature 22 | Classifier: Environment :: Console 23 | Classifier: Environment :: MacOS X 24 | Classifier: Environment :: Other Environment 25 | Classifier: Environment :: Web Environment 26 | Classifier: Environment :: Win32 (MS Windows) 27 | Classifier: Framework :: Django 28 | Classifier: Framework :: Plone 29 | Classifier: Framework :: Pylons 30 | Classifier: Framework :: TurboGears 31 | Classifier: Framework :: Zope2 32 | Classifier: Framework :: Zope3 33 | Classifier: Intended Audience :: Customer Service 34 | Classifier: Intended Audience :: Developers 35 | Classifier: Intended Audience :: Education 36 | Classifier: Intended Audience :: Financial and Insurance Industry 37 | Classifier: Intended Audience :: Healthcare Industry 38 | Classifier: Intended Audience :: Information Technology 39 | Classifier: Intended Audience :: Legal Industry 40 | Classifier: Intended Audience :: Manufacturing 41 | Classifier: Intended Audience :: Science/Research 42 | Classifier: Intended Audience :: System Administrators 43 | Classifier: Intended Audience :: Telecommunications Industry 44 | Classifier: License :: Free for non-commercial use 45 | Classifier: License :: OSI Approved :: GNU General Public License (GPL) 46 | Classifier: Natural Language :: English 47 | Classifier: Natural Language :: German 48 | Classifier: Operating System :: MacOS 49 | Classifier: Operating System :: MacOS :: MacOS X 50 | Classifier: Operating System :: Microsoft 51 | Classifier: Operating System :: Microsoft :: MS-DOS 52 | Classifier: Operating System :: Microsoft :: Windows 53 | Classifier: Operating System :: Other OS 54 | Classifier: Operating System :: POSIX 55 | Classifier: Operating System :: POSIX :: Linux 56 | Classifier: Operating System :: Unix 57 | Classifier: Topic :: Documentation 58 | Classifier: Topic :: Internet 59 | Classifier: Topic :: Multimedia 60 | Classifier: Topic :: Office/Business 61 | Classifier: Topic :: Office/Business :: Financial 62 | Classifier: Topic :: Office/Business :: Financial :: Accounting 63 | Classifier: Topic :: Printing 64 | Classifier: Topic :: Text Processing 65 | Classifier: Topic :: Text Processing :: Filters 66 | Classifier: Topic :: Text Processing :: Fonts 67 | Classifier: Topic :: Text Processing :: General 68 | Classifier: Topic :: Text Processing :: Indexing 69 | Classifier: Topic :: Text Processing :: Linguistic 70 | Classifier: Topic :: Text Processing :: Markup 71 | Classifier: Topic :: Text Processing :: Markup :: HTML 72 | Classifier: Topic :: Text Processing :: Markup :: XML 73 | Classifier: Topic :: Utilities 74 | Requires: html5lib 75 | Requires: pypdf 76 | Requires: pil 77 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | HELP 2 | ==== 3 | 4 | > xhtml2pdf -h 5 | 6 | REQUIREMENTS 7 | ============ 8 | 9 | - Reportlab Toolkit 2.2+ 10 | 11 | 12 | - html5lib 0.11.1+ 13 | 14 | 15 | - pyPdf 1.11+ (optional) 16 | 17 | 18 | EXAMPLES 19 | ======== 20 | 21 | > xhtml2pdf -s test\test-loremipsum.html 22 | > xhtml2pdf -s http://www.python.org 23 | > xhtml2pdf test\test-*.html 24 | 25 | PYTHON INTEGRATION 26 | ================== 27 | 28 | Some simple demos of how to integrate PISA into 29 | a Python program may be found here: test\simple.py 30 | 31 | CONTACT 32 | ======= 33 | 34 | dirk.holtwick@gmail.com 35 | 36 | LICENSE 37 | ======= 38 | 39 | Copyright 2010 Dirk Holtwick, holtwick.it 40 | 41 | Licensed under the Apache License, Version 2.0 (the "License"); 42 | you may not use this file except in compliance with the License. 43 | You may obtain a copy of the License at 44 | 45 | http://www.apache.org/licenses/LICENSE-2.0 46 | 47 | Unless required by applicable law or agreed to in writing, software 48 | distributed under the License is distributed on an "AS IS" BASIS, 49 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 50 | See the License for the specific language governing permissions and 51 | limitations under the License. -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 3.0.33 -------------------------------------------------------------------------------- /demo/cherrypy/demo-cherrypy.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | server.socket_host = "localhost" 3 | server.socket_port = 8080 4 | server.thread_pool = 10 5 | -------------------------------------------------------------------------------- /demo/cherrypy/demo-cherrypy.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | PDF Demo 8 | 9 | 10 |

PDF Demo

11 |

Please enter some HTML (and KID) code:

12 |
13 | 18 |
19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/cherrypy/demo-cherrypy.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | # -*- coding: ISO-8859-1 -*- 3 | ############################################# 4 | ## (C)opyright by Dirk Holtwick, 2008 ## 5 | ## All rights reserved ## 6 | ############################################# 7 | 8 | import cherrypy as cp 9 | import sx.pisa3 as pisa 10 | import cStringIO as StringIO 11 | 12 | try: 13 | import kid 14 | except: 15 | kid = None 16 | 17 | class PDFDemo(object): 18 | 19 | """ 20 | Simple demo showing a form where you can enter some HTML code. 21 | After sending PISA is used to convert HTML to PDF and publish 22 | it directly. 23 | """ 24 | 25 | @cp.expose 26 | def index(self): 27 | if kid: 28 | return file("demo-cherrypy.html","r").read() 29 | 30 | return """ 31 | 32 | Please enter some HTML code: 33 |
34 | 35 |
36 | 37 |
38 | 39 | """ 40 | 41 | @cp.expose 42 | def download(self, data): 43 | 44 | if kid: 45 | data = """ 46 | 48 | 50 | 51 | PDF Demo 52 | 53 | %s 54 | """ % data 55 | test = kid.Template(source=data) 56 | data = test.serialize(output='xhtml') 57 | 58 | result = StringIO.StringIO() 59 | pdf = pisa.CreatePDF( 60 | StringIO.StringIO(data), 61 | result 62 | ) 63 | if pdf.err: 64 | return "We had some errors in HTML" 65 | else: 66 | cp.response.headers["content-type"] = "application/pdf" 67 | return result.getvalue() 68 | 69 | cp.tree.mount(PDFDemo()) 70 | 71 | if __name__ == '__main__': 72 | import os.path 73 | cp.config.update(os.path.join(__file__.replace(".py", ".conf"))) 74 | cp.server.quickstart() 75 | cp.engine.start() 76 | -------------------------------------------------------------------------------- /demo/djangoproject/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/djangoproject/__init__.py -------------------------------------------------------------------------------- /demo/djangoproject/django-admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core import management 3 | 4 | if __name__ == "__main__": 5 | management.execute_from_command_line() 6 | -------------------------------------------------------------------------------- /demo/djangoproject/ezpdf.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- encoding: utf-8 -*- 3 | 4 | from django.template.loader import get_template 5 | from django.template import Context 6 | from django.http import HttpResponse 7 | import cStringIO as StringIO 8 | from sx.pisa3 import pisaDocument 9 | import cgi 10 | 11 | def render_to_pdf(template_src, context_dict): 12 | ''' 13 | Renderiza el template con el contexto. 14 | Envía al cliente la Respuesta HTTP del contenido PDF para 15 | el template renderizado. 16 | ''' 17 | template = get_template(template_src) 18 | context = Context(context_dict) 19 | html = template.render(context) 20 | result = StringIO.StringIO() 21 | pdf = pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result) 22 | if not pdf.err: 23 | return HttpResponse(result.getvalue(), mimetype='application/pdf') 24 | return HttpResponse('We had some errors
%s
' % cgi.escape(html)) 25 | -------------------------------------------------------------------------------- /demo/djangoproject/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from django.core.management import execute_manager 3 | 4 | # Patch for Python 2.5 5 | 6 | try: 7 | import sitecustomize 8 | except: 9 | pass 10 | 11 | # Set logging 12 | 13 | import logging 14 | 15 | try: 16 | logging.basicConfig( 17 | level=logging.WARN, 18 | format="%(levelname)s [%(name)s] %(pathname)s line %(lineno)d in %(funcName)s: %(message)s") 19 | except: 20 | logging.basicConfig() 21 | 22 | try: 23 | import settings # Assumed to be in the same directory. 24 | except ImportError: 25 | import sys 26 | sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) 27 | sys.exit(1) 28 | 29 | if __name__ == "__main__": 30 | execute_manager(settings) 31 | -------------------------------------------------------------------------------- /demo/djangoproject/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for the example project. 2 | 3 | import os 4 | DEBUG = True 5 | TEMPLATE_DEBUG = DEBUG 6 | ROOT_URLCONF = 'djangoproject.urls' 7 | TEMPLATE_DIRS = ( 8 | os.path.join(os.path.dirname(__file__), 'templates'), 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /demo/djangoproject/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {%block title%} 6 | {%endblock%} 7 | 8 | 23 | 24 | 25 |
26 | {%block page_header%} 27 |
28 |

pisa htmltopdf

29 |

Python module for HTML/CSS to PDF conversion - Ezpdf example

30 |
31 |
32 | {%endblock%} 33 | {%block content%} 34 | {%endblock%} 35 |
36 |
37 | {%block page_foot%} 38 | 39 | {%endblock%} 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /demo/djangoproject/templates/entries.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}{{title}}{% endblock %} 4 | 5 | {% block content %} 6 | {% for entry in blog_entries %} 7 |

{{ entry.id }} {{ entry.title|upper }}

8 |

{{ entry.body }}

9 | {% endfor %} 10 | {% endblock %} 11 | 12 | {%block page_foot%} 13 | Sample page {{block.super}} 14 | {%endblock%} 15 | 16 | -------------------------------------------------------------------------------- /demo/djangoproject/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | 3 | urlpatterns = patterns('', 4 | (r'^$', 'djangoproject.views.index'), 5 | (r'^download', 'djangoproject.views.download'), 6 | (r'^ezpdf_sample', 'djangoproject.views.ezpdf_sample'), 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /demo/djangoproject/views.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # -*- encoding: utf-8 -*- 3 | 4 | from django import http 5 | from django.shortcuts import render_to_response 6 | from django.template.loader import get_template 7 | from django.template import Context 8 | import ho.pisa as pisa 9 | import cStringIO as StringIO 10 | import cgi 11 | 12 | def index(request): 13 | return http.HttpResponse(""" 14 | 15 |

Example 1

16 | Please enter some HTML code: 17 |
18 | 19 |
20 | 21 |
22 |
23 |

Example 2

24 |

Example with template 25 | 26 | """) 27 | 28 | def download(request): 29 | if request.POST: 30 | result = StringIO.StringIO() 31 | pdf = pisa.CreatePDF( 32 | StringIO.StringIO(request.POST["data"]), 33 | result 34 | ) 35 | 36 | if not pdf.err: 37 | return http.HttpResponse( 38 | result.getvalue(), 39 | mimetype='application/pdf') 40 | 41 | return http.HttpResponse('We had some errors') 42 | 43 | def render_to_pdf(template_src, context_dict): 44 | template = get_template(template_src) 45 | context = Context(context_dict) 46 | html = template.render(context) 47 | result = StringIO.StringIO() 48 | pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result) 49 | if not pdf.err: 50 | return http.HttpResponse(result.getvalue(), mimetype='application/pdf') 51 | return http.HttpResponse('We had some errors

%s
' % cgi.escape(html)) 52 | 53 | def ezpdf_sample(request): 54 | blog_entries = [] 55 | for i in range(1,10): 56 | blog_entries.append({ 57 | 'id': i, 58 | 'title':'Playing with pisa 3.0.16 and dJango Template Engine', 59 | 'body':'This is a simple example..' 60 | }) 61 | return render_to_pdf('entries.html',{ 62 | 'pagesize':'A4', 63 | 'title':'My amazing blog', 64 | 'blog_entries':blog_entries}) 65 | -------------------------------------------------------------------------------- /demo/tgpisa/dev.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # This is where all of your settings go for your development environment 3 | # Settings that are the same for both development and production 4 | # (such as template engine, encodings, etc.) all go in 5 | # tgpisa/config/app.cfg 6 | 7 | # DATABASE 8 | 9 | # pick the form for your database 10 | # sqlobject.dburi="postgres://username@hostname/databasename" 11 | # sqlobject.dburi="mysql://username:password@hostname:port/databasename" 12 | # sqlobject.dburi="sqlite:///file_name_and_path" 13 | 14 | # If you have sqlite, here's a simple default to get you started 15 | # in development 16 | sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" 17 | 18 | 19 | # if you are using a database or table type without transactions 20 | # (MySQL default, for example), you should turn off transactions 21 | # by prepending notrans_ on the uri 22 | # sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" 23 | 24 | # for Windows users, sqlite URIs look like: 25 | # sqlobject.dburi="sqlite:///drive_letter:/path/to/file" 26 | 27 | # SERVER 28 | 29 | # Some server parameters that you may want to tweak 30 | # server.socket_port=8080 31 | 32 | # Enable the debug output at the end on pages. 33 | # log_debug_info_filter.on = False 34 | 35 | server.environment="development" 36 | autoreload.package="tgpisa" 37 | 38 | # Auto-Reload after code modification 39 | # autoreload.on = True 40 | 41 | # Set to True if you'd like to abort execution if a controller gets an 42 | # unexpected parameter. False by default 43 | tg.strict_parameters = True 44 | 45 | # LOGGING 46 | # Logging configuration generally follows the style of the standard 47 | # Python logging module configuration. Note that when specifying 48 | # log format messages, you need to use *() for formatting variables. 49 | # Deployment independent log configuration is in tgpisa/config/log.cfg 50 | [logging] 51 | 52 | [[loggers]] 53 | [[[tgpisa]]] 54 | level='DEBUG' 55 | qualname='tgpisa' 56 | handlers=['debug_out'] 57 | 58 | [[[allinfo]]] 59 | level='INFO' 60 | handlers=['debug_out'] 61 | 62 | [[[access]]] 63 | level='INFO' 64 | qualname='turbogears.access' 65 | handlers=['access_out'] 66 | propagate=0 67 | 68 | 69 | -------------------------------------------------------------------------------- /demo/tgpisa/sample-prod.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # This is where all of your settings go for your production environment. 3 | # You'll copy this file over to your production server and provide it 4 | # as a command-line option to your start script. 5 | # Settings that are the same for both development and production 6 | # (such as template engine, encodings, etc.) all go in 7 | # tgpisa/config/app.cfg 8 | 9 | # DATABASE 10 | 11 | # pick the form for your database 12 | # sqlobject.dburi="postgres://username@hostname/databasename" 13 | # sqlobject.dburi="mysql://username:password@hostname:port/databasename" 14 | # sqlobject.dburi="sqlite:///file_name_and_path" 15 | 16 | # If you have sqlite, here's a simple default to get you started 17 | # in development 18 | sqlobject.dburi="sqlite://%(current_dir_uri)s/devdata.sqlite" 19 | 20 | 21 | # if you are using a database or table type without transactions 22 | # (MySQL default, for example), you should turn off transactions 23 | # by prepending notrans_ on the uri 24 | # sqlobject.dburi="notrans_mysql://username:password@hostname:port/databasename" 25 | 26 | # for Windows users, sqlite URIs look like: 27 | # sqlobject.dburi="sqlite:///drive_letter:/path/to/file" 28 | 29 | 30 | # SERVER 31 | 32 | server.environment="production" 33 | 34 | # Sets the number of threads the server uses 35 | # server.thread_pool = 1 36 | 37 | # if this is part of a larger site, you can set the path 38 | # to the TurboGears instance here 39 | # server.webpath="" 40 | 41 | # Set to True if you are deploying your App behind a proxy 42 | # e.g. Apache using mod_proxy 43 | # base_url_filter.on = False 44 | 45 | # Set to True if your proxy adds the x_forwarded_host header 46 | # base_url_filter.use_x_forwarded_host = True 47 | 48 | # If your proxy does not add the x_forwarded_host header, set 49 | # the following to the *public* host url. 50 | # (Note: This will be overridden by the use_x_forwarded_host option 51 | # if it is set to True and the proxy adds the header correctly. 52 | # base_url_filter.base_url = "http://www.example.com" 53 | 54 | # Set to True if you'd like to abort execution if a controller gets an 55 | # unexpected parameter. False by default 56 | # tg.strict_parameters = False 57 | 58 | # LOGGING 59 | # Logging configuration generally follows the style of the standard 60 | # Python logging module configuration. Note that when specifying 61 | # log format messages, you need to use *() for formatting variables. 62 | # Deployment independent log configuration is in tgpisa/config/log.cfg 63 | [logging] 64 | 65 | [[handlers]] 66 | 67 | [[[access_out]]] 68 | # set the filename as the first argument below 69 | args="('server.log',)" 70 | class='FileHandler' 71 | level='INFO' 72 | formatter='message_only' 73 | 74 | [[loggers]] 75 | [[[tgpisa]]] 76 | level='ERROR' 77 | qualname='tgpisa' 78 | handlers=['error_out'] 79 | 80 | [[[access]]] 81 | level='INFO' 82 | qualname='turbogears.access' 83 | handlers=['access_out'] 84 | propagate=0 85 | -------------------------------------------------------------------------------- /demo/tgpisa/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup, find_packages 4 | from turbogears.finddata import find_package_data 5 | 6 | import os 7 | execfile(os.path.join("tgpisa", "release.py")) 8 | 9 | packages=find_packages() 10 | package_data = find_package_data(where='tgpisa', 11 | package='tgpisa') 12 | if os.path.isdir('locales'): 13 | packages.append('locales') 14 | package_data.update(find_package_data(where='locales', 15 | exclude=('*.po',), only_in_packages=False)) 16 | 17 | setup( 18 | name="tgpisa", 19 | version=version, 20 | # uncomment the following lines if you fill them out in release.py 21 | #description=description, 22 | #author=author, 23 | #author_email=email, 24 | #url=url, 25 | #download_url=download_url, 26 | #license=license, 27 | 28 | install_requires=[ 29 | "TurboGears >= 1.0.4.3", 30 | "SQLObject>=0.8,<=0.10.0" 31 | ], 32 | zip_safe=False, 33 | packages=packages, 34 | package_data=package_data, 35 | keywords=[ 36 | # Use keywords if you'll be adding your package to the 37 | # Python Cheeseshop 38 | 39 | # if this has widgets, uncomment the next line 40 | # 'turbogears.widgets', 41 | 42 | # if this has a tg-admin command, uncomment the next line 43 | # 'turbogears.command', 44 | 45 | # if this has identity providers, uncomment the next line 46 | # 'turbogears.identity.provider', 47 | 48 | # If this is a template plugin, uncomment the next line 49 | # 'python.templating.engines', 50 | 51 | # If this is a full application, uncomment the next line 52 | # 'turbogears.app', 53 | ], 54 | classifiers=[ 55 | 'Development Status :: 3 - Alpha', 56 | 'Operating System :: OS Independent', 57 | 'Programming Language :: Python', 58 | 'Topic :: Software Development :: Libraries :: Python Modules', 59 | 'Framework :: TurboGears', 60 | # if this is an application that you'll distribute through 61 | # the Cheeseshop, uncomment the next line 62 | # 'Framework :: TurboGears :: Applications', 63 | 64 | # if this is a package that includes widgets that you'll distribute 65 | # through the Cheeseshop, uncomment the next line 66 | # 'Framework :: TurboGears :: Widgets', 67 | ], 68 | test_suite='nose.collector', 69 | entry_points = { 70 | 'console_scripts': [ 71 | 'start-tgpisa = tgpisa.commands:start', 72 | ], 73 | }, 74 | # Uncomment next line and create a default.cfg file in your project dir 75 | # if you want to package a default configuration in your egg. 76 | #data_files = [('config', ['default.cfg'])], 77 | ) 78 | -------------------------------------------------------------------------------- /demo/tgpisa/start-tgpisa.py: -------------------------------------------------------------------------------- 1 | #!C:\Python25\python.exe 2 | # -*- coding: utf-8 -*- 3 | """Start script for the tgpisa TurboGears project. 4 | 5 | This script is only needed during development for running from the project 6 | directory. When the project is installed, easy_install will create a 7 | proper start script. 8 | """ 9 | 10 | import sys 11 | 12 | from tgpisa.commands import start, ConfigurationError 13 | 14 | if __name__ == "__main__": 15 | try: 16 | start() 17 | except ConfigurationError, exc: 18 | sys.stderr.write(str(exc)) 19 | sys.exit(1) 20 | -------------------------------------------------------------------------------- /demo/tgpisa/test.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # You can place test-specific configuration options here (like test db uri, etc) 3 | 4 | # DATABASE 5 | 6 | sqlobject.dburi = "sqlite:///:memory:" 7 | 8 | # LOGGING 9 | 10 | [logging] 11 | 12 | [[formatters]] 13 | [[[full_content]]] 14 | format='*(asctime)s *(name)s *(levelname)s *(message)s' 15 | 16 | [[handlers]] 17 | [[[test_out]]] 18 | class='StreamHandler' 19 | level='DEBUG' 20 | args='(sys.stdout,)' 21 | formatter='full_content' 22 | 23 | [[loggers]] 24 | [[[tgpisa]]] 25 | level='DEBUG' 26 | qualname='tgpisa' 27 | handlers=['test_out'] 28 | 29 | [[[turbogears]]] 30 | level='INFO' 31 | qualname='turbogears' 32 | handlers=['test_out'] 33 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/__init__.py -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/commands.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """This module contains functions called from console script entry points.""" 3 | 4 | import os 5 | import sys 6 | 7 | from os.path import dirname, exists, join 8 | 9 | import pkg_resources 10 | pkg_resources.require("TurboGears") 11 | 12 | import turbogears 13 | import cherrypy 14 | 15 | cherrypy.lowercase_api = True 16 | 17 | class ConfigurationError(Exception): 18 | pass 19 | 20 | def start(): 21 | """Start the CherryPy application server.""" 22 | 23 | setupdir = dirname(dirname(__file__)) 24 | curdir = os.getcwd() 25 | 26 | # First look on the command line for a desired config file, 27 | # if it's not on the command line, then look for 'setup.py' 28 | # in the current directory. If there, load configuration 29 | # from a file called 'dev.cfg'. If it's not there, the project 30 | # is probably installed and we'll look first for a file called 31 | # 'prod.cfg' in the current directory and then for a default 32 | # config file called 'default.cfg' packaged in the egg. 33 | if len(sys.argv) > 1: 34 | configfile = sys.argv[1] 35 | elif exists(join(setupdir, "setup.py")): 36 | configfile = join(setupdir, "dev.cfg") 37 | elif exists(join(curdir, "prod.cfg")): 38 | configfile = join(curdir, "prod.cfg") 39 | else: 40 | try: 41 | configfile = pkg_resources.resource_filename( 42 | pkg_resources.Requirement.parse("tgpisa"), 43 | "config/default.cfg") 44 | except pkg_resources.DistributionNotFound: 45 | raise ConfigurationError("Could not find default configuration.") 46 | 47 | turbogears.update_config(configfile=configfile, 48 | modulename="tgpisa.config") 49 | 50 | from tgpisa.controllers import Root 51 | 52 | turbogears.start_server(Root()) 53 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/config/__init__.py -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/config/app.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # The settings in this file should not vary depending on the deployment 3 | # environment. dev.cfg and prod.cfg are the locations for 4 | # the different deployment settings. Settings in this file will 5 | # be overridden by settings in those other files. 6 | 7 | # The commented out values below are the defaults 8 | 9 | # VIEW 10 | 11 | # which view (template engine) to use if one is not specified in the 12 | # template name 13 | # tg.defaultview = "kid" 14 | 15 | # The following kid settings determine the settings used by the kid serializer. 16 | 17 | # Kid output method (e.g. html, html-strict, xhtml, xhtml-strict, xml, json) 18 | # and formatting (e.g. default, straight, compact, newlines, wrap, nice) 19 | # kid.outputformat="html default" 20 | 21 | # kid.encoding="utf-8" 22 | 23 | # The sitetemplate is used for overall styling of a site that 24 | # includes multiple TurboGears applications 25 | # tg.sitetemplate="" 26 | 27 | # Allow every exposed function to be called as json, 28 | # tg.allow_json = False 29 | 30 | # Suppress the inclusion of the shipped MochiKit version, which is rather outdated. 31 | # Attention: setting this to True and listing 'turbogears.mochikit' in 'tg.include_widgets' 32 | # is a contradiction. This option will overrule the default-inclusion to prevent version 33 | # mismatch bugs. 34 | # tg.mochikit_suppress = True 35 | 36 | # List of Widgets to include on every page. 37 | # for example ['turbogears.mochikit'] 38 | # tg.include_widgets = [] 39 | 40 | # Set to True if the scheduler should be started 41 | # tg.scheduler = False 42 | 43 | # Set to True to allow paginate decorator redirects when page number gets 44 | # out of bound. Useful for getting the real page id in the url 45 | # paginate.redirect_on_out_of_range = True 46 | 47 | # Set to True to allow paginate decorator redirects when last page is requested. 48 | # This is useful for getting the real last page id in the url 49 | # paginate.redirect_on_last_page = True 50 | 51 | # Set session or cookie 52 | # session_filter.on = True 53 | 54 | 55 | # compress the data sends to the web browser 56 | # [/] 57 | # gzip_filter.on = True 58 | # gzip_filter.mime_types = ["application/x-javascript", "text/javascript", "text/html", "text/css", "text/plain"] 59 | 60 | [/static] 61 | static_filter.on = True 62 | static_filter.dir = "%(top_level_dir)s/static" 63 | 64 | [/favicon.ico] 65 | static_filter.on = True 66 | static_filter.file = "%(top_level_dir)s/static/images/favicon.ico" 67 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/config/log.cfg: -------------------------------------------------------------------------------- 1 | # LOGGING 2 | # Logging is often deployment specific, but some handlers and 3 | # formatters can be defined here. 4 | 5 | [logging] 6 | [[formatters]] 7 | [[[message_only]]] 8 | format='*(message)s' 9 | 10 | [[[full_content]]] 11 | format='*(asctime)s *(name)s *(levelname)s *(message)s' 12 | 13 | [[handlers]] 14 | [[[debug_out]]] 15 | class='StreamHandler' 16 | level='DEBUG' 17 | args='(sys.stdout,)' 18 | formatter='full_content' 19 | 20 | [[[access_out]]] 21 | class='StreamHandler' 22 | level='INFO' 23 | args='(sys.stdout,)' 24 | formatter='message_only' 25 | 26 | [[[error_out]]] 27 | class='StreamHandler' 28 | level='ERROR' 29 | args='(sys.stdout,)' 30 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/controllers.py: -------------------------------------------------------------------------------- 1 | from turbogears import controllers, expose, flash 2 | # from tgpisa import model 3 | import pkg_resources 4 | try: 5 | pkg_resources.require("SQLObject>=0.8,<=0.10.0") 6 | except pkg_resources.DistributionNotFound: 7 | import sys 8 | print >> sys.stderr, """You are required to install SQLObject but appear not to have done so. 9 | Please run your projects setup.py or run `easy_install SQLObject`. 10 | 11 | """ 12 | sys.exit(1) 13 | # import logging 14 | # log = logging.getLogger("tgpisa.controllers") 15 | 16 | from turbogears.decorator import weak_signature_decorator 17 | import sx.pisa3 as pisa 18 | import cStringIO as StringIO 19 | import cherrypy 20 | 21 | def pdf(filename=None, content_type="application/pdf"): 22 | def entangle(func): 23 | def decorated(func, *args, **kw): 24 | def kwpop(default, *names): 25 | for name in names: 26 | if kw.has_key(name): 27 | return kw.pop(name) 28 | return default 29 | 30 | # get the output from the decorated function 31 | output = func(*args, **kw) 32 | 33 | dst = StringIO.StringIO() 34 | result = pisa.CreatePDF( 35 | StringIO.StringIO(output), 36 | dst 37 | ) 38 | 39 | # print cherrypy.url("index.html") 40 | if not result.err: 41 | cherrypy.response.headers["Content-Type"] = content_type 42 | if filename: 43 | cherrypy.response.headers["Content-Disposition"] = "attachment; filename=" + filename 44 | output = dst.getvalue() 45 | 46 | return output 47 | return decorated 48 | return weak_signature_decorator(entangle) 49 | 50 | class Root(controllers.RootController): 51 | 52 | @expose() 53 | def index(self): 54 | import time 55 | return """Open PDF...""" 56 | 57 | @pdf(filename="test.pdf") 58 | @expose(template="tgpisa.templates.welcome") 59 | def pdf(self): 60 | import time 61 | # log.debug("Happy TurboGears Controller Responding For Duty") 62 | flash("Your application is now running") 63 | return dict(now=time.ctime()) 64 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/json.py: -------------------------------------------------------------------------------- 1 | # A JSON-based API(view) for your app. 2 | # Most rules would look like: 3 | # @jsonify.when("isinstance(obj, YourClass)") 4 | # def jsonify_yourclass(obj): 5 | # return [obj.val1, obj.val2] 6 | # @jsonify can convert your objects to following types: 7 | # lists, dicts, numbers and strings 8 | 9 | from turbojson.jsonify import jsonify 10 | 11 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/model.py: -------------------------------------------------------------------------------- 1 | from turbogears.database import PackageHub 2 | # import some basic SQLObject classes for declaring the data model 3 | # (see http://www.sqlobject.org/SQLObject.html#declaring-the-class) 4 | from sqlobject import SQLObject, SQLObjectNotFound, RelatedJoin 5 | # import some datatypes for table columns from SQLObject 6 | # (see http://www.sqlobject.org/SQLObject.html#column-types for more) 7 | from sqlobject import StringCol, UnicodeCol, IntCol, DateTimeCol 8 | 9 | __connection__ = hub = PackageHub('tgpisa') 10 | 11 | 12 | # your data model 13 | 14 | 15 | # class YourDataClass(SQLObject): 16 | # pass 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/release.py: -------------------------------------------------------------------------------- 1 | # Release information about tgpisa 2 | 3 | version = "1.0" 4 | 5 | # description = "Your plan to rule the world" 6 | # long_description = "More description about your plan" 7 | # author = "Your Name Here" 8 | # email = "YourEmail@YourDomain" 9 | # copyright = "Vintage 2006 - a good year indeed" 10 | 11 | # if it's open source, you might want to specify these 12 | # url = "http://yourcool.site/" 13 | # download_url = "http://yourcool.site/download" 14 | # license = "MIT" 15 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Quick mash-up of CSS for the TG quick start page. 3 | */ 4 | 5 | html, body { 6 | color: black; 7 | background-color: #ddd; 8 | font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | td, th {padding:3px;border:none;} 14 | tr th {text-align:left;background-color:#f0f0f0;color:#333;} 15 | tr.odd td {background-color:#edf3fe;} 16 | tr.even td {background-color:#fff;} 17 | 18 | #header { 19 | height: 80px; 20 | width: 777px; 21 | background: blue URL('../images/header_inner.png') no-repeat; 22 | border-left: 1px solid #aaa; 23 | border-right: 1px solid #aaa; 24 | margin: 0 auto 0 auto; 25 | } 26 | 27 | a.link, a, a.active { 28 | color: #369; 29 | } 30 | 31 | 32 | #main_content { 33 | color: black; 34 | font-size: 127%; 35 | background-color: white; 36 | width: 757px; 37 | margin: 0 auto 0 auto; 38 | border-left: 1px solid #aaa; 39 | border-right: 1px solid #aaa; 40 | padding: 10px; 41 | } 42 | 43 | #sidebar { 44 | border: 1px solid #aaa; 45 | background-color: #eee; 46 | margin: 0.5em; 47 | padding: 1em; 48 | float: right; 49 | width: 200px; 50 | font-size: 88%; 51 | } 52 | 53 | #sidebar h2 { 54 | margin-top: 0; 55 | } 56 | 57 | #sidebar ul { 58 | margin-left: 1.5em; 59 | padding-left: 0; 60 | } 61 | 62 | h1,h2,h3,h4,h5,h6,#getting_started_steps { 63 | font-family: "Century Schoolbook L", Georgia, serif; 64 | font-weight: bold; 65 | } 66 | 67 | h2 { 68 | font-size: 150%; 69 | } 70 | 71 | #getting_started_steps a { 72 | text-decoration: none; 73 | } 74 | 75 | #getting_started_steps a:hover { 76 | text-decoration: underline; 77 | } 78 | 79 | #getting_started_steps li { 80 | font-size: 80%; 81 | margin-bottom: 0.5em; 82 | } 83 | 84 | #getting_started_steps h2 { 85 | font-size: 120%; 86 | } 87 | 88 | #getting_started_steps p { 89 | font: 100% "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif; 90 | } 91 | 92 | #footer { 93 | border: 1px solid #aaa; 94 | border-top: 0px none; 95 | color: #999; 96 | background-color: white; 97 | padding: 10px; 98 | font-size: 80%; 99 | text-align: center; 100 | width: 757px; 101 | margin: 0 auto 1em auto; 102 | } 103 | 104 | .code { 105 | font-family: monospace; 106 | } 107 | 108 | span.code { 109 | font-weight: bold; 110 | background: #eee; 111 | } 112 | 113 | #status_block { 114 | margin: 0 auto 0.5em auto; 115 | padding: 15px 10px 15px 55px; 116 | background: #cec URL('../images/ok.png') left center no-repeat; 117 | border: 1px solid #9c9; 118 | width: 450px; 119 | font-size: 120%; 120 | font-weight: bolder; 121 | } 122 | 123 | .notice { 124 | margin: 0.5em auto 0.5em auto; 125 | padding: 15px 10px 15px 55px; 126 | width: 450px; 127 | background: #eef URL('../images/info.png') left center no-repeat; 128 | border: 1px solid #cce; 129 | } 130 | 131 | .fielderror { 132 | color: red; 133 | font-weight: bold; 134 | } -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/favicon.ico -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/header_inner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/header_inner.png -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/info.png -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/ok.png -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/tg_under_the_hood.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/tg_under_the_hood.png -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/static/images/under_the_hood_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/static/images/under_the_hood_blue.png -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/demo/tgpisa/tgpisa/templates/__init__.py -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/templates/login.kid: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | 9 | Login 10 | 73 | 74 | 75 | 76 |
77 |

Login

78 |

${message}

79 |
80 | 81 | 82 | 85 | 88 | 89 | 90 | 93 | 96 | 97 | 98 | 101 | 102 |
83 | 84 | 86 | 87 |
91 | 92 | 94 | 95 |
99 | 100 |
103 | 104 | 106 | 107 |
108 | 110 |
111 |
112 |
113 | 114 | 115 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/templates/master.kid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | Your title goes here 9 | 10 | 18 | 20 | 21 | 22 | 23 |
24 | 25 | Login 26 | 27 | 28 | Welcome ${tg.identity.user.display_name or tg.identity.user.user_name}. 29 | Logout 30 | 31 |
32 | 33 | 34 | 35 |
36 |
38 |
page content
39 |
40 | 41 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /demo/tgpisa/tgpisa/templates/welcome.kid: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Welcome to TurboGears 7 | 8 | 9 | 10 | 23 |
24 |
    25 |
  1. 26 |

    Model

    27 |

    Design models in the model.py.
    28 | Edit dev.cfg to use a different backend, or start with a pre-configured SQLite database.
    29 | Use script tg-admin sql create to create the database tables.

    30 |
  2. 31 |
  3. 32 |

    View

    33 |

    Edit html-like templates in the /templates folder;
    34 | Put all static contents in the /static folder.

    35 |
  4. 36 |
  5. 37 |

    Controller

    38 |

    Edit controllers.py and build your 39 | website structure with the simplicity of Python objects.
    40 | TurboGears will automatically reload itself when you modify your project.

    41 |
  6. 42 |
43 |
If you create something cool, please let people know, and consider contributing something back to the community.
44 |
45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /demo/wsgi/pisawsgidemo.py: -------------------------------------------------------------------------------- 1 | #!/bin/python2.5 2 | # -*- coding: UTF-8 -*- 3 | 4 | # Copyright 2010 Dirk Holtwick, holtwick.it 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # Copyright 2010 Dirk Holtwick, holtwick.it 18 | # 19 | # Licensed under the Apache License, Version 2.0 (the "License"); 20 | # you may not use this file except in compliance with the License. 21 | # You may obtain a copy of the License at 22 | # 23 | # http://www.apache.org/licenses/LICENSE-2.0 24 | # 25 | # Unless required by applicable law or agreed to in writing, software 26 | # distributed under the License is distributed on an "AS IS" BASIS, 27 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | # See the License for the specific language governing permissions and 29 | # limitations under the License. 30 | 31 | 32 | __version__ = "$Revision: 103 $" 33 | __author__ = "$Author: holtwick $" 34 | __date__ = "$Date: 2007-10-31 17:08:54 +0100 (Mi, 31 Okt 2007) $" 35 | __svnid__ = "$Id: pisa.py 103 2007-10-31 16:08:54Z holtwick $" 36 | 37 | from wsgiref.simple_server import make_server 38 | import logging 39 | 40 | import sx.pisa3.pisa_wsgi as pisa_wsgi 41 | 42 | def SimpleApp(environ, start_response): 43 | 44 | # That's the magic! 45 | # 46 | # Set the environment variable "pisa.topdf" to the filename 47 | # you would like to have for the resulting PDF 48 | environ["pisa.topdf"] = "index.pdf" 49 | 50 | # Simple Hello World example 51 | start_response( 52 | '200 OK', [ 53 | ('content-type', "text/html"), 54 | ]) 55 | return ["Hello World"] 56 | 57 | if __name__ == '__main__': 58 | 59 | HOST = '' 60 | PORT = 8080 61 | logging.basicConfig(level=logging.DEBUG) 62 | 63 | app = SimpleApp 64 | 65 | # Add PISA WSGI Middleware 66 | app = pisa_wsgi.PisaMiddleware(app) 67 | 68 | httpd = make_server(HOST, PORT, app) 69 | print "Serving HTTP on port %d..." % PORT 70 | httpd.serve_forever() 71 | -------------------------------------------------------------------------------- /doc/pisa-en.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/doc/pisa-en.pdf -------------------------------------------------------------------------------- /doc/pisa.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | 3 | @font-face { 4 | font-family: "DejaVu Sans Mono", dejamono; 5 | src: url(DejaVuSansMono.ttf); 6 | } 7 | 8 | @font-face { 9 | font-family: "DejaVu Sans Mono", dejamono; 10 | font-weight: bold; 11 | src: url(DejaVuSansMono-Bold.ttf); 12 | } 13 | 14 | @font-face { 15 | font-family: Calibri; 16 | src: url("CALIBRI.TTF"); 17 | } 18 | 19 | @font-face { 20 | font-family: Calibri; 21 | src: url("CALIBRIB.TTF"); 22 | font-weight: bold; 23 | } 24 | 25 | @font-face { 26 | font-family: Calibri; 27 | src: url("CALIBRII.TTF"); 28 | font-style: italic; 29 | } 30 | 31 | @font-face { 32 | font-family: Calibri; 33 | src: url("CALIBRIZ.TTF"); 34 | font-weight: bold; 35 | font-style: italic; 36 | } 37 | 38 | @page { 39 | 40 | -pdf-page-orientation: portrait; 41 | -pdf-page-size: a4; 42 | 43 | /* background-image: url(background.pdf); */ 44 | 45 | margin-top: 2cm; 46 | margin-left: 4cm; 47 | margin-right: 2cm; 48 | margin-bottom: 3cm; 49 | 50 | } 51 | 52 | @page regular { 53 | 54 | -pdf-page-orientation: portrait; 55 | -pdf-page-size: a4; 56 | 57 | /* background-image: url(background.pdf); */ 58 | 59 | margin-top: 2cm; 60 | margin-left: 4cm; 61 | margin-right: 2cm; 62 | margin-bottom: 3cm; 63 | 64 | @frame footer { 65 | -pdf-frame-content: footer; 66 | bottom: 2cm; 67 | left: 4cm; 68 | right: 2cm; 69 | height: 1cm; 70 | } 71 | 72 | } 73 | 74 | } 75 | 76 | html { 77 | font-family: Calibri, Arial, Helvetica, sans-serif; 78 | font-size: 10pt; 79 | } 80 | 81 | h1 { 82 | page-break-before: always; 83 | border-bottom: 2px solid #ccc; 84 | } 85 | 86 | pre, code { 87 | background: #eee; 88 | font-family: dejamono, "DejaVu Sans Mono", "Courier New", Courier, monospace; 89 | font-size: 8pt; 90 | } 91 | 92 | code { 93 | font-weight: bold; 94 | color: #333; 95 | /* text-decoration: underline; */ 96 | font-size: 8pt; 97 | } 98 | 99 | .del { 100 | text-decoration: line-through; 101 | } 102 | 103 | p, pre { 104 | margin-top: 1em; 105 | margin-bottom: 1em; 106 | } 107 | 108 | .license { 109 | font-size: 8px; 110 | line-height: 110%; 111 | } 112 | 113 | 114 | .title { 115 | font-size: 300%; 116 | font-weight: bold; 117 | margin-top: 20pt; 118 | margin-bottom: 20pt; 119 | } 120 | 121 | .subtitle { 122 | font-size: 150%; 123 | font-weight: bold; 124 | margin-top: 20pt; 125 | margin-bottom: 20pt; 126 | } 127 | 128 | #footer { 129 | text-align: right; 130 | } 131 | 132 | i, em { 133 | font-style: normal; 134 | color: #990000; 135 | font-weight: bold; 136 | } 137 | 138 | a { 139 | color: #000066; 140 | } 141 | 142 | pdftoc { 143 | } 144 | 145 | pdftoc.pdftoclevel0 { 146 | font-weight: bold; 147 | margin-top: 0.5em; 148 | } 149 | 150 | pdftoc.pdftoclevel1 { 151 | margin-left: 1em; 152 | } 153 | 154 | pdftoc.pdftoclevel2 { 155 | margin-left: 1em; 156 | font-weight: normal; 157 | } 158 | 159 | h2 { 160 | margin-top: 1.5em; 161 | margin-bottom: 0.5em; 162 | font-size: 125%; 163 | color: #666; 164 | } 165 | 166 | h3 { 167 | font-size: 100%; 168 | color: #000000; 169 | margin-bottom: 0.5em; 170 | } 171 | -------------------------------------------------------------------------------- /doc/screen.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 2em; 3 | counter-reset: Ebene01; 4 | } 5 | h1:before { 6 | content: counter(Ebene01) " "; 7 | counter-increment: Ebene01; 8 | } 9 | h1 { 10 | counter-reset: Ebene02; 11 | background: #CCCC99; 12 | } 13 | h2:before { 14 | content: counter(Ebene01) "." counter(Ebene02) " "; 15 | counter-increment: Ebene02; 16 | } 17 | h2 { 18 | border-bottom: 2px solid #CCCC99; 19 | } 20 | pre { 21 | padding: 0.5em; 22 | } 23 | a, code { 24 | background: #CCCC99; 25 | color: #333333; 26 | } 27 | 28 | code { 29 | font-weight: bold; 30 | } 31 | 32 | @media screen { 33 | .pdf { 34 | display: none; 35 | } 36 | } -------------------------------------------------------------------------------- /ho/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 128 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-01-10 21:26:42 +0100 (Do, 10 Jan 2008) $" 20 | __svnid__ = "$Id: __init__.py 128 2008-01-10 20:26:42Z holtwick $" 21 | 22 | # Also look in other packages with the same name 23 | 24 | from pkgutil import extend_path 25 | __path__ = extend_path(__path__, __name__) 26 | -------------------------------------------------------------------------------- /ho/pisa/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | In the near future pisa will be moved into the namespace "ho" 19 | This is a helper for migration 20 | """ 21 | 22 | __reversion__ = "$Revision: 160 $" 23 | __author__ = "$Author: holtwick $" 24 | __date__ = "$Date: 2008-03-12 16:40:03 +0100 (Mi, 12 Mrz 2008) $" 25 | 26 | from sx.pisa3.pisa import * 27 | 28 | __version__ = VERSION 29 | -------------------------------------------------------------------------------- /pisa.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/python 2 | # -*- coding: ISO-8859-1 -*- 3 | 4 | # Copyright 2010 Dirk Holtwick, holtwick.it 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | __version__ = "$Revision: 221 $" 19 | __author__ = "$Author: holtwick $" 20 | __date__ = "$Date: 2008-05-31 18:56:27 +0200 (Sa, 31 Mai 2008) $" 21 | __svnid__ = "$Id: pisa.py 221 2008-05-31 16:56:27Z holtwick $" 22 | 23 | import ho.pisa as pisa 24 | pisa.command() 25 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [install] 2 | 3 | compile = 1 4 | optimize = 1 5 | 6 | [sdist] 7 | 8 | formats = gztar, zip 9 | 10 | [bdist] 11 | 12 | formats = wininst -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: ISO-8859-1 -*- 3 | 4 | # Copyright 2010 Dirk Holtwick, holtwick.it 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | __version__ = "$Revision: 247 $" 19 | __author__ = "$Author: holtwick $" 20 | __date__ = "$Date: 2008-08-15 13:37:57 +0200 (Fr, 15 Aug 2008) $" 21 | __svnid__ = "$Id: setup.py 247 2008-08-15 11:37:57Z holtwick $" 22 | 23 | try: 24 | from setuptools import setup, find_packages 25 | except ImportError: 26 | from ez_setup import use_setuptools 27 | use_setuptools() 28 | from setuptools import setup, find_packages 29 | 30 | setup( 31 | name = "pisa", 32 | version = "VERSION{3.0.33}VERSION"[8:-8], 33 | description = "PDF generator using HTML and CSS", 34 | license = "Apache License 2.0", 35 | author = "Dirk Holtwick", 36 | author_email = "dirk.holtwick@gmail.com", 37 | url = "http://www.xhtml2pdf.com", 38 | download_url = "http://pypi.python.org/pypi/pisa/", 39 | keywords = "PDF, HTML, XHTML, XML, CSS", 40 | 41 | requires = ["html5lib", "pypdf", "pil"], #, "reportlab"], 42 | 43 | include_package_data = False, 44 | 45 | packages = [ 46 | 'ho', 47 | 'ho.pisa', 48 | 'sx', 49 | 'sx.pisa3', 50 | 'sx.w3c', 51 | ], 52 | 53 | test_suite = "tests", 54 | 55 | entry_points = { 56 | 'console_scripts': [ 57 | 'pisa = sx.pisa3:command', 58 | 'xhtml2pdf = sx.pisa3:command', 59 | ] 60 | }, 61 | 62 | long_description = """ 63 | pisa is a html2pdf converter using the ReportLab Toolkit, 64 | the HTML5lib and pyPdf. It supports HTML 5 and CSS 2.1 (and some of CSS 3). 65 | It is completely written in pure Python so it is platform independent. 66 | The main benefit of this tool that a user with Web skills like HTML and CSS 67 | is able to generate PDF templates very quickly without learning new 68 | technologies. Easy integration into Python frameworks like CherryPy, 69 | KID Templating, TurboGears, Django, Zope, Plone, Google AppEngine (GAE) etc. 70 | (see 'demo' folder for examples) 71 | """.strip(), 72 | 73 | classifiers = [x.strip() for x in """ 74 | License :: Freeware 75 | License :: OSI Approved 76 | License :: OSI Approved :: Apache Software License 77 | Development Status :: 5 - Production/Stable 78 | Development Status :: 6 - Mature 79 | Environment :: Console 80 | Environment :: MacOS X 81 | Environment :: Other Environment 82 | Environment :: Web Environment 83 | Environment :: Win32 (MS Windows) 84 | Framework :: Django 85 | Framework :: Plone 86 | Framework :: Pylons 87 | Framework :: TurboGears 88 | Framework :: Zope2 89 | Framework :: Zope3 90 | Intended Audience :: Customer Service 91 | Intended Audience :: Developers 92 | Intended Audience :: Education 93 | Intended Audience :: Financial and Insurance Industry 94 | Intended Audience :: Healthcare Industry 95 | Intended Audience :: Information Technology 96 | Intended Audience :: Legal Industry 97 | Intended Audience :: Manufacturing 98 | Intended Audience :: Science/Research 99 | Intended Audience :: System Administrators 100 | Intended Audience :: Telecommunications Industry 101 | Natural Language :: English 102 | Natural Language :: German 103 | Operating System :: MacOS 104 | Operating System :: MacOS :: MacOS X 105 | Operating System :: Microsoft 106 | Operating System :: Microsoft :: MS-DOS 107 | Operating System :: Microsoft :: Windows 108 | Operating System :: Other OS 109 | Operating System :: POSIX 110 | Operating System :: POSIX :: Linux 111 | Operating System :: Unix 112 | Topic :: Documentation 113 | Topic :: Internet 114 | Topic :: Multimedia 115 | Topic :: Office/Business 116 | Topic :: Office/Business :: Financial 117 | Topic :: Office/Business :: Financial :: Accounting 118 | Topic :: Printing 119 | Topic :: Text Processing 120 | Topic :: Text Processing :: Filters 121 | Topic :: Text Processing :: Fonts 122 | Topic :: Text Processing :: General 123 | Topic :: Text Processing :: Indexing 124 | Topic :: Text Processing :: Linguistic 125 | Topic :: Text Processing :: Markup 126 | Topic :: Text Processing :: Markup :: HTML 127 | Topic :: Text Processing :: Markup :: XML 128 | Topic :: Utilities 129 | """.strip().splitlines()], 130 | 131 | ) 132 | -------------------------------------------------------------------------------- /setup_exe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 247 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-08-15 13:37:57 +0200 (Fr, 15 Aug 2008) $" 20 | __svnid__ = "$Id: setup_exe.py 247 2008-08-15 11:37:57Z holtwick $" 21 | 22 | """ 23 | Optimiert für den Einsatz mit py2exe Version 0.5.4 24 | """ 25 | 26 | import zipfile 27 | from distutils.core import setup 28 | import py2exe 29 | 30 | VERSION = "VERSION{3.0.33}VERSION"[8:-8] 31 | 32 | class Target: 33 | def __init__(self, **kw): 34 | self.__dict__.update(kw) 35 | self.version = VERSION 36 | self.company_name = "Dirk Holtwick" 37 | self.copyright = "(c) Dirk Holtwick, 2002-2010" 38 | self.name = "xhtml2pdf" 39 | self.description = "xhtml2pdf" 40 | 41 | setup( 42 | 43 | console = [{ 44 | 'script': "pisa.py", 45 | }], 46 | 47 | options = { 48 | "py2exe": { 49 | # "optimize" : 2, 50 | "excludes" : ["Tkinter","Tix"], 51 | "includes": [ 52 | "encodings", 53 | "encodings.*", 54 | 'ho', 55 | 'ho.pisa', 56 | "sx", 57 | "sx.pisa3", 58 | "sx.w3c", 59 | "html5lib", 60 | "html5lib.treebuilders.*", 61 | "html5lib.treewalkers.*", 62 | "reportlab", 63 | ], 64 | "dist_dir" : "exe", 65 | "bundle_files": 1, 66 | } 67 | }, 68 | 69 | name = "pisa", 70 | version = "VERSION{3.0.33}VERSION"[8:-8], 71 | description = "PDF generator using HTML and CSS", 72 | license = "GNU General Public License (GPL)", 73 | author = "Dirk Holtwick", 74 | author_email = "dirk.holtwick@gmail.com", 75 | url = "http://www.xhtml2pdf.com", 76 | download_url = "http://pypi.python.org/pypi/pisa/", 77 | keywords = "HTML, CSS, PDF", 78 | 79 | requires = ["html5lib"], #, "reportlab"], 80 | 81 | packages = [ 82 | 'ho', 83 | 'ho.pisa', 84 | 'sx', 85 | 'sx.pisa3', 86 | 'sx.w3c', 87 | ], 88 | 89 | long_description = """ 90 | pisa is a html2pdf converter using the ReportLab Toolkit, 91 | the HTML5lib and pyPdf. It supports HTML 5 and CSS 2.1 (and some of CSS 3). 92 | It is completely written in pure Python so it is platform independent. 93 | The main benefit of this tool that a user with Web skills like HTML and CSS 94 | is able to generate PDF templates very quickly without learning new 95 | technologies. Easy integration into Python frameworks like CherryPy, 96 | KID Templating, TurboGears, Django, Zope, Plone, Google AppEngine (GAE) etc. 97 | (see 'demo' folder for examples) 98 | """.strip(), 99 | 100 | classifiers = [x.strip() for x in """ 101 | Development Status :: 5 - Production/Stable 102 | Development Status :: 6 - Mature 103 | Environment :: Console 104 | Environment :: MacOS X 105 | Environment :: Other Environment 106 | Environment :: Web Environment 107 | Environment :: Win32 (MS Windows) 108 | Framework :: Django 109 | Framework :: Plone 110 | Framework :: Pylons 111 | Framework :: TurboGears 112 | Framework :: Zope2 113 | Framework :: Zope3 114 | Intended Audience :: Customer Service 115 | Intended Audience :: Developers 116 | Intended Audience :: Education 117 | Intended Audience :: Financial and Insurance Industry 118 | Intended Audience :: Healthcare Industry 119 | Intended Audience :: Information Technology 120 | Intended Audience :: Legal Industry 121 | Intended Audience :: Manufacturing 122 | Intended Audience :: Science/Research 123 | Intended Audience :: System Administrators 124 | Intended Audience :: Telecommunications Industry 125 | License :: Free for non-commercial use 126 | License :: OSI Approved :: GNU General Public License (GPL) 127 | Natural Language :: English 128 | Natural Language :: German 129 | Operating System :: MacOS 130 | Operating System :: MacOS :: MacOS X 131 | Operating System :: Microsoft 132 | Operating System :: Microsoft :: MS-DOS 133 | Operating System :: Microsoft :: Windows 134 | Operating System :: Other OS 135 | Operating System :: POSIX 136 | Operating System :: POSIX :: Linux 137 | Operating System :: Unix 138 | Topic :: Documentation 139 | Topic :: Internet 140 | Topic :: Multimedia 141 | Topic :: Office/Business 142 | Topic :: Office/Business :: Financial 143 | Topic :: Office/Business :: Financial :: Accounting 144 | Topic :: Printing 145 | Topic :: Text Processing 146 | Topic :: Text Processing :: Filters 147 | Topic :: Text Processing :: Fonts 148 | Topic :: Text Processing :: General 149 | Topic :: Text Processing :: Indexing 150 | Topic :: Text Processing :: Linguistic 151 | Topic :: Text Processing :: Markup 152 | Topic :: Text Processing :: Markup :: HTML 153 | Topic :: Text Processing :: Markup :: XML 154 | Topic :: Utilities 155 | """.strip().splitlines()], 156 | 157 | ) 158 | 159 | 160 | import os, os.path, shutil 161 | 162 | SDIST = "pisa-" + VERSION + "\\" 163 | DIR = "pisa-" + VERSION + "-windows" 164 | # os.makedirs(DIR) 165 | os.rename("exe", DIR) 166 | shutil.copy(r"LICENSE.txt", DIR) 167 | shutil.copy(r"LICENSE.pdf", DIR) 168 | shutil.copy(r"README-WINDOWS.TXT", DIR + "\\README.txt") 169 | shutil.copytree(SDIST + "doc", DIR + "\\doc") 170 | shutil.copytree(SDIST + "test", DIR + "\\test") 171 | os.system(r"zip -r dist\xhtml2pdf-windows.zip pisa-" + VERSION + "-windows") 172 | -------------------------------------------------------------------------------- /setup_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | """ 18 | Updates the version infos 19 | """ 20 | 21 | import time 22 | import re 23 | import cgi 24 | 25 | VERSION = open("VERSION.txt", "r").read().strip() 26 | BUILD = time.strftime("%Y-%m-%d") 27 | FILES = [ 28 | "setup.py", 29 | "setup_exe.py", 30 | # "setup_egg.py", 31 | "sx/pisa3/pisa_version.py", 32 | "doc/pisa-en.html", 33 | ] 34 | try: 35 | HELP = cgi.escape(open("HELP.txt", "r").read(), 1) 36 | except: 37 | HELP = "" 38 | HELP = "
" + HELP + "
" 39 | 40 | rxversion = re.compile("VERSION{.*?}VERSION", re.MULTILINE|re.IGNORECASE|re.DOTALL) 41 | rxbuild = re.compile("BUILD{.*?}BUILD", re.MULTILINE|re.IGNORECASE|re.DOTALL) 42 | rxversionhtml = re.compile("\<\!--VERSION--\>.*?\<\!--VERSION--\>", re.MULTILINE|re.IGNORECASE|re.DOTALL) 43 | rxhelphtml = re.compile("\<\!--HELP--\>.*?\<\!--HELP--\>", re.MULTILINE|re.IGNORECASE|re.DOTALL) 44 | 45 | for fname in FILES: 46 | print "Update", fname, "..." 47 | data = open(fname, "rb").read() 48 | data = rxversion.sub("VERSION{" + VERSION + "}VERSION", data) 49 | data = rxversionhtml.sub("" + VERSION + "", data) 50 | data = rxbuild.sub("BUILD{" + BUILD + "}BUILD", data) 51 | data = rxhelphtml.sub(HELP, data) 52 | open(fname, "wb").write(data) 53 | 54 | -------------------------------------------------------------------------------- /sx/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 128 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-01-10 21:26:42 +0100 (Do, 10 Jan 2008) $" 20 | __svnid__ = "$Id: __init__.py 128 2008-01-10 20:26:42Z holtwick $" 21 | 22 | # Also look in other packages with the same name 23 | 24 | from pkgutil import extend_path 25 | __path__ = extend_path(__path__, __name__) 26 | -------------------------------------------------------------------------------- /sx/pisa3/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __reversion__ = "$Revision: 238 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-06-26 20:06:02 +0200 (Do, 26 Jun 2008) $" 20 | 21 | REQUIRED_INFO = """ 22 | **************************************************** 23 | IMPORT ERROR! 24 | %s 25 | **************************************************** 26 | 27 | The following Python packages are required for PISA: 28 | - Reportlab Toolkit >= 2.2 29 | - HTML5lib >= 0.11.1 30 | 31 | Optional packages: 32 | - pyPDF 33 | - PIL 34 | 35 | """.lstrip() 36 | 37 | import logging 38 | log = logging.getLogger(__name__) 39 | 40 | try: 41 | from pisa import * 42 | if not REPORTLAB22: 43 | raise ImportError, "Reportlab Toolkit Version 2.2 or higher needed" 44 | except ImportError, e: 45 | import sys 46 | sys.stderr.write(REQUIRED_INFO % e) 47 | log.error(REQUIRED_INFO % e) 48 | raise 49 | 50 | __version__ = VERSION 51 | -------------------------------------------------------------------------------- /sx/pisa3/pisa.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/sx/pisa3/pisa.py -------------------------------------------------------------------------------- /sx/pisa3/pisa_document.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | __reversion__ = "$Revision: 20 $" 19 | __author__ = "$Author: holtwick $" 20 | __date__ = "$Date: 2007-10-09 12:58:24 +0200 (Di, 09 Okt 2007) $" 21 | 22 | from pisa_context import pisaContext 23 | from pisa_parser import pisaParser 24 | from pisa_util import * 25 | from pisa_reportlab import * 26 | from pisa_default import DEFAULT_CSS 27 | 28 | from reportlab.platypus.flowables import Spacer 29 | 30 | #import os 31 | #import types 32 | #import cgi 33 | #import logging 34 | 35 | log = logging.getLogger("ho.pisa") 36 | 37 | def pisaErrorDocument(dest, c): 38 | out = pisaTempFile(capacity=c.capacity) 39 | out.write("

%d error(s) occured:

" % c.err) 40 | for mode, line, msg, code in c.log: 41 | if mode=="error": 42 | out.write("

%s in line %d: %s
" % (mode, line, cgi.escape(msg))) 43 | 44 | out.write("

%d warning(s) occured:

" % c.warn) 45 | for mode, line, msg, code in c.log: 46 | if mode=="warning": 47 | out.write("

%s in line %d: %s

" % (mode, line, cgi.escape(msg))) 48 | 49 | return pisaDocument(out.getvalue(), dest, raise_exception=False) 50 | 51 | def pisaStory( 52 | src, 53 | path = None, 54 | link_callback = None, 55 | debug = 0, 56 | default_css = None, 57 | xhtml = False, 58 | encoding = None, 59 | c = None, 60 | xml_output = None, 61 | **kw): 62 | 63 | # Prepare Context 64 | if not c: 65 | c = pisaContext(path, debug=debug) 66 | c.pathCallback = link_callback 67 | 68 | # Use a default set of CSS definitions to get an expected output 69 | if default_css is None: 70 | default_css = DEFAULT_CSS 71 | 72 | # Parse and fill the story 73 | pisaParser(src, c, default_css, xhtml, encoding, xml_output) 74 | 75 | #if 0: 76 | # import reportlab.pdfbase.pdfmetrics as pm 77 | # pm.dumpFontData() 78 | 79 | # Avoid empty documents 80 | if not c.story: 81 | c.story = [Spacer(1,1)] 82 | # c.addPara(force=True) 83 | 84 | # Remove anchors if they do not exist (because of a bug in Reportlab) 85 | for frag, anchor in c.anchorFrag: 86 | if anchor not in c.anchorName: 87 | frag.link = None 88 | 89 | return c 90 | 91 | def pisaDocument( 92 | src, 93 | dest = None, 94 | path = None, 95 | link_callback = None, 96 | debug = 0, 97 | show_error_as_pdf = False, 98 | default_css = None, 99 | xhtml = False, 100 | encoding = None, 101 | xml_output = None, 102 | raise_exception = True, 103 | capacity = 100 * 1024, # -1, 104 | **kw): 105 | 106 | c = None 107 | if show_error_as_pdf: 108 | raise_exception = False 109 | 110 | try: 111 | 112 | log.debug("pisaDocument options:\n src = %r\n dest = %r\n path = %r\n link_callback = %r\n xhtml = %r", 113 | src, 114 | dest, 115 | path, 116 | link_callback, 117 | xhtml) 118 | 119 | # Prepare simple context 120 | c = pisaContext(path, debug=debug, capacity=capacity) 121 | c.pathCallback = link_callback 122 | 123 | if dest is None: 124 | dest = pisaTempFile(capacity=c.capacity) 125 | c.dest = dest 126 | 127 | # Build story 128 | c = pisaStory(src, path, link_callback, debug, default_css, xhtml, encoding, c=c, xml_output=xml_output) 129 | 130 | # Buffer PDF into memory 131 | out = pisaTempFile(capacity=c.capacity) 132 | 133 | doc = PmlBaseDoc( 134 | out, 135 | pagesize = c.pageSize, 136 | author = c.meta["author"].strip(), 137 | subject = c.meta["subject"].strip(), 138 | keywords = [x.strip() for x in c.meta["keywords"].strip().split(",") if x], 139 | title = c.meta["title"].strip(), 140 | showBoundary = 0, 141 | allowSplitting = 1) 142 | 143 | # XXX It is not possible to access PDF info, because it is private in canvas 144 | # doc.info.producer = "pisa " 145 | 146 | # Prepare templates and their frames 147 | if c.templateList.has_key("body"): 148 | body = c.templateList["body"] 149 | del c.templateList["body"] 150 | else: 151 | x, y, w, h = getBox("1cm 1cm -1cm -1cm", c.pageSize) 152 | body = PmlPageTemplate( 153 | id="body", 154 | frames=[ 155 | Frame(x, y, w, h, 156 | id = "body", 157 | leftPadding = 0, 158 | rightPadding = 0, 159 | bottomPadding = 0, 160 | topPadding = 0)], 161 | pagesize = c.pageSize) 162 | 163 | # print body.frames 164 | 165 | # print [body] + c.templateList.values() 166 | doc.addPageTemplates([body] + c.templateList.values()) 167 | 168 | # Use multibuild e.g. if a TOC has to be created 169 | if c.multiBuild: 170 | doc.multiBuild(c.story) 171 | else: 172 | doc.build(c.story) 173 | 174 | # Add watermarks 175 | if pyPdf: 176 | for bgouter in c.pisaBackgroundList: 177 | 178 | # If we have at least one background, then lets do it 179 | if bgouter: 180 | 181 | istream = out 182 | try: 183 | output = pyPdf.PdfFileWriter() 184 | input1 = pyPdf.PdfFileReader(istream) 185 | ctr = 0 186 | for bg in c.pisaBackgroundList: 187 | page = input1.getPage(ctr) 188 | if bg and not bg.notFound() and (bg.mimetype=="application/pdf"): 189 | bginput = pyPdf.PdfFileReader(bg.getFile()) 190 | pagebg = bginput.getPage(0) 191 | pagebg.mergePage(page) 192 | page = pagebg 193 | else: 194 | log.warn(c.warning("Background PDF %s doesn't exist.", bg)) 195 | output.addPage(page) 196 | ctr += 1 197 | out = pisaTempFile(capacity=c.capacity) 198 | output.write(out) 199 | # data = sout.getvalue() 200 | except Exception: 201 | log.exception(c.error("pyPDF error")) 202 | if raise_exception: 203 | raise 204 | 205 | 206 | # Found a background? So leave loop after first occurence 207 | break 208 | else: 209 | log.warn(c.warning("pyPDF not installed!")) 210 | 211 | # In web frameworks for debugging purposes maybe an output of 212 | # errors in a PDF is preferred 213 | if show_error_as_pdf and c and c.err: 214 | return pisaErrorDocument(c.dest, c) 215 | 216 | # Get the resulting PDF and write it to the file object 217 | # passed from the caller 218 | data = out.getvalue() 219 | c.dest.write(data) 220 | 221 | except: 222 | # log.exception(c.error("Document error")) 223 | log.exception("Document error") 224 | c.err += 1 225 | if raise_exception: 226 | raise 227 | 228 | if raise_exception and c.err: 229 | raise Exception("Errors occured, please see log files for more informations") 230 | 231 | return c 232 | -------------------------------------------------------------------------------- /sx/pisa3/pisa_pdf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | __reversion__ = "$Revision: 20 $" 19 | __author__ = "$Author: holtwick $" 20 | __date__ = "$Date: 2007-10-09 12:58:24 +0200 (Di, 09 Okt 2007) $" 21 | 22 | from pisa_util import pisaTempFile, getFile 23 | 24 | import logging 25 | log = logging.getLogger("ho.pisa") 26 | 27 | class pisaPDF: 28 | 29 | def __init__(self, capacity=-1): 30 | self.capacity = capacity 31 | self.files = [] 32 | 33 | def addFromURI(self, url, basepath=None): 34 | obj = getFile(url, basepath) 35 | if obj and (not obj.notFound()): 36 | self.files.append(obj.getFile()) 37 | 38 | addFromFileName = addFromURI 39 | 40 | def addFromFile(self, f): 41 | if hasattr(f, "read"): 42 | self.files.append(f) 43 | self.addFromURI(f) 44 | 45 | def addFromString(self, data): 46 | self.files.append(pisaTempFile(data, capacity=self.capacity)) 47 | 48 | def addDocument(self, doc): 49 | if hasattr(doc.dest, "read"): 50 | self.files.append(doc.dest) 51 | 52 | def join(self, file=None): 53 | import pyPdf 54 | if pyPdf: 55 | output = pyPdf.PdfFileWriter() 56 | for pdffile in self.files: 57 | input = pyPdf.PdfFileReader(pdffile) 58 | for pageNumber in range(0, input.getNumPages()): 59 | output.addPage(input.getPage(pageNumber)) 60 | if file is not None: 61 | output.write(file) 62 | return file 63 | out = pisaTempFile(capacity=self.capacity) 64 | output.write(out) 65 | return out.getvalue() 66 | 67 | getvalue = join 68 | __str__ = join 69 | -------------------------------------------------------------------------------- /sx/pisa3/pisa_turbogears.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __reversion__ = "$Revision: 20 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2007-10-09 12:58:24 +0200 (Di, 09 Okt 2007) $" 20 | 21 | from turbogears.decorator import weak_signature_decorator 22 | import sx.pisa3 as pisa 23 | import StringIO 24 | import cherrypy 25 | 26 | def to_pdf(filename=None, content_type="application/pdf"): 27 | def entangle(func): 28 | def decorated(func, *args, **kw): 29 | output = func(*args, **kw) 30 | dst = StringIO.StringIO() 31 | result = pisa.CreatePDF( 32 | StringIO.StringIO(output), 33 | dst 34 | ) 35 | if not result.err: 36 | cherrypy.response.headers["Content-Type"] = content_type 37 | if filename: 38 | cherrypy.response.headers["Content-Disposition"] = "attachment; filename=" + filename 39 | output = dst.getvalue() 40 | return output 41 | return decorated 42 | return weak_signature_decorator(entangle) 43 | 44 | topdf = to_pdf 45 | -------------------------------------------------------------------------------- /sx/pisa3/pisa_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __reversion__ = "$Revision: 247 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-08-15 13:37:57 +0200 (Fr, 15 Aug 2008) $" 20 | __version__ = VERSION = "VERSION{3.0.33}VERSION"[8:-8] 21 | __build__ = BUILD = "BUILD{2010-06-16}BUILD"[6:-6] 22 | 23 | VERSION_STR = """XHTML2PDF/pisa %s (Build %s) 24 | http://www.xhtml2pdf.com 25 | 26 | Copyright 2010 Dirk Holtwick, holtwick.it 27 | 28 | Licensed under the Apache License, Version 2.0 (the "License"); 29 | you may not use this file except in compliance with the License. 30 | You may obtain a copy of the License at 31 | 32 | http://www.apache.org/licenses/LICENSE-2.0 33 | 34 | Unless required by applicable law or agreed to in writing, software 35 | distributed under the License is distributed on an "AS IS" BASIS, 36 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 37 | See the License for the specific language governing permissions and 38 | limitations under the License.""" % ( 39 | VERSION, 40 | BUILD, 41 | ) 42 | -------------------------------------------------------------------------------- /sx/pisa3/pisa_wsgi.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 103 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2007-10-31 17:08:54 +0100 (Mi, 31 Okt 2007) $" 20 | __svnid__ = "$Id: pisa.py 103 2007-10-31 16:08:54Z holtwick $" 21 | 22 | import ho.pisa as pisa 23 | import StringIO 24 | 25 | import logging 26 | log = logging.getLogger("ho.pisa.wsgi") 27 | 28 | class Filter(object): 29 | 30 | def __init__(self, app): 31 | self.app = app 32 | 33 | def __call__(self, environ, start_response): 34 | script_name = environ.get('SCRIPT_NAME', '') 35 | path_info = environ.get('PATH_INFO', '') 36 | sent = [] 37 | written_response = StringIO.StringIO() 38 | def replacement_start_response(status, headers, exc_info=None): 39 | if not self.should_filter(status, headers): 40 | return start_response(status, headers, exc_info) 41 | else: 42 | sent[:] = [status, headers, exc_info] 43 | return written_response.write 44 | app_iter = self.app(environ, replacement_start_response) 45 | if not sent: 46 | return app_iter 47 | status, headers, exc_info = sent 48 | try: 49 | for chunk in app_iter: 50 | written_response.write(chunk) 51 | finally: 52 | if hasattr(app_iter, 'close'): 53 | app_iter.close() 54 | body = written_response.getvalue() 55 | status, headers, body = self.filter( 56 | script_name, path_info, environ, status, headers, body) 57 | start_response(status, headers, exc_info) 58 | return [body] 59 | 60 | def should_filter(self, status, headers): 61 | print headers 62 | 63 | def filter(self, status, headers, body): 64 | raise NotImplementedError 65 | 66 | 67 | class HTMLFilter(Filter): 68 | 69 | def should_filter(self, status, headers): 70 | if not status.startswith('200'): 71 | return False 72 | for name, value in headers: 73 | if name.lower() == 'content-type': 74 | return value.startswith('text/html') 75 | return False 76 | 77 | class PisaMiddleware(HTMLFilter): 78 | 79 | def filter(self, 80 | script_name, 81 | path_info, 82 | environ, 83 | status, 84 | headers, 85 | body): 86 | topdf = environ.get("pisa.topdf", "") 87 | if topdf: 88 | dst = StringIO.StringIO() 89 | result = pisa.CreatePDF( 90 | body, 91 | dst, 92 | show_error_as_pdf=True, 93 | ) 94 | headers = [ 95 | ("content-type", "application/pdf"), 96 | ("content-disposition", "attachment; filename=" + topdf) 97 | ] 98 | body = dst.getvalue() 99 | return status, headers, body 100 | -------------------------------------------------------------------------------- /sx/w3c/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | ##~ Copyright (C) 2002-2004 TechGame Networks, LLC. 4 | ##~ 5 | ##~ This library is free software; you can redistribute it and/or 6 | ##~ modify it under the terms of the BSD style License as found in the 7 | ##~ LICENSE file included with this distribution. 8 | ## 9 | ## Modified by Dirk Holtwick , 2007 10 | ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | 12 | 13 | -------------------------------------------------------------------------------- /sx/w3c/cssDOMElementInterface.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | ##~ Copyright (C) 2002-2004 TechGame Networks, LLC. 4 | ##~ 5 | ##~ This library is free software; you can redistribute it and/or 6 | ##~ modify it under the terms of the BSD style License as found in the 7 | ##~ LICENSE file included with this distribution. 8 | ## 9 | ## Modified by Dirk Holtwick , 2007-2008 10 | ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 11 | 12 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 | #~ Imports 14 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 | 16 | import css 17 | 18 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | #~ Definitions 20 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | class CSSDOMElementInterface(css.CSSElementInterfaceAbstract): 23 | """An implementation of css.CSSElementInterfaceAbstract for xml.dom Element Nodes""" 24 | 25 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 26 | #~ Constants / Variables / Etc. 27 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 28 | 29 | style = None 30 | 31 | _pseudoStateHandlerLookup = { 32 | 'first-child': 33 | lambda self: not bool(self.getPreviousSibling()), 34 | 'not-first-child': 35 | lambda self: bool(self.getPreviousSibling()), 36 | 37 | 'last-child': 38 | lambda self: not bool(self.getNextSibling()), 39 | 'not-last-child': 40 | lambda self: bool(self.getNextSibling()), 41 | 42 | 'middle-child': 43 | lambda self: not bool(self.getPreviousSibling()) and not bool(self.getNextSibling()), 44 | 'not-middle-child': 45 | lambda self: bool(self.getPreviousSibling()) or bool(self.getNextSibling()), 46 | 47 | # XXX 'first-line': 48 | 49 | } 50 | 51 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | #~ Definitions 53 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | 55 | def __init__(self, domElement, cssParser=None): 56 | self.domElement = domElement 57 | # print self.domElement.attributes 58 | if cssParser is not None: 59 | self.onCSSParserVisit(cssParser) 60 | 61 | def onCSSParserVisit(self, cssParser): 62 | styleSrc = self.getStyleAttr() 63 | if styleSrc: 64 | style = cssParser.parseInline(styleSrc) 65 | self.setInlineStyle(style) 66 | 67 | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | def matchesNode(self, (namespace, tagName)): 70 | if tagName not in ('*', self.domElement.tagName): 71 | return False 72 | if namespace in (None, '', '*'): 73 | # matches any namespace 74 | return True 75 | else: # full compare 76 | return namespace == self.domElement.namespaceURI 77 | 78 | def getAttr(self, name, default=NotImplemented): 79 | attrValue = self.domElement.attributes.get(name) 80 | if attrValue is not None: 81 | return attrValue.value 82 | else: 83 | return default 84 | 85 | def getIdAttr(self): 86 | return self.getAttr('id', '') 87 | def getClassAttr(self): 88 | return self.getAttr('class', '') 89 | def getStyleAttr(self): 90 | return self.getAttr('style', None) 91 | 92 | def inPseudoState(self, name, params=()): 93 | handler = self._pseudoStateHandlerLookup.get(name, lambda self: False) 94 | return handler(self) 95 | 96 | def iterXMLParents(self, includeSelf=False): 97 | klass = self.__class__ 98 | current = self.domElement 99 | if not includeSelf: 100 | current = current.parentNode 101 | while (current is not None) and (current.nodeType == current.ELEMENT_NODE): 102 | yield klass(current) 103 | current = current.parentNode 104 | 105 | def getPreviousSibling(self): 106 | sibling = self.domElement.previousSibling 107 | while sibling: 108 | if sibling.nodeType == sibling.ELEMENT_NODE: 109 | return sibling 110 | else: 111 | sibling = sibling.previousSibling 112 | return None 113 | def getNextSibling(self): 114 | sibling = self.domElement.nextSibling 115 | while sibling: 116 | if sibling.nodeType == sibling.ELEMENT_NODE: 117 | return sibling 118 | else: 119 | sibling = sibling.nextSibling 120 | return None 121 | 122 | def getInlineStyle(self): 123 | return self.style 124 | def setInlineStyle(self, style): 125 | self.style = style 126 | 127 | -------------------------------------------------------------------------------- /test/cookbook.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | HTML to PDF conversion example 4 | 9 | 10 | 11 |

Hello Wörld 12 |


13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
AmountDescriptionTotal
1Good weather0 EUR
Sum0 EUR
29 | 30 | -------------------------------------------------------------------------------- /test/cookbook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 176 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-03-15 00:11:47 +0100 (Sa, 15 Mrz 2008) $" 20 | 21 | """ 22 | HTML/CSS to PDF converter 23 | 24 | Most people know how to write a page with HTML and CSS. Why not using these skills to dynamically generate PDF documents using it? The "pisa" project http://www.htmltopdf.org enables you to to this quite simple. 25 | """ 26 | 27 | import cStringIO 28 | import ho.pisa as pisa 29 | import os 30 | 31 | # Shortcut for dumping all logs to the screen 32 | pisa.showLogging() 33 | 34 | def HTML2PDF(data, filename, open=False): 35 | 36 | """ 37 | Simple test showing how to create a PDF file from 38 | PML Source String. Also shows errors and tries to start 39 | the resulting PDF 40 | """ 41 | 42 | pdf = pisa.CreatePDF( 43 | cStringIO.StringIO(data), 44 | file(filename, "wb")) 45 | 46 | if open and (not pdf.err): 47 | pisa.startViewer(filename) 48 | 49 | return not pdf.err 50 | 51 | if __name__=="__main__": 52 | HTMLTEST = """ 53 | 54 |

Hello World 55 |


56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
AmountDescriptionTotal
1Good weather0 EUR
Sum0 EUR
72 | 73 | """ 74 | 75 | HTML2PDF(HTMLTEST, "test.pdf", open=True) 76 | -------------------------------------------------------------------------------- /test/css/reset.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | font-weight: normal; 5 | font-style: inherit; 6 | font-size: 100%; 7 | font-family: inherit; 8 | /* vertical-align: baseline; */ 9 | list-style: none; 10 | } 11 | 12 | @media screen { 13 | html, body { 14 | height:100%; 15 | } 16 | } 17 | 18 | img, 19 | fieldset, 20 | legend, 21 | table, 22 | tr, 23 | td, 24 | th, 25 | tbody, 26 | tfoot, 27 | thead, 28 | applet, 29 | object, 30 | iframe, 31 | frame, 32 | frameset { 33 | border: 0; 34 | } 35 | 36 | h1, 37 | h2, 38 | h3, 39 | h4, 40 | h5, 41 | h6, 42 | pre, 43 | table, 44 | blockquote { 45 | clear: both; 46 | } 47 | 48 | body { 49 | line-height: 1.5; 50 | color: #000; 51 | background: #fff; 52 | font:13px/1.22 arial,helvetica,clean,sans-serif; 53 | font-style: normal; 54 | *font-size:small; 55 | *font:x-small; 56 | } 57 | 58 | input, 59 | textarea, 60 | select { 61 | font:13px/1.22 arial,helvetica,clean,sans-serif; 62 | *font-size:13px; 63 | } 64 | 65 | pre, 66 | code, 67 | kbd, 68 | samp, 69 | tt { 70 | font-family:monospace; 71 | *font-size:108%; 72 | line-height:99%; 73 | } 74 | 75 | /* Tables still need 'cellspacing="0"' in the markup. */ 76 | table { 77 | font-size:inherit; 78 | font:100%; 79 | table-layout: fixed; 80 | border-collapse: collapse; 81 | border-spacing: 0; 82 | empty-cells: show; 83 | } 84 | caption, th, td { 85 | text-align: left; 86 | font-weight: normal; 87 | empty-cells: show; 88 | vertical-align: top; 89 | } 90 | 91 | /* Remove possible quote marks (") from ,
. */ 92 | blockquote:before, 93 | blockquote:after, 94 | q:before, 95 | q:after { 96 | content: ""; 97 | } 98 | 99 | blockquote, q { 100 | quotes: "" ""; 101 | } 102 | 103 | /* CLEAR */ 104 | 105 | .clear { 106 | clear: both; 107 | } 108 | 109 | /* 110 | .clear { 111 | display: inline-block; 112 | height: 0; 113 | clear: both; 114 | } 115 | 116 | .clear:after, 117 | .container:after { 118 | content: "."; 119 | display: block; 120 | height: 0; 121 | clear: both; 122 | visibility: hidden; 123 | } 124 | 125 | * html .clear { height: 1%; } 126 | 127 | .clear { display: block; } 128 | */ 129 | 130 | /* HIDE */ 131 | 132 | .hide, 133 | .hidden { 134 | display: none; 135 | } 136 | 137 | /* FORM */ 138 | 139 | textarea.validation-failed, 140 | input.validation-failed, 141 | select.validation-failed { 142 | background: #FFFFCC; 143 | border: 2px solid red; 144 | } 145 | 146 | textarea.sample, 147 | input.sample { 148 | color: #999; 149 | } 150 | 151 | input.readonly { 152 | background: #eee; 153 | } 154 | 155 | input.required { 156 | } 157 | 158 | 159 | /* A container should group all your columns. */ 160 | .container { 161 | text-align: left; 162 | position: relative; 163 | padding: 0; 164 | margin: 0 auto; /* Centers layout */ 165 | width: 960px; /* Total width */ 166 | } 167 | 168 | .sidenote { 169 | width: 25%; 170 | float: right; 171 | margin-bottom: 0.5em; 172 | margin-left: 0.5em; 173 | padding: 0.5em; 174 | background: #ccc; 175 | overflow: hidden; 176 | } 177 | 178 | /* Print normalize */ 179 | @media print { 180 | 181 | html, body { 182 | background: white !important; 183 | color: black !important; 184 | width: 100% !important; 185 | height: inherit !important; 186 | } 187 | 188 | body a:link, 189 | body a:visited { 190 | background: transparent; 191 | text-decoration: underline; 192 | } 193 | 194 | body a[href*="http://"]:link:after, 195 | body a[href*="http://"]:visited:after { 196 | content: " <" attr(href) "> "; 197 | } 198 | 199 | body .noprint { 200 | display: none !important; 201 | } 202 | 203 | body .print { 204 | display: block !important; 205 | position: static !important; 206 | margin: 0 !important; 207 | padding: 0 !important; 208 | border: none !important; 209 | width: auto !important; 210 | height: auto !important; 211 | float: none !important; 212 | clear: both !important; 213 | overflow: inherit !important; 214 | } 215 | } 216 | /* CSS Document */ 217 | 218 | -------------------------------------------------------------------------------- /test/css/test-css-media-3.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | @import "../test-css-media-4.css" print; 4 | 5 | .green6 { color: green; } -------------------------------------------------------------------------------- /test/datauri.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 194 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-04-18 18:59:53 +0200 (Fr, 18 Apr 2008) $" 20 | 21 | import ho.pisa as pisa 22 | import os, os.path 23 | import logging 24 | log = logging.getLogger(__file__) 25 | 26 | def helloWorld(): 27 | filename = __file__ + ".pdf" 28 | datauri = pisa.makeDataURIFromFile('img/denker.png') 29 | bguri = os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir, "pdf/background-sample.pdf")) 30 | bguri = pisa.makeDataURIFromFile(bguri) 31 | html = u""" 32 | 44 | 45 |

46 | Hello World 47 |

48 | 49 | """ % (bguri, datauri) 50 | pdf = pisa.pisaDocument( 51 | html, 52 | file(filename, "wb"), 53 | path = __file__ 54 | ) 55 | if not pdf.err: 56 | pisa.startViewer(filename) 57 | 58 | if __name__=="__main__": 59 | pisa.showLogging() 60 | helloWorld() 61 | -------------------------------------------------------------------------------- /test/default.css: -------------------------------------------------------------------------------- 1 | 2 | html { 3 | font-family: Helvetica; 4 | font-size: 10px; 5 | font-weight: normal; 6 | color: #000000; 7 | background-color: transparent; 8 | margin: 0; 9 | padding: 0; 10 | line-height: 150%; 11 | border: 1px none; 12 | display: inline; 13 | width: auto; 14 | height: auto; 15 | white-space: normal; 16 | } 17 | 18 | b, 19 | strong { 20 | font-weight: bold; 21 | } 22 | 23 | i, 24 | em { 25 | font-style: italic; 26 | } 27 | 28 | u { 29 | text-decoration: underline; 30 | } 31 | 32 | s, 33 | strike { 34 | text-decoration: line-through; 35 | } 36 | 37 | a { 38 | text-decoration: underline; 39 | color: blue; 40 | } 41 | 42 | ins { 43 | color: green; 44 | text-decoration: underline; 45 | } 46 | del { 47 | color: red; 48 | text-decoration: line-through; 49 | } 50 | 51 | pre, 52 | code, 53 | kbd, 54 | samp, 55 | tt { 56 | font-family: "Courier New"; 57 | } 58 | 59 | h1, 60 | h2, 61 | h3, 62 | h4, 63 | h5, 64 | h6 { 65 | font-weight:bold; 66 | -pdf-outline: true; 67 | -pdf-outline-open: false; 68 | } 69 | 70 | h1 { 71 | /*18px via YUI Fonts CSS foundation*/ 72 | font-size:138.5%; 73 | -pdf-outline-level: 0; 74 | } 75 | 76 | h2 { 77 | /*16px via YUI Fonts CSS foundation*/ 78 | font-size:123.1%; 79 | -pdf-outline-level: 1; 80 | } 81 | 82 | h3 { 83 | /*14px via YUI Fonts CSS foundation*/ 84 | font-size:108%; 85 | -pdf-outline-level: 2; 86 | } 87 | 88 | h4 { 89 | -pdf-outline-level: 3; 90 | } 91 | 92 | h5 { 93 | -pdf-outline-level: 4; 94 | } 95 | 96 | h6 { 97 | -pdf-outline-level: 5; 98 | } 99 | 100 | h1, 101 | h2, 102 | h3, 103 | h4, 104 | h5, 105 | h6, 106 | p, 107 | pre, 108 | hr { 109 | margin:1em 0; 110 | } 111 | 112 | address, 113 | blockquote, 114 | body, 115 | center, 116 | dl, 117 | dir, 118 | div, 119 | fieldset, 120 | form, 121 | h1, 122 | h2, 123 | h3, 124 | h4, 125 | h5, 126 | h6, 127 | hr, 128 | isindex, 129 | menu, 130 | noframes, 131 | noscript, 132 | ol, 133 | p, 134 | pre, 135 | table, 136 | th, 137 | tr, 138 | td, 139 | ul, 140 | li, 141 | dd, 142 | dt, 143 | pdftoc { 144 | display: block; 145 | } 146 | 147 | table { 148 | -pdf-keep-in-frame-mode: shrink; 149 | } 150 | 151 | tr, 152 | th, 153 | td { 154 | 155 | vertical-align: middle; 156 | width: auto; 157 | } 158 | 159 | th { 160 | text-align: center; 161 | font-weight: bold; 162 | } 163 | 164 | center { 165 | text-align: center; 166 | } 167 | 168 | big { 169 | font-size: 125%; 170 | } 171 | 172 | small { 173 | font-size: 75%; 174 | } 175 | 176 | 177 | ul { 178 | margin-left: 1.5em; 179 | list-style-type: disc; 180 | } 181 | 182 | ul ul { 183 | list-style-type: circle; 184 | } 185 | 186 | ul ul ul { 187 | list-style-type: square; 188 | } 189 | 190 | ol { 191 | list-style-type: decimal; 192 | margin-left: 1.5em; 193 | } 194 | 195 | pre { 196 | white-space: pre; 197 | } 198 | 199 | blockquote { 200 | margin-left: 1.5em; 201 | margin-right: 1.5em; 202 | } 203 | 204 | -------------------------------------------------------------------------------- /test/font/README.txt: -------------------------------------------------------------------------------- 1 | The font of this directory have not been included into the SVN 2 | because of copyright issues. The names of the fonts are: 3 | 4 | - CODE2000 (TTF) 5 | - DejaVu (TTF) 6 | - Forelle (AFM + PFB) 7 | - leerc (AFM + PFB) 8 | - milano (TTF) 9 | - rina (TTF) 10 | -------------------------------------------------------------------------------- /test/helloworld.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 194 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-04-18 18:59:53 +0200 (Fr, 18 Apr 2008) $" 20 | 21 | import ho.pisa as pisa 22 | 23 | def helloWorld(): 24 | filename = __file__ + ".pdf" 25 | pdf = pisa.CreatePDF( 26 | u"Hello World", 27 | file(filename, "wb") 28 | ) 29 | if not pdf.err: 30 | pisa.startViewer(filename) 31 | 32 | if __name__=="__main__": 33 | pisa.showLogging() 34 | helloWorld() 35 | -------------------------------------------------------------------------------- /test/img/beach.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/img/beach.jpg -------------------------------------------------------------------------------- /test/img/denker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/img/denker.png -------------------------------------------------------------------------------- /test/img/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/img/test.jpg -------------------------------------------------------------------------------- /test/pdf.css: -------------------------------------------------------------------------------- 1 | .rand_oben{ 2 | border-top: 1px solid #333; 3 | } 4 | .rand_unten{ 5 | border-bottom: 1px solid #333; 6 | } 7 | .rand_links{ 8 | border-left: 1px solid #333; 9 | } 10 | .rand_rechts{ 11 | border-right: 1px solid #333; 12 | } 13 | td{ 14 | font-family:Arial, Helvetica, sans-serif; 15 | font-size: 8px; 16 | } 17 | 18 | h1{ 19 | font-size:20px; 20 | margin:0; 21 | padding:0; 22 | font-weight:normal; 23 | } 24 | h3{ 25 | margin:0; 26 | padding:0; 27 | font-size: 10px; 28 | } 29 | h4{ 30 | margin:0; 31 | margin-bottom:5px; 32 | padding:0; 33 | } 34 | .gueltig_bis{ 35 | font-size: 20px; 36 | color:white; 37 | margin: auto 0; 38 | } 39 | .grauer_bg{ 40 | background-color:#CCCCCC; 41 | font-family:Arial, Helvetica, sans-serif; 42 | font-size: 10px; 43 | border:solid 1px #000000; 44 | } 45 | .dunkel_grauer_bg{ 46 | background-color:#999999; 47 | } 48 | .niedrig{ 49 | 50 | font-size:1px; 51 | } 52 | .duenn{ 53 | margin-left:0; 54 | margin-right:0; 55 | padding-left:0; 56 | padding-right:0; 57 | font-size:1px; 58 | width:0.1cm; 59 | } 60 | -------------------------------------------------------------------------------- /test/pdf/background-sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/pdf/background-sample.pdf -------------------------------------------------------------------------------- /test/pdf/test-invoice-bg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/pdf/test-invoice-bg.pdf -------------------------------------------------------------------------------- /test/pdfform.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | HTML to PDF conversion example 4 | 9 | 10 | 11 |

Form Example

12 |
13 |

14 | Name 15 | 16 |

17 |

18 | Address 19 | 22 |

23 |

24 | Country 25 | 31 |

32 |

33 | Gender 34 | male 35 | female 36 |

37 |

38 | Agree with conditions? 39 | 40 |

41 |
42 | 43 | -------------------------------------------------------------------------------- /test/pdfjoiner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | __reversion__ = "$Revision: 20 $" 19 | __author__ = "$Author: holtwick $" 20 | __date__ = "$Date: 2007-10-09 12:58:24 +0200 (Di, 09 Okt 2007) $" 21 | 22 | from sx.pisa3 import pisa 23 | from sx.pisa3 import pisa_pdf 24 | 25 | if __name__=="__main__": 26 | 27 | pdf = pisa_pdf.pisaPDF() 28 | 29 | subPdf = pisa.pisaDocument( 30 | u""" 31 | Hello World 32 | """) 33 | pdf.addDocument(subPdf) 34 | 35 | raw = open("test-loremipsum.pdf", "rb").read() 36 | pdf.addFromString(raw) 37 | 38 | pdf.addFromURI("test-loremipsum.pdf") 39 | 40 | pdf.addFromFile(open("test-loremipsum.pdf", "rb")) 41 | 42 | datauri = pisa.makeDataURIFromFile("test-loremipsum.pdf") 43 | pdf.addFromURI(datauri) 44 | 45 | # Write the result to a file and open it 46 | filename = __file__ + ".pdf" 47 | result = pdf.getvalue() 48 | open(filename, "wb").write(result) 49 | pisa.startViewer(filename) 50 | -------------------------------------------------------------------------------- /test/simple-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 20 |

Paragraph without styles

21 |

Paragraph with styles

22 | 23 | 24 | -------------------------------------------------------------------------------- /test/simple-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 38 | 39 | 40 |

Das ist ein einfaches Beispiel. Etwas Text zum üben 41 | und fertig. Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig. 42 | Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig. 43 | Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.

44 |

Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.Das ist 45 | ein 46 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 47 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 48 | Etwas Text zum üben und fertig.

49 |

Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.Das ist 50 | ein 51 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 52 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 53 | Etwas Text zum üben und fertig. g. Das ist ein einfaches Beispiel. 54 | Etwas Text zum üben und fertig. g. Das ist ein einfaches Beispiel. 55 | Etwas Text zum üben und fertig. g. Das ist ein einfaches Beispiel. 56 | Etwas Text zum üben und fertig. g. Das ist ein einfaches Beispiel. 57 | Etwas Text zum üben und fertig.

58 |

Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.Das ist 59 | ein 60 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 61 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 62 | Etwas Text zum üben und fertig.

63 |

Das ist ein einfaches Beispiel. 64 | Etwas Text zum üben und fertig.Das ist 65 | ein 66 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 67 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 68 | Etwas Text zum üben und fertig.

69 |

Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.Das ist 70 | ein 71 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 72 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 73 | Etwas Text zum üben und fertig.

74 |

Das ist ein einfaches Beispiel. Etwas Text zum üben und fertig.Das ist 75 | ein 76 | einfaches Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches 77 | Beispiel. Etwas Text zum üben und fertig. Das ist ein einfaches Beispiel. 78 | Etwas Text zum üben und fertig.

79 | 80 | 81 | -------------------------------------------------------------------------------- /test/simple.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 194 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-04-18 18:59:53 +0200 (Fr, 18 Apr 2008) $" 20 | 21 | import os 22 | import sys 23 | import cgi 24 | import cStringIO 25 | import logging 26 | 27 | import ho.pisa as pisa 28 | 29 | # Shortcut for dumping all logs to the screen 30 | pisa.showLogging() 31 | 32 | def dumpErrors(pdf, showLog=True): 33 | #if showLog and pdf.log: 34 | # for mode, line, msg, code in pdf.log: 35 | # print "%s in line %d: %s" % (mode, line, msg) 36 | #if pdf.warn: 37 | # print "*** %d WARNINGS OCCURED" % pdf.warn 38 | if pdf.err: 39 | print "*** %d ERRORS OCCURED" % pdf.err 40 | 41 | def testSimple( 42 | data="""Hello World
""", 43 | dest="test.pdf"): 44 | 45 | """ 46 | Simple test showing how to create a PDF file from 47 | PML Source String. Also shows errors and tries to start 48 | the resulting PDF 49 | """ 50 | 51 | pdf = pisa.CreatePDF( 52 | cStringIO.StringIO(data), 53 | file(dest, "wb") 54 | ) 55 | 56 | if pdf.err: 57 | dumpErrors(pdf) 58 | else: 59 | pisa.startViewer(dest) 60 | 61 | def testCGI(data="Hello World"): 62 | 63 | """ 64 | This one shows, how to get the resulting PDF as a 65 | file object and then send it to STDOUT 66 | """ 67 | 68 | result = cStringIO.StringIO() 69 | 70 | pdf = pisa.CreatePDF( 71 | cStringIO.StringIO(data), 72 | result 73 | ) 74 | 75 | if pdf.err: 76 | print "Content-Type: text/plain" 77 | print 78 | dumpErrors(pdf) 79 | else: 80 | print "Content-Type: application/octet-stream" 81 | print 82 | sys.stdout.write(result.getvalue()) 83 | 84 | def testBackgroundAndImage( 85 | src="test-background.html", 86 | dest="test-background.pdf"): 87 | 88 | """ 89 | Simple test showing how to create a PDF file from 90 | PML Source String. Also shows errors and tries to start 91 | the resulting PDF 92 | """ 93 | 94 | pdf = pisa.CreatePDF( 95 | file(src, "r"), 96 | file(dest, "wb"), 97 | log_warn = 1, 98 | log_err = 1, 99 | path = os.path.join(os.getcwd(), src) 100 | ) 101 | 102 | dumpErrors(pdf) 103 | if not pdf.err: 104 | pisa.startViewer(dest) 105 | 106 | def testURL( 107 | url="http://www.htmltopdf.org", 108 | dest="test-website.pdf"): 109 | 110 | """ 111 | Loading from an URL. We open a file like object for the URL by 112 | using 'urllib'. If there have to be loaded more data from the web, 113 | the pisaLinkLoader helper is passed as 'link_callback'. The 114 | pisaLinkLoader creates temporary files for everything it loads, because 115 | the Reportlab Toolkit needs real filenames for images and stuff. Then 116 | we also pass the url as 'path' for relative path calculations. 117 | """ 118 | import urllib 119 | 120 | pdf = pisa.CreatePDF( 121 | urllib.urlopen(url), 122 | file(dest, "wb"), 123 | log_warn = 1, 124 | log_err = 1, 125 | path = url, 126 | link_callback = pisa.pisaLinkLoader(url).getFileName 127 | ) 128 | 129 | dumpErrors(pdf) 130 | if not pdf.err: 131 | pisa.startViewer(dest) 132 | 133 | if __name__=="__main__": 134 | 135 | testSimple() 136 | # testCGI() 137 | #testBackgroundAndImage() 138 | #testURL() 139 | -------------------------------------------------------------------------------- /test/story2canvas.py: -------------------------------------------------------------------------------- 1 | # -*- coding: ISO-8859-1 -*- 2 | 3 | # Copyright 2010 Dirk Holtwick, holtwick.it 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | __version__ = "$Revision: 194 $" 18 | __author__ = "$Author: holtwick $" 19 | __date__ = "$Date: 2008-04-18 18:59:53 +0200 (Fr, 18 Apr 2008) $" 20 | 21 | from reportlab.pdfgen.canvas import Canvas 22 | from reportlab.lib.units import inch 23 | from reportlab.platypus import Frame 24 | 25 | import ho.pisa as pisa 26 | 27 | def test(filename): 28 | 29 | # Convert HTML to "Reportlab Story" structure 30 | story = pisa.pisaStory(""" 31 |

Sample

32 |

Hello World!

33 | """ * 20).story 34 | 35 | # Draw to Canvas 36 | c = Canvas(filename) 37 | f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1) 38 | f.addFromList(story,c) 39 | c.save() 40 | 41 | # Show PDF 42 | pisa.startViewer(filename) 43 | 44 | if __name__=="__main__": 45 | test('story2canvas.pdf') 46 | -------------------------------------------------------------------------------- /test/test-align.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Unbenanntes Dokument 7 | 12 | 13 | 14 |

NORMAL Lorem ipsum...

15 |

CENTER Lorem ipsum...

16 |

RIGHT Lorem ipsum...

17 |

LEFT Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec euismod ante sed mi. Mauris tortor purus, fermentum sit amet, interdum et, rutrum nec, risus. Suspendisse erat nisi, vestibulum a, mattis at, dignissim quis, ante. Ut in dui. Etiam sed elit nec tellus vulputate tincidunt. Nunc tortor risus, dignissim in, tempor a, sodales vel, erat. Aenean metus ipsum, vulputate vel, eleifend sed, tristique sed, nibh. Aliquam erat volutpat. Sed lobortis fringilla mauris. Nullam convallis ullamcorper urna. Integer id justo sed pede suscipit convallis. Cras non purus sed nisi faucibus scelerisque. Donec ullamcorper massa id risus. Vivamus tristique hendrerit neque. Maecenas pulvinar ipsum dictum ante. Cras eleifend lacus. Suspendisse et massa. Vestibulum sit amet lacus. In sed libero. Duis quis enim sit amet lorem ultrices rhoncus.

18 |

JUSTIFY Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec euismod ante sed mi. Mauris tortor purus, fermentum sit amet, interdum et, rutrum nec, risus. Suspendisse erat nisi, vestibulum a, mattis at, dignissim quis, ante. Ut in dui. Etiam sed elit nec tellus vulputate tincidunt. Nunc tortor risus, dignissim in, tempor a, sodales vel, erat. Aenean metus ipsum, vulputate vel, eleifend sed, tristique sed, nibh. Aliquam erat volutpat. Sed lobortis fringilla mauris. Nullam convallis ullamcorper urna. Integer id justo sed pede suscipit convallis. Cras non purus sed nisi faucibus scelerisque. Donec ullamcorper massa id risus. Vivamus tristique hendrerit neque. Maecenas pulvinar ipsum dictum ante. Cras eleifend lacus. Suspendisse et massa. Vestibulum sit amet lacus. In sed libero. Duis quis enim sit amet lorem ultrices rhoncus.

19 |

CENTER CLASS Lorem ipsum...

20 |

CENTER STYLE Lorem ipsum...

21 |

CENTER ID Lorem ipsum...

22 |

CENTER STYLE ATTR Lorem ipsum...

23 |

 

24 | 25 | 26 | -------------------------------------------------------------------------------- /test/test-all.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Test all features of pisa 6 | 14 | 15 | 16 |

This document will try to test all features of pisa in an easy way. On the left side 17 | you see the rendered snippet an on the right an Image showing how it should look like

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 36 | 39 | 40 | 41 | 42 | 78 | 81 | 82 |
#RenderedSample
01 27 | Normal 28 | Bold 29 | Italic 30 | Underlined 31 | Red 32 | Bigger 33 | Times 34 | Normal 35 | 37 | 38 |
02 43 | Normal 44 | 1 45 | 2 46 | 3 47 | 4 48 | 5 49 | 6 50 | 7 51 |
52 | Normal 53 | smaller 54 | larger 55 | -4 56 | -3 57 | -2 58 | -1 59 | +2 60 | +3 61 | +4 62 | +5 63 |
64 |

65 | Normal 66 | smaller 67 | larger 68 | -4 69 | -3 70 | -2 71 | -1 72 | +2 73 | +3 74 | +4 75 | +5 76 |

77 |
79 | 80 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /test/test-all/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/test-all/01.jpg -------------------------------------------------------------------------------- /test/test-all/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/test-all/02.jpg -------------------------------------------------------------------------------- /test/test-background-img.html: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | Background 7 | 42 | 43 | 44 | 45 |

46 | Hello World 47 |

48 | 49 | 50 | 51 |

52 | Hello World 53 |

54 | 55 | 56 | 57 |

58 | Hello World 59 |

60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/test-background.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Background 7 | 19 | 20 | 21 | Lorem ipsum... 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/test-barcode.html: -------------------------------------------------------------------------------- 1 |

2 | This is a test: 3 |

4 |

5 | Next Line 6 |

7 | -------------------------------------------------------------------------------- /test/test-blocks.html: -------------------------------------------------------------------------------- 1 | 19 | Test 20 |

21 | Block 1 22 |
23 | Neue Zeile 24 |

25 | Und noch eine! 26 |

27 | Block 2 28 | 29 |

30 | DIV 1 BEGIN 31 | 32 |
33 | INNERDIV A 34 | 35 |

36 | INNERP 37 | 38 |

39 | 40 | DIV 1 END 41 |
42 | 43 | (NEW PAGE?) AFTERDIV 44 | 45 |

Heading 1(NEW PAGE?)

46 | 47 | AFTERH1 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 70 | 71 |
Oben linksOben rechts
Unten links 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
xxx linksyyy rechts
xxx linksyyy rechts
68 | 69 |
72 | 73 |

74 | ENDE -------------------------------------------------------------------------------- /test/test-cdata.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CSS 2.1 Test Suite: Inheritance 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 |

This be blue and underlined.

20 | 21 |

This sentence should be underlined, including 22 | this part, this part, this part, 23 | and this part.

24 | 25 |

This sentence should also be underlined, as 26 | well as italics, including this part.

27 | 28 |

This sentence should be blue but not underlined, like 29 | this part, but this part should be underlined.

31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/test-cdata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CSS 2.1 Test Suite: Inheritance 5 | 12 | 13 | 14 | 15 | 16 |

This uld]]> be blue and underlined.

17 | 18 |

This sentence should be underlined, including 19 | this part, this part, this part, 20 | and this part.

21 | 22 |

This sentence should also be underlined, as 23 | well as italics, including this part.

24 | 25 |

This sentence should be blue but not underlined, like 26 | this part, but this part should be underlined.

28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /test/test-collapse.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Unbenanntes Dokument 6 | 17 | 18 | 19 | 20 |
21 |
yyy 22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 |
xxx
30 | 31 |
32 |
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
33 |
34 |
35 |
36 | x x 37 |
38 |
39 | 40 |
41 | 42 |
43 |
44 | x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x 45 |
46 |
47 | x x 48 |
49 |
50 | 51 | 52 | 53 | 54 |

xxx 55 |

xxx 56 |

xxx 57 |

xxx 58 |

xxx 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/test-css-body.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 29 | 30 | 31 | 32 | 33 | 34 |

35 | 36 | Test bold normal underline normal 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/test-css-border.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | 26 | 27 | 28 | 29 |

green text, green border

30 |

normal text, green border

31 |

normal text, green border, thick blue on top

32 |
33 |

normal text, green border, thick blue on top

34 |
35 |

normal text, no border

36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test/test-css-fontface.html: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | Test Media 7 | 84 | 85 | 86 | 87 |

Standard

88 | 89 |

90 | 91 | This is normal 92 | 93 | becomes bold 94 | 95 | then also italic 96 | 97 | 98 | 99 | and then just italic 100 | 101 |

102 | 103 |

TTF (DejaVu Mono)

104 | 105 |

106 | 107 | This is normal 108 | 109 | becomes bold 110 | 111 | then also italic 112 | 113 | 114 | 115 | and then just italic 116 | 117 |

118 | 119 |

120 | Some text in "Farsi": 121 | به گفته وزیرخارجه روسیه، قطعنامه ای که اعضای دائم شورای امنیت سازمان ملل متحد و آلمان دیروز بر سر تصویب آن علیه ایران به توافق 122 | رسیدند، تحریمهای تازه و سختی بر این کشور اعمال نخواهد کرد 123 |

124 | 125 |

126 | Some text in "Korean": 127 | 박응용 128 |

129 | 130 |

Postscript (XXX)

131 | 132 |

133 | 134 | This is normal 135 | 136 | becomes bold 137 | 138 | then also italic 139 | 140 | 141 | 142 | and then just italic 143 | 144 |

145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /test/test-css-id.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 29 | 30 | 31 | 32 | 33 | Standardfarbe: grau 34 | 35 |
36 | grau

blau gruen blau

37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /test/test-css-media-1.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | .green4 { color: green; } -------------------------------------------------------------------------------- /test/test-css-media-2.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | .green5 { color: green; } -------------------------------------------------------------------------------- /test/test-css-media-4.css: -------------------------------------------------------------------------------- 1 | /* CSS Document */ 2 | 3 | .green7 { color: green; } -------------------------------------------------------------------------------- /test/test-css-media.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Test Media 7 | 27 | 28 | 29 |

Normal

30 | 31 |

Green

32 | 33 |

Green only in PDF

34 | 35 |

Green only on screen

36 | 37 |

Green only in PDF (external)

38 | 39 |

Green only on screen (external)

40 | 41 |

Green only in PDF (external)

42 | 43 |

Green only in PDF (sub-external)

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/test-css.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 40 | 41 | 42 | 43 | 44 | Standardfarbe: grau 45 | 46 |
47 | (mit div#eins > p :display inline;) 48 | grau

blau gruen blau

49 |
50 |
51 | (mit div.zwei > p :display inline;) 52 | grau

blau gruen blau

53 |
54 |
55 | (ohne div > p :display inline;) 56 | grau

blau gruen blau

57 |
58 |
59 | grau

blau rot blau

60 |
61 |
62 |

dick und fett

63 |

fett und underlined

hier nicht 64 | mehr

hier nur noch dick

65 |
66 |
67 |

xx

68 |

underlined

69 |

hier nicht

70 |
71 | 72 | -------------------------------------------------------------------------------- /test/test-error.html: -------------------------------------------------------------------------------- 1 |

Image

2 | 3 | 4 | 5 | 6 |

Deprecation

7 | 8 | 9 | 10 |

Bidi

11 | 12 | A sentence. -------------------------------------------------------------------------------- /test/test-font.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Unbenanntes Dokument 7 | 25 | 26 | 27 |
28 | 29 | 30 |
31 |

Lorem ipsum äöü ÄÖÜ ß @ €...

32 |

Lorem ipsum äöü ÄÖÜ ß @ €...

33 |

Lorem ipsum äöü ÄÖÜ ß @ €...

34 |

Lorem ipsum äöü ÄÖÜ ß @ €...

35 | 36 | 37 | -------------------------------------------------------------------------------- /test/test-helloworld.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | HTML to PDF conversion example 4 | 5 | 10 | 11 | 12 |

Hello Wörld 13 |


14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
AmountDescriptionTotal
1Good weather0 EUR
2More sub0 EUR
Sum0 EUR
35 | 36 | -------------------------------------------------------------------------------- /test/test-image-align.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Grafiken ausrichten mit CSS 6 | 7 | 8 | 9 |

10 | Denkt der Denker 11 | Denker 12 | an das Oben? 13 |

14 | 15 | 16 |

Woran der Denker denkt

17 | 18 |

19 | Denkt der Denker 20 | an das Oben? 21 |

22 | 23 |

24 | Denkt der Denker 25 | Denker 26 | an das Oben? 27 |

28 | 29 |
30 | 31 |

32 | Denkt der Denker 33 | Denker 34 | an die Mitte? 35 |

36 | 37 |
38 | 39 |

40 | Denkt der Denker 41 | Denker 42 | an das Unten? 43 |

44 | 45 | 46 |
47 | 48 |

Woran der Denker denkt

49 | 50 |

51 | Denkt der Denker 52 | Denker 53 | an das Oben? 54 |

55 | 56 |
57 | 58 |

59 | Denkt der Denker 60 | Denker 61 | an die Mitte? 62 |

63 | 64 |
65 | 66 |

67 | Denkt der Denker 68 | Denker 69 | an das Unten? 70 |

71 | 72 |
73 | 74 |

75 | Text?Oder denkt er an ...? 77 |

78 | 79 |

80 | Manche Texte erschließen sich nur aus der nötigen Distanz. 81 | Aber das hier, das ist richtiger Text. Und er fließt sogar, 82 | nämlich um die Grafik.
83 |

84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /test/test-image.html: -------------------------------------------------------------------------------- 1 |

2 | Image #0 (BIG!) 3 |
4 | 5 | 6 |

7 | Image #1 8 |
9 | 10 | 11 |

12 | Image #2 13 |
14 | 15 | 16 |

17 | Image #3 18 |
19 | 20 | 21 |

22 | Image #4 23 |
24 | 25 | -------------------------------------------------------------------------------- /test/test-invoice.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/test-invoice.html -------------------------------------------------------------------------------- /test/test-linespacing.html: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | Line Spacing 7 | 17 | 18 | 19 |

20 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

21 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

22 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

23 |
24 |
25 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

26 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

27 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /test/test-link.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Unbenanntes Dokument 7 | 8 | 9 | Lorem ipsum... 10 | 11 | Test 12 | 13 | Test 14 | Test 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/test-list.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/test-list.html -------------------------------------------------------------------------------- /test/test-loremipsum.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/test-loremipsum.html -------------------------------------------------------------------------------- /test/test-margins.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | MARGINS 7 | 15 | 16 | 17 | 18 |

Heading 1

19 | 20 |

Para 1 21 | 22 |

Para 2 23 | 24 |

Heading 1

25 | 26 |

Para 1 27 | 28 |

Para 2 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/test-page-box.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Unbenanntes Dokument 7 | 43 | 44 | 45 | 46 |

65 | 66 |

Lorem ipsum... 1

67 | 68 | 69 | 70 |

Lorem ipsum... 2

71 | 72 | 73 | 74 |

Lorem ipsum... 3

75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /test/test-para-border.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30 | 31 | 32 |

Hello World: class=c1

33 |

Goodbye: class=c2

34 |

Mixed colors: class=c3

35 |
Hello World: class=c1
36 |
Goodbye: class=c2 
37 |
Mixed colors: class=c3
38 | 39 | 40 | -------------------------------------------------------------------------------- /test/test-pisa-linkfont.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | xxx 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/test-pisa-toc-entities.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 |

17 | 18 |

Table of Contents

19 | 20 | 21 | 22 |
23 | 24 |

1. Blender 25 |

26 |

Blender is a software that is used for creating 3D content. 27 | Primarily this means you can make three dimensional images. However, 28 | you can also add sound, and make it time-based and interactive. This 29 | makes Blender ideal for a wide range of uses including creating 3D 30 | models, rendering, post -production, story boarding, creating 31 | animations, making movies, creating 3D 'programs', and making 32 | interactive environments and games. 33 |

34 |

2. Blender & it's Unique Interface

35 |

­It's no secret that the Blender interface breaks a lot of rules. 36 | Luckily Blender has good reasons for it and after time these reasons 37 | become clear. Blender's interface is built around providing you with 38 | the shortest routes to the most results. It's also particularly focused 39 | on reducing the kind of stress on wrists and hands that's often felt 40 | when using mouse-intensive graphical applications every day. 41 |

42 | 43 |

3. < Installing Blender on OSX 44 |

45 |

Software Name: Blender 46 |
Homepage: http://www.blender.org 47 |
Software version used for this installation: Blender 2.43 48 |
Operating System used for thisinstalltion: OSX (10.4.8) 49 |
Reccomended Hardware: Powerbook G4, Powermac G5, Mac Pro, MacBookPro, iMac (core Duo) 50 |

51 |

Downloading 52 |

53 |

The latest stable version of Blender for OSX can be downloaded at http://www.blender.org/download/get-blender/ 54 |

55 | 56 | 57 | -------------------------------------------------------------------------------- /test/test-pisa-toc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 |

17 | 18 |

Table of Contents

19 | 20 | 21 | 22 |
23 | 24 |

1. Blender 25 |

26 |

Blender is a software that is used for creating 3D content. 27 | Primarily this means you can make three dimensional images. However, 28 | you can also add sound, and make it time-based and interactive. This 29 | makes Blender ideal for a wide range of uses including creating 3D 30 | models, rendering, post -production, story boarding, creating 31 | animations, making movies, creating 3D 'programs', and making 32 | interactive environments and games. 33 |

34 |

2. Blender's Unique Interface

35 |

­It's no secret that the Blender interface breaks a lot of rules. 36 | Luckily Blender has good reasons for it and after time these reasons 37 | become clear. Blender's interface is built around providing you with 38 | the shortest routes to the most results. It's also particularly focused 39 | on reducing the kind of stress on wrists and hands that's often felt 40 | when using mouse-intensive graphical applications every day. 41 |

42 | 43 |

3. Installing Blender on OSX 44 |

45 |

Software Name: Blender 46 |
Homepage: http://www.blender.org 47 |
Software version used for this installation: Blender 2.43 48 |
Operating System used for thisinstalltion: OSX (10.4.8) 49 |
Reccomended Hardware: Powerbook G4, Powermac G5, Mac Pro, MacBookPro, iMac (core Duo) 50 |

51 |

Downloading 52 |

53 |

The latest stable version of Blender for OSX can be downloaded at http://www.blender.org/download/get-blender/ 54 |

55 | 56 | 57 | -------------------------------------------------------------------------------- /test/test-pre.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Unbenanntes Dokument 7 | 12 | 13 | 14 |

Lorem ipsum...

15 |
  1. Eins
16 |  2.  Zwei
17 | 3.   Drei   
18 | 
19 |

Lorem ipsum...

20 |
	
21 |   1. Eins
22 | 2. Zwei 23 | 3. Drei 24 |
25 | Ende 26 |

Some space     end of space

27 |

Some space end of space

28 | 29 | 30 | -------------------------------------------------------------------------------- /test/test-syntax.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Das ist der Titel 7 | 25 | 26 | 27 |

28 | Lorem hidden bold=red normal silverblue silver orange 29 | 30 | yellow amet nochwas normal 31 |

32 | Weiterer sit Absatz 33 | 38 |

39 | Anfang DIV 1 40 |
41 | AD2 42 |

43 | Test 44 |

45 | ED2 46 |

47 | Ende DIV 2 48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/test-tables.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Table Test 7 | 24 | 25 | 26 |

Lorem ipsum...

27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
12
34
37 |

Lorem ipsum...

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
12
34
48 | 49 | 50 | 51 | 52 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 |
LOREM

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec euismod ante sed mi. Mauris tortor purus, fermentum sit amet, interdum et, rutrum nec, risus. Suspendisse erat nisi, vestibulum a, mattis at, dignissim quis, ante. Ut in dui. Etiam sed elit nec tellus vulputate tincidunt.

53 | LOREM
In blandit justo sed lorem. Proin malesuada. Pellentesque dignissim sem tempus augue ultrices sagittis. Nulla ac justo quis quam bibendum adipiscing. Curabitur pharetra, ipsum at iaculis faucibus, quam massa egestas mi, et mollis tellus tellus ac sapien. Etiam odio. Vivamus posuere dapibus velit. Etiam eros dui, vulputate non, congue eu, elementum ac, tellus. Vestibulum ac sapien sit amet diam blandit sollicitudin. Aenean in augue. Suspendisse potenti. Proin a leo. Etiam consequat, neque sed vulputate hendrerit, nibh nibh vehicula justo, sed dapibus augue justo bibendum erat. Suspendisse iaculis. Curabitur tempus. Aenean ultricies nonummy pede. Nulla facilisi. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras sem. Cras dolor risus, consequat ut, viverra eget, viverra a, orci.LOREMIn blandit justo sed lorem. Proin malesuada. Pellentesque dignissim sem tempus augue ultrices sagittis. Nulla ac justo quis quam bibendum adipiscing. Curabitur pharetra, ipsum at iaculis faucibus, quam massa egestas mi, et mollis tellus tellus ac sapien. Etiam odio. Vivamus posuere dapibus velit. Etiam eros dui, vulputate non, congue eu, elementum ac, tellus. Vestibulum ac sapien sit amet diam blandit sollicitudin. Aenean in augue. Suspendisse potenti. Proin a leo. Etiam consequat, neque sed vulputate hendrerit, nibh nibh vehicula justo, sed dapibus augue justo bibendum erat. Suspendisse iaculis. Curabitur tempus. Aenean ultricies nonummy pede. Nulla facilisi. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras sem. Cras dolor risus, consequat ut, viverra eget, viverra a, orci.
LOREMIn blandit justo sed lorem. Proin malesuada. Pellentesque dignissim sem tempus augue ultrices sagittis. Nulla ac justo quis quam bibendum adipiscing. Curabitur pharetra, ipsum at iaculis faucibus, quam massa egestas mi, et mollis tellus tellus ac sapien. Etiam odio. VivamuLOREM

Unten

67 |

Unten

xxxyyy
72 |

Lorem ipsum...

73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 |
12
34
83 | 84 |

Lorem ipsum...

85 |

Lorem ipsum...

86 |

Lorem ipsum...

87 |

 

88 | 89 | 90 | -------------------------------------------------------------------------------- /test/test-template.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Template 7 | 8 | 9 | 10 | 11 | 12 | 13 | Das ist ein Test 14 | 15 | 16 | 17 |

18 | Etwas Text 19 | 20 | 21 | 22 | Nochwas 23 | 24 | 25 | 26 | Neue Seite 27 | 28 | 29 | 30 | DREI 31 | Nochwas 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/test-unicode-greek.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Greek Test and Resource Link 6 | 9 | 10 | 11 | Following are the names of the books of the New Testament in Greek (and Ojibwe). 12 |

13 |

14 | 15 | ΜΑΤΘΑΙΟΝ - ᒪᑎᔪ 16 |
17 | ΜΑΡΚΟΝ - ᒪᕒᒃ 18 | 19 |
20 | ΛΟΥΚΑΝ - ᓫᐅᒃ 21 |
22 | ΙΩΑΝΝΗΝ - ᒜᓐ 23 |
24 | ΠΡΑΞΕΙΣ - ᐃᔑᒋᑫᐎᓇᓐ 25 |
26 | ΡΩΜΑΙΟΥΣ - ᐅᕒᐅᒪᓇᒃ 27 |
28 | 29 | ΚΟΡΙΝΘΙΟΥΣ Α' - 1 ᑲᕒᐃᑎᔭᓇᒃ 30 |
31 | ΚΟΡΙΝΘΙΟΥΣ Β' - 2 ᑲᕒᐃᑎᔭᓇᒃ 32 |
33 | ΓΑΛΑΤΑΣ - ᑲᓫᐁᔑᔭᓇᒃ 34 |
35 | ΕΦΕΣΙΟΥΣ - ᐃᐱᔑᔭᓇᒃ 36 |
37 | ΦΙΛΙΠΠΗΣΙΟΥΣ - ᐱᓫᐃᐱᔭᓇᒃ 38 | 39 |
40 | ΚΟΛΟΣΣΑΕΙΣ - ᑲᓫᐊᔑᔭᓇᒃ 41 |
42 | ΘΕΣΣΑΛΟΝΙΚΕΙΣ Α' - 1 ᑌᓴᓫᐅᓂᔭᓇᒃ 43 |
44 | ΘΕΣΣΑΛΟΝΙΚΕΙΣ Β' - 2 ᑌᓴᓫᐅᓂᔭᓇᒃ 45 |
46 | ΤΙΜΟΘΕΟΝ Α' - 1 ᑎᒪᑎ 47 |
48 | 49 | ΤΙΜΟΘΕΟΝ Β' - 2 ᑎᒪᑎ 50 |
51 | ΤΙΤΟΝ - ᑕᐃᑕᔅ 52 |
53 | ΦΙΛΗΜΟΝΑ - ᐸᐃᓫᐃᒪᓐ 54 |
55 | ΕΒΡΑΙΟΥΣ - ᐃᐸᕒᐅᐗᒃ 56 |
57 | ΙΑΚΩΒΟΥ - ᒉᒥᔅ 58 | 59 |
60 | ΠΕΤΡΟΥ Α' - 1 ᐱᑕᕒ 61 |
62 | ΠΕΤΡΟΥ Β' - 2 ᐱᑕᕒ 63 |
64 | ΙΩΑΝΝΟΥ Α' - 1 ᒐᓐ 65 |
66 | ΙΩΑΝΝΟΥ Β' - 2 ᒐᓐ 67 |
68 | 69 | ΙΩΑΝΝΟΥ Γ' - 3 ᒐᓐ 70 |
71 | ΙΟΥΔΑ - ᒍᑦ 72 |
73 | ΑΠΟΚΑΛΥΨΙΣ - ᑭᑫᑕᒧᔑᐌᐎᓐ 74 |
75 |
76 |
77 |
78 | Many thanks to Birger Langker 79 | 80 | for providing the following link to The New 81 | Testament in Modern Greek. http://www.gospel.gr 82 |
83 | My home page 84 |
85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /test/test-unicode-thaana.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Thaana Unicode Support Test and Resource Links 6 | 9 | 10 | 11 |

12 | މަހަށް  ގޮސް  އުޅޭއިރު 13 |  އެންމެ  އުނދަގޫ  ކަމަކަށް 14 |
15 | ދިމާވަނީ ކޮން  ކަމެއް؟ 16 |  މަހަށް  ގޮސް  އުޅޭ  އިރު 17 |  އެންމެ އުނދަގޫ 18 |
19 | ކަމަކަށް 20 |  އަޅުގަނޑުމެންނަށް 21 |  ވަނީ  އެން  ދަތި 22 |  އިރުގައި  އެން 23 |
24 | ނިހިފިގެން  އެ  އުޅޭ. 25 |  އެން  ހިފަން  އުޅެނީ 26 |  ކިހިނެއް؟ 27 |

28 |

29 | Translation: 30 |
31 | Cuando vas a pescar ¿qué es lo peor que nos pasa ? Cuando vamos a pescar lo peor es cuando los 32 | cebos son escasos y no tenemos cebos. ¿Cómo haces para coger cebos ? 33 |
34 |
35 | The sample Thaana text on this page is from The World's Writing Systems by Daniels and Bright. 36 | The Spanish translation is borrowed from a web site which used the same sample text but failed to 37 | cite its source. 38 |

39 | Thaana is a right-to-left script. 40 |

41 | In order to make the spaces between the words a bit larger, I used two spaces between each word. 42 |

43 | For more information about the Thaana script and the history and culture of 44 | the Maldives, please visit: 45 |
46 | http://www.haveeru.com.mv/ 47 |
48 | http://www.maldivesculture.com/main.html 49 |

50 |
51 |
52 |
53 | 54 | Maldivian Internet Task Force 55 | 56 |
57 | http://www.mitf.net 58 |
59 |
60 | Links to Thaana and Thaana/Unicode material, including free Thaana Unicode fonts, 61 | text editors, Unicode sample documents, and more. 62 |

63 |
64 | My home page 65 | 66 | 67 | -------------------------------------------------------------------------------- /test/test-w3c-css.html: -------------------------------------------------------------------------------- 1 | margin-top 2 | 6 | 7 | 8 |

Überschrift erster Ordnung

9 |
10 | Bereich mit der Beispiel-Klasse. 11 |

Überschrift erster Ordnung

12 |
13 | 14 | -------------------------------------------------------------------------------- /test/unicode.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "code2000"; 3 | src: url("font/code2000.ttf") 4 | } 5 | 6 | html { 7 | font-family: code2000; 8 | } 9 | -------------------------------------------------------------------------------- /test/visualdiff.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Dirk Holtwick, holtwick.it 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import sys 16 | import glob 17 | import subprocess 18 | import tempfile 19 | import os 20 | import os.path 21 | 22 | CONVERT = r"C:\Programme\ImageMagick-6.3.8-Q16\convert.exe" 23 | DIFF = "tortoiseidiff.exe" 24 | 25 | __version__ = "0.1" 26 | 27 | class VisualObject: 28 | 29 | def __init__(self): 30 | self.files = [] 31 | self.files4del = [] 32 | self.folder4del = None 33 | 34 | def __del__(self): 35 | for file in self.files4del: 36 | os.remove(file) 37 | self.files4del = [] 38 | if self.folder4del: 39 | os.rmdir(self.folder4del) 40 | self.folder4del = None 41 | 42 | def execute(self, *a): 43 | print "EXECUTE", " ".join(a) 44 | return subprocess.Popen(a, stdout=subprocess.PIPE).communicate()[0] 45 | 46 | def getFiles(self, folder, pattern="*.*"): 47 | pattern = os.path.join(folder, pattern) 48 | self.files = [x for x in glob.glob(pattern) if not x.startswith(".")] 49 | self.files.sort() 50 | print "FILES", self.files 51 | return self.files 52 | 53 | def loadFile(self, file, folder=None, delete=True): 54 | if folder is None: 55 | folder = self.folder4del = tempfile.mkdtemp(prefix="visualdiff-tmp-") 56 | delete = True 57 | print "FOLDER", folder, "DELETE", delete 58 | source = os.path.abspath(file) 59 | destination = os.path.join(folder, "image.png") 60 | self.execute(CONVERT, source, destination) 61 | self.files4del = self.getFiles(folder, "*.png") 62 | return folder 63 | 64 | def compare(self, other, chunk=16 * 1024): 65 | if len(self.files) <> len(other.files): 66 | return False 67 | for i in range(len(self.files)): 68 | a = open(self.files[i], "rb") 69 | b = open(other.files[i], "rb") 70 | if a.read() <> b.read(): 71 | return False 72 | return True 73 | 74 | def getoptions(): 75 | from optparse import OptionParser 76 | usage = "usage: %prog [options] arg" 77 | description = """ 78 | Visual Differences 79 | """.strip() 80 | version = __version__ 81 | parser = OptionParser( 82 | usage, 83 | description=description, 84 | version=version, 85 | ) 86 | #parser.add_option( 87 | # "-c", "--css", 88 | # help="Path to default CSS file", 89 | # dest="css", 90 | # ) 91 | parser.add_option("-q", "--quiet", 92 | action="store_false", dest="verbose", default=True, 93 | help="don't print status messages to stdout") 94 | parser.set_defaults( 95 | # css=None, 96 | ) 97 | (options, args) = parser.parse_args() 98 | 99 | #if not (0 < len(args) <= 2): 100 | # parser.error("incorrect number of arguments") 101 | 102 | return options, args 103 | 104 | def main(): 105 | 106 | options, args = getoptions() 107 | 108 | print args 109 | 110 | a = VisualObject() 111 | b = VisualObject() 112 | 113 | a.loadFile("expected/test-loremipsum.pdf") 114 | b.files = a.files 115 | 116 | print a.compare(b) 117 | 118 | if __name__=="__main__": 119 | main() 120 | -------------------------------------------------------------------------------- /test/witherror.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/test/witherror.py -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | from runtests import buildTestSuite 2 | -------------------------------------------------------------------------------- /tests/runtests.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import glob 4 | import unittest 5 | 6 | #Allow us to import the parent module 7 | os.chdir(os.path.split(os.path.abspath(__file__))[0]) 8 | sys.path.insert(0, os.path.abspath(os.curdir)) 9 | sys.path.insert(0, os.path.abspath(os.pardir)) 10 | # sys.path.insert(0, os.path.join(os.path.abspath(os.pardir), "src")) 11 | 12 | def buildTestSuite(): 13 | suite = unittest.TestSuite() 14 | for testcase in glob.glob('test_*.py'): 15 | module = os.path.splitext(testcase)[0] 16 | suite.addTest(__import__(module).buildTestSuite()) 17 | return suite 18 | 19 | def main(): 20 | results = unittest.TextTestRunner().run(buildTestSuite()) 21 | return results 22 | 23 | if __name__ == "__main__": 24 | results = main() 25 | if not results.wasSuccessful(): 26 | sys.exit(1) 27 | -------------------------------------------------------------------------------- /tests/samples/borders.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 48 | 49 | 50 |

Hello World: class=c1

51 |

Goodbye: class=c2

52 |

Goodbye: class=c2 padding: 16px;

53 |

Goodbye: class=c2 padding: 16px;

54 |

Goodbye: class=c2
Next line

55 |

Mixed colors: class=c3

56 |
Hello World: class=c1
57 |
Goodbye: class=c2 
58 |
Mixed colors: class=c3
59 |

This is line A with line-spacing: 100%, there should not be any space between the lines
60 | This is line B

61 |

This is line A with line-spacing: 150%, there should be a gap
62 | This is line B

63 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

64 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

65 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea consectetur commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

66 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

67 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

68 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

69 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

70 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

71 | 72 | 73 | -------------------------------------------------------------------------------- /tests/samples/font-and-styles.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Font and Styles 6 | 11 | 12 | 13 |

Some common styles set by tags:

14 |

Normal Bold Italic Underlined Red Bigger Times Normal

15 |

Some common styles set by span. Should look the same as above:

16 |

Normal Bold Italic Underlined Red Bigger Times Normal

17 |

Font sizes are defined to be relative but in reality all browsers use fixed sizes. We go with the main stream. Here the regular example

18 |

Normal 1 2 3 4 5 6 7

19 |

The following is inside a <div style="font-size: 16pt">:

20 |
Normal smaller larger -4 -3 -2 -1 +2 +3 +4 +5
21 |

The following is inside an <h2>:

22 |

Normal smaller larger -4 -3 -2 -1 +2 +3 +4 +5

23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/samples/font-and-styles.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/font-and-styles.pdf -------------------------------------------------------------------------------- /tests/samples/font/CODE2000.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/font/CODE2000.TTF -------------------------------------------------------------------------------- /tests/samples/font/arialuni.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/font/arialuni.ttf -------------------------------------------------------------------------------- /tests/samples/images.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 |

Images

10 |

Here we are testing if the floating of images work and the inline feature.

11 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

12 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

13 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

14 |

15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/samples/images.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/images.pdf -------------------------------------------------------------------------------- /tests/samples/img/denker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/img/denker.png -------------------------------------------------------------------------------- /tests/samples/img/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/img/tree.jpg -------------------------------------------------------------------------------- /tests/samples/tables.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Font and Styles 6 | 11 | 12 | 13 |

Simple table:

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
abcd
eifgh
jnkl
mop
35 |

 

36 | 37 | 38 | -------------------------------------------------------------------------------- /tests/samples/tables.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/tables.pdf -------------------------------------------------------------------------------- /tests/samples/unicode.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: CODE2000; 3 | src: url("font/CODE2000.TTF") 4 | } 5 | 6 | /* 7 | @font-face { 8 | font-family: arialuni; 9 | font-weight: bold; 10 | src: url("font/CODE2000.TTF") 11 | } 12 | 13 | @font-face { 14 | font-family: arialuni; 15 | font-style: italic; 16 | src: url("font/CODE2000.TTF") 17 | } 18 | 19 | @font-face { 20 | font-family: arialuni; 21 | font-weight: bold; 22 | font-style: italic; 23 | src: url("font/CODE2000.TTF") 24 | } 25 | */ 26 | 27 | html { 28 | font-family: CODE2000; 29 | } 30 | -------------------------------------------------------------------------------- /tests/samples/utf8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/holtwick/xhtml2pdf/881850e49b67d8c9111ac53d09fb90138c1a3320/tests/samples/utf8.pdf -------------------------------------------------------------------------------- /tests/test_parser.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sx.pisa3.pisa_parser import pisaParser 3 | from sx.pisa3.pisa_context import pisaContext 4 | import StringIO 5 | 6 | _data = """ 7 | 8 | 9 | TITLE 10 | 11 | BODY 12 | 13 | 14 | """ 15 | 16 | class TestCase(unittest.TestCase): 17 | 18 | def testParser(self): 19 | c = pisaContext(".") 20 | r = pisaParser(_data, c) 21 | self.assertEqual(c, r) 22 | 23 | def buildTestSuite(): 24 | return unittest.defaultTestLoader.loadTestsFromName(__name__) 25 | 26 | def main(): 27 | buildTestSuite() 28 | unittest.main() 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /tests/test_samples.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import glob 3 | import subprocess 4 | import tempfile 5 | import os 6 | import os.path 7 | 8 | __version__ = "0.1" 9 | 10 | class VisualObject: 11 | 12 | CONVERT = r"C:\Programme\ImageMagick-6.3.8-Q16\convert.exe" 13 | DIFF = r"C:\Programme\TortoiseSVN\bin\tortoiseidiff.exe" 14 | SUFFIX = ".png" 15 | 16 | # C:\Programme\gs\gs8.54\bin\gswin32.exe -q -dSAFER -dNOPAUSE -dBATCH -sOutputFile=rl_hello-page%04d.png -sDEVICE=png256 -r144x144 -f test-font-and-styles.pdf 17 | # 18 | 19 | def __init__(self): 20 | self.files = [] 21 | self.files4del = [] 22 | self.folder4del = None 23 | 24 | def __del__(self): 25 | for file in self.files4del: 26 | os.remove(file) 27 | self.files4del = [] 28 | if self.folder4del: 29 | os.rmdir(self.folder4del) 30 | self.folder4del = None 31 | 32 | def execute(self, *a): 33 | #print 34 | #print "EXECUTE", " ".join(a) 35 | #print 36 | # os.system(" ".join(a)) 37 | r = subprocess.Popen(a, stdout=subprocess.PIPE).communicate()[0] 38 | # print r 39 | return r 40 | 41 | def showDiff(self, left, right): 42 | return self.execute(self.DIFF, "/fit", "/overlay", "/left:" + left, "/right:" + right) 43 | 44 | def convertFile(self, source, destination): 45 | self.execute(self.CONVERT, "-strip", source, destination) 46 | 47 | def getFiles(self, folder, pattern="*.*"): 48 | pattern = os.path.join(folder, pattern) 49 | self.files = [os.path.abspath(x) for x in glob.glob(pattern)] 50 | self.files.sort() 51 | # print "FILES", self.files 52 | return self.files 53 | 54 | def loadFile(self, file, folder=None, delete=True): 55 | if folder is None: 56 | folder = self.folder4del = tempfile.mkdtemp(prefix="visualdiff-tmp-") 57 | delete = True 58 | # print "FOLDER", folder, "DELETE", delete 59 | source = os.path.abspath(file) 60 | destination = os.path.join(folder, os.path.basename(file) + "-%04d" + self.SUFFIX) 61 | self.convertFile(source, destination) 62 | self.getFiles(folder, os.path.basename(file) + "-????" + self.SUFFIX) 63 | if delete: 64 | self.files4del = self.files 65 | return folder 66 | 67 | def compare(self, other, chunk=16*1024, show=True): 68 | if not self.files: 69 | return False, "No files" 70 | if len(self.files) <> len(other.files): 71 | return False, "Different number of files" 72 | for i in range(len(self.files)): 73 | left = self.files[i] 74 | right = other.files[i] 75 | a = open(left, "rb") 76 | b = open(right, "rb") 77 | diff = a.read() <> b.read() 78 | a.close() 79 | b.close() 80 | del a 81 | del b 82 | if diff: 83 | if show: 84 | self.showDiff(left, right) 85 | return False, "Difference in file %r" % left 86 | return True, "" 87 | 88 | ''' 89 | def getoptions(): 90 | from optparse import OptionParser 91 | usage = "usage: %prog [options] arg" 92 | description = """ 93 | Visual Differences 94 | """.strip() 95 | version = __version__ 96 | parser = OptionParser( 97 | usage, 98 | description=description, 99 | version=version, 100 | ) 101 | #parser.add_option( 102 | # "-c", "--css", 103 | # help="Path to default CSS file", 104 | # dest="css", 105 | # ) 106 | parser.add_option("-q", "--quiet", 107 | action="store_false", dest="verbose", default=True, 108 | help="don't print status messages to stdout") 109 | parser.set_defaults( 110 | # css=None, 111 | ) 112 | (options, args) = parser.parse_args() 113 | 114 | #if not (0 < len(args) <= 2): 115 | # parser.error("incorrect number of arguments") 116 | 117 | return options, args 118 | 119 | def main_command(): 120 | 121 | options, args = getoptions() 122 | 123 | print args 124 | 125 | a = VisualObject() 126 | b = VisualObject() 127 | 128 | a.loadFile("expected/test-loremipsum.pdf") 129 | b.files = a.files 130 | 131 | print a.compare(b) 132 | ''' 133 | 134 | import unittest 135 | import sx.pisa3.pisa as pisa 136 | import shutil 137 | 138 | class TestCase(unittest.TestCase): 139 | 140 | def testSamples(self): 141 | 142 | # Enable logging 143 | pisa.showLogging() 144 | 145 | # Calculate paths 146 | here = os.path.abspath(os.path.join(__file__, os.pardir)) 147 | folder = os.path.join(here, "tmp") 148 | left = os.path.join(folder, "left") 149 | right = os.path.join(folder, "right") 150 | 151 | # Cleanup old tests and create new structure 152 | if os.path.isdir(folder): 153 | shutil.rmtree(folder) 154 | os.makedirs(left) 155 | os.makedirs(right) 156 | 157 | for name in glob.glob(os.path.join(here, "samples", "*.html")): 158 | # print name 159 | 160 | # Create new PDF 161 | bname = os.path.basename(name) 162 | fname = os.path.join(right, bname[:-5] + ".pdf") 163 | 164 | dest = open(fname, "wb") 165 | pdf = pisa.pisaDocument( 166 | open(name, "rb"), 167 | dest, 168 | path = name) 169 | dest.close() 170 | 171 | self.assertTrue(not pdf.err, name) 172 | 173 | # New object 174 | r = VisualObject() 175 | r.loadFile(fname, right, delete=False) 176 | 177 | # Expected object 178 | l = VisualObject() 179 | l.loadFile(name[:-5] + ".pdf", left, delete=False) 180 | 181 | # Compare both and open Diff if differences 182 | result, msg = l.compare(r) 183 | self.assertTrue(result, name + ": " + msg) 184 | 185 | def buildTestSuite(): 186 | return unittest.defaultTestLoader.loadTestsFromName(__name__) 187 | 188 | def main(): 189 | buildTestSuite() 190 | unittest.main() 191 | 192 | if __name__ == "__main__": 193 | main() 194 | -------------------------------------------------------------------------------- /tests/test_uri.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from sx.pisa3.pisa_util import * 3 | 4 | _datauri = """data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAACJBAMAAABjvlPWAAAAMFBMVEUAAAAKBgQYFA8tJh5MQjZ3 5 | YEWPgHGynIK+oXvHsJbRvanZ0Mbv6+b48+//+/f///9agwt8AAAM7UlEQVR4XnWXXWwbV3bHZ+Jg 6 | gb7NJUdy06dQEmW3AYrQIilFAQKP+CGLSRcQxSEpKTVg8UO0sjVgU7JsuTWQSKQuuS91JEsz46KA 7 | VxLFOUz2YbONY07WQLcfAYqFFpst+oFNuECDbNHYvHwp0JeC6rkzkuwizhEgCTM//s//nnvOASgc 8 | PjcY+8L6Rf9rX7YY6x4+j+kyZP4lVKiY+gcfICQ8D+GRq1TBrFTyv2Tsu5jfTeuGYVlQ+tPW85hD 9 | W6aqVaFhNekbre/QaX2RzHkryFj1RfZdOv8w2JfON7lQ8dG3mK5j5y/ToeuBe5ZlNdcutoXnmWFP 10 | lPFSKP5DztRGv8UwG/rvU6myEnCYKHuOTpuxv/VPpdcLtMkPluw8R6fFHr/64mKQFrIAlkWTT57n 11 | 5zeL0kAgo6eW5g2ruZf89rkwV1YOjSihpWRkKgZQTKLnZ993uujmbwbG1YDH5XO7fL2R0rkgE54x 12 | whO1/k4NbqXVYcUr57f1ostD3j5hnNK1rQfbUwuwpXrrlNJ5gGtE7jxlEGh/c130Z6cBthKReqlQ 13 | ilzPZ/1R9gyDsUNEPwWA8qQ7eim4EfER9+WLnf/PXJJIhDaAZpFZcy8R4nKF/ph1nvXzXx5C0lUA 14 | mvPJyTK5vLzsk04/Os7VOex02+xfvUOkH5m5gZfdarlnjPSv6I+6yBx3ebvTuKsu+wtV0DNToqyW 15 | A2vhAuQZvhJOzHwzPFXYTwLUaVESXGqBUoC9FBbtiMH6dncmAmlzaqKp0RKRxP4kpeG88ma7c8wg 16 | wrLGzezo/qIFtEYkQQyUCu6bxexfs6cM+/dxrbK2YjU/aOrmJBEJ6etzDb129fYJ02HsIzW4nAjf 17 | tMBKwZokEX84HAG49BfssHPiOZMYCKXMpGVBrnDXI8qg69dv1Yq3GTthOi4fEd03GzhSy4P1rDy/ 18 | Hi4szn48/+NOmx0zbQ/qi4V71qfWw/hlfdncIEPR2Wb+nRY7Zrp/LxJBEM7iiFv/VJqZyAxv9fUF 19 | Z63aD1jrpM5xURAksbfSQKg2eH84PmguX51oNI2nOo8jWBNBdNszbkbuyBl3Vl70G9bc7RPmdwFJ 20 | kJChRgWZ3H1CiCiHfbes2pkT5qeh8wJmc6UpvdcwDc5IIpHCFcgen729F/eLBG0H0rQCyCAhz+GD 21 | C8adK1yHdTvtbNx7XhIFiSQL1DDvEgl10pkh7MSrF4+Yxy/GB8YkO8FZWtErChFFEqDgGxhH0w7z 22 | tTfuTrwsiaJAetASXcM0QxFkvHSAOQz7VSBOIh5BkvBsA9hd6wpxL8m05pET8pfIdB1G6iOiwOtI 23 | BimYi8S91zMSF2XllTZnsEN+FVCJKAk8COm9YcBdzx/tkbxCZOUNdsT8OqziUWxmyEeClcYn8Rgo 24 | Pjym+8D2g8znCdVHHCaNw0UbD0sAiiLI2d6T+/pcVacIz4WW0/mx9A+bnBGkcV/44CkzpZ6XHDte 25 | ulWoWlsxeJ+8WCQzX3Y50+10P1fjXhRyoDRUwPpkFMZyg/elxJEfxpAhcp9zMLGnNt2wHsY2z1ya 26 | efWFxMFhV7CX0zU1QYgsOEFqQR0bLVOYTHnk5G3n7C3mV1WCJZQcRu2hVWsvormz5FTuDfZbZFot 27 | FkBGwnt3kgVDI9QaH67LZY//XK/jp/312bI6iXd1FLLivnFPX6kPr/v6BueutG0//9xbiqjKMSSK 28 | IfJaYxPqF9ZcSSjNtITuYZt95M6PhRPOjYnISFIQslCnGXcM9pO/5EzrEomVXBHFvjEiICrKxoBJ 29 | a77wAtRzj7ifb3y9g9qAK8fbUEQQj/jCQj+tbESLAJQzXfY/qf4e3NaDCAm8AMOKSNL9hYpfGz9h 30 | frSwmfEtZ/yGERoaOtdHAM6Lve8XNmHttWaznnub59q9ZcFeaBxql3MqxjmAXUIyBV3LLgDUy69z 31 | nfvv4JDvRw2NquEwmkGOSO4CLateANNmurvGp1azqVV1Xc+Gsdr2jL6QLOQWXVHQOHPY3sU90AQw 32 | oAFlH46rzZBIct30eVOwxZnHMQRA3xw2AO5iiSTetcRD+gF2htKwj567/1YFk5YJmZsBM0RQRyRS 33 | 36lieIV/EqwHnPmkCjqN42jPQy7Ly0jcA2qQol3dwJ1l/gCZVBWg5iEBNTmfz8cJIkl/uUCrGqVa 34 | 9VNLexOZ8SrU58ggjvDliXDeI3vj6Ty1o1K/Z1maHz1HUWd7/gbFWNDrRC6s8//LqpqsXMCalFyO 35 | Dg/N+aRHLlCMdVWNeHcLDTBLb3GGGk8hmpDTHMkqhIwWLzerdfrnyERpxWZMiibpdjiJf3NjRJIX 36 | Jlce3tNKr9gMrdgUvgNDX1Tz+VziZULcsYRG9fAaP3uEW0SkAXWKv/ZUHrgzzgS9pqrNr32I5zo/ 37 | TLX8TScbYChYKvVVUUwt9T4YzowkmcCYEqR0Sr2s6zwbWM0de3fyrh3+SZQscaajBArrSgCXLszr 38 | tArwPjJ8ZCUS+0m+J5TscJ1Aal1xD43Tmj+mGwDvEUngu5G4pndq08Egz/VzWc1NERIolIhcsT5L 39 | v+eRiMi13MnQteyZtzjz61OqigOIjIdc+PT+zDW0g4YCOffU6ZuhpS+5n/8VI2piaCiSxgYJNuYu 40 | +7HE/WKPVvKJohy50ubMk8mAioH1xWTVRO0P73LdC7Q8ia7IFcaEDut+FkJoiqjlsCtZ2dybtSxT 41 | ITdoaQpHkhxw5vC3H2/39fW9TAZLArlQndi7ZVk7pAfvLCGJ0ulO9xCZdhPuoEvSWyJuWoFN7Ks7 42 | njQyk4IkvtI55MyTGGxw1WCJBAtVmFqxmv54mpb5+iO3OcO6yJT5fN4okeg0QLJhfZwM26kE4dQj 43 | m2HIYGuJsl4ikQrspyxraUtGRsGRxW2IzCF7Mgi05BEnAHNV68XZZnimJtNyAidRfLNjM4ftu1Bf 44 | I2TGmJPT1dXA7MMBKMtldQoZcpF1bKazB3Ws3u5Llwj2VnB2JwbvjuRUhYiSzBymy5BZQ+a0QsIh 45 | d2E9B/V3x1UsDpH+pNU6YcyyT97tJRg9dHsGtHeDCZ+EJftZ9ygX21kAqsi7ST7qEd5lmhJUsGLi 46 | 9w5Qx/F8nzMkUeTbOUoNZM7lfZz5szY71rk/g4wYvoYMGV9fAXPNvfgC9ioWh7WP/Ng6YuKaJEiu 47 | ZHkBzExPdkCy24J1Haa9s2IzcyjjTqGfbV8kEZdEXmOsoONnDwDrPDSHH5VTS7xYw1hB6S00c8R0 48 | n/gB1vBI2MdETmZNOtdzRp0S5R8/y4yiS5G45kRJlNOjWp54g4GxhPdn/FDHTIzfqfCi/1VRCKTO 49 | 5nwkkZVVNfgIma7DYI+ZlCqCOJERcIAGfeIp9dJAXk1/iJ6f6tQpnSPiQlYQAtl+XBjXx5JUTb/d 50 | OmEOvxhFZlUgK4vIZLC+waQvSvM3XkfkWOdxzARaJsJLDkNcWRq4YCyat1qtp7lGdRN1pJeyNiN9 51 | z0sj1Ng1V9vsGcYADev6Uslhxkbqq5pRh1X2jE4MGtp5Iv6ew5BrI/UyZ0pXWBfv65gBmsf6lYjN 52 | zAWjVc7U38Ar7Toz+HgUmXUJvRIRGVcx0GPYjMpYiznz9R8xqNNVSXThcLizflrxkQ3NqDXM3AFr 53 | HzMzANqq5A4vEomksVaLvqIGxRUo8Zs/tJmvAIDG3SnIEkL8zQqtFHO6eRUnfJixEx0DaCRVGz8n 54 | hkOkAZTujuhwFRs4jNfqeP7qRrVe0WvnerEzrvsaVlPTORMDmnl0dHYWogaYRVHsFSJ4b7ihAEZ0 55 | Y68XBd/sOvPOopTCJh8/kqbVzTNNHWCmMr8TRGYDDdl+wmkKl0g4rIhp3TAHLF3fWtle2c8blJZu 56 | HzH3vVVYUil9z/6iN2CBVjb2clpuhdLy94/PJcMFCvhA7B8xYO6dZn1qxdwYVm9pdPX7LcfzzyeK 57 | fwAapevEQybg/VkLJgtLmUGvUadrBy2eq8PGtpTTAHVallQlWNmLIZN0Jwavg0nXb7ecXJHFcL9h 58 | 6nRdSf/IT/eDTcgQd8C7tGLS8qjD/Od8lManTUyGX22zFJaMejlHyNmsYZbmQx9ypvVAKxilQdME 59 | mu3ZyGqwH6bUWCLuaTDWCuoBZ9p5jUI9fgYdlXpWt6tg6hpCuskdBt+26/ObCjT5TtjGYyhZswJ7 60 | 8w8KFUDDGP5O12YoPsCevzMOGtrSoebJT6CogUjuYtvW+UcuDLB6VZ6Bzd83AJY83iggq9Fs4MDp 61 | w4+QR6n4xrwfduVKtbrZIyeBS6/FPmh17T78KeVhjG3CRswcWt7cnw5m0fg6Lf3VATvqw1PUjt3l 62 | GYCGj2TKE9Eeo54PRVgbfxwdVS3Y2UY1dJUgoaWJqDrdVGf5TsXJsZnHSsQWmtbQlZkgSjS1f6uF 63 | RIv/cvY8e7gdDvmpE7BHblrWL1r8JbOZrs0wZuFjHp8NDQWtFnsmEMD4P1oK8Sv7gdVGAAAAAElF 64 | TkSuQmCC""" 65 | 66 | class TestCase(unittest.TestCase): 67 | 68 | def testAbsouluteUri(self): 69 | 70 | # URL 71 | f = getFile("http://www.holtwick.it") 72 | c = f.getFile().read(1) 73 | 74 | self.assertEqual(len(c), 1) 75 | self.assertEqual(c, "<") 76 | self.assertEqual(f.mimetype, "text/html") 77 | 78 | # Path 79 | f = getFile("__init__.py") 80 | c = f.getFile().read(1) 81 | 82 | self.assertEqual(len(c), 1) 83 | self.assertEqual(c, "f") 84 | self.assertEqual(f.mimetype, "text/x-python") 85 | 86 | # Data URI 87 | f = getFile(_datauri) 88 | c = f.getFile().read(1) 89 | 90 | self.assertEqual(len(c), 1) 91 | self.assertEqual(c, '\x89') 92 | self.assertEqual(f.mimetype, "image/png") 93 | 94 | def testRelativeUri(self): 95 | 96 | # URL 97 | f = getFile("index", basepath="http://www.holtwick.it") 98 | c = f.getFile().read(1) 99 | 100 | self.assertEqual(len(c), 1) 101 | self.assertEqual(c, "<") 102 | self.assertEqual(f.mimetype, "text/html") 103 | 104 | # Path 105 | base = os.path.join(os.path.abspath(__file__), os.pardir, os.pardir) 106 | f = getFile("tests/__init__.py", base) 107 | c = f.getFile().read(1) 108 | 109 | # print f.mimetype 110 | self.assertEqual(len(c), 1) 111 | self.assertEqual(c, "f") 112 | self.assertEqual(f.mimetype, "text/x-python") 113 | 114 | def buildTestSuite(): 115 | return unittest.defaultTestLoader.loadTestsFromName(__name__) 116 | 117 | def main(): 118 | buildTestSuite() 119 | unittest.main() 120 | 121 | if __name__ == "__main__": 122 | main() 123 | --------------------------------------------------------------------------------