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/cfde_textout.h" 8 9 #include <algorithm> 10 #include <utility> 11 12 #include "core/fxcrt/fx_coordinates.h" 13 #include "core/fxcrt/fx_system.h" 14 #include "core/fxge/cfx_font.h" 15 #include "core/fxge/cfx_pathdata.h" 16 #include "third_party/base/ptr_util.h" 17 #include "third_party/base/stl_util.h" 18 #include "xfa/fgas/font/cfgas_gefont.h" 19 #include "xfa/fgas/layout/cfx_txtbreak.h" 20 21 namespace { 22 23 bool TextAlignmentVerticallyCentered(const FDE_TextAlignment align) { 24 return align == FDE_TextAlignment::kCenterLeft || 25 align == FDE_TextAlignment::kCenter || 26 align == FDE_TextAlignment::kCenterRight; 27 } 28 29 bool IsTextAlignmentTop(const FDE_TextAlignment align) { 30 return align == FDE_TextAlignment::kTopLeft; 31 } 32 33 } // namespace 34 35 // static 36 bool CFDE_TextOut::DrawString(CFX_RenderDevice* device, 37 FX_ARGB color, 38 const RetainPtr<CFGAS_GEFont>& pFont, 39 FXTEXT_CHARPOS* pCharPos, 40 int32_t iCount, 41 float fFontSize, 42 const CFX_Matrix* pMatrix) { 43 ASSERT(pFont && pCharPos && iCount > 0); 44 45 CFX_Font* pFxFont = pFont->GetDevFont(); 46 if (FontStyleIsItalic(pFont->GetFontStyles()) && !pFxFont->IsItalic()) { 47 for (int32_t i = 0; i < iCount; ++i) { 48 static const float mc = 0.267949f; 49 float* pAM = pCharPos->m_AdjustMatrix; 50 pAM[2] = mc * pAM[0] + pAM[2]; 51 pAM[3] = mc * pAM[1] + pAM[3]; 52 ++pCharPos; 53 } 54 } 55 56 #if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 57 uint32_t dwFontStyle = pFont->GetFontStyles(); 58 CFX_Font FxFont; 59 auto SubstFxFont = pdfium::MakeUnique<CFX_SubstFont>(); 60 SubstFxFont->m_Weight = FontStyleIsBold(dwFontStyle) ? 700 : 400; 61 SubstFxFont->m_ItalicAngle = FontStyleIsItalic(dwFontStyle) ? -12 : 0; 62 SubstFxFont->m_WeightCJK = SubstFxFont->m_Weight; 63 SubstFxFont->m_bItalicCJK = FontStyleIsItalic(dwFontStyle); 64 FxFont.SetSubstFont(std::move(SubstFxFont)); 65 #endif // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 66 67 RetainPtr<CFGAS_GEFont> pCurFont; 68 FXTEXT_CHARPOS* pCurCP = nullptr; 69 int32_t iCurCount = 0; 70 for (int32_t i = 0; i < iCount; ++i) { 71 RetainPtr<CFGAS_GEFont> pSTFont = 72 pFont->GetSubstFont(static_cast<int32_t>(pCharPos->m_GlyphIndex)); 73 pCharPos->m_GlyphIndex &= 0x00FFFFFF; 74 pCharPos->m_bFontStyle = false; 75 if (pCurFont != pSTFont) { 76 if (pCurFont) { 77 pFxFont = pCurFont->GetDevFont(); 78 79 CFX_Font* font; 80 #if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 81 FxFont.SetFace(pFxFont->GetFace()); 82 font = &FxFont; 83 #else 84 font = pFxFont; 85 #endif // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 86 87 device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, pMatrix, 88 color, FXTEXT_CLEARTYPE); 89 } 90 pCurFont = pSTFont; 91 pCurCP = pCharPos; 92 iCurCount = 1; 93 } else { 94 ++iCurCount; 95 } 96 ++pCharPos; 97 } 98 99 bool bRet = true; 100 if (pCurFont && iCurCount) { 101 pFxFont = pCurFont->GetDevFont(); 102 CFX_Font* font; 103 #if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 104 FxFont.SetFace(pFxFont->GetFace()); 105 font = &FxFont; 106 #else 107 font = pFxFont; 108 #endif // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 109 110 bRet = device->DrawNormalText(iCurCount, pCurCP, font, -fFontSize, pMatrix, 111 color, FXTEXT_CLEARTYPE); 112 } 113 114 #if _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 115 FxFont.SetFace(nullptr); 116 #endif // _FX_PLATFORM_ != _FX_PLATFORM_WINDOWS_ 117 118 return bRet; 119 } 120 121 FDE_TTOPIECE::FDE_TTOPIECE() = default; 122 123 FDE_TTOPIECE::FDE_TTOPIECE(const FDE_TTOPIECE& that) = default; 124 125 FDE_TTOPIECE::~FDE_TTOPIECE() = default; 126 127 CFDE_TextOut::CFDE_TextOut() 128 : m_pTxtBreak(pdfium::MakeUnique<CFX_TxtBreak>()), 129 m_pFont(nullptr), 130 m_fFontSize(12.0f), 131 m_fLineSpace(m_fFontSize), 132 m_fLinePos(0.0f), 133 m_fTolerance(0.0f), 134 m_iAlignment(FDE_TextAlignment::kTopLeft), 135 m_TxtColor(0xFF000000), 136 m_dwTxtBkStyles(0), 137 m_ttoLines(5), 138 m_iCurLine(0), 139 m_iCurPiece(0), 140 m_iTotalLines(0) {} 141 142 CFDE_TextOut::~CFDE_TextOut() {} 143 144 void CFDE_TextOut::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) { 145 ASSERT(pFont); 146 m_pFont = pFont; 147 m_pTxtBreak->SetFont(pFont); 148 } 149 150 void CFDE_TextOut::SetFontSize(float fFontSize) { 151 ASSERT(fFontSize > 0); 152 m_fFontSize = fFontSize; 153 m_pTxtBreak->SetFontSize(fFontSize); 154 } 155 156 void CFDE_TextOut::SetStyles(const FDE_TextStyle& dwStyles) { 157 m_Styles = dwStyles; 158 159 m_dwTxtBkStyles = 0; 160 if (m_Styles.single_line_) 161 m_dwTxtBkStyles |= FX_LAYOUTSTYLE_SingleLine; 162 163 m_pTxtBreak->SetLayoutStyles(m_dwTxtBkStyles); 164 } 165 166 void CFDE_TextOut::SetAlignment(FDE_TextAlignment iAlignment) { 167 m_iAlignment = iAlignment; 168 169 int32_t txtBreakAlignment = 0; 170 switch (m_iAlignment) { 171 case FDE_TextAlignment::kCenter: 172 txtBreakAlignment = CFX_TxtLineAlignment_Center; 173 break; 174 case FDE_TextAlignment::kCenterRight: 175 txtBreakAlignment = CFX_TxtLineAlignment_Right; 176 break; 177 case FDE_TextAlignment::kCenterLeft: 178 case FDE_TextAlignment::kTopLeft: 179 txtBreakAlignment = CFX_TxtLineAlignment_Left; 180 break; 181 } 182 m_pTxtBreak->SetAlignment(txtBreakAlignment); 183 } 184 185 void CFDE_TextOut::SetLineSpace(float fLineSpace) { 186 ASSERT(fLineSpace > 1.0f); 187 m_fLineSpace = fLineSpace; 188 } 189 190 void CFDE_TextOut::SetLineBreakTolerance(float fTolerance) { 191 m_fTolerance = fTolerance; 192 m_pTxtBreak->SetLineBreakTolerance(m_fTolerance); 193 } 194 195 void CFDE_TextOut::CalcLogicSize(const WideString& str, CFX_SizeF& size) { 196 CFX_RectF rtText(0.0f, 0.0f, size.width, size.height); 197 CalcLogicSize(str, rtText); 198 size = rtText.Size(); 199 } 200 201 void CFDE_TextOut::CalcLogicSize(const WideString& str, CFX_RectF& rect) { 202 if (str.IsEmpty()) { 203 rect.width = 0.0f; 204 rect.height = 0.0f; 205 return; 206 } 207 208 ASSERT(m_pFont && m_fFontSize >= 1.0f); 209 210 if (!m_Styles.single_line_) { 211 if (rect.Width() < 1.0f) 212 rect.width = m_fFontSize * 1000.0f; 213 214 m_pTxtBreak->SetLineWidth(rect.Width()); 215 } 216 217 m_iTotalLines = 0; 218 float fWidth = 0.0f; 219 float fHeight = 0.0f; 220 float fStartPos = rect.right(); 221 CFX_BreakType dwBreakStatus = CFX_BreakType::None; 222 bool break_char_is_set = false; 223 for (const wchar_t& wch : str) { 224 if (!break_char_is_set && (wch == L'\n' || wch == L'\r')) { 225 break_char_is_set = true; 226 m_pTxtBreak->SetParagraphBreakChar(wch); 227 } 228 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 229 if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 230 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 231 } 232 233 dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph); 234 if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 235 RetrieveLineWidth(dwBreakStatus, fStartPos, fWidth, fHeight); 236 237 m_pTxtBreak->Reset(); 238 float fInc = rect.Height() - fHeight; 239 if (TextAlignmentVerticallyCentered(m_iAlignment)) 240 fInc /= 2.0f; 241 else if (IsTextAlignmentTop(m_iAlignment)) 242 fInc = 0.0f; 243 244 rect.left += fStartPos; 245 rect.top += fInc; 246 rect.width = std::min(fWidth, rect.Width()); 247 rect.height = fHeight; 248 if (m_Styles.last_line_height_) 249 rect.height -= m_fLineSpace - m_fFontSize; 250 } 251 252 bool CFDE_TextOut::RetrieveLineWidth(CFX_BreakType dwBreakStatus, 253 float& fStartPos, 254 float& fWidth, 255 float& fHeight) { 256 if (CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 257 return false; 258 259 float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 260 float fLineWidth = 0.0f; 261 for (int32_t i = 0; i < m_pTxtBreak->CountBreakPieces(); i++) { 262 const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i); 263 fLineWidth += static_cast<float>(pPiece->m_iWidth) / 20000.0f; 264 fStartPos = 265 std::min(fStartPos, static_cast<float>(pPiece->m_iStartPos) / 20000.0f); 266 } 267 m_pTxtBreak->ClearBreakPieces(); 268 269 if (dwBreakStatus == CFX_BreakType::Paragraph) 270 m_pTxtBreak->Reset(); 271 if (!m_Styles.line_wrap_ && dwBreakStatus == CFX_BreakType::Line) { 272 fWidth += fLineWidth; 273 } else { 274 fWidth = std::max(fWidth, fLineWidth); 275 fHeight += fLineStep; 276 } 277 ++m_iTotalLines; 278 return true; 279 } 280 281 void CFDE_TextOut::DrawLogicText(CFX_RenderDevice* device, 282 const WideStringView& str, 283 const CFX_RectF& rect) { 284 ASSERT(m_pFont && m_fFontSize >= 1.0f); 285 286 if (str.IsEmpty()) 287 return; 288 if (rect.width < m_fFontSize || rect.height < m_fFontSize) 289 return; 290 291 float fLineWidth = rect.width; 292 m_pTxtBreak->SetLineWidth(fLineWidth); 293 m_ttoLines.clear(); 294 m_wsText.clear(); 295 296 LoadText(WideString(str), rect); 297 Reload(rect); 298 DoAlignment(rect); 299 300 if (!device || m_ttoLines.empty()) 301 return; 302 303 CFX_RectF rtClip = m_Matrix.TransformRect(CFX_RectF()); 304 device->SaveState(); 305 if (rtClip.Width() > 0.0f && rtClip.Height() > 0.0f) 306 device->SetClip_Rect(rtClip); 307 308 for (auto& line : m_ttoLines) { 309 int32_t iPieces = line.GetSize(); 310 for (int32_t j = 0; j < iPieces; j++) { 311 FDE_TTOPIECE* pPiece = line.GetPtrAt(j); 312 if (!pPiece) 313 continue; 314 315 int32_t iCount = GetDisplayPos(pPiece); 316 if (iCount > 0) { 317 CFDE_TextOut::DrawString(device, m_TxtColor, m_pFont, m_CharPos.data(), 318 iCount, m_fFontSize, &m_Matrix); 319 } 320 } 321 } 322 device->RestoreState(false); 323 } 324 325 void CFDE_TextOut::LoadText(const WideString& str, const CFX_RectF& rect) { 326 ASSERT(!str.IsEmpty()); 327 328 m_wsText = str; 329 330 if (pdfium::CollectionSize<size_t>(m_CharWidths) < str.GetLength()) 331 m_CharWidths.resize(str.GetLength(), 0); 332 333 float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 334 float fLineStop = rect.bottom(); 335 m_fLinePos = rect.top; 336 int32_t iStartChar = 0; 337 int32_t iPieceWidths = 0; 338 CFX_BreakType dwBreakStatus; 339 bool bRet = false; 340 for (const auto& wch : str) { 341 dwBreakStatus = m_pTxtBreak->AppendChar(wch); 342 if (CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 343 continue; 344 345 bool bEndofLine = 346 RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect); 347 if (bEndofLine && 348 (m_Styles.line_wrap_ || dwBreakStatus == CFX_BreakType::Paragraph || 349 dwBreakStatus == CFX_BreakType::Page)) { 350 iPieceWidths = 0; 351 ++m_iCurLine; 352 m_fLinePos += fLineStep; 353 } 354 if (m_fLinePos + fLineStep > fLineStop) { 355 int32_t iCurLine = bEndofLine ? m_iCurLine - 1 : m_iCurLine; 356 m_ttoLines[iCurLine].SetNewReload(true); 357 bRet = true; 358 break; 359 } 360 } 361 362 dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph); 363 if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus) && !bRet) 364 RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, false, rect); 365 366 m_pTxtBreak->ClearBreakPieces(); 367 m_pTxtBreak->Reset(); 368 } 369 370 bool CFDE_TextOut::RetrievePieces(CFX_BreakType dwBreakStatus, 371 int32_t& iStartChar, 372 int32_t& iPieceWidths, 373 bool bReload, 374 const CFX_RectF& rect) { 375 float fLineStep = (m_fLineSpace > m_fFontSize) ? m_fLineSpace : m_fFontSize; 376 bool bNeedReload = false; 377 int32_t iLineWidth = FXSYS_round(rect.Width() * 20000.0f); 378 int32_t iCount = m_pTxtBreak->CountBreakPieces(); 379 for (int32_t i = 0; i < iCount; i++) { 380 const CFX_BreakPiece* pPiece = m_pTxtBreak->GetBreakPieceUnstable(i); 381 int32_t iPieceChars = pPiece->GetLength(); 382 int32_t iChar = iStartChar; 383 int32_t iWidth = 0; 384 int32_t j = 0; 385 for (; j < iPieceChars; j++) { 386 const CFX_Char* pTC = pPiece->GetChar(j); 387 int32_t iCurCharWidth = pTC->m_iCharWidth > 0 ? pTC->m_iCharWidth : 0; 388 if (m_Styles.single_line_ || !m_Styles.line_wrap_) { 389 if (iLineWidth - iPieceWidths - iWidth < iCurCharWidth) { 390 bNeedReload = true; 391 break; 392 } 393 } 394 iWidth += iCurCharWidth; 395 m_CharWidths[iChar++] = iCurCharWidth; 396 } 397 398 if (j == 0 && !bReload) { 399 m_ttoLines[m_iCurLine].SetNewReload(true); 400 } else if (j > 0) { 401 FDE_TTOPIECE ttoPiece; 402 ttoPiece.iStartChar = iStartChar; 403 ttoPiece.iChars = j; 404 ttoPiece.dwCharStyles = pPiece->m_dwCharStyles; 405 ttoPiece.rtPiece = CFX_RectF( 406 rect.left + static_cast<float>(pPiece->m_iStartPos) / 20000.0f, 407 m_fLinePos, iWidth / 20000.0f, fLineStep); 408 409 if (FX_IsOdd(pPiece->m_iBidiLevel)) 410 ttoPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel; 411 412 AppendPiece(ttoPiece, bNeedReload, (bReload && i == iCount - 1)); 413 } 414 iStartChar += iPieceChars; 415 iPieceWidths += iWidth; 416 } 417 m_pTxtBreak->ClearBreakPieces(); 418 419 return m_Styles.single_line_ || m_Styles.line_wrap_ || bNeedReload || 420 dwBreakStatus == CFX_BreakType::Paragraph; 421 } 422 423 void CFDE_TextOut::AppendPiece(const FDE_TTOPIECE& ttoPiece, 424 bool bNeedReload, 425 bool bEnd) { 426 if (m_iCurLine >= pdfium::CollectionSize<int32_t>(m_ttoLines)) { 427 CFDE_TTOLine ttoLine; 428 ttoLine.SetNewReload(bNeedReload); 429 430 m_iCurPiece = ttoLine.AddPiece(m_iCurPiece, ttoPiece); 431 m_ttoLines.push_back(ttoLine); 432 m_iCurLine = pdfium::CollectionSize<int32_t>(m_ttoLines) - 1; 433 } else { 434 CFDE_TTOLine* pLine = &m_ttoLines[m_iCurLine]; 435 pLine->SetNewReload(bNeedReload); 436 437 m_iCurPiece = pLine->AddPiece(m_iCurPiece, ttoPiece); 438 if (bEnd) { 439 int32_t iPieces = pLine->GetSize(); 440 if (m_iCurPiece < iPieces) 441 pLine->RemoveLast(iPieces - m_iCurPiece - 1); 442 } 443 } 444 if (!bEnd && bNeedReload) 445 m_iCurPiece = 0; 446 } 447 448 void CFDE_TextOut::Reload(const CFX_RectF& rect) { 449 int i = 0; 450 for (auto& line : m_ttoLines) { 451 if (line.GetNewReload()) { 452 m_iCurLine = i; 453 m_iCurPiece = 0; 454 ReloadLinePiece(&line, rect); 455 } 456 ++i; 457 } 458 } 459 460 void CFDE_TextOut::ReloadLinePiece(CFDE_TTOLine* pLine, const CFX_RectF& rect) { 461 const wchar_t* pwsStr = m_wsText.c_str(); 462 int32_t iPieceWidths = 0; 463 464 FDE_TTOPIECE* pPiece = pLine->GetPtrAt(0); 465 int32_t iStartChar = pPiece->iStartChar; 466 int32_t iPieceCount = pLine->GetSize(); 467 int32_t iPieceIndex = 0; 468 CFX_BreakType dwBreakStatus = CFX_BreakType::None; 469 m_fLinePos = pPiece->rtPiece.top; 470 while (iPieceIndex < iPieceCount) { 471 int32_t iStar = iStartChar; 472 int32_t iEnd = pPiece->iChars + iStar; 473 while (iStar < iEnd) { 474 dwBreakStatus = m_pTxtBreak->AppendChar(*(pwsStr + iStar)); 475 if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 476 RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect); 477 478 ++iStar; 479 } 480 ++iPieceIndex; 481 pPiece = pLine->GetPtrAt(iPieceIndex); 482 } 483 484 dwBreakStatus = m_pTxtBreak->EndBreak(CFX_BreakType::Paragraph); 485 if (!CFX_BreakTypeNoneOrPiece(dwBreakStatus)) 486 RetrievePieces(dwBreakStatus, iStartChar, iPieceWidths, true, rect); 487 488 m_pTxtBreak->Reset(); 489 } 490 491 void CFDE_TextOut::DoAlignment(const CFX_RectF& rect) { 492 if (m_ttoLines.empty()) 493 return; 494 495 FDE_TTOPIECE* pFirstPiece = m_ttoLines.back().GetPtrAt(0); 496 if (!pFirstPiece) 497 return; 498 499 float fInc = rect.bottom() - pFirstPiece->rtPiece.bottom(); 500 if (TextAlignmentVerticallyCentered(m_iAlignment)) 501 fInc /= 2.0f; 502 else if (IsTextAlignmentTop(m_iAlignment)) 503 fInc = 0.0f; 504 505 if (fInc < 1.0f) 506 return; 507 508 for (auto& line : m_ttoLines) { 509 int32_t iPieces = line.GetSize(); 510 for (int32_t j = 0; j < iPieces; j++) 511 line.GetPtrAt(j)->rtPiece.top += fInc; 512 } 513 } 514 515 int32_t CFDE_TextOut::GetDisplayPos(FDE_TTOPIECE* pPiece) { 516 ASSERT(pPiece->iChars >= 0); 517 518 if (pdfium::CollectionSize<int32_t>(m_CharPos) < pPiece->iChars) 519 m_CharPos.resize(pPiece->iChars, FXTEXT_CHARPOS()); 520 521 FX_TXTRUN tr; 522 tr.wsStr = m_wsText + pPiece->iStartChar; 523 tr.pWidths = &m_CharWidths[pPiece->iStartChar]; 524 tr.iLength = pPiece->iChars; 525 tr.pFont = m_pFont; 526 tr.fFontSize = m_fFontSize; 527 tr.dwStyles = m_dwTxtBkStyles; 528 tr.dwCharStyles = pPiece->dwCharStyles; 529 tr.pRect = &pPiece->rtPiece; 530 531 return m_pTxtBreak->GetDisplayPos(&tr, m_CharPos.data()); 532 } 533 534 CFDE_TextOut::CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {} 535 536 CFDE_TextOut::CFDE_TTOLine::CFDE_TTOLine(const CFDE_TTOLine& ttoLine) 537 : m_pieces(5) { 538 m_bNewReload = ttoLine.m_bNewReload; 539 m_pieces = ttoLine.m_pieces; 540 } 541 542 CFDE_TextOut::CFDE_TTOLine::~CFDE_TTOLine() {} 543 544 int32_t CFDE_TextOut::CFDE_TTOLine::AddPiece(int32_t index, 545 const FDE_TTOPIECE& ttoPiece) { 546 if (index >= pdfium::CollectionSize<int32_t>(m_pieces)) { 547 m_pieces.push_back(ttoPiece); 548 return pdfium::CollectionSize<int32_t>(m_pieces); 549 } 550 m_pieces[index] = ttoPiece; 551 return index; 552 } 553 554 int32_t CFDE_TextOut::CFDE_TTOLine::GetSize() const { 555 return pdfium::CollectionSize<int32_t>(m_pieces); 556 } 557 558 FDE_TTOPIECE* CFDE_TextOut::CFDE_TTOLine::GetPtrAt(int32_t index) { 559 return pdfium::IndexInBounds(m_pieces, index) ? &m_pieces[index] : nullptr; 560 } 561 562 void CFDE_TextOut::CFDE_TTOLine::RemoveLast(int32_t icount) { 563 if (icount < 0) 564 return; 565 m_pieces.erase( 566 m_pieces.end() - 567 std::min(icount, pdfium::CollectionSize<int32_t>(m_pieces)), 568 m_pieces.end()); 569 } 570