• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CFDE_TextOut()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 
~CFDE_TextOut()49 CFDE_TextOut::~CFDE_TextOut() {}
50 
SetFont(const CFX_RetainPtr<CFGAS_GEFont> & pFont)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 
SetFontSize(FX_FLOAT fFontSize)57 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize) {
58   ASSERT(fFontSize > 0);
59   m_fFontSize = fFontSize;
60   m_pTxtBreak->SetFontSize(fFontSize);
61 }
62 
SetTextColor(FX_ARGB color)63 void CFDE_TextOut::SetTextColor(FX_ARGB color) {
64   m_TxtColor = color;
65 }
66 
SetStyles(uint32_t dwStyles)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 
SetTabWidth(FX_FLOAT fTabWidth)89 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth) {
90   ASSERT(fTabWidth > 1.0f);
91   m_pTxtBreak->SetTabWidth(fTabWidth, false);
92 }
93 
SetEllipsisString(const CFX_WideString & wsEllipsis)94 void CFDE_TextOut::SetEllipsisString(const CFX_WideString& wsEllipsis) {
95   m_bElliChanged = true;
96   m_wsEllipsis = wsEllipsis;
97 }
98 
SetParagraphBreakChar(FX_WCHAR wch)99 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch) {
100   m_wParagraphBkChar = wch;
101   m_pTxtBreak->SetParagraphBreakChar(wch);
102 }
103 
SetAlignment(int32_t iAlignment)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 
SetLineSpace(FX_FLOAT fLineSpace)124 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace) {
125   ASSERT(fLineSpace > 1.0f);
126   m_fLineSpace = fLineSpace;
127 }
128 
SetDIBitmap(CFX_DIBitmap * pDIB)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 
SetRenderDevice(CFX_RenderDevice * pDevice)138 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice* pDevice) {
139   ASSERT(pDevice);
140   m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(pDevice, false);
141 }
142 
SetClipRect(const CFX_Rect & rtClip)143 void CFDE_TextOut::SetClipRect(const CFX_Rect& rtClip) {
144   m_rtClip = rtClip.As<FX_FLOAT>();
145 }
146 
SetClipRect(const CFX_RectF & rtClip)147 void CFDE_TextOut::SetClipRect(const CFX_RectF& rtClip) {
148   m_rtClip = rtClip;
149 }
150 
SetLogicClipRect(const CFX_RectF & rtClip)151 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF& rtClip) {
152   m_rtLogicClip = rtClip;
153 }
154 
SetMatrix(const CFX_Matrix & matrix)155 void CFDE_TextOut::SetMatrix(const CFX_Matrix& matrix) {
156   m_Matrix = matrix;
157 }
158 
SetLineBreakTolerance(FX_FLOAT fTolerance)159 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance) {
160   m_fTolerance = fTolerance;
161   m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);
162 }
163 
GetTotalLines()164 int32_t CFDE_TextOut::GetTotalLines() {
165   return m_iTotalLines;
166 }
167 
CalcLogicSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_SizeF & size)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 
CalcLogicSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_RectF & rect)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 
CalcTextSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_RectF & rect)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 
SetLineWidth(CFX_RectF & rect)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 
RetrieveLineWidth(uint32_t dwBreakStatus,FX_FLOAT & fStartPos,FX_FLOAT & fWidth,FX_FLOAT & fHeight)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 
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,int32_t x,int32_t y)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 
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,FX_FLOAT x,FX_FLOAT y)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 
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_Rect & rect)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 
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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 
DrawLogicText(const FX_WCHAR * pwsStr,int32_t iLength,FX_FLOAT x,FX_FLOAT y)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 
DrawLogicText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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 
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect,const CFX_RectF & rtClip)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 
ExpandBuffer(int32_t iSize,int32_t iType)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 
LoadEllipsis()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 
RetrieveEllPieces(std::vector<int32_t> * pCharWidths)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 
LoadText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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 
RetriecePieces(uint32_t dwBreakStatus,int32_t & iStartChar,int32_t & iPieceWidths,bool bReload,const CFX_RectF & rect)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 
AppendPiece(const FDE_TTOPIECE & ttoPiece,bool bNeedReload,bool bEnd)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 
ReplaceWidthEllipsis()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 
Reload(const CFX_RectF & rect)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 
ReloadLinePiece(CFDE_TTOLine * pLine,const CFX_RectF & rect)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 
DoAlignment(const CFX_RectF & rect)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 
OnDraw(const CFX_RectF & rtClip)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 
GetDisplayPos(FDE_TTOPIECE * pPiece)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 
GetCharRects(const FDE_TTOPIECE * pPiece)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 
ToTextRun(const FDE_TTOPIECE * pPiece)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 
DrawLine(const FDE_TTOPIECE * pPiece,CFDE_Pen * pPen)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 
CFDE_TTOLine()838 CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {}
839 
CFDE_TTOLine(const CFDE_TTOLine & ttoLine)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 
~CFDE_TTOLine()845 CFDE_TTOLine::~CFDE_TTOLine() {}
846 
AddPiece(int32_t index,const FDE_TTOPIECE & ttoPiece)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 
GetSize() const856 int32_t CFDE_TTOLine::GetSize() const {
857   return pdfium::CollectionSize<int32_t>(m_pieces);
858 }
859 
GetPtrAt(int32_t index)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 
RemoveLast(int32_t icount)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 
RemoveAll()874 void CFDE_TTOLine::RemoveAll() {
875   m_pieces.clear();
876 }
877