www.pudn.com > uoth_src.zip > PrintOutput.cpp
//-----------------------------------------------------------------------------
//
// @doc
//
// @module PrintOutput.cpp - Print support |
//
// This module contains the definition of the print support
//
// Copyright (c) 2002 - Descartes Systems Sciences, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Neither the name of Descartes Systems Sciences, Inc nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// @end
//
// $History: PrintOutput.cpp $
//
//-----------------------------------------------------------------------------
#include "StdAfx.h"
#include "PrintOutput.h"
#include "PrintingDlg.h"
#include "Treasure.h"
#include "Region.h"
#include "uoth.h"
#include "PrintDlg.h"
#include "Filter.h"
#include "Scroll.h"
//
// Debug NEW
//
#if defined (_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//
// Externals
//
extern TCHAR g_szAppName [];
extern CPrinter g_sPrinter;
extern CDevMode g_sDevMode;
extern CSize g_sizeSmallMap;
extern CSize g_sizeLargeMap;
extern CRect g_rectPrintMargins;
extern CFilter g_sFilter;
//
// Printing abort processor
//
BOOL CALLBACK PrintingAbortProc (HDC, int)
{
MSG msg;
while (!CPrintingDlg::gm_fPrintAborted &&
PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return !CPrintingDlg::gm_fPrintAborted;
}
//-----------------------------------------------------------------------------
//
// @mfunc constructor.
//
// @rdesc None.
//
//-----------------------------------------------------------------------------
CPrintOutput::CPrintOutput ()
{
//
// Perform basic initialization
//
m_hdc = NULL;
m_nCurPage = 0;
m_tmHeight = 0;
m_tmAveCharWidth = 0;
m_fError = false;
m_nPageUsed = INT_MAX;
return;
}
//-----------------------------------------------------------------------------
//
// @mfunc Destructor.
//
// @rdesc None.
//
//-----------------------------------------------------------------------------
CPrintOutput::~CPrintOutput ()
{
return;
}
//-----------------------------------------------------------------------------
//
// @mfunc Print a centered string
//
// @parm LPCTSTR | pszString | String to print
//
// @parm int | y | Y position on page
//
// @rdesc None.
//
//-----------------------------------------------------------------------------
void CPrintOutput::PrintCentered (LPCTSTR pszString, int y)
{
//
// Get the length of the string
//
int nLength = (int) _tcslen (pszString);
//
// Get the text size
//
CSize sizeText;
::GetTextExtentPoint32 (m_hdc, pszString, nLength, &sizeText);
//
// Compute the x position
//
int x = (m_rectPage .Width () - sizeText .cx) / 2 + m_rectPage .left;
//
// Print string
//
::TextOut (m_hdc, x, y, pszString, nLength);
}
//-----------------------------------------------------------------------------
//
// @mfunc Print a page header
//
// @rdesc None.
//
//-----------------------------------------------------------------------------
void CPrintOutput::PrintPageHeader ()
{
USES_CONVERSION;
TCHAR szBuffer [256];
//
// Generate the title
//
PrintCentered (g_szAppName, m_rectPage .top);
//
// Format the second line
//
::GetDateFormat (LOCALE_USER_DEFAULT, DATE_SHORTDATE,
&m_stStart, NULL, szBuffer, _countof (szBuffer));
LPTSTR p = szBuffer + _tcslen (szBuffer);
*p++ = ' ';
::GetTimeFormat (LOCALE_USER_DEFAULT, 0, &m_stStart, NULL,
p, _countof (szBuffer) - (p - szBuffer));
PrintCentered (szBuffer, m_rectPage .top + m_tmHeight * 2);
}
//-----------------------------------------------------------------------------
//
// @mfunc Print a page footer
//
// @rdesc None.
//
//-----------------------------------------------------------------------------
void CPrintOutput::PrintPageFooter ()
{
//
// Format the page number at the bottom
//
CString str;
str .Format (IDS_PRINT_PAGENUM, m_nCurPage);
PrintCentered (str, m_rectPage .bottom - m_tmHeight);
}
//-----------------------------------------------------------------------------
//
// @mfunc Print configuration
//
// @parm HWND | hWnd | Window requesting print
//
// @rdesc None
//
//-----------------------------------------------------------------------------
void CPrintOutput::Print (HWND hWnd)
{
bool fDrawImages = true;
bool fDrawImageBackground = false;
bool fDrawSimpleMap = true;
ScrollBorder nScrollBorder;
bool fImageHalfSize = true;
CString str;
//
// Create a temp filter
//
CFilter sFilter;
sFilter = g_sFilter;
//
// Display the print dialog
//
CPrintDlg dlg (&sFilter);
if (dlg .DoModal (hWnd) != IDOK)
return;
//
// Update the filter
//
sFilter .Update ();
//
// Set the flags
//
switch (dlg .m_nMapImage)
{
case CPrintDlg::NoImage:
fDrawImages = false;
fDrawImageBackground = false;
fDrawSimpleMap = false;
fImageHalfSize = false;
break;
case CPrintDlg::BW_Small:
fDrawImages = true;
fDrawImageBackground = false;
fDrawSimpleMap = true;
fImageHalfSize = true;
break;
case CPrintDlg::BW_Large:
fDrawImages = true;
fDrawImageBackground = false;
fDrawSimpleMap = true;
fImageHalfSize = false;
break;
case CPrintDlg::Color_Small:
fDrawImages = true;
fDrawImageBackground = true;
fDrawSimpleMap = false;
fImageHalfSize = true;
break;
case CPrintDlg::Color_Large:
fDrawImages = true;
fDrawImageBackground = true;
fDrawSimpleMap = false;
fImageHalfSize = false;
break;
default:
assert (false);
return;
}
//
// Get the current time
//
::GetLocalTime (&m_stStart);
//
// Open the printer
//
m_hdc = g_sPrinter .CreatePrinterDC (g_sDevMode .m_pDevMode);
if (m_hdc == NULL)
return;
//
// Get the device name
//
CString strDeviceName;
CString strPortName;
{
CPrinterInfo <5> pinfo5;
CPrinterInfo <2> pinfo2;
//Some printers fail for PRINTER_INFO_5 in some situations
if (pinfo5 .GetPrinterInfo (g_sPrinter))
{
strDeviceName = pinfo5 .m_pi ->pPrinterName;
strPortName = pinfo5 .m_pi ->pPortName;
}
else if (pinfo2 .GetPrinterInfo (g_sPrinter))
{
strDeviceName = pinfo2 .m_pi ->pPrinterName;
strPortName = pinfo2 .m_pi ->pPortName;
}
}
//
// Get the border style
//
nScrollBorder = fDrawSimpleMap ? ScrollBorder_Thin : ScrollBorder_Thick;
//
// Get the rect for the image including the scroll size
//
CRect rectImage (CPoint (0, 0), dlg .m_nContent == CPrintDlg::Treasures
? g_sizeSmallMap : g_sizeLargeMap );
CScroll::DeflateRect (ScrollBorder_Thick, &rectImage);
CScroll::InflateRect (nScrollBorder, &rectImage);
rectImage .OffsetRect (- rectImage .left, - rectImage .top);
CScroll scroll (nScrollBorder, fDrawImageBackground,
RGB (255, 255, 255), rectImage);
//
// Create the temp destination bitmap
//
BITMAPINFOHEADER BMI;
BMI .biSize = sizeof (BITMAPINFOHEADER);
BMI .biWidth = rectImage .Width ();
BMI .biHeight = rectImage .Height ();
BMI .biPlanes = 1;
BMI .biBitCount = 32;
BMI .biCompression = BI_RGB;
BMI .biSizeImage = 0;
BMI .biXPelsPerMeter = 0;
BMI .biYPelsPerMeter = 0;
BMI .biClrUsed = 0;
BMI .biClrImportant = 0;
BYTE *pDstBits;
CBitmap bmImage;
bmImage = ::CreateDIBSection (m_hdc, (BITMAPINFO *) &BMI,
DIB_RGB_COLORS, (void **) &pDstBits, 0, 0l);
CDC dcImage;
dcImage .CreateCompatibleDC (NULL);
HBITMAP hbmOld = dcImage .SelectBitmap (bmImage);
//
// Pixel size
//
CSize sizePixel (
::GetDeviceCaps (m_hdc, LOGPIXELSX),
::GetDeviceCaps (m_hdc, LOGPIXELSY)
);
CSize sizeImagePrint (
MulDiv (rectImage .Width (), sizePixel .cx, fImageHalfSize ? 200 : 100),
MulDiv (rectImage .Height (), sizePixel .cy, fImageHalfSize ? 200 : 100)
);
//
// Get the metrics of the dc
//
TEXTMETRIC tm;
::GetTextMetrics (m_hdc, &tm);
m_tmHeight = tm .tmHeight;
m_tmAveCharWidth = tm .tmAveCharWidth;
//
// Get the page drawing rect
//
GetDrawingArea (m_hdc, &g_rectPrintMargins, &m_rectPage);
//
// Compute draw size from page size
//
int nLines = (m_rectPage .Height ()) / m_tmHeight;
m_rectDraw .left = m_rectPage .left;
m_rectDraw .top = m_rectPage .top + m_tmHeight * 4;
m_rectDraw .right = m_rectPage .right;
m_rectDraw .bottom = m_rectDraw .top + m_tmHeight * (nLines - 6);
//
// Create the document information block (since we don't
// support the selection of a printer at print time, then
// we don't need to support printing to file.)
//
DOCINFO docInfo;
memset (&docInfo, 0, sizeof (DOCINFO));
docInfo .cbSize = sizeof (DOCINFO);
docInfo .lpszDocName = g_szAppName;
docInfo .lpszOutput = NULL;
//
// Setup the printing DC
//
::SetAbortProc (m_hdc, PrintingAbortProc);
//
// Disable main window while printing
//
::EnableWindow (hWnd, FALSE);
//
// Initialize the contents of the dialog box
//
CPrintingDlg dlgPrintStatus;
dlgPrintStatus .Create (hWnd);
str .Format (IDS_PRINT_ON, (LPCTSTR) strPortName);
dlgPrintStatus .SetWindowText (g_szAppName);
dlgPrintStatus .SetDlgItemText (IDC_PRINT_PRINTERNAME, strDeviceName);
dlgPrintStatus .SetDlgItemText (IDC_PRINT_PORTNAME, str);
dlgPrintStatus .SetDlgItemText (IDC_PRINT_APPNAME, g_szAppName);
dlgPrintStatus .ShowWindow (SW_SHOW);
dlgPrintStatus .UpdateWindow ();
//
// Start document printing process, if failed
//
if (::StartDoc (m_hdc, &docInfo) == SP_ERROR)
{
//
// Enable main window before proceeding
//
::EnableWindow (hWnd, TRUE);
//
// Cleanup and show error message
//
dlgPrintStatus .DestroyWindow();
str .LoadString (IDS_ERR_PRINT_START);
::MessageBox (hWnd, str, g_szAppName, MB_OK);
::DeleteDC (m_hdc);
dcImage .SelectBitmap (hbmOld);
return;
}
//
// Allocate space for the treasure draw array
//
int *panTreasures = (int *) alloca (
CTreasure::gm_vTreasures .GetCount () * sizeof (int));
//
// Loop through the objects
//
m_nPageUsed = INT_MAX;
m_nCurPage = 0;
m_fError = false;
bool fContinue = true;
for (int nIndex = 0;; ++nIndex)
{
//
// Locals
//
CString str;
//
// If we are doing treasures
//
if (dlg .m_nContent == CPrintDlg::Treasures)
{
//
// Validate the treasure
//
if (nIndex >= CTreasure::gm_vTreasures .GetCount ())
break;
CTreasure *pTreasure = &CTreasure::gm_vTreasures [nIndex];
if (!pTreasure ->IsValid ())
continue;
//
// Get the treasure data
//
if (sFilter .IsMineFilterEnabled () &&
!sFilter .DoesTreasureHaveMap (pTreasure ->m_nIndex))
continue;
str = pTreasure ->GetInformation (dlg .m_fTerse);
//
// If we are drawing images, then draw
//
if (fDrawImages)
{
//
// Get the map rect
//
CRect rectMap;
CPoint pt (pTreasure ->m_x, pTreasure ->m_y);
scroll .GetMapAt (pt, &rectMap);
//
// Draw the map
//
scroll .DrawMap (dcImage, rectMap, &pTreasure ->m_nIndex,
1, false, &sFilter, fDrawSimpleMap);
}
}
//
// Otherwise, this is a region
//
else
{
//
// Validate the region
//
if (nIndex >= CRegion::gm_vRegions .GetCount ())
break;
CRegion *pRegion = &CRegion::gm_vRegions [nIndex];
if (!pRegion ->IsValid ())
continue;
//
// Get the treasure data
//
if (sFilter .IsMineFilterEnabled () &&
!sFilter .DoesRegionHaveMap (pRegion ->m_nIndex))
continue;
str = pRegion ->GetInformation (dlg .m_fTerse);
//
// If we are drawing images, then draw
//
if (fDrawImages)
{
//
// Get the map rect
//
int nTreasures;
CRect rectMap;
scroll .GetRegionMap (nIndex, &nTreasures,
panTreasures, &rectMap);
//
// Draw the map
//
scroll .DrawMap (dcImage, rectMap, panTreasures,
nTreasures, true, &sFilter, fDrawSimpleMap);
}
}
//
// Write current page
//
{
CString strPage;
strPage .Format (IDS_PRINT_PAGENUM, m_nCurPage < 1 ? 1 : m_nCurPage);
dlgPrintStatus .SetDlgItemText (IDC_PRINT_PAGENUM, strPage);
}
//
// Compute the length of a section
//
CRect rectAll;
CRect rectText (m_rectDraw);
if (fDrawImages)
rectText .right -= sizeImagePrint .cx + m_tmAveCharWidth * 4;
::DrawText (m_hdc, str, str .GetLength (),
&rectText, DT_CALCRECT | DT_WORDBREAK);
//
// Compute the size of the while thing including the optional image
//
rectAll = rectText;
if (fDrawImages)
{
if (rectAll .Height () < sizeImagePrint .cy + m_tmHeight)
rectAll .bottom = rectAll .top + sizeImagePrint .cy + m_tmHeight;
}
//
// If there isn't enough room on the page, start a new page
//
if (m_rectDraw .Height () - m_nPageUsed < rectAll .Height ())
{
//
// End the old page
//
if (m_nCurPage > 0)
{
PrintPageFooter ();
if (::EndPage (m_hdc) < 0)
{
m_fError = true;
break;
}
}
//
// Start new page
//
if (::StartPage (m_hdc) < 0)
{
m_fError = true;
break;
}
//
// Generate header and footer
//
PrintPageHeader ();
//
// Reset values
//
m_nPageUsed = 0;
m_nCurPage++;
}
//
// Draw the rect
//
CRect rectText2 (rectText);
rectText2 .OffsetRect (0, m_nPageUsed);
::DrawText (m_hdc, str, str .GetLength (),
&rectText2, DT_WORDBREAK);
//
// Draw the image
//
if (fDrawImages)
{
//
// Compute the rectangle
//
CRect rectImageOut;
rectImageOut .left = m_rectDraw .right - sizeImagePrint .cx;
rectImageOut .top = m_rectDraw .top + m_nPageUsed;
rectImageOut .right = m_rectDraw .right;
rectImageOut .bottom = rectImageOut .top + sizeImagePrint .cy;
//
// Blt the image
//
::StretchBlt (m_hdc, rectImageOut .left, rectImageOut .top,
rectImageOut .Width (), rectImageOut .Height (), dcImage,
0, 0, rectImage .Width (), rectImage .Height (),
SRCCOPY);
}
//
// Adjust the amount of the page used
//
m_nPageUsed += rectAll .Height ();
//
// Test for user termination
//
if (!PrintingAbortProc (m_hdc, 0))
m_fError = true;
}
//
// End the page if need be
//
if (!m_fError && m_nCurPage > 0 && m_nPageUsed > 0)
{
PrintPageFooter ();
if (::EndPage (m_hdc) < 0)
m_fError = true;
}
//
// Finish the document printing process
//
if (!m_fError)
::EndDoc (m_hdc);
else
::AbortDoc (m_hdc);
//
// Enable the main window
//
::EnableWindow (hWnd, TRUE);
//
// Destroy the printing dialog
//
dlgPrintStatus .DestroyWindow ();
//
// Delete the DC
//
::DeleteDC (m_hdc);
dcImage .SelectBitmap (hbmOld);
}
//-----------------------------------------------------------------------------
//
// @mfunc Get the print rect based on the given margins
//
// @parm HDC | hDC | Printer DC
//
// @parm const RECT * | prectMargins | Margins in 1000th of an inch.
//
// @parm RECT * | pRect | Drawing area
//
// @rdesc None
//
//-----------------------------------------------------------------------------
void CPrintOutput::GetDrawingArea (HDC hDC,
const RECT *prectMargins, RECT *pRect)
{
//
// Get the physical offset
//
CPoint ptPhysicalOffset (
::GetDeviceCaps (hDC, PHYSICALOFFSETX),
::GetDeviceCaps (hDC, PHYSICALOFFSETY)
);
//
// Get the physical page size
//
CSize sizePhysical (
::GetDeviceCaps (hDC, PHYSICALWIDTH),
::GetDeviceCaps (hDC, PHYSICALHEIGHT)
);
//
// Get the drawing size
//
CSize sizeDrawing (
::GetDeviceCaps (hDC, HORZRES),
::GetDeviceCaps (hDC, VERTRES)
);
//
// Pixel size
//
CSize sizePixel (
::GetDeviceCaps (hDC, LOGPIXELSX),
::GetDeviceCaps (hDC, LOGPIXELSY)
);
//
// Compute the actual drawing rect
//
CRect rectDraw (ptPhysicalOffset, sizeDrawing);
//
// Compute the physical page rect
//
CRect rectPhysical (CPoint (0, 0), sizePhysical);
//
// Compute the margins in pixels
//
int nLeftMargin = ::MulDiv (sizePixel .cx, prectMargins ->left, 1000);
int nTopMargin = ::MulDiv (sizePixel .cy, prectMargins ->top, 1000);
int nRightMargin = ::MulDiv (sizePixel .cx, prectMargins ->right, 1000);
int nBottomMargin = ::MulDiv (sizePixel .cy, prectMargins ->bottom, 1000);
//
// Adjust the physical rect by the margins
//
pRect ->left = rectPhysical .left + nLeftMargin;
pRect ->top = rectPhysical .top + nTopMargin;
pRect ->right = rectPhysical .right - nRightMargin;
pRect ->bottom = rectPhysical .bottom - nBottomMargin;
//
// Make sure the rect is inside the drawing rect
//
if (pRect ->left < rectDraw .left)
pRect ->left = rectDraw .left;
if (pRect ->top < rectDraw .top)
pRect ->top = rectDraw .top;
if (pRect ->right > rectDraw .right)
pRect ->right = rectDraw .right;
if (pRect ->bottom > rectDraw .bottom)
pRect ->bottom = rectDraw .bottom;
//
// Adjust the rect (remember, the REAL drawing rect is 0,0 based
// on the drawing area and NOT the physical area. Our computations
// are all based on the phyical area.)
//
::OffsetRect (pRect, - ptPhysicalOffset .x, - ptPhysicalOffset .y);
return;
}