Prawn PDF Attachments in the Email

Prawn pdf attachments in the email

Take a look at the Action Mailer Guide. You need to call the attachments method for you to add an attachment.

Try this:

attachments['attachment_filename'] = InvoicePdf.new(invoice)

This is assuming that calling InvoicePdf.new(invoice) generates a file and returns an IO object representing that file. I also noticed that your InvoicePdf class initializer expects two parameters but you are passing only one.

Update:
Also note that Action Mailer will take the file name and work out the mime type, set the Content-Type, Content-Disposition, Content-Transfer-Encoding and base64 encode the contents of the attachment all for you so setting it manually isn't necessary unless you want to override the defaults.

Based on your pdf generation method, this will probably be better:

invoice = InvoicePdf.new(invoice)
attachments["invoice.pdf"] = { :mime_type => 'application/pdf', :content => invoice.render }
mail(:to => @user.email, :subject => "Your Invoice")

Attach Prawn pdf to email

How you do this depends on how long the PDF generation takes and/or how much load it places on your server and whether you care about that. In my case I was generating PDFs from user-generated-content and I was seeing some PDF creation times up in the 30+ seconds range. Solving for that becomes a run-the-job-somewhere-else and cache-it (whether DB or cloud storage) issue.

@toddmetheny is quite right in suggesting cloud storage for all but the simplest solutions. It gets more interesting if you are hosting on something like Heroku with ephemeral storage, or if you are separating PDF creation from email sending from user requests (e.g. from Heroku again, web dynos vs worker dynos). You can generate the PDF to a local temporary file, but that temporary file may not be there by the time you need to read it in your Mailer running on a 'worker'.

Really simple option

In your Mailer you could generate the PDF to a local file, read it back into memory, then attach it:

def invoice_email(user)
@user = user

attachments['filename_for_user.pdf'] = generate_pdf_content

mail(:to => user.email, :subject => "Invoice Recieved")
end

private

# I had troubles trying to get Prawn to generate 'in memory'
# so just write to, then read, then unlink a tempfile
def generate_pdf_content
pdf = some_method_that_generates_a_prawn_pdf_object
Tempfile.create do |f|
pdf.render_file f
f.flush
File.read(f)
end
end

I suggest you start here to get things working.

More complicated option

Someday you may want to separate the job that generates the PDF (which can take a long time) from the jobs that send email, which are usually much faster. The way I do this is to have a job that generates the PDF to a local Tempfile, then uploads that file to S3 storage and records the S3 object id (I do it in my database, you could just do it as a job attribute you push around).

When complete, that job creates a new mailer job. The mailer job (which may execute on a different server) downloads the PDF from S3 storage to a local file, then adds it to the email much like the simple option above.

Prawn PDF and Action Mailer not attaching PDF

I have resolved this. Realised i didn't have a view template.

Prawn PDF with Rails mailer?

I suppose it would be better if you store generated pdf somewhere - for caching purposes, etc.
But with current configuration, you can read generated page with Net::HTTP and attach response:

require 'net/http'

def your_mailer_method(record)
#...
attachment "application/pdf" do |a|
a.body = Net::HTTP.get('yourdomain.com', "/kase/#{record.id}.pdf")
a.filename="your_pdf_name.pdf"
end
end

Inserting external PDF into Prawn generated document

You're right about the lack of existing documentation for this - I found only this issue from 2010 which uses the outdated methods you describe. I also found this SO answer which does not work now since Prawn dropped support for templates.

However, the good news is that there is a way to do what you want with Ruby! What you will be doing is merging the PDFs together, not "inserting" PDFs into the original PDF.

I would recommend this library, combine_pdf, to do so. The documentation is good, so doing what you want would be as simple as:

my_prawn_pdf = CombinePDF.new
my_prawn_pdf << CombinePDF.new("my_bill_pdf.pdf")
my_prawn_pdf << CombinePDF.new("attachment.pdf")
my_prawn_pdf.save "combined.pdf"

Edit

In response to your questions:

I'm using Prawn to render a pdf view in Rails, which means that I don't think I get that kind of post-processing

You do! If you look at the documentation for combine_pdf, you'll see that loading from memory is the fastest way to use the gem - the documentation even explicitly says that Prawn can be used as input.

I'm not just tacking the PDFs to the end: a bill attachment must directly follow the generated page(s) for a bill

The combine_pdf gem isn't just for adding pages on the end. As the documentation shows, you can cycle through a PDF adding pages when you want to, for example:

my_pdf # previously defined
new_pdf = CombinePDF.new
my_pdf.pages.each.do |page|
i += 1
new_pdf << my_pdf if i == bill_number # or however you want to handle this logic
end
new_pdf.save "new_pdf.pdf"

Generate PDF file from Rails view

I decided to use wicked_pdf in the end as was suggested in the comment on my question. I was able to use it as such

mail subject: "Order #{order.id}" do |format|
format.text
format.html do
attachments["receipt #{Time.now}.pdf"] = WickedPdf.new.pdf_from_string(
render_to_string pdf: 'receipt', template: 'order_mailer/order_receipt.html.erb'
)
render 'order_receipt'
end
end
end

This still renders out emails in HTML and text forms but also attaches a PDF to the email which uses the same template as the mailer



Related Topics



Leave a reply



Submit