From ad34b9c278608dfdcfdbe7402acb3a6dd04c3d0e Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 8 Feb 2021 18:07:21 -0500 Subject: Implement helpers for file attachments --- libqpdf/QPDFEmbeddedFileDocumentHelper.cc | 146 ++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 libqpdf/QPDFEmbeddedFileDocumentHelper.cc (limited to 'libqpdf/QPDFEmbeddedFileDocumentHelper.cc') diff --git a/libqpdf/QPDFEmbeddedFileDocumentHelper.cc b/libqpdf/QPDFEmbeddedFileDocumentHelper.cc new file mode 100644 index 00000000..6348529d --- /dev/null +++ b/libqpdf/QPDFEmbeddedFileDocumentHelper.cc @@ -0,0 +1,146 @@ +#include + +// File attachments are stored in the /EmbeddedFiles (name tree) key +// of the /Names dictionary from the document catalog. Each entry +// points to a /FileSpec, which in turn points to one more Embedded +// File Streams. Note that file specs can appear in other places as +// well, such as file attachment annotations, among others. +// +// root -> /Names -> /EmbeddedFiles = name tree +// filename -> filespec +// << +// /Desc () +// /EF << +// /F x 0 R +// /UF x 0 R +// >> +// /F (name) +// /UF (name) +// /Type /Filespec +// >> +// x 0 obj +// << +// /Type /EmbeddedFile +// /DL filesize % not in spec? +// /Params << +// /CheckSum +// /CreationDate (D:yyyymmddhhmmss{-hh'mm'|+hh'mm'|Z}) +// /ModDate (D:yyyymmddhhmmss-hh'mm') +// /Size filesize +// /Subtype /mime#2ftype +// >> +// >> + +QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) : + QPDFDocumentHelper(qpdf), + m(new Members()) +{ + auto root = qpdf.getRoot(); + auto names = root.getKey("/Names"); + if (names.isDictionary()) + { + auto embedded_files = names.getKey("/EmbeddedFiles"); + if (embedded_files.isDictionary()) + { + this->m->embedded_files = + std::make_shared( + embedded_files, qpdf); + } + } +} + +QPDFEmbeddedFileDocumentHelper::Members::Members() +{ +} + +bool +QPDFEmbeddedFileDocumentHelper::hasEmbeddedFiles() const +{ + return (this->m->embedded_files.get() != nullptr); +} + +void +QPDFEmbeddedFileDocumentHelper::initEmbeddedFiles() +{ + if (hasEmbeddedFiles()) + { + return; + } + auto root = qpdf.getRoot(); + auto names = root.getKey("/Names"); + if (! names.isDictionary()) + { + names = QPDFObjectHandle::newDictionary(); + root.replaceKey("/Names", names); + } + auto embedded_files = names.getKey("/EmbeddedFiles"); + if (! embedded_files.isDictionary()) + { + auto nth = QPDFNameTreeObjectHelper::newEmpty(this->qpdf); + names.replaceKey("/EmbeddedFiles", nth.getObjectHandle()); + this->m->embedded_files = + std::make_shared(nth); + } +} + +std::shared_ptr +QPDFEmbeddedFileDocumentHelper::getEmbeddedFile(std::string const& name) +{ + std::shared_ptr result; + if (this->m->embedded_files) + { + auto i = this->m->embedded_files->find(name); + if (i != this->m->embedded_files->end()) + { + result = std::make_shared(i->second); + } + } + return result; +} + +std::map> +QPDFEmbeddedFileDocumentHelper::getEmbeddedFiles() +{ + std::map> result; + if (this->m->embedded_files) + { + for (auto const& i: *(this->m->embedded_files)) + { + result[i.first] = std::make_shared( + i.second); + } + } + return result; +} + +void +QPDFEmbeddedFileDocumentHelper::replaceEmbeddedFile( + std::string const& name, QPDFFileSpecObjectHelper const& fs) +{ + initEmbeddedFiles(); + this->m->embedded_files->insert( + name, fs.getObjectHandle()); +} + +bool +QPDFEmbeddedFileDocumentHelper::removeEmbeddedFile(std::string const& name) +{ + if (! hasEmbeddedFiles()) + { + return false; + } + auto iter = this->m->embedded_files->find(name); + if (iter == this->m->embedded_files->end()) + { + return false; + } + auto oh = iter->second; + iter.remove(); + if (oh.isIndirect()) + { + this->qpdf.replaceObject(oh.getObjGen(), QPDFObjectHandle::newNull()); + } + + return true; +} -- cgit v1.2.3-70-g09d2