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/fde/cfde_txtedtpage.h"
8
9 #include <algorithm>
10
11 #include "third_party/base/ptr_util.h"
12 #include "third_party/base/stl_util.h"
13 #include "xfa/fde/cfde_txtedtbuf.h"
14 #include "xfa/fde/cfde_txtedtengine.h"
15 #include "xfa/fde/cfde_txtedtparag.h"
16 #include "xfa/fde/cfde_txtedttextset.h"
17 #include "xfa/fde/cfx_wordbreak.h"
18 #include "xfa/fde/ifde_txtedtengine.h"
19 #include "xfa/fde/ifde_txtedtpage.h"
20
21 namespace {
22
23 const double kTolerance = 0.1f;
24
25 } // namespace
26
Create(CFDE_TxtEdtEngine * pEngine,int32_t nIndex)27 IFDE_TxtEdtPage* IFDE_TxtEdtPage::Create(CFDE_TxtEdtEngine* pEngine,
28 int32_t nIndex) {
29 return new CFDE_TxtEdtPage(pEngine, nIndex);
30 }
31
CFDE_TxtEdtPage(CFDE_TxtEdtEngine * pEngine,int32_t nPageIndex)32 CFDE_TxtEdtPage::CFDE_TxtEdtPage(CFDE_TxtEdtEngine* pEngine, int32_t nPageIndex)
33 : m_pEditEngine(pEngine),
34 m_pBgnParag(nullptr),
35 m_pEndParag(nullptr),
36 m_nRefCount(0),
37 m_nPageStart(-1),
38 m_nCharCount(0),
39 m_nPageIndex(nPageIndex),
40 m_bLoaded(false) {
41 }
42
~CFDE_TxtEdtPage()43 CFDE_TxtEdtPage::~CFDE_TxtEdtPage() {}
44
GetEngine() const45 CFDE_TxtEdtEngine* CFDE_TxtEdtPage::GetEngine() const {
46 return m_pEditEngine;
47 }
48
GetType()49 FDE_VISUALOBJTYPE CFDE_TxtEdtPage::GetType() {
50 return FDE_VISUALOBJ_Text;
51 }
52
GetRect(const FDE_TEXTEDITPIECE & hVisualObj)53 CFX_RectF CFDE_TxtEdtPage::GetRect(const FDE_TEXTEDITPIECE& hVisualObj) {
54 return CFX_RectF();
55 }
56
GetCharRect(int32_t nIndex,CFX_RectF & rect,bool bBBox) const57 int32_t CFDE_TxtEdtPage::GetCharRect(int32_t nIndex,
58 CFX_RectF& rect,
59 bool bBBox) const {
60 ASSERT(m_nRefCount > 0);
61 ASSERT(nIndex >= 0 && nIndex < m_nCharCount);
62 if (m_nRefCount < 1)
63 return 0;
64
65 for (const auto& piece : m_Pieces) {
66 if (nIndex >= piece.nStart && nIndex < piece.nStart + piece.nCount) {
67 std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(&piece, bBBox);
68 rect = rectArr[nIndex - piece.nStart];
69 return piece.nBidiLevel;
70 }
71 }
72 ASSERT(0);
73 return 0;
74 }
75
GetCharIndex(const CFX_PointF & fPoint,bool & bBefore)76 int32_t CFDE_TxtEdtPage::GetCharIndex(const CFX_PointF& fPoint, bool& bBefore) {
77 CFX_PointF ptF = fPoint;
78 NormalizePt2Rect(ptF, m_rtPageContents, kTolerance);
79 int32_t nCount = pdfium::CollectionSize<int32_t>(m_Pieces);
80 CFX_RectF rtLine;
81 int32_t nBgn = 0;
82 int32_t nEnd = 0;
83 bool bInLine = false;
84 int32_t i = 0;
85 for (i = 0; i < nCount; i++) {
86 const FDE_TEXTEDITPIECE* pPiece = &m_Pieces[i];
87 if (!bInLine &&
88 (pPiece->rtPiece.top <= ptF.y && pPiece->rtPiece.bottom() > ptF.y)) {
89 nBgn = nEnd = i;
90 rtLine = pPiece->rtPiece;
91 bInLine = true;
92 } else if (bInLine) {
93 if (pPiece->rtPiece.bottom() <= ptF.y || pPiece->rtPiece.top > ptF.y) {
94 nEnd = i - 1;
95 break;
96 } else {
97 rtLine.Union(pPiece->rtPiece);
98 }
99 }
100 }
101 NormalizePt2Rect(ptF, rtLine, kTolerance);
102 int32_t nCaret = 0;
103 FDE_TEXTEDITPIECE* pPiece = nullptr;
104 for (i = nBgn; i <= nEnd; i++) {
105 pPiece = &m_Pieces[i];
106 nCaret = m_nPageStart + pPiece->nStart;
107 if (pPiece->rtPiece.Contains(ptF)) {
108 std::vector<CFX_RectF> rectArr = m_pTextSet->GetCharRects(pPiece, false);
109 int32_t nRtCount = pdfium::CollectionSize<int32_t>(rectArr);
110 for (int32_t j = 0; j < nRtCount; j++) {
111 if (rectArr[j].Contains(ptF)) {
112 nCaret = m_nPageStart + pPiece->nStart + j;
113 if (nCaret >= m_pEditEngine->GetTextBufLength()) {
114 bBefore = true;
115 return m_pEditEngine->GetTextBufLength();
116 }
117 FX_WCHAR wChar = m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret);
118 if (wChar == L'\n' || wChar == L'\r') {
119 if (wChar == L'\n') {
120 if (m_pEditEngine->GetTextBuf()->GetCharByIndex(nCaret - 1) ==
121 L'\r') {
122 nCaret--;
123 }
124 }
125 bBefore = true;
126 return nCaret;
127 }
128 if (ptF.x > ((rectArr[j].left + rectArr[j].right()) / 2)) {
129 bBefore = FX_IsOdd(pPiece->nBidiLevel);
130 } else {
131 bBefore = !FX_IsOdd(pPiece->nBidiLevel);
132 }
133 return nCaret;
134 }
135 }
136 }
137 }
138 bBefore = true;
139 return nCaret;
140 }
141
GetCharStart() const142 int32_t CFDE_TxtEdtPage::GetCharStart() const {
143 return m_nPageStart;
144 }
145
GetCharCount() const146 int32_t CFDE_TxtEdtPage::GetCharCount() const {
147 return m_nCharCount;
148 }
149
GetDisplayPos(const CFX_RectF & rtClip,FXTEXT_CHARPOS * & pCharPos,CFX_RectF * pBBox) const150 int32_t CFDE_TxtEdtPage::GetDisplayPos(const CFX_RectF& rtClip,
151 FXTEXT_CHARPOS*& pCharPos,
152 CFX_RectF* pBBox) const {
153 pCharPos = FX_Alloc(FXTEXT_CHARPOS, m_nCharCount);
154 int32_t nCharPosCount = 0;
155 FXTEXT_CHARPOS* pos = pCharPos;
156 for (const auto& piece : m_Pieces) {
157 if (!rtClip.IntersectWith(m_pTextSet->GetRect(piece)))
158 continue;
159
160 int32_t nCount = m_pTextSet->GetDisplayPos(piece, pos, false);
161 nCharPosCount += nCount;
162 pos += nCount;
163 }
164 if ((nCharPosCount * 5) < (m_nCharCount << 2)) {
165 FXTEXT_CHARPOS* pTemp = FX_Alloc(FXTEXT_CHARPOS, nCharPosCount);
166 FXSYS_memcpy(pTemp, pCharPos, sizeof(FXTEXT_CHARPOS) * nCharPosCount);
167 FX_Free(pCharPos);
168 pCharPos = pTemp;
169 }
170 return nCharPosCount;
171 }
172
CalcRangeRectArray(int32_t nStart,int32_t nCount,std::vector<CFX_RectF> * pRectFArr) const173 void CFDE_TxtEdtPage::CalcRangeRectArray(
174 int32_t nStart,
175 int32_t nCount,
176 std::vector<CFX_RectF>* pRectFArr) const {
177 int32_t nEnd = nStart + nCount - 1;
178 bool bInRange = false;
179 for (const auto& piece : m_Pieces) {
180 if (!bInRange) {
181 if (nStart >= piece.nStart && nStart < piece.nStart + piece.nCount) {
182 int32_t nRangeEnd = piece.nCount - 1;
183 bool bEnd = false;
184 if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) {
185 nRangeEnd = nEnd - piece.nStart;
186 bEnd = true;
187 }
188 std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false);
189 CFX_RectF rectPiece = rcArr[nStart - piece.nStart];
190 rectPiece.Union(rcArr[nRangeEnd]);
191 pRectFArr->push_back(rectPiece);
192 if (bEnd)
193 return;
194
195 bInRange = true;
196 }
197 } else {
198 if (nEnd >= piece.nStart && nEnd < piece.nStart + piece.nCount) {
199 std::vector<CFX_RectF> rcArr = m_pTextSet->GetCharRects(&piece, false);
200 CFX_RectF rectPiece = rcArr[0];
201 rectPiece.Union(rcArr[nEnd - piece.nStart]);
202 pRectFArr->push_back(rectPiece);
203 return;
204 }
205 pRectFArr->push_back(piece.rtPiece);
206 }
207 }
208 }
209
SelectWord(const CFX_PointF & fPoint,int32_t & nCount)210 int32_t CFDE_TxtEdtPage::SelectWord(const CFX_PointF& fPoint, int32_t& nCount) {
211 if (m_nRefCount < 0) {
212 return -1;
213 }
214 CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf();
215 bool bBefore;
216 int32_t nIndex = GetCharIndex(fPoint, bBefore);
217 if (nIndex == m_pEditEngine->GetTextBufLength()) {
218 nIndex = m_pEditEngine->GetTextBufLength() - 1;
219 }
220 if (nIndex < 0) {
221 return -1;
222 }
223 std::unique_ptr<CFX_WordBreak> pIter(new CFX_WordBreak);
224 pIter->Attach(new CFDE_TxtEdtBuf::Iterator(pBuf));
225 pIter->SetAt(nIndex);
226 nCount = pIter->GetWordLength();
227 return pIter->GetWordPos();
228 }
229
IsLoaded(const CFX_RectF * pClipBox)230 bool CFDE_TxtEdtPage::IsLoaded(const CFX_RectF* pClipBox) {
231 return m_bLoaded;
232 }
233
LoadPage(const CFX_RectF * pClipBox,IFX_Pause * pPause)234 int32_t CFDE_TxtEdtPage::LoadPage(const CFX_RectF* pClipBox,
235 IFX_Pause* pPause) {
236 if (m_nRefCount > 0) {
237 m_nRefCount++;
238 return m_nRefCount;
239 }
240 CFDE_TxtEdtBuf* pBuf = m_pEditEngine->GetTextBuf();
241 const FDE_TXTEDTPARAMS* pParams = m_pEditEngine->GetEditParams();
242 FX_WCHAR wcAlias = 0;
243 if (pParams->dwMode & FDE_TEXTEDITMODE_Password) {
244 wcAlias = m_pEditEngine->GetAliasChar();
245 }
246 m_pIter.reset(new CFDE_TxtEdtBuf::Iterator(static_cast<CFDE_TxtEdtBuf*>(pBuf),
247 wcAlias));
248 CFX_TxtBreak* pBreak = m_pEditEngine->GetTextBreak();
249 pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
250 pBreak->ClearBreakPieces();
251 int32_t nPageLineCount = m_pEditEngine->GetPageLineCount();
252 int32_t nStartLine = nPageLineCount * m_nPageIndex;
253 int32_t nEndLine = std::min((nStartLine + nPageLineCount - 1),
254 (m_pEditEngine->GetLineCount() - 1));
255 int32_t nPageStart, nPageEnd, nTemp, nBgnParag, nStartLineInParag, nEndParag,
256 nEndLineInParag;
257 nBgnParag = m_pEditEngine->Line2Parag(0, 0, nStartLine, nStartLineInParag);
258 m_pBgnParag =
259 static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nBgnParag));
260 m_pBgnParag->LoadParag();
261 m_pBgnParag->GetLineRange(nStartLine - nStartLineInParag, nPageStart, nTemp);
262 nEndParag = m_pEditEngine->Line2Parag(nBgnParag, nStartLineInParag, nEndLine,
263 nEndLineInParag);
264 m_pEndParag =
265 static_cast<CFDE_TxtEdtParag*>(m_pEditEngine->GetParag(nEndParag));
266 m_pEndParag->LoadParag();
267 m_pEndParag->GetLineRange(nEndLine - nEndLineInParag, nPageEnd, nTemp);
268 nPageEnd += (nTemp - 1);
269
270 FX_FLOAT fLineStart = 0.0f;
271 FX_FLOAT fLineStep = pParams->fLineSpace;
272 FX_FLOAT fLinePos = fLineStart;
273 if (!m_pTextSet)
274 m_pTextSet = pdfium::MakeUnique<CFDE_TxtEdtTextSet>(this);
275
276 m_Pieces.clear();
277 uint32_t dwBreakStatus = FX_TXTBREAK_None;
278 int32_t nPieceStart = 0;
279
280 m_CharWidths.resize(nPageEnd - nPageStart + 1, 0);
281 pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
282 pBreak->ClearBreakPieces();
283 m_nPageStart = nPageStart;
284 m_nCharCount = nPageEnd - nPageStart + 1;
285 bool bReload = false;
286 FX_FLOAT fDefCharWidth = 0;
287 std::unique_ptr<IFX_CharIter> pIter(m_pIter->Clone());
288 pIter->SetAt(nPageStart);
289 m_pIter->SetAt(nPageStart);
290 bool bFirstPiece = true;
291 do {
292 if (bReload) {
293 dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
294 } else {
295 FX_WCHAR wAppend = pIter->GetChar();
296 dwBreakStatus = pBreak->AppendChar(wAppend);
297 }
298 if (pIter->GetAt() == nPageEnd && dwBreakStatus < FX_TXTBREAK_LineBreak) {
299 dwBreakStatus = pBreak->EndBreak(FX_TXTBREAK_ParagraphBreak);
300 }
301 if (dwBreakStatus > FX_TXTBREAK_PieceBreak) {
302 int32_t nPieceCount = pBreak->CountBreakPieces();
303 for (int32_t j = 0; j < nPieceCount; j++) {
304 const CFX_TxtPiece* pPiece = pBreak->GetBreakPiece(j);
305 FDE_TEXTEDITPIECE TxtEdtPiece;
306 FXSYS_memset(&TxtEdtPiece, 0, sizeof(FDE_TEXTEDITPIECE));
307 TxtEdtPiece.nBidiLevel = pPiece->m_iBidiLevel;
308 TxtEdtPiece.nCount = pPiece->GetLength();
309 TxtEdtPiece.nStart = nPieceStart;
310 TxtEdtPiece.dwCharStyles = pPiece->m_dwCharStyles;
311 if (FX_IsOdd(pPiece->m_iBidiLevel)) {
312 TxtEdtPiece.dwCharStyles |= FX_TXTCHARSTYLE_OddBidiLevel;
313 }
314 FX_FLOAT fParaBreakWidth = 0.0f;
315 if (pPiece->m_dwStatus > FX_TXTBREAK_PieceBreak) {
316 FX_WCHAR wRtChar = pParams->wLineBreakChar;
317 if (TxtEdtPiece.nCount >= 2) {
318 FX_WCHAR wChar = pBuf->GetCharByIndex(
319 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1);
320 FX_WCHAR wCharPre = pBuf->GetCharByIndex(
321 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 2);
322 if (wChar == wRtChar) {
323 fParaBreakWidth += fDefCharWidth;
324 }
325 if (wCharPre == wRtChar) {
326 fParaBreakWidth += fDefCharWidth;
327 }
328 } else if (TxtEdtPiece.nCount >= 1) {
329 FX_WCHAR wChar = pBuf->GetCharByIndex(
330 m_nPageStart + TxtEdtPiece.nStart + TxtEdtPiece.nCount - 1);
331 if (wChar == wRtChar) {
332 fParaBreakWidth += fDefCharWidth;
333 }
334 }
335 }
336
337 TxtEdtPiece.rtPiece.left = (FX_FLOAT)pPiece->m_iStartPos / 20000.0f;
338 TxtEdtPiece.rtPiece.top = fLinePos;
339 TxtEdtPiece.rtPiece.width =
340 (FX_FLOAT)pPiece->m_iWidth / 20000.0f + fParaBreakWidth;
341 TxtEdtPiece.rtPiece.height = pParams->fLineSpace;
342
343 if (bFirstPiece) {
344 m_rtPageContents = TxtEdtPiece.rtPiece;
345 bFirstPiece = false;
346 } else {
347 m_rtPageContents.Union(TxtEdtPiece.rtPiece);
348 }
349 nPieceStart += TxtEdtPiece.nCount;
350 m_Pieces.push_back(TxtEdtPiece);
351 for (int32_t k = 0; k < TxtEdtPiece.nCount; k++) {
352 CFX_Char* ptc = pPiece->GetCharPtr(k);
353 m_CharWidths[TxtEdtPiece.nStart + k] = ptc->m_iCharWidth;
354 }
355 }
356 fLinePos += fLineStep;
357 pBreak->ClearBreakPieces();
358 }
359 if (pIter->GetAt() == nPageEnd && dwBreakStatus == FX_TXTBREAK_LineBreak) {
360 bReload = true;
361 pIter->Next(true);
362 }
363 } while (pIter->Next(false) && (pIter->GetAt() <= nPageEnd));
364 if (m_rtPageContents.left != 0) {
365 FX_FLOAT fDelta = 0.0f;
366 if (m_rtPageContents.width < pParams->fPlateWidth) {
367 if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Right) {
368 fDelta = pParams->fPlateWidth - m_rtPageContents.width;
369 } else if (pParams->dwAlignment & FDE_TEXTEDITALIGN_Center) {
370 if ((pParams->dwLayoutStyles & FDE_TEXTEDITLAYOUT_CombText) &&
371 m_nCharCount > 1) {
372 int32_t nCount = m_nCharCount - 1;
373 int32_t n = (m_pEditEngine->m_nLimit - nCount) / 2;
374 fDelta = (m_rtPageContents.width / nCount) * n;
375 } else {
376 fDelta = (pParams->fPlateWidth - m_rtPageContents.width) / 2;
377 }
378 }
379 }
380 FX_FLOAT fOffset = m_rtPageContents.left - fDelta;
381 for (auto& piece : m_Pieces)
382 piece.rtPiece.Offset(-fOffset, 0.0f);
383
384 m_rtPageContents.Offset(-fOffset, 0.0f);
385 }
386 if (m_pEditEngine->GetEditParams()->dwLayoutStyles &
387 FDE_TEXTEDITLAYOUT_LastLineHeight) {
388 m_rtPageContents.height -= pParams->fLineSpace - pParams->fFontSize;
389 m_Pieces.back().rtPiece.height = pParams->fFontSize;
390 }
391 m_nRefCount = 1;
392 m_bLoaded = true;
393 return 0;
394 }
395
UnloadPage(const CFX_RectF * pClipBox)396 void CFDE_TxtEdtPage::UnloadPage(const CFX_RectF* pClipBox) {
397 ASSERT(m_nRefCount > 0);
398 m_nRefCount--;
399 if (m_nRefCount != 0)
400 return;
401
402 m_Pieces.clear();
403 m_pTextSet.reset();
404 m_CharWidths.clear();
405 if (m_pBgnParag) {
406 m_pBgnParag->UnloadParag();
407 m_pBgnParag = nullptr;
408 }
409 if (m_pEndParag) {
410 m_pEndParag->UnloadParag();
411 m_pEndParag = nullptr;
412 }
413 m_pIter.reset();
414 }
415
GetContentsBox()416 const CFX_RectF& CFDE_TxtEdtPage::GetContentsBox() {
417 return m_rtPageContents;
418 }
419
GetFirstPosition()420 FX_POSITION CFDE_TxtEdtPage::GetFirstPosition() {
421 if (m_Pieces.empty())
422 return nullptr;
423 return (FX_POSITION)1;
424 }
425
GetNext(FX_POSITION & pos,IFDE_VisualSet * & pVisualSet)426 FDE_TEXTEDITPIECE* CFDE_TxtEdtPage::GetNext(FX_POSITION& pos,
427 IFDE_VisualSet*& pVisualSet) {
428 if (!m_pTextSet) {
429 pos = nullptr;
430 return nullptr;
431 }
432 int32_t nPos = (int32_t)(uintptr_t)pos;
433 pVisualSet = m_pTextSet.get();
434 if (nPos + 1 > pdfium::CollectionSize<int32_t>(m_Pieces))
435 pos = nullptr;
436 else
437 pos = (FX_POSITION)(uintptr_t)(nPos + 1);
438
439 return &m_Pieces[nPos - 1];
440 }
441
GetChar(const FDE_TEXTEDITPIECE * pIdentity,int32_t index) const442 FX_WCHAR CFDE_TxtEdtPage::GetChar(const FDE_TEXTEDITPIECE* pIdentity,
443 int32_t index) const {
444 int32_t nIndex = m_nPageStart + pIdentity->nStart + index;
445 if (nIndex != m_pIter->GetAt())
446 m_pIter->SetAt(nIndex);
447
448 FX_WCHAR wChar = m_pIter->GetChar();
449 m_pIter->Next();
450 return wChar;
451 }
452
GetWidth(const FDE_TEXTEDITPIECE * pIdentity,int32_t index) const453 int32_t CFDE_TxtEdtPage::GetWidth(const FDE_TEXTEDITPIECE* pIdentity,
454 int32_t index) const {
455 int32_t nWidth = m_CharWidths[pIdentity->nStart + index];
456 return nWidth;
457 }
458
NormalizePt2Rect(CFX_PointF & ptF,const CFX_RectF & rtF,FX_FLOAT fTolerance) const459 void CFDE_TxtEdtPage::NormalizePt2Rect(CFX_PointF& ptF,
460 const CFX_RectF& rtF,
461 FX_FLOAT fTolerance) const {
462 if (rtF.Contains(ptF))
463 return;
464 if (ptF.x < rtF.left)
465 ptF.x = rtF.left;
466 else if (ptF.x >= rtF.right())
467 ptF.x = rtF.right() - fTolerance;
468
469 if (ptF.y < rtF.top)
470 ptF.y = rtF.top;
471 else if (ptF.y >= rtF.bottom())
472 ptF.y = rtF.bottom() - fTolerance;
473 }
474