embedding barcode generated with barby gem into the prawn document - ruby

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

Related

ruby and Gtk::FontChooserDialog.new font size

I am using ruby 2.6.3, installed by compiling the source.
When using Gtk::FontChooserDialog.new, the default font size given is 10.
Is it possible to call Gtk::FontChooserDialog.new with a different size, such
as 24, so that I can avoid having to change the size each time I select a font.
Here is how I am doing things:
dialog = Gtk::FontChooserDialog.new(:title => "Select font",
:parent => self,
:action => Gtk::FileChooserAction::OPEN,
:buttons => [[Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT], [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL]])
I have tried (in the argument list) :size => 24, :default_size => 24, etc. This does not work. I'm just guessing here. I have searched a lot, with no luck. I also looked in the gem sample dirs at test-gtk-font-chooser-dialog.rb and other files but no luck.
I am using Linux Mint Mate 19.1, installed a couple of weeks ago.
You need to set the size through a Pango.FontDescription. A short example in Python would be:
font_chooser = Gtk.FontChooserDialog.new(title = "Select font", parent = self)
font_description = Pango.FontDescription.new()
font_description.set_size(24 * Pango.SCALE)
font_chooser.set_font_desc(font_description)
EDIT
And here is a complete example in Ruby:
#!/usr/bin/ruby
'''
ZetCode Ruby GTK tutorial
This program runs a font dialog with a default (settable) font size.
Author: Jan Bodnar
Website: www.zetcode.com
Last modified: May 2014
'''
require 'gtk3'
require 'pango'
class RubyApp < Gtk::Window
def initialize
super
init_ui
end
def init_ui
set_title "Center"
signal_connect "destroy" do
Gtk.main_quit
end
button = Gtk::Button.new
button.set_label "Font"
button.signal_connect "clicked" do
dialog = Gtk::FontChooserDialog.new(:title => "Select font", :parent => self, :action => Gtk::FileChooserAction::OPEN, :buttons => [[Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT], [Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL]])
font_description = Pango::FontDescription.new
font_description.set_size 24 * Pango::SCALE
dialog.set_font_desc font_description
dialog.run
end
add button
set_default_size 300, 200
set_window_position Gtk::WindowPosition::CENTER
show_all
end
end
window = RubyApp.new
Gtk.main

Bounding box across multiple pages with Prawn PDF

I'm trying to create a document with Prawn Ruby PDF generator, but I'm facing the following problem:
The image below shows what structure I'm trying to do.
And this is the example code that tries to mimics my real scenario with the way that I'm trying to achieve this. The 2.times and (50.times.map { |i| i.to_s }.join("\n")) mimics dynamic data.
require 'prawn'
class MyPdf
def self.to_pdf(*args)
new(*args).to_pdf
end
def to_pdf
pdf.move_down 200
2.times do
pdf.bounding_box(
[0, pdf.cursor],
width: pdf.bounds.width
) do
pdf.text (50.times.map { |i| i.to_s }.join("\n"))
pdf.stroke_bounds
end
end
pdf
end
def pdf
#pdf ||= Prawn::Document.new(page_size: 'A4')
end
end
But I'm having a lot of trouble with the dynamic bounding box placing.
Do you people know a way to achieve this with or without bounding boxes?
You may be looking for span:
def to_pdf
pdf.move_down 200
2.times do
pdf.span(pdf.bounds.width) do
pdf.text (50.times.map { |i| i.to_s }.join("\n"))
pdf.stroke_bounds
end
end
end
I failed to do that with boundig boxes. May be this is not the best solution, but you can do this with tables :
data = []
500.times do |i|
data.push [i.to_s]
end
table(data, width: bounds.width) do |t|
t.cells.border_width = 0 # We don't want to see a table
t.before_rendering_page do |page|
page.row(0).border_top_width = 1
page.row(-1).border_bottom_width = 1
page.column(0).border_left_width = 1
page.column(-1).border_right_width = 1
end
end
source : http://prawnpdf.org/prawn-table-manual.pdf (page 17)
Check your margins to have the continuation on the top of the next page

Controlling content flow with Prawn

Let's say we want to display a title on the first page that takes up the top half of the page. The bottom half of the page should then fill up with our article text, and the text should continue to flow over into the subsequent pages until it runs out:
This is a pretty basic layout scenario but I don't understand how one would implement it in Prawn.
Here's some example code derived from their online documentation:
pdf = Prawn::Document.new do
text "The Prince", :align => :center, :size => 48
text "Niccolò Machiavelli", :align => :center, :size => 20
move_down 42
column_box([0, cursor], :columns => 3, :width => bounds.width) do
text((<<-END.gsub(/\s+/, ' ') + "\n\n") * 20)
All the States and Governments by which men are or ever have been ruled,
have been and are either Republics or Princedoms. Princedoms are either
hereditary, in which the bla bla bla bla .....
END
end
end.render
but that will just continue to show the title space for every page:
What's the right way to do this?
I have been fighting with this same problem. I ended up subclassing ColumnBox and adding a helper to invoke it like so:
module Prawn
class Document
def reflow_column_box(*args, &block)
init_column_box(block) do |parent_box|
map_to_absolute!(args[0])
#bounding_box = ReflowColumnBox.new(self, parent_box, *args)
end
end
private
class ReflowColumnBox < ColumnBox
def move_past_bottom
#current_column = (#current_column + 1) % #columns
#document.y = #y
if 0 == #current_column
#y = #parent.absolute_top
#document.start_new_page
end
end
end
end
end
Then it is invoked exactly like a normal column box, but on the next page break will reflow to the parents bounding box. Change your line:
column_box([0, cursor], :columns => 3, :width => bounds.width) do
to
reflow_column_box([0, cursor], :columns => 3, :width => bounds.width) do
Hope it helps you. Prawn is pretty low level, which is a two-edged sword, it sometimes fails to do what you need, but the tools are there to extend and build more complicated structures.
I know this is old, but I thought I'd share that a new option has been added to fix this in v0.14.0.
:reflow_margins is an option that sets column boxes to fill their parent boxes on new page creation.
column_box(reflow_margins: true, columns: 3)
So, the column_box method creates a bounding box. The documented behavior of the bounding box is that it starts at the same position as on the previous page if it changes to the next page. So the behavior you are seeing is basically correct, also not what you want. The suggested workaround I have found by googling is to use a span instead, because spans do not have this behavior.
The problem now is, how to build text columns with spans? They don't seem to support spans natively. I tried to build a small script that mimicks columns with spans. It creates one span for each column and aligns them accordingly. Then, the text is written with text_box, which has the overflow: :truncate option. This makes the method return the text that did not fit in the text box, so that this text can then be rendered in the next column. The code probably needs some tweaking, but it should be enough to demonstrate how to do this.
require 'prawn'
text_to_write = ((<<-END.gsub(/\s+/, ' ') + "\n\n") * 20)
All the States and Governments by which men are or ever have been ruled,
have been and are either Republics or Princedoms. Princedoms are either
hereditary, in which the bla bla bla bla .....
END
pdf = Prawn::Document.generate("test.pdf") do
text "The Prince", :align => :center, :size => 48
text "Niccolò Machiavelli", :align => :center, :size => 20
move_down 42
starting_y = cursor
starting_page = page_number
span(bounds.width / 3, position: :left) do
text_to_write = text_box text_to_write, at: [bounds.left, 0], overflow: :truncate
end
go_to_page(starting_page)
move_cursor_to(starting_y)
span(bounds.width / 3, position: :center) do
text_to_write = text_box text_to_write, at: [bounds.left, 0], overflow: :truncate
end
go_to_page(starting_page)
move_cursor_to(starting_y)
span(bounds.width / 3, position: :right) do
text_box text_to_write, at: [bounds.left, 0]
end
end
I know this is not an ideal solution. However, this was the best I could come up with.
Use floats.
float do
span((bounds.width / 3) - 20, :position => :left) do
# Row Table Code
end
end
float do
span((bounds.width / 3) - 20, :position => :center) do
# Row Table Code
end
end
float do
span((bounds.width / 3) - 20, :position => :right) do
# Row Table Code
end
end
Use Prawns grid layout instead. It is very well documented...and easier to control your layout.

Prawn PDF: create a template without the default page

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

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