webshooter 0.0.1a

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.png
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "webshooter", :path => "."
data/Gemfile.lock ADDED
@@ -0,0 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ webshooter (0.0.1a)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+
10
+ PLATFORMS
11
+ ruby
12
+
13
+ DEPENDENCIES
14
+ webshooter!
data/README.txt ADDED
@@ -0,0 +1,32 @@
1
+ This library uses the Webkit Ruby Cocoa library to take headless webshots
2
+
3
+ # Usage
4
+
5
+ ## From within ruby
6
+ require 'webshooter'
7
+ Webshooter.capture('http://www.jedi.be','jedi.png','1024x768')
8
+
9
+ ## As a commandline tool
10
+ webshooter 'http://www.jedi.be' 'jedi.png' '1024x768'
11
+
12
+ # Limitations
13
+ - does not handle redirects currently
14
+ - create more configurable options
15
+ - cleanup code
16
+ - only support png
17
+
18
+ # Inspiration
19
+
20
+ This library is a compilation of various parts I found on the web
21
+ The research was done a few years ago, so unfortunatly I don't have all the references anymore
22
+
23
+ - Darkroom - Copyright (c) 2007 Justin Palmer.
24
+ - https://gist.github.com/34824
25
+ - https://gist.github.com/86435
26
+
27
+ - Thanks to the heavy lifting done by others:
28
+ - https://gist.github.com/244948
29
+ - http://pastie.caboo.se/69235
30
+ - http://pastie.caboo.se/68511
31
+ - https://gist.github.com/248077 Webview to PDF
32
+ - http://www.bencurtis.com/wp-content/uploads/2008/05/snapper.rb
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'bundler/setup'
4
+ Bundler::GemHelper.install_tasks
5
+
data/bin/webshooter.rb ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ require 'webshotr'
5
+
6
+ module Webshotr
7
+ class CLI
8
+ #Ruby CLI tool - http://rubylearning.com/blog/2011/01/03/how-do-i-make-a-command-line-tool-in-ruby/
9
+ def self.execute(args)
10
+ url=args[0]
11
+ filename=args[1]
12
+ size=args[2]
13
+ webshotr=Webshotr.capture(url, filename, size )
14
+ end
15
+ end
16
+ end
17
+
18
+ Webshotr::CLI.execute(ARGV)
@@ -0,0 +1,9 @@
1
+ module Webshooter
2
+ # We want to allow any https certificate
3
+ # OSX::NSURLRequest.setAllowsAnyHTTPSCertificate(true,myURI.host) has method missing
4
+ # Therefore we create a wrapper object
5
+ # NewRequest.setAllowsAnyHTTPSCertificate_forHost(true,OSX::NSURL.URLWithString(uri))
6
+
7
+ class NewNSURLRequest < OSX::NSURLRequest
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Webshooter
2
+ VERSION = "0.0.1a"
3
+ end
@@ -0,0 +1,224 @@
1
+ require 'osx/cocoa'
2
+ require 'logger'
3
+ require 'uri'
4
+
5
+ require 'webshooter/newnsurlrequest'
6
+ OSX.require_framework 'WebKit'
7
+
8
+ #Respond to URL not found....
9
+ #http://stackoverflow.com/questions/697108/testing-use-of-nsurlconnection-with-http-response-error-statuses
10
+ #http://www.gigliwood.com/weblog/Cocoa/Q__When_is_an_conne.html
11
+ #http://stackoverflow.com/questions/2236407/how-to-detect-and-handle-http-error-codes-in-uiwebview
12
+
13
+
14
+ module Webshooter
15
+
16
+ class WebShotProcessor
17
+
18
+ def initialize
19
+ @logger = Logger.new(STDOUT)
20
+
21
+ # get NSApplication code ready for action
22
+ OSX.NSApplicationLoad
23
+
24
+ # Make an offscreen window
25
+ # The frame is in screen coordinates, but it's irrelevant since we'll never display it
26
+ # We want a buffered backing store, simply so we can read it later
27
+ # We don't want to defer, or it won't draw until it gets on the screen
28
+ rect = OSX::NSRect.new(-16000,-16000,100,100)
29
+ @window = OSX::NSWindow.alloc.initWithContentRect_styleMask_backing_defer(
30
+ rect , OSX::NSBorderlessWindowMask, OSX::NSBackingStoreBuffered, false
31
+ )
32
+
33
+ # Make a web @webView - the frame here is in window coordinates
34
+ @webView = OSX::WebView.alloc.initWithFrame(rect)
35
+
36
+ # Use the screen stylesheet, rather than the print one.
37
+ @webView.setMediaStyle('screen')
38
+ # Make sure we don't save any of the prefs that we change.
39
+ @webView.preferences.setAutosaves(false)
40
+ # Set some useful options.
41
+ @webView.preferences.setShouldPrintBackgrounds(true)
42
+ @webView.preferences.setJavaScriptCanOpenWindowsAutomatically(false)
43
+ @webView.preferences.setAllowsAnimatedImages(false)
44
+ # Make sure we don't get a scroll bar.
45
+ @webView.mainFrame.frameView.setAllowsScrolling(false)
46
+
47
+ # This @delegate will get a message when the load completes
48
+ @delegate = SimpleLoadDelegate.alloc.init
49
+ @delegate.webshooter = self
50
+ @webView.setFrameLoadDelegate(@delegate)
51
+
52
+ # Replace the window's content @webView with the web @webView
53
+ @window.setContentView(@webView)
54
+ @webView.release
55
+ puts "its not in the release"
56
+ end
57
+
58
+ def capture(uri, path, dimensions = "1024x768" )
59
+
60
+ snapshot_dimension=dimensions.split('x')
61
+ # Tell the frame to load the URL we want
62
+ @webView.window.setContentSize(snapshot_dimension)
63
+ @webView.setFrameSize(snapshot_dimension)
64
+
65
+ myURI = URI.parse(uri)
66
+
67
+ #Allow all https certificates
68
+ NewNSURLRequest.setAllowsAnyHTTPSCertificate_forHost(true,myURI.host)
69
+
70
+ #puts "Getting ready for the loadRequest"+uri
71
+ @webView.mainFrame.loadRequest(NewNSURLRequest.requestWithURL(OSX::NSURL.URLWithString(uri)))
72
+
73
+ #
74
+ # Run the main event loop until the frame loads
75
+ @timeout=false
76
+ result=OSX.CFRunLoopRunInMode(OSX::KCFRunLoopDefaultMode, 20, false)
77
+ if (result == OSX::KCFRunLoopRunTimedOut)
78
+ @timeout=true
79
+ end
80
+
81
+ #This is what we need but the upon_ also changes
82
+ #OSX.CFRunLoopRun
83
+ #puts "first loop enters here"
84
+
85
+ puts "its not in the after upon success"
86
+
87
+ upon_success do |view| # Capture the content of the view as an image
88
+
89
+ view.window.orderFront(nil)
90
+ view.window.display
91
+
92
+ #puts "-------------------------------------------------"
93
+ #puts "We got success"
94
+ @docview=view.mainFrame.frameView.documentView
95
+
96
+ #We need the contents of the first frame
97
+ #pp view.bounds
98
+ #pp @docview.bounds
99
+
100
+ if @docview.bounds.size.height == 0.0
101
+ #pp "trying alternative"
102
+ #pp @docview
103
+ @docview= view.mainFrame.frameView.documentView
104
+ else
105
+ view.window.setContentSize(@docview.bounds.size)
106
+ view.setFrame(@docview.bounds)
107
+ if view.bounds.size.height > 300000
108
+ #view.bounds.size.height=300000
109
+ #puts "Adjusted maximum size to 300000"
110
+ end
111
+ end
112
+
113
+
114
+ # Write the bitmap to a file as a PNG
115
+ #
116
+ #view.bounds=initialsize
117
+ #view.window.setContentSize(view.bounds.size)
118
+
119
+ view.setNeedsDisplay(true)
120
+ view.displayIfNeeded
121
+ if view.bounds.size.height < 300000
122
+ view.lockFocus
123
+ bitmap = OSX::NSBitmapImageRep.alloc.initWithFocusedViewRect(view.bounds)
124
+ bitmap.representationUsingType_properties(OSX::NSPNGFileType, nil).writeToFile_atomically(path, true)
125
+ bitmap.release
126
+ view.unlockFocus
127
+
128
+ else
129
+ puts "Something went wrong, the size became to big"
130
+ end
131
+
132
+ end
133
+
134
+ upon_failure do |error, logger|
135
+ logger.warn("Unable to load URI: #{uri} (#{error})")
136
+ end
137
+
138
+
139
+ end
140
+
141
+ def release
142
+ #@window.release
143
+ @delegate.release
144
+ end
145
+
146
+ attr_accessor :load_success, :load_error
147
+
148
+ private
149
+
150
+ def upon_success(&block)
151
+ block.call(@webView) if @load_success
152
+ end
153
+
154
+ def upon_failure(&block)
155
+ if (@timeout)
156
+ block.call("Timeout error", @logger) unless @load_success
157
+ else
158
+ block.call(@load_error.localizedDescription, @logger) unless @load_success
159
+ end
160
+ end
161
+
162
+
163
+ class SimpleLoadDelegate < OSX::NSObject
164
+
165
+ attr_accessor :webshooter
166
+
167
+ def stopLoop
168
+ mainLoop=OSX.CFRunLoopGetMain
169
+ currentLoop=OSX.CFRunLoopGetCurrent
170
+ OSX.CFRunLoopStop(mainLoop)
171
+ OSX.CFRunLoopStop(currentLoop)
172
+ end
173
+
174
+ def webView_didFinishLoadForFrame(sender, frame)
175
+ #This did the trick, we have to wait for the right frame to load, not other frames
176
+ if (frame == sender.mainFrame)
177
+ then
178
+ #puts "Finish Load For Frame"
179
+ @webshooter.load_success = true;
180
+
181
+ stopLoop
182
+ else
183
+ #puts "WARN: the mainframe is not the frame"
184
+ return
185
+ end
186
+ end
187
+
188
+ def webView_didFailLoadWithError_forFrame(webview, load_error, frame)
189
+
190
+ #This is trick # 2
191
+ #We have to catch this stupid error
192
+ if (load_error.code == OSX::NSURLErrorCancelled)
193
+ then
194
+ #pp "WARN: did Fail with Error For Frame"
195
+ #pp "WARN: we don't give up"
196
+ return
197
+ else
198
+ #puts load_error.localizedDescription
199
+ end
200
+
201
+ @webshooter.load_success = false; @webshooter.load_error = load_error;
202
+
203
+ stopLoop
204
+ end
205
+
206
+ def webView_didFailProvisionalLoadWithError_forFrame(webview, load_error, frame)
207
+ if (load_error.code == OSX::NSURLErrorCancelled)
208
+ then
209
+ #pp "WARN: did Fail PROVISIONAL LOAD WITH ERROR For Frame"
210
+ #pp "WARN: we don't give up"
211
+ return
212
+ else
213
+ #puts load_error.localizedDescription
214
+ end
215
+ @webshooter.load_success = false; @webshooter.load_error = load_error;
216
+
217
+ stopLoop
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ end
data/lib/webshooter.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'webshooter/webshotprocessor'
2
+
3
+ # We might handle redirection in the future
4
+ # http://shadow-file.blogspot.com/2009/03/handling-http-redirection-in-ruby.html
5
+
6
+ module Webshooter
7
+ class Webshooter
8
+ def self.capture(uri, path, dimensions = "1024x768" )
9
+ webProcessor=WebShotProcessor.new
10
+ webProcessor.capture(uri,path,dimensions)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # DarkRoom
4
+ # Takes fullsize screenshots of a web page.
5
+ # Copyright (c) 2007 Justin Palmer.
6
+ #
7
+ # Released under an MIT LICENSE
8
+ #
9
+ # Usage
10
+ # ====
11
+ # ruby ./darkroom.rb http://activereload.net
12
+ # ruby ./darkroom.rb --output=google.png http://google.com
13
+ #
14
+ require 'optparse'
15
+ require 'osx/cocoa'
16
+ OSX.require_framework 'Webkit'
17
+
18
+ module ActiveReload
19
+ module DarkRoom
20
+ USER_AGENT = "DarkRoom/0.1"
21
+ class Photographer
22
+ def initialize
23
+ options = {}
24
+ opts = OptionParser.new do |opts|
25
+ opts.banner = "Usage: #$0 [options] URL"
26
+
27
+ opts.on('-w', '--width=[WIDTH]', Integer, 'Force width of the screenshot') do |v|
28
+ options[:width] = v
29
+ end
30
+
31
+ opts.on('-h', '--height=[HEIGHT]', Integer, 'Force height of screenshot') do |v|
32
+ options[:height] = v
33
+ end
34
+
35
+ opts.on('-o', '--output=[FILENAME]', String, 'Specify filename for saving') do |v|
36
+ options[:output] = v
37
+ end
38
+
39
+ opts.on_tail('-h', '--help', 'Display this message and exit') do
40
+ puts opts
41
+ exit
42
+ end
43
+ end.parse!
44
+ options[:width] ||= 1024
45
+ options[:height] ||= 0
46
+ options[:website] = ARGV.first || 'http://google.com'
47
+ Camera.shoot(options)
48
+ Camera.shoot(options)
49
+ end
50
+ end
51
+
52
+ class Camera
53
+ def self.shoot(options)
54
+ app = OSX::NSApplication.sharedApplication
55
+ delegate = Processor.alloc.init
56
+ delegate.options = options
57
+ app.setDelegate(delegate)
58
+ app.run
59
+ end
60
+ end
61
+
62
+ class Processor < OSX::NSObject
63
+ include OSX
64
+ attr_accessor :options, :web_view
65
+
66
+ def initialize
67
+ rect = [-16000.0, -16000.0, 100, 100]
68
+ win = NSWindow.alloc.initWithContentRect_styleMask_backing_defer(rect, NSBorderlessWindowMask, 2, 0)
69
+
70
+ @web_view = WebView.alloc.initWithFrame(rect)
71
+ @web_view.mainFrame.frameView.setAllowsScrolling(false)
72
+ @web_view.setApplicationNameForUserAgent(USER_AGENT)
73
+
74
+ NSNotificationCenter.defaultCenter.objc_send(:addObserver, self,
75
+ :selector, :webview_progress_finished,
76
+ :name, WebViewProgressFinishedNotification,
77
+ :object, @web_view)
78
+
79
+ win.setContentView(@web_view)
80
+ end
81
+
82
+ def webview_progress_finished(sender)
83
+ viewport = web_view.mainFrame.frameView.documentView
84
+ viewport.window.orderFront(nil)
85
+ viewport.window.display
86
+ viewport.window.setContentSize([@options[:width], (@options[:height] > 0 ? @options[:height] : viewport.bounds.height)])
87
+ viewport.setFrame(viewport.bounds)
88
+ sleep(@options[:delay]) if @options[:delay]
89
+ capture_and_save(viewport)
90
+ end
91
+
92
+ def applicationDidFinishLaunching(notification)
93
+ @options[:output] ||= "#{Time.now.strftime('%m-%d-%y-%H%I%S')}.png"
94
+ @web_view.window.setContentSize([@options[:width], @options[:height]])
95
+ @web_view.setFrameSize([@options[:width], @options[:height]])
96
+ @web_view.mainFrame.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(@options[:website])))
97
+ end
98
+
99
+ def capture_and_save(view)
100
+ print "we never get here"
101
+ view.lockFocus
102
+ bitmap = NSBitmapImageRep.alloc.initWithFocusedViewRect(view.bounds)
103
+ view.unlockFocus
104
+
105
+ bitmap.representationUsingType_properties(NSPNGFileType, nil).writeToFile_atomically(@options[:output], true)
106
+ #NSApplication.sharedApplication.terminate(nil)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ ActiveReload::DarkRoom::Photographer.new
112
+ ActiveReload::DarkRoom::Photographer.new
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # DarkRoom
4
+ # Takes fullsize screenshots of a web page.
5
+ # Copyright (c) 2007 Justin Palmer.
6
+ #
7
+ # Released under an MIT LICENSE
8
+ #
9
+ # Usage
10
+ # ====
11
+ # ruby ./darkroom.rb http://activereload.net
12
+ # ruby ./darkroom.rb --output=google.png http://google.com
13
+ #
14
+ require 'optparse'
15
+ require 'osx/cocoa'
16
+ OSX.require_framework 'Webkit'
17
+
18
+ module ActiveReload
19
+ module DarkRoom
20
+ USER_AGENT = "DarkRoom/0.1"
21
+
22
+ class Camera
23
+ def self.shoot(options)
24
+ app = OSX::NSApplication.sharedApplication
25
+ delegate = Processor.alloc.init
26
+ delegate.options = options
27
+ app.setDelegate(delegate)
28
+ app.run
29
+ end
30
+ end
31
+
32
+ class Processor < OSX::NSObject
33
+ include OSX
34
+ attr_accessor :options, :web_view
35
+
36
+ def initialize
37
+ rect = [-16000.0, -16000.0, 100, 100]
38
+ win = NSWindow.alloc.initWithContentRect_styleMask_backing_defer(rect, NSBorderlessWindowMask, 2, 0)
39
+
40
+ @web_view = WebView.alloc.initWithFrame(rect)
41
+ @web_view.mainFrame.frameView.setAllowsScrolling(false)
42
+ @web_view.setApplicationNameForUserAgent(USER_AGENT)
43
+
44
+ NSNotificationCenter.defaultCenter.objc_send(:addObserver, self,
45
+ :selector, :webview_progress_finished,
46
+ :name, WebViewProgressFinishedNotification,
47
+ :object, @web_view)
48
+
49
+ win.setContentView(@web_view)
50
+ end
51
+
52
+ def webview_progress_finished(sender)
53
+ viewport = web_view.mainFrame.frameView.documentView
54
+ viewport.window.orderFront(nil)
55
+ viewport.window.display
56
+ viewport.window.setContentSize([@options[:width], (@options[:height] > 0 ? @options[:height] : viewport.bounds.height)])
57
+ viewport.setFrame(viewport.bounds)
58
+ sleep(@options[:delay]) if @options[:delay]
59
+ capture_and_save(viewport)
60
+ end
61
+
62
+ def applicationDidFinishLaunching(notification)
63
+ @options[:output] ||= "#{Time.now.strftime('%m-%d-%y-%H%I%S')}.png"
64
+ @web_view.window.setContentSize([@options[:width], @options[:height]])
65
+ @web_view.setFrameSize([@options[:width], @options[:height]])
66
+ @web_view.mainFrame.loadRequest(NSURLRequest.requestWithURL(NSURL.URLWithString(@options[:website])))
67
+ end
68
+
69
+ def capture_and_save(view)
70
+ print "we never get here"
71
+ view.lockFocus
72
+ bitmap = NSBitmapImageRep.alloc.initWithFocusedViewRect(view.bounds)
73
+ view.unlockFocus
74
+
75
+ bitmap.representationUsingType_properties(NSPNGFileType, nil).writeToFile_atomically(@options[:output], true)
76
+ #NSApplication.sharedApplication.terminate(nil)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ #ActiveReload::DarkRoom::Photographer.new
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Thanks to the heavy lifting done by others:
4
+ # http://pastie.caboo.se/69235
5
+ # http://pastie.caboo.se/68511
6
+
7
+ require 'osx/cocoa'
8
+ OSX.require_framework 'WebKit'
9
+
10
+ class Snapper < OSX::NSObject
11
+
12
+ def init
13
+ # This sets up some context that we need for creating windows.
14
+ OSX::NSApplication.sharedApplication
15
+
16
+ # Create an offscreen window into which we can stick our WebView.
17
+ @window = OSX::NSWindow.alloc.initWithContentRect_styleMask_backing_defer(
18
+ [0, 0, 800, 600], OSX::NSBorderlessWindowMask, OSX::NSBackingStoreBuffered, false
19
+ )
20
+
21
+ # Create a WebView and stick it in our offscreen window.
22
+ @webView = OSX::WebView.alloc.initWithFrame([0, 0, 800, 600])
23
+ @window.setContentView(@webView)
24
+
25
+ # Use the screen stylesheet, rather than the print one.
26
+ @webView.setMediaStyle('screen')
27
+ # Make sure we don't save any of the prefs that we change.
28
+ @webView.preferences.setAutosaves(false)
29
+ # Set some useful options.
30
+ @webView.preferences.setShouldPrintBackgrounds(true)
31
+ @webView.preferences.setJavaScriptCanOpenWindowsAutomatically(false)
32
+ @webView.preferences.setAllowsAnimatedImages(false)
33
+ # Make sure we don't get a scroll bar.
34
+ @webView.mainFrame.frameView.setAllowsScrolling(false)
35
+
36
+ self
37
+ end
38
+
39
+ def fetch(url)
40
+ # This sets up the webView_* methods to be called when loading finishes.
41
+ @webView.setFrameLoadDelegate(self)
42
+ # Tell the webView what URL to load.
43
+ @webView.setValue_forKey(url, 'mainFrameURL')
44
+ # Pass control to Cocoa for a bit.
45
+ OSX.CFRunLoopRun
46
+ @succeeded
47
+ end
48
+
49
+ attr_reader :error
50
+
51
+ def webView_didFinishLoadForFrame(view, frame)
52
+ @succeeded = true
53
+
54
+ # Resize the view to fit the page.
55
+ @docView = @webView.mainFrame.frameView.documentView
56
+ @docView.window.setContentSize(@docView.bounds.size)
57
+ @docView.setFrame(@docView.bounds)
58
+
59
+ # Return control to the fetch method.
60
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
61
+ end
62
+
63
+ def webView_didFailLoadWithError_forFrame(webview, error, frame)
64
+ @error = error
65
+ @succeeed = false
66
+ # Return control to the fetch method.
67
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
68
+ end
69
+
70
+ def webView_didFailProvisionalLoadWithError_forFrame(webview, error, frame)
71
+ @error = error
72
+ @succeeed = false
73
+ # Return control to the fetch method.
74
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
75
+ end
76
+
77
+ def save(filename, options = {})
78
+ @docView.setNeedsDisplay(true)
79
+ @docView.displayIfNeeded
80
+ @docView.lockFocus
81
+ bitmap = OSX::NSBitmapImageRep.alloc.initWithFocusedViewRect(@docView.bounds)
82
+ @docView.unlockFocus
83
+
84
+ # Write the bitmap to a file as a PNG
85
+ bitmap.representationUsingType_properties(OSX::NSPNGFileType, nil).writeToFile_atomically(filename, true)
86
+ bitmap.release
87
+ end
88
+
89
+ end
90
+
data/trials/snapper.rb ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Thanks to the heavy lifting done by others:
4
+ # http://pastie.caboo.se/69235
5
+ # http://pastie.caboo.se/68511
6
+
7
+ require 'osx/cocoa'
8
+ OSX.require_framework 'WebKit'
9
+
10
+ class Snapper < OSX::NSObject
11
+
12
+ def init
13
+ # This sets up some context that we need for creating windows.
14
+ OSX::NSApplication.sharedApplication
15
+
16
+ # Create an offscreen window into which we can stick our WebView.
17
+ #@window = OSX::NSWindow.alloc.initWithContentRect_styleMask_backing_defer(
18
+ # [0, 0, 800, 600], OSX::NSBorderlessWindowMask, OSX::NSBackingStoreBuffered, false
19
+ #)
20
+ rect = [-16000.0, -16000.0, 100, 100]
21
+ @window = OSX::NSWindow.alloc.initWithContentRect_styleMask_backing_defer(rect, OSX::NSBorderlessWindowMask, OSX::NSBackingStoreBuffered, false )
22
+
23
+
24
+
25
+ # Create a WebView and stick it in our offscreen window.
26
+ #@webView = OSX::WebView.alloc.initWithFrame([0, 0, 800, 600])
27
+ @webView = OSX::WebView.alloc.initWithFrame(rect)
28
+ @window.setContentView(@webView)
29
+
30
+ # Use the screen stylesheet, rather than the print one.
31
+ @webView.setMediaStyle('screen')
32
+ # Make sure we don't save any of the prefs that we change.
33
+ @webView.preferences.setAutosaves(false)
34
+ # Set some useful options.
35
+ @webView.preferences.setShouldPrintBackgrounds(true)
36
+ @webView.preferences.setJavaScriptCanOpenWindowsAutomatically(false)
37
+ @webView.preferences.setAllowsAnimatedImages(false)
38
+ # Make sure we don't get a scroll bar.
39
+ @webView.mainFrame.frameView.setAllowsScrolling(false)
40
+
41
+ self
42
+ end
43
+
44
+ def fetch(url)
45
+ # This sets up the webView_* methods to be called when loading finishes.
46
+ @webView.setFrameLoadDelegate(self)
47
+ # Tell the webView what URL to load.
48
+ @webView.setValue_forKey(url, 'mainFrameURL')
49
+ # Pass control to Cocoa for a bit.
50
+ OSX.CFRunLoopRun
51
+ @succeeded
52
+ end
53
+
54
+ attr_reader :error
55
+
56
+ def webView_didFinishLoadForFrame(view, frame)
57
+ @succeeded = true
58
+
59
+ # Resize the view to fit the page.
60
+ @docView = @webView.mainFrame.frameView.documentView
61
+ @docView.window.orderFront(nil)
62
+ @docView.display
63
+ @docView.window.setContentSize(@docView.bounds.size)
64
+ @docView.setFrame(@docView.bounds)
65
+ sleep(4)
66
+ # Return control to the fetch method.
67
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
68
+ end
69
+
70
+ def webView_didFailLoadWithError_forFrame(webview, error, frame)
71
+ @error = error
72
+ @succeeed = false
73
+ # Return control to the fetch method.
74
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
75
+ end
76
+
77
+ def webView_didFailProvisionalLoadWithError_forFrame(webview, error, frame)
78
+ @error = error
79
+ @succeeed = false
80
+ # Return control to the fetch method.
81
+ OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
82
+ end
83
+
84
+ def save(filename, options = {})
85
+ @docView.setNeedsDisplay(true)
86
+ @docView.displayIfNeeded
87
+ @docView.lockFocus
88
+ bitmap = OSX::NSBitmapImageRep.alloc.initWithFocusedViewRect(@docView.bounds)
89
+ @docView.unlockFocus
90
+
91
+ # Write the bitmap to a file as a PNG
92
+ bitmap.representationUsingType_properties(OSX::NSPNGFileType, nil).writeToFile_atomically(filename, true)
93
+ bitmap.release
94
+ end
95
+
96
+ end
97
+
@@ -0,0 +1,101 @@
1
+ # crafterm@redartisan.com, with major help from vastheman :)
2
+
3
+ require 'osx/cocoa'
4
+ require 'logger'
5
+
6
+ OSX.require_framework 'WebKit'
7
+
8
+ class Shooter
9
+
10
+ def initialize
11
+ @logger = Logger.new(STDOUT)
12
+
13
+ @pool = OSX::NSAutoreleasePool.alloc.init
14
+
15
+ # get NSApplication code ready for action
16
+ OSX.NSApplicationLoad
17
+
18
+ # Make an offscreen window
19
+ # The frame is in screen coordinates, but it's irrelevant since we'll never display it
20
+ # We want a buffered backing store, simply so we can read it later
21
+ # We don't want to defer, or it won't draw until it gets on the screen
22
+ @window = OSX::NSWindow.alloc.initWithContentRect_styleMask_backing_defer(
23
+ OSX::NSRect.new(0, 0, 1024, 768), OSX::NSBorderlessWindowMask, OSX::NSBackingStoreBuffered, false
24
+ )
25
+
26
+ # Make a web @view - the frame here is in window coordinates
27
+ @view = OSX::WebView.alloc.initWithFrame(OSX::NSRect.new(0, 0, 1024, 768))
28
+ @view.mainFrame.frameView.setAllowsScrolling(false)
29
+
30
+ # This @delegate will get a message when the load completes
31
+ @delegate = SimpleLoadDelegate.alloc.init
32
+ @delegate.shooter = self
33
+ @view.setFrameLoadDelegate(@delegate)
34
+
35
+ # Replace the window's content @view with the web @view
36
+ @window.setContentView(@view)
37
+ @view.release
38
+ end
39
+
40
+ def capture(uri, path)
41
+ # Tell the frame to load the URL we want
42
+ @view.mainFrame.loadRequest(OSX::NSURLRequest.requestWithURL(OSX::NSURL.URLWithString(uri)))
43
+
44
+ # Run the main event loop until the frame loads
45
+ OSX.CFRunLoopRun
46
+
47
+ upon_success do |view| # Capture the content of the view as an image
48
+ view.setNeedsDisplay(true)
49
+ view.displayIfNeeded
50
+ view.lockFocus
51
+ bitmap = OSX::NSBitmapImageRep.alloc.initWithFocusedViewRect(view.bounds)
52
+ view.unlockFocus
53
+
54
+ # Write the bitmap to a file as a PNG
55
+ bitmap.representationUsingType_properties(OSX::NSPNGFileType, nil).writeToFile_atomically(path, true)
56
+ bitmap.release
57
+ end
58
+
59
+ upon_failure do |error, logger|
60
+ logger.warn("Unable to load URI: #{uri} (#{error})")
61
+ end
62
+ end
63
+
64
+ def release
65
+ @window.release
66
+ @delegate.release
67
+ @pool.release
68
+ end
69
+
70
+ attr_accessor :load_success, :load_error
71
+
72
+ private
73
+
74
+ def upon_success(&block)
75
+ block.call(@view) if @load_success
76
+ end
77
+
78
+ def upon_failure(&block)
79
+ block.call(@load_error.localizedDescription, @logger) unless @load_success
80
+ end
81
+
82
+ class SimpleLoadDelegate < OSX::NSObject
83
+
84
+ attr_accessor :shooter
85
+
86
+ def webView_didFinishLoadForFrame(sender, frame)
87
+ @shooter.load_success = true; OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
88
+ end
89
+
90
+ def webView_didFailLoadWithError_forFrame(webview, load_error, frame)
91
+ @shooter.load_success = false; @shooter.load_error = load_error; OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
92
+ end
93
+
94
+ def webView_didFailProvisionalLoadWithError_forFrame(webview, load_error, frame)
95
+ @shooter.load_success = false; @shooter.load_error = load_error; OSX.CFRunLoopStop(OSX.CFRunLoopGetCurrent)
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/webshooter/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "webshooter"
6
+ s.version = Webshooter::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Patrick Debois"]
9
+ s.email = ["patrick.debois@jedi.be"]
10
+ s.homepage = "http://github.com/jedi4ever/webshooter/"
11
+ s.summary = %q{Create webshot using webkit on MacOSX}
12
+ s.description = %q{This library allows you to create webshots using webkit on MacOSX. A webshot is a screenshot taking inside the browser. The advantage of this library is that it is headless and gives you the real view not a parsed view}
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "webshooter"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
21
+ s.require_path = 'lib'
22
+ end
23
+
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webshooter
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1a
9
+ version: 0.0.1a
10
+ platform: ruby
11
+ authors:
12
+ - Patrick Debois
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-19 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ segments:
26
+ - 1
27
+ - 0
28
+ - 0
29
+ version: 1.0.0
30
+ requirement: *id001
31
+ name: bundler
32
+ prerelease: false
33
+ type: :development
34
+ description: This library allows you to create webshots using webkit on MacOSX. A webshot is a screenshot taking inside the browser. The advantage of this library is that it is headless and gives you the real view not a parsed view
35
+ email:
36
+ - patrick.debois@jedi.be
37
+ executables:
38
+ - webshooter.rb
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - .gitignore
45
+ - Gemfile
46
+ - Gemfile.lock
47
+ - README.txt
48
+ - Rakefile
49
+ - bin/webshooter.rb
50
+ - lib/webshooter.rb
51
+ - lib/webshooter/newnsurlrequest.rb
52
+ - lib/webshooter/version.rb
53
+ - lib/webshooter/webshotprocessor.rb
54
+ - trials/darkroom.orig.rb
55
+ - trials/darkroom.rb
56
+ - trials/snapper.orig.rb
57
+ - trials/snapper.rb
58
+ - trials/webkit-shooter.orig.rb
59
+ - webshooter.gemspec
60
+ has_rdoc: true
61
+ homepage: http://github.com/jedi4ever/webshooter/
62
+ licenses: []
63
+
64
+ post_install_message:
65
+ rdoc_options: []
66
+
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 1
82
+ - 3
83
+ - 6
84
+ version: 1.3.6
85
+ requirements: []
86
+
87
+ rubyforge_project: webshooter
88
+ rubygems_version: 1.3.6
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Create webshot using webkit on MacOSX
92
+ test_files: []
93
+