7#include "xfa/fgas/layout/cfgas_txtbreak.h"
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"
33bool IsCtrlCode(
wchar_t wch) {
34 FX_CHARTYPE dwRet = pdfium::unicode::GetCharType(wch);
35 return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
46 DCHECK(m_iLineWidth >= 20000);
50 DCHECK(iAlignment >= CFX_TxtLineAlignment_Left);
51 DCHECK(iAlignment <= CFX_TxtLineAlignment_Justified);
52 m_iAlignment = iAlignment;
59void CFGAS_TxtBreak::AppendChar_Combination(
CFGAS_Char* pCurChar) {
60 FX_SAFE_INT32 iCharWidth = m_iCombWidth;
68 absl::optional<uint16_t> maybe_shadda;
70 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wLast);
72 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
74 if (maybe_shadda.has_value()) {
75 wch = maybe_shadda.value();
81 absl::optional<uint16_t> iCharWidthRet;
83 iCharWidthRet = m_pFont->GetCharWidth(wch);
85 iCharWidth = iCharWidthRet.value_or(0);
94void CFGAS_TxtBreak::AppendChar_Tab(
CFGAS_Char* pCurChar) {
95 m_eCharType = FX_CHARTYPE::kTab;
99 m_eCharType = FX_CHARTYPE::kControl;
126 FX_CHARTYPE chartype = pCurChar->GetCharType();
127 int32_t& iLineWidth = m_pCurLine->m_iWidth;
131 if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
132 m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
133 FX_SAFE_INT32 iCharWidth = 0;
142 bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
143 pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
145 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
154 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
156 iLineWidth += iCharWidthValid;
160 m_eCharType = chartype;
163 FX_SAFE_INT32 iCharWidth = 0;
165 iCharWidth = m_iCombWidth;
167 if (m_pFont && wForm != pdfium::unicode::kZeroWidthNoBreakSpace) {
168 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
175 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
177 iLineWidth += iCharWidthValid;
179 m_pCurLine->IncrementArabicCharCount();
186 FX_CHARTYPE chartype = pCurChar->GetCharType();
187 int32_t& iLineWidth = m_pCurLine->m_iWidth;
188 m_eCharType = chartype;
192 FX_SAFE_INT32 iCharWidth = 0;
194 iCharWidth = m_iCombWidth;
195 }
else if (m_pFont) {
196 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
203 int32_t iValidCharWidth = iCharWidth.ValueOrDefault(0);
205 iLineWidth += iValidCharWidth;
206 if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
207 IsGreaterThanLineWidth(iLineWidth)) {
215 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
216 m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
218 CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
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)) {
229 if (!m_pCurLine->m_LineChars.empty())
230 pCurChar = &m_pCurLine->m_LineChars.back();
242 case FX_CHARTYPE::kTab:
243 AppendChar_Tab(pCurChar);
245 case FX_CHARTYPE::kControl:
246 dwRet2 = AppendChar_Control(pCurChar);
248 case FX_CHARTYPE::kCombination:
249 AppendChar_Combination(pCurChar);
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);
259 case FX_CHARTYPE::kUnknown:
260 case FX_CHARTYPE::kSpace:
261 case FX_CHARTYPE::kNumeric:
262 case FX_CHARTYPE::kNormal:
263 dwRet2 = AppendChar_Others(pCurChar);
267 return std::max(dwRet1, dwRet2);
274 if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
275 pTC = m_pCurLine->LastChar();
276 switch (pTC->GetCharType()) {
277 case FX_CHARTYPE::kTab:
278 case FX_CHARTYPE::kControl:
279 case FX_CHARTYPE::kSpace:
282 SplitTextLine(m_pCurLine, pNextLine, bAllChars);
287 if (bAllChars && !bDone) {
288 int32_t iEndPos = m_pCurLine->m_iWidth;
289 GetBreakPos(&m_pCurLine->m_LineChars, bAllChars,
true, &iEndPos);
293std::deque<CFGAS_Break::TPO> CFGAS_TxtBreak::EndBreakBidiLine(
296 std::deque<TPO> tpos;
298 std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
299 if (!m_pCurLine->HasArabicChar()) {
301 tp.SetStartPos(m_pCurLine->m_iStart);
302 tp.SetWidth(m_pCurLine->m_iWidth);
304 tp.SetCharCount(fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars));
305 tp.SetChars(&m_pCurLine->m_LineChars);
310 m_pCurLine->m_LinePieces.push_back(tp);
311 tpos.push_back({0, 0});
316 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
319 if (pTC->GetCharType() != FX_CHARTYPE::kControl)
324 CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
327 tp.SetStartPos(m_pCurLine->m_iStart);
328 tp.SetChars(&m_pCurLine->m_LineChars);
329 int32_t iBidiLevel = -1;
333 int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
336 if (iBidiLevel < 0) {
357 m_pCurLine->m_LinePieces.push_back(tp);
373 m_pCurLine->m_LinePieces.push_back(tp);
378 std::sort(tpos.begin(), tpos.end());
379 int32_t iStartPos = 0;
380 for (i = 0; i <= j; i++) {
386 m_pCurLine->m_LinePieces[j].SetStatus(dwStatus);
391void CFGAS_TxtBreak::EndBreakAlignment(
const std::deque<TPO>& tpos,
394 int32_t iNetWidth = m_pCurLine->m_iWidth;
395 int32_t iGapChars = 0;
397 for (
const TPO& pos : pdfium::base::Reversed(tpos)) {
398 const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
400 iNetWidth = ttp.GetEndPos();
402 bool bArabic =
FX_IsOdd(ttp.GetBidiLevel());
403 int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
404 while (j > -1 && j < ttp.GetCharCount()) {
405 const CFGAS_Char* pTC = ttp.GetChar(j);
406 if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
408 if (!bFind || !bAllChars) {
409 FX_CHARTYPE chartype = pTC->GetCharType();
410 if (chartype == FX_CHARTYPE::kSpace ||
411 chartype == FX_CHARTYPE::kControl) {
412 if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
413 iNetWidth -= pTC->m_iCharWidth;
420 j += bArabic ? 1 : -1;
422 if (!bAllChars && bFind)
430 for (
auto& tpo : tpos) {
431 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
433 iStart = ttp.GetStartPos();
435 ttp.SetStartPos(iStart);
437 for (int32_t j = 0; j < ttp.GetCharCount() && iGapChars > 0;
439 CFGAS_Char* pTC = ttp.GetChar(j);
440 if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
441 pTC->m_iCharWidth < 0) {
444 int32_t k = iOffset / iGapChars;
445 pTC->m_iCharWidth += k;
446 ttp.IncrementWidth(k);
449 iStart += ttp.GetWidth();
458 for (
auto& ttp : m_pCurLine->m_LinePieces)
459 ttp.IncrementStartPos(iOffset);
465 DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
467 if (!m_pCurLine->m_LinePieces.empty()) {
468 if (dwStatus != CFGAS_Char::BreakType::kPiece)
469 m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
470 return m_pCurLine->m_LinePieces.back().GetStatus();
474 if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
477 if (dwStatus != CFGAS_Char::BreakType::kPiece)
478 m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
479 return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
482 if (m_pCurLine->m_LineChars.empty())
485 m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
489 m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
492 EndBreakSplitLine(pNextLine, bAllChars);
494 std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
495 if (m_iAlignment > CFX_TxtLineAlignment_Left)
496 EndBreakAlignment(tpos, bAllChars, dwStatus);
498 m_pCurLine = pNextLine;
500 m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
504int32_t CFGAS_TxtBreak::GetBreakPos(std::vector<CFGAS_Char>* pChars,
508 std::vector<CFGAS_Char>& chars = *pChars;
509 int32_t iLength =
fxcrt::CollectionSize<int32_t>(chars) - 1;
514 int32_t iBreakPos = -1;
515 int32_t iIndirect = -1;
516 int32_t iIndirectPos = -1;
518 int32_t iLastPos = -1;
524 iBreakPos = *pEndPos;
528 FX_BREAKPROPERTY nCur;
529 FX_BREAKPROPERTY nNext;
534 nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
537 *pEndPos -= iCharWidth;
539 while (iLength >= 0) {
540 pCur = &chars[iLength];
541 nCur = pdfium::unicode::GetBreakProperty(pCur->char_code());
542 if (nNext == FX_BREAKPROPERTY::kSP)
545 eType = GetLineBreakTypeFromPair(nCur, nNext);
549 if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
550 nCur == FX_BREAKPROPERTY::kSP) {
553 iBreakPos = *pEndPos;
558 iIndirectPos = *pEndPos;
567 *pEndPos -= iCharWidth;
575 *pEndPos = iBreakPos;
578 if (iIndirect > -1) {
579 *pEndPos = iIndirectPos;
595 if (pCurLine->m_LineChars.size() < 2)
599 std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
600 int32_t iCharPos = GetBreakPos(&curChars, bAllChars,
false, &iEndPos);
605 if (iCharPos >=
fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
612 pNextLine->m_LineChars =
613 std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
614 curChars.erase(curChars.begin() + iCharPos, curChars.end());
619 for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
620 if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
624 iWidth +=
std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
635 Engine* pEngine = run.pEdtEngine;
636 const wchar_t* pStr = run.wsStr.c_str();
639 RetainPtr<CFGAS_GEFont> pFont = run.pFont;
645 const int32_t iAscent = pFont->GetAscent();
646 const int32_t iDescent = pFont->GetDescent();
647 const int32_t iMaxHeight = iAscent - iDescent;
648 const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
653 const float fYBase = rtText
.top + (rtText
.height - fFontSize) / 2.0f;
655 float fY = fYBase + fAscent;
663 bool bShadda =
false;
665 for (int32_t i = 0; i <= iLength; i++) {
666 int32_t iAbsolute = i + run
.iStart;
670 wch = pEngine->GetChar(iAbsolute);
671 iWidth = pEngine->GetWidthOfChar(iAbsolute);
677 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
678 if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
684 if (chartype >= FX_CHARTYPE::kArabicAlef) {
688 while (iNext <= iLength) {
689 int32_t iNextAbsolute = iNext + run
.iStart;
690 wNext = pEngine->GetChar(iNextAbsolute);
691 if (pdfium::unicode::GetCharType(wNext) !=
692 FX_CHARTYPE::kCombination) {
703 if (i + j >= iLength)
707 }
while (pdfium::unicode::GetCharType(wNext) ==
708 FX_CHARTYPE::kCombination);
709 if (i + j >= iLength)
720 }
else if (chartype == FX_CHARTYPE::kCombination) {
722 if (wch >= 0x064C && wch <= 0x0651) {
730 if (iNext <= iLength) {
731 int32_t iNextAbsolute = iNext + run
.iStart;
732 wNext = pEngine->GetChar(iNextAbsolute);
734 }
else if (i < iLength) {
737 absl::optional<uint16_t> maybe_shadda;
739 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wNext);
741 maybe_shadda = pdfium::arabic::GetArabicFromShaddaTable(wch);
743 if (maybe_shadda.has_value()) {
744 wForm = maybe_shadda.value();
751 }
else if (chartype == FX_CHARTYPE::kNumeric) {
753 }
else if (wch == L'.') {
755 }
else if (wch == L',') {
757 }
else if (bRTLPiece) {
762 if (chartype != FX_CHARTYPE::kCombination)
764 if (chartype < FX_CHARTYPE::kArabicAlef)
768 (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
772 int32_t iForms = bLam ? 3 : 1;
773 szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
781 int32_t iCharWidth = iWidth;
783 iCharWidth = -iCharWidth;
785 iCharWidth /= iFontSize;
786 FX_FORMCHAR formChars[3];
787 formChars[0].wch = wch;
788 formChars[0].wForm = wForm;
789 formChars[0].iWidth = iCharWidth;
792 formChars[1].iWidth =
795 formChars[2].iWidth =
800 for (int32_t j = 0; j < iForms; j++) {
801 wForm = (
wchar_t)formChars[j].wForm;
802 iCharWidth = formChars[j].iWidth;
804 chartype = FX_CHARTYPE::kCombination;
806 wLast = (
wchar_t)formChars[j - 1].wForm;
808 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
810#if BUILDFLAG(IS_APPLE)
811 pCharPos->m_ExtGID = pCharPos->m_GlyphIndex;
816 const float fCharWidth = fFontSize * iCharWidth / 1000.0f;
817 if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
820 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
821 pCharPos->m_Origin = CFX_PointF(fX, fY);
824 int32_t iFormWidth = pFont->GetCharWidth(wForm).value_or(iCharWidth);
825 float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
826 pCharPos->m_Origin.x += fOffset;
828 if (chartype == FX_CHARTYPE::kCombination) {
829 absl::optional<FX_RECT> rtBBox = pFont->GetCharBBox(wForm);
830 if (rtBBox.has_value()) {
831 pCharPos->m_Origin.y =
833 fFontSize * rtBBox.value().Height() / iMaxHeight;
837 if (pdfium::unicode::GetCharType(wLast) ==
838 FX_CHARTYPE::kCombination) {
839 absl::optional<FX_RECT> rtOtherBox = pFont->GetCharBBox(wLast);
840 if (rtOtherBox.has_value()) {
841 pCharPos->m_Origin.y -=
842 fFontSize * rtOtherBox.value().Height() / iMaxHeight;
848 if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
851 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
858 if (iHorScale != 100 || iVerScale != 100) {
872 wPrev =
static_cast<
wchar_t>(formChars[0].wch);
880 return std::vector<CFX_RectF>();
882 Engine* pEngine = run.pEdtEngine;
883 const wchar_t* pStr = run.wsStr.c_str();
892 std::vector<CFX_RectF> rtArray(iLength);
893 for (int32_t i = 0; i < iLength; i++) {
897 int32_t iAbsolute = i + run
.iStart;
898 wch = pEngine->GetChar(iAbsolute);
899 iCharSize = pEngine->GetWidthOfChar(iAbsolute);
902 iCharSize = *pWidths++;
905 bool bRet = (!bSingleLine && IsCtrlCode(wch));
906 if (!(wch == L'\v' || wch == L'\f' ||
912 fCharSize = fFontSize / 2.0f;
915 rect
.left -= fCharSize;
928CFGAS_TxtBreak::
Run::
Run() =
default;
930CFGAS_TxtBreak::
Run::~
Run() =
default;
932CFGAS_TxtBreak::
Run::
Run(
const CFGAS_TxtBreak::
Run& other) =
default;
#define FX_TXTCHARSTYLE_ArabicShadda
@ CFX_TxtLineAlignment_Justified
@ CFX_TxtLineAlignment_Right
@ CFX_TxtLineAlignment_Center
#define FX_TXTCHARSTYLE_OddBidiLevel
void IncrementArabicCharCount()
void DecrementArabicCharCount()
int32_t GetStartChar() const
void SetVerticalScale(int32_t scale)
void SetStatus(CFGAS_Char::BreakType status)
void SetBidiLevel(int32_t level)
void SetStartPos(int32_t pos)
void SetCharCount(int32_t count)
void SetStartChar(int32_t pos)
int32_t GetBidiPos() const
void IncrementWidth(int32_t width)
void IncrementStartPos(int32_t count)
void SetBidiPos(int32_t pos)
void SetHorizontalScale(int32_t scale)
void SetCharStyles(uint32_t styles)
void SetWidth(int32_t width)
int32_t m_iHorizontalScale
CFGAS_Char * GetLastChar(int32_t index, bool bOmitChar, bool bRichText) const
wchar_t m_wParagraphBreakChar
bool IsGreaterThanLineWidth(int32_t width) const
static const float kConversionFactor
int16_t vertical_scale() const
int16_t horizonal_scale() const
FX_LINEBREAKTYPE m_eLineBreakType
uint16_t char_code() const
std::vector< CFX_RectF > GetCharRects(const Run &run) const
size_t GetDisplayPos(const Run &run, TextCharPos *pCharPos) const
CFGAS_Char::BreakType EndBreak(CFGAS_Char::BreakType dwStatus)
~CFGAS_TxtBreak() override
void SetLineWidth(float fLineWidth)
CFGAS_Char::BreakType AppendChar(wchar_t wch)
void SetAlignment(int32_t iAlignment)
void SetCombWidth(float fCombWidth)
int FXSYS_roundf(float f)
constexpr wchar_t kArabicLetterLam
wchar_t GetFormChar(const CFGAS_Char *cur, const CFGAS_Char *prev, const CFGAS_Char *next)
constexpr wchar_t kArabicShadda
constexpr wchar_t kArabicLetterSuperscriptAlef
wchar_t GetFormChar(wchar_t wch, wchar_t prev, wchar_t next)
constexpr wchar_t kArabicLetterHeh
wchar_t GetMirrorChar(wchar_t wch)
constexpr wchar_t kZeroWidthNoBreakSpace
constexpr wchar_t kLineSeparator
constexpr wchar_t kParagraphSeparator
UNOWNED_PTR_EXCLUSION int32_t * pWidths