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
19namespace {
20
21bool 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
28CFX_TxtBreak::CFX_TxtBreak()
29    : CFX_Break(FX_LAYOUTSTYLE_None),
30      m_iAlignment(CFX_TxtLineAlignment_Left),
31      m_iCombWidth(360000) {}
32
33CFX_TxtBreak::~CFX_TxtBreak() {}
34
35void CFX_TxtBreak::SetLineWidth(float fLineWidth) {
36  m_iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
37  ASSERT(m_iLineWidth >= 20000);
38}
39
40void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
41  ASSERT(iAlignment >= CFX_TxtLineAlignment_Left &&
42         iAlignment <= CFX_TxtLineAlignment_Justified);
43  m_iAlignment = iAlignment;
44}
45
46void CFX_TxtBreak::SetCombWidth(float fCombWidth) {
47  m_iCombWidth = FXSYS_round(fCombWidth * 20000.0f);
48}
49
50void 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
90void CFX_TxtBreak::AppendChar_Tab(CFX_Char* pCurChar) {
91  m_eCharType = FX_CHARTYPE_Tab;
92}
93
94CFX_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
121CFX_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
175CFX_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
204CFX_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
261bool 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
289void 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
395void 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
468CFX_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
511int32_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
598void 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
639struct FX_FORMCHAR {
640  uint16_t wch;
641  uint16_t wForm;
642  int32_t iWidth;
643};
644
645int32_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
899std::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
982FX_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
996FX_TXTRUN::~FX_TXTRUN() {}
997
998FX_TXTRUN::FX_TXTRUN(const FX_TXTRUN& other) = default;
999