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>
|
||||
|
||||
* 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
|
||||
for me to decide to do this.)
|
||||
|
||||
|
@ -786,6 +786,22 @@ class QPDFObjectHandle
|
||||
QPDF_DLL
|
||||
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
|
||||
// original data should be considered to be modified. This means we
|
||||
// should avoid optimizations such as not filtering a stream that
|
||||
|
@ -1176,6 +1176,20 @@ QPDFObjectHandle::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
|
||||
QPDFObjectHandle::isDataModified()
|
||||
{
|
||||
|
@ -1470,6 +1470,7 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
||||
{
|
||||
compress_stream = false;
|
||||
is_metadata = false;
|
||||
|
||||
QPDFObjGen old_og = stream.getObjGen();
|
||||
QPDFObjectHandle stream_dict = stream.getDict();
|
||||
|
||||
@ -1481,7 +1482,13 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
||||
bool filter = (stream.isDataModified() ||
|
||||
this->m->compress_streams ||
|
||||
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
|
||||
// FlateDecode. This way we don't make it worse if the
|
||||
@ -1502,7 +1509,7 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
||||
}
|
||||
bool normalize = false;
|
||||
bool uncompress = false;
|
||||
if (is_metadata &&
|
||||
if (filter_on_write && is_metadata &&
|
||||
((! this->m->encrypted) || (this->m->encrypt_metadata == false)))
|
||||
{
|
||||
QTC::TC("qpdf", "QPDFWriter not compressing metadata");
|
||||
@ -1510,13 +1517,13 @@ QPDFWriter::willFilterStream(QPDFObjectHandle stream,
|
||||
compress_stream = false;
|
||||
uncompress = true;
|
||||
}
|
||||
else if (this->m->normalize_content &&
|
||||
else if (filter_on_write && this->m->normalize_content &&
|
||||
this->m->normalized_streams.count(old_og))
|
||||
{
|
||||
normalize = true;
|
||||
filter = true;
|
||||
}
|
||||
else if (filter && this->m->compress_streams)
|
||||
else if (filter_on_write && filter && this->m->compress_streams)
|
||||
{
|
||||
compress_stream = true;
|
||||
QTC::TC("qpdf", "QPDFWriter compressing uncompressed stream");
|
||||
|
@ -90,6 +90,7 @@ QPDF_Stream::QPDF_Stream(QPDF* qpdf, int objid, int generation,
|
||||
qpdf(qpdf),
|
||||
objid(objid),
|
||||
generation(generation),
|
||||
filter_on_write(true),
|
||||
stream_dict(stream_dict),
|
||||
offset(offset),
|
||||
length(length)
|
||||
@ -115,6 +116,18 @@ QPDF_Stream::registerStreamFilter(
|
||||
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
|
||||
QPDF_Stream::releaseResolved()
|
||||
{
|
||||
|
@ -27,6 +27,8 @@ class QPDF_Stream: public QPDFObject
|
||||
virtual void setDescription(QPDF*, std::string const&);
|
||||
QPDFObjectHandle getDict() const;
|
||||
bool isDataModified() const;
|
||||
void setFilterOnWrite(bool);
|
||||
bool getFilterOnWrite() const;
|
||||
|
||||
// Methods to help QPDF copy foreign streams
|
||||
qpdf_offset_t getOffset() const;
|
||||
@ -83,6 +85,7 @@ class QPDF_Stream: public QPDFObject
|
||||
QPDF* qpdf;
|
||||
int objid;
|
||||
int generation;
|
||||
bool filter_on_write;
|
||||
QPDFObjectHandle stream_dict;
|
||||
qpdf_offset_t offset;
|
||||
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_resolved 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 => "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();
|
||||
# ----------
|
||||
$td->notify("--- Invalid objects ---");
|
||||
@ -1197,7 +1210,7 @@ $td->runtest("object with zero offset",
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Error/output rediction ---");
|
||||
$td->notify("--- Error/output redirection ---");
|
||||
$n_tests += 2;
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
throw std::runtime_error(std::string("invalid test ") +
|
||||
|
Loading…
Reference in New Issue
Block a user