// Copyright 2017 The PDFium Authors
// 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/cxfa_textparser.h"

#include <algorithm>
#include <utility>

#include "core/fxcrt/check.h"
#include "core/fxcrt/css/cfx_css.h"
#include "core/fxcrt/css/cfx_csscomputedstyle.h"
#include "core/fxcrt/css/cfx_cssdeclaration.h"
#include "core/fxcrt/css/cfx_cssstyleselector.h"
#include "core/fxcrt/css/cfx_cssstylesheet.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/notreached.h"
#include "core/fxcrt/xml/cfx_xmlelement.h"
#include "core/fxcrt/xml/cfx_xmlnode.h"
#include "core/fxge/fx_font.h"
#include "xfa/fgas/font/cfgas_fontmgr.h"
#include "xfa/fgas/font/cfgas_gefont.h"
#include "xfa/fxfa/cxfa_ffapp.h"
#include "xfa/fxfa/cxfa_ffdoc.h"
#include "xfa/fxfa/cxfa_fontmgr.h"
#include "xfa/fxfa/cxfa_textprovider.h"
#include "xfa/fxfa/cxfa_texttabstopscontext.h"
#include "xfa/fxfa/parser/cxfa_font.h"
#include "xfa/fxfa/parser/cxfa_measurement.h"
#include "xfa/fxfa/parser/cxfa_para.h"

namespace {

enum class TabStopStatus {
  Error,
  EOS,
  None,
  Alignment,
  StartLeader,
  Leader,
  Location,
};

WideString GetLowerCaseElementAttributeOrDefault(
    const CFX_XMLElement* pElement,
    const WideString& wsName,
    const WideString& wsDefaultValue) {
  WideString ws = pElement->GetAttribute(wsName);
  if (ws.IsEmpty()) {
    ws = wsDefaultValue;
  } else {
    ws.MakeLower();
  }
  return ws;
}

}  // namespace

CXFA_TextParser::CXFA_TextParser() = default;

CXFA_TextParser::~CXFA_TextParser() = default;

void CXFA_TextParser::Reset() {
  map_xmlnode_to_parse_context_.clear();
  parsed_ = false;
}

void CXFA_TextParser::InitCSSData(CXFA_TextProvider* pTextProvider) {
  if (!pTextProvider) {
    return;
  }

  if (!selector_) {
    selector_ = std::make_unique<CFX_CSSStyleSelector>();

    CXFA_Font* font = pTextProvider->GetFontIfExists();
    selector_->SetDefaultFontSize(font ? font->GetFontSize() : 10.0f);
  }

  if (css_initialized_) {
    return;
  }

  css_initialized_ = true;
  auto uaSheet = LoadDefaultSheetStyle();
  selector_->SetUAStyleSheet(std::move(uaSheet));
  selector_->UpdateStyleIndex();
}

std::unique_ptr<CFX_CSSStyleSheet> CXFA_TextParser::LoadDefaultSheetStyle() {
  static const char kStyle[] =
      "html,body,ol,p,ul{display:block}"
      "li{display:list-item}"
      "ol,ul{padding-left:33px;margin:1.12em 0}"
      "ol{list-style-type:decimal}"
      "a{color:#0000ff;text-decoration:underline}"
      "b{font-weight:bolder}"
      "i{font-style:italic}"
      "sup{vertical-align:+15em;font-size:.66em}"
      "sub{vertical-align:-15em;font-size:.66em}";
  WideString ws = WideString::FromASCII(kStyle);
  auto sheet = std::make_unique<CFX_CSSStyleSheet>();
  if (!sheet->LoadBuffer(ws.AsStringView())) {
    return nullptr;
  }

  return sheet;
}

RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateRootStyle(
    CXFA_TextProvider* pTextProvider) {
  CXFA_Para* para = pTextProvider->GetParaIfExists();
  auto pStyle = selector_->CreateComputedStyle(nullptr);
  float fLineHeight = 0;
  float fFontSize = 10;

  if (para) {
    fLineHeight = para->GetLineHeight();
    CFX_CSSLength indent;
    indent.Set(CFX_CSSLengthUnit::Point, para->GetTextIndent());
    pStyle->SetTextIndent(indent);
    CFX_CSSTextAlign hAlign = CFX_CSSTextAlign::Left;
    switch (para->GetHorizontalAlign()) {
      case XFA_AttributeValue::Center:
        hAlign = CFX_CSSTextAlign::Center;
        break;
      case XFA_AttributeValue::Right:
        hAlign = CFX_CSSTextAlign::Right;
        break;
      case XFA_AttributeValue::Justify:
        hAlign = CFX_CSSTextAlign::Justify;
        break;
      case XFA_AttributeValue::JustifyAll:
        hAlign = CFX_CSSTextAlign::JustifyAll;
        break;
      case XFA_AttributeValue::Left:
      case XFA_AttributeValue::Radix:
        break;
      default:
        NOTREACHED();
    }
    pStyle->SetTextAlign(hAlign);
    CFX_CSSRect rtMarginWidth;
    rtMarginWidth.left.Set(CFX_CSSLengthUnit::Point, para->GetMarginLeft());
    rtMarginWidth.top.Set(CFX_CSSLengthUnit::Point, para->GetSpaceAbove());
    rtMarginWidth.right.Set(CFX_CSSLengthUnit::Point, para->GetMarginRight());
    rtMarginWidth.bottom.Set(CFX_CSSLengthUnit::Point, para->GetSpaceBelow());
    pStyle->SetMarginWidth(rtMarginWidth);
  }

  CXFA_Font* font = pTextProvider->GetFontIfExists();
  if (font) {
    pStyle->SetColor(font->GetColor());
    pStyle->SetFontStyle(font->IsItalic() ? CFX_CSSFontStyle::Italic
                                          : CFX_CSSFontStyle::Normal);
    pStyle->SetFontWeight(font->IsBold() ? pdfium::kFontWeightBold
                                         : pdfium::kFontWeightNormal);
    pStyle->SetNumberVerticalAlign(-font->GetBaselineShift());
    fFontSize = font->GetFontSize();
    CFX_CSSLength letterSpacing;
    letterSpacing.Set(CFX_CSSLengthUnit::Point, font->GetLetterSpacing());
    pStyle->SetLetterSpacing(letterSpacing);
    Mask<CFX_CSSTEXTDECORATION> dwDecoration;
    if (font->GetLineThrough() > 0) {
      dwDecoration |= CFX_CSSTEXTDECORATION::kLineThrough;
    }
    if (font->GetUnderline() > 1) {
      dwDecoration |= CFX_CSSTEXTDECORATION::kDouble;
    } else if (font->GetUnderline() > 0) {
      dwDecoration |= CFX_CSSTEXTDECORATION::kUnderline;
    }

    pStyle->SetTextDecoration(dwDecoration);
  }
  pStyle->SetLineHeight(fLineHeight);
  pStyle->SetFontSize(fFontSize);
  return pStyle;
}

RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::CreateStyle(
    const CFX_CSSComputedStyle* pParentStyle) {
  auto pNewStyle = selector_->CreateComputedStyle(pParentStyle);
  DCHECK(pNewStyle);
  if (!pParentStyle) {
    return pNewStyle;
  }

  Mask<CFX_CSSTEXTDECORATION> dwDecoration = pParentStyle->GetTextDecoration();
  float fBaseLine = 0;
  if (pParentStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number) {
    fBaseLine = pParentStyle->GetNumberVerticalAlign();
  }

  pNewStyle->SetTextDecoration(dwDecoration);
  pNewStyle->SetNumberVerticalAlign(fBaseLine);

  const CFX_CSSRect* pRect = pParentStyle->GetMarginWidth();
  if (pRect) {
    pNewStyle->SetMarginWidth(*pRect);
  }
  return pNewStyle;
}

RetainPtr<CFX_CSSComputedStyle> CXFA_TextParser::ComputeStyle(
    const CFX_XMLNode* pXMLNode,
    RetainPtr<const CFX_CSSComputedStyle> pParentStyle) {
  auto it = map_xmlnode_to_parse_context_.find(pXMLNode);
  if (it == map_xmlnode_to_parse_context_.end()) {
    return nullptr;
  }

  Context* context = it->second.get();
  if (!context) {
    return nullptr;
  }

  context->SetParentStyle(pParentStyle);

  auto tagProvider = ParseTagInfo(pXMLNode);
  if (tagProvider->content_) {
    return nullptr;
  }

  auto pStyle = CreateStyle(pParentStyle);
  selector_->ComputeStyle(context->GetDecls(),
                          tagProvider->GetAttribute(L"style"),
                          tagProvider->GetAttribute(L"align"), pStyle.Get());
  return pStyle;
}

void CXFA_TextParser::DoParse(const CFX_XMLNode* pXMLContainer,
                              CXFA_TextProvider* pTextProvider) {
  if (!pXMLContainer || !pTextProvider || parsed_) {
    return;
  }

  parsed_ = true;
  InitCSSData(pTextProvider);
  auto pRootStyle = CreateRootStyle(pTextProvider);
  ParseRichText(pXMLContainer, pRootStyle.Get());
}

void CXFA_TextParser::ParseRichText(const CFX_XMLNode* pXMLNode,
                                    const CFX_CSSComputedStyle* pParentStyle) {
  if (!pXMLNode) {
    return;
  }

  auto tagProvider = ParseTagInfo(pXMLNode);
  if (!tagProvider->tag_available_) {
    return;
  }

  RetainPtr<CFX_CSSComputedStyle> pNewStyle;
  if (!(tagProvider->GetTagName().EqualsASCII("body") &&
        tagProvider->GetTagName().EqualsASCII("html"))) {
    auto pTextContext = std::make_unique<Context>();
    CFX_CSSDisplay eDisplay = CFX_CSSDisplay::Inline;
    if (!tagProvider->content_) {
      auto declArray = selector_->MatchDeclarations(tagProvider->GetTagName());
      pNewStyle = CreateStyle(pParentStyle);
      selector_->ComputeStyle(declArray, tagProvider->GetAttribute(L"style"),
                              tagProvider->GetAttribute(L"align"),
                              pNewStyle.Get());

      if (!declArray.empty()) {
        pTextContext->SetDecls(std::move(declArray));
      }

      eDisplay = pNewStyle->GetDisplay();
    }
    pTextContext->SetDisplay(eDisplay);
    map_xmlnode_to_parse_context_[pXMLNode] = std::move(pTextContext);
  }

  for (CFX_XMLNode* pXMLChild = pXMLNode->GetFirstChild(); pXMLChild;
       pXMLChild = pXMLChild->GetNextSibling()) {
    ParseRichText(pXMLChild, pNewStyle.Get());
  }
}

bool CXFA_TextParser::TagValidate(const 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
  };
  return std::binary_search(std::begin(s_XFATagName), std::end(s_XFATagName),
                            FX_HashCode_GetLoweredW(wsName.AsStringView()));
}

// static
std::unique_ptr<CXFA_TextParser::TagProvider> CXFA_TextParser::ParseTagInfo(
    const CFX_XMLNode* pXMLNode) {
  auto tagProvider = std::make_unique<TagProvider>();
  const CFX_XMLElement* pXMLElement = ToXMLElement(pXMLNode);
  if (pXMLElement) {
    WideString wsName = pXMLElement->GetLocalTagName();
    tagProvider->SetTagName(wsName);
    tagProvider->tag_available_ = TagValidate(wsName);
    WideString wsValue = pXMLElement->GetAttribute(L"style");
    if (!wsValue.IsEmpty()) {
      tagProvider->SetAttribute(L"style", wsValue);
    }

    return tagProvider;
  }
  if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
    tagProvider->tag_available_ = true;
    tagProvider->content_ = true;
  }
  return tagProvider;
}

XFA_AttributeValue CXFA_TextParser::GetVAlign(
    CXFA_TextProvider* pTextProvider) const {
  CXFA_Para* para = pTextProvider->GetParaIfExists();
  return para ? para->GetVerticalAlign() : XFA_AttributeValue::Top;
}

float CXFA_TextParser::GetTabInterval(
    const CFX_CSSComputedStyle* pStyle) const {
  WideString wsValue;
  if (pStyle && pStyle->GetCustomStyle(L"tab-interval", &wsValue)) {
    return CXFA_Measurement(wsValue.AsStringView()).ToUnit(XFA_Unit::Pt);
  }
  return 36;
}

int32_t CXFA_TextParser::CountTabs(const CFX_CSSComputedStyle* pStyle) const {
  WideString wsValue;
  if (pStyle && pStyle->GetCustomStyle(L"xfa-tab-count", &wsValue)) {
    return wsValue.GetInteger();
  }
  return 0;
}

bool CXFA_TextParser::IsSpaceRun(const CFX_CSSComputedStyle* pStyle) const {
  WideString wsValue;
  return pStyle && pStyle->GetCustomStyle(L"xfa-spacerun", &wsValue) &&
         wsValue.EqualsASCIINoCase("yes");
}

RetainPtr<CFGAS_GEFont> CXFA_TextParser::GetFont(
    CXFA_FFDoc* doc,
    CXFA_TextProvider* pTextProvider,
    const CFX_CSSComputedStyle* pStyle) const {
  WideString wsFamily = L"Courier";
  uint32_t dwStyle = 0;
  CXFA_Font* font = pTextProvider->GetFontIfExists();
  if (font) {
    wsFamily = font->GetTypeface();
    if (font->IsBold()) {
      dwStyle |= pdfium::kFontStyleForceBold;
    }
    if (font->IsItalic()) {
      dwStyle |= pdfium::kFontStyleItalic;
    }
  }

  if (pStyle) {
    std::optional<WideString> last_family = pStyle->GetLastFontFamily();
    if (last_family.has_value()) {
      wsFamily = last_family.value();
    }

    dwStyle = 0;
    if (pStyle->GetFontWeight() > pdfium::kFontWeightNormal) {
      dwStyle |= pdfium::kFontStyleForceBold;
    }
    if (pStyle->GetFontStyle() == CFX_CSSFontStyle::Italic) {
      dwStyle |= pdfium::kFontStyleItalic;
    }
  }

  CXFA_FontMgr* font_mgr = doc->GetApp()->GetXFAFontMgr();
  return font_mgr->GetFont(doc, wsFamily, dwStyle);
}

float CXFA_TextParser::GetFontSize(CXFA_TextProvider* pTextProvider,
                                   const CFX_CSSComputedStyle* pStyle) const {
  if (pStyle) {
    return pStyle->GetFontSize();
  }

  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? font->GetFontSize() : 10;
}

int32_t CXFA_TextParser::GetHorScale(CXFA_TextProvider* pTextProvider,
                                     const CFX_CSSComputedStyle* pStyle,
                                     const CFX_XMLNode* pXMLNode) const {
  if (pStyle) {
    WideString wsValue;
    if (pStyle->GetCustomStyle(L"xfa-font-horizontal-scale", &wsValue)) {
      return wsValue.GetInteger();
    }

    while (pXMLNode) {
      auto it = map_xmlnode_to_parse_context_.find(pXMLNode);
      if (it != map_xmlnode_to_parse_context_.end()) {
        Context* context = it->second.get();
        if (context && context->GetParentStyle() &&
            context->GetParentStyle()->GetCustomStyle(
                L"xfa-font-horizontal-scale", &wsValue)) {
          return wsValue.GetInteger();
        }
      }
      pXMLNode = pXMLNode->GetParent();
    }
  }

  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? static_cast<int32_t>(font->GetHorizontalScale()) : 100;
}

int32_t CXFA_TextParser::GetVerScale(CXFA_TextProvider* pTextProvider,
                                     const CFX_CSSComputedStyle* pStyle) const {
  if (pStyle) {
    WideString wsValue;
    if (pStyle->GetCustomStyle(L"xfa-font-vertical-scale", &wsValue)) {
      return wsValue.GetInteger();
    }
  }

  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? static_cast<int32_t>(font->GetVerticalScale()) : 100;
}

int32_t CXFA_TextParser::GetUnderline(
    CXFA_TextProvider* pTextProvider,
    const CFX_CSSComputedStyle* pStyle) const {
  CXFA_Font* font = pTextProvider->GetFontIfExists();
  if (!pStyle) {
    return font ? font->GetUnderline() : 0;
  }

  const Mask<CFX_CSSTEXTDECORATION> dwDecoration = pStyle->GetTextDecoration();
  if (dwDecoration & CFX_CSSTEXTDECORATION::kDouble) {
    return 2;
  }
  if (dwDecoration & CFX_CSSTEXTDECORATION::kUnderline) {
    return 1;
  }
  return 0;
}

XFA_AttributeValue CXFA_TextParser::GetUnderlinePeriod(
    CXFA_TextProvider* pTextProvider,
    const CFX_CSSComputedStyle* pStyle) const {
  WideString wsValue;
  if (pStyle && pStyle->GetCustomStyle(L"underlinePeriod", &wsValue)) {
    return wsValue.EqualsASCII("word") ? XFA_AttributeValue::Word
                                       : XFA_AttributeValue::All;
  }
  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? font->GetUnderlinePeriod() : XFA_AttributeValue::All;
}

int32_t CXFA_TextParser::GetLinethrough(
    CXFA_TextProvider* pTextProvider,
    const CFX_CSSComputedStyle* pStyle) const {
  if (pStyle) {
    const Mask<CFX_CSSTEXTDECORATION> dwDecoration =
        pStyle->GetTextDecoration();
    return (dwDecoration & CFX_CSSTEXTDECORATION::kLineThrough) ? 1 : 0;
  }
  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? font->GetLineThrough() : 0;
}

FX_ARGB CXFA_TextParser::GetColor(CXFA_TextProvider* pTextProvider,
                                  const CFX_CSSComputedStyle* pStyle) const {
  if (pStyle) {
    return pStyle->GetColor();
  }

  CXFA_Font* font = pTextProvider->GetFontIfExists();
  return font ? font->GetColor() : 0xFF000000;
}

float CXFA_TextParser::GetBaseline(CXFA_TextProvider* pTextProvider,
                                   const CFX_CSSComputedStyle* pStyle) const {
  if (pStyle) {
    if (pStyle->GetVerticalAlign() == CFX_CSSVerticalAlign::Number) {
      return pStyle->GetNumberVerticalAlign();
    }
  } else {
    CXFA_Font* font = pTextProvider->GetFontIfExists();
    if (font) {
      return font->GetBaselineShift();
    }
  }
  return 0;
}

float CXFA_TextParser::GetLineHeight(CXFA_TextProvider* pTextProvider,
                                     const CFX_CSSComputedStyle* pStyle,
                                     bool bFirst,
                                     float fVerScale) const {
  float fLineHeight = 0;
  if (pStyle) {
    fLineHeight = pStyle->GetLineHeight();
  } else {
    CXFA_Para* para = pTextProvider->GetParaIfExists();
    if (para) {
      fLineHeight = para->GetLineHeight();
    }
  }

  if (bFirst) {
    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;
}

std::optional<WideString> CXFA_TextParser::GetEmbeddedObj(
    const CXFA_TextProvider* pTextProvider,
    const CFX_XMLNode* pXMLNode) {
  if (!pXMLNode) {
    return std::nullopt;
  }

  const CFX_XMLElement* pElement = ToXMLElement(pXMLNode);
  if (!pElement) {
    return std::nullopt;
  }

  WideString embed_attr = pElement->GetAttribute(L"xfa:embed");
  WideStringView embed_attr_view = embed_attr.AsStringView();
  if (embed_attr_view.IsEmpty()) {
    return std::nullopt;
  }
  if (embed_attr_view.Front() == L'#') {
    embed_attr_view = embed_attr_view.Substr(1);
  }

  WideString ws =
      GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedType", L"som");
  if (!ws.EqualsASCII("uri")) {
    return std::nullopt;
  }

  ws = GetLowerCaseElementAttributeOrDefault(pElement, L"xfa:embedMode",
                                             L"formatted");
  if (!(ws.EqualsASCII("raw") || ws.EqualsASCII("formatted"))) {
    return std::nullopt;
  }

  return pTextProvider->GetEmbeddedObj(embed_attr_view);
}

CXFA_TextParser::Context* CXFA_TextParser::GetParseContextFromMap(
    const CFX_XMLNode* pXMLNode) {
  auto it = map_xmlnode_to_parse_context_.find(pXMLNode);
  return it != map_xmlnode_to_parse_context_.end() ? it->second.get() : nullptr;
}

bool CXFA_TextParser::GetTabstops(const CFX_CSSComputedStyle* pStyle,
                                  CXFA_TextTabstopsContext* pTabstopContext) {
  if (!pStyle || !pTabstopContext) {
    return false;
  }

  WideString wsValue;
  if (!pStyle->GetCustomStyle(L"xfa-tab-stops", &wsValue) &&
      !pStyle->GetCustomStyle(L"tab-stops", &wsValue)) {
    return false;
  }

  pdfium::span<const wchar_t> spTabStops = wsValue.span();
  size_t iCur = 0;
  size_t iLast = 0;
  WideString wsAlign;
  TabStopStatus eStatus = TabStopStatus::None;
  while (iCur < spTabStops.size()) {
    wchar_t ch = spTabStops[iCur];
    switch (eStatus) {
      case TabStopStatus::None:
        if (ch <= ' ') {
          iCur++;
        } else {
          eStatus = TabStopStatus::Alignment;
          iLast = iCur;
        }
        break;
      case TabStopStatus::Alignment:
        if (ch == ' ') {
          wsAlign = WideStringView(spTabStops.subspan(iLast, iCur - iLast));
          eStatus = TabStopStatus::StartLeader;
          iCur++;
          while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ') {
            iCur++;
          }
          iLast = iCur;
        } else {
          iCur++;
        }
        break;
      case TabStopStatus::StartLeader:
        if (ch != 'l') {
          eStatus = TabStopStatus::Location;
        } else {
          int32_t iCount = 0;
          while (iCur < spTabStops.size()) {
            ch = spTabStops[iCur];
            iCur++;
            if (ch == '(') {
              iCount++;
            } else if (ch == ')') {
              iCount--;
              if (iCount == 0) {
                break;
              }
            }
          }
          while (iCur < spTabStops.size() && spTabStops[iCur] <= ' ') {
            iCur++;
          }

          iLast = iCur;
          eStatus = TabStopStatus::Location;
        }
        break;
      case TabStopStatus::Location:
        if (ch == ' ') {
          uint32_t dwHashCode = FX_HashCode_GetLoweredW(wsAlign.AsStringView());
          CXFA_Measurement ms(
              WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
          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_GetLoweredW(wsAlign.AsStringView());
    CXFA_Measurement ms(
        WideStringView(spTabStops.subspan(iLast, iCur - iLast)));
    float fPos = ms.ToUnit(XFA_Unit::Pt);
    pTabstopContext->Append(dwHashCode, fPos);
  }
  return true;
}

CXFA_TextParser::TagProvider::TagProvider() = default;

CXFA_TextParser::TagProvider::~TagProvider() = default;

CXFA_TextParser::Context::Context() = default;

CXFA_TextParser::Context::~Context() = default;

void CXFA_TextParser::Context::SetParentStyle(
    RetainPtr<const CFX_CSSComputedStyle> style) {
  parent_style_ = std::move(style);
}

void CXFA_TextParser::Context::SetDecls(
    std::vector<const CFX_CSSDeclaration*>&& decl) {
  decls_ = std::move(decl);
}
