I have searched all over the web for good printing code and was unable to find it. I have created a printing class which allows text to be formatted at the character level. It has taken me about a week to get what I have working correctly and I thought it would be nice to save others the time.
My code is easily modified and shouldn’t be too hard to understand. It is well commented. It recognizes the HTML bold tag and a special <ST=> tag which I created to take the place of what tables do in HTML. Paragraphs are separated using vbCrLf and lines will wrap correctly without chopping off a word.
Here is the block of code to instantiate and use the class:
Dim MyPrintObject As New TextPrint("<B>this will be bold</B>" + _
vbCrLf + "<ST=400>this will start at 400 pixels")
MyPrintObject.Font = New Font("Tahoma", 8)
MyPrintObject.Print()
Here is the actual printing class which does the work:
Public Class TextPrint
Inherits Printing.PrintDocument
Private fntPrintFont As Font
Private strText As String
Dim MySplitLine As String()
Dim varStart As Integer = 0
Dim varChar As Integer = 0
Public Sub New(ByVal Text As String)
MyBase.New()
varStart = 0
strText = Text
MySplitLine = strText.Split(vbCrLf)
End Sub
Public Property Text() As String
Get
Return strText
End Get
Set(ByVal Value As String)
strText = Value
MySplitLine = strText.Split(vbCrLf)
End Set
End Property
Protected Overrides Sub OnBeginPrint(ByVal ev As Printing.PrintEventArgs)
MyBase.OnBeginPrint(ev)
If fntPrintFont Is Nothing Then
fntPrintFont = New Font("Times New Roman", 12, FontStyle.Regular, GraphicsUnit.Point)
End If
End Sub
Public Property Font() As Font
Get
Return fntPrintFont
End Get
Set(ByVal Value As Font)
fntPrintFont = Value
End Set
End Property
Protected Overrides Sub OnPrintPage(ByVal e As Printing.PrintPageEventArgs)
MyBase.OnPrintPage(e)
Dim the_font As Font = fntPrintFont
Dim string_format As New StringFormat
string_format.Alignment = StringAlignment.Near
string_format.FormatFlags = StringFormatFlags.LineLimit
string_format.Trimming = StringTrimming.Word
Dim ymin As Integer = e.MarginBounds.Top
Dim layout_rect As RectangleF
Dim text_size As SizeF
Dim characters_fitted As Integer
Dim lines_filled As Integer
Static i As Integer
For i = varStart To MySplitLine.GetUpperBound(0)
Dim smallArray As String(,)
Dim xmin As Integer = e.MarginBounds.Left
Dim varWord As RectangleF()
ReDim varWord(1)
Dim wordCountForLine As Integer = 0
If Trim(Len(MySplitLine(i))) = 1 Then
ReDim smallArray(3, 1)
smallArray(0, 0) = ""
smallArray(1, 0) = FontStyle.Regular
smallArray(2, 0) = -1
ymin += CInt(the_font.Height)
Else
smallArray = checkBold(Trim(MySplitLine(i).ToString), fntPrintFont)
End If
Dim x As Integer
For x = varChar To smallArray.GetUpperBound(1) - 1
If smallArray(0, x).Length = 0 Then smallArray(0, x) = Chr(0)
the_font = New Font(fntPrintFont.Name, fntPrintFont.Size, _
CInt(smallArray(1, x)), fntPrintFont.Unit)
If CInt(smallArray(2, x)) > -1 Then xmin = CInt(smallArray(2, x))
layout_rect = New RectangleF(xmin, ymin, e.MarginBounds.Right - xmin, the_font.Height)
If layout_rect.Height < 1 Then layout_rect.Height = 1
text_size = e.Graphics.MeasureString(smallArray(0, x).ToString, the_font, _
New SizeF(layout_rect.Width, layout_rect.Height), _
string_format, characters_fitted, lines_filled)
If characters_fitted > 0 Then
varWord(varWord.GetUpperBound(0) - 1) = layout_rect
If Asc(smallArray(0, x).Chars(0)) = 32 Or x = smallArray.GetUpperBound(1) - 1 Then
Dim z As Integer
For z = x - (varWord.GetUpperBound(0) - 1) To x
the_font = New Font(fntPrintFont.Name, fntPrintFont.Size, _
CInt(smallArray(1, z)), fntPrintFont.Unit)
e.Graphics.DrawString(smallArray(0, z), _
the_font, Brushes.Black, _
varWord((z - x) + (varWord.GetUpperBound(0) - 1)), string_format)
Next
xmin += 4
ReDim varWord(0)
wordCountForLine += 1
End If
xmin += CInt(text_size.Width) - 4
ReDim Preserve varWord(varWord.GetUpperBound(0) + 1)
ElseIf Asc(smallArray(0, x).Chars(0)) < 30 Then
Else If wordCountForLine = 0 Then
varWord(varWord.GetUpperBound(0) - 1) = layout_rect
Dim z As Integer
For z = x - (varWord.GetUpperBound(0) - 1) To x
e.Graphics.DrawString(smallArray(0, z), _
the_font, Brushes.Black, _
varWord((z - x) + (varWord.GetUpperBound(0) - 1)), string_format)
Next
ReDim varWord(0)
End If
wordCountForLine = 0
x -= varWord.GetUpperBound(0)
ReDim varWord(1)
xmin = e.MarginBounds.Left
ymin += CInt(the_font.Height) If (e.MarginBounds.Bottom - ymin) < the_font.Height Then
Exit For End If
End If
Next
ymin += CInt(the_font.Height) If (e.MarginBounds.Bottom - ymin) < the_font.Height Then
varChar = x varStart = i e.HasMorePages = True Exit For Else
varChar = 0
e.HasMorePages = False
End If
Next
End Sub
Private Function checkBold(ByVal varString As String, ByVal startFont As Font) As String(,)
Dim aryString As String(,)
ReDim aryString(3, 1)
Dim printStyle As FontStyle = FontStyle.Regular
Dim varStartPlace As Integer = -1
aryString(0, 0) = "" aryString(1, 0) = printStyle
aryString(2, 0) = varStartPlace
Dim varPlace As Integer = 0
For varPlace = 1 To varString.Length
If Mid(varString, varPlace, 3) = "<B>" Then
printStyle = FontStyle.Bold
varPlace += 2
ElseIf Mid(varString, varPlace, 4) = "</B>" Then
printStyle = FontStyle.Regular
varPlace += 3
ElseIf Mid(varString, varPlace, 4) = "<ST=" Then
varStartPlace = CInt(Mid(varString, varPlace + 4, _
InStr(varPlace + 4, varString, ">") - (varPlace + 4)))
varPlace += 4 + varStartPlace.ToString.Length
Else
ReDim Preserve (aryString(3, aryString.GetUpperBound(1) + 1))
aryString(0, aryString.GetUpperBound(1) - 1) = Mid(varString, varPlace, 1)
aryString(1, aryString.GetUpperBound(1) - 1) = printStyle
aryString(2, aryString.GetUpperBound(1) - 1) = varStartPlace
varStartPlace = -1
End If
Next
checkBold = aryString
End Function
End Class
Obviously there are many enhancements which can be done to it; however it should be a great start for most programmers.