mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
999 lines
25 KiB
999 lines
25 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-1995 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
|
|
|
|
static long AFXAPI MultMultDivDiv(int factor, int num1, int num2,
|
|
int den1, int den2);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// 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 = 0;
|
|
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
|
|
|
|
#ifdef _MAC
|
|
VERIFY(::GetCharWidth(m_hDC, 0, 255, m_aCharWidthsDraw));
|
|
VERIFY(::GetCharWidth(m_hAttribDC, 0, 255, m_aCharWidthsAttrib));
|
|
#endif
|
|
}
|
|
|
|
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
|
|
|
|
static int ComputeNextTab(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;
|
|
::GetTextExtentPointA(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;
|
|
::GetTextExtentPoint(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 = ComputeNextTab(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
|
|
{
|
|
#ifndef _MAC
|
|
::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;
|
|
}
|
|
#else
|
|
cxScreen = m_aCharWidthsDraw[(_TUCHAR)*lpszCurChar];
|
|
*pnCurDelta = m_aCharWidthsAttrib[(_TUCHAR)*lpszCurChar];
|
|
#endif
|
|
}
|
|
*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));
|
|
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 = MultMultDivDiv(m_sizeVpExt.cx,
|
|
m_nScaleNum, afxData.cxPixelsPerInch,
|
|
m_nScaleDen, ::GetDeviceCaps(m_hAttribDC, LOGPIXELSX));
|
|
|
|
ASSERT(m_sizeWinExt.cx != 0);
|
|
m_sizeVpExt.cx = (int)lTempExt;
|
|
|
|
lTempExt = MultMultDivDiv(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 = MultMultDivDiv(lpPoint->x,
|
|
sizePrinterWinExt.cx, m_sizeVpExt.cx,
|
|
sizePrinterVpExt.cx, m_sizeWinExt.cx);
|
|
|
|
lpPoint->x = (int)xScreen;
|
|
|
|
long yScreen = MultMultDivDiv(lpPoint->y,
|
|
sizePrinterWinExt.cy, m_sizeVpExt.cy,
|
|
sizePrinterVpExt.cy, m_sizeWinExt.cy);
|
|
|
|
lpPoint->y = (int)yScreen;
|
|
}
|
|
|
|
static long AFXAPI MultMultDivDiv(int factor, int num1, int num2,
|
|
int den1, int den2)
|
|
{
|
|
#if defined(_AFX_PORTABLE) || defined(_MAC)
|
|
// 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);
|
|
}
|
|
|
|
#ifdef AFX_INIT_SEG
|
|
#pragma code_seg(AFX_INIT_SEG)
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC(CPreviewDC, CDC)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|