mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-02 22:50:20 +00:00
Add QPDFObjectHandle::setFilterOnWrite
This commit is contained in:
parent
3f9191a344
commit
12ecd2019a
@ -1,5 +1,13 @@
|
|||||||
2020-12-26 Jay Berkenbilt <ejb@ql.org>
|
2020-12-26 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add QPDFObjectHandle::setFilterOnWrite, which can be used to
|
||||||
|
tell QPDFWriter not to filter a stream on output even if it can.
|
||||||
|
You can use this to prevent QPDFWriter from touching a stream
|
||||||
|
(either uncompressing or compressing) that you have optimized or
|
||||||
|
otherwise ensured looks exactly the way you want it, even if
|
||||||
|
decode level or stream compression would otherwise cause
|
||||||
|
QPDFWriter to modify the stream.
|
||||||
|
|
||||||
* Add ostream << for QPDFObjGen. (Don't ask why it took 7.5 years
|
* Add ostream << for QPDFObjGen. (Don't ask why it took 7.5 years
|
||||||
for me to decide to do this.)
|
for me to decide to do this.)
|
||||||
|
|
||||||
|
@ -786,6 +786,22 @@ class QPDFObjectHandle
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
QPDFObjectHandle getDict();
|
QPDFObjectHandle getDict();
|
||||||
|
|
||||||
|
// By default, or if true passed, QPDFWriter will attempt to
|
||||||
|
// filter a stream based on decode level, whether compression is
|
||||||
|
// enabled, and its ability to filter. Passing false will prevent
|
||||||
|
// QPDFWriter from attempting to filter the stream even if it can.
|
||||||
|
// This includes both decoding and compressing. This makes it
|
||||||
|
// possible for you to prevent QPDFWriter from uncompressing and
|
||||||
|
// recompressing a stream that it knows how to operate on for any
|
||||||
|
// application-specific reason, such as that you have already
|
||||||
|
// optimized its filtering. Note that this doesn't affect any
|
||||||
|
// other ways to get the stream's data, such as pipeStreamData or
|
||||||
|
// getStreamData.
|
||||||
|
QPDF_DLL
|
||||||
|
void setFilterOnWrite(bool);
|
||||||
|
QPDF_DLL
|
||||||
|
bool getFilterOnWrite();
|
||||||
|
|
||||||
// If addTokenFilter has been called for this stream, then the
|
// If addTokenFilter has been called for this stream, then the
|
||||||
// original data should be considered to be modified. This means we
|
// original data should be considered to be modified. This means we
|
||||||
// should avoid optimizations such as not filtering a stream that
|
// should avoid optimizations such as not filtering a stream that
|
||||||
|
@ -1176,6 +1176,20 @@ QPDFObjectHandle::getDict()
|
|||||||
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->getDict();
|
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->getDict();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFObjectHandle::setFilterOnWrite(bool val)
|
||||||
|
{
|
||||||
|
assertStream();
|
||||||
|
dynamic_cast<QPDF_Stream*>(obj.getPointer())->setFilterOnWrite(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDFObjectHandle::getFilterOnWrite()
|
||||||
|
{
|
||||||
|
assertStream();
|
||||||
|
return dynamic_cast<QPDF_Stream*>(obj.getPointer())->getFilterOnWrite();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
QPDFObjectHandle::isDataModified()
|
QPDFObjectHandle::isDataModified()
|
||||||
{
|
{
|
||||||
|
@ -1470,6 +1470,7 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
|||||||
{
|
{
|
||||||
compress_stream = false;
|
compress_stream = false;
|
||||||
is_metadata = false;
|
is_metadata = false;
|
||||||
|
|
||||||
QPDFObjGen old_og = stream.getObjGen();
|
QPDFObjGen old_og = stream.getObjGen();
|
||||||
QPDFObjectHandle stream_dict = stream.getDict();
|
QPDFObjectHandle stream_dict = stream.getDict();
|
||||||
|
|
||||||
@ -1481,7 +1482,13 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
|||||||
bool filter = (stream.isDataModified() ||
|
bool filter = (stream.isDataModified() ||
|
||||||
this->m->compress_streams ||
|
this->m->compress_streams ||
|
||||||
this->m->stream_decode_level);
|
this->m->stream_decode_level);
|
||||||
if (this->m->compress_streams)
|
bool filter_on_write = stream.getFilterOnWrite();
|
||||||
|
if (! filter_on_write)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFWriter getFilterOnWrite false");
|
||||||
|
filter = false;
|
||||||
|
}
|
||||||
|
if (filter_on_write && this->m->compress_streams)
|
||||||
{
|
{
|
||||||
// Don't filter if the stream is already compressed with
|
// Don't filter if the stream is already compressed with
|
||||||
// FlateDecode. This way we don't make it worse if the
|
// FlateDecode. This way we don't make it worse if the
|
||||||
@ -1502,7 +1509,7 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
|||||||
}
|
}
|
||||||
bool normalize = false;
|
bool normalize = false;
|
||||||
bool uncompress = false;
|
bool uncompress = false;
|
||||||
if (is_metadata &&
|
if (filter_on_write && is_metadata &&
|
||||||
((! this->m->encrypted) || (this->m->encrypt_metadata == false)))
|
((! this->m->encrypted) || (this->m->encrypt_metadata == false)))
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "QPDFWriter not compressing metadata");
|
QTC::TC("qpdf", "QPDFWriter not compressing metadata");
|
||||||
@ -1510,13 +1517,13 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
|||||||
compress_stream = false;
|
compress_stream = false;
|
||||||
uncompress = true;
|
uncompress = true;
|
||||||
}
|
}
|
||||||
else if (this->m->normalize_content &&
|
else if (filter_on_write && this->m->normalize_content &&
|
||||||
this->m->normalized_streams.count(old_og))
|
this->m->normalized_streams.count(old_og))
|
||||||
{
|
{
|
||||||
normalize = true;
|
normalize = true;
|
||||||
filter = true;
|
filter = true;
|
||||||
}
|
}
|
||||||
else if (filter && this->m->compress_streams)
|
else if (filter_on_write && filter && this->m->compress_streams)
|
||||||
{
|
{
|
||||||
compress_stream = true;
|
compress_stream = true;
|
||||||
QTC::TC("qpdf", "QPDFWriter compressing uncompressed stream");
|
QTC::TC("qpdf", "QPDFWriter compressing uncompressed stream");
|
||||||
|
@ -90,6 +90,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
|
|||||||
qpdf(qpdf),
|
qpdf(qpdf),
|
||||||
objid(objid),
|
objid(objid),
|
||||||
generation(generation),
|
generation(generation),
|
||||||
|
filter_on_write(true),
|
||||||
stream_dict(stream_dict),
|
stream_dict(stream_dict),
|
||||||
offset(offset),
|
offset(offset),
|
||||||
length(length)
|
length(length)
|
||||||
@ -115,6 +116,18 @@ QPDF_Stream::registerStreamFilter(
|
|||||||
filter_factories[filter_name] = factory;
|
filter_factories[filter_name] = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDF_Stream::setFilterOnWrite(bool val)
|
||||||
|
{
|
||||||
|
this->filter_on_write = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QPDF_Stream::getFilterOnWrite() const
|
||||||
|
{
|
||||||
|
return this->filter_on_write;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDF_Stream::releaseResolved()
|
QPDF_Stream::releaseResolved()
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,8 @@ class QPDF_Stream: public QPDFObject
|
|||||||
virtual void setDescription(QPDF*, std::string const&);
|
virtual void setDescription(QPDF*, std::string const&);
|
||||||
QPDFObjectHandle getDict() const;
|
QPDFObjectHandle getDict() const;
|
||||||
bool isDataModified() const;
|
bool isDataModified() const;
|
||||||
|
void setFilterOnWrite(bool);
|
||||||
|
bool getFilterOnWrite() const;
|
||||||
|
|
||||||
// Methods to help QPDF copy foreign streams
|
// Methods to help QPDF copy foreign streams
|
||||||
qpdf_offset_t getOffset() const;
|
qpdf_offset_t getOffset() const;
|
||||||
@ -83,6 +85,7 @@ class QPDF_Stream: public QPDFObject
|
|||||||
QPDF* qpdf;
|
QPDF* qpdf;
|
||||||
int objid;
|
int objid;
|
||||||
int generation;
|
int generation;
|
||||||
|
bool filter_on_write;
|
||||||
QPDFObjectHandle stream_dict;
|
QPDFObjectHandle stream_dict;
|
||||||
qpdf_offset_t offset;
|
qpdf_offset_t offset;
|
||||||
size_t length;
|
size_t length;
|
||||||
|
@ -520,3 +520,4 @@ qpdf-c called qpdf_oh_get_generation 0
|
|||||||
qpdf-c called qpdf_oh_unparse 0
|
qpdf-c called qpdf_oh_unparse 0
|
||||||
qpdf-c called qpdf_oh_unparse_resolved 0
|
qpdf-c called qpdf_oh_unparse_resolved 0
|
||||||
qpdf-c called qpdf_oh_unparse_binary 0
|
qpdf-c called qpdf_oh_unparse_binary 0
|
||||||
|
QPDFWriter getFilterOnWrite false 0
|
||||||
|
@ -1179,6 +1179,19 @@ $td->runtest("check output",
|
|||||||
{$td->FILE => "a.pdf"},
|
{$td->FILE => "a.pdf"},
|
||||||
{$td->FILE => "filter-abbreviation.out"});
|
{$td->FILE => "filter-abbreviation.out"});
|
||||||
|
|
||||||
|
show_ntests();
|
||||||
|
# ----------
|
||||||
|
$td->notify("--- Disable filter on write ---");
|
||||||
|
$n_tests += 2;
|
||||||
|
|
||||||
|
$td->runtest("no filter on write",
|
||||||
|
{$td->COMMAND => "test_driver 70 filter-on-write.pdf"},
|
||||||
|
{$td->STRING => "test 70 done\n", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("check output",
|
||||||
|
{$td->FILE => "a.pdf"},
|
||||||
|
{$td->FILE => "filter-on-write-out.pdf"});
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Invalid objects ---");
|
$td->notify("--- Invalid objects ---");
|
||||||
@ -1197,7 +1210,7 @@ $td->runtest("object with zero offset",
|
|||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Error/output rediction ---");
|
$td->notify("--- Error/output redirection ---");
|
||||||
$n_tests += 2;
|
$n_tests += 2;
|
||||||
|
|
||||||
$td->runtest("error/output redirection to null",
|
$td->runtest("error/output redirection to null",
|
||||||
|
BIN
qpdf/qtest/qpdf/filter-on-write-out.pdf
Normal file
BIN
qpdf/qtest/qpdf/filter-on-write-out.pdf
Normal file
Binary file not shown.
41
qpdf/qtest/qpdf/filter-on-write.pdf
Normal file
41
qpdf/qtest/qpdf/filter-on-write.pdf
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
%¿÷¢þ
|
||||||
|
1 0 obj
|
||||||
|
<< /Pages 2 0 R /Type /Catalog >>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<< /Count 0 /Kids [ ] /Type /Pages >>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<< /Filter /RunLengthDecode /Length 5 >>
|
||||||
|
stream
|
||||||
|
<EFBFBD>w¹w€endstream
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<< /Length 6 >>
|
||||||
|
stream
|
||||||
|
potatoendstream
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<< /Filter /RunLengthDecode /Length 5 >>
|
||||||
|
stream
|
||||||
|
<EFBFBD>w¹w€endstream
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<< /Length 5 >>
|
||||||
|
stream
|
||||||
|
saladendstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000015 00000 n
|
||||||
|
0000000064 00000 n
|
||||||
|
0000000117 00000 n
|
||||||
|
0000000195 00000 n
|
||||||
|
0000000249 00000 n
|
||||||
|
0000000327 00000 n
|
||||||
|
trailer << /Root 1 0 R /Size 7 /ID [<5ab7a0329a828e2f46377e16247bc367><5ab7a0329a828e2f46377e16247bc367>] /S1 3 0 R /S2 4 0 R /S3 5 0 R /S4 6 0 R >>
|
||||||
|
startxref
|
||||||
|
380
|
||||||
|
%%EOF
|
@ -2219,6 +2219,16 @@ void runtest(int n, char const* filename1, char const* arg2)
|
|||||||
w.write();
|
w.write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (n == 70)
|
||||||
|
{
|
||||||
|
auto trailer = pdf.getTrailer();
|
||||||
|
trailer.getKey("/S1").setFilterOnWrite(false);
|
||||||
|
trailer.getKey("/S2").setFilterOnWrite(false);
|
||||||
|
QPDFWriter w(pdf, "a.pdf");
|
||||||
|
w.setStaticID(true);
|
||||||
|
w.setDecodeLevel(qpdf_dl_specialized);
|
||||||
|
w.write();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("invalid test ") +
|
throw std::runtime_error(std::string("invalid test ") +
|
||||||
|
Loading…
Reference in New Issue
Block a user