Source code for safe_qgis.html_renderer

"""
InaSAFE Disaster risk assessment tool developed by AusAid -
  **InaSAFE map making module.**

Contact : ole.moller.nielsen@gmail.com

.. note:: This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

"""
__author__ = 'tim@linfiniti.com'
__revision__ = '$Format:%H$'
__date__ = '10/01/2011'
__copyright__ = 'Copyright 2012, Australia Indonesia Facility for '
__copyright__ += 'Disaster Reduction'

import time
import logging

from PyQt4 import QtCore, QtGui, QtWebKit
from safe_qgis.utilities import (htmlHeader,
                                 htmlFooter,
                                 mmToPoints,
                                 setupPrinter,
                                 impactLayerAttribution)
from safe_interface import unique_filename, temp_dir
LOGGER = logging.getLogger('InaSAFE')


[docs]class HtmlRenderer(): """A class for creating a map.""" def __init__(self, thePageDpi): """Constructor for the Map class. Args: None Returns: None Raises: Any exceptions will be propagated. """ LOGGER.debug('InaSAFE HtmlRenderer class initialised') self.pageDpi = thePageDpi # Need to keep state here for loadCompleted signals self.webView = None self.htmlLoadedFlag = False
[docs] def tr(self, theString): """We implement this since we do not inherit QObject. Args: theString - string for translation. Returns: Translated version of theString. Raises: no exceptions explicitly raised. """ return QtCore.QCoreApplication.translate('HtmlRenderer', theString)
[docs] def renderHtmlToPixmap(self, theHtml, theWidthMM): """Render some HTML to a pixmap. Args: * theHtml - HTML to be rendered. It is assumed that the html is a snippet only, containing no body element - a standard header and footer will be appended. * theWidthMM- width of the table in mm - will be converted to points based on the resolution of our page. Returns: QPixmap Raises: Any exceptions raised by the InaSAFE library will be propagated. """ LOGGER.debug('InaSAFE Map renderHtmlToPixmap called') # Using 150dpi as the baseline, work out a standard text size # multiplier so that page renders equally well at different print # resolutions. myBaselineDpi = 150 myFactor = float(self.pageDpi) / myBaselineDpi myWidthPx = mmToPoints(theWidthMM, self.pageDpi) self.loadAndWait(theHtmlSnippet=theHtml) myFrame = self.webView.page().mainFrame() myFrame.setTextSizeMultiplier(myFactor) mySize = myFrame.contentsSize() mySize.setWidth(myWidthPx) self.webView.page().setViewportSize(mySize) myPixmap = QtGui.QPixmap(mySize) myPixmap.fill(QtGui.QColor(255, 255, 255)) myPainter = QtGui.QPainter(myPixmap) myFrame.render(myPainter) myPainter.end() myPixmap.save('/tmp/test.png') return myPixmap
[docs] def printToPdf(self, theHtml, theFilename=None): """Render an html snippet into the printer, paginating as needed. Args: * theHtml: str A string containing an html snippet. It will have a header and footer appended to it in order to make it a valid html document. The header will also apply the bootstrap theme to the document. * theFilename: str String containing a pdf file path that the output will be written to. Returns: str: The file path of the output pdf (which is the same as the theFilename parameter if it was specified. Raises: None """ LOGGER.info('InaSAFE Map printToPdf called') if theFilename is None: myHtmlPdfPath = unique_filename(prefix='table', suffix='.pdf', dir=temp_dir('work')) else: # We need to cast to python string in case we receive a QString myHtmlPdfPath = str(theFilename) self.printer = setupPrinter(myHtmlPdfPath) self.loadAndWait(theHtmlSnippet=theHtml) self.webView.print_(self.printer) return myHtmlPdfPath
[docs] def loadAndWait(self, theHtmlPath=None, theHtmlSnippet=None): """Load some html to a web view and wait till it is done.""" if theHtmlSnippet: myHeader = htmlHeader() myFooter = htmlFooter() myHtml = myHeader + theHtmlSnippet + myFooter else: myFile = file(theHtmlPath, 'rt') myHtml = myFile.readlines() myFile.close() self.webView = QtWebKit.QWebView() myFrame = self.webView.page().mainFrame() myFrame.setScrollBarPolicy(QtCore.Qt.Vertical, QtCore.Qt.ScrollBarAlwaysOff) myFrame.setScrollBarPolicy(QtCore.Qt.Horizontal, QtCore.Qt.ScrollBarAlwaysOff) self.webView.loadFinished.connect(self.htmlLoadedSlot) self.webView.setHtml(myHtml) self.htmlLoadedFlag = False myTimeOut = 20 myCounter = 0 mySleepPeriod = 1 while not self.htmlLoadedFlag and myCounter < myTimeOut: # Block until the event loop is done printing the page myCounter += 1 time.sleep(mySleepPeriod) QtCore.QCoreApplication.processEvents() if not self.htmlLoadedFlag: LOGGER.error('Failed to load html')
[docs] def htmlLoadedSlot(self): """Slot called when the page is loaded. Args: None Returns: None Raises: None """ self.htmlLoadedFlag = True LOGGER.debug('htmlLoadedSlot slot called') QtCore.QObject.disconnect(self.webView, QtCore.SIGNAL("loadFinished(bool)"), self.htmlLoadedSlot)
[docs] def printImpactTable(self, theKeywords, theFilename=None): """High level table generator to print layer keywords. It gets the summary and impact table from a QgsMapLayer's keywords and renders to pdf, returning the resulting PDF file path. Args: theKeywords: dic containing impact layer keywords (required) Returns: str: Path to generated pdf file. Raises: None """ myFilePath = theFilename if theFilename is None: myFilePath = unique_filename(suffix='.pdf', dir=temp_dir()) try: mySummaryTable = theKeywords['impact_summary'] except KeyError: mySummaryTable = None myAttributionTable = impactLayerAttribution(theKeywords) try: myFullTable = theKeywords['impact_table'] except KeyError: myFullTable = None try: myAggregationTable = theKeywords['postprocessing_report'] except KeyError: myAggregationTable = None myHtml = '' if mySummaryTable != myFullTable and mySummaryTable is not None: myHtml = '<h2>%s</h2>' % self.tr('Summary Table') myHtml += mySummaryTable if myAggregationTable is not None: myHtml += myAggregationTable if myAttributionTable is not None: myHtml += myAttributionTable myHtml += '<h2>%s</h2>' % self.tr('Detailed Table') myHtml += myFullTable else: if myAggregationTable is not None: myHtml = myAggregationTable if myFullTable is not None: myHtml += myFullTable if myAttributionTable is not None: myHtml += myAttributionTable # myNewFilePath should be the same as myFilePath myNewFilePath = self.printToPdf(myHtml, myFilePath) return myNewFilePath