= clamsy Ruby wrapper for generating a single pdf for multiple contexts from an odt template. == 1. Basics A template odt is just an ordinary odt (http://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 (http://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) == 2. Examples === 2.1. Text Replacement Example 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 - === 2.2: Multiple Contexts Example 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 !! === 2.3: Image Replacement Example 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' ) == 3. Configuration (see http://wiki.github.com/ngty/clamsy/configuration) == 4. Pre-requisites * Make sure openoffice, java & ghostscript are installed == TODO * 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 == Note on Patches/Pull Requests * 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. == Contacts Written 2010 by: 1. NgTzeYang, contact http://github.com/ngty 2. JasonOng, contact http://github.com/jasonong 3. ZhenyiTan, contact http://github.com/zhenyi Released under the MIT license