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