Commit 179636571352e754ec3d05ae28a10bf0a4659c9a

Authored by m-holger
Committed by GitHub
2 parents f6ae1ff1 50d385c8

Merge branch 'main' into mslichao/capifreebuf

.idea/cmake.xml
@@ -2,7 +2,6 @@ @@ -2,7 +2,6 @@
2 <project version="4"> 2 <project version="4">
3 <component name="CMakeSharedSettings"> 3 <component name="CMakeSharedSettings">
4 <configurations> 4 <configurations>
5 - <configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />  
6 <configuration PROFILE_NAME="Maintainer" ENABLED="true" CONFIG_NAME="RelWithDebInfo" GENERATION_OPTIONS="-DMAINTAINER_MODE=ON -DBUILD_STATIC_LIBS=OFF" /> 5 <configuration PROFILE_NAME="Maintainer" ENABLED="true" CONFIG_NAME="RelWithDebInfo" GENERATION_OPTIONS="-DMAINTAINER_MODE=ON -DBUILD_STATIC_LIBS=OFF" />
7 <configuration PROFILE_NAME="Windows" ENABLED="true" CONFIG_NAME="RelWithDebInfo" TOOLCHAIN_NAME="Visual Studio" GENERATION_OPTIONS="-DBUILD_SHARED_LIBS=OFF" /> 6 <configuration PROFILE_NAME="Windows" ENABLED="true" CONFIG_NAME="RelWithDebInfo" TOOLCHAIN_NAME="Visual Studio" GENERATION_OPTIONS="-DBUILD_SHARED_LIBS=OFF" />
8 </configurations> 7 </configurations>
ChangeLog
1 2024-09-20 Chao Li <mslichao@outlook.com> 1 2024-09-20 Chao Li <mslichao@outlook.com>
2 2
3 * Add C API qpdf_oh_free_buffer to release memory allocated by 3 * Add C API qpdf_oh_free_buffer to release memory allocated by
4 - stream data functions 4 + stream data functions.
  5 +
  6 +2024-08-25 M Holger <m.holger@qpdf.org>
  7 +
  8 + * Add new command-line arguments --remove-metadata and --remove-info
  9 + to exclude document metadata and information from the output PDF
  10 + file. Patially fixes #1145.
5 11
6 2024-08-06 M Holger <m.holger@qpdf.org> 12 2024-08-06 M Holger <m.holger@qpdf.org>
7 13
fuzz/CMakeLists.txt
@@ -142,6 +142,10 @@ set(CORPUS_OTHER @@ -142,6 +142,10 @@ set(CORPUS_OTHER
142 70306b.fuzz 142 70306b.fuzz
143 71624.fuzz 143 71624.fuzz
144 71689.fuzz 144 71689.fuzz
  145 + 99999a.fuzz
  146 + 99999b.fuzz
  147 + 99999c.fuzz
  148 + 99999d.fuzz
145 ) 149 )
146 150
147 set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) 151 set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)
fuzz/qpdf_extra/99999a.fuzz 0 → 100644
  1 +%PDF-1.5
  2 +%€€€€
  3 +1 0 obj
  4 +<<
  5 + /Type /Catalog
  6 + /Pages 2 0 R
  7 +>>
  8 +endobj
  9 +2 0 obj
  10 +<<
  11 + /Count 6 Ri
  12 + 0K/ds [3 0 R]
  13 + /Type /Pages
  14 +>>
  15 +endobj
  16 +3 0 obj
  17 +<<
  18 + /Resources <<
  19 + /Font <<
  20 + /F1 5 0 R
  21 + >>
  22 + >>
  23 + /MediaBox [0 0 795 842]
  24 + /Parent 2 0 R
  25 + /Contents 4 0 R
  26 + /Type /Page
  27 +=>
  28 +endobj
  29 +4 0 obj
  30 +<<444444444444444444444444 1 Tr /F1 30 Tf 350 750 Td (foobar) Tj ET
  31 +endstream
  32 +endobj
  33 +5 0 obj
  34 +<<
  35 + /Name /F1
  36 + /BaseFont /Helvetica
  37 + /Type /Font
  38 + /Subtype /Type1
  39 +>>
  40 +e„dobj
  41 +6 0 obj
  42 +<< /Length 6 0 R >>
  43 +stre444444444444444444444444444444<<>>
  44 +endobj
  45 +xref
  46 +0 8
  47 +0000000000 65535 f
  48 +0000000015 00000 n
  49 +0000000066 00000 n
  50 +0000000130 00000 n
  51 +0000000269 00000 n
  52 +0000000362 00000 n
  53 +000000ÎËËÉßÏÏÏ00 n
  54 +0000000500 00000 n
  55 +trailer
  56 +<<
  57 + /Size 713115528178535
  58 + /Root 1 0 R
  59 + /Info 7 0 R
  60 +>>
  61 +startxref
  62 +520
  63 +%%EOF
0 \ No newline at end of file 64 \ No newline at end of file
fuzz/qpdf_extra/99999b.fuzz 0 → 100644
No preview for this file type
fuzz/qpdf_extra/99999c.fuzz 0 → 100644
No preview for this file type
fuzz/qpdf_extra/99999d.fuzz 0 → 100644
No preview for this file type
fuzz/qtest/fuzz.test
@@ -11,7 +11,7 @@ my $td = new TestDriver(&#39;fuzz&#39;); @@ -11,7 +11,7 @@ my $td = new TestDriver(&#39;fuzz&#39;);
11 11
12 my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS"; 12 my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS";
13 13
14 -my $n_qpdf_files = 79; # increment when adding new files 14 +my $n_qpdf_files = 83; # increment when adding new files
15 15
16 my @fuzzers = ( 16 my @fuzzers = (
17 ['ascii85' => 1], 17 ['ascii85' => 1],
include/qpdf/QPDFJob.hh
@@ -692,6 +692,8 @@ class QPDFJob @@ -692,6 +692,8 @@ class QPDFJob
692 bool optimize_images{false}; 692 bool optimize_images{false};
693 bool externalize_inline_images{false}; 693 bool externalize_inline_images{false};
694 bool keep_inline_images{false}; 694 bool keep_inline_images{false};
  695 + bool remove_info{false};
  696 + bool remove_metadata{false};
695 bool remove_page_labels{false}; 697 bool remove_page_labels{false};
696 size_t oi_min_width{DEFAULT_OI_MIN_WIDTH}; 698 size_t oi_min_width{DEFAULT_OI_MIN_WIDTH};
697 size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT}; 699 size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT};
include/qpdf/auto_job_c_main.hh
@@ -32,6 +32,8 @@ QPDF_DLL Config* progress(); @@ -32,6 +32,8 @@ QPDF_DLL Config* progress();
32 QPDF_DLL Config* qdf(); 32 QPDF_DLL Config* qdf();
33 QPDF_DLL Config* rawStreamData(); 33 QPDF_DLL Config* rawStreamData();
34 QPDF_DLL Config* recompressFlate(); 34 QPDF_DLL Config* recompressFlate();
  35 +QPDF_DLL Config* removeInfo();
  36 +QPDF_DLL Config* removeMetadata();
35 QPDF_DLL Config* removePageLabels(); 37 QPDF_DLL Config* removePageLabels();
36 QPDF_DLL Config* reportMemoryUsage(); 38 QPDF_DLL Config* reportMemoryUsage();
37 QPDF_DLL Config* requiresPassword(); 39 QPDF_DLL Config* requiresPassword();
job.sums
@@ -4,17 +4,17 @@ generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a @@ -4,17 +4,17 @@ generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a
4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 5 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
6 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 6 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5
7 -include/qpdf/auto_job_c_main.hh dbfc221d1533120d1aa9c361d8d2483dea5fcb1c0fd95144d98d305e64ed32a6 7 +include/qpdf/auto_job_c_main.hh 84f463237235b2c095b747a4f5dd00f109ee596a1c207b944efb296c0c568cae
8 include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506 8 include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506
9 include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 9 include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62
10 -job.yml 53cad86659db6722e8f415aacb19fc51ab81bb1589c3cb8f65ec893bb4bf5566 10 +job.yml 31935064eca625af7657b23f2f12c614d14751ec0b12702482b1768a04905d22
11 libqpdf/qpdf/auto_job_decl.hh 20d6affe1e260f5a1af4f1d82a820b933835440ff03020e877382da2e8dac6c6 11 libqpdf/qpdf/auto_job_decl.hh 20d6affe1e260f5a1af4f1d82a820b933835440ff03020e877382da2e8dac6c6
12 -libqpdf/qpdf/auto_job_help.hh 74b2982771720927ce7be8f1690720ec65cb9989620493a0c154f50ba2c254e4  
13 -libqpdf/qpdf/auto_job_init.hh 19d1da7c4c0c635bd1c5db8d5f17df8edad3442f8eba006adb075cec295fa158 12 +libqpdf/qpdf/auto_job_help.hh 1e9181f4729a22ff91ab54e2b4a82e6af0c57a8327efb222a4196adb609c1ade
  13 +libqpdf/qpdf/auto_job_init.hh e2a6bb87870c5522a01b15461c9fe909e360f5c7fed06e41acf13a125bd1d03e
14 libqpdf/qpdf/auto_job_json_decl.hh 843892c8e8652a86b7eb573893ef24050b7f36fe313f7251874be5cd4cdbe3fd 14 libqpdf/qpdf/auto_job_json_decl.hh 843892c8e8652a86b7eb573893ef24050b7f36fe313f7251874be5cd4cdbe3fd
15 -libqpdf/qpdf/auto_job_json_init.hh a87256c082427ec0318223762472970b2eced535c0c8b0288d45c8cdaaf62f74  
16 -libqpdf/qpdf/auto_job_schema.hh 5dac568dff39614e161a0af59a0f328f1e28edf69b96f08bb76fd592d51bb053 15 +libqpdf/qpdf/auto_job_json_init.hh 344c2fb473f88fe829c93b1efe6c70a0e4796537b8eb35e421d955fff481ba7d
  16 +libqpdf/qpdf/auto_job_schema.hh 6d3eef5137b8828eaa301a1b3cf75cb7bb812aa6e2d8301de865b42d238d7a7c
17 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 17 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
18 -manual/cli.rst 94057baba9ecffb4ce19ae61c8fa507ef07209c280fccae97b283c3dfce834e0  
19 -manual/qpdf.1 0ec05f1392c160165cdf6adada4de84c0de75bd2fb5762caff4e1372aacada4c 18 +manual/cli.rst b7f37995f13346518ae7b2ea84836fba13b4da4e1f55be5f2a861f20dea0ccdb
  19 +manual/qpdf.1 59c26635017cba5d142ec3fcc4aebcb91e0cf1355d51365db84f48b21585ad8d
20 manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b 20 manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b
@@ -130,6 +130,8 @@ options: @@ -130,6 +130,8 @@ options:
130 - qdf 130 - qdf
131 - raw-stream-data 131 - raw-stream-data
132 - recompress-flate 132 - recompress-flate
  133 + - remove-info
  134 + - remove-metadata
133 - remove-page-labels 135 - remove-page-labels
134 - replace-input 136 - replace-input
135 - report-memory-usage 137 - report-memory-usage
@@ -440,6 +442,8 @@ json: @@ -440,6 +442,8 @@ json:
440 - Pages.file: 442 - Pages.file:
441 Pages.password: 443 Pages.password:
442 range: 444 range:
  445 + remove-info:
  446 + remove-metadata:
443 remove-page-labels: 447 remove-page-labels:
444 report-memory-usage: 448 report-memory-usage:
445 rotate: 449 rotate:
libqpdf/QPDF.cc
@@ -832,10 +832,6 @@ std::vector&lt;QPDF::Xref_table::Subsection&gt; @@ -832,10 +832,6 @@ std::vector&lt;QPDF::Xref_table::Subsection&gt;
832 QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) 832 QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start)
833 { 833 {
834 std::vector<QPDF::Xref_table::Subsection> result; 834 std::vector<QPDF::Xref_table::Subsection> result;
835 - qpdf_offset_t f1 = 0;  
836 - int f2 = 0;  
837 - char type = '\0';  
838 -  
839 file->seek(start, SEEK_SET); 835 file->seek(start, SEEK_SET);
840 836
841 while (true) { 837 while (true) {
@@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string&amp; line, qpdf_offset_t start) @@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string&amp; line, qpdf_offset_t start)
844 auto [obj, num, offset] = result.emplace_back(subsection(line)); 840 auto [obj, num, offset] = result.emplace_back(subsection(line));
845 file->seek(offset, SEEK_SET); 841 file->seek(offset, SEEK_SET);
846 for (qpdf_offset_t i = obj; i - num < obj; ++i) { 842 for (qpdf_offset_t i = obj; i - num < obj; ++i) {
847 - if (!read_entry(f1, f2, type)) { 843 + if (!std::get<0>(read_entry())) {
848 QTC::TC("qpdf", "QPDF invalid xref entry"); 844 QTC::TC("qpdf", "QPDF invalid xref entry");
849 throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); 845 throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")");
850 } 846 }
@@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string&amp; line) @@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string&amp; line)
890 } 886 }
891 } 887 }
892 888
893 -bool  
894 -QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) 889 +// Returns (success, f1, f2, type).
  890 +std::tuple<bool, qpdf_offset_t, int, char>
  891 +QPDF::Xref_table::read_bad_entry()
895 { 892 {
  893 + qpdf_offset_t f1{0};
  894 + int f2{0};
  895 + char type{'\0'};
896 // Reposition after initial read attempt and reread. 896 // Reposition after initial read attempt and reread.
897 file->seek(file->getLastOffset(), SEEK_SET); 897 file->seek(file->getLastOffset(), SEEK_SET);
898 auto line = file->readLine(30); 898 auto line = file->readLine(30);
@@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
910 } 910 }
911 // Require digit 911 // Require digit
912 if (!QUtil::is_digit(*p)) { 912 if (!QUtil::is_digit(*p)) {
913 - return false; 913 + return {false, 0, 0, '\0'};
914 } 914 }
915 // Gather digits 915 // Gather digits
916 std::string f1_str; 916 std::string f1_str;
@@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
919 } 919 }
920 // Require space 920 // Require space
921 if (!QUtil::is_space(*p)) { 921 if (!QUtil::is_space(*p)) {
922 - return false; 922 + return {false, 0, 0, '\0'};
923 } 923 }
924 if (QUtil::is_space(*(p + 1))) { 924 if (QUtil::is_space(*(p + 1))) {
925 QTC::TC("qpdf", "QPDF ignore first extra space in xref entry"); 925 QTC::TC("qpdf", "QPDF ignore first extra space in xref entry");
@@ -931,7 +931,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -931,7 +931,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
931 } 931 }
932 // Require digit 932 // Require digit
933 if (!QUtil::is_digit(*p)) { 933 if (!QUtil::is_digit(*p)) {
934 - return false; 934 + return {false, 0, 0, '\0'};
935 } 935 }
936 // Gather digits 936 // Gather digits
937 std::string f2_str; 937 std::string f2_str;
@@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
940 } 940 }
941 // Require space 941 // Require space
942 if (!QUtil::is_space(*p)) { 942 if (!QUtil::is_space(*p)) {
943 - return false; 943 + return {false, 0, 0, '\0'};
944 } 944 }
945 if (QUtil::is_space(*(p + 1))) { 945 if (QUtil::is_space(*(p + 1))) {
946 QTC::TC("qpdf", "QPDF ignore second extra space in xref entry"); 946 QTC::TC("qpdf", "QPDF ignore second extra space in xref entry");
@@ -953,7 +953,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -953,7 +953,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
953 if ((*p == 'f') || (*p == 'n')) { 953 if ((*p == 'f') || (*p == 'n')) {
954 type = *p; 954 type = *p;
955 } else { 955 } else {
956 - return false; 956 + return {false, 0, 0, '\0'};
957 } 957 }
958 if ((f1_str.length() != 10) || (f2_str.length() != 5)) { 958 if ((f1_str.length() != 10) || (f2_str.length() != 5)) {
959 QTC::TC("qpdf", "QPDF ignore length error xref entry"); 959 QTC::TC("qpdf", "QPDF ignore length error xref entry");
@@ -967,18 +967,23 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -967,18 +967,23 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
967 f1 = QUtil::string_to_ll(f1_str.c_str()); 967 f1 = QUtil::string_to_ll(f1_str.c_str());
968 f2 = QUtil::string_to_int(f2_str.c_str()); 968 f2 = QUtil::string_to_int(f2_str.c_str());
969 969
970 - return true; 970 + return {true, f1, f2, type};
971 } 971 }
972 972
973 // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return 973 // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return
974 -// result.  
975 -bool  
976 -QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) 974 +// result. Returns (success, f1, f2, type).
  975 +std::tuple<bool, qpdf_offset_t, int, char>
  976 +QPDF::Xref_table::read_entry()
977 { 977 {
  978 + qpdf_offset_t f1{0};
  979 + int f2{0};
  980 + char type{'\0'};
978 std::array<char, 21> line; 981 std::array<char, 21> line;
  982 + f1 = 0;
  983 + f2 = 0;
979 if (file->read(line.data(), 20) != 20) { 984 if (file->read(line.data(), 20) != 20) {
980 // C++20: [[unlikely]] 985 // C++20: [[unlikely]]
981 - return false; 986 + return {false, 0, 0, '\0'};
982 } 987 }
983 line[20] = '\0'; 988 line[20] = '\0';
984 char const* p = line.data(); 989 char const* p = line.data();
@@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
1002 if (!QUtil::is_space(*p++)) { 1007 if (!QUtil::is_space(*p++)) {
1003 // Entry doesn't start with space or digit. 1008 // Entry doesn't start with space or digit.
1004 // C++20: [[unlikely]] 1009 // C++20: [[unlikely]]
1005 - return false; 1010 + return {false, 0, 0, '\0'};
1006 } 1011 }
1007 // Gather digits. NB No risk of overflow as 99'999 < max int. 1012 // Gather digits. NB No risk of overflow as 99'999 < max int.
1008 while (*p == '0') { 1013 while (*p == '0') {
@@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type) @@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t&amp; f1, int&amp; f2, char&amp; type)
1019 // No test for valid line[19]. 1024 // No test for valid line[19].
1020 if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) { 1025 if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) {
1021 // C++20: [[likely]] 1026 // C++20: [[likely]]
1022 - return true; 1027 + return {true, f1, f2, type};
1023 } 1028 }
1024 } 1029 }
1025 - return read_bad_entry(f1, f2, type); 1030 + return read_bad_entry();
1026 } 1031 }
1027 1032
1028 // Read a single cross-reference table section and associated trailer. 1033 // Read a single cross-reference table section and associated trailer.
@@ -1052,7 +1057,10 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) @@ -1052,7 +1057,10 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset)
1052 QTC::TC("qpdf", "QPDF trailer size not integer"); 1057 QTC::TC("qpdf", "QPDF trailer size not integer");
1053 throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); 1058 throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer");
1054 } 1059 }
1055 - 1060 + if (sz >= static_cast<unsigned int>(max_id_)) {
  1061 + QTC::TC("qpdf", "QPDF trailer size impossibly large");
  1062 + throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is impossibly large");
  1063 + }
1056 table.resize(sz); 1064 table.resize(sz);
1057 } 1065 }
1058 1066
@@ -1064,10 +1072,8 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset) @@ -1064,10 +1072,8 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset)
1064 first_item_offset_ = file->tell(); 1072 first_item_offset_ = file->tell();
1065 } 1073 }
1066 // For xref_table, these will always be small enough to be ints 1074 // For xref_table, these will always be small enough to be ints
1067 - qpdf_offset_t f1 = 0;  
1068 - int f2 = 0;  
1069 - char type = '\0';  
1070 - if (!read_entry(f1, f2, type)) { 1075 + auto [success, f1, f2, type] = read_entry();
  1076 + if (!success) {
1071 throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); 1077 throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")");
1072 } 1078 }
1073 if (type == 'f') { 1079 if (type == 'f') {
@@ -1585,8 +1591,7 @@ QPDF::Xref_table::read_trailer() @@ -1585,8 +1591,7 @@ QPDF::Xref_table::read_trailer()
1585 { 1591 {
1586 qpdf_offset_t offset = file->tell(); 1592 qpdf_offset_t offset = file->tell();
1587 bool empty = false; 1593 bool empty = false;
1588 - auto object =  
1589 - QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false); 1594 + auto object = QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false);
1590 if (empty) { 1595 if (empty) {
1591 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in 1596 // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
1592 // actual PDF files and Adobe Reader appears to ignore them. 1597 // actual PDF files and Adobe Reader appears to ignore them.
libqpdf/QPDFJob.cc
@@ -471,6 +471,21 @@ QPDFJob::createQPDF() @@ -471,6 +471,21 @@ QPDFJob::createQPDF()
471 } 471 }
472 handleUnderOverlay(pdf); 472 handleUnderOverlay(pdf);
473 handleTransformations(pdf); 473 handleTransformations(pdf);
  474 + if (m->remove_info) {
  475 + auto trailer = pdf.getTrailer();
  476 + auto mod_date = trailer.getKey("/Info").getKeyIfDict("/ModDate");
  477 + if (mod_date.isNull()) {
  478 + trailer.removeKey("/Info");
  479 + } else {
  480 + auto info = trailer.replaceKeyAndGetNew(
  481 + "/Info", pdf.makeIndirectObject(QPDFObjectHandle::newDictionary()));
  482 + info.replaceKey("/ModDate", mod_date);
  483 + }
  484 + pdf.getRoot().removeKey("/Metadata");
  485 + }
  486 + if (m->remove_metadata) {
  487 + pdf.getRoot().removeKey("/Metadata");
  488 + }
474 489
475 for (auto& foreign: page_heap) { 490 for (auto& foreign: page_heap) {
476 if (foreign->anyWarnings()) { 491 if (foreign->anyWarnings()) {
libqpdf/QPDFJob_config.cc
@@ -511,6 +511,20 @@ QPDFJob::Config::removeAttachment(std::string const&amp; parameter) @@ -511,6 +511,20 @@ QPDFJob::Config::removeAttachment(std::string const&amp; parameter)
511 } 511 }
512 512
513 QPDFJob::Config* 513 QPDFJob::Config*
  514 +QPDFJob::Config::removeInfo()
  515 +{
  516 + o.m->remove_info = true;
  517 + return this;
  518 +}
  519 +
  520 +QPDFJob::Config*
  521 +QPDFJob::Config::removeMetadata()
  522 +{
  523 + o.m->remove_metadata = true;
  524 + return this;
  525 +}
  526 +
  527 +QPDFJob::Config*
514 QPDFJob::Config::removePageLabels() 528 QPDFJob::Config::removePageLabels()
515 { 529 {
516 o.m->remove_page_labels = true; 530 o.m->remove_page_labels = true;
libqpdf/QPDFParser.cc
@@ -469,13 +469,14 @@ QPDFParser::fixMissingKeys() @@ -469,13 +469,14 @@ QPDFParser::fixMissingKeys()
469 bool 469 bool
470 QPDFParser::tooManyBadTokens() 470 QPDFParser::tooManyBadTokens()
471 { 471 {
472 - if (good_count <= 4) {  
473 - if (++bad_count > 5) {  
474 - warn("too many errors; giving up on reading object");  
475 - return true;  
476 - }  
477 - } else { 472 + if (--max_bad_count > 0 && good_count > 4) {
  473 + good_count = 0;
478 bad_count = 1; 474 bad_count = 1;
  475 + return false;
  476 + }
  477 + if (++bad_count > 5) {
  478 + warn("too many errors; giving up on reading object");
  479 + return true;
479 } 480 }
480 good_count = 0; 481 good_count = 0;
481 return false; 482 return false;
libqpdf/QPDFTokenizer.cc
@@ -47,7 +47,7 @@ QPDFWordTokenFinder::check() @@ -47,7 +47,7 @@ QPDFWordTokenFinder::check()
47 // Find a word token matching the given string, preceded by a delimiter, and followed by a 47 // Find a word token matching the given string, preceded by a delimiter, and followed by a
48 // delimiter or EOF. 48 // delimiter or EOF.
49 QPDFTokenizer tokenizer; 49 QPDFTokenizer tokenizer;
50 - QPDFTokenizer::Token t = tokenizer.readToken(is, "finder", true); 50 + QPDFTokenizer::Token t = tokenizer.readToken(is, "finder", true, str.size() + 2);
51 qpdf_offset_t pos = is.tell(); 51 qpdf_offset_t pos = is.tell();
52 if (!(t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, str))) { 52 if (!(t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, str))) {
53 QTC::TC("qpdf", "QPDFTokenizer finder found wrong word"); 53 QTC::TC("qpdf", "QPDFTokenizer finder found wrong word");
libqpdf/qpdf/QPDFParser.hh
@@ -83,9 +83,11 @@ class QPDFParser @@ -83,9 +83,11 @@ class QPDFParser
83 std::vector<StackFrame> stack; 83 std::vector<StackFrame> stack;
84 StackFrame* frame; 84 StackFrame* frame;
85 // Number of recent bad tokens. 85 // Number of recent bad tokens.
86 - int bad_count = 0; 86 + int bad_count{0};
  87 + // Number of bad tokens (remaining) before giving up.
  88 + int max_bad_count{15};
87 // Number of good tokens since last bad token. Irrelevant if bad_count == 0. 89 // Number of good tokens since last bad token. Irrelevant if bad_count == 0.
88 - int good_count = 0; 90 + int good_count{0};
89 // Start offset including any leading whitespace. 91 // Start offset including any leading whitespace.
90 qpdf_offset_t start; 92 qpdf_offset_t start;
91 // Number of successive integer tokens. 93 // Number of successive integer tokens.
libqpdf/qpdf/QPDF_private.hh
@@ -292,8 +292,8 @@ class QPDF::Xref_table @@ -292,8 +292,8 @@ class QPDF::Xref_table
292 std::vector<Subsection> subsections(std::string& line); 292 std::vector<Subsection> subsections(std::string& line);
293 std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); 293 std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset);
294 Subsection subsection(std::string const& line); 294 Subsection subsection(std::string const& line);
295 - bool read_entry(qpdf_offset_t& f1, int& f2, char& type);  
296 - bool read_bad_entry(qpdf_offset_t& f1, int& f2, char& type); 295 + std::tuple<bool, qpdf_offset_t, int, char> read_entry();
  296 + std::tuple<bool, qpdf_offset_t, int, char> read_bad_entry();
297 297
298 // Methods to parse streams 298 // Methods to parse streams
299 qpdf_offset_t read_stream(qpdf_offset_t offset); 299 qpdf_offset_t read_stream(qpdf_offset_t offset);
libqpdf/qpdf/auto_job_help.hh
@@ -414,6 +414,13 @@ Don&#39;t optimize images whose area in pixels is below the specified value. @@ -414,6 +414,13 @@ Don&#39;t optimize images whose area in pixels is below the specified value.
414 )"); 414 )");
415 ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images from optimization", R"(Prevent inline images from being considered by --optimize-images. 415 ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images from optimization", R"(Prevent inline images from being considered by --optimize-images.
416 )"); 416 )");
  417 +ap.addOptionHelp("--remove-info", "modification", "remove file information", R"(Exclude file information (except modification date) from the output file.
  418 +)");
  419 +ap.addOptionHelp("--remove-metadata", "modification", "remove metadata", R"(Exclude metadata from the output file.
  420 +)");
  421 +}
  422 +static void add_help_5(QPDFArgParser& ap)
  423 +{
417 ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file. 424 ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file.
418 )"); 425 )");
419 ap.addOptionHelp("--set-page-labels", "modification", "number pages for the entire document", R"(--set-page-labels label-spec ... -- 426 ap.addOptionHelp("--set-page-labels", "modification", "number pages for the entire document", R"(--set-page-labels label-spec ... --
@@ -460,9 +467,6 @@ iv, then the remaining pages with Arabic numerals starting with @@ -460,9 +467,6 @@ iv, then the remaining pages with Arabic numerals starting with
460 1 and continuing sequentially until the end of the document. For 467 1 and continuing sequentially until the end of the document. For
461 additional examples, please consult the manual. 468 additional examples, please consult the manual.
462 )"); 469 )");
463 -}  
464 -static void add_help_5(QPDFArgParser& ap)  
465 -{  
466 ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage: 470 ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage:
467 471
468 --encrypt \ 472 --encrypt \
@@ -641,6 +645,9 @@ ap.addOptionHelp(&quot;--force-R5&quot;, &quot;encryption&quot;, &quot;use unsupported R=5 encryption&quot;, R @@ -641,6 +645,9 @@ ap.addOptionHelp(&quot;--force-R5&quot;, &quot;encryption&quot;, &quot;use unsupported R=5 encryption&quot;, R
641 algorithm that existed only in Acrobat version IX. This option 645 algorithm that existed only in Acrobat version IX. This option
642 should not be used except for compatibility testing. 646 should not be used except for compatibility testing.
643 )"); 647 )");
  648 +}
  649 +static void add_help_6(QPDFArgParser& ap)
  650 +{
644 ap.addHelpTopic("page-selection", "select pages from one or more files", R"(Use the --pages option to select pages from multiple files. Usage: 651 ap.addHelpTopic("page-selection", "select pages from one or more files", R"(Use the --pages option to select pages from multiple files. Usage:
645 652
646 qpdf in.pdf --pages --file=input-file \ 653 qpdf in.pdf --pages --file=input-file \
@@ -725,9 +732,6 @@ appearance: first underlays, then the original page, then overlays. @@ -725,9 +732,6 @@ appearance: first underlays, then the original page, then overlays.
725 732
726 Run qpdf --help=page-ranges for help with page ranges. 733 Run qpdf --help=page-ranges for help with page ranges.
727 )"); 734 )");
728 -}  
729 -static void add_help_6(QPDFArgParser& ap)  
730 -{  
731 ap.addOptionHelp("--to", "overlay-underlay", "destination pages for underlay/overlay", R"(--to=page-range 735 ap.addOptionHelp("--to", "overlay-underlay", "destination pages for underlay/overlay", R"(--to=page-range
732 736
733 Specify the range of pages in the primary output to apply 737 Specify the range of pages in the primary output to apply
@@ -829,6 +833,9 @@ its terminating &quot;--&quot;. @@ -829,6 +833,9 @@ its terminating &quot;--&quot;.
829 To copy attachments from a password-protected file, use 833 To copy attachments from a password-protected file, use
830 the --password option after the file name. 834 the --password option after the file name.
831 )"); 835 )");
  836 +}
  837 +static void add_help_7(QPDFArgParser& ap)
  838 +{
832 ap.addOptionHelp("--prefix", "copy-attachments", "key prefix for copying attachments", R"(--prefix=prefix 839 ap.addOptionHelp("--prefix", "copy-attachments", "key prefix for copying attachments", R"(--prefix=prefix
833 840
834 Prepend a prefix to each key; may be needed if there are 841 Prepend a prefix to each key; may be needed if there are
@@ -839,9 +846,6 @@ ap.addHelpTopic(&quot;inspection&quot;, &quot;inspect PDF files&quot;, R&quot;(These options provide tool @@ -839,9 +846,6 @@ ap.addHelpTopic(&quot;inspection&quot;, &quot;inspect PDF files&quot;, R&quot;(These options provide tool
839 the options in this section are specified, no output file may be 846 the options in this section are specified, no output file may be
840 given. 847 given.
841 )"); 848 )");
842 -}  
843 -static void add_help_7(QPDFArgParser& ap)  
844 -{  
845 ap.addOptionHelp("--is-encrypted", "inspection", "silently test whether a file is encrypted", R"(Silently exit with a code indicating the file's encryption status: 849 ap.addOptionHelp("--is-encrypted", "inspection", "silently test whether a file is encrypted", R"(Silently exit with a code indicating the file's encryption status:
846 850
847 0: the file is encrypted 851 0: the file is encrypted
@@ -919,6 +923,9 @@ output as binary data. Get the key with --list-attachments. @@ -919,6 +923,9 @@ output as binary data. Get the key with --list-attachments.
919 ap.addHelpTopic("json", "JSON output for PDF information", R"(Show information about the PDF file in JSON format. Please see the 923 ap.addHelpTopic("json", "JSON output for PDF information", R"(Show information about the PDF file in JSON format. Please see the
920 JSON chapter in the qpdf manual for details. 924 JSON chapter in the qpdf manual for details.
921 )"); 925 )");
  926 +}
  927 +static void add_help_8(QPDFArgParser& ap)
  928 +{
922 ap.addOptionHelp("--json", "json", "show file in JSON format", R"(--json[=version] 929 ap.addOptionHelp("--json", "json", "show file in JSON format", R"(--json[=version]
923 930
924 Generate a JSON representation of the file. This is described in 931 Generate a JSON representation of the file. This is described in
@@ -932,9 +939,6 @@ Describe the format of the JSON output by writing to standard @@ -932,9 +939,6 @@ Describe the format of the JSON output by writing to standard
932 output a JSON object with the same keys and with values 939 output a JSON object with the same keys and with values
933 containing descriptive text. 940 containing descriptive text.
934 )"); 941 )");
935 -}  
936 -static void add_help_8(QPDFArgParser& ap)  
937 -{  
938 ap.addOptionHelp("--json-key", "json", "limit which keys are in JSON output", R"(--json-key=key 942 ap.addOptionHelp("--json-key", "json", "limit which keys are in JSON output", R"(--json-key=key
939 943
940 This option is repeatable. If given, only the specified 944 This option is repeatable. If given, only the specified
libqpdf/qpdf/auto_job_init.hh
@@ -68,6 +68,8 @@ this-&gt;ap.addBare(&quot;progress&quot;, [this](){c_main-&gt;progress();}); @@ -68,6 +68,8 @@ this-&gt;ap.addBare(&quot;progress&quot;, [this](){c_main-&gt;progress();});
68 this->ap.addBare("qdf", [this](){c_main->qdf();}); 68 this->ap.addBare("qdf", [this](){c_main->qdf();});
69 this->ap.addBare("raw-stream-data", [this](){c_main->rawStreamData();}); 69 this->ap.addBare("raw-stream-data", [this](){c_main->rawStreamData();});
70 this->ap.addBare("recompress-flate", [this](){c_main->recompressFlate();}); 70 this->ap.addBare("recompress-flate", [this](){c_main->recompressFlate();});
  71 +this->ap.addBare("remove-info", [this](){c_main->removeInfo();});
  72 +this->ap.addBare("remove-metadata", [this](){c_main->removeMetadata();});
71 this->ap.addBare("remove-page-labels", [this](){c_main->removePageLabels();}); 73 this->ap.addBare("remove-page-labels", [this](){c_main->removePageLabels();});
72 this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput)); 74 this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput));
73 this->ap.addBare("report-memory-usage", [this](){c_main->reportMemoryUsage();}); 75 this->ap.addBare("report-memory-usage", [this](){c_main->reportMemoryUsage();});
libqpdf/qpdf/auto_job_json_init.hh
@@ -412,6 +412,12 @@ addParameter([this](std::string const&amp; p) { c_pages-&gt;range(p); }); @@ -412,6 +412,12 @@ addParameter([this](std::string const&amp; p) { c_pages-&gt;range(p); });
412 popHandler(); // key: range 412 popHandler(); // key: range
413 popHandler(); // array: .pages[] 413 popHandler(); // array: .pages[]
414 popHandler(); // key: pages 414 popHandler(); // key: pages
  415 +pushKey("removeInfo");
  416 +addBare([this]() { c_main->removeInfo(); });
  417 +popHandler(); // key: removeInfo
  418 +pushKey("removeMetadata");
  419 +addBare([this]() { c_main->removeMetadata(); });
  420 +popHandler(); // key: removeMetadata
415 pushKey("removePageLabels"); 421 pushKey("removePageLabels");
416 addBare([this]() { c_main->removePageLabels(); }); 422 addBare([this]() { c_main->removePageLabels(); });
417 popHandler(); // key: removePageLabels 423 popHandler(); // key: removePageLabels
libqpdf/qpdf/auto_job_schema.hh
@@ -145,6 +145,8 @@ static constexpr char const* JOB_SCHEMA_DATA = R&quot;({ @@ -145,6 +145,8 @@ static constexpr char const* JOB_SCHEMA_DATA = R&quot;({
145 "range": "page range" 145 "range": "page range"
146 } 146 }
147 ], 147 ],
  148 + "removeInfo": "remove file information",
  149 + "removeMetadata": "remove metadata",
148 "removePageLabels": "remove explicit page numbers", 150 "removePageLabels": "remove explicit page numbers",
149 "reportMemoryUsage": "best effort report of memory usage", 151 "reportMemoryUsage": "best effort report of memory usage",
150 "rotate": "rotate pages", 152 "rotate": "rotate pages",
manual/cli.rst
@@ -1773,6 +1773,27 @@ Related Options @@ -1773,6 +1773,27 @@ Related Options
1773 Prevent inline images from being included in image optimization 1773 Prevent inline images from being included in image optimization
1774 done by :qpdf:ref:`--optimize-images`. 1774 done by :qpdf:ref:`--optimize-images`.
1775 1775
  1776 +.. qpdf:option:: --remove-info
  1777 +
  1778 + .. help: remove file information
  1779 +
  1780 + Exclude file information (except modification date) from the output file.
  1781 +
  1782 + Exclude file information (except modification date) from the output file by
  1783 + omitting all entries (except ``/ModDate``) from the ``/Info`` dictionary in
  1784 + the document trailer.
  1785 + See also :qpdf:ref:`--remove-metadata`.
  1786 +
  1787 +.. qpdf:option:: --remove-metadata
  1788 +
  1789 + .. help: remove metadata
  1790 +
  1791 + Exclude metadata from the output file.
  1792 +
  1793 + Exclude metadata from the output file by omitting the ``/Metadata``
  1794 + dictionary in the document catalog.
  1795 + See also :qpdf:ref:`--remove-info`.
  1796 +
1776 .. qpdf:option:: --remove-page-labels 1797 .. qpdf:option:: --remove-page-labels
1777 1798
1778 .. help: remove explicit page numbers 1799 .. help: remove explicit page numbers
manual/qpdf.1
@@ -530,6 +530,12 @@ Don&#39;t optimize images whose area in pixels is below the specified value. @@ -530,6 +530,12 @@ Don&#39;t optimize images whose area in pixels is below the specified value.
530 .B --keep-inline-images \-\- exclude inline images from optimization 530 .B --keep-inline-images \-\- exclude inline images from optimization
531 Prevent inline images from being considered by --optimize-images. 531 Prevent inline images from being considered by --optimize-images.
532 .TP 532 .TP
  533 +.B --remove-info \-\- remove file information
  534 +Exclude file information (except modification date) from the output file.
  535 +.TP
  536 +.B --remove-metadata \-\- remove metadata
  537 +Exclude metadata from the output file.
  538 +.TP
533 .B --remove-page-labels \-\- remove explicit page numbers 539 .B --remove-page-labels \-\- remove explicit page numbers
534 Exclude page labels (explicit page numbers) from the output file. 540 Exclude page labels (explicit page numbers) from the output file.
535 .TP 541 .TP
qpdf/qpdf.testcov
@@ -55,6 +55,7 @@ QPDF invalid xref entry 0 @@ -55,6 +55,7 @@ QPDF invalid xref entry 0
55 QPDF missing trailer 0 55 QPDF missing trailer 0
56 QPDF trailer lacks size 0 56 QPDF trailer lacks size 0
57 QPDF trailer size not integer 0 57 QPDF trailer size not integer 0
  58 +QPDF trailer size impossibly large 0
58 QPDF trailer prev not integer 0 59 QPDF trailer prev not integer 0
59 QPDFParser bad brace 0 60 QPDFParser bad brace 0
60 QPDFParser bad brace in parseRemainder 0 61 QPDFParser bad brace in parseRemainder 0
qpdf/qtest/merge-and-split.test
@@ -14,7 +14,7 @@ cleanup(); @@ -14,7 +14,7 @@ cleanup();
14 14
15 my $td = new TestDriver('merge-and-split'); 15 my $td = new TestDriver('merge-and-split');
16 16
17 -my $n_tests = 28; 17 +my $n_tests = 34;
18 18
19 # Select pages from the same file multiple times including selecting 19 # Select pages from the same file multiple times including selecting
20 # twice from an encrypted file and specifying the password only the 20 # twice from an encrypted file and specifying the password only the
@@ -103,6 +103,39 @@ $td-&gt;runtest(&quot;check output&quot;, @@ -103,6 +103,39 @@ $td-&gt;runtest(&quot;check output&quot;,
103 {$td->COMMAND => "qpdf-test-compare a.pdf remove-labels.pdf"}, 103 {$td->COMMAND => "qpdf-test-compare a.pdf remove-labels.pdf"},
104 {$td->FILE => "remove-labels.pdf", $td->EXIT_STATUS => 0}); 104 {$td->FILE => "remove-labels.pdf", $td->EXIT_STATUS => 0});
105 105
  106 +$td->runtest("remove metadata",
  107 + {$td->COMMAND =>
  108 + "qpdf metadata-crypt-filter.pdf a.pdf" .
  109 + " --remove-metadata" .
  110 + " --decrypt" .
  111 + " --static-id"},
  112 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  113 +$td->runtest("check output",
  114 + {$td->FILE => "a.pdf"},
  115 + {$td->FILE => "remove-metadata.pdf"});
  116 +
  117 +$td->runtest("remove info (with moddate)",
  118 + {$td->COMMAND =>
  119 + "qpdf remove-metadata.pdf a.pdf" .
  120 + " --remove-info" .
  121 + " --decrypt" .
  122 + " --static-id"},
  123 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  124 +$td->runtest("check output",
  125 + {$td->FILE => "a.pdf"},
  126 + {$td->FILE => "remove-info.pdf"});
  127 +
  128 +$td->runtest("remove info (without moddate)",
  129 + {$td->COMMAND =>
  130 + "qpdf remove-metadata-no-moddate.pdf a.pdf" .
  131 + " --remove-info" .
  132 + " --decrypt" .
  133 + " --static-id"},
  134 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  135 +$td->runtest("check output",
  136 + {$td->FILE => "a.pdf"},
  137 + {$td->FILE => "remove-info-no-moddate.pdf"});
  138 +
106 $td->runtest("split with shared resources", 139 $td->runtest("split with shared resources",
107 {$td->COMMAND => 140 {$td->COMMAND =>
108 "qpdf --qdf --static-id" . 141 "qpdf --qdf --static-id" .
qpdf/qtest/qpdf/issue-fuzz.out 0 → 100644
  1 +WARNING: issue-fuzz.pdf: can't find PDF header
  2 +WARNING: issue-fuzz.pdf (xref table, offset 19): accepting invalid xref table entry
  3 +WARNING: issue-fuzz.pdf (trailer, offset 36): unknown token while reading object; treating as string
  4 +WARNING: issue-fuzz.pdf (trailer, offset 53): unexpected >
  5 +WARNING: issue-fuzz.pdf (trailer, offset 54): unknown token while reading object; treating as string
  6 +WARNING: issue-fuzz.pdf (trailer, offset 58): unknown token while reading object; treating as string
  7 +WARNING: issue-fuzz.pdf (trailer, offset 72): unknown token while reading object; treating as string
  8 +WARNING: issue-fuzz.pdf (trailer, offset 36): dictionary ended prematurely; using null as value for last key
  9 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake1
  10 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake2
  11 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake3
  12 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake4
  13 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake5
  14 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake6
  15 +WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake7
  16 +WARNING: issue-fuzz.pdf: file is damaged
  17 +WARNING: issue-fuzz.pdf (trailer, offset 32): /Size key in trailer dictionary is impossibly large
  18 +WARNING: issue-fuzz.pdf: Attempting to reconstruct cross-reference table
  19 +qpdf: issue-fuzz.pdf: unable to find /Root dictionary
qpdf/qtest/qpdf/issue-fuzz.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/remove-info-no-moddate.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/remove-info.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/remove-metadata-no-moddate.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/remove-metadata.pdf 0 → 100644
No preview for this file type
qpdf/qtest/specific-bugs.test
@@ -38,6 +38,7 @@ my @bug_tests = ( @@ -38,6 +38,7 @@ my @bug_tests = (
38 ["263", "empty xref stream", 2], 38 ["263", "empty xref stream", 2],
39 ["335a", "ozz-fuzz-12152", 2], 39 ["335a", "ozz-fuzz-12152", 2],
40 ["335b", "ozz-fuzz-14845", 2], 40 ["335b", "ozz-fuzz-14845", 2],
  41 + ["fuzz", "impossibly large trailer /Size"],
41 # ["fuzz-16214", "stream in object stream", 3, "--preserve-unreferenced"], 42 # ["fuzz-16214", "stream in object stream", 3, "--preserve-unreferenced"],
42 # When adding to this list, consider adding to CORPUS_FROM_TEST in 43 # When adding to this list, consider adding to CORPUS_FROM_TEST in
43 # fuzz/CMakeLists.txt and updating the count in 44 # fuzz/CMakeLists.txt and updating the count in