aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--include/qpdf/QPDFWriter.hh25
-rw-r--r--libqpdf/QPDFWriter.cc60
-rw-r--r--qpdf/qpdf.cc36
4 files changed, 125 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index dd19b0a2..018b9f59 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2018-06-22 Jay Berkenbilt <ejb@ql.org>
+ * Add progress reporting to QPDFWriter. Programmatically, you can
+ register a progress reporter with registerProgressReporter(). From
+ the command line, passing --progress will give progress indicators
+ in increments of no less than 1% as output files are written.
+ Fixes #200.
+
* Add new method QPDF::getObjectCount(). This gives an approximate
(upper bound) account of objects in the QPDF object.
diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh
index cdd63e47..1802078a 100644
--- a/include/qpdf/QPDFWriter.hh
+++ b/include/qpdf/QPDFWriter.hh
@@ -76,6 +76,19 @@ class QPDFWriter
QPDF_DLL
~QPDFWriter();
+ class ProgressReporter
+ {
+ public:
+ virtual ~ProgressReporter()
+ {
+ }
+
+ // This method is called with a value from 0 to 100 to
+ // indicate approximate progress through the write process.
+ // See registerProgressReporter.
+ virtual void reportProgress(int) = 0;
+ };
+
// Setting Output. Output may be set only one time. If you don't
// use the filename version of the QPDFWriter constructor, you
// must call exactly one of these methods.
@@ -386,6 +399,11 @@ class QPDFWriter
QPDF_DLL
void setPCLm(bool);
+ // If you want to be notified of progress, derive a class from
+ // ProgressReporter and override the reportProgress method.
+ QPDF_DLL
+ void registerProgressReporter(PointerHolder<ProgressReporter>);
+
QPDF_DLL
void write();
@@ -450,6 +468,7 @@ class QPDFWriter
void prepareFileForWrite();
void enqueueObjectsStandard();
void enqueueObjectsPCLm();
+ void indicateProgress(bool decrement, bool finished);
void writeStandard();
void writeLinearized();
void enqueuePart(std::vector<QPDFObjectHandle>& part);
@@ -583,6 +602,12 @@ class QPDFWriter
std::string lin_pass1_filename;
std::map<int, int> obj_renumber_no_gen;
std::map<int, int> object_to_object_stream_no_gen;
+
+ // For progress reporting
+ PointerHolder<ProgressReporter> progress_reporter;
+ int events_expected;
+ int events_seen;
+ int next_progress_report;
};
// Keep all member variables inside the Members object, which we
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index 3dcff164..447b6627 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -62,7 +62,10 @@ QPDFWriter::Members::Members(QPDF& pdf) :
added_newline(false),
max_ostream_index(0),
deterministic_id(false),
- md5_pipeline(0)
+ md5_pipeline(0),
+ events_expected(0),
+ events_seen(0),
+ next_progress_report(0)
{
}
@@ -1856,6 +1859,10 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
if (pass == 1)
{
offsets.push_back(this->m->pipeline->getCount());
+ // To avoid double-counting objects being written in
+ // object streams for progress reporting, decrement in
+ // pass 1.
+ indicateProgress(true, false);
}
writeObject(this->m->pdf.getObjectByObjGen(obj), count);
@@ -1929,6 +1936,7 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
return;
}
+ indicateProgress(false, false);
int new_id = this->m->obj_renumber[old_og];
if (this->m->qdf_mode)
{
@@ -2253,6 +2261,7 @@ QPDFWriter::prepareFileForWrite()
{
continue;
}
+ indicateProgress(false, false);
visited.insert(node.getObjectID());
}
@@ -2500,6 +2509,15 @@ QPDFWriter::write()
setMinimumPDFVersion("1.5");
}
+ // Set up progress reporting. We spent about equal amounts of time
+ // preparing and writing one pass. To get a rough estimate of
+ // progress, we track handling of indirect objects. For linearized
+ // files, we write two passes. events_expected is an
+ // approximation, but it's good enough for progress reporting,
+ // which is mostly a guess anyway.
+ this->m->events_expected = (
+ this->m->pdf.getObjectCount() * (this->m->linearized ? 3 : 2));
+
prepareFileForWrite();
if (this->m->linearized)
@@ -2522,6 +2540,7 @@ QPDFWriter::write()
this->m->output_buffer = this->m->buffer_pipeline->getBuffer();
this->m->buffer_pipeline = 0;
}
+ indicateProgress(false, true);
}
void
@@ -3312,6 +3331,45 @@ QPDFWriter::enqueueObjectsPCLm()
}
void
+QPDFWriter::indicateProgress(bool decrement, bool finished)
+{
+ if (decrement)
+ {
+ --this->m->events_seen;
+ return;
+ }
+
+ ++this->m->events_seen;
+
+ if (! this->m->progress_reporter.getPointer())
+ {
+ return;
+ }
+
+ if (finished || (this->m->events_seen >= this->m->next_progress_report))
+ {
+ int percentage = (
+ finished
+ ? 100
+ : this->m->next_progress_report == 0
+ ? 0
+ : std::min(99, 1 + ((100 * this->m->events_seen) /
+ this->m->events_expected)));
+ this->m->progress_reporter->reportProgress(percentage);
+ }
+ while (this->m->events_seen >= this->m->next_progress_report)
+ {
+ this->m->next_progress_report += (this->m->events_expected / 100);
+ }
+}
+
+void
+QPDFWriter::registerProgressReporter(PointerHolder<ProgressReporter> pr)
+{
+ this->m->progress_reporter = pr;
+}
+
+void
QPDFWriter::writeStandard()
{
if (this->m->deterministic_id)
diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc
index 783548ef..1684215a 100644
--- a/qpdf/qpdf.cc
+++ b/qpdf/qpdf.cc
@@ -60,6 +60,7 @@ struct Options
decrypt(false),
split_pages(0),
verbose(false),
+ progress(false),
copy_encryption(false),
encryption_file(0),
encryption_file_password(0),
@@ -122,7 +123,8 @@ struct Options
bool linearize;
bool decrypt;
int split_pages;
- bool verbose;
+ bool verbose;
+ bool progress;
bool copy_encryption;
char const* encryption_file;
char const* encryption_file_password;
@@ -204,6 +206,29 @@ class DiscardContents: public QPDFObjectHandle::ParserCallbacks
virtual void handleEOF() {}
};
+class ProgressReporter: public QPDFWriter::ProgressReporter
+{
+ public:
+ ProgressReporter(char const* filename) :
+ filename(filename)
+ {
+ }
+ virtual ~ProgressReporter()
+ {
+ }
+
+ virtual void reportProgress(int);
+ private:
+ std::string filename;
+};
+
+void
+ProgressReporter::reportProgress(int percentage)
+{
+ std::cout << whoami << ": " << filename << ": write progress: "
+ << percentage << "%" << std::endl;
+}
+
// Note: let's not be too noisy about documenting the fact that this
// software purposely fails to enforce the distinction between user
// and owner passwords. A user password is sufficient to gain full
@@ -235,6 +260,7 @@ Basic Options\n\
--help show command-line argument help\n\
--password=password specify a password for accessing encrypted files\n\
--verbose provide additional informational output\n\
+--progress give progress indicators while writing output\n\
--linearize generated a linearized (web optimized) file\n\
--copy-encryption=file copy encryption parameters from specified file\n\
--encryption-file-password=password\n\
@@ -1642,6 +1668,10 @@ static void parse_options(int argc, char* argv[], Options& o)
{
o.verbose = true;
}
+ else if (strcmp(arg, "progress") == 0)
+ {
+ o.progress = true;
+ }
else if (strcmp(arg, "deterministic-id") == 0)
{
o.deterministic_id = true;
@@ -2350,6 +2380,10 @@ static void set_writer_options(QPDF& pdf, Options& o, QPDFWriter& w)
parse_version(o.force_version, version, extension_level);
w.forcePDFVersion(version, extension_level);
}
+ if (o.progress && o.outfilename)
+ {
+ w.registerProgressReporter(new ProgressReporter(o.outfilename));
+ }
}
static void write_outfile(QPDF& pdf, Options& o)