Commit 65ae8511a7f986433b5631460c963de96a5907f2
1 parent
fbac4725
Improve pdf-invert-images example
Showing
2 changed files
with
79 additions
and
40 deletions
ChangeLog
| 1 | +2020-04-07 Jay Berkenbilt <ejb@ql.org> | |
| 2 | + | |
| 3 | + * Improve pdf-invert-images example to show a pattern of copying | |
| 4 | + streams into another QPDF object to enable a stream data provider | |
| 5 | + to access the original stream data. | |
| 6 | + | |
| 7 | + * Fix error that caused a compilation error with clang. Fixes | |
| 8 | + #424. | |
| 9 | + | |
| 1 | 10 | 2020-04-06 Jay Berkenbilt <ejb@ql.org> |
| 2 | 11 | |
| 3 | 12 | * 10.0.0: release | ... | ... |
examples/pdf-invert-images.cc
| ... | ... | @@ -21,28 +21,78 @@ void usage() |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | // Derive a class from StreamDataProvider to provide updated stream |
| 24 | -// data. The main purpose of using this object is to avoid having to | |
| 25 | -// allocate memory up front for the objects. A real application might | |
| 26 | -// use temporary files in order to avoid having to allocate all the | |
| 27 | -// memory. Here, we're not going to worry about that since the goal | |
| 28 | -// is really to show how to use this facility rather than to show the | |
| 29 | -// best possible way to write an image inverter. This class still | |
| 30 | -// illustrates dynamic creation of the new stream data. | |
| 24 | +// data. The main purpose of using this object is to avoid having to | |
| 25 | +// allocate memory up front for the objects. We want to replace the | |
| 26 | +// stream data with a function of the original stream data. In order | |
| 27 | +// to do this without actually holding all the images in memory, we | |
| 28 | +// create another QPDF object and copy the streams. Copying the | |
| 29 | +// streams doesn't actually copy the data. Internally, the qpdf | |
| 30 | +// library is holding onto the location of the stream data in the | |
| 31 | +// original file, which makes it possible for the StreamDataProvider | |
| 32 | +// to access it when it needs it. | |
| 31 | 33 | class ImageInverter: public QPDFObjectHandle::StreamDataProvider |
| 32 | 34 | { |
| 33 | 35 | public: |
| 36 | + ImageInverter(); | |
| 34 | 37 | virtual ~ImageInverter() |
| 35 | 38 | { |
| 36 | 39 | } |
| 37 | 40 | virtual void provideStreamData(int objid, int generation, |
| 38 | - Pipeline* pipeline); | |
| 41 | + Pipeline* pipeline) override; | |
| 39 | 42 | |
| 40 | - // Map [og] = image object | |
| 41 | - std::map<QPDFObjGen, QPDFObjectHandle> image_objects; | |
| 42 | - // Map [og] = image data | |
| 43 | - std::map<QPDFObjGen, PointerHolder<Buffer> > image_data; | |
| 43 | + void setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider>); | |
| 44 | + void registerImage(QPDFObjectHandle image); | |
| 45 | + | |
| 46 | + private: | |
| 47 | + QPDF other; | |
| 48 | + PointerHolder<QPDFObjectHandle::StreamDataProvider> self_ph; | |
| 49 | + // Map og in original to copied image | |
| 50 | + std::map<QPDFObjGen, QPDFObjectHandle> copied_images; | |
| 44 | 51 | }; |
| 45 | 52 | |
| 53 | +ImageInverter::ImageInverter() | |
| 54 | +{ | |
| 55 | + this->other.emptyPDF(); | |
| 56 | +} | |
| 57 | + | |
| 58 | +void | |
| 59 | +ImageInverter::setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider> p) | |
| 60 | +{ | |
| 61 | + // replaceStreamData requires a pointer holder to the stream data | |
| 62 | + // provider, but there's no way for us to generate one ourselves, | |
| 63 | + // so we have to have it handed to us. | |
| 64 | + this->self_ph = p; | |
| 65 | +} | |
| 66 | + | |
| 67 | + | |
| 68 | +void | |
| 69 | +ImageInverter::registerImage(QPDFObjectHandle image) | |
| 70 | +{ | |
| 71 | + QPDFObjGen og(image.getObjGen()); | |
| 72 | + // Store information about the images based on the object and | |
| 73 | + // generation number. Recall that a single image object may be | |
| 74 | + // used more than once, so no need to update the same stream | |
| 75 | + // multiple times. | |
| 76 | + if (this->copied_images.count(og) > 0) | |
| 77 | + { | |
| 78 | + return; | |
| 79 | + } | |
| 80 | + this->copied_images[og] = this->other.copyForeignObject(image); | |
| 81 | + | |
| 82 | + // Register our stream data provider for this stream. Future calls | |
| 83 | + // to getStreamData or pipeStreamData will use the new | |
| 84 | + // information. Provide null for both filter and decode | |
| 85 | + // parameters. Note that this does not mean the image data will be | |
| 86 | + // uncompressed when we write the file. By default, QPDFWriter | |
| 87 | + // will use /FlateDecode for anything that is uncompressed or | |
| 88 | + // filterable in the input QPDF object, so we don't have to deal | |
| 89 | + // with it explicitly here. We could explicitly use /DCTDecode and | |
| 90 | + // write through a DCT filter if we wanted. | |
| 91 | + image.replaceStreamData(this->self_ph, | |
| 92 | + QPDFObjectHandle::newNull(), | |
| 93 | + QPDFObjectHandle::newNull()); | |
| 94 | +} | |
| 95 | + | |
| 46 | 96 | void |
| 47 | 97 | ImageInverter::provideStreamData(int objid, int generation, |
| 48 | 98 | Pipeline* pipeline) |
| ... | ... | @@ -50,8 +100,9 @@ ImageInverter::provideStreamData(int objid, int generation, |
| 50 | 100 | // Use the object and generation number supplied to look up the |
| 51 | 101 | // image data. Then invert the image data and write the inverted |
| 52 | 102 | // data to the pipeline. |
| 103 | + QPDFObjGen og(objid, generation); | |
| 53 | 104 | PointerHolder<Buffer> data = |
| 54 | - this->image_data[QPDFObjGen(objid, generation)]; | |
| 105 | + this->copied_images[og].getStreamData(qpdf_dl_all); | |
| 55 | 106 | size_t size = data->getSize(); |
| 56 | 107 | unsigned char* buf = data->getBuffer(); |
| 57 | 108 | unsigned char ch; |
| ... | ... | @@ -98,6 +149,9 @@ int main(int argc, char* argv[]) |
| 98 | 149 | |
| 99 | 150 | ImageInverter* inv = new ImageInverter; |
| 100 | 151 | PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv; |
| 152 | + // We need to give ImageInverter the pointer holder that it | |
| 153 | + // needs to pass to replaceStreamData. | |
| 154 | + inv->setSelfPh(p); | |
| 101 | 155 | |
| 102 | 156 | // For each page... |
| 103 | 157 | std::vector<QPDFPageObjectHelper> pages = |
| ... | ... | @@ -126,38 +180,14 @@ int main(int argc, char* argv[]) |
| 126 | 180 | // whether the image is filterable. Directly inspect |
| 127 | 181 | // keys to determine the image type. |
| 128 | 182 | if (image.pipeStreamData(0, qpdf_ef_compress, |
| 129 | - qpdf_dl_generalized) && | |
| 183 | + qpdf_dl_all) && | |
| 130 | 184 | color_space.isName() && |
| 131 | 185 | bits_per_component.isInteger() && |
| 132 | 186 | (color_space.getName() == "/DeviceGray") && |
| 133 | 187 | (bits_per_component.getIntValue() == 8)) |
| 134 | 188 | { |
| 135 | - // Store information about the images based on the | |
| 136 | - // object and generation number. Recall that a single | |
| 137 | - // image object may be used more than once. | |
| 138 | - QPDFObjGen og = image.getObjGen(); | |
| 139 | - if (inv->image_objects.count(og) == 0) | |
| 140 | - { | |
| 141 | - inv->image_objects[og] = image; | |
| 142 | - inv->image_data[og] = image.getStreamData(); | |
| 143 | - | |
| 144 | - // Register our stream data provider for this | |
| 145 | - // stream. Future calls to getStreamData or | |
| 146 | - // pipeStreamData will use the new | |
| 147 | - // information. Provide null for both filter | |
| 148 | - // and decode parameters. Note that this does | |
| 149 | - // not mean the image data will be | |
| 150 | - // uncompressed when we write the file. By | |
| 151 | - // default, QPDFWriter will use /FlateDecode | |
| 152 | - // for anything that is uncompressed or | |
| 153 | - // filterable in the input QPDF object, so we | |
| 154 | - // don't have to deal with it explicitly here. | |
| 155 | - image.replaceStreamData( | |
| 156 | - p, | |
| 157 | - QPDFObjectHandle::newNull(), | |
| 158 | - QPDFObjectHandle::newNull()); | |
| 159 | - } | |
| 160 | - } | |
| 189 | + inv->registerImage(image); | |
| 190 | + } | |
| 161 | 191 | } |
| 162 | 192 | } |
| 163 | 193 | ... | ... |