[silva.export.pdf][Emiliano D'Alterio] Added new export to PDF f...

[email protected] Mon, 07 Oct 2013 15:04:06 +0200
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Emiliano D'Alterio
date:      Mon Oct 07 15:03:59 2013 +0200
revision:  1:cf331910dcd7 in silva.export.pdf
branch:    
details:   https://hg.infrae.com/silva.export.pdf?cmd=changeset;node=cf331910dcd7
modified:  .hgignore setup.py src/silva/export/pdf/configure.zcml src/silva/export/pdf/pdf.py
added:     src/silva/export/pdf/configure.zcml src/silva/export/pdf/pdf.py
removed:   
log:       Added new export to PDF feature (still experimental and misses skin
	choice).


diffstat:

 .hgignore                           |   1 +
 setup.py                            |  24 ++++++-----
 src/silva/export/pdf/configure.zcml |  12 +++++
 src/silva/export/pdf/pdf.py         |  79 +++++++++++++++++++++++++++++++++++++
 4 files changed, 105 insertions(+), 11 deletions(-)

diffs (149 lines):

diff -r 49268e92e2b2 -r cf331910dcd7 .hgignore
--- a/.hgignore	Fri Feb 17 15:43:19 2012 +0100
+++ b/.hgignore	Mon Oct 07 15:03:59 2013 +0200
@@ -1,3 +1,4 @@
+.DS_Store
 .pyo
 .pyc
 .egg-info
diff -r 49268e92e2b2 -r cf331910dcd7 setup.py
--- a/setup.py	Fri Feb 17 15:43:19 2012 +0100
+++ b/setup.py	Mon Oct 07 15:03:59 2013 +0200
@@ -15,14 +15,14 @@
 setup(name='silva.export.pdf',
       version=version,
       description="Export Silva content to PDF",
-      long_description=open("README.txt").read() + "\n" +
-                       open(os.path.join("docs", "HISTORY.txt")).read(),
+      long_description=open("README.txt").read() + "\n" + open(
+          os.path.join("docs", "HISTORY.txt")).read(),
       classifiers=[
-        "Framework :: Zope2",
-        "License :: OSI Approved :: BSD License",
-        "Programming Language :: Python",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-        ],
+          "Framework :: Zope2",
+          "License :: OSI Approved :: BSD License",
+          "Programming Language :: Python",
+          "Topic :: Software Development :: Libraries :: Python Modules",
+      ],
       keywords='silva export pdf',
       author='Infrae',
       author_email='[email protected]',
@@ -34,10 +34,12 @@
       include_package_data=True,
       zip_safe=False,
       install_requires=[
-        'Zope2',
-        'five.grok',
-        'silva.core.interfaces',
-        ],
+          'Zope2',
+          'five.grok',
+          'silva.core.interfaces',
+          'silva.export.html',
+          'xhtml2pdf'
+      ],
       tests_require=tests_require,
       extras_require={'test': tests_require},
       )
diff -r 49268e92e2b2 -r cf331910dcd7 src/silva/export/pdf/configure.zcml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/export/pdf/configure.zcml	Mon Oct 07 15:03:59 2013 +0200
@@ -0,0 +1,12 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:grok="http://namespaces.zope.org/grok"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="silva">
+
+  <include package="five.grok" />
+
+  <!-- Grok package -->
+  <grok:grok package="." />
+
+</configure>
diff -r 49268e92e2b2 -r cf331910dcd7 src/silva/export/pdf/pdf.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/export/pdf/pdf.py	Mon Oct 07 15:03:59 2013 +0200
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2012-2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import logging
+import shutil
+import tempfile
+import fnmatch
+import re
+import os
+
+from cStringIO import StringIO
+from zope.component import getAdapter
+from silva.core.interfaces import IPublishable, IContentExporter
+from zipfile import ZipFile, ZIP_DEFLATED
+from zope.interface import Interface
+from five import grok
+from xhtml2pdf import pisa
+
+
+logger = logging.getLogger('silva.export.pdf')
+
+
+def convert_html_to_pdf(html_file_path, pdf_file_path):
+    with open(html_file_path, 'r') as f:
+        html_data = f.read()
+    os.unlink(html_file_path)
+
+    for link in re.findall('href="(.*?)"', html_data, re.DOTALL):
+        if not 'http://' in link and '.html' in link:
+            ## We change the local links to be as xhtml2pdf expects,
+            ## so it will generate correct "GoToR" syntax for them.
+            local_pdf_link = 'pdf:%s' % (link.replace('.html', '.pdf'))
+            html_data = html_data.replace(link, local_pdf_link)
+
+    with open(pdf_file_path, "w+b") as pdf:
+        pisa.CreatePDF(html_data, dest=pdf, path=pdf_file_path,
+                       encoding='UTF-8')
+
+    return pdf_file_path
+
+
+class IExportOptions(Interface):
+    pass
+
+
+class PDFExporter(grok.Adapter):
+    """Export content to HTML.
+    """
+    grok.provides(IContentExporter)
+    grok.context(IPublishable)
+    grok.name('pdf')
+
+    name = "PDF (zip)"
+    extension = "zip"
+    options = IExportOptions
+
+    def export(self, **options):
+        output = StringIO()
+        archive = ZipFile(output, "w", ZIP_DEFLATED)
+        temp_folder = tempfile.mkdtemp()
+        logger.info('Creating temp dir: %s' % (temp_folder))
+        HTMLEx = getAdapter(self.context, IContentExporter, name='html')
+        HTMLEx.export_to_folder(temp_folder, **options)
+        for root, dirs, files in os.walk(temp_folder):
+            for html_file in fnmatch.filter(files, '*.html'):
+                html_file_path = os.path.join(root, html_file)
+                pdf_file_name = '%s.pdf' % os.path.splitext(html_file)[0]
+                pdf_file_path = os.path.join(root, pdf_file_name)
+
+                archive.write(
+                    convert_html_to_pdf(html_file_path, pdf_file_path),
+                    os.path.relpath(pdf_file_path, temp_folder))
+
+        archive.close()
+        logger.info('Removing temp dir: %s' % (temp_folder))
+        shutil.rmtree(temp_folder)
+
+        return output.getvalue()