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_txtbreak.h"
8
9 #include <algorithm>
10
11 #include "build/build_config.h"
12 #include "core/fxcrt/fx_codepage.h"
13 #include "core/fxcrt/fx_extension.h"
14 #include "core/fxcrt/fx_safe_types.h"
15 #include "core/fxcrt/stl_util.h"
16 #include "core/fxge/text_char_pos.h"
17 #include "third_party/base/check.h"
18 #include "third_party/base/containers/adapters.h"
19 #include "third_party/base/numerics/safe_conversions.h"
20 #include "xfa/fgas/font/cfgas_gefont.h"
21 #include "xfa/fgas/layout/cfgas_char.h"
22 #include "xfa/fgas/layout/fgas_arabic.h"
23 #include "xfa/fgas/layout/fgas_linebreak.h"
24
25 namespace {
26
27 struct FX_FORMCHAR {
28 uint16_t wch;
29 uint16_t wForm;
30 int32_t iWidth;
31 };
32
IsCtrlCode(wchar_t wch)33 bool IsCtrlCode(wchar_t wch) {
34 FX_CHARTYPE dwRet = pdfium::unicode::GetCharType(wch);
35 return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
36 }
37
38 } // namespace
39
CFGAS_TxtBreak()40 CFGAS_TxtBreak::CFGAS_TxtBreak() : CFGAS_Break(LayoutStyle::kNone) {}
41
42 CFGAS_TxtBreak::~CFGAS_TxtBreak() = default;
43
SetLineWidth(float fLineWidth)44 void CFGAS_TxtBreak::SetLineWidth(float fLineWidth) {
45 m_iLineWidth = FXSYS_roundf(fLineWidth * kConversionFactor);
46 DCHECK(m_iLineWidth >= 20000);
47 }
48
SetAlignment(int32_t iAlignment)49 void CFGAS_TxtBreak::SetAlignment(int32_t iAlignment) {
50 DCHECK(iAlignment >= CFX_TxtLineAlignment_Left);
51 DCHECK(iAlignment <= CFX_TxtLineAlignment_Justified);
52 m_iAlignment = iAlignment;
53 }
54
SetCombWidth(float fCombWidth)55 void CFGAS_TxtBreak::SetCombWidth(float fCombWidth) {
56 m_iCombWidth = FXSYS_roundf(fCombWidth * kConversionFactor);
57 }
58
AppendChar_Combination(CFGAS_Char * pCurChar)59 void CFGAS_TxtBreak::AppendChar_Combination(CFGAS_Char* pCurChar) {
60 FX_SAFE_INT32 iCharWidth = m_iCombWidth;
61 pCurChar->m_iCharWidth = -1;
62 if (!m_bCombText) {
63 wchar_t wch = pCurChar->char_code();
64 CFGAS_Char* pLastChar = GetLastChar(0, false, false);
65 if (pLastChar &&
66 (pLastChar->m_dwCharStyles & FX_TXTCHARSTYLE_ArabicShadda) == 0) {
67 wchar_t wLast = pLastChar->char_code();
68 absl::optional<uint16_t> maybe_shadda;
69 if (wch == pdfium::arabic::kArabicShadda) {
70 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wLast);
71 } else if (wLast == pdfium::arabic::kArabicShadda) {
72 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
73 }
74 if (maybe_shadda.has_value()) {
75 wch = maybe_shadda.value();
76 pCurChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
77 pLastChar->m_dwCharStyles |= FX_TXTCHARSTYLE_ArabicShadda;
78 pLastChar->m_iCharWidth = 0;
79 }
80 }
81 absl::optional<uint16_t> iCharWidthRet;
82 if (m_pFont) {
83 iCharWidthRet = m_pFont->GetCharWidth(wch);
84 }
85 iCharWidth = iCharWidthRet.value_or(0);
86 iCharWidth *= m_iFontSize;
87 iCharWidth *= m_iHorizontalScale;
88 iCharWidth /= 100;
89 }
90 iCharWidth *= -1;
91 pCurChar->m_iCharWidth = iCharWidth.ValueOrDefault(0);
92 }
93
AppendChar_Tab(CFGAS_Char * pCurChar)94 void CFGAS_TxtBreak::AppendChar_Tab(CFGAS_Char* pCurChar) {
95 m_eCharType = FX_CHARTYPE::kTab;
96 }
97
AppendChar_Control(CFGAS_Char * pCurChar)98 CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Control(CFGAS_Char* pCurChar) {
99 m_eCharType = FX_CHARTYPE::kControl;
100 CFGAS_Char::BreakType dwRet = CFGAS_Char::BreakType::kNone;
101 if (!m_bSingleLine) {
102 wchar_t wch = pCurChar->char_code();
103 switch (wch) {
104 case L'\v':
105 case pdfium::unicode::kLineSeparator:
106 dwRet = CFGAS_Char::BreakType::kLine;
107 break;
108 case L'\f':
109 dwRet = CFGAS_Char::BreakType::kPage;
110 break;
111 case pdfium::unicode::kParagraphSeparator:
112 dwRet = CFGAS_Char::BreakType::kParagraph;
113 break;
114 default:
115 if (wch == m_wParagraphBreakChar)
116 dwRet = CFGAS_Char::BreakType::kParagraph;
117 break;
118 }
119 if (dwRet != CFGAS_Char::BreakType::kNone)
120 dwRet = EndBreak(dwRet);
121 }
122 return dwRet;
123 }
124
AppendChar_Arabic(CFGAS_Char * pCurChar)125 CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Arabic(CFGAS_Char* pCurChar) {
126 FX_CHARTYPE chartype = pCurChar->GetCharType();
127 int32_t& iLineWidth = m_pCurLine->m_iWidth;
128 wchar_t wForm;
129 CFGAS_Char* pLastChar = nullptr;
130 bool bAlef = false;
131 if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
132 m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
133 FX_SAFE_INT32 iCharWidth = 0;
134 pLastChar = GetLastChar(1, true, false);
135 if (pLastChar) {
136 if (pLastChar->m_iCharWidth > 0)
137 iLineWidth -= pLastChar->m_iCharWidth;
138 iCharWidth = pLastChar->m_iCharWidth;
139
140 CFGAS_Char* pPrevChar = GetLastChar(2, true, false);
141 wForm = pdfium::arabic::GetFormChar(pLastChar, pPrevChar, pCurChar);
142 bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
143 pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
144 if (m_pFont) {
145 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
146 }
147 if (wForm == pdfium::unicode::kZeroWidthNoBreakSpace)
148 iCharWidth = 0;
149
150 iCharWidth *= m_iFontSize;
151 iCharWidth *= m_iHorizontalScale;
152 iCharWidth /= 100;
153
154 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
155 pLastChar->m_iCharWidth = iCharWidthValid;
156 iLineWidth += iCharWidthValid;
157 }
158 }
159
160 m_eCharType = chartype;
161 wForm = pdfium::arabic::GetFormChar(pCurChar, bAlef ? nullptr : pLastChar,
162 nullptr);
163 FX_SAFE_INT32 iCharWidth = 0;
164 if (m_bCombText) {
165 iCharWidth = m_iCombWidth;
166 } else {
167 if (m_pFont && wForm != pdfium::unicode::kZeroWidthNoBreakSpace) {
168 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
169 }
170 iCharWidth *= m_iFontSize;
171 iCharWidth *= m_iHorizontalScale;
172 iCharWidth /= 100;
173 }
174
175 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
176 pCurChar->m_iCharWidth = iCharWidthValid;
177 iLineWidth += iCharWidthValid;
178
179 m_pCurLine->IncrementArabicCharCount();
180 if (!m_bSingleLine && IsGreaterThanLineWidth(iLineWidth))
181 return EndBreak(CFGAS_Char::BreakType::kLine);
182 return CFGAS_Char::BreakType::kNone;
183 }
184
AppendChar_Others(CFGAS_Char * pCurChar)185 CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar_Others(CFGAS_Char* pCurChar) {
186 FX_CHARTYPE chartype = pCurChar->GetCharType();
187 int32_t& iLineWidth = m_pCurLine->m_iWidth;
188 m_eCharType = chartype;
189 wchar_t wch = pCurChar->char_code();
190 wchar_t wForm = wch;
191
192 FX_SAFE_INT32 iCharWidth = 0;
193 if (m_bCombText) {
194 iCharWidth = m_iCombWidth;
195 } else if (m_pFont) {
196 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
197 iCharWidth *= m_iFontSize;
198 iCharWidth *= m_iHorizontalScale;
199 iCharWidth /= 100;
200 }
201 iCharWidth += m_iCharSpace;
202
203 int32_t iValidCharWidth = iCharWidth.ValueOrDefault(0);
204 pCurChar->m_iCharWidth = iValidCharWidth;
205 iLineWidth += iValidCharWidth;
206 if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
207 IsGreaterThanLineWidth(iLineWidth)) {
208 return EndBreak(CFGAS_Char::BreakType::kLine);
209 }
210
211 return CFGAS_Char::BreakType::kNone;
212 }
213
AppendChar(wchar_t wch)214 CFGAS_Char::BreakType CFGAS_TxtBreak::AppendChar(wchar_t wch) {
215 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
216 m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
217 m_iVerticalScale);
218 CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
219 pCurChar->m_dwCharStyles = m_iAlignment | (1 << 8);
220
221 CFGAS_Char::BreakType dwRet1 = CFGAS_Char::BreakType::kNone;
222 if (chartype != FX_CHARTYPE::kCombination &&
223 GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
224 m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
225 IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
226 (m_eCharType != FX_CHARTYPE::kSpace ||
227 chartype != FX_CHARTYPE::kControl)) {
228 dwRet1 = EndBreak(CFGAS_Char::BreakType::kLine);
229 if (!m_pCurLine->m_LineChars.empty())
230 pCurChar = &m_pCurLine->m_LineChars.back();
231 }
232
233 CFGAS_Char::BreakType dwRet2 = CFGAS_Char::BreakType::kNone;
234 if (wch == m_wParagraphBreakChar) {
235 // This is handled in AppendChar_Control, but it seems like \n and \r
236 // don't get matched as control characters so we go into AppendChar_other
237 // and never detect the new paragraph ...
238 dwRet2 = CFGAS_Char::BreakType::kParagraph;
239 EndBreak(dwRet2);
240 } else {
241 switch (chartype) {
242 case FX_CHARTYPE::kTab:
243 AppendChar_Tab(pCurChar);
244 break;
245 case FX_CHARTYPE::kControl:
246 dwRet2 = AppendChar_Control(pCurChar);
247 break;
248 case FX_CHARTYPE::kCombination:
249 AppendChar_Combination(pCurChar);
250 break;
251 case FX_CHARTYPE::kArabicAlef:
252 case FX_CHARTYPE::kArabicSpecial:
253 case FX_CHARTYPE::kArabicDistortion:
254 case FX_CHARTYPE::kArabicNormal:
255 case FX_CHARTYPE::kArabicForm:
256 case FX_CHARTYPE::kArabic:
257 dwRet2 = AppendChar_Arabic(pCurChar);
258 break;
259 case FX_CHARTYPE::kUnknown:
260 case FX_CHARTYPE::kSpace:
261 case FX_CHARTYPE::kNumeric:
262 case FX_CHARTYPE::kNormal:
263 default:
264 dwRet2 = AppendChar_Others(pCurChar);
265 break;
266 }
267 }
268 return std::max(dwRet1, dwRet2);
269 }
270
EndBreakSplitLine(CFGAS_BreakLine * pNextLine,bool bAllChars)271 void CFGAS_TxtBreak::EndBreakSplitLine(CFGAS_BreakLine* pNextLine,
272 bool bAllChars) {
273 bool bDone = false;
274 CFGAS_Char* pTC;
275 if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
276 pTC = m_pCurLine->LastChar();
277 switch (pTC->GetCharType()) {
278 case FX_CHARTYPE::kTab:
279 case FX_CHARTYPE::kControl:
280 case FX_CHARTYPE::kSpace:
281 break;
282 default:
283 SplitTextLine(m_pCurLine, pNextLine, bAllChars);
284 bDone = true;
285 break;
286 }
287 }
288 if (bAllChars && !bDone) {
289 int32_t iEndPos = m_pCurLine->m_iWidth;
290 GetBreakPos(&m_pCurLine->m_LineChars, bAllChars, true, &iEndPos);
291 }
292 }
293
EndBreakBidiLine(CFGAS_Char::BreakType dwStatus)294 std::deque<CFGAS_Break::TPO> CFGAS_TxtBreak::EndBreakBidiLine(
295 CFGAS_Char::BreakType dwStatus) {
296 CFGAS_BreakPiece tp;
297 std::deque<TPO> tpos;
298 CFGAS_Char* pTC;
299 std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
300 if (!m_pCurLine->HasArabicChar()) {
301 tp.SetStatus(dwStatus);
302 tp.SetStartPos(m_pCurLine->m_iStart);
303 tp.SetWidth(m_pCurLine->m_iWidth);
304 tp.SetStartChar(0);
305 tp.SetCharCount(fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars));
306 tp.SetChars(&m_pCurLine->m_LineChars);
307 pTC = &chars[0];
308 tp.SetCharStyles(pTC->m_dwCharStyles);
309 tp.SetHorizontalScale(pTC->horizonal_scale());
310 tp.SetVerticalScale(pTC->vertical_scale());
311 m_pCurLine->m_LinePieces.push_back(tp);
312 tpos.push_back({0, 0});
313 return tpos;
314 }
315
316 size_t iBidiNum = 0;
317 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
318 pTC = &chars[i];
319 pTC->m_iBidiPos = static_cast<int32_t>(i);
320 if (pTC->GetCharType() != FX_CHARTYPE::kControl)
321 iBidiNum = i;
322 if (i == 0)
323 pTC->m_iBidiLevel = 1;
324 }
325 CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
326
327 tp.SetStatus(CFGAS_Char::BreakType::kPiece);
328 tp.SetStartPos(m_pCurLine->m_iStart);
329 tp.SetChars(&m_pCurLine->m_LineChars);
330 int32_t iBidiLevel = -1;
331 int32_t iCharWidth;
332 int32_t i = 0;
333 int32_t j = -1;
334 int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
335 while (i < iCount) {
336 pTC = &chars[i];
337 if (iBidiLevel < 0) {
338 iBidiLevel = pTC->m_iBidiLevel;
339 tp.SetWidth(0);
340 tp.SetBidiLevel(iBidiLevel);
341 tp.SetBidiPos(pTC->m_iBidiOrder);
342 tp.SetCharStyles(pTC->m_dwCharStyles);
343 tp.SetHorizontalScale(pTC->horizonal_scale());
344 tp.SetVerticalScale(pTC->vertical_scale());
345 tp.SetStatus(CFGAS_Char::BreakType::kPiece);
346 }
347 if (iBidiLevel != pTC->m_iBidiLevel ||
348 pTC->m_dwStatus != CFGAS_Char::BreakType::kNone) {
349 if (iBidiLevel == pTC->m_iBidiLevel) {
350 tp.SetStatus(pTC->m_dwStatus);
351 iCharWidth = pTC->m_iCharWidth;
352 if (iCharWidth > 0)
353 tp.IncrementWidth(iCharWidth);
354
355 i++;
356 }
357 tp.SetCharCount(i - tp.GetStartChar());
358 m_pCurLine->m_LinePieces.push_back(tp);
359 tp.IncrementStartPos(tp.GetWidth());
360 tp.SetStartChar(i);
361 tpos.push_back({++j, tp.GetBidiPos()});
362 iBidiLevel = -1;
363 } else {
364 iCharWidth = pTC->m_iCharWidth;
365 if (iCharWidth > 0)
366 tp.IncrementWidth(iCharWidth);
367
368 i++;
369 }
370 }
371 if (i > tp.GetStartChar()) {
372 tp.SetStatus(dwStatus);
373 tp.SetCharCount(i - tp.GetStartChar());
374 m_pCurLine->m_LinePieces.push_back(tp);
375 tpos.push_back({++j, tp.GetBidiPos()});
376 }
377 if (j > -1) {
378 if (j > 0) {
379 std::sort(tpos.begin(), tpos.end());
380 int32_t iStartPos = 0;
381 for (i = 0; i <= j; i++) {
382 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpos[i].index];
383 ttp.SetStartPos(iStartPos);
384 iStartPos += ttp.GetWidth();
385 }
386 }
387 m_pCurLine->m_LinePieces[j].SetStatus(dwStatus);
388 }
389 return tpos;
390 }
391
EndBreakAlignment(const std::deque<TPO> & tpos,bool bAllChars,CFGAS_Char::BreakType dwStatus)392 void CFGAS_TxtBreak::EndBreakAlignment(const std::deque<TPO>& tpos,
393 bool bAllChars,
394 CFGAS_Char::BreakType dwStatus) {
395 int32_t iNetWidth = m_pCurLine->m_iWidth;
396 int32_t iGapChars = 0;
397 bool bFind = false;
398 for (const TPO& pos : pdfium::base::Reversed(tpos)) {
399 const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
400 if (!bFind)
401 iNetWidth = ttp.GetEndPos();
402
403 bool bArabic = FX_IsOdd(ttp.GetBidiLevel());
404 int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
405 while (j > -1 && j < ttp.GetCharCount()) {
406 const CFGAS_Char* pTC = ttp.GetChar(j);
407 if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
408 iGapChars++;
409 if (!bFind || !bAllChars) {
410 FX_CHARTYPE chartype = pTC->GetCharType();
411 if (chartype == FX_CHARTYPE::kSpace ||
412 chartype == FX_CHARTYPE::kControl) {
413 if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
414 iNetWidth -= pTC->m_iCharWidth;
415 } else {
416 bFind = true;
417 if (!bAllChars)
418 break;
419 }
420 }
421 j += bArabic ? 1 : -1;
422 }
423 if (!bAllChars && bFind)
424 break;
425 }
426
427 int32_t iOffset = m_iLineWidth - iNetWidth;
428 if (iGapChars > 0 && m_iAlignment & CFX_TxtLineAlignment_Justified &&
429 dwStatus != CFGAS_Char::BreakType::kParagraph) {
430 int32_t iStart = -1;
431 for (auto& tpo : tpos) {
432 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
433 if (iStart < -1)
434 iStart = ttp.GetStartPos();
435 else
436 ttp.SetStartPos(iStart);
437
438 for (int32_t j = 0; j < ttp.GetCharCount() && iGapChars > 0;
439 j++, iGapChars--) {
440 CFGAS_Char* pTC = ttp.GetChar(j);
441 if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
442 pTC->m_iCharWidth < 0) {
443 continue;
444 }
445 int32_t k = iOffset / iGapChars;
446 pTC->m_iCharWidth += k;
447 ttp.IncrementWidth(k);
448 iOffset -= k;
449 }
450 iStart += ttp.GetWidth();
451 }
452 } else if (m_iAlignment & CFX_TxtLineAlignment_Center ||
453 m_iAlignment & CFX_TxtLineAlignment_Right) {
454 if (m_iAlignment & CFX_TxtLineAlignment_Center &&
455 !(m_iAlignment & CFX_TxtLineAlignment_Right)) {
456 iOffset /= 2;
457 }
458 if (iOffset > 0) {
459 for (auto& ttp : m_pCurLine->m_LinePieces)
460 ttp.IncrementStartPos(iOffset);
461 }
462 }
463 }
464
EndBreak(CFGAS_Char::BreakType dwStatus)465 CFGAS_Char::BreakType CFGAS_TxtBreak::EndBreak(CFGAS_Char::BreakType dwStatus) {
466 DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
467
468 if (!m_pCurLine->m_LinePieces.empty()) {
469 if (dwStatus != CFGAS_Char::BreakType::kPiece)
470 m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
471 return m_pCurLine->m_LinePieces.back().GetStatus();
472 }
473
474 if (HasLine()) {
475 if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
476 return CFGAS_Char::BreakType::kNone;
477
478 if (dwStatus != CFGAS_Char::BreakType::kPiece)
479 m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
480 return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
481 }
482
483 if (m_pCurLine->m_LineChars.empty())
484 return CFGAS_Char::BreakType::kNone;
485
486 m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
487 if (dwStatus == CFGAS_Char::BreakType::kPiece)
488 return dwStatus;
489
490 m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
491 CFGAS_BreakLine* pNextLine = &m_Lines[1 - m_iReadyLineIndex];
492 const bool bAllChars = m_iAlignment > CFX_TxtLineAlignment_Right;
493 EndBreakSplitLine(pNextLine, bAllChars);
494
495 std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
496 if (m_iAlignment > CFX_TxtLineAlignment_Left)
497 EndBreakAlignment(tpos, bAllChars, dwStatus);
498
499 m_pCurLine = pNextLine;
500 CFGAS_Char* pTC = GetLastChar(0, false, false);
501 m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
502 return dwStatus;
503 }
504
GetBreakPos(std::vector<CFGAS_Char> * pChars,bool bAllChars,bool bOnlyBrk,int32_t * pEndPos)505 int32_t CFGAS_TxtBreak::GetBreakPos(std::vector<CFGAS_Char>* pChars,
506 bool bAllChars,
507 bool bOnlyBrk,
508 int32_t* pEndPos) {
509 std::vector<CFGAS_Char>& chars = *pChars;
510 int32_t iLength = fxcrt::CollectionSize<int32_t>(chars) - 1;
511 if (iLength < 1)
512 return iLength;
513
514 int32_t iBreak = -1;
515 int32_t iBreakPos = -1;
516 int32_t iIndirect = -1;
517 int32_t iIndirectPos = -1;
518 int32_t iLast = -1;
519 int32_t iLastPos = -1;
520 if (m_bSingleLine || *pEndPos <= m_iLineWidth) {
521 if (!bAllChars)
522 return iLength;
523
524 iBreak = iLength;
525 iBreakPos = *pEndPos;
526 }
527
528 FX_LINEBREAKTYPE eType;
529 FX_BREAKPROPERTY nCur;
530 FX_BREAKPROPERTY nNext;
531 CFGAS_Char* pCur = &chars[iLength--];
532 if (bAllChars)
533 pCur->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
534
535 nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
536 int32_t iCharWidth = pCur->m_iCharWidth;
537 if (iCharWidth > 0)
538 *pEndPos -= iCharWidth;
539
540 while (iLength >= 0) {
541 pCur = &chars[iLength];
542 nCur = pdfium::unicode::GetBreakProperty(pCur->char_code());
543 if (nNext == FX_BREAKPROPERTY::kSP)
544 eType = FX_LINEBREAKTYPE::kPROHIBITED_BRK;
545 else
546 eType = GetLineBreakTypeFromPair(nCur, nNext);
547 if (bAllChars)
548 pCur->m_eLineBreakType = eType;
549 if (!bOnlyBrk) {
550 if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
551 nCur == FX_BREAKPROPERTY::kSP) {
552 if (eType == FX_LINEBREAKTYPE::kDIRECT_BRK && iBreak < 0) {
553 iBreak = iLength;
554 iBreakPos = *pEndPos;
555 if (!bAllChars)
556 return iLength;
557 } else if (eType == FX_LINEBREAKTYPE::kINDIRECT_BRK && iIndirect < 0) {
558 iIndirect = iLength;
559 iIndirectPos = *pEndPos;
560 }
561 if (iLast < 0) {
562 iLast = iLength;
563 iLastPos = *pEndPos;
564 }
565 }
566 iCharWidth = pCur->m_iCharWidth;
567 if (iCharWidth > 0)
568 *pEndPos -= iCharWidth;
569 }
570 nNext = nCur;
571 iLength--;
572 }
573 if (bOnlyBrk)
574 return 0;
575 if (iBreak > -1) {
576 *pEndPos = iBreakPos;
577 return iBreak;
578 }
579 if (iIndirect > -1) {
580 *pEndPos = iIndirectPos;
581 return iIndirect;
582 }
583 if (iLast > -1) {
584 *pEndPos = iLastPos;
585 return iLast;
586 }
587 return 0;
588 }
589
SplitTextLine(CFGAS_BreakLine * pCurLine,CFGAS_BreakLine * pNextLine,bool bAllChars)590 void CFGAS_TxtBreak::SplitTextLine(CFGAS_BreakLine* pCurLine,
591 CFGAS_BreakLine* pNextLine,
592 bool bAllChars) {
593 DCHECK(pCurLine);
594 DCHECK(pNextLine);
595
596 if (pCurLine->m_LineChars.size() < 2)
597 return;
598
599 int32_t iEndPos = pCurLine->m_iWidth;
600 std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
601 int32_t iCharPos = GetBreakPos(&curChars, bAllChars, false, &iEndPos);
602 if (iCharPos < 0)
603 iCharPos = 0;
604
605 iCharPos++;
606 if (iCharPos >= fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
607 pNextLine->Clear();
608 CFGAS_Char* pTC = &curChars[iCharPos - 1];
609 pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
610 return;
611 }
612
613 pNextLine->m_LineChars =
614 std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
615 curChars.erase(curChars.begin() + iCharPos, curChars.end());
616 pCurLine->m_iWidth = iEndPos;
617 CFGAS_Char* pTC = &curChars[iCharPos - 1];
618 pTC->m_eLineBreakType = FX_LINEBREAKTYPE::kUNKNOWN;
619 int32_t iWidth = 0;
620 for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
621 if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
622 pCurLine->DecrementArabicCharCount();
623 pNextLine->IncrementArabicCharCount();
624 }
625 iWidth += std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
626 pNextLine->m_LineChars[i].m_dwStatus = CFGAS_Char::BreakType::kNone;
627 }
628 pNextLine->m_iWidth = iWidth;
629 }
630
GetDisplayPos(const Run & run,TextCharPos * pCharPos) const631 size_t CFGAS_TxtBreak::GetDisplayPos(const Run& run,
632 TextCharPos* pCharPos) const {
633 if (run.iLength < 1)
634 return 0;
635
636 Engine* pEngine = run.pEdtEngine;
637 const wchar_t* pStr = run.wsStr.c_str();
638 int32_t* pWidths = run.pWidths;
639 int32_t iLength = run.iLength - 1;
640 RetainPtr<CFGAS_GEFont> pFont = run.pFont;
641 Mask<LayoutStyle> dwStyles = run.dwStyles;
642 CFX_RectF rtText(*run.pRect);
643 const bool bRTLPiece = (run.dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel) != 0;
644 const float fFontSize = run.fFontSize;
645 const int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
646 const int32_t iAscent = pFont->GetAscent();
647 const int32_t iDescent = pFont->GetDescent();
648 const int32_t iMaxHeight = iAscent - iDescent;
649 const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
650 int32_t iHorScale = run.iHorizontalScale;
651 int32_t iVerScale = run.iVerticalScale;
652 bool bSkipSpace = run.bSkipSpace;
653
654 const float fYBase = rtText.top + (rtText.height - fFontSize) / 2.0f;
655 float fX = bRTLPiece ? rtText.right() : rtText.left;
656 float fY = fYBase + fAscent;
657
658 size_t szCount = 0;
659 int32_t iNext = 0;
660 wchar_t wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
661 wchar_t wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
662 wchar_t wForm = pdfium::unicode::kZeroWidthNoBreakSpace;
663 wchar_t wLast = pdfium::unicode::kZeroWidthNoBreakSpace;
664 bool bShadda = false;
665 bool bLam = false;
666 for (int32_t i = 0; i <= iLength; i++) {
667 int32_t iAbsolute = i + run.iStart;
668 int32_t iWidth;
669 wchar_t wch;
670 if (pEngine) {
671 wch = pEngine->GetChar(iAbsolute);
672 iWidth = pEngine->GetWidthOfChar(iAbsolute);
673 } else {
674 wch = *pStr++;
675 iWidth = *pWidths++;
676 }
677
678 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
679 if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
680 wPrev = pdfium::unicode::kZeroWidthNoBreakSpace;
681 wLast = wch;
682 continue;
683 }
684
685 if (chartype >= FX_CHARTYPE::kArabicAlef) {
686 if (i < iLength) {
687 if (pEngine) {
688 iNext = i + 1;
689 while (iNext <= iLength) {
690 int32_t iNextAbsolute = iNext + run.iStart;
691 wNext = pEngine->GetChar(iNextAbsolute);
692 if (pdfium::unicode::GetCharType(wNext) !=
693 FX_CHARTYPE::kCombination) {
694 break;
695 }
696 iNext++;
697 }
698 if (iNext > iLength)
699 wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
700 } else {
701 int32_t j = -1;
702 do {
703 j++;
704 if (i + j >= iLength)
705 break;
706
707 wNext = pStr[j];
708 } while (pdfium::unicode::GetCharType(wNext) ==
709 FX_CHARTYPE::kCombination);
710 if (i + j >= iLength)
711 wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
712 }
713 } else {
714 wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
715 }
716
717 wForm = pdfium::arabic::GetFormChar(wch, wPrev, wNext);
718 bLam = (wPrev == pdfium::arabic::kArabicLetterLam &&
719 wch == pdfium::arabic::kArabicLetterLam &&
720 wNext == pdfium::arabic::kArabicLetterHeh);
721 } else if (chartype == FX_CHARTYPE::kCombination) {
722 wForm = wch;
723 if (wch >= 0x064C && wch <= 0x0651) {
724 if (bShadda) {
725 wForm = pdfium::unicode::kZeroWidthNoBreakSpace;
726 bShadda = false;
727 } else {
728 wNext = pdfium::unicode::kZeroWidthNoBreakSpace;
729 if (pEngine) {
730 iNext = i + 1;
731 if (iNext <= iLength) {
732 int32_t iNextAbsolute = iNext + run.iStart;
733 wNext = pEngine->GetChar(iNextAbsolute);
734 }
735 } else if (i < iLength) {
736 wNext = *pStr;
737 }
738 absl::optional<uint16_t> maybe_shadda;
739 if (wch == pdfium::arabic::kArabicShadda) {
740 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wNext);
741 } else if (wNext == pdfium::arabic::kArabicShadda) {
742 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
743 }
744 if (maybe_shadda.has_value()) {
745 wForm = maybe_shadda.value();
746 bShadda = true;
747 }
748 }
749 } else {
750 bShadda = false;
751 }
752 } else if (chartype == FX_CHARTYPE::kNumeric) {
753 wForm = wch;
754 } else if (wch == L'.') {
755 wForm = wch;
756 } else if (wch == L',') {
757 wForm = wch;
758 } else if (bRTLPiece) {
759 wForm = pdfium::unicode::GetMirrorChar(wch);
760 } else {
761 wForm = wch;
762 }
763 if (chartype != FX_CHARTYPE::kCombination)
764 bShadda = false;
765 if (chartype < FX_CHARTYPE::kArabicAlef)
766 bLam = false;
767
768 bool bEmptyChar =
769 (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
770 if (wForm == pdfium::unicode::kZeroWidthNoBreakSpace)
771 bEmptyChar = true;
772
773 int32_t iForms = bLam ? 3 : 1;
774 szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
775 if (!pCharPos) {
776 if (iWidth > 0)
777 wPrev = wch;
778 wLast = wch;
779 continue;
780 }
781
782 int32_t iCharWidth = iWidth;
783 if (iCharWidth < 0)
784 iCharWidth = -iCharWidth;
785
786 iCharWidth /= iFontSize;
787 FX_FORMCHAR formChars[3];
788 formChars[0].wch = wch;
789 formChars[0].wForm = wForm;
790 formChars[0].iWidth = iCharWidth;
791 if (bLam) {
792 formChars[1].wForm = pdfium::arabic::kArabicShadda;
793 formChars[1].iWidth =
794 pFont->GetCharWidth(pdfium::arabic::kArabicShadda).value_or(0);
795 formChars[2].wForm = pdfium::arabic::kArabicLetterSuperscriptAlef;
796 formChars[2].iWidth =
797 pFont->GetCharWidth(pdfium::arabic::kArabicLetterSuperscriptAlef)
798 .value_or(0);
799 }
800
801 for (int32_t j = 0; j < iForms; j++) {
802 wForm = (wchar_t)formChars[j].wForm;
803 iCharWidth = formChars[j].iWidth;
804 if (j > 0) {
805 chartype = FX_CHARTYPE::kCombination;
806 wch = wForm;
807 wLast = (wchar_t)formChars[j - 1].wForm;
808 }
809 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
810 pCharPos->m_GlyphIndex = pFont->GetGlyphIndex(wForm);
811 #if BUILDFLAG(IS_APPLE)
812 pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
813 #endif
814 pCharPos->m_FontCharWidth = iCharWidth;
815 }
816
817 const float fCharWidth = fFontSize * iCharWidth / 1000.0f;
818 if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
819 fX -= fCharWidth;
820
821 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
822 pCharPos->m_Origin = CFX_PointF(fX, fY);
823
824 if (!!(dwStyles & LayoutStyle::kCombText)) {
825 int32_t iFormWidth = pFont->GetCharWidth(wForm).value_or(iCharWidth);
826 float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
827 pCharPos->m_Origin.x += fOffset;
828 }
829 if (chartype == FX_CHARTYPE::kCombination) {
830 absl::optional<FX_RECT> rtBBox = pFont->GetCharBBox(wForm);
831 if (rtBBox.has_value()) {
832 pCharPos->m_Origin.y =
833 fYBase + fFontSize -
834 fFontSize * rtBBox.value().Height() / iMaxHeight;
835 }
836 if (wForm == wch &&
837 wLast != pdfium::unicode::kZeroWidthNoBreakSpace) {
838 if (pdfium::unicode::GetCharType(wLast) ==
839 FX_CHARTYPE::kCombination) {
840 absl::optional<FX_RECT> rtOtherBox = pFont->GetCharBBox(wLast);
841 if (rtOtherBox.has_value()) {
842 pCharPos->m_Origin.y -=
843 fFontSize * rtOtherBox.value().Height() / iMaxHeight;
844 }
845 }
846 }
847 }
848 }
849 if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
850 fX += fCharWidth;
851
852 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
853 pCharPos->m_bGlyphAdjust = true;
854 pCharPos->m_AdjustMatrix[0] = -1;
855 pCharPos->m_AdjustMatrix[1] = 0;
856 pCharPos->m_AdjustMatrix[2] = 0;
857 pCharPos->m_AdjustMatrix[3] = 1;
858
859 if (iHorScale != 100 || iVerScale != 100) {
860 pCharPos->m_AdjustMatrix[0] =
861 pCharPos->m_AdjustMatrix[0] * iHorScale / 100.0f;
862 pCharPos->m_AdjustMatrix[1] =
863 pCharPos->m_AdjustMatrix[1] * iHorScale / 100.0f;
864 pCharPos->m_AdjustMatrix[2] =
865 pCharPos->m_AdjustMatrix[2] * iVerScale / 100.0f;
866 pCharPos->m_AdjustMatrix[3] =
867 pCharPos->m_AdjustMatrix[3] * iVerScale / 100.0f;
868 }
869 pCharPos++;
870 }
871 }
872 if (iWidth > 0)
873 wPrev = static_cast<wchar_t>(formChars[0].wch);
874 wLast = wch;
875 }
876 return szCount;
877 }
878
GetCharRects(const Run & run) const879 std::vector<CFX_RectF> CFGAS_TxtBreak::GetCharRects(const Run& run) const {
880 if (run.iLength < 1)
881 return std::vector<CFX_RectF>();
882
883 Engine* pEngine = run.pEdtEngine;
884 const wchar_t* pStr = run.wsStr.c_str();
885 int32_t* pWidths = run.pWidths;
886 int32_t iLength = run.iLength;
887 CFX_RectF rect(*run.pRect);
888 float fFontSize = run.fFontSize;
889 bool bRTLPiece = !!(run.dwCharStyles & FX_TXTCHARSTYLE_OddBidiLevel);
890 bool bSingleLine = !!(run.dwStyles & LayoutStyle::kSingleLine);
891 float fStart = bRTLPiece ? rect.right() : rect.left;
892
893 std::vector<CFX_RectF> rtArray(iLength);
894 for (int32_t i = 0; i < iLength; i++) {
895 wchar_t wch;
896 int32_t iCharSize;
897 if (pEngine) {
898 int32_t iAbsolute = i + run.iStart;
899 wch = pEngine->GetChar(iAbsolute);
900 iCharSize = pEngine->GetWidthOfChar(iAbsolute);
901 } else {
902 wch = *pStr++;
903 iCharSize = *pWidths++;
904 }
905 float fCharSize = static_cast<float>(iCharSize) / kConversionFactor;
906 bool bRet = (!bSingleLine && IsCtrlCode(wch));
907 if (!(wch == L'\v' || wch == L'\f' ||
908 wch == pdfium::unicode::kLineSeparator ||
909 wch == pdfium::unicode::kParagraphSeparator || wch == L'\n')) {
910 bRet = false;
911 }
912 if (bRet)
913 fCharSize = fFontSize / 2.0f;
914 rect.left = fStart;
915 if (bRTLPiece) {
916 rect.left -= fCharSize;
917 fStart -= fCharSize;
918 } else {
919 fStart += fCharSize;
920 }
921 rect.width = fCharSize;
922 rtArray[i] = rect;
923 }
924 return rtArray;
925 }
926
927 CFGAS_TxtBreak::Engine::~Engine() = default;
928
929 CFGAS_TxtBreak::Run::Run() = default;
930
931 CFGAS_TxtBreak::Run::~Run() = default;
932
933 CFGAS_TxtBreak::Run::Run(const CFGAS_TxtBreak::Run& other) = default;
934