aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--TODO11
-rw-r--r--examples/build.mk7
-rw-r--r--examples/pdf-invert-images.cc172
-rw-r--r--examples/qtest/invert-images.test32
-rw-r--r--examples/qtest/invert-images/in.pdfbin0 -> 4328 bytes
-rw-r--r--examples/qtest/invert-images/out.pdfbin0 -> 4328 bytes
7 files changed, 215 insertions, 13 deletions
diff --git a/ChangeLog b/ChangeLog
index 09781c14..d2e65e7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,13 +2,13 @@
* Add QPDFObjectHandle::addPageContents, a convenience routine for
appending or prepending new streams to a page's content streams.
- The "double-page-size" example illustrates its use.
+ The "pdf-double-page-size" example illustrates its use.
* Add new methods to QPDFObjectHandle: replaceStreamData and
newStream. These methods allow users of the qpdf library to add
new streams and to replace data of existing streams. The
- "double-page-size" and "tweak-images" examples illustrate their
- use.
+ "pdf-double-page-size" and "pdf-invert-images" examples illustrate
+ their use.
2010-06-06 Jay Berkenbilt <ejb@ql.org>
diff --git a/TODO b/TODO
index 7f227149..5495c8cf 100644
--- a/TODO
+++ b/TODO
@@ -5,20 +5,13 @@ Next
* Find messages exchanged with Stefan Heinsen <stefan.heinsen@gmx.de>
in August, 2009. He seems to like to send encrypted mail (key
- 01FCC336). Tell him about newStream and replaceStreamData.
+ 01FCC336). Tell him about newStream, replaceStreamData, and the
+ invert-images example.
* Tell stronghorse@tom.com about QPDFObjectHandle::addPageContents
and double-page-size example. See message from stronghorse@tom.com
("Suggestion for qpdf") from 2010-06-09 and my response.
-2.2
-===
-
- * Create an example (tweak-images) that does some kind of
- manipulation on every image. Use QPDF::getAllPages and
- QPDFObjectHandle::getPageImages along with new stream data and
- dictionary manipulation.
-
General
=======
diff --git a/examples/build.mk b/examples/build.mk
index f2e0432d..38e4c3cc 100644
--- a/examples/build.mk
+++ b/examples/build.mk
@@ -1,4 +1,9 @@
-BINS_examples = pdf-bookmarks pdf-mod-info pdf-npages double-page-size
+BINS_examples = \
+ pdf-bookmarks \
+ pdf-mod-info \
+ pdf-npages \
+ pdf-double-page-size \
+ pdf-invert-images
CBINS_examples = pdf-linearize
TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B)))
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;
+}
diff --git a/examples/qtest/invert-images.test b/examples/qtest/invert-images.test
new file mode 100644
index 00000000..0dc15a28
--- /dev/null
+++ b/examples/qtest/invert-images.test
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+require 5.008;
+BEGIN { $^W = 1; }
+use strict;
+
+chdir("invert-images") or die "chdir testdir failed: $!\n";
+
+require TestDriver;
+
+my $td = new TestDriver('invert-images');
+
+cleanup();
+
+$td->runtest("double page size",
+ {$td->COMMAND => "pdf-invert-images in.pdf a.pdf"},
+ {$td->STRING =>
+ "pdf-invert-images: new file written to a.pdf\n",
+ $td->EXIT_STATUS => 0},
+ $td->NORMALIZE_NEWLINES);
+
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "out.pdf"});
+
+cleanup();
+
+$td->report(2);
+
+sub cleanup
+{
+ unlink 'a.pdf';
+}
diff --git a/examples/qtest/invert-images/in.pdf b/examples/qtest/invert-images/in.pdf
new file mode 100644
index 00000000..30a16494
--- /dev/null
+++ b/examples/qtest/invert-images/in.pdf
Binary files differ
diff --git a/examples/qtest/invert-images/out.pdf b/examples/qtest/invert-images/out.pdf
new file mode 100644
index 00000000..cd3c5915
--- /dev/null
+++ b/examples/qtest/invert-images/out.pdf
Binary files differ