summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2009-10-05 02:42:48 +0200
committerJay Berkenbilt <ejb@ql.org>2009-10-05 02:42:48 +0200
commitc2023db265ea35ad7d0ab0cd989f16479bcb798d (patch)
treea4cbc126f6152a185ec10b58f5330c7bfbe5f90a
parentc1e53f148096c329650068694fbde82f018560bf (diff)
downloadqpdf-c2023db265ea35ad7d0ab0cd989f16479bcb798d.tar.zst
Implement changes suggested by Zarko and our subsequent conversations:
- Add a way to set the minimum PDF version - Add a way to force the PDF version - Have isEncrypted return true if an /Encrypt dictionary exists even when we can't read the file - Allow qpdf_init_write to be called multiple times - Update some comments in headers git-svn-id: svn+q:///qpdf/trunk@748 71b93d88-0707-0410-a8cf-f5a4172ac649
-rw-r--r--ChangeLog4
-rw-r--r--TODO12
-rw-r--r--include/qpdf/QPDFWriter.hh33
-rw-r--r--include/qpdf/qpdf-c.h13
-rw-r--r--libqpdf/QPDFWriter.cc50
-rw-r--r--libqpdf/QPDF_encryption.cc6
-rw-r--r--libqpdf/qpdf-c.cc20
-rw-r--r--qpdf/qpdf-ctest.c71
-rw-r--r--qpdf/qpdf.cc36
-rw-r--r--qpdf/qpdf.testcov5
-rw-r--r--qpdf/qtest/qpdf.test42
-rw-r--r--qpdf/qtest/qpdf/forced-version.out5
-rw-r--r--qpdf/qtest/qpdf/min-version.out5
13 files changed, 255 insertions, 47 deletions
diff --git a/ChangeLog b/ChangeLog
index e21ad6b8..0f18cfc6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2009-10-04 Jay Berkenbilt <ejb@ql.org>
+ * Add methods to QPDFWriter and corresponding command line
+ arguments to qpdf to set the minimum output PDF version and also
+ to force the version to a particular value.
+
* libqpdf/QPDF.cc (processXRefStream): warn and ignore extra xref
stream entries when stream is larger than reported size. This
used to be a fatal error. (Fixes qpdf-Bugs-2872265.)
diff --git a/TODO b/TODO
index 796fbb11..19ad50e3 100644
--- a/TODO
+++ b/TODO
@@ -1,15 +1,3 @@
-Now
-===
-
- * Add functions to set minimum version and to force pdf version
-
- * Make multiple calls to init_write safe; document that write
- parameter settings must be repeated
-
- * qpdf_is_encrypted returns false for encrypted file when incorrect
- password is given
-
-
2.1
===
diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh
index 7d62e476..b5f23457 100644
--- a/include/qpdf/QPDFWriter.hh
+++ b/include/qpdf/QPDFWriter.hh
@@ -34,7 +34,13 @@ class Pl_Count;
class QPDFWriter
{
public:
- // Passing null as filename means write to stdout
+ // Passing null as filename means write to stdout. QPDFWriter
+ // will create a zero-length output file upon construction. If
+ // write fails, the empty or partially written file will not be
+ // deleted. This is by design: sometimes the partial file may be
+ // useful for tracking down problems. If your application doesn't
+ // want the partially written file to be left behind, you should
+ // delete it the eventual call to write fails.
DLL_EXPORT
QPDFWriter(QPDF& pdf, char const* filename);
DLL_EXPORT
@@ -78,6 +84,30 @@ class QPDFWriter
DLL_EXPORT
void setQDFMode(bool);
+ // Set the minimum PDF version. If the PDF version of the input
+ // file (or previously set minimum version) is less than the
+ // version passed to this method, the PDF version of the output
+ // file will be set to this value. If the original PDF file's
+ // version or previously set minimum version is already this
+ // version or later, the original file's version will be used.
+ // QPDFWriter automatically sets the minimum version to 1.4 when
+ // R3 encryption parameters are used, and to 1.5 when object
+ // streams are used.
+ DLL_EXPORT
+ void setMinimumPDFVersion(std::string const&);
+
+ // 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
+ // properly with older PDF viewers. When a PDF version is set
+ // using this function, qpdf will use this version even if the
+ // file contains features that are not supported in that version
+ // of PDF. In other words, you should only use this function if
+ // you are sure the PDF file in question has no features of newer
+ // versions of PDF or if you are willing to create files that old
+ // viewers may try to open but not be able to properly interpret.
+ DLL_EXPORT
+ void forcePDFVersion(std::string const&);
+
// Cause a static /ID value to be generated. Use only in test
// suites.
DLL_EXPORT
@@ -241,6 +271,7 @@ class QPDFWriter
std::string id1; // for /ID key of
std::string id2; // trailer dictionary
std::string min_pdf_version;
+ std::string forced_pdf_version;
int encryption_dict_objid;
std::string cur_data_key;
std::list<PointerHolder<Pipeline> > to_delete;
diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h
index 11dae4f1..fab8112b 100644
--- a/include/qpdf/qpdf-c.h
+++ b/include/qpdf/qpdf-c.h
@@ -189,7 +189,12 @@ extern "C" {
/* Supply the name of the file to be written and initialize the
* qpdf_data object to handle writing operations. This function
* also attempts to create the file. The PDF data is not written
- * until the call to qpdf_write.
+ * until the call to qpdf_write. qpdf_init_write may be called
+ * multiple times for the same qpdf_data object. When
+ * qpdf_init_write is called, all information from previous calls
+ * to functions that set write parameters (qpdf_set_linearization,
+ * etc.) is lost, so any write parameter functions must be called
+ * again.
*/
DLL_EXPORT
QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename);
@@ -256,6 +261,12 @@ extern "C" {
DLL_EXPORT
void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value);
+ DLL_EXPORT
+ void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version);
+
+ DLL_EXPORT
+ void qpdf_force_pdf_version(qpdf_data qpdf, char const* version);
+
/* Do actual write operation. */
DLL_EXPORT
QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf);
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index 4191e906..0ede7889 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -100,6 +100,37 @@ QPDFWriter::setQDFMode(bool val)
}
void
+QPDFWriter::setMinimumPDFVersion(std::string const& version)
+{
+ bool set_version = false;
+ if (this->min_pdf_version.empty())
+ {
+ set_version = true;
+ }
+ else
+ {
+ float v = atof(version.c_str());
+ float mv = atof(this->min_pdf_version.c_str());
+ if (v > mv)
+ {
+ QTC::TC("qpdf", "QPDFWriter increasing minimum version");
+ set_version = true;
+ }
+ }
+
+ if (set_version)
+ {
+ this->min_pdf_version = version;
+ }
+}
+
+void
+QPDFWriter::forcePDFVersion(std::string const& version)
+{
+ this->forced_pdf_version = version;
+}
+
+void
QPDFWriter::setStaticID(bool val)
{
this->static_id = val;
@@ -147,7 +178,7 @@ QPDFWriter::setR2EncryptionParameters(
clear.insert(6);
}
- this->min_pdf_version = "1.3";
+ setMinimumPDFVersion("1.3");
setEncryptionParameters(user_password, owner_password, 1, 2, 5, clear);
}
@@ -221,7 +252,7 @@ QPDFWriter::setR3EncryptionParameters(
// no default so gcc warns for missing cases
}
- this->min_pdf_version = "1.4";
+ setMinimumPDFVersion("1.4");
setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear);
}
@@ -1361,7 +1392,7 @@ QPDFWriter::write()
if (! this->object_stream_to_objects.empty())
{
- this->min_pdf_version = "1.5";
+ setMinimumPDFVersion("1.5");
}
generateID();
@@ -1417,15 +1448,12 @@ QPDFWriter::writeEncryptionDictionary()
void
QPDFWriter::writeHeader()
{
- std::string version = pdf.getPDFVersion();
- if (! this->min_pdf_version.empty())
+ setMinimumPDFVersion(pdf.getPDFVersion());
+ std::string version = this->min_pdf_version;
+ if (! this->forced_pdf_version.empty())
{
- float ov = atof(version.c_str());
- float mv = atof(this->min_pdf_version.c_str());
- if (mv > ov)
- {
- version = this->min_pdf_version;
- }
+ QTC::TC("qpdf", "QPDFWriter using forced PDF version");
+ version = this->forced_pdf_version;
}
writeString("%PDF-");
diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc
index 5d061dfd..075ab22a 100644
--- a/libqpdf/QPDF_encryption.cc
+++ b/libqpdf/QPDF_encryption.cc
@@ -289,6 +289,11 @@ QPDF::initializeEncryption()
return;
}
+ // Go ahead and set this->encryption here. That way, isEncrypted
+ // will return true even if there were errors reading the
+ // encryption dictionary.
+ this->encrypted = true;
+
QPDFObjectHandle id_obj = this->trailer.getKey("/ID");
if (! (id_obj.isArray() &&
(id_obj.getArrayNItems() == 2) &&
@@ -377,7 +382,6 @@ QPDF::initializeEncryption()
throw QPDFExc(this->file.getName() + ": invalid password");
}
- this->encrypted = true;
this->encryption_key = compute_encryption_key(this->user_password, data);
}
diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc
index 9e2d0cbf..966d3dbb 100644
--- a/libqpdf/qpdf-c.cc
+++ b/libqpdf/qpdf-c.cc
@@ -252,6 +252,12 @@ DLL_EXPORT
QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename)
{
QPDF_ERROR_CODE status = QPDF_SUCCESS;
+ if (qpdf->qpdf_writer)
+ {
+ QTC::TC("qpdf", "qpdf-c called qpdf_init_write multiple times");
+ delete qpdf->qpdf_writer;
+ qpdf->qpdf_writer = 0;
+ }
try
{
qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), filename);
@@ -391,6 +397,20 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value)
}
DLL_EXPORT
+void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_set_minimum_pdf_version");
+ qpdf->qpdf_writer->setMinimumPDFVersion(version);
+}
+
+DLL_EXPORT
+void qpdf_force_pdf_version(qpdf_data qpdf, char const* version)
+{
+ QTC::TC("qpdf", "qpdf-c called qpdf_force_pdf_version");
+ qpdf->qpdf_writer->forcePDFVersion(version);
+}
+
+DLL_EXPORT
QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf)
{
QPDF_ERROR_CODE status = QPDF_SUCCESS;
diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c
index 42d8f9f4..c3e1040b 100644
--- a/qpdf/qpdf-ctest.c
+++ b/qpdf/qpdf-ctest.c
@@ -20,7 +20,8 @@ static void report_errors()
static void test01(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
printf("version: %s\n", qpdf_get_pdf_version(qpdf));
@@ -53,7 +54,8 @@ static void test01(char const* infile,
static void test02(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_set_suppress_warnings(qpdf, QPDF_TRUE);
if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) &&
@@ -67,7 +69,8 @@ static void test02(char const* infile,
static void test03(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -79,7 +82,8 @@ static void test03(char const* infile,
static void test04(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_set_ignore_xref_streams(qpdf, QPDF_TRUE);
qpdf_read(qpdf, infile, password);
@@ -91,7 +95,8 @@ static void test04(char const* infile,
static void test05(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -103,7 +108,8 @@ static void test05(char const* infile,
static void test06(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -115,7 +121,8 @@ static void test06(char const* infile,
static void test07(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -127,7 +134,8 @@ static void test07(char const* infile,
static void test08(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -140,7 +148,8 @@ static void test08(char const* infile,
static void test09(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -152,7 +161,8 @@ static void test09(char const* infile,
static void test10(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_set_attempt_recovery(qpdf, QPDF_FALSE);
qpdf_read(qpdf, infile, password);
@@ -161,7 +171,8 @@ static void test10(char const* infile,
static void test11(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -174,7 +185,8 @@ static void test11(char const* infile,
static void test12(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
qpdf_init_write(qpdf, outfile);
@@ -188,7 +200,8 @@ static void test12(char const* infile,
static void test13(char const* infile,
char const* password,
- char const* outfile)
+ char const* outfile,
+ char const* outfile2)
{
qpdf_read(qpdf, infile, password);
printf("user password: %s\n", qpdf_get_user_password(qpdf));
@@ -199,15 +212,33 @@ static void test13(char const* infile,
report_errors();
}
+static void test14(char const* infile,
+ char const* password,
+ char const* outfile,
+ char const* outfile2)
+{
+ qpdf_read(qpdf, infile, password);
+ qpdf_init_write(qpdf, outfile);
+ qpdf_set_static_ID(qpdf, QPDF_TRUE);
+ qpdf_set_minimum_pdf_version(qpdf, "1.6");
+ qpdf_write(qpdf);
+ qpdf_init_write(qpdf, outfile2);
+ qpdf_set_static_ID(qpdf, QPDF_TRUE);
+ qpdf_force_pdf_version(qpdf, "1.4");
+ qpdf_write(qpdf);
+ report_errors();
+}
+
int main(int argc, char* argv[])
{
char* whoami = 0;
char* p = 0;
int n = 0;
- char const* infile;
- char const* password;
- char const* outfile;
- void (*fn)(char const*, char const*, char const*) = 0;
+ char const* infile = 0;
+ char const* password = 0;
+ char const* outfile = 0;
+ char const* outfile2 = 0;
+ void (*fn)(char const*, char const*, char const*, char const*) = 0;
if ((p = strrchr(argv[0], '/')) != NULL)
{
@@ -221,7 +252,7 @@ int main(int argc, char* argv[])
{
whoami = argv[0];
}
- if (argc != 5)
+ if (argc < 5)
{
fprintf(stderr, "usage: %s n infile password outfile\n", whoami);
exit(2);
@@ -231,6 +262,7 @@ int main(int argc, char* argv[])
infile = argv[2];
password = argv[3];
outfile = argv[4];
+ outfile2 = (argc > 5 ? argv[5] : 0);
fn = ((n == 1) ? test01 :
(n == 2) ? test02 :
@@ -245,6 +277,7 @@ int main(int argc, char* argv[])
(n == 11) ? test11 :
(n == 12) ? test12 :
(n == 13) ? test13 :
+ (n == 14) ? test14 :
0);
if (fn == 0)
@@ -254,7 +287,7 @@ int main(int argc, char* argv[])
}
qpdf = qpdf_init();
- fn(infile, password, outfile);
+ fn(infile, password, outfile, outfile2);
qpdf_cleanup(&qpdf);
assert(qpdf == 0);
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index cc65328f..85bbea1c 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -103,6 +103,8 @@ familiar with the PDF file format or who are PDF developers.\n\
--object-streams=mode controls handing of object streams\n\
--ignore-xref-streams tells qpdf to ignore any cross-reference streams\n\
--qdf turns on \"QDF mode\" (below)\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\
Values for stream data options:\n\
\n\
@@ -119,6 +121,12 @@ Values for object stream mode:\n\
In qdf mode, by default, content normalization is turned on, and the\n\
stream data mode is set to uncompress.\n\
\n\
+Setting the minimum PDF version of the output file may raise the version\n\
+but will never lower it. Forcing the PDF version of the output file may\n\
+set the PDF version to a lower value than actually allowed by the file's\n\
+contents. You should only do this if you have no other possible way to\n\
+open the file or if you know that the file definitely doesn't include\n\
+features not supported later versions.\n\
\n\
Testing, Inspection, and Debugging Options\n\
------------------------------------------\n\
@@ -518,6 +526,8 @@ int main(int argc, char* argv[])
QPDFWriter::object_stream_e object_stream_mode = QPDFWriter::o_preserve;
bool ignore_xref_streams = false;
bool qdf_mode = false;
+ std::string min_version;
+ std::string force_version;
bool static_id = false;
bool suppress_original_object_id = false;
@@ -651,6 +661,24 @@ int main(int argc, char* argv[])
{
qdf_mode = true;
}
+ else if (strcmp(arg, "min-version") == 0)
+ {
+ if (parameter == 0)
+ {
+ usage("--min-version be given as"
+ "--min-version=version");
+ }
+ min_version = parameter;
+ }
+ else if (strcmp(arg, "force-version") == 0)
+ {
+ if (parameter == 0)
+ {
+ usage("--force-version be given as"
+ "--force-version=version");
+ }
+ force_version = parameter;
+ }
else if (strcmp(arg, "static-id") == 0)
{
static_id = true;
@@ -977,6 +1005,14 @@ int main(int argc, char* argv[])
{
w.setObjectStreamMode(object_stream_mode);
}
+ if (! min_version.empty())
+ {
+ w.setMinimumPDFVersion(min_version);
+ }
+ if (! force_version.empty())
+ {
+ w.forcePDFVersion(force_version);
+ }
w.write();
}
if (! pdf.getWarnings().empty())
diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov
index cf4dc333..a2905e2a 100644
--- a/qpdf/qpdf.testcov
+++ b/qpdf/qpdf.testcov
@@ -153,3 +153,8 @@ 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 using forced PDF version 0
+qpdf-c called qpdf_set_minimum_pdf_version 0
+qpdf-c called qpdf_force_pdf_version 0
+qpdf-c called qpdf_init_write multiple times 0
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 1c724ec3..1aba8e15 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -81,7 +81,7 @@ flush_tiff_cache();
show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
-$n_tests += 7;
+$n_tests += 14;
foreach (my $i = 1; $i <= 3; ++$i)
{
@@ -115,6 +115,44 @@ $td->runtest("show new xref stream",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
+# Min/Force version
+$td->runtest("set min version",
+ {$td->COMMAND => "qpdf --min-version=1.6 good1.pdf a.pdf"},
+ {$td->STRING => "",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check version",
+ {$td->COMMAND => "qpdf --check a.pdf"},
+ {$td->FILE => "min-version.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("force version",
+ {$td->COMMAND => "qpdf --force-version=1.4 a.pdf b.pdf"},
+ {$td->STRING => "",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("check version",
+ {$td->COMMAND => "qpdf --check b.pdf"},
+ {$td->FILE => "forced-version.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+unlink "a.pdf", "b.pdf" or die;
+$td->runtest("C API: min/force versions",
+ {$td->COMMAND => "qpdf-ctest 14 object-stream.pdf '' a.pdf b.pdf"},
+ {$td->STRING => "",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("C check version 1",
+ {$td->COMMAND => "qpdf --check a.pdf"},
+ {$td->FILE => "min-version.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+$td->runtest("C check version 2",
+ {$td->COMMAND => "qpdf --check b.pdf"},
+ {$td->FILE => "forced-version.out",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
show_ntests();
# ----------
$td->notify("--- Error Condition Tests ---");
@@ -883,7 +921,7 @@ foreach my $d (@cenc)
my ($n, $infile, $pass, $description, $output) = @$d;
my $outfile = $description;
$outfile =~ s/ /-/g;
- my $outfile = "c-$outfile.pdf";
+ $outfile = "c-$outfile.pdf";
$td->runtest("C API encryption: $description",
{$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"},
{$td->STRING => $output, $td->EXIT_STATUS => 0},
diff --git a/qpdf/qtest/qpdf/forced-version.out b/qpdf/qtest/qpdf/forced-version.out
new file mode 100644
index 00000000..9a71439d
--- /dev/null
+++ b/qpdf/qtest/qpdf/forced-version.out
@@ -0,0 +1,5 @@
+checking b.pdf
+PDF Version: 1.4
+File is not encrypted
+File is not linearized
+No errors found
diff --git a/qpdf/qtest/qpdf/min-version.out b/qpdf/qtest/qpdf/min-version.out
new file mode 100644
index 00000000..3e34d6a0
--- /dev/null
+++ b/qpdf/qtest/qpdf/min-version.out
@@ -0,0 +1,5 @@
+checking a.pdf
+PDF Version: 1.6
+File is not encrypted
+File is not linearized
+No errors found