Files
Yajbir Singh f1b860b25c
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

1228 lines
32 KiB
C++

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "Objects.h"
#include "Utils.h"
#include "Types.h"
#include "Encrypt.h"
#include "Streams.h"
#include "Document.h"
#include "EncryptDictionary.h"
// Если установлен бит OTYPE_DIRECT, значит данный объект принадлежит другому
// объекту. Если установлен бит OTYPE_INDIRECT, значит объект управляется таблицей xref.
#define FLAG_NONE 0x0
#define FLAG_HIDDEN 0x1
#define FLAG_INDIRECT 0x4
#define FLAG_DIRECT 0x8
//------ Значения относящиеся к объекту xref ------------------------------------
#define FREE_ENTRY 'f'
#define IN_USE_ENTRY 'n'
#define RELEASE_OBJECT(pObject) \
if (pObject && !pObject->IsIndirect())\
delete pObject;\
static const BYTE UNICODE_HEADER[] = { 0xFE, 0xFF };
namespace PdfWriter
{
//----------------------------------------------------------------------------------------
// CObjectBase
//----------------------------------------------------------------------------------------
bool CObjectBase::IsHidden() const
{
return (m_unFlags & FLAG_HIDDEN ? true : false);
}
bool CObjectBase::IsDirect() const
{
return (m_unFlags & FLAG_DIRECT ? true : false);
}
bool CObjectBase::IsIndirect() const
{
return (m_unFlags & FLAG_INDIRECT ? true : false);
}
void CObjectBase::SetDirect()
{
m_unFlags |= FLAG_DIRECT;
}
void CObjectBase::SetIndirect()
{
m_unFlags |= FLAG_INDIRECT;
}
void CObjectBase::SetHidden()
{
m_unFlags |= FLAG_HIDDEN;
}
void CObjectBase::SetXrefEntry(TXrefEntry* pEntry)
{
m_pXrefEntry = pEntry;
}
TXrefEntry* CObjectBase::GetXrefEntry()
{
return m_pXrefEntry;
}
void CObjectBase::WriteValue(CStream* pStream, CEncrypt* pEncrypt)
{
switch (GetType())
{
case object_type_NAME: pStream->Write((CNameObject*)this); break;
case object_type_NUMBER: pStream->Write((CNumberObject*)this); break;
case object_type_REAL: pStream->Write((CRealObject*)this); break;
case object_type_STRING: pStream->Write((CStringObject*)this, pEncrypt); break;
case object_type_BINARY: pStream->Write((CBinaryObject*)this, pEncrypt); break;
case object_type_ARRAY: pStream->Write((CArrayObject*)this, pEncrypt); break;
case object_type_DICT: pStream->Write((CDictObject*)this, pEncrypt); break;
case object_type_BOOLEAN:pStream->Write((CBoolObject*)this); break;
case object_type_NULL: pStream->WriteStr("null"); break;
}
}
void CObjectBase::Write (CStream* pStream, CEncrypt* pEncrypt)
{
if (IsHidden())
return;
if (object_type_PROXY == GetType())
{
CObjectBase* pObject = ((CProxyObject*)this)->Get();
if (!pObject)
return;
char sBuf[SHORT_BUFFER_SIZE];
char *pBuf = sBuf;
char *pEndPtr = sBuf + SHORT_BUFFER_SIZE - 1;
pBuf = ItoA(pBuf, pObject->m_unObjId & 0x00FFFFFF, pEndPtr);
*pBuf++ = ' ';
pBuf = ItoA(pBuf, pObject->m_unGenNo, pEndPtr);
StrCpy(pBuf, " R", pEndPtr);
pStream->WriteStr(sBuf);
}
else
{
WriteValue(pStream, pEncrypt);
}
}
//----------------------------------------------------------------------------------------
// CNameObject
//----------------------------------------------------------------------------------------
void CNameObject::Set(const char* sValue)
{
if (!sValue || 0 == sValue[0])
m_sValue[0] = 0;
else
{
StrCpy(m_sValue, sValue, m_sValue + LIMIT_MAX_NAME_LEN);
}
}
//----------------------------------------------------------------------------------------
// CStringObject
//----------------------------------------------------------------------------------------
CStringObject::CStringObject(const char* sValue, bool isUTF16, bool isDictValue)
{
m_pValue = NULL;
m_unLen = 0;
Set(sValue, isUTF16, isDictValue);
}
CStringObject::CStringObject()
{
m_pValue = NULL;
m_unLen = 0;
}
CStringObject::~CStringObject()
{
if (m_pValue)
delete[] m_pValue;
}
void CStringObject::Set(const char* sValue, bool isUTF16, bool isDictValue, int nMax)
{
if (m_pValue)
{
delete[] m_pValue;
m_unLen = 0;
}
unsigned int unLen = StrLen(sValue, nMax);
m_pValue = new BYTE[unLen + 1];
StrCpy((char*)m_pValue, (char*)sValue, (char*)(m_pValue + unLen));
m_unLen = unLen;
m_bUTF16 = isUTF16;
m_bDictValue = isDictValue;
}
void CStringObject::Add(const char* sValue)
{
if (!sValue || !*sValue)
return;
unsigned int unAppendLen = StrLen(sValue, LIMIT_MAX_STRING_LEN);
BYTE* pNewValue = new BYTE[m_unLen + unAppendLen + 1];
if (m_unLen > 0)
StrCpy((char*)pNewValue, (char*)m_pValue, (char*)(pNewValue + m_unLen));
StrCpy((char*)(pNewValue + m_unLen), (char*)sValue, (char*)(pNewValue + m_unLen + unAppendLen));
if (m_pValue)
delete[] m_pValue;
m_pValue = pNewValue;
m_unLen += unAppendLen;
}
//----------------------------------------------------------------------------------------
// CBinaryObject
//----------------------------------------------------------------------------------------
CBinaryObject::CBinaryObject(BYTE* pValue, unsigned int unLen, bool bCopy)
{
m_pValue = NULL;
m_unLen = 0;
Set(pValue, unLen, bCopy);
}
CBinaryObject::~CBinaryObject()
{
if (m_pValue)
delete[] m_pValue;
}
void CBinaryObject::Set(BYTE* pValue, unsigned int unLen, bool bCopy)
{
unLen = std::min((unsigned int)LIMIT_MAX_STRING_LEN, unLen);
if (m_pValue)
{
delete[] m_pValue;
m_pValue = NULL;
m_unLen = 0;
}
if (!pValue || !unLen)
return;
m_unLen = unLen;
if (bCopy)
{
m_pValue = new BYTE[unLen];
MemCpy(m_pValue, pValue, unLen);
}
else
m_pValue = pValue;
}
void CBinaryObject::Add(BYTE* pValue, unsigned int unLen)
{
if (!pValue || !unLen)
return;
unLen = std::min((unsigned int)LIMIT_MAX_STRING_LEN, unLen);
if (!m_pValue || m_unLen == 0)
{
Set(pValue, unLen, true);
return;
}
if (m_unLen + unLen > (unsigned int)LIMIT_MAX_STRING_LEN)
{
unLen = (unsigned int)LIMIT_MAX_STRING_LEN - m_unLen;
if (unLen == 0)
return;
}
BYTE* pNewValue = new BYTE[m_unLen + unLen];
MemCpy(pNewValue, m_pValue, m_unLen);
MemCpy(pNewValue + m_unLen, pValue, unLen);
if (m_pValue)
delete[] m_pValue;
m_pValue = pNewValue;
m_unLen += unLen;
}
//----------------------------------------------------------------------------------------
// CProxyObject
//----------------------------------------------------------------------------------------
CProxyObject::CProxyObject(CObjectBase* pObject, bool bClear)
{
m_pObject = pObject;
m_bClear = bClear;
TXrefEntry* pXrefEntry = m_pObject->GetXrefEntry();
if (pXrefEntry)
pXrefEntry->pRefObj.push_back(this);
}
CProxyObject::~CProxyObject()
{
TXrefEntry* pXrefEntry = m_pObject ? m_pObject->GetXrefEntry() : NULL;
if (pXrefEntry)
{
std::vector<CProxyObject*>::iterator it = std::find(pXrefEntry->pRefObj.begin(), pXrefEntry->pRefObj.end(), this);
if (it != pXrefEntry->pRefObj.end())
pXrefEntry->pRefObj.erase(it);
}
if (m_bClear)
RELEASE_OBJECT(m_pObject);
}
void CProxyObject::Clear()
{
if (m_bClear)
{
RELEASE_OBJECT(m_pObject);
}
else
m_pObject = NULL;
}
//----------------------------------------------------------------------------------------
// CArrayObject
//----------------------------------------------------------------------------------------
void CArrayObject::Add(CObjectBase* pObject, bool bPushBack)
{
if (!pObject)
return;
// Не даем писать сложные объекты в массив не по ссылке
if (pObject->IsDirect())
return;
if (GetCount() >= LIMIT_MAX_ARRAY)
{
RELEASE_OBJECT(pObject);
return;
}
if (pObject->IsIndirect())
{
CObjectBase* pProxy = new CProxyObject(pObject);
if (!pProxy)
{
RELEASE_OBJECT(pObject);
return;
}
pObject = pProxy;
}
pObject->SetDirect();
if (bPushBack)
m_arrList.push_back(pObject);
else
m_arrList.insert(m_arrList.begin(), pObject);
}
void CArrayObject::Add(bool bValue)
{
CObjectBase* pBool = new CBoolObject(bValue);
if (pBool)
Add(pBool);
}
void CArrayObject::Add(int nValue)
{
CObjectBase* pNumber = new CNumberObject(nValue);
if (pNumber)
Add(pNumber);
}
void CArrayObject::Add(unsigned int unValue)
{
CObjectBase* pNumber = new CNumberObject((int)unValue);
if (pNumber)
Add(pNumber);
}
void CArrayObject::Add(float fValue)
{
CObjectBase* pReal = new CRealObject(fValue);
if (pReal)
Add(pReal);
}
void CArrayObject::Add(const char* sName)
{
CObjectBase* pName = new CNameObject(sName);
if (pName)
Add(pName);
}
void CArrayObject::Add(double dValue)
{
CObjectBase* pReal = new CRealObject(dValue);
if (pReal)
Add(pReal);
}
void CArrayObject::Insert(CObjectBase *pTarget, CObjectBase* pObject, bool bReplace)
{
if (!pObject)
return;
if (pObject->IsDirect())
return;
if (GetCount() >= LIMIT_MAX_ARRAY)
{
RELEASE_OBJECT(pObject);
return;
}
if (pObject->IsIndirect())
{
CObjectBase* pProxy = new CProxyObject(pObject);
if (!pProxy)
{
RELEASE_OBJECT(pObject);
return;
}
pObject = pProxy;
}
pObject->SetDirect();
//получаем target-object из списка
//рассмотреть случай, когда указатель на содержимое списка
//может быть proxy-object.
for (int nIndex = 0, nCount = m_arrList.size(); nIndex < nCount; nIndex++)
{
CObjectBase* pObjectItem = m_arrList.at(nIndex);
if (pObjectItem == pTarget || (object_type_PROXY == pObjectItem->GetType() && ((CProxyObject*)pObjectItem)->Get() == pTarget))
{
if (bReplace)
{
m_arrList.erase(m_arrList.begin() + nIndex);
RELEASE_OBJECT(pObjectItem);
}
m_arrList.insert(m_arrList.begin() + nIndex, pObject);
return;
}
}
// Дошли до сюда, значит не вставили данный объект, поэтому удаляем его
RELEASE_OBJECT(pObject);
}
CObjectBase* CArrayObject::Get(unsigned int unIndex, bool bCheckProxy) const
{
if (unIndex >= m_arrList.size())
return NULL;
CObjectBase* pObject = m_arrList.at(unIndex);
if (bCheckProxy && object_type_PROXY == pObject->GetType())
pObject = ((CProxyObject*)pObject)->Get();
return pObject;
}
CObjectBase* CArrayObject::Remove(unsigned int unIndex)
{
if (unIndex >= m_arrList.size())
return NULL;
CObjectBase* pObject = Get(unIndex);
if (pObject)
m_arrList.erase(m_arrList.begin() + unIndex);
return pObject;
}
void CArrayObject::Clear()
{
for (int nIndex = 0, nCount = m_arrList.size(); nIndex < nCount; nIndex++)
{
CObjectBase* pObject = m_arrList.at(nIndex);
RELEASE_OBJECT(pObject);
}
m_arrList.clear();
}
CArrayObject* CArrayObject::CreateBox(const TBox& oBox)
{
CArrayObject* pArray = new CArrayObject();
if (!pArray)
return NULL;
pArray->Add(oBox.fLeft);
pArray->Add(oBox.fBottom);
pArray->Add(oBox.fRight);
pArray->Add(oBox.fTop);
return pArray;
}
CArrayObject* CArrayObject::CreateBox(double dL, double dB, double dR, double dT)
{
CArrayObject* pArray = new CArrayObject();
if (!pArray)
return NULL;
pArray->Add(dL);
pArray->Add(dB);
pArray->Add(dR);
pArray->Add(dT);
return pArray;
}
CObjectBase* CArrayObject::Copy(CObjectBase* pOut) const
{
CArrayObject* pArray = pOut && pOut->GetType() == object_type_ARRAY ? (CArrayObject*)pOut : new CArrayObject();
if (!pArray)
return NULL;
for (unsigned int unIndex = 0, unCount = GetCount(); unIndex < unCount; ++unIndex)
{
pArray->Add(Get(unIndex, false)->Copy());
}
return pArray;
}
#define AddToObject(oVal)\
{\
if (pObject->GetType() == object_type_DICT)\
((CDictObject*)pObject)->Add(sName, oVal);\
else if (pObject->GetType() == object_type_ARRAY)\
((CArrayObject*)pObject)->Add(oVal);\
}
void ReadDict(XmlUtils::CXmlLiteReader& oCoreReader, CObjectBase* pObject)
{
int gen = 0;
std::string sType;
std::string sName = oCoreReader.GetNameA();
while (oCoreReader.MoveToNextAttribute())
{
std::wstring sAName = oCoreReader.GetName();
std::string sAText = oCoreReader.GetTextA();
if (sAName == L"type")
sType = sAText;
else if (sAName == L"gen")
gen = std::stoi(sAText);
else if (sAName == L"num")
{
if (sType == "Bool")
AddToObject(sAText == "true")
else if (sType == "Int")
AddToObject(std::stoi(sAText))
else if (sType == "Real")
AddToObject(std::stod(sAText))
else if (sType == "String")
AddToObject(new CStringObject(sAText.c_str()))
else if (sType == "Name")
AddToObject(sAText.c_str())
// Null ниже
// Array ниже
// Dict ниже
// Stream игнорируется
else if (sType == "Ref")
{
CObjectBase* pBase = new CObjectBase();
pBase->SetRef(std::stoi(sAText), gen);
AddToObject(new CProxyObject(pBase, true));
}
// Cmd игнорируется
else if (sType == "Cmd")
AddToObject(sAText.c_str())
// Error игнорируется
// EOF игнорируется
// None ниже
else if (sType == "Binary")
gen = std::stoi(sAText);
}
}
oCoreReader.MoveToElement();
if (sType == "Array")
{
CArrayObject* pArray = new CArrayObject();
AddToObject(pArray);
int n2Death = oCoreReader.GetDepth();
while (oCoreReader.ReadNextSiblingNode(n2Death))
ReadDict(oCoreReader, pArray);
}
else if (sType == "Dict")
{
CDictObject* pDict = new CDictObject();
AddToObject(pDict);
int n2Death = oCoreReader.GetDepth();
while (oCoreReader.ReadNextSiblingNode(n2Death))
ReadDict(oCoreReader, pDict);
}
else if (sType == "None")
AddToObject("None")
else if (sType == "Null")
AddToObject(new CNullObject())
else if (sType == "Binary")
{
BYTE* arrId = new BYTE[gen];
int n2Death = oCoreReader.GetDepth(), i = 0;
while (oCoreReader.ReadNextSiblingNode(n2Death))
{
std::wstring sChar = oCoreReader.GetText2();
arrId[i++] = std::stoi(sChar);
}
AddToObject(new CBinaryObject(arrId, gen));
RELEASEARRAYOBJECTS(arrId);
}
}
void CArrayObject::FromXml(const std::wstring& sXml)
{
XmlUtils::CXmlLiteReader oCoreReader;
oCoreReader.FromString(sXml);
oCoreReader.ReadNextNode();
int nDeath = oCoreReader.GetDepth();
while (oCoreReader.ReadNextSiblingNode(nDeath))
ReadDict(oCoreReader, this);
}
//----------------------------------------------------------------------------------------
// CDictObject
//----------------------------------------------------------------------------------------
CDictObject::CDictObject()
{
m_unFilter = STREAM_FILTER_NONE;
m_unPredictor = STREAM_PREDICTOR_NONE;
m_pStream = NULL;
}
CDictObject::CDictObject(CXref* pXref)
{
m_unFilter = STREAM_FILTER_NONE;
m_unPredictor = STREAM_PREDICTOR_NONE;
m_pStream = NULL;
CNumberObject* pLength = new CNumberObject(0);
// Только stream object добавляются в таблицу xref автоматически
pXref->Add((CObjectBase*)this);
pXref->Add((CObjectBase*)pLength);
Add("Length", (CObjectBase*)pLength);
m_pStream = new CMemoryStream(STREAM_BUF_SIZ);
}
CDictObject::~CDictObject()
{
for (auto const &oIter : m_mList)
{
CObjectBase* pObject = oIter.second;
RELEASE_OBJECT(pObject);
}
if (m_pStream)
delete m_pStream;
}
CObjectBase* CDictObject::Get(const std::string& sKey) const
{
std::map<std::string, CObjectBase*>::const_iterator oIter = m_mList.find(sKey);
if (m_mList.end() != oIter)
{
CObjectBase* pObject = oIter->second;
if (pObject && object_type_PROXY == pObject->GetType())
pObject = ((CProxyObject*)pObject)->Get();
return pObject;
}
return NULL;
}
void CDictObject::Add(const std::string& sKey, CObjectBase* pObject)
{
if (!pObject)
return;
if (pObject->IsDirect())
return;
if (m_mList.size() >= LIMIT_MAX_DICT_ELEMENT)
{
RELEASE_OBJECT(pObject);
return;
}
// Удаляем старую запись, если она была
Remove(sKey);
if (pObject->IsIndirect())
{
CObjectBase* pProxy = new CProxyObject(pObject);
pObject = pProxy;
}
pObject->SetDirect();
m_mList.insert(std::make_pair(sKey, pObject));
}
void CDictObject::Remove(const std::string& sKey)
{
std::map<std::string, CObjectBase*>::const_iterator pIter = m_mList.find(sKey);
if (m_mList.end() != pIter)
{
CObjectBase* pObject = pIter->second;
RELEASE_OBJECT(pObject);
m_mList.erase(sKey);
}
}
void CDictObject::Add(const std::string& sKey, const char* sName)
{
Add(sKey, new CNameObject(sName));
}
void CDictObject::Add(const std::string& sKey, int nNumber)
{
Add(sKey, new CNumberObject(nNumber));
}
void CDictObject::Add(const std::string& sKey, unsigned int unNumber)
{
Add(sKey, (int)unNumber);
}
void CDictObject::Add(const std::string& sKey, float fReal)
{
Add(sKey, new CRealObject(fReal));
}
void CDictObject::Add(const std::string& sKey, double dReal)
{
Add(sKey, new CRealObject(dReal));
}
void CDictObject::Add(const std::string& sKey, bool bBool)
{
Add(sKey, new CBoolObject(bBool));
}
const char* CDictObject::GetKey(const CObjectBase* pObject)
{
for (auto const &oIter : m_mList)
{
CObjectBase* pListObject = oIter.second;
if (pListObject && object_type_PROXY == pListObject->GetType())
pListObject = ((CProxyObject*)pListObject)->Get();
if (pListObject == pObject)
return oIter.first.c_str();
}
return NULL;
}
void CDictObject::WriteToStream(CStream* pStream, CEncrypt* pEncrypt)
{
for (auto const &oIter : m_mList)
{
CObjectBase* pObject = oIter.second;
if (!pObject)
continue;
if (pObject->IsHidden())
{
// ничего не делаем
}
else
{
pStream->WriteEscapeName(oIter.first.c_str());
pStream->WriteChar(' ');
pStream->Write(pObject, pEncrypt);
pStream->WriteStr("\012");
}
}
}
void CDictObject::SetStream(CStream* pStream)
{
m_pStream = pStream;
}
void CDictObject::SetStream(CXref* pXref, CStream* pStream, bool bThis)
{
if (m_pStream)
delete m_pStream;
if (!Get("Length"))
{
CNumberObject* pLength = new CNumberObject(0);
// Только stream object добавляются в таблицу xref автоматически
if (bThis)
pXref->Add((CObjectBase*)this);
pXref->Add((CObjectBase*)pLength);
Add("Length", pLength);
}
m_pStream = pStream;
}
CObjectBase* CDictObject::Copy(CObjectBase* pOut) const
{
CDictObject* pDict = pOut && pOut->GetType() == object_type_DICT ? (CDictObject*)pOut : new CDictObject();
if (!pDict)
return NULL;
for (auto const &oIter : m_mList)
{
pDict->Add(oIter.first, oIter.second->Copy());
}
return pDict;
}
void CDictObject::FromXml(const std::wstring& sXml)
{
XmlUtils::CXmlLiteReader oCoreReader;
oCoreReader.FromString(sXml);
oCoreReader.ReadNextNode();
int num = 0, gen = 0;
while (oCoreReader.MoveToNextAttribute())
{
std::wstring sAName = oCoreReader.GetName();
std::string sAText = oCoreReader.GetTextA();
if (sAName == L"gen")
gen = std::stoi(sAText);
else if (sAName == L"num")
num = std::stoi(sAText);
}
oCoreReader.MoveToElement();
if (num)
SetRef(num, gen);
int nDeath = oCoreReader.GetDepth();
while (oCoreReader.ReadNextSiblingNode(nDeath))
ReadDict(oCoreReader, this);
}
//----------------------------------------------------------------------------------------
// CXref
//----------------------------------------------------------------------------------------
CXref::CXref(CDocument* pDocument, unsigned int unOffset)
{
m_pDocument = pDocument;
m_unStartOffset = unOffset;
m_unAddr = 0;
m_pPrev = NULL;
m_pTrailer = NULL;
if (0 == m_unStartOffset)
{
// Добавляем первый элемент в таблицу xref
// он должен иметь вид 0000000000 65535 f
TXrefEntry* pEntry = new TXrefEntry;
pEntry->nEntryType = FREE_ENTRY;
pEntry->unByteOffset = 0;
pEntry->unGenNo = MAX_GENERATION_NUM;
pEntry->pObject = NULL;
m_arrEntries.push_back(pEntry);
}
m_pTrailer = new CDictObject();
}
CXref::CXref(CDocument* pDocument, unsigned int unRemoveId, unsigned int unRemoveGen)
{
m_pDocument = pDocument;
m_unStartOffset = unRemoveId;
m_unAddr = 0;
m_pPrev = NULL;
m_pTrailer = NULL;
// Добавляем удаляемый элемент в таблицу xref
// он должен иметь вид 0000000000 gen+1 f
TXrefEntry* pEntry = new TXrefEntry;
pEntry->nEntryType = FREE_ENTRY;
pEntry->unByteOffset = 0;
pEntry->unGenNo = unRemoveGen + 1 > MAX_GENERATION_NUM ? MAX_GENERATION_NUM : unRemoveGen + 1;
pEntry->pObject = NULL;
m_arrEntries.push_back(pEntry);
m_pTrailer = new CDictObject();
}
CXref::~CXref()
{
for (int nIndex = 0, nCount = m_arrEntries.size(); nIndex < nCount; nIndex++)
{
TXrefEntry* pEntry = m_arrEntries.at(nIndex);
if (pEntry)
{
CObjectBase* pObject = pEntry->pObject;
if (pObject)
delete pObject;
}
for (int i = 0; i < pEntry->pRefObj.size(); ++i)
{
CProxyObject* pProxy = pEntry->pRefObj[i];
pProxy->Clear();
}
delete pEntry;
}
if (m_pTrailer)
delete m_pTrailer;
if (m_pPrev)
delete m_pPrev;
}
TXrefEntry* CXref::GetEntry(unsigned int unIndex) const
{
return m_arrEntries.at(unIndex);
}
TXrefEntry* CXref::GetEntryByObjectId(unsigned int nObjectId) const
{
const CXref* pXref = this;
while (pXref)
{
if (pXref->m_arrEntries.size() + pXref->m_unStartOffset <= nObjectId)
return NULL;
if (pXref->m_unStartOffset <= nObjectId)
{
for (unsigned int unIndex = 0, nCount = pXref->m_arrEntries.size(); unIndex < nCount; unIndex++)
{
if (pXref->m_unStartOffset + unIndex == nObjectId)
{
return pXref->GetEntry(unIndex);
}
}
}
pXref = (const CXref*)pXref->m_pPrev;
}
return NULL;
}
CXref* CXref::GetXrefByObjectId(unsigned int unObjectId)
{
CXref* pXref = this;
while (pXref)
{
if (unObjectId >= pXref->m_unStartOffset && unObjectId < pXref->m_unStartOffset + pXref->m_arrEntries.size())
return pXref;
pXref = pXref->m_pPrev;
}
return NULL;
}
void CXref::Add(CObjectBase* pObject)
{
if (!pObject)
return;
if (pObject->IsDirect() || pObject->IsIndirect())
return;
if (m_arrEntries.size() >= LIMIT_MAX_XREF_ELEMENT)
{
RELEASE_OBJECT(pObject);
return;
}
// В случае ошибки r объектe нужно применить dispose
TXrefEntry* pEntry = new TXrefEntry;
if (NULL == pEntry)
{
RELEASE_OBJECT(pObject);
return;
}
m_arrEntries.push_back(pEntry);
pEntry->nEntryType = IN_USE_ENTRY;
pEntry->unByteOffset = 0;
pEntry->unGenNo = 0;
pEntry->pObject = pObject;
pObject->SetIndirect();
pObject->SetXrefEntry(pEntry);
}
void CXref::Add(CObjectBase* pObject, unsigned int unObjectGen)
{
if (!pObject)
return;
if (pObject->IsDirect() || pObject->IsIndirect())
return;
if (m_arrEntries.size() >= LIMIT_MAX_XREF_ELEMENT)
{
RELEASE_OBJECT(pObject);
return;
}
// В случае ошибки r объектe нужно применить dispose
TXrefEntry* pEntry = new TXrefEntry;
if (NULL == pEntry)
{
RELEASE_OBJECT(pObject);
return;
}
m_arrEntries.push_back(pEntry);
pEntry->nEntryType = IN_USE_ENTRY;
pEntry->unByteOffset = 0;
pEntry->unGenNo = unObjectGen;
pEntry->pObject = pObject;
pObject->SetRef(m_unStartOffset + m_arrEntries.size() - 1, pEntry->unGenNo);
pObject->SetIndirect();
pObject->SetXrefEntry(pEntry);
}
void CXref::Remove(CObjectBase* pObject)
{
if (!pObject)
return;
for (unsigned int unIndex = 0, unCount = m_arrEntries.size(); unIndex < unCount; ++unIndex)
{
TXrefEntry* pEntry = m_arrEntries.at(unIndex);
if (pEntry->pObject == pObject)
{
for (int i = 0; i < pEntry->pRefObj.size(); ++i)
pEntry->pRefObj[i]->Clear();
CObjectBase* pObject = pEntry->pObject;
if (pObject)
delete pObject;
m_arrEntries.erase(m_arrEntries.begin() + unIndex);
delete pEntry;
break;
}
}
}
void CXref::WriteTrailer(CStream* pStream)
{
CXref* pPrev = m_pPrev;
if (m_pPrev)
while (pPrev->m_pPrev) pPrev = pPrev->m_pPrev;
unsigned int unMaxObjId = m_pPrev ? pPrev->m_arrEntries.size() + pPrev->m_unStartOffset : m_arrEntries.size() + m_unStartOffset;
m_pTrailer->Add("Size", unMaxObjId);
if (m_pPrev && pPrev->m_unAddr)
m_pTrailer->Add("Prev", pPrev->m_unAddr);
pStream->WriteStr("trailer\012");
pStream->Write(m_pTrailer, NULL);
pStream->WriteStr("\012startxref\012");
pStream->WriteUInt(m_unAddr);
pStream->WriteStr("\012%%EOF\012");
}
void CXref::WriteToStream(CStream* pStream, CEncrypt* pEncrypt, bool bStream)
{
char sBuf[SHORT_BUFFER_SIZE];
char* pBuf;
char* pEndPtr = sBuf + SHORT_BUFFER_SIZE - 1;
CXref* pXref = this;
while (pXref)
{
for (unsigned int unIndex = 0, unCount = pXref->m_arrEntries.size(); unIndex < unCount; unIndex++)
{
TXrefEntry* pEntry = pXref->m_arrEntries.at(unIndex);
if (pEntry->nEntryType != FREE_ENTRY && pEntry->pObject->GetObjId() == 0)
pEntry->pObject->SetRef(pXref->m_unStartOffset + unIndex, pEntry->unGenNo);
}
pXref = pXref->m_pPrev;
}
pXref = this;
TXrefEntry* pNextFreeObj = NULL;
while (pXref)
{
for (unsigned int unIndex = 0, unCount = pXref->m_arrEntries.size(); unIndex < unCount; unIndex++)
{
TXrefEntry* pEntry = pXref->m_arrEntries.at(unIndex);
if (pEntry->nEntryType == FREE_ENTRY)
{
if (pNextFreeObj)
pNextFreeObj->unByteOffset = pXref->m_unStartOffset + unIndex;
pNextFreeObj = pEntry;
}
else
{
unsigned int unObjId = pXref->m_unStartOffset + unIndex;
unsigned int unGenNo = pEntry->unGenNo;
pEntry->unByteOffset = pStream->Tell();
if (pEncrypt)
pEncrypt->InitKey(unObjId, unGenNo);
pBuf = sBuf;
pBuf = ItoA(pBuf, unObjId, pEndPtr);
*pBuf++ = ' ';
pBuf = ItoA(pBuf, unGenNo, pEndPtr);
StrCpy(pBuf, " obj\012", pEndPtr);
pStream->WriteStr(sBuf);
pEntry->pObject->WriteValue(pStream, pEncrypt);
pStream->WriteStr("\012endobj\012");
}
}
pXref = pXref->m_pPrev;
}
if (pNextFreeObj)
pNextFreeObj->unByteOffset = 0;
if (bStream)
{
CXref* pPrev = m_pPrev;
if (m_pPrev)
while (pPrev->m_pPrev) pPrev = pPrev->m_pPrev;
unsigned int unMaxObjId = m_pPrev ? pPrev->m_arrEntries.size() + pPrev->m_unStartOffset : m_arrEntries.size() + m_unStartOffset;
CDictObject* pTrailer = m_pTrailer;
pTrailer->Add("Type", "XRef");
pTrailer->Add("Size", unMaxObjId + 1);
if (m_pPrev && pPrev->m_unAddr)
pTrailer->Add("Prev", pPrev->m_unAddr);
int nStreamOffset = pStream->Tell();
int nOffsetSize = 1;
if (nStreamOffset > 1 << 24)
nOffsetSize = 4;
else if (nStreamOffset > 1 << 16)
nOffsetSize = 3;
else if (nStreamOffset > 1 << 8)
nOffsetSize = 2;
CArrayObject* pW = new CArrayObject();
pW->Add(1);
pW->Add(nOffsetSize);
pW->Add(2);
pTrailer->Add("W", pW);
CArrayObject* pIndex = new CArrayObject();
pTrailer->Add("Index", pIndex);
CNumberObject* pLength = new CNumberObject(0);
pTrailer->Add("Length", pLength);
#ifndef FILTER_FLATE_DECODE_DISABLED
pTrailer->SetFilter(STREAM_FILTER_FLATE_DECODE);
pTrailer->Add("Filter", "FlateDecode");
#endif
// Сортируем pXref, Index должен быть в порядке возрастания
pXref = this;
CXref* q, *p, *pr, *out = NULL;
while (pXref)
{
q = pXref;
pXref = pXref->m_pPrev;
for ( p = out, pr = NULL; p && (q->m_unStartOffset > p->m_unStartOffset); pr = p, p = p->m_pPrev);
if (pr)
{
q->m_pPrev = p;
pr->m_pPrev = q;
}
else
{
q->m_pPrev = out;
out = q;
}
}
// Записываем поток
pXref = out;
pXref->m_unAddr = nStreamOffset;
CStream* pTrailerStream = new CMemoryStream();
unsigned int unEntries = 0, unEntriesSize = 0;
while (pXref)
{
unsigned int unNewEntries = pXref->m_unStartOffset;
unsigned int unNewEntriesSize = (unsigned int)pXref->m_arrEntries.size() + (pXref->m_pPrev ? 0 : 1);
if (unNewEntries == unEntries + unEntriesSize)
unEntriesSize += unNewEntriesSize;
else
{
pIndex->Add(unEntries);
pIndex->Add(unEntriesSize);
unEntries = unNewEntries;
unEntriesSize = unNewEntriesSize;
}
for (unsigned int unIndex = 0, unCount = pXref->m_arrEntries.size(); unIndex < unCount; unIndex++)
{
TXrefEntry* pEntry = pXref->GetEntry(unIndex);
if (pEntry->nEntryType == FREE_ENTRY)
pTrailerStream->WriteChar('\000');
else if (pEntry->nEntryType == IN_USE_ENTRY)
pTrailerStream->WriteChar('\001');
for (int i = nOffsetSize - 1; i >= 0; --i)
pTrailerStream->WriteChar((pEntry->unByteOffset >> (8 * i)) & 0xFF);
pTrailerStream->WriteChar((unsigned char)(pEntry->unGenNo >> 8));
pTrailerStream->WriteChar((unsigned char)(pEntry->unGenNo));
}
pXref = pXref->m_pPrev;
}
// Добавляем последний элемент
pIndex->Add(unEntries);
pIndex->Add(unEntriesSize);
pTrailerStream->WriteChar('\001');
for (int i = nOffsetSize - 1; i >= 0; --i)
pTrailerStream->WriteChar((nStreamOffset >> (8 * i)) & 0xFF);
pTrailerStream->WriteChar('\000');
pTrailerStream->WriteChar('\000');
// Фильтруем поток, pEncrypt = NULL поток перекрестных ссылок не шифруется
CStream* pFlateStream = new CMemoryStream();
pFlateStream->WriteStream(pTrailerStream, pTrailer->GetFilter(), NULL);
pLength->Set(pFlateStream->Size());
pBuf = sBuf;
pBuf = ItoA(pBuf, unMaxObjId, pEndPtr);
*pBuf++ = ' ';
pBuf = ItoA(pBuf, 0, pEndPtr);
StrCpy(pBuf, " obj\012", pEndPtr);
// Записываем cross-reference table
pStream->WriteStr(sBuf);
pTrailer->WriteValue(pStream, NULL);
pStream->WriteStr("\012stream\015\012");
pStream->WriteStream(pFlateStream, 0, NULL);
pStream->WriteStr("\012endstream\012endobj\012startxref\012");
pStream->WriteUInt(nStreamOffset);
pStream->WriteStr("\012%%EOF\012");
RELEASEOBJECT(pFlateStream);
RELEASEOBJECT(pTrailerStream);
return;
}
// Записываем cross-reference table
pXref = this;
pXref->m_unAddr = pStream->Tell();
pStream->WriteStr("xref\012");
while (pXref)
{
if (pXref->m_arrEntries.size())
{
pStream->WriteInt(pXref->m_unStartOffset);
pStream->WriteChar(' ');
pStream->WriteInt(pXref->m_arrEntries.size());
pStream->WriteChar('\012');
}
for (unsigned int unIndex = 0, unCount = pXref->m_arrEntries.size(); unIndex < unCount; unIndex++)
{
TXrefEntry* pEntry = pXref->GetEntry(unIndex);
pBuf = sBuf;
pBuf = ItoA2(pBuf, pEntry->unByteOffset, BYTE_OFFSET_LEN + 1);
*pBuf++ = ' ';
pBuf = ItoA2(pBuf, pEntry->unGenNo, GEN_NO_LEN + 1);
*pBuf++ = ' ';
*pBuf++ = pEntry->nEntryType;
StrCpy(pBuf, "\015\012", pEndPtr);
pStream->WriteStr(sBuf);
}
pXref = pXref->m_pPrev;
}
// Записываем Trailer
WriteTrailer(pStream);
}
bool CXref::IsPDFA() const
{
return m_pDocument->IsPDFA();
}
}