Date: | January 12, 2007 / year-entry #13 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20070112-02/?p=28423 |
Comments: | 5 |
Summary: | 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... |
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 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 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 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
See?
It's not so hard.
Once you find |
Comments (5)
Comments are closed. |
It’s worth noting also that the RichEdit control doesn’t support WM_PRINTCLIENT, so EM_FORMATRANGE is the only way to approximate this behavior as well.
I haven’t looked at printing in windows since win95, and back then it was non-trivial to figure out. While I comprehend the code you’ve posted, I see printing continues to be a non-trivial task. Maybe since you talk of graphics etc enough, the abstraction of the device context/DC means who cares if you’re drawing to the screen or printer, but to the rest of us rubes, it’s stupidly complex.
WM_PRINTCLIENT has nothing to do with printing Raymond, I was just pointing out that if you do want to get the RichEdit control to render itself to an arbitrary DC, EM_FORMATRANGE is the only way to do it.
Despite the relatively large quantity of documentation on this topic, I think there are two reasons people have been confused. (Note: I write this as somebody who’s never needed to make a RichEdit control print, and never even thought to look up the documentation before: So I’m genuinely approaching this as a newbie, an educated and smart newbie, but a newbie nonetheless.)
First, I think people are confused because this is what MSDN says about EM_FORMATRANGE:
"EM_FORMATRANGE Message"
"The EM_FORMATRANGE message formats a range of text in a rich edit control for a specific device."
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/richedit/richeditcontrols/richeditcontrolreference/richeditmessages/em_formatrange.asp
You don’t see the word "print" anywhere in its documentation, and the word "printer" only appears once, near the bottom. Anybody naively searching MSDN for the word "print" or "printing" is going to have a hard time finding this. Try it for yourself; type "site:msdn.microsoft.com print richedit" into Google and see how long it takes to find a meaningful page. A more useful (and yes, redundant) opening sentence might’ve been:
"The EM_FORMATRANGE message formats a range of text in a rich edit control for a specific device, most commonly to print a RichEdit control’s contents to a printer."
A search engine can *find* that one since it has all the right keywords in all the right places, and 99% of the time that’s what people are gonna use EM_FORMATRANGE for anyway, so it’s not inaccurate documentation.
The second reason that people probably have trouble is that for many users, EM_FORMATRANGE is probably overkill; there’s no such thing as a simple EM_PRINT message. As you yourself demonstrated, it’s 30 lines of code to get the thing to print. Admittedly, that’s not a lot, and the additional code offers flexibility, but if all you want is to simply dump the contents of a RichEdit control (which I think is what a lot of people want), you have either some work ahead of you to figure it out or a lot of copy+paste ahead of you — if you can find a good example. Just like CreateWindow can’t throw up a window in one or two lines of code, EM_FORMATMESSAGE can’t print in one or two lines of code, and I think a lot of developers have come to expect common operations can be done in one or two lines of code these days.
So that’s my two cents on reading the documentation and sample code now, but I could always be wrong.
I’ve implemented printing for Windows apps and it is, as my colleague put it, "eine sauerei" (a mess). I think he was talking about the problem generally, rather than my code, but…
Anyway, definitely non-trivial, though I eventually knocked up a small framework which made things a lot easier for anyone following me.