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 | 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 | 7 | * libqpdf/QPDF.cc (processXRefStream): warn and ignore extra xref |
| 4 | 8 | stream entries when stream is larger than reported size. This |
| 5 | 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 | 1 | 2.1 |
| 14 | 2 | === |
| 15 | 3 | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -34,7 +34,13 @@ class Pl_Count; |
| 34 | 34 | class QPDFWriter |
| 35 | 35 | { |
| 36 | 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 | 44 | DLL_EXPORT |
| 39 | 45 | QPDFWriter(QPDF& pdf, char const* filename); |
| 40 | 46 | DLL_EXPORT |
| ... | ... | @@ -78,6 +84,30 @@ class QPDFWriter |
| 78 | 84 | DLL_EXPORT |
| 79 | 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 | 111 | // Cause a static /ID value to be generated. Use only in test |
| 82 | 112 | // suites. |
| 83 | 113 | DLL_EXPORT |
| ... | ... | @@ -241,6 +271,7 @@ class QPDFWriter |
| 241 | 271 | std::string id1; // for /ID key of |
| 242 | 272 | std::string id2; // trailer dictionary |
| 243 | 273 | std::string min_pdf_version; |
| 274 | + std::string forced_pdf_version; | |
| 244 | 275 | int encryption_dict_objid; |
| 245 | 276 | std::string cur_data_key; |
| 246 | 277 | std::list<PointerHolder<Pipeline> > to_delete; | ... | ... |
include/qpdf/qpdf-c.h
| ... | ... | @@ -189,7 +189,12 @@ extern "C" { |
| 189 | 189 | /* Supply the name of the file to be written and initialize the |
| 190 | 190 | * qpdf_data object to handle writing operations. This function |
| 191 | 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 | 199 | DLL_EXPORT |
| 195 | 200 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename); |
| ... | ... | @@ -256,6 +261,12 @@ extern "C" { |
| 256 | 261 | DLL_EXPORT |
| 257 | 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 | 270 | /* Do actual write operation. */ |
| 260 | 271 | DLL_EXPORT |
| 261 | 272 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf); | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -100,6 +100,37 @@ QPDFWriter::setQDFMode(bool val) |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | 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 | 134 | QPDFWriter::setStaticID(bool val) |
| 104 | 135 | { |
| 105 | 136 | this->static_id = val; |
| ... | ... | @@ -147,7 +178,7 @@ QPDFWriter::setR2EncryptionParameters( |
| 147 | 178 | clear.insert(6); |
| 148 | 179 | } |
| 149 | 180 | |
| 150 | - this->min_pdf_version = "1.3"; | |
| 181 | + setMinimumPDFVersion("1.3"); | |
| 151 | 182 | setEncryptionParameters(user_password, owner_password, 1, 2, 5, clear); |
| 152 | 183 | } |
| 153 | 184 | |
| ... | ... | @@ -221,7 +252,7 @@ QPDFWriter::setR3EncryptionParameters( |
| 221 | 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 | 256 | setEncryptionParameters(user_password, owner_password, 2, 3, 16, clear); |
| 226 | 257 | } |
| 227 | 258 | |
| ... | ... | @@ -1361,7 +1392,7 @@ QPDFWriter::write() |
| 1361 | 1392 | |
| 1362 | 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 | 1398 | generateID(); |
| ... | ... | @@ -1417,15 +1448,12 @@ QPDFWriter::writeEncryptionDictionary() |
| 1417 | 1448 | void |
| 1418 | 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 | 1459 | writeString("%PDF-"); | ... | ... |
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -289,6 +289,11 @@ QPDF::initializeEncryption() |
| 289 | 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 | 297 | QPDFObjectHandle id_obj = this->trailer.getKey("/ID"); |
| 293 | 298 | if (! (id_obj.isArray() && |
| 294 | 299 | (id_obj.getArrayNItems() == 2) && |
| ... | ... | @@ -377,7 +382,6 @@ QPDF::initializeEncryption() |
| 377 | 382 | throw QPDFExc(this->file.getName() + ": invalid password"); |
| 378 | 383 | } |
| 379 | 384 | |
| 380 | - this->encrypted = true; | |
| 381 | 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 | 252 | QPDF_ERROR_CODE qpdf_init_write(qpdf_data qpdf, char const* filename) |
| 253 | 253 | { |
| 254 | 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 | 261 | try |
| 256 | 262 | { |
| 257 | 263 | qpdf->qpdf_writer = new QPDFWriter(*(qpdf->qpdf), filename); |
| ... | ... | @@ -391,6 +397,20 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) |
| 391 | 397 | } |
| 392 | 398 | |
| 393 | 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 | 414 | QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) |
| 395 | 415 | { |
| 396 | 416 | QPDF_ERROR_CODE status = QPDF_SUCCESS; | ... | ... |
qpdf/qpdf-ctest.c
| ... | ... | @@ -20,7 +20,8 @@ static void report_errors() |
| 20 | 20 | |
| 21 | 21 | static void test01(char const* infile, |
| 22 | 22 | char const* password, |
| 23 | - char const* outfile) | |
| 23 | + char const* outfile, | |
| 24 | + char const* outfile2) | |
| 24 | 25 | { |
| 25 | 26 | qpdf_read(qpdf, infile, password); |
| 26 | 27 | printf("version: %s\n", qpdf_get_pdf_version(qpdf)); |
| ... | ... | @@ -53,7 +54,8 @@ static void test01(char const* infile, |
| 53 | 54 | |
| 54 | 55 | static void test02(char const* infile, |
| 55 | 56 | char const* password, |
| 56 | - char const* outfile) | |
| 57 | + char const* outfile, | |
| 58 | + char const* outfile2) | |
| 57 | 59 | { |
| 58 | 60 | qpdf_set_suppress_warnings(qpdf, QPDF_TRUE); |
| 59 | 61 | if (((qpdf_read(qpdf, infile, password) & QPDF_ERRORS) == 0) && |
| ... | ... | @@ -67,7 +69,8 @@ static void test02(char const* infile, |
| 67 | 69 | |
| 68 | 70 | static void test03(char const* infile, |
| 69 | 71 | char const* password, |
| 70 | - char const* outfile) | |
| 72 | + char const* outfile, | |
| 73 | + char const* outfile2) | |
| 71 | 74 | { |
| 72 | 75 | qpdf_read(qpdf, infile, password); |
| 73 | 76 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -79,7 +82,8 @@ static void test03(char const* infile, |
| 79 | 82 | |
| 80 | 83 | static void test04(char const* infile, |
| 81 | 84 | char const* password, |
| 82 | - char const* outfile) | |
| 85 | + char const* outfile, | |
| 86 | + char const* outfile2) | |
| 83 | 87 | { |
| 84 | 88 | qpdf_set_ignore_xref_streams(qpdf, QPDF_TRUE); |
| 85 | 89 | qpdf_read(qpdf, infile, password); |
| ... | ... | @@ -91,7 +95,8 @@ static void test04(char const* infile, |
| 91 | 95 | |
| 92 | 96 | static void test05(char const* infile, |
| 93 | 97 | char const* password, |
| 94 | - char const* outfile) | |
| 98 | + char const* outfile, | |
| 99 | + char const* outfile2) | |
| 95 | 100 | { |
| 96 | 101 | qpdf_read(qpdf, infile, password); |
| 97 | 102 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -103,7 +108,8 @@ static void test05(char const* infile, |
| 103 | 108 | |
| 104 | 109 | static void test06(char const* infile, |
| 105 | 110 | char const* password, |
| 106 | - char const* outfile) | |
| 111 | + char const* outfile, | |
| 112 | + char const* outfile2) | |
| 107 | 113 | { |
| 108 | 114 | qpdf_read(qpdf, infile, password); |
| 109 | 115 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -115,7 +121,8 @@ static void test06(char const* infile, |
| 115 | 121 | |
| 116 | 122 | static void test07(char const* infile, |
| 117 | 123 | char const* password, |
| 118 | - char const* outfile) | |
| 124 | + char const* outfile, | |
| 125 | + char const* outfile2) | |
| 119 | 126 | { |
| 120 | 127 | qpdf_read(qpdf, infile, password); |
| 121 | 128 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -127,7 +134,8 @@ static void test07(char const* infile, |
| 127 | 134 | |
| 128 | 135 | static void test08(char const* infile, |
| 129 | 136 | char const* password, |
| 130 | - char const* outfile) | |
| 137 | + char const* outfile, | |
| 138 | + char const* outfile2) | |
| 131 | 139 | { |
| 132 | 140 | qpdf_read(qpdf, infile, password); |
| 133 | 141 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -140,7 +148,8 @@ static void test08(char const* infile, |
| 140 | 148 | |
| 141 | 149 | static void test09(char const* infile, |
| 142 | 150 | char const* password, |
| 143 | - char const* outfile) | |
| 151 | + char const* outfile, | |
| 152 | + char const* outfile2) | |
| 144 | 153 | { |
| 145 | 154 | qpdf_read(qpdf, infile, password); |
| 146 | 155 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -152,7 +161,8 @@ static void test09(char const* infile, |
| 152 | 161 | |
| 153 | 162 | static void test10(char const* infile, |
| 154 | 163 | char const* password, |
| 155 | - char const* outfile) | |
| 164 | + char const* outfile, | |
| 165 | + char const* outfile2) | |
| 156 | 166 | { |
| 157 | 167 | qpdf_set_attempt_recovery(qpdf, QPDF_FALSE); |
| 158 | 168 | qpdf_read(qpdf, infile, password); |
| ... | ... | @@ -161,7 +171,8 @@ static void test10(char const* infile, |
| 161 | 171 | |
| 162 | 172 | static void test11(char const* infile, |
| 163 | 173 | char const* password, |
| 164 | - char const* outfile) | |
| 174 | + char const* outfile, | |
| 175 | + char const* outfile2) | |
| 165 | 176 | { |
| 166 | 177 | qpdf_read(qpdf, infile, password); |
| 167 | 178 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -174,7 +185,8 @@ static void test11(char const* infile, |
| 174 | 185 | |
| 175 | 186 | static void test12(char const* infile, |
| 176 | 187 | char const* password, |
| 177 | - char const* outfile) | |
| 188 | + char const* outfile, | |
| 189 | + char const* outfile2) | |
| 178 | 190 | { |
| 179 | 191 | qpdf_read(qpdf, infile, password); |
| 180 | 192 | qpdf_init_write(qpdf, outfile); |
| ... | ... | @@ -188,7 +200,8 @@ static void test12(char const* infile, |
| 188 | 200 | |
| 189 | 201 | static void test13(char const* infile, |
| 190 | 202 | char const* password, |
| 191 | - char const* outfile) | |
| 203 | + char const* outfile, | |
| 204 | + char const* outfile2) | |
| 192 | 205 | { |
| 193 | 206 | qpdf_read(qpdf, infile, password); |
| 194 | 207 | printf("user password: %s\n", qpdf_get_user_password(qpdf)); |
| ... | ... | @@ -199,15 +212,33 @@ static void test13(char const* infile, |
| 199 | 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 | 232 | int main(int argc, char* argv[]) |
| 203 | 233 | { |
| 204 | 234 | char* whoami = 0; |
| 205 | 235 | char* p = 0; |
| 206 | 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 | 243 | if ((p = strrchr(argv[0], '/')) != NULL) |
| 213 | 244 | { |
| ... | ... | @@ -221,7 +252,7 @@ int main(int argc, char* argv[]) |
| 221 | 252 | { |
| 222 | 253 | whoami = argv[0]; |
| 223 | 254 | } |
| 224 | - if (argc != 5) | |
| 255 | + if (argc < 5) | |
| 225 | 256 | { |
| 226 | 257 | fprintf(stderr, "usage: %s n infile password outfile\n", whoami); |
| 227 | 258 | exit(2); |
| ... | ... | @@ -231,6 +262,7 @@ int main(int argc, char* argv[]) |
| 231 | 262 | infile = argv[2]; |
| 232 | 263 | password = argv[3]; |
| 233 | 264 | outfile = argv[4]; |
| 265 | + outfile2 = (argc > 5 ? argv[5] : 0); | |
| 234 | 266 | |
| 235 | 267 | fn = ((n == 1) ? test01 : |
| 236 | 268 | (n == 2) ? test02 : |
| ... | ... | @@ -245,6 +277,7 @@ int main(int argc, char* argv[]) |
| 245 | 277 | (n == 11) ? test11 : |
| 246 | 278 | (n == 12) ? test12 : |
| 247 | 279 | (n == 13) ? test13 : |
| 280 | + (n == 14) ? test14 : | |
| 248 | 281 | 0); |
| 249 | 282 | |
| 250 | 283 | if (fn == 0) |
| ... | ... | @@ -254,7 +287,7 @@ int main(int argc, char* argv[]) |
| 254 | 287 | } |
| 255 | 288 | |
| 256 | 289 | qpdf = qpdf_init(); |
| 257 | - fn(infile, password, outfile); | |
| 290 | + fn(infile, password, outfile, outfile2); | |
| 258 | 291 | qpdf_cleanup(&qpdf); |
| 259 | 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 | 103 | --object-streams=mode controls handing of object streams\n\ |
| 104 | 104 | --ignore-xref-streams tells qpdf to ignore any cross-reference streams\n\ |
| 105 | 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 | 108 | \n\ |
| 107 | 109 | Values for stream data options:\n\ |
| 108 | 110 | \n\ |
| ... | ... | @@ -119,6 +121,12 @@ Values for object stream mode:\n\ |
| 119 | 121 | In qdf mode, by default, content normalization is turned on, and the\n\ |
| 120 | 122 | stream data mode is set to uncompress.\n\ |
| 121 | 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 | 130 | \n\ |
| 123 | 131 | Testing, Inspection, and Debugging Options\n\ |
| 124 | 132 | ------------------------------------------\n\ |
| ... | ... | @@ -518,6 +526,8 @@ int main(int argc, char* argv[]) |
| 518 | 526 | QPDFWriter::object_stream_e object_stream_mode = QPDFWriter::o_preserve; |
| 519 | 527 | bool ignore_xref_streams = false; |
| 520 | 528 | bool qdf_mode = false; |
| 529 | + std::string min_version; | |
| 530 | + std::string force_version; | |
| 521 | 531 | |
| 522 | 532 | bool static_id = false; |
| 523 | 533 | bool suppress_original_object_id = false; |
| ... | ... | @@ -651,6 +661,24 @@ int main(int argc, char* argv[]) |
| 651 | 661 | { |
| 652 | 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 | 682 | else if (strcmp(arg, "static-id") == 0) |
| 655 | 683 | { |
| 656 | 684 | static_id = true; |
| ... | ... | @@ -977,6 +1005,14 @@ int main(int argc, char* argv[]) |
| 977 | 1005 | { |
| 978 | 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 | 1016 | w.write(); |
| 981 | 1017 | } |
| 982 | 1018 | if (! pdf.getWarnings().empty()) | ... | ... |
qpdf/qpdf.testcov
| ... | ... | @@ -153,3 +153,8 @@ qpdf-c called qpdf_allow_modify_form 0 |
| 153 | 153 | qpdf-c called qpdf_allow_modify_annotation 0 |
| 154 | 154 | qpdf-c called qpdf_allow_modify_other 0 |
| 155 | 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 | 81 | show_ntests(); |
| 82 | 82 | # ---------- |
| 83 | 83 | $td->notify("--- Miscellaneous Tests ---"); |
| 84 | -$n_tests += 7; | |
| 84 | +$n_tests += 14; | |
| 85 | 85 | |
| 86 | 86 | foreach (my $i = 1; $i <= 3; ++$i) |
| 87 | 87 | { |
| ... | ... | @@ -115,6 +115,44 @@ $td->runtest("show new xref stream", |
| 115 | 115 | $td->EXIT_STATUS => 0}, |
| 116 | 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 | 156 | show_ntests(); |
| 119 | 157 | # ---------- |
| 120 | 158 | $td->notify("--- Error Condition Tests ---"); |
| ... | ... | @@ -883,7 +921,7 @@ foreach my $d (@cenc) |
| 883 | 921 | my ($n, $infile, $pass, $description, $output) = @$d; |
| 884 | 922 | my $outfile = $description; |
| 885 | 923 | $outfile =~ s/ /-/g; |
| 886 | - my $outfile = "c-$outfile.pdf"; | |
| 924 | + $outfile = "c-$outfile.pdf"; | |
| 887 | 925 | $td->runtest("C API encryption: $description", |
| 888 | 926 | {$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"}, |
| 889 | 927 | {$td->STRING => $output, $td->EXIT_STATUS => 0}, | ... | ... |
qpdf/qtest/qpdf/forced-version.out
0 → 100644