[Sugar-devel] [PATCH Browse 2/2] Add support for exporting ("printing") to PDF

Sascha Silbe silbe at activitycentral.com
Sun May 29 10:04:22 EDT 2011


The content of the current tab gets exported as a new data store entry
(independent from the current Browse session), including some of the
metadata (title, original_url).

Similar to the way it works for Turtle Art, we expose the functionality as a
button in the Activity toolbar, with 'document-save' as icon. There is no way
for the user to influence the result (i.e. to set print options).

To work around a python-xpcom bug [1], we "patch" python-xpcom at runtime.
Given the current state of affairs regarding python-xpcom, this is probably
the best we can do.

[1] http://bugs.debian.org/628484

Signed-off-by: Sascha Silbe <silbe at activitycentral.com>
---
 browser.py    |   68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 webtoolbar.py |   10 ++++++++
 2 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/browser.py b/browser.py
index 649a71e..bd57e79 100644
--- a/browser.py
+++ b/browser.py
@@ -20,6 +20,7 @@ import os
 import time
 import logging
 from gettext import gettext as _
+import tempfile
 
 import gobject
 import gtk
@@ -304,6 +305,73 @@ class Browser(WebView):
 
         self.emit('is-setup')
 
+    def export_pdf(self):
+        temp_dir = os.path.join(activity.get_activity_root(), 'instance')
+        pdf_file = tempfile.NamedTemporaryFile(suffix='.pdf', dir=temp_dir,
+                                               delete=False)
+
+        logging.debug('pdf_file.name = %r', pdf_file.name)
+        try:
+            self._generate_pdf(pdf_file.name)
+        except:
+            os.remove(pdf_file.name)
+            raise
+
+    def _generate_pdf(self, file_name):
+        cls = components.classes['@mozilla.org/gfx/printsettings-service;1']
+        setting_service = cls.getService(interfaces.nsIPrintSettingsService)
+        req = self.get_dom_window().QueryInterface(interfaces.nsIInterfaceRequestor)
+        print_iface = req.getInterface(interfaces.nsIWebBrowserPrint)
+
+        settings = setting_service.newPrintSettings
+        settings.printSilent = True
+        settings.printToFile = True
+        settings.toFileName = file_name
+        settings.outputFormat = interfaces.nsIPrintSettings.kOutputFormatPDF
+
+        try:
+            print_method = getattr(print_iface, 'print')
+        except SyntaxError:
+            # HACK: Run-time patch python-xpcom to work around a bug.
+            # 'print' is a reserved keyword, so it must not occur in generated
+            # code.
+            iid = interfaces.nsIWebBrowserPrint
+            internal_interface = print_iface.__dict__['_interfaces_'][iid]
+            info = internal_interface.__dict__['_method_infos_']['print']
+            info.name = 'print_'
+            print_method = getattr(print_iface, 'print')
+
+        save_info = {'file_name': file_name, 'title': self.props.title,
+            'uri': self.get_url_from_nsiuri(self.progress.location)}
+
+        def save_callback(info):
+            # Even after the progress listener reports completion, the file
+            # is still empty. We need to wait for everything to calm down
+            # before triggering the save.
+            gobject.idle_add(self._save_pdf, info)
+
+        listener = xpcom.server.WrapObject(SaveListener(save_info,
+                                                        save_callback),
+                                           interfaces.nsIWebProgressListener)
+        print_method(settings, listener)
+
+    def _save_pdf(self, info):
+        logging.debug('_save_pdf(%r)', info)
+        try:
+            if os.stat(info['file_name']).st_size == 0:
+                raise ValueError('Generated PDF file is empty')
+
+            ds_object = datastore.create()
+            ds_object.file_path = info['file_name']
+            ds_object.metadata['mime_type'] = 'application/pdf'
+            ds_object.metadata['title'] = info['title']
+            ds_object.metadata['original_uri'] = info['uri']
+            datastore.write(ds_object, transfer_ownership=True)
+        except:
+            if os.path.exists(info['file_name']):
+                os.remove(info['file_name'])
+            raise
+
     def get_url_from_nsiuri(self, uri):
         """
         get a nsIURI object and return a string with the url
diff --git a/webtoolbar.py b/webtoolbar.py
index 8b0e108..3ce4011 100644
--- a/webtoolbar.py
+++ b/webtoolbar.py
@@ -255,6 +255,12 @@ class PrimaryToolbar(ToolbarBase):
         else:
             toolbar = self
 
+        export_pdf_button = ToolButton('document-save')
+        export_pdf_button.set_tooltip(_('Export as PDF'))
+        export_pdf_button.connect('clicked', self._export_pdf_cb)
+        activity_button.page.insert(export_pdf_button, -1)
+        export_pdf_button.show()
+
         self._go_home = ToolButton('go-home')
         self._go_home.set_tooltip(_('Home page'))
         self._go_home.connect('clicked', self._go_home_cb)
@@ -494,3 +500,7 @@ class PrimaryToolbar(ToolbarBase):
 
     def _link_add_clicked_cb(self, button):
         self.emit('add-link')
+
+    def _export_pdf_cb(self, button):
+        browser = self._tabbed_view.props.current_browser
+        browser.export_pdf()
-- 
1.7.2.5



More information about the Sugar-devel mailing list