Commit 179636571352e754ec3d05ae28a10bf0a4659c9a
Committed by
GitHub
Merge branch 'main' into mslichao/capifreebuf
Showing
34 changed files
with
274 additions
and
62 deletions
.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('fuzz'); | @@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz'); | ||
| 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 |
job.yml
| @@ -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<QPDF::Xref_table::Subsection> | @@ -832,10 +832,6 @@ std::vector<QPDF::Xref_table::Subsection> | ||
| 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& line, qpdf_offset_t start) | @@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string& 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& line) | @@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string& 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& f1, int& f2, char& type) | @@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -931,7 +931,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -953,7 +953,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -967,18 +967,23 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& 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& f1, int& f2, char& type) | @@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& 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& parameter) | @@ -511,6 +511,20 @@ QPDFJob::Config::removeAttachment(std::string const& 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't optimize images whose area in pixels is below the specified value. | @@ -414,6 +414,13 @@ Don'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("--force-R5", "encryption", "use unsupported R=5 encryption", R | @@ -641,6 +645,9 @@ ap.addOptionHelp("--force-R5", "encryption", "use unsupported R=5 encryption", 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 "--". | @@ -829,6 +833,9 @@ its terminating "--". | ||
| 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("inspection", "inspect PDF files", R"(These options provide tool | @@ -839,9 +846,6 @@ ap.addHelpTopic("inspection", "inspect PDF files", R"(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->ap.addBare("progress", [this](){c_main->progress();}); | @@ -68,6 +68,8 @@ this->ap.addBare("progress", [this](){c_main->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& p) { c_pages->range(p); }); | @@ -412,6 +412,12 @@ addParameter([this](std::string const& p) { c_pages->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"({ | @@ -145,6 +145,8 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({ | ||
| 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't optimize images whose area in pixels is below the specified value. | @@ -530,6 +530,12 @@ Don'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->runtest("check output", | @@ -103,6 +103,39 @@ $td->runtest("check output", | ||
| 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 |