Re: Header and Footer with RichTextBox Control



"DORI" <DORI@xxxxxxxxxxxxxxxxxxxxxxxxx> wrote in message news:E72389DA-963F-478A-853D-33834DE4976E@xxxxxxxxxxxxxxxx

Thanks very much for the information. Yes, it would be ok if
I send the footer and header to RTB when it is prining but I
am not sure how to calculate the exact location of the page to
put the header and the footer as I am using WYSIWYG_RTF
module for printing the RTF and it may get print over with the
text from the body of the page depending on length of the
text in some pages. Any suggestions?

Presumably you're printing the RichTextBox using SendMessage to send it a series of EM_FORMATRANGE messages, in which case you will be passing it a FORMATRANGE structure which contains, amongst other things, the desired printing rectangle on the page (the desired print area that lies within your desired left, right, top and bottom page margins). In that case the RTB will print its contents within that area of the page and you will know exactly what the minimum top and bottom margins of the printed pages will be. The WYSIWYG display of the RTB so that it wraps all lines at exactly the same point as it will do on the printer is usually achieved by sending the RTB an EM_SETTARGETDEVICE message, so that it is the RTB that wraps its lines in accordance with how they will wrap on the selected printer, not the other way around.

If the above is approximately what you are currently doing then all you need to do is print the header and the footer within the specified top and bottom margins, normally with the "nearest to the edge" vertical position of the header or footer text about half of the margin size from the edge of the page. The trick is to initially send just one EM_FORMATRANGE messages and then print your header and footer on the same page /before/ you send the next EM_FORMATRANGE message, continuing the loop until you have sent the final page. In this way you can print whatever you like in the header or footer, and you can use the standard VB Printer Object methods to do so if you wish.

If you want page numbers as either the header or the footer then you can simply keep a running count of the pages and print the desired page number on each of them. There is a little more to it if you want page numbers in the format "page 1 of n" of course because initially you do not know exactly how many pages the RichTextBox contents will need. The trick here is to add a bit of code to effectively first perform a "dummy run" through all pages by sending a series of EM_FORMATRANGE message with the wParam parameter set to zero (or False if you are using a Boolean there) so as to ask the RTB to measure the output without actually printing it. This will enable you to get an accurate count of the total pages before you perform the actual printing run (with wParam non zero, or True if using a Boolean).

Here is an example of what I am talking about. There are two blocks of code, one which should be pasted into a standard Code Module and the other into a Form containing a RichTextBox and a Command Button. This example includes the "page 1 of n" stuff (so that either the header or the footer can be page numbers in that format). Currently it handles headers and footers which are limited to a single line of text, but it would be easy to amend it to include larger headers and footers if desired. Also it does not currently include any code for setting the RichTextBox to a WYSIWYG display, buit again that code would be easy to add. In fact you should easily be able to incorporate your existing code which does that, or alternatively incorporate this code into your existing project. Anyway, here is the example (below):

Mike

' ***** START OF FORM CODE *****
Option Explicit
' (Mike Williams) Code to print a RichTextBox
' over multiple pages with user selectable all
' round page margins and with page numbers and
' headers/footers.

Private Sub PrintRTB2(RTB As RichTextBox, _
marginsize As Single, header As String, _
footer As String)
Dim charsToPrint As Long, nextChar As Long
Dim pageWide As Single, pageHigh As Single
Dim margin As Single, printControl As Boolean
Dim nPage As Long, totalPages As Long
Dim PageNums As String
charsToPrint = Len(RichTextBox1.Text)
If charsToPrint < 1 Then Exit Sub
Printer.Orientation = vbPRORPortrait
Printer.Print ' always start a print job with this
With Printer
.ScaleMode = vbInches
.Font.Name = "Times new Roman" ' font for header/footer
.Font.Size = 10
pageWide = .ScaleX(.Width, vbTwips, vbInches)
pageHigh = .ScaleY(.Height, vbTwips, vbInches)
End With
margin = marginsize
SetPrinterOrigin 0, 0 ' required for VB methods
nextChar = 0: totalPages = 0
printControl = False 'Don't print, just count the pages
Do
nextChar = PrintRTF2(RichTextBox1, margin, margin, _
pageWide - margin * 2, pageHigh - margin _
* 2, nextChar, printControl)
totalPages = totalPages + 1
Loop Until (nextChar > charsToPrint) Or nextChar = 0
nextChar = 0: nPage = 0
printControl = True ' Now actually print the stuff
Do
With Printer
nPage = nPage + 1
PageNums = "Page " & Format(nPage) & _
" of " & Format(totalPages)
.Font.Italic = True '(or whatever you wish)
.CurrentY = pageHigh - 0.5 - Printer.TextHeight(footer)
' I've had occasional problems with .TextWidth in
' a With Printer block in Vista, which is why I've
' used Printer.TextWidth below
If footer = "<pagenums>" Then
.CurrentX = pageWide / 2 - Printer.TextWidth(PageNums) / 2
Printer.Print PageNums;
Else
.CurrentX = pageWide / 2 - Printer.TextWidth(footer) / 2
Printer.Print footer;
End If
.Font.Italic = True ' (or whatever you wish)
.CurrentY = 0.5
If header = "<pagenums>" Then
.CurrentX = pageWide / 2 - Printer.TextWidth(PageNums) / 2
Printer.Print PageNums;
Else
.CurrentX = pageWide / 2 - Printer.TextWidth(header) / 2
Printer.Print header;
End If
'
nextChar = PrintRTF2(RichTextBox1, margin, margin, _
pageWide - margin * 2, pageHigh - _
margin * 2, nextChar, printControl)
If nextChar <= charsToPrint Then
Printer.NewPage
End If
End With
' >= (not >) because nextchar is a zero based count
Loop Until (nextChar >= charsToPrint) Or nextChar = 0
Printer.EndDoc
End Sub

Private Sub Command1_Click()
' Print RTB with a one inch all round margin
' If you want either the header or the footer text to be
' page numbers then use the text "<pagenums>" instead
PrintRTB2 RichTextBox1, 1, "Some Header Text", "<pagenums>"
End Sub
' ***** END OF FORM CODE *****
'
' ***** START OF MODULE CODE *****
Option Explicit
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function SendMessage Lib "user32" _
Alias "SendMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type CHARRANGE
cpMin As Long
cpMax As Long
End Type
Private Type FORMATRANGE
hdc As Long
hdcTarget As Long
rc As RECT
rcPage As RECT
chrg As CHARRANGE
End Type
Private Const WM_USER As Long = &H400
Private Const EM_FORMATRANGE As Long = WM_USER + 57
Private Const EM_SETTARGETDEVICE As Long = WM_USER + 72
Private Const PHYSICALOFFSETX As Long = 112
Private Const PHYSICALOFFSETY As Long = 113

Public Sub SetPrinterOrigin(x As Single, y As Single)
With Printer
.ScaleLeft = .ScaleX(GetDeviceCaps(.hdc, _
PHYSICALOFFSETX), vbPixels, .ScaleMode) - x
.ScaleTop = .ScaleY(GetDeviceCaps(.hdc, _
PHYSICALOFFSETY), vbPixels, .ScaleMode) - y
.CurrentX = 0
.CurrentY = 0
End With
End Sub

Public Function PrintRTF2(RTF As RichTextBox, x1 As Single, _
y1 As Single, Wide As Single, High As Single, _
charPos As Long, printControl As Boolean) As Long
Dim LeftOffset As Long, TopOffset As Long
Dim fr As FORMATRANGE, rcDrawTo As RECT
Dim rcPage As RECT, Block As RECT
Dim NextCharPosition As Long, r As Long
LeftOffset = Printer.ScaleX(GetDeviceCaps(Printer.hdc, _
PHYSICALOFFSETX), vbPixels, vbTwips)
TopOffset = Printer.ScaleY(GetDeviceCaps(Printer.hdc, _
PHYSICALOFFSETY), vbPixels, vbTwips)
Block.Left = Printer.ScaleX(x1, Printer.ScaleMode, _
vbTwips) - LeftOffset
Block.Top = Printer.ScaleY(y1, Printer.ScaleMode, _
vbTwips) - TopOffset
Block.Right = Block.Left + Printer.ScaleX(Wide, _
Printer.ScaleMode, vbTwips)
Block.Bottom = Block.Top + Printer.ScaleY(High, _
Printer.ScaleMode, vbTwips)
rcPage.Left = 0
rcPage.Top = 0
rcPage.Right = Printer.ScaleX(Printer.ScaleWidth, _
Printer.ScaleMode, vbTwips)
rcPage.Bottom = Printer.ScaleY(Printer.ScaleHeight, _
Printer.ScaleMode, vbTwips)
rcDrawTo.Left = Block.Left
rcDrawTo.Top = Block.Top
rcDrawTo.Right = Block.Right
rcDrawTo.Bottom = Block.Bottom
fr.hdc = Printer.hdc
fr.hdcTarget = Printer.hdc
fr.rc = rcDrawTo
fr.rcPage = rcPage
fr.chrg.cpMin = charPos
fr.chrg.cpMax = -1
PrintRTF2 = SendMessage(RTF.hwnd, _
EM_FORMATRANGE, printControl, fr)
r = SendMessage(RTF.hwnd, EM_FORMATRANGE, _
False, ByVal 0&)
End Function
' ***** END OF MODULE CODE *****

.


Loading