text: //
  // fontlnkv.cpp
  //
  //
  // Vertical version DrawTextW()
  //
  
  #include "private.h"
  #include "flshare.h"
  #include "fontlink.h"
  #include "xstring.h"
  #include "osver.h"
  #include "globals.h"
  
  typedef struct tagDRAWTEXTPARAMSVERT
  {
      UINT    cbSize;
      int     iTabLength;
      int     iTopMargin;
      int     iBottomMargin;
      UINT    uiLengthDrawn;
  } DRAWTEXTPARAMSVERT, FAR *LPDRAWTEXTPARAMSVERT;
  
  // Outputs the text and puts and _ below the character with an &
  // before it. Note that this routine isn't used for menus since menus
  // have their own special one so that it is specialized and faster...
  void PSMTextOutVert(
      HDC hdc,
      int xRight,
      int yTop,
      LPWSTR lpsz,
      int cch,
      DWORD dwFlags)
  {
      int cy;
      LONG textsize, result;
      WCHAR achWorkBuffer[255];
      WCHAR *pchOut = achWorkBuffer;
      TEXTMETRIC textMetric;
      SIZE size;
      RECT rc;
      COLORREF color;
  
      if (dwFlags & DT_NOPREFIX)
      {
          FLTextOutW(hdc, xRight, yTop, lpsz, cch);
          return;
      }
  
      if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR))
      {
          pchOut = (WCHAR*)LocalAlloc(LPTR, (cch+1) * sizeof(WCHAR));
          if (pchOut == NULL)
              return;
      }
  
      result = GetPrefixCount(lpsz, cch, pchOut, cch);
  
      // DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off to on.
      if (!(dwFlags & DT_PREFIXONLY))
          FLTextOutW(hdc, xRight, yTop, pchOut, cch - HIWORD(result));
  
      // Any true prefix characters to underline?
      if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX)
      {
          if (pchOut != achWorkBuffer)
              LocalFree(pchOut);
          return;
      }
  
      if (!GetTextMetrics(hdc, &textMetric))
      {
          textMetric.tmOverhang = 0;
          textMetric.tmAscent = 0;
      }
  
      // For proportional fonts, find starting point of underline.
      if (LOWORD(result) != 0)
      {
          // How far in does underline start (if not at 0th byte.).
          FLGetTextExtentPoint32(hdc, pchOut, LOWORD(result), &size);
          xRight += size.cy;
  
          // Adjust starting point of underline if not at first char and there is
          // an overhang.  (Italics or bold fonts.)
          yTop = yTop - textMetric.tmOverhang;
      }
  
      // Adjust for proportional font when setting the length of the underline and
      // height of text.
      FLGetTextExtentPoint32(hdc, pchOut + LOWORD(result), 1, &size);
      textsize = size.cx;
  
      // Find the width of the underline character.  Just subtract out the overhang
      // divided by two so that we look better with italic fonts.  This is not
      // going to effect embolded fonts since their overhang is 1.
      cy = LOWORD(textsize) - textMetric.tmOverhang / 2;
  
      // Get height of text so that underline is at bottom.
      xRight -= textMetric.tmAscent + 1;
  
      // Draw the underline using the foreground color.
      SetRect(&rc, xRight, yTop, xRight+1, yTop+cy);
      color = SetBkColor(hdc, GetTextColor(hdc));
      FLExtTextOutW(hdc, xRight, yTop, ETO_OPAQUE, &rc, L"", 0, NULL);
      SetBkColor(hdc, color);
  
      if (pchOut != achWorkBuffer)
          LocalFree(pchOut);
  }
  
  int DT_GetExtentMinusPrefixesVert(HDC hdc, LPCWSTR lpchStr, int cchCount, UINT wFormat, int iOverhang)
  {
      int iPrefixCount;
      int cxPrefixes = 0;
      WCHAR PrefixChar = CH_PREFIX;
      SIZE size;
  
      if (!(wFormat & DT_NOPREFIX) &&
          (iPrefixCount = HIWORD(GetPrefixCount(lpchStr, cchCount, NULL, 0))))
      {
          // Kanji Windows has three shortcut prefixes...
          if (IsOnDBCS())
          {
              // 16bit apps compatibility
              cxPrefixes = KKGetPrefixWidth(hdc, lpchStr, cchCount) - (iPrefixCount * iOverhang);
          }
          else
          {
              cxPrefixes = FLGetTextExtentPoint32(hdc, &PrefixChar, 1, &size);
              cxPrefixes = size.cx - iOverhang;
              cxPrefixes *=  iPrefixCount;
          }
      }
      FLGetTextExtentPoint32(hdc, lpchStr, cchCount, &size);
      return (size.cx - cxPrefixes);
  }
  
  // This will draw the given string in the given location without worrying
  // about the left/right justification. Gets the extent and returns it.
  // If fDraw is TRUE and if NOT DT_CALCRECT, this draws the text.
  // NOTE: This returns the extent minus Overhang.
  int DT_DrawStrVert(HDC hdc, int  xRight, int yTop, LPCWSTR lpchStr,
                 int cchCount, BOOL fDraw, UINT wFormat,
                 LPDRAWTEXTDATAVERT lpDrawInfo)
  {
      LPCWSTR lpch;
      int     iLen;
      int     cyExtent;
      int     yOldLeft = yTop;   // Save the xRight given to compute the extent later
      int     yTabLength = lpDrawInfo->cyTabLength;
      int     iTabOrigin = lpDrawInfo->rcFormat.left;
  
      // Check if the tabs need to be expanded
      if (wFormat & DT_EXPANDTABS)
      {
          while (cchCount)
          {
              // Look for a tab
              for (iLen = 0, lpch = lpchStr; iLen < cchCount; iLen++)
                  if(*lpch++ == L'\t')
                      break;
  
              // Draw text, if any, upto the tab
              if (iLen)
              {
                  // Draw the substring taking care of the prefixes.
                  if (fDraw && !(wFormat & DT_CALCRECT))  // Only if we need to draw text
                      PSMTextOutVert(hdc, xRight, yTop, (LPWSTR)lpchStr, iLen, wFormat);
                  // Get the extent of this sub string and add it to xRight.
                  yTop += DT_GetExtentMinusPrefixesVert(hdc, lpchStr, iLen, wFormat, lpDrawInfo->cyOverhang) - lpDrawInfo->cyOverhang;
              }
  
              //if a TAB was found earlier, calculate the start of next sub-string.
              if (iLen < cchCount)
              {
                  iLen++;  // Skip the tab
                  if (yTabLength) // Tab length could be zero
                      yTop = (((yTop - iTabOrigin)/yTabLength) + 1)*yTabLength + iTabOrigin;
              }
  
              // Calculate the details of the string that remains to be drawn.
              cchCount -= iLen;
              lpchStr = lpch;
          }
          cyExtent = yTop - yOldLeft;
      }
      else
      {
          // If required, draw the text
          if (fDraw && !(wFormat & DT_CALCRECT))
              PSMTextOutVert(hdc, xRight, yTop, (LPWSTR)lpchStr, cchCount, wFormat);
          // Compute the extent of the text.
          cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchStr, cchCount, wFormat, lpDrawInfo->cyOverhang) - lpDrawInfo->cyOverhang;
      }
      return cyExtent;
  }
  
  // This function draws one complete line with proper justification
  void DT_DrawJustifiedLineVert(HDC hdc, int xRight, LPCWSTR lpchLineSt, int cchCount, UINT wFormat, LPDRAWTEXTDATAVERT lpDrawInfo)
  {
      LPRECT  lprc;
      int     cyExtent;
      int     yTop;
  
      lprc = &(lpDrawInfo->rcFormat);
      yTop = lprc->top;
  
      // Handle the special justifications (right or centered) properly.
      if (wFormat & (DT_CENTER | DT_RIGHT))
      {
          cyExtent = DT_DrawStrVert(hdc, xRight, yTop, lpchLineSt, cchCount, FALSE, wFormat, lpDrawInfo)
                   + lpDrawInfo->cyOverhang;
          if(wFormat & DT_CENTER)
              yTop = lprc->top + (((lprc->bottom - lprc->top) - cyExtent) >> 1);
          else
              yTop = lprc->bottom - cyExtent;
      }
      else
          yTop = lprc->top;
  
      // Draw the whole line.
      cyExtent = DT_DrawStrVert(hdc, xRight, yTop, lpchLineSt, cchCount, TRUE, wFormat, lpDrawInfo)
               + lpDrawInfo->cyOverhang;
      if (cyExtent > lpDrawInfo->cyMaxExtent)
          lpDrawInfo->cyMaxExtent = cyExtent;
  }
  
  // This is called at the begining of DrawText(); This initializes the
  // DRAWTEXTDATAVERT structure passed to this function with all the required info.
  BOOL DT_InitDrawTextInfoVert(
      HDC                 hdc,
      LPRECT              lprc,
      UINT                wFormat,
      LPDRAWTEXTDATAVERT  lpDrawInfo,
      LPDRAWTEXTPARAMSVERT lpDTparams)
  {
      SIZE        sizeViewPortExt = {0, 0}, sizeWindowExt = {0, 0};
      TEXTMETRIC  tm;
      LPRECT      lprcDest;
      int         iTabLength = 8;   // Default Tab length is 8 characters.
      int         iTopMargin;
      int         iBottomMargin;
  
      if (lpDTparams)
      {
          // Only if DT_TABSTOP flag is mentioned, we must use the iTabLength field.
          if (wFormat & DT_TABSTOP)
              iTabLength = lpDTparams->iTabLength;
          iTopMargin = lpDTparams->iTopMargin;
          iBottomMargin = lpDTparams->iBottomMargin;
      }
      else
          iTopMargin = iBottomMargin = 0;
  
      // Get the View port and Window extents for the given DC
      // If this call fails, hdc must be invalid
      if (!GetViewportExtEx(hdc, &sizeViewPortExt))
          return FALSE;
      GetWindowExtEx(hdc, &sizeWindowExt);
  
      // For the current mapping mode,  find out the sign of x from left to right.
      lpDrawInfo->iXSign = (((sizeViewPortExt.cx ^ sizeWindowExt.cx) & 0x80000000) ? -1 : 1);
  
      // For the current mapping mode,  find out the sign of y from top to bottom.
      lpDrawInfo->iYSign = (((sizeViewPortExt.cy ^ sizeWindowExt.cy) & 0x80000000) ? -1 : 1);
  
      // Calculate the dimensions of the current font in this DC.
      GetTextMetrics(hdc, &tm);
  
      // cxLineHeight is in pixels (This will be signed).
      lpDrawInfo->cxLineHeight = (tm.tmHeight +
          ((wFormat & DT_EXTERNALLEADING) ? tm.tmExternalLeading : 0)) * lpDrawInfo->iXSign;
  
      // cyTabLength is the tab length in pixels (This will not be signed)
      lpDrawInfo->cyTabLength = tm.tmAveCharWidth * iTabLength;
  
      // Set the cyOverhang
      lpDrawInfo->cyOverhang = tm.tmOverhang;
  
      // Set up the format rectangle based on the margins.
      lprcDest = &(lpDrawInfo->rcFormat);
      *lprcDest = *lprc;
  
      // We need to do the following only if the margins are given
      if (iTopMargin | iBottomMargin)
      {
          lprcDest->top += iTopMargin * lpDrawInfo->iYSign;
          lprcDest->bottom -= (lpDrawInfo->cyBottomMargin = iBottomMargin * lpDrawInfo->iYSign);
      }
      else
          lpDrawInfo->cyBottomMargin = 0;  // Initialize to zero.
  
      // cyMaxWidth is unsigned.
      lpDrawInfo->cyMaxWidth = (lprcDest->bottom - lprcDest->top) * lpDrawInfo->iYSign;
      lpDrawInfo->cyMaxExtent = 0;  // Initialize this to zero.
  
      return TRUE;
  }
  
  // A word needs to be broken across lines and this finds out where to break it.
  LPCWSTR  DT_BreakAWordVert(HDC hdc, LPCWSTR lpchText, int iLength, int iWidth, UINT wFormat, int iOverhang)
  {
    int  iLow = 0, iHigh = iLength;
    int  iNew;
  
    while ((iHigh - iLow) > 1)
    {
        iNew = iLow + (iHigh - iLow)/2;
        if(DT_GetExtentMinusPrefixesVert(hdc, lpchText, iNew, wFormat, iOverhang) > iWidth)
            iHigh = iNew;
        else
            iLow = iNew;
    }
    // If the width is too low, we must print atleast one char per line.
    // Else, we will be in an infinite loop.
    if(!iLow && iLength)
        iLow = 1;
    return (lpchText+iLow);
  }
  
  // This finds out the location where we can break a line.
  // Returns LPCSTR to the begining of next line.
  // Also returns via lpiLineLength, the length of the current line.
  // NOTE: (lpstNextLineStart - lpstCurrentLineStart) is not equal to the
  // line length; This is because, we exclude some white spaces at the begining
  // and/or end of lines; Also, CR/LF is excluded from the line length.
  LPWSTR DT_GetLineBreakVert(
      HDC             hdc,
      LPCWSTR         lpchLineStart,
      int             cchCount,
      DWORD           dwFormat,
      LPINT           lpiLineLength,
      LPDRAWTEXTDATAVERT  lpDrawInfo)
  {
      LPCWSTR lpchText, lpchEnd, lpch, lpchLineEnd;
      int   cxStart, cyExtent, cyNewExtent;
      BOOL  fAdjustWhiteSpaces = FALSE;
      WCHAR ch;
  
      cxStart = lpDrawInfo->rcFormat.left;
      cyExtent = cyNewExtent = 0;
      lpchText = lpchLineStart;
      lpchEnd = lpchLineStart + cchCount;
      lpch = lpchEnd;
      lpchLineEnd = lpchEnd;
  
      while(lpchText < lpchEnd)
      {
          lpchLineEnd = lpch = GetNextWordbreak(lpchText, lpchEnd, dwFormat, NULL);
          // DT_DrawStrVert does not return the overhang; Otherwise we will end up
          // adding one overhang for every word in the string.
  
          // For simulated Bold fonts, the summation of extents of individual
          // words in a line is greater than the extent of the whole line. So,
          // always calculate extent from the LineStart.
          // BUGTAG: #6054 -- Win95B -- SANKAR -- 3/9/95 --
          cyNewExtent = DT_DrawStrVert(hdc, cxStart, 0, lpchLineStart, (int)(((PBYTE)lpch - (PBYTE)lpchLineStart)/sizeof(WCHAR)),
                                   FALSE, dwFormat, lpDrawInfo);
  
          if ((dwFormat & DT_WORDBREAK) && ((cyNewExtent + lpDrawInfo->cyOverhang) > lpDrawInfo->cyMaxWidth))
          {
              // Are there more than one word in this line?
              if (lpchText != lpchLineStart)
              {
                  lpchLineEnd = lpch = lpchText;
                  fAdjustWhiteSpaces = TRUE;
              }
              else
              {
                  //One word is longer than the maximum width permissible.
                  //See if we are allowed to break that single word.
                  if((dwFormat & DT_EDITCONTROL) && !(dwFormat & DT_WORD_ELLIPSIS))
                  {
                      lpchLineEnd = lpch = DT_BreakAWordVert(hdc, lpchText, (int)(((PBYTE)lpch - (PBYTE)lpchText)/sizeof(WCHAR)),
                            lpDrawInfo->cyMaxWidth - cyExtent, dwFormat, lpDrawInfo->cyOverhang); //Break that word
                      //Note: Since we broke in the middle of a word, no need to
                      // adjust for white spaces.
                  }
                  else
                  {
                      fAdjustWhiteSpaces = TRUE;
                      // Check if we need to end this line with ellipsis
                      if(dwFormat & DT_WORD_ELLIPSIS)
                      {
                          // Don't do this if already at the end of the string.
                          if (lpch < lpchEnd)
                          {
                              // If there are CR/LF at the end, skip them.
                              if ((ch = *lpch) == CR || ch == LF)
                              {
                                  if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
                                      lpch++;
                                  fAdjustWhiteSpaces = FALSE;
                              }
                          }
                      }
                  }
              }
              // Well! We found a place to break the line. Let us break from this loop;
              break;
          }
          else
          {
              // Don't do this if already at the end of the string.
              if (lpch < lpchEnd)
              {
                  if ((ch = *lpch) == CR || ch == LF)
                  {
                      if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
                          lpch++;
                      fAdjustWhiteSpaces = FALSE;
                      break;
                  }
              }
          }
          // Point at the beginning of the next word.
          lpchText = lpch;
          cyExtent = cyNewExtent;
      }
      // Calculate the length of current line.
      *lpiLineLength = (INT)((PBYTE)lpchLineEnd - (PBYTE)lpchLineStart)/sizeof(WCHAR);
  
      // Adjust the line length and lpch to take care of spaces.
      if(fAdjustWhiteSpaces && (lpch < lpchEnd))
          lpch = DT_AdjustWhiteSpaces(lpch, lpiLineLength, dwFormat);
  
      // return the begining of next line;
      return (LPWSTR)lpch;
  }
  
  // This function checks whether the given string fits within the given
  // width or we need to add end-ellipse. If it required end-ellipses, it
  // returns TRUE and it returns the number of characters that are saved
  // in the given string via lpCount.
  BOOL  NeedsEndEllipsisVert(
      HDC             hdc,
      LPCWSTR         lpchText,
      LPINT           lpCount,
      LPDRAWTEXTDATAVERT  lpDTdata,
      UINT            wFormat)
  {
      int   cchText;
      int   ichMin, ichMax, ichMid;
      int   cyMaxWidth;
      int   iOverhang;
      int   cyExtent;
      SIZE size;
      cchText = *lpCount;  // Get the current count.
  
      if (cchText == 0)
          return FALSE;
  
      cyMaxWidth  = lpDTdata->cyMaxWidth;
      iOverhang   = lpDTdata->cyOverhang;
  
      cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchText, cchText, wFormat, iOverhang);
  
      if (cyExtent <= cyMaxWidth)
          return FALSE;
      // Reserve room for the "..." ellipses;
      // (Assumption: The ellipses don't have any prefixes!)
      FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
      cyMaxWidth -= size.cx - iOverhang;
  
      // If no room for ellipses, always show first character.
      //
      ichMax = 1;
      if (cyMaxWidth > 0)
      {
          // Binary search to find characters that will fit.
          ichMin = 0;
          ichMax = cchText;
          while (ichMin < ichMax)
          {
              // Be sure to round up, to make sure we make progress in
              // the loop if ichMax == ichMin + 1.
              ichMid = (ichMin + ichMax + 1) / 2;
  
              cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchText, ichMid, wFormat, iOverhang);
  
              if (cyExtent < cyMaxWidth)
                  ichMin = ichMid;
              else
              {
                  if (cyExtent > cyMaxWidth)
                      ichMax = ichMid - 1;
                  else
                  {
                      // Exact match up up to ichMid: just exit.
                      ichMax = ichMid;
                      break;
                  }
              }
          }
          // Make sure we always show at least the first character...
          if (ichMax < 1)
              ichMax = 1;
      }
      *lpCount = ichMax;
      return TRUE;
  }
  
  // This adds a path ellipse to the given path name.
  // Returns TRUE if the resultant string's extent is less the the
  // cyMaxWidth. FALSE, if otherwise.
  int AddPathEllipsisVert(
      HDC    hdc,
      LPWSTR lpszPath,
      int    cchText,
      UINT   wFormat,
      int    cyMaxWidth,
      int    iOverhang)
  {
      int    iLen;
      UINT   dxFixed, dxEllipsis;
      LPWSTR lpEnd;          /* end of the unfixed string */
      LPWSTR lpFixed;        /* start of text that we always display */
      BOOL   bEllipsisIn;
      int    iLenFixed;
      SIZE   size;
  
      lpFixed = PathFindFileName(lpszPath, cchText);
      if (lpFixed != lpszPath)
          lpFixed--;  // point at the slash
      else
          return cchText;
  
      lpEnd = lpFixed;
      bEllipsisIn = FALSE;
      iLenFixed = cchText - (int)(lpFixed - lpszPath);
      dxFixed = DT_GetExtentMinusPrefixesVert(hdc, lpFixed, iLenFixed, wFormat, iOverhang);
  
      // It is assumed that the "..." string does not have any prefixes ('&').
      FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
      dxEllipsis = size.cx - iOverhang;
  
      while (TRUE)
      {
          iLen = dxFixed + DT_GetExtentMinusPrefixesVert(hdc, lpszPath, (int)((PBYTE)lpEnd - (PBYTE)lpszPath)/sizeof(WCHAR),
                                                     wFormat, iOverhang) - iOverhang;
  
          if (bEllipsisIn)
              iLen += dxEllipsis;
  
          if (iLen <= cyMaxWidth)
              break;
  
          bEllipsisIn = TRUE;
  
          if (lpEnd <= lpszPath)
          {
              // Things didn't fit.
              lpEnd = lpszPath;
              break;
          }
          // Step back a character.
          lpEnd--;
      }
  
      if (bEllipsisIn && (lpEnd + CCHELLIPSIS < lpFixed))
      {
          // NOTE: the strings could over lap here. So, we use LCopyStruct.
          MoveMemory((lpEnd + CCHELLIPSIS), lpFixed, iLenFixed * sizeof(WCHAR));
          CopyMemory(lpEnd, szEllipsis, CCHELLIPSIS * sizeof(WCHAR));
  
          cchText = (int)(lpEnd - lpszPath) + CCHELLIPSIS + iLenFixed;
  
          // now we can NULL terminate the string
          *(lpszPath + cchText) = L'\0';
      }
      return cchText;
  }
  
  // This function returns the number of characters actually drawn.
  int AddEllipsisAndDrawLineVert(
      HDC            hdc,
      int            xLine,
      LPCWSTR        lpchText,
      int            cchText,
      DWORD          dwDTformat,
      LPDRAWTEXTDATAVERT lpDrawInfo)
  {
      LPWSTR pEllipsis = NULL;
      WCHAR  szTempBuff[MAXBUFFSIZE];
      LPWSTR lpDest;
      BOOL   fAlreadyCopied = FALSE;
  
      // Check if this is a filename with a path AND
      // Check if the width is too narrow to hold all the text.
      if ((dwDTformat & DT_PATH_ELLIPSIS) &&
          ((DT_GetExtentMinusPrefixesVert(hdc, lpchText, cchText, dwDTformat, lpDrawInfo->cyOverhang)) > lpDrawInfo->cyMaxWidth))
      {
          // We need to add Path-Ellipsis. See if we can do it in-place.
          if (!(dwDTformat & DT_MODIFYSTRING)) {
              // NOTE: When you add Path-Ellipsis, the string could grow by
              // CCHELLIPSIS bytes.
              if((cchText + CCHELLIPSIS + 1) <= MAXBUFFSIZE)
                  lpDest = szTempBuff;
              else
              {
                  // Alloc the buffer from local heap.
                  if(!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
                      return 0;
                  lpDest = (LPWSTR)pEllipsis;
              }
              // Source String may not be NULL terminated. So, copy just
              // the given number of characters.
              CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
              lpchText = lpDest;        // lpchText points to the copied buff.
              fAlreadyCopied = TRUE;    // Local copy has been made.
          }
          // Add the path ellipsis now!
          cchText = AddPathEllipsisVert(hdc, (LPWSTR)lpchText, cchText, dwDTformat, lpDrawInfo->cyMaxWidth, lpDrawInfo->cyOverhang);
      }
  
      // Check if end-ellipsis are to be added.
      if ((dwDTformat & (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)) &&
          NeedsEndEllipsisVert(hdc, lpchText, &cchText, lpDrawInfo, dwDTformat))
      {
          // We need to add end-ellipsis; See if we can do it in-place.
          if (!(dwDTformat & DT_MODIFYSTRING) && !fAlreadyCopied)
          {
              // See if the string is small enough for the buff on stack.
              if ((cchText+CCHELLIPSIS+1) <= MAXBUFFSIZE)
                  lpDest = szTempBuff;  // If so, use it.
              else {
                  // Alloc the buffer from local heap.
                  if (!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
                      return 0;
                  lpDest = pEllipsis;
              }
              // Make a copy of the string in the local buff.
              CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
              lpchText = lpDest;
          }
          // Add an end-ellipsis at the proper place.
          CopyMemory((LPWSTR)(lpchText+cchText), szEllipsis, (CCHELLIPSIS+1)*sizeof(WCHAR));
          cchText += CCHELLIPSIS;
      }
  
      // Draw the line that we just formed.
      DT_DrawJustifiedLineVert(hdc, xLine, lpchText, cchText, dwDTformat, lpDrawInfo);
  
      // Free the block allocated for End-Ellipsis.
      if (pEllipsis)
          LocalFree(pEllipsis);
  
      return cchText;
  }
  
  BOOL IsComplexScriptPresent(LPWSTR lpchText, int cchText);
  
  int  FLDrawTextExPrivWVert(
     HDC               hdc,
     LPWSTR            lpchText,
     int               cchText,
     LPRECT            lprc,
     UINT              dwDTformat,
     LPDRAWTEXTPARAMSVERT  lpDTparams)
  {
      DRAWTEXTDATAVERT DrawInfo;
      WORD         wFormat = LOWORD(dwDTformat);
      LPWSTR       lpchTextBegin;
      LPWSTR       lpchEnd;
      LPWSTR       lpchNextLineSt;
      int          iLineLength;
      int          ixSign;
      int          xLine;
      int          xLastLineHeight;
      HRGN         hrgnClip;
      int          iLineCount;
      RECT         rc;
      BOOL         fLastLine;
      WCHAR        ch;
      UINT         oldAlign;
  
      if ((cchText == 0) && lpchText && (*lpchText))
      {
          // infoview.exe passes lpchText that points to '\0'
          // Lotus Notes doesn't like getting a zero return here
          return 1;
      }
  
      if (cchText == -1)
          cchText = lstrlenW(lpchText);
      else if (lpchText[cchText - 1] == L'\0')
          cchText--;      // accommodate counting of NULLS for ME
  
  
      if ((lpDTparams) && (lpDTparams->cbSize != sizeof(DRAWTEXTPARAMS)))
      {
          ASSERT(0 && "DrawTextExWorker: cbSize is invalid");
          return 0;
      }
  
  
      // If DT_MODIFYSTRING is specified, then check for read-write pointer.
      if ((dwDTformat & DT_MODIFYSTRING) &&
          (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
      {
          if(IsBadWritePtr(lpchText, cchText))
          {
              ASSERT(0 && "DrawTextExWorker: For DT_MODIFYSTRING, lpchText must be read-write");
              return 0;
          }
      }
  
      // Initialize the DrawInfo structure.
      if (!DT_InitDrawTextInfoVert(hdc, lprc, dwDTformat, (LPDRAWTEXTDATAVERT)&DrawInfo, lpDTparams))
          return 0;
  
      // If the rect is too narrow or the margins are too wide.....Just forget it!
      //
      // If wordbreak is specified, the MaxWidth must be a reasonable value.
      // This check is sufficient because this will allow CALCRECT and NOCLIP
      // cases.  --SANKAR.
      //
      // This also fixed all of our known problems with AppStudio.
      if (DrawInfo.cyMaxWidth <= 0)
      {
          if (wFormat & DT_WORDBREAK)
          {
              ASSERT(0 && "DrawTextExW: FAILURE DrawInfo.cyMaxWidth <= 0");
              return 1;
          }
      }
  
      // if we're not doing the drawing, initialise the lpk-dll
      if (dwDTformat & DT_RTLREADING)
          oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  
      // If we need to clip, let us do that.
      if (!(wFormat & DT_NOCLIP))
      {
          // Save clipping region so we can restore it later.
          hrgnClip = CreateRectRgn(0,0,0,0);
          if (hrgnClip != NULL)
          {
              if (GetClipRgn(hdc, hrgnClip) != 1)
              {
                  DeleteObject(hrgnClip);
                  hrgnClip = (HRGN)-1;
              }
              rc = *lprc;
              IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
          }
      }
      else
          hrgnClip = NULL;
  
      lpchTextBegin = lpchText;
      lpchEnd = lpchText + cchText;
  
  ProcessDrawText:
  
      iLineCount = 0;  // Reset number of lines to 1.
      xLine = lprc->right;
  
      if (wFormat & DT_SINGLELINE)
      {
          iLineCount = 1;  // It is a single line.
  
          // Process single line DrawText.
          switch (wFormat & DT_VFMTMASK)
          {
              case DT_BOTTOM:
                  xLine = lprc->left + DrawInfo.cxLineHeight;
                  break;
  
              case DT_VCENTER:
                  xLine = lprc->right - ((lprc->right - lprc->left - DrawInfo.cxLineHeight) / 2);
                  break;
          }
  
          cchText = AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, cchText, dwDTformat, &DrawInfo);
          xLine += DrawInfo.cxLineHeight;
          lpchText += cchText;
      }
      else
      {
          // Multiline
          // If the height of the rectangle is not an integral multiple of the
          // average char height, then it is possible that the last line drawn
          // is only partially visible. However, if DT_EDITCONTROL style is
          // specified, then we must make sure that the last line is not drawn if
          // it is going to be partially visible. This will help imitate the
          // appearance of an edit control.
          if (wFormat & DT_EDITCONTROL)
              xLastLineHeight = DrawInfo.cxLineHeight;
          else
              xLastLineHeight = 0;
  
          ixSign = DrawInfo.iXSign;
          fLastLine = FALSE;
          // Process multiline DrawText.
          while ((lpchText < lpchEnd) && (!fLastLine))
          {
              // Check if the line we are about to draw is the last line that needs
              // to be drawn.
              // Let us check if the display goes out of the clip rect and if so
              // let us stop here, as an optimisation;
              if (!(wFormat & DT_CALCRECT) && // We don't need to calc rect?
                  !(wFormat & DT_NOCLIP) &&   // Must we clip the display ?
                                              // Are we outside the rect?
                  ((xLine + DrawInfo.cxLineHeight + xLastLineHeight)*ixSign > (lprc->right*ixSign)))
              {
                  fLastLine = TRUE;    // Let us quit this loop
              }
  
              // We do the Ellipsis processing only for the last line.
              if (fLastLine && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
                  lpchText += AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, cchText, dwDTformat, &DrawInfo);
              else
              {
                  lpchNextLineSt = (LPWSTR)DT_GetLineBreakVert(hdc, lpchText, cchText, dwDTformat, &iLineLength, &DrawInfo);
  
                  // Check if we need to put ellipsis at the end of this line.
                  // Also check if this is the last line.
                  if ((dwDTformat & DT_WORD_ELLIPSIS) ||
                      ((lpchNextLineSt >= lpchEnd) && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS))))
                      AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
                  else
                      DT_DrawJustifiedLineVert(hdc, xLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
                  cchText -= (int)((PBYTE)lpchNextLineSt - (PBYTE)lpchText) / sizeof(WCHAR);
                  lpchText = lpchNextLineSt;
              }
              iLineCount++; // We draw one more line.
              xLine += DrawInfo.cxLineHeight;
          }
  
          // For Win3.1 and NT compatibility, if the last char is a CR or a LF
          // then the height returned includes one more line.
          if (!(dwDTformat & DT_EDITCONTROL) &&
              (lpchEnd > lpchTextBegin) &&   // If zero length it will fault.
              (((ch = (*(lpchEnd-1))) == CR) || (ch == LF)))
              xLine += DrawInfo.cxLineHeight;
      }
  
      // If DT_CALCRECT, modify width and height of rectangle to include
      // all of the text drawn.
      if (wFormat & DT_CALCRECT)
      {
          DrawInfo.rcFormat.bottom = DrawInfo.rcFormat.top + DrawInfo.cyMaxExtent * DrawInfo.iYSign;
          lprc->bottom = DrawInfo.rcFormat.bottom + DrawInfo.cyBottomMargin;
  
          // If the Width is more than what was provided, we have to redo all
          // the calculations, because, the number of lines can be less now.
          // (We need to do this only if we have more than one line).
          if((iLineCount > 1) && (DrawInfo.cyMaxExtent > DrawInfo.cyMaxWidth))
          {
              DrawInfo.cyMaxWidth = DrawInfo.cyMaxExtent;
              lpchText = lpchTextBegin;
              cchText = (int)((PBYTE)lpchEnd - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
              goto  ProcessDrawText;  // Start all over again!
          }
          lprc->left = xLine;
      }
  
      if (hrgnClip != NULL)
      {
          if (hrgnClip == (HRGN)-1)
              ExtSelectClipRgn(hdc, NULL, RGN_COPY);
          else
          {
              ExtSelectClipRgn(hdc, hrgnClip, RGN_COPY);
              DeleteObject(hrgnClip);
          }
      }
  
      if (dwDTformat & DT_RTLREADING)
          SetTextAlign(hdc, oldAlign);
  
      // Copy the number of characters actually drawn
      if(lpDTparams != NULL)
          lpDTparams->uiLengthDrawn = (UINT)((PBYTE)lpchText - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  
      if (xLine == lprc->right)
          return 1;
  
      return (xLine + lprc->right);
  }
  
  int FLDrawTextWVert(HDC hdc, LPCWSTR lpchText, int cchText, LPCRECT lprc, UINT format)
  {
      DRAWTEXTPARAMSVERT DTparams;
      LPDRAWTEXTPARAMSVERT lpDTparams = NULL;
  
      if (cchText < -1)
          return(0);
  
      if (format & DT_TABSTOP)
      {
          DTparams.cbSize      = sizeof(DRAWTEXTPARAMSVERT);
          DTparams.iTopMargin = DTparams.iBottomMargin = 0;
          DTparams.iTabLength  = (format & 0xff00) >> 8;
          lpDTparams           = &DTparams;
          format              &= 0xffff00ff;
      }
      return FLDrawTextExPrivWVert(hdc, (LPWSTR)lpchText, cchText, (LPRECT)lprc, format, lpDTparams);
  }
  