summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/CMakeLists.txt1
-rw-r--r--examples/qpdfjob-remove-annotations.cc78
-rw-r--r--examples/qtest/qpdfjob-remove-annotations.test28
-rw-r--r--examples/qtest/qpdfjob-remove-annotations/annotations-out.pdfbin0 -> 5284 bytes
-rw-r--r--examples/qtest/qpdfjob-remove-annotations/annotations.pdf942
-rw-r--r--include/qpdf/QPDF.hh3
-rw-r--r--include/qpdf/QPDFJob.hh27
-rw-r--r--include/qpdf/QPDFWriter.hh89
-rw-r--r--libqpdf/QPDFJob.cc48
-rw-r--r--libqpdf/QPDFWriter.cc58
-rw-r--r--libqpdf/QPDF_optimization.cc18
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
new file mode 100644
index 00000000..88056506
--- /dev/null
+++ b/examples/qtest/qpdfjob-remove-annotations/annotations-out.pdf
Binary files differ
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);
}
}
}