summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--TODO10
-rw-r--r--include/qpdf/QPDF.hh29
-rw-r--r--libqpdf/QPDF.cc22
-rw-r--r--libqpdf/QPDF_linearization.cc64
-rw-r--r--manual/qpdf-manual.xml55
-rw-r--r--qpdf/qtest/qpdf.test14
-rw-r--r--qpdf/qtest/qpdf/linearized-and-warnings-1.out52
-rw-r--r--qpdf/qtest/qpdf/linearized-and-warnings-2.out54
-rw-r--r--qpdf/qtest/qpdf/linearized-and-warnings.pdfbin0 -> 1310 bytes
-rw-r--r--qpdf/test_driver.cc17
11 files changed, 276 insertions, 45 deletions
diff --git a/ChangeLog b/ChangeLog
index be0163cb..8474f68d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/TODO b/TODO
index d3c25152..1542fbe7 100644
--- a/TODO
+++ b/TODO
@@ -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.
diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh
index 241a45de..a6ccc409 100644
--- a/include/qpdf/QPDF.hh
+++ b/include/qpdf/QPDF.hh
@@ -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;
diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc
index 3ea0f813..122b0bdb 100644
--- a/libqpdf/QPDF.cc
+++ b/libqpdf/QPDF.cc
@@ -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),
@@ -333,6 +335,13 @@ QPDF::setIgnoreXRefStreams(bool 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)
{
this->suppress_warnings = 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;
}
}
diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc
index f291c201..ca3fdbae 100644
--- a/libqpdf/QPDF_linearization.cc
+++ b/libqpdf/QPDF_linearization.cc
@@ -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)
diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml
index cab6740e..773a918e 100644
--- a/manual/qpdf-manual.xml
+++ b/manual/qpdf-manual.xml
@@ -2075,6 +2075,61 @@ print "\n";
</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>
<itemizedlist>
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index fd787364..8257633d 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -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 ---");
diff --git a/qpdf/qtest/qpdf/linearized-and-warnings-1.out b/qpdf/qtest/qpdf/linearized-and-warnings-1.out
new file mode 100644
index 00000000..954181f6
--- /dev/null
+++ b/qpdf/qtest/qpdf/linearized-and-warnings-1.out
@@ -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
diff --git a/qpdf/qtest/qpdf/linearized-and-warnings-2.out b/qpdf/qtest/qpdf/linearized-and-warnings-2.out
new file mode 100644
index 00000000..d8d17b7c
--- /dev/null
+++ b/qpdf/qtest/qpdf/linearized-and-warnings-2.out
@@ -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
diff --git a/qpdf/qtest/qpdf/linearized-and-warnings.pdf b/qpdf/qtest/qpdf/linearized-and-warnings.pdf
new file mode 100644
index 00000000..44037566
--- /dev/null
+++ b/qpdf/qtest/qpdf/linearized-and-warnings.pdf
Binary files differ
diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc
index 3127503a..c7b6a6b4 100644
--- a/qpdf/test_driver.cc
+++ b/qpdf/test_driver.cc
@@ -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 ") +