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