1
0
mirror of https://github.com/ONLYOFFICE/core.git synced 2025-04-18 14:04:06 +03:00
core/PdfFile/PdfReader.cpp

1649 lines
47 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
*
*/
#define errMemory 12
#include "PdfReader.h"
#include "../DesktopEditor/graphics/IRenderer.h"
#include "../DesktopEditor/common/Directory.h"
#include "../DesktopEditor/common/StringExt.h"
#include "../DesktopEditor/graphics/pro/js/wasm/src/serialize.h"
#include "SrcReader/Adaptors.h"
#include "SrcReader/PdfAnnot.h"
#include "Resources/BaseFonts.h"
#include "lib/xpdf/PDFDoc.h"
#include "lib/xpdf/PDFCore.h"
#include "lib/xpdf/GlobalParams.h"
#include "lib/xpdf/ErrorCodes.h"
#include "lib/xpdf/TextString.h"
#include "lib/xpdf/Lexer.h"
#include "lib/xpdf/Parser.h"
#include "lib/xpdf/Outline.h"
#include "lib/xpdf/Link.h"
#include "lib/xpdf/TextOutputDev.h"
#include "lib/xpdf/AcroForm.h"
#include "lib/goo/GList.h"
#include <vector>
CPdfReader::CPdfReader(NSFonts::IApplicationFonts* pAppFonts)
{
m_pPDFDocument = NULL;
m_nFileLength = 0;
globalParams = new GlobalParamsAdaptor(NULL);
#ifndef _DEBUG
globalParams->setErrQuiet(gTrue);
#endif
m_pFontList = new PdfReader::CPdfFontList();
// Создаем менеджер шрифтов с собственным кэшем
m_pFontManager = pAppFonts->GenerateFontManager();
NSFonts::IFontsCache* pMeasurerCache = NSFonts::NSFontCache::Create();
pMeasurerCache->SetStreams(pAppFonts->GetStreams());
m_pFontManager->SetOwnerCache(pMeasurerCache);
pMeasurerCache->SetCacheSize(1);
((GlobalParamsAdaptor*)globalParams)->SetFontManager(m_pFontManager);
#ifndef BUILDING_WASM_MODULE
globalParams->setupBaseFonts(NULL);
SetCMapFile(NSFile::GetProcessDirectory() + L"/cmap.bin");
#else
globalParams->setDrawFormFields(gFalse);
globalParams->setDrawAnnotations(gFalse);
SetCMapMemory(NULL, 0);
#endif
m_eError = errNone;
}
CPdfReader::~CPdfReader()
{
if (m_pFontList)
{
m_pFontList->Clear();
delete m_pFontList;
}
if (!m_wsTempFolder.empty())
{
NSDirectory::DeleteDirectory(m_wsTempFolder);
m_wsTempFolder = L"";
}
RELEASEOBJECT(m_pPDFDocument);
RELEASEOBJECT(globalParams);
RELEASEINTERFACE(m_pFontManager);
}
bool scanFonts(Dict *pResources, const std::vector<std::string>& arrCMap, int nDepth, std::vector<int>& arrUniqueResources)
{
if (nDepth > 5)
return false;
Object oFonts;
if (pResources->lookup("Font", &oFonts)->isDict())
{
for (int i = 0, nLength = oFonts.dictGetLength(); i < nLength; ++i)
{
Object oFont, oEncoding;
if (!oFonts.dictGetVal(i, &oFont)->isDict() || !oFont.dictLookup("Encoding", &oEncoding)->isName())
{
oFont.free(); oEncoding.free();
continue;
}
oFont.free();
char* sName = oEncoding.getName();
if (std::find(arrCMap.begin(), arrCMap.end(), sName) != arrCMap.end())
{
oEncoding.free(); oFonts.free();
return true;
}
oEncoding.free();
}
}
oFonts.free();
auto fScanFonts = [pResources, nDepth, &arrUniqueResources](const std::vector<std::string>& arrCMap, const char* sName)
{
Object oObject;
if (!pResources->lookup(sName, &oObject)->isDict())
{
oObject.free();
return false;
}
for (int i = 0, nLength = oObject.dictGetLength(); i < nLength; ++i)
{
Object oXObj, oResources;
if (!oObject.dictGetVal(i, &oXObj)->isStream() || !oXObj.streamGetDict()->lookup("Resources", &oResources)->isDict())
{
oXObj.free(); oResources.free();
continue;
}
Object oRef;
if (oXObj.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end())
{
oXObj.free(); oResources.free(); oRef.free();
continue;
}
arrUniqueResources.push_back(oRef.getRef().num);
oXObj.free(); oRef.free();
if (scanFonts(oResources.getDict(), arrCMap, nDepth + 1, arrUniqueResources))
{
oResources.free(); oObject.free();
return true;
}
oResources.free();
}
oObject.free();
return false;
};
if (fScanFonts(arrCMap, "XObject") || fScanFonts(arrCMap, "Pattern"))
return true;
Object oExtGState;
if (!pResources->lookup("ExtGState", &oExtGState)->isDict())
{
oExtGState.free();
return false;
}
for (int i = 0, nLength = oExtGState.dictGetLength(); i < nLength; ++i)
{
Object oGS, oSMask, oSMaskGroup, oResources;
if (!oExtGState.dictGetVal(i, &oGS)->isDict() || !oGS.dictLookup("SMask", &oSMask)->isDict() || !oSMask.dictLookup("G", &oSMaskGroup)->isStream() || !oSMaskGroup.streamGetDict()->lookup("Resources", &oResources)->isDict())
{
oGS.free(); oSMask.free(); oSMaskGroup.free(); oResources.free();
continue;
}
oGS.free(); oSMask.free();
Object oRef;
if (oSMaskGroup.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end())
{
oSMaskGroup.free(); oResources.free(); oRef.free();
continue;
}
arrUniqueResources.push_back(oRef.getRef().num);
oSMaskGroup.free(); oRef.free();
if (scanFonts(oResources.getDict(), arrCMap, nDepth + 1, arrUniqueResources))
{
oResources.free(); oExtGState.free();
return true;
}
oResources.free();
}
oExtGState.free();
return false;
}
bool scanAPfonts(Object* oAnnot, const std::vector<std::string>& arrCMap, std::vector<int>& arrUniqueResources)
{
Object oAP;
if (!oAnnot->dictLookup("AP", &oAP)->isDict())
{
oAP.free();
return false;
}
auto fScanAPView = [&arrUniqueResources](Object* oAP, const std::vector<std::string>& arrCMap, const char* sName)
{
Object oAPi, oRes;
if (!oAP->dictLookup(sName, &oAPi)->isStream() || !oAPi.streamGetDict()->lookup("Resources", &oRes)->isDict())
{
oAPi.free(); oRes.free();
return false;
}
Object oRef;
if (oAPi.streamGetDict()->lookupNF("Resources", &oRef)->isRef() && std::find(arrUniqueResources.begin(), arrUniqueResources.end(), oRef.getRef().num) != arrUniqueResources.end())
{
oAPi.free(); oRes.free(); oRef.free();
return false;
}
arrUniqueResources.push_back(oRef.getRef().num);
oAPi.free(); oRef.free();
bool bRes = scanFonts(oRes.getDict(), arrCMap, 0, arrUniqueResources);
oRes.free();
return bRes;
};
bool bRes = fScanAPView(&oAP, arrCMap, "N") || fScanAPView(&oAP, arrCMap, "D") || fScanAPView(&oAP, arrCMap, "R");
oAP.free();
return bRes;
}
bool CPdfReader::IsNeedCMap()
{
std::vector<std::string> arrCMap = {"GB-EUC-H", "GB-EUC-V", "GB-H", "GB-V", "GBpc-EUC-H", "GBpc-EUC-V", "GBK-EUC-H",
"GBK-EUC-V", "GBKp-EUC-H", "GBKp-EUC-V", "GBK2K-H", "GBK2K-V", "GBT-H", "GBT-V", "GBTpc-EUC-H", "GBTpc-EUC-V",
"UniGB-UCS2-H", "UniGB-UCS2-V", "UniGB-UTF8-H", "UniGB-UTF8-V", "UniGB-UTF16-H", "UniGB-UTF16-V", "UniGB-UTF32-H",
"UniGB-UTF32-V", "B5pc-H", "B5pc-V", "B5-H", "B5-V", "HKscs-B5-H", "HKscs-B5-V", "HKdla-B5-H", "HKdla-B5-V",
"HKdlb-B5-H", "HKdlb-B5-V", "HKgccs-B5-H", "HKgccs-B5-V", "HKm314-B5-H", "HKm314-B5-V", "HKm471-B5-H",
"HKm471-B5-V", "ETen-B5-H", "ETen-B5-V", "ETenms-B5-H", "ETenms-B5-V", "ETHK-B5-H", "ETHK-B5-V", "CNS-EUC-H",
"CNS-EUC-V", "CNS1-H", "CNS1-V", "CNS2-H", "CNS2-V", "UniCNS-UCS2-H", "UniCNS-UCS2-V", "UniCNS-UTF8-H",
"UniCNS-UTF8-V", "UniCNS-UTF16-H", "UniCNS-UTF16-V", "UniCNS-UTF32-H", "UniCNS-UTF32-V", "78-EUC-H", "78-EUC-V",
"78-H", "78-V", "78-RKSJ-H", "78-RKSJ-V", "78ms-RKSJ-H", "78ms-RKSJ-V","83pv-RKSJ-H", "90ms-RKSJ-H", "90ms-RKSJ-V",
"90msp-RKSJ-H", "90msp-RKSJ-V", "90pv-RKSJ-H", "90pv-RKSJ-V", "Add-H", "Add-V", "Add-RKSJ-H", "Add-RKSJ-V",
"EUC-H", "EUC-V", "Ext-RKSJ-H", "Ext-RKSJ-V", "H", "V", "NWP-H", "NWP-V", "RKSJ-H", "RKSJ-V", "UniJIS-UCS2-H",
"UniJIS-UCS2-V", "UniJIS-UCS2-HW-H", "UniJIS-UCS2-HW-V", "UniJIS-UTF8-H", "UniJIS-UTF8-V", "UniJIS-UTF16-H",
"UniJIS-UTF16-V", "UniJIS-UTF32-H", "UniJIS-UTF32-V", "UniJIS2004-UTF8-H", "UniJIS2004-UTF8-V", "UniJIS2004-UTF16-H",
"UniJIS2004-UTF16-V", "UniJIS2004-UTF32-H", "UniJIS2004-UTF32-V", "UniJISPro-UCS2-V", "UniJISPro-UCS2-HW-V",
"UniJISPro-UTF8-V", "UniJISX0213-UTF32-H", "UniJISX0213-UTF32-V", "UniJISX02132004-UTF32-H", "UniJISX02132004-UTF32-V",
"WP-Symbol", "Hankaku", "Hiragana", "Katakana", "Roman", "KSC-EUC-H", "KSC-EUC-V", "KSC-H", "KSC-V", "KSC-Johab-H",
"KSC-Johab-V", "KSCms-UHC-H", "KSCms-UHC-V", "KSCms-UHC-HW-H", "KSCms-UHC-HW-V", "KSCpc-EUC-H", "KSCpc-EUC-V",
"UniKS-UCS2-H", "UniKS-UCS2-V", "UniKS-UTF8-H", "UniKS-UTF8-V", "UniKS-UTF16-H", "UniKS-UTF16-V", "UniKS-UTF32-H",
"UniKS-UTF32-V", "UniAKR-UTF8-H", "UniAKR-UTF16-H", "UniAKR-UTF32-H"};
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return false;
std::vector<int> arrUniqueResources;
for (int nPage = 1, nLastPage = m_pPDFDocument->getNumPages(); nPage <= nLastPage; ++nPage)
{
Page* pPage = m_pPDFDocument->getCatalog()->getPage(nPage);
Dict* pResources = pPage->getResourceDict();
if (pResources && scanFonts(pResources, arrCMap, 0, arrUniqueResources))
return true;
Object oAnnots;
if (!pPage->getAnnots(&oAnnots)->isArray())
{
oAnnots.free();
continue;
}
for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i)
{
Object oAnnot;
if (!oAnnots.arrayGet(i, &oAnnot)->isDict())
{
oAnnot.free();
continue;
}
Object oDR;
if (oAnnot.dictLookup("DR", &oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources))
{
oDR.free(); oAnnot.free(); oAnnots.free();
return true;
}
oDR.free();
if (scanAPfonts(&oAnnot, arrCMap, arrUniqueResources))
{
oAnnot.free(); oAnnots.free();
return true;
}
oAnnot.free();
}
oAnnots.free();
}
AcroForm* pAcroForms = m_pPDFDocument->getCatalog()->getForm();
if (!pAcroForms)
return false;
Object oDR;
Object* oAcroForm = pAcroForms->getAcroFormObj();
if (oAcroForm->dictLookup("DR", &oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources))
{
oDR.free();
return true;
}
oDR.free();
for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i)
{
AcroFormField* pField = pAcroForms->getField(i);
if (pField->getResources(&oDR)->isDict() && scanFonts(oDR.getDict(), arrCMap, 0, arrUniqueResources))
{
oDR.free();
return true;
}
oDR.free();
Object oWidgetRef, oWidget;
pField->getFieldRef(&oWidgetRef);
oWidgetRef.fetch(m_pPDFDocument->getXRef(), &oWidget);
oWidgetRef.free();
if (scanAPfonts(&oWidget, arrCMap, arrUniqueResources))
{
oWidget.free();
return true;
}
oWidget.free();
}
return false;
}
void CPdfReader::SetCMapMemory(BYTE* pData, DWORD nSizeData)
{
((GlobalParamsAdaptor*)globalParams)->SetCMapMemory(pData, nSizeData);
}
void CPdfReader::SetCMapFolder(const std::wstring& sFolder)
{
((GlobalParamsAdaptor*)globalParams)->SetCMapFolder(sFolder);
}
void CPdfReader::SetCMapFile(const std::wstring& sFile)
{
((GlobalParamsAdaptor*)globalParams)->SetCMapFile(sFile);
}
bool CPdfReader::LoadFromFile(NSFonts::IApplicationFonts* pAppFonts, const std::wstring& wsSrcPath, const std::wstring& wsOwnerPassword, const std::wstring& wsUserPassword)
{
RELEASEINTERFACE(m_pFontManager);
m_pFontManager = pAppFonts->GenerateFontManager();
NSFonts::IFontsCache* pMeasurerCache = NSFonts::NSFontCache::Create();
pMeasurerCache->SetStreams(pAppFonts->GetStreams());
m_pFontManager->SetOwnerCache(pMeasurerCache);
pMeasurerCache->SetCacheSize(1);
((GlobalParamsAdaptor*)globalParams)->SetFontManager(m_pFontManager);
RELEASEOBJECT(m_pPDFDocument);
if (m_wsTempFolder == L"")
SetTempDirectory(NSDirectory::GetTempPath());
m_eError = errNone;
GString* owner_pswd = NSStrings::CreateString(wsOwnerPassword);
GString* user_pswd = NSStrings::CreateString(wsUserPassword);
// конвертим путь в utf8 - под виндой они сконвертят в юникод, а на остальных - так и надо
std::string sPathUtf8 = U_TO_UTF8(wsSrcPath);
m_pPDFDocument = new PDFDoc((char*)sPathUtf8.c_str(), owner_pswd, user_pswd);
delete owner_pswd;
delete user_pswd;
NSFile::CFileBinary oFile;
if (oFile.OpenFile(wsSrcPath))
{
m_nFileLength = oFile.GetFileSize();
oFile.CloseFile();
}
m_eError = m_pPDFDocument ? m_pPDFDocument->getErrorCode() : errMemory;
if (!m_pPDFDocument || !m_pPDFDocument->isOk())
{
RELEASEOBJECT(m_pPDFDocument);
return false;
}
m_pFontList->Clear();
std::map<std::wstring, std::wstring> mFonts = PdfReader::CAnnotFonts::GetAllFonts(m_pPDFDocument, m_pFontManager, m_pFontList);
m_mFonts.insert(mFonts.begin(), mFonts.end());
return true;
}
bool CPdfReader::LoadFromMemory(NSFonts::IApplicationFonts* pAppFonts, BYTE* data, DWORD length, const std::wstring& owner_password, const std::wstring& user_password)
{
RELEASEINTERFACE(m_pFontManager);
m_pFontManager = pAppFonts->GenerateFontManager();
NSFonts::IFontsCache* pMeasurerCache = NSFonts::NSFontCache::Create();
pMeasurerCache->SetStreams(pAppFonts->GetStreams());
m_pFontManager->SetOwnerCache(pMeasurerCache);
pMeasurerCache->SetCacheSize(1);
((GlobalParamsAdaptor*)globalParams)->SetFontManager(m_pFontManager);
RELEASEOBJECT(m_pPDFDocument);
m_eError = errNone;
GString* owner_pswd = NSStrings::CreateString(owner_password);
GString* user_pswd = NSStrings::CreateString(user_password);
Object obj;
obj.initNull();
// будет освобожден в деструкторе PDFDoc
BaseStream *str = new MemStream((char*)data, 0, length, &obj);
m_pPDFDocument = new PDFDoc(str, owner_pswd, user_pswd);
m_nFileLength = length;
delete owner_pswd;
delete user_pswd;
m_eError = m_pPDFDocument ? m_pPDFDocument->getErrorCode() : errMemory;
if (!m_pPDFDocument || !m_pPDFDocument->isOk())
{
RELEASEOBJECT(m_pPDFDocument);
return false;
}
m_pFontList->Clear();
std::map<std::wstring, std::wstring> mFonts = PdfReader::CAnnotFonts::GetAllFonts(m_pPDFDocument, m_pFontManager, m_pFontList);
m_mFonts.insert(mFonts.begin(), mFonts.end());
return true;
}
void CPdfReader::Close()
{
RELEASEOBJECT(m_pPDFDocument);
m_mFonts.clear();
}
void CPdfReader::SetParams(COfficeDrawingPageParams* pParams)
{
if (!pParams)
return;
GBool bDraw = pParams->m_bNeedDrawAnnotation ? gTrue : gFalse;
globalParams->setDrawFormFields(bDraw);
globalParams->setDrawAnnotations(bDraw);
}
int CPdfReader::GetError()
{
if (!m_pPDFDocument)
return m_eError;
if (m_pPDFDocument->isOk())
return 0;
return m_pPDFDocument->getErrorCode();
}
void CPdfReader::GetPageInfo(int _nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY)
{
int nPageIndex = _nPageIndex + 1;
if (!m_pPDFDocument)
return;
#ifdef BUILDING_WASM_MODULE
*pdWidth = m_pPDFDocument->getPageCropWidth(nPageIndex);
*pdHeight = m_pPDFDocument->getPageCropHeight(nPageIndex);
#else
int nRotate = m_pPDFDocument->getPageRotate(nPageIndex);
if (nRotate % 180 == 0)
{
*pdWidth = m_pPDFDocument->getPageCropWidth(nPageIndex);
*pdHeight = m_pPDFDocument->getPageCropHeight(nPageIndex);
}
else
{
*pdHeight = m_pPDFDocument->getPageCropWidth(nPageIndex);
*pdWidth = m_pPDFDocument->getPageCropHeight(nPageIndex);
}
#endif
*pdDpiX = 72.0;
*pdDpiY = 72.0;
}
int CPdfReader::GetRotate(int _nPageIndex)
{
if (!m_pPDFDocument)
return 0;
return m_pPDFDocument->getPageRotate(_nPageIndex + 1);
}
int CPdfReader::GetMaxRefID()
{
if (!m_pPDFDocument)
return 0;
return m_pPDFDocument->getXRef()->getNumObjects();
}
bool CPdfReader::ValidMetaData()
{
if (!m_pPDFDocument)
return false;
XRef* xref = m_pPDFDocument->getXRef();
Object oMeta, oType, oID;
if (!xref->fetch(1, 0, &oMeta)->isStream() || !oMeta.streamGetDict()->lookup("Type", &oType)->isName("MetaOForm") || !oMeta.streamGetDict()->lookup("ID", &oID)->isString())
{
oMeta.free(); oType.free(); oID.free();
return false;
}
oMeta.free(); oType.free();
Object oTID, oID2;
Object* pTrailerDict = xref->getTrailerDict();
if (!pTrailerDict->dictLookup("ID", &oTID)->isArray() || !oTID.arrayGet(1, &oID2)->isString())
{
oID.free(); oTID.free(); oID2.free();
return false;
}
oTID.free();
bool bRes = oID2.getString()->cmp(oID.getString()) == 0;
oID.free(); oID2.free();
return bRes;
}
void CPdfReader::DrawPageOnRenderer(IRenderer* pRenderer, int _nPageIndex, bool* pbBreak)
{
if (m_pPDFDocument && pRenderer)
{
PdfReader::RendererOutputDev oRendererOut(pRenderer, m_pFontManager, m_pFontList);
oRendererOut.NewPDF(m_pPDFDocument->getXRef());
oRendererOut.SetBreak(pbBreak);
int nRotate = 0;
#ifdef BUILDING_WASM_MODULE
nRotate = -m_pPDFDocument->getPageRotate(_nPageIndex + 1);
#endif
m_pPDFDocument->displayPage(&oRendererOut, _nPageIndex + 1, 72.0, 72.0, nRotate, gFalse, gTrue, gFalse);
}
}
void CPdfReader::SetTempDirectory(const std::wstring& wsTempFolder)
{
if (!m_wsTempFolder.empty())
{
NSDirectory::DeleteDirectory(m_wsTempFolder);
m_wsTempFolder = wsTempFolder;
}
if (!wsTempFolder.empty())
{
std::wstring wsFolderName = wsTempFolder + L"/pdftemp";
std::wstring wsFolder = wsFolderName;
int nCounter = 0;
while (NSDirectory::Exists(wsFolder))
{
nCounter++;
wsFolder = wsFolderName + L"_" + std::to_wstring(nCounter);
}
NSDirectory::CreateDirectory(wsFolder);
m_wsTempFolder = wsFolder;
}
else
m_wsTempFolder = L"";
if (globalParams)
((GlobalParamsAdaptor*)globalParams)->SetTempFolder(m_wsTempFolder);
}
std::wstring CPdfReader::GetTempDirectory()
{
return m_wsTempFolder;
}
std::wstring CPdfReader::ToXml(const std::wstring& wsFilePath, bool isPrintStream)
{
XMLConverter oConverter(m_pPDFDocument->getXRef(), isPrintStream);
std::wstring wsXml = oConverter.GetXml();
if (wsFilePath != L"")
{
NSFile::CFileBinary oFile;
if (!oFile.CreateFileW(wsFilePath))
return wsXml;
oFile.WriteStringUTF8(wsXml);
oFile.CloseFile();
}
return wsXml;
}
void CPdfReader::ChangeLength(DWORD nLength)
{
m_nFileLength = nLength;
}
std::wstring CPdfReader::GetInfo()
{
if (!m_pPDFDocument)
return NULL;
XRef* xref = m_pPDFDocument->getXRef();
BaseStream* str = m_pPDFDocument->getBaseStream();
if (!xref || !str)
return NULL;
std::wstring sRes = L"{";
Object oInfo;
if (m_pPDFDocument->getDocInfo(&oInfo)->isDict())
{
auto fDictLookup = [&oInfo](const char* sName, const wchar_t* wsName)
{
std::wstring sRes;
Object obj1;
if (oInfo.dictLookup(sName, &obj1)->isString())
{
TextString* s = new TextString(obj1.getString());
std::wstring sValue = NSStringExt::CConverter::GetUnicodeFromUTF32(s->getUnicode(), s->getLength());
delete s;
NSStringExt::Replace(sValue, L"\\", L"\\\\");
NSStringExt::Replace(sValue, L"\"", L"\\\"");
sValue.erase(std::remove_if(sValue.begin(), sValue.end(), [] (const wchar_t& wc) { return wc < 0x20; } ), sValue.end());
if (!sValue.empty())
{
sRes += L"\"";
sRes += wsName;
sRes += L"\":\"";
sRes += sValue;
sRes += L"\",";
}
}
obj1.free();
return sRes;
};
sRes += fDictLookup("Title", L"Title");
sRes += fDictLookup("Author", L"Author");
sRes += fDictLookup("Subject", L"Subject");
sRes += fDictLookup("Keywords", L"Keywords");
sRes += fDictLookup("Creator", L"Creator");
sRes += fDictLookup("Producer", L"Producer");
auto fDictLookupDate = [&oInfo](const char* sName, const wchar_t* wsName)
{
std::wstring sRes;
Object obj1;
if (!oInfo.dictLookup(sName, &obj1)->isString() || !obj1.getString()->getLength())
{
obj1.free();
return sRes;
}
TextString* s = new TextString(obj1.getString());
std::wstring sNoDate = NSStringExt::CConverter::GetUnicodeFromUTF32(s->getUnicode(), s->getLength());
if (sNoDate.length() > 16)
{
std::wstring sDate = sNoDate.substr(2, 4) + L'-' + sNoDate.substr(6, 2) + L'-' + sNoDate.substr(8, 2) + L'T' +
sNoDate.substr(10, 2) + L':' + sNoDate.substr(12, 2) + L':' + sNoDate.substr(14, 2);
if (sNoDate.length() > 21 && (sNoDate[16] == L'+' || sNoDate[16] == L'-'))
sDate += (L".000" + sNoDate.substr(16, 3) + L':' + sNoDate.substr(20, 2));
else
sDate += L"Z";
NSStringExt::Replace(sDate, L"\\", L"\\\\");
NSStringExt::Replace(sDate, L"\"", L"\\\"");
sDate.erase(std::remove_if(sDate.begin(), sDate.end(), [] (const wchar_t& wc) { return wc < 0x20; } ), sDate.end());
sRes += L"\"";
sRes += wsName;
sRes += L"\":\"";
sRes += sDate;
sRes += L"\",";
}
delete s;
obj1.free();
return sRes;
};
sRes += fDictLookupDate("CreationDate", L"CreationDate");
sRes += fDictLookupDate("ModDate", L"ModDate");
}
oInfo.free();
std::wstring version = std::to_wstring(m_pPDFDocument->getPDFVersion());
std::wstring::size_type posDot = version.find('.');
if (posDot != std::wstring::npos)
version.resize(posDot + 2);
if (!version.empty())
sRes += (L"\"Version\":" + version + L",");
double nW = 0;
double nH = 0;
double nDpi = 0;
GetPageInfo(0, &nW, &nH, &nDpi, &nDpi);
sRes += L"\"PageWidth\":";
sRes += std::to_wstring((int)(nW * 100));
sRes += L",\"PageHeight\":";
sRes += std::to_wstring((int)(nH * 100));
sRes += L",\"NumberOfPages\":";
sRes += std::to_wstring(m_pPDFDocument->getNumPages());
sRes += L",\"FastWebView\":";
Object obj1, obj2, obj3, obj4, obj5, obj6;
bool bLinearized = false;
obj1.initNull();
Parser* parser = new Parser(xref, new Lexer(xref, str->makeSubStream(str->getStart(), gFalse, 0, &obj1)), gTrue);
parser->getObj(&obj1);
parser->getObj(&obj2);
parser->getObj(&obj3);
parser->getObj(&obj4);
if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") && obj4.isDict())
{
obj4.dictLookup("Linearized", &obj5);
obj4.dictLookup("L", &obj6);
if (obj5.isNum() && obj5.getNum() > 0 && obj6.isNum())
{
unsigned long size = (unsigned long)obj6.getNum();
bLinearized = size == m_nFileLength;
}
obj6.free();
obj5.free();
}
obj4.free();
obj3.free();
obj2.free();
obj1.free();
delete parser;
sRes += bLinearized ? L"true" : L"false";
sRes += L",\"Tagged\":";
bool bTagged = false;
Object catDict, markInfoObj;
if (xref->getCatalog(&catDict)->isDict() && catDict.dictLookup("MarkInfo", &markInfoObj)->isDict())
{
Object marked, suspects;
if (markInfoObj.dictLookup("Marked", &marked)->isBool() && marked.getBool() == gTrue)
{
bTagged = true;
// If Suspects is true, the document may not completely conform to Tagged PDF conventions.
if (markInfoObj.dictLookup("Suspects", &suspects)->isBool() && suspects.getBool() == gTrue)
bTagged = false;
}
marked.free();
suspects.free();
}
markInfoObj.free();
catDict.free();
sRes += bTagged ? L"true}" : L"false}";
return sRes;
}
std::wstring CPdfReader::GetFontPath(const std::wstring& wsFontName, bool bSave)
{
std::map<std::wstring, std::wstring>::const_iterator oIter = m_mFonts.find(wsFontName);
return oIter == m_mFonts.end() ? std::wstring() : oIter->second;
}
void getBookmarks(PDFDoc* pdfDoc, OutlineItem* pOutlineItem, NSWasm::CData& out, int level)
{
LinkAction* pLinkAction = pOutlineItem->getAction();
if (!pLinkAction || pLinkAction->getKind() != actionGoTo)
return;
GString* str = ((LinkGoTo*)pLinkAction)->getNamedDest();
LinkDest* pLinkDest = str ? pdfDoc->findDest(str) : ((LinkGoTo*)pLinkAction)->getDest();
if (!pLinkDest)
return;
int pg;
if (pLinkDest->isPageRef())
{
Ref pageRef = pLinkDest->getPageRef();
pg = pdfDoc->findPage(pageRef.num, pageRef.gen);
}
else
pg = pLinkDest->getPageNum();
if (pg == 0)
pg = 1;
double dy = 0;
double dTop = pLinkDest->getTop();
double dHeight = pdfDoc->getPageCropHeight(pg);
/*
if (pdfDoc->getPageRotate(pg) % 180 != 0)
{
dHeight = pdfDoc->getPageCropWidth(pg);
dTop = pLinkDest->getLeft();
}
*/
if (dTop > 0 && dTop < dHeight)
dy = dHeight - dTop;
if (str)
RELEASEOBJECT(pLinkDest);
std::string sTitle = NSStringExt::CConverter::GetUtf8FromUTF32(pOutlineItem->getTitle(), pOutlineItem->getTitleLength());
out.AddInt(pg - 1);
out.AddInt(level);
out.AddDouble(dy);
out.WriteString((BYTE*)sTitle.c_str(), (unsigned int)sTitle.length());
pOutlineItem->open();
GList* pList = pOutlineItem->getKids();
if (!pList)
{
pOutlineItem->close();
return;
}
for (int i = 0, num = pList->getLength(); i < num; i++)
{
OutlineItem* pOutlineItemKid = (OutlineItem*)pList->get(i);
if (pOutlineItemKid)
getBookmarks(pdfDoc, pOutlineItemKid, out, level + 1);
}
pOutlineItem->close();
}
BYTE* CPdfReader::GetStructure()
{
if (!m_pPDFDocument)
return NULL;
Outline* pOutline = m_pPDFDocument->getOutline();
if (!pOutline)
return NULL;
GList* pList = pOutline->getItems();
if (!pList)
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
for (int i = 0, num = pList->getLength(); i < num; i++)
{
OutlineItem* pOutlineItem = (OutlineItem*)pList->get(i);
if (pOutlineItem)
getBookmarks(m_pPDFDocument, pOutlineItem, oRes, 1);
}
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetLinks(int nPageIndex)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
nPageIndex++;
Page* pPage = m_pPDFDocument->getCatalog()->getPage(nPageIndex);
if (!pPage)
return NULL;
NSWasm::CPageLink oLinks;
// Гиперссылка
Links* pLinks = m_pPDFDocument->getLinks(nPageIndex);
if (pLinks)
{
PDFRectangle* cropBox = pPage->getCropBox();
for (int i = 0, num = pLinks->getNumLinks(); i < num; i++)
{
Link* pLink = pLinks->getLink(i);
if (!pLink)
continue;
GString* str = NULL;
double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0, dy = 0.0;
pLink->getRect(&x1, &y1, &x2, &y2);
x1 = x1 - cropBox->x1;
y1 = cropBox->y2 - y1;
x2 = x2 - cropBox->x1;
y2 = cropBox->y2 - y2;
LinkAction* pLinkAction = pLink->getAction();
if (!pLinkAction)
continue;
LinkActionKind kind = pLinkAction->getKind();
if (kind == actionGoTo)
{
str = ((LinkGoTo*)pLinkAction)->getNamedDest();
LinkDest* pLinkDest = str ? m_pPDFDocument->findDest(str) : ((LinkGoTo*)pLinkAction)->getDest()->copy();
if (pLinkDest)
{
int pg;
if (pLinkDest->isPageRef())
{
Ref pageRef = pLinkDest->getPageRef();
pg = m_pPDFDocument->findPage(pageRef.num, pageRef.gen);
}
else
pg = pLinkDest->getPageNum();
if (0 == pg)
++pg;
std::string sLink = "#" + std::to_string(pg - 1);
str = new GString(sLink.c_str());
dy = m_pPDFDocument->getPageCropHeight(pg) - pLinkDest->getTop();
}
else
str = NULL;
RELEASEOBJECT(pLinkDest);
}
else if (kind == actionURI)
str = ((LinkURI*)pLinkAction)->getURI()->copy();
else if (kind == actionNamed)
{
str = ((LinkNamed*)pLinkAction)->getName();
int pg = 1;
if (!str->cmp("NextPage"))
{
pg = nPageIndex + 1;
if (pg > m_pPDFDocument->getNumPages())
pg = m_pPDFDocument->getNumPages();
}
else if (!str->cmp("PrevPage"))
{
pg = nPageIndex - 1;
if (pg < 1)
pg = 1;
}
else if (!str->cmp("LastPage"))
pg = m_pPDFDocument->getNumPages();
std::string sLink = "#" + std::to_string(pg - 1);
str = new GString(sLink.c_str());
}
oLinks.m_arLinks.push_back({str ? std::string(str->getCString(), str->getLength()) : "", dy, x1, y2, x2 - x1, y1 - y2});
RELEASEOBJECT(str);
}
}
RELEASEOBJECT(pLinks);
int nRotate = 0;
#ifdef BUILDING_WASM_MODULE
nRotate = -m_pPDFDocument->getPageRotate(nPageIndex);
#endif
// Текст-ссылка
TextOutputControl textOutControl;
textOutControl.mode = textOutReadingOrder;
TextOutputDev* pTextOut = new TextOutputDev(NULL, &textOutControl, gFalse);
m_pPDFDocument->displayPage(pTextOut, nPageIndex, 72.0, 72.0, nRotate, gFalse, gTrue, gFalse);
m_pPDFDocument->processLinks(pTextOut, nPageIndex);
TextWordList* pWordList = pTextOut->makeWordList();
for (int i = 0; i < pWordList->getLength(); i++)
{
TextWord* pWord = pWordList->get(i);
if (!pWord)
continue;
GString* sLink = pWord->getText();
if (!sLink)
continue;
std::string link(sLink->getCString(), sLink->getLength());
RELEASEOBJECT(sLink);
size_t find = link.find("http://");
if (find == std::string::npos)
find = link.find("https://");
if (find == std::string::npos)
find = link.find("www.");
if (find != std::string::npos)
{
link.erase(0, find);
double x1, y1, x2, y2;
pWord->getBBox(&x1, &y1, &x2, &y2);
oLinks.m_arLinks.push_back({link, 0, x1, y1, x2 - x1, y2 - y1});
}
}
RELEASEOBJECT(pWordList);
RELEASEOBJECT(pTextOut);
return oLinks.Serialize();
}
BYTE* CPdfReader::GetWidgets()
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
if (!m_pPDFDocument->getCatalog()->getForm() || !m_pPDFDocument->getXRef())
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
PdfReader::CAnnots* pAnnots = new PdfReader::CAnnots(m_pPDFDocument, m_pFontManager, m_pFontList);
if (pAnnots)
pAnnots->ToWASM(oRes);
RELEASEOBJECT(pAnnots);
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetFonts(bool bStandart)
{
NSWasm::CData oRes;
oRes.SkipLen();
int nFonts = 0;
int nFontsPos = oRes.GetSize();
oRes.AddInt(nFonts);
for (std::map<std::wstring, std::wstring>::iterator it = m_mFonts.begin(); it != m_mFonts.end(); ++it)
{
if (PdfReader::CAnnotFonts::IsBaseFont(it->second))
{
if (bStandart)
{
oRes.WriteString(it->first);
nFonts++;
}
}
else if (!bStandart)
{
oRes.WriteString(it->first);
nFonts++;
}
}
oRes.AddInt(nFonts, nFontsPos);
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::VerifySign(const std::wstring& sFile, ICertificate* pCertificate, int nWidget)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
AcroForm* pAcroForms = m_pPDFDocument->getCatalog()->getForm();
if (!pAcroForms)
return NULL;
BYTE* pFileData = NULL;
DWORD nFileSize;
if (!NSFile::CFileBinary::ReadAllBytes(sFile, &pFileData, nFileSize))
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i)
{
AcroFormField* pField = pAcroForms->getField(i);
AcroFormFieldType oType = pField->getAcroFormFieldType();
if (oType != acroFormFieldSignature || (nWidget >= 0 && i != nWidget))
continue;
Object oObj, oObj1;
if (!pField->fieldLookup("V", &oObj)->isDict())
continue;
std::vector<int> arrByteOffset, arrByteLength;
if (oObj.dictLookup("ByteRange", &oObj1)->isArray())
{
for (int j = 0; j < oObj1.arrayGetLength(); ++j)
{
Object oObjJ;
if (oObj1.arrayGet(j, &oObjJ)->isInt())
{
if (j % 2 == 0)
arrByteOffset.push_back(oObjJ.getInt());
else
arrByteLength.push_back(oObjJ.getInt());
}
oObjJ.free();
}
}
oObj1.free();
DWORD dwLenDataForVerify = 0;
for (int j = 0; j < arrByteLength.size(); ++j)
dwLenDataForVerify += arrByteLength[j];
BYTE* pDataForVerify = new BYTE[dwLenDataForVerify];
int nByteOffset = 0;
for (int j = 0; j < arrByteOffset.size(); ++j)
{
// TODO проверка длины файла и ByteRange
memcpy(pDataForVerify + nByteOffset, pFileData + arrByteOffset[j], arrByteLength[j]);
nByteOffset += arrByteLength[j];
}
BYTE* pPKCS7Data = NULL;
DWORD nPKCS7Size = 0;
if (oObj.dictLookup("Contents", &oObj1)->isString())
{
GString* sContents = oObj1.getString();
pPKCS7Data = (BYTE*)sContents->getCString();
nPKCS7Size = sContents->getLength();
}
int nRes = pCertificate->VerifyPKCS7(pPKCS7Data, nPKCS7Size, pDataForVerify, dwLenDataForVerify);
RELEASEARRAYOBJECTS(pDataForVerify);
oObj1.free();
// Номер аннотации для сопоставления с AP
oRes.AddInt(i);
oRes.AddInt(nRes);
}
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetAPWidget(int nRasterW, int nRasterH, int nBackgroundColor, int nPageIndex, int nWidget, const char* sView, const char* sButtonView)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
AcroForm* pAcroForms = m_pPDFDocument->getCatalog()->getForm();
if (!pAcroForms)
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i)
{
AcroFormField* pField = pAcroForms->getField(i);
Object oRef;
if (pField->getPageNum() != nPageIndex + 1 || (nWidget >= 0 && pField->getFieldRef(&oRef) && oRef.getRefNum() != nWidget))
{
oRef.free();
continue;
}
oRef.free();
PdfReader::CAnnotAP* pAP = new PdfReader::CAnnotAP(m_pPDFDocument, m_pFontManager, m_pFontList, nRasterW, nRasterH, nBackgroundColor, nPageIndex, sView, sButtonView, pField);
if (pAP)
pAP->ToWASM(oRes);
RELEASEOBJECT(pAP);
}
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetButtonIcon(int nBackgroundColor, int nPageIndex, bool bBase64, int nButtonWidget, const char* sIconView)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
AcroForm* pAcroForms = m_pPDFDocument->getCatalog()->getForm();
if (!pAcroForms)
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
std::vector<int> arrUniqueImage;
for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i)
{
AcroFormField* pField = pAcroForms->getField(i);
if (pField->getPageNum() != nPageIndex + 1 || pField->getAcroFormFieldType() != acroFormFieldPushbutton || (nButtonWidget >= 0 && i != nButtonWidget))
continue;
Object oMK;
if (!pField->fieldLookup("MK", &oMK)->isDict())
{
oMK.free();
continue;
}
bool bFirst = true;
int nMKPos = -1;
unsigned int nMKLength = 0;
std::vector<const char*> arrMKName { "I", "RI", "IX" };
for (unsigned int j = 0; j < arrMKName.size(); ++j)
{
if (sIconView && strcmp(sIconView, arrMKName[j]) != 0)
continue;
std::string sMKName(arrMKName[j]);
Object oStr;
if (!oMK.dictLookup(sMKName.c_str(), &oStr)->isStream())
{
oStr.free();
continue;
}
if (bFirst)
{
Object oFieldRef;
pField->getFieldRef(&oFieldRef);
// Номер аннотации для сопоставления с AP
oRes.AddInt(oFieldRef.getRefNum());
oFieldRef.free();
// Количество иконок 1-3
nMKPos = oRes.GetSize();
oRes.AddInt(nMKLength);
bFirst = false;
}
// Получение единственного XObject из Resources, если возможно
Object oResources, oXObject, oIm;
if (!oStr.streamGetDict()->lookup("Resources", &oResources)->isDict() || !oResources.dictLookup("XObject", &oXObject)->isDict() || oXObject.dictGetLength() != 1 || !oXObject.dictGetVal(0, &oIm)->isStream())
{
oStr.free(); oResources.free(); oXObject.free(); oIm.free();
continue;
}
oStr.free(); oResources.free();
Dict *oImDict = oIm.streamGetDict();
Object oType, oSubtype;
if (!oImDict->lookup("Type", &oType)->isName("XObject") || !oImDict->lookup("Subtype", &oSubtype)->isName("Image"))
{
oType.free(); oSubtype.free();
oXObject.free(); oIm.free();
continue;
}
oType.free(); oSubtype.free();
oRes.WriteString(sMKName);
Object oStrRef;
oXObject.dictGetValNF(0, &oStrRef);
int nView = oStrRef.getRefNum();
oRes.AddInt(nView);
oStrRef.free(); oXObject.free();
if (std::find(arrUniqueImage.begin(), arrUniqueImage.end(), nView) != arrUniqueImage.end())
{
oIm.free();
oRes.WriteBYTE(0);
nMKLength++;
continue;
}
arrUniqueImage.push_back(nView);
oRes.WriteBYTE(1);
// Width & Height
Object oWidth, oHeight;
int nWidth = 0;
int nHeight = 0;
if (oImDict->lookup("Width", &oWidth)->isInt() && oImDict->lookup("Height", &oHeight)->isInt())
{
nWidth = oWidth.getInt();
nHeight = oHeight.getInt();
}
oRes.AddInt(nWidth);
oRes.AddInt(nHeight);
oWidth.free(); oHeight.free();
if (bBase64)
{
int nLength = 0;
Object oLength;
if (oImDict->lookup("Length", &oLength)->isInt())
nLength = oLength.getInt();
oLength.free();
if (oImDict->lookup("DL", &oLength)->isInt())
nLength = oLength.getInt();
oLength.free();
bool bNew = false;
BYTE* pBuffer = NULL;
Stream* pImage = oIm.getStream()->getUndecodedStream();
pImage->reset();
MemStream* pMemory = dynamic_cast<MemStream*>(pImage);
if (pImage->getKind() == strWeird && pMemory)
{
if (pMemory->getBufPtr() + nLength == pMemory->getBufEnd())
pBuffer = (BYTE*)pMemory->getBufPtr();
else
nLength = 0;
}
else
{
bNew = true;
pBuffer = new BYTE[nLength];
BYTE* pBufferPtr = pBuffer;
for (int nI = 0; nI < nLength; ++nI)
*pBufferPtr++ = (BYTE)pImage->getChar();
}
char* cData64 = NULL;
int nData64Dst = 0;
NSFile::CBase64Converter::Encode(pBuffer, nLength, cData64, nData64Dst, NSBase64::B64_BASE64_FLAG_NOCRLF);
oRes.WriteString((BYTE*)cData64, nData64Dst);
nMKLength++;
if (bNew)
RELEASEARRAYOBJECTS(pBuffer);
RELEASEARRAYOBJECTS(cData64);
continue;
}
// else
BYTE* pBgraData = new BYTE[nWidth * nHeight * 4];
unsigned int nColor = (unsigned int)nBackgroundColor;
unsigned int nSize = (unsigned int)(nWidth * nHeight);
unsigned int* pTemp = (unsigned int*)pBgraData;
for (unsigned int k = 0; k < nSize; ++k)
*pTemp++ = nColor;
int bits = 0;
StreamColorSpaceMode csMode = streamCSNone;
oIm.getStream()->getImageParams(&bits, &csMode);
if (bits == 0)
{
Object oBits;
if (oImDict->lookup("BitsPerComponent", &oBits)->isNull())
{
oBits.free();
oImDict->lookup("BPC", &oBits);
}
bits = oBits.isInt() ? oBits.getInt() : 8;
oBits.free();
}
GfxColorSpace* colorSpace = NULL;
Object oColorSpace;
if (oImDict->lookup("ColorSpace", &oColorSpace)->isNull())
{
oColorSpace.free();
oImDict->lookup("CS", &oColorSpace);
}
if (oColorSpace.isName())
{
// TODO
}
if (!oColorSpace.isNull())
colorSpace = GfxColorSpace::parse(&oColorSpace);
else if (csMode == streamCSDeviceGray)
colorSpace = GfxColorSpace::create(csDeviceGray);
else if (csMode == streamCSDeviceRGB)
colorSpace = GfxColorSpace::create(csDeviceRGB);
else if (csMode == streamCSDeviceCMYK)
colorSpace = GfxColorSpace::create(csDeviceCMYK);
else
colorSpace = NULL;
oColorSpace.free();
Object oDecode;
if (oImDict->lookup("Decode", &oDecode)->isNull())
{
oDecode.free();
oImDict->lookup("D", &oDecode);
}
GfxImageColorMap* pColorMap = new GfxImageColorMap(bits, &oDecode, colorSpace);
oDecode.free();
ImageStream *pImageStream = new ImageStream(oIm.getStream(), nWidth, pColorMap->getNumPixelComps(), pColorMap->getBits());
pImageStream->reset();
int nComps = pImageStream->getComps();
int nCheckWidth = std::min(nWidth, pImageStream->getVals() / nComps);
int nColorMapType = pColorMap->getFillType();
GfxColorComp** pColorMapLookup = pColorMap->getLookup();
if (!pColorMapLookup)
nColorMapType = 0;
for (int nY = 0; nY < nHeight; ++nY)
{
unsigned char* pLine = pImageStream->getLine();
unsigned char* pLineDst = pBgraData + 4 * nWidth * nY;
if (!pLine)
{
memset(pLineDst, 0, 4 * nWidth);
continue;
}
for (int nX = 0; nX < nCheckWidth; ++nX)
{
if (2 == nColorMapType)
{
pLineDst[0] = colToByte(clip01(pColorMapLookup[0][pLine[0]]));
pLineDst[1] = colToByte(clip01(pColorMapLookup[1][pLine[1]]));
pLineDst[2] = colToByte(clip01(pColorMapLookup[2][pLine[2]]));
}
else if (1 == nColorMapType)
{
pLineDst[0] = pLineDst[1] = pLineDst[2] = colToByte(clip01(pColorMapLookup[0][pLine[0]]));
}
else
{
GfxRGB oRGB;
pColorMap->getRGB(pLine, &oRGB, gfxRenderingIntentAbsoluteColorimetric);
pLineDst[0] = colToByte(oRGB.r);
pLineDst[1] = colToByte(oRGB.g);
pLineDst[2] = colToByte(oRGB.b);
}
pLineDst[3] = 255;
pLine += nComps;
pLineDst += 4;
}
}
delete pColorMap;
nMKLength++;
unsigned long long npSubMatrix = (unsigned long long)pBgraData;
unsigned int npSubMatrix1 = npSubMatrix & 0xFFFFFFFF;
oRes.AddInt(npSubMatrix1);
oRes.AddInt(npSubMatrix >> 32);
oIm.free();
}
oMK.free();
if (nMKPos > 0)
oRes.AddInt(nMKLength, nMKPos);
}
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
void GetPageAnnots(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, PdfReader::CPdfFontList *pFontList, NSWasm::CData& oRes, int nPageIndex)
{
Page* pPage = pdfDoc->getCatalog()->getPage(nPageIndex + 1);
if (!pPage)
return;
Object oAnnots;
if (!pPage->getAnnots(&oAnnots)->isArray())
{
oAnnots.free();
return;
}
for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i)
{
Object oAnnot;
if (!oAnnots.arrayGet(i, &oAnnot)->isDict())
{
oAnnot.free();
continue;
}
Object oSubtype;
std::string sType;
if (oAnnot.dictLookup("Subtype", &oSubtype)->isName())
sType = oSubtype.getName();
oSubtype.free(); oAnnot.free();
Object oAnnotRef;
PdfReader::CAnnot* pAnnot = NULL;
oAnnots.arrayGetNF(i, &oAnnotRef);
if (sType == "Text")
{
pAnnot = new PdfReader::CAnnotText(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Link")
{
}
else if (sType == "FreeText")
{
PdfReader::CAnnotFreeText* pFreeText = new PdfReader::CAnnotFreeText(pdfDoc, &oAnnotRef, nPageIndex);
pFreeText->SetFont(pdfDoc, &oAnnotRef, pFontManager, pFontList);
pAnnot = pFreeText;
}
else if (sType == "Line")
{
pAnnot = new PdfReader::CAnnotLine(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Square" || sType == "Circle")
{
pAnnot = new PdfReader::CAnnotSquareCircle(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Polygon" || sType == "PolyLine")
{
pAnnot = new PdfReader::CAnnotPolygonLine(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Highlight" ||
sType == "Underline" ||
sType == "Squiggly" ||
sType == "StrikeOut")
{
pAnnot = new PdfReader::CAnnotTextMarkup(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Stamp")
{
pAnnot = new PdfReader::CAnnotStamp(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Caret")
{
pAnnot = new PdfReader::CAnnotCaret(pdfDoc, &oAnnotRef, nPageIndex);
}
else if (sType == "Ink")
{
pAnnot = new PdfReader::CAnnotInk(pdfDoc, &oAnnotRef, nPageIndex);
}
// else if (sType == "FileAttachment")
// {
// pAnnot = new PdfReader::CAnnotFileAttachment(pdfDoc, &oAnnotRef, nPageIndex);
// }
// else if (sType == "Popup")
// {
// pAnnot = new PdfReader::CAnnotPopup(pdfDoc, &oAnnotRef, nPageIndex);
// }
// TODO Все аннотации
oAnnotRef.free();
if (pAnnot)
pAnnot->ToWASM(oRes);
RELEASEOBJECT(pAnnot);
}
oAnnots.free();
}
BYTE* CPdfReader::GetAnnots(int nPageIndex)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
NSWasm::CData oRes;
oRes.SkipLen();
if (nPageIndex >= 0)
GetPageAnnots(m_pPDFDocument, m_pFontManager, m_pFontList, oRes, nPageIndex);
else
for (int nPage = 0, nLastPage = m_pPDFDocument->getNumPages(); nPage < nLastPage; ++nPage)
GetPageAnnots(m_pPDFDocument, m_pFontManager, m_pFontList, oRes, nPage);
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetShapes(int nPageIndex)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
Dict* pResources = m_pPDFDocument->getCatalog()->getPage(nPageIndex + 1)->getResourceDict();
if (!pResources)
return NULL;
Object oProperties, oMetaOForm, oID;
XRef* xref = m_pPDFDocument->getXRef();
if (!pResources->lookup("Properties", &oProperties)->isDict() || !oProperties.dictLookup("OShapes", &oMetaOForm)->isDict("OShapes") || !oMetaOForm.dictLookup("ID", &oID)->isString())
{
oProperties.free(); oMetaOForm.free(); oID.free();
return NULL;
}
oProperties.free();
Object oTID, oID2;
Object* pTrailerDict = xref->getTrailerDict();
if (!pTrailerDict->dictLookup("ID", &oTID)->isArray() || !oTID.arrayGet(1, &oID2)->isString() || oID2.getString()->cmp(oID.getString()) != 0)
{
oMetaOForm.free(); oID.free(); oTID.free(); oID2.free();
return NULL;
}
oID.free(); oTID.free(); oID2.free();
Object oMetadata;
if (!oMetaOForm.dictLookup("Metadata", &oMetadata)->isArray())
{
oMetaOForm.free(); oMetadata.free();
return NULL;
}
oMetaOForm.free();
NSWasm::CData oRes;
oRes.SkipLen();
int nMetadataLength = oMetadata.arrayGetLength();
oRes.AddInt(nMetadataLength);
for (int i = 0; i < nMetadataLength; ++i)
{
Object oMetaStr;
std::string sStr;
if (oMetadata.arrayGet(i, &oMetaStr)->isString())
{
TextString* s = new TextString(oMetaStr.getString());
sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
delete s;
}
oRes.WriteString(sStr);
}
oMetadata.free();
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetAPAnnots(int nRasterW, int nRasterH, int nBackgroundColor, int nPageIndex, int nAnnot, const char* sView)
{
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
return NULL;
Object oAnnots;
Page* pPage = m_pPDFDocument->getCatalog()->getPage(nPageIndex + 1);
if (!pPage || !pPage->getAnnots(&oAnnots)->isArray())
{
oAnnots.free();
return NULL;
}
NSWasm::CData oRes;
oRes.SkipLen();
for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i)
{
Object oAnnotRef;
if (!oAnnots.arrayGetNF(i, &oAnnotRef)->isRef() || (nAnnot >= 0 && oAnnotRef.getRefNum() != nAnnot))
{
oAnnotRef.free();
continue;
}
Object oAnnot, oObj;
std::string sType;
oAnnots.arrayGet(i, &oAnnot);
if (oAnnot.dictLookup("Subtype", &oObj)->isName())
sType = oObj.getName();
oObj.free(); oAnnot.free();
if (sType == "Widget")
{
oAnnotRef.free();
continue;
}
PdfReader::CAnnotAP* pAP = new PdfReader::CAnnotAP(m_pPDFDocument, m_pFontManager, m_pFontList, nRasterW, nRasterH, nBackgroundColor, nPageIndex, sView, &oAnnotRef);
if (pAP)
pAP->ToWASM(oRes);
RELEASEOBJECT(pAP);
oAnnotRef.free();
}
oAnnots.free();
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
std::map<std::wstring, std::wstring> CPdfReader::GetAnnotFonts(Object* pRefAnnot)
{
return PdfReader::CAnnotFonts::GetAnnotFont(m_pPDFDocument, m_pFontManager, m_pFontList, pRefAnnot);
}