aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-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/DLL.h6
-rw-r--r--include/qpdf/QPDF.hh3
-rw-r--r--include/qpdf/QPDFJob.hh27
-rw-r--r--include/qpdf/QPDFWriter.hh89
-rw-r--r--include/qpdf/QUtil.hh5
-rw-r--r--libqpdf/QPDFJob.cc48
-rw-r--r--libqpdf/QPDFJob_config.cc6
-rw-r--r--libqpdf/QPDFWriter.cc58
-rw-r--r--libqpdf/QPDF_optimization.cc18
-rw-r--r--libqpdf/QUtil.cc31
-rw-r--r--libtests/qtest/qutil/qutil.out1
-rw-r--r--libtests/qutil.cc6
-rw-r--r--manual/conf.py2
-rw-r--r--qpdf/fix-qdf.cc107
20 files changed, 1272 insertions, 186 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 655ae174..d2ec8ffd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.16)
# the project line. When updating the version, check make_dist for all
# the places it has to be updated.
project(qpdf
- VERSION 11.3.1
+ VERSION 11.4.0
LANGUAGES C CXX)
# Enable correct rpath handling for MacOSX
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/DLL.h b/include/qpdf/DLL.h
index f6e17740..667a0cf5 100644
--- a/include/qpdf/DLL.h
+++ b/include/qpdf/DLL.h
@@ -25,9 +25,9 @@
/* The first version of qpdf to include the version constants is 10.6.0. */
#define QPDF_MAJOR_VERSION 11
-#define QPDF_MINOR_VERSION 3
-#define QPDF_PATCH_VERSION 1
-#define QPDF_VERSION "11.3.1"
+#define QPDF_MINOR_VERSION 4
+#define QPDF_PATCH_VERSION 0
+#define QPDF_VERSION "11.4.0"
/*
* This file defines symbols that control the which functions,
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/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh
index c2ad8448..b42fe195 100644
--- a/include/qpdf/QUtil.hh
+++ b/include/qpdf/QUtil.hh
@@ -485,6 +485,11 @@ namespace QUtil
void read_file_into_memory(
char const* filename, std::shared_ptr<char>& file_buf, size_t& size);
+ QPDF_DLL
+ std::string read_file_into_string(char const* filename);
+ QPDF_DLL
+ std::string read_file_into_string(FILE* f, std::string_view filename = "");
+
// This used to be called strcasecmp, but that is a macro on some
// platforms, so we have to give it a name that is not likely to
// be a macro anywhere.
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/QPDFJob_config.cc b/libqpdf/QPDFJob_config.cc
index 3a6bcdf4..a7f22443 100644
--- a/libqpdf/QPDFJob_config.cc
+++ b/libqpdf/QPDFJob_config.cc
@@ -760,11 +760,9 @@ QPDFJob::Config::showObject(std::string const& parameter)
QPDFJob::Config*
QPDFJob::Config::jobJsonFile(std::string const& parameter)
{
- std::shared_ptr<char> file_buf;
- size_t size;
- QUtil::read_file_into_memory(parameter.c_str(), file_buf, size);
try {
- o.initializeFromJson(std::string(file_buf.get(), size), true);
+ o.initializeFromJson(
+ QUtil::read_file_into_string(parameter.c_str()), true);
} catch (std::exception& e) {
throw std::runtime_error(
"error with job-json file " + std::string(parameter) + ": " +
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);
}
}
}
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index 7f23bd03..bae067b6 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -1243,6 +1243,37 @@ QUtil::read_file_into_memory(
}
}
+std::string
+QUtil::read_file_into_string(char const* filename)
+{
+ FILE* f = safe_fopen(filename, "rb");
+ FileCloser fc(f);
+ return read_file_into_string(f, filename);
+}
+
+std::string
+QUtil::read_file_into_string(FILE* f, std::string_view filename)
+{
+ fseek(f, 0, SEEK_END);
+ auto size = QIntC::to_size(QUtil::tell(f));
+ fseek(f, 0, SEEK_SET);
+ std::string result(size, '\0');
+ if (auto read = fread(result.data(), 1, size, f); read != size) {
+ if (ferror(f)) {
+ throw std::runtime_error(
+ std::string("failure reading file ") + std::string(filename) +
+ " into memory: read " + uint_to_string(read) + "; wanted " +
+ uint_to_string(size));
+ } else {
+ throw std::runtime_error(
+ std::string("premature eof reading file ") +
+ std::string(filename) + " into memory: read " +
+ uint_to_string(read) + "; wanted " + uint_to_string(size));
+ }
+ }
+ return result;
+}
+
static bool
read_char_from_FILE(char& ch, FILE* f)
{
diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out
index 8d3e6d8e..a5f7b108 100644
--- a/libtests/qtest/qutil/qutil.out
+++ b/libtests/qtest/qutil/qutil.out
@@ -116,6 +116,7 @@ This file is used for qutil testing.
It has mixed newlines.
Some lines are very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long.
read 24652 bytes
+read 24652 bytes
---- hex encode/decode
begin hex encode/decode
end hex encode/decode
diff --git a/libtests/qutil.cc b/libtests/qutil.cc
index 972046b9..66264d19 100644
--- a/libtests/qutil.cc
+++ b/libtests/qutil.cc
@@ -576,6 +576,12 @@ read_from_file_test()
auto buf2 = b2.getBufferSharedPointer();
assert(buf2->getSize() == size);
assert(memcmp(buf2->getBuffer(), p, size) == 0);
+
+ auto s = QUtil::read_file_into_string("other-file");
+ std::cout << "read " << s.size() << " bytes" << std::endl;
+ assert(s.size() == 24652);
+ assert(s.substr(0, 36) == "This file is used for qutil testing.");
+ assert(s.substr(24641, 10) == "very long.");
}
void
diff --git a/manual/conf.py b/manual/conf.py
index 3597ebbe..9d3148a3 100644
--- a/manual/conf.py
+++ b/manual/conf.py
@@ -16,7 +16,7 @@ project = 'QPDF'
copyright = '2005-2023, Jay Berkenbilt'
author = 'Jay Berkenbilt'
# make_dist and the CI build lexically find the release version from this file.
-release = '11.3.1'
+release = '11.4.0'
version = release
extensions = [
'sphinx_rtd_theme',
diff --git a/qpdf/fix-qdf.cc b/qpdf/fix-qdf.cc
index 4445fbd1..34b3d3dc 100644
--- a/qpdf/fix-qdf.cc
+++ b/qpdf/fix-qdf.cc
@@ -6,6 +6,7 @@
#include <cstring>
#include <iostream>
#include <regex>
+#include <string_view>
static char const* whoami = 0;
@@ -20,7 +21,7 @@ class QdfFixer
{
public:
QdfFixer(std::string const& filename);
- void processLines(std::list<std::string>& lines);
+ void processLines(std::string const& input);
private:
void fatal(std::string const&);
@@ -58,9 +59,9 @@ class QdfFixer
size_t xref_f1_nbytes;
size_t xref_f2_nbytes;
size_t xref_size;
- std::vector<std::string> ostream;
+ std::vector<std::string_view> ostream;
std::vector<qpdf_offset_t> ostream_offsets;
- std::vector<std::string> ostream_discarded;
+ std::vector<std::string_view> ostream_discarded;
size_t ostream_idx;
int ostream_id;
std::string ostream_extends;
@@ -92,54 +93,71 @@ QdfFixer::fatal(std::string const& msg)
}
void
-QdfFixer::processLines(std::list<std::string>& lines)
+QdfFixer::processLines(std::string const& input)
{
- static std::regex re_n_0_obj("^(\\d+) 0 obj\n$");
- static std::regex re_xref("^xref\n$");
- static std::regex re_stream("^stream\n$");
- static std::regex re_endobj("^endobj\n$");
- static std::regex re_type_objstm("/Type /ObjStm");
- static std::regex re_type_xref("/Type /XRef");
- static std::regex re_extends("/Extends (\\d+ 0 R)");
- static std::regex re_ostream_obj("^%% Object stream: object (\\d+)");
- static std::regex re_endstream("^endstream\n$");
- static std::regex re_length_or_w("/(Length|W) ");
- static std::regex re_size("/Size ");
- static std::regex re_ignore_newline("^%QDF: ignore_newline\n$");
- static std::regex re_num("^\\d+\n$");
- static std::regex re_trailer("^trailer <<");
- static std::regex re_size_n("^ /Size \\d+\n$");
- static std::regex re_dict_end("^>>\n$");
+ using namespace std::literals;
+
+ static const std::regex re_n_0_obj("^(\\d+) 0 obj\n$");
+ static const std::regex re_extends("/Extends (\\d+ 0 R)");
+ static const std::regex re_ostream_obj("^%% Object stream: object (\\d+)");
+ static const std::regex re_num("^\\d+\n$");
+ static const std::regex re_size_n("^ /Size \\d+\n$");
+
+ auto sv_diff = [](size_t i) {
+ return static_cast<std::string_view::difference_type>(i);
+ };
lineno = 0;
- for (auto const& line: lines) {
+ bool more = true;
+ auto len_line = sv_diff(0);
+
+ std::string_view line;
+ std::string_view input_view{input.data(), input.size()};
+ size_t offs = 0;
+
+ auto b_line = input.cbegin();
+ std::smatch m;
+ auto const matches = [&m, &b_line, &len_line](std::regex const& r) {
+ return std::regex_search(b_line, b_line + len_line, m, r);
+ };
+
+ while (more) {
++lineno;
last_offset = offset;
- offset += QIntC::to_offset(line.length());
- std::smatch m;
- auto matches = [&m, &line](std::regex& r) {
- return std::regex_search(line, m, r);
- };
+ b_line += len_line;
+
+ offs = input_view.find('\n');
+ if (offs == std::string::npos) {
+ more = false;
+ line = input_view;
+ } else {
+ offs++;
+ line = input_view.substr(0, offs);
+ input_view.remove_prefix(offs);
+ }
+ len_line = sv_diff(line.size());
+ offset += len_line;
+
if (state == st_top) {
if (matches(re_n_0_obj)) {
checkObjId(m[1].str());
state = st_in_obj;
- } else if (matches(re_xref)) {
+ } else if (line.compare("xref\n"sv) == 0) {
xref_offset = last_offset;
state = st_at_xref;
}
std::cout << line;
} else if (state == st_in_obj) {
std::cout << line;
- if (matches(re_stream)) {
+ if (line.compare("stream\n"sv) == 0) {
state = st_in_stream;
stream_start = offset;
- } else if (matches(re_endobj)) {
+ } else if (line.compare("endobj\n"sv) == 0) {
state = st_top;
- } else if (matches(re_type_objstm)) {
+ } else if (line.find("/Type /ObjStm"sv) != line.npos) {
state = st_in_ostream_dict;
ostream_id = last_obj;
- } else if (matches(re_type_xref)) {
+ } else if (line.find("/Type /XRef"sv) != line.npos) {
xref_offset = xref.back().getOffset();
xref_f1_nbytes = 0;
auto t = xref_offset;
@@ -171,7 +189,7 @@ QdfFixer::processLines(std::list<std::string>& lines)
state = st_in_xref_stream_dict;
}
} else if (state == st_in_ostream_dict) {
- if (matches(re_stream)) {
+ if (line.compare("stream\n"sv) == 0) {
state = st_in_ostream_offsets;
} else {
ostream_discarded.push_back(line);
@@ -200,21 +218,22 @@ QdfFixer::processLines(std::list<std::string>& lines)
if (matches(re_ostream_obj)) {
checkObjId(m[1].str());
state = st_in_ostream_outer;
- } else if (matches(re_endstream)) {
+ } else if (line.compare("endstream\n"sv) == 0) {
stream_length = QIntC::to_size(last_offset - stream_start);
writeOstream();
state = st_in_obj;
}
} else if (state == st_in_xref_stream_dict) {
- if (matches(re_length_or_w)) {
+ if ((line.find("/Length"sv) != line.npos) ||
+ (line.find("/W"sv) != line.npos)) {
// already printed
- } else if (matches(re_size)) {
+ } else if (line.find("/Size"sv) != line.npos) {
auto xref_size = 1 + xref.size();
std::cout << " /Size " << xref_size << "\n";
} else {
std::cout << line;
}
- if (matches(re_stream)) {
+ if (line.compare("stream\n"sv) == 0) {
writeBinary(0, 1);
writeBinary(0, xref_f1_nbytes);
writeBinary(0, xref_f2_nbytes);
@@ -238,13 +257,13 @@ QdfFixer::processLines(std::list<std::string>& lines)
state = st_done;
}
} else if (state == st_in_stream) {
- if (matches(re_endstream)) {
+ if (line.compare("endstream\n"sv) == 0) {
stream_length = QIntC::to_size(last_offset - stream_start);
state = st_after_stream;
}
std::cout << line;
} else if (state == st_after_stream) {
- if (matches(re_ignore_newline)) {
+ if (line.compare("%QDF: ignore_newline\n"sv) == 0) {
if (stream_length > 0) {
--stream_length;
}
@@ -273,7 +292,7 @@ QdfFixer::processLines(std::list<std::string>& lines)
}
state = st_before_trailer;
} else if (state == st_before_trailer) {
- if (matches(re_trailer)) {
+ if (line.compare("trailer <<\n"sv) == 0) {
std::cout << line;
state = st_in_trailer;
}
@@ -284,7 +303,7 @@ QdfFixer::processLines(std::list<std::string>& lines)
} else {
std::cout << line;
}
- if (matches(re_dict_end)) {
+ if (line.compare(">>\n"sv) == 0) {
std::cout << "startxref\n" << xref_offset << "\n%%EOF\n";
state = st_done;
}
@@ -385,17 +404,17 @@ realmain(int argc, char* argv[])
} else if (argc == 2) {
filename = argv[1];
}
- std::list<std::string> lines;
+ std::string input;
if (filename == 0) {
filename = "standard input";
QUtil::binary_stdin();
- lines = QUtil::read_lines_from_file(stdin, true);
+ input = QUtil::read_file_into_string(stdin);
} else {
- lines = QUtil::read_lines_from_file(filename, true);
+ input = QUtil::read_file_into_string(filename);
}
QUtil::binary_stdout();
QdfFixer qf(filename);
- qf.processLines(lines);
+ qf.processLines(input);
return 0;
}