mirror of
https://github.com/encounter/phantomjs.git
synced 2026-03-30 11:35:11 -07:00
65563ca89d
* Convert all QString and QVariant types * Replaced all references to QString with Python str object - Fixed bug where phantom.version didn't work - Don't cd to scripts directory anymore (but loadScript still loads scripts from directory relative to it) - clipRect.setter and viewportSize.setter both have improved system now. * if you miss or neglect to specify an attritube (width, height, etc), there will no longer be an exception. it will be handled gracefully, and whatever you didn't specify won't be changed * if you enter a negative value on any of them, it'll be reset to 0 (except for phantom.clipRect top and left) and you won't loose your attempted changes - Cleaned up Webpage's javaScriptConsoleMessage print JavaScript (sourceID) - Allow viewportSize to be set to 0 (default) - General cleanup
383 lines
13 KiB
Python
383 lines
13 KiB
Python
'''
|
|
This file is part of the PyPhantomJS project.
|
|
|
|
Copyright (C) 2011 James Roe <roejames12@hotmail.com>
|
|
Copyright (C) 2010-2011 Ariya Hidayat <ariya.hidayat@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
'''
|
|
|
|
import sys, os
|
|
|
|
from utils import version_major, version_minor, version_patch
|
|
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.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_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)
|
|
if sys.platform.startswith('win'):
|
|
self.m_scriptDir += '\\'
|
|
else:
|
|
self.m_scriptDir += '/'
|
|
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()
|
|
|
|
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)
|
|
|
|
if self.m_verbose:
|
|
m_netAccessMan = NetworkAccessManager(args.disk_cache, 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)
|
|
|
|
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 = open(self.m_scriptDir + script)
|
|
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))
|