com.identityx.clientSDK.piiSupport.PNGUtility Maven / Gradle / Ivy
package com.identityx.clientSDK.piiSupport;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.imageio.ImageIO;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/*
* Utility class for image resizing
*
*/
public class PNGUtility {
private static final Log logger = LogFactory.getLog(PNGUtility.class);
private List fontSizes = Arrays.asList( 60, 48, 36, 28, 26, 24, 22,20, 18, 16, 14, 12, 11, 10, 9, 8);
class TextDetails {
private List lines = new ArrayList();
private int fontSize;
private boolean overrun;
public TextDetails(int fontSize, String firstLine, boolean overrun) {
this.lines.add(firstLine);
this.fontSize = fontSize;
this.overrun = overrun;
}
public TextDetails(int fontSize, boolean overrun) {
this.fontSize = fontSize;
this.overrun = overrun;
}
public TextDetails(int fontSize) {
this.fontSize = fontSize;
}
public List getLines() {
return lines;
}
public void addLine(String line) {
this.lines.add(line);
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public boolean isOverrun() {
return overrun;
}
public void setOverrun(boolean overrun) {
this.overrun = overrun;
}
}
public BufferedImage createPNGFromBase64Data(String content) throws IOException
{
BufferedImage bufferedImage = null;
byte[] imageContent = Base64.decodeBase64(content);
try {
bufferedImage = ImageIO.read(new ByteArrayInputStream(imageContent));
} catch (IOException e) {
logger.error("The PNG Data supplied for the transaction was not valid");
throw e;
}
return bufferedImage;
}
public BufferedImage resizePNGFromBase64Data(DisplayPNGCharacteristics descriptor, String content) throws IOException
{
BufferedImage bufferedImage = null;
byte[] imageContent = Base64.decodeBase64(content);
BufferedImage scaledImage = null;
if (descriptor != null)
{
try {
bufferedImage = ImageIO.read(new ByteArrayInputStream(imageContent));
scaledImage = this.getScaledInstance(bufferedImage, (int)descriptor.getWidth(), (int)descriptor.getHeight(), RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR, false);
} catch (IOException e) {
logger.error("The PNG Data supplied for the transaction was not valid");
throw e;
}
return scaledImage;
}
else
{
logger.error("Null DisplayPNGCharacteristicsDescriptor supplied so just returning the original image");
}
return bufferedImage;
}
public String createBase64DatafromPNG(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", baos);
} catch (IOException e) {
logger.error("Failed to write the scaled PNG image");
throw e;
}
return Base64.encodeBase64URLSafeString(baos.toByteArray());
}
public DisplayPNGCharacteristics getDisplayCharacteristicsOfImage(String content) throws IOException
{
BufferedImage image = createPNGFromBase64Data(content);
DisplayPNGCharacteristics pngDescriptor = new DisplayPNGCharacteristics();
int bitDepth = image.getColorModel().getPixelSize();
pngDescriptor.setBitDepth(bitDepth);
pngDescriptor.setWidth(image.getWidth());
pngDescriptor.setHeight(image.getHeight());
return pngDescriptor;
}
public BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint, boolean higherQuality)
{
int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage)img;
int w, h;
h = img.getHeight();
w = img.getWidth();
if (higherQuality)
{
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached w = img.getWidth();
double scaleFactor = determineImageScale(w, h, targetWidth, targetHeight);
w = (int) (w * scaleFactor);
h = (int) (h * scaleFactor);
}
else
{
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call w = targetWidth;
h = targetHeight;
} w = targetWidth;
do
{
if (higherQuality && w > targetWidth)
{
w /= 2;
if (w < targetWidth)
{
w = targetWidth;
}
}
if (higherQuality && h > targetHeight)
{
h /= 2;
if (h < targetHeight)
{
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
}
while (w > targetWidth || h > targetHeight);
return ret;
}
public double determineImageScale(int sourceWidth, int sourceHeight, int targetWidth, int targetHeight)
{
double scalex = (double) targetWidth / sourceWidth;
double scaley = (double) targetHeight / sourceHeight;
return Math.min(scalex, scaley);
}
public String createBase64ImageFromText(String text, int width, int height) throws IOException {
int leftPadding = 2;
int rightPadding = 10;
TextDetails details = getFontSize(text, width - leftPadding - rightPadding, height);
if (logger.isDebugEnabled()) {
logger.debug("Creating PNG image from text, using font size: " + String.valueOf(details.getFontSize()) + " width: " + String.valueOf(width) + " and text: " + text);
}
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = new Font("Arial", Font.PLAIN, details.getFontSize());
g2d.setFont(font);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, width, height);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
int counter = 1;
for (String line : details.getLines()) {
g2d.drawString(line, leftPadding, fm.getAscent() * counter);
counter++;
}
g2d.dispose();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(img, "png", baos);
} catch (IOException e) {
logger.error("Failed to write the text to PNG image");
throw e;
}
return Base64.encodeBase64URLSafeString(baos.toByteArray());
}
public BufferedImage addTransparentBackground(BufferedImage sourceImage, int requiredWidth, int requiredHeight, boolean isCentred) {
int imageWidth = 0;
int imageHeight = 0;
BufferedImage combinedImage = null;
imageWidth = sourceImage.getWidth();
imageHeight = sourceImage.getHeight();
if (imageWidth > requiredWidth || imageHeight > requiredHeight) {
// TODO problem
}
combinedImage = new BufferedImage(requiredWidth, requiredHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = combinedImage.createGraphics();
if (isCentred) {
g2d.drawImage(sourceImage, (requiredWidth - imageWidth) / 2, (requiredHeight - imageHeight) / 2, null);
}
else {
g2d.drawImage(sourceImage, 0, 0, null);
}
return combinedImage == null ? sourceImage : combinedImage;
}
private TextDetails getFontSize(String text, int width, int height) {
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = null;
int testWidth = 0;
int testHeight = 0;
int numPossibleLines = 1;
TextDetails details = null;
// Use as large a font size as possible so start with largest
// 1. Can it fit on one line?
// 2. If not, can we fit it on multiple lines
// 3. If not, move down to next largest font
// 4. If on smallest font and still can't fit, make best effort to keep overrun to minimum
for (int size : fontSizes) {
font = new Font("Arial", Font.PLAIN, size);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
testWidth = fm.stringWidth(text);
if (testWidth <= width) {
// Fits on one line
g2d.dispose();
return new TextDetails(size, text, false);
}
else {
testHeight = fm.getHeight();
numPossibleLines = height / testHeight;
// Try to fit with multiple lines
if (testWidth <= width * numPossibleLines) {
// Theoretically possible to fit the text, depending on how line breaks work out
details = new TextDetails(size);
details = splitToMultiLine(text, width, numPossibleLines, fm, details);
if (!details.isOverrun()) {
g2d.dispose();
return details;
}
}
}
}
// Last chance saloon, it is going to overrun anyway so make the best effort possible at it
g2d.dispose();
return details;
}
private TextDetails splitToMultiLine(String text, int width, int numPossibleLines, FontMetrics fm, TextDetails details) {
// Recursive method to split main text string into a number of lines that will fit into the
if (fm.stringWidth(text) < width) {
// Text fits as is, add line and return with no overrun
details.addLine(text);
details.setOverrun(false);
return details;
}
else if (numPossibleLines == 1) {
// Overrun on last line, add line and return but with overrun
details.addLine(text);
details.setOverrun(true);
return details;
}
else {
// More than one line left to deal with, recursively measure and split
String thisLine = "";
String testLine = "";
String remainingText = null;
int index;
int prevIndex = 0;
while (prevIndex < text.length()) {
index = text.indexOf(" ", prevIndex);
if (index != -1) {
testLine = text.substring(0, index);
if (fm.stringWidth(testLine) < width) {
thisLine = testLine;
prevIndex = index+1;
}
else {
break;
}
}
else {
break;
}
}
details.addLine(thisLine);
remainingText = text.substring(thisLine.length() + 1);
return splitToMultiLine(remainingText, width, numPossibleLines - 1, fm, details);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy