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