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_textlayout.h" 8 9 #include <algorithm> 10 #include <utility> 11 12 #include "core/fxcrt/css/cfx_csscomputedstyle.h" 13 #include "core/fxcrt/css/cfx_cssstyleselector.h" 14 #include "core/fxcrt/xml/cfx_xmlelement.h" 15 #include "core/fxcrt/xml/cfx_xmlnode.h" 16 #include "core/fxcrt/xml/cfx_xmltext.h" 17 #include "core/fxge/cfx_graphstatedata.h" 18 #include "core/fxge/cfx_pathdata.h" 19 #include "fxjs/xfa/cjx_object.h" 20 #include "third_party/base/ptr_util.h" 21 #include "third_party/base/stl_util.h" 22 #include "xfa/fde/cfde_textout.h" 23 #include "xfa/fgas/font/cfgas_gefont.h" 24 #include "xfa/fxfa/cxfa_linkuserdata.h" 25 #include "xfa/fxfa/cxfa_loadercontext.h" 26 #include "xfa/fxfa/cxfa_pieceline.h" 27 #include "xfa/fxfa/cxfa_textparsecontext.h" 28 #include "xfa/fxfa/cxfa_textpiece.h" 29 #include "xfa/fxfa/cxfa_textprovider.h" 30 #include "xfa/fxfa/cxfa_texttabstopscontext.h" 31 #include "xfa/fxfa/cxfa_textuserdata.h" 32 #include "xfa/fxfa/parser/cxfa_font.h" 33 #include "xfa/fxfa/parser/cxfa_node.h" 34 #include "xfa/fxfa/parser/cxfa_para.h" 35 36 #define XFA_LOADERCNTXTFLG_FILTERSPACE 0x001 37 38 CXFA_TextLayout::CXFA_TextLayout(CXFA_FFDoc* doc, 39 CXFA_TextProvider* pTextProvider) 40 : m_bHasBlock(false), 41 m_pDoc(doc), 42 m_pTextProvider(pTextProvider), 43 m_pTextDataNode(nullptr), 44 m_bRichText(false), 45 m_iLines(0), 46 m_fMaxWidth(0), 47 m_bBlockContinue(true) { 48 ASSERT(m_pTextProvider); 49 } 50 51 CXFA_TextLayout::~CXFA_TextLayout() { 52 m_textParser.Reset(); 53 Unload(); 54 } 55 56 void CXFA_TextLayout::Unload() { 57 m_pieceLines.clear(); 58 m_pBreak.reset(); 59 } 60 61 void CXFA_TextLayout::GetTextDataNode() { 62 if (!m_pTextProvider) 63 return; 64 65 CXFA_Node* pNode = m_pTextProvider->GetTextNode(m_bRichText); 66 if (pNode && m_bRichText) 67 m_textParser.Reset(); 68 69 m_pTextDataNode = pNode; 70 } 71 72 CFX_XMLNode* CXFA_TextLayout::GetXMLContainerNode() { 73 if (!m_bRichText) 74 return nullptr; 75 76 CFX_XMLNode* pXMLRoot = m_pTextDataNode->GetXMLMappingNode(); 77 if (!pXMLRoot) 78 return nullptr; 79 80 CFX_XMLNode* pXMLContainer = nullptr; 81 for (CFX_XMLNode* pXMLChild = pXMLRoot->GetNodeItem(CFX_XMLNode::FirstChild); 82 pXMLChild; 83 pXMLChild = pXMLChild->GetNodeItem(CFX_XMLNode::NextSibling)) { 84 if (pXMLChild->GetType() == FX_XMLNODE_Element) { 85 CFX_XMLElement* pXMLElement = static_cast<CFX_XMLElement*>(pXMLChild); 86 WideString wsTag = pXMLElement->GetLocalTagName(); 87 if (wsTag == L"body" || wsTag == L"html") { 88 pXMLContainer = pXMLChild; 89 break; 90 } 91 } 92 } 93 return pXMLContainer; 94 } 95 96 std::unique_ptr<CFX_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) { 97 uint32_t dwStyle = FX_LAYOUTSTYLE_ExpandTab; 98 if (!bDefault) 99 dwStyle |= FX_LAYOUTSTYLE_Pagination; 100 101 auto pBreak = pdfium::MakeUnique<CFX_RTFBreak>(dwStyle); 102 pBreak->SetLineBreakTolerance(1); 103 pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, nullptr)); 104 pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr)); 105 return pBreak; 106 } 107 108 void CXFA_TextLayout::InitBreak(float fLineWidth) { 109 CXFA_Para* para = m_pTextProvider->GetParaIfExists(); 110 float fStart = 0; 111 float fStartPos = 0; 112 if (para) { 113 CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left; 114 switch (para->GetHorizontalAlign()) { 115 case XFA_AttributeEnum::Center: 116 iAlign = CFX_RTFLineAlignment::Center; 117 break; 118 case XFA_AttributeEnum::Right: 119 iAlign = CFX_RTFLineAlignment::Right; 120 break; 121 case XFA_AttributeEnum::Justify: 122 iAlign = CFX_RTFLineAlignment::Justified; 123 break; 124 case XFA_AttributeEnum::JustifyAll: 125 iAlign = CFX_RTFLineAlignment::Distributed; 126 break; 127 case XFA_AttributeEnum::Left: 128 case XFA_AttributeEnum::Radix: 129 break; 130 default: 131 NOTREACHED(); 132 break; 133 } 134 m_pBreak->SetAlignment(iAlign); 135 136 fStart = para->GetMarginLeft(); 137 if (m_pTextProvider->IsCheckButtonAndAutoWidth()) { 138 if (iAlign != CFX_RTFLineAlignment::Left) 139 fLineWidth -= para->GetMarginRight(); 140 } else { 141 fLineWidth -= para->GetMarginRight(); 142 } 143 if (fLineWidth < 0) 144 fLineWidth = fStart; 145 146 fStartPos = fStart; 147 float fIndent = para->GetTextIndent(); 148 if (fIndent > 0) 149 fStartPos += fIndent; 150 } 151 152 m_pBreak->SetLineBoundary(fStart, fLineWidth); 153 m_pBreak->SetLineStartPos(fStartPos); 154 155 CXFA_Font* font = m_pTextProvider->GetFontIfExists(); 156 if (font) { 157 m_pBreak->SetHorizontalScale( 158 static_cast<int32_t>(font->GetHorizontalScale())); 159 m_pBreak->SetVerticalScale(static_cast<int32_t>(font->GetVerticalScale())); 160 m_pBreak->SetCharSpace(font->GetLetterSpacing()); 161 } 162 163 float fFontSize = m_textParser.GetFontSize(m_pTextProvider, nullptr); 164 m_pBreak->SetFontSize(fFontSize); 165 m_pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, nullptr)); 166 m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f); 167 } 168 169 void CXFA_TextLayout::InitBreak(CFX_CSSComputedStyle* pStyle, 170 CFX_CSSDisplay eDisplay, 171 float fLineWidth, 172 CFX_XMLNode* pXMLNode, 173 CFX_CSSComputedStyle* pParentStyle) { 174 if (!pStyle) { 175 InitBreak(fLineWidth); 176 return; 177 } 178 179 if (eDisplay == CFX_CSSDisplay::Block || 180 eDisplay == CFX_CSSDisplay::ListItem) { 181 CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left; 182 switch (pStyle->GetTextAlign()) { 183 case CFX_CSSTextAlign::Right: 184 iAlign = CFX_RTFLineAlignment::Right; 185 break; 186 case CFX_CSSTextAlign::Center: 187 iAlign = CFX_RTFLineAlignment::Center; 188 break; 189 case CFX_CSSTextAlign::Justify: 190 iAlign = CFX_RTFLineAlignment::Justified; 191 break; 192 case CFX_CSSTextAlign::JustifyAll: 193 iAlign = CFX_RTFLineAlignment::Distributed; 194 break; 195 default: 196 break; 197 } 198 m_pBreak->SetAlignment(iAlign); 199 200 float fStart = 0; 201 const CFX_CSSRect* pRect = pStyle->GetMarginWidth(); 202 const CFX_CSSRect* pPaddingRect = pStyle->GetPaddingWidth(); 203 if (pRect) { 204 fStart = pRect->left.GetValue(); 205 fLineWidth -= pRect->right.GetValue(); 206 if (pPaddingRect) { 207 fStart += pPaddingRect->left.GetValue(); 208 fLineWidth -= pPaddingRect->right.GetValue(); 209 } 210 if (eDisplay == CFX_CSSDisplay::ListItem) { 211 const CFX_CSSRect* pParRect = pParentStyle->GetMarginWidth(); 212 const CFX_CSSRect* pParPaddingRect = pParentStyle->GetPaddingWidth(); 213 if (pParRect) { 214 fStart += pParRect->left.GetValue(); 215 fLineWidth -= pParRect->right.GetValue(); 216 if (pParPaddingRect) { 217 fStart += pParPaddingRect->left.GetValue(); 218 fLineWidth -= pParPaddingRect->right.GetValue(); 219 } 220 } 221 CFX_CSSRect pNewRect; 222 pNewRect.left.Set(CFX_CSSLengthUnit::Point, fStart); 223 pNewRect.right.Set(CFX_CSSLengthUnit::Point, pRect->right.GetValue()); 224 pNewRect.top.Set(CFX_CSSLengthUnit::Point, pRect->top.GetValue()); 225 pNewRect.bottom.Set(CFX_CSSLengthUnit::Point, pRect->bottom.GetValue()); 226 pStyle->SetMarginWidth(pNewRect); 227 } 228 } 229 m_pBreak->SetLineBoundary(fStart, fLineWidth); 230 float fIndent = pStyle->GetTextIndent().GetValue(); 231 if (fIndent > 0) 232 fStart += fIndent; 233 234 m_pBreak->SetLineStartPos(fStart); 235 m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle)); 236 if (!m_pTabstopContext) 237 m_pTabstopContext = pdfium::MakeUnique<CXFA_TextTabstopsContext>(); 238 m_textParser.GetTabstops(pStyle, m_pTabstopContext.get()); 239 for (const auto& stop : m_pTabstopContext->m_tabstops) 240 m_pBreak->AddPositionedTab(stop.fTabstops); 241 } 242 float fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle); 243 m_pBreak->SetFontSize(fFontSize); 244 m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f); 245 m_pBreak->SetFont(m_textParser.GetFont(m_pDoc, m_pTextProvider, pStyle)); 246 m_pBreak->SetHorizontalScale( 247 m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode)); 248 m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle)); 249 m_pBreak->SetCharSpace(pStyle->GetLetterSpacing().GetValue()); 250 } 251 252 float CXFA_TextLayout::GetLayoutHeight() { 253 if (!m_pLoader) 254 return 0; 255 256 if (m_pLoader->m_lineHeights.empty() && m_pLoader->m_fWidth > 0) { 257 CFX_SizeF szMax(m_pLoader->m_fWidth, m_pLoader->m_fHeight); 258 m_pLoader->m_bSaveLineHeight = true; 259 m_pLoader->m_fLastPos = 0; 260 CFX_SizeF szDef = CalcSize(szMax, szMax); 261 m_pLoader->m_bSaveLineHeight = false; 262 return szDef.height; 263 } 264 265 float fHeight = m_pLoader->m_fHeight; 266 if (fHeight < 0.1f) { 267 fHeight = 0; 268 for (float value : m_pLoader->m_lineHeights) 269 fHeight += value; 270 } 271 return fHeight; 272 } 273 274 float CXFA_TextLayout::StartLayout(float fWidth) { 275 if (!m_pLoader) 276 m_pLoader = pdfium::MakeUnique<CXFA_LoaderContext>(); 277 278 if (fWidth < 0 || 279 (m_pLoader->m_fWidth > -1 && fabs(fWidth - m_pLoader->m_fWidth) > 0)) { 280 m_pLoader->m_lineHeights.clear(); 281 m_Blocks.clear(); 282 Unload(); 283 m_pLoader->m_fStartLineOffset = 0; 284 } 285 m_pLoader->m_fWidth = fWidth; 286 287 if (fWidth >= 0) 288 return fWidth; 289 290 CFX_SizeF szMax; 291 292 m_pLoader->m_bSaveLineHeight = true; 293 m_pLoader->m_fLastPos = 0; 294 CFX_SizeF szDef = CalcSize(szMax, szMax); 295 m_pLoader->m_bSaveLineHeight = false; 296 return szDef.width; 297 } 298 299 float CXFA_TextLayout::DoLayout(int32_t iBlockIndex, 300 float fCalcHeight, 301 float fContentAreaHeight, 302 float fTextHeight) { 303 if (!m_pLoader) 304 return fCalcHeight; 305 306 int32_t iBlockCount = pdfium::CollectionSize<int32_t>(m_Blocks); 307 float fHeight = fTextHeight; 308 if (fHeight < 0) 309 fHeight = GetLayoutHeight(); 310 311 m_pLoader->m_fHeight = fHeight; 312 if (fContentAreaHeight < 0) 313 return fCalcHeight; 314 315 m_bHasBlock = true; 316 if (iBlockCount == 0 && fHeight > 0) { 317 fHeight = fTextHeight - GetLayoutHeight(); 318 if (fHeight > 0) { 319 XFA_AttributeEnum iAlign = m_textParser.GetVAlign(m_pTextProvider); 320 if (iAlign == XFA_AttributeEnum::Middle) 321 fHeight /= 2.0f; 322 else if (iAlign != XFA_AttributeEnum::Bottom) 323 fHeight = 0; 324 m_pLoader->m_fStartLineOffset = fHeight; 325 } 326 } 327 328 float fLinePos = m_pLoader->m_fStartLineOffset; 329 int32_t iLineIndex = 0; 330 if (iBlockCount > 1) { 331 if (iBlockCount >= (iBlockIndex + 1) * 2) { 332 iLineIndex = m_Blocks[iBlockIndex * 2]; 333 } else { 334 iLineIndex = m_Blocks[iBlockCount - 1] + m_Blocks[iBlockCount - 2]; 335 } 336 if (!m_pLoader->m_BlocksHeight.empty()) { 337 for (int32_t i = 0; i < iBlockIndex; i++) 338 fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1]; 339 } 340 } 341 342 int32_t iCount = pdfium::CollectionSize<int32_t>(m_pLoader->m_lineHeights); 343 int32_t i = 0; 344 for (i = iLineIndex; i < iCount; i++) { 345 float fLineHeight = m_pLoader->m_lineHeights[i]; 346 if (i == iLineIndex && fLineHeight - fContentAreaHeight > 0.001) 347 return 0; 348 349 if (fLinePos + fLineHeight - fContentAreaHeight > 0.001) { 350 if (iBlockCount >= (iBlockIndex + 1) * 2) { 351 m_Blocks[iBlockIndex * 2] = iLineIndex; 352 m_Blocks[iBlockIndex * 2 + 1] = i - iLineIndex; 353 } else { 354 m_Blocks.push_back(iLineIndex); 355 m_Blocks.push_back(i - iLineIndex); 356 } 357 if (i == iLineIndex) { 358 if (fCalcHeight <= fLinePos) { 359 if (pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight) > 360 iBlockIndex * 2 && 361 (m_pLoader->m_BlocksHeight[iBlockIndex * 2] == iBlockIndex)) { 362 m_pLoader->m_BlocksHeight[iBlockIndex * 2 + 1] = fCalcHeight; 363 } else { 364 m_pLoader->m_BlocksHeight.push_back((float)iBlockIndex); 365 m_pLoader->m_BlocksHeight.push_back(fCalcHeight); 366 } 367 } 368 return fCalcHeight; 369 } 370 return fLinePos; 371 } 372 fLinePos += fLineHeight; 373 } 374 return fCalcHeight; 375 } 376 377 int32_t CXFA_TextLayout::CountBlocks() const { 378 int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks) / 2; 379 return iCount > 0 ? iCount : 1; 380 } 381 382 CFX_SizeF CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize, 383 const CFX_SizeF& maxSize) { 384 float width = maxSize.width; 385 if (width < 1) 386 width = 0xFFFF; 387 388 m_pBreak = CreateBreak(false); 389 float fLinePos = 0; 390 m_iLines = 0; 391 m_fMaxWidth = 0; 392 Loader(width, fLinePos, false); 393 if (fLinePos < 0.1f) 394 fLinePos = m_textParser.GetFontSize(m_pTextProvider, nullptr); 395 396 m_pTabstopContext.reset(); 397 return CFX_SizeF(m_fMaxWidth, fLinePos); 398 } 399 400 float CXFA_TextLayout::Layout(const CFX_SizeF& size) { 401 if (size.width < 1) 402 return 0.f; 403 404 Unload(); 405 m_pBreak = CreateBreak(true); 406 if (m_pLoader) { 407 m_pLoader->m_iTotalLines = -1; 408 m_pLoader->m_iChar = 0; 409 } 410 411 m_iLines = 0; 412 float fLinePos = 0; 413 Loader(size.width, fLinePos, true); 414 UpdateAlign(size.height, fLinePos); 415 m_pTabstopContext.reset(); 416 return fLinePos; 417 } 418 419 bool CXFA_TextLayout::Layout(int32_t iBlock) { 420 if (!m_pLoader || iBlock < 0 || iBlock >= CountBlocks()) 421 return false; 422 if (m_pLoader->m_fWidth < 1) 423 return false; 424 425 m_pLoader->m_iTotalLines = -1; 426 m_iLines = 0; 427 float fLinePos = 0; 428 CXFA_Node* pNode = nullptr; 429 CFX_SizeF szText(m_pLoader->m_fWidth, m_pLoader->m_fHeight); 430 int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks); 431 int32_t iBlocksHeightCount = 432 pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight); 433 iBlocksHeightCount /= 2; 434 if (iBlock < iBlocksHeightCount) 435 return true; 436 if (iBlock == iBlocksHeightCount) { 437 Unload(); 438 m_pBreak = CreateBreak(true); 439 fLinePos = m_pLoader->m_fStartLineOffset; 440 for (int32_t i = 0; i < iBlocksHeightCount; i++) 441 fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1]; 442 443 m_pLoader->m_iChar = 0; 444 if (iCount > 1) 445 m_pLoader->m_iTotalLines = m_Blocks[iBlock * 2 + 1]; 446 447 Loader(szText.width, fLinePos, true); 448 if (iCount == 0 && m_pLoader->m_fStartLineOffset < 0.1f) 449 UpdateAlign(szText.height, fLinePos); 450 } else if (m_pTextDataNode) { 451 iBlock *= 2; 452 if (iBlock < iCount - 2) 453 m_pLoader->m_iTotalLines = m_Blocks[iBlock + 1]; 454 455 m_pBreak->Reset(); 456 if (m_bRichText) { 457 CFX_XMLNode* pContainerNode = GetXMLContainerNode(); 458 if (!pContainerNode) 459 return true; 460 461 CFX_XMLNode* pXMLNode = m_pLoader->m_pXMLNode; 462 if (!pXMLNode) 463 return true; 464 465 CFX_XMLNode* pSaveXMLNode = m_pLoader->m_pXMLNode; 466 for (; pXMLNode; 467 pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) { 468 if (!LoadRichText(pXMLNode, szText.width, fLinePos, 469 m_pLoader->m_pParentStyle, true, nullptr)) { 470 break; 471 } 472 } 473 while (!pXMLNode) { 474 pXMLNode = pSaveXMLNode->GetNodeItem(CFX_XMLNode::Parent); 475 if (pXMLNode == pContainerNode) 476 break; 477 if (!LoadRichText(pXMLNode, szText.width, fLinePos, 478 m_pLoader->m_pParentStyle, true, nullptr, false)) { 479 break; 480 } 481 pSaveXMLNode = pXMLNode; 482 pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling); 483 if (!pXMLNode) 484 continue; 485 for (; pXMLNode; 486 pXMLNode = pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling)) { 487 if (!LoadRichText(pXMLNode, szText.width, fLinePos, 488 m_pLoader->m_pParentStyle, true, nullptr)) { 489 break; 490 } 491 } 492 } 493 } else { 494 pNode = m_pLoader->m_pNode; 495 if (!pNode) 496 return true; 497 LoadText(pNode, szText.width, fLinePos, true); 498 } 499 } 500 if (iBlock == iCount) { 501 m_pTabstopContext.reset(); 502 m_pLoader.reset(); 503 } 504 return true; 505 } 506 507 void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, int32_t iBlockIndex) { 508 if (!m_pLoader) 509 return; 510 511 int32_t iCountHeight = 512 pdfium::CollectionSize<int32_t>(m_pLoader->m_lineHeights); 513 if (iCountHeight == 0) 514 return; 515 516 bool bEndItem = true; 517 int32_t iBlockCount = pdfium::CollectionSize<int32_t>(m_Blocks); 518 float fLinePos = m_pLoader->m_fStartLineOffset; 519 int32_t iLineIndex = 0; 520 if (iBlockIndex > 0) { 521 int32_t iBlockHeightCount = 522 pdfium::CollectionSize<int32_t>(m_pLoader->m_BlocksHeight); 523 iBlockHeightCount /= 2; 524 if (iBlockHeightCount >= iBlockIndex) { 525 for (int32_t i = 0; i < iBlockIndex; i++) 526 fLinePos -= m_pLoader->m_BlocksHeight[i * 2 + 1]; 527 } else { 528 fLinePos = 0; 529 } 530 iLineIndex = m_Blocks[iBlockCount - 1] + m_Blocks[iBlockCount - 2]; 531 } 532 533 int32_t i = 0; 534 for (i = iLineIndex; i < iCountHeight; i++) { 535 float fLineHeight = m_pLoader->m_lineHeights[i]; 536 if (fLinePos + fLineHeight - rtText.height > 0.001) { 537 m_Blocks.push_back(iLineIndex); 538 m_Blocks.push_back(i - iLineIndex); 539 bEndItem = false; 540 break; 541 } 542 fLinePos += fLineHeight; 543 } 544 if (iCountHeight > 0 && (i - iLineIndex) > 0 && bEndItem) { 545 m_Blocks.push_back(iLineIndex); 546 m_Blocks.push_back(i - iLineIndex); 547 } 548 } 549 550 bool CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice, 551 const CFX_Matrix& tmDoc2Device, 552 const CFX_RectF& rtClip, 553 int32_t iBlock) { 554 if (!pFxDevice) 555 return false; 556 557 pFxDevice->SaveState(); 558 pFxDevice->SetClip_Rect(rtClip); 559 560 if (m_pieceLines.empty()) { 561 int32_t iBlockCount = CountBlocks(); 562 for (int32_t i = 0; i < iBlockCount; i++) 563 Layout(i); 564 } 565 566 FXTEXT_CHARPOS* pCharPos = nullptr; 567 int32_t iCharCount = 0; 568 int32_t iLineStart = 0; 569 int32_t iPieceLines = pdfium::CollectionSize<int32_t>(m_pieceLines); 570 int32_t iCount = pdfium::CollectionSize<int32_t>(m_Blocks); 571 if (iCount > 0) { 572 iBlock *= 2; 573 if (iBlock < iCount) { 574 iLineStart = m_Blocks[iBlock]; 575 iPieceLines = m_Blocks[iBlock + 1]; 576 } else { 577 iPieceLines = 0; 578 } 579 } 580 581 for (int32_t i = 0; i < iPieceLines; i++) { 582 if (i + iLineStart >= pdfium::CollectionSize<int32_t>(m_pieceLines)) 583 break; 584 585 CXFA_PieceLine* pPieceLine = m_pieceLines[i + iLineStart].get(); 586 int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces); 587 int32_t j = 0; 588 for (j = 0; j < iPieces; j++) { 589 const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[j].get(); 590 int32_t iChars = pPiece->iChars; 591 if (iCharCount < iChars) { 592 FX_Free(pCharPos); 593 pCharPos = FX_Alloc(FXTEXT_CHARPOS, iChars); 594 iCharCount = iChars; 595 } 596 memset(pCharPos, 0, iCharCount * sizeof(FXTEXT_CHARPOS)); 597 RenderString(pFxDevice, pPieceLine, j, pCharPos, tmDoc2Device); 598 } 599 for (j = 0; j < iPieces; j++) 600 RenderPath(pFxDevice, pPieceLine, j, pCharPos, tmDoc2Device); 601 } 602 pFxDevice->RestoreState(false); 603 FX_Free(pCharPos); 604 return iPieceLines > 0; 605 } 606 607 void CXFA_TextLayout::UpdateAlign(float fHeight, float fBottom) { 608 fHeight -= fBottom; 609 if (fHeight < 0.1f) 610 return; 611 612 switch (m_textParser.GetVAlign(m_pTextProvider)) { 613 case XFA_AttributeEnum::Middle: 614 fHeight /= 2.0f; 615 break; 616 case XFA_AttributeEnum::Bottom: 617 break; 618 default: 619 return; 620 } 621 622 for (const auto& pPieceLine : m_pieceLines) { 623 for (const auto& pPiece : pPieceLine->m_textPieces) 624 pPiece->rtPiece.top += fHeight; 625 } 626 } 627 628 bool CXFA_TextLayout::Loader(float textWidth, 629 float& fLinePos, 630 bool bSavePieces) { 631 GetTextDataNode(); 632 if (!m_pTextDataNode) 633 return true; 634 635 if (m_bRichText) { 636 CFX_XMLNode* pXMLContainer = GetXMLContainerNode(); 637 if (pXMLContainer) { 638 if (!m_textParser.IsParsed()) 639 m_textParser.DoParse(pXMLContainer, m_pTextProvider); 640 641 auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider); 642 LoadRichText(pXMLContainer, textWidth, fLinePos, pRootStyle, bSavePieces, 643 nullptr); 644 } 645 } else { 646 LoadText(m_pTextDataNode, textWidth, fLinePos, bSavePieces); 647 } 648 return true; 649 } 650 651 void CXFA_TextLayout::LoadText(CXFA_Node* pNode, 652 float textWidth, 653 float& fLinePos, 654 bool bSavePieces) { 655 InitBreak(textWidth); 656 657 CXFA_Para* para = m_pTextProvider->GetParaIfExists(); 658 float fSpaceAbove = 0; 659 if (para) { 660 fSpaceAbove = para->GetSpaceAbove(); 661 if (fSpaceAbove < 0.1f) 662 fSpaceAbove = 0; 663 664 switch (para->GetVerticalAlign()) { 665 case XFA_AttributeEnum::Top: 666 case XFA_AttributeEnum::Middle: 667 case XFA_AttributeEnum::Bottom: { 668 fLinePos += fSpaceAbove; 669 break; 670 } 671 default: 672 NOTREACHED(); 673 break; 674 } 675 } 676 677 WideString wsText = pNode->JSObject()->GetContent(false); 678 wsText.TrimRight(L" "); 679 bool bRet = AppendChar(wsText, fLinePos, fSpaceAbove, bSavePieces); 680 if (bRet && m_pLoader) 681 m_pLoader->m_pNode = pNode; 682 else 683 EndBreak(CFX_BreakType::Paragraph, fLinePos, bSavePieces); 684 } 685 686 bool CXFA_TextLayout::LoadRichText( 687 CFX_XMLNode* pXMLNode, 688 float textWidth, 689 float& fLinePos, 690 const RetainPtr<CFX_CSSComputedStyle>& pParentStyle, 691 bool bSavePieces, 692 RetainPtr<CXFA_LinkUserData> pLinkData, 693 bool bEndBreak, 694 bool bIsOl, 695 int32_t iLiCount) { 696 if (!pXMLNode) 697 return false; 698 699 CXFA_TextParseContext* pContext = 700 m_textParser.GetParseContextFromMap(pXMLNode); 701 CFX_CSSDisplay eDisplay = CFX_CSSDisplay::None; 702 bool bContentNode = false; 703 float fSpaceBelow = 0; 704 RetainPtr<CFX_CSSComputedStyle> pStyle; 705 WideString wsName; 706 if (bEndBreak) { 707 bool bCurOl = false; 708 bool bCurLi = false; 709 CFX_XMLElement* pElement = nullptr; 710 if (pContext) { 711 if (m_bBlockContinue || 712 (m_pLoader && pXMLNode == m_pLoader->m_pXMLNode)) { 713 m_bBlockContinue = true; 714 } 715 if (pXMLNode->GetType() == FX_XMLNODE_Text) { 716 bContentNode = true; 717 } else if (pXMLNode->GetType() == FX_XMLNODE_Element) { 718 pElement = static_cast<CFX_XMLElement*>(pXMLNode); 719 wsName = pElement->GetLocalTagName(); 720 } 721 if (wsName == L"ol") { 722 bIsOl = true; 723 bCurOl = true; 724 } 725 if (m_bBlockContinue || bContentNode == false) { 726 eDisplay = pContext->GetDisplay(); 727 if (eDisplay != CFX_CSSDisplay::Block && 728 eDisplay != CFX_CSSDisplay::Inline && 729 eDisplay != CFX_CSSDisplay::ListItem) { 730 return true; 731 } 732 733 pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle.Get()); 734 InitBreak(bContentNode ? pParentStyle.Get() : pStyle.Get(), eDisplay, 735 textWidth, pXMLNode, pParentStyle.Get()); 736 if ((eDisplay == CFX_CSSDisplay::Block || 737 eDisplay == CFX_CSSDisplay::ListItem) && 738 pStyle && 739 (wsName.IsEmpty() || (wsName != L"body" && wsName != L"html" && 740 wsName != L"ol" && wsName != L"ul"))) { 741 const CFX_CSSRect* pRect = pStyle->GetMarginWidth(); 742 if (pRect) { 743 fLinePos += pRect->top.GetValue(); 744 fSpaceBelow = pRect->bottom.GetValue(); 745 } 746 } 747 748 if (wsName == L"a") { 749 ASSERT(pElement); 750 WideString wsLinkContent = pElement->GetString(L"href"); 751 if (!wsLinkContent.IsEmpty()) { 752 pLinkData = pdfium::MakeRetain<CXFA_LinkUserData>( 753 wsLinkContent.GetBuffer(wsLinkContent.GetLength())); 754 wsLinkContent.ReleaseBuffer(wsLinkContent.GetLength()); 755 } 756 } 757 758 int32_t iTabCount = m_textParser.CountTabs( 759 bContentNode ? pParentStyle.Get() : pStyle.Get()); 760 bool bSpaceRun = m_textParser.IsSpaceRun( 761 bContentNode ? pParentStyle.Get() : pStyle.Get()); 762 WideString wsText; 763 if (bContentNode && iTabCount == 0) { 764 wsText = static_cast<CFX_XMLText*>(pXMLNode)->GetText(); 765 } else if (wsName == L"br") { 766 wsText = L'\n'; 767 } else if (wsName == L"li") { 768 bCurLi = true; 769 if (bIsOl) 770 wsText = WideString::Format(L"%d. ", iLiCount); 771 else 772 wsText = 0x00B7 + WideStringView(L" ", 1); 773 } else if (!bContentNode) { 774 if (iTabCount > 0) { 775 while (iTabCount-- > 0) 776 wsText += L'\t'; 777 } else { 778 m_textParser.GetEmbbedObj(m_pTextProvider, pXMLNode, wsText); 779 } 780 } 781 782 int32_t iLength = wsText.GetLength(); 783 if (iLength > 0 && bContentNode && !bSpaceRun) 784 ProcessText(wsText); 785 786 if (m_pLoader) { 787 if (wsText.GetLength() > 0 && 788 (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) { 789 wsText.TrimLeft(L" "); 790 } 791 if (CFX_CSSDisplay::Block == eDisplay) { 792 m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; 793 } else if (CFX_CSSDisplay::Inline == eDisplay && 794 (m_pLoader->m_dwFlags & XFA_LOADERCNTXTFLG_FILTERSPACE)) { 795 m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; 796 } else if (wsText.GetLength() > 0 && 797 (0x20 == wsText[wsText.GetLength() - 1])) { 798 m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; 799 } else if (wsText.GetLength() != 0) { 800 m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; 801 } 802 } 803 804 if (wsText.GetLength() > 0) { 805 if (!m_pLoader || m_pLoader->m_iChar == 0) { 806 auto pUserData = pdfium::MakeRetain<CXFA_TextUserData>( 807 bContentNode ? pParentStyle : pStyle, pLinkData); 808 m_pBreak->SetUserData(pUserData); 809 } 810 811 if (AppendChar(wsText, fLinePos, 0, bSavePieces)) { 812 if (m_pLoader) 813 m_pLoader->m_dwFlags &= ~XFA_LOADERCNTXTFLG_FILTERSPACE; 814 if (IsEnd(bSavePieces)) { 815 if (m_pLoader && m_pLoader->m_iTotalLines > -1) { 816 m_pLoader->m_pXMLNode = pXMLNode; 817 m_pLoader->m_pParentStyle = pParentStyle; 818 } 819 return false; 820 } 821 return true; 822 } 823 } 824 } 825 } 826 827 for (CFX_XMLNode* pChildNode = 828 pXMLNode->GetNodeItem(CFX_XMLNode::FirstChild); 829 pChildNode; 830 pChildNode = pChildNode->GetNodeItem(CFX_XMLNode::NextSibling)) { 831 if (bCurOl) 832 iLiCount++; 833 834 if (!LoadRichText(pChildNode, textWidth, fLinePos, 835 pContext ? pStyle : pParentStyle, bSavePieces, 836 pLinkData, true, bIsOl, iLiCount)) 837 return false; 838 } 839 840 if (m_pLoader) { 841 if (CFX_CSSDisplay::Block == eDisplay) 842 m_pLoader->m_dwFlags |= XFA_LOADERCNTXTFLG_FILTERSPACE; 843 } 844 if (bCurLi) 845 EndBreak(CFX_BreakType::Line, fLinePos, bSavePieces); 846 } else { 847 if (pContext) 848 eDisplay = pContext->GetDisplay(); 849 } 850 851 if (m_bBlockContinue) { 852 if (pContext && !bContentNode) { 853 CFX_BreakType dwStatus = (eDisplay == CFX_CSSDisplay::Block) 854 ? CFX_BreakType::Paragraph 855 : CFX_BreakType::Piece; 856 EndBreak(dwStatus, fLinePos, bSavePieces); 857 if (eDisplay == CFX_CSSDisplay::Block) { 858 fLinePos += fSpaceBelow; 859 if (m_pTabstopContext) 860 m_pTabstopContext->RemoveAll(); 861 } 862 if (IsEnd(bSavePieces)) { 863 if (m_pLoader && m_pLoader->m_iTotalLines > -1) { 864 m_pLoader->m_pXMLNode = 865 pXMLNode->GetNodeItem(CFX_XMLNode::NextSibling); 866 m_pLoader->m_pParentStyle = pParentStyle; 867 } 868 return false; 869 } 870 } 871 } 872 return true; 873 } 874 875 bool CXFA_TextLayout::AppendChar(const WideString& wsText, 876 float& fLinePos, 877 float fSpaceAbove, 878 bool bSavePieces) { 879 CFX_BreakType dwStatus = CFX_BreakType::None; 880 int32_t iChar = 0; 881 if (m_pLoader) 882 iChar = m_pLoader->m_iChar; 883 884 int32_t iLength = wsText.GetLength(); 885 for (int32_t i = iChar; i < iLength; i++) { 886 wchar_t wch = wsText[i]; 887 if (wch == 0xA0) 888 wch = 0x20; 889 890 dwStatus = m_pBreak->AppendChar(wch); 891 if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece) { 892 AppendTextLine(dwStatus, fLinePos, bSavePieces); 893 if (IsEnd(bSavePieces)) { 894 if (m_pLoader) 895 m_pLoader->m_iChar = i; 896 return true; 897 } 898 if (dwStatus == CFX_BreakType::Paragraph && m_bRichText) 899 fLinePos += fSpaceAbove; 900 } 901 } 902 if (m_pLoader) 903 m_pLoader->m_iChar = 0; 904 905 return false; 906 } 907 908 bool CXFA_TextLayout::IsEnd(bool bSavePieces) { 909 if (!bSavePieces) 910 return false; 911 if (m_pLoader && m_pLoader->m_iTotalLines > 0) 912 return m_iLines >= m_pLoader->m_iTotalLines; 913 return false; 914 } 915 916 void CXFA_TextLayout::ProcessText(WideString& wsText) { 917 int32_t iLen = wsText.GetLength(); 918 if (iLen == 0) 919 return; 920 921 wchar_t* psz = wsText.GetBuffer(iLen); 922 int32_t iTrimLeft = 0; 923 wchar_t wch = 0, wPrev = 0; 924 for (int32_t i = 0; i < iLen; i++) { 925 wch = psz[i]; 926 if (wch < 0x20) 927 wch = 0x20; 928 if (wch == 0x20 && wPrev == 0x20) 929 continue; 930 931 wPrev = wch; 932 psz[iTrimLeft++] = wch; 933 } 934 wsText.ReleaseBuffer(iLen); 935 wsText = wsText.Left(iTrimLeft); 936 } 937 938 void CXFA_TextLayout::EndBreak(CFX_BreakType dwStatus, 939 float& fLinePos, 940 bool bSavePieces) { 941 dwStatus = m_pBreak->EndBreak(dwStatus); 942 if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece) 943 AppendTextLine(dwStatus, fLinePos, bSavePieces, true); 944 } 945 946 void CXFA_TextLayout::DoTabstops(CFX_CSSComputedStyle* pStyle, 947 CXFA_PieceLine* pPieceLine) { 948 if (!pStyle || !pPieceLine) 949 return; 950 951 if (!m_pTabstopContext || m_pTabstopContext->m_tabstops.empty()) 952 return; 953 954 int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces); 955 if (iPieces == 0) 956 return; 957 958 CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPieces - 1].get(); 959 int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex; 960 int32_t iCount = m_textParser.CountTabs(pStyle); 961 if (!pdfium::IndexInBounds(m_pTabstopContext->m_tabstops, iTabstopsIndex)) 962 return; 963 964 if (iCount > 0) { 965 iTabstopsIndex++; 966 m_pTabstopContext->m_bTabstops = true; 967 float fRight = 0; 968 if (iPieces > 1) { 969 CXFA_TextPiece* p = pPieceLine->m_textPieces[iPieces - 2].get(); 970 fRight = p->rtPiece.right(); 971 } 972 m_pTabstopContext->m_fTabWidth = 973 pPiece->rtPiece.width + pPiece->rtPiece.left - fRight; 974 } else if (iTabstopsIndex > -1) { 975 float fLeft = 0; 976 if (m_pTabstopContext->m_bTabstops) { 977 uint32_t dwAlign = m_pTabstopContext->m_tabstops[iTabstopsIndex].dwAlign; 978 if (dwAlign == FX_HashCode_GetW(L"center", false)) { 979 fLeft = pPiece->rtPiece.width / 2.0f; 980 } else if (dwAlign == FX_HashCode_GetW(L"right", false) || 981 dwAlign == FX_HashCode_GetW(L"before", false)) { 982 fLeft = pPiece->rtPiece.width; 983 } else if (dwAlign == FX_HashCode_GetW(L"decimal", false)) { 984 int32_t iChars = pPiece->iChars; 985 for (int32_t i = 0; i < iChars; i++) { 986 if (pPiece->szText[i] == L'.') 987 break; 988 989 fLeft += pPiece->Widths[i] / 20000.0f; 990 } 991 } 992 m_pTabstopContext->m_fLeft = 993 std::min(fLeft, m_pTabstopContext->m_fTabWidth); 994 m_pTabstopContext->m_bTabstops = false; 995 m_pTabstopContext->m_fTabWidth = 0; 996 } 997 pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft; 998 } 999 } 1000 1001 void CXFA_TextLayout::AppendTextLine(CFX_BreakType dwStatus, 1002 float& fLinePos, 1003 bool bSavePieces, 1004 bool bEndBreak) { 1005 int32_t iPieces = m_pBreak->CountBreakPieces(); 1006 if (iPieces < 1) 1007 return; 1008 1009 RetainPtr<CFX_CSSComputedStyle> pStyle; 1010 if (bSavePieces) { 1011 auto pNew = pdfium::MakeUnique<CXFA_PieceLine>(); 1012 CXFA_PieceLine* pPieceLine = pNew.get(); 1013 m_pieceLines.push_back(std::move(pNew)); 1014 if (m_pTabstopContext) 1015 m_pTabstopContext->Reset(); 1016 1017 float fLineStep = 0, fBaseLine = 0; 1018 int32_t i = 0; 1019 for (i = 0; i < iPieces; i++) { 1020 const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i); 1021 CXFA_TextUserData* pUserData = pPiece->m_pUserData.Get(); 1022 if (pUserData) 1023 pStyle = pUserData->m_pStyle; 1024 float fVerScale = pPiece->m_iVerticalScale / 100.0f; 1025 1026 auto pTP = pdfium::MakeUnique<CXFA_TextPiece>(); 1027 pTP->iChars = pPiece->m_iChars; 1028 pTP->szText = pPiece->GetString(); 1029 pTP->Widths = pPiece->GetWidths(); 1030 pTP->iBidiLevel = pPiece->m_iBidiLevel; 1031 pTP->iHorScale = pPiece->m_iHorizontalScale; 1032 pTP->iVerScale = pPiece->m_iVerticalScale; 1033 m_textParser.GetUnderline(m_pTextProvider, pStyle.Get(), pTP->iUnderline, 1034 pTP->iPeriod); 1035 m_textParser.GetLinethrough(m_pTextProvider, pStyle.Get(), 1036 pTP->iLineThrough); 1037 pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle.Get()); 1038 pTP->pFont = m_textParser.GetFont(m_pDoc, m_pTextProvider, pStyle.Get()); 1039 pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle.Get()); 1040 pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f; 1041 pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f; 1042 pTP->rtPiece.height = (float)pPiece->m_iFontSize * fVerScale / 20.0f; 1043 float fBaseLineTemp = 1044 m_textParser.GetBaseline(m_pTextProvider, pStyle.Get()); 1045 pTP->rtPiece.top = fBaseLineTemp; 1046 1047 float fLineHeight = m_textParser.GetLineHeight( 1048 m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale); 1049 if (fBaseLineTemp > 0) { 1050 float fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height; 1051 if (fLineHeight < fLineHeightTmp) 1052 fLineHeight = fLineHeightTmp; 1053 else 1054 fBaseLineTemp = 0; 1055 } else if (fBaseLine < -fBaseLineTemp) { 1056 fBaseLine = -fBaseLineTemp; 1057 } 1058 fLineStep = std::max(fLineStep, fLineHeight); 1059 pTP->pLinkData = pUserData ? pUserData->m_pLinkData : nullptr; 1060 pPieceLine->m_textPieces.push_back(std::move(pTP)); 1061 DoTabstops(pStyle.Get(), pPieceLine); 1062 } 1063 for (const auto& pTP : pPieceLine->m_textPieces) { 1064 float& fTop = pTP->rtPiece.top; 1065 float fBaseLineTemp = fTop; 1066 fTop = fLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp; 1067 fTop = std::max(0.0f, fTop); 1068 } 1069 fLinePos += fLineStep + fBaseLine; 1070 } else { 1071 float fLineStep = 0; 1072 float fLineWidth = 0; 1073 for (int32_t i = 0; i < iPieces; i++) { 1074 const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i); 1075 CXFA_TextUserData* pUserData = pPiece->m_pUserData.Get(); 1076 if (pUserData) 1077 pStyle = pUserData->m_pStyle; 1078 float fVerScale = pPiece->m_iVerticalScale / 100.0f; 1079 float fBaseLine = m_textParser.GetBaseline(m_pTextProvider, pStyle.Get()); 1080 float fLineHeight = m_textParser.GetLineHeight( 1081 m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale); 1082 if (fBaseLine > 0) { 1083 float fLineHeightTmp = 1084 fBaseLine + (float)pPiece->m_iFontSize * fVerScale / 20.0f; 1085 if (fLineHeight < fLineHeightTmp) { 1086 fLineHeight = fLineHeightTmp; 1087 } 1088 } 1089 fLineStep = std::max(fLineStep, fLineHeight); 1090 fLineWidth += pPiece->m_iWidth / 20000.0f; 1091 } 1092 fLinePos += fLineStep; 1093 m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth); 1094 if (m_pLoader && m_pLoader->m_bSaveLineHeight) { 1095 float fHeight = fLinePos - m_pLoader->m_fLastPos; 1096 m_pLoader->m_fLastPos = fLinePos; 1097 m_pLoader->m_lineHeights.push_back(fHeight); 1098 } 1099 } 1100 1101 m_pBreak->ClearBreakPieces(); 1102 if (dwStatus == CFX_BreakType::Paragraph) { 1103 m_pBreak->Reset(); 1104 if (!pStyle && bEndBreak) { 1105 CXFA_Para* para = m_pTextProvider->GetParaIfExists(); 1106 if (para) { 1107 float fStartPos = para->GetMarginLeft(); 1108 float fIndent = para->GetTextIndent(); 1109 if (fIndent > 0) 1110 fStartPos += fIndent; 1111 1112 float fSpaceBelow = para->GetSpaceBelow(); 1113 if (fSpaceBelow < 0.1f) 1114 fSpaceBelow = 0; 1115 1116 m_pBreak->SetLineStartPos(fStartPos); 1117 fLinePos += fSpaceBelow; 1118 } 1119 } 1120 } 1121 1122 if (pStyle) { 1123 float fStart = 0; 1124 const CFX_CSSRect* pRect = pStyle->GetMarginWidth(); 1125 if (pRect) 1126 fStart = pRect->left.GetValue(); 1127 1128 float fTextIndent = pStyle->GetTextIndent().GetValue(); 1129 if (fTextIndent < 0) 1130 fStart -= fTextIndent; 1131 1132 m_pBreak->SetLineStartPos(fStart); 1133 } 1134 m_iLines++; 1135 } 1136 1137 void CXFA_TextLayout::RenderString(CFX_RenderDevice* pDevice, 1138 CXFA_PieceLine* pPieceLine, 1139 int32_t iPiece, 1140 FXTEXT_CHARPOS* pCharPos, 1141 const CFX_Matrix& tmDoc2Device) { 1142 const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get(); 1143 int32_t iCount = GetDisplayPos(pPiece, pCharPos); 1144 if (iCount > 0) { 1145 CFDE_TextOut::DrawString(pDevice, pPiece->dwColor, pPiece->pFont, pCharPos, 1146 iCount, pPiece->fFontSize, &tmDoc2Device); 1147 } 1148 pPieceLine->m_charCounts.push_back(iCount); 1149 } 1150 1151 void CXFA_TextLayout::RenderPath(CFX_RenderDevice* pDevice, 1152 CXFA_PieceLine* pPieceLine, 1153 int32_t iPiece, 1154 FXTEXT_CHARPOS* pCharPos, 1155 const CFX_Matrix& tmDoc2Device) { 1156 CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPiece].get(); 1157 bool bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2; 1158 bool bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2; 1159 if (bNoUnderline && bNoLineThrough) 1160 return; 1161 1162 CFX_PathData path; 1163 int32_t iChars = GetDisplayPos(pPiece, pCharPos); 1164 if (iChars > 0) { 1165 CFX_PointF pt1, pt2; 1166 float fEndY = pCharPos[0].m_Origin.y + 1.05f; 1167 if (pPiece->iPeriod == XFA_AttributeEnum::Word) { 1168 for (int32_t i = 0; i < pPiece->iUnderline; i++) { 1169 for (int32_t j = 0; j < iChars; j++) { 1170 pt1.x = pCharPos[j].m_Origin.x; 1171 pt2.x = 1172 pt1.x + pCharPos[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f; 1173 pt1.y = pt2.y = fEndY; 1174 path.AppendLine(pt1, pt2); 1175 } 1176 fEndY += 2.0f; 1177 } 1178 } else { 1179 pt1.x = pCharPos[0].m_Origin.x; 1180 pt2.x = 1181 pCharPos[iChars - 1].m_Origin.x + 1182 pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; 1183 for (int32_t i = 0; i < pPiece->iUnderline; i++) { 1184 pt1.y = pt2.y = fEndY; 1185 path.AppendLine(pt1, pt2); 1186 fEndY += 2.0f; 1187 } 1188 } 1189 fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f; 1190 pt1.x = pCharPos[0].m_Origin.x; 1191 pt2.x = pCharPos[iChars - 1].m_Origin.x + 1192 pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; 1193 for (int32_t i = 0; i < pPiece->iLineThrough; i++) { 1194 pt1.y = pt2.y = fEndY; 1195 path.AppendLine(pt1, pt2); 1196 fEndY += 2.0f; 1197 } 1198 } else { 1199 if (bNoLineThrough && 1200 (bNoUnderline || pPiece->iPeriod != XFA_AttributeEnum::All)) { 1201 return; 1202 } 1203 int32_t iCharsTmp = 0; 1204 int32_t iPiecePrev = iPiece; 1205 int32_t iPieceNext = iPiece; 1206 while (iPiecePrev > 0) { 1207 iPiecePrev--; 1208 iCharsTmp = pPieceLine->m_charCounts[iPiecePrev]; 1209 if (iCharsTmp > 0) 1210 break; 1211 } 1212 if (iCharsTmp == 0) 1213 return; 1214 1215 iCharsTmp = 0; 1216 int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces); 1217 while (iPieceNext < iPieces - 1) { 1218 iPieceNext++; 1219 iCharsTmp = pPieceLine->m_charCounts[iPieceNext]; 1220 if (iCharsTmp > 0) 1221 break; 1222 } 1223 if (iCharsTmp == 0) 1224 return; 1225 1226 float fOrgX = 0.0f; 1227 float fEndX = 0.0f; 1228 pPiece = pPieceLine->m_textPieces[iPiecePrev].get(); 1229 iChars = GetDisplayPos(pPiece, pCharPos); 1230 if (iChars < 1) 1231 return; 1232 1233 fOrgX = pCharPos[iChars - 1].m_Origin.x + 1234 pCharPos[iChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f; 1235 pPiece = pPieceLine->m_textPieces[iPieceNext].get(); 1236 iChars = GetDisplayPos(pPiece, pCharPos); 1237 if (iChars < 1) 1238 return; 1239 1240 fEndX = pCharPos[0].m_Origin.x; 1241 CFX_PointF pt1; 1242 CFX_PointF pt2; 1243 pt1.x = fOrgX; 1244 pt2.x = fEndX; 1245 float fEndY = pCharPos[0].m_Origin.y + 1.05f; 1246 for (int32_t i = 0; i < pPiece->iUnderline; i++) { 1247 pt1.y = fEndY; 1248 pt2.y = fEndY; 1249 path.AppendLine(pt1, pt2); 1250 fEndY += 2.0f; 1251 } 1252 fEndY = pCharPos[0].m_Origin.y - pPiece->rtPiece.height * 0.25f; 1253 for (int32_t i = 0; i < pPiece->iLineThrough; i++) { 1254 pt1.y = fEndY; 1255 pt2.y = fEndY; 1256 path.AppendLine(pt1, pt2); 1257 fEndY += 2.0f; 1258 } 1259 } 1260 1261 CFX_GraphStateData graphState; 1262 graphState.m_LineCap = CFX_GraphStateData::LineCapButt; 1263 graphState.m_LineJoin = CFX_GraphStateData::LineJoinMiter; 1264 graphState.m_LineWidth = 1; 1265 graphState.m_MiterLimit = 10; 1266 graphState.m_DashPhase = 0; 1267 pDevice->DrawPath(&path, &tmDoc2Device, &graphState, 0, pPiece->dwColor, 0); 1268 } 1269 1270 int32_t CXFA_TextLayout::GetDisplayPos(const CXFA_TextPiece* pPiece, 1271 FXTEXT_CHARPOS* pCharPos, 1272 bool bCharCode) { 1273 if (!pPiece) 1274 return 0; 1275 1276 FX_RTFTEXTOBJ tr; 1277 if (!ToRun(pPiece, &tr)) 1278 return 0; 1279 return m_pBreak->GetDisplayPos(&tr, pCharPos, bCharCode); 1280 } 1281 1282 bool CXFA_TextLayout::ToRun(const CXFA_TextPiece* pPiece, FX_RTFTEXTOBJ* tr) { 1283 int32_t iLength = pPiece->iChars; 1284 if (iLength < 1) 1285 return false; 1286 1287 tr->pStr = pPiece->szText; 1288 tr->pFont = pPiece->pFont; 1289 tr->pRect = &pPiece->rtPiece; 1290 tr->pWidths = pPiece->Widths; 1291 tr->iLength = iLength; 1292 tr->fFontSize = pPiece->fFontSize; 1293 tr->iBidiLevel = pPiece->iBidiLevel; 1294 tr->wLineBreakChar = L'\n'; 1295 tr->iVerticalScale = pPiece->iVerScale; 1296 tr->iHorizontalScale = pPiece->iHorScale; 1297 return true; 1298 } 1299