// Print.cpp - Functions to handle MSInfo printing of the text report. // // Copyright (c) 1998-1999 Microsoft Corporation #include "stdafx.h" #include "DataSrc.h" #include "Resource.h" #include "DataObj.h" #include "CompData.h" #include #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /* * PreparePrintDialog - Do all initialization required before the print * dialog is displayed, most notably calculating the page count. * * History: a-jsari 3/17/98 Initial version */ BOOL CDataSource::RefreshPrintData(CPrintDialog * pdlgPrint, CFolder * pfolSelection) { // FIX: Make calculation of line length device independent. LPTSTR szLine; int cLength = 84; int cLineCount = 57; unsigned short cPage = 0; CFolder * pPrintRoot = NULL; if (pdlgPrint->PrintSelection() && pfolSelection) pPrintRoot = pfolSelection; else pPrintRoot = GetRootNode(); if (m_pPrintContent != NULL) delete m_pPrintContent; m_pPrintContent = new CMSInfoMemoryFile(); ASSERT(m_pPrintContent != NULL); if (m_pPrintContent == NULL) ::AfxThrowMemoryException(); if (m_pPrintInfo != NULL) { // Set this pointer to NULL so it doesn't delete the // pointer we're storing internally. m_pPrintInfo->m_pPD = NULL; // This prevents the delete below from asserting. m_pPrintInfo->m_strPageDesc = _T(""); delete m_pPrintInfo; m_pPrintInfo = NULL; } m_pPrintInfo = new CPrintInfo; ASSERT(m_pPrintInfo != NULL); if (m_pPrintInfo == NULL) { delete m_pPrintContent; m_pPrintContent = NULL; ::AfxThrowMemoryException(); } m_pPrintInfo->m_nCurPage = 0; m_pPrintInfo->m_pPD = pdlgPrint; if (FAILED(WriteOutput(m_pPrintContent, pPrintRoot))) return FALSE; m_pPrintContent->SeekToBegin(); szLine = new TCHAR[cLength + 1]; m_fEndOfFile = FALSE; while (!m_fEndOfFile) { int iLine = cLineCount; ++cPage; while (iLine--) { GetLine(szLine, cLength); if (m_fEndOfFile) goto WhileEnd; } } WhileEnd: delete [] szLine; pdlgPrint->m_pd.nMaxPage = cPage; if (pdlgPrint->m_pd.nToPage > pdlgPrint->m_pd.nMaxPage) pdlgPrint->m_pd.nToPage = pdlgPrint->m_pd.nMaxPage; return TRUE; } /* * PrintReport - Send the report data to the printer. * * History: a-jsari 12/9/97 Initial version. */ BOOL CDataSource::PrintReport(CPrintDialog *pdlgPrint, CFolder *pfolSelection) { BOOL fReturn; do { int nResult = BeginPrinting(pdlgPrint, pfolSelection); if (nResult < 0) break; m_pPrintContent->SeekToBegin(); while (m_pPrintInfo->m_bContinuePrinting) PrintPage(pdlgPrint); EndPrinting(); fReturn = TRUE; } while (FALSE); delete m_pPrintContent; m_pPrintContent = NULL; delete m_pPrintInfo; m_pPrintInfo = NULL; return fReturn; } /* * BeginPrinting - Do the initialization required to print the file. * * History: a-jsari 12/30/97 Thieved from msishell's msiview.cpp, * modified to allow page ranges. */ int CDataSource::BeginPrinting(CPrintDialog *pdlgPrint, CFolder *pfolSelection) { ASSERT(m_pPrintContent != NULL); if (m_pPrintContent == NULL) { m_fEndOfFile = TRUE; m_pPrintInfo->m_bContinuePrinting = FALSE; ::AfxThrowMemoryException(); } if (m_pDC != NULL) { delete m_pDC; m_pDC = NULL; } m_pDC = new CDC; ASSERT(m_pDC != NULL); if (m_pDC == NULL) ::AfxThrowMemoryException(); if (m_pprinterFont != NULL) { delete m_pprinterFont; m_pprinterFont = NULL; } m_pprinterFont = new CFont; ASSERT(m_pprinterFont != NULL); if (m_pprinterFont == NULL) ::AfxThrowMemoryException(); #if 0 m_strHeaderLeft = pScope->MachineName(); #endif CString strFormat; COleDateTime datetime; strFormat.LoadString(IDS_PRINT_HDR_RIGHT_CURRENT); datetime = COleDateTime::GetCurrentTime(); m_strHeaderRight = datetime.Format(strFormat); m_strFooterCenter.LoadString(IDS_PRINT_FTR_CTR); // Reset the end of file member. m_fEndOfFile = FALSE; // Create the font for printing. Read font information from string // resources, to allow the localizers to control what font is // used for printing. Set the variables for the default font to use. int nHeight = 10; int nWeight = FW_NORMAL; BYTE nCharSet = DEFAULT_CHARSET; BYTE nPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; CString strFace = "Courier New"; // Load string resources to see if we should use other values // than the defaults. CString strHeight, strWeight, strCharSet, strPitchAndFamily, strFaceName; strHeight.LoadString(IDS_PRINT_FONT_HEIGHT); strWeight.LoadString(IDS_PRINT_FONT_WEIGHT); strCharSet.LoadString(IDS_PRINT_FONT_CHARSET); strPitchAndFamily.LoadString(IDS_PRINT_FONT_PITCHANDFAMILY); strFaceName.LoadString(IDS_PRINT_FONT_FACENAME); if (!strHeight.IsEmpty() && ::_ttol(strHeight)) nHeight = ::_ttoi(strHeight); if (!strWeight.IsEmpty()) nWeight = ::_ttoi(strWeight); if (!strCharSet.IsEmpty()) nCharSet = (BYTE) ::_ttoi(strCharSet); if (!strPitchAndFamily.IsEmpty()) nPitchAndFamily = (BYTE) ::_ttoi(strPitchAndFamily); strFaceName.TrimLeft(); if (!strFaceName.IsEmpty() && strFaceName != CString("facename")) strFace = strFaceName; CString strDriver = pdlgPrint->GetDriverName(); CString strDevice = pdlgPrint->GetDeviceName(); CString strPort = pdlgPrint->GetPortName(); LPDEVMODE pdevMode = pdlgPrint->GetDevMode(); VERIFY(m_pDC->CreateDC(strDriver, strDevice, strPort, pdevMode)); // Convert the height from points to a value specific to the printer. nHeight = -((m_pDC->GetDeviceCaps (LOGPIXELSY) * nHeight) / 72); // Create the font object. VERIFY(m_pprinterFont->CreateFont(nHeight, 0, 0, 0, nWeight, 0, 0, 0, nCharSet, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, nPitchAndFamily, strFace)); m_pPrintInfo->m_rectDraw.SetRect(0, 0, m_pDC->GetDeviceCaps(HORZRES), m_pDC->GetDeviceCaps(VERTRES)); m_pDC->DPtoLP(&m_pPrintInfo->m_rectDraw); m_pPrintInfo->m_bContinuePrinting = TRUE; CString strDocName; CString strOutput; DOCINFO diJob; diJob.cbSize = sizeof(DOCINFO); diJob.lpszDocName = strDocName; diJob.lpszOutput = strOutput; return m_pDC->StartDoc(&diJob); } /* * OnEndPrinting - Clean up anything we allocated for the print job. Specifically, * delete the CMemFile holding the content. * * History: a-jsari 12/30/97 Thieved from MSIShell's msiview.cpp */ void CDataSource::EndPrinting() { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); if (m_pPrintContent != NULL) { delete m_pPrintContent; m_pPrintContent = NULL; } int nResult = m_pDC->EndDoc(); ASSERT(nResult >= 0); if (nResult < 0) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CString strError, strTitle; switch(nResult) { case SP_OUTOFDISK: VERIFY(strError.LoadString(IDS_PRINT_NODISK)); break; case SP_OUTOFMEMORY: VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY)); break; case SP_USERABORT: VERIFY(strError.LoadString(IDS_PRINT_USERABORTED)); break; case SP_ERROR: default: VERIFY(strError.LoadString(IDS_PRINT_GENERIC)); break; } strTitle.LoadString(IDS_DESCRIPTION); ::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK); } VERIFY(m_pprinterFont->DeleteObject()); VERIFY(m_pDC->DeleteDC()); } //--------------------------------------------------------------------------- // In this function, we use two rectangles: rectOuter is what we consider // the printable area of the page, and includes headers and footers. // rectInner is the rectange we actually print the content in (doesn't // include headers or footers). // // TBD: add header and footer // TBD: add error checking // TBD: fix intelegence issues and speed this up // TBD: insure that 80 columns will always fit //--------------------------------------------------------------------------- #define TOP_MARGIN 2540/2 // HIMETRIC for 1/2 inch #define BOTTOM_MARGIN 2540/2 // HIMETRIC for 1/2 inch #define LEFT_MARGIN 2540/2 // HIMETRIC for 1/2 inch #define RIGHT_MARGIN 2540/2 // HIMETRIC for 1/2 inch /* * GetLine - Copy a line of data from m_pPrintContent into szLineBuffer. * * History: a-jsari 3/16/98 Initial version. */ void CDataSource::GetLine(LPTSTR szLineBuffer, int cSize) { for (int i = 0; i < cSize; i++) { try { m_pPrintContent->ReadTchar(szLineBuffer[i]); } catch (...) { m_pPrintInfo->m_bContinuePrinting = FALSE; m_fEndOfFile = TRUE; szLineBuffer[i] = '\0'; break; } if (szLineBuffer[i] == '\t') { // Convert tabs into spaces for printing. szLineBuffer[i] = ' '; } else if (szLineBuffer[i] == '\r') { i--; } else if (szLineBuffer[i] == '\n') { szLineBuffer[i] = '\0'; break; } } } /* * GetTextSize - Get the sizes of the text. * * History: a-jsari 3/16/98 Initial version. */ void CDataSource::GetTextSize(int &cLineLength, int &cCharHeight, CRect &rectOuter, CRect &rectText) { TEXTMETRIC tm; m_pDC->GetTextMetrics(&tm); cCharHeight = tm.tmHeight + tm.tmExternalLeading; // Take the actual print area (in pInfo) and adjust it for the // header and footer. rectOuter = m_pPrintInfo->m_rectDraw; CSize sizeLeftTop(LEFT_MARGIN, TOP_MARGIN); CSize sizeRightBottom(RIGHT_MARGIN, BOTTOM_MARGIN); m_pDC->HIMETRICtoDP(&sizeLeftTop); m_pDC->HIMETRICtoDP(&sizeRightBottom); rectOuter.DeflateRect(sizeLeftTop.cx, sizeLeftTop.cy, sizeRightBottom.cx, sizeRightBottom.cy); rectText = rectOuter; rectText.DeflateRect(0, cCharHeight * 4); // Get the number of characters which will fit on a line (use the average, because this // might not be a monospaced font). Note: it's possible that text could be cut off // if a line consisted of very wide characters. This is unlikely because the font // used SHOULD be monospace (for alignment of text, etc.). cLineLength = rectText.Width() / tm.tmAveCharWidth; } /* * PrintPage - Write one page to the printer Device Context. * * History: a-jsari 12/30/97 Thieved from msishell's msiview.cpp. */ void CDataSource::PrintPage(CPrintDialog *pdlgPrint) { int cLineLength; int cy = 0, cyCharHeight; LPTSTR szLineBuffer; CRect rectOuter, rectInner; ASSERT(m_pPrintContent != NULL); if (m_pPrintContent == NULL) { m_fEndOfFile = TRUE; m_pPrintInfo->m_bContinuePrinting = FALSE; return; } // Select the font used for printing. CGdiObject* pOldFont = m_pDC->SelectObject(m_pprinterFont); // this makes a small font: CGdiObject *pOldFont = pDC->SelectStockObject(ANSI_FIXED_FONT); // Compute the height of each character (we'll need the text metric). GetTextSize(cLineLength, cyCharHeight, rectOuter, rectInner); ++m_pPrintInfo->m_nCurPage; if (!(pdlgPrint->PrintAll() || pdlgPrint->PrintSelection()) && m_pPrintInfo->m_nCurPage > m_pPrintInfo->GetToPage()) { m_pPrintInfo->m_bContinuePrinting = FALSE; m_pDC->SelectObject(pOldFont); return; } // Allocate the buffer of character to hold a single line of the print out. szLineBuffer = new TCHAR[cLineLength + 1]; szLineBuffer[cLineLength] = (TCHAR)'\0'; if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection() || m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage()) { m_pDC->StartPage(); // Draw the header. m_pDC->TextOut(rectOuter.left, rectOuter.top, m_strHeaderLeft); int cxHeaderRight = rectOuter.right - m_pDC->GetTextExtent(m_strHeaderRight).cx; m_pDC->TextOut(cxHeaderRight, rectOuter.top, m_strHeaderRight); } // Process the output a line at a time, until either we have emptied out // the memfile, or we have gotten to the bottom of this page. while (!m_fEndOfFile) { if (cy + cyCharHeight > rectInner.Height()) break; GetLine(szLineBuffer, cLineLength); if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection() || m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage()) m_pDC->TextOut(rectInner.left, rectInner.top + cy, szLineBuffer, ::_tcslen(szLineBuffer)); cy += cyCharHeight; } if (pdlgPrint->PrintAll() || pdlgPrint->PrintSelection() || m_pPrintInfo->m_nCurPage >= m_pPrintInfo->GetFromPage()) { // Draw the footer. CString strActualFooter; strActualFooter.Format(m_strFooterCenter, m_pPrintInfo->m_nCurPage); int cxFooterCenter = (rectOuter.Width() - m_pDC->GetTextExtent(strActualFooter).cx) / 2; m_pDC->TextOut(rectOuter.left + cxFooterCenter, rectOuter.bottom - cyCharHeight, strActualFooter); int nResult = m_pDC->EndPage(); ASSERT(nResult >= 0); } #if 0 if (nResult < 0) { AFX_MANAGE_STATE(::AfxGetStaticModuleState()); CString strError; switch (nResult) { case SP_APPABORT: VERIFY(strError.LoadString(IDS_PRINT_APPABORTED)); break; case SP_USERABORT: VERIFY(strError.LoadString(IDS_PRINT_USERABORTED)); break; case SP_OUTOFDISK: VERIFY(strError.LoadString(IDS_PRINT_NODISK)); break; case SP_OUTOFMEMORY: VERIFY(strError.LoadString(IDS_PRINT_NOMEMORY)); break; case SP_ERROR: default: VERIFY(strError.LoadString(IDS_PRINT_GENERIC)); break; } strTitle.LoadString(IDS_DESCRIPTION) ::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strError, strTitle, MB_OK); } // Clean up. #endif delete [] szLineBuffer; m_pDC->SelectObject(pOldFont); }