Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PDFkit hangs when generating a pdf with an image

I want to render a webpage as a PDF. It uses a single image, and I've read that you need to supply the absolute URL for PDFkit to be able to use the image, so my code is:

= image_tag image_url(user.avatar)

This works when viewed as HTML, and PDFkit is able to generate a PDF with the image removed. However, when using the image, it just hangs until I kill the server. How can I get this to work?

Here's the full output when I kill the server:

2013-12-04 13:53:36.576 wkhtmltopdf[27410:507] CoreText performance note: Client called CTFontCreateWithName() using name "Arial" and got font with PostScript name "ArialMT". For best performance, only use PostScript names when calling this API.
2013-12-04 13:53:36.577 wkhtmltopdf[27410:507] CoreText performance note: Set a breakpoint on CTFontLogSuboptimalRequest to debug.
2013-12-04 13:53:36.582 wkhtmltopdf[27410:507] CoreText performance note: Client called CTFontCreateWithName() using name "Arial" and got font with PostScript name "ArialMT". For best performance, only use PostScript names when calling this API.
2013-12-04 13:53:36.584 wkhtmltopdf[27410:507] CoreText performance note: Client called CTFontCreateWithName() using name "Arial" and got font with PostScript name "ArialMT". For best performance, only use PostScript names when calling this API.
^C
RuntimeError - command failed: /usr/local/bin/wkhtmltopdf --page-size Legal --print-media-type --quiet - -:
  pdfkit (0.5.4) lib/pdfkit/pdfkit.rb:73:in `to_pdf'
  pdfkit (0.5.4) lib/pdfkit/middleware.rb:21:in `call'
  warden (1.2.3) lib/warden/manager.rb:35:in `block in call'
  warden (1.2.3) lib/warden/manager.rb:34:in `catch'
  warden (1.2.3) lib/warden/manager.rb:34:in `call'
  rack (1.5.2) lib/rack/etag.rb:23:in `call'
  rack (1.5.2) lib/rack/conditionalget.rb:25:in `call'
  rack (1.5.2) lib/rack/head.rb:11:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/flash.rb:241:in `call'
  rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/cookies.rb:486:in `call'
  activerecord (4.0.0) lib/active_record/query_cache.rb:36:in `call'
  activerecord (4.0.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:626:in `call'
  activerecord (4.0.0) lib/active_record/migration.rb:369:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.0.0) lib/active_support/callbacks.rb:373:in `_run__4124003592524659480__call__callbacks'
  activesupport (4.0.0) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/reloader.rb:64:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:84:in `protected_app_call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:79:in `better_errors_call'
  better_errors (1.0.1) lib/better_errors/middleware.rb:56:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.0.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.0.0) lib/rails/rack/logger.rb:21:in `block in call'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `block in tagged'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:25:in `tagged'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `tagged'
  railties (4.0.0) lib/rails/rack/logger.rb:21:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
  rack (1.5.2) lib/rack/runtime.rb:17:in `call'
  activesupport (4.0.0) lib/active_support/cache/strategy/local_cache.rb:83:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/static.rb:64:in `call'
  railties (4.0.0) lib/rails/engine.rb:511:in `call'
  railties (4.0.0) lib/rails/application.rb:97:in `call'
  rack (1.5.2) lib/rack/content_length.rb:14:in `call'
  thin (1.6.0) lib/thin/connection.rb:82:in `block in pre_process'
  thin (1.6.0) lib/thin/connection.rb:80:in `catch'
  thin (1.6.0) lib/thin/connection.rb:80:in `pre_process'
  thin (1.6.0) lib/thin/connection.rb:55:in `process'
  thin (1.6.0) lib/thin/connection.rb:41:in `receive_data'
  eventmachine (1.0.3) lib/eventmachine.rb:187:in `run_machine'
  eventmachine (1.0.3) lib/eventmachine.rb:187:in `run'
  thin (1.6.0) lib/thin/backends/base.rb:73:in `start'
  thin (1.6.0) lib/thin/server.rb:162:in `start'
  rack (1.5.2) lib/rack/handler/thin.rb:16:in `run'
  rack (1.5.2) lib/rack/server.rb:264:in `start'
  railties (4.0.0) lib/rails/commands/server.rb:84:in `start'
  railties (4.0.0) lib/rails/commands.rb:78:in `block in <top (required)>'
  railties (4.0.0) lib/rails/commands.rb:73:in `tap'
  railties (4.0.0) lib/rails/commands.rb:73:in `<top (required)>'
  bin/rails:4:in `require'
  bin/rails:4:in `<main>'
like image 402
raphael_turtle Avatar asked Nov 30 '22 11:11

raphael_turtle


2 Answers

This is a notorious issue, and you're running into it because your probably have relatively linked assets in your HTML (i.e. images, CSS, JS, fonts, etc), and your web server is only capable of handling one request/thread at a time (like WEBrick).

So what happens? The server begins generating the PDF when you request its URL. PDFkit finds a linked asset, so it tries to load this asset from the server, which happens to be the same server that PDFkit is running on. However, the server's single thread is already busy running PDFkit, so it cannot "free up" to serve the requested asset. In conclusion, it's a deadlock -- PDFkit is awaiting on an asset on the same server that is waiting for PDFkit to finish up processing, so that it can serve the asset to PDFkit...

Solution: either Base64-embed your assets in the HTML so that PDFkit doesn't need to make any additional requests (my personally preferred solution), or temporarily offload the assets to another server (e.g. a temporary AWS bucket). You can also try using the unicorn or Thin webserver with multi-threading enabled, or adding config.threadsafe! in in application.rb, but there is no guarantee that these methods will work.

Of course, these hacks (embedding assets or hosting elsewhere) should only be used in the dev environment -- you shouldn't be running into these kinds of issues in production, as the live server should (hopefully) be able to handle multiple GET requests.

like image 160
Arman H Avatar answered Dec 05 '22 00:12

Arman H


You can use unicorn in development which serve multiple request at a time. And is easy to setup. refer http://dave.is/unicorn.html and https://devcenter.heroku.com/articles/rails-unicorn

It solved my problem in development.

like image 27
jbmyid Avatar answered Dec 05 '22 02:12

jbmyid