1 // Copyright 2017 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/fxfa/cxfa_textlayout.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "core/fxcrt/css/cfx_csscomputedstyle.h"
13 #include "core/fxcrt/css/cfx_cssstyleselector.h"
14 #include "core/fxcrt/xml/cfx_xmlelement.h"
15 #include "core/fxcrt/xml/cfx_xmlnode.h"
16 #include "core/fxcrt/xml/cfx_xmltext.h"
17 #include "core/fxge/cfx_graphstatedata.h"
18 #include "core/fxge/cfx_pathdata.h"
19 #include "core/fxge/cfx_renderdevice.h"
20 #include "core/fxge/text_char_pos.h"
21 #include "fxjs/xfa/cjx_object.h"
22 #include "third_party/base/ptr_util.h"
23 #include "third_party/base/stl_util.h"
24 #include "xfa/fde/cfde_textout.h"
25 #include "xfa/fgas/font/cfgas_gefont.h"
26 #include "xfa/fgas/layout/cfx_linkuserdata.h"
27 #include "xfa/fgas/layout/cfx_rtfbreak.h"
28 #include "xfa/fgas/layout/cfx_textuserdata.h"
29 #include "xfa/fxfa/cxfa_loadercontext.h"
30 #include "xfa/fxfa/cxfa_pieceline.h"
31 #include "xfa/fxfa/cxfa_textparsecontext.h"
32 #include "xfa/fxfa/cxfa_textpiece.h"
33 #include "xfa/fxfa/cxfa_textprovider.h"
34 #include "xfa/fxfa/cxfa_texttabstopscontext.h"
35 #include "xfa/fxfa/parser/cxfa_font.h"
36 #include "xfa/fxfa/parser/cxfa_node.h"
37 #include "xfa/fxfa/parser/cxfa_para.h"
38
39 namespace {
40
41 constexpr float kHeightTolerance = 0.001f;
42
ProcessText(WideString * pText)43 void ProcessText(WideString* pText) {
44 int32_t iLen = pText->GetLength();
45 if (iLen == 0)
46 return;
47
48 int32_t iTrimLeft = 0;
49 {
50 // Span's lifetime must end before ReleaseBuffer() below.
51 pdfium::span<wchar_t> psz = pText->GetBuffer(iLen);
52 wchar_t wPrev = 0;
53 for (int32_t i = 0; i < iLen; i++) {
54 wchar_t wch = psz[i];
55 if (wch < 0x20)
56 wch = 0x20;
57 if (wch == 0x20 && wPrev == 0x20)
58 continue;
59
60 wPrev = wch;
61 psz[iTrimLeft++] = wch;
62 }
63 }
64 pText->ReleaseBuffer(iTrimLeft);
65 }
66
67 } // namespace
68
CXFA_TextLayout(CXFA_FFDoc * doc,CXFA_TextProvider * pTextProvider)69 CXFA_TextLayout::CXFA_TextLayout(CXFA_FFDoc* doc,
70 CXFA_TextProvider* pTextProvider)
71 : m_pDoc(doc), m_pTextProvider(pTextProvider) {
72 ASSERT(m_pTextProvider);
73 }
74
~CXFA_TextLayout()75 CXFA_TextLayout::~CXFA_TextLayout() {
76 m_textParser.Reset();
77 Unload();
78 }
79
Unload()80 void CXFA_TextLayout::Unload() {
81 m_pieceLines.clear();
82 m_pBreak.reset();
83 }
84
GetTextDataNode()85 void CXFA_TextLayout::GetTextDataNode() {
86 CXFA_Node* pNode = m_pTextProvider->GetTextNode(&m_bRichText);
87 if (pNode && m_bRichText)
88 m_textParser.Reset();
89
90 m_pTextDataNode = pNode;
91 }
92
GetXMLContainerNode()93 CFX_XMLNode* CXFA_TextLayout::GetXMLContainerNode() {
94 if (!m_bRichText)
95 return nullptr;
96
97 CFX_XMLNode* pXMLRoot = m_pTextDataNode->GetXMLMappingNode();
98 if (!pXMLRoot)
99 return nullptr;
100
101 for (CFX_XMLNode* pXMLChild = pXMLRoot->GetFirstChild(); pXMLChild;
102 pXMLChild = pXMLChild->GetNextSibling()) {
103 CFX_XMLElement* pXMLElement = ToXMLElement(pXMLChild);
104 if (!pXMLElement)
105 continue;
106 WideString wsTag = pXMLElement->GetLocalTagName();
107 if (wsTag.EqualsASCII("body") || wsTag.EqualsASCII("html"))
108 return pXMLChild;
109 }
110 return nullptr;
111 }
112
CreateBreak(bool bDefault)113 std::unique_ptr<CFX_RTFBreak> CXFA_TextLayout::CreateBreak(bool bDefault) {
114 uint32_t dwStyle = FX_LAYOUTSTYLE_ExpandTab;
115 if (!bDefault)
116 dwStyle |= FX_LAYOUTSTYLE_Pagination;
117
118 auto pBreak = pdfium::MakeUnique<CFX_RTFBreak>(dwStyle);
119 pBreak->SetLineBreakTolerance(1);
120 pBreak->SetFont(m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
121 pBreak->SetFontSize(m_textParser.GetFontSize(m_pTextProvider, nullptr));
122 return pBreak;
123 }
124
InitBreak(float fLineWidth)125 void CXFA_TextLayout::InitBreak(float fLineWidth) {
126 CXFA_Para* para = m_pTextProvider->GetParaIfExists();
127 float fStart = 0;
128 float fStartPos = 0;
129 if (para) {
130 CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
131 switch (para->GetHorizontalAlign()) {
132 case XFA_AttributeValue::Center:
133 iAlign = CFX_RTFLineAlignment::Center;
134 break;
135 case XFA_AttributeValue::Right:
136 iAlign = CFX_RTFLineAlignment::Right;
137 break;
138 case XFA_AttributeValue::Justify:
139 iAlign = CFX_RTFLineAlignment::Justified;
140 break;
141 case XFA_AttributeValue::JustifyAll:
142 iAlign = CFX_RTFLineAlignment::Distributed;
143 break;
144 case XFA_AttributeValue::Left:
145 case XFA_AttributeValue::Radix:
146 break;
147 default:
148 NOTREACHED();
149 break;
150 }
151 m_pBreak->SetAlignment(iAlign);
152
153 fStart = para->GetMarginLeft();
154 if (m_pTextProvider->IsCheckButtonAndAutoWidth()) {
155 if (iAlign != CFX_RTFLineAlignment::Left)
156 fLineWidth -= para->GetMarginRight();
157 } else {
158 fLineWidth -= para->GetMarginRight();
159 }
160 if (fLineWidth < 0)
161 fLineWidth = fStart;
162
163 fStartPos = fStart;
164 float fIndent = para->GetTextIndent();
165 if (fIndent > 0)
166 fStartPos += fIndent;
167 }
168
169 m_pBreak->SetLineBoundary(fStart, fLineWidth);
170 m_pBreak->SetLineStartPos(fStartPos);
171
172 CXFA_Font* font = m_pTextProvider->GetFontIfExists();
173 if (font) {
174 m_pBreak->SetHorizontalScale(
175 static_cast<int32_t>(font->GetHorizontalScale()));
176 m_pBreak->SetVerticalScale(static_cast<int32_t>(font->GetVerticalScale()));
177 m_pBreak->SetCharSpace(font->GetLetterSpacing());
178 }
179
180 float fFontSize = m_textParser.GetFontSize(m_pTextProvider, nullptr);
181 m_pBreak->SetFontSize(fFontSize);
182 m_pBreak->SetFont(
183 m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, nullptr));
184 m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
185 }
186
InitBreak(CFX_CSSComputedStyle * pStyle,CFX_CSSDisplay eDisplay,float fLineWidth,const CFX_XMLNode * pXMLNode,CFX_CSSComputedStyle * pParentStyle)187 void CXFA_TextLayout::InitBreak(CFX_CSSComputedStyle* pStyle,
188 CFX_CSSDisplay eDisplay,
189 float fLineWidth,
190 const CFX_XMLNode* pXMLNode,
191 CFX_CSSComputedStyle* pParentStyle) {
192 if (!pStyle) {
193 InitBreak(fLineWidth);
194 return;
195 }
196
197 if (eDisplay == CFX_CSSDisplay::Block ||
198 eDisplay == CFX_CSSDisplay::ListItem) {
199 CFX_RTFLineAlignment iAlign = CFX_RTFLineAlignment::Left;
200 switch (pStyle->GetTextAlign()) {
201 case CFX_CSSTextAlign::Right:
202 iAlign = CFX_RTFLineAlignment::Right;
203 break;
204 case CFX_CSSTextAlign::Center:
205 iAlign = CFX_RTFLineAlignment::Center;
206 break;
207 case CFX_CSSTextAlign::Justify:
208 iAlign = CFX_RTFLineAlignment::Justified;
209 break;
210 case CFX_CSSTextAlign::JustifyAll:
211 iAlign = CFX_RTFLineAlignment::Distributed;
212 break;
213 default:
214 break;
215 }
216 m_pBreak->SetAlignment(iAlign);
217
218 float fStart = 0;
219 const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
220 const CFX_CSSRect* pPaddingRect = pStyle->GetPaddingWidth();
221 if (pRect) {
222 fStart = pRect->left.GetValue();
223 fLineWidth -= pRect->right.GetValue();
224 if (pPaddingRect) {
225 fStart += pPaddingRect->left.GetValue();
226 fLineWidth -= pPaddingRect->right.GetValue();
227 }
228 if (eDisplay == CFX_CSSDisplay::ListItem) {
229 const CFX_CSSRect* pParRect = pParentStyle->GetMarginWidth();
230 const CFX_CSSRect* pParPaddingRect = pParentStyle->GetPaddingWidth();
231 if (pParRect) {
232 fStart += pParRect->left.GetValue();
233 fLineWidth -= pParRect->right.GetValue();
234 if (pParPaddingRect) {
235 fStart += pParPaddingRect->left.GetValue();
236 fLineWidth -= pParPaddingRect->right.GetValue();
237 }
238 }
239 CFX_CSSRect pNewRect;
240 pNewRect.left.Set(CFX_CSSLengthUnit::Point, fStart);
241 pNewRect.right.Set(CFX_CSSLengthUnit::Point, pRect->right.GetValue());
242 pNewRect.top.Set(CFX_CSSLengthUnit::Point, pRect->top.GetValue());
243 pNewRect.bottom.Set(CFX_CSSLengthUnit::Point, pRect->bottom.GetValue());
244 pStyle->SetMarginWidth(pNewRect);
245 }
246 }
247 m_pBreak->SetLineBoundary(fStart, fLineWidth);
248 float fIndent = pStyle->GetTextIndent().GetValue();
249 if (fIndent > 0)
250 fStart += fIndent;
251
252 m_pBreak->SetLineStartPos(fStart);
253 m_pBreak->SetTabWidth(m_textParser.GetTabInterval(pStyle));
254 if (!m_pTabstopContext)
255 m_pTabstopContext = pdfium::MakeUnique<CXFA_TextTabstopsContext>();
256 m_textParser.GetTabstops(pStyle, m_pTabstopContext.get());
257 for (const auto& stop : m_pTabstopContext->m_tabstops)
258 m_pBreak->AddPositionedTab(stop.fTabstops);
259 }
260 float fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle);
261 m_pBreak->SetFontSize(fFontSize);
262 m_pBreak->SetLineBreakTolerance(fFontSize * 0.2f);
263 m_pBreak->SetFont(
264 m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle));
265 m_pBreak->SetHorizontalScale(
266 m_textParser.GetHorScale(m_pTextProvider, pStyle, pXMLNode));
267 m_pBreak->SetVerticalScale(m_textParser.GetVerScale(m_pTextProvider, pStyle));
268 m_pBreak->SetCharSpace(pStyle->GetLetterSpacing().GetValue());
269 }
270
GetLayoutHeight()271 float CXFA_TextLayout::GetLayoutHeight() {
272 if (!m_pLoader)
273 return 0;
274
275 if (m_pLoader->lineHeights.empty() && m_pLoader->fWidth > 0) {
276 CFX_SizeF szMax(m_pLoader->fWidth, m_pLoader->fHeight);
277 m_pLoader->bSaveLineHeight = true;
278 m_pLoader->fLastPos = 0;
279 CFX_SizeF szDef = CalcSize(szMax, szMax);
280 m_pLoader->bSaveLineHeight = false;
281 return szDef.height;
282 }
283
284 float fHeight = m_pLoader->fHeight;
285 if (fHeight < 0.1f) {
286 fHeight = 0;
287 for (float value : m_pLoader->lineHeights)
288 fHeight += value;
289 }
290 return fHeight;
291 }
292
StartLayout(float fWidth)293 float CXFA_TextLayout::StartLayout(float fWidth) {
294 if (!m_pLoader)
295 m_pLoader = pdfium::MakeUnique<CXFA_LoaderContext>();
296
297 if (fWidth < 0 ||
298 (m_pLoader->fWidth > -1 && fabs(fWidth - m_pLoader->fWidth) > 0)) {
299 m_pLoader->lineHeights.clear();
300 m_Blocks.clear();
301 Unload();
302 m_pLoader->fStartLineOffset = 0;
303 }
304 m_pLoader->fWidth = fWidth;
305
306 if (fWidth >= 0)
307 return fWidth;
308
309 CFX_SizeF szMax;
310
311 m_pLoader->bSaveLineHeight = true;
312 m_pLoader->fLastPos = 0;
313 CFX_SizeF szDef = CalcSize(szMax, szMax);
314 m_pLoader->bSaveLineHeight = false;
315 return szDef.width;
316 }
317
DoLayout(float fTextHeight)318 float CXFA_TextLayout::DoLayout(float fTextHeight) {
319 if (!m_pLoader)
320 return fTextHeight;
321
322 UpdateLoaderHeight(fTextHeight);
323 return fTextHeight;
324 }
325
DoSplitLayout(size_t szBlockIndex,float fCalcHeight,float fTextHeight)326 float CXFA_TextLayout::DoSplitLayout(size_t szBlockIndex,
327 float fCalcHeight,
328 float fTextHeight) {
329 if (!m_pLoader)
330 return fCalcHeight;
331
332 UpdateLoaderHeight(fTextHeight);
333
334 if (fCalcHeight < 0)
335 return fCalcHeight;
336
337 m_bHasBlock = true;
338 if (m_Blocks.empty() && m_pLoader->fHeight > 0) {
339 float fHeight = fTextHeight - GetLayoutHeight();
340 if (fHeight > 0) {
341 XFA_AttributeValue iAlign = m_textParser.GetVAlign(m_pTextProvider);
342 if (iAlign == XFA_AttributeValue::Middle)
343 fHeight /= 2.0f;
344 else if (iAlign != XFA_AttributeValue::Bottom)
345 fHeight = 0;
346 m_pLoader->fStartLineOffset = fHeight;
347 }
348 }
349
350 float fLinePos = m_pLoader->fStartLineOffset;
351 size_t szLineIndex = 0;
352 if (!m_Blocks.empty()) {
353 if (szBlockIndex < m_Blocks.size())
354 szLineIndex = m_Blocks[szBlockIndex].szIndex;
355 else
356 szLineIndex = GetNextIndexFromLastBlockData();
357 for (size_t i = 0;
358 i < std::min(szBlockIndex, m_pLoader->blockHeights.size()); ++i) {
359 fLinePos -= m_pLoader->blockHeights[i].fHeight;
360 }
361 }
362
363 if (szLineIndex >= m_pLoader->lineHeights.size())
364 return fCalcHeight;
365
366 if (m_pLoader->lineHeights[szLineIndex] - fCalcHeight > kHeightTolerance)
367 return 0;
368
369 for (size_t i = szLineIndex; i < m_pLoader->lineHeights.size(); ++i) {
370 float fLineHeight = m_pLoader->lineHeights[i];
371 if (fLinePos + fLineHeight - fCalcHeight <= kHeightTolerance) {
372 fLinePos += fLineHeight;
373 continue;
374 }
375
376 if (szBlockIndex < m_Blocks.size())
377 m_Blocks[szBlockIndex] = {szLineIndex, i - szLineIndex};
378 else
379 m_Blocks.push_back({szLineIndex, i - szLineIndex});
380
381 if (i != szLineIndex)
382 return fLinePos;
383
384 if (fCalcHeight > fLinePos)
385 return fCalcHeight;
386
387 if (szBlockIndex < m_pLoader->blockHeights.size() &&
388 m_pLoader->blockHeights[szBlockIndex].szBlockIndex == szBlockIndex) {
389 m_pLoader->blockHeights[szBlockIndex].fHeight = fCalcHeight;
390 } else {
391 m_pLoader->blockHeights.push_back({szBlockIndex, fCalcHeight});
392 }
393 return fCalcHeight;
394 }
395 return fCalcHeight;
396 }
397
CountBlocks() const398 size_t CXFA_TextLayout::CountBlocks() const {
399 size_t szCount = m_Blocks.size();
400 return szCount > 0 ? szCount : 1;
401 }
402
GetNextIndexFromLastBlockData() const403 size_t CXFA_TextLayout::GetNextIndexFromLastBlockData() const {
404 return m_Blocks.back().szIndex + m_Blocks.back().szLength;
405 }
406
UpdateLoaderHeight(float fTextHeight)407 void CXFA_TextLayout::UpdateLoaderHeight(float fTextHeight) {
408 m_pLoader->fHeight = fTextHeight;
409 if (m_pLoader->fHeight < 0)
410 m_pLoader->fHeight = GetLayoutHeight();
411 }
412
CalcSize(const CFX_SizeF & minSize,const CFX_SizeF & maxSize)413 CFX_SizeF CXFA_TextLayout::CalcSize(const CFX_SizeF& minSize,
414 const CFX_SizeF& maxSize) {
415 float width = maxSize.width;
416 if (width < 1)
417 width = 0xFFFF;
418
419 m_pBreak = CreateBreak(false);
420 float fLinePos = 0;
421 m_iLines = 0;
422 m_fMaxWidth = 0;
423 Loader(width, &fLinePos, false);
424 if (fLinePos < 0.1f)
425 fLinePos = m_textParser.GetFontSize(m_pTextProvider, nullptr);
426
427 m_pTabstopContext.reset();
428 return CFX_SizeF(m_fMaxWidth, fLinePos);
429 }
430
Layout(const CFX_SizeF & size)431 float CXFA_TextLayout::Layout(const CFX_SizeF& size) {
432 if (size.width < 1)
433 return 0.f;
434
435 Unload();
436 m_pBreak = CreateBreak(true);
437 if (m_pLoader) {
438 m_pLoader->iTotalLines = -1;
439 m_pLoader->iChar = 0;
440 }
441
442 m_iLines = 0;
443 float fLinePos = 0;
444 Loader(size.width, &fLinePos, true);
445 UpdateAlign(size.height, fLinePos);
446 m_pTabstopContext.reset();
447 return fLinePos;
448 }
449
LayoutInternal(size_t szBlockIndex)450 bool CXFA_TextLayout::LayoutInternal(size_t szBlockIndex) {
451 ASSERT(szBlockIndex < CountBlocks());
452
453 if (!m_pLoader || m_pLoader->fWidth < 1)
454 return false;
455
456 m_pLoader->iTotalLines = -1;
457 m_iLines = 0;
458 float fLinePos = 0;
459 CXFA_Node* pNode = nullptr;
460 CFX_SizeF szText(m_pLoader->fWidth, m_pLoader->fHeight);
461 if (szBlockIndex < m_pLoader->blockHeights.size())
462 return true;
463 if (szBlockIndex == m_pLoader->blockHeights.size()) {
464 Unload();
465 m_pBreak = CreateBreak(true);
466 fLinePos = m_pLoader->fStartLineOffset;
467 for (size_t i = 0; i < m_pLoader->blockHeights.size(); ++i)
468 fLinePos -= m_pLoader->blockHeights[i].fHeight;
469
470 m_pLoader->iChar = 0;
471 if (!m_Blocks.empty())
472 m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
473
474 Loader(szText.width, &fLinePos, true);
475 if (m_Blocks.empty() && m_pLoader->fStartLineOffset < 0.1f)
476 UpdateAlign(szText.height, fLinePos);
477 } else if (m_pTextDataNode) {
478 if (!m_Blocks.empty() && szBlockIndex < m_Blocks.size() - 1)
479 m_pLoader->iTotalLines = m_Blocks[szBlockIndex].szLength;
480
481 m_pBreak->Reset();
482 if (m_bRichText) {
483 CFX_XMLNode* pContainerNode = GetXMLContainerNode();
484 if (!pContainerNode)
485 return true;
486
487 const CFX_XMLNode* pXMLNode = m_pLoader->pXMLNode.Get();
488 if (!pXMLNode)
489 return true;
490
491 const CFX_XMLNode* pSaveXMLNode = pXMLNode;
492 for (; pXMLNode; pXMLNode = pXMLNode->GetNextSibling()) {
493 if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
494 m_pLoader->pParentStyle, true, nullptr, true, false,
495 0)) {
496 break;
497 }
498 }
499 while (!pXMLNode) {
500 pXMLNode = pSaveXMLNode->GetParent();
501 if (pXMLNode == pContainerNode)
502 break;
503 if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
504 m_pLoader->pParentStyle, true, nullptr, false, false,
505 0)) {
506 break;
507 }
508 pSaveXMLNode = pXMLNode;
509 pXMLNode = pXMLNode->GetNextSibling();
510 if (!pXMLNode)
511 continue;
512 for (; pXMLNode; pXMLNode = pXMLNode->GetNextSibling()) {
513 if (!LoadRichText(pXMLNode, szText.width, &fLinePos,
514 m_pLoader->pParentStyle, true, nullptr, true, false,
515 0)) {
516 break;
517 }
518 }
519 }
520 } else {
521 pNode = m_pLoader->pNode.Get();
522 if (!pNode)
523 return true;
524 LoadText(pNode, szText.width, &fLinePos, true);
525 }
526 }
527 if (szBlockIndex == m_Blocks.size()) {
528 m_pTabstopContext.reset();
529 m_pLoader.reset();
530 }
531 return true;
532 }
533
ItemBlocks(const CFX_RectF & rtText,size_t szBlockIndex)534 void CXFA_TextLayout::ItemBlocks(const CFX_RectF& rtText, size_t szBlockIndex) {
535 if (!m_pLoader)
536 return;
537
538 if (m_pLoader->lineHeights.empty())
539 return;
540
541 float fLinePos = m_pLoader->fStartLineOffset;
542 size_t szLineIndex = 0;
543 if (szBlockIndex > 0) {
544 if (szBlockIndex <= m_pLoader->blockHeights.size()) {
545 for (size_t i = 0; i < szBlockIndex; ++i)
546 fLinePos -= m_pLoader->blockHeights[i].fHeight;
547 } else {
548 fLinePos = 0;
549 }
550 szLineIndex = GetNextIndexFromLastBlockData();
551 }
552
553 size_t i;
554 for (i = szLineIndex; i < m_pLoader->lineHeights.size(); ++i) {
555 float fLineHeight = m_pLoader->lineHeights[i];
556 if (fLinePos + fLineHeight - rtText.height > kHeightTolerance) {
557 m_Blocks.push_back({szLineIndex, i - szLineIndex});
558 return;
559 }
560 fLinePos += fLineHeight;
561 }
562 if (i > szLineIndex)
563 m_Blocks.push_back({szLineIndex, i - szLineIndex});
564 }
565
DrawString(CFX_RenderDevice * pFxDevice,const CFX_Matrix & mtDoc2Device,const CFX_RectF & rtClip,size_t szBlockIndex)566 bool CXFA_TextLayout::DrawString(CFX_RenderDevice* pFxDevice,
567 const CFX_Matrix& mtDoc2Device,
568 const CFX_RectF& rtClip,
569 size_t szBlockIndex) {
570 if (!pFxDevice)
571 return false;
572
573 pFxDevice->SaveState();
574 pFxDevice->SetClip_Rect(rtClip.GetOuterRect());
575
576 if (m_pieceLines.empty()) {
577 size_t szBlockCount = CountBlocks();
578 for (size_t i = 0; i < szBlockCount; ++i)
579 LayoutInternal(i);
580 }
581
582 std::vector<TextCharPos> char_pos(1);
583 size_t szLineStart = 0;
584 size_t szPieceLines = m_pieceLines.size();
585 if (!m_Blocks.empty()) {
586 if (szBlockIndex < m_Blocks.size()) {
587 szLineStart = m_Blocks[szBlockIndex].szIndex;
588 szPieceLines = m_Blocks[szBlockIndex].szLength;
589 } else {
590 szPieceLines = 0;
591 }
592 }
593
594 for (size_t i = 0; i < szPieceLines; ++i) {
595 if (i + szLineStart >= m_pieceLines.size())
596 break;
597
598 CXFA_PieceLine* pPieceLine = m_pieceLines[i + szLineStart].get();
599 for (size_t j = 0; j < pPieceLine->m_textPieces.size(); ++j) {
600 const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[j].get();
601 int32_t iChars = pPiece->iChars;
602 if (pdfium::CollectionSize<int32_t>(char_pos) < iChars)
603 char_pos.resize(iChars);
604 RenderString(pFxDevice, pPieceLine, j, &char_pos, mtDoc2Device);
605 }
606 for (size_t j = 0; j < pPieceLine->m_textPieces.size(); ++j)
607 RenderPath(pFxDevice, pPieceLine, j, &char_pos, mtDoc2Device);
608 }
609 pFxDevice->RestoreState(false);
610 return szPieceLines > 0;
611 }
612
UpdateAlign(float fHeight,float fBottom)613 void CXFA_TextLayout::UpdateAlign(float fHeight, float fBottom) {
614 fHeight -= fBottom;
615 if (fHeight < 0.1f)
616 return;
617
618 switch (m_textParser.GetVAlign(m_pTextProvider)) {
619 case XFA_AttributeValue::Middle:
620 fHeight /= 2.0f;
621 break;
622 case XFA_AttributeValue::Bottom:
623 break;
624 default:
625 return;
626 }
627
628 for (const auto& pPieceLine : m_pieceLines) {
629 for (const auto& pPiece : pPieceLine->m_textPieces)
630 pPiece->rtPiece.top += fHeight;
631 }
632 }
633
Loader(float textWidth,float * pLinePos,bool bSavePieces)634 void CXFA_TextLayout::Loader(float textWidth,
635 float* pLinePos,
636 bool bSavePieces) {
637 GetTextDataNode();
638 if (!m_pTextDataNode)
639 return;
640
641 if (!m_bRichText) {
642 LoadText(m_pTextDataNode, textWidth, pLinePos, bSavePieces);
643 return;
644 }
645
646 const CFX_XMLNode* pXMLContainer = GetXMLContainerNode();
647 if (!pXMLContainer)
648 return;
649
650 if (!m_textParser.IsParsed())
651 m_textParser.DoParse(pXMLContainer, m_pTextProvider);
652
653 auto pRootStyle = m_textParser.CreateRootStyle(m_pTextProvider);
654 LoadRichText(pXMLContainer, textWidth, pLinePos, pRootStyle, bSavePieces,
655 nullptr, true, false, 0);
656 }
657
LoadText(CXFA_Node * pNode,float textWidth,float * pLinePos,bool bSavePieces)658 void CXFA_TextLayout::LoadText(CXFA_Node* pNode,
659 float textWidth,
660 float* pLinePos,
661 bool bSavePieces) {
662 InitBreak(textWidth);
663
664 CXFA_Para* para = m_pTextProvider->GetParaIfExists();
665 float fSpaceAbove = 0;
666 if (para) {
667 fSpaceAbove = para->GetSpaceAbove();
668 if (fSpaceAbove < 0.1f)
669 fSpaceAbove = 0;
670
671 switch (para->GetVerticalAlign()) {
672 case XFA_AttributeValue::Top:
673 case XFA_AttributeValue::Middle:
674 case XFA_AttributeValue::Bottom: {
675 *pLinePos += fSpaceAbove;
676 break;
677 }
678 default:
679 NOTREACHED();
680 break;
681 }
682 }
683
684 WideString wsText = pNode->JSObject()->GetContent(false);
685 wsText.TrimRight(L" ");
686 bool bRet = AppendChar(wsText, pLinePos, fSpaceAbove, bSavePieces);
687 if (bRet && m_pLoader)
688 m_pLoader->pNode = pNode;
689 else
690 EndBreak(CFX_BreakType::Paragraph, pLinePos, bSavePieces);
691 }
692
LoadRichText(const CFX_XMLNode * pXMLNode,float textWidth,float * pLinePos,const RetainPtr<CFX_CSSComputedStyle> & pParentStyle,bool bSavePieces,RetainPtr<CFX_LinkUserData> pLinkData,bool bEndBreak,bool bIsOl,int32_t iLiCount)693 bool CXFA_TextLayout::LoadRichText(
694 const CFX_XMLNode* pXMLNode,
695 float textWidth,
696 float* pLinePos,
697 const RetainPtr<CFX_CSSComputedStyle>& pParentStyle,
698 bool bSavePieces,
699 RetainPtr<CFX_LinkUserData> pLinkData,
700 bool bEndBreak,
701 bool bIsOl,
702 int32_t iLiCount) {
703 if (!pXMLNode)
704 return false;
705
706 CXFA_TextParseContext* pContext =
707 m_textParser.GetParseContextFromMap(pXMLNode);
708 CFX_CSSDisplay eDisplay = CFX_CSSDisplay::None;
709 bool bContentNode = false;
710 float fSpaceBelow = 0;
711 RetainPtr<CFX_CSSComputedStyle> pStyle;
712 WideString wsName;
713 if (bEndBreak) {
714 bool bCurOl = false;
715 bool bCurLi = false;
716 const CFX_XMLElement* pElement = nullptr;
717 if (pContext) {
718 if (m_bBlockContinue || (m_pLoader && pXMLNode == m_pLoader->pXMLNode)) {
719 m_bBlockContinue = true;
720 }
721 if (pXMLNode->GetType() == CFX_XMLNode::Type::kText) {
722 bContentNode = true;
723 } else if (pXMLNode->GetType() == CFX_XMLNode::Type::kElement) {
724 pElement = static_cast<const CFX_XMLElement*>(pXMLNode);
725 wsName = pElement->GetLocalTagName();
726 }
727 if (wsName.EqualsASCII("ol")) {
728 bIsOl = true;
729 bCurOl = true;
730 }
731 if (m_bBlockContinue || !bContentNode) {
732 eDisplay = pContext->GetDisplay();
733 if (eDisplay != CFX_CSSDisplay::Block &&
734 eDisplay != CFX_CSSDisplay::Inline &&
735 eDisplay != CFX_CSSDisplay::ListItem) {
736 return true;
737 }
738
739 pStyle = m_textParser.ComputeStyle(pXMLNode, pParentStyle.Get());
740 InitBreak(bContentNode ? pParentStyle.Get() : pStyle.Get(), eDisplay,
741 textWidth, pXMLNode, pParentStyle.Get());
742 if ((eDisplay == CFX_CSSDisplay::Block ||
743 eDisplay == CFX_CSSDisplay::ListItem) &&
744 pStyle &&
745 (wsName.IsEmpty() ||
746 !(wsName.EqualsASCII("body") || wsName.EqualsASCII("html") ||
747 wsName.EqualsASCII("ol") || wsName.EqualsASCII("ul")))) {
748 const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
749 if (pRect) {
750 *pLinePos += pRect->top.GetValue();
751 fSpaceBelow = pRect->bottom.GetValue();
752 }
753 }
754
755 if (wsName.EqualsASCII("a")) {
756 WideString wsLinkContent = pElement->GetAttribute(L"href");
757 if (!wsLinkContent.IsEmpty())
758 pLinkData = pdfium::MakeRetain<CFX_LinkUserData>(wsLinkContent);
759 }
760
761 int32_t iTabCount = m_textParser.CountTabs(
762 bContentNode ? pParentStyle.Get() : pStyle.Get());
763 bool bSpaceRun = m_textParser.IsSpaceRun(
764 bContentNode ? pParentStyle.Get() : pStyle.Get());
765 WideString wsText;
766 if (bContentNode && iTabCount == 0) {
767 wsText = ToXMLText(pXMLNode)->GetText();
768 } else if (wsName.EqualsASCII("br")) {
769 wsText = L'\n';
770 } else if (wsName.EqualsASCII("li")) {
771 bCurLi = true;
772 if (bIsOl)
773 wsText = WideString::Format(L"%d. ", iLiCount);
774 else
775 wsText = 0x00B7 + WideStringView(L" ", 1);
776 } else if (!bContentNode) {
777 if (iTabCount > 0) {
778 while (iTabCount-- > 0)
779 wsText += L'\t';
780 } else {
781 Optional<WideString> obj =
782 m_textParser.GetEmbeddedObj(m_pTextProvider, pXMLNode);
783 if (obj)
784 wsText = *obj;
785 }
786 }
787
788 int32_t iLength = wsText.GetLength();
789 if (iLength > 0 && bContentNode && !bSpaceRun)
790 ProcessText(&wsText);
791
792 if (m_pLoader) {
793 if (wsText.GetLength() > 0 && m_pLoader->bFilterSpace) {
794 wsText.TrimLeft(L" ");
795 }
796 if (CFX_CSSDisplay::Block == eDisplay) {
797 m_pLoader->bFilterSpace = true;
798 } else if (CFX_CSSDisplay::Inline == eDisplay &&
799 m_pLoader->bFilterSpace) {
800 m_pLoader->bFilterSpace = false;
801 } else if (wsText.GetLength() > 0 && wsText.Back() == 0x20) {
802 m_pLoader->bFilterSpace = true;
803 } else if (wsText.GetLength() != 0) {
804 m_pLoader->bFilterSpace = false;
805 }
806 }
807
808 if (wsText.GetLength() > 0) {
809 if (!m_pLoader || m_pLoader->iChar == 0) {
810 auto pUserData = pdfium::MakeRetain<CFX_TextUserData>(
811 bContentNode ? pParentStyle : pStyle, pLinkData);
812 m_pBreak->SetUserData(pUserData);
813 }
814
815 if (AppendChar(wsText, pLinePos, 0, bSavePieces)) {
816 if (m_pLoader)
817 m_pLoader->bFilterSpace = false;
818 if (!IsEnd(bSavePieces))
819 return true;
820 if (m_pLoader && m_pLoader->iTotalLines > -1) {
821 m_pLoader->pXMLNode = pXMLNode;
822 m_pLoader->pParentStyle = pParentStyle;
823 }
824 return false;
825 }
826 }
827 }
828 }
829
830 for (CFX_XMLNode* pChildNode = pXMLNode->GetFirstChild(); pChildNode;
831 pChildNode = pChildNode->GetNextSibling()) {
832 if (bCurOl)
833 iLiCount++;
834
835 if (!LoadRichText(pChildNode, textWidth, pLinePos,
836 pContext ? pStyle : pParentStyle, bSavePieces,
837 pLinkData, true, bIsOl, iLiCount))
838 return false;
839 }
840
841 if (m_pLoader) {
842 if (CFX_CSSDisplay::Block == eDisplay)
843 m_pLoader->bFilterSpace = true;
844 }
845 if (bCurLi)
846 EndBreak(CFX_BreakType::Line, pLinePos, bSavePieces);
847 } else {
848 if (pContext)
849 eDisplay = pContext->GetDisplay();
850 }
851
852 if (m_bBlockContinue) {
853 if (pContext && !bContentNode) {
854 CFX_BreakType dwStatus = (eDisplay == CFX_CSSDisplay::Block)
855 ? CFX_BreakType::Paragraph
856 : CFX_BreakType::Piece;
857 EndBreak(dwStatus, pLinePos, bSavePieces);
858 if (eDisplay == CFX_CSSDisplay::Block) {
859 *pLinePos += fSpaceBelow;
860 if (m_pTabstopContext)
861 m_pTabstopContext->RemoveAll();
862 }
863 if (IsEnd(bSavePieces)) {
864 if (m_pLoader && m_pLoader->iTotalLines > -1) {
865 m_pLoader->pXMLNode = pXMLNode->GetNextSibling();
866 m_pLoader->pParentStyle = pParentStyle;
867 }
868 return false;
869 }
870 }
871 }
872 return true;
873 }
874
AppendChar(const WideString & wsText,float * pLinePos,float fSpaceAbove,bool bSavePieces)875 bool CXFA_TextLayout::AppendChar(const WideString& wsText,
876 float* pLinePos,
877 float fSpaceAbove,
878 bool bSavePieces) {
879 CFX_BreakType dwStatus = CFX_BreakType::None;
880 int32_t iChar = 0;
881 if (m_pLoader)
882 iChar = m_pLoader->iChar;
883
884 int32_t iLength = wsText.GetLength();
885 for (int32_t i = iChar; i < iLength; i++) {
886 wchar_t wch = wsText[i];
887 if (wch == 0xA0)
888 wch = 0x20;
889
890 dwStatus = m_pBreak->AppendChar(wch);
891 if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece) {
892 AppendTextLine(dwStatus, pLinePos, bSavePieces, false);
893 if (IsEnd(bSavePieces)) {
894 if (m_pLoader)
895 m_pLoader->iChar = i;
896 return true;
897 }
898 if (dwStatus == CFX_BreakType::Paragraph && m_bRichText)
899 *pLinePos += fSpaceAbove;
900 }
901 }
902 if (m_pLoader)
903 m_pLoader->iChar = 0;
904
905 return false;
906 }
907
IsEnd(bool bSavePieces)908 bool CXFA_TextLayout::IsEnd(bool bSavePieces) {
909 if (!bSavePieces)
910 return false;
911 if (m_pLoader && m_pLoader->iTotalLines > 0)
912 return m_iLines >= m_pLoader->iTotalLines;
913 return false;
914 }
915
EndBreak(CFX_BreakType dwStatus,float * pLinePos,bool bSavePieces)916 void CXFA_TextLayout::EndBreak(CFX_BreakType dwStatus,
917 float* pLinePos,
918 bool bSavePieces) {
919 dwStatus = m_pBreak->EndBreak(dwStatus);
920 if (dwStatus != CFX_BreakType::None && dwStatus != CFX_BreakType::Piece)
921 AppendTextLine(dwStatus, pLinePos, bSavePieces, true);
922 }
923
DoTabstops(CFX_CSSComputedStyle * pStyle,CXFA_PieceLine * pPieceLine)924 void CXFA_TextLayout::DoTabstops(CFX_CSSComputedStyle* pStyle,
925 CXFA_PieceLine* pPieceLine) {
926 if (!pStyle || !pPieceLine)
927 return;
928
929 if (!m_pTabstopContext || m_pTabstopContext->m_tabstops.empty())
930 return;
931
932 int32_t iPieces = pdfium::CollectionSize<int32_t>(pPieceLine->m_textPieces);
933 if (iPieces == 0)
934 return;
935
936 CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[iPieces - 1].get();
937 int32_t& iTabstopsIndex = m_pTabstopContext->m_iTabIndex;
938 int32_t iCount = m_textParser.CountTabs(pStyle);
939 if (!pdfium::IndexInBounds(m_pTabstopContext->m_tabstops, iTabstopsIndex))
940 return;
941
942 if (iCount > 0) {
943 iTabstopsIndex++;
944 m_pTabstopContext->m_bTabstops = true;
945 float fRight = 0;
946 if (iPieces > 1) {
947 CXFA_TextPiece* p = pPieceLine->m_textPieces[iPieces - 2].get();
948 fRight = p->rtPiece.right();
949 }
950 m_pTabstopContext->m_fTabWidth =
951 pPiece->rtPiece.width + pPiece->rtPiece.left - fRight;
952 } else if (iTabstopsIndex > -1) {
953 float fLeft = 0;
954 if (m_pTabstopContext->m_bTabstops) {
955 uint32_t dwAlign = m_pTabstopContext->m_tabstops[iTabstopsIndex].dwAlign;
956 if (dwAlign == FX_HashCode_GetW(L"center", false)) {
957 fLeft = pPiece->rtPiece.width / 2.0f;
958 } else if (dwAlign == FX_HashCode_GetW(L"right", false) ||
959 dwAlign == FX_HashCode_GetW(L"before", false)) {
960 fLeft = pPiece->rtPiece.width;
961 } else if (dwAlign == FX_HashCode_GetW(L"decimal", false)) {
962 int32_t iChars = pPiece->iChars;
963 for (int32_t i = 0; i < iChars; i++) {
964 if (pPiece->szText[i] == L'.')
965 break;
966
967 fLeft += pPiece->Widths[i] / 20000.0f;
968 }
969 }
970 m_pTabstopContext->m_fLeft =
971 std::min(fLeft, m_pTabstopContext->m_fTabWidth);
972 m_pTabstopContext->m_bTabstops = false;
973 m_pTabstopContext->m_fTabWidth = 0;
974 }
975 pPiece->rtPiece.left -= m_pTabstopContext->m_fLeft;
976 }
977 }
978
AppendTextLine(CFX_BreakType dwStatus,float * pLinePos,bool bSavePieces,bool bEndBreak)979 void CXFA_TextLayout::AppendTextLine(CFX_BreakType dwStatus,
980 float* pLinePos,
981 bool bSavePieces,
982 bool bEndBreak) {
983 int32_t iPieces = m_pBreak->CountBreakPieces();
984 if (iPieces < 1)
985 return;
986
987 RetainPtr<CFX_CSSComputedStyle> pStyle;
988 if (bSavePieces) {
989 auto pNew = pdfium::MakeUnique<CXFA_PieceLine>();
990 CXFA_PieceLine* pPieceLine = pNew.get();
991 m_pieceLines.push_back(std::move(pNew));
992 if (m_pTabstopContext)
993 m_pTabstopContext->Reset();
994
995 float fLineStep = 0, fBaseLine = 0;
996 int32_t i = 0;
997 for (i = 0; i < iPieces; i++) {
998 const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
999 CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
1000 if (pUserData)
1001 pStyle = pUserData->m_pStyle;
1002 float fVerScale = pPiece->m_iVerticalScale / 100.0f;
1003
1004 auto pTP = pdfium::MakeUnique<CXFA_TextPiece>();
1005 pTP->iChars = pPiece->m_iChars;
1006 pTP->szText = pPiece->GetString();
1007 pTP->Widths = pPiece->GetWidths();
1008 pTP->iBidiLevel = pPiece->m_iBidiLevel;
1009 pTP->iHorScale = pPiece->m_iHorizontalScale;
1010 pTP->iVerScale = pPiece->m_iVerticalScale;
1011 m_textParser.GetUnderline(m_pTextProvider, pStyle.Get(), pTP->iUnderline,
1012 pTP->iPeriod);
1013 m_textParser.GetLinethrough(m_pTextProvider, pStyle.Get(),
1014 pTP->iLineThrough);
1015 pTP->dwColor = m_textParser.GetColor(m_pTextProvider, pStyle.Get());
1016 pTP->pFont =
1017 m_textParser.GetFont(m_pDoc.Get(), m_pTextProvider, pStyle.Get());
1018 pTP->fFontSize = m_textParser.GetFontSize(m_pTextProvider, pStyle.Get());
1019 pTP->rtPiece.left = pPiece->m_iStartPos / 20000.0f;
1020 pTP->rtPiece.width = pPiece->m_iWidth / 20000.0f;
1021 pTP->rtPiece.height =
1022 static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
1023 float fBaseLineTemp =
1024 m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
1025 pTP->rtPiece.top = fBaseLineTemp;
1026
1027 float fLineHeight = m_textParser.GetLineHeight(
1028 m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
1029 if (fBaseLineTemp > 0) {
1030 float fLineHeightTmp = fBaseLineTemp + pTP->rtPiece.height;
1031 if (fLineHeight < fLineHeightTmp)
1032 fLineHeight = fLineHeightTmp;
1033 }
1034 fLineStep = std::max(fLineStep, fLineHeight);
1035 pTP->pLinkData = pUserData ? pUserData->m_pLinkData : nullptr;
1036 pPieceLine->m_textPieces.push_back(std::move(pTP));
1037 DoTabstops(pStyle.Get(), pPieceLine);
1038 }
1039 for (const auto& pTP : pPieceLine->m_textPieces) {
1040 float& fTop = pTP->rtPiece.top;
1041 float fBaseLineTemp = fTop;
1042 fTop = *pLinePos + fLineStep - pTP->rtPiece.height - fBaseLineTemp;
1043 fTop = std::max(0.0f, fTop);
1044 }
1045 *pLinePos += fLineStep + fBaseLine;
1046 } else {
1047 float fLineStep = 0;
1048 float fLineWidth = 0;
1049 for (int32_t i = 0; i < iPieces; i++) {
1050 const CFX_BreakPiece* pPiece = m_pBreak->GetBreakPieceUnstable(i);
1051 CFX_TextUserData* pUserData = pPiece->m_pUserData.Get();
1052 if (pUserData)
1053 pStyle = pUserData->m_pStyle;
1054 float fVerScale = pPiece->m_iVerticalScale / 100.0f;
1055 float fBaseLine = m_textParser.GetBaseline(m_pTextProvider, pStyle.Get());
1056 float fLineHeight = m_textParser.GetLineHeight(
1057 m_pTextProvider, pStyle.Get(), m_iLines == 0, fVerScale);
1058 if (fBaseLine > 0) {
1059 float fLineHeightTmp =
1060 fBaseLine +
1061 static_cast<float>(pPiece->m_iFontSize) * fVerScale / 20.0f;
1062 if (fLineHeight < fLineHeightTmp) {
1063 fLineHeight = fLineHeightTmp;
1064 }
1065 }
1066 fLineStep = std::max(fLineStep, fLineHeight);
1067 fLineWidth += pPiece->m_iWidth / 20000.0f;
1068 }
1069 *pLinePos += fLineStep;
1070 m_fMaxWidth = std::max(m_fMaxWidth, fLineWidth);
1071 if (m_pLoader && m_pLoader->bSaveLineHeight) {
1072 float fHeight = *pLinePos - m_pLoader->fLastPos;
1073 m_pLoader->fLastPos = *pLinePos;
1074 m_pLoader->lineHeights.push_back(fHeight);
1075 }
1076 }
1077
1078 m_pBreak->ClearBreakPieces();
1079 if (dwStatus == CFX_BreakType::Paragraph) {
1080 m_pBreak->Reset();
1081 if (!pStyle && bEndBreak) {
1082 CXFA_Para* para = m_pTextProvider->GetParaIfExists();
1083 if (para) {
1084 float fStartPos = para->GetMarginLeft();
1085 float fIndent = para->GetTextIndent();
1086 if (fIndent > 0)
1087 fStartPos += fIndent;
1088
1089 float fSpaceBelow = para->GetSpaceBelow();
1090 if (fSpaceBelow < 0.1f)
1091 fSpaceBelow = 0;
1092
1093 m_pBreak->SetLineStartPos(fStartPos);
1094 *pLinePos += fSpaceBelow;
1095 }
1096 }
1097 }
1098
1099 if (pStyle) {
1100 float fStart = 0;
1101 const CFX_CSSRect* pRect = pStyle->GetMarginWidth();
1102 if (pRect)
1103 fStart = pRect->left.GetValue();
1104
1105 float fTextIndent = pStyle->GetTextIndent().GetValue();
1106 if (fTextIndent < 0)
1107 fStart -= fTextIndent;
1108
1109 m_pBreak->SetLineStartPos(fStart);
1110 }
1111 m_iLines++;
1112 }
1113
RenderString(CFX_RenderDevice * pDevice,CXFA_PieceLine * pPieceLine,size_t szPiece,std::vector<TextCharPos> * pCharPos,const CFX_Matrix & mtDoc2Device)1114 void CXFA_TextLayout::RenderString(CFX_RenderDevice* pDevice,
1115 CXFA_PieceLine* pPieceLine,
1116 size_t szPiece,
1117 std::vector<TextCharPos>* pCharPos,
1118 const CFX_Matrix& mtDoc2Device) {
1119 const CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
1120 size_t szCount = GetDisplayPos(pPiece, pCharPos);
1121 if (szCount > 0) {
1122 auto span = pdfium::make_span(pCharPos->data(), szCount);
1123 CFDE_TextOut::DrawString(pDevice, pPiece->dwColor, pPiece->pFont, span,
1124 pPiece->fFontSize, mtDoc2Device);
1125 }
1126 pPieceLine->m_charCounts.push_back(szCount);
1127 }
1128
RenderPath(CFX_RenderDevice * pDevice,CXFA_PieceLine * pPieceLine,size_t szPiece,std::vector<TextCharPos> * pCharPos,const CFX_Matrix & mtDoc2Device)1129 void CXFA_TextLayout::RenderPath(CFX_RenderDevice* pDevice,
1130 CXFA_PieceLine* pPieceLine,
1131 size_t szPiece,
1132 std::vector<TextCharPos>* pCharPos,
1133 const CFX_Matrix& mtDoc2Device) {
1134 CXFA_TextPiece* pPiece = pPieceLine->m_textPieces[szPiece].get();
1135 bool bNoUnderline = pPiece->iUnderline < 1 || pPiece->iUnderline > 2;
1136 bool bNoLineThrough = pPiece->iLineThrough < 1 || pPiece->iLineThrough > 2;
1137 if (bNoUnderline && bNoLineThrough)
1138 return;
1139
1140 CFX_PathData path;
1141 size_t szChars = GetDisplayPos(pPiece, pCharPos);
1142 if (szChars > 0) {
1143 CFX_PointF pt1;
1144 CFX_PointF pt2;
1145 float fEndY = (*pCharPos)[0].m_Origin.y + 1.05f;
1146 if (pPiece->iPeriod == XFA_AttributeValue::Word) {
1147 for (int32_t i = 0; i < pPiece->iUnderline; i++) {
1148 for (size_t j = 0; j < szChars; j++) {
1149 pt1.x = (*pCharPos)[j].m_Origin.x;
1150 pt2.x = pt1.x +
1151 (*pCharPos)[j].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
1152 pt1.y = pt2.y = fEndY;
1153 path.AppendLine(pt1, pt2);
1154 }
1155 fEndY += 2.0f;
1156 }
1157 } else {
1158 pt1.x = (*pCharPos)[0].m_Origin.x;
1159 pt2.x = (*pCharPos)[szChars - 1].m_Origin.x +
1160 (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize /
1161 1000.0f;
1162 for (int32_t i = 0; i < pPiece->iUnderline; i++) {
1163 pt1.y = pt2.y = fEndY;
1164 path.AppendLine(pt1, pt2);
1165 fEndY += 2.0f;
1166 }
1167 }
1168 fEndY = (*pCharPos)[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
1169 pt1.x = (*pCharPos)[0].m_Origin.x;
1170 pt2.x =
1171 (*pCharPos)[szChars - 1].m_Origin.x +
1172 (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
1173 for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
1174 pt1.y = pt2.y = fEndY;
1175 path.AppendLine(pt1, pt2);
1176 fEndY += 2.0f;
1177 }
1178 } else {
1179 if (bNoLineThrough &&
1180 (bNoUnderline || pPiece->iPeriod != XFA_AttributeValue::All)) {
1181 return;
1182 }
1183 bool bHasCount = false;
1184 size_t szPiecePrev = szPiece;
1185 size_t szPieceNext = szPiece;
1186 while (szPiecePrev > 0) {
1187 szPiecePrev--;
1188 if (pPieceLine->m_charCounts[szPiecePrev] > 0) {
1189 bHasCount = true;
1190 break;
1191 }
1192 }
1193 if (!bHasCount)
1194 return;
1195
1196 bHasCount = false;
1197 while (szPieceNext < pPieceLine->m_textPieces.size() - 1) {
1198 szPieceNext++;
1199 if (pPieceLine->m_charCounts[szPieceNext] > 0) {
1200 bHasCount = true;
1201 break;
1202 }
1203 }
1204 if (!bHasCount)
1205 return;
1206
1207 float fOrgX = 0.0f;
1208 float fEndX = 0.0f;
1209 pPiece = pPieceLine->m_textPieces[szPiecePrev].get();
1210 szChars = GetDisplayPos(pPiece, pCharPos);
1211 if (szChars < 1)
1212 return;
1213
1214 fOrgX =
1215 (*pCharPos)[szChars - 1].m_Origin.x +
1216 (*pCharPos)[szChars - 1].m_FontCharWidth * pPiece->fFontSize / 1000.0f;
1217 pPiece = pPieceLine->m_textPieces[szPieceNext].get();
1218 szChars = GetDisplayPos(pPiece, pCharPos);
1219 if (szChars < 1)
1220 return;
1221
1222 fEndX = (*pCharPos)[0].m_Origin.x;
1223 CFX_PointF pt1;
1224 CFX_PointF pt2;
1225 pt1.x = fOrgX;
1226 pt2.x = fEndX;
1227 float fEndY = (*pCharPos)[0].m_Origin.y + 1.05f;
1228 for (int32_t i = 0; i < pPiece->iUnderline; i++) {
1229 pt1.y = fEndY;
1230 pt2.y = fEndY;
1231 path.AppendLine(pt1, pt2);
1232 fEndY += 2.0f;
1233 }
1234 fEndY = (*pCharPos)[0].m_Origin.y - pPiece->rtPiece.height * 0.25f;
1235 for (int32_t i = 0; i < pPiece->iLineThrough; i++) {
1236 pt1.y = fEndY;
1237 pt2.y = fEndY;
1238 path.AppendLine(pt1, pt2);
1239 fEndY += 2.0f;
1240 }
1241 }
1242
1243 CFX_GraphStateData graphState;
1244 graphState.m_LineCap = CFX_GraphStateData::LineCapButt;
1245 graphState.m_LineJoin = CFX_GraphStateData::LineJoinMiter;
1246 graphState.m_LineWidth = 1;
1247 graphState.m_MiterLimit = 10;
1248 graphState.m_DashPhase = 0;
1249 pDevice->DrawPath(&path, &mtDoc2Device, &graphState, 0, pPiece->dwColor, 0);
1250 }
1251
GetDisplayPos(const CXFA_TextPiece * pPiece,std::vector<TextCharPos> * pCharPos)1252 size_t CXFA_TextLayout::GetDisplayPos(const CXFA_TextPiece* pPiece,
1253 std::vector<TextCharPos>* pCharPos) {
1254 if (!pPiece || pPiece->iChars < 1)
1255 return 0;
1256 return m_pBreak->GetDisplayPos(pPiece, pCharPos);
1257 }
1258