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