Commit 12396702af28520b807c0b7a243ce140487e2340
1 parent
2394dd85
QPDFJob: reorder functions, no other changes
Showing
2 changed files
with
396 additions
and
389 deletions
include/qpdf/QPDFJob.hh
| @@ -170,60 +170,6 @@ class QPDFJob | @@ -170,60 +170,6 @@ class QPDFJob | ||
| 170 | std::string prefix; | 170 | std::string prefix; |
| 171 | }; | 171 | }; |
| 172 | 172 | ||
| 173 | - PointerHolder<QPDF> doProcessOnce( | ||
| 174 | - std::function<void(QPDF*, char const*)> fn, | ||
| 175 | - char const* password, bool empty); | ||
| 176 | - PointerHolder<QPDF> doProcess( | ||
| 177 | - std::function<void(QPDF*, char const*)> fn, | ||
| 178 | - char const* password, bool empty); | ||
| 179 | - PointerHolder<QPDF> processFile( | ||
| 180 | - char const* filename, char const* password); | ||
| 181 | - void validateUnderOverlay(QPDF& pdf, QPDFJob::UnderOverlay* uo); | ||
| 182 | - void handleUnderOverlay(QPDF& pdf); | ||
| 183 | - void copyAttachments(QPDF& pdf); | ||
| 184 | - void handleTransformations(QPDF& pdf); | ||
| 185 | - void addAttachments(QPDF& pdf); | ||
| 186 | - void setWriterOptions(QPDF& pdf, QPDFWriter& w); | ||
| 187 | - void doSplitPages(QPDF& pdf, bool& warnings); | ||
| 188 | - void writeOutfile(QPDF& pdf); | ||
| 189 | - void doJSON(QPDF& pdf); | ||
| 190 | - void doInspection(QPDF& pdf); | ||
| 191 | - void setQPDFOptions(QPDF& pdf); | ||
| 192 | - void showEncryption(QPDF& pdf); | ||
| 193 | - void doCheck(QPDF& pdf); | ||
| 194 | - void doShowObj(QPDF& pdf); | ||
| 195 | - void doShowPages(QPDF& pdf); | ||
| 196 | - void doListAttachments(QPDF& pdf); | ||
| 197 | - void setEncryptionOptions(QPDF&, QPDFWriter&); | ||
| 198 | - void maybeFixWritePassword(int R, std::string& password); | ||
| 199 | - | ||
| 200 | - void doShowAttachment(QPDF& pdf); | ||
| 201 | - std::set<QPDFObjGen> getWantedJSONObjects(); | ||
| 202 | - void doJSONObjects(QPDF& pdf, JSON& j); | ||
| 203 | - void doJSONObjectinfo(QPDF& pdf, JSON& j); | ||
| 204 | - void doJSONPages(QPDF& pdf, JSON& j); | ||
| 205 | - void doJSONPageLabels(QPDF& pdf, JSON& j); | ||
| 206 | - void doJSONOutlines(QPDF& pdf, JSON& j); | ||
| 207 | - void doJSONAcroform(QPDF& pdf, JSON& j); | ||
| 208 | - void doJSONEncrypt(QPDF& pdf, JSON& j); | ||
| 209 | - void doJSONAttachments(QPDF& pdf, JSON& j); | ||
| 210 | - PointerHolder<QPDF> processInputSource( | ||
| 211 | - PointerHolder<InputSource> is, char const* password); | ||
| 212 | - void doUnderOverlayForPage( | ||
| 213 | - QPDF& pdf, | ||
| 214 | - QPDFJob::UnderOverlay& uo, | ||
| 215 | - std::map<int, std::vector<int> >& pagenos, | ||
| 216 | - size_t page_idx, | ||
| 217 | - std::map<int, QPDFObjectHandle>& fo, | ||
| 218 | - std::vector<QPDFPageObjectHelper>& pages, | ||
| 219 | - QPDFPageObjectHelper& dest_page, | ||
| 220 | - bool before); | ||
| 221 | - bool shouldRemoveUnreferencedResources(QPDF& pdf); | ||
| 222 | - void handlePageSpecs( | ||
| 223 | - QPDF& pdf, bool& warnings, | ||
| 224 | - std::vector<PointerHolder<QPDF>>& page_heap); | ||
| 225 | - void handleRotations(QPDF& pdf); | ||
| 226 | - | ||
| 227 | enum remove_unref_e { re_auto, re_yes, re_no }; | 173 | enum remove_unref_e { re_auto, re_yes, re_no }; |
| 228 | 174 | ||
| 229 | char const* password; | 175 | char const* password; |
| @@ -341,6 +287,68 @@ class QPDFJob | @@ -341,6 +287,68 @@ class QPDFJob | ||
| 341 | // QXXXQ END-PUBLIC | 287 | // QXXXQ END-PUBLIC |
| 342 | 288 | ||
| 343 | private: | 289 | private: |
| 290 | + // Basic file processing | ||
| 291 | + PointerHolder<QPDF> processFile( | ||
| 292 | + char const* filename, char const* password); | ||
| 293 | + PointerHolder<QPDF> processInputSource( | ||
| 294 | + PointerHolder<InputSource> is, char const* password); | ||
| 295 | + PointerHolder<QPDF> doProcess( | ||
| 296 | + std::function<void(QPDF*, char const*)> fn, | ||
| 297 | + char const* password, bool empty); | ||
| 298 | + PointerHolder<QPDF> doProcessOnce( | ||
| 299 | + std::function<void(QPDF*, char const*)> fn, | ||
| 300 | + char const* password, bool empty); | ||
| 301 | + | ||
| 302 | + // Transformations | ||
| 303 | + void setQPDFOptions(QPDF& pdf); | ||
| 304 | + void handlePageSpecs( | ||
| 305 | + QPDF& pdf, bool& warnings, | ||
| 306 | + std::vector<PointerHolder<QPDF>>& page_heap); | ||
| 307 | + bool shouldRemoveUnreferencedResources(QPDF& pdf); | ||
| 308 | + void handleRotations(QPDF& pdf); | ||
| 309 | + void handleUnderOverlay(QPDF& pdf); | ||
| 310 | + void doUnderOverlayForPage( | ||
| 311 | + QPDF& pdf, | ||
| 312 | + QPDFJob::UnderOverlay& uo, | ||
| 313 | + std::map<int, std::vector<int> >& pagenos, | ||
| 314 | + size_t page_idx, | ||
| 315 | + std::map<int, QPDFObjectHandle>& fo, | ||
| 316 | + std::vector<QPDFPageObjectHelper>& pages, | ||
| 317 | + QPDFPageObjectHelper& dest_page, | ||
| 318 | + bool before); | ||
| 319 | + void validateUnderOverlay(QPDF& pdf, QPDFJob::UnderOverlay* uo); | ||
| 320 | + void handleTransformations(QPDF& pdf); | ||
| 321 | + void addAttachments(QPDF& pdf); | ||
| 322 | + void copyAttachments(QPDF& pdf); | ||
| 323 | + | ||
| 324 | + // Inspection | ||
| 325 | + void doInspection(QPDF& pdf); | ||
| 326 | + void doCheck(QPDF& pdf); | ||
| 327 | + void showEncryption(QPDF& pdf); | ||
| 328 | + void doShowObj(QPDF& pdf); | ||
| 329 | + void doShowPages(QPDF& pdf); | ||
| 330 | + void doListAttachments(QPDF& pdf); | ||
| 331 | + void doShowAttachment(QPDF& pdf); | ||
| 332 | + | ||
| 333 | + // Output generation | ||
| 334 | + void doSplitPages(QPDF& pdf, bool& warnings); | ||
| 335 | + void setWriterOptions(QPDF& pdf, QPDFWriter& w); | ||
| 336 | + void setEncryptionOptions(QPDF&, QPDFWriter&); | ||
| 337 | + void maybeFixWritePassword(int R, std::string& password); | ||
| 338 | + void writeOutfile(QPDF& pdf); | ||
| 339 | + | ||
| 340 | + // JSON | ||
| 341 | + void doJSON(QPDF& pdf); | ||
| 342 | + std::set<QPDFObjGen> getWantedJSONObjects(); | ||
| 343 | + void doJSONObjects(QPDF& pdf, JSON& j); | ||
| 344 | + void doJSONObjectinfo(QPDF& pdf, JSON& j); | ||
| 345 | + void doJSONPages(QPDF& pdf, JSON& j); | ||
| 346 | + void doJSONPageLabels(QPDF& pdf, JSON& j); | ||
| 347 | + void doJSONOutlines(QPDF& pdf, JSON& j); | ||
| 348 | + void doJSONAcroform(QPDF& pdf, JSON& j); | ||
| 349 | + void doJSONEncrypt(QPDF& pdf, JSON& j); | ||
| 350 | + void doJSONAttachments(QPDF& pdf, JSON& j); | ||
| 351 | + | ||
| 344 | class Members | 352 | class Members |
| 345 | { | 353 | { |
| 346 | friend class QPDFJob; | 354 | friend class QPDFJob; |
libqpdf/QPDFJob.cc
| @@ -97,6 +97,182 @@ namespace | @@ -97,6 +97,182 @@ namespace | ||
| 97 | }; | 97 | }; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | +ImageOptimizer::ImageOptimizer(QPDFJob& o, QPDFObjectHandle& image) : | ||
| 101 | + o(o), | ||
| 102 | + image(image) | ||
| 103 | +{ | ||
| 104 | +} | ||
| 105 | + | ||
| 106 | +PointerHolder<Pipeline> | ||
| 107 | +ImageOptimizer::makePipeline(std::string const& description, Pipeline* next) | ||
| 108 | +{ | ||
| 109 | + PointerHolder<Pipeline> result; | ||
| 110 | + QPDFObjectHandle dict = image.getDict(); | ||
| 111 | + QPDFObjectHandle w_obj = dict.getKey("/Width"); | ||
| 112 | + QPDFObjectHandle h_obj = dict.getKey("/Height"); | ||
| 113 | + QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace"); | ||
| 114 | + if (! (w_obj.isNumber() && h_obj.isNumber())) | ||
| 115 | + { | ||
| 116 | + if (! description.empty()) | ||
| 117 | + { | ||
| 118 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 119 | + cout << prefix << ": " << description | ||
| 120 | + << ": not optimizing because image dictionary" | ||
| 121 | + << " is missing required keys" << std::endl; | ||
| 122 | + }); | ||
| 123 | + } | ||
| 124 | + return result; | ||
| 125 | + } | ||
| 126 | + QPDFObjectHandle components_obj = dict.getKey("/BitsPerComponent"); | ||
| 127 | + if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8))) | ||
| 128 | + { | ||
| 129 | + QTC::TC("qpdf", "qpdf image optimize bits per component"); | ||
| 130 | + if (! description.empty()) | ||
| 131 | + { | ||
| 132 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 133 | + cout << prefix << ": " << description | ||
| 134 | + << ": not optimizing because image has other than" | ||
| 135 | + << " 8 bits per component" << std::endl; | ||
| 136 | + }); | ||
| 137 | + } | ||
| 138 | + return result; | ||
| 139 | + } | ||
| 140 | + // Files have been seen in the wild whose width and height are | ||
| 141 | + // floating point, which is goofy, but we can deal with it. | ||
| 142 | + JDIMENSION w = 0; | ||
| 143 | + if (w_obj.isInteger()) | ||
| 144 | + { | ||
| 145 | + w = w_obj.getUIntValueAsUInt(); | ||
| 146 | + } | ||
| 147 | + else | ||
| 148 | + { | ||
| 149 | + w = static_cast<JDIMENSION>(w_obj.getNumericValue()); | ||
| 150 | + } | ||
| 151 | + JDIMENSION h = 0; | ||
| 152 | + if (h_obj.isInteger()) | ||
| 153 | + { | ||
| 154 | + h = h_obj.getUIntValueAsUInt(); | ||
| 155 | + } | ||
| 156 | + else | ||
| 157 | + { | ||
| 158 | + h = static_cast<JDIMENSION>(h_obj.getNumericValue()); | ||
| 159 | + } | ||
| 160 | + std::string colorspace = (colorspace_obj.isName() ? | ||
| 161 | + colorspace_obj.getName() : | ||
| 162 | + std::string()); | ||
| 163 | + int components = 0; | ||
| 164 | + J_COLOR_SPACE cs = JCS_UNKNOWN; | ||
| 165 | + if (colorspace == "/DeviceRGB") | ||
| 166 | + { | ||
| 167 | + components = 3; | ||
| 168 | + cs = JCS_RGB; | ||
| 169 | + } | ||
| 170 | + else if (colorspace == "/DeviceGray") | ||
| 171 | + { | ||
| 172 | + components = 1; | ||
| 173 | + cs = JCS_GRAYSCALE; | ||
| 174 | + } | ||
| 175 | + else if (colorspace == "/DeviceCMYK") | ||
| 176 | + { | ||
| 177 | + components = 4; | ||
| 178 | + cs = JCS_CMYK; | ||
| 179 | + } | ||
| 180 | + else | ||
| 181 | + { | ||
| 182 | + QTC::TC("qpdf", "qpdf image optimize colorspace"); | ||
| 183 | + if (! description.empty()) | ||
| 184 | + { | ||
| 185 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 186 | + cout << prefix << ": " << description | ||
| 187 | + << ": not optimizing because qpdf can't optimize" | ||
| 188 | + << " images with this colorspace" << std::endl; | ||
| 189 | + }); | ||
| 190 | + } | ||
| 191 | + return result; | ||
| 192 | + } | ||
| 193 | + if (((o.oi_min_width > 0) && (w <= o.oi_min_width)) || | ||
| 194 | + ((o.oi_min_height > 0) && (h <= o.oi_min_height)) || | ||
| 195 | + ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area))) | ||
| 196 | + { | ||
| 197 | + QTC::TC("qpdf", "qpdf image optimize too small"); | ||
| 198 | + if (! description.empty()) | ||
| 199 | + { | ||
| 200 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 201 | + cout << prefix << ": " << description | ||
| 202 | + << ": not optimizing because image" | ||
| 203 | + << " is smaller than requested minimum dimensions" | ||
| 204 | + << std::endl; | ||
| 205 | + }); | ||
| 206 | + } | ||
| 207 | + return result; | ||
| 208 | + } | ||
| 209 | + | ||
| 210 | + result = new Pl_DCT("jpg", next, w, h, components, cs); | ||
| 211 | + return result; | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +bool | ||
| 215 | +ImageOptimizer::evaluate(std::string const& description) | ||
| 216 | +{ | ||
| 217 | + if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) | ||
| 218 | + { | ||
| 219 | + QTC::TC("qpdf", "qpdf image optimize no pipeline"); | ||
| 220 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 221 | + cout << prefix << ": " << description | ||
| 222 | + << ": not optimizing because unable to decode data" | ||
| 223 | + << " or data already uses DCT" | ||
| 224 | + << std::endl; | ||
| 225 | + }); | ||
| 226 | + return false; | ||
| 227 | + } | ||
| 228 | + Pl_Discard d; | ||
| 229 | + Pl_Count c("count", &d); | ||
| 230 | + PointerHolder<Pipeline> p = makePipeline(description, &c); | ||
| 231 | + if (p.getPointer() == 0) | ||
| 232 | + { | ||
| 233 | + // message issued by makePipeline | ||
| 234 | + return false; | ||
| 235 | + } | ||
| 236 | + if (! image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized)) | ||
| 237 | + { | ||
| 238 | + return false; | ||
| 239 | + } | ||
| 240 | + long long orig_length = image.getDict().getKey("/Length").getIntValue(); | ||
| 241 | + if (c.getCount() >= orig_length) | ||
| 242 | + { | ||
| 243 | + QTC::TC("qpdf", "qpdf image optimize no shrink"); | ||
| 244 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 245 | + cout << prefix << ": " << description | ||
| 246 | + << ": not optimizing because DCT compression does not" | ||
| 247 | + << " reduce image size" << std::endl; | ||
| 248 | + }); | ||
| 249 | + return false; | ||
| 250 | + } | ||
| 251 | + o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 252 | + cout << prefix << ": " << description | ||
| 253 | + << ": optimizing image reduces size from " | ||
| 254 | + << orig_length << " to " << c.getCount() | ||
| 255 | + << std::endl; | ||
| 256 | + }); | ||
| 257 | + return true; | ||
| 258 | +} | ||
| 259 | + | ||
| 260 | +void | ||
| 261 | +ImageOptimizer::provideStreamData(int, int, Pipeline* pipeline) | ||
| 262 | +{ | ||
| 263 | + PointerHolder<Pipeline> p = makePipeline("", pipeline); | ||
| 264 | + if (p.getPointer() == 0) | ||
| 265 | + { | ||
| 266 | + // Should not be possible | ||
| 267 | + image.warnIfPossible("unable to create pipeline after previous" | ||
| 268 | + " success; image data will be lost"); | ||
| 269 | + pipeline->finish(); | ||
| 270 | + return; | ||
| 271 | + } | ||
| 272 | + image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized, | ||
| 273 | + false, false); | ||
| 274 | +} | ||
| 275 | + | ||
| 100 | QPDFPageData::QPDFPageData(std::string const& filename, | 276 | QPDFPageData::QPDFPageData(std::string const& filename, |
| 101 | QPDF* qpdf, | 277 | QPDF* qpdf, |
| 102 | char const* range) : | 278 | char const* range) : |
| @@ -132,7 +308,6 @@ ProgressReporter::reportProgress(int percentage) | @@ -132,7 +308,6 @@ ProgressReporter::reportProgress(int percentage) | ||
| 132 | << percentage << "%" << std::endl; | 308 | << percentage << "%" << std::endl; |
| 133 | } | 309 | } |
| 134 | 310 | ||
| 135 | - | ||
| 136 | QPDFJob::Members::Members() : | 311 | QPDFJob::Members::Members() : |
| 137 | message_prefix("qpdf"), | 312 | message_prefix("qpdf"), |
| 138 | warnings(false), | 313 | warnings(false), |
| @@ -246,6 +421,7 @@ QPDFJob::QPDFJob() : | @@ -246,6 +421,7 @@ QPDFJob::QPDFJob() : | ||
| 246 | { | 421 | { |
| 247 | } | 422 | } |
| 248 | 423 | ||
| 424 | + | ||
| 249 | void | 425 | void |
| 250 | QPDFJob::setMessagePrefix(std::string const& message_prefix) | 426 | QPDFJob::setMessagePrefix(std::string const& message_prefix) |
| 251 | { | 427 | { |
| @@ -269,19 +445,105 @@ QPDFJob::doIfVerbose( | @@ -269,19 +445,105 @@ QPDFJob::doIfVerbose( | ||
| 269 | } | 445 | } |
| 270 | } | 446 | } |
| 271 | 447 | ||
| 272 | -static void parse_version(std::string const& full_version_string, | ||
| 273 | - std::string& version, int& extension_level) | 448 | +void |
| 449 | +QPDFJob::run() | ||
| 274 | { | 450 | { |
| 275 | - PointerHolder<char> vp(true, QUtil::copy_string(full_version_string)); | ||
| 276 | - char* v = vp.getPointer(); | ||
| 277 | - char* p1 = strchr(v, '.'); | ||
| 278 | - char* p2 = (p1 ? strchr(1 + p1, '.') : 0); | ||
| 279 | - if (p2 && *(p2 + 1)) | 451 | + QPDFJob& o = *this; // QXXXQ |
| 452 | + PointerHolder<QPDF> pdf_ph; | ||
| 453 | + try | ||
| 280 | { | 454 | { |
| 281 | - *p2++ = '\0'; | ||
| 282 | - extension_level = QUtil::string_to_int(p2); | 455 | + pdf_ph = processFile(o.infilename, o.password); |
| 456 | + } | ||
| 457 | + catch (QPDFExc& e) | ||
| 458 | + { | ||
| 459 | + if ((e.getErrorCode() == qpdf_e_password) && | ||
| 460 | + (o.check_is_encrypted || o.check_requires_password)) | ||
| 461 | + { | ||
| 462 | + // Allow --is-encrypted and --requires-password to | ||
| 463 | + // work when an incorrect password is supplied. | ||
| 464 | + this->m->encryption_status = | ||
| 465 | + qpdf_es_encrypted | | ||
| 466 | + qpdf_es_password_incorrect; | ||
| 467 | + return; | ||
| 468 | + } | ||
| 469 | + throw e; | ||
| 470 | + } | ||
| 471 | + QPDF& pdf = *pdf_ph; | ||
| 472 | + if (pdf.isEncrypted()) | ||
| 473 | + { | ||
| 474 | + this->m->encryption_status = qpdf_es_encrypted; | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + if (o.check_is_encrypted || o.check_requires_password) | ||
| 478 | + { | ||
| 479 | + return; | ||
| 480 | + } | ||
| 481 | + bool other_warnings = false; | ||
| 482 | + std::vector<PointerHolder<QPDF>> page_heap; | ||
| 483 | + if (! o.page_specs.empty()) | ||
| 484 | + { | ||
| 485 | + handlePageSpecs(pdf, other_warnings, page_heap); | ||
| 486 | + } | ||
| 487 | + if (! o.rotations.empty()) | ||
| 488 | + { | ||
| 489 | + handleRotations(pdf); | ||
| 490 | + } | ||
| 491 | + handleUnderOverlay(pdf); | ||
| 492 | + handleTransformations(pdf); | ||
| 493 | + | ||
| 494 | + this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input); | ||
| 495 | + if (! this->m->creates_output) | ||
| 496 | + { | ||
| 497 | + doInspection(pdf); | ||
| 498 | + } | ||
| 499 | + else if (o.split_pages) | ||
| 500 | + { | ||
| 501 | + doSplitPages(pdf, other_warnings); | ||
| 502 | + } | ||
| 503 | + else | ||
| 504 | + { | ||
| 505 | + writeOutfile(pdf); | ||
| 506 | + } | ||
| 507 | + if (! pdf.getWarnings().empty()) | ||
| 508 | + { | ||
| 509 | + this->m->warnings = true; | ||
| 283 | } | 510 | } |
| 284 | - version = v; | 511 | +} |
| 512 | + | ||
| 513 | +bool | ||
| 514 | +QPDFJob::hasWarnings() | ||
| 515 | +{ | ||
| 516 | + return this->m->warnings; | ||
| 517 | +} | ||
| 518 | + | ||
| 519 | +bool | ||
| 520 | +QPDFJob::createsOutput() | ||
| 521 | +{ | ||
| 522 | + return this->m->creates_output; | ||
| 523 | +} | ||
| 524 | + | ||
| 525 | +bool | ||
| 526 | +QPDFJob::suppressWarnings() | ||
| 527 | +{ | ||
| 528 | + return this->suppress_warnings; | ||
| 529 | +} | ||
| 530 | + | ||
| 531 | +bool | ||
| 532 | +QPDFJob::checkRequiresPassword() | ||
| 533 | +{ | ||
| 534 | + return this->check_requires_password; | ||
| 535 | +} | ||
| 536 | + | ||
| 537 | +bool | ||
| 538 | +QPDFJob::checkIsEncrypted() | ||
| 539 | +{ | ||
| 540 | + return this->check_is_encrypted; | ||
| 541 | +} | ||
| 542 | + | ||
| 543 | +unsigned long | ||
| 544 | +QPDFJob::getEncryptionStatus() | ||
| 545 | +{ | ||
| 546 | + return this->m->encryption_status; | ||
| 285 | } | 547 | } |
| 286 | 548 | ||
| 287 | void | 549 | void |
| @@ -1626,225 +1888,48 @@ QPDFJob::doInspection(QPDF& pdf) | @@ -1626,225 +1888,48 @@ QPDFJob::doInspection(QPDF& pdf) | ||
| 1626 | { | 1888 | { |
| 1627 | *(this->m->cout) | 1889 | *(this->m->cout) |
| 1628 | << o.infilename << ": no linearization errors" << std::endl; | 1890 | << o.infilename << ": no linearization errors" << std::endl; |
| 1629 | - } | ||
| 1630 | - else | ||
| 1631 | - { | ||
| 1632 | - this->m->warnings = true; | ||
| 1633 | - } | ||
| 1634 | - } | ||
| 1635 | - if (o.show_linearization) | ||
| 1636 | - { | ||
| 1637 | - if (pdf.isLinearized()) | ||
| 1638 | - { | ||
| 1639 | - pdf.showLinearizationData(); | ||
| 1640 | - } | ||
| 1641 | - else | ||
| 1642 | - { | ||
| 1643 | - *(this->m->cout) | ||
| 1644 | - << o.infilename << " is not linearized" << std::endl; | ||
| 1645 | - } | ||
| 1646 | - } | ||
| 1647 | - if (o.show_xref) | ||
| 1648 | - { | ||
| 1649 | - pdf.showXRefTable(); | ||
| 1650 | - } | ||
| 1651 | - if ((o.show_obj > 0) || o.show_trailer) | ||
| 1652 | - { | ||
| 1653 | - doShowObj(pdf); | ||
| 1654 | - } | ||
| 1655 | - if (o.show_pages) | ||
| 1656 | - { | ||
| 1657 | - doShowPages(pdf); | ||
| 1658 | - } | ||
| 1659 | - if (o.list_attachments) | ||
| 1660 | - { | ||
| 1661 | - doListAttachments(pdf); | ||
| 1662 | - } | ||
| 1663 | - if (! o.attachment_to_show.empty()) | ||
| 1664 | - { | ||
| 1665 | - doShowAttachment(pdf); | ||
| 1666 | - } | ||
| 1667 | - if (! pdf.getWarnings().empty()) | ||
| 1668 | - { | ||
| 1669 | - this->m->warnings = true; | ||
| 1670 | - } | ||
| 1671 | -} | ||
| 1672 | - | ||
| 1673 | - | ||
| 1674 | -ImageOptimizer::ImageOptimizer(QPDFJob& o, QPDFObjectHandle& image) : | ||
| 1675 | - o(o), | ||
| 1676 | - image(image) | ||
| 1677 | -{ | ||
| 1678 | -} | ||
| 1679 | - | ||
| 1680 | -PointerHolder<Pipeline> | ||
| 1681 | -ImageOptimizer::makePipeline(std::string const& description, Pipeline* next) | ||
| 1682 | -{ | ||
| 1683 | - PointerHolder<Pipeline> result; | ||
| 1684 | - QPDFObjectHandle dict = image.getDict(); | ||
| 1685 | - QPDFObjectHandle w_obj = dict.getKey("/Width"); | ||
| 1686 | - QPDFObjectHandle h_obj = dict.getKey("/Height"); | ||
| 1687 | - QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace"); | ||
| 1688 | - if (! (w_obj.isNumber() && h_obj.isNumber())) | ||
| 1689 | - { | ||
| 1690 | - if (! description.empty()) | ||
| 1691 | - { | ||
| 1692 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1693 | - cout << prefix << ": " << description | ||
| 1694 | - << ": not optimizing because image dictionary" | ||
| 1695 | - << " is missing required keys" << std::endl; | ||
| 1696 | - }); | ||
| 1697 | - } | ||
| 1698 | - return result; | ||
| 1699 | - } | ||
| 1700 | - QPDFObjectHandle components_obj = dict.getKey("/BitsPerComponent"); | ||
| 1701 | - if (! (components_obj.isInteger() && (components_obj.getIntValue() == 8))) | ||
| 1702 | - { | ||
| 1703 | - QTC::TC("qpdf", "qpdf image optimize bits per component"); | ||
| 1704 | - if (! description.empty()) | ||
| 1705 | - { | ||
| 1706 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1707 | - cout << prefix << ": " << description | ||
| 1708 | - << ": not optimizing because image has other than" | ||
| 1709 | - << " 8 bits per component" << std::endl; | ||
| 1710 | - }); | ||
| 1711 | - } | ||
| 1712 | - return result; | ||
| 1713 | - } | ||
| 1714 | - // Files have been seen in the wild whose width and height are | ||
| 1715 | - // floating point, which is goofy, but we can deal with it. | ||
| 1716 | - JDIMENSION w = 0; | ||
| 1717 | - if (w_obj.isInteger()) | ||
| 1718 | - { | ||
| 1719 | - w = w_obj.getUIntValueAsUInt(); | ||
| 1720 | - } | ||
| 1721 | - else | ||
| 1722 | - { | ||
| 1723 | - w = static_cast<JDIMENSION>(w_obj.getNumericValue()); | ||
| 1724 | - } | ||
| 1725 | - JDIMENSION h = 0; | ||
| 1726 | - if (h_obj.isInteger()) | ||
| 1727 | - { | ||
| 1728 | - h = h_obj.getUIntValueAsUInt(); | ||
| 1729 | - } | ||
| 1730 | - else | ||
| 1731 | - { | ||
| 1732 | - h = static_cast<JDIMENSION>(h_obj.getNumericValue()); | ||
| 1733 | - } | ||
| 1734 | - std::string colorspace = (colorspace_obj.isName() ? | ||
| 1735 | - colorspace_obj.getName() : | ||
| 1736 | - std::string()); | ||
| 1737 | - int components = 0; | ||
| 1738 | - J_COLOR_SPACE cs = JCS_UNKNOWN; | ||
| 1739 | - if (colorspace == "/DeviceRGB") | ||
| 1740 | - { | ||
| 1741 | - components = 3; | ||
| 1742 | - cs = JCS_RGB; | ||
| 1743 | - } | ||
| 1744 | - else if (colorspace == "/DeviceGray") | ||
| 1745 | - { | ||
| 1746 | - components = 1; | ||
| 1747 | - cs = JCS_GRAYSCALE; | ||
| 1748 | - } | ||
| 1749 | - else if (colorspace == "/DeviceCMYK") | ||
| 1750 | - { | ||
| 1751 | - components = 4; | ||
| 1752 | - cs = JCS_CMYK; | ||
| 1753 | - } | ||
| 1754 | - else | ||
| 1755 | - { | ||
| 1756 | - QTC::TC("qpdf", "qpdf image optimize colorspace"); | ||
| 1757 | - if (! description.empty()) | 1891 | + } |
| 1892 | + else | ||
| 1758 | { | 1893 | { |
| 1759 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1760 | - cout << prefix << ": " << description | ||
| 1761 | - << ": not optimizing because qpdf can't optimize" | ||
| 1762 | - << " images with this colorspace" << std::endl; | ||
| 1763 | - }); | 1894 | + this->m->warnings = true; |
| 1764 | } | 1895 | } |
| 1765 | - return result; | ||
| 1766 | } | 1896 | } |
| 1767 | - if (((o.oi_min_width > 0) && (w <= o.oi_min_width)) || | ||
| 1768 | - ((o.oi_min_height > 0) && (h <= o.oi_min_height)) || | ||
| 1769 | - ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area))) | 1897 | + if (o.show_linearization) |
| 1770 | { | 1898 | { |
| 1771 | - QTC::TC("qpdf", "qpdf image optimize too small"); | ||
| 1772 | - if (! description.empty()) | 1899 | + if (pdf.isLinearized()) |
| 1773 | { | 1900 | { |
| 1774 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1775 | - cout << prefix << ": " << description | ||
| 1776 | - << ": not optimizing because image" | ||
| 1777 | - << " is smaller than requested minimum dimensions" | ||
| 1778 | - << std::endl; | ||
| 1779 | - }); | 1901 | + pdf.showLinearizationData(); |
| 1902 | + } | ||
| 1903 | + else | ||
| 1904 | + { | ||
| 1905 | + *(this->m->cout) | ||
| 1906 | + << o.infilename << " is not linearized" << std::endl; | ||
| 1780 | } | 1907 | } |
| 1781 | - return result; | ||
| 1782 | } | 1908 | } |
| 1783 | - | ||
| 1784 | - result = new Pl_DCT("jpg", next, w, h, components, cs); | ||
| 1785 | - return result; | ||
| 1786 | -} | ||
| 1787 | - | ||
| 1788 | -bool | ||
| 1789 | -ImageOptimizer::evaluate(std::string const& description) | ||
| 1790 | -{ | ||
| 1791 | - if (! image.pipeStreamData(0, 0, qpdf_dl_specialized, true)) | 1909 | + if (o.show_xref) |
| 1792 | { | 1910 | { |
| 1793 | - QTC::TC("qpdf", "qpdf image optimize no pipeline"); | ||
| 1794 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1795 | - cout << prefix << ": " << description | ||
| 1796 | - << ": not optimizing because unable to decode data" | ||
| 1797 | - << " or data already uses DCT" | ||
| 1798 | - << std::endl; | ||
| 1799 | - }); | ||
| 1800 | - return false; | 1911 | + pdf.showXRefTable(); |
| 1801 | } | 1912 | } |
| 1802 | - Pl_Discard d; | ||
| 1803 | - Pl_Count c("count", &d); | ||
| 1804 | - PointerHolder<Pipeline> p = makePipeline(description, &c); | ||
| 1805 | - if (p.getPointer() == 0) | 1913 | + if ((o.show_obj > 0) || o.show_trailer) |
| 1806 | { | 1914 | { |
| 1807 | - // message issued by makePipeline | ||
| 1808 | - return false; | 1915 | + doShowObj(pdf); |
| 1809 | } | 1916 | } |
| 1810 | - if (! image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized)) | 1917 | + if (o.show_pages) |
| 1811 | { | 1918 | { |
| 1812 | - return false; | 1919 | + doShowPages(pdf); |
| 1813 | } | 1920 | } |
| 1814 | - long long orig_length = image.getDict().getKey("/Length").getIntValue(); | ||
| 1815 | - if (c.getCount() >= orig_length) | 1921 | + if (o.list_attachments) |
| 1816 | { | 1922 | { |
| 1817 | - QTC::TC("qpdf", "qpdf image optimize no shrink"); | ||
| 1818 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1819 | - cout << prefix << ": " << description | ||
| 1820 | - << ": not optimizing because DCT compression does not" | ||
| 1821 | - << " reduce image size" << std::endl; | ||
| 1822 | - }); | ||
| 1823 | - return false; | 1923 | + doListAttachments(pdf); |
| 1824 | } | 1924 | } |
| 1825 | - o.doIfVerbose([&](std::ostream& cout, std::string const& prefix) { | ||
| 1826 | - cout << prefix << ": " << description | ||
| 1827 | - << ": optimizing image reduces size from " | ||
| 1828 | - << orig_length << " to " << c.getCount() | ||
| 1829 | - << std::endl; | ||
| 1830 | - }); | ||
| 1831 | - return true; | ||
| 1832 | -} | ||
| 1833 | - | ||
| 1834 | -void | ||
| 1835 | -ImageOptimizer::provideStreamData(int, int, Pipeline* pipeline) | ||
| 1836 | -{ | ||
| 1837 | - PointerHolder<Pipeline> p = makePipeline("", pipeline); | ||
| 1838 | - if (p.getPointer() == 0) | 1925 | + if (! o.attachment_to_show.empty()) |
| 1839 | { | 1926 | { |
| 1840 | - // Should not be possible | ||
| 1841 | - image.warnIfPossible("unable to create pipeline after previous" | ||
| 1842 | - " success; image data will be lost"); | ||
| 1843 | - pipeline->finish(); | ||
| 1844 | - return; | 1927 | + doShowAttachment(pdf); |
| 1928 | + } | ||
| 1929 | + if (! pdf.getWarnings().empty()) | ||
| 1930 | + { | ||
| 1931 | + this->m->warnings = true; | ||
| 1845 | } | 1932 | } |
| 1846 | - image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized, | ||
| 1847 | - false, false); | ||
| 1848 | } | 1933 | } |
| 1849 | 1934 | ||
| 1850 | PointerHolder<QPDF> | 1935 | PointerHolder<QPDF> |
| @@ -2021,27 +2106,6 @@ QPDFJob::validateUnderOverlay(QPDF& pdf, QPDFJob::UnderOverlay* uo) | @@ -2021,27 +2106,6 @@ QPDFJob::validateUnderOverlay(QPDF& pdf, QPDFJob::UnderOverlay* uo) | ||
| 2021 | } | 2106 | } |
| 2022 | } | 2107 | } |
| 2023 | 2108 | ||
| 2024 | -static void get_uo_pagenos(QPDFJob::UnderOverlay& uo, | ||
| 2025 | - std::map<int, std::vector<int> >& pagenos) | ||
| 2026 | -{ | ||
| 2027 | - size_t idx = 0; | ||
| 2028 | - size_t from_size = uo.from_pagenos.size(); | ||
| 2029 | - size_t repeat_size = uo.repeat_pagenos.size(); | ||
| 2030 | - for (std::vector<int>::iterator iter = uo.to_pagenos.begin(); | ||
| 2031 | - iter != uo.to_pagenos.end(); ++iter, ++idx) | ||
| 2032 | - { | ||
| 2033 | - if (idx < from_size) | ||
| 2034 | - { | ||
| 2035 | - pagenos[*iter].push_back(uo.from_pagenos.at(idx)); | ||
| 2036 | - } | ||
| 2037 | - else if (repeat_size) | ||
| 2038 | - { | ||
| 2039 | - pagenos[*iter].push_back( | ||
| 2040 | - uo.repeat_pagenos.at((idx - from_size) % repeat_size)); | ||
| 2041 | - } | ||
| 2042 | - } | ||
| 2043 | -} | ||
| 2044 | - | ||
| 2045 | static QPDFAcroFormDocumentHelper* get_afdh_for_qpdf( | 2109 | static QPDFAcroFormDocumentHelper* get_afdh_for_qpdf( |
| 2046 | std::map<unsigned long long, | 2110 | std::map<unsigned long long, |
| 2047 | PointerHolder<QPDFAcroFormDocumentHelper>>& afdh_map, | 2111 | PointerHolder<QPDFAcroFormDocumentHelper>>& afdh_map, |
| @@ -2146,6 +2210,27 @@ QPDFJob::doUnderOverlayForPage( | @@ -2146,6 +2210,27 @@ QPDFJob::doUnderOverlayForPage( | ||
| 2146 | } | 2210 | } |
| 2147 | } | 2211 | } |
| 2148 | 2212 | ||
| 2213 | +static void get_uo_pagenos(QPDFJob::UnderOverlay& uo, | ||
| 2214 | + std::map<int, std::vector<int> >& pagenos) | ||
| 2215 | +{ | ||
| 2216 | + size_t idx = 0; | ||
| 2217 | + size_t from_size = uo.from_pagenos.size(); | ||
| 2218 | + size_t repeat_size = uo.repeat_pagenos.size(); | ||
| 2219 | + for (std::vector<int>::iterator iter = uo.to_pagenos.begin(); | ||
| 2220 | + iter != uo.to_pagenos.end(); ++iter, ++idx) | ||
| 2221 | + { | ||
| 2222 | + if (idx < from_size) | ||
| 2223 | + { | ||
| 2224 | + pagenos[*iter].push_back(uo.from_pagenos.at(idx)); | ||
| 2225 | + } | ||
| 2226 | + else if (repeat_size) | ||
| 2227 | + { | ||
| 2228 | + pagenos[*iter].push_back( | ||
| 2229 | + uo.repeat_pagenos.at((idx - from_size) % repeat_size)); | ||
| 2230 | + } | ||
| 2231 | + } | ||
| 2232 | +} | ||
| 2233 | + | ||
| 2149 | void | 2234 | void |
| 2150 | QPDFJob::handleUnderOverlay(QPDF& pdf) | 2235 | QPDFJob::handleUnderOverlay(QPDF& pdf) |
| 2151 | { | 2236 | { |
| @@ -3207,6 +3292,21 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w) | @@ -3207,6 +3292,21 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w) | ||
| 3207 | } | 3292 | } |
| 3208 | } | 3293 | } |
| 3209 | 3294 | ||
| 3295 | +static void parse_version(std::string const& full_version_string, | ||
| 3296 | + std::string& version, int& extension_level) | ||
| 3297 | +{ | ||
| 3298 | + PointerHolder<char> vp(true, QUtil::copy_string(full_version_string)); | ||
| 3299 | + char* v = vp.getPointer(); | ||
| 3300 | + char* p1 = strchr(v, '.'); | ||
| 3301 | + char* p2 = (p1 ? strchr(1 + p1, '.') : 0); | ||
| 3302 | + if (p2 && *(p2 + 1)) | ||
| 3303 | + { | ||
| 3304 | + *p2++ = '\0'; | ||
| 3305 | + extension_level = QUtil::string_to_int(p2); | ||
| 3306 | + } | ||
| 3307 | + version = v; | ||
| 3308 | +} | ||
| 3309 | + | ||
| 3210 | void | 3310 | void |
| 3211 | QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w) | 3311 | QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w) |
| 3212 | { | 3312 | { |
| @@ -3504,104 +3604,3 @@ QPDFJob::writeOutfile(QPDF& pdf) | @@ -3504,104 +3604,3 @@ QPDFJob::writeOutfile(QPDF& pdf) | ||
| 3504 | } | 3604 | } |
| 3505 | } | 3605 | } |
| 3506 | } | 3606 | } |
| 3507 | - | ||
| 3508 | -void | ||
| 3509 | -QPDFJob::run() | ||
| 3510 | -{ | ||
| 3511 | - QPDFJob& o = *this; // QXXXQ | ||
| 3512 | - PointerHolder<QPDF> pdf_ph; | ||
| 3513 | - try | ||
| 3514 | - { | ||
| 3515 | - pdf_ph = processFile(o.infilename, o.password); | ||
| 3516 | - } | ||
| 3517 | - catch (QPDFExc& e) | ||
| 3518 | - { | ||
| 3519 | - if ((e.getErrorCode() == qpdf_e_password) && | ||
| 3520 | - (o.check_is_encrypted || o.check_requires_password)) | ||
| 3521 | - { | ||
| 3522 | - // Allow --is-encrypted and --requires-password to | ||
| 3523 | - // work when an incorrect password is supplied. | ||
| 3524 | - this->m->encryption_status = | ||
| 3525 | - qpdf_es_encrypted | | ||
| 3526 | - qpdf_es_password_incorrect; | ||
| 3527 | - return; | ||
| 3528 | - } | ||
| 3529 | - throw e; | ||
| 3530 | - } | ||
| 3531 | - QPDF& pdf = *pdf_ph; | ||
| 3532 | - if (pdf.isEncrypted()) | ||
| 3533 | - { | ||
| 3534 | - this->m->encryption_status = qpdf_es_encrypted; | ||
| 3535 | - } | ||
| 3536 | - | ||
| 3537 | - if (o.check_is_encrypted || o.check_requires_password) | ||
| 3538 | - { | ||
| 3539 | - return; | ||
| 3540 | - } | ||
| 3541 | - bool other_warnings = false; | ||
| 3542 | - std::vector<PointerHolder<QPDF>> page_heap; | ||
| 3543 | - if (! o.page_specs.empty()) | ||
| 3544 | - { | ||
| 3545 | - handlePageSpecs(pdf, other_warnings, page_heap); | ||
| 3546 | - } | ||
| 3547 | - if (! o.rotations.empty()) | ||
| 3548 | - { | ||
| 3549 | - handleRotations(pdf); | ||
| 3550 | - } | ||
| 3551 | - handleUnderOverlay(pdf); | ||
| 3552 | - handleTransformations(pdf); | ||
| 3553 | - | ||
| 3554 | - this->m->creates_output = ((o.outfilename != nullptr) || o.replace_input); | ||
| 3555 | - if (! this->m->creates_output) | ||
| 3556 | - { | ||
| 3557 | - doInspection(pdf); | ||
| 3558 | - } | ||
| 3559 | - else if (o.split_pages) | ||
| 3560 | - { | ||
| 3561 | - doSplitPages(pdf, other_warnings); | ||
| 3562 | - } | ||
| 3563 | - else | ||
| 3564 | - { | ||
| 3565 | - writeOutfile(pdf); | ||
| 3566 | - } | ||
| 3567 | - if (! pdf.getWarnings().empty()) | ||
| 3568 | - { | ||
| 3569 | - this->m->warnings = true; | ||
| 3570 | - } | ||
| 3571 | -} | ||
| 3572 | - | ||
| 3573 | -bool | ||
| 3574 | -QPDFJob::hasWarnings() | ||
| 3575 | -{ | ||
| 3576 | - return this->m->warnings; | ||
| 3577 | -} | ||
| 3578 | - | ||
| 3579 | -bool | ||
| 3580 | -QPDFJob::createsOutput() | ||
| 3581 | -{ | ||
| 3582 | - return this->m->creates_output; | ||
| 3583 | -} | ||
| 3584 | - | ||
| 3585 | -unsigned long | ||
| 3586 | -QPDFJob::getEncryptionStatus() | ||
| 3587 | -{ | ||
| 3588 | - return this->m->encryption_status; | ||
| 3589 | -} | ||
| 3590 | - | ||
| 3591 | -bool | ||
| 3592 | -QPDFJob::suppressWarnings() | ||
| 3593 | -{ | ||
| 3594 | - return this->suppress_warnings; | ||
| 3595 | -} | ||
| 3596 | - | ||
| 3597 | -bool | ||
| 3598 | -QPDFJob::checkRequiresPassword() | ||
| 3599 | -{ | ||
| 3600 | - return this->check_requires_password; | ||
| 3601 | -} | ||
| 3602 | - | ||
| 3603 | -bool | ||
| 3604 | -QPDFJob::checkIsEncrypted() | ||
| 3605 | -{ | ||
| 3606 | - return this->check_is_encrypted; | ||
| 3607 | -} |