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; later we'll base the drawing rectangle on the printable area rectangle supplied by the printer driver.
Here are the steps for building the example:
Note that this is an MDI application.
public: CStringArray m_stringArray;
The document data is stored in a string array. The MFC library CStringArray class holds an array of CString objects, accessible by a zero-based subscript. You need not set a maximum dimension in the declaration because the array is dynamic.
private: CRect m_rectPrint;
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; }
The CStringArray class supports dynamic arrays, but here we're using the m_stringArray object as though 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 illustrates 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); }
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); }
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)); }
BOOL CStringView::OnPreparePrinting(CPrintInfo* pInfo) { pInfo->SetMaxPage(1); return DoPreparePrinting(pInfo); }
CStringView::CStringView() : m_rectPrint(0, 0, 11520, -15120) { }
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 illustrated here.
Remember "logical twips" from Chapter 5? We're going to use logical twips now 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. You will be changing the view's base class from CScrollView to CLogScrollView, which is a class that I created by copying and modifying the MFC code in ViewScrl.cpp. The files LogScrollView.h and LogScrollView.cpp are in the \vcpp32\ex19a directory on the companion CD-ROM.
#include "LogScrollView.h"
Then change the line
class CStringView : public CScrollView
to
class CStringView : public CLogScrollView
void CStringView::OnInitialUpdate() { CLogScrollView::OnInitialUpdate(); CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height()); SetLogScrollSizes(sizeTotal); }
The EX19A 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 you 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 EX19A program to read the printer rectangle and adjust the size of the scroll view, use ClassWizard 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); }