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

2061 lines
55 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (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 "Document.h"
#include "Info.h"
#include "Catalog.h"
#include "Streams.h"
#include "EncryptDictionary.h"
#include "Encrypt.h"
#include "Pages.h"
#include "Outline.h"
#include "Destination.h"
#include "GState.h"
#include "Annotation.h"
#include "Image.h"
#include "Font14.h"
#include "FontCidTT.h"
#include "FontTT.h"
#include "FontTTWriter.h"
#include "Shading.h"
#include "Pattern.h"
#include "AcroForm.h"
#include "Field.h"
#include "ResourcesDictionary.h"
#include "Metadata.h"
#include "../../DesktopEditor/agg-2.4/include/agg_span_hatch.h"
#include "../../DesktopEditor/common/SystemUtils.h"
#ifdef CreateFont
#undef CreateFont
#endif
#ifndef VALUE2STR
#define VALUE_TO_STRING(x) #x
#define VALUE2STR(x) VALUE_TO_STRING(x)
#endif
namespace PdfWriter
{
const char* c_sPdfHeader = "%PDF-1.7\015%\315\312\322\251\015";
const char* c_sPdfAHeader = "%PDF-1.4\015%\315\312\322\251\015";
//----------------------------------------------------------------------------------------
// CDocument
//----------------------------------------------------------------------------------------
CDocument::CDocument()
{
m_pCatalog = NULL;
m_pOutlines = NULL;
m_pXref = NULL;
m_pLastXref = NULL;
m_pPageTree = NULL;
m_pCurPage = NULL;
m_nCurPageNum = -1;
m_pCurImage = NULL;
m_pInfo = NULL;
m_pTrailer = NULL;
m_pResources = NULL;
m_pMetaData = NULL;
m_bEncrypt = false;
m_pEncryptDict = NULL;
m_unFormFields = 0;
m_unCompressMode = COMP_NONE;
memset((void*)m_sTTFontTag, 0x00, 8);
m_pJbig2 = NULL;
m_pDefaultCheckBoxFont = NULL;
m_pTransparencyGroup = NULL;
m_pFreeTypeLibrary = NULL;
m_bPDFAConformance = false;
m_pAcroForm = NULL;
m_pFieldsResources = NULL;
}
CDocument::~CDocument()
{
Close();
}
bool CDocument::CreateNew()
{
Close();
m_pXref = new CXref(this, 0);
if (!m_pXref)
return false;
m_pTrailer = m_pXref->GetTrailer();
if (!m_pTrailer)
return false;
m_pMetaData = new CStreamData(m_pXref);
if (!m_pMetaData)
return false;
m_pCatalog = new CCatalog(m_pXref);
if (!m_pCatalog)
return false;
m_pCatalog->SetPageMode(pagemode_UseNone);
m_pCatalog->SetPageLayout(pagelayout_OneColumn);
m_pPageTree = m_pCatalog->GetRoot();
if (!m_pPageTree)
return false;
m_pInfo = new CInfoDict(m_pXref);
if (!m_pInfo)
return false;
m_pInfo->SetTime(InfoCreationDate);
m_pInfo->SetTime(InfoModaDate);
std::wstring sCreator = NSSystemUtils::GetEnvVariable(NSSystemUtils::gc_EnvApplicationName);
if (sCreator.empty())
sCreator = NSSystemUtils::gc_EnvApplicationNameDefault;
std::string sCreatorA = NSFile::CUtf8Converter::GetUtf8StringFromUnicode(sCreator);
#if defined(INTVER)
std::string sVersion = VALUE2STR(INTVER);
sCreatorA += ("/" + sVersion);
#endif
m_pInfo->SetInfo(InfoProducer, sCreatorA.c_str());
m_pInfo->SetInfo(InfoCreator, sCreatorA.c_str());
if (IsPDFA())
{
CArrayObject* pID = (CArrayObject*)m_pTrailer->Get("ID");
if (!pID)
{
BYTE arrId[16];
CEncryptDict::CreateId(m_pInfo, m_pXref, (BYTE*)arrId);
pID = new CArrayObject();
m_pTrailer->Add("ID", pID);
pID->Add(new CBinaryObject(arrId, 16));
pID->Add(new CBinaryObject(arrId, 16));
}
}
m_nCurPageNum = -1;
m_vExtGrStates.clear();
m_vFillAlpha.clear();
m_vStrokeAlpha.clear();
m_vRadioGroups.clear();
m_vMetaOForms.clear();
m_vImages.clear();
m_pTransparencyGroup = NULL;
return true;
}
void CDocument::Close()
{
// Все объекты удаляются внутри CXref
RELEASEOBJECT(m_pXref);
m_pLastXref = NULL;
m_pTrailer = NULL;
m_pResources = NULL;
m_pCatalog = NULL;
m_pOutlines = NULL;
m_pPageTree = NULL;
m_pCurPage = NULL;
m_nCurPageNum = 0;
m_pCurImage = NULL;
m_unFormFields = 0;
m_bEncrypt = false;
m_pEncryptDict = NULL;
m_pInfo = NULL;
m_unCompressMode = COMP_NONE;
m_pJbig2 = NULL;
m_pTransparencyGroup= NULL;
m_pAcroForm = NULL;
m_pFieldsResources = NULL;
memset((void*)m_sTTFontTag, 0x00, 8);
m_pDefaultCheckBoxFont = NULL;
m_vExtGrStates.clear();
m_vStrokeAlpha.clear();
m_vFillAlpha.clear();
m_vShadings.clear();
m_vCidTTFonts.clear();
m_vTTFonts.clear();
m_vFreeTypeFonts.clear();
m_vSignatures.clear();
m_vMetaOForms.clear();
m_vImages.clear();
if (m_pFreeTypeLibrary)
{
FT_Done_FreeType(m_pFreeTypeLibrary);
m_pFreeTypeLibrary = NULL;
}
}
bool CDocument::SaveToFile(const std::wstring& wsPath)
{
CFileStream* pStream = new CFileStream();
if (!pStream || !pStream->OpenFile(wsPath, true))
return false;
if (m_pJbig2)
m_pJbig2->FlushStreams();
SaveToStream((CStream*)pStream);
delete pStream;
Sign(wsPath, m_pXref->GetSizeXRef());
return true;
}
bool CDocument::SaveToMemory(BYTE** pData, int* pLength)
{
CMemoryStream* pStream = new CMemoryStream();
if (!pStream)
return false;
if (m_pJbig2)
m_pJbig2->FlushStreams();
SaveToStream(pStream);
*pData = pStream->GetBuffer();
*pLength = pStream->Size();
pStream->ClearWithoutAttack();
return true;
}
void CDocument::SaveToStream(CStream* pStream)
{
m_pCatalog->AddMetadata(m_pXref, m_pInfo);
// Пишем заголовок
if (IsPDFA())
pStream->WriteStr(c_sPdfAHeader);
else
pStream->WriteStr(c_sPdfHeader);
if (false == m_wsDocumentID.empty())
{
std::string sDocumentID = "%DocumentID " + NSFile::CUtf8Converter::GetUtf8StringFromUnicode(m_wsDocumentID);
pStream->WriteStr(sDocumentID.c_str());
}
// Добавляем в Trailer необходимые элементы
m_pTrailer->Add("Root", m_pCatalog);
m_pTrailer->Add("Info", m_pInfo);
// Шифруем документ, если это необходимо
CEncrypt* pEncrypt = NULL;
if (m_bEncrypt)
{
pEncrypt = m_pEncryptDict->GetEncrypt();
PrepareEncryption();
}
m_pXref->WriteToStream(pStream, pEncrypt, true);
}
bool CDocument::SaveNewWithPassword(CXref* pXref, CXref* _pXref, const std::wstring& wsPath, const std::wstring& wsOwnerPassword, const std::wstring& wsUserPassword, CDictObject* pTrailer)
{
if (!pXref || !pTrailer || !_pXref)
return false;
m_pTrailer = pTrailer;
CEncrypt* pEncrypt = NULL;
if (!wsOwnerPassword.empty())
{
m_pEncryptDict = new CEncryptDict(_pXref);
m_pEncryptDict->SetPasswords(wsOwnerPassword, wsUserPassword);
m_pTrailer->Add("Encrypt", m_pEncryptDict);
pEncrypt = m_pEncryptDict->GetEncrypt();
PrepareEncryption();
}
CFileStream* pStream = new CFileStream();
if (!pStream || !pStream->OpenFile(wsPath, true))
return false;
pStream->WriteStr(c_sPdfHeader);
pXref->WriteToStream(pStream, pEncrypt);
delete pStream;
return true;
}
void CDocument::PrepareEncryption()
{
CEncrypt* pEncrypt = m_pEncryptDict->GetEncrypt();
if (!pEncrypt)
return;
m_pEncryptDict->Prepare(m_pInfo, m_pXref);
CArrayObject* pID = (CArrayObject*)m_pTrailer->Get("ID");
if (!pID)
{
pID = new CArrayObject();
m_pTrailer->Add("ID", pID);
}
else
pID->Clear();
pID->Add(new CBinaryObject(pEncrypt->m_anEncryptID, 16));
pID->Add(new CBinaryObject(pEncrypt->m_anEncryptID, 16));
if (m_pMetaData)
m_pMetaData->SetID(new CBinaryObject(pEncrypt->m_anEncryptID, 16));
for (int i = 0; i < m_vMetaOForms.size(); ++i)
m_vMetaOForms[i]->Add("ID", new CBinaryObject(pEncrypt->m_anEncryptID, 16));
}
void CDocument::SetPasswords(const std::wstring & wsOwnerPassword, const std::wstring & wsUserPassword)
{
if (IsPDFA())
return;
if (!m_pEncryptDict)
m_pEncryptDict = new CEncryptDict(m_pXref);
if (!m_pEncryptDict)
return;
m_pEncryptDict->SetPasswords(wsOwnerPassword, wsUserPassword);
m_pTrailer->Add("Encrypt", m_pEncryptDict);
m_bEncrypt = true;
}
CPage* CDocument::AddPage()
{
CPage* pPage = new CPage(m_pXref, m_pPageTree, this);
m_pPageTree->AddPage(pPage);
m_pCurPage = pPage;
#ifndef FILTER_FLATE_DECODE_DISABLED
if (m_unCompressMode & COMP_TEXT)
pPage->SetFilter(STREAM_FILTER_FLATE_DECODE);
#endif
m_nCurPageNum++;
return pPage;
}
CPage* CDocument::GetPage(const unsigned int &unPage)
{
if (unPage >= m_pPageTree->GetCount())
return NULL;
return m_pPageTree->GetPage(unPage);
}
CPage* CDocument::GetEditPage(const unsigned int& unPage)
{
CPage* pRes = NULL;
std::map<int, CPage*>::iterator p = m_mEditPages.find(unPage);
if (p != m_mEditPages.end())
pRes = p->second;
return pRes;
}
int CDocument::FindPage(CPage* pPage)
{
int nI = 0;
return m_pPageTree->Find(pPage, nI) ? nI : -1;
}
unsigned int CDocument::GetPagesCount() const
{
return m_pPageTree->GetCount();
}
void CDocument::SetDocumentID(const std::wstring& documentID)
{
m_wsDocumentID = documentID;
}
void CDocument::SetTitle(const std::string& sTitle)
{
if (!m_pInfo)
return;
m_pInfo->SetInfo(InfoTitle, sTitle.c_str());
}
void CDocument::SetAuthor(const std::string& sAuthor)
{
if (!m_pInfo)
return;
m_pInfo->SetInfo(InfoAuthor, sAuthor.c_str());
}
void CDocument::SetSubject(const std::string& sSubject)
{
if (!m_pInfo)
return;
m_pInfo->SetInfo(InfoSubject, sSubject.c_str());
}
void CDocument::SetKeywords(const std::string& sKeywords)
{
if (!m_pInfo)
return;
m_pInfo->SetInfo(InfoKeyWords, sKeywords.c_str());
}
void CDocument::SetPermission(unsigned int unPermission)
{
if (!m_bEncrypt)
return;
CEncrypt* pEncrypt = m_pEncryptDict->GetEncrypt();
pEncrypt->SetPermission(unPermission);
}
void CDocument::SetCompressionMode(unsigned int unMode)
{
m_unCompressMode = unMode;
}
unsigned int CDocument::GetCompressionMode() const
{
return m_unCompressMode;
}
void CDocument::SetPDFAConformanceMode(bool isPDFA)
{
m_bPDFAConformance = isPDFA;
}
bool CDocument::IsPDFA() const
{
return m_bPDFAConformance;
}
void CDocument::AddPageLabel(EPageNumStyle eStyle, unsigned int unFirstPage, const char* sPrefix)
{
CDictObject* pPageLabel = CreatePageLabel(eStyle, unFirstPage, sPrefix);
if (!pPageLabel)
return;
m_pCatalog->AddPageLabel(m_nCurPageNum, pPageLabel);
}
void CDocument::AddPageLabel(unsigned int unPageNum, EPageNumStyle eStyle, unsigned int unFirstPage, const char* sPrefix)
{
CDictObject* pPageLabel = CreatePageLabel(eStyle, unFirstPage, sPrefix);
if (!pPageLabel)
return;
m_pCatalog->AddPageLabel(unPageNum, pPageLabel);
}
bool CDocument::AddMetaData(const std::wstring& sMetaName, BYTE* pMetaData, DWORD nMetaLength)
{
if (!m_pMetaData)
return false;
CBinaryObject* sID = NULL;
CArrayObject* pID = (CArrayObject*)m_pTrailer->Get("ID");
if (!pID)
{
BYTE arrId[16];
CEncryptDict::CreateId(m_pInfo, m_pXref, (BYTE*)arrId);
pID = new CArrayObject();
m_pTrailer->Add("ID", pID);
pID->Add(new CBinaryObject(arrId, 16));
pID->Add(new CBinaryObject(arrId, 16));
sID = new CBinaryObject(arrId, 16);
}
else
sID = (CBinaryObject*)pID->Get(1)->Copy();
m_pMetaData->SetID(sID);
return m_pMetaData->AddMetaData(sMetaName, pMetaData, nMetaLength);
}
CDictObject* CDocument::CreatePageLabel(EPageNumStyle eStyle, unsigned int unFirstPage, const char* sPrefix)
{
CDictObject* pLabel = new CDictObject();
if (!pLabel)
return NULL;
eStyle = std::min(std::max(eStyle, pagenumstyle_Min), pagenumstyle_Max);
switch (eStyle)
{
case pagenumstyle_UpperRoman: pLabel->Add("S", "R"); break;
case pagenumstyle_LowerRoman: pLabel->Add("S", "r"); break;
case pagenumstyle_UpperLetters: pLabel->Add("S", "A"); break;
case pagenumstyle_LowerLetters: pLabel->Add("S", "a"); break;
case pagenumstyle_Decimal: pLabel->Add("S", "D"); break;
}
if (sPrefix && 0 != sPrefix[0])
pLabel->Add("P", new CStringObject(sPrefix));
if (0 != unFirstPage)
pLabel->Add("St", unFirstPage);
return pLabel;
}
COutline* CDocument::CreateOutline(COutline* pParent, const char* sTitle)
{
if (!pParent)
{
if (!m_pOutlines)
{
m_pOutlines = new COutline(m_pXref);
if (m_pOutlines)
m_pCatalog->Add("Outlines", m_pOutlines);
else
return NULL;
}
pParent = m_pOutlines;
}
return new COutline(pParent, sTitle, m_pXref);
}
CDestination* CDocument::CreateDestination(CObjectBase* pPage, bool bInline)
{
if (pPage)
return new CDestination(pPage, m_pXref, bInline);
return NULL;
}
CExtGrState* CDocument::FindExtGrState(double dAlphaStroke, double dAlphaFill, EBlendMode eMode, int nStrokeAdjustment)
{
CExtGrState* pExtGrState = NULL;
for (unsigned int unIndex = 0, unCount = m_vExtGrStates.size(); unIndex < unCount; unIndex++)
{
pExtGrState = m_vExtGrStates.at(unIndex);
if (dAlphaStroke != pExtGrState->GetAlphaStroke())
continue;
if (dAlphaFill != pExtGrState->GetAlphaFill())
continue;
if (eMode != pExtGrState->GetBlendMode())
continue;
if ((0 == nStrokeAdjustment ? false : true) != pExtGrState->GetStrokeAdjustment())
continue;
return pExtGrState;
}
return NULL;
}
void CDocument::AddExtGState(CExtGrState* pState)
{
m_vExtGrStates.push_back(pState);
}
CExtGrState* CDocument::GetExtGState(double dAlphaStroke, double dAlphaFill, EBlendMode eMode, int nStrokeAdjustment)
{
CExtGrState* pExtGrState = FindExtGrState(dAlphaStroke, dAlphaFill, eMode, nStrokeAdjustment);
if (!pExtGrState)
{
pExtGrState = new CExtGrState(m_pXref);
if (!pExtGrState)
return NULL;
if (-1 != dAlphaStroke)
pExtGrState->SetAlphaStroke(dAlphaStroke);
if (-1 != dAlphaFill)
pExtGrState->SetAlphaFill(dAlphaFill);
if (blendmode_Unknown != eMode)
pExtGrState->SetBlendMode(eMode);
if (-1 != nStrokeAdjustment)
pExtGrState->SetStrokeAdjustment(0 == nStrokeAdjustment ? false : true);
m_vExtGrStates.push_back(pExtGrState);
}
return pExtGrState;
}
CExtGrState* CDocument::GetStrokeAlpha(double dAlpha)
{
CExtGrState* pExtGrState = NULL;
for (unsigned int unIndex = 0, unCount = m_vStrokeAlpha.size(); unIndex < unCount; unIndex++)
{
pExtGrState = m_vStrokeAlpha.at(unIndex);
if (fabs(dAlpha - pExtGrState->GetAlphaStroke()) < 0.001)
return pExtGrState;
}
pExtGrState = new CExtGrState(m_pXref);
if (!pExtGrState)
return NULL;
pExtGrState->SetAlphaStroke(dAlpha);
m_vStrokeAlpha.push_back(pExtGrState);
return pExtGrState;
}
CExtGrState* CDocument::GetFillAlpha(double dAlpha)
{
CExtGrState* pExtGrState = NULL;
for (unsigned int unIndex = 0, unCount = m_vFillAlpha.size(); unIndex < unCount; unIndex++)
{
pExtGrState = m_vFillAlpha.at(unIndex);
if (fabs(dAlpha - pExtGrState->GetAlphaFill()) < 0.001)
return pExtGrState;
}
pExtGrState = new CExtGrState(m_pXref);
if (!pExtGrState)
return NULL;
pExtGrState->SetAlphaFill(dAlpha);
m_vFillAlpha.push_back(pExtGrState);
return pExtGrState;
}
CAnnotation* CDocument::CreateAnnot(BYTE m_nType)
{
CAnnotation* pAnnot = NULL;
if (m_nType == 0)
{
pAnnot = new CTextAnnotation(m_pXref);
pAnnot->SetC({ 1.0, 0.8, 0.0 });
}
else if (m_nType == 14)
pAnnot = new CInkAnnotation(m_pXref);
else if (m_nType == 3)
pAnnot = new CLineAnnotation(m_pXref);
else if (m_nType >= 8 && m_nType <= 11)
pAnnot = new CTextMarkupAnnotation(m_pXref);
else if (m_nType == 4 || m_nType == 5)
pAnnot = new CSquareCircleAnnotation(m_pXref);
else if (m_nType == 6 || m_nType == 7)
pAnnot = new CPolygonLineAnnotation(m_pXref);
else if (m_nType == 15)
pAnnot = new CPopupAnnotation(m_pXref);
else if (m_nType == 2)
pAnnot = new CFreeTextAnnotation(m_pXref);
else if (m_nType == 13)
pAnnot = new CCaretAnnotation(m_pXref);
else if (m_nType == 12)
pAnnot = new CStampAnnotation(m_pXref);
else if (m_nType == 25)
pAnnot = new CRedactAnnotation(m_pXref);
if (pAnnot)
m_pXref->Add(pAnnot);
if (m_nType >= 26)
{
if (!CheckAcroForm())
return NULL;
switch (m_nType)
{
case 26:
{
pAnnot = new CWidgetAnnotation(m_pXref, EAnnotType::AnnotWidget);
break;
}
case 27:
{
pAnnot = new CPushButtonWidget(m_pXref);
pAnnot->Add("FT", "Btn");
break;
}
case 28:
case 29:
{
pAnnot = new CCheckBoxWidget(m_pXref);
pAnnot->Add("FT", "Btn");
break;
}
case 30:
{
pAnnot = new CTextWidget(m_pXref);
pAnnot->Add("FT", "Tx");
break;
}
case 31:
case 32:
{
pAnnot = new CChoiceWidget(m_pXref);
pAnnot->Add("FT", "Ch");
break;
}
case 33:
{
pAnnot = new CSignatureWidget(m_pXref);
pAnnot->Add("FT", "Sig");
break;
}
default: break;
}
if (pAnnot)
{
m_pXref->Add(pAnnot);
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pAnnot);
}
}
return pAnnot;
}
CAnnotation* CDocument::CreateLinkAnnot(const TRect& oRect, CDestination* pDest)
{
CAnnotation* pAnnot = new CLinkAnnotation(m_pXref, pDest);
pAnnot->SetRect(oRect);
m_pXref->Add(pAnnot);
return pAnnot;
}
CAnnotation* CDocument::CreateUriLinkAnnot(const TRect& oRect, const char* sUrl)
{
CAnnotation* pAnnot = new CUriLinkAnnotation(m_pXref, sUrl);
pAnnot->SetRect(oRect);
m_pXref->Add(pAnnot);
return pAnnot;
}
CAction* CDocument::CreateAction(BYTE nType)
{
switch (nType)
{
case 1: return new CActionGoTo(m_pXref);
case 6: return new CActionURI(m_pXref);
case 9: return new CActionHide(m_pXref);
case 10: return new CActionNamed(m_pXref);
case 12: return new CActionResetForm(m_pXref);
case 14: return new CActionJavaScript(m_pXref);
}
return NULL;
}
void CDocument::AddAnnotation(const int& nID, CAnnotation* pAnnot)
{
pAnnot->SetXref(m_pXref);
m_mAnnotations[nID] = pAnnot;
}
CImageDict* CDocument::CreateImage()
{
return new CImageDict(m_pXref, this);
}
CXObject* CDocument::CreateForm(CImageDict* pImage, const std::string& sName)
{
if (!pImage)
return NULL;
std::string sFrmName = "FRM" + sName;
std::string sImgName = "Img" + sName;
CXObject* pForm = new CXObject();
CStream* pStream = new CMemoryStream();
pForm->SetStream(m_pXref, pStream);
#ifndef FILTER_FLATE_DECODE_DISABLED
if (m_unCompressMode & COMP_TEXT)
pForm->SetFilter(STREAM_FILTER_FLATE_DECODE);
#endif
double dOriginW = pImage->GetWidth();
double dOriginH = pImage->GetHeight();
pForm->SetWidth(dOriginW);
pForm->SetHeight(dOriginH);
CArrayObject* pBBox = new CArrayObject();
pForm->Add("BBox", pBBox);
pBBox->Add(0);
pBBox->Add(0);
pBBox->Add(dOriginW);
pBBox->Add(dOriginH);
pForm->Add("FormType", 1);
CArrayObject* pFormMatrix = new CArrayObject();
pForm->Add("Matrix", pFormMatrix);
pFormMatrix->Add(1);
pFormMatrix->Add(0);
pFormMatrix->Add(0);
pFormMatrix->Add(1);
pFormMatrix->Add(0);
pFormMatrix->Add(0);
pForm->Add("Name", sFrmName.c_str());
pForm->SetName(sFrmName);
CDictObject* pFormRes = new CDictObject();
CArrayObject* pFormResProcset = new CArrayObject();
pFormRes->Add("ProcSet", pFormResProcset);
pFormResProcset->Add(new CNameObject("PDF"));
pFormResProcset->Add(new CNameObject("ImageC"));
CDictObject* pFormResXObject = new CDictObject();
pFormRes->Add("XObject", pFormResXObject);
pFormResXObject->Add(sImgName, pImage);
pForm->Add("Resources", pFormRes);
pForm->Add("Subtype", "Form");
pForm->Add("Type", "XObject");
pStream->WriteStr("q\012");
pStream->WriteReal(dOriginW);
pStream->WriteStr(" 0 0 ");
pStream->WriteReal(dOriginH);
pStream->WriteStr(" 0 0 cm\012/");
pStream->WriteStr(sImgName.c_str());
pStream->WriteStr(" Do\012Q");
return pForm;
}
CFont14* CDocument::CreateFont14(const std::wstring& wsFontPath, unsigned int unIndex, EStandard14Fonts eType)
{
CFont14* pFont = FindFont14(wsFontPath, unIndex);
if (pFont)
return pFont;
pFont = new CFont14(m_pXref, this, eType);
m_vFonts14.push_back(TFontInfo(wsFontPath, unIndex, pFont));
return pFont;
}
CFont14* CDocument::FindFont14(const std::wstring& wsFontPath, unsigned int unIndex)
{
for (int nIndex = 0, nCount = m_vFonts14.size(); nIndex < nCount; nIndex++)
{
TFontInfo& oInfo = m_vFonts14.at(nIndex);
if (wsFontPath == oInfo.wsPath && unIndex == oInfo.unIndex)
return (CFont14*)oInfo.pFont;
}
return NULL;
}
CFontCidTrueType* CDocument::CreateCidTrueTypeFont(const std::wstring& wsFontPath, unsigned int unIndex)
{
CFontCidTrueType* pFont = FindCidTrueTypeFont(wsFontPath, unIndex);
if (pFont)
return pFont;
CFontFileTrueType* pFontTT = CFontFileTrueType::LoadFromFile(wsFontPath, unIndex);
if (!pFontTT)
return NULL;
pFont = new CFontCidTrueType(m_pXref, this, wsFontPath, unIndex, pFontTT);
if (!pFont)
return NULL;
// 0 GID всегда используется для .notdef символа, не используем данный код для настоящих символов
unsigned int unUnicode = 0;
pFont->EncodeGID(0, &unUnicode, 1);
m_vCidTTFonts.push_back(TFontInfo(wsFontPath, unIndex, pFont));
return pFont;
}
CFontCidTrueType* CDocument::FindCidTrueTypeFont(const std::wstring &wsFontPath, unsigned int unIndex)
{
for (int nIndex = 0, nCount = m_vCidTTFonts.size(); nIndex < nCount; nIndex++)
{
TFontInfo& oInfo = m_vCidTTFonts.at(nIndex);
if (wsFontPath == oInfo.wsPath && unIndex == oInfo.unIndex)
return (CFontCidTrueType*)oInfo.pFont;
}
return NULL;
}
CFontTrueType* CDocument::CreateTrueTypeFont(const std::wstring& wsFontPath, unsigned int unIndex)
{
for (int nIndex = 0, nCount = m_vTTFonts.size(); nIndex < nCount; nIndex++)
{
TFontInfo& oInfo = m_vTTFonts.at(nIndex);
if (wsFontPath == oInfo.wsPath && unIndex == oInfo.unIndex)
return (CFontTrueType*)oInfo.pFont;
}
CFontTrueType* pFont = new CFontTrueType(m_pXref, this, wsFontPath, unIndex);
if (!pFont)
return NULL;
m_vTTFonts.push_back(TFontInfo(wsFontPath, unIndex, pFont));
return pFont;
}
CFontTrueType* CDocument::CreateTrueTypeFont(CFontCidTrueType* pCidFont)
{
for (int nIndex = 0, nCount = m_vCidTTFonts.size(); nIndex < nCount; nIndex++)
{
TFontInfo& oInfo = m_vCidTTFonts.at(nIndex);
if (pCidFont == (CFontCidTrueType*)oInfo.pFont)
{
return CreateTrueTypeFont(oInfo.wsPath, oInfo.unIndex);
}
}
return NULL;
}
CFont14* CDocument::GetDefaultCheckboxFont()
{
if (!m_pDefaultCheckBoxFont)
m_pDefaultCheckBoxFont = new CFont14(m_pXref, this, EStandard14Fonts::standard14fonts_ZapfDingbats);
return m_pDefaultCheckBoxFont;
}
char* CDocument::GetTTFontTag()
{
if (0 == m_sTTFontTag[0])
{
MemCpy((BYTE*)m_sTTFontTag, (BYTE*)"BAAAAA+", 7);
}
else
{
for (unsigned int nIndex = 0; nIndex <= 5; nIndex++)
{
m_sTTFontTag[nIndex] += 1;
if (m_sTTFontTag[nIndex] > 'Z')
m_sTTFontTag[nIndex] = 'A';
else
break;
}
}
return m_sTTFontTag;
}
void CDocument::AddFreeTypeFont(CFontCidTrueType* pFont)
{
for (int nIndex = 0, nCount = m_vFreeTypeFonts.size(); nIndex < nCount; nIndex++)
{
if (pFont == m_vFreeTypeFonts.at(nIndex))
{
if (nIndex >= 10)
{
m_vFreeTypeFonts.erase(m_vFreeTypeFonts.begin() + nIndex);
m_vFreeTypeFonts.insert(m_vFreeTypeFonts.begin(), pFont);
}
return;
}
}
m_vFreeTypeFonts.insert(m_vFreeTypeFonts.begin(), pFont);
int nFontsCount = m_vFreeTypeFonts.size();
if (nFontsCount > MAX_OPENED_FT_FACES)
{
for (int nFontIndex = MAX_OPENED_FT_FACES; nFontIndex < nFontsCount; nFontIndex++)
{
CFontCidTrueType* pFont = m_vFreeTypeFonts.at(nFontIndex);
pFont->CloseFontFace();
}
m_vFreeTypeFonts.erase(m_vFreeTypeFonts.begin() + MAX_OPENED_FT_FACES, m_vFreeTypeFonts.end());
}
}
FT_Library CDocument::GetFreeTypeLibrary()
{
if (!m_pFreeTypeLibrary)
FT_Init_FreeType(&m_pFreeTypeLibrary);
return m_pFreeTypeLibrary;
}
CJbig2Global* CDocument::GetJbig2Global()
{
if (m_pJbig2 && m_pJbig2->GetImagesCount() > 4)
{
// Удалять не надо, т.к. объект удалится в CXref
m_pJbig2->FlushStreams();
m_pJbig2 = NULL;
}
if (!m_pJbig2)
m_pJbig2 = new CJbig2Global(m_pXref);
return m_pJbig2;
}
CShading* CDocument::CreateShading(CPage* pPage, double *pPattern, bool bAxial, unsigned char* pColors, unsigned char* pAlphas, double* pPoints, int nCount, CExtGrState*& pExtGrState)
{
pExtGrState = NULL;
bool bNeedAlpha = false;
unsigned char* pA = new unsigned char[3 * nCount];
if (!pA)
return NULL;
for (int nIndex = 0; nIndex < nCount; nIndex++)
{
pA[3 * nIndex + 0] = pAlphas[nIndex];
pA[3 * nIndex + 1] = pAlphas[nIndex];
pA[3 * nIndex + 2] = pAlphas[nIndex];
if (255 != pAlphas[nIndex])
bNeedAlpha = true;
}
if (!bNeedAlpha)
{
delete[] pA;
if (bAxial)
return CreateAxialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pColors, pPoints, nCount);
else
return CreateRadialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pPattern[4], pPattern[5], pColors, pPoints, nCount);
}
// Создаем 2 shading-объекта, один цветной RGB, второй серый со значениями альфа-канала
CShading* pColorShading = NULL;
CShading* pAlphaShading = NULL;
if (bAxial)
{
pColorShading = CreateAxialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pColors, pPoints, nCount);
pAlphaShading = CreateAxialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pA, pPoints, nCount);
}
else
{
pColorShading = CreateRadialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pPattern[4], pPattern[5], pColors, pPoints, nCount);
pAlphaShading = CreateRadialShading(pPattern[0], pPattern[1], pPattern[2], pPattern[3], pPattern[4], pPattern[5], pA, pPoints, nCount);
}
delete[] pA;
if (!IsPDFA())
{
if (!m_pTransparencyGroup)
{
m_pTransparencyGroup = new CDictObject();
m_pTransparencyGroup->Add("Type", "Group");
m_pTransparencyGroup->Add("S", "Transparency");
m_pTransparencyGroup->Add("CS", "DeviceRGB");
}
pPage->Add("Group", m_pTransparencyGroup);
}
double dWidth = pPage->GetWidth();
double dHeight = pPage->GetHeight();
// Создаем графический объект, который будет альфа-маской
CDictObject* pXObject = new CDictObject(m_pXref);
pXObject->Add("Type", "XObject");
pXObject->Add("Subtype", "Form");
pXObject->Add("BBox", CArrayObject::CreateBox(0, 0, dWidth, dHeight));
if (m_pTransparencyGroup)
pXObject->Add("Group", m_pTransparencyGroup);
CDictObject* pResources = new CDictObject();
pXObject->Add("Resources", pResources);
CDictObject* pResShadings = new CDictObject();
pResources->Add("Shading", pResShadings);
pResShadings->Add("S1", pAlphaShading);
CStream* pStream = pXObject->GetStream();
pStream->WriteStr("0 0 ");
pStream->WriteReal(dWidth);
pStream->WriteChar(' ');
pStream->WriteReal(dHeight);
pStream->WriteStr(" re\012W\012\n\012/S1 sh\012");
// Создаем обект-маску для графического состояние
CDictObject* pMask = new CDictObject();
m_pXref->Add(pMask);
pMask->Add("Type", "Mask");
pMask->Add("S", "Luminosity");
pMask->Add("G", pXObject);
if (!IsPDFA())
{
// Создаем ExtGState объект, в который мы запишем альфа-маску
pExtGrState = new CExtGrState(m_pXref);
pExtGrState->Add("BM", "Normal");
pExtGrState->Add("ca", 1);
pExtGrState->Add("SMask", pMask);
}
return pColorShading;
}
CShading* CDocument::CreateAxialShading(double dX0, double dY0, double dX1, double dY1, unsigned char* pColors, double* pPoints, int nCount)
{
for (int nIndex = 0, nShadingsCount = m_vShadings.size(); nIndex < nShadingsCount; nIndex++)
{
CShading* pShading = m_vShadings.at(nIndex);
if (shadingtype_Axial == pShading->GetShadingType()
&& ((CAxialShading*)pShading)->Compare(dX0, dY0, dX1, dY1)
&& pShading->CompareColors(pColors, pPoints, nCount, true)
&& pShading->CompareExtend(true, true))
return pShading;
}
CAxialShading* pShading = new CAxialShading(m_pXref, dX0, dY0, dX1, dY1);
if (!pShading)
return NULL;
pShading->SetRgbColors(pColors, pPoints, nCount);
pShading->SetExtend(true, true);
m_vShadings.push_back(pShading);
return pShading;
}
CShading* CDocument::CreateRadialShading(double dX0, double dY0, double dR0, double dX1, double dY1, double dR1, unsigned char* pColors, double* pPoints, int nCount)
{
for (int nIndex = 0, nShadingsCount = m_vShadings.size(); nIndex < nShadingsCount; nIndex++)
{
CShading* pShading = m_vShadings.at(nIndex);
if (shadingtype_Radial == pShading->GetShadingType()
&& ((CRadialShading*)pShading)->Compare(dX0, dY0, dR0, dX1, dY1, dR1)
&& pShading->CompareColors(pColors, pPoints, nCount, true)
&& pShading->CompareExtend(true, true))
return pShading;
}
CRadialShading* pShading = new CRadialShading(m_pXref, dX0, dY0, dR0, dX1, dY1, dR1);
if (!pShading)
return NULL;
pShading->SetRgbColors(pColors, pPoints, nCount);
pShading->SetExtend(true, true);
m_vShadings.push_back(pShading);
return pShading;
}
CImageTilePattern*CDocument::CreateImageTilePattern(double dW, double dH, CImageDict* pImageDict, CMatrix* pMatrix, EImageTilePatternType eType, double dXStepSpacing, double dYStepSpacing)
{
return new CImageTilePattern(m_pXref, dW, dH, pImageDict, pMatrix, eType, dXStepSpacing, dYStepSpacing);
}
CImageTilePattern*CDocument::CreateHatchPattern(double dW, double dH, const BYTE& nR1, const BYTE& nG1, const BYTE& nB1, const BYTE& nAlpha1, const BYTE& nR2, const BYTE& nG2, const BYTE& nB2, const BYTE& nAlpha2, const std::wstring& wsHatch)
{
// TODO: Надо бы сделать мап, чтобы не создавать одинаковых паттернов
CImageDict* pImage = CreateImage();
BYTE* pBuffer = new BYTE[3 * HATCH_TX_SIZE * HATCH_TX_SIZE];
if (!pBuffer)
return NULL;
TColor oColor1(nR1, nG1, nB1);
TColor oColor2(nR2, nG2, nB2);
agg::GetHatchPattern<TColor>(wsHatch, (TColor*)pBuffer, oColor1, oColor2);
pImage->LoadRaw(pBuffer, 3 * HATCH_TX_SIZE * HATCH_TX_SIZE, HATCH_TX_SIZE, HATCH_TX_SIZE);
delete[] pBuffer;
if (255 != nAlpha1 || 255 != nAlpha2)
{
BYTE* pSMask = new BYTE[HATCH_TX_SIZE * HATCH_TX_SIZE];
if (pSMask)
{
agg::GetHatchPattern<BYTE>(wsHatch, pSMask, nAlpha1, nAlpha2);
pImage->LoadSMask(pSMask, (unsigned int)HATCH_TX_SIZE * HATCH_TX_SIZE, (unsigned int)HATCH_TX_SIZE, (unsigned int)HATCH_TX_SIZE);
delete[] pSMask;
}
}
return CreateImageTilePattern(dW, dH, pImage, NULL, imagetilepatterntype_Default);
}
CShading* CDocument::CreateAxialShading(CPage* pPage, double dX0, double dY0, double dX1, double dY1, unsigned char* pColors, unsigned char* pAlphas, double* pPoints, int nCount, CExtGrState*& pExtGrState)
{
double pPattern[] ={ dX0, dY0, dX1, dY1 };
return CreateShading(pPage, pPattern, true, pColors, pAlphas, pPoints, nCount, pExtGrState);
}
CShading* CDocument::CreateRadialShading(CPage* pPage, double dX0, double dY0, double dR0, double dX1, double dY1, double dR1, unsigned char* pColors, unsigned char* pAlphas, double* pPoints, int nCount, CExtGrState*& pExtGrState)
{
double pPattern[] ={ dX0, dY0, dR0, dX1, dY1, dR1 };
return CreateShading(pPage, pPattern, false, pColors, pAlphas, pPoints, nCount, pExtGrState);
}
CResourcesDict* CDocument::GetFieldsResources()
{
if (!m_pFieldsResources)
{
if (!CheckAcroForm())
return NULL;
m_pFieldsResources = new CResourcesDict(m_pXref, false, true);
m_pAcroForm->Add("DR", m_pFieldsResources);
}
return m_pFieldsResources;
}
CTextField* CDocument::CreateTextField()
{
if (!CheckAcroForm())
return NULL;
CTextField* pField = new CTextField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
CChoiceField* CDocument::CreateChoiceField()
{
if (!CheckAcroForm())
return NULL;
CChoiceField* pField = new CChoiceField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
CSignatureField* CDocument::CreateSignatureField()
{
if (!CheckAcroForm())
return NULL;
CSignatureField* pField = new CSignatureField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
CDateTimeField* CDocument::CreateDateTimeField()
{
if (!CheckAcroForm())
return NULL;
CDateTimeField* pField = new CDateTimeField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
CCheckBoxField* CDocument::CreateCheckBoxField()
{
if (!CheckAcroForm())
return NULL;
CCheckBoxField* pField = new CCheckBoxField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
CRadioGroupField* CDocument::GetRadioGroupField(const std::wstring& wsGroupName)
{
CRadioGroupField* pField = FindRadioGroupField(wsGroupName);
if (!pField)
{
if (!CheckAcroForm())
return NULL;
pField = new CRadioGroupField(m_pXref, this);
if (!pField)
return NULL;
m_vRadioGroups.push_back(pField);
pField->SetFieldName(wsGroupName);
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
}
return pField;
}
CRadioGroupField* CDocument::FindRadioGroupField(const std::wstring& wsGroupName)
{
CRadioGroupField* pField = NULL;
for (unsigned int unIndex = 0, unCount = m_vRadioGroups.size(); unIndex < unCount; ++unIndex)
{
pField = m_vRadioGroups.at(unIndex);
if (pField->GetFieldName() == wsGroupName)
return pField;
}
return NULL;
}
CPictureField* CDocument::CreatePictureField()
{
if (!CheckAcroForm())
return NULL;
CPictureField* pField = new CPictureField(m_pXref, this);
if (!pField)
return NULL;
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
ppFields->Add(pField);
return pField;
}
bool CDocument::HasImage(const std::wstring& wsImagePath, BYTE nAlpha)
{
for (size_t i = 0, nSize = m_vImages.size(); i < nSize; ++i)
{
if (m_vImages[i].wsImagePath == wsImagePath && m_vImages[i].nAlpha == nAlpha)
return true;
}
return false;
}
CImageDict* CDocument::GetImage(const std::wstring& wsImagePath, BYTE nAlpha)
{
for (size_t i = 0, nSize = m_vImages.size(); i < nSize; ++i)
{
if (m_vImages[i].wsImagePath == wsImagePath && m_vImages[i].nAlpha == nAlpha)
{
m_pCurImage = m_vImages[i].pImage;
return m_vImages[i].pImage;
}
}
return NULL;
}
void CDocument::AddImage(const std::wstring& wsImagePath, BYTE nAlpha, CImageDict* pImage)
{
if (!pImage)
return;
m_pCurImage = pImage;
m_vImages.push_back({wsImagePath, nAlpha, pImage});
}
void CDocument::AddObject(CObjectBase* pObj)
{
m_pXref->Add(pObj);
}
void CDocument::RemoveObj(CObjectBase* pObj)
{
std::map<int, CAnnotation*>::iterator it1 = std::find_if(m_mAnnotations.begin(), m_mAnnotations.end(), [pObj](const std::pair<int, CAnnotation*>& t){ return t.second == pObj; });
if (it1 != m_mAnnotations.end())
m_mAnnotations.erase(it1);
std::map<int, CPage*>::iterator it2 = std::find_if(m_mEditPages.begin(), m_mEditPages.end(), [pObj](const std::pair<int, CPage*>& t){ return t.second == pObj; });
if (it2 != m_mEditPages.end())
m_mEditPages.erase(it2);
if (m_pCurPage == pObj)
m_pCurPage = NULL;
m_pXref->Remove(pObj);
}
bool CDocument::CheckFieldName(CFieldBase* pField, const std::string& sName)
{
CFieldBase* pBase = m_mFields[sName];
if (pBase)
{
if (!pBase->GetKidsCount())
{
CFieldBase* pParent = new CFieldBase(m_pXref, this);
pParent->SetFieldName(sName, true);
pParent->Add("Ff", pBase->GetFieldFlag());
pParent->Add("FT", pBase->GetFieldType());
CObjectBase* pT = pBase->Get("T");
if (pT && pT->GetType() == object_type_STRING)
pParent->Add("T", pT->Copy());
CObjectBase* pV = pBase->Get("V");
if (pV && pV->GetType() == object_type_STRING)
pParent->Add("V", pV->Copy());
CObjectBase* pAA = pBase->Get("AA");
if (pAA)
pParent->Add("AA", pAA->Copy());
CTextField* pTextField = dynamic_cast<CTextField*>(pBase);
int nMaxLen = 0;
if (pTextField && 0 != (nMaxLen = pTextField->GetMaxLen()))
{
pBase->Remove("MaxLen");
pParent->Add("MaxLen", nMaxLen);
}
pBase->SetParent(pParent);
pBase->ClearKidRecords();
pParent->AddKid(pBase);
m_mFields[sName] = pParent;
pField->ClearKidRecords();
pField->SetParent(pParent);
pParent->AddKid(pField);
CChoiceField* pChoice = dynamic_cast<CChoiceField*>(pBase);
if (pChoice)
pChoice->UpdateSelectedIndexToParent();
pParent->UpdateKidsPlaceHolder();
}
else
{
pField->ClearKidRecords();
pField->SetParent(pBase);
pBase->AddKid(pField);
CChoiceField* pChoice = dynamic_cast<CChoiceField*>(pBase);
if (pChoice)
pChoice->UpdateSelectedIndexToParent();
pBase->UpdateKidsPlaceHolder();
}
return true;
}
else
{
m_mFields[sName] = pField;
return false;
}
}
bool CDocument::CheckAcroForm()
{
if (!m_pXref || !m_pCatalog)
return false;
if (!m_pAcroForm)
{
m_pAcroForm = new CDictObject();
if (!m_pAcroForm)
return false;
m_pCatalog->Add("AcroForm", m_pAcroForm);
m_pAcroForm->Add("Fields", new CArrayObject());
}
return (!!m_pAcroForm);
}
void CDocument::SetAcroForm(CDictObject* pObj)
{
if (!m_pXref || !m_pCatalog)
return;
m_pCatalog->Add("AcroForm", pObj);
m_pAcroForm = pObj;
}
CResourcesDict* CDocument::CreateResourcesDict(bool bInline, bool bProcSet)
{
return new CResourcesDict(m_pXref, bInline, bProcSet);
}
bool CDocument::CreatePageTree(CXref* pXref, CPageTree* pPageTree)
{
if (!pPageTree || !EditXref(pXref))
return false;
if (!m_pPageTree)
m_pPageTree = pPageTree;
else
m_pPageTree->Join(pPageTree);
return true;
}
bool CDocument::EditPdf(int nPosLastXRef, int nSizeXRef, CXref* pXref, CCatalog* pCatalog, CEncryptDict* pEncrypt, int nFormField)
{
if (!pXref || !pCatalog)
return false;
Close();
m_pXref = new CXref(this, nSizeXRef);
if (!m_pXref)
return false;
m_pXref->SetPrevAddr(nPosLastXRef);
m_pLastXref = m_pXref;
m_pTrailer = m_pXref->GetTrailer();
if (!m_pTrailer)
return false;
SetCompressionMode(COMP_ALL);
m_pCatalog = pCatalog;
pXref->SetPrev(m_pLastXref);
m_pLastXref = pXref;
CObjectBase* pAcroForm = m_pCatalog->Get("AcroForm");
if (pAcroForm && pAcroForm->GetType() == object_type_DICT)
m_pAcroForm = (CDictObject*)pAcroForm;
if (pEncrypt)
{
m_pEncryptDict = pEncrypt;
m_bEncrypt = true;
}
m_unFormFields = nFormField;
return true;
}
bool CDocument::EditResources(CXref* pXref, CResourcesDict* pResources)
{
if (!pResources || !EditXref(pXref))
return false;
CheckAcroForm();
m_pAcroForm->Add("DR", pResources);
m_pFieldsResources = pResources;
return true;
}
std::pair<int, int> CDocument::GetPageRef(int nPageIndex)
{
std::pair<int, int> pRes = std::make_pair(0, 0);
if (!m_pPageTree)
return pRes;
CObjectBase* pObj = m_pPageTree->GetObj(nPageIndex);
if (pObj)
{
pRes.first = pObj->GetObjId();
pRes.second = pObj->GetGenNo();
}
return pRes;
}
bool CDocument::EditPage(CXref* pXref, CPage* pPage, int nPageIndex)
{
if (!pPage || !EditXref(pXref))
return false;
pPage->AddContents(m_pXref);
#ifndef FILTER_FLATE_DECODE_DISABLED
if (m_unCompressMode & COMP_TEXT)
pPage->SetFilter(STREAM_FILTER_FLATE_DECODE);
#endif
m_pCurPage = pPage;
m_mEditPages[nPageIndex] = pPage;
if (m_pPageTree)
m_pPageTree->ReplacePage(nPageIndex, pPage);
return true;
}
void CDocument::FixEditPage(CPage* _pPage, int nPageIndex)
{
CPage* pPage = _pPage ? _pPage : m_mEditPages[nPageIndex];
if (!pPage)
return;
pPage->AddContents(m_pXref);
#ifndef FILTER_FLATE_DECODE_DISABLED
if (m_unCompressMode & COMP_TEXT)
pPage->SetFilter(STREAM_FILTER_FLATE_DECODE);
#endif
}
void CDocument::AddEditPage(CPage* pPage, int nPageIndex)
{
m_mEditPages[nPageIndex] = pPage;
}
bool CDocument::EditAnnot(CXref* pXref, CAnnotation* pAnnot, int nID)
{
if (!pAnnot || !EditXref(pXref))
return false;
pAnnot->SetXref(m_pXref);
m_mAnnotations[nID] = pAnnot;
return true;
}
void CDocument::AddParent(int nID, CDictObject* pParent)
{
m_mParents[nID] = pParent;
}
CDictObject* CDocument::CreateParent(int nID)
{
CDictObject* pParent = new CDictObject();
m_pXref->Add(pParent);
m_mParents[nID] = pParent;
return pParent;
}
bool CDocument::EditParent(CXref* pXref, CDictObject* pParent, int nID)
{
if (!pParent || !EditXref(pXref))
return false;
m_mParents[nID] = pParent;
return true;
}
bool CDocument::EditXref(CXref* pXref)
{
if (!pXref)
return true;
pXref->SetPrev(m_pLastXref);
m_pLastXref = pXref;
return true;
}
bool CDocument::DeleteAnnot(int nObjNum, int nObjGen)
{
if (m_pCurPage && m_pCurPage->DeleteAnnotation(nObjNum))
{
if (m_pAcroForm)
{
CArrayObject* ppFields = (CArrayObject*)m_pAcroForm->Get("Fields");
for (int i = 0; i < ppFields->GetCount(); ++i)
{
CObjectBase* pObj = ppFields->Get(i);
if (pObj->GetObjId() == nObjNum)
{
CObjectBase* pDelete = ppFields->Remove(i);
RELEASEOBJECT(pDelete);
break;
}
}
}
CXref* pXref = new CXref(this, nObjNum, nObjGen);
if (!pXref)
return false;
pXref->SetPrev(m_pLastXref);
m_pLastXref = pXref;
return true;
}
return false;
}
CAnnotation* CDocument::GetAnnot(int nID)
{
std::map<int, CAnnotation*>::iterator p = m_mAnnotations.find(nID);
if (p != m_mAnnotations.end())
return p->second;
return NULL;
}
CDictObject* CDocument::GetParent(int nID)
{
std::map<int, CDictObject*>::iterator p = m_mParents.find(nID);
if (p != m_mParents.end())
return p->second;
return NULL;
}
std::string CDocument::SetParentKids(int nParentID)
{
CDictObject* pParent = GetParent(nParentID);
if (!pParent)
return "";
for (auto it = m_mAnnotations.begin(); it != m_mAnnotations.end(); it++)
{
CAnnotation* pAnnot = it->second;
if (pAnnot->GetAnnotationType() != AnnotWidget)
continue;
CWidgetAnnotation* pWidget = (CWidgetAnnotation*)pAnnot;
int nWidgetParentID = pWidget->GetParentID();
if (nWidgetParentID != nParentID)
continue;
pWidget->SetParent(pParent);
CObjectBase* pFT = pParent->Get("FT");
CObjectBase* pWidgetFT = pWidget->Get("FT");
if (!pFT && pParent->Get("T") && pWidgetFT)
pParent->Add("FT", pWidgetFT->Copy());
CArrayObject* pKids = dynamic_cast<CArrayObject*>(pParent->Get("Kids"));
if (!pKids)
{
pKids = new CArrayObject();
pParent->Add("Kids", pKids);
}
bool bReplase = false;
int nID = pWidget->GetObjId();
if (nID > 0)
{
for (int i = 0; i < pKids->GetCount(); ++i)
{
CObjectBase* pKid = pKids->Get(i);
if (pKid->GetObjId() == nID)
{
pKids->Insert(pKid, pWidget, true);
bReplase = true;
break;
}
}
}
if (!bReplase)
pKids->Add(pWidget);
}
CObjectBase* pFT = pParent->Get("FT");
if (pFT && pFT->GetType() == object_type_NAME)
return ((CNameObject*)pFT)->Get();
return "";
}
bool CDocument::EditCO(const std::vector< std::pair<int, int> >& arrCO)
{
if (arrCO.empty())
return true;
if (!CheckAcroForm())
return false;
CArrayObject* pArray = new CArrayObject();
if (!pArray)
return false;
m_pAcroForm->Add("CO", pArray);
for (std::pair<int, int> CO : arrCO)
{
CDictObject* pObj = GetParent(CO.first);
if (pObj)
pArray->Add(pObj);
else
{
CAnnotation* pAnnot = GetAnnot(CO.first);
if (pAnnot)
pArray->Add(pAnnot);
else if (CO.second >= 0)
{
PdfWriter::CObjectBase* pBase = new PdfWriter::CObjectBase();
pBase->SetRef(CO.first, CO.second);
pArray->Add(new PdfWriter::CProxyObject(pBase, true));
}
}
}
return true;
}
CPage* CDocument::AddPage(int nPageIndex, CPage* _pNewPage)
{
if (!m_pPageTree)
return NULL;
CPage* pNewPage = _pNewPage ? _pNewPage : new CPage(m_pXref, NULL, this);
if (!pNewPage)
return NULL;
bool bRes = m_pPageTree->InsertPage(nPageIndex, pNewPage);
if (!bRes)
return NULL;
if (!_pNewPage)
pNewPage->SetFilter(STREAM_FILTER_FLATE_DECODE);
m_pCurPage = pNewPage;
return pNewPage;
}
bool CDocument::DeletePage(int nPageIndex)
{
if (!m_pPageTree)
return false;
CObjectBase* pObj = m_pPageTree->RemovePage(nPageIndex);
if (pObj)
{
if (pObj->IsIndirect())
return true;
if (!pObj->GetObjId())
{
delete pObj;
return true;
}
CXref* pXref = new CXref(this, pObj->GetObjId(), pObj->GetGenNo());
delete pObj;
if (!pXref)
return false;
pXref->SetPrev(m_pLastXref);
m_pLastXref = pXref;
return true;
}
return false;
}
bool CDocument::MovePage(int nPageIndex, int nPos)
{
if (m_pPageTree)
{
CObjectBase* pObj = m_pPageTree->RemovePage(nPageIndex);
if (pObj)
{
if (pObj->GetType() == object_type_UNKNOWN)
{
CObjectBase* pObjTemp = pObj->Copy();
delete pObj;
pObj = pObjTemp;
}
return m_pPageTree->InsertPage(nPos, pObj);
}
}
return false;
}
bool CDocument::AddToFile(const std::wstring& wsPath, CXref* pXref, CDictObject* pTrailer, CXref* pInfoXref, CInfoDict* pInfo)
{
if (!pTrailer || wsPath.empty())
return false;
CFileStream* pStream = new CFileStream();
if (!pStream)
return false;
if (!pStream->OpenFile(wsPath, false))
{
RELEASEOBJECT(pStream);
return false;
}
m_pTrailer = pTrailer;
m_pInfo = pInfo;
if (!m_pInfo)
m_pInfo = new PdfWriter::CInfoDict(m_pXref);
std::wstring sCreator = NSSystemUtils::GetEnvVariable(NSSystemUtils::gc_EnvApplicationName);
if (sCreator.empty())
sCreator = NSSystemUtils::gc_EnvApplicationNameDefault;
std::string sCreatorA = U_TO_UTF8(sCreator);
#if defined(INTVER)
sCreatorA += "/";
sCreatorA += VALUE2STR(INTVER);
#endif
const char* cCreator = m_pInfo->GetInfo(InfoProducer);
m_pInfo->SetInfo(InfoCreator, cCreator ? cCreator : sCreatorA.c_str());
m_pInfo->SetInfo(InfoProducer, sCreatorA.c_str());
if (pInfoXref)
{
pInfoXref->SetPrev(m_pLastXref);
m_pLastXref = pInfoXref;
}
pXref->SetPrev(m_pLastXref);
m_pLastXref = pXref;
// Вторая часть идентификатора должна обновляться
CObjectBase* pID = m_pTrailer->Get("ID");
if ((pID && pID->GetType() == object_type_ARRAY) || !m_vMetaOForms.empty())
{
BYTE arrId[16];
CEncryptDict::CreateId(m_pInfo, m_pXref, (BYTE*)arrId);
CArrayObject* pArrID = (CArrayObject*)pID;
if (pArrID)
{
CObjectBase* pObject = pArrID->Get(1, false);
pArrID->Insert(pObject, new CBinaryObject(arrId, 16), true);
}
else
{
pArrID = new CArrayObject();
m_pTrailer->Add("ID", pArrID);
pArrID->Add(new CBinaryObject(arrId, 16));
pArrID->Add(new CBinaryObject(arrId, 16));
}
for (int i = 0; i < m_vMetaOForms.size(); ++i)
m_vMetaOForms[i]->Add("ID", new CBinaryObject(arrId, 16));
}
CEncrypt* pEncrypt = NULL;
if (m_bEncrypt)
pEncrypt = m_pEncryptDict->GetEncrypt();
// Если m_pTrailer поток перекрестных ссылок, то при дозаписи тоже должен быть поток
m_pTrailer->Remove("XRefStm");
bool bNeedStreamXRef = false;
pStream->WriteChar('\n');
if (m_pTrailer->Get("Type"))
{
m_pTrailer->Remove("Length");
m_pTrailer->Remove("Filter");
m_pTrailer->Remove("DecodeParms");
m_pTrailer->Remove("F");
m_pTrailer->Remove("FFilter");
m_pTrailer->Remove("FDecodeParms");
m_pTrailer->Remove("DL");
m_pTrailer->Remove("Type");
m_pTrailer->Remove("Index");
m_pTrailer->Remove("W");
bNeedStreamXRef = true;
m_pLastXref->WriteToStream(pStream, pEncrypt, bNeedStreamXRef);
}
else
m_pLastXref->WriteToStream(pStream, pEncrypt);
RELEASEOBJECT(pStream);
unsigned int nSizeXRef = m_pXref->GetSizeXRef();
m_pXref = m_pLastXref;
Sign(wsPath, nSizeXRef, bNeedStreamXRef);
RELEASEOBJECT(m_pEncryptDict);
return true;
}
void CDocument::Sign(const TRect& oRect, CImageDict* pImage, ICertificate* pCertificate)
{
m_vSignatures.push_back({ oRect, m_pCurPage ? m_pCurPage : m_pPageTree->GetPage(0), pImage, pCertificate });
}
void CDocument::Sign(const std::wstring& wsPath, unsigned int nSizeXRef, bool bNeedStreamXRef)
{
unsigned int nPrevAddr = m_pXref->GetPrevAddr();
std::vector<CXref*> vXRefForWrite;
for (unsigned int i = 0; i < m_vSignatures.size(); i++)
{
CXref* pXrefBefore = m_pXref;
m_pXref = new CXref(this, nSizeXRef);
if (!m_pXref)
{
m_pXref = pXrefBefore;
continue;
}
m_pXref->SetPrevAddr(nPrevAddr);
CSignatureField* pField = CreateSignatureField();
if (!pField)
{
RELEASEOBJECT(m_pXref);
m_pXref = pXrefBefore;
continue;
}
m_pAcroForm->Add("SigFlags", 3);
pField->GetSignatureDict()->SetCert(m_vSignatures[i].pCertificate);
pField->GetSignatureDict()->SetDate();
pField->AddPageRect(m_vSignatures[i].pPage, m_vSignatures[i].oRect);
pField->Add("F", 132);
pField->SetFieldName("Sig" + std::to_string(i + m_unFormFields + 1));
if (m_vSignatures[i].pImage)
pField->SetAppearance(m_vSignatures[i].pImage);
CFileStream* pStream = new CFileStream();
if (!pStream || !pStream->OpenFile(wsPath, false))
{
RELEASEOBJECT(m_pXref);
m_pXref = pXrefBefore;
continue;
}
CXref* pXrefCatalog = new CXref(this, m_pCatalog->GetObjId());
if (pXrefCatalog)
{
pXrefCatalog->Add(m_pCatalog->Copy(), m_pCatalog->GetGenNo());
pXrefCatalog->SetPrev(m_pXref);
}
CXref* pXrefPage = new CXref(this, m_vSignatures[i].pPage->GetObjId());
if (pXrefPage)
{
pXrefPage->Add(m_vSignatures[i].pPage->Copy(), m_vSignatures[i].pPage->GetGenNo());
pXrefPage->SetPrev(pXrefCatalog);
}
CXref* pXref = new CXref(this, 0, 65535);
if (pXref)
{
pXref->SetPrev(pXrefPage);
CDictObject* pTrailer = pXref->GetTrailer();
m_pTrailer->Copy(pTrailer);
CEncrypt* pEncrypt = NULL;
if (m_bEncrypt && m_pEncryptDict)
pEncrypt = m_pEncryptDict->GetEncrypt();
pXref->WriteToStream(pStream, pEncrypt, bNeedStreamXRef);
nPrevAddr = pXref->GetPrevAddr();
nSizeXRef = m_pXref->GetSizeXRef();
vXRefForWrite.push_back(pXref);
}
RELEASEOBJECT(pStream);
pStream = new CFileStream();
if (pStream && pStream->OpenFile(wsPath, false))
pField->GetSignatureDict()->WriteToStream(pStream, pStream->Size());
m_pXref = pXrefBefore;
RELEASEOBJECT(pStream);
}
for (CXref* XRef : vXRefForWrite)
RELEASEOBJECT(XRef);
vXRefForWrite.clear();
}
void CDocument::AddShapeXML(const std::string& sXML)
{
CDictObject* pResources = (CDictObject*)m_pCurPage->GetResourcesItem();
if (!pResources)
{
pResources = new CResourcesDict(NULL, true, false);
m_pCurPage->Add("Resources", pResources);
}
CDictObject* pProperties = (CDictObject*)pResources->Get("Properties");
if (!pProperties)
{
pProperties = new CDictObject();
pResources->Add("Properties", pProperties);
}
CObjectBase* pObj = pProperties->Get("OShapes");
if (pObj && pObj->GetType() != object_type_DICT)
{
pProperties->Remove("OShapes");
pObj = NULL;
}
CBinaryObject* sID = NULL;
CArrayObject* pID = (CArrayObject*)m_pTrailer->Get("ID");
if (!pID)
{
BYTE arrId[16];
CEncryptDict::CreateId(m_pInfo, m_pXref, (BYTE*)arrId);
pID = new CArrayObject();
m_pTrailer->Add("ID", pID);
pID->Add(new CBinaryObject(arrId, 16));
pID->Add(new CBinaryObject(arrId, 16));
sID = new CBinaryObject(arrId, 16);
}
else
sID = (CBinaryObject*)pID->Get(1)->Copy();
CDictObject* pMetaOForm = (CDictObject*)pObj;
if (!pMetaOForm)
{
pMetaOForm = new CDictObject();
m_pXref->Add(pMetaOForm);
pMetaOForm->Add("Type", "OShapes");
pProperties->Add("OShapes", pMetaOForm);
m_vMetaOForms.push_back(pMetaOForm);
pMetaOForm->Add("IDF", sID->Copy());
pMetaOForm->Add("ID", sID->Copy());
}
CArrayObject* pArrayMeta = (CArrayObject*)pMetaOForm->Get("Metadata");
if (!pArrayMeta)
{
pArrayMeta = new CArrayObject();
pMetaOForm->Add("Metadata", pArrayMeta);
CArrayObject* pArrayImage = new CArrayObject();
pMetaOForm->Add("Image", pArrayImage);
}
CStringObject* pXML = new CStringObject();
pXML->Set(sXML.c_str(), false, false, -1);
pArrayMeta->Add(pXML);
CDictObject* pBDC = new CDictObject();
pBDC->Add("MCID", pArrayMeta->GetCount() - 1);
pBDC->Add("IDF", sID);
m_pCurPage->BeginMarkedContentDict("OShapes", pBDC);
RELEASEOBJECT(pBDC);
}
void CDocument::EndShapeXML()
{
CDictObject* pResources = (CDictObject*)m_pCurPage->GetResourcesItem();
if (!pResources)
return;
CDictObject* pProperties = (CDictObject*)pResources->Get("Properties");
if (!pProperties)
return;
CObjectBase* pObj = pProperties->Get("OShapes");
if (!pObj || pObj->GetType() != object_type_DICT)
return;
CDictObject* pMetaOForm = (CDictObject*)pObj;
CArrayObject* pArrayImage = (CArrayObject*)pMetaOForm->Get("Image");
if (!pArrayImage)
return;
pObj = m_pCurImage;
if (!pObj)
pObj = new PdfWriter::CNullObject();
pArrayImage->Add(pObj);
m_pCurPage->EndMarkedContent();
}
void CDocument::ClearPage()
{
m_pCurPage->ClearContent(m_pXref);
m_pCurPage->StartTransform(1, 0, 0, 1, 0, 0);
}
void CDocument::ClearPageFull()
{
m_pCurPage->ClearContentFull(m_pXref);
}
}