class WebPage(QObject): javaScriptAlertSent = pyqtSignal(str) javaScriptConsoleMessageSent = pyqtSignal(str, int, str) loadStarted = pyqtSignal() loadFinished = pyqtSignal(str) resourceReceived = pyqtSignal('QVariantMap') resourceRequested = pyqtSignal('QVariantMap') def __init__(self, parent=None): QObject.__init__(self, parent) # variable declarations self.m_paperSize = {} self.m_clipRect = QRect() self.m_libraryPath = '' self.setObjectName('WebPage') self.m_webPage = CustomPage(self) self.m_mainFrame = self.m_webPage.mainFrame() self.m_webPage.loadStarted.connect(self.loadStarted) self.m_webPage.loadFinished.connect(self.finish) # Start with transparent background palette = self.m_webPage.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.m_webPage.setPalette(palette) # Page size does not need to take scrollbars into account self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.m_webPage.settings().setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True) self.m_webPage.settings().setOfflineStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageDatabaseEnabled, True) self.m_webPage.settings().setAttribute(QWebSettings.OfflineWebApplicationCacheEnabled, True) self.m_webPage.settings().setOfflineWebApplicationCachePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_webPage.settings().setAttribute(QWebSettings.FrameFlatteningEnabled, True) self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageEnabled, True) self.m_webPage.settings().setLocalStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) # Ensure we have a document.body. self.m_webPage.mainFrame().setHtml('<html><body></body></html>') self.m_webPage.setViewportSize(QSize(400, 300)) do_action('WebPageInit') def applySettings(self, defaults): opt = self.m_webPage.settings() opt.setAttribute(QWebSettings.AutoLoadImages, defaults['loadImages']) opt.setAttribute(QWebSettings.PluginsEnabled, defaults['loadPlugins']) opt.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, defaults['localAccessRemote']) if 'userAgent' in defaults: self.m_webPage.m_userAgent = defaults['userAgent'] def finish(self, ok): status = 'success' if ok else 'fail' self.loadFinished.emit(status) def mainFrame(self): return self.m_mainFrame def renderImage(self): viewportSize = self.m_webPage.viewportSize() frameRect = QRect(QPoint(0, 0), viewportSize) if not self.m_clipRect.isEmpty(): frameRect = self.m_clipRect if self.m_webPage.scrollPosition: self.m_webPage.mainFrame().\ setScrollPosition(QPoint(self.m_webPage.scrollPosition.x(), self.m_webPage.scrollPosition.y() )) image = QImage(frameRect.size(), QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) painter = QPainter() # We use tiling approach to work-around Qt software rasterizer bug # when dealing with very large paint device. # See http://code.google.com/p/phantomjs/issues/detail?id=54. tileSize = 4096 htiles = (image.width() + tileSize - 1) / tileSize vtiles = (image.height() + tileSize - 1) / tileSize for x in range(htiles): for y in range(vtiles): tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32) tileBuffer.fill(qRgba(255, 255, 255, 0)) # Render the web page onto the small tile first painter.begin(tileBuffer) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.translate(-frameRect.left(), -frameRect.top()) painter.translate(-x * tileSize, -y * tileSize) self.m_mainFrame.render(painter, QRegion(frameRect)) painter.end() # Copy the tile to the main buffer painter.begin(image) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.drawImage(x * tileSize, y * tileSize, tileBuffer) painter.end() self.m_webPage.setViewportSize(viewportSize) return image def renderPdf(self, fileName): p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(fileName) p.setResolution(pdf_dpi) paperSize = self.m_paperSize if not len(paperSize): pageSize = QSize(self.m_webPage.mainFrame().contentsSize()) paperSize['width'] = str(pageSize.width()) + 'px' paperSize['height'] = str(pageSize.height()) + 'px' paperSize['border'] = '0px' if paperSize.get('width') and paperSize.get('height'): sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])), ceil(self.stringToPointSize(paperSize['height']))) p.setPaperSize(sizePt, QPrinter.Point) elif 'format' in paperSize: orientation = QPrinter.Landscape if paperSize.get('orientation') and paperSize['orientation'].lower() == 'landscape' else QPrinter.Portrait orientation = QPrinter.Orientation(orientation) p.setOrientation(orientation) formats = { 'A0': QPrinter.A0, 'A1': QPrinter.A1, 'A2': QPrinter.A2, 'A3': QPrinter.A3, 'A4': QPrinter.A4, 'A5': QPrinter.A5, 'A6': QPrinter.A6, 'A7': QPrinter.A7, 'A8': QPrinter.A8, 'A9': QPrinter.A9, 'B0': QPrinter.B0, 'B1': QPrinter.B1, 'B2': QPrinter.B2, 'B3': QPrinter.B3, 'B4': QPrinter.B4, 'B5': QPrinter.B5, 'B6': QPrinter.B6, 'B7': QPrinter.B7, 'B8': QPrinter.B8, 'B9': QPrinter.B9, 'B10': QPrinter.B10, 'C5E': QPrinter.C5E, 'Comm10E': QPrinter.Comm10E, 'DLE': QPrinter.DLE, 'Executive': QPrinter.Executive, 'Folio': QPrinter.Folio, 'Ledger': QPrinter.Ledger, 'Legal': QPrinter.Legal, 'Letter': QPrinter.Letter, 'Tabloid': QPrinter.Tabloid } p.setPaperSize(QPrinter.A4) # fallback for format, size in formats.items(): if format.lower() == paperSize['format'].lower(): p.setPaperSize(size) break else: return False border = floor(self.stringToPointSize(paperSize['border'])) if paperSize.get('border') else 0 p.setPageMargins(border, border, border, border, QPrinter.Point) self.m_webPage.mainFrame().print_(p) return True def setNetworkAccessManager(self, networkAccessManager): self.m_webPage.setNetworkAccessManager(networkAccessManager) networkAccessManager.resourceRequested.connect(self.resourceRequested) networkAccessManager.resourceReceived.connect(self.resourceReceived) def stringToPointSize(self, string): units = ( ('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0), ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54) ) for unit, format in units: if string.endswith(unit): value = string.rstrip(unit) return float(value) * format return 0 def userAgent(self): return self.m_webPage.m_userAgent ## # Properties and methods exposed to JavaScript ## @pyqtSlot(str) def _appendScriptElement(self, scriptUrl): self.m_mainFrame.evaluateJavaScript(''' var el = document.createElement('script'); el.onload = function() { alert('%(scriptUrl)s'); }; el.src = '%(scriptUrl)s'; document.body.appendChild(el); ''' % {'scriptUrl': scriptUrl}) @pyqtProperty('QVariantMap') def clipRect(self): result = { 'width': self.m_clipRect.width(), 'height': self.m_clipRect.height(), 'top': self.m_clipRect.top(), 'left': self.m_clipRect.left() } return result @clipRect.setter def clipRect(self, size): sizes = {'width': 0, 'height': 0, 'top': 0, 'left': 0} for item in sizes.keys(): try: sizes[item] = int(size[item]) if sizes[item] < 0: if item not in ('top', 'left'): sizes[item] = 0 except (KeyError, ValueError): sizes[item] = self.clipRect[item] self.m_clipRect = QRect(sizes['left'], sizes['top'], sizes['width'], sizes['height']) @pyqtProperty(str) def content(self): return self.m_mainFrame.toHtml() @content.setter def content(self, content): self.m_mainFrame.setHtml(content) @pyqtSlot(str, result='QVariant') def evaluate(self, code): function = '(%s)()' % code return self.m_mainFrame.evaluateJavaScript(function) @pyqtSlot(str, result=bool) def injectJs(self, filePath): return injectJsInFrame(filePath, self.m_libraryPath, self.m_mainFrame) @pyqtSlot(str, str, 'QVariantMap') @pyqtSlot(str, 'QVariantMap', 'QVariantMap') def openUrl(self, address, op, settings): operation = op body = QByteArray() self.applySettings(settings) self.m_webPage.triggerAction(QWebPage.Stop) if type(op) is dict: operation = op.get('operation') body = QByteArray(op.get('data', '')) if operation == '': operation = 'get' networkOp = QNetworkAccessManager.CustomOperation operation = operation.lower() if operation == 'get': networkOp = QNetworkAccessManager.GetOperation elif operation == 'head': networkOp = QNetworkAccessManager.HeadOperation elif operation == 'put': networkOp = QNetworkAccessManager.PutOperation elif operation == 'post': networkOp = QNetworkAccessManager.PostOperation elif operation == 'delete': networkOp = QNetworkAccessManager.DeleteOperation if networkOp == QNetworkAccessManager.CustomOperation: self.m_mainFrame.evaluateJavaScript('console.error("Unknown network operation: %s");' % operation) return self.m_mainFrame.load(QNetworkRequest(QUrl(address)), networkOp, body) @pyqtProperty('QVariantMap') def paperSize(self): return self.m_paperSize @paperSize.setter def paperSize(self, size): self.m_paperSize = size @pyqtSlot(str, result=bool) def render(self, fileName): if self.m_mainFrame.contentsSize() == '': return False fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith('.pdf'): return self.renderPdf(fileName) image = self.renderImage() return image.save(fileName) @pyqtProperty(str) def libraryPath(self): return self.m_libraryPath @libraryPath.setter def libraryPath(self, dirPath): self.m_libraryPath = dirPath @pyqtSlot(str, str) def uploadFile(self, selector, fileName): el = self.m_mainFrame.findFirstElement(selector) if el.isNull(): return self.m_webPage.m_uploadFile = fileName el.evaluateJavaScript(''' (function (el) { var ev = document.createEvent('MouseEvents'); ev.initEvent('click', true, true); el.dispatchEvent(ev); })(this) ''') @pyqtProperty('QVariantMap') def viewportSize(self): size = self.m_webPage.viewportSize() result = { 'width': size.width(), 'height': size.height() } return result @viewportSize.setter def viewportSize(self, size): sizes = {'width': 0, 'height': 0} for item in sizes.keys(): try: sizes[item] = int(size[item]) if sizes[item] < 0: sizes[item] = 0 except (KeyError, ValueError): sizes[item] = self.viewportSize[item] self.m_webPage.setViewportSize(QSize(sizes['width'], sizes['height'])) @pyqtProperty('QVariantMap') def scrollPosition(self): scroll = self.m_webPage.scrollPosition result = { 'left': scroll.x(), 'top': scroll.y() } return result @scrollPosition.setter def scrollPosition(self, size): names = ('left', 'top') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_webPage.scrollPosition, item)() self.m_webPage.scrollPosition = QPoint(left, top) do_action('WebPage')
class Phantom(QObject): def __init__(self, args, parent=None): QObject.__init__(self, parent) # variable declarations self.m_loadStatus = self.m_state = '' self.m_var = self.m_paperSize = self.m_loadScript_cache = {} self.m_verbose = args.verbose self.m_page = WebPage(self) self.m_clipRect = QRect() # setup the values from args self.m_script = args.script.read() self.m_scriptFile = args.script.name self.m_scriptDir = os.path.dirname(args.script.name) + '/' self.m_args = args.script_args self.m_upload_file = args.upload_file autoLoadImages = False if args.load_images == 'no' else True pluginsEnabled = True if args.load_plugins == 'yes' else False args.script.close() do_action('PhantomInitPre', Bunch(locals())) palette = self.m_page.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.m_page.setPalette(palette) if not args.proxy: QNetworkProxyFactory.setUseSystemConfiguration(True) else: proxy = QNetworkProxy(QNetworkProxy.HttpProxy, args.proxy[0], int(args.proxy[1])) QNetworkProxy.setApplicationProxy(proxy) self.m_page.settings().setAttribute(QWebSettings.AutoLoadImages, autoLoadImages) self.m_page.settings().setAttribute(QWebSettings.PluginsEnabled, pluginsEnabled) self.m_page.settings().setAttribute( QWebSettings.FrameFlatteningEnabled, True) self.m_page.settings().setAttribute( QWebSettings.OfflineStorageDatabaseEnabled, True) self.m_page.settings().setAttribute(QWebSettings.LocalStorageEnabled, True) self.m_page.settings().setLocalStoragePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_page.settings().setOfflineStoragePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation)) # Ensure we have a document.body. self.m_page.mainFrame().setHtml('<html><body></body></html>') self.m_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.m_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) m_netAccessMan = NetworkAccessManager(args.disk_cache, args.ignore_ssl_errors, self) self.m_page.setNetworkAccessManager(m_netAccessMan) # inject our properties and slots into javascript self.m_page.mainFrame().javaScriptWindowObjectCleared.connect( self.inject) self.m_page.loadFinished.connect(self.finish) do_action('PhantomInitPost', Bunch(locals())) def execute(self): if self.m_script.startswith('#!'): self.m_script = '//' + self.m_script if self.m_scriptFile.lower().endswith('.coffee'): coffee = CSConverter(self) self.m_script = coffee.convert(self.m_script) self.m_page.mainFrame().evaluateJavaScript(self.m_script) def finish(self, success): self.m_loadStatus = 'success' if success else 'fail' self.m_page.mainFrame().evaluateJavaScript(self.m_script) def inject(self): self.m_page.mainFrame().addToJavaScriptWindowObject('phantom', self) def renderPdf(self, fileName): p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(fileName) p.setResolution(pdf_dpi) paperSize = self.m_paperSize if not len(paperSize): pageSize = QSize(self.m_page.mainFrame().contentsSize()) paperSize['width'] = str(pageSize.width()) + 'px' paperSize['height'] = str(pageSize.height()) + 'px' paperSize['border'] = '0px' if paperSize.get('width') and paperSize.get('height'): sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])), ceil(self.stringToPointSize(paperSize['height']))) p.setPaperSize(sizePt, QPrinter.Point) elif 'format' in paperSize: orientation = QPrinter.Landscape if paperSize.get( 'orientation') and paperSize['orientation'].lower( ) == 'landscape' else QPrinter.Portrait orientation = QPrinter.Orientation(orientation) p.setOrientation(orientation) formats = { 'A3': QPrinter.A3, 'A4': QPrinter.A4, 'A5': QPrinter.A5, 'Legal': QPrinter.Legal, 'Letter': QPrinter.Letter, 'Tabloid': QPrinter.Tabloid } p.setPaperSize(QPrinter.A4) # fallback for format, size in formats.items(): if format.lower() == paperSize['format'].lower(): p.setPaperSize(size) break else: return False border = floor(self.stringToPointSize( paperSize['border'])) if paperSize.get('border') else 0 p.setPageMargins(border, border, border, border, QPrinter.Point) self.m_page.mainFrame().print_(p) return True def returnValue(self): return self.m_returnValue def stringToPointSize(self, string): units = (('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0), ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54)) for unit, format in units: if string.endswith(unit): value = string.rstrip(unit) return float(value) * format return 0 ## # Properties and methods exposed to JavaScript ## @pyqtProperty('QStringList') def args(self): return self.m_args @pyqtProperty('QVariantMap') def clipRect(self): result = { 'width': self.m_clipRect.width(), 'height': self.m_clipRect.height(), 'top': self.m_clipRect.top(), 'left': self.m_clipRect.left() } return result @clipRect.setter def clipRect(self, size): names = ('width', 'height', 'top', 'left') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: if item not in ('top', 'left'): globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_clipRect, item)() self.m_clipRect = QRect(left, top, width, height) @pyqtProperty(str) def content(self): return self.m_page.mainFrame().toHtml() @content.setter def content(self, content): self.m_page.mainFrame().setHtml(content) @pyqtSlot() @pyqtSlot(int) def exit(self, code=0): self.m_returnValue = code self.m_page.loadFinished.disconnect(self.finish) QTimer.singleShot(0, qApp, SLOT('quit()')) @pyqtProperty(str) def loadStatus(self): return self.m_loadStatus @pyqtSlot(str, result=bool) def loadScript(self, script): if script in self.m_loadScript_cache: self.m_page.mainFrame().evaluateJavaScript( self.m_loadScript_cache[script]) return True scriptFile = script try: script = codecs.open(self.m_scriptDir + script, encoding='utf-8') script = script.read() except IOError: return False if script.startswith('#!'): script = '//' + script if scriptFile.lower().endswith('.coffee'): coffee = CSConverter(self) script = coffee.convert(script) self.m_loadScript_cache[scriptFile] = script self.m_page.mainFrame().evaluateJavaScript(script) return True @pyqtSlot(str, name='open') def open_(self, address): qDebug('Opening address %s' % address) self.m_page.triggerAction(QWebPage.Stop) self.m_loadStatus = 'loading' self.m_page.mainFrame().setUrl(QUrl(address)) @pyqtProperty('QVariantMap') def paperSize(self): return self.m_paperSize @paperSize.setter def paperSize(self, size): self.m_paperSize = size @pyqtSlot(str, result=bool) def render(self, fileName): fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith('.pdf'): return self.renderPdf(fileName) viewportSize = QSize(self.m_page.viewportSize()) pageSize = QSize(self.m_page.mainFrame().contentsSize()) bufferSize = QSize() if not self.m_clipRect.isEmpty(): bufferSize = self.m_clipRect.size() else: bufferSize = self.m_page.mainFrame().contentsSize() if pageSize == '': return False image = QImage(bufferSize, QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) p = QPainter(image) p.setRenderHint(QPainter.Antialiasing, True) p.setRenderHint(QPainter.TextAntialiasing, True) p.setRenderHint(QPainter.SmoothPixmapTransform, True) self.m_page.setViewportSize(pageSize) if not self.m_clipRect.isEmpty(): p.translate(-self.m_clipRect.left(), -self.m_clipRect.top()) self.m_page.mainFrame().render(p, QRegion(self.m_clipRect)) else: self.m_page.mainFrame().render(p) p.end() self.m_page.setViewportSize(viewportSize) return image.save(fileName) @pyqtSlot('QWebElement', str) def setFormInputFile(self, el, fileTag): self.m_page.m_nextFileTag = fileTag el.evaluateJavaScript('''(function(target){ var evt = document.createEvent('MouseEvents'); evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); target.dispatchEvent(evt);})(this);''') @pyqtSlot(int) def sleep(self, ms): startTime = QTime.currentTime() while True: QApplication.processEvents(QEventLoop.AllEvents, 25) if startTime.msecsTo(QTime.currentTime()) > ms: break usleep(0.005) @pyqtProperty(str) def state(self): return self.m_state @state.setter def state(self, value): self.m_state = value @pyqtProperty(str) def userAgent(self): return self.m_page.m_userAgent @userAgent.setter def userAgent(self, ua): self.m_page.m_userAgent = ua @pyqtSlot(str, result='QVariant') @pyqtSlot(int, result='QVariant') @pyqtSlot(str, 'QVariant') @pyqtSlot(int, 'QVariant') def ctx(self, name, value=None): if not value: return self.m_var.get(name) self.m_var[name] = value @pyqtProperty('QVariantMap') def version(self): version = { 'major': version_major, 'minor': version_minor, 'patch': version_patch } return version @pyqtProperty('QVariantMap') def viewportSize(self): size = self.m_page.viewportSize() result = {'width': size.width(), 'height': size.height()} return result @viewportSize.setter def viewportSize(self, size): names = ('width', 'height') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_page.viewportSize(), item)() self.m_page.setViewportSize(QSize(width, height)) do_action('Phantom', Bunch(locals()))
class Phantom(QObject): def __init__(self, args, parent=None): QObject.__init__(self, parent) # variable declarations self.m_loadStatus = self.m_state = '' self.m_var = self.m_paperSize = self.m_loadScript_cache = {} self.m_verbose = args.verbose self.m_page = WebPage(self) self.m_clipRect = QRect() # setup the values from args self.m_script = args.script.read() self.m_scriptFile = args.script.name self.m_scriptDir = os.path.dirname(args.script.name) + '/' self.m_args = args.script_args self.m_upload_file = args.upload_file autoLoadImages = False if args.load_images == 'no' else True pluginsEnabled = True if args.load_plugins == 'yes' else False args.script.close() do_action('PhantomInitPre', Bunch(locals())) palette = self.m_page.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.m_page.setPalette(palette) if not args.proxy: QNetworkProxyFactory.setUseSystemConfiguration(True) else: proxy = QNetworkProxy(QNetworkProxy.HttpProxy, args.proxy[0], int(args.proxy[1])) QNetworkProxy.setApplicationProxy(proxy) self.m_page.settings().setAttribute(QWebSettings.AutoLoadImages, autoLoadImages) self.m_page.settings().setAttribute(QWebSettings.PluginsEnabled, pluginsEnabled) self.m_page.settings().setAttribute(QWebSettings.FrameFlatteningEnabled, True) self.m_page.settings().setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True) self.m_page.settings().setAttribute(QWebSettings.LocalStorageEnabled, True) self.m_page.settings().setLocalStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_page.settings().setOfflineStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) # Ensure we have a document.body. self.m_page.mainFrame().setHtml('<html><body></body></html>') self.m_page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.m_page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) m_netAccessMan = NetworkAccessManager(args.disk_cache, args.ignore_ssl_errors, self) self.m_page.setNetworkAccessManager(m_netAccessMan) # inject our properties and slots into javascript self.m_page.mainFrame().javaScriptWindowObjectCleared.connect(self.inject) self.m_page.loadFinished.connect(self.finish) do_action('PhantomInitPost', Bunch(locals())) def execute(self): if self.m_script.startswith('#!'): self.m_script = '//' + self.m_script if self.m_scriptFile.lower().endswith('.coffee'): coffee = CSConverter(self) self.m_script = coffee.convert(self.m_script) self.m_page.mainFrame().evaluateJavaScript(self.m_script) def finish(self, success): self.m_loadStatus = 'success' if success else 'fail' self.m_page.mainFrame().evaluateJavaScript(self.m_script) def inject(self): self.m_page.mainFrame().addToJavaScriptWindowObject('phantom', self) def renderPdf(self, fileName): p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(fileName) p.setResolution(pdf_dpi) paperSize = self.m_paperSize if not len(paperSize): pageSize = QSize(self.m_page.mainFrame().contentsSize()) paperSize['width'] = str(pageSize.width()) + 'px' paperSize['height'] = str(pageSize.height()) + 'px' paperSize['border'] = '0px' if paperSize.get('width') and paperSize.get('height'): sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])), ceil(self.stringToPointSize(paperSize['height']))) p.setPaperSize(sizePt, QPrinter.Point) elif 'format' in paperSize: orientation = QPrinter.Landscape if paperSize.get('orientation') and paperSize['orientation'].lower() == 'landscape' else QPrinter.Portrait orientation = QPrinter.Orientation(orientation) p.setOrientation(orientation) formats = { 'A3': QPrinter.A3, 'A4': QPrinter.A4, 'A5': QPrinter.A5, 'Legal': QPrinter.Legal, 'Letter': QPrinter.Letter, 'Tabloid': QPrinter.Tabloid } p.setPaperSize(QPrinter.A4) # fallback for format, size in formats.items(): if format.lower() == paperSize['format'].lower(): p.setPaperSize(size) break else: return False border = floor(self.stringToPointSize(paperSize['border'])) if paperSize.get('border') else 0 p.setPageMargins(border, border, border, border, QPrinter.Point) self.m_page.mainFrame().print_(p) return True def returnValue(self): return self.m_returnValue def stringToPointSize(self, string): units = ( ('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0), ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54) ) for unit, format in units: if string.endswith(unit): value = string.rstrip(unit) return float(value) * format return 0 ## # Properties and methods exposed to JavaScript ## @pyqtProperty('QStringList') def args(self): return self.m_args @pyqtProperty('QVariantMap') def clipRect(self): result = { 'width': self.m_clipRect.width(), 'height': self.m_clipRect.height(), 'top': self.m_clipRect.top(), 'left': self.m_clipRect.left() } return result @clipRect.setter def clipRect(self, size): names = ('width', 'height', 'top', 'left') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: if item not in ('top', 'left'): globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_clipRect, item)() self.m_clipRect = QRect(left, top, width, height) @pyqtProperty(str) def content(self): return self.m_page.mainFrame().toHtml() @content.setter def content(self, content): self.m_page.mainFrame().setHtml(content) @pyqtSlot() @pyqtSlot(int) def exit(self, code=0): self.m_returnValue = code self.m_page.loadFinished.disconnect(self.finish) QTimer.singleShot(0, qApp, SLOT('quit()')) @pyqtProperty(str) def loadStatus(self): return self.m_loadStatus @pyqtSlot(str, result=bool) def loadScript(self, script): if script in self.m_loadScript_cache: self.m_page.mainFrame().evaluateJavaScript(self.m_loadScript_cache[script]) return True scriptFile = script try: script = codecs.open(self.m_scriptDir + script, encoding='utf-8') script = script.read() except IOError: return False if script.startswith('#!'): script = '//' + script if scriptFile.lower().endswith('.coffee'): coffee = CSConverter(self) script = coffee.convert(script) self.m_loadScript_cache[scriptFile] = script self.m_page.mainFrame().evaluateJavaScript(script) return True @pyqtSlot(str, name='open') def open_(self, address): qDebug('Opening address %s' % address) self.m_page.triggerAction(QWebPage.Stop) self.m_loadStatus = 'loading' self.m_page.mainFrame().setUrl(QUrl(address)) @pyqtProperty('QVariantMap') def paperSize(self): return self.m_paperSize @paperSize.setter def paperSize(self, size): self.m_paperSize = size @pyqtSlot(str, result=bool) def render(self, fileName): fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith('.pdf'): return self.renderPdf(fileName) viewportSize = QSize(self.m_page.viewportSize()) pageSize = QSize(self.m_page.mainFrame().contentsSize()) bufferSize = QSize() if not self.m_clipRect.isEmpty(): bufferSize = self.m_clipRect.size() else: bufferSize = self.m_page.mainFrame().contentsSize() if pageSize == '': return False image = QImage(bufferSize, QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) p = QPainter(image) p.setRenderHint(QPainter.Antialiasing, True) p.setRenderHint(QPainter.TextAntialiasing, True) p.setRenderHint(QPainter.SmoothPixmapTransform, True) self.m_page.setViewportSize(pageSize) if not self.m_clipRect.isEmpty(): p.translate(-self.m_clipRect.left(), -self.m_clipRect.top()) self.m_page.mainFrame().render(p, QRegion(self.m_clipRect)) else: self.m_page.mainFrame().render(p) p.end() self.m_page.setViewportSize(viewportSize) return image.save(fileName) @pyqtSlot('QWebElement', str) def setFormInputFile(self, el, fileTag): self.m_page.m_nextFileTag = fileTag el.evaluateJavaScript('''(function(target){ var evt = document.createEvent('MouseEvents'); evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); target.dispatchEvent(evt);})(this);''') @pyqtSlot(int) def sleep(self, ms): startTime = QTime.currentTime() while True: QApplication.processEvents(QEventLoop.AllEvents, 25) if startTime.msecsTo(QTime.currentTime()) > ms: break usleep(0.005) @pyqtProperty(str) def state(self): return self.m_state @state.setter def state(self, value): self.m_state = value @pyqtProperty(str) def userAgent(self): return self.m_page.m_userAgent @userAgent.setter def userAgent(self, ua): self.m_page.m_userAgent = ua @pyqtSlot(str, result='QVariant') @pyqtSlot(int, result='QVariant') @pyqtSlot(str, 'QVariant') @pyqtSlot(int, 'QVariant') def ctx(self, name, value=None): if not value: return self.m_var.get(name) self.m_var[name] = value @pyqtProperty('QVariantMap') def version(self): version = { 'major': version_major, 'minor': version_minor, 'patch': version_patch } return version @pyqtProperty('QVariantMap') def viewportSize(self): size = self.m_page.viewportSize() result = { 'width': size.width(), 'height': size.height() } return result @viewportSize.setter def viewportSize(self, size): names = ('width', 'height') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_page.viewportSize(), item)() self.m_page.setViewportSize(QSize(width, height)) do_action('Phantom', Bunch(locals()))
class WebPage(QObject): javaScriptAlertSent = pyqtSignal(str) javaScriptConsoleMessageSent = pyqtSignal(str, int, str) loadStarted = pyqtSignal() loadFinished = pyqtSignal(str) resourceReceived = pyqtSignal('QVariantMap') resourceRequested = pyqtSignal('QVariantMap') def __init__(self, parent=None): QObject.__init__(self, parent) # variable declarations self.m_paperSize = {} self.m_clipRect = QRect() self.m_libraryPath = '' self.setObjectName('WebPage') self.m_webPage = CustomPage(self) self.m_mainFrame = self.m_webPage.mainFrame() self.m_webPage.loadStarted.connect(self.loadStarted) self.m_webPage.loadFinished.connect(self.finish) # Start with transparent background palette = self.m_webPage.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.m_webPage.setPalette(palette) # Page size does not need to take scrollbars into account self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.m_webPage.settings().setAttribute( QWebSettings.OfflineStorageDatabaseEnabled, True) self.m_webPage.settings().setOfflineStoragePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_webPage.settings().setAttribute( QWebSettings.LocalStorageDatabaseEnabled, True) self.m_webPage.settings().setAttribute( QWebSettings.OfflineWebApplicationCacheEnabled, True) self.m_webPage.settings().setOfflineWebApplicationCachePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_webPage.settings().setAttribute( QWebSettings.FrameFlatteningEnabled, True) self.m_webPage.settings().setAttribute( QWebSettings.LocalStorageEnabled, True) self.m_webPage.settings().setLocalStoragePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation)) # Ensure we have a document.body. self.m_webPage.mainFrame().setHtml('<html><body></body></html>') self.m_webPage.setViewportSize(QSize(400, 300)) do_action('WebPageInit', Bunch(locals())) def applySettings(self, defaults): opt = self.m_webPage.settings() opt.setAttribute(QWebSettings.AutoLoadImages, defaults['loadImages']) opt.setAttribute(QWebSettings.PluginsEnabled, defaults['loadPlugins']) opt.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, defaults['localAccessRemote']) if 'userAgent' in defaults: self.m_webPage.m_userAgent = defaults['userAgent'] def finish(self, ok): status = 'success' if ok else 'fail' self.loadFinished.emit(status) def mainFrame(self): return self.m_mainFrame def renderImage(self): frameRect = QRect(QPoint(0, 0), self.m_mainFrame.contentsSize()) if not self.m_clipRect.isEmpty(): frameRect = self.m_clipRect viewportSize = self.m_webPage.viewportSize() self.m_webPage.setViewportSize(self.m_mainFrame.contentsSize()) image = QImage(frameRect.size(), QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) painter = QPainter() # We use tiling approach to work-around Qt software rasterizer bug # when dealing with very large paint device. # See http://code.google.com/p/phantomjs/issues/detail?id=54. tileSize = 4096 htiles = (image.width() + tileSize - 1) / tileSize vtiles = (image.height() + tileSize - 1) / tileSize for x in range(htiles): for y in range(vtiles): tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32) tileBuffer.fill(qRgba(255, 255, 255, 0)) # Render the web page onto the small tile first painter.begin(tileBuffer) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.translate(-frameRect.left(), -frameRect.top()) painter.translate(-x * tileSize, -y * tileSize) self.m_mainFrame.render(painter, QRegion(frameRect)) painter.end() # Copy the tile to the main buffer painter.begin(image) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.drawImage(x * tileSize, y * tileSize, tileBuffer) painter.end() self.m_webPage.setViewportSize(viewportSize) return image def renderPdf(self, fileName): p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(fileName) p.setResolution(pdf_dpi) paperSize = self.m_paperSize if not len(paperSize): pageSize = QSize(self.m_webPage.mainFrame().contentsSize()) paperSize['width'] = str(pageSize.width()) + 'px' paperSize['height'] = str(pageSize.height()) + 'px' paperSize['border'] = '0px' if paperSize.get('width') and paperSize.get('height'): sizePt = QSizeF(ceil(self.stringToPointSize(paperSize['width'])), ceil(self.stringToPointSize(paperSize['height']))) p.setPaperSize(sizePt, QPrinter.Point) elif 'format' in paperSize: orientation = QPrinter.Landscape if paperSize.get( 'orientation') and paperSize['orientation'].lower( ) == 'landscape' else QPrinter.Portrait orientation = QPrinter.Orientation(orientation) p.setOrientation(orientation) formats = { 'A0': QPrinter.A0, 'A1': QPrinter.A1, 'A2': QPrinter.A2, 'A3': QPrinter.A3, 'A4': QPrinter.A4, 'A5': QPrinter.A5, 'A6': QPrinter.A6, 'A7': QPrinter.A7, 'A8': QPrinter.A8, 'A9': QPrinter.A9, 'B0': QPrinter.B0, 'B1': QPrinter.B1, 'B2': QPrinter.B2, 'B3': QPrinter.B3, 'B4': QPrinter.B4, 'B5': QPrinter.B5, 'B6': QPrinter.B6, 'B7': QPrinter.B7, 'B8': QPrinter.B8, 'B9': QPrinter.B9, 'B10': QPrinter.B10, 'C5E': QPrinter.C5E, 'Comm10E': QPrinter.Comm10E, 'DLE': QPrinter.DLE, 'Executive': QPrinter.Executive, 'Folio': QPrinter.Folio, 'Ledger': QPrinter.Ledger, 'Legal': QPrinter.Legal, 'Letter': QPrinter.Letter, 'Tabloid': QPrinter.Tabloid } p.setPaperSize(QPrinter.A4) # fallback for format, size in formats.items(): if format.lower() == paperSize['format'].lower(): p.setPaperSize(size) break else: return False border = floor(self.stringToPointSize( paperSize['border'])) if paperSize.get('border') else 0 p.setPageMargins(border, border, border, border, QPrinter.Point) self.m_webPage.mainFrame().print_(p) return True def setNetworkAccessManager(self, networkAccessManager): self.m_webPage.setNetworkAccessManager(networkAccessManager) networkAccessManager.resourceRequested.connect(self.resourceRequested) networkAccessManager.resourceReceived.connect(self.resourceReceived) def stringToPointSize(self, string): units = (('mm', 72 / 25.4), ('cm', 72 / 2.54), ('in', 72.0), ('px', 72.0 / pdf_dpi / 2.54), ('', 72.0 / pdf_dpi / 2.54)) for unit, format in units: if string.endswith(unit): value = string.rstrip(unit) return float(value) * format return 0 def userAgent(self): return self.m_webPage.m_userAgent ## # Properties and methods exposed to JavaScript ## @pyqtSlot(str) def _appendScriptElement(self, scriptUrl): self.m_mainFrame.evaluateJavaScript(''' var el = document.createElement('script'); el.onload = function() { alert('%(scriptUrl)s'); }; el.src = '%(scriptUrl)s'; document.body.appendChild(el); ''' % {'scriptUrl': scriptUrl}) @pyqtProperty('QVariantMap') def clipRect(self): result = { 'width': self.m_clipRect.width(), 'height': self.m_clipRect.height(), 'top': self.m_clipRect.top(), 'left': self.m_clipRect.left() } return result @clipRect.setter def clipRect(self, size): names = ('width', 'height', 'top', 'left') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: if item not in ('top', 'left'): globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_clipRect, item)() self.m_clipRect = QRect(left, top, width, height) @pyqtProperty(str) def content(self): return self.m_mainFrame.toHtml() @content.setter def content(self, content): self.m_mainFrame.setHtml(content) @pyqtSlot(str, result='QVariant') def evaluate(self, code): function = '(%s)()' % code return self.m_mainFrame.evaluateJavaScript(function) @pyqtSlot(str, result=bool) def injectJs(self, filePath): return injectJsInFrame(filePath, self.m_libraryPath, self.m_mainFrame) @pyqtSlot(str, str, 'QVariantMap') @pyqtSlot(str, 'QVariantMap', 'QVariantMap') def openUrl(self, address, op, settings): operation = op body = QByteArray() self.applySettings(settings) self.m_webPage.triggerAction(QWebPage.Stop) if type(op) is dict: operation = op.get('operation') body = QByteArray(op.get('data', '')) if operation == '': operation = 'get' networkOp = QNetworkAccessManager.CustomOperation operation = operation.lower() if operation == 'get': networkOp = QNetworkAccessManager.GetOperation elif operation == 'head': networkOp = QNetworkAccessManager.HeadOperation elif operation == 'put': networkOp = QNetworkAccessManager.PutOperation elif operation == 'post': networkOp = QNetworkAccessManager.PostOperation elif operation == 'delete': networkOp = QNetworkAccessManager.DeleteOperation if networkOp == QNetworkAccessManager.CustomOperation: self.m_mainFrame.evaluateJavaScript( 'console.error("Unknown network operation: %s");' % operation) return self.m_mainFrame.load(QNetworkRequest(QUrl(address)), networkOp, body) @pyqtProperty('QVariantMap') def paperSize(self): return self.m_paperSize @paperSize.setter def paperSize(self, size): self.m_paperSize = size @pyqtSlot(str, result=bool) def render(self, fileName): if self.m_mainFrame.contentsSize() == '': return False fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith('.pdf'): return self.renderPdf(fileName) image = self.renderImage() return image.save(fileName) @pyqtProperty(str) def libraryPath(self): return self.m_libraryPath @libraryPath.setter def libraryPath(self, dirPath): self.m_libraryPath = dirPath @pyqtSlot(str, str) def uploadFile(self, selector, fileName): el = self.m_mainFrame.findFirstElement(selector) if el.isNull(): return self.m_webPage.m_uploadFile = fileName el.evaluateJavaScript(''' (function (el) { var ev = document.createEvent('MouseEvents'); ev.initEvent('click', true, true); el.dispatchEvent(ev); })(this) ''') @pyqtProperty('QVariantMap') def viewportSize(self): size = self.m_webPage.viewportSize() result = {'width': size.width(), 'height': size.height()} return result @viewportSize.setter def viewportSize(self, size): names = ('width', 'height') for item in names: try: globals()[item] = int(size[item]) if globals()[item] < 0: globals()[item] = 0 except KeyError: globals()[item] = getattr(self.m_webPage.viewportSize(), item)() self.m_webPage.setViewportSize(QSize(width, height)) do_action('WebPage', Bunch(locals()))
class WebPage(QObject): initialized = pyqtSignal() javaScriptAlertSent = pyqtSignal(str) javaScriptConsoleMessageSent = pyqtSignal(str, int, str) loadStarted = pyqtSignal() loadFinished = pyqtSignal(str) resourceReceived = pyqtSignal("QVariantMap") resourceRequested = pyqtSignal("QVariantMap") def __init__(self, parent): QObject.__init__(self, parent) self.m_parent = parent # variable declarations self.m_paperSize = {} self.m_clipRect = QRect() self.m_libraryPath = "" self.m_mousePos = QPoint() self.setObjectName("WebPage") self.m_webPage = CustomPage(self) self.m_mainFrame = self.m_webPage.mainFrame() self.m_mainFrame.javaScriptWindowObjectCleared.connect(self.initialized) self.m_webPage.loadStarted.connect(self.loadStarted) self.m_webPage.loadFinished.connect(self.finish) # Start with transparent background palette = self.m_webPage.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.m_webPage.setPalette(palette) # Page size does not need to take scrollbars into account self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff) self.m_webPage.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff) self.m_webPage.settings().setAttribute(QWebSettings.OfflineStorageDatabaseEnabled, True) self.m_webPage.settings().setOfflineStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageDatabaseEnabled, True) self.m_webPage.settings().setAttribute(QWebSettings.OfflineWebApplicationCacheEnabled, True) self.m_webPage.settings().setOfflineWebApplicationCachePath( QDesktopServices.storageLocation(QDesktopServices.DataLocation) ) self.m_webPage.settings().setAttribute(QWebSettings.FrameFlatteningEnabled, True) self.m_webPage.settings().setAttribute(QWebSettings.LocalStorageEnabled, True) self.m_webPage.settings().setLocalStoragePath(QDesktopServices.storageLocation(QDesktopServices.DataLocation)) # Ensure we have a document.body. self.m_webPage.mainFrame().setHtml("<html><body></body></html>") self.m_webPage.setViewportSize(QSize(400, 300)) do_action("WebPageInit") def applySettings(self, defaults): opt = self.m_webPage.settings() opt.setAttribute(QWebSettings.AutoLoadImages, defaults["loadImages"]) opt.setAttribute(QWebSettings.PluginsEnabled, defaults["loadPlugins"]) opt.setAttribute(QWebSettings.JavascriptEnabled, defaults["javascriptEnabled"]) opt.setAttribute(QWebSettings.XSSAuditingEnabled, defaults["XSSAuditingEnabled"]) opt.setAttribute(QWebSettings.LocalContentCanAccessRemoteUrls, defaults["localAccessRemote"]) if "userAgent" in defaults: self.m_webPage.m_userAgent = defaults["userAgent"] def finish(self, ok): status = "success" if ok else "fail" self.loadFinished.emit(status) def mainFrame(self): return self.m_mainFrame def renderImage(self): viewportSize = self.m_webPage.viewportSize() frameRect = QRect(QPoint(0, 0), viewportSize) if not self.m_clipRect.isEmpty(): frameRect = self.m_clipRect if self.m_webPage.m_scrollPosition: self.m_webPage.mainFrame().setScrollPosition( QPoint(self.m_webPage.m_scrollPosition.x(), self.m_webPage.m_scrollPosition.y()) ) image = QImage(frameRect.size(), QImage.Format_ARGB32) image.fill(qRgba(255, 255, 255, 0)) painter = QPainter() # We use tiling approach to work-around Qt software rasterizer bug # when dealing with very large paint device. # See http://code.google.com/p/phantomjs/issues/detail?id=54. tileSize = 4096 htiles = (image.width() + tileSize - 1) / tileSize vtiles = (image.height() + tileSize - 1) / tileSize for x in range(htiles): for y in range(vtiles): tileBuffer = QImage(tileSize, tileSize, QImage.Format_ARGB32) tileBuffer.fill(qRgba(255, 255, 255, 0)) # Render the web page onto the small tile first painter.begin(tileBuffer) painter.setRenderHint(QPainter.Antialiasing, True) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setRenderHint(QPainter.SmoothPixmapTransform, True) painter.translate(-frameRect.left(), -frameRect.top()) painter.translate(-x * tileSize, -y * tileSize) self.m_mainFrame.render(painter, QRegion(frameRect)) painter.end() # Copy the tile to the main buffer painter.begin(image) painter.setCompositionMode(QPainter.CompositionMode_Source) painter.drawImage(x * tileSize, y * tileSize, tileBuffer) painter.end() self.m_webPage.setViewportSize(viewportSize) return image # Different defaults. # OSX: 72, X11: 75(?), Windows: 96 pdf_dpi = 72 def renderPdf(self, fileName): p = QPrinter() p.setOutputFormat(QPrinter.PdfFormat) p.setOutputFileName(fileName) p.setResolution(self.pdf_dpi) paperSize = self.m_paperSize if not len(paperSize): pageSize = QSize(self.m_webPage.mainFrame().contentsSize()) paperSize["width"] = str(pageSize.width()) + "px" paperSize["height"] = str(pageSize.height()) + "px" paperSize["border"] = "0px" if paperSize.get("width") and paperSize.get("height"): sizePt = QSizeF( ceil(self.stringToPointSize(paperSize["width"])), ceil(self.stringToPointSize(paperSize["height"])) ) p.setPaperSize(sizePt, QPrinter.Point) elif "format" in paperSize: orientation = ( QPrinter.Landscape if paperSize.get("orientation") and paperSize["orientation"].lower() == "landscape" else QPrinter.Portrait ) orientation = QPrinter.Orientation(orientation) p.setOrientation(orientation) formats = { "A0": QPrinter.A0, "A1": QPrinter.A1, "A2": QPrinter.A2, "A3": QPrinter.A3, "A4": QPrinter.A4, "A5": QPrinter.A5, "A6": QPrinter.A6, "A7": QPrinter.A7, "A8": QPrinter.A8, "A9": QPrinter.A9, "B0": QPrinter.B0, "B1": QPrinter.B1, "B2": QPrinter.B2, "B3": QPrinter.B3, "B4": QPrinter.B4, "B5": QPrinter.B5, "B6": QPrinter.B6, "B7": QPrinter.B7, "B8": QPrinter.B8, "B9": QPrinter.B9, "B10": QPrinter.B10, "C5E": QPrinter.C5E, "Comm10E": QPrinter.Comm10E, "DLE": QPrinter.DLE, "Executive": QPrinter.Executive, "Folio": QPrinter.Folio, "Ledger": QPrinter.Ledger, "Legal": QPrinter.Legal, "Letter": QPrinter.Letter, "Tabloid": QPrinter.Tabloid, } p.setPaperSize(QPrinter.A4) # fallback for format_, size in formats.items(): if format_.lower() == paperSize["format"].lower(): p.setPaperSize(size) break else: return False border = floor(self.stringToPointSize(paperSize["border"])) if paperSize.get("border") else 0 p.setPageMargins(border, border, border, border, QPrinter.Point) self.m_webPage.mainFrame().print_(p) return True def setNetworkAccessManager(self, networkAccessManager): self.m_webPage.setNetworkAccessManager(networkAccessManager) networkAccessManager.resourceRequested.connect(self.resourceRequested) networkAccessManager.resourceReceived.connect(self.resourceReceived) def stringToPointSize(self, string): units = ( ("mm", 72 / 25.4), ("cm", 72 / 2.54), ("in", 72.0), ("px", 72.0 / self.pdf_dpi / 2.54), ("", 72.0 / self.pdf_dpi / 2.54), ) for unit, format_ in units: if string.endswith(unit): value = string.rstrip(unit) return float(value) * format_ return 0 def userAgent(self): return self.m_webPage.m_userAgent ## # Properties and methods exposed to JavaScript ## @pyqtSlot(str) def _appendScriptElement(self, scriptUrl): self.m_mainFrame.evaluateJavaScript( """ var el = document.createElement('script'); el.onload = function() { alert('%(scriptUrl)s'); }; el.src = '%(scriptUrl)s'; document.body.appendChild(el); """ % {"scriptUrl": scriptUrl} ) @pyqtSlot(int, int) def click(self, x, y): self.mouseMoveTo(x, y) self.mouseDown() self.mouseUp() @pyqtProperty("QVariantMap") def clipRect(self): clipRect = self.m_clipRect result = { "width": clipRect.width(), "height": clipRect.height(), "top": clipRect.top(), "left": clipRect.left(), } return result @clipRect.setter def clipRect(self, size): sizes = {"width": 0, "height": 0, "top": 0, "left": 0} for item in sizes: try: sizes[item] = int(size[item]) if sizes[item] < 0: if item not in ("top", "left"): sizes[item] = 0 except (KeyError, ValueError): sizes[item] = self.clipRect[item] self.m_clipRect = QRect(sizes["left"], sizes["top"], sizes["width"], sizes["height"]) @pyqtProperty(str) def content(self): return self.m_mainFrame.toHtml() @content.setter def content(self, content): self.m_mainFrame.setHtml(content) @pyqtSlot(str, result="QVariant") def evaluate(self, code): function = "(%s)()" % code return self.m_mainFrame.evaluateJavaScript(function) @pyqtSlot(str, result=bool) def injectJs(self, filePath): return injectJsInFrame(filePath, self.m_libraryPath, self.m_mainFrame) @pyqtSlot() def mouseDown(self): event = QMouseEvent(QEvent.MouseButtonPress, self.m_mousePos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QApplication.postEvent(self.m_webPage, event) QApplication.processEvents() @pyqtSlot() def mouseUp(self): event = QMouseEvent(QEvent.MouseButtonRelease, self.m_mousePos, Qt.LeftButton, Qt.LeftButton, Qt.NoModifier) QApplication.postEvent(self.m_webPage, event) QApplication.processEvents() @pyqtSlot(int, int) def mouseMoveTo(self, x, y): self.m_mousePos = QPoint(x, y) event = QMouseEvent(QEvent.MouseMove, self.m_mousePos, Qt.NoButton, Qt.NoButton, Qt.NoModifier) QApplication.postEvent(self.m_webPage, event) QApplication.processEvents() @pyqtSlot(str, str, "QVariantMap") @pyqtSlot(str, "QVariantMap", "QVariantMap") def openUrl(self, address, op, settings): operation = op body = QByteArray() self.applySettings(settings) self.m_webPage.triggerAction(QWebPage.Stop) if type(op) is dict: operation = op.get("operation") body = QByteArray(op.get("data", "")) if operation == "": operation = "get" networkOp = QNetworkAccessManager.CustomOperation operation = operation.lower() if operation == "get": networkOp = QNetworkAccessManager.GetOperation elif operation == "head": networkOp = QNetworkAccessManager.HeadOperation elif operation == "put": networkOp = QNetworkAccessManager.PutOperation elif operation == "post": networkOp = QNetworkAccessManager.PostOperation elif operation == "delete": networkOp = QNetworkAccessManager.DeleteOperation if networkOp == QNetworkAccessManager.CustomOperation: self.m_mainFrame.evaluateJavaScript('console.error("Unknown network operation: %s");' % operation) return self.m_mainFrame.load(QNetworkRequest(QUrl(address)), networkOp, body) @pyqtProperty("QVariantMap") def paperSize(self): return self.m_paperSize @paperSize.setter def paperSize(self, size): self.m_paperSize = size @pyqtSlot() def release(self): self.m_parent.m_pages.remove(self) sip.delete(self) @pyqtSlot(str, result=bool) def render(self, fileName): if self.m_mainFrame.contentsSize() == "": return False fileInfo = QFileInfo(fileName) path = QDir() path.mkpath(fileInfo.absolutePath()) if fileName.lower().endswith(".pdf"): return self.renderPdf(fileName) image = self.renderImage() return image.save(fileName) @pyqtProperty(str) def libraryPath(self): return self.m_libraryPath @libraryPath.setter def libraryPath(self, dirPath): self.m_libraryPath = dirPath @pyqtProperty("QVariantMap") def scrollPosition(self): scroll = self.m_webPage.m_scrollPosition result = {"left": scroll.x(), "top": scroll.y()} return result @scrollPosition.setter def scrollPosition(self, size): positions = {"left": 0, "top": 0} for item in positions: try: positions[item] = int(size[item]) if positions[item] < 0: positions[item] = 0 except (KeyError, ValueError): positions[item] = self.scrollPosition[item] self.m_webPage.m_scrollPosition = QPoint(positions["left"], positions["top"]) @pyqtSlot(str, str) def uploadFile(self, selector, fileName): el = self.m_mainFrame.findFirstElement(selector) if el.isNull(): return self.m_webPage.m_uploadFile = fileName el.evaluateJavaScript( """ (function (el) { var ev = document.createEvent('MouseEvents'); ev.initEvent('click', true, true); el.dispatchEvent(ev); })(this) """ ) @pyqtProperty("QVariantMap") def viewportSize(self): size = self.m_webPage.viewportSize() result = {"width": size.width(), "height": size.height()} return result @viewportSize.setter def viewportSize(self, size): sizes = {"width": 0, "height": 0} for item in sizes: try: sizes[item] = int(size[item]) if sizes[item] < 0: sizes[item] = 0 except (KeyError, ValueError): sizes[item] = self.viewportSize[item] self.m_webPage.setViewportSize(QSize(sizes["width"], sizes["height"])) do_action("WebPage")