Wicked_pdf: Is it possible to render an inline template to PDF string?

Created on 18 Feb 2019  路  10Comments  路  Source: mileszs/wicked_pdf

Rails allows you to render an "inline" template, for cases where you have a dynamic template stored in a database:

render :inline => "<p>Hello World</p>", :layout => "application"

Is it possible to use render_to_string in the same way, by passing the complete template string, rather than the filename of a template on disk?

All 10 comments

Yes.

Here's an example of converting an html string to a PDF and rendering it:

def show
  pdf = WickedPdf.new.pdf_from_string(
    '<!doctype html><html><head></head><body><h1>Hello World!</h1></body></html>'
  )
  respond_to do |format|
    format.pdf end
      send_data pdf, type: 'application/pdf', disposition: 'inline'
    end
  end
end

Just make sure it's a complete, fully-formed HTML document, including doctype.

Please let me know if you have any trouble with this.

Thank you! So in my case, if I want to use rails templating to build my HTML document, then I'm guessing I would need to use Rails' render_to_string function first (not to be confused with wicked_pdf's render_to_string function)?

I'm not sure I'm understanding your follow-up question correctly. Maybe an example of what you are trying to do would help?

If you were using Rails' templating, then you'd use WickedPdf's hooks into render(pdf: 'mypdf') or render_to_string(pdf: 'mypdf') with the appropriate options shown in the README, which pretty much do what I just showed you under the hood, after assembling your templates and layout into an HTML tempfile.

So if you had a PostsController#show action, you'd create an app/views/posts/show.pdf.erb file with your html you want to be converted to PDF, and an app/views/layouts/pdf.erb file for the layout, and it'd look something like this:

class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    respond_to do |format|
      format.pdf do
        render pdf: 'mypdf'
      end
    end
  end
end

Rails' render_to_string works pretty much the same as render, but it outputs a string instead of a response body. If you use render_to_string(pdf: 'mypdf'), you'll get a stringified version of the PDF that can be used to perhaps save the final PDF to your database, or as a file attachment to an email.

Here's an example of how that might be used:

class PostsController < ApplicationController
  def email_post
    @post = Post.find(params[:id])
    pdf = render_to_string('email_post.pdf.erb', layout: 'pdf', pdf: 'mypdf')
    current_user.saved_posts.create(pdf_contents: pdf) # save to the db
    PostMailer.email_post(current_user, pdf).deliver # email as attachment
    redirect_to :show, notice: "Email sent!"
  end
end

class PostMailer < ApplicationMailer
  def email_post(user, pdf)
    attachments['post.pdf'] = pdf
    mail to: user.email, subject: "Here's your post!"
  end
end

Please let me know if this helps or not.

Thanks. I'm currently using render_to_string to save the PDF to my database鈥擨 don't want to render it to the browser. I'm able to do this with a static template saved on disk, as per your example.

However, I'm working on a multi-tenant application, and would like to use a custom template per-tenant. The template is saved as a string in my database. I want to be able to render that template to a pdf string, using all of Rails' templating + layout functionality.

I think I see.

So you are doing something like:

pdf = render_to_string('email_post.pdf.erb', layout: 'pdf', pdf: 'mypdf')

but you don't want to hard-code layout: 'pdf', as some users might have a different layout.

I don't believe that render allows you to specify an HTML string for the layout; just a string to a template stored in one of Rails' view paths.

If you aren't pre-saving these, you may need to use layout: false, and apply the layout to your HTML string beforehand something like this:

custom_layout = '<!doctype html><html><head></head><body><%= body %></body></html>'
html_contents = render_to_string('email_post.pdf.erb', layout: false)
template = Erubis::Eruby.new(custom_layout)
complete_html_document = template.result(body: html_contents)
pdf_string = WickedPdf.new.pdf_from_string(complete_html_document)

Rails does let you render a raw string as a template, in the manner I indicated in my initial post. See here:
https://guides.rubyonrails.org/layouts_and_rendering.html#using-render-with-inline

But doing the rendering in two phases as you suggest might be the right approach鈥擨'll attempt to see if that works, given that wicked_pdf's version of render_to_string doesn't seem to support the :inline option.

Ah. I think that might fixed if we included the inline option in make_pdf's render_options here.

Additionally, I think you could use Rails's render_to_string, directly like so:

html = render_to_string :inline => "<p>Hello World</p>", :layout => "application"
pdf = WickedPdf.new.pdf_from_string(html)
send_data pdf, filename: 'file_name.pdf'

Do any of these options work for you?

@unixmonkey yes, this approach works for me. Thanks again.

@yowzadave FYI, I've released a new version (1.2.0) of the gem, which also includes support for the inline option.

Let me know if you have any trouble, and I'll reopen this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

thomasbalsloev picture thomasbalsloev  路  3Comments

m-andreas picture m-andreas  路  5Comments

supriyas12 picture supriyas12  路  4Comments

kovloq picture kovloq  路  4Comments

BlakeWard picture BlakeWard  路  3Comments