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