Commit 759c56e1fed2849b77bff18f2a50639876395e5e
1 parent
655c55f8
implement ability to save PDF to memory, also update ChangeLog
Showing
4 changed files
with
150 additions
and
39 deletions
ChangeLog
| 1 | +2011-08-10 Jay Berkenbilt <ejb@ql.org> | ||
| 2 | + | ||
| 3 | + * include/qpdf/QPDFWriter.hh: add a new constructor that takes | ||
| 4 | + only a QPDF reference and leaves specification of output for | ||
| 5 | + later. Add methods setOutputFilename() to set the output to a | ||
| 6 | + filename or stdout, and setOutputMemory() to indicate that output | ||
| 7 | + should go to a memory buffer. Add method getBuffer() to retrieve | ||
| 8 | + the buffer used if output was saved to a memory buffer. | ||
| 9 | + | ||
| 10 | + * include/qpdf/QPDF.hh: add methods replaceObject() and | ||
| 11 | + swapObjects() to allow replacement of an object and swapping of | ||
| 12 | + two objects by object ID. | ||
| 13 | + | ||
| 14 | + * include/qpdf/QPDFObjectHandle.hh: add new methods getDictAsMap() | ||
| 15 | + and getArrayAsVector() for returning the elements of a dictionary | ||
| 16 | + or an array as a map or vector. | ||
| 17 | + | ||
| 1 | 2011-06-25 Jay Berkenbilt <ejb@ql.org> | 18 | 2011-06-25 Jay Berkenbilt <ejb@ql.org> |
| 2 | 19 | ||
| 3 | * 2.2.4: release | 20 | * 2.2.4: release |
include/qpdf/QPDFWriter.hh
| @@ -24,6 +24,7 @@ | @@ -24,6 +24,7 @@ | ||
| 24 | 24 | ||
| 25 | #include <qpdf/QPDFXRefEntry.hh> | 25 | #include <qpdf/QPDFXRefEntry.hh> |
| 26 | 26 | ||
| 27 | +#include <qpdf/Pl_Buffer.hh> | ||
| 27 | #include <qpdf/PointerHolder.hh> | 28 | #include <qpdf/PointerHolder.hh> |
| 28 | #include <qpdf/Pipeline.hh> | 29 | #include <qpdf/Pipeline.hh> |
| 29 | #include <qpdf/Buffer.hh> | 30 | #include <qpdf/Buffer.hh> |
| @@ -35,6 +36,24 @@ class Pl_Count; | @@ -35,6 +36,24 @@ class Pl_Count; | ||
| 35 | class QPDFWriter | 36 | class QPDFWriter |
| 36 | { | 37 | { |
| 37 | public: | 38 | public: |
| 39 | + // Construct a QPDFWriter object without specifying output. You | ||
| 40 | + // must call one of the output setting routines defined below. | ||
| 41 | + QPDF_DLL | ||
| 42 | + QPDFWriter(QPDF& pdf); | ||
| 43 | + | ||
| 44 | + // Create a QPDFWriter object that writes its output to a file or | ||
| 45 | + // to stdout. This is equivalent to using the previous | ||
| 46 | + // constructor and then calling setOutputFilename(). See | ||
| 47 | + // setOutputFilename() for details. | ||
| 48 | + QPDF_DLL | ||
| 49 | + QPDFWriter(QPDF& pdf, char const* filename); | ||
| 50 | + QPDF_DLL | ||
| 51 | + ~QPDFWriter(); | ||
| 52 | + | ||
| 53 | + // Setting Output. Output may be set only one time. If you don't | ||
| 54 | + // use the filename version of the QPDFWriter constructor, you | ||
| 55 | + // must call exactly one of these methods. | ||
| 56 | + | ||
| 38 | // Passing null as filename means write to stdout. QPDFWriter | 57 | // Passing null as filename means write to stdout. QPDFWriter |
| 39 | // will create a zero-length output file upon construction. If | 58 | // will create a zero-length output file upon construction. If |
| 40 | // write fails, the empty or partially written file will not be | 59 | // write fails, the empty or partially written file will not be |
| @@ -42,10 +61,20 @@ class QPDFWriter | @@ -42,10 +61,20 @@ class QPDFWriter | ||
| 42 | // useful for tracking down problems. If your application doesn't | 61 | // useful for tracking down problems. If your application doesn't |
| 43 | // want the partially written file to be left behind, you should | 62 | // want the partially written file to be left behind, you should |
| 44 | // delete it the eventual call to write fails. | 63 | // delete it the eventual call to write fails. |
| 45 | - QPDF_DLL | ||
| 46 | - QPDFWriter(QPDF& pdf, char const* filename); | ||
| 47 | - QPDF_DLL | ||
| 48 | - ~QPDFWriter(); | 64 | + void setOutputFilename(char const* filename); |
| 65 | + | ||
| 66 | + // Indicate that QPDFWriter should create a memory buffer to | ||
| 67 | + // contain the final PDF file. Obtain the memory by calling | ||
| 68 | + // getBuffer(). | ||
| 69 | + void setOutputMemory(); | ||
| 70 | + | ||
| 71 | + // Return the buffer object containing the PDF file. If | ||
| 72 | + // setOutputMemory() has been called, this method may be called | ||
| 73 | + // exactly one time after write() has returned. The caller is | ||
| 74 | + // responsible for deleting the buffer when done. | ||
| 75 | + Buffer* getBuffer(); | ||
| 76 | + | ||
| 77 | + // Setting Parameters | ||
| 49 | 78 | ||
| 50 | // Set the value of object stream mode. In disable mode, we never | 79 | // Set the value of object stream mode. In disable mode, we never |
| 51 | // generate any object streams. In preserve mode, we preserve | 80 | // generate any object streams. In preserve mode, we preserve |
| @@ -177,6 +206,7 @@ class QPDFWriter | @@ -177,6 +206,7 @@ class QPDFWriter | ||
| 177 | 206 | ||
| 178 | enum trailer_e { t_normal, t_lin_first, t_lin_second }; | 207 | enum trailer_e { t_normal, t_lin_first, t_lin_second }; |
| 179 | 208 | ||
| 209 | + void init(); | ||
| 180 | int bytesNeeded(unsigned long n); | 210 | int bytesNeeded(unsigned long n); |
| 181 | void writeBinary(unsigned long val, unsigned int bytes); | 211 | void writeBinary(unsigned long val, unsigned int bytes); |
| 182 | void writeString(std::string const& str); | 212 | void writeString(std::string const& str); |
| @@ -253,6 +283,7 @@ class QPDFWriter | @@ -253,6 +283,7 @@ class QPDFWriter | ||
| 253 | // clearPipelineStack is called. | 283 | // clearPipelineStack is called. |
| 254 | Pipeline* pushPipeline(Pipeline*); | 284 | Pipeline* pushPipeline(Pipeline*); |
| 255 | void activatePipelineStack(); | 285 | void activatePipelineStack(); |
| 286 | + void initializePipelineStack(Pipeline *); | ||
| 256 | 287 | ||
| 257 | // Calls finish on the current pipeline and pops the pipeline | 288 | // Calls finish on the current pipeline and pops the pipeline |
| 258 | // stack until the top of stack is a previous active top of stack, | 289 | // stack until the top of stack is a previous active top of stack, |
| @@ -269,6 +300,8 @@ class QPDFWriter | @@ -269,6 +300,8 @@ class QPDFWriter | ||
| 269 | char const* filename; | 300 | char const* filename; |
| 270 | FILE* file; | 301 | FILE* file; |
| 271 | bool close_file; | 302 | bool close_file; |
| 303 | + Pl_Buffer* buffer_pipeline; | ||
| 304 | + Buffer* output_buffer; | ||
| 272 | bool normalize_content_set; | 305 | bool normalize_content_set; |
| 273 | bool normalize_content; | 306 | bool normalize_content; |
| 274 | bool stream_data_mode_set; | 307 | bool stream_data_mode_set; |
libqpdf/QPDFWriter.cc
| @@ -4,7 +4,6 @@ | @@ -4,7 +4,6 @@ | ||
| 4 | #include <qpdf/Pl_StdioFile.hh> | 4 | #include <qpdf/Pl_StdioFile.hh> |
| 5 | #include <qpdf/Pl_Count.hh> | 5 | #include <qpdf/Pl_Count.hh> |
| 6 | #include <qpdf/Pl_Discard.hh> | 6 | #include <qpdf/Pl_Discard.hh> |
| 7 | -#include <qpdf/Pl_Buffer.hh> | ||
| 8 | #include <qpdf/Pl_RC4.hh> | 7 | #include <qpdf/Pl_RC4.hh> |
| 9 | #include <qpdf/Pl_AES_PDF.hh> | 8 | #include <qpdf/Pl_AES_PDF.hh> |
| 10 | #include <qpdf/Pl_Flate.hh> | 9 | #include <qpdf/Pl_Flate.hh> |
| @@ -21,32 +20,65 @@ | @@ -21,32 +20,65 @@ | ||
| 21 | 20 | ||
| 22 | #include <stdlib.h> | 21 | #include <stdlib.h> |
| 23 | 22 | ||
| 23 | +QPDFWriter::QPDFWriter(QPDF& pdf) : | ||
| 24 | + pdf(pdf) | ||
| 25 | +{ | ||
| 26 | + init(); | ||
| 27 | +} | ||
| 28 | + | ||
| 24 | QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : | 29 | QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : |
| 25 | - pdf(pdf), | ||
| 26 | - filename(filename), | ||
| 27 | - file(0), | ||
| 28 | - close_file(false), | ||
| 29 | - normalize_content_set(false), | ||
| 30 | - normalize_content(false), | ||
| 31 | - stream_data_mode_set(false), | ||
| 32 | - stream_data_mode(qpdf_s_compress), | ||
| 33 | - qdf_mode(false), | ||
| 34 | - static_id(false), | ||
| 35 | - suppress_original_object_ids(false), | ||
| 36 | - direct_stream_lengths(true), | ||
| 37 | - encrypted(false), | ||
| 38 | - preserve_encryption(true), | ||
| 39 | - linearized(false), | ||
| 40 | - object_stream_mode(qpdf_o_preserve), | ||
| 41 | - encrypt_metadata(true), | ||
| 42 | - encrypt_use_aes(false), | ||
| 43 | - encryption_dict_objid(0), | ||
| 44 | - next_objid(1), | ||
| 45 | - cur_stream_length_id(0), | ||
| 46 | - cur_stream_length(0), | ||
| 47 | - added_newline(false), | ||
| 48 | - max_ostream_index(0) | 30 | + pdf(pdf) |
| 31 | +{ | ||
| 32 | + init(); | ||
| 33 | + setOutputFilename(filename); | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +void | ||
| 37 | +QPDFWriter::init() | ||
| 38 | +{ | ||
| 39 | + filename = 0; | ||
| 40 | + file = 0; | ||
| 41 | + close_file = false; | ||
| 42 | + buffer_pipeline = 0; | ||
| 43 | + output_buffer = 0; | ||
| 44 | + normalize_content_set = false; | ||
| 45 | + normalize_content = false; | ||
| 46 | + stream_data_mode_set = false; | ||
| 47 | + stream_data_mode = qpdf_s_compress; | ||
| 48 | + qdf_mode = false; | ||
| 49 | + static_id = false; | ||
| 50 | + suppress_original_object_ids = false; | ||
| 51 | + direct_stream_lengths = true; | ||
| 52 | + encrypted = false; | ||
| 53 | + preserve_encryption = true; | ||
| 54 | + linearized = false; | ||
| 55 | + object_stream_mode = qpdf_o_preserve; | ||
| 56 | + encrypt_metadata = true; | ||
| 57 | + encrypt_use_aes = false; | ||
| 58 | + encryption_dict_objid = 0; | ||
| 59 | + next_objid = 1; | ||
| 60 | + cur_stream_length_id = 0; | ||
| 61 | + cur_stream_length = 0; | ||
| 62 | + added_newline = false; | ||
| 63 | + max_ostream_index = 0; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +QPDFWriter::~QPDFWriter() | ||
| 67 | +{ | ||
| 68 | + if (file && close_file) | ||
| 69 | + { | ||
| 70 | + fclose(file); | ||
| 71 | + } | ||
| 72 | + if (output_buffer) | ||
| 73 | + { | ||
| 74 | + delete output_buffer; | ||
| 75 | + } | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +void | ||
| 79 | +QPDFWriter::setOutputFilename(char const* filename) | ||
| 49 | { | 80 | { |
| 81 | + this->filename = filename; | ||
| 50 | if (filename == 0) | 82 | if (filename == 0) |
| 51 | { | 83 | { |
| 52 | this->filename = "standard output"; | 84 | this->filename = "standard output"; |
| @@ -61,19 +93,25 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : | @@ -61,19 +93,25 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : | ||
| 61 | fopen(filename, "wb+")); | 93 | fopen(filename, "wb+")); |
| 62 | close_file = true; | 94 | close_file = true; |
| 63 | } | 95 | } |
| 64 | - Pipeline* p = new Pl_StdioFile("qdf output", file); | 96 | + Pipeline* p = new Pl_StdioFile("qpdf output", file); |
| 65 | to_delete.push_back(p); | 97 | to_delete.push_back(p); |
| 66 | - pipeline = new Pl_Count("qdf count", p); | ||
| 67 | - to_delete.push_back(pipeline); | ||
| 68 | - pipeline_stack.push_back(pipeline); | 98 | + initializePipelineStack(p); |
| 69 | } | 99 | } |
| 70 | 100 | ||
| 71 | -QPDFWriter::~QPDFWriter() | 101 | +void |
| 102 | +QPDFWriter::setOutputMemory() | ||
| 72 | { | 103 | { |
| 73 | - if (file && close_file) | ||
| 74 | - { | ||
| 75 | - fclose(file); | ||
| 76 | - } | 104 | + this->buffer_pipeline = new Pl_Buffer("qpdf output"); |
| 105 | + to_delete.push_back(this->buffer_pipeline); | ||
| 106 | + initializePipelineStack(this->buffer_pipeline); | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +Buffer* | ||
| 110 | +QPDFWriter::getBuffer() | ||
| 111 | +{ | ||
| 112 | + Buffer* result = this->output_buffer; | ||
| 113 | + this->output_buffer = 0; | ||
| 114 | + return result; | ||
| 77 | } | 115 | } |
| 78 | 116 | ||
| 79 | void | 117 | void |
| @@ -566,6 +604,14 @@ QPDFWriter::pushPipeline(Pipeline* p) | @@ -566,6 +604,14 @@ QPDFWriter::pushPipeline(Pipeline* p) | ||
| 566 | } | 604 | } |
| 567 | 605 | ||
| 568 | void | 606 | void |
| 607 | +QPDFWriter::initializePipelineStack(Pipeline *p) | ||
| 608 | +{ | ||
| 609 | + this->pipeline = new Pl_Count("qpdf count", p); | ||
| 610 | + to_delete.push_back(this->pipeline); | ||
| 611 | + this->pipeline_stack.push_back(this->pipeline); | ||
| 612 | +} | ||
| 613 | + | ||
| 614 | +void | ||
| 569 | QPDFWriter::activatePipelineStack() | 615 | QPDFWriter::activatePipelineStack() |
| 570 | { | 616 | { |
| 571 | Pl_Count* c = new Pl_Count("count", this->pipeline_stack.back()); | 617 | Pl_Count* c = new Pl_Count("count", this->pipeline_stack.back()); |
| @@ -1503,6 +1549,8 @@ QPDFWriter::generateObjectStreams() | @@ -1503,6 +1549,8 @@ QPDFWriter::generateObjectStreams() | ||
| 1503 | void | 1549 | void |
| 1504 | QPDFWriter::write() | 1550 | QPDFWriter::write() |
| 1505 | { | 1551 | { |
| 1552 | + // XXX Check output | ||
| 1553 | + | ||
| 1506 | // Do preliminary setup | 1554 | // Do preliminary setup |
| 1507 | 1555 | ||
| 1508 | if (this->linearized) | 1556 | if (this->linearized) |
| @@ -1656,6 +1704,11 @@ QPDFWriter::write() | @@ -1656,6 +1704,11 @@ QPDFWriter::write() | ||
| 1656 | fclose(this->file); | 1704 | fclose(this->file); |
| 1657 | } | 1705 | } |
| 1658 | this->file = 0; | 1706 | this->file = 0; |
| 1707 | + if (this->buffer_pipeline) | ||
| 1708 | + { | ||
| 1709 | + this->output_buffer = this->buffer_pipeline->getBuffer(); | ||
| 1710 | + this->buffer_pipeline = 0; | ||
| 1711 | + } | ||
| 1659 | } | 1712 | } |
| 1660 | 1713 | ||
| 1661 | void | 1714 | void |
qpdf/test_driver.cc
| @@ -584,10 +584,18 @@ void runtest(int n, char const* filename) | @@ -584,10 +584,18 @@ void runtest(int n, char const* filename) | ||
| 584 | << std::endl; | 584 | << std::endl; |
| 585 | } | 585 | } |
| 586 | 586 | ||
| 587 | - QPDFWriter w(pdf, "a.pdf"); | 587 | + // Exercise writing to memory buffer |
| 588 | + QPDFWriter w(pdf); | ||
| 589 | + w.setOutputMemory(); | ||
| 588 | w.setStaticID(true); | 590 | w.setStaticID(true); |
| 589 | w.setStreamDataMode(qpdf_s_preserve); | 591 | w.setStreamDataMode(qpdf_s_preserve); |
| 590 | w.write(); | 592 | w.write(); |
| 593 | + Buffer* b = w.getBuffer(); | ||
| 594 | + FILE* f = QUtil::fopen_wrapper(std::string("open a.pdf"), | ||
| 595 | + fopen("a.pdf", "wb")); | ||
| 596 | + fwrite(b->getBuffer(), b->getSize(), 1, f); | ||
| 597 | + fclose(f); | ||
| 598 | + delete b; | ||
| 591 | } | 599 | } |
| 592 | else | 600 | else |
| 593 | { | 601 | { |