Introduction
I see many questions about printing, when helping people on the VC++ forum here at CP, but the one thing I have never been able to help people with, was printing the content of a Rich Edit Control. So off I went to the documentation to try and do something about it.
So how did I do?
- Yes, you can print a Rich Edit Control
- But the print preview sucks
I will be continuing to look into the print preview problems with the Rich Edit Control, but in the meantime, I though I would make available my solution to actual printing.
What to do to get a Rich Edit Control to print?
A Rich Edit Control has built in support for printing, but it is covered by a number of functions which have to be used in the correct way to get output on your target printer DC. These 3 functions are:
SetTargetDevice()
This function in effect attaches the printer DC to the Rich Edit control, until you do another call to this function passing NULL
as the new HDC
.
FormatRange()
This procedure looks at the start/end characters provided and see what part of the control's content will be visible in the output range.
DisplayBand()
This procedure actually does the plotting of the output.
What steps do I need to take?
First you need to determine how many pages of output you are going to require for your printout. This will depend on the control content and the printable area of the paper etc. My example assumes that the Rich Edit Control will print all its content and have all of every page to print to when generating output, so this is how I calculate the number of pages:
BOOL CRichEditPrintView::OnPreparePrinting(CPrintInfo* pInfo)
{
CDC dc;
CRect page;
FORMATRANGE fmtRange;
long lLineWidth;
int last = 0;
AfxGetApp()->CreatePrinterDC(dc);
if (!dc.m_hDC)
{
TRACE("Failed to get printer DC\n");
return FALSE;
}
dc.SaveDC();
pInfo->SetMaxPage(0);
page.left = 0;
page.top = 0;
page.right = ::MulDiv(dc.GetDeviceCaps(PHYSICALWIDTH),
1440, dc.GetDeviceCaps(LOGPIXELSX));
page.bottom = ::MulDiv(dc.GetDeviceCaps(PHYSICALHEIGHT),
1440, dc.GetDeviceCaps(LOGPIXELSY));
lLineWidth = ::MulDiv(dc.GetDeviceCaps(PHYSICALWIDTH),
1440, dc.GetDeviceCaps(LOGPIXELSX));
fmtRange.hdc = dc.m_hDC;
fmtRange.hdcTarget = dc.m_hAttribDC;
fmtRange.rc = page;
fmtRange.rcPage = page;
m_Control.SetTargetDevice(dc, lLineWidth);
while (last < m_Control.GetTextLength())
{
fmtRange.chrg.cpMin = last;
fmtRange.chrg.cpMax = -1;
last = m_Control.FormatRange(&fmtRange, FALSE);
fmtRange.chrg.cpMax = last;
pInfo->SetMaxPage(pInfo->GetMaxPage() + 1);
}
m_Control.FormatRange(NULL, FALSE);
dc.RestoreDC(-1);
dc.DeleteDC();
return DoPreparePrinting(pInfo);
}
This should give us the correct number of pages of output for the content.
Do the OnPrint() version
The actual version that does the printing is very similar except that:
- It skips over pages already printed.
- It prints the output.
The code looks like:
void CRichEditPrintView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
long lLineWidth = ::MulDiv(pDC->GetDeviceCaps(PHYSICALWIDTH),
1440, pDC->GetDeviceCaps(LOGPIXELSX));
FORMATRANGE fmtRange;
CRect rect;
CRect page;
int last = 0;
page.left = 0;
page.top = 0;
page.right = ::MulDiv(pDC->GetDeviceCaps(PHYSICALWIDTH),
1440, pDC->GetDeviceCaps(LOGPIXELSX));
page.bottom = ::MulDiv(pDC->GetDeviceCaps(PHYSICALHEIGHT),
1440, pDC->GetDeviceCaps(LOGPIXELSY));
rect = page;
fmtRange.hdc = pDC->m_hDC;
fmtRange.hdcTarget = pDC->m_hAttribDC;
fmtRange.rc = rect;
fmtRange.rcPage = page;
pDC->SaveDC();
m_Control.SetTargetDevice(*pDC, lLineWidth);
for (UINT i = 0 ; i < pInfo->m_nCurPage ; i++)
{
fmtRange.chrg.cpMin = last;
fmtRange.chrg.cpMax = -1;
last = m_Control.FormatRange(&fmtRange, TRUE);
fmtRange.chrg.cpMax = last;
}
m_Control.DisplayBand(&rect);
m_Control.FormatRange(NULL, FALSE);
pDC->RestoreDC(-1);
}
And that's it basically.
Problems, problems, problems
As mentioned earlier, the print preview of the output sucks due to the Rich Edit Control not scaling the font(s) and spacing the text correctly for the Screen DC used during the preview process. I will be continuing to look into this. My best guess at the moment may be to implement a complete RTF output library, that will be able to plot the RTF text correctly for preview and print, but its a lot of work.
Enjoy - or not when it comes to Rich Edit Controls.