diff options
author | Jay Berkenbilt <ejb@ql.org> | 2010-08-06 03:27:47 +0200 |
---|---|---|
committer | Jay Berkenbilt <ejb@ql.org> | 2010-08-06 03:27:47 +0200 |
commit | cb1d89e7635cd373fa9bcc2ad240ed08fc13d27b (patch) | |
tree | 1c47e8cc5ec490d77c2a462bfa12369dc9fef133 | |
parent | b6c7a80950c18be54540bb1ef851b35bb27e116f (diff) | |
download | qpdf-cb1d89e7635cd373fa9bcc2ad240ed08fc13d27b.tar.zst |
invert images example
git-svn-id: svn+q:///qpdf/trunk@1001 71b93d88-0707-0410-a8cf-f5a4172ac649
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | TODO | 11 | ||||
-rw-r--r-- | examples/build.mk | 7 | ||||
-rw-r--r-- | examples/pdf-invert-images.cc | 172 | ||||
-rw-r--r-- | examples/qtest/invert-images.test | 32 | ||||
-rw-r--r-- | examples/qtest/invert-images/in.pdf | bin | 0 -> 4328 bytes | |||
-rw-r--r-- | examples/qtest/invert-images/out.pdf | bin | 0 -> 4328 bytes |
7 files changed, 215 insertions, 13 deletions
@@ -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> @@ -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 Binary files differnew file mode 100644 index 00000000..30a16494 --- /dev/null +++ b/examples/qtest/invert-images/in.pdf diff --git a/examples/qtest/invert-images/out.pdf b/examples/qtest/invert-images/out.pdf Binary files differnew file mode 100644 index 00000000..cd3c5915 --- /dev/null +++ b/examples/qtest/invert-images/out.pdf |