mirror of
https://github.com/qpdf/qpdf.git
synced 2025-04-02 14:41:50 +00:00
Improve pdf-invert-images example
This commit is contained in:
parent
fbac472510
commit
65ae8511a7
@ -1,3 +1,12 @@
|
|||||||
|
2020-04-07 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Improve pdf-invert-images example to show a pattern of copying
|
||||||
|
streams into another QPDF object to enable a stream data provider
|
||||||
|
to access the original stream data.
|
||||||
|
|
||||||
|
* Fix error that caused a compilation error with clang. Fixes
|
||||||
|
#424.
|
||||||
|
|
||||||
2020-04-06 Jay Berkenbilt <ejb@ql.org>
|
2020-04-06 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
* 10.0.0: release
|
* 10.0.0: release
|
||||||
|
@ -22,27 +22,77 @@ void usage()
|
|||||||
|
|
||||||
// Derive a class from StreamDataProvider to provide updated stream
|
// Derive a class from StreamDataProvider to provide updated stream
|
||||||
// data. The main purpose of using this object is to avoid having to
|
// data. The main purpose of using this object is to avoid having to
|
||||||
// allocate memory up front for the objects. A real application might
|
// allocate memory up front for the objects. We want to replace the
|
||||||
// use temporary files in order to avoid having to allocate all the
|
// stream data with a function of the original stream data. In order
|
||||||
// memory. Here, we're not going to worry about that since the goal
|
// to do this without actually holding all the images in memory, we
|
||||||
// is really to show how to use this facility rather than to show the
|
// create another QPDF object and copy the streams. Copying the
|
||||||
// best possible way to write an image inverter. This class still
|
// streams doesn't actually copy the data. Internally, the qpdf
|
||||||
// illustrates dynamic creation of the new stream data.
|
// library is holding onto the location of the stream data in the
|
||||||
|
// original file, which makes it possible for the StreamDataProvider
|
||||||
|
// to access it when it needs it.
|
||||||
class ImageInverter: public QPDFObjectHandle::StreamDataProvider
|
class ImageInverter: public QPDFObjectHandle::StreamDataProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ImageInverter();
|
||||||
virtual ~ImageInverter()
|
virtual ~ImageInverter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual void provideStreamData(int objid, int generation,
|
virtual void provideStreamData(int objid, int generation,
|
||||||
Pipeline* pipeline);
|
Pipeline* pipeline) override;
|
||||||
|
|
||||||
// Map [og] = image object
|
void setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider>);
|
||||||
std::map<QPDFObjGen, QPDFObjectHandle> image_objects;
|
void registerImage(QPDFObjectHandle image);
|
||||||
// Map [og] = image data
|
|
||||||
std::map<QPDFObjGen, PointerHolder<Buffer> > image_data;
|
private:
|
||||||
|
QPDF other;
|
||||||
|
PointerHolder<QPDFObjectHandle::StreamDataProvider> self_ph;
|
||||||
|
// Map og in original to copied image
|
||||||
|
std::map<QPDFObjGen, QPDFObjectHandle> copied_images;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ImageInverter::ImageInverter()
|
||||||
|
{
|
||||||
|
this->other.emptyPDF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ImageInverter::setSelfPh(PointerHolder<QPDFObjectHandle::StreamDataProvider> p)
|
||||||
|
{
|
||||||
|
// replaceStreamData requires a pointer holder to the stream data
|
||||||
|
// provider, but there's no way for us to generate one ourselves,
|
||||||
|
// so we have to have it handed to us.
|
||||||
|
this->self_ph = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ImageInverter::registerImage(QPDFObjectHandle image)
|
||||||
|
{
|
||||||
|
QPDFObjGen og(image.getObjGen());
|
||||||
|
// Store information about the images based on the object and
|
||||||
|
// generation number. Recall that a single image object may be
|
||||||
|
// used more than once, so no need to update the same stream
|
||||||
|
// multiple times.
|
||||||
|
if (this->copied_images.count(og) > 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->copied_images[og] = this->other.copyForeignObject(image);
|
||||||
|
|
||||||
|
// 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. We could explicitly use /DCTDecode and
|
||||||
|
// write through a DCT filter if we wanted.
|
||||||
|
image.replaceStreamData(this->self_ph,
|
||||||
|
QPDFObjectHandle::newNull(),
|
||||||
|
QPDFObjectHandle::newNull());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ImageInverter::provideStreamData(int objid, int generation,
|
ImageInverter::provideStreamData(int objid, int generation,
|
||||||
Pipeline* pipeline)
|
Pipeline* pipeline)
|
||||||
@ -50,8 +100,9 @@ ImageInverter::provideStreamData(int objid, int generation,
|
|||||||
// Use the object and generation number supplied to look up the
|
// Use the object and generation number supplied to look up the
|
||||||
// image data. Then invert the image data and write the inverted
|
// image data. Then invert the image data and write the inverted
|
||||||
// data to the pipeline.
|
// data to the pipeline.
|
||||||
|
QPDFObjGen og(objid, generation);
|
||||||
PointerHolder<Buffer> data =
|
PointerHolder<Buffer> data =
|
||||||
this->image_data[QPDFObjGen(objid, generation)];
|
this->copied_images[og].getStreamData(qpdf_dl_all);
|
||||||
size_t size = data->getSize();
|
size_t size = data->getSize();
|
||||||
unsigned char* buf = data->getBuffer();
|
unsigned char* buf = data->getBuffer();
|
||||||
unsigned char ch;
|
unsigned char ch;
|
||||||
@ -98,6 +149,9 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
ImageInverter* inv = new ImageInverter;
|
ImageInverter* inv = new ImageInverter;
|
||||||
PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv;
|
PointerHolder<QPDFObjectHandle::StreamDataProvider> p = inv;
|
||||||
|
// We need to give ImageInverter the pointer holder that it
|
||||||
|
// needs to pass to replaceStreamData.
|
||||||
|
inv->setSelfPh(p);
|
||||||
|
|
||||||
// For each page...
|
// For each page...
|
||||||
std::vector<QPDFPageObjectHelper> pages =
|
std::vector<QPDFPageObjectHelper> pages =
|
||||||
@ -126,37 +180,13 @@ int main(int argc, char* argv[])
|
|||||||
// whether the image is filterable. Directly inspect
|
// whether the image is filterable. Directly inspect
|
||||||
// keys to determine the image type.
|
// keys to determine the image type.
|
||||||
if (image.pipeStreamData(0, qpdf_ef_compress,
|
if (image.pipeStreamData(0, qpdf_ef_compress,
|
||||||
qpdf_dl_generalized) &&
|
qpdf_dl_all) &&
|
||||||
color_space.isName() &&
|
color_space.isName() &&
|
||||||
bits_per_component.isInteger() &&
|
bits_per_component.isInteger() &&
|
||||||
(color_space.getName() == "/DeviceGray") &&
|
(color_space.getName() == "/DeviceGray") &&
|
||||||
(bits_per_component.getIntValue() == 8))
|
(bits_per_component.getIntValue() == 8))
|
||||||
{
|
{
|
||||||
// Store information about the images based on the
|
inv->registerImage(image);
|
||||||
// object and generation number. Recall that a single
|
|
||||||
// image object may be used more than once.
|
|
||||||
QPDFObjGen og = image.getObjGen();
|
|
||||||
if (inv->image_objects.count(og) == 0)
|
|
||||||
{
|
|
||||||
inv->image_objects[og] = image;
|
|
||||||
inv->image_data[og] = 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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user