mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 02:49:00 +00:00
Add functional versions of QPDFObjectHandle::replaceStreamData
Also fix a bug in checking consistency of length for stream data providers. Length should not be checked or recorded if the provider says it failed to generate the data.
This commit is contained in:
parent
e2593e2efe
commit
efbb21673c
@ -1,3 +1,11 @@
|
||||
2021-02-14 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Add new versions of QPDFObjectHandle::replaceStreamData that
|
||||
take std::function objects for cases when you need something
|
||||
between a static string and a full-fledged StreamDataProvider.
|
||||
Using this with QUtil::file_provider is a very easy way to create
|
||||
a stream from the contents of a file.
|
||||
|
||||
2021-02-12 Jay Berkenbilt <ejb@ql.org>
|
||||
|
||||
* Move formerly internal QPDFMatrix class to the public API. This
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
#include <qpdf/QPDFObjGen.hh>
|
||||
#include <qpdf/PointerHolder.hh>
|
||||
@ -986,6 +987,26 @@ class QPDFObjectHandle
|
||||
QPDFObjectHandle const& filter,
|
||||
QPDFObjectHandle const& decode_parms);
|
||||
|
||||
// Starting in qpdf 10.2, you can use C++-11 function objects
|
||||
// instead of StreamDataProvider.
|
||||
|
||||
// The provider should write the stream data to the pipeline. For
|
||||
// a one-liner to replace stream data with the contents of a file,
|
||||
// pass QUtil::file_provider(filename) as provider.
|
||||
QPDF_DLL
|
||||
void replaceStreamData(std::function<void(Pipeline*)> provider,
|
||||
QPDFObjectHandle const& filter,
|
||||
QPDFObjectHandle const& decode_parms);
|
||||
// The provider should write the stream data to the pipeline,
|
||||
// returning true if it succeeded without errors.
|
||||
QPDF_DLL
|
||||
void replaceStreamData(
|
||||
std::function<bool(Pipeline*,
|
||||
bool suppress_warnings,
|
||||
bool will_retry)> provider,
|
||||
QPDFObjectHandle const& filter,
|
||||
QPDFObjectHandle const& decode_parms);
|
||||
|
||||
// Access object ID and generation. For direct objects, return
|
||||
// object ID 0.
|
||||
|
||||
|
@ -1347,6 +1347,62 @@ QPDFObjectHandle::replaceStreamData(PointerHolder<StreamDataProvider> provider,
|
||||
provider, filter, decode_parms);
|
||||
}
|
||||
|
||||
class FunctionProvider: public QPDFObjectHandle::StreamDataProvider
|
||||
{
|
||||
public:
|
||||
FunctionProvider(std::function<void(Pipeline*)> provider) :
|
||||
StreamDataProvider(false),
|
||||
p1(provider),
|
||||
p2(nullptr)
|
||||
{
|
||||
}
|
||||
FunctionProvider(std::function<bool(Pipeline*, bool, bool)> provider) :
|
||||
StreamDataProvider(true),
|
||||
p1(nullptr),
|
||||
p2(provider)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void provideStreamData(int, int, Pipeline* pipeline) override
|
||||
{
|
||||
p1(pipeline);
|
||||
}
|
||||
|
||||
virtual bool provideStreamData(int, int, Pipeline* pipeline,
|
||||
bool suppress_warnings,
|
||||
bool will_retry) override
|
||||
{
|
||||
return p2(pipeline, suppress_warnings, will_retry);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(Pipeline*)> p1;
|
||||
std::function<bool(Pipeline*, bool, bool)> p2;
|
||||
};
|
||||
|
||||
void
|
||||
QPDFObjectHandle::replaceStreamData(std::function<void(Pipeline*)> provider,
|
||||
QPDFObjectHandle const& filter,
|
||||
QPDFObjectHandle const& decode_parms)
|
||||
{
|
||||
assertStream();
|
||||
PointerHolder<StreamDataProvider> sdp = new FunctionProvider(provider);
|
||||
dynamic_cast<QPDF_Stream*>(obj.getPointer())->replaceStreamData(
|
||||
sdp, filter, decode_parms);
|
||||
}
|
||||
|
||||
void
|
||||
QPDFObjectHandle::replaceStreamData(
|
||||
std::function<bool(Pipeline*, bool, bool)> provider,
|
||||
QPDFObjectHandle const& filter,
|
||||
QPDFObjectHandle const& decode_parms)
|
||||
{
|
||||
assertStream();
|
||||
PointerHolder<StreamDataProvider> sdp = new FunctionProvider(provider);
|
||||
dynamic_cast<QPDF_Stream*>(obj.getPointer())->replaceStreamData(
|
||||
sdp, filter, decode_parms);
|
||||
}
|
||||
|
||||
QPDFObjGen
|
||||
QPDFObjectHandle::getObjGen() const
|
||||
{
|
||||
|
@ -533,7 +533,7 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool* filterp,
|
||||
}
|
||||
qpdf_offset_t actual_length = count.getCount();
|
||||
qpdf_offset_t desired_length = 0;
|
||||
if (this->stream_dict.hasKey("/Length"))
|
||||
if (success && this->stream_dict.hasKey("/Length"))
|
||||
{
|
||||
desired_length = this->stream_dict.getKey("/Length").getIntValue();
|
||||
if (actual_length == desired_length)
|
||||
@ -555,7 +555,7 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool* filterp,
|
||||
QUtil::int_to_string(desired_length) + " bytes");
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (success)
|
||||
{
|
||||
QTC::TC("qpdf", "QPDF_Stream provider length not provided");
|
||||
this->stream_dict.replaceKey(
|
||||
|
@ -5211,6 +5211,17 @@ print "\n";
|
||||
contents of a file through a pipeline as binary data.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add new versions of
|
||||
<function>QPDFObjectHandle::replaceStreamData</function>
|
||||
that take <classname>std::function</classname> objects for
|
||||
cases when you need something between a static string and a
|
||||
full-fledged StreamDataProvider. Using this with
|
||||
QUtil::file_provider is a very easy way to create a stream
|
||||
from the contents of a file.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Add option to <function>QUtil::double_to_string</function>
|
||||
|
@ -717,7 +717,7 @@ $td->runtest("check dates",
|
||||
show_ntests();
|
||||
# ----------
|
||||
$td->notify("--- Stream Replacement Tests ---");
|
||||
$n_tests += 8;
|
||||
$n_tests += 10;
|
||||
|
||||
$td->runtest("replace stream data",
|
||||
{$td->COMMAND => "test_driver 7 qstream.pdf"},
|
||||
@ -747,6 +747,13 @@ $td->runtest("add page contents",
|
||||
$td->runtest("new stream",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "add-contents.pdf"});
|
||||
$td->runtest("functional replace stream data",
|
||||
{$td->COMMAND => "test_driver 78 minimal.pdf"},
|
||||
{$td->FILE => "test78.out", $td->EXIT_STATUS => 0},
|
||||
$td->NORMALIZE_NEWLINES);
|
||||
$td->runtest("check output",
|
||||
{$td->FILE => "a.pdf"},
|
||||
{$td->FILE => "test78.pdf"});
|
||||
|
||||
show_ntests();
|
||||
# ----------
|
||||
|
10
qpdf/qtest/qpdf/test78.out
Normal file
10
qpdf/qtest/qpdf/test78.out
Normal file
@ -0,0 +1,10 @@
|
||||
piping with warning suppression
|
||||
f2
|
||||
f2 done
|
||||
writing
|
||||
f2
|
||||
failing
|
||||
f2
|
||||
warning
|
||||
f2 done
|
||||
test 78 done
|
139
qpdf/qtest/qpdf/test78.pdf
Normal file
139
qpdf/qtest/qpdf/test78.pdf
Normal file
@ -0,0 +1,139 @@
|
||||
%PDF-1.3
|
||||
%¿÷¢þ
|
||||
%QDF-1.0
|
||||
|
||||
%% Original object ID: 1 0
|
||||
1 0 obj
|
||||
<<
|
||||
/Pages 6 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Original object ID: 7 0
|
||||
2 0 obj
|
||||
<<
|
||||
/Length 3 0 R
|
||||
>>
|
||||
stream
|
||||
potato
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
3 0 obj
|
||||
6
|
||||
endobj
|
||||
|
||||
%% Original object ID: 8 0
|
||||
4 0 obj
|
||||
<<
|
||||
/Length 5 0 R
|
||||
>>
|
||||
stream
|
||||
salad
|
||||
endstream
|
||||
endobj
|
||||
|
||||
%QDF: ignore_newline
|
||||
5 0 obj
|
||||
5
|
||||
endobj
|
||||
|
||||
%% Original object ID: 2 0
|
||||
6 0 obj
|
||||
<<
|
||||
/Count 1
|
||||
/Kids [
|
||||
7 0 R
|
||||
]
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Page 1
|
||||
%% Original object ID: 3 0
|
||||
7 0 obj
|
||||
<<
|
||||
/Contents 8 0 R
|
||||
/MediaBox [
|
||||
0
|
||||
0
|
||||
612
|
||||
792
|
||||
]
|
||||
/Parent 6 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 10 0 R
|
||||
>>
|
||||
/ProcSet 11 0 R
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Contents for page 1
|
||||
%% Original object ID: 4 0
|
||||
8 0 obj
|
||||
<<
|
||||
/Length 9 0 R
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
/F1 24 Tf
|
||||
72 720 Td
|
||||
(Potato) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
9 0 obj
|
||||
44
|
||||
endobj
|
||||
|
||||
%% Original object ID: 6 0
|
||||
10 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica
|
||||
/Encoding /WinAnsiEncoding
|
||||
/Name /F1
|
||||
/Subtype /Type1
|
||||
/Type /Font
|
||||
>>
|
||||
endobj
|
||||
|
||||
%% Original object ID: 5 0
|
||||
11 0 obj
|
||||
[
|
||||
/PDF
|
||||
/Text
|
||||
]
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 12
|
||||
0000000000 65535 f
|
||||
0000000052 00000 n
|
||||
0000000133 00000 n
|
||||
0000000216 00000 n
|
||||
0000000261 00000 n
|
||||
0000000343 00000 n
|
||||
0000000388 00000 n
|
||||
0000000497 00000 n
|
||||
0000000741 00000 n
|
||||
0000000840 00000 n
|
||||
0000000886 00000 n
|
||||
0000001032 00000 n
|
||||
trailer <<
|
||||
/Root 1 0 R
|
||||
/Size 12
|
||||
/Streams [
|
||||
2 0 R
|
||||
4 0 R
|
||||
]
|
||||
/ID [<31415926535897932384626433832795><31415926535897932384626433832795>]
|
||||
>>
|
||||
startxref
|
||||
1068
|
||||
%%EOF
|
@ -16,6 +16,7 @@
|
||||
#include <qpdf/Pl_StdioFile.hh>
|
||||
#include <qpdf/Pl_Buffer.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/QPDFWriter.hh>
|
||||
#include <qpdf/QPDFSystemError.hh>
|
||||
#include <qpdf/QIntC.hh>
|
||||
@ -2798,6 +2799,48 @@ void runtest(int n, char const* filename1, char const* arg2)
|
||||
w.setQDFMode(true);
|
||||
w.write();
|
||||
}
|
||||
else if (n == 78)
|
||||
{
|
||||
// Test functional versions of replaceStreamData()
|
||||
|
||||
auto f1 = [](Pipeline* p) {
|
||||
p->write(QUtil::unsigned_char_pointer("potato"), 6);
|
||||
p->finish();
|
||||
};
|
||||
auto f2 = [](Pipeline* p, bool suppress_warnings, bool will_retry) {
|
||||
std::cerr << "f2" << std::endl;
|
||||
if (will_retry)
|
||||
{
|
||||
std::cerr << "failing" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (! suppress_warnings)
|
||||
{
|
||||
std::cerr << "warning" << std::endl;
|
||||
}
|
||||
p->write(QUtil::unsigned_char_pointer("salad"), 5);
|
||||
p->finish();
|
||||
std::cerr << "f2 done" << std::endl;
|
||||
return true;
|
||||
};
|
||||
|
||||
auto null = QPDFObjectHandle::newNull();
|
||||
auto s1 = QPDFObjectHandle::newStream(&pdf);
|
||||
s1.replaceStreamData(f1, null, null);
|
||||
auto s2 = QPDFObjectHandle::newStream(&pdf);
|
||||
s2.replaceStreamData(f2, null, null);
|
||||
pdf.getTrailer().replaceKey(
|
||||
"/Streams", QPDFObjectHandle::newArray({s1, s2}));
|
||||
std::cout << "piping with warning suppression" << std::endl;
|
||||
Pl_Discard d;
|
||||
s2.pipeStreamData(&d, nullptr, 0, qpdf_dl_all, true, false);
|
||||
|
||||
std::cout << "writing" << std::endl;
|
||||
QPDFWriter w(pdf, "a.pdf");
|
||||
w.setStaticID(true);
|
||||
w.setQDFMode(true);
|
||||
w.write();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("invalid test ") +
|
||||
|
Loading…
Reference in New Issue
Block a user