16-bit images in PDF output
Introduction
In Mako, the IDOMImage
class represents an image. Images can be loaded from a file:
const auto mako = IJawsMako::create();
IJawsMako::enableAllFeatures(mako);
...
U8String inputFile = "MyImage.tif";
const auto image = IDOMTIFFImage::create(mako, IInputStream::createFromFile(mako, inputFile));
Images are also created by rendering a PDF page. In this example, to a 16 bits-per-sample, CMYK image:
IJawsMakoPtr mako = IJawsMako::create();
IJawsMako::enableAllFeatures(mako);
String inputFile = "MyFile.pdf";
...
// Create an input
IPDFInputPtr input = IPDFInput::create(mako);
// Get the assembly from the input
IDocumentAssemblyPtr assembly = input->open(inputFile);
// Get the document from the assembly
IDocumentPtr document = assembly->getDocument();
// Get first page
IDOMFixedPagePtr fixedPage = document->getPage(0)->getContent();
// Render the page 16bpp, cmyk
IJawsRendererPtr renderer = IJawsRenderer::create(mako);
IDOMImage composite = renderer->render(
fixedPage,
renderResolution,
16,
IDOMColorSpaceDeviceCMYK::create(mako)
);
// Write a TIFF
IDOMTIFFImage::encode(mako, composite, IOutputStream::createToFile(mako, "render.tif"));
Maintaining 16-images in PDF output
Currently, Mako will encode IDOMImages
to 8-bits when writing PDF output, even though the IDOMImage
may be 16-bit. This limitation can be circumvented by converting to an IDOMPDFImage
. An IDOMPDFImage
represents the native format for images in PDF. When Mako writes a page that contains such images to a PDF file, they are passed directly into the output without further processing, thereby preserving 16-bit images. This means compression of the image must be handled when creating an IDOMPDFImage
, but this is easily achieved with a stream creator that supports compression. This function shows it in action.
/**
* \brief Create a flate-compressed IDOMPDFImage from an IDOMImage. This allows 16-bit images to be passed to PDF output
* \param mako Pointer to Mako instance
* \param image Pointer to IDOMImage
* \return IDOMPDFImage
*/
IDOMPDFImagePtr getAsPdfImage(const IJawsMakoPtr& mako, const IDOMImagePtr& image)
{
// Get image particulars
const auto frame = image->getImageFrame(mako);
const auto colorSpace = frame->getColorSpace();
const auto width = frame->getWidth();
const auto height = frame->getHeight();
const auto bps = frame->getBPS();
const auto rawBytes = frame->getRawBytesPerRow();
// Compress the image
IRAInputStreamPtr reader;
IRAOutputStreamPtr writer;
mako->getTempStore()->createTemporaryReaderWriterPair(reader, writer);
{
const auto flateWriter = IOutputStream::createToFlateCompressed(mako, writer, 5, false);
if (!flateWriter->open())
{
throw std::runtime_error("Failed to create flate stream");
}
const uint32 actualStride = (width * colorSpace->getNumComponents() * bps + 7) / 8;
if (actualStride > rawBytes)
{
throw std::runtime_error("Failed to calculate stride correctly");
}
CEDLVector<uint8> rowBuffer(rawBytes);
for (uint32 y = 0; y < height; y++)
{
frame->readScanLine(&rowBuffer[0], rawBytes);
if (!flateWriter->completeWrite(&rowBuffer[0], static_cast<int32>(actualStride)))
{
throwEDLError(EDL_ERR_IMAGE_DECODE_FAILURE);
}
}
if (!flateWriter->flush())
{
throw std::runtime_error("Failed to flush flate stream");
}
flateWriter->close();
}
// Create the new image
return IDOMPDFImage::create(mako,
reader,
eDITFlate,
IDOMPDFImage::FlateLZWParams::create(),
colorSpace, width, height, bps);
}