win32-captureie 0.1.0

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.
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 %*