Graphics.DrawImage - how to merge graphics with different DPI

Hi, I’m trying to “burn” a signature onto a TIFF image. They may have different resolutions and the API doesn’t really explain what unit of measure the various positioning and size parameters should be in. If I have two Aspose.Image objects and then create a graphics object from the larger TIIFF image and use drawimage on that to “burn” the signature image onto the graphics object, it seems to replace one pixel in the source image with one pixel from the signature image. Can you suggest how I can get the signature to come out at the correct size please. Thank you

@msutherland25

Can you please explain your requirements in the form of source images and desired output that you want to achieve. We will try to evaluate them based on that to help you further whether desired output could be achieved by API or not.

Hi, thank you for replying. I’m not sure that I need to supply example images as such. Imagine that the main image is an A4/letter sized TIFF image at 200dpi and I need to merge a signature onto the bottom left. The Signature is for example a 400dpi image with dimensions of 2" by 1". Right now, you will get a huge signature twice as big as needed since dpi is not taken into account. Perhaps they need to match and if so, can the Aspose.Imaging help to do this? Thank you

@msutherland25

I have created a ticket with ID IMAGINGNET-4330 in our issue tracking system to further investigate and resolve the issue. This thread has been linked with the issue so that you may be notified once the issue will be addressed.

Thank you. It would also be highly helpful to support a transparency colour that would be ignored when merging so that a large white box is not written over the original. Thanks

@msutherland25

Can you please share the details of your requirements in the form of source files and generated output so that I may help you further in this regard.

@msutherland25

We have evaluated the requirements on our end. For now, in Graphics we use coordinates in pixels and do not take in account dpi of images, but for your convenience, we have prepared a following code serving your needs. Also we are going to implement the support of coordinates in others graphics units than only pixels.

// Using
SignatureBurner.putSignature(new SignatureBurner.Options()
                .setMainImageFile("document.tif")
                .setSignImageFile("sign.tif")
                .setOutImageFile("resultImage.tif")
                .setGraphicsUnit(GraphicsUnit.Inch) // graphics units of coordinates of sign's position
                .setPosition(new PointF(2.85f, 7.31f)) // position for signing in document in inches (as set in line upper )
        );

// Utility class

class SignatureBurner
{
    private final RasterImage signature;
    private /*GraphicsUnit*/ int graphicsUnit = GraphicsUnit.Pixel;

    public SignatureBurner(RasterImage signature)
    {
        this.signature = signature;
    }

    public SignatureBurner setGraphicsUnit(/*GraphicsUnit*/ int graphicsUnit)
    {
        this.graphicsUnit = graphicsUnit;
        return this;
    }

    public void applyTo(RasterImage image, PointF position)
    {
        if (image instanceof IMultipageImage)
        {
            for (Image page : ((IMultipageImage) image).getPages())
            {
                process((RasterImage)page, new Graphics(page), position);
            }
        }
        else
        {
            process(image, new Graphics(image), position);
        }
    }

    public void applyTo(Graphics canvas, PointF position)
    {
        process((RasterImage)canvas.getImage(), canvas, position);
    }

    private void process(RasterImage dest, Graphics canvas, PointF position)
    {
        double mainXRes = dest.getHorizontalResolution();
        double mainYRes = dest.getVerticalResolution();

        double signXRes = signature.getHorizontalResolution();
        double signYRes = signature.getVerticalResolution();

        double widthX = signature.getWidth() * mainXRes / signXRes; // width of the signature in pixels of main doc
        double heightY = signature.getHeight() * mainYRes / signYRes; // height of the signature in pixels of main doc

        ImageAttributes a = new ImageAttributes();
        a.setColorKey(Color.fromArgb(247, 247, 247), Color.getWhite());

        double posX = position.getX() * getScaleForUnit(graphicsUnit, mainXRes);
        double posY = position.getY() * getScaleForUnit(graphicsUnit, mainYRes);

        canvas.drawImage(signature,
                new RectangleF(
                        (float)posX,
                        (float)posY,
                        (float)widthX,
                        (float)heightY),
                GraphicsUnit.Pixel,
                a);

        if (canvas.isInBeginUpdateCall())
            canvas.endUpdate();
    }

    private static double getScaleForUnit(int graphicsUnit, double docResolution)
    {
        switch (graphicsUnit)
        {
            case GraphicsUnit.Pixel:
                return 1.0;
            case GraphicsUnit.Display:
                return docResolution / 96.0; // for Windows Display
            case GraphicsUnit.Document:
                return  docResolution / 300.0; // 300 dpi
            case GraphicsUnit.Point:
                return docResolution / 72.0;
            case GraphicsUnit.Millimeter:
                return docResolution / 25.4;
            default:
                return docResolution;
        }
    }

    public static void putSignature(String mainImage,
                                    String signatureImage,
                                    int graphicsUnit,
                                    PointF positionInUnits,
                                    String outputImage)
    {
        List<Image> resourcesForClear = new LinkedList<Image>();
        try
        {
            RasterImage img = (RasterImage)Image.load(mainImage);
            resourcesForClear.add(img);
            RasterImage sign = (RasterImage)Image.load(signatureImage);
            resourcesForClear.add(sign);

            new SignatureBurner(sign)
                    .setGraphicsUnit(graphicsUnit)
                    .applyTo(img, positionInUnits);

            img.save(outputImage);
        }
        finally
        {
            for (Image image : resourcesForClear)
            {
                image.close();
            }
            resourcesForClear.clear();
        }
    }

    public static void putSignature(Options options)
    {
        options.verify();
        List<Image> resourcesForClear = new LinkedList<Image>();
        try
        {
            RasterImage img = (RasterImage)Image.load(options.mainImageFile);
            resourcesForClear.add(img);
            RasterImage sign = (RasterImage)Image.load(options.signImageFile);
            resourcesForClear.add(sign);

            new SignatureBurner(sign)
                    .setGraphicsUnit(options.graphicsUnit)
                    .applyTo(img, options.position);

            if (options.outFormat == null)
                img.save(options.outImageFile);
            else
                img.save(options.outImageFile, options.outFormat);
        }
        finally
        {
            for (Image image : resourcesForClear)
            {
                image.close();
            }
            resourcesForClear.clear();
        }
    }

    public static class Options
    {
        String mainImageFile;
        String signImageFile;
        String outImageFile;
        PointF position = new PointF(0,0);
        int graphicsUnit = GraphicsUnit.Pixel;
        ImageOptionsBase outFormat;

        public Options setMainImageFile(String mainImageFile)
        {
            this.mainImageFile = mainImageFile;
            return this;
        }

        public Options setSignImageFile(String signImageFile)
        {
            this.signImageFile = signImageFile;
            return this;
        }

        public Options setOutImageFile(String outImageFile)
        {
            this.outImageFile = outImageFile;
            return this;
        }

        public Options setPosition(PointF position)
        {
            this.position = position;
            return this;
        }

        public Options setGraphicsUnit(int graphicsUnit)
        {
            this.graphicsUnit = graphicsUnit;
            return this;
        }

        public Options setOutFormat(ImageOptionsBase outFormat)
        {
            this.outFormat = outFormat;
            return this;
        }

        public void verify()
        {
            if (mainImageFile == null || mainImageFile.isEmpty())
                throw new IllegalArgumentException("Please set the document image file path: mainImageFile");
            if (signImageFile == null || signImageFile.isEmpty())
                throw new IllegalArgumentException("Please set the signature image file path: signImageFile");
            if (outImageFile == null || outImageFile.isEmpty())
                throw new IllegalArgumentException("Please set the output image file path: outImageFile");
        }
    }
}

Thank you, that is most appreciated

@msutherland25

You are always welcome.