summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/qpdf/QPDF.hh2
-rw-r--r--include/qpdf/QPDFWriter.hh12
-rw-r--r--libqpdf/QPDF.cc24
-rw-r--r--libqpdf/QPDFWriter.cc279
-rw-r--r--qpdf/qpdf.cc38
-rw-r--r--qpdf/qpdf.testcov13
-rw-r--r--qpdf/qtest/qpdf.test54
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdfbin0 -> 865 bytes
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf104
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-force-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-min-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdfbin0 -> 923 bytes
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf108
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe-other.pdf104
-rw-r--r--qpdf/qtest/qpdf/extensions-adbe.pdf106
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdfbin0 -> 865 bytes
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf102
-rw-r--r--qpdf/qtest/qpdf/extensions-none-force-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-none-min-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdfbin0 -> 923 bytes
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf108
-rw-r--r--qpdf/qtest/qpdf/extensions-other-force-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.6.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.7.1.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.7.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.7.3.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.8.0.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.8.2.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.8.5.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other-min-1.8.out4
-rw-r--r--qpdf/qtest/qpdf/extensions-other.pdf98
-rw-r--r--qpdf/qtest/qpdf/obj0-check.out6
-rw-r--r--qpdf/test_driver.cc7
92 files changed, 1423 insertions, 30 deletions
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 00591ea5..866b11b3 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -144,6 +144,8 @@ class QPDF
QPDF_DLL
std::string getPDFVersion() const;
QPDF_DLL
+ int getExtensionLevel();
+ QPDF_DLL
QPDFObjectHandle getTrailer();
QPDF_DLL
QPDFObjectHandle getRoot();
diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh
index 89536e13..15963890 100644
--- a/include/qpdf/QPDFWriter.hh
+++ b/include/qpdf/QPDFWriter.hh
@@ -153,6 +153,8 @@ class QPDFWriter
// streams are used.
QPDF_DLL
void setMinimumPDFVersion(std::string const&);
+ QPDF_DLL
+ void setMinimumPDFVersion(std::string const&, int extension_level);
// Force the PDF version of the output file to be a given version.
// Use of this function may create PDF files that will not work
@@ -171,6 +173,8 @@ class QPDFWriter
// object streams.
QPDF_DLL
void forcePDFVersion(std::string const&);
+ QPDF_DLL
+ void forcePDFVersion(std::string const&, int extension_level);
// Provide additional text to insert in the PDF file somewhere
// near the beginning of the file. This can be used to add
@@ -251,6 +255,7 @@ class QPDFWriter
static int const f_stream = 1 << 0;
static int const f_filtered = 1 << 1;
static int const f_in_ostream = 1 << 2;
+ static int const f_in_extensions = 1 << 3;
enum trailer_e { t_normal, t_lin_first, t_lin_second };
@@ -286,7 +291,8 @@ class QPDFWriter
char const* user_password, char const* owner_password,
bool allow_accessibility, bool allow_extract,
qpdf_r3_print_e print, qpdf_r3_modify_e modify);
- void disableIncompatibleEncryption(int major, int minor);
+ void disableIncompatibleEncryption(int major, int minor,
+ int extension_level);
void parseVersion(std::string const& version, int& major, int& minor) const;
int compareVersions(int major1, int minor1, int major2, int minor2) const;
void setEncryptionParameters(
@@ -375,8 +381,12 @@ class QPDFWriter
std::string id1; // for /ID key of
std::string id2; // trailer dictionary
+ std::string final_pdf_version;
+ int final_extension_level;
std::string min_pdf_version;
+ int min_extension_level;
std::string forced_pdf_version;
+ int forced_extension_level;
std::string extra_header_text;
int encryption_dict_objid;
std::string cur_data_key;
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index fac79796..9d5d8240 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -1872,6 +1872,30 @@ QPDF::getPDFVersion() const
return this->pdf_version;
}
+int
+QPDF::getExtensionLevel()
+{
+ int result = 0;
+ QPDFObjectHandle obj = getRoot();
+ if (obj.hasKey("/Extensions"))
+ {
+ obj = obj.getKey("/Extensions");
+ if (obj.isDictionary() && obj.hasKey("/ADBE"))
+ {
+ obj = obj.getKey("/ADBE");
+ if (obj.isDictionary() && obj.hasKey("/ExtensionLevel"))
+ {
+ obj = obj.getKey("/ExtensionLevel");
+ if (obj.isInteger())
+ {
+ result = obj.getIntValue();
+ }
+ }
+ }
+ }
+ return result;
+}
+
QPDFObjectHandle
QPDF::getTrailer()
{
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index f893053b..963f9627 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -64,6 +64,9 @@ QPDFWriter::init()
object_stream_mode = qpdf_o_preserve;
encrypt_metadata = true;
encrypt_use_aes = false;
+ min_extension_level = 0;
+ final_extension_level = 0;
+ forced_extension_level = 0;
encryption_dict_objid = 0;
next_objid = 1;
cur_stream_length_id = 0;
@@ -170,10 +173,19 @@ QPDFWriter::setQDFMode(bool val)
void
QPDFWriter::setMinimumPDFVersion(std::string const& version)
{
+ setMinimumPDFVersion(version, 0);
+}
+
+void
+QPDFWriter::setMinimumPDFVersion(std::string const& version,
+ int extension_level)
+{
bool set_version = false;
+ bool set_extension_level = false;
if (this->min_pdf_version.empty())
{
set_version = true;
+ set_extension_level = true;
}
else
{
@@ -183,10 +195,22 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version)
int min_minor = 0;
parseVersion(version, old_major, old_minor);
parseVersion(this->min_pdf_version, min_major, min_minor);
- if (compareVersions(old_major, old_minor, min_major, min_minor) > 0)
+ int compare = compareVersions(
+ old_major, old_minor, min_major, min_minor);
+ if (compare > 0)
{
- QTC::TC("qpdf", "QPDFWriter increasing minimum version");
+ QTC::TC("qpdf", "QPDFWriter increasing minimum version",
+ extension_level == 0 ? 0 : 1);
set_version = true;
+ set_extension_level = true;
+ }
+ else if (compare == 0)
+ {
+ if (extension_level > this->min_extension_level)
+ {
+ QTC::TC("qpdf", "QPDFWriter increasing extension level");
+ set_extension_level = true;
+ }
}
}
@@ -194,12 +218,24 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version)
{
this->min_pdf_version = version;
}
+ if (set_extension_level)
+ {
+ this->min_extension_level = extension_level;
+ }
}
void
QPDFWriter::forcePDFVersion(std::string const& version)
{
+ forcePDFVersion(version, 0);
+}
+
+void
+QPDFWriter::forcePDFVersion(std::string const& version,
+ int extension_level)
+{
this->forced_pdf_version = version;
+ this->forced_extension_level = extension_level;
}
void
@@ -476,7 +512,8 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
}
void
-QPDFWriter::disableIncompatibleEncryption(int major, int minor)
+QPDFWriter::disableIncompatibleEncryption(int major, int minor,
+ int extension_level)
{
if (! this->encrypted)
{
@@ -513,6 +550,15 @@ QPDFWriter::disableIncompatibleEncryption(int major, int minor)
disable = true;
}
}
+ else if ((compareVersions(major, minor, 1, 7) < 0) ||
+ ((compareVersions(major, minor, 1, 7) == 0) &&
+ extension_level < 3))
+ {
+ if ((V >= 5) || (R >= 5))
+ {
+ disable = true;
+ }
+ }
}
if (disable)
{
@@ -584,15 +630,26 @@ QPDFWriter::setEncryptionParametersInternal(
encryption_dictionary["/P"] = QUtil::int_to_string(P);
encryption_dictionary["/O"] = QPDF_String(O).unparse(true);
encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
- setMinimumPDFVersion("1.3");
- if (R == 3)
+ if (V >= 5)
{
setMinimumPDFVersion("1.4");
}
- else if (R >= 4)
+ if (R >= 5)
+ {
+ setMinimumPDFVersion("1.7", 3);
+ }
+ else if (R == 4)
{
setMinimumPDFVersion(this->encrypt_use_aes ? "1.6" : "1.5");
}
+ else if (R == 3)
+ {
+ setMinimumPDFVersion("1.4");
+ }
+ else
+ {
+ setMinimumPDFVersion("1.3");
+ }
if ((R >= 4) && (! encrypt_metadata))
{
@@ -1005,7 +1062,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
unsigned int flags, size_t stream_length,
bool compress)
{
- unsigned int child_flags = flags & ~f_stream;
+ unsigned int child_flags = flags & ~f_stream & ~f_in_extensions;
std::string indent;
for (int i = 0; i < level; ++i)
@@ -1037,8 +1094,143 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
}
else if (object.isDictionary())
{
+ // Handle special cases for specific dictionaries.
+
+ // Extensions dictionaries are complicated. We have one of
+ // several cases:
+ //
+ // * We need ADBE
+ // - We already have Extensions
+ // - If it has the right ADBE, preserve it
+ // - Otherwise, replace ADBE
+ // - We don't have Extensions: create one from scratch
+ // * We don't want ADBE
+ // - We already have Extensions
+ // - If it only has ADBE, remove it
+ // - If it has other things, keep those and remove ADBE
+ // - We have no extensions: no action required
+ //
+ // We may be in the root dictionary, or we may be inside the
+ // extensions dictionary itself. The latter is determined by
+ // the presence of the f_in_extensions flag.
+
+ bool is_root = false;
+ bool have_extensions_other = false;
+ bool have_extensions_adbe = false;
+
+ QPDFObjectHandle extensions;
+ if (object.getObjectID() == pdf.getRoot().getObjectID())
+ {
+ is_root = true;
+ if (object.hasKey("/Extensions") &&
+ object.getKey("/Extensions").isDictionary())
+ {
+ extensions = object.getKey("/Extensions");
+ }
+ }
+ else if (flags & f_in_extensions)
+ {
+ extensions = object;
+ }
+ if (extensions.isInitialized())
+ {
+ std::set<std::string> keys = extensions.getKeys();
+ if (keys.count("/ADBE") > 0)
+ {
+ have_extensions_adbe = true;
+ keys.erase("/ADBE");
+ }
+ if (keys.size() > 0)
+ {
+ have_extensions_other = true;
+ }
+ }
+
+ bool need_extensions_adbe = (this->final_extension_level > 0);
+
+ bool write_new_extensions = false;
+ bool write_new_adbe = false;
+ bool suppress_existing_extensions = false;
+ bool suppress_existing_adbe = false;
+ if (is_root)
+ {
+ if (need_extensions_adbe)
+ {
+ if (! (have_extensions_other || have_extensions_adbe))
+ {
+ // We need Extensions and don't have it. Create
+ // it here.
+ QTC::TC("qpdf", "QPDFWriter create Extensions",
+ this->qdf_mode ? 0 : 1);
+ write_new_extensions = true;
+ suppress_existing_extensions = true;
+ }
+ else
+ {
+ // Preserve existing Extensions and do the work
+ // in the extensions dictionary.
+ }
+ }
+ else if (! have_extensions_other)
+ {
+ // We have Extensions dictionary and don't want one.
+ suppress_existing_extensions = true;
+ if (have_extensions_adbe)
+ {
+ QTC::TC("qpdf", "QPDFWriter remove existing Extensions");
+ }
+ }
+ }
+ else if (flags & f_in_extensions)
+ {
+ QTC::TC("qpdf", "QPDFWriter preserve Extensions");
+ QPDFObjectHandle adbe = extensions.getKey("/ADBE");
+ if (adbe.isDictionary() &&
+ adbe.hasKey("/BaseVersion") &&
+ adbe.getKey("/BaseVersion").isName() &&
+ (adbe.getKey("/BaseVersion").getName() ==
+ "/" + this->final_pdf_version) &&
+ adbe.hasKey("/ExtensionLevel") &&
+ adbe.getKey("/ExtensionLevel").isInteger() &&
+ (adbe.getKey("/ExtensionLevel").getIntValue() ==
+ this->final_extension_level))
+ {
+ QTC::TC("qpdf", "QPDFWriter preserve ADBE");
+ }
+ else
+ {
+ suppress_existing_adbe = true;
+ if (need_extensions_adbe)
+ {
+ write_new_adbe = true;
+ }
+ }
+ }
+
writeString("<<");
writeStringQDF("\n");
+
+ if (write_new_extensions || write_new_adbe)
+ {
+ writeStringQDF(indent);
+ writeStringQDF(" ");
+ writeStringNoQDF(" ");
+ if (write_new_extensions)
+ {
+ writeString("/Extensions << ");
+ }
+ writeString("/ADBE << /BaseVersion /");
+ writeString(this->final_pdf_version);
+ writeString(" /ExtensionLevel ");
+ writeString(QUtil::int_to_string(this->final_extension_level));
+ writeString(" >>");
+ if (write_new_extensions)
+ {
+ writeString(" >>");
+ }
+ writeStringQDF("\n");
+ }
+
std::set<std::string> keys = object.getKeys();
for (std::set<std::string>::iterator iter = keys.begin();
iter != keys.end(); ++iter)
@@ -1057,12 +1249,27 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
{
continue;
}
+
+ bool is_extensions = (is_root && (key == "/Extensions"));
+ if (suppress_existing_extensions && is_extensions)
+ {
+ QTC::TC("qpdf", "QPDFWriter skip Extensions");
+ continue;
+ }
+ if (suppress_existing_adbe && (key == "/ADBE"))
+ {
+ QTC::TC("qpdf", "QPDFWriter skip ADBE");
+ continue;
+ }
+
+
writeStringQDF(indent);
writeStringQDF(" ");
writeStringNoQDF(" ");
writeString(QPDF_Name::normalizeName(key));
writeString(" ");
- unparseChild(object.getKey(key), level + 1, child_flags);
+ unparseChild(object.getKey(key), level + 1,
+ child_flags | (is_extensions ? f_in_extensions : 0));
writeStringQDF("\n");
}
@@ -1706,12 +1913,17 @@ QPDFWriter::prepareFileForWrite()
else if (node.isDictionary() || node.isStream())
{
bool is_stream = false;
+ bool is_root = false;
QPDFObjectHandle dict = node;
if (node.isStream())
{
is_stream = true;
dict = node.getDict();
}
+ else if (pdf.getRoot().getObjectID() == node.getObjectID())
+ {
+ is_root = true;
+ }
std::set<std::string> keys = dict.getKeys();
for (std::set<std::string>::iterator iter = keys.begin();
@@ -1720,19 +1932,43 @@ QPDFWriter::prepareFileForWrite()
std::string const& key = *iter;
QPDFObjectHandle oh = dict.getKey(key);
bool add_to_queue = true;
- if (oh.isIndirect())
+ if (is_stream)
+ {
+ if (oh.isIndirect() &&
+ ((key == "/Length") ||
+ (key == "/Filter") ||
+ (key == "/DecodeParms")))
+ {
+ QTC::TC("qpdf", "QPDFWriter make stream key direct");
+ add_to_queue = false;
+ oh.makeDirect();
+ dict.replaceKey(key, oh);
+ }
+ }
+ else if (is_root)
{
- if (is_stream)
+ if ((key == "/Extensions") && (oh.isDictionary()))
{
- if ((key == "/Length") ||
- (key == "/Filter") ||
- (key == "/DecodeParms"))
+ bool extensions_indirect = false;
+ if (oh.isIndirect())
{
- QTC::TC("qpdf", "QPDF make stream key direct");
+ QTC::TC("qpdf", "QPDFWriter make Extensions direct");
+ extensions_indirect = true;
add_to_queue = false;
- oh.makeDirect();
+ oh = oh.shallowCopy();
dict.replaceKey(key, oh);
}
+ if (oh.hasKey("/ADBE"))
+ {
+ QPDFObjectHandle adbe = oh.getKey("/ADBE");
+ if (adbe.isIndirect())
+ {
+ QTC::TC("qpdf", "QPDFWriter make ADBE direct",
+ extensions_indirect ? 0 : 1);
+ adbe.makeDirect();
+ oh.replaceKey("/ADBE", adbe);
+ }
+ }
}
}
@@ -1791,7 +2027,8 @@ QPDFWriter::write()
int major = 0;
int minor = 0;
parseVersion(this->forced_pdf_version, major, minor);
- disableIncompatibleEncryption(major, minor);
+ disableIncompatibleEncryption(major, minor,
+ this->forced_extension_level);
if (compareVersions(major, minor, 1, 5) < 0)
{
QTC::TC("qpdf", "QPDFWriter forcing object stream disable");
@@ -1938,16 +2175,18 @@ QPDFWriter::writeEncryptionDictionary()
void
QPDFWriter::writeHeader()
{
- setMinimumPDFVersion(pdf.getPDFVersion());
- std::string version = this->min_pdf_version;
+ setMinimumPDFVersion(pdf.getPDFVersion(), pdf.getExtensionLevel());
+ this->final_pdf_version = this->min_pdf_version;
+ this->final_extension_level = this->min_extension_level;
if (! this->forced_pdf_version.empty())
{
QTC::TC("qpdf", "QPDFWriter using forced PDF version");
- version = this->forced_pdf_version;
+ this->final_pdf_version = this->forced_pdf_version;
+ this->final_extension_level = this->forced_extension_level;
}
writeString("%PDF-");
- writeString(version);
+ writeString(this->final_pdf_version);
// This string of binary characters would not be valid UTF-8, so
// it really should be treated as binary.
writeString("\n%\xbf\xf7\xa2\xfe\n");
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index ddb2ad69..e5b47494 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -189,6 +189,9 @@ familiar with the PDF file format or who are PDF developers.\n\
--min-version=version sets the minimum PDF version of the output file\n\
--force-version=version forces this to be the PDF version of the output file\n\
\n\
+Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n\
+means PDF version 1.7 at extension level 3.\n\
+\n\
Values for stream data options:\n\
\n\
compress recompress stream data when possible (default)\n\
@@ -838,6 +841,21 @@ QPDFPageData::QPDFPageData(QPDF* qpdf, char const* range) :
this->selected_pages = parse_numrange(range, (int)this->orig_pages.size());
}
+static void parse_version(std::string const& full_version_string,
+ std::string& version, int& extension_level)
+{
+ PointerHolder<char> vp(true, QUtil::copy_string(full_version_string));
+ char* v = vp.getPointer();
+ char* p1 = strchr(v, '.');
+ char* p2 = (p1 ? strchr(1 + p1, '.') : 0);
+ if (p2 && *(p2 + 1))
+ {
+ *p2++ = '\0';
+ extension_level = atoi(p2);
+ }
+ version = v;
+}
+
int main(int argc, char* argv[])
{
whoami = QUtil::getWhoami(argv[0]);
@@ -1370,8 +1388,14 @@ int main(int argc, char* argv[])
std::cout << "checking " << infilename << std::endl;
try
{
- std::cout << "PDF Version: " << pdf.getPDFVersion()
- << std::endl;
+ int extension_level = pdf.getExtensionLevel();
+ std::cout << "PDF Version: " << pdf.getPDFVersion();
+ if (extension_level > 0)
+ {
+ std::cout << " extension level "
+ << pdf.getExtensionLevel();
+ }
+ std::cout << std::endl;
::show_encryption(pdf);
if (pdf.isLinearized())
{
@@ -1603,11 +1627,17 @@ int main(int argc, char* argv[])
}
if (! min_version.empty())
{
- w.setMinimumPDFVersion(min_version);
+ std::string version;
+ int extension_level = 0;
+ parse_version(min_version, version, extension_level);
+ w.setMinimumPDFVersion(version, extension_level);
}
if (! force_version.empty())
{
- w.forcePDFVersion(force_version);
+ std::string version;
+ int extension_level = 0;
+ parse_version(force_version, version, extension_level);
+ w.forcePDFVersion(version, extension_level);
}
w.write();
}
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index bad9bbf7..1f8238e0 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -151,7 +151,7 @@ qpdf-c called qpdf_allow_modify_form 0
qpdf-c called qpdf_allow_modify_annotation 0
qpdf-c called qpdf_allow_modify_other 0
qpdf-c called qpdf_allow_modify_all 0
-QPDFWriter increasing minimum version 0
+QPDFWriter increasing minimum version 1
QPDFWriter using forced PDF version 0
qpdf-c called qpdf_set_minimum_pdf_version 0
qpdf-c called qpdf_force_pdf_version 0
@@ -243,4 +243,13 @@ QPDFWriter extra header text no newline 0
QPDFWriter extra header text add newline 0
QPDF bogus 0 offset 0
QPDF global offset 0
-QPDF make stream key direct 0
+QPDFWriter make stream key direct 0
+QPDFWriter increasing extension level 0
+QPDFWriter make Extensions direct 0
+QPDFWriter make ADBE direct 1
+QPDFWriter preserve Extensions 0
+QPDFWriter create Extensions 1
+QPDFWriter skip ADBE 0
+QPDFWriter remove existing Extensions 0
+QPDFWriter skip Extensions 0
+QPDFWriter preserve ADBE 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index e06ac221..cf73c992 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -111,6 +111,55 @@ $td->runtest("new stream",
show_ntests();
# ----------
+$td->notify("--- Extensions Dictionary Tests ---");
+my @ext_inputs = ('minimal.pdf', 'extensions-adbe.pdf',
+ 'extensions-other.pdf', 'extensions-adbe-other.pdf');
+my @new_versions = ('1.3', '1.6', '1.7.1', '1.7.2', '1.7.3',
+ '1.8', '1.8.0', '1.8.2', '1.8.5');
+$n_tests += (4 * @new_versions + 3) * @ext_inputs;
+foreach my $input (@ext_inputs)
+{
+ my $base = $input;
+ $base =~ s/\.pdf$//;
+ if ($base eq 'minimal')
+ {
+ $base = 'extensions-none';
+ }
+ foreach my $version (@new_versions)
+ {
+ foreach my $op (qw(min force))
+ {
+ $td->runtest("$input: $op version to $version",
+ {$td->COMMAND =>
+ "qpdf --static-id" .
+ " --$op-version=$version $input a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+ $td->runtest("check version information",
+ {$td->COMMAND => "test_driver 34 a.pdf"},
+ {$td->FILE => "$base-$op-$version.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+ if (($op eq 'force') && ($version eq '1.8.5'))
+ {
+ # Look at the actual file for a few cases to make sure
+ # qdf and non-qdf output are okay
+ $td->runtest("check file",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "$base-$op-$version.pdf"});
+ $td->runtest("$input: $op version to $version",
+ {$td->COMMAND =>
+ "qpdf --qdf --static-id" .
+ " --$op-version=$version $input a.qdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+ $td->runtest("check file",
+ {$td->FILE => "a.qdf"},
+ {$td->FILE => "$base-$op-$version.qdf"});
+ }
+ }
+ }
+}
+show_ntests();
+# ----------
$td->notify("--- Page API Tests ---");
$n_tests += 9;
@@ -147,6 +196,7 @@ $td->runtest("remove page we don't have",
{$td->COMMAND => "test_driver 22 page_api_1.pdf"},
{$td->FILE => "page_api_1.out2", $td->EXIT_STATUS => 2},
$td->NORMALIZE_NEWLINES);
+show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
$n_tests += 57;
@@ -466,6 +516,7 @@ foreach my $d (@nrange_tests)
$td->NORMALIZE_NEWLINES);
}
+show_ntests();
# ----------
$td->notify("--- Merging and Splitting ---");
$n_tests += 6;
@@ -513,6 +564,7 @@ $td->runtest("avoid respecification of password",
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "pages-copy-encryption.pdf"});
+show_ntests();
# ----------
$td->notify("--- PDF From Scratch ---");
$n_tests += 2;
@@ -524,6 +576,7 @@ $td->runtest("basic qpdf from scratch",
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "from-scratch-0.pdf"});
+show_ntests();
# ----------
$td->notify("--- Copy Foreign Objects ---");
$n_tests += 7;
@@ -546,6 +599,7 @@ $td->runtest("copy objects error",
{$td->FILE => "copy-foreign-objects-errors.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
+show_ntests();
# ----------
$td->notify("--- Error Condition Tests ---");
# $n_tests incremented after initialization of badfiles below.
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out
new file mode 100644
index 00000000..3795c645
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out
@@ -0,0 +1,4 @@
+version: 1.3
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out
new file mode 100644
index 00000000..762063b9
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out
new file mode 100644
index 00000000..96168639
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out
new file mode 100644
index 00000000..4571bf2e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out
new file mode 100644
index 00000000..76321208
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out
new file mode 100644
index 00000000..7823a1dd
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out
new file mode 100644
index 00000000..2d78af7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf
new file mode 100644
index 00000000..7dbab516
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf
new file mode 100644
index 00000000..111ed12a
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf
@@ -0,0 +1,104 @@
+%PDF-1.8
+%¿÷¢þ
+%QDF-1.0
+
+%% Original object ID: 1 0
+1 0 obj
+<<
+ /Extensions <<
+ /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
+ >>
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+%% Original object ID: 2 0
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+%% Original object ID: 3 0
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+%% Original object ID: 4 0
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+%% Original object ID: 6 0
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 7 0
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+xref
+0 8
+0000000000 65535 f
+0000000052 00000 n
+0000000207 00000 n
+0000000316 00000 n
+0000000558 00000 n
+0000000657 00000 n
+0000000703 00000 n
+0000000848 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+ /ID [<e42c124696c09bd2cacaf7196e9c88a0><31415926535897932384626433832795>]
+>>
+startxref
+883
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out
new file mode 100644
index 00000000..4571bf2e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out
new file mode 100644
index 00000000..4571bf2e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out
new file mode 100644
index 00000000..4571bf2e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out
new file mode 100644
index 00000000..4571bf2e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out
new file mode 100644
index 00000000..76321208
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out
new file mode 100644
index 00000000..7823a1dd
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out
new file mode 100644
index 00000000..2d78af7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out
new file mode 100644
index 00000000..14467414
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out
@@ -0,0 +1,4 @@
+version: 1.3
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out
new file mode 100644
index 00000000..0cc726a6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out
new file mode 100644
index 00000000..a714b145
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out
new file mode 100644
index 00000000..f13ea01b
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out
new file mode 100644
index 00000000..00858aaa
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out
new file mode 100644
index 00000000..dcf87e86
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf
new file mode 100644
index 00000000..39ea71ef
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf
new file mode 100644
index 00000000..5993af1f
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf
@@ -0,0 +1,108 @@
+%PDF-1.8
+%¿÷¢þ
+%QDF-1.0
+
+%% Original object ID: 1 0
+1 0 obj
+<<
+ /Extensions <<
+ /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
+ /Potato <<
+ /BaseVersion /3.14159
+ /ExtensionLevel 16059
+ >>
+ >>
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+%% Original object ID: 2 0
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+%% Original object ID: 3 0
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+%% Original object ID: 4 0
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+%% Original object ID: 6 0
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 7 0
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+xref
+0 8
+0000000000 65535 f
+0000000052 00000 n
+0000000285 00000 n
+0000000394 00000 n
+0000000636 00000 n
+0000000735 00000 n
+0000000781 00000 n
+0000000926 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+ /ID [<484577389048fa45fc00a1f5b434efa5><31415926535897932384626433832795>]
+>>
+startxref
+961
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out
new file mode 100644
index 00000000..f13ea01b
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out
new file mode 100644
index 00000000..00858aaa
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out
new file mode 100644
index 00000000..dcf87e86
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-adbe-other.pdf b/qpdf/qtest/qpdf/extensions-adbe-other.pdf
new file mode 100644
index 00000000..367f3766
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe-other.pdf
@@ -0,0 +1,104 @@
+%PDF-1.7
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 2 0 R
+ /Type /Catalog
+ /Extensions <<
+ /ADBE 8 0 R
+ /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >>
+ >>
+>>
+endobj
+
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+8 0 obj
+<< /BaseVersion /1.7 /ExtensionLevel 2 >>
+endobj
+
+xref
+0 9
+0000000000 65535 f
+0000000025 00000 n
+0000000179 00000 n
+0000000261 00000 n
+0000000476 00000 n
+0000000575 00000 n
+0000000594 00000 n
+0000000712 00000 n
+0000000747 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+ /ID [<484577389048fa45fc00a1f5b434efa5><484577389048fa45fc00a1f5b434efa5>]
+>>
+startxref
+805
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-adbe.pdf b/qpdf/qtest/qpdf/extensions-adbe.pdf
new file mode 100644
index 00000000..4d13cf7d
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-adbe.pdf
@@ -0,0 +1,106 @@
+%PDF-1.7
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 2 0 R
+ /Type /Catalog
+ /Extensions 8 0 R
+>>
+endobj
+
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+8 0 obj
+<< /ADBE 9 0 R >>
+endobj
+
+9 0 obj
+<< /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >>
+endobj
+
+xref
+0 10
+0000000000 65535 f
+0000000025 00000 n
+0000000099 00000 n
+0000000181 00000 n
+0000000396 00000 n
+0000000495 00000 n
+0000000514 00000 n
+0000000632 00000 n
+0000000667 00000 n
+0000000701 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+ /ID [<e42c124696c09bd2cacaf7196e9c88a0><e42c124696c09bd2cacaf7196e9c88a0>]
+>>
+startxref
+793
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.3.out b/qpdf/qtest/qpdf/extensions-none-force-1.3.out
new file mode 100644
index 00000000..3795c645
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.3.out
@@ -0,0 +1,4 @@
+version: 1.3
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.6.out b/qpdf/qtest/qpdf/extensions-none-force-1.6.out
new file mode 100644
index 00000000..762063b9
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out
new file mode 100644
index 00000000..96168639
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out
new file mode 100644
index 00000000..234701c6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out
new file mode 100644
index 00000000..76321208
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out
new file mode 100644
index 00000000..7823a1dd
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out
new file mode 100644
index 00000000..2d78af7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf
new file mode 100644
index 00000000..52f5623e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf
new file mode 100644
index 00000000..c6f9a1f6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf
@@ -0,0 +1,102 @@
+%PDF-1.8
+%¿÷¢þ
+%QDF-1.0
+
+%% Original object ID: 1 0
+1 0 obj
+<<
+ /Extensions << /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+%% Original object ID: 2 0
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+%% Original object ID: 3 0
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+%% Original object ID: 4 0
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+%% Original object ID: 6 0
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 5 0
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+xref
+0 8
+0000000000 65535 f
+0000000052 00000 n
+0000000201 00000 n
+0000000310 00000 n
+0000000552 00000 n
+0000000651 00000 n
+0000000697 00000 n
+0000000842 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+ /ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
+>>
+startxref
+877
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.3.out b/qpdf/qtest/qpdf/extensions-none-min-1.3.out
new file mode 100644
index 00000000..3795c645
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.3.out
@@ -0,0 +1,4 @@
+version: 1.3
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.6.out b/qpdf/qtest/qpdf/extensions-none-min-1.6.out
new file mode 100644
index 00000000..762063b9
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out
new file mode 100644
index 00000000..96168639
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out
new file mode 100644
index 00000000..234701c6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out
new file mode 100644
index 00000000..76321208
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out
new file mode 100644
index 00000000..7823a1dd
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out
new file mode 100644
index 00000000..2d78af7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.out
new file mode 100644
index 00000000..491fdb7e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+null
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.3.out b/qpdf/qtest/qpdf/extensions-other-force-1.3.out
new file mode 100644
index 00000000..14467414
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.3.out
@@ -0,0 +1,4 @@
+version: 1.3
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.6.out b/qpdf/qtest/qpdf/extensions-other-force-1.6.out
new file mode 100644
index 00000000..0cc726a6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out
new file mode 100644
index 00000000..a714b145
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out
new file mode 100644
index 00000000..f13ea01b
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out
new file mode 100644
index 00000000..00858aaa
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out
new file mode 100644
index 00000000..dcf87e86
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf
new file mode 100644
index 00000000..8bf4fe06
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf
new file mode 100644
index 00000000..3c596f7d
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf
@@ -0,0 +1,108 @@
+%PDF-1.8
+%¿÷¢þ
+%QDF-1.0
+
+%% Original object ID: 1 0
+1 0 obj
+<<
+ /Extensions <<
+ /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >>
+ /Potato <<
+ /BaseVersion /3.14159
+ /ExtensionLevel 16059
+ >>
+ >>
+ /Pages 2 0 R
+ /Type /Catalog
+>>
+endobj
+
+%% Original object ID: 2 0
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+%% Original object ID: 3 0
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+%% Original object ID: 4 0
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+%% Original object ID: 6 0
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+%% Original object ID: 7 0
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+xref
+0 8
+0000000000 65535 f
+0000000052 00000 n
+0000000285 00000 n
+0000000394 00000 n
+0000000636 00000 n
+0000000735 00000 n
+0000000781 00000 n
+0000000926 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+ /ID [<369e89600ee1a6c4c7e73533610180c2><31415926535897932384626433832795>]
+>>
+startxref
+961
+%%EOF
diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.3.out b/qpdf/qtest/qpdf/extensions-other-min-1.3.out
new file mode 100644
index 00000000..0f576775
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.3.out
@@ -0,0 +1,4 @@
+version: 1.5
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.6.out b/qpdf/qtest/qpdf/extensions-other-min-1.6.out
new file mode 100644
index 00000000..0cc726a6
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.6.out
@@ -0,0 +1,4 @@
+version: 1.6
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out
new file mode 100644
index 00000000..a714b145
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 1
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out
new file mode 100644
index 00000000..ed0bf57c
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 2
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out
new file mode 100644
index 00000000..f13ea01b
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out
@@ -0,0 +1,4 @@
+version: 1.7
+extension level: 3
+<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out
new file mode 100644
index 00000000..00858aaa
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 2
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out
new file mode 100644
index 00000000..dcf87e86
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 5
+<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.out
new file mode 100644
index 00000000..d121666e
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.out
@@ -0,0 +1,4 @@
+version: 1.8
+extension level: 0
+<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >>
+test 34 done
diff --git a/qpdf/qtest/qpdf/extensions-other.pdf b/qpdf/qtest/qpdf/extensions-other.pdf
new file mode 100644
index 00000000..e8639813
--- /dev/null
+++ b/qpdf/qtest/qpdf/extensions-other.pdf
@@ -0,0 +1,98 @@
+%PDF-1.5
+%¿÷¢þ
+%QDF-1.0
+
+1 0 obj
+<<
+ /Pages 2 0 R
+ /Type /Catalog
+ /Extensions <<
+ /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >>
+ >>
+>>
+endobj
+
+2 0 obj
+<<
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+ /Type /Pages
+>>
+endobj
+
+%% Page 1
+3 0 obj
+<<
+ /Contents 4 0 R
+ /MediaBox [
+ 0
+ 0
+ 612
+ 792
+ ]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ /ProcSet 7 0 R
+ >>
+ /Type /Page
+>>
+endobj
+
+%% Contents for page 1
+4 0 obj
+<<
+ /Length 5 0 R
+>>
+stream
+BT
+ /F1 24 Tf
+ 72 720 Td
+ (Potato) Tj
+ET
+endstream
+endobj
+
+5 0 obj
+44
+endobj
+
+6 0 obj
+<<
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+ /Name /F1
+ /Subtype /Type1
+ /Type /Font
+>>
+endobj
+
+7 0 obj
+[
+ /PDF
+ /Text
+]
+endobj
+
+xref
+0 8
+0000000000 65535 f
+0000000025 00000 n
+0000000163 00000 n
+0000000245 00000 n
+0000000460 00000 n
+0000000559 00000 n
+0000000578 00000 n
+0000000696 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+ /ID [<369e89600ee1a6c4c7e73533610180c2><369e89600ee1a6c4c7e73533610180c2>]
+>>
+startxref
+731
+%%EOF
diff --git a/qpdf/qtest/qpdf/obj0-check.out b/qpdf/qtest/qpdf/obj0-check.out
index f0a71b66..1b4bcf46 100644
--- a/qpdf/qtest/qpdf/obj0-check.out
+++ b/qpdf/qtest/qpdf/obj0-check.out
@@ -1,7 +1,7 @@
checking obj0.pdf
-PDF Version: 1.3
-File is not encrypted
-File is not linearized
WARNING: obj0.pdf: file is damaged
WARNING: obj0.pdf (object 1 0, file position 77): expected n n obj
WARNING: obj0.pdf: Attempting to reconstruct cross-reference table
+PDF Version: 1.3
+File is not encrypted
+File is not linearized
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 2c729987..f8294889 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -1126,6 +1126,13 @@ void runtest(int n, char const* filename1, char const* filename2)
fwrite(b->getBuffer(), b->getSize(), 1, f);
fclose(f);
}
+ else if (n == 34)
+ {
+ // Look at Extensions dictionary
+ std::cout << "version: " << pdf.getPDFVersion() << std::endl
+ << "extension level: " << pdf.getExtensionLevel() << std::endl
+ << pdf.getRoot().getKey("/Extensions").unparse() << std::endl;
+ }
else
{
throw std::runtime_error(std::string("invalid test ") +