The Ex17a Example: A WYSIWYG Print Program
This example displays and prints a single page of text stored in a document. The printed image should match the displayed image. The MM_TWIPS mapping mode is used for both printer and display. First, we'll use a fixed drawing rectangle, and then we'll base the drawing rectangle on the printable area rectangle supplied by the printer driver.Here are the steps for building the example:
Run the MFC Application Wizard to generate the Ex17a project. Accept the default options. On the Generated Classes page, rename the document class CPoemDoc and the view class CStringView. Derive CStringView from CScrollView. Note that this is an MDI application.
Add a CStringArray data member to the CPoemDoc class. Edit the

public:
CStringArray m_stringArray;
The document data is stored in a string array. The MFC library CStringArray class holds an array of CString objects, which are accessible by a zero-based subscript. You need not set a maximum dimension in the declaration because the array is dynamic.
Add a CRect data member to the CStringView class. Edit the

private:
CRect m_rectPrint;
Edit three CPoemDoc member functions in the file

BOOL CPoemDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
m_stringArray.SetSize(10);
m_stringArray[0] = "The pennycandystore beyond the El";
m_stringArray[1] = "is where I first";
m_stringArray[2] = " fell in love";
m_stringArray[3] = " with unreality";
m_stringArray[4] = "Jellybeans glowed in the semi-gloom";
m_stringArray[5] = "of that september afternoon";
m_stringArray[6] = "A cat upon the counter moved among";
m_stringArray[7] = " the licorice sticks";
m_stringArray[8] = " and tootsie rolls";
m_stringArray[9] = " and Oh Boy Gum";
return TRUE;
}
Note | The CStringArray class supports dynamic arrays, but here we're using the m_stringArray object as if it were a static array of 10 elements. |
The application framework calls the document's virtual DeleteContents function when it closes the document; this action deletes the strings in the array. A CStringArray contains actual objects, and a CObArray contains pointers to objects. This distinction is important when it's time to delete the array elements. Here, the RemoveAll function actually deletes the string objects:
void CPoemDoc::DeleteContents()
{
// called before OnNewDocument and when document is closed
m_stringArray.RemoveAll();
}
Serialization isn't important in this example, but the following function shows how easy it is to serialize strings. The application framework calls the DeleteContents function before loading from the archive, so you don't have to worry about emptying the array. Add the following boldface code:
void CPoemDoc::Serialize(CArchive& ar)
{
m_stringArray.Serialize(ar);
}
Edit the OnInitialUpdate function in

void CStringView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height());
CSize sizePage(sizeTotal.cx / 2,
sizeTotal.cy / 2); // page scroll
CSize sizeLine(sizeTotal.cx / 100,
sizeTotal.cy / 100); // line scroll
SetScrollSizes(MM_TWIPS, sizeTotal, sizePage, sizeLine);
}
Edit the OnDraw function in

void CStringView::OnDraw(CDC* pDC)
{
int i, j, nHeight;
CString str;
CFont font;
TEXTMETRIC tm;
CPoemDoc* pDoc = GetDocument();
// Draw a border — slightly smaller to avoid truncation
pDC->Rectangle(m_rectPrint + CRect(0, 0, -20, 20));
// Draw horizontal and vertical rulers
j = m_rectPrint.Width() / 1440;
for (i = 0; i <= j; i++) {
str.Format("%02d", i);
pDC->TextOut(i * 1440, 0, str);
}
j = -(m_rectPrint.Height() / 1440);
for (i = 0; i <= j; i++) {
str.Format("%02d", i);
pDC->TextOut(0, -i * 1440, str);
}
// Print the poem 0.5 inch down and over;
// use 10-point roman font
font.CreateFont(-200, 0, 0, 0, 400, FALSE,
FALSE, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_ROMAN,
"Times New Roman");
CFont* pOldFont = (CFont*) pDC->SelectObject(&font);
pDC->GetTextMetrics(&tm);
nHeight = tm.tmHeight + tm.tmExternalLeading;
TRACE("font height = %d, internal leading = %d\n",
nHeight, tm.tmInternalLeading);
j = pDoc->m_stringArray.GetSize();
for (i = 0; i < j; i++) {
pDC->TextOut(720, -i * nHeight - 720,
pDoc->m_stringArray[i]);
}
pDC->SelectObject(pOldFont);
TRACE("LOGPIXELSX = %d, LOGPIXELSY = %d\n",
pDC->GetDeviceCaps(LOGPIXELSX),
pDC->GetDeviceCaps(LOGPIXELSY));
TRACE("HORZSIZE = %d, VERTSIZE = %d\n",
pDC->GetDeviceCaps(HORZSIZE),
pDC->GetDeviceCaps(VERTSIZE));
}
Edit the OnPreparePrinting function in

BOOL CStringView::OnPreparePrinting(CPrintInfo* pInfo)
{
pInfo->SetMaxPage(1);
return DoPreparePrinting(pInfo);
}
Edit the constructor in

CStringView::CStringView() : m_rectPrint(0, 0, 11520, -21600)
{
}
Build and test the application. If you run the Ex17a application under Windows NT, Window 2000, or Windows XP with the lowest screen resolution, your MDI child window will look like the one shown here. (The text will be larger with higher resolutions.)

The window text is too small, isn't it? Go ahead and choose Print Preview from the File menu, and then click twice with the magnifying glass to enlarge the image. The Print Preview output is shown here:

Remember logical twips from Chapter 6? We'll now use logical twips to enlarge type on the display while keeping the printed text the same size. This requires some extra work because the CScrollView class wasn't designed for nonstandard mapping modes. We'll change the view's base class from CScrollView to CLogScrollView, which is a class that borrows from the MFC code in ViewScrl.cpp. The files


Insert the CLogScrollView class into the project. Copy the files


Edit the

#include "LogScrollView.h"
Then change the line
class CStringView : public CScrollView
to
class CStringView : public CLogScrollView
Edit the

void CStringView::OnInitialUpdate()
{
CLogScrollView::OnInitialUpdate();
CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height());
SetLogScrollSizes(sizeTotal);
}
Build and test the application again. Now the screen should look like this:

Reading the Printer Rectangle
The Ex17a program prints in a fixed-size rectangle that's appropriate for a laser printer set to portrait mode with 8.5-by-11-inch (letter-size) paper. But what if you load European-size paper or switch to landscape mode? The program should be able to adjust accordingly.It's relatively easy to read the printer rectangle. Remember the CPrintInfo pointer that's passed to OnPrint? That structure has a data member m_rectDraw that contains the rectangle in logical coordinates. Your overridden OnPrint function simply stuffs the rectangle in a view data member, and OnDraw uses it. There's only one problem: You can't get the rectangle until you start printing, so the constructor still needs to set a default value for OnDraw to use before printing begins.If you want the Ex17a program to read the printer rectangle and adjust the size of the scroll view, use Class View's Properties window to override OnPrint and then code the function as follows:
void CStringView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
m_rectPrint = pInfo->m_rectDraw;
SetLogScrollSizes(CSize(m_rectPrint.Width(),
-m_rectPrint.Height()));
CLogScrollView::OnPrint(pDC, pInfo);
}