ferrum_pdf 2.0.0 → 3.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ad1aec4cf5dc3fd72ee568cc2e963d14be0d1d7934efc082dba0ec92368fd16
4
- data.tar.gz: 80a623342478906fc8b2fff9ac7067218387271ac4416c3492f8a1d759c7ec0d
3
+ metadata.gz: a0c9bdbd12753b4efc5b29bb208908735218fc2614e45eb74e57b6623ca61688
4
+ data.tar.gz: cb96c86e8c45a34037db93555c9ac5d692f7ccc4d0f37c88e211e6dcb17f7aa1
5
5
  SHA512:
6
- metadata.gz: 3ffa22c676e622ee3361ef927c87f3ff91f73e74465f69c63252406cdd27714356fadd66c960b5731ede0262e7af7f4c1649704328768d4e89baab2c6031d508
7
- data.tar.gz: e7e1f120c6739e88927ee62dba5974ef480ca715e846835b1d307a16f577667733e53cc6a9afb84cf172616f319a134aeb26a290fb1a6790d1cef4555d2fb293
6
+ metadata.gz: b3e3efc16d9c2601e6ef725d9d1aa6f73d631c8dcfaff8a4aa76a2254e933d7205e33f00a98ac780c7cf0b99fa074e2d820abee42de372fb68966a00fb533faa
7
+ data.tar.gz: 292b03680170140e5e901f8e84128970cb88a17e17147b57142ad686edca9c31da24c327e8c733505e6188a50310cac7d112a3ce1f92f89ed50b301ec7a0cd9b
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # 📄 FerrumPdf
2
2
 
3
- PDFs & screentshots for Rails using [Ferrum](https://github.com/rubycdp/ferrum) & headless Chrome.
3
+ PDFs & screenshots for Rails using [Ferrum](https://github.com/rubycdp/ferrum) & headless Chrome.
4
4
 
5
5
  Inspired by [Grover](https://github.com/Studiosity/grover), but without the Node.js and puppeteer dependencies. 🎉
6
6
 
7
+ <img src="ferrum_pdf.png" alt="logo" style="width:450px;"/>
8
+
7
9
  ## Installation
8
10
 
9
11
  First, make sure Chrome is installed. Then run the following or add the gem to your Gemfile:
@@ -39,8 +41,8 @@ end
39
41
  You can also customize which template is rendered. This will render the template to string with `render_to_string` in Rails, then pass it along to Chrome. For example, you can add headers and footers using `pdf_options` and use a specific layout:
40
42
 
41
43
  ```ruby
42
- render ferrum_pdf: {
43
- pdf_options: {
44
+ def show
45
+ render ferrum_pdf: {
44
46
  display_header_footer: true,
45
47
  header_template: FerrumPdf::DEFAULT_HEADER_TEMPLATE,
46
48
  footer_template: FerrumPdf::DEFAULT_FOOTER_TEMPLATE
@@ -49,7 +51,7 @@ render ferrum_pdf: {
49
51
  template: "pdf",
50
52
  disposition: :inline,
51
53
  filename: "example.pdf"
52
- }
54
+ end
53
55
  ```
54
56
 
55
57
  #### Render PDFs
@@ -68,10 +70,11 @@ FerrumPdf.render_pdf(
68
70
  url: "https://example.com/page", # Provide a URL to the content
69
71
 
70
72
  html: content, # or provide HTML
71
- base_url: request.base_url, # Preprocesses `html` to convert relative paths and protocols. Example: "https://example.org"
73
+ display_url: request.original_url, # When supplying content via :html its best to give Chrome a hint of the current url so that it can process relative paths in the document. If you don't provide this, http://example.com will be used instead.
72
74
 
73
75
  authorize: { user: "username", password: "password" }, # Used for authenticating with basic auth
74
76
  wait_for_idle_options: { connections: 0, duration: 0.05, timeout: 5 }, # Used for setting network wait_for_idle options
77
+ timeout_if_open_connections: true,
75
78
 
76
79
  pdf_options: {
77
80
  landscape: false, # paper orientation
@@ -129,8 +132,8 @@ end
129
132
  You can also customize which template is rendered. This will render the template to string with `render_to_string` in Rails, then pass it along to Chrome.
130
133
 
131
134
  ```ruby
132
- render ferrum_screenshot: {
133
- screenshot_options: {
135
+ def show
136
+ render ferrum_screenshot: {
134
137
  format: "png" # or "jpeg"
135
138
  quality: nil # Integer 0-100 works for jpeg only
136
139
  full: true # Boolean whether you need full page screenshot or a viewport
@@ -140,10 +143,10 @@ render ferrum_screenshot: {
140
143
  background_color: nil # Ferrum::RGBA.new(0, 0, 0, 0.0)
141
144
  },
142
145
  layout: "example",
143
- template: "example"
146
+ template: "example",
144
147
  disposition: :inline,
145
148
  filename: "example.png"
146
- }
149
+ end
147
150
  ```
148
151
 
149
152
  See [Ferrum screenshot docs](https://github.com/rubycdp/ferrum?tab=readme-ov-file#screenshotoptions--string--integer) for the full set of options.
@@ -164,7 +167,7 @@ FerrumPdf.render_screenshot(
164
167
  url: "https://example.com/page", # Provide a URL to the content
165
168
 
166
169
  html: content, # or provide HTML
167
- base_url: request.base_url, # Preprocesses `html` to convert relative paths and protocols. Example: "https://example.org"
170
+ display_url: request.original_url, # When supplying content via :html its best to give Chrome a hint of the current url so that it can process relative paths in the document. If you don't provide this, http://example.com will be used instead.
168
171
 
169
172
  screenshot_options: {
170
173
  format: "png" # or "jpeg"
@@ -178,7 +181,27 @@ FerrumPdf.render_screenshot(
178
181
  )
179
182
  ```
180
183
 
181
- ## Configuring the Browser
184
+ ## Configuration
185
+
186
+ You can set default values for page loads, PDF renders, and screenshot renders with the configure block.
187
+
188
+ ```ruby
189
+ FerrumPdf.configure do |config|
190
+ config.page_options.authorize = { user: "username", password: "password" }
191
+ config.page_options.wait_for_idle_options = { timeout: 90 }
192
+ config.page_options.retries = 3
193
+
194
+ config.pdf_options.margin_top = 0.2
195
+ config.pdf_options.margin_bottom = 0.2
196
+ config.pdf_options.margin_left = 0.2
197
+ config.pdf_options.margin_right = 0.2
198
+
199
+ config.screenshot_options.format = :png
200
+ config.screenshot_options.full = false
201
+ end
202
+ ```
203
+
204
+ ### Configuring the Browser
182
205
 
183
206
  You can set the default browser options with the configure block.
184
207
 
@@ -211,7 +234,7 @@ RUN apt-get update && apt-get install gnupg wget -y && \
211
234
  rm -rf /var/lib/apt/lists/*
212
235
  ```
213
236
 
214
- ### Browser Management
237
+ #### Browser Management
215
238
 
216
239
  FerrumPdf uses a single browser instance per Ruby process that is automatically created when needed using your configuration settings:
217
240
 
@@ -1,11 +1,5 @@
1
1
  module FerrumPdf
2
2
  class Railtie < ::Rails::Railtie
3
- initializer "ferrum_pdf.assets_helper" do
4
- ActiveSupport.on_load(:action_view) do
5
- include FerrumPdf::AssetsHelper
6
- end
7
- end
8
-
9
3
  initializer "ferrum_pdf.controller" do
10
4
  ActiveSupport.on_load(:action_controller) do
11
5
  # render ferrum_pdf: { pdf options }, template: "whatever", disposition: :inline, filename: "example.pdf"
@@ -13,7 +7,7 @@ module FerrumPdf
13
7
  send_data_options = options.extract!(:disposition, :filename, :status)
14
8
  url = pdf_options.delete(:url)
15
9
  html = render_to_string(**options.with_defaults(formats: [ :html ])) if url.blank?
16
- pdf = FerrumPdf.render_pdf(html: html, base_url: request.base_url, url: url, pdf_options: pdf_options)
10
+ pdf = FerrumPdf.render_pdf(html: html, display_url: request.original_url, url: url, pdf_options: pdf_options)
17
11
  send_data(pdf, **send_data_options.with_defaults(type: :pdf))
18
12
  end
19
13
 
@@ -22,7 +16,7 @@ module FerrumPdf
22
16
  send_data_options = options.extract!(:disposition, :filename, :status)
23
17
  url = screenshot_options.delete(:url)
24
18
  html = render_to_string(**options.with_defaults(formats: [ :html ])) if url.blank?
25
- screenshot = FerrumPdf.render_screenshot(url: url, html: html, base_url: request.base_url, screenshot_options: screenshot_options)
19
+ screenshot = FerrumPdf.render_screenshot(url: url, html: html, display_url: request.original_url, screenshot_options: screenshot_options)
26
20
  send_data(screenshot, **send_data_options.with_defaults(type: screenshot_options.fetch(:format, :png)))
27
21
  end
28
22
  end
@@ -1,3 +1,3 @@
1
1
  module FerrumPdf
2
- VERSION = "2.0.0"
2
+ VERSION = "3.0.0"
3
3
  end
data/lib/ferrum_pdf.rb CHANGED
@@ -9,12 +9,12 @@ module FerrumPdf
9
9
  <div class='text right'><span class='pageNumber'></span>/<span class='totalPages'></span></div>
10
10
  HTML
11
11
 
12
- autoload :AssetsHelper, "ferrum_pdf/assets_helper"
13
- autoload :HTMLPreprocessor, "ferrum_pdf/html_preprocessor"
14
-
15
12
  mattr_accessor :browser_mutex, default: Mutex.new
16
13
  mattr_accessor :config, default: ActiveSupport::OrderedOptions.new.merge(
17
- window_size: [ 1920, 1080 ]
14
+ window_size: [ 1920, 1080 ],
15
+ page_options: ActiveSupport::OrderedOptions.new,
16
+ pdf_options: ActiveSupport::OrderedOptions.new,
17
+ screenshot_options: ActiveSupport::OrderedOptions.new
18
18
  )
19
19
 
20
20
  # This doesn't use mattr_accessor because having a `.browser` getter and also
@@ -42,7 +42,7 @@ module FerrumPdf
42
42
  yield browser
43
43
  else
44
44
  browser_mutex.synchronize do
45
- @@browser ||= Ferrum::Browser.new(config)
45
+ @@browser ||= Ferrum::Browser.new(config.except(:page_options, :pdf_options, :screenshot_options))
46
46
  @@browser.restart unless @@browser.client.present?
47
47
  yield @@browser
48
48
  end
@@ -54,14 +54,14 @@ module FerrumPdf
54
54
  # render_pdf(url: "https://example.org/receipts/example.pdf")
55
55
  # render_pdf(html: "<h1>Hello world</h1>")
56
56
  #
57
- # For rendering HTML, we also need the base_url for preprocessing URLs with relative paths & protocols
57
+ # For rendering HTML, we also need display_url for so that Chrome can interpret URLs with relative paths & protocols
58
58
  #
59
- # render_pdf(html: "<h1>Hello world</h1>", base_url: "https://example.org/")
59
+ # render_pdf(html: "<h1>Hello world</h1>", display_url: "https://example.org/hello_world")
60
60
  #
61
61
  def render_pdf(pdf_options: {}, **load_page_args)
62
62
  load_page(**load_page_args) do |browser, page|
63
63
  yield browser, page if block_given?
64
- page.pdf(**pdf_options.with_defaults(encoding: :binary))
64
+ page.pdf(**pdf_options.with_defaults(encoding: :binary, **config.pdf_options))
65
65
  end
66
66
  end
67
67
 
@@ -70,24 +70,25 @@ module FerrumPdf
70
70
  # render_screenshot(url: "https://example.org/receipts/example.pdf")
71
71
  # render_screenshot(html: "<h1>Hello world</h1>")
72
72
  #
73
- # For rendering HTML, we also need the base_url for preprocessing URLs with relative paths & protocols
73
+ # For rendering HTML, we also need display_url for so that Chrome can interpret URLs with relative paths & protocols
74
74
  #
75
- # render_screenshot(html: "<h1>Hello world</h1>", base_url: "https://example.org/")
75
+ # render_screenshot(html: "<h1>Hello world</h1>", display_url: "https://example.org/hello_world")
76
76
  #
77
77
  def render_screenshot(screenshot_options: {}, **load_page_args)
78
78
  load_page(**load_page_args) do |browser, page|
79
79
  yield browser, page if block_given?
80
- page.screenshot(**screenshot_options.with_defaults(encoding: :binary, full: true))
80
+ page.screenshot(**screenshot_options.with_defaults(encoding: :binary, full: true, **config.screenshot_options))
81
81
  end
82
82
  end
83
83
 
84
84
  # Loads page into the browser to be used for rendering PDFs or screenshots
85
85
  #
86
- # This automatically applies HTML preprocessing if `html:` is present
87
- #
88
- def load_page(url: nil, html: nil, base_url: nil, authorize: nil, wait_for_idle_options: nil, browser: nil, retries: 1)
89
- try = 0
90
- wait_for_idle_options ||= {}
86
+ def load_page(url: nil, html: nil, display_url: nil, authorize: nil, wait_for_idle_options: nil, timeout_if_open_connections: true, browser: nil, retries: nil)
87
+ try ||= 0
88
+ authorize ||= config.dig(:page_options, :authorize)
89
+ retries ||= config.page_options.fetch(:retries, 1)
90
+ wait_for_idle_options = config.page_options.fetch(:wait_for_idle_options, {}).merge(wait_for_idle_options || {})
91
+ timeout_if_open_connections ||= config.page_options.fetch(:timeout_if_open_connections, true)
91
92
 
92
93
  with_browser(browser) do |browser|
93
94
  # Closes page automatically after block finishes
@@ -97,18 +98,29 @@ module FerrumPdf
97
98
 
98
99
  # Load content
99
100
  if html
100
- page.content = FerrumPdf::HTMLPreprocessor.process(html, base_url)
101
+ html_intercepted = false
102
+ page.network.intercept
103
+ page.on(:request) do |request|
104
+ if html_intercepted
105
+ request.continue
106
+ else
107
+ html_intercepted = true
108
+ request.respond(body: html.blank? ? " " : html)
109
+ end
110
+ end
111
+ page.go_to(display_url || "http://example.com")
101
112
  else
102
113
  page.go_to(url)
103
114
  end
104
115
 
105
116
  # Wait for everything to load
106
- page.network.wait_for_idle(**wait_for_idle_options)
117
+ idle = page.network.wait_for_idle(**wait_for_idle_options)
118
+ raise Ferrum::TimeoutError if timeout_if_open_connections && !idle
107
119
 
108
120
  yield browser, page
109
121
  end
110
122
  end
111
- rescue Ferrum::DeadBrowserError
123
+ rescue Ferrum::DeadBrowserError, Ferrum::TimeoutError
112
124
  try += 1
113
125
  if try <= retries
114
126
  with_browser(&:restart)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ferrum_pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -49,11 +49,8 @@ files:
49
49
  - README.md
50
50
  - Rakefile
51
51
  - lib/ferrum_pdf.rb
52
- - lib/ferrum_pdf/assets_helper.rb
53
- - lib/ferrum_pdf/html_preprocessor.rb
54
52
  - lib/ferrum_pdf/railtie.rb
55
53
  - lib/ferrum_pdf/version.rb
56
- - lib/tasks/ferrum_pdf_tasks.rake
57
54
  homepage: https://github.com/excid3/ferrum_pdf
58
55
  licenses:
59
56
  - MIT
@@ -75,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
72
  - !ruby/object:Gem::Version
76
73
  version: '0'
77
74
  requirements: []
78
- rubygems_version: 3.7.0
75
+ rubygems_version: 3.7.2
79
76
  specification_version: 4
80
77
  summary: PDFs & screenshots for Rails using Ferrum & headless Chrome
81
78
  test_files: []
@@ -1,65 +0,0 @@
1
- module FerrumPdf
2
- class BaseAsset
3
- def initialize(asset)
4
- @asset = asset
5
- end
6
- end
7
-
8
- class PropshaftAsset < BaseAsset
9
- def content_type
10
- @asset.content_type.to_s
11
- end
12
-
13
- def content
14
- @asset.content
15
- end
16
- end
17
-
18
- class SprocketsAsset < BaseAsset
19
- def content_type
20
- @asset.content_type
21
- end
22
-
23
- def content
24
- @asset.source
25
- end
26
- end
27
-
28
- class AssetFinder
29
- class << self
30
- def find(path)
31
- if Rails.application.assets.respond_to?(:load_path)
32
- propshaft_asset(path)
33
- elsif Rails.application.assets.respond_to?(:find_asset)
34
- sprockets_asset(path)
35
- else
36
- nil
37
- end
38
- end
39
-
40
- def propshaft_asset(path)
41
- (asset = Rails.application.assets.load_path.find(path)) ? PropshaftAsset.new(asset) : nil
42
- end
43
-
44
- def sprockets_asset(path)
45
- (asset = Rails.application.assets.find_asset(path)) ? SprocketsAsset.new(asset) : nil
46
- end
47
- end
48
- end
49
-
50
- module AssetsHelper
51
- def ferrum_pdf_inline_stylesheet(path)
52
- (asset = AssetFinder.find(path)) ? "<style>#{asset.content}</style>".html_safe : nil
53
- end
54
-
55
- def ferrum_pdf_inline_javascript(path)
56
- (asset = AssetFinder.find(path)) ? "<script>#{asset.content}</script>".html_safe : nil
57
- end
58
-
59
- def ferrum_pdf_base64_asset(path)
60
- return nil unless (asset = AssetFinder.find(path))
61
-
62
- "data:#{asset.content_type};base64,#{Base64.encode64(asset.content).gsub(/\s+/, '')}"
63
- end
64
- end
65
- end
@@ -1,33 +0,0 @@
1
- module FerrumPdf
2
- # Helper module for preparing HTML for conversion
3
- #
4
- # Sourced from the PDFKit project
5
- # @see https://github.com/pdfkit/pdfkit
6
- module HTMLPreprocessor
7
- # Change relative paths to absolute, and relative protocols to absolute protocols
8
- #
9
- # process("Some HTML", "https://example.org")
10
- #
11
- def self.process(html, base_url)
12
- return html if base_url.blank?
13
-
14
- base_url += "/" unless base_url.end_with? "/"
15
- protocol = base_url.split("://").first
16
- html = translate_relative_paths(html, base_url) if base_url
17
- html = translate_relative_protocols(html, protocol) if protocol
18
- html
19
- end
20
-
21
- def self.translate_relative_paths(html, base_url)
22
- # Try out this regexp using rubular http://rubular.com/r/hiAxBNX7KE
23
- html.gsub(%r{(href|src)=(['"])/([^/"']([^"']*|[^"']*))?['"]}, "\\1=\\2#{base_url}\\3\\2")
24
- end
25
- private_class_method :translate_relative_paths
26
-
27
- def self.translate_relative_protocols(body, protocol)
28
- # Try out this regexp using rubular http://rubular.com/r/0Ohk0wFYxV
29
- body.gsub(%r{(href|src)=(['"])//([^"']*|[^"']*)['"]}, "\\1=\\2#{protocol}://\\3\\2")
30
- end
31
- private_class_method :translate_relative_protocols
32
- end
33
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :ferrum_pdf do
3
- # # Task goes here
4
- # end