2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-22 10:58:58 +00:00

Merge branch 'main' into mslichao/capifreebuf

This commit is contained in:
m-holger 2024-09-27 11:31:55 +01:00 committed by GitHub
commit 1796365713
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 274 additions and 62 deletions

View File

@ -2,7 +2,6 @@
<project version="4"> <project version="4">
<component name="CMakeSharedSettings"> <component name="CMakeSharedSettings">
<configurations> <configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
<configuration PROFILE_NAME="Maintainer" ENABLED="true" CONFIG_NAME="RelWithDebInfo" GENERATION_OPTIONS="-DMAINTAINER_MODE=ON -DBUILD_STATIC_LIBS=OFF" /> <configuration PROFILE_NAME="Maintainer" ENABLED="true" CONFIG_NAME="RelWithDebInfo" GENERATION_OPTIONS="-DMAINTAINER_MODE=ON -DBUILD_STATIC_LIBS=OFF" />
<configuration PROFILE_NAME="Windows" ENABLED="true" CONFIG_NAME="RelWithDebInfo" TOOLCHAIN_NAME="Visual Studio" GENERATION_OPTIONS="-DBUILD_SHARED_LIBS=OFF" /> <configuration PROFILE_NAME="Windows" ENABLED="true" CONFIG_NAME="RelWithDebInfo" TOOLCHAIN_NAME="Visual Studio" GENERATION_OPTIONS="-DBUILD_SHARED_LIBS=OFF" />
</configurations> </configurations>

View File

@ -1,7 +1,13 @@
2024-09-20 Chao Li <mslichao@outlook.com> 2024-09-20 Chao Li <mslichao@outlook.com>
* Add C API qpdf_oh_free_buffer to release memory allocated by * Add C API qpdf_oh_free_buffer to release memory allocated by
stream data functions stream data functions.
2024-08-25 M Holger <m.holger@qpdf.org>
* Add new command-line arguments --remove-metadata and --remove-info
to exclude document metadata and information from the output PDF
file. Patially fixes #1145.
2024-08-06 M Holger <m.holger@qpdf.org> 2024-08-06 M Holger <m.holger@qpdf.org>

View File

@ -142,6 +142,10 @@ set(CORPUS_OTHER
70306b.fuzz 70306b.fuzz
71624.fuzz 71624.fuzz
71689.fuzz 71689.fuzz
99999a.fuzz
99999b.fuzz
99999c.fuzz
99999d.fuzz
) )
set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus) set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)

View File

@ -0,0 +1,63 @@
%PDF-1.5
%€€€€
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Count 6 Ri
0K/ds [3 0 R]
/Type /Pages
>>
endobj
3 0 obj
<<
/Resources <<
/Font <<
/F1 5 0 R
>>
>>
/MediaBox [0 0 795 842]
/Parent 2 0 R
/Contents 4 0 R
/Type /Page
=>
endobj
4 0 obj
<<444444444444444444444444 1 Tr /F1 30 Tf 350 750 Td (foobar) Tj ET
endstream
endobj
5 0 obj
<<
/Name /F1
/BaseFont /Helvetica
/Type /Font
/Subtype /Type1
>>
e„dobj
6 0 obj
<< /Length 6 0 R >>
stre444444444444444444444444444444<<>>
endobj
xref
0 8
0000000000 65535 f
0000000015 00000 n
0000000066 00000 n
0000000130 00000 n
0000000269 00000 n
0000000362 00000 n
000000ÎËËÉßÏÏÏ00 n
0000000500 00000 n
trailer
<<
/Size 713115528178535
/Root 1 0 R
/Info 7 0 R
>>
startxref
520
%%EOF

BIN
fuzz/qpdf_extra/99999b.fuzz Normal file

Binary file not shown.

BIN
fuzz/qpdf_extra/99999c.fuzz Normal file

Binary file not shown.

BIN
fuzz/qpdf_extra/99999d.fuzz Normal file

Binary file not shown.

View File

@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz');
my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS"; my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS";
my $n_qpdf_files = 79; # increment when adding new files my $n_qpdf_files = 83; # increment when adding new files
my @fuzzers = ( my @fuzzers = (
['ascii85' => 1], ['ascii85' => 1],

View File

@ -692,6 +692,8 @@ class QPDFJob
bool optimize_images{false}; bool optimize_images{false};
bool externalize_inline_images{false}; bool externalize_inline_images{false};
bool keep_inline_images{false}; bool keep_inline_images{false};
bool remove_info{false};
bool remove_metadata{false};
bool remove_page_labels{false}; bool remove_page_labels{false};
size_t oi_min_width{DEFAULT_OI_MIN_WIDTH}; size_t oi_min_width{DEFAULT_OI_MIN_WIDTH};
size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT}; size_t oi_min_height{DEFAULT_OI_MIN_HEIGHT};

View File

@ -32,6 +32,8 @@ QPDF_DLL Config* progress();
QPDF_DLL Config* qdf(); QPDF_DLL Config* qdf();
QPDF_DLL Config* rawStreamData(); QPDF_DLL Config* rawStreamData();
QPDF_DLL Config* recompressFlate(); QPDF_DLL Config* recompressFlate();
QPDF_DLL Config* removeInfo();
QPDF_DLL Config* removeMetadata();
QPDF_DLL Config* removePageLabels(); QPDF_DLL Config* removePageLabels();
QPDF_DLL Config* reportMemoryUsage(); QPDF_DLL Config* reportMemoryUsage();
QPDF_DLL Config* requiresPassword(); QPDF_DLL Config* requiresPassword();

View File

@ -4,17 +4,17 @@ generate_auto_job f64733b79dcee5a0e3e8ccc6976448e8ddf0e8b6529987a66a7d3ab2ebc10a
include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4 include/qpdf/auto_job_c_att.hh 4c2b171ea00531db54720bf49a43f8b34481586ae7fb6cbf225099ee42bc5bb4
include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42 include/qpdf/auto_job_c_copy_att.hh 50609012bff14fd82f0649185940d617d05d530cdc522185c7f3920a561ccb42
include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5 include/qpdf/auto_job_c_enc.hh 28446f3c32153a52afa239ea40503e6cc8ac2c026813526a349e0cd4ae17ddd5
include/qpdf/auto_job_c_main.hh dbfc221d1533120d1aa9c361d8d2483dea5fcb1c0fd95144d98d305e64ed32a6 include/qpdf/auto_job_c_main.hh 84f463237235b2c095b747a4f5dd00f109ee596a1c207b944efb296c0c568cae
include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506 include/qpdf/auto_job_c_pages.hh 09ca15649cc94fdaf6d9bdae28a20723f2a66616bf15aa86d83df31051d82506
include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62 include/qpdf/auto_job_c_uo.hh 9c2f98a355858dd54d0bba444b73177a59c9e56833e02fa6406f429c07f39e62
job.yml 53cad86659db6722e8f415aacb19fc51ab81bb1589c3cb8f65ec893bb4bf5566 job.yml 31935064eca625af7657b23f2f12c614d14751ec0b12702482b1768a04905d22
libqpdf/qpdf/auto_job_decl.hh 20d6affe1e260f5a1af4f1d82a820b933835440ff03020e877382da2e8dac6c6 libqpdf/qpdf/auto_job_decl.hh 20d6affe1e260f5a1af4f1d82a820b933835440ff03020e877382da2e8dac6c6
libqpdf/qpdf/auto_job_help.hh 74b2982771720927ce7be8f1690720ec65cb9989620493a0c154f50ba2c254e4 libqpdf/qpdf/auto_job_help.hh 1e9181f4729a22ff91ab54e2b4a82e6af0c57a8327efb222a4196adb609c1ade
libqpdf/qpdf/auto_job_init.hh 19d1da7c4c0c635bd1c5db8d5f17df8edad3442f8eba006adb075cec295fa158 libqpdf/qpdf/auto_job_init.hh e2a6bb87870c5522a01b15461c9fe909e360f5c7fed06e41acf13a125bd1d03e
libqpdf/qpdf/auto_job_json_decl.hh 843892c8e8652a86b7eb573893ef24050b7f36fe313f7251874be5cd4cdbe3fd libqpdf/qpdf/auto_job_json_decl.hh 843892c8e8652a86b7eb573893ef24050b7f36fe313f7251874be5cd4cdbe3fd
libqpdf/qpdf/auto_job_json_init.hh a87256c082427ec0318223762472970b2eced535c0c8b0288d45c8cdaaf62f74 libqpdf/qpdf/auto_job_json_init.hh 344c2fb473f88fe829c93b1efe6c70a0e4796537b8eb35e421d955fff481ba7d
libqpdf/qpdf/auto_job_schema.hh 5dac568dff39614e161a0af59a0f328f1e28edf69b96f08bb76fd592d51bb053 libqpdf/qpdf/auto_job_schema.hh 6d3eef5137b8828eaa301a1b3cf75cb7bb812aa6e2d8301de865b42d238d7a7c
manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580 manual/_ext/qpdf.py 6add6321666031d55ed4aedf7c00e5662bba856dfcd66ccb526563bffefbb580
manual/cli.rst 94057baba9ecffb4ce19ae61c8fa507ef07209c280fccae97b283c3dfce834e0 manual/cli.rst b7f37995f13346518ae7b2ea84836fba13b4da4e1f55be5f2a861f20dea0ccdb
manual/qpdf.1 0ec05f1392c160165cdf6adada4de84c0de75bd2fb5762caff4e1372aacada4c manual/qpdf.1 59c26635017cba5d142ec3fcc4aebcb91e0cf1355d51365db84f48b21585ad8d
manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b manual/qpdf.1.in 436ecc85d45c4c9e2dbd1725fb7f0177fb627179469f114561adf3cb6cbb677b

View File

@ -130,6 +130,8 @@ options:
- qdf - qdf
- raw-stream-data - raw-stream-data
- recompress-flate - recompress-flate
- remove-info
- remove-metadata
- remove-page-labels - remove-page-labels
- replace-input - replace-input
- report-memory-usage - report-memory-usage
@ -440,6 +442,8 @@ json:
- Pages.file: - Pages.file:
Pages.password: Pages.password:
range: range:
remove-info:
remove-metadata:
remove-page-labels: remove-page-labels:
report-memory-usage: report-memory-usage:
rotate: rotate:

View File

@ -832,10 +832,6 @@ std::vector<QPDF::Xref_table::Subsection>
QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start) QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start)
{ {
std::vector<QPDF::Xref_table::Subsection> result; std::vector<QPDF::Xref_table::Subsection> result;
qpdf_offset_t f1 = 0;
int f2 = 0;
char type = '\0';
file->seek(start, SEEK_SET); file->seek(start, SEEK_SET);
while (true) { while (true) {
@ -844,7 +840,7 @@ QPDF::Xref_table::bad_subsections(std::string& line, qpdf_offset_t start)
auto [obj, num, offset] = result.emplace_back(subsection(line)); auto [obj, num, offset] = result.emplace_back(subsection(line));
file->seek(offset, SEEK_SET); file->seek(offset, SEEK_SET);
for (qpdf_offset_t i = obj; i - num < obj; ++i) { for (qpdf_offset_t i = obj; i - num < obj; ++i) {
if (!read_entry(f1, f2, type)) { if (!std::get<0>(read_entry())) {
QTC::TC("qpdf", "QPDF invalid xref entry"); QTC::TC("qpdf", "QPDF invalid xref entry");
throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")");
} }
@ -890,9 +886,13 @@ QPDF::Xref_table::subsections(std::string& line)
} }
} }
bool // Returns (success, f1, f2, type).
QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type) std::tuple<bool, qpdf_offset_t, int, char>
QPDF::Xref_table::read_bad_entry()
{ {
qpdf_offset_t f1{0};
int f2{0};
char type{'\0'};
// Reposition after initial read attempt and reread. // Reposition after initial read attempt and reread.
file->seek(file->getLastOffset(), SEEK_SET); file->seek(file->getLastOffset(), SEEK_SET);
auto line = file->readLine(30); auto line = file->readLine(30);
@ -910,7 +910,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type)
} }
// Require digit // Require digit
if (!QUtil::is_digit(*p)) { if (!QUtil::is_digit(*p)) {
return false; return {false, 0, 0, '\0'};
} }
// Gather digits // Gather digits
std::string f1_str; std::string f1_str;
@ -919,7 +919,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type)
} }
// Require space // Require space
if (!QUtil::is_space(*p)) { if (!QUtil::is_space(*p)) {
return false; return {false, 0, 0, '\0'};
} }
if (QUtil::is_space(*(p + 1))) { if (QUtil::is_space(*(p + 1))) {
QTC::TC("qpdf", "QPDF ignore first extra space in xref entry"); 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)
} }
// Require digit // Require digit
if (!QUtil::is_digit(*p)) { if (!QUtil::is_digit(*p)) {
return false; return {false, 0, 0, '\0'};
} }
// Gather digits // Gather digits
std::string f2_str; std::string f2_str;
@ -940,7 +940,7 @@ QPDF::Xref_table::read_bad_entry(qpdf_offset_t& f1, int& f2, char& type)
} }
// Require space // Require space
if (!QUtil::is_space(*p)) { if (!QUtil::is_space(*p)) {
return false; return {false, 0, 0, '\0'};
} }
if (QUtil::is_space(*(p + 1))) { if (QUtil::is_space(*(p + 1))) {
QTC::TC("qpdf", "QPDF ignore second extra space in xref entry"); 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)
if ((*p == 'f') || (*p == 'n')) { if ((*p == 'f') || (*p == 'n')) {
type = *p; type = *p;
} else { } else {
return false; return {false, 0, 0, '\0'};
} }
if ((f1_str.length() != 10) || (f2_str.length() != 5)) { if ((f1_str.length() != 10) || (f2_str.length() != 5)) {
QTC::TC("qpdf", "QPDF ignore length error xref entry"); 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)
f1 = QUtil::string_to_ll(f1_str.c_str()); f1 = QUtil::string_to_ll(f1_str.c_str());
f2 = QUtil::string_to_int(f2_str.c_str()); f2 = QUtil::string_to_int(f2_str.c_str());
return true; return {true, f1, f2, type};
} }
// Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return // Optimistically read and parse xref entry. If entry is bad, call read_bad_xrefEntry and return
// result. // result. Returns (success, f1, f2, type).
bool std::tuple<bool, qpdf_offset_t, int, char>
QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type) QPDF::Xref_table::read_entry()
{ {
qpdf_offset_t f1{0};
int f2{0};
char type{'\0'};
std::array<char, 21> line; std::array<char, 21> line;
f1 = 0;
f2 = 0;
if (file->read(line.data(), 20) != 20) { if (file->read(line.data(), 20) != 20) {
// C++20: [[unlikely]] // C++20: [[unlikely]]
return false; return {false, 0, 0, '\0'};
} }
line[20] = '\0'; line[20] = '\0';
char const* p = line.data(); char const* p = line.data();
@ -1002,7 +1007,7 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type)
if (!QUtil::is_space(*p++)) { if (!QUtil::is_space(*p++)) {
// Entry doesn't start with space or digit. // Entry doesn't start with space or digit.
// C++20: [[unlikely]] // C++20: [[unlikely]]
return false; return {false, 0, 0, '\0'};
} }
// Gather digits. NB No risk of overflow as 99'999 < max int. // Gather digits. NB No risk of overflow as 99'999 < max int.
while (*p == '0') { while (*p == '0') {
@ -1019,10 +1024,10 @@ QPDF::Xref_table::read_entry(qpdf_offset_t& f1, int& f2, char& type)
// No test for valid line[19]. // No test for valid line[19].
if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) { if (*(++p) && *(++p) && (*p == '\n' || *p == '\r') && f1_len == 10 && f2_len == 5) {
// C++20: [[likely]] // C++20: [[likely]]
return true; return {true, f1, f2, type};
} }
} }
return read_bad_entry(f1, f2, type); return read_bad_entry();
} }
// Read a single cross-reference table section and associated trailer. // Read a single cross-reference table section and associated trailer.
@ -1052,7 +1057,10 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset)
QTC::TC("qpdf", "QPDF trailer size not integer"); QTC::TC("qpdf", "QPDF trailer size not integer");
throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer"); throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is not an integer");
} }
if (sz >= static_cast<unsigned int>(max_id_)) {
QTC::TC("qpdf", "QPDF trailer size impossibly large");
throw qpdf.damagedPDF("trailer", "/Size key in trailer dictionary is impossibly large");
}
table.resize(sz); table.resize(sz);
} }
@ -1064,10 +1072,8 @@ QPDF::Xref_table::process_section(qpdf_offset_t xref_offset)
first_item_offset_ = file->tell(); first_item_offset_ = file->tell();
} }
// For xref_table, these will always be small enough to be ints // For xref_table, these will always be small enough to be ints
qpdf_offset_t f1 = 0; auto [success, f1, f2, type] = read_entry();
int f2 = 0; if (!success) {
char type = '\0';
if (!read_entry(f1, f2, type)) {
throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")"); throw damaged_table("invalid xref entry (obj=" + std::to_string(i) + ")");
} }
if (type == 'f') { if (type == 'f') {
@ -1585,8 +1591,7 @@ QPDF::Xref_table::read_trailer()
{ {
qpdf_offset_t offset = file->tell(); qpdf_offset_t offset = file->tell();
bool empty = false; bool empty = false;
auto object = auto object = QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false);
QPDFParser(*file, "trailer", tokenizer, nullptr, &qpdf, true).parse(empty, false);
if (empty) { if (empty) {
// Nothing in the PDF spec appears to allow empty objects, but they have been encountered in // Nothing in the PDF spec appears to allow empty objects, but they have been encountered in
// actual PDF files and Adobe Reader appears to ignore them. // actual PDF files and Adobe Reader appears to ignore them.

View File

@ -471,6 +471,21 @@ QPDFJob::createQPDF()
} }
handleUnderOverlay(pdf); handleUnderOverlay(pdf);
handleTransformations(pdf); handleTransformations(pdf);
if (m->remove_info) {
auto trailer = pdf.getTrailer();
auto mod_date = trailer.getKey("/Info").getKeyIfDict("/ModDate");
if (mod_date.isNull()) {
trailer.removeKey("/Info");
} else {
auto info = trailer.replaceKeyAndGetNew(
"/Info", pdf.makeIndirectObject(QPDFObjectHandle::newDictionary()));
info.replaceKey("/ModDate", mod_date);
}
pdf.getRoot().removeKey("/Metadata");
}
if (m->remove_metadata) {
pdf.getRoot().removeKey("/Metadata");
}
for (auto& foreign: page_heap) { for (auto& foreign: page_heap) {
if (foreign->anyWarnings()) { if (foreign->anyWarnings()) {

View File

@ -510,6 +510,20 @@ QPDFJob::Config::removeAttachment(std::string const& parameter)
return this; return this;
} }
QPDFJob::Config*
QPDFJob::Config::removeInfo()
{
o.m->remove_info = true;
return this;
}
QPDFJob::Config*
QPDFJob::Config::removeMetadata()
{
o.m->remove_metadata = true;
return this;
}
QPDFJob::Config* QPDFJob::Config*
QPDFJob::Config::removePageLabels() QPDFJob::Config::removePageLabels()
{ {

View File

@ -469,14 +469,15 @@ QPDFParser::fixMissingKeys()
bool bool
QPDFParser::tooManyBadTokens() QPDFParser::tooManyBadTokens()
{ {
if (good_count <= 4) { if (--max_bad_count > 0 && good_count > 4) {
good_count = 0;
bad_count = 1;
return false;
}
if (++bad_count > 5) { if (++bad_count > 5) {
warn("too many errors; giving up on reading object"); warn("too many errors; giving up on reading object");
return true; return true;
} }
} else {
bad_count = 1;
}
good_count = 0; good_count = 0;
return false; return false;
} }

View File

@ -47,7 +47,7 @@ QPDFWordTokenFinder::check()
// Find a word token matching the given string, preceded by a delimiter, and followed by a // Find a word token matching the given string, preceded by a delimiter, and followed by a
// delimiter or EOF. // delimiter or EOF.
QPDFTokenizer tokenizer; QPDFTokenizer tokenizer;
QPDFTokenizer::Token t = tokenizer.readToken(is, "finder", true); QPDFTokenizer::Token t = tokenizer.readToken(is, "finder", true, str.size() + 2);
qpdf_offset_t pos = is.tell(); qpdf_offset_t pos = is.tell();
if (!(t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, str))) { if (!(t == QPDFTokenizer::Token(QPDFTokenizer::tt_word, str))) {
QTC::TC("qpdf", "QPDFTokenizer finder found wrong word"); QTC::TC("qpdf", "QPDFTokenizer finder found wrong word");

View File

@ -83,9 +83,11 @@ class QPDFParser
std::vector<StackFrame> stack; std::vector<StackFrame> stack;
StackFrame* frame; StackFrame* frame;
// Number of recent bad tokens. // Number of recent bad tokens.
int bad_count = 0; int bad_count{0};
// Number of bad tokens (remaining) before giving up.
int max_bad_count{15};
// Number of good tokens since last bad token. Irrelevant if bad_count == 0. // Number of good tokens since last bad token. Irrelevant if bad_count == 0.
int good_count = 0; int good_count{0};
// Start offset including any leading whitespace. // Start offset including any leading whitespace.
qpdf_offset_t start; qpdf_offset_t start;
// Number of successive integer tokens. // Number of successive integer tokens.

View File

@ -292,8 +292,8 @@ class QPDF::Xref_table
std::vector<Subsection> subsections(std::string& line); std::vector<Subsection> subsections(std::string& line);
std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset); std::vector<Subsection> bad_subsections(std::string& line, qpdf_offset_t offset);
Subsection subsection(std::string const& line); Subsection subsection(std::string const& line);
bool read_entry(qpdf_offset_t& f1, int& f2, char& type); std::tuple<bool, qpdf_offset_t, int, char> read_entry();
bool read_bad_entry(qpdf_offset_t& f1, int& f2, char& type); std::tuple<bool, qpdf_offset_t, int, char> read_bad_entry();
// Methods to parse streams // Methods to parse streams
qpdf_offset_t read_stream(qpdf_offset_t offset); qpdf_offset_t read_stream(qpdf_offset_t offset);

View File

@ -414,6 +414,13 @@ Don't optimize images whose area in pixels is below the specified value.
)"); )");
ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images from optimization", R"(Prevent inline images from being considered by --optimize-images. ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images from optimization", R"(Prevent inline images from being considered by --optimize-images.
)"); )");
ap.addOptionHelp("--remove-info", "modification", "remove file information", R"(Exclude file information (except modification date) from the output file.
)");
ap.addOptionHelp("--remove-metadata", "modification", "remove metadata", R"(Exclude metadata from the output file.
)");
}
static void add_help_5(QPDFArgParser& ap)
{
ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file. ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file.
)"); )");
ap.addOptionHelp("--set-page-labels", "modification", "number pages for the entire document", R"(--set-page-labels label-spec ... -- 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
1 and continuing sequentially until the end of the document. For 1 and continuing sequentially until the end of the document. For
additional examples, please consult the manual. additional examples, please consult the manual.
)"); )");
}
static void add_help_5(QPDFArgParser& ap)
{
ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage: ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage:
--encrypt \ --encrypt \
@ -641,6 +645,9 @@ ap.addOptionHelp("--force-R5", "encryption", "use unsupported R=5 encryption", R
algorithm that existed only in Acrobat version IX. This option algorithm that existed only in Acrobat version IX. This option
should not be used except for compatibility testing. should not be used except for compatibility testing.
)"); )");
}
static void add_help_6(QPDFArgParser& ap)
{
ap.addHelpTopic("page-selection", "select pages from one or more files", R"(Use the --pages option to select pages from multiple files. Usage: ap.addHelpTopic("page-selection", "select pages from one or more files", R"(Use the --pages option to select pages from multiple files. Usage:
qpdf in.pdf --pages --file=input-file \ qpdf in.pdf --pages --file=input-file \
@ -725,9 +732,6 @@ appearance: first underlays, then the original page, then overlays.
Run qpdf --help=page-ranges for help with page ranges. Run qpdf --help=page-ranges for help with page ranges.
)"); )");
}
static void add_help_6(QPDFArgParser& ap)
{
ap.addOptionHelp("--to", "overlay-underlay", "destination pages for underlay/overlay", R"(--to=page-range ap.addOptionHelp("--to", "overlay-underlay", "destination pages for underlay/overlay", R"(--to=page-range
Specify the range of pages in the primary output to apply Specify the range of pages in the primary output to apply
@ -829,6 +833,9 @@ its terminating "--".
To copy attachments from a password-protected file, use To copy attachments from a password-protected file, use
the --password option after the file name. the --password option after the file name.
)"); )");
}
static void add_help_7(QPDFArgParser& ap)
{
ap.addOptionHelp("--prefix", "copy-attachments", "key prefix for copying attachments", R"(--prefix=prefix ap.addOptionHelp("--prefix", "copy-attachments", "key prefix for copying attachments", R"(--prefix=prefix
Prepend a prefix to each key; may be needed if there are 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
the options in this section are specified, no output file may be the options in this section are specified, no output file may be
given. given.
)"); )");
}
static void add_help_7(QPDFArgParser& ap)
{
ap.addOptionHelp("--is-encrypted", "inspection", "silently test whether a file is encrypted", R"(Silently exit with a code indicating the file's encryption status: ap.addOptionHelp("--is-encrypted", "inspection", "silently test whether a file is encrypted", R"(Silently exit with a code indicating the file's encryption status:
0: the file is encrypted 0: the file is encrypted
@ -919,6 +923,9 @@ output as binary data. Get the key with --list-attachments.
ap.addHelpTopic("json", "JSON output for PDF information", R"(Show information about the PDF file in JSON format. Please see the ap.addHelpTopic("json", "JSON output for PDF information", R"(Show information about the PDF file in JSON format. Please see the
JSON chapter in the qpdf manual for details. JSON chapter in the qpdf manual for details.
)"); )");
}
static void add_help_8(QPDFArgParser& ap)
{
ap.addOptionHelp("--json", "json", "show file in JSON format", R"(--json[=version] ap.addOptionHelp("--json", "json", "show file in JSON format", R"(--json[=version]
Generate a JSON representation of the file. This is described in 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
output a JSON object with the same keys and with values output a JSON object with the same keys and with values
containing descriptive text. containing descriptive text.
)"); )");
}
static void add_help_8(QPDFArgParser& ap)
{
ap.addOptionHelp("--json-key", "json", "limit which keys are in JSON output", R"(--json-key=key ap.addOptionHelp("--json-key", "json", "limit which keys are in JSON output", R"(--json-key=key
This option is repeatable. If given, only the specified This option is repeatable. If given, only the specified

View File

@ -68,6 +68,8 @@ this->ap.addBare("progress", [this](){c_main->progress();});
this->ap.addBare("qdf", [this](){c_main->qdf();}); this->ap.addBare("qdf", [this](){c_main->qdf();});
this->ap.addBare("raw-stream-data", [this](){c_main->rawStreamData();}); this->ap.addBare("raw-stream-data", [this](){c_main->rawStreamData();});
this->ap.addBare("recompress-flate", [this](){c_main->recompressFlate();}); this->ap.addBare("recompress-flate", [this](){c_main->recompressFlate();});
this->ap.addBare("remove-info", [this](){c_main->removeInfo();});
this->ap.addBare("remove-metadata", [this](){c_main->removeMetadata();});
this->ap.addBare("remove-page-labels", [this](){c_main->removePageLabels();}); this->ap.addBare("remove-page-labels", [this](){c_main->removePageLabels();});
this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput)); this->ap.addBare("replace-input", b(&ArgParser::argReplaceInput));
this->ap.addBare("report-memory-usage", [this](){c_main->reportMemoryUsage();}); this->ap.addBare("report-memory-usage", [this](){c_main->reportMemoryUsage();});

View File

@ -412,6 +412,12 @@ addParameter([this](std::string const& p) { c_pages->range(p); });
popHandler(); // key: range popHandler(); // key: range
popHandler(); // array: .pages[] popHandler(); // array: .pages[]
popHandler(); // key: pages popHandler(); // key: pages
pushKey("removeInfo");
addBare([this]() { c_main->removeInfo(); });
popHandler(); // key: removeInfo
pushKey("removeMetadata");
addBare([this]() { c_main->removeMetadata(); });
popHandler(); // key: removeMetadata
pushKey("removePageLabels"); pushKey("removePageLabels");
addBare([this]() { c_main->removePageLabels(); }); addBare([this]() { c_main->removePageLabels(); });
popHandler(); // key: removePageLabels popHandler(); // key: removePageLabels

View File

@ -145,6 +145,8 @@ static constexpr char const* JOB_SCHEMA_DATA = R"({
"range": "page range" "range": "page range"
} }
], ],
"removeInfo": "remove file information",
"removeMetadata": "remove metadata",
"removePageLabels": "remove explicit page numbers", "removePageLabels": "remove explicit page numbers",
"reportMemoryUsage": "best effort report of memory usage", "reportMemoryUsage": "best effort report of memory usage",
"rotate": "rotate pages", "rotate": "rotate pages",

View File

@ -1773,6 +1773,27 @@ Related Options
Prevent inline images from being included in image optimization Prevent inline images from being included in image optimization
done by :qpdf:ref:`--optimize-images`. done by :qpdf:ref:`--optimize-images`.
.. qpdf:option:: --remove-info
.. help: remove file information
Exclude file information (except modification date) from the output file.
Exclude file information (except modification date) from the output file by
omitting all entries (except ``/ModDate``) from the ``/Info`` dictionary in
the document trailer.
See also :qpdf:ref:`--remove-metadata`.
.. qpdf:option:: --remove-metadata
.. help: remove metadata
Exclude metadata from the output file.
Exclude metadata from the output file by omitting the ``/Metadata``
dictionary in the document catalog.
See also :qpdf:ref:`--remove-info`.
.. qpdf:option:: --remove-page-labels .. qpdf:option:: --remove-page-labels
.. help: remove explicit page numbers .. help: remove explicit page numbers

View File

@ -530,6 +530,12 @@ Don't optimize images whose area in pixels is below the specified value.
.B --keep-inline-images \-\- exclude inline images from optimization .B --keep-inline-images \-\- exclude inline images from optimization
Prevent inline images from being considered by --optimize-images. Prevent inline images from being considered by --optimize-images.
.TP .TP
.B --remove-info \-\- remove file information
Exclude file information (except modification date) from the output file.
.TP
.B --remove-metadata \-\- remove metadata
Exclude metadata from the output file.
.TP
.B --remove-page-labels \-\- remove explicit page numbers .B --remove-page-labels \-\- remove explicit page numbers
Exclude page labels (explicit page numbers) from the output file. Exclude page labels (explicit page numbers) from the output file.
.TP .TP

View File

@ -55,6 +55,7 @@ QPDF invalid xref entry 0
QPDF missing trailer 0 QPDF missing trailer 0
QPDF trailer lacks size 0 QPDF trailer lacks size 0
QPDF trailer size not integer 0 QPDF trailer size not integer 0
QPDF trailer size impossibly large 0
QPDF trailer prev not integer 0 QPDF trailer prev not integer 0
QPDFParser bad brace 0 QPDFParser bad brace 0
QPDFParser bad brace in parseRemainder 0 QPDFParser bad brace in parseRemainder 0

View File

@ -14,7 +14,7 @@ cleanup();
my $td = new TestDriver('merge-and-split'); my $td = new TestDriver('merge-and-split');
my $n_tests = 28; my $n_tests = 34;
# Select pages from the same file multiple times including selecting # Select pages from the same file multiple times including selecting
# twice from an encrypted file and specifying the password only the # twice from an encrypted file and specifying the password only the
@ -103,6 +103,39 @@ $td->runtest("check output",
{$td->COMMAND => "qpdf-test-compare a.pdf remove-labels.pdf"}, {$td->COMMAND => "qpdf-test-compare a.pdf remove-labels.pdf"},
{$td->FILE => "remove-labels.pdf", $td->EXIT_STATUS => 0}); {$td->FILE => "remove-labels.pdf", $td->EXIT_STATUS => 0});
$td->runtest("remove metadata",
{$td->COMMAND =>
"qpdf metadata-crypt-filter.pdf a.pdf" .
" --remove-metadata" .
" --decrypt" .
" --static-id"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "remove-metadata.pdf"});
$td->runtest("remove info (with moddate)",
{$td->COMMAND =>
"qpdf remove-metadata.pdf a.pdf" .
" --remove-info" .
" --decrypt" .
" --static-id"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "remove-info.pdf"});
$td->runtest("remove info (without moddate)",
{$td->COMMAND =>
"qpdf remove-metadata-no-moddate.pdf a.pdf" .
" --remove-info" .
" --decrypt" .
" --static-id"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check output",
{$td->FILE => "a.pdf"},
{$td->FILE => "remove-info-no-moddate.pdf"});
$td->runtest("split with shared resources", $td->runtest("split with shared resources",
{$td->COMMAND => {$td->COMMAND =>
"qpdf --qdf --static-id" . "qpdf --qdf --static-id" .

View File

@ -0,0 +1,19 @@
WARNING: issue-fuzz.pdf: can't find PDF header
WARNING: issue-fuzz.pdf (xref table, offset 19): accepting invalid xref table entry
WARNING: issue-fuzz.pdf (trailer, offset 36): unknown token while reading object; treating as string
WARNING: issue-fuzz.pdf (trailer, offset 53): unexpected >
WARNING: issue-fuzz.pdf (trailer, offset 54): unknown token while reading object; treating as string
WARNING: issue-fuzz.pdf (trailer, offset 58): unknown token while reading object; treating as string
WARNING: issue-fuzz.pdf (trailer, offset 72): unknown token while reading object; treating as string
WARNING: issue-fuzz.pdf (trailer, offset 36): dictionary ended prematurely; using null as value for last key
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake1
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake2
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake3
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake4
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake5
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake6
WARNING: issue-fuzz.pdf (trailer, offset 36): expected dictionary key but found non-name object; inserting key /QPDFFake7
WARNING: issue-fuzz.pdf: file is damaged
WARNING: issue-fuzz.pdf (trailer, offset 32): /Size key in trailer dictionary is impossibly large
WARNING: issue-fuzz.pdf: Attempting to reconstruct cross-reference table
qpdf: issue-fuzz.pdf: unable to find /Root dictionary

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -38,6 +38,7 @@ my @bug_tests = (
["263", "empty xref stream", 2], ["263", "empty xref stream", 2],
["335a", "ozz-fuzz-12152", 2], ["335a", "ozz-fuzz-12152", 2],
["335b", "ozz-fuzz-14845", 2], ["335b", "ozz-fuzz-14845", 2],
["fuzz", "impossibly large trailer /Size"],
# ["fuzz-16214", "stream in object stream", 3, "--preserve-unreferenced"], # ["fuzz-16214", "stream in object stream", 3, "--preserve-unreferenced"],
# When adding to this list, consider adding to CORPUS_FROM_TEST in # When adding to this list, consider adding to CORPUS_FROM_TEST in
# fuzz/CMakeLists.txt and updating the count in # fuzz/CMakeLists.txt and updating the count in