mirror of https://github.com/vitalif/phantomjs
PyPhantomJS version 1.2.0
Refactored most of functionalities to WebPage object WebPage object can have settings Support different HTTP operations for loading a page Simplify buffer handling in render() Refactor render(), split the buffer rendering to its own function Render the web page using the tiling approach WebPage callback for it's console message Make exit() synchronous WebPage callback for its JS alert Updated all examples1.2
parent
a7a8a6749a
commit
a313d1befd
|
@ -0,0 +1,40 @@
|
|||
// This allows creating a new web page using the construct "new WebPage",
|
||||
// which feels more natural than "phantom.createWebPage()".
|
||||
window.WebPage = function() {
|
||||
var page = phantom.createWebPage();
|
||||
|
||||
// deep copy
|
||||
page.settings = JSON.parse(JSON.stringify(phantom.defaultPageSettings));
|
||||
|
||||
page.onAlert = function (msg) {};
|
||||
|
||||
page.onConsoleMessage = function (msg) {};
|
||||
|
||||
page.open = function () {
|
||||
if (typeof this.onAlert === 'function') {
|
||||
this.javaScriptAlertSent.connect(this.onAlert);
|
||||
}
|
||||
if (typeof this.onConsoleMessage === 'function') {
|
||||
this.javaScriptConsoleMessageSent.connect(this.onConsoleMessage);
|
||||
}
|
||||
if (arguments.length === 2) {
|
||||
this.loadStatusChanged.connect(arguments[1]);
|
||||
this.openUrl(arguments[0], 'get', this.settings);
|
||||
return;
|
||||
} else if (arguments.length === 3) {
|
||||
this.loadStatusChanged.connect(arguments[2]);
|
||||
this.openUrl(arguments[0], arguments[1], this.settings);
|
||||
return;
|
||||
} else if (arguments.length === 4) {
|
||||
this.loadStatusChanged.connect(arguments[3]);
|
||||
this.openUrl(arguments[0], {
|
||||
operation: arguments[1],
|
||||
data: arguments[2]
|
||||
}, this.settings);
|
||||
return;
|
||||
}
|
||||
throw "Wrong use of WebPage#open";
|
||||
};
|
||||
|
||||
return page;
|
||||
}
|
|
@ -17,16 +17,21 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
from PyQt4.QtCore import QObject, QFile
|
||||
import sys
|
||||
|
||||
from PyQt4.QtCore import QObject, QFile, qCritical
|
||||
from PyQt4.QtWebKit import QWebPage
|
||||
|
||||
|
||||
class CSConverter(QObject):
|
||||
def __init__(self, parent=None):
|
||||
QObject.__init__(self, parent)
|
||||
self.m_webPage = QWebPage(self)
|
||||
|
||||
converter = QFile(':/resources/coffee-script.js')
|
||||
converter.open(QFile.ReadOnly)
|
||||
if not converter.open(QFile.ReadOnly):
|
||||
qCritical('CoffeeScript compiler is not available!')
|
||||
sys.exit(1)
|
||||
|
||||
script = str(converter.readAll())
|
||||
converter.close()
|
||||
|
@ -36,6 +41,6 @@ class CSConverter(QObject):
|
|||
def convert(self, script):
|
||||
self.setProperty('source', script)
|
||||
result = self.m_webPage.mainFrame().evaluateJavaScript('this.CoffeeScript.compile(converter.source)')
|
||||
if len(result):
|
||||
if result:
|
||||
return result
|
||||
return ''
|
||||
|
|
|
@ -24,6 +24,7 @@ from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkDiskCache, \
|
|||
|
||||
from plugincontroller import Bunch, do_action
|
||||
|
||||
|
||||
class NetworkAccessManager(QNetworkAccessManager):
|
||||
def __init__(self, diskCacheEnabled, ignoreSslErrors, parent=None):
|
||||
QNetworkAccessManager.__init__(self, parent)
|
||||
|
|
|
@ -17,82 +17,67 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import os
|
||||
import codecs
|
||||
import sys
|
||||
|
||||
from utils import version_major, version_minor, version_patch
|
||||
from plugincontroller import Bunch, do_action
|
||||
from csconverter import CSConverter
|
||||
from math import ceil, floor
|
||||
from time import sleep as usleep
|
||||
from webpage import WebPage
|
||||
from networkaccessmanager import NetworkAccessManager
|
||||
|
||||
from PyQt4.QtCore import pyqtProperty, pyqtSlot, Qt, QObject, QRect, \
|
||||
SLOT, QTimer, QUrl, QFileInfo, QDir, QSize, \
|
||||
QSizeF, QTime, QEventLoop, qDebug
|
||||
from PyQt4.QtGui import QPalette, QDesktopServices, qApp, QPrinter, \
|
||||
QImage, QPainter, QRegion, QApplication, qRgba
|
||||
from PyQt4.QtWebKit import QWebSettings, QWebPage
|
||||
from PyQt4.QtCore import pyqtProperty, pyqtSlot, QObject, \
|
||||
QFile, qCritical
|
||||
from PyQt4.QtGui import QApplication
|
||||
from PyQt4.QtNetwork import QNetworkProxy, QNetworkProxyFactory
|
||||
|
||||
# Different defaults.
|
||||
# OSX: 72, X11: 75(?), Windows: 96
|
||||
pdf_dpi = 72
|
||||
|
||||
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_defaultPageSettings = {}
|
||||
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_script = args.script
|
||||
self.m_scriptFile = 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))
|
||||
# Provide WebPage with a non-standard Network Access Manager
|
||||
self.m_netAccessMan = NetworkAccessManager(args.disk_cache, args.ignore_ssl_errors, self)
|
||||
self.m_page.setNetworkAccessManager(self.m_netAccessMan)
|
||||
|
||||
# Ensure we have a document.body.
|
||||
self.m_page.mainFrame().setHtml('<html><body></body></html>')
|
||||
self.m_page.javaScriptConsoleMessageSent.connect(self.printConsoleMessage)
|
||||
|
||||
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)
|
||||
self.m_defaultPageSettings['loadImages'] = autoLoadImages
|
||||
self.m_defaultPageSettings['loadPlugins'] = pluginsEnabled
|
||||
self.m_defaultPageSettings['userAgent'] = self.m_page.userAgent()
|
||||
self.m_page.applySettings(self.m_defaultPageSettings)
|
||||
|
||||
# inject our properties and slots into javascript
|
||||
self.m_page.mainFrame().javaScriptWindowObjectCleared.connect(self.inject)
|
||||
self.m_page.loadFinished.connect(self.finish)
|
||||
self.m_page.mainFrame().addToJavaScriptWindowObject('phantom', self)
|
||||
|
||||
bootstrap = QFile(':/bootstrap.js')
|
||||
if not bootstrap.open(QFile.ReadOnly):
|
||||
qCritical('Can not bootstrap!')
|
||||
sys.exit(1)
|
||||
bootstrapper = str(bootstrap.readAll())
|
||||
bootstrap.close()
|
||||
if not bootstrapper:
|
||||
qCritical('Can not bootstrap!')
|
||||
sys.exit(1)
|
||||
self.m_page.mainFrame().evaluateJavaScript(bootstrapper)
|
||||
|
||||
do_action('PhantomInitPost', Bunch(locals()))
|
||||
|
||||
|
@ -106,76 +91,12 @@ class Phantom(QObject):
|
|||
|
||||
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 printConsoleMessage(self, msg):
|
||||
print msg
|
||||
|
||||
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
|
||||
##
|
||||
|
@ -184,171 +105,22 @@ class Phantom(QObject):
|
|||
def args(self):
|
||||
return self.m_args
|
||||
|
||||
@pyqtSlot(result=WebPage)
|
||||
def createWebPage(self):
|
||||
page = WebPage(self)
|
||||
page.applySettings(self.m_defaultPageSettings)
|
||||
page.setNetworkAccessManager(self.m_netAccessMan)
|
||||
return page
|
||||
|
||||
@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)
|
||||
def defaultPageSettings(self):
|
||||
return self.m_defaultPageSettings
|
||||
|
||||
@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
|
||||
QApplication.instance().exit(code)
|
||||
|
||||
@pyqtProperty('QVariantMap')
|
||||
def version(self):
|
||||
|
@ -359,26 +131,4 @@ class Phantom(QObject):
|
|||
}
|
||||
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()))
|
||||
|
|
|
@ -24,6 +24,7 @@ from os.path import dirname, split, splitext
|
|||
plugins = defaultdict(list)
|
||||
hook_count = {}
|
||||
|
||||
|
||||
class Bunch(object):
|
||||
''' Simple class to bunch a dict into
|
||||
an object that with attributes
|
||||
|
@ -31,9 +32,10 @@ class Bunch(object):
|
|||
def __init__(self, adict):
|
||||
self.__dict__ = adict
|
||||
|
||||
|
||||
def add_action(*hooks):
|
||||
''' Decorator to be used for registering a function to
|
||||
a specific hook or list of hooks.
|
||||
a specific hook or list of hooks.
|
||||
'''
|
||||
def register(func):
|
||||
for hook in hooks:
|
||||
|
@ -41,10 +43,12 @@ def add_action(*hooks):
|
|||
return func
|
||||
return register
|
||||
|
||||
|
||||
def did_action(hook):
|
||||
'''Find out how many times a hook was fired'''
|
||||
return hook_count[hook]
|
||||
|
||||
|
||||
def do_action(hook, *args, **kwargs):
|
||||
''' Trigger a hook. It will run any functions that have registered
|
||||
themselves to the hook. Any additional arguments or keyword
|
||||
|
@ -55,21 +59,37 @@ def do_action(hook, *args, **kwargs):
|
|||
hook_count[hook] += 1
|
||||
plugin(*args, **kwargs)
|
||||
|
||||
def has_action(hook):
|
||||
'''Check if any functions have been registered for a hook'''
|
||||
|
||||
def has_action(hook, func=None):
|
||||
'''Check if hook exists. If function is specified,
|
||||
check if function has been registered for hook.
|
||||
'''
|
||||
if hook in plugins:
|
||||
return True
|
||||
if not func:
|
||||
return True
|
||||
else:
|
||||
for f in plugins[hook]:
|
||||
if f == func:
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_action(hook, func):
|
||||
'''Remove function that has been registered to hook'''
|
||||
|
||||
def remove_action(hook, func=None):
|
||||
'''Remove hook if hook exists. If function is specified,
|
||||
remove function from hook.
|
||||
'''
|
||||
if hook in plugins:
|
||||
for f in plugins[hook]:
|
||||
if f == func:
|
||||
del plugins[hook][plugins[hook].index(func)]
|
||||
return True
|
||||
if not func:
|
||||
del plugins[hook]
|
||||
return True
|
||||
else:
|
||||
for f in plugins[hook]:
|
||||
if f == func:
|
||||
del plugins[hook][plugins[hook].index(func)]
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def remove_all_actions(hook):
|
||||
'''Remove all functions that have been registered to hook'''
|
||||
if hook in plugins:
|
||||
|
@ -77,6 +97,7 @@ def remove_all_actions(hook):
|
|||
return True
|
||||
return False
|
||||
|
||||
|
||||
def load_plugins():
|
||||
''' Loads the plugins.
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ from utils import SafeStreamFilter
|
|||
sys.stdout = SafeStreamFilter(sys.stdout)
|
||||
sys.stderr = SafeStreamFilter(sys.stderr)
|
||||
|
||||
|
||||
def parseArgs(args):
|
||||
# Handle all command-line options
|
||||
p = argParser()
|
||||
|
@ -119,12 +120,15 @@ def parseArgs(args):
|
|||
sys.exit(1)
|
||||
|
||||
try:
|
||||
args.script = codecs.open(args.script, encoding='utf-8')
|
||||
with codecs.open(args.script, encoding='utf-8') as script:
|
||||
args.script_name = script.name
|
||||
args.script = script.read()
|
||||
except IOError as (errno, stderr):
|
||||
sys.exit('%s: \'%s\'' % (stderr, args.script))
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = parseArgs(sys.argv[1:])
|
||||
|
||||
|
@ -146,9 +150,11 @@ def main():
|
|||
|
||||
phantom.execute()
|
||||
app.exec_()
|
||||
sys.exit(phantom.returnValue())
|
||||
return phantom.returnValue()
|
||||
|
||||
|
||||
do_action('PyPhantomJS', Bunch(locals()))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(main())
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Resource object code
|
||||
#
|
||||
# Created: Fri May 13 01:08:54 2011
|
||||
# Created: Tue May 31 11:30:31 2011
|
||||
# by: The Resource Compiler for PyQt (Qt v4.7.2)
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
@ -10,6 +10,97 @@
|
|||
from PyQt4 import QtCore
|
||||
|
||||
qt_resource_data = "\
|
||||
\x00\x00\x05\x81\
|
||||
\x2f\
|
||||
\x2f\x20\x54\x68\x69\x73\x20\x61\x6c\x6c\x6f\x77\x73\x20\x63\x72\
|
||||
\x65\x61\x74\x69\x6e\x67\x20\x61\x20\x6e\x65\x77\x20\x77\x65\x62\
|
||||
\x20\x70\x61\x67\x65\x20\x75\x73\x69\x6e\x67\x20\x74\x68\x65\x20\
|
||||
\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x20\x22\x6e\x65\x77\x20\x57\
|
||||
\x65\x62\x50\x61\x67\x65\x22\x2c\x0a\x2f\x2f\x20\x77\x68\x69\x63\
|
||||
\x68\x20\x66\x65\x65\x6c\x73\x20\x6d\x6f\x72\x65\x20\x6e\x61\x74\
|
||||
\x75\x72\x61\x6c\x20\x74\x68\x61\x6e\x20\x22\x70\x68\x61\x6e\x74\
|
||||
\x6f\x6d\x2e\x63\x72\x65\x61\x74\x65\x57\x65\x62\x50\x61\x67\x65\
|
||||
\x28\x29\x22\x2e\x0a\x77\x69\x6e\x64\x6f\x77\x2e\x57\x65\x62\x50\
|
||||
\x61\x67\x65\x20\x3d\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x28\x29\
|
||||
\x20\x7b\x0a\x20\x20\x20\x20\x76\x61\x72\x20\x70\x61\x67\x65\x20\
|
||||
\x3d\x20\x70\x68\x61\x6e\x74\x6f\x6d\x2e\x63\x72\x65\x61\x74\x65\
|
||||
\x57\x65\x62\x50\x61\x67\x65\x28\x29\x3b\x0a\x0a\x20\x20\x20\x20\
|
||||
\x2f\x2f\x20\x64\x65\x65\x70\x20\x63\x6f\x70\x79\x0a\x20\x20\x20\
|
||||
\x20\x70\x61\x67\x65\x2e\x73\x65\x74\x74\x69\x6e\x67\x73\x20\x3d\
|
||||
\x20\x4a\x53\x4f\x4e\x2e\x70\x61\x72\x73\x65\x28\x4a\x53\x4f\x4e\
|
||||
\x2e\x73\x74\x72\x69\x6e\x67\x69\x66\x79\x28\x70\x68\x61\x6e\x74\
|
||||
\x6f\x6d\x2e\x64\x65\x66\x61\x75\x6c\x74\x50\x61\x67\x65\x53\x65\
|
||||
\x74\x74\x69\x6e\x67\x73\x29\x29\x3b\x0a\x0a\x20\x20\x20\x20\x70\
|
||||
\x61\x67\x65\x2e\x6f\x6e\x41\x6c\x65\x72\x74\x20\x3d\x20\x66\x75\
|
||||
\x6e\x63\x74\x69\x6f\x6e\x20\x28\x6d\x73\x67\x29\x20\x7b\x7d\x3b\
|
||||
\x0a\x0a\x20\x20\x20\x20\x70\x61\x67\x65\x2e\x6f\x6e\x43\x6f\x6e\
|
||||
\x73\x6f\x6c\x65\x4d\x65\x73\x73\x61\x67\x65\x20\x3d\x20\x66\x75\
|
||||
\x6e\x63\x74\x69\x6f\x6e\x20\x28\x6d\x73\x67\x29\x20\x7b\x7d\x3b\
|
||||
\x0a\x0a\x20\x20\x20\x20\x70\x61\x67\x65\x2e\x6f\x70\x65\x6e\x20\
|
||||
\x3d\x20\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x28\x29\x20\x7b\x0a\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x69\x66\x20\x28\x74\x79\x70\x65\
|
||||
\x6f\x66\x20\x74\x68\x69\x73\x2e\x6f\x6e\x41\x6c\x65\x72\x74\x20\
|
||||
\x3d\x3d\x3d\x20\x27\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27\x29\x20\
|
||||
\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\x68\
|
||||
\x69\x73\x2e\x6a\x61\x76\x61\x53\x63\x72\x69\x70\x74\x41\x6c\x65\
|
||||
\x72\x74\x53\x65\x6e\x74\x2e\x63\x6f\x6e\x6e\x65\x63\x74\x28\x74\
|
||||
\x68\x69\x73\x2e\x6f\x6e\x41\x6c\x65\x72\x74\x29\x3b\x0a\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x69\x66\x20\x28\x74\x79\x70\x65\x6f\x66\x20\x74\x68\x69\x73\x2e\
|
||||
\x6f\x6e\x43\x6f\x6e\x73\x6f\x6c\x65\x4d\x65\x73\x73\x61\x67\x65\
|
||||
\x20\x3d\x3d\x3d\x20\x27\x66\x75\x6e\x63\x74\x69\x6f\x6e\x27\x29\
|
||||
\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\
|
||||
\x68\x69\x73\x2e\x6a\x61\x76\x61\x53\x63\x72\x69\x70\x74\x43\x6f\
|
||||
\x6e\x73\x6f\x6c\x65\x4d\x65\x73\x73\x61\x67\x65\x53\x65\x6e\x74\
|
||||
\x2e\x63\x6f\x6e\x6e\x65\x63\x74\x28\x74\x68\x69\x73\x2e\x6f\x6e\
|
||||
\x43\x6f\x6e\x73\x6f\x6c\x65\x4d\x65\x73\x73\x61\x67\x65\x29\x3b\
|
||||
\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x69\x66\x20\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\
|
||||
\x2e\x6c\x65\x6e\x67\x74\x68\x20\x3d\x3d\x3d\x20\x32\x29\x20\x7b\
|
||||
\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x74\x68\x69\
|
||||
\x73\x2e\x6c\x6f\x61\x64\x53\x74\x61\x74\x75\x73\x43\x68\x61\x6e\
|
||||
\x67\x65\x64\x2e\x63\x6f\x6e\x6e\x65\x63\x74\x28\x61\x72\x67\x75\
|
||||
\x6d\x65\x6e\x74\x73\x5b\x31\x5d\x29\x3b\x0a\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x74\x68\x69\x73\x2e\x6f\x70\x65\x6e\
|
||||
\x55\x72\x6c\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x5b\x30\x5d\
|
||||
\x2c\x20\x27\x67\x65\x74\x27\x2c\x20\x74\x68\x69\x73\x2e\x73\x65\
|
||||
\x74\x74\x69\x6e\x67\x73\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x3b\x0a\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\
|
||||
\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\
|
||||
\x20\x3d\x3d\x3d\x20\x33\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x74\x68\x69\x73\x2e\x6c\x6f\x61\x64\x53\
|
||||
\x74\x61\x74\x75\x73\x43\x68\x61\x6e\x67\x65\x64\x2e\x63\x6f\x6e\
|
||||
\x6e\x65\x63\x74\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x5b\x32\
|
||||
\x5d\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x74\x68\x69\x73\x2e\x6f\x70\x65\x6e\x55\x72\x6c\x28\x61\x72\x67\
|
||||
\x75\x6d\x65\x6e\x74\x73\x5b\x30\x5d\x2c\x20\x61\x72\x67\x75\x6d\
|
||||
\x65\x6e\x74\x73\x5b\x31\x5d\x2c\x20\x74\x68\x69\x73\x2e\x73\x65\
|
||||
\x74\x74\x69\x6e\x67\x73\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x3b\x0a\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x7d\x20\x65\x6c\x73\x65\x20\x69\x66\x20\x28\
|
||||
\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x2e\x6c\x65\x6e\x67\x74\x68\
|
||||
\x20\x3d\x3d\x3d\x20\x34\x29\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x74\x68\x69\x73\x2e\x6c\x6f\x61\x64\x53\
|
||||
\x74\x61\x74\x75\x73\x43\x68\x61\x6e\x67\x65\x64\x2e\x63\x6f\x6e\
|
||||
\x6e\x65\x63\x74\x28\x61\x72\x67\x75\x6d\x65\x6e\x74\x73\x5b\x33\
|
||||
\x5d\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x74\x68\x69\x73\x2e\x6f\x70\x65\x6e\x55\x72\x6c\x28\x61\x72\x67\
|
||||
\x75\x6d\x65\x6e\x74\x73\x5b\x30\x5d\x2c\x20\x7b\x0a\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6f\x70\x65\
|
||||
\x72\x61\x74\x69\x6f\x6e\x3a\x20\x61\x72\x67\x75\x6d\x65\x6e\x74\
|
||||
\x73\x5b\x31\x5d\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x64\x61\x74\x61\x3a\x20\x61\x72\x67\x75\
|
||||
\x6d\x65\x6e\x74\x73\x5b\x32\x5d\x0a\x20\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x2c\x20\x74\x68\x69\x73\
|
||||
\x2e\x73\x65\x74\x74\x69\x6e\x67\x73\x29\x3b\x0a\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x72\x65\x74\x75\x72\x6e\x3b\x0a\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x74\x68\x72\x6f\x77\x20\x22\x57\x72\x6f\x6e\x67\x20\x75\
|
||||
\x73\x65\x20\x6f\x66\x20\x57\x65\x62\x50\x61\x67\x65\x23\x6f\x70\
|
||||
\x65\x6e\x22\x3b\x0a\x20\x20\x20\x20\x7d\x3b\x0a\x0a\x20\x20\x20\
|
||||
\x20\x72\x65\x74\x75\x72\x6e\x20\x70\x61\x67\x65\x3b\x0a\x7d\x0a\
|
||||
\
|
||||
\x00\x00\x56\x27\
|
||||
\x89\
|
||||
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
|
||||
|
@ -4129,6 +4220,10 @@ qt_resource_data = "\
|
|||
"
|
||||
|
||||
qt_resource_name = "\
|
||||
\x00\x0c\
|
||||
\x06\xa4\xe2\x53\
|
||||
\x00\x62\
|
||||
\x00\x6f\x00\x6f\x00\x74\x00\x73\x00\x74\x00\x72\x00\x61\x00\x70\x00\x2e\x00\x6a\x00\x73\
|
||||
\x00\x09\
|
||||
\x0a\x6c\x78\x43\
|
||||
\x00\x72\
|
||||
|
@ -4145,10 +4240,11 @@ qt_resource_name = "\
|
|||
"
|
||||
|
||||
qt_resource_struct = "\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
|
||||
\x00\x00\x00\x46\x00\x01\x00\x00\x00\x01\x00\x00\x56\x2b\
|
||||
\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x01\
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
|
||||
\x00\x00\x00\x1e\x00\x02\x00\x00\x00\x02\x00\x00\x00\x03\
|
||||
\x00\x00\x00\x64\x00\x01\x00\x00\x00\x01\x00\x00\x5b\xb0\
|
||||
\x00\x00\x00\x36\x00\x00\x00\x00\x00\x01\x00\x00\x05\x85\
|
||||
"
|
||||
|
||||
def qInitResources():
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>bootstrap.js</file>
|
||||
<file>resources/pyphantomjs-icon.png</file>
|
||||
<file>resources/coffee-script.js</file>
|
||||
</qresource>
|
||||
|
|
|
@ -39,11 +39,13 @@ if sys.platform.startswith('win'):
|
|||
print 'install pywin32 extensions if you want the file stamped'
|
||||
sleep(2)
|
||||
|
||||
|
||||
exe = Executable(
|
||||
script = '../pyphantomjs.py',
|
||||
icon = '../resources/pyphantomjs-icon.ico'
|
||||
)
|
||||
|
||||
|
||||
setup(
|
||||
name = 'PyPhantomJS',
|
||||
version = version,
|
||||
|
|
|
@ -23,7 +23,7 @@ from plugincontroller import Bunch, do_action
|
|||
|
||||
from PyQt4.QtCore import QDateTime, Qt, QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg
|
||||
|
||||
version_major, version_minor, version_patch = (1, 1, 0)
|
||||
version_major, version_minor, version_patch = (1, 2, 0)
|
||||
version = '%d.%d.%d' % (version_major, version_minor, version_patch)
|
||||
|
||||
license = '''
|
||||
|
@ -45,6 +45,7 @@ license = '''
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
''' % version
|
||||
|
||||
|
||||
def argParser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Minimalistic headless WebKit-based JavaScript-driven tool',
|
||||
|
@ -90,6 +91,7 @@ def argParser():
|
|||
|
||||
return parser
|
||||
|
||||
|
||||
class MessageHandler:
|
||||
def __init__(self, verbose):
|
||||
self.verbose = verbose
|
||||
|
@ -107,6 +109,7 @@ class MessageHandler:
|
|||
elif msgType == QtFatalMsg:
|
||||
print >> sys.stderr, '%s [FATAL] %s' % (now, msg)
|
||||
|
||||
|
||||
class SafeStreamFilter(object):
|
||||
'''Convert string to something safe'''
|
||||
def __init__(self, target):
|
||||
|
|
|
@ -17,51 +17,340 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
from PyQt4.QtCore import QUrl, QEventLoop, qDebug
|
||||
from PyQt4.QtGui import QApplication
|
||||
from PyQt4.QtWebKit import QWebPage
|
||||
from math import ceil, floor
|
||||
|
||||
from PyQt4.QtCore import pyqtProperty, pyqtSlot, pyqtSignal, Qt, QObject, \
|
||||
QRect, QPoint, QUrl, QFileInfo, QDir, QSize, \
|
||||
QSizeF, QByteArray, QEventLoop, QFile
|
||||
from PyQt4.QtGui import QPalette, QDesktopServices, QPrinter, QImage, \
|
||||
QPainter, QRegion, QApplication, qRgba
|
||||
from PyQt4.QtWebKit import QWebSettings, QWebPage
|
||||
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
|
||||
from plugincontroller import Bunch, do_action
|
||||
|
||||
class WebPage(QWebPage):
|
||||
# Different defaults.
|
||||
# OSX: 72, X11: 75(?), Windows: 96
|
||||
pdf_dpi = 72
|
||||
|
||||
|
||||
class CustomPage(QWebPage):
|
||||
def __init__(self, parent=None):
|
||||
QWebPage.__init__(self, parent)
|
||||
|
||||
self.parent = parent
|
||||
self.m_nextFileTag = ''
|
||||
self.m_userAgent = QWebPage.userAgentForUrl(self, QUrl())
|
||||
|
||||
if self.parent.m_verbose:
|
||||
self.currentFrame().urlChanged.connect(self.handleFrameUrlChanged)
|
||||
self.linkClicked.connect(self.handleLinkClicked)
|
||||
|
||||
do_action('WebPageInit', Bunch(locals()))
|
||||
|
||||
def handleFrameUrlChanged(self, url):
|
||||
qDebug('URL Changed: %s' % url.toString())
|
||||
|
||||
def handleLinkClicked(self, url):
|
||||
qDebug('URL Clicked: %s' % url.toString())
|
||||
|
||||
def javaScriptAlert(self, webframe, msg):
|
||||
print 'JavaScript alert: %s' % msg
|
||||
|
||||
def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
|
||||
if sourceID:
|
||||
print '%s:%d %s' % (sourceID, lineNumber, message)
|
||||
else:
|
||||
print message
|
||||
do_action('CustomPageInit', Bunch(locals()))
|
||||
|
||||
def shouldInterruptJavaScript(self):
|
||||
QApplication.processEvents(QEventLoop.AllEvents, 42)
|
||||
return False
|
||||
|
||||
def javaScriptAlert(self, originatingFrame, msg):
|
||||
self.parent.emitAlert(msg)
|
||||
|
||||
def javaScriptConsoleMessage(self, message, lineNumber, sourceID):
|
||||
if sourceID:
|
||||
message = '%s:%d %s' % (sourceID, lineNumber, message)
|
||||
self.parent.emitConsoleMessage(message)
|
||||
|
||||
def userAgentForUrl(self, url):
|
||||
return self.m_userAgent
|
||||
|
||||
def chooseFile(self, webframe, suggestedFile):
|
||||
if self.m_nextFileTag in self.parent.m_upload_file:
|
||||
return self.parent.m_upload_file[self.m_nextFileTag]
|
||||
return ''
|
||||
do_action('CustomPage', Bunch(locals()))
|
||||
|
||||
|
||||
class WebPage(QObject):
|
||||
def __init__(self, parent=None):
|
||||
QObject.__init__(self, parent)
|
||||
|
||||
# variable declarations
|
||||
self.m_paperSize = {}
|
||||
self.m_clipRect = QRect()
|
||||
|
||||
self.setObjectName('WebPage')
|
||||
self.m_webPage = CustomPage(self)
|
||||
self.m_mainFrame = self.m_webPage.mainFrame()
|
||||
|
||||
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.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'])
|
||||
if 'userAgent' in defaults:
|
||||
self.m_webPage.m_userAgent = defaults['userAgent']
|
||||
|
||||
javaScriptAlertSent = pyqtSignal(str)
|
||||
def emitAlert(self, msg):
|
||||
self.javaScriptAlertSent.emit(msg)
|
||||
|
||||
javaScriptConsoleMessageSent = pyqtSignal(str)
|
||||
def emitConsoleMessage(self, msg):
|
||||
self.javaScriptConsoleMessageSent.emit(msg)
|
||||
|
||||
loadStatusChanged = pyqtSignal(str)
|
||||
def finish(self, ok):
|
||||
status = 'success' if ok else 'fail'
|
||||
self.loadStatusChanged.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 = {
|
||||
'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_webPage.mainFrame().print_(p)
|
||||
return True
|
||||
|
||||
def setNetworkAccessManager(self, networkAccessManager):
|
||||
self.m_webPage.setNetworkAccessManager(networkAccessManager)
|
||||
|
||||
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
|
||||
##
|
||||
|
||||
@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, 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('body'))
|
||||
|
||||
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('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()))
|
||||
|
|
Loading…
Reference in New Issue