aboutsummaryrefslogtreecommitdiffstats
path: root/examples/pdf-invert-images.cc
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2020-04-08 00:11:00 +0200
committerJay Berkenbilt <ejb@ql.org>2020-04-08 00:11:00 +0200
commit65ae8511a7f986433b5631460c963de96a5907f2 (patch)
tree09061af3c2c07a7a249c7c1eea05ac9715352622 /examples/pdf-invert-images.cc
parentfbac472510d8ae51872aa8a8ae1dba8f6abdccbb (diff)
downloadqpdf-65ae8511a7f986433b5631460c963de96a5907f2.tar.zst
Improve pdf-invert-images example
Diffstat (limited to 'examples/pdf-invert-images.cc')
-rw-r--r--examples/pdf-invert-images.cc110
1 files changed, 70 insertions, 40 deletions
diff --git a/examples/pdf-invert-images.cc b/examples/pdf-invert-images.cc
index a149c495..37af1b73 100644
--- a/examples/pdf-invert-images.cc
+++ b/examples/pdf-invert-images.cc
@@ -21,28 +21,78 @@ void usage()
}
// Derive a class from StreamDataProvider to provide updated stream
-// data. The main purpose of using this object is to avoid having to
-// allocate memory up front for the objects. A real application might
-// use temporary files in order to avoid having to allocate all the
-// memory. Here, we're not going to worry about that since the goal
-// is really to show how to use this facility rather than to show the
-// best possible way to write an image inverter. This class still
-// illustrates dynamic creation of the new stream data.
+// data. The main purpose of using this object is to avoid having to
+// allocate memory up front for the objects. We want to replace the
+// stream data with a function of the original stream data. In order
+// to do this without actually holding all the images in memory, we
+// create another QPDF object and copy the streams. Copying the
+// streams doesn't actually copy the data. Internally, the qpdf
+// library is holding onto the location of the stream data in the
+// original file, which makes it possible for the StreamDataProvider
+// to access it when it needs it.
class ImageInverter: public QPDFObjectHandle::StreamDataProvider
{
public:
+ ImageInverter();
virtual ~ImageInverter()
{
}
virtual void provideStreamData(int objid, int generation,
- Pipeline* pipeline);
+ Pipeline* pipeline) override;
- // Map [og] = image object
- std::map<QPDFObjGen, QPDFObjectHandle> image_objects;
- // Map [og] = image data
- std::map<QPDFObjGen, PointerHolder<Buffer> > image_data;
+ void setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider>);
+ void registerImage(QPDFObjectHandle image);
+
+ private:
+ QPDF other;
+ PointerHolder<QPDFObjectHandle::StreamDataProvider> self_ph;
+ // Map og in original to copied image
+ std::map<QPDFObjGen, QPDFObjectHandle> copied_images;
};
+ImageInverter::ImageInverter()
+{
+ this->other.emptyPDF();
+}
+
+void
+ImageInverter::setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider> p)
+{
+ // replaceStreamData requires a pointer holder to the stream data
+ // provider, but there's no way for us to generate one ourselves,
+ // so we have to have it handed to us.
+ this->self_ph = p;
+}
+
+
+void
+ImageInverter::registerImage(QPDFObjectHandle image)
+{
+ QPDFObjGen og(image.getObjGen());
+ // Store information about the images based on the object and
+ // generation number. Recall that a single image object may be
+ // used more than once, so no need to update the same stream
+ // multiple times.
+ if (this->copied_images.count(og) > 0)
+ {
+ return;
+ }
+ this->copied_images[og] = this->other.copyForeignObject(image);
+
+ // Register our stream data provider for this stream. Future calls
+ // to getStreamData or pipeStreamData will use the new
+ // information. Provide null for both filter and decode
+ // parameters. Note that this does not mean the image data will be
+ // uncompressed when we write the file. By default, QPDFWriter
+ // will use /FlateDecode for anything that is uncompressed or
+ // filterable in the input QPDF object, so we don't have to deal
+ // with it explicitly here. We could explicitly use /DCTDecode and
+ // write through a DCT filter if we wanted.
+ image.replaceStreamData(this->self_ph,
+ QPDFObjectHandle::newNull(),
+ QPDFObjectHandle::newNull());
+}
+
void
ImageInverter::provideStreamData(int objid, int generation,
Pipeline* pipeline)
@@ -50,8 +100,9 @@ ImageInverter::provideStreamData(int objid, int generation,
// Use the object and generation number supplied to look up the
// image data. Then invert the image data and write the inverted
// data to the pipeline.
+ QPDFObjGen og(objid, generation);
PointerHolder<Buffer> data =
- this->image_data[QPDFObjGen(objid, generation)];
+ this->copied_images[og].getStreamData(qpdf_dl_all);
size_t size = data->getSize();
unsigned char* buf = data->getBuffer();
unsigned char ch;
@@ -98,6 +149,9 @@ int main(int argc, char* argv[])
ImageInverter* inv = new ImageInverter;
PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv;
+ // We need to give ImageInverter the pointer holder that it
+ // needs to pass to replaceStreamData.
+ inv->setSelfPh(p);
// For each page...
std::vector<QPDFPageObjectHelper> pages =
@@ -126,38 +180,14 @@ int main(int argc, char* argv[])
// whether the image is filterable. Directly inspect
// keys to determine the image type.
if (image.pipeStreamData(0, qpdf_ef_compress,
- qpdf_dl_generalized) &&
+ qpdf_dl_all) &&
color_space.isName() &&
bits_per_component.isInteger() &&
(color_space.getName() == "/DeviceGray") &&
(bits_per_component.getIntValue() == 8))
{
- // Store information about the images based on the
- // object and generation number. Recall that a single
- // image object may be used more than once.
- QPDFObjGen og = image.getObjGen();
- if (inv->image_objects.count(og) == 0)
- {
- inv->image_objects[og] = image;
- inv->image_data[og] = image.getStreamData();
-
- // Register our stream data provider for this
- // stream. Future calls to getStreamData or
- // pipeStreamData will use the new
- // information. Provide null for both filter
- // and decode parameters. Note that this does
- // not mean the image data will be
- // uncompressed when we write the file. By
- // default, QPDFWriter will use /FlateDecode
- // for anything that is uncompressed or
- // filterable in the input QPDF object, so we
- // don't have to deal with it explicitly here.
- image.replaceStreamData(
- p,
- QPDFObjectHandle::newNull(),
- QPDFObjectHandle::newNull());
- }
- }
+ inv->registerImage(image);
+ }
}
}