mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
1411 lines
51 KiB
C++
1411 lines
51 KiB
C++
/*
|
||
* (c) Copyright Ascensio System SIA 2010-2023
|
||
*
|
||
* This program is a free software product. You can redistribute it and/or
|
||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||
* version 3 as published by the Free Software Foundation. In accordance with
|
||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||
* of any third-party rights.
|
||
*
|
||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||
*
|
||
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
||
* street, Riga, Latvia, EU, LV-1050.
|
||
*
|
||
* The interactive user interfaces in modified source and object code versions
|
||
* of the Program must display Appropriate Legal Notices, as required under
|
||
* Section 5 of the GNU AGPL version 3.
|
||
*
|
||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||
* grant you any rights under trademark law for use of our trademarks.
|
||
*
|
||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||
* well as technical writing content are licensed under the terms of the
|
||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||
*
|
||
*/
|
||
|
||
#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 "lib/xpdf/PDFDoc.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 "SrcReader/Adaptors.h"
|
||
#include "lib/xpdf/Outline.h"
|
||
#include "lib/xpdf/Link.h"
|
||
#include "lib/xpdf/TextOutputDev.h"
|
||
#include "lib/xpdf/AcroForm.h"
|
||
#include "lib/xpdf/Annot.h"
|
||
#include "lib/goo/GList.h"
|
||
|
||
#include <vector>
|
||
|
||
CPdfReader::CPdfReader(NSFonts::IApplicationFonts* pAppFonts, IOfficeDrawingFile* pRenderer)
|
||
{
|
||
m_pRenderer = pRenderer;
|
||
m_wsTempFolder = L"";
|
||
m_pPDFDocument = NULL;
|
||
m_nFileLength = 0;
|
||
|
||
globalParams = new GlobalParamsAdaptor(NULL);
|
||
#ifndef _DEBUG
|
||
globalParams->setErrQuiet(gTrue);
|
||
#endif
|
||
|
||
m_pFontList = new PdfReader::CFontList();
|
||
|
||
// Создаем менеджер шрифтов с собственным кэшем
|
||
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);
|
||
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, PDFDoc *pDoc, const std::vector<std::string>& arrCMap)
|
||
{
|
||
Object oFonts;
|
||
if (pResources->lookup("Font", &oFonts) && oFonts.isDict())
|
||
{
|
||
for (int i = 0, nLength = oFonts.dictGetLength(); i < nLength; ++i)
|
||
{
|
||
Object oFont, oEncoding;
|
||
if (!oFonts.dictGetVal(i, &oFont) || !oFont.isDict() || !oFont.dictLookup("Encoding", &oEncoding) || !oEncoding.isName())
|
||
{
|
||
oFont.free(); oEncoding.free();
|
||
continue;
|
||
}
|
||
|
||
char* sName = oEncoding.getName();
|
||
if (std::find(arrCMap.begin(), arrCMap.end(), sName) != arrCMap.end())
|
||
{
|
||
oEncoding.free(); oFont.free(); oFonts.free();
|
||
return true;
|
||
}
|
||
oEncoding.free(); oFont.free();
|
||
}
|
||
}
|
||
oFonts.free();
|
||
|
||
#define SCAN_FONTS(sName)\
|
||
{\
|
||
Object oObject;\
|
||
if (pResources->lookup(sName, &oObject) && oObject.isDict())\
|
||
{\
|
||
for (int i = 0, nLength = oObject.dictGetLength(); i < nLength; ++i)\
|
||
{\
|
||
Object oXObj, oResources;\
|
||
if (!oObject.dictGetVal(i, &oXObj) || !oXObj.isStream() || !oXObj.streamGetDict()->lookup("Resources", &oResources) || !oResources.isDict())\
|
||
{\
|
||
oXObj.free(); oResources.free();\
|
||
continue;\
|
||
}\
|
||
oXObj.free();\
|
||
if (scanFonts(oResources.getDict(), pDoc, arrCMap))\
|
||
{\
|
||
oResources.free(); oObject.free();\
|
||
return true;\
|
||
}\
|
||
oResources.free();\
|
||
}\
|
||
}\
|
||
oObject.free();\
|
||
}
|
||
SCAN_FONTS("XObject");
|
||
SCAN_FONTS("Pattern");
|
||
|
||
Object oExtGState;
|
||
if (pResources->lookup("ExtGState", &oExtGState) && oExtGState.isDict())
|
||
{
|
||
for (int i = 0, nLength = oExtGState.dictGetLength(); i < nLength; ++i)
|
||
{
|
||
Object oGS, oSMask, oSMaskGroup, oResources;
|
||
if (!oExtGState.dictGetVal(i, &oGS) || !oGS.isDict() || !oGS.dictLookup("SMask", &oSMask) || !oSMask.isDict() || !oSMask.dictLookup("G", &oSMaskGroup) || !oSMaskGroup.isStream() ||
|
||
oSMaskGroup.streamGetDict()->lookup("Resources", &oResources) || !oResources.isDict())
|
||
{
|
||
oGS.free(); oSMask.free(); oSMaskGroup.free(); oResources.free();
|
||
continue;
|
||
}
|
||
oGS.free(); oSMask.free(); oSMaskGroup.free();
|
||
if (scanFonts(oResources.getDict(), pDoc, arrCMap))
|
||
{
|
||
oResources.free(); oExtGState.free();
|
||
return true;
|
||
}
|
||
oResources.free();
|
||
}
|
||
}
|
||
oExtGState.free();
|
||
|
||
return false;
|
||
}
|
||
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;
|
||
|
||
for (int nPage = 0, nLastPage = m_pPDFDocument->getNumPages(); nPage < nLastPage; ++nPage)
|
||
{
|
||
Page* pPage = m_pPDFDocument->getCatalog()->getPage(nPage + 1);
|
||
Dict* pResources = pPage->getResourceDict();
|
||
if (pResources && scanFonts(pResources, m_pPDFDocument, arrCMap))
|
||
return true;
|
||
}
|
||
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();
|
||
|
||
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();
|
||
|
||
return true;
|
||
}
|
||
void CPdfReader::Close()
|
||
{
|
||
RELEASEOBJECT(m_pPDFDocument);
|
||
}
|
||
|
||
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;
|
||
|
||
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);
|
||
}
|
||
|
||
*pdDpiX = 72.0;
|
||
*pdDpiY = 72.0;
|
||
}
|
||
void CPdfReader::DrawPageOnRenderer(IRenderer* pRenderer, int _nPageIndex, bool* pbBreak)
|
||
{
|
||
int nPageIndex = _nPageIndex + 1;
|
||
|
||
if (m_pPDFDocument && pRenderer)
|
||
{
|
||
PdfReader::RendererOutputDev oRendererOut(pRenderer, m_pFontManager, m_pFontList);
|
||
oRendererOut.NewPDF(m_pPDFDocument->getXRef());
|
||
oRendererOut.SetBreak(pbBreak);
|
||
m_pPDFDocument->displayPage(&oRendererOut, nPageIndex, 72.0, 72.0, 0, 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.c_str());
|
||
}
|
||
std::wstring CPdfReader::GetTempDirectory()
|
||
{
|
||
return m_wsTempFolder;
|
||
}
|
||
|
||
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 info, obj1;
|
||
m_pPDFDocument->getDocInfo(&info);
|
||
if (info.isDict())
|
||
{
|
||
#define DICT_LOOKUP(sName, wsName) \
|
||
if (info.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"\\\"");\
|
||
size_t nFind = sValue.find(L'\000');\
|
||
while (nFind != std::wstring::npos)\
|
||
{\
|
||
sValue.erase(nFind, 1);\
|
||
nFind = sValue.find(L'\000');\
|
||
}\
|
||
if (!sValue.empty())\
|
||
{\
|
||
sRes += L"\"";\
|
||
sRes += wsName;\
|
||
sRes += L"\":\"";\
|
||
sRes += sValue;\
|
||
sRes += L"\",";\
|
||
}\
|
||
}
|
||
DICT_LOOKUP("Title", L"Title");
|
||
DICT_LOOKUP("Author", L"Author");
|
||
DICT_LOOKUP("Subject", L"Subject");
|
||
DICT_LOOKUP("Keywords", L"Keywords");
|
||
DICT_LOOKUP("Creator", L"Creator");
|
||
DICT_LOOKUP("Producer", L"Producer");
|
||
#define DICT_LOOKUP_DATE(sName, wsName) \
|
||
if (info.dictLookup(sName, &obj1)->isString())\
|
||
{\
|
||
char* str = obj1.getString()->getCString();\
|
||
if (str)\
|
||
{\
|
||
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)\
|
||
sDate += (L".000" + sNoDate.substr(16, 3) + L':' + sNoDate.substr(20, 2));\
|
||
else\
|
||
sDate += L"Z";\
|
||
NSStringExt::Replace(sDate, L"\"", L"\\\"");\
|
||
sRes += L"\"";\
|
||
sRes += wsName;\
|
||
sRes += L"\":\"";\
|
||
sRes += sDate;\
|
||
sRes += L"\",";\
|
||
}\
|
||
delete s;\
|
||
}\
|
||
}
|
||
DICT_LOOKUP_DATE("CreationDate", L"CreationDate");
|
||
DICT_LOOKUP_DATE("ModDate", L"ModDate");
|
||
}
|
||
|
||
info.free();
|
||
obj1.free();
|
||
|
||
std::wstring version = std::to_wstring(m_pPDFDocument->getPDFVersion());
|
||
std::wstring::size_type posDot = version.find('.');
|
||
if (posDot != std::wstring::npos)
|
||
version = version.substr(0, 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 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) && catDict.isDict() && catDict.dictLookup("MarkInfo", &markInfoObj) && markInfoObj.isDict())
|
||
{
|
||
Object marked, suspects;
|
||
if (markInfoObj.dictLookup("Marked", &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) && suspects.isBool() && suspects.getBool() == gTrue)
|
||
bTagged = false;
|
||
}
|
||
marked.free();
|
||
suspects.free();
|
||
}
|
||
markInfoObj.free();
|
||
catDict.free();
|
||
|
||
sRes += bTagged ? L"true}" : L"false}";
|
||
|
||
return sRes;
|
||
}
|
||
|
||
void getBookmars(PDFDoc* pdfDoc, OutlineItem* pOutlineItem, NSWasm::CData& out, int level)
|
||
{
|
||
int nLengthTitle = pOutlineItem->getTitleLength();
|
||
Unicode* pTitle = pOutlineItem->getTitle();
|
||
std::string sTitle = NSStringExt::CConverter::GetUtf8FromUTF32(pTitle, nLengthTitle);
|
||
|
||
LinkAction* pLinkAction = pOutlineItem->getAction();
|
||
if (!pLinkAction)
|
||
return;
|
||
LinkActionKind kind = pLinkAction->getKind();
|
||
if (kind != 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 (dTop > 0 && dTop < dHeight)
|
||
dy = dHeight - dTop;
|
||
if (str)
|
||
RELEASEOBJECT(pLinkDest);
|
||
|
||
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)
|
||
return;
|
||
for (int i = 0, num = pList->getLength(); i < num; i++)
|
||
{
|
||
OutlineItem* pOutlineItemKid = (OutlineItem*)pList->get(i);
|
||
if (!pOutlineItemKid)
|
||
continue;
|
||
getBookmars(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();
|
||
int num = pList->getLength();
|
||
for (int i = 0; i < num; i++)
|
||
{
|
||
OutlineItem* pOutlineItem = (OutlineItem*)pList->get(i);
|
||
if (pOutlineItem)
|
||
getBookmars(m_pPDFDocument, pOutlineItem, oRes, 1);
|
||
}
|
||
oRes.WriteLen();
|
||
|
||
BYTE* bRes = oRes.GetBuffer();
|
||
oRes.ClearWithoutAttack();
|
||
return bRes;
|
||
}
|
||
BYTE* CPdfReader::GetLinks(int nPageIndex)
|
||
{
|
||
if (!m_pPDFDocument)
|
||
return NULL;
|
||
|
||
nPageIndex++;
|
||
|
||
NSWasm::CPageLink oLinks;
|
||
double height = m_pPDFDocument->getPageCropHeight(nPageIndex);
|
||
|
||
// Гиперссылка
|
||
Links* pLinks = m_pPDFDocument->getLinks(nPageIndex);
|
||
if (!pLinks)
|
||
return NULL;
|
||
|
||
int num = pLinks->getNumLinks();
|
||
for (int i = 0; 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);
|
||
y1 = height - y1;
|
||
y2 = height - 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();
|
||
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();
|
||
|
||
oLinks.m_arLinks.push_back({str ? std::string(str->getCString(), str->getLength()) : "", dy, x1, y2, x2 - x1, y1 - y2});
|
||
RELEASEOBJECT(str);
|
||
}
|
||
RELEASEOBJECT(pLinks);
|
||
|
||
// Текст-ссылка
|
||
TextOutputControl textOutControl;
|
||
textOutControl.mode = textOutReadingOrder;
|
||
TextOutputDev* pTextOut = new TextOutputDev(NULL, &textOutControl, gFalse);
|
||
m_pPDFDocument->displayPage(pTextOut, nPageIndex, 72.0, 72.0, 0, 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());
|
||
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(pTextOut);
|
||
|
||
return oLinks.Serialize();
|
||
}
|
||
|
||
BYTE* CPdfReader::GetWidgets(int nPageIndex, int nRasterW, int nRasterH)
|
||
{
|
||
if (!m_pPDFDocument || !m_pPDFDocument->getCatalog())
|
||
return NULL;
|
||
|
||
AcroForm* pAcroForms = m_pPDFDocument->getCatalog()->getForm();
|
||
if (!pAcroForms)
|
||
return NULL;
|
||
|
||
GBool bDrawContent = globalParams->getDrawContent();
|
||
GBool bDrawAnnotations = globalParams->getDrawAnnotations();
|
||
GBool bDrawFormFileds = globalParams->getDrawFormFields();
|
||
|
||
globalParams->setDrawContent(gFalse);
|
||
globalParams->setDrawAnnotations(gFalse);
|
||
globalParams->setDrawFormFields(gTrue);
|
||
|
||
BYTE* pPixData = m_pRenderer->ConvertToPixels(nPageIndex, nRasterW, nRasterH, true, m_pFontManager, 0, false);
|
||
|
||
globalParams->setDrawContent(bDrawContent);
|
||
globalParams->setDrawAnnotations(bDrawAnnotations);
|
||
globalParams->setDrawFormFields(bDrawFormFileds);
|
||
|
||
CBgraFrame oFrame;
|
||
oFrame.put_Data(pPixData);
|
||
oFrame.put_Width(nRasterW);
|
||
oFrame.put_Height(nRasterH);
|
||
oFrame.put_Stride(4 * nRasterW);
|
||
oFrame.put_IsRGBA(true);
|
||
oFrame.SaveFile(NSFile::GetProcessDirectory() + L"/res2.png", _CXIMAGE_FORMAT_PNG);
|
||
oFrame.ClearNoAttack();
|
||
|
||
NSWasm::CData oRes;
|
||
oRes.SkipLen();
|
||
|
||
for (int i = 0, nNum = pAcroForms->getNumFields(); i < nNum; ++i)
|
||
{
|
||
AcroFormField* pField = pAcroForms->getField(i);
|
||
Object oFieldRef, oField;
|
||
XRef* xref = m_pPDFDocument->getXRef();
|
||
if (!xref || !pField->getFieldRef(&oFieldRef) || !oFieldRef.isRef() || !oFieldRef.fetch(xref, &oField) || !oField.isDict() || pField->getPageNum() != nPageIndex + 1)
|
||
{
|
||
oFieldRef.free(); oField.free();
|
||
continue;
|
||
}
|
||
|
||
// Флаг аннотации - F
|
||
Object oFlag;
|
||
int nAnnotFlag = 0;
|
||
if (oField.dictLookup("F", &oFlag) && oFlag.isInt())
|
||
nAnnotFlag = oFlag.getInt();
|
||
oFlag.free();
|
||
oRes.AddInt(nAnnotFlag);
|
||
|
||
// Полное имя поля - T (parent_full_name.child_name)
|
||
int nLengthName;
|
||
Unicode* uName = pField->getName(&nLengthName);
|
||
std::string sTName = NSStringExt::CConverter::GetUtf8FromUTF32(uName, nLengthName);
|
||
oRes.WriteString((BYTE*)sTName.c_str(), (unsigned int)sTName.length());
|
||
gfree(uName);
|
||
|
||
// Координаты - BBox
|
||
double dx1, dy1, dx2, dy2;
|
||
pField->getBBox(&dx1, &dy1, &dx2, &dy2);
|
||
double height = m_pPDFDocument->getPageCropHeight(nPageIndex + 1);
|
||
oRes.AddDouble(dx1);
|
||
oRes.AddDouble(height - dy1);
|
||
oRes.AddDouble(dx2);
|
||
oRes.AddDouble(height - dy2);
|
||
|
||
// Выравнивание текста - Q
|
||
Object oQ;
|
||
pField->fieldLookup("Q", &oQ);
|
||
int nQ = 0;
|
||
if (oQ.isInt())
|
||
nQ = oQ.getInt();
|
||
oQ.free();
|
||
oRes.AddInt(nQ);
|
||
|
||
// Тип - FT + флаги
|
||
AcroFormFieldType oType = pField->getAcroFormFieldType();
|
||
std::string sType;
|
||
switch (oType)
|
||
{
|
||
case acroFormFieldPushbutton: sType = "button"; break;
|
||
case acroFormFieldRadioButton: sType = "radiobutton"; break;
|
||
case acroFormFieldCheckbox: sType = "checkbox"; break;
|
||
case acroFormFieldFileSelect: sType = "text"/*"fileselect"*/; break;
|
||
case acroFormFieldMultilineText: sType = "text"/*"multilinetext"*/; break;
|
||
case acroFormFieldText: sType = "text"; break;
|
||
case acroFormFieldBarcode: sType = "text"/*"barcode"*/; break;
|
||
case acroFormFieldComboBox: sType = "combobox"; break;
|
||
case acroFormFieldListBox: sType = "listbox"; break;
|
||
case acroFormFieldSignature: sType = "signature"; break;
|
||
default: sType = ""; break;
|
||
}
|
||
oRes.WriteString((BYTE*)sType.c_str(), (unsigned int)sType.length());
|
||
|
||
// Цвет - оператор g/rg в DA
|
||
// double r, g, b;
|
||
// pField->getColor(&r, &g, &b);
|
||
|
||
// Шрифт и размер шрифта - операторы Tf и Tm в DA
|
||
Ref pFontRef; double dFontSize;
|
||
pField->getFont(&pFontRef, &dFontSize);
|
||
|
||
// Флаг - Ff
|
||
unsigned int nFieldFlag = pField->getFlags();
|
||
oRes.AddInt(nFieldFlag);
|
||
|
||
int nFlags = 0;
|
||
int nFlagPos = oRes.GetSize();
|
||
oRes.AddInt(nFlags);
|
||
|
||
// 1 - Альтернативное имя поля, используется во всплывающей подсказке и сообщениях об ошибке - TU
|
||
Object oObj;
|
||
#define DICT_LOOKUP_STRING(field, sName, byte) \
|
||
{\
|
||
if (field.dictLookup(sName, &oObj)->isString())\
|
||
{\
|
||
TextString* s = new TextString(oObj.getString());\
|
||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());\
|
||
nFlags |= (1 << byte);\
|
||
oRes.WriteString((BYTE*)sStr.c_str(), (unsigned int)sStr.length());\
|
||
delete s;\
|
||
}\
|
||
oObj.free();\
|
||
}
|
||
DICT_LOOKUP_STRING(oField, "TU", 0);
|
||
|
||
// 2 - Строка стиля по умолчанию - DS
|
||
DICT_LOOKUP_STRING(oField, "DS", 1);
|
||
|
||
// 3 - Эффекты границы - BE
|
||
Object oBE, oBorderBE, oBorderBEI;
|
||
if (oField.dictLookup("BE", &oBE)->isDict() && oBE.dictLookup("S", &oBorderBE)->isName("C") && oBE.dictLookup("I", &oBorderBEI)->isNum())
|
||
{
|
||
nFlags |= (1 << 2);
|
||
oRes.AddDouble(oBorderBEI.getNum());
|
||
}
|
||
oBE.free(); oBorderBE.free(); oBorderBEI.free();
|
||
|
||
// 4 - Режим выделения - H
|
||
Object oHighlighting;
|
||
if (oField.dictLookup("H", &oHighlighting)->isName())
|
||
{
|
||
nFlags |= (1 << 3);
|
||
std::string sName(oHighlighting.getName());
|
||
oRes.WriteString((BYTE*)sName.c_str(), (unsigned int)sName.length());
|
||
}
|
||
oHighlighting.free();
|
||
|
||
// 5 - Границы и Dash Pattern - Border/BS
|
||
Object oBorder;
|
||
unsigned int nBorderType = 5;
|
||
double dBorderWidth = 1, dDashesAlternating = 3, dGaps = 3;
|
||
if (oField.dictLookup("BS", &oBorder)->isDict())
|
||
{
|
||
Object oV;
|
||
if (oBorder.dictLookup("S", &oV)->isName())
|
||
{
|
||
if (oV.isName("S"))
|
||
nBorderType = annotBorderSolid;
|
||
else if (oV.isName("D"))
|
||
nBorderType = annotBorderDashed;
|
||
else if (oV.isName("B"))
|
||
nBorderType = annotBorderBeveled;
|
||
else if (oV.isName("I"))
|
||
nBorderType = annotBorderInset;
|
||
else if (oV.isName("U"))
|
||
nBorderType = annotBorderUnderlined;
|
||
else
|
||
nBorderType = annotBorderSolid;
|
||
}
|
||
oV.free();
|
||
if (oBorder.dictLookup("W", &oV)->isNum())
|
||
dBorderWidth = oV.getNum();
|
||
oV.free();
|
||
if (oBorder.dictLookup("D", &oV)->isArray())
|
||
{
|
||
Object oV1;
|
||
if (oV.arrayGet(0, &oV1)->isNum())
|
||
dDashesAlternating = oV1.getNum();
|
||
oV1.free();
|
||
if (oV.arrayGet(1, &oV1)->isNum())
|
||
dGaps = oV1.getNum();
|
||
else
|
||
dGaps = dDashesAlternating;
|
||
oV1.free();
|
||
}
|
||
oV.free();
|
||
}
|
||
else
|
||
{
|
||
oBorder.free();
|
||
if (oField.dictLookup("Border", &oBorder)->isArray() && oBorder.arrayGetLength() > 2)
|
||
{
|
||
nBorderType = annotBorderSolid;
|
||
Object oV;
|
||
if (oBorder.arrayGet(2, &oV) && oV.isNum())
|
||
dBorderWidth = oV.getNum();
|
||
oV.free();
|
||
|
||
if (oBorder.arrayGetLength() > 3 && oBorder.arrayGet(3, &oV)->isArray() && oV.arrayGetLength() > 1)
|
||
{
|
||
nBorderType = annotBorderDashed;
|
||
Object oV1;
|
||
if (oV.arrayGet(0, &oV1)->isNum())
|
||
dDashesAlternating = oV1.getNum();
|
||
oV1.free();
|
||
if (oV.arrayGet(1, &oV1)->isNum())
|
||
dGaps = oV1.getNum();
|
||
oV1.free();
|
||
}
|
||
oV.free();
|
||
}
|
||
}
|
||
oBorder.free();
|
||
if (nBorderType != 5)
|
||
{
|
||
nFlags |= (1 << 4);
|
||
oRes.AddInt(nBorderType);
|
||
oRes.AddDouble(dBorderWidth);
|
||
if (nBorderType == annotBorderDashed)
|
||
{
|
||
oRes.AddDouble(dDashesAlternating);
|
||
oRes.AddDouble(dGaps);
|
||
}
|
||
}
|
||
|
||
// Значение аннотации из текущего внешнего вида
|
||
/*
|
||
Object oAP, oAppearance;
|
||
if (oField.dictLookup("AP", &oAP)->isDict())
|
||
{
|
||
Object oN;
|
||
oAP.dictLookup("N", &oN);
|
||
if (oN.isDict())
|
||
{
|
||
Object oAS;
|
||
if (oField.dictLookup("AS", &oAS)->isName())
|
||
oN.dictLookup(oAS.getName(), &oAppearance);
|
||
else if (oN.dictGetLength() == 1)
|
||
oN.dictGetVal(0, &oAppearance);
|
||
else
|
||
oN.dictLookup("Off", &oAppearance);
|
||
oAS.free();
|
||
}
|
||
else
|
||
oN.copy(&oAppearance);
|
||
oN.free();
|
||
}
|
||
oAP.free();
|
||
|
||
if (oAppearance.isStream())
|
||
{
|
||
}
|
||
oAppearance.free();
|
||
*/
|
||
|
||
Object oMK;
|
||
if (oField.dictLookup("MK", &oMK) && oMK.isDict())
|
||
{
|
||
// 6 - Цвет границ - BC. Даже если граница не задана BS/Border, то при наличии BC предоставляется граница по-умолчанию (сплошная, толщиной 1)
|
||
if (oMK.dictLookup("BC", &oObj)->isArray())
|
||
{
|
||
nFlags |= (1 << 5);
|
||
int nBCLength = oObj.arrayGetLength();
|
||
oRes.AddInt(nBCLength);
|
||
for (int j = 0; j < nBCLength; ++j)
|
||
{
|
||
Object oBCj;
|
||
oRes.AddDouble(oObj.arrayGet(j, &oBCj)->isNum() ? oBCj.getNum() : 0.0);
|
||
oBCj.free();
|
||
}
|
||
}
|
||
oObj.free();
|
||
|
||
// 7 - Поворот аннотации относительно страницы - R
|
||
if (oMK.dictLookup("R", &oObj)->isInt())
|
||
{
|
||
nFlags |= (1 << 6);
|
||
oRes.AddInt(oObj.getInt());
|
||
}
|
||
oObj.free();
|
||
|
||
// 8 - Цвет фона - BG
|
||
if (oMK.dictLookup("BG", &oObj)->isArray())
|
||
{
|
||
nFlags |= (1 << 7);
|
||
int nBGLength = oObj.arrayGetLength();
|
||
oRes.AddInt(nBGLength);
|
||
for (int j = 0; j < nBGLength; ++j)
|
||
{
|
||
Object oBGj;
|
||
oRes.AddDouble(oObj.arrayGet(j, &oBGj)->isNum() ? oBGj.getNum() : 0.0);
|
||
oBGj.free();
|
||
}
|
||
}
|
||
oObj.free();
|
||
}
|
||
oMK.free();
|
||
|
||
// Значение поля - V
|
||
int nValueLength;
|
||
Unicode* pValue = pField->getValue(&nValueLength);
|
||
std::string sValue = NSStringExt::CConverter::GetUtf8FromUTF32(pValue, nValueLength);
|
||
gfree(pValue);
|
||
|
||
// Запись данных необходимых каждому типу
|
||
switch (oType)
|
||
{
|
||
case acroFormFieldPushbutton:
|
||
case acroFormFieldRadioButton:
|
||
case acroFormFieldCheckbox:
|
||
{
|
||
if (oField.dictLookup("AS", &oObj) && oObj.isName())
|
||
sValue = oObj.getName();
|
||
oObj.free();
|
||
|
||
// 10 - Включено
|
||
if (sValue != "Off")
|
||
nFlags |= (1 << 9);
|
||
|
||
// Если Checkbox/RadioButton наследует Opt, то состояние соответствует порядковому в массиве Kids из Opt
|
||
/*
|
||
int nDepth = 0;
|
||
Object oParentObj;
|
||
oField.dictLookup("Parent", &oParentObj);
|
||
while (oParentObj.isDict() && nDepth < 50)
|
||
{
|
||
Object oOpt, oKids;
|
||
if (oParentObj.dictLookup("Opt", &oOpt) && oOpt.isArray() && oParentObj.dictLookup("Kids", &oKids) && oKids.isArray() && oOpt.arrayGetLength() == oKids.arrayGetLength())
|
||
{
|
||
for (int j = 0, nLength = oOpt.arrayGetLength(); j < nLength; ++j)
|
||
{
|
||
Object oOptJ, oOptI;
|
||
if (oKids.arrayGetNF(j, &oOptJ) && oOptJ.isRef() && oOptJ.getRefGen() == oFieldRef.getRefGen() && oOptJ.getRefNum() == oFieldRef.getRefNum() &&
|
||
oOpt.arrayGet(j, &oOptI) && oOptI.isString())
|
||
{
|
||
TextString* s = new TextString(oOptI.getString());
|
||
sValue = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
if (sValue != "Off")
|
||
nFlags |= (1 << 9);
|
||
delete s;
|
||
}
|
||
oOptJ.free(); oOptI.free();
|
||
}
|
||
}
|
||
oOpt.free(); oKids.free();
|
||
|
||
Object oParentObj2;
|
||
oParentObj.dictLookup("Parent", &oParentObj2);
|
||
oParentObj.free();
|
||
oParentObj = oParentObj2;
|
||
++nDepth;
|
||
}
|
||
oParentObj.free();
|
||
*/
|
||
|
||
Object oMK;
|
||
if (oField.dictLookup("MK", &oMK) && oMK.isDict())
|
||
{
|
||
// 11 - Заголовок - СА
|
||
DICT_LOOKUP_STRING(oMK, "CA", 10);
|
||
|
||
// 12 - Заголовок прокрутки - RC
|
||
// 13 - Альтернативный заголовок - AC
|
||
if (oType == acroFormFieldPushbutton)
|
||
{
|
||
DICT_LOOKUP_STRING(oMK, "RC", 11);
|
||
DICT_LOOKUP_STRING(oMK, "AC", 12);
|
||
}
|
||
|
||
// 14 - Положение заголовка - TP
|
||
if (oMK.dictLookup("TP", &oObj) && oObj.isInt())
|
||
{
|
||
nFlags |= (1 << 13);
|
||
oRes.AddInt(oObj.getInt());
|
||
}
|
||
oObj.free();
|
||
|
||
// TODO Обычный значок - I Значок прокрутки - RI Альтернативный значок - IX Значок соответствия - IF
|
||
}
|
||
oMK.free();
|
||
|
||
// 15 - Имя вкл состояния - AP - N - Yes
|
||
Object oNorm;
|
||
if (oField.dictLookup("AP", &oObj) && oObj.isDict() && oObj.dictLookup("N", &oNorm) && oNorm.isDict())
|
||
{
|
||
for (int j = 0, nNormLength = oNorm.dictGetLength(); j < nNormLength; ++j)
|
||
{
|
||
std::string sNormName(oNorm.dictGetKey(j));
|
||
if (sNormName != "Off")
|
||
{
|
||
nFlags |= (1 << 14);
|
||
oRes.WriteString((BYTE*)sNormName.c_str(), (unsigned int)sNormName.length());
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
oNorm.free(); oObj.free();
|
||
|
||
break;
|
||
}
|
||
case acroFormFieldFileSelect:
|
||
case acroFormFieldMultilineText:
|
||
case acroFormFieldText:
|
||
case acroFormFieldBarcode:
|
||
{
|
||
// 10 - Значение
|
||
// 11 - Максимальное количество символов
|
||
// 12 - Расширенный текст RV
|
||
|
||
if (!sValue.empty())
|
||
{
|
||
nFlags |= (1 << 9);
|
||
oRes.WriteString((BYTE*)sValue.c_str(), (unsigned int)sValue.length());
|
||
}
|
||
|
||
// Максимальное количество символов в Tx - MaxLen
|
||
int nMaxLen = pField->getMaxLen();
|
||
if (nMaxLen > 0)
|
||
{
|
||
nFlags |= (1 << 10);
|
||
oRes.AddInt(nMaxLen);
|
||
}
|
||
|
||
// RichText
|
||
if (nFieldFlag & (1 << 25))
|
||
{
|
||
DICT_LOOKUP_STRING(oField, "RV", 11);
|
||
}
|
||
|
||
break;
|
||
}
|
||
case acroFormFieldComboBox:
|
||
case acroFormFieldListBox:
|
||
{
|
||
// 10 - Значение
|
||
if (!sValue.empty())
|
||
{
|
||
nFlags |= (1 << 9);
|
||
oRes.WriteString((BYTE*)sValue.c_str(), (unsigned int)sValue.length());
|
||
}
|
||
|
||
Object oOpt;
|
||
pField->fieldLookup("Opt", &oOpt);
|
||
// 11 - Список значений
|
||
if (oOpt.isArray())
|
||
{
|
||
nFlags |= (1 << 10);
|
||
int nOptLength = oOpt.arrayGetLength();
|
||
oRes.AddInt(nOptLength);
|
||
for (int j = 0; j < nOptLength; ++j)
|
||
{
|
||
Object oOptJ;
|
||
if (!oOpt.arrayGet(j, &oOptJ) || !(oOptJ.isString() || oOptJ.isArray()))
|
||
{
|
||
oOptJ.free();
|
||
oRes.WriteString(NULL, 0);
|
||
oRes.WriteString(NULL, 0);
|
||
continue;
|
||
}
|
||
|
||
std::string sOpt1, sOpt2;
|
||
if (oOptJ.isArray() && oOptJ.arrayGetLength() > 1)
|
||
{
|
||
Object oOptJ2;
|
||
if (oOptJ.arrayGet(0, &oOptJ2) && oOptJ2.isString())
|
||
{
|
||
TextString* s = new TextString(oOptJ2.getString());
|
||
sOpt1 = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
delete s;
|
||
}
|
||
oOptJ2.free();
|
||
if (oOptJ.arrayGet(1, &oOptJ2) && oOptJ2.isString())
|
||
{
|
||
TextString* s = new TextString(oOptJ2.getString());
|
||
sOpt2 = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
delete s;
|
||
}
|
||
oOptJ2.free();
|
||
}
|
||
else if (oOptJ.isString())
|
||
{
|
||
TextString* s = new TextString(oOptJ.getString());
|
||
sOpt2 = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
delete s;
|
||
}
|
||
oRes.WriteString((BYTE*)sOpt1.c_str(), (unsigned int)sOpt1.length());
|
||
oRes.WriteString((BYTE*)sOpt2.c_str(), (unsigned int)sOpt2.length());
|
||
oOptJ.free();
|
||
}
|
||
}
|
||
oOpt.free();
|
||
|
||
break;
|
||
}
|
||
case acroFormFieldSignature:
|
||
{
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
oFieldRef.free(); oField.free();
|
||
|
||
// 20 - Action - A
|
||
Object oAA;
|
||
pField->fieldLookup("AA", &oAA);
|
||
if (oAA.isDict())
|
||
{
|
||
nFlags |= (1 << 19);
|
||
int nLength = oAA.dictGetLength();
|
||
oRes.AddInt(nLength);
|
||
for (int j = 0; j < nLength; ++j)
|
||
{
|
||
std::string sAA(oAA.dictGetKey(j));
|
||
oRes.WriteString((BYTE*)sAA.c_str(), (unsigned int)sAA.length());
|
||
|
||
Object oAction, oType;
|
||
if (!oAA.dictGetVal(j, &oAction) || !oAction.isDict() || !oAction.dictLookup("S", &oType) || !oType.isName())
|
||
{
|
||
oRes.WriteString(NULL, 0);
|
||
oAction.free();
|
||
oType.free();
|
||
continue;
|
||
}
|
||
std::string sSName(oType.getName());
|
||
oRes.WriteString((BYTE*)sSName.c_str(), (unsigned int)sSName.length());
|
||
|
||
LinkAction* oAct = LinkAction::parseAction(&oAction);
|
||
if (!oAct)
|
||
continue;
|
||
|
||
LinkActionKind kind = oAct->getKind();
|
||
switch (kind)
|
||
{
|
||
// Переход внутри файла
|
||
case actionGoTo:
|
||
{
|
||
GString* str = ((LinkGoTo*)oAct)->getNamedDest();
|
||
LinkDest* pLinkDest = str ? m_pPDFDocument->findDest(str) : ((LinkGoTo*)oAct)->getDest();
|
||
if (!pLinkDest)
|
||
break;
|
||
// 21 - GoTo
|
||
int pg;
|
||
if (pLinkDest->isPageRef())
|
||
{
|
||
Ref pageRef = pLinkDest->getPageRef();
|
||
pg = m_pPDFDocument->findPage(pageRef.num, pageRef.gen);
|
||
}
|
||
else
|
||
pg = pLinkDest->getPageNum();
|
||
nFlags |= (1 << 20);
|
||
std::string sLink = "#" + std::to_string(pg - 1);
|
||
double dy = m_pPDFDocument->getPageCropHeight(pg) - pLinkDest->getTop();
|
||
oRes.WriteString((BYTE*)sLink.c_str(), (unsigned int)sLink.length());
|
||
oRes.AddDouble(dy);
|
||
break;
|
||
}
|
||
// Переход к внешнему файлу
|
||
case actionGoToR:
|
||
{
|
||
break;
|
||
}
|
||
// Запуск стороннего приложения
|
||
case actionLaunch:
|
||
{
|
||
break;
|
||
}
|
||
// Внешняя ссылка
|
||
case actionURI:
|
||
{
|
||
// 21 - URI
|
||
TextString* s = new TextString(((LinkURI*)oAct)->getURI());
|
||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
nFlags |= (1 << 20);
|
||
oRes.WriteString((BYTE*)sStr.c_str(), (unsigned int)sStr.length());
|
||
delete s;
|
||
break;
|
||
}
|
||
// Нестандартно именованные действия
|
||
case actionNamed:
|
||
{
|
||
// 21 - Named
|
||
TextString* s = new TextString(((LinkNamed*)oAct)->getName());
|
||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
nFlags |= (1 << 20);
|
||
oRes.WriteString((BYTE*)sStr.c_str(), (unsigned int)sStr.length());
|
||
delete s;
|
||
break;
|
||
}
|
||
// Воспроизведение фильма
|
||
case actionMovie:
|
||
{
|
||
break;
|
||
}
|
||
// JavaScript
|
||
case actionJavaScript:
|
||
{
|
||
// 21 - JS
|
||
TextString* s = new TextString(((LinkJavaScript*)oAct)->getJS());
|
||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
nFlags |= (1 << 20);
|
||
oRes.WriteString((BYTE*)sStr.c_str(), (unsigned int)sStr.length());
|
||
delete s;
|
||
break;
|
||
}
|
||
// Отправка формы
|
||
case actionSubmitForm:
|
||
{
|
||
break;
|
||
}
|
||
// Скрытие аннотаций
|
||
case actionHide:
|
||
{
|
||
// 21 - Hide
|
||
nFlags |= (1 << 20);
|
||
// 22 - Hide flag
|
||
if (((LinkHide*)oAct)->getHideFlag())
|
||
nFlags |= (1 << 21);
|
||
Object* oHideObj = ((LinkHide*)oAct)->getFields();
|
||
int nHide = 1, k = 0;
|
||
if (oHideObj->isArray())
|
||
nHide = oHideObj->arrayGetLength();
|
||
oRes.AddInt(nHide);
|
||
Object oHide;
|
||
oHideObj->copy(&oHide);
|
||
do
|
||
{
|
||
if (oHideObj->isArray())
|
||
{
|
||
oHide.free();
|
||
oHideObj->arrayGetNF(k, &oHide);
|
||
}
|
||
if (oHide.isString())
|
||
{
|
||
TextString* s = new TextString(oHide.getString());
|
||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||
oRes.WriteString((BYTE*)sStr.c_str(), (unsigned int)sStr.length());
|
||
delete s;
|
||
}
|
||
else if (oHide.isRef())
|
||
{
|
||
for (int m = 0; m < nNum; ++m)
|
||
{
|
||
AcroFormField* pSField = pAcroForms->getField(i);
|
||
Object oSFieldRef;
|
||
if (pSField->getFieldRef(&oSFieldRef) && oSFieldRef.isRef() && oSFieldRef.getRefGen() == oHide.getRefGen() && oSFieldRef.getRefNum() == oHide.getRefNum())
|
||
{
|
||
int nLengthName;
|
||
Unicode* uName = pSField->getName(&nLengthName);
|
||
std::string sTName = NSStringExt::CConverter::GetUtf8FromUTF32(uName, nLengthName);
|
||
oRes.WriteString((BYTE*)sTName.c_str(), (unsigned int)sTName.length());
|
||
gfree(uName);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
k++;
|
||
} while (k < nHide);
|
||
oHide.free();
|
||
break;
|
||
}
|
||
// Неизвестное действие
|
||
case actionUnknown:
|
||
default:
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
oType.free();
|
||
RELEASEOBJECT(oAct);
|
||
oAction.free();
|
||
}
|
||
}
|
||
oAA.free();
|
||
|
||
oRes.AddInt(nFlags, nFlagPos);
|
||
}
|
||
|
||
RELEASEARRAYOBJECTS(pPixData);
|
||
oRes.WriteLen();
|
||
BYTE* bRes = oRes.GetBuffer();
|
||
oRes.ClearWithoutAttack();
|
||
return bRes;
|
||
}
|