bidi2pdf 0.1.5 → 0.1.7
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 +4 -4
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +61 -1
- data/README.md +47 -10
- data/docker/Dockerfile +11 -3
- data/docker/Dockerfile.chromedriver +4 -2
- data/docker/Dockerfile.slim +75 -0
- data/lib/bidi2pdf/bidi/browser_console_logger.rb +92 -0
- data/lib/bidi2pdf/bidi/browser_tab.rb +415 -39
- data/lib/bidi2pdf/bidi/client.rb +85 -23
- data/lib/bidi2pdf/bidi/command_manager.rb +46 -48
- data/lib/bidi2pdf/bidi/commands/base.rb +39 -1
- data/lib/bidi2pdf/bidi/commands/browser_remove_user_context.rb +27 -0
- data/lib/bidi2pdf/bidi/commands/browsing_context_print.rb +4 -0
- data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +5 -0
- data/lib/bidi2pdf/bidi/commands.rb +1 -0
- data/lib/bidi2pdf/bidi/event_manager.rb +1 -1
- data/lib/bidi2pdf/bidi/interceptor.rb +1 -1
- data/lib/bidi2pdf/bidi/js_logger_helper.rb +16 -0
- data/lib/bidi2pdf/bidi/logger_events.rb +66 -0
- data/lib/bidi2pdf/bidi/network_event.rb +40 -7
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rb +110 -0
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rb +53 -0
- data/lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb +125 -0
- data/lib/bidi2pdf/bidi/network_event_formatters.rb +11 -0
- data/lib/bidi2pdf/bidi/network_events.rb +46 -17
- data/lib/bidi2pdf/bidi/session.rb +120 -13
- data/lib/bidi2pdf/bidi/user_context.rb +62 -0
- data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +7 -7
- data/lib/bidi2pdf/chromedriver_manager.rb +48 -21
- data/lib/bidi2pdf/cli.rb +27 -3
- data/lib/bidi2pdf/dsl.rb +33 -0
- data/lib/bidi2pdf/launcher.rb +34 -2
- data/lib/bidi2pdf/notifications/event.rb +52 -0
- data/lib/bidi2pdf/notifications/instrumenter.rb +65 -0
- data/lib/bidi2pdf/notifications/logging_subscriber.rb +136 -0
- data/lib/bidi2pdf/notifications.rb +78 -0
- data/lib/bidi2pdf/session_runner.rb +49 -7
- data/lib/bidi2pdf/verbose_logger.rb +79 -0
- data/lib/bidi2pdf/version.rb +1 -1
- data/lib/bidi2pdf.rb +99 -5
- data/sig/bidi2pdf/bidi/client.rbs +1 -1
- metadata +45 -4
- data/lib/bidi2pdf/utils.rb +0 -15
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bidi2pdf
|
4
|
+
module Notifications
|
5
|
+
# for reuse within ActiveSupport::LogSubscriber
|
6
|
+
module LoggingSubscriberActions
|
7
|
+
def handle_response(event)
|
8
|
+
payload = event.payload
|
9
|
+
|
10
|
+
if payload[:error]
|
11
|
+
logger.error "Received error: #{payload[:error].inspect} for cmd: #{payload[:id] || "-"}"
|
12
|
+
elsif !payload[:handled]
|
13
|
+
Bidi2pdf.logger.warn "Unknown response: #{payload[:data].inspect}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_cmd(event)
|
18
|
+
logger.debug "Sending command: #{event.payload[:cmd].method_name} id: ##{event.payload[:cmd_payload][:id]}"
|
19
|
+
|
20
|
+
logger.debug1 do
|
21
|
+
payload = redact_sensitive_fields(event.payload[:cmd_payload])
|
22
|
+
"Sending command: #{payload.inspect} (#{event.duration.round(1)}ms)"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def send_cmd_and_wait(event)
|
27
|
+
return unless event.payload[:exception]
|
28
|
+
|
29
|
+
payload = redact_sensitive_fields(event.payload[:cmd]&.params || {})
|
30
|
+
logger.error "Error sending command: #{payload} (#{event.duration.round(1)}ms) - #{event.payload[:exception].inspect}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def session_close(event)
|
34
|
+
return unless event.payload[:error]
|
35
|
+
|
36
|
+
logger.error "Session close error: #{event.payload[:error].inspect}, attempt: #{event.payload[:attempt]}, retry: #{event.payload[:retry]}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# rubocop: disable Metrics/AbcSize
|
40
|
+
def network_event_received(event)
|
41
|
+
return unless logger.debug2?
|
42
|
+
|
43
|
+
msg = case event.payload[:method]
|
44
|
+
when "network.beforeRequestSent"
|
45
|
+
"Request url '#{event.payload[:url]}' started"
|
46
|
+
|
47
|
+
when "network.responseStarted"
|
48
|
+
nil
|
49
|
+
when "network.responseCompleted"
|
50
|
+
"Request url '#{event.payload[:url]}' completed"
|
51
|
+
when "network.fetchError"
|
52
|
+
"Request url '#{event.payload[:url]}' error."
|
53
|
+
else
|
54
|
+
"Unknown network event: #{event.payload[:method]} for url '#{event.payload[:url]}'"
|
55
|
+
end
|
56
|
+
|
57
|
+
logger.debug2 msg if msg
|
58
|
+
end
|
59
|
+
|
60
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
61
|
+
def network_idle(event)
|
62
|
+
return unless logger.info?
|
63
|
+
|
64
|
+
requests = event.payload[:requests]
|
65
|
+
transfered = requests.map { |request| request.bytes_received || 0 }.sum
|
66
|
+
status_counts = requests
|
67
|
+
.group_by { |evt| evt.http_status_code || 0 }
|
68
|
+
.transform_keys { |code| code.zero? || code.nil? ? "pending" : code.to_s }
|
69
|
+
.transform_values(&:count)
|
70
|
+
.map { |code, count| "#{code}: #{count}" }
|
71
|
+
.join(", ")
|
72
|
+
|
73
|
+
logger.info "Network was idle after #{event.duration.round(1)}ms, #{requests.size} requests, " \
|
74
|
+
"transferred #{transfered} bytes (status codes: #{status_counts})"
|
75
|
+
end
|
76
|
+
|
77
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
78
|
+
|
79
|
+
def page_loaded(event)
|
80
|
+
logger.info "Page loaded: #{event.duration.round(1)}ms"
|
81
|
+
end
|
82
|
+
|
83
|
+
def print(event)
|
84
|
+
logger.info "Page printed: #{event.duration.round(1)}ms"
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def redact_sensitive_fields(obj, sensitive_keys = %w[value token password authorization username])
|
90
|
+
case obj
|
91
|
+
when Hash
|
92
|
+
obj.transform_values.with_index do |v, idx|
|
93
|
+
k = obj.keys[idx]
|
94
|
+
sensitive_keys.include?(k.to_s.downcase) ? "[REDACTED]" : redact_sensitive_fields(v, sensitive_keys)
|
95
|
+
end
|
96
|
+
when Array
|
97
|
+
obj.map { |item| redact_sensitive_fields(item, sensitive_keys) }
|
98
|
+
else
|
99
|
+
obj
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class LoggingSubscriber
|
105
|
+
include LoggingSubscriberActions
|
106
|
+
|
107
|
+
attr_accessor :logger
|
108
|
+
|
109
|
+
# rubocop: disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
110
|
+
def initialize(logger: Logger.new($stdout))
|
111
|
+
@logger = logger
|
112
|
+
Bidi2pdf.notification_service&.subscribe("handle_response.bidi2pdf", &method(:handle_response))
|
113
|
+
Bidi2pdf.notification_service&.subscribe("send_cmd.bidi2pdf", &method(:send_cmd))
|
114
|
+
Bidi2pdf.notification_service&.subscribe("send_cmd_and_wait.bidi2pdf", &method(:send_cmd_and_wait))
|
115
|
+
Bidi2pdf.notification_service&.subscribe("session_close.bidi2pdf", &method(:session_close))
|
116
|
+
Bidi2pdf.notification_service&.subscribe("network_idle.bidi2pdf", &method(:network_idle))
|
117
|
+
Bidi2pdf.notification_service&.subscribe("page_loaded.bidi2pdf", &method(:page_loaded))
|
118
|
+
Bidi2pdf.notification_service&.subscribe("network_event_received.bidi2pdf", &method(:network_event_received))
|
119
|
+
Bidi2pdf.notification_service&.subscribe("print.bidi2pdf", &method(:network_event_received))
|
120
|
+
end
|
121
|
+
|
122
|
+
def unsubscribe
|
123
|
+
Bidi2pdf.notification_service&.unsubscribe("handle_response.bidi2pdf", &method(:handle_response))
|
124
|
+
Bidi2pdf.notification_service&.unsubscribe("send_cmd.bidi2pdf", &method(:send_cmd))
|
125
|
+
Bidi2pdf.notification_service&.unsubscribe("send_cmd_and_wait.bidi2pdf", &method(:send_cmd_and_wait))
|
126
|
+
Bidi2pdf.notification_service&.unsubscribe("session_close.bidi2pdf", &method(:session_close))
|
127
|
+
Bidi2pdf.notification_service&.unsubscribe("network_idle.bidi2pdf", &method(:network_idle))
|
128
|
+
Bidi2pdf.notification_service&.unsubscribe("page_loaded.bidi2pdf", &method(:page_loaded))
|
129
|
+
Bidi2pdf.notification_service&.unsubscribe("network_event_received.bidi2pdf", &method(:network_event_received))
|
130
|
+
Bidi2pdf.notification_service&.unsubscribe("print.bidi2pdf", &method(:network_event_received))
|
131
|
+
end
|
132
|
+
|
133
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "chromedriver_manager"
|
4
|
+
require_relative "session_runner"
|
5
|
+
require_relative "bidi/session"
|
6
|
+
require_relative "notifications/event"
|
7
|
+
require_relative "notifications/instrumenter"
|
8
|
+
|
9
|
+
require "securerandom"
|
10
|
+
|
11
|
+
module Bidi2pdf
|
12
|
+
# This module provides a way to instrument events in the Bidi2pdf library.
|
13
|
+
# It it's heavyly inspired by ActiveSupport::Notifications.
|
14
|
+
# and thought to be used in a similar way.
|
15
|
+
# In Rails environment, ActiveSupport::Notifications should be used instead.
|
16
|
+
# via configuration: config.notification_service = ActiveSupport::Notifications
|
17
|
+
|
18
|
+
module Notifications
|
19
|
+
Thread.attr_accessor :bidi2pdf_notification_instrumenter
|
20
|
+
|
21
|
+
@subscribers = Hash.new { |h, k| h[k] = [] }
|
22
|
+
|
23
|
+
class << self
|
24
|
+
attr_reader :subscribers
|
25
|
+
|
26
|
+
def instrument(name, payload = {})
|
27
|
+
payload = payload.dup
|
28
|
+
|
29
|
+
if listening?(name)
|
30
|
+
notify(name, payload) { yield payload if block_given? }
|
31
|
+
elsif block_given?
|
32
|
+
yield payload
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def subscribe(event_pattern, &block)
|
37
|
+
pattern = normalize_pattern(event_pattern)
|
38
|
+
|
39
|
+
@subscribers[pattern] << block
|
40
|
+
|
41
|
+
block
|
42
|
+
end
|
43
|
+
|
44
|
+
def unsubscribe(event_pattern, block = nil)
|
45
|
+
pattern = normalize_pattern(event_pattern)
|
46
|
+
|
47
|
+
if block
|
48
|
+
@subscribers[pattern].delete(block)
|
49
|
+
else
|
50
|
+
@subscribers[pattern].clear
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# rubocop: disable Style/CaseEquality
|
55
|
+
def listening?(name)
|
56
|
+
@subscribers.any? do |pattern, blocks|
|
57
|
+
pattern === name && blocks.any?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# rubocop: enable Style/CaseEquality
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def bidi2pdf_notification_instrumenter = Thread.current.bidi2pdf_notification_instrumenter ||= Instrumenter.new
|
66
|
+
|
67
|
+
def notify(name, payload, &) = bidi2pdf_notification_instrumenter.notify(name, payload, &)
|
68
|
+
|
69
|
+
def normalize_pattern(pat)
|
70
|
+
case pat
|
71
|
+
when String, Regexp then pat
|
72
|
+
else
|
73
|
+
raise ArgumentError, "Pattern must be String or Regexp"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -1,9 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Bidi2pdf
|
4
|
+
# Represents a runner for managing browser sessions and executing tasks
|
5
|
+
# using the Bidi2pdf library. This class handles the setup, configuration,
|
6
|
+
# and execution of browser-related workflows, including navigation, cookie
|
7
|
+
# management, and printing.
|
8
|
+
#
|
9
|
+
# @example Running a session
|
10
|
+
# session_runner = Bidi2pdf::SessionRunner.new(
|
11
|
+
# session: session,
|
12
|
+
# url: "http://example.com",
|
13
|
+
# inputfile: "input.html",
|
14
|
+
# output: "output.pdf",
|
15
|
+
# cookies: { "key" => "value" },
|
16
|
+
# headers: { "Authorization" => "Bearer token" },
|
17
|
+
# auth: { username: "user", password: "pass" },
|
18
|
+
# wait_window_loaded: true,
|
19
|
+
# wait_network_idle: true,
|
20
|
+
# print_options: { landscape: true },
|
21
|
+
# network_log_format: :json
|
22
|
+
# )
|
23
|
+
# session_runner.run
|
24
|
+
#
|
25
|
+
# @param [Object] session The browser session object to use.
|
26
|
+
# @param [String, nil] url The URL to navigate to in the browser session.
|
27
|
+
# @param [String, nil] inputfile The path to the input file to be processed if no URL is provided.
|
28
|
+
# @param [String, nil] output The path to the output file to be generated.
|
29
|
+
# @param [Hash] cookies A hash of cookies to set in the browser session. Defaults to an empty hash.
|
30
|
+
# @param [Hash] headers A hash of HTTP headers to include in the browser session. Defaults to an empty hash.
|
31
|
+
# @param [Hash, nil] auth Authentication credentials (e.g., username and password). Defaults to an empty hash.
|
32
|
+
# @param [Boolean] wait_window_loaded Whether to wait for the window to fully load. Defaults to false.
|
33
|
+
# @param [Boolean] wait_network_idle Whether to wait for the network to become idle. Defaults to false.
|
34
|
+
# @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
|
35
|
+
# @param [Symbol] network_log_format The format for network logs. Defaults to :console.
|
4
36
|
class SessionRunner
|
37
|
+
# rubocop: disable Metrics/ParameterLists
|
5
38
|
def initialize(session:, url:, inputfile:, output:, cookies: {}, headers: {}, auth: {}, wait_window_loaded: false,
|
6
|
-
wait_network_idle: false, print_options: {})
|
39
|
+
wait_network_idle: false, print_options: {}, network_log_format: :console)
|
7
40
|
@session = session
|
8
41
|
@url = url
|
9
42
|
@inputfile = inputfile
|
@@ -14,8 +47,11 @@ module Bidi2pdf
|
|
14
47
|
@wait_window_loaded = wait_window_loaded
|
15
48
|
@wait_network_idle = wait_network_idle
|
16
49
|
@print_options = print_options || {}
|
50
|
+
@network_log_format = network_log_format
|
17
51
|
end
|
18
52
|
|
53
|
+
# rubocop: enable Metrics/ParameterLists
|
54
|
+
|
19
55
|
def run
|
20
56
|
@session.start
|
21
57
|
@session.client.on_close { Bidi2pdf.logger.info "WebSocket closed" }
|
@@ -35,6 +71,7 @@ module Bidi2pdf
|
|
35
71
|
|
36
72
|
@window = window
|
37
73
|
@tab = tab
|
74
|
+
@user_context = user_context
|
38
75
|
|
39
76
|
add_cookies(tab)
|
40
77
|
|
@@ -72,36 +109,41 @@ module Bidi2pdf
|
|
72
109
|
)
|
73
110
|
end
|
74
111
|
|
112
|
+
# rubocop: disable Metrics/AbcSize
|
75
113
|
def run_flow
|
76
114
|
@session.status
|
77
115
|
@session.user_contexts
|
78
116
|
|
79
117
|
if @url
|
80
|
-
@tab.
|
118
|
+
@tab.navigate_to(@url)
|
81
119
|
else
|
82
120
|
Bidi2pdf.logger.info "Loading HTML file #{@inputfile}"
|
83
121
|
data = File.read(@inputfile)
|
84
|
-
@tab.
|
122
|
+
@tab.render_html_content(data)
|
85
123
|
end
|
86
124
|
|
87
125
|
if @wait_network_idle
|
88
126
|
Bidi2pdf.logger.info "Waiting for network idle"
|
89
|
-
@tab.
|
127
|
+
@tab.wait_until_network_idle
|
90
128
|
end
|
91
129
|
|
130
|
+
log_output_file = (@output || "report").sub(/\.pdf$/, "-network.pdf") # only need, when html output
|
131
|
+
@tab.log_network_traffic format: @network_log_format, output: log_output_file
|
132
|
+
|
92
133
|
if @wait_window_loaded
|
93
134
|
Bidi2pdf.logger.info "Waiting for window to be loaded"
|
94
|
-
@tab.
|
95
|
-
new Promise(resolve => { const check = () => window.loaded ? resolve('done') : setTimeout(check, 100); check(); });
|
96
|
-
EOF_SCRIPT
|
135
|
+
@tab.wait_until_page_loaded
|
97
136
|
end
|
98
137
|
|
99
138
|
@tab.print(@output, print_options: @print_options)
|
100
139
|
ensure
|
101
140
|
@tab.close
|
102
141
|
@window.close
|
142
|
+
@user_context.close
|
103
143
|
end
|
104
144
|
|
145
|
+
# rubocop: enable Metrics/AbcSize
|
146
|
+
|
105
147
|
def uri
|
106
148
|
@uri ||= URI(@url)
|
107
149
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bidi2pdf
|
4
|
+
class VerboseLogger < SimpleDelegator
|
5
|
+
VERBOSITY_LEVELS = {
|
6
|
+
none: 0,
|
7
|
+
low: 1,
|
8
|
+
medium: 2,
|
9
|
+
high: 3
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
attr_reader :logger, :verbosity
|
13
|
+
|
14
|
+
def initialize(logger, verbosity = :low)
|
15
|
+
super(logger)
|
16
|
+
self.verbosity = verbosity
|
17
|
+
@logger = logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def verbosity=(verbosity)
|
21
|
+
min_verbosity = VERBOSITY_LEVELS.values.min
|
22
|
+
|
23
|
+
@verbosity = if verbosity.is_a?(Numeric)
|
24
|
+
verbosity = verbosity.to_i
|
25
|
+
max_verbosity = VERBOSITY_LEVELS.values.max
|
26
|
+
|
27
|
+
verbosity.clamp(min_verbosity, max_verbosity)
|
28
|
+
else
|
29
|
+
VERBOSITY_LEVELS.fetch verbosity.to_sym, min_verbosity
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def verbosity_sym
|
34
|
+
VERBOSITY_LEVELS.find { |_, v| v == verbosity }.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def debug1(progname = nil, &)
|
38
|
+
return unless debug1?
|
39
|
+
|
40
|
+
logger.debug("[D1] #{progname}", &)
|
41
|
+
end
|
42
|
+
|
43
|
+
def debug1?
|
44
|
+
verbosity >= 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug1!
|
48
|
+
@verbosity = VERBOSITY_LEVELS[:high]
|
49
|
+
end
|
50
|
+
|
51
|
+
def debug2(progname = nil, &)
|
52
|
+
return unless debug2?
|
53
|
+
|
54
|
+
logger.debug("[D2] #{progname}", &)
|
55
|
+
end
|
56
|
+
|
57
|
+
def debug2?
|
58
|
+
verbosity >= 2
|
59
|
+
end
|
60
|
+
|
61
|
+
def debug2!
|
62
|
+
@verbosity = VERBOSITY_LEVELS[:high]
|
63
|
+
end
|
64
|
+
|
65
|
+
def debug3(progname = nil, &)
|
66
|
+
return unless debug3?
|
67
|
+
|
68
|
+
logger.debug("[D3] #{progname}", &)
|
69
|
+
end
|
70
|
+
|
71
|
+
def debug3?
|
72
|
+
verbosity >= 3
|
73
|
+
end
|
74
|
+
|
75
|
+
def debug3!
|
76
|
+
@verbosity = VERBOSITY_LEVELS[:high]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/bidi2pdf/version.rb
CHANGED
data/lib/bidi2pdf.rb
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "bidi2pdf/utils"
|
4
3
|
require_relative "bidi2pdf/process_tree"
|
5
4
|
require_relative "bidi2pdf/launcher"
|
6
5
|
require_relative "bidi2pdf/bidi/session"
|
7
6
|
require_relative "bidi2pdf/dsl"
|
7
|
+
require_relative "bidi2pdf/notifications"
|
8
|
+
require_relative "bidi2pdf/notifications/logging_subscriber"
|
9
|
+
require_relative "bidi2pdf/verbose_logger"
|
8
10
|
|
9
11
|
require "logger"
|
10
12
|
|
11
13
|
module Bidi2pdf
|
14
|
+
PAPER_FORMATS_CM = {
|
15
|
+
letter: { width: 21.59, height: 27.94 },
|
16
|
+
legal: { width: 21.59, height: 35.56 },
|
17
|
+
tabloid: { width: 27.94, height: 43.18 },
|
18
|
+
ledger: { width: 43.18, height: 27.94 },
|
19
|
+
a0: { width: 84.1, height: 118.9 },
|
20
|
+
a1: { width: 59.4, height: 84.1 },
|
21
|
+
a2: { width: 42.0, height: 59.4 },
|
22
|
+
a3: { width: 29.7, height: 42.0 },
|
23
|
+
a4: { width: 21.0, height: 29.7 },
|
24
|
+
a5: { width: 14.8, height: 21.0 },
|
25
|
+
a6: { width: 10.5, height: 14.8 }
|
26
|
+
}.freeze
|
27
|
+
|
12
28
|
class Error < StandardError; end
|
13
29
|
|
14
30
|
class SessionNotStartedError < Error; end
|
@@ -25,17 +41,95 @@ module Bidi2pdf
|
|
25
41
|
|
26
42
|
class PrintError < Error; end
|
27
43
|
|
28
|
-
|
29
|
-
|
44
|
+
class ScriptInjectionError < Error; end
|
45
|
+
|
46
|
+
class StyleInjectionError < Error; end
|
47
|
+
|
48
|
+
class NotificationsError < Error
|
49
|
+
attr_reader :causes
|
30
50
|
|
31
|
-
|
51
|
+
def initialize(causes)
|
52
|
+
@causes = causes
|
53
|
+
exception_class_names = causes.map { |e| e.class.name }
|
54
|
+
super("Notifications errors: #{exception_class_names.join(", ")}")
|
55
|
+
end
|
56
|
+
end
|
32
57
|
|
33
58
|
class << self
|
34
|
-
attr_accessor :
|
59
|
+
attr_accessor :default_timeout, :enable_default_logging_subscriber
|
60
|
+
attr_reader :logging_subscriber, :logger, :network_events_logger, :browser_console_logger, :notification_service
|
35
61
|
|
36
62
|
# Allow configuration through a block
|
37
63
|
def configure
|
38
64
|
yield self if block_given?
|
65
|
+
|
66
|
+
init
|
67
|
+
end
|
68
|
+
|
69
|
+
def init
|
70
|
+
self.logging_subscriber = (Notifications::LoggingSubscriber.new(logger: logger) if enable_default_logging_subscriber)
|
71
|
+
begin
|
72
|
+
require "websocket-native"
|
73
|
+
|
74
|
+
logger.debug "websocket-native available; use enhance performance."
|
75
|
+
rescue LoadError => e
|
76
|
+
raise unless e.message =~ /websocket-native/
|
77
|
+
|
78
|
+
logger.warn "websocket-native not available; installing it may enhance performance."
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def translate_paper_format(format)
|
83
|
+
format = format.to_s.downcase.to_sym
|
84
|
+
|
85
|
+
dim = PAPER_FORMATS_CM[format]
|
86
|
+
|
87
|
+
raise ArgumentError, "Invalid paper format: #{format}" unless dim
|
88
|
+
|
89
|
+
width = dim[:width] || 0
|
90
|
+
height = dim[:height] || 0
|
91
|
+
|
92
|
+
{ width: width, height: height }
|
93
|
+
end
|
94
|
+
|
95
|
+
def logger=(new_logger)
|
96
|
+
@logger = Bidi2pdf::VerboseLogger.new new_logger
|
97
|
+
end
|
98
|
+
|
99
|
+
def network_events_logger=(new_network_events_logger)
|
100
|
+
@network_events_logger = Bidi2pdf::VerboseLogger.new(new_network_events_logger)
|
101
|
+
end
|
102
|
+
|
103
|
+
def browser_console_logger=(new_browser_console_logger)
|
104
|
+
@browser_console_logger = Bidi2pdf::VerboseLogger.new(new_browser_console_logger)
|
39
105
|
end
|
106
|
+
|
107
|
+
def logging_subscriber=(new_logging_subscriber)
|
108
|
+
@logging_subscriber&.unsubscribe
|
109
|
+
@logging_subscriber = new_logging_subscriber
|
110
|
+
end
|
111
|
+
|
112
|
+
def notification_service=(new_notification_service)
|
113
|
+
@logging_subscriber&.unsubscribe
|
114
|
+
|
115
|
+
@notification_service = new_notification_service
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
configure do |config|
|
120
|
+
config.logger = Logger.new($stdout)
|
121
|
+
config.logger.level = Logger::INFO
|
122
|
+
|
123
|
+
config.network_events_logger = Logger.new($stdout)
|
124
|
+
config.network_events_logger.level = Logger::FATAL
|
125
|
+
|
126
|
+
config.browser_console_logger = Logger.new($stdout)
|
127
|
+
config.browser_console_logger.level = Logger::WARN
|
128
|
+
|
129
|
+
config.enable_default_logging_subscriber = true
|
130
|
+
|
131
|
+
config.default_timeout = 60
|
132
|
+
|
133
|
+
config.notification_service = Notifications
|
40
134
|
end
|
41
135
|
end
|
@@ -34,7 +34,7 @@ module Bidi2pdf
|
|
34
34
|
|
35
35
|
def wait_until_open: (?timeout: untyped) -> untyped
|
36
36
|
|
37
|
-
def send_cmd: (
|
37
|
+
def send_cmd: (Bidi2pdf::Bidi::Commands::Base cmd) -> untyped
|
38
38
|
|
39
39
|
# rubocop:disable Metrics/AbcSize
|
40
40
|
def send_cmd_and_wait: (untyped method, ?::Hash[untyped, untyped] params, ?timeout: untyped) ?{ (untyped) -> untyped } -> untyped
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bidi2pdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dieter S.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '3.0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rspec-benchmark
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0.6'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0.6'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: rubocop
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -276,6 +290,20 @@ dependencies:
|
|
276
290
|
- - "~>"
|
277
291
|
- !ruby/object:Gem::Version
|
278
292
|
version: '1.4'
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: websocket-native
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - "~>"
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '1.0'
|
300
|
+
type: :development
|
301
|
+
prerelease: false
|
302
|
+
version_requirements: !ruby/object:Gem::Requirement
|
303
|
+
requirements:
|
304
|
+
- - "~>"
|
305
|
+
- !ruby/object:Gem::Version
|
306
|
+
version: '1.0'
|
279
307
|
description: |
|
280
308
|
Bidi2pdf is a powerful PDF generation tool that uses Chrome's BiDirectional Protocol
|
281
309
|
to render web pages as high-quality PDF documents. It offers:
|
@@ -310,6 +338,7 @@ files:
|
|
310
338
|
- cliff.toml
|
311
339
|
- docker/Dockerfile
|
312
340
|
- docker/Dockerfile.chromedriver
|
341
|
+
- docker/Dockerfile.slim
|
313
342
|
- docker/docker-compose.yml
|
314
343
|
- docker/entrypoint.sh
|
315
344
|
- docker/nginx/default.conf
|
@@ -319,6 +348,7 @@ files:
|
|
319
348
|
- lib/bidi2pdf/bidi/add_headers_interceptor.rb
|
320
349
|
- lib/bidi2pdf/bidi/auth_interceptor.rb
|
321
350
|
- lib/bidi2pdf/bidi/browser.rb
|
351
|
+
- lib/bidi2pdf/bidi/browser_console_logger.rb
|
322
352
|
- lib/bidi2pdf/bidi/browser_tab.rb
|
323
353
|
- lib/bidi2pdf/bidi/client.rb
|
324
354
|
- lib/bidi2pdf/bidi/command_manager.rb
|
@@ -327,6 +357,7 @@ files:
|
|
327
357
|
- lib/bidi2pdf/bidi/commands/base.rb
|
328
358
|
- lib/bidi2pdf/bidi/commands/browser_close.rb
|
329
359
|
- lib/bidi2pdf/bidi/commands/browser_create_user_context.rb
|
360
|
+
- lib/bidi2pdf/bidi/commands/browser_remove_user_context.rb
|
330
361
|
- lib/bidi2pdf/bidi/commands/browsing_context_close.rb
|
331
362
|
- lib/bidi2pdf/bidi/commands/browsing_context_navigate.rb
|
332
363
|
- lib/bidi2pdf/bidi/commands/browsing_context_print.rb
|
@@ -346,7 +377,13 @@ files:
|
|
346
377
|
- lib/bidi2pdf/bidi/connection_manager.rb
|
347
378
|
- lib/bidi2pdf/bidi/event_manager.rb
|
348
379
|
- lib/bidi2pdf/bidi/interceptor.rb
|
380
|
+
- lib/bidi2pdf/bidi/js_logger_helper.rb
|
381
|
+
- lib/bidi2pdf/bidi/logger_events.rb
|
349
382
|
- lib/bidi2pdf/bidi/network_event.rb
|
383
|
+
- lib/bidi2pdf/bidi/network_event_formatters.rb
|
384
|
+
- lib/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rb
|
385
|
+
- lib/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rb
|
386
|
+
- lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb
|
350
387
|
- lib/bidi2pdf/bidi/network_events.rb
|
351
388
|
- lib/bidi2pdf/bidi/session.rb
|
352
389
|
- lib/bidi2pdf/bidi/user_context.rb
|
@@ -355,9 +392,13 @@ files:
|
|
355
392
|
- lib/bidi2pdf/cli.rb
|
356
393
|
- lib/bidi2pdf/dsl.rb
|
357
394
|
- lib/bidi2pdf/launcher.rb
|
395
|
+
- lib/bidi2pdf/notifications.rb
|
396
|
+
- lib/bidi2pdf/notifications/event.rb
|
397
|
+
- lib/bidi2pdf/notifications/instrumenter.rb
|
398
|
+
- lib/bidi2pdf/notifications/logging_subscriber.rb
|
358
399
|
- lib/bidi2pdf/process_tree.rb
|
359
400
|
- lib/bidi2pdf/session_runner.rb
|
360
|
-
- lib/bidi2pdf/
|
401
|
+
- lib/bidi2pdf/verbose_logger.rb
|
361
402
|
- lib/bidi2pdf/version.rb
|
362
403
|
- sig/bidi2pdf.rbs
|
363
404
|
- sig/bidi2pdf/bidi/add_headers_interceptor.rbs
|
@@ -423,7 +464,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
423
464
|
requirements:
|
424
465
|
- - ">="
|
425
466
|
- !ruby/object:Gem::Version
|
426
|
-
version: 3.
|
467
|
+
version: 3.3.0
|
427
468
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
428
469
|
requirements:
|
429
470
|
- - ">="
|
data/lib/bidi2pdf/utils.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Bidi2pdf
|
4
|
-
module Utils
|
5
|
-
def timed(operation_name)
|
6
|
-
start_time = Time.now
|
7
|
-
result = yield
|
8
|
-
elapsed = Time.now - start_time
|
9
|
-
Bidi2pdf.logger.debug "#{operation_name} completed in #{elapsed.round(3)}s"
|
10
|
-
result
|
11
|
-
end
|
12
|
-
|
13
|
-
module_function :timed
|
14
|
-
end
|
15
|
-
end
|