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 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 173 enum remove_unref_e { re_auto, re_yes, re_no };
228 174  
229 175 char const* password;
... ... @@ -341,6 +287,68 @@ class QPDFJob
341 287 // QXXXQ END-PUBLIC
342 288  
343 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 352 class Members
345 353 {
346 354 friend class QPDFJob;
... ...
libqpdf/QPDFJob.cc
... ... @@ -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 276 QPDFPageData::QPDFPageData(std::string const& filename,
101 277 QPDF* qpdf,
102 278 char const* range) :
... ... @@ -132,7 +308,6 @@ ProgressReporter::reportProgress(int percentage)
132 308 << percentage << "%" << std::endl;
133 309 }
134 310  
135   -
136 311 QPDFJob::Members::Members() :
137 312 message_prefix("qpdf"),
138 313 warnings(false),
... ... @@ -246,6 +421,7 @@ QPDFJob::QPDFJob() :
246 421 {
247 422 }
248 423  
  424 +
249 425 void
250 426 QPDFJob::setMessagePrefix(std::string const& message_prefix)
251 427 {
... ... @@ -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 549 void
... ... @@ -1626,225 +1888,48 @@ QPDFJob::doInspection(QPDF&amp; pdf)
1626 1888 {
1627 1889 *(this->m->cout)
1628 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 1935 PointerHolder<QPDF>
... ... @@ -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 2109 static QPDFAcroFormDocumentHelper* get_afdh_for_qpdf(
2046 2110 std::map<unsigned long long,
2047 2111 PointerHolder<QPDFAcroFormDocumentHelper>>& afdh_map,
... ... @@ -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 2234 void
2150 2235 QPDFJob::handleUnderOverlay(QPDF& pdf)
2151 2236 {
... ... @@ -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 3310 void
3211 3311 QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w)
3212 3312 {
... ... @@ -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   -}
... ...