• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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