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