Nathan Long
Nathan Long

Reputation: 125882

How can I have Rails render PDF-specific HTML for PDFKit?

I'm using the PDFKit middleware to render PDFs. Here's what it does:

Generally, I want that behavior. But I have one case where I actually need my app to render different content based on the fact that a PDF was requested.

PDFKit provides me a marker to detect that it is planning to render my response: it sets env["Rack-Middleware-PDFKit"] to be true.

But I need to tell Rails that, based on that flag, I want it to render show.pdf.haml. How can I do that?

Upvotes: 4

Views: 1915

Answers (2)

Shane
Shane

Reputation: 379

You could also use PDFKit without middleware.

Upvotes: 1

Nathan Long
Nathan Long

Reputation: 125882

Set request.format and response headers

Figured it out. According to the Rails source, request.format = 'pdf' will manually set the response format to PDF. This means that Rails will render, for example, show.pdf.haml.

However, now PDFKit will not convert the response to an actual PDF, because the Content-Type header says it's already PDF, when we're actually only generating HTML. So we also need to override the Rails' response header to say that it's still HTML.

This controller method handles it:

# By default, when PDF format is requested, PDFKit's middleware asks the app
# to respond with HTML. If we actually need to generate different HTML based
# on the fact that a PDF was requested, this method reverts us back to the
# normal Rails `respond_to` for PDF.
def use_pdf_specific_template
  return unless env['Rack-Middleware-PDFKit']

  # Tell the controller that the request is for PDF so it 
  # will use a PDF-specific template
  request.format = 'pdf'
  # Tell PDFKit that the response is HTML so it will convert to PDF
  response.headers['Content-Type'] = 'text/html'
end

Which means the controller action looks like:

def show
  @invoice = Finance::Invoice.get!(params[:id])

  # Only call this if PDF responses should not use the same templates as HTML
  use_pdf_specific_template

  respond_to do |format|
    format.html
    format.pdf
  end
end

Upvotes: 5

Related Questions