WickedPDF generating blank pdfs when attempting to render multiple pdfs - ruby-on-rails

I am using WickedPDF in a Rails 6 project to generate PDFs from HTML, and then combine them with prefilled PDF forms. The WickedPDF generated PDFs are split into two sections, each of which must be combined with a form. The forms must appear at the beginning of their respective sections.
I have attempted to generate two PDFs with WickedPDF and then combine them with the prefilled forms using combine_pdf in the appropriate order.
The render_ar_docs and render_gl_docs both work as expected when I visit their routes individually: they generate and save the expected PDF. However, when I call them sequentially in the print_complete_docs action, the resulting PDFs are a single blank page.
How can I generate multiple PDFs from a single action?
Thank you for your help.
def print_complete_docs
#policy.fill_and_save_acord28
#policy.fill_and_save_acord25
render_ar_docs
render_gl_docs
pdf = CombinePDF.new
pdf << CombinePDF.load("tmp/acord28.pdf")
pdf << CombinePDF.load("tmp/ar_docs.pdf")
pdf << CombinePDF.load("tmp/acord25.pdf")
pdf << CombinePDF.load("tmp/gl_docs.pdf")
pdf.save "tmp/complete_docs.pdf"
send_file("#{Rails.root}/tmp/complete_docs.pdf", filename: "tmp/#{#policy.legal_vesting} Complete Docs.pdf", type: 'application/pdf')
end
def render_ar_docs
render pdf: 'ar_docs',
layout: 'document',
save_to_file: Rails.root.join('tmp', "ar_docs.pdf"),
save_only: true
end
def render_gl_docs
render pdf: 'gl_docs',
layout: 'document',
save_to_file: Rails.root.join('tmp', "gl_docs.pdf"),
save_only: true
end

My issue seems to be related to trying to render multiple times in a single request, which Rails does not allow. Instead I should have used WickedPDF's pdf_from_string method.
My new (over verbose and un-refactored, but functional) method looks like this:
def print_complete_docs
#policy.fill_and_save_acord28
#policy.fill_and_save_acord25
render_ar_docs
render_gl_docs
pdf = CombinePDF.new
pdf << CombinePDF.load("tmp/acord28.pdf")
pdf << CombinePDF.load("tmp/ar_docs.pdf")
pdf << CombinePDF.load("tmp/acord25.pdf")
pdf << CombinePDF.load("tmp/gl_docs.pdf")
pdf.save "tmp/complete_docs.pdf"
send_file("#{Rails.root}/tmp/complete_docs.pdf", filename: "tmp/#{#policy.legal_vesting} Complete Docs.pdf", type: 'application/pdf')
end
def render_ar_docs
ar_docs = WickedPdf.new.pdf_from_string(
render_to_string(
'policies/ar_docs.html.erb',
layout:'document.html.erb',
locals: { policy: #policy }
),
layout: 'document.html.erb'
)
save_path = Rails.root.join('tmp','ar_docs.pdf')
File.open(save_path, 'wb') do |file|
file << ar_docs
end
end
def render_gl_docs
gl_docs = WickedPdf.new.pdf_from_string(
render_to_string(
'policies/gl_docs.html.erb',
layout:'document.html.erb',
locals: { policy: #policy }
),
layout: 'document.html.erb'
)
save_path = Rails.root.join('tmp','gl_docs.pdf')
File.open(save_path, 'wb') do |file|
file << gl_docs
end
end
PS: Thanks to #Unixmonkey for trying to help, and for his contributions to WickedPDF!

Related

RAILS - Generate PDF from HTML with GROVER

I want to use GROVER to export same ERB/HTML pages from my application as PDF.
It works, but the generated PDF seems to be missing the styles and formatting, no CSS seems to be processed.
Here my code within the controller:
html_string = render_to_string(
{
template: 'users/show.html.erb',
locals: { id: params[:id] }
})
pdf = Grover.new(html_string, format: 'A4').to_pdf
respond_to do |format|
format.html
format.pdf do
send_data(pdf, disposition: 'inline', filename: "Show_ID_#{params[:id]}", type: 'application/pdf')
end
end
My question is, how I can persuade GROVER also to process the CSS files ?
I had the same problem where my layout was rendered without the CSS. I was able to resolve the issue by adding the display_url option in Grover.
In local development this would be for example:
pdf = Grover.new(html_string, format: 'A4', display_url: "http://localhost:3000").to_pdf
You need to change your display_url depending on your environment.
If this does not work, make sure the html_string that you are rendering actually includes the correct markup. You can render out the html_string as HTML to verify all markup is included.

Encoding error using wicked_pdf and rails ActiveJob

I am using wicked-pdf to simplify generating pdf invoices in my Rails 5 application. I have two actions making use of the pdf functionality of wicked-pdf. The first action generates the pdf and renders it in a new browser window. The second method attaches the pdf to an email.
Both of these actions work just fine. The issue comes in when I set my pdf mailer action to 'deliver_later' using ActiveJob/Sidekiq. When I add 'deliver_later' I am presented with a error stating:
"\xFE" from ASCII-8BIT to UTF-8
This error does not happen if I use the "deliver_now" command. Using "deliver_now" send the email and attaches the PDF correctly.
Here is some of my code for the mailing action, mailer and job:
invoices_controller.rb
...
def create
respond_to do |format|
format.html do
pdf = render_to_string( pdf: #order.ruling_invoice,
template: "orders/invoices/show.pdf.haml",
encoding: "utf8",
locals: { order: #order.decorate}
)
SendInvoiceMailJob.new.perform( #order, pdf, #order.token )
redirect_to order_url(id: #order.id, subdomain: current_company.slug), notice: "This invoice has been emailed."
end
end
end
...
send_invoice_mail_job.rb
...
def perform( order, pdf, order_token )
InvoiceMailer.send_invoice(order, pdf, order_token).deliver_later
end
...
invoice_mailer.rb
...
def send_invoice( order, pdf_invoice , invoice_token)
#order = order
attachments["#{invoice_token}.pdf"] = pdf_invoice
mail(
to: #order.email,
from: #order.seller_email
)
end
...
Why would this code work using "deliver_now" in send_invoice_mail_job.rb but it doesn't work using "deliver_later" ?
You can't just throw binary data (the PDF) into job arguments.
https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple
You need to move the PDF compiling from outside of your mailer job to inside it. So in your controller, you can do:
SendInvoiceMailJob.new.perform(#order, #order.token)
Then in your mailer job you can do:
def send_invoice(order, invoice_token)
#order = order
pdf = render_to_string(
pdf: #order.ruling_invoice,
template: "orders/invoices/show.pdf.haml",
locals: { order: #order.decorate }
) )
attachments["#{invoice_token}.pdf"] = pdf_invoice
mail(
to: #order.email,
from: #order.seller_email
)
end
That way you're not passing the PDF binary into the jobs queue.

How to save a Prawn generated PDF to the public folder?

I'm using Prawn gem to generate PDFs In my application...
app/controllers/orders.rb
class OrdersController < ApplicationController
...
def show
respond_to do |format|
format.html
format.pdf do
require "prawn/measurement_extensions"
...
#render pdf document
send_data pdf.render,
filename: "order_#{#order.id}.pdf",
type: 'application/pdf',
disposition: 'inline'
end
end
end
...
end
And It's working fine for displaying, But My questions are..
How to -Automatically- save those generated pdfs in the public folder (folder for each day) after a successful order creation? I've tried searching Prawn Documentation But I've found nothing.
How to show orders in only pdf format? I've Tried to Comment the format.html line but It didn't work
When you call pdf.render and send it to the client with send_data, you're actually dumping the contents of the pdf over the wire. Instead of that, you could dump the result of pdf.render in a file with File.new and use send_file in the controller. Check the documentation for File. Alternatively, you could attach the generated pdf to the specific order using something like Paperclip.
If you generate the pdf as a file in the server, you should use send_file instead of send_data. Read more about this in the guides.
You can use Prawn's render_file method to save the generated PDF to a file:
pdf.render_file(Rails.root.join('public', "order_#{#order.id}.pdf"))
See documentation: https://prawnpdf.org/docs/0.11.1/Prawn/Document.html

Rails render multiple pdf files to a folder

I have a controller action that renders a pdf for download.
I want to render multiple pdfs to a tmp folder ( then zip them for download )
I can generate the pdfs and present to the user but I can't figure out how to create a folder to store them in.
I'm using prawn. It has the render_file method to save it to the filesystem but I don't know what directory it is or if other users could save their pdf's to the same folder, so I need to create a uniques folder for each user then save the pdf's there.
How can I do this?
my controller action is currently
def showpdf
respond_to do |format|
format.html
format.pdf do
#items.each do |pdf|
pdf = Prawn::Document.new(page_size: "A4",margin: [0,0,0,0])
# pdf creation stuff...
# this was used previously to render one pdf to the browser
# but I need to save multiple pdf's
#send_data pdf.render, filename: 'report.pdf', type: 'application/pdf'
end
end
end
You will need to store all files into tmp/your-folder folder, something like this
require 'prawn'
#items.each do |item|
pdf = Prawn::Document.new
pdf.text("Lets Zip All.")
pdf.render_file('tmp/your-folder/#{item.id}.pdf')
end
and then simply use https://github.com/rubyzip/rubyzip to zip the your-folder.
require 'zip'
folder = "tmp/your-folder/"
zipfile_name = "tmp/archive.zip"
input_filenames = Dir.entries("tmp/your-folder/").select {|f| !File.directory? f}
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
input_filenames.each do |filename|
zipfile.add(filename, folder + '/' + filename)
end
zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
end
Simply send the file to user. But if the PDF contains a lot of data move them to delayed jobs.
Hope this makes sense but if not please hit reply.

Rails send multiple files to browser using send_data or send_file

I am trying to send multiple files to the browser. I cant call send_data for every record like in the code below because i get a double render error. According to this post i need to create the files and zip them so i can send them in one request.
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
send_data #json_encrypted, :filename => "#{r.name}.json" }
end
I am creating an array of hashes with the #json_encrypted and the file_name for each record. My question is how can i create a file for every record and then bundle them into a zip file to then pass that zip file to send_file. Or better yet have multiple file download dialogs pop up on the screen. One for each file.
file_data = []
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
file_data << { json_encrypted: #json_encrypted, filename: "#{r.name}.json" }
end
So the issue i was having is that send_file does not respond to an ajax call which is how i was posting to that action. I got rid of the ajax, and am sending the necessary data though a hidden_field_tag and submitting it with jquery. The code below creates files for the data, zips them, and passes the zip file to send_data.
file_data.each do |hash|
hash.each do |key, value|
if key == :json_encrypted
#data = value
elsif key == :filename
#file_name = value
end
end
name = "#{Rails.root}/export_multiple/#{#file_name}"
File.open(name, "w+") {|f| f.write(#data)}
`zip -r export_selected "export_multiple/#{#file_name}"`
send_file "#{Rails.root}/export_selected.zip", type: "application/zip", disposition: 'attachment'
end

Resources