mirror of https://github.com/qpdf/qpdf.git
invert images example
git-svn-id: svn+q:///qpdf/trunk@1001 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
parent
b6c7a80950
commit
cb1d89e763
|
@ -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>
|
||||
|
||||
|
|
11
TODO
11
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
|
||||
=======
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
}
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue