Home | History | Annotate | Download | only in app
      1 // Copyright 2017 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include "xfa/fxfa/app/cxfa_textparser.h"
      8 
      9 #include <algorithm>
     10 #include <utility>
     11 #include <vector>
     12 
     13 #include "third_party/base/ptr_util.h"
     14 #include "xfa/fde/css/cfde_csscomputedstyle.h"
     15 #include "xfa/fde/css/cfde_cssstyleselector.h"
     16 #include "xfa/fde/css/cfde_cssstylesheet.h"
     17 #include "xfa/fde/css/fde_css.h"
     18 #include "xfa/fgas/crt/fgas_codepage.h"
     19 #include "xfa/fgas/font/cfgas_fontmgr.h"
     20 #include "xfa/fxfa/app/cxfa_csstagprovider.h"
     21 #include "xfa/fxfa/app/cxfa_textparsecontext.h"
     22 #include "xfa/fxfa/app/cxfa_texttabstopscontext.h"
     23 #include "xfa/fxfa/app/xfa_ffwidgetacc.h"
     24 #include "xfa/fxfa/parser/cxfa_measurement.h"
     25 #include "xfa/fxfa/xfa_ffapp.h"
     26 #include "xfa/fxfa/xfa_ffdoc.h"
     27 #include "xfa/fxfa/xfa_fontmgr.h"
     28 
     29 namespace {
     30 
     31 enum class TabStopStatus {
     32   Error,
     33   EOS,
     34   None,
     35   Alignment,
     36   StartLeader,
     37   Leader,
     38   Location,
     39 };
     40 
     41 }  // namespace
     42 
     43 CXFA_TextParser::CXFA_TextParser()
     44     : m_bParsed(false), m_cssInitialized(false) {}
     45 
     46 CXFA_TextParser::~CXFA_TextParser() {
     47   for (auto& pair : m_mapXMLNodeToParseContext) {
     48     if (pair.second)
     49       delete pair.second;
     50   }
     51 }
     52 
     53 void CXFA_TextParser::Reset() {
     54   for (auto& pair : m_mapXMLNodeToParseContext) {
     55     if (pair.second)
     56       delete pair.second;
     57   }
     58   m_mapXMLNodeToParseContext.clear();
     59   m_bParsed = false;
     60 }
     61 void CXFA_TextParser::InitCSSData(CXFA_TextProvider* pTextProvider) {
     62   if (!pTextProvider)
     63     return;
     64 
     65   if (!m_pSelector) {
     66     CXFA_FFDoc* pDoc = pTextProvider->GetDocNode();
     67     CFGAS_FontMgr* pFontMgr = pDoc->GetApp()->GetFDEFontMgr();
     68     ASSERT(pFontMgr);
     69     m_pSelector = pdfium::MakeUnique<CFDE_CSSStyleSelector>(pFontMgr);
     70     FX_FLOAT fFontSize = 10;
     71     CXFA_Font font = pTextProvider->GetFontNode();
     72     if (font) {
     73       fFontSize = font.GetFontSize();
     74     }
     75     m_pSelector->SetDefFontSize(fFontSize);
     76   }
     77 
     78   if (m_cssInitialized)
     79     return;
     80 
     81   m_cssInitialized = true;
     82   auto uaSheet = LoadDefaultSheetStyle();
     83   m_pSelector->SetUAStyleSheet(std::move(uaSheet));
     84   m_pSelector->UpdateStyleIndex();
     85 }
     86 
     87 std::unique_ptr<CFDE_CSSStyleSheet> CXFA_TextParser::LoadDefaultSheetStyle() {
     88   static const FX_WCHAR s_pStyle[] =
     89       L"html,body,ol,p,ul{display:block}"
     90       L"li{display:list-item}"
     91       L"ol,ul{padding-left:33px;margin:1.12em 0}"
     92       L"ol{list-style-type:decimal}"
     93       L"a{color:#0000ff;text-decoration:underline}"
     94       L"b{font-weight:bolder}"
     95       L"i{font-style:italic}"
     96       L"sup{vertical-align:+15em;font-size:.66em}"
     97       L"sub{vertical-align:-15em;font-size:.66em}";
     98 
     99   auto sheet = pdfium::MakeUnique<CFDE_CSSStyleSheet>();
    100   return sheet->LoadBuffer(s_pStyle, FXSYS_wcslen(s_pStyle)) ? std::move(sheet)
    101                                                              : nullptr;
    102 }
    103 
    104 CFX_RetainPtr<CFDE_CSSComputedStyle> CXFA_TextParser::CreateRootStyle(
    105     CXFA_TextProvider* pTextProvider) {
    106   CXFA_Font font = pTextProvider->GetFontNode();
    107   CXFA_Para para = pTextProvider->GetParaNode();
    108   auto pStyle = m_pSelector->CreateComputedStyle(nullptr);
    109   FX_FLOAT fLineHeight = 0;
    110   FX_FLOAT fFontSize = 10;
    111 
    112   if (para) {
    113     fLineHeight = para.GetLineHeight();
    114     FDE_CSSLength indent;
    115     indent.Set(FDE_CSSLengthUnit::Point, para.GetTextIndent());
    116     pStyle->SetTextIndent(indent);
    117     FDE_CSSTextAlign hAlign = FDE_CSSTextAlign::Left;
    118     switch (para.GetHorizontalAlign()) {
    119       case XFA_ATTRIBUTEENUM_Center:
    120         hAlign = FDE_CSSTextAlign::Center;
    121         break;
    122       case XFA_ATTRIBUTEENUM_Right:
    123         hAlign = FDE_CSSTextAlign::Right;
    124         break;
    125       case XFA_ATTRIBUTEENUM_Justify:
    126         hAlign = FDE_CSSTextAlign::Justify;
    127         break;
    128       case XFA_ATTRIBUTEENUM_JustifyAll:
    129         hAlign = FDE_CSSTextAlign::JustifyAll;
    130         break;
    131     }
    132     pStyle->SetTextAlign(hAlign);
    133     FDE_CSSRect rtMarginWidth;
    134     rtMarginWidth.left.Set(FDE_CSSLengthUnit::Point, para.GetMarginLeft());
    135     rtMarginWidth.top.Set(FDE_CSSLengthUnit::Point, para.GetSpaceAbove());
    136     rtMarginWidth.right.Set(FDE_CSSLengthUnit::Point, para.GetMarginRight());
    137     rtMarginWidth.bottom.Set(FDE_CSSLengthUnit::Point, para.GetSpaceBelow());
    138     pStyle->SetMarginWidth(rtMarginWidth);
    139   }
    140 
    141   if (font) {
    142     pStyle->SetColor(font.GetColor());
    143     pStyle->SetFontStyle(font.IsItalic() ? FDE_CSSFontStyle::Italic
    144                                          : FDE_CSSFontStyle::Normal);
    145     pStyle->SetFontWeight(font.IsBold() ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL);
    146     pStyle->SetNumberVerticalAlign(-font.GetBaselineShift());
    147     fFontSize = font.GetFontSize();
    148     FDE_CSSLength letterSpacing;
    149     letterSpacing.Set(FDE_CSSLengthUnit::Point, font.GetLetterSpacing());
    150     pStyle->SetLetterSpacing(letterSpacing);
    151     uint32_t dwDecoration = 0;
    152     if (font.GetLineThrough() > 0)
    153       dwDecoration |= FDE_CSSTEXTDECORATION_LineThrough;
    154     if (font.GetUnderline() > 1)
    155       dwDecoration |= FDE_CSSTEXTDECORATION_Double;
    156     else if (font.GetUnderline() > 0)
    157       dwDecoration |= FDE_CSSTEXTDECORATION_Underline;
    158 
    159     pStyle->SetTextDecoration(dwDecoration);
    160   }
    161   pStyle->SetLineHeight(fLineHeight);
    162   pStyle->SetFontSize(fFontSize);
    163   return pStyle;
    164 }
    165 
    166 CFX_RetainPtr<CFDE_CSSComputedStyle> CXFA_TextParser::CreateStyle(
    167     CFDE_CSSComputedStyle* pParentStyle) {
    168   auto pNewStyle = m_pSelector->CreateComputedStyle(pParentStyle);
    169   ASSERT(pNewStyle);
    170   if (!pParentStyle)
    171     return pNewStyle;
    172 
    173   uint32_t dwDecoration = pParentStyle->GetTextDecoration();
    174   FX_FLOAT fBaseLine = 0;
    175   if (pParentStyle->GetVerticalAlign() == FDE_CSSVerticalAlign::Number)
    176     fBaseLine = pParentStyle->GetNumberVerticalAlign();
    177 
    178   pNewStyle->SetTextDecoration(dwDecoration);
    179   pNewStyle->SetNumberVerticalAlign(fBaseLine);
    180 
    181   const FDE_CSSRect* pRect = pParentStyle->GetMarginWidth();
    182   if (pRect)
    183     pNewStyle->SetMarginWidth(*pRect);
    184   return pNewStyle;
    185 }
    186 
    187 CFX_RetainPtr<CFDE_CSSComputedStyle> CXFA_TextParser::ComputeStyle(
    188     CFDE_XMLNode* pXMLNode,
    189     CFDE_CSSComputedStyle* pParentStyle) {
    190   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
    191   if (it == m_mapXMLNodeToParseContext.end())
    192     return nullptr;
    193 
    194   CXFA_TextParseContext* pContext = it->second;
    195   if (!pContext)
    196     return nullptr;
    197 
    198   pContext->m_pParentStyle.Reset(pParentStyle);
    199 
    200   auto tagProvider = ParseTagInfo(pXMLNode);
    201   if (tagProvider->m_bContent)
    202     return nullptr;
    203 
    204   auto pStyle = CreateStyle(pParentStyle);
    205   m_pSelector->ComputeStyle(pContext->GetDecls(),
    206                             tagProvider->GetAttribute(L"style"),
    207                             tagProvider->GetAttribute(L"align"), pStyle.Get());
    208   return pStyle;
    209 }
    210 
    211 void CXFA_TextParser::DoParse(CFDE_XMLNode* pXMLContainer,
    212                               CXFA_TextProvider* pTextProvider) {
    213   if (!pXMLContainer || !pTextProvider || m_bParsed)
    214     return;
    215 
    216   m_bParsed = true;
    217   InitCSSData(pTextProvider);
    218   auto pRootStyle = CreateRootStyle(pTextProvider);
    219   ParseRichText(pXMLContainer, pRootStyle.Get());
    220 }
    221 
    222 void CXFA_TextParser::ParseRichText(CFDE_XMLNode* pXMLNode,
    223                                     CFDE_CSSComputedStyle* pParentStyle) {
    224   if (!pXMLNode)
    225     return;
    226 
    227   auto tagProvider = ParseTagInfo(pXMLNode);
    228   if (!tagProvider->m_bTagAvailable)
    229     return;
    230 
    231   CFX_RetainPtr<CFDE_CSSComputedStyle> pNewStyle;
    232   if ((tagProvider->GetTagName() != L"body") ||
    233       (tagProvider->GetTagName() != L"html")) {
    234     CXFA_TextParseContext* pTextContext = new CXFA_TextParseContext;
    235     FDE_CSSDisplay eDisplay = FDE_CSSDisplay::Inline;
    236     if (!tagProvider->m_bContent) {
    237       auto declArray =
    238           m_pSelector->MatchDeclarations(tagProvider->GetTagName());
    239       pNewStyle = CreateStyle(pParentStyle);
    240       m_pSelector->ComputeStyle(declArray, tagProvider->GetAttribute(L"style"),
    241                                 tagProvider->GetAttribute(L"align"),
    242                                 pNewStyle.Get());
    243 
    244       if (!declArray.empty())
    245         pTextContext->SetDecls(std::move(declArray));
    246 
    247       eDisplay = pNewStyle->GetDisplay();
    248     }
    249     pTextContext->SetDisplay(eDisplay);
    250     m_mapXMLNodeToParseContext[pXMLNode] = pTextContext;
    251   }
    252 
    253   for (CFDE_XMLNode* pXMLChild =
    254            pXMLNode->GetNodeItem(CFDE_XMLNode::FirstChild);
    255        pXMLChild;
    256        pXMLChild = pXMLChild->GetNodeItem(CFDE_XMLNode::NextSibling)) {
    257     ParseRichText(pXMLChild, pNewStyle.Get());
    258   }
    259 }
    260 
    261 bool CXFA_TextParser::TagValidate(const CFX_WideString& wsName) const {
    262   static const uint32_t s_XFATagName[] = {
    263       0x61,        // a
    264       0x62,        // b
    265       0x69,        // i
    266       0x70,        // p
    267       0x0001f714,  // br
    268       0x00022a55,  // li
    269       0x000239bb,  // ol
    270       0x00025881,  // ul
    271       0x0bd37faa,  // sub
    272       0x0bd37fb8,  // sup
    273       0xa73e3af2,  // span
    274       0xb182eaae,  // body
    275       0xdb8ac455,  // html
    276   };
    277   static const int32_t s_iCount = FX_ArraySize(s_XFATagName);
    278 
    279   return std::binary_search(s_XFATagName, s_XFATagName + s_iCount,
    280                             FX_HashCode_GetW(wsName.AsStringC(), true));
    281 }
    282 
    283 std::unique_ptr<CXFA_CSSTagProvider> CXFA_TextParser::ParseTagInfo(
    284     CFDE_XMLNode* pXMLNode) {
    285   auto tagProvider = pdfium::MakeUnique<CXFA_CSSTagProvider>();
    286 
    287   CFX_WideString wsName;
    288   if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
    289     CFDE_XMLElement* pXMLElement = static_cast<CFDE_XMLElement*>(pXMLNode);
    290     pXMLElement->GetLocalTagName(wsName);
    291     tagProvider->SetTagName(wsName);
    292     tagProvider->m_bTagAvailable = TagValidate(wsName);
    293 
    294     CFX_WideString wsValue;
    295     pXMLElement->GetString(L"style", wsValue);
    296     if (!wsValue.IsEmpty())
    297       tagProvider->SetAttribute(L"style", wsValue);
    298   } else if (pXMLNode->GetType() == FDE_XMLNODE_Text) {
    299     tagProvider->m_bTagAvailable = true;
    300     tagProvider->m_bContent = true;
    301   }
    302   return tagProvider;
    303 }
    304 
    305 int32_t CXFA_TextParser::GetVAlign(CXFA_TextProvider* pTextProvider) const {
    306   CXFA_Para para = pTextProvider->GetParaNode();
    307   return para ? para.GetVerticalAlign() : XFA_ATTRIBUTEENUM_Top;
    308 }
    309 
    310 FX_FLOAT CXFA_TextParser::GetTabInterval(CFDE_CSSComputedStyle* pStyle) const {
    311   CFX_WideString wsValue;
    312   if (pStyle && pStyle->GetCustomStyle(L"tab-interval", wsValue))
    313     return CXFA_Measurement(wsValue.AsStringC()).ToUnit(XFA_UNIT_Pt);
    314   return 36;
    315 }
    316 
    317 int32_t CXFA_TextParser::CountTabs(CFDE_CSSComputedStyle* pStyle) const {
    318   CFX_WideString wsValue;
    319   if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", wsValue))
    320     return wsValue.GetInteger();
    321   return 0;
    322 }
    323 
    324 bool CXFA_TextParser::IsSpaceRun(CFDE_CSSComputedStyle* pStyle) const {
    325   CFX_WideString wsValue;
    326   if (pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", wsValue)) {
    327     wsValue.MakeLower();
    328     return wsValue == L"yes";
    329   }
    330   return false;
    331 }
    332 
    333 CFX_RetainPtr<CFGAS_GEFont> CXFA_TextParser::GetFont(
    334     CXFA_TextProvider* pTextProvider,
    335     CFDE_CSSComputedStyle* pStyle) const {
    336   CFX_WideStringC wsFamily = L"Courier";
    337   uint32_t dwStyle = 0;
    338   CXFA_Font font = pTextProvider->GetFontNode();
    339   if (font) {
    340     font.GetTypeface(wsFamily);
    341     if (font.IsBold())
    342       dwStyle |= FX_FONTSTYLE_Bold;
    343     if (font.IsItalic())
    344       dwStyle |= FX_FONTSTYLE_Italic;
    345   }
    346 
    347   if (pStyle) {
    348     int32_t iCount = pStyle->CountFontFamilies();
    349     if (iCount > 0)
    350       wsFamily = pStyle->GetFontFamily(iCount - 1).AsStringC();
    351 
    352     dwStyle = 0;
    353     if (pStyle->GetFontWeight() > FXFONT_FW_NORMAL)
    354       dwStyle |= FX_FONTSTYLE_Bold;
    355     if (pStyle->GetFontStyle() == FDE_CSSFontStyle::Italic)
    356       dwStyle |= FX_FONTSTYLE_Italic;
    357   }
    358 
    359   CXFA_FFDoc* pDoc = pTextProvider->GetDocNode();
    360   CXFA_FontMgr* pFontMgr = pDoc->GetApp()->GetXFAFontMgr();
    361   return pFontMgr->GetFont(pDoc, wsFamily, dwStyle);
    362 }
    363 
    364 FX_FLOAT CXFA_TextParser::GetFontSize(CXFA_TextProvider* pTextProvider,
    365                                       CFDE_CSSComputedStyle* pStyle) const {
    366   if (pStyle)
    367     return pStyle->GetFontSize();
    368 
    369   CXFA_Font font = pTextProvider->GetFontNode();
    370   if (font)
    371     return font.GetFontSize();
    372   return 10;
    373 }
    374 
    375 int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider,
    376                                      CFDE_CSSComputedStyle* pStyle,
    377                                      CFDE_XMLNode* pXMLNode) const {
    378   if (pStyle) {
    379     CFX_WideString wsValue;
    380     if (pStyle->GetCustomStyle(L"xfa-font-horizontal-scale", wsValue))
    381       return wsValue.GetInteger();
    382 
    383     while (pXMLNode) {
    384       auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
    385       if (it != m_mapXMLNodeToParseContext.end()) {
    386         CXFA_TextParseContext* pContext = it->second;
    387         if (pContext && pContext->m_pParentStyle &&
    388             pContext->m_pParentStyle->GetCustomStyle(
    389                 L"xfa-font-horizontal-scale", wsValue)) {
    390           return wsValue.GetInteger();
    391         }
    392       }
    393       pXMLNode = pXMLNode->GetNodeItem(CFDE_XMLNode::Parent);
    394     }
    395   }
    396 
    397   if (CXFA_Font font = pTextProvider->GetFontNode())
    398     return static_cast<int32_t>(font.GetHorizontalScale());
    399   return 100;
    400 }
    401 
    402 int32_t CXFA_TextParser::GetVerScale(CXFA_TextProvider* pTextProvider,
    403                                      CFDE_CSSComputedStyle* pStyle) const {
    404   if (pStyle) {
    405     CFX_WideString wsValue;
    406     if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", wsValue))
    407       return wsValue.GetInteger();
    408   }
    409 
    410   if (CXFA_Font font = pTextProvider->GetFontNode())
    411     return (int32_t)font.GetVerticalScale();
    412   return 100;
    413 }
    414 
    415 void CXFA_TextParser::GetUnderline(CXFA_TextProvider* pTextProvider,
    416                                    CFDE_CSSComputedStyle* pStyle,
    417                                    int32_t& iUnderline,
    418                                    int32_t& iPeriod) const {
    419   iUnderline = 0;
    420   iPeriod = XFA_ATTRIBUTEENUM_All;
    421   if (!pStyle) {
    422     CXFA_Font font = pTextProvider->GetFontNode();
    423     if (font) {
    424       iUnderline = font.GetUnderline();
    425       iPeriod = font.GetUnderlinePeriod();
    426     }
    427     return;
    428   }
    429 
    430   uint32_t dwDecoration = pStyle->GetTextDecoration();
    431   if (dwDecoration & FDE_CSSTEXTDECORATION_Double)
    432     iUnderline = 2;
    433   else if (dwDecoration & FDE_CSSTEXTDECORATION_Underline)
    434     iUnderline = 1;
    435 
    436   CFX_WideString wsValue;
    437   if (pStyle->GetCustomStyle(L"underlinePeriod", wsValue)) {
    438     if (wsValue == L"word")
    439       iPeriod = XFA_ATTRIBUTEENUM_Word;
    440   } else if (CXFA_Font font = pTextProvider->GetFontNode()) {
    441     iPeriod = font.GetUnderlinePeriod();
    442   }
    443 }
    444 
    445 void CXFA_TextParser::GetLinethrough(CXFA_TextProvider* pTextProvider,
    446                                      CFDE_CSSComputedStyle* pStyle,
    447                                      int32_t& iLinethrough) const {
    448   if (pStyle) {
    449     uint32_t dwDecoration = pStyle->GetTextDecoration();
    450     iLinethrough = (dwDecoration & FDE_CSSTEXTDECORATION_LineThrough) ? 1 : 0;
    451     return;
    452   }
    453 
    454   CXFA_Font font = pTextProvider->GetFontNode();
    455   if (font)
    456     iLinethrough = font.GetLineThrough();
    457 }
    458 
    459 FX_ARGB CXFA_TextParser::GetColor(CXFA_TextProvider* pTextProvider,
    460                                   CFDE_CSSComputedStyle* pStyle) const {
    461   if (pStyle)
    462     return pStyle->GetColor();
    463   if (CXFA_Font font = pTextProvider->GetFontNode())
    464     return font.GetColor();
    465 
    466   return 0xFF000000;
    467 }
    468 
    469 FX_FLOAT CXFA_TextParser::GetBaseline(CXFA_TextProvider* pTextProvider,
    470                                       CFDE_CSSComputedStyle* pStyle) const {
    471   if (pStyle) {
    472     if (pStyle->GetVerticalAlign() == FDE_CSSVerticalAlign::Number)
    473       return pStyle->GetNumberVerticalAlign();
    474   } else if (CXFA_Font font = pTextProvider->GetFontNode()) {
    475     return font.GetBaselineShift();
    476   }
    477   return 0;
    478 }
    479 
    480 FX_FLOAT CXFA_TextParser::GetLineHeight(CXFA_TextProvider* pTextProvider,
    481                                         CFDE_CSSComputedStyle* pStyle,
    482                                         bool bFirst,
    483                                         FX_FLOAT fVerScale) const {
    484   FX_FLOAT fLineHeight = 0;
    485   if (pStyle)
    486     fLineHeight = pStyle->GetLineHeight();
    487   else if (CXFA_Para para = pTextProvider->GetParaNode())
    488     fLineHeight = para.GetLineHeight();
    489 
    490   if (bFirst) {
    491     FX_FLOAT fFontSize = GetFontSize(pTextProvider, pStyle);
    492     if (fLineHeight < 0.1f)
    493       fLineHeight = fFontSize;
    494     else
    495       fLineHeight = std::min(fLineHeight, fFontSize);
    496   } else if (fLineHeight < 0.1f) {
    497     fLineHeight = GetFontSize(pTextProvider, pStyle) * 1.2f;
    498   }
    499   fLineHeight *= fVerScale;
    500   return fLineHeight;
    501 }
    502 
    503 bool CXFA_TextParser::GetEmbbedObj(CXFA_TextProvider* pTextProvider,
    504                                    CFDE_XMLNode* pXMLNode,
    505                                    CFX_WideString& wsValue) {
    506   wsValue.clear();
    507   if (!pXMLNode)
    508     return false;
    509 
    510   bool bRet = false;
    511   if (pXMLNode->GetType() == FDE_XMLNODE_Element) {
    512     CFDE_XMLElement* pElement = static_cast<CFDE_XMLElement*>(pXMLNode);
    513     CFX_WideString wsAttr;
    514     pElement->GetString(L"xfa:embed", wsAttr);
    515     if (wsAttr.IsEmpty())
    516       return false;
    517     if (wsAttr.GetAt(0) == L'#')
    518       wsAttr.Delete(0);
    519 
    520     CFX_WideString ws;
    521     pElement->GetString(L"xfa:embedType", ws);
    522     if (ws.IsEmpty())
    523       ws = L"som";
    524     else
    525       ws.MakeLower();
    526 
    527     bool bURI = (ws == L"uri");
    528     if (!bURI && ws != L"som")
    529       return false;
    530 
    531     ws.clear();
    532     pElement->GetString(L"xfa:embedMode", ws);
    533     if (ws.IsEmpty())
    534       ws = L"formatted";
    535     else
    536       ws.MakeLower();
    537 
    538     bool bRaw = (ws == L"raw");
    539     if (!bRaw && ws != L"formatted")
    540       return false;
    541 
    542     bRet = pTextProvider->GetEmbbedObj(bURI, bRaw, wsAttr, wsValue);
    543   }
    544   return bRet;
    545 }
    546 
    547 CXFA_TextParseContext* CXFA_TextParser::GetParseContextFromMap(
    548     CFDE_XMLNode* pXMLNode) {
    549   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
    550   return it != m_mapXMLNodeToParseContext.end() ? it->second : nullptr;
    551 }
    552 
    553 bool CXFA_TextParser::GetTabstops(CFDE_CSSComputedStyle* pStyle,
    554                                   CXFA_TextTabstopsContext* pTabstopContext) {
    555   if (!pStyle || !pTabstopContext)
    556     return false;
    557 
    558   CFX_WideString wsValue;
    559   if (!pStyle->GetCustomStyle(L"xfa-tab-stops", wsValue) &&
    560       !pStyle->GetCustomStyle(L"tab-stops", wsValue)) {
    561     return false;
    562   }
    563 
    564   int32_t iLength = wsValue.GetLength();
    565   const FX_WCHAR* pTabStops = wsValue.c_str();
    566   int32_t iCur = 0;
    567   int32_t iLast = 0;
    568   CFX_WideString wsAlign;
    569   TabStopStatus eStatus = TabStopStatus::None;
    570   FX_WCHAR ch;
    571   while (iCur < iLength) {
    572     ch = pTabStops[iCur];
    573     switch (eStatus) {
    574       case TabStopStatus::None:
    575         if (ch <= ' ') {
    576           iCur++;
    577         } else {
    578           eStatus = TabStopStatus::Alignment;
    579           iLast = iCur;
    580         }
    581         break;
    582       case TabStopStatus::Alignment:
    583         if (ch == ' ') {
    584           wsAlign = CFX_WideStringC(pTabStops + iLast, iCur - iLast);
    585           eStatus = TabStopStatus::StartLeader;
    586           iCur++;
    587           while (iCur < iLength && pTabStops[iCur] <= ' ')
    588             iCur++;
    589           iLast = iCur;
    590         } else {
    591           iCur++;
    592         }
    593         break;
    594       case TabStopStatus::StartLeader:
    595         if (ch != 'l') {
    596           eStatus = TabStopStatus::Location;
    597         } else {
    598           int32_t iCount = 0;
    599           while (iCur < iLength) {
    600             ch = pTabStops[iCur];
    601             iCur++;
    602             if (ch == '(') {
    603               iCount++;
    604             } else if (ch == ')') {
    605               iCount--;
    606               if (iCount == 0)
    607                 break;
    608             }
    609           }
    610           while (iCur < iLength && pTabStops[iCur] <= ' ')
    611             iCur++;
    612 
    613           iLast = iCur;
    614           eStatus = TabStopStatus::Location;
    615         }
    616         break;
    617       case TabStopStatus::Location:
    618         if (ch == ' ') {
    619           uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringC(), true);
    620           CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast));
    621           FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt);
    622           pTabstopContext->Append(dwHashCode, fPos);
    623           wsAlign.clear();
    624           eStatus = TabStopStatus::None;
    625         }
    626         iCur++;
    627         break;
    628       default:
    629         break;
    630     }
    631   }
    632 
    633   if (!wsAlign.IsEmpty()) {
    634     uint32_t dwHashCode = FX_HashCode_GetW(wsAlign.AsStringC(), true);
    635     CXFA_Measurement ms(CFX_WideStringC(pTabStops + iLast, iCur - iLast));
    636     FX_FLOAT fPos = ms.ToUnit(XFA_UNIT_Pt);
    637     pTabstopContext->Append(dwHashCode, fPos);
    638   }
    639   return true;
    640 }
    641