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
CFDE_TextOut()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
~CFDE_TextOut()49 CFDE_TextOut::~CFDE_TextOut() {}
50
SetFont(const CFX_RetainPtr<CFGAS_GEFont> & pFont)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
SetFontSize(FX_FLOAT fFontSize)57 void CFDE_TextOut::SetFontSize(FX_FLOAT fFontSize) {
58 ASSERT(fFontSize > 0);
59 m_fFontSize = fFontSize;
60 m_pTxtBreak->SetFontSize(fFontSize);
61 }
62
SetTextColor(FX_ARGB color)63 void CFDE_TextOut::SetTextColor(FX_ARGB color) {
64 m_TxtColor = color;
65 }
66
SetStyles(uint32_t dwStyles)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
SetTabWidth(FX_FLOAT fTabWidth)89 void CFDE_TextOut::SetTabWidth(FX_FLOAT fTabWidth) {
90 ASSERT(fTabWidth > 1.0f);
91 m_pTxtBreak->SetTabWidth(fTabWidth, false);
92 }
93
SetEllipsisString(const CFX_WideString & wsEllipsis)94 void CFDE_TextOut::SetEllipsisString(const CFX_WideString& wsEllipsis) {
95 m_bElliChanged = true;
96 m_wsEllipsis = wsEllipsis;
97 }
98
SetParagraphBreakChar(FX_WCHAR wch)99 void CFDE_TextOut::SetParagraphBreakChar(FX_WCHAR wch) {
100 m_wParagraphBkChar = wch;
101 m_pTxtBreak->SetParagraphBreakChar(wch);
102 }
103
SetAlignment(int32_t iAlignment)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
SetLineSpace(FX_FLOAT fLineSpace)124 void CFDE_TextOut::SetLineSpace(FX_FLOAT fLineSpace) {
125 ASSERT(fLineSpace > 1.0f);
126 m_fLineSpace = fLineSpace;
127 }
128
SetDIBitmap(CFX_DIBitmap * pDIB)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
SetRenderDevice(CFX_RenderDevice * pDevice)138 void CFDE_TextOut::SetRenderDevice(CFX_RenderDevice* pDevice) {
139 ASSERT(pDevice);
140 m_pRenderDevice = pdfium::MakeUnique<CFDE_RenderDevice>(pDevice, false);
141 }
142
SetClipRect(const CFX_Rect & rtClip)143 void CFDE_TextOut::SetClipRect(const CFX_Rect& rtClip) {
144 m_rtClip = rtClip.As<FX_FLOAT>();
145 }
146
SetClipRect(const CFX_RectF & rtClip)147 void CFDE_TextOut::SetClipRect(const CFX_RectF& rtClip) {
148 m_rtClip = rtClip;
149 }
150
SetLogicClipRect(const CFX_RectF & rtClip)151 void CFDE_TextOut::SetLogicClipRect(const CFX_RectF& rtClip) {
152 m_rtLogicClip = rtClip;
153 }
154
SetMatrix(const CFX_Matrix & matrix)155 void CFDE_TextOut::SetMatrix(const CFX_Matrix& matrix) {
156 m_Matrix = matrix;
157 }
158
SetLineBreakTolerance(FX_FLOAT fTolerance)159 void CFDE_TextOut::SetLineBreakTolerance(FX_FLOAT fTolerance) {
160 m_fTolerance = fTolerance;
161 m_pTxtBreak->SetLineBreakTolerance(m_fTolerance);
162 }
163
GetTotalLines()164 int32_t CFDE_TextOut::GetTotalLines() {
165 return m_iTotalLines;
166 }
167
CalcLogicSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_SizeF & size)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
CalcLogicSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_RectF & rect)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
CalcTextSize(const FX_WCHAR * pwsStr,int32_t iLength,CFX_RectF & rect)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
SetLineWidth(CFX_RectF & rect)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
RetrieveLineWidth(uint32_t dwBreakStatus,FX_FLOAT & fStartPos,FX_FLOAT & fWidth,FX_FLOAT & fHeight)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
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,int32_t x,int32_t y)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
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,FX_FLOAT x,FX_FLOAT y)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
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_Rect & rect)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
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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
DrawLogicText(const FX_WCHAR * pwsStr,int32_t iLength,FX_FLOAT x,FX_FLOAT y)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
DrawLogicText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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
DrawText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect,const CFX_RectF & rtClip)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
ExpandBuffer(int32_t iSize,int32_t iType)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
LoadEllipsis()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
RetrieveEllPieces(std::vector<int32_t> * pCharWidths)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
LoadText(const FX_WCHAR * pwsStr,int32_t iLength,const CFX_RectF & rect)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
RetriecePieces(uint32_t dwBreakStatus,int32_t & iStartChar,int32_t & iPieceWidths,bool bReload,const CFX_RectF & rect)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
AppendPiece(const FDE_TTOPIECE & ttoPiece,bool bNeedReload,bool bEnd)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
ReplaceWidthEllipsis()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
Reload(const CFX_RectF & rect)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
ReloadLinePiece(CFDE_TTOLine * pLine,const CFX_RectF & rect)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
DoAlignment(const CFX_RectF & rect)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
OnDraw(const CFX_RectF & rtClip)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
GetDisplayPos(FDE_TTOPIECE * pPiece)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
GetCharRects(const FDE_TTOPIECE * pPiece)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
ToTextRun(const FDE_TTOPIECE * pPiece)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
DrawLine(const FDE_TTOPIECE * pPiece,CFDE_Pen * pPen)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
CFDE_TTOLine()838 CFDE_TTOLine::CFDE_TTOLine() : m_bNewReload(false) {}
839
CFDE_TTOLine(const CFDE_TTOLine & ttoLine)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
~CFDE_TTOLine()845 CFDE_TTOLine::~CFDE_TTOLine() {}
846
AddPiece(int32_t index,const FDE_TTOPIECE & ttoPiece)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
GetSize() const856 int32_t CFDE_TTOLine::GetSize() const {
857 return pdfium::CollectionSize<int32_t>(m_pieces);
858 }
859
GetPtrAt(int32_t index)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
RemoveLast(int32_t icount)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
RemoveAll()874 void CFDE_TTOLine::RemoveAll() {
875 m_pieces.clear();
876 }
877