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