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