Home | History | Annotate | Download | only in app
      1 // Copyright 2017 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/fxfa/app/cxfa_textlayout.h"
      8 
      9 #include <algorithm>
     10 #include <utility>
     11 
     12 #include "third_party/base/ptr_util.h"
     13 #include "third_party/base/stl_util.h"
     14 #include "xfa/fde/cfde_path.h"
     15 #include "xfa/fde/css/cfde_csscomputedstyle.h"
     16 #include "xfa/fde/css/cfde_cssstyleselector.h"
     17 #include "xfa/fde/fde_gedevice.h"
     18 #include "xfa/fde/fde_object.h"
     19 #include "xfa/fde/xml/fde_xml_imp.h"
     20 #include "xfa/fxfa/app/cxfa_linkuserdata.h"
     21 #include "xfa/fxfa/app/cxfa_loadercontext.h"
     22 #include "xfa/fxfa/app/cxfa_pieceline.h"
     23 #include "xfa/fxfa/app/cxfa_textparsecontext.h"
     24 #include "xfa/fxfa/app/cxfa_texttabstopscontext.h"
     25 #include "xfa/fxfa/app/cxfa_textuserdata.h"
     26 #include "xfa/fxfa/app/xfa_ffwidgetacc.h"
     27 #include "xfa/fxfa/app/xfa_textpiece.h"
     28 #include "xfa/fxfa/parser/cxfa_font.h"
     29 #include "xfa/fxfa/parser/cxfa_para.h"
     30 #include "xfa/fxfa/parser/xfa_object.h"
     31 
     32 #define XFA_LOADERCNTXTFLG_FILTERSPACE 0x001
     33 
     34 CXFA_TextLayout::CXFA_TextLayout(CXFA_TextProvider* pTextProvider)
     35     : m_bHasBlock(false),
     36       m_pTextProvider(pTextProvider),
     37       m_pTextDataNode(nullptr),
     38       m_bRichText(false),
     39       m_iLines(0),
     40       m_fMaxWidth(0),
     41       m_bBlockContinue(true) {
     42   ASSERT(m_pTextProvider);
     43 }
     44 
     45 CXFA_TextLayout::~CXFA_TextLayout() {
     46   m_textParser.Reset();
     47   Unload();
     48 }
     49 
     50 void CXFA_TextLayout::Unload() {
     51   m_pieceLines.clear();
     52   m_pBreak.reset();
     53 }
     54 
     55 void CXFA_TextLayout::GetTextDataNode() {
     56   if (!m_pTextProvider)
     57     return;
     58 
     59   CXFA_Node* pNode = m_pTextProvider->GetTextNode(m_bRichText);
     60   if (pNode && m_bRichText)
     61     m_textParser.Reset();
     62 
     63   m_pTextDataNode = pNode;
     64 }
     65 
     66 CFDE_XMLNode* CXFA_TextLayout::GetXMLContainerNode() {
     67   if (!m_bRichText)
     68     return nullptr;
     69 
     70   CFDE_XMLNode* pXMLRoot = m_pTextDataNode->GetXMLMappingNode();
     71   if (!pXMLRoot)
     72     return nullptr;
     73 
     74   CFDE_XMLNode* pXMLContainer = nullptr;
     75   for (CFDE_XMLNode* pXMLChild =
     76            pXMLRoot->GetNodeItem(CFDE_XMLNode::FirstChild);
     77        pXMLChild;
     78        pXMLChild = pXMLChild->GetNodeItem(CFDE_XMLNode::NextSibling)) {
     79     if (pXMLChild->GetType() == FDE_XMLNODE_Element) {
     80       CFDE_XMLElement* pXMLElement = static_cast<CFDE_XMLElement*>(pXMLChild);
     81       CFX_WideString wsTag;
     82       pXMLElement->GetLocalTagName(wsTag);
     83       if (wsTag == L"body" || wsTag == L"html") {
     84         pXMLContainer = pXMLChild;
     85         break;
     86       }
     87     }
     88   }
     89   return pXMLContainer;
     90 }
     91 
     92 std::unique_ptr<CFX_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) {
     93   uint32_t dwStyle = FX_RTFLAYOUTSTYLE_ExpandTab;
     94   if (!bDefault)
     95     dwStyle |= FX_RTFLAYOUTSTYLE_Pagination;
     96 
     97   auto pBreak = pdfium::MakeUnique<CFX_RTFBreak>(dwStyle);
     98   pBreak->SetLineBreakTolerance(1);
     99   pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, nullptr));
    100   pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr));
    101   return pBreak;
    102 }
    103 
    104 void CXFA_TextLayout::InitBreak(FX_FLOAT fLineWidth) {
    105   CXFA_Font font = m_pTextProvider->GetFontNode();
    106   CXFA_Para para = m_pTextProvider->GetParaNode();
    107   FX_FLOAT fStart = 0;
    108   FX_FLOAT fStartPos = 0;
    109   if (para) {
    110     CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
    111     switch (para.GetHorizontalAlign()) {
    112       case XFA_ATTRIBUTEENUM_Center:
    113         iAlign = CFX_RTFLineAlignment::Center;
    114         break;
    115       case XFA_ATTRIBUTEENUM_Right:
    116         iAlign = CFX_RTFLineAlignment::Right;
    117         break;
    118       case XFA_ATTRIBUTEENUM_Justify:
    119         iAlign = CFX_RTFLineAlignment::Justified;
    120         break;
    121       case XFA_ATTRIBUTEENUM_JustifyAll:
    122         iAlign = CFX_RTFLineAlignment::Distributed;
    123         break;
    124     }
    125     m_pBreak->SetAlignment(iAlign);
    126 
    127     fStart = para.GetMarginLeft();
    128     if (m_pTextProvider->IsCheckButtonAndAutoWidth()) {
    129       if (iAlign != CFX_RTFLineAlignment::Left)
    130         fLineWidth -= para.GetMarginRight();
    131     } else {
    132       fLineWidth -= para.GetMarginRight();
    133     }
    134     if (fLineWidth < 0)
    135       fLineWidth = fStart;
    136 
    137     fStartPos = fStart;
    138     FX_FLOAT fIndent = para.GetTextIndent();
    139     if (fIndent > 0)
    140       fStartPos += fIndent;
    141   }
    142 
    143   m_pBreak->SetLineBoundary(fStart, fLineWidth);
    144   m_pBreak->SetLineStartPos(fStartPos);
    145   if (font) {
    146     m_pBreak->SetHorizontalScale((int32_t)font.GetHorizontalScale());
    147     m_pBreak->SetVerticalScale((int32_t)font.GetVerticalScale());
    148     m_pBreak->SetCharSpace(font.GetLetterSpacing());
    149   }
    150 
    151   FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, nullptr);
    152   m_pBreak->SetFontSize(fFontSize);
    153   m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, nullptr));
    154   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
    155 }
    156 
    157 void CXFA_TextLayout::InitBreak(CFDE_CSSComputedStyle* pStyle,
    158                                 FDE_CSSDisplay eDisplay,
    159                                 FX_FLOAT fLineWidth,
    160                                 CFDE_XMLNode* pXMLNode,
    161                                 CFDE_CSSComputedStyle* pParentStyle) {
    162   if (!pStyle) {
    163     InitBreak(fLineWidth);
    164     return;
    165   }
    166 
    167   if (eDisplay == FDE_CSSDisplay::Block ||
    168       eDisplay == FDE_CSSDisplay::ListItem) {
    169     CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
    170     switch (pStyle->GetTextAlign()) {
    171       case FDE_CSSTextAlign::Right:
    172         iAlign = CFX_RTFLineAlignment::Right;
    173         break;
    174       case FDE_CSSTextAlign::Center:
    175         iAlign = CFX_RTFLineAlignment::Center;
    176         break;
    177       case FDE_CSSTextAlign::Justify:
    178         iAlign = CFX_RTFLineAlignment::Justified;
    179         break;
    180       case FDE_CSSTextAlign::JustifyAll:
    181         iAlign = CFX_RTFLineAlignment::Distributed;
    182         break;
    183       default:
    184         break;
    185     }
    186     m_pBreak->SetAlignment(iAlign);
    187 
    188     FX_FLOAT fStart = 0;
    189     const FDE_CSSRect* pRect = pStyle->GetMarginWidth();
    190     const FDE_CSSRect* pPaddingRect = pStyle->GetPaddingWidth();
    191     if (pRect) {
    192       fStart = pRect->left.GetValue();
    193       fLineWidth -= pRect->right.GetValue();
    194       if (pPaddingRect) {
    195         fStart += pPaddingRect->left.GetValue();
    196         fLineWidth -= pPaddingRect->right.GetValue();
    197       }
    198       if (eDisplay == FDE_CSSDisplay::ListItem) {
    199         const FDE_CSSRect* pParRect = pParentStyle->GetMarginWidth();
    200         const FDE_CSSRect* pParPaddingRect = pParentStyle->GetPaddingWidth();
    201         if (pParRect) {
    202           fStart += pParRect->left.GetValue();
    203           fLineWidth -= pParRect->right.GetValue();
    204           if (pParPaddingRect) {
    205             fStart += pParPaddingRect->left.GetValue();
    206             fLineWidth -= pParPaddingRect->right.GetValue();
    207           }
    208         }
    209         FDE_CSSRect pNewRect;
    210         pNewRect.left.Set(FDE_CSSLengthUnit::Point, fStart);
    211         pNewRect.right.Set(FDE_CSSLengthUnit::Point, pRect->right.GetValue());
    212         pNewRect.top.Set(FDE_CSSLengthUnit::Point, pRect->top.GetValue());
    213         pNewRect.bottom.Set(FDE_CSSLengthUnit::Point, pRect->bottom.GetValue());
    214         pStyle->SetMarginWidth(pNewRect);
    215       }
    216     }
    217     m_pBreak->SetLineBoundary(fStart, fLineWidth);
    218     FX_FLOAT fIndent = pStyle->GetTextIndent().GetValue();
    219     if (fIndent > 0)
    220       fStart += fIndent;
    221 
    222     m_pBreak->SetLineStartPos(fStart);
    223     m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle));
    224     if (!m_pTabstopContext)
    225       m_pTabstopContext = pdfium::MakeUnique<CXFA_TextTabstopsContext>();
    226     m_textParser.GetTabstops(pStyle, m_pTabstopContext.get());
    227     for (int32_t i = 0; i < m_pTabstopContext->m_iTabCount; i++) {
    228       XFA_TABSTOPS* pTab = m_pTabstopContext->m_tabstops.GetDataPtr(i);
    229       m_pBreak->AddPositionedTab(pTab->fTabstops);
    230     }
    231   }
    232 
    233   FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
    234   m_pBreak->SetFontSize(fFontSize);
    235   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
    236   m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, pStyle));
    237   m_pBreak->SetHorizontalScale(
    238       m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode));
    239   m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle));
    240   m_pBreak->SetCharSpace(pStyle->GetLetterSpacing().GetValue());
    241 }
    242 
    243 int32_t CXFA_TextLayout::GetText(CFX_WideString& wsText) {
    244   GetTextDataNode();
    245   wsText.clear();
    246   if (!m_bRichText)
    247     wsText = m_pTextDataNode->GetContent();
    248   return wsText.GetLength();
    249 }
    250 
    251 FX_FLOAT CXFA_TextLayout::GetLayoutHeight() {
    252   if (!m_pLoader)
    253     return 0;
    254 
    255   int32_t iCount = m_pLoader->m_lineHeights.GetSize();
    256   if (iCount == 0 && m_pLoader->m_fWidth > 0) {
    257     CFX_SizeF szMax(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
    258     CFX_SizeF szDef;
    259     m_pLoader->m_bSaveLineHeight = true;
    260     m_pLoader->m_fLastPos = 0;
    261     CalcSize(szMax, szMax, szDef);
    262     m_pLoader->m_bSaveLineHeight = false;
    263     return szDef.height;
    264   }
    265 
    266   FX_FLOAT fHeight = m_pLoader->m_fHeight;
    267   if (fHeight < 0.1f) {
    268     fHeight = 0;
    269     for (int32_t i = 0; i < iCount; i++)
    270       fHeight += m_pLoader->m_lineHeights.ElementAt(i);
    271   }
    272   return fHeight;
    273 }
    274 
    275 FX_FLOAT CXFA_TextLayout::StartLayout(FX_FLOAT fWidth) {
    276   if (!m_pLoader)
    277     m_pLoader = pdfium::MakeUnique<CXFA_LoaderContext>();
    278 
    279   if (fWidth < 0 || (m_pLoader->m_fWidth > -1 &&
    280                      FXSYS_fabs(fWidth - m_pLoader->m_fWidth) > 0)) {
    281     m_pLoader->m_lineHeights.RemoveAll();
    282     m_Blocks.RemoveAll();
    283     Unload();
    284     m_pLoader->m_fStartLineOffset = 0;
    285   }
    286   m_pLoader->m_fWidth = fWidth;
    287 
    288   if (fWidth < 0) {
    289     CFX_SizeF szMax;
    290     CFX_SizeF szDef;
    291     m_pLoader->m_bSaveLineHeight = true;
    292     m_pLoader->m_fLastPos = 0;
    293     CalcSize(szMax, szMax, szDef);
    294     m_pLoader->m_bSaveLineHeight = false;
    295     fWidth = szDef.width;
    296   }
    297   return fWidth;
    298 }
    299 
    300 bool CXFA_TextLayout::DoLayout(int32_t iBlockIndex,
    301                                FX_FLOAT& fCalcHeight,
    302                                FX_FLOAT fContentAreaHeight,
    303                                FX_FLOAT fTextHeight) {
    304   if (!m_pLoader)
    305     return false;
    306 
    307   int32_t iBlockCount = m_Blocks.GetSize();
    308   FX_FLOAT fHeight = fTextHeight;
    309   if (fHeight < 0)
    310     fHeight = GetLayoutHeight();
    311 
    312   m_pLoader->m_fHeight = fHeight;
    313   if (fContentAreaHeight < 0)
    314     return false;
    315 
    316   m_bHasBlock = true;
    317   if (iBlockCount == 0 && fHeight > 0) {
    318     fHeight = fTextHeight - GetLayoutHeight();
    319     if (fHeight > 0) {
    320       int32_t iAlign = m_textParser.GetVAlign(m_pTextProvider);
    321       if (iAlign == XFA_ATTRIBUTEENUM_Middle)
    322         fHeight /= 2.0f;
    323       else if (iAlign != XFA_ATTRIBUTEENUM_Bottom)
    324         fHeight = 0;
    325       m_pLoader->m_fStartLineOffset = fHeight;
    326     }
    327   }
    328 
    329   FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset;
    330   int32_t iLineIndex = 0;
    331   if (iBlockCount > 1) {
    332     if (iBlockCount >= (iBlockIndex + 1) * 2) {
    333       iLineIndex = m_Blocks.ElementAt(iBlockIndex * 2);
    334     } else {
    335       iLineIndex = m_Blocks.ElementAt(iBlockCount - 1) +
    336                    m_Blocks.ElementAt(iBlockCount - 2);
    337     }
    338     if (!m_pLoader->m_BlocksHeight.empty()) {
    339       for (int32_t i = 0; i < iBlockIndex; i++)
    340         fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
    341     }
    342   }
    343 
    344   int32_t iCount = m_pLoader->m_lineHeights.GetSize();
    345   int32_t i = 0;
    346   for (i = iLineIndex; i < iCount; i++) {
    347     FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i);
    348     if ((i == iLineIndex) && (fLineHeight - fContentAreaHeight > 0.001)) {
    349       fCalcHeight = 0;
    350       return true;
    351     }
    352     if (fLinePos + fLineHeight - fContentAreaHeight > 0.001) {
    353       if (iBlockCount >= (iBlockIndex + 1) * 2) {
    354         m_Blocks.SetAt(iBlockIndex * 2, iLineIndex);
    355         m_Blocks.SetAt(iBlockIndex * 2 + 1, i - iLineIndex);
    356       } else {
    357         m_Blocks.Add(iLineIndex);
    358         m_Blocks.Add(i - iLineIndex);
    359       }
    360       if (i == iLineIndex) {
    361         if (fCalcHeight <= fLinePos) {
    362           if (pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight) >
    363                   iBlockIndex * 2 &&
    364               (m_pLoader->m_BlocksHeight[iBlockIndex * 2] == iBlockIndex)) {
    365             m_pLoader->m_BlocksHeight[iBlockIndex * 2 + 1] = fCalcHeight;
    366           } else {
    367             m_pLoader->m_BlocksHeight.push_back((FX_FLOAT)iBlockIndex);
    368             m_pLoader->m_BlocksHeight.push_back(fCalcHeight);
    369           }
    370         }
    371         return true;
    372       }
    373 
    374       fCalcHeight = fLinePos;
    375       return true;
    376     }
    377     fLinePos += fLineHeight;
    378   }
    379   return false;
    380 }
    381 
    382 int32_t CXFA_TextLayout::CountBlocks() const {
    383   int32_t iCount = m_Blocks.GetSize() / 2;
    384   return iCount > 0 ? iCount : 1;
    385 }
    386 
    387 bool CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize,
    388                                const CFX_SizeF& maxSize,
    389                                CFX_SizeF& defaultSize) {
    390   defaultSize.width = maxSize.width;
    391   if (defaultSize.width < 1)
    392     defaultSize.width = 0xFFFF;
    393 
    394   m_pBreak = CreateBreak(false);
    395   FX_FLOAT fLinePos = 0;
    396   m_iLines = 0;
    397   m_fMaxWidth = 0;
    398   Loader(defaultSize, fLinePos, false);
    399   if (fLinePos < 0.1f)
    400     fLinePos = m_textParser.GetFontSize(m_pTextProvider, nullptr);
    401 
    402   m_pTabstopContext.reset();
    403   defaultSize = CFX_SizeF(m_fMaxWidth, fLinePos);
    404   return true;
    405 }
    406 
    407 bool CXFA_TextLayout::Layout(const CFX_SizeF& size, FX_FLOAT* fHeight) {
    408   if (size.width < 1)
    409     return false;
    410 
    411   Unload();
    412   m_pBreak = CreateBreak(true);
    413   if (m_pLoader) {
    414     m_pLoader->m_iTotalLines = -1;
    415     m_pLoader->m_iChar = 0;
    416   }
    417 
    418   m_iLines = 0;
    419   FX_FLOAT fLinePos = 0;
    420   Loader(size, fLinePos, true);
    421   UpdateAlign(size.height, fLinePos);
    422   m_pTabstopContext.reset();
    423   if (fHeight)
    424     *fHeight = fLinePos;
    425   return true;
    426 }
    427 
    428 bool CXFA_TextLayout::Layout(int32_t iBlock) {
    429   if (!m_pLoader || iBlock < 0 || iBlock >= CountBlocks())
    430     return false;
    431   if (m_pLoader->m_fWidth < 1)
    432     return false;
    433 
    434   m_pLoader->m_iTotalLines = -1;
    435   m_iLines = 0;
    436   FX_FLOAT fLinePos = 0;
    437   CXFA_Node* pNode = nullptr;
    438   CFX_SizeF szText(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
    439   int32_t iCount = m_Blocks.GetSize();
    440   int32_t iBlocksHeightCount =
    441       pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight);
    442   iBlocksHeightCount /= 2;
    443   if (iBlock < iBlocksHeightCount)
    444     return true;
    445   if (iBlock == iBlocksHeightCount) {
    446     Unload();
    447     m_pBreak = CreateBreak(true);
    448     fLinePos = m_pLoader->m_fStartLineOffset;
    449     for (int32_t i = 0; i < iBlocksHeightCount; i++)
    450       fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
    451 
    452     m_pLoader->m_iChar = 0;
    453     if (iCount > 1)
    454       m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock * 2 + 1);
    455 
    456     Loader(szText, fLinePos, true);
    457     if (iCount == 0 && m_pLoader->m_fStartLineOffset < 0.1f)
    458       UpdateAlign(szText.height, fLinePos);
    459   } else if (m_pTextDataNode) {
    460     iBlock *= 2;
    461     if (iBlock < iCount - 2)
    462       m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock + 1);
    463 
    464     m_pBreak->Reset();
    465     if (m_bRichText) {
    466       CFDE_XMLNode* pContainerNode = GetXMLContainerNode();
    467       if (!pContainerNode)
    468         return true;
    469 
    470       CFDE_XMLNode* pXMLNode = m_pLoader->m_pXMLNode;
    471       if (!pXMLNode)
    472         return true;
    473 
    474       CFDE_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode;
    475       for (; pXMLNode;
    476            pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) {
    477         if (!LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle,
    478                           true, nullptr)) {
    479           break;
    480         }
    481       }
    482       while (!pXMLNode) {
    483         pXMLNode = pSaveXMLNode->GetNodeItem(CFDE_XMLNode::Parent);
    484         if (pXMLNode == pContainerNode)
    485           break;
    486         if (!LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle,
    487                           true, nullptr, false)) {
    488           break;
    489         }
    490         pSaveXMLNode = pXMLNode;
    491         pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling);
    492         if (!pXMLNode)
    493           continue;
    494         for (; pXMLNode;
    495              pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) {
    496           if (!LoadRichText(pXMLNode, szText, fLinePos,
    497                             m_pLoader->m_pParentStyle, true, nullptr)) {
    498             break;
    499           }
    500         }
    501       }
    502     } else {
    503       pNode = m_pLoader->m_pNode;
    504       if (!pNode)
    505         return true;
    506       LoadText(pNode, szText, fLinePos, true);
    507     }
    508   }
    509   if (iBlock == iCount) {
    510     m_pTabstopContext.reset();
    511     m_pLoader.reset();
    512   }
    513   return true;
    514 }
    515 
    516 void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex) {
    517   if (!m_pLoader)
    518     return;
    519 
    520   int32_t iCountHeight = m_pLoader->m_lineHeights.GetSize();
    521   if (iCountHeight == 0)
    522     return;
    523 
    524   bool bEndItem = true;
    525   int32_t iBlockCount = m_Blocks.GetSize();
    526   FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset;
    527   int32_t iLineIndex = 0;
    528   if (iBlockIndex > 0) {
    529     int32_t iBlockHeightCount =
    530         pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight);
    531     iBlockHeightCount /= 2;
    532     if (iBlockHeightCount >= iBlockIndex) {
    533       for (int32_t i = 0; i < iBlockIndex; i++)
    534         fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1];
    535     } else {
    536       fLinePos = 0;
    537     }
    538     iLineIndex = m_Blocks[iBlockCount - 1] + m_Blocks[iBlockCount - 2];
    539   }
    540 
    541   int32_t i = 0;
    542   for (i = iLineIndex; i < iCountHeight; i++) {
    543     FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i);
    544     if (fLinePos + fLineHeight - rtText.height > 0.001) {
    545       m_Blocks.Add(iLineIndex);
    546       m_Blocks.Add(i - iLineIndex);
    547       bEndItem = false;
    548       break;
    549     }
    550     fLinePos += fLineHeight;
    551   }
    552   if (iCountHeight > 0 && (i - iLineIndex) > 0 && bEndItem) {
    553     m_Blocks.Add(iLineIndex);
    554     m_Blocks.Add(i - iLineIndex);
    555   }
    556 }
    557 
    558 bool CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice,
    559                                  const CFX_Matrix& tmDoc2Device,
    560                                  const CFX_RectF& rtClip,
    561                                  int32_t iBlock) {
    562   if (!pFxDevice)
    563     return false;
    564 
    565   std::unique_ptr<CFDE_RenderDevice> pDevice(
    566       new CFDE_RenderDevice(pFxDevice, false));
    567   pDevice->SaveState();
    568   pDevice->SetClipRect(rtClip);
    569 
    570   auto pSolidBrush = pdfium::MakeUnique<CFDE_Brush>();
    571   auto pPen = pdfium::MakeUnique<CFDE_Pen>();
    572   if (m_pieceLines.empty()) {
    573     int32_t iBlockCount = CountBlocks();
    574     for (int32_t i = 0; i < iBlockCount; i++)
    575       Layout(i);
    576   }
    577 
    578   FXTEXT_CHARPOS* pCharPos = nullptr;
    579   int32_t iCharCount = 0;
    580   int32_t iLineStart = 0;
    581   int32_t iPieceLines = pdfium::CollectionSize<int32_t>(m_pieceLines);
    582   int32_t iCount = m_Blocks.GetSize();
    583   if (iCount > 0) {
    584     iBlock *= 2;
    585     if (iBlock < iCount) {
    586       iLineStart = m_Blocks.ElementAt(iBlock);
    587       iPieceLines = m_Blocks.ElementAt(iBlock + 1);
    588     } else {
    589       iPieceLines = 0;
    590     }
    591   }
    592 
    593   for (int32_t i = 0; i < iPieceLines; i++) {
    594     if (i + iLineStart >= pdfium::CollectionSize<int32_t>(m_pieceLines))
    595       break;
    596 
    597     CXFA_PieceLine* pPieceLine = m_pieceLines[i + iLineStart].get();
    598     int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
    599     int32_t j = 0;
    600     for (j = 0; j < iPieces; j++) {
    601       const XFA_TextPiece* pPiece = pPieceLine->m_textPieces[j].get();
    602       int32_t iChars = pPiece->iChars;
    603       if (iCharCount < iChars) {
    604         FX_Free(pCharPos);
    605         pCharPos = FX_Alloc(FXTEXT_CHARPOS, iChars);
    606         iCharCount = iChars;
    607       }
    608       FXSYS_memset(pCharPos, 0, iCharCount * sizeof(FXTEXT_CHARPOS));
    609       RenderString(pDevice.get(), pSolidBrush.get(), pPieceLine, j, pCharPos,
    610                    tmDoc2Device);
    611     }
    612     for (j = 0; j < iPieces; j++) {
    613       RenderPath(pDevice.get(), pPen.get(), pPieceLine, j, pCharPos,
    614                  tmDoc2Device);
    615     }
    616   }
    617   pDevice->RestoreState();
    618   FX_Free(pCharPos);
    619   return iPieceLines > 0;
    620 }
    621 
    622 void CXFA_TextLayout::UpdateAlign(FX_FLOAT fHeight, FX_FLOAT fBottom) {
    623   fHeight -= fBottom;
    624   if (fHeight < 0.1f)
    625     return;
    626 
    627   switch (m_textParser.GetVAlign(m_pTextProvider)) {
    628     case XFA_ATTRIBUTEENUM_Middle:
    629       fHeight /= 2.0f;
    630       break;
    631     case XFA_ATTRIBUTEENUM_Bottom:
    632       break;
    633     default:
    634       return;
    635   }
    636 
    637   for (const auto& pPieceLine : m_pieceLines) {
    638     for (const auto& pPiece : pPieceLine->m_textPieces)
    639       pPiece->rtPiece.top += fHeight;
    640   }
    641 }
    642 
    643 bool CXFA_TextLayout::Loader(const CFX_SizeF& szText,
    644                              FX_FLOAT& fLinePos,
    645                              bool bSavePieces) {
    646   GetTextDataNode();
    647   if (!m_pTextDataNode)
    648     return true;
    649 
    650   if (m_bRichText) {
    651     CFDE_XMLNode* pXMLContainer = GetXMLContainerNode();
    652     if (pXMLContainer) {
    653       if (!m_textParser.IsParsed())
    654         m_textParser.DoParse(pXMLContainer, m_pTextProvider);
    655 
    656       auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider);
    657       LoadRichText(pXMLContainer, szText, fLinePos, pRootStyle, bSavePieces,
    658                    nullptr);
    659     }
    660   } else {
    661     LoadText(m_pTextDataNode, szText, fLinePos, bSavePieces);
    662   }
    663   return true;
    664 }
    665 
    666 void CXFA_TextLayout::LoadText(CXFA_Node* pNode,
    667                                const CFX_SizeF& szText,
    668                                FX_FLOAT& fLinePos,
    669                                bool bSavePieces) {
    670   InitBreak(szText.width);
    671 
    672   CXFA_Para para = m_pTextProvider->GetParaNode();
    673   FX_FLOAT fSpaceAbove = 0;
    674   if (para) {
    675     fSpaceAbove = para.GetSpaceAbove();
    676     if (fSpaceAbove < 0.1f) {
    677       fSpaceAbove = 0;
    678     }
    679     int32_t verAlign = para.GetVerticalAlign();
    680     switch (verAlign) {
    681       case XFA_ATTRIBUTEENUM_Top:
    682       case XFA_ATTRIBUTEENUM_Middle:
    683       case XFA_ATTRIBUTEENUM_Bottom: {
    684         fLinePos += fSpaceAbove;
    685         break;
    686       }
    687     }
    688   }
    689 
    690   CFX_WideString wsText = pNode->GetContent();
    691   wsText.TrimRight(L" ");
    692   bool bRet = AppendChar(wsText, fLinePos, fSpaceAbove, bSavePieces);
    693   if (bRet && m_pLoader)
    694     m_pLoader->m_pNode = pNode;
    695   else
    696     EndBreak(CFX_RTFBreakType::Paragraph, fLinePos, bSavePieces);
    697 }
    698 
    699 bool CXFA_TextLayout::LoadRichText(
    700     CFDE_XMLNode* pXMLNode,
    701     const CFX_SizeF& szText,
    702     FX_FLOAT& fLinePos,
    703     const CFX_RetainPtr<CFDE_CSSComputedStyle>& pParentStyle,
    704     bool bSavePieces,
    705     CFX_RetainPtr<CXFA_LinkUserData> pLinkData,
    706     bool bEndBreak,
    707     bool bIsOl,
    708     int32_t iLiCount) {
    709   if (!pXMLNode)
    710     return false;
    711 
    712   CXFA_TextParseContext* pContext =
    713       m_textParser.GetParseContextFromMap(pXMLNode);
    714   FDE_CSSDisplay eDisplay = FDE_CSSDisplay::None;
    715   bool bContentNode = false;
    716   FX_FLOAT fSpaceBelow = 0;
    717   CFX_RetainPtr<CFDE_CSSComputedStyle> pStyle;
    718   CFX_WideString wsName;
    719   if (bEndBreak) {
    720     bool bCurOl = false;
    721     bool bCurLi = false;
    722     CFDE_XMLElement* pElement = nullptr;
    723     if (pContext) {
    724       if (m_bBlockContinue ||
    725           (m_pLoader && pXMLNode == m_pLoader->m_pXMLNode)) {
    726         m_bBlockContinue = true;
    727       }
    728       if (pXMLNode->GetType() == FDE_XMLNODE_Text) {
    729         bContentNode = true;
    730       } else if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
    731         pElement = static_cast<CFDE_XMLElement*>(pXMLNode);
    732         pElement->GetLocalTagName(wsName);
    733       }
    734       if (wsName == L"ol") {
    735         bIsOl = true;
    736         bCurOl = true;
    737       }
    738       if (m_bBlockContinue || bContentNode == false) {
    739         eDisplay = pContext->GetDisplay();
    740         if (eDisplay != FDE_CSSDisplay::Block &&
    741             eDisplay != FDE_CSSDisplay::Inline &&
    742             eDisplay != FDE_CSSDisplay::ListItem) {
    743           return true;
    744         }
    745 
    746         pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle.Get());
    747         InitBreak(bContentNode ? pParentStyle.Get() : pStyle.Get(), eDisplay,
    748                   szText.width, pXMLNode, pParentStyle.Get());
    749         if ((eDisplay == FDE_CSSDisplay::Block ||
    750              eDisplay == FDE_CSSDisplay::ListItem) &&
    751             pStyle &&
    752             (wsName.IsEmpty() || (wsName != L"body" && wsName != L"html" &&
    753                                   wsName != L"ol" && wsName != L"ul"))) {
    754           const FDE_CSSRect* pRect = pStyle->GetMarginWidth();
    755           if (pRect) {
    756             fLinePos += pRect->top.GetValue();
    757             fSpaceBelow = pRect->bottom.GetValue();
    758           }
    759         }
    760 
    761         if (wsName == L"a") {
    762           CFX_WideString wsLinkContent;
    763           ASSERT(pElement);
    764           pElement->GetString(L"href", wsLinkContent);
    765           if (!wsLinkContent.IsEmpty()) {
    766             pLinkData = pdfium::MakeRetain<CXFA_LinkUserData>(
    767                 wsLinkContent.GetBuffer(wsLinkContent.GetLength()));
    768             wsLinkContent.ReleaseBuffer(wsLinkContent.GetLength());
    769           }
    770         }
    771 
    772         int32_t iTabCount = m_textParser.CountTabs(
    773             bContentNode ? pParentStyle.Get() : pStyle.Get());
    774         bool bSpaceRun = m_textParser.IsSpaceRun(
    775             bContentNode ? pParentStyle.Get() : pStyle.Get());
    776         CFX_WideString wsText;
    777         if (bContentNode && iTabCount == 0) {
    778           static_cast<CFDE_XMLText*>(pXMLNode)->GetText(wsText);
    779         } else if (wsName == L"br") {
    780           wsText = L'\n';
    781         } else if (wsName == L"li") {
    782           bCurLi = true;
    783           if (bIsOl)
    784             wsText.Format(L"%d.  ", iLiCount);
    785           else
    786             wsText = 0x00B7 + CFX_WideStringC(L"  ", 1);
    787         } else if (!bContentNode) {
    788           if (iTabCount > 0) {
    789             while (iTabCount-- > 0)
    790               wsText += L'\t';
    791           } else {
    792             m_textParser.GetEmbbedObj(m_pTextProvider, pXMLNode, wsText);
    793           }
    794         }
    795 
    796         int32_t iLength = wsText.GetLength();
    797         if (iLength > 0 && bContentNode && !bSpaceRun)
    798           ProcessText(wsText);
    799 
    800         if (m_pLoader) {
    801           if (wsText.GetLength() > 0 &&
    802               (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
    803             wsText.TrimLeft(0x20);
    804           }
    805           if (FDE_CSSDisplay::Block == eDisplay) {
    806             m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
    807           } else if (FDE_CSSDisplay::Inline == eDisplay &&
    808                      (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
    809             m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
    810           } else if (wsText.GetLength() > 0 &&
    811                      (0x20 == wsText.GetAt(wsText.GetLength() - 1))) {
    812             m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
    813           } else if (wsText.GetLength() != 0) {
    814             m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
    815           }
    816         }
    817 
    818         if (wsText.GetLength() > 0) {
    819           if (!m_pLoader || m_pLoader->m_iChar == 0) {
    820             auto pUserData = pdfium::MakeRetain<CXFA_TextUserData>(
    821                 bContentNode ? pParentStyle : pStyle, pLinkData);
    822             m_pBreak->SetUserData(pUserData);
    823           }
    824 
    825           if (AppendChar(wsText, fLinePos, 0, bSavePieces)) {
    826             if (m_pLoader)
    827               m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
    828             if (IsEnd(bSavePieces)) {
    829               if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
    830                 m_pLoader->m_pXMLNode = pXMLNode;
    831                 m_pLoader->m_pParentStyle = pParentStyle;
    832               }
    833               return false;
    834             }
    835             return true;
    836           }
    837         }
    838       }
    839     }
    840 
    841     for (CFDE_XMLNode* pChildNode =
    842              pXMLNode->GetNodeItem(CFDE_XMLNode::FirstChild);
    843          pChildNode;
    844          pChildNode = pChildNode->GetNodeItem(CFDE_XMLNode::NextSibling)) {
    845       if (bCurOl)
    846         iLiCount++;
    847 
    848       if (!LoadRichText(pChildNode, szText, fLinePos,
    849                         pContext ? pStyle : pParentStyle, bSavePieces,
    850                         pLinkData, true, bIsOl, iLiCount))
    851         return false;
    852     }
    853 
    854     if (m_pLoader) {
    855       if (FDE_CSSDisplay::Block == eDisplay)
    856         m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
    857     }
    858     if (bCurLi)
    859       EndBreak(CFX_RTFBreakType::Line, fLinePos, bSavePieces);
    860   } else {
    861     if (pContext)
    862       eDisplay = pContext->GetDisplay();
    863   }
    864 
    865   if (m_bBlockContinue) {
    866     if (pContext && !bContentNode) {
    867       CFX_RTFBreakType dwStatus = (eDisplay == FDE_CSSDisplay::Block)
    868                                       ? CFX_RTFBreakType::Paragraph
    869                                       : CFX_RTFBreakType::Piece;
    870       EndBreak(dwStatus, fLinePos, bSavePieces);
    871       if (eDisplay == FDE_CSSDisplay::Block) {
    872         fLinePos += fSpaceBelow;
    873         if (m_pTabstopContext)
    874           m_pTabstopContext->RemoveAll();
    875       }
    876       if (IsEnd(bSavePieces)) {
    877         if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
    878           m_pLoader->m_pXMLNode =
    879               pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling);
    880           m_pLoader->m_pParentStyle = pParentStyle;
    881         }
    882         return false;
    883       }
    884     }
    885   }
    886   return true;
    887 }
    888 
    889 bool CXFA_TextLayout::AppendChar(const CFX_WideString& wsText,
    890                                  FX_FLOAT& fLinePos,
    891                                  FX_FLOAT fSpaceAbove,
    892                                  bool bSavePieces) {
    893   CFX_RTFBreakType dwStatus = CFX_RTFBreakType::None;
    894   int32_t iChar = 0;
    895   if (m_pLoader)
    896     iChar = m_pLoader->m_iChar;
    897 
    898   int32_t iLength = wsText.GetLength();
    899   for (int32_t i = iChar; i < iLength; i++) {
    900     FX_WCHAR wch = wsText.GetAt(i);
    901     if (wch == 0xA0)
    902       wch = 0x20;
    903 
    904     dwStatus = m_pBreak->AppendChar(wch);
    905     if (dwStatus != CFX_RTFBreakType::None &&
    906         dwStatus != CFX_RTFBreakType::Piece) {
    907       AppendTextLine(dwStatus, fLinePos, bSavePieces);
    908       if (IsEnd(bSavePieces)) {
    909         if (m_pLoader)
    910           m_pLoader->m_iChar = i;
    911         return true;
    912       }
    913       if (dwStatus == CFX_RTFBreakType::Paragraph && m_bRichText)
    914         fLinePos += fSpaceAbove;
    915     }
    916   }
    917   if (m_pLoader)
    918     m_pLoader->m_iChar = 0;
    919 
    920   return false;
    921 }
    922 
    923 bool CXFA_TextLayout::IsEnd(bool bSavePieces) {
    924   if (!bSavePieces)
    925     return false;
    926   if (m_pLoader && m_pLoader->m_iTotalLines > 0)
    927     return m_iLines >= m_pLoader->m_iTotalLines;
    928   return false;
    929 }
    930 
    931 void CXFA_TextLayout::ProcessText(CFX_WideString& wsText) {
    932   int32_t iLen = wsText.GetLength();
    933   if (iLen == 0)
    934     return;
    935 
    936   FX_WCHAR* psz = wsText.GetBuffer(iLen);
    937   int32_t iTrimLeft = 0;
    938   FX_WCHAR wch = 0, wPrev = 0;
    939   for (int32_t i = 0; i < iLen; i++) {
    940     wch = psz[i];
    941     if (wch < 0x20)
    942       wch = 0x20;
    943     if (wch == 0x20 && wPrev == 0x20)
    944       continue;
    945 
    946     wPrev = wch;
    947     psz[iTrimLeft++] = wch;
    948   }
    949   wsText.ReleaseBuffer(iLen);
    950   wsText = wsText.Left(iTrimLeft);
    951 }
    952 
    953 void CXFA_TextLayout::EndBreak(CFX_RTFBreakType dwStatus,
    954                                FX_FLOAT& fLinePos,
    955                                bool bSavePieces) {
    956   dwStatus = m_pBreak->EndBreak(dwStatus);
    957   if (dwStatus != CFX_RTFBreakType::None && dwStatus != CFX_RTFBreakType::Piece)
    958     AppendTextLine(dwStatus, fLinePos, bSavePieces, true);
    959 }
    960 
    961 void CXFA_TextLayout::DoTabstops(CFDE_CSSComputedStyle* pStyle,
    962                                  CXFA_PieceLine* pPieceLine) {
    963   if (!m_pTabstopContext || m_pTabstopContext->m_iTabCount == 0)
    964     return;
    965   if (!pStyle || !pPieceLine)
    966     return;
    967 
    968   int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
    969   if (iPieces == 0)
    970     return;
    971 
    972   XFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPieces - 1].get();
    973   int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex;
    974   int32_t iCount = m_textParser.CountTabs(pStyle);
    975   if (iTabstopsIndex > m_pTabstopContext->m_iTabCount - 1)
    976     return;
    977 
    978   if (iCount > 0) {
    979     iTabstopsIndex++;
    980     m_pTabstopContext->m_bTabstops = true;
    981     FX_FLOAT fRight = 0;
    982     if (iPieces > 1) {
    983       XFA_TextPiece* p = pPieceLine->m_textPieces[iPieces - 2].get();
    984       fRight = p->rtPiece.right();
    985     }
    986     m_pTabstopContext->m_fTabWidth =
    987         pPiece->rtPiece.width + pPiece->rtPiece.left - fRight;
    988   } else if (iTabstopsIndex > -1) {
    989     FX_FLOAT fLeft = 0;
    990     if (m_pTabstopContext->m_bTabstops) {
    991       XFA_TABSTOPS* pTabstops =
    992           m_pTabstopContext->m_tabstops.GetDataPtr(iTabstopsIndex);
    993       uint32_t dwAlign = pTabstops->dwAlign;
    994       if (dwAlign == FX_HashCode_GetW(L"center", false)) {
    995         fLeft = pPiece->rtPiece.width / 2.0f;
    996       } else if (dwAlign == FX_HashCode_GetW(L"right", false) ||
    997                  dwAlign == FX_HashCode_GetW(L"before", false)) {
    998         fLeft = pPiece->rtPiece.width;
    999       } else if (dwAlign == FX_HashCode_GetW(L"decimal", false)) {
   1000         int32_t iChars = pPiece->iChars;
   1001         for (int32_t i = 0; i < iChars; i++) {
   1002           if (pPiece->szText[i] == L'.')
   1003             break;
   1004 
   1005           fLeft += pPiece->Widths[i] / 20000.0f;
   1006         }
   1007       }
   1008       m_pTabstopContext->m_fLeft =
   1009           std::min(fLeft, m_pTabstopContext->m_fTabWidth);
   1010       m_pTabstopContext->m_bTabstops = false;
   1011       m_pTabstopContext->m_fTabWidth = 0;
   1012     }
   1013     pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft;
   1014   }
   1015 }
   1016 
   1017 void CXFA_TextLayout::AppendTextLine(CFX_RTFBreakType dwStatus,
   1018                                      FX_FLOAT& fLinePos,
   1019                                      bool bSavePieces,
   1020                                      bool bEndBreak) {
   1021   int32_t iPieces = m_pBreak->CountBreakPieces();
   1022   if (iPieces < 1)
   1023     return;
   1024 
   1025   CFX_RetainPtr<CFDE_CSSComputedStyle> pStyle;
   1026   if (bSavePieces) {
   1027     auto pNew = pdfium::MakeUnique<CXFA_PieceLine>();
   1028     CXFA_PieceLine* pPieceLine = pNew.get();
   1029     m_pieceLines.push_back(std::move(pNew));
   1030     if (m_pTabstopContext)
   1031       m_pTabstopContext->Reset();
   1032 
   1033     FX_FLOAT fLineStep = 0, fBaseLine = 0;
   1034     int32_t i = 0;
   1035     for (i = 0; i < iPieces; i++) {
   1036       const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i);
   1037       CXFA_TextUserData* pUserData =
   1038           static_cast<CXFA_TextUserData*>(pPiece->m_pUserData.Get());
   1039       if (pUserData)
   1040         pStyle = pUserData->m_pStyle;
   1041       FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f;
   1042 
   1043       auto pTP = pdfium::MakeUnique<XFA_TextPiece>();
   1044       pTP->iChars = pPiece->m_iChars;
   1045       pTP->szText = pPiece->GetString();
   1046       pTP->Widths = pPiece->GetWidths();
   1047       pTP->iBidiLevel = pPiece->m_iBidiLevel;
   1048       pTP->iHorScale = pPiece->m_iHorizontalScale;
   1049       pTP->iVerScale = pPiece->m_iVerticalScale;
   1050       m_textParser.GetUnderline(m_pTextProvider, pStyle.Get(), pTP->iUnderline,
   1051                                 pTP->iPeriod);
   1052       m_textParser.GetLinethrough(m_pTextProvider, pStyle.Get(),
   1053                                   pTP->iLineThrough);
   1054       pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle.Get());
   1055       pTP->pFont = m_textParser.GetFont(m_pTextProvider, pStyle.Get());
   1056       pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle.Get());
   1057       pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f;
   1058       pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f;
   1059       pTP->rtPiece.height = (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f;
   1060       FX_FLOAT fBaseLineTemp =
   1061           m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
   1062       pTP->rtPiece.top = fBaseLineTemp;
   1063 
   1064       FX_FLOAT fLineHeight = m_textParser.GetLineHeight(
   1065           m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
   1066       if (fBaseLineTemp > 0) {
   1067         FX_FLOAT fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height;
   1068         if (fLineHeight < fLineHeightTmp)
   1069           fLineHeight = fLineHeightTmp;
   1070         else
   1071           fBaseLineTemp = 0;
   1072       } else if (fBaseLine < -fBaseLineTemp) {
   1073         fBaseLine = -fBaseLineTemp;
   1074       }
   1075       fLineStep = std::max(fLineStep, fLineHeight);
   1076       pTP->pLinkData = pUserData ? pUserData->m_pLinkData : nullptr;
   1077       pPieceLine->m_textPieces.push_back(std::move(pTP));
   1078       DoTabstops(pStyle.Get(), pPieceLine);
   1079     }
   1080     for (const auto& pTP : pPieceLine->m_textPieces) {
   1081       FX_FLOAT& fTop = pTP->rtPiece.top;
   1082       FX_FLOAT fBaseLineTemp = fTop;
   1083       fTop = fLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp;
   1084       fTop = std::max(0.0f, fTop);
   1085     }
   1086     fLinePos += fLineStep + fBaseLine;
   1087   } else {
   1088     FX_FLOAT fLineStep = 0;
   1089     FX_FLOAT fLineWidth = 0;
   1090     for (int32_t i = 0; i < iPieces; i++) {
   1091       const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i);
   1092       CXFA_TextUserData* pUserData =
   1093           static_cast<CXFA_TextUserData*>(pPiece->m_pUserData.Get());
   1094       if (pUserData)
   1095         pStyle = pUserData->m_pStyle;
   1096       FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f;
   1097       FX_FLOAT fBaseLine =
   1098           m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
   1099       FX_FLOAT fLineHeight = m_textParser.GetLineHeight(
   1100           m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
   1101       if (fBaseLine > 0) {
   1102         FX_FLOAT fLineHeightTmp =
   1103             fBaseLine + (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f;
   1104         if (fLineHeight < fLineHeightTmp) {
   1105           fLineHeight = fLineHeightTmp;
   1106         }
   1107       }
   1108       fLineStep = std::max(fLineStep, fLineHeight);
   1109       fLineWidth += pPiece->m_iWidth / 20000.0f;
   1110     }
   1111     fLinePos += fLineStep;
   1112     m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth);
   1113     if (m_pLoader && m_pLoader->m_bSaveLineHeight) {
   1114       FX_FLOAT fHeight = fLinePos - m_pLoader->m_fLastPos;
   1115       m_pLoader->m_fLastPos = fLinePos;
   1116       m_pLoader->m_lineHeights.Add(fHeight);
   1117     }
   1118   }
   1119 
   1120   m_pBreak->ClearBreakPieces();
   1121   if (dwStatus == CFX_RTFBreakType::Paragraph) {
   1122     m_pBreak->Reset();
   1123     if (!pStyle && bEndBreak) {
   1124       CXFA_Para para = m_pTextProvider->GetParaNode();
   1125       if (para) {
   1126         FX_FLOAT fStartPos = para.GetMarginLeft();
   1127         FX_FLOAT fIndent = para.GetTextIndent();
   1128         if (fIndent > 0)
   1129           fStartPos += fIndent;
   1130 
   1131         FX_FLOAT fSpaceBelow = para.GetSpaceBelow();
   1132         if (fSpaceBelow < 0.1f)
   1133           fSpaceBelow = 0;
   1134 
   1135         m_pBreak->SetLineStartPos(fStartPos);
   1136         fLinePos += fSpaceBelow;
   1137       }
   1138     }
   1139   }
   1140 
   1141   if (pStyle) {
   1142     FX_FLOAT fStart = 0;
   1143     const FDE_CSSRect* pRect = pStyle->GetMarginWidth();
   1144     if (pRect)
   1145       fStart = pRect->left.GetValue();
   1146 
   1147     FX_FLOAT fTextIndent = pStyle->GetTextIndent().GetValue();
   1148     if (fTextIndent < 0)
   1149       fStart -= fTextIndent;
   1150 
   1151     m_pBreak->SetLineStartPos(fStart);
   1152   }
   1153   m_iLines++;
   1154 }
   1155 
   1156 void CXFA_TextLayout::RenderString(CFDE_RenderDevice* pDevice,
   1157                                    CFDE_Brush* pBrush,
   1158                                    CXFA_PieceLine* pPieceLine,
   1159                                    int32_t iPiece,
   1160                                    FXTEXT_CHARPOS* pCharPos,
   1161                                    const CFX_Matrix& tmDoc2Device) {
   1162   const XFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get();
   1163   int32_t iCount = GetDisplayPos(pPiece, pCharPos);
   1164   if (iCount > 0) {
   1165     pBrush->SetColor(pPiece->dwColor);
   1166     pDevice->DrawString(pBrush, pPiece->pFont, pCharPos, iCount,
   1167                         pPiece->fFontSize, &tmDoc2Device);
   1168   }
   1169   pPieceLine->m_charCounts.Add(iCount);
   1170 }
   1171 
   1172 void CXFA_TextLayout::RenderPath(CFDE_RenderDevice* pDevice,
   1173                                  CFDE_Pen* pPen,
   1174                                  CXFA_PieceLine* pPieceLine,
   1175                                  int32_t iPiece,
   1176                                  FXTEXT_CHARPOS* pCharPos,
   1177                                  const CFX_Matrix& tmDoc2Device) {
   1178   XFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get();
   1179   bool bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2;
   1180   bool bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2;
   1181   if (bNoUnderline && bNoLineThrough)
   1182     return;
   1183 
   1184   pPen->SetColor(pPiece->dwColor);
   1185   std::unique_ptr<CFDE_Path> pPath(new CFDE_Path);
   1186   int32_t iChars = GetDisplayPos(pPiece, pCharPos);
   1187   if (iChars > 0) {
   1188     CFX_PointF pt1, pt2;
   1189     FX_FLOAT fEndY = pCharPos[0].m_Origin.y + 1.05f;
   1190     if (pPiece->iPeriod == XFA_ATTRIBUTEENUM_Word) {
   1191       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
   1192         for (int32_t j = 0; j < iChars; j++) {
   1193           pt1.x = pCharPos[j].m_Origin.x;
   1194           pt2.x =
   1195               pt1.x + pCharPos[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1196           pt1.y = pt2.y = fEndY;
   1197           pPath->AddLine(pt1, pt2);
   1198         }
   1199         fEndY += 2.0f;
   1200       }
   1201     } else {
   1202       pt1.x = pCharPos[0].m_Origin.x;
   1203       pt2.x =
   1204           pCharPos[iChars - 1].m_Origin.x +
   1205           pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1206       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
   1207         pt1.y = pt2.y = fEndY;
   1208         pPath->AddLine(pt1, pt2);
   1209         fEndY += 2.0f;
   1210       }
   1211     }
   1212     fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
   1213     pt1.x = pCharPos[0].m_Origin.x;
   1214     pt2.x = pCharPos[iChars - 1].m_Origin.x +
   1215             pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1216     for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
   1217       pt1.y = pt2.y = fEndY;
   1218       pPath->AddLine(pt1, pt2);
   1219       fEndY += 2.0f;
   1220     }
   1221   } else {
   1222     if (bNoLineThrough &&
   1223         (bNoUnderline || pPiece->iPeriod != XFA_ATTRIBUTEENUM_All)) {
   1224       return;
   1225     }
   1226     int32_t iCharsTmp = 0;
   1227     int32_t iPiecePrev = iPiece, iPieceNext = iPiece;
   1228     while (iPiecePrev > 0) {
   1229       iPiecePrev--;
   1230       iCharsTmp = pPieceLine->m_charCounts.GetAt(iPiecePrev);
   1231       if (iCharsTmp > 0)
   1232         break;
   1233     }
   1234     if (iCharsTmp == 0)
   1235       return;
   1236 
   1237     iCharsTmp = 0;
   1238     int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
   1239     while (iPieceNext < iPieces - 1) {
   1240       iPieceNext++;
   1241       iCharsTmp = pPieceLine->m_charCounts.GetAt(iPieceNext);
   1242       if (iCharsTmp > 0)
   1243         break;
   1244     }
   1245     if (iCharsTmp == 0)
   1246       return;
   1247 
   1248     FX_FLOAT fOrgX = 0.0f;
   1249     FX_FLOAT fEndX = 0.0f;
   1250     pPiece = pPieceLine->m_textPieces[iPiecePrev].get();
   1251     iChars = GetDisplayPos(pPiece, pCharPos);
   1252     if (iChars < 1)
   1253       return;
   1254 
   1255     fOrgX = pCharPos[iChars - 1].m_Origin.x +
   1256             pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1257     pPiece = pPieceLine->m_textPieces[iPieceNext].get();
   1258     iChars = GetDisplayPos(pPiece, pCharPos);
   1259     if (iChars < 1)
   1260       return;
   1261 
   1262     fEndX = pCharPos[0].m_Origin.x;
   1263     CFX_PointF pt1;
   1264     CFX_PointF pt2;
   1265     pt1.x = fOrgX;
   1266     pt2.x = fEndX;
   1267     FX_FLOAT fEndY = pCharPos[0].m_Origin.y + 1.05f;
   1268     for (int32_t i = 0; i < pPiece->iUnderline; i++) {
   1269       pt1.y = fEndY;
   1270       pt2.y = fEndY;
   1271       pPath->AddLine(pt1, pt2);
   1272       fEndY += 2.0f;
   1273     }
   1274     fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
   1275     for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
   1276       pt1.y = fEndY;
   1277       pt2.y = fEndY;
   1278       pPath->AddLine(pt1, pt2);
   1279       fEndY += 2.0f;
   1280     }
   1281   }
   1282   pDevice->DrawPath(pPen, 1, pPath.get(), &tmDoc2Device);
   1283 }
   1284 
   1285 int32_t CXFA_TextLayout::GetDisplayPos(const XFA_TextPiece* pPiece,
   1286                                        FXTEXT_CHARPOS* pCharPos,
   1287                                        bool bCharCode) {
   1288   if (!pPiece)
   1289     return 0;
   1290 
   1291   FX_RTFTEXTOBJ tr;
   1292   if (!ToRun(pPiece, &tr))
   1293     return 0;
   1294   return m_pBreak->GetDisplayPos(&tr, pCharPos, bCharCode);
   1295 }
   1296 
   1297 bool CXFA_TextLayout::ToRun(const XFA_TextPiece* pPiece, FX_RTFTEXTOBJ* tr) {
   1298   int32_t iLength = pPiece->iChars;
   1299   if (iLength < 1)
   1300     return false;
   1301 
   1302   tr->pStr = pPiece->szText;
   1303   tr->pFont = pPiece->pFont;
   1304   tr->pRect = &pPiece->rtPiece;
   1305   tr->pWidths = pPiece->Widths;
   1306   tr->iLength = iLength;
   1307   tr->fFontSize = pPiece->fFontSize;
   1308   tr->iBidiLevel = pPiece->iBidiLevel;
   1309   tr->wLineBreakChar = L'\n';
   1310   tr->iVerticalScale = pPiece->iVerScale;
   1311   tr->iHorizontalScale = pPiece->iHorScale;
   1312   return true;
   1313 }
   1314