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/fgas/layout/fgas_textbreak.h"
8
9#include <algorithm>
10
11#include "core/fxcrt/fx_arabic.h"
12#include "core/fxcrt/fx_arb.h"
13#include "core/fxcrt/fx_memory.h"
14#include "third_party/base/ptr_util.h"
15#include "xfa/fgas/font/cfgas_gefont.h"
16#include "xfa/fgas/layout/fgas_linebreak.h"
17
18namespace {
19
20typedef uint32_t (CFX_TxtBreak::*FX_TxtBreak_LPFAppendChar)(
21    CFX_TxtChar* pCurChar,
22    int32_t iRotation);
23const FX_TxtBreak_LPFAppendChar g_FX_TxtBreak_lpfAppendChar[16] = {
24    &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Tab,
25    &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Control,
26    &CFX_TxtBreak::AppendChar_Combination, &CFX_TxtBreak::AppendChar_Others,
27    &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Arabic,
28    &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Arabic,
29    &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Arabic,
30    &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Others,
31    &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Others,
32};
33
34}  // namespace
35
36CFX_TxtBreak::CFX_TxtBreak(uint32_t dwPolicies)
37    : m_dwPolicies(dwPolicies),
38      m_iLineWidth(2000000),
39      m_dwLayoutStyles(0),
40      m_bVertical(false),
41      m_bArabicContext(false),
42      m_bArabicShapes(false),
43      m_bRTL(false),
44      m_bSingleLine(false),
45      m_bCombText(false),
46      m_iArabicContext(1),
47      m_iCurArabicContext(1),
48      m_pFont(nullptr),
49      m_iFontSize(240),
50      m_bEquidistant(true),
51      m_iTabWidth(720000),
52      m_wDefChar(0xFEFF),
53      m_wParagBreakChar(L'\n'),
54      m_iDefChar(0),
55      m_iLineRotation(0),
56      m_iCharRotation(0),
57      m_iRotation(0),
58      m_iAlignment(FX_TXTLINEALIGNMENT_Left),
59      m_dwContextCharStyles(0),
60      m_iCombWidth(360000),
61      m_pUserData(nullptr),
62      m_eCharType(FX_CHARTYPE_Unknown),
63      m_bArabicNumber(false),
64      m_bArabicComma(false),
65      m_pCurLine(nullptr),
66      m_iReady(0),
67      m_iTolerance(0),
68      m_iHorScale(100),
69      m_iCharSpace(0) {
70  m_bPagination = (m_dwPolicies & FX_TXTBREAKPOLICY_Pagination) != 0;
71  int32_t iSize = m_bPagination ? sizeof(CFX_Char) : sizeof(CFX_TxtChar);
72  m_pTxtLine1 = pdfium::MakeUnique<CFX_TxtLine>(iSize);
73  m_pTxtLine2 = pdfium::MakeUnique<CFX_TxtLine>(iSize);
74  m_pCurLine = m_pTxtLine1.get();
75  ResetArabicContext();
76}
77
78CFX_TxtBreak::~CFX_TxtBreak() {
79  Reset();
80}
81
82void CFX_TxtBreak::SetLineWidth(FX_FLOAT fLineWidth) {
83  m_iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
84  ASSERT(m_iLineWidth >= 20000);
85}
86
87void CFX_TxtBreak::SetLinePos(FX_FLOAT fLinePos) {
88  int32_t iLinePos =
89      std::min(std::max(FXSYS_round(fLinePos * 20000.0f), 0), m_iLineWidth);
90  m_pCurLine->m_iStart = iLinePos;
91  m_pCurLine->m_iWidth += iLinePos;
92}
93
94void CFX_TxtBreak::SetLayoutStyles(uint32_t dwLayoutStyles) {
95  m_dwLayoutStyles = dwLayoutStyles;
96  m_bVertical = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_VerticalChars) != 0;
97  m_bArabicContext = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ArabicContext) != 0;
98  m_bArabicShapes = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ArabicShapes) != 0;
99  m_bRTL = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_RTLReadingOrder) != 0;
100  m_bSingleLine = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_SingleLine) != 0;
101  m_bCombText = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_CombText) != 0;
102  ResetArabicContext();
103  m_iLineRotation = GetLineRotation(m_dwLayoutStyles);
104  m_iRotation = m_iLineRotation + m_iCharRotation;
105  m_iRotation %= 4;
106}
107
108void CFX_TxtBreak::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont) {
109  if (!pFont || pFont == m_pFont)
110    return;
111
112  SetBreakStatus();
113  m_pFont = pFont;
114  FontChanged();
115}
116
117void CFX_TxtBreak::SetFontSize(FX_FLOAT fFontSize) {
118  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
119  if (m_iFontSize == iFontSize)
120    return;
121
122  SetBreakStatus();
123  m_iFontSize = iFontSize;
124  FontChanged();
125}
126
127void CFX_TxtBreak::FontChanged() {
128  m_iDefChar = 0;
129  if (m_wDefChar == 0xFEFF || !m_pFont)
130    return;
131
132  m_pFont->GetCharWidth(m_wDefChar, m_iDefChar, false);
133  m_iDefChar *= m_iFontSize;
134}
135
136void CFX_TxtBreak::SetTabWidth(FX_FLOAT fTabWidth, bool bEquidistant) {
137  m_iTabWidth = FXSYS_round(fTabWidth * 20000.0f);
138  if (m_iTabWidth < FX_TXTBREAK_MinimumTabWidth)
139    m_iTabWidth = FX_TXTBREAK_MinimumTabWidth;
140
141  m_bEquidistant = bEquidistant;
142}
143
144void CFX_TxtBreak::SetDefaultChar(FX_WCHAR wch) {
145  m_wDefChar = wch;
146  m_iDefChar = 0;
147  if (m_wDefChar == 0xFEFF || !m_pFont)
148    return;
149
150  m_pFont->GetCharWidth(m_wDefChar, m_iDefChar, false);
151  if (m_iDefChar < 0)
152    m_iDefChar = 0;
153  else
154    m_iDefChar *= m_iFontSize;
155}
156
157void CFX_TxtBreak::SetParagraphBreakChar(FX_WCHAR wch) {
158  if (wch != L'\r' && wch != L'\n')
159    return;
160  m_wParagBreakChar = wch;
161}
162
163void CFX_TxtBreak::SetLineBreakTolerance(FX_FLOAT fTolerance) {
164  m_iTolerance = FXSYS_round(fTolerance * 20000.0f);
165}
166
167void CFX_TxtBreak::SetCharRotation(int32_t iCharRotation) {
168  if (iCharRotation < 0)
169    iCharRotation += (-iCharRotation / 4 + 1) * 4;
170  else if (iCharRotation > 3)
171    iCharRotation -= (iCharRotation / 4) * 4;
172
173  if (m_iCharRotation == iCharRotation)
174    return;
175
176  SetBreakStatus();
177  m_iCharRotation = iCharRotation;
178  m_iRotation = m_iLineRotation + m_iCharRotation;
179  m_iRotation %= 4;
180}
181
182void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
183  ASSERT(iAlignment >= FX_TXTLINEALIGNMENT_Left &&
184         iAlignment <= FX_TXTLINEALIGNMENT_Distributed);
185  m_iAlignment = iAlignment;
186  ResetArabicContext();
187}
188
189void CFX_TxtBreak::ResetContextCharStyles() {
190  m_dwContextCharStyles = m_bArabicContext ? m_iCurAlignment : m_iAlignment;
191  if (m_bArabicNumber)
192    m_dwContextCharStyles |= FX_TXTCHARSTYLE_ArabicNumber;
193  if (m_bArabicComma)
194    m_dwContextCharStyles |= FX_TXTCHARSTYLE_ArabicComma;
195  if ((m_bArabicContext && m_bCurRTL) || (!m_bArabicContext && m_bRTL))
196    m_dwContextCharStyles |= FX_TXTCHARSTYLE_RTLReadingOrder;
197  m_dwContextCharStyles |= (m_iArabicContext << 8);
198}
199
200void CFX_TxtBreak::SetCombWidth(FX_FLOAT fCombWidth) {
201  m_iCombWidth = FXSYS_round(fCombWidth * 20000.0f);
202}
203
204void CFX_TxtBreak::SetUserData(void* pUserData) {
205  if (m_pUserData == pUserData)
206    return;
207
208  SetBreakStatus();
209  m_pUserData = pUserData;
210}
211
212void CFX_TxtBreak::SetBreakStatus() {
213  if (m_bPagination)
214    return;
215
216  int32_t iCount = m_pCurLine->CountChars();
217  if (iCount < 1)
218    return;
219
220  CFX_TxtChar* pTC = m_pCurLine->GetCharPtr(iCount - 1);
221  if (pTC->m_dwStatus == 0)
222    pTC->m_dwStatus = FX_TXTBREAK_PieceBreak;
223}
224
225void CFX_TxtBreak::SetHorizontalScale(int32_t iScale) {
226  if (iScale < 0)
227    iScale = 0;
228  if (iScale == m_iHorScale)
229    return;
230
231  SetBreakStatus();
232  m_iHorScale = iScale;
233}
234
235void CFX_TxtBreak::SetCharSpace(FX_FLOAT fCharSpace) {
236  m_iCharSpace = FXSYS_round(fCharSpace * 20000.0f);
237}
238
239static const int32_t gs_FX_TxtLineRotations[8] = {0, 3, 1, 0, 2, 1, 3, 2};
240
241int32_t CFX_TxtBreak::GetLineRotation(uint32_t dwStyles) const {
242  return gs_FX_TxtLineRotations[(dwStyles & 0x0E) >> 1];
243}
244
245CFX_TxtChar* CFX_TxtBreak::GetLastChar(int32_t index, bool bOmitChar) const {
246  std::vector<CFX_TxtChar>& ca = *m_pCurLine->m_pLineChars.get();
247  int32_t iCount = pdfium::CollectionSize<int32_t>(ca);
248  if (index < 0 || index >= iCount)
249    return nullptr;
250
251  int32_t iStart = iCount - 1;
252  while (iStart > -1) {
253    CFX_TxtChar* pTC = &ca[iStart--];
254    if (bOmitChar && pTC->GetCharType() == FX_CHARTYPE_Combination)
255      continue;
256    if (--index < 0)
257      return pTC;
258  }
259  return nullptr;
260}
261
262CFX_TxtLine* CFX_TxtBreak::GetTxtLine() const {
263  if (m_iReady == 1)
264    return m_pTxtLine1.get();
265  if (m_iReady == 2)
266    return m_pTxtLine2.get();
267  return nullptr;
268}
269
270CFX_TxtPieceArray* CFX_TxtBreak::GetTxtPieces() const {
271  CFX_TxtLine* pTxtLine = GetTxtLine();
272  return pTxtLine ? pTxtLine->m_pLinePieces.get() : nullptr;
273}
274
275inline FX_CHARTYPE CFX_TxtBreak::GetUnifiedCharType(
276    FX_CHARTYPE chartype) const {
277  return chartype >= FX_CHARTYPE_ArabicAlef ? FX_CHARTYPE_Arabic : chartype;
278}
279
280void CFX_TxtBreak::ResetArabicContext() {
281  if (m_bArabicContext) {
282    m_bCurRTL = m_iCurArabicContext > 1;
283    m_iCurAlignment = m_iCurArabicContext > 1 ? FX_TXTLINEALIGNMENT_Right
284                                              : FX_TXTLINEALIGNMENT_Left;
285    m_iCurAlignment |= (m_iAlignment & FX_TXTLINEALIGNMENT_HigherMask);
286    m_bArabicNumber = m_iArabicContext >= 1 && m_bArabicShapes;
287  } else {
288    if (m_bPagination) {
289      m_bCurRTL = false;
290      m_iCurAlignment = 0;
291    } else {
292      m_bCurRTL = m_bRTL;
293      m_iCurAlignment = m_iAlignment;
294    }
295    if (m_bRTL)
296      m_bArabicNumber = m_iArabicContext >= 1;
297    else
298      m_bArabicNumber = m_iArabicContext > 1;
299    m_bArabicNumber = m_bArabicNumber && m_bArabicShapes;
300  }
301  m_bArabicComma = m_bArabicNumber;
302  ResetContextCharStyles();
303}
304
305void CFX_TxtBreak::AppendChar_PageLoad(CFX_TxtChar* pCurChar,
306                                       uint32_t dwProps) {
307  if (!m_bPagination) {
308    pCurChar->m_dwStatus = 0;
309    pCurChar->m_pUserData = m_pUserData;
310  }
311  if (m_bArabicContext || m_bArabicShapes) {
312    int32_t iBidiCls = (dwProps & FX_BIDICLASSBITSMASK) >> FX_BIDICLASSBITS;
313    int32_t iArabicContext =
314        (iBidiCls == FX_BIDICLASS_R || iBidiCls == FX_BIDICLASS_AL)
315            ? 2
316            : ((iBidiCls == FX_BIDICLASS_L || iBidiCls == FX_BIDICLASS_S) ? 0
317                                                                          : 1);
318    if (iArabicContext != m_iArabicContext && iArabicContext != 1) {
319      m_iArabicContext = iArabicContext;
320      if (m_iCurArabicContext == 1)
321        m_iCurArabicContext = iArabicContext;
322
323      ResetArabicContext();
324      if (!m_bPagination) {
325        CFX_TxtChar* pLastChar = GetLastChar(1, false);
326        if (pLastChar && pLastChar->m_dwStatus < 1)
327          pLastChar->m_dwStatus = FX_TXTBREAK_PieceBreak;
328      }
329    }
330  }
331  pCurChar->m_dwCharStyles = m_dwContextCharStyles;
332}
333
334uint32_t CFX_TxtBreak::AppendChar_Combination(CFX_TxtChar* pCurChar,
335                                              int32_t iRotation) {
336  FX_WCHAR wch = pCurChar->m_wCharCode;
337  FX_WCHAR wForm;
338  int32_t iCharWidth = 0;
339  pCurChar->m_iCharWidth = -1;
340  if (m_bCombText) {
341    iCharWidth = m_iCombWidth;
342  } else {
343    if (m_bVertical != FX_IsOdd(iRotation)) {
344      iCharWidth = 1000;
345    } else {
346      wForm = wch;
347      if (!m_bPagination) {
348        CFX_TxtChar* pLastChar = GetLastChar(0, false);
349        if (pLastChar &&
350            (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
351          bool bShadda = false;
352          if (wch == 0x0651) {
353            FX_WCHAR wLast = pLastChar->m_wCharCode;
354            if (wLast >= 0x064C && wLast <= 0x0650) {
355              wForm = FX_GetArabicFromShaddaTable(wLast);
356              bShadda = true;
357            }
358          } else if (wch >= 0x064C && wch <= 0x0650) {
359            if (pLastChar->m_wCharCode == 0x0651) {
360              wForm = FX_GetArabicFromShaddaTable(wch);
361              bShadda = true;
362            }
363          }
364          if (bShadda) {
365            pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
366            pLastChar->m_iCharWidth = 0;
367            pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
368          }
369        }
370      }
371      if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
372        iCharWidth = 0;
373    }
374    iCharWidth *= m_iFontSize;
375    iCharWidth = iCharWidth * m_iHorScale / 100;
376  }
377  pCurChar->m_iCharWidth = -iCharWidth;
378  return FX_TXTBREAK_None;
379}
380
381uint32_t CFX_TxtBreak::AppendChar_Tab(CFX_TxtChar* pCurChar,
382                                      int32_t iRotation) {
383  m_eCharType = FX_CHARTYPE_Tab;
384  if ((m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ExpandTab) == 0)
385    return FX_TXTBREAK_None;
386
387  int32_t& iLineWidth = m_pCurLine->m_iWidth;
388  int32_t iCharWidth;
389  if (m_bCombText) {
390    iCharWidth = m_iCombWidth;
391  } else {
392    if (m_bEquidistant) {
393      iCharWidth = iLineWidth;
394      iCharWidth = m_iTabWidth * (iCharWidth / m_iTabWidth + 1) - iCharWidth;
395      if (iCharWidth < FX_TXTBREAK_MinimumTabWidth)
396        iCharWidth += m_iTabWidth;
397    } else {
398      iCharWidth = m_iTabWidth;
399    }
400  }
401
402  pCurChar->m_iCharWidth = iCharWidth;
403  iLineWidth += iCharWidth;
404  if (!m_bSingleLine && iLineWidth >= m_iLineWidth + m_iTolerance)
405    return EndBreak(FX_TXTBREAK_LineBreak);
406
407  return FX_TXTBREAK_None;
408}
409
410uint32_t CFX_TxtBreak::AppendChar_Control(CFX_TxtChar* pCurChar,
411                                          int32_t iRotation) {
412  m_eCharType = FX_CHARTYPE_Control;
413  uint32_t dwRet = FX_TXTBREAK_None;
414  if (!m_bSingleLine) {
415    FX_WCHAR wch = pCurChar->m_wCharCode;
416    switch (wch) {
417      case L'\v':
418      case 0x2028:
419        dwRet = FX_TXTBREAK_LineBreak;
420        break;
421      case L'\f':
422        dwRet = FX_TXTBREAK_PageBreak;
423        break;
424      case 0x2029:
425        dwRet = FX_TXTBREAK_ParagraphBreak;
426        break;
427      default:
428        if (wch == m_wParagBreakChar)
429          dwRet = FX_TXTBREAK_ParagraphBreak;
430        break;
431    }
432    if (dwRet != FX_TXTBREAK_None)
433      dwRet = EndBreak(dwRet);
434  }
435  return dwRet;
436}
437
438uint32_t CFX_TxtBreak::AppendChar_Arabic(CFX_TxtChar* pCurChar,
439                                         int32_t iRotation) {
440  FX_CHARTYPE chartype = pCurChar->GetCharType();
441  int32_t& iLineWidth = m_pCurLine->m_iWidth;
442  FX_WCHAR wForm;
443  int32_t iCharWidth = 0;
444  CFX_TxtChar* pLastChar = nullptr;
445  bool bAlef = false;
446  if (!m_bCombText && m_eCharType >= FX_CHARTYPE_ArabicAlef &&
447      m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
448    pLastChar = GetLastChar(1);
449    if (pLastChar) {
450      iCharWidth = pLastChar->m_iCharWidth;
451      if (iCharWidth > 0)
452        iLineWidth -= iCharWidth;
453
454      CFX_Char* pPrevChar = GetLastChar(2);
455      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
456      bAlef = (wForm == 0xFEFF &&
457               pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
458      int32_t iLastRotation = pLastChar->m_nRotation + m_iLineRotation;
459      if (m_bVertical && (pLastChar->m_dwCharProps & 0x8000) != 0)
460        iLastRotation++;
461      if (m_bVertical != FX_IsOdd(iLastRotation))
462        iCharWidth = 1000;
463      else
464        m_pFont->GetCharWidth(wForm, iCharWidth, false);
465
466      if (wForm == 0xFEFF)
467        iCharWidth = m_iDefChar;
468
469      iCharWidth *= m_iFontSize;
470      iCharWidth = iCharWidth * m_iHorScale / 100;
471      pLastChar->m_iCharWidth = iCharWidth;
472      iLineWidth += iCharWidth;
473      iCharWidth = 0;
474    }
475  }
476
477  m_eCharType = chartype;
478  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
479                                      nullptr);
480  if (m_bCombText) {
481    iCharWidth = m_iCombWidth;
482  } else {
483    if (m_bVertical != FX_IsOdd(iRotation))
484      iCharWidth = 1000;
485    else
486      m_pFont->GetCharWidth(wForm, iCharWidth, false);
487
488    if (wForm == 0xFEFF)
489      iCharWidth = m_iDefChar;
490
491    iCharWidth *= m_iFontSize;
492    iCharWidth = iCharWidth * m_iHorScale / 100;
493  }
494  pCurChar->m_iCharWidth = iCharWidth;
495  iLineWidth += iCharWidth;
496  m_pCurLine->m_iArabicChars++;
497  if (!m_bSingleLine && iLineWidth > m_iLineWidth + m_iTolerance)
498    return EndBreak(FX_TXTBREAK_LineBreak);
499  return FX_TXTBREAK_None;
500}
501
502uint32_t CFX_TxtBreak::AppendChar_Others(CFX_TxtChar* pCurChar,
503                                         int32_t iRotation) {
504  uint32_t dwProps = pCurChar->m_dwCharProps;
505  FX_CHARTYPE chartype = pCurChar->GetCharType();
506  int32_t& iLineWidth = m_pCurLine->m_iWidth;
507  int32_t iCharWidth = 0;
508  m_eCharType = chartype;
509  FX_WCHAR wch = pCurChar->m_wCharCode;
510  FX_WCHAR wForm = wch;
511  if (chartype == FX_CHARTYPE_Numeric) {
512    if (m_bArabicNumber) {
513      wForm = wch + 0x0630;
514      pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicIndic;
515    }
516  } else if (wch == L',') {
517    if (m_bArabicShapes && m_iCurArabicContext > 0) {
518      wForm = 0x060C;
519      pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicComma;
520    }
521  } else if (m_bCurRTL || m_bVertical) {
522    wForm = FX_GetMirrorChar(wch, dwProps, m_bCurRTL, m_bVertical);
523  }
524
525  if (m_bCombText) {
526    iCharWidth = m_iCombWidth;
527  } else {
528    if (m_bVertical != FX_IsOdd(iRotation))
529      iCharWidth = 1000;
530    else if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
531      iCharWidth = m_iDefChar;
532
533    iCharWidth *= m_iFontSize;
534    iCharWidth = iCharWidth * m_iHorScale / 100;
535  }
536
537  iCharWidth += m_iCharSpace;
538  pCurChar->m_iCharWidth = iCharWidth;
539  iLineWidth += iCharWidth;
540  bool bBreak = (chartype != FX_CHARTYPE_Space ||
541                 (m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0);
542  if (!m_bSingleLine && bBreak && iLineWidth > m_iLineWidth + m_iTolerance)
543    return EndBreak(FX_TXTBREAK_LineBreak);
544
545  return FX_TXTBREAK_None;
546}
547
548uint32_t CFX_TxtBreak::AppendChar(FX_WCHAR wch) {
549  uint32_t dwProps = kTextLayoutCodeProperties[static_cast<uint16_t>(wch)];
550  FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
551  m_pCurLine->m_pLineChars->emplace_back();
552
553  CFX_TxtChar* pCurChar = &m_pCurLine->m_pLineChars->back();
554  pCurChar->m_wCharCode = static_cast<uint16_t>(wch);
555  pCurChar->m_nRotation = m_iCharRotation;
556  pCurChar->m_dwCharProps = dwProps;
557  pCurChar->m_dwCharStyles = 0;
558  pCurChar->m_iCharWidth = 0;
559  pCurChar->m_iHorizontalScale = m_iHorScale;
560  pCurChar->m_iVerticalScale = 100;
561  pCurChar->m_dwStatus = 0;
562  pCurChar->m_iBidiClass = 0;
563  pCurChar->m_iBidiLevel = 0;
564  pCurChar->m_iBidiPos = 0;
565  pCurChar->m_iBidiOrder = 0;
566  pCurChar->m_pUserData = nullptr;
567  AppendChar_PageLoad(pCurChar, dwProps);
568  uint32_t dwRet1 = FX_TXTBREAK_None;
569  if (chartype != FX_CHARTYPE_Combination &&
570      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
571      m_eCharType != FX_CHARTYPE_Unknown &&
572      m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance && !m_bSingleLine &&
573      (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
574    dwRet1 = EndBreak(FX_TXTBREAK_LineBreak);
575    int32_t iCount = m_pCurLine->CountChars();
576    if (iCount > 0)
577      pCurChar = &(*m_pCurLine->m_pLineChars)[iCount - 1];
578  }
579
580  int32_t iRotation = m_iRotation;
581  if (m_bVertical && (dwProps & 0x8000) != 0)
582    iRotation = (iRotation + 1) % 4;
583
584  uint32_t dwRet2 =
585      (this->*g_FX_TxtBreak_lpfAppendChar[chartype >> FX_CHARTYPEBITS])(
586          pCurChar, iRotation);
587  return std::max(dwRet1, dwRet2);
588}
589
590void CFX_TxtBreak::EndBreak_UpdateArabicShapes() {
591  ASSERT(m_bArabicShapes);
592  int32_t iCount = m_pCurLine->CountChars();
593  if (iCount < 2)
594    return;
595
596  int32_t& iLineWidth = m_pCurLine->m_iWidth;
597  CFX_TxtChar* pCur = m_pCurLine->GetCharPtr(0);
598  bool bPrevNum = (pCur->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
599  pCur = m_pCurLine->GetCharPtr(1);
600  FX_WCHAR wch, wForm;
601  bool bNextNum;
602  int32_t i = 1;
603  int32_t iCharWidth;
604  int32_t iRotation;
605  CFX_TxtChar* pNext;
606  do {
607    i++;
608    if (i < iCount) {
609      pNext = m_pCurLine->GetCharPtr(i);
610      bNextNum = (pNext->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
611    } else {
612      pNext = nullptr;
613      bNextNum = false;
614    }
615
616    wch = pCur->m_wCharCode;
617    if (wch == L'.') {
618      if (bPrevNum && bNextNum) {
619        iRotation = m_iRotation;
620        if (m_bVertical && (pCur->m_dwCharProps & 0x8000) != 0)
621          iRotation = ((iRotation + 1) & 0x03);
622
623        wForm = wch == L'.' ? 0x066B : 0x066C;
624        iLineWidth -= pCur->m_iCharWidth;
625        if (m_bCombText) {
626          iCharWidth = m_iCombWidth;
627        } else {
628          if (m_bVertical != FX_IsOdd(iRotation))
629            iCharWidth = 1000;
630          else if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
631            iCharWidth = m_iDefChar;
632
633          iCharWidth *= m_iFontSize;
634          iCharWidth = iCharWidth * m_iHorScale / 100;
635        }
636        pCur->m_iCharWidth = iCharWidth;
637        iLineWidth += iCharWidth;
638      }
639    }
640    bPrevNum = (pCur->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
641    pCur = pNext;
642  } while (i < iCount);
643}
644
645bool CFX_TxtBreak::EndBreak_SplitLine(CFX_TxtLine* pNextLine,
646                                      bool bAllChars,
647                                      uint32_t dwStatus) {
648  int32_t iCount = m_pCurLine->CountChars();
649  bool bDone = false;
650  CFX_TxtChar* pTC;
651  if (!m_bSingleLine && m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance) {
652    pTC = m_pCurLine->GetCharPtr(iCount - 1);
653    switch (pTC->GetCharType()) {
654      case FX_CHARTYPE_Tab:
655      case FX_CHARTYPE_Control:
656        break;
657      case FX_CHARTYPE_Space:
658        if ((m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0) {
659          SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
660          bDone = true;
661        }
662        break;
663      default:
664        SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
665        bDone = true;
666        break;
667    }
668  }
669
670  iCount = m_pCurLine->CountChars();
671  CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
672  CFX_TxtPiece tp;
673  if (m_bPagination) {
674    tp.m_dwStatus = dwStatus;
675    tp.m_iStartPos = m_pCurLine->m_iStart;
676    tp.m_iWidth = m_pCurLine->m_iWidth;
677    tp.m_iStartChar = 0;
678    tp.m_iChars = iCount;
679    tp.m_pChars = m_pCurLine->m_pLineChars.get();
680    tp.m_pUserData = m_pUserData;
681    pTC = m_pCurLine->GetCharPtr(0);
682    tp.m_dwCharStyles = pTC->m_dwCharStyles;
683    tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
684    tp.m_iVerticalScale = pTC->m_iVerticalScale;
685    pCurPieces->Add(tp);
686    m_pCurLine = pNextLine;
687    m_eCharType = FX_CHARTYPE_Unknown;
688    return true;
689  }
690  if (bAllChars && !bDone) {
691    int32_t iEndPos = m_pCurLine->m_iWidth;
692    GetBreakPos(*m_pCurLine->m_pLineChars.get(), iEndPos, bAllChars, true);
693  }
694  return false;
695}
696
697void CFX_TxtBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
698                                     uint32_t dwStatus) {
699  CFX_TxtPiece tp;
700  FX_TPO tpo;
701  CFX_TxtChar* pTC;
702  int32_t i;
703  int32_t j;
704  std::vector<CFX_TxtChar>& chars = *m_pCurLine->m_pLineChars.get();
705  int32_t iCount = m_pCurLine->CountChars();
706  bool bDone = (m_pCurLine->m_iArabicChars > 0 || m_bCurRTL);
707  if (!m_bPagination && bDone) {
708    int32_t iBidiNum = 0;
709    for (i = 0; i < iCount; i++) {
710      pTC = &chars[i];
711      pTC->m_iBidiPos = i;
712      if (pTC->GetCharType() != FX_CHARTYPE_Control)
713        iBidiNum = i;
714      if (i == 0)
715        pTC->m_iBidiLevel = 1;
716    }
717    FX_BidiLine(chars, iBidiNum + 1, m_bCurRTL ? 1 : 0);
718  }
719
720  CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
721  if (!m_bPagination &&
722      (bDone || (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_MutipleFormat) != 0)) {
723    tp.m_dwStatus = FX_TXTBREAK_PieceBreak;
724    tp.m_iStartPos = m_pCurLine->m_iStart;
725    tp.m_pChars = m_pCurLine->m_pLineChars.get();
726    int32_t iBidiLevel = -1;
727    int32_t iCharWidth;
728    i = 0;
729    j = -1;
730    while (i < iCount) {
731      pTC = &chars[i];
732      if (iBidiLevel < 0) {
733        iBidiLevel = pTC->m_iBidiLevel;
734        tp.m_iWidth = 0;
735        tp.m_iBidiLevel = iBidiLevel;
736        tp.m_iBidiPos = pTC->m_iBidiOrder;
737        tp.m_dwCharStyles = pTC->m_dwCharStyles;
738        tp.m_pUserData = pTC->m_pUserData;
739        tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
740        tp.m_iVerticalScale = pTC->m_iVerticalScale;
741        tp.m_dwStatus = FX_TXTBREAK_PieceBreak;
742      }
743      if (iBidiLevel != pTC->m_iBidiLevel || pTC->m_dwStatus != 0) {
744        if (iBidiLevel == pTC->m_iBidiLevel) {
745          tp.m_dwStatus = pTC->m_dwStatus;
746          iCharWidth = pTC->m_iCharWidth;
747          if (iCharWidth > 0)
748            tp.m_iWidth += iCharWidth;
749
750          i++;
751        }
752        tp.m_iChars = i - tp.m_iStartChar;
753        pCurPieces->Add(tp);
754        tp.m_iStartPos += tp.m_iWidth;
755        tp.m_iStartChar = i;
756        tpo.index = ++j;
757        tpo.pos = tp.m_iBidiPos;
758        tpos->push_back(tpo);
759        iBidiLevel = -1;
760      } else {
761        iCharWidth = pTC->m_iCharWidth;
762        if (iCharWidth > 0)
763          tp.m_iWidth += iCharWidth;
764
765        i++;
766      }
767    }
768    if (i > tp.m_iStartChar) {
769      tp.m_dwStatus = dwStatus;
770      tp.m_iChars = i - tp.m_iStartChar;
771      pCurPieces->Add(tp);
772      tpo.index = ++j;
773      tpo.pos = tp.m_iBidiPos;
774      tpos->push_back(tpo);
775    }
776    if (j > -1) {
777      if (j > 0) {
778        std::sort(tpos->begin(), tpos->end());
779        int32_t iStartPos = 0;
780        for (i = 0; i <= j; i++) {
781          tpo = (*tpos)[i];
782          CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
783          ttp.m_iStartPos = iStartPos;
784          iStartPos += ttp.m_iWidth;
785        }
786      }
787      CFX_TxtPiece& ttp = pCurPieces->GetAt(j);
788      ttp.m_dwStatus = dwStatus;
789    }
790  } else {
791    tp.m_dwStatus = dwStatus;
792    tp.m_iStartPos = m_pCurLine->m_iStart;
793    tp.m_iWidth = m_pCurLine->m_iWidth;
794    tp.m_iStartChar = 0;
795    tp.m_iChars = iCount;
796    tp.m_pChars = m_pCurLine->m_pLineChars.get();
797    tp.m_pUserData = m_pUserData;
798    pTC = &chars[0];
799    tp.m_dwCharStyles = pTC->m_dwCharStyles;
800    tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
801    tp.m_iVerticalScale = pTC->m_iVerticalScale;
802    pCurPieces->Add(tp);
803    tpos->push_back({0, 0});
804  }
805}
806
807void CFX_TxtBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
808                                      bool bAllChars,
809                                      uint32_t dwStatus) {
810  int32_t iNetWidth = m_pCurLine->m_iWidth;
811  int32_t iGapChars = 0;
812  int32_t iCharWidth;
813  CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
814  int32_t i;
815  int32_t j;
816  int32_t iCount = pCurPieces->GetSize();
817  bool bFind = false;
818  FX_TPO tpo;
819  CFX_TxtChar* pTC;
820  FX_CHARTYPE chartype;
821  for (i = iCount - 1; i > -1; i--) {
822    tpo = tpos[i];
823    CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
824    if (!bFind)
825      iNetWidth = ttp.GetEndPos();
826
827    bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
828    j = bArabic ? 0 : ttp.m_iChars - 1;
829    while (j > -1 && j < ttp.m_iChars) {
830      pTC = ttp.GetCharPtr(j);
831      if (pTC->m_nBreakType == FX_LBT_DIRECT_BRK)
832        iGapChars++;
833      if (!bFind || !bAllChars) {
834        chartype = pTC->GetCharType();
835        if (chartype == FX_CHARTYPE_Space || chartype == FX_CHARTYPE_Control) {
836          if (!bFind) {
837            iCharWidth = pTC->m_iCharWidth;
838            if (bAllChars && iCharWidth > 0)
839              iNetWidth -= iCharWidth;
840          }
841        } else {
842          bFind = true;
843          if (!bAllChars)
844            break;
845        }
846      }
847      j += bArabic ? 1 : -1;
848    }
849    if (!bAllChars && bFind)
850      break;
851  }
852
853  int32_t iOffset = m_iLineWidth - iNetWidth;
854  int32_t iLowerAlignment = (m_iCurAlignment & FX_TXTLINEALIGNMENT_LowerMask);
855  int32_t iHigherAlignment = (m_iCurAlignment & FX_TXTLINEALIGNMENT_HigherMask);
856  if (iGapChars > 0 && (iHigherAlignment == FX_TXTLINEALIGNMENT_Distributed ||
857                        (iHigherAlignment == FX_TXTLINEALIGNMENT_Justified &&
858                         dwStatus != FX_TXTBREAK_ParagraphBreak))) {
859    int32_t iStart = -1;
860    for (i = 0; i < iCount; i++) {
861      tpo = tpos[i];
862      CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
863      if (iStart < -1)
864        iStart = ttp.m_iStartPos;
865      else
866        ttp.m_iStartPos = iStart;
867
868      for (j = 0; j < ttp.m_iChars; j++) {
869        pTC = ttp.GetCharPtr(j);
870        if (pTC->m_nBreakType != FX_LBT_DIRECT_BRK || pTC->m_iCharWidth < 0)
871          continue;
872
873        int32_t k = iOffset / iGapChars;
874        pTC->m_iCharWidth += k;
875        ttp.m_iWidth += k;
876        iOffset -= k;
877        iGapChars--;
878        if (iGapChars < 1)
879          break;
880      }
881      iStart += ttp.m_iWidth;
882    }
883  } else if (iLowerAlignment > FX_TXTLINEALIGNMENT_Left) {
884    if (iLowerAlignment == FX_TXTLINEALIGNMENT_Center)
885      iOffset /= 2;
886    if (iOffset > 0) {
887      for (i = 0; i < iCount; i++) {
888        CFX_TxtPiece& ttp = pCurPieces->GetAt(i);
889        ttp.m_iStartPos += iOffset;
890      }
891    }
892  }
893}
894
895uint32_t CFX_TxtBreak::EndBreak(uint32_t dwStatus) {
896  ASSERT(dwStatus >= FX_TXTBREAK_PieceBreak &&
897         dwStatus <= FX_TXTBREAK_PageBreak);
898  CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
899  int32_t iCount = pCurPieces->GetSize();
900  if (iCount > 0) {
901    CFX_TxtPiece* pLastPiece = pCurPieces->GetPtrAt(--iCount);
902    if (dwStatus > FX_TXTBREAK_PieceBreak)
903      pLastPiece->m_dwStatus = dwStatus;
904    else
905      dwStatus = pLastPiece->m_dwStatus;
906    return dwStatus;
907  } else {
908    CFX_TxtLine* pLastLine = GetTxtLine();
909    if (pLastLine) {
910      pCurPieces = pLastLine->m_pLinePieces.get();
911      iCount = pCurPieces->GetSize();
912      if (iCount-- > 0) {
913        CFX_TxtPiece* pLastPiece = pCurPieces->GetPtrAt(iCount);
914        if (dwStatus > FX_TXTBREAK_PieceBreak)
915          pLastPiece->m_dwStatus = dwStatus;
916        else
917          dwStatus = pLastPiece->m_dwStatus;
918        return dwStatus;
919      }
920      return FX_TXTBREAK_None;
921    }
922
923    iCount = m_pCurLine->CountChars();
924    if (iCount < 1)
925      return FX_TXTBREAK_None;
926    if (!m_bPagination) {
927      CFX_TxtChar* pTC = m_pCurLine->GetCharPtr(iCount - 1);
928      pTC->m_dwStatus = dwStatus;
929    }
930    if (dwStatus <= FX_TXTBREAK_PieceBreak)
931      return dwStatus;
932  }
933
934  m_iReady = (m_pCurLine == m_pTxtLine1.get()) ? 1 : 2;
935  CFX_TxtLine* pNextLine =
936      (m_pCurLine == m_pTxtLine1.get()) ? m_pTxtLine2.get() : m_pTxtLine1.get();
937  bool bAllChars = (m_iCurAlignment > FX_TXTLINEALIGNMENT_Right);
938  if (m_bArabicShapes)
939    EndBreak_UpdateArabicShapes();
940
941  if (!EndBreak_SplitLine(pNextLine, bAllChars, dwStatus)) {
942    std::deque<FX_TPO> tpos;
943    EndBreak_BidiLine(&tpos, dwStatus);
944    if (!m_bPagination && m_iCurAlignment > FX_TXTLINEALIGNMENT_Left)
945      EndBreak_Alignment(tpos, bAllChars, dwStatus);
946  }
947
948  m_pCurLine = pNextLine;
949  CFX_Char* pTC = GetLastChar(0, false);
950  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
951  if (dwStatus == FX_TXTBREAK_ParagraphBreak) {
952    m_iArabicContext = m_iCurArabicContext = 1;
953    ResetArabicContext();
954  }
955  return dwStatus;
956}
957
958int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_TxtChar>& ca,
959                                  int32_t& iEndPos,
960                                  bool bAllChars,
961                                  bool bOnlyBrk) {
962  int32_t iLength = pdfium::CollectionSize<int32_t>(ca) - 1;
963  if (iLength < 1)
964    return iLength;
965
966  int32_t iBreak = -1;
967  int32_t iBreakPos = -1;
968  int32_t iIndirect = -1;
969  int32_t iIndirectPos = -1;
970  int32_t iLast = -1;
971  int32_t iLastPos = -1;
972  if (m_bSingleLine || iEndPos <= m_iLineWidth) {
973    if (!bAllChars)
974      return iLength;
975
976    iBreak = iLength;
977    iBreakPos = iEndPos;
978  }
979
980  bool bSpaceBreak = (m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0;
981  bool bNumberBreak = (m_dwPolicies & FX_TXTBREAKPOLICY_NumberBreak) != 0;
982  FX_LINEBREAKTYPE eType;
983  uint32_t nCodeProp;
984  uint32_t nCur;
985  uint32_t nNext;
986  CFX_Char* pCur = &ca[iLength--];
987  if (bAllChars)
988    pCur->m_nBreakType = FX_LBT_UNKNOWN;
989
990  nCodeProp = pCur->m_dwCharProps;
991  nNext = nCodeProp & 0x003F;
992  int32_t iCharWidth = pCur->m_iCharWidth;
993  if (iCharWidth > 0)
994    iEndPos -= iCharWidth;
995
996  while (iLength >= 0) {
997    pCur = &ca[iLength];
998    nCodeProp = pCur->m_dwCharProps;
999    nCur = nCodeProp & 0x003F;
1000    if (nCur == FX_CBP_SP) {
1001      if (nNext == FX_CBP_SP)
1002        eType = bSpaceBreak ? FX_LBT_DIRECT_BRK : FX_LBT_PROHIBITED_BRK;
1003      else
1004        eType = gs_FX_LineBreak_PairTable[nCur][nNext];
1005    } else if (bNumberBreak && nCur == FX_CBP_NU && nNext == FX_CBP_NU) {
1006      eType = FX_LBT_DIRECT_BRK;
1007    } else {
1008      if (nNext == FX_CBP_SP)
1009        eType = FX_LBT_PROHIBITED_BRK;
1010      else
1011        eType = gs_FX_LineBreak_PairTable[nCur][nNext];
1012    }
1013    if (bAllChars)
1014      pCur->m_nBreakType = static_cast<uint8_t>(eType);
1015    if (!bOnlyBrk) {
1016      if (m_bSingleLine || iEndPos <= m_iLineWidth ||
1017          (nCur == FX_CBP_SP && !bSpaceBreak)) {
1018        if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
1019          iBreak = iLength;
1020          iBreakPos = iEndPos;
1021          if (!bAllChars)
1022            return iLength;
1023        } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
1024          iIndirect = iLength;
1025          iIndirectPos = iEndPos;
1026        }
1027        if (iLast < 0) {
1028          iLast = iLength;
1029          iLastPos = iEndPos;
1030        }
1031      }
1032      iCharWidth = pCur->m_iCharWidth;
1033      if (iCharWidth > 0)
1034        iEndPos -= iCharWidth;
1035    }
1036    nNext = nCodeProp & 0x003F;
1037    iLength--;
1038  }
1039  if (bOnlyBrk)
1040    return 0;
1041  if (iBreak > -1) {
1042    iEndPos = iBreakPos;
1043    return iBreak;
1044  }
1045  if (iIndirect > -1) {
1046    iEndPos = iIndirectPos;
1047    return iIndirect;
1048  }
1049  if (iLast > -1) {
1050    iEndPos = iLastPos;
1051    return iLast;
1052  }
1053  return 0;
1054}
1055
1056void CFX_TxtBreak::SplitTextLine(CFX_TxtLine* pCurLine,
1057                                 CFX_TxtLine* pNextLine,
1058                                 bool bAllChars) {
1059  ASSERT(pCurLine && pNextLine);
1060  int32_t iCount = pCurLine->CountChars();
1061  if (iCount < 2)
1062    return;
1063
1064  int32_t iEndPos = pCurLine->m_iWidth;
1065  std::vector<CFX_TxtChar>& curChars = *pCurLine->m_pLineChars;
1066  int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
1067  if (iCharPos < 0)
1068    iCharPos = 0;
1069
1070  iCharPos++;
1071  if (iCharPos >= iCount) {
1072    pNextLine->RemoveAll(true);
1073    CFX_Char* pTC = &curChars[iCharPos - 1];
1074    pTC->m_nBreakType = FX_LBT_UNKNOWN;
1075    return;
1076  }
1077
1078  // m_pLineChars is a unique_ptr<vector>. Assign the ref into nextChars
1079  // so we can change the m_pLineChars vector ...
1080  std::vector<CFX_TxtChar>& nextChars = *pNextLine->m_pLineChars;
1081  nextChars =
1082      std::vector<CFX_TxtChar>(curChars.begin() + iCharPos, curChars.end());
1083  curChars.erase(curChars.begin() + iCharPos, curChars.end());
1084  pCurLine->m_iWidth = iEndPos;
1085  CFX_TxtChar* pTC = &curChars[iCharPos - 1];
1086  pTC->m_nBreakType = FX_LBT_UNKNOWN;
1087  iCount = pdfium::CollectionSize<int>(nextChars);
1088  int32_t iWidth = 0;
1089  for (int32_t i = 0; i < iCount; i++) {
1090    if (nextChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
1091      pCurLine->m_iArabicChars--;
1092      pNextLine->m_iArabicChars++;
1093    }
1094    int32_t iCharWidth = nextChars[i].m_iCharWidth;
1095    if (iCharWidth > 0)
1096      iWidth += iCharWidth;
1097    if (m_bPagination)
1098      continue;
1099
1100    nextChars[i].m_dwStatus = 0;
1101  }
1102  pNextLine->m_iWidth = iWidth;
1103}
1104
1105int32_t CFX_TxtBreak::CountBreakPieces() const {
1106  CFX_TxtPieceArray* pTxtPieces = GetTxtPieces();
1107  return pTxtPieces ? pTxtPieces->GetSize() : 0;
1108}
1109
1110const CFX_TxtPiece* CFX_TxtBreak::GetBreakPiece(int32_t index) const {
1111  CFX_TxtPieceArray* pTxtPieces = GetTxtPieces();
1112  if (!pTxtPieces)
1113    return nullptr;
1114  if (index < 0 || index >= pTxtPieces->GetSize())
1115    return nullptr;
1116  return pTxtPieces->GetPtrAt(index);
1117}
1118
1119void CFX_TxtBreak::ClearBreakPieces() {
1120  CFX_TxtLine* pTxtLine = GetTxtLine();
1121  if (pTxtLine)
1122    pTxtLine->RemoveAll(true);
1123  m_iReady = 0;
1124}
1125
1126void CFX_TxtBreak::Reset() {
1127  m_eCharType = FX_CHARTYPE_Unknown;
1128  m_iArabicContext = 1;
1129  m_iCurArabicContext = 1;
1130  ResetArabicContext();
1131  m_pTxtLine1->RemoveAll(true);
1132  m_pTxtLine2->RemoveAll(true);
1133}
1134
1135struct FX_FORMCHAR {
1136  uint16_t wch;
1137  uint16_t wForm;
1138  int32_t iWidth;
1139};
1140
1141int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun,
1142                                    FXTEXT_CHARPOS* pCharPos,
1143                                    bool bCharCode,
1144                                    CFX_WideString* pWSForms) const {
1145  if (!pTxtRun || pTxtRun->iLength < 1)
1146    return 0;
1147
1148  IFX_TxtAccess* pAccess = pTxtRun->pAccess;
1149  const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity;
1150  const FX_WCHAR* pStr = pTxtRun->wsStr.c_str();
1151  int32_t* pWidths = pTxtRun->pWidths;
1152  int32_t iLength = pTxtRun->iLength - 1;
1153  CFX_RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
1154  uint32_t dwStyles = pTxtRun->dwStyles;
1155  CFX_RectF rtText(*pTxtRun->pRect);
1156  bool bRTLPiece = (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
1157  bool bArabicNumber =
1158      (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_ArabicNumber) != 0;
1159  bool bArabicComma =
1160      (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_ArabicComma) != 0;
1161  FX_FLOAT fFontSize = pTxtRun->fFontSize;
1162  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
1163  int32_t iAscent = pFont->GetAscent();
1164  int32_t iDescent = pFont->GetDescent();
1165  int32_t iMaxHeight = iAscent - iDescent;
1166  FX_FLOAT fFontHeight = fFontSize;
1167  FX_FLOAT fAscent = fFontHeight * (FX_FLOAT)iAscent / (FX_FLOAT)iMaxHeight;
1168  FX_FLOAT fDescent = fFontHeight * (FX_FLOAT)iDescent / (FX_FLOAT)iMaxHeight;
1169  bool bVerticalDoc = (dwStyles & FX_TXTLAYOUTSTYLE_VerticalLayout) != 0;
1170  bool bVerticalChar = (dwStyles & FX_TXTLAYOUTSTYLE_VerticalChars) != 0;
1171  int32_t iRotation = GetLineRotation(dwStyles) + pTxtRun->iCharRotation;
1172  FX_FLOAT fX = rtText.left;
1173  FX_FLOAT fY;
1174  FX_FLOAT fCharWidth;
1175  FX_FLOAT fCharHeight;
1176  int32_t iHorScale = pTxtRun->iHorizontalScale;
1177  int32_t iVerScale = pTxtRun->iVerticalScale;
1178  bool bSkipSpace = pTxtRun->bSkipSpace;
1179  FX_FORMCHAR formChars[3];
1180  FX_FLOAT fYBase;
1181
1182  if (bVerticalDoc) {
1183    fX += (rtText.width - fFontSize) / 2.0f;
1184    fYBase = bRTLPiece ? rtText.bottom() : rtText.top;
1185    fY = fYBase;
1186  } else {
1187    if (bRTLPiece)
1188      fX = rtText.right();
1189
1190    fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
1191    fY = fYBase + fAscent;
1192  }
1193
1194  int32_t iCount = 0;
1195  int32_t iNext = 0;
1196  FX_WCHAR wPrev = 0xFEFF;
1197  FX_WCHAR wNext = 0xFEFF;
1198  FX_WCHAR wForm = 0xFEFF;
1199  FX_WCHAR wLast = 0xFEFF;
1200  bool bShadda = false;
1201  bool bLam = false;
1202  for (int32_t i = 0; i <= iLength; i++) {
1203    int32_t iWidth;
1204    FX_WCHAR wch;
1205    if (pAccess) {
1206      wch = pAccess->GetChar(pIdentity, i);
1207      iWidth = pAccess->GetWidth(pIdentity, i);
1208    } else {
1209      wch = *pStr++;
1210      iWidth = *pWidths++;
1211    }
1212
1213    uint32_t dwProps = FX_GetUnicodeProperties(wch);
1214    FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
1215    if (chartype == FX_CHARTYPE_ArabicAlef && iWidth == 0) {
1216      wPrev = 0xFEFF;
1217      wLast = wch;
1218      continue;
1219    }
1220
1221    if (chartype >= FX_CHARTYPE_ArabicAlef) {
1222      if (i < iLength) {
1223        if (pAccess) {
1224          iNext = i + 1;
1225          while (iNext <= iLength) {
1226            wNext = pAccess->GetChar(pIdentity, iNext);
1227            dwProps = FX_GetUnicodeProperties(wNext);
1228            if ((dwProps & FX_CHARTYPEBITSMASK) != FX_CHARTYPE_Combination)
1229              break;
1230
1231            iNext++;
1232          }
1233          if (iNext > iLength)
1234            wNext = 0xFEFF;
1235        } else {
1236          int32_t j = -1;
1237          do {
1238            j++;
1239            if (i + j >= iLength)
1240              break;
1241
1242            wNext = pStr[j];
1243            dwProps = FX_GetUnicodeProperties(wNext);
1244          } while ((dwProps & FX_CHARTYPEBITSMASK) == FX_CHARTYPE_Combination);
1245          if (i + j >= iLength)
1246            wNext = 0xFEFF;
1247        }
1248      } else {
1249        wNext = 0xFEFF;
1250      }
1251
1252      wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
1253      bLam = (wPrev == 0x0644 && wch == 0x0644 && wNext == 0x0647);
1254    } else if (chartype == FX_CHARTYPE_Combination) {
1255      wForm = wch;
1256      if (wch >= 0x064C && wch <= 0x0651) {
1257        if (bShadda) {
1258          wForm = 0xFEFF;
1259          bShadda = false;
1260        } else {
1261          wNext = 0xFEFF;
1262          if (pAccess) {
1263            iNext = i + 1;
1264            if (iNext <= iLength)
1265              wNext = pAccess->GetChar(pIdentity, iNext);
1266          } else {
1267            if (i < iLength)
1268              wNext = *pStr;
1269          }
1270          if (wch == 0x0651) {
1271            if (wNext >= 0x064C && wNext <= 0x0650) {
1272              wForm = FX_GetArabicFromShaddaTable(wNext);
1273              bShadda = true;
1274            }
1275          } else {
1276            if (wNext == 0x0651) {
1277              wForm = FX_GetArabicFromShaddaTable(wch);
1278              bShadda = true;
1279            }
1280          }
1281        }
1282      } else {
1283        bShadda = false;
1284      }
1285    } else if (chartype == FX_CHARTYPE_Numeric) {
1286      wForm = wch;
1287      if (bArabicNumber)
1288        wForm += 0x0630;
1289    } else if (wch == L'.') {
1290      wForm = wch;
1291      if (bArabicNumber) {
1292        wNext = 0xFEFF;
1293        if (pAccess) {
1294          iNext = i + 1;
1295          if (iNext <= iLength)
1296            wNext = pAccess->GetChar(pIdentity, iNext);
1297        } else {
1298          if (i < iLength)
1299            wNext = *pStr;
1300        }
1301        if (wNext >= L'0' && wNext <= L'9')
1302          wForm = 0x066B;
1303      }
1304    } else if (wch == L',') {
1305      wForm = wch;
1306      if (bArabicComma)
1307        wForm = 0x060C;
1308    } else if (bRTLPiece || bVerticalChar) {
1309      wForm = FX_GetMirrorChar(wch, dwProps, bRTLPiece, bVerticalChar);
1310    } else {
1311      wForm = wch;
1312    }
1313    if (chartype != FX_CHARTYPE_Combination)
1314      bShadda = false;
1315    if (chartype < FX_CHARTYPE_ArabicAlef)
1316      bLam = false;
1317
1318    dwProps = FX_GetUnicodeProperties(wForm);
1319    int32_t iCharRotation = iRotation;
1320    if (bVerticalChar && (dwProps & 0x8000) != 0)
1321      iCharRotation++;
1322
1323    iCharRotation %= 4;
1324    bool bEmptyChar =
1325        (chartype >= FX_CHARTYPE_Tab && chartype <= FX_CHARTYPE_Control);
1326    if (wForm == 0xFEFF)
1327      bEmptyChar = true;
1328
1329    int32_t iForms = bLam ? 3 : 1;
1330    iCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
1331    if (!pCharPos) {
1332      if (iWidth > 0)
1333        wPrev = wch;
1334      wLast = wch;
1335      continue;
1336    }
1337
1338    int32_t iCharWidth = iWidth;
1339    if (iCharWidth < 0)
1340      iCharWidth = -iCharWidth;
1341
1342    iCharWidth /= iFontSize;
1343    formChars[0].wch = wch;
1344    formChars[0].wForm = wForm;
1345    formChars[0].iWidth = iCharWidth;
1346    if (bLam) {
1347      formChars[1].wForm = 0x0651;
1348      iCharWidth = 0;
1349      pFont->GetCharWidth(0x0651, iCharWidth, false);
1350      formChars[1].iWidth = iCharWidth;
1351      formChars[2].wForm = 0x0670;
1352      iCharWidth = 0;
1353      pFont->GetCharWidth(0x0670, iCharWidth, false);
1354      formChars[2].iWidth = iCharWidth;
1355    }
1356
1357    for (int32_t j = 0; j < iForms; j++) {
1358      wForm = (FX_WCHAR)formChars[j].wForm;
1359      iCharWidth = formChars[j].iWidth;
1360      if (j > 0) {
1361        chartype = FX_CHARTYPE_Combination;
1362        wch = wForm;
1363        wLast = (FX_WCHAR)formChars[j - 1].wForm;
1364      }
1365      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
1366        pCharPos->m_GlyphIndex =
1367            bCharCode ? wch : pFont->GetGlyphIndex(wForm, false);
1368#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
1369        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
1370#endif
1371        pCharPos->m_FontCharWidth = iCharWidth;
1372        if (pWSForms)
1373          *pWSForms += wForm;
1374      }
1375
1376      int32_t iCharHeight;
1377      if (bVerticalDoc) {
1378        iCharHeight = iCharWidth;
1379        iCharWidth = 1000;
1380      } else {
1381        iCharHeight = 1000;
1382      }
1383
1384      fCharWidth = fFontSize * iCharWidth / 1000.0f;
1385      fCharHeight = fFontSize * iCharHeight / 1000.0f;
1386      if (bRTLPiece && chartype != FX_CHARTYPE_Combination) {
1387        if (bVerticalDoc)
1388          fY -= fCharHeight;
1389        else
1390          fX -= fCharWidth;
1391      }
1392      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
1393        pCharPos->m_Origin = CFX_PointF(fX, fY);
1394        if ((dwStyles & FX_TXTLAYOUTSTYLE_CombText) != 0) {
1395          int32_t iFormWidth = iCharWidth;
1396          pFont->GetCharWidth(wForm, iFormWidth, false);
1397          FX_FLOAT fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
1398          if (bVerticalDoc)
1399            pCharPos->m_Origin.y += fOffset;
1400          else
1401            pCharPos->m_Origin.x += fOffset;
1402        }
1403
1404        if (chartype == FX_CHARTYPE_Combination) {
1405          CFX_Rect rtBBox;
1406          if (pFont->GetCharBBox(wForm, &rtBBox, false)) {
1407            pCharPos->m_Origin.y =
1408                fYBase + fFontSize -
1409                fFontSize * (FX_FLOAT)rtBBox.height / (FX_FLOAT)iMaxHeight;
1410          }
1411          if (wForm == wch && wLast != 0xFEFF) {
1412            uint32_t dwLastProps = FX_GetUnicodeProperties(wLast);
1413            if ((dwLastProps & FX_CHARTYPEBITSMASK) ==
1414                FX_CHARTYPE_Combination) {
1415              CFX_Rect rtBox;
1416              if (pFont->GetCharBBox(wLast, &rtBox, false))
1417                pCharPos->m_Origin.y -= fFontSize * rtBox.height / iMaxHeight;
1418            }
1419          }
1420        }
1421        CFX_PointF ptOffset;
1422        if (bVerticalChar && (dwProps & 0x00010000) != 0) {
1423          CFX_Rect rtBBox;
1424          if (pFont->GetCharBBox(wForm, &rtBBox, false)) {
1425            ptOffset.x = fFontSize * (850 - rtBBox.right()) / iMaxHeight;
1426            ptOffset.y = fFontSize * (iAscent - rtBBox.top - 150) / iMaxHeight;
1427          }
1428        }
1429        pCharPos->m_Origin.x += ptOffset.x;
1430        pCharPos->m_Origin.y -= ptOffset.y;
1431      }
1432      if (!bRTLPiece && chartype != FX_CHARTYPE_Combination) {
1433        if (bVerticalDoc)
1434          fY += fCharHeight;
1435        else
1436          fX += fCharWidth;
1437      }
1438
1439      if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
1440        pCharPos->m_bGlyphAdjust = true;
1441        if (bVerticalDoc) {
1442          if (iCharRotation == 0) {
1443            pCharPos->m_AdjustMatrix[0] = -1;
1444            pCharPos->m_AdjustMatrix[1] = 0;
1445            pCharPos->m_AdjustMatrix[2] = 0;
1446            pCharPos->m_AdjustMatrix[3] = 1;
1447            pCharPos->m_Origin.y += fAscent;
1448          } else if (iCharRotation == 1) {
1449            pCharPos->m_AdjustMatrix[0] = 0;
1450            pCharPos->m_AdjustMatrix[1] = -1;
1451            pCharPos->m_AdjustMatrix[2] = -1;
1452            pCharPos->m_AdjustMatrix[3] = 0;
1453            pCharPos->m_Origin.x -= fDescent;
1454          } else if (iCharRotation == 2) {
1455            pCharPos->m_AdjustMatrix[0] = 1;
1456            pCharPos->m_AdjustMatrix[1] = 0;
1457            pCharPos->m_AdjustMatrix[2] = 0;
1458            pCharPos->m_AdjustMatrix[3] = -1;
1459            pCharPos->m_Origin.x += fCharWidth;
1460            pCharPos->m_Origin.y += fAscent;
1461          } else {
1462            pCharPos->m_AdjustMatrix[0] = 0;
1463            pCharPos->m_AdjustMatrix[1] = 1;
1464            pCharPos->m_AdjustMatrix[2] = 1;
1465            pCharPos->m_AdjustMatrix[3] = 0;
1466            pCharPos->m_Origin.x += fAscent;
1467          }
1468        } else {
1469          if (iCharRotation == 0) {
1470            pCharPos->m_AdjustMatrix[0] = -1;
1471            pCharPos->m_AdjustMatrix[1] = 0;
1472            pCharPos->m_AdjustMatrix[2] = 0;
1473            pCharPos->m_AdjustMatrix[3] = 1;
1474          } else if (iCharRotation == 1) {
1475            pCharPos->m_AdjustMatrix[0] = 0;
1476            pCharPos->m_AdjustMatrix[1] = -1;
1477            pCharPos->m_AdjustMatrix[2] = -1;
1478            pCharPos->m_AdjustMatrix[3] = 0;
1479            pCharPos->m_Origin.x -= fDescent;
1480            pCharPos->m_Origin.y -= fAscent + fDescent;
1481          } else if (iCharRotation == 2) {
1482            pCharPos->m_AdjustMatrix[0] = 1;
1483            pCharPos->m_AdjustMatrix[1] = 0;
1484            pCharPos->m_AdjustMatrix[2] = 0;
1485            pCharPos->m_AdjustMatrix[3] = -1;
1486            pCharPos->m_Origin.x += fCharWidth;
1487            pCharPos->m_Origin.y -= fAscent;
1488          } else {
1489            pCharPos->m_AdjustMatrix[0] = 0;
1490            pCharPos->m_AdjustMatrix[1] = 1;
1491            pCharPos->m_AdjustMatrix[2] = 1;
1492            pCharPos->m_AdjustMatrix[3] = 0;
1493            pCharPos->m_Origin.x += fAscent;
1494          }
1495        }
1496        if (iHorScale != 100 || iVerScale != 100) {
1497          pCharPos->m_AdjustMatrix[0] =
1498              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
1499          pCharPos->m_AdjustMatrix[1] =
1500              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
1501          pCharPos->m_AdjustMatrix[2] =
1502              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
1503          pCharPos->m_AdjustMatrix[3] =
1504              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
1505        }
1506        pCharPos++;
1507      }
1508    }
1509    if (iWidth > 0)
1510      wPrev = static_cast<FX_WCHAR>(formChars[0].wch);
1511    wLast = wch;
1512  }
1513  return iCount;
1514}
1515
1516std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun,
1517                                                  bool bCharBBox) const {
1518  if (!pTxtRun || pTxtRun->iLength < 1)
1519    return std::vector<CFX_RectF>();
1520
1521  IFX_TxtAccess* pAccess = pTxtRun->pAccess;
1522  const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity;
1523  const FX_WCHAR* pStr = pTxtRun->wsStr.c_str();
1524  int32_t* pWidths = pTxtRun->pWidths;
1525  int32_t iLength = pTxtRun->iLength;
1526  CFX_RectF rect(*pTxtRun->pRect);
1527  FX_FLOAT fFontSize = pTxtRun->fFontSize;
1528  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
1529  FX_FLOAT fScale = fFontSize / 1000.0f;
1530  CFX_RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
1531  if (!pFont)
1532    bCharBBox = false;
1533
1534  CFX_Rect bbox;
1535  if (bCharBBox)
1536    bCharBBox = pFont->GetBBox(&bbox);
1537
1538  FX_FLOAT fLeft = std::max(0.0f, bbox.left * fScale);
1539  FX_FLOAT fHeight = FXSYS_fabs(bbox.height * fScale);
1540  bool bRTLPiece = !!(pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
1541  bool bVertical = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_VerticalLayout);
1542  bool bSingleLine = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_SingleLine);
1543  bool bCombText = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_CombText);
1544  FX_WCHAR wch;
1545  FX_WCHAR wLineBreakChar = pTxtRun->wLineBreakChar;
1546  int32_t iCharSize;
1547  FX_FLOAT fCharSize;
1548  FX_FLOAT fStart;
1549  if (bVertical)
1550    fStart = bRTLPiece ? rect.bottom() : rect.top;
1551  else
1552    fStart = bRTLPiece ? rect.right() : rect.left;
1553
1554  std::vector<CFX_RectF> rtArray(iLength);
1555  for (int32_t i = 0; i < iLength; i++) {
1556    if (pAccess) {
1557      wch = pAccess->GetChar(pIdentity, i);
1558      iCharSize = pAccess->GetWidth(pIdentity, i);
1559    } else {
1560      wch = *pStr++;
1561      iCharSize = *pWidths++;
1562    }
1563    fCharSize = static_cast<FX_FLOAT>(iCharSize) / 20000.0f;
1564    bool bRet = (!bSingleLine && FX_IsCtrlCode(wch));
1565    if (!(wch == L'\v' || wch == L'\f' || wch == 0x2028 || wch == 0x2029 ||
1566          (wLineBreakChar != 0xFEFF && wch == wLineBreakChar))) {
1567      bRet = false;
1568    }
1569    if (bRet) {
1570      iCharSize = iFontSize * 500;
1571      fCharSize = fFontSize / 2.0f;
1572    }
1573    if (bVertical) {
1574      rect.top = fStart;
1575      if (bRTLPiece) {
1576        rect.top -= fCharSize;
1577        fStart -= fCharSize;
1578      } else {
1579        fStart += fCharSize;
1580      }
1581      rect.height = fCharSize;
1582    } else {
1583      rect.left = fStart;
1584      if (bRTLPiece) {
1585        rect.left -= fCharSize;
1586        fStart -= fCharSize;
1587      } else {
1588        fStart += fCharSize;
1589      }
1590      rect.width = fCharSize;
1591    }
1592
1593    if (bCharBBox && !bRet) {
1594      int32_t iCharWidth = 1000;
1595      pFont->GetCharWidth(wch, iCharWidth, false);
1596      FX_FLOAT fRTLeft = 0, fCharWidth = 0;
1597      if (iCharWidth > 0) {
1598        fCharWidth = iCharWidth * fScale;
1599        fRTLeft = fLeft;
1600        if (bCombText)
1601          fRTLeft = (rect.width - fCharWidth) / 2.0f;
1602      }
1603      CFX_RectF rtBBoxF;
1604      if (bVertical) {
1605        rtBBoxF.top = rect.left + fRTLeft;
1606        rtBBoxF.left = rect.top + (rect.height - fHeight) / 2.0f;
1607        rtBBoxF.height = fCharWidth;
1608        rtBBoxF.width = fHeight;
1609        rtBBoxF.left = std::max(rtBBoxF.left, 0.0f);
1610      } else {
1611        rtBBoxF.left = rect.left + fRTLeft;
1612        rtBBoxF.top = rect.top + (rect.height - fHeight) / 2.0f;
1613        rtBBoxF.width = fCharWidth;
1614        rtBBoxF.height = fHeight;
1615        rtBBoxF.top = std::max(rtBBoxF.top, 0.0f);
1616      }
1617      rtArray[i] = rtBBoxF;
1618      continue;
1619    }
1620    rtArray[i] = rect;
1621  }
1622  return rtArray;
1623}
1624
1625FX_TXTRUN::FX_TXTRUN()
1626    : pAccess(nullptr),
1627      pIdentity(nullptr),
1628      pWidths(nullptr),
1629      iLength(0),
1630      pFont(nullptr),
1631      fFontSize(12),
1632      dwStyles(0),
1633      iHorizontalScale(100),
1634      iVerticalScale(100),
1635      iCharRotation(0),
1636      dwCharStyles(0),
1637      pRect(nullptr),
1638      wLineBreakChar(L'\n'),
1639      bSkipSpace(true) {}
1640
1641FX_TXTRUN::~FX_TXTRUN() {}
1642
1643FX_TXTRUN::FX_TXTRUN(const FX_TXTRUN& other) = default;
1644
1645CFX_TxtPiece::CFX_TxtPiece()
1646    : m_dwStatus(FX_TXTBREAK_PieceBreak),
1647      m_iStartPos(0),
1648      m_iWidth(-1),
1649      m_iStartChar(0),
1650      m_iChars(0),
1651      m_iBidiLevel(0),
1652      m_iBidiPos(0),
1653      m_iHorizontalScale(100),
1654      m_iVerticalScale(100),
1655      m_dwCharStyles(0),
1656      m_pChars(nullptr),
1657      m_pUserData(nullptr) {}
1658
1659CFX_TxtLine::CFX_TxtLine(int32_t iBlockSize)
1660    : m_pLineChars(new std::vector<CFX_TxtChar>),
1661      m_pLinePieces(new CFX_TxtPieceArray(16)),
1662      m_iStart(0),
1663      m_iWidth(0),
1664      m_iArabicChars(0) {}
1665
1666CFX_TxtLine::~CFX_TxtLine() {
1667  RemoveAll();
1668}
1669