News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Microsoft is WRONG, WRONG, WRONG (printing from a RichEdit control)

Started by NoCforMe, September 23, 2022, 02:32:39 PM

Previous topic - Next topic

NoCforMe

I just figured out something almost on my own (well, with help from some stuff I found on the interwebs) that might save someone else some grief, so I wanted to get it posted here. This is how to print pages from a RichEdit control using the EM_FORMATRANGE message.

The thing is, the Microsoft documentation on how to print using this is dead wrong. Couldn't be wronger. (And the really odd thing is that this page is almost exactly the same code as given as an example by the usually astute Raymond Chen.) There's just no way this code can work. And because this is given as gospel by Microsoft, the error gets propagated across the web on numerous other sites.

Details: there are two RECTs in the structure that EM_FORMATRANGE uses. One is a "rendering rectangle", fr.rect, and the other is a "page rectangle", fr.pageRect. Now, they say to do this to fill out these RECTs:


    int cxPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETX);
    int cyPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETY);
   
    int cxPhys = GetDeviceCaps(hdc, PHYSICALWIDTH);
    int cyPhys = GetDeviceCaps(hdc, PHYSICALHEIGHT);

    // Set page rect to physical page size in twips.
    fr.rcPage.top    = 0; 
    fr.rcPage.left   = 0; 
    fr.rcPage.right  = MulDiv(cxPhys, 1440, GetDeviceCaps(hDC, LOGPIXELSX)); 
    fr.rcPage.bottom = MulDiv(cyPhys, 1440, GetDeviceCaps(hDC, LOGPIXELSY));

    // Set the rendering rectangle to the pintable area of the page.
    fr.rc.left   = cxPhysOffset;
    fr.rc.right  = cxPhysOffset + cxPhys;
    fr.rc.top    = cyPhysOffset;
    fr.rc.bottom = cyPhysOffset + cyPhys;


But that's just plain wrong. They're mixing apples and oranges here; the page RECT is getting set to dimensions measured in "twips" (a twip is 1/20th of a point, or 1/1440th of an inch), while the rendering RECT is being set to device units (from GetDeviceCaps() ), which are not the same units.

This is a glaring error; obviously nobody up there in Redmond actually checked this code. The correct thing to do is to set both RECTs in twips. That works, as I just proved, both with a physical printer and with a PDF creator.

The other error is that you need to subtract the X- and Y-offsets from the page width and height, respectively, not add them. In fact, you need to subtract twice the offset for each dimension, since there's an offset at both the left and right (and top and bottom).

Here's my code (in assembler) that works:


INVOKE GetDeviceCaps, pdex.hDC, PHYSICALOFFSETX
MOV pPhysOffsetX, EAX
INVOKE GetDeviceCaps, pdex.hDC, PHYSICALOFFSETY
MOV pPhysOffsetY, EAX

INVOKE GetDeviceCaps, pdex.hDC, PHYSICALWIDTH
MOV pPhysW, EAX
INVOKE GetDeviceCaps, pdex.hDC, PHYSICALHEIGHT
MOV pPhysH, EAX

; Set up "page rectangle":
MOV fr.rcPage.top, 0
MOV fr.rcPage.left, 0
INVOKE GetDeviceCaps, pdex.hDC, LOGPIXELSX
MOV xPPI, EAX
INVOKE MulDiv, pPhysW, 1440, EAX
MOV fr.rcPage.right, EAX
INVOKE GetDeviceCaps, pdex.hDC, LOGPIXELSY
MOV yPPI, EAX
INVOKE MulDiv, pPhysH, 1440, EAX
MOV fr.rcPage.bottom, EAX

; Now the "rendering rectangle":
INVOKE MulDiv, pPhysOffsetX, 1440, xPPI
MOV fr.rc.left, EAX
INVOKE MulDiv, pPhysOffsetY, 1440, yPPI
MOV fr.rc.top, EAX
INVOKE MulDiv, pPhysW, 1440, xPPI
MOV EDX, fr.rc.left
SHL EDX, 1
SUB EAX, EDX
MOV fr.rc.right, EAX
INVOKE MulDiv, pPhysH, 1440, yPPI
MOV EDX, fr.rc.top
SHL EDX, 1
SUB EAX, EDX
MOV fr.rc.bottom, EAX


This could probably be simplified a bit to eliminate some of those MulDiv()s, but that's icing on the cake. (Speed is of no concern here, since we're dealing with a printer.)
Assembly language programming should be fun. That's why I do it.

NoCforMe

Someone just PMed me with the following:
QuoteThe "Microsoft Print to PDF" printer reports a PHYSICALOFFSETY DevCap value of 0, yet printing at that offset with TextAlign set to Top/Left does not output visible characters, at least in my code. Have you experienced similar?

Since it's been a while since I dealt with this issue I'll have to reacquaint myself with it. Any clues?
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on September 23, 2022, 02:32:39 PMThe thing is, the Microsoft documentation on how to print using this is dead wrong.

I believe you because you know your stuff. However, so far I've not had problems with printing from a RichEd control - PrintRtf produces the expected results. How does this problem manifest itself? Wrong margins, wrong size(s)?

NoCforMe

Quote from: jj2007 on January 09, 2024, 08:36:44 PM
Quote from: NoCforMe on September 23, 2022, 02:32:39 PMThe thing is, the Microsoft documentation on how to print using this is dead wrong.

I believe you because you know your stuff.
Sometimes, sometimes ...
QuoteHowever, so far I've not had problems with printing from a RichEd control - PrintRtf produces the expected results. How does this problem manifest itself? Wrong margins, wrong size(s)?
I don't remember exactly, but when I tried the code suggested both by Micro$oft and by R. Chen it just didn't work correctly at all; I could print, but the page handling logic was waaaaay off. Couldn't print an entire document correctly. When I implemented the code shown (I think that is pretty much exactly what I used in my editor, which prints perfectly) it worked.

Can you show us how your RichEdit printing code works, maybe in pseudocode? I'd be willing to bet that you ended up with something close to what I came up with, and certainly not the code suggested by Micro$oft.

I'm currently in a non-programming mode, haven't written a line of code in weeks. But I'm interested in this problem and plan to take another look at it, maybe put together a test bed to see what's what.
Assembly language programming should be fun. That's why I do it.