Appunti di Programmazione

Creative Commons License

Pagina 1 2 3 4 5 6 7 8

Stampare documenti di Testo e Immagini

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

Stampa Testo e Immagini in documento multi-pagina

Download sorgente "Stampa_Testo_e_Immagini.zip" ( 73KB )

Pagina 1 2 3 4 5 6 7 8