Prawn PDF: create a template without the default page - ruby

I want to create a document with prawn one or more pages long that uses a different template for each page.
Prawn::Document.generate("test.pdf") do
doc.faces.each do |face|
start_new_page(:template => face.background_path)
end
end
This works and creates a document, however the first page is a blank letter sized page and then my pages added with start_new_page show up. Is there a way to have prawn not generate that first page?
thanks!

pdf = Prawn::Document.new(:skip_page_creation => true)
doc.faces.each do |face|
pdf.start_new_page(:template => face.background_path)
< your page building code here >
end
should work if I'm reading the docs correctly.
My controller code looks like:
def pdf
#person = Person.find(params[:id])
send_data #person.as_pdf, :filename => "#{#person.pdfName}.pdf", :type => "application/pdf"
end
and the as_pdf method in person.rb looks like
def as_pdf(type = 'short')
pdf = Prawn::Document.new(:margin => [36, 36, 36, 36] )
driver = self.pdf_layout_info
driver.each do |element|
< lots of ugly layout logic >
end
pdf.render
end

Related

Image with axlsx gem

i am trying to add the images to excel. am able to send the different images when calling the method res_data. but after the execution i see the last image overwrites all the previous images captured.
here is my code. please help .am using axlsx gem in ruby
class abc
##row=1
##f = File.open('result.xlsx', 'w+')
##p = Axlsx::Package.new
##ws = ##p.workbook.add_worksheet
##ws.name="result"
def res_data(path)
##ws.add_image(:image_src => path, :noSelect => true, :noMove => true) do |image|
image.width=1366
image.height=768
image.start_at 0, ##row
end
##row=##row+42
##p.serialize(##f)
end
end
calling the method as (using capybara methods along with this)
require 'axlsx'
obj = abc.new
path='c:\test\image_file.png'
visit "http://google.com
save_screenshot(path, :full => true) # google home page screenshot is captured
obj.res_data(path)
click_button "btnI"
save_screenshot(path, :full => true) #clicks "i'm feeling lucky " and capture the screenshot again
obj.res_data(path)
am able to see the 'c:\test\image_file.png' file gets updated with the new image (when save screenshot is called)
but when i see the 'result_xlsx" file, it shows only the last image in all images.

How can I download multiple .xlsx files using axlsx gem?

Hi I'm having trouble downloading multiple files with axlsx. The problem is I'm sending an array of Id's to the controller and asking it to download the report using the render command. It raises an AbstractController::DoubleRenderError. I was thinking of overriding the error but realized it's a bad idea, I don't know what else to do... Any suggestions? Thanks.
My controller code looks like this:
def download_report
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
render :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
end
end
My axlsx template:
wb = xlsx_package.workbook
wb.add_worksheet(name: "Reports") do |sheet|
wb.styles do |s|
# template code
end
end
It is the built in expectation of Rails that you would call render once per request. And, the browser is going to expect one response per request. So, you are going to have to do something else!
You can use render_to_string, and combine the results into a zip file, serving that. See the bottom of this response.
Or, you could create a single spreadsheet and have each user's report show up on their own worksheet.
Or, on the client side, you could use javascript to request each spreadsheet and download each one separately.
The zip one would be something like this code, which uses render_to_string, rubyzip, and send_data:
def download_report
compressed_filestream = Zip::ZipOutputStream.write_buffer do |zos|
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
content = render_to_string :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
zos.put_next_entry("user_#{user_id}.xlsx")
zos.print content
end
end
compressed_filestream.rewind
send_data compressed_filestream.read, :filename => 'download_report.zip', :type => "application/zip"
end
Axlsx requires rubyzip, so you should have it already. And you probably want to lookup each user and use their name for the spreadsheet, unless you have it otherwise.

embedding barcode generated with barby gem into the prawn document

From my controller I create pdf:
def show
#order = Order.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #order }
format.pdf do
pdf = InvoicePdf.new(#order, view_context)
send_data pdf.render, filename: "invoice_#{#order.order_number}.pdf",
type: "application/pdf",
disposition: "inline",
size: 10
end
end
end
invoice_pdf.rb:
require 'barby'
require 'barby/barcode/code_39'
require 'barby/outputter/prawn_outputter'
class InvoicePdf < Prawn::Document
def initialize(order, view)
super({
:top_margin => 70,
:page_size => 'A4',
:font_size => 10,
:text => 8
})
#order = order
#view = view
order_number
barcode
end
def order_number
text "Order #{#order.order_number}"
end
def barcode
barcode = Barby::Code39.new #order.order_number
barcode.annotate_pdf(XXXX)
end
end
How should I modify my barcode method or the options marked as XXXX to embed barcode into PDF document?
EDIT
In your InvoicePdf class, change the barcode method to:
def barcode
barcode = Barby::Code39.new #order.order_number
barcode.annotate_pdf(self)
end
The annotate_pdf method takes a Prawn::Document as its argument, which is self here.
Original Answer
If you want to create a new pdf with your barcode, you can just do:
def barcode
_barcode = Barby::Code39.new(#order.order_number)
outputter = Barby::PrawnOutputter.new(_barcode)
outputter.to_pdf
end
Note that you can specify pdf options (including height, margins, page size, etc.) on the new PrawnOutputter or on the to_pdf call. See the documentation for more details: https://github.com/toretore/barby/wiki/Outputters and http://rdoc.info/github/toretore/barby/Barby/PrawnOutputter.
And if you want to write it to a file do:
File.open("my_file.pdf","w") { |f| f.print barcode }
Note that you can also just call _barcode.to_pdf, which seems to have the same effect as creating a new PrawnOutputter, but this functionality is not described in the Barby documentation.
If you have an existing pdf document (as a Prawn::Document) that you want to write a barcode to, note that you could do:
def barcode(p_pdf)
_barcode = Barby::Code39.new(#order.order_number)
_barcode.annotate_pdf(p_pdf)
end
In my case, barcode.annotate_pdf(self) was throwing some errors because it wasn't recognizing attributes like fill_color, so I ended up creating the Barby barcode as a PNG image and then inserting it in the PDF. Something like this:
require 'barby'
require 'barby/barcode/code_128'
require 'barby/outputter/png_outputter'
...
pdf = Prawn::Document.new(page_size: 'A4', margin: 20)
# Print data as text
order = "Order ##{#data.order_number} "
pdf.text_box order, size: 12, at: [0, pdf.cursor], width: 550
# Print data as image
barcode = Barby::Code128.new #data.order_number
barcode_blob = Barby::PngOutputter.new(barcode).to_png
# barcode.png is stored in tmp directory
File.open("tmp/barcode.png", 'wb'){|f| f.write barcode.to_png }
pdf.image "#{Rails.root.to_s}/tmp/barcode.png", image_width: 50, image_height: 50, position: :center
...
pdf.render
Option #2: Using StringIO instead of tmp File
# Print data as image
barcode = Barby::Code128.new #data.order_number
barcode_blob = Barby::PngOutputter.new(barcode).to_png
barcode_io = StringIO.new(barcode_blob)
barcode_io.rewind
pdf.image(barcode_io, image_width: 50, image_height: 50, position: :center)
...
pdf.render

Setting a hyperlink text color in axlsx

I'm trying to set the foreground color of text in a hyperlink cell but it doesn't seem to work.
Using something like: sheet["A1"].color = "0000FF" works fine for a normal cell, but not for a hyperlinked cell
This code simply creates a link to cell D1 on the "Log" sheet (which works fine) but A1 never turns blue!
sheet.add_hyperlink :location => "'Log'!D1", :target => :sheet, :ref => "A1"
sheet["A1"].color = "0000FF"
Thanks!
There are two important things to do before applying a color to a link:
You have to define the color within a style, and
You have to know the exact address of the cell in question.
Styles are normally applied to rows, but in this case you want to apply it to a specific cell. This is possible, but you need to address the cell directly through the Sheet Object. Also, and somewhat counter intuitively, the 'add_hyperlink' method is available to the Sheet object, not the Cell. So beware of that as well.
Here is an example of how to apply a style to a cell containing a link:
p = Axlsx::Package.new
p.workbook do |wb|
wb.styles do |s|
blue_link = s.add_style :fg_color => '0000FF'
wb.add_worksheet(:name => "Anchor Link Test") do |sheet|
sheet.add_row ['Title', 'Link']
# Define the row here, we will use that later
row = sheet.add_row ['Google', 'Click to go']
# Add the hyperlink by addressing the column you have used and add 1 to the row's index value.
sheet.add_hyperlink :location => "http://www.google.com", :ref => "B#{row.index + 1}"
sheet["B#{row.index + 1}"].style = blue_link
end
s = p.to_stream()
File.open("anchor_link_test.xlsx", 'w') { |f| f.write(s.read) }
end
end
Final note: You might note that I have written this spreadsheet using the methods
s = p.to_stream()
File.open("anchor_link_test.xlsx", 'w') { |f| f.write(s.read) }
There is evidence presented on the Axlsx Github Issues Page which shows that this means of writing out the file is significantly faster than
p.serialize
Just thought that deserved mention somewhere on StackOverflow!
This seems to work:
require 'axlsx'
p = Axlsx::Package.new
ws = p.workbook.add_worksheet
ws.add_row ['hoge-hoge']
ws['A1'].color = '0000FF'
ws.add_hyperlink :location => 'F6', :target => :sheet, :ref => 'A1'
p.serialize 'where_is_my_color.xlsx'
Can you post a larger example of your code that does not set the color?
Apparently Axlsx is only applying custom styles to String data types. Fixed this by setting each column to type :string like this:
Sheet.add_row [ "1", "2", "3" ], :types => [:string, :string, :string]
Thanks Randy!

Does a Markdown parser exist that can also generate Markdown in Ruby?

I want to parse a Markdown document so I get a tree structure that I am able to manipulate. Afterwards I want the output to be Markdown again.
Example:
# This is a title
And a short paragraph...
m = SomeLib.parse("# This is a tit...")
m.insert(1, "Here is a new paragraph") # or something simmilar
m.to_md
Should become
# This is a title
Here is a new paragraph
And a short paragraph...
As I want to heavily change the document I do not want to use REGEX or simillar techniques.
I looked into Maruku and BlueCloth but somehow I cannot generate Markdown again.
Probably not out of the box, but using redcarpet you could write a custom renderer to build your tree and then manipulate it.
Though beware in this case you can't reuse the Markdown and Renderer instance and all methods in the custom Renderer subclass are supposed to return a string. Something like this could be a starting point:
class StackRenderer < Redcarpet::Render::Base
attr_reader :items
def initialize
super
#items = []
end
def header(title, level)
items << { :text => title, :level => level, :type => :header }
"#{'#' * level} #{title}\n\n"
end
def paragraph(text)
items << { :text => text, :type => :paragraph }
"#{text}\n\n"
end
end
# example...
sr = StackRenderer.new
md = Redcarpet::Markdown.new(sr)
text = <<-EOF
# This is a title
And a short paragraph...
EOF
md.render(text) # => "# This is a title\n\nAnd a short paragraph...\n\n"
sr.items # => [{:type=>:header, :level=>1, :text=>"This is a title"},
# {:type=>:paragraph, :text=>"And a short paragraph..."}]

Resources