1 // Copyright 2014 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/fde/tto/fde_textout.h" 8 9 #include <algorithm> 10 11 #include "core/fxcrt/fx_coordinates.h" 12 #include "core/fxcrt/fx_system.h" 13 #include "third_party/base/ptr_util.h" 14 #include "third_party/base/stl_util.h" 15 #include "xfa/fde/cfde_path.h" 16 #include "xfa/fde/fde_gedevice.h" 17 #include "xfa/fde/fde_object.h" 18 #include "xfa/fgas/crt/fgas_utils.h" 19 #include "xfa/fgas/layout/fgas_textbreak.h" 20 21 FDE_TTOPIECE::FDE_TTOPIECE() = default; 22 FDE_TTOPIECE::FDE_TTOPIECE(const FDE_TTOPIECE& that) = default; 23 FDE_TTOPIECE::~FDE_TTOPIECE() = default; 24 25 CFDE_TextOut::CFDE_TextOut() 26 : m_pTxtBreak(new CFX_TxtBreak(FX_TXTBREAKPOLICY_None)), 27 m_pFont(nullptr), 28 m_fFontSize(12.0f), 29 m_fLineSpace(m_fFontSize), 30 m_fLinePos(0.0f), 31 m_fTolerance(0.0f), 32 m_iAlignment(0), 33 m_iTxtBkAlignment(0), 34 m_wParagraphBkChar(L'\n'), 35 m_TxtColor(0xFF000000), 36 m_dwStyles(0), 37 m_dwTxtBkStyles(0), 38 m_bElliChanged(false), 39 m_iEllipsisWidth(0), 40 m_ttoLines(5), 41 m_iCurLine(0), 42 m_iCurPiece(0), 43 m_iTotalLines(0) { 44 m_Matrix.SetIdentity(); 45 m_rtClip.Reset(); 46 m_rtLogicClip.Reset(); 47 } 48 49 CFDE_TextOut::~CFDE_TextOut() {} 50 51 void CFDE_TextOut::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont) { 52 ASSERT(pFont); 53 m_pFont = pFont; 54 m_pTxtBreak->SetFont(pFont); 55 } 56 57 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize) { 58 ASSERT(fFontSize > 0); 59 m_fFontSize = fFontSize; 60 m_pTxtBreak->SetFontSize(fFontSize); 61 } 62 63 void CFDE_TextOut::SetTextColor(FX_ARGB color) { 64 m_TxtColor = color; 65 } 66 67 void CFDE_TextOut::SetStyles(uint32_t dwStyles) { 68 m_dwStyles = dwStyles; 69 m_dwTxtBkStyles = 0; 70 if (dwStyles & FDE_TTOSTYLE_SingleLine) { 71 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_SingleLine; 72 } 73 if (dwStyles & FDE_TTOSTYLE_ExpandTab) { 74 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ExpandTab; 75 } 76 if (dwStyles & FDE_TTOSTYLE_ArabicShapes) { 77 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicShapes; 78 } 79 if (dwStyles & FDE_TTOSTYLE_ArabicContext) { 80 m_dwTxtBkStyles |= FX_TXTLAYOUTSTYLE_ArabicContext; 81 } 82 if (dwStyles & FDE_TTOSTYLE_VerticalLayout) { 83 m_dwTxtBkStyles |= 84 (FX_TXTLAYOUTSTYLE_VerticalChars | FX_TXTLAYOUTSTYLE_VerticalLayout); 85 } 86 m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles); 87 } 88 89 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth) { 90 ASSERT(fTabWidth > 1.0f); 91 m_pTxtBreak->SetTabWidth(fTabWidth, false); 92 } 93 94 void CFDE_TextOut::SetEllipsisString(const CFX_WideString& wsEllipsis) { 95 m_bElliChanged = true; 96 m_wsEllipsis = wsEllipsis; 97 } 98 99 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch) { 100 m_wParagraphBkChar = wch; 101 m_pTxtBreak->SetParagraphBreakChar(wch); 102 } 103 104 void CFDE_TextOut::SetAlignment(int32_t iAlignment) { 105 m_iAlignment = iAlignment; 106 switch (m_iAlignment) { 107 case FDE_TTOALIGNMENT_TopCenter: 108 case FDE_TTOALIGNMENT_Center: 109 case FDE_TTOALIGNMENT_BottomCenter: 110 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Center; 111 break; 112 case FDE_TTOALIGNMENT_TopRight: 113 case FDE_TTOALIGNMENT_CenterRight: 114 case FDE_TTOALIGNMENT_BottomRight: 115 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Right; 116 break; 117 default: 118 m_iTxtBkAlignment = FX_TXTLINEALIGNMENT_Left; 119 break; 120 } 121 m_pTxtBreak->SetAlignment(m_iTxtBkAlignment); 122 } 123 124 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace) { 125 ASSERT(fLineSpace > 1.0f); 126 m_fLineSpace = fLineSpace; 127 } 128 129 void CFDE_TextOut::SetDIBitmap(CFX_DIBitmap* pDIB) { 130 ASSERT(pDIB); 131 132 m_pRenderDevice.reset(); 133 CFX_FxgeDevice* device = new CFX_FxgeDevice; 134 device->Attach(pDIB, false, nullptr, false); 135 m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(device, false); 136 } 137 138 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice* pDevice) { 139 ASSERT(pDevice); 140 m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(pDevice, false); 141 } 142 143 void CFDE_TextOut::SetClipRect(const CFX_Rect& rtClip) { 144 m_rtClip = rtClip.As<FX_FLOAT>(); 145 } 146 147 void CFDE_TextOut::SetClipRect(const CFX_RectF& rtClip) { 148 m_rtClip = rtClip; 149 } 150 151 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF& rtClip) { 152 m_rtLogicClip = rtClip; 153 } 154 155 void CFDE_TextOut::SetMatrix(const CFX_Matrix& matrix) { 156 m_Matrix = matrix; 157 } 158 159 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance) { 160 m_fTolerance = fTolerance; 161 m_pTxtBreak->SetLineBreakTolerance(m_fTolerance); 162 } 163 164 int32_t CFDE_TextOut::GetTotalLines() { 165 return m_iTotalLines; 166 } 167 168 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr, 169 int32_t iLength, 170 CFX_SizeF& size) { 171 CFX_RectF rtText(0.0f, 0.0f, size.width, size.height); 172 CalcLogicSize(pwsStr, iLength, rtText); 173 size = rtText.Size(); 174 } 175 176 void CFDE_TextOut::CalcLogicSize(const FX_WCHAR* pwsStr, 177 int32_t iLength, 178 CFX_RectF& rect) { 179 if (!pwsStr || iLength < 1) { 180 rect.width = 0.0f; 181 rect.height = 0.0f; 182 } else { 183 CalcTextSize(pwsStr, iLength, rect); 184 } 185 } 186 187 void CFDE_TextOut::CalcTextSize(const FX_WCHAR* pwsStr, 188 int32_t iLength, 189 CFX_RectF& rect) { 190 ASSERT(m_pFont && m_fFontSize >= 1.0f); 191 SetLineWidth(rect); 192 m_iTotalLines = 0; 193 const FX_WCHAR* pStr = pwsStr; 194 bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 195 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 196 FX_FLOAT fWidth = 0.0f; 197 FX_FLOAT fHeight = 0.0f; 198 FX_FLOAT fStartPos = bVertical ? rect.bottom() : rect.right(); 199 uint32_t dwBreakStatus = 0; 200 FX_WCHAR wPreChar = 0; 201 FX_WCHAR wch; 202 FX_WCHAR wBreak = 0; 203 while (iLength-- > 0) { 204 wch = *pStr++; 205 if (wBreak == 0 && (wch == L'\n' || wch == L'\r')) { 206 wBreak = wch; 207 m_pTxtBreak->SetParagraphBreakChar(wch); 208 } 209 if (bHotKey && wch == L'&' && wPreChar != L'&') { 210 wPreChar = wch; 211 continue; 212 } 213 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 214 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 215 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 216 } 217 wPreChar = 0; 218 } 219 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 220 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 221 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 222 } 223 m_pTxtBreak->Reset(); 224 FX_FLOAT fInc = rect.Height() - fHeight; 225 if (bVertical) { 226 fInc = rect.Width() - fHeight; 227 } 228 if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && 229 m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) { 230 fInc /= 2.0f; 231 } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) { 232 fInc = 0.0f; 233 } 234 if (bVertical) { 235 rect.top += fStartPos; 236 rect.left += fInc; 237 rect.width = fHeight; 238 rect.height = std::min(fWidth, rect.Height()); 239 } else { 240 rect.left += fStartPos; 241 rect.top += fInc; 242 rect.width = std::min(fWidth, rect.Width()); 243 rect.height = fHeight; 244 if (m_dwStyles & FDE_TTOSTYLE_LastLineHeight) { 245 rect.height -= m_fLineSpace - m_fFontSize; 246 } 247 } 248 } 249 250 void CFDE_TextOut::SetLineWidth(CFX_RectF& rect) { 251 if ((m_dwStyles & FDE_TTOSTYLE_SingleLine) == 0) { 252 FX_FLOAT fLineWidth = 0.0f; 253 if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) { 254 if (rect.Height() < 1.0f) { 255 rect.height = m_fFontSize * 1000.0f; 256 } 257 fLineWidth = rect.Height(); 258 } else { 259 if (rect.Width() < 1.0f) { 260 rect.width = m_fFontSize * 1000.0f; 261 } 262 fLineWidth = rect.Width(); 263 } 264 m_pTxtBreak->SetLineWidth(fLineWidth); 265 } 266 } 267 268 bool CFDE_TextOut::RetrieveLineWidth(uint32_t dwBreakStatus, 269 FX_FLOAT& fStartPos, 270 FX_FLOAT& fWidth, 271 FX_FLOAT& fHeight) { 272 if (dwBreakStatus <= FX_TXTBREAK_PieceBreak) { 273 return false; 274 } 275 FX_FLOAT fLineStep = 276 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 277 bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 278 FX_FLOAT fLineWidth = 0.0f; 279 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 280 for (int32_t i = 0; i < iCount; i++) { 281 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 282 fLineWidth += (FX_FLOAT)pPiece->m_iWidth / 20000.0f; 283 fStartPos = std::min(fStartPos, (FX_FLOAT)pPiece->m_iStartPos / 20000.0f); 284 } 285 m_pTxtBreak->ClearBreakPieces(); 286 if (dwBreakStatus == FX_TXTBREAK_ParagraphBreak) { 287 m_pTxtBreak->Reset(); 288 } 289 if (!bLineWrap && dwBreakStatus == FX_TXTBREAK_LineBreak) { 290 fWidth += fLineWidth; 291 } else { 292 fWidth = std::max(fWidth, fLineWidth); 293 fHeight += fLineStep; 294 } 295 m_iTotalLines++; 296 return true; 297 } 298 299 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 300 int32_t iLength, 301 int32_t x, 302 int32_t y) { 303 CFX_RectF rtText(static_cast<FX_FLOAT>(x), static_cast<FX_FLOAT>(y), 304 m_fFontSize * 1000.0f, m_fFontSize * 1000.0f); 305 DrawText(pwsStr, iLength, rtText); 306 } 307 308 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 309 int32_t iLength, 310 FX_FLOAT x, 311 FX_FLOAT y) { 312 DrawText(pwsStr, iLength, 313 CFX_RectF(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f)); 314 } 315 316 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 317 int32_t iLength, 318 const CFX_Rect& rect) { 319 DrawText(pwsStr, iLength, rect.As<FX_FLOAT>()); 320 } 321 322 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 323 int32_t iLength, 324 const CFX_RectF& rect) { 325 CFX_RectF rtText(rect.left, rect.top, rect.width, rect.height); 326 CFX_Matrix rm; 327 rm.SetReverse(m_Matrix); 328 rm.TransformRect(rtText); 329 DrawText(pwsStr, iLength, rtText, m_rtClip); 330 } 331 332 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr, 333 int32_t iLength, 334 FX_FLOAT x, 335 FX_FLOAT y) { 336 CFX_RectF rtText(x, y, m_fFontSize * 1000.0f, m_fFontSize * 1000.0f); 337 DrawLogicText(pwsStr, iLength, rtText); 338 } 339 340 void CFDE_TextOut::DrawLogicText(const FX_WCHAR* pwsStr, 341 int32_t iLength, 342 const CFX_RectF& rect) { 343 CFX_RectF rtClip(m_rtLogicClip.left, m_rtLogicClip.top, m_rtLogicClip.width, 344 m_rtLogicClip.height); 345 m_Matrix.TransformRect(rtClip); 346 DrawText(pwsStr, iLength, rect, rtClip); 347 } 348 349 void CFDE_TextOut::DrawText(const FX_WCHAR* pwsStr, 350 int32_t iLength, 351 const CFX_RectF& rect, 352 const CFX_RectF& rtClip) { 353 ASSERT(m_pFont && m_fFontSize >= 1.0f); 354 if (!pwsStr || iLength < 1) 355 return; 356 357 if (rect.width < m_fFontSize || rect.height < m_fFontSize) { 358 return; 359 } 360 FX_FLOAT fLineWidth = rect.width; 361 if (m_dwStyles & FDE_TTOSTYLE_VerticalLayout) { 362 fLineWidth = rect.height; 363 } 364 m_pTxtBreak->SetLineWidth(fLineWidth); 365 m_ttoLines.clear(); 366 m_wsText.clear(); 367 LoadText(pwsStr, iLength, rect); 368 if (m_dwStyles & FDE_TTOSTYLE_Ellipsis) { 369 ReplaceWidthEllipsis(); 370 } 371 Reload(rect); 372 DoAlignment(rect); 373 OnDraw(rtClip); 374 } 375 376 void CFDE_TextOut::ExpandBuffer(int32_t iSize, int32_t iType) { 377 ASSERT(iSize >= 0); 378 size_t size = iSize; 379 switch (iType) { 380 case 0: 381 if (m_CharWidths.size() < size) 382 m_CharWidths.resize(size, 0); 383 break; 384 case 1: 385 if (m_EllCharWidths.size() < size) 386 m_EllCharWidths.resize(size, 0); 387 break; 388 case 2: 389 if (m_CharPos.size() < size) 390 m_CharPos.resize(size, FXTEXT_CHARPOS()); 391 break; 392 } 393 } 394 395 void CFDE_TextOut::LoadEllipsis() { 396 if (!m_bElliChanged) { 397 return; 398 } 399 m_bElliChanged = false; 400 m_iEllipsisWidth = 0; 401 int32_t iLength = m_wsEllipsis.GetLength(); 402 if (iLength < 1) { 403 return; 404 } 405 ExpandBuffer(iLength, 1); 406 const FX_WCHAR* pStr = m_wsEllipsis.c_str(); 407 uint32_t dwBreakStatus; 408 FX_WCHAR wch; 409 while (iLength-- > 0) { 410 wch = *pStr++; 411 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 412 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) 413 RetrieveEllPieces(&m_EllCharWidths); 414 } 415 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 416 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) 417 RetrieveEllPieces(&m_EllCharWidths); 418 m_pTxtBreak->Reset(); 419 } 420 421 void CFDE_TextOut::RetrieveEllPieces(std::vector<int32_t>* pCharWidths) { 422 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 423 int32_t iCharIndex = 0; 424 for (int32_t i = 0; i < iCount; i++) { 425 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 426 int32_t iPieceChars = pPiece->GetLength(); 427 for (int32_t j = 0; j < iPieceChars; j++) { 428 CFX_Char* pTC = pPiece->GetCharPtr(j); 429 (*pCharWidths)[iCharIndex] = std::max(pTC->m_iCharWidth, 0); 430 m_iEllipsisWidth += (*pCharWidths)[iCharIndex]; 431 iCharIndex++; 432 } 433 } 434 m_pTxtBreak->ClearBreakPieces(); 435 } 436 437 void CFDE_TextOut::LoadText(const FX_WCHAR* pwsStr, 438 int32_t iLength, 439 const CFX_RectF& rect) { 440 FX_WCHAR* pStr = m_wsText.GetBuffer(iLength); 441 int32_t iTxtLength = iLength; 442 ExpandBuffer(iTxtLength, 0); 443 bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 444 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 445 bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 446 FX_FLOAT fLineStep = 447 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 448 FX_FLOAT fLineStop = bVertical ? rect.left : rect.bottom(); 449 m_fLinePos = bVertical ? rect.right() : rect.top; 450 if (bVertical) { 451 fLineStep = -fLineStep; 452 } 453 m_hotKeys.RemoveAll(); 454 int32_t iStartChar = 0; 455 int32_t iChars = 0; 456 int32_t iPieceWidths = 0; 457 uint32_t dwBreakStatus; 458 FX_WCHAR wch; 459 bool bRet = false; 460 while (iTxtLength-- > 0) { 461 wch = *pwsStr++; 462 if (bHotKey && wch == L'&' && *(pStr - 1) != L'&') { 463 if (iTxtLength > 0) 464 m_hotKeys.Add(iChars); 465 continue; 466 } 467 *pStr++ = wch; 468 iChars++; 469 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 470 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 471 bool bEndofLine = 472 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect); 473 if (bEndofLine && (bLineWrap || (dwBreakStatus > FX_TXTBREAK_LineBreak && 474 !bLineWrap))) { 475 iPieceWidths = 0; 476 m_iCurLine++; 477 m_fLinePos += fLineStep; 478 } 479 if ((bVertical && m_fLinePos + fLineStep < fLineStop) || 480 (!bVertical && m_fLinePos + fLineStep > fLineStop)) { 481 int32_t iCurLine = bEndofLine ? m_iCurLine - 1 : m_iCurLine; 482 m_ttoLines[iCurLine].SetNewReload(true); 483 bRet = true; 484 break; 485 } 486 } 487 } 488 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 489 if (dwBreakStatus > FX_TXTBREAK_PieceBreak && !bRet) { 490 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect); 491 } 492 m_pTxtBreak->ClearBreakPieces(); 493 m_pTxtBreak->Reset(); 494 m_wsText.ReleaseBuffer(iLength); 495 } 496 497 bool CFDE_TextOut::RetriecePieces(uint32_t dwBreakStatus, 498 int32_t& iStartChar, 499 int32_t& iPieceWidths, 500 bool bReload, 501 const CFX_RectF& rect) { 502 bool bSingleLine = !!(m_dwStyles & FDE_TTOSTYLE_SingleLine); 503 bool bLineWrap = !!(m_dwStyles & FDE_TTOSTYLE_LineWrap); 504 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 505 FX_FLOAT fLineStep = 506 (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 507 if (bVertical) { 508 fLineStep = -fLineStep; 509 } 510 CFX_Char* pTC = nullptr; 511 bool bNeedReload = false; 512 FX_FLOAT fLineWidth = bVertical ? rect.Height() : rect.Width(); 513 int32_t iLineWidth = FXSYS_round(fLineWidth * 20000.0f); 514 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 515 for (int32_t i = 0; i < iCount; i++) { 516 const CFX_TxtPiece* pPiece = m_pTxtBreak->GetBreakPiece(i); 517 int32_t iPieceChars = pPiece->GetLength(); 518 int32_t iChar = iStartChar; 519 int32_t iWidth = 0; 520 int32_t j = 0; 521 for (; j < iPieceChars; j++) { 522 pTC = pPiece->GetCharPtr(j); 523 int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0; 524 if (bSingleLine || !bLineWrap) { 525 if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) { 526 bNeedReload = true; 527 break; 528 } 529 } 530 iWidth += iCurCharWidth; 531 m_CharWidths[iChar++] = iCurCharWidth; 532 } 533 if (j == 0 && !bReload) { 534 m_ttoLines[m_iCurLine].SetNewReload(true); 535 } else if (j > 0) { 536 CFX_RectF rtPiece; 537 if (bVertical) { 538 rtPiece.left = m_fLinePos; 539 rtPiece.top = rect.top + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f; 540 rtPiece.width = fLineStep; 541 rtPiece.height = iWidth / 20000.0f; 542 } else { 543 rtPiece.left = rect.left + (FX_FLOAT)pPiece->m_iStartPos / 20000.0f; 544 rtPiece.top = m_fLinePos; 545 rtPiece.width = iWidth / 20000.0f; 546 rtPiece.height = fLineStep; 547 } 548 FDE_TTOPIECE ttoPiece; 549 ttoPiece.iStartChar = iStartChar; 550 ttoPiece.iChars = j; 551 ttoPiece.rtPiece = rtPiece; 552 ttoPiece.dwCharStyles = pPiece->m_dwCharStyles; 553 if (FX_IsOdd(pPiece->m_iBidiLevel)) { 554 ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; 555 } 556 AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1)); 557 } 558 iStartChar += iPieceChars; 559 iPieceWidths += iWidth; 560 } 561 m_pTxtBreak->ClearBreakPieces(); 562 bool bRet = bSingleLine || bLineWrap || (!bLineWrap && bNeedReload) || 563 dwBreakStatus == FX_TXTBREAK_ParagraphBreak; 564 return bRet; 565 } 566 567 void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE& ttoPiece, 568 bool bNeedReload, 569 bool bEnd) { 570 if (m_iCurLine >= pdfium::CollectionSize<int32_t>(m_ttoLines)) { 571 CFDE_TTOLine ttoLine; 572 ttoLine.SetNewReload(bNeedReload); 573 m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, ttoPiece); 574 m_ttoLines.push_back(ttoLine); 575 m_iCurLine = pdfium::CollectionSize<int32_t>(m_ttoLines) - 1; 576 } else { 577 CFDE_TTOLine* pLine = &m_ttoLines[m_iCurLine]; 578 pLine->SetNewReload(bNeedReload); 579 m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece); 580 if (bEnd) { 581 int32_t iPieces = pLine->GetSize(); 582 if (m_iCurPiece < iPieces) { 583 pLine->RemoveLast(iPieces - m_iCurPiece - 1); 584 } 585 } 586 } 587 if (!bEnd && bNeedReload) 588 m_iCurPiece = 0; 589 } 590 591 void CFDE_TextOut::ReplaceWidthEllipsis() { 592 LoadEllipsis(); 593 int32_t iLength = m_wsEllipsis.GetLength(); 594 if (iLength < 1) 595 return; 596 597 for (auto& line : m_ttoLines) { 598 if (!line.GetNewReload()) 599 continue; 600 601 int32_t iEllipsisCharIndex = iLength - 1; 602 int32_t iCharWidth = 0; 603 int32_t iCharCount = 0; 604 int32_t iPiece = line.GetSize(); 605 while (iPiece-- > 0) { 606 FDE_TTOPIECE* pPiece = line.GetPtrAt(iPiece); 607 if (!pPiece) 608 break; 609 610 for (int32_t j = pPiece->iChars - 1; j >= 0; j--) { 611 if (iEllipsisCharIndex < 0) 612 break; 613 614 int32_t index = pPiece->iStartChar + j; 615 iCharWidth += m_CharWidths[index]; 616 iCharCount++; 617 if (iCharCount <= iLength) { 618 m_wsText.SetAt(index, m_wsEllipsis.GetAt(iEllipsisCharIndex)); 619 m_CharWidths[index] = m_EllCharWidths[iEllipsisCharIndex]; 620 } else if (iCharWidth <= m_iEllipsisWidth) { 621 m_wsText.SetAt(index, 0); 622 m_CharWidths[index] = 0; 623 } 624 iEllipsisCharIndex--; 625 } 626 if (iEllipsisCharIndex < 0) 627 break; 628 } 629 } 630 } 631 632 void CFDE_TextOut::Reload(const CFX_RectF& rect) { 633 int i = 0; 634 for (auto& line : m_ttoLines) { 635 if (line.GetNewReload()) { 636 m_iCurLine = i; 637 m_iCurPiece = 0; 638 ReloadLinePiece(&line, rect); 639 } 640 ++i; 641 } 642 } 643 644 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) { 645 const FX_WCHAR* pwsStr = m_wsText.c_str(); 646 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 647 int32_t iPieceWidths = 0; 648 FDE_TTOPIECE* pPiece = pLine->GetPtrAt(0); 649 int32_t iStartChar = pPiece->iStartChar; 650 m_fLinePos = bVertical ? pPiece->rtPiece.left : pPiece->rtPiece.top; 651 int32_t iPieceCount = pLine->GetSize(); 652 int32_t iPieceIndex = 0; 653 uint32_t dwBreakStatus = 0; 654 FX_WCHAR wch; 655 while (iPieceIndex < iPieceCount) { 656 int32_t iStar = iStartChar; 657 int32_t iEnd = pPiece->iChars + iStar; 658 while (iStar < iEnd) { 659 wch = *(pwsStr + iStar); 660 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 661 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 662 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect); 663 } 664 iStar++; 665 } 666 iPieceIndex++; 667 pPiece = pLine->GetPtrAt(iPieceIndex); 668 } 669 dwBreakStatus = m_pTxtBreak->EndBreak(FX_TXTBREAK_ParagraphBreak); 670 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) { 671 RetriecePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect); 672 } 673 m_pTxtBreak->Reset(); 674 } 675 676 void CFDE_TextOut::DoAlignment(const CFX_RectF& rect) { 677 if (m_ttoLines.empty()) 678 return; 679 680 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 681 FX_FLOAT fLineStopS = bVertical ? rect.right() : rect.bottom(); 682 FDE_TTOPIECE* pFirstPiece = m_ttoLines.back().GetPtrAt(0); 683 if (!pFirstPiece) 684 return; 685 686 FX_FLOAT fLineStopD = 687 bVertical ? pFirstPiece->rtPiece.right() : pFirstPiece->rtPiece.bottom(); 688 FX_FLOAT fInc = fLineStopS - fLineStopD; 689 if (m_iAlignment >= FDE_TTOALIGNMENT_CenterLeft && 690 m_iAlignment < FDE_TTOALIGNMENT_BottomLeft) { 691 fInc /= 2.0f; 692 } else if (m_iAlignment < FDE_TTOALIGNMENT_CenterLeft) { 693 fInc = 0.0f; 694 } 695 if (fInc < 1.0f) 696 return; 697 for (auto& line : m_ttoLines) { 698 int32_t iPieces = line.GetSize(); 699 for (int32_t j = 0; j < iPieces; j++) { 700 FDE_TTOPIECE* pPiece = line.GetPtrAt(j); 701 if (bVertical) 702 pPiece->rtPiece.left += fInc; 703 else 704 pPiece->rtPiece.top += fInc; 705 } 706 } 707 } 708 709 void CFDE_TextOut::OnDraw(const CFX_RectF& rtClip) { 710 if (!m_pRenderDevice || m_ttoLines.empty()) 711 return; 712 713 auto pBrush = pdfium::MakeUnique<CFDE_Brush>(); 714 pBrush->SetColor(m_TxtColor); 715 m_pRenderDevice->SaveState(); 716 if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f) 717 m_pRenderDevice->SetClipRect(rtClip); 718 719 auto pPen = pdfium::MakeUnique<CFDE_Pen>(); 720 pPen->SetColor(m_TxtColor); 721 722 for (auto& line : m_ttoLines) { 723 int32_t iPieces = line.GetSize(); 724 for (int32_t j = 0; j < iPieces; j++) { 725 FDE_TTOPIECE* pPiece = line.GetPtrAt(j); 726 if (!pPiece) 727 continue; 728 729 int32_t iCount = GetDisplayPos(pPiece); 730 if (iCount > 0) { 731 m_pRenderDevice->DrawString(pBrush.get(), m_pFont, m_CharPos.data(), 732 iCount, m_fFontSize, &m_Matrix); 733 } 734 DrawLine(pPiece, pPen.get()); 735 } 736 } 737 m_pRenderDevice->RestoreState(); 738 } 739 740 int32_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) { 741 FX_TXTRUN tr = ToTextRun(pPiece); 742 ExpandBuffer(tr.iLength, 2); 743 return m_pTxtBreak->GetDisplayPos(&tr, m_CharPos.data()); 744 } 745 746 int32_t CFDE_TextOut::GetCharRects(const FDE_TTOPIECE* pPiece) { 747 FX_TXTRUN tr = ToTextRun(pPiece); 748 m_rectArray = m_pTxtBreak->GetCharRects(&tr); 749 return pdfium::CollectionSize<int32_t>(m_rectArray); 750 } 751 752 FX_TXTRUN CFDE_TextOut::ToTextRun(const FDE_TTOPIECE* pPiece) { 753 FX_TXTRUN tr; 754 tr.wsStr = m_wsText + pPiece->iStartChar; 755 tr.pWidths = &m_CharWidths[pPiece->iStartChar]; 756 tr.iLength = pPiece->iChars; 757 tr.pFont = m_pFont; 758 tr.fFontSize = m_fFontSize; 759 tr.dwStyles = m_dwTxtBkStyles; 760 tr.dwCharStyles = pPiece->dwCharStyles; 761 tr.wLineBreakChar = m_wParagraphBkChar; 762 tr.pRect = &pPiece->rtPiece; 763 return tr; 764 } 765 766 void CFDE_TextOut::DrawLine(const FDE_TTOPIECE* pPiece, CFDE_Pen* pPen) { 767 bool bUnderLine = !!(m_dwStyles & FDE_TTOSTYLE_Underline); 768 bool bStrikeOut = !!(m_dwStyles & FDE_TTOSTYLE_Strikeout); 769 bool bHotKey = !!(m_dwStyles & FDE_TTOSTYLE_HotKey); 770 bool bVertical = !!(m_dwStyles & FDE_TTOSTYLE_VerticalLayout); 771 if (!bUnderLine && !bStrikeOut && !bHotKey) 772 return; 773 774 std::unique_ptr<CFDE_Path> pPath(new CFDE_Path); 775 int32_t iLineCount = 0; 776 CFX_RectF rtText = pPiece->rtPiece; 777 CFX_PointF pt1, pt2; 778 if (bUnderLine) { 779 if (bVertical) { 780 pt1.x = rtText.left; 781 pt1.y = rtText.top; 782 pt2.x = rtText.left; 783 pt2.y = rtText.bottom(); 784 } else { 785 pt1.x = rtText.left; 786 pt1.y = rtText.bottom(); 787 pt2.x = rtText.right(); 788 pt2.y = rtText.bottom(); 789 } 790 pPath->AddLine(pt1, pt2); 791 iLineCount++; 792 } 793 if (bStrikeOut) { 794 if (bVertical) { 795 pt1.x = rtText.left + rtText.width * 2.0f / 5.0f; 796 pt1.y = rtText.top; 797 pt2.x = pt1.x; 798 pt2.y = rtText.bottom(); 799 } else { 800 pt1.x = rtText.left; 801 pt1.y = rtText.bottom() - rtText.height * 2.0f / 5.0f; 802 pt2.x = rtText.right(); 803 pt2.y = pt1.y; 804 } 805 pPath->AddLine(pt1, pt2); 806 iLineCount++; 807 } 808 if (bHotKey) { 809 int32_t iHotKeys = m_hotKeys.GetSize(); 810 int32_t iCount = GetCharRects(pPiece); 811 if (iCount > 0) { 812 for (int32_t i = 0; i < iHotKeys; i++) { 813 int32_t iCharIndex = m_hotKeys.GetAt(i); 814 if (iCharIndex >= pPiece->iStartChar && 815 iCharIndex < pPiece->iStartChar + pPiece->iChars) { 816 CFX_RectF rect = m_rectArray[iCharIndex - pPiece->iStartChar]; 817 if (bVertical) { 818 pt1.x = rect.left; 819 pt1.y = rect.top; 820 pt2.x = rect.left; 821 pt2.y = rect.bottom(); 822 } else { 823 pt1.x = rect.left; 824 pt1.y = rect.bottom(); 825 pt2.x = rect.right(); 826 pt2.y = rect.bottom(); 827 } 828 pPath->AddLine(pt1, pt2); 829 iLineCount++; 830 } 831 } 832 } 833 } 834 if (iLineCount > 0) 835 m_pRenderDevice->DrawPath(pPen, 1, pPath.get(), &m_Matrix); 836 } 837 838 CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {} 839 840 CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine& ttoLine) : m_pieces(5) { 841 m_bNewReload = ttoLine.m_bNewReload; 842 m_pieces = ttoLine.m_pieces; 843 } 844 845 CFDE_TTOLine::~CFDE_TTOLine() {} 846 847 int32_t CFDE_TTOLine::AddPiece(int32_t index, const FDE_TTOPIECE& ttoPiece) { 848 if (index >= pdfium::CollectionSize<int32_t>(m_pieces)) { 849 m_pieces.push_back(ttoPiece); 850 return pdfium::CollectionSize<int32_t>(m_pieces); 851 } 852 m_pieces[index] = ttoPiece; 853 return index; 854 } 855 856 int32_t CFDE_TTOLine::GetSize() const { 857 return pdfium::CollectionSize<int32_t>(m_pieces); 858 } 859 860 FDE_TTOPIECE* CFDE_TTOLine::GetPtrAt(int32_t index) { 861 if (index < 0 || index >= pdfium::CollectionSize<int32_t>(m_pieces)) 862 return nullptr; 863 864 return &m_pieces[index]; 865 } 866 867 void CFDE_TTOLine::RemoveLast(int32_t icount) { 868 if (icount < 0) 869 return; 870 icount = std::min(icount, pdfium::CollectionSize<int32_t>(m_pieces)); 871 m_pieces.erase(m_pieces.end() - icount, m_pieces.end()); 872 } 873 874 void CFDE_TTOLine::RemoveAll() { 875 m_pieces.clear(); 876 } 877