1 #include <qpdf/QPDFEmbeddedFileDocumentHelper.hh>
2
3 // File attachments are stored in the /EmbeddedFiles (name tree) key
4 // of the /Names dictionary from the document catalog. Each entry
5 // points to a /FileSpec, which in turn points to one more Embedded
6 // File Streams. Note that file specs can appear in other places as
7 // well, such as file attachment annotations, among others.
8 //
9 // root -> /Names -> /EmbeddedFiles = name tree
10 // filename -> filespec
11 // <<
12 // /Desc ()
13 // /EF <<
14 // /F x 0 R
15 // /UF x 0 R
16 // >>
17 // /F (name)
18 // /UF (name)
19 // /Type /Filespec
20 // >>
21 // x 0 obj
22 // <<
23 // /Type /EmbeddedFile
24 // /DL filesize % not in spec?
25 // /Params <<
26 // /CheckSum <md5>
27 // /CreationDate (D:yyyymmddhhmmss{-hh'mm'|+hh'mm'|Z})
28 // /ModDate (D:yyyymmddhhmmss-hh'mm')
29 // /Size filesize
30 // /Subtype /mime#2ftype
31 // >>
32 // >>
33
QPDFEmbeddedFileDocumentHelper(QPDF & qpdf)34 QPDFEmbeddedFileDocumentHelper::QPDFEmbeddedFileDocumentHelper(QPDF& qpdf) :
35 QPDFDocumentHelper(qpdf),
36 m(new Members())
37 {
38 auto root = qpdf.getRoot();
39 auto names = root.getKey("/Names");
40 if (names.isDictionary())
41 {
42 auto embedded_files = names.getKey("/EmbeddedFiles");
43 if (embedded_files.isDictionary())
44 {
45 this->m->embedded_files =
46 std::make_shared<QPDFNameTreeObjectHelper>(
47 embedded_files, qpdf);
48 }
49 }
50 }
51
Members()52 QPDFEmbeddedFileDocumentHelper::Members::Members()
53 {
54 }
55
56 bool
hasEmbeddedFiles() const57 QPDFEmbeddedFileDocumentHelper::hasEmbeddedFiles() const
58 {
59 return (this->m->embedded_files.get() != nullptr);
60 }
61
62 void
initEmbeddedFiles()63 QPDFEmbeddedFileDocumentHelper::initEmbeddedFiles()
64 {
65 if (hasEmbeddedFiles())
66 {
67 return;
68 }
69 auto root = qpdf.getRoot();
70 auto names = root.getKey("/Names");
71 if (! names.isDictionary())
72 {
73 names = QPDFObjectHandle::newDictionary();
74 root.replaceKey("/Names", names);
75 }
76 auto embedded_files = names.getKey("/EmbeddedFiles");
77 if (! embedded_files.isDictionary())
78 {
79 auto nth = QPDFNameTreeObjectHelper::newEmpty(this->qpdf);
80 names.replaceKey("/EmbeddedFiles", nth.getObjectHandle());
81 this->m->embedded_files =
82 std::make_shared<QPDFNameTreeObjectHelper>(nth);
83 }
84 }
85
86 std::shared_ptr<QPDFFileSpecObjectHelper>
getEmbeddedFile(std::string const & name)87 QPDFEmbeddedFileDocumentHelper::getEmbeddedFile(std::string const& name)
88 {
89 std::shared_ptr<QPDFFileSpecObjectHelper> result;
90 if (this->m->embedded_files)
91 {
92 auto i = this->m->embedded_files->find(name);
93 if (i != this->m->embedded_files->end())
94 {
95 result = std::make_shared<QPDFFileSpecObjectHelper>(i->second);
96 }
97 }
98 return result;
99 }
100
101 std::map<std::string, std::shared_ptr<QPDFFileSpecObjectHelper>>
getEmbeddedFiles()102 QPDFEmbeddedFileDocumentHelper::getEmbeddedFiles()
103 {
104 std::map<std::string,
105 std::shared_ptr<QPDFFileSpecObjectHelper>> result;
106 if (this->m->embedded_files)
107 {
108 for (auto const& i: *(this->m->embedded_files))
109 {
110 result[i.first] = std::make_shared<QPDFFileSpecObjectHelper>(
111 i.second);
112 }
113 }
114 return result;
115 }
116
117 void
replaceEmbeddedFile(std::string const & name,QPDFFileSpecObjectHelper const & fs)118 QPDFEmbeddedFileDocumentHelper::replaceEmbeddedFile(
119 std::string const& name, QPDFFileSpecObjectHelper const& fs)
120 {
121 initEmbeddedFiles();
122 this->m->embedded_files->insert(
123 name, fs.getObjectHandle());
124 }
125
126 bool
removeEmbeddedFile(std::string const & name)127 QPDFEmbeddedFileDocumentHelper::removeEmbeddedFile(std::string const& name)
128 {
129 if (! hasEmbeddedFiles())
130 {
131 return false;
132 }
133 auto iter = this->m->embedded_files->find(name);
134 if (iter == this->m->embedded_files->end())
135 {
136 return false;
137 }
138 auto oh = iter->second;
139 iter.remove();
140 if (oh.isIndirect())
141 {
142 this->qpdf.replaceObject(oh.getObjGen(), QPDFObjectHandle::newNull());
143 }
144
145 return true;
146 }
147