Ruby wrapper for generating a single pdf for multiple contexts from an odt template.
A template odt is just an ordinary odt (en.wikipedia.org/wiki/OpenDocument). In order for it to function as a template, we just need to decorate it (just use openoffice, pls don’t buy ms office for that) with placeholders & embedded ruby code.
Under the hood, clamsy is using a slightly hacked version of the awesome tenjin template engine (www.kuwata-lab.com/tenjin) to do the rendering. The following is all u need to know to write odt templates:
-
‘{? … ?}’ represents embedded Ruby statement (this differs from vanilla tenjin)
-
‘${ … }’ represents embedded Ruby expression which is to be escaped (eg. ‘& < > “’ are escaped to ‘& < > "’)
-
‘#{ … }’ represents embedded Ruby expression (u should really avoid using this)
Template Odt:
-------------------------------------------- ${@company_full_name} ${@company_address_line_1} ${@company_address_line_2} S/N Item Description Amount (SGD) {? @items.each_with_index do |item, i| ?} ${i+1} ${item.description} ${amount} {? end ?} Total ${total} ----------------------------------- Page 1 -
Code:
class Item attr_reader :description, :amount def initialize(description, amount) @description, @amount = description, amount end end items = [ Item.new('Shells (x2 Bags)', 10), Item.new('Clams (x2 Bags)', 20) ] context = { :company_full_name => 'Monster Inc', :company_address_line_1 => 'Planet Mars, Street xyz,', :company_address_line_2 => 'Postal Code 009900', :items => items, :total => items.inject(0) {|sum, item| sum + item.amount } } Clamsy.process( context, path_to_template_odt, # eg. '/tmp/invoice.odt' path_to_final_pdf, # eg. '/tmp/invoice.pdf' )
Generated Pdf:
-------------------------------------------- Monster Inc Planet Mars, Street xyz, Postal Code 009900 S/N Item Description Amount (SGD) 1 Shells (x2 Bags) 10 2 Clams (x2 Bags) 20 Total 30 ----------------------------------- Page 1 -
Taking the above example one step further, if we have multiple contexts:
items << Item.new('Shrimps (x2 Bags)', 15) items << Item.new('Prawns (x2 Bags)', 25) contexts = [{ :company_full_name => 'Monster Inc', :company_address_line_1 => 'Planet Mars, Street xyz,', :company_address_line_2 => 'Postal Code 009900', :items => items[0..1], :total => items[0..1].inject(0) {|sum, item| sum + item.amount } }, { :company_full_name => 'Fisherman Inc', :company_address_line_1 => 'Planet Jupiter, Street xyz,', :company_address_line_2 => 'Postal Code 008800', :items => items[2..3], :total => items[2..3].inject(0) {|sum, item| sum + item.amount } }] Clamsy.process( contexts, path_to_template_odt, # eg. '/tmp/invoice.odt' path_to_final_pdf # eg. '/tmp/invoice.pdf' )
Generated Pdf:
-------------------------------------------- Monster Inc Planet Mars, Street xyz, Postal Code 009900 S/N Item Description Amount (SGD) 1 Shells (x2 Bags) 10 2 Clams (x2 Bags) 20 Total 30 ----------------------------------- Page 1 - -------------------------------------------- Fisherman Inc Planet Jupiter, Street xyz, Postal Code 008800 S/N Item Description Amount (SGD) 1 Shrimps (x2 Bags) 15 2 Prawns (x2 Bags) 25 Total 40 ----------------------------------- Page 1 -
As you can see, (whether you like it or not) the pages are merely concatenated together to form a single pdf !!
It is possible to replace images in an odt file, a useful feature if u need to insert digital signature into the final pdf. To acheive this, u have to:
-
insert a placeholder image into the odt file,
-
resize & move it to your heart content
-
right click on the picture, under [Picture …]/[Options], assign a unique value for [Name]
Template Odt:
-------------------------------------------- ~~~~~~~~~~~~~~~~~~ Hey | nice_guy_image | ~~~~~~~~~~~~~~~~~~ This is a gentle reminder that i still owe you some money ... ~~~~~~~~~~~~~~~~~~~~~~ | my_signature_image | ~~~~~~~~~~~~~~~~~~~~~~ ----------------------------------- Page 1 -
Assuming ‘nice_guy_image’ & ‘my_signature_image’ as names of the images, the following code does the appropriate replacing of images:
Clamsy.process( context = {:_pictures => { :nice_guy_image => path_to_avatar_image, # eg. '/tmp/handsome.png' :my_signature_image => path_to_signature_image # eg. '/tmp/signature.png' }}, path_to_template_odt # eg. '/tmp/confession.odt' path_to_final_pdf # eg. '/tmp/confession.pdf' )
(see wiki.github.com/ngty/clamsy/configuration)
-
Make sure openoffice, java & ghostscript are installed
-
address security risk of user running destructive commands within embedded ruby in odt template
-
support for other platforms (eg. windows & jruby)
-
turn off non-santized ruby expression by default (since the generated doc easily breaks when underlying xml is invalid)
-
automate cups-pdf printer setup
-
Fork the project.
-
Make your feature addition or bug fix.
-
Add tests for it. This is important so I don’t break it in a future version unintentionally.
-
Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
-
Send me a pull request. Bonus points for topic branches.
Written 2010 by:
-
NgTzeYang, contact github.com/ngty
-
JasonOng, contact github.com/jasonong
-
ZhenyiTan, contact github.com/zhenyi
Released under the MIT license