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/cfx_rtfbreak.h"
8
9#include <algorithm>
10
11#include "core/fxcrt/fx_arabic.h"
12#include "core/fxcrt/fx_bidi.h"
13#include "core/fxge/cfx_renderdevice.h"
14#include "third_party/base/stl_util.h"
15#include "xfa/fgas/font/cfgas_gefont.h"
16#include "xfa/fgas/layout/cfx_linebreak.h"
17
18CFX_RTFBreak::CFX_RTFBreak(uint32_t dwLayoutStyles)
19    : CFX_Break(dwLayoutStyles),
20      m_bPagination(false),
21      m_iAlignment(CFX_RTFLineAlignment::Left) {
22  SetBreakStatus();
23  m_bPagination = !!(m_dwLayoutStyles & FX_LAYOUTSTYLE_Pagination);
24}
25
26CFX_RTFBreak::~CFX_RTFBreak() {}
27
28void CFX_RTFBreak::SetLineStartPos(float fLinePos) {
29  int32_t iLinePos = FXSYS_round(fLinePos * 20000.0f);
30  iLinePos = std::min(iLinePos, m_iLineWidth);
31  iLinePos = std::max(iLinePos, m_iLineStart);
32  m_pCurLine->m_iStart = iLinePos;
33}
34
35void CFX_RTFBreak::AddPositionedTab(float fTabPos) {
36  int32_t iTabPos =
37      std::min(FXSYS_round(fTabPos * 20000.0f) + m_iLineStart, m_iLineWidth);
38  auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
39                             iTabPos);
40  if (it != m_PositionedTabs.end() && *it == iTabPos)
41    return;
42  m_PositionedTabs.insert(it, iTabPos);
43}
44
45void CFX_RTFBreak::SetUserData(const RetainPtr<CXFA_TextUserData>& pUserData) {
46  if (m_pUserData == pUserData)
47    return;
48
49  SetBreakStatus();
50  m_pUserData = pUserData;
51}
52
53int32_t CFX_RTFBreak::GetLastPositionedTab() const {
54  return m_PositionedTabs.empty() ? m_iLineStart : m_PositionedTabs.back();
55}
56
57bool CFX_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
58  auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
59                             *iTabPos);
60  if (it == m_PositionedTabs.end())
61    return false;
62
63  *iTabPos = *it;
64  return true;
65}
66
67CFX_BreakType CFX_RTFBreak::AppendChar(wchar_t wch) {
68  ASSERT(m_pFont && m_pCurLine);
69
70  uint32_t dwProps = FX_GetUnicodeProperties(wch);
71  FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
72  m_pCurLine->m_LineChars.emplace_back(wch, dwProps, m_iHorizontalScale,
73                                       m_iVerticalScale);
74  CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
75  pCurChar->m_iFontSize = m_iFontSize;
76  pCurChar->m_dwIdentity = m_dwIdentity;
77  pCurChar->m_pUserData = m_pUserData;
78
79  CFX_BreakType dwRet1 = CFX_BreakType::None;
80  if (chartype != FX_CHARTYPE_Combination &&
81      GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
82      m_eCharType != FX_CHARTYPE_Unknown &&
83      m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance &&
84      (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
85    dwRet1 = EndBreak(CFX_BreakType::Line);
86    int32_t iCount = m_pCurLine->CountChars();
87    if (iCount > 0)
88      pCurChar = &m_pCurLine->m_LineChars[iCount - 1];
89  }
90
91  CFX_BreakType dwRet2 = CFX_BreakType::None;
92  switch (chartype) {
93    case FX_CHARTYPE_Tab:
94      AppendChar_Tab(pCurChar);
95      break;
96    case FX_CHARTYPE_Control:
97      dwRet2 = AppendChar_Control(pCurChar);
98      break;
99    case FX_CHARTYPE_Combination:
100      AppendChar_Combination(pCurChar);
101      break;
102    case FX_CHARTYPE_ArabicAlef:
103    case FX_CHARTYPE_ArabicSpecial:
104    case FX_CHARTYPE_ArabicDistortion:
105    case FX_CHARTYPE_ArabicNormal:
106    case FX_CHARTYPE_ArabicForm:
107    case FX_CHARTYPE_Arabic:
108      dwRet2 = AppendChar_Arabic(pCurChar);
109      break;
110    case FX_CHARTYPE_Unknown:
111    case FX_CHARTYPE_Space:
112    case FX_CHARTYPE_Numeric:
113    case FX_CHARTYPE_Normal:
114    default:
115      dwRet2 = AppendChar_Others(pCurChar);
116      break;
117  }
118
119  m_eCharType = chartype;
120  return std::max(dwRet1, dwRet2);
121}
122
123void CFX_RTFBreak::AppendChar_Combination(CFX_Char* pCurChar) {
124  int32_t iCharWidth = 0;
125  if (!m_pFont->GetCharWidth(pCurChar->char_code(), iCharWidth))
126    iCharWidth = 0;
127
128  iCharWidth *= m_iFontSize;
129  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
130  CFX_Char* pLastChar = GetLastChar(0, false, true);
131  if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE_Combination)
132    iCharWidth = -iCharWidth;
133  else
134    m_eCharType = FX_CHARTYPE_Combination;
135
136  pCurChar->m_iCharWidth = iCharWidth;
137  if (iCharWidth > 0)
138    m_pCurLine->m_iWidth += iCharWidth;
139}
140
141void CFX_RTFBreak::AppendChar_Tab(CFX_Char* pCurChar) {
142  if (!(m_dwLayoutStyles & FX_LAYOUTSTYLE_ExpandTab))
143    return;
144
145  int32_t& iLineWidth = m_pCurLine->m_iWidth;
146  int32_t iCharWidth = iLineWidth;
147  if (GetPositionedTab(&iCharWidth))
148    iCharWidth -= iLineWidth;
149  else
150    iCharWidth = m_iTabWidth * (iLineWidth / m_iTabWidth + 1) - iLineWidth;
151
152  pCurChar->m_iCharWidth = iCharWidth;
153  iLineWidth += iCharWidth;
154}
155
156CFX_BreakType CFX_RTFBreak::AppendChar_Control(CFX_Char* pCurChar) {
157  CFX_BreakType dwRet2 = CFX_BreakType::None;
158  switch (pCurChar->char_code()) {
159    case L'\v':
160    case 0x2028:
161      dwRet2 = CFX_BreakType::Line;
162      break;
163    case L'\f':
164      dwRet2 = CFX_BreakType::Page;
165      break;
166    case 0x2029:
167      dwRet2 = CFX_BreakType::Paragraph;
168      break;
169    default:
170      if (pCurChar->char_code() == m_wParagraphBreakChar)
171        dwRet2 = CFX_BreakType::Paragraph;
172      break;
173  }
174  if (dwRet2 != CFX_BreakType::None)
175    dwRet2 = EndBreak(dwRet2);
176
177  return dwRet2;
178}
179
180CFX_BreakType CFX_RTFBreak::AppendChar_Arabic(CFX_Char* pCurChar) {
181  CFX_Char* pLastChar = nullptr;
182  int32_t iCharWidth = 0;
183  wchar_t wForm;
184  bool bAlef = false;
185  if (m_eCharType >= FX_CHARTYPE_ArabicAlef &&
186      m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
187    pLastChar = GetLastChar(1, false, true);
188    if (pLastChar) {
189      m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
190      CFX_Char* pPrevChar = GetLastChar(2, false, true);
191      wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
192      bAlef = (wForm == 0xFEFF &&
193               pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
194      if (!m_pFont->GetCharWidth(wForm, iCharWidth) &&
195          !m_pFont->GetCharWidth(pLastChar->char_code(), iCharWidth)) {
196        iCharWidth = m_iDefChar;
197      }
198
199      iCharWidth *= m_iFontSize;
200      iCharWidth = iCharWidth * m_iHorizontalScale / 100;
201      pLastChar->m_iCharWidth = iCharWidth;
202      m_pCurLine->m_iWidth += iCharWidth;
203      iCharWidth = 0;
204    }
205  }
206
207  wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
208                                      nullptr);
209  if (!m_pFont->GetCharWidth(wForm, iCharWidth) &&
210      !m_pFont->GetCharWidth(pCurChar->char_code(), iCharWidth)) {
211    iCharWidth = m_iDefChar;
212  }
213
214  iCharWidth *= m_iFontSize;
215  iCharWidth = iCharWidth * m_iHorizontalScale / 100;
216  pCurChar->m_iCharWidth = iCharWidth;
217  m_pCurLine->m_iWidth += iCharWidth;
218  m_pCurLine->m_iArabicChars++;
219
220  if (m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance)
221    return EndBreak(CFX_BreakType::Line);
222  return CFX_BreakType::None;
223}
224
225CFX_BreakType CFX_RTFBreak::AppendChar_Others(CFX_Char* pCurChar) {
226  FX_CHARTYPE chartype = pCurChar->GetCharType();
227  wchar_t wForm = pCurChar->char_code();
228  int32_t iCharWidth = 0;
229  if (!m_pFont->GetCharWidth(wForm, iCharWidth))
230    iCharWidth = m_iDefChar;
231
232  iCharWidth *= m_iFontSize;
233  iCharWidth *= m_iHorizontalScale / 100;
234  iCharWidth += m_iCharSpace;
235
236  pCurChar->m_iCharWidth = iCharWidth;
237  m_pCurLine->m_iWidth += iCharWidth;
238  if (chartype != FX_CHARTYPE_Space &&
239      m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance) {
240    return EndBreak(CFX_BreakType::Line);
241  }
242  return CFX_BreakType::None;
243}
244
245CFX_BreakType CFX_RTFBreak::EndBreak(CFX_BreakType dwStatus) {
246  ASSERT(dwStatus != CFX_BreakType::None);
247
248  ++m_dwIdentity;
249  if (!m_pCurLine->m_LinePieces.empty()) {
250    if (dwStatus != CFX_BreakType::Piece)
251      m_pCurLine->m_LinePieces.back().m_dwStatus = dwStatus;
252    return m_pCurLine->m_LinePieces.back().m_dwStatus;
253  }
254
255  if (HasLine()) {
256    if (!m_Line[m_iReadyLineIndex].m_LinePieces.empty()) {
257      if (dwStatus != CFX_BreakType::Piece)
258        m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
259      return m_Line[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
260    }
261    return CFX_BreakType::None;
262  }
263
264  int32_t iCount = m_pCurLine->CountChars();
265  if (iCount < 1)
266    return CFX_BreakType::None;
267
268  CFX_Char* tc = m_pCurLine->GetChar(iCount - 1);
269  tc->m_dwStatus = dwStatus;
270  if (dwStatus == CFX_BreakType::Piece)
271    return dwStatus;
272
273  m_iReadyLineIndex = m_pCurLine == &m_Line[0] ? 0 : 1;
274  CFX_BreakLine* pNextLine = &m_Line[1 - m_iReadyLineIndex];
275  bool bAllChars = m_iAlignment == CFX_RTFLineAlignment::Justified ||
276                   m_iAlignment == CFX_RTFLineAlignment::Distributed;
277
278  if (!EndBreak_SplitLine(pNextLine, bAllChars, dwStatus)) {
279    std::deque<FX_TPO> tpos;
280    EndBreak_BidiLine(&tpos, dwStatus);
281    if (!m_bPagination && m_iAlignment != CFX_RTFLineAlignment::Left)
282      EndBreak_Alignment(tpos, bAllChars, dwStatus);
283  }
284  m_pCurLine = pNextLine;
285  m_pCurLine->m_iStart = m_iLineStart;
286
287  CFX_Char* pTC = GetLastChar(0, false, true);
288  m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
289  return dwStatus;
290}
291
292bool CFX_RTFBreak::EndBreak_SplitLine(CFX_BreakLine* pNextLine,
293                                      bool bAllChars,
294                                      CFX_BreakType dwStatus) {
295  bool bDone = false;
296  if (m_pCurLine->GetLineEnd() > m_iLineWidth + m_iTolerance) {
297    const CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->CountChars() - 1);
298    switch (tc->GetCharType()) {
299      case FX_CHARTYPE_Tab:
300      case FX_CHARTYPE_Control:
301      case FX_CHARTYPE_Space:
302        break;
303      default:
304        SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
305        bDone = true;
306        break;
307    }
308  }
309
310  if (!m_bPagination) {
311    if (bAllChars && !bDone) {
312      int32_t endPos = m_pCurLine->GetLineEnd();
313      GetBreakPos(m_pCurLine->m_LineChars, endPos, bAllChars, true);
314    }
315    return false;
316  }
317
318  const CFX_Char* pCurChars = m_pCurLine->m_LineChars.data();
319  CFX_BreakPiece tp;
320  tp.m_pChars = &m_pCurLine->m_LineChars;
321  bool bNew = true;
322  uint32_t dwIdentity = static_cast<uint32_t>(-1);
323  int32_t iLast = m_pCurLine->CountChars() - 1;
324  int32_t j = 0;
325  for (int32_t i = 0; i <= iLast;) {
326    const CFX_Char* pTC = pCurChars + i;
327    if (bNew) {
328      tp.m_iStartChar = i;
329      tp.m_iStartPos += tp.m_iWidth;
330      tp.m_iWidth = 0;
331      tp.m_dwStatus = pTC->m_dwStatus;
332      tp.m_iFontSize = pTC->m_iFontSize;
333      tp.m_iHorizontalScale = pTC->horizonal_scale();
334      tp.m_iVerticalScale = pTC->vertical_scale();
335      dwIdentity = pTC->m_dwIdentity;
336      tp.m_dwIdentity = dwIdentity;
337      tp.m_pUserData = pTC->m_pUserData.As<CXFA_TextUserData>();
338      j = i;
339      bNew = false;
340    }
341
342    if (i == iLast || pTC->m_dwStatus != CFX_BreakType::None ||
343        pTC->m_dwIdentity != dwIdentity) {
344      tp.m_iChars = i - j;
345      if (pTC->m_dwIdentity == dwIdentity) {
346        tp.m_dwStatus = pTC->m_dwStatus;
347        tp.m_iWidth += pTC->m_iCharWidth;
348        tp.m_iChars += 1;
349        ++i;
350      }
351      m_pCurLine->m_LinePieces.push_back(tp);
352      bNew = true;
353    } else {
354      tp.m_iWidth += pTC->m_iCharWidth;
355      ++i;
356    }
357  }
358  return true;
359}
360
361void CFX_RTFBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
362                                     CFX_BreakType dwStatus) {
363  CFX_Char* pTC;
364  std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
365  int32_t iCount = m_pCurLine->CountChars();
366  if (!m_bPagination && m_pCurLine->m_iArabicChars > 0) {
367    ASSERT(iCount >= 0);
368
369    size_t iBidiNum = 0;
370    for (size_t i = 0; i < static_cast<size_t>(iCount); ++i) {
371      pTC = &chars[i];
372      pTC->m_iBidiPos = static_cast<int32_t>(i);
373      if (pTC->GetCharType() != FX_CHARTYPE_Control)
374        iBidiNum = i;
375      if (i == 0)
376        pTC->m_iBidiLevel = 1;
377    }
378    FX_BidiLine(&chars, iBidiNum + 1);
379  } else {
380    for (int32_t i = 0; i < iCount; ++i) {
381      pTC = &chars[i];
382      pTC->m_iBidiLevel = 0;
383      pTC->m_iBidiPos = 0;
384      pTC->m_iBidiOrder = 0;
385    }
386  }
387
388  CFX_BreakPiece tp;
389  tp.m_dwStatus = CFX_BreakType::Piece;
390  tp.m_iStartPos = m_pCurLine->m_iStart;
391  tp.m_pChars = &chars;
392
393  int32_t iBidiLevel = -1;
394  int32_t iCharWidth;
395  FX_TPO tpo;
396  uint32_t dwIdentity = static_cast<uint32_t>(-1);
397  int32_t i = 0;
398  int32_t j = 0;
399  while (i < iCount) {
400    pTC = &chars[i];
401    if (iBidiLevel < 0) {
402      iBidiLevel = pTC->m_iBidiLevel;
403      iCharWidth = pTC->m_iCharWidth;
404      tp.m_iWidth = iCharWidth < 1 ? 0 : iCharWidth;
405      tp.m_iBidiLevel = iBidiLevel;
406      tp.m_iBidiPos = pTC->m_iBidiOrder;
407      tp.m_iFontSize = pTC->m_iFontSize;
408      tp.m_iHorizontalScale = pTC->horizonal_scale();
409      tp.m_iVerticalScale = pTC->vertical_scale();
410      dwIdentity = pTC->m_dwIdentity;
411      tp.m_dwIdentity = dwIdentity;
412      tp.m_pUserData = pTC->m_pUserData.As<CXFA_TextUserData>();
413      tp.m_dwStatus = CFX_BreakType::Piece;
414      ++i;
415    } else if (iBidiLevel != pTC->m_iBidiLevel ||
416               pTC->m_dwIdentity != dwIdentity) {
417      tp.m_iChars = i - tp.m_iStartChar;
418      m_pCurLine->m_LinePieces.push_back(tp);
419
420      tp.m_iStartPos += tp.m_iWidth;
421      tp.m_iStartChar = i;
422      tpo.index = j++;
423      tpo.pos = tp.m_iBidiPos;
424      tpos->push_back(tpo);
425      iBidiLevel = -1;
426    } else {
427      iCharWidth = pTC->m_iCharWidth;
428      if (iCharWidth > 0)
429        tp.m_iWidth += iCharWidth;
430      ++i;
431    }
432  }
433
434  if (i > tp.m_iStartChar) {
435    tp.m_dwStatus = dwStatus;
436    tp.m_iChars = i - tp.m_iStartChar;
437    m_pCurLine->m_LinePieces.push_back(tp);
438
439    tpo.index = j;
440    tpo.pos = tp.m_iBidiPos;
441    tpos->push_back(tpo);
442  }
443
444  std::sort(tpos->begin(), tpos->end());
445  int32_t iStartPos = m_pCurLine->m_iStart;
446  for (const auto& it : *tpos) {
447    CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it.index];
448    ttp.m_iStartPos = iStartPos;
449    iStartPos += ttp.m_iWidth;
450  }
451}
452
453void CFX_RTFBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
454                                      bool bAllChars,
455                                      CFX_BreakType dwStatus) {
456  int32_t iNetWidth = m_pCurLine->m_iWidth;
457  int32_t iGapChars = 0;
458  bool bFind = false;
459  for (auto it = tpos.rbegin(); it != tpos.rend(); it++) {
460    CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it->index];
461    if (!bFind)
462      iNetWidth = ttp.GetEndPos();
463
464    bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
465    int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
466    while (j > -1 && j < ttp.m_iChars) {
467      const CFX_Char* tc = ttp.GetChar(j);
468      if (tc->m_nBreakType == FX_LBT_DIRECT_BRK)
469        ++iGapChars;
470
471      if (!bFind || !bAllChars) {
472        uint32_t dwCharType = tc->GetCharType();
473        if (dwCharType == FX_CHARTYPE_Space ||
474            dwCharType == FX_CHARTYPE_Control) {
475          if (!bFind) {
476            int32_t iCharWidth = tc->m_iCharWidth;
477            if (bAllChars && iCharWidth > 0)
478              iNetWidth -= iCharWidth;
479          }
480        } else {
481          bFind = true;
482          if (!bAllChars)
483            break;
484        }
485      }
486      j += bArabic ? 1 : -1;
487    }
488    if (!bAllChars && bFind)
489      break;
490  }
491
492  int32_t iOffset = m_iLineWidth - iNetWidth;
493  if (iGapChars > 0 && (m_iAlignment == CFX_RTFLineAlignment::Distributed ||
494                        (m_iAlignment == CFX_RTFLineAlignment::Justified &&
495                         dwStatus != CFX_BreakType::Paragraph))) {
496    int32_t iStart = -1;
497    for (const auto& tpo : tpos) {
498      CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
499      if (iStart < 0)
500        iStart = ttp.m_iStartPos;
501      else
502        ttp.m_iStartPos = iStart;
503
504      for (int32_t j = 0; j < ttp.m_iChars; ++j) {
505        CFX_Char* tc = ttp.GetChar(j);
506        if (tc->m_nBreakType != FX_LBT_DIRECT_BRK || tc->m_iCharWidth < 0)
507          continue;
508
509        int32_t k = iOffset / iGapChars;
510        tc->m_iCharWidth += k;
511        ttp.m_iWidth += k;
512        iOffset -= k;
513        --iGapChars;
514        if (iGapChars < 1)
515          break;
516      }
517      iStart += ttp.m_iWidth;
518    }
519  } else if (m_iAlignment == CFX_RTFLineAlignment::Right ||
520             m_iAlignment == CFX_RTFLineAlignment::Center) {
521    if (m_iAlignment == CFX_RTFLineAlignment::Center)
522      iOffset /= 2;
523    if (iOffset > 0) {
524      for (auto& ttp : m_pCurLine->m_LinePieces)
525        ttp.m_iStartPos += iOffset;
526    }
527  }
528}
529
530int32_t CFX_RTFBreak::GetBreakPos(std::vector<CFX_Char>& tca,
531                                  int32_t& iEndPos,
532                                  bool bAllChars,
533                                  bool bOnlyBrk) {
534  int32_t iLength = pdfium::CollectionSize<int32_t>(tca) - 1;
535  if (iLength < 1)
536    return iLength;
537
538  int32_t iBreak = -1;
539  int32_t iBreakPos = -1;
540  int32_t iIndirect = -1;
541  int32_t iIndirectPos = -1;
542  int32_t iLast = -1;
543  int32_t iLastPos = -1;
544  if (iEndPos <= m_iLineWidth) {
545    if (!bAllChars)
546      return iLength;
547
548    iBreak = iLength;
549    iBreakPos = iEndPos;
550  }
551
552  CFX_Char* pCharArray = tca.data();
553  CFX_Char* pCur = pCharArray + iLength;
554  --iLength;
555  if (bAllChars)
556    pCur->m_nBreakType = FX_LBT_UNKNOWN;
557
558  uint32_t nCodeProp = pCur->char_props();
559  uint32_t nNext = nCodeProp & 0x003F;
560  int32_t iCharWidth = pCur->m_iCharWidth;
561  if (iCharWidth > 0)
562    iEndPos -= iCharWidth;
563
564  while (iLength >= 0) {
565    pCur = pCharArray + iLength;
566    nCodeProp = pCur->char_props();
567    uint32_t nCur = nCodeProp & 0x003F;
568    bool bNeedBreak = false;
569    FX_LINEBREAKTYPE eType;
570    if (nCur == kBreakPropertyTB) {
571      bNeedBreak = true;
572      eType = nNext == kBreakPropertyTB
573                  ? FX_LBT_PROHIBITED_BRK
574                  : gs_FX_LineBreak_PairTable[nCur][nNext];
575    } else {
576      if (nCur == kBreakPropertySpace)
577        bNeedBreak = true;
578
579      eType = nNext == kBreakPropertySpace
580                  ? FX_LBT_PROHIBITED_BRK
581                  : gs_FX_LineBreak_PairTable[nCur][nNext];
582    }
583    if (bAllChars)
584      pCur->m_nBreakType = eType;
585
586    if (!bOnlyBrk) {
587      iCharWidth = pCur->m_iCharWidth;
588      if (iEndPos <= m_iLineWidth || bNeedBreak) {
589        if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
590          iBreak = iLength;
591          iBreakPos = iEndPos;
592          if (!bAllChars)
593            return iLength;
594        } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
595          iIndirect = iLength;
596          iIndirectPos = iEndPos;
597        }
598        if (iLast < 0) {
599          iLast = iLength;
600          iLastPos = iEndPos;
601        }
602      }
603      if (iCharWidth > 0)
604        iEndPos -= iCharWidth;
605    }
606    nNext = nCodeProp & 0x003F;
607    --iLength;
608  }
609  if (bOnlyBrk)
610    return 0;
611
612  if (iBreak > -1) {
613    iEndPos = iBreakPos;
614    return iBreak;
615  }
616  if (iIndirect > -1) {
617    iEndPos = iIndirectPos;
618    return iIndirect;
619  }
620  if (iLast > -1) {
621    iEndPos = iLastPos;
622    return iLast;
623  }
624  return 0;
625}
626
627void CFX_RTFBreak::SplitTextLine(CFX_BreakLine* pCurLine,
628                                 CFX_BreakLine* pNextLine,
629                                 bool bAllChars) {
630  ASSERT(pCurLine && pNextLine);
631  int32_t iCount = pCurLine->CountChars();
632  if (iCount < 2)
633    return;
634
635  int32_t iEndPos = pCurLine->GetLineEnd();
636  std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
637  int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
638  if (iCharPos < 0)
639    iCharPos = 0;
640
641  ++iCharPos;
642  if (iCharPos >= iCount) {
643    pNextLine->Clear();
644    curChars[iCharPos - 1].m_nBreakType = FX_LBT_UNKNOWN;
645    return;
646  }
647
648  pNextLine->m_LineChars =
649      std::vector<CFX_Char>(curChars.begin() + iCharPos, curChars.end());
650  curChars.erase(curChars.begin() + iCharPos, curChars.end());
651  pNextLine->m_iStart = pCurLine->m_iStart;
652  pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
653  pCurLine->m_iWidth = iEndPos;
654  curChars[iCharPos - 1].m_nBreakType = FX_LBT_UNKNOWN;
655
656  for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
657    if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
658      pCurLine->m_iArabicChars--;
659      pNextLine->m_iArabicChars++;
660    }
661    pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
662  }
663}
664
665int32_t CFX_RTFBreak::GetDisplayPos(const FX_RTFTEXTOBJ* pText,
666                                    FXTEXT_CHARPOS* pCharPos,
667                                    bool bCharCode) const {
668  if (!pText || pText->iLength < 1)
669    return 0;
670
671  ASSERT(pText->pFont && pText->pRect);
672
673  RetainPtr<CFGAS_GEFont> pFont = pText->pFont;
674  CFX_RectF rtText(*pText->pRect);
675  bool bRTLPiece = FX_IsOdd(pText->iBidiLevel);
676  float fFontSize = pText->fFontSize;
677  int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
678  int32_t iAscent = pFont->GetAscent();
679  int32_t iDescent = pFont->GetDescent();
680  int32_t iMaxHeight = iAscent - iDescent;
681  float fFontHeight = fFontSize;
682  float fAscent = fFontHeight * static_cast<float>(iAscent) /
683                  static_cast<float>(iMaxHeight);
684  wchar_t wPrev = 0xFEFF;
685  wchar_t wNext;
686  float fX = rtText.left;
687  int32_t iHorScale = pText->iHorizontalScale;
688  int32_t iVerScale = pText->iVerticalScale;
689  if (bRTLPiece)
690    fX = rtText.right();
691
692  float fY = rtText.top + fAscent;
693  int32_t iCount = 0;
694  for (int32_t i = 0; i < pText->iLength; ++i) {
695    wchar_t wch = pText->pStr[i];
696    int32_t iWidth = pText->pWidths[i];
697    uint32_t dwProps = FX_GetUnicodeProperties(wch);
698    uint32_t dwCharType = (dwProps & FX_CHARTYPEBITSMASK);
699    if (iWidth == 0) {
700      if (dwCharType == FX_CHARTYPE_ArabicAlef)
701        wPrev = 0xFEFF;
702      continue;
703    }
704
705    int32_t iCharWidth = abs(iWidth);
706    bool bEmptyChar =
707        (dwCharType >= FX_CHARTYPE_Tab && dwCharType <= FX_CHARTYPE_Control);
708    if (!bEmptyChar)
709      ++iCount;
710
711    if (pCharPos) {
712      iCharWidth /= iFontSize;
713      wchar_t wForm = wch;
714      if (dwCharType >= FX_CHARTYPE_ArabicAlef) {
715        if (i + 1 < pText->iLength) {
716          wNext = pText->pStr[i + 1];
717          if (pText->pWidths[i + 1] < 0 && i + 2 < pText->iLength)
718            wNext = pText->pStr[i + 2];
719        } else {
720          wNext = 0xFEFF;
721        }
722        wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
723      } else if (bRTLPiece) {
724        wForm = FX_GetMirrorChar(wch, dwProps);
725      }
726      dwProps = FX_GetUnicodeProperties(wForm);
727
728      if (!bEmptyChar) {
729        if (bCharCode) {
730          pCharPos->m_GlyphIndex = wch;
731        } else {
732          pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
733          if (pCharPos->m_GlyphIndex == 0xFFFF)
734            pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wch);
735        }
736#if _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
737        pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
738#endif
739        pCharPos->m_FontCharWidth = iCharWidth;
740      }
741
742      float fCharWidth = fFontSize * iCharWidth / 1000.0f;
743      if (bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
744        fX -= fCharWidth;
745
746      if (!bEmptyChar)
747        pCharPos->m_Origin = CFX_PointF(fX, fY);
748      if (!bRTLPiece && dwCharType != FX_CHARTYPE_Combination)
749        fX += fCharWidth;
750
751      if (!bEmptyChar) {
752        pCharPos->m_bGlyphAdjust = true;
753        pCharPos->m_AdjustMatrix[0] = -1;
754        pCharPos->m_AdjustMatrix[1] = 0;
755        pCharPos->m_AdjustMatrix[2] = 0;
756        pCharPos->m_AdjustMatrix[3] = 1;
757        pCharPos->m_Origin.y += fAscent * iVerScale / 100.0f;
758        pCharPos->m_Origin.y -= fAscent;
759
760        if (iHorScale != 100 || iVerScale != 100) {
761          pCharPos->m_AdjustMatrix[0] =
762              pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
763          pCharPos->m_AdjustMatrix[1] =
764              pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
765          pCharPos->m_AdjustMatrix[2] =
766              pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
767          pCharPos->m_AdjustMatrix[3] =
768              pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
769        }
770        ++pCharPos;
771      }
772    }
773    if (iWidth > 0)
774      wPrev = wch;
775  }
776  return iCount;
777}
778
779FX_RTFTEXTOBJ::FX_RTFTEXTOBJ()
780    : pFont(nullptr),
781      pRect(nullptr),
782      wLineBreakChar(L'\n'),
783      fFontSize(12.0f),
784      iLength(0),
785      iBidiLevel(0),
786      iHorizontalScale(100),
787      iVerticalScale(100) {}
788
789FX_RTFTEXTOBJ::~FX_RTFTEXTOBJ() {}
790