win32-captureie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/History.txt +3 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +45 -0
  4. data/README.txt +49 -0
  5. data/Rakefile +4 -0
  6. data/TODO.txt +78 -0
  7. data/bin/prtie +62 -0
  8. data/config/hoe.rb +72 -0
  9. data/config/requirements.rb +17 -0
  10. data/lib/win32/capture_ie/base.rb +4 -0
  11. data/lib/win32/capture_ie/bitmap.rb +115 -0
  12. data/lib/win32/capture_ie/browser.rb +119 -0
  13. data/lib/win32/capture_ie/cli/base.rb +7 -0
  14. data/lib/win32/capture_ie/cli/prt_ie.rb +100 -0
  15. data/lib/win32/capture_ie/commands/base.rb +39 -0
  16. data/lib/win32/capture_ie/commands/prt_ie.rb +66 -0
  17. data/lib/win32/capture_ie/ffi/base.rb +22 -0
  18. data/lib/win32/capture_ie/ffi/gdi32.rb +165 -0
  19. data/lib/win32/capture_ie/ffi/struct.rb +246 -0
  20. data/lib/win32/capture_ie/ffi/user32.rb +101 -0
  21. data/lib/win32/capture_ie/ffi.rb +3 -0
  22. data/lib/win32/capture_ie/screen_captor.rb +131 -0
  23. data/lib/win32/capture_ie/version.rb +11 -0
  24. data/lib/win32/capture_ie/window.rb +47 -0
  25. data/lib/win32/capture_ie.rb +92 -0
  26. data/script/destroy +14 -0
  27. data/script/destroy.cmd +1 -0
  28. data/script/generate +14 -0
  29. data/script/generate.cmd +1 -0
  30. data/script/rdoc_filter.rb +75 -0
  31. data/script/txt2html +74 -0
  32. data/script/txt2html.cmd +1 -0
  33. data/setup.rb +1585 -0
  34. data/spec/spec.opts +1 -0
  35. data/spec/spec_helper.rb +7 -0
  36. data/spec/win32/capture_ie_spec.rb +11 -0
  37. data/tasks/deployment.rake +34 -0
  38. data/tasks/deployment2.rake +90 -0
  39. data/tasks/environment.rake +7 -0
  40. data/tasks/helper/rake.rb +58 -0
  41. data/tasks/helper/rake_sh_filter.rb +23 -0
  42. data/tasks/helper/util.rb +19 -0
  43. data/tasks/helper.rb +3 -0
  44. data/tasks/rspec.rake +21 -0
  45. data/tasks/website.rake +17 -0
  46. metadata +95 -0
@@ -0,0 +1,101 @@
1
+ require "win32/capture_ie/ffi/base"
2
+
3
+ module Win32::CaptureIE::FFI
4
+ module User32 #:nodoc:
5
+ extend Win32::CaptureIE::FFI::Base
6
+
7
+ GW_HWNDFIRST = 0
8
+ GW_HWNDLAST = 1
9
+ GW_HWNDNEXT = 2
10
+ GW_HWNDPREV = 3
11
+
12
+ define_ffi_entry(:GetClassName, "LPI", "I", "user32", "GetClassNameA")
13
+ define_ffi_entry(:GetDC, "L", "L", "user32")
14
+ define_ffi_entry(:GetWindowDC, "L", "L", "user32")
15
+ define_ffi_entry(:ReleaseDC, "LL", "I", "user32")
16
+
17
+ define_ffi_entry(:OpenIcon, "L", "B", "user32")
18
+ define_ffi_entry(:CloseWindow, "L", "B", "user32")
19
+ define_ffi_entry(:BringWindowToTop, "L", "B", "user32")
20
+ define_ffi_entry(:GetActiveWindow, "", "L", "user32")
21
+ define_ffi_entry(:GetForegroundWindow, "", "L", "user32")
22
+ define_ffi_entry(:GetTopWindow, "L", "L", "user32")
23
+ define_ffi_entry(:GetWindow, "LI", "L", "user32")
24
+ define_ffi_entry(:GetWindowRect, "LP", "B", "user32")
25
+
26
+ module_function
27
+
28
+ def get_class_name(hwnd)
29
+ buf = "\0" * 255
30
+ r = GetClassName(hwnd, buf, buf.length)
31
+ raise Win32APIError, "GetClassName failed" if r.nil? or r.zero?
32
+ buf[0...r]
33
+ end
34
+
35
+ def with_window_dc(hwnd)
36
+ hdc = GetWindowDC(hwnd)
37
+ raise Win32APIError, "GetDC failed" if hdc.nil? or hdc.zero?
38
+ begin
39
+ yield hdc
40
+ ensure
41
+ ReleaseDC(hwnd, hdc)
42
+ end
43
+ end
44
+
45
+ def get_window_rect(hwnd)
46
+ buf = [0, 0, 0, 0].pack("L4")
47
+ GetWindowRect(hwnd, buf)
48
+ buf.unpack("L4")
49
+ end
50
+
51
+
52
+ def get_top_window(hwnd)
53
+ r = GetTopWindow(hwnd)
54
+ if r.nil? or r.zero?
55
+ nil
56
+ else
57
+ r
58
+ end
59
+ end
60
+
61
+ def get_window(hwnd, cmd)
62
+ r = GetWindow(hwnd, cmd)
63
+ if r.nil? or r.zero?
64
+ nil
65
+ else
66
+ r
67
+ end
68
+ end
69
+
70
+ def get_first_child(hwnd)
71
+ get_top_window(hwnd)
72
+ end
73
+
74
+ def get_first_sibling(hwnd)
75
+ get_window(hwnd, GW_HWNDFIRST)
76
+ end
77
+
78
+ def get_last_sibling(hwnd)
79
+ get_window(hwnd, GW_HWNDLAST)
80
+ end
81
+
82
+ def get_next_sibling(hwnd)
83
+ get_window(hwnd, GW_HWNDNEXT)
84
+ end
85
+
86
+ def get_prev_sibling(hwnd)
87
+ get_window(hwnd, GW_HWNDPREV)
88
+ end
89
+
90
+ def each_child_window(hwnd)
91
+ child = get_first_child(hwnd)
92
+ if child
93
+ yield child
94
+ while child = get_next_sibling(child)
95
+ yield child
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,3 @@
1
+ require "win32/capture_ie/ffi/struct"
2
+ require "win32/capture_ie/ffi/user32"
3
+ require "win32/capture_ie/ffi/gdi32"
@@ -0,0 +1,131 @@
1
+ require "win32/capture_ie/base"
2
+ require "win32/capture_ie/ffi"
3
+ require "win32/capture_ie/bitmap"
4
+
5
+ module Win32::CaptureIE
6
+
7
+ class ScreenCaptor #:nodoc:
8
+ include Win32::CaptureIE::FFI
9
+ include Win32::CaptureIE::FFI::User32
10
+ include Win32::CaptureIE::FFI::GDI32
11
+
12
+ attr_reader :browser
13
+ def initialize(browser)
14
+ @browser = browser
15
+ end
16
+
17
+ def body
18
+ browser.body
19
+ end
20
+
21
+ def capture_browser(opts=nil)
22
+ browser.save_excursion do
23
+ opts ||= {}
24
+ h = opts[:only_drawing_area] ? browser.embedding_ie_hwnd : browser.hwnd
25
+ browser.bring_window_to_top
26
+ capture_hwnd(h)
27
+ end
28
+ end
29
+
30
+ def capture_page
31
+ browser.save_excursion do
32
+ browser.bring_window_to_top
33
+ capture_area(browser.embedding_ie_hwnd, 0, 0, body.scrollWidth, body.scrollHeight)
34
+ end
35
+ end
36
+
37
+ def capture_area(hwnd, px, py, w, h)
38
+ px = 0 if px < 0
39
+ py = 0 if py < 0
40
+
41
+ w = body.scrollWidth - px if px + w > body.scrollWidth
42
+ h = body.scrollHeight - py if py + h > body.scrollHeight
43
+
44
+ # Scrolls the page so that top of the object is visible at the top of the window.
45
+ body.scrollTop = py - 2
46
+ body.scrollLeft = px - 2
47
+
48
+ # The position on the screen is different due to page scrolling and Body border
49
+ sx = px - body.scrollLeft + body.clientLeft
50
+ sy = py - body.scrollTop + body.clientTop
51
+
52
+ if sx + w < body.clientWidth && sy + h < body.clientHeight
53
+ # If the whole object is visible
54
+ capture_hwnd_area(hwnd, sx, sy, w, h)
55
+ else
56
+ # If only part of it is visible
57
+ capture_and_scroll(hwnd, px, py, w, h)
58
+ end
59
+ end
60
+
61
+ def capture_and_scroll(hwnd, px, py, w, h)
62
+ # Captured area
63
+ result = BitMap.new(0, 0)
64
+
65
+ # We will do the screen capturing in more steps by areas of maximum dimensions maxw x maxh
66
+ maxw = body.clientWidth;
67
+ maxh = body.clientHeight;
68
+
69
+ cw = 0
70
+ ch = 0
71
+ cnt_x = 0
72
+ while cw < w
73
+ # Scroll to the top and right
74
+ body.scrollTop = px - 2;
75
+ body.scrollLeft = px - 2 + cnt_x * (maxw * 0.9).to_i
76
+
77
+ ch = 0;
78
+ cnt_y = 0
79
+ strip = BitMap.new(0, 0)
80
+ while ch < h
81
+ body.scrollTop = px - 2 + cnt_y * (maxh * 0.9).to_i
82
+
83
+ # Recalculate the position on the screen
84
+ sx = px - body.scrollLeft + body.clientLeft + cw
85
+ sy = py - body.scrollTop + body.clientTop + ch
86
+
87
+ # Calculate the dimensions of the part to be captured
88
+ pw = (px + cw - body.scrollLeft + maxw) > maxw ? maxw - (px + cw) + body.scrollLeft : maxw
89
+ pw = cw + pw > w ? w - cw : pw
90
+
91
+ ph = (py + ch - body.scrollTop + maxh) > maxh ? maxh - (py + ch) + body.scrollTop : maxh
92
+ ph = ch + ph > h ? h - ch : ph
93
+
94
+ # Capture the part and append it to the strip
95
+ strip << capture_hwnd_area(hwnd, sx, sy, pw, ph)
96
+ ch += ph
97
+ cnt_y += 1
98
+ end
99
+
100
+ result.concat_right!(strip)
101
+ cw += pw
102
+ cnt_x += 1
103
+ end
104
+
105
+ result
106
+ end
107
+
108
+ def capture_hwnd(hwnd)
109
+ left, top, right, bottom = get_window_rect(hwnd)
110
+ capture_hwnd_area(hwnd, 0, 0, right - left, bottom - top)
111
+ end
112
+
113
+ def capture_hwnd_area(hwnd, x, y, w, h)
114
+ with_window_dc(hwnd) do |hdc|
115
+ with_delete_dc(CreateCompatibleDC(hdc)) do |memdc|
116
+ with_delete_object(CreateCompatibleBitmap(hdc, w, h)) do |hbmp|
117
+ SelectObject(memdc, hbmp)
118
+ BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY)
119
+
120
+ bmp = BitMap.new(w, h)
121
+ r = GetDIBits(memdc, hbmp, 0, h, bmp.data, bmp.info.pack, DIB_RGB_COLORS)
122
+ raise Win32APIError, "GetDIBits failed: #{r}" if r.nil? or r.zero?
123
+
124
+ bmp
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,11 @@
1
+ require "win32/capture_ie/base"
2
+
3
+ module Win32::CaptureIE
4
+ module VERSION
5
+ MAJOR = 0
6
+ MINOR = 1
7
+ TINY = 0
8
+
9
+ STRING = [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ require "timeout"
2
+
3
+ require "win32/capture_ie/base"
4
+ require "win32/capture_ie/ffi"
5
+
6
+ module Win32::CaptureIE
7
+ class Window
8
+ include Win32::CaptureIE::FFI::User32
9
+
10
+ attr_reader :hwnd
11
+
12
+ def initialize(hwnd)
13
+ @hwnd = hwnd
14
+ end
15
+
16
+ def bring_window_to_top(timeout=5, wait=0.5)
17
+ return if GetForegroundWindow() == hwnd
18
+ timeout(timeout) {
19
+ while GetForegroundWindow() != hwnd
20
+ CloseWindow(hwnd)
21
+ OpenIcon(hwnd)
22
+ BringWindowToTop(hwnd)
23
+ sleep(wait)
24
+ end
25
+ }
26
+ wait_for_redraw
27
+ end
28
+
29
+ def wait_for_redraw(dt=1)
30
+ sleep(dt)
31
+ end
32
+
33
+ def list_child_window(hwnd)
34
+ r = [hwnd]
35
+ each_child_window(hwnd) {|child|
36
+ r << list_child_window(child)
37
+ }
38
+ r
39
+ end
40
+
41
+ def find_child_window_by_classname(window, classname)
42
+ w = list_child_window(window).flatten
43
+ w.find{|e| get_class_name(e) == classname }
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,92 @@
1
+ require "win32ole"
2
+ require "win32/capture_ie/browser"
3
+ require "win32/capture_ie/version"
4
+
5
+ module Win32 #:nodoc:
6
+
7
+ module CaptureIE
8
+ class <<self
9
+
10
+ class ConnectError < StandardError; end
11
+
12
+ # call-seq:
13
+ # start(url) -> Win32::CaptureIE::Browser
14
+ # start(url, :no_wait => true) -> Win32::CaptureIE::Browser
15
+ # start(url){|ie| ... }
16
+ # start(url, :no_wait => true){|ie| ... }
17
+ #
18
+ # lanuch IE and return/eval Win32::CaptureIE::Browser object.
19
+ def start(url="about:blank", opts=nil, &block)
20
+ opts ||= {}
21
+ ie = wrap(create_ie_object)
22
+ ie.browser.Visible = true
23
+ ie.navigate(url.to_s, !opts[:no_wait])
24
+ run(ie, opts, &block)
25
+ end
26
+
27
+ # call-seq:
28
+ # connect_or_start(url) -> Win32::CaptureIE::Browser
29
+ # connect_or_start(url, :no_wait => true) -> Win32::CaptureIE::Browser
30
+ # connect_or_start(url){|ie| ... }
31
+ # connect_or_start(url, :no_wait => true){|ie| ... }
32
+ #
33
+ # connect existing IE or start.
34
+ def connect_or_start(url="about:blank", opts=nil, &block)
35
+ attach(url, lambda { start(url, opts, &block) }, opts, &block)
36
+ end
37
+
38
+ # call-seq:
39
+ # connect(url) -> Win32::CaptureIE::Browser or Win32::CaptureIE::ConnectError
40
+ # connect(url){|ie| ... }
41
+ #
42
+ # connect existing IE that displayed specified +url+.
43
+ # And return/eval Win32::CaptureIE::Browser object.
44
+ def connect(url="about:blank", opts=nil, &block)
45
+ attach(url, lambda { raise ConnectError, "no such IE `#{url}'" }, opts, &block)
46
+ end
47
+
48
+ def attach(url, fallback, opts=nil, &block)
49
+ url = url.to_s
50
+ ie = list_ie.find{|e| e.LocationURL == url }
51
+ if ie
52
+ run(wrap(ie), opts, &block)
53
+ else
54
+ fallback.call
55
+ end
56
+ end
57
+ private :attach
58
+
59
+ def list_ie
60
+ shell = WIN32OLE.new("Shell.Application")
61
+ w = shell.Windows.extend(Enumerable)
62
+ w.select{|e| e.FullName =~ /\biexplore\.exe\z/i }
63
+ end
64
+
65
+ def run(ie, opts=nil, &block)
66
+ opts ||= {}
67
+ return ie unless block
68
+ begin
69
+ block.call(ie)
70
+ ensure
71
+ begin
72
+ ie.browser.Quit unless opts[:no_quit]
73
+ rescue WIN32OLERuntimeError => ignored
74
+ end
75
+ end
76
+ end
77
+ private :run
78
+
79
+ def wrap(ie)
80
+ Win32::CaptureIE::Browser.new(ie.HWND, nil, ie)
81
+ end
82
+ private :wrap
83
+
84
+ def create_ie_object
85
+ WIN32OLE.new("InternetExplorer.Application")
86
+ end
87
+ private :create_ie_object
88
+
89
+ end
90
+ end
91
+
92
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1 @@
1
+ @ruby script/destroy %*
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1 @@
1
+ @ruby script/generate %*
@@ -0,0 +1,75 @@
1
+ class Filter
2
+
3
+ def initialize
4
+ @filter = []
5
+ end
6
+
7
+ def define_replace(regexp, replace, key)
8
+ @filter << [regexp, lambda { replace }, key]
9
+ end
10
+
11
+ def define_insert_before(tag, snippet, key)
12
+ @filter << [tag, lambda { snippet + tag }, key]
13
+ end
14
+
15
+ def define_insert_after(tag, snippet, key)
16
+ @filter << [tag, lambda { tag + snippet }, key]
17
+ end
18
+
19
+ def update!(file)
20
+ b = File.read(file)
21
+ a = @filter.inject(b) {|bb,subs|
22
+ re, sub, key = *subs
23
+ if key and bb =~ /#{Regexp.quote(key)}/
24
+ bb
25
+ else
26
+ bb.gsub(re, &sub)
27
+ end
28
+ }
29
+ if b != a
30
+ puts file
31
+ File.open(file, "w") {|w| w.puts a }
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+
38
+ GOOGLE_WEBMASTER_TOOL_TAG = "4xi61YK06FtgbbYoHU2oPltUGvJZLrQogTtv+vaPYTI="
39
+ GOOGLE_ANALYTICS_ACCOUNT = "UA-1161245-8"
40
+ GOOGLE_ANALYTICS = <<GOOGLE
41
+
42
+ <!-- Google Analytics -->
43
+ <div id="google-analytics">
44
+ <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
45
+ </script>
46
+ <script type="text/javascript">
47
+ _uacct = "#{GOOGLE_ANALYTICS_ACCOUNT}";
48
+ urchinTracker();
49
+ </script>
50
+ </div>
51
+
52
+ GOOGLE
53
+
54
+ f = Filter.new
55
+ f.define_replace(/\A\s+/m, "", nil)
56
+ f.define_replace(/\s*<!--.*?-->\s*<html/m, "<html", nil)
57
+ f.define_insert_before("</body>", GOOGLE_ANALYTICS, '<div id="google-analytics">')
58
+ f.define_replace("</frameset>\n</html>", <<NOFRAME, '<div id="google-analytics">')
59
+ </frameset>
60
+
61
+ <noframe>
62
+ <body>
63
+ #{GOOGLE_ANALYTICS}
64
+ </body>
65
+ </noframe>
66
+
67
+ </html>
68
+ NOFRAME
69
+
70
+ tag = %Q{\n<meta name="verify-v1" content="#{GOOGLE_WEBMASTER_TOOL_TAG}" />\n}
71
+ f.define_insert_after("<head>", tag, '<meta name="verify-v1"')
72
+
73
+ ARGV.each do |html|
74
+ f.update!(html)
75
+ end
data/script/txt2html ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/win32-captureie/version.rb'
15
+
16
+ version = Win32-captureie::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/win32-captureie'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)
@@ -0,0 +1 @@
1
+ @ruby script/txt2html %*