Add attachment information to the json output

This commit is contained in:
Jay Berkenbilt 2021-02-10 15:35:01 -05:00
parent 832d792e4e
commit accb891b4f
17 changed files with 99 additions and 2 deletions

View File

@ -1,5 +1,8 @@
2021-02-10 Jay Berkenbilt <ejb@ql.org>
* Add "attachments" as an additional json key, and add some
information about attachments to the json output.
* Add new command-line arguments for operating on attachments:
--list-attachments, --add-attachment, --remove-attachment,
--copy-attachments-from. See --help and manual for details.

View File

@ -5104,6 +5104,19 @@ print "\n";
than using <option>@file</option> for this purpose.
</para>
</listitem>
<listitem>
<para>
Add some information about attachments to the json output,
and added <literal>attachments</literal> as an additional
json key. The information included here is limited to the
preferred name and content stream and a reference to the
file spec object. This is enough detail for clients to avoid
the hassle of navigating a name tree and provides what is
needed for basic enumeration and extraction of attachments.
More detailed information can be obtained by following the
reference to the file spec object.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>

View File

@ -699,6 +699,22 @@ static JSON json_schema(std::set<std::string>* keys = 0)
"filemethod",
JSON::makeString("encryption method for attachments"));
}
if (all_keys || keys->count("attachments"))
{
JSON attachments = schema.addDictionaryMember(
"attachments", JSON::makeDictionary());
JSON details = attachments.addDictionaryMember(
"<attachment-key>", JSON::makeDictionary());
details.addDictionaryMember(
"filespec",
JSON::makeString("object containing the file spec"));
details.addDictionaryMember(
"preferredname",
JSON::makeString("most preferred file name"));
details.addDictionaryMember(
"preferredcontents",
JSON::makeString("most preferred embedded file stream"));
}
return schema;
}
@ -1114,7 +1130,7 @@ ArgParser::initOptionTable()
// places: json_schema, do_json, and initOptionTable.
char const* json_key_choices[] = {
"objects", "objectinfo", "pages", "pagelabels", "outlines",
"acroform", "encrypt", 0};
"acroform", "encrypt", "attachments", 0};
(*t)["json-key"] = oe_requiredChoices(
&ArgParser::argJsonKey, json_key_choices);
(*t)["json-object"] = oe_requiredParameter(
@ -4568,6 +4584,28 @@ static void do_json_encrypt(QPDF& pdf, Options& o, JSON& j)
"filemethod", JSON::makeString(s_file_method));
}
static void do_json_attachments(QPDF& pdf, Options& o, JSON& j)
{
JSON j_attachments = j.addDictionaryMember(
"attachments", JSON::makeDictionary());
QPDFEmbeddedFileDocumentHelper efdh(pdf);
for (auto const& iter: efdh.getEmbeddedFiles())
{
std::string const& key = iter.first;
auto fsoh = iter.second;
auto j_details = j_attachments.addDictionaryMember(
key, JSON::makeDictionary());
j_details.addDictionaryMember(
"filespec",
JSON::makeString(fsoh->getObjectHandle().unparse()));
j_details.addDictionaryMember(
"preferredname", JSON::makeString(fsoh->getFilename()));
j_details.addDictionaryMember(
"preferredcontents",
JSON::makeString(fsoh->getEmbeddedFileStream().unparse()));
}
}
static void do_json(QPDF& pdf, Options& o)
{
JSON j = JSON::makeDictionary();
@ -4628,6 +4666,10 @@ static void do_json(QPDF& pdf, Options& o)
{
do_json_encrypt(pdf, o, j);
}
if (all_keys || o.json_keys.count("attachments"))
{
do_json_attachments(pdf, o, j);
}
// Check against schema

View File

@ -523,7 +523,7 @@ $td->runtest("page operations on form xobject",
show_ntests();
# ----------
$td->notify("--- File Attachments ---");
$n_tests += 33;
$n_tests += 34;
open(F, ">auto-txt") or die;
print F "from file";
@ -547,6 +547,10 @@ $td->runtest("list attachments verbose",
{$td->COMMAND => "qpdf --list-attachments --verbose a.pdf"},
{$td->FILE => "test76-list-verbose.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("attachments json",
{$td->COMMAND => "qpdf --json --json-key=attachments a.pdf"},
{$td->FILE => "test76-json.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("remove attachment (test_driver)",
{$td->COMMAND => "test_driver 77 test76.pdf"},
{$td->STRING => "test 77 done\n", $td->EXIT_STATUS => 0},

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -385,6 +385,7 @@
"hasacroform": true,
"needappearances": true
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -385,6 +385,7 @@
"hasacroform": true,
"needappearances": true
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -4,6 +4,7 @@
"hasacroform": false,
"needappearances": false
},
"attachments": {},
"encrypt": {
"capabilities": {
"accessibility": true,

View File

@ -0,0 +1,23 @@
{
"attachments": {
"att1": {
"filespec": "4 0 R",
"preferredcontents": "8 0 R",
"preferredname": "att1.txt"
},
"att2": {
"filespec": "5 0 R",
"preferredcontents": "10 0 R",
"preferredname": "att2.txt"
},
"att3": {
"filespec": "6 0 R",
"preferredcontents": "12 0 R",
"preferredname": "π.txt"
}
},
"parameters": {
"decodelevel": "generalized"
},
"version": 1
}