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/fgas_textbreak.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "core/fxcrt/fx_arabic.h"
     12 #include "core/fxcrt/fx_arb.h"
     13 #include "core/fxcrt/fx_memory.h"
     14 #include "third_party/base/ptr_util.h"
     15 #include "xfa/fgas/font/cfgas_gefont.h"
     16 #include "xfa/fgas/layout/fgas_linebreak.h"
     17 
     18 namespace {
     19 
     20 typedef uint32_t (CFX_TxtBreak::*FX_TxtBreak_LPFAppendChar)(
     21     CFX_TxtChar* pCurChar,
     22     int32_t iRotation);
     23 const FX_TxtBreak_LPFAppendChar g_FX_TxtBreak_lpfAppendChar[16] = {
     24     &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Tab,
     25     &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Control,
     26     &CFX_TxtBreak::AppendChar_Combination, &CFX_TxtBreak::AppendChar_Others,
     27     &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Arabic,
     28     &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Arabic,
     29     &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Arabic,
     30     &CFX_TxtBreak::AppendChar_Arabic,      &CFX_TxtBreak::AppendChar_Others,
     31     &CFX_TxtBreak::AppendChar_Others,      &CFX_TxtBreak::AppendChar_Others,
     32 };
     33 
     34 }  // namespace
     35 
     36 CFX_TxtBreak::CFX_TxtBreak(uint32_t dwPolicies)
     37     : m_dwPolicies(dwPolicies),
     38       m_iLineWidth(2000000),
     39       m_dwLayoutStyles(0),
     40       m_bVertical(false),
     41       m_bArabicContext(false),
     42       m_bArabicShapes(false),
     43       m_bRTL(false),
     44       m_bSingleLine(false),
     45       m_bCombText(false),
     46       m_iArabicContext(1),
     47       m_iCurArabicContext(1),
     48       m_pFont(nullptr),
     49       m_iFontSize(240),
     50       m_bEquidistant(true),
     51       m_iTabWidth(720000),
     52       m_wDefChar(0xFEFF),
     53       m_wParagBreakChar(L'\n'),
     54       m_iDefChar(0),
     55       m_iLineRotation(0),
     56       m_iCharRotation(0),
     57       m_iRotation(0),
     58       m_iAlignment(FX_TXTLINEALIGNMENT_Left),
     59       m_dwContextCharStyles(0),
     60       m_iCombWidth(360000),
     61       m_pUserData(nullptr),
     62       m_eCharType(FX_CHARTYPE_Unknown),
     63       m_bArabicNumber(false),
     64       m_bArabicComma(false),
     65       m_pCurLine(nullptr),
     66       m_iReady(0),
     67       m_iTolerance(0),
     68       m_iHorScale(100),
     69       m_iCharSpace(0) {
     70   m_bPagination = (m_dwPolicies & FX_TXTBREAKPOLICY_Pagination) != 0;
     71   int32_t iSize = m_bPagination ? sizeof(CFX_Char) : sizeof(CFX_TxtChar);
     72   m_pTxtLine1 = pdfium::MakeUnique<CFX_TxtLine>(iSize);
     73   m_pTxtLine2 = pdfium::MakeUnique<CFX_TxtLine>(iSize);
     74   m_pCurLine = m_pTxtLine1.get();
     75   ResetArabicContext();
     76 }
     77 
     78 CFX_TxtBreak::~CFX_TxtBreak() {
     79   Reset();
     80 }
     81 
     82 void CFX_TxtBreak::SetLineWidth(FX_FLOAT fLineWidth) {
     83   m_iLineWidth = FXSYS_round(fLineWidth * 20000.0f);
     84   ASSERT(m_iLineWidth >= 20000);
     85 }
     86 
     87 void CFX_TxtBreak::SetLinePos(FX_FLOAT fLinePos) {
     88   int32_t iLinePos =
     89       std::min(std::max(FXSYS_round(fLinePos * 20000.0f), 0), m_iLineWidth);
     90   m_pCurLine->m_iStart = iLinePos;
     91   m_pCurLine->m_iWidth += iLinePos;
     92 }
     93 
     94 void CFX_TxtBreak::SetLayoutStyles(uint32_t dwLayoutStyles) {
     95   m_dwLayoutStyles = dwLayoutStyles;
     96   m_bVertical = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_VerticalChars) != 0;
     97   m_bArabicContext = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ArabicContext) != 0;
     98   m_bArabicShapes = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ArabicShapes) != 0;
     99   m_bRTL = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_RTLReadingOrder) != 0;
    100   m_bSingleLine = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_SingleLine) != 0;
    101   m_bCombText = (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_CombText) != 0;
    102   ResetArabicContext();
    103   m_iLineRotation = GetLineRotation(m_dwLayoutStyles);
    104   m_iRotation = m_iLineRotation + m_iCharRotation;
    105   m_iRotation %= 4;
    106 }
    107 
    108 void CFX_TxtBreak::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont) {
    109   if (!pFont || pFont == m_pFont)
    110     return;
    111 
    112   SetBreakStatus();
    113   m_pFont = pFont;
    114   FontChanged();
    115 }
    116 
    117 void CFX_TxtBreak::SetFontSize(FX_FLOAT fFontSize) {
    118   int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
    119   if (m_iFontSize == iFontSize)
    120     return;
    121 
    122   SetBreakStatus();
    123   m_iFontSize = iFontSize;
    124   FontChanged();
    125 }
    126 
    127 void CFX_TxtBreak::FontChanged() {
    128   m_iDefChar = 0;
    129   if (m_wDefChar == 0xFEFF || !m_pFont)
    130     return;
    131 
    132   m_pFont->GetCharWidth(m_wDefChar, m_iDefChar, false);
    133   m_iDefChar *= m_iFontSize;
    134 }
    135 
    136 void CFX_TxtBreak::SetTabWidth(FX_FLOAT fTabWidth, bool bEquidistant) {
    137   m_iTabWidth = FXSYS_round(fTabWidth * 20000.0f);
    138   if (m_iTabWidth < FX_TXTBREAK_MinimumTabWidth)
    139     m_iTabWidth = FX_TXTBREAK_MinimumTabWidth;
    140 
    141   m_bEquidistant = bEquidistant;
    142 }
    143 
    144 void CFX_TxtBreak::SetDefaultChar(FX_WCHAR wch) {
    145   m_wDefChar = wch;
    146   m_iDefChar = 0;
    147   if (m_wDefChar == 0xFEFF || !m_pFont)
    148     return;
    149 
    150   m_pFont->GetCharWidth(m_wDefChar, m_iDefChar, false);
    151   if (m_iDefChar < 0)
    152     m_iDefChar = 0;
    153   else
    154     m_iDefChar *= m_iFontSize;
    155 }
    156 
    157 void CFX_TxtBreak::SetParagraphBreakChar(FX_WCHAR wch) {
    158   if (wch != L'\r' && wch != L'\n')
    159     return;
    160   m_wParagBreakChar = wch;
    161 }
    162 
    163 void CFX_TxtBreak::SetLineBreakTolerance(FX_FLOAT fTolerance) {
    164   m_iTolerance = FXSYS_round(fTolerance * 20000.0f);
    165 }
    166 
    167 void CFX_TxtBreak::SetCharRotation(int32_t iCharRotation) {
    168   if (iCharRotation < 0)
    169     iCharRotation += (-iCharRotation / 4 + 1) * 4;
    170   else if (iCharRotation > 3)
    171     iCharRotation -= (iCharRotation / 4) * 4;
    172 
    173   if (m_iCharRotation == iCharRotation)
    174     return;
    175 
    176   SetBreakStatus();
    177   m_iCharRotation = iCharRotation;
    178   m_iRotation = m_iLineRotation + m_iCharRotation;
    179   m_iRotation %= 4;
    180 }
    181 
    182 void CFX_TxtBreak::SetAlignment(int32_t iAlignment) {
    183   ASSERT(iAlignment >= FX_TXTLINEALIGNMENT_Left &&
    184          iAlignment <= FX_TXTLINEALIGNMENT_Distributed);
    185   m_iAlignment = iAlignment;
    186   ResetArabicContext();
    187 }
    188 
    189 void CFX_TxtBreak::ResetContextCharStyles() {
    190   m_dwContextCharStyles = m_bArabicContext ? m_iCurAlignment : m_iAlignment;
    191   if (m_bArabicNumber)
    192     m_dwContextCharStyles |= FX_TXTCHARSTYLE_ArabicNumber;
    193   if (m_bArabicComma)
    194     m_dwContextCharStyles |= FX_TXTCHARSTYLE_ArabicComma;
    195   if ((m_bArabicContext && m_bCurRTL) || (!m_bArabicContext && m_bRTL))
    196     m_dwContextCharStyles |= FX_TXTCHARSTYLE_RTLReadingOrder;
    197   m_dwContextCharStyles |= (m_iArabicContext << 8);
    198 }
    199 
    200 void CFX_TxtBreak::SetCombWidth(FX_FLOAT fCombWidth) {
    201   m_iCombWidth = FXSYS_round(fCombWidth * 20000.0f);
    202 }
    203 
    204 void CFX_TxtBreak::SetUserData(void* pUserData) {
    205   if (m_pUserData == pUserData)
    206     return;
    207 
    208   SetBreakStatus();
    209   m_pUserData = pUserData;
    210 }
    211 
    212 void CFX_TxtBreak::SetBreakStatus() {
    213   if (m_bPagination)
    214     return;
    215 
    216   int32_t iCount = m_pCurLine->CountChars();
    217   if (iCount < 1)
    218     return;
    219 
    220   CFX_TxtChar* pTC = m_pCurLine->GetCharPtr(iCount - 1);
    221   if (pTC->m_dwStatus == 0)
    222     pTC->m_dwStatus = FX_TXTBREAK_PieceBreak;
    223 }
    224 
    225 void CFX_TxtBreak::SetHorizontalScale(int32_t iScale) {
    226   if (iScale < 0)
    227     iScale = 0;
    228   if (iScale == m_iHorScale)
    229     return;
    230 
    231   SetBreakStatus();
    232   m_iHorScale = iScale;
    233 }
    234 
    235 void CFX_TxtBreak::SetCharSpace(FX_FLOAT fCharSpace) {
    236   m_iCharSpace = FXSYS_round(fCharSpace * 20000.0f);
    237 }
    238 
    239 static const int32_t gs_FX_TxtLineRotations[8] = {0, 3, 1, 0, 2, 1, 3, 2};
    240 
    241 int32_t CFX_TxtBreak::GetLineRotation(uint32_t dwStyles) const {
    242   return gs_FX_TxtLineRotations[(dwStyles & 0x0E) >> 1];
    243 }
    244 
    245 CFX_TxtChar* CFX_TxtBreak::GetLastChar(int32_t index, bool bOmitChar) const {
    246   std::vector<CFX_TxtChar>& ca = *m_pCurLine->m_pLineChars.get();
    247   int32_t iCount = pdfium::CollectionSize<int32_t>(ca);
    248   if (index < 0 || index >= iCount)
    249     return nullptr;
    250 
    251   int32_t iStart = iCount - 1;
    252   while (iStart > -1) {
    253     CFX_TxtChar* pTC = &ca[iStart--];
    254     if (bOmitChar && pTC->GetCharType() == FX_CHARTYPE_Combination)
    255       continue;
    256     if (--index < 0)
    257       return pTC;
    258   }
    259   return nullptr;
    260 }
    261 
    262 CFX_TxtLine* CFX_TxtBreak::GetTxtLine() const {
    263   if (m_iReady == 1)
    264     return m_pTxtLine1.get();
    265   if (m_iReady == 2)
    266     return m_pTxtLine2.get();
    267   return nullptr;
    268 }
    269 
    270 CFX_TxtPieceArray* CFX_TxtBreak::GetTxtPieces() const {
    271   CFX_TxtLine* pTxtLine = GetTxtLine();
    272   return pTxtLine ? pTxtLine->m_pLinePieces.get() : nullptr;
    273 }
    274 
    275 inline FX_CHARTYPE CFX_TxtBreak::GetUnifiedCharType(
    276     FX_CHARTYPE chartype) const {
    277   return chartype >= FX_CHARTYPE_ArabicAlef ? FX_CHARTYPE_Arabic : chartype;
    278 }
    279 
    280 void CFX_TxtBreak::ResetArabicContext() {
    281   if (m_bArabicContext) {
    282     m_bCurRTL = m_iCurArabicContext > 1;
    283     m_iCurAlignment = m_iCurArabicContext > 1 ? FX_TXTLINEALIGNMENT_Right
    284                                               : FX_TXTLINEALIGNMENT_Left;
    285     m_iCurAlignment |= (m_iAlignment & FX_TXTLINEALIGNMENT_HigherMask);
    286     m_bArabicNumber = m_iArabicContext >= 1 && m_bArabicShapes;
    287   } else {
    288     if (m_bPagination) {
    289       m_bCurRTL = false;
    290       m_iCurAlignment = 0;
    291     } else {
    292       m_bCurRTL = m_bRTL;
    293       m_iCurAlignment = m_iAlignment;
    294     }
    295     if (m_bRTL)
    296       m_bArabicNumber = m_iArabicContext >= 1;
    297     else
    298       m_bArabicNumber = m_iArabicContext > 1;
    299     m_bArabicNumber = m_bArabicNumber && m_bArabicShapes;
    300   }
    301   m_bArabicComma = m_bArabicNumber;
    302   ResetContextCharStyles();
    303 }
    304 
    305 void CFX_TxtBreak::AppendChar_PageLoad(CFX_TxtChar* pCurChar,
    306                                        uint32_t dwProps) {
    307   if (!m_bPagination) {
    308     pCurChar->m_dwStatus = 0;
    309     pCurChar->m_pUserData = m_pUserData;
    310   }
    311   if (m_bArabicContext || m_bArabicShapes) {
    312     int32_t iBidiCls = (dwProps & FX_BIDICLASSBITSMASK) >> FX_BIDICLASSBITS;
    313     int32_t iArabicContext =
    314         (iBidiCls == FX_BIDICLASS_R || iBidiCls == FX_BIDICLASS_AL)
    315             ? 2
    316             : ((iBidiCls == FX_BIDICLASS_L || iBidiCls == FX_BIDICLASS_S) ? 0
    317                                                                           : 1);
    318     if (iArabicContext != m_iArabicContext && iArabicContext != 1) {
    319       m_iArabicContext = iArabicContext;
    320       if (m_iCurArabicContext == 1)
    321         m_iCurArabicContext = iArabicContext;
    322 
    323       ResetArabicContext();
    324       if (!m_bPagination) {
    325         CFX_TxtChar* pLastChar = GetLastChar(1, false);
    326         if (pLastChar && pLastChar->m_dwStatus < 1)
    327           pLastChar->m_dwStatus = FX_TXTBREAK_PieceBreak;
    328       }
    329     }
    330   }
    331   pCurChar->m_dwCharStyles = m_dwContextCharStyles;
    332 }
    333 
    334 uint32_t CFX_TxtBreak::AppendChar_Combination(CFX_TxtChar* pCurChar,
    335                                               int32_t iRotation) {
    336   FX_WCHAR wch = pCurChar->m_wCharCode;
    337   FX_WCHAR wForm;
    338   int32_t iCharWidth = 0;
    339   pCurChar->m_iCharWidth = -1;
    340   if (m_bCombText) {
    341     iCharWidth = m_iCombWidth;
    342   } else {
    343     if (m_bVertical != FX_IsOdd(iRotation)) {
    344       iCharWidth = 1000;
    345     } else {
    346       wForm = wch;
    347       if (!m_bPagination) {
    348         CFX_TxtChar* pLastChar = GetLastChar(0, false);
    349         if (pLastChar &&
    350             (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
    351           bool bShadda = false;
    352           if (wch == 0x0651) {
    353             FX_WCHAR wLast = pLastChar->m_wCharCode;
    354             if (wLast >= 0x064C && wLast <= 0x0650) {
    355               wForm = FX_GetArabicFromShaddaTable(wLast);
    356               bShadda = true;
    357             }
    358           } else if (wch >= 0x064C && wch <= 0x0650) {
    359             if (pLastChar->m_wCharCode == 0x0651) {
    360               wForm = FX_GetArabicFromShaddaTable(wch);
    361               bShadda = true;
    362             }
    363           }
    364           if (bShadda) {
    365             pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
    366             pLastChar->m_iCharWidth = 0;
    367             pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
    368           }
    369         }
    370       }
    371       if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
    372         iCharWidth = 0;
    373     }
    374     iCharWidth *= m_iFontSize;
    375     iCharWidth = iCharWidth * m_iHorScale / 100;
    376   }
    377   pCurChar->m_iCharWidth = -iCharWidth;
    378   return FX_TXTBREAK_None;
    379 }
    380 
    381 uint32_t CFX_TxtBreak::AppendChar_Tab(CFX_TxtChar* pCurChar,
    382                                       int32_t iRotation) {
    383   m_eCharType = FX_CHARTYPE_Tab;
    384   if ((m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_ExpandTab) == 0)
    385     return FX_TXTBREAK_None;
    386 
    387   int32_t& iLineWidth = m_pCurLine->m_iWidth;
    388   int32_t iCharWidth;
    389   if (m_bCombText) {
    390     iCharWidth = m_iCombWidth;
    391   } else {
    392     if (m_bEquidistant) {
    393       iCharWidth = iLineWidth;
    394       iCharWidth = m_iTabWidth * (iCharWidth / m_iTabWidth + 1) - iCharWidth;
    395       if (iCharWidth < FX_TXTBREAK_MinimumTabWidth)
    396         iCharWidth += m_iTabWidth;
    397     } else {
    398       iCharWidth = m_iTabWidth;
    399     }
    400   }
    401 
    402   pCurChar->m_iCharWidth = iCharWidth;
    403   iLineWidth += iCharWidth;
    404   if (!m_bSingleLine && iLineWidth >= m_iLineWidth + m_iTolerance)
    405     return EndBreak(FX_TXTBREAK_LineBreak);
    406 
    407   return FX_TXTBREAK_None;
    408 }
    409 
    410 uint32_t CFX_TxtBreak::AppendChar_Control(CFX_TxtChar* pCurChar,
    411                                           int32_t iRotation) {
    412   m_eCharType = FX_CHARTYPE_Control;
    413   uint32_t dwRet = FX_TXTBREAK_None;
    414   if (!m_bSingleLine) {
    415     FX_WCHAR wch = pCurChar->m_wCharCode;
    416     switch (wch) {
    417       case L'\v':
    418       case 0x2028:
    419         dwRet = FX_TXTBREAK_LineBreak;
    420         break;
    421       case L'\f':
    422         dwRet = FX_TXTBREAK_PageBreak;
    423         break;
    424       case 0x2029:
    425         dwRet = FX_TXTBREAK_ParagraphBreak;
    426         break;
    427       default:
    428         if (wch == m_wParagBreakChar)
    429           dwRet = FX_TXTBREAK_ParagraphBreak;
    430         break;
    431     }
    432     if (dwRet != FX_TXTBREAK_None)
    433       dwRet = EndBreak(dwRet);
    434   }
    435   return dwRet;
    436 }
    437 
    438 uint32_t CFX_TxtBreak::AppendChar_Arabic(CFX_TxtChar* pCurChar,
    439                                          int32_t iRotation) {
    440   FX_CHARTYPE chartype = pCurChar->GetCharType();
    441   int32_t& iLineWidth = m_pCurLine->m_iWidth;
    442   FX_WCHAR wForm;
    443   int32_t iCharWidth = 0;
    444   CFX_TxtChar* pLastChar = nullptr;
    445   bool bAlef = false;
    446   if (!m_bCombText && m_eCharType >= FX_CHARTYPE_ArabicAlef &&
    447       m_eCharType <= FX_CHARTYPE_ArabicDistortion) {
    448     pLastChar = GetLastChar(1);
    449     if (pLastChar) {
    450       iCharWidth = pLastChar->m_iCharWidth;
    451       if (iCharWidth > 0)
    452         iLineWidth -= iCharWidth;
    453 
    454       CFX_Char* pPrevChar = GetLastChar(2);
    455       wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
    456       bAlef = (wForm == 0xFEFF &&
    457                pLastChar->GetCharType() == FX_CHARTYPE_ArabicAlef);
    458       int32_t iLastRotation = pLastChar->m_nRotation + m_iLineRotation;
    459       if (m_bVertical && (pLastChar->m_dwCharProps & 0x8000) != 0)
    460         iLastRotation++;
    461       if (m_bVertical != FX_IsOdd(iLastRotation))
    462         iCharWidth = 1000;
    463       else
    464         m_pFont->GetCharWidth(wForm, iCharWidth, false);
    465 
    466       if (wForm == 0xFEFF)
    467         iCharWidth = m_iDefChar;
    468 
    469       iCharWidth *= m_iFontSize;
    470       iCharWidth = iCharWidth * m_iHorScale / 100;
    471       pLastChar->m_iCharWidth = iCharWidth;
    472       iLineWidth += iCharWidth;
    473       iCharWidth = 0;
    474     }
    475   }
    476 
    477   m_eCharType = chartype;
    478   wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
    479                                       nullptr);
    480   if (m_bCombText) {
    481     iCharWidth = m_iCombWidth;
    482   } else {
    483     if (m_bVertical != FX_IsOdd(iRotation))
    484       iCharWidth = 1000;
    485     else
    486       m_pFont->GetCharWidth(wForm, iCharWidth, false);
    487 
    488     if (wForm == 0xFEFF)
    489       iCharWidth = m_iDefChar;
    490 
    491     iCharWidth *= m_iFontSize;
    492     iCharWidth = iCharWidth * m_iHorScale / 100;
    493   }
    494   pCurChar->m_iCharWidth = iCharWidth;
    495   iLineWidth += iCharWidth;
    496   m_pCurLine->m_iArabicChars++;
    497   if (!m_bSingleLine && iLineWidth > m_iLineWidth + m_iTolerance)
    498     return EndBreak(FX_TXTBREAK_LineBreak);
    499   return FX_TXTBREAK_None;
    500 }
    501 
    502 uint32_t CFX_TxtBreak::AppendChar_Others(CFX_TxtChar* pCurChar,
    503                                          int32_t iRotation) {
    504   uint32_t dwProps = pCurChar->m_dwCharProps;
    505   FX_CHARTYPE chartype = pCurChar->GetCharType();
    506   int32_t& iLineWidth = m_pCurLine->m_iWidth;
    507   int32_t iCharWidth = 0;
    508   m_eCharType = chartype;
    509   FX_WCHAR wch = pCurChar->m_wCharCode;
    510   FX_WCHAR wForm = wch;
    511   if (chartype == FX_CHARTYPE_Numeric) {
    512     if (m_bArabicNumber) {
    513       wForm = wch + 0x0630;
    514       pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicIndic;
    515     }
    516   } else if (wch == L',') {
    517     if (m_bArabicShapes && m_iCurArabicContext > 0) {
    518       wForm = 0x060C;
    519       pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicComma;
    520     }
    521   } else if (m_bCurRTL || m_bVertical) {
    522     wForm = FX_GetMirrorChar(wch, dwProps, m_bCurRTL, m_bVertical);
    523   }
    524 
    525   if (m_bCombText) {
    526     iCharWidth = m_iCombWidth;
    527   } else {
    528     if (m_bVertical != FX_IsOdd(iRotation))
    529       iCharWidth = 1000;
    530     else if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
    531       iCharWidth = m_iDefChar;
    532 
    533     iCharWidth *= m_iFontSize;
    534     iCharWidth = iCharWidth * m_iHorScale / 100;
    535   }
    536 
    537   iCharWidth += m_iCharSpace;
    538   pCurChar->m_iCharWidth = iCharWidth;
    539   iLineWidth += iCharWidth;
    540   bool bBreak = (chartype != FX_CHARTYPE_Space ||
    541                  (m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0);
    542   if (!m_bSingleLine && bBreak && iLineWidth > m_iLineWidth + m_iTolerance)
    543     return EndBreak(FX_TXTBREAK_LineBreak);
    544 
    545   return FX_TXTBREAK_None;
    546 }
    547 
    548 uint32_t CFX_TxtBreak::AppendChar(FX_WCHAR wch) {
    549   uint32_t dwProps = kTextLayoutCodeProperties[static_cast<uint16_t>(wch)];
    550   FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
    551   m_pCurLine->m_pLineChars->emplace_back();
    552 
    553   CFX_TxtChar* pCurChar = &m_pCurLine->m_pLineChars->back();
    554   pCurChar->m_wCharCode = static_cast<uint16_t>(wch);
    555   pCurChar->m_nRotation = m_iCharRotation;
    556   pCurChar->m_dwCharProps = dwProps;
    557   pCurChar->m_dwCharStyles = 0;
    558   pCurChar->m_iCharWidth = 0;
    559   pCurChar->m_iHorizontalScale = m_iHorScale;
    560   pCurChar->m_iVerticalScale = 100;
    561   pCurChar->m_dwStatus = 0;
    562   pCurChar->m_iBidiClass = 0;
    563   pCurChar->m_iBidiLevel = 0;
    564   pCurChar->m_iBidiPos = 0;
    565   pCurChar->m_iBidiOrder = 0;
    566   pCurChar->m_pUserData = nullptr;
    567   AppendChar_PageLoad(pCurChar, dwProps);
    568   uint32_t dwRet1 = FX_TXTBREAK_None;
    569   if (chartype != FX_CHARTYPE_Combination &&
    570       GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
    571       m_eCharType != FX_CHARTYPE_Unknown &&
    572       m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance && !m_bSingleLine &&
    573       (m_eCharType != FX_CHARTYPE_Space || chartype != FX_CHARTYPE_Control)) {
    574     dwRet1 = EndBreak(FX_TXTBREAK_LineBreak);
    575     int32_t iCount = m_pCurLine->CountChars();
    576     if (iCount > 0)
    577       pCurChar = &(*m_pCurLine->m_pLineChars)[iCount - 1];
    578   }
    579 
    580   int32_t iRotation = m_iRotation;
    581   if (m_bVertical && (dwProps & 0x8000) != 0)
    582     iRotation = (iRotation + 1) % 4;
    583 
    584   uint32_t dwRet2 =
    585       (this->*g_FX_TxtBreak_lpfAppendChar[chartype >> FX_CHARTYPEBITS])(
    586           pCurChar, iRotation);
    587   return std::max(dwRet1, dwRet2);
    588 }
    589 
    590 void CFX_TxtBreak::EndBreak_UpdateArabicShapes() {
    591   ASSERT(m_bArabicShapes);
    592   int32_t iCount = m_pCurLine->CountChars();
    593   if (iCount < 2)
    594     return;
    595 
    596   int32_t& iLineWidth = m_pCurLine->m_iWidth;
    597   CFX_TxtChar* pCur = m_pCurLine->GetCharPtr(0);
    598   bool bPrevNum = (pCur->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
    599   pCur = m_pCurLine->GetCharPtr(1);
    600   FX_WCHAR wch, wForm;
    601   bool bNextNum;
    602   int32_t i = 1;
    603   int32_t iCharWidth;
    604   int32_t iRotation;
    605   CFX_TxtChar* pNext;
    606   do {
    607     i++;
    608     if (i < iCount) {
    609       pNext = m_pCurLine->GetCharPtr(i);
    610       bNextNum = (pNext->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
    611     } else {
    612       pNext = nullptr;
    613       bNextNum = false;
    614     }
    615 
    616     wch = pCur->m_wCharCode;
    617     if (wch == L'.') {
    618       if (bPrevNum && bNextNum) {
    619         iRotation = m_iRotation;
    620         if (m_bVertical && (pCur->m_dwCharProps & 0x8000) != 0)
    621           iRotation = ((iRotation + 1) & 0x03);
    622 
    623         wForm = wch == L'.' ? 0x066B : 0x066C;
    624         iLineWidth -= pCur->m_iCharWidth;
    625         if (m_bCombText) {
    626           iCharWidth = m_iCombWidth;
    627         } else {
    628           if (m_bVertical != FX_IsOdd(iRotation))
    629             iCharWidth = 1000;
    630           else if (!m_pFont->GetCharWidth(wForm, iCharWidth, false))
    631             iCharWidth = m_iDefChar;
    632 
    633           iCharWidth *= m_iFontSize;
    634           iCharWidth = iCharWidth * m_iHorScale / 100;
    635         }
    636         pCur->m_iCharWidth = iCharWidth;
    637         iLineWidth += iCharWidth;
    638       }
    639     }
    640     bPrevNum = (pCur->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicIndic) != 0;
    641     pCur = pNext;
    642   } while (i < iCount);
    643 }
    644 
    645 bool CFX_TxtBreak::EndBreak_SplitLine(CFX_TxtLine* pNextLine,
    646                                       bool bAllChars,
    647                                       uint32_t dwStatus) {
    648   int32_t iCount = m_pCurLine->CountChars();
    649   bool bDone = false;
    650   CFX_TxtChar* pTC;
    651   if (!m_bSingleLine && m_pCurLine->m_iWidth > m_iLineWidth + m_iTolerance) {
    652     pTC = m_pCurLine->GetCharPtr(iCount - 1);
    653     switch (pTC->GetCharType()) {
    654       case FX_CHARTYPE_Tab:
    655       case FX_CHARTYPE_Control:
    656         break;
    657       case FX_CHARTYPE_Space:
    658         if ((m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0) {
    659           SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
    660           bDone = true;
    661         }
    662         break;
    663       default:
    664         SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
    665         bDone = true;
    666         break;
    667     }
    668   }
    669 
    670   iCount = m_pCurLine->CountChars();
    671   CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
    672   CFX_TxtPiece tp;
    673   if (m_bPagination) {
    674     tp.m_dwStatus = dwStatus;
    675     tp.m_iStartPos = m_pCurLine->m_iStart;
    676     tp.m_iWidth = m_pCurLine->m_iWidth;
    677     tp.m_iStartChar = 0;
    678     tp.m_iChars = iCount;
    679     tp.m_pChars = m_pCurLine->m_pLineChars.get();
    680     tp.m_pUserData = m_pUserData;
    681     pTC = m_pCurLine->GetCharPtr(0);
    682     tp.m_dwCharStyles = pTC->m_dwCharStyles;
    683     tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
    684     tp.m_iVerticalScale = pTC->m_iVerticalScale;
    685     pCurPieces->Add(tp);
    686     m_pCurLine = pNextLine;
    687     m_eCharType = FX_CHARTYPE_Unknown;
    688     return true;
    689   }
    690   if (bAllChars && !bDone) {
    691     int32_t iEndPos = m_pCurLine->m_iWidth;
    692     GetBreakPos(*m_pCurLine->m_pLineChars.get(), iEndPos, bAllChars, true);
    693   }
    694   return false;
    695 }
    696 
    697 void CFX_TxtBreak::EndBreak_BidiLine(std::deque<FX_TPO>* tpos,
    698                                      uint32_t dwStatus) {
    699   CFX_TxtPiece tp;
    700   FX_TPO tpo;
    701   CFX_TxtChar* pTC;
    702   int32_t i;
    703   int32_t j;
    704   std::vector<CFX_TxtChar>& chars = *m_pCurLine->m_pLineChars.get();
    705   int32_t iCount = m_pCurLine->CountChars();
    706   bool bDone = (m_pCurLine->m_iArabicChars > 0 || m_bCurRTL);
    707   if (!m_bPagination && bDone) {
    708     int32_t iBidiNum = 0;
    709     for (i = 0; i < iCount; i++) {
    710       pTC = &chars[i];
    711       pTC->m_iBidiPos = i;
    712       if (pTC->GetCharType() != FX_CHARTYPE_Control)
    713         iBidiNum = i;
    714       if (i == 0)
    715         pTC->m_iBidiLevel = 1;
    716     }
    717     FX_BidiLine(chars, iBidiNum + 1, m_bCurRTL ? 1 : 0);
    718   }
    719 
    720   CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
    721   if (!m_bPagination &&
    722       (bDone || (m_dwLayoutStyles & FX_TXTLAYOUTSTYLE_MutipleFormat) != 0)) {
    723     tp.m_dwStatus = FX_TXTBREAK_PieceBreak;
    724     tp.m_iStartPos = m_pCurLine->m_iStart;
    725     tp.m_pChars = m_pCurLine->m_pLineChars.get();
    726     int32_t iBidiLevel = -1;
    727     int32_t iCharWidth;
    728     i = 0;
    729     j = -1;
    730     while (i < iCount) {
    731       pTC = &chars[i];
    732       if (iBidiLevel < 0) {
    733         iBidiLevel = pTC->m_iBidiLevel;
    734         tp.m_iWidth = 0;
    735         tp.m_iBidiLevel = iBidiLevel;
    736         tp.m_iBidiPos = pTC->m_iBidiOrder;
    737         tp.m_dwCharStyles = pTC->m_dwCharStyles;
    738         tp.m_pUserData = pTC->m_pUserData;
    739         tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
    740         tp.m_iVerticalScale = pTC->m_iVerticalScale;
    741         tp.m_dwStatus = FX_TXTBREAK_PieceBreak;
    742       }
    743       if (iBidiLevel != pTC->m_iBidiLevel || pTC->m_dwStatus != 0) {
    744         if (iBidiLevel == pTC->m_iBidiLevel) {
    745           tp.m_dwStatus = pTC->m_dwStatus;
    746           iCharWidth = pTC->m_iCharWidth;
    747           if (iCharWidth > 0)
    748             tp.m_iWidth += iCharWidth;
    749 
    750           i++;
    751         }
    752         tp.m_iChars = i - tp.m_iStartChar;
    753         pCurPieces->Add(tp);
    754         tp.m_iStartPos += tp.m_iWidth;
    755         tp.m_iStartChar = i;
    756         tpo.index = ++j;
    757         tpo.pos = tp.m_iBidiPos;
    758         tpos->push_back(tpo);
    759         iBidiLevel = -1;
    760       } else {
    761         iCharWidth = pTC->m_iCharWidth;
    762         if (iCharWidth > 0)
    763           tp.m_iWidth += iCharWidth;
    764 
    765         i++;
    766       }
    767     }
    768     if (i > tp.m_iStartChar) {
    769       tp.m_dwStatus = dwStatus;
    770       tp.m_iChars = i - tp.m_iStartChar;
    771       pCurPieces->Add(tp);
    772       tpo.index = ++j;
    773       tpo.pos = tp.m_iBidiPos;
    774       tpos->push_back(tpo);
    775     }
    776     if (j > -1) {
    777       if (j > 0) {
    778         std::sort(tpos->begin(), tpos->end());
    779         int32_t iStartPos = 0;
    780         for (i = 0; i <= j; i++) {
    781           tpo = (*tpos)[i];
    782           CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
    783           ttp.m_iStartPos = iStartPos;
    784           iStartPos += ttp.m_iWidth;
    785         }
    786       }
    787       CFX_TxtPiece& ttp = pCurPieces->GetAt(j);
    788       ttp.m_dwStatus = dwStatus;
    789     }
    790   } else {
    791     tp.m_dwStatus = dwStatus;
    792     tp.m_iStartPos = m_pCurLine->m_iStart;
    793     tp.m_iWidth = m_pCurLine->m_iWidth;
    794     tp.m_iStartChar = 0;
    795     tp.m_iChars = iCount;
    796     tp.m_pChars = m_pCurLine->m_pLineChars.get();
    797     tp.m_pUserData = m_pUserData;
    798     pTC = &chars[0];
    799     tp.m_dwCharStyles = pTC->m_dwCharStyles;
    800     tp.m_iHorizontalScale = pTC->m_iHorizontalScale;
    801     tp.m_iVerticalScale = pTC->m_iVerticalScale;
    802     pCurPieces->Add(tp);
    803     tpos->push_back({0, 0});
    804   }
    805 }
    806 
    807 void CFX_TxtBreak::EndBreak_Alignment(const std::deque<FX_TPO>& tpos,
    808                                       bool bAllChars,
    809                                       uint32_t dwStatus) {
    810   int32_t iNetWidth = m_pCurLine->m_iWidth;
    811   int32_t iGapChars = 0;
    812   int32_t iCharWidth;
    813   CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
    814   int32_t i;
    815   int32_t j;
    816   int32_t iCount = pCurPieces->GetSize();
    817   bool bFind = false;
    818   FX_TPO tpo;
    819   CFX_TxtChar* pTC;
    820   FX_CHARTYPE chartype;
    821   for (i = iCount - 1; i > -1; i--) {
    822     tpo = tpos[i];
    823     CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
    824     if (!bFind)
    825       iNetWidth = ttp.GetEndPos();
    826 
    827     bool bArabic = FX_IsOdd(ttp.m_iBidiLevel);
    828     j = bArabic ? 0 : ttp.m_iChars - 1;
    829     while (j > -1 && j < ttp.m_iChars) {
    830       pTC = ttp.GetCharPtr(j);
    831       if (pTC->m_nBreakType == FX_LBT_DIRECT_BRK)
    832         iGapChars++;
    833       if (!bFind || !bAllChars) {
    834         chartype = pTC->GetCharType();
    835         if (chartype == FX_CHARTYPE_Space || chartype == FX_CHARTYPE_Control) {
    836           if (!bFind) {
    837             iCharWidth = pTC->m_iCharWidth;
    838             if (bAllChars && iCharWidth > 0)
    839               iNetWidth -= iCharWidth;
    840           }
    841         } else {
    842           bFind = true;
    843           if (!bAllChars)
    844             break;
    845         }
    846       }
    847       j += bArabic ? 1 : -1;
    848     }
    849     if (!bAllChars && bFind)
    850       break;
    851   }
    852 
    853   int32_t iOffset = m_iLineWidth - iNetWidth;
    854   int32_t iLowerAlignment = (m_iCurAlignment & FX_TXTLINEALIGNMENT_LowerMask);
    855   int32_t iHigherAlignment = (m_iCurAlignment & FX_TXTLINEALIGNMENT_HigherMask);
    856   if (iGapChars > 0 && (iHigherAlignment == FX_TXTLINEALIGNMENT_Distributed ||
    857                         (iHigherAlignment == FX_TXTLINEALIGNMENT_Justified &&
    858                          dwStatus != FX_TXTBREAK_ParagraphBreak))) {
    859     int32_t iStart = -1;
    860     for (i = 0; i < iCount; i++) {
    861       tpo = tpos[i];
    862       CFX_TxtPiece& ttp = pCurPieces->GetAt(tpo.index);
    863       if (iStart < -1)
    864         iStart = ttp.m_iStartPos;
    865       else
    866         ttp.m_iStartPos = iStart;
    867 
    868       for (j = 0; j < ttp.m_iChars; j++) {
    869         pTC = ttp.GetCharPtr(j);
    870         if (pTC->m_nBreakType != FX_LBT_DIRECT_BRK || pTC->m_iCharWidth < 0)
    871           continue;
    872 
    873         int32_t k = iOffset / iGapChars;
    874         pTC->m_iCharWidth += k;
    875         ttp.m_iWidth += k;
    876         iOffset -= k;
    877         iGapChars--;
    878         if (iGapChars < 1)
    879           break;
    880       }
    881       iStart += ttp.m_iWidth;
    882     }
    883   } else if (iLowerAlignment > FX_TXTLINEALIGNMENT_Left) {
    884     if (iLowerAlignment == FX_TXTLINEALIGNMENT_Center)
    885       iOffset /= 2;
    886     if (iOffset > 0) {
    887       for (i = 0; i < iCount; i++) {
    888         CFX_TxtPiece& ttp = pCurPieces->GetAt(i);
    889         ttp.m_iStartPos += iOffset;
    890       }
    891     }
    892   }
    893 }
    894 
    895 uint32_t CFX_TxtBreak::EndBreak(uint32_t dwStatus) {
    896   ASSERT(dwStatus >= FX_TXTBREAK_PieceBreak &&
    897          dwStatus <= FX_TXTBREAK_PageBreak);
    898   CFX_TxtPieceArray* pCurPieces = m_pCurLine->m_pLinePieces.get();
    899   int32_t iCount = pCurPieces->GetSize();
    900   if (iCount > 0) {
    901     CFX_TxtPiece* pLastPiece = pCurPieces->GetPtrAt(--iCount);
    902     if (dwStatus > FX_TXTBREAK_PieceBreak)
    903       pLastPiece->m_dwStatus = dwStatus;
    904     else
    905       dwStatus = pLastPiece->m_dwStatus;
    906     return dwStatus;
    907   } else {
    908     CFX_TxtLine* pLastLine = GetTxtLine();
    909     if (pLastLine) {
    910       pCurPieces = pLastLine->m_pLinePieces.get();
    911       iCount = pCurPieces->GetSize();
    912       if (iCount-- > 0) {
    913         CFX_TxtPiece* pLastPiece = pCurPieces->GetPtrAt(iCount);
    914         if (dwStatus > FX_TXTBREAK_PieceBreak)
    915           pLastPiece->m_dwStatus = dwStatus;
    916         else
    917           dwStatus = pLastPiece->m_dwStatus;
    918         return dwStatus;
    919       }
    920       return FX_TXTBREAK_None;
    921     }
    922 
    923     iCount = m_pCurLine->CountChars();
    924     if (iCount < 1)
    925       return FX_TXTBREAK_None;
    926     if (!m_bPagination) {
    927       CFX_TxtChar* pTC = m_pCurLine->GetCharPtr(iCount - 1);
    928       pTC->m_dwStatus = dwStatus;
    929     }
    930     if (dwStatus <= FX_TXTBREAK_PieceBreak)
    931       return dwStatus;
    932   }
    933 
    934   m_iReady = (m_pCurLine == m_pTxtLine1.get()) ? 1 : 2;
    935   CFX_TxtLine* pNextLine =
    936       (m_pCurLine == m_pTxtLine1.get()) ? m_pTxtLine2.get() : m_pTxtLine1.get();
    937   bool bAllChars = (m_iCurAlignment > FX_TXTLINEALIGNMENT_Right);
    938   if (m_bArabicShapes)
    939     EndBreak_UpdateArabicShapes();
    940 
    941   if (!EndBreak_SplitLine(pNextLine, bAllChars, dwStatus)) {
    942     std::deque<FX_TPO> tpos;
    943     EndBreak_BidiLine(&tpos, dwStatus);
    944     if (!m_bPagination && m_iCurAlignment > FX_TXTLINEALIGNMENT_Left)
    945       EndBreak_Alignment(tpos, bAllChars, dwStatus);
    946   }
    947 
    948   m_pCurLine = pNextLine;
    949   CFX_Char* pTC = GetLastChar(0, false);
    950   m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE_Unknown;
    951   if (dwStatus == FX_TXTBREAK_ParagraphBreak) {
    952     m_iArabicContext = m_iCurArabicContext = 1;
    953     ResetArabicContext();
    954   }
    955   return dwStatus;
    956 }
    957 
    958 int32_t CFX_TxtBreak::GetBreakPos(std::vector<CFX_TxtChar>& ca,
    959                                   int32_t& iEndPos,
    960                                   bool bAllChars,
    961                                   bool bOnlyBrk) {
    962   int32_t iLength = pdfium::CollectionSize<int32_t>(ca) - 1;
    963   if (iLength < 1)
    964     return iLength;
    965 
    966   int32_t iBreak = -1;
    967   int32_t iBreakPos = -1;
    968   int32_t iIndirect = -1;
    969   int32_t iIndirectPos = -1;
    970   int32_t iLast = -1;
    971   int32_t iLastPos = -1;
    972   if (m_bSingleLine || iEndPos <= m_iLineWidth) {
    973     if (!bAllChars)
    974       return iLength;
    975 
    976     iBreak = iLength;
    977     iBreakPos = iEndPos;
    978   }
    979 
    980   bool bSpaceBreak = (m_dwPolicies & FX_TXTBREAKPOLICY_SpaceBreak) != 0;
    981   bool bNumberBreak = (m_dwPolicies & FX_TXTBREAKPOLICY_NumberBreak) != 0;
    982   FX_LINEBREAKTYPE eType;
    983   uint32_t nCodeProp;
    984   uint32_t nCur;
    985   uint32_t nNext;
    986   CFX_Char* pCur = &ca[iLength--];
    987   if (bAllChars)
    988     pCur->m_nBreakType = FX_LBT_UNKNOWN;
    989 
    990   nCodeProp = pCur->m_dwCharProps;
    991   nNext = nCodeProp & 0x003F;
    992   int32_t iCharWidth = pCur->m_iCharWidth;
    993   if (iCharWidth > 0)
    994     iEndPos -= iCharWidth;
    995 
    996   while (iLength >= 0) {
    997     pCur = &ca[iLength];
    998     nCodeProp = pCur->m_dwCharProps;
    999     nCur = nCodeProp & 0x003F;
   1000     if (nCur == FX_CBP_SP) {
   1001       if (nNext == FX_CBP_SP)
   1002         eType = bSpaceBreak ? FX_LBT_DIRECT_BRK : FX_LBT_PROHIBITED_BRK;
   1003       else
   1004         eType = gs_FX_LineBreak_PairTable[nCur][nNext];
   1005     } else if (bNumberBreak && nCur == FX_CBP_NU && nNext == FX_CBP_NU) {
   1006       eType = FX_LBT_DIRECT_BRK;
   1007     } else {
   1008       if (nNext == FX_CBP_SP)
   1009         eType = FX_LBT_PROHIBITED_BRK;
   1010       else
   1011         eType = gs_FX_LineBreak_PairTable[nCur][nNext];
   1012     }
   1013     if (bAllChars)
   1014       pCur->m_nBreakType = static_cast<uint8_t>(eType);
   1015     if (!bOnlyBrk) {
   1016       if (m_bSingleLine || iEndPos <= m_iLineWidth ||
   1017           (nCur == FX_CBP_SP && !bSpaceBreak)) {
   1018         if (eType == FX_LBT_DIRECT_BRK && iBreak < 0) {
   1019           iBreak = iLength;
   1020           iBreakPos = iEndPos;
   1021           if (!bAllChars)
   1022             return iLength;
   1023         } else if (eType == FX_LBT_INDIRECT_BRK && iIndirect < 0) {
   1024           iIndirect = iLength;
   1025           iIndirectPos = iEndPos;
   1026         }
   1027         if (iLast < 0) {
   1028           iLast = iLength;
   1029           iLastPos = iEndPos;
   1030         }
   1031       }
   1032       iCharWidth = pCur->m_iCharWidth;
   1033       if (iCharWidth > 0)
   1034         iEndPos -= iCharWidth;
   1035     }
   1036     nNext = nCodeProp & 0x003F;
   1037     iLength--;
   1038   }
   1039   if (bOnlyBrk)
   1040     return 0;
   1041   if (iBreak > -1) {
   1042     iEndPos = iBreakPos;
   1043     return iBreak;
   1044   }
   1045   if (iIndirect > -1) {
   1046     iEndPos = iIndirectPos;
   1047     return iIndirect;
   1048   }
   1049   if (iLast > -1) {
   1050     iEndPos = iLastPos;
   1051     return iLast;
   1052   }
   1053   return 0;
   1054 }
   1055 
   1056 void CFX_TxtBreak::SplitTextLine(CFX_TxtLine* pCurLine,
   1057                                  CFX_TxtLine* pNextLine,
   1058                                  bool bAllChars) {
   1059   ASSERT(pCurLine && pNextLine);
   1060   int32_t iCount = pCurLine->CountChars();
   1061   if (iCount < 2)
   1062     return;
   1063 
   1064   int32_t iEndPos = pCurLine->m_iWidth;
   1065   std::vector<CFX_TxtChar>& curChars = *pCurLine->m_pLineChars;
   1066   int32_t iCharPos = GetBreakPos(curChars, iEndPos, bAllChars, false);
   1067   if (iCharPos < 0)
   1068     iCharPos = 0;
   1069 
   1070   iCharPos++;
   1071   if (iCharPos >= iCount) {
   1072     pNextLine->RemoveAll(true);
   1073     CFX_Char* pTC = &curChars[iCharPos - 1];
   1074     pTC->m_nBreakType = FX_LBT_UNKNOWN;
   1075     return;
   1076   }
   1077 
   1078   // m_pLineChars is a unique_ptr<vector>. Assign the ref into nextChars
   1079   // so we can change the m_pLineChars vector ...
   1080   std::vector<CFX_TxtChar>& nextChars = *pNextLine->m_pLineChars;
   1081   nextChars =
   1082       std::vector<CFX_TxtChar>(curChars.begin() + iCharPos, curChars.end());
   1083   curChars.erase(curChars.begin() + iCharPos, curChars.end());
   1084   pCurLine->m_iWidth = iEndPos;
   1085   CFX_TxtChar* pTC = &curChars[iCharPos - 1];
   1086   pTC->m_nBreakType = FX_LBT_UNKNOWN;
   1087   iCount = pdfium::CollectionSize<int>(nextChars);
   1088   int32_t iWidth = 0;
   1089   for (int32_t i = 0; i < iCount; i++) {
   1090     if (nextChars[i].GetCharType() >= FX_CHARTYPE_ArabicAlef) {
   1091       pCurLine->m_iArabicChars--;
   1092       pNextLine->m_iArabicChars++;
   1093     }
   1094     int32_t iCharWidth = nextChars[i].m_iCharWidth;
   1095     if (iCharWidth > 0)
   1096       iWidth += iCharWidth;
   1097     if (m_bPagination)
   1098       continue;
   1099 
   1100     nextChars[i].m_dwStatus = 0;
   1101   }
   1102   pNextLine->m_iWidth = iWidth;
   1103 }
   1104 
   1105 int32_t CFX_TxtBreak::CountBreakPieces() const {
   1106   CFX_TxtPieceArray* pTxtPieces = GetTxtPieces();
   1107   return pTxtPieces ? pTxtPieces->GetSize() : 0;
   1108 }
   1109 
   1110 const CFX_TxtPiece* CFX_TxtBreak::GetBreakPiece(int32_t index) const {
   1111   CFX_TxtPieceArray* pTxtPieces = GetTxtPieces();
   1112   if (!pTxtPieces)
   1113     return nullptr;
   1114   if (index < 0 || index >= pTxtPieces->GetSize())
   1115     return nullptr;
   1116   return pTxtPieces->GetPtrAt(index);
   1117 }
   1118 
   1119 void CFX_TxtBreak::ClearBreakPieces() {
   1120   CFX_TxtLine* pTxtLine = GetTxtLine();
   1121   if (pTxtLine)
   1122     pTxtLine->RemoveAll(true);
   1123   m_iReady = 0;
   1124 }
   1125 
   1126 void CFX_TxtBreak::Reset() {
   1127   m_eCharType = FX_CHARTYPE_Unknown;
   1128   m_iArabicContext = 1;
   1129   m_iCurArabicContext = 1;
   1130   ResetArabicContext();
   1131   m_pTxtLine1->RemoveAll(true);
   1132   m_pTxtLine2->RemoveAll(true);
   1133 }
   1134 
   1135 struct FX_FORMCHAR {
   1136   uint16_t wch;
   1137   uint16_t wForm;
   1138   int32_t iWidth;
   1139 };
   1140 
   1141 int32_t CFX_TxtBreak::GetDisplayPos(const FX_TXTRUN* pTxtRun,
   1142                                     FXTEXT_CHARPOS* pCharPos,
   1143                                     bool bCharCode,
   1144                                     CFX_WideString* pWSForms) const {
   1145   if (!pTxtRun || pTxtRun->iLength < 1)
   1146     return 0;
   1147 
   1148   IFX_TxtAccess* pAccess = pTxtRun->pAccess;
   1149   const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity;
   1150   const FX_WCHAR* pStr = pTxtRun->wsStr.c_str();
   1151   int32_t* pWidths = pTxtRun->pWidths;
   1152   int32_t iLength = pTxtRun->iLength - 1;
   1153   CFX_RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
   1154   uint32_t dwStyles = pTxtRun->dwStyles;
   1155   CFX_RectF rtText(*pTxtRun->pRect);
   1156   bool bRTLPiece = (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
   1157   bool bArabicNumber =
   1158       (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_ArabicNumber) != 0;
   1159   bool bArabicComma =
   1160       (pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_ArabicComma) != 0;
   1161   FX_FLOAT fFontSize = pTxtRun->fFontSize;
   1162   int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
   1163   int32_t iAscent = pFont->GetAscent();
   1164   int32_t iDescent = pFont->GetDescent();
   1165   int32_t iMaxHeight = iAscent - iDescent;
   1166   FX_FLOAT fFontHeight = fFontSize;
   1167   FX_FLOAT fAscent = fFontHeight * (FX_FLOAT)iAscent / (FX_FLOAT)iMaxHeight;
   1168   FX_FLOAT fDescent = fFontHeight * (FX_FLOAT)iDescent / (FX_FLOAT)iMaxHeight;
   1169   bool bVerticalDoc = (dwStyles & FX_TXTLAYOUTSTYLE_VerticalLayout) != 0;
   1170   bool bVerticalChar = (dwStyles & FX_TXTLAYOUTSTYLE_VerticalChars) != 0;
   1171   int32_t iRotation = GetLineRotation(dwStyles) + pTxtRun->iCharRotation;
   1172   FX_FLOAT fX = rtText.left;
   1173   FX_FLOAT fY;
   1174   FX_FLOAT fCharWidth;
   1175   FX_FLOAT fCharHeight;
   1176   int32_t iHorScale = pTxtRun->iHorizontalScale;
   1177   int32_t iVerScale = pTxtRun->iVerticalScale;
   1178   bool bSkipSpace = pTxtRun->bSkipSpace;
   1179   FX_FORMCHAR formChars[3];
   1180   FX_FLOAT fYBase;
   1181 
   1182   if (bVerticalDoc) {
   1183     fX += (rtText.width - fFontSize) / 2.0f;
   1184     fYBase = bRTLPiece ? rtText.bottom() : rtText.top;
   1185     fY = fYBase;
   1186   } else {
   1187     if (bRTLPiece)
   1188       fX = rtText.right();
   1189 
   1190     fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
   1191     fY = fYBase + fAscent;
   1192   }
   1193 
   1194   int32_t iCount = 0;
   1195   int32_t iNext = 0;
   1196   FX_WCHAR wPrev = 0xFEFF;
   1197   FX_WCHAR wNext = 0xFEFF;
   1198   FX_WCHAR wForm = 0xFEFF;
   1199   FX_WCHAR wLast = 0xFEFF;
   1200   bool bShadda = false;
   1201   bool bLam = false;
   1202   for (int32_t i = 0; i <= iLength; i++) {
   1203     int32_t iWidth;
   1204     FX_WCHAR wch;
   1205     if (pAccess) {
   1206       wch = pAccess->GetChar(pIdentity, i);
   1207       iWidth = pAccess->GetWidth(pIdentity, i);
   1208     } else {
   1209       wch = *pStr++;
   1210       iWidth = *pWidths++;
   1211     }
   1212 
   1213     uint32_t dwProps = FX_GetUnicodeProperties(wch);
   1214     FX_CHARTYPE chartype = GetCharTypeFromProp(dwProps);
   1215     if (chartype == FX_CHARTYPE_ArabicAlef && iWidth == 0) {
   1216       wPrev = 0xFEFF;
   1217       wLast = wch;
   1218       continue;
   1219     }
   1220 
   1221     if (chartype >= FX_CHARTYPE_ArabicAlef) {
   1222       if (i < iLength) {
   1223         if (pAccess) {
   1224           iNext = i + 1;
   1225           while (iNext <= iLength) {
   1226             wNext = pAccess->GetChar(pIdentity, iNext);
   1227             dwProps = FX_GetUnicodeProperties(wNext);
   1228             if ((dwProps & FX_CHARTYPEBITSMASK) != FX_CHARTYPE_Combination)
   1229               break;
   1230 
   1231             iNext++;
   1232           }
   1233           if (iNext > iLength)
   1234             wNext = 0xFEFF;
   1235         } else {
   1236           int32_t j = -1;
   1237           do {
   1238             j++;
   1239             if (i + j >= iLength)
   1240               break;
   1241 
   1242             wNext = pStr[j];
   1243             dwProps = FX_GetUnicodeProperties(wNext);
   1244           } while ((dwProps & FX_CHARTYPEBITSMASK) == FX_CHARTYPE_Combination);
   1245           if (i + j >= iLength)
   1246             wNext = 0xFEFF;
   1247         }
   1248       } else {
   1249         wNext = 0xFEFF;
   1250       }
   1251 
   1252       wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
   1253       bLam = (wPrev == 0x0644 && wch == 0x0644 && wNext == 0x0647);
   1254     } else if (chartype == FX_CHARTYPE_Combination) {
   1255       wForm = wch;
   1256       if (wch >= 0x064C && wch <= 0x0651) {
   1257         if (bShadda) {
   1258           wForm = 0xFEFF;
   1259           bShadda = false;
   1260         } else {
   1261           wNext = 0xFEFF;
   1262           if (pAccess) {
   1263             iNext = i + 1;
   1264             if (iNext <= iLength)
   1265               wNext = pAccess->GetChar(pIdentity, iNext);
   1266           } else {
   1267             if (i < iLength)
   1268               wNext = *pStr;
   1269           }
   1270           if (wch == 0x0651) {
   1271             if (wNext >= 0x064C && wNext <= 0x0650) {
   1272               wForm = FX_GetArabicFromShaddaTable(wNext);
   1273               bShadda = true;
   1274             }
   1275           } else {
   1276             if (wNext == 0x0651) {
   1277               wForm = FX_GetArabicFromShaddaTable(wch);
   1278               bShadda = true;
   1279             }
   1280           }
   1281         }
   1282       } else {
   1283         bShadda = false;
   1284       }
   1285     } else if (chartype == FX_CHARTYPE_Numeric) {
   1286       wForm = wch;
   1287       if (bArabicNumber)
   1288         wForm += 0x0630;
   1289     } else if (wch == L'.') {
   1290       wForm = wch;
   1291       if (bArabicNumber) {
   1292         wNext = 0xFEFF;
   1293         if (pAccess) {
   1294           iNext = i + 1;
   1295           if (iNext <= iLength)
   1296             wNext = pAccess->GetChar(pIdentity, iNext);
   1297         } else {
   1298           if (i < iLength)
   1299             wNext = *pStr;
   1300         }
   1301         if (wNext >= L'0' && wNext <= L'9')
   1302           wForm = 0x066B;
   1303       }
   1304     } else if (wch == L',') {
   1305       wForm = wch;
   1306       if (bArabicComma)
   1307         wForm = 0x060C;
   1308     } else if (bRTLPiece || bVerticalChar) {
   1309       wForm = FX_GetMirrorChar(wch, dwProps, bRTLPiece, bVerticalChar);
   1310     } else {
   1311       wForm = wch;
   1312     }
   1313     if (chartype != FX_CHARTYPE_Combination)
   1314       bShadda = false;
   1315     if (chartype < FX_CHARTYPE_ArabicAlef)
   1316       bLam = false;
   1317 
   1318     dwProps = FX_GetUnicodeProperties(wForm);
   1319     int32_t iCharRotation = iRotation;
   1320     if (bVerticalChar && (dwProps & 0x8000) != 0)
   1321       iCharRotation++;
   1322 
   1323     iCharRotation %= 4;
   1324     bool bEmptyChar =
   1325         (chartype >= FX_CHARTYPE_Tab && chartype <= FX_CHARTYPE_Control);
   1326     if (wForm == 0xFEFF)
   1327       bEmptyChar = true;
   1328 
   1329     int32_t iForms = bLam ? 3 : 1;
   1330     iCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
   1331     if (!pCharPos) {
   1332       if (iWidth > 0)
   1333         wPrev = wch;
   1334       wLast = wch;
   1335       continue;
   1336     }
   1337 
   1338     int32_t iCharWidth = iWidth;
   1339     if (iCharWidth < 0)
   1340       iCharWidth = -iCharWidth;
   1341 
   1342     iCharWidth /= iFontSize;
   1343     formChars[0].wch = wch;
   1344     formChars[0].wForm = wForm;
   1345     formChars[0].iWidth = iCharWidth;
   1346     if (bLam) {
   1347       formChars[1].wForm = 0x0651;
   1348       iCharWidth = 0;
   1349       pFont->GetCharWidth(0x0651, iCharWidth, false);
   1350       formChars[1].iWidth = iCharWidth;
   1351       formChars[2].wForm = 0x0670;
   1352       iCharWidth = 0;
   1353       pFont->GetCharWidth(0x0670, iCharWidth, false);
   1354       formChars[2].iWidth = iCharWidth;
   1355     }
   1356 
   1357     for (int32_t j = 0; j < iForms; j++) {
   1358       wForm = (FX_WCHAR)formChars[j].wForm;
   1359       iCharWidth = formChars[j].iWidth;
   1360       if (j > 0) {
   1361         chartype = FX_CHARTYPE_Combination;
   1362         wch = wForm;
   1363         wLast = (FX_WCHAR)formChars[j - 1].wForm;
   1364       }
   1365       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
   1366         pCharPos->m_GlyphIndex =
   1367             bCharCode ? wch : pFont->GetGlyphIndex(wForm, false);
   1368 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
   1369         pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
   1370 #endif
   1371         pCharPos->m_FontCharWidth = iCharWidth;
   1372         if (pWSForms)
   1373           *pWSForms += wForm;
   1374       }
   1375 
   1376       int32_t iCharHeight;
   1377       if (bVerticalDoc) {
   1378         iCharHeight = iCharWidth;
   1379         iCharWidth = 1000;
   1380       } else {
   1381         iCharHeight = 1000;
   1382       }
   1383 
   1384       fCharWidth = fFontSize * iCharWidth / 1000.0f;
   1385       fCharHeight = fFontSize * iCharHeight / 1000.0f;
   1386       if (bRTLPiece && chartype != FX_CHARTYPE_Combination) {
   1387         if (bVerticalDoc)
   1388           fY -= fCharHeight;
   1389         else
   1390           fX -= fCharWidth;
   1391       }
   1392       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
   1393         pCharPos->m_Origin = CFX_PointF(fX, fY);
   1394         if ((dwStyles & FX_TXTLAYOUTSTYLE_CombText) != 0) {
   1395           int32_t iFormWidth = iCharWidth;
   1396           pFont->GetCharWidth(wForm, iFormWidth, false);
   1397           FX_FLOAT fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
   1398           if (bVerticalDoc)
   1399             pCharPos->m_Origin.y += fOffset;
   1400           else
   1401             pCharPos->m_Origin.x += fOffset;
   1402         }
   1403 
   1404         if (chartype == FX_CHARTYPE_Combination) {
   1405           CFX_Rect rtBBox;
   1406           if (pFont->GetCharBBox(wForm, &rtBBox, false)) {
   1407             pCharPos->m_Origin.y =
   1408                 fYBase + fFontSize -
   1409                 fFontSize * (FX_FLOAT)rtBBox.height / (FX_FLOAT)iMaxHeight;
   1410           }
   1411           if (wForm == wch && wLast != 0xFEFF) {
   1412             uint32_t dwLastProps = FX_GetUnicodeProperties(wLast);
   1413             if ((dwLastProps & FX_CHARTYPEBITSMASK) ==
   1414                 FX_CHARTYPE_Combination) {
   1415               CFX_Rect rtBox;
   1416               if (pFont->GetCharBBox(wLast, &rtBox, false))
   1417                 pCharPos->m_Origin.y -= fFontSize * rtBox.height / iMaxHeight;
   1418             }
   1419           }
   1420         }
   1421         CFX_PointF ptOffset;
   1422         if (bVerticalChar && (dwProps & 0x00010000) != 0) {
   1423           CFX_Rect rtBBox;
   1424           if (pFont->GetCharBBox(wForm, &rtBBox, false)) {
   1425             ptOffset.x = fFontSize * (850 - rtBBox.right()) / iMaxHeight;
   1426             ptOffset.y = fFontSize * (iAscent - rtBBox.top - 150) / iMaxHeight;
   1427           }
   1428         }
   1429         pCharPos->m_Origin.x += ptOffset.x;
   1430         pCharPos->m_Origin.y -= ptOffset.y;
   1431       }
   1432       if (!bRTLPiece && chartype != FX_CHARTYPE_Combination) {
   1433         if (bVerticalDoc)
   1434           fY += fCharHeight;
   1435         else
   1436           fX += fCharWidth;
   1437       }
   1438 
   1439       if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
   1440         pCharPos->m_bGlyphAdjust = true;
   1441         if (bVerticalDoc) {
   1442           if (iCharRotation == 0) {
   1443             pCharPos->m_AdjustMatrix[0] = -1;
   1444             pCharPos->m_AdjustMatrix[1] = 0;
   1445             pCharPos->m_AdjustMatrix[2] = 0;
   1446             pCharPos->m_AdjustMatrix[3] = 1;
   1447             pCharPos->m_Origin.y += fAscent;
   1448           } else if (iCharRotation == 1) {
   1449             pCharPos->m_AdjustMatrix[0] = 0;
   1450             pCharPos->m_AdjustMatrix[1] = -1;
   1451             pCharPos->m_AdjustMatrix[2] = -1;
   1452             pCharPos->m_AdjustMatrix[3] = 0;
   1453             pCharPos->m_Origin.x -= fDescent;
   1454           } else if (iCharRotation == 2) {
   1455             pCharPos->m_AdjustMatrix[0] = 1;
   1456             pCharPos->m_AdjustMatrix[1] = 0;
   1457             pCharPos->m_AdjustMatrix[2] = 0;
   1458             pCharPos->m_AdjustMatrix[3] = -1;
   1459             pCharPos->m_Origin.x += fCharWidth;
   1460             pCharPos->m_Origin.y += fAscent;
   1461           } else {
   1462             pCharPos->m_AdjustMatrix[0] = 0;
   1463             pCharPos->m_AdjustMatrix[1] = 1;
   1464             pCharPos->m_AdjustMatrix[2] = 1;
   1465             pCharPos->m_AdjustMatrix[3] = 0;
   1466             pCharPos->m_Origin.x += fAscent;
   1467           }
   1468         } else {
   1469           if (iCharRotation == 0) {
   1470             pCharPos->m_AdjustMatrix[0] = -1;
   1471             pCharPos->m_AdjustMatrix[1] = 0;
   1472             pCharPos->m_AdjustMatrix[2] = 0;
   1473             pCharPos->m_AdjustMatrix[3] = 1;
   1474           } else if (iCharRotation == 1) {
   1475             pCharPos->m_AdjustMatrix[0] = 0;
   1476             pCharPos->m_AdjustMatrix[1] = -1;
   1477             pCharPos->m_AdjustMatrix[2] = -1;
   1478             pCharPos->m_AdjustMatrix[3] = 0;
   1479             pCharPos->m_Origin.x -= fDescent;
   1480             pCharPos->m_Origin.y -= fAscent + fDescent;
   1481           } else if (iCharRotation == 2) {
   1482             pCharPos->m_AdjustMatrix[0] = 1;
   1483             pCharPos->m_AdjustMatrix[1] = 0;
   1484             pCharPos->m_AdjustMatrix[2] = 0;
   1485             pCharPos->m_AdjustMatrix[3] = -1;
   1486             pCharPos->m_Origin.x += fCharWidth;
   1487             pCharPos->m_Origin.y -= fAscent;
   1488           } else {
   1489             pCharPos->m_AdjustMatrix[0] = 0;
   1490             pCharPos->m_AdjustMatrix[1] = 1;
   1491             pCharPos->m_AdjustMatrix[2] = 1;
   1492             pCharPos->m_AdjustMatrix[3] = 0;
   1493             pCharPos->m_Origin.x += fAscent;
   1494           }
   1495         }
   1496         if (iHorScale != 100 || iVerScale != 100) {
   1497           pCharPos->m_AdjustMatrix[0] =
   1498               pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
   1499           pCharPos->m_AdjustMatrix[1] =
   1500               pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
   1501           pCharPos->m_AdjustMatrix[2] =
   1502               pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
   1503           pCharPos->m_AdjustMatrix[3] =
   1504               pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
   1505         }
   1506         pCharPos++;
   1507       }
   1508     }
   1509     if (iWidth > 0)
   1510       wPrev = static_cast<FX_WCHAR>(formChars[0].wch);
   1511     wLast = wch;
   1512   }
   1513   return iCount;
   1514 }
   1515 
   1516 std::vector<CFX_RectF> CFX_TxtBreak::GetCharRects(const FX_TXTRUN* pTxtRun,
   1517                                                   bool bCharBBox) const {
   1518   if (!pTxtRun || pTxtRun->iLength < 1)
   1519     return std::vector<CFX_RectF>();
   1520 
   1521   IFX_TxtAccess* pAccess = pTxtRun->pAccess;
   1522   const FDE_TEXTEDITPIECE* pIdentity = pTxtRun->pIdentity;
   1523   const FX_WCHAR* pStr = pTxtRun->wsStr.c_str();
   1524   int32_t* pWidths = pTxtRun->pWidths;
   1525   int32_t iLength = pTxtRun->iLength;
   1526   CFX_RectF rect(*pTxtRun->pRect);
   1527   FX_FLOAT fFontSize = pTxtRun->fFontSize;
   1528   int32_t iFontSize = FXSYS_round(fFontSize * 20.0f);
   1529   FX_FLOAT fScale = fFontSize / 1000.0f;
   1530   CFX_RetainPtr<CFGAS_GEFont> pFont = pTxtRun->pFont;
   1531   if (!pFont)
   1532     bCharBBox = false;
   1533 
   1534   CFX_Rect bbox;
   1535   if (bCharBBox)
   1536     bCharBBox = pFont->GetBBox(&bbox);
   1537 
   1538   FX_FLOAT fLeft = std::max(0.0f, bbox.left * fScale);
   1539   FX_FLOAT fHeight = FXSYS_fabs(bbox.height * fScale);
   1540   bool bRTLPiece = !!(pTxtRun->dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
   1541   bool bVertical = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_VerticalLayout);
   1542   bool bSingleLine = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_SingleLine);
   1543   bool bCombText = !!(pTxtRun->dwStyles & FX_TXTLAYOUTSTYLE_CombText);
   1544   FX_WCHAR wch;
   1545   FX_WCHAR wLineBreakChar = pTxtRun->wLineBreakChar;
   1546   int32_t iCharSize;
   1547   FX_FLOAT fCharSize;
   1548   FX_FLOAT fStart;
   1549   if (bVertical)
   1550     fStart = bRTLPiece ? rect.bottom() : rect.top;
   1551   else
   1552     fStart = bRTLPiece ? rect.right() : rect.left;
   1553 
   1554   std::vector<CFX_RectF> rtArray(iLength);
   1555   for (int32_t i = 0; i < iLength; i++) {
   1556     if (pAccess) {
   1557       wch = pAccess->GetChar(pIdentity, i);
   1558       iCharSize = pAccess->GetWidth(pIdentity, i);
   1559     } else {
   1560       wch = *pStr++;
   1561       iCharSize = *pWidths++;
   1562     }
   1563     fCharSize = static_cast<FX_FLOAT>(iCharSize) / 20000.0f;
   1564     bool bRet = (!bSingleLine && FX_IsCtrlCode(wch));
   1565     if (!(wch == L'\v' || wch == L'\f' || wch == 0x2028 || wch == 0x2029 ||
   1566           (wLineBreakChar != 0xFEFF && wch == wLineBreakChar))) {
   1567       bRet = false;
   1568     }
   1569     if (bRet) {
   1570       iCharSize = iFontSize * 500;
   1571       fCharSize = fFontSize / 2.0f;
   1572     }
   1573     if (bVertical) {
   1574       rect.top = fStart;
   1575       if (bRTLPiece) {
   1576         rect.top -= fCharSize;
   1577         fStart -= fCharSize;
   1578       } else {
   1579         fStart += fCharSize;
   1580       }
   1581       rect.height = fCharSize;
   1582     } else {
   1583       rect.left = fStart;
   1584       if (bRTLPiece) {
   1585         rect.left -= fCharSize;
   1586         fStart -= fCharSize;
   1587       } else {
   1588         fStart += fCharSize;
   1589       }
   1590       rect.width = fCharSize;
   1591     }
   1592 
   1593     if (bCharBBox && !bRet) {
   1594       int32_t iCharWidth = 1000;
   1595       pFont->GetCharWidth(wch, iCharWidth, false);
   1596       FX_FLOAT fRTLeft = 0, fCharWidth = 0;
   1597       if (iCharWidth > 0) {
   1598         fCharWidth = iCharWidth * fScale;
   1599         fRTLeft = fLeft;
   1600         if (bCombText)
   1601           fRTLeft = (rect.width - fCharWidth) / 2.0f;
   1602       }
   1603       CFX_RectF rtBBoxF;
   1604       if (bVertical) {
   1605         rtBBoxF.top = rect.left + fRTLeft;
   1606         rtBBoxF.left = rect.top + (rect.height - fHeight) / 2.0f;
   1607         rtBBoxF.height = fCharWidth;
   1608         rtBBoxF.width = fHeight;
   1609         rtBBoxF.left = std::max(rtBBoxF.left, 0.0f);
   1610       } else {
   1611         rtBBoxF.left = rect.left + fRTLeft;
   1612         rtBBoxF.top = rect.top + (rect.height - fHeight) / 2.0f;
   1613         rtBBoxF.width = fCharWidth;
   1614         rtBBoxF.height = fHeight;
   1615         rtBBoxF.top = std::max(rtBBoxF.top, 0.0f);
   1616       }
   1617       rtArray[i] = rtBBoxF;
   1618       continue;
   1619     }
   1620     rtArray[i] = rect;
   1621   }
   1622   return rtArray;
   1623 }
   1624 
   1625 FX_TXTRUN::FX_TXTRUN()
   1626     : pAccess(nullptr),
   1627       pIdentity(nullptr),
   1628       pWidths(nullptr),
   1629       iLength(0),
   1630       pFont(nullptr),
   1631       fFontSize(12),
   1632       dwStyles(0),
   1633       iHorizontalScale(100),
   1634       iVerticalScale(100),
   1635       iCharRotation(0),
   1636       dwCharStyles(0),
   1637       pRect(nullptr),
   1638       wLineBreakChar(L'\n'),
   1639       bSkipSpace(true) {}
   1640 
   1641 FX_TXTRUN::~FX_TXTRUN() {}
   1642 
   1643 FX_TXTRUN::FX_TXTRUN(const FX_TXTRUN& other) = default;
   1644 
   1645 CFX_TxtPiece::CFX_TxtPiece()
   1646     : m_dwStatus(FX_TXTBREAK_PieceBreak),
   1647       m_iStartPos(0),
   1648       m_iWidth(-1),
   1649       m_iStartChar(0),
   1650       m_iChars(0),
   1651       m_iBidiLevel(0),
   1652       m_iBidiPos(0),
   1653       m_iHorizontalScale(100),
   1654       m_iVerticalScale(100),
   1655       m_dwCharStyles(0),
   1656       m_pChars(nullptr),
   1657       m_pUserData(nullptr) {}
   1658 
   1659 CFX_TxtLine::CFX_TxtLine(int32_t iBlockSize)
   1660     : m_pLineChars(new std::vector<CFX_TxtChar>),
   1661       m_pLinePieces(new CFX_TxtPieceArray(16)),
   1662       m_iStart(0),
   1663       m_iWidth(0),
   1664       m_iArabicChars(0) {}
   1665 
   1666 CFX_TxtLine::~CFX_TxtLine() {
   1667   RemoveAll();
   1668 }
   1669