Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I set a global default font for all PDFs generated with prawn-rails? #54

Closed
denmarkmeralpis opened this issue Dec 19, 2024 · 16 comments

Comments

@denmarkmeralpis
Copy link

denmarkmeralpis commented Dec 19, 2024

Currently, I am able to set the font inside the prawn_document block like this:

prawn_document do |pdf|
  pdf.font_families.update(
    'helvetica' => {
      normal: Rails.root.join('app/assets/fonts/print/helvetica.ttf'),
      italic: Rails.root.join('app/assets/fonts/print/helvetica.ttf'),
      bold: Rails.root.join('app/assets/fonts/print/helvetica-bold.ttf'),
      bold_italic: Rails.root.join('app/assets/fonts/print/helvetica-bold.ttf')
    }
  )
  pdf.font 'helvetica'
end

However, I want to set this font globally for all PDFs without having to specify it inside each prawn_document block. Is there a way to configure this globally for my entire application, ideally in an config/initializers/prawn_rails.rb?

Thanks!

@westonganger
Copy link
Collaborator

It would appear that prawn itself does not have this option.

Prawn seems to explicitly default to "Helvetica" (defaults to it by name not font ordering)

https://github.com/prawnpdf/prawn/blob/c531f87e8cedda95afffbe6207e5c6f3a2de7609/lib/prawn/font.rb#L56-L57

    def font(name = nil, options = DEFAULT_OPTS)
      return((defined?(@font) && @font) || font('Helvetica')) if name.nil?

So you would probably need to apply a monkey patch of some sort currently

@westonganger
Copy link
Collaborator

westonganger commented Dec 19, 2024

It kind of seems like prawn-rails is in a good position to apply a default font since we are the ones who have implemented prawn_document

I could see adding a configuration option for this.

The only concern I see with changing prawn_document only is that anything created outside of a Rails view would not have the default applied. But this is probably not an issue given all of the solutions listed in #32

@westonganger
Copy link
Collaborator

westonganger commented Dec 19, 2024

@denmarkmeralpis I've created a PR for the configuration changes. Can you please try it out and see if it solves this issue for you. (see the instructions that were added in the README within the PR) Once I receive confirmation from you I will merge the PR and release a new version of the gem.

@denmarkmeralpis
Copy link
Author

It didn't work. It raises an exception Prawn::Errors::NotOnPage

@westonganger
Copy link
Collaborator

Can you please post the stacktrace?

@westonganger
Copy link
Collaborator

westonganger commented Dec 20, 2024

Looks to me like thats an issue with your applications code. I dont think the PR or the gem has anything to do with that error.

@denmarkmeralpis
Copy link
Author

I don't believe the issue is with my code, as I followed the configuration exactly as outlined in the README.md. The problem seems to be related to how the font is being set via default_font_name. I discovered this because when I removed config.default_font_name from the configuration and instead specified the font directly inside the prawn_document block, everything worked as expected.

# config/initializers/prawn_rails.rb
PrawnRails.config do |config|
  config.page_layout = :portrait
  config.page_size   = [8.5.in, 11.in]
  config.skip_page_creation = true
  config.additional_fonts = {
    "some-custom-font" => {
      normal: Rails.root.join('app/assets/fonts/print/some-custom-font.ttf'),
      italic: Rails.root.join('app/assets/fonts/print/some-custom-font.ttf'),
      bold: Rails.root.join('app/assets/fonts/print/some-custom-font-bold.ttf'),
      bold_italic: Rails.root.join('app/assets/fonts/print/some-custom-font-bold.ttf')
     }
  }
  # config.default_font_name = "some-custom-font"
end

# print.pdf.prawn
prawn_document do |pdf|
  pdf.font 'some-custom-font'
end

Anyways, here's the stacktrace:

prawn (2.5.0) lib/prawn/font.rb:60:in `font'
prawn-rails (7da37ba87f94) lib/prawn-rails/rails_helper.rb:33:in `prawn_document'
actionview (7.1.5.1) lib/action_view/base.rb:263:in `public_send'
actionview (7.1.5.1) lib/action_view/base.rb:263:in `_run'
actionview (7.1.5.1) lib/action_view/template.rb:261:in `block in render'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.1.5.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `instrument'
actionview (7.1.5.1) lib/action_view/template.rb:556:in `instrument_render_template'
actionview (7.1.5.1) lib/action_view/template.rb:249:in `render'
actionview (7.1.5.1) lib/action_view/renderer/template_renderer.rb:66:in `block (2 levels) in render_template'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.1.5.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `instrument'
actionview (7.1.5.1) lib/action_view/renderer/template_renderer.rb:60:in `block in render_template'
actionview (7.1.5.1) lib/action_view/renderer/template_renderer.rb:80:in `render_with_layout'
actionview (7.1.5.1) lib/action_view/renderer/template_renderer.rb:59:in `render_template'
actionview (7.1.5.1) lib/action_view/renderer/template_renderer.rb:11:in `render'
actionview (7.1.5.1) lib/action_view/renderer/renderer.rb:63:in `render_template_to_object'
actionview (7.1.5.1) lib/action_view/renderer/renderer.rb:31:in `render_to_object'
actionview (7.1.5.1) lib/action_view/rendering.rb:135:in `block in _render_template'
actionview (7.1.5.1) lib/action_view/base.rb:290:in `in_rendering_context'
actionview (7.1.5.1) lib/action_view/rendering.rb:134:in `_render_template'
actionpack (7.1.5.1) lib/action_controller/metal/streaming.rb:256:in `_render_template'
actionview (7.1.5.1) lib/action_view/rendering.rb:121:in `render_to_body'
actionpack (7.1.5.1) lib/action_controller/metal/rendering.rb:158:in `render_to_body'
actionpack (7.1.5.1) lib/action_controller/metal/renderers.rb:141:in `render_to_body'
actionpack (7.1.5.1) lib/abstract_controller/rendering.rb:27:in `render'
actionpack (7.1.5.1) lib/action_controller/metal/rendering.rb:139:in `render'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:29:in `block (2 levels) in render'
benchmark (0.4.0) lib/benchmark.rb:323:in `realtime'
activesupport (7.1.5.1) lib/active_support/core_ext/benchmark.rb:14:in `ms'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:29:in `block in render'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:98:in `cleanup_view_runtime'
activerecord (7.1.5.1) lib/active_record/railties/controller_runtime.rb:39:in `cleanup_view_runtime'
mongoid (8.1.7) lib/mongoid/railties/controller_runtime.rb:27:in `cleanup_view_runtime'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:28:in `render'
actionpack (7.1.5.1) lib/action_controller/metal/implicit_render.rb:37:in `default_render'
actionpack (7.1.5.1) lib/action_controller/metal/basic_implicit_render.rb:7:in `send_action'
actionpack (7.1.5.1) lib/abstract_controller/base.rb:224:in `process_action'
actionpack (7.1.5.1) lib/action_controller/metal/rendering.rb:165:in `process_action'
actionpack (7.1.5.1) lib/abstract_controller/callbacks.rb:259:in `block in process_action'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:121:in `block in run_callbacks'
activesupport (7.1.5.1) lib/active_support/core_ext/time/zones.rb:65:in `use_zone'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks'
actiontext (7.1.5.1) lib/action_text/rendering.rb:23:in `with_renderer'
actiontext (7.1.5.1) lib/action_text/engine.rb:69:in `block (4 levels) in <class:Engine>'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:130:in `instance_exec'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks'
sentry-rails (5.21.0) lib/sentry/rails/controller_transaction.rb:32:in `block in sentry_around_action'
sentry-ruby (5.21.0) lib/sentry/hub.rb:108:in `with_child_span'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:499:in `with_child_span'
sentry-rails (5.21.0) lib/sentry/rails/controller_transaction.rb:18:in `sentry_around_action'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:141:in `run_callbacks'
actionpack (7.1.5.1) lib/abstract_controller/callbacks.rb:258:in `process_action'
actionpack (7.1.5.1) lib/action_controller/metal/rescue.rb:25:in `process_action'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:74:in `block in process_action'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.1.5.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument'
activesupport (7.1.5.1) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.1.5.1) lib/action_controller/metal/instrumentation.rb:73:in `process_action'
actionpack (7.1.5.1) lib/action_controller/metal/params_wrapper.rb:261:in `process_action'
activerecord (7.1.5.1) lib/active_record/railties/controller_runtime.rb:32:in `process_action'
mongoid (8.1.7) lib/mongoid/railties/controller_runtime.rb:21:in `process_action'
actionpack (7.1.5.1) lib/abstract_controller/base.rb:160:in `process'
actionview (7.1.5.1) lib/action_view/rendering.rb:40:in `process'
actionpack (7.1.5.1) lib/action_controller/metal.rb:227:in `dispatch'
actionpack (7.1.5.1) lib/action_controller/metal.rb:309:in `dispatch'
actionpack (7.1.5.1) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (7.1.5.1) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (7.1.5.1) lib/action_dispatch/journey/router.rb:51:in `block in serve'
actionpack (7.1.5.1) lib/action_dispatch/journey/router.rb:131:in `block in find_routes'
actionpack (7.1.5.1) lib/action_dispatch/journey/router.rb:124:in `each'
actionpack (7.1.5.1) lib/action_dispatch/journey/router.rb:124:in `find_routes'
actionpack (7.1.5.1) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.1.5.1) lib/action_dispatch/routing/route_set.rb:882:in `call'
rack (3.1.8) lib/rack/tempfile_reaper.rb:20:in `call'
rack (3.1.8) lib/rack/etag.rb:29:in `call'
rack (3.1.8) lib/rack/conditional_get.rb:31:in `call'
rack (3.1.8) lib/rack/head.rb:15:in `call'
actionpack (7.1.5.1) lib/action_dispatch/http/permissions_policy.rb:36:in `call'
actionpack (7.1.5.1) lib/action_dispatch/http/content_security_policy.rb:36:in `call'
rack-session (2.0.0) lib/rack/session/abstract/id.rb:272:in `context'
rack-session (2.0.0) lib/rack/session/abstract/id.rb:266:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/cookies.rb:689:in `call'
activerecord (7.1.5.1) lib/active_record/migration.rb:655:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
activesupport (7.1.5.1) lib/active_support/callbacks.rb:101:in `run_callbacks'
actionpack (7.1.5.1) lib/action_dispatch/middleware/callbacks.rb:28:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/actionable_exceptions.rb:16:in `call'
sentry-rails (5.21.0) lib/sentry/rails/rescued_exception_interceptor.rb:14:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'
web-console (4.2.1) lib/web_console/middleware.rb:132:in `call_app'
web-console (4.2.1) lib/web_console/middleware.rb:28:in `block in call'
web-console (4.2.1) lib/web_console/middleware.rb:17:in `catch'
web-console (4.2.1) lib/web_console/middleware.rb:17:in `call'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:30:in `block (2 levels) in call'
sentry-ruby (5.21.0) lib/sentry/hub.rb:262:in `with_session_tracking'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:412:in `with_session_tracking'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:21:in `block in call'
sentry-ruby (5.21.0) lib/sentry/hub.rb:59:in `with_scope'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:392:in `with_scope'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:20:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
railties (7.1.5.1) lib/rails/rack/logger.rb:37:in `call_app'
railties (7.1.5.1) lib/rails/rack/logger.rb:24:in `block in call'
activesupport (7.1.5.1) lib/active_support/tagged_logging.rb:139:in `block in tagged'
activesupport (7.1.5.1) lib/active_support/tagged_logging.rb:39:in `tagged'
activesupport (7.1.5.1) lib/active_support/tagged_logging.rb:139:in `tagged'
activesupport (7.1.5.1) lib/active_support/broadcast_logger.rb:241:in `method_missing'
railties (7.1.5.1) lib/rails/rack/logger.rb:24:in `call'
sprockets-rails (3.5.2) lib/sprockets/rails/quiet_assets.rb:17:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/remote_ip.rb:92:in `call'
request_store (1.7.0) lib/request_store/middleware.rb:19:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/request_id.rb:28:in `call'
rack (3.1.8) lib/rack/method_override.rb:28:in `call'
rack (3.1.8) lib/rack/runtime.rb:24:in `call'
activesupport (7.1.5.1) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/server_timing.rb:59:in `block in call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/server_timing.rb:24:in `collect_events'
actionpack (7.1.5.1) lib/action_dispatch/middleware/server_timing.rb:58:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/static.rb:25:in `call'
rack (3.1.8) lib/rack/sendfile.rb:114:in `call'
actionpack (7.1.5.1) lib/action_dispatch/middleware/host_authorization.rb:141:in `call'
railties (7.1.5.1) lib/rails/engine.rb:536:in `call'
puma (6.4.3) lib/puma/configuration.rb:272:in `call'
puma (6.4.3) lib/puma/request.rb:100:in `block in handle_request'
puma (6.4.3) lib/puma/thread_pool.rb:378:in `with_force_shutdown'
puma (6.4.3) lib/puma/request.rb:99:in `handle_request'
puma (6.4.3) lib/puma/server.rb:464:in `process_client'
puma (6.4.3) lib/puma/server.rb:245:in `block in run'
puma (6.4.3) lib/puma/thread_pool.rb:155:in `block in spawn_thread'

@westonganger
Copy link
Collaborator

Heres the cause

config.skip_page_creation = true

That setting throws a wrench in this solution. Will need to think how to handle it.

@westonganger
Copy link
Collaborator

What benefit does skip_page_creation have in your application?

@westonganger
Copy link
Collaborator

Im inquiring about the reason why the first page must exist to set the font, prawnpdf/prawn#1369

@westonganger
Copy link
Collaborator

I've now updated to PR to monkey_patch start_new_page seems this is likely our only option. Can you pull in the latest changes in that PR and try again?

@denmarkmeralpis
Copy link
Author

Setting skip_page_creation to false results in an additional first page with no content, which is likely why I chose to set it to true instead

@westonganger
Copy link
Collaborator

westonganger commented Dec 20, 2024

Sure but then isnt the very first thing you do is call start_new_page explicitly then start filling it in with content?

Not sure why you'd create a PDF with no content.

But its a valid config option, so we must support it I suppose.

@denmarkmeralpis
Copy link
Author

denmarkmeralpis commented Dec 20, 2024

Yeah, you're right! What my PDF does is loop through multiple printable data, and each of them should start a new page so I can print them in bulk. For now, I'll skip calling start_new_page if the index is 0."

printables = ['text 0', 'text 1', 'text 2]

prawn_document do |pdf|
  printables.each_with_index do |printable, i|
  
  pdf.start_new_page unless i.zero?
  pdf.text printable
  ...
end

@denmarkmeralpis
Copy link
Author

denmarkmeralpis commented Dec 20, 2024

Anyway, for now, I’ve applied a monkey patch and specified the font 'some-font' directly in my print.pdf.prawn file.

# config/initializers/prawn_rails.rb
require 'prawn-rails/document'

PrawnRails.config do |config|
  config.page_layout = :portrait
  config.page_size   = [8.5.in, 11.in]
  config.skip_page_creation = true
end

# monkey patching PrawnRails::Document to add custom fonts
PrawnRails::Document.class_eval do
  alias_method :orig_init, :initialize

  def initialize(*args)
    orig_init(*args)

    font_families.update(
      'some-font' => {
        normal: Rails.root.join('app/assets/fonts/print/some-font.ttf'),
        italic: Rails.root.join('app/assets/fonts/print/some-font.ttf'),
        bold: Rails.root.join('app/assets/fonts/print/some-font-bold.ttf'),
        bold_italic: Rails.root.join('app/assets/fonts/print/some-font-bold.ttf')
      }
    )
  end
end

# print.pdf.prawn
prawn_document do |pdf|
  # ...
  font 'some-font'
  # ...
end

@westonganger
Copy link
Collaborator

v1.6.0 is now released which contains this new feature

You can feel free to keep your original skip_page_creation if desired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants