How to change image compression from RLE to flate in a PDF document.
There is sometimes a requirement to recompress images to a different filter, often to make the PDF smaller. This example converts all images that use the RLE filter to replace it with the flate decode filter.
It can be amended to handle other filter types.
The example uses a custom transform (see Custom transforms) which can iterate through all the images on a page and replace the RLE filter images with flate decode images.
Recompression Custom Transform
class Recompression : public ICustomTransform::IImplementation
{
public:
Recompression(const IJawsMakoPtr& jawsMako)
{
this->jawsMako = jawsMako;
};
~Recompression()
{
};
IDOMImagePtr transformImage(IImplementation* genericImplementation, const IDOMImagePtr& image, const CTransformState& state)
{
auto imageType = image->getImageType();
if (imageType == eDOMImageType::eDITRunLength)
{
IImageDecoderPtr decoder = image->createImageDecoder(jawsMako, image->getImageProperties());
IImageFramePtr frame = decoder->getFrame();
IDOMColorSpacePtr colorspace = frame->getColorSpace();
uint32 width = frame->getWidth();
uint32 height = frame->getHeight();
uint32 bps = frame->getBPS();
uint8 numComponents = colorspace->getNumComponents();
IInputStreamPtr stream = image->getStream();
CEDLVector<float> decodeArray;
for (int i = 0; i < numComponents; i++)
{
float low = 0;
float high = 0;
colorspace->getComponentRange(i, low, high);
decodeArray.append(low);
decodeArray.append(high);
}
uint32 bpr = frame->getRawBytesPerRow();
uint8* buffer2 = (uint8*)malloc((size_t)(bpr * height));
uint32 pos = 0;
for (int y = 0; y < height; y++)
{
frame->readScanLine(&buffer2[pos], bpr);
pos += bpr;
}
// Use Mako's temp store to store the compressed data
IRAInputStreamPtr reader;
IRAOutputStreamPtr writer;
jawsMako->getTempStore()->createTemporaryReaderWriterPair(reader, writer);
// Create a flate compressor - maximum quality
IOutputStreamPtr flateWriter = IOutputStream::createToFlateCompressed(jawsMako, writer, 9, false);
if (!flateWriter->open())
{
// Handle error appropriately
throw std::runtime_error("Unable to open flate writer stream?");
}
// Write entire buffer in one lot
if (!flateWriter->completeWrite(&buffer2[0], bpr * height))
{
throw std::runtime_error("Failed to write compressed flate data for band!");
}
flateWriter->close();
// Create the IDOMPDFImage which will ferry this image data to the output PDF untouched.
IDOMImagePtr newimage = IDOMPDFImage::create(jawsMako,
reader,
eDITFlate,
IDOMPDFImage::FlateLZWParams::create(),
colorspace, width, height, bps);
return newimage;
}
return image;
};
private:
IJawsMakoPtr jawsMako;
};
This custom transform can then be used as follows:
Recompression recompression(jawsMako);
auto tf = ICustomTransform::create(jawsMako, &recompression);
for (uint8 i = 0; i < document->getNumPages(); i++)
{
auto newPage = document->getPage(i)->clone();
tf->transform(newPage->getContent(), changed);
newDocument->appendPage(newPage);
}