Home | History | Annotate | Download | only in tto
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "xfa/fde/tto/fde_textout.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "core/fxcrt/fx_coordinates.h"
     12 #include "core/fxcrt/fx_system.h"
     13 #include "third_party/base/ptr_util.h"
     14 #include "third_party/base/stl_util.h"
     15 #include "xfa/fde/cfde_path.h"
     16 #include "xfa/fde/fde_gedevice.h"
     17 #include "xfa/fde/fde_object.h"
     18 #include "xfa/fgas/crt/fgas_utils.h"
     19 #include "xfa/fgas/layout/fgas_textbreak.h"
     20 
     21 FDE_TTOPIECE::FDE_TTOPIECE() = default;
     22 FDE_TTOPIECE::FDE_TTOPIECE(const FDE_TTOPIECE& that) = default;
     23 FDE_TTOPIECE::~FDE_TTOPIECE() = default;
     24 
     25 CFDE_TextOut::CFDE_TextOut()
     26     : m_pTxtBreak(new CFX_TxtBreak(FX_TXTBREAKPOLICY_None)),
     27       m_pFont(nullptr),
     28       m_fFontSize(12.0f),
     29       m_fLineSpace(m_fFontSize),
     30       m_fLinePos(0.0f),
     31       m_fTolerance(0.0f),
     32       m_iAlignment(0),
     33       m_iTxtBkAlignment(0),
     34       m_wParagraphBkChar(L'\n'),
     35       m_TxtColor(0xFF000000),
     36       m_dwStyles(0),
     37       m_dwTxtBkStyles(0),
     38       m_bElliChanged(false),
     39       m_iEllipsisWidth(0),
     40       m_ttoLines(5),
     41       m_iCurLine(0),
     42       m_iCurPiece(0),
     43       m_iTotalLines(0) {
     44   m_Matrix.SetIdentity();
     45   m_rtClip.Reset();
     46   m_rtLogicClip.Reset();
     47 }
     48 
     49 CFDE_TextOut::~CFDE_TextOut() {}
     50 
     51 void CFDE_TextOut::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont) {
     52   ASSERT(pFont);
     53   m_pFont = pFont;
     54   m_pTxtBreak->SetFont(pFont);
     55 }
     56 
     57 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize) {
     58   ASSERT(fFontSize > 0);
     59   m_fFontSize = fFontSize;
     60   m_pTxtBreak->SetFontSize(fFontSize);
     61 }
     62 
     63 void CFDE_TextOut::SetTextColor(FX_ARGB color) {
     64   m_TxtColor = color;
     65 }
     66 
     67 void CFDE_TextOut::SetStyles(uint32_t dwStyles) {
     68   m_dwStyles = dwStyles;
     69   m_dwTxtBkStyles = 0;
     70   if (dwStyles & FDE_TTOSTYLE_SingleLine) {
     71     m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_SingleLine;
     72   }
     73   if (dwStyles & FDE_TTOSTYLE_ExpandTab) {
     74     m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ExpandTab;
     75   }
     76   if (dwStyles & FDE_TTOSTYLE_ArabicShapes) {
     77     m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicShapes;
     78   }
     79   if (dwStyles & FDE_TTOSTYLE_ArabicContext) {
     80     m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicContext;
     81   }
     82   if (dwStyles & FDE_TTOSTYLE_VerticalLayout) {
     83     m_dwTxtBkStyles |=
     84         (FX_TXTLAYOUTSTYLE_VerticalChars | FX_TXTLAYOUTSTYLE_VerticalLayout);
     85   }
     86   m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles);
     87 }
     88 
     89 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth) {
     90   ASSERT(fTabWidth > 1.0f);
     91   m_pTxtBreak->SetTabWidth(fTabWidth, false);
     92 }
     93 
     94 void CFDE_TextOut::SetEllipsisString(const CFX_WideString& wsEllipsis) {
     95   m_bElliChanged = true;
     96   m_wsEllipsis = wsEllipsis;
     97 }
     98 
     99 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch) {
    100   m_wParagraphBkChar = wch;
    101   m_pTxtBreak->SetParagraphBreakChar(wch);
    102 }
    103 
    104 void CFDE_TextOut::SetAlignment(int32_t iAlignment) {
    105   m_iAlignment = iAlignment;
    106   switch (m_iAlignment) {
    107     case FDE_TTOALIGNMENT_TopCenter:
    108     case FDE_TTOALIGNMENT_Center:
    109     case FDE_TTOALIGNMENT_BottomCenter:
    110       m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Center;
    111       break;
    112     case FDE_TTOALIGNMENT_TopRight:
    113     case FDE_TTOALIGNMENT_CenterRight:
    114     case FDE_TTOALIGNMENT_BottomRight:
    115       m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Right;
    116       break;
    117     default:
    118       m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Left;
    119       break;
    120   }
    121   m_pTxtBreak->SetAlignment(m_iTxtBkAlignment);
    122 }
    123 
    124 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace) {
    125   ASSERT(fLineSpace > 1.0f);
    126   m_fLineSpace = fLineSpace;
    127 }
    128 
    129 void CFDE_TextOut::SetDIBitmap(CFX_DIBitmap* pDIB) {
    130   ASSERT(pDIB);
    131 
    132   m_pRenderDevice.reset();
    133   CFX_FxgeDevice* device = new CFX_FxgeDevice;
    134   device->Attach(pDIB, false, nullptr, false);
    135   m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(device, false);
    136 }
    137 
    138 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice* pDevice) {
    139   ASSERT(pDevice);
    140   m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(pDevice, false);
    141 }
    142 
    143 void CFDE_TextOut::SetClipRect(const CFX_Rect& rtClip) {
    144   m_rtClip = rtClip.As<FX_FLOAT>();
    145 }
    146 
    147 void CFDE_TextOut::SetClipRect(const CFX_RectF& rtClip) {
    148   m_rtClip = rtClip;
    149 }
    150 
    151 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF& rtClip) {
    152   m_rtLogicClip = rtClip;
    153 }
    154 
    155 void CFDE_TextOut::SetMatrix(const CFX_Matrix& matrix) {
    156   m_Matrix = matrix;
    157 }
    158 
    159 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance) {
    160   m_fTolerance = fTolerance;
    161   m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);
    162 }
    163 
    164 int32_t CFDE_TextOut::GetTotalLines() {
    165   return m_iTotalLines;
    166 }
    167 
    168 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr,
    169                                  int32_t iLength,
    170                                  CFX_SizeF& size) {
    171   CFX_RectF rtText(0.0f, 0.0f, size.width, size.height);
    172   CalcLogicSize(pwsStr, iLength, rtText);
    173   size = rtText.Size();
    174 }
    175 
    176 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr,
    177                                  int32_t iLength,
    178                                  CFX_RectF& rect) {
    179   if (!pwsStr || iLength < 1) {
    180     rect.width = 0.0f;
    181     rect.height = 0.0f;
    182   } else {
    183     CalcTextSize(pwsStr, iLength, rect);
    184   }
    185 }
    186 
    187 void CFDE_TextOut::CalcTextSize(const FX_WCHAR* pwsStr,
    188                                 int32_t iLength,
    189                                 CFX_RectF& rect) {
    190   ASSERT(m_pFont && m_fFontSize >= 1.0f);
    191   SetLineWidth(rect);
    192   m_iTotalLines = 0;
    193   const FX_WCHAR* pStr = pwsStr;
    194   bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey);
    195   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    196   FX_FLOAT fWidth = 0.0f;
    197   FX_FLOAT fHeight = 0.0f;
    198   FX_FLOAT fStartPos = bVertical ? rect.bottom() : rect.right();
    199   uint32_t dwBreakStatus = 0;
    200   FX_WCHAR wPreChar = 0;
    201   FX_WCHAR wch;
    202   FX_WCHAR wBreak = 0;
    203   while (iLength-- > 0) {
    204     wch = *pStr++;
    205     if (wBreak == 0 && (wch == L'\n' || wch == L'\r')) {
    206       wBreak = wch;
    207       m_pTxtBreak->SetParagraphBreakChar(wch);
    208     }
    209     if (bHotKey && wch == L'&' && wPreChar != L'&') {
    210       wPreChar = wch;
    211       continue;
    212     }
    213     dwBreakStatus = m_pTxtBreak->AppendChar(wch);
    214     if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
    215       RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);
    216     }
    217     wPreChar = 0;
    218   }
    219   dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
    220   if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
    221     RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight);
    222   }
    223   m_pTxtBreak->Reset();
    224   FX_FLOAT fInc = rect.Height() - fHeight;
    225   if (bVertical) {
    226     fInc = rect.Width() - fHeight;
    227   }
    228   if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft &&
    229       m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) {
    230     fInc /= 2.0f;
    231   } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) {
    232     fInc = 0.0f;
    233   }
    234   if (bVertical) {
    235     rect.top += fStartPos;
    236     rect.left += fInc;
    237     rect.width = fHeight;
    238     rect.height = std::min(fWidth, rect.Height());
    239   } else {
    240     rect.left += fStartPos;
    241     rect.top += fInc;
    242     rect.width = std::min(fWidth, rect.Width());
    243     rect.height = fHeight;
    244     if (m_dwStyles & FDE_TTOSTYLE_LastLineHeight) {
    245       rect.height -= m_fLineSpace - m_fFontSize;
    246     }
    247   }
    248 }
    249 
    250 void CFDE_TextOut::SetLineWidth(CFX_RectF& rect) {
    251   if ((m_dwStyles & FDE_TTOSTYLE_SingleLine) == 0) {
    252     FX_FLOAT fLineWidth = 0.0f;
    253     if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) {
    254       if (rect.Height() < 1.0f) {
    255         rect.height = m_fFontSize * 1000.0f;
    256       }
    257       fLineWidth = rect.Height();
    258     } else {
    259       if (rect.Width() < 1.0f) {
    260         rect.width = m_fFontSize * 1000.0f;
    261       }
    262       fLineWidth = rect.Width();
    263     }
    264     m_pTxtBreak->SetLineWidth(fLineWidth);
    265   }
    266 }
    267 
    268 bool CFDE_TextOut::RetrieveLineWidth(uint32_t dwBreakStatus,
    269                                      FX_FLOAT& fStartPos,
    270                                      FX_FLOAT& fWidth,
    271                                      FX_FLOAT& fHeight) {
    272   if (dwBreakStatus <= FX_TXTBREAK_PieceBreak) {
    273     return false;
    274   }
    275   FX_FLOAT fLineStep =
    276       (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
    277   bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap);
    278   FX_FLOAT fLineWidth = 0.0f;
    279   int32_t iCount = m_pTxtBreak->CountBreakPieces();
    280   for (int32_t i = 0; i < iCount; i++) {
    281     const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i);
    282     fLineWidth += (FX_FLOAT)pPiece->m_iWidth / 20000.0f;
    283     fStartPos = std::min(fStartPos, (FX_FLOAT)pPiece->m_iStartPos / 20000.0f);
    284   }
    285   m_pTxtBreak->ClearBreakPieces();
    286   if (dwBreakStatus == FX_TXTBREAK_ParagraphBreak) {
    287     m_pTxtBreak->Reset();
    288   }
    289   if (!bLineWrap && dwBreakStatus == FX_TXTBREAK_LineBreak) {
    290     fWidth += fLineWidth;
    291   } else {
    292     fWidth = std::max(fWidth, fLineWidth);
    293     fHeight += fLineStep;
    294   }
    295   m_iTotalLines++;
    296   return true;
    297 }
    298 
    299 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr,
    300                             int32_t iLength,
    301                             int32_t x,
    302                             int32_t y) {
    303   CFX_RectF rtText(static_cast<FX_FLOAT>(x), static_cast<FX_FLOAT>(y),
    304                    m_fFontSize * 1000.0f, m_fFontSize * 1000.0f);
    305   DrawText(pwsStr, iLength, rtText);
    306 }
    307 
    308 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr,
    309                             int32_t iLength,
    310                             FX_FLOAT x,
    311                             FX_FLOAT y) {
    312   DrawText(pwsStr, iLength,
    313            CFX_RectF(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f));
    314 }
    315 
    316 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr,
    317                             int32_t iLength,
    318                             const CFX_Rect& rect) {
    319   DrawText(pwsStr, iLength, rect.As<FX_FLOAT>());
    320 }
    321 
    322 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr,
    323                             int32_t iLength,
    324                             const CFX_RectF& rect) {
    325   CFX_RectF rtText(rect.left, rect.top, rect.width, rect.height);
    326   CFX_Matrix rm;
    327   rm.SetReverse(m_Matrix);
    328   rm.TransformRect(rtText);
    329   DrawText(pwsStr, iLength, rtText, m_rtClip);
    330 }
    331 
    332 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr,
    333                                  int32_t iLength,
    334                                  FX_FLOAT x,
    335                                  FX_FLOAT y) {
    336   CFX_RectF rtText(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f);
    337   DrawLogicText(pwsStr, iLength, rtText);
    338 }
    339 
    340 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr,
    341                                  int32_t iLength,
    342                                  const CFX_RectF& rect) {
    343   CFX_RectF rtClip(m_rtLogicClip.left, m_rtLogicClip.top, m_rtLogicClip.width,
    344                    m_rtLogicClip.height);
    345   m_Matrix.TransformRect(rtClip);
    346   DrawText(pwsStr, iLength, rect, rtClip);
    347 }
    348 
    349 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr,
    350                             int32_t iLength,
    351                             const CFX_RectF& rect,
    352                             const CFX_RectF& rtClip) {
    353   ASSERT(m_pFont && m_fFontSize >= 1.0f);
    354   if (!pwsStr || iLength < 1)
    355     return;
    356 
    357   if (rect.width < m_fFontSize || rect.height < m_fFontSize) {
    358     return;
    359   }
    360   FX_FLOAT fLineWidth = rect.width;
    361   if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) {
    362     fLineWidth = rect.height;
    363   }
    364   m_pTxtBreak->SetLineWidth(fLineWidth);
    365   m_ttoLines.clear();
    366   m_wsText.clear();
    367   LoadText(pwsStr, iLength, rect);
    368   if (m_dwStyles & FDE_TTOSTYLE_Ellipsis) {
    369     ReplaceWidthEllipsis();
    370   }
    371   Reload(rect);
    372   DoAlignment(rect);
    373   OnDraw(rtClip);
    374 }
    375 
    376 void CFDE_TextOut::ExpandBuffer(int32_t iSize, int32_t iType) {
    377   ASSERT(iSize >= 0);
    378   size_t size = iSize;
    379   switch (iType) {
    380     case 0:
    381       if (m_CharWidths.size() < size)
    382         m_CharWidths.resize(size, 0);
    383       break;
    384     case 1:
    385       if (m_EllCharWidths.size() < size)
    386         m_EllCharWidths.resize(size, 0);
    387       break;
    388     case 2:
    389       if (m_CharPos.size() < size)
    390         m_CharPos.resize(size, FXTEXT_CHARPOS());
    391       break;
    392   }
    393 }
    394 
    395 void CFDE_TextOut::LoadEllipsis() {
    396   if (!m_bElliChanged) {
    397     return;
    398   }
    399   m_bElliChanged = false;
    400   m_iEllipsisWidth = 0;
    401   int32_t iLength = m_wsEllipsis.GetLength();
    402   if (iLength < 1) {
    403     return;
    404   }
    405   ExpandBuffer(iLength, 1);
    406   const FX_WCHAR* pStr = m_wsEllipsis.c_str();
    407   uint32_t dwBreakStatus;
    408   FX_WCHAR wch;
    409   while (iLength-- > 0) {
    410     wch = *pStr++;
    411     dwBreakStatus = m_pTxtBreak->AppendChar(wch);
    412     if (dwBreakStatus > FX_TXTBREAK_PieceBreak)
    413       RetrieveEllPieces(&m_EllCharWidths);
    414   }
    415   dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
    416   if (dwBreakStatus > FX_TXTBREAK_PieceBreak)
    417     RetrieveEllPieces(&m_EllCharWidths);
    418   m_pTxtBreak->Reset();
    419 }
    420 
    421 void CFDE_TextOut::RetrieveEllPieces(std::vector<int32_t>* pCharWidths) {
    422   int32_t iCount = m_pTxtBreak->CountBreakPieces();
    423   int32_t iCharIndex = 0;
    424   for (int32_t i = 0; i < iCount; i++) {
    425     const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i);
    426     int32_t iPieceChars = pPiece->GetLength();
    427     for (int32_t j = 0; j < iPieceChars; j++) {
    428       CFX_Char* pTC = pPiece->GetCharPtr(j);
    429       (*pCharWidths)[iCharIndex] = std::max(pTC->m_iCharWidth, 0);
    430       m_iEllipsisWidth += (*pCharWidths)[iCharIndex];
    431       iCharIndex++;
    432     }
    433   }
    434   m_pTxtBreak->ClearBreakPieces();
    435 }
    436 
    437 void CFDE_TextOut::LoadText(const FX_WCHAR* pwsStr,
    438                             int32_t iLength,
    439                             const CFX_RectF& rect) {
    440   FX_WCHAR* pStr = m_wsText.GetBuffer(iLength);
    441   int32_t iTxtLength = iLength;
    442   ExpandBuffer(iTxtLength, 0);
    443   bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey);
    444   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    445   bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap);
    446   FX_FLOAT fLineStep =
    447       (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
    448   FX_FLOAT fLineStop = bVertical ? rect.left : rect.bottom();
    449   m_fLinePos = bVertical ? rect.right() : rect.top;
    450   if (bVertical) {
    451     fLineStep = -fLineStep;
    452   }
    453   m_hotKeys.RemoveAll();
    454   int32_t iStartChar = 0;
    455   int32_t iChars = 0;
    456   int32_t iPieceWidths = 0;
    457   uint32_t dwBreakStatus;
    458   FX_WCHAR wch;
    459   bool bRet = false;
    460   while (iTxtLength-- > 0) {
    461     wch = *pwsStr++;
    462     if (bHotKey && wch == L'&' && *(pStr - 1) != L'&') {
    463       if (iTxtLength > 0)
    464         m_hotKeys.Add(iChars);
    465       continue;
    466     }
    467     *pStr++ = wch;
    468     iChars++;
    469     dwBreakStatus = m_pTxtBreak->AppendChar(wch);
    470     if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
    471       bool bEndofLine =
    472           RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect);
    473       if (bEndofLine && (bLineWrap || (dwBreakStatus > FX_TXTBREAK_LineBreak &&
    474                                        !bLineWrap))) {
    475         iPieceWidths = 0;
    476         m_iCurLine++;
    477         m_fLinePos += fLineStep;
    478       }
    479       if ((bVertical && m_fLinePos + fLineStep < fLineStop) ||
    480           (!bVertical && m_fLinePos + fLineStep > fLineStop)) {
    481         int32_t iCurLine = bEndofLine ? m_iCurLine - 1 : m_iCurLine;
    482         m_ttoLines[iCurLine].SetNewReload(true);
    483         bRet = true;
    484         break;
    485       }
    486     }
    487   }
    488   dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
    489   if (dwBreakStatus > FX_TXTBREAK_PieceBreak && !bRet) {
    490     RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect);
    491   }
    492   m_pTxtBreak->ClearBreakPieces();
    493   m_pTxtBreak->Reset();
    494   m_wsText.ReleaseBuffer(iLength);
    495 }
    496 
    497 bool CFDE_TextOut::RetriecePieces(uint32_t dwBreakStatus,
    498                                   int32_t& iStartChar,
    499                                   int32_t& iPieceWidths,
    500                                   bool bReload,
    501                                   const CFX_RectF& rect) {
    502   bool bSingleLine = !!(m_dwStyles & FDE_TTOSTYLE_SingleLine);
    503   bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap);
    504   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    505   FX_FLOAT fLineStep =
    506       (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize;
    507   if (bVertical) {
    508     fLineStep = -fLineStep;
    509   }
    510   CFX_Char* pTC = nullptr;
    511   bool bNeedReload = false;
    512   FX_FLOAT fLineWidth = bVertical ? rect.Height() : rect.Width();
    513   int32_t iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
    514   int32_t iCount = m_pTxtBreak->CountBreakPieces();
    515   for (int32_t i = 0; i < iCount; i++) {
    516     const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i);
    517     int32_t iPieceChars = pPiece->GetLength();
    518     int32_t iChar = iStartChar;
    519     int32_t iWidth = 0;
    520     int32_t j = 0;
    521     for (; j < iPieceChars; j++) {
    522       pTC = pPiece->GetCharPtr(j);
    523       int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0;
    524       if (bSingleLine || !bLineWrap) {
    525         if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) {
    526           bNeedReload = true;
    527           break;
    528         }
    529       }
    530       iWidth += iCurCharWidth;
    531       m_CharWidths[iChar++] = iCurCharWidth;
    532     }
    533     if (j == 0 && !bReload) {
    534       m_ttoLines[m_iCurLine].SetNewReload(true);
    535     } else if (j > 0) {
    536       CFX_RectF rtPiece;
    537       if (bVertical) {
    538         rtPiece.left = m_fLinePos;
    539         rtPiece.top = rect.top + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;
    540         rtPiece.width = fLineStep;
    541         rtPiece.height = iWidth / 20000.0f;
    542       } else {
    543         rtPiece.left = rect.left + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;
    544         rtPiece.top = m_fLinePos;
    545         rtPiece.width = iWidth / 20000.0f;
    546         rtPiece.height = fLineStep;
    547       }
    548       FDE_TTOPIECE ttoPiece;
    549       ttoPiece.iStartChar = iStartChar;
    550       ttoPiece.iChars = j;
    551       ttoPiece.rtPiece = rtPiece;
    552       ttoPiece.dwCharStyles = pPiece->m_dwCharStyles;
    553       if (FX_IsOdd(pPiece->m_iBidiLevel)) {
    554         ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
    555       }
    556       AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1));
    557     }
    558     iStartChar += iPieceChars;
    559     iPieceWidths += iWidth;
    560   }
    561   m_pTxtBreak->ClearBreakPieces();
    562   bool bRet = bSingleLine || bLineWrap || (!bLineWrap && bNeedReload) ||
    563               dwBreakStatus == FX_TXTBREAK_ParagraphBreak;
    564   return bRet;
    565 }
    566 
    567 void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE& ttoPiece,
    568                                bool bNeedReload,
    569                                bool bEnd) {
    570   if (m_iCurLine >= pdfium::CollectionSize<int32_t>(m_ttoLines)) {
    571     CFDE_TTOLine ttoLine;
    572     ttoLine.SetNewReload(bNeedReload);
    573     m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, ttoPiece);
    574     m_ttoLines.push_back(ttoLine);
    575     m_iCurLine = pdfium::CollectionSize<int32_t>(m_ttoLines) - 1;
    576   } else {
    577     CFDE_TTOLine* pLine = &m_ttoLines[m_iCurLine];
    578     pLine->SetNewReload(bNeedReload);
    579     m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece);
    580     if (bEnd) {
    581       int32_t iPieces = pLine->GetSize();
    582       if (m_iCurPiece < iPieces) {
    583         pLine->RemoveLast(iPieces - m_iCurPiece - 1);
    584       }
    585     }
    586   }
    587   if (!bEnd && bNeedReload)
    588     m_iCurPiece = 0;
    589 }
    590 
    591 void CFDE_TextOut::ReplaceWidthEllipsis() {
    592   LoadEllipsis();
    593   int32_t iLength = m_wsEllipsis.GetLength();
    594   if (iLength < 1)
    595     return;
    596 
    597   for (auto& line : m_ttoLines) {
    598     if (!line.GetNewReload())
    599       continue;
    600 
    601     int32_t iEllipsisCharIndex = iLength - 1;
    602     int32_t iCharWidth = 0;
    603     int32_t iCharCount = 0;
    604     int32_t iPiece = line.GetSize();
    605     while (iPiece-- > 0) {
    606       FDE_TTOPIECE* pPiece = line.GetPtrAt(iPiece);
    607       if (!pPiece)
    608         break;
    609 
    610       for (int32_t j = pPiece->iChars - 1; j >= 0; j--) {
    611         if (iEllipsisCharIndex < 0)
    612           break;
    613 
    614         int32_t index = pPiece->iStartChar + j;
    615         iCharWidth += m_CharWidths[index];
    616         iCharCount++;
    617         if (iCharCount <= iLength) {
    618           m_wsText.SetAt(index, m_wsEllipsis.GetAt(iEllipsisCharIndex));
    619           m_CharWidths[index] = m_EllCharWidths[iEllipsisCharIndex];
    620         } else if (iCharWidth <= m_iEllipsisWidth) {
    621           m_wsText.SetAt(index, 0);
    622           m_CharWidths[index] = 0;
    623         }
    624         iEllipsisCharIndex--;
    625       }
    626       if (iEllipsisCharIndex < 0)
    627         break;
    628     }
    629   }
    630 }
    631 
    632 void CFDE_TextOut::Reload(const CFX_RectF& rect) {
    633   int i = 0;
    634   for (auto& line : m_ttoLines) {
    635     if (line.GetNewReload()) {
    636       m_iCurLine = i;
    637       m_iCurPiece = 0;
    638       ReloadLinePiece(&line, rect);
    639     }
    640     ++i;
    641   }
    642 }
    643 
    644 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) {
    645   const FX_WCHAR* pwsStr = m_wsText.c_str();
    646   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    647   int32_t iPieceWidths = 0;
    648   FDE_TTOPIECE* pPiece = pLine->GetPtrAt(0);
    649   int32_t iStartChar = pPiece->iStartChar;
    650   m_fLinePos = bVertical ? pPiece->rtPiece.left : pPiece->rtPiece.top;
    651   int32_t iPieceCount = pLine->GetSize();
    652   int32_t iPieceIndex = 0;
    653   uint32_t dwBreakStatus = 0;
    654   FX_WCHAR wch;
    655   while (iPieceIndex < iPieceCount) {
    656     int32_t iStar = iStartChar;
    657     int32_t iEnd = pPiece->iChars + iStar;
    658     while (iStar < iEnd) {
    659       wch = *(pwsStr + iStar);
    660       dwBreakStatus = m_pTxtBreak->AppendChar(wch);
    661       if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
    662         RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect);
    663       }
    664       iStar++;
    665     }
    666     iPieceIndex++;
    667     pPiece = pLine->GetPtrAt(iPieceIndex);
    668   }
    669   dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
    670   if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
    671     RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect);
    672   }
    673   m_pTxtBreak->Reset();
    674 }
    675 
    676 void CFDE_TextOut::DoAlignment(const CFX_RectF& rect) {
    677   if (m_ttoLines.empty())
    678     return;
    679 
    680   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    681   FX_FLOAT fLineStopS = bVertical ? rect.right() : rect.bottom();
    682   FDE_TTOPIECE* pFirstPiece = m_ttoLines.back().GetPtrAt(0);
    683   if (!pFirstPiece)
    684     return;
    685 
    686   FX_FLOAT fLineStopD =
    687       bVertical ? pFirstPiece->rtPiece.right() : pFirstPiece->rtPiece.bottom();
    688   FX_FLOAT fInc = fLineStopS - fLineStopD;
    689   if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft &&
    690       m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) {
    691     fInc /= 2.0f;
    692   } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) {
    693     fInc = 0.0f;
    694   }
    695   if (fInc < 1.0f)
    696     return;
    697   for (auto& line : m_ttoLines) {
    698     int32_t iPieces = line.GetSize();
    699     for (int32_t j = 0; j < iPieces; j++) {
    700       FDE_TTOPIECE* pPiece = line.GetPtrAt(j);
    701       if (bVertical)
    702         pPiece->rtPiece.left += fInc;
    703       else
    704         pPiece->rtPiece.top += fInc;
    705     }
    706   }
    707 }
    708 
    709 void CFDE_TextOut::OnDraw(const CFX_RectF& rtClip) {
    710   if (!m_pRenderDevice || m_ttoLines.empty())
    711     return;
    712 
    713   auto pBrush = pdfium::MakeUnique<CFDE_Brush>();
    714   pBrush->SetColor(m_TxtColor);
    715   m_pRenderDevice->SaveState();
    716   if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f)
    717     m_pRenderDevice->SetClipRect(rtClip);
    718 
    719   auto pPen = pdfium::MakeUnique<CFDE_Pen>();
    720   pPen->SetColor(m_TxtColor);
    721 
    722   for (auto& line : m_ttoLines) {
    723     int32_t iPieces = line.GetSize();
    724     for (int32_t j = 0; j < iPieces; j++) {
    725       FDE_TTOPIECE* pPiece = line.GetPtrAt(j);
    726       if (!pPiece)
    727         continue;
    728 
    729       int32_t iCount = GetDisplayPos(pPiece);
    730       if (iCount > 0) {
    731         m_pRenderDevice->DrawString(pBrush.get(), m_pFont, m_CharPos.data(),
    732                                     iCount, m_fFontSize, &m_Matrix);
    733       }
    734       DrawLine(pPiece, pPen.get());
    735     }
    736   }
    737   m_pRenderDevice->RestoreState();
    738 }
    739 
    740 int32_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) {
    741   FX_TXTRUN tr = ToTextRun(pPiece);
    742   ExpandBuffer(tr.iLength, 2);
    743   return m_pTxtBreak->GetDisplayPos(&tr, m_CharPos.data());
    744 }
    745 
    746 int32_t CFDE_TextOut::GetCharRects(const FDE_TTOPIECE* pPiece) {
    747   FX_TXTRUN tr = ToTextRun(pPiece);
    748   m_rectArray = m_pTxtBreak->GetCharRects(&tr);
    749   return pdfium::CollectionSize<int32_t>(m_rectArray);
    750 }
    751 
    752 FX_TXTRUN CFDE_TextOut::ToTextRun(const FDE_TTOPIECE* pPiece) {
    753   FX_TXTRUN tr;
    754   tr.wsStr = m_wsText + pPiece->iStartChar;
    755   tr.pWidths = &m_CharWidths[pPiece->iStartChar];
    756   tr.iLength = pPiece->iChars;
    757   tr.pFont = m_pFont;
    758   tr.fFontSize = m_fFontSize;
    759   tr.dwStyles = m_dwTxtBkStyles;
    760   tr.dwCharStyles = pPiece->dwCharStyles;
    761   tr.wLineBreakChar = m_wParagraphBkChar;
    762   tr.pRect = &pPiece->rtPiece;
    763   return tr;
    764 }
    765 
    766 void CFDE_TextOut::DrawLine(const FDE_TTOPIECE* pPiece, CFDE_Pen* pPen) {
    767   bool bUnderLine = !!(m_dwStyles & FDE_TTOSTYLE_Underline);
    768   bool bStrikeOut = !!(m_dwStyles & FDE_TTOSTYLE_Strikeout);
    769   bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey);
    770   bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout);
    771   if (!bUnderLine && !bStrikeOut && !bHotKey)
    772     return;
    773 
    774   std::unique_ptr<CFDE_Path> pPath(new CFDE_Path);
    775   int32_t iLineCount = 0;
    776   CFX_RectF rtText = pPiece->rtPiece;
    777   CFX_PointF pt1, pt2;
    778   if (bUnderLine) {
    779     if (bVertical) {
    780       pt1.x = rtText.left;
    781       pt1.y = rtText.top;
    782       pt2.x = rtText.left;
    783       pt2.y = rtText.bottom();
    784     } else {
    785       pt1.x = rtText.left;
    786       pt1.y = rtText.bottom();
    787       pt2.x = rtText.right();
    788       pt2.y = rtText.bottom();
    789     }
    790     pPath->AddLine(pt1, pt2);
    791     iLineCount++;
    792   }
    793   if (bStrikeOut) {
    794     if (bVertical) {
    795       pt1.x = rtText.left + rtText.width * 2.0f / 5.0f;
    796       pt1.y = rtText.top;
    797       pt2.x = pt1.x;
    798       pt2.y = rtText.bottom();
    799     } else {
    800       pt1.x = rtText.left;
    801       pt1.y = rtText.bottom() - rtText.height * 2.0f / 5.0f;
    802       pt2.x = rtText.right();
    803       pt2.y = pt1.y;
    804     }
    805     pPath->AddLine(pt1, pt2);
    806     iLineCount++;
    807   }
    808   if (bHotKey) {
    809     int32_t iHotKeys = m_hotKeys.GetSize();
    810     int32_t iCount = GetCharRects(pPiece);
    811     if (iCount > 0) {
    812       for (int32_t i = 0; i < iHotKeys; i++) {
    813         int32_t iCharIndex = m_hotKeys.GetAt(i);
    814         if (iCharIndex >= pPiece->iStartChar &&
    815             iCharIndex < pPiece->iStartChar + pPiece->iChars) {
    816           CFX_RectF rect = m_rectArray[iCharIndex - pPiece->iStartChar];
    817           if (bVertical) {
    818             pt1.x = rect.left;
    819             pt1.y = rect.top;
    820             pt2.x = rect.left;
    821             pt2.y = rect.bottom();
    822           } else {
    823             pt1.x = rect.left;
    824             pt1.y = rect.bottom();
    825             pt2.x = rect.right();
    826             pt2.y = rect.bottom();
    827           }
    828           pPath->AddLine(pt1, pt2);
    829           iLineCount++;
    830         }
    831       }
    832     }
    833   }
    834   if (iLineCount > 0)
    835     m_pRenderDevice->DrawPath(pPen, 1, pPath.get(), &m_Matrix);
    836 }
    837 
    838 CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {}
    839 
    840 CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine& ttoLine) : m_pieces(5) {
    841   m_bNewReload = ttoLine.m_bNewReload;
    842   m_pieces = ttoLine.m_pieces;
    843 }
    844 
    845 CFDE_TTOLine::~CFDE_TTOLine() {}
    846 
    847 int32_t CFDE_TTOLine::AddPiece(int32_t index, const FDE_TTOPIECE& ttoPiece) {
    848   if (index >= pdfium::CollectionSize<int32_t>(m_pieces)) {
    849     m_pieces.push_back(ttoPiece);
    850     return pdfium::CollectionSize<int32_t>(m_pieces);
    851   }
    852   m_pieces[index] = ttoPiece;
    853   return index;
    854 }
    855 
    856 int32_t CFDE_TTOLine::GetSize() const {
    857   return pdfium::CollectionSize<int32_t>(m_pieces);
    858 }
    859 
    860 FDE_TTOPIECE* CFDE_TTOLine::GetPtrAt(int32_t index) {
    861   if (index < 0 || index >= pdfium::CollectionSize<int32_t>(m_pieces))
    862     return nullptr;
    863 
    864   return &m_pieces[index];
    865 }
    866 
    867 void CFDE_TTOLine::RemoveLast(int32_t icount) {
    868   if (icount < 0)
    869     return;
    870   icount = std::min(icount, pdfium::CollectionSize<int32_t>(m_pieces));
    871   m_pieces.erase(m_pieces.end() - icount, m_pieces.end());
    872 }
    873 
    874 void CFDE_TTOLine::RemoveAll() {
    875   m_pieces.clear();
    876 }
    877