7#include "xfa/fgas/layout/cfgas_rtfbreak.h"
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"
30 m_bPagination = !!(m_dwLayoutStyles & LayoutStyle::kPagination);
39 m_pCurLine->m_iStart = iLinePos;
43 int32_t iTabPos =
std::min(
45 auto it = std::lower_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
47 if (it != m_PositionedTabs.end() && *it == iTabPos)
49 m_PositionedTabs.insert(it, iTabPos);
53 const RetainPtr<CFGAS_TextUserData>& pUserData) {
54 if (m_pUserData == pUserData)
58 m_pUserData = pUserData;
61bool CFGAS_RTFBreak::GetPositionedTab(int32_t* iTabPos)
const {
62 auto it = std::upper_bound(m_PositionedTabs.begin(), m_PositionedTabs.end(),
64 if (it == m_PositionedTabs.end())
74 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
75 m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
77 CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
80 pCurChar->m_pUserData = m_pUserData;
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)) {
90 if (!m_pCurLine->m_LineChars.empty())
91 pCurChar = &m_pCurLine->m_LineChars.back();
96 case FX_CHARTYPE::kTab:
97 AppendChar_Tab(pCurChar);
99 case FX_CHARTYPE::kControl:
100 dwRet2 = AppendChar_Control(pCurChar);
102 case FX_CHARTYPE::kCombination:
103 AppendChar_Combination(pCurChar);
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);
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);
121 m_eCharType = chartype;
122 return std::max(dwRet1, dwRet2);
125void CFGAS_RTFBreak::AppendChar_Combination(
CFGAS_Char* pCurChar) {
126 std::optional<uint16_t> iCharWidthRet;
128 iCharWidthRet = m_pFont->GetCharWidth(pCurChar->char_code());
135 if (pLastChar && pLastChar->GetCharType() > FX_CHARTYPE::kCombination)
138 m_eCharType = FX_CHARTYPE::kCombination;
140 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
142 if (iCharWidthValid > 0) {
144 checked_width += iCharWidthValid;
145 if (!checked_width.IsValid())
148 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
152void CFGAS_RTFBreak::AppendChar_Tab(
CFGAS_Char* pCurChar) {
153 if (!(m_dwLayoutStyles & LayoutStyle::kExpandTab))
156 int32_t& iLineWidth = m_pCurLine->m_iWidth;
157 int32_t iCharWidth = iLineWidth;
159 if (GetPositionedTab(&iCharWidth)) {
160 iSafeCharWidth = iCharWidth;
163 DCHECK(m_iTabWidth >= kMinimumTabWidth);
167 iSafeCharWidth -= iLineWidth;
169 iCharWidth = iSafeCharWidth.ValueOrDefault(0);
172 iLineWidth += iCharWidth;
200 m_pCurLine->IncrementArabicCharCount();
205 if (m_eCharType >= FX_CHARTYPE::kArabicAlef &&
206 m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
209 m_pCurLine->m_iWidth -= pLastChar->m_iCharWidth;
212 bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
213 pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
216 std::optional<uint16_t> iCharWidthRet = m_pFont->GetCharWidth(wForm);
217 if (iCharWidthRet.has_value()) {
218 iCharWidth = iCharWidthRet.value();
220 iCharWidthRet = m_pFont->GetCharWidth(pLastChar->char_code());
221 iCharWidth = iCharWidthRet.value_or(0);
228 int iCharWidthValid = iCharWidth.ValueOrDefault(0);
232 checked_width += iCharWidthValid;
233 if (!checked_width.IsValid())
236 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
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);
254 int iCharWidthValid = iCharWidth.ValueOrDefault(0);
258 checked_width += iCharWidthValid;
259 if (!checked_width.IsValid())
262 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
264 if (IsGreaterThanLineWidth(m_pCurLine->GetLineEnd()))
270 FX_CHARTYPE chartype = pCurChar->GetCharType();
274 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
281 int iValidCharWidth = iCharWidth.ValueOrDefault(0);
285 checked_width += iValidCharWidth;
286 if (!checked_width.IsValid())
289 m_pCurLine->m_iWidth = checked_width.ValueOrDie();
290 if (chartype != FX_CHARTYPE::kSpace &&
291 IsGreaterThanLineWidth(m_pCurLine->GetLineEnd())) {
298 DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
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();
308 if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
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();
324 m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
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);
334 m_pCurLine = pNextLine;
335 m_pCurLine->m_iStart = m_iLineStart;
338 m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
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:
354 SplitTextLine(m_pCurLine, pNextLine, !m_bPagination && bAllChars);
360 if (!m_bPagination) {
361 if (bAllChars && !bDone) {
362 int32_t endPos = m_pCurLine->GetLineEnd();
363 GetBreakPos(m_pCurLine->m_LineChars, bAllChars,
true, &endPos);
368 const CFGAS_Char* pCurChars = m_pCurLine->m_LineChars.data();
370 tp.SetChars(&m_pCurLine->m_LineChars);
372 uint32_t dwIdentity =
static_cast<uint32_t>(-1);
373 int32_t iLast = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars) - 1;
375 for (int32_t i = 0; i <= iLast;) {
399 m_pCurLine->m_LinePieces.push_back(tp);
409std::deque<CFGAS_Break::TPO> CFGAS_RTFBreak::EndBreakBidiLine(
412 std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
413 if (!m_bPagination && m_pCurLine->HasArabicChar()) {
415 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
418 if (pTC->GetCharType() != FX_CHARTYPE::kControl)
423 CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
425 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
435 tp.SetStartPos(m_pCurLine->m_iStart);
438 int32_t iBidiLevel = -1;
440 std::deque<TPO> tpos;
441 uint32_t dwIdentity =
static_cast<uint32_t>(-1);
444 int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
447 if (iBidiLevel < 0) {
463 m_pCurLine->m_LinePieces.push_back(tp);
479 m_pCurLine->m_LinePieces.push_back(tp);
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();
493void CFGAS_RTFBreak::EndBreakAlignment(
const std::deque<TPO>& tpos,
496 int32_t iNetWidth = m_pCurLine->m_iWidth;
497 int32_t iGapChars = 0;
499 for (
const TPO& pos : pdfium::Reversed(tpos)) {
500 const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
502 iNetWidth = ttp.GetEndPos();
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)
511 if (!bFind || !bAllChars) {
512 FX_CHARTYPE dwCharType = tc->GetCharType();
513 if (dwCharType == FX_CHARTYPE::kSpace ||
514 dwCharType == FX_CHARTYPE::kControl) {
516 int32_t iCharWidth = tc->m_iCharWidth;
517 if (bAllChars && iCharWidth > 0)
518 iNetWidth -= iCharWidth;
526 j += bArabic ? 1 : -1;
528 if (!bAllChars && bFind)
537 for (
const auto& tpo : tpos) {
538 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
540 iStart = ttp.GetStartPos();
542 ttp.SetStartPos(iStart);
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) {
550 int32_t k = iOffset / iGapChars;
551 tc->m_iCharWidth += k;
552 ttp.IncrementWidth(k);
558 iStart += ttp.GetWidth();
565 for (
auto& ttp : m_pCurLine->m_LinePieces)
566 ttp.IncrementStartPos(iOffset);
571int32_t CFGAS_RTFBreak::GetBreakPos(std::vector<CFGAS_Char>& tca,
575 int32_t iLength =
fxcrt::CollectionSize<int32_t>(tca) - 1;
580 int32_t iBreakPos = -1;
581 int32_t iIndirect = -1;
582 int32_t iIndirectPos = -1;
584 int32_t iLastPos = -1;
590 iBreakPos = *pEndPos;
599 FX_BREAKPROPERTY nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
602 *pEndPos -= iCharWidth;
604 while (iLength >= 0) {
606 FX_BREAKPROPERTY nCur =
607 pdfium::unicode::GetBreakProperty(pCur->char_code());
608 bool bNeedBreak =
false;
610 if (nCur == FX_BREAKPROPERTY::kTB) {
612 eType = nNext == FX_BREAKPROPERTY::kTB
613 ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
614 : GetLineBreakTypeFromPair(nCur, nNext);
616 if (nCur == FX_BREAKPROPERTY::kSP)
619 eType = nNext == FX_BREAKPROPERTY::kSP
620 ? FX_LINEBREAKTYPE::kPROHIBITED_BRK
621 : GetLineBreakTypeFromPair(nCur, nNext);
631 iBreakPos = *pEndPos;
636 iIndirectPos = *pEndPos;
644 *pEndPos -= iCharWidth;
653 *pEndPos = iBreakPos;
656 if (iIndirect > -1) {
657 *pEndPos = iIndirectPos;
673 if (pCurLine->m_LineChars.size() < 2)
677 std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
678 int32_t iCharPos = GetBreakPos(curChars, bAllChars,
false, &iEndPos);
683 if (iCharPos >=
fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
689 pNextLine->m_LineChars =
690 std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
691 curChars.erase(curChars.begin() + iCharPos, curChars.end());
697 for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
698 if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
707 pdfium::span<TextCharPos> pCharPos)
const {
708 if (pPiece
->iChars == 0 || !pPiece->pFont)
711 RetainPtr<CFGAS_GEFont> pFont = pPiece->pFont;
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;
725 float fX = rtText
.left;
731 float fY = rtText
.top + fAscent;
733 for (int32_t i = 0; i < pPiece
->iChars; ++i) {
735 wchar_t wch = pPiece->szText[i];
736 int32_t iWidth = pPiece->Widths[i];
737 FX_CHARTYPE dwCharType = pdfium::unicode::GetCharType(wch);
739 if (dwCharType == FX_CHARTYPE::kArabicAlef)
744 int iCharWidth = abs(iWidth);
745 const bool bEmptyChar = (dwCharType >= FX_CHARTYPE::kTab &&
746 dwCharType <= FX_CHARTYPE::kControl);
750 iCharWidth /= iFontSize;
752 if (dwCharType >= FX_CHARTYPE::kArabicAlef) {
754 wNext = pPiece->szText[i + 1];
755 if (pPiece->Widths[i + 1] < 0 && i + 2 < pPiece
->iChars)
756 wNext = pPiece->szText[i + 2];
761 }
else if (bRTLPiece) {
769#if BUILDFLAG(IS_APPLE)
770 current_char_pos.m_ExtGID = current_char_pos.m_GlyphIndex;
775 float fCharWidth = fFontSize * iCharWidth / 1000.0f;
776 if (bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
780 current_char_pos.m_Origin = CFX_PointF(fX, fY);
781 if (!bRTLPiece && dwCharType != FX_CHARTYPE::kCombination)
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;
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;
int32_t GetLineEnd() const
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 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 SetFontSize(int32_t font_size)
void SetHorizontalScale(int32_t scale)
void SetWidth(int32_t width)
void SetUserData(const RetainPtr< CFGAS_TextUserData > &user_data)
int32_t m_iHorizontalScale
CFGAS_Char * GetLastChar(int32_t index, bool bOmitChar, bool bRichText) const
wchar_t m_wParagraphBreakChar
static const float kConversionFactor
int16_t vertical_scale() const
int16_t horizonal_scale() const
FX_LINEBREAKTYPE m_eLineBreakType
uint16_t char_code() const
void SetUserData(const RetainPtr< CFGAS_TextUserData > &pUserData)
CFGAS_Char::BreakType AppendChar(wchar_t wch)
CFGAS_RTFBreak(Mask< LayoutStyle > dwLayoutStyles)
~CFGAS_RTFBreak() override
CFGAS_Char::BreakType EndBreak(CFGAS_Char::BreakType dwStatus)
void SetLineStartPos(float fLinePos)
size_t GetDisplayPos(const CFGAS_TextPiece *pPiece, pdfium::span< TextCharPos > pCharPos) const
void AddPositionedTab(float fTabPos)
CFX_RectF(const CFX_RectF &other)=default
pdfium::CheckedNumeric< int32_t > FX_SAFE_INT32
int FXSYS_roundf(float f)
wchar_t GetMirrorChar(wchar_t wch)
constexpr wchar_t kZeroWidthNoBreakSpace
constexpr wchar_t kLineSeparator
constexpr wchar_t kParagraphSeparator
wchar_t GetArabicFormChar(const CFGAS_Char *cur, const CFGAS_Char *prev, const CFGAS_Char *next)
wchar_t GetArabicFormChar(wchar_t wch, wchar_t prev, wchar_t next)