233 Commits
1.1 ... 4.5

Author SHA1 Message Date
Antoni Sawicki
fafe232463 more readme updates 2019-12-24 03:14:29 -08:00
Antoni Sawicki
6c49b1f73c added geometry flags to readme 2019-12-24 02:56:36 -08:00
Antoni Sawicki
a533521784 readme fixes 2019-12-24 02:51:44 -08:00
Antoni Sawicki
0f9ebc6252 add arm64 linux target 2019-11-14 00:52:25 -08:00
Antoni Sawicki
af5174456a add propper img src width and height and log dimension 2019-11-13 00:22:07 -08:00
Antoni Sawicki
d49ef9c1c2 Update README.md 2019-11-03 23:59:22 -08:00
Antoni Sawicki
23b4fbaf63 Update README.md 2019-11-03 23:55:33 -08:00
Antoni Sawicki
a91cc60a51 fix footer to render on mosaic hpux 2019-11-03 18:37:15 -08:00
Antoni Sawicki
51cd108bad scale is now a dropdown 2019-11-03 17:53:20 -08:00
Antoni Sawicki
cd2cf0baae char dangling in select dropdown 2019-11-03 17:41:11 -08:00
Antoni Sawicki
a344d177d6 footer and img alt displays page height and image size 2019-11-03 17:24:18 -08:00
Antoni Sawicki
02766d8844 footer to pass img type in request 2019-11-03 17:03:00 -08:00
Antoni Sawicki
91091cf94b png/gif is now a dropdown 2019-11-03 16:58:38 -08:00
Antoni Sawicki
95d9de7348 added flag to supply default geometry/size 2019-11-03 15:38:26 -08:00
Antoni Sawicki
6449c64e36 support unlimited page lenght via height=0 2019-11-03 15:01:05 -08:00
Antoni Sawicki
b058831ec6 fixed float/int casting 2019-11-03 01:00:44 -07:00
Antoni Sawicki
7c50c6e841 increase size of url input box 2019-11-03 00:59:56 -07:00
Antoni Sawicki
2f2e99eb85 changed imgmap to img for better readability 2019-11-03 00:56:04 -07:00
Antoni Sawicki
4dee5ea8d9 Update README.md 2019-10-27 17:03:54 -07:00
Antoni Sawicki
333666d3b0 Delete make.ps1 2019-10-27 17:02:11 -07:00
Antoni Sawicki
780143b766 gcr.io in readme 2019-10-14 22:26:32 -07:00
Antoni Sawicki
6b89e463f3 added gcr.io 2019-10-14 22:24:14 -07:00
Antoni Sawicki
ea1ae10f97 Update README.md 2019-10-02 16:00:51 -07:00
Antoni Sawicki
eb4201c56b added initial support for png image output 2019-08-22 00:38:36 -07:00
Antoni Sawicki
4cd55b31b0 adaptive background color 2019-08-12 23:35:29 -07:00
Antoni Sawicki
f0ba852785 added more build targets 2019-08-12 23:34:03 -07:00
Antoni Sawicki
66412fa95e add debug option to not delete images and maps from memory 2019-08-08 02:31:23 -07:00
Antoni Sawicki
cd5bb94def Update README.md 2019-08-05 00:22:19 -07:00
Antoni Sawicki
357f3ed6bf ver bump 2019-08-04 09:21:17 -07:00
Antoni Sawicki
97c0679e0b smaller font for status line 2019-08-04 09:08:23 -07:00
Antoni Sawicki
e2223af833 makefile update 2019-08-04 09:01:55 -07:00
Antoni Sawicki
60989d3395 Update README.md 2019-08-04 01:16:07 -07:00
Antoni Sawicki
74d015a4da Update README.md 2019-08-04 01:15:26 -07:00
Antoni Sawicki
8628c00dd7 Update README.md 2019-08-04 01:14:31 -07:00
Antoni Sawicki
327baf318a add docker support 2019-08-04 00:51:55 -07:00
Antoni Sawicki
0e07f422f6 fix content type for shutdown 2019-08-04 00:39:21 -07:00
Antoni Sawicki
f7aece10e9 Update README.md 2019-07-30 16:46:46 -07:00
Antoni Sawicki
a7b7164932 Update README.md 2019-07-30 16:45:00 -07:00
Antoni Sawicki
7a27cf7b62 better ps make script 2019-07-29 23:59:32 -07:00
Antoni Sawicki
749f8bea5d Update README.md 2019-07-17 14:04:53 -07:00
Antoni Sawicki
290bc5a977 Update README.md 2019-07-17 13:59:44 -07:00
Antoni Sawicki
cc98932f5a Update README.md 2019-07-17 13:54:54 -07:00
Antoni Sawicki
f69e213a0b readme update 2019-07-17 01:01:18 -07:00
Antoni Sawicki
66641a099b readme update 2019-07-17 00:52:59 -07:00
Antoni Sawicki
872321c699 readme update 2019-07-17 00:49:36 -07:00
Antoni Sawicki
ba4183e0b4 4.3 2019-07-17 00:05:01 -07:00
Antoni Sawicki
4212678d81 readme updates 2019-07-16 23:47:37 -07:00
Antoni Sawicki
0d2ba9d4b2 added readme to ld 2019-07-16 23:44:27 -07:00
Antoni Sawicki
a63d4ef50d added historical versions to old directory 2019-07-16 23:39:40 -07:00
Antoni Sawicki
127114f753 added gitignore 2019-07-16 23:31:54 -07:00
Antoni Sawicki
b313e703fb fixes for ps build 2019-07-16 23:30:40 -07:00
Antoni Sawicki
ee26c40eb3 added simple ps1 build script 2019-07-16 23:20:52 -07:00
Antoni Sawicki
6c29008eb5 added cache control cruft 2019-07-16 22:47:23 -07:00
Antoni Sawicki
bb84d43d31 fixed handling interrupts and shutdown 2019-07-16 22:29:35 -07:00
Antoni Sawicki
7067d2cdf8 img update 2019-07-14 22:51:57 -07:00
Antoni Sawicki
640a405622 Merge branch 'master' of https://github.com/tenox7/wrp 2019-07-14 01:54:50 -07:00
Antoni Sawicki
22dd6aaab2 removed alt as it was being annoying in netscape 2019-07-14 01:54:41 -07:00
Antoni Sawicki
d102016ba9 reduced sleep to 2 sec 2019-07-14 01:53:55 -07:00
Antoni Sawicki
253fef2aad fixed logging for wrpreq from from 2019-07-14 01:53:33 -07:00
Antoni Sawicki
fdfad6bc69 Update README.md 2019-07-14 01:40:17 -07:00
Antoni Sawicki
1807790629 readme update 2019-07-13 01:31:24 -07:00
Antoni Sawicki
bd7d92393d readme update 2019-07-13 01:25:35 -07:00
Antoni Sawicki
e3b28e93c5 readme update 2019-07-13 01:23:34 -07:00
Antoni Sawicki
6784d47892 readme update 2019-07-13 00:47:19 -07:00
Antoni Sawicki
c96eb9ae35 readme update 2019-07-13 00:45:01 -07:00
Antoni Sawicki
cebebfa408 ver bump 2019-07-13 00:42:27 -07:00
Antoni Sawicki
9d7bb952c5 added buttons for backspace, enter and arrows 2019-07-13 00:40:56 -07:00
Antoni Sawicki
fd4b7a381e Update README.md 2019-07-12 14:28:26 -07:00
Antoni Sawicki
b894c3f809 readme update 2019-07-12 02:36:04 -07:00
Antoni Sawicki
733efaea56 ver bump 2019-07-12 02:25:17 -07:00
Antoni Sawicki
ad668d1bca readme update 2019-07-12 02:20:56 -07:00
Antoni Sawicki
4e28a50a8d readme update 2019-07-12 02:10:51 -07:00
Antoni Sawicki
2fab53d8a3 readme update 2019-07-12 02:04:35 -07:00
Antoni Sawicki
9557f172ed Merge branch 'clicky' 2019-07-12 01:54:59 -07:00
Antoni Sawicki
a3661003b0 add support for sending keys in to web forms 2019-07-12 01:51:23 -07:00
Antoni Sawicki
7baaa0bd6e more clicky stuff 2019-07-10 23:58:40 -07:00
Antoni Sawicki
e5e5e321e8 more cruft to make clicks work 2019-07-10 18:09:20 -07:00
Antoni Sawicki
650ac026c3 comments 2019-07-10 01:20:18 -07:00
Antoni Sawicki
579d67f7fb made clicky work somehow 2019-07-10 01:01:40 -07:00
0502f7a99d Fix typo in README. 2019-06-27 00:24:21 +01:00
Antoni Sawicki
eb1476e579 fix 2019-06-26 01:25:50 -07:00
Antoni Sawicki
f599a51c8d added more clicky cruft 2019-06-26 01:07:13 -07:00
Antoni Sawicki
dedf7479b8 clicky data encoding 2019-06-25 17:07:43 -07:00
Antoni Sawicki
877c42a388 Update README.md 2019-06-25 12:22:45 -07:00
Antoni Sawicki
1f5592cbde initial 2019-06-24 00:40:34 -07:00
Antoni Sawicki
404af50aa1 Update README.md 2019-06-20 10:20:41 -07:00
Antoni Sawicki
bb59229438 Update README.md 2019-06-18 14:49:32 -07:00
Antoni Sawicki
faa0818f18 Update README.md 2019-06-18 14:48:55 -07:00
Antoni Sawicki
26f999f262 Update README.md 2019-06-18 14:48:39 -07:00
Antoni Sawicki
4d02165619 Update README.md 2019-06-18 14:46:19 -07:00
Antoni Sawicki
1578b14fcd Update README.md 2019-06-18 14:45:50 -07:00
Antoni Sawicki
ceb6a67ff3 added shutdown handler 2019-06-17 23:53:22 -07:00
Antoni Sawicki
3ee146dee7 fix for #38, changed Content to Padding 2019-06-17 00:39:32 -07:00
Antoni Sawicki
1e58c94263 fix for #40 hang on page with no nodes, added timeout for cdp.Nodes 2019-06-17 00:38:52 -07:00
Antoni Sawicki
ce51eb6226 because lint said so 2019-06-17 00:21:32 -07:00
Antoni Sawicki
1b68593fd2 makefile fix 2019-06-17 00:19:01 -07:00
Antoni Sawicki
8f16abacde Update README.md 2019-06-11 02:56:00 -07:00
Antoni Sawicki
b1e0b417c3 Update README.md 2019-06-10 15:14:17 -07:00
Antoni Sawicki
fd6f8592ef Update README.md 2019-06-10 15:12:27 -07:00
Antoni Sawicki
210a12fe3d Update README.md 2019-06-10 15:11:40 -07:00
Antoni Sawicki
92f3cb7aee create new context on context cancelled 2019-06-08 17:32:35 -07:00
Antoni Sawicki
d9381ef71a print error message when failed to start server 2019-06-08 17:30:30 -07:00
Antoni Sawicki
64f86b4fd9 Update README.md 2019-06-07 01:30:32 -07:00
Antoni Sawicki
2d41aa1044 Update README.md 2019-06-06 16:46:40 -07:00
Antoni Sawicki
6e43026100 Update README.md 2019-06-06 16:33:39 -07:00
Antoni Sawicki
3285a60c69 Update README.md 2019-06-06 02:05:03 -07:00
Antoni Sawicki
c873e53df0 Update README.md 2019-06-06 01:35:23 -07:00
Antoni Sawicki
dc6c8eca52 Merge pull request #39 from djames1/master
Add support for PPC64 Little Endian, fix incorrect spelling in wrp.go and README.md
2019-06-06 01:21:23 -07:00
Antoni Sawicki
849239fc8e added real color quantizer, number of colors works now 2019-06-06 01:06:36 -07:00
Daniel James
d71a48b746 README.md: Fix typos 2019-06-06 04:05:29 -04:00
Daniel James
2671fc236c wrp.go: Fixed typos 2019-06-06 04:01:19 -04:00
Daniel James
15b227ccf1 Makefile: Add support for PPC64LE 2019-06-06 04:00:07 -04:00
Antoni Sawicki
89f5f556f9 assign to map before flush 2019-06-06 00:15:57 -07:00
Antoni Sawicki
af3aef5c39 switched to websafe color pallete, disabled number of colors input box for now 2019-06-06 00:15:36 -07:00
Antoni Sawicki
26ad732d99 Update README.md 2019-06-05 01:42:17 -07:00
Antoni Sawicki
033f2f3578 Update README.md 2019-06-05 01:35:42 -07:00
Antoni Sawicki
277d70f4c3 Add files via upload 2019-06-05 01:34:41 -07:00
Antoni Sawicki
ab4122a9ba Update README.md 2019-06-05 01:20:14 -07:00
Antoni Sawicki
9bd1359a4d Update README.md 2019-06-05 01:19:07 -07:00
Antoni Sawicki
adff09c6b9 Update README.md 2019-06-05 01:18:23 -07:00
Antoni Sawicki
93c84fdfca Update README.md 2019-06-05 01:17:25 -07:00
Antoni Sawicki
de780b353d Delete .gitignore 2019-06-04 23:50:27 -07:00
Antoni Sawicki
d64ae7e5d0 readme update 2019-06-04 23:48:26 -07:00
Antoni Sawicki
6f702d74e5 merge 2019-06-04 23:47:00 -07:00
Antoni Sawicki
6a8f655953 delete old readme 2019-06-04 23:46:16 -07:00
Antoni Sawicki
1b8d3544ed Update README.md 2019-06-04 23:35:40 -07:00
Antoni Sawicki
b5f5d6c576 readme update 2019-06-04 02:31:11 -07:00
Antoni Sawicki
99f4c8cac3 cleanup of pywebkit 2019-06-04 02:29:38 -07:00
Antoni Sawicki
6e75da10f3 readme update 2019-06-04 02:20:43 -07:00
Antoni Sawicki
fb4848d235 added makefile 2019-06-04 01:42:22 -07:00
Antoni Sawicki
06317022a6 better scaling 2019-06-04 01:30:00 -07:00
Antoni Sawicki
69d4b39eff restored context 2019-06-04 01:23:46 -07:00
Antoni Sawicki
5f6a1154df single line toolbar 2019-06-04 01:20:41 -07:00
Antoni Sawicki
d6005b52fd removed halt server 2019-06-04 01:20:19 -07:00
Antoni Sawicki
fabcd721c3 obtain server address from context, -l no longer needed 2019-06-04 00:58:45 -07:00
Antoni Sawicki
0ee45139c3 readme update 2019-06-03 17:52:02 -07:00
Antoni Sawicki
936cb97bc0 added headed mode and chromedp debug output 2019-06-03 17:50:16 -07:00
Antoni Sawicki
02758bd039 readme update 2019-06-03 01:47:44 -07:00
Antoni Sawicki
dd1031a35b Merge pull request #32 from rakslice/encoding_fix
Fix unicode encode error
2019-06-03 01:41:00 -07:00
Antoni Sawicki
a05a30c26f hack for ISMAP redirect 2019-06-03 00:12:17 -07:00
Antoni Sawicki
9c96a62816 dont crash on missing image or map 2019-06-02 22:23:41 -07:00
Antoni Sawicki
5dd4b5feab img border=0 2019-06-02 17:07:31 -07:00
Antoni Sawicki
791e87d7ed added number of colors 2019-06-02 17:06:41 -07:00
Antoni Sawicki
a8cc1b6b4e version branding etc 2019-06-02 16:24:46 -07:00
Antoni Sawicki
9358691ce5 version string 2019-06-02 16:19:08 -07:00
Antoni Sawicki
253d36e963 better handling of ismap variable 2019-06-02 16:18:53 -07:00
Antoni Sawicki
719a7fc560 better logging 2019-06-02 16:05:36 -07:00
Antoni Sawicki
3270bbcdd3 ismap server cruft 2019-06-02 15:55:58 -07:00
rakslice
fec97243ba Fix unicode encode error 2019-06-02 14:02:56 -07:00
Antoni Sawicki
6dfe7ddafc added more ismap cruft 2019-06-02 02:25:18 -07:00
Antoni Sawicki
a6df4cbec4 readme update 2019-06-01 01:26:18 -07:00
Antoni Sawicki
e48f0c9ff2 formatting 2019-06-01 01:17:50 -07:00
Antoni Sawicki
c7fcea908f changed names to prev/next due to issue with ie1.5 2019-06-01 01:17:41 -07:00
Antoni Sawicki
b91bbed4a7 readme fix 2019-05-31 18:28:48 -07:00
Antoni Sawicki
7a2f673fd0 more ismap stuff 2019-05-31 18:20:55 -07:00
Antoni Sawicki
61b84116b1 default to 0 page when go button is pressed 2019-05-31 17:49:14 -07:00
Antoni Sawicki
ea738f206a log image size 2019-05-31 17:12:26 -07:00
Antoni Sawicki
981055dff9 paginated scrolling 2019-05-31 16:41:25 -07:00
Antoni Sawicki
deb0cf7923 ISMAP checkbox 2019-05-31 01:56:34 -07:00
Antoni Sawicki
186fda4949 windows bug 2019-05-31 01:34:44 -07:00
Antoni Sawicki
7610f52574 readme update 2019-05-31 00:44:50 -07:00
Antoni Sawicki
416490289d chromedp.run error handling 2019-05-31 00:41:46 -07:00
Antoni Sawicki
0ae49044c2 simple google search if url doesnt start with http 2019-05-31 00:36:53 -07:00
Antoni Sawicki
cb87a83d26 add simple page scrolling 2019-05-31 00:19:10 -07:00
Antoni Sawicki
ebe19912e6 scale area map too 2019-05-30 23:40:43 -07:00
Antoni Sawicki
5d8f51ac66 width height and scale go to area target 2019-05-30 18:10:52 -07:00
Antoni Sawicki
d905704a2a add width height and scale input boxes 2019-05-30 18:08:48 -07:00
Antoni Sawicki
d382c38547 unexpected eof on windows 2019-05-30 02:15:52 -07:00
Antoni Sawicki
12664e6a10 readme fix 2019-05-30 02:05:04 -07:00
Antoni Sawicki
e643ec1d69 readme fix 2019-05-30 02:04:25 -07:00
Antoni Sawicki
9ad651c72c randomized image url 2019-05-30 02:03:17 -07:00
Antoni Sawicki
a897f76e20 readme updates 2019-05-30 02:02:24 -07:00
Antoni Sawicki
df400d57b3 log encode errors 2019-05-30 00:53:59 -07:00
Antoni Sawicki
546e686cbc shutdown function via /halt url 2019-05-30 00:07:08 -07:00
Antoni Sawicki
57a107aa69 log decode error instead of bailing out 2019-05-29 23:49:39 -07:00
Antoni Sawicki
aabc8cf021 link to dev branch 2019-05-29 18:54:04 -07:00
Antoni Sawicki
a3eb7cb69a add copyright 2019-05-29 18:53:05 -07:00
Antoni Sawicki
d8617af9c2 resolve relative a href urls 2019-05-29 18:48:07 -07:00
Antoni Sawicki
c6186d6fb4 added favico handler 2019-05-29 18:47:03 -07:00
Antoni Sawicki
7d84d01268 encode image as gif 2019-05-29 18:02:29 -07:00
Antoni Sawicki
5b827bffb0 add browser size setting 2019-05-29 02:39:06 -07:00
Antoni Sawicki
0680b4a72e add flags 2019-05-29 01:52:28 -07:00
Antoni Sawicki
e869291f8e gocdp initial checkin 2019-05-29 01:29:01 -07:00
Antoni Sawicki
071a75dfc6 Update README.md 2019-05-27 16:37:19 -07:00
Antoni Sawicki
34d4e2fa2a Create LICENSE 2019-03-27 13:29:02 -07:00
Antoni Sawicki
2ac4464b98 changed reference to link 2019-01-07 17:26:09 -08:00
Antoni Sawicki
cc3d4a674b readme update 2019-01-07 17:24:22 -08:00
Antoni Sawicki
6655993c07 readme update 2019-01-07 17:04:07 -08:00
Antoni Sawicki
64cf34fb85 Merge pull request #27 from girst/master
support https:// sites by stripping with sslstrip
2019-01-07 16:50:53 -08:00
girst
85063cefc8 support https:// sites by stripping with sslstrip
if this project wasn't perverse enough, the web-rendering-proxy now
requires another proxy as its dependency.
2018-12-28 23:10:13 +01:00
Antoni Sawicki
7013f521b3 Status update in readme 2018-12-11 17:19:05 -08:00
tenox7
b15d64351b ISMAP to bool 2018-08-09 13:21:31 -07:00
Antoni Sawicki
0b617d1592 merge from dev 2018-06-23 01:01:03 -07:00
Antoni Sawicki
f6446c0a3e readme update 2018-06-23 00:44:42 -07:00
Antoni Sawicki
4526dfca64 allow to run on freebsd 2018-06-23 00:40:27 -07:00
Antoni Sawicki
40c0329d1a readme update 2018-06-23 00:21:52 -07:00
Antoni Sawicki
67ec48aa62 small copyright update and formatting fixes 2018-06-23 00:17:39 -07:00
Antoni Sawicki
b89211e432 Merge pull request #21 from miniupnp/freebsd
Support for FreeBSD
2018-03-26 02:19:57 -07:00
Thomas Bernard
ef96884e77 Support for FreeBSD 2018-03-25 23:45:32 +02:00
8549e13f64 Added changelog. 2017-05-10 07:41:19 +01:00
6a6d559a74 Version bumped to 2.0 and some typos corrected. 2017-05-10 07:28:17 +01:00
5c1559984d Corrected how webpage title is got on mac OS. 2017-05-10 07:25:36 +01:00
18b382f82d Added PythonMagick support.
Fixes #7
2017-05-10 06:54:22 +01:00
b090ea47b0 Merge pull request #20 from tenox7/v1.4
V1.4
2017-05-10 05:10:14 +01:00
20544c2d50 Add support for user choosing file format.
BMP would be huge so I don't see a reason to add it, and with this we could consider this fixes #14
2017-05-10 05:08:27 +01:00
12496f3d7c Remove spurious ctags file. 2017-05-10 04:27:00 +01:00
fb569d0a62 Added option to auto adjust browser width using javascript code. 2017-05-10 04:25:32 +01:00
b4333ca2fd Added code to prevent crashing when URLs contain non-ASCII characters.
Does not guarantee handling of non-UTF-8 URLs (like ISO 8859-15, Shift-JIS, etc)
2017-05-10 04:24:56 +01:00
9914700ae9 Adds support for stopping when browser prepends www to wrp.stop command. 2017-05-10 03:46:15 +01:00
100ab83720 Adds support for returning erors and non-HTML pages as is. Fixes #18 2017-05-10 03:45:13 +01:00
4fefe187cd Correct title working. 2017-05-10 03:43:26 +01:00
762a3822f7 Sets title from read one. 2017-05-10 03:40:58 +01:00
afc5e5724d Adds support for PyQt5, fixes #19 2017-05-09 01:10:37 +01:00
1e79b37d7c Do not remove mapfile, client may go back. 2017-01-22 16:43:10 +00:00
90d1e82b00 Implemented ISMAP on Linux, closes #3. 2017-01-22 16:42:32 +00:00
e34a63604f Clean up used maps. 2017-01-22 14:17:25 +00:00
d7a2db49b8 Uses memory streams instead of files, closes #6 2017-01-21 21:40:30 +00:00
6ea543b077 Remove spurious debug line. 2017-01-21 20:54:02 +00:00
6854596629 Updated README. 2017-01-21 18:15:09 +00:00
2faee75543 Remove and ignore Visual Studio Code files. 2017-01-21 18:12:40 +00:00
b4b730b652 Use QApplication.exit(0) when wrp.stop. 2017-01-21 17:56:54 +00:00
3d5d56c5f7 Exit as soon as requested (not working). 2017-01-21 16:44:21 +00:00
29c71e6fd9 Refactored code to follow common python style. 2017-01-21 16:38:14 +00:00
ebb8d0efdf Add Visual Studio Code configuration to use python2. 2017-01-21 14:41:30 +00:00
85f9b175ce Merged both codepaths in same file. This time for real closes #12
Signed-off-by: Natalia Portillo <claunia@claunia.com>
2017-01-20 00:12:17 +00:00
966923949b Merge common code in a single executable that detects corresponding codepath and runs it, closes #12
Signed-off-by: Natalia Portillo <claunia@claunia.com>
2017-01-18 15:35:24 +00:00
4ebed0ad60 Merge branch 'Dev' of https://github.com/tenox7/wrp into Dev 2017-01-10 10:54:10 +00:00
Antoni Sawicki
b463aad8ca 1.2 dev update queue and ISMAP
passes parameters through queue instead of globals, adds preliminary
support for ISMAP
2017-01-10 10:53:50 +00:00
f91e9ce356 Allow Mac OS X to load non-SSL secured URLs, closes #11
Signed-off-by: Natalia Portillo <claunia@claunia.com>
2017-01-10 10:32:52 +00:00
e209d42e30 Make executables as such.
Signed-off-by: Natalia Portillo <claunia@claunia.com>
2017-01-10 10:30:15 +00:00
Antoni Sawicki
8a431f4668 1.2 dev update queue and ISMAP
passes parameters through queue instead of globals, adds preliminary
support for ISMAP
2016-12-26 23:09:34 -08:00
Antoni Sawicki
f827d20300 readme update 2016-07-13 14:28:37 -07:00
Antoni Sawicki
e1891e2e37 readme update 2016-07-13 14:10:20 -07:00
15 changed files with 2744 additions and 653 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
wrp-*

5
Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM chromedp/headless-shell
ADD wrp /wrp
ENTRYPOINT ["/wrp"]
ENV PATH="/headless-shell:${PATH}"
LABEL maintainer="as@tenoware.com"

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

26
Makefile Normal file
View File

@@ -0,0 +1,26 @@
all: wrp
wrp: wrp.go
go build wrp.go
cross:
GOOS=linux GOARCH=amd64 go build -a -o wrp-amd64-linux wrp.go
GOOS=freebsd GOARCH=amd64 go build -a -o wrp-amd64-freebsd wrp.go
GOOS=openbsd GOARCH=amd64 go build -a -o wrp-amd64-openbsd wrp.go
GOOS=darwin GOARCH=amd64 go build -a -o wrp-amd64-macos wrp.go
GOOS=windows GOARCH=amd64 go build -a -o wrp-amd64-windows.exe wrp.go
GOOS=linux GOARCH=arm go build -a -o wrp-arm-linux wrp.go
GOOS=linux GOARCH=arm64 go build -a -o wrp-arm64-linux wrp.go
docker: wrp
docker build -t tenox7/wrp:latest .
dockerhub:
docker push tenox7/wrp:latest
gcrio:
docker tag tenox7/wrp:latest gcr.io/tenox7/wrp
docker push gcr.io/tenox7/wrp
clean:
rm -rf wrp-* wrp

3
README
View File

@@ -1,3 +0,0 @@
WRP is a HTTP proxy service that renders the web page in to a GIF/JPEG image associated with clickable imagemap of the original web links. It allows to use historical and obsolete web browsers on the modern web. It's still a work in progress but it's quite stable and usable for casual web browsing.
Two versions of WRP exist, Cocoa-Webkit for Mac OS X and QT-Webkit, for Linux, BSD and other platforms.

67
README.md Normal file
View File

@@ -0,0 +1,67 @@
# WRP - Web Rendering Proxy
A browser-in-browser "proxy" server that allows to use historical / vintage web browsers on the modern web. It works by rendering a web page in to a GIF or PNG image with clickable imagemap.
![Internet Explorer 1.5 doing Gmail](wrp.png)
## Usage
1. [Download a WRP binary](https://github.com/tenox7/wrp/releases/) and run it on a machine that will become your WRP gateway/server.
2. Point your legacy browser to `http://address:port` of WRP server. Do not set or use it as a "http proxy server".
3. Type a search string or a http/https URL and click Go.
4. Adjust your screen width/height/scale/#colors to fit in your old browser.
5. Scroll web page by clicking on the in-image scroll bar.
6. Do not use client browser history-back, instead use **Bk** button in the app.
7. To send keystrokes, fill **K** input box and press Go. There also are buttons for backspace, enter and arrow keys.
8. Experimentally you can set height **H** to `0` to render in to one tall image without the vertical scrollbar. Note it will be large, slow to process, download and display on client browser.
## Docker
docker hub:
```shell
docker run -d -p 8080:8080 tenox7/wrp
```
gcr.io:
```shell
docker run -d -p 8080:8080 gcr.io/tenox7/wrp:latest
```
## Flags
```flags
-l listen address:port, default :8080
-t image type gif (default) or png, when using PNG number of colors is ignored
-g image geometry, WxHXC, height can be 0 for unlimited, default 1152x600x256"
-h headed mode, display browser window on the server
-d chromedp debug logging
-n do not free maps and gif images after use
```
## Minimal Requirements
* Server Gateway should run on a modern hardware/os that supports memory hungry Chrome.
* Client Browser needs to support `HTML FORMs` and `ISMAP`. Typically Mosaic 2.0 would be minimum version for forms. However ISMAP was supported since 0.6B, so if you manually enter url using `?url=...`, you can use the ealier version.
## History
* In 2014, version 1.0 started as a *cgi-bin* script, adaptation of `webkit2png.py` and `pcidade.py`, [blog post](https://virtuallyfun.com/2014/03/03/surfing-modern-web-with-ancient-browsers/).
* Later in 2014, version 2.0 became a stand alone http-proxy server, also support for both Linux/MacOS, [another post](https://virtuallyfun.com/wordpress/2014/03/11/web-rendering-proxy-update//).
* In 2016 the whole internet migrated to HTTPS/SSL/TLS and WRP largely stopped working. Python code became unmaintainable and mostly unportable (especially to Windows, even WSL).
* In 2019 WRP 3.0 has been rewritten in Golang/Chromedp as browser-in-browser instead of http proxy.
* Later in 2019, WRP 4.0 has been completely refactored to use mouse clicks instead using a href nodes. Also in 4.1 added sending keystrokes in to input boxes. You can now login to Gmail. Also now runs as a Docker container.
## Credits
* Uses [chromedp](https://github.com/chromedp), thanks to [mvdan](https://github.com/mvdan) for dealing with my issues
* Uses [go-quantize](https://github.com/ericpauley/go-quantize), thanks to [ericpauley](https://github.com/ericpauley) for developing the missing go quantizer
* Thanks to Jason Stevens of [Fun With Virtualization](https://virtuallyfun.com/) for graciously hosting my rumblings
* Thanks to [claunia](https://github.com/claunia/) for help with the Python/Webkit version in the past
## Legal Stuff
License: Apache 2.0
Copyright (c) 2013-2018 Antoni Sawicki
Copyright (c) 2019 Google LLC

9
old/README Normal file
View File

@@ -0,0 +1,9 @@
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 Normal file
View File

@@ -0,0 +1,392 @@
#!/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()

506
old/webkit2png.py Normal file
View File

@@ -0,0 +1,506 @@
#!/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 Normal file
View File

@@ -0,0 +1,212 @@
#!/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 Normal file
View File

@@ -0,0 +1,931 @@
#!/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()

View File

@@ -1,228 +0,0 @@
#!/usr/bin/env python
# wrp.py - Web Rendering Proxy
# A HTTP proxy service that renders the requested URL in to a GIF image associated
# with an imagemap of clickable links. This is an adaptation of previous works by
# picidae.net and Paul Hammond.
__version__ = "1.1"
#
# This program is based on the software picidae.py from picidae.net
# It was modified by Antoni Sawicki http://www.tenox.net/out/#wrp
#
# This program is based on the software webkit2png from Paul Hammond.
# It was extended by picidae.net
#
# Copyright (c) 2013-2014 Antoni Sawicki
# Copyright (c) 2012-2013 picidae.net
# 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.
#
# Configuration options:
PORT = 8080
WIDTH = 1024
HEIGHT = 768
import re
import random
import Foundation
import WebKit
import AppKit
import objc
import os
import time
import string
import urllib
import socket
import SocketServer
import SimpleHTTPServer
import threading
import Queue
# Request queue (URLs go in here)
REQ = Queue.Queue()
# Response queue (dummy response objects)
RESP = Queue.Queue()
#import pdb; pdb.set_trace()
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):
rurl = REQ.get()
if (rurl == "http://wrp.stop/"):
print ">>> Terminate Request Received"
AppKit.NSApplication.sharedApplication().terminate_(None)
nsurl = Foundation.NSURL.URLWithString_(rurl)
if not (nsurl and nsurl.scheme()):
nsurl = Foundation.NSURL.alloc().initFileURLWithPath_(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()
bitmapdata = self.captureView(view)
bitmapdata.representationUsingType_properties_(AppKit.NSGIFFileType,None).writeToFile_atomically_(GIF,objc.YES)
httpout.write("<!-- Web Rendering Proxy v%s by Antoni Sawicki -->\n<html>\n<body>\n<img src=\"http://%s\" alt=\"webrender\" usemap=\"#map\">\n<map name=\"map\">\n" % (__version__, GIF))
domdocument = frame.DOMDocument()
domnodelist = domdocument.getElementsByTagName_('A')
i = 0
while i < domnodelist.length():
value = domnodelist.item_(i).valueForKey_('href')
myrect = domnodelist.item_(i).boundingBox()
xmin = Foundation.NSMinX(myrect)
ymin = Foundation.NSMinY(myrect)
xmax = Foundation.NSMaxX(myrect)
ymax = Foundation.NSMaxY(myrect)
httpout.write("<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"%s\" href=\"%s\">\n" % (xmin, ymin, xmax, ymax, value, value))
i += 1
httpout.write("</map>\n</body>\n</html>\n")
RESP.put('')
self.getURL(webview)
class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
req_url=self.path
global httpout
httpout=self.wfile
self.send_response(200, 'OK')
gif_re = re.compile("http://webrender-[0-9]+\.gif")
ico_re = re.compile(".+\.ico")
if (gif_re.search(req_url)):
img=req_url.split("/")
print ">>> request for rendered gif image... %s" % (img[2])
self.send_header('Content-type', 'image/gif')
self.end_headers()
fimg = open(img[2])
httpout.write(fimg.read())
fimg.close()
os.remove(img[2])
elif (ico_re.search(req_url)):
#print ">>> request for .ico file - skipping"
self.send_error(404, "ICO not supported")
self.end_headers()
else:
print ">>> request for url: " + req_url
self.send_header('Content-type', 'text/html')
self.end_headers()
global GIF
GIF = "webrender-%s.gif" % (random.randrange(0,1000))
# To thread
REQ.put(req_url)
# Wait for completition
RESP.get()
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():
# Launch Proxy Thread
threading.Thread(target=run_proxy).start()
# Launch NS Application
AppKit.NSApplicationLoad();
app = AppKit.NSApplication.sharedApplication()
delegate = AppDelegate.alloc().init()
AppKit.NSApp().setDelegate_(delegate)
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()
if __name__ == '__main__' : main()

422
wrp-qt.py
View File

@@ -1,422 +0,0 @@
#!/usr/bin/env python
# Web Rendering Proxy (qt-webkit) by Antoni Sawicki - http://www.tenox.net/out/#wrp
# HTTP proxy service that renders the web page in to a JPEG image
# associated with clickable imagemap of the original web links
# This version works only with QT-Webkit (eg.: Linux, BSD, others)
#
# This program is loosely based on the following software:
# Adam Nelson webkit2png: https://github.com/adamn/python-webkit2png
# Roland Tapken: http://www.blogs.uni-osnabrueck.de/rotapken/2008/12/03/create-screenshots-of-a-web-page-using-python-and-qtwebkit/
# picidae.py from picidae.net: https://code.google.com/p/phantomjs/issues/attachmentText?id=209&aid=2090003000&name=picidae.py
# Paul Hammond webkit2png: http://www.paulhammond.org/webkit2png/
#
# Copyright (c) 2013-2014 Antoni Sawicki
# Copyright (c) 2012 picidae.net
# Copyright (c) 2008 Roland Tapken
# Copyright (c) 2004-2014 Paul Hammond
#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#
#
# Configuration options:
#
PORT = 8080
WIDTH = 0 # eg.: 640, 800, 1024, 0 for auto
HEIGHT = 0 # eg.: 480, 600, 768, 0 for auto
WAIT = 1 # sleep for 1 second to allow javascript renders
QUALITY = 80 # jpeg image quality 0-100
__version__ = "1.1qt"
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 signal
import logging
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
from PyQt4.QtNetwork import *
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
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()
image = QPixmap.grabWindow(self._window.winId())
else:
image = QPixmap.grabWidget(self._window)
# Write URL map
httpout.write("<!-- Web Rendering Proxy v%s by Antoni Sawicki -->\n<html>\n<body>\n<img src=\"http://%s\" alt=\"webrender\" usemap=\"#map\">\n<map name=\"map\">\n" % (__version__, IMG))
frame = self._view.page().currentFrame()
for x in frame.findAllElements('a'):
value = x.attribute('href')
xmin, ymin, xmax, ymax = x.geometry().getCoords()
httpout.write("<area shape=\"rect\" coords=\"%i,%i,%i,%i\" alt=\"%s\" href=\"%s\">\n" % (xmin, ymin, xmax, ymax, value, value))
httpout.write("</map>\n</body>\n</html>\n")
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)
# Request queue (URLs go in here)
REQ = Queue.Queue()
# Response queue (dummy response objects)
RESP = Queue.Queue()
#import pdb; pdb.set_trace()
class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
req_url=self.path
global httpout
httpout=self.wfile
self.send_response(200, 'OK')
jpg_re = re.compile("http://webrender-[0-9]+\.jpg")
ico_re = re.compile(".+\.ico")
if (jpg_re.search(req_url)):
img=req_url.split("/")
print ">>> request for rendered jpg image... %s [%d kb]" % (img[2], os.path.getsize(img[2])/1024)
self.send_header('Content-type', 'image/jpeg')
self.end_headers()
fimg = open(img[2])
httpout.write(fimg.read())
fimg.close()
os.remove(img[2])
elif (ico_re.search(req_url)):
print ">>> request for .ico file - skipping"
self.send_error(404, "ICO not supported")
self.end_headers()
else:
print ">>> request for url: " + req_url
self.send_header('Content-type', 'text/html')
self.end_headers()
global IMG
IMG = "webrender-%s.jpg" % (random.randrange(0,1000))
# To thread
REQ.put(req_url)
# Wait for completition
RESP.get()
def run_proxy():
httpd = SocketServer.TCPServer(('', PORT), Proxy)
print "Web Rendering Proxy v%s serving port: %s" % (__version__, PORT)
while 1:
httpd.serve_forever()
def main():
# Launch Proxy Thread
threading.Thread(target=run_proxy).start()
# 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:
rurl = REQ.get()
if rurl == "http://wrp.stop/":
print ">>> Terminate Request Received"
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(rurl)
qBuffer = QBuffer()
image.save(qBuffer, 'jpg', QUALITY)
output = open(IMG, 'w')
output.write(qBuffer.buffer().data())
output.close()
del renderer
print ">>> done: %s [%d kb]..." % (IMG, os.path.getsize(IMG)/1024)
RESP.put('')
QApplication.exit(0)
except RuntimeError, e:
logger.error("main: %s" % e)
print >> sys.stderr, e
QApplication.exit(1)
# 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_())
if __name__ == '__main__' : main()

394
wrp.go Normal file
View File

@@ -0,0 +1,394 @@
//
// WRP - Web Rendering Proxy
//
// Copyright (c) 2013-2018 Antoni Sawicki
// Copyright (c) 2019 Google LLC
//
package main
import (
"bytes"
"context"
"flag"
"fmt"
"image"
"image/gif"
"image/png"
"log"
"math"
"math/rand"
"net/http"
"net/url"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
"github.com/chromedp/cdproto/css"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/cdproto/page"
"github.com/chromedp/chromedp"
"github.com/ericpauley/go-quantize/quantize"
)
var (
version = "4.5"
srv http.Server
ctx context.Context
cancel context.CancelFunc
img = make(map[string]bytes.Buffer)
ismap = make(map[string]wrpReq)
nodel bool
deftype string
defgeom geom
)
type geom struct {
w int64
h int64
c int64
}
type wrpReq struct {
U string // url
W int64 // width
H int64 // height
S float64 // scale
C int64 // #colors
X int64 // mouseX
Y int64 // mouseY
K string // keys to send
F string // Fn buttons
T string // imgtype
}
func (w *wrpReq) parseForm(req *http.Request) {
req.ParseForm()
w.U = req.FormValue("url")
if len(w.U) > 1 && !strings.HasPrefix(w.U, "http") {
w.U = fmt.Sprintf("http://www.google.com/search?q=%s", url.QueryEscape(w.U))
}
w.W, _ = strconv.ParseInt(req.FormValue("w"), 10, 64)
w.H, _ = strconv.ParseInt(req.FormValue("h"), 10, 64)
if w.W < 10 && w.H < 10 {
w.W = defgeom.w
w.H = defgeom.h
}
w.S, _ = strconv.ParseFloat(req.FormValue("s"), 64)
if w.S < 0.1 {
w.S = 1.0
}
w.C, _ = strconv.ParseInt(req.FormValue("c"), 10, 64)
if w.C < 2 || w.C > 256 {
w.C = defgeom.c
}
w.K = req.FormValue("k")
w.F = req.FormValue("Fn")
w.T = req.FormValue("t")
if w.T != "gif" && w.T != "png" {
w.T = deftype
}
log.Printf("%s WrpReq from Form: %+v\n", req.RemoteAddr, w)
}
func (w wrpReq) printPage(out http.ResponseWriter, bgcolor string) {
var s string
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Header().Set("Content-Type", "text/html")
fmt.Fprintf(out, "<!-- Web Rendering Proxy Version %s -->\n", version)
fmt.Fprintf(out, "<HTML>\n<HEAD><TITLE>WRP %s</TITLE></HEAD>\n<BODY BGCOLOR=\"%s\">\n", w.U, bgcolor)
fmt.Fprintf(out, "<FORM ACTION=\"/\" METHOD=\"POST\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"TEXT\" NAME=\"url\" VALUE=\"%s\" SIZE=\"20\">", w.U)
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" VALUE=\"Go\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Bk\">\n")
fmt.Fprintf(out, "W <INPUT TYPE=\"TEXT\" NAME=\"w\" VALUE=\"%d\" SIZE=\"4\"> \n", w.W)
fmt.Fprintf(out, "H <INPUT TYPE=\"TEXT\" NAME=\"h\" VALUE=\"%d\" SIZE=\"4\"> \n", w.H)
fmt.Fprintf(out, "S <SELECT NAME=\"s\">\n")
for _, v := range []float64{0.65, 0.75, 0.85, 0.95, 1.0, 1.05, 1.15, 1.25} {
if v == w.S {
s = "SELECTED"
} else {
s = ""
}
fmt.Fprintf(out, "<OPTION VALUE=\"%1.2f\" %s>%1.2f</OPTION>\n", v, s, v)
}
fmt.Fprintf(out, "</SELECT>\n")
fmt.Fprintf(out, "T <SELECT NAME=\"t\">\n")
for _, v := range []string{"gif", "png"} {
if v == w.T {
s = "SELECTED"
} else {
s = ""
}
fmt.Fprintf(out, "<OPTION VALUE=\"%s\" %s>%s</OPTION>\n", v, s, strings.ToUpper(v))
}
fmt.Fprintf(out, "</SELECT>\n")
fmt.Fprintf(out, "C <INPUT TYPE=\"TEXT\" NAME=\"c\" VALUE=\"%d\" SIZE=\"3\">\n", w.C)
fmt.Fprintf(out, "K <INPUT TYPE=\"TEXT\" NAME=\"k\" VALUE=\"\" SIZE=\"4\"> \n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Bs\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"Rt\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"&lt;\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"^\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"v\">\n")
fmt.Fprintf(out, "<INPUT TYPE=\"SUBMIT\" NAME=\"Fn\" VALUE=\"&gt;\" SIZE=\"1\">\n")
fmt.Fprintf(out, "</FORM><BR>\n")
}
func (w wrpReq) printFooter(out http.ResponseWriter, h string, s string) {
fmt.Fprintf(out, "\n<P><FONT SIZE=\"-2\"><A HREF=\"/?url=https://github.com/tenox7/wrp/&w=%d&h=%d&s=%1.2f&c=%d&t=%s\">"+
"Web Rendering Proxy Version %s</A> | <A HREF=\"/shutdown/\">Shutdown WRP</A> | "+
"<A HREF=\"/\">Page Height: %s</A> | <A HREF=\"/\">Img Size: %s</A></FONT></BODY>\n</HTML>\n", w.W, w.H, w.S, w.C, w.T, version, h, s)
}
func pageServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s Page Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
var w wrpReq
w.parseForm(req)
if len(w.U) > 4 {
w.capture(req.RemoteAddr, out)
} else {
w.printPage(out, "#FFFFFF")
w.printFooter(out, "", "")
}
}
func mapServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s ISMAP Request for %s [%+v]\n", req.RemoteAddr, req.URL.Path, req.URL.RawQuery)
w, ok := ismap[req.URL.Path]
if !ok {
fmt.Fprintf(out, "Unable to find map %s\n", req.URL.Path)
log.Printf("Unable to find map %s\n", req.URL.Path)
return
}
if !nodel {
defer delete(ismap, req.URL.Path)
}
n, err := fmt.Sscanf(req.URL.RawQuery, "%d,%d", &w.X, &w.Y)
if err != nil || n != 2 {
fmt.Fprintf(out, "n=%d, err=%s\n", n, err)
log.Printf("%s ISMAP n=%d, err=%s\n", req.RemoteAddr, n, err)
return
}
log.Printf("%s WrpReq from ISMAP: %+v\n", req.RemoteAddr, w)
if len(w.U) > 4 {
w.capture(req.RemoteAddr, out)
} else {
w.printPage(out, "#FFFFFF")
w.printFooter(out, "", "")
}
}
func imgServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s IMG Request for %s\n", req.RemoteAddr, req.URL.Path)
imgbuf, ok := img[req.URL.Path]
if !ok || imgbuf.Bytes() == nil {
fmt.Fprintf(out, "Unable to find image %s\n", req.URL.Path)
log.Printf("%s Unable to find image %s\n", req.RemoteAddr, req.URL.Path)
return
}
if !nodel {
defer delete(img, req.URL.Path)
}
if strings.HasPrefix(req.URL.Path, ".gif") {
out.Header().Set("Content-Type", "image/gif")
} else if strings.HasPrefix(req.URL.Path, ".png") {
out.Header().Set("Content-Type", "image/png")
}
out.Header().Set("Content-Length", strconv.Itoa(len(imgbuf.Bytes())))
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Write(imgbuf.Bytes())
out.(http.Flusher).Flush()
}
func (w wrpReq) capture(c string, out http.ResponseWriter) {
var err error
if w.X > 0 && w.Y > 0 {
log.Printf("%s Mouse Click %d,%d\n", c, w.X, w.Y)
err = chromedp.Run(ctx, chromedp.MouseClickXY(int64(float64(w.X)/float64(w.S)), int64(float64(w.Y)/float64(w.S))))
} else if len(w.F) > 0 {
log.Printf("%s Button %v\n", c, w.F)
switch w.F {
case "Bk":
err = chromedp.Run(ctx, chromedp.NavigateBack())
case "Bs":
err = chromedp.Run(ctx, chromedp.KeyEvent("\b"))
case "Rt":
err = chromedp.Run(ctx, chromedp.KeyEvent("\r"))
case "<":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0302"))
case "^":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0304"))
case "v":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0301"))
case ">":
err = chromedp.Run(ctx, chromedp.KeyEvent("\u0303"))
}
} else if len(w.K) > 0 {
log.Printf("%s Sending Keys: %#v\n", c, w.K)
err = chromedp.Run(ctx, chromedp.KeyEvent(w.K))
} else {
log.Printf("%s Processing Capture Request for %s\n", c, w.U)
err = chromedp.Run(ctx, chromedp.Navigate(w.U))
}
if err != nil {
if err.Error() == "context canceled" {
log.Printf("%s Contex cancelled, try again", c)
fmt.Fprintf(out, "<BR>%s<BR> -- restarting, try again", err)
ctx, cancel = chromedp.NewContext(context.Background())
} else {
log.Printf("%s %s", c, err)
fmt.Fprintf(out, "<BR>%s<BR>", err)
}
return
}
var styles []*css.ComputedProperty
var r, g, b int
var h int64
var pngcap []byte
chromedp.Run(ctx,
emulation.SetDeviceMetricsOverride(int64(float64(w.W)/w.S), 10, w.S, false),
chromedp.Sleep(time.Second*2),
chromedp.Location(&w.U),
chromedp.ComputedStyle("body", &styles, chromedp.ByQuery),
chromedp.ActionFunc(func(ctx context.Context) error {
_, _, s, err := page.GetLayoutMetrics().Do(ctx)
if err == nil {
h = int64(math.Ceil(s.Height))
}
return nil
}),
)
for _, style := range styles {
if style.Name == "background-color" {
fmt.Sscanf(style.Value, "rgb(%d,%d,%d)", &r, &g, &b)
}
}
log.Printf("%s Landed on: %s, Height: %v\n", c, w.U, h)
w.printPage(out, fmt.Sprintf("#%02X%02X%02X", r, g, b))
if w.H == 0 && h > 0 {
chromedp.Run(ctx, emulation.SetDeviceMetricsOverride(int64(float64(w.W)/w.S), h+30, w.S, false))
} else {
chromedp.Run(ctx, emulation.SetDeviceMetricsOverride(int64(float64(w.W)/w.S), int64(float64(w.H)/w.S), w.S, false))
}
err = chromedp.Run(ctx, chromedp.CaptureScreenshot(&pngcap))
if err != nil {
log.Printf("%s Failed to capture screenshot: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to capture screenshot:<BR>%s<BR>\n", err)
return
}
seq := rand.Intn(9999)
imgpath := fmt.Sprintf("/img/%04d.%s", seq, w.T)
mappath := fmt.Sprintf("/map/%04d.map", seq)
ismap[mappath] = w
var ssize string
var sw, sh int
if w.T == "gif" {
i, err := png.Decode(bytes.NewReader(pngcap))
if err != nil {
log.Printf("%s Failed to decode screenshot: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to decode page screenshot:<BR>%s<BR>\n", err)
return
}
var gifbuf bytes.Buffer
err = gif.Encode(&gifbuf, i, &gif.Options{NumColors: int(w.C), Quantizer: quantize.MedianCutQuantizer{}})
if err != nil {
log.Printf("%s Failed to encode GIF: %s\n", c, err)
fmt.Fprintf(out, "<BR>Unable to encode GIF:<BR>%s<BR>\n", err)
return
}
img[imgpath] = gifbuf
ssize = fmt.Sprintf("%.1f MB", float32(len(gifbuf.Bytes()))/1024.0/1024.0)
sw = i.Bounds().Max.X
sh = i.Bounds().Max.Y
log.Printf("%s Encoded GIF image: %s, Size: %s, Colors: %d, %dx%d\n", c, imgpath, ssize, w.C, sw, sh)
} else if w.T == "png" {
pngbuf := bytes.NewBuffer(pngcap)
img[imgpath] = *pngbuf
cfg, _, _ := image.DecodeConfig(pngbuf)
ssize = fmt.Sprintf("%.1f MB", float32(len(pngbuf.Bytes()))/1024.0/1024.0)
sw = cfg.Width
sh = cfg.Height
log.Printf("%s Got PNG image: %s, Size: %s, %dx%d\n", c, imgpath, ssize, sw, sh)
}
fmt.Fprintf(out, "<A HREF=\"%s\"><IMG SRC=\"%s\" BORDER=\"0\" ALT=\"Url: %s, Size: %s\" WIDTH=\"%d\" HEIGHT=\"%d\" ISMAP></A>", mappath, imgpath, w.U, ssize, sw, sh)
w.printFooter(out, fmt.Sprintf("%d PX", h), ssize)
log.Printf("%s Done with caputure for %s\n", c, w.U)
}
func haltServer(out http.ResponseWriter, req *http.Request) {
log.Printf("%s Shutdown Request for %s\n", req.RemoteAddr, req.URL.Path)
out.Header().Set("Cache-Control", "max-age=0")
out.Header().Set("Expires", "-1")
out.Header().Set("Pragma", "no-cache")
out.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(out, "Shutting down WRP...\n")
out.(http.Flusher).Flush()
time.Sleep(time.Second * 2)
cancel()
srv.Shutdown(context.Background())
os.Exit(1)
}
func main() {
var addr, fgeom string
var head, headless bool
var debug bool
var err error
flag.StringVar(&addr, "l", ":8080", "Listen address:port, default :8080")
flag.BoolVar(&head, "h", false, "Headed mode - display browser window")
flag.BoolVar(&debug, "d", false, "Debug ChromeDP")
flag.BoolVar(&nodel, "n", false, "Do not free maps and images after use")
flag.StringVar(&deftype, "t", "gif", "Image type: gif|png")
flag.StringVar(&fgeom, "g", "1152x600x256", "Geometry: width x height x colors, height can be 0 for unlimited")
flag.Parse()
if head {
headless = false
} else {
headless = true
}
n, err := fmt.Sscanf(fgeom, "%dx%dx%d", &defgeom.w, &defgeom.h, &defgeom.c)
if err != nil || n != 3 {
log.Fatalf("Unable to parse -g geometry flag / %s", err)
}
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", headless),
chromedp.Flag("hide-scrollbars", false),
)
actx, acancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer acancel()
if debug {
ctx, cancel = chromedp.NewContext(actx, chromedp.WithDebugf(log.Printf))
} else {
ctx, cancel = chromedp.NewContext(actx)
}
defer cancel()
rand.Seed(time.Now().UnixNano())
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
log.Printf("Interrupt - shutting down.")
cancel()
srv.Shutdown(context.Background())
os.Exit(1)
}()
http.HandleFunc("/", pageServer)
http.HandleFunc("/map/", mapServer)
http.HandleFunc("/img/", imgServer)
http.HandleFunc("/shutdown/", haltServer)
http.HandleFunc("/favicon.ico", http.NotFound)
log.Printf("Web Rendering Proxy Version %s\n", version)
log.Printf("Starting WRP http server on %s\n", addr)
srv.Addr = addr
err = srv.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}

BIN
wrp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB