mirror of
https://github.com/tenox7/wrp.git
synced 2024-12-22 06:29:16 +00:00
moved old folder to separate repo
This commit is contained in:
parent
ac594cdebd
commit
fa25e816a7
@ -1,9 +0,0 @@
|
|||||||
Historical versions of WRP and prior art
|
|
||||||
|
|
||||||
License: GNU
|
|
||||||
|
|
||||||
Copyright (c) 2013-2018 Antoni Sawicki
|
|
||||||
Copyright (c) 2012-2013 picidae.net
|
|
||||||
Copyright (c) 2004-2013 Paul Hammond
|
|
||||||
Copyright (c) 2017-2018 Natalia Portillo
|
|
||||||
Copyright (c) 2018 //gir.st/
|
|
392
old/picidae.py
392
old/picidae.py
@ -1,392 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# picidae.py - makes screenshots of webpages
|
|
||||||
# and analyzes the webpage structure and writes image-maps of the links
|
|
||||||
# as well as forms that are placed on the exact position of the old form.
|
|
||||||
# It is a part of the art project www.picidae.net
|
|
||||||
# http://www.picidae.net
|
|
||||||
|
|
||||||
#
|
|
||||||
# This script is based on webkit2png from Paul Hammond.
|
|
||||||
# It was extended by picidae.net
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License
|
|
||||||
# as published by the Free Software Foundation; either version 2
|
|
||||||
# of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
|
|
||||||
__version__ = "1.0"
|
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
#print "hello ... "
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
import Foundation
|
|
||||||
import WebKit
|
|
||||||
import AppKit
|
|
||||||
import objc
|
|
||||||
import urllib
|
|
||||||
except ImportError:
|
|
||||||
print "Cannot find pyobjc library files. Are you sure it is installed?"
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#try:
|
|
||||||
# from optparse import OptionParser
|
|
||||||
#except ImportError:
|
|
||||||
# print "OptionParser not imported"
|
|
||||||
# sys.exit()
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
|
|
||||||
class AppDelegate (Foundation.NSObject):
|
|
||||||
# what happens when the app starts up
|
|
||||||
def applicationDidFinishLaunching_(self, aNotification):
|
|
||||||
webview = aNotification.object().windows()[0].contentView()
|
|
||||||
webview.frameLoadDelegate().getURL(webview)
|
|
||||||
|
|
||||||
|
|
||||||
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
|
|
||||||
# what happens if something goes wrong while loading
|
|
||||||
def webView_didFailLoadWithError_forFrame_(self,webview,error,frame):
|
|
||||||
print " ... something went wrong 1"
|
|
||||||
self.getURL(webview)
|
|
||||||
def webView_didFailProvisionalLoadWithError_forFrame_(self,webview,error,frame):
|
|
||||||
print " ... something went wrong 2"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def makeFilename(self,URL,options):
|
|
||||||
# make the filename
|
|
||||||
if options.filename:
|
|
||||||
filename = options.filename
|
|
||||||
elif options.md5:
|
|
||||||
try:
|
|
||||||
import md5
|
|
||||||
except ImportError:
|
|
||||||
print "--md5 requires python md5 library"
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
filename = md5.new(URL).hexdigest()
|
|
||||||
else:
|
|
||||||
import re
|
|
||||||
filename = re.sub('\W','',URL);
|
|
||||||
filename = re.sub('^http','',filename);
|
|
||||||
if options.datestamp:
|
|
||||||
import time
|
|
||||||
now = time.strftime("%Y%m%d")
|
|
||||||
filename = now + "-" + filename
|
|
||||||
import os
|
|
||||||
dir = os.path.abspath(os.path.expanduser(options.dir))
|
|
||||||
return os.path.join(dir,filename)
|
|
||||||
|
|
||||||
def saveImages(self,bitmapdata,filename,options):
|
|
||||||
# save the fullsize png
|
|
||||||
if options.fullsize:
|
|
||||||
bitmapdata.representationUsingType_properties_(AppKit.NSPNGFileType,None).writeToFile_atomically_(filename + ".png",objc.YES)
|
|
||||||
|
|
||||||
if options.thumb or options.clipped:
|
|
||||||
# work out how big the thumbnail is
|
|
||||||
width = bitmapdata.pixelsWide()
|
|
||||||
height = bitmapdata.pixelsHigh()
|
|
||||||
thumbWidth = (width * options.scale)
|
|
||||||
thumbHeight = (height * options.scale)
|
|
||||||
|
|
||||||
# make the thumbnails in a scratch image
|
|
||||||
scratch = AppKit.NSImage.alloc().initWithSize_(
|
|
||||||
Foundation.NSMakeSize(thumbWidth,thumbHeight))
|
|
||||||
scratch.lockFocus()
|
|
||||||
AppKit.NSGraphicsContext.currentContext().setImageInterpolation_(
|
|
||||||
AppKit.NSImageInterpolationHigh)
|
|
||||||
thumbRect = Foundation.NSMakeRect(0.0, 0.0, thumbWidth, thumbHeight)
|
|
||||||
clipRect = Foundation.NSMakeRect(0.0,
|
|
||||||
thumbHeight-options.clipheight,
|
|
||||||
options.clipwidth, options.clipheight)
|
|
||||||
bitmapdata.drawInRect_(thumbRect)
|
|
||||||
thumbOutput = AppKit.NSBitmapImageRep.alloc().initWithFocusedViewRect_(thumbRect)
|
|
||||||
clipOutput = AppKit.NSBitmapImageRep.alloc().initWithFocusedViewRect_(clipRect)
|
|
||||||
scratch.unlockFocus()
|
|
||||||
|
|
||||||
# save the thumbnails as pngs
|
|
||||||
if options.thumb:
|
|
||||||
thumbOutput.representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType,None
|
|
||||||
).writeToFile_atomically_(filename + "-thumb.png",objc.YES)
|
|
||||||
if options.clipped:
|
|
||||||
clipOutput.representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType,None
|
|
||||||
).writeToFile_atomically_(filename + "-clipped.png",objc.YES)
|
|
||||||
|
|
||||||
def getURL(self,webview):
|
|
||||||
if self.urls:
|
|
||||||
if self.urls[0] == '-':
|
|
||||||
url = sys.stdin.readline().rstrip()
|
|
||||||
if not url: AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
else:
|
|
||||||
url = self.urls.pop(0)
|
|
||||||
else:
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
#print "<urlcall href=\"\" />", url, "..."
|
|
||||||
#print "<urlcall href=\"%s\" />" % (url)
|
|
||||||
self.resetWebview(webview)
|
|
||||||
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(Foundation.NSURL.URLWithString_(url)))
|
|
||||||
if not webview.mainFrame().provisionalDataSource():
|
|
||||||
print "<nosuccess />"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def resetWebview(self,webview):
|
|
||||||
rect = Foundation.NSMakeRect(0,0,self.options.initWidth,self.options.initHeight)
|
|
||||||
webview.window().setContentSize_((self.options.initWidth,self.options.initHeight))
|
|
||||||
webview.setFrame_(rect)
|
|
||||||
|
|
||||||
def resizeWebview(self,view):
|
|
||||||
view.window().display()
|
|
||||||
view.window().setContentSize_(view.bounds().size)
|
|
||||||
view.setFrame_(view.bounds())
|
|
||||||
|
|
||||||
def captureView(self,view):
|
|
||||||
view.lockFocus()
|
|
||||||
bitmapdata = AppKit.NSBitmapImageRep.alloc()
|
|
||||||
bitmapdata.initWithFocusedViewRect_(view.bounds())
|
|
||||||
view.unlockFocus()
|
|
||||||
return bitmapdata
|
|
||||||
|
|
||||||
# what happens when the page has finished loading
|
|
||||||
def webView_didFinishLoadForFrame_(self,webview,frame):
|
|
||||||
# don't care about subframes
|
|
||||||
if (frame == webview.mainFrame()):
|
|
||||||
view = frame.frameView().documentView()
|
|
||||||
|
|
||||||
self.resizeWebview(view)
|
|
||||||
|
|
||||||
URL = frame.dataSource().initialRequest().URL().absoluteString()
|
|
||||||
filename = self.makeFilename(URL, self.options)
|
|
||||||
|
|
||||||
bitmapdata = self.captureView(view)
|
|
||||||
self.saveImages(bitmapdata,filename,self.options)
|
|
||||||
|
|
||||||
# ----------------------------------
|
|
||||||
# picidae my stuff
|
|
||||||
|
|
||||||
|
|
||||||
#print "url"
|
|
||||||
print "<page>"
|
|
||||||
print frame.dataSource().request().URL().absoluteString()
|
|
||||||
print "</page>"
|
|
||||||
|
|
||||||
|
|
||||||
# Analyse HTML and get links
|
|
||||||
xmloutput = "<map name=\"map\">\r";
|
|
||||||
|
|
||||||
domdocument = frame.DOMDocument()
|
|
||||||
domnodelist = domdocument.getElementsByTagName_('A')
|
|
||||||
i = 0
|
|
||||||
while i < domnodelist.length():
|
|
||||||
# linkvalue
|
|
||||||
value = domnodelist.item_(i).valueForKey_('href')
|
|
||||||
|
|
||||||
# position-rect
|
|
||||||
myrect = domnodelist.item_(i).boundingBox()
|
|
||||||
|
|
||||||
xmin = Foundation.NSMinX(myrect)
|
|
||||||
ymin = Foundation.NSMinY(myrect)
|
|
||||||
xmax = Foundation.NSMaxX(myrect)
|
|
||||||
ymax = Foundation.NSMaxY(myrect)
|
|
||||||
|
|
||||||
# print Link
|
|
||||||
prefix = ""
|
|
||||||
xmloutput += "<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"\"><![CDATA[%s%s]]></area>\r" % (xmin, ymin, xmax, ymax, prefix, value)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
#print "</map>"
|
|
||||||
xmloutput += "</map>"
|
|
||||||
f = open(filename +'.xml', 'w+')
|
|
||||||
f.write(xmloutput)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# ----------------------------------
|
|
||||||
# get forms
|
|
||||||
xmloutput = "<forms>\r";
|
|
||||||
xmloutput += "<page><![CDATA["
|
|
||||||
xmloutput += frame.dataSource().request().URL().absoluteString()
|
|
||||||
xmloutput += "]]></page>\r"
|
|
||||||
|
|
||||||
domdocument = frame.DOMDocument()
|
|
||||||
domnodelist = domdocument.getElementsByTagName_('form')
|
|
||||||
i = 0
|
|
||||||
while i < domnodelist.length():
|
|
||||||
# form
|
|
||||||
action = domnodelist.item_(i).valueForKey_('action')
|
|
||||||
method = domnodelist.item_(i).valueForKey_('method')
|
|
||||||
xmloutput += "<form method=\"%s\" ><action><![CDATA[%s]]></action>\r" % (method, action)
|
|
||||||
|
|
||||||
# form fields
|
|
||||||
fieldlist = domnodelist.item_(i).getElementsByTagName_('input')
|
|
||||||
j=0
|
|
||||||
while j < fieldlist.length():
|
|
||||||
# values
|
|
||||||
type = fieldlist.item_(j).valueForKey_('type')
|
|
||||||
name = fieldlist.item_(j).valueForKey_('name')
|
|
||||||
formvalue = fieldlist.item_(j).valueForKey_('value')
|
|
||||||
size = fieldlist.item_(j).valueForKey_('size')
|
|
||||||
checked = fieldlist.item_(j).valueForKey_('checked')
|
|
||||||
# write output
|
|
||||||
xmloutput += "\t<input "
|
|
||||||
if (type):
|
|
||||||
xmloutput += "type=\"%s\" " % (type)
|
|
||||||
if (name):
|
|
||||||
xmloutput += "name=\"%s\" " % (name)
|
|
||||||
if (size):
|
|
||||||
xmloutput += "size=\"%s\" " % (size)
|
|
||||||
if (type and type != "hidden"):
|
|
||||||
myrect = fieldlist.item_(j).boundingBox()
|
|
||||||
xmin = Foundation.NSMinX(myrect)
|
|
||||||
ymin = Foundation.NSMinY(myrect)
|
|
||||||
xmax = Foundation.NSMaxX(myrect)
|
|
||||||
ymax = Foundation.NSMaxY(myrect)
|
|
||||||
height = ymax - ymin
|
|
||||||
width = xmax - xmin
|
|
||||||
if (type == "radio" or type == "checkbox"):
|
|
||||||
xmin -= 3
|
|
||||||
ymin -= 3
|
|
||||||
xmloutput += "style=\"position:absolute;top:%i;left:%i;width:%i;height:%i;\" " % (ymin, xmin, width, height)
|
|
||||||
if (checked):
|
|
||||||
xmloutput += "checked=\"%s\" " % (checked)
|
|
||||||
xmloutput += "><![CDATA["
|
|
||||||
if (formvalue and type!="text" and type!="password"):
|
|
||||||
#xmloutput += urllib.quote(formvalue)
|
|
||||||
dummy=10
|
|
||||||
xmloutput += "]]></input>\r"
|
|
||||||
j += 1
|
|
||||||
xmloutput += "</form>\r"
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
xmloutput += "</forms>"
|
|
||||||
f = open(filename +'.form.xml', 'w+')
|
|
||||||
f.write(xmloutput)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
# End picidae
|
|
||||||
# ----------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
#print " ... done"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
#trying to give back the real url
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
|
|
||||||
# parse the command line
|
|
||||||
usage = """%prog [options] [http://example.net/ ...]
|
|
||||||
|
|
||||||
examples:
|
|
||||||
%prog http://google.com/ # screengrab google
|
|
||||||
%prog -W 1000 -H 1000 http://google.com/ # bigger screengrab of google
|
|
||||||
%prog -T http://google.com/ # just the thumbnail screengrab
|
|
||||||
%prog -TF http://google.com/ # just thumbnail and fullsize grab
|
|
||||||
%prog -o foo http://google.com/ # save images as "foo-thumb.png" etc
|
|
||||||
%prog - # screengrab urls from stdin"""
|
|
||||||
|
|
||||||
cmdparser = OptionParser(usage, version=("webkit2png "+__version__))
|
|
||||||
# TODO: add quiet/verbose options
|
|
||||||
cmdparser.add_option("-W", "--width",type="float",default=800.0,
|
|
||||||
help="initial (and minimum) width of browser (default: 800)")
|
|
||||||
cmdparser.add_option("-H", "--height",type="float",default=600.0,
|
|
||||||
help="initial (and minimum) height of browser (default: 600)")
|
|
||||||
cmdparser.add_option("--clipwidth",type="float",default=200.0,
|
|
||||||
help="width of clipped thumbnail (default: 200)",
|
|
||||||
metavar="WIDTH")
|
|
||||||
cmdparser.add_option("--clipheight",type="float",default=150.0,
|
|
||||||
help="height of clipped thumbnail (default: 150)",
|
|
||||||
metavar="HEIGHT")
|
|
||||||
cmdparser.add_option("-s", "--scale",type="float",default=0.25,
|
|
||||||
help="scale factor for thumbnails (default: 0.25)")
|
|
||||||
cmdparser.add_option("-m", "--md5", action="store_true",
|
|
||||||
help="use md5 hash for filename (like del.icio.us)")
|
|
||||||
cmdparser.add_option("-o", "--filename", type="string",default="",
|
|
||||||
metavar="NAME", help="save images as NAME.png,NAME-thumb.png etc")
|
|
||||||
cmdparser.add_option("-F", "--fullsize", action="store_true",
|
|
||||||
help="only create fullsize screenshot")
|
|
||||||
cmdparser.add_option("-T", "--thumb", action="store_true",
|
|
||||||
help="only create thumbnail sreenshot")
|
|
||||||
cmdparser.add_option("-C", "--clipped", action="store_true",
|
|
||||||
help="only create clipped thumbnail screenshot")
|
|
||||||
cmdparser.add_option("-d", "--datestamp", action="store_true",
|
|
||||||
help="include date in filename")
|
|
||||||
cmdparser.add_option("-D", "--dir",type="string",default="./",
|
|
||||||
help="directory to place images into")
|
|
||||||
(options, args) = cmdparser.parse_args()
|
|
||||||
if len(args) == 0:
|
|
||||||
cmdparser.print_help()
|
|
||||||
return
|
|
||||||
if options.filename:
|
|
||||||
if len(args) != 1 or args[0] == "-":
|
|
||||||
print "--filename option requires exactly one url"
|
|
||||||
return
|
|
||||||
if options.scale == 0:
|
|
||||||
cmdparser.error("scale cannot be zero")
|
|
||||||
# make sure we're outputing something
|
|
||||||
if not (options.fullsize or options.thumb or options.clipped):
|
|
||||||
options.fullsize = True
|
|
||||||
options.thumb = True
|
|
||||||
options.clipped = True
|
|
||||||
# work out the initial size of the browser window
|
|
||||||
# (this might need to be larger so clipped image is right size)
|
|
||||||
options.initWidth = (options.clipwidth / options.scale)
|
|
||||||
options.initHeight = (options.clipheight / options.scale)
|
|
||||||
if options.width>options.initWidth:
|
|
||||||
options.initWidth = options.width
|
|
||||||
if options.height>options.initHeight:
|
|
||||||
options.initHeight = options.height
|
|
||||||
|
|
||||||
|
|
||||||
app = AppKit.NSApplication.sharedApplication()
|
|
||||||
|
|
||||||
# create an app delegate
|
|
||||||
delegate = AppDelegate.alloc().init()
|
|
||||||
AppKit.NSApp().setDelegate_(delegate)
|
|
||||||
|
|
||||||
# create a window
|
|
||||||
rect = Foundation.NSMakeRect(-16000,-16000,100,100)
|
|
||||||
win = AppKit.NSWindow.alloc()
|
|
||||||
win.initWithContentRect_styleMask_backing_defer_ (rect,
|
|
||||||
AppKit.NSBorderlessWindowMask, 2, 0)
|
|
||||||
|
|
||||||
# create a webview object
|
|
||||||
webview = WebKit.WebView.alloc()
|
|
||||||
webview.initWithFrame_(rect)
|
|
||||||
# turn off scrolling so the content is actually x wide and not x-15
|
|
||||||
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
|
|
||||||
# add the webview to the window
|
|
||||||
win.setContentView_(webview)
|
|
||||||
|
|
||||||
|
|
||||||
# create a LoadDelegate
|
|
||||||
loaddelegate = WebkitLoad.alloc().init()
|
|
||||||
loaddelegate.options = options
|
|
||||||
loaddelegate.urls = args
|
|
||||||
webview.setFrameLoadDelegate_(loaddelegate)
|
|
||||||
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
if __name__ == '__main__' : main()
|
|
||||||
|
|
@ -1,506 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
# webkit2png - makes screenshots of web pages
|
|
||||||
# http://www.paulhammond.org/webkit2png
|
|
||||||
|
|
||||||
__version__ = "dev"
|
|
||||||
|
|
||||||
# Copyright (c) 2004-2013 Paul Hammond
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import optparse
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
|
|
||||||
try:
|
|
||||||
import Foundation
|
|
||||||
import WebKit
|
|
||||||
import AppKit
|
|
||||||
import Quartz
|
|
||||||
import objc
|
|
||||||
except ImportError:
|
|
||||||
print "Cannot find pyobjc library files. Are you sure it is installed?"
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
class AppDelegate(Foundation.NSObject):
|
|
||||||
# what happens when the app starts up
|
|
||||||
def applicationDidFinishLaunching_(self, aNotification):
|
|
||||||
webview = aNotification.object().windows()[0].contentView()
|
|
||||||
webview.frameLoadDelegate().getURL(webview)
|
|
||||||
self.performSelector_withObject_afterDelay_("timeout:", None,
|
|
||||||
self.timeout)
|
|
||||||
|
|
||||||
def timeout_(self, obj):
|
|
||||||
Foundation.NSLog("timed out!")
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
|
|
||||||
class Webkit2PngScriptBridge(Foundation.NSObject):
|
|
||||||
def init(self):
|
|
||||||
self = super(Webkit2PngScriptBridge, self).init()
|
|
||||||
self.is_stopped = False
|
|
||||||
self.start_callback = False
|
|
||||||
return self
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.is_stopped = True
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.is_stopped = False
|
|
||||||
self.start_callback()
|
|
||||||
|
|
||||||
def isSelectorExcludedFromWebScript_(self, sel):
|
|
||||||
if sel in ['stop', 'start']:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
|
|
||||||
|
|
||||||
# what happens if something goes wrong while loading
|
|
||||||
def webView_didFailLoadWithError_forFrame_(self, webview, error, frame):
|
|
||||||
if error.code() == Foundation.NSURLErrorCancelled:
|
|
||||||
return
|
|
||||||
print " ... something went wrong: "+error.localizedDescription()
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def webView_didFailProvisionalLoadWithError_forFrame_(self, webview, error,
|
|
||||||
frame):
|
|
||||||
if error.code() == Foundation.NSURLErrorCancelled:
|
|
||||||
return
|
|
||||||
print " ... something went wrong: "+error.localizedDescription()
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def makeFilename(self, URL, options):
|
|
||||||
# make the filename
|
|
||||||
if options.filename:
|
|
||||||
filename = options.filename
|
|
||||||
elif options.md5:
|
|
||||||
try:
|
|
||||||
import md5
|
|
||||||
except ImportError:
|
|
||||||
print "--md5 requires python md5 library"
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
filename = md5.new(URL).hexdigest()
|
|
||||||
else:
|
|
||||||
filename = re.sub('^https?', '', URL)
|
|
||||||
filename = re.sub('\W', '', filename)
|
|
||||||
if options.datestamp:
|
|
||||||
import time
|
|
||||||
now = time.strftime("%Y%m%d")
|
|
||||||
filename = now + "-" + filename
|
|
||||||
dir = os.path.abspath(os.path.expanduser(options.dir))
|
|
||||||
if not os.path.exists(options.dir):
|
|
||||||
os.makedirs(dir)
|
|
||||||
return os.path.join(dir, filename)
|
|
||||||
|
|
||||||
def saveImages(self, bitmapdata, filename, options):
|
|
||||||
# save the fullsize png
|
|
||||||
if options.fullsize:
|
|
||||||
bitmapdata.representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType,
|
|
||||||
None
|
|
||||||
).writeToFile_atomically_(filename + "-full.png", objc.YES)
|
|
||||||
|
|
||||||
if options.thumb or options.clipped:
|
|
||||||
# work out how big the thumbnail is
|
|
||||||
width = bitmapdata.pixelsWide()
|
|
||||||
height = bitmapdata.pixelsHigh()
|
|
||||||
thumbWidth = (width * options.scale)
|
|
||||||
thumbHeight = (height * options.scale)
|
|
||||||
|
|
||||||
# make the thumbnails in a scratch image
|
|
||||||
scratch = AppKit.NSImage.alloc().initWithSize_(
|
|
||||||
Foundation.NSMakeSize(thumbWidth, thumbHeight))
|
|
||||||
scratch.lockFocus()
|
|
||||||
AppKit.NSGraphicsContext.currentContext().setImageInterpolation_(
|
|
||||||
AppKit.NSImageInterpolationHigh)
|
|
||||||
thumbRect = Foundation.NSMakeRect(0.0, 0.0, thumbWidth,
|
|
||||||
thumbHeight)
|
|
||||||
clipRect = Foundation.NSMakeRect(
|
|
||||||
0.0,
|
|
||||||
thumbHeight-options.clipheight,
|
|
||||||
options.clipwidth,
|
|
||||||
options.clipheight)
|
|
||||||
bitmapdata.drawInRect_(thumbRect)
|
|
||||||
thumbOutput = AppKit.NSBitmapImageRep.alloc()\
|
|
||||||
.initWithFocusedViewRect_(thumbRect)
|
|
||||||
clipOutput = AppKit.NSBitmapImageRep.alloc()\
|
|
||||||
.initWithFocusedViewRect_(clipRect)
|
|
||||||
scratch.unlockFocus()
|
|
||||||
|
|
||||||
# save the thumbnails as pngs
|
|
||||||
if options.thumb:
|
|
||||||
thumbOutput.representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType, None).writeToFile_atomically_(
|
|
||||||
filename + "-thumb.png", objc.YES)
|
|
||||||
if options.clipped:
|
|
||||||
clipOutput.representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType, None).writeToFile_atomically_(
|
|
||||||
filename + "-clipped.png", objc.YES)
|
|
||||||
|
|
||||||
def getURL(self, webview):
|
|
||||||
if self.urls:
|
|
||||||
if self.urls[0] == '-':
|
|
||||||
url = sys.stdin.readline().rstrip()
|
|
||||||
if not url:
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
else:
|
|
||||||
url = self.urls.pop(0)
|
|
||||||
else:
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
nsurl = Foundation.NSURL.URLWithString_(url)
|
|
||||||
if not (nsurl and nsurl.scheme()):
|
|
||||||
nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(url)
|
|
||||||
nsurl = nsurl.absoluteURL()
|
|
||||||
|
|
||||||
if self.options.ignore_ssl_check:
|
|
||||||
Foundation.NSURLRequest.setAllowsAnyHTTPSCertificate_forHost_(objc.YES, nsurl.host())
|
|
||||||
|
|
||||||
print "Fetching", nsurl, "..."
|
|
||||||
self.resetWebview(webview)
|
|
||||||
scriptobject = webview.windowScriptObject()
|
|
||||||
scriptobject.setValue_forKey_(Webkit2PngScriptBridge.alloc().init(),
|
|
||||||
'webkit2png')
|
|
||||||
|
|
||||||
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(nsurl))
|
|
||||||
if not webview.mainFrame().provisionalDataSource():
|
|
||||||
print " ... not a proper url?"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def resetWebview(self, webview):
|
|
||||||
rect = Foundation.NSMakeRect(0, 0, self.options.initWidth,
|
|
||||||
self.options.initHeight)
|
|
||||||
window = webview.window()
|
|
||||||
window.setContentSize_((self.options.initWidth,
|
|
||||||
self.options.initHeight))
|
|
||||||
|
|
||||||
if self.options.transparent:
|
|
||||||
window.setOpaque_(objc.NO)
|
|
||||||
window.setBackgroundColor_(AppKit.NSColor.clearColor())
|
|
||||||
webview.setDrawsBackground_(objc.NO)
|
|
||||||
|
|
||||||
webview.setFrame_(rect)
|
|
||||||
|
|
||||||
def captureView(self, view):
|
|
||||||
bounds = view.bounds()
|
|
||||||
if bounds.size.height > self.options.UNSAFE_max_height:
|
|
||||||
print >> sys.stderr, "Error: page height greater than %s, " \
|
|
||||||
"clipping to avoid crashing windowserver." % \
|
|
||||||
self.options.UNSAFE_max_height
|
|
||||||
bounds.size.height = self.options.UNSAFE_max_height
|
|
||||||
if bounds.size.width > self.options.UNSAFE_max_width:
|
|
||||||
print >> sys.stderr, "Error: page width greater than %s, " \
|
|
||||||
"clipping to avoid crashing windowserver." % \
|
|
||||||
self.options.UNSAFE_max_width
|
|
||||||
bounds.size.width = self.options.UNSAFE_max_width
|
|
||||||
|
|
||||||
view.window().display()
|
|
||||||
view.window().setContentSize_(
|
|
||||||
Foundation.NSSize(self.options.initWidth, self.options.initHeight))
|
|
||||||
view.setFrame_(bounds)
|
|
||||||
|
|
||||||
if hasattr(view, "bitmapImageRepForCachingDisplayInRect_"):
|
|
||||||
bitmapdata = view.bitmapImageRepForCachingDisplayInRect_(bounds)
|
|
||||||
view.cacheDisplayInRect_toBitmapImageRep_(bounds, bitmapdata)
|
|
||||||
else:
|
|
||||||
view.lockFocus()
|
|
||||||
bitmapdata = AppKit.NSBitmapImageRep.alloc()
|
|
||||||
bitmapdata.initWithFocusedViewRect_(bounds)
|
|
||||||
view.unlockFocus()
|
|
||||||
return bitmapdata
|
|
||||||
|
|
||||||
# what happens when the page has finished loading
|
|
||||||
def webView_didFinishLoadForFrame_(self, webview, frame):
|
|
||||||
# don't care about subframes
|
|
||||||
if (frame == webview.mainFrame()):
|
|
||||||
scriptobject = webview.windowScriptObject()
|
|
||||||
if self.options.js:
|
|
||||||
scriptobject.evaluateWebScript_(self.options.js)
|
|
||||||
|
|
||||||
bridge = scriptobject.valueForKey_('webkit2png')
|
|
||||||
|
|
||||||
def doGrab():
|
|
||||||
Foundation.NSTimer.\
|
|
||||||
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
|
||||||
self.options.delay, self, self.doGrab, webview, False)
|
|
||||||
|
|
||||||
if bridge.is_stopped:
|
|
||||||
bridge.start_callback = doGrab
|
|
||||||
else:
|
|
||||||
doGrab()
|
|
||||||
|
|
||||||
def doGrab(self, timer):
|
|
||||||
webview = timer.userInfo()
|
|
||||||
frame = webview.mainFrame()
|
|
||||||
view = frame.frameView().documentView()
|
|
||||||
|
|
||||||
URL = webview.mainFrame().dataSource().initialRequest().URL()\
|
|
||||||
.absoluteString()
|
|
||||||
filename = self.makeFilename(URL, self.options)
|
|
||||||
|
|
||||||
bitmapdata = self.captureView(view)
|
|
||||||
|
|
||||||
if self.options.selector:
|
|
||||||
doc = frame.DOMDocument()
|
|
||||||
el = doc.querySelector_(self.options.selector)
|
|
||||||
|
|
||||||
if not el:
|
|
||||||
print " ... no element matching %s found?" % \
|
|
||||||
self.options.selector
|
|
||||||
self.getURL(webview)
|
|
||||||
return
|
|
||||||
|
|
||||||
left, top = 0, 0
|
|
||||||
parent = el
|
|
||||||
while parent:
|
|
||||||
left += parent.offsetLeft()
|
|
||||||
top += parent.offsetTop()
|
|
||||||
parent = parent.offsetParent()
|
|
||||||
|
|
||||||
zoom = self.options.zoom
|
|
||||||
|
|
||||||
cropRect = view.window().convertRectToBacking_(Foundation.NSMakeRect(
|
|
||||||
zoom * left, zoom * top,
|
|
||||||
zoom * el.offsetWidth(), zoom * el.offsetHeight()))
|
|
||||||
|
|
||||||
cropped = Quartz.CGImageCreateWithImageInRect(
|
|
||||||
bitmapdata.CGImage(), cropRect)
|
|
||||||
bitmapdata = AppKit.NSBitmapImageRep.alloc().initWithCGImage_(
|
|
||||||
cropped)
|
|
||||||
Quartz.CGImageRelease(cropped)
|
|
||||||
|
|
||||||
self.saveImages(bitmapdata, filename, self.options)
|
|
||||||
|
|
||||||
print " ... done"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
|
|
||||||
# parse the command line
|
|
||||||
usage = """%prog [options] [http://example.net/ ...]
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
%prog http://google.com/ # screengrab google
|
|
||||||
%prog -W 1000 -H 1000 http://google.com/ # bigger screengrab of google
|
|
||||||
%prog -T http://google.com/ # just the thumbnail screengrab
|
|
||||||
%prog -TF http://google.com/ # just thumbnail and fullsize grab
|
|
||||||
%prog -o foo http://google.com/ # save images as "foo-thumb.png" etc
|
|
||||||
%prog - # screengrab urls from stdin
|
|
||||||
%prog /path/to/file.html # screengrab local html file
|
|
||||||
%prog -h | less # full documentation"""
|
|
||||||
|
|
||||||
cmdparser = optparse.OptionParser(usage,
|
|
||||||
version=("webkit2png " + __version__))
|
|
||||||
# TODO: add quiet/verbose options
|
|
||||||
cmdparser.add_option("--debug", action="store_true",
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
|
|
||||||
# warning: setting these too high can crash your window server
|
|
||||||
cmdparser.add_option("--UNSAFE-max-height", type="int", default=30000,
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
cmdparser.add_option("--UNSAFE-max-width", type="int", default=30000,
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
|
|
||||||
group = optparse.OptionGroup(cmdparser, "Network Options")
|
|
||||||
group.add_option("--timeout", type="float", default=60.0,
|
|
||||||
help="page load timeout (default: 60)")
|
|
||||||
group.add_option("--user-agent", type="string", default=False,
|
|
||||||
help="set user agent header")
|
|
||||||
group.add_option("--ignore-ssl-check", action="store_true", default=False,
|
|
||||||
help="ignore SSL Certificate name mismatches")
|
|
||||||
cmdparser.add_option_group(group)
|
|
||||||
|
|
||||||
group = optparse.OptionGroup(cmdparser, "Browser Window Options")
|
|
||||||
group.add_option(
|
|
||||||
"-W", "--width", type="float", default=800.0,
|
|
||||||
help="initial (and minimum) width of browser (default: 800)")
|
|
||||||
group.add_option(
|
|
||||||
"-H", "--height", type="float", default=600.0,
|
|
||||||
help="initial (and minimum) height of browser (default: 600)")
|
|
||||||
group.add_option(
|
|
||||||
"-z", "--zoom", type="float", default=1.0,
|
|
||||||
help='zoom level of browser, equivalent to "Zoom In" and "Zoom Out" '
|
|
||||||
'in "View" menu (default: 1.0)')
|
|
||||||
group.add_option(
|
|
||||||
"--selector", type="string",
|
|
||||||
help="CSS selector for a single element to capture (first matching "
|
|
||||||
"element will be used)")
|
|
||||||
cmdparser.add_option_group(group)
|
|
||||||
|
|
||||||
group = optparse.OptionGroup(cmdparser, "Output size options")
|
|
||||||
group.add_option(
|
|
||||||
"-F", "--fullsize", action="store_true",
|
|
||||||
help="only create fullsize screenshot")
|
|
||||||
group.add_option(
|
|
||||||
"-T", "--thumb", action="store_true",
|
|
||||||
help="only create thumbnail sreenshot")
|
|
||||||
group.add_option(
|
|
||||||
"-C", "--clipped", action="store_true",
|
|
||||||
help="only create clipped thumbnail screenshot")
|
|
||||||
group.add_option(
|
|
||||||
"--clipwidth", type="float", default=200.0,
|
|
||||||
help="width of clipped thumbnail (default: 200)",
|
|
||||||
metavar="WIDTH")
|
|
||||||
group.add_option(
|
|
||||||
"--clipheight", type="float", default=150.0,
|
|
||||||
help="height of clipped thumbnail (default: 150)",
|
|
||||||
metavar="HEIGHT")
|
|
||||||
group.add_option(
|
|
||||||
"-s", "--scale", type="float", default=0.25,
|
|
||||||
help="scale factor for thumbnails (default: 0.25)")
|
|
||||||
cmdparser.add_option_group(group)
|
|
||||||
|
|
||||||
group = optparse.OptionGroup(cmdparser, "Output filename options")
|
|
||||||
group.add_option(
|
|
||||||
"-D", "--dir", type="string", default="./",
|
|
||||||
help="directory to place images into")
|
|
||||||
group.add_option(
|
|
||||||
"-o", "--filename", type="string", default="",
|
|
||||||
metavar="NAME", help="save images as NAME-full.png,NAME-thumb.png etc")
|
|
||||||
group.add_option(
|
|
||||||
"-m", "--md5", action="store_true",
|
|
||||||
help="use md5 hash for filename (like del.icio.us)")
|
|
||||||
group.add_option(
|
|
||||||
"-d", "--datestamp", action="store_true",
|
|
||||||
help="include date in filename")
|
|
||||||
cmdparser.add_option_group(group)
|
|
||||||
|
|
||||||
group = optparse.OptionGroup(cmdparser, "Web page functionality")
|
|
||||||
group.add_option(
|
|
||||||
"--delay", type="float", default=0,
|
|
||||||
help="delay between page load finishing and screenshot")
|
|
||||||
group.add_option(
|
|
||||||
"--js", type="string", default=None,
|
|
||||||
help="JavaScript to execute when the window finishes loading"
|
|
||||||
"(example: --js='document.bgColor=\"red\";'). "
|
|
||||||
"If you need to wait for asynchronous code to finish before "
|
|
||||||
"capturing the screenshot, call webkit2png.stop() before the "
|
|
||||||
"async code runs, then webkit2png.start() to capture the image.")
|
|
||||||
group.add_option(
|
|
||||||
"--noimages", action="store_true",
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
group.add_option(
|
|
||||||
"--no-images", action="store_true",
|
|
||||||
help="don't load images")
|
|
||||||
group.add_option(
|
|
||||||
"--nojs", action="store_true",
|
|
||||||
help=optparse.SUPPRESS_HELP)
|
|
||||||
group.add_option(
|
|
||||||
"--no-js", action="store_true",
|
|
||||||
help="disable JavaScript support")
|
|
||||||
group.add_option(
|
|
||||||
"--transparent", action="store_true",
|
|
||||||
help="render output on a transparent background (requires a web "
|
|
||||||
"page with a transparent background)", default=False)
|
|
||||||
cmdparser.add_option_group(group)
|
|
||||||
|
|
||||||
(options, args) = cmdparser.parse_args()
|
|
||||||
if len(args) == 0:
|
|
||||||
cmdparser.print_usage()
|
|
||||||
return
|
|
||||||
if options.filename:
|
|
||||||
if len(args) != 1 or args[0] == "-":
|
|
||||||
print "--filename option requires exactly one url"
|
|
||||||
return
|
|
||||||
|
|
||||||
# deprecated options
|
|
||||||
if options.nojs:
|
|
||||||
print >> sys.stderr, 'Warning: --nojs will be removed in ' \
|
|
||||||
'webkit2png 1.0. Please use --no-js.'
|
|
||||||
options.no_js = True
|
|
||||||
if options.noimages:
|
|
||||||
print >> sys.stderr, 'Warning: --noimages will be removed in ' \
|
|
||||||
'webkit2png 1.0. Please use --no-images.'
|
|
||||||
options.no_images = True
|
|
||||||
|
|
||||||
if options.scale == 0:
|
|
||||||
cmdparser.error("scale cannot be zero")
|
|
||||||
# make sure we're outputing something
|
|
||||||
if not (options.fullsize or options.thumb or options.clipped):
|
|
||||||
options.fullsize = True
|
|
||||||
options.thumb = True
|
|
||||||
options.clipped = True
|
|
||||||
# work out the initial size of the browser window
|
|
||||||
# (this might need to be larger so clipped image is right size)
|
|
||||||
options.initWidth = (options.clipwidth / options.scale)
|
|
||||||
options.initHeight = (options.clipheight / options.scale)
|
|
||||||
options.width *= options.zoom
|
|
||||||
if options.width > options.initWidth:
|
|
||||||
options.initWidth = options.width
|
|
||||||
if options.height > options.initHeight:
|
|
||||||
options.initHeight = options.height
|
|
||||||
|
|
||||||
# Hide the dock icon (needs to run before NSApplication.sharedApplication)
|
|
||||||
AppKit.NSBundle.mainBundle().infoDictionary()['LSBackgroundOnly'] = '1'
|
|
||||||
|
|
||||||
app = AppKit.NSApplication.sharedApplication()
|
|
||||||
|
|
||||||
# create an app delegate
|
|
||||||
delegate = AppDelegate.alloc().init()
|
|
||||||
delegate.timeout = options.timeout
|
|
||||||
AppKit.NSApp().setDelegate_(delegate)
|
|
||||||
|
|
||||||
# create a window
|
|
||||||
rect = Foundation.NSMakeRect(0, 0, 100, 100)
|
|
||||||
win = AppKit.NSWindow.alloc()
|
|
||||||
win.initWithContentRect_styleMask_backing_defer_(
|
|
||||||
rect, AppKit.NSBorderlessWindowMask, 2, 0)
|
|
||||||
if options.debug:
|
|
||||||
win.orderFrontRegardless()
|
|
||||||
# create a webview object
|
|
||||||
webview = WebKit.WebView.alloc()
|
|
||||||
webview.initWithFrame_(rect)
|
|
||||||
# turn off scrolling so the content is actually x wide and not x-15
|
|
||||||
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
|
|
||||||
|
|
||||||
if options.user_agent:
|
|
||||||
webview.setCustomUserAgent_(options.user_agent)
|
|
||||||
else:
|
|
||||||
webkit_version = Foundation.NSBundle.bundleForClass_(WebKit.WebView)\
|
|
||||||
.objectForInfoDictionaryKey_(WebKit.kCFBundleVersionKey)[1:]
|
|
||||||
webview.setApplicationNameForUserAgent_(
|
|
||||||
"Like-Version/6.0 Safari/%s webkit2png/%s" % (webkit_version, __version__))
|
|
||||||
webview.setPreferencesIdentifier_('webkit2png')
|
|
||||||
webview.preferences().setLoadsImagesAutomatically_(not options.no_images)
|
|
||||||
webview.preferences().setJavaScriptEnabled_(not options.no_js)
|
|
||||||
|
|
||||||
if options.zoom != 1.0:
|
|
||||||
webview._setZoomMultiplier_isTextOnly_(options.zoom, False)
|
|
||||||
|
|
||||||
# add the webview to the window
|
|
||||||
win.setContentView_(webview)
|
|
||||||
|
|
||||||
# create a LoadDelegate
|
|
||||||
loaddelegate = WebkitLoad.alloc().init()
|
|
||||||
loaddelegate.options = options
|
|
||||||
loaddelegate.urls = args
|
|
||||||
webview.setFrameLoadDelegate_(loaddelegate)
|
|
||||||
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
212
old/webrender.py
212
old/webrender.py
@ -1,212 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# webrender.py - recursively render web pages to a gif+imagemap of clickable links
|
|
||||||
# caveat: this script requires to be run as a regular user and cannot run as a daemon
|
|
||||||
# from apache cgi-bin, you can use python built in http server instead
|
|
||||||
# usage:
|
|
||||||
# create cgi-bin directory, copy webrender.py to cgi-bin and chmod 755
|
|
||||||
# python -m CGIHTTPServer 8000
|
|
||||||
# navigate web browser to http://x.x.x.x:8000/cgi-bin/webrender.py
|
|
||||||
# the webrender-xxx.gif images are created in the CWD of the http server
|
|
||||||
|
|
||||||
|
|
||||||
__version__ = "1.0"
|
|
||||||
|
|
||||||
#
|
|
||||||
# This program is based on the software picidae.py 1.0 from http://www.picidae.net
|
|
||||||
# It was modified by Antoni Sawicki
|
|
||||||
#
|
|
||||||
# This program is based on the software webkit2png 0.4 from Paul Hammond.
|
|
||||||
# It was extended by picidae.net
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License
|
|
||||||
# as published by the Free Software Foundation; either version 2
|
|
||||||
# of the License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# 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, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import random
|
|
||||||
import Foundation
|
|
||||||
import WebKit
|
|
||||||
import AppKit
|
|
||||||
import objc
|
|
||||||
import string
|
|
||||||
import urllib
|
|
||||||
import socket
|
|
||||||
import cgi
|
|
||||||
import cgitb; cgitb.enable() # for trubleshooting
|
|
||||||
except ImportError:
|
|
||||||
print "Cannot find pyobjc library files. Are you sure it is installed?"
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
|
|
||||||
class AppDelegate (Foundation.NSObject):
|
|
||||||
# what happens when the app starts up
|
|
||||||
def applicationDidFinishLaunching_(self, aNotification):
|
|
||||||
webview = aNotification.object().windows()[0].contentView()
|
|
||||||
webview.frameLoadDelegate().getURL(webview)
|
|
||||||
|
|
||||||
|
|
||||||
class WebkitLoad (Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
|
|
||||||
# what happens if something goes wrong while loading
|
|
||||||
def webView_didFailLoadWithError_forFrame_(self,webview,error,frame):
|
|
||||||
print " ... something went wrong 1: " + error.localizedDescription()
|
|
||||||
self.getURL(webview)
|
|
||||||
def webView_didFailProvisionalLoadWithError_forFrame_(self,webview,error,frame):
|
|
||||||
print " ... something went wrong 2: " + error.localizedDescription()
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def getURL(self,webview):
|
|
||||||
if self.urls:
|
|
||||||
if self.urls[0] == '-':
|
|
||||||
url = sys.stdin.readline().rstrip()
|
|
||||||
if not url: AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
else:
|
|
||||||
url = self.urls.pop(0)
|
|
||||||
else:
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
self.resetWebview(webview)
|
|
||||||
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(Foundation.NSURL.URLWithString_(url)))
|
|
||||||
if not webview.mainFrame().provisionalDataSource():
|
|
||||||
print "<nosuccess />"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def resetWebview(self,webview):
|
|
||||||
rect = Foundation.NSMakeRect(0,0,1024,768)
|
|
||||||
webview.window().setContentSize_((1024,768))
|
|
||||||
webview.setFrame_(rect)
|
|
||||||
|
|
||||||
def resizeWebview(self,view):
|
|
||||||
view.window().display()
|
|
||||||
view.window().setContentSize_(view.bounds().size)
|
|
||||||
view.setFrame_(view.bounds())
|
|
||||||
|
|
||||||
def captureView(self,view):
|
|
||||||
view.lockFocus()
|
|
||||||
bitmapdata = AppKit.NSBitmapImageRep.alloc()
|
|
||||||
bitmapdata.initWithFocusedViewRect_(view.bounds())
|
|
||||||
view.unlockFocus()
|
|
||||||
return bitmapdata
|
|
||||||
|
|
||||||
# what happens when the page has finished loading
|
|
||||||
def webView_didFinishLoadForFrame_(self,webview,frame):
|
|
||||||
# don't care about subframes
|
|
||||||
if (frame == webview.mainFrame()):
|
|
||||||
view = frame.frameView().documentView()
|
|
||||||
|
|
||||||
self.resizeWebview(view)
|
|
||||||
|
|
||||||
URL = frame.dataSource().initialRequest().URL().absoluteString()
|
|
||||||
|
|
||||||
for fl in glob.glob("webrender-*.gif"):
|
|
||||||
os.remove(fl)
|
|
||||||
|
|
||||||
GIF = "webrender-%s.gif" % (random.randrange(0,1000))
|
|
||||||
|
|
||||||
bitmapdata = self.captureView(view)
|
|
||||||
bitmapdata.representationUsingType_properties_(AppKit.NSGIFFileType,None).writeToFile_atomically_(GIF,objc.YES)
|
|
||||||
|
|
||||||
myurl = "http://%s:%s%s" % (socket.gethostbyname(socket.gethostname()), os.getenv("SERVER_PORT"), os.getenv("SCRIPT_NAME"))
|
|
||||||
|
|
||||||
print "Content-type: text/html\r\n\r\n"
|
|
||||||
print "<!-- webrender.py by Antoni Sawicki -->"
|
|
||||||
print "<html><head><title>Webrender - %s</title></head><body><table border=\"0\"><tr>" % (URL)
|
|
||||||
print "<td><form action=\"%s\">" % (myurl)
|
|
||||||
print "<input type=\"text\" name=\"url\" value=\"%s\" size=\"80\">" % (URL)
|
|
||||||
print "<input type=\"submit\" value=\"go\">"
|
|
||||||
print "</form></td><td>"
|
|
||||||
print "<form action=\"%s\">" % (myurl)
|
|
||||||
print "<input type=\"text\" name=\"search\" value=\"\" size=\"20\">"
|
|
||||||
print "<input type=\"submit\" value=\"search\">"
|
|
||||||
print "</form></td></tr></table>"
|
|
||||||
print "<img src=\"../%s\" alt=\"webrender\" usemap=\"#map\" border=\"0\">" % (GIF)
|
|
||||||
|
|
||||||
|
|
||||||
# Analyse HTML and get links
|
|
||||||
print "<map name=\"map\">";
|
|
||||||
|
|
||||||
domdocument = frame.DOMDocument()
|
|
||||||
domnodelist = domdocument.getElementsByTagName_('A')
|
|
||||||
i = 0
|
|
||||||
while i < domnodelist.length():
|
|
||||||
# linkvalue
|
|
||||||
value = domnodelist.item_(i).valueForKey_('href')
|
|
||||||
|
|
||||||
# position-rect
|
|
||||||
myrect = domnodelist.item_(i).boundingBox()
|
|
||||||
|
|
||||||
xmin = Foundation.NSMinX(myrect)
|
|
||||||
ymin = Foundation.NSMinY(myrect)
|
|
||||||
xmax = Foundation.NSMaxX(myrect)
|
|
||||||
ymax = Foundation.NSMaxY(myrect)
|
|
||||||
|
|
||||||
# print Link
|
|
||||||
escval = string.replace( string.replace(value, "?", "TNXQUE"), "&", "TNXAMP" )
|
|
||||||
print "<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"\" href=\"%s?url=%s\"></area>" % (xmin, ymin, xmax, ymax, myurl, escval)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
print "</map>"
|
|
||||||
print "</body></html>"
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
|
|
||||||
# obtain url from cgi input
|
|
||||||
form = cgi.FieldStorage()
|
|
||||||
rawurl = form.getfirst("url", "http://www.google.com")
|
|
||||||
rawsearch = form.getfirst("search")
|
|
||||||
if rawsearch:
|
|
||||||
url = "http://www.google.com/search?q=%s" % (rawsearch)
|
|
||||||
else:
|
|
||||||
url = string.replace( string.replace(rawurl, "TNXAMP", "&"), "TNXQUE", "?")
|
|
||||||
|
|
||||||
|
|
||||||
AppKit.NSApplicationLoad();
|
|
||||||
|
|
||||||
app = AppKit.NSApplication.sharedApplication()
|
|
||||||
|
|
||||||
# create an app delegate
|
|
||||||
delegate = AppDelegate.alloc().init()
|
|
||||||
AppKit.NSApp().setDelegate_(delegate)
|
|
||||||
|
|
||||||
# create a window
|
|
||||||
rect = Foundation.NSMakeRect(-16000,-16000,100,100)
|
|
||||||
win = AppKit.NSWindow.alloc()
|
|
||||||
win.initWithContentRect_styleMask_backing_defer_ (rect, AppKit.NSBorderlessWindowMask, 2, 0)
|
|
||||||
|
|
||||||
# create a webview object
|
|
||||||
webview = WebKit.WebView.alloc()
|
|
||||||
webview.initWithFrame_(rect)
|
|
||||||
# turn off scrolling so the content is actually x wide and not x-15
|
|
||||||
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
|
|
||||||
# add the webview to the window
|
|
||||||
win.setContentView_(webview)
|
|
||||||
|
|
||||||
|
|
||||||
# create a LoadDelegate
|
|
||||||
loaddelegate = WebkitLoad.alloc().init()
|
|
||||||
loaddelegate.options = [""]
|
|
||||||
loaddelegate.urls = [url]
|
|
||||||
webview.setFrameLoadDelegate_(loaddelegate)
|
|
||||||
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
if __name__ == '__main__' : main()
|
|
||||||
|
|
931
old/wrp.py
931
old/wrp.py
@ -1,931 +0,0 @@
|
|||||||
#!/usr/bin/env python2.7
|
|
||||||
|
|
||||||
# wrp.py - Web Rendering Proxy - https://github.com/tenox7/wrp
|
|
||||||
# A HTTP proxy service that renders the requested URL in to a image associated
|
|
||||||
# with an imagemap of clickable links. This is an adaptation of previous works by
|
|
||||||
# picidae.net and Paul Hammond.
|
|
||||||
|
|
||||||
__version__ = "2.0"
|
|
||||||
|
|
||||||
#
|
|
||||||
# This program is based on the software picidae.py from picidae.net
|
|
||||||
# It was modified by Antoni Sawicki and Natalia Portillo
|
|
||||||
#
|
|
||||||
# This program is based on the software webkit2png from Paul Hammond.
|
|
||||||
# It was extended by picidae.net
|
|
||||||
#
|
|
||||||
# Copyright (c) 2013-2018 Antoni Sawicki
|
|
||||||
# Copyright (c) 2012-2013 picidae.net
|
|
||||||
# Copyright (c) 2004-2013 Paul Hammond
|
|
||||||
# Copyright (c) 2017-2018 Natalia Portillo
|
|
||||||
# Copyright (c) 2018 //gir.st/
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Configuration options:
|
|
||||||
PORT = 8080
|
|
||||||
WIDTH = 1024
|
|
||||||
HEIGHT = 768
|
|
||||||
ISMAP = False # ISMAP=True is Server side for Mosaic 1.1 and up. HTML 3.2 supports Client side maps (ISMAP=False)
|
|
||||||
WAIT = 1 # sleep for 1 second to allow javascript renders
|
|
||||||
QUALITY = 75 # For JPEG: image quality 0-100; For PNG: sets compression level (leftmost digit 0 fastest, 9 best)
|
|
||||||
AUTOWIDTH = True # Check for browser width using javascript
|
|
||||||
FORMAT = "AUTO" # AUTO = GIF for mac OS, JPG for rest; PNG, GIF, JPG as supported values.
|
|
||||||
SSLSTRIP = True # enable to automatically downgrade secure requests
|
|
||||||
|
|
||||||
# PythonMagick configuration options
|
|
||||||
MK_MONOCHROME = False # Convert the render to a black and white dithered image
|
|
||||||
MK_GRAYSCALE = False # Convert the render to a grayscal dithered image
|
|
||||||
MK_COLORS = 0 # Reduce number of colors in the image. 0 for not reducing. Less than 256 works in grayscale also.
|
|
||||||
MK_DITHER = False # Dither the image to reduce size. GIFs will always be dithered. Ignored if MK_COLORS is not set.
|
|
||||||
|
|
||||||
import re
|
|
||||||
import random
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import string
|
|
||||||
import urllib
|
|
||||||
import socket
|
|
||||||
import SocketServer
|
|
||||||
import SimpleHTTPServer
|
|
||||||
import threading
|
|
||||||
import Queue
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
import StringIO
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
try:
|
|
||||||
import PythonMagick
|
|
||||||
HasMagick = True
|
|
||||||
except ImportError:
|
|
||||||
HasMagick = False
|
|
||||||
|
|
||||||
# Request queue (URLs go in here)
|
|
||||||
REQ = Queue.Queue()
|
|
||||||
# Response queue (dummy response objects)
|
|
||||||
RESP = Queue.Queue()
|
|
||||||
# Renders dictionary
|
|
||||||
RENDERS = {}
|
|
||||||
|
|
||||||
#######################
|
|
||||||
### Linux CODEPATH ###
|
|
||||||
#######################
|
|
||||||
|
|
||||||
if sys.platform.startswith('linux') or sys.platform.startswith('freebsd'):
|
|
||||||
try:
|
|
||||||
from PyQt5.QtCore import *
|
|
||||||
from PyQt5.QtGui import *
|
|
||||||
from PyQt5.QtWebKit import *
|
|
||||||
from PyQt5.QtWebKitWidgets import *
|
|
||||||
from PyQt5.QtNetwork import *
|
|
||||||
from PyQt5.QtWidgets import *
|
|
||||||
IsPyQt5 = True
|
|
||||||
except ImportError:
|
|
||||||
from PyQt4.QtCore import *
|
|
||||||
from PyQt4.QtGui import *
|
|
||||||
from PyQt4.QtWebKit import *
|
|
||||||
from PyQt4.QtNetwork import *
|
|
||||||
IsPyQt5 = False
|
|
||||||
|
|
||||||
# claunia: Check how to use this in macOS
|
|
||||||
logging.basicConfig(filename='/dev/stdout', level=logging.WARN, )
|
|
||||||
logger = logging.getLogger('wrp')
|
|
||||||
|
|
||||||
# Class for Website-Rendering. Uses QWebPage, which
|
|
||||||
# requires a running QtGui to work.
|
|
||||||
class WebkitRenderer(QObject):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
"""Sets default values for the properties."""
|
|
||||||
|
|
||||||
if not QApplication.instance():
|
|
||||||
raise RuntimeError(self.__class__.__name__ + \
|
|
||||||
" requires a running QApplication instance")
|
|
||||||
QObject.__init__(self)
|
|
||||||
|
|
||||||
# Initialize default properties
|
|
||||||
self.width = kwargs.get('width', 0)
|
|
||||||
self.height = kwargs.get('height', 0)
|
|
||||||
self.timeout = kwargs.get('timeout', 0)
|
|
||||||
self.wait = kwargs.get('wait', 0)
|
|
||||||
self.logger = kwargs.get('logger', None)
|
|
||||||
# Set this to true if you want to capture flash.
|
|
||||||
# Not that your desktop must be large enough for
|
|
||||||
# fitting the whole window.
|
|
||||||
self.grabWholeWindow = kwargs.get('grabWholeWindow', False)
|
|
||||||
|
|
||||||
# Set some default options for QWebPage
|
|
||||||
self.qWebSettings = {
|
|
||||||
QWebSettings.JavascriptEnabled : True,
|
|
||||||
QWebSettings.PluginsEnabled : True,
|
|
||||||
QWebSettings.PrivateBrowsingEnabled : True,
|
|
||||||
QWebSettings.JavascriptCanOpenWindows : False
|
|
||||||
}
|
|
||||||
|
|
||||||
def render(self, url):
|
|
||||||
"""Renders the given URL into a QImage object"""
|
|
||||||
# We have to use this helper object because
|
|
||||||
# QApplication.processEvents may be called, causing
|
|
||||||
# this method to get called while it has not returned yet.
|
|
||||||
helper = _WebkitRendererHelper(self)
|
|
||||||
helper._window.resize(self.width, self.height)
|
|
||||||
image = helper.render(url)
|
|
||||||
|
|
||||||
# Bind helper instance to this image to prevent the
|
|
||||||
# object from being cleaned up (and with it the QWebPage, etc)
|
|
||||||
# before the data has been used.
|
|
||||||
image.helper = helper
|
|
||||||
|
|
||||||
return image
|
|
||||||
|
|
||||||
class _WebkitRendererHelper(QObject):
|
|
||||||
"""This helper class is doing the real work. It is required to
|
|
||||||
allow WebkitRenderer.render() to be called "asynchronously"
|
|
||||||
(but always from Qt's GUI thread).
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
"""Copies the properties from the parent (WebkitRenderer) object,
|
|
||||||
creates the required instances of QWebPage, QWebView and QMainWindow
|
|
||||||
and registers some Slots.
|
|
||||||
"""
|
|
||||||
QObject.__init__(self)
|
|
||||||
|
|
||||||
# Copy properties from parent
|
|
||||||
for key, value in parent.__dict__.items():
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
# Create and connect required PyQt4 objects
|
|
||||||
self._page = CustomWebPage(logger=self.logger)
|
|
||||||
self._view = QWebView()
|
|
||||||
self._view.setPage(self._page)
|
|
||||||
self._window = QMainWindow()
|
|
||||||
self._window.setCentralWidget(self._view)
|
|
||||||
|
|
||||||
# Import QWebSettings
|
|
||||||
for key, value in self.qWebSettings.iteritems():
|
|
||||||
self._page.settings().setAttribute(key, value)
|
|
||||||
|
|
||||||
# Connect required event listeners
|
|
||||||
if IsPyQt5:
|
|
||||||
self._page.loadFinished.connect(self._on_load_finished)
|
|
||||||
self._page.loadStarted.connect(self._on_load_started)
|
|
||||||
self._page.networkAccessManager().sslErrors.connect(self._on_ssl_errors)
|
|
||||||
self._page.networkAccessManager().finished.connect(self._on_each_reply)
|
|
||||||
else:
|
|
||||||
self.connect(self._page, SIGNAL("loadFinished(bool)"), self._on_load_finished)
|
|
||||||
self.connect(self._page, SIGNAL("loadStarted()"), self._on_load_started)
|
|
||||||
self.connect(self._page.networkAccessManager(),
|
|
||||||
SIGNAL("sslErrors(QNetworkReply *,const QList<QSslError>&)"),
|
|
||||||
self._on_ssl_errors)
|
|
||||||
self.connect(self._page.networkAccessManager(),
|
|
||||||
SIGNAL("finished(QNetworkReply *)"),
|
|
||||||
self._on_each_reply)
|
|
||||||
|
|
||||||
# The way we will use this, it seems to be unesseccary to have Scrollbars enabled
|
|
||||||
self._page.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
|
|
||||||
self._page.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
|
|
||||||
self._page.settings().setUserStyleSheetUrl(
|
|
||||||
QUrl("data:text/css,html,body{overflow-y:hidden !important;}"))
|
|
||||||
|
|
||||||
# Show this widget
|
|
||||||
# self._window.show()
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
"""Clean up Qt4 objects. """
|
|
||||||
self._window.close()
|
|
||||||
del self._window
|
|
||||||
del self._view
|
|
||||||
del self._page
|
|
||||||
|
|
||||||
def render(self, url):
|
|
||||||
"""The real worker. Loads the page (_load_page) and awaits
|
|
||||||
the end of the given 'delay'. While it is waiting outstanding
|
|
||||||
QApplication events are processed.
|
|
||||||
After the given delay, the Window or Widget (depends
|
|
||||||
on the value of 'grabWholeWindow' is drawn into a QPixmap
|
|
||||||
"""
|
|
||||||
self._load_page(url, self.width, self.height, self.timeout)
|
|
||||||
# Wait for end of timer. In this time, process
|
|
||||||
# other outstanding Qt events.
|
|
||||||
if self.wait > 0:
|
|
||||||
if self.logger: self.logger.debug("Waiting %d seconds " % self.wait)
|
|
||||||
waitToTime = time.time() + self.wait
|
|
||||||
while time.time() < waitToTime:
|
|
||||||
if QApplication.hasPendingEvents():
|
|
||||||
QApplication.processEvents()
|
|
||||||
|
|
||||||
if self.grabWholeWindow:
|
|
||||||
# Note that this does not fully ensure that the
|
|
||||||
# window still has the focus when the screen is
|
|
||||||
# grabbed. This might result in a race condition.
|
|
||||||
self._view.activateWindow()
|
|
||||||
if IsPyQt5:
|
|
||||||
image = QScreen.grabWindow(self._window.winId())
|
|
||||||
else:
|
|
||||||
image = QPixmap.grabWindow(self._window.winId())
|
|
||||||
else:
|
|
||||||
if IsPyQt5:
|
|
||||||
image = QWidget.grab(self._window)
|
|
||||||
else:
|
|
||||||
image = QPixmap.grabWidget(self._window)
|
|
||||||
|
|
||||||
httpout = WebkitRenderer.httpout
|
|
||||||
|
|
||||||
frame = self._view.page().currentFrame()
|
|
||||||
web_url = frame.url().toString()
|
|
||||||
|
|
||||||
# Write URL map
|
|
||||||
httpout.write("<!-- Web Rendering Proxy v%s by Antoni Sawicki -->\n"
|
|
||||||
% (__version__))
|
|
||||||
httpout.write("<!-- Request for [%s] frame [%s] -->\n"
|
|
||||||
% (WebkitRenderer.req_url, web_url))
|
|
||||||
# Get title
|
|
||||||
httpout.write("<HTML><HEAD>")
|
|
||||||
for ttl in frame.findAllElements('title'):
|
|
||||||
httpout.write((u"<TITLE>%s</TITLE>"
|
|
||||||
% ttl.toPlainText()).encode('utf-8', errors='ignore'))
|
|
||||||
break # Don't repeat bad HTML coding with several title marks
|
|
||||||
httpout.write("</HEAD>\n<BODY>\n")
|
|
||||||
|
|
||||||
if AUTOWIDTH:
|
|
||||||
httpout.write("<script>document.write('<span style=\"display: none;\"><img src=\"http://width-' + document.body.clientWidth + '-px.jpg\" width=\"0\" height=\"0\"></span>');</script>\n")
|
|
||||||
|
|
||||||
if ISMAP == True:
|
|
||||||
httpout.write("<A HREF=\"http://%s\">"
|
|
||||||
"<IMG SRC=\"http://%s\" ALT=\"wrp-render\" ISMAP>\n"
|
|
||||||
"</A>\n" % (WebkitRenderer.req_map, WebkitRenderer.req_img))
|
|
||||||
mapfile = StringIO.StringIO()
|
|
||||||
mapfile.write("default %s\n" % (web_url))
|
|
||||||
else:
|
|
||||||
httpout.write("<IMG SRC=\"http://%s\" ALT=\"wrp-render\" USEMAP=\"#map\">\n"
|
|
||||||
"<MAP NAME=\"map\">\n" % (WebkitRenderer.req_img))
|
|
||||||
|
|
||||||
for x in frame.findAllElements('a'):
|
|
||||||
turl = QUrl(web_url).resolved(QUrl(x.attribute('href'))).toString()
|
|
||||||
xmin, ymin, xmax, ymax = x.geometry().getCoords()
|
|
||||||
if ISMAP == True:
|
|
||||||
mapfile.write("rect %s %i,%i %i,%i\n".decode('utf-8', errors='ignore') % (turl, xmin, ymin, xmax, ymax))
|
|
||||||
else:
|
|
||||||
httpout.write(("<AREA SHAPE=\"RECT\""
|
|
||||||
" COORDS=\"%i,%i,%i,%i\""
|
|
||||||
" ALT=\"%s\" HREF=\"%s\">\n".decode('utf-8', errors='ignore')
|
|
||||||
% (xmin, ymin, xmax, ymax, turl, turl)).encode("utf-8"))
|
|
||||||
|
|
||||||
if ISMAP != True:
|
|
||||||
httpout.write("</MAP>\n")
|
|
||||||
|
|
||||||
httpout.write("</BODY>\n</HTML>\n")
|
|
||||||
|
|
||||||
if ISMAP == True:
|
|
||||||
RENDERS[WebkitRenderer.req_map] = mapfile
|
|
||||||
|
|
||||||
return image
|
|
||||||
|
|
||||||
def _load_page(self, url, width, height, timeout):
|
|
||||||
"""
|
|
||||||
This method implements the logic for retrieving and displaying
|
|
||||||
the requested page.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# This is an event-based application. So we have to wait until
|
|
||||||
# "loadFinished(bool)" raised.
|
|
||||||
cancelAt = time.time() + timeout
|
|
||||||
self.__loading = True
|
|
||||||
self.__loadingResult = False # Default
|
|
||||||
self._page.mainFrame().load(QUrl(url))
|
|
||||||
while self.__loading:
|
|
||||||
if timeout > 0 and time.time() >= cancelAt:
|
|
||||||
raise RuntimeError("Request timed out on %s" % url)
|
|
||||||
while QApplication.hasPendingEvents() and self.__loading:
|
|
||||||
QCoreApplication.processEvents()
|
|
||||||
|
|
||||||
if self.logger: self.logger.debug("Processing result")
|
|
||||||
|
|
||||||
if self.__loading_result == False:
|
|
||||||
if self.logger: self.logger.warning("Failed to load %s" % url)
|
|
||||||
|
|
||||||
# Set initial viewport (the size of the "window")
|
|
||||||
size = self._page.mainFrame().contentsSize()
|
|
||||||
if self.logger: self.logger.debug("contentsSize: %s", size)
|
|
||||||
if width > 0:
|
|
||||||
size.setWidth(width)
|
|
||||||
if height > 0:
|
|
||||||
size.setHeight(height)
|
|
||||||
|
|
||||||
self._window.resize(size)
|
|
||||||
|
|
||||||
def _on_each_reply(self, reply):
|
|
||||||
"""Logs each requested uri"""
|
|
||||||
self.logger.debug("Received %s" % (reply.url().toString()))
|
|
||||||
|
|
||||||
# Eventhandler for "loadStarted()" signal
|
|
||||||
def _on_load_started(self):
|
|
||||||
"""Slot that sets the '__loading' property to true."""
|
|
||||||
if self.logger: self.logger.debug("loading started")
|
|
||||||
self.__loading = True
|
|
||||||
|
|
||||||
# Eventhandler for "loadFinished(bool)" signal
|
|
||||||
def _on_load_finished(self, result):
|
|
||||||
"""Slot that sets the '__loading' property to false and stores
|
|
||||||
the result code in '__loading_result'.
|
|
||||||
"""
|
|
||||||
if self.logger: self.logger.debug("loading finished with result %s", result)
|
|
||||||
self.__loading = False
|
|
||||||
self.__loading_result = result
|
|
||||||
|
|
||||||
# Eventhandler for "sslErrors(QNetworkReply *,const QList<QSslError>&)" signal
|
|
||||||
def _on_ssl_errors(self, reply, errors):
|
|
||||||
"""Slot that writes SSL warnings into the log but ignores them."""
|
|
||||||
for e in errors:
|
|
||||||
if self.logger: self.logger.warn("SSL: " + e.errorString())
|
|
||||||
reply.ignoreSslErrors()
|
|
||||||
|
|
||||||
class CustomWebPage(QWebPage):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(CustomWebPage, self).__init__()
|
|
||||||
self.logger = kwargs.get('logger', None)
|
|
||||||
|
|
||||||
def javaScriptAlert(self, frame, message):
|
|
||||||
if self.logger: self.logger.debug('Alert: %s', message)
|
|
||||||
|
|
||||||
def javaScriptConfirm(self, frame, message):
|
|
||||||
if self.logger: self.logger.debug('Confirm: %s', message)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def javaScriptPrompt(self, frame, message, default, result):
|
|
||||||
"""This function is called whenever a JavaScript program running inside frame tries to
|
|
||||||
prompt the user for input. The program may provide an optional message, msg, as well
|
|
||||||
as a default value for the input in defaultValue.
|
|
||||||
|
|
||||||
If the prompt was cancelled by the user the implementation should return false;
|
|
||||||
otherwise the result should be written to result and true should be returned.
|
|
||||||
If the prompt was not cancelled by the user, the implementation should return true and
|
|
||||||
the result string must not be null.
|
|
||||||
"""
|
|
||||||
if self.logger: self.logger.debug('Prompt: %s (%s)' % (message, default))
|
|
||||||
return False
|
|
||||||
|
|
||||||
def shouldInterruptJavaScript(self):
|
|
||||||
"""This function is called when a JavaScript program is running for a long period of
|
|
||||||
time. If the user wanted to stop the JavaScript the implementation should return
|
|
||||||
true; otherwise false.
|
|
||||||
"""
|
|
||||||
if self.logger: self.logger.debug("WebKit ask to interrupt JavaScript")
|
|
||||||
return True
|
|
||||||
|
|
||||||
#===============================================================================
|
|
||||||
|
|
||||||
def init_qtgui(display=None, style=None, qtargs=None):
|
|
||||||
"""Initiates the QApplication environment using the given args."""
|
|
||||||
if QApplication.instance():
|
|
||||||
logger.debug("QApplication has already been instantiated. \
|
|
||||||
Ignoring given arguments and returning existing QApplication.")
|
|
||||||
return QApplication.instance()
|
|
||||||
|
|
||||||
qtargs2 = [sys.argv[0]]
|
|
||||||
|
|
||||||
if display:
|
|
||||||
qtargs2.append('-display')
|
|
||||||
qtargs2.append(display)
|
|
||||||
# Also export DISPLAY var as this may be used
|
|
||||||
# by flash plugin
|
|
||||||
os.environ["DISPLAY"] = display
|
|
||||||
|
|
||||||
if style:
|
|
||||||
qtargs2.append('-style')
|
|
||||||
qtargs2.append(style)
|
|
||||||
|
|
||||||
qtargs2.extend(qtargs or [])
|
|
||||||
|
|
||||||
return QApplication(qtargs2)
|
|
||||||
|
|
||||||
# Technically, this is a QtGui application, because QWebPage requires it
|
|
||||||
# to be. But because we will have no user interaction, and rendering can
|
|
||||||
# not start before 'app.exec_()' is called, we have to trigger our "main"
|
|
||||||
# by a timer event.
|
|
||||||
def __main_qt():
|
|
||||||
# Render the page.
|
|
||||||
# If this method times out or loading failed, a
|
|
||||||
# RuntimeException is thrown
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
req = REQ.get()
|
|
||||||
WebkitRenderer.httpout = req[0]
|
|
||||||
WebkitRenderer.req_url = req[1]
|
|
||||||
WebkitRenderer.req_img = req[2]
|
|
||||||
WebkitRenderer.req_map = req[3]
|
|
||||||
if WebkitRenderer.req_url == "http://wrp.stop/" or WebkitRenderer.req_url == "http://www.wrp.stop/":
|
|
||||||
print ">>> Terminate Request Received"
|
|
||||||
QApplication.exit(0)
|
|
||||||
break
|
|
||||||
|
|
||||||
# Initialize WebkitRenderer object
|
|
||||||
renderer = WebkitRenderer()
|
|
||||||
renderer.logger = logger
|
|
||||||
renderer.width = WIDTH
|
|
||||||
renderer.height = HEIGHT
|
|
||||||
renderer.timeout = 60
|
|
||||||
renderer.wait = WAIT
|
|
||||||
renderer.grabWholeWindow = False
|
|
||||||
|
|
||||||
image = renderer.render(WebkitRenderer.req_url)
|
|
||||||
qBuffer = QBuffer()
|
|
||||||
|
|
||||||
if HasMagick:
|
|
||||||
image.save(qBuffer, 'png', QUALITY)
|
|
||||||
blob = PythonMagick.Blob(qBuffer.buffer().data())
|
|
||||||
mimg = PythonMagick.Image(blob)
|
|
||||||
mimg.quality(QUALITY)
|
|
||||||
|
|
||||||
if FORMAT=="GIF" and not MK_MONOCHROME and not MK_GRAYSCALE and not MK_DITHER and MK_COLORS != 0 and not MK_COLORS <= 256:
|
|
||||||
mimg.quantizeColors(256)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
|
|
||||||
if MK_MONOCHROME:
|
|
||||||
mimg.quantizeColorSpace(PythonMagick.ColorspaceType.GRAYColorspace)
|
|
||||||
mimg.quantizeColors(2)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
mimg.monochrome()
|
|
||||||
elif MK_GRAYSCALE:
|
|
||||||
mimg.quantizeColorSpace(PythonMagick.ColorspaceType.GRAYColorspace)
|
|
||||||
if MK_COLORS > 0 and MK_COLORS < 256:
|
|
||||||
mimg.quantizeColors(MK_COLORS)
|
|
||||||
else:
|
|
||||||
mimg.quantizeColors(256)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
else:
|
|
||||||
if MK_COLORS > 0:
|
|
||||||
mimg.quantizeColors(MK_COLORS)
|
|
||||||
if MK_DITHER:
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
|
|
||||||
if FORMAT=="AUTO" or FORMAT=="JPG":
|
|
||||||
mimg.write(blob, "jpg")
|
|
||||||
elif FORMAT=="PNG":
|
|
||||||
mimg.write(blob, "png")
|
|
||||||
elif FORMAT=="GIF":
|
|
||||||
mimg.write(blob, "gif")
|
|
||||||
output = StringIO.StringIO()
|
|
||||||
output.write(blob.data)
|
|
||||||
else:
|
|
||||||
if FORMAT=="AUTO" or FORMAT=="JPG":
|
|
||||||
image.save(qBuffer, 'jpg', QUALITY)
|
|
||||||
elif FORMAT=="PNG":
|
|
||||||
image.save(qBuffer, 'png', QUALITY)
|
|
||||||
|
|
||||||
output = StringIO.StringIO()
|
|
||||||
output.write(qBuffer.buffer().data())
|
|
||||||
|
|
||||||
RENDERS[req[2]] = output
|
|
||||||
|
|
||||||
del renderer
|
|
||||||
print ">>> done: %s [%d kb]..." % (WebkitRenderer.req_img, output.len/1024)
|
|
||||||
|
|
||||||
RESP.put('')
|
|
||||||
|
|
||||||
QApplication.exit(0)
|
|
||||||
except RuntimeError, e:
|
|
||||||
logger.error("main: %s" % e)
|
|
||||||
print >> sys.stderr, e
|
|
||||||
QApplication.exit(1)
|
|
||||||
|
|
||||||
######################
|
|
||||||
### macOS CODEPATH ###
|
|
||||||
######################
|
|
||||||
|
|
||||||
elif sys.platform == "darwin":
|
|
||||||
import Foundation
|
|
||||||
import WebKit
|
|
||||||
import AppKit
|
|
||||||
import objc
|
|
||||||
|
|
||||||
class AppDelegate(Foundation.NSObject):
|
|
||||||
# what happens when the app starts up
|
|
||||||
def applicationDidFinishLaunching_(self, aNotification):
|
|
||||||
webview = aNotification.object().windows()[0].contentView()
|
|
||||||
webview.frameLoadDelegate().getURL(webview)
|
|
||||||
|
|
||||||
class WebkitLoad(Foundation.NSObject, WebKit.protocols.WebFrameLoadDelegate):
|
|
||||||
# what happens if something goes wrong while loading
|
|
||||||
def webView_didFailLoadWithError_forFrame_(self, webview, error, frame):
|
|
||||||
if error.code() == Foundation.NSURLErrorCancelled:
|
|
||||||
return
|
|
||||||
print " ... something went wrong 1: " + error.localizedDescription()
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
def webView_didFailProvisionalLoadWithError_forFrame_(self, webview, error, frame):
|
|
||||||
if error.code() == Foundation.NSURLErrorCancelled:
|
|
||||||
return
|
|
||||||
print " ... something went wrong 2: " + error.localizedDescription()
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
def getURL(self, webview):
|
|
||||||
req = REQ.get()
|
|
||||||
WebkitLoad.httpout = req[0]
|
|
||||||
WebkitLoad.req_url = req[1]
|
|
||||||
WebkitLoad.req_img = req[2]
|
|
||||||
WebkitLoad.req_map = req[3]
|
|
||||||
|
|
||||||
if WebkitLoad.req_url == "http://wrp.stop/" or WebkitLoad.req_url == "http://www.wrp.stop/":
|
|
||||||
print ">>> Terminate Request Received"
|
|
||||||
AppKit.NSApplication.sharedApplication().terminate_(None)
|
|
||||||
|
|
||||||
nsurl = Foundation.NSURL.URLWithString_(WebkitLoad.req_url)
|
|
||||||
if not (nsurl and nsurl.scheme()):
|
|
||||||
nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(WebkitLoad.req_url)
|
|
||||||
nsurl = nsurl.absoluteURL()
|
|
||||||
|
|
||||||
Foundation.NSURLRequest.setAllowsAnyHTTPSCertificate_forHost_(objc.YES, nsurl.host())
|
|
||||||
|
|
||||||
self.resetWebview(webview)
|
|
||||||
webview.mainFrame().loadRequest_(Foundation.NSURLRequest.requestWithURL_(nsurl))
|
|
||||||
if not webview.mainFrame().provisionalDataSource():
|
|
||||||
print " ... not a proper url?"
|
|
||||||
RESP.put('')
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def resetWebview(self, webview):
|
|
||||||
rect = Foundation.NSMakeRect(0, 0, WIDTH, HEIGHT)
|
|
||||||
webview.window().setContentSize_((WIDTH, HEIGHT))
|
|
||||||
webview.setFrame_(rect)
|
|
||||||
|
|
||||||
def captureView(self, view):
|
|
||||||
view.window().display()
|
|
||||||
view.window().setContentSize_(view.bounds().size)
|
|
||||||
view.setFrame_(view.bounds())
|
|
||||||
|
|
||||||
if hasattr(view, "bitmapImageRepForCachingDisplayInRect_"):
|
|
||||||
bitmapdata = view.bitmapImageRepForCachingDisplayInRect_(view.bounds())
|
|
||||||
view.cacheDisplayInRect_toBitmapImageRep_(view.bounds(), bitmapdata)
|
|
||||||
else:
|
|
||||||
view.lockFocus()
|
|
||||||
bitmapdata = AppKit.NSBitmapImageRep.alloc()
|
|
||||||
bitmapdata.initWithFocusedViewRect_(view.bounds())
|
|
||||||
view.unlockFocus()
|
|
||||||
return bitmapdata
|
|
||||||
|
|
||||||
# what happens when the page has finished loading
|
|
||||||
def webView_didFinishLoadForFrame_(self, webview, frame):
|
|
||||||
# don't care about subframes
|
|
||||||
if frame == webview.mainFrame():
|
|
||||||
view = frame.frameView().documentView()
|
|
||||||
|
|
||||||
output = StringIO.StringIO()
|
|
||||||
|
|
||||||
if HasMagick:
|
|
||||||
output.write(self.captureView(view).representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType, None))
|
|
||||||
blob = PythonMagick.Blob(output)
|
|
||||||
mimg = PythonMagick.Image(blob)
|
|
||||||
mimg.quality(QUALITY)
|
|
||||||
|
|
||||||
if FORMAT=="GIF" and not MK_MONOCHROME and not MK_GRAYSCALE and not MK_DITHER and MK_COLORS != 0 and not MK_COLORS <= 256:
|
|
||||||
mimg.quantizeColors(256)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
|
|
||||||
if MK_MONOCHROME:
|
|
||||||
mimg.quantizeColorSpace(PythonMagick.ColorspaceType.GRAYColorspace)
|
|
||||||
mimg.quantizeColors(2)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
mimg.monochrome()
|
|
||||||
elif MK_GRAYSCALE:
|
|
||||||
mimg.quantizeColorSpace(PythonMagick.ColorspaceType.GRAYColorspace)
|
|
||||||
if MK_COLORS > 0 and MK_COLORS < 256:
|
|
||||||
mimg.quantizeColors(MK_COLORS)
|
|
||||||
else:
|
|
||||||
mimg.quantizeColors(256)
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
else:
|
|
||||||
if MK_COLORS > 0:
|
|
||||||
mimg.quantizeColors(MK_COLORS)
|
|
||||||
if MK_DITHER:
|
|
||||||
mimg.quantizeDither()
|
|
||||||
mimg.quantize()
|
|
||||||
|
|
||||||
if FORMAT=="JPG":
|
|
||||||
mimg.write(blob, "jpg")
|
|
||||||
elif FORMAT=="PNG":
|
|
||||||
mimg.write(blob, "png")
|
|
||||||
elif FORMAT=="AUTO" or FORMAT=="GIF":
|
|
||||||
mimg.write(blob, "gif")
|
|
||||||
output = StringIO.StringIO()
|
|
||||||
output.write(blob.data)
|
|
||||||
else:
|
|
||||||
if FORMAT=="AUTO" or FORMAT=="GIF":
|
|
||||||
output.write(self.captureView(view).representationUsingType_properties_(
|
|
||||||
AppKit.NSGIFFileType, None))
|
|
||||||
elif FORMAT=="JPG":
|
|
||||||
output.write(self.captureView(view).representationUsingType_properties_(
|
|
||||||
AppKit.NSJPEGFileType, None))
|
|
||||||
elif FORMAT=="PNG":
|
|
||||||
output.write(self.captureView(view).representationUsingType_properties_(
|
|
||||||
AppKit.NSPNGFileType, None))
|
|
||||||
|
|
||||||
RENDERS[WebkitLoad.req_img] = output
|
|
||||||
|
|
||||||
# url of the rendered page
|
|
||||||
web_url = frame.dataSource().initialRequest().URL().absoluteString()
|
|
||||||
|
|
||||||
httpout = WebkitLoad.httpout
|
|
||||||
|
|
||||||
httpout.write("<!-- Web Rendering Proxy v%s by Antoni Sawicki -->\n"
|
|
||||||
% (__version__))
|
|
||||||
httpout.write("<!-- Request for [%s] frame [%s] -->\n"
|
|
||||||
% (WebkitLoad.req_url, web_url))
|
|
||||||
|
|
||||||
domdocument = frame.DOMDocument()
|
|
||||||
# Get title
|
|
||||||
httpout.write("<HTML><HEAD>")
|
|
||||||
httpout.write((u"<TITLE>%s</TITLE>"
|
|
||||||
% domdocument.title()).encode('utf-8', errors='ignore'))
|
|
||||||
httpout.write("</HEAD>\n<BODY>\n")
|
|
||||||
|
|
||||||
if AUTOWIDTH:
|
|
||||||
httpout.write("<script>document.write('<span style=\"display: none;\"><img src=\"http://width-' + document.body.clientWidth + '-px.jpg\" width=\"0\" height=\"0\"></span>');</script>\n")
|
|
||||||
|
|
||||||
if ISMAP == True:
|
|
||||||
httpout.write("<A HREF=\"http://%s\">"
|
|
||||||
"<IMG SRC=\"http://%s\" ALT=\"wrp-render\" ISMAP>\n"
|
|
||||||
"</A>\n" % (WebkitLoad.req_map, WebkitLoad.req_img))
|
|
||||||
mapfile = StringIO.StringIO()
|
|
||||||
mapfile.write("default %s\n" % (web_url))
|
|
||||||
else:
|
|
||||||
httpout.write("<IMG SRC=\"http://%s\" ALT=\"wrp-render\" USEMAP=\"#map\">\n"
|
|
||||||
"<MAP NAME=\"map\">\n" % (WebkitLoad.req_img))
|
|
||||||
|
|
||||||
domnodelist = domdocument.getElementsByTagName_('A')
|
|
||||||
i = 0
|
|
||||||
while i < domnodelist.length():
|
|
||||||
turl = domnodelist.item_(i).valueForKey_('href')
|
|
||||||
#TODO: crashes? validate url? insert web_url if wrong?
|
|
||||||
myrect = domnodelist.item_(i).boundingBox()
|
|
||||||
|
|
||||||
xmin = Foundation.NSMinX(myrect)
|
|
||||||
ymin = Foundation.NSMinY(myrect)
|
|
||||||
xmax = Foundation.NSMaxX(myrect)
|
|
||||||
ymax = Foundation.NSMaxY(myrect)
|
|
||||||
|
|
||||||
if ISMAP == True:
|
|
||||||
mapfile.write("rect %s %i,%i %i,%i\n".decode('utf-8', errors='ignore') % (turl, xmin, ymin, xmax, ymax))
|
|
||||||
else:
|
|
||||||
httpout.write("<AREA SHAPE=\"RECT\""
|
|
||||||
" COORDS=\"%i,%i,%i,%i\""
|
|
||||||
" ALT=\"%s\" HREF=\"%s\">\n".decode('utf-8', errors='ignore')
|
|
||||||
% (xmin, ymin, xmax, ymax, turl, turl))
|
|
||||||
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if ISMAP != True:
|
|
||||||
httpout.write("</MAP>\n")
|
|
||||||
|
|
||||||
httpout.write("</BODY>\n</HTML>\n")
|
|
||||||
|
|
||||||
if ISMAP == True:
|
|
||||||
RENDERS[WebkitLoad.req_map] = mapfile
|
|
||||||
|
|
||||||
# Return to Proxy thread and Loop...
|
|
||||||
RESP.put('')
|
|
||||||
self.getURL(webview)
|
|
||||||
|
|
||||||
def main_cocoa():
|
|
||||||
# Launch NS Application
|
|
||||||
AppKit.NSApplicationLoad()
|
|
||||||
app = AppKit.NSApplication.sharedApplication()
|
|
||||||
delegate = AppDelegate.alloc().init()
|
|
||||||
AppKit.NSApp().setDelegate_(delegate)
|
|
||||||
AppKit.NSBundle.mainBundle().infoDictionary()['NSAppTransportSecurity'] = \
|
|
||||||
dict(NSAllowsArbitraryLoads=True)
|
|
||||||
rect = Foundation.NSMakeRect(-16000, -16000, 100, 100)
|
|
||||||
win = AppKit.NSWindow.alloc()
|
|
||||||
win.initWithContentRect_styleMask_backing_defer_(rect, AppKit.NSBorderlessWindowMask, 2, 0)
|
|
||||||
webview = WebKit.WebView.alloc()
|
|
||||||
webview.initWithFrame_(rect)
|
|
||||||
webview.mainFrame().frameView().setAllowsScrolling_(objc.NO)
|
|
||||||
webkit_version = Foundation.NSBundle.bundleForClass_(WebKit.WebView). \
|
|
||||||
objectForInfoDictionaryKey_(WebKit.kCFBundleVersionKey)[1:]
|
|
||||||
webview.setApplicationNameForUserAgent_("Like-Version/6.0 Safari/%s wrp/%s"
|
|
||||||
% (webkit_version, __version__))
|
|
||||||
win.setContentView_(webview)
|
|
||||||
loaddelegate = WebkitLoad.alloc().init()
|
|
||||||
loaddelegate.options = [""]
|
|
||||||
webview.setFrameLoadDelegate_(loaddelegate)
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
#######################
|
|
||||||
### COMMON CODEPATH ###
|
|
||||||
#######################
|
|
||||||
class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
|
||||||
def do_GET(self):
|
|
||||||
req_url = self.path
|
|
||||||
httpout = self.wfile
|
|
||||||
|
|
||||||
map_re = re.match(r"http://(wrp-\d+\.map).*?(\d+),(\d+)", req_url)
|
|
||||||
wid_re = re.match(r"http://(width-[0-9]+-px\.jpg).*", req_url)
|
|
||||||
gif_re = re.match(r"http://(wrp-\d+\.gif).*", req_url)
|
|
||||||
jpg_re = re.match(r"http://(wrp-\d+\.jpg).*", req_url)
|
|
||||||
png_re = re.match(r"http://(wrp-\d+\.png).*", req_url)
|
|
||||||
|
|
||||||
# Serve Rendered GIF
|
|
||||||
if gif_re:
|
|
||||||
img = gif_re.group(1)
|
|
||||||
print ">>> request for rendered gif image... %s [%d kb]" \
|
|
||||||
% (img, RENDERS[img].len/1024)
|
|
||||||
self.send_response(200, 'OK')
|
|
||||||
self.send_header('Content-type', 'image/gif')
|
|
||||||
self.end_headers()
|
|
||||||
httpout.write(RENDERS[img].getvalue())
|
|
||||||
del RENDERS[img]
|
|
||||||
|
|
||||||
elif jpg_re:
|
|
||||||
img = jpg_re.group(1)
|
|
||||||
print ">>> request for rendered jpg image... %s [%d kb]" \
|
|
||||||
% (img, RENDERS[img].len/1024)
|
|
||||||
self.send_response(200, 'OK')
|
|
||||||
self.send_header('Content-type', 'image/jpeg')
|
|
||||||
self.end_headers()
|
|
||||||
httpout.write(RENDERS[img].getvalue())
|
|
||||||
del RENDERS[img]
|
|
||||||
|
|
||||||
elif png_re:
|
|
||||||
img = png_re.group(1)
|
|
||||||
print ">>> request for rendered png image... %s [%d kb]" \
|
|
||||||
% (img, RENDERS[img].len/1024)
|
|
||||||
self.send_response(200, 'OK')
|
|
||||||
self.send_header('Content-type', 'image/png')
|
|
||||||
self.end_headers()
|
|
||||||
httpout.write(RENDERS[img].getvalue())
|
|
||||||
del RENDERS[img]
|
|
||||||
|
|
||||||
elif wid_re:
|
|
||||||
global WIDTH
|
|
||||||
try:
|
|
||||||
wid = req_url.split("-")
|
|
||||||
WIDTH = int(wid[1])
|
|
||||||
print ">>> width request: %d" % WIDTH
|
|
||||||
except:
|
|
||||||
print ">>> width request error" % WIDTH
|
|
||||||
|
|
||||||
self.send_error(404, "Width request")
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
# Process ISMAP Request
|
|
||||||
elif map_re:
|
|
||||||
map = map_re.group(1)
|
|
||||||
req_x = int(map_re.group(2))
|
|
||||||
req_y = int(map_re.group(3))
|
|
||||||
print ">>> ISMAP request... %s [%d,%d] " % (map, req_x, req_y)
|
|
||||||
|
|
||||||
mapf = RENDERS[map]
|
|
||||||
mapf.seek(0)
|
|
||||||
goto_url = "none"
|
|
||||||
for line in mapf.readlines():
|
|
||||||
if re.match(r"(\S+)", line).group(1) == "default":
|
|
||||||
default_url = re.match(r"\S+\s+(\S+)", line).group(1)
|
|
||||||
|
|
||||||
elif re.match(r"(\S+)", line).group(1) == "rect":
|
|
||||||
try:
|
|
||||||
rect = re.match(r"(\S+)\s+(\S+)\s+(\d+),(\d+)\s+(\d+),(\d+)", line)
|
|
||||||
min_x = int(rect.group(3))
|
|
||||||
min_y = int(rect.group(4))
|
|
||||||
max_x = int(rect.group(5))
|
|
||||||
max_y = int(rect.group(6))
|
|
||||||
if (req_x >= min_x) and \
|
|
||||||
(req_x <= max_x) and \
|
|
||||||
(req_y >= min_y) and \
|
|
||||||
(req_y <= max_y):
|
|
||||||
goto_url = rect.group(2)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if goto_url == "none":
|
|
||||||
goto_url = default_url
|
|
||||||
|
|
||||||
print ">>> ISMAP redirect: %s\n" % (goto_url)
|
|
||||||
|
|
||||||
self.send_response(302, "Found")
|
|
||||||
self.send_header("Location", goto_url)
|
|
||||||
self.send_header("Content-type", "text/html")
|
|
||||||
self.end_headers()
|
|
||||||
httpout.write("<HTML><BODY><A HREF=\"%s\">%s</A></BODY></HTML>\n"
|
|
||||||
% (goto_url, goto_url))
|
|
||||||
|
|
||||||
# Process a web page request and generate image
|
|
||||||
else:
|
|
||||||
print ">>> URL request... " + req_url
|
|
||||||
|
|
||||||
if req_url == "http://wrp.stop/" or req_url == "http://www.wrp.stop/":
|
|
||||||
REQ.put((httpout, req_url, "", ""))
|
|
||||||
RESP.get()
|
|
||||||
else:
|
|
||||||
reqst = urllib.urlopen(req_url)
|
|
||||||
|
|
||||||
if reqst.info().type == "text/html" or reqst.info().type == "application/xhtml+xml":
|
|
||||||
# If an error occurs, send error headers to the requester
|
|
||||||
if reqst.getcode() >= 400:
|
|
||||||
self.send_response(reqst.getcode())
|
|
||||||
for hdr in reqst.info():
|
|
||||||
self.send_header(hdr, reqst.info()[hdr])
|
|
||||||
self.end_headers()
|
|
||||||
else:
|
|
||||||
self.send_response(200, 'OK')
|
|
||||||
self.send_header('Content-type', 'text/html')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
rnd = random.randrange(0, 1000)
|
|
||||||
|
|
||||||
if FORMAT == "GIF":
|
|
||||||
req_extension = ".gif"
|
|
||||||
elif FORMAT == "JPG":
|
|
||||||
req_extension = ".jpg"
|
|
||||||
elif FORMAT == "PNG":
|
|
||||||
req_extension = ".png"
|
|
||||||
elif (sys.platform.startswith('linux') or sys.platform.startswitch('freebsd')) and FORMAT == "AUTO":
|
|
||||||
req_extension = ".jpg"
|
|
||||||
elif sys.platform == "darwin" and FORMAT == "AUTO":
|
|
||||||
req_extension = ".gif"
|
|
||||||
|
|
||||||
req_img = "wrp-%s%s" % (rnd, req_extension)
|
|
||||||
req_map = "wrp-%s.map" % (rnd)
|
|
||||||
|
|
||||||
# To WebKit Thread
|
|
||||||
REQ.put((httpout, req_url, req_img, req_map))
|
|
||||||
# Wait for completition
|
|
||||||
RESP.get()
|
|
||||||
# If the requested file is not HTML or XHTML, just return it as is.
|
|
||||||
else:
|
|
||||||
self.send_response(reqst.getcode())
|
|
||||||
for hdr in reqst.info():
|
|
||||||
self.send_header(hdr, reqst.info()[hdr])
|
|
||||||
self.end_headers()
|
|
||||||
httpout.write(reqst.read())
|
|
||||||
|
|
||||||
def run_proxy():
|
|
||||||
httpd = SocketServer.TCPServer(('', PORT), Proxy)
|
|
||||||
print "Web Rendering Proxy v%s serving at port: %s" % (__version__, PORT)
|
|
||||||
while 1:
|
|
||||||
httpd.serve_forever()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
if(FORMAT != "AUTO" and FORMAT != "GIF" and FORMAT != "JPG" and FORMAT != "PNG"):
|
|
||||||
sys.exit("Unsupported image format \"%s\". Exiting." % FORMAT)
|
|
||||||
|
|
||||||
if (sys.platform.startswith('linux') or sys.platform.startswith('freebsd')) and FORMAT == "GIF" and not HasMagick:
|
|
||||||
sys.exit("GIF format is not supported on this platform. Exiting.")
|
|
||||||
|
|
||||||
# run traffic through sslstrip as a quick workaround for getting SSL webpages to work
|
|
||||||
# NOTE: modern browsers are doing their best to stop this kind of 'attack'. Firefox
|
|
||||||
# supports an about:config flag test.currentTimeOffsetSeconds(int) = 12000000, which
|
|
||||||
# you can use to circumvent those checks.
|
|
||||||
if SSLSTRIP:
|
|
||||||
try:
|
|
||||||
subprocess.check_output(["pidof", "sslstrip"])
|
|
||||||
except:
|
|
||||||
subprocess.Popen(["sslstrip"], stdout=open(os.devnull,'w'), stderr=subprocess.STDOUT) # runs on port 10000 by default
|
|
||||||
QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.HttpProxy, "localhost", 10000))
|
|
||||||
# Launch Proxy Thread
|
|
||||||
threading.Thread(target=run_proxy).start()
|
|
||||||
|
|
||||||
if sys.platform.startswith('linux') or sys.platform.startswith('freebsd'):
|
|
||||||
import signal
|
|
||||||
try:
|
|
||||||
import PyQt5.QtCore
|
|
||||||
except ImportError:
|
|
||||||
import PyQt4.QtCore
|
|
||||||
# Initialize Qt-Application, but make this script
|
|
||||||
# abortable via CTRL-C
|
|
||||||
app = init_qtgui(display=None, style=None)
|
|
||||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
||||||
|
|
||||||
QTimer.singleShot(0, __main_qt)
|
|
||||||
sys.exit(app.exec_())
|
|
||||||
elif sys.platform == "darwin":
|
|
||||||
main_cocoa()
|
|
||||||
else:
|
|
||||||
sys.exit("Unsupported platform: %s. Exiting." % sys.platform)
|
|
||||||
|
|
||||||
if __name__ == '__main__': main()
|
|
Loading…
Reference in New Issue
Block a user