Commit 65ae8511a7f986433b5631460c963de96a5907f2

Authored by Jay Berkenbilt
1 parent fbac4725

Improve pdf-invert-images example

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 2020-04-06 Jay Berkenbilt <ejb@ql.org> 10 2020-04-06 Jay Berkenbilt <ejb@ql.org>
2 11
3 * 10.0.0: release 12 * 10.0.0: release
examples/pdf-invert-images.cc
@@ -21,28 +21,78 @@ void usage() @@ -21,28 +21,78 @@ void usage()
21 } 21 }
22 22
23 // Derive a class from StreamDataProvider to provide updated stream 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 class ImageInverter: public QPDFObjectHandle::StreamDataProvider 33 class ImageInverter: public QPDFObjectHandle::StreamDataProvider
32 { 34 {
33 public: 35 public:
  36 + ImageInverter();
34 virtual ~ImageInverter() 37 virtual ~ImageInverter()
35 { 38 {
36 } 39 }
37 virtual void provideStreamData(int objid, int generation, 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 void 96 void
47 ImageInverter::provideStreamData(int objid, int generation, 97 ImageInverter::provideStreamData(int objid, int generation,
48 Pipeline* pipeline) 98 Pipeline* pipeline)
@@ -50,8 +100,9 @@ ImageInverter::provideStreamData(int objid, int generation, @@ -50,8 +100,9 @@ ImageInverter::provideStreamData(int objid, int generation,
50 // Use the object and generation number supplied to look up the 100 // Use the object and generation number supplied to look up the
51 // image data. Then invert the image data and write the inverted 101 // image data. Then invert the image data and write the inverted
52 // data to the pipeline. 102 // data to the pipeline.
  103 + QPDFObjGen og(objid, generation);
53 PointerHolder<Buffer> data = 104 PointerHolder<Buffer> data =
54 - this->image_data[QPDFObjGen(objid, generation)]; 105 + this->copied_images[og].getStreamData(qpdf_dl_all);
55 size_t size = data->getSize(); 106 size_t size = data->getSize();
56 unsigned char* buf = data->getBuffer(); 107 unsigned char* buf = data->getBuffer();
57 unsigned char ch; 108 unsigned char ch;
@@ -98,6 +149,9 @@ int main(int argc, char* argv[]) @@ -98,6 +149,9 @@ int main(int argc, char* argv[])
98 149
99 ImageInverter* inv = new ImageInverter; 150 ImageInverter* inv = new ImageInverter;
100 PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv; 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 // For each page... 156 // For each page...
103 std::vector<QPDFPageObjectHelper> pages = 157 std::vector<QPDFPageObjectHelper> pages =
@@ -126,38 +180,14 @@ int main(int argc, char* argv[]) @@ -126,38 +180,14 @@ int main(int argc, char* argv[])
126 // whether the image is filterable. Directly inspect 180 // whether the image is filterable. Directly inspect
127 // keys to determine the image type. 181 // keys to determine the image type.
128 if (image.pipeStreamData(0, qpdf_ef_compress, 182 if (image.pipeStreamData(0, qpdf_ef_compress,
129 - qpdf_dl_generalized) && 183 + qpdf_dl_all) &&
130 color_space.isName() && 184 color_space.isName() &&
131 bits_per_component.isInteger() && 185 bits_per_component.isInteger() &&
132 (color_space.getName() == "/DeviceGray") && 186 (color_space.getName() == "/DeviceGray") &&
133 (bits_per_component.getIntValue() == 8)) 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