xref: /aosp_15_r20/external/pdfium/xfa/fxfa/cxfa_textlayout.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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_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 
InitBreak(CFX_CSSComputedStyle * pStyle,CFX_CSSDisplay eDisplay,float fLineWidth,const CFX_XMLNode * pXMLNode,CFX_CSSComputedStyle * pParentStyle)223 void 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()) {
237       case CFX_CSSTextAlign::Right:
238         iAlign = CFGAS_RTFBreak::LineAlignment::Right;
239         break;
240       case CFX_CSSTextAlign::Center:
241         iAlign = CFGAS_RTFBreak::LineAlignment::Center;
242         break;
243       case CFX_CSSTextAlign::Justify:
244         iAlign = CFGAS_RTFBreak::LineAlignment::Justified;
245         break;
246       case CFX_CSSTextAlign::JustifyAll:
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;
276         pNewRect.left.Set(CFX_CSSLengthUnit::Point, fStart);
277         pNewRect.right.Set(CFX_CSSLengthUnit::Point, pRect->right.GetValue());
278         pNewRect.top.Set(CFX_CSSLengthUnit::Point, pRect->top.GetValue());
279         pNewRect.bottom.Set(CFX_CSSLengthUnit::Point, pRect->bottom.GetValue());
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 
GetLayoutHeight()308 float 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 
StartLayout(float fWidth)330 float 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 
DoLayout(float fTextHeight)356 float CXFA_TextLayout::DoLayout(float fTextHeight) {
357   if (!m_pLoader)
358     return fTextHeight;
359 
360   UpdateLoaderHeight(fTextHeight);
361   return fTextHeight;
362 }
363 
DoSplitLayout(size_t szBlockIndex,float fCalcHeight,float fTextHeight)364 float 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 
CountBlocks() const436 size_t CXFA_TextLayout::CountBlocks() const {
437   size_t szCount = m_Blocks.size();
438   return szCount > 0 ? szCount : 1;
439 }
440 
GetNextIndexFromLastBlockData() const441 size_t CXFA_TextLayout::GetNextIndexFromLastBlockData() const {
442   return m_Blocks.back().szIndex + m_Blocks.back().szLength;
443 }
444 
UpdateLoaderHeight(float fTextHeight)445 void CXFA_TextLayout::UpdateLoaderHeight(float fTextHeight) {
446   m_pLoader->fHeight = fTextHeight;
447   if (m_pLoader->fHeight < 0)
448     m_pLoader->fHeight = GetLayoutHeight();
449 }
450 
CalcSize(const CFX_SizeF & minSize,const CFX_SizeF & maxSize)451 CFX_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 
Layout(const CFX_SizeF & size)469 float 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 
LayoutInternal(size_t szBlockIndex)488 bool 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 
ItemBlocks(const CFX_RectF & rtText,size_t szBlockIndex)570 void 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 
DrawString(CFX_RenderDevice * pFxDevice,const CFX_Matrix & mtDoc2Device,const CFX_RectF & rtClip,size_t szBlockIndex)602 bool 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 
UpdateAlign(float fHeight,float fBottom)651 void 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 
Loader(float textWidth,float * pLinePos,bool bSavePieces)672 void 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 
LoadText(CXFA_Node * pNode,float textWidth,float * pLinePos,bool bSavePieces)696 void 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 
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)730 bool 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);
744   CFX_CSSDisplay eDisplay = CFX_CSSDisplay::None;
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)
887                                        ? CFGAS_Char::BreakType::kParagraph
888                                        : CFGAS_Char::BreakType::kPiece;
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 
AppendChar(const WideString & wsText,float * pLinePos,float fSpaceAbove,bool bSavePieces)905 bool CXFA_TextLayout::AppendChar(const WideString& wsText,
906                                  float* pLinePos,
907                                  float fSpaceAbove,
908                                  bool bSavePieces) {
909   CFGAS_Char::BreakType dwStatus = CFGAS_Char::BreakType::kNone;
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 
IsEnd(bool bSavePieces)936 bool 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 
EndBreak(CFGAS_Char::BreakType dwStatus,float * pLinePos,bool bSavePieces)944 void 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 &&
949       dwStatus != CFGAS_Char::BreakType::kPiece)
950     AppendTextLine(dwStatus, pLinePos, bSavePieces, true);
951 }
952 
DoTabstops(CFX_CSSComputedStyle * pStyle,PieceLine * pPieceLine)953 void 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 
AppendTextLine(CFGAS_Char::BreakType dwStatus,float * pLinePos,bool bSavePieces,bool bEndBreak)1008 void 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 
RenderString(CFX_RenderDevice * pDevice,PieceLine * pPieceLine,size_t szPiece,std::vector<TextCharPos> * pCharPos,const CFX_Matrix & mtDoc2Device)1148 void 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 
RenderPath(CFX_RenderDevice * pDevice,const PieceLine * pPieceLine,size_t szPiece,std::vector<TextCharPos> * pCharPos,const CFX_Matrix & mtDoc2Device)1163 void 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 < pPieceLine->m_textPieces.size() - 1) {
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;
1278   graphState.m_LineCap = CFX_GraphStateData::LineCap::kButt;
1279   graphState.m_LineJoin = CFX_GraphStateData::LineJoin::kMiter;
1280   graphState.m_LineWidth = 1;
1281   graphState.m_MiterLimit = 10;
1282   graphState.m_DashPhase = 0;
1283   pDevice->DrawPath(path, &mtDoc2Device, &graphState, 0, pPiece->dwColor,
1284                     CFX_FillRenderOptions());
1285 }
1286 
GetDisplayPos(const TextPiece * pPiece,std::vector<TextCharPos> * pCharPos)1287 size_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 }
1293