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