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