|  | // Copyright 2014 PDFium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | 
|  |  | 
|  | #include "xfa/fxfa/app/xfa_textlayout.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "core/fxcrt/include/fx_ext.h" | 
|  | #include "xfa/fde/cfde_path.h" | 
|  | #include "xfa/fde/css/fde_csscache.h" | 
|  | #include "xfa/fde/css/fde_cssstyleselector.h" | 
|  | #include "xfa/fde/fde_gedevice.h" | 
|  | #include "xfa/fde/fde_object.h" | 
|  | #include "xfa/fde/xml/fde_xml_imp.h" | 
|  | #include "xfa/fgas/crt/fgas_codepage.h" | 
|  | #include "xfa/fxfa/app/xfa_ffwidgetacc.h" | 
|  | #include "xfa/fxfa/include/xfa_ffapp.h" | 
|  | #include "xfa/fxfa/include/xfa_ffdoc.h" | 
|  | #include "xfa/fxfa/include/xfa_fontmgr.h" | 
|  |  | 
|  | void CXFA_TextParseContext::SetDecls(const CFDE_CSSDeclaration** ppDeclArray, | 
|  | int32_t iDeclCount) { | 
|  | if (iDeclCount <= 0 || !ppDeclArray) | 
|  | return; | 
|  |  | 
|  | m_dwMatchedDecls = iDeclCount; | 
|  | m_ppMatchedDecls = FX_Alloc(CFDE_CSSDeclaration*, iDeclCount); | 
|  | FXSYS_memcpy(m_ppMatchedDecls, ppDeclArray, | 
|  | iDeclCount * sizeof(CFDE_CSSDeclaration*)); | 
|  | } | 
|  |  | 
|  | CXFA_TextParser::CXFA_TextParser() : m_pUASheet(nullptr) {} | 
|  |  | 
|  | CXFA_TextParser::~CXFA_TextParser() { | 
|  | if (m_pUASheet) | 
|  | m_pUASheet->Release(); | 
|  |  | 
|  | FX_POSITION ps = m_mapXMLNodeToParseContext.GetStartPosition(); | 
|  | while (ps) { | 
|  | CFDE_XMLNode* pXMLNode; | 
|  | CXFA_TextParseContext* pParseContext; | 
|  | m_mapXMLNodeToParseContext.GetNextAssoc(ps, pXMLNode, pParseContext); | 
|  | if (pParseContext) | 
|  | FXTARGET_DeleteWith(CXFA_TextParseContext, m_pAllocator.get(), | 
|  | pParseContext); | 
|  | } | 
|  | m_mapXMLNodeToParseContext.RemoveAll(); | 
|  | } | 
|  | void CXFA_TextParser::Reset() { | 
|  | FX_POSITION ps = m_mapXMLNodeToParseContext.GetStartPosition(); | 
|  | while (ps) { | 
|  | CFDE_XMLNode* pXMLNode; | 
|  | CXFA_TextParseContext* pParseContext; | 
|  | m_mapXMLNodeToParseContext.GetNextAssoc(ps, pXMLNode, pParseContext); | 
|  | if (pParseContext) | 
|  | FXTARGET_DeleteWith(CXFA_TextParseContext, m_pAllocator.get(), | 
|  | pParseContext); | 
|  | } | 
|  | m_mapXMLNodeToParseContext.RemoveAll(); | 
|  | m_pAllocator.reset(); | 
|  | } | 
|  | void CXFA_TextParser::InitCSSData(CXFA_TextProvider* pTextProvider) { | 
|  | if (!pTextProvider) | 
|  | return; | 
|  |  | 
|  | if (!m_pSelector) { | 
|  | CXFA_FFDoc* pDoc = pTextProvider->GetDocNode(); | 
|  | IFGAS_FontMgr* pFontMgr = pDoc->GetApp()->GetFDEFontMgr(); | 
|  | ASSERT(pFontMgr); | 
|  | m_pSelector.reset(new CFDE_CSSStyleSelector); | 
|  | m_pSelector->SetFontMgr(pFontMgr); | 
|  | FX_FLOAT fFontSize = 10; | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | if (font) { | 
|  | fFontSize = font.GetFontSize(); | 
|  | } | 
|  | m_pSelector->SetDefFontSize(fFontSize); | 
|  | } | 
|  | if (!m_pUASheet) { | 
|  | m_pUASheet = LoadDefaultSheetStyle(); | 
|  | m_pSelector->SetStyleSheet(FDE_CSSSTYLESHEETGROUP_UserAgent, m_pUASheet); | 
|  | m_pSelector->UpdateStyleIndex(FDE_CSSMEDIATYPE_ALL); | 
|  | } | 
|  | } | 
|  | IFDE_CSSStyleSheet* CXFA_TextParser::LoadDefaultSheetStyle() { | 
|  | static const FX_WCHAR s_pStyle[] = | 
|  | L"html,body,ol,p,ul{display:block}" | 
|  | L"li{display:list-item}" | 
|  | L"ol,ul{padding-left:33px}ol{list-style-type:decimal}ol,ul{margin-top:0;" | 
|  | L"margin-bottom:0}ul,ol{margin:1.12em 0}" | 
|  | L"a{color:#0000ff;text-decoration:underline}b{font-weight:bolder}i{font-" | 
|  | L"style:italic}" | 
|  | L"sup{vertical-align:+15em;font-size:.66em}sub{vertical-align:-15em;font-" | 
|  | L"size:.66em}"; | 
|  | return IFDE_CSSStyleSheet::LoadFromBuffer( | 
|  | CFX_WideString(), s_pStyle, FXSYS_wcslen(s_pStyle), FX_CODEPAGE_UTF8); | 
|  | } | 
|  | IFDE_CSSComputedStyle* CXFA_TextParser::CreateRootStyle( | 
|  | CXFA_TextProvider* pTextProvider) { | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | CXFA_Para para = pTextProvider->GetParaNode(); | 
|  | IFDE_CSSComputedStyle* pStyle = m_pSelector->CreateComputedStyle(NULL); | 
|  | IFDE_CSSFontStyle* pFontStyle = pStyle->GetFontStyles(); | 
|  | IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles(); | 
|  | FX_FLOAT fLineHeight = 0, fFontSize = 10; | 
|  | if (para) { | 
|  | fLineHeight = para.GetLineHeight(); | 
|  | FDE_CSSLENGTH indent; | 
|  | indent.Set(FDE_CSSLENGTHUNIT_Point, para.GetTextIndent()); | 
|  | pParaStyle->SetTextIndent(indent); | 
|  | FDE_CSSTEXTALIGN hAlign = FDE_CSSTEXTALIGN_Left; | 
|  | switch (para.GetHorizontalAlign()) { | 
|  | case XFA_ATTRIBUTEENUM_Center: | 
|  | hAlign = FDE_CSSTEXTALIGN_Center; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_Right: | 
|  | hAlign = FDE_CSSTEXTALIGN_Right; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_Justify: | 
|  | hAlign = FDE_CSSTEXTALIGN_Justify; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_JustifyAll: | 
|  | hAlign = FDE_CSSTEXTALIGN_JustifyAll; | 
|  | break; | 
|  | } | 
|  | pParaStyle->SetTextAlign(hAlign); | 
|  | FDE_CSSRECT rtMarginWidth; | 
|  | rtMarginWidth.left.Set(FDE_CSSLENGTHUNIT_Point, para.GetMarginLeft()); | 
|  | rtMarginWidth.top.Set(FDE_CSSLENGTHUNIT_Point, para.GetSpaceAbove()); | 
|  | rtMarginWidth.right.Set(FDE_CSSLENGTHUNIT_Point, para.GetMarginRight()); | 
|  | rtMarginWidth.bottom.Set(FDE_CSSLENGTHUNIT_Point, para.GetSpaceBelow()); | 
|  | pStyle->GetBoundaryStyles()->SetMarginWidth(rtMarginWidth); | 
|  | } | 
|  | if (font) { | 
|  | pFontStyle->SetColor(font.GetColor()); | 
|  | pFontStyle->SetFontStyle(font.IsItalic() ? FDE_CSSFONTSTYLE_Italic | 
|  | : FDE_CSSFONTSTYLE_Normal); | 
|  | pFontStyle->SetFontWeight(font.IsBold() ? FXFONT_FW_BOLD | 
|  | : FXFONT_FW_NORMAL); | 
|  | pParaStyle->SetNumberVerticalAlign(-font.GetBaselineShift()); | 
|  | fFontSize = font.GetFontSize(); | 
|  | FDE_CSSLENGTH letterSpacing; | 
|  | letterSpacing.Set(FDE_CSSLENGTHUNIT_Point, font.GetLetterSpacing()); | 
|  | pParaStyle->SetLetterSpacing(letterSpacing); | 
|  | uint32_t dwDecoration = 0; | 
|  | if (font.GetLineThrough() > 0) { | 
|  | dwDecoration |= FDE_CSSTEXTDECORATION_LineThrough; | 
|  | } | 
|  | if (font.GetUnderline() > 1) { | 
|  | dwDecoration |= FDE_CSSTEXTDECORATION_Double; | 
|  | } else if (font.GetUnderline() > 0) { | 
|  | dwDecoration |= FDE_CSSTEXTDECORATION_Underline; | 
|  | } | 
|  | pParaStyle->SetTextDecoration(dwDecoration); | 
|  | } | 
|  | pParaStyle->SetLineHeight(fLineHeight); | 
|  | pFontStyle->SetFontSize(fFontSize); | 
|  | return pStyle; | 
|  | } | 
|  | IFDE_CSSComputedStyle* CXFA_TextParser::CreateStyle( | 
|  | IFDE_CSSComputedStyle* pParentStyle) { | 
|  | IFDE_CSSComputedStyle* pNewStyle = | 
|  | m_pSelector->CreateComputedStyle(pParentStyle); | 
|  | ASSERT(pNewStyle); | 
|  | if (pParentStyle) { | 
|  | IFDE_CSSParagraphStyle* pParaStyle = pParentStyle->GetParagraphStyles(); | 
|  | uint32_t dwDecoration = pParaStyle->GetTextDecoration(); | 
|  | FX_FLOAT fBaseLine = 0; | 
|  | if (pParaStyle->GetVerticalAlign() == FDE_CSSVERTICALALIGN_Number) { | 
|  | fBaseLine = pParaStyle->GetNumberVerticalAlign(); | 
|  | } | 
|  | pParaStyle = pNewStyle->GetParagraphStyles(); | 
|  | pParaStyle->SetTextDecoration(dwDecoration); | 
|  | pParaStyle->SetNumberVerticalAlign(fBaseLine); | 
|  | IFDE_CSSBoundaryStyle* pBoundarytyle = pParentStyle->GetBoundaryStyles(); | 
|  | const FDE_CSSRECT* pRect = pBoundarytyle->GetMarginWidth(); | 
|  | if (pRect) { | 
|  | pBoundarytyle = pNewStyle->GetBoundaryStyles(); | 
|  | pBoundarytyle->SetMarginWidth(*pRect); | 
|  | } | 
|  | } | 
|  | return pNewStyle; | 
|  | } | 
|  | IFDE_CSSComputedStyle* CXFA_TextParser::ComputeStyle( | 
|  | CFDE_XMLNode* pXMLNode, | 
|  | IFDE_CSSComputedStyle* pParentStyle) { | 
|  | CXFA_TextParseContext* pContext = static_cast<CXFA_TextParseContext*>( | 
|  | m_mapXMLNodeToParseContext.GetValueAt(pXMLNode)); | 
|  | if (!pContext) | 
|  | return nullptr; | 
|  | pContext->m_pParentStyle = pParentStyle; | 
|  | pParentStyle->Retain(); | 
|  | CXFA_CSSTagProvider tagProvider; | 
|  | ParseTagInfo(pXMLNode, tagProvider); | 
|  | if (tagProvider.m_bContent) | 
|  | return nullptr; | 
|  | IFDE_CSSComputedStyle* pStyle = CreateStyle(pParentStyle); | 
|  | CFDE_CSSAccelerator* pCSSAccel = m_pSelector->InitAccelerator(); | 
|  | pCSSAccel->OnEnterTag(&tagProvider); | 
|  | m_pSelector->ComputeStyle(&tagProvider, pContext->GetDecls(), | 
|  | pContext->CountDecls(), pStyle); | 
|  | pCSSAccel->OnLeaveTag(&tagProvider); | 
|  | return pStyle; | 
|  | } | 
|  | void CXFA_TextParser::DoParse(CFDE_XMLNode* pXMLContainer, | 
|  | CXFA_TextProvider* pTextProvider) { | 
|  | if (pXMLContainer == NULL || pTextProvider == NULL || m_pAllocator) { | 
|  | return; | 
|  | } | 
|  | m_pAllocator.reset(IFX_MemoryAllocator::Create(FX_ALLOCTYPE_Fixed, 32, | 
|  | sizeof(CXFA_CSSTagProvider))); | 
|  | InitCSSData(pTextProvider); | 
|  | IFDE_CSSComputedStyle* pRootStyle = CreateRootStyle(pTextProvider); | 
|  | ParseRichText(pXMLContainer, pRootStyle); | 
|  | pRootStyle->Release(); | 
|  | } | 
|  | void CXFA_TextParser::ParseRichText(CFDE_XMLNode* pXMLNode, | 
|  | IFDE_CSSComputedStyle* pParentStyle) { | 
|  | if (!pXMLNode) | 
|  | return; | 
|  |  | 
|  | CXFA_CSSTagProvider tagProvider; | 
|  | ParseTagInfo(pXMLNode, tagProvider); | 
|  | if (!tagProvider.m_bTagAvailable) | 
|  | return; | 
|  |  | 
|  | IFDE_CSSComputedStyle* pNewStyle = NULL; | 
|  | if ((tagProvider.GetTagName() != FX_WSTRC(L"body")) || | 
|  | (tagProvider.GetTagName() != FX_WSTRC(L"html"))) { | 
|  | CXFA_TextParseContext* pTextContext = | 
|  | FXTARGET_NewWith(m_pAllocator.get()) CXFA_TextParseContext; | 
|  | FDE_CSSDISPLAY eDisplay = FDE_CSSDISPLAY_Inline; | 
|  | if (!tagProvider.m_bContent) { | 
|  | pNewStyle = CreateStyle(pParentStyle); | 
|  | CFDE_CSSAccelerator* pCSSAccel = m_pSelector->InitAccelerator(); | 
|  | pCSSAccel->OnEnterTag(&tagProvider); | 
|  | CFDE_CSSDeclarationArray DeclArray; | 
|  | int32_t iMatchedDecls = | 
|  | m_pSelector->MatchDeclarations(&tagProvider, DeclArray); | 
|  | const CFDE_CSSDeclaration** ppMatchDecls = | 
|  | const_cast<const CFDE_CSSDeclaration**>(DeclArray.GetData()); | 
|  | m_pSelector->ComputeStyle(&tagProvider, ppMatchDecls, iMatchedDecls, | 
|  | pNewStyle); | 
|  | pCSSAccel->OnLeaveTag(&tagProvider); | 
|  | if (iMatchedDecls > 0) { | 
|  | pTextContext->SetDecls(ppMatchDecls, iMatchedDecls); | 
|  | } | 
|  | eDisplay = pNewStyle->GetPositionStyles()->GetDisplay(); | 
|  | } | 
|  | pTextContext->SetDisplay(eDisplay); | 
|  | m_mapXMLNodeToParseContext.SetAt(pXMLNode, pTextContext); | 
|  | } | 
|  | for (CFDE_XMLNode* pXMLChild = | 
|  | pXMLNode->GetNodeItem(CFDE_XMLNode::FirstChild); | 
|  | pXMLChild; | 
|  | pXMLChild = pXMLChild->GetNodeItem(CFDE_XMLNode::NextSibling)) { | 
|  | ParseRichText(pXMLChild, pNewStyle); | 
|  | } | 
|  | if (pNewStyle) | 
|  | pNewStyle->Release(); | 
|  | } | 
|  |  | 
|  | bool CXFA_TextParser::TagValidate(const CFX_WideString& wsName) const { | 
|  | static const uint32_t s_XFATagName[] = { | 
|  | 0x61,        // a | 
|  | 0x62,        // b | 
|  | 0x69,        // i | 
|  | 0x70,        // p | 
|  | 0x0001f714,  // br | 
|  | 0x00022a55,  // li | 
|  | 0x000239bb,  // ol | 
|  | 0x00025881,  // ul | 
|  | 0x0bd37faa,  // sub | 
|  | 0x0bd37fb8,  // sup | 
|  | 0xa73e3af2,  // span | 
|  | 0xb182eaae,  // body | 
|  | 0xdb8ac455,  // html | 
|  | }; | 
|  | static const int32_t s_iCount = FX_ArraySize(s_XFATagName); | 
|  |  | 
|  | return std::binary_search(s_XFATagName, s_XFATagName + s_iCount, | 
|  | FX_HashCode_GetW(wsName.AsStringC(), true)); | 
|  | } | 
|  |  | 
|  | void CXFA_TextParser::ParseTagInfo(CFDE_XMLNode* pXMLNode, | 
|  | CXFA_CSSTagProvider& tagProvider) { | 
|  | CFX_WideString wsName; | 
|  | if (pXMLNode->GetType() == FDE_XMLNODE_Element) { | 
|  | CFDE_XMLElement* pXMLElement = static_cast<CFDE_XMLElement*>(pXMLNode); | 
|  | pXMLElement->GetLocalTagName(wsName); | 
|  | tagProvider.SetTagNameObj(wsName); | 
|  | tagProvider.m_bTagAvailable = TagValidate(wsName); | 
|  |  | 
|  | CFX_WideString wsValue; | 
|  | pXMLElement->GetString(L"style", wsValue); | 
|  | if (!wsValue.IsEmpty()) { | 
|  | tagProvider.SetAttribute(L"style", wsValue); | 
|  | } | 
|  | } else if (pXMLNode->GetType() == FDE_XMLNODE_Text) { | 
|  | tagProvider.m_bTagAvailable = TRUE; | 
|  | tagProvider.m_bContent = TRUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t CXFA_TextParser::GetVAlign(CXFA_TextProvider* pTextProvider) const { | 
|  | CXFA_Para para = pTextProvider->GetParaNode(); | 
|  | return para ? para.GetVerticalAlign() : XFA_ATTRIBUTEENUM_Top; | 
|  | } | 
|  |  | 
|  | FX_FLOAT CXFA_TextParser::GetTabInterval(IFDE_CSSComputedStyle* pStyle) const { | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"tab-interval"), wsValue)) | 
|  | return CXFA_Measurement(wsValue.AsStringC()).ToUnit(XFA_UNIT_Pt); | 
|  | return 36; | 
|  | } | 
|  |  | 
|  | int32_t CXFA_TextParser::CountTabs(IFDE_CSSComputedStyle* pStyle) const { | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"xfa-tab-count"), wsValue)) | 
|  | return wsValue.GetInteger(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | FX_BOOL CXFA_TextParser::IsSpaceRun(IFDE_CSSComputedStyle* pStyle) const { | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle && pStyle->GetCustomStyle(FX_WSTRC(L"xfa-spacerun"), wsValue)) { | 
|  | wsValue.MakeLower(); | 
|  | return wsValue == FX_WSTRC(L"yes"); | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | IFGAS_Font* CXFA_TextParser::GetFont(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle) const { | 
|  | CFX_WideStringC wsFamily = FX_WSTRC(L"Courier"); | 
|  | uint32_t dwStyle = 0; | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | if (font) { | 
|  | font.GetTypeface(wsFamily); | 
|  | if (font.IsBold()) { | 
|  | dwStyle |= FX_FONTSTYLE_Bold; | 
|  | } | 
|  | if (font.IsItalic()) { | 
|  | dwStyle |= FX_FONTSTYLE_Italic; | 
|  | } | 
|  | } | 
|  | if (pStyle) { | 
|  | IFDE_CSSFontStyle* pFontStyle = pStyle->GetFontStyles(); | 
|  | int32_t iCount = pFontStyle->CountFontFamilies(); | 
|  | if (iCount > 0) { | 
|  | wsFamily = pFontStyle->GetFontFamily(iCount - 1); | 
|  | } | 
|  | dwStyle = 0; | 
|  | if (pFontStyle->GetFontWeight() > FXFONT_FW_NORMAL) { | 
|  | dwStyle |= FX_FONTSTYLE_Bold; | 
|  | } | 
|  | if (pFontStyle->GetFontStyle() == FDE_CSSFONTSTYLE_Italic) { | 
|  | dwStyle |= FX_FONTSTYLE_Italic; | 
|  | } | 
|  | } | 
|  | CXFA_FFDoc* pDoc = pTextProvider->GetDocNode(); | 
|  | CXFA_FontMgr* pFontMgr = pDoc->GetApp()->GetXFAFontMgr(); | 
|  | return pFontMgr->GetFont(pDoc, wsFamily, dwStyle); | 
|  | } | 
|  | FX_FLOAT CXFA_TextParser::GetFontSize(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle) const { | 
|  | if (pStyle) | 
|  | return pStyle->GetFontStyles()->GetFontSize(); | 
|  |  | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | if (font) { | 
|  | return font.GetFontSize(); | 
|  | } | 
|  | return 10; | 
|  | } | 
|  | int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle, | 
|  | CFDE_XMLNode* pXMLNode) const { | 
|  | if (pStyle) { | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle->GetCustomStyle(FX_WSTRC(L"xfa-font-horizontal-scale"), | 
|  | wsValue)) { | 
|  | return wsValue.GetInteger(); | 
|  | } | 
|  | while (pXMLNode) { | 
|  | CXFA_TextParseContext* pContext = static_cast<CXFA_TextParseContext*>( | 
|  | m_mapXMLNodeToParseContext.GetValueAt(pXMLNode)); | 
|  | if (pContext && pContext->m_pParentStyle && | 
|  | pContext->m_pParentStyle->GetCustomStyle( | 
|  | FX_WSTRC(L"xfa-font-horizontal-scale"), wsValue)) { | 
|  | return wsValue.GetInteger(); | 
|  | } | 
|  | pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::Parent); | 
|  | } | 
|  | } | 
|  | if (CXFA_Font font = pTextProvider->GetFontNode()) { | 
|  | return static_cast<int32_t>(font.GetHorizontalScale()); | 
|  | } | 
|  | return 100; | 
|  | } | 
|  | int32_t CXFA_TextParser::GetVerScale(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle) const { | 
|  | if (pStyle) { | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle->GetCustomStyle(FX_WSTRC(L"xfa-font-vertical-scale"), wsValue)) { | 
|  | return wsValue.GetInteger(); | 
|  | } | 
|  | } | 
|  | if (CXFA_Font font = pTextProvider->GetFontNode()) { | 
|  | return (int32_t)font.GetVerticalScale(); | 
|  | } | 
|  | return 100; | 
|  | } | 
|  | void CXFA_TextParser::GetUnderline(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle, | 
|  | int32_t& iUnderline, | 
|  | int32_t& iPeriod) const { | 
|  | iUnderline = 0; | 
|  | iPeriod = XFA_ATTRIBUTEENUM_All; | 
|  | if (pStyle) { | 
|  | uint32_t dwDecoration = pStyle->GetParagraphStyles()->GetTextDecoration(); | 
|  | if (dwDecoration & FDE_CSSTEXTDECORATION_Double) { | 
|  | iUnderline = 2; | 
|  | } else if (dwDecoration & FDE_CSSTEXTDECORATION_Underline) { | 
|  | iUnderline = 1; | 
|  | } | 
|  | CFX_WideString wsValue; | 
|  | if (pStyle->GetCustomStyle(FX_WSTRC(L"underlinePeriod"), wsValue)) { | 
|  | if (wsValue == FX_WSTRC(L"word")) { | 
|  | iPeriod = XFA_ATTRIBUTEENUM_Word; | 
|  | } | 
|  | } else if (CXFA_Font font = pTextProvider->GetFontNode()) { | 
|  | iPeriod = font.GetUnderlinePeriod(); | 
|  | } | 
|  | } else { | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | if (font) { | 
|  | iUnderline = font.GetUnderline(); | 
|  | iPeriod = font.GetUnderlinePeriod(); | 
|  | } | 
|  | } | 
|  | } | 
|  | void CXFA_TextParser::GetLinethrough(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle, | 
|  | int32_t& iLinethrough) const { | 
|  | if (pStyle) { | 
|  | uint32_t dwDecoration = pStyle->GetParagraphStyles()->GetTextDecoration(); | 
|  | iLinethrough = (dwDecoration & FDE_CSSTEXTDECORATION_LineThrough) ? 1 : 0; | 
|  | } else { | 
|  | CXFA_Font font = pTextProvider->GetFontNode(); | 
|  | if (font) { | 
|  | iLinethrough = font.GetLineThrough(); | 
|  | } | 
|  | } | 
|  | } | 
|  | FX_ARGB CXFA_TextParser::GetColor(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle) const { | 
|  | if (pStyle) | 
|  | return pStyle->GetFontStyles()->GetColor(); | 
|  |  | 
|  | if (CXFA_Font font = pTextProvider->GetFontNode()) | 
|  | return font.GetColor(); | 
|  |  | 
|  | return 0xFF000000; | 
|  | } | 
|  | FX_FLOAT CXFA_TextParser::GetBaseline(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle) const { | 
|  | if (pStyle) { | 
|  | IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles(); | 
|  | if (pParaStyle->GetVerticalAlign() == FDE_CSSVERTICALALIGN_Number) { | 
|  | return pParaStyle->GetNumberVerticalAlign(); | 
|  | } | 
|  | } else if (CXFA_Font font = pTextProvider->GetFontNode()) { | 
|  | return font.GetBaselineShift(); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | FX_FLOAT CXFA_TextParser::GetLineHeight(CXFA_TextProvider* pTextProvider, | 
|  | IFDE_CSSComputedStyle* pStyle, | 
|  | FX_BOOL bFirst, | 
|  | FX_FLOAT fVerScale) const { | 
|  | FX_FLOAT fLineHeight = 0; | 
|  | if (pStyle) { | 
|  | fLineHeight = pStyle->GetParagraphStyles()->GetLineHeight(); | 
|  | } else if (CXFA_Para para = pTextProvider->GetParaNode()) { | 
|  | fLineHeight = para.GetLineHeight(); | 
|  | } | 
|  | if (bFirst) { | 
|  | FX_FLOAT fFontSize = GetFontSize(pTextProvider, pStyle); | 
|  | if (fLineHeight < 0.1f) { | 
|  | fLineHeight = fFontSize; | 
|  | } else { | 
|  | fLineHeight = std::min(fLineHeight, fFontSize); | 
|  | } | 
|  | } else if (fLineHeight < 0.1f) { | 
|  | fLineHeight = GetFontSize(pTextProvider, pStyle) * 1.2f; | 
|  | } | 
|  | fLineHeight *= fVerScale; | 
|  | return fLineHeight; | 
|  | } | 
|  | FX_BOOL CXFA_TextParser::GetEmbbedObj(CXFA_TextProvider* pTextProvider, | 
|  | CFDE_XMLNode* pXMLNode, | 
|  | CFX_WideString& wsValue) { | 
|  | wsValue.clear(); | 
|  | if (pXMLNode == NULL) { | 
|  | return FALSE; | 
|  | } | 
|  | FX_BOOL bRet = FALSE; | 
|  | if (pXMLNode->GetType() == FDE_XMLNODE_Element) { | 
|  | CFDE_XMLElement* pElement = static_cast<CFDE_XMLElement*>(pXMLNode); | 
|  | CFX_WideString wsAttr; | 
|  | pElement->GetString(L"xfa:embed", wsAttr); | 
|  | if (wsAttr.IsEmpty()) { | 
|  | return FALSE; | 
|  | } | 
|  | if (wsAttr.GetAt(0) == L'#') { | 
|  | wsAttr.Delete(0); | 
|  | } | 
|  | CFX_WideString ws; | 
|  | pElement->GetString(L"xfa:embedType", ws); | 
|  | if (ws.IsEmpty()) { | 
|  | ws = L"som"; | 
|  | } else { | 
|  | ws.MakeLower(); | 
|  | } | 
|  | FX_BOOL bURI = (ws == FX_WSTRC(L"uri")); | 
|  | if (!bURI && ws != FX_WSTRC(L"som")) { | 
|  | return FALSE; | 
|  | } | 
|  | ws.clear(); | 
|  | pElement->GetString(L"xfa:embedMode", ws); | 
|  | if (ws.IsEmpty()) { | 
|  | ws = L"formatted"; | 
|  | } else { | 
|  | ws.MakeLower(); | 
|  | } | 
|  | FX_BOOL bRaw = (ws == FX_WSTRC(L"raw")); | 
|  | if (!bRaw && ws != FX_WSTRC(L"formatted")) { | 
|  | return FALSE; | 
|  | } | 
|  | bRet = pTextProvider->GetEmbbedObj(bURI, bRaw, wsAttr, wsValue); | 
|  | } | 
|  | return bRet; | 
|  | } | 
|  | CXFA_TextParseContext* CXFA_TextParser::GetParseContextFromMap( | 
|  | CFDE_XMLNode* pXMLNode) { | 
|  | return (CXFA_TextParseContext*)m_mapXMLNodeToParseContext.GetValueAt( | 
|  | pXMLNode); | 
|  | } | 
|  | enum XFA_TABSTOPSSTATUS { | 
|  | XFA_TABSTOPSSTATUS_Error, | 
|  | XFA_TABSTOPSSTATUS_EOS, | 
|  | XFA_TABSTOPSSTATUS_None, | 
|  | XFA_TABSTOPSSTATUS_Alignment, | 
|  | XFA_TABSTOPSSTATUS_StartLeader, | 
|  | XFA_TABSTOPSSTATUS_Leader, | 
|  | XFA_TABSTOPSSTATUS_Location, | 
|  | }; | 
|  | FX_BOOL CXFA_TextParser::GetTabstops( | 
|  | IFDE_CSSComputedStyle* pStyle, | 
|  | CXFA_TextTabstopsContext* pTabstopContext) { | 
|  | if (pStyle == NULL || pTabstopContext == NULL) { | 
|  | return FALSE; | 
|  | } | 
|  | CFX_WideString wsValue; | 
|  | if (!pStyle->GetCustomStyle(FX_WSTRC(L"xfa-tab-stops"), wsValue) && | 
|  | !pStyle->GetCustomStyle(FX_WSTRC(L"tab-stops"), wsValue)) { | 
|  | return FALSE; | 
|  | } | 
|  | int32_t iLength = wsValue.GetLength(); | 
|  | const FX_WCHAR* pTabStops = wsValue.c_str(); | 
|  | int32_t iCur = 0; | 
|  | int32_t iLast = 0; | 
|  | CFX_WideString wsAlign; | 
|  | XFA_TABSTOPSSTATUS eStatus = XFA_TABSTOPSSTATUS_None; | 
|  | FX_WCHAR ch; | 
|  | while (iCur < iLength) { | 
|  | ch = pTabStops[iCur]; | 
|  | switch (eStatus) { | 
|  | case XFA_TABSTOPSSTATUS_None: | 
|  | if (ch <= ' ') { | 
|  | iCur++; | 
|  | } else { | 
|  | eStatus = XFA_TABSTOPSSTATUS_Alignment; | 
|  | iLast = iCur; | 
|  | } | 
|  | break; | 
|  | case XFA_TABSTOPSSTATUS_Alignment: | 
|  | if (ch == ' ') { | 
|  | wsAlign = CFX_WideStringC(pTabStops + iLast, iCur - iLast); | 
|  | eStatus = XFA_TABSTOPSSTATUS_StartLeader; | 
|  | iCur++; | 
|  | while (iCur < iLength && pTabStops[iCur] <= ' ') { | 
|  | iCur++; | 
|  | } | 
|  | iLast = iCur; | 
|  | } else { | 
|  | iCur++; | 
|  | } | 
|  | break; | 
|  | case XFA_TABSTOPSSTATUS_StartLeader: | 
|  | if (ch != 'l') { | 
|  | eStatus = XFA_TABSTOPSSTATUS_Location; | 
|  | } else { | 
|  | int32_t iCount = 0; | 
|  | while (iCur < iLength) { | 
|  | ch = pTabStops[iCur]; | 
|  | iCur++; | 
|  | if (ch == '(') { | 
|  | iCount++; | 
|  | } else if (ch == ')') { | 
|  | iCount--; | 
|  | if (iCount == 0) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | while (iCur < iLength && pTabStops[iCur] <= ' ') { | 
|  | iCur++; | 
|  | } | 
|  | iLast = iCur; | 
|  | eStatus = XFA_TABSTOPSSTATUS_Location; | 
|  | } | 
|  | break; | 
|  | case XFA_TABSTOPSSTATUS_Location: | 
|  | if (ch == ' ') { | 
|  | uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringC(), true); | 
|  | CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast)); | 
|  | FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt); | 
|  | pTabstopContext->Append(dwHashCode, fPos); | 
|  | wsAlign.clear(); | 
|  | eStatus = XFA_TABSTOPSSTATUS_None; | 
|  | } | 
|  | iCur++; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!wsAlign.IsEmpty()) { | 
|  | uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringC(), true); | 
|  | CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast)); | 
|  | FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt); | 
|  | pTabstopContext->Append(dwHashCode, fPos); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | CXFA_TextLayout::CXFA_TextLayout(CXFA_TextProvider* pTextProvider) | 
|  | : m_bHasBlock(FALSE), | 
|  | m_pTextProvider(pTextProvider), | 
|  | m_pTextDataNode(nullptr), | 
|  | m_bRichText(FALSE), | 
|  | m_iLines(0), | 
|  | m_fMaxWidth(0), | 
|  | m_bBlockContinue(TRUE) { | 
|  | ASSERT(m_pTextProvider); | 
|  | } | 
|  | CXFA_TextLayout::~CXFA_TextLayout() { | 
|  | m_textParser.Reset(); | 
|  | Unload(); | 
|  | } | 
|  | void CXFA_TextLayout::Unload() { | 
|  | int32_t iCount = m_pieceLines.GetSize(); | 
|  | for (int32_t i = 0; i < iCount; i++) { | 
|  | CXFA_PieceLine* pLine = m_pieceLines.GetAt(i); | 
|  | FXTARGET_DeleteWith(CXFA_PieceLine, m_pAllocator.get(), pLine); | 
|  | } | 
|  | m_pieceLines.RemoveAll(); | 
|  | m_pBreak.reset(); | 
|  | m_pAllocator.reset(); | 
|  | } | 
|  | const CXFA_PieceLineArray* CXFA_TextLayout::GetPieceLines() { | 
|  | return &m_pieceLines; | 
|  | } | 
|  | void CXFA_TextLayout::GetTextDataNode() { | 
|  | if (m_pTextProvider == NULL) { | 
|  | return; | 
|  | } | 
|  | CXFA_Node* pNode = m_pTextProvider->GetTextNode(m_bRichText); | 
|  | if (pNode && m_bRichText) { | 
|  | m_textParser.Reset(); | 
|  | } | 
|  | m_pTextDataNode = pNode; | 
|  | } | 
|  | CFDE_XMLNode* CXFA_TextLayout::GetXMLContainerNode() { | 
|  | CFDE_XMLNode* pXMLContainer = NULL; | 
|  | if (m_bRichText) { | 
|  | CFDE_XMLNode* pXMLRoot = m_pTextDataNode->GetXMLMappingNode(); | 
|  | if (!pXMLRoot) { | 
|  | return pXMLContainer; | 
|  | } | 
|  | for (CFDE_XMLNode* pXMLChild = | 
|  | pXMLRoot->GetNodeItem(CFDE_XMLNode::FirstChild); | 
|  | pXMLChild; | 
|  | pXMLChild = pXMLChild->GetNodeItem(CFDE_XMLNode::NextSibling)) { | 
|  | if (pXMLChild->GetType() == FDE_XMLNODE_Element) { | 
|  | CFDE_XMLElement* pXMLElement = static_cast<CFDE_XMLElement*>(pXMLChild); | 
|  | CFX_WideString wsTag; | 
|  | pXMLElement->GetLocalTagName(wsTag); | 
|  | if (wsTag == FX_WSTRC(L"body") || wsTag == FX_WSTRC(L"html")) { | 
|  | pXMLContainer = pXMLChild; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return pXMLContainer; | 
|  | } | 
|  | CFX_RTFBreak* CXFA_TextLayout::CreateBreak(FX_BOOL bDefault) { | 
|  | uint32_t dwStyle = FX_RTFLAYOUTSTYLE_ExpandTab; | 
|  | if (!bDefault) { | 
|  | dwStyle |= FX_RTFLAYOUTSTYLE_Pagination; | 
|  | } | 
|  | CFX_RTFBreak* pBreak = new CFX_RTFBreak(0); | 
|  | pBreak->SetLayoutStyles(dwStyle); | 
|  | pBreak->SetLineBreakChar(L'\n'); | 
|  | pBreak->SetLineBreakTolerance(1); | 
|  | pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, NULL)); | 
|  | pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, NULL)); | 
|  | return pBreak; | 
|  | } | 
|  | void CXFA_TextLayout::InitBreak(FX_FLOAT fLineWidth) { | 
|  | CXFA_Font font = m_pTextProvider->GetFontNode(); | 
|  | CXFA_Para para = m_pTextProvider->GetParaNode(); | 
|  | FX_FLOAT fStart = 0; | 
|  | FX_FLOAT fStartPos = 0; | 
|  | if (para) { | 
|  | int32_t iAlign = FX_RTFLINEALIGNMENT_Left; | 
|  | switch (para.GetHorizontalAlign()) { | 
|  | case XFA_ATTRIBUTEENUM_Center: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Center; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_Right: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Right; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_Justify: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Justified; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_JustifyAll: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Distributed; | 
|  | break; | 
|  | } | 
|  | m_pBreak->SetAlignment(iAlign); | 
|  | fStart = para.GetMarginLeft(); | 
|  | if (m_pTextProvider->IsCheckButtonAndAutoWidth()) { | 
|  | if (iAlign != FX_RTFLINEALIGNMENT_Left) { | 
|  | fLineWidth -= para.GetMarginRight(); | 
|  | } | 
|  | } else { | 
|  | fLineWidth -= para.GetMarginRight(); | 
|  | } | 
|  | if (fLineWidth < 0) { | 
|  | fLineWidth = fStart; | 
|  | } | 
|  | fStartPos = fStart; | 
|  | FX_FLOAT fIndent = para.GetTextIndent(); | 
|  | if (fIndent > 0) { | 
|  | fStartPos += fIndent; | 
|  | } | 
|  | } | 
|  | m_pBreak->SetLineBoundary(fStart, fLineWidth); | 
|  | m_pBreak->SetLineStartPos(fStartPos); | 
|  | if (font) { | 
|  | m_pBreak->SetHorizontalScale((int32_t)font.GetHorizontalScale()); | 
|  | m_pBreak->SetVerticalScale((int32_t)font.GetVerticalScale()); | 
|  | m_pBreak->SetCharSpace(font.GetLetterSpacing()); | 
|  | } | 
|  | FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, NULL); | 
|  | m_pBreak->SetFontSize(fFontSize); | 
|  | m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, NULL)); | 
|  | m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f); | 
|  | } | 
|  | void CXFA_TextLayout::InitBreak(IFDE_CSSComputedStyle* pStyle, | 
|  | FDE_CSSDISPLAY eDisplay, | 
|  | FX_FLOAT fLineWidth, | 
|  | CFDE_XMLNode* pXMLNode, | 
|  | IFDE_CSSComputedStyle* pParentStyle) { | 
|  | if (pStyle == NULL) { | 
|  | InitBreak(fLineWidth); | 
|  | return; | 
|  | } | 
|  | IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles(); | 
|  | if (eDisplay == FDE_CSSDISPLAY_Block || eDisplay == FDE_CSSDISPLAY_ListItem) { | 
|  | int32_t iAlign = FX_RTFLINEALIGNMENT_Left; | 
|  | switch (pParaStyle->GetTextAlign()) { | 
|  | case FDE_CSSTEXTALIGN_Right: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Right; | 
|  | break; | 
|  | case FDE_CSSTEXTALIGN_Center: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Center; | 
|  | break; | 
|  | case FDE_CSSTEXTALIGN_Justify: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Justified; | 
|  | break; | 
|  | case FDE_CSSTEXTALIGN_JustifyAll: | 
|  | iAlign = FX_RTFLINEALIGNMENT_Distributed; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | m_pBreak->SetAlignment(iAlign); | 
|  | FX_FLOAT fStart = 0; | 
|  | const FDE_CSSRECT* pRect = pStyle->GetBoundaryStyles()->GetMarginWidth(); | 
|  | const FDE_CSSRECT* pPaddingRect = | 
|  | pStyle->GetBoundaryStyles()->GetPaddingWidth(); | 
|  | if (pRect) { | 
|  | fStart = pRect->left.GetValue(); | 
|  | fLineWidth -= pRect->right.GetValue(); | 
|  | if (pPaddingRect) { | 
|  | fStart += pPaddingRect->left.GetValue(); | 
|  | fLineWidth -= pPaddingRect->right.GetValue(); | 
|  | } | 
|  | if (eDisplay == FDE_CSSDISPLAY_ListItem) { | 
|  | const FDE_CSSRECT* pParRect = | 
|  | pParentStyle->GetBoundaryStyles()->GetMarginWidth(); | 
|  | const FDE_CSSRECT* pParPaddingRect = | 
|  | pParentStyle->GetBoundaryStyles()->GetPaddingWidth(); | 
|  | if (pParRect) { | 
|  | fStart += pParRect->left.GetValue(); | 
|  | fLineWidth -= pParRect->right.GetValue(); | 
|  | if (pParPaddingRect) { | 
|  | fStart += pParPaddingRect->left.GetValue(); | 
|  | fLineWidth -= pParPaddingRect->right.GetValue(); | 
|  | } | 
|  | } | 
|  | FDE_CSSRECT pNewRect; | 
|  | pNewRect.left.Set(FDE_CSSLENGTHUNIT_Point, fStart); | 
|  | pNewRect.right.Set(FDE_CSSLENGTHUNIT_Point, pRect->right.GetValue()); | 
|  | pNewRect.top.Set(FDE_CSSLENGTHUNIT_Point, pRect->top.GetValue()); | 
|  | pNewRect.bottom.Set(FDE_CSSLENGTHUNIT_Point, pRect->bottom.GetValue()); | 
|  | pStyle->GetBoundaryStyles()->SetMarginWidth(pNewRect); | 
|  | } | 
|  | } | 
|  | m_pBreak->SetLineBoundary(fStart, fLineWidth); | 
|  | FX_FLOAT fIndent = pParaStyle->GetTextIndent().GetValue(); | 
|  | if (fIndent > 0) { | 
|  | fStart += fIndent; | 
|  | } | 
|  | m_pBreak->SetLineStartPos(fStart); | 
|  | m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle)); | 
|  | if (!m_pTabstopContext) | 
|  | m_pTabstopContext.reset(new CXFA_TextTabstopsContext); | 
|  | m_textParser.GetTabstops(pStyle, m_pTabstopContext.get()); | 
|  | for (int32_t i = 0; i < m_pTabstopContext->m_iTabCount; i++) { | 
|  | XFA_TABSTOPS* pTab = m_pTabstopContext->m_tabstops.GetDataPtr(i); | 
|  | m_pBreak->AddPositionedTab(pTab->fTabstops); | 
|  | } | 
|  | } | 
|  | FX_FLOAT fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle); | 
|  | m_pBreak->SetFontSize(fFontSize); | 
|  | m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f); | 
|  | m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, pStyle)); | 
|  | m_pBreak->SetHorizontalScale( | 
|  | m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode)); | 
|  | m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle)); | 
|  | m_pBreak->SetCharSpace(pParaStyle->GetLetterSpacing().GetValue()); | 
|  | } | 
|  | int32_t CXFA_TextLayout::GetText(CFX_WideString& wsText) { | 
|  | GetTextDataNode(); | 
|  | wsText.clear(); | 
|  | if (m_bRichText) { | 
|  | } else { | 
|  | wsText = m_pTextDataNode->GetContent(); | 
|  | } | 
|  | return wsText.GetLength(); | 
|  | } | 
|  | FX_FLOAT CXFA_TextLayout::GetLayoutHeight() { | 
|  | if (m_pLoader == NULL) { | 
|  | return 0; | 
|  | } | 
|  | int32_t iCount = m_pLoader->m_lineHeights.GetSize(); | 
|  | if (iCount == 0 && m_pLoader->m_fWidth > 0) { | 
|  | CFX_SizeF szMax(m_pLoader->m_fWidth, m_pLoader->m_fHeight); | 
|  | CFX_SizeF szDef; | 
|  | m_pLoader->m_bSaveLineHeight = TRUE; | 
|  | m_pLoader->m_fLastPos = 0; | 
|  | CalcSize(szMax, szMax, szDef); | 
|  | m_pLoader->m_bSaveLineHeight = FALSE; | 
|  | return szDef.y; | 
|  | } | 
|  | FX_FLOAT fHeight = m_pLoader->m_fHeight; | 
|  | if (fHeight < 0.1f) { | 
|  | fHeight = 0; | 
|  | for (int32_t i = 0; i < iCount; i++) { | 
|  | fHeight += m_pLoader->m_lineHeights.ElementAt(i); | 
|  | } | 
|  | } | 
|  | return fHeight; | 
|  | } | 
|  | FX_FLOAT CXFA_TextLayout::StartLayout(FX_FLOAT fWidth) { | 
|  | if (!m_pLoader) | 
|  | m_pLoader.reset(new CXFA_LoaderContext); | 
|  |  | 
|  | if (fWidth < 0 || (m_pLoader->m_fWidth > -1 && | 
|  | FXSYS_fabs(fWidth - m_pLoader->m_fWidth) > 0)) { | 
|  | m_pLoader->m_lineHeights.RemoveAll(); | 
|  | m_Blocks.RemoveAll(); | 
|  | Unload(); | 
|  | m_pLoader->m_fStartLineOffset = 0; | 
|  | } | 
|  | m_pLoader->m_fWidth = fWidth; | 
|  | if (fWidth < 0) { | 
|  | CFX_SizeF szMax; | 
|  | CFX_SizeF szDef; | 
|  | m_pLoader->m_bSaveLineHeight = TRUE; | 
|  | m_pLoader->m_fLastPos = 0; | 
|  | CalcSize(szMax, szMax, szDef); | 
|  | m_pLoader->m_bSaveLineHeight = FALSE; | 
|  | fWidth = szDef.x; | 
|  | } | 
|  | return fWidth; | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::DoLayout(int32_t iBlockIndex, | 
|  | FX_FLOAT& fCalcHeight, | 
|  | FX_FLOAT fContentAreaHeight, | 
|  | FX_FLOAT fTextHeight) { | 
|  | if (m_pLoader == NULL) { | 
|  | return FALSE; | 
|  | } | 
|  | int32_t iBlockCount = m_Blocks.GetSize(); | 
|  | FX_FLOAT fHeight = fTextHeight; | 
|  | if (fHeight < 0) { | 
|  | fHeight = GetLayoutHeight(); | 
|  | } | 
|  | m_pLoader->m_fHeight = fHeight; | 
|  | if (fContentAreaHeight < 0) { | 
|  | return FALSE; | 
|  | } | 
|  | m_bHasBlock = TRUE; | 
|  | if (iBlockCount == 0 && fHeight > 0) { | 
|  | fHeight = fTextHeight - GetLayoutHeight(); | 
|  | if (fHeight > 0) { | 
|  | int32_t iAlign = m_textParser.GetVAlign(m_pTextProvider); | 
|  | if (iAlign == XFA_ATTRIBUTEENUM_Middle) { | 
|  | fHeight /= 2.0f; | 
|  | } else if (iAlign != XFA_ATTRIBUTEENUM_Bottom) { | 
|  | fHeight = 0; | 
|  | } | 
|  | m_pLoader->m_fStartLineOffset = fHeight; | 
|  | } | 
|  | } | 
|  | FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset; | 
|  | int32_t iLineIndex = 0; | 
|  | if (iBlockCount > 1) { | 
|  | if (iBlockCount >= (iBlockIndex + 1) * 2) { | 
|  | iLineIndex = m_Blocks.ElementAt(iBlockIndex * 2); | 
|  | } else { | 
|  | iLineIndex = m_Blocks.ElementAt(iBlockCount - 1) + | 
|  | m_Blocks.ElementAt(iBlockCount - 2); | 
|  | } | 
|  | if (m_pLoader->m_BlocksHeight.GetSize() > 0) { | 
|  | for (int32_t i = 0; i < iBlockIndex; i++) { | 
|  | fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1); | 
|  | } | 
|  | } | 
|  | } | 
|  | int32_t iCount = m_pLoader->m_lineHeights.GetSize(); | 
|  | int32_t i = 0; | 
|  | for (i = iLineIndex; i < iCount; i++) { | 
|  | FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i); | 
|  | if ((i == iLineIndex) && (fLineHeight - fContentAreaHeight > 0.001)) { | 
|  | fCalcHeight = 0; | 
|  | return TRUE; | 
|  | } | 
|  | if (fLinePos + fLineHeight - fContentAreaHeight > 0.001) { | 
|  | if (iBlockCount >= (iBlockIndex + 1) * 2) { | 
|  | m_Blocks.SetAt(iBlockIndex * 2, iLineIndex); | 
|  | m_Blocks.SetAt(iBlockIndex * 2 + 1, i - iLineIndex); | 
|  | } else { | 
|  | m_Blocks.Add(iLineIndex); | 
|  | m_Blocks.Add(i - iLineIndex); | 
|  | } | 
|  | if (i == iLineIndex) { | 
|  | if (fCalcHeight <= fLinePos) { | 
|  | if (m_pLoader->m_BlocksHeight.GetSize() > iBlockIndex * 2 && | 
|  | (m_pLoader->m_BlocksHeight.GetAt(iBlockIndex * 2) == | 
|  | iBlockIndex)) { | 
|  | m_pLoader->m_BlocksHeight.SetAt(iBlockIndex * 2 + 1, fCalcHeight); | 
|  | } else { | 
|  | m_pLoader->m_BlocksHeight.Add((FX_FLOAT)iBlockIndex); | 
|  | m_pLoader->m_BlocksHeight.Add(fCalcHeight); | 
|  | } | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | fCalcHeight = fLinePos; | 
|  | return TRUE; | 
|  | } | 
|  | fLinePos += fLineHeight; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | int32_t CXFA_TextLayout::CountBlocks() const { | 
|  | int32_t iCount = m_Blocks.GetSize() / 2; | 
|  | return iCount > 0 ? iCount : 1; | 
|  | } | 
|  |  | 
|  | FX_BOOL CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize, | 
|  | const CFX_SizeF& maxSize, | 
|  | CFX_SizeF& defaultSize) { | 
|  | defaultSize.x = maxSize.x; | 
|  | if (defaultSize.x < 1) | 
|  | defaultSize.x = 0xFFFF; | 
|  |  | 
|  | m_pBreak.reset(CreateBreak(FALSE)); | 
|  | FX_FLOAT fLinePos = 0; | 
|  | m_iLines = 0; | 
|  | m_fMaxWidth = 0; | 
|  | Loader(defaultSize, fLinePos, FALSE); | 
|  | if (fLinePos < 0.1f) | 
|  | fLinePos = m_textParser.GetFontSize(m_pTextProvider, NULL); | 
|  |  | 
|  | m_pTabstopContext.reset(); | 
|  | defaultSize = CFX_SizeF(m_fMaxWidth, fLinePos); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | FX_BOOL CXFA_TextLayout::Layout(const CFX_SizeF& size, FX_FLOAT* fHeight) { | 
|  | if (size.x < 1) | 
|  | return FALSE; | 
|  |  | 
|  | Unload(); | 
|  | m_pBreak.reset(CreateBreak(TRUE)); | 
|  | if (m_pLoader) { | 
|  | m_pLoader->m_iTotalLines = -1; | 
|  | m_pLoader->m_iChar = 0; | 
|  | } | 
|  | m_iLines = 0; | 
|  | FX_FLOAT fLinePos = 0; | 
|  | Loader(size, fLinePos, TRUE); | 
|  | UpdateAlign(size.y, fLinePos); | 
|  | m_pTabstopContext.reset(); | 
|  | if (fHeight) | 
|  | *fHeight = fLinePos; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | FX_BOOL CXFA_TextLayout::Layout(int32_t iBlock) { | 
|  | if (m_pLoader == NULL || iBlock < 0 || iBlock >= CountBlocks()) | 
|  | return FALSE; | 
|  | if (m_pLoader->m_fWidth < 1) | 
|  | return FALSE; | 
|  |  | 
|  | m_pLoader->m_iTotalLines = -1; | 
|  | m_iLines = 0; | 
|  | FX_FLOAT fLinePos = 0; | 
|  | CXFA_Node* pNode = NULL; | 
|  | CFX_SizeF szText(m_pLoader->m_fWidth, m_pLoader->m_fHeight); | 
|  | int32_t iCount = m_Blocks.GetSize(); | 
|  | int32_t iBlocksHeightCount = m_pLoader->m_BlocksHeight.GetSize(); | 
|  | iBlocksHeightCount /= 2; | 
|  | if (iBlock < iBlocksHeightCount) | 
|  | return TRUE; | 
|  | if (iBlock == iBlocksHeightCount) { | 
|  | Unload(); | 
|  | m_pBreak.reset(CreateBreak(TRUE)); | 
|  | fLinePos = m_pLoader->m_fStartLineOffset; | 
|  | for (int32_t i = 0; i < iBlocksHeightCount; i++) { | 
|  | fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1); | 
|  | } | 
|  | m_pLoader->m_iChar = 0; | 
|  | if (iCount > 1) | 
|  | m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock * 2 + 1); | 
|  | Loader(szText, fLinePos, TRUE); | 
|  | if (iCount == 0 && m_pLoader->m_fStartLineOffset < 0.1f) | 
|  | UpdateAlign(szText.y, fLinePos); | 
|  | } else if (m_pTextDataNode) { | 
|  | iBlock *= 2; | 
|  | if (iBlock < iCount - 2) | 
|  | m_pLoader->m_iTotalLines = m_Blocks.ElementAt(iBlock + 1); | 
|  | m_pBreak->Reset(); | 
|  | if (m_bRichText) { | 
|  | CFDE_XMLNode* pContainerNode = GetXMLContainerNode(); | 
|  | if (!pContainerNode) { | 
|  | return TRUE; | 
|  | } | 
|  | CFDE_XMLNode* pXMLNode = m_pLoader->m_pXMLNode; | 
|  | if (!pXMLNode) | 
|  | return TRUE; | 
|  | CFDE_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode; | 
|  | for (; pXMLNode; | 
|  | pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) { | 
|  | if (!LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle, | 
|  | TRUE)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | while (!pXMLNode) { | 
|  | pXMLNode = pSaveXMLNode->GetNodeItem(CFDE_XMLNode::Parent); | 
|  | if (pXMLNode == pContainerNode) | 
|  | break; | 
|  | if (!LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle, | 
|  | TRUE, NULL, FALSE)) { | 
|  | break; | 
|  | } | 
|  | pSaveXMLNode = pXMLNode; | 
|  | pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling); | 
|  | if (!pXMLNode) | 
|  | continue; | 
|  | for (; pXMLNode; | 
|  | pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) { | 
|  | if (!LoadRichText(pXMLNode, szText, fLinePos, | 
|  | m_pLoader->m_pParentStyle, TRUE)) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | pNode = m_pLoader->m_pNode; | 
|  | if (!pNode) | 
|  | return TRUE; | 
|  | LoadText(pNode, szText, fLinePos, TRUE); | 
|  | } | 
|  | } | 
|  | if (iBlock == iCount) { | 
|  | m_pTabstopContext.reset(); | 
|  | m_pLoader.reset(); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex) { | 
|  | if (!m_pLoader) { | 
|  | return; | 
|  | } | 
|  | int32_t iCountHeight = m_pLoader->m_lineHeights.GetSize(); | 
|  | if (iCountHeight == 0) { | 
|  | return; | 
|  | } | 
|  | FX_BOOL bEndItem = TRUE; | 
|  | int32_t iBlockCount = m_Blocks.GetSize(); | 
|  | FX_FLOAT fLinePos = m_pLoader->m_fStartLineOffset; | 
|  | int32_t iLineIndex = 0; | 
|  | if (iBlockIndex > 0) { | 
|  | int32_t iBlockHeightCount = m_pLoader->m_BlocksHeight.GetSize(); | 
|  | iBlockHeightCount /= 2; | 
|  | if (iBlockHeightCount >= iBlockIndex) { | 
|  | for (int32_t i = 0; i < iBlockIndex; i++) { | 
|  | fLinePos -= m_pLoader->m_BlocksHeight.ElementAt(i * 2 + 1); | 
|  | } | 
|  | } else { | 
|  | fLinePos = 0; | 
|  | } | 
|  | iLineIndex = m_Blocks.ElementAt(iBlockCount - 1) + | 
|  | m_Blocks.ElementAt(iBlockCount - 2); | 
|  | } | 
|  | int32_t i = 0; | 
|  | for (i = iLineIndex; i < iCountHeight; i++) { | 
|  | FX_FLOAT fLineHeight = m_pLoader->m_lineHeights.ElementAt(i); | 
|  | if (fLinePos + fLineHeight - rtText.height > 0.001) { | 
|  | m_Blocks.Add(iLineIndex); | 
|  | m_Blocks.Add(i - iLineIndex); | 
|  | bEndItem = FALSE; | 
|  | break; | 
|  | } | 
|  | fLinePos += fLineHeight; | 
|  | } | 
|  | if (iCountHeight > 0 && (i - iLineIndex) > 0 && bEndItem) { | 
|  | m_Blocks.Add(iLineIndex); | 
|  | m_Blocks.Add(i - iLineIndex); | 
|  | } | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice, | 
|  | const CFX_Matrix& tmDoc2Device, | 
|  | const CFX_RectF& rtClip, | 
|  | int32_t iBlock) { | 
|  | if (!pFxDevice) | 
|  | return FALSE; | 
|  |  | 
|  | std::unique_ptr<CFDE_RenderDevice> pDevice( | 
|  | new CFDE_RenderDevice(pFxDevice, FALSE)); | 
|  | pDevice->SaveState(); | 
|  | pDevice->SetClipRect(rtClip); | 
|  |  | 
|  | std::unique_ptr<CFDE_Brush> pSolidBrush(new CFDE_Brush); | 
|  | std::unique_ptr<CFDE_Pen> pPen(new CFDE_Pen); | 
|  | if (m_pieceLines.GetSize() == 0) { | 
|  | int32_t iBlockCount = CountBlocks(); | 
|  | for (int32_t i = 0; i < iBlockCount; i++) { | 
|  | Layout(i); | 
|  | } | 
|  | } | 
|  | FXTEXT_CHARPOS* pCharPos = NULL; | 
|  | int32_t iCharCount = 0; | 
|  | int32_t iLineStart = 0; | 
|  | int32_t iPieceLines = m_pieceLines.GetSize(); | 
|  | int32_t iCount = m_Blocks.GetSize(); | 
|  | if (iCount > 0) { | 
|  | iBlock *= 2; | 
|  | if (iBlock < iCount) { | 
|  | iLineStart = m_Blocks.ElementAt(iBlock); | 
|  | iPieceLines = m_Blocks.ElementAt(iBlock + 1); | 
|  | } else { | 
|  | iPieceLines = 0; | 
|  | } | 
|  | } | 
|  | for (int32_t i = 0; i < iPieceLines; i++) { | 
|  | if (i + iLineStart >= m_pieceLines.GetSize()) { | 
|  | break; | 
|  | } | 
|  | CXFA_PieceLine* pPieceLine = m_pieceLines.GetAt(i + iLineStart); | 
|  | int32_t iPieces = pPieceLine->m_textPieces.GetSize(); | 
|  | int32_t j = 0; | 
|  | for (j = 0; j < iPieces; j++) { | 
|  | const XFA_TextPiece* pPiece = pPieceLine->m_textPieces.GetAt(j); | 
|  | int32_t iChars = pPiece->iChars; | 
|  | if (iCharCount < iChars) { | 
|  | FX_Free(pCharPos); | 
|  | pCharPos = FX_Alloc(FXTEXT_CHARPOS, iChars); | 
|  | iCharCount = iChars; | 
|  | } | 
|  | FXSYS_memset(pCharPos, 0, iCharCount * sizeof(FXTEXT_CHARPOS)); | 
|  | RenderString(pDevice.get(), pSolidBrush.get(), pPieceLine, j, pCharPos, | 
|  | tmDoc2Device); | 
|  | } | 
|  | for (j = 0; j < iPieces; j++) { | 
|  | RenderPath(pDevice.get(), pPen.get(), pPieceLine, j, pCharPos, | 
|  | tmDoc2Device); | 
|  | } | 
|  | } | 
|  | pDevice->RestoreState(); | 
|  | FX_Free(pCharPos); | 
|  | return iPieceLines; | 
|  | } | 
|  | void CXFA_TextLayout::UpdateAlign(FX_FLOAT fHeight, FX_FLOAT fBottom) { | 
|  | fHeight -= fBottom; | 
|  | if (fHeight < 0.1f) { | 
|  | return; | 
|  | } | 
|  | switch (m_textParser.GetVAlign(m_pTextProvider)) { | 
|  | case XFA_ATTRIBUTEENUM_Middle: | 
|  | fHeight /= 2.0f; | 
|  | break; | 
|  | case XFA_ATTRIBUTEENUM_Bottom: | 
|  | break; | 
|  | default: | 
|  | return; | 
|  | } | 
|  | int32_t iCount = m_pieceLines.GetSize(); | 
|  | for (int32_t i = 0; i < iCount; i++) { | 
|  | CXFA_PieceLine* pPieceLine = m_pieceLines.GetAt(i); | 
|  | int32_t iPieces = pPieceLine->m_textPieces.GetSize(); | 
|  | for (int32_t j = 0; j < iPieces; j++) { | 
|  | XFA_TextPiece* pPiece = pPieceLine->m_textPieces.GetAt(j); | 
|  | CFX_RectF& rect = pPiece->rtPiece; | 
|  | rect.top += fHeight; | 
|  | } | 
|  | } | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::Loader(const CFX_SizeF& szText, | 
|  | FX_FLOAT& fLinePos, | 
|  | FX_BOOL bSavePieces) { | 
|  | if (!m_pAllocator) { | 
|  | m_pAllocator.reset( | 
|  | IFX_MemoryAllocator::Create(FX_ALLOCTYPE_Static, 256, 0)); | 
|  | } | 
|  | GetTextDataNode(); | 
|  | if (!m_pTextDataNode) | 
|  | return TRUE; | 
|  |  | 
|  | if (m_bRichText) { | 
|  | CFDE_XMLNode* pXMLContainer = GetXMLContainerNode(); | 
|  | if (pXMLContainer) { | 
|  | if (!m_textParser.IsParsed()) { | 
|  | m_textParser.DoParse(pXMLContainer, m_pTextProvider); | 
|  | } | 
|  | IFDE_CSSComputedStyle* pRootStyle = | 
|  | m_textParser.CreateRootStyle(m_pTextProvider); | 
|  | LoadRichText(pXMLContainer, szText, fLinePos, pRootStyle, bSavePieces); | 
|  | pRootStyle->Release(); | 
|  | } | 
|  | } else { | 
|  | LoadText(m_pTextDataNode, szText, fLinePos, bSavePieces); | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | void CXFA_TextLayout::LoadText(CXFA_Node* pNode, | 
|  | const CFX_SizeF& szText, | 
|  | FX_FLOAT& fLinePos, | 
|  | FX_BOOL bSavePieces) { | 
|  | InitBreak(szText.x); | 
|  | CXFA_Para para = m_pTextProvider->GetParaNode(); | 
|  | FX_FLOAT fSpaceAbove = 0; | 
|  | if (para) { | 
|  | fSpaceAbove = para.GetSpaceAbove(); | 
|  | if (fSpaceAbove < 0.1f) { | 
|  | fSpaceAbove = 0; | 
|  | } | 
|  | int32_t verAlign = para.GetVerticalAlign(); | 
|  | switch (verAlign) { | 
|  | case XFA_ATTRIBUTEENUM_Top: | 
|  | case XFA_ATTRIBUTEENUM_Middle: | 
|  | case XFA_ATTRIBUTEENUM_Bottom: { | 
|  | fLinePos += fSpaceAbove; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | CFX_WideString wsText = pNode->GetContent(); | 
|  | wsText.TrimRight(L" "); | 
|  | FX_BOOL bRet = AppendChar(wsText, fLinePos, fSpaceAbove, bSavePieces); | 
|  | if (bRet && m_pLoader) { | 
|  | m_pLoader->m_pNode = pNode; | 
|  | } else { | 
|  | EndBreak(FX_RTFBREAK_ParagraphBreak, fLinePos, bSavePieces); | 
|  | } | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::LoadRichText(CFDE_XMLNode* pXMLNode, | 
|  | const CFX_SizeF& szText, | 
|  | FX_FLOAT& fLinePos, | 
|  | IFDE_CSSComputedStyle* pParentStyle, | 
|  | FX_BOOL bSavePieces, | 
|  | CXFA_LinkUserData* pLinkData, | 
|  | FX_BOOL bEndBreak, | 
|  | FX_BOOL bIsOl, | 
|  | int32_t iLiCount) { | 
|  | if (pXMLNode == NULL) { | 
|  | return FALSE; | 
|  | } | 
|  | CXFA_TextParseContext* pContext = | 
|  | m_textParser.GetParseContextFromMap(pXMLNode); | 
|  | FDE_CSSDISPLAY eDisplay = FDE_CSSDISPLAY_None; | 
|  | FX_BOOL bContentNode = FALSE; | 
|  | FX_FLOAT fSpaceBelow = 0; | 
|  | IFDE_CSSComputedStyle* pStyle = NULL; | 
|  | CFX_WideString wsName; | 
|  | if (bEndBreak) { | 
|  | FX_BOOL bCurOl = FALSE; | 
|  | FX_BOOL bCurLi = FALSE; | 
|  | CFDE_XMLElement* pElement = NULL; | 
|  | if (pContext) { | 
|  | if (m_bBlockContinue || | 
|  | (m_pLoader && pXMLNode == m_pLoader->m_pXMLNode)) { | 
|  | m_bBlockContinue = TRUE; | 
|  | } | 
|  | if (pXMLNode->GetType() == FDE_XMLNODE_Text) { | 
|  | bContentNode = TRUE; | 
|  | } else if (pXMLNode->GetType() == FDE_XMLNODE_Element) { | 
|  | pElement = static_cast<CFDE_XMLElement*>(pXMLNode); | 
|  | pElement->GetLocalTagName(wsName); | 
|  | } | 
|  | if (wsName == FX_WSTRC(L"ol")) { | 
|  | bIsOl = TRUE; | 
|  | bCurOl = TRUE; | 
|  | } | 
|  | if (m_bBlockContinue || bContentNode == FALSE) { | 
|  | eDisplay = pContext->GetDisplay(); | 
|  | if (eDisplay != FDE_CSSDISPLAY_Block && | 
|  | eDisplay != FDE_CSSDISPLAY_Inline && | 
|  | eDisplay != FDE_CSSDISPLAY_ListItem) { | 
|  | return TRUE; | 
|  | } | 
|  | pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle); | 
|  | InitBreak(bContentNode ? pParentStyle : pStyle, eDisplay, szText.x, | 
|  | pXMLNode, pParentStyle); | 
|  | if ((eDisplay == FDE_CSSDISPLAY_Block || | 
|  | eDisplay == FDE_CSSDISPLAY_ListItem) && | 
|  | pStyle && | 
|  | (wsName.IsEmpty() || | 
|  | (wsName != FX_WSTRC(L"body") && wsName != FX_WSTRC(L"html") && | 
|  | wsName != FX_WSTRC(L"ol") && wsName != FX_WSTRC(L"ul")))) { | 
|  | const FDE_CSSRECT* pRect = | 
|  | pStyle->GetBoundaryStyles()->GetMarginWidth(); | 
|  | if (pRect) { | 
|  | fLinePos += pRect->top.GetValue(); | 
|  | fSpaceBelow = pRect->bottom.GetValue(); | 
|  | } | 
|  | } | 
|  | if (wsName == FX_WSTRC(L"a")) { | 
|  | CFX_WideString wsLinkContent; | 
|  | ASSERT(pElement); | 
|  | pElement->GetString(L"href", wsLinkContent); | 
|  | if (!wsLinkContent.IsEmpty()) { | 
|  | pLinkData = FXTARGET_NewWith(m_pAllocator.get()) CXFA_LinkUserData( | 
|  | m_pAllocator.get(), | 
|  | wsLinkContent.GetBuffer(wsLinkContent.GetLength())); | 
|  | wsLinkContent.ReleaseBuffer(wsLinkContent.GetLength()); | 
|  | } | 
|  | } | 
|  | int32_t iTabCount = | 
|  | m_textParser.CountTabs(bContentNode ? pParentStyle : pStyle); | 
|  | FX_BOOL bSpaceRun = | 
|  | m_textParser.IsSpaceRun(bContentNode ? pParentStyle : pStyle); | 
|  | CFX_WideString wsText; | 
|  | if (bContentNode && iTabCount == 0) { | 
|  | static_cast<CFDE_XMLText*>(pXMLNode)->GetText(wsText); | 
|  | } else if (wsName == FX_WSTRC(L"br")) { | 
|  | wsText = L'\n'; | 
|  | } else if (wsName == FX_WSTRC(L"li")) { | 
|  | bCurLi = TRUE; | 
|  | if (bIsOl) { | 
|  | wsText.Format(L"%d.  ", iLiCount); | 
|  | } else { | 
|  | wsText = 0x00B7 + FX_WSTRC(L"  "); | 
|  | } | 
|  | } else if (!bContentNode) { | 
|  | if (iTabCount > 0) { | 
|  | while (iTabCount-- > 0) | 
|  | wsText += L'\t'; | 
|  | } else { | 
|  | m_textParser.GetEmbbedObj(m_pTextProvider, pXMLNode, wsText); | 
|  | } | 
|  | } | 
|  | int32_t iLength = wsText.GetLength(); | 
|  | if (iLength > 0 && bContentNode && !bSpaceRun) { | 
|  | ProcessText(wsText); | 
|  | } | 
|  | if (m_pLoader) { | 
|  | if (wsText.GetLength() > 0 && | 
|  | (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) { | 
|  | wsText.TrimLeft(0x20); | 
|  | } | 
|  | if (FDE_CSSDISPLAY_Block == eDisplay) { | 
|  | m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } else if (FDE_CSSDISPLAY_Inline == eDisplay && | 
|  | (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) { | 
|  | m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } else if (wsText.GetLength() > 0 && | 
|  | (0x20 == wsText.GetAt(wsText.GetLength() - 1))) { | 
|  | m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } else if (wsText.GetLength() != 0) { | 
|  | m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } | 
|  | } | 
|  | if (wsText.GetLength() > 0) { | 
|  | if (m_pLoader == NULL || m_pLoader->m_iChar == 0) { | 
|  | if (pLinkData) { | 
|  | pLinkData->Retain(); | 
|  | } | 
|  | CXFA_TextUserData* pUserData = FXTARGET_NewWith(m_pAllocator.get()) | 
|  | CXFA_TextUserData(m_pAllocator.get(), | 
|  | bContentNode ? pParentStyle : pStyle, | 
|  | pLinkData); | 
|  | m_pBreak->SetUserData(pUserData); | 
|  | } | 
|  | if (AppendChar(wsText, fLinePos, 0, bSavePieces)) { | 
|  | if (m_pLoader) { | 
|  | m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } | 
|  | if (IsEnd(bSavePieces)) { | 
|  | if (m_pLoader && m_pLoader->m_iTotalLines > -1) { | 
|  | m_pLoader->m_pXMLNode = pXMLNode; | 
|  | m_pLoader->m_pParentStyle = pParentStyle; | 
|  | } | 
|  | if (pStyle) | 
|  | pStyle->Release(); | 
|  | return FALSE; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | FX_BOOL ret = TRUE; | 
|  | for (CFDE_XMLNode* pChildNode = | 
|  | pXMLNode->GetNodeItem(CFDE_XMLNode::FirstChild); | 
|  | pChildNode; | 
|  | pChildNode = pChildNode->GetNodeItem(CFDE_XMLNode::NextSibling)) { | 
|  | if (bCurOl) { | 
|  | iLiCount++; | 
|  | } | 
|  | ret = LoadRichText(pChildNode, szText, fLinePos, | 
|  | pContext ? pStyle : pParentStyle, bSavePieces, | 
|  | pLinkData, TRUE, bIsOl, iLiCount); | 
|  | if (ret == FALSE) { | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  | if (m_pLoader) { | 
|  | if (FDE_CSSDISPLAY_Block == eDisplay) { | 
|  | m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; | 
|  | } | 
|  | } | 
|  | if (bCurLi) { | 
|  | EndBreak(FX_RTFBREAK_LineBreak, fLinePos, bSavePieces); | 
|  | } | 
|  | } else { | 
|  | if (pContext) { | 
|  | eDisplay = pContext->GetDisplay(); | 
|  | } | 
|  | } | 
|  | if (m_bBlockContinue) { | 
|  | if (pContext && !bContentNode) { | 
|  | uint32_t dwStatus = (eDisplay == FDE_CSSDISPLAY_Block) | 
|  | ? FX_RTFBREAK_ParagraphBreak | 
|  | : FX_RTFBREAK_PieceBreak; | 
|  | EndBreak(dwStatus, fLinePos, bSavePieces); | 
|  | if (eDisplay == FDE_CSSDISPLAY_Block) { | 
|  | fLinePos += fSpaceBelow; | 
|  | if (m_pTabstopContext) { | 
|  | m_pTabstopContext->RemoveAll(); | 
|  | } | 
|  | } | 
|  | if (wsName == FX_WSTRC(L"a")) { | 
|  | if (pLinkData) { | 
|  | pLinkData->Release(); | 
|  | pLinkData = nullptr; | 
|  | } | 
|  | } | 
|  | if (IsEnd(bSavePieces)) { | 
|  | if (pStyle) { | 
|  | pStyle->Release(); | 
|  | } | 
|  | if (m_pLoader && m_pLoader->m_iTotalLines > -1) { | 
|  | m_pLoader->m_pXMLNode = | 
|  | pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling); | 
|  | m_pLoader->m_pParentStyle = pParentStyle; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (pStyle) | 
|  | pStyle->Release(); | 
|  | return TRUE; | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::AppendChar(const CFX_WideString& wsText, | 
|  | FX_FLOAT& fLinePos, | 
|  | FX_FLOAT fSpaceAbove, | 
|  | FX_BOOL bSavePieces) { | 
|  | uint32_t dwStatus = 0; | 
|  | int32_t iChar = 0; | 
|  | if (m_pLoader) { | 
|  | iChar = m_pLoader->m_iChar; | 
|  | } | 
|  | int32_t iLength = wsText.GetLength(); | 
|  | for (int32_t i = iChar; i < iLength; i++) { | 
|  | FX_WCHAR wch = wsText.GetAt(i); | 
|  | if (wch == 0xA0) { | 
|  | wch = 0x20; | 
|  | } | 
|  | if ((dwStatus = m_pBreak->AppendChar(wch)) > FX_RTFBREAK_PieceBreak) { | 
|  | AppendTextLine(dwStatus, fLinePos, bSavePieces); | 
|  | if (IsEnd(bSavePieces)) { | 
|  | if (m_pLoader) | 
|  | m_pLoader->m_iChar = i; | 
|  | return TRUE; | 
|  | } | 
|  | if (dwStatus == FX_RTFBREAK_ParagraphBreak && m_bRichText) { | 
|  | fLinePos += fSpaceAbove; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (m_pLoader) { | 
|  | m_pLoader->m_iChar = 0; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::IsEnd(FX_BOOL bSavePieces) { | 
|  | if (!bSavePieces) { | 
|  | return FALSE; | 
|  | } | 
|  | if (m_pLoader && m_pLoader->m_iTotalLines > 0) { | 
|  | return m_iLines >= m_pLoader->m_iTotalLines; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  | void CXFA_TextLayout::ProcessText(CFX_WideString& wsText) { | 
|  | int32_t iLen = wsText.GetLength(); | 
|  | if (iLen == 0) { | 
|  | return; | 
|  | } | 
|  | FX_WCHAR* psz = wsText.GetBuffer(iLen); | 
|  | int32_t iTrimLeft = 0; | 
|  | FX_WCHAR wch = 0, wPrev = 0; | 
|  | for (int32_t i = 0; i < iLen; i++) { | 
|  | wch = psz[i]; | 
|  | if (wch < 0x20) { | 
|  | wch = 0x20; | 
|  | } | 
|  | if (wch == 0x20 && wPrev == 0x20) { | 
|  | continue; | 
|  | } | 
|  | wPrev = wch; | 
|  | psz[iTrimLeft++] = wch; | 
|  | } | 
|  | wsText.ReleaseBuffer(iLen); | 
|  | wsText = wsText.Left(iTrimLeft); | 
|  | } | 
|  | void CXFA_TextLayout::EndBreak(uint32_t dwStatus, | 
|  | FX_FLOAT& fLinePos, | 
|  | FX_BOOL bSavePieces) { | 
|  | dwStatus = m_pBreak->EndBreak(dwStatus); | 
|  | if (dwStatus > FX_RTFBREAK_PieceBreak) { | 
|  | AppendTextLine(dwStatus, fLinePos, bSavePieces, TRUE); | 
|  | } | 
|  | } | 
|  | void CXFA_TextLayout::DoTabstops(IFDE_CSSComputedStyle* pStyle, | 
|  | CXFA_PieceLine* pPieceLine) { | 
|  | if (m_pTabstopContext == NULL || m_pTabstopContext->m_iTabCount == 0) { | 
|  | return; | 
|  | } | 
|  | if (pStyle == NULL || pPieceLine == NULL) { | 
|  | return; | 
|  | } | 
|  | int32_t iPieces = pPieceLine->m_textPieces.GetSize(); | 
|  | if (iPieces == 0) { | 
|  | return; | 
|  | } | 
|  | XFA_TextPiece* pPiece = pPieceLine->m_textPieces.GetAt(iPieces - 1); | 
|  | int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex; | 
|  | int32_t iCount = m_textParser.CountTabs(pStyle); | 
|  | if (iTabstopsIndex > m_pTabstopContext->m_iTabCount - 1) { | 
|  | return; | 
|  | } | 
|  | if (iCount > 0) { | 
|  | iTabstopsIndex++; | 
|  | m_pTabstopContext->m_bTabstops = TRUE; | 
|  | FX_FLOAT fRight = 0; | 
|  | if (iPieces > 1) { | 
|  | XFA_TextPiece* p = pPieceLine->m_textPieces.GetAt(iPieces - 2); | 
|  | fRight = p->rtPiece.right(); | 
|  | } | 
|  | m_pTabstopContext->m_fTabWidth = | 
|  | pPiece->rtPiece.width + pPiece->rtPiece.left - fRight; | 
|  | } else if (iTabstopsIndex > -1) { | 
|  | FX_FLOAT fLeft = 0; | 
|  | if (m_pTabstopContext->m_bTabstops) { | 
|  | XFA_TABSTOPS* pTabstops = | 
|  | m_pTabstopContext->m_tabstops.GetDataPtr(iTabstopsIndex); | 
|  | uint32_t dwAlign = pTabstops->dwAlign; | 
|  | if (dwAlign == FX_HashCode_GetW(L"center", false)) { | 
|  | fLeft = pPiece->rtPiece.width / 2.0f; | 
|  | } else if (dwAlign == FX_HashCode_GetW(L"right", false) || | 
|  | dwAlign == FX_HashCode_GetW(L"before", false)) { | 
|  | fLeft = pPiece->rtPiece.width; | 
|  | } else if (dwAlign == FX_HashCode_GetW(L"decimal", false)) { | 
|  | int32_t iChars = pPiece->iChars; | 
|  | for (int32_t i = 0; i < iChars; i++) { | 
|  | if (pPiece->pszText[i] == L'.') { | 
|  | break; | 
|  | } | 
|  | fLeft += pPiece->pWidths[i] / 20000.0f; | 
|  | } | 
|  | } | 
|  | m_pTabstopContext->m_fLeft = | 
|  | std::min(fLeft, m_pTabstopContext->m_fTabWidth); | 
|  | m_pTabstopContext->m_bTabstops = FALSE; | 
|  | m_pTabstopContext->m_fTabWidth = 0; | 
|  | } | 
|  | pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft; | 
|  | } | 
|  | } | 
|  | void CXFA_TextLayout::AppendTextLine(uint32_t dwStatus, | 
|  | FX_FLOAT& fLinePos, | 
|  | FX_BOOL bSavePieces, | 
|  | FX_BOOL bEndBreak) { | 
|  | int32_t iPieces = m_pBreak->CountBreakPieces(); | 
|  | if (iPieces < 1) { | 
|  | return; | 
|  | } | 
|  | IFDE_CSSComputedStyle* pStyle = NULL; | 
|  | if (bSavePieces) { | 
|  | CXFA_PieceLine* pPieceLine = | 
|  | FXTARGET_NewWith(m_pAllocator.get()) CXFA_PieceLine; | 
|  | m_pieceLines.Add(pPieceLine); | 
|  | if (m_pTabstopContext) { | 
|  | m_pTabstopContext->Reset(); | 
|  | } | 
|  | FX_FLOAT fLineStep = 0, fBaseLine = 0; | 
|  | int32_t i = 0; | 
|  | for (i = 0; i < iPieces; i++) { | 
|  | const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i); | 
|  | CXFA_TextUserData* pUserData = (CXFA_TextUserData*)pPiece->m_pUserData; | 
|  | if (pUserData) | 
|  | pStyle = pUserData->m_pStyle; | 
|  | FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f; | 
|  | XFA_TextPiece* pTP = FXTARGET_NewWith(m_pAllocator.get()) XFA_TextPiece(); | 
|  | pTP->pszText = | 
|  | (FX_WCHAR*)m_pAllocator->Alloc(pPiece->m_iChars * sizeof(FX_WCHAR)); | 
|  | pTP->pWidths = | 
|  | (int32_t*)m_pAllocator->Alloc(pPiece->m_iChars * sizeof(int32_t)); | 
|  | pTP->iChars = pPiece->m_iChars; | 
|  | pPiece->GetString(pTP->pszText); | 
|  | pPiece->GetWidths(pTP->pWidths); | 
|  | pTP->iBidiLevel = pPiece->m_iBidiLevel; | 
|  | pTP->iHorScale = pPiece->m_iHorizontalScale; | 
|  | pTP->iVerScale = pPiece->m_iVerticalScale; | 
|  | m_textParser.GetUnderline(m_pTextProvider, pStyle, pTP->iUnderline, | 
|  | pTP->iPeriod); | 
|  | m_textParser.GetLinethrough(m_pTextProvider, pStyle, pTP->iLineThrough); | 
|  | pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle); | 
|  | pTP->pFont = m_textParser.GetFont(m_pTextProvider, pStyle); | 
|  | pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle); | 
|  | pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f; | 
|  | pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f; | 
|  | pTP->rtPiece.height = (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f; | 
|  | FX_FLOAT fBaseLineTemp = | 
|  | m_textParser.GetBaseline(m_pTextProvider, pStyle); | 
|  | pTP->rtPiece.top = fBaseLineTemp; | 
|  | pPieceLine->m_textPieces.Add(pTP); | 
|  | FX_FLOAT fLineHeight = m_textParser.GetLineHeight( | 
|  | m_pTextProvider, pStyle, m_iLines == 0, fVerScale); | 
|  | if (fBaseLineTemp > 0) { | 
|  | FX_FLOAT fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height; | 
|  | if (fLineHeight < fLineHeightTmp) { | 
|  | fLineHeight = fLineHeightTmp; | 
|  | } else { | 
|  | fBaseLineTemp = 0; | 
|  | } | 
|  | } else if (fBaseLine < -fBaseLineTemp) { | 
|  | fBaseLine = -fBaseLineTemp; | 
|  | } | 
|  | fLineStep = std::max(fLineStep, fLineHeight); | 
|  | if (pUserData && pUserData->m_pLinkData) { | 
|  | pUserData->m_pLinkData->Retain(); | 
|  | pTP->pLinkData = pUserData->m_pLinkData; | 
|  | } else { | 
|  | pTP->pLinkData = NULL; | 
|  | } | 
|  | DoTabstops(pStyle, pPieceLine); | 
|  | } | 
|  | for (i = 0; i < iPieces; i++) { | 
|  | XFA_TextPiece* pTP = pPieceLine->m_textPieces.GetAt(i); | 
|  | FX_FLOAT& fTop = pTP->rtPiece.top; | 
|  | FX_FLOAT fBaseLineTemp = fTop; | 
|  | fTop = fLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp; | 
|  | fTop = std::max(0.0f, fTop); | 
|  | } | 
|  | fLinePos += fLineStep + fBaseLine; | 
|  | } else { | 
|  | FX_FLOAT fLineStep = 0; | 
|  | FX_FLOAT fLineWidth = 0; | 
|  | for (int32_t i = 0; i < iPieces; i++) { | 
|  | const CFX_RTFPiece* pPiece = m_pBreak->GetBreakPiece(i); | 
|  | CXFA_TextUserData* pUserData = (CXFA_TextUserData*)pPiece->m_pUserData; | 
|  | if (pUserData) | 
|  | pStyle = pUserData->m_pStyle; | 
|  | FX_FLOAT fVerScale = pPiece->m_iVerticalScale / 100.0f; | 
|  | FX_FLOAT fBaseLine = m_textParser.GetBaseline(m_pTextProvider, pStyle); | 
|  | FX_FLOAT fLineHeight = m_textParser.GetLineHeight( | 
|  | m_pTextProvider, pStyle, m_iLines == 0, fVerScale); | 
|  | if (fBaseLine > 0) { | 
|  | FX_FLOAT fLineHeightTmp = | 
|  | fBaseLine + (FX_FLOAT)pPiece->m_iFontSize * fVerScale / 20.0f; | 
|  | if (fLineHeight < fLineHeightTmp) { | 
|  | fLineHeight = fLineHeightTmp; | 
|  | } | 
|  | } | 
|  | fLineStep = std::max(fLineStep, fLineHeight); | 
|  | fLineWidth += pPiece->m_iWidth / 20000.0f; | 
|  | } | 
|  | fLinePos += fLineStep; | 
|  | m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth); | 
|  | if (m_pLoader && m_pLoader->m_bSaveLineHeight) { | 
|  | FX_FLOAT fHeight = fLinePos - m_pLoader->m_fLastPos; | 
|  | m_pLoader->m_fLastPos = fLinePos; | 
|  | m_pLoader->m_lineHeights.Add(fHeight); | 
|  | } | 
|  | } | 
|  | if (pStyle) { | 
|  | pStyle->Retain(); | 
|  | } | 
|  | m_pBreak->ClearBreakPieces(); | 
|  | if (dwStatus == FX_RTFBREAK_ParagraphBreak) { | 
|  | m_pBreak->Reset(); | 
|  | if (!pStyle && bEndBreak) { | 
|  | CXFA_Para para = m_pTextProvider->GetParaNode(); | 
|  | if (para) { | 
|  | FX_FLOAT fStartPos = para.GetMarginLeft(); | 
|  | FX_FLOAT fIndent = para.GetTextIndent(); | 
|  | if (fIndent > 0) { | 
|  | fStartPos += fIndent; | 
|  | } | 
|  | FX_FLOAT fSpaceBelow = para.GetSpaceBelow(); | 
|  | if (fSpaceBelow < 0.1f) { | 
|  | fSpaceBelow = 0; | 
|  | } | 
|  | m_pBreak->SetLineStartPos(fStartPos); | 
|  | fLinePos += fSpaceBelow; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (pStyle) { | 
|  | FX_FLOAT fStart = 0; | 
|  | const FDE_CSSRECT* pRect = pStyle->GetBoundaryStyles()->GetMarginWidth(); | 
|  | if (pRect) { | 
|  | fStart = pRect->left.GetValue(); | 
|  | } | 
|  | FX_FLOAT fTextIndent = | 
|  | pStyle->GetParagraphStyles()->GetTextIndent().GetValue(); | 
|  | if (fTextIndent < 0) { | 
|  | fStart -= fTextIndent; | 
|  | } | 
|  | m_pBreak->SetLineStartPos(fStart); | 
|  | pStyle->Release(); | 
|  | } | 
|  | m_iLines++; | 
|  | } | 
|  | void CXFA_TextLayout::RenderString(CFDE_RenderDevice* pDevice, | 
|  | CFDE_Brush* pBrush, | 
|  | CXFA_PieceLine* pPieceLine, | 
|  | int32_t iPiece, | 
|  | FXTEXT_CHARPOS* pCharPos, | 
|  | const CFX_Matrix& tmDoc2Device) { | 
|  | const XFA_TextPiece* pPiece = pPieceLine->m_textPieces.GetAt(iPiece); | 
|  | int32_t iCount = GetDisplayPos(pPiece, pCharPos); | 
|  | if (iCount > 0) { | 
|  | pBrush->SetColor(pPiece->dwColor); | 
|  | pDevice->DrawString(pBrush, pPiece->pFont, pCharPos, iCount, | 
|  | pPiece->fFontSize, &tmDoc2Device); | 
|  | } | 
|  | pPieceLine->m_charCounts.Add(iCount); | 
|  | } | 
|  |  | 
|  | void CXFA_TextLayout::RenderPath(CFDE_RenderDevice* pDevice, | 
|  | CFDE_Pen* pPen, | 
|  | CXFA_PieceLine* pPieceLine, | 
|  | int32_t iPiece, | 
|  | FXTEXT_CHARPOS* pCharPos, | 
|  | const CFX_Matrix& tmDoc2Device) { | 
|  | XFA_TextPiece* pPiece = pPieceLine->m_textPieces.GetAt(iPiece); | 
|  | FX_BOOL bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2; | 
|  | FX_BOOL bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2; | 
|  | if (bNoUnderline && bNoLineThrough) { | 
|  | return; | 
|  | } | 
|  | pPen->SetColor(pPiece->dwColor); | 
|  | std::unique_ptr<CFDE_Path> pPath(new CFDE_Path); | 
|  | int32_t iChars = GetDisplayPos(pPiece, pCharPos); | 
|  | if (iChars > 0) { | 
|  | CFX_PointF pt1, pt2; | 
|  | FX_FLOAT fEndY = pCharPos[0].m_OriginY + 1.05f; | 
|  | if (pPiece->iPeriod == XFA_ATTRIBUTEENUM_Word) { | 
|  | for (int32_t i = 0; i < pPiece->iUnderline; i++) { | 
|  | for (int32_t j = 0; j < iChars; j++) { | 
|  | pt1.x = pCharPos[j].m_OriginX; | 
|  | pt2.x = | 
|  | pt1.x + pCharPos[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f; | 
|  | pt1.y = pt2.y = fEndY; | 
|  | pPath->AddLine(pt1, pt2); | 
|  | } | 
|  | fEndY += 2.0f; | 
|  | } | 
|  | } else { | 
|  | pt1.x = pCharPos[0].m_OriginX; | 
|  | pt2.x = | 
|  | pCharPos[iChars - 1].m_OriginX + | 
|  | pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; | 
|  | for (int32_t i = 0; i < pPiece->iUnderline; i++) { | 
|  | pt1.y = pt2.y = fEndY; | 
|  | pPath->AddLine(pt1, pt2); | 
|  | fEndY += 2.0f; | 
|  | } | 
|  | } | 
|  | fEndY = pCharPos[0].m_OriginY - pPiece->rtPiece.height * 0.25f; | 
|  | pt1.x = pCharPos[0].m_OriginX; | 
|  | pt2.x = pCharPos[iChars - 1].m_OriginX + | 
|  | pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; | 
|  | for (int32_t i = 0; i < pPiece->iLineThrough; i++) { | 
|  | pt1.y = pt2.y = fEndY; | 
|  | pPath->AddLine(pt1, pt2); | 
|  | fEndY += 2.0f; | 
|  | } | 
|  | } else { | 
|  | if (bNoLineThrough && | 
|  | (bNoUnderline || pPiece->iPeriod != XFA_ATTRIBUTEENUM_All)) { | 
|  | return; | 
|  | } | 
|  | int32_t iCharsTmp = 0; | 
|  | int32_t iPiecePrev = iPiece, iPieceNext = iPiece; | 
|  | while (iPiecePrev > 0) { | 
|  | iPiecePrev--; | 
|  | iCharsTmp = pPieceLine->m_charCounts.GetAt(iPiecePrev); | 
|  | if (iCharsTmp > 0) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (iCharsTmp == 0) { | 
|  | return; | 
|  | } | 
|  | iCharsTmp = 0; | 
|  | int32_t iPieces = pPieceLine->m_textPieces.GetSize(); | 
|  | while (iPieceNext < iPieces - 1) { | 
|  | iPieceNext++; | 
|  | iCharsTmp = pPieceLine->m_charCounts.GetAt(iPieceNext); | 
|  | if (iCharsTmp > 0) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (iCharsTmp == 0) { | 
|  | return; | 
|  | } | 
|  | FX_FLOAT fOrgX = 0.0f, fEndX = 0.0f; | 
|  | pPiece = pPieceLine->m_textPieces.GetAt(iPiecePrev); | 
|  | iChars = GetDisplayPos(pPiece, pCharPos); | 
|  | if (iChars < 1) { | 
|  | return; | 
|  | } | 
|  | fOrgX = pCharPos[iChars - 1].m_OriginX + | 
|  | pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; | 
|  | pPiece = pPieceLine->m_textPieces.GetAt(iPieceNext); | 
|  | iChars = GetDisplayPos(pPiece, pCharPos); | 
|  | if (iChars < 1) { | 
|  | return; | 
|  | } | 
|  | fEndX = pCharPos[0].m_OriginX; | 
|  | CFX_PointF pt1, pt2; | 
|  | pt1.x = fOrgX, pt2.x = fEndX; | 
|  | FX_FLOAT fEndY = pCharPos[0].m_OriginY + 1.05f; | 
|  | for (int32_t i = 0; i < pPiece->iUnderline; i++) { | 
|  | pt1.y = pt2.y = fEndY; | 
|  | pPath->AddLine(pt1, pt2); | 
|  | fEndY += 2.0f; | 
|  | } | 
|  | fEndY = pCharPos[0].m_OriginY - pPiece->rtPiece.height * 0.25f; | 
|  | for (int32_t i = 0; i < pPiece->iLineThrough; i++) { | 
|  | pt1.y = pt2.y = fEndY; | 
|  | pPath->AddLine(pt1, pt2); | 
|  | fEndY += 2.0f; | 
|  | } | 
|  | } | 
|  | pDevice->DrawPath(pPen, 1, pPath.get(), &tmDoc2Device); | 
|  | } | 
|  |  | 
|  | int32_t CXFA_TextLayout::GetDisplayPos(const XFA_TextPiece* pPiece, | 
|  | FXTEXT_CHARPOS* pCharPos, | 
|  | FX_BOOL bCharCode) { | 
|  | if (pPiece == NULL) { | 
|  | return 0; | 
|  | } | 
|  | FX_RTFTEXTOBJ tr; | 
|  | if (!ToRun(pPiece, tr)) { | 
|  | return 0; | 
|  | } | 
|  | return m_pBreak->GetDisplayPos(&tr, pCharPos, bCharCode); | 
|  | } | 
|  | FX_BOOL CXFA_TextLayout::ToRun(const XFA_TextPiece* pPiece, FX_RTFTEXTOBJ& tr) { | 
|  | int32_t iLength = pPiece->iChars; | 
|  | if (iLength < 1) { | 
|  | return FALSE; | 
|  | } | 
|  | tr.pStr = pPiece->pszText; | 
|  | tr.pFont = pPiece->pFont; | 
|  | tr.pRect = &pPiece->rtPiece; | 
|  | tr.pWidths = pPiece->pWidths; | 
|  | tr.iLength = iLength; | 
|  | tr.fFontSize = pPiece->fFontSize; | 
|  | tr.iBidiLevel = pPiece->iBidiLevel; | 
|  | tr.iCharRotation = 0; | 
|  | tr.wLineBreakChar = L'\n'; | 
|  | tr.iVerticalScale = pPiece->iVerScale; | 
|  | tr.dwLayoutStyles = FX_RTFLAYOUTSTYLE_ExpandTab; | 
|  | tr.iHorizontalScale = pPiece->iHorScale; | 
|  | return TRUE; | 
|  | } |