Skip to main content
Skip table of contents

Replacing images

This example was written in response to the question Click How to convert colored images to black and white?


replaceimage

JAVA

JAVA
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2023 Apryse Group NV
    Authors: Apryse Software.

    For more information, please contact iText Software at this address:
    sales@itextpdf.com
 */
package com.itextpdf.samples.sandbox.images;

import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.pdf.PdfDictionary;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfStream;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
import com.itextpdf.layout.element.Image;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ReplaceImage {
    public static final String DEST = "./target/sandbox/images/replace_image.pdf";

    public static final String SRC = "./src/main/resources/pdfs/image.pdf";

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();

        new ReplaceImage().manipulatePdf(DEST);
    }

    protected void manipulatePdf(String dest) throws Exception {
        PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest));

        // Assume that there is a single XObject in the source document
        // and this single object is an image.
        PdfDictionary pageDict = pdfDoc.getFirstPage().getPdfObject();
        PdfDictionary resources = pageDict.getAsDictionary(PdfName.Resources);
        PdfDictionary xObjects = resources.getAsDictionary(PdfName.XObject);
        PdfName imgRef = xObjects.keySet().iterator().next();
        PdfStream stream = xObjects.getAsStream(imgRef);
        Image img = convertToBlackAndWhitePng(new PdfImageXObject(stream));

        // Replace the original image with the grayscale image
        xObjects.put(imgRef, img.getXObject().getPdfObject());

        pdfDoc.close();
    }

    private static Image convertToBlackAndWhitePng(PdfImageXObject image) throws IOException {
        BufferedImage bi = image.getBufferedImage();
        BufferedImage newBi = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_USHORT_GRAY);
        newBi.getGraphics().drawImage(bi, 0, 0, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(newBi, "png", baos);
        return new Image(ImageDataFactory.create(baos.toByteArray()));
    }
}

C#

C#
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using iText.IO.Image;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Xobject;
using Image = iText.Layout.Element.Image;

namespace iText.Samples.Sandbox.Images
{
    public class ReplaceImage
    {
        public static readonly String DEST = "results/sandbox/images/replace_image.pdf";

        public static readonly String SRC = "../../../resources/pdfs/image.pdf";

        public static void Main(String[] args)
        {
            FileInfo file = new FileInfo(DEST);
            file.Directory.Create();

            new ReplaceImage().ManipulatePdf(DEST);
        }

        protected void ManipulatePdf(String dest)
        {
            PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest));

            // Assume that there is a single XObject in the source document
            // and this single object is an image.
            PdfDictionary pageDict = pdfDoc.GetFirstPage().GetPdfObject();
            PdfDictionary resources = pageDict.GetAsDictionary(PdfName.Resources);
            PdfDictionary xObjects = resources.GetAsDictionary(PdfName.XObject);
            PdfName imgRef = xObjects.KeySet().First();
            PdfStream stream = xObjects.GetAsStream(imgRef);
            Image img = ConvertToBlackAndWhitePng(new PdfImageXObject(stream));

            // Replace the original image with the grayscale image
            xObjects.Put(imgRef, img.GetXObject().GetPdfObject());

            pdfDoc.Close();
        }

        private static Image ConvertToBlackAndWhitePng(PdfImageXObject image)
        {
            
            /* We want to convert image to grayscale. In PDF this corresponds
             * to DeviceGray color space. Images in DeviceGray colorspace shall have
             * only 8 bits per pixel (bpp).
             * In C# 8bpp images are not well supported, therefore we would need to perform
             * some tricks on a low level in order to convert RGB 24bpp image to 8 bit image.
             *
             * We will manually set image pixel 8 bit values according to original image
             * RGB pixel values. We know from PDF specification, that DeviceGray color space
             * treats each pixel as the value from 0 to 255 and we know that taking an average
             * of RGB values is a very basic but working approach to get corresponding grayscale
             * value.
             * Note that due to C# restrictions we create image with indexed colorspace
             * (Format8bppIndexed). For now we don't care what are the actual colors in color
             * palette, because we already define pixle values as if they were in grayscale
             * color space. We will explicitly overide color space directly in PDF object later.
             */
            MemoryStream memoryStream = new MemoryStream(image.GetImageBytes());
            Bitmap original = new Bitmap(memoryStream);
            memoryStream.Close();
            Bitmap result = new Bitmap(original.Width, original.Height, PixelFormat.Format8bppIndexed);

            BitmapData data = result.LockBits(new System.Drawing.Rectangle(0, 0, result.Width, result.Height),
                ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
            byte[] bytes = new byte[data.Height * data.Stride];
            Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);

            // Convert all pixels to grayscale
            for (int y = 0; y < original.Height; y++)
            {
                for (int x = 0; x < original.Width; x++)
                {
                    var c = original.GetPixel(x, y);
                    var rgb = (byte) ((c.R + c.G + c.B) / 3);
                    bytes[y * data.Stride + x] = rgb;
                }
            }

            Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
            result.UnlockBits(data);

            using (var stream = new MemoryStream())
            {
                result.Save(stream, ImageFormat.Png);

                /* As discussed above, we want this image to have /DeviceGray colorspace and we have already
                 * ensured that image pixel values are defined correctly for this color space on low level
                 * (by taking average values of red, green and blue components). So, we explicitly override
                 * color space directly in the created image PDF object.
                 */
                ImageData imageData = ImageDataFactory.Create(stream.ToArray());
                PdfImageXObject imageXObject = new PdfImageXObject(imageData);
                PdfStream imageXObjectStream = imageXObject.GetPdfObject();
                imageXObjectStream.Put(PdfName.ColorSpace, PdfName.DeviceGray);

                // Remove a redundant submask
                imageXObjectStream.Remove(PdfName.Mask);

                /* In C# an alpha channel (transparency) is automatically added, however we know that original
                 * image didn't have transparency, that's why we just explicitly throw away any transparency
                 * defined for the PDF object that represents an image.
                 */
                return new Image(imageXObject);
            }
        }
    }
}
JavaScript errors detected

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

If this problem persists, please contact our support.