/* * Copyright (C) 2008-2020, 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.util; import com.juick.model.Attachment; 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.FileUtils; import org.apache.commons.io.FilenameUtils; import org.imgscalr.Scalr; import org.imgscalr.Scalr.Rotation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; 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; import java.util.Iterator; public class ImageUtils { private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class); private String imgDir; private String tmpDir; public ImageUtils(String imgDir, String tmpDir) { this.imgDir = imgDir; this.tmpDir = tmpDir; } /** * 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 | IOException e) { // failed to read metadata. // nothing to do here, return image as is. } return image; } public void saveImageWithPreviews(String tempFilename, String outputFilename) throws IOException { String ext = FilenameUtils.getExtension(outputFilename); Path outputImagePath = Paths.get(imgDir, "p", outputFilename); // this throws strange exceptions // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile()); BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); int width = originalImage.getWidth(); int height = originalImage.getHeight(); int maxDimension = Math.max(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()); } public void saveAvatar(String tempFilename, int uid) 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 Attachment getAttachment(File imgFile) throws IOException { Attachment attachment = new Attachment(); try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) { Iterator iter = ImageIO.getImageReaders(stream); while (iter.hasNext()) { ImageReader reader = iter.next(); try { reader.setInput(stream); attachment.setWidth(reader.getWidth(reader.getMinIndex())); attachment.setHeight(reader.getHeight(reader.getMinIndex())); return attachment; } catch (Exception e) { logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath()); } finally { reader.dispose(); } } } logger.warn("Not a known image file {}", imgFile.getAbsolutePath()); return attachment; } }