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 | 18 | 2011-06-25 Jay Berkenbilt <ejb@ql.org> |
| 2 | 19 | |
| 3 | 20 | * 2.2.4: release | ... | ... |
include/qpdf/QPDFWriter.hh
| ... | ... | @@ -24,6 +24,7 @@ |
| 24 | 24 | |
| 25 | 25 | #include <qpdf/QPDFXRefEntry.hh> |
| 26 | 26 | |
| 27 | +#include <qpdf/Pl_Buffer.hh> | |
| 27 | 28 | #include <qpdf/PointerHolder.hh> |
| 28 | 29 | #include <qpdf/Pipeline.hh> |
| 29 | 30 | #include <qpdf/Buffer.hh> |
| ... | ... | @@ -35,6 +36,24 @@ class Pl_Count; |
| 35 | 36 | class QPDFWriter |
| 36 | 37 | { |
| 37 | 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 | 57 | // Passing null as filename means write to stdout. QPDFWriter |
| 39 | 58 | // will create a zero-length output file upon construction. If |
| 40 | 59 | // write fails, the empty or partially written file will not be |
| ... | ... | @@ -42,10 +61,20 @@ class QPDFWriter |
| 42 | 61 | // useful for tracking down problems. If your application doesn't |
| 43 | 62 | // want the partially written file to be left behind, you should |
| 44 | 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 | 79 | // Set the value of object stream mode. In disable mode, we never |
| 51 | 80 | // generate any object streams. In preserve mode, we preserve |
| ... | ... | @@ -177,6 +206,7 @@ class QPDFWriter |
| 177 | 206 | |
| 178 | 207 | enum trailer_e { t_normal, t_lin_first, t_lin_second }; |
| 179 | 208 | |
| 209 | + void init(); | |
| 180 | 210 | int bytesNeeded(unsigned long n); |
| 181 | 211 | void writeBinary(unsigned long val, unsigned int bytes); |
| 182 | 212 | void writeString(std::string const& str); |
| ... | ... | @@ -253,6 +283,7 @@ class QPDFWriter |
| 253 | 283 | // clearPipelineStack is called. |
| 254 | 284 | Pipeline* pushPipeline(Pipeline*); |
| 255 | 285 | void activatePipelineStack(); |
| 286 | + void initializePipelineStack(Pipeline *); | |
| 256 | 287 | |
| 257 | 288 | // Calls finish on the current pipeline and pops the pipeline |
| 258 | 289 | // stack until the top of stack is a previous active top of stack, |
| ... | ... | @@ -269,6 +300,8 @@ class QPDFWriter |
| 269 | 300 | char const* filename; |
| 270 | 301 | FILE* file; |
| 271 | 302 | bool close_file; |
| 303 | + Pl_Buffer* buffer_pipeline; | |
| 304 | + Buffer* output_buffer; | |
| 272 | 305 | bool normalize_content_set; |
| 273 | 306 | bool normalize_content; |
| 274 | 307 | bool stream_data_mode_set; | ... | ... |
libqpdf/QPDFWriter.cc
| ... | ... | @@ -4,7 +4,6 @@ |
| 4 | 4 | #include <qpdf/Pl_StdioFile.hh> |
| 5 | 5 | #include <qpdf/Pl_Count.hh> |
| 6 | 6 | #include <qpdf/Pl_Discard.hh> |
| 7 | -#include <qpdf/Pl_Buffer.hh> | |
| 8 | 7 | #include <qpdf/Pl_RC4.hh> |
| 9 | 8 | #include <qpdf/Pl_AES_PDF.hh> |
| 10 | 9 | #include <qpdf/Pl_Flate.hh> |
| ... | ... | @@ -21,32 +20,65 @@ |
| 21 | 20 | |
| 22 | 21 | #include <stdlib.h> |
| 23 | 22 | |
| 23 | +QPDFWriter::QPDFWriter(QPDF& pdf) : | |
| 24 | + pdf(pdf) | |
| 25 | +{ | |
| 26 | + init(); | |
| 27 | +} | |
| 28 | + | |
| 24 | 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 | 82 | if (filename == 0) |
| 51 | 83 | { |
| 52 | 84 | this->filename = "standard output"; |
| ... | ... | @@ -61,19 +93,25 @@ QPDFWriter::QPDFWriter(QPDF& pdf, char const* filename) : |
| 61 | 93 | fopen(filename, "wb+")); |
| 62 | 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 | 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 | 117 | void |
| ... | ... | @@ -566,6 +604,14 @@ QPDFWriter::pushPipeline(Pipeline* p) |
| 566 | 604 | } |
| 567 | 605 | |
| 568 | 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 | 615 | QPDFWriter::activatePipelineStack() |
| 570 | 616 | { |
| 571 | 617 | Pl_Count* c = new Pl_Count("count", this->pipeline_stack.back()); |
| ... | ... | @@ -1503,6 +1549,8 @@ QPDFWriter::generateObjectStreams() |
| 1503 | 1549 | void |
| 1504 | 1550 | QPDFWriter::write() |
| 1505 | 1551 | { |
| 1552 | + // XXX Check output | |
| 1553 | + | |
| 1506 | 1554 | // Do preliminary setup |
| 1507 | 1555 | |
| 1508 | 1556 | if (this->linearized) |
| ... | ... | @@ -1656,6 +1704,11 @@ QPDFWriter::write() |
| 1656 | 1704 | fclose(this->file); |
| 1657 | 1705 | } |
| 1658 | 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 | 1714 | void | ... | ... |
qpdf/test_driver.cc
| ... | ... | @@ -584,10 +584,18 @@ void runtest(int n, char const* filename) |
| 584 | 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 | 590 | w.setStaticID(true); |
| 589 | 591 | w.setStreamDataMode(qpdf_s_preserve); |
| 590 | 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 | 600 | else |
| 593 | 601 | { | ... | ... |