All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.europa.esig.dss.pdf.pdfbox.visible.defaultdrawer.DefaultImageDrawerUtils Maven / Gradle / Ivy

The newest version!
/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 * 
 * This file is part of the "DSS - Digital Signature Services" project.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss.pdf.pdfbox.visible.defaultdrawer;

import eu.europa.esig.dss.spi.exception.IllegalInputException;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.pades.DSSFont;
import eu.europa.esig.dss.pades.SignatureImageParameters;
import eu.europa.esig.dss.pades.SignatureImageTextParameters;
import eu.europa.esig.dss.pdf.visible.DPIUtils;
import eu.europa.esig.dss.pdf.visible.ImageRotationUtils;
import eu.europa.esig.dss.pdf.visible.ImageUtils;
import eu.europa.esig.dss.pdf.visible.SignatureFieldDimensionAndPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Contains the util methods used by the
 * {@code eu.europa.esig.dss.pdf.pdfbox.visible.defaultdrawer.DefaultPdfBoxVisibleSignatureDrawer}
 *
 */
public final class DefaultImageDrawerUtils {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultImageDrawerUtils.class);

    /**
     * Default constructor
     */
    private DefaultImageDrawerUtils() {
        // empty
    }

    /**
     * Creates an image representing the specified text
     *
     * @param imageParameters {@link SignatureImageParameters} to use
     * @param dimensionAndPosition {@link SignatureFieldDimensionAndPosition}
     * @param fontMetrics {@link JavaDSSFontMetrics}
     * @return {@link BufferedImage} of the text picture
     */
    public static BufferedImage createTextImage(final SignatureImageParameters imageParameters,
                                                final SignatureFieldDimensionAndPosition dimensionAndPosition,
                                                final JavaDSSFontMetrics fontMetrics) {
        SignatureImageTextParameters textParameters = imageParameters.getTextParameters();
        String[] lines = dimensionAndPosition.getText().split("\n");

        int imageType;
        if (isTransparent(textParameters.getTextColor(), textParameters.getBackgroundColor())) {
            LOG.warn("Transparency detected and enabled (Be aware: not valid with PDF/A !)");
            imageType = BufferedImage.TYPE_INT_ARGB;
        } else if (isGrayscale(textParameters.getTextColor(), textParameters.getBackgroundColor())) {
            imageType = BufferedImage.TYPE_BYTE_GRAY;
        } else {
            imageType = BufferedImage.TYPE_INT_RGB;
        }

        int textDPI = DPIUtils.getDpi(imageParameters.getDpi());
        BufferedImage img = new BufferedImage((int) DPIUtils.computeProperSize(dimensionAndPosition.getTextBoxWidth(), textDPI),
                (int) DPIUtils.computeProperSize(dimensionAndPosition.getTextBoxHeight(), textDPI), imageType);

        Graphics2D g = img.createGraphics();
        Font font = getJavaFont(imageParameters, dimensionAndPosition.getTextSize(), textDPI);
        g.setFont(font);

        // Improve text rendering
        initRendering(g);

        if (textParameters.getBackgroundColor() == null) {
            g.setColor(Color.WHITE);
        } else {
            g.setColor(textParameters.getBackgroundColor());
        }
        g.fillRect(0, 0, img.getWidth(), img.getHeight());

        if (textParameters.getTextColor() == null) {
            g.setPaint(Color.BLACK);
        } else {
            g.setPaint(textParameters.getTextColor());
        }

        float lineHeight = fontMetrics.getHeight(lines[0], font.getSize());
        float y = fontMetrics.getMaxAscent(font.getSize()) + DPIUtils.computeProperSize(
                dimensionAndPosition.getTextY() - dimensionAndPosition.getTextBoxY(), textDPI);

        for (String line : lines) {
            // left alignment by default
            float x = DPIUtils.computeProperSize(dimensionAndPosition.getTextX() - dimensionAndPosition.getTextBoxX(), textDPI);
            if (textParameters.getSignerTextHorizontalAlignment() != null) {
                switch (textParameters.getSignerTextHorizontalAlignment()) {
                    case RIGHT:
                        x = img.getWidth() - fontMetrics.getWidth(line, font.getSize()) - x; // -x because of margin
                        break;
                    case CENTER:
                        x = (img.getWidth() - fontMetrics.getWidth(line, font.getSize())) / 2;
                        break;
                    case LEFT:
                    default:
                        // nothing
                        break;
                }
            }
            g.drawString(line, x, y);
            y += lineHeight;
        }
        g.dispose();

        return img;
    }

    private static Font getJavaFont(SignatureImageParameters imageParameters, float textSize, int dpi) {
        DSSFont dssFont = imageParameters.getTextParameters().getFont();
        float fontSize = DPIUtils.computeProperSize(textSize, dpi);

        Font javaFont = dssFont.getJavaFont();
        return javaFont.deriveFont(fontSize);
    }

    private static boolean isTransparent(Color... colors) {
        if (colors != null) {
            for (Color color : colors) {
                if (color != null) {
                    int alpha = color.getAlpha();
                    if (alpha < 255) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private static boolean isGrayscale(Color... colors) {
        if (colors != null) {
            for (Color color : colors) {
                if (color != null && !ImageUtils.isGrayscale(color)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Reads and converts the given image document to a {@code BufferedImage}
     *
     * @param imageDocument {@link BufferedImage}
     * @return {@link BufferedImage}
     */
    public static BufferedImage toBufferedImage(final DSSDocument imageDocument) {
        try {
            return ImageUtils.toBufferedImage(imageDocument);
        } catch (IOException e) {
            throw new IllegalInputException(String.format("An error occurred during image document reading : %s", e.getMessage()), e);
        }
    }

    /**
     * Sets the preferred image creation parameters to improve the rendering
     *
     * @param g {@link Graphics2D} to set
     */
    public static void initRendering(Graphics2D g) {
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
    }

    /**
     * Creates the final image, by merging the given {@code image} and {@code textImage}
     * according to the provided parameters
     *
     * @param image {@link BufferedImage} the image picture
     * @param textImage {@link BufferedImage} the rasterized text
     * @param dimensionAndPosition {@link SignatureFieldDimensionAndPosition}
     * @param imageParameters {@link SignatureImageParameters}
     * @return {@link BufferedImage}
     */
    public static BufferedImage mergeImages(BufferedImage image, BufferedImage textImage,
                                            SignatureFieldDimensionAndPosition dimensionAndPosition,
                                            SignatureImageParameters imageParameters) {
        final int imageType = getImageType(image, textImage);
        int xDpi = dimensionAndPosition.getImageResolution().getXDpi();
        int yDpi = dimensionAndPosition.getImageResolution().getYDpi();
        if (textImage != null) {
            xDpi = DPIUtils.getDpi(imageParameters.getDpi());
            yDpi = DPIUtils.getDpi(imageParameters.getDpi());
        }

        float imageWidthRatio = 1f;
        float imageHeightRatio = 1f;
        if (image != null) {
            float widthRatio = (image.getWidth() / dimensionAndPosition.getImageWidth()) * DPIUtils.getPageScaleFactor(xDpi);
            imageWidthRatio = widthRatio > 1 ? widthRatio : imageWidthRatio;
            float heightRatio = image.getHeight() / dimensionAndPosition.getImageHeight() * DPIUtils.getPageScaleFactor(yDpi);
            imageHeightRatio = heightRatio > 1 ? heightRatio : imageHeightRatio;
        }

        float width = dimensionAndPosition.getBoxWidth();
        float height = dimensionAndPosition.getBoxHeight();
        if (ImageRotationUtils.isSwapOfDimensionsRequired(dimensionAndPosition.getGlobalRotation())) {
            width =  dimensionAndPosition.getBoxHeight();
            height = dimensionAndPosition.getBoxWidth();
        }
        BufferedImage result = getEmptyImage(width * imageWidthRatio, height * imageHeightRatio, xDpi, yDpi, imageType);
        Graphics2D g = result.createGraphics();
        initRendering(g);

        // required for non-transparent and text containing pictures to avoid black spaces
        if (BufferedImage.TYPE_INT_ARGB != imageType ||
                imageParameters.getTextParameters() != null && !imageParameters.getTextParameters().isEmpty() ||
                imageParameters.getBackgroundColor() != null) {
            fillBackground(g, result.getWidth(), result.getHeight(), imageParameters.getBackgroundColor());
        }

        if (textImage != null) {
            drawImage(g, textImage, DPIUtils.computeProperSize(dimensionAndPosition.getTextBoxX() * imageWidthRatio, xDpi),
                    DPIUtils.computeProperSize((height - dimensionAndPosition.getTextBoxY() - dimensionAndPosition.getTextBoxHeight()) * imageHeightRatio, yDpi),
                    DPIUtils.computeProperSize(dimensionAndPosition.getTextBoxWidth() * imageWidthRatio, xDpi),
                    DPIUtils.computeProperSize(dimensionAndPosition.getTextBoxHeight() * imageHeightRatio, yDpi));
        }
        if (image != null) {
            drawImage(g, image, DPIUtils.computeProperSize(dimensionAndPosition.getImageX() * imageWidthRatio, xDpi),
                    DPIUtils.computeProperSize((height - dimensionAndPosition.getImageY() - dimensionAndPosition.getImageHeight()) * imageHeightRatio, yDpi),
                    DPIUtils.computeProperSize(dimensionAndPosition.getImageWidth() * imageWidthRatio, xDpi),
                    DPIUtils.computeProperSize(dimensionAndPosition.getImageHeight() * imageHeightRatio, yDpi));
        }
        return result;
    }

    private static int getImageType(final BufferedImage image1, final BufferedImage image2) {
        // default
        int imageType = BufferedImage.TYPE_INT_RGB;
        if (image1 != null && image2 == null) {
            imageType = image1.getType();
        } else if (image1 == null && image2 != null) {
            imageType = image2.getType();
        } else if (image1 != null && image1.getType() == image2.getType()) {
            imageType = image1.getType();
        }

        if ((image1 != null && ImageUtils.isTransparent(image1)) || (image2 != null && ImageUtils.isTransparent(image2))) {
            LOG.warn("Transparency detected and enabled (Be aware: not valid with PDF/A !)");
            imageType = BufferedImage.TYPE_INT_ARGB;

        } else if (BufferedImage.TYPE_CUSTOM == imageType) {
            LOG.info("Original image type is not recognized! Use RGB as the target color profile.");
            imageType = BufferedImage.TYPE_INT_RGB;
        }

        return imageType;
    }

    private static BufferedImage getEmptyImage(float width, float height, int xDpi, int yDpi, int imageType) {
        return new BufferedImage((int) DPIUtils.computeProperSize(width, xDpi),
               (int) DPIUtils.computeProperSize(height, yDpi), imageType);
    }

    private static void fillBackground(Graphics g, float width, float height, Color bgColor) {
        g.setColor(bgColor);
        g.fillRect(0, 0, (int) width, (int) height);
    }

    private static void drawImage(Graphics g, BufferedImage image, float x, float y, float width, float height) {
        g.drawImage(image, (int) x, (int) y,
                (int) width, (int) height, null);
    }

    /**
     * Rotates the provided image to the given {@code angle}
     *
     * @param image {@link BufferedImage} to rotate
     * @param angle the rotation angle
     * @return rotated {@link BufferedImage}
     */
    public static BufferedImage rotate(BufferedImage image, double angle) {
        if (ImageRotationUtils.ANGLE_0 == angle || ImageRotationUtils.ANGLE_360 == angle) {
            return image;
        }
        double sin = Math.abs(Math.sin(Math.toRadians(angle)));
        double cos = Math.abs(Math.cos(Math.toRadians(angle)));

        int w = image.getWidth();
        int h = image.getHeight();

        double neww = Math.floor(w * cos + h * sin);
        double newh = Math.floor(h * cos + w * sin);

        BufferedImage result = new BufferedImage((int) neww, (int) newh, image.getType());
        Graphics2D g = result.createGraphics();

        g.translate((neww - w) / 2, (newh - h) / 2);
        g.rotate(Math.toRadians(angle), (double) w / 2, (double) h / 2);
        g.drawRenderedImage(image, null);
        g.dispose();

        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy