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