Search code examples
javaimagerotationitextcenter

How to rotate around the image center by itext?


double degPi = degrees * Math.PI / 180;   
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;

contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/

How to rotate around the image center by itext?


Solution

  • If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this

    contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
    

    A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.

    If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:

    // Draw image as if the previous image was rotated around its center
    // Image starts out being 1x1 with origin in lower left
    // Move origin to center of image
    AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
    // Stretch it to its dimensions
    AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
    // Rotate it
    AffineTransform C = AffineTransform.getRotateInstance(rotate);
    // Move it to have the same center as above
    AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
    // Concatenate
    AffineTransform M = (AffineTransform) A.clone();
    M.preConcatenate(B);
    M.preConcatenate(C);
    M.preConcatenate(D);
    //Draw
    contentByte.addImage(image, M);
    

    (AddRotatedImage.java test method testAddRotatedImage)

    For example drawing both images using

    int x = 200;
    int y = 300;
    float rotate = (float) Math.PI / 3;
    

    results in something like this:

    Screenshot

    With a Flip

    The OP asked in a comment

    how to add rotate and flip image?

    For this you simply insert a mirroring affine transformation into the sequence of transformations above.

    Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.

    // Draw image as if the previous image was flipped and rotated around its center
    // Image starts out being 1x1 with origin in lower left
    // Move origin to center of image
    AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
    // Flip it horizontally
    AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
    // Stretch it to its dimensions
    AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
    // Rotate it
    AffineTransform D = AffineTransform.getRotateInstance(rotate);
    // Move it to have the same center as above
    AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
    // Concatenate
    AffineTransform M = (AffineTransform) A.clone();
    M.preConcatenate(B);
    M.preConcatenate(C);
    M.preConcatenate(D);
    M.preConcatenate(E);
    //Draw
    contentByte.addImage(image, M);
    

    (AddRotatedImage.java test method testAddRotatedFlippedImage)

    The result with the same image as above:

    Screenshot

    With Interpolation

    The OP asked in a yet another comment

    How anti aliasing ?

    The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),

    image.setInterpolation(true);
    

    low resolution images are subject to interpolation when drawn.

    E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:

    rotatedInterpolatedImage.pdf screenshot

    Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:

    Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:

    NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.

    Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.