Rails + Prawn_rails: How do I add an image to my PDF? - ruby-on-rails

I'm using this gem to use Prawn to create a PDF: https://github.com/Whoops/prawn-rails
However, I can't seem to figure out how to add an image. I've tried pdf.image "path/to/img.jpg but it would say the file is not a recognized format.
I've also looked into this on page 101: http://prawn.majesticseacreature.com/manual.pdf , but it doesn't work.
This is happening in the views:
prawn_document() do |pdf|
pdf.image "#{Rails.root}/public/logo.gif"
end
This throws:
Prawn::Errors::UnsupportedImageType at /admin/purchases/6188.pdf
image file is an unrecognised format
Same happens for a .jpg image

I would do something like this.
gem 'prawn'
bundle install
In your controller.
def controller_method
pdf = Prawn::Document.new
begin
pdf_file_path = "#{Rails.root}/public/output"
full_doc = "#{Rails.root}/public/something.png"
pdf.image full_doc
pdf.start_new_page
pdf.render_file pdf_file_path
rescue Prawn::Errors::UnsupportedImageType
flash[:notice] = "Image unsupported"
redirect_to '/handle'
end
end

Adding an image is easy enough.
def download
#document = Document.find(params[:id])
tmp_file = Tempfile.new(Digest::MD5.hexdigest(rand(12).to_s))
pdf = Prawn::Document.generate(tmp_file.path, :margin => 0, :page_size => "A4", :skip_page_creation => true) do |posting|
posting.start_new_page
posting.image #document.user.logo.path
send_data posting.render, :filename => "whatever.pdf", :type => "application/pdf"
end
end
Obviously #document.user.logo.path could be a literal path, that's just a Paperclip attachment in this case.
UPDATE
If it's a literal path you might need to do something like this:
require "open-uri"
def download
...
posting.image File.open("/path/to/image.jpg")
...
end

It seems the latest prawn (version 1.0.0) has changed. You'll get
undefined method `image=' for #<Prawn::Document
if you try and apply something to pdf.image.
This worked for me:
img = "#{Rails.root}/public/my_lovely_image.png"
Prawn::Document.new(background: img)

Related

Rails 4, How to add an s3 image to wicked pdf

In rails 4, I am using wicked_pdf gem for .pdf file download. I have to add an image inside this pdf, right now image is rendering through wicked_pdf_image_tag in development but in test environment image(s3) is not rendering.
Used Gems are,
gem 'wicked_pdf', '1.0.3'
gem 'wkhtmltopdf-binary', '0.9.9.3'
In initializers,
class WickedPdf
wkhtmltopdf_path = Rails.env.production? ? "#{Rails.root}/bin/wkhtmltopdf-amd64" : "#{Rails.root}/bin/wkhtmltopdf-amd64"
WICKED_PDF = {
:exe_path => wkhtmltopdf_path,
:wkhtmltopdf => wkhtmltopdf_path
}
end
In controller,
respond_to do |format|
format.html {
render :pdf => "sample",
:margin => {:top => 10, :bottom => 10, :left => 10, :right => 10},
:orientation => 'Portrait', # default , Landscape,
:no_background => true
}
end
In views, I have tried to load through
<%= Rails.env.development? ? wicked_pdf_image_tag("img/logo.png") : wicked_pdf_image_tag("#{Rails.root}/public/assets/img/logo.png") %>
<%= Rails.env.development? ? wicked_pdf_image_tag("img/logo.png") : wicked_pdf_image_tag("#{Rails.root}/assets/img/logo.png") %>
<%= image_tag(ActionController::Base.helpers.asset_path('img/logo.png')) %>
How can I load s3 image in pdf file?
You can put the image in a s3 bucket and make it public. After that try it like below. If you use the below code no need to use separate syntax for different environments, it will works for all .Hope it works.
<%= wicked_pdf_image_tag('//s3.amazonaws.com/bucket_name/image.png') %>
I have worked on the similar functionality but using PDFKit gem. But I think rendering logic will be almost similar.
Below is the code where I rendered my partial in controller
def print_batch
batch = Batch.find(params[:id])
respond_to do |format|
format.pdf {
html = render_to_string("_batch",:formats => [:html], layout: false , locals: { batch: batch })
Rails.logger.debug(html.inspect)
kit = PDFKit.new(html)
send_data(kit.to_pdf, :filename => "file_name_#{batch.id}.pdf", :type => 'application/pdf') and return
}
format.html
format.json { render json: {id: batch.id, processed: batch.processed?} }
end
end
In _batch.html.haml. You can see I have used person.s3_logo for rendering the pdf image.
- logo_image_pdf = person.logo.present? ? person.s3_logo : default_credit_logo
- logo_image_html = person.logo.present? ? image_path(Person.logo.thumb('100x100').url) : image_path('default_credit_logo.png')
- logo_image = params[:format] == 'pdf' ? logo_image_pdf : logo_image_html
.bucks
%img.logo{src: logo_image }
In Person.rb model. Anyway we cannot directly render image in PDF file directly from s3. So any gem will first download it in /tmp folder and render it. This is how I have done it in my model file.
def s3_logo
file = open(self.logo.remote_url)
file.path if file
end

Rails 3 Wicked PDF - include Paperclip S3 pdf files

I have a Rails 3 app that uses these gems:
gem 'paperclip'
gem 'wicked_pdf'
gem 'combine_pdf'
I'm using wicked_pdf to open a pdf for a costproject. The costproject has an HTML page called viewproject.pdf.erb.
I'm trying to combine the wicked pdf with the costproject attachments into a single pdf.
This is my controller code:
def viewproject
#costproject = Costproject.find(params[:costproject_id])
respond_to do |format|
format.html
format.pdf do
pdf = CombinePDF.new
pdf2 = render_to_string pdf: "Costproject.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.new(pdf2)
#costproject.attachments.each do |attachment|
pdf << CombinePDF.new(attachment.attach.path)
end
send_data pdf.to_pdf, :disposition => 'inline', :type => "application/pdf"
end
end
end
The line pdf << CombinePDF.new(pdf2) is giving me:
string contains null byte
If I look at pdf2, it starts like this - so it looks like a pdf:
>> pdf2
=> "%PDF-1.4\n1 0 obj\n<<\n/Title (\xFE\xFF)\n/Producer (wkhtmltopdf)\n/CreationDate (D:20150405202628)\n>>\nendobj\n4 0 obj\n<<\n/Type /ExtGState\n/SA true\n/SM 0.02\n/ca 1.0\n/CA 1.0\n/AIS false\n/SMask /None>>\nendobj\n5 0 obj\n[/Pattern /DeviceRGB]\nendobj\n8 0 obj\n<<\n/Type /XObject\n/Subtype /Image\n/Width 71\n/Height 75\n/BitsPerComponent 8\n/ColorSpace /DeviceRGB\n/Length 9 0 R\n/Filter
I also tried pdf << CombinePDF.new(pdf2.to_pdf)
Thanks for the help!
UPDATE1
As a test, to see if pdf2 is working, I did this successfully:
def viewproject
#costproject = Costproject.find(params[:costproject_id])
respond_to do |format|
format.html
format.pdf do
pdf2 = render_to_string pdf: "Costproject.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
send_data pdf2, :disposition => 'inline', :type => "application/pdf"
end
end
end
UPDATE2
Myst was correct about using parse. Thanks!
I am now using this line in the controller code:
pdf << CombinePDF.new(attachment.attach.url)
I get this error:
No such file or directory - http://s3.amazonaws.com/ ...
But, if I copy the http address and paste into the browser the pdf shows up.
I am editing this answer to reflect the issue of remotely stored PDF files.
I should point out that without a persistent connection to the S3 storage and without using the S3 API, the following solution WILL effect performance*.
As I pointed out, the CombinePDF.new method is the same as the CombinePDF.load method. It accepts a file name and attempts to open the file. The CombinePDF.parse method will accept raw PDF data and parses it into a PDF Object.
In the following code I use Net::HTTP.get(URI.parse(url)) to get the raw PDF data.
I recommend replacing this solution with a S3 native solution, so that the whole application can share one or more persistent connections. This is a performance issue that may or may not be important for you.
require 'net/http'
def viewproject
#costproject = Costproject.find(params[:costproject_id])
respond_to do |format|
format.html
format.pdf do
pdf = CombinePDF.new
pdf2 = render_to_string pdf: "Costproject.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
#costproject.attachments.each do |attachment|
pdf << CombinePDF.parse( Net::HTTP.get( URI.parse( attachment.attach.url ) ) )
end
send_data pdf.to_pdf, :disposition => 'inline', :type => "application/pdf"
end
end
end
* The performance hit is dependent on the amount of PDF attachments you have, on the number of users your app has, on network traffic, on your framework (single/multi-thread) and other factors.
A persistent connection should reduce the performance hit in a dramatic way, mainly due to the fact that establishing connections is an expensive action.

Get mosaic-like screenshot using IMGKit

Here I want to take a screenshot of an external url, and I use IMGkit with CarrierWave.
#Class Micropost
after_create :take_snapshot
def take_snapshot
file = Tempfile.new(["template_#{self.id.to_s}", 'jpg'], 'tmp', :encoding => 'ascii-8bit')
file.write(IMGKit.new(self.external_url).to_jpg)
file.flush
self.snapshot = file
self.save
file.unlink
end
and the screenshot get generated is.....
So whats wrong with it?

How to save pdf file inside the public folder using wicked_pdf

Right Now i am using Rails 3.0.0.i already installed the gem wicked_pdf.Now to want to save the pdf file inside the public folder.please help me.
If you need to just create a pdf and not display it:
# create a pdf from a string
pdf = WickedPdf.new.pdf_from_string('<h1>Hello There!</h1>')
# create a pdf from string using templates, layouts and content option for header or footer
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'})}
# or from your controller, using views & templates and all wicked_pdf options as normal
pdf = render_to_string :pdf => "some_file_name"
# then save to a file
save_path = Rails.root.join('pdfs','filename.pdf')
File.open(save_path, 'wb') do |file|
file << pdf
end
This is top answer on How to convert string in pdf in ruby on rails
And if you aren't against i put one of the answer:
Some services returns pdf as a string like: JVBERi0xLjQKJeLjz9MKNCAwIG9iago8PC9Ue. . .
You can create a pdf file from the sting, with:
f = File.new("/tmp/file.pdf", "w")
f.write(Base64.decode64(invoice[:file]).force_encoding('UTF-8'))
f.close
And then you can open pdf file with AcrobatReader or another pdf reader.
Wish it helps.
Also, you can do this:
render :pdf => 'foo',
:save_to_file => Rails.root.join('public', "foo.pdf"),
:save_only => true

Difficulty with send_data in Ruby on Rails in conjunction with Spreadsheet plug-in

I have a function in a controller that takes in some specifications and generates a report on them. This function user_report is called in a view:
< %= submit_to_remote 'submit-button', "Export Report to Excel", :url => { :controller => :reports, :action => :user_report, :print_state => 'print'} % >
In reports_controller I use the Spreadsheet plugin to generate an Excel file within the user_report function. I want to use send_data to stream the file to the user without creating it on the server first. The research I've done shows that using StringIO is the way to go, as shown below. Frustratingly, nothing happens when I call send_data. The plugin seems to work well creating a file and saving it on the server, but does nothing when I use send_file, suggesting that the problem doesn't lie in the plugin. But then what am I doing wrong with send_file/send_data?
The function itself looks like this:
def user_report
if request.post?
unless params[:reports][:userid].blank?
#userid=params[:reports][:userid]
end
if params[:print_state]=='print'
report = Spreadsheet::Workbook.new
info = report.create_worksheet :name => 'User Information'
info.row(1).push 'User ID', #userid
#outfile = "Report_for_#{#userid}.xls"
require 'stringio'
data = StringIO.new ''
report.write data
send_data data.string, :type=>"application/excel", :disposition=>'attachment', :filename => #outfile
end
respond_to do |format|
format.js { }
end
end
end
The log file reads
2010-10-18 14:13:59 INFO -- Sending data Report_for_jjohnson.xls
but no download begins in-browser. I've succeed in using send_data on this app before, which is confusing.
I'm using Rails v2.3, Ruby v1.8.7, and Spreadsheet v6.4.1 at spreadsheet.rubyforge.org.
Just change the line:
send_data data.string, :type=>"application/excel", :disposition=>'attachment', :filename => #outfile
to:
send_data data.string.bytes.to_a.pack("C*"), :type=>"application/excel", :disposition=>'attachment', :filename => #outfile
Even though I dont like to write and delete , but with spreadsheet seems like the only solution.
# write the file
book.write "Employee_History_#{ params[:id]}.xls"
# send the file
send_file "Employee_History_#{ params[:id]}.xls", :type => "application/vnd.ms-excel", :filename => "data.xls", :stream => false
# and then delete the file
File.delete("Employee_History_#{ params[:id]}.xls")
For someone looking at this in (or after) 2022, a possible solution to this would be to use Axlsx Gem. The interface provides a method for converting it to a StringIO object. (From Axlsx Documentation)
#serialize to a file
p = Axlsx::Package.new
# ......add cool stuff to your workbook......
# Serialize to a stream
s = p.to_stream()
send_data(
s.read,
:type => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
:disposition => 'attachment',
:filename => #filename
)

Resources