Commit c2023db265ea35ad7d0ab0cd989f16479bcb798d
1 parent
c1e53f14
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
Showing
13 changed files
with
255 additions
and
47 deletions
ChangeLog
| 1 | 2009-10-04 Jay Berkenbilt <ejb@ql.org> | 1 | 2009-10-04 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Add methods to QPDFWriter and corresponding command line | ||
| 4 | + arguments to qpdf to set the minimum output PDF version and also | ||
| 5 | + to force the version to a particular value. | ||
| 6 | + | ||
| 3 | * libqpdf/QPDF.cc (processXRefStream): warn and ignore extra xref | 7 | * libqpdf/QPDF.cc (processXRefStream): warn and ignore extra xref |
| 4 | stream entries when stream is larger than reported size. This | 8 | stream entries when stream is larger than reported size. This |
| 5 | used to be a fatal error. (Fixes qpdf-Bugs-2872265.) | 9 | used to be a fatal error. (Fixes qpdf-Bugs-2872265.) |
TODO
| 1 | -Now | ||
| 2 | -=== | ||
| 3 | - | ||
| 4 | - * Add functions to set minimum version and to force pdf version | ||
| 5 | - | ||
| 6 | - * Make multiple calls to init_write safe; document that write | ||
| 7 | - parameter settings must be repeated | ||
| 8 | - | ||
| 9 | - * qpdf_is_encrypted returns false for encrypted file when incorrect | ||
| 10 | - password is given | ||
| 11 | - | ||
| 12 | - | ||
| 13 | 2.1 | 1 | 2.1 |
| 14 | === | 2 | === |
| 15 | 3 |
include/qpdf/QPDFWriter.hh
| @@ -34,7 +34,13 @@ class Pl_Count; | @@ -34,7 +34,13 @@ class Pl_Count; | ||
| 34 | class QPDFWriter | 34 | class QPDFWriter |
| 35 | { | 35 | { |
| 36 | public: | 36 | public: |
| 37 | - // Passing null as filename means write to stdout | 37 | + // Passing null as filename means write to stdout. QPDFWriter |
| 38 | + // will create a zero-length output file upon construction. If | ||
| 39 | + // write fails, the empty or partially written file will not be | ||
| 40 | + // deleted. This is by design: sometimes the partial file may be | ||
| 41 | + // useful for tracking down problems. If your application doesn't | ||
| 42 | + // want the partially written file to be left behind, you should | ||
| 43 | + // delete it the eventual call to write fails. | ||
| 38 | DLL_EXPORT | 44 | DLL_EXPORT |
| 39 | QPDFWriter(QPDF& pdf, char const* filename); | 45 | QPDFWriter(QPDF& pdf, char const* filename); |
| 40 | DLL_EXPORT | 46 | DLL_EXPORT |
| @@ -78,6 +84,30 @@ class QPDFWriter | @@ -78,6 +84,30 @@ class QPDFWriter | ||
| 78 | DLL_EXPORT | 84 | DLL_EXPORT |
| 79 | void setQDFMode(bool); | 85 | void setQDFMode(bool); |
| 80 | 86 | ||
| 87 | + // Set the minimum PDF version. If the PDF version of the input | ||
| 88 | + // file (or previously set minimum version) is less than the | ||
| 89 | + // version passed to this method, the PDF version of the output | ||
| 90 | + // file will be set to this value. If the original PDF file's | ||
| 91 | + // version or previously set minimum version is already this | ||
| 92 | + // version or later, the original file's version will be used. | ||
| 93 | + // QPDFWriter automatically sets the minimum version to 1.4 when | ||
| 94 | + // R3 encryption parameters are used, and to 1.5 when object | ||
| 95 | + // streams are used. | ||
| 96 | + DLL_EXPORT | ||
| 97 | + void setMinimumPDFVersion(std::string const&); | ||
| 98 | + | ||
| 99 | + // Force the PDF version of the output file to be a given version. | ||
| 100 | + // Use of this function may create PDF files that will not work | ||
| 101 | + // properly with older PDF viewers. When a PDF version is set | ||
| 102 | + // using this function, qpdf will use this version even if the | ||
| 103 | + // file contains features that are not supported in that version | ||
| 104 | + // of PDF. In other words, you should only use this function if | ||
| 105 | + // you are sure the PDF file in question has no features of newer | ||
| 106 | + // versions of PDF or if you are willing to create files that old | ||
| 107 | + // viewers may try to open but not be able to properly interpret. | ||
| 108 | + DLL_EXPORT | ||
| 109 | + void forcePDFVersion(std::string const&); | ||
| 110 | + | ||
| 81 | // Cause a static /ID value to be generated. Use only in test | 111 | // Cause a static /ID value to be generated. Use only in test |
| 82 | // suites. | 112 | // suites. |
| 83 | DLL_EXPORT | 113 | DLL_EXPORT |
| @@ -241,6 +271,7 @@ class QPDFWriter | @@ -241,6 +271,7 @@ class QPDFWriter | ||
| 241 | std::string id1; // for /ID key of | 271 | std::string id1; // for /ID key of |
| 242 | std::string id2; // trailer dictionary | 272 | std::string id2; // trailer dictionary |
| 243 | std::string min_pdf_version; | 273 | std::string min_pdf_version; |
| 274 | + std::string forced_pdf_version; | ||
| 244 | int encryption_dict_objid; | 275 | int encryption_dict_objid; |
| 245 | std::string cur_data_key; | 276 | std::string cur_data_key; |
| 246 | std::list<PointerHolder<Pipeline> > to_delete; | 277 | std::list<PointerHolder<Pipeline> > to_delete; |
include/qpdf/qpdf-c.h
| @@ -189,7 +189,12 @@ extern "C" { | @@ -189,7 +189,12 @@ extern "C" { | ||
| 189 | /* Supply the name of the file to be written and initialize the | 189 | /* Supply the name of the file to be written and initialize the |
| 190 | * qpdf_data object to handle writing operations. This function | 190 | * qpdf_data object to handle writing operations. This function |
| 191 | * also attempts to create the file. The PDF data is not written | 191 | * also attempts to create the file. The PDF data is not written |
| 192 | - * until the call to qpdf_write. | 192 | + * until the call to qpdf_write. qpdf_init_write may be called |
| 193 | + * multiple times for the same qpdf_data object. When | ||
| 194 | + * qpdf_init_write is called, all information from previous calls | ||
| 195 | + * to functions that set write parameters (qpdf_set_linearization, | ||
| 196 | + * etc.) is lost, so any write parameter functions must be called | ||
| 197 | + * again. | ||
| 193 | */ | 198 | */ |
| 194 | DLL_EXPORT | 199 | DLL_EXPORT |
| 195 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename); | 200 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename); |
| @@ -256,6 +261,12 @@ extern "C" { | @@ -256,6 +261,12 @@ extern "C" { | ||
| 256 | DLL_EXPORT | 261 | DLL_EXPORT |
| 257 | void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value); | 262 | void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value); |
| 258 | 263 | ||
| 264 | + DLL_EXPORT | ||
| 265 | + void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version); | ||
| 266 | + | ||
| 267 | + DLL_EXPORT | ||
| 268 | + void qpdf_force_pdf_version(qpdf_data qpdf, char const* version); | ||
| 269 | + | ||
| 259 | /* Do actual write operation. */ | 270 | /* Do actual write operation. */ |
| 260 | DLL_EXPORT | 271 | DLL_EXPORT |
| 261 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf); | 272 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf); |
libqpdf/QPDFWriter.cc
| @@ -100,6 +100,37 @@ QPDFWriter::setQDFMode(bool val) | @@ -100,6 +100,37 @@ QPDFWriter::setQDFMode(bool val) | ||
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | void | 102 | void |
| 103 | +QPDFWriter::setMinimumPDFVersion(std::string const& version) | ||
| 104 | +{ | ||
| 105 | + bool set_version = false; | ||
| 106 | + if (this->min_pdf_version.empty()) | ||
| 107 | + { | ||
| 108 | + set_version = true; | ||
| 109 | + } | ||
| 110 | + else | ||
| 111 | + { | ||
| 112 | + float v = atof(version.c_str()); | ||
| 113 | + float mv = atof(this->min_pdf_version.c_str()); | ||
| 114 | + if (v > mv) | ||
| 115 | + { | ||
| 116 | + QTC::TC("qpdf", "QPDFWriter increasing minimum version"); | ||
| 117 | + set_version = true; | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + if (set_version) | ||
| 122 | + { | ||
| 123 | + this->min_pdf_version = version; | ||
| 124 | + } | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +void | ||
| 128 | +QPDFWriter::forcePDFVersion(std::string const& version) | ||
| 129 | +{ | ||
| 130 | + this->forced_pdf_version = version; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +void | ||
| 103 | QPDFWriter::setStaticID(bool val) | 134 | QPDFWriter::setStaticID(bool val) |
| 104 | { | 135 | { |
| 105 | this->static_id = val; | 136 | this->static_id = val; |
| @@ -147,7 +178,7 @@ QPDFWriter::setR2EncryptionParameters( | @@ -147,7 +178,7 @@ QPDFWriter::setR2EncryptionParameters( | ||
| 147 | clear.insert(6); | 178 | clear.insert(6); |
| 148 | } | 179 | } |
| 149 | 180 | ||
| 150 | - this->min_pdf_version = "1.3"; | 181 | + setMinimumPDFVersion("1.3"); |
| 151 | setEncryptionParameters(user_password, owner_password, 1, 2, 5, clear); | 182 | setEncryptionParameters(user_password, owner_password, 1, 2, 5, clear); |
| 152 | } | 183 | } |
| 153 | 184 | ||
| @@ -221,7 +252,7 @@ QPDFWriter::setR3EncryptionParameters( | @@ -221,7 +252,7 @@ QPDFWriter::setR3EncryptionParameters( | ||
| 221 | // no default so gcc warns for missing cases | 252 | // no default so gcc warns for missing cases |
| 222 | } | 253 | } |
| 223 | 254 | ||
| 224 | - this->min_pdf_version = "1.4"; | 255 | + setMinimumPDFVersion("1.4"); |
| 225 | setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear); | 256 | setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear); |
| 226 | } | 257 | } |
| 227 | 258 | ||
| @@ -1361,7 +1392,7 @@ QPDFWriter::write() | @@ -1361,7 +1392,7 @@ QPDFWriter::write() | ||
| 1361 | 1392 | ||
| 1362 | if (! this->object_stream_to_objects.empty()) | 1393 | if (! this->object_stream_to_objects.empty()) |
| 1363 | { | 1394 | { |
| 1364 | - this->min_pdf_version = "1.5"; | 1395 | + setMinimumPDFVersion("1.5"); |
| 1365 | } | 1396 | } |
| 1366 | 1397 | ||
| 1367 | generateID(); | 1398 | generateID(); |
| @@ -1417,15 +1448,12 @@ QPDFWriter::writeEncryptionDictionary() | @@ -1417,15 +1448,12 @@ QPDFWriter::writeEncryptionDictionary() | ||
| 1417 | void | 1448 | void |
| 1418 | QPDFWriter::writeHeader() | 1449 | QPDFWriter::writeHeader() |
| 1419 | { | 1450 | { |
| 1420 | - std::string version = pdf.getPDFVersion(); | ||
| 1421 | - if (! this->min_pdf_version.empty()) | 1451 | + setMinimumPDFVersion(pdf.getPDFVersion()); |
| 1452 | + std::string version = this->min_pdf_version; | ||
| 1453 | + if (! this->forced_pdf_version.empty()) | ||
| 1422 | { | 1454 | { |
| 1423 | - float ov = atof(version.c_str()); | ||
| 1424 | - float mv = atof(this->min_pdf_version.c_str()); | ||
| 1425 | - if (mv > ov) | ||
| 1426 | - { | ||
| 1427 | - version = this->min_pdf_version; | ||
| 1428 | - } | 1455 | + QTC::TC("qpdf", "QPDFWriter using forced PDF version"); |
| 1456 | + version = this->forced_pdf_version; | ||
| 1429 | } | 1457 | } |
| 1430 | 1458 | ||
| 1431 | writeString("%PDF-"); | 1459 | writeString("%PDF-"); |
libqpdf/QPDF_encryption.cc
| @@ -289,6 +289,11 @@ QPDF::initializeEncryption() | @@ -289,6 +289,11 @@ QPDF::initializeEncryption() | ||
| 289 | return; | 289 | return; |
| 290 | } | 290 | } |
| 291 | 291 | ||
| 292 | + // Go ahead and set this->encryption here. That way, isEncrypted | ||
| 293 | + // will return true even if there were errors reading the | ||
| 294 | + // encryption dictionary. | ||
| 295 | + this->encrypted = true; | ||
| 296 | + | ||
| 292 | QPDFObjectHandle id_obj = this->trailer.getKey("/ID"); | 297 | QPDFObjectHandle id_obj = this->trailer.getKey("/ID"); |
| 293 | if (! (id_obj.isArray() && | 298 | if (! (id_obj.isArray() && |
| 294 | (id_obj.getArrayNItems() == 2) && | 299 | (id_obj.getArrayNItems() == 2) && |
| @@ -377,7 +382,6 @@ QPDF::initializeEncryption() | @@ -377,7 +382,6 @@ QPDF::initializeEncryption() | ||
| 377 | throw QPDFExc(this->file.getName() + ": invalid password"); | 382 | throw QPDFExc(this->file.getName() + ": invalid password"); |
| 378 | } | 383 | } |
| 379 | 384 | ||
| 380 | - this->encrypted = true; | ||
| 381 | this->encryption_key = compute_encryption_key(this->user_password, data); | 385 | this->encryption_key = compute_encryption_key(this->user_password, data); |
| 382 | } | 386 | } |
| 383 | 387 |
libqpdf/qpdf-c.cc
| @@ -252,6 +252,12 @@ DLL_EXPORT | @@ -252,6 +252,12 @@ DLL_EXPORT | ||
| 252 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename) | 252 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename) |
| 253 | { | 253 | { |
| 254 | QPDF_ERROR_CODE status = QPDF_SUCCESS; | 254 | QPDF_ERROR_CODE status = QPDF_SUCCESS; |
| 255 | + if (qpdf->qpdf_writer) | ||
| 256 | + { | ||
| 257 | + QTC::TC("qpdf", "qpdf-c called qpdf_init_write multiple times"); | ||
| 258 | + delete qpdf->qpdf_writer; | ||
| 259 | + qpdf->qpdf_writer = 0; | ||
| 260 | + } | ||
| 255 | try | 261 | try |
| 256 | { | 262 | { |
| 257 | qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), filename); | 263 | qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), filename); |
| @@ -391,6 +397,20 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) | @@ -391,6 +397,20 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) | ||
| 391 | } | 397 | } |
| 392 | 398 | ||
| 393 | DLL_EXPORT | 399 | DLL_EXPORT |
| 400 | +void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version) | ||
| 401 | +{ | ||
| 402 | + QTC::TC("qpdf", "qpdf-c called qpdf_set_minimum_pdf_version"); | ||
| 403 | + qpdf->qpdf_writer->setMinimumPDFVersion(version); | ||
| 404 | +} | ||
| 405 | + | ||
| 406 | +DLL_EXPORT | ||
| 407 | +void qpdf_force_pdf_version(qpdf_data qpdf, char const* version) | ||
| 408 | +{ | ||
| 409 | + QTC::TC("qpdf", "qpdf-c called qpdf_force_pdf_version"); | ||
| 410 | + qpdf->qpdf_writer->forcePDFVersion(version); | ||
| 411 | +} | ||
| 412 | + | ||
| 413 | +DLL_EXPORT | ||
| 394 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) | 414 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) |
| 395 | { | 415 | { |
| 396 | QPDF_ERROR_CODE status = QPDF_SUCCESS; | 416 | QPDF_ERROR_CODE status = QPDF_SUCCESS; |
qpdf/qpdf-ctest.c
| @@ -20,7 +20,8 @@ static void report_errors() | @@ -20,7 +20,8 @@ static void report_errors() | ||
| 20 | 20 | ||
| 21 | static void test01(char const* infile, | 21 | static void test01(char const* infile, |
| 22 | char const* password, | 22 | char const* password, |
| 23 | - char const* outfile) | 23 | + char const* outfile, |
| 24 | + char const* outfile2) | ||
| 24 | { | 25 | { |
| 25 | qpdf_read(qpdf, infile, password); | 26 | qpdf_read(qpdf, infile, password); |
| 26 | printf("version: %s\n", qpdf_get_pdf_version(qpdf)); | 27 | printf("version: %s\n", qpdf_get_pdf_version(qpdf)); |
| @@ -53,7 +54,8 @@ static void test01(char const* infile, | @@ -53,7 +54,8 @@ static void test01(char const* infile, | ||
| 53 | 54 | ||
| 54 | static void test02(char const* infile, | 55 | static void test02(char const* infile, |
| 55 | char const* password, | 56 | char const* password, |
| 56 | - char const* outfile) | 57 | + char const* outfile, |
| 58 | + char const* outfile2) | ||
| 57 | { | 59 | { |
| 58 | qpdf_set_suppress_warnings(qpdf, QPDF_TRUE); | 60 | qpdf_set_suppress_warnings(qpdf, QPDF_TRUE); |
| 59 | if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && | 61 | if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && |
| @@ -67,7 +69,8 @@ static void test02(char const* infile, | @@ -67,7 +69,8 @@ static void test02(char const* infile, | ||
| 67 | 69 | ||
| 68 | static void test03(char const* infile, | 70 | static void test03(char const* infile, |
| 69 | char const* password, | 71 | char const* password, |
| 70 | - char const* outfile) | 72 | + char const* outfile, |
| 73 | + char const* outfile2) | ||
| 71 | { | 74 | { |
| 72 | qpdf_read(qpdf, infile, password); | 75 | qpdf_read(qpdf, infile, password); |
| 73 | qpdf_init_write(qpdf, outfile); | 76 | qpdf_init_write(qpdf, outfile); |
| @@ -79,7 +82,8 @@ static void test03(char const* infile, | @@ -79,7 +82,8 @@ static void test03(char const* infile, | ||
| 79 | 82 | ||
| 80 | static void test04(char const* infile, | 83 | static void test04(char const* infile, |
| 81 | char const* password, | 84 | char const* password, |
| 82 | - char const* outfile) | 85 | + char const* outfile, |
| 86 | + char const* outfile2) | ||
| 83 | { | 87 | { |
| 84 | qpdf_set_ignore_xref_streams(qpdf, QPDF_TRUE); | 88 | qpdf_set_ignore_xref_streams(qpdf, QPDF_TRUE); |
| 85 | qpdf_read(qpdf, infile, password); | 89 | qpdf_read(qpdf, infile, password); |
| @@ -91,7 +95,8 @@ static void test04(char const* infile, | @@ -91,7 +95,8 @@ static void test04(char const* infile, | ||
| 91 | 95 | ||
| 92 | static void test05(char const* infile, | 96 | static void test05(char const* infile, |
| 93 | char const* password, | 97 | char const* password, |
| 94 | - char const* outfile) | 98 | + char const* outfile, |
| 99 | + char const* outfile2) | ||
| 95 | { | 100 | { |
| 96 | qpdf_read(qpdf, infile, password); | 101 | qpdf_read(qpdf, infile, password); |
| 97 | qpdf_init_write(qpdf, outfile); | 102 | qpdf_init_write(qpdf, outfile); |
| @@ -103,7 +108,8 @@ static void test05(char const* infile, | @@ -103,7 +108,8 @@ static void test05(char const* infile, | ||
| 103 | 108 | ||
| 104 | static void test06(char const* infile, | 109 | static void test06(char const* infile, |
| 105 | char const* password, | 110 | char const* password, |
| 106 | - char const* outfile) | 111 | + char const* outfile, |
| 112 | + char const* outfile2) | ||
| 107 | { | 113 | { |
| 108 | qpdf_read(qpdf, infile, password); | 114 | qpdf_read(qpdf, infile, password); |
| 109 | qpdf_init_write(qpdf, outfile); | 115 | qpdf_init_write(qpdf, outfile); |
| @@ -115,7 +121,8 @@ static void test06(char const* infile, | @@ -115,7 +121,8 @@ static void test06(char const* infile, | ||
| 115 | 121 | ||
| 116 | static void test07(char const* infile, | 122 | static void test07(char const* infile, |
| 117 | char const* password, | 123 | char const* password, |
| 118 | - char const* outfile) | 124 | + char const* outfile, |
| 125 | + char const* outfile2) | ||
| 119 | { | 126 | { |
| 120 | qpdf_read(qpdf, infile, password); | 127 | qpdf_read(qpdf, infile, password); |
| 121 | qpdf_init_write(qpdf, outfile); | 128 | qpdf_init_write(qpdf, outfile); |
| @@ -127,7 +134,8 @@ static void test07(char const* infile, | @@ -127,7 +134,8 @@ static void test07(char const* infile, | ||
| 127 | 134 | ||
| 128 | static void test08(char const* infile, | 135 | static void test08(char const* infile, |
| 129 | char const* password, | 136 | char const* password, |
| 130 | - char const* outfile) | 137 | + char const* outfile, |
| 138 | + char const* outfile2) | ||
| 131 | { | 139 | { |
| 132 | qpdf_read(qpdf, infile, password); | 140 | qpdf_read(qpdf, infile, password); |
| 133 | qpdf_init_write(qpdf, outfile); | 141 | qpdf_init_write(qpdf, outfile); |
| @@ -140,7 +148,8 @@ static void test08(char const* infile, | @@ -140,7 +148,8 @@ static void test08(char const* infile, | ||
| 140 | 148 | ||
| 141 | static void test09(char const* infile, | 149 | static void test09(char const* infile, |
| 142 | char const* password, | 150 | char const* password, |
| 143 | - char const* outfile) | 151 | + char const* outfile, |
| 152 | + char const* outfile2) | ||
| 144 | { | 153 | { |
| 145 | qpdf_read(qpdf, infile, password); | 154 | qpdf_read(qpdf, infile, password); |
| 146 | qpdf_init_write(qpdf, outfile); | 155 | qpdf_init_write(qpdf, outfile); |
| @@ -152,7 +161,8 @@ static void test09(char const* infile, | @@ -152,7 +161,8 @@ static void test09(char const* infile, | ||
| 152 | 161 | ||
| 153 | static void test10(char const* infile, | 162 | static void test10(char const* infile, |
| 154 | char const* password, | 163 | char const* password, |
| 155 | - char const* outfile) | 164 | + char const* outfile, |
| 165 | + char const* outfile2) | ||
| 156 | { | 166 | { |
| 157 | qpdf_set_attempt_recovery(qpdf, QPDF_FALSE); | 167 | qpdf_set_attempt_recovery(qpdf, QPDF_FALSE); |
| 158 | qpdf_read(qpdf, infile, password); | 168 | qpdf_read(qpdf, infile, password); |
| @@ -161,7 +171,8 @@ static void test10(char const* infile, | @@ -161,7 +171,8 @@ static void test10(char const* infile, | ||
| 161 | 171 | ||
| 162 | static void test11(char const* infile, | 172 | static void test11(char const* infile, |
| 163 | char const* password, | 173 | char const* password, |
| 164 | - char const* outfile) | 174 | + char const* outfile, |
| 175 | + char const* outfile2) | ||
| 165 | { | 176 | { |
| 166 | qpdf_read(qpdf, infile, password); | 177 | qpdf_read(qpdf, infile, password); |
| 167 | qpdf_init_write(qpdf, outfile); | 178 | qpdf_init_write(qpdf, outfile); |
| @@ -174,7 +185,8 @@ static void test11(char const* infile, | @@ -174,7 +185,8 @@ static void test11(char const* infile, | ||
| 174 | 185 | ||
| 175 | static void test12(char const* infile, | 186 | static void test12(char const* infile, |
| 176 | char const* password, | 187 | char const* password, |
| 177 | - char const* outfile) | 188 | + char const* outfile, |
| 189 | + char const* outfile2) | ||
| 178 | { | 190 | { |
| 179 | qpdf_read(qpdf, infile, password); | 191 | qpdf_read(qpdf, infile, password); |
| 180 | qpdf_init_write(qpdf, outfile); | 192 | qpdf_init_write(qpdf, outfile); |
| @@ -188,7 +200,8 @@ static void test12(char const* infile, | @@ -188,7 +200,8 @@ static void test12(char const* infile, | ||
| 188 | 200 | ||
| 189 | static void test13(char const* infile, | 201 | static void test13(char const* infile, |
| 190 | char const* password, | 202 | char const* password, |
| 191 | - char const* outfile) | 203 | + char const* outfile, |
| 204 | + char const* outfile2) | ||
| 192 | { | 205 | { |
| 193 | qpdf_read(qpdf, infile, password); | 206 | qpdf_read(qpdf, infile, password); |
| 194 | printf("user password: %s\n", qpdf_get_user_password(qpdf)); | 207 | printf("user password: %s\n", qpdf_get_user_password(qpdf)); |
| @@ -199,15 +212,33 @@ static void test13(char const* infile, | @@ -199,15 +212,33 @@ static void test13(char const* infile, | ||
| 199 | report_errors(); | 212 | report_errors(); |
| 200 | } | 213 | } |
| 201 | 214 | ||
| 215 | +static void test14(char const* infile, | ||
| 216 | + char const* password, | ||
| 217 | + char const* outfile, | ||
| 218 | + char const* outfile2) | ||
| 219 | +{ | ||
| 220 | + qpdf_read(qpdf, infile, password); | ||
| 221 | + qpdf_init_write(qpdf, outfile); | ||
| 222 | + qpdf_set_static_ID(qpdf, QPDF_TRUE); | ||
| 223 | + qpdf_set_minimum_pdf_version(qpdf, "1.6"); | ||
| 224 | + qpdf_write(qpdf); | ||
| 225 | + qpdf_init_write(qpdf, outfile2); | ||
| 226 | + qpdf_set_static_ID(qpdf, QPDF_TRUE); | ||
| 227 | + qpdf_force_pdf_version(qpdf, "1.4"); | ||
| 228 | + qpdf_write(qpdf); | ||
| 229 | + report_errors(); | ||
| 230 | +} | ||
| 231 | + | ||
| 202 | int main(int argc, char* argv[]) | 232 | int main(int argc, char* argv[]) |
| 203 | { | 233 | { |
| 204 | char* whoami = 0; | 234 | char* whoami = 0; |
| 205 | char* p = 0; | 235 | char* p = 0; |
| 206 | int n = 0; | 236 | int n = 0; |
| 207 | - char const* infile; | ||
| 208 | - char const* password; | ||
| 209 | - char const* outfile; | ||
| 210 | - void (*fn)(char const*, char const*, char const*) = 0; | 237 | + char const* infile = 0; |
| 238 | + char const* password = 0; | ||
| 239 | + char const* outfile = 0; | ||
| 240 | + char const* outfile2 = 0; | ||
| 241 | + void (*fn)(char const*, char const*, char const*, char const*) = 0; | ||
| 211 | 242 | ||
| 212 | if ((p = strrchr(argv[0], '/')) != NULL) | 243 | if ((p = strrchr(argv[0], '/')) != NULL) |
| 213 | { | 244 | { |
| @@ -221,7 +252,7 @@ int main(int argc, char* argv[]) | @@ -221,7 +252,7 @@ int main(int argc, char* argv[]) | ||
| 221 | { | 252 | { |
| 222 | whoami = argv[0]; | 253 | whoami = argv[0]; |
| 223 | } | 254 | } |
| 224 | - if (argc != 5) | 255 | + if (argc < 5) |
| 225 | { | 256 | { |
| 226 | fprintf(stderr, "usage: %s n infile password outfile\n", whoami); | 257 | fprintf(stderr, "usage: %s n infile password outfile\n", whoami); |
| 227 | exit(2); | 258 | exit(2); |
| @@ -231,6 +262,7 @@ int main(int argc, char* argv[]) | @@ -231,6 +262,7 @@ int main(int argc, char* argv[]) | ||
| 231 | infile = argv[2]; | 262 | infile = argv[2]; |
| 232 | password = argv[3]; | 263 | password = argv[3]; |
| 233 | outfile = argv[4]; | 264 | outfile = argv[4]; |
| 265 | + outfile2 = (argc > 5 ? argv[5] : 0); | ||
| 234 | 266 | ||
| 235 | fn = ((n == 1) ? test01 : | 267 | fn = ((n == 1) ? test01 : |
| 236 | (n == 2) ? test02 : | 268 | (n == 2) ? test02 : |
| @@ -245,6 +277,7 @@ int main(int argc, char* argv[]) | @@ -245,6 +277,7 @@ int main(int argc, char* argv[]) | ||
| 245 | (n == 11) ? test11 : | 277 | (n == 11) ? test11 : |
| 246 | (n == 12) ? test12 : | 278 | (n == 12) ? test12 : |
| 247 | (n == 13) ? test13 : | 279 | (n == 13) ? test13 : |
| 280 | + (n == 14) ? test14 : | ||
| 248 | 0); | 281 | 0); |
| 249 | 282 | ||
| 250 | if (fn == 0) | 283 | if (fn == 0) |
| @@ -254,7 +287,7 @@ int main(int argc, char* argv[]) | @@ -254,7 +287,7 @@ int main(int argc, char* argv[]) | ||
| 254 | } | 287 | } |
| 255 | 288 | ||
| 256 | qpdf = qpdf_init(); | 289 | qpdf = qpdf_init(); |
| 257 | - fn(infile, password, outfile); | 290 | + fn(infile, password, outfile, outfile2); |
| 258 | qpdf_cleanup(&qpdf); | 291 | qpdf_cleanup(&qpdf); |
| 259 | assert(qpdf == 0); | 292 | assert(qpdf == 0); |
| 260 | 293 |
qpdf/qpdf.cc
| @@ -103,6 +103,8 @@ familiar with the PDF file format or who are PDF developers.\n\ | @@ -103,6 +103,8 @@ familiar with the PDF file format or who are PDF developers.\n\ | ||
| 103 | --object-streams=mode controls handing of object streams\n\ | 103 | --object-streams=mode controls handing of object streams\n\ |
| 104 | --ignore-xref-streams tells qpdf to ignore any cross-reference streams\n\ | 104 | --ignore-xref-streams tells qpdf to ignore any cross-reference streams\n\ |
| 105 | --qdf turns on \"QDF mode\" (below)\n\ | 105 | --qdf turns on \"QDF mode\" (below)\n\ |
| 106 | +--min-version=version sets the minimum PDF version of the output file\n\ | ||
| 107 | +--force-version=version forces this to be the PDF version of the output file\n\ | ||
| 106 | \n\ | 108 | \n\ |
| 107 | Values for stream data options:\n\ | 109 | Values for stream data options:\n\ |
| 108 | \n\ | 110 | \n\ |
| @@ -119,6 +121,12 @@ Values for object stream mode:\n\ | @@ -119,6 +121,12 @@ Values for object stream mode:\n\ | ||
| 119 | In qdf mode, by default, content normalization is turned on, and the\n\ | 121 | In qdf mode, by default, content normalization is turned on, and the\n\ |
| 120 | stream data mode is set to uncompress.\n\ | 122 | stream data mode is set to uncompress.\n\ |
| 121 | \n\ | 123 | \n\ |
| 124 | +Setting the minimum PDF version of the output file may raise the version\n\ | ||
| 125 | +but will never lower it. Forcing the PDF version of the output file may\n\ | ||
| 126 | +set the PDF version to a lower value than actually allowed by the file's\n\ | ||
| 127 | +contents. You should only do this if you have no other possible way to\n\ | ||
| 128 | +open the file or if you know that the file definitely doesn't include\n\ | ||
| 129 | +features not supported later versions.\n\ | ||
| 122 | \n\ | 130 | \n\ |
| 123 | Testing, Inspection, and Debugging Options\n\ | 131 | Testing, Inspection, and Debugging Options\n\ |
| 124 | ------------------------------------------\n\ | 132 | ------------------------------------------\n\ |
| @@ -518,6 +526,8 @@ int main(int argc, char* argv[]) | @@ -518,6 +526,8 @@ int main(int argc, char* argv[]) | ||
| 518 | QPDFWriter::object_stream_e object_stream_mode = QPDFWriter::o_preserve; | 526 | QPDFWriter::object_stream_e object_stream_mode = QPDFWriter::o_preserve; |
| 519 | bool ignore_xref_streams = false; | 527 | bool ignore_xref_streams = false; |
| 520 | bool qdf_mode = false; | 528 | bool qdf_mode = false; |
| 529 | + std::string min_version; | ||
| 530 | + std::string force_version; | ||
| 521 | 531 | ||
| 522 | bool static_id = false; | 532 | bool static_id = false; |
| 523 | bool suppress_original_object_id = false; | 533 | bool suppress_original_object_id = false; |
| @@ -651,6 +661,24 @@ int main(int argc, char* argv[]) | @@ -651,6 +661,24 @@ int main(int argc, char* argv[]) | ||
| 651 | { | 661 | { |
| 652 | qdf_mode = true; | 662 | qdf_mode = true; |
| 653 | } | 663 | } |
| 664 | + else if (strcmp(arg, "min-version") == 0) | ||
| 665 | + { | ||
| 666 | + if (parameter == 0) | ||
| 667 | + { | ||
| 668 | + usage("--min-version be given as" | ||
| 669 | + "--min-version=version"); | ||
| 670 | + } | ||
| 671 | + min_version = parameter; | ||
| 672 | + } | ||
| 673 | + else if (strcmp(arg, "force-version") == 0) | ||
| 674 | + { | ||
| 675 | + if (parameter == 0) | ||
| 676 | + { | ||
| 677 | + usage("--force-version be given as" | ||
| 678 | + "--force-version=version"); | ||
| 679 | + } | ||
| 680 | + force_version = parameter; | ||
| 681 | + } | ||
| 654 | else if (strcmp(arg, "static-id") == 0) | 682 | else if (strcmp(arg, "static-id") == 0) |
| 655 | { | 683 | { |
| 656 | static_id = true; | 684 | static_id = true; |
| @@ -977,6 +1005,14 @@ int main(int argc, char* argv[]) | @@ -977,6 +1005,14 @@ int main(int argc, char* argv[]) | ||
| 977 | { | 1005 | { |
| 978 | w.setObjectStreamMode(object_stream_mode); | 1006 | w.setObjectStreamMode(object_stream_mode); |
| 979 | } | 1007 | } |
| 1008 | + if (! min_version.empty()) | ||
| 1009 | + { | ||
| 1010 | + w.setMinimumPDFVersion(min_version); | ||
| 1011 | + } | ||
| 1012 | + if (! force_version.empty()) | ||
| 1013 | + { | ||
| 1014 | + w.forcePDFVersion(force_version); | ||
| 1015 | + } | ||
| 980 | w.write(); | 1016 | w.write(); |
| 981 | } | 1017 | } |
| 982 | if (! pdf.getWarnings().empty()) | 1018 | if (! pdf.getWarnings().empty()) |
qpdf/qpdf.testcov
| @@ -153,3 +153,8 @@ qpdf-c called qpdf_allow_modify_form 0 | @@ -153,3 +153,8 @@ qpdf-c called qpdf_allow_modify_form 0 | ||
| 153 | qpdf-c called qpdf_allow_modify_annotation 0 | 153 | qpdf-c called qpdf_allow_modify_annotation 0 |
| 154 | qpdf-c called qpdf_allow_modify_other 0 | 154 | qpdf-c called qpdf_allow_modify_other 0 |
| 155 | qpdf-c called qpdf_allow_modify_all 0 | 155 | qpdf-c called qpdf_allow_modify_all 0 |
| 156 | +QPDFWriter increasing minimum version 0 | ||
| 157 | +QPDFWriter using forced PDF version 0 | ||
| 158 | +qpdf-c called qpdf_set_minimum_pdf_version 0 | ||
| 159 | +qpdf-c called qpdf_force_pdf_version 0 | ||
| 160 | +qpdf-c called qpdf_init_write multiple times 0 |
qpdf/qtest/qpdf.test
| @@ -81,7 +81,7 @@ flush_tiff_cache(); | @@ -81,7 +81,7 @@ flush_tiff_cache(); | ||
| 81 | show_ntests(); | 81 | show_ntests(); |
| 82 | # ---------- | 82 | # ---------- |
| 83 | $td->notify("--- Miscellaneous Tests ---"); | 83 | $td->notify("--- Miscellaneous Tests ---"); |
| 84 | -$n_tests += 7; | 84 | +$n_tests += 14; |
| 85 | 85 | ||
| 86 | foreach (my $i = 1; $i <= 3; ++$i) | 86 | foreach (my $i = 1; $i <= 3; ++$i) |
| 87 | { | 87 | { |
| @@ -115,6 +115,44 @@ $td->runtest("show new xref stream", | @@ -115,6 +115,44 @@ $td->runtest("show new xref stream", | ||
| 115 | $td->EXIT_STATUS => 0}, | 115 | $td->EXIT_STATUS => 0}, |
| 116 | $td->NORMALIZE_NEWLINES); | 116 | $td->NORMALIZE_NEWLINES); |
| 117 | 117 | ||
| 118 | +# Min/Force version | ||
| 119 | +$td->runtest("set min version", | ||
| 120 | + {$td->COMMAND => "qpdf --min-version=1.6 good1.pdf a.pdf"}, | ||
| 121 | + {$td->STRING => "", | ||
| 122 | + $td->EXIT_STATUS => 0}, | ||
| 123 | + $td->NORMALIZE_NEWLINES); | ||
| 124 | +$td->runtest("check version", | ||
| 125 | + {$td->COMMAND => "qpdf --check a.pdf"}, | ||
| 126 | + {$td->FILE => "min-version.out", | ||
| 127 | + $td->EXIT_STATUS => 0}, | ||
| 128 | + $td->NORMALIZE_NEWLINES); | ||
| 129 | +$td->runtest("force version", | ||
| 130 | + {$td->COMMAND => "qpdf --force-version=1.4 a.pdf b.pdf"}, | ||
| 131 | + {$td->STRING => "", | ||
| 132 | + $td->EXIT_STATUS => 0}, | ||
| 133 | + $td->NORMALIZE_NEWLINES); | ||
| 134 | +$td->runtest("check version", | ||
| 135 | + {$td->COMMAND => "qpdf --check b.pdf"}, | ||
| 136 | + {$td->FILE => "forced-version.out", | ||
| 137 | + $td->EXIT_STATUS => 0}, | ||
| 138 | + $td->NORMALIZE_NEWLINES); | ||
| 139 | +unlink "a.pdf", "b.pdf" or die; | ||
| 140 | +$td->runtest("C API: min/force versions", | ||
| 141 | + {$td->COMMAND => "qpdf-ctest 14 object-stream.pdf '' a.pdf b.pdf"}, | ||
| 142 | + {$td->STRING => "", | ||
| 143 | + $td->EXIT_STATUS => 0}, | ||
| 144 | + $td->NORMALIZE_NEWLINES); | ||
| 145 | +$td->runtest("C check version 1", | ||
| 146 | + {$td->COMMAND => "qpdf --check a.pdf"}, | ||
| 147 | + {$td->FILE => "min-version.out", | ||
| 148 | + $td->EXIT_STATUS => 0}, | ||
| 149 | + $td->NORMALIZE_NEWLINES); | ||
| 150 | +$td->runtest("C check version 2", | ||
| 151 | + {$td->COMMAND => "qpdf --check b.pdf"}, | ||
| 152 | + {$td->FILE => "forced-version.out", | ||
| 153 | + $td->EXIT_STATUS => 0}, | ||
| 154 | + $td->NORMALIZE_NEWLINES); | ||
| 155 | + | ||
| 118 | show_ntests(); | 156 | show_ntests(); |
| 119 | # ---------- | 157 | # ---------- |
| 120 | $td->notify("--- Error Condition Tests ---"); | 158 | $td->notify("--- Error Condition Tests ---"); |
| @@ -883,7 +921,7 @@ foreach my $d (@cenc) | @@ -883,7 +921,7 @@ foreach my $d (@cenc) | ||
| 883 | my ($n, $infile, $pass, $description, $output) = @$d; | 921 | my ($n, $infile, $pass, $description, $output) = @$d; |
| 884 | my $outfile = $description; | 922 | my $outfile = $description; |
| 885 | $outfile =~ s/ /-/g; | 923 | $outfile =~ s/ /-/g; |
| 886 | - my $outfile = "c-$outfile.pdf"; | 924 | + $outfile = "c-$outfile.pdf"; |
| 887 | $td->runtest("C API encryption: $description", | 925 | $td->runtest("C API encryption: $description", |
| 888 | {$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"}, | 926 | {$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"}, |
| 889 | {$td->STRING => $output, $td->EXIT_STATUS => 0}, | 927 | {$td->STRING => $output, $td->EXIT_STATUS => 0}, |
qpdf/qtest/qpdf/forced-version.out
0 → 100644