rails prawn draw a line inside table or cell - ruby-on-rails

I have a table made up of many cells, and in one cell i want to draw a horizontal line, and if i try horizontal_line it shows table document unsupported type.
and i'm using prawn ruby gem
Ex
cell_1 = make_cell(:content => "this row content comes directly ", height: 62.5.mm, size: 6)
cell_2 = make_cell(:content => "this row content comes directly ", height: 62.5.mm, size: 6)
cell_3 = make_cell(:content => "this row content comes directly ", height: 62.5.mm, size: 6)
t = make_table([[cell_1],[cell2], [cell3])
t.draw
i tried like using
cell4 = horizontal_rule(some values)
make_table shows error unrecognized table content, when i add cell4 to it.
How do i draw line inside the table or make cell

I was trying to do the same thing months a go but finally I decided to forget using prawn cause it make every thing hard! If you have enough time to use another pdf generator I recommend you to use Wicked_PDF gem (here) and draw your table with simple html and css. Wicked pdf is faster easier and makes you be more creative.

You need to override the Prawn::Table::Cell class. Here's how I did it:
class RedTriangleCell < Prawn::Table::Cell
def natural_content_width
0
end
def natural_content_height
0
end
def draw_content
color = #pdf.fill_color
#pdf.fill_color("e93131")
#pdf.fill_polygon([-5,2], [-1,8], [3,2])
#pdf.fill_color(color)
end
end
You can drop an instance of this as the cell content and it will render whatever you have in draw_content.

Related

Find PDF Form Field position

I realize this question has been asked a lot, but I could not find any information about how to do this in RoR. I am filling a PDF with form text fields using pdf-forms but this does not support adding images, and I need to be able to add an image of a customer's signature into the PDF. I have used prawn to render the image on the existing PDF, but I need to know the exact location to add the image on the signature line. So my question is how can I look at an arbitrary PDF and find the exact position of the "Signature" form field?
I ended up using pdf2json to find the x,y position of the form field. I generate a JSON file of the original pdf using this command:
%x{ pdf2json -f "#{form_path}" }
The JSON file is generated in the same directory as form_path. I find the field I want using these commands:
jsonObj = JSON.parse(File.read(json_path))
signature_fields = jsonObj["formImage"]["Pages"].first["Fields"].find_all do |f|
f["id"]["Id"] == 'signature'
end
I can use prawn to first create a new PDF with the image. Then using pdf-forms, I multistamp the image pdf onto the original PDF that I want to add the image to. But multistamp applies each page of the stamp PDF to the corresponding page of the input PDF so make sure your image PDF has the correct number of pages or else your image will get stamped on every page. I only want the image stamped onto the first page, so I do the following:
num_pages = %x{ #{Rails.configuration.pdftk_path} #{form_path} dump_data | grep "NumberOfPages" | cut -d":" -f2 }.to_i
signaturePDF = "/tmp/signature.pdf"
Prawn::Document.generate(signaturePDF) do
signature_fields.each do |field|
image Rails.root.join("signature.png"), at: [field["x"], field["y"]],
width: 50
end
[0...num_pages - 1].each{|p| start_new_page }
end
outputPDF = "/tmp/output.pdf"
pdftk.multistamp originalPDF, signaturePDF, outputPDF
You can use this gem 'wicked_pdf. You just write html, and this gem automatically convert it to pdf
Read more https://github.com/mileszs/wicked_pdf
Here's a pure ruby implementation that will return the field's name, page, x, y, height, and width using Origami https://github.com/gdelugre/origami
require "origami"
def pdf_field_metadata(file_path)
pdf = Origami::PDF.read file_path
field_to_page = {}
pdf.pages.each_with_index do |page, page_index|
(page.Annots || []).each do |annot|
field_to_page[annot.refno] = page_index
end
end
field_metas = []
pdf.fields.each do |field|
field_metas << {
name: field.T,
page_index: field_to_page[field.no],
x: field.Rect[0].to_f,
y: field.Rect[1].to_f,
h: field.Rect[3].to_f - field.Rect[1],
w: field.Rect[2].to_f - field.Rect[0]
}
end
field_metas
end
pdf_field_metadata "<path to pdf>"
I haven't tested it particularly thoroughly but the snippet can hopefully get you most of the way there.
Also -- keep in mind the above coordinates calculated are in points from the bottom left of the pdf page rather than the top right (and are not in pixels). I believe there's always 72 points per inch, and you can get the total page points by calling page.MediaBox in the pdf.pages loop above. If you're looking for pixel coordinates, you need to know the DPI of the resulting rendered document.

Render JSON to image in Prawn PDF

I'm using prawn pdf in conjunction with signature-pad gem in my rails 3.2 app and i'm having troubles converting the JSON data to an image to render in the pdf.
I have the signature-pad on completion throw the JSON data into the table and it looks like this.
JSON
[{"lx":29,"ly":18,"mx":29,"my":17},{"lx":29,"ly":19,"mx":29,"my":18},{"lx":29,"ly":24,"mx":29,"my":19},{"lx":29,"ly":27,"mx":29,"my":24},{"lx":29,"ly":30,"mx":29,"my":27},{"lx":29,"ly":32,"mx":29,"my":30},{"lx":32,"ly":32,"mx":29,"my":32},{"lx":33,"ly":32,"mx":32,"my":32},{"lx":35,"ly":31,"mx":33,"my":32},{"lx":39,"ly":24,"mx":35,"my":31},{"lx":42,"ly":16,"mx":39,"my":24},{"lx":48,"ly":7,"mx":42,"my":16},{"lx":51,"ly":2,"mx":48,"my":7},{"lx":54,"ly":-3,"mx":51,"my":2},{"lx":58,"ly":2,"mx":58,"my":1},{"lx":59,"ly":9,"mx":58,"my":2},{"lx":60,"ly":18,"mx":59,"my":9},{"lx":60,"ly":27,"mx":60,"my":18},{"lx":60,"ly":38,"mx":60,"my":27},{"lx":55,"ly":45,"mx":60,"my":38},{"lx":49,"ly":51,"mx":55,"my":45},{"lx":45,"ly":54,"mx":49,"my":51},{"lx":39,"ly":57,"mx":45,"my":54},{"lx":35,"ly":51,"mx":35,"my":50},{"lx":43,"ly":45,"mx":35,"my":51},{"lx":54,"ly":39,"mx":43,"my":45},{"lx":70,"ly":32,"mx":54,"my":39},{"lx":81,"ly":28,"mx":70,"my":32},{"lx":96,"ly":25,"mx":81,"my":28},{"lx":111,"ly":23,"mx":96,"my":25},{"lx":119,"ly":23,"mx":111,"my":23},{"lx":126,"ly":23,"mx":119,"my":23},{"lx":129,"ly":23,"mx":126,"my":23},{"lx":130,"ly":23,"mx":129,"my":23},{"lx":128,"ly":24,"mx":130,"my":23},{"lx":117,"ly":25,"mx":128,"my":24},{"lx":105,"ly":27,"mx":117,"my":25},{"lx":96,"ly":29,"mx":105,"my":27},{"lx":89,"ly":30,"mx":96,"my":29},{"lx":85,"ly":30,"mx":89,"my":30},{"lx":84,"ly":31,"mx":85,"my":30},{"lx":87,"ly":32,"mx":84,"my":31},{"lx":101,"ly":36,"mx":87,"my":32},{"lx":118,"ly":39,"mx":101,"my":36},{"lx":136,"ly":42,"mx":118,"my":39},{"lx":151,"ly":43,"mx":136,"my":42},{"lx":165,"ly":43,"mx":151,"my":43},{"lx":171,"ly":40,"mx":165,"my":43},{"lx":175,"ly":37,"mx":171,"my":40},{"lx":177,"ly":34,"mx":175,"my":37},{"lx":178,"ly":32,"mx":177,"my":34},{"lx":178,"ly":31,"mx":178,"my":32}]
I have seen this, but i'm not sure how best to implement it?
Controller
def show
#form = Form.find(params[:id])
respond_to do |format|
format.html
format.pdf do
pdf = FormPdf.new(#form)
send_data pdf.render, filename: "form - #{#form.title}", type: "application/pdf", disposition: "inline"
end
end
end
Prawn PDF
# encoding: utf-8
class FormPdf < Prawn::Document
def initialize(form)
super()
#form = form
all
end
def all
text "Form text here"
move_down 20
signature_data = [[#form.signature, "Signature of person"]]
table(signature_data, position: :center) do
cells.style(:border_width => 0)
end
end
Please see: https://github.com/nqngo/rails-signature-pad-prawns-demo
The signature in question image:
Luckily I did something similar at my workplace, so I will walk you through the whole thought process. Assume we store the data in #sig and setup a signature box dimension :
signature = '[{"lx":29,"ly":18,"mx":29,"my":17},{"lx":29,"ly":19,"mx":29,"my":18},{"lx":29,"ly":24,"mx":29,"my":19},{"lx":29,"ly":27,"mx":29,"my":24},{"lx":29,"ly":30,"mx":29,"my":27},{"lx":29,"ly":32,"mx":29,"my":30},{"lx":32,"ly":32,"mx":29,"my":32},{"lx":33,"ly":32,"mx":32,"my":32},{"lx":35,"ly":31,"mx":33,"my":32},{"lx":39,"ly":24,"mx":35,"my":31},{"lx":42,"ly":16,"mx":39,"my":24},{"lx":48,"ly":7,"mx":42,"my":16},{"lx":51,"ly":2,"mx":48,"my":7},{"lx":54,"ly":-3,"mx":51,"my":2},{"lx":58,"ly":2,"mx":58,"my":1},{"lx":59,"ly":9,"mx":58,"my":2},{"lx":60,"ly":18,"mx":59,"my":9},{"lx":60,"ly":27,"mx":60,"my":18},{"lx":60,"ly":38,"mx":60,"my":27},{"lx":55,"ly":45,"mx":60,"my":38},{"lx":49,"ly":51,"mx":55,"my":45},{"lx":45,"ly":54,"mx":49,"my":51},{"lx":39,"ly":57,"mx":45,"my":54},{"lx":35,"ly":51,"mx":35,"my":50},{"lx":43,"ly":45,"mx":35,"my":51},{"lx":54,"ly":39,"mx":43,"my":45},{"lx":70,"ly":32,"mx":54,"my":39},{"lx":81,"ly":28,"mx":70,"my":32},{"lx":96,"ly":25,"mx":81,"my":28},{"lx":111,"ly":23,"mx":96,"my":25},{"lx":119,"ly":23,"mx":111,"my":23},{"lx":126,"ly":23,"mx":119,"my":23},{"lx":129,"ly":23,"mx":126,"my":23},{"lx":130,"ly":23,"mx":129,"my":23},{"lx":128,"ly":24,"mx":130,"my":23},{"lx":117,"ly":25,"mx":128,"my":24},{"lx":105,"ly":27,"mx":117,"my":25},{"lx":96,"ly":29,"mx":105,"my":27},{"lx":89,"ly":30,"mx":96,"my":29},{"lx":85,"ly":30,"mx":89,"my":30},{"lx":84,"ly":31,"mx":85,"my":30},{"lx":87,"ly":32,"mx":84,"my":31},{"lx":101,"ly":36,"mx":87,"my":32},{"lx":118,"ly":39,"mx":101,"my":36},{"lx":136,"ly":42,"mx":118,"my":39},{"lx":151,"ly":43,"mx":136,"my":42},{"lx":165,"ly":43,"mx":151,"my":43},{"lx":171,"ly":40,"mx":165,"my":43},{"lx":175,"ly":37,"mx":171,"my":40},{"lx":177,"ly":34,"mx":175,"my":37},{"lx":178,"ly":32,"mx":177,"my":34},{"lx":178,"ly":31,"mx":178,"my":32}]'
#sig = JSON.parse signature
sigpad_height = 55
sigpad_width = 198
You then create a bounding_box at the cursor point and draw the line from the JSON data. The reason why we have to use a bounding_box is to set the coordinate of the line origin. Otherwise, the line function will use the bottom left of the page as the origin point:
bounding_box([0, cursor], width: sigpad_width, height: sigpad_height) do
stroke_bounds
#sig.each do |e|
stroke { line [e["lx"], e["ly"]],
[e["mx"], e["my"]] }
end
end
The resulting PDF will be:
Notice how the image is upside down, this is due to the different point of axis-direction between PDF and canvas. In PDF the origin point is bottom-left, where in canvas, the origin point is top-left. What we need to do is convert the coordinate from canvas style to PDF style. A basic transformation is to flip it over the x-axis, and translate it back by sigpad_height. The line code is now:
stroke { line [e["lx"], sigpad_height - e["ly"]],
[e["mx"], sigpad_height - e["my"]] }
The end result will be:
If you do not want the border around the bounding_box removes the stroke_bounds. A couple of gotchas you need to be careful about:
SignaturePad captures data coordinates outside the HTML signature pad dimension, hence why you see the rendered PDF signature have overdrawn lines outside its bounding_box.
The above transformation assumes the signature height of the bounding box and the HTML pad is the same. If different, you will need to add some offset to translate the signature back into the correct position due to the flipping over the x-axis.
Depends on how you store your JSON in the database. You might be able to access the coordinate as a :hash. Hence e["lx"] will yield nil, you must use e[:lx] instead.

Rails Axlsx New Line in Cell

Is there a way that I can add a new line to a cell using the Axlsx gem in Rails?
So basically replicating in Excel once you enter a value you can do a Alt + Enter to add additional text to the new line in the cell. I tried
sheet.add_row ["Testing cell row 1" + \r\n + "Testing cell row 2"]
but that throws an error.
I recently had the same problem and I found a solution that works.
I used this to setup:
p = Axlsx::Package.new
p.use_shared_strings = true
And this code adds a wrap style that makes the \r line breaks work correctly:
wrap = p.workbook.styles.add_style alignment: {wrap_text: true}
sheet.add_row "1\r2\r3", style: wrap
Now the new line in cell works, and the output is:
1
2
3
Notes:
The new line in cell doesn't work (#Gary Pinkham)
The "\x0D\x0A" didn't work (#noel)
For a forced line feed use "\x0A" (breaks between paragraphs.)
If you want both carriage return and line feed, use "\x0D\x0A".
I couldn't comment on the "doesn't work in mac excel" comment so adding this as an answer.. use package.use_shared_strings = true.. needed for Mac Excel..

multi-line footer in Prawn PDF

I have footer text that may be more than one line, the length of the text will vary depending on the user's name and the company they work for. Like all footers, it needs to be displayed below the document's bottom bound so that it doesn't get intermingled with the main content of the PDF.
The problem is that the only why I have found in Prawn to get text printed below the document's bottom bound is by using #draw_text. This is the same method that number_pages uses to get its text to appear below the document's bottom bound. However the one caveat of using #draw_text appears to be its inability to wrap text to a second line.
I have found many methods that allow me to wrap text to a second line such as #text_box, #bounding_box, etc. but the caveat of these methods is that they don't allow you to print anything below the document's bottom bound.
For example, the following will not print anything on the document because it would be below the document's bottom bound:
text_box "Generated by Tom Cruise for Universal Studios", :at => [bounds.left, 0], :width => 200
The following does print on the document because it is within the document's bottom bound but will also be printed on top of any content that already exists there:
text_box "Generated by Tom Cruise for Universal Studios", :at => [bounds.left, bounds.bottom - 20], :width => 200
And finally the following will print below the document's bottom bound ensuring that it is not being printed on top of any existing content in the PDF, but there is no available :width option or the ability to have the text wrap to a second line if needed:
draw_text "Generated by Tom Cruise for Universal Studios", :at => [bounds.left, 0]
Is there a way to get the best of both worlds? A way to print below the document's bottom bound AND enforce a maximum width with line wrapping?
I suspect you'll need to do the line wrapping manually (e.g. calculate when to break).
But I was able to get a multi-line footer using the standard number_pages method and the following:
pdf.number_pages "Copyright #{Time.now.year} Company.", [pdf.bounds.left, 0]
pdf.number_pages "Profile generated on #{Time.now.strftime('%B %d, %Y')}.", [pdf.bounds.left, 10]
Is that what you are looking for?
I ended up writing my own little routine to handle multiple lines in the footer. It'd be nice if Prawn supported something like this out of the box, I'm still a bit mystified why some things can't be displayed below the bottom bound while other things can be. It would also be nice if all the different types of text methods supported the :width attribute with line wrapping...but I digress, here is the code I ended up using:
line_wrapper = Prawn::Core::Text::LineWrap.new
repeat :all do
str = "Generated on " + Time.zone.now.strftime("%m/%d/%y at %I:%M:%S %p %Z") + " by #{user.full_name} at #{user.company.name}"
starting_position = 0
while !str.blank?
single_line = line_wrapper.wrap_line(str, :width => 470, :document => pdf)
draw_text(single_line, :at => [bounds.left, starting_position])
starting_position -= 10
str.slice!(single_line)
end
end

Truncate Markdown?

I have a Rails site, where the content is written in markdown. I wish to display a snippet of each, with a "Read more.." link.
How do I go about this? Simple truncating the raw text will not work, for example..
>> "This is an [example](http://example.com)"[0..25]
=> "This is an [example](http:"
Ideally I want to allow the author to (optionally) insert a marker to specify what to use as the "snippet", if not it would take 250 words, and append "..." - for example..
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
The marker could be thought of like an EOF marker (which can be ignored when displaying the full document)
I am using maruku for the Markdown processing (RedCloth is very biased towards Textile, BlueCloth is extremely buggy, and I wanted a native-Ruby parser which ruled out peg-markdown and RDiscount)
Alternatively (since the Markdown is translated to HTML anyway) truncating the HTML correctly would be an option - although it would be preferable to not markdown() the entire document, just to get the first few lines.
So, the options I can think of are (in order of preference)..
Add a "truncate" option to the maruku parser, which will only parse the first x words, or till the "excerpt" marker.
Write/find a parser-agnostic Markdown truncate'r
Write/find an intelligent HTML truncating function
Write/find an intelligent HTML truncating function
The following from http://mikeburnscoder.wordpress.com/2006/11/11/truncating-html-in-ruby/, with some modifications will correctly truncate HTML, and easily allow appending a string before the closing tags.
>> puts "<p><b>Something</p>".truncate_html(5, at_end = "...")
=> <p><b>Someth...</b></p>
The modified code:
require 'rexml/parsers/pullparser'
class String
def truncate_html(len = 30, at_end = nil)
p = REXML::Parsers::PullParser.new(self)
tags = []
new_len = len
results = ''
while p.has_next? && new_len > 0
p_e = p.pull
case p_e.event_type
when :start_element
tags.push p_e[0]
results << "<#{tags.last}#{attrs_to_s(p_e[1])}>"
when :end_element
results << "</#{tags.pop}>"
when :text
results << p_e[0][0..new_len]
new_len -= p_e[0].length
else
results << "<!-- #{p_e.inspect} -->"
end
end
if at_end
results << "..."
end
tags.reverse.each do |tag|
results << "</#{tag}>"
end
results
end
private
def attrs_to_s(attrs)
if attrs.empty?
''
else
' ' + attrs.to_a.map { |attr| %{#{attr[0]}="#{attr[1]}"} }.join(' ')
end
end
end
Here's a solution that works for me with Textile.
Convert it to HTML
Truncate it.
Remove any HTML tags that got cut in half with
html_string.gsub(/<[^>]*$/, "")
Then, uses Hpricot to clean it up and close unclosed tags
html_string = Hpricot( html_string ).to_s
I do this in a helper, and with caching there's no performance issue.
You could use a regular expression to find a line consisting of nothing but "^" characters:
markdown_string = <<-eos
This article is an example of something or other.
This segment will be used as the snippet on the index page.
^^^^^^^^^^^^^^^
This text will be visible once clicking the "Read more.." link
eos
preview = markdown_string[0...(markdown_string =~ /^\^+$/)]
puts preview
Rather than trying to truncate the text, why not have 2 input boxes, one for the "opening blurb" and one for the main "guts". That way your authors will know exactly what is being show when without having to rely on some sort of funkly EOF marker.
I will have to agree with the "two inputs" approach, and the content writer would need not to worry, since you can modify the background logic to mix the two inputs in one when showing the full content.
full_content = input1 + input2 // perhaps with some complementary html, for a better formatting
Not sure if it applies to this case, but adding the solution below for the sake of completeness. You can use strip_tags method if you are truncating Markdown-rendered contents:
truncate(strip_tags(markdown(article.contents)), length: 50)
Sourced from:
http://devblog.boonecommunitynetwork.com/rails-and-markdown/
A simpler option that just works:
truncate(markdown(item.description), length: 100, escape: false)

Resources