PDF è un ottimo formato per condividere documenti e i motivi sono noti: un documento PDF appare sullo schermo esattamente come apparirebbe su un foglio di carta, è compatto, può contenere link e possiamo facilmente inviarlo via email.
Il PDF è inoltre riconosciuto per l'alto grado di trasportabilità sui piú svariati dispositivi, anche architetturalmente diversi. Un PDF risulta leggibile allo stesso modo su PC, MAC e dispositivi mobili. La sola cosa necessaria é l'installazione di un reader (Adobe Reader, xPDF, eVince ed altri).
In questo articolo vedremo come creare documenti PDF con Ruby utilizzando la gemma PDF-Writer. Questa gem non richiede dipendenze esterne come html2ps
e ghostscript
. PDF-Writer
ci fornisce una serie di metodi per mostrare testi, immagini, tabelle e grafici
Installare la gem
Il modo più semplice per ottenere PDF-Writer
è usare RubyGems
che provvederà anche a risolvere le dipendenze.
gem install pdf-writer
Nota: nel caso in cui si voglia installare la libreria a partire dai sorgenti, è necessario installare altre due gemme: transaction-simple
e color-tools
.
Per utilizzare la gem possiamo includerla nel nostro sorgente con require 'rubygems'
oppure impostare la variabile d'ambiente RUBYOPT
per includere rubygems.
Esempi d'uso
È arrivato il momento di scrivere il nostro primo esempio. Creeremo un semplice file PDF stile "Hello World" impostando il tipo di font da utlizzare, la sua dimensione, la giustificazione del testo e lo salveremo in una directory da noi scelta.
Listato 1. Esempio "HelloWorld-like"
require 'rubygems' require 'pdf/writer' pdf = PDF::Writer.new pdf.select_font("Helvetica") pdf.text("Hello Ruby.HTML.it", 72, :justification => center) pdf.save_as("../output/example.pdf")
Ci sono 14 font disponibili per ogni lettore PDF. Possiamo fare riferimento alla documentazione ufficiale (ben fatta) per la lista completa.
Risulta possibile specificare il tipo di codifica da usare per il testo. Il formato PDF standard usa come codifiche "none", "WinAnsiEncoding", "MacRomanEncoding" o "MacExpertEncoding". Come default PDF-Writer usa "WinAnsiEncoding". Possiamo anche inserire una mappa di differenze tra le codifiche in questo modo:
Listato 2. Impostazione della specifica e mapping di caratteri
encoding = { :encoding => "WinAnsiEncoding", :differences => { 215 => "multiply", # mappa il byte 215 con il carattere "x" 148 => "copyright" } } pdf.select_font("Times-Roman", encoding)
Esiste un modo alternativo di creare un documento PDF in cui si specificano anche i margini sinistro, destro, alto e basso) e questo avviene tramite il metodo
Listato 3. Specificare i margini della pagina
options = { :left_margin => 50, :right_margin => 50, :top_margin => 100, :bottom_margin => 100, :bleed_size => 24, #default 12 points :mark_length => 36 #default 18 points } PDF::Writer.prepress(options) do |i| i.text( <<-EOS "Pezzi di arte locativa esposti in uno spazio elettronico che trascende i nostri sensi naturali. Una rivista dedicata alle nuove tendenze, che nessuno ha ancora mai visto ma che vorrebbe imporsi come l'equivalente europeo di 'Wired'. Lingue artificiali figlie della rivoluzione informatica. (tratto da 'Con Gibson, nel Paese delle Spie', http://www.next-station.org)" EOS ) i.save_as("prepress.pdf") end
Se vogliamo il nostro testo posizionato con coordinate specifiche possiamo usare uno dei due seguenti metodi
# assicurarsi che il testo rientri nella dimensione width
# se size non viene specificato si fa riferimento a quanto impostato con select_font
# angle è misurato in gradi ed in senso orario
# test = true il testo non sarà scritto ma verra restiruito dal metodo
PDF::Writer.add_text_wrap(x, y, width, text, size = nil, justification = :left, angle = 0, test = false)
PDF::Writer.add_text(x, y, text, size = nil, angle = 0, word_space_adjust = 0)
Vediamo ora come inserire delle immagini all'interno del nostro documento. I formati supportati dalla libreria sono JPEG e PNG, anche se quest'ultimo presenta delle limitazioni su alcune caratteristiche del formato.
Listato 4. Inserimento di immagini
pdf = PDF::Writer.new pdf.image(filename, :justification => :center, :resize => 2.20) pdf.save_as("image_file.pdf")
Passiamo ora ad analizzare il modo con cui è possibile creare tabelle con PDF-Writer. Abbiamo a disposizione la classe PDF::SimpleTable
il cui utilizzo é molto semplice, ma che prevede numerose configurazioni.
Listato 5. Creazione tabelle
require 'rubygems' require 'pdf/writer' require 'pdf/simpletable' pdf_document = PDF::Writer.new pdf_document.select_font("Helvetica") table_data = [] table_data << { "Nome" => "Mario", "Cognome" => "Rossi", "Eta" => 45 } table_data << { "Nome" => "Carlo", "Cognome" => "Bianchi", "Eta" => 54 } table = PDF::SimpleTable.new table.title = "tabella di prova" table.position = :left table.orientation = 50 table.data = table_data table.column_order = ["Nome", "Cognome", "Età"] table.render_on(pdf_document) pdf_document.save_as("table.pdf")
Esempio finale
Riuniamo quanto detto finora creando una classe wrapper di utilità per la creazione di documenti PDF.
Listato 6. Creare la classe PdfDocument
require 'rubygems' require 'pdf/writer' require 'pdf/simpletable' class PdfDocument attr_accessor :pdf def initialize(paper, orientation = :landscape) # paper imposta le dimensioni della pagina. # Possiamo specificare un formato standard(A4, A5, A3, C1...) oppure specificare le dimensioni in centimetri # o ancora il lower left-hand corner e l'upper right-hand corner [x0, y0, x1, y1] # orientation =>[:landscape, :portrait] @pdf = PDF::Writer.new(:paper => paper, :orientation => orientation) # impostiamo i margini con un unico metodo # oppure chiamiamo i metodi top_margin, bottom_margin... @pdf.margins_pt(10,10, 15, 15) end def set_font(font, encoding = nil) @pdf.select_font(font, encoding) end def set_text(text, fontsize = 12, justification = :center) @pdf.text(text, :font_size=> fontsize, :justification => justification) end def new_page(force = true) @pdf.start_new_page(force) end def new_page_numbering @pdf.start_page_numbering(1100, 20, 10, :center) end def insert_image(filename, just, scale) pdf.image(filename, :justification => just, :resize => scale) end def insert_table(mytable) table = PDF::SimpleTable.new table.title_font_size = 22 table.title_gap = 15 # distanza tra il titolo e la tabella table.heading_font_size = 18 table.bold_headings = true table.font_size = 16 table.row_gap = 4 table.shade_color = Color::RGB::Grey90 # oppure table.shade_rows = :shaded table.show_lines = :all table.show_headings = true table.position = :center table.orientation = :center table.data = mytable[:table_data] table.column_order = mytable[:table_column] table.title = mytable[:table_title] table.render_on(@pdf ) end def save_as(filename) @pdf.save_as(filename) puts "=== scrittura di #{filename} completata ===" end end mytable = { :table_column => ["Categoria", "Titolo", "Autore", "Data"], :table_title => "Lista degli Articoli", :table_data => [ {"Categoria" => "Programmazione Ruby", "Titolo" => "Creare file XML con Ruby", "Autore" => "Simone Carletti", "Data" => "08.07..2008" }, {"Categoria" => "Programmazione Ruby", "Titolo" => "Espressioni regolari con Ruby e Oniguruma", "Autore" => "Sandro Paganotti", "Data" => "01.07.2008" }, {"Categoria" => "Rails", "Titolo" => "Capistrano: il deployment Rails diventa facile", "Autore" => "Calogero Lo Leggio", "Data" => "27.05.2008" }, {"Categoria" => "Rails", "Titolo" => "Non solo Rails: costruire un blog con Merb", "Autore" => "Carlo Pecchia", "Data" => "10.06.2008" }, {"Categoria" => "Rails", "Titolo" => "ActiveRecord - un ORM per Ruby", "Autore" => "Mirco Veltri", "Data" => "13.05.2008" } ] } mydoc = PdfDocument.new("A4") mydoc.new_page_numbering mydoc.set_font("Helvetica") mydoc.insert_image("../images/logo.png",:center, 2.20) mydoc.set_text("Hello Ruby.HTML.it", 72) mydoc.new_page(true) mydoc.set_text(":pdf => 'Testing new page method' ", 36) mydoc.new_page(true) mydoc.insert_table(mytable) mydoc.save_as("../output/example.pdf")