Add PDF writer.
This commit is contained in:
parent
c05b7b6315
commit
9c116b1efd
72 changed files with 1819 additions and 114 deletions
36
src/publishing/CMakeLists.txt
Normal file
36
src/publishing/CMakeLists.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
list(APPEND publishing_HEADERS
|
||||
pdf/PdfDocument.h
|
||||
pdf/PdfDocumentCatalog.h
|
||||
pdf/PdfDictionary.h
|
||||
pdf/PdfObject.h
|
||||
pdf/PdfOutline.h
|
||||
pdf/PdfPageTree.h
|
||||
pdf/PdfPage.h
|
||||
pdf/PdfStream.h
|
||||
pdf/PdfXRefTable.h
|
||||
pdf/PdfWriter.h
|
||||
)
|
||||
|
||||
list(APPEND publishing_LIB_INCLUDES
|
||||
pdf/PdfDocument.cpp
|
||||
pdf/PdfDocumentCatalog.cpp
|
||||
pdf/PdfDictionary.cpp
|
||||
pdf/PdfObject.cpp
|
||||
pdf/PdfOutline.cpp
|
||||
pdf/PdfPageTree.cpp
|
||||
pdf/PdfPage.cpp
|
||||
pdf/PdfStream.cpp
|
||||
pdf/PdfXRefTable.cpp
|
||||
pdf/PdfWriter.cpp
|
||||
)
|
||||
|
||||
add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS})
|
||||
|
||||
target_include_directories(publishing PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pdf"
|
||||
)
|
||||
set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||
target_link_libraries( publishing PUBLIC core)
|
||||
|
||||
set_property(TARGET publishing PROPERTY FOLDER src)
|
45
src/publishing/pdf/PdfDictionary.cpp
Normal file
45
src/publishing/pdf/PdfDictionary.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include "PdfDictionary.h"
|
||||
|
||||
std::string PdfDictionary::ToString() const
|
||||
{
|
||||
const auto keys = GetStringKeys();
|
||||
if (keys.empty())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string content = " <<";
|
||||
bool first = true;
|
||||
auto ending = keys.size() == 1 ? "" : "\n";
|
||||
for (const auto& key : keys)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
content += " /" + key + " " + GetItem(key) + ending;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += " /" + key + " " + GetItem(key) + ending;
|
||||
}
|
||||
}
|
||||
|
||||
const auto dictKeys = GetDictKeys();
|
||||
ending = dictKeys.size() == 1 ? "" : "\n";
|
||||
for (const auto& key : GetDictKeys())
|
||||
{
|
||||
auto pdfDict = dynamic_cast<PdfDictionary*>(GetDict(key));
|
||||
if (first)
|
||||
{
|
||||
content += " /" + key + " " + pdfDict->ToString() + ending;
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += " /" + key + " " + pdfDict->ToString() + ending;
|
||||
}
|
||||
}
|
||||
|
||||
content += " >>\n";
|
||||
return content;
|
||||
}
|
10
src/publishing/pdf/PdfDictionary.h
Normal file
10
src/publishing/pdf/PdfDictionary.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Dictionary.h"
|
||||
|
||||
class PdfDictionary : public Dictionary
|
||||
{
|
||||
public:
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
73
src/publishing/pdf/PdfDocument.cpp
Normal file
73
src/publishing/pdf/PdfDocument.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "PdfDocument.h"
|
||||
|
||||
#include "PdfObject.h"
|
||||
#include "PdfDocumentCatalog.h"
|
||||
#include "PdfPageTree.h"
|
||||
#include "PdfPage.h"
|
||||
#include "PdfOutline.h"
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
PdfDocument::PdfDocument()
|
||||
{
|
||||
mXRefTable = std::make_unique<PdfXRefTable>();
|
||||
mCatalog = std::make_unique<PdfDocumentCatalog>();
|
||||
}
|
||||
|
||||
PdfDocument::~PdfDocument()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string PdfDocument::ToString()
|
||||
{
|
||||
IndexObjects();
|
||||
auto content = GetHeaderString();
|
||||
content += GetBodyString();
|
||||
|
||||
content += GetXRefString();
|
||||
|
||||
content += GetTrailerString();
|
||||
|
||||
content += "%%EOF\n";
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string PdfDocument::GetHeaderString()
|
||||
{
|
||||
auto content = "%PDF-" + mVersion + "\n";
|
||||
mXRefOffset = content.size();
|
||||
mXRefTable->AddRecord(content.size(), 65535, true);
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string PdfDocument::GetBodyString()
|
||||
{
|
||||
const auto content = mCatalog->ToString(mXRefTable.get());
|
||||
mXRefOffset += content.size();
|
||||
return content;
|
||||
}
|
||||
|
||||
std::string PdfDocument::GetXRefString()
|
||||
{
|
||||
return mXRefTable->ToString();
|
||||
}
|
||||
|
||||
std::string PdfDocument::GetTrailerString()
|
||||
{
|
||||
const auto numObjects = mXRefTable->GetNumEntries();
|
||||
mTrailer.AddStringItem("Size", std::to_string(numObjects));
|
||||
mTrailer.AddStringItem("Root", mCatalog->GetRefString());
|
||||
|
||||
std::string content = "trailer\n";
|
||||
content += mTrailer.ToString();
|
||||
|
||||
content += "startxref\n";
|
||||
content += std::to_string(mXRefOffset) + "\n";
|
||||
return content;
|
||||
}
|
||||
|
||||
void PdfDocument::IndexObjects()
|
||||
{
|
||||
mCatalog->IndexObjects(0);
|
||||
}
|
||||
|
44
src/publishing/pdf/PdfDocument.h
Normal file
44
src/publishing/pdf/PdfDocument.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class PdfDocumentCatalog;
|
||||
using PdfDocumentCatalogPtr = std::unique_ptr<PdfDocumentCatalog>;
|
||||
|
||||
class PdfXRefTable;
|
||||
using PdfXRefTablePtr = std::unique_ptr<PdfXRefTable>;
|
||||
|
||||
class PdfDocument
|
||||
{
|
||||
|
||||
public:
|
||||
PdfDocument();
|
||||
~PdfDocument();
|
||||
|
||||
std::string ToString();
|
||||
|
||||
private:
|
||||
std::string GetHeaderString();
|
||||
|
||||
std::string GetTrailerString();
|
||||
|
||||
std::string GetBodyString();
|
||||
|
||||
std::string GetXRefString();
|
||||
|
||||
void IndexObjects();
|
||||
|
||||
private:
|
||||
PdfDictionary mTrailer;
|
||||
std::string mVersion {"1.7"};
|
||||
PdfXRefTablePtr mXRefTable;
|
||||
unsigned mXRefOffset{0};
|
||||
PdfDocumentCatalogPtr mCatalog;
|
||||
};
|
||||
|
||||
using PdfDocumentPtr = std::unique_ptr<PdfDocument>;
|
44
src/publishing/pdf/PdfDocumentCatalog.cpp
Normal file
44
src/publishing/pdf/PdfDocumentCatalog.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "PdfDocumentCatalog.h"
|
||||
|
||||
#include "PdfOutline.h"
|
||||
#include "PdfPageTree.h"
|
||||
#include "PdfPage.h"
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
PdfDocumentCatalog::PdfDocumentCatalog()
|
||||
: PdfObject()
|
||||
{
|
||||
mOutlines = std::make_unique<PdfOutlineCollection>();
|
||||
mPages = std::make_unique<PdfPageTree>();
|
||||
}
|
||||
|
||||
unsigned PdfDocumentCatalog::IndexObjects(unsigned count)
|
||||
{
|
||||
auto newCount = count + 1;
|
||||
mObjectNumber = newCount;
|
||||
auto objectCount = mOutlines->IndexObjects(mObjectNumber);
|
||||
objectCount = mPages->IndexObjects(objectCount);
|
||||
return objectCount;
|
||||
}
|
||||
|
||||
std::string PdfDocumentCatalog::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
UpdateDictionary();
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
|
||||
content += mOutlines->ToString(xRefTable);
|
||||
|
||||
content += mPages->ToString(xRefTable);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
void PdfDocumentCatalog::UpdateDictionary()
|
||||
{
|
||||
mDictionary.AddStringItem("Type", "/Catalog");
|
||||
mDictionary.AddStringItem("Outlines", mOutlines->GetRefString());
|
||||
mDictionary.AddStringItem("Pages", mPages->GetRefString());
|
||||
}
|
23
src/publishing/pdf/PdfDocumentCatalog.h
Normal file
23
src/publishing/pdf/PdfDocumentCatalog.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
class PdfPageTree;
|
||||
class PdfOutlineCollection;
|
||||
|
||||
class PdfDocumentCatalog : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
PdfDocumentCatalog();
|
||||
|
||||
unsigned IndexObjects(unsigned count) override;
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
private:
|
||||
void UpdateDictionary() override;
|
||||
|
||||
std::unique_ptr<PdfOutlineCollection> mOutlines;
|
||||
std::unique_ptr<PdfPageTree> mPages;
|
||||
};
|
58
src/publishing/pdf/PdfObject.cpp
Normal file
58
src/publishing/pdf/PdfObject.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "PdfObject.h"
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
std::string PdfObject::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
UpdateDictionary();
|
||||
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
return content;
|
||||
}
|
||||
|
||||
void PdfObject::UpdateDictionary()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string PdfObject::GetStringSuffix() const
|
||||
{
|
||||
return "endobj\n\n";
|
||||
}
|
||||
|
||||
unsigned PdfObject::IndexObjects(unsigned count)
|
||||
{
|
||||
const auto newCount = count + 1;
|
||||
mObjectNumber = newCount;
|
||||
return newCount;
|
||||
}
|
||||
|
||||
void PdfObject::SetObjectNumber(unsigned num)
|
||||
{
|
||||
mObjectNumber = num;
|
||||
}
|
||||
|
||||
void PdfObject::SetGenerationNumber(unsigned num)
|
||||
{
|
||||
mGenerationNumber = num;
|
||||
}
|
||||
|
||||
std::string PdfObject::GetStringPrefix() const
|
||||
{
|
||||
return std::to_string(mObjectNumber) + " " + std::to_string(mGenerationNumber) + " obj\n";
|
||||
}
|
||||
|
||||
std::string PdfObject::GetRefString() const
|
||||
{
|
||||
return std::to_string(mObjectNumber) + " " + std::to_string(mGenerationNumber) + " R";
|
||||
}
|
||||
|
||||
bool PdfObject::IsFree() const
|
||||
{
|
||||
return mIsFree;
|
||||
}
|
40
src/publishing/pdf/PdfObject.h
Normal file
40
src/publishing/pdf/PdfObject.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class PdfXRefTable;
|
||||
|
||||
class PdfObject
|
||||
{
|
||||
public:
|
||||
virtual ~PdfObject() = default;
|
||||
|
||||
std::string GetStringPrefix() const;
|
||||
|
||||
std::string GetStringSuffix() const;
|
||||
|
||||
std::string GetRefString() const;
|
||||
|
||||
virtual unsigned IndexObjects(unsigned count);
|
||||
|
||||
bool IsFree() const;
|
||||
|
||||
virtual std::string ToString(PdfXRefTable* xRefTable);
|
||||
|
||||
void SetObjectNumber(unsigned num);
|
||||
|
||||
void SetGenerationNumber(unsigned num);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void UpdateDictionary();
|
||||
|
||||
unsigned mObjectNumber{0};
|
||||
unsigned mGenerationNumber{0};
|
||||
PdfDictionary mDictionary;
|
||||
bool mIsFree {false};
|
||||
};
|
||||
using PdfObjectPtr = std::unique_ptr<PdfObject>;
|
40
src/publishing/pdf/PdfOutline.cpp
Normal file
40
src/publishing/pdf/PdfOutline.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "PdfOutline.h"
|
||||
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
std::string PdfOutline::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
mDictionary.AddStringItem("Type", "/Outline");
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
return content;
|
||||
}
|
||||
|
||||
unsigned PdfOutlineCollection::IndexObjects(unsigned count)
|
||||
{
|
||||
auto newCount = count + 1;
|
||||
mObjectNumber = newCount;
|
||||
for (const auto& outline : mOutlines)
|
||||
{
|
||||
newCount = outline->IndexObjects(newCount);
|
||||
}
|
||||
return newCount;
|
||||
}
|
||||
|
||||
std::string PdfOutlineCollection::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
UpdateDictionary();
|
||||
std::string content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
return content;
|
||||
}
|
||||
|
||||
void PdfOutlineCollection::UpdateDictionary()
|
||||
{
|
||||
mDictionary.AddStringItem("Type", "/Outlines");
|
||||
mDictionary.AddStringItem("Count", std::to_string(mOutlines.size()));
|
||||
}
|
26
src/publishing/pdf/PdfOutline.h
Normal file
26
src/publishing/pdf/PdfOutline.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class PdfOutline : public PdfObject
|
||||
{
|
||||
public:
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
};
|
||||
using PdfOutlinePtr = std::unique_ptr<PdfOutline>;
|
||||
|
||||
class PdfOutlineCollection : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
unsigned IndexObjects(unsigned count) override;
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
void UpdateDictionary();
|
||||
|
||||
private:
|
||||
std::vector<PdfOutlinePtr> mOutlines;
|
||||
};
|
0
src/publishing/pdf/PdfPage.cpp
Normal file
0
src/publishing/pdf/PdfPage.cpp
Normal file
121
src/publishing/pdf/PdfPage.h
Normal file
121
src/publishing/pdf/PdfPage.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
#include "PdfStream.h"
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
class PdfPageTree;
|
||||
|
||||
class PdfProcSet : public PdfObject
|
||||
{
|
||||
public:
|
||||
std::string ToString(PdfXRefTable* xRefTable) override
|
||||
{
|
||||
UpdateDictionary();
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += "[/PDF]\n";
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
class PdfFont : public PdfObject
|
||||
{
|
||||
public:
|
||||
std::string ToString(PdfXRefTable* xRefTable) override
|
||||
{
|
||||
UpdateDictionary();
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
return content;
|
||||
}
|
||||
|
||||
void UpdateDictionary()
|
||||
{
|
||||
mDictionary.AddStringItem("Type", "/Font");
|
||||
mDictionary.AddStringItem("Subtype", "/Type1");
|
||||
mDictionary.AddStringItem("Name", "/F1");
|
||||
mDictionary.AddStringItem("BaseFont", "/Helvetica");
|
||||
mDictionary.AddStringItem("Encoding", "/MacRomanEncoding");
|
||||
}
|
||||
};
|
||||
|
||||
class PdfPage : public PdfObject
|
||||
{
|
||||
public:
|
||||
PdfPage(PdfPageTree* parent)
|
||||
: mParent(parent)
|
||||
{
|
||||
mContent = std::make_unique<PdfStream>();
|
||||
|
||||
std::string pageContent = "BT\n";
|
||||
pageContent += "/F1 24 Tf\n";
|
||||
pageContent += "100 100 Td\n";
|
||||
pageContent += "(Hello World) Tj\n";
|
||||
pageContent += "ET";
|
||||
|
||||
mContent->SetContent(pageContent);
|
||||
mProcSet = std::make_unique<PdfProcSet>();
|
||||
|
||||
mDefaultFont = std::make_unique<PdfFont>();
|
||||
}
|
||||
|
||||
unsigned IndexObjects(unsigned count)
|
||||
{
|
||||
auto newCount = count + 1;
|
||||
mObjectNumber = newCount;
|
||||
newCount = mContent->IndexObjects(newCount);
|
||||
newCount = mProcSet->IndexObjects(newCount);
|
||||
newCount = mDefaultFont->IndexObjects(newCount);
|
||||
return newCount;
|
||||
}
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override
|
||||
{
|
||||
UpdateDictionary();
|
||||
auto content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
|
||||
content += mContent->ToString(xRefTable);
|
||||
|
||||
content += mProcSet->ToString(xRefTable);
|
||||
|
||||
content += mDefaultFont->ToString(xRefTable);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
void UpdateDictionary()
|
||||
{
|
||||
std::string mediaBox = "[0 0 " + std::to_string(mWidth) + " " + std::to_string(mHeight) + "]";
|
||||
mDictionary.AddStringItem("Type", "/Page");
|
||||
mDictionary.AddStringItem("MediaBox", mediaBox);
|
||||
mDictionary.AddStringItem("Parent", mParent->GetRefString());
|
||||
mDictionary.AddStringItem("Contents", mContent->GetRefString());
|
||||
|
||||
auto resourcesDict = std::make_unique<PdfDictionary>();
|
||||
resourcesDict->AddStringItem("ProcSet", mProcSet->GetRefString());
|
||||
|
||||
auto fontDict = std::make_unique<PdfDictionary>();
|
||||
fontDict->AddStringItem("F1", mDefaultFont->GetRefString());
|
||||
resourcesDict->AddDictItem("Font", std::move(fontDict));
|
||||
|
||||
mDictionary.AddDictItem("Resources", std::move(resourcesDict));
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned mWidth{612};
|
||||
unsigned mHeight{792};
|
||||
std::unique_ptr<PdfStream> mContent;
|
||||
std::unique_ptr<PdfFont> mDefaultFont;
|
||||
PdfObjectPtr mProcSet;
|
||||
PdfPageTree* mParent;
|
||||
|
||||
};
|
37
src/publishing/pdf/PdfPageTree.cpp
Normal file
37
src/publishing/pdf/PdfPageTree.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "PdfPageTree.h"
|
||||
|
||||
#include "PdfXRefTable.h"
|
||||
#include "PdfPage.h"
|
||||
|
||||
PdfPageTree::PdfPageTree()
|
||||
{
|
||||
mRootPage = std::make_unique<PdfPage>(this);
|
||||
}
|
||||
|
||||
unsigned PdfPageTree::IndexObjects(unsigned count)
|
||||
{
|
||||
auto newCount = count + 1;
|
||||
mObjectNumber = newCount;
|
||||
newCount = mRootPage->IndexObjects(newCount);
|
||||
return newCount;
|
||||
}
|
||||
|
||||
std::string PdfPageTree::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
UpdateDictionary();
|
||||
std::string content = GetStringPrefix();
|
||||
content += mDictionary.ToString();
|
||||
content += GetStringSuffix();
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
|
||||
content += mRootPage->ToString(xRefTable);
|
||||
return content;
|
||||
}
|
||||
|
||||
void PdfPageTree::UpdateDictionary()
|
||||
{
|
||||
mDictionary.AddStringItem("Type", "/Pages");
|
||||
std::string kids = "[" + mRootPage->GetRefString() + "]";
|
||||
mDictionary.AddStringItem("Kids", kids);
|
||||
mDictionary.AddStringItem("Count", "1");
|
||||
}
|
23
src/publishing/pdf/PdfPageTree.h
Normal file
23
src/publishing/pdf/PdfPageTree.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
class PdfPage;
|
||||
using PdfPagePtr = std::unique_ptr<PdfPage>;
|
||||
|
||||
class PdfPageTree : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
PdfPageTree();
|
||||
|
||||
unsigned IndexObjects(unsigned count) override;
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
void UpdateDictionary() override;
|
||||
|
||||
private:
|
||||
|
||||
PdfPagePtr mRootPage;
|
||||
};
|
36
src/publishing/pdf/PdfStream.cpp
Normal file
36
src/publishing/pdf/PdfStream.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "PdfStream.h"
|
||||
|
||||
#include "PdfDictionary.h"
|
||||
#include "PdfXRefTable.h"
|
||||
|
||||
PdfStream::PdfStream()
|
||||
{
|
||||
mDictionary.AddStringItem("Length", "0");
|
||||
}
|
||||
|
||||
void PdfStream::SetContent(const std::string& content)
|
||||
{
|
||||
mContent = content;
|
||||
Update();
|
||||
}
|
||||
|
||||
void PdfStream::Update()
|
||||
{
|
||||
auto length = mContent.size();
|
||||
mDictionary.AddStringItem("Length", std::to_string(length));
|
||||
}
|
||||
|
||||
std::string PdfStream::ToString(PdfXRefTable* xRefTable)
|
||||
{
|
||||
std::string content = GetStringPrefix();
|
||||
|
||||
content += mDictionary.ToString();
|
||||
|
||||
content += "stream\n";
|
||||
content += mContent;
|
||||
content += "\nendstream\n";
|
||||
content += "endobj\n\n";
|
||||
|
||||
xRefTable->AddRecord(content.size(), mGenerationNumber, mIsFree);
|
||||
return content;
|
||||
}
|
19
src/publishing/pdf/PdfStream.h
Normal file
19
src/publishing/pdf/PdfStream.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "PdfObject.h"
|
||||
|
||||
class PdfStream : public PdfObject
|
||||
{
|
||||
public:
|
||||
|
||||
PdfStream();
|
||||
|
||||
void Update();
|
||||
|
||||
void SetContent(const std::string& content);
|
||||
|
||||
std::string ToString(PdfXRefTable* xRefTable) override;
|
||||
|
||||
private:
|
||||
std::string mContent;
|
||||
};
|
12
src/publishing/pdf/PdfWriter.cpp
Normal file
12
src/publishing/pdf/PdfWriter.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "PdfWriter.h"
|
||||
|
||||
#include "PdfDocument.h"
|
||||
|
||||
std::string PdfWriter::ToString(const std::unique_ptr<PdfDocument>& document) const
|
||||
{
|
||||
std::string content;
|
||||
|
||||
content += document->ToString();
|
||||
|
||||
return content;
|
||||
}
|
13
src/publishing/pdf/PdfWriter.h
Normal file
13
src/publishing/pdf/PdfWriter.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class PdfDocument;
|
||||
|
||||
class PdfWriter
|
||||
{
|
||||
public:
|
||||
|
||||
std::string ToString(const std::unique_ptr<PdfDocument>& document) const;
|
||||
};
|
68
src/publishing/pdf/PdfXRefTable.cpp
Normal file
68
src/publishing/pdf/PdfXRefTable.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "PdfXRefTable.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
|
||||
PdfXRefTable::PdfXRefTable()
|
||||
{
|
||||
mSections.push_back(TableSubSection());
|
||||
}
|
||||
|
||||
std::string PdfXRefTable::ToString()
|
||||
{
|
||||
std::string content;
|
||||
for (const auto& section : mSections)
|
||||
{
|
||||
content += "xref\n" + std::to_string(section.mStartIndex) + " " + std::to_string(section.mRecords.size());
|
||||
content += "\n";
|
||||
for (const auto& record : section.mRecords)
|
||||
{
|
||||
auto offsetString = StringUtils::ToPaddedString(10, record.mOffsetBytes);
|
||||
auto generationString = StringUtils::ToPaddedString(5, record.mGenerationNumber);
|
||||
auto freeString = record.mIsFree ? "f" : "n";
|
||||
|
||||
content += offsetString + " " + generationString + " " + freeString + "\n";
|
||||
}
|
||||
content += "\n";
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
unsigned PdfXRefTable::GetNextOffset()
|
||||
{
|
||||
auto lastNumRecords = mSections[mSections.size() - 1].mRecords.size();
|
||||
if (lastNumRecords > 0)
|
||||
{
|
||||
return mSections[mSections.size() - 1].mRecords[lastNumRecords -1].mOffsetBytes + mLastAddedBytes;
|
||||
}
|
||||
else if (mSections.size() > 1)
|
||||
{
|
||||
lastNumRecords = mSections[mSections.size() - 2].mRecords.size();
|
||||
return mSections[mSections.size() - 2].mRecords[lastNumRecords -1].mOffsetBytes + mLastAddedBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PdfXRefTable::AddRecord(unsigned numBytes, unsigned generation, unsigned isFree)
|
||||
{
|
||||
XRefRecord record;
|
||||
|
||||
record.mOffsetBytes = GetNextOffset();
|
||||
record.mGenerationNumber = generation;
|
||||
record.mIsFree = isFree;
|
||||
mSections[mSections.size()-1].mRecords.push_back(record);
|
||||
mLastAddedBytes = numBytes;
|
||||
}
|
||||
|
||||
unsigned PdfXRefTable::GetNumEntries()
|
||||
{
|
||||
unsigned count = 0;
|
||||
for (const auto& section : mSections)
|
||||
{
|
||||
count += section.mRecords.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
37
src/publishing/pdf/PdfXRefTable.h
Normal file
37
src/publishing/pdf/PdfXRefTable.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct XRefRecord
|
||||
{
|
||||
unsigned mOffsetBytes{0};
|
||||
unsigned mGenerationNumber{0};
|
||||
bool mIsFree{false};
|
||||
};
|
||||
|
||||
struct TableSubSection
|
||||
{
|
||||
unsigned mStartIndex{0};
|
||||
std::vector<XRefRecord> mRecords;
|
||||
};
|
||||
|
||||
class PdfXRefTable
|
||||
{
|
||||
public:
|
||||
|
||||
PdfXRefTable();
|
||||
|
||||
std::string ToString();
|
||||
|
||||
unsigned GetNextOffset();
|
||||
|
||||
void AddRecord(unsigned numBytes, unsigned generation, unsigned isFree);
|
||||
|
||||
unsigned GetNumEntries();
|
||||
|
||||
private:
|
||||
unsigned mLastAddedBytes{0};
|
||||
std::vector<TableSubSection> mSections;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue