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