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 >
C/C++ Source or Header  |  1998-06-16  |  26KB  |  989 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_PRINT_SEG
  14. #pragma code_seg(AFX_PRINT_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // Helper functions
  26.  
  27. AFX_STATIC long AFXAPI _AfxMultMultDivDiv(
  28.     int factor, int num1, int num2,
  29.     int den1, int den2)
  30. {
  31. #ifdef _AFX_PORTABLE
  32.     // make sure that (num1 * num2) does not overflow 31-bits.
  33.     long temp = num1 < 0 ? -num1 : num1;
  34.     for (int nBitsResult = 0; temp != 0; nBitsResult++)
  35.         temp >>= 1;
  36.     temp = num2 < 0 ? -num2 : num2;
  37.     for (; temp != 0; nBitsResult++)
  38.         temp >>= 1;
  39.     if (nBitsResult > 31)
  40.     {
  41.         num1 >>= nBitsResult - 31;
  42.         num2 >>= nBitsResult - 31;
  43.     }
  44.  
  45.     // make sure that (den1 * den2) does not overflow 31-bits
  46.     temp = den1 < 0 ? -den1 : den1;
  47.     for (nBitsResult = 0; temp != 0; nBitsResult++)
  48.         temp >>= 1;
  49.     temp = den2 < 0 ? -den2 : den2;
  50.     for (; temp != 0; nBitsResult++)
  51.         temp >>= 1;
  52.     if (nBitsResult > 31)
  53.     {
  54.         den1 >>= nBitsResult - 31;
  55.         den2 >>= nBitsResult - 31;
  56.     }
  57.  
  58.     long numerator = (long)num1 * (long)num2;   // no overflow
  59.     long denominator = (long)den1 * (long)den2; // no overflow
  60. #else
  61.     __int64 numerator = (__int64)num1 * (__int64)num2;   // no overflow
  62.     __int64 denominator = (__int64)den1 * (__int64)den2; // no overflow
  63.     __int64 temp;
  64. #endif
  65.  
  66.     temp = numerator < 0 ? -numerator : numerator;
  67.     for (int nBitsInNum = 0; temp != 0; nBitsInNum++)
  68.         temp >>= 1;
  69.  
  70.     temp = factor < 0 ? -factor : factor;
  71.     for (int nBitsInFactor = 0; temp != 0; nBitsInFactor++)
  72.         temp >>= 1;
  73.  
  74.     nBitsInNum += nBitsInFactor;
  75.  
  76.     //
  77.     // normalizing the denominator to positive results in an easier
  78.     // determination of whether there is overflow
  79.     //
  80.     if (denominator < 0)
  81.     {
  82.         denominator = -denominator;
  83.         numerator = -numerator;
  84.     }
  85.  
  86.     // Get the product of factor * numerator representable in a long/__int64
  87.     // while distributing loss of presision across all three numerator terms
  88.     // Adjust denominator as well
  89.     //
  90.     while (nBitsInNum-- > 31)
  91.     {
  92.         numerator >>= 1;
  93.         denominator >>= 1;
  94.         if (nBitsInNum-- <= 31)
  95.             break;
  96.         numerator >>= 1;
  97.         denominator >>= 1;
  98.         if (nBitsInNum-- <= 31)
  99.             break;
  100.         factor >>= 1;
  101.         denominator >>= 1;
  102.     }
  103.     numerator *= factor;
  104.  
  105.     if (denominator == 0)
  106.         return numerator < 0 ? LONG_MIN : LONG_MAX;
  107.  
  108.     return (long) ((numerator + denominator/2) / denominator);
  109. }
  110.  
  111. /////////////////////////////////////////////////////////////////////////////
  112. // Print Preview DC (CPreviewDC)
  113.  
  114. CPreviewDC::CPreviewDC()
  115. {
  116.     // Initial scale factor and top-left offset
  117.     m_nScaleNum = m_nScaleDen = 1;
  118.     m_sizeTopLeft.cx = m_sizeTopLeft.cy = 8;
  119.     m_hFont = m_hPrinterFont = NULL;
  120. }
  121.  
  122. void CPreviewDC::SetOutputDC(HDC hDC)
  123. {
  124.     ASSERT(hDC != NULL);
  125.     m_nSaveDCIndex = ::SaveDC(hDC); // restore in ReleaseOutputDC()
  126.     CDC::SetOutputDC(hDC);
  127.  
  128.     if (m_hAttribDC != NULL)
  129.     {
  130.         MirrorMappingMode(FALSE);
  131.  
  132.         if (m_hFont)
  133.             ::SelectObject(m_hDC, m_hFont);
  134.         else
  135.             MirrorFont();
  136.         MirrorAttributes();
  137.     }
  138. }
  139.  
  140. void CPreviewDC::ReleaseOutputDC()
  141. {
  142.     ASSERT(m_hDC != NULL);
  143.     ::RestoreDC(m_hDC, m_nSaveDCIndex); // Saved in SetOutputDC()
  144.     CDC::ReleaseOutputDC();
  145. }
  146.  
  147. void CPreviewDC::SetAttribDC(HDC hDC)
  148. {
  149.     ASSERT(hDC != NULL);
  150.     CDC::SetAttribDC(hDC);
  151.  
  152.     MirrorMappingMode(TRUE);
  153.     MirrorFont();
  154.     MirrorAttributes();
  155. }
  156.  
  157. CPreviewDC::~CPreviewDC()
  158. {
  159.     ASSERT(m_hDC == NULL);      // Should not have a screen DC at this time
  160.     AfxDeleteObject((HGDIOBJ*)&m_hFont);
  161. }
  162.  
  163. void CPreviewDC::SetScaleRatio(int nNumerator, int nDenominator)
  164. {
  165.     m_nScaleNum = nNumerator;
  166.     m_nScaleDen = nDenominator;
  167.     if (m_hAttribDC != NULL)
  168.     {
  169.         MirrorMappingMode(TRUE);
  170.         MirrorFont();
  171.     }
  172. }
  173.  
  174. // Implementation support
  175. #ifdef _DEBUG
  176. void CPreviewDC::AssertValid() const
  177. {
  178.     CDC::AssertValid();
  179. }
  180.  
  181.  
  182. void CPreviewDC::Dump(CDumpContext& dc) const
  183. {
  184.     CDC::Dump(dc);
  185.  
  186.     dc << "Scale Factor: " << m_nScaleNum << "/" << m_nScaleDen;
  187.     dc << "\n";
  188. }
  189. #endif
  190.  
  191. int CPreviewDC::SaveDC()
  192. {
  193.     ASSERT(m_hAttribDC != NULL);
  194.     int nAttribIndex = ::SaveDC(m_hAttribDC);
  195.     if (m_hDC != NULL)
  196.     {
  197.         // remove font from object
  198.         ::SelectObject(m_hDC, GetStockObject(SYSTEM_FONT));
  199.         m_nSaveDCDelta = ::SaveDC(m_hDC) - nAttribIndex;
  200.         // Select font back in after save
  201.         ::SelectObject(m_hDC, m_hFont);
  202.     }
  203.     else
  204.     {
  205.         m_nSaveDCDelta = 0x7fff;        // impossibly high value
  206.     }
  207.     return nAttribIndex;
  208. }
  209.  
  210. BOOL CPreviewDC::RestoreDC(int nSavedDC)
  211. {
  212.     ASSERT(m_hAttribDC != NULL);
  213.     BOOL bSuccess = ::RestoreDC(m_hAttribDC, nSavedDC);
  214.     if (bSuccess)
  215.     {
  216.         if (m_nSaveDCDelta != 0x7fff)
  217.         {
  218.             ASSERT(m_hDC != NULL);      // removed Output DC after save
  219.  
  220.             if (nSavedDC != -1)
  221.                 nSavedDC += m_nSaveDCDelta;
  222.             bSuccess = ::RestoreDC(m_hDC, nSavedDC);
  223.             MirrorFont();               // mirror the font
  224.         }
  225.         else
  226.         {
  227.             ASSERT(m_hDC == NULL);      // Added the Output DC after save
  228.         }
  229.     }
  230.     return bSuccess;
  231. }
  232.  
  233. void CPreviewDC::MirrorAttributes()
  234. {
  235.     ASSERT(m_hAttribDC != NULL);
  236.  
  237.     if (m_hDC != NULL)
  238.     {
  239.         // extract and re-set Pen and Brush
  240.         HGDIOBJ hTemp = ::SelectObject(m_hAttribDC, ::GetStockObject(BLACK_PEN));
  241.         ::SelectObject(m_hAttribDC, hTemp);
  242.         ::SelectObject(m_hDC, hTemp);
  243.         hTemp = ::SelectObject(m_hAttribDC, ::GetStockObject(BLACK_BRUSH));
  244.         ::SelectObject(m_hAttribDC, hTemp);
  245.         ::SelectObject(m_hDC, hTemp);
  246.  
  247.         SetROP2(GetROP2());
  248.         SetBkMode(GetBkMode());
  249.         SetTextAlign(GetTextAlign());
  250.         SetPolyFillMode(GetPolyFillMode());
  251.         SetStretchBltMode(GetStretchBltMode());
  252.         SetTextColor(GetNearestColor(GetTextColor()));
  253.         SetBkColor(GetNearestColor(GetBkColor()));
  254.     }
  255. }
  256.  
  257. CGdiObject* CPreviewDC::SelectStockObject(int nIndex)
  258. {
  259.     ASSERT(m_hAttribDC != NULL);
  260.  
  261.     HGDIOBJ hObj = ::GetStockObject(nIndex);
  262.  
  263.     switch (nIndex)
  264.     {
  265.     case ANSI_FIXED_FONT:
  266.     case ANSI_VAR_FONT:
  267.     case DEVICE_DEFAULT_FONT:
  268.     case OEM_FIXED_FONT:
  269.     case SYSTEM_FONT:
  270.     case SYSTEM_FIXED_FONT:
  271.     case DEFAULT_GUI_FONT:
  272.         // Handle the stock fonts correctly
  273.         {
  274.             CGdiObject* pObject = CGdiObject::FromHandle(
  275.                             ::SelectObject(m_hAttribDC, hObj));
  276.  
  277.             // Don't re-mirror screen font if this is the same font.
  278.             if (m_hPrinterFont == (HFONT) hObj)
  279.                 return pObject;
  280.  
  281.             m_hPrinterFont = (HFONT) hObj;
  282.  
  283.             ASSERT(m_hPrinterFont != NULL); // Do not allow infinite recursion
  284.  
  285.             MirrorFont();
  286.             return pObject;
  287.         }
  288.  
  289.     default:
  290.         if (m_hDC != NULL)
  291.             ::SelectObject(m_hDC, hObj);
  292.         return CGdiObject::FromHandle(::SelectObject(m_hAttribDC, hObj));
  293.     }
  294. }
  295.  
  296. void CPreviewDC::MirrorFont()
  297. {
  298.     if (m_hAttribDC == NULL)
  299.         return;         // Can't do anything without Attrib DC
  300.  
  301.     if (m_hPrinterFont == NULL)
  302.     {
  303.         SelectStockObject(DEVICE_DEFAULT_FONT); // will recurse
  304.         return;
  305.     }
  306.  
  307.     if (m_hDC == NULL)
  308.         return;         // can't mirror font without a screen DC
  309.  
  310.     LOGFONT logFont;
  311.     // Fill the logFont structure with the original info
  312.     ::GetObject(m_hPrinterFont, sizeof(LOGFONT), (LPVOID)&logFont);
  313.  
  314.     TEXTMETRIC tm;
  315.  
  316.     GetTextFace(LF_FACESIZE, (LPTSTR)&logFont.lfFaceName[0]);
  317.     GetTextMetrics(&tm);
  318.  
  319.     // Set real values based on the Printer's text metrics.
  320.  
  321.     if (tm.tmHeight < 0)
  322.         logFont.lfHeight = tm.tmHeight;
  323.     else
  324.         logFont.lfHeight = -(tm.tmHeight - tm.tmInternalLeading);
  325.  
  326.     logFont.lfWidth = tm.tmAveCharWidth;
  327.     logFont.lfWeight = tm.tmWeight;
  328.     logFont.lfItalic = tm.tmItalic;
  329.     logFont.lfUnderline = tm.tmUnderlined;
  330.     logFont.lfStrikeOut = tm.tmStruckOut;
  331.     logFont.lfCharSet = tm.tmCharSet;
  332.     logFont.lfPitchAndFamily = tm.tmPitchAndFamily;
  333.  
  334.     HFONT hNewFont = ::CreateFontIndirect(&logFont);
  335.     ::SelectObject(m_hDC, hNewFont);
  336.  
  337.     ::GetTextMetrics(m_hDC, &tm);
  338.  
  339.     // Is the displayed font too large?
  340.  
  341.     int cyDesired = -logFont.lfHeight;
  342.     int cyActual;
  343.     if (tm.tmHeight < 0)
  344.         cyActual = -tm.tmHeight;
  345.     else
  346.         cyActual = tm.tmHeight - tm.tmInternalLeading;
  347.  
  348.     CSize sizeWinExt;
  349.     VERIFY(::GetWindowExtEx(m_hDC, &sizeWinExt));
  350.     CSize sizeVpExt;
  351.     VERIFY(::GetViewportExtEx(m_hDC, &sizeVpExt));
  352.  
  353.     // Only interested in Extent Magnitudes, not direction
  354.     if (sizeWinExt.cy < 0)
  355.         sizeWinExt.cy = -sizeWinExt.cy;
  356.     if (sizeVpExt.cy < 0)
  357.         sizeVpExt.cy = -sizeVpExt.cy;
  358.  
  359.     // Convert to screen device coordinates to eliminate rounding
  360.     // errors as a source of SmallFont aliasing
  361.  
  362.     cyDesired = MulDiv(cyDesired, sizeVpExt.cy, sizeWinExt.cy);
  363.     cyActual = MulDiv(cyActual, sizeVpExt.cy, sizeWinExt.cy);
  364.  
  365.     ASSERT(cyDesired >= 0 && cyActual >= 0);
  366.  
  367.     if (cyDesired < cyActual)
  368.     {
  369.         logFont.lfFaceName[0] = 0;      // let the mapper find a good fit
  370.  
  371.         if ((logFont.lfPitchAndFamily & 0xf0) == FF_DECORATIVE)
  372.             logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DECORATIVE;
  373.         else
  374.             logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  375.  
  376.         HFONT hTempFont = ::CreateFontIndirect(&logFont);
  377.         ::SelectObject(m_hDC, hTempFont);           // Select it in.
  378.         ::DeleteObject(hNewFont);
  379.         hNewFont = hTempFont;
  380.     }
  381.  
  382.     AfxDeleteObject((HGDIOBJ*)&m_hFont);  // delete the old logical font
  383.     m_hFont = hNewFont;         // save the new one
  384. }
  385.  
  386. CFont* CPreviewDC::SelectObject(CFont* pFont)
  387. {
  388.     if (pFont == NULL)
  389.         return NULL;
  390.  
  391.     ASSERT(m_hAttribDC != NULL);
  392.     ASSERT_VALID(pFont);
  393.  
  394.     CFont* pOldFont = (CFont*) CGdiObject::FromHandle(
  395.                 ::SelectObject(m_hAttribDC, pFont->m_hObject));
  396.  
  397.     // If same as already selected, don't re-mirror screen font
  398.     if (m_hPrinterFont != pFont->m_hObject)
  399.     {
  400.         m_hPrinterFont = (HFONT)pFont->m_hObject;
  401.         MirrorFont();
  402.     }
  403.  
  404.     return pOldFont;
  405. }
  406.  
  407. /////////////////////////////////////////////////////////////////////////////
  408. // Drawing-Attribute Functions
  409.  
  410. COLORREF CPreviewDC::SetBkColor(COLORREF crColor)
  411. {
  412.     ASSERT(m_hAttribDC != NULL);
  413.     if (m_hDC != NULL)
  414.         ::SetBkColor(m_hDC, ::GetNearestColor(m_hAttribDC, crColor));
  415.     return ::SetBkColor(m_hAttribDC, crColor);
  416. }
  417.  
  418. COLORREF CPreviewDC::SetTextColor(COLORREF crColor)
  419. {
  420.     ASSERT(m_hAttribDC != NULL);
  421.     if (m_hDC != NULL)
  422.         ::SetTextColor(m_hDC, ::GetNearestColor(m_hAttribDC, crColor));
  423.     return ::SetTextColor(m_hAttribDC, crColor);
  424. }
  425.  
  426. int CPreviewDC::SetMapMode(int nMapMode)
  427. {
  428.     ASSERT(m_hAttribDC != NULL);
  429.     int nModeOld = ::SetMapMode(m_hAttribDC, nMapMode);
  430.     MirrorMappingMode(TRUE);
  431.     return nModeOld;
  432. }
  433.  
  434. CPoint CPreviewDC::SetViewportOrg(int x, int y)
  435. {
  436.     ASSERT(m_hAttribDC != NULL);
  437.     CPoint ptOrgOld;
  438.     VERIFY(::SetViewportOrgEx(m_hAttribDC, x, y, &ptOrgOld));
  439.     MirrorViewportOrg();
  440.     return ptOrgOld;
  441. }
  442.  
  443. CPoint CPreviewDC::OffsetViewportOrg(int nWidth, int nHeight)
  444. {
  445.     ASSERT(m_hAttribDC != NULL);
  446.     CPoint ptOrgOld;
  447.     VERIFY(::OffsetViewportOrgEx(m_hAttribDC, nWidth, nHeight, &ptOrgOld));
  448.     MirrorViewportOrg();
  449.     return ptOrgOld;
  450. }
  451.  
  452. CSize CPreviewDC::SetViewportExt(int x, int y)
  453. {
  454.     ASSERT(m_hAttribDC != NULL);
  455.     CSize sizeExtOld;
  456.     VERIFY(::SetViewportExtEx(m_hAttribDC, x, y, &sizeExtOld));
  457.     MirrorMappingMode(TRUE);
  458.     return sizeExtOld;
  459. }
  460.  
  461. CSize CPreviewDC::ScaleViewportExt(int xNum, int xDenom, int yNum, int yDenom)
  462. {
  463.     ASSERT(m_hAttribDC != NULL);
  464.     CSize sizeExtOld;
  465.     VERIFY(::ScaleViewportExtEx(m_hAttribDC, xNum, xDenom,
  466.         yNum, yDenom, &sizeExtOld));
  467.     MirrorMappingMode(TRUE);
  468.     return sizeExtOld;
  469. }
  470.  
  471. CSize CPreviewDC::SetWindowExt(int x, int y)
  472. {
  473.     ASSERT(m_hAttribDC != NULL);
  474.     CSize sizeExtOld;
  475.     VERIFY(::SetWindowExtEx(m_hAttribDC, x, y, &sizeExtOld));
  476.     MirrorMappingMode(TRUE);
  477.     return sizeExtOld;
  478. }
  479.  
  480. CSize CPreviewDC::ScaleWindowExt(int xNum, int xDenom, int yNum, int yDenom)
  481. {
  482.     ASSERT(m_hAttribDC != NULL);
  483.     CSize sizeExtOld;
  484.     VERIFY(::ScaleWindowExtEx(m_hAttribDC, xNum, xDenom, yNum, yDenom,
  485.         &sizeExtOld));
  486.     MirrorMappingMode(TRUE);
  487.     return sizeExtOld;
  488. }
  489.  
  490. /////////////////////////////////////////////////////////////////////////////
  491. // Text Functions
  492.  
  493. // private helpers for TextOut functions
  494.  
  495. AFX_STATIC int AFXAPI _AfxComputeNextTab(int x, UINT nTabStops, LPINT lpnTabStops,
  496.                                         int nTabOrigin, int nTabWidth)
  497. {
  498.     x -= nTabOrigin;        // normalize position to tab origin
  499.     for (UINT i = 0; i < nTabStops; i++, lpnTabStops++)
  500.     {
  501.         if (*lpnTabStops > x)
  502.             return *lpnTabStops + nTabOrigin;
  503.     }
  504.     return (x / nTabWidth + 1) * nTabWidth + nTabOrigin;
  505. }
  506.  
  507. // Compute a character delta table for correctly positioning the screen
  508. // font characters where the printer characters will appear on the page
  509. CSize CPreviewDC::ComputeDeltas(int& x, LPCTSTR lpszString, UINT &nCount,
  510.     BOOL bTabbed, UINT nTabStops, LPINT lpnTabStops, int nTabOrigin,
  511.     LPTSTR lpszOutputString, int* pnDxWidths, int& nRightFixup)
  512. {
  513.     ASSERT_VALID(this);
  514.  
  515.     TEXTMETRIC tmAttrib;
  516.     TEXTMETRIC tmScreen;
  517.     ::GetTextMetrics(m_hAttribDC, &tmAttrib);
  518.     ::GetTextMetrics(m_hDC, &tmScreen);
  519.  
  520.     CSize sizeExtent;
  521.     ::GetTextExtentPoint32A(m_hAttribDC, "A", 1, &sizeExtent);
  522.  
  523.     CPoint ptCurrent;
  524.     UINT nAlignment = ::GetTextAlign(m_hAttribDC);
  525.     BOOL bUpdateCP = (nAlignment & TA_UPDATECP) != 0;
  526.     if (bUpdateCP)
  527.     {
  528.         ::GetCurrentPositionEx(m_hDC, &ptCurrent);
  529.         x = ptCurrent.x;
  530.     }
  531.  
  532.     LPCTSTR lpszCurChar = lpszString;
  533.     LPCTSTR lpszStartRun = lpszString;
  534.     int* pnCurDelta = pnDxWidths;
  535.     int nStartRunPos = x;
  536.     int nCurrentPos = x;
  537.     int nStartOffset = 0;
  538.  
  539.     int nTabWidth = 0;
  540.     if (bTabbed)
  541.     {
  542.         if (nTabStops == 1)
  543.         {
  544.             nTabWidth = lpnTabStops[0];
  545.         }
  546.         else
  547.         {
  548.             // Get default size of a tab
  549.             nTabWidth = LOWORD(::GetTabbedTextExtentA(m_hAttribDC,
  550.                 "\t", 1, 0, NULL));
  551.         }
  552.     }
  553.  
  554.     for (UINT i = 0; i < nCount; i++)
  555.     {
  556.         BOOL bSpace = ((_TUCHAR)*lpszCurChar == (_TUCHAR)tmAttrib.tmBreakChar);
  557.         if (bSpace || (bTabbed && *lpszCurChar == '\t'))
  558.         {
  559.             // bSpace will be either TRUE (==1) or FALSE (==0).  For spaces
  560.             // we want the space included in the GetTextExtent, for tabs we
  561.             // do not want the tab included
  562.             int nRunLength = (int)(lpszCurChar - lpszStartRun) + bSpace;
  563.  
  564.             CSize sizeExtent;
  565.             ::GetTextExtentPoint32(m_hAttribDC, lpszStartRun, nRunLength,
  566.                 &sizeExtent);
  567.             int nNewPos = nStartRunPos + sizeExtent.cx
  568.                 - tmAttrib.tmOverhang;
  569.  
  570.             // now, if this is a Tab (!bSpace), compute the next tab stop
  571.             if (!bSpace)
  572.             {
  573.                 nNewPos = _AfxComputeNextTab(nNewPos, nTabStops, lpnTabStops,
  574.                                 nTabOrigin, nTabWidth);
  575.             }
  576.  
  577.             // Add this width to previous width
  578.             if (pnCurDelta == pnDxWidths)
  579.                 nStartOffset += nNewPos - nCurrentPos;
  580.             else
  581.                 *(pnCurDelta-1) += nNewPos - nCurrentPos;
  582.  
  583.             nCurrentPos = nNewPos;
  584.  
  585.             nStartRunPos = nCurrentPos;     // set start of run
  586.             // *lpszCurChar must be SBC: tmBreakChar or '\t'
  587.             lpszStartRun = lpszCurChar + 1;
  588.         }
  589.         else
  590.         {
  591.             // For the non-tabbed or non-tab-character case
  592.             int cxScreen;
  593.             if (_istlead(*lpszCurChar))
  594.             {
  595.                 cxScreen = tmScreen.tmAveCharWidth;
  596.                 *pnCurDelta = tmAttrib.tmAveCharWidth;
  597.             }
  598.             else
  599.             {
  600.                 ::GetCharWidth(m_hDC, (_TUCHAR)*lpszCurChar,
  601.                     (_TUCHAR)*lpszCurChar, &cxScreen);
  602.                 if (!::GetCharWidth(m_hAttribDC, (_TUCHAR)*lpszCurChar,
  603.                     (_TUCHAR)*lpszCurChar, pnCurDelta))
  604.                 {
  605.                     // If printer driver fails the above call, use the average width
  606.                     *pnCurDelta = tmAttrib.tmAveCharWidth;
  607.                 }
  608.             }
  609.             *pnCurDelta -= tmAttrib.tmOverhang;
  610.             cxScreen -= tmScreen.tmOverhang;
  611.             nCurrentPos += *pnCurDelta;     // update current position
  612.  
  613.             // Center character in allotted space
  614.             if (pnCurDelta != pnDxWidths)
  615.             {
  616.                 int diff = (*pnCurDelta - cxScreen) / 2;
  617.                 *pnCurDelta -= diff;
  618.                 *(pnCurDelta - 1) += diff;
  619.             }
  620.             *lpszOutputString++ = *lpszCurChar;
  621.             if (_istlead(*lpszCurChar))
  622.             {
  623.                 *lpszOutputString++ = *(lpszCurChar+1); // copy trailing byte
  624.                 *(pnCurDelta + 1) = *pnCurDelta;        // double wide
  625.                 nCurrentPos += *pnCurDelta;
  626.                 pnCurDelta++;
  627.                 i++;
  628.             }
  629.             pnCurDelta++;
  630.         }
  631.         lpszCurChar = _tcsinc(lpszCurChar);
  632.     }
  633.  
  634.     nAlignment &= TA_CENTER|TA_RIGHT;
  635.     sizeExtent.cx = nCurrentPos - x;
  636.     nRightFixup = 0;
  637.  
  638.     if (nAlignment == TA_LEFT)
  639.         x += nStartOffset;      // Full left side adjustment
  640.     else if (nAlignment == TA_CENTER)
  641.         x += nStartOffset/2;    // Adjust Center by 1/2 left side adjustment
  642.     else if (nAlignment == TA_RIGHT)
  643.         nRightFixup = nStartOffset; // Right adjust needed later if TA_UPDATECP
  644.  
  645.     if (bUpdateCP)
  646.         ::MoveToEx(m_hDC, x, ptCurrent.y, NULL);
  647.  
  648.     nCount = (UINT)(pnCurDelta - pnDxWidths);   // number of characters output
  649.     return sizeExtent;
  650. }
  651.  
  652. BOOL CPreviewDC::TextOut(int x, int y, LPCTSTR lpszString, int nCount)
  653. {
  654.     return ExtTextOut(x, y, 0, NULL, lpszString, nCount, NULL);
  655. }
  656.  
  657. BOOL CPreviewDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lpRect,
  658.     LPCTSTR lpszString, UINT nCount, LPINT lpDxWidths)
  659. {
  660.     ASSERT(m_hDC != NULL);
  661.     ASSERT(m_hAttribDC != NULL);
  662.     ASSERT(lpszString != NULL);
  663.     ASSERT(lpDxWidths == NULL ||
  664.             AfxIsValidAddress(lpDxWidths, sizeof(int) * nCount, FALSE));
  665.     ASSERT(AfxIsValidAddress(lpszString, nCount, FALSE));
  666.  
  667.     int* pDeltas = NULL;
  668.     LPTSTR pOutputString = NULL;
  669.     int nRightFixup = 0;
  670.  
  671.     if (lpDxWidths == NULL)
  672.     {
  673.         if (nCount == 0)    // Do nothing
  674.             return TRUE;
  675.  
  676.         TRY
  677.         {
  678.             pDeltas = new int[nCount];
  679.             pOutputString = new TCHAR[nCount];
  680.         }
  681.         CATCH_ALL(e)
  682.         {
  683.             delete[] pDeltas;  // in case it was allocated
  684.             // Note: DELETE_EXCEPTION(e) not required
  685.             return FALSE;   // Could not allocate buffer, cannot display
  686.         }
  687.         END_CATCH_ALL
  688.  
  689.         ComputeDeltas(x, (LPTSTR)lpszString, nCount, FALSE, 0, NULL, 0,
  690.                                         pOutputString, pDeltas, nRightFixup);
  691.  
  692.         lpDxWidths = pDeltas;
  693.         lpszString = pOutputString;
  694.     }
  695.  
  696.     BOOL bSuccess = ::ExtTextOut(m_hDC, x, y, nOptions, lpRect, lpszString,
  697.                                                         nCount, lpDxWidths);
  698.     if (nRightFixup != 0 && bSuccess && (GetTextAlign() & TA_UPDATECP))
  699.     {
  700.         CPoint pt;
  701.         ::GetCurrentPositionEx(m_hDC, &pt);
  702.         MoveTo(pt.x - nRightFixup, pt.y);
  703.     }
  704.     delete[] pDeltas;
  705.     delete[] pOutputString;
  706.  
  707.     return bSuccess;
  708. }
  709.  
  710. CSize CPreviewDC::TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount,
  711.     int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin)
  712. {
  713.     ASSERT(m_hAttribDC != NULL);
  714.     ASSERT(m_hDC != NULL);
  715.     ASSERT(lpszString != NULL);
  716.     ASSERT(AfxIsValidAddress(lpszString, nCount, FALSE));
  717.     ASSERT(lpnTabStopPositions == NULL ||
  718.             AfxIsValidAddress(lpnTabStopPositions, sizeof(int) * nTabPositions,
  719.                 FALSE));
  720.  
  721.     if (nCount <= 0)
  722.         return 0;         // nCount is zero, there is nothing to print
  723.  
  724.     int* pDeltas = NULL;
  725.     LPTSTR pOutputString = NULL;
  726.     int nRightFixup;
  727.  
  728.     TRY
  729.     {
  730.         pDeltas = new int[nCount];
  731.         pOutputString = new TCHAR[nCount];
  732.     }
  733.     CATCH_ALL(e)
  734.     {
  735.         delete[] pDeltas;
  736.         // Note: DELETE_EXCEPTION(e) not required
  737.         return 0;           // signify error
  738.     }
  739.     END_CATCH_ALL
  740.  
  741.     UINT uCount = nCount;
  742.     CSize sizeFinalExtent = ComputeDeltas(x, lpszString, uCount, TRUE,
  743.                             nTabPositions, lpnTabStopPositions, nTabOrigin,
  744.                             pOutputString, pDeltas, nRightFixup);
  745.  
  746.     BOOL bSuccess = ExtTextOut(x, y, 0, NULL, pOutputString, uCount, pDeltas);
  747.  
  748.     delete[] pDeltas;
  749.     delete[] pOutputString;
  750.  
  751.     if (bSuccess && (GetTextAlign() & TA_UPDATECP))
  752.     {
  753.         CPoint pt;
  754.         ::GetCurrentPositionEx(m_hDC, &pt);
  755.         MoveTo(pt.x - nRightFixup, pt.y);
  756.     }
  757.  
  758.     return sizeFinalExtent;
  759. }
  760.  
  761. // This one is too complicated to do character-by-character output positioning
  762. // All we really need to do here is mirror the current position
  763. int CPreviewDC::DrawText(LPCTSTR lpszString, int nCount, LPRECT lpRect,
  764.     UINT nFormat)
  765. {
  766.     ASSERT(m_hAttribDC != NULL);
  767.     ASSERT(m_hDC != NULL);
  768.     ASSERT(lpszString != NULL);
  769.     ASSERT(lpRect != NULL);
  770.     ASSERT(AfxIsValidAddress(lpRect, sizeof(RECT)));
  771.     ASSERT(nCount == -1 ?
  772.         AfxIsValidString(lpszString) :
  773.         AfxIsValidAddress(lpszString, nCount, FALSE));
  774.  
  775.     int retVal = ::DrawText(m_hDC, lpszString, nCount, lpRect, nFormat);
  776.  
  777.     CPoint pos;
  778.     ::GetCurrentPositionEx(m_hDC, &pos);
  779.     ::MoveToEx(m_hAttribDC, pos.x, pos.y, NULL);
  780.     return retVal;
  781. }
  782.  
  783. BOOL CPreviewDC::GrayString(CBrush*,
  784.                 BOOL (CALLBACK *)(HDC, LPARAM, int),
  785.                     LPARAM lpData, int nCount, int x, int y, int, int)
  786. {
  787.     TRACE0("TextOut() substituted for GrayString() in Print Preview.\n");
  788.     return TextOut(x, y, (LPCTSTR)lpData, nCount);
  789. }
  790.  
  791. int CPreviewDC::Escape(int nEscape, int nCount, LPCSTR lpszInData, void* lpOutData)
  792. {
  793.     // The tact here is to NOT allow any of the document control escapes
  794.     // to be passed through.  Elimination of StartDoc and EndDoc should
  795.     // eliminate anything actually going to the printer.  Also anything
  796.     // that actually draws something will be filtered.
  797.  
  798.     ASSERT(m_hAttribDC != NULL);
  799.  
  800.     switch (nEscape)
  801.     {
  802.     case NEXTBAND:
  803.     case SETCOLORTABLE:
  804.     case GETCOLORTABLE:
  805.     case FLUSHOUTPUT:
  806.     case DRAFTMODE:
  807.     case QUERYESCSUPPORT:
  808.     case GETPHYSPAGESIZE:
  809.     case GETPRINTINGOFFSET:
  810.     case GETSCALINGFACTOR:
  811.     case GETPENWIDTH:
  812.     case SETCOPYCOUNT:
  813.     case SELECTPAPERSOURCE:
  814.     case GETTECHNOLOGY:
  815.     case SETLINECAP:
  816.     case SETLINEJOIN:
  817.     case SETMITERLIMIT:
  818.     case BANDINFO:
  819.     case GETVECTORPENSIZE:
  820.     case GETVECTORBRUSHSIZE:
  821.     case ENABLEDUPLEX:
  822.     case GETSETPAPERBINS:
  823.     case GETSETPRINTORIENT:
  824.     case ENUMPAPERBINS:
  825.     case SETDIBSCALING:
  826.     case ENUMPAPERMETRICS:
  827.     case GETSETPAPERMETRICS:
  828.     case GETEXTENDEDTEXTMETRICS:
  829.     case GETEXTENTTABLE:
  830.     case GETPAIRKERNTABLE:
  831.     case GETTRACKKERNTABLE:
  832.     case ENABLERELATIVEWIDTHS:
  833.     case ENABLEPAIRKERNING:
  834.     case SETKERNTRACK:
  835.     case SETALLJUSTVALUES:
  836.     case SETCHARSET:
  837.     case SET_BACKGROUND_COLOR:
  838.     case SET_SCREEN_ANGLE:
  839.     case SET_SPREAD:
  840.         return ::Escape(m_hAttribDC, nEscape, nCount, lpszInData, lpOutData);
  841.  
  842.     default:
  843.         return 0;
  844.     }
  845. }
  846.  
  847. void CPreviewDC::MirrorMappingMode(BOOL bCompute)
  848. {
  849.     ASSERT(m_hAttribDC != NULL);
  850.     if (bCompute)
  851.     {
  852.         //
  853.         // The following formula is used to compute the screen's viewport extent
  854.         // From the printer and screen information and the Printer's Viewport
  855.         // Extents.  (Note:  This formula is used twice, once for horizontal
  856.         // and once for vertical)
  857.         //
  858.         // It is assumed that the Window Extents are maintained as equal.
  859.         //
  860.         //                  m * LogPixPerInch(Screen) * VpExt(Printer)
  861.         // VpExt(Screen) = -------------------------------------------------
  862.         //                          n * LogPixPerInch(Printer)
  863.         //
  864.         // Where m/n is the scaling factor.  (m/n > 1 is expansion)
  865.         //
  866.  
  867.         VERIFY(::GetViewportExtEx(m_hAttribDC, &m_sizeVpExt));
  868.         VERIFY(::GetWindowExtEx(m_hAttribDC, &m_sizeWinExt));
  869.  
  870.         while (m_sizeWinExt.cx > -0x4000 && m_sizeWinExt.cx < 0x4000 &&
  871.                m_sizeVpExt.cx  > -0x4000 && m_sizeVpExt.cx  < 0x4000)
  872.         {
  873.             m_sizeWinExt.cx <<= 1;
  874.             m_sizeVpExt.cx  <<= 1;
  875.         }
  876.  
  877.         while (m_sizeWinExt.cy > -0x4000 && m_sizeWinExt.cy < 0x4000 &&
  878.                m_sizeVpExt.cy  > -0x4000 && m_sizeVpExt.cy  < 0x4000)
  879.         {
  880.             m_sizeWinExt.cy <<= 1;
  881.             m_sizeVpExt.cy  <<= 1;
  882.         }
  883.  
  884.         long lTempExt = _AfxMultMultDivDiv(m_sizeVpExt.cx,
  885.             m_nScaleNum, afxData.cxPixelsPerInch,
  886.             m_nScaleDen, ::GetDeviceCaps(m_hAttribDC, LOGPIXELSX));
  887.  
  888.         ASSERT(m_sizeWinExt.cx != 0);
  889.         m_sizeVpExt.cx = (int)lTempExt;
  890.  
  891.         lTempExt = _AfxMultMultDivDiv(m_sizeVpExt.cy,
  892.             m_nScaleNum, afxData.cyPixelsPerInch,
  893.             m_nScaleDen, ::GetDeviceCaps(m_hAttribDC, LOGPIXELSY));
  894.  
  895.         ASSERT(m_sizeWinExt.cy != 0);
  896.         m_sizeVpExt.cy = (int)lTempExt;
  897.     }
  898.  
  899.     if (m_hDC != NULL)
  900.     {
  901.         ::SetMapMode(m_hDC, MM_ANISOTROPIC);
  902.         ::SetWindowExtEx(m_hDC, m_sizeWinExt.cx, m_sizeWinExt.cy, NULL);
  903.         ::SetViewportExtEx(m_hDC, m_sizeVpExt.cx, m_sizeVpExt.cy, NULL);
  904.  
  905.         // Now that the Logical Units are synchronized, we can set the Viewport Org
  906.         MirrorViewportOrg();
  907.     }
  908. }
  909.  
  910. void CPreviewDC::MirrorViewportOrg()
  911. {
  912.     if (m_hAttribDC == NULL || m_hDC == NULL)
  913.         return;
  914.  
  915.     CPoint ptVpOrg;
  916.     VERIFY(::GetViewportOrgEx(m_hAttribDC, &ptVpOrg));
  917.     PrinterDPtoScreenDP(&ptVpOrg);
  918.     ptVpOrg += m_sizeTopLeft;
  919.     ::SetViewportOrgEx(m_hDC, ptVpOrg.x, ptVpOrg.y, NULL);
  920.  
  921.     CPoint ptWinOrg;
  922.     VERIFY(::GetWindowOrgEx(m_hAttribDC, &ptWinOrg));
  923.     ::SetWindowOrgEx(m_hDC, ptWinOrg.x, ptWinOrg.y, NULL);
  924. }
  925.  
  926. void CPreviewDC::SetTopLeftOffset(CSize sizeTopLeft)
  927. {
  928.     ASSERT(m_hAttribDC != NULL);
  929.     m_sizeTopLeft = sizeTopLeft;
  930.     MirrorViewportOrg();
  931. }
  932.  
  933. void CPreviewDC::ClipToPage()
  934. {
  935.     ASSERT(m_hAttribDC != NULL);
  936.     ASSERT(m_hDC != NULL);
  937.     // Create a rect in Screen Device coordinates that is one pixel larger
  938.     // on all sides than the actual page.  This is to hide the fact that
  939.     // the printer to screen mapping mode is approximate and may result
  940.     // in rounding error.
  941.  
  942.     CPoint pt(::GetDeviceCaps(m_hAttribDC, HORZRES),
  943.                 ::GetDeviceCaps(m_hAttribDC, VERTRES));
  944.     PrinterDPtoScreenDP(&pt);
  945.  
  946.     // Set the screen dc to MM_TEXT and no WindowOrg for the interesection
  947.  
  948.     ::SetMapMode(m_hDC, MM_TEXT);
  949.     ::SetWindowOrgEx(m_hDC, 0, 0, NULL);
  950.     ::SetViewportOrgEx(m_hDC, m_sizeTopLeft.cx, m_sizeTopLeft.cy, NULL);
  951.     ::IntersectClipRect(m_hDC, -1, -1, pt.x + 2, pt.y + 2);
  952.  
  953.     // Resynchronize the mapping mode
  954.     MirrorMappingMode(FALSE);
  955. }
  956.  
  957. // these conversion functions can be used without an attached screen DC
  958.  
  959. void CPreviewDC::PrinterDPtoScreenDP(LPPOINT lpPoint) const
  960. {
  961.     ASSERT(m_hAttribDC != NULL);
  962.  
  963.     CSize sizePrinterVpExt;
  964.     VERIFY(::GetViewportExtEx(m_hAttribDC, &sizePrinterVpExt));
  965.     CSize sizePrinterWinExt;
  966.     VERIFY(::GetWindowExtEx(m_hAttribDC, &sizePrinterWinExt));
  967.  
  968.     long xScreen = _AfxMultMultDivDiv(lpPoint->x,
  969.         sizePrinterWinExt.cx, m_sizeVpExt.cx,
  970.         sizePrinterVpExt.cx, m_sizeWinExt.cx);
  971.  
  972.     lpPoint->x = (int)xScreen;
  973.  
  974.     long yScreen = _AfxMultMultDivDiv(lpPoint->y,
  975.         sizePrinterWinExt.cy, m_sizeVpExt.cy,
  976.         sizePrinterVpExt.cy, m_sizeWinExt.cy);
  977.  
  978.     lpPoint->y = (int)yScreen;
  979. }
  980.  
  981.  
  982. #ifdef AFX_INIT_SEG
  983. #pragma code_seg(AFX_INIT_SEG)
  984. #endif
  985.  
  986. IMPLEMENT_DYNAMIC(CPreviewDC, CDC)
  987.  
  988. /////////////////////////////////////////////////////////////////////////////
  989.