1 // Copyright 2014 The PDFium Authors
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/fgas/layout/cfgas_rtfbreak.h"
8
9 #include <algorithm>
10
11 #include "build/build_config.h"
12 #include "core/fxcrt/check.h"
13 #include "core/fxcrt/compiler_specific.h"
14 #include "core/fxcrt/containers/adapters.h"
15 #include "core/fxcrt/fx_extension.h"
16 #include "core/fxcrt/fx_safe_types.h"
17 #include "core/fxcrt/numerics/safe_math.h"
18 #include "core/fxcrt/stl_util.h"
19 #include "core/fxge/text_char_pos.h"
20 #include "xfa/fgas/font/cfgas_gefont.h"
21 #include "xfa/fgas/layout/cfgas_char.h"
22 #include "xfa/fgas/layout/cfgas_textpiece.h"
23 #include "xfa/fgas/layout/cfgas_textuserdata.h"
24 #include "xfa/fgas/layout/fgas_arabic.h"
25 #include "xfa/fgas/layout/fgas_linebreak.h"
26
CFGAS_RTFBreak(Mask<LayoutStyle> dwLayoutStyles)27 CFGAS_RTFBreak::CFGAS_RTFBreak(Mask<LayoutStyle> dwLayoutStyles)
28 : CFGAS_Break(dwLayoutStyles) {
29 SetBreakStatus();
30 m_bPagination = !!(m_dwLayoutStyles & LayoutStyle::kPagination);
31 }
32
33 CFGAS_RTFBreak::~CFGAS_RTFBreak() = default;
34
SetLineStartPos(float fLinePos)35 void CFGAS_RTFBreak::SetLineStartPos(float fLinePos) {
36 int32_t iLinePos = FXSYS_roundf(fLinePos * kConversionFactor);
37 iLinePos = std::min(iLinePos, m_iLineWidth);
38 iLinePos = std::max(iLinePos, m_iLineStart);
39 m_pCurLine->m_iStart = iLinePos;
40 }
41
AddPositionedTab(float fTabPos)42 void CFGAS_RTFBreak::AddPositionedTab(float fTabPos) {
43 int32_t iTabPos = std::min(
44 FXSYS_roundf(fTabPos * kConversionFactor) + m_iLineStart, m_iLineWidth);
45 auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
46 iTabPos);
47 if (it != m_PositionedTabs.end() && *it == iTabPos)
48 return;
49 m_PositionedTabs.insert(it, iTabPos);
50 }
51
SetUserData(const RetainPtr<CFGAS_TextUserData> & pUserData)52 void CFGAS_RTFBreak::SetUserData(
53 const RetainPtr<CFGAS_TextUserData>& pUserData) {
54 if (m_pUserData == pUserData)
55 return;
56
57 SetBreakStatus();
58 m_pUserData = pUserData;
59 }
60
GetPositionedTab(int32_t * iTabPos) const61 bool CFGAS_RTFBreak::GetPositionedTab(int32_t* iTabPos) const {
62 auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
63 *iTabPos);
64 if (it == m_PositionedTabs.end())
65 return false;
66
67 *iTabPos = *it;
68 return true;
69 }
70
AppendChar(wchar_t wch)71 CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar(wchar_t wch) {
72 DCHECK(m_pCurLine);
73
74 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
75 m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
76 m_iVerticalScale);
77 CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
78 pCurChar->m_iFontSize = m_iFontSize;
79 pCurChar->m_dwIdentity = m_dwIdentity;
80 pCurChar->m_pUserData = m_pUserData;
81
82 CFGAS_Char::BreakType dwRet1 = CFGAS_Char::BreakType::kNone;
83 if (chartype != FX_CHARTYPE::kCombination &&
84 GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
85 m_eCharType != FX_CHARTYPE::kUnknown &&
86 IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()) &&
87 (m_eCharType != FX_CHARTYPE::kSpace ||
88 chartype != FX_CHARTYPE::kControl)) {
89 dwRet1 = EndBreak(CFGAS_Char::BreakType::kLine);
90 if (!m_pCurLine->m_LineChars.empty())
91 pCurChar = &m_pCurLine->m_LineChars.back();
92 }
93
94 CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
95 switch (chartype) {
96 case FX_CHARTYPE::kTab:
97 AppendChar_Tab(pCurChar);
98 break;
99 case FX_CHARTYPE::kControl:
100 dwRet2 = AppendChar_Control(pCurChar);
101 break;
102 case FX_CHARTYPE::kCombination:
103 AppendChar_Combination(pCurChar);
104 break;
105 case FX_CHARTYPE::kArabicAlef:
106 case FX_CHARTYPE::kArabicSpecial:
107 case FX_CHARTYPE::kArabicDistortion:
108 case FX_CHARTYPE::kArabicNormal:
109 case FX_CHARTYPE::kArabicForm:
110 case FX_CHARTYPE::kArabic:
111 dwRet2 = AppendChar_Arabic(pCurChar);
112 break;
113 case FX_CHARTYPE::kUnknown:
114 case FX_CHARTYPE::kSpace:
115 case FX_CHARTYPE::kNumeric:
116 case FX_CHARTYPE::kNormal:
117 dwRet2 = AppendChar_Others(pCurChar);
118 break;
119 }
120
121 m_eCharType = chartype;
122 return std::max(dwRet1, dwRet2);
123 }
124
AppendChar_Combination(CFGAS_Char * pCurChar)125 void CFGAS_RTFBreak::AppendChar_Combination(CFGAS_Char* pCurChar) {
126 std::optional<uint16_t> iCharWidthRet;
127 if (m_pFont) {
128 iCharWidthRet = m_pFont->GetCharWidth(pCurChar->char_code());
129 }
130 FX_SAFE_INT32 iCharWidth = iCharWidthRet.value_or(0);
131 iCharWidth *= m_iFontSize;
132 iCharWidth *= m_iHorizontalScale;
133 iCharWidth /= 100;
134 CFGAS_Char* pLastChar = GetLastChar(0, false, true);
135 if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE::kCombination)
136 iCharWidth *= -1;
137 else
138 m_eCharType = FX_CHARTYPE::kCombination;
139
140 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
141 pCurChar->m_iCharWidth = iCharWidthValid;
142 if (iCharWidthValid > 0) {
143 FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
144 checked_width += iCharWidthValid;
145 if (!checked_width.IsValid())
146 return;
147
148 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
149 }
150 }
151
AppendChar_Tab(CFGAS_Char * pCurChar)152 void CFGAS_RTFBreak::AppendChar_Tab(CFGAS_Char* pCurChar) {
153 if (!(m_dwLayoutStyles & LayoutStyle::kExpandTab))
154 return;
155
156 int32_t& iLineWidth = m_pCurLine->m_iWidth;
157 int32_t iCharWidth = iLineWidth;
158 FX_SAFE_INT32 iSafeCharWidth;
159 if (GetPositionedTab(&iCharWidth)) {
160 iSafeCharWidth = iCharWidth;
161 } else {
162 // Tab width is >= 160000, so this part does not need to be checked.
163 DCHECK(m_iTabWidth >= kMinimumTabWidth);
164 iSafeCharWidth = iLineWidth / m_iTabWidth + 1;
165 iSafeCharWidth *= m_iTabWidth;
166 }
167 iSafeCharWidth -= iLineWidth;
168
169 iCharWidth = iSafeCharWidth.ValueOrDefault(0);
170
171 pCurChar->m_iCharWidth = iCharWidth;
172 iLineWidth += iCharWidth;
173 }
174
AppendChar_Control(CFGAS_Char * pCurChar)175 CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Control(CFGAS_Char* pCurChar) {
176 CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
177 switch (pCurChar->char_code()) {
178 case L'\v':
179 case pdfium::unicode::kLineSeparator:
180 dwRet2 = CFGAS_Char::BreakType::kLine;
181 break;
182 case L'\f':
183 dwRet2 = CFGAS_Char::BreakType::kPage;
184 break;
185 case pdfium::unicode::kParagraphSeparator:
186 dwRet2 = CFGAS_Char::BreakType::kParagraph;
187 break;
188 default:
189 if (pCurChar->char_code() == m_wParagraphBreakChar)
190 dwRet2 = CFGAS_Char::BreakType::kParagraph;
191 break;
192 }
193 if (dwRet2 != CFGAS_Char::BreakType::kNone)
194 dwRet2 = EndBreak(dwRet2);
195
196 return dwRet2;
197 }
198
AppendChar_Arabic(CFGAS_Char * pCurChar)199 CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Arabic(CFGAS_Char* pCurChar) {
200 m_pCurLine->IncrementArabicCharCount();
201
202 CFGAS_Char* pLastChar = nullptr;
203 wchar_t wForm;
204 bool bAlef = false;
205 if (m_eCharType >= FX_CHARTYPE::kArabicAlef &&
206 m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
207 pLastChar = GetLastChar(1, false, true);
208 if (pLastChar) {
209 m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
210 CFGAS_Char* pPrevChar = GetLastChar(2, false, true);
211 wForm = pdfium::GetArabicFormChar(pLastChar, pPrevChar, pCurChar);
212 bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
213 pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
214 FX_SAFE_INT32 iCharWidth = 0;
215 if (m_pFont) {
216 std::optional<uint16_t> iCharWidthRet = m_pFont->GetCharWidth(wForm);
217 if (iCharWidthRet.has_value()) {
218 iCharWidth = iCharWidthRet.value();
219 } else {
220 iCharWidthRet = m_pFont->GetCharWidth(pLastChar->char_code());
221 iCharWidth = iCharWidthRet.value_or(0);
222 }
223 }
224 iCharWidth *= m_iFontSize;
225 iCharWidth *= m_iHorizontalScale;
226 iCharWidth /= 100;
227
228 int iCharWidthValid = iCharWidth.ValueOrDefault(0);
229 pLastChar->m_iCharWidth = iCharWidthValid;
230
231 FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
232 checked_width += iCharWidthValid;
233 if (!checked_width.IsValid())
234 return CFGAS_Char::BreakType::kNone;
235
236 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
237 iCharWidth = 0;
238 }
239 }
240
241 wForm =
242 pdfium::GetArabicFormChar(pCurChar, bAlef ? nullptr : pLastChar, nullptr);
243 FX_SAFE_INT32 iCharWidth = 0;
244 if (m_pFont) {
245 std::optional<uint16_t> iCharWidthRet = m_pFont->GetCharWidth(wForm);
246 if (!iCharWidthRet.has_value())
247 iCharWidthRet = m_pFont->GetCharWidth(pCurChar->char_code());
248 iCharWidth = iCharWidthRet.value_or(0);
249 iCharWidth *= m_iFontSize;
250 iCharWidth *= m_iHorizontalScale;
251 iCharWidth /= 100;
252 }
253
254 int iCharWidthValid = iCharWidth.ValueOrDefault(0);
255 pCurChar->m_iCharWidth = iCharWidthValid;
256
257 FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
258 checked_width += iCharWidthValid;
259 if (!checked_width.IsValid())
260 return CFGAS_Char::BreakType::kNone;
261
262 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
263
264 if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()))
265 return EndBreak(CFGAS_Char::BreakType::kLine);
266 return CFGAS_Char::BreakType::kNone;
267 }
268
AppendChar_Others(CFGAS_Char * pCurChar)269 CFGAS_Char::BreakType CFGAS_RTFBreak::AppendChar_Others(CFGAS_Char* pCurChar) {
270 FX_CHARTYPE chartype = pCurChar->GetCharType();
271 wchar_t wForm = pCurChar->char_code();
272 FX_SAFE_INT32 iCharWidth = 0;
273 if (m_pFont) {
274 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
275 }
276 iCharWidth *= m_iFontSize;
277 iCharWidth *= m_iHorizontalScale;
278 iCharWidth /= 100;
279 iCharWidth += m_iCharSpace;
280
281 int iValidCharWidth = iCharWidth.ValueOrDefault(0);
282 pCurChar->m_iCharWidth = iValidCharWidth;
283
284 FX_SAFE_INT32 checked_width = m_pCurLine->m_iWidth;
285 checked_width += iValidCharWidth;
286 if (!checked_width.IsValid())
287 return CFGAS_Char::BreakType::kNone;
288
289 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
290 if (chartype != FX_CHARTYPE::kSpace &&
291 IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
292 return EndBreak(CFGAS_Char::BreakType::kLine);
293 }
294 return CFGAS_Char::BreakType::kNone;
295 }
296
EndBreak(CFGAS_Char::BreakType dwStatus)297 CFGAS_Char::BreakType CFGAS_RTFBreak::EndBreak(CFGAS_Char::BreakType dwStatus) {
298 DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
299
300 ++m_dwIdentity;
301 if (!m_pCurLine->m_LinePieces.empty()) {
302 if (dwStatus != CFGAS_Char::BreakType::kPiece)
303 m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
304 return m_pCurLine->m_LinePieces.back().GetStatus();
305 }
306
307 if (HasLine()) {
308 if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
309 return CFGAS_Char::BreakType::kNone;
310
311 if (dwStatus != CFGAS_Char::BreakType::kPiece)
312 m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
313 return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
314 }
315
316 CFGAS_Char* tc = m_pCurLine->LastChar();
317 if (!tc)
318 return CFGAS_Char::BreakType::kNone;
319
320 tc->m_dwStatus = dwStatus;
321 if (dwStatus == CFGAS_Char::BreakType::kPiece)
322 return dwStatus;
323
324 m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
325 CFGAS_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
326 bool bAllChars = m_iAlignment == LineAlignment::Justified ||
327 m_iAlignment == LineAlignment::Distributed;
328
329 if (!EndBreakSplitLine(pNextLine, bAllChars, dwStatus)) {
330 std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
331 if (!m_bPagination && m_iAlignment != LineAlignment::Left)
332 EndBreakAlignment(tpos, bAllChars, dwStatus);
333 }
334 m_pCurLine = pNextLine;
335 m_pCurLine->m_iStart = m_iLineStart;
336
337 CFGAS_Char* pTC = GetLastChar(0, false, true);
338 m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
339 return dwStatus;
340 }
341
EndBreakSplitLine(CFGAS_BreakLine * pNextLine,bool bAllChars,CFGAS_Char::BreakType dwStatus)342 bool CFGAS_RTFBreak::EndBreakSplitLine(CFGAS_BreakLine* pNextLine,
343 bool bAllChars,
344 CFGAS_Char::BreakType dwStatus) {
345 bool bDone = false;
346 if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
347 const CFGAS_Char* tc = m_pCurLine->LastChar();
348 switch (tc->GetCharType()) {
349 case FX_CHARTYPE::kTab:
350 case FX_CHARTYPE::kControl:
351 case FX_CHARTYPE::kSpace:
352 break;
353 default:
354 SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
355 bDone = true;
356 break;
357 }
358 }
359
360 if (!m_bPagination) {
361 if (bAllChars && !bDone) {
362 int32_t endPos = m_pCurLine->GetLineEnd();
363 GetBreakPos(m_pCurLine->m_LineChars, bAllChars, true, &endPos);
364 }
365 return false;
366 }
367
368 const CFGAS_Char* pCurChars = m_pCurLine->m_LineChars.data();
369 CFGAS_BreakPiece tp;
370 tp.SetChars(&m_pCurLine->m_LineChars);
371 bool bNew = true;
372 uint32_t dwIdentity = static_cast<uint32_t>(-1);
373 int32_t iLast = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars) - 1;
374 int32_t j = 0;
375 for (int32_t i = 0; i <= iLast;) {
376 const CFGAS_Char* pTC = UNSAFE_TODO(pCurChars + i);
377 if (bNew) {
378 tp.SetStartChar(i);
379 tp.IncrementStartPos(tp.GetWidth());
380 tp.SetWidth(0);
381 tp.SetStatus(pTC->m_dwStatus);
382 tp.SetFontSize(pTC->m_iFontSize);
383 tp.SetHorizontalScale(pTC->horizonal_scale());
384 tp.SetVerticalScale(pTC->vertical_scale());
385 dwIdentity = pTC->m_dwIdentity;
386 tp.SetUserData(pTC->m_pUserData);
387 j = i;
388 bNew = false;
389 }
390
391 if (i == iLast || pTC->m_dwStatus != CFGAS_Char::BreakType::kNone ||
392 pTC->m_dwIdentity != dwIdentity) {
393 if (pTC->m_dwIdentity == dwIdentity) {
394 tp.SetStatus(pTC->m_dwStatus);
395 tp.IncrementWidth(pTC->m_iCharWidth);
396 ++i;
397 }
398 tp.SetCharCount(i - j);
399 m_pCurLine->m_LinePieces.push_back(tp);
400 bNew = true;
401 } else {
402 tp.IncrementWidth(pTC->m_iCharWidth);
403 ++i;
404 }
405 }
406 return true;
407 }
408
EndBreakBidiLine(CFGAS_Char::BreakType dwStatus)409 std::deque<CFGAS_Break::TPO> CFGAS_RTFBreak::EndBreakBidiLine(
410 CFGAS_Char::BreakType dwStatus) {
411 CFGAS_Char* pTC;
412 std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
413 if (!m_bPagination && m_pCurLine->HasArabicChar()) {
414 size_t iBidiNum = 0;
415 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
416 pTC = &chars[i];
417 pTC->m_iBidiPos = static_cast<int32_t>(i);
418 if (pTC->GetCharType() != FX_CHARTYPE::kControl)
419 iBidiNum = i;
420 if (i == 0)
421 pTC->m_iBidiLevel = 1;
422 }
423 CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
424 } else {
425 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
426 pTC = &chars[i];
427 pTC->m_iBidiLevel = 0;
428 pTC->m_iBidiPos = 0;
429 pTC->m_iBidiOrder = 0;
430 }
431 }
432
433 CFGAS_BreakPiece tp;
434 tp.SetStatus(CFGAS_Char::BreakType::kPiece);
435 tp.SetStartPos(m_pCurLine->m_iStart);
436 tp.SetChars(&chars);
437
438 int32_t iBidiLevel = -1;
439 int32_t iCharWidth;
440 std::deque<TPO> tpos;
441 uint32_t dwIdentity = static_cast<uint32_t>(-1);
442 int32_t i = 0;
443 int32_t j = 0;
444 int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
445 while (i < iCount) {
446 pTC = &chars[i];
447 if (iBidiLevel < 0) {
448 iBidiLevel = pTC->m_iBidiLevel;
449 iCharWidth = pTC->m_iCharWidth;
450 tp.SetWidth(iCharWidth < 1 ? 0 : iCharWidth);
451 tp.SetBidiLevel(iBidiLevel);
452 tp.SetBidiPos(pTC->m_iBidiOrder);
453 tp.SetFontSize(pTC->m_iFontSize);
454 tp.SetHorizontalScale(pTC->horizonal_scale());
455 tp.SetVerticalScale(pTC->vertical_scale());
456 dwIdentity = pTC->m_dwIdentity;
457 tp.SetUserData(pTC->m_pUserData);
458 tp.SetStatus(CFGAS_Char::BreakType::kPiece);
459 ++i;
460 } else if (iBidiLevel != pTC->m_iBidiLevel ||
461 pTC->m_dwIdentity != dwIdentity) {
462 tp.SetCharCount(i - tp.GetStartChar());
463 m_pCurLine->m_LinePieces.push_back(tp);
464 tp.IncrementStartPos(tp.GetWidth());
465 tp.SetStartChar(i);
466 tpos.push_back({j++, tp.GetBidiPos()});
467 iBidiLevel = -1;
468 } else {
469 iCharWidth = pTC->m_iCharWidth;
470 if (iCharWidth > 0)
471 tp.IncrementWidth(iCharWidth);
472 ++i;
473 }
474 }
475
476 if (i > tp.GetStartChar()) {
477 tp.SetStatus(dwStatus);
478 tp.SetCharCount(i - tp.GetStartChar());
479 m_pCurLine->m_LinePieces.push_back(tp);
480 tpos.push_back({j, tp.GetBidiPos()});
481 }
482
483 std::sort(tpos.begin(), tpos.end());
484 int32_t iStartPos = m_pCurLine->m_iStart;
485 for (const auto& it : tpos) {
486 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[it.index];
487 ttp.SetStartPos(iStartPos);
488 iStartPos += ttp.GetWidth();
489 }
490 return tpos;
491 }
492
EndBreakAlignment(const std::deque<TPO> & tpos,bool bAllChars,CFGAS_Char::BreakType dwStatus)493 void CFGAS_RTFBreak::EndBreakAlignment(const std::deque<TPO>& tpos,
494 bool bAllChars,
495 CFGAS_Char::BreakType dwStatus) {
496 int32_t iNetWidth = m_pCurLine->m_iWidth;
497 int32_t iGapChars = 0;
498 bool bFind = false;
499 for (const TPO& pos : pdfium::Reversed(tpos)) {
500 const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
501 if (!bFind)
502 iNetWidth = ttp.GetEndPos();
503
504 bool bArabic = FX_IsOdd(ttp.GetBidiLevel());
505 int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
506 while (j > -1 && j < ttp.GetCharCount()) {
507 const CFGAS_Char* tc = ttp.GetChar(j);
508 if (tc->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
509 ++iGapChars;
510
511 if (!bFind || !bAllChars) {
512 FX_CHARTYPE dwCharType = tc->GetCharType();
513 if (dwCharType == FX_CHARTYPE::kSpace ||
514 dwCharType == FX_CHARTYPE::kControl) {
515 if (!bFind) {
516 int32_t iCharWidth = tc->m_iCharWidth;
517 if (bAllChars && iCharWidth > 0)
518 iNetWidth -= iCharWidth;
519 }
520 } else {
521 bFind = true;
522 if (!bAllChars)
523 break;
524 }
525 }
526 j += bArabic ? 1 : -1;
527 }
528 if (!bAllChars && bFind)
529 break;
530 }
531
532 int32_t iOffset = m_iLineWidth - iNetWidth;
533 if (iGapChars > 0 && (m_iAlignment == LineAlignment::Distributed ||
534 (m_iAlignment == LineAlignment::Justified &&
535 dwStatus != CFGAS_Char::BreakType::kParagraph))) {
536 int32_t iStart = -1;
537 for (const auto& tpo : tpos) {
538 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
539 if (iStart < 0)
540 iStart = ttp.GetStartPos();
541 else
542 ttp.SetStartPos(iStart);
543
544 for (int32_t j = 0; j < ttp.GetCharCount(); ++j) {
545 CFGAS_Char* tc = ttp.GetChar(j);
546 if (tc->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
547 tc->m_iCharWidth < 0) {
548 continue;
549 }
550 int32_t k = iOffset / iGapChars;
551 tc->m_iCharWidth += k;
552 ttp.IncrementWidth(k);
553 iOffset -= k;
554 --iGapChars;
555 if (iGapChars < 1)
556 break;
557 }
558 iStart += ttp.GetWidth();
559 }
560 } else if (m_iAlignment == LineAlignment::Right ||
561 m_iAlignment == LineAlignment::Center) {
562 if (m_iAlignment == LineAlignment::Center)
563 iOffset /= 2;
564 if (iOffset > 0) {
565 for (auto& ttp : m_pCurLine->m_LinePieces)
566 ttp.IncrementStartPos(iOffset);
567 }
568 }
569 }
570
GetBreakPos(std::vector<CFGAS_Char> & tca,bool bAllChars,bool bOnlyBrk,int32_t * pEndPos)571 int32_t CFGAS_RTFBreak::GetBreakPos(std::vector<CFGAS_Char>& tca,
572 bool bAllChars,
573 bool bOnlyBrk,
574 int32_t* pEndPos) {
575 int32_t iLength = fxcrt::CollectionSize<int32_t>(tca) - 1;
576 if (iLength < 1)
577 return iLength;
578
579 int32_t iBreak = -1;
580 int32_t iBreakPos = -1;
581 int32_t iIndirect = -1;
582 int32_t iIndirectPos = -1;
583 int32_t iLast = -1;
584 int32_t iLastPos = -1;
585 if (*pEndPos <= m_iLineWidth) {
586 if (!bAllChars)
587 return iLength;
588
589 iBreak = iLength;
590 iBreakPos = *pEndPos;
591 }
592
593 CFGAS_Char* pCharArray = tca.data();
594 CFGAS_Char* pCur = UNSAFE_TODO(pCharArray + iLength);
595 --iLength;
596 if (bAllChars)
597 pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
598
599 FX_BREAKPROPERTY nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
600 int32_t iCharWidth = pCur->m_iCharWidth;
601 if (iCharWidth > 0)
602 *pEndPos -= iCharWidth;
603
604 while (iLength >= 0) {
605 pCur = UNSAFE_TODO(pCharArray + iLength);
606 FX_BREAKPROPERTY nCur =
607 pdfium::unicode::GetBreakProperty(pCur->char_code());
608 bool bNeedBreak = false;
609 FX_LINEBREAKTYPE eType;
610 if (nCur == FX_BREAKPROPERTY::kTB) {
611 bNeedBreak = true;
612 eType = nNext == FX_BREAKPROPERTY::kTB
613 ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
614 : GetLineBreakTypeFromPair(nCur, nNext);
615 } else {
616 if (nCur == FX_BREAKPROPERTY::kSP)
617 bNeedBreak = true;
618
619 eType = nNext == FX_BREAKPROPERTY::kSP
620 ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
621 : GetLineBreakTypeFromPair(nCur, nNext);
622 }
623 if (bAllChars)
624 pCur->m_eLineBreakType = eType;
625
626 if (!bOnlyBrk) {
627 iCharWidth = pCur->m_iCharWidth;
628 if (*pEndPos <= m_iLineWidth || bNeedBreak) {
629 if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
630 iBreak = iLength;
631 iBreakPos = *pEndPos;
632 if (!bAllChars)
633 return iLength;
634 } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
635 iIndirect = iLength;
636 iIndirectPos = *pEndPos;
637 }
638 if (iLast < 0) {
639 iLast = iLength;
640 iLastPos = *pEndPos;
641 }
642 }
643 if (iCharWidth > 0)
644 *pEndPos -= iCharWidth;
645 }
646 nNext = nCur;
647 --iLength;
648 }
649 if (bOnlyBrk)
650 return 0;
651
652 if (iBreak > -1) {
653 *pEndPos = iBreakPos;
654 return iBreak;
655 }
656 if (iIndirect > -1) {
657 *pEndPos = iIndirectPos;
658 return iIndirect;
659 }
660 if (iLast > -1) {
661 *pEndPos = iLastPos;
662 return iLast;
663 }
664 return 0;
665 }
666
SplitTextLine(CFGAS_BreakLine * pCurLine,CFGAS_BreakLine * pNextLine,bool bAllChars)667 void CFGAS_RTFBreak::SplitTextLine(CFGAS_BreakLine* pCurLine,
668 CFGAS_BreakLine* pNextLine,
669 bool bAllChars) {
670 DCHECK(pCurLine);
671 DCHECK(pNextLine);
672
673 if (pCurLine->m_LineChars.size() < 2)
674 return;
675
676 int32_t iEndPos = pCurLine->GetLineEnd();
677 std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
678 int32_t iCharPos = GetBreakPos(curChars, bAllChars, false, &iEndPos);
679 if (iCharPos < 0)
680 iCharPos = 0;
681
682 ++iCharPos;
683 if (iCharPos >= fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
684 pNextLine->Clear();
685 curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
686 return;
687 }
688
689 pNextLine->m_LineChars =
690 std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
691 curChars.erase(curChars.begin() + iCharPos, curChars.end());
692 pNextLine->m_iStart = pCurLine->m_iStart;
693 pNextLine->m_iWidth = pCurLine->GetLineEnd() - iEndPos;
694 pCurLine->m_iWidth = iEndPos;
695 curChars[iCharPos - 1].m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
696
697 for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
698 if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
699 pCurLine->DecrementArabicCharCount();
700 pNextLine->IncrementArabicCharCount();
701 }
702 pNextLine->m_LineChars[i].m_dwStatus = CFGAS_Char::BreakType::kNone;
703 }
704 }
705
GetDisplayPos(const CFGAS_TextPiece * pPiece,pdfium::span<TextCharPos> pCharPos) const706 size_t CFGAS_RTFBreak::GetDisplayPos(const CFGAS_TextPiece* pPiece,
707 pdfium::span<TextCharPos> pCharPos) const {
708 if (pPiece->iChars == 0 || !pPiece->pFont)
709 return 0;
710
711 RetainPtr<CFGAS_GEFont> pFont = pPiece->pFont;
712 CFX_RectF rtText(pPiece->rtPiece);
713 const bool bRTLPiece = FX_IsOdd(pPiece->iBidiLevel);
714 const float fFontSize = pPiece->fFontSize;
715 const int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
716 if (iFontSize == 0)
717 return 0;
718
719 const int32_t iAscent = pFont->GetAscent();
720 const int32_t iDescent = pFont->GetDescent();
721 const int32_t iMaxHeight = iAscent - iDescent;
722 const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
723 wchar_t wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
724 wchar_t wNext;
725 float fX = rtText.left;
726 int32_t iHorScale = pPiece->iHorScale;
727 int32_t iVerScale = pPiece->iVerScale;
728 if (bRTLPiece)
729 fX = rtText.right();
730
731 float fY = rtText.top + fAscent;
732 size_t szCount = 0;
733 for (int32_t i = 0; i < pPiece->iChars; ++i) {
734 TextCharPos& current_char_pos = pCharPos[szCount];
735 wchar_t wch = pPiece->szText[i];
736 int32_t iWidth = pPiece->Widths[i];
737 FX_CHARTYPE dwCharType = pdfium::unicode::GetCharType(wch);
738 if (iWidth == 0) {
739 if (dwCharType == FX_CHARTYPE::kArabicAlef)
740 wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
741 continue;
742 }
743
744 int iCharWidth = abs(iWidth);
745 const bool bEmptyChar = (dwCharType >= FX_CHARTYPE::kTab &&
746 dwCharType <= FX_CHARTYPE::kControl);
747 if (!bEmptyChar)
748 ++szCount;
749
750 iCharWidth /= iFontSize;
751 wchar_t wForm = wch;
752 if (dwCharType >= FX_CHARTYPE::kArabicAlef) {
753 if (i + 1 < pPiece->iChars) {
754 wNext = pPiece->szText[i + 1];
755 if (pPiece->Widths[i + 1] < 0 && i + 2 < pPiece->iChars)
756 wNext = pPiece->szText[i + 2];
757 } else {
758 wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
759 }
760 wForm = pdfium::GetArabicFormChar(wch, wPrev, wNext);
761 } else if (bRTLPiece) {
762 wForm = pdfium::unicode::GetMirrorChar(wch);
763 }
764
765 if (!bEmptyChar) {
766 current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wForm);
767 if (current_char_pos.m_GlyphIndex == 0xFFFF)
768 current_char_pos.m_GlyphIndex = pFont->GetGlyphIndex(wch);
769 #if BUILDFLAG(IS_APPLE)
770 current_char_pos.m_ExtGID = current_char_pos.m_GlyphIndex;
771 #endif
772 current_char_pos.m_FontCharWidth = iCharWidth;
773 }
774
775 float fCharWidth = fFontSize * iCharWidth / 1000.0f;
776 if (bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
777 fX -= fCharWidth;
778
779 if (!bEmptyChar)
780 current_char_pos.m_Origin = CFX_PointF(fX, fY);
781 if (!bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
782 fX += fCharWidth;
783
784 if (!bEmptyChar) {
785 current_char_pos.m_bGlyphAdjust = true;
786 current_char_pos.m_AdjustMatrix[0] = -1;
787 current_char_pos.m_AdjustMatrix[1] = 0;
788 current_char_pos.m_AdjustMatrix[2] = 0;
789 current_char_pos.m_AdjustMatrix[3] = 1;
790 current_char_pos.m_Origin.y += fAscent * iVerScale / 100.0f;
791 current_char_pos.m_Origin.y -= fAscent;
792
793 if (iHorScale != 100 || iVerScale != 100) {
794 current_char_pos.m_AdjustMatrix[0] =
795 current_char_pos.m_AdjustMatrix[0] * iHorScale / 100.0f;
796 current_char_pos.m_AdjustMatrix[1] =
797 current_char_pos.m_AdjustMatrix[1] * iHorScale / 100.0f;
798 current_char_pos.m_AdjustMatrix[2] =
799 current_char_pos.m_AdjustMatrix[2] * iVerScale / 100.0f;
800 current_char_pos.m_AdjustMatrix[3] =
801 current_char_pos.m_AdjustMatrix[3] * iVerScale / 100.0f;
802 }
803 }
804 if (iWidth > 0)
805 wPrev = wch;
806 }
807 return szCount;
808 }
809