Commit 12396702af28520b807c0b7a243ce140487e2340

Authored by Jay Berkenbilt
1 parent 2394dd85

QPDFJob: reorder functions, no other changes

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&amp; pdf) @@ -1626,225 +1888,48 @@ QPDFJob::doInspection(QPDF&amp; 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&amp; pdf, QPDFJob::UnderOverlay* uo) @@ -2021,27 +2106,6 @@ QPDFJob::validateUnderOverlay(QPDF&amp; 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&amp; pdf, QPDFWriter&amp; w) @@ -3207,6 +3292,21 @@ QPDFJob::setEncryptionOptions(QPDF&amp; pdf, QPDFWriter&amp; 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&amp; pdf) @@ -3504,104 +3604,3 @@ QPDFJob::writeOutfile(QPDF&amp; 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 -}