| // 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" |
| #include "xfa/fxfa/parser/cxfa_measurement.h" |
| |
| CXFA_CSSTagProvider::CXFA_CSSTagProvider() |
| : m_bTagAvailable(FALSE), m_bContent(FALSE) {} |
| |
| CXFA_CSSTagProvider::~CXFA_CSSTagProvider() {} |
| |
| XFA_TextPiece::XFA_TextPiece() |
| : pszText(nullptr), pFont(nullptr), pLinkData(nullptr) {} |
| |
| XFA_TextPiece::~XFA_TextPiece() { |
| if (pLinkData) |
| pLinkData->Release(); |
| } |
| |
| CXFA_TextParseContext::CXFA_TextParseContext() |
| : m_pParentStyle(nullptr), |
| m_ppMatchedDecls(nullptr), |
| m_dwMatchedDecls(0), |
| m_eDisplay(FDE_CSSDISPLAY_None) {} |
| |
| CXFA_TextParseContext::~CXFA_TextParseContext() { |
| if (m_pParentStyle) |
| m_pParentStyle->Release(); |
| FX_Free(m_ppMatchedDecls); |
| } |
| |
| 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); |
| } |
| } |
| |
| CXFA_LoaderContext::CXFA_LoaderContext() |
| : m_bSaveLineHeight(FALSE), |
| m_fWidth(0), |
| m_fHeight(0), |
| m_fLastPos(0), |
| m_fStartLineOffset(0), |
| m_iChar(0), |
| m_iTotalLines(-1), |
| m_pXMLNode(nullptr), |
| m_pNode(nullptr), |
| m_pParentStyle(nullptr), |
| m_dwFlags(0) {} |
| |
| CXFA_LoaderContext::~CXFA_LoaderContext() {} |
| |
| 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(nullptr); |
| 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 || !pTextProvider || 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 = nullptr; |
| 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; |
| } |
| CFGAS_GEFont* 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) { |
| 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 || !pTabstopContext) { |
| 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) { |
| 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 = nullptr; |
| 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, nullptr)); |
| pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr)); |
| 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, nullptr); |
| m_pBreak->SetFontSize(fFontSize); |
| m_pBreak->SetFont(m_textParser.GetFont(m_pTextProvider, nullptr)); |
| 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) { |
| 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) { |
| 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) { |
| 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, nullptr); |
| |
| 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 || 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 = nullptr; |
| 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, nullptr, 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 = nullptr; |
| 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) { |
| 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 = nullptr; |
| CFX_WideString wsName; |
| if (bEndBreak) { |
| FX_BOOL bCurOl = FALSE; |
| FX_BOOL bCurLi = FALSE; |
| CFDE_XMLElement* pElement = nullptr; |
| 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 || 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 || m_pTabstopContext->m_iTabCount == 0) { |
| return; |
| } |
| if (!pStyle || !pPieceLine) { |
| 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 = nullptr; |
| 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 = nullptr; |
| } |
| 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) { |
| 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; |
| } |
| |
| CXFA_LinkUserData::CXFA_LinkUserData(IFX_MemoryAllocator* pAllocator, |
| FX_WCHAR* pszText) |
| : m_pAllocator(pAllocator), m_dwRefCount(1), m_wsURLContent(pszText) {} |
| |
| CXFA_LinkUserData::~CXFA_LinkUserData() {} |
| |
| uint32_t CXFA_LinkUserData::Retain() { |
| return ++m_dwRefCount; |
| } |
| |
| uint32_t CXFA_LinkUserData::Release() { |
| uint32_t dwRefCount = --m_dwRefCount; |
| if (dwRefCount <= 0) |
| FXTARGET_DeleteWith(CXFA_LinkUserData, m_pAllocator, this); |
| return dwRefCount; |
| } |
| |
| const FX_WCHAR* CXFA_LinkUserData::GetLinkURL() { |
| return m_wsURLContent.c_str(); |
| } |
| |
| CXFA_TextUserData::CXFA_TextUserData(IFX_MemoryAllocator* pAllocator, |
| IFDE_CSSComputedStyle* pStyle) |
| : m_pStyle(pStyle), |
| m_pLinkData(nullptr), |
| m_pAllocator(pAllocator), |
| m_dwRefCount(0) { |
| ASSERT(m_pAllocator); |
| if (m_pStyle) |
| m_pStyle->Retain(); |
| } |
| |
| CXFA_TextUserData::CXFA_TextUserData(IFX_MemoryAllocator* pAllocator, |
| IFDE_CSSComputedStyle* pStyle, |
| CXFA_LinkUserData* pLinkData) |
| : m_pStyle(pStyle), |
| m_pLinkData(pLinkData), |
| m_pAllocator(pAllocator), |
| m_dwRefCount(0) { |
| ASSERT(m_pAllocator); |
| if (m_pStyle) |
| m_pStyle->Retain(); |
| } |
| |
| CXFA_TextUserData::~CXFA_TextUserData() { |
| if (m_pStyle) |
| m_pStyle->Release(); |
| if (m_pLinkData) |
| m_pLinkData->Release(); |
| } |
| |
| uint32_t CXFA_TextUserData::Retain() { |
| return ++m_dwRefCount; |
| } |
| |
| uint32_t CXFA_TextUserData::Release() { |
| uint32_t dwRefCount = --m_dwRefCount; |
| if (dwRefCount == 0) |
| FXTARGET_DeleteWith(CXFA_TextUserData, m_pAllocator, this); |
| return dwRefCount; |
| } |
| |
| CXFA_PieceLine::CXFA_PieceLine() {} |
| |
| CXFA_PieceLine::~CXFA_PieceLine() {} |
| |
| CXFA_TextTabstopsContext::CXFA_TextTabstopsContext() |
| : m_iTabCount(0), |
| m_iTabIndex(-1), |
| m_bTabstops(FALSE), |
| m_fTabWidth(0), |
| m_fLeft(0) {} |
| |
| CXFA_TextTabstopsContext::~CXFA_TextTabstopsContext() {} |
| |
| void CXFA_TextTabstopsContext::Append(uint32_t dwAlign, FX_FLOAT fTabstops) { |
| int32_t i = 0; |
| for (i = 0; i < m_iTabCount; i++) { |
| XFA_TABSTOPS* pTabstop = m_tabstops.GetDataPtr(i); |
| if (fTabstops < pTabstop->fTabstops) { |
| break; |
| } |
| } |
| m_tabstops.InsertSpaceAt(i, 1); |
| XFA_TABSTOPS tabstop; |
| tabstop.dwAlign = dwAlign; |
| tabstop.fTabstops = fTabstops; |
| m_tabstops.SetAt(i, tabstop); |
| m_iTabCount++; |
| } |
| |
| void CXFA_TextTabstopsContext::RemoveAll() { |
| m_tabstops.RemoveAll(); |
| m_iTabCount = 0; |
| } |
| |
| void CXFA_TextTabstopsContext::Reset() { |
| m_iTabIndex = -1; |
| m_bTabstops = FALSE; |
| m_fTabWidth = 0; |
| m_fLeft = 0; |
| } |