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