1 // Copyright 2017 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_break.h"
8 
9 #include <algorithm>
10 #include <vector>
11 
12 #include "core/fxcrt/fx_safe_types.h"
13 #include "third_party/base/stl_util.h"
14 #include "xfa/fgas/font/cfgas_gefont.h"
15 
16 const float CFX_Break::kConversionFactor = 20000.0f;
17 const int CFX_Break::kMinimumTabWidth = 160000;
18 
CFX_Break(uint32_t dwLayoutStyles)19 CFX_Break::CFX_Break(uint32_t dwLayoutStyles)
20     : m_dwLayoutStyles(dwLayoutStyles), m_pCurLine(&m_Lines[0]) {}
21 
22 CFX_Break::~CFX_Break() = default;
23 
Reset()24 void CFX_Break::Reset() {
25   m_eCharType = FX_CHARTYPE::kUnknown;
26   for (CFX_BreakLine& line : m_Lines)
27     line.Clear();
28 }
29 
SetLayoutStyles(uint32_t dwLayoutStyles)30 void CFX_Break::SetLayoutStyles(uint32_t dwLayoutStyles) {
31   m_dwLayoutStyles = dwLayoutStyles;
32   m_bSingleLine = (m_dwLayoutStyles & FX_LAYOUTSTYLE_SingleLine) != 0;
33   m_bCombText = (m_dwLayoutStyles & FX_LAYOUTSTYLE_CombText) != 0;
34 }
35 
SetHorizontalScale(int32_t iScale)36 void CFX_Break::SetHorizontalScale(int32_t iScale) {
37   iScale = std::max(iScale, 0);
38   if (m_iHorizontalScale == iScale)
39     return;
40 
41   SetBreakStatus();
42   m_iHorizontalScale = iScale;
43 }
44 
SetVerticalScale(int32_t iScale)45 void CFX_Break::SetVerticalScale(int32_t iScale) {
46   if (iScale < 0)
47     iScale = 0;
48   if (m_iVerticalScale == iScale)
49     return;
50 
51   SetBreakStatus();
52   m_iVerticalScale = iScale;
53 }
54 
SetFont(const RetainPtr<CFGAS_GEFont> & pFont)55 void CFX_Break::SetFont(const RetainPtr<CFGAS_GEFont>& pFont) {
56   if (!pFont || pFont == m_pFont)
57     return;
58 
59   SetBreakStatus();
60   m_pFont = pFont;
61 }
62 
SetFontSize(float fFontSize)63 void CFX_Break::SetFontSize(float fFontSize) {
64   int32_t iFontSize = FXSYS_roundf(fFontSize * 20.0f);
65   if (m_iFontSize == iFontSize)
66     return;
67 
68   SetBreakStatus();
69   m_iFontSize = iFontSize;
70 }
71 
SetBreakStatus()72 void CFX_Break::SetBreakStatus() {
73   ++m_dwIdentity;
74   if (m_pCurLine->m_LineChars.empty())
75     return;
76 
77   CFX_Char* tc = m_pCurLine->GetChar(m_pCurLine->m_LineChars.size() - 1);
78   if (tc->m_dwStatus == CFX_BreakType::None)
79     tc->m_dwStatus = CFX_BreakType::Piece;
80 }
81 
IsGreaterThanLineWidth(int32_t width) const82 bool CFX_Break::IsGreaterThanLineWidth(int32_t width) const {
83   FX_SAFE_INT32 line_width = m_iLineWidth;
84   line_width += m_iTolerance;
85   return line_width.IsValid() && width > line_width.ValueOrDie();
86 }
87 
GetUnifiedCharType(FX_CHARTYPE chartype) const88 FX_CHARTYPE CFX_Break::GetUnifiedCharType(FX_CHARTYPE chartype) const {
89   return chartype >= FX_CHARTYPE::kArabicAlef ? FX_CHARTYPE::kArabic : chartype;
90 }
91 
SetTabWidth(float fTabWidth)92 void CFX_Break::SetTabWidth(float fTabWidth) {
93   // Note, the use of max here was only done in the TxtBreak code. Leaving this
94   // in for the RTFBreak code for consistency. If we see issues with tab widths
95   // we may need to fix this.
96   m_iTabWidth =
97       std::max(FXSYS_roundf(fTabWidth * kConversionFactor), kMinimumTabWidth);
98 }
99 
SetParagraphBreakChar(wchar_t wch)100 void CFX_Break::SetParagraphBreakChar(wchar_t wch) {
101   if (wch != L'\r' && wch != L'\n')
102     return;
103   m_wParagraphBreakChar = wch;
104 }
105 
SetLineBreakTolerance(float fTolerance)106 void CFX_Break::SetLineBreakTolerance(float fTolerance) {
107   m_iTolerance = FXSYS_roundf(fTolerance * kConversionFactor);
108 }
109 
SetCharSpace(float fCharSpace)110 void CFX_Break::SetCharSpace(float fCharSpace) {
111   m_iCharSpace = FXSYS_roundf(fCharSpace * kConversionFactor);
112 }
113 
SetLineBoundary(float fLineStart,float fLineEnd)114 void CFX_Break::SetLineBoundary(float fLineStart, float fLineEnd) {
115   if (fLineStart > fLineEnd)
116     return;
117 
118   m_iLineStart = FXSYS_roundf(fLineStart * kConversionFactor);
119   m_iLineWidth = FXSYS_roundf(fLineEnd * kConversionFactor);
120   m_pCurLine->m_iStart = std::min(m_pCurLine->m_iStart, m_iLineWidth);
121   m_pCurLine->m_iStart = std::max(m_pCurLine->m_iStart, m_iLineStart);
122 }
123 
GetLastChar(int32_t index,bool bOmitChar,bool bRichText) const124 CFX_Char* CFX_Break::GetLastChar(int32_t index,
125                                  bool bOmitChar,
126                                  bool bRichText) const {
127   std::vector<CFX_Char>& tca = m_pCurLine->m_LineChars;
128   if (!pdfium::IndexInBounds(tca, index))
129     return nullptr;
130 
131   int32_t iStart = pdfium::CollectionSize<int32_t>(tca) - 1;
132   while (iStart > -1) {
133     CFX_Char* pTC = &tca[iStart--];
134     if (((bRichText && pTC->m_iCharWidth < 0) || bOmitChar) &&
135         pTC->GetCharType() == FX_CHARTYPE::kCombination) {
136       continue;
137     }
138     if (--index < 0)
139       return pTC;
140   }
141   return nullptr;
142 }
143 
CountBreakPieces() const144 int32_t CFX_Break::CountBreakPieces() const {
145   return HasLine() ? pdfium::CollectionSize<int32_t>(
146                          m_Lines[m_iReadyLineIndex].m_LinePieces)
147                    : 0;
148 }
149 
GetBreakPieceUnstable(int32_t index) const150 const CFX_BreakPiece* CFX_Break::GetBreakPieceUnstable(int32_t index) const {
151   if (!HasLine())
152     return nullptr;
153   if (!pdfium::IndexInBounds(m_Lines[m_iReadyLineIndex].m_LinePieces, index))
154     return nullptr;
155   return &m_Lines[m_iReadyLineIndex].m_LinePieces[index];
156 }
157 
ClearBreakPieces()158 void CFX_Break::ClearBreakPieces() {
159   if (HasLine())
160     m_Lines[m_iReadyLineIndex].Clear();
161   m_iReadyLineIndex = -1;
162 }
163