• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The PDFium Authors
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/cxfa_textparser.h"
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "core/fxcrt/css/cfx_css.h"
13 #include "core/fxcrt/css/cfx_csscomputedstyle.h"
14 #include "core/fxcrt/css/cfx_cssdeclaration.h"
15 #include "core/fxcrt/css/cfx_cssstyleselector.h"
16 #include "core/fxcrt/css/cfx_cssstylesheet.h"
17 #include "core/fxcrt/fx_codepage.h"
18 #include "core/fxcrt/xml/cfx_xmlelement.h"
19 #include "core/fxcrt/xml/cfx_xmlnode.h"
20 #include "core/fxge/fx_font.h"
21 #include "third_party/base/check.h"
22 #include "third_party/base/notreached.h"
23 #include "xfa/fgas/font/cfgas_fontmgr.h"
24 #include "xfa/fgas/font/cfgas_gefont.h"
25 #include "xfa/fxfa/cxfa_ffapp.h"
26 #include "xfa/fxfa/cxfa_ffdoc.h"
27 #include "xfa/fxfa/cxfa_fontmgr.h"
28 #include "xfa/fxfa/cxfa_textprovider.h"
29 #include "xfa/fxfa/cxfa_texttabstopscontext.h"
30 #include "xfa/fxfa/parser/cxfa_font.h"
31 #include "xfa/fxfa/parser/cxfa_measurement.h"
32 #include "xfa/fxfa/parser/cxfa_para.h"
33 
34 namespace {
35 
36 enum class TabStopStatus {
37   Error,
38   EOS,
39   None,
40   Alignment,
41   StartLeader,
42   Leader,
43   Location,
44 };
45 
GetLowerCaseElementAttributeOrDefault(const CFX_XMLElement * pElement,const WideString & wsName,const WideString & wsDefaultValue)46 WideString GetLowerCaseElementAttributeOrDefault(
47     const CFX_XMLElement* pElement,
48     const WideString& wsName,
49     const WideString& wsDefaultValue) {
50   WideString ws = pElement->GetAttribute(wsName);
51   if (ws.IsEmpty())
52     ws = wsDefaultValue;
53   else
54     ws.MakeLower();
55   return ws;
56 }
57 
58 }  // namespace
59 
60 CXFA_TextParser::CXFA_TextParser() = default;
61 
62 CXFA_TextParser::~CXFA_TextParser() = default;
63 
Reset()64 void CXFA_TextParser::Reset() {
65   m_mapXMLNodeToParseContext.clear();
66   m_bParsed = false;
67 }
68 
InitCSSData(CXFA_TextProvider * pTextProvider)69 void CXFA_TextParser::InitCSSData(CXFA_TextProvider* pTextProvider) {
70   if (!pTextProvider)
71     return;
72 
73   if (!m_pSelector) {
74     m_pSelector = std::make_unique<CFX_CSSStyleSelector>();
75 
76     CXFA_Font* font = pTextProvider->GetFontIfExists();
77     m_pSelector->SetDefaultFontSize(font ? font->GetFontSize() : 10.0f);
78   }
79 
80   if (m_cssInitialized)
81     return;
82 
83   m_cssInitialized = true;
84   auto uaSheet = LoadDefaultSheetStyle();
85   m_pSelector->SetUAStyleSheet(std::move(uaSheet));
86   m_pSelector->UpdateStyleIndex();
87 }
88 
LoadDefaultSheetStyle()89 std::unique_ptr<CFX_CSSStyleSheet> CXFA_TextParser::LoadDefaultSheetStyle() {
90   static const char kStyle[] =
91       "html,body,ol,p,ul{display:block}"
92       "li{display:list-item}"
93       "ol,ul{padding-left:33px;margin:1.12em 0}"
94       "ol{list-style-type:decimal}"
95       "a{color:#0000ff;text-decoration:underline}"
96       "b{font-weight:bolder}"
97       "i{font-style:italic}"
98       "sup{vertical-align:+15em;font-size:.66em}"
99       "sub{vertical-align:-15em;font-size:.66em}";
100   WideString ws = WideString::FromASCII(kStyle);
101   auto sheet = std::make_unique<CFX_CSSStyleSheet>();
102   if (!sheet->LoadBuffer(ws.AsStringView()))
103     return nullptr;
104 
105   return sheet;
106 }
107 
CreateRootStyle(CXFA_TextProvider * pTextProvider)108 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateRootStyle(
109     CXFA_TextProvider* pTextProvider) {
110   CXFA_Para* para = pTextProvider->GetParaIfExists();
111   auto pStyle = m_pSelector->CreateComputedStyle(nullptr);
112   float fLineHeight = 0;
113   float fFontSize = 10;
114 
115   if (para) {
116     fLineHeight = para->GetLineHeight();
117     CFX_CSSLength indent;
118     indent.Set(CFX_CSSLengthUnit::Point, para->GetTextIndent());
119     pStyle->SetTextIndent(indent);
120     CFX_CSSTextAlign hAlign = CFX_CSSTextAlign::Left;
121     switch (para->GetHorizontalAlign()) {
122       case XFA_AttributeValue::Center:
123         hAlign = CFX_CSSTextAlign::Center;
124         break;
125       case XFA_AttributeValue::Right:
126         hAlign = CFX_CSSTextAlign::Right;
127         break;
128       case XFA_AttributeValue::Justify:
129         hAlign = CFX_CSSTextAlign::Justify;
130         break;
131       case XFA_AttributeValue::JustifyAll:
132         hAlign = CFX_CSSTextAlign::JustifyAll;
133         break;
134       case XFA_AttributeValue::Left:
135       case XFA_AttributeValue::Radix:
136         break;
137       default:
138         NOTREACHED();
139         break;
140     }
141     pStyle->SetTextAlign(hAlign);
142     CFX_CSSRect rtMarginWidth;
143     rtMarginWidth.left.Set(CFX_CSSLengthUnit::Point, para->GetMarginLeft());
144     rtMarginWidth.top.Set(CFX_CSSLengthUnit::Point, para->GetSpaceAbove());
145     rtMarginWidth.right.Set(CFX_CSSLengthUnit::Point, para->GetMarginRight());
146     rtMarginWidth.bottom.Set(CFX_CSSLengthUnit::Point, para->GetSpaceBelow());
147     pStyle->SetMarginWidth(rtMarginWidth);
148   }
149 
150   CXFA_Font* font = pTextProvider->GetFontIfExists();
151   if (font) {
152     pStyle->SetColor(font->GetColor());
153     pStyle->SetFontStyle(font->IsItalic() ? CFX_CSSFontStyle::Italic
154                                           : CFX_CSSFontStyle::Normal);
155     pStyle->SetFontWeight(font->IsBold() ? FXFONT_FW_BOLD : FXFONT_FW_NORMAL);
156     pStyle->SetNumberVerticalAlign(-font->GetBaselineShift());
157     fFontSize = font->GetFontSize();
158     CFX_CSSLength letterSpacing;
159     letterSpacing.Set(CFX_CSSLengthUnit::Point, font->GetLetterSpacing());
160     pStyle->SetLetterSpacing(letterSpacing);
161     Mask<CFX_CSSTEXTDECORATION> dwDecoration;
162     if (font->GetLineThrough() > 0)
163       dwDecoration |= CFX_CSSTEXTDECORATION::kLineThrough;
164     if (font->GetUnderline() > 1)
165       dwDecoration |= CFX_CSSTEXTDECORATION::kDouble;
166     else if (font->GetUnderline() > 0)
167       dwDecoration |= CFX_CSSTEXTDECORATION::kUnderline;
168 
169     pStyle->SetTextDecoration(dwDecoration);
170   }
171   pStyle->SetLineHeight(fLineHeight);
172   pStyle->SetFontSize(fFontSize);
173   return pStyle;
174 }
175 
CreateStyle(const CFX_CSSComputedStyle * pParentStyle)176 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateStyle(
177     const CFX_CSSComputedStyle* pParentStyle) {
178   auto pNewStyle = m_pSelector->CreateComputedStyle(pParentStyle);
179   DCHECK(pNewStyle);
180   if (!pParentStyle)
181     return pNewStyle;
182 
183   Mask<CFX_CSSTEXTDECORATION> dwDecoration = pParentStyle->GetTextDecoration();
184   float fBaseLine = 0;
185   if (pParentStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number)
186     fBaseLine = pParentStyle->GetNumberVerticalAlign();
187 
188   pNewStyle->SetTextDecoration(dwDecoration);
189   pNewStyle->SetNumberVerticalAlign(fBaseLine);
190 
191   const CFX_CSSRect* pRect = pParentStyle->GetMarginWidth();
192   if (pRect)
193     pNewStyle->SetMarginWidth(*pRect);
194   return pNewStyle;
195 }
196 
ComputeStyle(const CFX_XMLNode * pXMLNode,RetainPtr<const CFX_CSSComputedStyle> pParentStyle)197 RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::ComputeStyle(
198     const CFX_XMLNode* pXMLNode,
199     RetainPtr<const CFX_CSSComputedStyle> pParentStyle) {
200   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
201   if (it == m_mapXMLNodeToParseContext.end())
202     return nullptr;
203 
204   Context* pContext = it->second.get();
205   if (!pContext)
206     return nullptr;
207 
208   pContext->SetParentStyle(pParentStyle);
209 
210   auto tagProvider = ParseTagInfo(pXMLNode);
211   if (tagProvider->m_bContent)
212     return nullptr;
213 
214   auto pStyle = CreateStyle(pParentStyle);
215   m_pSelector->ComputeStyle(pContext->GetDecls(),
216                             tagProvider->GetAttribute(L"style"),
217                             tagProvider->GetAttribute(L"align"), pStyle.Get());
218   return pStyle;
219 }
220 
DoParse(const CFX_XMLNode * pXMLContainer,CXFA_TextProvider * pTextProvider)221 void CXFA_TextParser::DoParse(const CFX_XMLNode* pXMLContainer,
222                               CXFA_TextProvider* pTextProvider) {
223   if (!pXMLContainer || !pTextProvider || m_bParsed)
224     return;
225 
226   m_bParsed = true;
227   InitCSSData(pTextProvider);
228   auto pRootStyle = CreateRootStyle(pTextProvider);
229   ParseRichText(pXMLContainer, pRootStyle.Get());
230 }
231 
ParseRichText(const CFX_XMLNode * pXMLNode,const CFX_CSSComputedStyle * pParentStyle)232 void CXFA_TextParser::ParseRichText(const CFX_XMLNode* pXMLNode,
233                                     const CFX_CSSComputedStyle* pParentStyle) {
234   if (!pXMLNode)
235     return;
236 
237   auto tagProvider = ParseTagInfo(pXMLNode);
238   if (!tagProvider->m_bTagAvailable)
239     return;
240 
241   RetainPtr<CFX_CSSComputedStyle> pNewStyle;
242   if (!(tagProvider->GetTagName().EqualsASCII("body") &&
243         tagProvider->GetTagName().EqualsASCII("html"))) {
244     auto pTextContext = std::make_unique<Context>();
245     CFX_CSSDisplay eDisplay = CFX_CSSDisplay::Inline;
246     if (!tagProvider->m_bContent) {
247       auto declArray =
248           m_pSelector->MatchDeclarations(tagProvider->GetTagName());
249       pNewStyle = CreateStyle(pParentStyle);
250       m_pSelector->ComputeStyle(declArray, tagProvider->GetAttribute(L"style"),
251                                 tagProvider->GetAttribute(L"align"),
252                                 pNewStyle.Get());
253 
254       if (!declArray.empty())
255         pTextContext->SetDecls(std::move(declArray));
256 
257       eDisplay = pNewStyle->GetDisplay();
258     }
259     pTextContext->SetDisplay(eDisplay);
260     m_mapXMLNodeToParseContext[pXMLNode] = std::move(pTextContext);
261   }
262 
263   for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
264        pXMLChild = pXMLChild->GetNextSibling()) {
265     ParseRichText(pXMLChild, pNewStyle.Get());
266   }
267 }
268 
TagValidate(const WideString & wsName) const269 bool CXFA_TextParser::TagValidate(const WideString& wsName) const {
270   static const uint32_t s_XFATagName[] = {
271       0x61,        // a
272       0x62,        // b
273       0x69,        // i
274       0x70,        // p
275       0x0001f714,  // br
276       0x00022a55,  // li
277       0x000239bb,  // ol
278       0x00025881,  // ul
279       0x0bd37faa,  // sub
280       0x0bd37fb8,  // sup
281       0xa73e3af2,  // span
282       0xb182eaae,  // body
283       0xdb8ac455,  // html
284   };
285   return std::binary_search(std::begin(s_XFATagName), std::end(s_XFATagName),
286                             FX_HashCode_GetLoweredW(wsName.AsStringView()));
287 }
288 
289 // static
ParseTagInfo(const CFX_XMLNode * pXMLNode)290 std::unique_ptr<CXFA_TextParser::TagProvider> CXFA_TextParser::ParseTagInfo(
291     const CFX_XMLNode* pXMLNode) {
292   auto tagProvider = std::make_unique<TagProvider>();
293   const CFX_XMLElement* pXMLElement = ToXMLElement(pXMLNode);
294   if (pXMLElement) {
295     WideString wsName = pXMLElement->GetLocalTagName();
296     tagProvider->SetTagName(wsName);
297     tagProvider->m_bTagAvailable = TagValidate(wsName);
298     WideString wsValue = pXMLElement->GetAttribute(L"style");
299     if (!wsValue.IsEmpty())
300       tagProvider->SetAttribute(L"style", wsValue);
301 
302     return tagProvider;
303   }
304   if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
305     tagProvider->m_bTagAvailable = true;
306     tagProvider->m_bContent = true;
307   }
308   return tagProvider;
309 }
310 
GetVAlign(CXFA_TextProvider * pTextProvider) const311 XFA_AttributeValue CXFA_TextParser::GetVAlign(
312     CXFA_TextProvider* pTextProvider) const {
313   CXFA_Para* para = pTextProvider->GetParaIfExists();
314   return para ? para->GetVerticalAlign() : XFA_AttributeValue::Top;
315 }
316 
GetTabInterval(const CFX_CSSComputedStyle * pStyle) const317 float CXFA_TextParser::GetTabInterval(
318     const CFX_CSSComputedStyle* pStyle) const {
319   WideString wsValue;
320   if (pStyle && pStyle->GetCustomStyle(L"tab-interval", &wsValue))
321     return CXFA_Measurement(wsValue.AsStringView()).ToUnit(XFA_Unit::Pt);
322   return 36;
323 }
324 
CountTabs(const CFX_CSSComputedStyle * pStyle) const325 int32_t CXFA_TextParser::CountTabs(const CFX_CSSComputedStyle* pStyle) const {
326   WideString wsValue;
327   if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", &wsValue))
328     return wsValue.GetInteger();
329   return 0;
330 }
331 
IsSpaceRun(const CFX_CSSComputedStyle * pStyle) const332 bool CXFA_TextParser::IsSpaceRun(const CFX_CSSComputedStyle* pStyle) const {
333   WideString wsValue;
334   return pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", &wsValue) &&
335          wsValue.EqualsASCIINoCase("yes");
336 }
337 
GetFont(CXFA_FFDoc * doc,CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const338 RetainPtr<CFGAS_GEFont> CXFA_TextParser::GetFont(
339     CXFA_FFDoc* doc,
340     CXFA_TextProvider* pTextProvider,
341     const CFX_CSSComputedStyle* pStyle) const {
342   WideString wsFamily = L"Courier";
343   uint32_t dwStyle = 0;
344   CXFA_Font* font = pTextProvider->GetFontIfExists();
345   if (font) {
346     wsFamily = font->GetTypeface();
347     if (font->IsBold())
348       dwStyle |= FXFONT_FORCE_BOLD;
349     if (font->IsItalic())
350       dwStyle |= FXFONT_FORCE_BOLD;
351   }
352 
353   if (pStyle) {
354     absl::optional<WideString> last_family = pStyle->GetLastFontFamily();
355     if (last_family.has_value())
356       wsFamily = last_family.value();
357 
358     dwStyle = 0;
359     if (pStyle->GetFontWeight() > FXFONT_FW_NORMAL)
360       dwStyle |= FXFONT_FORCE_BOLD;
361     if (pStyle->GetFontStyle() == CFX_CSSFontStyle::Italic)
362       dwStyle |= FXFONT_ITALIC;
363   }
364 
365   CXFA_FontMgr* pFontMgr = doc->GetApp()->GetXFAFontMgr();
366   return pFontMgr->GetFont(doc, std::move(wsFamily), dwStyle);
367 }
368 
GetFontSize(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const369 float CXFA_TextParser::GetFontSize(CXFA_TextProvider* pTextProvider,
370                                    const CFX_CSSComputedStyle* pStyle) const {
371   if (pStyle)
372     return pStyle->GetFontSize();
373 
374   CXFA_Font* font = pTextProvider->GetFontIfExists();
375   return font ? font->GetFontSize() : 10;
376 }
377 
GetHorScale(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle,const CFX_XMLNode * pXMLNode) const378 int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider,
379                                      const CFX_CSSComputedStyle* pStyle,
380                                      const CFX_XMLNode* pXMLNode) const {
381   if (pStyle) {
382     WideString wsValue;
383     if (pStyle->GetCustomStyle(L"xfa-font-horizontal-scale", &wsValue))
384       return wsValue.GetInteger();
385 
386     while (pXMLNode) {
387       auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
388       if (it != m_mapXMLNodeToParseContext.end()) {
389         Context* pContext = it->second.get();
390         if (pContext && pContext->GetParentStyle() &&
391             pContext->GetParentStyle()->GetCustomStyle(
392                 L"xfa-font-horizontal-scale", &wsValue)) {
393           return wsValue.GetInteger();
394         }
395       }
396       pXMLNode = pXMLNode->GetParent();
397     }
398   }
399 
400   CXFA_Font* font = pTextProvider->GetFontIfExists();
401   return font ? static_cast<int32_t>(font->GetHorizontalScale()) : 100;
402 }
403 
GetVerScale(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const404 int32_t CXFA_TextParser::GetVerScale(CXFA_TextProvider* pTextProvider,
405                                      const CFX_CSSComputedStyle* pStyle) const {
406   if (pStyle) {
407     WideString wsValue;
408     if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", &wsValue))
409       return wsValue.GetInteger();
410   }
411 
412   CXFA_Font* font = pTextProvider->GetFontIfExists();
413   return font ? static_cast<int32_t>(font->GetVerticalScale()) : 100;
414 }
415 
GetUnderline(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const416 int32_t CXFA_TextParser::GetUnderline(
417     CXFA_TextProvider* pTextProvider,
418     const CFX_CSSComputedStyle* pStyle) const {
419   CXFA_Font* font = pTextProvider->GetFontIfExists();
420   if (!pStyle)
421     return font ? font->GetUnderline() : 0;
422 
423   const Mask<CFX_CSSTEXTDECORATION> dwDecoration = pStyle->GetTextDecoration();
424   if (dwDecoration & CFX_CSSTEXTDECORATION::kDouble)
425     return 2;
426   if (dwDecoration & CFX_CSSTEXTDECORATION::kUnderline)
427     return 1;
428   return 0;
429 }
430 
GetUnderlinePeriod(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const431 XFA_AttributeValue CXFA_TextParser::GetUnderlinePeriod(
432     CXFA_TextProvider* pTextProvider,
433     const CFX_CSSComputedStyle* pStyle) const {
434   WideString wsValue;
435   if (pStyle && pStyle->GetCustomStyle(L"underlinePeriod", &wsValue)) {
436     return wsValue.EqualsASCII("word") ? XFA_AttributeValue::Word
437                                        : XFA_AttributeValue::All;
438   }
439   CXFA_Font* font = pTextProvider->GetFontIfExists();
440   return font ? font->GetUnderlinePeriod() : XFA_AttributeValue::All;
441 }
442 
GetLinethrough(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const443 int32_t CXFA_TextParser::GetLinethrough(
444     CXFA_TextProvider* pTextProvider,
445     const CFX_CSSComputedStyle* pStyle) const {
446   if (pStyle) {
447     const Mask<CFX_CSSTEXTDECORATION> dwDecoration =
448         pStyle->GetTextDecoration();
449     return (dwDecoration & CFX_CSSTEXTDECORATION::kLineThrough) ? 1 : 0;
450   }
451   CXFA_Font* font = pTextProvider->GetFontIfExists();
452   return font ? font->GetLineThrough() : 0;
453 }
454 
GetColor(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const455 FX_ARGB CXFA_TextParser::GetColor(CXFA_TextProvider* pTextProvider,
456                                   const CFX_CSSComputedStyle* pStyle) const {
457   if (pStyle)
458     return pStyle->GetColor();
459 
460   CXFA_Font* font = pTextProvider->GetFontIfExists();
461   return font ? font->GetColor() : 0xFF000000;
462 }
463 
GetBaseline(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle) const464 float CXFA_TextParser::GetBaseline(CXFA_TextProvider* pTextProvider,
465                                    const CFX_CSSComputedStyle* pStyle) const {
466   if (pStyle) {
467     if (pStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number)
468       return pStyle->GetNumberVerticalAlign();
469   } else {
470     CXFA_Font* font = pTextProvider->GetFontIfExists();
471     if (font)
472       return font->GetBaselineShift();
473   }
474   return 0;
475 }
476 
GetLineHeight(CXFA_TextProvider * pTextProvider,const CFX_CSSComputedStyle * pStyle,bool bFirst,float fVerScale) const477 float CXFA_TextParser::GetLineHeight(CXFA_TextProvider* pTextProvider,
478                                      const CFX_CSSComputedStyle* pStyle,
479                                      bool bFirst,
480                                      float fVerScale) const {
481   float fLineHeight = 0;
482   if (pStyle) {
483     fLineHeight = pStyle->GetLineHeight();
484   } else {
485     CXFA_Para* para = pTextProvider->GetParaIfExists();
486     if (para)
487       fLineHeight = para->GetLineHeight();
488   }
489 
490   if (bFirst) {
491     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 
GetEmbeddedObj(const CXFA_TextProvider * pTextProvider,const CFX_XMLNode * pXMLNode)503 absl::optional<WideString> CXFA_TextParser::GetEmbeddedObj(
504     const CXFA_TextProvider* pTextProvider,
505     const CFX_XMLNode* pXMLNode) {
506   if (!pXMLNode)
507     return absl::nullopt;
508 
509   const CFX_XMLElement* pElement = ToXMLElement(pXMLNode);
510   if (!pElement)
511     return absl::nullopt;
512 
513   WideString wsAttr = pElement->GetAttribute(L"xfa:embed");
514   if (wsAttr.IsEmpty())
515     return absl::nullopt;
516 
517   if (wsAttr[0] == L'#')
518     wsAttr.Delete(0);
519 
520   WideString ws =
521       GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedType", L"som");
522   if (!ws.EqualsASCII("uri"))
523     return absl::nullopt;
524 
525   ws = GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedMode",
526                                              L"formatted");
527   if (!(ws.EqualsASCII("raw") || ws.EqualsASCII("formatted")))
528     return absl::nullopt;
529 
530   return pTextProvider->GetEmbeddedObj(wsAttr);
531 }
532 
GetParseContextFromMap(const CFX_XMLNode * pXMLNode)533 CXFA_TextParser::Context* CXFA_TextParser::GetParseContextFromMap(
534     const CFX_XMLNode* pXMLNode) {
535   auto it = m_mapXMLNodeToParseContext.find(pXMLNode);
536   return it != m_mapXMLNodeToParseContext.end() ? it->second.get() : nullptr;
537 }
538 
GetTabstops(const CFX_CSSComputedStyle * pStyle,CXFA_TextTabstopsContext * pTabstopContext)539 bool CXFA_TextParser::GetTabstops(const CFX_CSSComputedStyle* pStyle,
540                                   CXFA_TextTabstopsContext* pTabstopContext) {
541   if (!pStyle || !pTabstopContext)
542     return false;
543 
544   WideString wsValue;
545   if (!pStyle->GetCustomStyle(L"xfa-tab-stops", &wsValue) &&
546       !pStyle->GetCustomStyle(L"tab-stops", &wsValue)) {
547     return false;
548   }
549 
550   pdfium::span<const wchar_t> spTabStops = wsValue.span();
551   size_t iCur = 0;
552   size_t iLast = 0;
553   WideString wsAlign;
554   TabStopStatus eStatus = TabStopStatus::None;
555   while (iCur < spTabStops.size()) {
556     wchar_t ch = spTabStops[iCur];
557     switch (eStatus) {
558       case TabStopStatus::None:
559         if (ch <= ' ') {
560           iCur++;
561         } else {
562           eStatus = TabStopStatus::Alignment;
563           iLast = iCur;
564         }
565         break;
566       case TabStopStatus::Alignment:
567         if (ch == ' ') {
568           wsAlign = WideStringView(spTabStops.subspan(iLast, iCur - iLast));
569           eStatus = TabStopStatus::StartLeader;
570           iCur++;
571           while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ')
572             iCur++;
573           iLast = iCur;
574         } else {
575           iCur++;
576         }
577         break;
578       case TabStopStatus::StartLeader:
579         if (ch != 'l') {
580           eStatus = TabStopStatus::Location;
581         } else {
582           int32_t iCount = 0;
583           while (iCur < spTabStops.size()) {
584             ch = spTabStops[iCur];
585             iCur++;
586             if (ch == '(') {
587               iCount++;
588             } else if (ch == ')') {
589               iCount--;
590               if (iCount == 0)
591                 break;
592             }
593           }
594           while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ')
595             iCur++;
596 
597           iLast = iCur;
598           eStatus = TabStopStatus::Location;
599         }
600         break;
601       case TabStopStatus::Location:
602         if (ch == ' ') {
603           uint32_t dwHashCode = FX_HashCode_GetLoweredW(wsAlign.AsStringView());
604           CXFA_Measurement ms(
605               WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
606           float fPos = ms.ToUnit(XFA_Unit::Pt);
607           pTabstopContext->Append(dwHashCode, fPos);
608           wsAlign.clear();
609           eStatus = TabStopStatus::None;
610         }
611         iCur++;
612         break;
613       default:
614         break;
615     }
616   }
617 
618   if (!wsAlign.IsEmpty()) {
619     uint32_t dwHashCode = FX_HashCode_GetLoweredW(wsAlign.AsStringView());
620     CXFA_Measurement ms(
621         WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
622     float fPos = ms.ToUnit(XFA_Unit::Pt);
623     pTabstopContext->Append(dwHashCode, fPos);
624   }
625   return true;
626 }
627 
628 CXFA_TextParser::TagProvider::TagProvider() = default;
629 
630 CXFA_TextParser::TagProvider::~TagProvider() = default;
631 
632 CXFA_TextParser::Context::Context() = default;
633 
634 CXFA_TextParser::Context::~Context() = default;
635 
SetParentStyle(RetainPtr<const CFX_CSSComputedStyle> style)636 void CXFA_TextParser::Context::SetParentStyle(
637     RetainPtr<const CFX_CSSComputedStyle> style) {
638   m_pParentStyle = std::move(style);
639 }
640 
SetDecls(std::vector<const CFX_CSSDeclaration * > && decl)641 void CXFA_TextParser::Context::SetDecls(
642     std::vector<const CFX_CSSDeclaration*>&& decl) {
643   decls_ = std::move(decl);
644 }
645