// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "xfa/fee/fde_txtedtbuf.h" #include #include "xfa/fee/ifde_txtedtengine.h" namespace { const int kDefaultChunkSize = 1024; } // namespace #define FDE_DEFCHUNKCOUNT 2 #define FDE_TXTEDT_FORMATBLOCK_BGN 0xFFF9 #define FDE_TXTEDT_FORMATBLOCK_END 0xFFFB #define FDE_TXTEDT_ZEROWIDTHSPACE 0x200B CFDE_TxtEdtBufIter::CFDE_TxtEdtBufIter(CFDE_TxtEdtBuf* pBuf, FX_WCHAR wcAlias) : m_pBuf(pBuf), m_nCurChunk(0), m_nCurIndex(0), m_nIndex(0), m_Alias(wcAlias) { ASSERT(m_pBuf); } CFDE_TxtEdtBufIter::~CFDE_TxtEdtBufIter() {} FX_BOOL CFDE_TxtEdtBufIter::Next(FX_BOOL bPrev) { if (bPrev) { if (m_nIndex == 0) { return FALSE; } ASSERT(m_nCurChunk < m_pBuf->m_Chunks.GetSize()); CFDE_TxtEdtBuf::FDE_CHUNKHEADER* lpChunk = nullptr; if (m_nCurIndex > 0) { m_nCurIndex--; } else { while (m_nCurChunk > 0) { --m_nCurChunk; lpChunk = m_pBuf->m_Chunks[m_nCurChunk]; if (lpChunk->nUsed > 0) { m_nCurIndex = lpChunk->nUsed - 1; break; } } } ASSERT(m_nCurChunk >= 0); m_nIndex--; return TRUE; } else { if (m_nIndex >= (m_pBuf->m_nTotal - 1)) { return FALSE; } ASSERT(m_nCurChunk < m_pBuf->m_Chunks.GetSize()); CFDE_TxtEdtBuf::FDE_CHUNKHEADER* lpChunk = m_pBuf->m_Chunks[m_nCurChunk]; if (lpChunk->nUsed != (m_nCurIndex + 1)) { m_nCurIndex++; } else { int32_t nEnd = m_pBuf->m_Chunks.GetSize() - 1; while (m_nCurChunk < nEnd) { m_nCurChunk++; CFDE_TxtEdtBuf::FDE_CHUNKHEADER* lpChunkTemp = m_pBuf->m_Chunks[m_nCurChunk]; if (lpChunkTemp->nUsed > 0) { m_nCurIndex = 0; break; } } } m_nIndex++; return TRUE; } } void CFDE_TxtEdtBufIter::SetAt(int32_t nIndex) { ASSERT(nIndex >= 0 && nIndex < m_pBuf->m_nTotal); CFDE_TxtEdtBuf::FDE_CHUNKPLACE cp; m_pBuf->Index2CP(nIndex, cp); m_nIndex = nIndex; m_nCurChunk = cp.nChunkIndex; m_nCurIndex = cp.nCharIndex; } int32_t CFDE_TxtEdtBufIter::GetAt() const { return m_nIndex; } FX_WCHAR CFDE_TxtEdtBufIter::GetChar() { ASSERT(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal); if (m_Alias == 0 || m_nIndex == (m_pBuf->m_nTotal - 1)) { return m_pBuf->m_Chunks[m_nCurChunk]->wChars[m_nCurIndex]; } return m_Alias; } FX_BOOL CFDE_TxtEdtBufIter::IsEOF(FX_BOOL bTail) const { return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0; } IFX_CharIter* CFDE_TxtEdtBufIter::Clone() { CFDE_TxtEdtBufIter* pIter = new CFDE_TxtEdtBufIter(m_pBuf); pIter->m_nCurChunk = m_nCurChunk; pIter->m_nCurIndex = m_nCurIndex; pIter->m_nIndex = m_nIndex; pIter->m_Alias = m_Alias; return pIter; } CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() : m_nChunkSize(kDefaultChunkSize), m_nTotal(0), m_bChanged(FALSE), m_pAllocator(NULL) { ASSERT(m_nChunkSize); ResetChunkBuffer(FDE_DEFCHUNKCOUNT, m_nChunkSize); } void CFDE_TxtEdtBuf::Release() { delete this; } CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() { Clear(TRUE); delete m_pAllocator; m_Chunks.RemoveAll(); } FX_BOOL CFDE_TxtEdtBuf::SetChunkSize(int32_t nChunkSize) { ASSERT(nChunkSize); ResetChunkBuffer(FDE_DEFCHUNKCOUNT, nChunkSize); return TRUE; } int32_t CFDE_TxtEdtBuf::GetChunkSize() const { return m_nChunkSize; } int32_t CFDE_TxtEdtBuf::GetTextLength() const { return m_nTotal; } void CFDE_TxtEdtBuf::SetText(const CFX_WideString& wsText) { ASSERT(!wsText.IsEmpty()); Clear(FALSE); int32_t nTextLength = wsText.GetLength(); int32_t nNeedCount = ((nTextLength - 1) / m_nChunkSize + 1) - m_Chunks.GetSize(); int32_t i = 0; for (i = 0; i < nNeedCount; i++) { FDE_CHUNKHEADER* lpChunk = static_cast(m_pAllocator->Alloc( sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); lpChunk->nUsed = 0; m_Chunks.Add(lpChunk); } int32_t nTotalCount = m_Chunks.GetSize(); const FX_WCHAR* lpSrcBuf = wsText.c_str(); int32_t nLeave = nTextLength; int32_t nCopyedLength = m_nChunkSize; for (i = 0; i < nTotalCount && nLeave > 0; i++) { if (nLeave < nCopyedLength) { nCopyedLength = nLeave; } FDE_CHUNKHEADER* lpChunk = m_Chunks[i]; FXSYS_memcpy(lpChunk->wChars, lpSrcBuf, nCopyedLength * sizeof(FX_WCHAR)); nLeave -= nCopyedLength; lpSrcBuf += nCopyedLength; lpChunk->nUsed = nCopyedLength; } m_nTotal = nTextLength; m_bChanged = TRUE; } void CFDE_TxtEdtBuf::GetText(CFX_WideString& wsText) const { GetRange(wsText, 0, m_nTotal); } FX_WCHAR CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const { ASSERT(nIndex >= 0 && nIndex < GetTextLength()); FDE_CHUNKHEADER* pChunkHeader = nullptr; int32_t nTotal = 0; int32_t nCount = m_Chunks.GetSize(); int32_t i = 0; for (i = 0; i < nCount; i++) { pChunkHeader = m_Chunks[i]; nTotal += pChunkHeader->nUsed; if (nTotal > nIndex) { break; } } ASSERT(pChunkHeader); return pChunkHeader->wChars[pChunkHeader->nUsed - (nTotal - nIndex)]; } void CFDE_TxtEdtBuf::GetRange(CFX_WideString& wsText, int32_t nBegin, int32_t nLength) const { FDE_CHUNKPLACE cp; Index2CP(nBegin, cp); int32_t nLeave = nLength; int32_t nCount = m_Chunks.GetSize(); FX_WCHAR* lpDstBuf = wsText.GetBuffer(nLength); int32_t nChunkIndex = cp.nChunkIndex; FDE_CHUNKHEADER* lpChunkHeader = m_Chunks[nChunkIndex]; int32_t nCopyLength = lpChunkHeader->nUsed - cp.nCharIndex; FX_WCHAR* lpSrcBuf = lpChunkHeader->wChars + cp.nCharIndex; while (nLeave > 0) { if (nLeave <= nCopyLength) { nCopyLength = nLeave; } FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(FX_WCHAR)); nChunkIndex++; if (nChunkIndex >= nCount) { break; } lpChunkHeader = m_Chunks[nChunkIndex]; lpSrcBuf = lpChunkHeader->wChars; nLeave -= nCopyLength; lpDstBuf += nCopyLength; nCopyLength = lpChunkHeader->nUsed; } wsText.ReleaseBuffer(); } void CFDE_TxtEdtBuf::Insert(int32_t nPos, const FX_WCHAR* lpText, int32_t nLength) { ASSERT(nPos >= 0 && nPos <= m_nTotal); FDE_CHUNKPLACE cp; Index2CP(nPos, cp); int32_t nLengthTemp = nLength; if (cp.nCharIndex != 0) { FDE_CHUNKHEADER* lpNewChunk = static_cast(m_pAllocator->Alloc( sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); FDE_CHUNKHEADER* lpChunk = m_Chunks[cp.nChunkIndex]; int32_t nCopy = lpChunk->nUsed - cp.nCharIndex; FXSYS_memcpy(lpNewChunk->wChars, lpChunk->wChars + cp.nCharIndex, nCopy * sizeof(FX_WCHAR)); lpChunk->nUsed -= nCopy; cp.nChunkIndex++; m_Chunks.InsertAt(cp.nChunkIndex, lpNewChunk); lpNewChunk->nUsed = nCopy; cp.nCharIndex = 0; } if (cp.nChunkIndex != 0) { FDE_CHUNKHEADER* lpChunk = m_Chunks[cp.nChunkIndex - 1]; if (lpChunk->nUsed != m_nChunkSize) { cp.nChunkIndex--; int32_t nFree = m_nChunkSize - lpChunk->nUsed; int32_t nCopy = std::min(nLengthTemp, nFree); FXSYS_memcpy(lpChunk->wChars + lpChunk->nUsed, lpText, nCopy * sizeof(FX_WCHAR)); lpText += nCopy; nLengthTemp -= nCopy; lpChunk->nUsed += nCopy; cp.nChunkIndex++; } } while (nLengthTemp > 0) { FDE_CHUNKHEADER* lpChunk = static_cast(m_pAllocator->Alloc( sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); ASSERT(lpChunk); int32_t nCopy = std::min(nLengthTemp, m_nChunkSize); FXSYS_memcpy(lpChunk->wChars, lpText, nCopy * sizeof(FX_WCHAR)); lpText += nCopy; nLengthTemp -= nCopy; lpChunk->nUsed = nCopy; m_Chunks.InsertAt(cp.nChunkIndex, lpChunk); cp.nChunkIndex++; } m_nTotal += nLength; m_bChanged = TRUE; } void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) { ASSERT(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal); FDE_CHUNKPLACE cpEnd; Index2CP(nIndex + nLength - 1, cpEnd); m_nTotal -= nLength; FDE_CHUNKHEADER* lpChunk = m_Chunks[cpEnd.nChunkIndex]; int32_t nFirstPart = cpEnd.nCharIndex + 1; int32_t nMovePart = lpChunk->nUsed - nFirstPart; if (nMovePart != 0) { int32_t nDelete = std::min(nFirstPart, nLength); FXSYS_memmove(lpChunk->wChars + nFirstPart - nDelete, lpChunk->wChars + nFirstPart, nMovePart * sizeof(FX_WCHAR)); lpChunk->nUsed -= nDelete; nLength -= nDelete; cpEnd.nChunkIndex--; } while (nLength > 0) { lpChunk = m_Chunks[cpEnd.nChunkIndex]; int32_t nDeleted = std::min(lpChunk->nUsed, nLength); lpChunk->nUsed -= nDeleted; if (lpChunk->nUsed == 0) { m_pAllocator->Free(lpChunk); m_Chunks.RemoveAt(cpEnd.nChunkIndex); lpChunk = NULL; } nLength -= nDeleted; cpEnd.nChunkIndex--; } m_bChanged = TRUE; } void CFDE_TxtEdtBuf::Clear(FX_BOOL bRelease) { int32_t i = 0; int32_t nCount = m_Chunks.GetSize(); if (bRelease) { while (i < nCount) { m_pAllocator->Free(m_Chunks[i++]); } m_Chunks.RemoveAll(); } else { while (i < nCount) { m_Chunks[i++]->nUsed = 0; } } m_nTotal = 0; m_bChanged = TRUE; } FX_BOOL CFDE_TxtEdtBuf::Optimize(IFX_Pause* pPause) { if (m_bChanged == FALSE) { return TRUE; } if (m_nTotal == 0) { return TRUE; } int32_t nCount = m_Chunks.GetSize(); if (nCount == 0) { return TRUE; } int32_t i = 0; for (; i < nCount; i++) { FDE_CHUNKHEADER* lpChunk = m_Chunks[i]; if (lpChunk->nUsed == 0) { m_pAllocator->Free(lpChunk); m_Chunks.RemoveAt(i); --i; --nCount; } } if (pPause && pPause->NeedToPauseNow()) return FALSE; FDE_CHUNKHEADER* lpPreChunk = m_Chunks[0]; FDE_CHUNKHEADER* lpCurChunk = nullptr; for (i = 1; i < nCount; i++) { lpCurChunk = m_Chunks[i]; if (lpPreChunk->nUsed + lpCurChunk->nUsed <= m_nChunkSize) { FXSYS_memcpy(lpPreChunk->wChars + lpPreChunk->nUsed, lpCurChunk->wChars, lpCurChunk->nUsed * sizeof(FX_WCHAR)); lpPreChunk->nUsed += lpCurChunk->nUsed; m_pAllocator->Free(lpCurChunk); m_Chunks.RemoveAt(i); --i; --nCount; } else { lpPreChunk = lpCurChunk; } if (pPause != NULL && pPause->NeedToPauseNow()) { return FALSE; } } m_bChanged = FALSE; return TRUE; } void CFDE_TxtEdtBuf::ResetChunkBuffer(int32_t nDefChunkCount, int32_t nChunkSize) { ASSERT(nChunkSize); ASSERT(nDefChunkCount); delete m_pAllocator; m_pAllocator = nullptr; m_Chunks.RemoveAll(); m_nChunkSize = nChunkSize; int32_t nChunkLength = sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR); m_pAllocator = IFX_MemoryAllocator::Create(FX_ALLOCTYPE_Fixed, nDefChunkCount, nChunkLength); FDE_CHUNKHEADER* lpChunkHeader = static_cast(m_pAllocator->Alloc(nChunkLength)); ASSERT(lpChunkHeader); lpChunkHeader->nUsed = 0; m_Chunks.Add(lpChunkHeader); m_nTotal = 0; } int32_t CFDE_TxtEdtBuf::CP2Index(const FDE_CHUNKPLACE& cp) const { int32_t nTotal = cp.nCharIndex; int32_t i = 0; for (i = 0; i < cp.nChunkIndex; i++) { nTotal += m_Chunks[i]->nUsed; } return nTotal; } void CFDE_TxtEdtBuf::Index2CP(int32_t nIndex, FDE_CHUNKPLACE& cp) const { ASSERT(nIndex <= GetTextLength()); if (nIndex == m_nTotal) { cp.nChunkIndex = m_Chunks.GetSize() - 1; cp.nCharIndex = m_Chunks[cp.nChunkIndex]->nUsed; return; } int32_t i = 0; int32_t nTotal = 0; int32_t nCount = m_Chunks.GetSize(); for (; i < nCount; i++) { nTotal += m_Chunks[i]->nUsed; if (nTotal > nIndex) { break; } } cp.nChunkIndex = i; cp.nCharIndex = m_Chunks[i]->nUsed - (nTotal - nIndex); }