chupa-text-decomposer-webkit 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0522e247250744ce0a2df3317cc3226948f152a6
4
- data.tar.gz: 1162b7e926467f66728378fb4b64cb6928afef96
3
+ metadata.gz: b28aa0772e09cadcde4ed6a92ebe681019aac5b2
4
+ data.tar.gz: 12f606f199ef7593ff54aec5f11674bc90b077bf
5
5
  SHA512:
6
- metadata.gz: 7f7030778edcfe73c3a7ef5211c48d7218efc7e10f38cbf6f4a6c9a1e3374c202501669503ded21b5db483b18e48b3406a0890fbb725859d3aca6338a59cd3c2
7
- data.tar.gz: 0b6aa0901239e94e35d65d70054dcd5d0d58e584034f17208d989ffc51848f3782069b04e45890fb7661697511480d4d1f43fefb9565ff948c5d05cbf5a5b4a2
6
+ metadata.gz: b1bda3a888d92deab02ef9e51d3a8a2ef97bbc4f6d9e39b85c3310ffbe51e9f6b46cb01510cbeef793129203f7f43d0431f16e52ce04abb58f94aa22c5f3b9c6
7
+ data.tar.gz: e39a3397f022c2df03284f88657443f14e5940720619f2ff17b44586e0d6400d7417c349e53db3939081c54f8c3fa4024a0ac2a6769ed3fd8b3f9d33d4e90a69
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  Gem::Specification.new do |spec|
24
24
  spec.name = "chupa-text-decomposer-webkit"
25
- spec.version = "1.0.4"
25
+ spec.version = "1.0.5"
26
26
  spec.homepage = "https://github.com/ranguba/chupa-text-decomposer-webkit"
27
27
  spec.authors = ["Kouhei Sutou"]
28
28
  spec.email = ["kou@clear-code.com"]
@@ -1,5 +1,11 @@
1
1
  # News
2
2
 
3
+ ## 1.0.5: 2017-07-12
4
+
5
+ ### Improvements
6
+
7
+ * Added workaround for crash.
8
+
3
9
  ## 1.0.4: 2017-07-12
4
10
 
5
11
  ### Improvements
@@ -0,0 +1,216 @@
1
+ # Copyright (C) 2017 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ require "webkit2-gtk"
18
+
19
+ module ChupaTextDecomposerWebKit
20
+ class Screenshoter
21
+ def initialize(logger)
22
+ @logger = logger
23
+ @view_context = create_view_context
24
+ @view = create_view
25
+ @window = create_window
26
+ @main_loop = GLib::MainLoop.new(nil, false)
27
+ @timeout_second = compute_timeout_second
28
+ @screenshot_cancellable = nil
29
+ @on_snapshot = nil
30
+ end
31
+
32
+ def run(body, uri, output_path, width, height)
33
+ @on_snapshot = lambda do |snapshot_surface|
34
+ scaled_surface = scale_snapshot(snapshot_surface, width, height)
35
+ scaled_surface.write_to_png(output_path)
36
+ end
37
+
38
+ begin
39
+ timeout do
40
+ debug do
41
+ "#{log_tag}[load][HTML] #{uri}"
42
+ end
43
+ @view.load_html(body, uri)
44
+ @main_loop.run
45
+ end
46
+ ensure
47
+ @on_snapshot = nil
48
+ end
49
+ end
50
+
51
+ private
52
+ def create_view_context
53
+ context = WebKit2Gtk::WebContext.new(ephemeral: true)
54
+ http_proxy = ENV["http_proxy"]
55
+ https_proxy = ENV["https_proxy"]
56
+ ftp_proxy = ENV["ftp_proxy"]
57
+ if http_proxy or https_proxy or ftp_proxy
58
+ proxy_settings = WebKit2Gtk::NetworkProxySettings.new
59
+ if http_proxy
60
+ proxy_settings.add_proxy_for_scheme("http", http_proxy)
61
+ end
62
+ if https_proxy
63
+ proxy_settings.add_proxy_for_scheme("https", https_proxy)
64
+ end
65
+ if ftp_proxy
66
+ proxy_settings.add_proxy_for_scheme("ftp", ftp_proxy)
67
+ end
68
+ context.set_network_proxy_settings(:custom, proxy_settings)
69
+ end
70
+ context
71
+ end
72
+
73
+ def create_view
74
+ view = WebKit2Gtk::WebView.new(context: @view_context)
75
+
76
+ view.signal_connect("load-changed") do |_, load_event|
77
+ debug do
78
+ "#{log_tag}[load][#{load_event.nick}] #{view.uri}"
79
+ end
80
+
81
+ case load_event
82
+ when WebKit2Gtk::LoadEvent::FINISHED
83
+ debug do
84
+ "#{log_tag}[screenshot][start] #{view.uri}"
85
+ end
86
+ cancel_screenshot
87
+ @screenshot_cancellable = Gio::Cancellable.new
88
+ view.get_snapshot(:full_document,
89
+ :none,
90
+ @screenshot_cancellable) do |_, result|
91
+ @screenshot_cancellable = nil
92
+ @main_loop.quit
93
+ begin
94
+ snapshot_surface = view.get_snapshot_finish(result)
95
+ rescue
96
+ error do
97
+ message = "failed to create snapshot: #{view.uri}: "
98
+ message << "#{$!.class}: #{$!.message}"
99
+ "#{log_tag}[screenshot][failed] #{message}"
100
+ end
101
+ else
102
+ debug do
103
+ size = "#{snapshot_surface.width}x#{snapshot_surface.height}"
104
+ "#{log_tag}[screenshot][finish] #{view.uri}: #{size}"
105
+ end
106
+ unless snapshot_surface.width.zero?
107
+ @on_snapshot.call(snapshot_surface) if @on_snapshot
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ view.signal_connect("load-failed") do |_, _, failed_uri, error|
115
+ cancel_screenshot
116
+ @main_loop.quit
117
+ error do
118
+ message = "failed to load URI: #{failed_uri}: "
119
+ message << "#{error.class}(#{error.code}): #{error.message}"
120
+ "#{log_tag}[load][failed] #{message}"
121
+ end
122
+ true
123
+ end
124
+
125
+ view
126
+ end
127
+
128
+ def scale_snapshot(snapshot_surface, width, height)
129
+ scaled_surface = Cairo::ImageSurface.new(:argb32, width, height)
130
+
131
+ context = Cairo::Context.new(scaled_surface)
132
+ context.set_source_color(:white)
133
+ context.paint
134
+
135
+ ratio = width.to_f / snapshot_surface.width
136
+ context.scale(ratio, ratio)
137
+ context.set_source(snapshot_surface)
138
+ context.paint
139
+
140
+ scaled_surface
141
+ end
142
+
143
+ def create_window
144
+ window = Gtk::OffscreenWindow.new
145
+ window.set_default_size(800, 600)
146
+ window.add(@view)
147
+ window.show_all
148
+ window
149
+ end
150
+
151
+ def cancel_screenshot
152
+ return if @screenshot_cancellable.nil?
153
+
154
+ debug do
155
+ "#{log_tag}[snapshot][cancel] cancel screenshot: #{@view.uri}"
156
+ end
157
+ @screenshot_cancellable.cancel
158
+ @screenshot_cancellable = nil
159
+ end
160
+
161
+ def timeout
162
+ timeout_id = GLib::Timeout.add_seconds(@timeout_second) do
163
+ timeout_id = nil
164
+ error do
165
+ message = "timeout to load URI: #{@timeout_second}s: #{@view.uri}"
166
+ message << ": loading" if @view.loading?
167
+ "#{log_tag}[load][timeout] #{message}"
168
+ end
169
+ cancel_screenshot
170
+ if @view.loading?
171
+ close_id = @view.signal_connect("close") do
172
+ @view.signal_handler_disconnect(close_id)
173
+ @main_loop.quit
174
+ error do
175
+ "#{log_tag}[load][closed] #{@view.uri}"
176
+ end
177
+ end
178
+ @view.try_close
179
+ else
180
+ @main_loop.quit
181
+ end
182
+ GLib::Source::REMOVE
183
+ end
184
+
185
+ begin
186
+ yield
187
+ ensure
188
+ GLib::Source.remove(timeout_id) if timeout_id
189
+ end
190
+ end
191
+
192
+ def compute_timeout_second
193
+ default_timeout = 5
194
+ timeout_string =
195
+ ENV["CHUPA_TEXT_DECOMPOSER_WEBKIT_TIMEOUT"] || default_timeout.to_s
196
+ begin
197
+ Integer(timeout_string)
198
+ rescue ArgumentError
199
+ default_timeout
200
+ end
201
+ end
202
+
203
+ private
204
+ def log_tag
205
+ "[decomposer][webkit]"
206
+ end
207
+
208
+ def debug(*args, &block)
209
+ @logger.debug(*args, &block)
210
+ end
211
+
212
+ def error(*args, &block)
213
+ @logger.error(*args, &block)
214
+ end
215
+ end
216
+ end
@@ -14,7 +14,8 @@
14
14
  # License along with this library; if not, write to the Free Software
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
- require "webkit2-gtk"
17
+ require "English"
18
+ require "rbconfig"
18
19
 
19
20
  module ChupaText
20
21
  module Decomposers
@@ -57,161 +58,90 @@ module ChupaText
57
58
  false
58
59
  end
59
60
 
61
+ IN_PROCESS = ENV["CHUPA_TEXT_DECOMPOSER_WEBKIT_IN_PROCESS"] == "yes"
62
+ if IN_PROCESS
63
+ require "chupa-text-decomposer-webkit/screenshoter"
64
+ end
65
+
60
66
  def decompose(data)
61
- screenshoter = Screenshoter.new(data)
62
- screenshoter.run
67
+ body = data.source.body
68
+ uri = data.source.uri.to_s
69
+ output = Tempfile.new(["chupa-text-decomposer-webkit", ".png"])
70
+ width, height = data.expected_screenshot_size
71
+ if IN_PROCESS
72
+ screenshoter = ChupaTextDecomposerWebKit::Screenshoter.new(logger)
73
+ screenshoter.run(body, uri, output.path, width, height)
74
+ else
75
+ screenshoter = ExternalScreenshoter.new
76
+ screenshoter.run(data.source.path, uri, output.path, width, height)
77
+ end
78
+ unless File.size(output.path).zero?
79
+ png = output.read
80
+ data.screenshot = Screenshot.new("image/png",
81
+ [png].pack("m*"),
82
+ "base64")
83
+ end
63
84
  data[AVAILABLE_ATTRIBUTE_NAME] = !data.screenshot.nil?
64
85
  yield(data)
65
86
  end
66
87
 
67
- class Screenshoter
88
+ class ExternalScreenshoter
68
89
  include Loggable
69
90
  include LogTag
70
91
 
71
- def initialize(data)
72
- @data = data
73
- @@view_context ||= create_view_context
74
- @main_loop = GLib::MainLoop.new(nil, false)
75
- @timeout_second = compute_timeout_second
76
- end
77
-
78
- def run
79
- view = WebKit2Gtk::WebView.new(context: @@view_context)
80
- window = Gtk::OffscreenWindow.new
81
- window.set_default_size(800, 600)
82
- window.add(view)
83
- window.show_all
84
-
85
- setup_callbacks(view)
86
-
87
- timeout(view) do
88
- debug do
89
- "#{log_tag}[load][HTML] #{@data.uri}"
90
- end
91
- view.load_html(@data.source.body, @data.source.uri.to_s)
92
- @main_loop.run
93
- end
94
-
95
- window.destroy
96
- end
97
-
98
- private
99
- def create_view_context
100
- context = WebKit2Gtk::WebContext.new(ephemeral: true)
101
- http_proxy = ENV["http_proxy"]
102
- https_proxy = ENV["https_proxy"]
103
- ftp_proxy = ENV["ftp_proxy"]
104
- if http_proxy or https_proxy or ftp_proxy
105
- proxy_settings = WebKit2Gtk::NetworkProxySettings.new
106
- if http_proxy
107
- proxy_settings.add_proxy_for_scheme("http", http_proxy)
108
- end
109
- if https_proxy
110
- proxy_settings.add_proxy_for_scheme("https", https_proxy)
111
- end
112
- if ftp_proxy
113
- proxy_settings.add_proxy_for_scheme("ftp", ftp_proxy)
114
- end
115
- context.set_network_proxy_settings(:custom, proxy_settings)
116
- end
117
- context
92
+ def initialize
93
+ @screenshoter = File.join(__dir__,
94
+ "..",
95
+ "..",
96
+ "..",
97
+ "bin",
98
+ "chupa-text-decomposer-webkit-screenshoter")
99
+ @command = ExternalCommand.new(RbConfig.ruby)
118
100
  end
119
101
 
120
- def setup_callbacks(view)
121
- view.signal_connect("load-changed") do |_, load_event|
122
- debug do
123
- "#{log_tag}[load][#{load_event.nick}] #{view.uri}"
124
- end
125
-
126
- case load_event
127
- when WebKit2Gtk::LoadEvent::FINISHED
128
- debug do
129
- "#{log_tag}[screenshot][start] #{view.uri}"
130
- end
131
- view.get_snapshot(:full_document, :none) do |_, result|
132
- @main_loop.quit
133
- snapshot_surface = view.get_snapshot_finish(result)
134
- debug do
135
- size = "#{snapshot_surface.width}x#{snapshot_surface.height}"
136
- "#{log_tag}[screenshot][finish] #{view.uri}: #{size}"
137
- end
138
- unless snapshot_surface.width.zero?
139
- png = convert_snapshot_surface_to_png(snapshot_surface)
140
- @data.screenshot = Screenshot.new("image/png",
141
- [png].pack("m*"),
142
- "base64")
143
- end
102
+ def run(html_path, uri, output_path, width, height)
103
+ output_read, output_write = IO.pipe
104
+ error_output = Tempfile.new("chupa-text-decomposer-webkit-error")
105
+ output_reader = Thread.new do
106
+ loop do
107
+ IO.select([output_read])
108
+ line = output_read.gets
109
+ break if line.nil?
110
+
111
+ case line.chomp
112
+ when /\Adebug: /
113
+ debug($POSTMATCH)
114
+ when /\Aerror: /
115
+ error($POSTMATCH)
144
116
  end
145
117
  end
146
118
  end
147
- view.signal_connect("load-failed") do |_, _, failed_uri, error|
148
- @main_loop.quit
119
+ successed = @command.run(@screenshoter,
120
+ html_path,
121
+ uri,
122
+ output_path,
123
+ width.to_s,
124
+ height.to_s,
125
+ {
126
+ :spawn_options => {
127
+ :out => output_write,
128
+ :err => error_output.path,
129
+ },
130
+ })
131
+ output_write.close
132
+ output_reader.join
133
+
134
+ unless successed
149
135
  error do
150
- message = "failed to load URI: #{failed_uri}: "
151
- message << "#{error.class}(#{error.code}): #{error.message}"
152
- "#{log_tag}[load][failed] #{message}"
136
+ message = "failed to external screenshoter: #{uri}: "
137
+ message << "#{@command.path} #{@screenshoter}"
138
+ "#{log_tag}[external-screenshoter][run][failed] #{message}"
153
139
  end
154
- true
155
140
  end
156
- end
157
-
158
- def convert_snapshot_surface_to_png(snapshot_surface)
159
- screenshot_width, screenshot_height = @data.expected_screenshot_size
160
-
161
- screenshot_surface = Cairo::ImageSurface.new(:argb32,
162
- screenshot_width,
163
- screenshot_height)
164
- context = Cairo::Context.new(screenshot_surface)
165
- context.set_source_color(:white)
166
- context.paint
167
-
168
- ratio = screenshot_width.to_f / snapshot_surface.width
169
- context.scale(ratio, ratio)
170
- context.set_source(snapshot_surface)
171
- context.paint
172
-
173
- png = StringIO.new
174
- screenshot_surface.write_to_png(png)
175
- png.string
176
- end
177
-
178
- def timeout(view)
179
- timeout_id = GLib::Timeout.add_seconds(@timeout_second) do
180
- timeout_id = nil
181
- error do
182
- message = "timeout to load URI: #{@timeout_second}s: #{view.uri}"
183
- message << ": loading" if view.loading?
184
- "#{log_tag}[load][timeout] #{message}"
141
+ unless error_output.size.zero?
142
+ error_output.each_line do |line|
143
+ error(line)
185
144
  end
186
- if view.loading?
187
- view.signal_connect("close") do
188
- @main_loop.quit
189
- error do
190
- "#{log_tag}[load][closed] #{view.uri}"
191
- end
192
- end
193
- view.try_close
194
- else
195
- @main_loop.quit
196
- end
197
- GLib::Source::REMOVE
198
- end
199
-
200
- begin
201
- yield
202
- ensure
203
- GLib::Source.remove(timeout_id) if timeout_id
204
- end
205
- end
206
-
207
- def compute_timeout_second
208
- default_timeout = 5
209
- timeout_string =
210
- ENV["CHUPA_TEXT_DECOMPOSER_WEBKIT_TIMEOUT"] || default_timeout.to_s
211
- begin
212
- Integer(timeout_string)
213
- rescue ArgumentError
214
- default_timeout
215
145
  end
216
146
  end
217
147
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chupa-text-decomposer-webkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou
@@ -126,6 +126,7 @@ files:
126
126
  - Rakefile
127
127
  - chupa-text-decomposer-webkit.gemspec
128
128
  - doc/text/news.md
129
+ - lib/chupa-text-decomposer-webkit/screenshoter.rb
129
130
  - lib/chupa-text/decomposers/webkit.rb
130
131
  - test/run-test.rb
131
132
  - test/test-webkit.rb