/* * (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::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::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::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(); } }