aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDFEFStreamObjectHelper.cc193
-rw-r--r--libqpdf/QPDFEmbeddedFileDocumentHelper.cc146
-rw-r--r--libqpdf/QPDFFileSpecObjectHelper.cc157
-rw-r--r--libqpdf/build.mk3
4 files changed, 499 insertions, 0 deletions
diff --git a/libqpdf/QPDFEFStreamObjectHelper.cc b/libqpdf/QPDFEFStreamObjectHelper.cc
new file mode 100644
index 00000000..c4e64a71
--- /dev/null
+++ b/libqpdf/QPDFEFStreamObjectHelper.cc
@@ -0,0 +1,193 @@
+#include <qpdf/QPDFEFStreamObjectHelper.hh>
+#include <qpdf/QIntC.hh>
+#include <qpdf/QUtil.hh>
+#include <qpdf/Pl_Count.hh>
+#include <qpdf/Pl_MD5.hh>
+#include <qpdf/Pl_Discard.hh>
+
+QPDFEFStreamObjectHelper::QPDFEFStreamObjectHelper(
+ QPDFObjectHandle oh) :
+ QPDFObjectHelper(oh),
+ m(new Members())
+{
+}
+
+QPDFEFStreamObjectHelper::Members::Members()
+{
+}
+
+QPDFObjectHandle
+QPDFEFStreamObjectHelper::getParam(std::string const& pkey)
+{
+ auto params = this->oh.getDict().getKey("/Params");
+ if (params.isDictionary())
+ {
+ return params.getKey(pkey);
+ }
+ return QPDFObjectHandle::newNull();
+}
+
+void
+QPDFEFStreamObjectHelper::setParam(
+ std::string const& pkey, QPDFObjectHandle const& pval)
+{
+ auto params = this->oh.getDict().getKey("/Params");
+ if (! params.isDictionary())
+ {
+ params = QPDFObjectHandle::newDictionary();
+ this->oh.getDict().replaceKey("/Params", params);
+ }
+ params.replaceKey(pkey, pval);
+}
+
+std::string
+QPDFEFStreamObjectHelper::getCreationDate()
+{
+ auto val = getParam("/CreationDate");
+ if (val.isString())
+ {
+ return val.getUTF8Value();
+ }
+ return "";
+}
+
+std::string
+QPDFEFStreamObjectHelper::getModDate()
+{
+ auto val = getParam("/ModDate");
+ if (val.isString())
+ {
+ return val.getUTF8Value();
+ }
+ return "";
+}
+
+size_t
+QPDFEFStreamObjectHelper::getSize()
+{
+ auto val = getParam("/Size");
+ if (val.isInteger())
+ {
+ return QIntC::to_size(val.getUIntValueAsUInt());
+ }
+ return 0;
+}
+
+std::string
+QPDFEFStreamObjectHelper::getSubtype()
+{
+ auto val = getParam("/Subtype");
+ if (val.isName())
+ {
+ auto n = val.getName();
+ if (n.length() > 1)
+ {
+ return n.substr(1);
+ }
+ }
+ return "";
+}
+
+std::string
+QPDFEFStreamObjectHelper::getChecksum()
+{
+ auto val = getParam("/CheckSum");
+ if (val.isString())
+ {
+ return val.getStringValue();
+ }
+ return "";
+}
+
+QPDFEFStreamObjectHelper
+QPDFEFStreamObjectHelper::createEFStream(
+ QPDF& qpdf, PointerHolder<Buffer> data)
+{
+ return newFromStream(QPDFObjectHandle::newStream(&qpdf, data));
+}
+
+QPDFEFStreamObjectHelper
+QPDFEFStreamObjectHelper::createEFStream(
+ QPDF& qpdf, std::string const& data)
+{
+ return newFromStream(QPDFObjectHandle::newStream(&qpdf, data));
+}
+
+namespace QEF
+{
+ class Provider: public QPDFObjectHandle::StreamDataProvider
+ {
+ public:
+ Provider(std::function<void(Pipeline*)> provider) :
+ StreamDataProvider(false),
+ provider(provider)
+ {
+ }
+ virtual ~Provider() = default;
+ virtual void provideStreamData(int objid, int generation,
+ Pipeline* pipeline) override
+ {
+ this->provider(pipeline);
+ }
+
+ private:
+ std::function<void(Pipeline*)> provider;
+ };
+};
+
+QPDFEFStreamObjectHelper
+QPDFEFStreamObjectHelper::createEFStream(
+ QPDF& qpdf, std::function<void(Pipeline*)> provider)
+{
+ auto stream = QPDFObjectHandle::newStream(&qpdf);
+ stream.replaceStreamData(new QEF::Provider(provider),
+ QPDFObjectHandle::newNull(),
+ QPDFObjectHandle::newNull());
+ return newFromStream(stream);
+}
+
+QPDFEFStreamObjectHelper&
+QPDFEFStreamObjectHelper::setCreationDate(std::string const& date)
+{
+ setParam("/CreationDate", QPDFObjectHandle::newString(date));
+ return *this;
+}
+
+QPDFEFStreamObjectHelper&
+QPDFEFStreamObjectHelper::setModDate(std::string const& date)
+{
+ setParam("/ModDate", QPDFObjectHandle::newString(date));
+ return *this;
+}
+
+QPDFEFStreamObjectHelper&
+QPDFEFStreamObjectHelper::setSubtype(std::string const& subtype)
+{
+ setParam("/Subtype", QPDFObjectHandle::newName("/" + subtype));
+ return *this;
+}
+
+QPDFEFStreamObjectHelper
+QPDFEFStreamObjectHelper::newFromStream(QPDFObjectHandle stream)
+{
+ QPDFEFStreamObjectHelper result(stream);
+ stream.getDict().replaceKey(
+ "/Type", QPDFObjectHandle::newName("/EmbeddedFile"));
+ Pl_Discard discard;
+ Pl_MD5 md5("EF md5", &discard);
+ Pl_Count count("EF size", &md5);
+ if (! stream.pipeStreamData(&count, nullptr, 0, qpdf_dl_all))
+ {
+ stream.warnIfPossible(
+ "unable to get stream data for new embedded file stream");
+ }
+ else
+ {
+ result.setParam(
+ "/Size", QPDFObjectHandle::newInteger(count.getCount()));
+ result.setParam(
+ "/CheckSum", QPDFObjectHandle::newString(
+ QUtil::hex_decode(md5.getHexDigest())));
+ }
+ return result;
+}
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 <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
+
+// 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 <md5>
+// /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<QPDFNameTreeObjectHelper>(
+ 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<QPDFNameTreeObjectHelper>(nth);
+ }
+}
+
+std::shared_ptr<QPDFFileSpecObjectHelper>
+QPDFEmbeddedFileDocumentHelper::getEmbeddedFile(std::string const& name)
+{
+ std::shared_ptr<QPDFFileSpecObjectHelper> 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<QPDFFileSpecObjectHelper>(i->second);
+ }
+ }
+ return result;
+}
+
+std::map<std::string, std::shared_ptr<QPDFFileSpecObjectHelper>>
+QPDFEmbeddedFileDocumentHelper::getEmbeddedFiles()
+{
+ std::map<std::string,
+ std::shared_ptr<QPDFFileSpecObjectHelper>> result;
+ if (this->m->embedded_files)
+ {
+ for (auto const& i: *(this->m->embedded_files))
+ {
+ result[i.first] = std::make_shared<QPDFFileSpecObjectHelper>(
+ 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;
+}
diff --git a/libqpdf/QPDFFileSpecObjectHelper.cc b/libqpdf/QPDFFileSpecObjectHelper.cc
new file mode 100644
index 00000000..ad422d2b
--- /dev/null
+++ b/libqpdf/QPDFFileSpecObjectHelper.cc
@@ -0,0 +1,157 @@
+#include <qpdf/QPDFFileSpecObjectHelper.hh>
+#include <qpdf/QTC.hh>
+#include <qpdf/QPDF.hh>
+#include <qpdf/QUtil.hh>
+
+#include <vector>
+#include <string>
+
+QPDFFileSpecObjectHelper::QPDFFileSpecObjectHelper(
+ QPDFObjectHandle oh) :
+ QPDFObjectHelper(oh)
+{
+ if (! oh.isDictionary())
+ {
+ oh.warnIfPossible("Embedded file object is not a dictionary");
+ return;
+ }
+ auto type = oh.getKey("/Type");
+ if (! (type.isName() && (type.getName() == "/Filespec")))
+ {
+ oh.warnIfPossible("Embedded file object's type is not /Filespec");
+ }
+}
+
+QPDFFileSpecObjectHelper::Members::Members()
+{
+}
+
+static std::vector<std::string> name_keys = {
+ "/UF", "/F", "/Unix", "/DOS", "/Mac"};
+
+std::string
+QPDFFileSpecObjectHelper::getDescription()
+{
+ std::string result;
+ auto desc = this->oh.getKey("/Desc");
+ if (desc.isString())
+ {
+ result = desc.getUTF8Value();
+ }
+ return result;
+}
+
+std::string
+QPDFFileSpecObjectHelper::getFilename()
+{
+ for (auto const& i: name_keys)
+ {
+ auto k = this->oh.getKey(i);
+ if (k.isString())
+ {
+ return k.getUTF8Value();
+ }
+ }
+ return "";
+}
+
+std::map<std::string, std::string>
+QPDFFileSpecObjectHelper::getFilenames()
+{
+ std::map<std::string, std::string> result;
+ for (auto const& i: name_keys)
+ {
+ auto k = this->oh.getKey(i);
+ if (k.isString())
+ {
+ result[i] = k.getUTF8Value();
+ }
+ }
+ return result;
+}
+
+QPDFObjectHandle
+QPDFFileSpecObjectHelper::getEmbeddedFileStream(std::string const& key)
+{
+ auto ef = this->oh.getKey("/EF");
+ if (! ef.isDictionary())
+ {
+ return QPDFObjectHandle::newNull();
+ }
+ if (! key.empty())
+ {
+ return ef.getKey(key);
+ }
+ for (auto const& i: name_keys)
+ {
+ auto k = ef.getKey(i);
+ if (k.isStream())
+ {
+ return k;
+ }
+ }
+ return QPDFObjectHandle::newNull();
+}
+
+QPDFObjectHandle
+QPDFFileSpecObjectHelper::getEmbeddedFileStreams()
+{
+ return this->oh.getKey("/EF");
+}
+
+QPDFFileSpecObjectHelper
+QPDFFileSpecObjectHelper::createFileSpec(
+ QPDF& qpdf,
+ std::string const& filename,
+ std::string const& fullpath)
+{
+ return createFileSpec(
+ qpdf, filename,
+ QPDFEFStreamObjectHelper::createEFStream(
+ qpdf,
+ QUtil::file_provider(fullpath)));
+}
+
+QPDFFileSpecObjectHelper
+QPDFFileSpecObjectHelper::createFileSpec(
+ QPDF& qpdf,
+ std::string const& filename,
+ QPDFEFStreamObjectHelper efsoh)
+{
+ auto oh = qpdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
+ oh.replaceKey("/Type", QPDFObjectHandle::newName("/Filespec"));
+ QPDFFileSpecObjectHelper result(oh);
+ result.setFilename(filename);
+ auto ef = QPDFObjectHandle::newDictionary();
+ ef.replaceKey("/F", efsoh.getObjectHandle());
+ ef.replaceKey("/UF", efsoh.getObjectHandle());
+ oh.replaceKey("/EF", ef);
+ return result;
+}
+
+QPDFFileSpecObjectHelper&
+QPDFFileSpecObjectHelper::setDescription(std::string const& desc)
+{
+ this->oh.replaceKey("/Desc", QPDFObjectHandle::newUnicodeString(desc));
+ return *this;
+}
+
+QPDFFileSpecObjectHelper&
+QPDFFileSpecObjectHelper::setFilename(
+ std::string const& unicode_name,
+ std::string const& compat_name)
+{
+ auto uf = QPDFObjectHandle::newUnicodeString(unicode_name);
+ this->oh.replaceKey("/UF", uf);
+ if (compat_name.empty())
+ {
+ QTC::TC("qpdf", "QPDFFileSpecObjectHelper empty compat_name");
+ this->oh.replaceKey("/F", uf);
+ }
+ else
+ {
+ QTC::TC("qpdf", "QPDFFileSpecObjectHelper non-empty compat_name");
+ this->oh.replaceKey("/F", QPDFObjectHandle::newString(compat_name));
+ }
+ return *this;
+}
diff --git a/libqpdf/build.mk b/libqpdf/build.mk
index ca15611a..f453e58e 100644
--- a/libqpdf/build.mk
+++ b/libqpdf/build.mk
@@ -58,7 +58,10 @@ SRCS_libqpdf = \
libqpdf/QPDFAcroFormDocumentHelper.cc \
libqpdf/QPDFAnnotationObjectHelper.cc \
libqpdf/QPDFCryptoProvider.cc \
+ libqpdf/QPDFEFStreamObjectHelper.cc \
+ libqpdf/QPDFEmbeddedFileDocumentHelper.cc \
libqpdf/QPDFExc.cc \
+ libqpdf/QPDFFileSpecObjectHelper.cc \
libqpdf/QPDFFormFieldObjectHelper.cc \
libqpdf/QPDFMatrix.cc \
libqpdf/QPDFNameTreeObjectHelper.cc \