PDFkit gem table of contents - ruby-on-rails

Using version 0.8.2 of the PDFKit gem, passing in toc as indicated in the docs throws an error:
format.pdf {
html = render_to_string(:action => 'pdf.html.haml')
kit = PDFKit.new(html, {toc: true})
send_data kit.to_pdf, :filename => "thing.pdf", :type => 'application/pdf'
}
The generic error, which I'm showing so you can see that toc is being passed as wkhtmltopdf requires, and as the specs of the PDFkit gem suggest, without the --:
command failed (exitstatus=0): /Users/Emma/.rvm/gems/ruby-2.2.1/bin/wkhtmltopdf --page-size Letter --margin-top 0.75in --margin-right 0.75in --margin-bottom 0.75in --margin-left 0.75in --encoding UTF-8 toc - -

You can try
format.pdf {
html = render_to_string(:action => '<controller_name>/action_name')
kit = PDFKit.new(html.to_s, toc: '')
send_data kit.to_pdf, :filename => "thing.pdf", :type => 'application/pdf'
}

Related

batch jpg images in string blob not all written to file with ruby file open write barby gem rmagick

I am using barby gem to generate barcodes. I am doing it a batch at a time and then save all of them in a file to be viewed by the user. I am not trying to print one at a time.
def generate_barcode
number_of_instances = params[:times].to_i
value = 12.times.map{rand(10)}.join
barcodes = 10.times.collect { Barby::EAN13.new(value) } #collects ten barcodes in an array
processed_barcodes = barcodes.map {|barcode| barcode.to_jpg_2(:height => 60)} #returns an array and each element is a string of jpeg file
File.open('code.jpg', 'wb') do |f|
processed_barcodes.each {|barcode| f.write(barcode)} #stuck here
#f.puts(processed_barcodes) #tried this
end
send_file('code.jpg',
:type => 'image/jpeg',
:disposition => 'inline'
)
end
the view
<%= image_tag(url_for({:controller => 'business_partners', :action => 'generate_barcode', :format => 'jpg' })) %>
processed_barcodes.count came back with 10.
My code.jpg got processed but I only have 1 image.
each image in jpg string blob format looks like this
>> processed_barcodes.first
=> "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xFF\xDB\x00C\x00\x03\x02\x02\x02\x02\x02\x03\x02\x02\x02\x03\x03\x03\x03\x04\x06\x04\x04\x04\x04\x04\b\x06\x06\x05\x06\t\b\n\n\t\b\t\t\n\f\x0F\f\n\v\x0E\v\t\t\r\x11\r\x0E\x0F\x10\x10\x11\x10\n\f\x12\x13\x12\x10\x13\x0F\x10\x10\x10\xFF\xC0\x00\v\b\x00P\x00s\x01\x01\x11\x00\xFF\xC4\x00\e\x00\x01\x01\x01\x01\x01\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\a\b\x06\x05\x02\x03\t\xFF\xC4\x009\x10\x00\x00\x05\x03\x01\x05\x05\x03\f\x03\x01\x00\x00\x00\x00\x00\x01\x02\x03\x04\x06\x00\x05\a\b\x11\x12\x13\x14w\"79\xB6\xB7\x158\x87\t\x16\x18!#3FI\x85\xB5\xC4\xC5\x17$B%\xFF\xDA\x00\b\x01\x01\x00\x00?\x00\xFDS\xA5)JR\x94\xA5)JR\x95\x8F\xF2\xA6{\xCB\x11\xB9\xA6q\xB4\xD9e|\xBBH|>mt\xB2\xA7\xC8\xB6?*\xE9\x85\x82&\xED\xA2\x9BL\x98\x89\xF7\x17\xBA\xBF>\xC3\x89\x8An>\xC3\x01\x8AB\x01h\x1A`\xCAS\xBC\x89\xECo\x9E7\xDFhsx~\x01)[\xFDTR\xDE\xBA\\\xBD\xAB\xCE\xAF\xF6d.\xCE'*\x87`;\x05\xDC\xEC\x14\xBBM\xB6\x7F\xA4\xAC\xF7\x962l\xD25i\x9CJ\xFD\xA4\xD2\xE1\x0FR\xE8\xE1>E\xB2;\xEE\x82\xC1\tv\nmI2\x88}\xBD\xEA\xE6}\x80 _\xF66l\xDDM0$\x02\xC1\xAC\xCDI\xBD\x85\xE8\xAA\xEC\xEB$o\xBB\xCBs\v\xAD\xAEb\xA7\xB1\xD8\a\xB4\xDA\xA3\x7Fl\xD14\xF6\x02\e\x11\xD8\x82\x87&\xD4#\x86\x1D\xBBDD\xC0\x03W\xFCi\x9E\xF2\xC4\x83G9c*\xDD\xE5|\xC4\xA63\x0Fmt\xB5\xBF\xE4[\x13\x97tx5\xA6\xE8e8eL\x13>\xD7\xAE\xDC+\xB0\xC5\x12\xF6\xF76n\x14\xA5\b\x064\xD6f\xA4\xE4\x1A\xC6\xCB\x18\xAA\xEF\x929\x88\xB4f`\xDA\xD7ka\xECv\x04\xE5\xDA\x9Esi\xB5\x99>!P\x05\x0F\xB5\x93\xB7\tm1\x84\xDD\xBD\xFD\xBB\xE5)\x82\xFF\x00\xA4\xAC\xF7\x962l\xD25i\x9CJ\xFD\xA4\xD2\xE1\x0FR\xE8\xE1>E\xB2;\xEE\x82\xC1\tv\nmI2\x88}\xBD\xEA\xE6}\x80 _\xF66l\xDDM0\"!\x9E\xF2\xC5\xD2\x17\x15\xBB>\x95\xF1]\xDC\xA1\xF8R\xE8\xE9NE\xB1x\x8E\xAF\xD7\xF5\x9A]\x94\xD8\t\x80\a\x1D\x02\x14\x9B\x00\x00\xA9\xEC\xDA\x98\x10\xDF]p\x1A\x90\xD5\x1Ev\x81}1>i\xCEy\x1F\xF1_\xF8\xFB\xE6\x97\xFEc5}\x9B\xED>\x0F;\xF7\x89\e\x8D\xC4\xDF7\xDE\xEF\xEE\xED\xECn\xD25\xAA<\xEDp\xD7\xD68\xC2\x8E\xE7<He\xFB\xE7\xAF\xB4-\x9E\xCCf\x1CnB\xF5&l\xD3\xED\x81.)w\x11\xB6\xB2/d\xE1\xBD\xC1\xDAm\xE19\xC4\xDD\xFEK\xCFyb?\xA3\x9CO\x95m\x12\xBE^S&\x87\xB9\xBA]\x1F\xF2-\x8F\xCC:$\x1A\xEDt*\x9C3&)\x93c\xD6\x8D\xD5\xD8R\x81{\e\x9B7\fb\x8F\x01\x85uG\x9D\xA5\xB8_\x01K$3\x9En\xEB5\xE7=\xBA\xE3\xD9\x8C\xD3\xE6\xF8s\xE8\xFD\xA8\x9D\x92$\x05Oc\x17\xEE\xD1\xEC\x01~\xF7|{e!\xCB\xAF\xF4\xF5)\xBE\xCEp\x164\x9A\xCA_s\xB7\xA9\x04>\xCDt\xB8\xB9\xE1\x11>;\xA5\xD9$\xA2\xAAn\x10\nB\xEF\x1C\xE6\x1D\x85\x00(m\xD8\x00\x01\xF5U\x02\x95\x803\x87x\xBA\x97\xE9\xFEH\xF2\xAC\x16\xAA\xBA+\xFC=\xD0\fY\xFD\xE5J\xB4\e\xDE,;\xA7\xEA\xF9W\e\xD6U\x8Bwu\xF2qu\x02\xF9\xE6\xA6u\xAA\xB0\xDF\x87\xBEv\xE9\xFB?L\xEC5\x95p\xDF\x88Fv\xEA\x03?S,5\xAA\xB4\e\xDE,;\xA7\xEA\xF9W\e\xD2\x03\xDD\xD4\e\xA7\xFAq\xF3S\x8A\x95j\xFF\x00\xF3\x05\xF8Q\xFCzC|Sq\x0F\xC4\x7F1\xCC\xAA\xAB\x99<=\xF0OO\xDEzg~\xA9V\x9C}\xDDt\xB1\xFA\x8F\xAAqZ\xDF\xFAN\xF7X\xC3}?\x8F~\xDC\x85UiX\x038w\x8B\xA9~\x9F\xE4\x8F*\xC1j\xAB\xA2\xBF\xC3\xDD\x00\xC5\x9F\xDET\xABA\xBD\xE2\xC3\xBA~\xAF\x95q\xBDeX\xB7w_'\x17P/\x9EjgZ\xAB\r\xF8{\xE7n\x9F\xB3\xF4\xCE\xC3YW\r\xF8\x84gn\xA03\xF52\xC3Z\xABA\xBD\xE2\xC3\xBA~\xAF\x95q\xBD =\xDDA\xBA\x7F\xA7\x1F58\xA9V\xAF\xFF\x000_\x85\x1F\xC7\xA47\xC57\x10\xFCG\xF3\x1C\xCA\xAA\xB9\x93\xC3\xDF\x04\xF4\xFD\xE7\xA6w\xEA\x95i\xC7\xDD\xD7K\x1F\xA8\xFA\xA7\x15\xAD\xFF\x00\xA4\xEFu\x8C7\xD3\xF8\xF7\xED\xC8UV\x95\x803\x87x\xBA\x97\xE9\xFEH\xF2\xAC\x16\xAA\xBA+\xFC=\xD0\fY\xFD\xE5J\xB4\e\xDE,;\xA7\xEA\xF9W\e\xD6U\x8Bwu\xF2qu\x02\xF9\xE6\xA6u\xAA\xB0\xDF\x87\xBEv\xE9\xFB?L\xEC5\x95p\xDF\x88Fv\xEA\x03?S,5\xAA\xB4\e\xDE,;\xA7\xEA\xF9W\e\xD2\x03\xDD\xD4\e\xA7\xFAq\xF3S\x8A\x95j\xFF\x00\xF3\x05\xF8Q\xFCzC|Sq\x0F\xC4\x7F1\xCC\xAA\xAB\x99<=\xF0OO\xDEzg~\xA9V\x9C}\xDDt\xB1\xFA\x8F\xAAqZ\xDF\xFAN\xF7X\xC3}?
You open one file using File.open('code.jpg', 'wb') and write into it processed_barcodes times. If you want processed_barcodes files then you need to loop around File.open with unique names.
Instead of:
File.open('code.jpg', 'wb') do |f|
processed_barcodes.each {|barcode| f.write(barcode)} #stuck here
#f.puts(processed_barcodes) #tried this
end
Use this untested code:
processed_barcodes.each_with_index do |barcode, i|
File.open("code_#{i + 1}.jpg", 'wb') do |f|
f.write(barcode)
end
end
You'll need to figure out how to modify:
send_file('code.jpg',
:type => 'image/jpeg',
:disposition => 'inline'
)

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.

Wicked PDF RuntimeError

I have installed Wicked PDF on rails 4, and now I have this issue:
RuntimeError in ClientsController#show
Error: Failed to execute: ["C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe", "--footer-center", "Center", "--footer-left", "Left", "--footer-right", "Right", "file://C:/Users/Rashid/AppData/Local/Temp/wicked_pdf20141017-9664-18xoryq.html", "C:/Users/Rashid/AppData/Local/Temp/wicked_pdf_generated_file20141017-9664-zo89le.pdf"] Error: PDF could not be generated! Command Error: Loading pages (1/6) [> ] 0% [======> ] 10% Error: Failed loading page file://c/Users/Rashid/AppData/Local/Temp/wicked_pdf20141017-9664-18xoryq.html (sometimes it will work just to ignore this error with --load-error-handling ignore) Exit with code 1 due to network error: ContentNotFoundError
def show
respond_to do |format|
format.html
format.pdf do
#example_text = "some text"
render :pdf => "file_name",
:template => 'clients/show.pdf.erb',
#:layout => 'pdf',
:footer => {
:center => "Center",
:left => "Left",
:right => "Right"
}
end
end
end
I was getting the same error and fixed it by following directions here
https://github.com/mileszs/wicked_pdf/issues/157
Namely I switched to tempory fix of gem by changing gemfile entry to:
gem 'wicked_pdf', github: 'mileszs/wicked_pdf'
I believe it has to do with using file:// instead of file:///.
Find wicked_pdf.rb in path/to/gem/wkhtmltopdf/bin, on line 64 replace file:// with file:///.

PDF could not be generated!” with wkhtmltopdf in rails

I am using wkhtmltopdf(0.10.0 rc2) AND Wicked_pdf(0.9.4) for generating pdf report.
I am getting below error
Failed to execute:
"/usr/bin/wkhtmltopdf" -q --footer-left "[page]" --footer-right "Powered by AMP" --footer-font-size 7 --margin-top 5 --margin-bottom 10 --margin-left 10 --margin-right 10 --page-size "A4" "file:////tmp/["wicked_pdf", ".html"]29083-0" "/tmp/["wicked_pdf_generated_file", ".pdf"]29083-0"
Error: PDF could not be generated!
my controller:
render :pdf => "#{#note_name}",:layout => '/layouts/pdf_template.html.erb', :template=>'/leasing_activity/lease_act_pdf.html.erb'
gemfile:
gem "wicked_pdf","0.9.4"
gem "wkhtmltopdf-binary"
initializers/wicked_pdf.rb:
WickedPdf.config = {
:exe_path => '/usr/bin/wkhtmltopdf'
}

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