In wicked_pdf doc, it is said that I can use wicked_pdf_stylesheet_pack_tag and wicked_pdf_javascript_pack_tag to include my stylesheets and javascript from webpack but nothing works.
In a rails project with webpack, here is the code from the controller:
format.pdf do
render template: "pdf_reports/show",
layout: "wicked_layout",
pdf: "report"
end
Here is the code from the layout:
<!DOCTYPE html>
<html>
<head>
<%= csrf_meta_tags %>
<%= wicked_pdf_javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= wicked_pdf_stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= wicked_pdf_stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
Here is the code from the view pdf.erb:
<h1 class="text-red-base">Test pdf</h1>
<h2 class="test-wicked">mldgmdjgfd</h2>
It works with wicked_pdf_stylesheet_link_tag (test-wicked is applied from sprockets: text is blue) but not with wicked_pdf_stylesheet_pack_tag (h1 should be red but is not).
PS: I posted originally the question on stackoverflow but I think it makes more sens to address an issue.
wicked_pdf gem version (output of cat Gemfile.lock | grep wicked_pdf): 1.4.0
wkhtmltopdf version (output of wkhtmltopdf --version): 0.12.4
whtmltopdf provider gem and version if one is used:
wkhtmltopdf-binary (0.12.4)
platform/distribution and version (e.g. Windows 10 / Ubuntu 16.04 / Heroku cedar):
Ubuntu 18.04
Thank you in advance!
Cross-referencing your StackOverflow post here:
https://stackoverflow.com/questions/58490299/how-to-include-css-stylesheet-into-wicked-pdf
There's no answer there. Did you ever figure this out or find a workaround?
No, I did not find a workaround. If anyone has a clue, he is welcomed!
https://stackoverflow.com/a/60541688/538534
The webpack helpers make several assumptions that might not hold in every project.
They produce two different results depending on what running_in_develpment? returns. With webpacker 3.0.0 or newer this method delegates to Webpacker.dev_server.running?.
Without the dev server running the helpers will assume the assets were precompiled and will attempt to paste the contents of the asset into a <style> or <script> tag. This should work if assets are precompiled in production and are available in the filesystem of the environment where the application is running. This is most often true.
With the dev server running the webpack helpers will return a tag with an asset path to the pack asset (eventually using standard asset_path helper). The actual path depends on the rails config. In some cases the path will be incompatible with the html being rendered by wkhtmltopdf from a file://...:
if config.action_controller.asset_host isn't set the asset_path will produce relative paths. These will not work in wkhtmltopdf rendering from file.
if config.action_controller.asset_host is set absolute URLs will be used through out the application (this is a general setting that controls what asset_path returns). Now wkhtmltopdf might be able to fetch the assets, if it is running in the environment where the asset host can be resolved and accessed. This might not be true in a containerized application.
In our case we had some additional constraints:
our PDF emitting actions support a param that makes them pass show_as_html: true to render. This makes wicked_pdf skip PDF generation and return the intermediate HTML. We use this to debug HTML views in the browser. This html will be rendered by the developer's browser in a different environment than where wkhtmltopdf runs.
our development setup is single threaded because we use better_errors debugger which relies on all requests being served by the same application instance. On the other hand we render PDF in the request. This means the wkhtmltopdf won't be able to request the assets from the application (e.f. by passing host: "localhost:3000" to the asset_path) because it already occupies the only available thread.
Our solution has been to implement our own helpers that are more aware of our set up.
In production the default behavior is compatible with our setup, so we delegate to the original, which will find the assets in the filesystem and include them in the generated HTML.
In development, when generating the PDF, we pass the hostname/port of the webpack dev server to webpacker tag helpers (which will pass them down to asset_path). Dev server runs in a separate process and hence will repond even while the application is inside the request handler.
In development, when returning intermediate HTML (as determined by our custom helper show_as_html?), don't pass host: to asset_pack_url, which will result in "normal" asset path like in the rest of the application, which will work in the browser.
module PdfHelper
def pdf_stylesheet_pack_tag(source)
if running_in_development?
options = { media: "all" }
wds = Webpacker.dev_server
options[:host] = "#{wds.host}:#{wds.port}" unless show_as_html?
stylesheet_pack_tag(source, options)
else
wicked_pdf_stylesheet_pack_tag(source)
end
end
def pdf_javascript_pack_tag(source)
if running_in_development?
options = {}
wds = Webpacker.dev_server
options[:host] = "#{wds.host}:#{wds.port}" unless show_as_html?
javascript_pack_tag(source, options)
else
wicked_pdf_javascript_pack_tag(source)
end
end
end
In the pdf layout (slim)
html
head
...
= pdf_stylesheet_pack_tag "pdf"
= pdf_javascript_pack_tag "pdf"
...
I already commented in the stackoverflow thread but for completeness I'll restate my comment here:
The solution of @artm is working perfectly for me except for the fact that the method show_as_html? has to be defined.
I did this by just defining it in the PdfHelper itself as
def show_as_html?
params[:debug].present?
end
This may not be the best solution but it basically works for the moment.
Another thing I came about is that using the custom helpers won't work when creating the pdf outside of the ActionController context. E.g. when doing
pdf_html = ActionController::Base.new.render_to_string(tamplate: "my_pdf_template.pdf", layout: "pdf")
pdf = WickedPdf.new.pdf_from_string(pdf_html)
I am running in undefined method pdf_stylesheet_pack_tag Errors. I went down the rabbit hole but it looks very nasty and I'd like to achieve a solution in the wicked_pdf upstream.
Any suggestions?
The instantiation of a ActionController::Base.new doesn't have view helpers included by default. That was a hacky way to reach into Rails internals for Rails versions less than 5, that should no longer be necessary today.
If you really wanted to make what you have work, you'd have to do something more like this:
renderer = ActionController::Base.new
renderer.extend(ApplicationHelper) # add helper methods
renderer.extend(Rails.application.routes.url_helpers) # add route helpers for link_to and such
pdf_html = renderer.render_to_string(tamplate: "my_pdf_template.pdf", layout: "pdf")
Rails now has a version of render that can be called like this from a model, or anywhere:
pdf_html = ApplicationController.render(
template: 'my_pdf_template',
format: :pdf,
layout: 'pdf',
assigns: { users: @users }
)
Please let me know if that helps or not!
This helped a lot. Thank you!
Most helpful comment
https://stackoverflow.com/a/60541688/538534
Analysis
The webpack helpers make several assumptions that might not hold in every project.
They produce two different results depending on what running_in_develpment? returns. With webpacker 3.0.0 or newer this method delegates to
Webpacker.dev_server.running?.Without the dev server running the helpers will assume the assets were precompiled and will attempt to paste the contents of the asset into a
<style>or<script>tag. This should work if assets are precompiled in production and are available in the filesystem of the environment where the application is running. This is most often true.With the dev server running the webpack helpers will return a tag with an asset path to the pack asset (eventually using standard
asset_pathhelper). The actual path depends on the rails config. In some cases the path will be incompatible with the html being rendered bywkhtmltopdffrom afile://...:if
config.action_controller.asset_hostisn't set theasset_pathwill produce relative paths. These will not work inwkhtmltopdfrendering from file.if
config.action_controller.asset_hostis set absolute URLs will be used through out the application (this is a general setting that controls whatasset_pathreturns). Nowwkhtmltopdfmight be able to fetch the assets, if it is running in the environment where the asset host can be resolved and accessed. This might not be true in a containerized application.Additional constraints
In our case we had some additional constraints:
our PDF emitting actions support a param that makes them pass
show_as_html: truetorender. This makeswicked_pdfskip PDF generation and return the intermediate HTML. We use this to debug HTML views in the browser. This html will be rendered by the developer's browser in a different environment than wherewkhtmltopdfruns.our development setup is single threaded because we use
better_errorsdebugger which relies on all requests being served by the same application instance. On the other hand we render PDF in the request. This means thewkhtmltopdfwon't be able to request the assets from the application (e.f. by passinghost: "localhost:3000"to theasset_path) because it already occupies the only available thread.Sample solution
Our solution has been to implement our own helpers that are more aware of our set up.
In production the default behavior is compatible with our setup, so we delegate to the original, which will find the assets in the filesystem and include them in the generated HTML.
In development, when generating the PDF, we pass the hostname/port of the webpack dev server to webpacker tag helpers (which will pass them down to
asset_path). Dev server runs in a separate process and hence will repond even while the application is inside the request handler.In development, when returning intermediate HTML (as determined by our custom helper
show_as_html?), don't passhost:toasset_pack_url, which will result in "normal" asset path like in the rest of the application, which will work in the browser.In the pdf layout (slim)