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 <algorithm>
8
9#include "xfa/src/foxitlib.h"
10#include "xfa/src/fee/include/ifde_txtedtbuf.h"
11#include "xfa/src/fee/include/ifde_txtedtengine.h"
12#include "fde_txtedtbuf.h"
13#define FDE_DEFCHUNKCOUNT 2
14#define FDE_TXTEDT_FORMATBLOCK_BGN 0xFFF9
15#define FDE_TXTEDT_FORMATBLOCK_END 0xFFFB
16#define FDE_TXTEDT_ZEROWIDTHSPACE 0x200B
17#ifdef FDE_USEFORMATBLOCK
18CFDE_TxtEdtBufIter::CFDE_TxtEdtBufIter(CFDE_TxtEdtBuf* pBuf,
19                                       FX_BOOL bForDisplay)
20#else
21CFDE_TxtEdtBufIter::CFDE_TxtEdtBufIter(CFDE_TxtEdtBuf* pBuf, FX_WCHAR wcAlias)
22#endif
23    : m_pBuf(pBuf),
24      m_nCurChunk(0),
25      m_nCurIndex(0),
26      m_nIndex(0),
27#ifdef FDE_USEFORMATBLOCK
28      m_bForDisplay(bForDisplay),
29      m_nAliasCount(0),
30#endif
31      m_Alias(wcAlias) {
32  FXSYS_assert(m_pBuf);
33}
34CFDE_TxtEdtBufIter::~CFDE_TxtEdtBufIter() {}
35void CFDE_TxtEdtBufIter::Release() {
36  delete this;
37}
38FX_BOOL CFDE_TxtEdtBufIter::Next(FX_BOOL bPrev) {
39  if (bPrev) {
40    if (m_nIndex == 0) {
41      return FALSE;
42    }
43    FXSYS_assert(m_nCurChunk < m_pBuf->m_Chunks.GetSize());
44    CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunk = NULL;
45    if (m_nCurIndex > 0) {
46      m_nCurIndex--;
47    } else {
48      while (m_nCurChunk > 0) {
49        --m_nCurChunk;
50        lpChunk =
51            (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];
52        if (lpChunk->nUsed > 0) {
53          m_nCurIndex = lpChunk->nUsed - 1;
54          break;
55        }
56      }
57    }
58    FXSYS_assert(m_nCurChunk >= 0);
59    m_nIndex--;
60    return TRUE;
61  } else {
62    if (m_nIndex >= (m_pBuf->m_nTotal - 1)) {
63      return FALSE;
64    }
65    FXSYS_assert(m_nCurChunk < m_pBuf->m_Chunks.GetSize());
66    CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunk =
67        (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];
68    if (lpChunk->nUsed != (m_nCurIndex + 1)) {
69      m_nCurIndex++;
70    } else {
71      int32_t nEnd = m_pBuf->m_Chunks.GetSize() - 1;
72      while (m_nCurChunk < nEnd) {
73        m_nCurChunk++;
74        CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunkTemp =
75            (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];
76        if (lpChunkTemp->nUsed > 0) {
77          m_nCurIndex = 0;
78          break;
79        }
80      }
81    }
82    m_nIndex++;
83    return TRUE;
84  }
85}
86void CFDE_TxtEdtBufIter::SetAt(int32_t nIndex) {
87  FXSYS_assert(nIndex >= 0 && nIndex < m_pBuf->m_nTotal);
88  CFDE_TxtEdtBuf::FDE_CHUNKPLACE cp;
89  m_pBuf->Index2CP(nIndex, cp);
90  m_nIndex = nIndex;
91  m_nCurChunk = cp.nChunkIndex;
92  m_nCurIndex = cp.nCharIndex;
93}
94int32_t CFDE_TxtEdtBufIter::GetAt() const {
95  return m_nIndex;
96}
97FX_WCHAR CFDE_TxtEdtBufIter::GetChar() {
98  FXSYS_assert(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal);
99#ifdef FDE_USEFORMATBLOCK
100  if (m_bForDisplay) {
101    if (m_bInField) {
102      FXSYS_assert(m_nAliasCount >= 0 && m_nAliasCount <= 2);
103      if (m_nAliasCount > 0) {
104        m_nAliasCount--;
105        return FDE_TXTEDT_ZEROWIDTHSPACE;
106      }
107      FX_WCHAR wc =
108          ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])
109              ->wChars[m_nCurIndex];
110      if (wc == FDE_TXTEDT_FORMATBLOCK_END) {
111        m_nAliasCount = 0;
112        m_bInField = FALSE;
113      }
114      return wc;
115    } else {
116      FX_WCHAR wc =
117          ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])
118              ->wChars[m_nCurIndex];
119      if (wc == FDE_TXTEDT_FORMATBLOCK_BGN) {
120        m_nAliasCount = 2;
121        m_bInField = TRUE;
122      }
123      return wc;
124    }
125  }
126#endif
127  if (m_Alias == 0 || m_nIndex == (m_pBuf->m_nTotal - 1)) {
128    return ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])
129        ->wChars[m_nCurIndex];
130  }
131  return m_Alias;
132}
133FX_BOOL CFDE_TxtEdtBufIter::IsEOF(FX_BOOL bTail) const {
134  return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0;
135}
136IFX_CharIter* CFDE_TxtEdtBufIter::Clone() {
137  CFDE_TxtEdtBufIter* pIter = new CFDE_TxtEdtBufIter(m_pBuf);
138  pIter->m_nCurChunk = m_nCurChunk;
139  pIter->m_nCurIndex = m_nCurIndex;
140  pIter->m_nIndex = m_nIndex;
141  pIter->m_Alias = m_Alias;
142  return pIter;
143}
144CFDE_TxtEdtBuf::CFDE_TxtEdtBuf(int32_t nDefChunkSize)
145    : m_nChunkSize(nDefChunkSize),
146      m_nTotal(0),
147      m_bChanged(FALSE),
148      m_pAllocator(NULL) {
149  FXSYS_assert(m_nChunkSize);
150  ResetChunkBuffer(FDE_DEFCHUNKCOUNT, m_nChunkSize);
151}
152void CFDE_TxtEdtBuf::Release() {
153  delete this;
154}
155CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() {
156  Clear(TRUE);
157  m_pAllocator->Release();
158  m_Chunks.RemoveAll();
159}
160FX_BOOL CFDE_TxtEdtBuf::SetChunkSize(int32_t nChunkSize) {
161  FXSYS_assert(nChunkSize);
162  ResetChunkBuffer(FDE_DEFCHUNKCOUNT, nChunkSize);
163  return TRUE;
164}
165int32_t CFDE_TxtEdtBuf::GetChunkSize() const {
166  return m_nChunkSize;
167}
168int32_t CFDE_TxtEdtBuf::GetTextLength() const {
169  return m_nTotal;
170}
171void CFDE_TxtEdtBuf::SetText(const CFX_WideString& wsText) {
172  FXSYS_assert(!wsText.IsEmpty());
173  Clear(FALSE);
174  int32_t nTextLength = wsText.GetLength();
175  int32_t nNeedCount =
176      ((nTextLength - 1) / m_nChunkSize + 1) - m_Chunks.GetSize();
177  int32_t i = 0;
178  for (i = 0; i < nNeedCount; i++) {
179    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(
180        sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR));
181    lpChunk->nUsed = 0;
182    m_Chunks.Add(lpChunk);
183  }
184  int32_t nTotalCount = m_Chunks.GetSize();
185  const FX_WCHAR* lpSrcBuf = wsText.c_str();
186  int32_t nLeave = nTextLength;
187  int32_t nCopyedLength = m_nChunkSize;
188  for (i = 0; i < nTotalCount && nLeave > 0; i++) {
189    if (nLeave < nCopyedLength) {
190      nCopyedLength = nLeave;
191    }
192    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];
193    FXSYS_memcpy(lpChunk->wChars, lpSrcBuf, nCopyedLength * sizeof(FX_WCHAR));
194    nLeave -= nCopyedLength;
195    lpSrcBuf += nCopyedLength;
196    lpChunk->nUsed = nCopyedLength;
197  }
198  m_nTotal = nTextLength;
199  m_bChanged = TRUE;
200}
201void CFDE_TxtEdtBuf::GetText(CFX_WideString& wsText) const {
202  GetRange(wsText, 0, m_nTotal);
203}
204FX_WCHAR CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const {
205  FXSYS_assert(nIndex >= 0 && nIndex < GetTextLength());
206  FDE_LPCHUNKHEADER pChunkHeader = NULL;
207  int32_t nTotal = 0;
208  int32_t nCount = m_Chunks.GetSize();
209  int32_t i = 0;
210  for (i = 0; i < nCount; i++) {
211    pChunkHeader = (FDE_LPCHUNKHEADER)m_Chunks[i];
212    nTotal += pChunkHeader->nUsed;
213    if (nTotal > nIndex) {
214      break;
215    }
216  }
217  FXSYS_assert(pChunkHeader);
218  return pChunkHeader->wChars[pChunkHeader->nUsed - (nTotal - nIndex)];
219}
220void CFDE_TxtEdtBuf::GetRange(CFX_WideString& wsText,
221                              int32_t nBegin,
222                              int32_t nLength) const {
223  FDE_CHUNKPLACE cp;
224  Index2CP(nBegin, cp);
225  int32_t nLeave = nLength;
226  int32_t nCount = m_Chunks.GetSize();
227  FX_WCHAR* lpDstBuf = wsText.GetBuffer(nLength);
228  int32_t nChunkIndex = cp.nChunkIndex;
229  FDE_LPCHUNKHEADER lpChunkHeader = (FDE_LPCHUNKHEADER)m_Chunks[nChunkIndex];
230  int32_t nCopyLength = lpChunkHeader->nUsed - cp.nCharIndex;
231  FX_WCHAR* lpSrcBuf = lpChunkHeader->wChars + cp.nCharIndex;
232  while (nLeave > 0) {
233    if (nLeave <= nCopyLength) {
234      nCopyLength = nLeave;
235    }
236    FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(FX_WCHAR));
237    nChunkIndex++;
238    if (nChunkIndex >= nCount) {
239      break;
240    }
241    lpChunkHeader = (FDE_LPCHUNKHEADER)m_Chunks[nChunkIndex];
242    lpSrcBuf = lpChunkHeader->wChars;
243    nLeave -= nCopyLength;
244    lpDstBuf += nCopyLength;
245    nCopyLength = lpChunkHeader->nUsed;
246  }
247  wsText.ReleaseBuffer();
248}
249void CFDE_TxtEdtBuf::Insert(int32_t nPos,
250                            const FX_WCHAR* lpText,
251                            int32_t nLength) {
252  FXSYS_assert(nPos >= 0 && nPos <= m_nTotal);
253  FDE_CHUNKPLACE cp;
254  Index2CP(nPos, cp);
255  int32_t nLengthTemp = nLength;
256  if (cp.nCharIndex != 0) {
257    FDE_LPCHUNKHEADER lpNewChunk = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(
258        sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR));
259    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex];
260    int32_t nCopy = lpChunk->nUsed - cp.nCharIndex;
261    FXSYS_memcpy(lpNewChunk->wChars, lpChunk->wChars + cp.nCharIndex,
262                 nCopy * sizeof(FX_WCHAR));
263    lpChunk->nUsed -= nCopy;
264    cp.nChunkIndex++;
265    m_Chunks.InsertAt(cp.nChunkIndex, lpNewChunk);
266    lpNewChunk->nUsed = nCopy;
267    cp.nCharIndex = 0;
268  }
269  if (cp.nChunkIndex != 0) {
270    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex - 1];
271    if (lpChunk->nUsed != m_nChunkSize) {
272      cp.nChunkIndex--;
273      int32_t nFree = m_nChunkSize - lpChunk->nUsed;
274      int32_t nCopy = std::min(nLengthTemp, nFree);
275      FXSYS_memcpy(lpChunk->wChars + lpChunk->nUsed, lpText,
276                   nCopy * sizeof(FX_WCHAR));
277      lpText += nCopy;
278      nLengthTemp -= nCopy;
279      lpChunk->nUsed += nCopy;
280      cp.nChunkIndex++;
281    }
282  }
283  while (nLengthTemp > 0) {
284    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(
285        sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR));
286    FXSYS_assert(lpChunk);
287    int32_t nCopy = std::min(nLengthTemp, m_nChunkSize);
288    FXSYS_memcpy(lpChunk->wChars, lpText, nCopy * sizeof(FX_WCHAR));
289    lpText += nCopy;
290    nLengthTemp -= nCopy;
291    lpChunk->nUsed = nCopy;
292    m_Chunks.InsertAt(cp.nChunkIndex, lpChunk);
293    cp.nChunkIndex++;
294  }
295  m_nTotal += nLength;
296  m_bChanged = TRUE;
297}
298void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) {
299  FXSYS_assert(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal);
300  FDE_CHUNKPLACE cpEnd;
301  Index2CP(nIndex + nLength - 1, cpEnd);
302  m_nTotal -= nLength;
303  FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cpEnd.nChunkIndex];
304  int32_t nFirstPart = cpEnd.nCharIndex + 1;
305  int32_t nMovePart = lpChunk->nUsed - nFirstPart;
306  if (nMovePart != 0) {
307    int32_t nDelete = std::min(nFirstPart, nLength);
308    FXSYS_memmove(lpChunk->wChars + nFirstPart - nDelete,
309                  lpChunk->wChars + nFirstPart, nMovePart * sizeof(FX_WCHAR));
310    lpChunk->nUsed -= nDelete;
311    nLength -= nDelete;
312    cpEnd.nChunkIndex--;
313  }
314  while (nLength > 0) {
315    lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cpEnd.nChunkIndex];
316    int32_t nDeleted = std::min(lpChunk->nUsed, nLength);
317    lpChunk->nUsed -= nDeleted;
318    if (lpChunk->nUsed == 0) {
319      m_pAllocator->Free(lpChunk);
320      m_Chunks.RemoveAt(cpEnd.nChunkIndex);
321      lpChunk = NULL;
322    }
323    nLength -= nDeleted;
324    cpEnd.nChunkIndex--;
325  }
326  m_bChanged = TRUE;
327}
328void CFDE_TxtEdtBuf::Clear(FX_BOOL bRelease) {
329  int32_t i = 0;
330  int32_t nCount = m_Chunks.GetSize();
331  if (bRelease) {
332    while (i < nCount) {
333      m_pAllocator->Free(m_Chunks[i++]);
334    }
335    m_Chunks.RemoveAll();
336  } else {
337    while (i < nCount) {
338      ((FDE_LPCHUNKHEADER)m_Chunks[i++])->nUsed = 0;
339    }
340  }
341  m_nTotal = 0;
342  m_bChanged = TRUE;
343}
344FX_BOOL CFDE_TxtEdtBuf::Optimize(IFX_Pause* pPause) {
345  if (m_bChanged == FALSE) {
346    return TRUE;
347  }
348  if (m_nTotal == 0) {
349    return TRUE;
350  }
351  int32_t nCount = m_Chunks.GetSize();
352  if (nCount == 0) {
353    return TRUE;
354  }
355  int32_t i = 0;
356  for (; i < nCount; i++) {
357    FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];
358    if (lpChunk->nUsed == 0) {
359      m_pAllocator->Free(lpChunk);
360      m_Chunks.RemoveAt(i);
361      --i;
362      --nCount;
363    }
364  }
365  if (pPause != NULL && pPause->NeedToPauseNow()) {
366    return FALSE;
367  }
368  FDE_LPCHUNKHEADER lpPreChunk = (FDE_LPCHUNKHEADER)m_Chunks[0];
369  FDE_LPCHUNKHEADER lpCurChunk = NULL;
370  for (i = 1; i < nCount; i++) {
371    lpCurChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];
372    if (lpPreChunk->nUsed + lpCurChunk->nUsed <= m_nChunkSize) {
373      FXSYS_memcpy(lpPreChunk->wChars + lpPreChunk->nUsed, lpCurChunk->wChars,
374                   lpCurChunk->nUsed * sizeof(FX_WCHAR));
375      lpPreChunk->nUsed += lpCurChunk->nUsed;
376      m_pAllocator->Free(lpCurChunk);
377      m_Chunks.RemoveAt(i);
378      --i;
379      --nCount;
380    } else {
381      lpPreChunk = lpCurChunk;
382    }
383    if (pPause != NULL && pPause->NeedToPauseNow()) {
384      return FALSE;
385    }
386  }
387  m_bChanged = FALSE;
388  return TRUE;
389}
390void CFDE_TxtEdtBuf::ResetChunkBuffer(int32_t nDefChunkCount,
391                                      int32_t nChunkSize) {
392  FXSYS_assert(nChunkSize);
393  FXSYS_assert(nDefChunkCount);
394  if (m_pAllocator) {
395    m_pAllocator->Release();
396    m_pAllocator = NULL;
397  }
398  m_Chunks.RemoveAll();
399  m_nChunkSize = nChunkSize;
400  int32_t nChunkLength =
401      sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR);
402  m_pAllocator =
403      FX_CreateAllocator(FX_ALLOCTYPE_Fixed, nDefChunkCount, nChunkLength);
404  FXSYS_assert(m_pAllocator);
405  FDE_LPCHUNKHEADER lpChunkHeader =
406      (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(nChunkLength);
407  FXSYS_assert(lpChunkHeader);
408  lpChunkHeader->nUsed = 0;
409  m_Chunks.Add(lpChunkHeader);
410  m_nTotal = 0;
411}
412int32_t CFDE_TxtEdtBuf::CP2Index(const FDE_CHUNKPLACE& cp) const {
413  int32_t nTotal = cp.nCharIndex;
414  int32_t i = 0;
415  for (i = 0; i < cp.nChunkIndex; i++) {
416    nTotal += ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed;
417  }
418  return nTotal;
419}
420void CFDE_TxtEdtBuf::Index2CP(int32_t nIndex, FDE_CHUNKPLACE& cp) const {
421  FXSYS_assert(nIndex <= GetTextLength());
422  if (nIndex == m_nTotal) {
423    cp.nChunkIndex = m_Chunks.GetSize() - 1;
424    cp.nCharIndex = ((FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex])->nUsed;
425    return;
426  }
427  int32_t i = 0;
428  int32_t nTotal = 0;
429  int32_t nCount = m_Chunks.GetSize();
430  for (; i < nCount; i++) {
431    nTotal += ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed;
432    if (nTotal > nIndex) {
433      break;
434    }
435  }
436  cp.nChunkIndex = i;
437  cp.nCharIndex = ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed - (nTotal - nIndex);
438}
439