Commit ee2aad438164da5f3fd9ff3f3eacbaad81bbebcc
1 parent
6f3b76b6
Add CLI flags for image optimization
Showing
19 changed files
with
1061 additions
and
0 deletions
ChangeLog
| 1 | 2019-01-04 Jay Berkenbilt <ejb@ql.org> | 1 | 2019-01-04 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Add new option --optimize-images, which recompresses every image | ||
| 4 | + using DCT (JPEG) compression as long as the image is not already | ||
| 5 | + compressed with lossy compression and recompressing the image | ||
| 6 | + reduces its size. The additional options --oi-min-width, | ||
| 7 | + --oi-min-height, and --oi-min-area prevent recompression of images | ||
| 8 | + whose width, height, or pixel area (width * height) are below a | ||
| 9 | + specified threshold. | ||
| 10 | + | ||
| 3 | * Add new option --collate. When specified, the semantics of | 11 | * Add new option --collate. When specified, the semantics of |
| 4 | --pages change from concatenation to collation. See the manual for | 12 | --pages change from concatenation to collation. See the manual for |
| 5 | a more detailed discussion. Fixes #259. | 13 | a more detailed discussion. Fixes #259. |
include/qpdf/QPDFObjectHandle.hh
| @@ -860,6 +860,7 @@ class QPDFObjectHandle | @@ -860,6 +860,7 @@ class QPDFObjectHandle | ||
| 860 | // do nothing. Objects read normally from the file have | 860 | // do nothing. Objects read normally from the file have |
| 861 | // descriptions. See comments on setObjectDescription for | 861 | // descriptions. See comments on setObjectDescription for |
| 862 | // additional details. | 862 | // additional details. |
| 863 | + QPDF_DLL | ||
| 863 | void warnIfPossible(std::string const& warning, | 864 | void warnIfPossible(std::string const& warning, |
| 864 | bool throw_if_no_description = false); | 865 | bool throw_if_no_description = false); |
| 865 | 866 |
qpdf/qpdf.cc
| @@ -11,6 +11,8 @@ | @@ -11,6 +11,8 @@ | ||
| 11 | #include <qpdf/FileInputSource.hh> | 11 | #include <qpdf/FileInputSource.hh> |
| 12 | #include <qpdf/Pl_StdioFile.hh> | 12 | #include <qpdf/Pl_StdioFile.hh> |
| 13 | #include <qpdf/Pl_Discard.hh> | 13 | #include <qpdf/Pl_Discard.hh> |
| 14 | +#include <qpdf/Pl_DCT.hh> | ||
| 15 | +#include <qpdf/Pl_Count.hh> | ||
| 14 | #include <qpdf/PointerHolder.hh> | 16 | #include <qpdf/PointerHolder.hh> |
| 15 | 17 | ||
| 16 | #include <qpdf/QPDF.hh> | 18 | #include <qpdf/QPDF.hh> |
| @@ -127,6 +129,10 @@ struct Options | @@ -127,6 +129,10 @@ struct Options | ||
| 127 | collate(false), | 129 | collate(false), |
| 128 | json(false), | 130 | json(false), |
| 129 | check(false), | 131 | check(false), |
| 132 | + optimize_images(false), | ||
| 133 | + oi_min_width(128), | ||
| 134 | + oi_min_height(128), | ||
| 135 | + oi_min_area(16384), | ||
| 130 | require_outfile(true), | 136 | require_outfile(true), |
| 131 | infilename(0), | 137 | infilename(0), |
| 132 | outfilename(0) | 138 | outfilename(0) |
| @@ -208,6 +214,10 @@ struct Options | @@ -208,6 +214,10 @@ struct Options | ||
| 208 | std::set<std::string> json_keys; | 214 | std::set<std::string> json_keys; |
| 209 | std::set<std::string> json_objects; | 215 | std::set<std::string> json_objects; |
| 210 | bool check; | 216 | bool check; |
| 217 | + bool optimize_images; | ||
| 218 | + size_t oi_min_width; | ||
| 219 | + size_t oi_min_height; | ||
| 220 | + size_t oi_min_area; | ||
| 211 | std::vector<PageSpec> page_specs; | 221 | std::vector<PageSpec> page_specs; |
| 212 | std::map<std::string, RotationSpec> rotations; | 222 | std::map<std::string, RotationSpec> rotations; |
| 213 | bool require_outfile; | 223 | bool require_outfile; |
| @@ -602,6 +612,10 @@ class ArgParser | @@ -602,6 +612,10 @@ class ArgParser | ||
| 602 | void argJsonKey(char* parameter); | 612 | void argJsonKey(char* parameter); |
| 603 | void argJsonObject(char* parameter); | 613 | void argJsonObject(char* parameter); |
| 604 | void argCheck(); | 614 | void argCheck(); |
| 615 | + void argOptimizeImages(); | ||
| 616 | + void argOiMinWidth(char* paramter); | ||
| 617 | + void argOiMinHeight(char* paramter); | ||
| 618 | + void argOiMinArea(char* paramter); | ||
| 605 | void arg40Print(char* parameter); | 619 | void arg40Print(char* parameter); |
| 606 | void arg40Modify(char* parameter); | 620 | void arg40Modify(char* parameter); |
| 607 | void arg40Extract(char* parameter); | 621 | void arg40Extract(char* parameter); |
| @@ -816,6 +830,13 @@ ArgParser::initOptionTable() | @@ -816,6 +830,13 @@ ArgParser::initOptionTable() | ||
| 816 | (*t)["json-object"] = oe_requiredParameter( | 830 | (*t)["json-object"] = oe_requiredParameter( |
| 817 | &ArgParser::argJsonObject, "trailer|obj[,gen]"); | 831 | &ArgParser::argJsonObject, "trailer|obj[,gen]"); |
| 818 | (*t)["check"] = oe_bare(&ArgParser::argCheck); | 832 | (*t)["check"] = oe_bare(&ArgParser::argCheck); |
| 833 | + (*t)["optimize-images"] = oe_bare(&ArgParser::argOptimizeImages); | ||
| 834 | + (*t)["oi-min-width"] = oe_requiredParameter( | ||
| 835 | + &ArgParser::argOiMinWidth, "minimum-width"); | ||
| 836 | + (*t)["oi-min-height"] = oe_requiredParameter( | ||
| 837 | + &ArgParser::argOiMinHeight, "minimum-height"); | ||
| 838 | + (*t)["oi-min-area"] = oe_requiredParameter( | ||
| 839 | + &ArgParser::argOiMinArea, "minimum-area"); | ||
| 819 | 840 | ||
| 820 | t = &this->encrypt40_option_table; | 841 | t = &this->encrypt40_option_table; |
| 821 | (*t)["--"] = oe_bare(&ArgParser::argEndEncrypt); | 842 | (*t)["--"] = oe_bare(&ArgParser::argEndEncrypt); |
| @@ -1417,6 +1438,30 @@ ArgParser::argCheck() | @@ -1417,6 +1438,30 @@ ArgParser::argCheck() | ||
| 1417 | } | 1438 | } |
| 1418 | 1439 | ||
| 1419 | void | 1440 | void |
| 1441 | +ArgParser::argOptimizeImages() | ||
| 1442 | +{ | ||
| 1443 | + o.optimize_images = true; | ||
| 1444 | +} | ||
| 1445 | + | ||
| 1446 | +void | ||
| 1447 | +ArgParser::argOiMinWidth(char* parameter) | ||
| 1448 | +{ | ||
| 1449 | + o.oi_min_width = QUtil::string_to_int(parameter); | ||
| 1450 | +} | ||
| 1451 | + | ||
| 1452 | +void | ||
| 1453 | +ArgParser::argOiMinHeight(char* parameter) | ||
| 1454 | +{ | ||
| 1455 | + o.oi_min_height = QUtil::string_to_int(parameter); | ||
| 1456 | +} | ||
| 1457 | + | ||
| 1458 | +void | ||
| 1459 | +ArgParser::argOiMinArea(char* parameter) | ||
| 1460 | +{ | ||
| 1461 | + o.oi_min_area = QUtil::string_to_int(parameter); | ||
| 1462 | +} | ||
| 1463 | + | ||
| 1464 | +void | ||
| 1420 | ArgParser::arg40Print(char* parameter) | 1465 | ArgParser::arg40Print(char* parameter) |
| 1421 | { | 1466 | { |
| 1422 | o.r2_print = (strcmp(parameter, "y") == 0); | 1467 | o.r2_print = (strcmp(parameter, "y") == 0); |
| @@ -1911,6 +1956,10 @@ familiar with the PDF file format or who are PDF developers.\n\ | @@ -1911,6 +1956,10 @@ familiar with the PDF file format or who are PDF developers.\n\ | ||
| 1911 | contents including those for interactive form\n\ | 1956 | contents including those for interactive form\n\ |
| 1912 | fields; may also want --generate-appearances\n\ | 1957 | fields; may also want --generate-appearances\n\ |
| 1913 | --generate-appearances generate appearance streams for form fields\n\ | 1958 | --generate-appearances generate appearance streams for form fields\n\ |
| 1959 | +--optimize-images compress images with DCT (JPEG) when advantageous\n\ | ||
| 1960 | +--oi-min-width=w do not optimize images whose width is below w\n\ | ||
| 1961 | +--oi-min-height=h do not optimize images whose height is below h\n\ | ||
| 1962 | +--oi-min-area=a do not optimize images whose pixel count is below a\n\ | ||
| 1914 | --qdf turns on \"QDF mode\" (below)\n\ | 1963 | --qdf turns on \"QDF mode\" (below)\n\ |
| 1915 | --linearize-pass1=file write intermediate pass of linearized file\n\ | 1964 | --linearize-pass1=file write intermediate pass of linearized file\n\ |
| 1916 | for debugging\n\ | 1965 | for debugging\n\ |
| @@ -3403,9 +3452,202 @@ static void do_inspection(QPDF& pdf, Options& o) | @@ -3403,9 +3452,202 @@ static void do_inspection(QPDF& pdf, Options& o) | ||
| 3403 | } | 3452 | } |
| 3404 | } | 3453 | } |
| 3405 | 3454 | ||
| 3455 | +class ImageOptimizer: public QPDFObjectHandle::StreamDataProvider | ||
| 3456 | +{ | ||
| 3457 | + public: | ||
| 3458 | + ImageOptimizer(Options& o, QPDFObjectHandle& image); | ||
| 3459 | + virtual ~ImageOptimizer() | ||
| 3460 | + { | ||
| 3461 | + } | ||
| 3462 | + virtual void provideStreamData(int objid, int generation, | ||
| 3463 | + Pipeline* pipeline); | ||
| 3464 | + PointerHolder<Pipeline> makePipeline( | ||
| 3465 | + std::string const& description, Pipeline* next); | ||
| 3466 | + bool evaluate(std::string const& description); | ||
| 3467 | + | ||
| 3468 | + private: | ||
| 3469 | + Options& o; | ||
| 3470 | + QPDFObjectHandle image; | ||
| 3471 | +}; | ||
| 3472 | + | ||
| 3473 | +ImageOptimizer::ImageOptimizer(Options& o, QPDFObjectHandle& image) : | ||
| 3474 | + o(o), | ||
| 3475 | + image(image) | ||
| 3476 | +{ | ||
| 3477 | +} | ||
| 3478 | + | ||
| 3479 | +PointerHolder<Pipeline> | ||
| 3480 | +ImageOptimizer::makePipeline(std::string const& description, Pipeline* next) | ||
| 3481 | +{ | ||
| 3482 | + PointerHolder<Pipeline> result; | ||
| 3483 | + QPDFObjectHandle dict = image.getDict(); | ||
| 3484 | + QPDFObjectHandle w_obj = dict.getKey("/Width"); | ||
| 3485 | + QPDFObjectHandle h_obj = dict.getKey("/Height"); | ||
| 3486 | + QPDFObjectHandle colorspace_obj = dict.getKey("/ColorSpace"); | ||
| 3487 | + QPDFObjectHandle components_obj = dict.getKey("/BitsPerComponent"); | ||
| 3488 | + if (! (w_obj.isInteger() && | ||
| 3489 | + h_obj.isInteger() && | ||
| 3490 | + colorspace_obj.isName() && | ||
| 3491 | + components_obj.isInteger())) | ||
| 3492 | + { | ||
| 3493 | + if (o.verbose && (! description.empty())) | ||
| 3494 | + { | ||
| 3495 | + std::cout << whoami << ": " << description | ||
| 3496 | + << ": not optimizing because image dictionary" | ||
| 3497 | + << " is missing required keys" << std::endl; | ||
| 3498 | + } | ||
| 3499 | + return result; | ||
| 3500 | + } | ||
| 3501 | + JDIMENSION w = w_obj.getIntValue(); | ||
| 3502 | + JDIMENSION h = h_obj.getIntValue(); | ||
| 3503 | + std::string colorspace = colorspace_obj.getName(); | ||
| 3504 | + int components = 0; | ||
| 3505 | + J_COLOR_SPACE cs = JCS_UNKNOWN; | ||
| 3506 | + if (colorspace == "/DeviceRGB") | ||
| 3507 | + { | ||
| 3508 | + components = 3; | ||
| 3509 | + cs = JCS_RGB; | ||
| 3510 | + } | ||
| 3511 | + else if (colorspace == "/DeviceGray") | ||
| 3512 | + { | ||
| 3513 | + components = 1; | ||
| 3514 | + cs = JCS_GRAYSCALE; | ||
| 3515 | + } | ||
| 3516 | + else if (colorspace == "/DeviceCMYK") | ||
| 3517 | + { | ||
| 3518 | + components = 4; | ||
| 3519 | + cs = JCS_CMYK; | ||
| 3520 | + } | ||
| 3521 | + else | ||
| 3522 | + { | ||
| 3523 | + if (o.verbose && (! description.empty())) | ||
| 3524 | + { | ||
| 3525 | + std::cout << whoami << ": " << description | ||
| 3526 | + << ": not optimizing because of unsupported" | ||
| 3527 | + << " image parameters" << std::endl; | ||
| 3528 | + } | ||
| 3529 | + return result; | ||
| 3530 | + } | ||
| 3531 | + if (((o.oi_min_width > 0) && (w <= o.oi_min_width)) || | ||
| 3532 | + ((o.oi_min_height > 0) && (h <= o.oi_min_height)) || | ||
| 3533 | + ((o.oi_min_area > 0) && ((w * h) <= o.oi_min_area))) | ||
| 3534 | + { | ||
| 3535 | + QTC::TC("qpdf", "qpdf image optimize too small"); | ||
| 3536 | + if (o.verbose && (! description.empty())) | ||
| 3537 | + { | ||
| 3538 | + std::cout << whoami << ": " << description | ||
| 3539 | + << ": not optimizing because of image" | ||
| 3540 | + << " is smaller than requested minimum dimensions" | ||
| 3541 | + << std::endl; | ||
| 3542 | + } | ||
| 3543 | + return result; | ||
| 3544 | + } | ||
| 3545 | + | ||
| 3546 | + result = new Pl_DCT("jpg", next, w, h, components, cs); | ||
| 3547 | + return result; | ||
| 3548 | +} | ||
| 3549 | + | ||
| 3550 | +bool | ||
| 3551 | +ImageOptimizer::evaluate(std::string const& description) | ||
| 3552 | +{ | ||
| 3553 | + Pl_Discard d; | ||
| 3554 | + Pl_Count c("count", &d); | ||
| 3555 | + PointerHolder<Pipeline> p = makePipeline(description, &c); | ||
| 3556 | + if (p.getPointer() == 0) | ||
| 3557 | + { | ||
| 3558 | + // message issued by makePipeline | ||
| 3559 | + return false; | ||
| 3560 | + } | ||
| 3561 | + if (! image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized, | ||
| 3562 | + true, false)) | ||
| 3563 | + { | ||
| 3564 | + QTC::TC("qpdf", "qpdf image optimize no pipeline"); | ||
| 3565 | + if (o.verbose) | ||
| 3566 | + { | ||
| 3567 | + std::cout << whoami << ": " << description | ||
| 3568 | + << ": not optimizing because unable to decode data" | ||
| 3569 | + << " or data already uses DCT" | ||
| 3570 | + << std::endl; | ||
| 3571 | + } | ||
| 3572 | + return false; | ||
| 3573 | + } | ||
| 3574 | + long long orig_length = image.getDict().getKey("/Length").getIntValue(); | ||
| 3575 | + if (c.getCount() >= orig_length) | ||
| 3576 | + { | ||
| 3577 | + QTC::TC("qpdf", "qpdf image optimize no shink"); | ||
| 3578 | + if (o.verbose) | ||
| 3579 | + { | ||
| 3580 | + std::cout << whoami << ": " << description | ||
| 3581 | + << ": not optimizing because DCT compression does not" | ||
| 3582 | + << " reduce image size" << std::endl; | ||
| 3583 | + } | ||
| 3584 | + return false; | ||
| 3585 | + } | ||
| 3586 | + if (o.verbose) | ||
| 3587 | + { | ||
| 3588 | + std::cout << whoami << ": " << description | ||
| 3589 | + << ": optimizing image reduces size from " | ||
| 3590 | + << orig_length << " to " << c.getCount() | ||
| 3591 | + << std::endl; | ||
| 3592 | + } | ||
| 3593 | + return true; | ||
| 3594 | +} | ||
| 3595 | + | ||
| 3596 | +void | ||
| 3597 | +ImageOptimizer::provideStreamData(int, int, Pipeline* pipeline) | ||
| 3598 | +{ | ||
| 3599 | + PointerHolder<Pipeline> p = makePipeline("", pipeline); | ||
| 3600 | + if (p.getPointer() == 0) | ||
| 3601 | + { | ||
| 3602 | + // Should not be possible | ||
| 3603 | + image.warnIfPossible("unable to create pipeline after previous" | ||
| 3604 | + " success; image data will be lost"); | ||
| 3605 | + pipeline->finish(); | ||
| 3606 | + return; | ||
| 3607 | + } | ||
| 3608 | + image.pipeStreamData(p.getPointer(), 0, qpdf_dl_specialized, | ||
| 3609 | + false, false); | ||
| 3610 | +} | ||
| 3611 | + | ||
| 3406 | static void handle_transformations(QPDF& pdf, Options& o) | 3612 | static void handle_transformations(QPDF& pdf, Options& o) |
| 3407 | { | 3613 | { |
| 3408 | QPDFPageDocumentHelper dh(pdf); | 3614 | QPDFPageDocumentHelper dh(pdf); |
| 3615 | + if (o.optimize_images) | ||
| 3616 | + { | ||
| 3617 | + int pageno = 0; | ||
| 3618 | + std::vector<QPDFPageObjectHelper> pages = dh.getAllPages(); | ||
| 3619 | + for (std::vector<QPDFPageObjectHelper>::iterator iter = pages.begin(); | ||
| 3620 | + iter != pages.end(); ++iter) | ||
| 3621 | + { | ||
| 3622 | + ++pageno; | ||
| 3623 | + QPDFPageObjectHelper& ph(*iter); | ||
| 3624 | + QPDFObjectHandle page = ph.getObjectHandle(); | ||
| 3625 | + std::map<std::string, QPDFObjectHandle> images = | ||
| 3626 | + ph.getPageImages(); | ||
| 3627 | + for (std::map<std::string, QPDFObjectHandle>::iterator iter = | ||
| 3628 | + images.begin(); | ||
| 3629 | + iter != images.end(); ++iter) | ||
| 3630 | + { | ||
| 3631 | + std::string name = (*iter).first; | ||
| 3632 | + QPDFObjectHandle& image = (*iter).second; | ||
| 3633 | + ImageOptimizer* io = new ImageOptimizer(o, image); | ||
| 3634 | + PointerHolder<QPDFObjectHandle::StreamDataProvider> sdp(io); | ||
| 3635 | + if (io->evaluate("image " + name + " on page " + | ||
| 3636 | + QUtil::int_to_string(pageno))) | ||
| 3637 | + { | ||
| 3638 | + QPDFObjectHandle new_image = | ||
| 3639 | + QPDFObjectHandle::newStream(&pdf); | ||
| 3640 | + new_image.replaceDict(image.getDict().shallowCopy()); | ||
| 3641 | + new_image.replaceStreamData( | ||
| 3642 | + sdp, | ||
| 3643 | + QPDFObjectHandle::newName("/DCTDecode"), | ||
| 3644 | + QPDFObjectHandle::newNull()); | ||
| 3645 | + page.getKey("/Resources").getKey("/XObject").replaceKey( | ||
| 3646 | + name, new_image); | ||
| 3647 | + } | ||
| 3648 | + } | ||
| 3649 | + } | ||
| 3650 | + } | ||
| 3409 | if (o.generate_appearances) | 3651 | if (o.generate_appearances) |
| 3410 | { | 3652 | { |
| 3411 | QPDFAcroFormDocumentHelper afdh(pdf); | 3653 | QPDFAcroFormDocumentHelper afdh(pdf); |
qpdf/qpdf.testcov
| @@ -402,3 +402,6 @@ QPDFFormFieldObjectHelper list found 0 | @@ -402,3 +402,6 @@ QPDFFormFieldObjectHelper list found 0 | ||
| 402 | QPDFFormFieldObjectHelper list first too low 0 | 402 | QPDFFormFieldObjectHelper list first too low 0 |
| 403 | QPDFFormFieldObjectHelper list last too high 0 | 403 | QPDFFormFieldObjectHelper list last too high 0 |
| 404 | QPDF detected dangling ref 0 | 404 | QPDF detected dangling ref 0 |
| 405 | +qpdf image optimize no pipeline 0 | ||
| 406 | +qpdf image optimize no shink 0 | ||
| 407 | +qpdf image optimize too small 0 |
qpdf/qtest/qpdf.test
| @@ -1851,6 +1851,43 @@ $td->runtest("get data", | @@ -1851,6 +1851,43 @@ $td->runtest("get data", | ||
| 1851 | 1851 | ||
| 1852 | show_ntests(); | 1852 | show_ntests(); |
| 1853 | # ---------- | 1853 | # ---------- |
| 1854 | +$td->notify("--- Image Optimization ---"); | ||
| 1855 | +my @image_opt = ( | ||
| 1856 | + ['image-streams', 'image-streams', ''], | ||
| 1857 | + ['small-images', 'defaults', ''], | ||
| 1858 | + ['small-images', 'min-width', | ||
| 1859 | + '--oi-min-width=150 --oi-min-height=0 --oi-min-area=0'], | ||
| 1860 | + ['small-images', 'min-height', | ||
| 1861 | + '--oi-min-width=0 --oi-min-height=150 --oi-min-area=0'], | ||
| 1862 | + ['small-images', 'min-area', | ||
| 1863 | + '--oi-min-width=0 --oi-min-height=0 --oi-min-area=30000'], | ||
| 1864 | + ['small-images', 'min-area-all', | ||
| 1865 | + '--oi-min-width=0 --oi-min-height=0 --oi-min-area=30000'], | ||
| 1866 | + ); | ||
| 1867 | + | ||
| 1868 | +$n_tests += 2 * scalar(@image_opt); | ||
| 1869 | + | ||
| 1870 | +foreach my $d (@image_opt) | ||
| 1871 | +{ | ||
| 1872 | + my ($f, $description, $args) = @$d; | ||
| 1873 | + | ||
| 1874 | + $td->runtest("optimize images: $description", | ||
| 1875 | + {$td->COMMAND => | ||
| 1876 | + "qpdf --static-id --optimize-images --verbose" . | ||
| 1877 | + " $args $f.pdf a.pdf", | ||
| 1878 | + $td->FILTER => "perl filter-optimize-images.pl"}, | ||
| 1879 | + {$td->FILE => "optimize-images-$description.out", | ||
| 1880 | + $td->EXIT_STATUS => 0}, | ||
| 1881 | + $td->NORMALIZE_NEWLINES); | ||
| 1882 | + $td->runtest("check json: $description", | ||
| 1883 | + {$td->COMMAND => "qpdf --json --json-key=pages a.pdf"}, | ||
| 1884 | + {$td->FILE => "optimize-images-$description-json.out", | ||
| 1885 | + $td->EXIT_STATUS => 0}, | ||
| 1886 | + $td->NORMALIZE_NEWLINES); | ||
| 1887 | +} | ||
| 1888 | + | ||
| 1889 | +show_ntests(); | ||
| 1890 | +# ---------- | ||
| 1854 | $td->notify("--- Preserve unreferenced objects ---"); | 1891 | $td->notify("--- Preserve unreferenced objects ---"); |
| 1855 | $n_tests += 6; | 1892 | $n_tests += 6; |
| 1856 | 1893 |
qpdf/qtest/qpdf/filter-optimize-images.pl
0 → 100644
qpdf/qtest/qpdf/optimize-images-defaults-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "4 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceGray", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/FlateDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": true, | ||
| 18 | + "height": 200, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "6 0 R", | ||
| 21 | + "width": 100 | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "bitspercomponent": 8, | ||
| 25 | + "colorspace": "/DeviceGray", | ||
| 26 | + "decodeparms": [ | ||
| 27 | + null | ||
| 28 | + ], | ||
| 29 | + "filter": [ | ||
| 30 | + "/FlateDecode" | ||
| 31 | + ], | ||
| 32 | + "filterable": true, | ||
| 33 | + "height": 100, | ||
| 34 | + "name": "/Im2", | ||
| 35 | + "object": "7 0 R", | ||
| 36 | + "width": 200 | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "bitspercomponent": 8, | ||
| 40 | + "colorspace": "/DeviceGray", | ||
| 41 | + "decodeparms": [ | ||
| 42 | + null | ||
| 43 | + ], | ||
| 44 | + "filter": [ | ||
| 45 | + "/FlateDecode" | ||
| 46 | + ], | ||
| 47 | + "filterable": true, | ||
| 48 | + "height": 200, | ||
| 49 | + "name": "/Im3", | ||
| 50 | + "object": "8 0 R", | ||
| 51 | + "width": 200 | ||
| 52 | + } | ||
| 53 | + ], | ||
| 54 | + "label": null, | ||
| 55 | + "object": "3 0 R", | ||
| 56 | + "outlines": [], | ||
| 57 | + "pageposfrom1": 1 | ||
| 58 | + } | ||
| 59 | + ], | ||
| 60 | + "parameters": { | ||
| 61 | + "decodelevel": "generalized" | ||
| 62 | + }, | ||
| 63 | + "version": 1 | ||
| 64 | +} |
qpdf/qtest/qpdf/optimize-images-defaults.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 2 | +qpdf: image /Im2 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 3 | +qpdf: image /Im3 on page 1: not optimizing because DCT compression does not reduce image size | ||
| 4 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/optimize-images-image-streams-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "12 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceCMYK", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/DCTDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": false, | ||
| 18 | + "height": 480, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "14 0 R", | ||
| 21 | + "width": 400 | ||
| 22 | + } | ||
| 23 | + ], | ||
| 24 | + "label": null, | ||
| 25 | + "object": "3 0 R", | ||
| 26 | + "outlines": [], | ||
| 27 | + "pageposfrom1": 1 | ||
| 28 | + }, | ||
| 29 | + { | ||
| 30 | + "contents": [ | ||
| 31 | + "15 0 R" | ||
| 32 | + ], | ||
| 33 | + "images": [ | ||
| 34 | + { | ||
| 35 | + "bitspercomponent": 8, | ||
| 36 | + "colorspace": "/DeviceCMYK", | ||
| 37 | + "decodeparms": [ | ||
| 38 | + null | ||
| 39 | + ], | ||
| 40 | + "filter": [ | ||
| 41 | + "/DCTDecode" | ||
| 42 | + ], | ||
| 43 | + "filterable": false, | ||
| 44 | + "height": 480, | ||
| 45 | + "name": "/Im1", | ||
| 46 | + "object": "16 0 R", | ||
| 47 | + "width": 400 | ||
| 48 | + } | ||
| 49 | + ], | ||
| 50 | + "label": null, | ||
| 51 | + "object": "4 0 R", | ||
| 52 | + "outlines": [], | ||
| 53 | + "pageposfrom1": 2 | ||
| 54 | + }, | ||
| 55 | + { | ||
| 56 | + "contents": [ | ||
| 57 | + "17 0 R" | ||
| 58 | + ], | ||
| 59 | + "images": [ | ||
| 60 | + { | ||
| 61 | + "bitspercomponent": 8, | ||
| 62 | + "colorspace": "/DeviceCMYK", | ||
| 63 | + "decodeparms": [ | ||
| 64 | + null | ||
| 65 | + ], | ||
| 66 | + "filter": [ | ||
| 67 | + "/DCTDecode" | ||
| 68 | + ], | ||
| 69 | + "filterable": false, | ||
| 70 | + "height": 480, | ||
| 71 | + "name": "/Im1", | ||
| 72 | + "object": "18 0 R", | ||
| 73 | + "width": 400 | ||
| 74 | + } | ||
| 75 | + ], | ||
| 76 | + "label": null, | ||
| 77 | + "object": "5 0 R", | ||
| 78 | + "outlines": [], | ||
| 79 | + "pageposfrom1": 3 | ||
| 80 | + }, | ||
| 81 | + { | ||
| 82 | + "contents": [ | ||
| 83 | + "19 0 R" | ||
| 84 | + ], | ||
| 85 | + "images": [ | ||
| 86 | + { | ||
| 87 | + "bitspercomponent": 8, | ||
| 88 | + "colorspace": "/DeviceRGB", | ||
| 89 | + "decodeparms": [ | ||
| 90 | + null | ||
| 91 | + ], | ||
| 92 | + "filter": [ | ||
| 93 | + "/DCTDecode" | ||
| 94 | + ], | ||
| 95 | + "filterable": false, | ||
| 96 | + "height": 480, | ||
| 97 | + "name": "/Im1", | ||
| 98 | + "object": "20 0 R", | ||
| 99 | + "width": 400 | ||
| 100 | + } | ||
| 101 | + ], | ||
| 102 | + "label": null, | ||
| 103 | + "object": "6 0 R", | ||
| 104 | + "outlines": [], | ||
| 105 | + "pageposfrom1": 4 | ||
| 106 | + }, | ||
| 107 | + { | ||
| 108 | + "contents": [ | ||
| 109 | + "21 0 R" | ||
| 110 | + ], | ||
| 111 | + "images": [ | ||
| 112 | + { | ||
| 113 | + "bitspercomponent": 8, | ||
| 114 | + "colorspace": "/DeviceRGB", | ||
| 115 | + "decodeparms": [ | ||
| 116 | + null | ||
| 117 | + ], | ||
| 118 | + "filter": [ | ||
| 119 | + "/DCTDecode" | ||
| 120 | + ], | ||
| 121 | + "filterable": false, | ||
| 122 | + "height": 480, | ||
| 123 | + "name": "/Im1", | ||
| 124 | + "object": "22 0 R", | ||
| 125 | + "width": 400 | ||
| 126 | + } | ||
| 127 | + ], | ||
| 128 | + "label": null, | ||
| 129 | + "object": "7 0 R", | ||
| 130 | + "outlines": [], | ||
| 131 | + "pageposfrom1": 5 | ||
| 132 | + }, | ||
| 133 | + { | ||
| 134 | + "contents": [ | ||
| 135 | + "23 0 R" | ||
| 136 | + ], | ||
| 137 | + "images": [ | ||
| 138 | + { | ||
| 139 | + "bitspercomponent": 8, | ||
| 140 | + "colorspace": "/DeviceRGB", | ||
| 141 | + "decodeparms": [ | ||
| 142 | + null | ||
| 143 | + ], | ||
| 144 | + "filter": [ | ||
| 145 | + "/DCTDecode" | ||
| 146 | + ], | ||
| 147 | + "filterable": false, | ||
| 148 | + "height": 480, | ||
| 149 | + "name": "/Im1", | ||
| 150 | + "object": "24 0 R", | ||
| 151 | + "width": 400 | ||
| 152 | + } | ||
| 153 | + ], | ||
| 154 | + "label": null, | ||
| 155 | + "object": "8 0 R", | ||
| 156 | + "outlines": [], | ||
| 157 | + "pageposfrom1": 6 | ||
| 158 | + }, | ||
| 159 | + { | ||
| 160 | + "contents": [ | ||
| 161 | + "25 0 R" | ||
| 162 | + ], | ||
| 163 | + "images": [ | ||
| 164 | + { | ||
| 165 | + "bitspercomponent": 8, | ||
| 166 | + "colorspace": "/DeviceGray", | ||
| 167 | + "decodeparms": [ | ||
| 168 | + null | ||
| 169 | + ], | ||
| 170 | + "filter": [ | ||
| 171 | + "/DCTDecode" | ||
| 172 | + ], | ||
| 173 | + "filterable": false, | ||
| 174 | + "height": 480, | ||
| 175 | + "name": "/Im1", | ||
| 176 | + "object": "26 0 R", | ||
| 177 | + "width": 400 | ||
| 178 | + } | ||
| 179 | + ], | ||
| 180 | + "label": null, | ||
| 181 | + "object": "9 0 R", | ||
| 182 | + "outlines": [], | ||
| 183 | + "pageposfrom1": 7 | ||
| 184 | + }, | ||
| 185 | + { | ||
| 186 | + "contents": [ | ||
| 187 | + "27 0 R" | ||
| 188 | + ], | ||
| 189 | + "images": [ | ||
| 190 | + { | ||
| 191 | + "bitspercomponent": 8, | ||
| 192 | + "colorspace": "/DeviceGray", | ||
| 193 | + "decodeparms": [ | ||
| 194 | + null | ||
| 195 | + ], | ||
| 196 | + "filter": [ | ||
| 197 | + "/DCTDecode" | ||
| 198 | + ], | ||
| 199 | + "filterable": false, | ||
| 200 | + "height": 480, | ||
| 201 | + "name": "/Im1", | ||
| 202 | + "object": "28 0 R", | ||
| 203 | + "width": 400 | ||
| 204 | + } | ||
| 205 | + ], | ||
| 206 | + "label": null, | ||
| 207 | + "object": "10 0 R", | ||
| 208 | + "outlines": [], | ||
| 209 | + "pageposfrom1": 8 | ||
| 210 | + }, | ||
| 211 | + { | ||
| 212 | + "contents": [ | ||
| 213 | + "29 0 R" | ||
| 214 | + ], | ||
| 215 | + "images": [ | ||
| 216 | + { | ||
| 217 | + "bitspercomponent": 8, | ||
| 218 | + "colorspace": "/DeviceGray", | ||
| 219 | + "decodeparms": [ | ||
| 220 | + null | ||
| 221 | + ], | ||
| 222 | + "filter": [ | ||
| 223 | + "/DCTDecode" | ||
| 224 | + ], | ||
| 225 | + "filterable": false, | ||
| 226 | + "height": 480, | ||
| 227 | + "name": "/Im1", | ||
| 228 | + "object": "30 0 R", | ||
| 229 | + "width": 400 | ||
| 230 | + } | ||
| 231 | + ], | ||
| 232 | + "label": null, | ||
| 233 | + "object": "11 0 R", | ||
| 234 | + "outlines": [], | ||
| 235 | + "pageposfrom1": 9 | ||
| 236 | + } | ||
| 237 | + ], | ||
| 238 | + "parameters": { | ||
| 239 | + "decodelevel": "generalized" | ||
| 240 | + }, | ||
| 241 | + "version": 1 | ||
| 242 | +} |
qpdf/qtest/qpdf/optimize-images-image-streams.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: optimizing image reduces size from 768000 to ... | ||
| 2 | +qpdf: image /Im1 on page 2: not optimizing because unable to decode data or data already uses DCT | ||
| 3 | +qpdf: image /Im1 on page 3: optimizing image reduces size from 768998 to ... | ||
| 4 | +qpdf: image /Im1 on page 4: optimizing image reduces size from 576000 to ... | ||
| 5 | +qpdf: image /Im1 on page 5: not optimizing because unable to decode data or data already uses DCT | ||
| 6 | +qpdf: image /Im1 on page 6: optimizing image reduces size from 641497 to ... | ||
| 7 | +qpdf: image /Im1 on page 7: optimizing image reduces size from 192000 to ... | ||
| 8 | +qpdf: image /Im1 on page 8: not optimizing because unable to decode data or data already uses DCT | ||
| 9 | +qpdf: image /Im1 on page 9: optimizing image reduces size from 3001 to ... | ||
| 10 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/optimize-images-min-area-all-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "4 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceGray", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/FlateDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": true, | ||
| 18 | + "height": 200, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "6 0 R", | ||
| 21 | + "width": 100 | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "bitspercomponent": 8, | ||
| 25 | + "colorspace": "/DeviceGray", | ||
| 26 | + "decodeparms": [ | ||
| 27 | + null | ||
| 28 | + ], | ||
| 29 | + "filter": [ | ||
| 30 | + "/FlateDecode" | ||
| 31 | + ], | ||
| 32 | + "filterable": true, | ||
| 33 | + "height": 100, | ||
| 34 | + "name": "/Im2", | ||
| 35 | + "object": "7 0 R", | ||
| 36 | + "width": 200 | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "bitspercomponent": 8, | ||
| 40 | + "colorspace": "/DeviceGray", | ||
| 41 | + "decodeparms": [ | ||
| 42 | + null | ||
| 43 | + ], | ||
| 44 | + "filter": [ | ||
| 45 | + "/FlateDecode" | ||
| 46 | + ], | ||
| 47 | + "filterable": true, | ||
| 48 | + "height": 200, | ||
| 49 | + "name": "/Im3", | ||
| 50 | + "object": "8 0 R", | ||
| 51 | + "width": 200 | ||
| 52 | + } | ||
| 53 | + ], | ||
| 54 | + "label": null, | ||
| 55 | + "object": "3 0 R", | ||
| 56 | + "outlines": [], | ||
| 57 | + "pageposfrom1": 1 | ||
| 58 | + } | ||
| 59 | + ], | ||
| 60 | + "parameters": { | ||
| 61 | + "decodelevel": "generalized" | ||
| 62 | + }, | ||
| 63 | + "version": 1 | ||
| 64 | +} |
qpdf/qtest/qpdf/optimize-images-min-area-all.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 2 | +qpdf: image /Im2 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 3 | +qpdf: image /Im3 on page 1: not optimizing because DCT compression does not reduce image size | ||
| 4 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/optimize-images-min-area-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "4 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceGray", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/FlateDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": true, | ||
| 18 | + "height": 200, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "6 0 R", | ||
| 21 | + "width": 100 | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "bitspercomponent": 8, | ||
| 25 | + "colorspace": "/DeviceGray", | ||
| 26 | + "decodeparms": [ | ||
| 27 | + null | ||
| 28 | + ], | ||
| 29 | + "filter": [ | ||
| 30 | + "/FlateDecode" | ||
| 31 | + ], | ||
| 32 | + "filterable": true, | ||
| 33 | + "height": 100, | ||
| 34 | + "name": "/Im2", | ||
| 35 | + "object": "7 0 R", | ||
| 36 | + "width": 200 | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "bitspercomponent": 8, | ||
| 40 | + "colorspace": "/DeviceGray", | ||
| 41 | + "decodeparms": [ | ||
| 42 | + null | ||
| 43 | + ], | ||
| 44 | + "filter": [ | ||
| 45 | + "/FlateDecode" | ||
| 46 | + ], | ||
| 47 | + "filterable": true, | ||
| 48 | + "height": 200, | ||
| 49 | + "name": "/Im3", | ||
| 50 | + "object": "8 0 R", | ||
| 51 | + "width": 200 | ||
| 52 | + } | ||
| 53 | + ], | ||
| 54 | + "label": null, | ||
| 55 | + "object": "3 0 R", | ||
| 56 | + "outlines": [], | ||
| 57 | + "pageposfrom1": 1 | ||
| 58 | + } | ||
| 59 | + ], | ||
| 60 | + "parameters": { | ||
| 61 | + "decodelevel": "generalized" | ||
| 62 | + }, | ||
| 63 | + "version": 1 | ||
| 64 | +} |
qpdf/qtest/qpdf/optimize-images-min-area.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 2 | +qpdf: image /Im2 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 3 | +qpdf: image /Im3 on page 1: not optimizing because DCT compression does not reduce image size | ||
| 4 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/optimize-images-min-height-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "4 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceGray", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/DCTDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": false, | ||
| 18 | + "height": 200, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "6 0 R", | ||
| 21 | + "width": 100 | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "bitspercomponent": 8, | ||
| 25 | + "colorspace": "/DeviceGray", | ||
| 26 | + "decodeparms": [ | ||
| 27 | + null | ||
| 28 | + ], | ||
| 29 | + "filter": [ | ||
| 30 | + "/FlateDecode" | ||
| 31 | + ], | ||
| 32 | + "filterable": true, | ||
| 33 | + "height": 100, | ||
| 34 | + "name": "/Im2", | ||
| 35 | + "object": "7 0 R", | ||
| 36 | + "width": 200 | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "bitspercomponent": 8, | ||
| 40 | + "colorspace": "/DeviceGray", | ||
| 41 | + "decodeparms": [ | ||
| 42 | + null | ||
| 43 | + ], | ||
| 44 | + "filter": [ | ||
| 45 | + "/FlateDecode" | ||
| 46 | + ], | ||
| 47 | + "filterable": true, | ||
| 48 | + "height": 200, | ||
| 49 | + "name": "/Im3", | ||
| 50 | + "object": "8 0 R", | ||
| 51 | + "width": 200 | ||
| 52 | + } | ||
| 53 | + ], | ||
| 54 | + "label": null, | ||
| 55 | + "object": "3 0 R", | ||
| 56 | + "outlines": [], | ||
| 57 | + "pageposfrom1": 1 | ||
| 58 | + } | ||
| 59 | + ], | ||
| 60 | + "parameters": { | ||
| 61 | + "decodelevel": "generalized" | ||
| 62 | + }, | ||
| 63 | + "version": 1 | ||
| 64 | +} |
qpdf/qtest/qpdf/optimize-images-min-height.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: optimizing image reduces size from 20000 to ... | ||
| 2 | +qpdf: image /Im2 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 3 | +qpdf: image /Im3 on page 1: not optimizing because DCT compression does not reduce image size | ||
| 4 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/optimize-images-min-width-json.out
0 → 100644
| 1 | +{ | ||
| 2 | + "pages": [ | ||
| 3 | + { | ||
| 4 | + "contents": [ | ||
| 5 | + "4 0 R" | ||
| 6 | + ], | ||
| 7 | + "images": [ | ||
| 8 | + { | ||
| 9 | + "bitspercomponent": 8, | ||
| 10 | + "colorspace": "/DeviceGray", | ||
| 11 | + "decodeparms": [ | ||
| 12 | + null | ||
| 13 | + ], | ||
| 14 | + "filter": [ | ||
| 15 | + "/FlateDecode" | ||
| 16 | + ], | ||
| 17 | + "filterable": true, | ||
| 18 | + "height": 200, | ||
| 19 | + "name": "/Im1", | ||
| 20 | + "object": "6 0 R", | ||
| 21 | + "width": 100 | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + "bitspercomponent": 8, | ||
| 25 | + "colorspace": "/DeviceGray", | ||
| 26 | + "decodeparms": [ | ||
| 27 | + null | ||
| 28 | + ], | ||
| 29 | + "filter": [ | ||
| 30 | + "/DCTDecode" | ||
| 31 | + ], | ||
| 32 | + "filterable": false, | ||
| 33 | + "height": 100, | ||
| 34 | + "name": "/Im2", | ||
| 35 | + "object": "7 0 R", | ||
| 36 | + "width": 200 | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + "bitspercomponent": 8, | ||
| 40 | + "colorspace": "/DeviceGray", | ||
| 41 | + "decodeparms": [ | ||
| 42 | + null | ||
| 43 | + ], | ||
| 44 | + "filter": [ | ||
| 45 | + "/FlateDecode" | ||
| 46 | + ], | ||
| 47 | + "filterable": true, | ||
| 48 | + "height": 200, | ||
| 49 | + "name": "/Im3", | ||
| 50 | + "object": "8 0 R", | ||
| 51 | + "width": 200 | ||
| 52 | + } | ||
| 53 | + ], | ||
| 54 | + "label": null, | ||
| 55 | + "object": "3 0 R", | ||
| 56 | + "outlines": [], | ||
| 57 | + "pageposfrom1": 1 | ||
| 58 | + } | ||
| 59 | + ], | ||
| 60 | + "parameters": { | ||
| 61 | + "decodelevel": "generalized" | ||
| 62 | + }, | ||
| 63 | + "version": 1 | ||
| 64 | +} |
qpdf/qtest/qpdf/optimize-images-min-width.out
0 → 100644
| 1 | +qpdf: image /Im1 on page 1: not optimizing because of image is smaller than requested minimum dimensions | ||
| 2 | +qpdf: image /Im2 on page 1: optimizing image reduces size from 20000 to ... | ||
| 3 | +qpdf: image /Im3 on page 1: not optimizing because DCT compression does not reduce image size | ||
| 4 | +qpdf: wrote file a.pdf |
qpdf/qtest/qpdf/small-images.pdf
0 → 100644
No preview for this file type