/* * Copyright (C) 2008-2017, Juick * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.juick.server.util; import org.apache.commons.imaging.ImageInfo; import org.apache.commons.imaging.ImageReadException; import org.apache.commons.imaging.Imaging; import org.apache.commons.imaging.common.ImageMetadata; import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; import org.apache.commons.imaging.formats.tiff.TiffField; import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; import org.apache.commons.io.FilenameUtils; import org.imgscalr.Scalr; import org.imgscalr.Scalr.Rotation; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; public class ImageUtils { /** * Returns BufferedImage, same as ImageIO.read() does. * *

JPEG images with EXIF metadata are rotated according to Orientation tag. * * @param imageFile a File to read from. */ private static BufferedImage readImageWithOrientation(File imageFile) throws IOException { BufferedImage image = ImageIO.read(imageFile); if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) { return image; } try { ImageMetadata metadata = Imaging.getMetadata(imageFile); if (metadata instanceof JpegImageMetadata) { JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION); if (orientationField != null) { int orientation = orientationField.getIntValue(); switch (orientation) { case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: image = Scalr.rotate(image, Rotation.CW_90); break; case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180: image = Scalr.rotate(image, Rotation.CW_180); break; case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: image = Scalr.rotate(image, Rotation.CW_270); break; case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL: image = Scalr.rotate(image, Rotation.FLIP_HORZ); break; case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL: image = Scalr.rotate(image, Rotation.FLIP_VERT); break; case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90); break; case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270); break; case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL: default: // do nothing break; } } } } catch (ImageReadException e) { // failed to read metadata. // nothing to do here, return image as is. } return image; } /** * Move attached image from temp folder to image folder. * Create preview images in corresponding folders. * * @param tempFilename Name of the image file in the temp folder. * @param outputFilename Name that will be used in the image folder. * @param tmpDir Path string for the temp folder. * @param imgDir Path string for the image folder. */ public static void saveImageWithPreviews(String tempFilename, String outputFilename, String tmpDir, String imgDir) throws IOException { String ext = FilenameUtils.getExtension(outputFilename); Path outputImagePath = Paths.get(imgDir, "p", outputFilename); Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); int width = originalImage.getWidth(); int height = originalImage.getHeight(); int maxDimension = (width > height) ? width : height; BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage; BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage; BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage; ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile()); ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile()); ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile()); } /** * Save new avatar in all required sizes. * * @param tempFilename Name of the image file in the temp folder. * @param uid User id that is used to build image file names. * @param tmpDir Path string for the temp folder. * @param imgDir Path string for the image folder. */ public static void saveAvatar(String tempFilename, int uid, String tmpDir, String imgDir) throws IOException { String ext = FilenameUtils.getExtension(tempFilename); String originalName = String.format("%s.%s", uid, ext); Path originalPath = Paths.get(imgDir, "ao", originalName); Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING); BufferedImage originalImage = ImageIO.read(originalPath.toFile()); String targetExt = "png"; String targetName = String.format("%s.%s", uid, targetExt); ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile()); ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile()); } public static Integer getImageHeight(File imageFile) throws IOException, ImageReadException { ImageInfo info = Imaging.getImageInfo(imageFile); return info.getHeight(); } public static Integer getImageWidth(File imageFile) throws IOException, ImageReadException { ImageInfo info = Imaging.getImageInfo(imageFile); return info.getWidth(); } }