From fcaa288c5b85d11dda8fbdeaba516988454b75e3 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 22 Mar 2020 15:30:34 +0100 Subject: [PATCH] Create a new blank document --- CodeGenerator/codegenerator.cpp | 4 +- PdfExampleGenerator/PdfExampleGenerator.pro | 37 ++ PdfExampleGenerator/main.cpp | 26 + PdfExampleGenerator/pdfexamplesgenerator.cpp | 33 + PdfExampleGenerator/pdfexamplesgenerator.h | 29 + PdfForQt.pro | 2 + PdfForQtLib/PdfForQtLib.pro | 2 + PdfForQtLib/sources/pdfdocument.h | 3 + PdfForQtLib/sources/pdfdocumentbuilder.cpp | 647 +++++++++++-------- PdfForQtLib/sources/pdfdocumentbuilder.h | 421 +++++++----- PdfForQtLib/sources/pdfdocumentwriter.cpp | 299 +++++++++ PdfForQtLib/sources/pdfdocumentwriter.h | 57 ++ PdfForQtLib/sources/pdfutils.h | 28 +- generated_code_definition.xml | 172 +++++ 14 files changed, 1308 insertions(+), 452 deletions(-) create mode 100644 PdfExampleGenerator/PdfExampleGenerator.pro create mode 100644 PdfExampleGenerator/main.cpp create mode 100644 PdfExampleGenerator/pdfexamplesgenerator.cpp create mode 100644 PdfExampleGenerator/pdfexamplesgenerator.h create mode 100644 PdfForQtLib/sources/pdfdocumentwriter.cpp create mode 100644 PdfForQtLib/sources/pdfdocumentwriter.h 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 +