For some reason, people are really puzzled by rich edit printing.
I’m no expert on printing, but even I was able to figure it out.
The kernel is the EM_FORMATRANGE
message.
Each time you call it, a little bit more of the rich text control
is printed, and the message returns the index of the first
unprinted character,
which you can pass back in to print the next chunk.
The rest is just setting up and tearing down.
BOOL PrintRTF(HWND hwnd, HDC hdc) { int cxPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETX); int cyPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETY); int cxPhys = GetDeviceCaps(hdc, PHYSICALWIDTH); int cyPhys = GetDeviceCaps(hdc, PHYSICALHEIGHT); SendMessage(hwnd, EM_SETTARGETDEVICE, (WPARAM)hdc, cxPhys); FORMATRANGE fr; fr.hdc = hdc; fr.hdcTarget = hdc; fr.rc.left = cxPhysOffset; fr.rc.right = cxPhysOffset + cxPhys; fr.rc.top = cyPhysOffset; fr.rc.bottom = cyPhysOffset + cyPhys; SendMessage(hwnd, EM_SETSEL, 0, (LPARAM)-1); SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg); BOOL fSuccess = TRUE; while (fr.chrg.cpMin < fr.chrg.cpMax && fSuccess) { fSuccess = StartPage(hdc) > 0; if (!fSuccess) break; int cpMin = SendMessage(hwnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr); if (cpMin <= fr.chrg.cpMin) { fSuccess = FALSE; break; } fr.chrg.cpMin = cpMin; fSuccess = EndPage(hdc) > 0; } SendMessage(hwnd, EM_FORMATRANGE, FALSE, 0); return fSuccess; }
We start by getting the dimensions of the page and
telling the rich edit control what we intend to render to
by using the EM_SETTARGETDEVICE
message.
Next, we need to fill out our FORMATRANGE
,
which we do by specifying the HDC
we are
rendering to, as well as the paper dimensions.
But what about the character range?
We are lazy and let the rich edit control take care of it for us:
We select all the text and then ask the rich edit control to
tell us what we just selected, which comes back in the form
of a CHARRANGE
, which is exactly what we needed.
Next comes the printing loop. While there is still text to print (and we haven’t encountered an error), we start a new page, ask the rich edit control to render that page, remember where the next page should begin, and end the current page. There’s a little sanity check in there to make sure that the rich edit control made forward progress; if not, then we’ll end up in an infinite loop spewing out blank pages! (I have no idea whether this is theoretically possible, but I’m going to protect against it just the same.)
Once the printing loop is complete, we clean up by sending
one last EM_FORMATRANGE
message to tell the
rich edit control that we’re all done and it can discard the
information it cached.
We can take all the information we’ve learned over the past few days to make a simple “print RTF” program.
int CALLBACK _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) { LoadLibrary(TEXT("riched20.dll")); HWND hwndRTF = CreateWindow(RICHEDIT_CLASS, NULL, ES_MULTILINE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, 0, 0, 0); if (hwndRTF) { SendMessage(hwndRTF, EM_EXLIMITTEXT, 0, -1); if (FillRichEditFromFile(hwndRTF, lpCmdLine)) { PRINTDLG pd = { sizeof(pd) }; pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT; if (PrintDlg(&pd)) { DOCINFO di = { sizeof(di) }; di.lpszDocName = TEXT("Sample Printout"); if (StartDoc(pd.hDC, &di) > 0) { if (PrintRTF(hwndRTF, pd.hDC)) { EndDoc(pd.hDC); } else { AbortDoc(pd.hDC); } } GlobalFree(pd.hDevMode); GlobalFree(pd.hDevNames); DeleteDC(pd.hDC); } } DestroyWindow(hwndRTF); } return 0; }
There’s not really much going on here; it’s all just glue and necessary typing.
We create a rich edit control and fill it with the file
passed on the command line.
We then ask the PrintDlg
function to give us a
DC to the user’s default printer.
We give the document a title, start the document, print the rich
text into the document, and then end the document (or abort it
if something went wrong during printing).
A little cleaning up, and we’re all done.
A tiny program to print an arbitrary RTF document with
no fanfare whatsoever.
See?
It’s not so hard.
Once you find EM_FORMATRANGE
the rest is just doing
the obvious.
0 comments