bidi2pdf-rails 0.0.1.pre.alpha → 0.1.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.idea/bidi2pdf-rails.iml +55 -9
  3. data/.rubocop.yml +14 -0
  4. data/CHANGELOG.md +33 -0
  5. data/README.md +117 -27
  6. data/Rakefile +2 -0
  7. data/cliff.toml +126 -0
  8. data/lib/bidi2pdf_rails/browser_console_log_subscriber.rb +24 -0
  9. data/lib/bidi2pdf_rails/chromedriver_manager_singleton.rb +11 -11
  10. data/lib/bidi2pdf_rails/config.rb +133 -0
  11. data/lib/bidi2pdf_rails/configurable.rb +106 -0
  12. data/lib/bidi2pdf_rails/main_log_subscriber.rb +33 -0
  13. data/lib/bidi2pdf_rails/network_log_subscriber.rb +20 -0
  14. data/lib/bidi2pdf_rails/railtie.rb +12 -45
  15. data/lib/bidi2pdf_rails/services/html_renderer.rb +33 -0
  16. data/lib/bidi2pdf_rails/services/html_to_pdf_converter.rb +28 -0
  17. data/lib/bidi2pdf_rails/services/pdf_browser_session.rb +39 -0
  18. data/lib/bidi2pdf_rails/services/pdf_renderer.rb +82 -0
  19. data/lib/bidi2pdf_rails/services/url_to_pdf_converter.rb +82 -0
  20. data/lib/bidi2pdf_rails/version.rb +1 -1
  21. data/lib/bidi2pdf_rails.rb +41 -58
  22. data/lib/generators/bidi2pdf_rails/USAGE +12 -4
  23. data/lib/generators/bidi2pdf_rails/initializer_generator.rb +136 -30
  24. data/lib/generators/bidi2pdf_rails/templates/bidi2pdf_rails.rb.tt +25 -79
  25. data/spec/acceptance/user_can_download_report_pdf_spec.rb +133 -0
  26. data/spec/acceptance/user_can_generate_pdf_from_protected_remote_url_spec.rb +173 -0
  27. data/spec/dummy/app/controllers/reports_controller.rb +37 -0
  28. data/spec/dummy/app/controllers/secure_controller.rb +52 -0
  29. data/spec/dummy/app/views/layouts/simple.html.erb +17 -0
  30. data/spec/dummy/app/views/secure/show.html.erb +10 -0
  31. data/spec/dummy/config/environments/production.rb +1 -1
  32. data/spec/dummy/config/initializers/bidi2pdf_rails.rb +68 -54
  33. data/spec/dummy/config/initializers/cors.rb +1 -1
  34. data/spec/dummy/config/routes.rb +10 -0
  35. data/spec/dummy/log/development.log +16567 -156
  36. data/spec/dummy/log/test.log +53046 -0
  37. data/spec/dummy/tmp/pids/server.pid +1 -1
  38. data/spec/integration/generators/bidi2pdf_rails/initializer_generator_spec.rb +64 -0
  39. data/spec/rails_helper.rb +8 -1
  40. data/spec/spec_helper.rb +47 -5
  41. data/spec/support/default_dirs_helper.rb +32 -0
  42. data/spec/support/pdf_helper.rb +12 -0
  43. data/spec/support/render_setting_helpers.rb +28 -0
  44. data/spec/support/request_server_bootstrap.rb +44 -0
  45. data/spec/{bidi2pdf_rails → unit/bidi2pdf_rails}/bidi2pdf_rails_spec.rb +1 -1
  46. data/spec/unit/bidi2pdf_rails/configurable/base_nested_config_spec.rb +133 -0
  47. data/tasks/changelog.rake +29 -0
  48. data/tasks/coverage.rake +23 -0
  49. metadata +95 -25
  50. data/lib/bidi2pdf_rails/log_subscriber.rb +0 -13
  51. data/spec/dummy/spec/helpers/reports_helper_spec.rb +0 -15
  52. data/spec/dummy/spec/requests/reports_spec.rb +0 -10
  53. data/spec/dummy/spec/views/reports/show.html.erb_spec.rb +0 -5
  54. data/spec/generator/bidie2pdf_rails_initializer_generator_spec.rb +0 -5
  55. data/spec/generator/initializer_generator_spec.rb +0 -5
  56. data/spec/requests/reports_spec.rb +0 -17
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "configurable"
4
+
5
+ module Bidi2pdfRails
6
+ class Config
7
+ include Bidi2pdfRails::Configurable
8
+
9
+ CONFIG_OPTIONS = {
10
+ general_options: {
11
+ name: "General Options",
12
+ ask: "Configure general options? (y/n)",
13
+ options: [
14
+ { name: :logger, desc: "The logger", default: -> { Rails.logger || ActiveSupport::TaggedLogging.new(Logger.new(nil)) }, default_as_str: "Rails.logger", ask: false, color: :yellow },
15
+ { name: :verbosity, desc: "How verbose to be", default: Bidi2pdf::VerboseLogger::VERBOSITY_LEVELS.keys.sort_by { |k| Bidi2pdf::VerboseLogger::VERBOSITY_LEVELS[k] }.first.to_s, limited_to: Bidi2pdf::VerboseLogger::VERBOSITY_LEVELS.keys.sort_by { |k| Bidi2pdf::VerboseLogger::VERBOSITY_LEVELS[k] }.map(&:to_s), ask: true, color: :yellow },
16
+ { name: :headless, desc: "Run Chrome in headless mode", default: -> { !Rails.env.development? }, default_as_str: "!Rails.env.development?", ask: true, color: :green },
17
+ { name: :wait_for_network_idle, desc: "Wait for network idle", default: true, ask: true, color: :green },
18
+ { name: :wait_for_page_loaded, desc: "Wait for page loaded", default: false, ask: true, color: :green },
19
+ { name: :wait_for_page_check_script, desc: "Wait for page check script", default: nil, ask: false },
20
+ { name: :notification_service, desc: "Notification service", default: -> { ActiveSupport::Notifications }, default_as_str: "-> { ActiveSupport::Notifications }", ask: false },
21
+ { name: :default_timeout, desc: "Default timeout for various Bidi commands", default: 10, ask: true, color: :yellow },
22
+ { name: :chrome_session_args, desc: "Chrome session arguments", default: Bidi2pdf::Bidi::Session::DEFAULT_CHROME_ARGS, ask: false }
23
+ ]
24
+ },
25
+
26
+ chromedriver_settings: {
27
+ name: "Chromedriver Settings (when chromedriver run within your app)",
28
+ ask: "Configure chromedriver settings? (y/n)",
29
+ options: [
30
+ { name: :install_dir, desc: "Chromedriver install directory", default: nil, ask: false, color: :yellow },
31
+ { name: :port, desc: "Chromedriver port", default: 0, ask: true, color: :yellow }
32
+ ]
33
+ },
34
+
35
+ proxy_settings: {
36
+ name: "Proxy Settings",
37
+ ask: "Use a proxy server? (y/n)",
38
+ options: [
39
+ { name: :addr, desc: "Proxy address (e.g., 127.0.0.1)", default: nil, ask: true, color: :yellow },
40
+ { name: :port, desc: "Proxy port (e.g., 8080)", default: nil, ask: true, color: :yellow },
41
+ { name: :user, desc: "Proxy user", default: nil, ask: true, color: :yellow },
42
+ {
43
+ name: :pass,
44
+ desc: "Proxy password",
45
+ default: -> { -> { Rails.application.credentials.dig("bidi2pdf_rails", "proxy_pass") } },
46
+ default_as_str: "-> { Rails.application.credentials.dig('bidi2pdf_rails', 'proxy_pass') }",
47
+ secret: true,
48
+ ask: true,
49
+ color: :yellow
50
+ }
51
+ ]
52
+ },
53
+
54
+ pdf_settings: {
55
+ name: "PDF Settings",
56
+ ask: "Configure custom PDF settings? (y/n)",
57
+ options: [
58
+ { name: :orientation, desc: "PDF orientation (portrait/landscape)", default: "portrait", limited_to: %w[portrait landscape], ask: true },
59
+ { name: :margins, desc: "Configure PDF margins?", default: false, ask: true, color: :green },
60
+ { name: :margin_top, desc: "PDF margin top (cm)", default: 2.5, ask: true, color: :yellow },
61
+ { name: :margin_bottom, desc: "PDF margin bottom (cm)", default: 2, ask: true, color: :yellow },
62
+ { name: :margin_left, desc: "PDF margin left (cm)", default: 2, ask: true, color: :yellow },
63
+ { name: :margin_right, desc: "PDF margin right (cm)", default: 2, ask: true, color: :yellow },
64
+ { name: :page_format, desc: "PDF page format (e.g., A4)", default: nil, limited_to: Bidi2pdf::PAPER_FORMATS_CM.keys.map(&:to_s), ask: true },
65
+ { name: :page_width, desc: "PDF page width (cm, not needed when format is specified)", default: Bidi2pdf::PAPER_FORMATS_CM[:a4][:width], ask: true, color: :yellow },
66
+ { name: :page_height, desc: "PDF page height (cm, not needed when format is specified)", default: Bidi2pdf::PAPER_FORMATS_CM[:a4][:height], ask: true, color: :yellow },
67
+ { name: :print_background, desc: "Print background graphics?", default: true, ask: true, color: :green },
68
+ { name: :scale, desc: "PDF scale (e.g., 1.0)", default: 1.0, ask: true, color: :yellow },
69
+ { name: :shrink_to_fit, desc: "Shrink to fit?", default: false, ask: true, color: :green }
70
+ ]
71
+ },
72
+
73
+ render_remote_settings: {
74
+ name: "Remote URL Settings",
75
+ ask: false,
76
+ options: [
77
+ { name: :browser_url, desc: "Remote browser URL (e.g. http://localhost:3001/sesion)", default: nil, ask: true, color: :yellow },
78
+ { name: :basic_auth_user, desc: "Basic auth user", default: nil },
79
+ { name: :basic_auth_pass, desc: "Basic auth password", default: nil, default_as_str: "-> { Rails.application.credentials.dig('bidi2pdf_rails', 'basic_auth_pass') }", secret: true },
80
+ { name: :headers, desc: "Headers to be send when allong an url", default: {}, default_as_str: '{"X-API-INFO" => "my info"}' },
81
+ { name: :cookies, desc: "Cookies to be send when alling an url", default: {}, default_as_str: '{"session_id" => "my session"}' }
82
+ ]
83
+ }
84
+
85
+ }.freeze
86
+
87
+ configure_with(CONFIG_OPTIONS)
88
+
89
+ def initialize
90
+ reset_to_defaults!
91
+ end
92
+
93
+ def pdf_settings_for_bidi_cmd(overrides = {})
94
+ overrides ||= {}
95
+ opts = {}
96
+
97
+ opts[:background] = pdf_settings.print_background_value
98
+ opts[:orientation] = pdf_settings.orientation_value
99
+ opts[:scale] = pdf_settings.scale_value
100
+ opts[:shrinkToFit] = pdf_settings.shrink_to_fit_value
101
+
102
+ # Margins
103
+ margins = {
104
+ top: pdf_settings.margin_top_value,
105
+ bottom: pdf_settings.margin_bottom_value,
106
+ left: pdf_settings.margin_left_value,
107
+ right: pdf_settings.margin_right_value
108
+ }.compact
109
+
110
+ opts[:margin] = margins unless margins.empty?
111
+
112
+ # Page size
113
+ page = {
114
+ width: pdf_settings.page_width_value,
115
+ height: pdf_settings.page_height_value,
116
+ format: pdf_settings.page_format_value
117
+ }.compact
118
+
119
+ page = overrides[:page].compact if overrides[:page]&.compact.present?
120
+ page = { format: page[:format] } if page[:format]
121
+
122
+ opts[:page] = page unless page.empty?
123
+
124
+ opts.deep_merge(overrides).compact
125
+ end
126
+
127
+ def validate_print_options!
128
+ validator = Bidi2pdf::Bidi::Commands::PrintParametersValidator.new(pdf_settings_for_bidi_cmd)
129
+
130
+ validator.validate!
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Bidi2pdfRails
6
+ module Configurable
7
+ extend ActiveSupport::Concern
8
+ included do
9
+ include Singleton
10
+ end
11
+
12
+ class BaseNestedConfig
13
+ def initialize(option_defs)
14
+ @__options = option_defs
15
+ define_accessors!
16
+ reset_to_defaults!
17
+ end
18
+
19
+ def define_accessors!
20
+ @__options.each do |opt|
21
+ name = opt[:name]
22
+ unless respond_to?(name)
23
+ define_singleton_method(name) { instance_variable_get("@#{name}") }
24
+ define_singleton_method("#{name}=") { |v| instance_variable_set("@#{name}", v) }
25
+
26
+ define_singleton_method("#{name}_value") do |*args|
27
+ value = send("#{name}")
28
+ value.respond_to?(:call) ? value.call(*args) : value
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def reset_to_defaults!
35
+ @__options.each do |opt|
36
+ default = opt[:default]
37
+ value = default
38
+ send("#{opt[:name]}=", value)
39
+ end
40
+ end
41
+
42
+ def config_options
43
+ @__options
44
+ end
45
+
46
+ def configure
47
+ yield self if block_given?
48
+ end
49
+
50
+ def to_h
51
+ @__options.to_h do |opt|
52
+ [opt[:name], send(opt[:name])]
53
+ end
54
+ end
55
+
56
+ def inspect
57
+ "#<#{self.class.name || "ConfigSection"} #{to_h.inspect}>"
58
+ end
59
+ end
60
+
61
+ def configure
62
+ yield self if block_given?
63
+ end
64
+
65
+ class_methods do
66
+ def configure_with(grouped_options)
67
+ @config_groups = grouped_options
68
+
69
+ grouped_options.each do |group_key, group_data|
70
+ group_class = Class.new(BaseNestedConfig)
71
+
72
+ define_method(group_key) do
73
+ ivar = "@#{group_key}"
74
+ instance_variable_get(ivar) || instance_variable_set(ivar, group_class.new(group_data[:options]))
75
+ end
76
+
77
+ define_method("#{group_key}=") do |val|
78
+ raise ArgumentError, "Use individual accessors, not direct assignment to config groups"
79
+ end
80
+ end
81
+
82
+ define_method(:reset_to_defaults!) do
83
+ grouped_options.each_key do |group_key|
84
+ section = send(group_key)
85
+ section.reset_to_defaults!
86
+ end
87
+ end
88
+
89
+ define_method(:config_groups) do
90
+ grouped_options
91
+ end
92
+
93
+ define_method(:to_h) do
94
+ grouped_options.keys.to_h do |group_key|
95
+ section = send(group_key)
96
+ [group_key, section.to_h]
97
+ end
98
+ end
99
+
100
+ define_method(:inspect) do
101
+ "#<#{self.class.name} #{to_h.inspect}>"
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ class MainLogSubscriber < ActiveSupport::LogSubscriber
5
+ @silenced_events = []
6
+
7
+ class << self
8
+ attr_accessor :silenced_events
9
+
10
+ def silence(event)
11
+ self.silenced_events << event
12
+ end
13
+
14
+ def silenced?(event)
15
+ self.silenced_events.any? { |silenced_event| silenced_event === event }
16
+ end
17
+ end
18
+
19
+ include Bidi2pdf::Notifications::LoggingSubscriberActions
20
+
21
+ def handle_printing(event)
22
+ logger.info "Page rendered and printed: #{event.duration.round(1)}ms"
23
+ end
24
+
25
+ def logger
26
+ Bidi2pdf::VerboseLogger.new(super.tagged("bidi2pdf_rails"), Bidi2pdfRails.config.general_options.verbosity_value)
27
+ end
28
+
29
+ def silenced?(event)
30
+ MainLogSubscriber.silenced?(event) || super(event)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bidi2pdf/bidi/network_event_formatters/network_event_console_formatter"
4
+
5
+ module Bidi2pdfRails
6
+ class NetworkLogSubscriber < ActiveSupport::LogSubscriber
7
+ def network_event_received(event)
8
+ payload = event.payload
9
+
10
+ return if payload[:method] == "network.responseStarted" || payload[:method] == "network.beforeRequestSent"
11
+
12
+ logger.tagged("bidi2pdf_rails", "network") do |tagged_logger|
13
+ verbose_logger = Bidi2pdf::VerboseLogger.new(tagged_logger, Bidi2pdfRails.config.general_options.verbosity_value)
14
+ formatter = Bidi2pdf::Bidi::NetworkEventFormatters::NetworkEventConsoleFormatter.new(logger: verbose_logger)
15
+
16
+ formatter.log [payload[:event]]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails/railtie"
4
+
5
+ require_relative "services/html_renderer"
6
+ require_relative "services/pdf_renderer"
7
+ require_relative "services/pdf_browser_session"
8
+ require_relative "services/url_to_pdf_converter"
9
+ require_relative "services/html_to_pdf_converter"
10
+
3
11
  module Bidi2pdfRails
4
12
  class Railtie < ::Rails::Railtie
5
13
  initializer "bidi2pdf_rails.add_mime_type" do
@@ -9,7 +17,8 @@ module Bidi2pdfRails
9
17
  initializer "bidi2pdf_rails.initialize_chromedriver_manager", after: :load_config_initializers do
10
18
  Bidi2pdfRails.apply_bidi2pdf_config
11
19
 
12
- Rails.application.config.after_initialize do
20
+ # defer the initialization of the ChromedriverManager to the ActionController::Base class load event
21
+ ActiveSupport.on_load(:action_controller_base) do
13
22
  ChromedriverManagerSingleton.initialize_manager
14
23
  end
15
24
  end
@@ -23,56 +32,14 @@ module Bidi2pdfRails
23
32
 
24
33
  initializer "bidi2pdf_rails.add_pdf_renderer" do
25
34
  ActionController::Renderers.add :pdf do |filename, options|
26
- pdf_data = ""
27
35
  options = options.dup
28
36
 
29
- original_asset_host = ActionController::Base.asset_host
30
-
31
- if options[:asset_host]
32
- ActionController::Base.asset_host = options[:asset_host]
33
- elsif !Rails.application.config.action_controller.asset_host
34
- # Use request base URL as fallback if no asset host configured
35
- ActionController::Base.asset_host = request.base_url
36
- end
37
-
38
- html = render_to_string(options.merge(formats: [ :html ]))
39
-
40
- Bidi2pdf.default_timeout = 30
41
-
42
- # without a thread, the app will be blocked in dev mode
43
- thread = Thread.new {
44
- Rails.application.executor.wrap do
45
- # subscriptions in other threads will not be inherited
46
- Bidi2pdfRails::LogSubscriber.attach_to "bidi2pdf", inherit_all: true
47
-
48
- browser = ChromedriverManagerSingleton.session.browser
49
- context = browser.create_user_context
50
- window = context.create_browser_window
51
- tab = window.create_browser_tab
37
+ pdf_content = Services::PdfRenderer.new(filename, options, self).render_pdf
52
38
 
53
- tab.render_html_content html
54
-
55
- tab.wait_until_network_idle
56
-
57
- tab.wait_until_page_loaded
58
-
59
- pdf_data = tab.print
60
- ensure
61
- tab&.close
62
- window&.close
63
- context&.close
64
- end
65
- }
66
-
67
- thread.join
68
-
69
- send_data Base64.decode64(pdf_data),
39
+ send_data pdf_content,
70
40
  type: Mime[:pdf],
71
41
  filename: "#{filename}.pdf",
72
42
  disposition: options.fetch(:disposition, "inline")
73
- ensure
74
-
75
- ActionController::Base.asset_host = original_asset_host
76
43
  end
77
44
  end
78
45
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ module Services
5
+ # app/services/html_renderer.rb
6
+ class HtmlRenderer
7
+ def initialize(controller, options)
8
+ @controller = controller
9
+ @options = options.dup
10
+ end
11
+
12
+ def render
13
+ with_overridden_asset_host { @controller.render_to_string(@options.merge(formats: [:html])) }
14
+ end
15
+
16
+ private
17
+
18
+ def with_overridden_asset_host
19
+ original_asset_host = ActionController::Base.asset_host
20
+
21
+ if @options[:asset_host]
22
+ ActionController::Base.asset_host = @options[:asset_host]
23
+ elsif !Rails.application.config.action_controller.asset_host
24
+ ActionController::Base.asset_host = @controller.request.base_url
25
+ end
26
+
27
+ yield
28
+ ensure
29
+ ActionController::Base.asset_host = original_asset_host
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ module Services
5
+ class HtmlToPdfConverter
6
+ include PdfBrowserSession
7
+
8
+ def initialize(html, print_options: {}, wait_for_network_idle: true, wait_for_page_loaded: true, wait_for_page_check_script: nil)
9
+ @html = html
10
+ @print_options = print_options
11
+ @wait_for_network_idle = wait_for_network_idle
12
+ @wait_for_page_loaded = wait_for_page_loaded
13
+ @wait_for_page_check_script = wait_for_page_check_script
14
+ end
15
+
16
+ def generate
17
+ run_browser_session
18
+ end
19
+
20
+ private
21
+
22
+ def prepare_tab(tab)
23
+ tab.render_html_content(@html)
24
+ wait_for_tab(tab)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ module Services
5
+ module PdfBrowserSession
6
+ def run_browser_session
7
+ pdf_data = nil
8
+
9
+ thread = Thread.new do
10
+ Rails.application.executor.wrap do
11
+ browser = ChromedriverManagerSingleton.session.browser
12
+ context = browser.create_user_context
13
+ window = context.create_browser_window
14
+ tab = window.create_browser_tab
15
+
16
+ begin
17
+ prepare_tab(tab)
18
+ pdf_data = tab.print(print_options: @print_options)
19
+ ensure
20
+ tab&.close
21
+ window&.close
22
+ context&.close
23
+ end
24
+ end
25
+ end
26
+
27
+ thread.join
28
+ Base64.decode64(pdf_data)
29
+ end
30
+
31
+ private
32
+
33
+ def wait_for_tab(tab)
34
+ tab.wait_until_network_idle if @wait_for_network_idle
35
+ tab.wait_until_page_loaded(check_script: @wait_for_page_check_script) if @wait_for_page_loaded
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ module Services
5
+ # PdfRenderer is responsible for rendering a Rails view into a PDF using headless Chrome.
6
+ #
7
+ # It accepts a mix of HTML rendering options (passed to `render_to_string`) and PDF generation options.
8
+ #
9
+ # @example Basic usage
10
+ # renderer = Bidi2pdfRails::Services::PdfRenderer.new("invoice", { template: "invoices/show" }, controller)
11
+ # renderer.render_pdf
12
+ #
13
+ # @param filename [String] the base filename for the generated PDF (used for download naming)
14
+ # @param options [Hash] rendering options including both HTML and PDF-specific options
15
+ # @option options [String] :template The template to render (or other render_to_string keys like :partial, :layout, etc.)
16
+ # @option options [String] :asset_host Optional asset host override
17
+ # @option options [Hash] :print_options Options passed to the Chrome PDF print API
18
+ # @option options [Boolean] :wait_for_network_idle Wait for network to go idle before generating PDF (default: config default)
19
+ # @option options [Boolean] :wait_for_page_loaded Wait for page load event before generating PDF (default: config default)
20
+ # @option options [String] :wait_for_page_check_script JavaScript condition to wait for before generating PDF
21
+ #
22
+ class PdfRenderer
23
+ PDF_OPTIONS = %i[
24
+ print_options
25
+ asset_host
26
+ wait_for_network_idle
27
+ wait_for_page_loaded
28
+ wait_for_page_check_script
29
+ html
30
+ ].freeze
31
+
32
+ PRINT_URL_OPTIONS = %i[
33
+ url
34
+ auth
35
+ headers
36
+ cookies
37
+ ].freeze
38
+
39
+ def initialize(filename, options, controller)
40
+ @filename = filename
41
+ @pdf_options = options.slice(*PDF_OPTIONS)
42
+ @print_url_options = options.slice(*PRINT_URL_OPTIONS)
43
+ @html_options = options.except(*(PDF_OPTIONS + PRINT_URL_OPTIONS))
44
+ @controller = controller
45
+ end
46
+
47
+ def render_pdf
48
+ ActiveSupport::Notifications.instrument("handle_printing.bidi2pdf_rails") do
49
+ print_options = Bidi2pdfRails.config.pdf_settings_for_bidi_cmd(@pdf_options[:print_options] || {})
50
+ wait_for_network_idle = @pdf_options.fetch(:wait_for_network_idle, Bidi2pdfRails.config.general_options.wait_for_network_idle_value)
51
+ wait_for_page_loaded = @pdf_options.fetch(:wait_for_page_loaded, Bidi2pdfRails.config.general_options.wait_for_page_loaded_value)
52
+ wait_for_page_check_script = @pdf_options.fetch(:wait_for_page_check_script, Bidi2pdfRails.config.general_options.wait_for_page_check_script_value)
53
+
54
+ if @print_url_options[:url]
55
+ headers = @print_url_options[:headers] || Bidi2pdfRails.config.render_remote_settings.headers_value(@controller)
56
+ cookies = @print_url_options[:cookies] || Bidi2pdfRails.config.render_remote_settings.cookies_value(@controller)
57
+ auth = @print_url_options[:auth] || { username: Bidi2pdfRails.config.render_remote_settings.basic_auth_user_value(@controller), password: Bidi2pdfRails.config.render_remote_settings.basic_auth_pass_value(@controller) }
58
+
59
+ UrlToPdfConverter.new(@print_url_options[:url],
60
+ headers: headers,
61
+ cookies: cookies,
62
+ auth: auth,
63
+ print_options: print_options,
64
+ wait_for_network_idle: wait_for_network_idle,
65
+ wait_for_page_loaded: wait_for_page_loaded,
66
+ wait_for_page_check_script: wait_for_page_check_script
67
+ ).generate
68
+ else
69
+ html = @html_options.fetch(:inline, HtmlRenderer.new(@controller, @html_options).render)
70
+
71
+ HtmlToPdfConverter.new(html,
72
+ print_options: print_options,
73
+ wait_for_network_idle: wait_for_network_idle,
74
+ wait_for_page_loaded: wait_for_page_loaded,
75
+ wait_for_page_check_script: wait_for_page_check_script
76
+ ).generate
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdfRails
4
+ module Services
5
+ class UrlToPdfConverter
6
+ include PdfBrowserSession
7
+
8
+ def initialize(url, headers: {}, cookies: {}, auth: {}, print_options: {}, wait_for_network_idle: true, wait_for_page_loaded: true, wait_for_page_check_script: nil)
9
+ @url = url
10
+ @headers = headers || {}
11
+ @cookies = cookies || {}
12
+ @auth = auth || {}
13
+ @print_options = print_options
14
+ @wait_for_network_idle = wait_for_network_idle
15
+ @wait_for_page_loaded = wait_for_page_loaded
16
+ @wait_for_page_check_script = wait_for_page_check_script
17
+ end
18
+
19
+ def generate
20
+ run_browser_session
21
+ end
22
+
23
+ private
24
+
25
+ def prepare_tab(tab)
26
+ add_headers(tab)
27
+ add_basic_auth(tab)
28
+ add_cookies(tab)
29
+
30
+ tab.navigate_to(@url)
31
+ wait_for_tab(tab)
32
+ end
33
+
34
+ def add_cookies(tab)
35
+ @cookies.each do |name, value|
36
+ tab.set_cookie(
37
+ name: name,
38
+ value: value,
39
+ domain: domain,
40
+ secure: uri.scheme == "https"
41
+ )
42
+ end
43
+ end
44
+
45
+ def add_headers(tab)
46
+ @headers.each do |name, value|
47
+ tab.add_headers(
48
+ url_patterns: url_patterns,
49
+ headers: [{ name: name, value: value }]
50
+ )
51
+ end
52
+ end
53
+
54
+ def add_basic_auth(tab)
55
+ return unless @auth[:username] && @auth[:password]
56
+
57
+ tab.basic_auth(
58
+ url_patterns: url_patterns,
59
+ username: @auth[:username],
60
+ password: @auth[:password]
61
+ )
62
+ end
63
+
64
+ def uri
65
+ @uri ||= URI(@url)
66
+ end
67
+
68
+ def domain
69
+ uri.host
70
+ end
71
+
72
+ def url_patterns
73
+ [{
74
+ type: "pattern",
75
+ protocol: uri.scheme,
76
+ hostname: uri.host,
77
+ port: uri.port.to_s
78
+ }]
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module Bidi2pdfRails
2
- VERSION = "0.0.1.pre.alpha"
2
+ VERSION = "0.1.0"
3
3
  end