grover 0.7.4 → 0.8.1

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
  SHA256:
3
- metadata.gz: b65f94b265a2cff7033dd91d10ca2d1cad51c0b4e447e644d4ccdcbb8df14b30
4
- data.tar.gz: 170d807936eb67a6d805717545548ffc5650a200c6dd2a06cf503411d87d00d4
3
+ metadata.gz: 3a0324b73710864e876853e8de3ffe71974b5544015b1f602d203b4b409cb6fc
4
+ data.tar.gz: 7c27e1acd0b266a69f71c2c2af90d8f0c14ff103ccbc1a28ccf3248854081b56
5
5
  SHA512:
6
- metadata.gz: 841ac6e650d6427cece6e3c1be1d21ed433d42bb3b5c1366efafd4dff29244a9bba2f5ccc606d51888e537bcfb6dc2ddbd43d87a680e73cc300c3ca74ff4aa18
7
- data.tar.gz: e87b7f047c77048d5b8b35dfd38dadff2cb3e1c7d40df699ea09bb51edfd0282ca47119ba5c6e8562b7eef70416098ecc02dfd6a5837818ed673e7172bcafa69
6
+ metadata.gz: df27ae8e1291e8d071afdbc7d6eb249776481c245b8f663261bb199f385e712132b6647d6a3e75c46508aec8dc6ed03a8a1b17b10adaaf08154bf97deef5875f
7
+ data.tar.gz: 211d0e9eb2ac4d033e9a9bae46fc4319cf618e62f6c380e5f600f80968621bef40365bbebadf93b2451a7ea8964b6abda83b11278edc08426b952ec3e5f297db
@@ -5,11 +5,16 @@ class Grover
5
5
  # Configuration of the options for Grover HTML to PDF conversion
6
6
  #
7
7
  class Configuration
8
- attr_accessor :options, :meta_tag_prefix
8
+ attr_accessor :options, :meta_tag_prefix, :ignore_path,
9
+ :use_pdf_middleware, :use_png_middleware, :use_jpeg_middleware
9
10
 
10
11
  def initialize
11
12
  @options = {}
12
13
  @meta_tag_prefix = 'grover-'
14
+ @ignore_path = nil
15
+ @use_pdf_middleware = true
16
+ @use_png_middleware = false
17
+ @use_jpeg_middleware = false
13
18
  end
14
19
  end
15
20
  end
@@ -12,21 +12,18 @@ class Grover
12
12
  class Middleware
13
13
  def initialize(app)
14
14
  @app = app
15
- @render_pdf = false
15
+ @pdf_request = false
16
+ @png_request = false
17
+ @jpeg_request = false
16
18
  end
17
19
 
18
20
  def call(env)
19
21
  @request = Rack::Request.new(env)
20
- @render_pdf = pdf_request?
22
+ identify_request_type
21
23
 
22
- configure_env_for_pdf_request(env) if rendering_pdf?
24
+ configure_env_for_grover_request(env) if grover_request?
23
25
  status, headers, response = @app.call(env)
24
-
25
- if rendering_pdf? && html_content?(headers)
26
- pdf = convert_to_pdf response
27
- response = [pdf]
28
- update_headers headers, pdf
29
- end
26
+ response = update_response response, headers if grover_request? && html_content?(headers)
30
27
 
31
28
  [status, headers, response]
32
29
  end
@@ -34,21 +31,57 @@ class Grover
34
31
  private
35
32
 
36
33
  PDF_REGEX = /\.pdf$/i.freeze
34
+ PNG_REGEX = /\.png$/i.freeze
35
+ JPEG_REGEX = /\.jpe?g$/i.freeze
36
+
37
+ attr_reader :pdf_request, :png_request, :jpeg_request
38
+
39
+ def identify_request_type
40
+ @pdf_request = Grover.configuration.use_pdf_middleware && path_matches?(PDF_REGEX)
41
+ @png_request = Grover.configuration.use_png_middleware && path_matches?(PNG_REGEX)
42
+ @jpeg_request = Grover.configuration.use_jpeg_middleware && path_matches?(JPEG_REGEX)
43
+ end
37
44
 
38
- def rendering_pdf?
39
- @render_pdf
45
+ def path_matches?(regex)
46
+ !@request.path.match(regex).nil?
40
47
  end
41
48
 
42
- def pdf_request?
43
- !@request.path.match(PDF_REGEX).nil?
49
+ def grover_request?
50
+ (pdf_request || png_request || jpeg_request) && !ignore_request?
51
+ end
52
+
53
+ def ignore_request?
54
+ ignore_path = Grover.configuration.ignore_path
55
+ case ignore_path
56
+ when String then @request.path.start_with? ignore_path
57
+ when Regexp then !ignore_path.match(@request.path).nil?
58
+ when Proc then ignore_path.call @request.path
59
+ end
44
60
  end
45
61
 
46
62
  def html_content?(headers)
47
63
  headers['Content-Type'] =~ %r{text/html|application/xhtml\+xml}
48
64
  end
49
65
 
50
- def convert_to_pdf(response)
66
+ def update_response(response, headers)
67
+ body, content_type = convert_response response
68
+ assign_headers headers, body, content_type
69
+ [body]
70
+ end
71
+
72
+ def convert_response(response)
51
73
  grover = create_grover_for_response(response)
74
+
75
+ if pdf_request
76
+ [convert_to_pdf(grover), 'application/pdf']
77
+ elsif png_request
78
+ [grover.to_png, 'image/png']
79
+ elsif jpeg_request
80
+ [grover.to_jpeg, 'image/jpeg']
81
+ end
82
+ end
83
+
84
+ def convert_to_pdf(grover)
52
85
  if grover.show_front_cover? || grover.show_back_cover?
53
86
  add_cover_content grover
54
87
  else
@@ -80,16 +113,16 @@ class Grover
80
113
  CombinePDF.parse grover.to_pdf
81
114
  end
82
115
 
83
- def update_headers(headers, body)
84
- # Do not cache PDFs
116
+ def assign_headers(headers, body, content_type)
117
+ # Do not cache results
85
118
  headers.delete 'ETag'
86
119
  headers.delete 'Cache-Control'
87
120
 
88
121
  headers['Content-Length'] = (body.respond_to?(:bytesize) ? body.bytesize : body.size).to_s
89
- headers['Content-Type'] = 'application/pdf'
122
+ headers['Content-Type'] = content_type
90
123
  end
91
124
 
92
- def configure_env_for_pdf_request(env)
125
+ def configure_env_for_grover_request(env)
93
126
  env['PATH_INFO'] = env['REQUEST_URI'] = path_without_extension
94
127
  env['HTTP_ACCEPT'] = concat(env['HTTP_ACCEPT'], Rack::Mime.mime_type('.html'))
95
128
  env['Rack-Middleware-Grover'] = 'true'
@@ -108,7 +141,17 @@ class Grover
108
141
  end
109
142
 
110
143
  def path_without_extension
111
- @request.path.sub(PDF_REGEX, '').sub(@request.script_name, '')
144
+ @request.path.sub(request_regex, '').sub(@request.script_name, '')
145
+ end
146
+
147
+ def request_regex
148
+ if pdf_request
149
+ PDF_REGEX
150
+ elsif png_request
151
+ PNG_REGEX
152
+ elsif jpeg_request
153
+ JPEG_REGEX
154
+ end
112
155
  end
113
156
 
114
157
  def request_url
data/lib/grover/utils.rb CHANGED
@@ -22,17 +22,6 @@ class Grover
22
22
  gsub(/[[:space:]]+/, ' ')
23
23
  end
24
24
 
25
- #
26
- # Remove minimum spaces from the front of all lines within a string
27
- #
28
- # Based on active support
29
- # @see active_support/core_ext/string/strip.rb
30
- #
31
- def self.strip_heredoc(string, inline: false)
32
- string = string.gsub(/^#{string.scan(/^[ \t]*(?=\S)/).min}/, '')
33
- inline ? string.delete("\n") : string
34
- end
35
-
36
25
  #
37
26
  # Assign value to a hash using an array of keys to traverse
38
27
  #
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Grover
4
- VERSION = '0.7.4'
4
+ VERSION = '0.8.1'
5
5
  end
data/lib/grover.rb CHANGED
@@ -26,76 +26,87 @@ class Grover
26
26
  ENV['GROVER_NO_SANDBOX'] == 'true' ? "{args: ['--no-sandbox', '--disable-setuid-sandbox']}" : '{}'
27
27
  end
28
28
 
29
- method :convert_pdf, Utils.strip_heredoc(<<-FUNCTION)
30
- async (url_or_html, options) => {
31
- let browser;
32
- try {
33
- let launchParams = #{launch_params};
34
-
35
- // Configure puppeteer debugging options
36
- const debug = options.debug; delete options.debug;
37
- if (typeof debug === 'object' && !!debug) {
38
- if (debug.headless != undefined) { launchParams.headless = debug.headless; }
39
- if (debug.devtools != undefined) { launchParams.devtools = debug.devtools; }
40
- }
41
-
42
- // Launch the browser and create a page
43
- browser = await puppeteer.launch(launchParams);
44
- const page = await browser.newPage();
45
-
46
- // Set caching flag (if provided)
47
- const cache = options.cache; delete options.cache;
48
- if (cache != undefined) {
49
- await page.setCacheEnabled(cache);
50
- }
51
-
52
- // Setup timeout option (if provided)
53
- let request_options = {};
54
- const timeout = options.timeout; delete options.timeout;
55
- if (timeout != undefined) {
56
- request_options.timeout = timeout;
57
- }
58
-
59
- if (url_or_html.match(/^http/i)) {
60
- // Request is for a URL, so request it
61
- request_options.waitUntil = 'networkidle2';
62
- await page.goto(url_or_html, request_options);
63
- } else {
64
- // Request is some HTML content. Use request interception to assign the body
65
- request_options.waitUntil = 'networkidle0';
66
- await page.setRequestInterception(true);
67
- page.once('request', request => {
68
- request.respond({ body: url_or_html });
69
- // Reset the request interception
70
- // (we only want to intercept the first request - ie our HTML)
71
- page.on('request', request => request.continue());
72
- });
73
- const displayUrl = options.displayUrl; delete options.displayUrl;
74
- await page.goto(displayUrl || 'http://example.com', request_options);
75
- }
76
-
77
- // If specified, emulate the media type
78
- const emulateMedia = options.emulateMedia; delete options.emulateMedia;
79
- if (emulateMedia != undefined) {
80
- await page.emulateMedia(emulateMedia);
81
- }
82
-
83
- // If we're running puppeteer in headless mode, return the converted PDF
84
- if (debug == undefined || (typeof debug === 'object' && (debug.headless == undefined || debug.headless))) {
85
- return await page.pdf(options);
86
- }
87
- } finally {
88
- if (browser) {
89
- await browser.close();
29
+ def self.convert_function(convert_action)
30
+ <<~FUNCTION
31
+ async (url_or_html, options) => {
32
+ let browser;
33
+ try {
34
+ let launchParams = #{launch_params};
35
+
36
+ // Configure puppeteer debugging options
37
+ const debug = options.debug; delete options.debug;
38
+ if (typeof debug === 'object' && !!debug) {
39
+ if (debug.headless != undefined) { launchParams.headless = debug.headless; }
40
+ if (debug.devtools != undefined) { launchParams.devtools = debug.devtools; }
41
+ }
42
+
43
+ // Launch the browser and create a page
44
+ browser = await puppeteer.launch(launchParams);
45
+ const page = await browser.newPage();
46
+
47
+ // Set caching flag (if provided)
48
+ const cache = options.cache; delete options.cache;
49
+ if (cache != undefined) {
50
+ await page.setCacheEnabled(cache);
51
+ }
52
+
53
+ // Setup timeout option (if provided)
54
+ let request_options = {};
55
+ const timeout = options.timeout; delete options.timeout;
56
+ if (timeout != undefined) {
57
+ request_options.timeout = timeout;
58
+ }
59
+
60
+ // Setup viewport options (if provided)
61
+ const viewport = options.viewport; delete options.viewport;
62
+ if (viewport != undefined) {
63
+ await page.setViewport(viewport);
64
+ }
65
+
66
+ if (url_or_html.match(/^http/i)) {
67
+ // Request is for a URL, so request it
68
+ request_options.waitUntil = 'networkidle2';
69
+ await page.goto(url_or_html, request_options);
70
+ } else {
71
+ // Request is some HTML content. Use request interception to assign the body
72
+ request_options.waitUntil = 'networkidle0';
73
+ await page.setRequestInterception(true);
74
+ page.once('request', request => {
75
+ request.respond({ body: url_or_html });
76
+ // Reset the request interception
77
+ // (we only want to intercept the first request - ie our HTML)
78
+ page.on('request', request => request.continue());
79
+ });
80
+ const displayUrl = options.displayUrl; delete options.displayUrl;
81
+ await page.goto(displayUrl || 'http://example.com', request_options);
82
+ }
83
+
84
+ // If specified, emulate the media type
85
+ const emulateMedia = options.emulateMedia; delete options.emulateMedia;
86
+ if (emulateMedia != undefined) {
87
+ await page.emulateMedia(emulateMedia);
88
+ }
89
+
90
+ // If we're running puppeteer in headless mode, return the converted PDF
91
+ if (debug == undefined || (typeof debug === 'object' && (debug.headless == undefined || debug.headless))) {
92
+ return await page.#{convert_action}(options);
93
+ }
94
+ } finally {
95
+ if (browser) {
96
+ await browser.close();
97
+ }
90
98
  }
91
99
  }
92
- }
93
- FUNCTION
100
+ FUNCTION
101
+ end
102
+
103
+ method :convert_pdf, convert_function('pdf')
104
+ method :convert_screenshot, convert_function('screenshot')
94
105
  end
95
106
  private_constant :Processor
96
107
 
97
108
  DEFAULT_HEADER_TEMPLATE = "<div class='date text left'></div><div class='title text center'></div>"
98
- DEFAULT_FOOTER_TEMPLATE = Utils.strip_heredoc(<<-HTML).freeze
109
+ DEFAULT_FOOTER_TEMPLATE = <<~HTML
99
110
  <div class='url text left grow'></div>
100
111
  <div class='text right'><span class='pageNumber'></span>/<span class='totalPages'></span></div>
101
112
  HTML
@@ -123,12 +134,46 @@ class Grover
123
134
  # @return [String] The resulting PDF data
124
135
  #
125
136
  def to_pdf(path = nil)
126
- normalized_options = Utils.normalize_object @options
127
- normalized_options['path'] = path if path.is_a? ::String
128
- result = processor.convert_pdf @url, normalized_options
137
+ result = processor.convert_pdf @url, normalized_options(path: path)
129
138
  return unless result
130
139
 
131
- result['data'].pack('c*')
140
+ result['data'].pack('C*')
141
+ end
142
+
143
+ #
144
+ # Request URL with provided options and create screenshot
145
+ #
146
+ # @param [String] path Optional path to write the screenshot to
147
+ # @param [String] format Optional format of the screenshot
148
+ # @return [String] The resulting image data
149
+ #
150
+ def screenshot(path: nil, format: nil)
151
+ options = normalized_options(path: path)
152
+ options['type'] = format if format.is_a? ::String
153
+ result = processor.convert_screenshot @url, options
154
+ return unless result
155
+
156
+ result['data'].pack('C*')
157
+ end
158
+
159
+ #
160
+ # Request URL with provided options and create PNG
161
+ #
162
+ # @param [String] path Optional path to write the screenshot to
163
+ # @return [String] The resulting PNG data
164
+ #
165
+ def to_png(path = nil)
166
+ screenshot(path: path, format: 'png')
167
+ end
168
+
169
+ #
170
+ # Request URL with provided options and create JPEG
171
+ #
172
+ # @param [String] path Optional path to write the screenshot to
173
+ # @return [String] The resulting JPEG data
174
+ #
175
+ def to_jpeg(path = nil)
176
+ screenshot(path: path, format: 'jpeg')
132
177
  end
133
178
 
134
179
  #
@@ -232,4 +277,10 @@ class Grover
232
277
 
233
278
  options['scale'] = options['scale'].to_f
234
279
  end
280
+
281
+ def normalized_options(path:)
282
+ normalized_options = Utils.normalize_object @options
283
+ normalized_options['path'] = path if path.is_a? ::String
284
+ normalized_options
285
+ end
235
286
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bromwich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-09 00:00:00.000000000 Z
11
+ date: 2019-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: combine_pdf
@@ -52,20 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mini_magick
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.9'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: pdf-reader
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - "~>"
60
74
  - !ruby/object:Gem::Version
61
- version: '2.1'
75
+ version: '2.2'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
- version: '2.1'
82
+ version: '2.2'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rack-test
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -100,57 +114,57 @@ dependencies:
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '3.7'
117
+ version: '3.8'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '3.7'
124
+ version: '3.8'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rubocop
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '0.53'
131
+ version: '0.72'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '0.53'
138
+ version: '0.72'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rubocop-rspec
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '1.28'
145
+ version: '1.33'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '1.28'
152
+ version: '1.33'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: simplecov
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
157
  - - "~>"
144
158
  - !ruby/object:Gem::Version
145
- version: '0.15'
159
+ version: '0.17'
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
164
  - - "~>"
151
165
  - !ruby/object:Gem::Version
152
- version: '0.15'
153
- description: Transform HTML into PDFs using Google Puppeteer/Chromium
166
+ version: '0.17'
167
+ description: Transform HTML into PDF/PNG/JPEG using Google Puppeteer/Chromium
154
168
  email:
155
169
  - abromwich@studiosity.com
156
170
  executables: []
@@ -187,6 +201,6 @@ requirements: []
187
201
  rubygems_version: 3.0.3
188
202
  signing_key:
189
203
  specification_version: 4
190
- summary: A Ruby gem to transform HTML into PDFs wrapper the NodeJS Google Puppeteer
191
- driver for Chromium
204
+ summary: A Ruby gem to transform HTML into PDF, PNG or JPEG by wrapping the NodeJS
205
+ Google Puppeteer driver for Chromium
192
206
  test_files: []