Commit cb1d89e7635cd373fa9bcc2ad240ed08fc13d27b

Authored by Jay Berkenbilt
1 parent b6c7a809

invert images example

git-svn-id: svn+q:///qpdf/trunk@1001 71b93d88-0707-0410-a8cf-f5a4172ac649
ChangeLog
... ... @@ -2,13 +2,13 @@
2 2  
3 3 * Add QPDFObjectHandle::addPageContents, a convenience routine for
4 4 appending or prepending new streams to a page's content streams.
5   - The "double-page-size" example illustrates its use.
  5 + The "pdf-double-page-size" example illustrates its use.
6 6  
7 7 * Add new methods to QPDFObjectHandle: replaceStreamData and
8 8 newStream. These methods allow users of the qpdf library to add
9 9 new streams and to replace data of existing streams. The
10   - "double-page-size" and "tweak-images" examples illustrate their
11   - use.
  10 + "pdf-double-page-size" and "pdf-invert-images" examples illustrate
  11 + their use.
12 12  
13 13 2010-06-06 Jay Berkenbilt <ejb@ql.org>
14 14  
... ...
... ... @@ -5,20 +5,13 @@ Next
5 5  
6 6 * Find messages exchanged with Stefan Heinsen <stefan.heinsen@gmx.de>
7 7 in August, 2009. He seems to like to send encrypted mail (key
8   - 01FCC336). Tell him about newStream and replaceStreamData.
  8 + 01FCC336). Tell him about newStream, replaceStreamData, and the
  9 + invert-images example.
9 10  
10 11 * Tell stronghorse@tom.com about QPDFObjectHandle::addPageContents
11 12 and double-page-size example. See message from stronghorse@tom.com
12 13 ("Suggestion for qpdf") from 2010-06-09 and my response.
13 14  
14   -2.2
15   -===
16   -
17   - * Create an example (tweak-images) that does some kind of
18   - manipulation on every image. Use QPDF::getAllPages and
19   - QPDFObjectHandle::getPageImages along with new stream data and
20   - dictionary manipulation.
21   -
22 15  
23 16 General
24 17 =======
... ...
examples/build.mk
1   -BINS_examples = pdf-bookmarks pdf-mod-info pdf-npages double-page-size
  1 +BINS_examples = \
  2 + pdf-bookmarks \
  3 + pdf-mod-info \
  4 + pdf-npages \
  5 + pdf-double-page-size \
  6 + pdf-invert-images
2 7 CBINS_examples = pdf-linearize
3 8  
4 9 TARGETS_examples = $(foreach B,$(BINS_examples) $(CBINS_examples),examples/$(OUTPUT_DIR)/$(call binname,$(B)))
... ...
examples/pdf-invert-images.cc 0 → 100644
  1 +#include <iostream>
  2 +#include <string.h>
  3 +#include <stdlib.h>
  4 +#include <qpdf/QPDF.hh>
  5 +#include <qpdf/QUtil.hh>
  6 +#include <qpdf/Buffer.hh>
  7 +#include <qpdf/QPDFWriter.hh>
  8 +
  9 +static char const* whoami = 0;
  10 +
  11 +void usage()
  12 +{
  13 + std::cerr << "Usage: " << whoami << " infile.pdf outfile.pdf [in-password]"
  14 + << std::endl
  15 + << "Invert some images in infile.pdf;"
  16 + << " write output to outfile.pdf" << std::endl;
  17 + exit(2);
  18 +}
  19 +
  20 +// Derive a class from StreamDataProvider to provide updated stream
  21 +// data. The main purpose of using this object is to avoid having to
  22 +// allocate memory up front for the objects. A real application might
  23 +// use temporary files in order to avoid having to allocate all the
  24 +// memory. Here, we're not going to worry about that since the goal
  25 +// is really to show how to use this facility rather than to show the
  26 +// best possible way to write an image inverter. This class still
  27 +// illustrates dynamic creation of the new stream data.
  28 +class ImageInverter: public QPDFObjectHandle::StreamDataProvider
  29 +{
  30 + public:
  31 + virtual ~ImageInverter()
  32 + {
  33 + }
  34 + virtual void provideStreamData(int objid, int generation,
  35 + Pipeline* pipeline);
  36 +
  37 + // Map [obj][gen] = image object
  38 + std::map<int, std::map<int, QPDFObjectHandle> > image_objects;
  39 + // Map [obj][gen] = image data
  40 + std::map<int, std::map<int, PointerHolder<Buffer> > > image_data;
  41 +};
  42 +
  43 +void
  44 +ImageInverter::provideStreamData(int objid, int generation,
  45 + Pipeline* pipeline)
  46 +{
  47 + // Use the object and generation number supplied to look up the
  48 + // image data. Then invert the image data and write the inverted
  49 + // data to the pipeline.
  50 + PointerHolder<Buffer> data = this->image_data[objid][generation];
  51 + size_t size = data.getPointer()->getSize();
  52 + unsigned char* buf = data.getPointer()->getBuffer();
  53 + unsigned char ch;
  54 + for (size_t i = 0; i < size; ++i)
  55 + {
  56 + ch = (unsigned char)0xff - buf[i];
  57 + pipeline->write(&ch, 1);
  58 + }
  59 + pipeline->finish();
  60 +}
  61 +
  62 +int main(int argc, char* argv[])
  63 +{
  64 + whoami = QUtil::getWhoami(argv[0]);
  65 +
  66 + // For libtool's sake....
  67 + if (strncmp(whoami, "lt-", 3) == 0)
  68 + {
  69 + whoami += 3;
  70 + }
  71 +
  72 + if (! ((argc == 3) || (argc == 4)))
  73 + {
  74 + usage();
  75 + }
  76 +
  77 + char const* infilename = argv[1];
  78 + char const* outfilename = argv[2];
  79 + char const* password = (argc == 4) ? argv[3] : "";
  80 +
  81 + try
  82 + {
  83 + QPDF qpdf;
  84 + qpdf.processFile(infilename, password);
  85 +
  86 + ImageInverter* inv = new ImageInverter;
  87 + PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv;
  88 +
  89 + // For each page...
  90 + std::vector<QPDFObjectHandle> pages = qpdf.getAllPages();
  91 + for (std::vector<QPDFObjectHandle>::iterator iter = pages.begin();
  92 + iter != pages.end(); ++iter)
  93 + {
  94 + QPDFObjectHandle& page = *iter;
  95 + // Get all images on the page.
  96 + std::map<std::string, QPDFObjectHandle> images =
  97 + page.getPageImages();
  98 + for (std::map<std::string, QPDFObjectHandle>::iterator iter =
  99 + images.begin();
  100 + iter != images.end(); ++iter)
  101 + {
  102 + QPDFObjectHandle& image = (*iter).second;
  103 + QPDFObjectHandle image_dict = image.getDict();
  104 + QPDFObjectHandle color_space =
  105 + image_dict.getKey("/ColorSpace");
  106 + QPDFObjectHandle bits_per_component =
  107 + image_dict.getKey("/BitsPerComponent");
  108 +
  109 + // For our example, we can only work with images 8-bit
  110 + // grayscale images that we can fully decode. Use
  111 + // pipeStreamData with a null pipeline to determine
  112 + // whether the image is filterable. Directly inspect
  113 + // keys to determine the image type.
  114 + if (image.pipeStreamData(0, true, false, false) &&
  115 + color_space.isName() &&
  116 + bits_per_component.isInteger() &&
  117 + (color_space.getName() == "/DeviceGray") &&
  118 + (bits_per_component.getIntValue() == 8))
  119 + {
  120 + // Store information about the images based on the
  121 + // object and generation number. Recall that a single
  122 + // image object may be used more than once.
  123 + int objid = image.getObjectID();
  124 + int gen = image.getGeneration();
  125 + if (inv->image_objects[objid].count(gen) == 0)
  126 + {
  127 + inv->image_objects[objid][gen] = image;
  128 + inv->image_data[objid][gen] = image.getStreamData();
  129 +
  130 + // Register our stream data provider for this
  131 + // stream. Future calls to getStreamData or
  132 + // pipeStreamData will use the new
  133 + // information. Provide null for both filter
  134 + // and decode parameters. Note that this does
  135 + // not mean the image data will be
  136 + // uncompressed when we write the file. By
  137 + // default, QPDFWriter will use /FlateDecode
  138 + // for anything that is uncompressed or
  139 + // filterable in the input QPDF object, so we
  140 + // don't have to deal with it explicitly here.
  141 + image.replaceStreamData(
  142 + p,
  143 + QPDFObjectHandle::newNull(),
  144 + QPDFObjectHandle::newNull(),
  145 + inv->image_data[objid][gen].getPointer()->
  146 + getSize());
  147 + }
  148 + }
  149 + }
  150 + }
  151 +
  152 + // Write out a new file
  153 + QPDFWriter w(qpdf, outfilename);
  154 + if (QUtil::get_env("IN_TESTSUITE"))
  155 + {
  156 + // For the test suite, uncompress streams and use static
  157 + // IDs.
  158 + w.setStaticID(true);
  159 + }
  160 + w.write();
  161 + std::cout << whoami << ": new file written to " << outfilename
  162 + << std::endl;
  163 + }
  164 + catch (std::exception &e)
  165 + {
  166 + std::cerr << whoami << " processing file " << infilename << ": "
  167 + << e.what() << std::endl;
  168 + exit(2);
  169 + }
  170 +
  171 + return 0;
  172 +}
... ...
examples/qtest/invert-images.test 0 → 100644
  1 +#!/usr/bin/env perl
  2 +require 5.008;
  3 +BEGIN { $^W = 1; }
  4 +use strict;
  5 +
  6 +chdir("invert-images") or die "chdir testdir failed: $!\n";
  7 +
  8 +require TestDriver;
  9 +
  10 +my $td = new TestDriver('invert-images');
  11 +
  12 +cleanup();
  13 +
  14 +$td->runtest("double page size",
  15 + {$td->COMMAND => "pdf-invert-images in.pdf a.pdf"},
  16 + {$td->STRING =>
  17 + "pdf-invert-images: new file written to a.pdf\n",
  18 + $td->EXIT_STATUS => 0},
  19 + $td->NORMALIZE_NEWLINES);
  20 +
  21 +$td->runtest("check output",
  22 + {$td->FILE => "a.pdf"},
  23 + {$td->FILE => "out.pdf"});
  24 +
  25 +cleanup();
  26 +
  27 +$td->report(2);
  28 +
  29 +sub cleanup
  30 +{
  31 + unlink 'a.pdf';
  32 +}
... ...
examples/qtest/invert-images/in.pdf 0 → 100644
No preview for this file type
examples/qtest/invert-images/out.pdf 0 → 100644
No preview for this file type