// 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(); IFX_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; } IFX_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_pAllocator(nullptr), m_pBreak(nullptr), m_pLoader(nullptr), m_iLines(0), m_fMaxWidth(0), m_pTabstopContext(nullptr), 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 == NULL) { return TRUE; } CFDE_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode; for (; pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) { FX_BOOL bFlag = LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle, TRUE); if (!bFlag) { break; } } while (pXMLNode == NULL) { pXMLNode = pSaveXMLNode->GetNodeItem(CFDE_XMLNode::Parent); if (pXMLNode == pContainerNode) { break; } FX_BOOL bFlag = LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle, TRUE, NULL, FALSE); if (!bFlag) { break; } pSaveXMLNode = pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling); if (!pXMLNode) { continue; } for (; pXMLNode; pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::NextSibling)) { FX_BOOL bFlag = LoadRichText(pXMLNode, szText, fLinePos, m_pLoader->m_pParentStyle, TRUE); if (!bFlag) { break; } } } } else { pNode = m_pLoader->m_pNode; if (pNode == NULL) { 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)); FDE_HDEVICESTATE state = 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(state); 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; int32_t i = 0; 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 (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; int32_t i = 0; for (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 (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; }