websnap 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright (C) 2010 Francis Chong
2
+
3
+ GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
4
+ This is free software: you are free to change and redistribute it. There is NO
5
+ WARRANTY, to the extent permitted by law.
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ Manifest
3
+ README.md
4
+ Rakefile
5
+ bin/wkhtmltoimage-linux-i386-0.10.0
6
+ bin/wkhtmltoimage-proxy
7
+ lib/websnap.rb
8
+ lib/websnap/source.rb
9
+ lib/websnap/websnap.rb
10
+ spec/fixtures/google.html
11
+ spec/source_spec.rb
12
+ spec/spec_helper.rb
13
+ spec/websnap_spec.rb
14
+ websnap.gemspec
@@ -0,0 +1,45 @@
1
+ # WebSnap
2
+
3
+ Create snapshot of webpage. Uses [wkhtmltoimage](http://github.com/antialize/wkhtmltopdf) on the backend which renders HTML using Webkit.
4
+
5
+ ## Installation
6
+
7
+ 1. Install wkhtmltoimage (optional)
8
+ ** WebSnap comes bundled with wkhtmltoimage binaries for Linux i386 (OSX i386 will be bundled, when it is available from [wkhtmltopdf] site)
9
+ ** WebSnap defaults to user installed versions of wkhtmltoimage if found
10
+ ** Installing wkhtmltoimage binary
11
+ *** Download the latest binary from http://code.google.com/p/wkhtmltopdf/downloads/list
12
+ *** Place the binary somewhere on your path (e.g /usr/local/bin)
13
+ 2. Install WebSnap
14
+
15
+ $ gem install websnap
16
+
17
+ ## Usage
18
+
19
+ # WebSnap.new takes the HTML and any options for wkhtmltoimage
20
+ # run `wkhtmltoimage -H` for a full list of options
21
+ snap = WebSnap.new('http://google.com', :format => 'png')
22
+
23
+ # Get the binary image data
24
+ png = snap.bytes
25
+
26
+ # Save the PDF to a file
27
+ file = snap.to_file('/path/to/save/png')
28
+
29
+ # WebSnap.new can optionally accept a URL or a File.
30
+ kit = WebSnap.new('http://google.com')
31
+ kit = WebSnap.new(File.new('/path/to/html'))
32
+
33
+ ## Note on Patches/Pull Requests
34
+
35
+ * Fork the project.
36
+ * Make your feature addition or bug fix.
37
+ * Add tests for it. This is important so I don't break it in a
38
+ future version unintentionally.
39
+ * Commit, do not mess with rakefile, version, or history.
40
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
41
+ * Send me a pull request. Bonus points for topic branches.
42
+
43
+ ## Copyright
44
+
45
+ Copyright (c) 2010 Francis Chong. See LICENSE for details.
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'echoe'
3
+
4
+ require 'rake'
5
+ require 'spec/rake/spectask'
6
+
7
+ Echoe.new("websnap", "0.1.1") do |p|
8
+ p.author = "Francis Chong"
9
+ p.description = "Create snapshot of webpage"
10
+ p.url = "http://github.com/siuying/websnap"
11
+ p.ignore_pattern = []
12
+ p.development_dependencies = ["rspec", "echoe", "mocha"]
13
+ p.runtime_dependencies = []
14
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ executable = `which wkhtmltoimage`.chomp
4
+
5
+ if executable.empty?
6
+
7
+ binary = case RUBY_PLATFORM
8
+ when /linux/
9
+ 'wkhtmltoimage-linux-i386-0.10.0'
10
+ when /darwin/
11
+ 'wkhtmltoimage-osx-i386-0.10.0'
12
+ else
13
+ raise "No bundled binaries found for your system. Please install wkhtmltopdf."
14
+ end
15
+ executable = File.join(File.dirname(__FILE__), binary)
16
+
17
+ end
18
+
19
+ arguments = $*.map { |arg| arg.include?(' ') ? %Q("#{arg}") : arg }
20
+
21
+ system(executable + " " + arguments.join(" "))
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'websnap/websnap'
3
+ require 'websnap/source'
@@ -0,0 +1,27 @@
1
+ class WebSnap
2
+
3
+ class Source
4
+
5
+ def initialize(url_file_or_html)
6
+ @source = url_file_or_html
7
+ end
8
+
9
+ def url?
10
+ @source.is_a?(String) && @source.match(/^http/)
11
+ end
12
+
13
+ def file?
14
+ @source.kind_of?(File)
15
+ end
16
+
17
+ def html?
18
+ !(url? || file?)
19
+ end
20
+
21
+ def to_s
22
+ file? ? @source.path : @source
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,95 @@
1
+ class WebSnap
2
+
3
+ class NoExecutableError < StandardError
4
+ def initialize
5
+ super('Could not locate wkhtmltoimage-proxy executable')
6
+ end
7
+ end
8
+
9
+ class ImproperSourceError < StandardError
10
+ def initialize(msg)
11
+ super("Improper Source: #{msg}")
12
+ end
13
+ end
14
+
15
+ attr_accessor :source, :stylesheets
16
+ attr_reader :options
17
+
18
+ def initialize(url_file_or_html, options={})
19
+ @source = Source.new(url_file_or_html)
20
+
21
+ @stylesheets = []
22
+
23
+ default_options = {
24
+ :'scale-h' => '90',
25
+ :'scale-w' => '120',
26
+ :'crop-h' => '768',
27
+ :'crop-w' => '1024',
28
+ :'crop-x' => '0',
29
+ :'crop-y' => '0',
30
+ :'format' => 'png'
31
+ }
32
+ @options = normalize_options(default_options.merge(options))
33
+
34
+ raise NoExecutableError.new if wkhtmltoimage.nil? || wkhtmltoimage == ''
35
+ end
36
+
37
+ def command
38
+ args = [wkhtmltoimage]
39
+ args += @options.to_a.flatten.compact
40
+
41
+ if @source.html?
42
+ args << '-' # Get HTML from stdin
43
+ else
44
+ args << @source.to_s
45
+ end
46
+
47
+ args << '-' # Read PDF from stdout
48
+ args.join(' ')
49
+ end
50
+
51
+ def to_bytes
52
+ img = IO.popen(command, "w+")
53
+ img.puts(@source.to_s) if @source.html?
54
+ img.close_write
55
+ result = img.gets(nil)
56
+ img.close_read
57
+ return result
58
+ end
59
+
60
+ def to_file(path)
61
+ File.open(path,'w') {|file| file << self.to_bytes}
62
+ end
63
+
64
+ protected
65
+
66
+ def wkhtmltoimage
67
+ @wkhtmltoimage ||= `which wkhtmltoimage-proxy`.chomp
68
+ end
69
+
70
+ def normalize_options(options)
71
+ normalized_options = {}
72
+ options.each do |key, value|
73
+ next if !value
74
+ normalized_key = "--#{normalize_arg key}"
75
+ normalized_options[normalized_key] = normalize_value(value)
76
+ end
77
+ normalized_options
78
+ end
79
+
80
+ def normalize_arg(arg)
81
+ arg.to_s.downcase.gsub(/[^a-z0-9]/,'-')
82
+ end
83
+
84
+ def normalize_value(value)
85
+ case value
86
+ when TrueClass
87
+ nil
88
+ when String
89
+ value.match(/\s/) ? "\"#{value}\"" : value
90
+ else
91
+ value
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,265 @@
1
+ <!doctype html><html><head><meta content="text/html; charset=Big5" http-equiv="content-type" /><title>Google</title><script>window.google={kEI:"RVsnTITaHIqgkQXPkNyGAw",kEXPI:"24483,25013,25164,25233,25282,25299,25335,25368",kCSI:{e:"24483,25013,25164,25233,25282,25299,25335,25368",ei:"RVsnTITaHIqgkQXPkNyGAw",expi:"24483,25013,25164,25233,25282,25299,25335,25368"},ml:function(){},kHL:"zh-TW",time:function(){return(new Date).getTime()},log:function(b,d,c){var a=new Image,e=google,g=e.lc,f=e.li;a.onerror=(a.onload=(a.onabort=function(){delete g[f]}));g[f]=a;c=c||"/gen_204?atyp=i&ct="+b+"&cad="+d+"&zx="+google.time();a.src=c;e.li=f+1},lc:[],li:0,Toolbelt:{}};
2
+ window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{}catch(u){}window.google.jsrt_kill=1;
3
+ var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
4
+ window._gjuc())&&setTimeout(_gjp,500)};
5
+ window._gjp && _gjp()</script><style>body{margin:0}#gog{padding:3px 8px 0}td{line-height:.8em;}.gac_m td{line-height:17px;}form{margin-bottom:20px;}body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{color:#c03;font-style:normal;font-weight:normal}a em{text-decoration:underline}.lst{width:496px}.tiah{width:458px}input{font-family:inherit}a.gb1,a.gb2,a.gb3,a.gb4{color:#11c !important}#gbar,#guser{font-size:13px;padding-top:1px !important}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{color:#00c !important}body{background:#fff;color:black}input{-moz-box-sizing:content-box}a{color:#11c;text-decoration:none}a:hover,a:active{text-decoration:underline}.fl a{color:#4272db}a:visited{color:#551a8b}a.gb1,a.gb4{text-decoration:underline}a.gb3:hover{text-decoration:none}#ghead a.gb2:hover{color:#fff!important}.ds{display:-moz-inline-box}.ds{border-bottom:solid 1px #e7e7e7;border-right:solid 1px #e7e7e7;display:inline-block;margin:3px 0 4px;margin-left:4px;margin-right:3px:height:32px}.sblc{padding-top:5px}.sblc a{display:block;margin:2px 0;margin-left:13px;font-size:11px;}.lsbb{background:#eee;border:solid 1px;border-color:#ccc #999 #999 #ccc;height:30px;display:block}.lsb{background:url(/images/srpr/nav_logo13.png) bottom;font:15px arial,sans-serif;border:none;color:#000;cursor:pointer;height:30px;margin:0;outline:0;vertical-align:top}.lsb:active{background:#ccc}.lst:focus{outline:none}.ftl,#fll a{margin:0 12px}#addlang a{padding:0 3px}.gac_v div{display:none}.gac_v .gac_v2,.gac_bt{display:block!important}</style><script>google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};</script></head><body text="#000000" vlink="#551a8b" onload="document.f.q.focus();if(document.images)new Image().src='/images/srpr/nav_logo13.png'" alink="#ff0000" link="#0000cc" bgcolor="#ffffff"><textarea id="csi" style="display:none"></textarea><div id="ghead"><div id="gbar"><nobr /><b class="gb1">�Ҧ�����</b> <a href="http://www.google.com.hk/imghp?hl=zh-TW&tab=wi" class="gb1" onclick="gbar.qs(this)">�Ϥ�</a> <a href="http://video.google.com.hk/?hl=zh-TW&tab=wv" class="gb1" onclick="gbar.qs(this)">�v��</a> <a href="http://maps.google.com.hk/maps?hl=zh-TW&tab=wl" class="gb1" onclick="gbar.qs(this)">�a��</a> <a href="http://news.google.com.hk/nwshp?hl=zh-TW&tab=wn" class="gb1" onclick="gbar.qs(this)">�s�D</a> <a href="http://www.google.com.hk/finance?hl=zh-TW&tab=we" class="gb1" onclick="gbar.qs(this)">�]�g</a> <a href="http://mail.google.com/mail/?hl=zh-TW&tab=wm" class="gb1">Gmail</a> <a href="http://www.google.com.hk/intl/zh-TW/options/" class="gb1" style="text-decoration:none"><u>��h</u> &raquo;</a></div><div id="guser" width="100%"><nobr /><span class="gbi" id="gbn"></span><span class="gbf" id="gbf"></span><span id="gbe"><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com.hk/ig%3Fhl%3Dzh-TW%26source%3Diglk&usg=AFQjCNHPiYicHSGMua-uVhGTjPT-hWpWXA" class="gb4">iGoogle</a> | </span><a href="/preferences?hl=zh-TW" class="gb4">�]�w</a> | <a href="https://www.google.com/accounts/Login?hl=zh-TW&continue=http://www.google.com.hk/" class="gb4">�n�J</a></div><div class="gbh" style="left:0"></div><div class="gbh" style="right:0"></div></div> <center><br clear="all" id="lgpd" /><div id="lga"><table cellspacing="0" border="0" cellpadding="0"><tr><td align="right" valign="bottom"><img src="data:image/gif;base64,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
100
+ ADs=
101
+ " height="78" alt="Google" width="158" /></td><td valign="bottom"><img src="data:image/gif;base64,R0lGODdhMgBOAHcAACwAAAAAMgBOAIf///+9473v9++c06U5ukIxtjmMy4z3
102
+ 9/f38+/v6+/38/fv8/fW4//G1/+tx/elvu+EqveEmtZzjs6lttb39/9aiu9K
103
+ fedjlu85cd4xZdYhWdYYTcYYSbUIPKUQNIQQPJSUnrXv7+//+//n6/9zou8Y
104
+ Ra17hq3n4+f3+/9Sba05YbVjeaXGx8737+/n5+fe5//Gy9bv5+fn497W09al
105
+ rr3e29bOz87Oy87e297Gx8a9vr0QRbW1srW9tr29ur3W19be3+fv6+fGw8bG
106
+ vr21trWEhpTe394pUbWtrq2lpq1KYZTOw6UYUc6ljjEIJGOMkpS1usaUeQCc
107
+ mpyloqVagtYpQWuchkKUkoytqq2Urt6lqq2EaQDO19a1pozO0+dzeYylhnvO
108
+ 09b/9/eEdTn///daaXtKVXO1sq21w95jUQClpqW9z+/W2+ecnpw5WZStpq17
109
+ ZVLW186EkrWEfXMpSZStqqVraWvn5+/Ox8be19bW2961tr3e797O687W197O
110
+ z9b39+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
111
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
112
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
113
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
114
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
115
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
116
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
117
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
118
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI/wABCBxIsKDBgwgTKlzIsKHD
119
+ hxAjSpxIsaLFixgzatzIsaPHjyArBgjZUMAAAgVCNgxgwMCAkCFBHkAQ8uGB
120
+ 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
166
+ OHDgmGFDSI82R0pw4MChxAcPdGzcOSBCYEAAOw==
167
+ " height="78" alt="" width="50" /></td><td valign="bottom"><img src="data:image/gif;base64,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
227
+ ADs=
228
+ " height="78" alt="" width="68" /></td></tr><tr><td class="h" align="right" valign="top"><b></b></td><td valign="top"><img src="data:image/gif;base64,R0lGODdhMgAgAGYAACwAAAAAMgAgAIb///+Eqvdjlu9aiu85YbUQPJQIJGOl
229
+ pq3Gvr21srWtqq29ur3Oy87W19bW297e397e3+fv6+/v7+/n4+fW09a9vr0p
230
+ UbUQRbUYTcYYRa0QNISlrr338+//+//G1/9zou85cd4pSZRaaXutpq3Gx8b3
231
+ 9/f38/eUnrUYSbWMkpTn5+dKfeecnpytrq3e297Ox8Z7hq0YUc6EhpTO0+cp
232
+ QWucmpzOz873+/8xZdZzeYzGw8alvu+loqXe29ZagtYhWdb/9/+Urt6Emta1
233
+ trX/9/dKVXOlpqXv7/e9tr29z+9jeaWlttb39+8IPKWEkrXW4/+1w95zjs7e
234
+ 5/////etx/fe4+elqq3W1845WZRKYZS1usbv6+fGx86tqqVSba2Ukoy1sq0A
235
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
236
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
237
+ AAAH/4AAgoIBAgIDBAUGBwgJCgkJCwwNDg8PEBEREhISERMUDBUHFhcYFxkF
238
+ GhobEBwdg4MeAh8CICEGIgsjIwkLJBQPExElJiUdgwAlJREPDAgnGBgoGQUa
239
+ BikNKiUdg4IBHx8rFhoGKSMsLRUMLhESJR2Dg4MAJRIqFC8wMRgoBRqABjIM
240
+ LhIlHQAAADMfAQIgGRo0NTUtCzYuEiUdgACCg4SDNyUqLhU4MRgFGgY5OhQq
241
+ JYQ7Hx8DOAUGNDU8CQwuEh2FhYWCHBE9Pj8YBRoGMhUMKkCEQR8fKxYFBjQ1
242
+ CjoNKiaFhYWDNyURQj8YBRoGKUMMEyUdgiUBHwIgFgUaNP8sCQwTJh0dhB2F
243
+ hYMlREE4GAUGNCwJDBMlHYOAQgIDOBgFGho1CzYqJSWAAIKDhIWGQTgWGgZF
244
+ IwkMEyWEQisgOBgZBRo5OhQqJYaGhoRCOBkaBjJGCQwTJYIdAEE4OD8YGQUa
245
+ IRUNKiUdhoaGgkcrPwUGRSwsSAwqQIIdAEk/MTEYFxkFBUo9KiWGhoaDSzgW
246
+ GgY1PEYVDSolHYNMPhgYKCgoGRlNThEdgACCg4SFhU8gFgUGMgojCiQQHB2F
247
+ UCgoKBcoGRkZGVERHYaGhlIrGAUGOQsjI0gUKiYdhCYcTk2AGYKCKCgoBFBT
248
+ gACCg4SFAFQgGAUaMAwtPAkMLhL/JR2FEg9KTU1NGRkZGSgXGCBBVUyGhCZU
249
+ Az8YBSFWV0gJLRUUKiYdhiYqFDCABYKCGRkoKBgxPyBRQUtJHh5JOwEDIDEY
250
+ TQVKFC4ICQkLNhAcHYAAgoMAJREuLyxYGhoaBQUZGSgYgDGCg4QYGChNBVla
251
+ Ww0ICwsIDC4SHYAAgoODJSoUOgkyWQUaBQUZGSiAGIKDKCgoGU0FITBcKhMM
252
+ FQhIOgwuEh2AAIKDhAAcEw06SF0pOVkhIRkZKCgoKE0ZGRYWXk5aDSo9NjoI
253
+ Cws6Ng8qJR2FhYIcEw06SF1GBzVfKTIwMDBOTicbWiQ2DRQMDAgLCwgVOg0q
254
+ EiUdhYWE5yYqFAwVYApGPDxGPF0tQwlICwsVFUgLCxUVSDoMFC4qJSWFhYWC
255
+ JYAqExQMCAsJIwojCiMjViMJCQkLQwsLOjoMFD0TEiUdgACCg4SFhCURERMQ
256
+ DQwMCBVIYENDQ0gLCxU6DBQULi4qESUdhoaGhSUlERETEw0NgBSCg4INLi4P
257
+ KhElJYAAgoOEhYaHACUcEhISEhETEREqEREREhwcJSUlgx2IiIiGJR0lJSWI
258
+ iIiIiIiIiIiIhB0AHYIdAB2IiIiIiIiIiIiIAB0AHYiIiIiIiIiIiIiCHQAd
259
+ gh0AHYiIiIiIiIiEgQA7
260
+ " height="32" alt="" width="50" /></td><td class="h" valign="top"><font color="#666666" style="font-size:16px"><b>����</b></font></td></tr></table><br /></div><form name="f" action="/search"><table cellspacing="0" cellpadding="0"><tr valign="top"><td width="25%">&nbsp;</td><td nowrap align="center"><input name="hl" value="zh-TW" type="hidden" /><input name="source" value="hp" type="hidden" /><input name="ie" value="Big5" type="hidden" /><div class="ds" style="height:32px;margin:4px 0"><input name="q" size="57" maxlength="2048" title="Google �j�M" class="lst" value="" style="background:#fff;border:1px solid #ccc;border-bottom-color:#999;border-right-color:#999;color:#000;font:18px arial,sans-serif bold;height:25px;margin:0;padding:5px 8px 0 6px;vertical-align:top" autocomplete="off" /></div><br style="line-height:0" /><span class="ds"><span class="lsbb"><input name="btnG" class="lsb" value="Google �j�M" type="submit" /></span></span><span class="ds"><span class="lsbb"><input name="btnI" class="lsb" value="�n���" type="submit" /></span></span></td><td class="sblc" nowrap align="left" width="25%"><a href="/advanced_search?hl=zh-TW">�i���j�M</a><a href="/language_tools?hl=zh-TW">�y���ﶵ</a></td></tr></table></form><div style="font-size:83%;min-height:3.5em"><br /><div id="als"><font size="-1" id="addlang">Google.com.hk �ٴ��ѡG <a href="http://www.google.com.hk/setprefs?sig=0_UkHZK4ufPhWK5HQLOFa-oO1q8bU=&amp;hl=zh-CN">����(&#31616;�^)</a> <a href="http://www.google.com.hk/setprefs?sig=0_UkHZK4ufPhWK5HQLOFa-oO1q8bU=&amp;hl=en">English</a></font><br /><br /></div></div><div id="res"></div><span id="footer" /><center id="fctr"><div style="font-size:10pt"><div id="fll" style="margin:19px auto 19px auto;text-align:center"><a href="/intl/zh-TW/ads/">�s�i�A��</a><a href="/intl/zh-TW/about.html">Google ������U</a><a href="http://www.google.com/ncr">Google.com in English</a></div></div><p style="color:#767676;font-size:8pt">&copy; 2010 - <a href="/intl/zh-TW/privacy.html">���p�v�F��</a></p></center> <div id="xjsd"></div><div id="xjsi"><script>if(google.y)google.y.first=[];if(google.y)google.y.first=[];google.dstr=[];google.rein=[];window.setTimeout(function(){var a=document.createElement("script");a.src="/extern_js/f/CgV6aC1UVxICaGsgDSswRTgBLCswWjgBLCswDjgRLCswFzgHLCswJzgELCswPDgDLCswCjhtQAIsKzAlOMqIASwrMEA4EiwrMEE4BSwrMBg4BSwrMCY4CyyAAhM/ct5vAm6hZds.js";(document.getElementById("xjsd")||document.body).appendChild(a);if(google.timers&&google.timers.load.t)google.timers.load.t.xjsls=(new Date).getTime();},0);
261
+ ;google.neegg=1;google.y.first.push(function(){google.ac.i(document.f,document.f.q,'','','',{o:1,sw:1});google.History&&google.History.initialize('/')});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);google.fade=null;}</script></div><script>(function(){
262
+ var b,d,e,f;function g(a,c){if(a.removeEventListener){a.removeEventListener("load",c,false);a.removeEventListener("error",c,false)}else{a.detachEvent("onload",c);a.detachEvent("onerror",c)}}function h(a){f=(new Date).getTime();++d;a=a||window.event;var c=a.target||a.srcElement;g(c,h)}var i=document.getElementsByTagName("img");b=i.length;d=0;for(var j=0,k;j<b;++j){k=i[j];if(k.complete||typeof k.src!="string"||!k.src)++d;else if(k.addEventListener){k.addEventListener("load",h,false);k.addEventListener("error",
263
+ h,false)}else{k.attachEvent("onload",h);k.attachEvent("onerror",h)}}e=b-d;function l(){google.timers.load.t.ol=(new Date).getTime();google.timers.load.t.iml=f;google.kCSI.imc=d;google.kCSI.imn=b;google.kCSI.imp=e;google.report&&google.report(google.timers.load,google.kCSI)}if(window.addEventListener)window.addEventListener("load",l,false);else if(window.attachEvent)window.attachEvent("onload",l);google.timers.load.t.prt=(f=(new Date).getTime());
264
+ })();
265
+ </script></center></body></html>
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebSnap::Source do
4
+
5
+ describe "#url?" do
6
+ it "should return true if passed a url like string" do
7
+ source = WebSnap::Source.new('http://google.com')
8
+ source.should be_url
9
+ end
10
+
11
+ it "should return false if passed a file" do
12
+ source = WebSnap::Source.new(File.new(__FILE__))
13
+ source.should_not be_url
14
+ end
15
+
16
+ it "should return false if passed HTML" do
17
+ source = WebSnap::Source.new('<blink>Oh Hai!</blink>')
18
+ source.should_not be_url
19
+ end
20
+ end
21
+
22
+ describe "#file?" do
23
+ it "should return true if passed a file" do
24
+ source = WebSnap::Source.new(File.new(__FILE__))
25
+ source.should be_file
26
+ end
27
+
28
+ it "should return false if passed a url like string" do
29
+ source = WebSnap::Source.new('http://google.com')
30
+ source.should_not be_file
31
+ end
32
+
33
+ it "should return false if passed HTML" do
34
+ source = WebSnap::Source.new('<blink>Oh Hai!</blink>')
35
+ source.should_not be_file
36
+ end
37
+ end
38
+
39
+ describe "#html?" do
40
+ it "should return true if passed HTML" do
41
+ source = WebSnap::Source.new('<blink>Oh Hai!</blink>')
42
+ source.should be_html
43
+ end
44
+
45
+ it "should return false if passed a file" do
46
+ source = WebSnap::Source.new(File.new(__FILE__))
47
+ source.should_not be_html
48
+ end
49
+
50
+ it "should return false if passed a url like string" do
51
+ source = WebSnap::Source.new('http://google.com')
52
+ source.should_not be_html
53
+ end
54
+ end
55
+
56
+ describe "#to_s" do
57
+ it "should return the HTML if passed HTML" do
58
+ source = WebSnap::Source.new('<blink>Oh Hai!</blink>')
59
+ source.to_s.should == '<blink>Oh Hai!</blink>'
60
+ end
61
+
62
+ it "should return a path if passed a file" do
63
+ source = WebSnap::Source.new(File.new(__FILE__))
64
+ source.to_s.should == __FILE__
65
+ end
66
+
67
+ it "should return the url if passed a url like string" do
68
+ source = WebSnap::Source.new('http://google.com')
69
+ source.to_s.should == 'http://google.com'
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,8 @@
1
+ SPEC_ROOT = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(SPEC_ROOT)
3
+ $LOAD_PATH.unshift(File.join(SPEC_ROOT, '..', 'lib'))
4
+
5
+ require 'websnap'
6
+ require 'rubygems'
7
+ require 'spec/rake/spectask'
8
+ require 'mocha'
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe WebSnap do
4
+
5
+ context "initialization" do
6
+ it "should accept HTML as the source" do
7
+ websnap = WebSnap.new('<h1>Oh Hai</h1>')
8
+ websnap.source.should be_html
9
+ websnap.source.to_s.should == '<h1>Oh Hai</h1>'
10
+ end
11
+
12
+ it "should accept a URL as the source" do
13
+ websnap = WebSnap.new('http://google.com')
14
+ websnap.source.should be_url
15
+ websnap.source.to_s.should == 'http://google.com'
16
+ end
17
+
18
+ it "should accept a File as the source" do
19
+ file_path = File.join(SPEC_ROOT,'fixtures','google.html')
20
+ websnap = WebSnap.new(File.new(file_path))
21
+ websnap.source.should be_file
22
+ websnap.source.to_s.should == file_path
23
+ end
24
+
25
+ it "should parse the options into a cmd line friedly format" do
26
+ websnap = WebSnap.new('html', :'crop-w' => '800', :'crop-h' => '600')
27
+ websnap.options.should have_key('--crop-w')
28
+ end
29
+
30
+ it "should provide default options" do
31
+ websnap = WebSnap.new('<h1>Oh Hai</h1>')
32
+ ['--crop-w', '--crop-h', '--scale-w', '--scale-h', '--format'].each do |option|
33
+ websnap.options.should have_key(option)
34
+ end
35
+ end
36
+ end
37
+
38
+ context "command" do
39
+ it "should contstruct the correct command" do
40
+ websnap = WebSnap.new('html', :'encoding' => 'Big5')
41
+ websnap.command.should include('--encoding Big5')
42
+ end
43
+
44
+ it "read the source from stdin if it is html" do
45
+ websnap = WebSnap.new('html')
46
+ websnap.command.should match(/ - -$/)
47
+ end
48
+
49
+ it "specify the URL to the source if it is a url" do
50
+ websnap = WebSnap.new('http://google.com')
51
+ websnap.command.should match(/ http:\/\/google\.com -$/)
52
+ end
53
+
54
+ it "should specify the path to the source if it is a file" do
55
+ file_path = File.join(SPEC_ROOT,'fixtures','google.html')
56
+ websnap = WebSnap.new(File.new(file_path))
57
+ websnap.command.should match(/ #{file_path} -$/)
58
+ end
59
+ end
60
+
61
+ context "#to_bytes" do
62
+ it "should generate a PDF of the HTML" do
63
+ websnap = WebSnap.new('html')
64
+ websnap.expects(:to_bytes).returns('PNG')
65
+ png = websnap.to_bytes
66
+ png.should match(/PNG/)
67
+ end
68
+ end
69
+
70
+ context "#to_file" do
71
+ before do
72
+ @file_path = File.join(SPEC_ROOT,'fixtures','test.png')
73
+ File.delete(@file_path) if File.exist?(@file_path)
74
+ end
75
+
76
+ after do
77
+ File.delete(@file_path)
78
+ end
79
+
80
+ it "should create a file with the PNG as content" do
81
+ websnap = WebSnap.new('html')
82
+ websnap.expects(:to_bytes).returns('PNG')
83
+
84
+ file = websnap.to_file(@file_path)
85
+ file.should be_instance_of(File)
86
+ File.read(file.path).should == 'PNG'
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{websnap}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Francis Chong"]
9
+ s.date = %q{2010-06-27}
10
+ s.description = %q{Create snapshot of webpage}
11
+ s.email = %q{}
12
+ s.executables = ["wkhtmltoimage-linux-i386-0.10.0", "wkhtmltoimage-proxy"]
13
+ s.extra_rdoc_files = ["LICENSE", "README.md", "bin/wkhtmltoimage-linux-i386-0.10.0", "bin/wkhtmltoimage-proxy", "lib/websnap.rb", "lib/websnap/source.rb", "lib/websnap/websnap.rb"]
14
+ s.files = ["LICENSE", "Manifest", "README.md", "Rakefile", "bin/wkhtmltoimage-linux-i386-0.10.0", "bin/wkhtmltoimage-proxy", "lib/websnap.rb", "lib/websnap/source.rb", "lib/websnap/websnap.rb", "spec/fixtures/google.html", "spec/source_spec.rb", "spec/spec_helper.rb", "spec/websnap_spec.rb", "websnap.gemspec"]
15
+ s.homepage = %q{http://github.com/siuying/websnap}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Websnap", "--main", "README.md"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{websnap}
19
+ s.rubygems_version = %q{1.3.6}
20
+ s.summary = %q{Create snapshot of webpage}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 3
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_development_dependency(%q<rspec>, [">= 0"])
28
+ s.add_development_dependency(%q<echoe>, [">= 0"])
29
+ s.add_development_dependency(%q<mocha>, [">= 0"])
30
+ else
31
+ s.add_dependency(%q<rspec>, [">= 0"])
32
+ s.add_dependency(%q<echoe>, [">= 0"])
33
+ s.add_dependency(%q<mocha>, [">= 0"])
34
+ end
35
+ else
36
+ s.add_dependency(%q<rspec>, [">= 0"])
37
+ s.add_dependency(%q<echoe>, [">= 0"])
38
+ s.add_dependency(%q<mocha>, [">= 0"])
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: websnap
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Francis Chong
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-27 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: echoe
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 0
41
+ version: "0"
42
+ type: :development
43
+ version_requirements: *id002
44
+ - !ruby/object:Gem::Dependency
45
+ name: mocha
46
+ prerelease: false
47
+ requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ type: :development
55
+ version_requirements: *id003
56
+ description: Create snapshot of webpage
57
+ email: ""
58
+ executables:
59
+ - wkhtmltoimage-linux-i386-0.10.0
60
+ - wkhtmltoimage-proxy
61
+ extensions: []
62
+
63
+ extra_rdoc_files:
64
+ - LICENSE
65
+ - README.md
66
+ - bin/wkhtmltoimage-linux-i386-0.10.0
67
+ - bin/wkhtmltoimage-proxy
68
+ - lib/websnap.rb
69
+ - lib/websnap/source.rb
70
+ - lib/websnap/websnap.rb
71
+ files:
72
+ - LICENSE
73
+ - Manifest
74
+ - README.md
75
+ - Rakefile
76
+ - bin/wkhtmltoimage-linux-i386-0.10.0
77
+ - bin/wkhtmltoimage-proxy
78
+ - lib/websnap.rb
79
+ - lib/websnap/source.rb
80
+ - lib/websnap/websnap.rb
81
+ - spec/fixtures/google.html
82
+ - spec/source_spec.rb
83
+ - spec/spec_helper.rb
84
+ - spec/websnap_spec.rb
85
+ - websnap.gemspec
86
+ has_rdoc: true
87
+ homepage: http://github.com/siuying/websnap
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options:
92
+ - --line-numbers
93
+ - --inline-source
94
+ - --title
95
+ - Websnap
96
+ - --main
97
+ - README.md
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ segments:
112
+ - 1
113
+ - 2
114
+ version: "1.2"
115
+ requirements: []
116
+
117
+ rubyforge_project: websnap
118
+ rubygems_version: 1.3.6
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Create snapshot of webpage
122
+ test_files: []
123
+