Proviamo a scrivere un piccolo programma che consente di stampare un documento composto da immagini e testo in modo che le figure e le parole non si sovrappomgano le une alle altre.
Prima però scriviamoci una classe che ci consente di gestire le immagini contenute nel documento:
Public Class ImmagineX
Public Picture As Image
Public X As Integer '0=left; 1=center; 2=right;
Public Y As Integer
Public Width As Integer
Public Height As Integer
Public NPagina As Integer
Public Sub New(ByVal Picture1 As Image, ByVal X1 As Integer, ByVal Y1 As Integer, ByVal Width1 As Integer, _
ByVal Height1 As Integer, ByVal NPagina1 As Integer)
Picture = Picture1
X = X1
Y = Y1
Width = Width1
Height = Height1
NPagina = NPagina1
End Sub
End Class
E adesso inseriamo il codice che gestisce la stampa del documento.
Imports System.Drawing.Printing Public Class Form1 'creazione di un nuovo button Private WithEvents Button1 As New Button 'creazione di un nuovo PrintDocument Private WithEvents PrintDocument1 As New PrintDocument 'creazione della finestra di dialogo "Anteprima" Private PrintPreviewDialog1 As New PrintPreviewDialog 'creazione di un nuovo font Private NuovoFont As Font 'definizione dell'altezza del font Dim HFont As Single 'definizione della varaibile stringa che contiene il testo da stampare Dim Testo As String 'formattzaione del testo da stampare Dim CustomFormat As New StringFormat 'collection che raccoglie il documento da stampare suddiviso in paragrafi Dim ListaParagrafi As Collection 'matrice che raccoglie le immagini da stampare Dim Immagine(0 To 2) As ImmagineX 'contatore del numero di paragrafi in cui è suddiviso il testo Dim conteggio As Integer 'definizione del numero di pagina Dim NumeroPagina As Integer Public Sub New() InitializeComponent() 'impostazione della form Me.Size = New Size(210, 180) Me.Text = "Stampa Testo" 'impostazione del button Button1.Size = New Size(100, 30) Button1.Location = New Point(51, 58) Button1.Text = "Anteprima" 'aggiunta dei controlli alla form Me.Controls.Add(Button1) '***************************************************** 0=left; 1=center; 2=right; 'dichiarazione delle immagini che sono inserite nel testo Immagine(0) = New ImmagineX(Image.FromFile("D:\Immagini\Paesaggio_3.jpg"), 0, 100, 240, 180, 1) Immagine(1) = New ImmagineX(Image.FromFile("D:\Immagini\Paesaggio_7.jpg"), 1, 150, 400, 300, 2) Immagine(2) = New ImmagineX(Image.FromFile("D:\Immagini\Paesaggio_9.jpg"), 2, 720, 200, 150, 2) End Sub Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click 'associazione del documento da stampare alla finestra di dialogo "Anteprima di Stampa".... PrintPreviewDialog1.Document = PrintDocument1 '...per essere visualizzato PrintPreviewDialog1.Show() End Sub Private Sub PrintDocument1_BeginPrint1(ByVal sender As Object, ByVal e As _ System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.BeginPrint 'creazione dei un'istanza del tipo collection ListaParagrafi = New Collection 'azzeramento del numero di pagina NumeroPagina = 0 'definizione del testo da caricare da un file Testo = My.Computer.FileSystem.ReadAllText("d:\documenti\Prova.txt") 'definizione della variabile stringa "Paragrafo" che conterrà un paragrafo del nostro documento da stampare Dim paragrafo As String = "" 'si scorre il nostro documento, carattere per carattere, per poterlo suddividere in paragrafi... 'un paragrago viene identificato quando incontriamo un' "ANDATA A CAPO" (Line Feed) For Each carattere As Char In Testo 'se il carattere che stiamo processando è un'andata a capo... If carattere = vbLf Then 'aggiungi alla collection ListaParagrafi il testo contenuto nella stringa Paragrafo ListaParagrafi.Add(paragrafo) 'resetta il contenuto della variabile Paragrafo perché possa essere usata per contenerne un altro paragrafo = "" Else 'aggiungi il carattere al paragrafo paragrafo += carattere End If Next 'aggiorna il contatore dei paragrafi conteggio = ListaParagrafi.Count 'si definisce un nuovo font con cui scrivere il testo NuovoFont = New Font("Arial", 15, FontStyle.Regular, GraphicsUnit.Pixel) 'definizione dell'altezza del testo per recuperare l'interlinea HFont = NuovoFont.Height 'si imposta la formattazione del testo... 'in questo caso un allineamento a sinistra... CustomFormat.Alignment = StringAlignment.Near 'imponendo di visualizzare linee intere senza che siano divise a metà fra due pagine consecutive... CustomFormat.FormatFlags = StringFormatFlags.LineLimit 'e specificando che siano scritte solo parole intere quando si giunge alla fine della linea CustomFormat.Trimming = StringTrimming.Word End Sub Private Sub PrintDocument1_PrintPage1(ByVal sender As Object, ByVal e As _ System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage 'variabile che raccoglie le dimensioni occupate dal testo Dim dimensioniTesto As SizeF 'numero di caratteri che rientrano nello spazio disponibile alla scrittura del testo Dim characters_fitted As Integer 'numero di linee occupate dal testo nello spazio disponibile Dim line_filled As Integer 'coordinata y alla quale sarà scritto il testo Dim y As Single = 0 'indice per recuperare i paragrafi che compongono il testo Dim indice As Integer 'testo che compone i paragrafi Dim TestoP As String 'area rettangolare occupata dal testo Dim areaTesto As RectangleF 'area rettangolare occupata dall'immagine Dim areaImg As RectangleF 'coordinata x dell'angolo superiore sinistro dell'immagine Dim areaImgX As Single 'coordinata y dell'angolo superiore sinistro dell'immagine Dim areaImgY As Single 'misura della larghezza dell'immagine Dim areaImgW As Single 'misura dell'altezza dell'immagine Dim areaImgH As Single 'aggiornamento del nuemro di pagina NumeroPagina += 1 'per ogni immagine che è contenuta nel testo For Each foto As ImmagineX In Immagine 'se l'immagine appartiene alla pagina in corso di valutazione.. If foto.NPagina = NumeroPagina Then 'recuperiamo il valore dell'ascissa del punto di inserimento dell'immagine Select Case foto.X 'allineamento a sinistra Case 0 areaImgX = e.MarginBounds.Left 'allineamento centrato Case 1 areaImgX = CType((e.MarginBounds.Width - foto.Width) / 2 + e.MarginBounds.Left, Single) 'allineamento a destra Case 2 areaImgX = e.MarginBounds.Right - foto.Width End Select 'si disegna l'immagine e.Graphics.DrawImage(foto.Picture, areaImgX, foto.Y, foto.Width, foto.Height) End If Next 'si disegnano tutti i paragrafi dividendoli a loro volta in linee Do While conteggio > 0 'per ogni immagine che è contenuta nel testo For Each foto As ImmagineX In Immagine 'se la foto appartiene alla pagina corrente e la coordinata a cui bisogna scrivere il testo 'è inferiore a quella dell'immagine (ossia l'immagine è più in basso del punto in cui stiamo scrivendo..) If foto.NPagina = NumeroPagina And e.MarginBounds.Top + y < foto.Y + foto.Height Then 'recuperiamo il valore dell'ascissa del punto di inserimento dell'immagine Select Case foto.X 'allineamento a sinistra Case 0 areaImgX = e.MarginBounds.Left 'allineamento centrato Case 1 areaImgX = CType((e.MarginBounds.Width - foto.Width) / 2 + e.MarginBounds.Left, Single) 'allineamento a destra Case 2 areaImgX = e.MarginBounds.Right - foto.Width End Select 'coordinata y areaImgY = foto.Y 'larghezza immagine areaImgW = foto.Width 'altezza immagine areaImgH = foto.Height 'area occuopata dall'immagine areaImg = New RectangleF(areaImgX, areaImgY, areaImgW, areaImgH) Exit For End If Next 'definizione dell'indice della collection indice = ListaParagrafi.Count - conteggio + 1 'Si memorizza il paragrafo nella stringa TestoP TestoP = ListaParagrafi.Item(indice).ToString 'si stabilisce che l'area a disposizione del testo è una riga intera areaTesto = New RectangleF(e.MarginBounds.Left, e.MarginBounds.Top + y, e.MarginBounds.Width, HFont) 'se la riga in cui sarà scritto il testo interseca un'immagine... If areaTesto.IntersectsWith(areaImg) Then 'si determina l'area disponibile per il testo in funzione dell'allineamento dell'immagine Select Case areaImgX 'se l'immagine è a sinistra... Case e.MarginBounds.Left 'lo spazio disponibile per scrivere è a destra dell'immagine areaTesto = New RectangleF(e.MarginBounds.Left + areaImgW, e.MarginBounds.Top + y, _ e.MarginBounds.Width - areaImgW, HFont) 'se è a destra... Case (e.MarginBounds.Right - areaImgW) 'lo spazio disponibile per scrivere è a sinistra dell'immagine areaTesto = New RectangleF(e.MarginBounds.Left, e.MarginBounds.Top + y, _ e.MarginBounds.Width - areaImgW, HFont) 'se è centrata Case Else 'lo spazio disponibile è sia a destra che a sinistra dell'immagine 'per il momento prendiamo in considerazione lo spazio alla sinistra. areaTesto = New RectangleF(e.MarginBounds.Left, e.MarginBounds.Top + y, _ areaImgX - e.MarginBounds.Left, HFont) 'valutiamo le dimensione del testo considerando l'area a sinistra dell'immagine appena determinata dimensioniTesto = e.Graphics.MeasureString(TestoP, NuovoFont, New SizeF(areaTesto.Width, _ areaTesto.Height), CustomFormat, characters_fitted, line_filled) 'se nell'area vi possono essere scritti un numero di caratteri maggiori di zero... If characters_fitted > 0 Then 'li scriviamo e.Graphics.DrawString(TestoP, NuovoFont, Brushes.Black, areaTesto, CustomFormat) End If 'il testo nella stringa viene aggiornato per contenere solo quella parte di paragrafo che ancora non 'è stata stampata TestoP = TestoP.Substring(characters_fitted) 'se la lunghezza dalle stringa che contiene il paragrafo restante è maggiore di zero... If TestoP.Length > 0 Then 'significa che ancora non abbiamo terminato di completare il paragrafo 'quindi togliamo dalla collection tutto il paragrafo che stiamo processando, ListaParagrafi.Remove(indice) 'e vi inseriamo solo quella parte che ancora non è stata stampata ListaParagrafi.Add(TestoP, , indice, ) 'in pratica sostituiamo tutto il paragrafo con soltanto la parta ancora da processare Else 'se invece il testo nella stringa TestoP è stato stampato tutto... 'si incrementa la coordinata y di un valore pari all'altezza del font per continuare 'a scrivere nella linea successiva y += HFont End If 'si definisce l'altra area in cui è possibile stampare il testo , la parte a destra dell'immagine areaTesto = New RectangleF(areaImgX + areaImgW, e.MarginBounds.Top + y, _ e.MarginBounds.Right - areaImgW - areaImgX, HFont) End Select End If 'si ripete il procedimento: 'si valutano le dimensioni del testo dimensioniTesto = e.Graphics.MeasureString(TestoP, NuovoFont, New SizeF(areaTesto.Width, _ areaTesto.Height), CustomFormat, characters_fitted, line_filled) 'se nell'area vi possono essere scritti un numero di caratteri maggiori di zero... If characters_fitted > 0 Then 'si scrive il testo... e.Graphics.DrawString(TestoP, NuovoFont, Brushes.Black, areaTesto, CustomFormat) 'si incrementa la coordinata y di un valore pari all'altezza del font per continuare 'a scrivere nella linea successiva y += HFont End If 'il testo nella stringa viene aggiornato per contenere solo quella parte di paragrafo che ancora non è stata stampata TestoP = TestoP.Substring(characters_fitted) 'come prima: se la lunghezza dalle stringa che contiene il paragrafo restante è maggiore di zero... If TestoP.Length > 0 Then 'significa che ancora non abbiamo terminato di completare il paragrafo 'quindi togliamo dalla collection tutto il paragrafo che stiamo processando, ListaParagrafi.Remove(indice) 'e vi inseriamo solo quella parte che ancora non è stata stampata ListaParagrafi.Add(TestoP, , indice, ) 'in pratica sostituiamo tutto il paragrafo con soltanto la parta ancora da processare Else 'si aggiorna il conteggio dei paragrafi valutati sinora conteggio -= 1 End If 'se la coordinata y della linea appena stampata sommata all'altezza del font 'risulta maggiore del margine inferiore, significa che siamo arrivati alla fine del foglio 'e bisogna continuare la stampa su un'altra pagina If e.MarginBounds.Top + y + HFont > e.MarginBounds.Bottom Then Exit Do Loop 'si continua la stampa se i paragrafi da processare non sono ancora finiti e.HasMorePages = (conteggio > 0) End Sub Private Sub PrintDocument1_EndPrint1(ByVal sender As Object, ByVal e As _ System.Drawing.Printing.PrintEventArgs) Handles PrintDocument1.EndPrint 'si rilascia le risorse occupate dal font NuovoFont.Dispose() NuovoFont = Nothing End Sub End Class
Il programma usa come base di partenza il precedente codice sulla gestione del testo composto da molte pagine, modificandolo a seconda della necessità.
Anche in questo caso il testo viene suddiviso in paragrafi (come prima il paragrafo è quell'insieme di parole delimitato da due LineFeed, o per scriverlo in italiano da due "ritorno a capo") che a loro volta, per essere stampati vengono divisi ulteriornmente in righe controllando di volta in volta se la linea in fase di valutazione interseca un'eventuale immagine che può essere contenuta nella pagina.
In caso affermativo, lo spazio a disposizione per scrivere il testo viene ridotto della larghezza dell'immagine rappresentata e le parole scritte nello spazio restante:
-tutte a destra per un'immagine allineata a sinistra,
-tutte a sinistra per un'immagine allineata a destra,
-diviso in due parti uguali, una a destra e l'altra a sinistra, per immagini centrate