// 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) /////////////////////////////////////////////////////////////////////////////