home *** CD-ROM | disk | FTP | other *** search
/ Windows Graphics Programming / Feng_Yuan_Win32_GDI_DirectX.iso / Samples / include / FontText.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-05-11  |  23.7 KB  |  1,012 lines

  1. //-----------------------------------------------------------------------------------//
  2. //              Windows Graphics Programming: Win32 GDI and DirectDraw               //
  3. //                             ISBN  0-13-086985-6                                   //
  4. //                                                                                   //
  5. //  Written            by  Yuan, Feng                             www.fengyuan.com   //
  6. //  Copyright (c) 2000 by  Hewlett-Packard Company                www.hp.com         //
  7. //  Published          by  Prentice Hall PTR, Prentice-Hall, Inc. www.phptr.com      //
  8. //                                                                                   //
  9. //  FileName   : fonttext.cpp                                                         //
  10. //  Description: Generic font and text routines and classes                          //
  11. //  Version    : 1.00.000, May 31, 2000                                              //
  12. //-----------------------------------------------------------------------------------//
  13.  
  14. #define STRICT
  15. #define WIN32_LEAN_AND_MEAN
  16.  
  17. #pragma pack(push, 4)
  18. #include <windows.h>
  19. #pragma pack(pop)
  20.  
  21. #include <tchar.h>
  22. #include <assert.h>
  23. #include <math.h>
  24. #include <stdio.h>
  25.  
  26. #include "fonttext.h"
  27.  
  28. // convert point size to logical coordinate space size
  29. int PointSizetoLogical(HDC hDC, int points, int divisor)
  30. {
  31.     POINT P[2] = // two POINTs in device space whose distance is the needed height
  32.     {
  33.         { 0, 0 },
  34.         { 0, ::GetDeviceCaps(hDC, LOGPIXELSY) * points } 
  35.     };
  36.  
  37.     DPtoLP(hDC, P, 2); // map device coordinate to logical size
  38.     
  39.     return abs(P[1].y - P[0].y) / 72 / divisor;
  40. }
  41.  
  42.  
  43. // Create a font as large as EM square size for accurate metrics
  44. HFONT CreateReferenceFont(HFONT hFont, int & emsquare)
  45. {
  46.     LOGFONT           lf;
  47.     OUTLINETEXTMETRIC otm[3]; // big enough for the strings
  48.  
  49.     HDC hDC      = GetDC(NULL);
  50.     HGDIOBJ hOld = SelectObject(hDC, hFont);
  51.     int size = GetOutlineTextMetrics(hDC, sizeof(otm), otm);
  52.     SelectObject(hDC, hOld);
  53.     ReleaseDC(NULL, hDC);
  54.  
  55.     if ( size )                                // TrueType
  56.     {
  57.         GetObject(hFont, sizeof(lf), & lf);
  58.  
  59.         emsquare    = otm[0].otmEMSquare; // get EM square size
  60.         lf.lfHeight = - emsquare;          // font size for 1:1 mapping
  61.         lf.lfWidth  = 0;                  // original proportion
  62.  
  63.         return CreateFontIndirect(&lf);
  64.     }
  65.     else
  66.         return NULL;
  67. }
  68.  
  69.  
  70. // justify text string within a left..right margin
  71. BOOL TextOutJust(HDC hDC, int left, int right, int y, LPCTSTR lpStr, int nCount, bool bAllowNegative, TCHAR cBreakChar)
  72. {
  73.     SIZE size;
  74.     
  75.     SetTextJustification(hDC, 0, 0);
  76.  
  77.     GetTextExtentPoint32(hDC, lpStr, nCount, & size);
  78.  
  79.     int nBreak = 0;
  80.     for (int i=0; i<nCount; i++)
  81.         if ( lpStr[i]==cBreakChar )
  82.             nBreak ++;
  83.  
  84.     int breakextra = right - left - size.cx;
  85.     
  86.     if ( (breakextra<0) && ! bAllowNegative )
  87.         breakextra =0;
  88.  
  89.     SetTextJustification(hDC, breakextra, nBreak);
  90.  
  91.     return TextOut(hDC, left, y, lpStr, nCount);
  92. }
  93.  
  94.  
  95. // ABC extent of a text string
  96. // ( A0, B0, C0 ) + ( A1, B1, C1 ) = ( A0, B0+C0+A1+B1, C1 }
  97. BOOL GetTextABCExtent(HDC hDC, LPCTSTR lpString, int cbString, long * pHeight, ABC * pABC)
  98. {
  99.     SIZE size;
  100.  
  101.     if ( ! GetTextExtentPoint32(hDC, lpString, cbString, & size) )
  102.         return FALSE;
  103.  
  104.     * pHeight  = size.cy;
  105.     pABC->abcB = size.cx;
  106.  
  107.     ABC abc; 
  108.     GetCharABCWidths(hDC, lpString[0],          lpString[0],          & abc); // first
  109.     pABC->abcB -= abc.abcA;
  110.     pABC->abcA  = abc.abcA;
  111.  
  112.     GetCharABCWidths(hDC, lpString[cbString-1], lpString[cbString-1], & abc); // last
  113.     pABC->abcB -= abc.abcC;
  114.     pABC->abcC  = abc.abcC;
  115.  
  116.     return TRUE;
  117. }
  118.  
  119.  
  120. BOOL GetOpaqueBox(HDC hDC, LPCTSTR lpString, int cbString, RECT * pRect, int x, int y)
  121. {
  122.     long height;
  123.     ABC  abc;
  124.  
  125.     if ( ! GetTextABCExtent(hDC, lpString, cbString, & height, & abc) )
  126.         return FALSE;
  127.  
  128.     switch ( GetTextAlign(hDC) & (TA_LEFT | TA_RIGHT | TA_CENTER) )
  129.     {
  130.         case TA_LEFT    : break;
  131.         case TA_RIGHT   : x -=   abc.abcB; break;
  132.         case TA_CENTER  : x -= abc.abcB/2; break;
  133.         default:          assert(false);
  134.     }
  135.  
  136.     switch ( GetTextAlign(hDC) & (TA_TOP | TA_BASELINE | TA_BOTTOM) )
  137.     {
  138.         case TA_TOP     : break;
  139.         case TA_BOTTOM  : y = - height; break;
  140.         case TA_BASELINE: 
  141.             {
  142.                 TEXTMETRIC tm;
  143.                 GetTextMetrics(hDC, & tm);
  144.                 y = - tm.tmAscent;
  145.             }
  146.             break;
  147.         default:          assert(false);
  148.     }
  149.  
  150.     pRect->left   = x + min(abc.abcA, 0);
  151.     pRect->right  = x + abc.abcA + abc.abcB + max(abc.abcC, 0);
  152.     pRect->top    = y;
  153.     pRect->bottom = y + height;
  154.  
  155.     return TRUE;
  156. }
  157.  
  158.  
  159. // Pixel-level precise text alignment
  160. BOOL PreciseTextOut(HDC hDC, int x, int y, LPCTSTR lpString, int cbString)
  161. {
  162.     long height;
  163.     ABC  abc;
  164.  
  165.     if ( GetTextABCExtent(hDC, lpString, cbString, & height, & abc) )
  166.         switch ( GetTextAlign(hDC) & (TA_LEFT | TA_RIGHT | TA_CENTER) )
  167.         {
  168.             case TA_LEFT  : x -= abc.abcA;    break;
  169.             case TA_RIGHT : x += abc.abcC;    break;
  170.             case TA_CENTER: x -= (abc.abcA-abc.abcC)/2;  break;
  171.         }
  172.  
  173.     return TextOut(hDC, x, y, lpString, cbString);
  174. }
  175.  
  176.  
  177. KGlyph::~KGlyph(void)
  178. {
  179.     if ( m_pPixels )
  180.     {
  181.         delete [] m_pPixels;
  182.         m_pPixels = NULL;
  183.     }
  184. }
  185.  
  186.  
  187. DWORD KGlyph::GetGlyph(HDC hDC, UINT uChar, UINT uFormat, const MAT2 * pMat2)
  188. {
  189.     MAT2 mat2;
  190.  
  191.     if ( pMat2==NULL )
  192.     {
  193.         memset(&mat2, 0, sizeof(mat2));
  194.         mat2.eM11.value = 1;
  195.         mat2.eM22.value = 1;
  196.         pMat2 = & mat2;
  197.     }
  198.     
  199.     // query size
  200.     m_nDataSize = GetGlyphOutline(hDC, uChar, uFormat, & m_metrics, 0, NULL, pMat2);
  201.  
  202.     if ( (m_nDataSize==0) || (m_nDataSize==GDI_ERROR) )
  203.         return m_nDataSize;
  204.  
  205.     if ( m_pPixels && (m_nDataSize > m_nAllocSize) ) // deallocate if too small
  206.     {
  207.         delete m_pPixels;
  208.         m_pPixels = NULL;
  209.     }
  210.         
  211.     if ( m_pPixels==NULL )
  212.     {
  213.         m_pPixels = new BYTE[m_nDataSize];    // new buffer allocation
  214.         assert(m_pPixels);
  215.         m_nAllocSize   = m_nDataSize;
  216.     }
  217.  
  218.     m_uFormat = uFormat;
  219.     // real data
  220.     m_nDataSize = GetGlyphOutline(hDC, uChar, uFormat, & m_metrics, m_nAllocSize, m_pPixels, pMat2);
  221.  
  222.     return m_nDataSize;
  223. }
  224.  
  225.  
  226. typedef struct
  227. {
  228.     BITMAPINFOHEADER bmiHeader;
  229.     RGBQUAD             bmiColors[256];
  230. }    BITMAPINFO8BPP;
  231.  
  232.  
  233.  
  234. BOOL KGlyph::DrawGlyphROP(HDC hDC, int x, int y, DWORD rop, COLORREF crBack, COLORREF crFore)
  235. {
  236.     int levels;
  237.  
  238.     switch (m_uFormat & 0x0F )
  239.     {
  240.         case GGO_BITMAP:       levels =  2; break;
  241.         case GGO_GRAY2_BITMAP: levels =  5; break;
  242.         case GGO_GRAY4_BITMAP: levels = 17; break;
  243.         case GGO_GRAY8_BITMAP: levels = 65; break;
  244.  
  245.         default:
  246.             return FALSE;
  247.     }
  248.  
  249.     BITMAPINFO8BPP bmi;
  250.     memset(& bmi, 0, sizeof(bmi));
  251.     
  252.     bmi.bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
  253.     bmi.bmiHeader.biWidth    =     m_metrics.gmBlackBoxX;
  254.     bmi.bmiHeader.biHeight   = 0 - m_metrics.gmBlackBoxY; // top-down DIB
  255.     bmi.bmiHeader.biPlanes   = 1;
  256.     
  257.     if ( levels==2 )
  258.     {
  259.         bmi.bmiHeader.biBitCount = 1;
  260.  
  261.         bmi.bmiColors[0].rgbRed   = GetRValue(crBack);
  262.         bmi.bmiColors[0].rgbGreen = GetGValue(crBack);
  263.         bmi.bmiColors[0].rgbBlue  = GetBValue(crBack);
  264.  
  265.         bmi.bmiColors[1].rgbRed   = GetRValue(crFore);
  266.         bmi.bmiColors[1].rgbGreen = GetGValue(crFore);
  267.         bmi.bmiColors[1].rgbBlue  = GetBValue(crFore);
  268.     }
  269.     else
  270.     {
  271.         bmi.bmiHeader.biBitCount = 8;
  272.         
  273. //        KColor back(crBack); back.ToHLS();
  274. //        KColor fore(crFore); fore.ToHLS();
  275.     
  276.         for (int i=0; i<levels; i++)
  277.         {
  278. //            KColor m;
  279.  
  280. //            m.hue        = (back.hue        * (levels-i-1) + fore.hue        * i ) / (levels-1);
  281. //            m.lightness  = (back.lightness  * (levels-i-1) + fore.lightness  * i ) / (levels-1);
  282. //            m.saturation = (back.saturation * (levels-i-1) + fore.saturation * i ) / (levels-1);
  283. //            m.ToRGB();
  284.  
  285. //            bmi.bmiColors[i].rgbRed   = m.red;
  286. //            bmi.bmiColors[i].rgbGreen = m.green;
  287. //            bmi.bmiColors[i].rgbBlue  = m.blue;
  288.  
  289.             bmi.bmiColors[i].rgbRed   = (GetRValue(crBack) * (levels-i-1) + GetRValue(crFore) * i ) / (levels-1);
  290.             bmi.bmiColors[i].rgbGreen = (GetGValue(crBack) * (levels-i-1) + GetGValue(crFore) * i ) / (levels-1);
  291.             bmi.bmiColors[i].rgbBlue  = (GetBValue(crBack) * (levels-i-1) + GetBValue(crFore) * i ) / (levels-1);
  292.         }
  293.     }
  294.  
  295.     return StretchDIBits(hDC, 
  296.                 x, y, m_metrics.gmBlackBoxX, m_metrics.gmBlackBoxY,
  297.                 0, 0, m_metrics.gmBlackBoxX, m_metrics.gmBlackBoxY,
  298.                 m_pPixels,
  299.                 (const BITMAPINFO *) & bmi,
  300.                 DIB_RGB_COLORS, rop);
  301. }
  302.  
  303.  
  304. // assuming TA_LEFT | TA_BASELINE alignment
  305. BOOL KGlyph::DrawGlyph(HDC hDC, int x, int y, int & dx, int & dy)
  306. {
  307.     dx = m_metrics.gmCellIncX; // advancement
  308.     dy = m_metrics.gmCellIncY;
  309.  
  310.     if ( GetBkMode(hDC)==OPAQUE ) // may overlap with next glyph
  311.     {
  312.         // black box is not big enough, need to calculate actual background box
  313.         RECT opaque;
  314.  
  315.         TEXTMETRIC tm;
  316.         GetTextMetrics(hDC, & tm);
  317.  
  318.         opaque.left   = min(x,                        x + m_metrics.gmptGlyphOrigin.x);
  319.         opaque.right  = max(x + m_metrics.gmCellIncX, x + m_metrics.gmptGlyphOrigin.x + (int) m_metrics.gmBlackBoxX);
  320.         opaque.top    = y - tm.tmAscent;
  321.         opaque.bottom = y + tm.tmDescent;
  322.  
  323.         HBRUSH hBackBrush = CreateSolidBrush(GetBkColor(hDC));
  324.         FillRect(hDC, & opaque, hBackBrush);
  325.         DeleteObject(hBackBrush);
  326.     }
  327.  
  328.     // if foreground color, use pen color, otherwise donot change desitnation
  329.  
  330.     HBRUSH hBrush = CreateSolidBrush(GetTextColor(hDC));
  331.     HBRUSH hOld   = (HBRUSH) SelectObject(hDC, hBrush);
  332.  
  333.     BOOL rslt = DrawGlyphROP(hDC, 
  334.                     x + m_metrics.gmptGlyphOrigin.x, 
  335.                     y - m_metrics.gmptGlyphOrigin.y, 
  336.                     0xE20746,                // D^(S&(P^D)) -> if (S) P else D
  337.                     RGB(0,    0,    0),        // 0 -> 0
  338.                     RGB(0xFF, 0xFF, 0xFF));    // 1 -> 1
  339.  
  340.     SelectObject(hDC, hOld);
  341.     DeleteObject(hBrush);
  342.  
  343.     return rslt;
  344. }
  345.  
  346.  
  347.  
  348. FIXED MakeFixed(double value)
  349. {
  350.     long val = (long) (value * 65536);
  351.  
  352.     return * (FIXED *) & val;
  353. }
  354.  
  355.  
  356. BOOL OutlineTextOut(HDC hDC, int x, int y, const TCHAR * str, int count)
  357. {
  358.     if ( count<0 )
  359.         count = _tcslen(str);
  360.  
  361.     KGlyph             glyph;
  362.     KGlyphOutline<512> outline;
  363.  
  364. //    MAT2 mat2;
  365. //    mat2.eM11 = MakeFixed(cos(0.1));
  366. //    mat2.eM12 = MakeFixed(sin(0.1));
  367. //    mat2.eM21 = MakeFixed(-sin(0.1));
  368. //    mat2.eM22 = MakeFixed(cos(0.1));
  369.  
  370.     while ( count>0 )
  371.     {
  372.         if ( glyph.GetGlyph(hDC, * str, GGO_NATIVE/*, & mat2*/)>0 )
  373.             if ( outline.DecodeOutline(glyph) )
  374.                 outline.Draw(hDC, x, y);
  375.  
  376.         x += glyph.m_metrics.gmCellIncX;
  377.         y += glyph.m_metrics.gmCellIncY;
  378.  
  379.         str ++; 
  380.         count --;
  381.     }
  382.  
  383.     return TRUE;
  384. }
  385.  
  386.  
  387. BOOL BitmapTextOut(HDC hDC, int x, int y, const TCHAR * str, int count, int format)
  388. {
  389.     if ( count<0 )
  390.         count = _tcslen(str);
  391.  
  392.     KGlyph glyph;
  393.  
  394.     while ( count>0 )
  395.     {
  396.         int dx=0, dy=0;
  397.  
  398.         if ( glyph.GetGlyph(hDC, * str, format)>0 )
  399.             glyph.DrawGlyph(hDC, x, y, dx, dy);
  400.  
  401.         x += dx;
  402.         y += dy;
  403.  
  404.         str ++; 
  405.         count --;
  406.     }
  407.  
  408.     return TRUE;
  409. }
  410.  
  411.  
  412. BOOL BitmapTextOutROP(HDC hDC, int x, int y, const TCHAR * str, int count, DWORD rop)
  413. {
  414.     if ( count<0 )
  415.         count = _tcslen(str);
  416.  
  417.     KGlyph glyph;
  418.  
  419.     COLORREF crBack = GetBkColor(hDC);
  420.     COLORREF crFore = GetTextColor(hDC);
  421.  
  422.     while ( count>0 )
  423.     {
  424.         if ( glyph.GetGlyph(hDC, * str, GGO_BITMAP)>0 )
  425.             glyph.DrawGlyphROP(hDC, 
  426.                     x + glyph.m_metrics.gmptGlyphOrigin.x, 
  427.                     y - glyph.m_metrics.gmptGlyphOrigin.y, 
  428.                     rop, crBack, crFore);
  429.  
  430.         x += glyph.m_metrics.gmCellIncX;
  431.         y += glyph.m_metrics.gmCellIncY;
  432.  
  433.         str ++; 
  434.         count --;
  435.     }
  436.  
  437.     return TRUE;
  438. }
  439.  
  440.  
  441. /////////////////////////////////////////////////////////////
  442.  
  443. // pixelsize: in logical space
  444. BOOL KTextFormator::SetupPixel(HDC hDC, HFONT hFont, float pixelsize)
  445. {
  446.     OUTLINETEXTMETRIC otm[3]; // big enough for the strings
  447.  
  448.     if ( GetOutlineTextMetrics(hDC, sizeof(otm), otm)==0 )
  449.         return FALSE;
  450.  
  451.     LOGFONT   lf;
  452.  
  453.     GetObject(hFont, sizeof(lf), & lf);
  454.  
  455.     int emsquare= otm[0].otmEMSquare; // get EM square size
  456.     lf.lfHeight = - emsquare;          // font size for 1:1 mapping
  457.     lf.lfWidth  = 0;                  // original proportion
  458.  
  459.     HFONT hRefFont = CreateFontIndirect(&lf);
  460.  
  461.     HDC     hRefDC = CreateCompatibleDC(hDC);
  462.     HGDIOBJ hOld   = SelectObject(hRefDC, hRefFont);
  463.         
  464.     int nCharWidth[MaxCharNo];
  465.  
  466.     GetCharWidth(hRefDC, 0, MaxCharNo-1, nCharWidth);
  467.         
  468.     if ( GetOutlineTextMetrics(hRefDC, sizeof(otm), otm)==0 )
  469.         return FALSE;
  470.  
  471.     SelectObject(hRefDC, hOld);
  472.         
  473.     DeleteObject(hRefDC);
  474.     DeleteObject(hRefFont);
  475.  
  476.     m_fHeight    = otm[0].otmTextMetrics.tmHeight * pixelsize / emsquare;
  477.     m_fLinespace = ( otm[0].otmTextMetrics.tmHeight + otm[0].otmTextMetrics.tmExternalLeading) * pixelsize / emsquare;
  478.     
  479.     for (int i=0; i<MaxCharNo; i++)
  480.         m_fCharWidth[i] = ((float) nCharWidth[i]) * pixelsize / emsquare;
  481.         
  482.     return TRUE;
  483. }
  484.  
  485.  
  486. BOOL KTextFormator::Setup(HDC hDC, HFONT hFont, float pointsize)
  487. {
  488.     return SetupPixel(hDC, hFont, pointsize * GetDeviceCaps(hDC, LOGPIXELSY) / 72);
  489. }
  490.  
  491.  
  492. BOOL KTextFormator::GetTextExtent(HDC hdc, LPCTSTR lpString, int cbString, float & width, float & height)
  493. {
  494.     if ( cbString<=0 )
  495.         cbString = _tcslen(lpString);
  496.  
  497.     width = 0;
  498.         
  499.     for (int i=0; i<cbString; i++)
  500.         width = width + m_fCharWidth[lpString[i]];
  501.  
  502.     height = m_fHeight;
  503.  
  504.     return TRUE;
  505. }
  506.  
  507.  
  508. class KLineBreaker
  509. {
  510.     LPCTSTR m_pText;
  511.     int     m_nLength;
  512.     int        m_nPos;
  513.  
  514.     BOOL SkipWhite(void)
  515.     {
  516.         // skip white space
  517.         while ( (m_nPos<m_nLength) && (m_pText[m_nPos]<=' ') )
  518.             m_nPos ++;
  519.  
  520.         return m_nPos < m_nLength;
  521.     }
  522.  
  523.     // m_pText[m_nPos] is the starting of a word, find its end
  524.     void GetWord(void)
  525.     {
  526.         while ( (m_nPos<m_nLength) && (m_pText[m_nPos]>' ') )
  527.             m_nPos ++;
  528.     }
  529.  
  530.     BOOL Breakable(int pos)
  531.     {
  532.         if ( m_pText[pos]<=' ') // space character is breakable
  533.             return true;
  534.  
  535.         if ( pos && (m_pText[pos-1]<=' ') ) // having space before it
  536.             return true;
  537.  
  538.         if ( pos && (m_pText[pos-1]=='-') ) // having hypen before it
  539.             return true;
  540.  
  541.         return false;
  542.     }
  543.  
  544. public:
  545.  
  546.     float textwidth, textheight;
  547.  
  548.     KLineBreaker(LPCTSTR pText, int nCount)
  549.     {
  550.         m_pText   = pText;
  551.         m_nPos    = 0;
  552.  
  553.         if ( nCount<=0 )
  554.             m_nLength = _tcslen(m_pText);
  555.         else
  556.             m_nLength = nCount;
  557.     }
  558.  
  559.     BOOL GetLine(KTextFormator & formator, HDC hDC, int width, int & begin, int & end)
  560.     {
  561.         const float epsilon = (float) 0.001;
  562.  
  563.         if ( ! SkipWhite() )
  564.             return FALSE;
  565.     
  566.         begin = m_nPos; // first no white space to diplay
  567.         
  568.         // add words until the line is too long
  569.         while ( SkipWhite() )
  570.         {
  571.             // first end of word
  572.             GetWord();
  573.  
  574.             formator.GetTextExtent(hDC, m_pText + begin, m_nPos - begin, textwidth, textheight);
  575.  
  576.             // break out if it's too long
  577.             if ( textwidth >= (width-epsilon) )
  578.                 break;
  579.         }
  580.  
  581.         if ( textwidth > width )
  582.             for (int p=m_nPos-1; p>begin; p--) // find a place to break a word into two
  583.                 if ( Breakable(p) )
  584.                 {   // can we fit now ?
  585.                     formator.GetTextExtent(hDC, m_pText + begin, p - begin, textwidth, textheight);
  586.                     if ( textwidth<=(width+epsilon) )
  587.                     {
  588.                         m_nPos = p;
  589.                         break;
  590.                     }
  591.                 }
  592.  
  593.         end = m_nPos;
  594.  
  595.         return TRUE;
  596.     }
  597. };
  598.  
  599.  
  600. BOOL KTextFormator::TextOut(HDC hDC, int x, int y, LPCTSTR pString, int nCount)
  601. {
  602.     int Dx[MAX_PATH];
  603.  
  604.     int lastx = 0;
  605.     float sum = 0.0;
  606.  
  607.     int sumdx = 0;
  608.     for (int i=0; i<nCount; i++)
  609.     {
  610.         sum = sum + m_fCharWidth[pString[i]];
  611.  
  612.         int newx = (int) (sum + 0.5);
  613.         Dx[i] = newx - lastx;
  614.         lastx = newx;
  615.  
  616.         sumdx += Dx[i];
  617.     }
  618.  
  619. //    TCHAR temp[64];
  620. //    sprintf(temp, "sum: %8.5f sumdx : %d\n", sum, sumdx);
  621. //    OutputDebugString(temp);
  622.  
  623.     return ExtTextOut(hDC, x, y, 0, NULL, pString, nCount, Dx);
  624. }
  625.  
  626.  
  627. DWORD KTextFormator::DrawText(HDC hDC, LPCTSTR pString, int nCount, const RECT * pRect, UINT uFormat)
  628. {
  629.     if ( pString==NULL )
  630.         return 0;
  631.  
  632.     KLineBreaker breaker(pString, nCount);
  633.  
  634.     int   x     = pRect->left;
  635.     float y     = (float) pRect->top;
  636.     int width = pRect->right - pRect->left;
  637.     int begin, end;
  638.  
  639.     while ( breaker.GetLine(* this, hDC, width, begin, end) )
  640.     {
  641.         while ( (end>begin) && (pString[end-1]<=' ') )
  642.             end --;
  643.  
  644. //        TCHAR mess[64];
  645. //        sprintf(mess, "width %8.5f\n", breaker.textwidth);
  646. //        OutputDebugString(mess);
  647.  
  648.         TextOut(hDC, x, (int)(y+0.5), pString + begin, end - begin);
  649.  
  650.         y += breaker.textheight;
  651.     }
  652.     
  653.     return 0;
  654. }
  655.  
  656.  
  657. BOOL ColorText(HDC hDC, int x, int y, LPCTSTR pString, int nCount, HBRUSH hFore)
  658. {
  659.     HGDIOBJ hOld      = SelectObject(hDC, hFore);
  660.  
  661.     RECT rect;
  662.     GetOpaqueBox(hDC, pString, nCount, & rect, x, y);
  663.     PatBlt(hDC, rect.left, rect.top, 
  664.         rect.right-rect.left, rect.bottom - rect.top, PATINVERT);
  665.  
  666.     int      oldBk    = SetBkMode(hDC, TRANSPARENT);
  667.     COLORREF oldColor = SetTextColor(hDC, RGB(0, 0, 0));
  668.     
  669.     TextOut(hDC, x, y, pString, nCount);
  670.     SetBkMode(hDC, oldBk);
  671.     SetTextColor(hDC, oldColor);
  672.     
  673.     BOOL rslt = PatBlt(hDC, rect.left, rect.top, 
  674.                     rect.right-rect.left, rect.bottom - rect.top, PATINVERT);
  675.  
  676.     SelectObject(hDC, hOld);
  677.  
  678.     return rslt;
  679. }
  680.  
  681.  
  682. BOOL BitmapText(HDC hDC, int x, int y, LPCTSTR pString, int nCount, HBITMAP hBmp)
  683. {
  684.     RECT rect;
  685.     GetOpaqueBox(hDC, pString, nCount, & rect, x, y);
  686.  
  687.     HDC hMemDC = CreateCompatibleDC(hDC);
  688.     HGDIOBJ hOld = SelectObject(hMemDC, hBmp);
  689.  
  690.     BitBlt(hDC, rect.left, rect.top, 
  691.                 rect.right-rect.left, 
  692.                 rect.bottom - rect.top, hMemDC, 0, 0, SRCINVERT);
  693.  
  694.     int      oldBk    = SetBkMode(hDC, TRANSPARENT);
  695.     COLORREF oldColor = SetTextColor(hDC, RGB(0, 0, 0));
  696.     
  697.     TextOut(hDC, x, y, pString, nCount);
  698.     SetBkMode(hDC, oldBk);
  699.     SetTextColor(hDC, oldColor);
  700.     
  701.     BOOL rslt = BitBlt(hDC, rect.left, rect.top, 
  702.                     rect.right-rect.left, rect.bottom - rect.top, 
  703.                     hMemDC, 0, 0, SRCINVERT);
  704.  
  705.     SelectObject(hMemDC, hOld);
  706.     DeleteObject(hMemDC);
  707.  
  708.     return rslt;
  709. }
  710.  
  711.  
  712. BOOL BitmapText2(HDC hDC, int x, int y, LPCTSTR pString, int nCount, HBITMAP hBmp)
  713. {
  714.     RECT rect;
  715.     GetOpaqueBox(hDC, pString, nCount, & rect, x, y);
  716.  
  717.     HDC hMemDC = CreateCompatibleDC(hDC);
  718.     HGDIOBJ hOld = SelectObject(hMemDC, hBmp);
  719.  
  720.     BeginPath(hDC);
  721.     SetBkMode(hDC, TRANSPARENT);
  722.     TextOut(hDC, x, y, pString, nCount);
  723.     EndPath(hDC);
  724.     SelectClipPath(hDC, RGN_COPY);
  725.  
  726.     BOOL rslt = BitBlt(hDC, rect.left, rect.top, 
  727.                 rect.right-rect.left, 
  728.                 rect.bottom - rect.top, hMemDC, 0, 0, SRCCOPY);
  729.  
  730.     SelectObject(hMemDC, hOld);
  731.     DeleteObject(hMemDC);
  732.  
  733.     return rslt;
  734. }
  735.  
  736. BOOL OffsetTextOut(HDC hDC, int x, int y, LPCTSTR pStr, int nCount, 
  737.                    int dx1, int dy1, COLORREF cr1,
  738.                    int dx2, int dy2, COLORREF cr2)
  739. {
  740.     COLORREF cr = GetTextColor(hDC);
  741.     int         bk = GetBkMode(hDC);
  742.  
  743.     if ( bk==OPAQUE )
  744.     {
  745.         RECT rect;
  746.  
  747.         GetOpaqueBox(hDC, pStr, nCount, & rect, x, y);
  748.  
  749.         rect.left  += min(min(dx1, dx2), 0);
  750.         rect.right += max(max(dx1, dx2), 0);
  751.         rect.top   += min(min(dy1, dy2), 0);
  752.         rect.bottom+= max(max(dy1, dy2), 0);
  753.  
  754.         ExtTextOut(hDC, x, y, ETO_OPAQUE, & rect, NULL, 0, NULL);
  755.     }
  756.  
  757.     SetBkMode(hDC, TRANSPARENT);
  758.  
  759.     if ( (dx1!=0) || (dy1!=0) )
  760.     {
  761.         SetTextColor(hDC, cr1);
  762.         TextOut(hDC, x + dx1, y + dy1, pStr, nCount);
  763.     }
  764.  
  765.     if ( (dx1!=0) || (dy1!=0) )
  766.     {
  767.         SetTextColor(hDC, cr2);
  768.         TextOut(hDC, x + dx2, y + dy2, pStr, nCount);
  769.     }
  770.  
  771.     SetTextColor(hDC, cr);
  772.  
  773.     BOOL rslt = TextOut(hDC, x, y, pStr, nCount);
  774.     SetBkMode(hDC, bk);
  775.  
  776.     return rslt;
  777. }
  778.  
  779.  
  780. double dis(double x0, double y0, double x1, double y1)
  781. {
  782.     x1 -= x0;
  783.     y1 -= y0;
  784.  
  785.     return sqrt( x1 * x1 + y1 * y1 );
  786. }
  787.  
  788. const double pi = 3.141592654;
  789.  
  790.  
  791. BOOL DrawChar(HDC hDC, double x0, double y0, double x1, double y1, TCHAR ch)
  792. {
  793.     x1 -= x0;
  794.     y1 -= y0;
  795.  
  796.     int escapement = 0;
  797.  
  798.     if ( (x1<0.01) && (x1>-0.01) )
  799.         if ( y1>0 )
  800.             escapement = 2700;
  801.         else
  802.             escapement =  900;
  803.     else
  804.     {
  805.         double angle = atan(-y1/x1);
  806.         
  807.         escapement = (int) ( angle * 180 / pi * 10 + 0.5);
  808.  
  809. //        TCHAR temp[MAX_PATH];
  810. //        sprintf(temp, "%8.5f %8.5f %8.5f %8.5f -> %d\n", x0, y0, x1, y1, escapement);
  811. //        OutputDebugString(temp);
  812.     }
  813.  
  814.     LOGFONT lf;
  815.     GetObject(GetCurrentObject(hDC, OBJ_FONT), sizeof(lf), &lf);
  816.  
  817.     if ( lf.lfEscapement != escapement )
  818.     {
  819.         lf.lfEscapement = escapement;
  820.  
  821.         HFONT hFont = CreateFontIndirect(&lf);
  822.  
  823.         if ( hFont==NULL )
  824.             return FALSE;
  825.  
  826.         DeleteObject(SelectObject(hDC, hFont));
  827.     }
  828.     
  829.     TextOut(hDC, (int)x0, (int)y0, &ch, 1);
  830.  
  831.     return TRUE;
  832. }
  833.  
  834.  
  835. void PathTextOut(HDC hDC, LPCTSTR pString, POINT point[], int no)
  836. {
  837. //    MoveToEx(hDC, point[0].x, point[0].y, NULL);
  838.  
  839. //    for (int i=1; i<no; i++)
  840. //        LineTo(hDC, point[i].x, point[i].y);
  841.  
  842.     double x0 = point[0].x;
  843.     double y0 = point[0].y;
  844.  
  845.     for (int i=1; i<no; i++)
  846.     {
  847.         double x1 = point[i].x;
  848.         double y1 = point[i].y;
  849.  
  850.         double curlen = dis(x0, y0, x1, y1);
  851.  
  852.         while ( true )
  853.         {
  854.             int length;
  855.             GetCharWidth(hDC, * pString, * pString, & length);
  856.  
  857.             if ( curlen < length )
  858.                 break;
  859.  
  860.             double x00 = x0; 
  861.             double y00 = y0;
  862.  
  863.             x0 += (x1-x0) * length / curlen;
  864.             y0 += (y1-y0) * length / curlen;
  865.  
  866.             DrawChar(hDC, x00, y00, x0, y0, * pString);
  867.  
  868.             curlen -= length;
  869.             pString ++;
  870.  
  871.             if ( * pString==0 )
  872.             {
  873.                 i = no;
  874.                 break;
  875.             }
  876.         }
  877.     }
  878. }
  879.  
  880.  
  881.  
  882. BOOL PathTextOut(HDC hDC, LPCTSTR pString)
  883. {
  884.     if ( ! FlattenPath(hDC) ) // conver to polyline
  885.         return FALSE;
  886.  
  887.     POINT * pp;
  888.     BYTE  * pf;
  889.  
  890.     int no = GetPath(hDC, NULL, NULL, 0); // query point no
  891.  
  892.     if ( no<2 )    // at least two points
  893.         return FALSE;
  894.  
  895.     pp = new POINT[no];
  896.     pf = new  BYTE[no];
  897.  
  898.     no = GetPath(hDC, pp, pf, no); // real data
  899.  
  900.     PathTextOut(hDC, pString, pp, no); // aligning
  901.  
  902.     delete pp;
  903.     delete pf;
  904.  
  905.     return TRUE;
  906. }
  907.  
  908.  
  909. void KTextBitmap::Blur(void)
  910. {
  911.     Average<4>(m_pBits, m_width*4, m_width, m_height);
  912. }
  913.  
  914.  
  915.  
  916. BOOL KTextBitmap::RenderText(HDC hDC, int x, int y, const TCHAR * pString, int nCount)
  917. {
  918.     HGDIOBJ hOldFont = SelectObject(m_hMemDC, GetCurrentObject(hDC, OBJ_FONT));
  919.     SetTextColor(m_hMemDC, GetTextColor(hDC));
  920.     SetBkMode   (m_hMemDC, GetBkMode(hDC));
  921.     SetBkColor  (m_hMemDC, GetBkColor(hDC));
  922.     
  923.     SetTextAlign(m_hMemDC, TA_LEFT | TA_TOP);
  924.     BOOL rslt = TextOut(m_hMemDC, m_dx+x, m_dy+y, pString, nCount);
  925.     
  926.     SelectObject(m_hMemDC, hOldFont);
  927.  
  928.     return rslt;
  929. }
  930.  
  931.  
  932. BOOL KTextBitmap::Convert(HDC hDC, LPCTSTR pString, int nCount, int extra)
  933. {
  934.     RECT rect;
  935.  
  936.     ReleaseBitmap();
  937.  
  938.     SaveDC(hDC);
  939.  
  940.     SetTextAlign(hDC, TA_LEFT | TA_TOP);
  941.     GetOpaqueBox(hDC, pString, nCount, & rect, 0, 0);
  942.  
  943.     m_width  = rect.right - rect.left + extra * 2;
  944.     m_height = rect.bottom - rect.top + extra * 2;
  945.  
  946.     BITMAPINFOHEADER bmih;
  947.     memset(& bmih, 0, sizeof(bmih));
  948.     bmih.biSize     = sizeof(BITMAPINFOHEADER);
  949.     bmih.biWidth    = m_width;
  950.     bmih.biHeight   = m_height;
  951.     bmih.biPlanes   = 1;
  952.     bmih.biBitCount = 32;
  953.  
  954.     m_hBitmap = CreateDIBSection(hDC, (const BITMAPINFO *) & bmih, DIB_RGB_COLORS, (void **) & m_pBits, NULL, 0);
  955.     m_hMemDC  = CreateCompatibleDC(hDC);
  956.     m_hOldBmp = SelectObject(m_hMemDC, m_hBitmap);
  957.  
  958.     m_dx = extra - min(rect.left, 0);
  959.     m_dy = extra;
  960.  
  961.     SetBkColor  (m_hMemDC, GetBkColor(hDC));
  962.  
  963.     rect.left   = 0;
  964.     rect.top    = 0;
  965.     rect.right  = m_width;
  966.     rect.bottom = m_height;
  967.     ExtTextOut(m_hMemDC, 0, 0, ETO_OPAQUE, & rect, NULL, 0, NULL);
  968.     
  969.     RenderText(hDC, 0, 0, pString, nCount);
  970.     RestoreDC(hDC, -1);
  971.  
  972.     return TRUE;
  973. }
  974.  
  975.  
  976. // Embossing or Engraving by change edges only, good for non-solod background
  977. void TransparentEmboss(HDC hDC, const TCHAR * pString, int nCount, COLORREF crTL, COLORREF crBR, int offset, int x, int y)
  978. {
  979.     KTextBitmap bmp;
  980.  
  981.     // generate a mask bitmap with top-left and bottom-right edges
  982.     SetBkMode(hDC, OPAQUE);
  983.     SetBkColor(hDC, RGB(0xFF, 0xFF, 0xFF));                      // white background
  984.     SetTextColor(hDC, RGB(0, 0, 0));
  985.     bmp.Convert(hDC, pString, nCount, offset*2);              // black TL edge
  986.  
  987.     SetBkMode(hDC, TRANSPARENT);
  988.     bmp.RenderText(hDC, offset*2, offset*2, pString, nCount); // black BR edge
  989.  
  990.     SetTextColor(hDC, RGB(0xFF, 0xFF, 0xFF));                  // white main text
  991.     bmp.RenderText(hDC, offset, offset, pString, nCount);
  992.  
  993.  
  994.     // mask destination with top-left and bottom-right edges
  995.     bmp.Draw(hDC, x, y, SRCAND);
  996.  
  997.     // create a color bitmap with top-left and bottom-right edges
  998.     SetBkColor(hDC, RGB(0, 0, 0));                              // black background
  999.     SetTextColor(hDC, crTL);
  1000.     bmp.Convert(hDC, pString, nCount, offset);                  // TL edge
  1001.  
  1002.     SetBkMode(hDC, TRANSPARENT);
  1003.     SetTextColor(hDC, crBR);
  1004.     bmp.RenderText(hDC, offset*2, offset*2, pString, nCount); // BR edge
  1005.  
  1006.     SetTextColor(hDC, RGB(0, 0, 0));                
  1007.     bmp.RenderText(hDC, offset, offset, pString, nCount);      // black main text
  1008.  
  1009.     // draw color top-left and bottom-right edges
  1010.     bmp.Draw(hDC, x, y, SRCPAINT);
  1011. }
  1012.