-
-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathferrum_pdf.rb
More file actions
121 lines (107 loc) · 3.89 KB
/
ferrum_pdf.rb
File metadata and controls
121 lines (107 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
require "ferrum_pdf/version"
require "ferrum_pdf/railtie"
require "ferrum"
module FerrumPdf
DEFAULT_HEADER_TEMPLATE = "<div class='date text left'></div><div class='title text center'></div>"
DEFAULT_FOOTER_TEMPLATE = <<~HTML
<div class='url text left grow'></div>
<div class='text right'><span class='pageNumber'></span>/<span class='totalPages'></span></div>
HTML
autoload :AssetsHelper, "ferrum_pdf/assets_helper"
autoload :HTMLPreprocessor, "ferrum_pdf/html_preprocessor"
mattr_accessor :browser_mutex, default: Mutex.new
mattr_accessor :config, default: ActiveSupport::OrderedOptions.new.merge(
window_size: [ 1920, 1080 ]
)
# This doesn't use mattr_accessor because having a `.browser` getter and also
# having local variables named `browser` would be confusing. For simplicity,
# this is explicitly accessed.
@@browser = nil
class << self
def configure
yield config
end
# Sets the browser instance to use for all operations
# If a browser is already set, it will be shut down before setting the new one
def browser=(browser_instance)
browser_mutex.synchronize do
@@browser&.quit
@@browser = browser_instance
end
end
# Provides thread-safe access to the browser instance
def with_browser(browser = nil)
if browser
yield browser
else
browser_mutex.synchronize do
@@browser ||= Ferrum::Browser.new(config)
@@browser.restart unless @@browser.client.present?
yield @@browser
end
end
end
# Renders HTML or URL to PDF
#
# render_pdf(url: "https://example.org/receipts/example.pdf")
# render_pdf(html: "<h1>Hello world</h1>")
#
# For rendering HTML, we also need the base_url for preprocessing URLs with relative paths & protocols
#
# render_pdf(html: "<h1>Hello world</h1>", base_url: "https://example.org/")
#
def render_pdf(pdf_options: {}, **load_page_args)
load_page(**load_page_args) do |browser, page|
yield browser, page if block_given?
page.pdf(**pdf_options.with_defaults(encoding: :binary))
end
end
# Renders HTML or URL to Screenshot
#
# render_screenshot(url: "https://example.org/receipts/example.pdf")
# render_screenshot(html: "<h1>Hello world</h1>")
#
# For rendering HTML, we also need the base_url for preprocessing URLs with relative paths & protocols
#
# render_screenshot(html: "<h1>Hello world</h1>", base_url: "https://example.org/")
#
def render_screenshot(screenshot_options: {}, **load_page_args)
load_page(**load_page_args) do |browser, page|
yield browser, page if block_given?
page.screenshot(**screenshot_options.with_defaults(encoding: :binary, full: true))
end
end
# Loads page into the browser to be used for rendering PDFs or screenshots
#
# This automatically applies HTML preprocessing if `html:` is present
#
def load_page(url: nil, html: nil, base_url: nil, authorize: nil, wait_for_idle_options: nil, browser: nil, retries: 1)
try = 0
wait_for_idle_options ||= {}
with_browser(browser) do |browser|
# Closes page automatically after block finishes
# https://github.com/rubycdp/ferrum/blob/main/lib/ferrum/browser.rb#L169
browser.create_page do |page|
page.network.authorize(**authorize) { |req| req.continue } if authorize
# Load content
if html
page.content = FerrumPdf::HTMLPreprocessor.process(html, base_url)
else
page.go_to(url)
end
# Wait for everything to load
page.network.wait_for_idle(**wait_for_idle_options)
yield browser, page
end
end
rescue Ferrum::DeadBrowserError
try += 1
if try <= retries
with_browser(&:restart)
retry
else
raise
end
end
end
end