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