setOutputStreams

git-svn-id: svn+q:///qpdf/trunk@1035 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
Jay Berkenbilt 2010-10-01 11:02:35 +00:00
parent 9f444ffef3
commit a72ce95c92
11 changed files with 276 additions and 45 deletions

View File

@ -1,5 +1,9 @@
2010-10-01 Jay Berkenbilt <ejb@ql.org>
* include/qpdf/QPDF.hh: Add setOutputStreams method to allow
redirection of library-generated output/error to alternative
streams.
* include/qpdf/QPDF.hh: Add processMemoryFile method for
processing a PDF file from a memory buffer instead of a file.

10
TODO
View File

@ -1,5 +1,7 @@
General
=======
2.2.1
=====
* Remove cerr and cout from QPDF*.cc
* QPDF::checkLinearization writes things to std::cout, which makes it
hard for GUIs that want to display the result. Go through all
@ -12,6 +14,10 @@ General
rethrow if needed. The old version should call the ostream version
with std::cout.
General
=======
* In general, take a fresh look at private methods to see which, if
any, should be protected.

View File

@ -12,6 +12,7 @@
#include <string>
#include <map>
#include <list>
#include <iostream>
#include <qpdf/DLL.h>
@ -61,6 +62,21 @@ class QPDF
// Parameter settings
// By default, warning messages are issued to std::cerr and output
// messages printed by certain check calls are issued to
// std::cout. This method allows you to specify alternative
// streams for this purpose. Note that no normal QPDF operations
// generate output to std::cout, so for applications that just
// wish to avoid creating output and don't call any check
// functions, calling setSuppressWarnings(true) is sufficient.
// Applications that wish to present check or warning information
// to users may replace the output and error streams to capture
// the output and errors for other use. A null value for either
// stream will cause QPDF to use std::cout or std::cerr as
// appropriate.
QPDF_DLL
void setOutputStreams(std::ostream* out_stream, std::ostream* err_stream);
// If true, ignore any cross-reference streams in a hybrid file
// (one that contains both cross-reference streams and
// cross-reference tables). This can be useful for testing to
@ -68,7 +84,8 @@ class QPDF
QPDF_DLL
void setIgnoreXRefStreams(bool);
// By default, any warnings are issued to stderr as they are
// By default, any warnings are issued to std::cerr or the error
// stream specified in a call to setOutputStreams as they are
// encountered. If this is called with a true value, reporting of
// warnings is suppressed. You may still retrieve warnings by
// calling getWarnings.
@ -88,7 +105,7 @@ class QPDF
// clear the list. This method may be called even if processFile
// throws an exception. Note that if setSuppressWarnings was not
// called or was called with a false value, any warnings retrieved
// here will have already been issued to stderr.
// here will have already been output.
QPDF_DLL
std::vector<QPDFExc> getWarnings();
@ -204,12 +221,14 @@ class QPDF
// Performs various sanity checks on a linearized file. Return
// true if no errors or warnings. Otherwise, return false and
// output errors and warnings to stdout.
// output errors and warnings to std::cout or the output stream
// specified in a call to setOutputStreams.
QPDF_DLL
bool checkLinearization();
// Calls checkLinearization() and, if possible, prints normalized
// contents of some of the hints tables to stdout. Normalization
// contents of some of the hints tables to std::cout or the output
// stream specified in a call to setOutputStreams. Normalization
// includes adding min values to delta values and adjusting
// offsets based on the location and size of the primary hint
// stream.
@ -803,6 +822,8 @@ class QPDF
bool encryption_initialized;
bool ignore_xref_streams;
bool suppress_warnings;
std::ostream* out_stream;
std::ostream* err_stream;
bool attempt_recovery;
int encryption_V;
bool encrypt_metadata;

View File

@ -267,6 +267,8 @@ QPDF::QPDF() :
encryption_initialized(false),
ignore_xref_streams(false),
suppress_warnings(false),
out_stream(&std::cout),
err_stream(&std::cerr),
attempt_recovery(true),
encryption_V(0),
encrypt_metadata(true),
@ -332,6 +334,13 @@ QPDF::setIgnoreXRefStreams(bool val)
this->ignore_xref_streams = val;
}
void
QPDF::setOutputStreams(std::ostream* out, std::ostream* err)
{
this->out_stream = out ? out : &std::cout;
this->err_stream = err ? err : &std::cerr;
}
void
QPDF::setSuppressWarnings(bool val)
{
@ -449,7 +458,8 @@ QPDF::warn(QPDFExc const& e)
this->warnings.push_back(e);
if (! this->suppress_warnings)
{
std::cerr << "WARNING: " << this->warnings.back().what() << std::endl;
*err_stream << "WARNING: "
<< this->warnings.back().what() << std::endl;
}
}
@ -1045,16 +1055,16 @@ QPDF::showXRefTable()
{
ObjGen const& og = (*iter).first;
QPDFXRefEntry const& entry = (*iter).second;
std::cout << og.obj << "/" << og.gen << ": ";
*out_stream << og.obj << "/" << og.gen << ": ";
switch (entry.getType())
{
case 1:
std::cout << "uncompressed; offset = " << entry.getOffset();
*out_stream << "uncompressed; offset = " << entry.getOffset();
break;
case 2:
std::cout << "compressed; stream = " << entry.getObjStreamNumber()
<< ", index = " << entry.getObjStreamIndex();
*out_stream << "compressed; stream = " << entry.getObjStreamNumber()
<< ", index = " << entry.getObjStreamIndex();
break;
default:
@ -1062,7 +1072,7 @@ QPDF::showXRefTable()
" showing xref_table");
break;
}
std::cout << std::endl;
*out_stream << std::endl;
}
}

View File

@ -64,7 +64,7 @@ QPDF::checkLinearization()
}
catch (QPDFExc& e)
{
std::cout << e.what() << std::endl;
*out_stream << e.what() << std::endl;
}
return result;
}
@ -351,9 +351,9 @@ QPDF::readHintStream(Pipeline& pl, off_t offset, size_t length)
if ((computed_end < min_end_offset) ||
(computed_end > max_end_offset))
{
std::cout << "expected = " << computed_end
<< "; actual = " << min_end_offset << ".."
<< max_end_offset << std::endl;
*out_stream << "expected = " << computed_end
<< "; actual = " << min_end_offset << ".."
<< max_end_offset << std::endl;
throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(),
"linearization dictionary",
this->file->getLastOffset(),
@ -617,7 +617,7 @@ QPDF::checkLinearizationInternal()
for (std::list<std::string>::iterator iter = errors.begin();
iter != errors.end(); ++iter)
{
std::cout << "ERROR: " << (*iter) << std::endl;
*out_stream << "ERROR: " << (*iter) << std::endl;
}
}
@ -627,7 +627,7 @@ QPDF::checkLinearizationInternal()
for (std::list<std::string>::iterator iter = warnings.begin();
iter != warnings.end(); ++iter)
{
std::cout << "WARNING: " << (*iter) << std::endl;
*out_stream << "WARNING: " << (*iter) << std::endl;
}
}
@ -1008,17 +1008,17 @@ QPDF::showLinearizationData()
}
catch (QPDFExc& e)
{
std::cout << e.what() << std::endl;
*out_stream << e.what() << std::endl;
}
}
void
QPDF::dumpLinearizationDataInternal()
{
std::cout << this->file->getName() << ": linearization data:" << std::endl
<< std::endl;
*out_stream << this->file->getName() << ": linearization data:" << std::endl
<< std::endl;
std::cout
*out_stream
<< "file_size: " << this->linp.file_size << std::endl
<< "first_page_object: " << this->linp.first_page_object << std::endl
<< "first_page_end: " << this->linp.first_page_end << std::endl
@ -1029,19 +1029,19 @@ QPDF::dumpLinearizationDataInternal()
<< "H_length: " << this->linp.H_length << std::endl
<< std::endl;
std::cout << "Page Offsets Hint Table" << std::endl
<< std::endl;
*out_stream << "Page Offsets Hint Table" << std::endl
<< std::endl;
dumpHPageOffset();
std::cout << std::endl
<< "Shared Objects Hint Table" << std::endl
<< std::endl;
*out_stream << std::endl
<< "Shared Objects Hint Table" << std::endl
<< std::endl;
dumpHSharedObject();
if (this->outline_hints.nobjects > 0)
{
std::cout << std::endl
<< "Outlines Hint Table" << std::endl
<< std::endl;
*out_stream << std::endl
<< "Outlines Hint Table" << std::endl
<< std::endl;
dumpHGeneric(this->outline_hints);
}
}
@ -1064,7 +1064,7 @@ void
QPDF::dumpHPageOffset()
{
HPageOffset& t = this->page_offset_hints;
std::cout
*out_stream
<< "min_nobjects: " << t.min_nobjects
<< std::endl
<< "first_page_offset: " << adjusted_offset(t.first_page_offset)
@ -1095,7 +1095,7 @@ QPDF::dumpHPageOffset()
for (int i1 = 0; i1 < this->linp.npages; ++i1)
{
HPageOffsetEntry& pe = t.entries[i1];
std::cout
*out_stream
<< "Page " << i1 << ":" << std::endl
<< " nobjects: " << pe.delta_nobjects + t.min_nobjects
<< std::endl
@ -1109,10 +1109,10 @@ QPDF::dumpHPageOffset()
<< " nshared_objects: " << pe.nshared_objects << std::endl;
for (int i2 = 0; i2 < pe.nshared_objects; ++i2)
{
std::cout << " identifier " << i2 << ": "
<< pe.shared_identifiers[i2] << std::endl;
std::cout << " numerator " << i2 << ": "
<< pe.shared_numerators[i2] << std::endl;
*out_stream << " identifier " << i2 << ": "
<< pe.shared_identifiers[i2] << std::endl;
*out_stream << " numerator " << i2 << ": "
<< pe.shared_numerators[i2] << std::endl;
}
}
}
@ -1121,7 +1121,7 @@ void
QPDF::dumpHSharedObject()
{
HSharedObject& t = this->shared_object_hints;
std::cout
*out_stream
<< "first_shared_obj: " << t.first_shared_obj
<< std::endl
<< "first_shared_offset: " << adjusted_offset(t.first_shared_offset)
@ -1140,19 +1140,19 @@ QPDF::dumpHSharedObject()
for (int i = 0; i < t.nshared_total; ++i)
{
HSharedObjectEntry& se = t.entries[i];
std::cout << "Shared Object " << i << ":" << std::endl;
std::cout << " group length: "
<< se.delta_group_length + t.min_group_length << std::endl;
*out_stream << "Shared Object " << i << ":" << std::endl;
*out_stream << " group length: "
<< se.delta_group_length + t.min_group_length << std::endl;
// PDF spec says signature present nobjects_minus_one are
// always 0, so print them only if they have a non-zero value.
if (se.signature_present)
{
std::cout << " signature present" << std::endl;
*out_stream << " signature present" << std::endl;
}
if (se.nobjects_minus_one != 0)
{
std::cout << " nobjects: "
<< se.nobjects_minus_one + 1 << std::endl;
*out_stream << " nobjects: "
<< se.nobjects_minus_one + 1 << std::endl;
}
}
}
@ -1160,7 +1160,7 @@ QPDF::dumpHSharedObject()
void
QPDF::dumpHGeneric(HGeneric& t)
{
std::cout
*out_stream
<< "first_object: " << t.first_object
<< std::endl
<< "first_object_offset: " << adjusted_offset(t.first_object_offset)

View File

@ -2074,6 +2074,61 @@ print "\n";
<filename>ChangeLog</filename> in the source distribution.
</para>
<variablelist>
<varlistentry>
<term>2.2.1: XXX</term>
<listitem>
<itemizedlist>
<listitem>
<para>
Add new method <classname>QPDF::processMemoryFile</classname>
for operating on PDF files that are loaded into memory rather
than in a file on disk.
</para>
</listitem>
<listitem>
<para>
Add new method <classname>QPDF::setOutputStreams</classname>
to replace <varname>std::cout</varname> and
<varname>std::cerr</varname> with other streams for generation
of diagnostic messages and error messages. This can be useful
for GUIs or other applications that want to capture any output
generated by the library to present to the user in some other
way. Note that QPDF does not write to
<varname>std::cout</varname> (or the specified output stream)
except where explicitly mentioned in
<filename>QPDF.hh</filename>, and that the only use of the
error stream is for warnings. Note also that output of
warnings is suppressed when
<literal>setSuppressWarnings(true)</literal> is called.
</para>
</listitem>
<listitem>
<para>
Give a warning but otherwise ignore empty PDF objects by
treating them as null. Empty object are not permitted by the
PDF specification but have been known to appear in some actual
PDF files.
</para>
</listitem>
<listitem>
<para>
Handle inline image filter abbreviations when the appear as
stream filter abbreviations. The PDF specification does not
allow use of stream filter abbreviations in this way, but
Adobe Reader and some other PDF readers accept them since they
sometimes appear incorrectly in actual PDF files.
</para>
</listitem>
<listitem>
<para>
Implement miscellaneous enhancements to
<classname>PointerHolder</classname> and
<classname>Buffer</classname> to support other changes.
</para>
</listitem>
</itemizedlist>
</listitem>
</varlistentry>
<varlistentry>
<term>2.2.0: August 14, 2010</term>
<listitem>

View File

@ -111,7 +111,7 @@ $td->runtest("new stream",
show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
$n_tests += 26;
$n_tests += 28;
$td->runtest("qpdf version",
{$td->COMMAND => "qpdf --version"},
@ -245,6 +245,18 @@ $td->runtest("empty object",
$td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES);
$td->runtest("error/output redirection to null",
{$td->COMMAND => "test_driver 12 linearized-and-warnings.pdf"},
{$td->FILE => "linearized-and-warnings-1.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
$td->runtest("error/output redirection to strings",
{$td->COMMAND => "test_driver 13 linearized-and-warnings.pdf"},
{$td->FILE => "linearized-and-warnings-2.out",
$td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
show_ntests();
# ----------
$td->notify("--- Error Condition Tests ---");

View File

@ -0,0 +1,52 @@
WARNING: linearized-and-warnings.pdf (object 2 0, file position 1117): empty object treated as null
linearized-and-warnings.pdf: linearization data:
file_size: 1310
first_page_object: 6
first_page_end: 1044
npages: 1
xref_zero_offset: 1132
first_page: 0
H_offset: 528
H_length: 118
Page Offsets Hint Table
min_nobjects: 4
first_page_offset: 646
nbits_delta_nobjects: 0
min_page_length: 398
nbits_delta_page_length: 0
min_content_offset: 0
nbits_delta_content_offset: 0
min_content_length: 398
nbits_delta_content_length: 0
nbits_nshared_objects: 0
nbits_shared_identifier: 3
nbits_shared_numerator: 0
shared_denominator: 4
Page 0:
nobjects: 4
length: 398
content_offset: 0
content_length: 398
nshared_objects: 0
Shared Objects Hint Table
first_shared_obj: 0
first_shared_offset: 0
nshared_first_page: 4
nshared_total: 4
nbits_nobjects: 0
min_group_length: 30
nbits_delta_group_length: 7
Shared Object 0:
group length: 143
Shared Object 1:
group length: 118
Shared Object 2:
group length: 30
Shared Object 3:
group length: 107
test 12 done

View File

@ -0,0 +1,54 @@
---output---
linearized-and-warnings.pdf: linearization data:
file_size: 1310
first_page_object: 6
first_page_end: 1044
npages: 1
xref_zero_offset: 1132
first_page: 0
H_offset: 528
H_length: 118
Page Offsets Hint Table
min_nobjects: 4
first_page_offset: 646
nbits_delta_nobjects: 0
min_page_length: 398
nbits_delta_page_length: 0
min_content_offset: 0
nbits_delta_content_offset: 0
min_content_length: 398
nbits_delta_content_length: 0
nbits_nshared_objects: 0
nbits_shared_identifier: 3
nbits_shared_numerator: 0
shared_denominator: 4
Page 0:
nobjects: 4
length: 398
content_offset: 0
content_length: 398
nshared_objects: 0
Shared Objects Hint Table
first_shared_obj: 0
first_shared_offset: 0
nshared_first_page: 4
nshared_total: 4
nbits_nobjects: 0
min_group_length: 30
nbits_delta_group_length: 7
Shared Object 0:
group length: 143
Shared Object 1:
group length: 118
Shared Object 2:
group length: 30
Shared Object 3:
group length: 107
---error---
WARNING: linearized-and-warnings.pdf (object 2 0, file position 1117): empty object treated as null
test 13 done

Binary file not shown.

View File

@ -10,6 +10,7 @@
#include <qpdf/Pl_Flate.hh>
#include <qpdf/QPDFWriter.hh>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@ -502,6 +503,22 @@ void runtest(int n, char const* filename)
std::cout << "raw stream data okay" << std::endl;
}
}
else if (n == 12)
{
pdf.setOutputStreams(0, 0);
pdf.showLinearizationData();
}
else if (n == 13)
{
std::ostringstream out;
std::ostringstream err;
pdf.setOutputStreams(&out, &err);
pdf.showLinearizationData();
std::cout << "---output---" << std::endl
<< out.str()
<< "---error---" << std::endl
<< err.str();
}
else
{
throw std::runtime_error(std::string("invalid test ") +