diff options
-rw-r--r-- | examples/CMakeLists.txt | 1 | ||||
-rw-r--r-- | examples/qpdfjob-remove-annotations.cc | 78 | ||||
-rw-r--r-- | examples/qtest/qpdfjob-remove-annotations.test | 28 | ||||
-rw-r--r-- | examples/qtest/qpdfjob-remove-annotations/annotations-out.pdf | bin | 0 -> 5284 bytes | |||
-rw-r--r-- | examples/qtest/qpdfjob-remove-annotations/annotations.pdf | 942 | ||||
-rw-r--r-- | include/qpdf/QPDF.hh | 3 | ||||
-rw-r--r-- | include/qpdf/QPDFJob.hh | 27 | ||||
-rw-r--r-- | include/qpdf/QPDFWriter.hh | 89 | ||||
-rw-r--r-- | libqpdf/QPDFJob.cc | 48 | ||||
-rw-r--r-- | libqpdf/QPDFWriter.cc | 58 | ||||
-rw-r--r-- | libqpdf/QPDF_optimization.cc | 18 |
11 files changed, 1159 insertions, 133 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 947068dd..9af85fe2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,6 +15,7 @@ set(EXAMPLE_CXX_PROGRAMS pdf-set-form-values pdf-split-pages qpdf-job + qpdfjob-remove-annotations qpdfjob-save-attachment) set(EXAMPLE_C_PROGRAMS pdf-c-objects diff --git a/examples/qpdfjob-remove-annotations.cc b/examples/qpdfjob-remove-annotations.cc new file mode 100644 index 00000000..dfab3f7b --- /dev/null +++ b/examples/qpdfjob-remove-annotations.cc @@ -0,0 +1,78 @@ +#include <qpdf/QPDFJob.hh> +#include <qpdf/QPDFUsage.hh> +#include <qpdf/QUtil.hh> + +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <iostream> + +// This example demonstrates how we can use the QPDFJob createQPDF and writeQPDF +// methods to add custom transformations to the output produced by QPDFJob runs. +// The example is a full copy of the qpdf program modified to allways remove all +// annotations from the final output. + +static char const* whoami = 0; + +static void +usageExit(std::string const& msg) +{ + std::cerr << std::endl + << whoami << ": " << msg << std::endl + << std::endl + << "For help:" << std::endl + << " " << whoami << " --help=usage usage information" + << std::endl + << " " << whoami << " --help=topic help on a topic" + << std::endl + << " " << whoami << " --help=--option help on an option" + << std::endl + << " " << whoami + << " --help general help and a topic list" + << std::endl + << std::endl; + exit(QPDFJob::EXIT_ERROR); +} + +int +realmain(int argc, char* argv[]) +{ + whoami = QUtil::getWhoami(argv[0]); + QUtil::setLineBuf(stdout); + + QPDFJob j; + try { + // See "HOW TO ADD A COMMAND-LINE ARGUMENT" in README-maintainer. + j.initializeFromArgv(argv); + auto qpdf_sp = j.createQPDF(); + auto& pdf = *qpdf_sp; + for (auto page: pdf.getAllPages()) { + page.replaceKey("/Annots", "null"_qpdf); + } + j.writeQPDF(pdf); + } catch (QPDFUsage& e) { + usageExit(e.what()); + } catch (std::exception& e) { + std::cerr << whoami << ": " << e.what() << std::endl; + return QPDFJob::EXIT_ERROR; + } + return j.getExitCode(); +} + +#ifdef WINDOWS_WMAIN + +extern "C" int +wmain(int argc, wchar_t* argv[]) +{ + return QUtil::call_main_from_wmain(argc, argv, realmain); +} + +#else + +int +main(int argc, char* argv[]) +{ + return realmain(argc, argv); +} + +#endif diff --git a/examples/qtest/qpdfjob-remove-annotations.test b/examples/qtest/qpdfjob-remove-annotations.test new file mode 100644 index 00000000..32abf3ef --- /dev/null +++ b/examples/qtest/qpdfjob-remove-annotations.test @@ -0,0 +1,28 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +chdir("qpdfjob-remove-annotations"); + +require TestDriver; + +my $td = new TestDriver('qpdfjob-remove-annotations'); + +cleanup(); + +$td->runtest("remove-annotations", + {$td->COMMAND => "qpdfjob-remove-annotations --static-id annotations.pdf out.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); +$td->runtest("compare files", + {$td->FILE => "out.pdf"}, + {$td->FILE => "annotations-out.pdf"}); + +cleanup(); + +$td->report(2); + +sub cleanup +{ + unlink("out.pdf"); +} diff --git a/examples/qtest/qpdfjob-remove-annotations/annotations-out.pdf b/examples/qtest/qpdfjob-remove-annotations/annotations-out.pdf Binary files differnew file mode 100644 index 00000000..88056506 --- /dev/null +++ b/examples/qtest/qpdfjob-remove-annotations/annotations-out.pdf diff --git a/examples/qtest/qpdfjob-remove-annotations/annotations.pdf b/examples/qtest/qpdfjob-remove-annotations/annotations.pdf new file mode 100644 index 00000000..118363d7 --- /dev/null +++ b/examples/qtest/qpdfjob-remove-annotations/annotations.pdf @@ -0,0 +1,942 @@ +%PDF-1.6 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /AcroForm << + /DR 2 0 R + /Fields [ + 3 0 R + 4 0 R + 5 0 R + ] + >> + /Names << + /EmbeddedFiles 6 0 R + >> + /Pages 7 0 R + /Type /Catalog +>> +endobj + +2 0 obj +<< + /Font << + /F1 8 0 R + >> +>> +endobj + +3 0 obj +<< + /AP << + /N 9 0 R + >> + /DA (0 0.4 0 rg /F1 18 Tf) + /DR 2 0 R + /DV () + /FT /Tx + /Ff 0 + /Rect [ + 72 + 470.774 + 190.8 + 484.922 + ] + /Subtype /Widget + /T (Text Box 1) + /Type /Annot + /V (Formy field) +>> +endobj + +4 0 obj +<< + /AP << + /N 11 0 R + >> + /DA (0 0.4 0 rg /F1 18 Tf) + /DR 2 0 R + /DV () + /FT /Tx + /Ff 0 + /Rect [ + 372 + 330.774 + 386.148 + 470.374 + ] + /Subtype /Widget + /T (Text Box 1) + /Type /Annot + /V (Rot-ccw field) +>> +endobj + +5 0 obj +<< + /DV /1 + /FT /Btn + /Ff 49152 + /Kids [ + 13 0 R + 14 0 R + 15 0 R + ] + /T (r1) + /V /2 +>> +endobj + +6 0 obj +<< + /Names [ + (attachment1.txt) + 16 0 R + ] +>> +endobj + +7 0 obj +<< + /Count 1 + /Kids [ + 17 0 R + ] + /Type /Pages +>> +endobj + +8 0 obj +<< + /BaseFont /Courier + /Encoding /WinAnsiEncoding + /Subtype /Type1 + /Type /Font +>> +endobj + +9 0 obj +<< + /BBox [ + 0 + -2.826 + 118.8 + 11.322 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 10 0 R +>> +stream +/Tx BMC +q +BT + /F1 18 Tf + (Formy field) Tj +ET +Q +EMC +endstream +endobj + +10 0 obj +53 +endobj + +11 0 obj +<< + /BBox [ + 0 + -2.826 + 140.4 + 11.322 + ] + /Matrix [ + 0 + 1 + -1 + 0 + 0 + 0 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 12 0 R +>> +stream +/Tx BMC +q +BT + /F1 18 Tf + (Rot-ccw field) Tj +ET +Q +EMC +endstream +endobj + +12 0 obj +55 +endobj + +13 0 obj +<< + /AP << + /N << + /1 18 0 R + /Off 20 0 R + >> + >> + /AS /1 + /DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf) + /DR << + /Font << + /ZaDi 22 0 R + >> + >> + /F 4 + /FT /Btn + /MK << + /CA (l) + >> + /Parent 5 0 R + /Rect [ + 152.749 + 648.501 + 164.801 + 660.549 + ] + /Subtype /Widget + /Type /Annot +>> +endobj + +14 0 obj +<< + /AP << + /N << + /2 23 0 R + /Off 25 0 R + >> + >> + /AS /2 + /DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf) + /DR << + /Font << + /ZaDi 22 0 R + >> + >> + /F 4 + /FT /Btn + /MK << + /CA (l) + >> + /Parent 5 0 R + /Rect [ + 152.749 + 627.301 + 164.801 + 639.349 + ] + /Subtype /Widget + /Type /Annot +>> +endobj + +15 0 obj +<< + /AP << + /N << + /3 27 0 R + /Off 29 0 R + >> + >> + /AS /3 + /DA (0.18039 0.20392 0.21176 rg /ZaDi 0 Tf) + /DR << + /Font << + /ZaDi 22 0 R + >> + >> + /F 4 + /FT /Btn + /MK << + /CA (l) + >> + /Parent 5 0 R + /Rect [ + 151.399 + 606.501 + 163.451 + 618.549 + ] + /Subtype /Widget + /Type /Annot +>> +endobj + +16 0 obj +<< + /EF << + /F 31 0 R + /UF 31 0 R + >> + /F (attachment1.txt) + /Type /Filespec + /UF (attachment1.txt) +>> +endobj + +%% Page 1 +17 0 obj +<< + /Annots [ + 33 0 R + 3 0 R + 34 0 R + 4 0 R + 35 0 R + 36 0 R + 37 0 R + 38 0 R + 13 0 R + 14 0 R + 15 0 R + ] + /Contents 39 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 7 0 R + /Resources 2 0 R + /Type /Page +>> +endobj + +18 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 19 0 R +>> +stream +/Tx BMC +q BT +0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf +0 0 Td +ET +Q +1 0 0 rg +6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c +8.45 4.65 7.35 3.55 6 3.55 c +4.65 3.55 3.6 4.65 3.6 6 c +3.6 7.35 4.65 8.4 6 8.4 c f* + +EMC +endstream +endobj + +19 0 obj +202 +endobj + +20 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 21 0 R +>> +stream +/Tx BMC +EMC +endstream +endobj + +21 0 obj +12 +endobj + +22 0 obj +<< + /BaseFont /ZapfDingbats + /Subtype /Type1 + /Type /Font +>> +endobj + +23 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 24 0 R +>> +stream +/Tx BMC +q BT +0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf +0 0 Td +ET +Q +0 1 0 rg +6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c +8.45 4.65 7.35 3.55 6 3.55 c +4.65 3.55 3.6 4.65 3.6 6 c +3.6 7.35 4.65 8.4 6 8.4 c f* + +EMC +endstream +endobj + +24 0 obj +202 +endobj + +25 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 26 0 R +>> +stream +/Tx BMC +EMC +endstream +endobj + +26 0 obj +12 +endobj + +27 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 28 0 R +>> +stream +/Tx BMC +q BT +0.18039 0.20392 0.21176 rg /ZaDi 12.05 Tf +0 0 Td +ET +Q +0 0 1 rg +6 8.4 m 7.35 8.4 8.45 7.35 8.45 6 c +8.45 4.65 7.35 3.55 6 3.55 c +4.65 3.55 3.6 4.65 3.6 6 c +3.6 7.35 4.65 8.4 6 8.4 c f* + +EMC +endstream +endobj + +28 0 obj +202 +endobj + +29 0 obj +<< + /BBox [ + 0 + 0 + 12.05 + 12.05 + ] + /Resources 41 0 R + /Subtype /Form + /Type /XObject + /Length 30 0 R +>> +stream +/Tx BMC +EMC +endstream +endobj + +30 0 obj +12 +endobj + +31 0 obj +<< + /Params << + /CheckSum <80a33fc110b5a7b8b4d58b8d57e814bc> + /Size 22 + /Subtype /text#2fplain + >> + /Type /EmbeddedFile + /Length 32 0 R +>> +stream +content of attachment +endstream +endobj + +32 0 obj +22 +endobj + +33 0 obj +<< + /A << + /S /URI + /URI (https://www.qbilt.org/) + >> + /Border [ + 0 + 0 + .4 + ] + /C [ + .8 + .6 + .6 + ] + /H /I + /Rect [ + 72 + 501.832 + 374.4 + 520.696 + ] + /Subtype /Link + /Type /Annot +>> +endobj + +34 0 obj +<< + /AP << + /N 42 0 R + >> + /Contents (attachment1.txt) + /FS 16 0 R + /NM (attachment1.txt) + /Rect [ + 72 + 400 + 92 + 420 + ] + /Subtype /FileAttachment + /Type /Annot +>> +endobj + +35 0 obj +<< + /AP << + /N 44 0 R + >> + /DA () + /Rect [ + 72 + 350 + 92 + 360 + ] + /Subtype /FreeText + /Type /Annot +>> +endobj + +36 0 obj +<< + /AP << + /N 46 0 R + >> + /DA () + /Rect [ + 102 + 350 + 112 + 370 + ] + /Subtype /FreeText + /Type /Annot +>> +endobj + +37 0 obj +<< + /AP << + /N 48 0 R + >> + /DA () + /Rect [ + 122 + 350 + 142 + 360 + ] + /Subtype /FreeText + /Type /Annot +>> +endobj + +38 0 obj +<< + /AP << + /N 50 0 R + >> + /DA () + /Rect [ + 152 + 350 + 162 + 370 + ] + /Subtype /FreeText + /Type /Annot +>> +endobj + +%% Contents for page 1 +39 0 obj +<< + /Length 40 0 R +>> +stream +q +1 1 .7 rg +.5 .5 0 RG +72 470.77 118.8 14.15 re +B +Q +q +0 .5 .5 RG +0 1 1 rg +372 330.77 14.15 139.4 re +B +Q +q +1 0 0 RG +72 310 20 10 re +72 310 5 10 re +S +0 1 0 RG +102 310 10 20 re +102 310 10 5 re +S +0 0 1 RG +122 310 20 10 re +137 310 5 10 re +S +0.5 0 1 RG +152 310 10 20 re +152 325 10 5 re +S +10 w +0.14 .33 .18 RG +5 5 602 782 re +S +Q +BT + /F1 16 Tf + 20.6 TL + 170 650 Td + (radio button 1) Tj + (radio button 2) ' + (radio button 3) ' + 1 0 0 1 72 546 Tm + /F1 20 Tf + (Thick green border surrounds page.) Tj + 0 -40 Td + /F1 24 Tf + 0 0 1 rg + (https://www.qbilt.org) Tj + /F1 12 Tf + 1 0 0 1 202 474 Tm + (<- Formy field in yellow) Tj + 1 0 0 1 392 410 Tm + 14.4 TL + (<- Rot-ccw field) Tj + (with "Rot" at bottom) ' + (and text going up) ' + 0 g + 1 0 0 1 102 405 Tm + (Arrow to the left points down.) Tj + 1 0 0 1 182 310 Tm + (<- Drawn rectangles appear below annotations.) Tj +ET +endstream +endobj + +40 0 obj +874 +endobj + +41 0 obj +<< + /Font 52 0 R + /ProcSet [ + /PDF + /Text + ] +>> +endobj + +42 0 obj +<< + /BBox [ + 0 + 0 + 20 + 20 + ] + /Resources << + >> + /Subtype /Form + /Type /XObject + /Length 43 0 R +>> +stream +0 10 m +10 0 l +20 10 l +10 0 m +10 20 l +0 0 20 20 re +S +endstream +endobj + +43 0 obj +52 +endobj + +44 0 obj +<< + /BBox [ + 0 + 0 + 20 + 10 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 45 0 R +>> +stream +1 0 0 RG +0 0 20 10 re +0 0 5 10 re +S +endstream +endobj + +45 0 obj +36 +endobj + +46 0 obj +<< + /BBox [ + 0 + 0 + 20 + 10 + ] + /Matrix [ + 0 + 1 + -1 + 0 + 0 + 0 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 47 0 R +>> +stream +0 1 0 RG +0 0 20 10 re +0 0 5 10 re +S +endstream +endobj + +47 0 obj +36 +endobj + +48 0 obj +<< + /BBox [ + 0 + 0 + 20 + 10 + ] + /Matrix [ + -1 + 0 + 0 + -1 + 0 + 0 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 49 0 R +>> +stream +0 0 1 RG +0 0 20 10 re +0 0 5 10 re +S +endstream +endobj + +49 0 obj +36 +endobj + +50 0 obj +<< + /BBox [ + 0 + 0 + 20 + 10 + ] + /Matrix [ + 0 + -1 + 1 + 0 + 0 + 0 + ] + /Resources 2 0 R + /Subtype /Form + /Type /XObject + /Length 51 0 R +>> +stream +0.5 0 1 RG +0 0 20 10 re +0 0 5 10 re +S +endstream +endobj + +51 0 obj +38 +endobj + +52 0 obj +<< + /ZaDi 22 0 R +>> +endobj + +xref +0 53 +0000000000 65535 f +0000000025 00000 n +0000000211 00000 n +0000000263 00000 n +0000000506 00000 n +0000000755 00000 n +0000000874 00000 n +0000000944 00000 n +0000001017 00000 n +0000001121 00000 n +0000001335 00000 n +0000001355 00000 n +0000001625 00000 n +0000001645 00000 n +0000001997 00000 n +0000002349 00000 n +0000002701 00000 n +0000002842 00000 n +0000003114 00000 n +0000003473 00000 n +0000003494 00000 n +0000003663 00000 n +0000003683 00000 n +0000003764 00000 n +0000004123 00000 n +0000004144 00000 n +0000004313 00000 n +0000004333 00000 n +0000004692 00000 n +0000004713 00000 n +0000004882 00000 n +0000004902 00000 n +0000005110 00000 n +0000005130 00000 n +0000005374 00000 n +0000005578 00000 n +0000005718 00000 n +0000005860 00000 n +0000006002 00000 n +0000006167 00000 n +0000007098 00000 n +0000007119 00000 n +0000007193 00000 n +0000007397 00000 n +0000007417 00000 n +0000007603 00000 n +0000007623 00000 n +0000007862 00000 n +0000007882 00000 n +0000008122 00000 n +0000008142 00000 n +0000008383 00000 n +0000008403 00000 n +trailer << + /Root 1 0 R + /Size 53 + /ID [<a2f146daeb6d814a742556489dab9882><7b639c67bfc16b5e891fa5468aac3a14>] +>> +startxref +8441 +%%EOF diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index 5cc7a2f2..13b66977 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1633,8 +1633,7 @@ class QPDF QPDFObjectHandle oh, std::function<int(QPDFObjectHandle&)> skip_stream_parameters, std::set<QPDFObjGen>& visited, - bool top, - int depth); + bool top); void filterCompressedObjects(std::map<int, int> const& object_stream_data); // JSON import diff --git a/include/qpdf/QPDFJob.hh b/include/qpdf/QPDFJob.hh index 9bf81fcc..2a82d61c 100644 --- a/include/qpdf/QPDFJob.hh +++ b/include/qpdf/QPDFJob.hh @@ -397,6 +397,21 @@ class QPDFJob QPDF_DLL void run(); + // The following two methods allow a job to be run in two stages - creation + // of a QPDF object and writing of the QPDF object. This allows the QPDF + // object to be modified prior to writing it out. See + // examples/qpdfjob-remove-annotations for an illustration of its use. + + // Run the first stage of the job. Return a nullptr if the configuration is + // not valid. + QPDF_DLL + std::unique_ptr<QPDF> createQPDF(); + + // Run the second stage of the job. Do nothing if a nullptr is passed as + // parameter. + QPDF_DLL + void writeQPDF(QPDF& qpdf); + // CHECK STATUS -- these methods provide information known after // run() is called. @@ -474,7 +489,7 @@ class QPDFJob std::string to_nr; std::string from_nr; std::string repeat_nr; - std::shared_ptr<QPDF> pdf; + std::unique_ptr<QPDF> pdf; std::vector<int> to_pagenos; std::vector<int> from_pagenos; std::vector<int> repeat_pagenos; @@ -490,25 +505,25 @@ class QPDFJob // Basic file processing void processFile( - std::shared_ptr<QPDF>&, + std::unique_ptr<QPDF>&, char const* filename, char const* password, bool used_for_input, bool main_input); void processInputSource( - std::shared_ptr<QPDF>&, + std::unique_ptr<QPDF>&, std::shared_ptr<InputSource> is, char const* password, bool used_for_input); void doProcess( - std::shared_ptr<QPDF>&, + std::unique_ptr<QPDF>&, std::function<void(QPDF*, char const*)> fn, char const* password, bool empty, bool used_for_input, bool main_input); void doProcessOnce( - std::shared_ptr<QPDF>&, + std::unique_ptr<QPDF>&, std::function<void(QPDF*, char const*)> fn, char const* password, bool empty, @@ -518,7 +533,7 @@ class QPDFJob // Transformations void setQPDFOptions(QPDF& pdf); void - handlePageSpecs(QPDF& pdf, std::vector<std::shared_ptr<QPDF>>& page_heap); + handlePageSpecs(QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_heap); bool shouldRemoveUnreferencedResources(QPDF& pdf); void handleRotations(QPDF& pdf); void diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 464e20a0..c2896209 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -709,70 +709,71 @@ class QPDFWriter Members(Members const&) = delete; QPDF& pdf; - char const* filename; - FILE* file; - bool close_file; - Pl_Buffer* buffer_pipeline; - Buffer* output_buffer; - bool normalize_content_set; - bool normalize_content; - bool compress_streams; - bool compress_streams_set; - qpdf_stream_decode_level_e stream_decode_level; - bool stream_decode_level_set; - bool recompress_flate; - bool qdf_mode; - bool preserve_unreferenced_objects; - bool newline_before_endstream; - bool static_id; - bool suppress_original_object_ids; - bool direct_stream_lengths; - bool encrypted; - bool preserve_encryption; - bool linearized; - bool pclm; - qpdf_object_stream_e object_stream_mode; + QPDFObjGen root_og{-1, 0}; + char const* filename{"unspecified"}; + FILE* file{nullptr}; + bool close_file{false}; + Pl_Buffer* buffer_pipeline{nullptr}; + Buffer* output_buffer{nullptr}; + bool normalize_content_set{false}; + bool normalize_content{false}; + bool compress_streams{true}; + bool compress_streams_set{false}; + qpdf_stream_decode_level_e stream_decode_level{qpdf_dl_none}; + bool stream_decode_level_set{false}; + bool recompress_flate{false}; + bool qdf_mode{false}; + bool preserve_unreferenced_objects{false}; + bool newline_before_endstream{false}; + bool static_id{false}; + bool suppress_original_object_ids{false}; + bool direct_stream_lengths{true}; + bool encrypted{false}; + bool preserve_encryption{true}; + bool linearized{false}; + bool pclm{false}; + qpdf_object_stream_e object_stream_mode{qpdf_o_preserve}; std::string encryption_key; - bool encrypt_metadata; - bool encrypt_use_aes; + bool encrypt_metadata{true}; + bool encrypt_use_aes{false}; std::map<std::string, std::string> encryption_dictionary; - int encryption_V; - int encryption_R; + int encryption_V{0}; + int encryption_R{0}; std::string id1; // for /ID key of std::string id2; // trailer dictionary std::string final_pdf_version; - int final_extension_level; + int final_extension_level{0}; std::string min_pdf_version; - int min_extension_level; + int min_extension_level{0}; std::string forced_pdf_version; - int forced_extension_level; + int forced_extension_level{0}; std::string extra_header_text; - int encryption_dict_objid; + int encryption_dict_objid{0}; std::string cur_data_key; std::list<std::shared_ptr<Pipeline>> to_delete; - Pl_Count* pipeline; + Pl_Count* pipeline{nullptr}; std::vector<QPDFObjectHandle> object_queue; size_t object_queue_front{0}; std::map<QPDFObjGen, int> obj_renumber; std::map<int, QPDFXRefEntry> xref; std::map<int, qpdf_offset_t> lengths; - int next_objid; - int cur_stream_length_id; - size_t cur_stream_length; - bool added_newline; - int max_ostream_index; + int next_objid{1}; + int cur_stream_length_id{0}; + size_t cur_stream_length{0}; + bool added_newline{false}; + int max_ostream_index{0}; std::set<QPDFObjGen> normalized_streams; std::map<QPDFObjGen, int> page_object_to_seq; std::map<QPDFObjGen, int> contents_to_page_seq; std::map<QPDFObjGen, int> object_to_object_stream; std::map<int, std::set<QPDFObjGen>> object_stream_to_objects; std::list<Pipeline*> pipeline_stack; - unsigned long long next_stack_id; - bool deterministic_id; - Pl_MD5* md5_pipeline; + unsigned long long next_stack_id{0}; + bool deterministic_id{false}; + Pl_MD5* md5_pipeline{nullptr}; std::string deterministic_id_data; - bool did_write_setup; + bool did_write_setup{false}; // For linearization only std::string lin_pass1_filename; @@ -781,9 +782,9 @@ class QPDFWriter // For progress reporting std::shared_ptr<ProgressReporter> progress_reporter; - int events_expected; - int events_seen; - int next_progress_report; + int events_expected{0}; + int events_seen{0}; + int next_progress_report{0}; }; // Keep all member variables inside the Members object, which we diff --git a/libqpdf/QPDFJob.cc b/libqpdf/QPDFJob.cc index 87ec32b9..b57a791b 100644 --- a/libqpdf/QPDFJob.cc +++ b/libqpdf/QPDFJob.cc @@ -447,11 +447,11 @@ QPDFJob::parseNumrange(char const* range, int max) return std::vector<int>(); } -void -QPDFJob::run() +std::unique_ptr<QPDF> +QPDFJob::createQPDF() { checkConfiguration(); - std::shared_ptr<QPDF> pdf_sp; + std::unique_ptr<QPDF> pdf_sp; try { processFile(pdf_sp, m->infilename.get(), m->password.get(), true, true); } catch (QPDFExc& e) { @@ -461,12 +461,12 @@ QPDFJob::run() if (m->check_is_encrypted || m->check_requires_password) { this->m->encryption_status = qpdf_es_encrypted | qpdf_es_password_incorrect; - return; + return nullptr; } if (m->show_encryption && pdf_sp) { this->m->log->info("Incorrect password supplied\n"); showEncryption(*pdf_sp); - return; + return nullptr; } } throw e; @@ -477,7 +477,7 @@ QPDFJob::run() } if (m->check_is_encrypted || m->check_requires_password) { - return; + return nullptr; } // If we are updating from JSON, this has to be done first before @@ -486,7 +486,7 @@ QPDFJob::run() pdf.updateFromJSON(this->m->update_from_json); } - std::vector<std::shared_ptr<QPDF>> page_heap; + std::vector<std::unique_ptr<QPDF>> page_heap; if (!m->page_specs.empty()) { handlePageSpecs(pdf, page_heap); } @@ -495,7 +495,12 @@ QPDFJob::run() } handleUnderOverlay(pdf); handleTransformations(pdf); + return pdf_sp; +} +void +QPDFJob::writeQPDF(QPDF& pdf) +{ if (!createsOutput()) { doInspection(pdf); } else if (m->split_pages) { @@ -527,6 +532,15 @@ QPDFJob::run() } } +void +QPDFJob::run() +{ + auto pdf = createQPDF(); + if (pdf) { + writeQPDF(*pdf); + } +} + bool QPDFJob::hasWarnings() const { @@ -1868,14 +1882,14 @@ QPDFJob::doInspection(QPDF& pdf) void QPDFJob::doProcessOnce( - std::shared_ptr<QPDF>& pdf, + std::unique_ptr<QPDF>& pdf, std::function<void(QPDF*, char const*)> fn, char const* password, bool empty, bool used_for_input, bool main_input) { - pdf = QPDF::create(); + pdf = std::make_unique<QPDF>(); setQPDFOptions(*pdf); if (empty) { pdf->emptyPDF(); @@ -1892,7 +1906,7 @@ QPDFJob::doProcessOnce( void QPDFJob::doProcess( - std::shared_ptr<QPDF>& pdf, + std::unique_ptr<QPDF>& pdf, std::function<void(QPDF*, char const*)> fn, char const* password, bool empty, @@ -1976,7 +1990,7 @@ QPDFJob::doProcess( void QPDFJob::processFile( - std::shared_ptr<QPDF>& pdf, + std::unique_ptr<QPDF>& pdf, char const* filename, char const* password, bool used_for_input, @@ -1996,7 +2010,7 @@ QPDFJob::processFile( void QPDFJob::processInputSource( - std::shared_ptr<QPDF>& pdf, + std::unique_ptr<QPDF>& pdf, std::shared_ptr<InputSource> is, char const* password, bool used_for_input) @@ -2278,7 +2292,7 @@ QPDFJob::copyAttachments(QPDF& pdf) v << prefix << ": copying attachments from " << to_copy.path << "\n"; }); - std::shared_ptr<QPDF> other; + std::unique_ptr<QPDF> other; processFile( other, to_copy.path.c_str(), @@ -2540,7 +2554,7 @@ added_page(QPDF& pdf, QPDFPageObjectHelper page) void QPDFJob::handlePageSpecs( - QPDF& pdf, std::vector<std::shared_ptr<QPDF>>& page_heap) + QPDF& pdf, std::vector<std::unique_ptr<QPDF>>& page_heap) { // Parse all page specifications and translate them into lists of // actual pages. @@ -2612,10 +2626,10 @@ QPDFJob::handlePageSpecs( new FileInputSource(page_spec.filename.c_str()); is = std::shared_ptr<InputSource>(fis); } - std::shared_ptr<QPDF> qpdf_sp; + std::unique_ptr<QPDF> qpdf_sp; processInputSource(qpdf_sp, is, password, true); - page_heap.push_back(qpdf_sp); page_spec_qpdfs[page_spec.filename] = qpdf_sp.get(); + page_heap.push_back(std::move(qpdf_sp)); if (cis) { cis->stayOpen(false); page_spec_cfis[page_spec.filename] = cis; @@ -3116,7 +3130,7 @@ QPDFJob::setWriterOptions(QPDF& pdf, QPDFWriter& w) w.setSuppressOriginalObjectIDs(true); } if (m->copy_encryption) { - std::shared_ptr<QPDF> encryption_pdf; + std::unique_ptr<QPDF> encryption_pdf; processFile( encryption_pdf, m->encryption_file.c_str(), diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 907cc105..8287412c 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -52,50 +52,9 @@ QPDFWriter::FunctionProgressReporter::reportProgress(int progress) QPDFWriter::Members::Members(QPDF& pdf) : pdf(pdf), - filename("unspecified"), - file(nullptr), - close_file(false), - buffer_pipeline(nullptr), - output_buffer(nullptr), - normalize_content_set(false), - normalize_content(false), - compress_streams(true), - compress_streams_set(false), - stream_decode_level(qpdf_dl_none), - stream_decode_level_set(false), - recompress_flate(false), - qdf_mode(false), - preserve_unreferenced_objects(false), - newline_before_endstream(false), - static_id(false), - suppress_original_object_ids(false), - direct_stream_lengths(true), - encrypted(false), - preserve_encryption(true), - linearized(false), - pclm(false), - object_stream_mode(qpdf_o_preserve), - encrypt_metadata(true), - encrypt_use_aes(false), - encryption_V(0), - encryption_R(0), - final_extension_level(0), - min_extension_level(0), - forced_extension_level(0), - encryption_dict_objid(0), - pipeline(nullptr), - next_objid(1), - cur_stream_length_id(0), - cur_stream_length(0), - added_newline(false), - max_ostream_index(0), - next_stack_id(0), - deterministic_id(false), - md5_pipeline(nullptr), - did_write_setup(false), - events_expected(0), - events_seen(0), - next_progress_report(0) + root_og( + pdf.getRoot().getObjGen().isIndirect() ? pdf.getRoot().getObjGen() + : QPDFObjGen(-1, 0)) { } @@ -1534,14 +1493,12 @@ QPDFWriter::unparseObject( // is direct through the ADBE dictionary, so we can modify in // place. - bool is_root = false; + const bool is_root = (old_og == m->root_og); bool have_extensions_other = false; bool have_extensions_adbe = false; QPDFObjectHandle extensions; - if ((old_og.getObj() != 0) && - (old_og == this->m->pdf.getRoot().getObjGen())) { - is_root = true; + if (is_root) { if (object.hasKey("/Extensions") && object.getKey("/Extensions").isDictionary()) { extensions = object.getKey("/Extensions"); @@ -2396,10 +2353,9 @@ QPDFWriter::doWriteSetup() // 8.0.0 has a bug that prevents it from being able to handle // encrypted files with compressed document catalogs, so we // disable them in that case as well. - QPDFObjGen og = this->m->pdf.getRoot().getObjGen(); - if (this->m->object_to_object_stream.count(og)) { + if (m->object_to_object_stream.count(m->root_og)) { QTC::TC("qpdf", "QPDFWriter uncompressing root"); - this->m->object_to_object_stream.erase(og); + this->m->object_to_object_stream.erase(m->root_og); } } diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 1fe0b74f..41204fbd 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -285,7 +285,7 @@ QPDF::updateObjectMaps( std::function<int(QPDFObjectHandle&)> skip_stream_parameters) { std::set<QPDFObjGen> visited; - updateObjectMapsInternal(ou, oh, skip_stream_parameters, visited, true, 0); + updateObjectMapsInternal(ou, oh, skip_stream_parameters, visited, true); } void @@ -294,8 +294,7 @@ QPDF::updateObjectMapsInternal( QPDFObjectHandle oh, std::function<int(QPDFObjectHandle&)> skip_stream_parameters, std::set<QPDFObjGen>& visited, - bool top, - int depth) + bool top) { // Traverse the object tree from this point taking care to avoid // crossing page boundaries. @@ -324,12 +323,7 @@ QPDF::updateObjectMapsInternal( int n = oh.getArrayNItems(); for (int i = 0; i < n; ++i) { updateObjectMapsInternal( - ou, - oh.getArrayItem(i), - skip_stream_parameters, - visited, - false, - 1 + depth); + ou, oh.getArrayItem(i), skip_stream_parameters, visited, false); } } else if (oh.isDictionary() || oh.isStream()) { QPDFObjectHandle dict = oh; @@ -351,8 +345,7 @@ QPDF::updateObjectMapsInternal( dict.getKey(key), skip_stream_parameters, visited, - false, - 1 + depth); + false); } else if (is_page_node && (key == "/Parent")) { // Don't traverse back up the page tree } else if ( @@ -367,8 +360,7 @@ QPDF::updateObjectMapsInternal( dict.getKey(key), skip_stream_parameters, visited, - false, - 1 + depth); + false); } } } |