• 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 "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 
TextAlignmentVerticallyCentered(const FDE_TextAlignment align)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 
IsTextAlignmentTop(const FDE_TextAlignment align)29 bool IsTextAlignmentTop(const FDE_TextAlignment align) {
30   return align == FDE_TextAlignment::kTopLeft;
31 }
32 
33 }  // namespace
34 
35 // static
DrawString(CFX_RenderDevice * device,FX_ARGB color,const RetainPtr<CFGAS_GEFont> & pFont,FXTEXT_CHARPOS * pCharPos,int32_t iCount,float fFontSize,const CFX_Matrix * pMatrix)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 
CFDE_TextOut()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 
~CFDE_TextOut()142 CFDE_TextOut::~CFDE_TextOut() {}
143 
SetFont(const RetainPtr<CFGAS_GEFont> & pFont)144 void CFDE_TextOut::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) {
145   ASSERT(pFont);
146   m_pFont = pFont;
147   m_pTxtBreak->SetFont(pFont);
148 }
149 
SetFontSize(float fFontSize)150 void CFDE_TextOut::SetFontSize(float fFontSize) {
151   ASSERT(fFontSize > 0);
152   m_fFontSize = fFontSize;
153   m_pTxtBreak->SetFontSize(fFontSize);
154 }
155 
SetStyles(const FDE_TextStyle & dwStyles)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 
SetAlignment(FDE_TextAlignment iAlignment)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 
SetLineSpace(float fLineSpace)185 void CFDE_TextOut::SetLineSpace(float fLineSpace) {
186   ASSERT(fLineSpace > 1.0f);
187   m_fLineSpace = fLineSpace;
188 }
189 
SetLineBreakTolerance(float fTolerance)190 void CFDE_TextOut::SetLineBreakTolerance(float fTolerance) {
191   m_fTolerance = fTolerance;
192   m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);
193 }
194 
CalcLogicSize(const WideString & str,CFX_SizeF & size)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 
CalcLogicSize(const WideString & str,CFX_RectF & rect)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 
RetrieveLineWidth(CFX_BreakType dwBreakStatus,float & fStartPos,float & fWidth,float & fHeight)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 
DrawLogicText(CFX_RenderDevice * device,const WideStringView & str,const CFX_RectF & rect)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 
LoadText(const WideString & str,const CFX_RectF & rect)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 
RetrievePieces(CFX_BreakType dwBreakStatus,int32_t & iStartChar,int32_t & iPieceWidths,bool bReload,const CFX_RectF & rect)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 
AppendPiece(const FDE_TTOPIECE & ttoPiece,bool bNeedReload,bool bEnd)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 
Reload(const CFX_RectF & rect)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 
ReloadLinePiece(CFDE_TTOLine * pLine,const CFX_RectF & rect)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 
DoAlignment(const CFX_RectF & rect)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 
GetDisplayPos(FDE_TTOPIECE * pPiece)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 
CFDE_TTOLine()534 CFDE_TextOut::CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {}
535 
CFDE_TTOLine(const CFDE_TTOLine & ttoLine)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 
~CFDE_TTOLine()542 CFDE_TextOut::CFDE_TTOLine::~CFDE_TTOLine() {}
543 
AddPiece(int32_t index,const FDE_TTOPIECE & ttoPiece)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 
GetSize() const554 int32_t CFDE_TextOut::CFDE_TTOLine::GetSize() const {
555   return pdfium::CollectionSize<int32_t>(m_pieces);
556 }
557 
GetPtrAt(int32_t index)558 FDE_TTOPIECE* CFDE_TextOut::CFDE_TTOLine::GetPtrAt(int32_t index) {
559   return pdfium::IndexInBounds(m_pieces, index) ? &m_pieces[index] : nullptr;
560 }
561 
RemoveLast(int32_t icount)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