From 759c56e1fed2849b77bff18f2a50639876395e5e Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Wed, 10 Aug 2011 16:34:29 -0400 Subject: [PATCH] implement ability to save PDF to memory, also update ChangeLog --- ChangeLog | 17 +++++ include/qpdf/QPDFWriter.hh | 41 +++++++++++-- libqpdf/QPDFWriter.cc | 123 ++++++++++++++++++++++++++----------- qpdf/test_driver.cc | 10 ++- 4 files changed, 151 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index ed12c9bc..5fdb0464 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2011-08-10 Jay Berkenbilt + + * include/qpdf/QPDFWriter.hh: add a new constructor that takes + only a QPDF reference and leaves specification of output for + later. Add methods setOutputFilename() to set the output to a + filename or stdout, and setOutputMemory() to indicate that output + should go to a memory buffer. Add method getBuffer() to retrieve + the buffer used if output was saved to a memory buffer. + + * include/qpdf/QPDF.hh: add methods replaceObject() and + swapObjects() to allow replacement of an object and swapping of + two objects by object ID. + + * include/qpdf/QPDFObjectHandle.hh: add new methods getDictAsMap() + and getArrayAsVector() for returning the elements of a dictionary + or an array as a map or vector. + 2011-06-25 Jay Berkenbilt * 2.2.4: release diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 7a919b0d..a14ae514 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -35,6 +36,24 @@ class Pl_Count; class QPDFWriter { public: + // Construct a QPDFWriter object without specifying output. You + // must call one of the output setting routines defined below. + QPDF_DLL + QPDFWriter(QPDF& pdf); + + // Create a QPDFWriter object that writes its output to a file or + // to stdout. This is equivalent to using the previous + // constructor and then calling setOutputFilename(). See + // setOutputFilename() for details. + QPDF_DLL + QPDFWriter(QPDF& pdf, char const* filename); + QPDF_DLL + ~QPDFWriter(); + + // 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. + // Passing null as filename means write to stdout. QPDFWriter // will create a zero-length output file upon construction. If // write fails, the empty or partially written file will not be @@ -42,10 +61,20 @@ class QPDFWriter // useful for tracking down problems. If your application doesn't // want the partially written file to be left behind, you should // delete it the eventual call to write fails. - QPDF_DLL - QPDFWriter(QPDF& pdf, char const* filename); - QPDF_DLL - ~QPDFWriter(); + void setOutputFilename(char const* filename); + + // Indicate that QPDFWriter should create a memory buffer to + // contain the final PDF file. Obtain the memory by calling + // getBuffer(). + void setOutputMemory(); + + // Return the buffer object containing the PDF file. If + // setOutputMemory() has been called, this method may be called + // exactly one time after write() has returned. The caller is + // responsible for deleting the buffer when done. + Buffer* getBuffer(); + + // Setting Parameters // Set the value of object stream mode. In disable mode, we never // generate any object streams. In preserve mode, we preserve @@ -177,6 +206,7 @@ class QPDFWriter enum trailer_e { t_normal, t_lin_first, t_lin_second }; + void init(); int bytesNeeded(unsigned long n); void writeBinary(unsigned long val, unsigned int bytes); void writeString(std::string const& str); @@ -253,6 +283,7 @@ class QPDFWriter // clearPipelineStack is called. Pipeline* pushPipeline(Pipeline*); void activatePipelineStack(); + void initializePipelineStack(Pipeline *); // Calls finish on the current pipeline and pops the pipeline // stack until the top of stack is a previous active top of stack, @@ -269,6 +300,8 @@ class QPDFWriter char const* filename; FILE* file; bool close_file; + Pl_Buffer* buffer_pipeline; + Buffer* output_buffer; bool normalize_content_set; bool normalize_content; bool stream_data_mode_set; diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 40368f69..7217ded7 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -21,32 +20,65 @@ #include -QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : - pdf(pdf), - filename(filename), - file(0), - close_file(false), - normalize_content_set(false), - normalize_content(false), - stream_data_mode_set(false), - stream_data_mode(qpdf_s_compress), - qdf_mode(false), - static_id(false), - suppress_original_object_ids(false), - direct_stream_lengths(true), - encrypted(false), - preserve_encryption(true), - linearized(false), - object_stream_mode(qpdf_o_preserve), - encrypt_metadata(true), - encrypt_use_aes(false), - encryption_dict_objid(0), - next_objid(1), - cur_stream_length_id(0), - cur_stream_length(0), - added_newline(false), - max_ostream_index(0) +QPDFWriter::QPDFWriter(QPDF& pdf) : + pdf(pdf) { + init(); +} + +QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : + pdf(pdf) +{ + init(); + setOutputFilename(filename); +} + +void +QPDFWriter::init() +{ + filename = 0; + file = 0; + close_file = false; + buffer_pipeline = 0; + output_buffer = 0; + normalize_content_set = false; + normalize_content = false; + stream_data_mode_set = false; + stream_data_mode = qpdf_s_compress; + qdf_mode = false; + static_id = false; + suppress_original_object_ids = false; + direct_stream_lengths = true; + encrypted = false; + preserve_encryption = true; + linearized = false; + object_stream_mode = qpdf_o_preserve; + encrypt_metadata = true; + encrypt_use_aes = false; + encryption_dict_objid = 0; + next_objid = 1; + cur_stream_length_id = 0; + cur_stream_length = 0; + added_newline = false; + max_ostream_index = 0; +} + +QPDFWriter::~QPDFWriter() +{ + if (file && close_file) + { + fclose(file); + } + if (output_buffer) + { + delete output_buffer; + } +} + +void +QPDFWriter::setOutputFilename(char const* filename) +{ + this->filename = filename; if (filename == 0) { this->filename = "standard output"; @@ -61,19 +93,25 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : fopen(filename, "wb+")); close_file = true; } - Pipeline* p = new Pl_StdioFile("qdf output", file); + Pipeline* p = new Pl_StdioFile("qpdf output", file); to_delete.push_back(p); - pipeline = new Pl_Count("qdf count", p); - to_delete.push_back(pipeline); - pipeline_stack.push_back(pipeline); + initializePipelineStack(p); } -QPDFWriter::~QPDFWriter() +void +QPDFWriter::setOutputMemory() { - if (file && close_file) - { - fclose(file); - } + this->buffer_pipeline = new Pl_Buffer("qpdf output"); + to_delete.push_back(this->buffer_pipeline); + initializePipelineStack(this->buffer_pipeline); +} + +Buffer* +QPDFWriter::getBuffer() +{ + Buffer* result = this->output_buffer; + this->output_buffer = 0; + return result; } void @@ -565,6 +603,14 @@ QPDFWriter::pushPipeline(Pipeline* p) return p; } +void +QPDFWriter::initializePipelineStack(Pipeline *p) +{ + this->pipeline = new Pl_Count("qpdf count", p); + to_delete.push_back(this->pipeline); + this->pipeline_stack.push_back(this->pipeline); +} + void QPDFWriter::activatePipelineStack() { @@ -1503,6 +1549,8 @@ QPDFWriter::generateObjectStreams() void QPDFWriter::write() { + // XXX Check output + // Do preliminary setup if (this->linearized) @@ -1656,6 +1704,11 @@ QPDFWriter::write() fclose(this->file); } this->file = 0; + if (this->buffer_pipeline) + { + this->output_buffer = this->buffer_pipeline->getBuffer(); + this->buffer_pipeline = 0; + } } void diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index d551d6f2..89da9144 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -584,10 +584,18 @@ void runtest(int n, char const* filename) << std::endl; } - QPDFWriter w(pdf, "a.pdf"); + // Exercise writing to memory buffer + QPDFWriter w(pdf); + w.setOutputMemory(); w.setStaticID(true); w.setStreamDataMode(qpdf_s_preserve); w.write(); + Buffer* b = w.getBuffer(); + FILE* f = QUtil::fopen_wrapper(std::string("open a.pdf"), + fopen("a.pdf", "wb")); + fwrite(b->getBuffer(), b->getSize(), 1, f); + fclose(f); + delete b; } else {