mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 02:49:00 +00:00
Add progress reporting for QPDFWriter (fixes #200)
This commit is contained in:
parent
2a82f6e1e0
commit
a433ed24f9
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -3311,6 +3330,45 @@ QPDFWriter::enqueueObjectsPCLm()
|
||||
enqueueObject(trailer.getKey("/Root"));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
36
qpdf/qpdf.cc
36
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)
|
||||
|
Loading…
Reference in New Issue
Block a user