mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +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>
|
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
|
* Add new method QPDF::getObjectCount(). This gives an approximate
|
||||||
(upper bound) account of objects in the QPDF object.
|
(upper bound) account of objects in the QPDF object.
|
||||||
|
|
||||||
|
@ -76,6 +76,19 @@ class QPDFWriter
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
~QPDFWriter();
|
~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
|
// Setting Output. Output may be set only one time. If you don't
|
||||||
// use the filename version of the QPDFWriter constructor, you
|
// use the filename version of the QPDFWriter constructor, you
|
||||||
// must call exactly one of these methods.
|
// must call exactly one of these methods.
|
||||||
@ -386,6 +399,11 @@ class QPDFWriter
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setPCLm(bool);
|
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
|
QPDF_DLL
|
||||||
void write();
|
void write();
|
||||||
|
|
||||||
@ -450,6 +468,7 @@ class QPDFWriter
|
|||||||
void prepareFileForWrite();
|
void prepareFileForWrite();
|
||||||
void enqueueObjectsStandard();
|
void enqueueObjectsStandard();
|
||||||
void enqueueObjectsPCLm();
|
void enqueueObjectsPCLm();
|
||||||
|
void indicateProgress(bool decrement, bool finished);
|
||||||
void writeStandard();
|
void writeStandard();
|
||||||
void writeLinearized();
|
void writeLinearized();
|
||||||
void enqueuePart(std::vector<QPDFObjectHandle>& part);
|
void enqueuePart(std::vector<QPDFObjectHandle>& part);
|
||||||
@ -583,6 +602,12 @@ class QPDFWriter
|
|||||||
std::string lin_pass1_filename;
|
std::string lin_pass1_filename;
|
||||||
std::map<int, int> obj_renumber_no_gen;
|
std::map<int, int> obj_renumber_no_gen;
|
||||||
std::map<int, int> object_to_object_stream_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
|
// Keep all member variables inside the Members object, which we
|
||||||
|
@ -62,7 +62,10 @@ QPDFWriter::Members::Members(QPDF& pdf) :
|
|||||||
added_newline(false),
|
added_newline(false),
|
||||||
max_ostream_index(0),
|
max_ostream_index(0),
|
||||||
deterministic_id(false),
|
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)
|
if (pass == 1)
|
||||||
{
|
{
|
||||||
offsets.push_back(this->m->pipeline->getCount());
|
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);
|
writeObject(this->m->pdf.getObjectByObjGen(obj), count);
|
||||||
|
|
||||||
@ -1929,6 +1936,7 @@ QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indicateProgress(false, false);
|
||||||
int new_id = this->m->obj_renumber[old_og];
|
int new_id = this->m->obj_renumber[old_og];
|
||||||
if (this->m->qdf_mode)
|
if (this->m->qdf_mode)
|
||||||
{
|
{
|
||||||
@ -2253,6 +2261,7 @@ QPDFWriter::prepareFileForWrite()
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
indicateProgress(false, false);
|
||||||
visited.insert(node.getObjectID());
|
visited.insert(node.getObjectID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2500,6 +2509,15 @@ QPDFWriter::write()
|
|||||||
setMinimumPDFVersion("1.5");
|
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();
|
prepareFileForWrite();
|
||||||
|
|
||||||
if (this->m->linearized)
|
if (this->m->linearized)
|
||||||
@ -2522,6 +2540,7 @@ QPDFWriter::write()
|
|||||||
this->m->output_buffer = this->m->buffer_pipeline->getBuffer();
|
this->m->output_buffer = this->m->buffer_pipeline->getBuffer();
|
||||||
this->m->buffer_pipeline = 0;
|
this->m->buffer_pipeline = 0;
|
||||||
}
|
}
|
||||||
|
indicateProgress(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -3311,6 +3330,45 @@ QPDFWriter::enqueueObjectsPCLm()
|
|||||||
enqueueObject(trailer.getKey("/Root"));
|
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
|
void
|
||||||
QPDFWriter::writeStandard()
|
QPDFWriter::writeStandard()
|
||||||
{
|
{
|
||||||
|
36
qpdf/qpdf.cc
36
qpdf/qpdf.cc
@ -60,6 +60,7 @@ struct Options
|
|||||||
decrypt(false),
|
decrypt(false),
|
||||||
split_pages(0),
|
split_pages(0),
|
||||||
verbose(false),
|
verbose(false),
|
||||||
|
progress(false),
|
||||||
copy_encryption(false),
|
copy_encryption(false),
|
||||||
encryption_file(0),
|
encryption_file(0),
|
||||||
encryption_file_password(0),
|
encryption_file_password(0),
|
||||||
@ -122,7 +123,8 @@ struct Options
|
|||||||
bool linearize;
|
bool linearize;
|
||||||
bool decrypt;
|
bool decrypt;
|
||||||
int split_pages;
|
int split_pages;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
|
bool progress;
|
||||||
bool copy_encryption;
|
bool copy_encryption;
|
||||||
char const* encryption_file;
|
char const* encryption_file;
|
||||||
char const* encryption_file_password;
|
char const* encryption_file_password;
|
||||||
@ -204,6 +206,29 @@ class DiscardContents: public QPDFObjectHandle::ParserCallbacks
|
|||||||
virtual void handleEOF() {}
|
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
|
// Note: let's not be too noisy about documenting the fact that this
|
||||||
// software purposely fails to enforce the distinction between user
|
// software purposely fails to enforce the distinction between user
|
||||||
// and owner passwords. A user password is sufficient to gain full
|
// 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\
|
--help show command-line argument help\n\
|
||||||
--password=password specify a password for accessing encrypted files\n\
|
--password=password specify a password for accessing encrypted files\n\
|
||||||
--verbose provide additional informational output\n\
|
--verbose provide additional informational output\n\
|
||||||
|
--progress give progress indicators while writing output\n\
|
||||||
--linearize generated a linearized (web optimized) file\n\
|
--linearize generated a linearized (web optimized) file\n\
|
||||||
--copy-encryption=file copy encryption parameters from specified file\n\
|
--copy-encryption=file copy encryption parameters from specified file\n\
|
||||||
--encryption-file-password=password\n\
|
--encryption-file-password=password\n\
|
||||||
@ -1642,6 +1668,10 @@ static void parse_options(int argc, char* argv[], Options& o)
|
|||||||
{
|
{
|
||||||
o.verbose = true;
|
o.verbose = true;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(arg, "progress") == 0)
|
||||||
|
{
|
||||||
|
o.progress = true;
|
||||||
|
}
|
||||||
else if (strcmp(arg, "deterministic-id") == 0)
|
else if (strcmp(arg, "deterministic-id") == 0)
|
||||||
{
|
{
|
||||||
o.deterministic_id = true;
|
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);
|
parse_version(o.force_version, version, extension_level);
|
||||||
w.forcePDFVersion(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)
|
static void write_outfile(QPDF& pdf, Options& o)
|
||||||
|
Loading…
Reference in New Issue
Block a user