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