Compare commits

..

10 Commits

Author SHA1 Message Date
d18d3d1bdf Add FileID 2026-03-27 16:22:12 +03:00
920542cd78 Add const to link comparison 2026-03-27 16:21:58 +03:00
bd4d5ec1e9 Skip non-uri hyperlinks 2026-03-26 15:55:53 +03:00
b49997e615 From PdfLink to docx 2026-03-26 01:11:01 +03:00
d4b4fc04d9 Create PdfLink 2026-03-25 16:55:22 +03:00
78219fcba1 Add recording hyperlink for CShape 2026-03-23 20:12:25 +03:00
fa3ae4e2e2 Add recording hyperlinks 2026-03-23 20:11:40 +03:00
e69b1cf2d6 Add CLink for hyperlink 2026-03-23 20:09:11 +03:00
8870516a8a Fix bug 68012 2026-03-23 16:07:38 +03:00
8fa5f8944e Fix bug 56081 2026-03-20 14:56:53 +03:00
40 changed files with 782 additions and 214 deletions

View File

@ -505,7 +505,13 @@ public:
}
if (m_nType == 0)
{
std::vector<CPdfLink*> arrLinks = ((CPdfFile*)m_pFile)->GetPdfLinks(nPageIndex);
// TODO Usage
for (int i = 0; i < arrLinks.size(); ++i)
RELEASEOBJECT(arrLinks[i]);
((CPdfFile*)m_pFile)->SetPageFonts(nPageIndex);
}
BYTE* res = oRes.GetBuffer();
oRes.ClearWithoutAttack();
@ -617,19 +623,6 @@ 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,15 +263,6 @@ 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,9 +62,6 @@ 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,8 +36,6 @@
-(JSValue*) UndoRedact;
-(JSValue*) CheckOwnerPassword : (JSValue*)sPassword;
-(JSValue*) CheckPerm : (JSValue*)nPerm;
-(JSValue*) IsXFA;
-(JSValue*) GetXFA;
@end
@interface CJSCDrawingFileEmbed : NSObject<IJSCDrawingFileEmbed, JSEmbedObjectProtocol>
@ -81,8 +79,6 @@ 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,8 +39,6 @@ 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)
{
@ -79,8 +77,6 @@ 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,8 +60,6 @@
"_UndoRedact",
"_CheckOwnerPassword",
"_CheckPerm",
"_IsXFA",
"_GetXFA",
"_GetImageBase64",
"_GetImageBase64Len",
"_GetImageBase64Ptr",

View File

@ -177,30 +177,6 @@ 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,17 +184,6 @@ 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,17 +305,6 @@ 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,14 +210,6 @@ 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 (true)
if (false)
{
int i = nTestPage;
//for (int i = 0; i < nPagesCount; ++i)
@ -2333,29 +2333,6 @@ 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

@ -141,3 +141,8 @@ void IOfficeDrawingFile::ConvertToRaster(int nPageIndex, const std::wstring& pat
pFrame->SaveFile(path, nImageType);
RELEASEOBJECT(pFrame);
}
std::vector<CPdfLink*> IOfficeDrawingFile::GetPdfLinks(int nPageIndex)
{
return std::vector<CPdfLink*>();
}

View File

@ -56,6 +56,20 @@ struct COfficeDrawingPageParams
}
};
struct CPdfLink
{
~CPdfLink() { RELEASEOBJECT(pNext); }
double pRect[4];
BYTE nType;
BYTE nKind;
unsigned int unPage;
unsigned int unKindFlag;
double pData[4];
std::string sData;
CPdfLink* pNext = NULL;
};
class GRAPHICS_DECL IOfficeDrawingFile
{
public:
@ -103,6 +117,8 @@ public:
virtual std::wstring GetInfo() = 0;
virtual unsigned char* GetStructure() = 0;
virtual unsigned char* GetLinks(int nPageIndex) = 0;
virtual std::vector<CPdfLink*> GetPdfLinks(int nPageIndex);
};
#endif // _OFFICE_DRAWING_FILE_H

View File

@ -204,6 +204,30 @@ void CDocxRenderer::DrawPage(IOfficeDrawingFile* pFile, size_t nPage)
put_Width(dWidth);
put_Height(dHeight);
if (pFile->GetType() == OfficeDrawingFileType::odftPDF)
{
std::vector<CPdfLink*> arrLinks = pFile->GetPdfLinks(nPage);
for (int i = 0; i < arrLinks.size(); ++i)
{
LONG lType = arrLinks[i]->nType;
double x1, y1, x2, y2;
x1 = arrLinks[i]->pRect[0] / c_dMMToPix;
y1 = arrLinks[i]->pRect[1] / c_dMMToPix;
x2 = arrLinks[i]->pRect[2] / c_dMMToPix;
y2 = arrLinks[i]->pRect[3] / c_dMMToPix;
std::wstring wsData;
if (lType == 1 || lType == 9)
continue; // TODO: GoTo bookmarks or anchors
if (lType == 6)
wsData = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)arrLinks[i]->sData.c_str(), arrLinks[i]->sData.size());
m_pInternal->m_oDocument.AddLink(lType, x1, y1, x2, y2, wsData);
RELEASEOBJECT(arrLinks[i]);
}
m_pInternal->m_oDocument.m_oImageManager.UpdateId(arrLinks.size());
}
pFile->DrawPageOnRenderer(this, nPage, nullptr);
m_pInternal->m_oDocument.m_bIsDisablePageCommand = false;

View File

@ -30,6 +30,7 @@ LIBS += \
HEADERS += \
src/logic/elements/BaseItem.h \
src/logic/elements/ContText.h \
src/logic/elements/Link.h \
src/logic/elements/Paragraph.h \
src/logic/elements/Shape.h \
src/logic/elements/Table.h \
@ -55,6 +56,7 @@ HEADERS += \
SOURCES += \
src/logic/elements/BaseItem.cpp \
src/logic/elements/ContText.cpp \
src/logic/elements/Link.cpp \
src/logic/elements/Paragraph.cpp \
src/logic/elements/Shape.cpp \
src/logic/elements/Table.cpp \

View File

@ -744,6 +744,18 @@ namespace NSDocxRenderer
m_oCurrentPage.m_lClipMode = lMode;
return S_OK;
}
HRESULT CDocument::AddLink(const LONG& lType, const double& x1, const double y1, const double& x2, const double& y2, const std::wstring& wsData)
{
CLink oLink;
UINT nId = m_oImageManager.GetId() + m_arLinks.size();
oLink.AddLink(nId, lType, wsData);
oLink.AddBBox(x1, y1, x2, y2);
m_arLinks.push_back(oLink);
m_oCurrentPage.AddLink(oLink);
return S_OK;
}
void CDocument::ApplyTransform(double d1, double d2, double d3, double d4, double d5, double d6)
{
@ -912,6 +924,17 @@ namespace NSDocxRenderer
oWriter.WriteString(L"\"/>");
}
for (const auto& pLink : m_arLinks)
{
oWriter.WriteString(L"<Relationship Id=\"rId");
oWriter.AddUInt(c_iStartingIdForLinks + pLink.m_nId);
oWriter.WriteString(L"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"");
oWriter.WriteString(pLink.m_wsData);
oWriter.WriteString(L"\" TargetMode=\"");
oWriter.WriteString(pLink.m_eType == eLinkType::ltUri ? L"External" : L"Internal");
oWriter.WriteString(L"\"/>");
}
oWriter.WriteString(L"</Relationships>");
NSFile::CFileBinary::SaveToFile(m_strTempDirectory + L"/word/_rels/document.xml.rels", oWriter.GetData());

View File

@ -44,6 +44,7 @@ namespace NSDocxRenderer
NSStringUtils::CStringBuilder m_oPageBuilder;
std::list<std::string> m_arXmlString;
std::list<CLink> m_arLinks;
public:
CDocument(IRenderer* pRenderer, NSFonts::IApplicationFonts* pFonts);
@ -178,6 +179,8 @@ namespace NSDocxRenderer
HRESULT get_ClipMode(LONG* plMode);
HRESULT put_ClipMode(LONG lMode);
HRESULT AddLink(const LONG& lType, const double& x1, const double y1, const double& x2, const double& y2, const std::wstring& wsData);
protected:
void ApplyTransform(double d1, double d2, double d3, double d4, double d5, double d6);
void ApplyTransform2(double dAngle, double dLeft, double dTop, double dWidth, double dHeight, DWORD lFlags);

View File

@ -273,6 +273,18 @@ namespace NSDocxRenderer
shape->m_oBrush.Color1 == c_iWhiteColor && !info)
return;
for (const auto& link : m_arLinks)
{
if (fabs(shape->m_dTop - link.m_dTop) <= c_dLINK_X_OFFSET_MM &&
fabs(shape->m_dLeft - link.m_dLeft) <= c_dLINK_X_OFFSET_MM &&
fabs(shape->m_dBot - link.m_dBottom) <= c_dLINK_X_OFFSET_MM &&
fabs(shape->m_dRight - link.m_dRight) <= c_dLINK_X_OFFSET_MM)
{
shape->m_bIsHyperlink = true;
shape->m_nRid = c_iStartingIdForLinks + link.m_nId;
}
}
if (!skip_shape)
{
shape->m_nOrder = ++m_nCurrentOrder;
@ -488,6 +500,10 @@ namespace NSDocxRenderer
{
m_arCompleteObjectsBinBase64.push_back(oBase64);
}
void CPage::AddLink(const CLink& oLink)
{
m_arLinks.push_back(oLink);
}
void CPage::ReorderShapesForPptx()
{
// переместим nullptr в конец и удалим

View File

@ -3,6 +3,7 @@
#include "elements/Paragraph.h"
#include "elements/Table.h"
#include "elements/Shape.h"
#include "elements/Link.h"
#include "managers/ImageManager.h"
#include "managers/FontStyleManager.h"
#include "managers/ParagraphStyleManager.h"
@ -91,6 +92,8 @@ namespace NSDocxRenderer
void AddCompleteXml(const std::wstring& oXml);
void AddCompleteBinBase64(const std::string& oBase64);
void AddLink(const CLink& pLink);
private:
using shape_ptr_t = std::shared_ptr<CShape>;
using cont_ptr_t = std::shared_ptr<CContText>;
@ -235,6 +238,8 @@ namespace NSDocxRenderer
std::vector<shape_ptr_t> m_arLuminosityShapes;
std::vector<shape_ptr_t> m_arOneColorGradientShape;
std::list<CLink> m_arLinks;
long m_lLastType = 0;
size_t m_nCurrentOrder = 0;

View File

@ -0,0 +1,31 @@
#include "Link.h"
namespace NSDocxRenderer
{
CLink::~CLink()
{
Clear();
}
void CLink::Clear()
{
m_nId = 0;
m_eType = eLinkType::ltNone;
m_wsData.clear();
}
void CLink::AddLink(BYTE nId, const LONG& lType, const std::wstring& wsData)
{
m_nId = nId;
m_eType = static_cast<eLinkType>(lType);
m_wsData = wsData;
}
void CLink::AddBBox(const double& x1, const double& y1, const double& x2, const double& y2)
{
m_dTop = y1;
m_dLeft = x1;
m_dBottom = y2;
m_dRight = x2;
}
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "BaseItem.h"
namespace NSDocxRenderer
{
enum class eLinkType
{
ltNone = 0,
ltGoTo = 1,
ltUri = 6,
ltNamed = 9,
};
class CLink : public CBaseItem
{
public:
CLink() = default;
virtual ~CLink();
void Clear();
void AddLink(BYTE nId, const LONG& type, const std::wstring& wsData);
void AddBBox(const double& x1, const double& y1, const double& x2, const double& y2);
public:
UINT m_nId{0};
eLinkType m_eType{eLinkType::ltNone};
double m_dTop{0.0};
double m_dLeft{0.0};
double m_dBottom{0.0};
double m_dRight{0.0};
std::wstring m_wsData{};
};
}

View File

@ -637,7 +637,14 @@ namespace NSDocxRenderer
break;
}
oWriter.AddUInt(m_nShapeId);
oWriter.WriteString(L"\"/>");
oWriter.WriteString(L"\">");
if (m_bIsHyperlink)
{
oWriter.WriteString(L"<a:hlinkClick xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" r:id=\"rId");
oWriter.AddUInt(m_nRid);
oWriter.WriteString(L"\"/>");
}
oWriter.WriteString(L"</wp:docPr>");
oWriter.WriteString(L"<wp:cNvGraphicFramePr/>");
BuildSpecificProperties(oWriter);
oWriter.WriteString(L"</wp:anchor>");

View File

@ -46,11 +46,13 @@ namespace NSDocxRenderer
std::wstring m_strDstMedia {};
double m_dRotation {0.0};
int m_nRid {0};
bool m_bIsNoFill {true};
bool m_bIsNoStroke {true};
bool m_bIsBehindDoc {true};
bool m_bIsUseInTable{false};
bool m_bIsHyperlink {false};
std::shared_ptr<CImageInfo> m_pImageInfo{nullptr};
double m_dImageTop{};

View File

@ -132,10 +132,21 @@ namespace NSDocxRenderer
m_strDstMedia = L"";
m_lMaxSizeImage = 1200;
m_lNextIDImage = 0;
m_lFileID = 0;
m_mapImageData.clear();
}
void CImageManager::UpdateId(int idInc)
{
m_lNextIDImage += idInc;
}
int CImageManager::GetId() const
{
return m_lNextIDImage;
}
std::shared_ptr<CImageInfo> CImageManager::WriteImage(Aggplus::CImage* pImage, double& x, double& y, double& width, double& height)
{
if (height < 0)
@ -176,10 +187,11 @@ namespace NSDocxRenderer
return find->second;
++m_lNextIDImage;
++m_lFileID;
auto pInfo = std::make_shared<CImageInfo>();
pInfo->m_nId = m_lNextIDImage;
pInfo->m_eType = GetImageType(pImage);
pInfo->m_strFileName = L"image" + std::to_wstring(pInfo->m_nId);
pInfo->m_strFileName = L"image" + std::to_wstring(m_lFileID);
pInfo->m_strFileName += ((pInfo->m_eType == CImageInfo::itJPG) ? L".jpg" : L".png");
UINT format = (pInfo->m_eType == CImageInfo::itJPG) ? 3 : 4;

View File

@ -19,6 +19,9 @@ namespace NSDocxRenderer
~CImageManager() = default;
void Clear();
void UpdateId(int idInc);
int GetId() const;
std::shared_ptr<CImageInfo> WriteImage(Aggplus::CImage* pImage, double& x, double& y, double& width, double& height);
std::shared_ptr<CImageInfo> WriteImage(const std::wstring& strFile);
std::shared_ptr<CImageInfo> GenerateImageID(Aggplus::CImage* pImage);
@ -30,6 +33,7 @@ namespace NSDocxRenderer
std::shared_ptr<CImageInfo> GenerateImageID(const std::wstring& strFileName);
int m_lMaxSizeImage {1200};
int m_lNextIDImage {0};
int m_lFileID {0};
CCalculatorCRC32 m_oCRC;
};
}

View File

@ -46,6 +46,7 @@ const double c_dLINE_DISTANCE_MAX_MM = 50.0;
const double c_dSHAPE_TROUGH_MAX_MM = 80.0;
const double c_dLINE_SPLIT_DISTANCE_MM = 10.0;
const double c_dSHAPE_X_OFFSET_MM = 1.5;
const double c_dLINK_X_OFFSET_MM = 0.1;
const double c_dAVERAGE_SPACE_WIDTH_COEF = 0.9;
const double c_dSPACE_WIDTH_COEF = 0.4;
const double c_dMIN_ROTATION = 0.01;
@ -64,6 +65,7 @@ const double c_dSTANDART_FIRSTLINE_INDENT_MM = 12.5;
const uint32_t c_SPACE_SYM = 0x20;
const UINT c_iStartingIdForImages = 6;
const UINT c_iStartingIdForLinks = 6;
constexpr size_t c_nAntiZero = ~0;
const UINT c_iStandartRelativeHeight = 0x0400;

View File

@ -453,12 +453,6 @@ 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)
@ -505,12 +499,6 @@ 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)
@ -617,6 +605,12 @@ BYTE* CPdfFile::GetAPAnnots(int nRasterW, int nRasterH, int nBackgroundColor, in
return NULL;
return m_pInternal->pReader->GetAPAnnots(nRasterW, nRasterH, nBackgroundColor, nPageIndex, nAnnot, sView);
}
std::vector<CPdfLink*> CPdfFile::GetPdfLinks(int nPageIndex)
{
if (!m_pInternal->pReader)
return std::vector<CPdfLink*>();
return m_pInternal->pReader->GetPdfLinks(nPageIndex);
}
// ------------------------------------------------------------------------

View File

@ -136,6 +136,7 @@ public:
virtual std::wstring GetInfo();
virtual BYTE* GetStructure();
virtual BYTE* GetLinks(int nPageIndex);
std::vector<CPdfLink*> GetPdfLinks(int nPageIndex) override;
bool ValidMetaData();
// Захватывает полученную память malloc data
@ -145,11 +146,9 @@ 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,7 +56,6 @@
#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)
@ -653,20 +652,6 @@ 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;
@ -1088,40 +1073,6 @@ 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())
@ -1173,7 +1124,6 @@ BYTE* CPdfReader::GetStructure()
}
BYTE* CPdfReader::GetLinks(int _nPageIndex)
{
// TODO Links должны стать частью Annots
PDFDoc* pDoc = NULL;
int nPageIndex = GetPageIndex(_nPageIndex, &pDoc);
if (nPageIndex < 0 || !pDoc || !pDoc->getCatalog())
@ -1306,6 +1256,64 @@ BYTE* CPdfReader::GetLinks(int _nPageIndex)
return oLinks.Serialize();
}
std::vector<CPdfLink*> CPdfReader::GetPdfLinks(int _nPageIndex)
{
std::vector<CPdfLink*> oRes;
if (m_vPDFContext.empty())
return oRes;
PDFDoc* pDoc = NULL;
PdfReader::CPdfFontList* pFontList = NULL;
int nStartRefID = 0;
int nPageIndex = GetPageIndex(_nPageIndex, &pDoc, &pFontList, &nStartRefID);
if (nPageIndex < 0 || !pDoc || !pFontList || !pDoc->getCatalog())
return oRes;
Page* pPage = pDoc->getCatalog()->getPage(nPageIndex);
if (!pPage)
return oRes;
Object oAnnots;
if (!pPage->getAnnots(&oAnnots)->isArray())
{
oAnnots.free();
return oRes;
}
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::CAnnotLink* pAnnot = NULL;
oAnnots.arrayGetNF(i, &oAnnotRef);
if (sType == "Link")
pAnnot = new PdfReader::CAnnotLink(pDoc, &oAnnotRef, nPageIndex, nStartRefID);
CPdfLink* pLink = NULL;
if (pAnnot)
pLink = pAnnot->GetPdfLink();
if (pLink)
oRes.push_back(pLink);
RELEASEOBJECT(pAnnot);
}
oAnnots.free();
return oRes;
}
BYTE* CPdfReader::GetWidgets()
{
NSWasm::CData oRes;

View File

@ -97,7 +97,6 @@ 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();
@ -116,7 +115,6 @@ 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();
@ -128,6 +126,7 @@ public:
BYTE* GetButtonIcon(int nBackgroundColor, int nPageIndex, bool bBase64 = false, int nBWidget = -1, const char* sIView = NULL);
BYTE* StreamToCData(BYTE* pSteam, int nLength);
const std::map<std::wstring, std::wstring>& GetFonts() { return m_mFonts; }
std::vector<CPdfLink*> GetPdfLinks(int nPageIndex);
private:
void Clear();

View File

@ -533,6 +533,75 @@ CAnnot::CBorderType* getBorder(Object* oBorder, bool bBSorBorder)
return pBorderType;
}
//------------------------------------------------------------------------
// Action
//------------------------------------------------------------------------
void CActionGoTo::GetPdfLink(CPdfLink* pLink)
{
pLink->nType = 1;
pLink->unPage = unPage;
pLink->nKind = nKind;
switch (nKind)
{
case destXYZ:
case destFitH:
case destFitBH:
case destFitV:
case destFitBV:
{
pLink->unKindFlag = unKindFlag;
if (unKindFlag & (1 << 0))
pLink->pData[0] = pRect[0];
if (unKindFlag & (1 << 1))
pLink->pData[1] = pRect[1];
if (unKindFlag & (1 << 2))
pLink->pData[2] = pRect[2];
break;
}
case destFitR:
{
pLink->pData[0] = pRect[0];
pLink->pData[1] = pRect[1];
pLink->pData[2] = pRect[2];
pLink->pData[3] = pRect[3];
break;
}
case destFit:
case destFitB:
default:
break;
}
if (pNext)
{
pLink->pNext = new CPdfLink();
GetPdfLink(pLink->pNext);
}
}
void CActionURI::GetPdfLink(CPdfLink* pLink)
{
pLink->nType = 6;
pLink->sData = sURI;
if (pNext)
{
pLink->pNext = new CPdfLink();
GetPdfLink(pLink->pNext);
}
}
void CActionNamed::GetPdfLink(CPdfLink* pLink)
{
pLink->nType = 10;
pLink->sData = sNamed;
if (pNext)
{
pLink->pNext = new CPdfLink();
GetPdfLink(pLink->pNext);
}
}
//------------------------------------------------------------------------
// Widget
//------------------------------------------------------------------------
@ -1265,6 +1334,17 @@ CAnnotLink::~CAnnotLink()
RELEASEOBJECT(m_pAction);
RELEASEOBJECT(m_pPA);
}
CPdfLink* CAnnotLink::GetPdfLink()
{
CPdfLink* pRes = new CPdfLink();
pRes->pRect[0] = m_pRect[0];
pRes->pRect[1] = m_pRect[1];
pRes->pRect[2] = m_pRect[2];
pRes->pRect[3] = m_pRect[3];
if (m_pAction)
m_pAction->GetPdfLink(pRes);
return pRes;
}
//------------------------------------------------------------------------
// Screen

View File

@ -41,6 +41,7 @@
#include "../../DesktopEditor/graphics/pro/Fonts.h"
#include "../../DesktopEditor/graphics/pro/Graphics.h"
#include "../../DesktopEditor/graphics/pro/js/wasm/src/serialize.h"
#include "../../DesktopEditor/graphics/pro/officedrawingfile.h"
#include "RendererOutputDev.h"
@ -57,6 +58,7 @@ public:
virtual ~CAction() { RELEASEOBJECT(pNext); }
virtual void ToWASM(NSWasm::CData& oRes);
virtual void GetPdfLink(CPdfLink* pLink) {};
std::string sType;
CAction* pNext;
@ -70,18 +72,21 @@ struct CActionGoTo final : public CAction
double pRect[4];
BYTE nKind;
virtual void GetPdfLink(CPdfLink* pLink) override;
void ToWASM(NSWasm::CData& oRes) override;
};
struct CActionURI final : public CAction
{
std::string sURI;
virtual void GetPdfLink(CPdfLink* pLink) override;
void ToWASM(NSWasm::CData& oRes) override;
};
struct CActionNamed final : public CAction
{
std::string sNamed;
virtual void GetPdfLink(CPdfLink* pLink) override;
void ToWASM(NSWasm::CData& oRes) override;
};
struct CActionJavaScript final : public CAction
@ -347,6 +352,8 @@ public:
CAnnotLink(PDFDoc* pdfDoc, Object* oAnnotRef, int nPageIndex, int nStartRefID);
virtual ~CAnnotLink();
CPdfLink* GetPdfLink();
void ToWASM(NSWasm::CData& oRes) override;
private:

View File

@ -597,8 +597,9 @@ 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);
NSFonts::CFontInfo* pFontInfo = RendererOutputDev::GetFontByParams(xref, pFontManager, gfxFont, wsFBN, dStretch);
if (pFontInfo && !pFontInfo->m_wsFontPath.empty())
{
EraseSubsetTag(wsFontBaseName);
@ -1064,7 +1065,7 @@ void CollectFontWidths(GfxFont* gfxFont, Dict* pFontDict, std::map<unsigned int,
}
oDescendantFonts.free();
}
void CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
{
EraseSubsetTag(sName);
@ -1072,16 +1073,18 @@ void CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic)
CheckFontNameStyle(sName, L"semibold");
CheckFontNameStyle(sName, L"regular");
CheckFontNameStyle(sName, L"ultraexpanded");
CheckFontNameStyle(sName, L"extraexpanded");
CheckFontNameStyle(sName, L"semiexpanded");
CheckFontNameStyle(sName, L"expanded");
double dStretch = 1.0;
CheckFontNameStyle(sName, L"ultracondensed");
CheckFontNameStyle(sName, L"extracondensed");
CheckFontNameStyle(sName, L"semicondensed");
CheckFontNameStyle(sName, L"condensedlight");
CheckFontNameStyle(sName, L"condensed");
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"light");
if (CheckFontNameStyle(sName, L"bold_italic")) { bBold = true; bItalic = true; }
@ -1097,6 +1100,8 @@ void 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);
void CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic);
double CheckFontStylePDF(std::wstring& sName, bool& bBold, bool& bItalic);
bool EraseSubsetTag(std::wstring& sFontName);
}

View File

@ -77,12 +77,12 @@
namespace PdfReader
{
void CheckFontNamePDF(std::wstring& sName, NSFonts::CFontSelectFormat* format)
double CheckFontNamePDF(std::wstring& sName, NSFonts::CFontSelectFormat* format)
{
bool bBold = false;
bool bItalic = false;
CheckFontStylePDF(sName, bBold, bItalic);
double dStretch = CheckFontStylePDF(sName, bBold, bItalic);
if (format)
{
@ -91,6 +91,20 @@ 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)
@ -738,7 +752,7 @@ namespace PdfReader
{
}
NSFonts::CFontInfo* RendererOutputDev::GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName)
NSFonts::CFontInfo* RendererOutputDev::GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName, double& dStretch)
{
NSFonts::CFontInfo* pFontInfo = NULL;
if (!pFontManager)
@ -751,7 +765,9 @@ namespace PdfReader
oRefObject.free();
NSFonts::CFontSelectFormat oFontSelect;
CheckFontNamePDF(wsFontBaseName, &oFontSelect);
dStretch = CheckFontNamePDF(wsFontBaseName, &oFontSelect);
if (std::abs(dStretch - 1.0f) > 1e-5f)
oFontSelect.usWidth = new USHORT(StretchToWidthClass(dStretch));
if (oFontObject.isDict())
{
Dict* pFontDict = oFontObject.getDict();
@ -825,7 +841,7 @@ namespace PdfReader
oDictItem.free();
oFontDescriptor.dictLookup("StemV", &oDictItem);
if (oDictItem.isNum())
if (oDictItem.isNum() && !oFontSelect.usWidth)
{
double dStemV = oDictItem.getNum();
if (dStemV > 50.5)
@ -884,6 +900,7 @@ 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
@ -1194,7 +1211,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);
NSFonts::CFontInfo* pFontInfo = GetFontByParams(pXref, pFontManager, pFont, wsFontBaseName, dStretch);
if (pFontInfo && L"" != pFontInfo->m_wsFontPath)
{
@ -1593,6 +1610,7 @@ 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;
}
@ -2493,6 +2511,12 @@ 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,6 +57,7 @@ namespace PdfReader
bool bAvailable; // Доступен ли шрифт. Сделано для многопотоковости
bool bFontSubstitution = false;
bool bIsIdentity = false;
double dStretch = 1.0;
};
@ -244,7 +245,7 @@ namespace PdfReader
{
m_pbBreak = pbBreak;
}
static NSFonts::CFontInfo* GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName);
static NSFonts::CFontInfo* GetFontByParams(XRef* pXref, NSFonts::IFontManager* pFontManager, GfxFont* pFont, std::wstring& wsFontBaseName, double& dStretch);
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,15 +343,13 @@ 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()) {
sXFA = XFAScanner::readXFAStreams(&xfaObj);
acroForm->xfaScanner = NULL;
if (!acroFormObjA->dictLookup("XFA", &xfaObj)->isNull()) {
acroForm->xfaScanner = XFAScanner::load(&xfaObj);
if (!catalog->getNeedsRendering()) {
acroForm->isStaticXFA = gTrue;
}
@ -721,7 +719,7 @@ AcroFormField *AcroFormField::load(AcroForm *acroFormA, Object *fieldRefA) {
i0 = i1;
}
}
xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
xfaFieldA = acroFormA->xfaScanner->findField(xfaName);
delete xfaName;
}
@ -745,9 +743,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;
@ -862,9 +860,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 {
@ -1223,7 +1221,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,7 +125,6 @@ class XFAScanner {
public:
static XFAScanner *load(Object *xfaObj);
static GString *readXFAStreams(Object *xfaObj);
virtual ~XFAScanner();
@ -136,6 +135,7 @@ 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;
Object obj, root, rootType;
XRefPosSet *posSet;
int i;
@ -370,6 +370,17 @@ 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();
@ -972,6 +983,39 @@ 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;
}
@ -1016,6 +1060,316 @@ 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,6 +10,7 @@
#define XREF_H
#include <aconf.h>
#include <vector>
#ifdef USE_GCC_PRAGMAS
#pragma interface
@ -50,6 +51,21 @@ 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
@ -179,6 +195,9 @@ 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);