Premessa

Molti programmatori prima o poi nella loro carriera lavorativa si trovano davanti alla scelta di come creare i file PDF. Il formato PDF oltre ad essere diffusissimo porta con sé delle caratteristiche che lo rendono il miglior formato di scambio documenti attualmente in circolazione. Infatti è ottimo per la stampa potendo effettuare conversioni in PDF ad altissima qualità, ottimo per il trasporto di documenti tra sistemi operativi diversi perché un PDF si vede sempre nello stesso modo (usando ad esempio il viewer gratuito di Adobe), perfetto per la spedizione di documenti, come fatture o contratti, che non devono essere modificati avendo al suo interno un sistema di protezione abbastanza potente da garantire una buona sicurezza.

Per tutti questi ed altri motivi nessun programmatore che crea ad esempio un sistema di fatturazione online può sorvolare l'aspetto della creazione di documenti PDF.

Problemi

La creazione di un file PDF, nel mio caso, ha comportato parecchio lavoro nella fase iniziale, che poi però si è notevolmente semplificato nella fase finale che andrò a spiegarvi tra breve. Inizialmente infatti mi sono orientato nella ricerca di un componente utilizzabile da .NET anche a pagamento ma con il risultato di trovarne uno fantastico ma dai prezzi proibitivi (ActivePDF) ed un altro buono e dai prezzi competitivi (DynamicPDF). Questa soluzione però comportava come sempre l'obbligo di mantenere un legame con quel componente per ripagarsi dei soldi spesi.

Soluzione

Navigando qua e là per internet ho alla fine trovato proprio ciò che cercavo. Un componente open-source, potente, testato (e quindi molto affidabile) e completamente gratuito. Questo non solo permette un notevole risparmio per la mia azienda (alla fine abbiamo dato solo un contributo volontario al programmatore per l'ottimo lavoro svolto) ma anche l'indipendenza dal fornitore del componente perché, in quanto gratuito, non c'è nessun costo da recuperare e quindi è possibile passare ad un altro componente in qualsiasi momento (sperando che questo, ovviamente, non debba accadere mai). Il componente in questione è un porting per .NET della più famosa libreria Java per la creazione di PDF: iText.

In realtà esistono 2 porting di questa libreria:

ma mentre la prima libreria è realizzata in J# per velocizzare la conversione da Java in .NET, la seconda è completamente realizzata in C# e per forza di cose effettua il porting di una versione più vecchia di iText.

Nella prima stesura del codice sono stato tentato proprio dalla libreria iTextSharp (quella completamente riscritta in C#) ma a causa di alcuni bug sono stato costretto ad abbandonarla presto.

Sono così passato ad utilizzare iTextDotNet che, a parte qualche problema iniziale di installazione, svolge egregiamente il proprio lavoro.

Alcune note

I siti web di riferimento delle due librerie, se li avete visitati, vi saranno sembrati troppo scarni e assenti di documentazione. In effetti è la verità, ed è il motivo per cui in un primo tempo non avevo considerato queste due potenti soluzioni. In realtà il motivo è semplice. Come accennato sopra, le due librerie sono un porting esatto della libreria iText per Java, è quindi possibile trovare al sito ufficiale di iText tutta la documentazione necessaria.

Installazione della libreria

Come accennato prima, ho personalmente scelto l'iTextDotNet per la maggiore affidabilità che ha dimostrato durante le mie prove. Questa libreria è però scritta completamente o quasi in J# ed è quindi necessario per il suo corretto funzionamento che sul server sia installato Microsoft Visual J# .NET Redistributable Package che potete trovare cliccando qui.

iTextSharp essendo riscritta completamente in C# non risente di questo "problema", ma purtroppo non consente la corretta creazione di file PDF molto lunghi e sembra che lo sviluppo si sia fermato da parecchio tempo, quindi ribadisco la mia scelta per iTextDotNet. Le differenze nell'utilizzo tra le due librerie sono comunque irrisorie e tranne pochissime modifiche il codice scritto per una funzionerà anche con l'altra (l'unica cosa che sembra cambiare è la lettera iniziale dei metodi che è maiuscola - stile Microsoft - in iTextSharp e minuscola - stile Java - in iTextDotNet).

Successivamente è necessario copiare nella directory bin sia la Dll contenuta nel file compresso che scaricherete (iTextdotNET-dll-???-?.zip) sia la directory com che contiene alcuni file utili a iTextDotNet.

Un primo esempio

Prendo uno degli esempi semplici sul sito di iTextDotNet per dimostrare la facilità di utilizzo di questa libreria, più avanti vedremo come utilizzare le funzioni più avanzate della libreria.

  1:  /*
   2:   * This code is free software. It may only be copied or modified
   3:   * if you include the following copyright notice:
   4:   *
   5:   * --> Copyright 2001 by Bruno Lowagie <--
   6:   * --> Copyright 2004 by Kazuya Ujihara <--
   7:   *
   8:   * This code is example code of 'iText .NET'.
   9:   * See http://www.ujihara.jp/iTextdotNET/examples.html
  10:   *
  11:   * This code is distributed in the hope that it will be useful,
  12:   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13:   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14:   */
  15:   
  16:  using System; 
  17:  using com.lowagie.text;
  18:  using com.lowagie.text.pdf;
  19:  using System.IO;
  20:   
  21:  public class Chap0101 
  22:  {
  23:      public static void Main(string[] args) 
  24:      {
  25:          Console.WriteLine("Chapter 1 example 1: Hello World");
  26:          
  27:          // step 1: creation of a document-object
  28:          Document document = new Document();
  29:          
  30:          // step 2:
  31:          // we create a writer that listens to the document
  32:          // and directs a PDF-stream to a file
  33:          PdfWriter.getInstance(document, new FileStream("Chap0101.pdf", FileMode.Create));
  34:              
  35:          // step 3: we open the document
  36:          document.open();
  37:   
  38:          // step 4: we add a paragraph to the document
  39:          document.add(new Paragraph("Hello World"));
  40:   
  41:          // step 5: we close the document
  42:          document.close();
  43:      }
  44:  }
  45:   

Questo primo esempio, molto semplice, crea un file PDF che contiene solo una pagina con la scritta "Hello World" (fantasioso vero?)

Alla riga 17 e 18 troviamo le classiche dichiarazioni che ci consentono di scrivere codice più snello, senza dover richiamare ogni volta il namespace per intero.

Alla riga 28 viene creato un nuovo documento di tipo Document. Come vedremo più avanti alcuni metodi hanno il prefisso "Pdf" ed altri no. Questo succede perchè iTextDotNet, come iText dopo tutto, è una libreria che permette di fare molto più che creare Pdf. E' infatti possibile utilizzarla per la creazione di documenti RTF, XML ed XHTML.

Alla riga 33 viene creato uno stream che va a scrivere su disco il nostro file PDF. La possibilità di creare uno stream invece di scrivere solo un file su filesystem permette di avere maggiore libertà. Ad esempio si potranno creare file PDF in memoria per poi restituirli al client in una pagina ASP.NET senza utilizzare file temporanei dove salvare la nostra creazione.

Le restanti righe anche se autoesplicative le descriverò brevemente.

Il file viene virtualmente aperto alla riga 36; successivamente viene inserito all'interno del file un oggetto di tipo Paragraph (un paragrafo ovviamente) contenente una stringa ed infine il documento viene chiuso. In realtà solo nel momento della chiusura il file viene effettivamente scritto nello stream (e quindi in questo caso su disco) permettendo a questa libreria di essere una tra le più veloci che io abbia provato.

Un esempio più complesso

Nel primo esempio è stato molto semplice creare un file PDF che conteneva un semplice testo. Immaginiamo però di voler aggiungere qualcosa di più. Leggiamo il codice che andremo successivamente a spiegare.

   1:  using System; 
   2:  using com.lowagie.text;
   3:  using com.lowagie.text.pdf;
   4:  using System.IO;
   5:  using System.Drawing;
   6:  using itext=com.lowagie.text;
   7:   
   8:  public class Test2 
   9:  {
  10:      public static void Main(string[] args) 
  11:      {       
  12:          // definizione della grandezza della pagina
  13:          itext.Rectangle pageSize = new itext.Rectangle(250, 720);
  14:          // definizione del colore di fondo
  15:          pageSize.setBackgroundColor(Color.LightYellow);
  16:   
  17:          Document document = new Document(pageSize);
  18:          
  19:          // aggiunta di alcuni metatag
  20:          document.addCreator("Il mio software ASP.NET");
  21:          document.addAuthor("Alessandro Benedetti");
  22:          
  23:          FileStream FilePDF = new FileStream("d:\\temp\\Test2.pdf", FileMode.Create);
  24:              
  25:          PdfWriter writer1 = PdfWriter.getInstance(document, FilePDF);
  26:          
  27:          document.open();
  28:   
  29:          PdfContentByte cb1 = writer1.getDirectContent();
  30:            
  31:          // aggiunta di un link
  32:          Paragraph paragraph = new Paragraph("Visita il sito di ");
  33:          Anchor anchor1 = new Anchor("iTextdotNET", 
  34:                                      FontFactory.getFont(FontFactory.HELVETICA, 12, itext.Font.UNDERLINE, Color.Blue));
  35:          anchor1.setReference("http://www.ujihara.jp/iTextdotNET/");
  36:          anchor1.setName("top");
  37:          paragraph.add(anchor1);
  38:          document.add(paragraph);
  39:   
  40:          // aggiunta di una immagine
  41:          itext.Image jpeg = itext.Image.getInstance("d:\\temp\\test.jpg");
  42:          jpeg.setAbsolutePosition(10, 250);
  43:          jpeg.setAlignment(itext.Image.MIDDLE);
  44:          document.add(jpeg);
  45:   
  46:          // aggiunta di un testo che utilizza il carattere TrueType OCRB
  47:          BaseFont FontOCRB = BaseFont.createFont("d:\\temp\\ocrb.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);    
  48:          String text1 = "Testo scritto con il font True Type 'OCRB'.";
  49:          cb1.beginText();
  50:              cb1.setFontAndSize(FontOCRB, 7);
  51:              cb1.showTextAligned(PdfContentByte.ALIGN_RIGHT, text1, 240, 20, 0);
  52:          cb1.endText();
  53:   
  54:          document.close();
  55:      }
  56:  }

Andiamo con ordine nella spiegazione:

  • Linea 13: Viene definita la grandezza della pagina con le dimensioni specificate (x, y). Molte dimensioni di pagine standard (A4, Letter, A3, ecc...) sono già definite all'interno della libreria iTextdotNET (ad esempio PageSize.A4).
  • Linea 15: Viene settato un colore per il fondo pagina. Se questo comando non viene invocato il fondo della pagina sarà ovviamente bianco come visto nell'esempio precedente.
  • Linea 17: Viene creato un documento con le dimensioni specificate precedentemente.
  • Linee 20 e 21: Vengono aggiunti alcuni metatag che definiscono il titolo del documento ed il creatore.
  • Linee da 33 a 37: Viene creato un link nel quale sono settati parametri quali il font, il colore e lo stile. Il font non sarà incluso all'interno del PDF ma trattandosi di un font standard si presume che sia presente su tutti i PC. Non includere il font porta il vantaggio di creare file PDF con dimensioni inferiori.
  • Linee da 41 a 44: Viene aggiunta una immagine, settata la sua posizione e definito l'allineamento.
  • Linea 47: Viene creata un font caricato da un file specifico. Inoltre il font viene integrato all'interno del file PDF così da essere visibile da qualsiasi computer.
  • Linee da 49 a 52: Con il font appena creata viene scritto un testo in una posizione specifica. E' importante notare che, quando si usa questo metodo per la scrittura, bisogna sempre specificare il tipo di font e la grandezza del carattere prima della scrittura per evitare un fastidioso errore a run-time.

Come si può vedere la creazione di un file PDF contenente immagini e testi in diverse posizioni è molto semplice. Si tratta unicamente di definire alcune semplici operazioni.

Conclusioni

Nella prossima puntata vedremo come utilizzare un documento PDF come base per l'aggiunta di informazioni, molto utile nella compilazione ad esempio di fatture ed inoltre vedremo come unire più documenti PDF in un unico documento.