2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-06-05 11:50:53 +00:00

Add QPDFWriter::getFinalVersion (fixes #266)

This commit is contained in:
Jay Berkenbilt 2019-01-04 12:32:02 -05:00
parent 837dcf8fc2
commit 16fd6e64f9
5 changed files with 73 additions and 12 deletions

View File

@ -1,5 +1,10 @@
2019-01-04 Jay Berkenbilt <ejb@ql.org> 2019-01-04 Jay Berkenbilt <ejb@ql.org>
* Add new method QPDFWriter::getFinalVersion, which returns the
PDF version that will ultimately be written to the final file. See
comments in QPDFWriter.hh for some restrictions on its use. Fixes
#266.
* When unexpected errors are found while checking linearization * When unexpected errors are found while checking linearization
data, print an error message instead of calling assert, which data, print an error message instead of calling assert, which
cause the program to crash. Fixes #209, #231. cause the program to crash. Fixes #209, #231.

View File

@ -404,6 +404,18 @@ class QPDFWriter
QPDF_DLL QPDF_DLL
void registerProgressReporter(PointerHolder<ProgressReporter>); void registerProgressReporter(PointerHolder<ProgressReporter>);
// Return the PDF version that will be written into the header.
// Calling this method does all the preparation for writing, so it
// is an error to call any methods that may cause a change to the
// version. Adding new objects to the original file after calling
// this may also cause problems. It is safe to update existing
// objects or stream contents after calling this method, e.g., to
// include the final version number in metadata.
QPDF_DLL
std::string getFinalVersion();
// Write the final file. There is no expectation of being able to
// call write() more than once.
QPDF_DLL QPDF_DLL
void write(); void write();
@ -473,6 +485,7 @@ class QPDFWriter
void writeLinearized(); void writeLinearized();
void enqueuePart(std::vector<QPDFObjectHandle>& part); void enqueuePart(std::vector<QPDFObjectHandle>& part);
void writeEncryptionDictionary(); void writeEncryptionDictionary();
void doWriteSetup();
void writeHeader(); void writeHeader();
void writeHintStream(int hint_id); void writeHintStream(int hint_id);
qpdf_offset_t writeXRefTable( qpdf_offset_t writeXRefTable(
@ -598,6 +611,7 @@ class QPDFWriter
bool deterministic_id; bool deterministic_id;
Pl_MD5* md5_pipeline; Pl_MD5* md5_pipeline;
std::string deterministic_id_data; std::string deterministic_id_data;
bool did_write_setup;
// For linearization only // For linearization only
std::string lin_pass1_filename; std::string lin_pass1_filename;

View File

@ -63,6 +63,7 @@ QPDFWriter::Members::Members(QPDF& pdf) :
max_ostream_index(0), max_ostream_index(0),
deterministic_id(false), deterministic_id(false),
md5_pipeline(0), md5_pipeline(0),
did_write_setup(false),
events_expected(0), events_expected(0),
events_seen(0), events_seen(0),
next_progress_report(0) next_progress_report(0)
@ -2358,8 +2359,14 @@ QPDFWriter::prepareFileForWrite()
} }
void void
QPDFWriter::write() QPDFWriter::doWriteSetup()
{ {
if (this->m->did_write_setup)
{
return;
}
this->m->did_write_setup = true;
// Do preliminary setup // Do preliminary setup
if (this->m->linearized) if (this->m->linearized)
@ -2507,6 +2514,23 @@ QPDFWriter::write()
setMinimumPDFVersion("1.5"); setMinimumPDFVersion("1.5");
} }
setMinimumPDFVersion(this->m->pdf.getPDFVersion(),
this->m->pdf.getExtensionLevel());
this->m->final_pdf_version = this->m->min_pdf_version;
this->m->final_extension_level = this->m->min_extension_level;
if (! this->m->forced_pdf_version.empty())
{
QTC::TC("qpdf", "QPDFWriter using forced PDF version");
this->m->final_pdf_version = this->m->forced_pdf_version;
this->m->final_extension_level = this->m->forced_extension_level;
}
}
void
QPDFWriter::write()
{
doWriteSetup();
// Set up progress reporting. We spent about equal amounts of time // Set up progress reporting. We spent about equal amounts of time
// preparing and writing one pass. To get a rough estimate of // preparing and writing one pass. To get a rough estimate of
// progress, we track handling of indirect objects. For linearized // progress, we track handling of indirect objects. For linearized
@ -2569,20 +2593,16 @@ QPDFWriter::writeEncryptionDictionary()
closeObject(this->m->encryption_dict_objid); closeObject(this->m->encryption_dict_objid);
} }
std::string
QPDFWriter::getFinalVersion()
{
doWriteSetup();
return this->m->final_pdf_version;
}
void void
QPDFWriter::writeHeader() QPDFWriter::writeHeader()
{ {
setMinimumPDFVersion(this->m->pdf.getPDFVersion(),
this->m->pdf.getExtensionLevel());
this->m->final_pdf_version = this->m->min_pdf_version;
this->m->final_extension_level = this->m->min_extension_level;
if (! this->m->forced_pdf_version.empty())
{
QTC::TC("qpdf", "QPDFWriter using forced PDF version");
this->m->final_pdf_version = this->m->forced_pdf_version;
this->m->final_extension_level = this->m->forced_extension_level;
}
writeString("%PDF-"); writeString("%PDF-");
writeString(this->m->final_pdf_version); writeString(this->m->final_pdf_version);
if (this->m->pclm) if (this->m->pclm)

View File

@ -173,6 +173,16 @@ $td->runtest("\@file exists and file doesn't",
{$td->FILE => "check-at-file.out", $td->EXIT_STATUS => 0}, {$td->FILE => "check-at-file.out", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES); $td->NORMALIZE_NEWLINES);
show_ntests();
# ----------
$td->notify("--- Final Version ---");
$n_tests += 1;
$td->runtest("check final version",
{$td->COMMAND => "test_driver 54 minimal.pdf"},
{$td->STRING => "test 54 done\n", $td->EXIT_STATUS => 0},
$td->NORMALIZE_NEWLINES);
show_ntests(); show_ntests();
# ---------- # ----------
$td->notify("--- Dangling Refs ---"); $td->notify("--- Dangling Refs ---");

View File

@ -1865,6 +1865,18 @@ void runtest(int n, char const* filename1, char const* arg2)
w.setStaticID(true); w.setStaticID(true);
w.write(); w.write();
} }
else if (n == 54)
{
// Test getFinalVersion. This must be invoked with a file
// whose final version is not 1.5.
QPDFWriter w(pdf, "a.pdf");
assert(pdf.getPDFVersion() != "1.5");
w.setObjectStreamMode(qpdf_o_generate);
if (w.getFinalVersion() != "1.5")
{
std::cout << "oops: " << w.getFinalVersion() << std::endl;
}
}
else else
{ {
throw std::runtime_error(std::string("invalid test ") + throw std::runtime_error(std::string("invalid test ") +