aboutsummaryrefslogtreecommitdiffstats
path: root/examples/pdf-invert-images.cc
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2010-08-06 03:27:47 +0200
committerJay Berkenbilt <ejb@ql.org>2010-08-06 03:27:47 +0200
commitcb1d89e7635cd373fa9bcc2ad240ed08fc13d27b (patch)
tree1c47e8cc5ec490d77c2a462bfa12369dc9fef133 /examples/pdf-invert-images.cc
parentb6c7a80950c18be54540bb1ef851b35bb27e116f (diff)
downloadqpdf-cb1d89e7635cd373fa9bcc2ad240ed08fc13d27b.tar.zst
invert images example
git-svn-id: svn+q:///qpdf/trunk@1001 71b93d88-0707-0410-a8cf-f5a4172ac649
Diffstat (limited to 'examples/pdf-invert-images.cc')
-rw-r--r--examples/pdf-invert-images.cc172
1 files changed, 172 insertions, 0 deletions
diff --git a/examples/pdf-invert-images.cc b/examples/pdf-invert-images.cc
new file mode 100644
index 00000000..e84181bc
--- /dev/null
+++ b/examples/pdf-invert-images.cc
@@ -0,0 +1,172 @@
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+#include <qpdf/QPDF.hh>
+#include <qpdf/QUtil.hh>
+#include <qpdf/Buffer.hh>
+#include <qpdf/QPDFWriter.hh>
+
+static char const* whoami = 0;
+
+void usage()
+{
+ std::cerr << "Usage: " << whoami << " infile.pdf outfile.pdf [in-password]"
+ << std::endl
+ << "Invert some images in infile.pdf;"
+ << " write output to outfile.pdf" << std::endl;
+ exit(2);
+}
+
+// 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.
+class ImageInverter: public QPDFObjectHandle::StreamDataProvider
+{
+ public:
+ virtual ~ImageInverter()
+ {
+ }
+ virtual void provideStreamData(int objid, int generation,
+ Pipeline* pipeline);
+
+ // Map [obj][gen] = image object
+ std::map<int, std::map<int, QPDFObjectHandle> > image_objects;
+ // Map [obj][gen] = image data
+ std::map<int, std::map<int, PointerHolder<Buffer> > > image_data;
+};
+
+void
+ImageInverter::provideStreamData(int objid, int generation,
+ Pipeline* pipeline)
+{
+ // 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.
+ PointerHolder<Buffer> data = this->image_data[objid][generation];
+ size_t size = data.getPointer()->getSize();
+ unsigned char* buf = data.getPointer()->getBuffer();
+ unsigned char ch;
+ for (size_t i = 0; i < size; ++i)
+ {
+ ch = (unsigned char)0xff - buf[i];
+ pipeline->write(&ch, 1);
+ }
+ pipeline->finish();
+}
+
+int main(int argc, char* argv[])
+{
+ whoami = QUtil::getWhoami(argv[0]);
+
+ // For libtool's sake....
+ if (strncmp(whoami, "lt-", 3) == 0)
+ {
+ whoami += 3;
+ }
+
+ if (! ((argc == 3) || (argc == 4)))
+ {
+ usage();
+ }
+
+ char const* infilename = argv[1];
+ char const* outfilename = argv[2];
+ char const* password = (argc == 4) ? argv[3] : "";
+
+ try
+ {
+ QPDF qpdf;
+ qpdf.processFile(infilename, password);
+
+ ImageInverter* inv = new ImageInverter;
+ PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv;
+
+ // For each page...
+ std::vector<QPDFObjectHandle> pages = qpdf.getAllPages();
+ for (std::vector<QPDFObjectHandle>::iterator iter = pages.begin();
+ iter != pages.end(); ++iter)
+ {
+ QPDFObjectHandle& page = *iter;
+ // Get all images on the page.
+ std::map<std::string, QPDFObjectHandle> images =
+ page.getPageImages();
+ for (std::map<std::string, QPDFObjectHandle>::iterator iter =
+ images.begin();
+ iter != images.end(); ++iter)
+ {
+ QPDFObjectHandle& image = (*iter).second;
+ QPDFObjectHandle image_dict = image.getDict();
+ QPDFObjectHandle color_space =
+ image_dict.getKey("/ColorSpace");
+ QPDFObjectHandle bits_per_component =
+ image_dict.getKey("/BitsPerComponent");
+
+ // For our example, we can only work with images 8-bit
+ // grayscale images that we can fully decode. Use
+ // pipeStreamData with a null pipeline to determine
+ // whether the image is filterable. Directly inspect
+ // keys to determine the image type.
+ if (image.pipeStreamData(0, true, false, false) &&
+ 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.
+ int objid = image.getObjectID();
+ int gen = image.getGeneration();
+ if (inv->image_objects[objid].count(gen) == 0)
+ {
+ inv->image_objects[objid][gen] = image;
+ inv->image_data[objid][gen] = 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->image_data[objid][gen].getPointer()->
+ getSize());
+ }
+ }
+ }
+ }
+
+ // Write out a new file
+ QPDFWriter w(qpdf, outfilename);
+ if (QUtil::get_env("IN_TESTSUITE"))
+ {
+ // For the test suite, uncompress streams and use static
+ // IDs.
+ w.setStaticID(true);
+ }
+ w.write();
+ std::cout << whoami << ": new file written to " << outfilename
+ << std::endl;
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << whoami << " processing file " << infilename << ": "
+ << e.what() << std::endl;
+ exit(2);
+ }
+
+ return 0;
+}