Skip to main content
Skip table of contents

16-bit images in PDF output

📌 Overview

This article explains how to maintain 16-bit images in the PDF output. In Mako, the IDOMImage class represents an image. Images can be loaded from a file:

CPP
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:

CPP
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"));

📗 Instructions

Currently, Mako encodes 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 that 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.

🪜 Steps

  1. Get image particulars: Get the frame, color space, width, height, bps and rawBytes properties.

  2. Compress the image: Use a “flate writer”, created using IOutputStream::createToFlateCompressed() to compress the image.

  3. Create the new image: Use IDOMPDFImage::create() to make the new image which will preserve 16-bit encoding.

⌨️ Sample Code

This is also available at GetAsPdfImage.cpp.

CPP
/**
 * \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);
}

☑️ Conclusion

Handling 16-bit images in PDF output with Mako involves converting IDOMImages to IDOMPDFImages to preserve their bit depth. By following the outlined steps, users can ensure that their images maintain their quality and integrity when included in PDF files. The provided sample code demonstrates how to achieve this using flate compression, ensuring efficient and effective image handling.

📚 Additional Resources

If you need additional help, see our API documentation for detailed information on class/method usage, or raise a support ticket via our customer portal.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.