Home | History | Annotate | Download | only in layout
      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 
     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 
     28 CFX_TxtBreak::CFX_TxtBreak()
     29     : CFX_Break(FX_LAYOUTSTYLE_None),
     30       m_iAlignment(CFX_TxtLineAlignment_Left),
     31       m_iCombWidth(360000) {}
     32 
     33 CFX_TxtBreak::~CFX_TxtBreak() {}
     34 
     35 void CFX_TxtBreak::SetLineWidth(float fLineWidth) {
     36   m_iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
     37   ASSERT(m_iLineWidth >= 20000);
     38 }
     39 
     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 
     46 void CFX_TxtBreak::SetCombWidth(float fCombWidth) {
     47   m_iCombWidth = FXSYS_round(fCombWidth * 20000.0f);
     48 }
     49 
     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 
     90 void CFX_TxtBreak::AppendChar_Tab(CFX_Char* pCurChar) {
     91   m_eCharType = FX_CHARTYPE_Tab;
     92 }
     93 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    645 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 
    899 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 
    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 
    996 FX_TXTRUN::~FX_TXTRUN() {}
    997 
    998 FX_TXTRUN::FX_TXTRUN(const FX_TXTRUN& other) = default;
    999