7#include "xfa/fgas/layout/cfgas_txtbreak.h"
12#include "build/build_config.h"
13#include "core/fxcrt/check.h"
14#include "core/fxcrt/compiler_specific.h"
15#include "core/fxcrt/containers/adapters.h"
16#include "core/fxcrt/fx_codepage.h"
17#include "core/fxcrt/fx_extension.h"
18#include "core/fxcrt/fx_safe_types.h"
19#include "core/fxcrt/numerics/safe_conversions.h"
20#include "core/fxcrt/stl_util.h"
21#include "core/fxge/text_char_pos.h"
22#include "xfa/fgas/font/cfgas_gefont.h"
23#include "xfa/fgas/layout/cfgas_char.h"
24#include "xfa/fgas/layout/fgas_arabic.h"
25#include "xfa/fgas/layout/fgas_linebreak.h"
35bool IsCtrlCode(
wchar_t wch) {
36 FX_CHARTYPE dwRet = pdfium::unicode::GetCharType(wch);
37 return dwRet == FX_CHARTYPE::kTab || dwRet == FX_CHARTYPE::kControl;
48 DCHECK(m_iLineWidth >= 20000);
52 DCHECK(iAlignment >= CFX_TxtLineAlignment_Left);
53 DCHECK(iAlignment <= CFX_TxtLineAlignment_Justified);
54 m_iAlignment = iAlignment;
61void CFGAS_TxtBreak::AppendChar_Combination(
CFGAS_Char* pCurChar) {
70 std::optional<uint16_t> maybe_shadda;
76 if (maybe_shadda.has_value()) {
77 wch = maybe_shadda.value();
83 std::optional<uint16_t> iCharWidthRet;
85 iCharWidthRet = m_pFont->GetCharWidth(wch);
87 iCharWidth = iCharWidthRet.value_or(0);
96void CFGAS_TxtBreak::AppendChar_Tab(
CFGAS_Char* pCurChar) {
97 m_eCharType = FX_CHARTYPE::kTab;
101 m_eCharType = FX_CHARTYPE::kControl;
128 FX_CHARTYPE chartype = pCurChar->GetCharType();
129 int32_t& iLineWidth = m_pCurLine->m_iWidth;
133 if (!m_bCombText && m_eCharType >= FX_CHARTYPE::kArabicAlef &&
134 m_eCharType <= FX_CHARTYPE::kArabicDistortion) {
144 bAlef = (wForm == pdfium::unicode::kZeroWidthNoBreakSpace &&
145 pLastChar->GetCharType() == FX_CHARTYPE::kArabicAlef);
147 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
156 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
158 iLineWidth += iCharWidthValid;
162 m_eCharType = chartype;
167 iCharWidth = m_iCombWidth;
169 if (m_pFont && wForm != pdfium::unicode::kZeroWidthNoBreakSpace) {
170 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
177 int32_t iCharWidthValid = iCharWidth.ValueOrDefault(0);
179 iLineWidth += iCharWidthValid;
181 m_pCurLine->IncrementArabicCharCount();
188 FX_CHARTYPE chartype = pCurChar->GetCharType();
189 int32_t& iLineWidth = m_pCurLine->m_iWidth;
190 m_eCharType = chartype;
196 iCharWidth = m_iCombWidth;
197 }
else if (m_pFont) {
198 iCharWidth = m_pFont->GetCharWidth(wForm).value_or(0);
205 int32_t iValidCharWidth = iCharWidth.ValueOrDefault(0);
207 iLineWidth += iValidCharWidth;
208 if (!m_bSingleLine && chartype != FX_CHARTYPE::kSpace &&
209 IsGreaterThanLineWidth(iLineWidth)) {
217 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
218 m_pCurLine->m_LineChars.emplace_back(wch, m_iHorizontalScale,
220 CFGAS_Char* pCurChar = &m_pCurLine->m_LineChars.back();
224 if (chartype != FX_CHARTYPE::kCombination &&
225 GetUnifiedCharType(m_eCharType) != GetUnifiedCharType(chartype) &&
226 m_eCharType != FX_CHARTYPE::kUnknown && !m_bSingleLine &&
227 IsGreaterThanLineWidth(m_pCurLine->m_iWidth) &&
228 (m_eCharType != FX_CHARTYPE::kSpace ||
229 chartype != FX_CHARTYPE::kControl)) {
231 if (!m_pCurLine->m_LineChars.empty())
232 pCurChar = &m_pCurLine->m_LineChars.back();
244 case FX_CHARTYPE::kTab:
245 AppendChar_Tab(pCurChar);
247 case FX_CHARTYPE::kControl:
248 dwRet2 = AppendChar_Control(pCurChar);
250 case FX_CHARTYPE::kCombination:
251 AppendChar_Combination(pCurChar);
253 case FX_CHARTYPE::kArabicAlef:
254 case FX_CHARTYPE::kArabicSpecial:
255 case FX_CHARTYPE::kArabicDistortion:
256 case FX_CHARTYPE::kArabicNormal:
257 case FX_CHARTYPE::kArabicForm:
258 case FX_CHARTYPE::kArabic:
259 dwRet2 = AppendChar_Arabic(pCurChar);
261 case FX_CHARTYPE::kUnknown:
262 case FX_CHARTYPE::kSpace:
263 case FX_CHARTYPE::kNumeric:
264 case FX_CHARTYPE::kNormal:
265 dwRet2 = AppendChar_Others(pCurChar);
269 return std::max(dwRet1, dwRet2);
276 if (!m_bSingleLine && IsGreaterThanLineWidth(m_pCurLine->m_iWidth)) {
277 pTC = m_pCurLine->LastChar();
278 switch (pTC->GetCharType()) {
279 case FX_CHARTYPE::kTab:
280 case FX_CHARTYPE::kControl:
281 case FX_CHARTYPE::kSpace:
284 SplitTextLine(m_pCurLine, pNextLine, bAllChars);
289 if (bAllChars && !bDone) {
290 int32_t iEndPos = m_pCurLine->m_iWidth;
291 GetBreakPos(&m_pCurLine->m_LineChars, bAllChars,
true, &iEndPos);
295std::deque<CFGAS_Break::TPO> CFGAS_TxtBreak::EndBreakBidiLine(
298 std::deque<TPO> tpos;
300 std::vector<CFGAS_Char>& chars = m_pCurLine->m_LineChars;
301 if (!m_pCurLine->HasArabicChar()) {
303 tp.SetStartPos(m_pCurLine->m_iStart);
304 tp.SetWidth(m_pCurLine->m_iWidth);
306 tp.SetCharCount(fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars));
307 tp.SetChars(&m_pCurLine->m_LineChars);
312 m_pCurLine->m_LinePieces.push_back(tp);
313 tpos.push_back({0, 0});
318 for (size_t i = 0; i < m_pCurLine->m_LineChars.size(); ++i) {
321 if (pTC->GetCharType() != FX_CHARTYPE::kControl)
326 CFGAS_Char::BidiLine(&chars, iBidiNum + 1);
329 tp.SetStartPos(m_pCurLine->m_iStart);
330 tp.SetChars(&m_pCurLine->m_LineChars);
331 int32_t iBidiLevel = -1;
335 int32_t iCount = fxcrt::CollectionSize<int32_t>(m_pCurLine->m_LineChars);
338 if (iBidiLevel < 0) {
359 m_pCurLine->m_LinePieces.push_back(tp);
375 m_pCurLine->m_LinePieces.push_back(tp);
380 std::sort(tpos.begin(), tpos.end());
381 int32_t iStartPos = 0;
382 for (i = 0; i <= j; i++) {
388 m_pCurLine->m_LinePieces[j].SetStatus(dwStatus);
393void CFGAS_TxtBreak::EndBreakAlignment(
const std::deque<TPO>& tpos,
396 int32_t iNetWidth = m_pCurLine->m_iWidth;
397 int32_t iGapChars = 0;
399 for (
const TPO& pos : pdfium::Reversed(tpos)) {
400 const CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[pos.index];
402 iNetWidth = ttp.GetEndPos();
404 bool bArabic =
FX_IsOdd(ttp.GetBidiLevel());
405 int32_t j = bArabic ? 0 : ttp.GetCharCount() - 1;
406 while (j > -1 && j < ttp.GetCharCount()) {
407 const CFGAS_Char* pTC = ttp.GetChar(j);
408 if (pTC->m_eLineBreakType == FX_LINEBREAKTYPE::kDIRECT_BRK)
410 if (!bFind || !bAllChars) {
411 FX_CHARTYPE chartype = pTC->GetCharType();
412 if (chartype == FX_CHARTYPE::kSpace ||
413 chartype == FX_CHARTYPE::kControl) {
414 if (!bFind && bAllChars && pTC->m_iCharWidth > 0)
415 iNetWidth -= pTC->m_iCharWidth;
422 j += bArabic ? 1 : -1;
424 if (!bAllChars && bFind)
432 for (
auto& tpo : tpos) {
433 CFGAS_BreakPiece& ttp = m_pCurLine->m_LinePieces[tpo.index];
435 iStart = ttp.GetStartPos();
437 ttp.SetStartPos(iStart);
439 for (int32_t j = 0; j < ttp.GetCharCount() && iGapChars > 0;
441 CFGAS_Char* pTC = ttp.GetChar(j);
442 if (pTC->m_eLineBreakType != FX_LINEBREAKTYPE::kDIRECT_BRK ||
443 pTC->m_iCharWidth < 0) {
446 int32_t k = iOffset / iGapChars;
447 pTC->m_iCharWidth += k;
448 ttp.IncrementWidth(k);
451 iStart += ttp.GetWidth();
460 for (
auto& ttp : m_pCurLine->m_LinePieces)
461 ttp.IncrementStartPos(iOffset);
467 DCHECK(dwStatus != CFGAS_Char::BreakType::kNone);
469 if (!m_pCurLine->m_LinePieces.empty()) {
470 if (dwStatus != CFGAS_Char::BreakType::kPiece)
471 m_pCurLine->m_LinePieces.back().SetStatus(dwStatus);
472 return m_pCurLine->m_LinePieces.back().GetStatus();
476 if (m_Lines[m_iReadyLineIndex].m_LinePieces.empty())
479 if (dwStatus != CFGAS_Char::BreakType::kPiece)
480 m_Lines[m_iReadyLineIndex].m_LinePieces.back().SetStatus(dwStatus);
481 return m_Lines[m_iReadyLineIndex].m_LinePieces.back().GetStatus();
484 if (m_pCurLine->m_LineChars.empty())
487 m_pCurLine->m_LineChars.back().m_dwStatus = dwStatus;
491 m_iReadyLineIndex = m_pCurLine == &m_Lines[0] ? 0 : 1;
494 EndBreakSplitLine(pNextLine, bAllChars);
496 std::deque<TPO> tpos = EndBreakBidiLine(dwStatus);
497 if (m_iAlignment > CFX_TxtLineAlignment_Left)
498 EndBreakAlignment(tpos, bAllChars, dwStatus);
500 m_pCurLine = pNextLine;
502 m_eCharType = pTC ? pTC->GetCharType() : FX_CHARTYPE::kUnknown;
506int32_t CFGAS_TxtBreak::GetBreakPos(std::vector<CFGAS_Char>* pChars,
510 std::vector<CFGAS_Char>& chars = *pChars;
511 int32_t iLength =
fxcrt::CollectionSize<int32_t>(chars) - 1;
516 int32_t iBreakPos = -1;
517 int32_t iIndirect = -1;
518 int32_t iIndirectPos = -1;
520 int32_t iLastPos = -1;
526 iBreakPos = *pEndPos;
530 FX_BREAKPROPERTY nCur;
531 FX_BREAKPROPERTY nNext;
536 nNext = pdfium::unicode::GetBreakProperty(pCur->char_code());
539 *pEndPos -= iCharWidth;
541 while (iLength >= 0) {
542 pCur = &chars[iLength];
543 nCur = pdfium::unicode::GetBreakProperty(pCur->char_code());
544 if (nNext == FX_BREAKPROPERTY::kSP)
547 eType = GetLineBreakTypeFromPair(nCur, nNext);
551 if (m_bSingleLine || *pEndPos <= m_iLineWidth ||
552 nCur == FX_BREAKPROPERTY::kSP) {
555 iBreakPos = *pEndPos;
560 iIndirectPos = *pEndPos;
569 *pEndPos -= iCharWidth;
577 *pEndPos = iBreakPos;
580 if (iIndirect > -1) {
581 *pEndPos = iIndirectPos;
597 if (pCurLine->m_LineChars.size() < 2)
601 std::vector<CFGAS_Char>& curChars = pCurLine->m_LineChars;
602 int32_t iCharPos = GetBreakPos(&curChars, bAllChars,
false, &iEndPos);
607 if (iCharPos >=
fxcrt::CollectionSize<int32_t>(pCurLine->m_LineChars)) {
614 pNextLine->m_LineChars =
615 std::vector<CFGAS_Char>(curChars.begin() + iCharPos, curChars.end());
616 curChars.erase(curChars.begin() + iCharPos, curChars.end());
621 for (size_t i = 0; i < pNextLine->m_LineChars.size(); ++i) {
622 if (pNextLine->m_LineChars[i].GetCharType() >= FX_CHARTYPE::kArabicAlef) {
626 iWidth +=
std::max(0, pNextLine->m_LineChars[i].m_iCharWidth);
633 pdfium::span<TextCharPos> pCharPos)
const {
637 Engine* pEngine = run.pEdtEngine;
639 pdfium::span<int32_t> pWidths = run.pWidths;
641 RetainPtr<CFGAS_GEFont> pFont = run.pFont;
647 const int32_t iAscent = pFont->GetAscent();
648 const int32_t iDescent = pFont->GetDescent();
649 const int32_t iMaxHeight = iAscent - iDescent;
650 const float fAscent = iMaxHeight ? fFontSize * iAscent / iMaxHeight : 0;
655 const float fYBase = rtText
.top + (rtText
.height - fFontSize) / 2.0f;
657 float fY = fYBase + fAscent;
665 bool bShadda =
false;
667 for (int32_t i = 0; i <= iLength; i++) {
668 int32_t iAbsolute = i + run
.iStart;
672 wch = pEngine->GetChar(iAbsolute);
673 iWidth = pEngine->GetWidthOfChar(iAbsolute);
676 pStr = pStr.Substr(1);
677 iWidth = pWidths.front();
678 pWidths = pWidths.subspan(1);
681 FX_CHARTYPE chartype = pdfium::unicode::GetCharType(wch);
682 if (chartype == FX_CHARTYPE::kArabicAlef && iWidth == 0) {
688 if (chartype >= FX_CHARTYPE::kArabicAlef) {
692 while (iNext <= iLength) {
693 int32_t iNextAbsolute = iNext + run
.iStart;
694 wNext = pEngine->GetChar(iNextAbsolute);
695 if (pdfium::unicode::GetCharType(wNext) !=
696 FX_CHARTYPE::kCombination) {
707 if (i + j >= iLength) {
711 }
while (pdfium::unicode::GetCharType(wNext) ==
712 FX_CHARTYPE::kCombination);
713 if (i + j >= iLength)
724 }
else if (chartype == FX_CHARTYPE::kCombination) {
726 if (wch >= 0x064C && wch <= 0x0651) {
734 if (iNext <= iLength) {
735 int32_t iNextAbsolute = iNext + run
.iStart;
736 wNext = pEngine->GetChar(iNextAbsolute);
738 }
else if (i < iLength) {
739 wNext = pStr.Front();
741 std::optional<uint16_t> maybe_shadda;
747 if (maybe_shadda.has_value()) {
748 wForm = maybe_shadda.value();
755 }
else if (chartype == FX_CHARTYPE::kNumeric) {
757 }
else if (wch == L'.') {
759 }
else if (wch == L',') {
761 }
else if (bRTLPiece) {
766 if (chartype != FX_CHARTYPE::kCombination)
768 if (chartype < FX_CHARTYPE::kArabicAlef)
772 (chartype >= FX_CHARTYPE::kTab && chartype <= FX_CHARTYPE::kControl);
776 int32_t iForms = bLam ? 3 : 1;
777 szCount += (bEmptyChar && bSkipSpace) ? 0 : iForms;
778 if (pCharPos.empty()) {
785 int32_t iCharWidth = iWidth;
787 iCharWidth = -iCharWidth;
789 iCharWidth /= iFontSize;
790 std::array<FX_FORMCHAR, 3> form_chars;
791 form_chars[0].wch = wch;
792 form_chars[0].wForm = wForm;
793 form_chars[0].iWidth = iCharWidth;
796 form_chars[1].iWidth =
799 form_chars[2].iWidth =
803 for (int32_t j = 0; j < iForms; j++) {
805 wForm = (
wchar_t)form_chars[j].wForm;
806 iCharWidth = form_chars[j].iWidth;
808 chartype = FX_CHARTYPE::kCombination;
810 wLast = (
wchar_t)form_chars[j - 1].wForm;
812 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
814#if BUILDFLAG(IS_APPLE)
815 front_ref.m_ExtGID = front_ref.m_GlyphIndex;
820 const float fCharWidth = fFontSize * iCharWidth / 1000.0f;
821 if (bRTLPiece && chartype != FX_CHARTYPE::kCombination)
824 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
825 front_ref.m_Origin = CFX_PointF(fX, fY);
828 int32_t iFormWidth = pFont->GetCharWidth(wForm).value_or(iCharWidth);
829 float fOffset = fFontSize * (iCharWidth - iFormWidth) / 2000.0f;
830 front_ref.m_Origin.x += fOffset;
832 if (chartype == FX_CHARTYPE::kCombination) {
833 std::optional<
FX_RECT> rtBBox = pFont->GetCharBBox(wForm);
834 if (rtBBox.has_value()) {
835 front_ref.m_Origin.y =
837 fFontSize * rtBBox.value().Height() / iMaxHeight;
841 if (pdfium::unicode::GetCharType(wLast) ==
842 FX_CHARTYPE::kCombination) {
843 std::optional<
FX_RECT> rtOtherBox = pFont->GetCharBBox(wLast);
844 if (rtOtherBox.has_value()) {
845 front_ref.m_Origin.y -=
846 fFontSize * rtOtherBox.value().Height() / iMaxHeight;
852 if (!bRTLPiece && chartype != FX_CHARTYPE::kCombination)
855 if (!bEmptyChar || (bEmptyChar && !bSkipSpace)) {
857 front_ref.m_AdjustMatrix[0] = -1;
858 front_ref.m_AdjustMatrix[1] = 0;
859 front_ref.m_AdjustMatrix[2] = 0;
860 front_ref.m_AdjustMatrix[3] = 1;
862 if (iHorScale != 100 || iVerScale != 100) {
863 front_ref.m_AdjustMatrix[0] =
864 front_ref.m_AdjustMatrix[0] * iHorScale / 100.0f;
865 front_ref.m_AdjustMatrix[1] =
866 front_ref.m_AdjustMatrix[1] * iHorScale / 100.0f;
867 front_ref.m_AdjustMatrix[2] =
868 front_ref.m_AdjustMatrix[2] * iVerScale / 100.0f;
869 front_ref.m_AdjustMatrix[3] =
870 front_ref.m_AdjustMatrix[3] * iVerScale / 100.0f;
872 pCharPos = pCharPos.subspan(1);
876 wPrev =
static_cast<
wchar_t>(form_chars[0].wch);
884 return std::vector<CFX_RectF>();
886 Engine* pEngine = run.pEdtEngine;
888 pdfium::span<int32_t> pWidths = run.pWidths;
896 std::vector<CFX_RectF> rtArray(iLength);
897 for (int32_t i = 0; i < iLength; i++) {
901 int32_t iAbsolute = i + run
.iStart;
902 wch = pEngine->GetChar(iAbsolute);
903 iCharSize = pEngine->GetWidthOfChar(iAbsolute);
906 pStr = pStr.Substr(1);
907 iCharSize = pWidths.front();
908 pWidths = pWidths.subspan(1);
911 bool bRet = (!bSingleLine && IsCtrlCode(wch));
912 if (!(wch == L'\v' || wch == L'\f' ||
918 fCharSize = fFontSize / 2.0f;
921 rect
.left -= fCharSize;
934CFGAS_TxtBreak::
Run::
Run() =
default;
936CFGAS_TxtBreak::
Run::~
Run() =
default;
938CFGAS_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
CFGAS_Char::BreakType EndBreak(CFGAS_Char::BreakType dwStatus)
~CFGAS_TxtBreak() override
void SetLineWidth(float fLineWidth)
size_t GetDisplayPos(const Run &run, pdfium::span< TextCharPos > pCharPos) const
CFGAS_Char::BreakType AppendChar(wchar_t wch)
void SetAlignment(int32_t iAlignment)
void SetCombWidth(float fCombWidth)
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
std::optional< wchar_t > GetArabicFromShaddaTable(wchar_t shadda)
wchar_t GetArabicFormChar(const CFGAS_Char *cur, const CFGAS_Char *prev, const CFGAS_Char *next)
constexpr wchar_t kArabicLetterLam
wchar_t GetArabicFormChar(wchar_t wch, wchar_t prev, wchar_t next)
constexpr wchar_t kArabicShadda
constexpr wchar_t kArabicLetterSuperscriptAlef
constexpr wchar_t kArabicLetterHeh
fxcrt::WideStringView WideStringView