MORE INFORMATION
Printing from a Rich Edit control involves the use of the standard printing
APIs and two Rich Edit control messages, EM_FORMATRANGE and EM_DISPLAYBAND.
The EM_FORMATRANGE message can be used by itself or used in combination
with the EM_DISPLAYBAND message. Included below at the end of this article
is a code sample which demonstrates the usage of these messages.
EM_FORMATRANGE
This message is used to format the text for the printer DC and can
optionally send the output to the printer.
The wParam parameter for this message is a Boolean value that indicates
whether or not the text should be rendered (printed) to the printer. A zero
value only formats the text, while a nonzero value formats the text and
renders it to the printer.
The lParam parameter for this message is a pointer to the
FORMATRANGE structure. This structure needs to be filled out before sending the message to the control.
FORMATRANGE Members
HDC hdc - Contains the device context (DC) to render to if the wParam
parameter is nonzero. The output is actually sent to this DC.
HDC hdcTarget - Contains the device context to format for, which is
usually the same as the hdc member but can be different. For example, if
you create a print preview module, the hdc member is the DC of the window
in which the output is viewed, and the hdcTarget member is the DC for the
printer.
RECT rc - Contains the area to render to. This member contains the
rectangle that the text is formatted to fit in, and subsequently printed
in. It also contains the margins, room for headers and footers, and so
forth. The rc.bottom member may be changed after the message is sent. If it
is changed, it must indicate the largest rectangle that can fit within the
bounds of the original rectangle and still contain the specified text
without printing partial lines. It may be necessary to reset this value
after each page is printed. These dimensions are given in TWIPS.
RECT rcPage - Contains the entire area of the rendering device. This area
can be obtained using the
GetDeviceCaps() function. These dimensions are given in TWIPS.
CHARRANGE chrg - Contains the range of characters to be printed. Set
chrg.cpMin to 0 and chrg.cpMax to -1 to print all characters.
The return value from EM_FORMATRANGE is the index of the first character on
the next page. If you are printing multiple pages, you should set
chrg.cpMin to this value before the next EM_FORMATRANGE message is sent.
When printing is complete, this message must be sent to the control with
wParam = 0 and lParam = NULL to free the information cache by the control.
EM_DISPLAYBAND
If you use 0 for the wParam parameter in the EM_FORMATRANGE message, then
you can use the EM_DISPLAYBAND message to send the output to the printer.
The wParam parameter for this message is not used and should be 0.
The lParam parameter for this message is a pointer to a RECT structure.
This RECT structure is the area to display to and is usually the same as
the rc member of the FORMATRANGE structure used in the EM_FORMATRANGE
message but can be different. For example, the rectangles are not the same
if you are printing on a certain portion of a page or built-in margins are
being used.
This message should only be used after a previous EM_FORMATRANGE message.
Sample Code
void Print(HDC hPrinterDC, HWND hRTFWnd)
{
FORMATRANGE fr;
int nHorizRes = GetDeviceCaps(hPrinterDC, HORZRES),
nVertRes = GetDeviceCaps(hPrinterDC, VERTRES),
nLogPixelsX = GetDeviceCaps(hPrinterDC, LOGPIXELSX),
nLogPixelsY = GetDeviceCaps(hPrinterDC, LOGPIXELSY);
LONG lTextLength; // Length of document.
LONG lTextPrinted; // Amount of document printed.
// Ensure the printer DC is in MM_TEXT mode.
SetMapMode ( hPrinterDC, MM_TEXT );
// Rendering to the same DC we are measuring.
ZeroMemory(&fr, sizeof(fr));
fr.hdc = fr.hdcTarget = hPrinterDC;
// Set up the page.
fr.rcPage.left = fr.rcPage.top = 0;
fr.rcPage.right = (nHorizRes/nLogPixelsX) * 1440;
fr.rcPage.bottom = (nVertRes/nLogPixelsY) * 1440;
// Set up 1" margins all around.
fr.rc.left = fr.rcPage.left + 1440; // 1440 TWIPS = 1 inch.
fr.rc.top = fr.rcPage.top + 1440;
fr.rc.right = fr.rcPage.right - 1440;
fr.rc.bottom = fr.rcPage.bottom - 1440;
// Default the range of text to print as the entire document.
fr.chrg.cpMin = 0;
fr.chrg.cpMax = -1;
// Set up the print job (standard printing stuff here).
ZeroMemory(&di, sizeof(di));
di.cbSize = sizeof(DOCINFO);
if (*szFileName)
di.lpszDocName = szFileName;
else
{
di.lpszDocName = "(Untitled)";
// Do not print to file.
di.lpszOutput = NULL;
}
// Start the document.
StartDoc(hPrinterDC, &di);
// Find out real size of document in characters.
lTextLength = SendMessage ( hRTFWnd, WM_GETTEXTLENGTH, 0, 0 );
do
{
// Start the page.
StartPage(hPrinterDC);
// Print as much text as can fit on a page. The return value is
// the index of the first character on the next page. Using TRUE
// for the wParam parameter causes the text to be printed.
#ifdef USE_BANDING
lTextPrinted = SendMessage(hRTFWnd,
EM_FORMATRANGE,
FALSE,
(LPARAM)&fr);
SendMessage(hRTFWnd, EM_DISPLAYBAND, 0, (LPARAM)&fr.rc);
#else
lTextPrinted = SendMessage(hRTFWnd,
EM_FORMATRANGE,
TRUE,
(LPARAM)&fr);
#endif
// Print last page.
EndPage(hPrinterDC);
// If there is more text to print, adjust the range of characters
// to start printing at the first character of the next page.
if (lTextPrinted < lTextLength)
{
fr.chrg.cpMin = lTextPrinted;
fr.chrg.cpMax = -1;
}
}
while (lTextPrinted < lTextLength);
// Tell the control to release cached information.
SendMessage(hRTFWnd, EM_FORMATRANGE, 0, (LPARAM)NULL);
EndDoc (hPrinterDC);
}