Compare commits

..

2 Commits

Author SHA1 Message Date
030cede6ac Create isXFA and getXFA 2026-03-24 13:31:14 +03:00
194cc2b0ad Create XFAScanner 2026-03-19 16:08:41 +03:00
23 changed files with 211 additions and 435 deletions

View File

@ -617,6 +617,19 @@ public:
return ((CPdfFile*)m_pFile)->GetEmbeddedFontPath(sName);
}
bool isXFA()
{
if (0 != m_nType)
return false;
return ((CPdfFile*)m_pFile)->IsXFA();
}
BYTE* getXFA()
{
if (0 != m_nType)
return NULL;
return ((CPdfFile*)m_pFile)->GetXFA();
}
private:
int GetPagesCount()
{

View File

@ -263,6 +263,15 @@ JSSmart<CJSValue> CDrawingFileEmbed::CheckPerm(JSSmart<CJSValue> nPerm)
return CJSContext::createBool(m_pFile->CheckPerm(nPerm->toInt32()));
}
JSSmart<CJSValue> CDrawingFileEmbed::IsXFA()
{
return CJSContext::createBool(m_pFile->isXFA());
}
JSSmart<CJSValue> CDrawingFileEmbed::GetXFA()
{
return WasmMemoryToJS(m_pFile->getXFA());
}
bool EmbedDrawingFile(JSSmart<NSJSBase::CJSContext>& context, IOfficeDrawingFile* pFile)
{
CJSContext::Embed<CDrawingFileEmbed>(false);

View File

@ -62,6 +62,9 @@ public:
JSSmart<CJSValue> CheckOwnerPassword(JSSmart<CJSValue> sPassword);
JSSmart<CJSValue> CheckPerm(JSSmart<CJSValue> nPerm);
JSSmart<CJSValue> IsXFA();
JSSmart<CJSValue> GetXFA();
DECLARE_EMBED_METHODS
};

View File

@ -36,6 +36,8 @@
-(JSValue*) UndoRedact;
-(JSValue*) CheckOwnerPassword : (JSValue*)sPassword;
-(JSValue*) CheckPerm : (JSValue*)nPerm;
-(JSValue*) IsXFA;
-(JSValue*) GetXFA;
@end
@interface CJSCDrawingFileEmbed : NSObject<IJSCDrawingFileEmbed, JSEmbedObjectProtocol>
@ -79,6 +81,8 @@ FUNCTION_WRAPPER_JS_3(RedactPage, RedactPage)
FUNCTION_WRAPPER_JS_0(UndoRedact, UndoRedact)
FUNCTION_WRAPPER_JS_1(CheckOwnerPassword, CheckOwnerPassword)
FUNCTION_WRAPPER_JS_1(CheckPerm, CheckPerm)
FUNCTION_WRAPPER_JS_0(IsXFA, IsXFA)
FUNCTION_WRAPPER_JS_0(GetXFA, GetXFA)
@end
class CDrawingFileEmbedAdapter : public CJSEmbedObjectAdapterJSC

View File

@ -39,6 +39,8 @@ namespace NSDrawingFileEmbed
FUNCTION_WRAPPER_V8_0(_UndoRedact, UndoRedact)
FUNCTION_WRAPPER_V8_1(_CheckOwnerPassword, CheckOwnerPassword)
FUNCTION_WRAPPER_V8_1(_CheckPerm, CheckPerm)
FUNCTION_WRAPPER_V8_0(_IsXFA, IsXFA)
FUNCTION_WRAPPER_V8_0(_GetXFA, GetXFA)
v8::Handle<v8::ObjectTemplate> CreateTemplate(v8::Isolate* isolate)
{
@ -77,6 +79,8 @@ namespace NSDrawingFileEmbed
NSV8Objects::Template_Set(result, "UndoRedact", _UndoRedact);
NSV8Objects::Template_Set(result, "CheckOwnerPassword", _CheckOwnerPassword);
NSV8Objects::Template_Set(result, "CheckPerm", _CheckPerm);
NSV8Objects::Template_Set(result, "IsXFA", _IsXFA);
NSV8Objects::Template_Set(result, "GetXFA", _GetXFA);
return handle_scope.Escape(result);
}

View File

@ -60,6 +60,8 @@
"_UndoRedact",
"_CheckOwnerPassword",
"_CheckPerm",
"_IsXFA",
"_GetXFA",
"_GetImageBase64",
"_GetImageBase64Len",
"_GetImageBase64Ptr",

View File

@ -177,6 +177,30 @@ CFile.prototype["UndoRedact"] = function()
return this._UndoRedact();
};
// XFA
CFile.prototype["isXFA"] = function()
{
if (!this.nativeFile)
return false;
return this._isXFA();
};
CFile.prototype["getXFA"] = function()
{
if (!this.nativeFile)
return {};
let ptr = this._getXFA();
let reader = ptr.getReader();
if (!reader) return {};
let res = {};
res["dynamic"] = reader.readByte() ? true : false;
res["xfa"] = reader.readString();
ptr.free();
return res;
};
// INFO DOCUMENT
CFile.prototype.getInfo = function()
{

View File

@ -184,6 +184,17 @@ CFile.prototype._getInteractiveFormsFonts = function(type)
return g_module_pointer;
};
// XFA
CFile.prototype._isXFA = function()
{
return g_native_drawing_file["IsXFA"]();
};
CFile.prototype._getXFA = function()
{
g_module_pointer.ptr = g_native_drawing_file["GetXFA"]();
return g_module_pointer;
};
// INFO DOCUMENT
CFile.prototype._getInfo = function()
{

View File

@ -305,6 +305,17 @@ CFile.prototype._getInteractiveFormsFonts = function(type)
return g_module_pointer;
};
// XFA
CFile.prototype._isXFA = function()
{
return Module["_IsXFA"](this.nativeFile) ? true : false;
};
CFile.prototype._getXFA = function()
{
g_module_pointer.ptr = Module["_GetXFA"](this.nativeFile);
return g_module_pointer;
};
// INFO DOCUMENT
CFile.prototype._getInfo = function()
{

View File

@ -210,6 +210,14 @@ WASM_EXPORT int CheckPerm(CDrawingFile* pFile, int nPermFlag)
{
return pFile->CheckPerm(nPermFlag) ? 1 : 0;
}
WASM_EXPORT int IsXFA(CDrawingFile* pFile)
{
return pFile->isXFA() ? 1 : 0;
}
WASM_EXPORT BYTE* GetXFA(CDrawingFile* pFile)
{
return pFile->getXFA();
}
WASM_EXPORT void* GetImageBase64(CDrawingFile* pFile, int rId)
{

View File

@ -1194,7 +1194,7 @@ int main(int argc, char* argv[])
}
// RASTER
if (false)
if (true)
{
int i = nTestPage;
//for (int i = 0; i < nPagesCount; ++i)
@ -2333,6 +2333,29 @@ int main(int argc, char* argv[])
ReadInteractiveFormsFonts(pGrFile, 2);
}
// XFA
if (true && IsXFA(pGrFile))
{
BYTE* pXFA = GetXFA(pGrFile);
nLength = READ_INT(pXFA);
int i = 4;
nLength -= 4;
BYTE bD = READ_BYTE(pXFA + i);
i += 1;
std::cout << " XFA: Dynamic " << (bool)bD << std::endl;
int nPathLength = READ_INT(pXFA + i);
i += 4;
NSFile::CFileBinary oFile;
if (oFile.CreateFileW(NSFile::GetProcessDirectory() + L"/XFA.xml"))
oFile.WriteFile(pXFA + i, nPathLength);
oFile.CloseFile();
i += nPathLength;
std::cout << std::endl;
}
Close(pGrFile);
return 0;

View File

@ -453,6 +453,12 @@ bool CPdfFile::CheckPerm(int nPerm)
return false;
return m_pInternal->pReader->CheckPerm(nPerm);
}
bool CPdfFile::IsXFA()
{
if (!m_pInternal->pReader)
return false;
return m_pInternal->pReader->isXFA();
}
int CPdfFile::GetRotate(int nPageIndex)
{
if (!m_pInternal->pReader)
@ -499,6 +505,12 @@ BYTE* CPdfFile::GetLinks(int nPageIndex)
return NULL;
return m_pInternal->pReader->GetLinks(nPageIndex);
}
BYTE* CPdfFile::GetXFA()
{
if (!m_pInternal->pReader)
return NULL;
return m_pInternal->pReader->GetXFA();
}
BYTE* CPdfFile::GetWidgets()
{
if (!m_pInternal->pReader)

View File

@ -145,9 +145,11 @@ public:
bool UndoRedact();
bool CheckOwnerPassword(const wchar_t* sPassword);
bool CheckPerm(int nPerm);
bool IsXFA();
int GetRotate(int nPageIndex);
int GetMaxRefID();
void SetPageFonts(int nPageIndex);
BYTE* GetXFA();
BYTE* GetWidgets();
BYTE* GetAnnotEmbeddedFonts();
BYTE* GetAnnotStandardFonts();

View File

@ -56,6 +56,7 @@
#include "lib/xpdf/TextOutputDev.h"
#include "lib/xpdf/AcroForm.h"
#include "lib/xpdf/SecurityHandler.h"
#include "lib/xpdf/XFAScanner.h"
#include "lib/goo/GList.h"
NSFonts::IFontManager* InitFontManager(NSFonts::IApplicationFonts* pAppFonts)
@ -652,6 +653,20 @@ bool CPdfReader::CheckPerm(int nPerm)
return ownerPasswordOk || (permFlags & (1 << --nPerm));
}
bool CPdfReader::isXFA()
{
PDFDoc* pDoc = m_vPDFContext.front()->m_pDocument;
AcroForm* pAcroForms = pDoc->getCatalog()->getForm();
if (!pAcroForms)
return false;
Object* oAcroForm = pAcroForms->getAcroFormObj();
Object oXFA;
bool bRes = !oAcroForm->dictLookup("XFA", &oXFA)->isNull();
oXFA.free();
return bRes;
}
void CPdfReader::DrawPageOnRenderer(IRenderer* pRenderer, int _nPageIndex, bool* pbBreak)
{
PDFDoc* pDoc = NULL;
@ -1073,6 +1088,40 @@ void getBookmarks(PDFDoc* pdfDoc, OutlineItem* pOutlineItem, NSWasm::CData& out,
}
pOutlineItem->close();
}
BYTE* CPdfReader::GetXFA()
{
PDFDoc* pDoc = m_vPDFContext.front()->m_pDocument;
XRef* xref = pDoc->getXRef();
AcroForm* pAcroForms = pDoc->getCatalog()->getForm();
if (!pAcroForms)
return NULL;
Object* oAcroForm = pAcroForms->getAcroFormObj();
Object oXFA, oCatDict, oNR;
NSWasm::CData oRes;
oRes.SkipLen();
bool bNR = false;
if (xref->getCatalog(&oCatDict)->isDict() && oCatDict.dictLookup("NeedsRendering", &oNR)->isBool())
bNR = !!oNR.getBool();
oRes.WriteBool(bNR);
oCatDict.free(); oNR.free();
if (!oAcroForm->dictLookup("XFA", &oXFA)->isNull())
{
GString* sXFA = XFAScanner::readXFAStreams(&oXFA);
oRes.WriteString((BYTE*)sXFA->getCString(), sXFA->getLength());
}
else
oRes.AddInt(0);
oXFA.free();
oRes.WriteLen();
BYTE* bRes = oRes.GetBuffer();
oRes.ClearWithoutAttack();
return bRes;
}
BYTE* CPdfReader::GetStructure()
{
if (m_vPDFContext.empty())

View File

@ -97,6 +97,7 @@ public:
bool UndoRedact();
bool CheckOwnerPassword(const wchar_t* sPassword);
bool CheckPerm(int nPerm);
bool isXFA();
void GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY);
void DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak);
std::wstring GetInfo();
@ -115,6 +116,7 @@ public:
int GetPageIndex(int nPageIndex, PDFDoc** pDoc = NULL, PdfReader::CPdfFontList** pFontList = NULL, int* nStartRefID = NULL);
void SetFonts(int nPageIndex);
BYTE* GetXFA();
BYTE* GetStructure();
BYTE* GetLinks(int nPageIndex);
BYTE* GetWidgets();

View File

@ -597,9 +597,8 @@ std::wstring GetFontData(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CP
}
else
{
double dStretch = 1.0;
std::wstring wsFBN = wsFontBaseName;
NSFonts::CFontInfo* pFontInfo = RendererOutputDev::GetFontByParams(xref, pFontManager, gfxFont, wsFBN, dStretch);
NSFonts::CFontInfo* pFontInfo = RendererOutputDev::GetFontByParams(xref, pFontManager, gfxFont, wsFBN);
if (pFontInfo && !pFontInfo->m_wsFontPath.empty())
{
EraseSubsetTag(wsFontBaseName);
@ -1065,7 +1064,7 @@ void CollectFontWidths(GfxFont* gfxFont, Dict* pFontDict, std::map<unsigned int,
}
oDescendantFonts.free();
}
double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
void CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
{
EraseSubsetTag(sName);
@ -1073,18 +1072,16 @@ double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
CheckFontNameStyle(sName, L"semibold");
CheckFontNameStyle(sName, L"regular");
double dStretch = 1.0;
CheckFontNameStyle(sName, L"ultraexpanded");
CheckFontNameStyle(sName, L"extraexpanded");
CheckFontNameStyle(sName, L"semiexpanded");
CheckFontNameStyle(sName, L"expanded");
if (CheckFontNameStyle(sName, L"ultraexpanded")) dStretch = 2.0;
if (CheckFontNameStyle(sName, L"extraexpanded")) dStretch = 1.5;
if (CheckFontNameStyle(sName, L"semiexpanded")) dStretch = 1.125;
if (CheckFontNameStyle(sName, L"expanded")) dStretch = 1.25;
if (CheckFontNameStyle(sName, L"ultracondensed")) dStretch = 0.5;
if (CheckFontNameStyle(sName, L"extracondensed")) dStretch = 0.625;
if (CheckFontNameStyle(sName, L"semicondensed")) dStretch = 0.875;
if (CheckFontNameStyle(sName, L"condensedlight")) dStretch = 0.75;
if (CheckFontNameStyle(sName, L"condensed")) dStretch = 0.75;
CheckFontNameStyle(sName, L"ultracondensed");
CheckFontNameStyle(sName, L"extracondensed");
CheckFontNameStyle(sName, L"semicondensed");
CheckFontNameStyle(sName, L"condensedlight");
CheckFontNameStyle(sName, L"condensed");
//CheckFontNameStyle(sName, L"light");
if (CheckFontNameStyle(sName, L"bold_italic")) { bBold = true; bItalic = true; }
@ -1100,8 +1097,6 @@ double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
//if (CheckFontNameStyle(sName, L"bolditalicmt")) { bBold = true; bItalic = true; }
//if (CheckFontNameStyle(sName, L"bolditalic")) { bBold = true; bItalic = true; }
//if (CheckFontNameStyle(sName, L"boldoblique")) { bBold = true; bItalic = true; }
return dStretch;
}
bool EraseSubsetTag(std::wstring& sFontName)
{

View File

@ -65,7 +65,7 @@ std::vector<CAnnotFontInfo> GetAnnotFontInfos(PDFDoc* pdfDoc, NSFonts::IFontMana
std::map<std::wstring, std::wstring> GetFreeTextFont(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontList* pFontList, Object* oAnnotRef, std::vector<CAnnotMarkup::CFontData*>& arrRC);
bool FindFonts(Object* oStream, int nDepth, Object* oResFonts);
void CollectFontWidths(GfxFont* gfxFont, Dict* pFontDict, std::map<unsigned int, unsigned int>& mGIDToWidth);
double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic);
void CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic);
bool EraseSubsetTag(std::wstring& sFontName);
}

View File

@ -77,12 +77,12 @@
namespace PdfReader
{
double CheckFontNamePDF(std::wstring& sName, NSFonts::CFontSelectFormat* format)
void CheckFontNamePDF(std::wstring& sName, NSFonts::CFontSelectFormat* format)
{
bool bBold = false;
bool bItalic = false;
double dStretch = CheckFontStylePDF(sName, bBold, bItalic);
CheckFontStylePDF(sName, bBold, bItalic);
if (format)
{
@ -91,20 +91,6 @@ namespace PdfReader
if (bItalic)
format->bItalic = new INT(1);
}
return dStretch;
}
USHORT StretchToWidthClass(double fStretch)
{
if (fStretch <= 0.50) return 1; // Ultra-condensed
if (fStretch <= 0.625) return 2; // Extra-condensed
if (fStretch <= 0.75) return 3; // Condensed
if (fStretch <= 0.875) return 4; // Semi-condensed
if (fStretch <= 1.0) return 5; // Normal
if (fStretch <= 1.125) return 6; // Semi-expanded
if (fStretch <= 1.25) return 7; // Expanded
if (fStretch <= 1.50) return 8; // Extra-expanded
return 9; // Ultra-expanded
}
void Transform(double* pMatrix, double dUserX, double dUserY, double* pdDeviceX, double* pdDeviceY)
@ -752,7 +738,7 @@ namespace PdfReader
{
}
NSFonts::CFontInfo* RendererOutputDev::GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName, double& dStretch)
NSFonts::CFontInfo* RendererOutputDev::GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName)
{
NSFonts::CFontInfo* pFontInfo = NULL;
if (!pFontManager)
@ -765,9 +751,7 @@ namespace PdfReader
oRefObject.free();
NSFonts::CFontSelectFormat oFontSelect;
dStretch = CheckFontNamePDF(wsFontBaseName, &oFontSelect);
if (std::abs(dStretch - 1.0f) > 1e-5f)
oFontSelect.usWidth = new USHORT(StretchToWidthClass(dStretch));
CheckFontNamePDF(wsFontBaseName, &oFontSelect);
if (oFontObject.isDict())
{
Dict* pFontDict = oFontObject.getDict();
@ -841,7 +825,7 @@ namespace PdfReader
oDictItem.free();
oFontDescriptor.dictLookup("StemV", &oDictItem);
if (oDictItem.isNum() && !oFontSelect.usWidth)
if (oDictItem.isNum())
{
double dStemV = oDictItem.getNum();
if (dStemV > 50.5)
@ -900,7 +884,6 @@ namespace PdfReader
wsFontBaseName = L"Helvetica";
const BYTE* pData14 = NULL;
unsigned int nSize14 = 0;
double dStretch = 1.0;
#ifdef FONTS_USE_ONLY_MEMORY_STREAMS
CMemoryFontStream oMemoryFontStream;
#endif
@ -1211,7 +1194,7 @@ namespace PdfReader
else if (!pFont->locateFont(pXref, false) ||
(wsFileName = NSStrings::GetStringFromUTF32(pFont->locateFont(pXref, false)->path)).length() == 0)
{
NSFonts::CFontInfo* pFontInfo = GetFontByParams(pXref, pFontManager, pFont, wsFontBaseName, dStretch);
NSFonts::CFontInfo* pFontInfo = GetFontByParams(pXref, pFontManager, pFont, wsFontBaseName);
if (pFontInfo && L"" != pFontInfo->m_wsFontPath)
{
@ -1610,7 +1593,6 @@ namespace PdfReader
pEntry->unLenGID = (unsigned int)nLen;
pEntry->unLenUnicode = (unsigned int)nToUnicodeLen;
pEntry->bAvailable = true;
pEntry->dStretch = dStretch;
pEntry->bFontSubstitution = bFontSubstitution;
pEntry->bIsIdentity = pFont->isCIDFont() == gTrue ? ((GfxCIDFont*)pFont)->usesIdentityEncoding() || ((GfxCIDFont*)pFont)->usesIdentityCIDToGID() || ((GfxCIDFont*)pFont)->ctuUsesCharCodeToUnicode() || pFont->getType() == fontCIDType0C : false;
}
@ -2511,12 +2493,6 @@ namespace PdfReader
}
}
if (oEntry.dStretch != 1.0 && oEntry.bFontSubstitution)
{
arrMatrix[0] *= oEntry.dStretch;
arrMatrix[1] *= oEntry.dStretch;
}
double dShiftX = 0, dShiftY = 0;
DoTransform(arrMatrix, &dShiftX, &dShiftY, true);

View File

@ -57,7 +57,6 @@ namespace PdfReader
bool bAvailable; // Доступен ли шрифт. Сделано для многопотоковости
bool bFontSubstitution = false;
bool bIsIdentity = false;
double dStretch = 1.0;
};
@ -245,7 +244,7 @@ namespace PdfReader
{
m_pbBreak = pbBreak;
}
static NSFonts::CFontInfo* GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName, double& dStretch);
static NSFonts::CFontInfo* GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName);
static void GetFont(XRef* pXref, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList, GfxFont* pFont, std::wstring& wsFileName, std::wstring& wsFontName, bool bNotFullName = true);
private:

View File

@ -343,13 +343,15 @@ AcroForm *AcroForm::load(PDFDoc *docA, Catalog *catalog, Object *acroFormObjA) {
AcroFormField *field;
Object xfaObj, fieldsObj, annotsObj, annotRef, annotObj, obj1, obj2;
int pageNum, i, j;
GString* sXFA = NULL;
// this is the normal case: acroFormObj is a dictionary, as expected
if (acroFormObjA->isDict()) {
acroForm = new AcroForm(docA, acroFormObjA);
if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
acroForm->xfaScanner = XFAScanner::load(&xfaObj);
if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
sXFA = XFAScanner::readXFAStreams(&xfaObj);
acroForm->xfaScanner = NULL;
if (!catalog->getNeedsRendering()) {
acroForm->isStaticXFA = gTrue;
}
@ -719,7 +721,7 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
i0 = i1;
}
}
xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
delete xfaName;
}
@ -743,9 +745,9 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
typeA = acroFormFieldCheckbox;
}
} else if (!typeStr->cmp("Tx")) {
if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
typeA = acroFormFieldBarcode;
} else if (flagsA & acroFormFlagFileSelect) {
if (xfaFieldA && xfaFieldA->getBarcodeInfo()) {
typeA = acroFormFieldBarcode;
} else if (flagsA & acroFormFlagFileSelect) {
typeA = acroFormFieldFileSelect;
} else if (flagsA & acroFormFlagMultiline) {
typeA = acroFormFieldMultilineText;
@ -860,9 +862,9 @@ Unicode *AcroFormField::getValue(int *length) {
// from the XFA field (NB: an XFA field with no value overrides the
// AcroForm value)
if (xfaField) {
if (xfaField->getValue()) {
u = utf8ToUnicode(xfaField->getValue(), length);
}
if (xfaField->getValue()) {
u = utf8ToUnicode(xfaField->getValue(), length);
}
// no XFA form - take the AcroForm value
} else {
@ -1221,7 +1223,7 @@ void AcroFormField::drawAnnot(int pageNum, Gfx *gfx, GBool printing,
if (acroForm->needAppearances) {
render = gTrue;
} else if (xfaField && xfaField->getValue()) {
render = gTrue;
render = gTrue;
} else {
if (!annotObj->dictLookup("AP", &obj1)->isDict()) {
render = gTrue;

View File

@ -125,6 +125,7 @@ class XFAScanner {
public:
static XFAScanner *load(Object *xfaObj);
static GString *readXFAStreams(Object *xfaObj);
virtual ~XFAScanner();
@ -135,7 +136,6 @@ public:
private:
XFAScanner();
static GString *readXFAStreams(Object *xfaObj);
GHash *scanFormValues(ZxElement *xmlRoot);
void scanFormNode(ZxElement *elem, GString *fullName,
GHash *formValues);

View File

@ -293,7 +293,7 @@ Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
XRef::XRef(BaseStream *strA, GBool repair) {
GFileOffset pos;
Object obj, root, rootType;
Object obj;
XRefPosSet *posSet;
int i;
@ -370,17 +370,6 @@ XRef::XRef(BaseStream *strA, GBool repair) {
if (obj.isRef()) {
rootNum = obj.getRefNum();
rootGen = obj.getRefGen();
fetch(rootNum, rootGen, &root);
if (!root.isDict() || !root.dictLookup("Type", &rootType)->isName("Catalog"))
{
root.free(); rootType.free();
errCode = errDamaged;
ok = gFalse;
return;
}
root.free(); rootType.free();
obj.free();
} else {
obj.free();
@ -983,39 +972,6 @@ GBool XRef::constructXRef() {
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
return gFalse;
}
// validate the catalog object found by the initial xref scan
if (!quickCheckCatalog(str, entries[rootNum].offset + start)) {
error(errSyntaxWarning, -1, "invalid Catalog, trying repair scan");
// reset state before repair scan
rootNum = -1;
rootGen = -1;
gfree(entries);
entries = NULL;
size = 0;
last = -1;
for (int i = 0; i < xrefCacheSize; ++i) {
if (cache[i].num >= 0) {
cache[i].num = -1;
cache[i].obj.free();
}
}
for (int i = 0; i < objStrCacheLength; ++i) {
delete objStrs[i];
objStrs[i] = NULL;
}
objStrCacheLength = 0;
gfree(xrefTablePos);
xrefTablePos = NULL;
xrefTablePosLen = 0;
if (!trailerDict.isNone()) trailerDict.free();
return constructXRefRepair();
}
return gTrue;
}
@ -1060,316 +1016,6 @@ GBool XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) {
return bRes;
}
// quick check: is the object at the given offset a /Type /Catalog dict?
GBool XRef::quickCheckCatalog(BaseStream *str, GFileOffset pos) {
if (pos < 0) return gFalse;
char buf[512];
str->setPos(pos);
int n = str->getBlock(buf, sizeof(buf) - 1);
if (n <= 0) return gFalse;
buf[n] = '\0';
// skip "N G obj"
char *p = strstr(buf, " obj");
if (!p) return gFalse;
p += 4;
// look for /Type key
char *t = strstr(p, "/Type");
if (!t || t - buf > 450) return gFalse;
t += 5;
// skip whitespace between /Type and its value
while (*t == ' ' || *t == '\t' || *t == '\r' || *t == '\n') ++t;
// value may be "/Catalog" or "/ Catalog" — skip the slash
if (*t == '/') ++t;
while (*t == ' ' || *t == '\t') ++t;
return strncmp(t, "Catalog", 7) == 0;
}
GFileOffset XRef::findValidCutoff(XRefTempEntry *tempEntries, int tempSize, std::vector<XRefTrailerCandidate> &candidates) {
for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) {
XRefTrailerCandidate &c = *it;
GFileOffset rootPos = c.rootObjPos;
if (rootPos < 0 && c.rootNum < tempSize && tempEntries[c.rootNum].used)
rootPos = tempEntries[c.rootNum].offset;
if (rootPos < 0) continue;
if (quickCheckCatalog(str, rootPos + start)) {
return (c.sectionEnd >= 0) ? c.sectionEnd : c.trailerPos;
}
}
return -1;
}
GBool XRef::constructXRefRepair() {
int tempSize = 0;
XRefTempEntry *tempEntries = NULL;
auto ensureTemp = [&](int num) {
if (num >= tempSize) {
int newSize = num + 256;
tempEntries = (XRefTempEntry *)greallocn(
tempEntries, newSize, sizeof(XRefTempEntry));
for (int i = tempSize; i < newSize; ++i) {
tempEntries[i].offset = -1;
tempEntries[i].gen = 0;
tempEntries[i].used = gFalse;
}
tempSize = newSize;
}
};
std::vector<XRefTrailerCandidate> candidates;
int tmpStreamEndsSize = 0;
int tmpStreamEndsLen = 0;
GFileOffset *tmpStreamEnds = NULL;
// ---------- PASS 1 ----------
{
char buf[4096 + 1];
str->reset();
GFileOffset bufPos = start;
char *p = buf;
char *end = buf;
GBool startOfLine = gTrue;
GBool eof = gFalse;
GFileOffset sectionStart = start;
while (1) {
if (end - p < 256 && !eof) {
memmove(buf, p, end - p);
bufPos += p - buf;
p = buf + (end - p);
int n = (int)(buf + 4096 - p);
int m = str->getBlock(p, n);
end = p + m;
*end = '\0';
p = buf;
eof = m < n;
}
if (p == end && eof) break;
GFileOffset curPos = (GFileOffset)(bufPos + (p - buf));
// %%EOF — close current section
if (startOfLine && p[0] == '%' && !strncmp(p, "%%EOF", 5)) {
for (auto &c : candidates)
if (c.sectionEnd < 0 && c.sectionStart == sectionStart)
c.sectionEnd = curPos;
sectionStart = curPos + 5;
p += 5;
startOfLine = gFalse;
continue;
}
if (startOfLine && !strncmp(p, "endstream", 9)) {
if (tmpStreamEndsLen == tmpStreamEndsSize) {
tmpStreamEndsSize += 64;
tmpStreamEnds = (GFileOffset *)greallocn(
tmpStreamEnds, tmpStreamEndsSize, sizeof(GFileOffset));
}
tmpStreamEnds[tmpStreamEndsLen++] = curPos;
p += 9;
startOfLine = gFalse;
continue;
}
if (startOfLine && !strncmp(p, "trailer", 7)) {
GFileOffset tPos = (GFileOffset)(bufPos + (p + 7 - buf));
Object tDict, obj;
obj.initNull();
Parser *parser = new Parser(
NULL,
new Lexer(NULL, str->makeSubStream(tPos, gFalse, 0, &obj)),
gFalse);
parser->getObj(&tDict);
if (tDict.isDict()) {
Object rootRef;
tDict.getDict()->lookupNF("Root", &rootRef);
if (rootRef.isRef()) {
int rNum = rootRef.getRefNum();
ensureTemp(rNum);
XRefTrailerCandidate c;
c.trailerPos = tPos;
c.sectionStart = sectionStart;
c.sectionEnd = -1;
c.rootNum = rNum;
c.rootGen = rootRef.getRefGen();
// rootObjPos — берём то что уже видели к этому моменту
c.rootObjPos = tempEntries[rNum].used
? tempEntries[rNum].offset
: -1;
candidates.push_back(c);
}
rootRef.free();
}
tDict.free();
delete parser;
p += 7;
startOfLine = gFalse;
continue;
}
// N G obj — write to tempEntries only
if (startOfLine && *p >= '0' && *p <= '9') {
char *q = p;
int num = 0, gen = 0;
do { num = num * 10 + (*q - '0'); ++q; }
while (*q >= '0' && *q <= '9' && num < 100000000);
if (*q == '\t' || *q == '\x0c' || *q == ' ') {
do { ++q; } while (*q == '\t' || *q == '\x0c' || *q == ' ');
if (*q >= '0' && *q <= '9') {
do { gen = gen * 10 + (*q - '0'); ++q; }
while (*q >= '0' && *q <= '9' && gen < 100000000);
if ((*q == '\t' || *q == '\x0c' || *q == ' ') &&
!strncmp(q + 1, "obj", 3)) {
ensureTemp(num);
// will be overwritten — pass 2 is bounded by cutoff
tempEntries[num].offset = curPos - start;
tempEntries[num].gen = gen;
tempEntries[num].used = gTrue;
}
}
}
while (*p && *p != '\n' && *p != '\r') ++p;
startOfLine = gFalse;
continue;
}
startOfLine = (*p == '\n' || *p == '\r');
++p;
}
} // end of pass 1
// ---------- VALIDATION ----------
GFileOffset cutoffPos = findValidCutoff(tempEntries, tempSize, candidates);
gfree(tempEntries);
gfree(tmpStreamEnds);
if (cutoffPos < 0) {
error(errSyntaxWarning, -1, "no valid Catalog found, scanning entire file");
cutoffPos = str->getPos();
}
// ---------- PASS 2 ----------
// identical to original constructXRef, but stops at curPos >= cutoffPos
int streamObjNumsSize = 0;
int streamObjNumsLen = 0;
int *streamObjNums = NULL;
int lastObjNum = -1;
rootNum = -1;
gfree(streamEnds);
streamEnds = NULL;
streamEndsLen = 0;
int streamEndsSize = 0;
{
char buf[4096 + 1];
str->reset();
GFileOffset bufPos = start;
char *p = buf;
char *end = buf;
GBool startOfLine = gTrue;
GBool eof = gFalse;
while (1) {
if (end - p < 256 && !eof) {
memmove(buf, p, end - p);
bufPos += p - buf;
p = buf + (end - p);
int n = (int)(buf + 4096 - p);
int m = str->getBlock(p, n);
end = p + m;
*end = '\0';
p = buf;
eof = m < n;
}
if (p == end && eof) break;
GFileOffset curPos = (GFileOffset)(bufPos + (p - buf));
if (curPos - start >= cutoffPos)
break;
if (startOfLine && !strncmp(p, "trailer", 7)) {
constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf)));
p += 7;
startOfLine = gFalse;
} else if (startOfLine && !strncmp(p, "endstream", 9)) {
if (streamEndsLen == streamEndsSize) {
streamEndsSize += 64;
streamEnds = (GFileOffset *)greallocn(
streamEnds, streamEndsSize, sizeof(GFileOffset));
}
streamEnds[streamEndsLen++] = curPos;
p += 9;
startOfLine = gFalse;
} else if (startOfLine && *p >= '0' && *p <= '9') {
p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)),
&lastObjNum);
startOfLine = gFalse;
} else if (p[0] == '>' && p[1] == '>') {
p += 2;
startOfLine = gFalse;
while (*p == '\t' || *p == '\n' || *p == '\x0c' ||
*p == '\r' || *p == ' ') {
startOfLine = (*p == '\n' || *p == '\r');
++p;
}
if (!strncmp(p, "stream", 6)) {
if (lastObjNum >= 0) {
if (streamObjNumsLen == streamObjNumsSize) {
streamObjNumsSize += 64;
streamObjNums = (int *)greallocn(
streamObjNums, streamObjNumsSize, sizeof(int));
}
streamObjNums[streamObjNumsLen++] = lastObjNum;
}
p += 6;
startOfLine = gFalse;
}
} else {
startOfLine = (*p == '\n' || *p == '\r');
++p;
}
}
} // end of pass 2
GBool bRoot = gFalse;
for (int i = 0; i < streamObjNumsLen; ++i) {
Object obj;
fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj);
if (obj.isStream()) {
Dict *dict = obj.streamGetDict();
Object type;
dict->lookup("Type", &type);
if (type.isName("XRef") && !bRoot) {
bRoot = saveTrailerDict(dict, gTrue);
} else if (type.isName("ObjStm")) {
constructObjectStreamEntries(&obj, streamObjNums[i]);
}
type.free();
}
obj.free();
}
gfree(streamObjNums);
if (rootNum < 0) {
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
return gFalse;
}
return gTrue;
}
// Look for an object header ("nnn ggg obj") at [p]. The first
// character at *[p] is a digit. [pos] is the position of *[p].
char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) {

View File

@ -10,7 +10,6 @@
#define XREF_H
#include <aconf.h>
#include <vector>
#ifdef USE_GCC_PRAGMAS
#pragma interface
@ -51,21 +50,6 @@ struct XRefCacheEntry {
Object obj;
};
struct XRefTempEntry {
GFileOffset offset;
int gen;
GBool used;
};
struct XRefTrailerCandidate {
GFileOffset trailerPos;
GFileOffset sectionStart;
GFileOffset sectionEnd;
GFileOffset rootObjPos; // Root object offset at the time the trailer was encountered
int rootNum;
int rootGen;
};
#define xrefCacheSize 16
#define objStrCacheSize 128
@ -195,9 +179,6 @@ private:
void constructObjectStreamEntries(Object *objStr, int objStrObjNum);
GBool constructXRefEntry(int num, int gen, GFileOffset pos,
XRefEntryType type);
GBool constructXRefRepair();
GFileOffset findValidCutoff(XRefTempEntry *tempEntries, int tempSize, std::vector<XRefTrailerCandidate> &candidates);
static GBool quickCheckCatalog(BaseStream *str, GFileOffset pos);
GBool getObjectStreamObject(int objStrNum, int objIdx,
int objNum, Object *obj);
ObjectStream *getObjectStream(int objStrNum);