5

This is what the documentation of wicked pdf specifies:

WickedPdf.new.pdf_from_string(
render_to_string(:pdf => "pdf_file.pdf", :template => 'templates/pdf.html.erb', :layout => 'pdfs/layout_pdf'), 
:footer => {:content => render_to_string({:template => 'templates/pdf_footer.html.erb', :layout => 'pdfs/layout_pdf'})}
)   

What i get is ActionView::MissingTemplate Even though i have pdf.html.erb in directory. I use a gen_pdf method in the application controller and an equivalent pdf.html.erb in the views/application folder. What am i missing.

user1323136
  • 889
  • 2
  • 11
  • 20

4 Answers4

12

Don't Use template.

I ran into the same problem as you. You have to render your PDF quite differently when you're doing it in a Controller action as opposed to a Mailer or something else.

First off, using template won't work. There's some other nuances but here is my implementation you can build off of:

notification_mailer.rb

def export
  header_html = render_to_string( partial: 'exports/header.pdf.erb', 
                                  locals:  { report_title: 'Emissions Export' } )

  body_html   = render_to_string( partial: "exports/show.pdf.erb" )

  footer_html = render_to_string( partial: 'exports/footer.pdf.erb' )

  @pdf = WickedPdf.new.pdf_from_string( body_html,
                                        orientation: 'Landscape',
                                        margin: { bottom: 20, top: 30 },
                                        header: { content: header_html },
                                        footer: { content: footer_html } )

  # Attach to email as attachment.
  attachments["Export.pdf"] = @pdf

  # Send email. Attachment assigned above will automatically be included.
  mail( { subject: 'Export PDF', to: 'elon@musk.com' } )
end
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245
4

If you are using wicked_pdf and try to generate pdf outside of controller (i.e. in a cron job) you can do it this way (raild 4.1 +):

# app/renderers/pdf_renderer.rb

class PdfRenderer < ActionController::Metal
  include ActionView::ViewPaths
  include AbstractController::Rendering
  include AbstractController::Helpers
  include ActionController::Helpers
  include ActionView::Rendering
  include ActionView::Layouts
  include ActionController::Rendering
  include ActionController::Renderers
  include ActionController::Renderers::All

  append_view_path "app/views"

  View = Struct.new(:layout, :template, :locals)

  def render_pdf(view)
    wicked = WickedPdf.new
    pdf_string = render_to_string(template: view.template, layout: view.layout, locals: view.locals)
    # Make a PDF in memory
    pdf_file = wicked.pdf_from_string(pdf_string, pdf: "pdf_name",
                                      page_size: 'A4',
                                      wkhtmltopdf: ENV['WKHTMLTOPDF_EXECUTABLE_PATH'],
                                      margin: { top: '0.5in', bottom: '1in', left: '0.5in', right: '0.5in'}
    )

    # Write it to tempfile
    tempfile = Tempfile.new(['document', '.pdf'], Rails.root.join('tmp'))
    tempfile.binmode
    tempfile.write pdf_file
    tempfile.close
    tempfile
  end
end

and call this method:

  renderer = PdfRenderer.new
  pdf_view = PdfRenderer::View.new.tap do |v|
    v.template = "postal/#{template_file}.pdf.erb"
    v.layout = 'layouts/layout.pdf.erb'
    v.locals = {:user=> @user, :other_stuff => @details}
  end
  temp_pdf = renderer.render_pdf pdf_view

now you can use temp_pdf.path to do whatever you want with this file (i.e. send email)

you can read more about this way here: http://blog.arkency.com/2014/03/pdf-in-rails-without-controllers/

yossico
  • 3,421
  • 5
  • 41
  • 76
  • What are the modules for Rails 5? – Sushil May 24 '18 at 13:07
  • although this strategy worked for me, I didn't like how heavy it felt to create the `PdfRenderer` class. Instead, I preferred to simply use `ApplicationController.render`which gave me everything wicked_pdf needed to be happy (since wicked_pdf is seemless from a controller context). I went ahead and posted the code that worked for me as an answer. – Neil Dec 16 '22 at 19:44
2

You can have something like this inside your mailer:

class ReportMailer < ActionMailer::Base

  default :from => DEFAULT_FROM

  def report_pdf(user, bookings)
    @bookings = booking
    @user = user
    mail(:subject => 'Overtime', :to => user.email) do |format|
      format.text # renders overtime_pdf.text.erb for body of email
      format.pdf do
        attachments['monthly_report.pdf'] = WickedPdf.new.pdf_from_string(
            render_to_string(:pdf => 'monthly_report', :template =>
                'hospital_bookings/index.pdf.erb', :layouts => 'pdf.html')
        )
      end
    end
  end
end 

Hope this helps. Also if you want further help it would be ideal to post some of your code so that others can gain better understanding on what you have done and what your trying to achieve.

Deej
  • 5,334
  • 12
  • 44
  • 68
0

In case this helps someone else out: I had to generate a pdf via wicked_pdf from a job. This strategy worked for me from within the perform(*args) action of the job. WickedPDF appears to be intended to work from a controller context, so we just harness the power of controllers via ApplicationController.render:

generatedPdf  = WickedPdf.new.pdf_from_string(
  ApplicationController.render(
    template: "path/to/your/view/template.pdf.erb",
    layout: "layouts/your_wicked_pdf_layout.html.erb",
    locals: {some_var: @first_var, some_other_var: @second_var}
  )
)

save_path = Rails.root.join('public','your_saved_file.pdf')
File.open(save_path, 'wb') do |file|
  file << generatedPdf
end
Neil
  • 4,578
  • 14
  • 70
  • 155