diff --git a/CodeGenerator/codegenerator.cpp b/CodeGenerator/codegenerator.cpp
index fdac425..7ded935 100644
--- a/CodeGenerator/codegenerator.cpp
+++ b/CodeGenerator/codegenerator.cpp
@@ -251,7 +251,7 @@ void CodeGenerator::generateCode(QString headerName, QString sourceName) const
QFile headerFile(headerName);
if (headerFile.exists())
{
- if (headerFile.open(QFile::ReadOnly))
+ if (headerFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(headerFile.readAll());
headerFile.close();
@@ -273,7 +273,7 @@ void CodeGenerator::generateCode(QString headerName, QString sourceName) const
QFile sourceFile(sourceName);
if (sourceFile.exists())
{
- if (sourceFile.open(QFile::ReadOnly))
+ if (sourceFile.open(QFile::ReadOnly | QFile::Text))
{
QString utfCode = QString::fromUtf8(sourceFile.readAll());
sourceFile.close();
diff --git a/PdfExampleGenerator/PdfExampleGenerator.pro b/PdfExampleGenerator/PdfExampleGenerator.pro
new file mode 100644
index 0000000..bc1d2b5
--- /dev/null
+++ b/PdfExampleGenerator/PdfExampleGenerator.pro
@@ -0,0 +1,37 @@
+QT += gui
+
+CONFIG += c++11 console
+CONFIG -= app_bundle
+
+# The following define makes your compiler emit warnings if you use
+# any Qt feature that has been marked deprecated (the exact warnings
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+
+# You can also make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+QMAKE_CXXFLAGS += /std:c++latest /utf-8
+
+INCLUDEPATH += $$PWD/../PDFForQtLib/Sources
+
+DESTDIR = $$OUT_PWD/..
+
+LIBS += -L$$OUT_PWD/..
+
+LIBS += -lPDFForQtLib
+
+SOURCES += \
+ main.cpp \
+ pdfexamplesgenerator.cpp
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+HEADERS += \
+ pdfexamplesgenerator.h
diff --git a/PdfExampleGenerator/main.cpp b/PdfExampleGenerator/main.cpp
new file mode 100644
index 0000000..13fa186
--- /dev/null
+++ b/PdfExampleGenerator/main.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#include
+
+#include "pdfexamplesgenerator.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+ PDFExamplesGenerator::generateAnnotationsExample();
+}
diff --git a/PdfExampleGenerator/pdfexamplesgenerator.cpp b/PdfExampleGenerator/pdfexamplesgenerator.cpp
new file mode 100644
index 0000000..9c74988
--- /dev/null
+++ b/PdfExampleGenerator/pdfexamplesgenerator.cpp
@@ -0,0 +1,33 @@
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#include "pdfexamplesgenerator.h"
+
+#include "pdfdocumentbuilder.h"
+#include "pdfdocumentwriter.h"
+
+void PDFExamplesGenerator::generateAnnotationsExample()
+{
+ pdf::PDFDocumentBuilder builder;
+
+ builder.appendPage(QRectF(0, 0, 400, 400));
+
+ // Write result to a file
+ pdf::PDFDocument document = builder.build();
+ pdf::PDFDocumentWriter writer(nullptr);
+ writer.write("Ex_Annotations.pdf", &document);
+}
diff --git a/PdfExampleGenerator/pdfexamplesgenerator.h b/PdfExampleGenerator/pdfexamplesgenerator.h
new file mode 100644
index 0000000..ebbeea0
--- /dev/null
+++ b/PdfExampleGenerator/pdfexamplesgenerator.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#ifndef PDFEXAMPLESGENERATOR_H
+#define PDFEXAMPLESGENERATOR_H
+
+class PDFExamplesGenerator
+{
+public:
+ explicit PDFExamplesGenerator() = delete;
+
+ static void generateAnnotationsExample();
+};
+
+#endif // PDFEXAMPLESGENERATOR_H
diff --git a/PdfForQt.pro b/PdfForQt.pro
index 05207e4..51d925b 100644
--- a/PdfForQt.pro
+++ b/PdfForQt.pro
@@ -20,6 +20,7 @@ TEMPLATE = subdirs
SUBDIRS += \
CodeGenerator \
JBIG2_Viewer \
+ PdfExampleGenerator \
PdfForQtLib \
UnitTests \
PdfForQtViewer
@@ -27,3 +28,4 @@ SUBDIRS += \
UnitTests.depends = PdfForQtLib
PdfForQtViewer.depends = PdfForQtLib
JBIG2_Viewer.depends = PdfForQtLib
+PDFExampleGenerator.depends = PdfForQtLib
diff --git a/PdfForQtLib/PdfForQtLib.pro b/PdfForQtLib/PdfForQtLib.pro
index 8f4b532..6cd126c 100644
--- a/PdfForQtLib/PdfForQtLib.pro
+++ b/PdfForQtLib/PdfForQtLib.pro
@@ -43,6 +43,7 @@ SOURCES += \
sources/pdfcms.cpp \
sources/pdfcompiler.cpp \
sources/pdfdocumentbuilder.cpp \
+ sources/pdfdocumentwriter.cpp \
sources/pdfexecutionpolicy.cpp \
sources/pdffile.cpp \
sources/pdfitemmodels.cpp \
@@ -90,6 +91,7 @@ HEADERS += \
sources/pdfcompiler.h \
sources/pdfdocumentbuilder.h \
sources/pdfdocumentdrawinterface.h \
+ sources/pdfdocumentwriter.h \
sources/pdfexecutionpolicy.h \
sources/pdffile.h \
sources/pdfitemmodels.h \
diff --git a/PdfForQtLib/sources/pdfdocument.h b/PdfForQtLib/sources/pdfdocument.h
index 6018796..4210b53 100644
--- a/PdfForQtLib/sources/pdfdocument.h
+++ b/PdfForQtLib/sources/pdfdocument.h
@@ -78,6 +78,9 @@ public:
/// Returns security handler associated with these objects
const PDFSecurityHandler* getSecurityHandler() const { return m_securityHandler.data(); }
+ /// Sets security handler associated with these objects
+ void setSecurityHandler(PDFSecurityHandlerPointer handler) { m_securityHandler = qMove(handler); }
+
/// Adds a new object to the object list. This function
/// is not thread safe, do not call it from multiple threads.
/// \param object Object to be added
diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.cpp b/PdfForQtLib/sources/pdfdocumentbuilder.cpp
index 1b86433..52a9223 100644
--- a/PdfForQtLib/sources/pdfdocumentbuilder.cpp
+++ b/PdfForQtLib/sources/pdfdocumentbuilder.cpp
@@ -1,272 +1,320 @@
-// Copyright (C) 2020 Jakub Melka
-//
-// This file is part of PdfForQt.
-//
-// PdfForQt is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// PdfForQt is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with PDFForQt. If not, see .
-
-#include "pdfdocumentbuilder.h"
-#include "pdfencoding.h"
-#include "pdfconstants.h"
-
-namespace pdf
-{
-
-void PDFObjectFactory::beginArray()
-{
- m_items.emplace_back(ItemType::Array, PDFArray());
-}
-
-void PDFObjectFactory::endArray()
-{
- Item topItem = qMove(m_items.back());
- Q_ASSERT(topItem.type == ItemType::Array);
- m_items.pop_back();
- addObject(PDFObject::createArray(std::make_shared(qMove(std::get(topItem.object)))));
-}
-
-void PDFObjectFactory::beginDictionary()
-{
- m_items.emplace_back(ItemType::Dictionary, PDFDictionary());
-}
-
-void PDFObjectFactory::endDictionary()
-{
- Item topItem = qMove(m_items.back());
- Q_ASSERT(topItem.type == ItemType::Dictionary);
- m_items.pop_back();
- addObject(PDFObject::createDictionary(std::make_shared(qMove(std::get(topItem.object)))));
-}
-
-void PDFObjectFactory::beginDictionaryItem(const QByteArray& name)
-{
- m_items.emplace_back(ItemType::DictionaryItem, name, PDFObject());
-}
-
-void PDFObjectFactory::endDictionaryItem()
-{
- Item topItem = qMove(m_items.back());
- Q_ASSERT(topItem.type == ItemType::DictionaryItem);
- m_items.pop_back();
-
- Item& dictionaryItem = m_items.back();
- Q_ASSERT(dictionaryItem.type == ItemType::Dictionary);
- std::get(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get(topItem.object)));
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(QString textString)
-{
- if (!PDFEncoding::canConvertToEncoding(textString, PDFEncoding::Encoding::PDFDoc))
- {
- // Use unicode encoding
- QByteArray ba;
-
- {
- QTextStream textStream(&ba, QIODevice::WriteOnly);
- textStream.setCodec("UTF-16BE");
- textStream.setGenerateByteOrderMark(true);
- textStream << textString;
- }
-
- addObject(PDFObject::createString(std::make_shared(qMove(ba))));
- }
- else
- {
- // Use PDF document encoding
- addObject(PDFObject::createString(std::make_shared(PDFEncoding::convertToEncoding(textString, PDFEncoding::Encoding::PDFDoc))));
- }
-
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(WrapAnnotationColor color)
-{
- if (color.color.isValid())
- {
- // Jakub Melka: we will decide, if we have gray/rgb/cmyk color
- QColor value = color.color;
- if (value.spec() == QColor::Cmyk)
- {
- *this << std::initializer_list{ value.cyanF(), value.magentaF(), value.yellowF(), value.blackF() };
- }
- else if (qIsGray(value.rgb()))
- {
- *this << std::initializer_list{ value.redF() };
- }
- else
- {
- *this << std::initializer_list{ value.redF(), value.greenF(), value.blueF() };
- }
- }
- else
- {
- addObject(PDFObject::createNull());
- }
-
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(WrapCurrentDateTime)
-{
- addObject(PDFObject::createString(std::make_shared(PDFEncoding::converDateTimeToString(QDateTime::currentDateTime()))));
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(const QRectF& value)
-{
- *this << std::initializer_list{ value.left(), value.top(), value.right(), value.bottom() };
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(int value)
-{
- *this << PDFInteger(value);
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(WrapName wrapName)
-{
- addObject(PDFObject::createName(std::make_shared(qMove(wrapName.name))));
- return *this;
-}
-
-PDFObject PDFObjectFactory::takeObject()
-{
- Q_ASSERT(m_items.size() == 1);
- Q_ASSERT(m_items.back().type == ItemType::Object);
- PDFObject result = qMove(std::get(m_items.back().object));
- m_items.clear();
- return result;
-}
-
-void PDFObjectFactory::addObject(PDFObject object)
-{
- if (m_items.empty())
- {
- m_items.emplace_back(ItemType::Object, qMove(object));
- return;
- }
-
- Item& topItem = m_items.back();
- switch (topItem.type)
- {
- case ItemType::Object:
- // Just override the object
- topItem.object = qMove(object);
- break;
-
- case ItemType::Dictionary:
- // Do not do anything - we are inside dictionary
- break;
-
- case ItemType::DictionaryItem:
- // Add item to dictionary item
- topItem.object = qMove(object);
- break;
-
- case ItemType::Array:
- std::get(topItem.object).appendItem(qMove(object));
- break;
-
- default:
- Q_ASSERT(false);
- break;
- }
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(std::nullptr_t)
-{
- addObject(PDFObject::createNull());
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(bool value)
-{
- addObject(PDFObject::createBool(value));
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(PDFReal value)
-{
- addObject(PDFObject::createReal(value));
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(PDFInteger value)
-{
- addObject(PDFObject::createInteger(value));
- return *this;
-}
-
-PDFObjectFactory& PDFObjectFactory::operator<<(PDFObjectReference value)
-{
- addObject(PDFObject::createReference(value));
- return *this;
-}
-
-PDFDocumentBuilder::PDFDocumentBuilder() :
- m_version(1, 7)
-{
- createDocument();
-}
-
-PDFDocumentBuilder::PDFDocumentBuilder(const PDFDocument* document) :
- m_storage(document->getStorage()),
- m_version(document->getInfo()->version)
-{
-
-}
-
-void PDFDocumentBuilder::reset()
-{
- *this = PDFDocumentBuilder();
-}
-
-void PDFDocumentBuilder::createDocument()
-{
- reset();
-
- PDFObjectReference catalog = createCatalog();
- PDFObject trailerDictionary = createTrailerDictionary(catalog);
- m_storage.updateTrailerDictionary(trailerDictionary);
-}
-
-PDFDocument PDFDocumentBuilder::build()
-{
- updateTrailerDictionary(m_storage.getObjects().size());
- return PDFDocument(PDFObjectStorage(m_storage), m_version);
-}
-
-PDFObjectReference PDFDocumentBuilder::addObject(PDFObject object)
-{
- return m_storage.addObject(PDFObjectManipulator::removeNullObjects(object));
-}
-
-void PDFDocumentBuilder::mergeTo(PDFObjectReference reference, PDFObject object)
-{
- m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::RemoveNullObjects));
-}
-
-QRectF PDFDocumentBuilder::getPopupWindowRect(const QRectF& rectangle) const
-{
- return rectangle.translated(rectangle.width() * 1.25, 0);
-}
-
-QString PDFDocumentBuilder::getProducerString() const
-{
- return PDF_LIBRARY_NAME;
-}
-
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#include "pdfdocumentbuilder.h"
+#include "pdfencoding.h"
+#include "pdfconstants.h"
+
+namespace pdf
+{
+
+void PDFObjectFactory::beginArray()
+{
+ m_items.emplace_back(ItemType::Array, PDFArray());
+}
+
+void PDFObjectFactory::endArray()
+{
+ Item topItem = qMove(m_items.back());
+ Q_ASSERT(topItem.type == ItemType::Array);
+ m_items.pop_back();
+ addObject(PDFObject::createArray(std::make_shared(qMove(std::get(topItem.object)))));
+}
+
+void PDFObjectFactory::beginDictionary()
+{
+ m_items.emplace_back(ItemType::Dictionary, PDFDictionary());
+}
+
+void PDFObjectFactory::endDictionary()
+{
+ Item topItem = qMove(m_items.back());
+ Q_ASSERT(topItem.type == ItemType::Dictionary);
+ m_items.pop_back();
+ addObject(PDFObject::createDictionary(std::make_shared(qMove(std::get(topItem.object)))));
+}
+
+void PDFObjectFactory::beginDictionaryItem(const QByteArray& name)
+{
+ m_items.emplace_back(ItemType::DictionaryItem, name, PDFObject());
+}
+
+void PDFObjectFactory::endDictionaryItem()
+{
+ Item topItem = qMove(m_items.back());
+ Q_ASSERT(topItem.type == ItemType::DictionaryItem);
+ m_items.pop_back();
+
+ Item& dictionaryItem = m_items.back();
+ Q_ASSERT(dictionaryItem.type == ItemType::Dictionary);
+ std::get(dictionaryItem.object).addEntry(qMove(topItem.itemName), qMove(std::get(topItem.object)));
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(WrapEmptyArray)
+{
+ beginArray();
+ endArray();
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(QString textString)
+{
+ if (!PDFEncoding::canConvertToEncoding(textString, PDFEncoding::Encoding::PDFDoc))
+ {
+ // Use unicode encoding
+ QByteArray ba;
+
+ {
+ QTextStream textStream(&ba, QIODevice::WriteOnly);
+ textStream.setCodec("UTF-16BE");
+ textStream.setGenerateByteOrderMark(true);
+ textStream << textString;
+ }
+
+ addObject(PDFObject::createString(std::make_shared(qMove(ba))));
+ }
+ else
+ {
+ // Use PDF document encoding
+ addObject(PDFObject::createString(std::make_shared(PDFEncoding::convertToEncoding(textString, PDFEncoding::Encoding::PDFDoc))));
+ }
+
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(WrapAnnotationColor color)
+{
+ if (color.color.isValid())
+ {
+ // Jakub Melka: we will decide, if we have gray/rgb/cmyk color
+ QColor value = color.color;
+ if (value.spec() == QColor::Cmyk)
+ {
+ *this << std::initializer_list{ value.cyanF(), value.magentaF(), value.yellowF(), value.blackF() };
+ }
+ else if (qIsGray(value.rgb()))
+ {
+ *this << std::initializer_list{ value.redF() };
+ }
+ else
+ {
+ *this << std::initializer_list{ value.redF(), value.greenF(), value.blueF() };
+ }
+ }
+ else
+ {
+ addObject(PDFObject::createNull());
+ }
+
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(WrapCurrentDateTime)
+{
+ addObject(PDFObject::createString(std::make_shared(PDFEncoding::converDateTimeToString(QDateTime::currentDateTime()))));
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(const QRectF& value)
+{
+ *this << std::initializer_list{ value.left(), value.top(), value.right(), value.bottom() };
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(int value)
+{
+ *this << PDFInteger(value);
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(WrapName wrapName)
+{
+ addObject(PDFObject::createName(std::make_shared(qMove(wrapName.name))));
+ return *this;
+}
+
+PDFObject PDFObjectFactory::takeObject()
+{
+ Q_ASSERT(m_items.size() == 1);
+ Q_ASSERT(m_items.back().type == ItemType::Object);
+ PDFObject result = qMove(std::get(m_items.back().object));
+ m_items.clear();
+ return result;
+}
+
+void PDFObjectFactory::addObject(PDFObject object)
+{
+ if (m_items.empty())
+ {
+ m_items.emplace_back(ItemType::Object, qMove(object));
+ return;
+ }
+
+ Item& topItem = m_items.back();
+ switch (topItem.type)
+ {
+ case ItemType::Object:
+ // Just override the object
+ topItem.object = qMove(object);
+ break;
+
+ case ItemType::Dictionary:
+ // Do not do anything - we are inside dictionary
+ break;
+
+ case ItemType::DictionaryItem:
+ // Add item to dictionary item
+ topItem.object = qMove(object);
+ break;
+
+ case ItemType::Array:
+ std::get(topItem.object).appendItem(qMove(object));
+ break;
+
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(std::nullptr_t)
+{
+ addObject(PDFObject::createNull());
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(bool value)
+{
+ addObject(PDFObject::createBool(value));
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(PDFReal value)
+{
+ addObject(PDFObject::createReal(value));
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(PDFInteger value)
+{
+ addObject(PDFObject::createInteger(value));
+ return *this;
+}
+
+PDFObjectFactory& PDFObjectFactory::operator<<(PDFObjectReference value)
+{
+ addObject(PDFObject::createReference(value));
+ return *this;
+}
+
+PDFDocumentBuilder::PDFDocumentBuilder() :
+ m_version(1, 7)
+{
+ createDocument();
+}
+
+PDFDocumentBuilder::PDFDocumentBuilder(const PDFDocument* document) :
+ m_storage(document->getStorage()),
+ m_version(document->getInfo()->version)
+{
+
+}
+
+void PDFDocumentBuilder::reset()
+{
+ *this = PDFDocumentBuilder();
+}
+
+void PDFDocumentBuilder::createDocument()
+{
+ if (!m_storage.getObjects().empty())
+ {
+ reset();
+ }
+
+ addObject(PDFObject::createNull());
+ PDFObjectReference catalog = createCatalog();
+ PDFObject trailerDictionary = createTrailerDictionary(catalog);
+ m_storage.updateTrailerDictionary(trailerDictionary);
+ m_storage.setSecurityHandler(PDFSecurityHandlerPointer(new PDFNoneSecurityHandler()));
+}
+
+PDFDocument PDFDocumentBuilder::build()
+{
+ updateTrailerDictionary(m_storage.getObjects().size());
+ return PDFDocument(PDFObjectStorage(m_storage), m_version);
+}
+
+PDFObjectReference PDFDocumentBuilder::addObject(PDFObject object)
+{
+ return m_storage.addObject(PDFObjectManipulator::removeNullObjects(object));
+}
+
+void PDFDocumentBuilder::mergeTo(PDFObjectReference reference, PDFObject object)
+{
+ m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::RemoveNullObjects));
+}
+
+void PDFDocumentBuilder::appendTo(PDFObjectReference reference, PDFObject object)
+{
+ m_storage.setObject(reference, PDFObjectManipulator::merge(m_storage.getObject(reference), qMove(object), PDFObjectManipulator::ConcatenateArrays));
+}
+
+QRectF PDFDocumentBuilder::getPopupWindowRect(const QRectF& rectangle) const
+{
+ return rectangle.translated(rectangle.width() * 1.25, 0);
+}
+
+QString PDFDocumentBuilder::getProducerString() const
+{
+ return PDF_LIBRARY_NAME;
+}
+
+PDFObjectReference PDFDocumentBuilder::getPageTreeRoot() const
+{
+ if (const PDFDictionary* trailerDictionary = getDictionaryFromObject(m_storage.getTrailerDictionary()))
+ {
+ if (const PDFDictionary* catalogDictionary = getDictionaryFromObject(trailerDictionary->get("Root")))
+ {
+ PDFObject pagesRoot = catalogDictionary->get("Pages");
+ if (pagesRoot.isReference())
+ {
+ return pagesRoot.getReference();
+ }
+ }
+ }
+
+ return PDFObjectReference();
+}
+
+PDFInteger PDFDocumentBuilder::getPageTreeRootChildCount() const
+{
+ if (const PDFDictionary* pageTreeRootDictionary = getDictionaryFromObject(getObjectByReference(getPageTreeRoot())))
+ {
+ PDFObject childCountObject = getObject(pageTreeRootDictionary->get("Count"));
+ if (childCountObject.isInt())
+ {
+ return childCountObject.getInteger();
+ }
+ }
+
+ return 0;
+}
+
/* START GENERATED CODE */
PDFObjectReference PDFDocumentBuilder::createAnnotationSquare(PDFObjectReference page,
@@ -369,6 +417,9 @@ PDFObjectReference PDFDocumentBuilder::createCatalog()
objectBuilder.beginDictionaryItem("Type");
objectBuilder << WrapName("Catalog");
objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionaryItem("Pages");
+ objectBuilder << createCatalogPageTreeRoot();
+ objectBuilder.endDictionaryItem();
objectBuilder.endDictionary();
PDFObjectReference catalogReference = addObject(objectBuilder.takeObject());
return catalogReference;
@@ -429,6 +480,58 @@ void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount)
}
-/* END GENERATED CODE */
-
-} // namespace pdf
+PDFObjectReference PDFDocumentBuilder::appendPage(QRectF mediaBox)
+{
+ PDFObjectFactory objectBuilder;
+
+ objectBuilder.beginDictionary();
+ objectBuilder.beginDictionaryItem("Type");
+ objectBuilder << WrapName("Page");
+ objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionaryItem("Parent");
+ objectBuilder << getPageTreeRoot();
+ objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionary();
+ objectBuilder.endDictionary();
+ objectBuilder.beginDictionaryItem("MediaBox");
+ objectBuilder << mediaBox;
+ objectBuilder.endDictionaryItem();
+ objectBuilder.endDictionary();
+ PDFObjectReference pageReference = addObject(objectBuilder.takeObject());
+ objectBuilder.beginDictionary();
+ objectBuilder.beginDictionaryItem("Kids");
+ objectBuilder << std::initializer_list{ pageReference };
+ objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionaryItem("Count");
+ objectBuilder << getPageTreeRootChildCount() + 1;
+ objectBuilder.endDictionaryItem();
+ objectBuilder.endDictionary();
+ PDFObject updatedTreeRoot = objectBuilder.takeObject();
+ appendTo(getPageTreeRoot(), updatedTreeRoot);
+ return pageReference;
+}
+
+
+PDFObjectReference PDFDocumentBuilder::createCatalogPageTreeRoot()
+{
+ PDFObjectFactory objectBuilder;
+
+ objectBuilder.beginDictionary();
+ objectBuilder.beginDictionaryItem("Type");
+ objectBuilder << WrapName("Pages");
+ objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionaryItem("Kids");
+ objectBuilder << WrapEmptyArray();
+ objectBuilder.endDictionaryItem();
+ objectBuilder.beginDictionaryItem("Count");
+ objectBuilder << 0;
+ objectBuilder.endDictionaryItem();
+ objectBuilder.endDictionary();
+ PDFObjectReference pageTreeRoot = addObject(objectBuilder.takeObject());
+ return pageTreeRoot;
+}
+
+
+/* END GENERATED CODE */
+
+} // namespace pdf
diff --git a/PdfForQtLib/sources/pdfdocumentbuilder.h b/PdfForQtLib/sources/pdfdocumentbuilder.h
index e8654d4..39bbafa 100644
--- a/PdfForQtLib/sources/pdfdocumentbuilder.h
+++ b/PdfForQtLib/sources/pdfdocumentbuilder.h
@@ -1,165 +1,183 @@
-// Copyright (C) 2020 Jakub Melka
-//
-// This file is part of PdfForQt.
-//
-// PdfForQt is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// PdfForQt is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with PDFForQt. If not, see .
-
-#ifndef PDFDOCUMENTBUILDER_H
-#define PDFDOCUMENTBUILDER_H
-
-#include "pdfobject.h"
-#include "pdfdocument.h"
-
-namespace pdf
-{
-
-struct WrapName
-{
- WrapName(const char* name) :
- name(name)
- {
-
- }
-
- QByteArray name;
-};
-
-struct WrapAnnotationColor
-{
- WrapAnnotationColor(QColor color) :
- color(color)
- {
-
- }
-
- QColor color;
-};
-
-struct WrapCurrentDateTime { };
-
-/// Factory for creating various PDF objects, such as simple objects,
-/// dictionaries, arrays etc.
-class PDFObjectFactory
-{
-public:
- inline explicit PDFObjectFactory() = default;
-
- void beginArray();
- void endArray();
-
- void beginDictionary();
- void endDictionary();
-
- void beginDictionaryItem(const QByteArray& name);
- void endDictionaryItem();
-
- PDFObjectFactory& operator<<(std::nullptr_t);
- PDFObjectFactory& operator<<(bool value);
- PDFObjectFactory& operator<<(PDFReal value);
- PDFObjectFactory& operator<<(PDFInteger value);
- PDFObjectFactory& operator<<(PDFObjectReference value);
- PDFObjectFactory& operator<<(WrapName wrapName);
- PDFObjectFactory& operator<<(int value);
- PDFObjectFactory& operator<<(const QRectF& value);
- PDFObjectFactory& operator<<(WrapCurrentDateTime);
- PDFObjectFactory& operator<<(WrapAnnotationColor color);
- PDFObjectFactory& operator<<(QString textString);
-
- /// Treat containers - write them as array
- template()))>
- PDFObjectFactory& operator<<(Container container)
- {
- beginArray();
-
- auto it = std::begin(container);
- auto itEnd = std::end(container);
- for (; it != itEnd; ++it)
- {
- *this << *it;
- }
-
- endArray();
-
- return *this;
- }
-
- PDFObject takeObject();
-
-private:
- void addObject(PDFObject object);
-
- enum class ItemType
- {
- Object,
- Dictionary,
- DictionaryItem,
- Array
- };
-
- /// What is stored in this structure, depends on the type.
- /// If type is 'Object', then single simple object is in object,
- /// if type is dictionary, then PDFDictionary is stored in object,
- /// if type is dictionary item, then object and item name is stored
- /// in the data, if item is array, then array is stored in the data.
- struct Item
- {
- inline Item() = default;
-
- template
- inline Item(ItemType type, T&& data) :
- type(type),
- object(qMove(data))
- {
-
- }
-
- template
- inline Item(ItemType type, const QByteArray& itemName, T&& data) :
- type(type),
- itemName(qMove(itemName)),
- object(qMove(data))
- {
-
- }
-
- ItemType type = ItemType::Object;
- QByteArray itemName;
- std::variant object;
- };
-
- std::vector- m_items;
-};
-
-class PDFDocumentBuilder
-{
-public:
- /// Creates a new blank document (with no pages)
- explicit PDFDocumentBuilder();
-
- ///
- explicit PDFDocumentBuilder(const PDFDocument* document);
-
- /// Resets the object to the initial state.
- /// \warning All data are lost
- void reset();
-
- /// Create a new blank document with no pages. If some document
- /// is edited at call of this function, then it is lost.
- void createDocument();
-
- PDFDocument build();
-
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#ifndef PDFDOCUMENTBUILDER_H
+#define PDFDOCUMENTBUILDER_H
+
+#include "pdfobject.h"
+#include "pdfdocument.h"
+
+namespace pdf
+{
+
+struct WrapName
+{
+ WrapName(const char* name) :
+ name(name)
+ {
+
+ }
+
+ QByteArray name;
+};
+
+struct WrapAnnotationColor
+{
+ WrapAnnotationColor(QColor color) :
+ color(color)
+ {
+
+ }
+
+ QColor color;
+};
+
+struct WrapCurrentDateTime { };
+struct WrapEmptyArray { };
+
+/// Factory for creating various PDF objects, such as simple objects,
+/// dictionaries, arrays etc.
+class PDFObjectFactory
+{
+public:
+ inline explicit PDFObjectFactory() = default;
+
+ void beginArray();
+ void endArray();
+
+ void beginDictionary();
+ void endDictionary();
+
+ void beginDictionaryItem(const QByteArray& name);
+ void endDictionaryItem();
+
+ PDFObjectFactory& operator<<(std::nullptr_t);
+ PDFObjectFactory& operator<<(bool value);
+ PDFObjectFactory& operator<<(PDFReal value);
+ PDFObjectFactory& operator<<(PDFInteger value);
+ PDFObjectFactory& operator<<(PDFObjectReference value);
+ PDFObjectFactory& operator<<(WrapName wrapName);
+ PDFObjectFactory& operator<<(int value);
+ PDFObjectFactory& operator<<(const QRectF& value);
+ PDFObjectFactory& operator<<(WrapCurrentDateTime);
+ PDFObjectFactory& operator<<(WrapAnnotationColor color);
+ PDFObjectFactory& operator<<(QString textString);
+ PDFObjectFactory& operator<<(WrapEmptyArray);
+
+ /// Treat containers - write them as array
+ template()))>
+ PDFObjectFactory& operator<<(Container container)
+ {
+ beginArray();
+
+ auto it = std::begin(container);
+ auto itEnd = std::end(container);
+ for (; it != itEnd; ++it)
+ {
+ *this << *it;
+ }
+
+ endArray();
+
+ return *this;
+ }
+
+ PDFObject takeObject();
+
+private:
+ void addObject(PDFObject object);
+
+ enum class ItemType
+ {
+ Object,
+ Dictionary,
+ DictionaryItem,
+ Array
+ };
+
+ /// What is stored in this structure, depends on the type.
+ /// If type is 'Object', then single simple object is in object,
+ /// if type is dictionary, then PDFDictionary is stored in object,
+ /// if type is dictionary item, then object and item name is stored
+ /// in the data, if item is array, then array is stored in the data.
+ struct Item
+ {
+ inline Item() = default;
+
+ template
+ inline Item(ItemType type, T&& data) :
+ type(type),
+ object(qMove(data))
+ {
+
+ }
+
+ template
+ inline Item(ItemType type, const QByteArray& itemName, T&& data) :
+ type(type),
+ itemName(qMove(itemName)),
+ object(qMove(data))
+ {
+
+ }
+
+ ItemType type = ItemType::Object;
+ QByteArray itemName;
+ std::variant object;
+ };
+
+ std::vector
- m_items;
+};
+
+class PDFFORQTLIBSHARED_EXPORT PDFDocumentBuilder
+{
+public:
+ /// Creates a new blank document (with no pages)
+ explicit PDFDocumentBuilder();
+
+ ///
+ explicit PDFDocumentBuilder(const PDFDocument* document);
+
+ /// Resets the object to the initial state.
+ /// \warning All data are lost
+ void reset();
+
+ /// Create a new blank document with no pages. If some document
+ /// is edited at call of this function, then it is lost.
+ void createDocument();
+
+ /// Builds a new document. This function can throw exceptions,
+ /// if document being built was invalid.
+ PDFDocument build();
+
+ /// If object is reference, the dereference attempt is performed
+ /// and object is returned. If it is not a reference, then self
+ /// is returned. If dereference attempt fails, then null object
+ /// is returned (no exception is thrown).
+ const PDFObject& getObject(const PDFObject& object) const;
+
+ /// Returns dictionary from an object. If object is not a dictionary,
+ /// then nullptr is returned (no exception is thrown).
+ const PDFDictionary* getDictionaryFromObject(const PDFObject& object) const;
+
+ /// Returns object by reference. If dereference attempt fails, then null object
+ /// is returned (no exception is thrown).
+ const PDFObject& getObjectByReference(PDFObjectReference reference) const;
+
/* START GENERATED CODE */
/// Square annotation displays rectangle (or square). When opened, they display pop-up window
@@ -216,18 +234,67 @@ public:
void updateTrailerDictionary(PDFInteger objectCount);
-/* END GENERATED CODE */
-
-private:
- PDFObjectReference addObject(PDFObject object);
- void mergeTo(PDFObjectReference reference, PDFObject object);
- QRectF getPopupWindowRect(const QRectF& rectangle) const;
- QString getProducerString() const;
-
- PDFObjectStorage m_storage;
- PDFVersion m_version;
-};
-
-} // namespace pdf
-
-#endif // PDFDOCUMENTBUILDER_H
+ /// Appends a new page after last page.
+ /// \param mediaBox Media box of the page (size of paper)
+ PDFObjectReference appendPage(QRectF mediaBox);
+
+
+ /// Creates page tree root for the catalog. This function is only called when new document is being
+ /// created. Do not call this function manually.
+ PDFObjectReference createCatalogPageTreeRoot();
+
+
+/* END GENERATED CODE */
+
+private:
+ PDFObjectReference addObject(PDFObject object);
+ void mergeTo(PDFObjectReference reference, PDFObject object);
+ void appendTo(PDFObjectReference reference, PDFObject object);
+ QRectF getPopupWindowRect(const QRectF& rectangle) const;
+ QString getProducerString() const;
+ PDFObjectReference getPageTreeRoot() const;
+ PDFInteger getPageTreeRootChildCount() const;
+
+ PDFObjectStorage m_storage;
+ PDFVersion m_version;
+};
+
+// Implementation
+
+inline
+const PDFObject& PDFDocumentBuilder::getObject(const PDFObject& object) const
+{
+ if (object.isReference())
+ {
+ // Try to dereference the object
+ return m_storage.getObject(object.getReference());
+ }
+
+ return object;
+}
+
+inline
+const PDFDictionary* PDFDocumentBuilder::getDictionaryFromObject(const PDFObject& object) const
+{
+ const PDFObject& dereferencedObject = getObject(object);
+ if (dereferencedObject.isDictionary())
+ {
+ return dereferencedObject.getDictionary();
+ }
+ else if (dereferencedObject.isStream())
+ {
+ return dereferencedObject.getStream()->getDictionary();
+ }
+
+ return nullptr;
+}
+
+inline
+const PDFObject& PDFDocumentBuilder::getObjectByReference(PDFObjectReference reference) const
+{
+ return m_storage.getObject(reference);
+}
+
+} // namespace pdf
+
+#endif // PDFDOCUMENTBUILDER_H
diff --git a/PdfForQtLib/sources/pdfdocumentwriter.cpp b/PdfForQtLib/sources/pdfdocumentwriter.cpp
new file mode 100644
index 0000000..0366115
--- /dev/null
+++ b/PdfForQtLib/sources/pdfdocumentwriter.cpp
@@ -0,0 +1,299 @@
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#include "pdfdocumentwriter.h"
+#include "pdfconstants.h"
+#include "pdfvisitor.h"
+#include "pdfparser.h"
+
+#include
+
+namespace pdf
+{
+
+class PDFWriteObjectVisitor : public PDFAbstractVisitor
+{
+public:
+ explicit PDFWriteObjectVisitor(QIODevice* device) :
+ m_device(device)
+ {
+
+ }
+
+ virtual void visitNull() override;
+ virtual void visitBool(bool value) override;
+ virtual void visitInt(PDFInteger value) override;
+ virtual void visitReal(PDFReal value) override;
+ virtual void visitString(const PDFString* string) override;
+ virtual void visitName(const PDFString* name) override;
+ virtual void visitArray(const PDFArray* array) override;
+ virtual void visitDictionary(const PDFDictionary* dictionary) override;
+ virtual void visitStream(const PDFStream* stream) override;
+ virtual void visitReference(const PDFObjectReference reference) override;
+
+ PDFObject getDecryptedObject();
+
+private:
+ void writeName(const QByteArray& string);
+
+ QIODevice* m_device;
+};
+
+void PDFWriteObjectVisitor::visitNull()
+{
+ m_device->write("null ");
+}
+
+void PDFWriteObjectVisitor::visitBool(bool value)
+{
+ if (value)
+ {
+ m_device->write("true ");
+ }
+ else
+ {
+ m_device->write("false ");
+ }
+}
+
+void PDFWriteObjectVisitor::visitInt(PDFInteger value)
+{
+ m_device->write(QString::number(value).toLatin1());
+ m_device->write(" ");
+}
+
+void PDFWriteObjectVisitor::visitReal(PDFReal value)
+{
+ // Jakub Melka: we use 5 digits, because they are specified
+ // in PDF 1.7 specification, appendix C, Table C.1, where it is defined,
+ // that number of significant digits of precision is 5.
+ m_device->write(QString::number(value, 'f', 5).toLatin1());
+ m_device->write(" ");
+}
+
+void PDFWriteObjectVisitor::visitString(const PDFString* string)
+{
+ const QByteArray& data = string->getString();
+ if (data.indexOf('(') != -1 ||
+ data.indexOf(')') != -1 ||
+ data.indexOf('\\') != -1)
+ {
+ m_device->write("<");
+ m_device->write(data.toHex());
+ m_device->write(">");
+ }
+ else
+ {
+ m_device->write("(");
+ m_device->write(data);
+ m_device->write(")");
+ }
+
+ m_device->write(" ");
+}
+
+void PDFWriteObjectVisitor::writeName(const QByteArray& string)
+{
+ m_device->write("/");
+
+ for (const char character : string)
+ {
+ if (PDFLexicalAnalyzer::isRegular(character))
+ {
+ m_device->write(&character, 1);
+ }
+ else
+ {
+ m_device->write("#");
+ m_device->write(QByteArray(&character, 1).toHex());
+ }
+ }
+
+ m_device->write(" ");
+}
+
+void PDFWriteObjectVisitor::visitName(const PDFString* name)
+{
+ writeName(name->getString());
+}
+
+void PDFWriteObjectVisitor::visitArray(const PDFArray* array)
+{
+ m_device->write("[ ");
+ acceptArray(array);
+ m_device->write("] ");
+}
+
+void PDFWriteObjectVisitor::visitDictionary(const PDFDictionary* dictionary)
+{
+ m_device->write("<< ");
+
+ for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
+ {
+ writeName(dictionary->getKey(i));
+ dictionary->getValue(i).accept(this);
+ }
+
+ m_device->write(">> ");
+}
+
+void PDFWriteObjectVisitor::visitStream(const PDFStream* stream)
+{
+ visitDictionary(stream->getDictionary());
+
+ m_device->write("stream");
+ m_device->write("\x0D\x0A");
+ m_device->write(*stream->getContent());
+ m_device->write("\x0D\x0A");
+ m_device->write("endstream");
+}
+
+void PDFWriteObjectVisitor::visitReference(const PDFObjectReference reference)
+{
+ visitInt(reference.objectNumber);
+ visitInt(reference.generation);
+ m_device->write("R ");
+}
+
+PDFOperationResult PDFDocumentWriter::write(const QString& fileName, const PDFDocument* document)
+{
+ QFile file(fileName);
+
+ if (file.open(QFile::WriteOnly | QFile::Truncate))
+ {
+ PDFOperationResult result = write(&file, document);
+ file.close();
+ return result;
+ }
+ else
+ {
+ return tr("File '%1' can't be opened for writing. %2").arg(fileName, file.errorString());
+ }
+}
+
+PDFOperationResult PDFDocumentWriter::write(QIODevice* device, const PDFDocument* document)
+{
+ if (!device->isWritable())
+ {
+ return tr("Device is not writable.");
+ }
+
+ const PDFObjectStorage& storage = document->getStorage();
+ const PDFObjectStorage::PDFObjects& objects = storage.getObjects();
+ const size_t objectCount = objects.size();
+ if (storage.getSecurityHandler()->getMode() != EncryptionMode::None)
+ {
+ return tr("Writing of encrypted documents is not supported.");
+ }
+
+ // Write header
+ PDFVersion version = document->getInfo()->version;
+ device->write(QString("%PDF-%1.%2").arg(version.major).arg(version.minor).toLatin1());
+ writeCRLF(device);
+ device->write("% PDF producer: ");
+ device->write(PDF_LIBRARY_NAME);
+ writeCRLF(device);
+ writeCRLF(device);
+ writeCRLF(device);
+
+ // Write objects
+ std::vector offsets(objectCount, -1);
+ for (size_t i = 0; i < objectCount; ++i)
+ {
+ const PDFObjectStorage::Entry& entry = objects[i];
+ if (entry.object.isNull())
+ {
+ continue;
+ }
+
+ // Jakub Melka: we must mark actual position of object
+ offsets[i] = device->pos();
+
+ PDFWriteObjectVisitor visitor(device);
+ writeObjectHeader(device, PDFObjectReference(i, entry.generation));
+ entry.object.accept(&visitor);
+ writeObjectFooter(device);
+ }
+
+ // Write cross-reference table
+ PDFInteger xrefOffset = device->pos();
+ device->write("xref");
+ writeCRLF(device);
+ device->write(QString("0 %1").arg(objectCount).toLatin1());
+ writeCRLF(device);
+
+ for (size_t i = 0; i < objectCount; ++i)
+ {
+ const PDFObjectStorage::Entry& entry = objects[i];
+ PDFInteger generation = entry.generation;
+
+ if (i == 0)
+ {
+ generation = 65535;
+ }
+
+ PDFInteger offset = offsets[i];
+ if (offset == -1)
+ {
+ offset = 0;
+ }
+
+ QString offsetString = QString::number(offset).rightJustified(10, QChar('0'), true);
+ QString generationString = QString::number(generation).rightJustified(5, QChar('0'), true);
+
+ device->write(offsetString.toLatin1());
+ device->write(" ");
+ device->write(generationString.toLatin1());
+ device->write(" ");
+ device->write(entry.object.isNull() ? "f" : "n");
+ writeCRLF(device);
+ }
+ device->write("trailer");
+ writeCRLF(device);
+ PDFWriteObjectVisitor trailerVisitor(device);
+ storage.getTrailerDictionary().accept(&trailerVisitor);
+ writeCRLF(device);
+ device->write("startxref");
+ writeCRLF(device);
+ device->write(QString::number(xrefOffset).toLatin1());
+ writeCRLF(device);
+
+ // Write footer
+ device->write("%%EOF");
+
+ return true;
+}
+
+void PDFDocumentWriter::writeCRLF(QIODevice* device)
+{
+ device->write("\x0D\x0A");
+}
+
+void PDFDocumentWriter::writeObjectHeader(QIODevice* device, PDFObjectReference reference)
+{
+ QString objectHeader = QString("%1 %2 obj").arg(QString::number(reference.objectNumber)).arg(QString::number(reference.generation));
+ device->write(objectHeader.toLatin1());
+ writeCRLF(device);
+}
+
+void PDFDocumentWriter::writeObjectFooter(QIODevice* device)
+{
+ device->write("endobj");
+ writeCRLF(device);
+}
+
+} // namespace pdf
diff --git a/PdfForQtLib/sources/pdfdocumentwriter.h b/PdfForQtLib/sources/pdfdocumentwriter.h
new file mode 100644
index 0000000..f7dd093
--- /dev/null
+++ b/PdfForQtLib/sources/pdfdocumentwriter.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2020 Jakub Melka
+//
+// This file is part of PdfForQt.
+//
+// PdfForQt is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// PdfForQt is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDFForQt. If not, see .
+
+#ifndef PDFDOCUMENTWRITER_H
+#define PDFDOCUMENTWRITER_H
+
+#include "pdfdocument.h"
+#include "pdfprogress.h"
+#include "pdfutils.h"
+
+#include
+
+namespace pdf
+{
+
+/// Class used for writing PDF documents to the desired target device (or file,
+/// buffer, etc.). If writing is not successful, then error message is returned.
+class PDFFORQTLIBSHARED_EXPORT PDFDocumentWriter
+{
+ Q_DECLARE_TR_FUNCTIONS(pdf::PDFDocumentWriter)
+
+public:
+ explicit inline PDFDocumentWriter(PDFProgress* progress) :
+ m_progress(progress)
+ {
+
+ }
+
+ PDFOperationResult write(const QString& fileName, const PDFDocument* document);
+ PDFOperationResult write(QIODevice* device, const PDFDocument* document);
+
+private:
+ void writeCRLF(QIODevice* device);
+ void writeObjectHeader(QIODevice* device, PDFObjectReference reference);
+ void writeObjectFooter(QIODevice* device);
+
+ /// Progress indicator
+ PDFProgress* m_progress;
+};
+
+} // namespace pdf
+
+#endif // PDFDOCUMENTWRITER_H
diff --git a/PdfForQtLib/sources/pdfutils.h b/PdfForQtLib/sources/pdfutils.h
index 03fac55..b617bae 100644
--- a/PdfForQtLib/sources/pdfutils.h
+++ b/PdfForQtLib/sources/pdfutils.h
@@ -15,7 +15,6 @@
// You should have received a copy of the GNU Lesser General Public License
// along with PDFForQt. If not, see .
-
#ifndef PDFUTILS_H
#define PDFUTILS_H
@@ -497,6 +496,33 @@ static inline bool isFuzzyComparedPointsSame(const QPointF& p1, const QPointF& p
return squaredDistance < squaredTolerance;
}
+/// Storage for result of some operation. Stores, if operation was successful, or not and
+/// also error message, why operation has failed. Can be converted explicitly to bool.
+class PDFOperationResult
+{
+public:
+ inline PDFOperationResult(bool success) :
+ m_success(success)
+ {
+
+ }
+
+ inline PDFOperationResult(QString message) :
+ m_success(false),
+ m_errorMessage(qMove(message))
+ {
+
+ }
+
+ explicit operator bool() const { return m_success; }
+
+ const QString& getErrorMessage() const { return m_errorMessage; }
+
+private:
+ bool m_success;
+ QString m_errorMessage;
+};
+
} // namespace pdf
#endif // PDFUTILS_H
diff --git a/generated_code_definition.xml b/generated_code_definition.xml
index 33c6363..6217e26 100644
--- a/generated_code_definition.xml
+++ b/generated_code_definition.xml
@@ -340,6 +340,13 @@ return annotationObject;
DictionaryItemSimple
WrapName("Catalog")
+
+
+
+ Pages
+ DictionaryItemSimple
+ createCatalogPageTreeRoot()
+
Dictionary
@@ -553,5 +560,170 @@ return annotationObject;
This function is used to update trailer dictionary. Must be called each time the final document is being built.
_void
+
+
+
+
+
+
+
+
+
+ mediaBox
+ _QRectF
+ Media box of the page (size of paper)
+
+
+ Parameters
+
+ _void
+
+
+
+
+
+
+
+
+
+
+
+ Type
+ DictionaryItemSimple
+ WrapName("Page")
+
+
+
+
+ Parent
+ DictionaryItemSimple
+ getPageTreeRoot()
+
+
+
+
+
+ Dictionary
+
+
+
+
+
+ MediaBox
+ DictionaryItemSimple
+ mediaBox
+
+
+
+ Dictionary
+
+
+
+ CreateObject
+ pageReference
+ _PDFObjectReference
+
+
+
+
+
+
+
+
+
+
+
+ Kids
+ DictionaryItemSimple
+ std::initializer_list<PDFObjectReference>{ pageReference }
+
+
+
+
+ Count
+ DictionaryItemSimple
+ getPageTreeRootChildCount() + 1
+
+
+
+ Dictionary
+
+
+
+ CreateObject
+ updatedTreeRoot
+ _PDFObject
+
+
+
+
+
+ Code
+
+ _void
+ appendTo(getPageTreeRoot(), updatedTreeRoot);
+return pageReference;
+
+
+ Structure
+ appendPage
+ Appends a new page after last page.
+ _PDFObjectReference
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Type
+ DictionaryItemSimple
+ WrapName("Pages")
+
+
+
+
+ Kids
+ DictionaryItemSimple
+ WrapEmptyArray()
+
+
+
+
+ Count
+ DictionaryItemSimple
+ 0
+
+
+
+ Dictionary
+
+
+
+ CreateObject
+ pageTreeRoot
+ _PDFObjectReference
+
+
+
+
+
+ Code
+
+ _void
+ return pageTreeRoot;
+
+
+ Structure
+ createCatalogPageTreeRoot
+ Creates page tree root for the catalog. This function is only called when new document is being created. Do not call this function manually.
+ _PDFObjectReference
+