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_rtfbreak.h"
8
9#include <algorithm>
10
11#include "core/fxcrt/fx_arabic.h"
12#include "core/fxcrt/fx_arb.h"
13#include "third_party/base/stl_util.h"
14#include "xfa/fgas/font/cfgas_gefont.h"
15#include "xfa/fgas/layout/fgas_linebreak.h"
16
17namespace {
18
19typedef CFX_RTFBreakType (CFX_RTFBreak::*FX_RTFBreak_LPFAppendChar)(
20    CFX_RTFChar* pCurChar);
21const FX_RTFBreak_LPFAppendChar g_FX_RTFBreak_lpfAppendChar[16] = {
22    &CFX_RTFBreak::AppendChar_Others,      &CFX_RTFBreak::AppendChar_Tab,
23    &CFX_RTFBreak::AppendChar_Others,      &CFX_RTFBreak::AppendChar_Control,
24    &CFX_RTFBreak::AppendChar_Combination, &CFX_RTFBreak::AppendChar_Others,
25    &CFX_RTFBreak::AppendChar_Others,      &CFX_RTFBreak::AppendChar_Arabic,
26    &CFX_RTFBreak::AppendChar_Arabic,      &CFX_RTFBreak::AppendChar_Arabic,
27    &CFX_RTFBreak::AppendChar_Arabic,      &CFX_RTFBreak::AppendChar_Arabic,
28    &CFX_RTFBreak::AppendChar_Arabic,      &CFX_RTFBreak::AppendChar_Others,
29    &CFX_RTFBreak::AppendChar_Others,      &CFX_RTFBreak::AppendChar_Others,
30};
31
32}  // namespace
33
34CFX_RTFBreak::CFX_RTFBreak(uint32_t dwLayoutStyles)
35    : m_iBoundaryStart(0),
36      m_iBoundaryEnd(2000000),
37      m_dwLayoutStyles(dwLayoutStyles),
38      m_bPagination(false),
39      m_pFont(nullptr),
40      m_iFontHeight(240),
41      m_iFontSize(240),
42      m_iTabWidth(720000),
43      m_wDefChar(0xFEFF),
44      m_iDefChar(0),
45      m_wLineBreakChar(L'\n'),
46      m_iHorizontalScale(100),
47      m_iVerticalScale(100),
48      m_iCharSpace(0),
49      m_iAlignment(CFX_RTFLineAlignment::Left),
50      m_pUserData(nullptr),
51      m_eCharType(FX_CHARTYPE_Unknown),
52      m_dwIdentity(0),
53      m_RTFLine1(),
54      m_RTFLine2(),
55      m_pCurLine(nullptr),
56      m_iReady(0),
57      m_iTolerance(0) {
58  m_pCurLine = &m_RTFLine1;
59
60  SetBreakStatus();
61  m_bPagination = (m_dwLayoutStyles & FX_RTFLAYOUTSTYLE_Pagination) != 0;
62}
63
64CFX_RTFBreak::~CFX_RTFBreak() {
65  Reset();
66}
67
68void CFX_RTFBreak::SetLineBoundary(FX_FLOAT fLineStart, FX_FLOAT fLineEnd) {
69  if (fLineStart > fLineEnd)
70    return;
71
72  m_iBoundaryStart = FXSYS_round(fLineStart * 20000.0f);
73  m_iBoundaryEnd = FXSYS_round(fLineEnd * 20000.0f);
74  m_pCurLine->m_iStart = std::min(m_pCurLine->m_iStart, m_iBoundaryEnd);
75  m_pCurLine->m_iStart = std::max(m_pCurLine->m_iStart, m_iBoundaryStart);
76}
77
78void CFX_RTFBreak::SetLineStartPos(FX_FLOAT fLinePos) {
79  int32_t iLinePos = FXSYS_round(fLinePos * 20000.0f);
80  iLinePos = std::min(iLinePos, m_iBoundaryEnd);
81  iLinePos = std::max(iLinePos, m_iBoundaryStart);
82  m_pCurLine->m_iStart = iLinePos;
83}
84
85void CFX_RTFBreak::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont) {
86  if (!pFont || pFont == m_pFont)
87    return;
88
89  SetBreakStatus();
90  m_pFont = pFont;
91  FontChanged();
92}
93
94void CFX_RTFBreak::SetFontSize(FX_FLOAT fFontSize) {
95  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
96  if (m_iFontSize == iFontSize)
97    return;
98
99  SetBreakStatus();
100  m_iFontSize = iFontSize;
101  FontChanged();
102}
103
104void CFX_RTFBreak::FontChanged() {
105  m_iDefChar = 0;
106  if (!m_pFont)
107    return;
108
109  m_iFontHeight = m_iFontSize;
110  if (m_wDefChar == 0xFEFF)
111    return;
112
113  m_pFont->GetCharWidth(m_wDefChar, m_iDefChar, false);
114  m_iDefChar *= m_iFontSize;
115}
116
117void CFX_RTFBreak::SetTabWidth(FX_FLOAT fTabWidth) {
118  m_iTabWidth = FXSYS_round(fTabWidth * 20000.0f);
119}
120
121void CFX_RTFBreak::AddPositionedTab(FX_FLOAT fTabPos) {
122  int32_t iTabPos = std::min(FXSYS_round(fTabPos * 20000.0f) + m_iBoundaryStart,
123                             m_iBoundaryEnd);
124  auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
125                             iTabPos);
126  if (it != m_PositionedTabs.end() && *it == iTabPos)
127    return;
128  m_PositionedTabs.insert(it, iTabPos);
129}
130
131void CFX_RTFBreak::SetLineBreakTolerance(FX_FLOAT fTolerance) {
132  m_iTolerance = FXSYS_round(fTolerance * 20000.0f);
133}
134
135void CFX_RTFBreak::SetHorizontalScale(int32_t iScale) {
136  if (iScale < 0)
137    iScale = 0;
138  if (m_iHorizontalScale == iScale)
139    return;
140
141  SetBreakStatus();
142  m_iHorizontalScale = iScale;
143}
144
145void CFX_RTFBreak::SetVerticalScale(int32_t iScale) {
146  if (iScale < 0)
147    iScale = 0;
148  if (m_iVerticalScale == iScale)
149    return;
150
151  SetBreakStatus();
152  m_iVerticalScale = iScale;
153}
154
155void CFX_RTFBreak::SetCharSpace(FX_FLOAT fCharSpace) {
156  m_iCharSpace = FXSYS_round(fCharSpace * 20000.0f);
157}
158
159void CFX_RTFBreak::SetUserData(const CFX_RetainPtr<CFX_Retainable>& pUserData) {
160  if (m_pUserData == pUserData)
161    return;
162
163  SetBreakStatus();
164  m_pUserData = pUserData;
165}
166
167void CFX_RTFBreak::SetBreakStatus() {
168  m_dwIdentity++;
169  int32_t iCount = m_pCurLine->CountChars();
170  if (iCount < 1)
171    return;
172
173  CFX_RTFChar& tc = m_pCurLine->GetChar(iCount - 1);
174  if (tc.m_dwStatus == CFX_RTFBreakType::None)
175    tc.m_dwStatus = CFX_RTFBreakType::Piece;
176}
177
178CFX_RTFChar* CFX_RTFBreak::GetLastChar(int32_t index) const {
179  std::vector<CFX_RTFChar>& tca = m_pCurLine->m_LineChars;
180  int32_t iCount = pdfium::CollectionSize<int32_t>(tca);
181  if (index < 0 || index >= iCount)
182    return nullptr;
183
184  int32_t iStart = iCount - 1;
185  while (iStart > -1) {
186    CFX_RTFChar* pTC = &tca[iStart--];
187    if (pTC->m_iCharWidth >= 0 ||
188        pTC->GetCharType() != FX_CHARTYPE_Combination) {
189      if (--index < 0)
190        return pTC;
191    }
192  }
193  return nullptr;
194}
195
196const CFX_RTFLine* CFX_RTFBreak::GetRTFLine() const {
197  if (m_iReady == 1)
198    return &m_RTFLine1;
199  if (m_iReady == 2)
200    return &m_RTFLine2;
201  return nullptr;
202}
203
204const CFX_RTFPieceArray* CFX_RTFBreak::GetRTFPieces() const {
205  const CFX_RTFLine* pRTFLine = GetRTFLine();
206  return pRTFLine ? &pRTFLine->m_LinePieces : nullptr;
207}
208
209inline FX_CHARTYPE CFX_RTFBreak::GetUnifiedCharType(
210    FX_CHARTYPE chartype) const {
211  return chartype >= FX_CHARTYPE_ArabicAlef ? FX_CHARTYPE_Arabic : chartype;
212}
213
214int32_t CFX_RTFBreak::GetLastPositionedTab() const {
215  return m_PositionedTabs.empty() ? m_iBoundaryStart : m_PositionedTabs.back();
216}
217
218bool CFX_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
219  auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
220                             *iTabPos);
221  if (it == m_PositionedTabs.end())
222    return false;
223
224  *iTabPos = *it;
225  return true;
226}
227
228CFX_RTFBreakType CFX_RTFBreak::AppendChar(FX_WCHAR wch) {
229  ASSERT(m_pFont && m_pCurLine);
230
231  uint32_t dwProps = kTextLayoutCodeProperties[static_cast<uint16_t>(wch)];
232  FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
233  m_pCurLine->m_LineChars.emplace_back();
234
235  CFX_RTFChar* pCurChar = &m_pCurLine->m_LineChars.back();
236  pCurChar->m_dwStatus = CFX_RTFBreakType::None;
237  pCurChar->m_wCharCode = wch;
238  pCurChar->m_dwCharProps = dwProps;
239  pCurChar->m_iFontSize = m_iFontSize;
240  pCurChar->m_iFontHeight = m_iFontHeight;
241  pCurChar->m_iHorizontalScale = m_iHorizontalScale;
242  pCurChar->m_iVerticalScale = m_iVerticalScale;
243  pCurChar->m_iCharWidth = 0;
244  pCurChar->m_dwIdentity = m_dwIdentity;
245  pCurChar->m_pUserData = m_pUserData;
246
247  CFX_RTFBreakType dwRet1 = CFX_RTFBreakType::None;
248  if (chartype != FX_CHARTYPE_Combination &&
249      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
250      m_eCharType != FX_CHARTYPE_Unknown &&
251      m_pCurLine->GetLineEnd() > m_iBoundaryEnd + m_iTolerance &&
252      (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
253    dwRet1 = EndBreak(CFX_RTFBreakType::Line);
254    int32_t iCount = m_pCurLine->CountChars();
255    if (iCount > 0)
256      pCurChar = &m_pCurLine->m_LineChars[iCount - 1];
257  }
258
259  CFX_RTFBreakType dwRet2 =
260      (this->*g_FX_RTFBreak_lpfAppendChar[chartype >> FX_CHARTYPEBITS])(
261          pCurChar);
262  m_eCharType = chartype;
263  return std::max(dwRet1, dwRet2);
264}
265
266CFX_RTFBreakType CFX_RTFBreak::AppendChar_Combination(CFX_RTFChar* pCurChar) {
267  int32_t iCharWidth = 0;
268  if (!m_pFont->GetCharWidth(pCurChar->m_wCharCode, iCharWidth, false))
269    iCharWidth = 0;
270
271  iCharWidth *= m_iFontSize;
272  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
273  CFX_RTFChar* pLastChar = GetLastChar(0);
274  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE_Combination)
275    iCharWidth = -iCharWidth;
276  else
277    m_eCharType = FX_CHARTYPE_Combination;
278
279  pCurChar->m_iCharWidth = iCharWidth;
280  if (iCharWidth > 0)
281    m_pCurLine->m_iWidth += iCharWidth;
282
283  return CFX_RTFBreakType::None;
284}
285
286CFX_RTFBreakType CFX_RTFBreak::AppendChar_Tab(CFX_RTFChar* pCurChar) {
287  if (!(m_dwLayoutStyles & FX_RTFLAYOUTSTYLE_ExpandTab))
288    return CFX_RTFBreakType::None;
289
290  int32_t& iLineWidth = m_pCurLine->m_iWidth;
291  int32_t iCharWidth = iLineWidth;
292  if (GetPositionedTab(&iCharWidth))
293    iCharWidth -= iLineWidth;
294  else
295    iCharWidth = m_iTabWidth * (iLineWidth / m_iTabWidth + 1) - iLineWidth;
296
297  pCurChar->m_iCharWidth = iCharWidth;
298  iLineWidth += iCharWidth;
299  return CFX_RTFBreakType::None;
300}
301
302CFX_RTFBreakType CFX_RTFBreak::AppendChar_Control(CFX_RTFChar* pCurChar) {
303  CFX_RTFBreakType dwRet2 = CFX_RTFBreakType::None;
304  switch (pCurChar->m_wCharCode) {
305    case L'\v':
306    case 0x2028:
307      dwRet2 = CFX_RTFBreakType::Line;
308      break;
309    case L'\f':
310      dwRet2 = CFX_RTFBreakType::Page;
311      break;
312    case 0x2029:
313      dwRet2 = CFX_RTFBreakType::Paragraph;
314      break;
315    default:
316      if (pCurChar->m_wCharCode == m_wLineBreakChar)
317        dwRet2 = CFX_RTFBreakType::Paragraph;
318      break;
319  }
320  if (dwRet2 != CFX_RTFBreakType::None)
321    dwRet2 = EndBreak(dwRet2);
322
323  return dwRet2;
324}
325
326CFX_RTFBreakType CFX_RTFBreak::AppendChar_Arabic(CFX_RTFChar* pCurChar) {
327  CFX_RTFChar* pLastChar = nullptr;
328  int32_t iCharWidth = 0;
329  FX_WCHAR wForm;
330  bool bAlef = false;
331  if (m_eCharType >= FX_CHARTYPE_ArabicAlef &&
332      m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
333    pLastChar = GetLastChar(1);
334    if (pLastChar) {
335      m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
336      CFX_RTFChar* pPrevChar = GetLastChar(2);
337      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
338      bAlef = (wForm == 0xFEFF &&
339               pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
340      if (!m_pFont->GetCharWidth(wForm, iCharWidth, false) &&
341          !m_pFont->GetCharWidth(pLastChar->m_wCharCode, iCharWidth, false)) {
342        iCharWidth = m_iDefChar;
343      }
344
345      iCharWidth *= m_iFontSize;
346      iCharWidth = iCharWidth * m_iHorizontalScale / 100;
347      pLastChar->m_iCharWidth = iCharWidth;
348      m_pCurLine->m_iWidth += iCharWidth;
349      iCharWidth = 0;
350    }
351  }
352
353  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
354                                      nullptr);
355  if (!m_pFont->GetCharWidth(wForm, iCharWidth, false) &&
356      !m_pFont->GetCharWidth(pCurChar->m_wCharCode, iCharWidth, false)) {
357    iCharWidth = m_iDefChar;
358  }
359
360  iCharWidth *= m_iFontSize;
361  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
362  pCurChar->m_iCharWidth = iCharWidth;
363  m_pCurLine->m_iWidth += iCharWidth;
364  m_pCurLine->m_iArabicChars++;
365
366  if (m_pCurLine->GetLineEnd() > m_iBoundaryEnd + m_iTolerance)
367    return EndBreak(CFX_RTFBreakType::Line);
368  return CFX_RTFBreakType::None;
369}
370
371CFX_RTFBreakType CFX_RTFBreak::AppendChar_Others(CFX_RTFChar* pCurChar) {
372  FX_CHARTYPE chartype = pCurChar->GetCharType();
373  FX_WCHAR wForm = pCurChar->m_wCharCode;
374  int32_t iCharWidth = 0;
375  if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
376    iCharWidth = m_iDefChar;
377
378  iCharWidth *= m_iFontSize;
379  iCharWidth *= m_iHorizontalScale / 100;
380  iCharWidth += m_iCharSpace;
381
382  pCurChar->m_iCharWidth = iCharWidth;
383  m_pCurLine->m_iWidth += iCharWidth;
384  if (chartype != FX_CHARTYPE_Space &&
385      m_pCurLine->GetLineEnd() > m_iBoundaryEnd + m_iTolerance) {
386    return EndBreak(CFX_RTFBreakType::Line);
387  }
388  return CFX_RTFBreakType::None;
389}
390
391CFX_RTFBreakType CFX_RTFBreak::EndBreak(CFX_RTFBreakType dwStatus) {
392  ASSERT(dwStatus != CFX_RTFBreakType::None);
393
394  m_dwIdentity++;
395  const CFX_RTFPieceArray* pCurPieces = &m_pCurLine->m_LinePieces;
396  int32_t iCount = pCurPieces->GetSize();
397  if (iCount > 0) {
398    CFX_RTFPiece* pLastPiece = pCurPieces->GetPtrAt(--iCount);
399    if (dwStatus != CFX_RTFBreakType::Piece)
400      pLastPiece->m_dwStatus = dwStatus;
401    else
402      dwStatus = pLastPiece->m_dwStatus;
403    return dwStatus;
404  }
405
406  const CFX_RTFLine* pLastLine = GetRTFLine();
407  if (pLastLine) {
408    pCurPieces = &pLastLine->m_LinePieces;
409    iCount = pCurPieces->GetSize();
410    if (iCount-- > 0) {
411      CFX_RTFPiece* pLastPiece = pCurPieces->GetPtrAt(iCount);
412      if (dwStatus != CFX_RTFBreakType::Piece)
413        pLastPiece->m_dwStatus = dwStatus;
414      else
415        dwStatus = pLastPiece->m_dwStatus;
416      return dwStatus;
417    }
418    return CFX_RTFBreakType::None;
419  }
420
421  iCount = m_pCurLine->CountChars();
422  if (iCount < 1)
423    return CFX_RTFBreakType::None;
424
425  CFX_RTFChar& tc = m_pCurLine->GetChar(iCount - 1);
426  tc.m_dwStatus = dwStatus;
427  if (dwStatus == CFX_RTFBreakType::Piece)
428    return dwStatus;
429
430  m_iReady = m_pCurLine == &m_RTFLine1 ? 1 : 2;
431  CFX_RTFLine* pNextLine =
432      m_pCurLine == &m_RTFLine1 ? &m_RTFLine2 : &m_RTFLine1;
433  bool bAllChars = m_iAlignment == CFX_RTFLineAlignment::Justified ||
434                   m_iAlignment == CFX_RTFLineAlignment::Distributed;
435
436  if (!EndBreak_SplitLine(pNextLine, bAllChars, dwStatus)) {
437    std::deque<FX_TPO> tpos;
438    EndBreak_BidiLine(&tpos, dwStatus);
439    if (!m_bPagination && m_iAlignment != CFX_RTFLineAlignment::Left)
440      EndBreak_Alignment(tpos, bAllChars, dwStatus);
441  }
442  m_pCurLine = pNextLine;
443  m_pCurLine->m_iStart = m_iBoundaryStart;
444
445  CFX_RTFChar* pTC = GetLastChar(0);
446  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
447  return dwStatus;
448}
449
450bool CFX_RTFBreak::EndBreak_SplitLine(CFX_RTFLine* pNextLine,
451                                      bool bAllChars,
452                                      CFX_RTFBreakType dwStatus) {
453  bool bDone = false;
454  if (m_pCurLine->GetLineEnd() > m_iBoundaryEnd + m_iTolerance) {
455    const CFX_RTFChar& tc = m_pCurLine->GetChar(m_pCurLine->CountChars() - 1);
456    switch (tc.GetCharType()) {
457      case FX_CHARTYPE_Tab:
458      case FX_CHARTYPE_Control:
459      case FX_CHARTYPE_Space:
460        break;
461      default:
462        SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
463        bDone = true;
464        break;
465    }
466  }
467
468  if (!m_bPagination && m_pCurLine->m_iMBCSChars <= 0) {
469    if (bAllChars && !bDone) {
470      int32_t endPos = m_pCurLine->GetLineEnd();
471      GetBreakPos(m_pCurLine->m_LineChars, endPos, bAllChars, true);
472    }
473    return false;
474  }
475
476  const CFX_RTFChar* pCurChars = m_pCurLine->m_LineChars.data();
477  const CFX_RTFChar* pTC;
478  CFX_RTFPieceArray* pCurPieces = &m_pCurLine->m_LinePieces;
479  CFX_RTFPiece tp;
480  tp.m_pChars = &m_pCurLine->m_LineChars;
481  bool bNew = true;
482  uint32_t dwIdentity = static_cast<uint32_t>(-1);
483  int32_t iLast = m_pCurLine->CountChars() - 1;
484  int32_t j = 0;
485  for (int32_t i = 0; i <= iLast;) {
486    pTC = pCurChars + i;
487    if (bNew) {
488      tp.m_iStartChar = i;
489      tp.m_iStartPos += tp.m_iWidth;
490      tp.m_iWidth = 0;
491      tp.m_dwStatus = pTC->m_dwStatus;
492      tp.m_iFontSize = pTC->m_iFontSize;
493      tp.m_iFontHeight = pTC->m_iFontHeight;
494      tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
495      tp.m_iVerticalScale = pTC->m_iVerticalScale;
496      dwIdentity = pTC->m_dwIdentity;
497      tp.m_dwIdentity = dwIdentity;
498      tp.m_pUserData = pTC->m_pUserData;
499      j = i;
500      bNew = false;
501    }
502
503    if (i == iLast || pTC->m_dwStatus != CFX_RTFBreakType::None ||
504        pTC->m_dwIdentity != dwIdentity) {
505      tp.m_iChars = i - j;
506      if (pTC->m_dwIdentity == dwIdentity) {
507        tp.m_dwStatus = pTC->m_dwStatus;
508        tp.m_iWidth += pTC->m_iCharWidth;
509        tp.m_iChars += 1;
510        i++;
511      }
512      pCurPieces->Add(tp);
513      bNew = true;
514    } else {
515      tp.m_iWidth += pTC->m_iCharWidth;
516      i++;
517    }
518  }
519  return true;
520}
521
522void CFX_RTFBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
523                                     CFX_RTFBreakType dwStatus) {
524  FX_TPO tpo;
525  CFX_RTFPiece tp;
526  CFX_RTFChar* pTC;
527  int32_t i;
528  int32_t j;
529  std::vector<CFX_RTFChar>& chars = m_pCurLine->m_LineChars;
530  int32_t iCount = m_pCurLine->CountChars();
531  if (!m_bPagination && m_pCurLine->m_iArabicChars > 0) {
532    int32_t iBidiNum = 0;
533    for (i = 0; i < iCount; i++) {
534      pTC = &chars[i];
535      pTC->m_iBidiPos = i;
536      if (pTC->GetCharType() != FX_CHARTYPE_Control)
537        iBidiNum = i;
538      if (i == 0)
539        pTC->m_iBidiLevel = 1;
540    }
541    FX_BidiLine(chars, iBidiNum + 1, 0);
542  } else {
543    for (i = 0; i < iCount; i++) {
544      pTC = &chars[i];
545      pTC->m_iBidiLevel = 0;
546      pTC->m_iBidiPos = 0;
547      pTC->m_iBidiOrder = 0;
548    }
549  }
550
551  tp.m_dwStatus = CFX_RTFBreakType::Piece;
552  tp.m_iStartPos = m_pCurLine->m_iStart;
553  tp.m_pChars = &chars;
554  CFX_RTFPieceArray* pCurPieces = &m_pCurLine->m_LinePieces;
555  int32_t iBidiLevel = -1;
556  int32_t iCharWidth;
557  uint32_t dwIdentity = static_cast<uint32_t>(-1);
558  i = 0;
559  j = 0;
560  while (i < iCount) {
561    pTC = &chars[i];
562    if (iBidiLevel < 0) {
563      iBidiLevel = pTC->m_iBidiLevel;
564      iCharWidth = pTC->m_iCharWidth;
565      tp.m_iWidth = iCharWidth < 1 ? 0 : iCharWidth;
566      tp.m_iBidiLevel = iBidiLevel;
567      tp.m_iBidiPos = pTC->m_iBidiOrder;
568      tp.m_iFontSize = pTC->m_iFontSize;
569      tp.m_iFontHeight = pTC->m_iFontHeight;
570      tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
571      tp.m_iVerticalScale = pTC->m_iVerticalScale;
572      dwIdentity = pTC->m_dwIdentity;
573      tp.m_dwIdentity = dwIdentity;
574      tp.m_pUserData = pTC->m_pUserData;
575      tp.m_dwStatus = CFX_RTFBreakType::Piece;
576      i++;
577    } else if (iBidiLevel != pTC->m_iBidiLevel ||
578               pTC->m_dwIdentity != dwIdentity) {
579      tp.m_iChars = i - tp.m_iStartChar;
580      pCurPieces->Add(tp);
581      tp.m_iStartPos += tp.m_iWidth;
582      tp.m_iStartChar = i;
583      tpo.index = j++;
584      tpo.pos = tp.m_iBidiPos;
585      tpos->push_back(tpo);
586      iBidiLevel = -1;
587    } else {
588      iCharWidth = pTC->m_iCharWidth;
589      if (iCharWidth > 0)
590        tp.m_iWidth += iCharWidth;
591      i++;
592    }
593  }
594
595  if (i > tp.m_iStartChar) {
596    tp.m_dwStatus = dwStatus;
597    tp.m_iChars = i - tp.m_iStartChar;
598    pCurPieces->Add(tp);
599    tpo.index = j;
600    tpo.pos = tp.m_iBidiPos;
601    tpos->push_back(tpo);
602  }
603
604  std::sort(tpos->begin(), tpos->end());
605  int32_t iStartPos = m_pCurLine->m_iStart;
606  for (const auto& it : *tpos) {
607    CFX_RTFPiece& ttp = pCurPieces->GetAt(it.index);
608    ttp.m_iStartPos = iStartPos;
609    iStartPos += ttp.m_iWidth;
610  }
611}
612
613void CFX_RTFBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
614                                      bool bAllChars,
615                                      CFX_RTFBreakType dwStatus) {
616  CFX_RTFPieceArray* pCurPieces = &m_pCurLine->m_LinePieces;
617  int32_t iNetWidth = m_pCurLine->m_iWidth;
618  int32_t iGapChars = 0;
619  int32_t iCharWidth;
620  int32_t iCount = pCurPieces->GetSize();
621  bool bFind = false;
622  uint32_t dwCharType;
623  int32_t i;
624  int32_t j;
625  FX_TPO tpo;
626  for (i = iCount - 1; i > -1; i--) {
627    tpo = tpos[i];
628    CFX_RTFPiece& ttp = pCurPieces->GetAt(tpo.index);
629    if (!bFind)
630      iNetWidth = ttp.GetEndPos();
631
632    bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
633    j = bArabic ? 0 : ttp.m_iChars - 1;
634    while (j > -1 && j < ttp.m_iChars) {
635      const CFX_RTFChar& tc = ttp.GetChar(j);
636      if (tc.m_nBreakType == FX_LBT_DIRECT_BRK)
637        iGapChars++;
638
639      if (!bFind || !bAllChars) {
640        dwCharType = tc.GetCharType();
641        if (dwCharType == FX_CHARTYPE_Space ||
642            dwCharType == FX_CHARTYPE_Control) {
643          if (!bFind) {
644            iCharWidth = tc.m_iCharWidth;
645            if (bAllChars && iCharWidth > 0)
646              iNetWidth -= iCharWidth;
647          }
648        } else {
649          bFind = true;
650          if (!bAllChars)
651            break;
652        }
653      }
654      j += bArabic ? 1 : -1;
655    }
656    if (!bAllChars && bFind)
657      break;
658  }
659
660  int32_t iOffset = m_iBoundaryEnd - iNetWidth;
661  if (iGapChars > 0 && (m_iAlignment == CFX_RTFLineAlignment::Distributed ||
662                        (m_iAlignment == CFX_RTFLineAlignment::Justified &&
663                         dwStatus != CFX_RTFBreakType::Paragraph))) {
664    int32_t iStart = -1;
665    for (i = 0; i < iCount; i++) {
666      tpo = tpos[i];
667      CFX_RTFPiece& ttp = pCurPieces->GetAt(tpo.index);
668      if (iStart < 0)
669        iStart = ttp.m_iStartPos;
670      else
671        ttp.m_iStartPos = iStart;
672
673      for (j = 0; j < ttp.m_iChars; j++) {
674        CFX_RTFChar& tc = ttp.GetChar(j);
675        if (tc.m_nBreakType != FX_LBT_DIRECT_BRK || tc.m_iCharWidth < 0)
676          continue;
677
678        int32_t k = iOffset / iGapChars;
679        tc.m_iCharWidth += k;
680        ttp.m_iWidth += k;
681        iOffset -= k;
682        iGapChars--;
683        if (iGapChars < 1)
684          break;
685      }
686      iStart += ttp.m_iWidth;
687    }
688  } else if (m_iAlignment == CFX_RTFLineAlignment::Right ||
689             m_iAlignment == CFX_RTFLineAlignment::Center) {
690    if (m_iAlignment == CFX_RTFLineAlignment::Center)
691      iOffset /= 2;
692    if (iOffset > 0) {
693      for (i = 0; i < iCount; i++) {
694        CFX_RTFPiece& ttp = pCurPieces->GetAt(i);
695        ttp.m_iStartPos += iOffset;
696      }
697    }
698  }
699}
700
701int32_t CFX_RTFBreak::GetBreakPos(std::vector<CFX_RTFChar>& tca,
702                                  int32_t& iEndPos,
703                                  bool bAllChars,
704                                  bool bOnlyBrk) {
705  int32_t iLength = pdfium::CollectionSize<int32_t>(tca) - 1;
706  if (iLength < 1)
707    return iLength;
708
709  int32_t iBreak = -1;
710  int32_t iBreakPos = -1;
711  int32_t iIndirect = -1;
712  int32_t iIndirectPos = -1;
713  int32_t iLast = -1;
714  int32_t iLastPos = -1;
715  if (iEndPos <= m_iBoundaryEnd) {
716    if (!bAllChars)
717      return iLength;
718
719    iBreak = iLength;
720    iBreakPos = iEndPos;
721  }
722
723  CFX_RTFChar* pCharArray = tca.data();
724  CFX_RTFChar* pCur = pCharArray + iLength;
725  --iLength;
726  if (bAllChars)
727    pCur->m_nBreakType = FX_LBT_UNKNOWN;
728
729  uint32_t nCodeProp = pCur->m_dwCharProps;
730  uint32_t nNext = nCodeProp & 0x003F;
731  int32_t iCharWidth = pCur->m_iCharWidth;
732  if (iCharWidth > 0)
733    iEndPos -= iCharWidth;
734
735  while (iLength >= 0) {
736    pCur = pCharArray + iLength;
737    nCodeProp = pCur->m_dwCharProps;
738    uint32_t nCur = nCodeProp & 0x003F;
739    bool bNeedBreak = false;
740    FX_LINEBREAKTYPE eType;
741    if (nCur == FX_CBP_TB) {
742      bNeedBreak = true;
743      eType = nNext == FX_CBP_TB ? FX_LBT_PROHIBITED_BRK
744                                 : gs_FX_LineBreak_PairTable[nCur][nNext];
745    } else {
746      if (nCur == FX_CBP_SP)
747        bNeedBreak = true;
748
749      eType = nNext == FX_CBP_SP ? FX_LBT_PROHIBITED_BRK
750                                 : gs_FX_LineBreak_PairTable[nCur][nNext];
751    }
752    if (bAllChars)
753      pCur->m_nBreakType = eType;
754
755    if (!bOnlyBrk) {
756      iCharWidth = pCur->m_iCharWidth;
757      if (iEndPos <= m_iBoundaryEnd || bNeedBreak) {
758        if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
759          iBreak = iLength;
760          iBreakPos = iEndPos;
761          if (!bAllChars)
762            return iLength;
763        } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
764          iIndirect = iLength;
765          iIndirectPos = iEndPos;
766        }
767        if (iLast < 0) {
768          iLast = iLength;
769          iLastPos = iEndPos;
770        }
771      }
772      if (iCharWidth > 0)
773        iEndPos -= iCharWidth;
774    }
775    nNext = nCodeProp & 0x003F;
776    iLength--;
777  }
778  if (bOnlyBrk)
779    return 0;
780
781  if (iBreak > -1) {
782    iEndPos = iBreakPos;
783    return iBreak;
784  }
785  if (iIndirect > -1) {
786    iEndPos = iIndirectPos;
787    return iIndirect;
788  }
789  if (iLast > -1) {
790    iEndPos = iLastPos;
791    return iLast;
792  }
793  return 0;
794}
795
796void CFX_RTFBreak::SplitTextLine(CFX_RTFLine* pCurLine,
797                                 CFX_RTFLine* pNextLine,
798                                 bool bAllChars) {
799  ASSERT(pCurLine && pNextLine);
800  int32_t iCount = pCurLine->CountChars();
801  if (iCount < 2)
802    return;
803
804  int32_t iEndPos = pCurLine->GetLineEnd();
805  std::vector<CFX_RTFChar>& curChars = pCurLine->m_LineChars;
806  int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
807  if (iCharPos < 0)
808    iCharPos = 0;
809
810  iCharPos++;
811  if (iCharPos >= iCount) {
812    pNextLine->RemoveAll(true);
813    CFX_Char* pTC = &curChars[iCharPos - 1];
814    pTC->m_nBreakType = FX_LBT_UNKNOWN;
815    return;
816  }
817
818  pNextLine->m_LineChars =
819      std::vector<CFX_RTFChar>(curChars.begin() + iCharPos, curChars.end());
820  curChars.erase(curChars.begin() + iCharPos, curChars.end());
821  pNextLine->m_iStart = pCurLine->m_iStart;
822  pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
823  pCurLine->m_iWidth = iEndPos;
824  curChars[iCharPos - 1].m_nBreakType = FX_LBT_UNKNOWN;
825
826  for (size_t i = 0; i < pNextLine->m_LineChars.size(); i++) {
827    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
828      pCurLine->m_iArabicChars--;
829      pNextLine->m_iArabicChars++;
830    }
831    pNextLine->m_LineChars[i].m_dwStatus = CFX_RTFBreakType::None;
832  }
833}
834
835int32_t CFX_RTFBreak::CountBreakPieces() const {
836  const CFX_RTFPieceArray* pRTFPieces = GetRTFPieces();
837  return pRTFPieces ? pRTFPieces->GetSize() : 0;
838}
839
840const CFX_RTFPiece* CFX_RTFBreak::GetBreakPiece(int32_t index) const {
841  const CFX_RTFPieceArray* pRTFPieces = GetRTFPieces();
842  if (!pRTFPieces)
843    return nullptr;
844  if (index < 0 || index >= pRTFPieces->GetSize())
845    return nullptr;
846  return pRTFPieces->GetPtrAt(index);
847}
848
849void CFX_RTFBreak::ClearBreakPieces() {
850  const CFX_RTFLine* pRTFLine = GetRTFLine();
851  if (pRTFLine)
852    const_cast<CFX_RTFLine*>(pRTFLine)->RemoveAll(true);
853  m_iReady = 0;
854}
855
856void CFX_RTFBreak::Reset() {
857  m_eCharType = FX_CHARTYPE_Unknown;
858  m_RTFLine1.RemoveAll(true);
859  m_RTFLine2.RemoveAll(true);
860}
861
862int32_t CFX_RTFBreak::GetDisplayPos(const FX_RTFTEXTOBJ* pText,
863                                    FXTEXT_CHARPOS* pCharPos,
864                                    bool bCharCode) const {
865  if (!pText || pText->iLength < 1)
866    return 0;
867
868  ASSERT(pText->pFont && pText->pRect);
869
870  CFX_RetainPtr<CFGAS_GEFont> pFont = pText->pFont;
871  CFX_RectF rtText(*pText->pRect);
872  bool bRTLPiece = FX_IsOdd(pText->iBidiLevel);
873  FX_FLOAT fFontSize = pText->fFontSize;
874  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
875  int32_t iAscent = pFont->GetAscent();
876  int32_t iDescent = pFont->GetDescent();
877  int32_t iMaxHeight = iAscent - iDescent;
878  FX_FLOAT fFontHeight = fFontSize;
879  FX_FLOAT fAscent = fFontHeight * static_cast<FX_FLOAT>(iAscent) /
880                     static_cast<FX_FLOAT>(iMaxHeight);
881  FX_WCHAR wch;
882  FX_WCHAR wPrev = 0xFEFF;
883  FX_WCHAR wNext;
884  FX_WCHAR wForm;
885  int32_t iWidth;
886  int32_t iCharWidth;
887  int32_t iCharHeight;
888  FX_FLOAT fX = rtText.left;
889  FX_FLOAT fY = rtText.top;
890  FX_FLOAT fCharWidth;
891  FX_FLOAT fCharHeight;
892  int32_t iHorScale = pText->iHorizontalScale;
893  int32_t iVerScale = pText->iVerticalScale;
894  bool bEmptyChar;
895  uint32_t dwProps;
896  uint32_t dwCharType;
897
898  if (bRTLPiece)
899    fX = rtText.right();
900
901  fY += fAscent;
902  int32_t iCount = 0;
903  for (int32_t i = 0; i < pText->iLength; i++) {
904    wch = pText->pStr[i];
905    iWidth = pText->pWidths[i];
906    dwProps = FX_GetUnicodeProperties(wch);
907    dwCharType = (dwProps & FX_CHARTYPEBITSMASK);
908    if (iWidth == 0) {
909      if (dwCharType == FX_CHARTYPE_ArabicAlef)
910        wPrev = 0xFEFF;
911      continue;
912    }
913
914    iCharWidth = FXSYS_abs(iWidth);
915    bEmptyChar =
916        (dwCharType >= FX_CHARTYPE_Tab && dwCharType <= FX_CHARTYPE_Control);
917    if (!bEmptyChar)
918      iCount++;
919
920    if (pCharPos) {
921      iCharWidth /= iFontSize;
922      wForm = wch;
923      if (dwCharType >= FX_CHARTYPE_ArabicAlef) {
924        if (i + 1 < pText->iLength) {
925          wNext = pText->pStr[i + 1];
926          if (pText->pWidths[i + 1] < 0 && i + 2 < pText->iLength)
927            wNext = pText->pStr[i + 2];
928        } else {
929          wNext = 0xFEFF;
930        }
931        wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
932      } else if (bRTLPiece) {
933        wForm = FX_GetMirrorChar(wch, dwProps, bRTLPiece, false);
934      }
935      dwProps = FX_GetUnicodeProperties(wForm);
936
937      if (!bEmptyChar) {
938        if (bCharCode) {
939          pCharPos->m_GlyphIndex = wch;
940        } else {
941          pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm, false);
942          if (pCharPos->m_GlyphIndex == 0xFFFF)
943            pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wch, false);
944        }
945#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
946        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
947#endif
948        pCharPos->m_FontCharWidth = iCharWidth;
949      }
950      iCharHeight = 1000;
951
952      fCharWidth = fFontSize * iCharWidth / 1000.0f;
953      fCharHeight = fFontSize * iCharHeight / 1000.0f;
954      if (bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
955        fX -= fCharWidth;
956
957      if (!bEmptyChar)
958        pCharPos->m_Origin = CFX_PointF(fX, fY);
959      if (!bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
960        fX += fCharWidth;
961
962      if (!bEmptyChar) {
963        pCharPos->m_bGlyphAdjust = true;
964        pCharPos->m_AdjustMatrix[0] = -1;
965        pCharPos->m_AdjustMatrix[1] = 0;
966        pCharPos->m_AdjustMatrix[2] = 0;
967        pCharPos->m_AdjustMatrix[3] = 1;
968        pCharPos->m_Origin.y += fAscent * iVerScale / 100.0f;
969        pCharPos->m_Origin.y -= fAscent;
970
971        if (iHorScale != 100 || iVerScale != 100) {
972          pCharPos->m_AdjustMatrix[0] =
973              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
974          pCharPos->m_AdjustMatrix[1] =
975              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
976          pCharPos->m_AdjustMatrix[2] =
977              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
978          pCharPos->m_AdjustMatrix[3] =
979              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
980        }
981        pCharPos++;
982      }
983    }
984    if (iWidth > 0)
985      wPrev = wch;
986  }
987  return iCount;
988}
989
990CFX_RTFPiece::CFX_RTFPiece()
991    : m_dwStatus(CFX_RTFBreakType::Piece),
992      m_iStartPos(0),
993      m_iWidth(-1),
994      m_iStartChar(0),
995      m_iChars(0),
996      m_iBidiLevel(0),
997      m_iBidiPos(0),
998      m_iFontSize(0),
999      m_iFontHeight(0),
1000      m_iHorizontalScale(100),
1001      m_iVerticalScale(100),
1002      m_dwIdentity(0),
1003      m_pChars(nullptr),
1004      m_pUserData(nullptr) {}
1005
1006CFX_RTFPiece::~CFX_RTFPiece() {
1007  Reset();
1008}
1009
1010CFX_RTFLine::CFX_RTFLine()
1011    : m_LinePieces(16),
1012      m_iStart(0),
1013      m_iWidth(0),
1014      m_iArabicChars(0),
1015      m_iMBCSChars(0) {}
1016
1017CFX_RTFLine::~CFX_RTFLine() {
1018  RemoveAll(false);
1019}
1020
1021FX_RTFTEXTOBJ::FX_RTFTEXTOBJ()
1022    : pFont(nullptr),
1023      pRect(nullptr),
1024      wLineBreakChar(L'\n'),
1025      fFontSize(12.0f),
1026      iLength(0),
1027      iBidiLevel(0),
1028      iHorizontalScale(100),
1029      iVerticalScale(100) {}
1030
1031FX_RTFTEXTOBJ::~FX_RTFTEXTOBJ() {}
1032