Premessa
Lo scopo del presente articolo è di affrontare le problemiche connesse alla generazione di documenti in formo PDF con MS Visual Basic, senza l'ausilio di nessun controllo OCX, libreria DLL o software di terze parti.
Nella prima parte abbiamo affronto una breve analisi di un documento PDF, evidenziandone la struttura e gli elementi costitutivi.
In questa seconda parte vedremo come queste conoscenze possono essere utilizze per sviluppare una serie di funzioni, ed in particolare implementeremo una classe di esempio, di libero utilizzo ed implementazione.
Nelle righe che seguono, non saranno affronti alcuni dettagli dell'implementazione, lasciando al pubblico "avanzo" la possibilità di autoreferenziare l'esempio proposto.
L'idea di base: la struttura tipo del documento genero
Come abbiamo visto, gli elementi principali di un documento PDF sono gli oggetti. Abbiamo la necessità quindi di costruire un meccanismo che ci permetta di gestire la scrittura dell'oggetto e contestualmente la gestione della sezione CROSS-REFERENCE TABLE, in cui, come già detto, va scritto un riferimento all'oggetto creo. Non bisogna dimenticarsi inoltre che ci sono alcuni oggetti che in un ordine logico andrebbero prima di altri, per esempio la radice delle pagine /Pages, anche se contengono riferimenti, alle pagine, che sono noti solo dopo che tutti gli oggetti sono sti costruiti. Per risolvere i problemi evidenzii, si è penso di utilizzare una struttura ed una numerazione degli oggetti tale che i riferimenti fossero conosciuti a priori. Così facendo, il documento viene costruito in modo tale che l'oggetto /Pages, ed in modo simile gli altri, ha sempre lo stesso numero di riferimento, anche se viene fisicamente costruito dopo gli altri. In altre parole, la struttura tipo del documento genero è la seguente
1 0 obj Info
2 0 obj Calog
3 0 obj Encoding
6 0 obj (disponibile)
7 0 obj (disponibile)
. .. .. ..
.. .. .. .. ..
n-1 0 obj (disponibile)
n 0 obj (disponibile)
4 0 obj Pages
5 0 obj Resource
in questo modo il riferimento agli oggetti intermedi, ad esempio il /Parent presente in ogni oggetto /Page, è sempre noto, mentre gli altri possono essere costruiti a mano a mano che gli oggetti vengono crei e inseriti alla fine nella definizione degli oggetti 4 e 5.
L'oggetto Resources
Nella prima parte abbiamo accenno che ogni oggetto /Page contiene un riferimento ad un oggetto /Resources, un dictonary che descrive sostanzialmente il contenuto e le risorse utilizze nel documento, ovvero l'elenco dei Font utilizzi e gli oggetti Form e descrive il contenuto del documento. Ad esempio
4 0 obj
<<
/Type /Pages
/Resources 5 0 R
.. ..
>>
endobj
5 0 obj
<<
/Font <>
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/XObject <>
>>
endobj
l'oggetto 5 0 descrive il fto che il documento utilizza due font (Fnt1 e Fnt2) rappresenti rispettivamente dagli oggetti 6 0 e 7 0, un oggetto immagine Img1 (oggetto 8 0) ed un oggetto form (oggetto 9 0). Inoltre indica che all'interno del documento ci sono degli operori standard (/PDF), degli oggetti di tipo testo (/Text), delle immagini in scala di grigio (/ImageB), delle immagini a colori RGB (/ImageC) e delle immagini a tavolozza indicizza (/ImageI).
Le unità di misura
Oltre all'unità di misura dello standard PDF (72 punti per pollice), la classe permette di utilizzarne altre tre, centimetri, millimetri e pollici, gestendo le conversioni in modo trasparente. Per quanto riguarda le cifre digitali, la classe per default, ne utilizza tre sul valore assoluto in unità di misura standard, e non dovrebbe essere necessario andare.
Font standard e non
Nella prima parte dell'articolo, parlando dei font, abbiamo accenno al fto che lo standard ne prevede 14 di tipo TYPE1, che non necessitano di particolari informazioni. Oltre a questi, la classe ne implementa alcuni in formo TrueType (Courier New, Times, Arial), con i quali è possibile gestire e calcolare "l'ingombro" e quindi l'allineamento.
Immagini bitmap
La classe permette di gestire e riprodurre immagini BMP con 1, 4, 8 e 24 bitcolore. La routine che legge le bitmap dal disco non è ottimizza al massimo, quindi tenzione a non caricare immagini troppo grandi.
Le form
E' possibile fare riferimento ad un form non ancora definito (vedere la demo, con riferimento al form che scrive il numero totali di pagine). Occorre ricordare però, che le form, così come le immagini e i font, vanno definite all'esterno delle pagine (cioè non devono trovarsi tra .StartPage e .EndPage).
Grafica vettoriale
Prima di utilizzare gli operori tipo linea, arco o altro, ricordarsi di settare sempre il punto di inizio del traccio con l'istruzione MoveTo. Nel caso di traccii composti da più trti elementari, si consiglia di utilizzare solo sull'ultimo trto le opzioni di disegno, chiusura o riempimento. Inoltre, provare a utilizzare il parametro Qualità nella costruzione di figure quali archi o ellissi, per adtare il grado di approssimazione desidero alla curva.
Uso della classe
Fte le dovute premesse, veniamo ad un utilizzo semplice della classe clsPDFCreor. L'esempio che segue, genera un documento PDF con il classico "Hello World", in Courier New, 48 punti.
Dim clPDF As New clsPDFCreor
With clPDF
' Titolo
.Title = "Demo"
' Unità di misura
.ScaleMode = pdfCentimeter
' Formo pagina
.PaperSize = pdfA4
' inizializza il file
.InitPDFFile App.Ph & "\prova.pdf"
' Definisce il font da utilizzare
.LoadFontStandard "Fnt1", "Courier New"
' Inizializza la pagina
.StartPage
' Font 48 punti
.DrawText 1, 27, "Hello World", "Fnt1", 48
' Chiude la pagina
.EndPage
' Chiude il documento
.ClosePDFFile
End With
Il progetto dimostrivo che include la classe, comprende invece un esempio sufficientemente esaustivo delle capacità della classe stessa.
I metodi, funzioni e variabili pubbliche della classe
Le variabili
Title as String
Imposta il titolo del documento
ScaleMode as PdfScaleMode
Imposta l'unità di misura del documento. Per default, è 72 punti per pollice
PaperSize as PdfPaperSize
Imposta il formo pagina. Per default è A4 (21x29.7 cm)
PaperWidth as Single
Imposta la larghezza della pagina per il formo utente
PaperHeight as Single
Imposta l'altezza della pagina per il formo utente
Margin as Single
Imposta il margine di stampa. Per default è 0
Pages as Integer
Restituisce la pagina corrente
Oriention as PdfPrintOriention
Imposta l'orientamento della pagina. Per default è verticale
I metodi e le funzioni pubbliche
InitPDFFile(Optional FileName As String) As String
Inizializza il file PDF. Se non viene passo il nome , genera un file casuale nella cartella dei file temporanei e ne restituisce il nome completo
ClosePDFFile()
Chiude il documento aperto
StartPage() As Integer
Inizia una nuova pagina, restituendone il numero
EndPage()
Termina la pagina corrente
SetWordSpacing(W As Single)
Imposta la spaziura fra parole, in punti
SetCharSpacing(W As Single)
Imposta la spaziura fra carteri, in punti
SetTextHorizontalScaling(W As Single)
Imposta il ftore di compressione orizzontale dei carteri, in percentuale
SetTextRenderingMode(W As Single)
Imposta la modalità di visualizzazione del testo
(0=senza contorno, 1 = solo contorno, 2 = contorno e riempimento)
SetColorStroke(rgb As Long)
Imposta il colore rgb per le linee ed i contorni. Se l'argomento è negivo ed in valore assoluto minore di 256, imposta il colore sui toni di grigio
SetColorFill(rgb As Long)
Imposta il colore rgb per le aree. Se l'argomento è negivo ed in valore assoluto minore di 256, imposta il colore sui toni di grigio
SetDash(dash_on As Single, Optional dash_off As Single)
Imposta le carteristiche delle linee trteggie. SetDash 0 reimposta la linea intera
SetLineWidth(W As Single)
Imposta lo spessore della linea
SetLineCap(W As Integer)
Imposta il modo in cui deve terminare una linea
(0=normale, 1=semicerchio, 2=squadra)
SetLineJoin(W As Integer)
Imposta il modo in cui deveno congiungersi due linee
(0=angolo, 1=semicerchio, 2=taglio)
SetMiterLimit(W As Single)
Imposta la dimensione massima dell'intersezione di due linee
(W >= 1)
MoveTo(x As Single, y As Single)
Inizia un nuovo percorso, impostando il punto corrente
LineTo(x As Single, y As Single, Optional Options As pdfPhOptions = Stroked)
Aggiunge al percorso, una linea dal punto corrente alle coordine passe.
Rectangle(x As Single, y As Single, _
xdim As Single, ydim As Single, _
Optional Options As pdfPhOptions = Stroked, _
Optional Ray As Single)
Aggiunge al percorso un rettangolo, eventualmente arrotondo.
Curve(X1 As Single, Y1 As Single, _
X2 As Single, Y2 As Single, _
x3 As Single, y3 As Single, _
Optional Options As pdfPhOptions = Stroked)
Aggiunge al percorso una curva di Bezier
DrawCircle(x As Single, y As Single, Ray As Single, _
Optional Options As pdfPhOptions = Stroked)
Aggiunge al percorso un cerchio
Arc(x As Single, y As Single, Ray As Single, _
Optional StartAngle As Single = 0, _
Optional EndAngle As Single = 360, _
Optional Rio As Single = 1, _
Optional Pie As Boolean = False, _
Optional Rote As Single = 0, _
Optional Quality As Integer = 1, _
Optional Options As pdfPhOptions = Stroked)
Aggiunge al percorso un arco, ellisse o fetta di torta
LoadImgFromBMPFile(Name As String, FileName As String, _
Optional ColorSpace As _
pdfColorSpace = pdfRGB) As Boolean
Carica una Bitmap da un file, assegnandole un nome di risorsa
LeggeBMP(FileName As String, ByRef ImgBuf() As Byte, _
ByRef ImgColor As String) As Boolean
Carica un vettore di e colore con una Bitmap da file
LoadImgFromBuffer(Name As String, ByRef ImgBuf() As Byte, _
ByRef ImgColor As String, _
Optional ColorSpace As pdfColorSpace = pdfRGB)
Carica una Bitmap da un vettore di e colori, assegnandole un nome di risorsa
DrawImg(Name As String, DestX As Single, DestY As Single, _
ImgWidth As Single, ImgHeight As Single)
Disegna una Bitmap, prelevandola dell'elenco delle risorse
LoadFontStandard(Name As String, BaseFont As String, _
Optional Options As pdfFontStyle = pdfNormal)
Carica una font standard, assegnandogli un nome di risorsa
LoadFont(Name As String, BaseFont As String, _
Optional Options As pdfFontStyle = pdfNormal)
Carica una font TrueType, assegnandogli un nome di risorsa
DrawText(x As Single, y As Single, Text As String, _
FontName As String, Fontsize As Single, _
Optional Align As pdfTextAlign = pdfAlignLeft, _
Optional Rote As Single = 0)
Scrive il testo alle coordine passe, eventualmente ruoto
StartObject(Name As String, Optional Options As pdfObjectType = pdfNull)
Inizia un nuovo oggetto Form
EndObject()
Termina un oggetto Form
DrawObject(Name As String)
Traccia un oggetto Form
Length(Text As String, FontName As String, Fontsize As Single) As Single
Restituisce la lunghezza del testo nel font indico
Conclusioni
La demo a corredo della classe contiene un esempio abbastanza ampio delle capacità e delle funzioni disponibili, e così com'è la classe permette già di essere utilizza senza bisogno di conoscere altro. Certo potrebbe essere interessante implementare altre funzioni (ad esempio la compressione delle immagini, o la gestione delle password di protezione, ecc.), ma per questo aspetto i vostri suggerimenti.
Da ultimo, la classe è da ritenersi freeware per ogni utilizzo non commerciale.