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