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