diff options
Diffstat (limited to 'xfa/fxfa/app/cxfa_textparser.cpp')
-rw-r--r-- | xfa/fxfa/app/cxfa_textparser.cpp | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/xfa/fxfa/app/cxfa_textparser.cpp b/xfa/fxfa/app/cxfa_textparser.cpp new file mode 100644 index 0000000000..b865e5e576 --- /dev/null +++ b/xfa/fxfa/app/cxfa_textparser.cpp @@ -0,0 +1,659 @@ +// Copyright 2017 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/cxfa_textparser.h" + +#include <algorithm> + +#include "third_party/base/ptr_util.h" +#include "xfa/fde/css/fde_css.h" +#include "xfa/fde/css/fde_cssstyleselector.h" +#include "xfa/fgas/crt/fgas_codepage.h" +#include "xfa/fgas/font/cfgas_fontmgr.h" +#include "xfa/fxfa/app/cxfa_csstagprovider.h" +#include "xfa/fxfa/app/cxfa_textparsecontext.h" +#include "xfa/fxfa/app/cxfa_texttabstopscontext.h" +#include "xfa/fxfa/app/xfa_ffwidgetacc.h" +#include "xfa/fxfa/parser/cxfa_measurement.h" +#include "xfa/fxfa/xfa_ffapp.h" +#include "xfa/fxfa/xfa_ffdoc.h" +#include "xfa/fxfa/xfa_fontmgr.h" + +namespace { + +enum class TabStopStatus { + Error, + EOS, + None, + Alignment, + StartLeader, + Leader, + Location, +}; + +} // namespace + +CXFA_TextParser::CXFA_TextParser() : m_pUASheet(nullptr) {} + +CXFA_TextParser::~CXFA_TextParser() { + if (m_pUASheet) + m_pUASheet->Release(); + + for (auto& pair : m_mapXMLNodeToParseContext) { + if (pair.second) { + FXTARGET_DeleteWith(CXFA_TextParseContext, m_pAllocator.get(), + pair.second); + } + } +} + +void CXFA_TextParser::Reset() { + for (auto& pair : m_mapXMLNodeToParseContext) { + if (pair.second) { + FXTARGET_DeleteWith(CXFA_TextParseContext, m_pAllocator.get(), + pair.second); + } + } + m_mapXMLNodeToParseContext.clear(); + m_pAllocator.reset(); +} +void CXFA_TextParser::InitCSSData(CXFA_TextProvider* pTextProvider) { + if (!pTextProvider) + return; + + if (!m_pSelector) { + CXFA_FFDoc* pDoc = pTextProvider->GetDocNode(); + CFGAS_FontMgr* pFontMgr = pDoc->GetApp()->GetFDEFontMgr(); + ASSERT(pFontMgr); + m_pSelector = pdfium::MakeUnique<CFDE_CSSStyleSelector>(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(nullptr); + IFDE_CSSFontStyle* pFontStyle = pStyle->GetFontStyles(); + IFDE_CSSParagraphStyle* pParaStyle = pStyle->GetParagraphStyles(); + FX_FLOAT fLineHeight = 0; + FX_FLOAT 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) + return pNewStyle; + + 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) { + auto it = m_mapXMLNodeToParseContext.find(pXMLNode); + if (it == m_mapXMLNodeToParseContext.end()) + return nullptr; + + CXFA_TextParseContext* pContext = it->second; + 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 = 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[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; +} + +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; +} + +CFX_RetainPtr<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(L"xfa-font-horizontal-scale", wsValue)) + return wsValue.GetInteger(); + + while (pXMLNode) { + auto it = m_mapXMLNodeToParseContext.find(pXMLNode); + if (it != m_mapXMLNodeToParseContext.end()) { + CXFA_TextParseContext* pContext = it->second; + if (pContext && pContext->m_pParentStyle && + pContext->m_pParentStyle->GetCustomStyle( + 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) { + CXFA_Font font = pTextProvider->GetFontNode(); + if (font) { + iUnderline = font.GetUnderline(); + iPeriod = font.GetUnderlinePeriod(); + } + return; + } + + 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(); + } +} + +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; + return; + } + + 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, + 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; +} + +bool CXFA_TextParser::GetEmbbedObj(CXFA_TextProvider* pTextProvider, + CFDE_XMLNode* pXMLNode, + CFX_WideString& wsValue) { + wsValue.clear(); + if (!pXMLNode) + return false; + + 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(); + + 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(); + + 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) { + auto it = m_mapXMLNodeToParseContext.find(pXMLNode); + return it != m_mapXMLNodeToParseContext.end() ? it->second : nullptr; +} + +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; + TabStopStatus eStatus = TabStopStatus::None; + FX_WCHAR ch; + while (iCur < iLength) { + ch = pTabStops[iCur]; + switch (eStatus) { + case TabStopStatus::None: + if (ch <= ' ') { + iCur++; + } else { + eStatus = TabStopStatus::Alignment; + iLast = iCur; + } + break; + case TabStopStatus::Alignment: + if (ch == ' ') { + wsAlign = CFX_WideStringC(pTabStops + iLast, iCur - iLast); + eStatus = TabStopStatus::StartLeader; + iCur++; + while (iCur < iLength && pTabStops[iCur] <= ' ') + iCur++; + iLast = iCur; + } else { + iCur++; + } + break; + case TabStopStatus::StartLeader: + if (ch != 'l') { + eStatus = TabStopStatus::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 = TabStopStatus::Location; + } + break; + case TabStopStatus::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 = TabStopStatus::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; +} |