Home | History | Annotate | Download | only in app
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include <algorithm>
      8 
      9 #include "xfa/src/foxitlib.h"
     10 #include "xfa/src/fxfa/src/common/xfa_common.h"
     11 #include "xfa_textlayout.h"
     12 #include "xfa_ffapp.h"
     13 #include "xfa_ffdoc.h"
     14 #include "xfa_fontmgr.h"
     15 CXFA_CSSTagProvider::~CXFA_CSSTagProvider() {
     16   FX_POSITION pos = m_Attributes.GetStartPosition();
     17   while (pos) {
     18     CFX_WideString *pName = NULL, *pValue = NULL;
     19     m_Attributes.GetNextAssoc(pos, (void*&)pName, (void*&)pValue);
     20     if (pName != NULL) {
     21       delete pName;
     22     }
     23     if (pValue != NULL) {
     24       delete pValue;
     25     }
     26   }
     27 }
     28 void CXFA_CSSTagProvider::GetNextAttribute(FX_POSITION& pos,
     29                                            CFX_WideStringC& wsAttr,
     30                                            CFX_WideStringC& wsValue) {
     31   if (pos == NULL) {
     32     return;
     33   }
     34   CFX_WideString* pName = NULL;
     35   CFX_WideString* pValue = NULL;
     36   m_Attributes.GetNextAssoc(pos, (void*&)pName, (void*&)pValue);
     37   wsAttr = *pName;
     38   wsValue = *pValue;
     39 }
     40 void CXFA_CSSTagProvider::SetAttribute(const CFX_WideString& wsAttr,
     41                                        const CFX_WideString& wsValue) {
     42   CFX_WideString* pName = new CFX_WideString();
     43   CFX_WideString* pValue = new CFX_WideString();
     44   *pName = wsAttr;
     45   *pValue = wsValue;
     46   m_Attributes.SetAt(pName, pValue);
     47 }
     48 void CXFA_TextParseContext::SetDecls(const IFDE_CSSDeclaration** ppDeclArray,
     49                                      int32_t iDeclCount) {
     50   if (iDeclCount <= 0 || ppDeclArray == NULL) {
     51     return;
     52   }
     53   m_dwMatchedDecls = iDeclCount;
     54   m_ppMatchedDecls = FX_Alloc(IFDE_CSSDeclaration*, iDeclCount);
     55   FX_memcpy(m_ppMatchedDecls, ppDeclArray,
     56             iDeclCount * sizeof(IFDE_CSSDeclaration*));
     57 }
     58 CXFA_TextParser::~CXFA_TextParser() {
     59   if (m_pUASheet != NULL) {
     60     m_pUASheet->Release();
     61   }
     62   if (m_pSelector != NULL) {
     63     m_pSelector->Release();
     64   }
     65   if (m_pAllocator != NULL) {
     66     m_pAllocator->Release();
     67   }
     68   FX_POSITION ps = m_mapXMLNodeToParseContext.GetStartPosition();
     69   while (ps) {
     70     IFDE_XMLNode* pXMLNode;
     71     CXFA_TextParseContext* pParseContext;
     72     m_mapXMLNodeToParseContext.GetNextAssoc(ps, pXMLNode, pParseContext);
     73     if (pParseContext != NULL) {
     74       FDE_DeleteWith(CXFA_TextParseContext, m_pAllocator, pParseContext);
     75     }
     76   }
     77   m_mapXMLNodeToParseContext.RemoveAll();
     78 }
     79 void CXFA_TextParser::Reset() {
     80   FX_POSITION ps = m_mapXMLNodeToParseContext.GetStartPosition();
     81   while (ps) {
     82     IFDE_XMLNode* pXMLNode;
     83     CXFA_TextParseContext* pParseContext;
     84     m_mapXMLNodeToParseContext.GetNextAssoc(ps, pXMLNode, pParseContext);
     85     if (pParseContext != NULL) {
     86       FDE_DeleteWith(CXFA_TextParseContext, m_pAllocator, pParseContext);
     87     }
     88   }
     89   m_mapXMLNodeToParseContext.RemoveAll();
     90   if (m_pAllocator != NULL) {
     91     m_pAllocator->Release();
     92     m_pAllocator = NULL;
     93   }
     94 }
     95 void CXFA_TextParser::InitCSSData(IXFA_TextProvider* pTextProvider) {
     96   if (pTextProvider == NULL) {
     97     return;
     98   }
     99   if (m_pSelector == NULL) {
    100     CXFA_FFDoc* pDoc = pTextProvider->GetDocNode();
    101     IFX_FontMgr* pFontMgr = pDoc->GetApp()->GetFDEFontMgr();
    102     FXSYS_assert(pFontMgr != NULL);
    103     m_pSelector = IFDE_CSSStyleSelector::Create();
    104     m_pSelector->SetFontMgr(pFontMgr);
    105     FX_FLOAT fFontSize = 10;
    106     CXFA_Font font = pTextProvider->GetFontNode();
    107     if (font.IsExistInXML()) {
    108       fFontSize = font.GetFontSize();
    109     }
    110     m_pSelector->SetDefFontSize(fFontSize);
    111   }
    112   if (m_pUASheet == NULL) {
    113     m_pUASheet = LoadDefaultSheetStyle();
    114     m_pSelector->SetStyleSheet(FDE_CSSSTYLESHEETGROUP_UserAgent, m_pUASheet);
    115     m_pSelector->UpdateStyleIndex(FDE_CSSMEDIATYPE_ALL);
    116   }
    117 }
    118 IFDE_CSSStyleSheet* CXFA_TextParser::LoadDefaultSheetStyle() {
    119   static const FX_WCHAR s_pStyle[] =
    120       L"html,body,ol,p,ul{display:block}"
    121       L"li{display:list-item}"
    122       L"ol,ul{padding-left:33px}ol{list-style-type:decimal}ol,ul{margin-top:0;"
    123       L"margin-bottom:0}ul,ol{margin:1.12em 0}"
    124       L"a{color:#0000ff;text-decoration:underline}b{font-weight:bolder}i{font-"
    125       L"style:italic}"
    126       L"sup{vertical-align:+15em;font-size:.66em}sub{vertical-align:-15em;font-"
    127       L"size:.66em}";
    128   return IFDE_CSSStyleSheet::LoadFromBuffer(
    129       CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8);
    130 }
    131 IFDE_CSSComputedStyle* CXFA_TextParser::CreateRootStyle(
    132     IXFA_TextProvider* pTextProvider) {
    133   CXFA_Font font = pTextProvider->GetFontNode();
    134   CXFA_Para para = pTextProvider->GetParaNode();
    135   IFDE_CSSComputedStyle* pStyle = m_pSelector->CreateComputedStyle(NULL);
    136   IFDE_CSSFontStyle* pFontStyle = pStyle->GetFontStyles();
    137   IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles();
    138   FX_FLOAT fLineHeight = 0, fFontSize = 10;
    139   if (para.IsExistInXML()) {
    140     fLineHeight = para.GetLineHeight();
    141     FDE_CSSLENGTH indent;
    142     indent.Set(FDE_CSSLENGTHUNIT_Point, para.GetTextIndent());
    143     pParaStyle->SetTextIndent(indent);
    144     FDE_CSSTEXTALIGN hAlgin = FDE_CSSTEXTALIGN_Left;
    145     switch (para.GetHorizontalAlign()) {
    146       case XFA_ATTRIBUTEENUM_Center:
    147         hAlgin = FDE_CSSTEXTALIGN_Center;
    148         break;
    149       case XFA_ATTRIBUTEENUM_Right:
    150         hAlgin = FDE_CSSTEXTALIGN_Right;
    151         break;
    152       case XFA_ATTRIBUTEENUM_Justify:
    153         hAlgin = FDE_CSSTEXTALIGN_Justify;
    154         break;
    155       case XFA_ATTRIBUTEENUM_JustifyAll:
    156         hAlgin = FDE_CSSTEXTALIGN_JustifyAll;
    157         break;
    158     }
    159     pParaStyle->SetTextAlign(hAlgin);
    160     FDE_CSSRECT rtMarginWidth;
    161     rtMarginWidth.left.Set(FDE_CSSLENGTHUNIT_Point, para.GetMarginLeft());
    162     rtMarginWidth.top.Set(FDE_CSSLENGTHUNIT_Point, para.GetSpaceAbove());
    163     rtMarginWidth.right.Set(FDE_CSSLENGTHUNIT_Point, para.GetMarginRight());
    164     rtMarginWidth.bottom.Set(FDE_CSSLENGTHUNIT_Point, para.GetSpaceBelow());
    165     pStyle->GetBoundaryStyles()->SetMarginWidth(rtMarginWidth);
    166   }
    167   if (font.IsExistInXML()) {
    168     pFontStyle->SetColor(font.GetColor());
    169     pFontStyle->SetFontStyle(font.IsItalic() ? FDE_CSSFONTSTYLE_Italic
    170                                              : FDE_CSSFONTSTYLE_Normal);
    171     pFontStyle->SetFontWeight(font.IsBold() ? FXFONT_FW_BOLD
    172                                             : FXFONT_FW_NORMAL);
    173     pParaStyle->SetNumberVerticalAlign(-font.GetBaselineShift());
    174     fFontSize = font.GetFontSize();
    175     FDE_CSSLENGTH letterSpacing;
    176     letterSpacing.Set(FDE_CSSLENGTHUNIT_Point, font.GetLetterSpacing());
    177     pParaStyle->SetLetterSpacing(letterSpacing);
    178     FX_DWORD dwDecoration = 0;
    179     if (font.GetLineThrough() > 0) {
    180       dwDecoration |= FDE_CSSTEXTDECORATION_LineThrough;
    181     }
    182     if (font.GetUnderline() > 1) {
    183       dwDecoration |= FDE_CSSTEXTDECORATION_Double;
    184     } else if (font.GetUnderline() > 0) {
    185       dwDecoration |= FDE_CSSTEXTDECORATION_Underline;
    186     }
    187     pParaStyle->SetTextDecoration(dwDecoration);
    188   }
    189   pParaStyle->SetLineHeight(fLineHeight);
    190   pFontStyle->SetFontSize(fFontSize);
    191   return pStyle;
    192 }
    193 IFDE_CSSComputedStyle* CXFA_TextParser::CreateStyle(
    194     IFDE_CSSComputedStyle* pParentStyle) {
    195   IFDE_CSSComputedStyle* pNewStyle =
    196       m_pSelector->CreateComputedStyle(pParentStyle);
    197   FXSYS_assert(pNewStyle != NULL);
    198   if (pParentStyle) {
    199     IFDE_CSSParagraphStyle* pParaStyle = pParentStyle->GetParagraphStyles();
    200     FX_DWORD dwDecoration = pParaStyle->GetTextDecoration();
    201     FX_FLOAT fBaseLine = 0;
    202     if (pParaStyle->GetVerticalAlign() == FDE_CSSVERTICALALIGN_Number) {
    203       fBaseLine = pParaStyle->GetNumberVerticalAlign();
    204     }
    205     pParaStyle = pNewStyle->GetParagraphStyles();
    206     pParaStyle->SetTextDecoration(dwDecoration);
    207     pParaStyle->SetNumberVerticalAlign(fBaseLine);
    208     IFDE_CSSBoundaryStyle* pBoundarytyle = pParentStyle->GetBoundaryStyles();
    209     const FDE_CSSRECT* pRect = pBoundarytyle->GetMarginWidth();
    210     if (pRect != NULL) {
    211       pBoundarytyle = pNewStyle->GetBoundaryStyles();
    212       pBoundarytyle->SetMarginWidth(*pRect);
    213     }
    214   }
    215   return pNewStyle;
    216 }
    217 IFDE_CSSComputedStyle* CXFA_TextParser::ComputeStyle(
    218     IFDE_XMLNode* pXMLNode,
    219     IFDE_CSSComputedStyle* pParentStyle) {
    220   CXFA_TextParseContext* pContext = static_cast<CXFA_TextParseContext*>(
    221       m_mapXMLNodeToParseContext.GetValueAt(pXMLNode));
    222   if (!pContext)
    223     return nullptr;
    224   pContext->m_pParentStyle = pParentStyle;
    225   pParentStyle->AddRef();
    226   CXFA_CSSTagProvider tagProvider;
    227   ParseTagInfo(pXMLNode, tagProvider);
    228   if (tagProvider.m_bContent)
    229     return nullptr;
    230   IFDE_CSSComputedStyle* pStyle = CreateStyle(pParentStyle);
    231   IFDE_CSSAccelerator* pCSSAccel = m_pSelector->InitAccelerator();
    232   pCSSAccel->OnEnterTag(&tagProvider);
    233   m_pSelector->ComputeStyle(&tagProvider, pContext->GetDecls(),
    234                             pContext->CountDecls(), pStyle);
    235   pCSSAccel->OnLeaveTag(&tagProvider);
    236   return pStyle;
    237 }
    238 void CXFA_TextParser::DoParse(IFDE_XMLNode* pXMLContainer,
    239                               IXFA_TextProvider* pTextProvider) {
    240   if (pXMLContainer == NULL || pTextProvider == NULL || m_pAllocator != NULL) {
    241     return;
    242   }
    243   m_pAllocator =
    244       FX_CreateAllocator(FX_ALLOCTYPE_Fixed, 32, sizeof(CXFA_CSSTagProvider));
    245   InitCSSData(pTextProvider);
    246   IFDE_CSSComputedStyle* pRootStyle = CreateRootStyle(pTextProvider);
    247   ParseRichText(pXMLContainer, pRootStyle);
    248   pRootStyle->Release();
    249 }
    250 void CXFA_TextParser::ParseRichText(IFDE_XMLNode* pXMLNode,
    251                                     IFDE_CSSComputedStyle* pParentStyle) {
    252   if (pXMLNode == NULL) {
    253     return;
    254   }
    255   CXFA_CSSTagProvider tagProvider;
    256   ParseTagInfo(pXMLNode, tagProvider);
    257   if (!tagProvider.m_bTagAviliable) {
    258     return;
    259   }
    260   IFDE_CSSComputedStyle* pNewStyle = NULL;
    261   if ((tagProvider.GetTagName() != FX_WSTRC(L"body")) ||
    262       (tagProvider.GetTagName() != FX_WSTRC(L"html"))) {
    263     CXFA_TextParseContext* pTextContext =
    264         FDE_NewWith(m_pAllocator) CXFA_TextParseContext;
    265     FDE_CSSDISPLAY eDisplay = FDE_CSSDISPLAY_Inline;
    266     if (!tagProvider.m_bContent) {
    267       pNewStyle = CreateStyle(pParentStyle);
    268       IFDE_CSSAccelerator* pCSSAccel = m_pSelector->InitAccelerator();
    269       pCSSAccel->OnEnterTag(&tagProvider);
    270       CFDE_CSSDeclarationArray DeclArray;
    271       int32_t iMatchedDecls =
    272           m_pSelector->MatchDeclarations(&tagProvider, DeclArray);
    273       const IFDE_CSSDeclaration** ppMatchDecls =
    274           (const IFDE_CSSDeclaration**)DeclArray.GetData();
    275       m_pSelector->ComputeStyle(&tagProvider, ppMatchDecls, iMatchedDecls,
    276                                 pNewStyle);
    277       pCSSAccel->OnLeaveTag(&tagProvider);
    278       if (iMatchedDecls > 0) {
    279         pTextContext->SetDecls(ppMatchDecls, iMatchedDecls);
    280       }
    281       eDisplay = pNewStyle->GetPositionStyles()->GetDisplay();
    282     }
    283     pTextContext->SetDisplay(eDisplay);
    284     m_mapXMLNodeToParseContext.SetAt(pXMLNode, pTextContext);
    285   }
    286   for (IFDE_XMLNode* pXMLChild =
    287            pXMLNode->GetNodeItem(IFDE_XMLNode::FirstChild);
    288        pXMLChild;
    289        pXMLChild = pXMLChild->GetNodeItem(IFDE_XMLNode::NextSibling)) {
    290     ParseRichText(pXMLChild, pNewStyle);
    291   }
    292   if (pNewStyle != NULL) {
    293     pNewStyle->Release();
    294   }
    295 }
    296 void CXFA_TextParser::ParseTagInfo(IFDE_XMLNode* pXMLNode,
    297                                    CXFA_CSSTagProvider& tagProvider) {
    298   static const FX_DWORD s_XFATagName[] = {
    299       0x61,       0x62,       0x69,       0x70,       0x0001f714,
    300       0x00022a55, 0x000239bb, 0x00025881, 0x0bd37faa, 0x0bd37fb8,
    301       0xa73e3af2, 0xb182eaae, 0xdb8ac455,
    302   };
    303   CFX_WideString wsName;
    304   if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
    305     IFDE_XMLElement* pXMLElement = (IFDE_XMLElement*)pXMLNode;
    306     pXMLElement->GetLocalTagName(wsName);
    307     tagProvider.SetTagNameObj(wsName);
    308     FX_DWORD dwHashCode =
    309         FX_HashCode_String_GetW(wsName, wsName.GetLength(), TRUE);
    310     static const int32_t s_iCount = sizeof(s_XFATagName) / sizeof(FX_DWORD);
    311     CFX_DSPATemplate<FX_DWORD> lookup;
    312     tagProvider.m_bTagAviliable =
    313         lookup.Lookup(dwHashCode, s_XFATagName, s_iCount) > -1;
    314     CFX_WideString wsValue;
    315     pXMLElement->GetString(FX_WSTRC(L"style").GetPtr(), wsValue);
    316     if (!wsValue.IsEmpty()) {
    317       tagProvider.SetAttribute(FX_WSTRC(L"style"), wsValue);
    318     }
    319   } else if (pXMLNode->GetType() == FDE_XMLNODE_Text) {
    320     tagProvider.m_bTagAviliable = TRUE;
    321     tagProvider.m_bContent = TRUE;
    322   }
    323 }
    324 int32_t CXFA_TextParser::GetVAlgin(IXFA_TextProvider* pTextProvider) const {
    325   int32_t iAlign = XFA_ATTRIBUTEENUM_Top;
    326   CXFA_Para para = pTextProvider->GetParaNode();
    327   if (para.IsExistInXML()) {
    328     iAlign = para.GetVerticalAlign();
    329   }
    330   return iAlign;
    331 }
    332 FX_FLOAT CXFA_TextParser::GetTabInterval(IFDE_CSSComputedStyle* pStyle) const {
    333   CFX_WideString wsValue;
    334   if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"tab-interval"), wsValue)) {
    335     CXFA_Measurement ms(wsValue);
    336     return ms.ToUnit(XFA_UNIT_Pt);
    337   }
    338   return 36;
    339 }
    340 int32_t CXFA_TextParser::CountTabs(IFDE_CSSComputedStyle* pStyle) const {
    341   CFX_WideString wsValue;
    342   if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"xfa-tab-count"), wsValue)) {
    343     return wsValue.GetInteger();
    344   }
    345   return 0;
    346 }
    347 FX_BOOL CXFA_TextParser::IsSpaceRun(IFDE_CSSComputedStyle* pStyle) const {
    348   CFX_WideString wsValue;
    349   if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"xfa-spacerun"), wsValue)) {
    350     wsValue.MakeLower();
    351     return wsValue == FX_WSTRC(L"yes");
    352   }
    353   return FALSE;
    354 }
    355 IFX_Font* CXFA_TextParser::GetFont(IXFA_TextProvider* pTextProvider,
    356                                    IFDE_CSSComputedStyle* pStyle) const {
    357   CFX_WideStringC wsFamily = FX_WSTRC(L"Courier");
    358   FX_DWORD dwStyle = 0;
    359   CXFA_Font font = pTextProvider->GetFontNode();
    360   if (font.IsExistInXML()) {
    361     font.GetTypeface(wsFamily);
    362     if (font.IsBold()) {
    363       dwStyle |= FX_FONTSTYLE_Bold;
    364     }
    365     if (font.IsItalic()) {
    366       dwStyle |= FX_FONTSTYLE_Italic;
    367     }
    368   }
    369   if (pStyle) {
    370     IFDE_CSSFontStyle* pFontStyle = pStyle->GetFontStyles();
    371     int32_t iCount = pFontStyle->CountFontFamilies();
    372     if (iCount > 0) {
    373       wsFamily = pFontStyle->GetFontFamily(iCount - 1);
    374     }
    375     dwStyle = 0;
    376     if (pFontStyle->GetFontWeight() > FXFONT_FW_NORMAL) {
    377       dwStyle |= FX_FONTSTYLE_Bold;
    378     }
    379     if (pFontStyle->GetFontStyle() == FDE_CSSFONTSTYLE_Italic) {
    380       dwStyle |= FX_FONTSTYLE_Italic;
    381     }
    382   }
    383   CXFA_FFDoc* pDoc = pTextProvider->GetDocNode();
    384   FXSYS_assert(pDoc != NULL);
    385   CXFA_FontMgr* pFontMgr = pDoc->GetApp()->GetXFAFontMgr();
    386   return pFontMgr->GetFont(pDoc, wsFamily, dwStyle);
    387 }
    388 FX_FLOAT CXFA_TextParser::GetFontSize(IXFA_TextProvider* pTextProvider,
    389                                       IFDE_CSSComputedStyle* pStyle) const {
    390   if (pStyle != NULL) {
    391     return pStyle->GetFontStyles()->GetFontSize();
    392   }
    393   CXFA_Font font = pTextProvider->GetFontNode();
    394   if (font.IsExistInXML()) {
    395     return font.GetFontSize();
    396   }
    397   return 10;
    398 }
    399 int32_t CXFA_TextParser::GetHorScale(IXFA_TextProvider* pTextProvider,
    400                                      IFDE_CSSComputedStyle* pStyle,
    401                                      IFDE_XMLNode* pXMLNode) const {
    402   if (pStyle) {
    403     CFX_WideString wsValue;
    404     if (pStyle->GetCustomStyle(FX_WSTRC(L"xfa-font-horizontal-scale"),
    405                                wsValue)) {
    406       return wsValue.GetInteger();
    407     }
    408     while (pXMLNode) {
    409       CXFA_TextParseContext* pContext = static_cast<CXFA_TextParseContext*>(
    410           m_mapXMLNodeToParseContext.GetValueAt(pXMLNode));
    411       if (pContext && pContext->m_pParentStyle &&
    412           pContext->m_pParentStyle->GetCustomStyle(
    413               FX_WSTRC(L"xfa-font-horizontal-scale"), wsValue)) {
    414         return wsValue.GetInteger();
    415       }
    416       pXMLNode = pXMLNode->GetNodeItem(IFDE_XMLNode::Parent);
    417     }
    418   }
    419   if (CXFA_Font font = pTextProvider->GetFontNode()) {
    420     return static_cast<int32_t>(font.GetHorizontalScale());
    421   }
    422   return 100;
    423 }
    424 int32_t CXFA_TextParser::GetVerScale(IXFA_TextProvider* pTextProvider,
    425                                      IFDE_CSSComputedStyle* pStyle) const {
    426   if (pStyle != NULL) {
    427     CFX_WideString wsValue;
    428     if (pStyle->GetCustomStyle(FX_WSTRC(L"xfa-font-vertical-scale"), wsValue)) {
    429       return wsValue.GetInteger();
    430     }
    431   }
    432   if (CXFA_Font font = pTextProvider->GetFontNode()) {
    433     return (int32_t)font.GetVerticalScale();
    434   }
    435   return 100;
    436 }
    437 void CXFA_TextParser::GetUnderline(IXFA_TextProvider* pTextProvider,
    438                                    IFDE_CSSComputedStyle* pStyle,
    439                                    int32_t& iUnderline,
    440                                    int32_t& iPeriod) const {
    441   iUnderline = 0;
    442   iPeriod = XFA_ATTRIBUTEENUM_All;
    443   if (pStyle) {
    444     FX_DWORD dwDecoration = pStyle->GetParagraphStyles()->GetTextDecoration();
    445     if (dwDecoration & FDE_CSSTEXTDECORATION_Double) {
    446       iUnderline = 2;
    447     } else if (dwDecoration & FDE_CSSTEXTDECORATION_Underline) {
    448       iUnderline = 1;
    449     }
    450     CFX_WideString wsValue;
    451     if (pStyle->GetCustomStyle(FX_WSTRC(L"underlinePeriod"), wsValue)) {
    452       if (wsValue == FX_WSTRC(L"word")) {
    453         iPeriod = XFA_ATTRIBUTEENUM_Word;
    454       }
    455     } else if (CXFA_Font font = pTextProvider->GetFontNode()) {
    456       iPeriod = font.GetUnderlinePeriod();
    457     }
    458   } else {
    459     CXFA_Font font = pTextProvider->GetFontNode();
    460     if (font.IsExistInXML()) {
    461       iUnderline = font.GetUnderline();
    462       iPeriod = font.GetUnderlinePeriod();
    463     }
    464   }
    465 }
    466 void CXFA_TextParser::GetLinethrough(IXFA_TextProvider* pTextProvider,
    467                                      IFDE_CSSComputedStyle* pStyle,
    468                                      int32_t& iLinethrough) const {
    469   if (pStyle) {
    470     FX_DWORD dwDecoration = pStyle->GetParagraphStyles()->GetTextDecoration();
    471     iLinethrough = (dwDecoration & FDE_CSSTEXTDECORATION_LineThrough) ? 1 : 0;
    472   } else {
    473     CXFA_Font font = pTextProvider->GetFontNode();
    474     if (font.IsExistInXML()) {
    475       iLinethrough = font.GetLineThrough();
    476     }
    477   }
    478 }
    479 FX_ARGB CXFA_TextParser::GetColor(IXFA_TextProvider* pTextProvider,
    480                                   IFDE_CSSComputedStyle* pStyle) const {
    481   if (pStyle != NULL) {
    482     return pStyle->GetFontStyles()->GetColor();
    483   }
    484   if (CXFA_Font font = pTextProvider->GetFontNode()) {
    485     return font.GetColor();
    486   }
    487   return 0xFF000000;
    488 }
    489 FX_FLOAT CXFA_TextParser::GetBaseline(IXFA_TextProvider* pTextProvider,
    490                                       IFDE_CSSComputedStyle* pStyle) const {
    491   if (pStyle != NULL) {
    492     IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles();
    493     if (pParaStyle->GetVerticalAlign() == FDE_CSSVERTICALALIGN_Number) {
    494       return pParaStyle->GetNumberVerticalAlign();
    495     }
    496   } else if (CXFA_Font font = pTextProvider->GetFontNode()) {
    497     return font.GetBaselineShift();
    498   }
    499   return 0;
    500 }
    501 FX_FLOAT CXFA_TextParser::GetLineHeight(IXFA_TextProvider* pTextProvider,
    502                                         IFDE_CSSComputedStyle* pStyle,
    503                                         FX_BOOL bFirst,
    504                                         FX_FLOAT fVerScale) const {
    505   FX_FLOAT fLineHeight = 0;
    506   if (pStyle != NULL) {
    507     fLineHeight = pStyle->GetParagraphStyles()->GetLineHeight();
    508   } else if (CXFA_Para para = pTextProvider->GetParaNode()) {
    509     fLineHeight = para.GetLineHeight();
    510   }
    511   if (bFirst) {
    512     FX_FLOAT fFontSize = GetFontSize(pTextProvider, pStyle);
    513     if (fLineHeight < 0.1f) {
    514       fLineHeight = fFontSize;
    515     } else {
    516       fLineHeight = std::min(fLineHeight, fFontSize);
    517     }
    518   } else if (fLineHeight < 0.1f) {
    519     fLineHeight = GetFontSize(pTextProvider, pStyle) * 1.2f;
    520   }
    521   fLineHeight *= fVerScale;
    522   return fLineHeight;
    523 }
    524 FX_BOOL CXFA_TextParser::GetEmbbedObj(IXFA_TextProvider* pTextProvider,
    525                                       IFDE_XMLNode* pXMLNode,
    526                                       CFX_WideString& wsValue) {
    527   wsValue.Empty();
    528   if (pXMLNode == NULL) {
    529     return FALSE;
    530   }
    531   FX_BOOL bRet = FALSE;
    532   if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
    533     IFDE_XMLElement* pElement = (IFDE_XMLElement*)pXMLNode;
    534     CFX_WideString wsAttr;
    535     pElement->GetString(FX_WSTRC(L"xfa:embed").GetPtr(), wsAttr);
    536     if (wsAttr.IsEmpty()) {
    537       return FALSE;
    538     }
    539     if (wsAttr.GetAt(0) == L'#') {
    540       wsAttr.Delete(0);
    541     }
    542     CFX_WideString ws;
    543     pElement->GetString(FX_WSTRC(L"xfa:embedType").GetPtr(), ws);
    544     if (ws.IsEmpty()) {
    545       ws = L"som";
    546     } else {
    547       ws.MakeLower();
    548     }
    549     FX_BOOL bURI = (ws == FX_WSTRC(L"uri"));
    550     if (!bURI && ws != FX_WSTRC(L"som")) {
    551       return FALSE;
    552     }
    553     ws.Empty();
    554     pElement->GetString(FX_WSTRC(L"xfa:embedMode").GetPtr(), ws);
    555     if (ws.IsEmpty()) {
    556       ws = L"formatted";
    557     } else {
    558       ws.MakeLower();
    559     }
    560     FX_BOOL bRaw = (ws == FX_WSTRC(L"raw"));
    561     if (!bRaw && ws != FX_WSTRC(L"formatted")) {
    562       return FALSE;
    563     }
    564     bRet = pTextProvider->GetEmbbedObj(bURI, bRaw, wsAttr, wsValue);
    565   }
    566   return bRet;
    567 }
    568 CXFA_TextParseContext* CXFA_TextParser::GetParseContextFromMap(
    569     IFDE_XMLNode* pXMLNode) {
    570   return (CXFA_TextParseContext*)m_mapXMLNodeToParseContext.GetValueAt(
    571       pXMLNode);
    572 }
    573 enum XFA_TABSTOPSSTATUS {
    574   XFA_TABSTOPSSTATUS_Error,
    575   XFA_TABSTOPSSTATUS_EOS,
    576   XFA_TABSTOPSSTATUS_None,
    577   XFA_TABSTOPSSTATUS_Alignment,
    578   XFA_TABSTOPSSTATUS_StartLeader,
    579   XFA_TABSTOPSSTATUS_Leader,
    580   XFA_TABSTOPSSTATUS_Location,
    581 };
    582 FX_BOOL CXFA_TextParser::GetTabstops(
    583     IFDE_CSSComputedStyle* pStyle,
    584     CXFA_TextTabstopsContext* pTabstopContext) {
    585   if (pStyle == NULL || pTabstopContext == NULL) {
    586     return FALSE;
    587   }
    588   CFX_WideString wsValue;
    589   if (!pStyle->GetCustomStyle(FX_WSTRC(L"xfa-tab-stops"), wsValue) &&
    590       !pStyle->GetCustomStyle(FX_WSTRC(L"tab-stops"), wsValue)) {
    591     return FALSE;
    592   }
    593   int32_t iLength = wsValue.GetLength();
    594   const FX_WCHAR* pTabStops = wsValue;
    595   int32_t iCur = 0;
    596   int32_t iLast = 0;
    597   CFX_WideString wsAlign;
    598   XFA_TABSTOPSSTATUS eStatus = XFA_TABSTOPSSTATUS_None;
    599   FX_WCHAR ch;
    600   while (iCur < iLength) {
    601     ch = pTabStops[iCur];
    602     switch (eStatus) {
    603       case XFA_TABSTOPSSTATUS_None:
    604         if (ch <= ' ') {
    605           iCur++;
    606         } else {
    607           eStatus = XFA_TABSTOPSSTATUS_Alignment;
    608           iLast = iCur;
    609         }
    610         break;
    611       case XFA_TABSTOPSSTATUS_Alignment:
    612         if (ch == ' ') {
    613           wsAlign = CFX_WideStringC(pTabStops + iLast, iCur - iLast);
    614           eStatus = XFA_TABSTOPSSTATUS_StartLeader;
    615           iCur++;
    616           while (iCur < iLength && pTabStops[iCur] <= ' ') {
    617             iCur++;
    618           }
    619           iLast = iCur;
    620         } else {
    621           iCur++;
    622         }
    623         break;
    624       case XFA_TABSTOPSSTATUS_StartLeader:
    625         if (ch != 'l') {
    626           eStatus = XFA_TABSTOPSSTATUS_Location;
    627         } else {
    628           int32_t iCount = 0;
    629           while (iCur < iLength) {
    630             ch = pTabStops[iCur];
    631             iCur++;
    632             if (ch == '(') {
    633               iCount++;
    634             } else if (ch == ')') {
    635               iCount--;
    636               if (iCount == 0) {
    637                 break;
    638               }
    639             }
    640           }
    641           while (iCur < iLength && pTabStops[iCur] <= ' ') {
    642             iCur++;
    643           }
    644           iLast = iCur;
    645           eStatus = XFA_TABSTOPSSTATUS_Location;
    646         }
    647         break;
    648       case XFA_TABSTOPSSTATUS_Location:
    649         if (ch == ' ') {
    650           FX_DWORD dwHashCode =
    651               FX_HashCode_String_GetW(wsAlign, wsAlign.GetLength(), TRUE);
    652           CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast));
    653           FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt);
    654           pTabstopContext->Append(dwHashCode, fPos);
    655           wsAlign.Empty();
    656           eStatus = XFA_TABSTOPSSTATUS_None;
    657         }
    658         iCur++;
    659         break;
    660       default:
    661         break;
    662     }
    663   }
    664   if (!wsAlign.IsEmpty()) {
    665     FX_DWORD dwHashCode =
    666         FX_HashCode_String_GetW(wsAlign, wsAlign.GetLength(), TRUE);
    667     CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast));
    668     FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt);
    669     pTabstopContext->Append(dwHashCode, fPos);
    670   }
    671   return TRUE;
    672 }
    673 CXFA_TextLayout::CXFA_TextLayout(IXFA_TextProvider* pTextProvider)
    674     : m_bHasBlock(FALSE),
    675       m_pTextProvider(pTextProvider),
    676       m_pTextDataNode(nullptr),
    677       m_bRichText(FALSE),
    678       m_pAllocator(nullptr),
    679       m_pBreak(nullptr),
    680       m_pLoader(nullptr),
    681       m_iLines(0),
    682       m_fMaxWidth(0),
    683       m_pTabstopContext(nullptr),
    684       m_bBlockContinue(TRUE) {
    685   FXSYS_assert(m_pTextProvider);
    686 }
    687 CXFA_TextLayout::~CXFA_TextLayout() {
    688   m_textParser.Reset();
    689   delete m_pLoader;
    690   delete m_pTabstopContext;
    691   Unload();
    692 }
    693 void CXFA_TextLayout::Unload() {
    694   int32_t iCount = m_pieceLines.GetSize();
    695   for (int32_t i = 0; i < iCount; i++) {
    696     CXFA_PieceLine* pLine = m_pieceLines.GetAt(i);
    697     FDE_DeleteWith(CXFA_PieceLine, m_pAllocator, pLine);
    698   }
    699   m_pieceLines.RemoveAll();
    700   if (m_pBreak != NULL) {
    701     m_pBreak->Release();
    702     m_pBreak = NULL;
    703   }
    704   if (m_pAllocator != NULL) {
    705     m_pAllocator->Release();
    706     m_pAllocator = NULL;
    707   }
    708 }
    709 const CXFA_PieceLineArray* CXFA_TextLayout::GetPieceLines() {
    710   return &m_pieceLines;
    711 }
    712 void CXFA_TextLayout::GetTextDataNode() {
    713   if (m_pTextProvider == NULL) {
    714     return;
    715   }
    716   CXFA_Node* pNode = m_pTextProvider->GetTextNode(m_bRichText);
    717   if (pNode && m_bRichText) {
    718     m_textParser.Reset();
    719   }
    720   m_pTextDataNode = pNode;
    721 }
    722 IFDE_XMLNode* CXFA_TextLayout::GetXMLContainerNode() {
    723   IFDE_XMLNode* pXMLContainer = NULL;
    724   if (m_bRichText) {
    725     IFDE_XMLNode* pXMLRoot = m_pTextDataNode->GetXMLMappingNode();
    726     if (!pXMLRoot) {
    727       return pXMLContainer;
    728     }
    729     for (IFDE_XMLNode* pXMLChild =
    730              pXMLRoot->GetNodeItem(IFDE_XMLNode::FirstChild);
    731          pXMLChild;
    732          pXMLChild = pXMLChild->GetNodeItem(IFDE_XMLNode::NextSibling)) {
    733       if (pXMLChild->GetType() == FDE_XMLNODE_Element) {
    734         IFDE_XMLElement* pXMLElement = (IFDE_XMLElement*)pXMLChild;
    735         CFX_WideString wsTag;
    736         pXMLElement->GetLocalTagName(wsTag);
    737         if (wsTag.Equal(FX_WSTRC(L"body")) || wsTag.Equal(FX_WSTRC(L"html"))) {
    738           pXMLContainer = pXMLChild;
    739           break;
    740         }
    741       }
    742     }
    743   }
    744   return pXMLContainer;
    745 }
    746 IFX_RTFBreak* CXFA_TextLayout::CreateBreak(FX_BOOL bDefault) {
    747   FX_DWORD dwStyle = FX_RTFLAYOUTSTYLE_ExpandTab;
    748   if (!bDefault) {
    749     dwStyle |= FX_RTFLAYOUTSTYLE_Pagination;
    750   }
    751   IFX_RTFBreak* pBreak = IFX_RTFBreak::Create(0);
    752   pBreak->SetLayoutStyles(dwStyle);
    753   pBreak->SetLineBreakChar(L'\n');
    754   pBreak->SetLineBreakTolerance(1);
    755   pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, NULL));
    756   pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, NULL));
    757   return pBreak;
    758 }
    759 void CXFA_TextLayout::InitBreak(FX_FLOAT fLineWidth) {
    760   CXFA_Font font = m_pTextProvider->GetFontNode();
    761   CXFA_Para para = m_pTextProvider->GetParaNode();
    762   FX_FLOAT fStart = 0;
    763   FX_FLOAT fStartPos = 0;
    764   if (para.IsExistInXML()) {
    765     int32_t iAlign = FX_RTFLINEALIGNMENT_Left;
    766     switch (para.GetHorizontalAlign()) {
    767       case XFA_ATTRIBUTEENUM_Center:
    768         iAlign = FX_RTFLINEALIGNMENT_Center;
    769         break;
    770       case XFA_ATTRIBUTEENUM_Right:
    771         iAlign = FX_RTFLINEALIGNMENT_Right;
    772         break;
    773       case XFA_ATTRIBUTEENUM_Justify:
    774         iAlign = FX_RTFLINEALIGNMENT_Justified;
    775         break;
    776       case XFA_ATTRIBUTEENUM_JustifyAll:
    777         iAlign = FX_RTFLINEALIGNMENT_Distributed;
    778         break;
    779     }
    780     m_pBreak->SetAlignment(iAlign);
    781     fStart = para.GetMarginLeft();
    782     if (m_pTextProvider->IsCheckButtonAndAutoWidth()) {
    783       if (iAlign != FX_RTFLINEALIGNMENT_Left) {
    784         fLineWidth -= para.GetMarginRight();
    785       }
    786     } else {
    787       fLineWidth -= para.GetMarginRight();
    788     }
    789     if (fLineWidth < 0) {
    790       fLineWidth = fStart;
    791     }
    792     fStartPos = fStart;
    793     FX_FLOAT fIndent = para.GetTextIndent();
    794     if (fIndent > 0) {
    795       fStartPos += fIndent;
    796     }
    797   }
    798   m_pBreak->SetLineBoundary(fStart, fLineWidth);
    799   m_pBreak->SetLineStartPos(fStartPos);
    800   if (font.IsExistInXML()) {
    801     m_pBreak->SetHorizontalScale((int32_t)font.GetHorizontalScale());
    802     m_pBreak->SetVerticalScale((int32_t)font.GetVerticalScale());
    803     m_pBreak->SetCharSpace(font.GetLetterSpacing());
    804   }
    805   FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, NULL);
    806   m_pBreak->SetFontSize(fFontSize);
    807   m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, NULL));
    808   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
    809 }
    810 void CXFA_TextLayout::InitBreak(IFDE_CSSComputedStyle* pStyle,
    811                                 FDE_CSSDISPLAY eDisplay,
    812                                 FX_FLOAT fLineWidth,
    813                                 IFDE_XMLNode* pXMLNode,
    814                                 IFDE_CSSComputedStyle* pParentStyle) {
    815   if (pStyle == NULL) {
    816     InitBreak(fLineWidth);
    817     return;
    818   }
    819   IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles();
    820   if (eDisplay == FDE_CSSDISPLAY_Block || eDisplay == FDE_CSSDISPLAY_ListItem) {
    821     int32_t iAlign = FX_RTFLINEALIGNMENT_Left;
    822     switch (pParaStyle->GetTextAlign()) {
    823       case FDE_CSSTEXTALIGN_Right:
    824         iAlign = FX_RTFLINEALIGNMENT_Right;
    825         break;
    826       case FDE_CSSTEXTALIGN_Center:
    827         iAlign = FX_RTFLINEALIGNMENT_Center;
    828         break;
    829       case FDE_CSSTEXTALIGN_Justify:
    830         iAlign = FX_RTFLINEALIGNMENT_Justified;
    831         break;
    832       case FDE_CSSTEXTALIGN_JustifyAll:
    833         iAlign = FX_RTFLINEALIGNMENT_Distributed;
    834         break;
    835       default:
    836         break;
    837     }
    838     m_pBreak->SetAlignment(iAlign);
    839     FX_FLOAT fStart = 0;
    840     const FDE_CSSRECT* pRect = pStyle->GetBoundaryStyles()->GetMarginWidth();
    841     const FDE_CSSRECT* pPaddingRect =
    842         pStyle->GetBoundaryStyles()->GetPaddingWidth();
    843     if (pRect != NULL) {
    844       fStart = pRect->left.GetValue();
    845       fLineWidth -= pRect->right.GetValue();
    846       if (pPaddingRect != NULL) {
    847         fStart += pPaddingRect->left.GetValue();
    848         fLineWidth -= pPaddingRect->right.GetValue();
    849       }
    850       if (eDisplay == FDE_CSSDISPLAY_ListItem) {
    851         const FDE_CSSRECT* pParRect =
    852             pParentStyle->GetBoundaryStyles()->GetMarginWidth();
    853         const FDE_CSSRECT* pParPaddingRect =
    854             pParentStyle->GetBoundaryStyles()->GetPaddingWidth();
    855         if (pParRect != NULL) {
    856           fStart += pParRect->left.GetValue();
    857           fLineWidth -= pParRect->right.GetValue();
    858           if (pParPaddingRect != NULL) {
    859             fStart += pParPaddingRect->left.GetValue();
    860             fLineWidth -= pParPaddingRect->right.GetValue();
    861           }
    862         }
    863         FDE_CSSRECT pNewRect;
    864         pNewRect.left.Set(FDE_CSSLENGTHUNIT_Point, fStart);
    865         pNewRect.right.Set(FDE_CSSLENGTHUNIT_Point, pRect->right.GetValue());
    866         pNewRect.top.Set(FDE_CSSLENGTHUNIT_Point, pRect->top.GetValue());
    867         pNewRect.bottom.Set(FDE_CSSLENGTHUNIT_Point, pRect->bottom.GetValue());
    868         pStyle->GetBoundaryStyles()->SetMarginWidth(pNewRect);
    869       }
    870     }
    871     m_pBreak->SetLineBoundary(fStart, fLineWidth);
    872     FX_FLOAT fIndent = pParaStyle->GetTextIndent().GetValue();
    873     if (fIndent > 0) {
    874       fStart += fIndent;
    875     }
    876     m_pBreak->SetLineStartPos(fStart);
    877     m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle));
    878     if (m_pTabstopContext == NULL) {
    879       m_pTabstopContext = new CXFA_TextTabstopsContext;
    880     }
    881     m_textParser.GetTabstops(pStyle, m_pTabstopContext);
    882     for (int32_t i = 0; i < m_pTabstopContext->m_iTabCount; i++) {
    883       XFA_TABSTOPS* pTab = m_pTabstopContext->m_tabstops.GetDataPtr(i);
    884       m_pBreak->AddPositionedTab(pTab->fTabstops);
    885     }
    886   }
    887   FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
    888   m_pBreak->SetFontSize(fFontSize);
    889   m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
    890   m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, pStyle));
    891   m_pBreak->SetHorizontalScale(
    892       m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode));
    893   m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle));
    894   m_pBreak->SetCharSpace(pParaStyle->GetLetterSpacing().GetValue());
    895 }
    896 int32_t CXFA_TextLayout::GetText(CFX_WideString& wsText) {
    897   GetTextDataNode();
    898   wsText.Empty();
    899   if (m_bRichText) {
    900   } else {
    901     wsText = m_pTextDataNode->GetContent();
    902   }
    903   return wsText.GetLength();
    904 }
    905 FX_FLOAT CXFA_TextLayout::GetLayoutHeight() {
    906   if (m_pLoader == NULL) {
    907     return 0;
    908   }
    909   int32_t iCount = m_pLoader->m_lineHeights.GetSize();
    910   if (iCount == 0 && m_pLoader->m_fWidth > 0) {
    911     CFX_SizeF szMax, szDef;
    912     szMax.Set(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
    913     szDef.Set(0, 0);
    914     m_pLoader->m_bSaveLineHeight = TRUE;
    915     m_pLoader->m_fLastPos = 0;
    916     CalcSize(szMax, szMax, szDef);
    917     m_pLoader->m_bSaveLineHeight = FALSE;
    918     return szDef.y;
    919   }
    920   FX_FLOAT fHeight = m_pLoader->m_fHeight;
    921   if (fHeight < 0.1f) {
    922     fHeight = 0;
    923     for (int32_t i = 0; i < iCount; i++) {
    924       fHeight += m_pLoader->m_lineHeights.ElementAt(i);
    925     }
    926   }
    927   return fHeight;
    928 }
    929 FX_FLOAT CXFA_TextLayout::StartLayout(FX_FLOAT fWidth) {
    930   if (m_pLoader == NULL) {
    931     m_pLoader = new CXFA_LoaderContext;
    932   }
    933   if (fWidth < 0 || (m_pLoader->m_fWidth > -1 &&
    934                      FXSYS_fabs(fWidth - m_pLoader->m_fWidth) > 0)) {
    935     m_pLoader->m_lineHeights.RemoveAll();
    936     m_Blocks.RemoveAll();
    937     Unload();
    938     m_pLoader->m_fStartLineOffset = 0;
    939   }
    940   m_pLoader->m_fWidth = fWidth;
    941   if (fWidth < 0) {
    942     CFX_SizeF szMax, szDef;
    943     szMax.Set(0, 0);
    944     szDef.Set(0, 0);
    945     m_pLoader->m_bSaveLineHeight = TRUE;
    946     m_pLoader->m_fLastPos = 0;
    947     CalcSize(szMax, szMax, szDef);
    948     m_pLoader->m_bSaveLineHeight = FALSE;
    949     fWidth = szDef.x;
    950   }
    951   return fWidth;
    952 }
    953 FX_BOOL CXFA_TextLayout::DoLayout(int32_t iBlockIndex,
    954                                   FX_FLOAT& fCalcHeight,
    955                                   FX_FLOAT fContentAreaHeight,
    956                                   FX_FLOAT fTextHeight) {
    957   if (m_pLoader == NULL) {
    958     return FALSE;
    959   }
    960   int32_t iBlockCount = m_Blocks.GetSize();
    961   FX_FLOAT fHeight = fTextHeight;
    962   if (fHeight < 0) {
    963     fHeight = GetLayoutHeight();
    964   }
    965   m_pLoader->m_fHeight = fHeight;
    966   if (fContentAreaHeight < 0) {
    967     return FALSE;
    968   }
    969   m_bHasBlock = TRUE;
    970   if (iBlockCount == 0 && fHeight > 0) {
    971     fHeight = fTextHeight - GetLayoutHeight();
    972     if (fHeight > 0) {
    973       int32_t iAlign = m_textParser.GetVAlgin(m_pTextProvider);
    974       if (iAlign == XFA_ATTRIBUTEENUM_Middle) {
    975         fHeight /= 2.0f;
    976       } else if (iAlign != XFA_ATTRIBUTEENUM_Bottom) {
    977         fHeight = 0;
    978       }
    979       m_pLoader->m_fStartLineOffset = fHeight;
    980     }
    981   }
    982   FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset;
    983   int32_t iLineIndex = 0;
    984   if (iBlockCount > 1) {
    985     if (iBlockCount >= (iBlockIndex + 1) * 2) {
    986       iLineIndex = m_Blocks.ElementAt(iBlockIndex * 2);
    987     } else {
    988       iLineIndex = m_Blocks.ElementAt(iBlockCount - 1) +
    989                    m_Blocks.ElementAt(iBlockCount - 2);
    990     }
    991     if (m_pLoader->m_BlocksHeight.GetSize() > 0) {
    992       for (int32_t i = 0; i < iBlockIndex; i++) {
    993         fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1);
    994       }
    995     }
    996   }
    997   int32_t iCount = m_pLoader->m_lineHeights.GetSize();
    998   int32_t i = 0;
    999   for (i = iLineIndex; i < iCount; i++) {
   1000     FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i);
   1001     if ((i == iLineIndex) && (fLineHeight - fContentAreaHeight > 0.001)) {
   1002       fCalcHeight = 0;
   1003       return TRUE;
   1004     }
   1005     if (fLinePos + fLineHeight - fContentAreaHeight > 0.001) {
   1006       if (iBlockCount >= (iBlockIndex + 1) * 2) {
   1007         m_Blocks.SetAt(iBlockIndex * 2, iLineIndex);
   1008         m_Blocks.SetAt(iBlockIndex * 2 + 1, i - iLineIndex);
   1009       } else {
   1010         m_Blocks.Add(iLineIndex);
   1011         m_Blocks.Add(i - iLineIndex);
   1012       }
   1013       if (i == iLineIndex) {
   1014         if (fCalcHeight <= fLinePos) {
   1015           if (m_pLoader->m_BlocksHeight.GetSize() > iBlockIndex * 2 &&
   1016               (m_pLoader->m_BlocksHeight.GetAt(iBlockIndex * 2) ==
   1017                iBlockIndex)) {
   1018             m_pLoader->m_BlocksHeight.SetAt(iBlockIndex * 2 + 1, fCalcHeight);
   1019           } else {
   1020             m_pLoader->m_BlocksHeight.Add((FX_FLOAT)iBlockIndex);
   1021             m_pLoader->m_BlocksHeight.Add(fCalcHeight);
   1022           }
   1023         }
   1024         return TRUE;
   1025       }
   1026       fCalcHeight = fLinePos;
   1027       return TRUE;
   1028     }
   1029     fLinePos += fLineHeight;
   1030   }
   1031   return FALSE;
   1032 }
   1033 int32_t CXFA_TextLayout::CountBlocks() const {
   1034   int32_t iCount = m_Blocks.GetSize() / 2;
   1035   return iCount > 0 ? iCount : 1;
   1036 }
   1037 FX_BOOL CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize,
   1038                                   const CFX_SizeF& maxSize,
   1039                                   CFX_SizeF& defaultSize) {
   1040   defaultSize.x = maxSize.x;
   1041   if (defaultSize.x < 1) {
   1042     defaultSize.x = 0xFFFF;
   1043   }
   1044   if (m_pBreak != NULL) {
   1045     m_pBreak->Release();
   1046   }
   1047   m_pBreak = CreateBreak(FALSE);
   1048   FX_FLOAT fLinePos = 0;
   1049   m_iLines = 0;
   1050   m_fMaxWidth = 0;
   1051   Loader(defaultSize, fLinePos, FALSE);
   1052   if (fLinePos < 0.1f) {
   1053     fLinePos = m_textParser.GetFontSize(m_pTextProvider, NULL);
   1054   }
   1055   if (m_pTabstopContext) {
   1056     delete m_pTabstopContext;
   1057     m_pTabstopContext = NULL;
   1058   }
   1059   defaultSize.Set(m_fMaxWidth, fLinePos);
   1060   return TRUE;
   1061 }
   1062 FX_BOOL CXFA_TextLayout::Layout(const CFX_SizeF& size, FX_FLOAT* fHeight) {
   1063   if (size.x < 1) {
   1064     return FALSE;
   1065   }
   1066   Unload();
   1067   m_pBreak = CreateBreak(TRUE);
   1068   if (m_pLoader != NULL) {
   1069     m_pLoader->m_iTotalLines = -1;
   1070     m_pLoader->m_iChar = 0;
   1071   }
   1072   m_iLines = 0;
   1073   FX_FLOAT fLinePos = 0;
   1074   Loader(size, fLinePos, TRUE);
   1075   UpdateAlign(size.y, fLinePos);
   1076   if (m_pTabstopContext) {
   1077     delete m_pTabstopContext;
   1078     m_pTabstopContext = NULL;
   1079   }
   1080   if (fHeight) {
   1081     *fHeight = fLinePos;
   1082   }
   1083   return TRUE;
   1084 }
   1085 FX_BOOL CXFA_TextLayout::Layout(int32_t iBlock) {
   1086   if (m_pLoader == NULL || iBlock < 0 || iBlock >= CountBlocks()) {
   1087     return FALSE;
   1088   }
   1089   if (m_pLoader->m_fWidth < 1) {
   1090     return FALSE;
   1091   }
   1092   m_pLoader->m_iTotalLines = -1;
   1093   m_iLines = 0;
   1094   FX_FLOAT fLinePos = 0;
   1095   CXFA_Node* pNode = NULL;
   1096   CFX_SizeF szText;
   1097   szText.Set(m_pLoader->m_fWidth, m_pLoader->m_fHeight);
   1098   int32_t iCount = m_Blocks.GetSize();
   1099   int32_t iBlocksHeightCount = m_pLoader->m_BlocksHeight.GetSize();
   1100   iBlocksHeightCount /= 2;
   1101   if (iBlock < iBlocksHeightCount) {
   1102     return TRUE;
   1103   }
   1104   if (iBlock == iBlocksHeightCount) {
   1105     Unload();
   1106     m_pBreak = CreateBreak(TRUE);
   1107     fLinePos = m_pLoader->m_fStartLineOffset;
   1108     for (int32_t i = 0; i < iBlocksHeightCount; i++) {
   1109       fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1);
   1110     }
   1111     m_pLoader->m_iChar = 0;
   1112     if (iCount > 1) {
   1113       m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock * 2 + 1);
   1114     }
   1115     Loader(szText, fLinePos, TRUE);
   1116     if (iCount == 0 && m_pLoader->m_fStartLineOffset < 0.1f) {
   1117       UpdateAlign(szText.y, fLinePos);
   1118     }
   1119   } else if (m_pTextDataNode != NULL) {
   1120     iBlock *= 2;
   1121     if (iBlock < iCount - 2) {
   1122       m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock + 1);
   1123     }
   1124     m_pBreak->Reset();
   1125     if (m_bRichText) {
   1126       IFDE_XMLNode* pContainerNode = GetXMLContainerNode();
   1127       if (!pContainerNode) {
   1128         return TRUE;
   1129       }
   1130       IFDE_XMLNode* pXMLNode = m_pLoader->m_pXMLNode;
   1131       if (pXMLNode == NULL) {
   1132         return TRUE;
   1133       }
   1134       IFDE_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode;
   1135       for (; pXMLNode;
   1136            pXMLNode = pXMLNode->GetNodeItem(IFDE_XMLNode::NextSibling)) {
   1137         FX_BOOL bFlag = LoadRichText(pXMLNode, szText, fLinePos,
   1138                                      m_pLoader->m_pParentStyle, TRUE);
   1139         if (!bFlag) {
   1140           break;
   1141         }
   1142       }
   1143       while (pXMLNode == NULL) {
   1144         pXMLNode = pSaveXMLNode->GetNodeItem(IFDE_XMLNode::Parent);
   1145         if (pXMLNode == pContainerNode) {
   1146           break;
   1147         }
   1148         FX_BOOL bFlag =
   1149             LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle,
   1150                          TRUE, NULL, FALSE);
   1151         if (!bFlag) {
   1152           break;
   1153         }
   1154         pSaveXMLNode = pXMLNode;
   1155         pXMLNode = pXMLNode->GetNodeItem(IFDE_XMLNode::NextSibling);
   1156         if (!pXMLNode) {
   1157           continue;
   1158         }
   1159         for (; pXMLNode;
   1160              pXMLNode = pXMLNode->GetNodeItem(IFDE_XMLNode::NextSibling)) {
   1161           FX_BOOL bFlag = LoadRichText(pXMLNode, szText, fLinePos,
   1162                                        m_pLoader->m_pParentStyle, TRUE);
   1163           if (!bFlag) {
   1164             break;
   1165           }
   1166         }
   1167       }
   1168     } else {
   1169       pNode = m_pLoader->m_pNode;
   1170       if (pNode == NULL) {
   1171         return TRUE;
   1172       }
   1173       LoadText(pNode, szText, fLinePos, TRUE);
   1174     }
   1175   }
   1176   if (iBlock == iCount) {
   1177     if (m_pTabstopContext != NULL) {
   1178       delete m_pTabstopContext;
   1179       m_pTabstopContext = NULL;
   1180     }
   1181     if (m_pLoader != NULL) {
   1182       delete m_pLoader;
   1183       m_pLoader = NULL;
   1184     }
   1185   }
   1186   return TRUE;
   1187 }
   1188 void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex) {
   1189   if (!m_pLoader) {
   1190     return;
   1191   }
   1192   int32_t iCountHeight = m_pLoader->m_lineHeights.GetSize();
   1193   if (iCountHeight == 0) {
   1194     return;
   1195   }
   1196   FX_BOOL bEndItem = TRUE;
   1197   int32_t iBlockCount = m_Blocks.GetSize();
   1198   FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset;
   1199   int32_t iLineIndex = 0;
   1200   if (iBlockIndex > 0) {
   1201     int32_t iBlockHeightCount = m_pLoader->m_BlocksHeight.GetSize();
   1202     iBlockHeightCount /= 2;
   1203     if (iBlockHeightCount >= iBlockIndex) {
   1204       for (int32_t i = 0; i < iBlockIndex; i++) {
   1205         fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1);
   1206       }
   1207     } else {
   1208       fLinePos = 0;
   1209     }
   1210     iLineIndex = m_Blocks.ElementAt(iBlockCount - 1) +
   1211                  m_Blocks.ElementAt(iBlockCount - 2);
   1212   }
   1213   int32_t i = 0;
   1214   for (i = iLineIndex; i < iCountHeight; i++) {
   1215     FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i);
   1216     if (fLinePos + fLineHeight - rtText.height > 0.001) {
   1217       m_Blocks.Add(iLineIndex);
   1218       m_Blocks.Add(i - iLineIndex);
   1219       bEndItem = FALSE;
   1220       break;
   1221     }
   1222     fLinePos += fLineHeight;
   1223   }
   1224   if (iCountHeight > 0 && (i - iLineIndex) > 0 && bEndItem) {
   1225     m_Blocks.Add(iLineIndex);
   1226     m_Blocks.Add(i - iLineIndex);
   1227   }
   1228 }
   1229 FX_BOOL CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice,
   1230                                     const CFX_Matrix& tmDoc2Device,
   1231                                     const CFX_RectF& rtClip,
   1232                                     int32_t iBlock) {
   1233   IFDE_RenderDevice* pDevice = IFDE_RenderDevice::Create(pFxDevice);
   1234   if (pDevice == NULL) {
   1235     return FALSE;
   1236   }
   1237   FDE_HDEVICESTATE state = pDevice->SaveState();
   1238   pDevice->SetClipRect(rtClip);
   1239   IFDE_SolidBrush* pSolidBrush =
   1240       (IFDE_SolidBrush*)IFDE_Brush::Create(FDE_BRUSHTYPE_Solid);
   1241   IFDE_Pen* pPen = IFDE_Pen::Create();
   1242   FXSYS_assert(pDevice != NULL && pSolidBrush != NULL && pPen != NULL);
   1243   if (m_pieceLines.GetSize() == 0) {
   1244     int32_t iBlockCount = CountBlocks();
   1245     for (int32_t i = 0; i < iBlockCount; i++) {
   1246       Layout(i);
   1247     }
   1248   }
   1249   FXTEXT_CHARPOS* pCharPos = NULL;
   1250   int32_t iCharCount = 0;
   1251   int32_t iLineStart = 0;
   1252   int32_t iPieceLines = m_pieceLines.GetSize();
   1253   int32_t iCount = m_Blocks.GetSize();
   1254   if (iCount > 0) {
   1255     iBlock *= 2;
   1256     if (iBlock < iCount) {
   1257       iLineStart = m_Blocks.ElementAt(iBlock);
   1258       iPieceLines = m_Blocks.ElementAt(iBlock + 1);
   1259     } else {
   1260       iPieceLines = 0;
   1261     }
   1262   }
   1263   for (int32_t i = 0; i < iPieceLines; i++) {
   1264     if (i + iLineStart >= m_pieceLines.GetSize()) {
   1265       break;
   1266     }
   1267     CXFA_PieceLine* pPieceLine = m_pieceLines.GetAt(i + iLineStart);
   1268     int32_t iPieces = pPieceLine->m_textPieces.GetSize();
   1269     int32_t j = 0;
   1270     for (j = 0; j < iPieces; j++) {
   1271       XFA_LPCTEXTPIECE pPiece = pPieceLine->m_textPieces.GetAt(j);
   1272       int32_t iChars = pPiece->iChars;
   1273       if (iCharCount < iChars) {
   1274         FX_Free(pCharPos);
   1275         pCharPos = FX_Alloc(FXTEXT_CHARPOS, iChars);
   1276         iCharCount = iChars;
   1277       }
   1278       FXSYS_memset(pCharPos, 0, iCharCount * sizeof(FXTEXT_CHARPOS));
   1279       RenderString(pDevice, pSolidBrush, pPieceLine, j, pCharPos, tmDoc2Device);
   1280     }
   1281     for (j = 0; j < iPieces; j++) {
   1282       RenderPath(pDevice, pPen, pPieceLine, j, pCharPos, tmDoc2Device);
   1283     }
   1284   }
   1285   pDevice->RestoreState(state);
   1286   FX_Free(pCharPos);
   1287   pSolidBrush->Release();
   1288   pPen->Release();
   1289   pDevice->Release();
   1290   return iPieceLines;
   1291 }
   1292 void CXFA_TextLayout::UpdateAlign(FX_FLOAT fHeight, FX_FLOAT fBottom) {
   1293   fHeight -= fBottom;
   1294   if (fHeight < 0.1f) {
   1295     return;
   1296   }
   1297   switch (m_textParser.GetVAlgin(m_pTextProvider)) {
   1298     case XFA_ATTRIBUTEENUM_Middle:
   1299       fHeight /= 2.0f;
   1300       break;
   1301     case XFA_ATTRIBUTEENUM_Bottom:
   1302       break;
   1303     default:
   1304       return;
   1305   }
   1306   int32_t iCount = m_pieceLines.GetSize();
   1307   for (int32_t i = 0; i < iCount; i++) {
   1308     CXFA_PieceLine* pPieceLine = m_pieceLines.GetAt(i);
   1309     int32_t iPieces = pPieceLine->m_textPieces.GetSize();
   1310     for (int32_t j = 0; j < iPieces; j++) {
   1311       XFA_LPTEXTPIECE pPiece = pPieceLine->m_textPieces.GetAt(j);
   1312       CFX_RectF& rect = pPiece->rtPiece;
   1313       rect.top += fHeight;
   1314     }
   1315   }
   1316 }
   1317 FX_BOOL CXFA_TextLayout::Loader(const CFX_SizeF& szText,
   1318                                 FX_FLOAT& fLinePos,
   1319                                 FX_BOOL bSavePieces) {
   1320   if (m_pAllocator == NULL) {
   1321     m_pAllocator = FX_CreateAllocator(FX_ALLOCTYPE_Static, 256, 0);
   1322   }
   1323   GetTextDataNode();
   1324   if (m_pTextDataNode == NULL) {
   1325     return TRUE;
   1326   }
   1327   if (m_bRichText) {
   1328     IFDE_XMLNode* pXMLContainer = GetXMLContainerNode();
   1329     if (pXMLContainer) {
   1330       if (!m_textParser.IsParsed()) {
   1331         m_textParser.DoParse(pXMLContainer, m_pTextProvider);
   1332       }
   1333       IFDE_CSSComputedStyle* pRootStyle =
   1334           m_textParser.CreateRootStyle(m_pTextProvider);
   1335       LoadRichText(pXMLContainer, szText, fLinePos, pRootStyle, bSavePieces);
   1336       pRootStyle->Release();
   1337     }
   1338   } else {
   1339     LoadText(m_pTextDataNode, szText, fLinePos, bSavePieces);
   1340   }
   1341   return TRUE;
   1342 }
   1343 void CXFA_TextLayout::LoadText(CXFA_Node* pNode,
   1344                                const CFX_SizeF& szText,
   1345                                FX_FLOAT& fLinePos,
   1346                                FX_BOOL bSavePieces) {
   1347   InitBreak(szText.x);
   1348   CXFA_Para para = m_pTextProvider->GetParaNode();
   1349   FX_FLOAT fSpaceAbove = 0;
   1350   if (para.IsExistInXML()) {
   1351     fSpaceAbove = para.GetSpaceAbove();
   1352     if (fSpaceAbove < 0.1f) {
   1353       fSpaceAbove = 0;
   1354     }
   1355     int32_t verAlign = para.GetVerticalAlign();
   1356     switch (verAlign) {
   1357       case XFA_ATTRIBUTEENUM_Top:
   1358       case XFA_ATTRIBUTEENUM_Middle:
   1359       case XFA_ATTRIBUTEENUM_Bottom: {
   1360         fLinePos += fSpaceAbove;
   1361         break;
   1362       }
   1363     }
   1364   }
   1365   CFX_WideString wsText = pNode->GetContent();
   1366   wsText.TrimRight(L" ");
   1367   FX_BOOL bRet = AppendChar(wsText, fLinePos, fSpaceAbove, bSavePieces);
   1368   if (bRet && m_pLoader != NULL) {
   1369     m_pLoader->m_pNode = pNode;
   1370   } else {
   1371     EndBreak(FX_RTFBREAK_ParagraphBreak, fLinePos, bSavePieces);
   1372   }
   1373 }
   1374 FX_BOOL CXFA_TextLayout::LoadRichText(IFDE_XMLNode* pXMLNode,
   1375                                       const CFX_SizeF& szText,
   1376                                       FX_FLOAT& fLinePos,
   1377                                       IFDE_CSSComputedStyle* pParentStyle,
   1378                                       FX_BOOL bSavePieces,
   1379                                       CXFA_LinkUserData* pLinkData,
   1380                                       FX_BOOL bEndBreak,
   1381                                       FX_BOOL bIsOl,
   1382                                       int32_t iLiCount) {
   1383   if (pXMLNode == NULL) {
   1384     return FALSE;
   1385   }
   1386   CXFA_TextParseContext* pContext =
   1387       m_textParser.GetParseContextFromMap(pXMLNode);
   1388   FDE_CSSDISPLAY eDisplay = FDE_CSSDISPLAY_None;
   1389   FX_BOOL bContentNode = FALSE;
   1390   FX_FLOAT fSpaceBelow = 0;
   1391   IFDE_CSSComputedStyle* pStyle = NULL;
   1392   CFX_WideString wsName;
   1393   if (bEndBreak) {
   1394     FX_BOOL bCurOl = FALSE;
   1395     FX_BOOL bCurLi = FALSE;
   1396     IFDE_XMLElement* pElement = NULL;
   1397     if (pContext != NULL) {
   1398       if (m_bBlockContinue ||
   1399           (m_pLoader && pXMLNode == m_pLoader->m_pXMLNode)) {
   1400         m_bBlockContinue = TRUE;
   1401       }
   1402       if (pXMLNode->GetType() == FDE_XMLNODE_Text) {
   1403         bContentNode = TRUE;
   1404       } else if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
   1405         pElement = (IFDE_XMLElement*)pXMLNode;
   1406         pElement->GetLocalTagName(wsName);
   1407       }
   1408       if (wsName == FX_WSTRC(L"ol")) {
   1409         bIsOl = TRUE;
   1410         bCurOl = TRUE;
   1411       }
   1412       if (m_bBlockContinue || bContentNode == FALSE) {
   1413         eDisplay = pContext->GetDisplay();
   1414         if (eDisplay != FDE_CSSDISPLAY_Block &&
   1415             eDisplay != FDE_CSSDISPLAY_Inline &&
   1416             eDisplay != FDE_CSSDISPLAY_ListItem) {
   1417           return TRUE;
   1418         }
   1419         pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle);
   1420         InitBreak(bContentNode ? pParentStyle : pStyle, eDisplay, szText.x,
   1421                   pXMLNode, pParentStyle);
   1422         if ((eDisplay == FDE_CSSDISPLAY_Block ||
   1423              eDisplay == FDE_CSSDISPLAY_ListItem) &&
   1424             (pStyle != NULL) &&
   1425             (wsName.IsEmpty() ||
   1426              (wsName != FX_WSTRC(L"body") && wsName != FX_WSTRC(L"html") &&
   1427               wsName != FX_WSTRC(L"ol") && wsName != FX_WSTRC(L"ul")))) {
   1428           const FDE_CSSRECT* pRect =
   1429               pStyle->GetBoundaryStyles()->GetMarginWidth();
   1430           if (pRect) {
   1431             fLinePos += pRect->top.GetValue();
   1432             fSpaceBelow = pRect->bottom.GetValue();
   1433           }
   1434         }
   1435         if (wsName == FX_WSTRC(L"a")) {
   1436           CFX_WideString wsLinkContent;
   1437           FXSYS_assert(pElement);
   1438           pElement->GetString(FX_WSTRC(L"href").GetPtr(), wsLinkContent);
   1439           if (!wsLinkContent.IsEmpty()) {
   1440             pLinkData = FDE_NewWith(m_pAllocator) CXFA_LinkUserData(
   1441                 m_pAllocator,
   1442                 wsLinkContent.GetBuffer(wsLinkContent.GetLength()));
   1443             wsLinkContent.ReleaseBuffer(wsLinkContent.GetLength());
   1444           }
   1445         }
   1446         int32_t iTabCount =
   1447             m_textParser.CountTabs(bContentNode ? pParentStyle : pStyle);
   1448         FX_BOOL bSpaceRun =
   1449             m_textParser.IsSpaceRun(bContentNode ? pParentStyle : pStyle);
   1450         CFX_WideString wsText;
   1451         if (bContentNode && iTabCount == 0) {
   1452           ((IFDE_XMLText*)pXMLNode)->GetText(wsText);
   1453         } else if (wsName == FX_WSTRC(L"br")) {
   1454           wsText = L'\n';
   1455         } else if (wsName == FX_WSTRC(L"li")) {
   1456           bCurLi = TRUE;
   1457           if (bIsOl) {
   1458             wsText.Format(L"%d.  ", iLiCount);
   1459           } else {
   1460             wsText = 0x00B7 + FX_WSTRC(L"  ");
   1461           }
   1462         } else if (!bContentNode) {
   1463           if (iTabCount > 0)
   1464             while (iTabCount-- > 0) {
   1465               wsText += L'\t';
   1466             }
   1467           else {
   1468             m_textParser.GetEmbbedObj(m_pTextProvider, pXMLNode, wsText);
   1469           }
   1470         }
   1471         int32_t iLength = wsText.GetLength();
   1472         if (iLength > 0 && bContentNode && !bSpaceRun) {
   1473           ProcessText(wsText);
   1474         }
   1475         if (m_pLoader) {
   1476           if (wsText.GetLength() > 0 &&
   1477               (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
   1478             wsText.TrimLeft(0x20);
   1479           }
   1480           if (FDE_CSSDISPLAY_Block == eDisplay) {
   1481             m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
   1482           } else if (FDE_CSSDISPLAY_Inline == eDisplay &&
   1483                      (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) {
   1484             m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
   1485           } else if (wsText.GetLength() > 0 &&
   1486                      (0x20 == wsText.GetAt(wsText.GetLength() - 1))) {
   1487             m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
   1488           } else if (wsText.GetLength() == 0)
   1489             ;
   1490           else {
   1491             m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
   1492           }
   1493         }
   1494         if (wsText.GetLength() > 0) {
   1495           if (m_pLoader == NULL || m_pLoader->m_iChar == 0) {
   1496             if (pLinkData) {
   1497               pLinkData->AddRef();
   1498             }
   1499             CXFA_TextUserData* pUserData = FDE_NewWith(m_pAllocator)
   1500                 CXFA_TextUserData(m_pAllocator,
   1501                                   bContentNode ? pParentStyle : pStyle,
   1502                                   pLinkData);
   1503             m_pBreak->SetUserData(pUserData);
   1504           }
   1505           if (AppendChar(wsText, fLinePos, 0, bSavePieces)) {
   1506             if (m_pLoader) {
   1507               m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE;
   1508             }
   1509             if (IsEnd(bSavePieces)) {
   1510               if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
   1511                 m_pLoader->m_pXMLNode = pXMLNode;
   1512                 m_pLoader->m_pParentStyle = pParentStyle;
   1513               }
   1514               if (pStyle != NULL) {
   1515                 pStyle->Release();
   1516               }
   1517               return FALSE;
   1518             }
   1519             return TRUE;
   1520           }
   1521         }
   1522       }
   1523     }
   1524     FX_BOOL ret = TRUE;
   1525     for (IFDE_XMLNode* pChildNode =
   1526              pXMLNode->GetNodeItem(IFDE_XMLNode::FirstChild);
   1527          pChildNode;
   1528          pChildNode = pChildNode->GetNodeItem(IFDE_XMLNode::NextSibling)) {
   1529       if (bCurOl) {
   1530         iLiCount++;
   1531       }
   1532       ret = LoadRichText(pChildNode, szText, fLinePos,
   1533                          pContext ? pStyle : pParentStyle, bSavePieces,
   1534                          pLinkData, TRUE, bIsOl, iLiCount);
   1535       if (ret == FALSE) {
   1536         return FALSE;
   1537       }
   1538     }
   1539     if (m_pLoader) {
   1540       if (FDE_CSSDISPLAY_Block == eDisplay) {
   1541         m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE;
   1542       }
   1543     }
   1544     if (bCurLi) {
   1545       EndBreak(FX_RTFBREAK_LineBreak, fLinePos, bSavePieces);
   1546     }
   1547   } else {
   1548     if (pContext != NULL) {
   1549       eDisplay = pContext->GetDisplay();
   1550     }
   1551   }
   1552   if (m_bBlockContinue) {
   1553     if (pContext != NULL && !bContentNode) {
   1554       FX_DWORD dwStatus = (eDisplay == FDE_CSSDISPLAY_Block)
   1555                               ? FX_RTFBREAK_ParagraphBreak
   1556                               : FX_RTFBREAK_PieceBreak;
   1557       EndBreak(dwStatus, fLinePos, bSavePieces);
   1558       if (eDisplay == FDE_CSSDISPLAY_Block) {
   1559         fLinePos += fSpaceBelow;
   1560         if (m_pTabstopContext) {
   1561           m_pTabstopContext->RemoveAll();
   1562         }
   1563       }
   1564       if (wsName == FX_WSTRC(L"a")) {
   1565         if (pLinkData) {
   1566           pLinkData->Release();
   1567           pLinkData = nullptr;
   1568         }
   1569       }
   1570       if (IsEnd(bSavePieces)) {
   1571         if (pStyle) {
   1572           pStyle->Release();
   1573         }
   1574         if (m_pLoader && m_pLoader->m_iTotalLines > -1) {
   1575           m_pLoader->m_pXMLNode =
   1576               pXMLNode->GetNodeItem(IFDE_XMLNode::NextSibling);
   1577           m_pLoader->m_pParentStyle = pParentStyle;
   1578         }
   1579         return FALSE;
   1580       }
   1581     }
   1582   }
   1583   if (pStyle != NULL) {
   1584     pStyle->Release();
   1585   }
   1586   return TRUE;
   1587 }
   1588 FX_BOOL CXFA_TextLayout::AppendChar(const CFX_WideString& wsText,
   1589                                     FX_FLOAT& fLinePos,
   1590                                     FX_FLOAT fSpaceAbove,
   1591                                     FX_BOOL bSavePieces) {
   1592   FX_DWORD dwStatus = 0;
   1593   int32_t iChar = 0;
   1594   if (m_pLoader) {
   1595     iChar = m_pLoader->m_iChar;
   1596   }
   1597   int32_t iLength = wsText.GetLength();
   1598   for (int32_t i = iChar; i < iLength; i++) {
   1599     FX_WCHAR wch = wsText.GetAt(i);
   1600     if (wch == 0xA0) {
   1601       wch = 0x20;
   1602     }
   1603     if ((dwStatus = m_pBreak->AppendChar(wch)) > FX_RTFBREAK_PieceBreak) {
   1604       AppendTextLine(dwStatus, fLinePos, bSavePieces);
   1605       if (IsEnd(bSavePieces)) {
   1606         if (m_pLoader != NULL) {
   1607           m_pLoader->m_iChar = i;
   1608         }
   1609         return TRUE;
   1610       }
   1611       if (dwStatus == FX_RTFBREAK_ParagraphBreak && m_bRichText) {
   1612         fLinePos += fSpaceAbove;
   1613       }
   1614     }
   1615   }
   1616   if (m_pLoader) {
   1617     m_pLoader->m_iChar = 0;
   1618   }
   1619   return FALSE;
   1620 }
   1621 FX_BOOL CXFA_TextLayout::IsEnd(FX_BOOL bSavePieces) {
   1622   if (!bSavePieces) {
   1623     return FALSE;
   1624   }
   1625   if (m_pLoader && m_pLoader->m_iTotalLines > 0) {
   1626     return m_iLines >= m_pLoader->m_iTotalLines;
   1627   }
   1628   return FALSE;
   1629 }
   1630 void CXFA_TextLayout::ProcessText(CFX_WideString& wsText) {
   1631   int32_t iLen = wsText.GetLength();
   1632   if (iLen == 0) {
   1633     return;
   1634   }
   1635   FX_WCHAR* psz = wsText.GetBuffer(iLen);
   1636   int32_t iTrimLeft = 0;
   1637   FX_WCHAR wch = 0, wPrev = 0;
   1638   for (int32_t i = 0; i < iLen; i++) {
   1639     wch = psz[i];
   1640     if (wch < 0x20) {
   1641       wch = 0x20;
   1642     }
   1643     if (wch == 0x20 && wPrev == 0x20) {
   1644       continue;
   1645     }
   1646     wPrev = wch;
   1647     psz[iTrimLeft++] = wch;
   1648   }
   1649   wsText.ReleaseBuffer(iLen);
   1650   wsText = wsText.Left(iTrimLeft);
   1651 }
   1652 void CXFA_TextLayout::EndBreak(FX_DWORD dwStatus,
   1653                                FX_FLOAT& fLinePos,
   1654                                FX_BOOL bSavePieces) {
   1655   dwStatus = m_pBreak->EndBreak(dwStatus);
   1656   if (dwStatus > FX_RTFBREAK_PieceBreak) {
   1657     AppendTextLine(dwStatus, fLinePos, bSavePieces, TRUE);
   1658   }
   1659 }
   1660 void CXFA_TextLayout::DoTabstops(IFDE_CSSComputedStyle* pStyle,
   1661                                  CXFA_PieceLine* pPieceLine) {
   1662   if (m_pTabstopContext == NULL || m_pTabstopContext->m_iTabCount == 0) {
   1663     return;
   1664   }
   1665   if (pStyle == NULL || pPieceLine == NULL) {
   1666     return;
   1667   }
   1668   int32_t iPieces = pPieceLine->m_textPieces.GetSize();
   1669   if (iPieces == 0) {
   1670     return;
   1671   }
   1672   XFA_LPTEXTPIECE pPiece = pPieceLine->m_textPieces.GetAt(iPieces - 1);
   1673   int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex;
   1674   int32_t iCount = m_textParser.CountTabs(pStyle);
   1675   if (iTabstopsIndex > m_pTabstopContext->m_iTabCount - 1) {
   1676     return;
   1677   }
   1678   if (iCount > 0) {
   1679     iTabstopsIndex++;
   1680     m_pTabstopContext->m_bTabstops = TRUE;
   1681     FX_FLOAT fRight = 0;
   1682     if (iPieces > 1) {
   1683       XFA_LPTEXTPIECE p = pPieceLine->m_textPieces.GetAt(iPieces - 2);
   1684       fRight = p->rtPiece.right();
   1685     }
   1686     m_pTabstopContext->m_fTabWidth =
   1687         pPiece->rtPiece.width + pPiece->rtPiece.left - fRight;
   1688   } else if (iTabstopsIndex > -1) {
   1689     FX_FLOAT fLeft = 0;
   1690     if (m_pTabstopContext->m_bTabstops) {
   1691       XFA_TABSTOPS* pTabstops =
   1692           m_pTabstopContext->m_tabstops.GetDataPtr(iTabstopsIndex);
   1693       FX_DWORD dwAlgin = pTabstops->dwAlign;
   1694       if (dwAlgin == FX_HashCode_String_GetW(L"center", 6)) {
   1695         fLeft = pPiece->rtPiece.width / 2.0f;
   1696       } else if (dwAlgin == FX_HashCode_String_GetW(L"right", 5) ||
   1697                  dwAlgin == FX_HashCode_String_GetW(L"before", 6)) {
   1698         fLeft = pPiece->rtPiece.width;
   1699       } else if (dwAlgin == FX_HashCode_String_GetW(L"decimal", 7)) {
   1700         int32_t iChars = pPiece->iChars;
   1701         for (int32_t i = 0; i < iChars; i++) {
   1702           if (pPiece->pszText[i] == L'.') {
   1703             break;
   1704           }
   1705           fLeft += pPiece->pWidths[i] / 20000.0f;
   1706         }
   1707       }
   1708       m_pTabstopContext->m_fLeft =
   1709           std::min(fLeft, m_pTabstopContext->m_fTabWidth);
   1710       m_pTabstopContext->m_bTabstops = FALSE;
   1711       m_pTabstopContext->m_fTabWidth = 0;
   1712     }
   1713     pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft;
   1714   }
   1715 }
   1716 void CXFA_TextLayout::AppendTextLine(FX_DWORD dwStatus,
   1717                                      FX_FLOAT& fLinePos,
   1718                                      FX_BOOL bSavePieces,
   1719                                      FX_BOOL bEndBreak) {
   1720   int32_t iPieces = m_pBreak->CountBreakPieces();
   1721   if (iPieces < 1) {
   1722     return;
   1723   }
   1724   IFDE_CSSComputedStyle* pStyle = NULL;
   1725   if (bSavePieces) {
   1726     CXFA_PieceLine* pPieceLine = FDE_NewWith(m_pAllocator) CXFA_PieceLine;
   1727     m_pieceLines.Add(pPieceLine);
   1728     if (m_pTabstopContext) {
   1729       m_pTabstopContext->Reset();
   1730     }
   1731     FX_FLOAT fLineStep = 0, fBaseLine = 0;
   1732     int32_t i = 0;
   1733     for (i = 0; i < iPieces; i++) {
   1734       const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i);
   1735       CXFA_TextUserData* pUserData = (CXFA_TextUserData*)pPiece->m_pUserData;
   1736       if (pUserData != NULL) {
   1737         pStyle = pUserData->m_pStyle;
   1738       }
   1739       FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f;
   1740       XFA_LPTEXTPIECE pTP =
   1741           (XFA_LPTEXTPIECE)m_pAllocator->Alloc(sizeof(XFA_TEXTPIECE));
   1742       pTP->pszText =
   1743           (FX_WCHAR*)m_pAllocator->Alloc(pPiece->m_iChars * sizeof(FX_WCHAR));
   1744       pTP->pWidths =
   1745           (int32_t*)m_pAllocator->Alloc(pPiece->m_iChars * sizeof(int32_t));
   1746       pTP->iChars = pPiece->m_iChars;
   1747       pPiece->GetString(pTP->pszText);
   1748       pPiece->GetWidths(pTP->pWidths);
   1749       pTP->iBidiLevel = pPiece->m_iBidiLevel;
   1750       pTP->iHorScale = pPiece->m_iHorizontalScale;
   1751       pTP->iVerScale = pPiece->m_iVerticalScale;
   1752       m_textParser.GetUnderline(m_pTextProvider, pStyle, pTP->iUnderline,
   1753                                 pTP->iPeriod);
   1754       m_textParser.GetLinethrough(m_pTextProvider, pStyle, pTP->iLineThrough);
   1755       pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle);
   1756       pTP->pFont = m_textParser.GetFont(m_pTextProvider, pStyle);
   1757       pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
   1758       pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f;
   1759       pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f;
   1760       pTP->rtPiece.height = (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f;
   1761       FX_FLOAT fBaseLineTemp =
   1762           m_textParser.GetBaseline(m_pTextProvider, pStyle);
   1763       pTP->rtPiece.top = fBaseLineTemp;
   1764       pPieceLine->m_textPieces.Add(pTP);
   1765       FX_FLOAT fLineHeight = m_textParser.GetLineHeight(
   1766           m_pTextProvider, pStyle, m_iLines == 0, fVerScale);
   1767       if (fBaseLineTemp > 0) {
   1768         FX_FLOAT fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height;
   1769         if (fLineHeight < fLineHeightTmp) {
   1770           fLineHeight = fLineHeightTmp;
   1771         } else {
   1772           fBaseLineTemp = 0;
   1773         }
   1774       } else if (fBaseLine < -fBaseLineTemp) {
   1775         fBaseLine = -fBaseLineTemp;
   1776       }
   1777       fLineStep = std::max(fLineStep, fLineHeight);
   1778       if (pUserData != NULL && pUserData->m_pLinkData != NULL) {
   1779         pUserData->m_pLinkData->AddRef();
   1780         pTP->pLinkData = pUserData->m_pLinkData;
   1781       } else {
   1782         pTP->pLinkData = NULL;
   1783       }
   1784       DoTabstops(pStyle, pPieceLine);
   1785     }
   1786     for (i = 0; i < iPieces; i++) {
   1787       XFA_LPTEXTPIECE pTP = pPieceLine->m_textPieces.GetAt(i);
   1788       FX_FLOAT& fTop = pTP->rtPiece.top;
   1789       FX_FLOAT fBaseLineTemp = fTop;
   1790       fTop = fLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp;
   1791       fTop = std::max(0.0f, fTop);
   1792     }
   1793     fLinePos += fLineStep + fBaseLine;
   1794   } else {
   1795     FX_FLOAT fLineStep = 0;
   1796     FX_FLOAT fLineWidth = 0;
   1797     for (int32_t i = 0; i < iPieces; i++) {
   1798       const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i);
   1799       CXFA_TextUserData* pUserData = (CXFA_TextUserData*)pPiece->m_pUserData;
   1800       if (pUserData != NULL) {
   1801         pStyle = pUserData->m_pStyle;
   1802       }
   1803       FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f;
   1804       FX_FLOAT fBaseLine = m_textParser.GetBaseline(m_pTextProvider, pStyle);
   1805       FX_FLOAT fLineHeight = m_textParser.GetLineHeight(
   1806           m_pTextProvider, pStyle, m_iLines == 0, fVerScale);
   1807       if (fBaseLine > 0) {
   1808         FX_FLOAT fLineHeightTmp =
   1809             fBaseLine + (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f;
   1810         if (fLineHeight < fLineHeightTmp) {
   1811           fLineHeight = fLineHeightTmp;
   1812         }
   1813       }
   1814       fLineStep = std::max(fLineStep, fLineHeight);
   1815       fLineWidth += pPiece->m_iWidth / 20000.0f;
   1816     }
   1817     fLinePos += fLineStep;
   1818     m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth);
   1819     if (m_pLoader && m_pLoader->m_bSaveLineHeight) {
   1820       FX_FLOAT fHeight = fLinePos - m_pLoader->m_fLastPos;
   1821       m_pLoader->m_fLastPos = fLinePos;
   1822       m_pLoader->m_lineHeights.Add(fHeight);
   1823     }
   1824   }
   1825   if (pStyle) {
   1826     pStyle->AddRef();
   1827   }
   1828   m_pBreak->ClearBreakPieces();
   1829   if (dwStatus == FX_RTFBREAK_ParagraphBreak) {
   1830     m_pBreak->Reset();
   1831     if (!pStyle && bEndBreak) {
   1832       CXFA_Para para = m_pTextProvider->GetParaNode();
   1833       if (para.IsExistInXML()) {
   1834         FX_FLOAT fStartPos = para.GetMarginLeft();
   1835         FX_FLOAT fIndent = para.GetTextIndent();
   1836         if (fIndent > 0) {
   1837           fStartPos += fIndent;
   1838         }
   1839         FX_FLOAT fSpaceBelow = para.GetSpaceBelow();
   1840         if (fSpaceBelow < 0.1f) {
   1841           fSpaceBelow = 0;
   1842         }
   1843         m_pBreak->SetLineStartPos(fStartPos);
   1844         fLinePos += fSpaceBelow;
   1845       }
   1846     }
   1847   }
   1848   if (pStyle) {
   1849     FX_FLOAT fStart = 0;
   1850     const FDE_CSSRECT* pRect = pStyle->GetBoundaryStyles()->GetMarginWidth();
   1851     if (pRect) {
   1852       fStart = pRect->left.GetValue();
   1853     }
   1854     FX_FLOAT fTextIndent =
   1855         pStyle->GetParagraphStyles()->GetTextIndent().GetValue();
   1856     if (fTextIndent < 0) {
   1857       fStart -= fTextIndent;
   1858     }
   1859     m_pBreak->SetLineStartPos(fStart);
   1860     pStyle->Release();
   1861   }
   1862   m_iLines++;
   1863 }
   1864 void CXFA_TextLayout::RenderString(IFDE_RenderDevice* pDevice,
   1865                                    IFDE_SolidBrush* pBrush,
   1866                                    CXFA_PieceLine* pPieceLine,
   1867                                    int32_t iPiece,
   1868                                    FXTEXT_CHARPOS* pCharPos,
   1869                                    const CFX_Matrix& tmDoc2Device) {
   1870   XFA_LPCTEXTPIECE pPiece = pPieceLine->m_textPieces.GetAt(iPiece);
   1871   int32_t iCount = GetDisplayPos(pPiece, pCharPos);
   1872   if (iCount > 0) {
   1873     pBrush->SetColor(pPiece->dwColor);
   1874     pDevice->DrawString(pBrush, pPiece->pFont, pCharPos, iCount,
   1875                         pPiece->fFontSize, &tmDoc2Device);
   1876   }
   1877   pPieceLine->m_charCounts.Add(iCount);
   1878 }
   1879 void CXFA_TextLayout::RenderPath(IFDE_RenderDevice* pDevice,
   1880                                  IFDE_Pen* pPen,
   1881                                  CXFA_PieceLine* pPieceLine,
   1882                                  int32_t iPiece,
   1883                                  FXTEXT_CHARPOS* pCharPos,
   1884                                  const CFX_Matrix& tmDoc2Device) {
   1885   XFA_TEXTPIECE* pPiece = pPieceLine->m_textPieces.GetAt(iPiece);
   1886   FX_BOOL bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2;
   1887   FX_BOOL bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2;
   1888   if (bNoUnderline && bNoLineThrough) {
   1889     return;
   1890   }
   1891   pPen->SetColor(pPiece->dwColor);
   1892   IFDE_Path* pPath = IFDE_Path::Create();
   1893   int32_t iChars = GetDisplayPos(pPiece, pCharPos);
   1894   if (iChars > 0) {
   1895     CFX_PointF pt1, pt2;
   1896     FX_FLOAT fEndY = pCharPos[0].m_OriginY + 1.05f;
   1897     int32_t i = 0;
   1898     if (pPiece->iPeriod == XFA_ATTRIBUTEENUM_Word) {
   1899       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
   1900         for (int32_t j = 0; j < iChars; j++) {
   1901           pt1.x = pCharPos[j].m_OriginX;
   1902           pt2.x =
   1903               pt1.x + pCharPos[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1904           pt1.y = pt2.y = fEndY;
   1905           pPath->AddLine(pt1, pt2);
   1906         }
   1907         fEndY += 2.0f;
   1908       }
   1909     } else {
   1910       pt1.x = pCharPos[0].m_OriginX;
   1911       pt2.x =
   1912           pCharPos[iChars - 1].m_OriginX +
   1913           pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1914       for (int32_t i = 0; i < pPiece->iUnderline; i++) {
   1915         pt1.y = pt2.y = fEndY;
   1916         pPath->AddLine(pt1, pt2);
   1917         fEndY += 2.0f;
   1918       }
   1919     }
   1920     fEndY = pCharPos[0].m_OriginY - pPiece->rtPiece.height * 0.25f;
   1921     pt1.x = pCharPos[0].m_OriginX;
   1922     pt2.x = pCharPos[iChars - 1].m_OriginX +
   1923             pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1924     for (i = 0; i < pPiece->iLineThrough; i++) {
   1925       pt1.y = pt2.y = fEndY;
   1926       pPath->AddLine(pt1, pt2);
   1927       fEndY += 2.0f;
   1928     }
   1929   } else {
   1930     if (bNoLineThrough &&
   1931         (bNoUnderline || pPiece->iPeriod != XFA_ATTRIBUTEENUM_All)) {
   1932       goto XFA_RenderPathRet;
   1933     }
   1934     int32_t iCharsTmp = 0;
   1935     int32_t iPiecePrev = iPiece, iPieceNext = iPiece;
   1936     while (iPiecePrev > 0) {
   1937       iPiecePrev--;
   1938       iCharsTmp = pPieceLine->m_charCounts.GetAt(iPiecePrev);
   1939       if (iCharsTmp > 0) {
   1940         break;
   1941       }
   1942     }
   1943     if (iCharsTmp == 0) {
   1944       goto XFA_RenderPathRet;
   1945     }
   1946     iCharsTmp = 0;
   1947     int32_t iPieces = pPieceLine->m_textPieces.GetSize();
   1948     while (iPieceNext < iPieces - 1) {
   1949       iPieceNext++;
   1950       iCharsTmp = pPieceLine->m_charCounts.GetAt(iPieceNext);
   1951       if (iCharsTmp > 0) {
   1952         break;
   1953       }
   1954     }
   1955     if (iCharsTmp == 0) {
   1956       goto XFA_RenderPathRet;
   1957     }
   1958     FX_FLOAT fOrgX = 0.0f, fEndX = 0.0f;
   1959     pPiece = pPieceLine->m_textPieces.GetAt(iPiecePrev);
   1960     iChars = GetDisplayPos(pPiece, pCharPos);
   1961     if (iChars < 1) {
   1962       goto XFA_RenderPathRet;
   1963     }
   1964     fOrgX = pCharPos[iChars - 1].m_OriginX +
   1965             pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
   1966     pPiece = pPieceLine->m_textPieces.GetAt(iPieceNext);
   1967     iChars = GetDisplayPos(pPiece, pCharPos);
   1968     if (iChars < 1) {
   1969       goto XFA_RenderPathRet;
   1970     }
   1971     fEndX = pCharPos[0].m_OriginX;
   1972     CFX_PointF pt1, pt2;
   1973     pt1.x = fOrgX, pt2.x = fEndX;
   1974     FX_FLOAT fEndY = pCharPos[0].m_OriginY + 1.05f;
   1975     int32_t i = 0;
   1976     for (i = 0; i < pPiece->iUnderline; i++) {
   1977       pt1.y = pt2.y = fEndY;
   1978       pPath->AddLine(pt1, pt2);
   1979       fEndY += 2.0f;
   1980     }
   1981     fEndY = pCharPos[0].m_OriginY - pPiece->rtPiece.height * 0.25f;
   1982     for (i = 0; i < pPiece->iLineThrough; i++) {
   1983       pt1.y = pt2.y = fEndY;
   1984       pPath->AddLine(pt1, pt2);
   1985       fEndY += 2.0f;
   1986     }
   1987   }
   1988   pDevice->DrawPath(pPen, 1, pPath, &tmDoc2Device);
   1989 XFA_RenderPathRet:
   1990   pPath->Release();
   1991 }
   1992 int32_t CXFA_TextLayout::GetDisplayPos(XFA_LPCTEXTPIECE pPiece,
   1993                                        FXTEXT_CHARPOS* pCharPos,
   1994                                        FX_BOOL bCharCode) {
   1995   if (pPiece == NULL) {
   1996     return 0;
   1997   }
   1998   FX_RTFTEXTOBJ tr;
   1999   if (!ToRun(pPiece, tr)) {
   2000     return 0;
   2001   }
   2002   return m_pBreak->GetDisplayPos(&tr, pCharPos, bCharCode);
   2003 }
   2004 FX_BOOL CXFA_TextLayout::ToRun(XFA_LPCTEXTPIECE pPiece, FX_RTFTEXTOBJ& tr) {
   2005   int32_t iLength = pPiece->iChars;
   2006   if (iLength < 1) {
   2007     return FALSE;
   2008   }
   2009   tr.pStr = pPiece->pszText;
   2010   tr.pFont = pPiece->pFont;
   2011   tr.pRect = &pPiece->rtPiece;
   2012   tr.pWidths = pPiece->pWidths;
   2013   tr.iLength = iLength;
   2014   tr.fFontSize = pPiece->fFontSize;
   2015   tr.iBidiLevel = pPiece->iBidiLevel;
   2016   tr.iCharRotation = 0;
   2017   tr.wLineBreakChar = L'\n';
   2018   tr.iVerticalScale = pPiece->iVerScale;
   2019   tr.dwLayoutStyles = FX_RTFLAYOUTSTYLE_ExpandTab;
   2020   tr.iHorizontalScale = pPiece->iHorScale;
   2021   return TRUE;
   2022 }
   2023