home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
mfc
/
src
/
dcprev.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-16
|
26KB
|
989 lines
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFX_PRINT_SEG
#pragma code_seg(AFX_PRINT_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
/////////////////////////////////////////////////////////////////////////////
// Helper functions
AFX_STATIC long AFXAPI _AfxMultMultDivDiv(
int factor, int num1, int num2,
int den1, int den2)
{
#ifdef _AFX_PORTABLE
// make sure that (num1 * num2) does not overflow 31-bits.
long temp = num1 < 0 ? -num1 : num1;
for (int nBitsResult = 0; temp != 0; nBitsResult++)
temp >>= 1;
temp = num2 < 0 ? -num2 : num2;
for (; temp != 0; nBitsResult++)
temp >>= 1;
if (nBitsResult > 31)
{
num1 >>= nBitsResult - 31;
num2 >>= nBitsResult - 31;
}
// make sure that (den1 * den2) does not overflow 31-bits
temp = den1 < 0 ? -den1 : den1;
for (nBitsResult = 0; temp != 0; nBitsResult++)
temp >>= 1;
temp = den2 < 0 ? -den2 : den2;
for (; temp != 0; nBitsResult++)
temp >>= 1;
if (nBitsResult > 31)
{
den1 >>= nBitsResult - 31;
den2 >>= nBitsResult - 31;
}
long numerator = (long)num1 * (long)num2; // no overflow
long denominator = (long)den1 * (long)den2; // no overflow
#else
__int64 numerator = (__int64)num1 * (__int64)num2; // no overflow
__int64 denominator = (__int64)den1 * (__int64)den2; // no overflow
__int64 temp;
#endif
temp = numerator < 0 ? -numerator : numerator;
for (int nBitsInNum = 0; temp != 0; nBitsInNum++)
temp >>= 1;
temp = factor < 0 ? -factor : factor;
for (int nBitsInFactor = 0; temp != 0; nBitsInFactor++)
temp >>= 1;
nBitsInNum += nBitsInFactor;
//
// normalizing the denominator to positive results in an easier
// determination of whether there is overflow
//
if (denominator < 0)
{
denominator = -denominator;
numerator = -numerator;
}
// Get the product of factor * numerator representable in a long/__int64
// while distributing loss of presision across all three numerator terms
// Adjust denominator as well
//
while (nBitsInNum-- > 31)
{
numerator >>= 1;
denominator >>= 1;
if (nBitsInNum-- <= 31)
break;
numerator >>= 1;
denominator >>= 1;
if (nBitsInNum-- <= 31)
break;
factor >>= 1;
denominator >>= 1;
}
numerator *= factor;
if (denominator == 0)
return numerator < 0 ? LONG_MIN : LONG_MAX;
return (long) ((numerator + denominator/2) / denominator);
}
/////////////////////////////////////////////////////////////////////////////
// Print Preview DC (CPreviewDC)
CPreviewDC::CPreviewDC()
{
// Initial scale factor and top-left offset
m_nScaleNum = m_nScaleDen = 1;
m_sizeTopLeft.cx = m_sizeTopLeft.cy = 8;
m_hFont = m_hPrinterFont = NULL;
}
void CPreviewDC::SetOutputDC(HDC hDC)
{
ASSERT(hDC != NULL);
m_nSaveDCIndex = ::SaveDC(hDC); // restore in ReleaseOutputDC()
CDC::SetOutputDC(hDC);
if (m_hAttribDC != NULL)
{
MirrorMappingMode(FALSE);
if (m_hFont)
::SelectObject(m_hDC, m_hFont);
else
MirrorFont();
MirrorAttributes();
}
}
void CPreviewDC::ReleaseOutputDC()
{
ASSERT(m_hDC != NULL);
::RestoreDC(m_hDC, m_nSaveDCIndex); // Saved in SetOutputDC()
CDC::ReleaseOutputDC();
}
void CPreviewDC::SetAttribDC(HDC hDC)
{
ASSERT(hDC != NULL);
CDC::SetAttribDC(hDC);
MirrorMappingMode(TRUE);
MirrorFont();
MirrorAttributes();
}
CPreviewDC::~CPreviewDC()
{
ASSERT(m_hDC == NULL); // Should not have a screen DC at this time
AfxDeleteObject((HGDIOBJ*)&m_hFont);
}
void CPreviewDC::SetScaleRatio(int nNumerator, int nDenominator)
{
m_nScaleNum = nNumerator;
m_nScaleDen = nDenominator;
if (m_hAttribDC != NULL)
{
MirrorMappingMode(TRUE);
MirrorFont();
}
}
// Implementation support
#ifdef _DEBUG
void CPreviewDC::AssertValid() const
{
CDC::AssertValid();
}
void CPreviewDC::Dump(CDumpContext& dc) const
{
CDC::Dump(dc);
dc << "Scale Factor: " << m_nScaleNum << "/" << m_nScaleDen;
dc << "\n";
}
#endif
int CPreviewDC::SaveDC()
{
ASSERT(m_hAttribDC != NULL);
int nAttribIndex = ::SaveDC(m_hAttribDC);
if (m_hDC != NULL)
{
// remove font from object
::SelectObject(m_hDC, GetStockObject(SYSTEM_FONT));
m_nSaveDCDelta = ::SaveDC(m_hDC) - nAttribIndex;
// Select font back in after save
::SelectObject(m_hDC, m_hFont);
}
else
{
m_nSaveDCDelta = 0x7fff; // impossibly high value
}
return nAttribIndex;
}
BOOL CPreviewDC::RestoreDC(int nSavedDC)
{
ASSERT(m_hAttribDC != NULL);
BOOL bSuccess = ::RestoreDC(m_hAttribDC, nSavedDC);
if (bSuccess)
{
if (m_nSaveDCDelta != 0x7fff)
{
ASSERT(m_hDC != NULL); // removed Output DC after save
if (nSavedDC != -1)
nSavedDC += m_nSaveDCDelta;
bSuccess = ::RestoreDC(m_hDC, nSavedDC);
MirrorFont(); // mirror the font
}
else
{
ASSERT(m_hDC == NULL); // Added the Output DC after save
}
}
return bSuccess;
}
void CPreviewDC::MirrorAttributes()
{
ASSERT(m_hAttribDC != NULL);
if (m_hDC != NULL)
{
// extract and re-set Pen and Brush
HGDIOBJ hTemp = ::SelectObject(m_hAttribDC, ::GetStockObject(BLACK_PEN));
::SelectObject(m_hAttribDC, hTemp);
::SelectObject(m_hDC, hTemp);
hTemp = ::SelectObject(m_hAttribDC, ::GetStockObject(BLACK_BRUSH));
::SelectObject(m_hAttribDC, hTemp);
::SelectObject(m_hDC, hTemp);
SetROP2(GetROP2());
SetBkMode(GetBkMode());
SetTextAlign(GetTextAlign());
SetPolyFillMode(GetPolyFillMode());
SetStretchBltMode(GetStretchBltMode());
SetTextColor(GetNearestColor(GetTextColor()));
SetBkColor(GetNearestColor(GetBkColor()));
}
}
CGdiObject* CPreviewDC::SelectStockObject(int nIndex)
{
ASSERT(m_hAttribDC != NULL);
HGDIOBJ hObj = ::GetStockObject(nIndex);
switch (nIndex)
{
case ANSI_FIXED_FONT:
case ANSI_VAR_FONT:
case DEVICE_DEFAULT_FONT:
case OEM_FIXED_FONT:
case SYSTEM_FONT:
case SYSTEM_FIXED_FONT:
case DEFAULT_GUI_FONT:
// Handle the stock fonts correctly
{
CGdiObject* pObject = CGdiObject::FromHandle(
::SelectObject(m_hAttribDC, hObj));
// Don't re-mirror screen font if this is the same font.
if (m_hPrinterFont == (HFONT) hObj)
return pObject;
m_hPrinterFont = (HFONT) hObj;
ASSERT(m_hPrinterFont != NULL); // Do not allow infinite recursion
MirrorFont();
return pObject;
}
default:
if (m_hDC != NULL)
::SelectObject(m_hDC, hObj);
return CGdiObject::FromHandle(::SelectObject(m_hAttribDC, hObj));
}
}
void CPreviewDC::MirrorFont()
{
if (m_hAttribDC == NULL)
return; // Can't do anything without Attrib DC
if (m_hPrinterFont == NULL)
{
SelectStockObject(DEVICE_DEFAULT_FONT); // will recurse
return;
}
if (m_hDC == NULL)
return; // can't mirror font without a screen DC
LOGFONT logFont;
// Fill the logFont structure with the original info
::GetObject(m_hPrinterFont, sizeof(LOGFONT), (LPVOID)&logFont);
TEXTMETRIC tm;
GetTextFace(LF_FACESIZE, (LPTSTR)&logFont.lfFaceName[0]);
GetTextMetrics(&tm);
// Set real values based on the Printer's text metrics.
if (tm.tmHeight < 0)
logFont.lfHeight = tm.tmHeight;
else
logFont.lfHeight = -(tm.tmHeight - tm.tmInternalLeading);
logFont.lfWidth = tm.tmAveCharWidth;
logFont.lfWeight = tm.tmWeight;
logFont.lfItalic = tm.tmItalic;
logFont.lfUnderline = tm.tmUnderlined;
logFont.lfStrikeOut = tm.tmStruckOut;
logFont.lfCharSet = tm.tmCharSet;
logFont.lfPitchAndFamily = tm.tmPitchAndFamily;
HFONT hNewFont = ::CreateFontIndirect(&logFont);
::SelectObject(m_hDC, hNewFont);
::GetTextMetrics(m_hDC, &tm);
// Is the displayed font too large?
int cyDesired = -logFont.lfHeight;
int cyActual;
if (tm.tmHeight < 0)
cyActual = -tm.tmHeight;
else
cyActual = tm.tmHeight - tm.tmInternalLeading;
CSize sizeWinExt;
VERIFY(::GetWindowExtEx(m_hDC, &sizeWinExt));
CSize sizeVpExt;
VERIFY(::GetViewportExtEx(m_hDC, &sizeVpExt));
// Only interested in Extent Magnitudes, not direction
if (sizeWinExt.cy < 0)
sizeWinExt.cy = -sizeWinExt.cy;
if (sizeVpExt.cy < 0)
sizeVpExt.cy = -sizeVpExt.cy;
// Convert to screen device coordinates to eliminate rounding
// errors as a source of SmallFont aliasing
cyDesired = MulDiv(cyDesired, sizeVpExt.cy, sizeWinExt.cy);
cyActual = MulDiv(cyActual, sizeVpExt.cy, sizeWinExt.cy);
ASSERT(cyDesired >= 0 && cyActual >= 0);
if (cyDesired < cyActual)
{
logFont.lfFaceName[0] = 0; // let the mapper find a good fit
if ((logFont.lfPitchAndFamily & 0xf0) == FF_DECORATIVE)
logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DECORATIVE;
else
logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
HFONT hTempFont = ::CreateFontIndirect(&logFont);
::SelectObject(m_hDC, hTempFont); // Select it in.
::DeleteObject(hNewFont);
hNewFont = hTempFont;
}
AfxDeleteObject((HGDIOBJ*)&m_hFont); // delete the old logical font
m_hFont = hNewFont; // save the new one
}
CFont* CPreviewDC::SelectObject(CFont* pFont)
{
if (pFont == NULL)
return NULL;
ASSERT(m_hAttribDC != NULL);
ASSERT_VALID(pFont);
CFont* pOldFont = (CFont*) CGdiObject::FromHandle(
::SelectObject(m_hAttribDC, pFont->m_hObject));
// If same as already selected, don't re-mirror screen font
if (m_hPrinterFont != pFont->m_hObject)
{
m_hPrinterFont = (HFONT)pFont->m_hObject;
MirrorFont();
}
return pOldFont;
}
/////////////////////////////////////////////////////////////////////////////
// Drawing-Attribute Functions
COLORREF CPreviewDC::SetBkColor(COLORREF crColor)
{
ASSERT(m_hAttribDC != NULL);
if (m_hDC != NULL)
::SetBkColor(m_hDC, ::GetNearestColor(m_hAttribDC, crColor));
return ::SetBkColor(m_hAttribDC, crColor);
}
COLORREF CPreviewDC::SetTextColor(COLORREF crColor)
{
ASSERT(m_hAttribDC != NULL);
if (m_hDC != NULL)
::SetTextColor(m_hDC, ::GetNearestColor(m_hAttribDC, crColor));
return ::SetTextColor(m_hAttribDC, crColor);
}
int CPreviewDC::SetMapMode(int nMapMode)
{
ASSERT(m_hAttribDC != NULL);
int nModeOld = ::SetMapMode(m_hAttribDC, nMapMode);
MirrorMappingMode(TRUE);
return nModeOld;
}
CPoint CPreviewDC::SetViewportOrg(int x, int y)
{
ASSERT(m_hAttribDC != NULL);
CPoint ptOrgOld;
VERIFY(::SetViewportOrgEx(m_hAttribDC, x, y, &ptOrgOld));
MirrorViewportOrg();
return ptOrgOld;
}
CPoint CPreviewDC::OffsetViewportOrg(int nWidth, int nHeight)
{
ASSERT(m_hAttribDC != NULL);
CPoint ptOrgOld;
VERIFY(::OffsetViewportOrgEx(m_hAttribDC, nWidth, nHeight, &ptOrgOld));
MirrorViewportOrg();
return ptOrgOld;
}
CSize CPreviewDC::SetViewportExt(int x, int y)
{
ASSERT(m_hAttribDC != NULL);
CSize sizeExtOld;
VERIFY(::SetViewportExtEx(m_hAttribDC, x, y, &sizeExtOld));
MirrorMappingMode(TRUE);
return sizeExtOld;
}
CSize CPreviewDC::ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom)
{
ASSERT(m_hAttribDC != NULL);
CSize sizeExtOld;
VERIFY(::ScaleViewportExtEx(m_hAttribDC, xNum, xDenom,
yNum, yDenom, &sizeExtOld));
MirrorMappingMode(TRUE);
return sizeExtOld;
}
CSize CPreviewDC::SetWindowExt(int x, int y)
{
ASSERT(m_hAttribDC != NULL);
CSize sizeExtOld;
VERIFY(::SetWindowExtEx(m_hAttribDC, x, y, &sizeExtOld));
MirrorMappingMode(TRUE);
return sizeExtOld;
}
CSize CPreviewDC::ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom)
{
ASSERT(m_hAttribDC != NULL);
CSize sizeExtOld;
VERIFY(::ScaleWindowExtEx(m_hAttribDC, xNum, xDenom, yNum, yDenom,
&sizeExtOld));
MirrorMappingMode(TRUE);
return sizeExtOld;
}
/////////////////////////////////////////////////////////////////////////////
// Text Functions
// private helpers for TextOut functions
AFX_STATIC int AFXAPI _AfxComputeNextTab(int x, UINT nTabStops, LPINT lpnTabStops,
int nTabOrigin, int nTabWidth)
{
x -= nTabOrigin; // normalize position to tab origin
for (UINT i = 0; i < nTabStops; i++, lpnTabStops++)
{
if (*lpnTabStops > x)
return *lpnTabStops + nTabOrigin;
}
return (x / nTabWidth + 1) * nTabWidth + nTabOrigin;
}
// Compute a character delta table for correctly positioning the screen
// font characters where the printer characters will appear on the page
CSize CPreviewDC::ComputeDeltas(int& x, LPCTSTR lpszString, UINT &nCount,
BOOL bTabbed, UINT nTabStops, LPINT lpnTabStops, int nTabOrigin,
LPTSTR lpszOutputString, int* pnDxWidths, int& nRightFixup)
{
ASSERT_VALID(this);
TEXTMETRIC tmAttrib;
TEXTMETRIC tmScreen;
::GetTextMetrics(m_hAttribDC, &tmAttrib);
::GetTextMetrics(m_hDC, &tmScreen);
CSize sizeExtent;
::GetTextExtentPoint32A(m_hAttribDC, "A", 1, &sizeExtent);
CPoint ptCurrent;
UINT nAlignment = ::GetTextAlign(m_hAttribDC);
BOOL bUpdateCP = (nAlignment & TA_UPDATECP) != 0;
if (bUpdateCP)
{
::GetCurrentPositionEx(m_hDC, &ptCurrent);
x = ptCurrent.x;
}
LPCTSTR lpszCurChar = lpszString;
LPCTSTR lpszStartRun = lpszString;
int* pnCurDelta = pnDxWidths;
int nStartRunPos = x;
int nCurrentPos = x;
int nStartOffset = 0;
int nTabWidth = 0;
if (bTabbed)
{
if (nTabStops == 1)
{
nTabWidth = lpnTabStops[0];
}
else
{
// Get default size of a tab
nTabWidth = LOWORD(::GetTabbedTextExtentA(m_hAttribDC,
"\t", 1, 0, NULL));
}
}
for (UINT i = 0; i < nCount; i++)
{
BOOL bSpace = ((_TUCHAR)*lpszCurChar == (_TUCHAR)tmAttrib.tmBreakChar);
if (bSpace || (bTabbed && *lpszCurChar == '\t'))
{
// bSpace will be either TRUE (==1) or FALSE (==0). For spaces
// we want the space included in the GetTextExtent, for tabs we
// do not want the tab included
int nRunLength = (int)(lpszCurChar - lpszStartRun) + bSpace;
CSize sizeExtent;
::GetTextExtentPoint32(m_hAttribDC, lpszStartRun, nRunLength,
&sizeExtent);
int nNewPos = nStartRunPos + sizeExtent.cx
- tmAttrib.tmOverhang;
// now, if this is a Tab (!bSpace), compute the next tab stop
if (!bSpace)
{
nNewPos = _AfxComputeNextTab(nNewPos, nTabStops, lpnTabStops,
nTabOrigin, nTabWidth);
}
// Add this width to previous width
if (pnCurDelta == pnDxWidths)
nStartOffset += nNewPos - nCurrentPos;
else
*(pnCurDelta-1) += nNewPos - nCurrentPos;
nCurrentPos = nNewPos;
nStartRunPos = nCurrentPos; // set start of run
// *lpszCurChar must be SBC: tmBreakChar or '\t'
lpszStartRun = lpszCurChar + 1;
}
else
{
// For the non-tabbed or non-tab-character case
int cxScreen;
if (_istlead(*lpszCurChar))
{
cxScreen = tmScreen.tmAveCharWidth;
*pnCurDelta = tmAttrib.tmAveCharWidth;
}
else
{
::GetCharWidth(m_hDC, (_TUCHAR)*lpszCurChar,
(_TUCHAR)*lpszCurChar, &cxScreen);
if (!::GetCharWidth(m_hAttribDC, (_TUCHAR)*lpszCurChar,
(_TUCHAR)*lpszCurChar, pnCurDelta))
{
// If printer driver fails the above call, use the average width
*pnCurDelta = tmAttrib.tmAveCharWidth;
}
}
*pnCurDelta -= tmAttrib.tmOverhang;
cxScreen -= tmScreen.tmOverhang;
nCurrentPos += *pnCurDelta; // update current position
// Center character in allotted space
if (pnCurDelta != pnDxWidths)
{
int diff = (*pnCurDelta - cxScreen) / 2;
*pnCurDelta -= diff;
*(pnCurDelta - 1) += diff;
}
*lpszOutputString++ = *lpszCurChar;
if (_istlead(*lpszCurChar))
{
*lpszOutputString++ = *(lpszCurChar+1); // copy trailing byte
*(pnCurDelta + 1) = *pnCurDelta; // double wide
nCurrentPos += *pnCurDelta;
pnCurDelta++;
i++;
}
pnCurDelta++;
}
lpszCurChar = _tcsinc(lpszCurChar);
}
nAlignment &= TA_CENTER|TA_RIGHT;
sizeExtent.cx = nCurrentPos - x;
nRightFixup = 0;
if (nAlignment == TA_LEFT)
x += nStartOffset; // Full left side adjustment
else if (nAlignment == TA_CENTER)
x += nStartOffset/2; // Adjust Center by 1/2 left side adjustment
else if (nAlignment == TA_RIGHT)
nRightFixup = nStartOffset; // Right adjust needed later if TA_UPDATECP
if (bUpdateCP)
::MoveToEx(m_hDC, x, ptCurrent.y, NULL);
nCount = (UINT)(pnCurDelta - pnDxWidths); // number of characters output
return sizeExtent;
}
BOOL CPreviewDC::TextOut(int x, int y, LPCTSTR lpszString, int nCount)
{
return ExtTextOut(x, y, 0, NULL, lpszString, nCount, NULL);
}
BOOL CPreviewDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
LPCTSTR lpszString, UINT nCount, LPINT lpDxWidths)
{
ASSERT(m_hDC != NULL);
ASSERT(m_hAttribDC != NULL);
ASSERT(lpszString != NULL);
ASSERT(lpDxWidths == NULL ||
AfxIsValidAddress(lpDxWidths, sizeof(int) * nCount, FALSE));
ASSERT(AfxIsValidAddress(lpszString, nCount, FALSE));
int* pDeltas = NULL;
LPTSTR pOutputString = NULL;
int nRightFixup = 0;
if (lpDxWidths == NULL)
{
if (nCount == 0) // Do nothing
return TRUE;
TRY
{
pDeltas = new int[nCount];
pOutputString = new TCHAR[nCount];
}
CATCH_ALL(e)
{
delete[] pDeltas; // in case it was allocated
// Note: DELETE_EXCEPTION(e) not required
return FALSE; // Could not allocate buffer, cannot display
}
END_CATCH_ALL
ComputeDeltas(x, (LPTSTR)lpszString, nCount, FALSE, 0, NULL, 0,
pOutputString, pDeltas, nRightFixup);
lpDxWidths = pDeltas;
lpszString = pOutputString;
}
BOOL bSuccess = ::ExtTextOut(m_hDC, x, y, nOptions, lpRect, lpszString,
nCount, lpDxWidths);
if (nRightFixup != 0 && bSuccess && (GetTextAlign() & TA_UPDATECP))
{
CPoint pt;
::GetCurrentPositionEx(m_hDC, &pt);
MoveTo(pt.x - nRightFixup, pt.y);
}
delete[] pDeltas;
delete[] pOutputString;
return bSuccess;
}
CSize CPreviewDC::TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount,
int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin)
{
ASSERT(m_hAttribDC != NULL);
ASSERT(m_hDC != NULL);
ASSERT(lpszString != NULL);
ASSERT(AfxIsValidAddress(lpszString, nCount, FALSE));
ASSERT(lpnTabStopPositions == NULL ||
AfxIsValidAddress(lpnTabStopPositions, sizeof(int) * nTabPositions,
FALSE));
if (nCount <= 0)
return 0; // nCount is zero, there is nothing to print
int* pDeltas = NULL;
LPTSTR pOutputString = NULL;
int nRightFixup;
TRY
{
pDeltas = new int[nCount];
pOutputString = new TCHAR[nCount];
}
CATCH_ALL(e)
{
delete[] pDeltas;
// Note: DELETE_EXCEPTION(e) not required
return 0; // signify error
}
END_CATCH_ALL
UINT uCount = nCount;
CSize sizeFinalExtent = ComputeDeltas(x, lpszString, uCount, TRUE,
nTabPositions, lpnTabStopPositions, nTabOrigin,
pOutputString, pDeltas, nRightFixup);
BOOL bSuccess = ExtTextOut(x, y, 0, NULL, pOutputString, uCount, pDeltas);
delete[] pDeltas;
delete[] pOutputString;
if (bSuccess && (GetTextAlign() & TA_UPDATECP))
{
CPoint pt;
::GetCurrentPositionEx(m_hDC, &pt);
MoveTo(pt.x - nRightFixup, pt.y);
}
return sizeFinalExtent;
}
// This one is too complicated to do character-by-character output positioning
// All we really need to do here is mirror the current position
int CPreviewDC::DrawText(LPCTSTR lpszString, int nCount, LPRECT lpRect,
UINT nFormat)
{
ASSERT(m_hAttribDC != NULL);
ASSERT(m_hDC != NULL);
ASSERT(lpszString != NULL);
ASSERT(lpRect != NULL);
ASSERT(AfxIsValidAddress(lpRect, sizeof(RECT)));
ASSERT(nCount == -1 ?
AfxIsValidString(lpszString) :
AfxIsValidAddress(lpszString, nCount, FALSE));
int retVal = ::DrawText(m_hDC, lpszString, nCount, lpRect, nFormat);
CPoint pos;
::GetCurrentPositionEx(m_hDC, &pos);
::MoveToEx(m_hAttribDC, pos.x, pos.y, NULL);
return retVal;
}
BOOL CPreviewDC::GrayString(CBrush*,
BOOL (CALLBACK *)(HDC, LPARAM, int),
LPARAM lpData, int nCount, int x, int y, int, int)
{
TRACE0("TextOut() substituted for GrayString() in Print Preview.\n");
return TextOut(x, y, (LPCTSTR)lpData, nCount);
}
int CPreviewDC::Escape(int nEscape, int nCount, LPCSTR lpszInData, void* lpOutData)
{
// The tact here is to NOT allow any of the document control escapes
// to be passed through. Elimination of StartDoc and EndDoc should
// eliminate anything actually going to the printer. Also anything
// that actually draws something will be filtered.
ASSERT(m_hAttribDC != NULL);
switch (nEscape)
{
case NEXTBAND:
case SETCOLORTABLE:
case GETCOLORTABLE:
case FLUSHOUTPUT:
case DRAFTMODE:
case QUERYESCSUPPORT:
case GETPHYSPAGESIZE:
case GETPRINTINGOFFSET:
case GETSCALINGFACTOR:
case GETPENWIDTH:
case SETCOPYCOUNT:
case SELECTPAPERSOURCE:
case GETTECHNOLOGY:
case SETLINECAP:
case SETLINEJOIN:
case SETMITERLIMIT:
case BANDINFO:
case GETVECTORPENSIZE:
case GETVECTORBRUSHSIZE:
case ENABLEDUPLEX:
case GETSETPAPERBINS:
case GETSETPRINTORIENT:
case ENUMPAPERBINS:
case SETDIBSCALING:
case ENUMPAPERMETRICS:
case GETSETPAPERMETRICS:
case GETEXTENDEDTEXTMETRICS:
case GETEXTENTTABLE:
case GETPAIRKERNTABLE:
case GETTRACKKERNTABLE:
case ENABLERELATIVEWIDTHS:
case ENABLEPAIRKERNING:
case SETKERNTRACK:
case SETALLJUSTVALUES:
case SETCHARSET:
case SET_BACKGROUND_COLOR:
case SET_SCREEN_ANGLE:
case SET_SPREAD:
return ::Escape(m_hAttribDC, nEscape, nCount, lpszInData, lpOutData);
default:
return 0;
}
}
void CPreviewDC::MirrorMappingMode(BOOL bCompute)
{
ASSERT(m_hAttribDC != NULL);
if (bCompute)
{
//
// The following formula is used to compute the screen's viewport extent
// From the printer and screen information and the Printer's Viewport
// Extents. (Note: This formula is used twice, once for horizontal
// and once for vertical)
//
// It is assumed that the Window Extents are maintained as equal.
//
// m * LogPixPerInch(Screen) * VpExt(Printer)
// VpExt(Screen) = -------------------------------------------------
// n * LogPixPerInch(Printer)
//
// Where m/n is the scaling factor. (m/n > 1 is expansion)
//
VERIFY(::GetViewportExtEx(m_hAttribDC, &m_sizeVpExt));
VERIFY(::GetWindowExtEx(m_hAttribDC, &m_sizeWinExt));
while (m_sizeWinExt.cx > -0x4000 && m_sizeWinExt.cx < 0x4000 &&
m_sizeVpExt.cx > -0x4000 && m_sizeVpExt.cx < 0x4000)
{
m_sizeWinExt.cx <<= 1;
m_sizeVpExt.cx <<= 1;
}
while (m_sizeWinExt.cy > -0x4000 && m_sizeWinExt.cy < 0x4000 &&
m_sizeVpExt.cy > -0x4000 && m_sizeVpExt.cy < 0x4000)
{
m_sizeWinExt.cy <<= 1;
m_sizeVpExt.cy <<= 1;
}
long lTempExt = _AfxMultMultDivDiv(m_sizeVpExt.cx,
m_nScaleNum, afxData.cxPixelsPerInch,
m_nScaleDen, ::GetDeviceCaps(m_hAttribDC, LOGPIXELSX));
ASSERT(m_sizeWinExt.cx != 0);
m_sizeVpExt.cx = (int)lTempExt;
lTempExt = _AfxMultMultDivDiv(m_sizeVpExt.cy,
m_nScaleNum, afxData.cyPixelsPerInch,
m_nScaleDen, ::GetDeviceCaps(m_hAttribDC, LOGPIXELSY));
ASSERT(m_sizeWinExt.cy != 0);
m_sizeVpExt.cy = (int)lTempExt;
}
if (m_hDC != NULL)
{
::SetMapMode(m_hDC, MM_ANISOTROPIC);
::SetWindowExtEx(m_hDC, m_sizeWinExt.cx, m_sizeWinExt.cy, NULL);
::SetViewportExtEx(m_hDC, m_sizeVpExt.cx, m_sizeVpExt.cy, NULL);
// Now that the Logical Units are synchronized, we can set the Viewport Org
MirrorViewportOrg();
}
}
void CPreviewDC::MirrorViewportOrg()
{
if (m_hAttribDC == NULL || m_hDC == NULL)
return;
CPoint ptVpOrg;
VERIFY(::GetViewportOrgEx(m_hAttribDC, &ptVpOrg));
PrinterDPtoScreenDP(&ptVpOrg);
ptVpOrg += m_sizeTopLeft;
::SetViewportOrgEx(m_hDC, ptVpOrg.x, ptVpOrg.y, NULL);
CPoint ptWinOrg;
VERIFY(::GetWindowOrgEx(m_hAttribDC, &ptWinOrg));
::SetWindowOrgEx(m_hDC, ptWinOrg.x, ptWinOrg.y, NULL);
}
void CPreviewDC::SetTopLeftOffset(CSize sizeTopLeft)
{
ASSERT(m_hAttribDC != NULL);
m_sizeTopLeft = sizeTopLeft;
MirrorViewportOrg();
}
void CPreviewDC::ClipToPage()
{
ASSERT(m_hAttribDC != NULL);
ASSERT(m_hDC != NULL);
// Create a rect in Screen Device coordinates that is one pixel larger
// on all sides than the actual page. This is to hide the fact that
// the printer to screen mapping mode is approximate and may result
// in rounding error.
CPoint pt(::GetDeviceCaps(m_hAttribDC, HORZRES),
::GetDeviceCaps(m_hAttribDC, VERTRES));
PrinterDPtoScreenDP(&pt);
// Set the screen dc to MM_TEXT and no WindowOrg for the interesection
::SetMapMode(m_hDC, MM_TEXT);
::SetWindowOrgEx(m_hDC, 0, 0, NULL);
::SetViewportOrgEx(m_hDC, m_sizeTopLeft.cx, m_sizeTopLeft.cy, NULL);
::IntersectClipRect(m_hDC, -1, -1, pt.x + 2, pt.y + 2);
// Resynchronize the mapping mode
MirrorMappingMode(FALSE);
}
// these conversion functions can be used without an attached screen DC
void CPreviewDC::PrinterDPtoScreenDP(LPPOINT lpPoint) const
{
ASSERT(m_hAttribDC != NULL);
CSize sizePrinterVpExt;
VERIFY(::GetViewportExtEx(m_hAttribDC, &sizePrinterVpExt));
CSize sizePrinterWinExt;
VERIFY(::GetWindowExtEx(m_hAttribDC, &sizePrinterWinExt));
long xScreen = _AfxMultMultDivDiv(lpPoint->x,
sizePrinterWinExt.cx, m_sizeVpExt.cx,
sizePrinterVpExt.cx, m_sizeWinExt.cx);
lpPoint->x = (int)xScreen;
long yScreen = _AfxMultMultDivDiv(lpPoint->y,
sizePrinterWinExt.cy, m_sizeVpExt.cy,
sizePrinterVpExt.cy, m_sizeWinExt.cy);
lpPoint->y = (int)yScreen;
}
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
IMPLEMENT_DYNAMIC(CPreviewDC, CDC)
/////////////////////////////////////////////////////////////////////////////