• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/fgas/layout/cfx_txtbreak.h"
8 
9 #include <algorithm>
10 
11 #include "build/build_config.h"
12 #include "core/fxcrt/fx_safe_types.h"
13 #include "core/fxge/text_char_pos.h"
14 #include "third_party/base/stl_util.h"
15 #include "xfa/fgas/font/cfgas_gefont.h"
16 #include "xfa/fgas/layout/cfx_char.h"
17 #include "xfa/fgas/layout/fx_arabic.h"
18 #include "xfa/fgas/layout/fx_linebreak.h"
19 
20 namespace {
21 
IsCtrlCode(wchar_t wch)22 bool IsCtrlCode(wchar_t wch) {
23   FX_CHARTYPE dwRet = FX_GetCharType(wch);
24   return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
25 }
26 
27 }  // namespace
28 
CFX_TxtBreak()29 CFX_TxtBreak::CFX_TxtBreak()
30     : CFX_Break(FX_LAYOUTSTYLE_None),
31       m_iAlignment(CFX_TxtLineAlignment_Left),
32       m_iCombWidth(360000) {}
33 
~CFX_TxtBreak()34 CFX_TxtBreak::~CFX_TxtBreak() {}
35 
SetLineWidth(float fLineWidth)36 void CFX_TxtBreak::SetLineWidth(float fLineWidth) {
37   m_iLineWidth = FXSYS_roundf(fLineWidth * kConversionFactor);
38   ASSERT(m_iLineWidth >= 20000);
39 }
40 
SetAlignment(int32_t iAlignment)41 void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
42   ASSERT(iAlignment >= CFX_TxtLineAlignment_Left);
43   ASSERT(iAlignment <= CFX_TxtLineAlignment_Justified);
44   m_iAlignment = iAlignment;
45 }
46 
SetCombWidth(float fCombWidth)47 void CFX_TxtBreak::SetCombWidth(float fCombWidth) {
48   m_iCombWidth = FXSYS_roundf(fCombWidth * kConversionFactor);
49 }
50 
AppendChar_Combination(CFX_Char * pCurChar)51 void CFX_TxtBreak::AppendChar_Combination(CFX_Char* pCurChar) {
52   wchar_t wch = pCurChar->char_code();
53   wchar_t wForm;
54   FX_SAFE_INT32 iCharWidth = 0;
55   pCurChar->m_iCharWidth = -1;
56   if (m_bCombText) {
57     iCharWidth = m_iCombWidth;
58   } else {
59     wForm = wch;
60     CFX_Char* pLastChar = GetLastChar(0, false, false);
61     if (pLastChar &&
62         (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
63       bool bShadda = false;
64       if (wch == 0x0651) {
65         wchar_t wLast = pLastChar->char_code();
66         if (wLast >= 0x064C && wLast <= 0x0650) {
67           wForm = FX_GetArabicFromShaddaTable(wLast);
68           bShadda = true;
69         }
70       } else if (wch >= 0x064C && wch <= 0x0650) {
71         if (pLastChar->char_code() == 0x0651) {
72           wForm = FX_GetArabicFromShaddaTable(wch);
73           bShadda = true;
74         }
75       }
76       if (bShadda) {
77         pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
78         pLastChar->m_iCharWidth = 0;
79         pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
80       }
81     }
82     int32_t iCharWidthOut;
83     if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
84       iCharWidth = iCharWidthOut;
85     else
86       iCharWidth = 0;
87 
88     iCharWidth *= m_iFontSize;
89     iCharWidth *= m_iHorizontalScale;
90     iCharWidth /= 100;
91   }
92 
93   iCharWidth *= -1;
94   pCurChar->m_iCharWidth = iCharWidth.ValueOrDefault(0);
95 }
96 
AppendChar_Tab(CFX_Char * pCurChar)97 void CFX_TxtBreak::AppendChar_Tab(CFX_Char* pCurChar) {
98   m_eCharType = FX_CHARTYPE::kTab;
99 }
100 
AppendChar_Control(CFX_Char * pCurChar)101 CFX_BreakType CFX_TxtBreak::AppendChar_Control(CFX_Char* pCurChar) {
102   m_eCharType = FX_CHARTYPE::kControl;
103   CFX_BreakType dwRet = CFX_BreakType::None;
104   if (!m_bSingleLine) {
105     wchar_t wch = pCurChar->char_code();
106     switch (wch) {
107       case L'\v':
108       case 0x2028:
109         dwRet = CFX_BreakType::Line;
110         break;
111       case L'\f':
112         dwRet = CFX_BreakType::Page;
113         break;
114       case 0x2029:
115         dwRet = CFX_BreakType::Paragraph;
116         break;
117       default:
118         if (wch == m_wParagraphBreakChar)
119           dwRet = CFX_BreakType::Paragraph;
120         break;
121     }
122     if (dwRet != CFX_BreakType::None)
123       dwRet = EndBreak(dwRet);
124   }
125   return dwRet;
126 }
127 
AppendChar_Arabic(CFX_Char * pCurChar)128 CFX_BreakType CFX_TxtBreak::AppendChar_Arabic(CFX_Char* pCurChar) {
129   FX_CHARTYPE chartype = pCurChar->GetCharType();
130   int32_t& iLineWidth = m_pCurLine->m_iWidth;
131   wchar_t wForm;
132   CFX_Char* pLastChar = nullptr;
133   bool bAlef = false;
134   if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
135       m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
136     FX_SAFE_INT32 iCharWidth = 0;
137     pLastChar = GetLastChar(1, true, false);
138     if (pLastChar) {
139       if (pLastChar->m_iCharWidth > 0)
140         iLineWidth -= pLastChar->m_iCharWidth;
141       iCharWidth = pLastChar->m_iCharWidth;
142 
143       CFX_Char* pPrevChar = GetLastChar(2, true, false);
144       wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
145       bAlef = (wForm == 0xFEFF &&
146                pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
147       if (m_pFont) {
148         int32_t iCharWidthOut = 0;
149         m_pFont->GetCharWidth(wForm, &iCharWidthOut);
150         iCharWidth = iCharWidthOut;
151       }
152       if (wForm == 0xFEFF)
153         iCharWidth = 0;
154 
155       iCharWidth *= m_iFontSize;
156       iCharWidth *= m_iHorizontalScale;
157       iCharWidth /= 100;
158 
159       int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
160       pLastChar->m_iCharWidth = iCharWidthValid;
161       iLineWidth += iCharWidthValid;
162     }
163   }
164 
165   m_eCharType = chartype;
166   wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
167                                       nullptr);
168   FX_SAFE_INT32 iCharWidth = 0;
169   if (m_bCombText) {
170     iCharWidth = m_iCombWidth;
171   } else {
172     if (m_pFont) {
173       int32_t iCharWidthOut = 0;
174       m_pFont->GetCharWidth(wForm, &iCharWidthOut);
175       iCharWidth = iCharWidthOut;
176     }
177     if (wForm == 0xFEFF)
178       iCharWidth = 0;
179 
180     iCharWidth *= m_iFontSize;
181     iCharWidth *= m_iHorizontalScale;
182     iCharWidth /= 100;
183   }
184 
185   int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
186   pCurChar->m_iCharWidth = iCharWidthValid;
187   iLineWidth += iCharWidthValid;
188 
189   m_pCurLine->IncrementArabicCharCount();
190   if (!m_bSingleLine && IsGreaterThanLineWidth(iLineWidth))
191     return EndBreak(CFX_BreakType::Line);
192   return CFX_BreakType::None;
193 }
194 
AppendChar_Others(CFX_Char * pCurChar)195 CFX_BreakType CFX_TxtBreak::AppendChar_Others(CFX_Char* pCurChar) {
196   FX_CHARTYPE chartype = pCurChar->GetCharType();
197   int32_t& iLineWidth = m_pCurLine->m_iWidth;
198   FX_SAFE_INT32 iCharWidth = 0;
199   m_eCharType = chartype;
200   wchar_t wch = pCurChar->char_code();
201   wchar_t wForm = wch;
202 
203   if (m_bCombText) {
204     iCharWidth = m_iCombWidth;
205   } else {
206     int32_t iCharWidthOut;
207     if (m_pFont && m_pFont->GetCharWidth(wForm, &iCharWidthOut))
208       iCharWidth = iCharWidthOut;
209     else
210       iCharWidth = 0;
211 
212     iCharWidth *= m_iFontSize;
213     iCharWidth *= m_iHorizontalScale;
214     iCharWidth /= 100;
215   }
216 
217   iCharWidth += m_iCharSpace;
218 
219   int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
220   pCurChar->m_iCharWidth = iCharWidthValid;
221   iLineWidth += iCharWidthValid;
222   if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
223       IsGreaterThanLineWidth(iLineWidth)) {
224     return EndBreak(CFX_BreakType::Line);
225   }
226 
227   return CFX_BreakType::None;
228 }
229 
AppendChar(wchar_t wch)230 CFX_BreakType CFX_TxtBreak::AppendChar(wchar_t wch) {
231   FX_CHARTYPE chartype = FX_GetCharType(wch);
232   m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
233                                        m_iVerticalScale);
234   CFX_Char* pCurChar = &m_pCurLine->m_LineChars.back();
235   pCurChar->m_dwCharStyles = m_iAlignment | (1 << 8);
236 
237   CFX_BreakType dwRet1 = CFX_BreakType::None;
238   if (chartype != FX_CHARTYPE::kCombination &&
239       GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
240       m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
241       IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
242       (m_eCharType != FX_CHARTYPE::kSpace ||
243        chartype != FX_CHARTYPE::kControl)) {
244     dwRet1 = EndBreak(CFX_BreakType::Line);
245     if (!m_pCurLine->m_LineChars.empty())
246       pCurChar = &m_pCurLine->m_LineChars.back();
247   }
248 
249   CFX_BreakType dwRet2 = CFX_BreakType::None;
250   if (wch == m_wParagraphBreakChar) {
251     // This is handled in AppendChar_Control, but it seems like \n and \r
252     // don't get matched as control characters so we go into AppendChar_other
253     // and never detect the new paragraph ...
254     dwRet2 = CFX_BreakType::Paragraph;
255     EndBreak(dwRet2);
256   } else {
257     switch (chartype) {
258       case FX_CHARTYPE::kTab:
259         AppendChar_Tab(pCurChar);
260         break;
261       case FX_CHARTYPE::kControl:
262         dwRet2 = AppendChar_Control(pCurChar);
263         break;
264       case FX_CHARTYPE::kCombination:
265         AppendChar_Combination(pCurChar);
266         break;
267       case FX_CHARTYPE::kArabicAlef:
268       case FX_CHARTYPE::kArabicSpecial:
269       case FX_CHARTYPE::kArabicDistortion:
270       case FX_CHARTYPE::kArabicNormal:
271       case FX_CHARTYPE::kArabicForm:
272       case FX_CHARTYPE::kArabic:
273         dwRet2 = AppendChar_Arabic(pCurChar);
274         break;
275       case FX_CHARTYPE::kUnknown:
276       case FX_CHARTYPE::kSpace:
277       case FX_CHARTYPE::kNumeric:
278       case FX_CHARTYPE::kNormal:
279       default:
280         dwRet2 = AppendChar_Others(pCurChar);
281         break;
282     }
283   }
284   return std::max(dwRet1, dwRet2);
285 }
286 
EndBreak_SplitLine(CFX_BreakLine * pNextLine,bool bAllChars)287 bool CFX_TxtBreak::EndBreak_SplitLine(CFX_BreakLine* pNextLine,
288                                       bool bAllChars) {
289   bool bDone = false;
290   CFX_Char* pTC;
291   if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
292     pTC = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
293     switch (pTC->GetCharType()) {
294       case FX_CHARTYPE::kTab:
295       case FX_CHARTYPE::kControl:
296       case FX_CHARTYPE::kSpace:
297         break;
298       default:
299         SplitTextLine(m_pCurLine.Get(), pNextLine, bAllChars);
300         bDone = true;
301         break;
302     }
303   }
304 
305   CFX_BreakPiece tp;
306   if (bAllChars && !bDone) {
307     int32_t iEndPos = m_pCurLine->m_iWidth;
308     GetBreakPos(&m_pCurLine->m_LineChars, bAllChars, true, &iEndPos);
309   }
310   return false;
311 }
312 
EndBreak_BidiLine(std::deque<FX_TPO> * tpos,CFX_BreakType dwStatus)313 void CFX_TxtBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
314                                      CFX_BreakType dwStatus) {
315   CFX_BreakPiece tp;
316   FX_TPO tpo;
317   CFX_Char* pTC;
318   std::vector<CFX_Char>& chars = m_pCurLine->m_LineChars;
319   if (!m_pCurLine->HasArabicChar()) {
320     tp.m_dwStatus = dwStatus;
321     tp.m_iStartPos = m_pCurLine->m_iStart;
322     tp.m_iWidth = m_pCurLine->m_iWidth;
323     tp.m_iStartChar = 0;
324     tp.m_iChars = m_pCurLine->m_LineChars.size();
325     tp.m_pChars = &m_pCurLine->m_LineChars;
326     pTC = &chars[0];
327     tp.m_dwCharStyles = pTC->m_dwCharStyles;
328     tp.m_iHorizontalScale = pTC->horizonal_scale();
329     tp.m_iVerticalScale = pTC->vertical_scale();
330     m_pCurLine->m_LinePieces.push_back(tp);
331     tpos->push_back({0, 0});
332     return;
333   }
334 
335   size_t iBidiNum = 0;
336   for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
337     pTC = &chars[i];
338     pTC->m_iBidiPos = static_cast<int32_t>(i);
339     if (pTC->GetCharType() != FX_CHARTYPE::kControl)
340       iBidiNum = i;
341     if (i == 0)
342       pTC->m_iBidiLevel = 1;
343   }
344   CFX_Char::BidiLine(&chars, iBidiNum + 1);
345 
346   tp.m_dwStatus = CFX_BreakType::Piece;
347   tp.m_iStartPos = m_pCurLine->m_iStart;
348   tp.m_pChars = &m_pCurLine->m_LineChars;
349   int32_t iBidiLevel = -1;
350   int32_t iCharWidth;
351   int32_t i = 0;
352   int32_t j = -1;
353   int32_t iCount = pdfium::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
354   while (i < iCount) {
355     pTC = &chars[i];
356     if (iBidiLevel < 0) {
357       iBidiLevel = pTC->m_iBidiLevel;
358       tp.m_iWidth = 0;
359       tp.m_iBidiLevel = iBidiLevel;
360       tp.m_iBidiPos = pTC->m_iBidiOrder;
361       tp.m_dwCharStyles = pTC->m_dwCharStyles;
362       tp.m_iHorizontalScale = pTC->horizonal_scale();
363       tp.m_iVerticalScale = pTC->vertical_scale();
364       tp.m_dwStatus = CFX_BreakType::Piece;
365     }
366     if (iBidiLevel != pTC->m_iBidiLevel ||
367         pTC->m_dwStatus != CFX_BreakType::None) {
368       if (iBidiLevel == pTC->m_iBidiLevel) {
369         tp.m_dwStatus = pTC->m_dwStatus;
370         iCharWidth = pTC->m_iCharWidth;
371         if (iCharWidth > 0)
372           tp.m_iWidth += iCharWidth;
373 
374         i++;
375       }
376       tp.m_iChars = i - tp.m_iStartChar;
377       m_pCurLine->m_LinePieces.push_back(tp);
378       tp.m_iStartPos += tp.m_iWidth;
379       tp.m_iStartChar = i;
380       tpo.index = ++j;
381       tpo.pos = tp.m_iBidiPos;
382       tpos->push_back(tpo);
383       iBidiLevel = -1;
384     } else {
385       iCharWidth = pTC->m_iCharWidth;
386       if (iCharWidth > 0)
387         tp.m_iWidth += iCharWidth;
388 
389       i++;
390     }
391   }
392   if (i > tp.m_iStartChar) {
393     tp.m_dwStatus = dwStatus;
394     tp.m_iChars = i - tp.m_iStartChar;
395     m_pCurLine->m_LinePieces.push_back(tp);
396     tpo.index = ++j;
397     tpo.pos = tp.m_iBidiPos;
398     tpos->push_back(tpo);
399   }
400   if (j > -1) {
401     if (j > 0) {
402       std::sort(tpos->begin(), tpos->end());
403       int32_t iStartPos = 0;
404       for (i = 0; i <= j; i++) {
405         tpo = (*tpos)[i];
406         CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
407         ttp.m_iStartPos = iStartPos;
408         iStartPos += ttp.m_iWidth;
409       }
410     }
411     m_pCurLine->m_LinePieces[j].m_dwStatus = dwStatus;
412   }
413 }
414 
EndBreak_Alignment(const std::deque<FX_TPO> & tpos,bool bAllChars,CFX_BreakType dwStatus)415 void CFX_TxtBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
416                                       bool bAllChars,
417                                       CFX_BreakType dwStatus) {
418   int32_t iNetWidth = m_pCurLine->m_iWidth;
419   int32_t iGapChars = 0;
420   bool bFind = false;
421   for (auto it = tpos.rbegin(); it != tpos.rend(); ++it) {
422     CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[it->index];
423     if (!bFind)
424       iNetWidth = ttp.GetEndPos();
425 
426     bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
427     int32_t j = bArabic ? 0 : ttp.m_iChars - 1;
428     while (j > -1 && j < ttp.m_iChars) {
429       const CFX_Char* pTC = ttp.GetChar(j);
430       if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
431         iGapChars++;
432       if (!bFind || !bAllChars) {
433         FX_CHARTYPE chartype = pTC->GetCharType();
434         if (chartype == FX_CHARTYPE::kSpace ||
435             chartype == FX_CHARTYPE::kControl) {
436           if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
437             iNetWidth -= pTC->m_iCharWidth;
438         } else {
439           bFind = true;
440           if (!bAllChars)
441             break;
442         }
443       }
444       j += bArabic ? 1 : -1;
445     }
446     if (!bAllChars && bFind)
447       break;
448   }
449 
450   int32_t iOffset = m_iLineWidth - iNetWidth;
451   if (iGapChars > 0 && m_iAlignment & CFX_TxtLineAlignment_Justified &&
452       dwStatus != CFX_BreakType::Paragraph) {
453     int32_t iStart = -1;
454     for (auto& tpo : tpos) {
455       CFX_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
456       if (iStart < -1)
457         iStart = ttp.m_iStartPos;
458       else
459         ttp.m_iStartPos = iStart;
460 
461       for (int32_t j = 0; j < ttp.m_iChars && iGapChars > 0; j++, iGapChars--) {
462         CFX_Char* pTC = ttp.GetChar(j);
463         if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
464             pTC->m_iCharWidth < 0) {
465           continue;
466         }
467         int32_t k = iOffset / iGapChars;
468         pTC->m_iCharWidth += k;
469         ttp.m_iWidth += k;
470         iOffset -= k;
471       }
472       iStart += ttp.m_iWidth;
473     }
474   } else if (m_iAlignment & CFX_TxtLineAlignment_Center ||
475              m_iAlignment & CFX_TxtLineAlignment_Right) {
476     if (m_iAlignment & CFX_TxtLineAlignment_Center &&
477         !(m_iAlignment & CFX_TxtLineAlignment_Right)) {
478       iOffset /= 2;
479     }
480     if (iOffset > 0) {
481       for (auto& ttp : m_pCurLine->m_LinePieces)
482         ttp.m_iStartPos += iOffset;
483     }
484   }
485 }
486 
EndBreak(CFX_BreakType dwStatus)487 CFX_BreakType CFX_TxtBreak::EndBreak(CFX_BreakType dwStatus) {
488   ASSERT(dwStatus != CFX_BreakType::None);
489 
490   if (!m_pCurLine->m_LinePieces.empty()) {
491     if (dwStatus != CFX_BreakType::Piece)
492       m_pCurLine->m_LinePieces.back().m_dwStatus = dwStatus;
493     return m_pCurLine->m_LinePieces.back().m_dwStatus;
494   }
495 
496   if (HasLine()) {
497     if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
498       return CFX_BreakType::None;
499 
500     if (dwStatus != CFX_BreakType::Piece)
501       m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus = dwStatus;
502     return m_Lines[m_iReadyLineIndex].m_LinePieces.back().m_dwStatus;
503   }
504 
505   if (m_pCurLine->m_LineChars.empty())
506     return CFX_BreakType::None;
507 
508   m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
509   if (dwStatus == CFX_BreakType::Piece)
510     return dwStatus;
511 
512   m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
513   CFX_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
514   bool bAllChars = m_iAlignment > CFX_TxtLineAlignment_Right;
515   if (!EndBreak_SplitLine(pNextLine, bAllChars)) {
516     std::deque<FX_TPO> tpos;
517     EndBreak_BidiLine(&tpos, dwStatus);
518     if (m_iAlignment > CFX_TxtLineAlignment_Left)
519       EndBreak_Alignment(tpos, bAllChars, dwStatus);
520   }
521 
522   m_pCurLine = pNextLine;
523   CFX_Char* pTC = GetLastChar(0, false, false);
524   m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
525 
526   return dwStatus;
527 }
528 
GetBreakPos(std::vector<CFX_Char> * pChars,bool bAllChars,bool bOnlyBrk,int32_t * pEndPos)529 int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_Char>* pChars,
530                                   bool bAllChars,
531                                   bool bOnlyBrk,
532                                   int32_t* pEndPos) {
533   std::vector<CFX_Char>& chars = *pChars;
534   int32_t iLength = pdfium::CollectionSize<int32_t>(chars) - 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 (m_bSingleLine || *pEndPos <= m_iLineWidth) {
545     if (!bAllChars)
546       return iLength;
547 
548     iBreak = iLength;
549     iBreakPos = *pEndPos;
550   }
551 
552   FX_LINEBREAKTYPE eType;
553   FX_BREAKPROPERTY nCur;
554   FX_BREAKPROPERTY nNext;
555   CFX_Char* pCur = &chars[iLength--];
556   if (bAllChars)
557     pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
558 
559   nNext = FX_GetBreakProperty(pCur->char_code());
560   int32_t iCharWidth = pCur->m_iCharWidth;
561   if (iCharWidth > 0)
562     *pEndPos -= iCharWidth;
563 
564   while (iLength >= 0) {
565     pCur = &chars[iLength];
566     nCur = FX_GetBreakProperty(pCur->char_code());
567     if (nNext == FX_BREAKPROPERTY::kSP)
568       eType = FX_LINEBREAKTYPE::kPROHIBITED_BRK;
569     else
570       eType = GetLineBreakTypeFromPair(nCur, nNext);
571     if (bAllChars)
572       pCur->m_eLineBreakType = eType;
573     if (!bOnlyBrk) {
574       if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
575           nCur == FX_BREAKPROPERTY::kSP) {
576         if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
577           iBreak = iLength;
578           iBreakPos = *pEndPos;
579           if (!bAllChars)
580             return iLength;
581         } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
582           iIndirect = iLength;
583           iIndirectPos = *pEndPos;
584         }
585         if (iLast < 0) {
586           iLast = iLength;
587           iLastPos = *pEndPos;
588         }
589       }
590       iCharWidth = pCur->m_iCharWidth;
591       if (iCharWidth > 0)
592         *pEndPos -= iCharWidth;
593     }
594     nNext = nCur;
595     iLength--;
596   }
597   if (bOnlyBrk)
598     return 0;
599   if (iBreak > -1) {
600     *pEndPos = iBreakPos;
601     return iBreak;
602   }
603   if (iIndirect > -1) {
604     *pEndPos = iIndirectPos;
605     return iIndirect;
606   }
607   if (iLast > -1) {
608     *pEndPos = iLastPos;
609     return iLast;
610   }
611   return 0;
612 }
613 
SplitTextLine(CFX_BreakLine * pCurLine,CFX_BreakLine * pNextLine,bool bAllChars)614 void CFX_TxtBreak::SplitTextLine(CFX_BreakLine* pCurLine,
615                                  CFX_BreakLine* pNextLine,
616                                  bool bAllChars) {
617   ASSERT(pCurLine);
618   ASSERT(pNextLine);
619 
620   if (pCurLine->m_LineChars.size() < 2)
621     return;
622 
623   int32_t iEndPos = pCurLine->m_iWidth;
624   std::vector<CFX_Char>& curChars = pCurLine->m_LineChars;
625   int32_t iCharPos = GetBreakPos(&curChars, bAllChars, false, &iEndPos);
626   if (iCharPos < 0)
627     iCharPos = 0;
628 
629   iCharPos++;
630   if (iCharPos >= pdfium::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
631     pNextLine->Clear();
632     CFX_Char* pTC = &curChars[iCharPos - 1];
633     pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
634     return;
635   }
636 
637   pNextLine->m_LineChars =
638       std::vector<CFX_Char>(curChars.begin() + iCharPos, curChars.end());
639   curChars.erase(curChars.begin() + iCharPos, curChars.end());
640   pCurLine->m_iWidth = iEndPos;
641   CFX_Char* pTC = &curChars[iCharPos - 1];
642   pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
643   int32_t iWidth = 0;
644   for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
645     if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
646       pCurLine->DecrementArabicCharCount();
647       pNextLine->IncrementArabicCharCount();
648     }
649     iWidth += std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
650     pNextLine->m_LineChars[i].m_dwStatus = CFX_BreakType::None;
651   }
652   pNextLine->m_iWidth = iWidth;
653 }
654 
655 struct FX_FORMCHAR {
656   uint16_t wch;
657   uint16_t wForm;
658   int32_t iWidth;
659 };
660 
GetDisplayPos(const Run * pTxtRun,TextCharPos * pCharPos) const661 size_t CFX_TxtBreak::GetDisplayPos(const Run* pTxtRun,
662                                    TextCharPos* pCharPos) const {
663   if (!pTxtRun || pTxtRun->iLength < 1)
664     return 0;
665 
666   Engine* pEngine = pTxtRun->pEdtEngine;
667   const wchar_t* pStr = pTxtRun->wsStr.c_str();
668   int32_t* pWidths = pTxtRun->pWidths;
669   int32_t iLength = pTxtRun->iLength - 1;
670   RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
671   uint32_t dwStyles = pTxtRun->dwStyles;
672   CFX_RectF rtText(*pTxtRun->pRect);
673   bool bRTLPiece = (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
674   float fFontSize = pTxtRun->fFontSize;
675   int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
676   int32_t iAscent = pFont->GetAscent();
677   int32_t iDescent = pFont->GetDescent();
678   int32_t iMaxHeight = iAscent - iDescent;
679   float fFontHeight = fFontSize;
680   float fAscent = fFontHeight * iAscent / iMaxHeight;
681   float fX = rtText.left;
682   float fY;
683   float fCharWidth;
684   int32_t iHorScale = pTxtRun->iHorizontalScale;
685   int32_t iVerScale = pTxtRun->iVerticalScale;
686   bool bSkipSpace = pTxtRun->bSkipSpace;
687   FX_FORMCHAR formChars[3];
688   float fYBase;
689 
690   if (bRTLPiece)
691     fX = rtText.right();
692 
693   fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
694   fY = fYBase + fAscent;
695 
696   size_t szCount = 0;
697   int32_t iNext = 0;
698   wchar_t wPrev = 0xFEFF;
699   wchar_t wNext = 0xFEFF;
700   wchar_t wForm = 0xFEFF;
701   wchar_t wLast = 0xFEFF;
702   bool bShadda = false;
703   bool bLam = false;
704   for (int32_t i = 0; i <= iLength; i++) {
705     int32_t iAbsolute = i + pTxtRun->iStart;
706     int32_t iWidth;
707     wchar_t wch;
708     if (pEngine) {
709       wch = pEngine->GetChar(iAbsolute);
710       iWidth = pEngine->GetWidthOfChar(iAbsolute);
711     } else {
712       wch = *pStr++;
713       iWidth = *pWidths++;
714     }
715 
716     FX_CHARTYPE chartype = FX_GetCharType(wch);
717     if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
718       wPrev = 0xFEFF;
719       wLast = wch;
720       continue;
721     }
722 
723     if (chartype >= FX_CHARTYPE::kArabicAlef) {
724       if (i < iLength) {
725         if (pEngine) {
726           iNext = i + 1;
727           while (iNext <= iLength) {
728             int32_t iNextAbsolute = iNext + pTxtRun->iStart;
729             wNext = pEngine->GetChar(iNextAbsolute);
730             if (FX_GetCharType(wNext) != FX_CHARTYPE::kCombination)
731               break;
732 
733             iNext++;
734           }
735           if (iNext > iLength)
736             wNext = 0xFEFF;
737         } else {
738           int32_t j = -1;
739           do {
740             j++;
741             if (i + j >= iLength)
742               break;
743 
744             wNext = pStr[j];
745           } while (FX_GetCharType(wNext) == FX_CHARTYPE::kCombination);
746           if (i + j >= iLength)
747             wNext = 0xFEFF;
748         }
749       } else {
750         wNext = 0xFEFF;
751       }
752 
753       wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
754       bLam = (wPrev == 0x0644 && wch == 0x0644 && wNext == 0x0647);
755     } else if (chartype == FX_CHARTYPE::kCombination) {
756       wForm = wch;
757       if (wch >= 0x064C && wch <= 0x0651) {
758         if (bShadda) {
759           wForm = 0xFEFF;
760           bShadda = false;
761         } else {
762           wNext = 0xFEFF;
763           if (pEngine) {
764             iNext = i + 1;
765             if (iNext <= iLength) {
766               int32_t iNextAbsolute = iNext + pTxtRun->iStart;
767               wNext = pEngine->GetChar(iNextAbsolute);
768             }
769           } else {
770             if (i < iLength)
771               wNext = *pStr;
772           }
773           if (wch == 0x0651) {
774             if (wNext >= 0x064C && wNext <= 0x0650) {
775               wForm = FX_GetArabicFromShaddaTable(wNext);
776               bShadda = true;
777             }
778           } else {
779             if (wNext == 0x0651) {
780               wForm = FX_GetArabicFromShaddaTable(wch);
781               bShadda = true;
782             }
783           }
784         }
785       } else {
786         bShadda = false;
787       }
788     } else if (chartype == FX_CHARTYPE::kNumeric) {
789       wForm = wch;
790     } else if (wch == L'.') {
791       wForm = wch;
792     } else if (wch == L',') {
793       wForm = wch;
794     } else if (bRTLPiece) {
795       wForm = FX_GetMirrorChar(wch);
796     } else {
797       wForm = wch;
798     }
799     if (chartype != FX_CHARTYPE::kCombination)
800       bShadda = false;
801     if (chartype < FX_CHARTYPE::kArabicAlef)
802       bLam = false;
803 
804     bool bEmptyChar =
805         (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
806     if (wForm == 0xFEFF)
807       bEmptyChar = true;
808 
809     int32_t iForms = bLam ? 3 : 1;
810     szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
811     if (!pCharPos) {
812       if (iWidth > 0)
813         wPrev = wch;
814       wLast = wch;
815       continue;
816     }
817 
818     int32_t iCharWidth = iWidth;
819     if (iCharWidth < 0)
820       iCharWidth = -iCharWidth;
821 
822     iCharWidth /= iFontSize;
823     formChars[0].wch = wch;
824     formChars[0].wForm = wForm;
825     formChars[0].iWidth = iCharWidth;
826     if (bLam) {
827       formChars[1].wForm = 0x0651;
828       iCharWidth = 0;
829       pFont->GetCharWidth(0x0651, &iCharWidth);
830       formChars[1].iWidth = iCharWidth;
831       formChars[2].wForm = 0x0670;
832       iCharWidth = 0;
833       pFont->GetCharWidth(0x0670, &iCharWidth);
834       formChars[2].iWidth = iCharWidth;
835     }
836 
837     for (int32_t j = 0; j < iForms; j++) {
838       wForm = (wchar_t)formChars[j].wForm;
839       iCharWidth = formChars[j].iWidth;
840       if (j > 0) {
841         chartype = FX_CHARTYPE::kCombination;
842         wch = wForm;
843         wLast = (wchar_t)formChars[j - 1].wForm;
844       }
845       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
846         pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
847 #if defined(OS_MACOSX)
848         pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
849 #endif
850         // TODO(npm): change widths in this method to unsigned to avoid implicit
851         // cast in the following line.
852         pCharPos->m_FontCharWidth = iCharWidth;
853       }
854 
855       fCharWidth = fFontSize * iCharWidth / 1000.0f;
856       if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
857         fX -= fCharWidth;
858 
859       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
860         pCharPos->m_Origin = CFX_PointF(fX, fY);
861 
862         if ((dwStyles & FX_LAYOUTSTYLE_CombText) != 0) {
863           int32_t iFormWidth = iCharWidth;
864           pFont->GetCharWidth(wForm, &iFormWidth);
865           float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
866           pCharPos->m_Origin.x += fOffset;
867         }
868 
869         if (chartype == FX_CHARTYPE::kCombination) {
870           FX_RECT rtBBox;
871           if (pFont->GetCharBBox(wForm, &rtBBox)) {
872             pCharPos->m_Origin.y =
873                 fYBase + fFontSize - fFontSize * rtBBox.Height() / iMaxHeight;
874           }
875           if (wForm == wch && wLast != 0xFEFF) {
876             if (FX_GetCharType(wLast) == FX_CHARTYPE::kCombination) {
877               FX_RECT rtBox;
878               if (pFont->GetCharBBox(wLast, &rtBox))
879                 pCharPos->m_Origin.y -= fFontSize * rtBox.Height() / iMaxHeight;
880             }
881           }
882         }
883       }
884       if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
885         fX += fCharWidth;
886 
887       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
888         pCharPos->m_bGlyphAdjust = true;
889         pCharPos->m_AdjustMatrix[0] = -1;
890         pCharPos->m_AdjustMatrix[1] = 0;
891         pCharPos->m_AdjustMatrix[2] = 0;
892         pCharPos->m_AdjustMatrix[3] = 1;
893 
894         if (iHorScale != 100 || iVerScale != 100) {
895           pCharPos->m_AdjustMatrix[0] =
896               pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
897           pCharPos->m_AdjustMatrix[1] =
898               pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
899           pCharPos->m_AdjustMatrix[2] =
900               pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
901           pCharPos->m_AdjustMatrix[3] =
902               pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
903         }
904         pCharPos++;
905       }
906     }
907     if (iWidth > 0)
908       wPrev = static_cast<wchar_t>(formChars[0].wch);
909     wLast = wch;
910   }
911   return szCount;
912 }
913 
GetCharRects(const Run * pTxtRun,bool bCharBBox) const914 std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const Run* pTxtRun,
915                                                   bool bCharBBox) const {
916   if (!pTxtRun || pTxtRun->iLength < 1)
917     return std::vector<CFX_RectF>();
918 
919   Engine* pEngine = pTxtRun->pEdtEngine;
920   const wchar_t* pStr = pTxtRun->wsStr.c_str();
921   int32_t* pWidths = pTxtRun->pWidths;
922   int32_t iLength = pTxtRun->iLength;
923   CFX_RectF rect(*pTxtRun->pRect);
924   float fFontSize = pTxtRun->fFontSize;
925   float fScale = fFontSize / 1000.0f;
926   RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
927   if (!pFont)
928     bCharBBox = false;
929 
930   FX_RECT bbox;
931   if (bCharBBox)
932     bCharBBox = pFont->GetBBox(&bbox);
933 
934   float fLeft = std::max(0.0f, bbox.left * fScale);
935   float fHeight = fabs(bbox.Height() * fScale);
936   bool bRTLPiece = !!(pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
937   bool bSingleLine = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_SingleLine);
938   bool bCombText = !!(pTxtRun->dwStyles & FX_LAYOUTSTYLE_CombText);
939   wchar_t wch;
940   int32_t iCharSize;
941   float fCharSize;
942   float fStart = bRTLPiece ? rect.right() : rect.left;
943 
944   std::vector<CFX_RectF> rtArray(iLength);
945   for (int32_t i = 0; i < iLength; i++) {
946     int32_t iAbsolute = i + pTxtRun->iStart;
947     if (pEngine) {
948       wch = pEngine->GetChar(iAbsolute);
949       iCharSize = pEngine->GetWidthOfChar(iAbsolute);
950     } else {
951       wch = *pStr++;
952       iCharSize = *pWidths++;
953     }
954     fCharSize = static_cast<float>(iCharSize) / kConversionFactor;
955     bool bRet = (!bSingleLine && IsCtrlCode(wch));
956     if (!(wch == L'\v' || wch == L'\f' || wch == 0x2028 || wch == 0x2029 ||
957           wch == L'\n')) {
958       bRet = false;
959     }
960     if (bRet)
961       fCharSize = fFontSize / 2.0f;
962     rect.left = fStart;
963     if (bRTLPiece) {
964       rect.left -= fCharSize;
965       fStart -= fCharSize;
966     } else {
967       fStart += fCharSize;
968     }
969     rect.width = fCharSize;
970 
971     if (bCharBBox && !bRet) {
972       int32_t iCharWidth = 1000;
973       pFont->GetCharWidth(wch, &iCharWidth);
974       float fRTLeft = 0, fCharWidth = 0;
975       if (iCharWidth > 0) {
976         fCharWidth = iCharWidth * fScale;
977         fRTLeft = fLeft;
978         if (bCombText)
979           fRTLeft = (rect.width - fCharWidth) / 2.0f;
980       }
981       CFX_RectF rtBBoxF;
982       rtBBoxF.left = rect.left + fRTLeft;
983       rtBBoxF.top = rect.top + (rect.height - fHeight) / 2.0f;
984       rtBBoxF.width = fCharWidth;
985       rtBBoxF.height = fHeight;
986       rtBBoxF.top = std::max(rtBBoxF.top, 0.0f);
987       rtArray[i] = rtBBoxF;
988       continue;
989     }
990     rtArray[i] = rect;
991   }
992   return rtArray;
993 }
994 
995 CFX_TxtBreak::Engine::~Engine() = default;
996 
997 CFX_TxtBreak::Run::Run() = default;
998 
999 CFX_TxtBreak::Run::~Run() = default;
1000 
1001 CFX_TxtBreak::Run::Run(const CFX_TxtBreak::Run& other) = default;
1002