I'm printing an edit control - by first copying its contents to a rich edit control (2.0 at least - could be rich edit 3.0) - and then printing from there.
I've got it all working... but the margins are sloppy / incorrect.
I've gone over the code repeatedly, verified the numbers in the debugger, and still the margins (for the printer I have available for testing) come out wrong on the printed page.
The code is more or less a copy from various examples of printing from a rich edit available around the 'net - including The Old New Thing, MSDN's docs, and CodeProject and etc.
It boils down to (hdc is for the user-selected printer using the user-selected paper source):
// printer dot's per inch (pixels)
const CSize dpi = { GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY) };
// paper size (in printer's dots ~ pixels)
const CSize paper = { GetDeviceCaps(hdc, PHYSICALWIDTH), GetDeviceCaps(hdc, PHYSICALHEIGHT) };
// printable size (pixels) - this defines the largest possible usable rect for this printer
const CRect rcPrintable(CPoint(GetDeviceCaps(hdc, PHYSICALOFFSETX), GetDeviceCaps(hdc, PHYSICALOFFSETY)), CSize(GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES)));
// determine the paper extents using our desired margins without violating the device's minimum margins
const CSize margin = { dpi.cx / 4, dpi.cy / 2 }; // 1/4" horizontal x 1/2" vertical margins
const CRect rcPaper(__max(rcPrintable.left, margin.cx), __max(rcPrintable.top, margin.cy), __min(rcPrintable.right, paper.cx - margin.cx), __min(rcPrintable.bottom, paper.cy - margin.cy));
// convert paper size in printer dots (pixels) to paper size in TWIPS
const CRect rcPage(MulDiv(rcPaper.left, 1440, dpi.cx), MulDiv(rcPaper.top, 1440, dpi.cy), MulDiv(rcPaper.right, 1440, dpi.cx), MulDiv(rcPaper.bottom, 1440, dpi.cy));
// build our format range data
FORMATRANGE fr;
Zero(fr);
fr.hdc = hdc;
fr.hdcTarget = hdc;
// Set page rect to physical page size in TWIPS
fr.rc = rcPage;
fr.rcPage = rcPage;
// set our target device to the printer
m_RichEdit.SetTargetDevice(hdc, rcPage.Width());
m_RichEdit.SetSel(0, -1); // Select the entire contents.
m_RichEdit.GetSel(fr.chrg); // Get the selection into a CHARRANGE
// track the number of pages generated
unsigned nPages = 0;
// give this job a reasonable name
CString strPrintJobName = GetPrintJobName();
// Use GDI to print successive pages
DOCINFO di = { sizeof(di) };
di.lpszDocName = strPrintJobName;
if (!StartDoc(hdc, &di))
throw CLabeledException(_T("Unable to start the print job"));
BOOL fSuccess = TRUE;
while (fr.chrg.cpMin < fr.chrg.cpMax)
{
// start page
fSuccess = StartPage(hdc) > 0;
if (!fSuccess)
break;
// format page
int cpMin = m_RichEdit.FormatRange(&fr, TRUE);
// ensure we made forward progress (avoid infinite loop!)
fSuccess = cpMin > fr.chrg.cpMin;
if (!fSuccess)
break;
// render page
fSuccess = m_RichEdit.DisplayBand(const_cast<CRect&>(rcPage));
if (!fSuccess)
break;
// end page
fSuccess = EndPage(hdc) > 0;
if (!fSuccess)
break;
// update no. pages printed
++nPages;
// update our new position
fr.chrg.cpMin = cpMin;
}
// release internal cached data from rich edit control
m_RichEdit.FormatRange(nullptr, FALSE);
// complete or abort the print job
if (fSuccess)
{
EndDoc(hdc);
MessageBox(FormatString(_T("Printed %u pages"), nPages));
}
else
{
DWORD dwError = GetLastError();
AbortDoc(hdc);
throw CContextException(FormatString(_T("Print failed on page %u"), nPages+1), dwError);
}
The rcPaper for my 600dpi printer comes out to 5100 pixels horizontal by 6600 pixels vertical, less 100dots in all dimensions due to minimum physical offsets / limitations of this printer.
So my 1/4" x 1/2" margins always are larger than the printer's limitations, and we end up with a page = { 150, 300, 4950, 6300 } (pixels).
But when actually printed, I get a top margin of about 5/8" and a bottom margin of about 3/16" and a left margin of about 3/8" and a right margin of about 1/8".
So it's like the printer itself is adding back in its limitations (its PHYSICALOFFSETX
and PHYSICALOFFSETY
) on top of the values being sent to it (or the rich edit control is offsetting everything by those values).
... or something else is going on and/or I'm misunderstanding something!
Ideas?
The answer seems to be that the above maths are correct in finding the "absolute" rect that matches the printer's capabilities and limitations for a desired set of margins, but to create an output rect one needs to subtract the physical offsets back out in order for 0,0 to be 0,0 on the real physical page:
// subtract out the physical offsets
// note: I see no documentation that this is what must be done, yet, it must be done
// otherwise the entire page is off by this exact amount :(
rcPaper.left -= rcPrintable.left;
rcPaper.right -= rcPrintable.left;
rcPaper.top -= rcPrintable.top;
rcPaper.bottom -= rcPrintable.top;