/* * * A Java class to determine image width, height and MIME types for a number of * image file formats without loading the whole image data. * * Original name: SimpleImageInfo.java * Original version: 0.1 * * This file was originally created by Jaimon Mathew <http://www.jaimon.co.uk> * and licensed under the Apache License, Version 2.0. * * It was modified in order to satisfy code style check. * */ /* * * Copyright 2015 Andrey Yakovlev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package jodtemplate.image; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; //CHECKSTYLE.OFF: MagicNumber public class ImageMetadataExtractor { private int height; private int width; private String mimeType; public ImageMetadataExtractor(final File file) throws IOException { try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { processStream(is); } } public ImageMetadataExtractor(final InputStream is) throws IOException { processStream(is); } public ImageMetadataExtractor(final byte[] bytes) throws IOException { try (InputStream is = new ByteArrayInputStream(bytes)) { processStream(is); } } public int getHeight() { return height; } public void setHeight(final int height) { this.height = height; } public int getWidth() { return width; } public void setWidth(final int width) { this.width = width; } public String getMimeType() { return mimeType; } public void setMimeType(final String mimeType) { this.mimeType = mimeType; } @Override public String toString() { return "MIME Type : " + mimeType + "\t Width : " + width + "\t Height : " + height; } private void processStream(final InputStream is) throws IOException { final int c1 = is.read(); final int c2 = is.read(); final int c3 = is.read(); mimeType = null; width = -1; height = -1; if (isGif(c1, c2, c3)) { getGifInfo(is); } else if (isJpeg(c1, c2)) { getJpegInfo(is, c3); } else if (isPng(c1, c2, c3)) { getPngInfo(is); } else if (c1 == 66 && c2 == 77) { getBmpInfo(is); } else { final int c4 = is.read(); if (isTiff(c1, c2, c3, c4)) { getTiffInfo(is, c1); } } if (mimeType == null) { throw new IOException("Unsupported image type"); } } private boolean isTiff(final int c1, final int c2, final int c3, final int c4) { final boolean tiffCheck1 = c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42; final boolean tiffCheck2 = c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0; return tiffCheck1 || tiffCheck2; } private boolean isPng(final int c1, final int c2, final int c3) { return c1 == 137 && c2 == 80 && c3 == 78; } private boolean isJpeg(final int c1, final int c2) { return c1 == 0xFF && c2 == 0xD8; } private boolean isGif(final int c1, final int c2, final int c3) { return c1 == 'G' && c2 == 'I' && c3 == 'F'; } private void getTiffInfo(final InputStream is, final int c1) throws IOException { final boolean bigEndian = c1 == 'M'; int ifd = 0; int entries; ifd = readInt(is, 4, bigEndian); IOUtils.skipFully(is, ifd - 8); entries = readInt(is, 2, bigEndian); for (int i = 1; i <= entries; i++) { final int tag = readInt(is, 2, bigEndian); final int fieldType = readInt(is, 2, bigEndian); int valOffset; if (fieldType == 3 || fieldType == 8) { valOffset = readInt(is, 2, bigEndian); IOUtils.skipFully(is, 2); } else { valOffset = readInt(is, 4, bigEndian); } if (tag == 256) { width = valOffset; } else if (tag == 257) { height = valOffset; } if (width != -1 && height != -1) { mimeType = "tiff"; break; } } } private void getBmpInfo(final InputStream is) throws IOException { IOUtils.skipFully(is, 15); width = readInt(is, 2, false); IOUtils.skipFully(is, 2); height = readInt(is, 2, false); mimeType = "bmp"; } private void getPngInfo(final InputStream is) throws IOException { IOUtils.skipFully(is, 15); width = readInt(is, 2, true); IOUtils.skipFully(is, 2); height = readInt(is, 2, true); mimeType = "png"; } private void getJpegInfo(final InputStream is, final int c3) throws IOException { int c = c3; while (c == 255) { final int marker = is.read(); final int len = readInt(is, 2, true); if (marker == 192 || marker == 193 || marker == 194) { IOUtils.skipFully(is, 1); height = readInt(is, 2, true); width = readInt(is, 2, true); mimeType = "jpeg"; break; } IOUtils.skipFully(is, len - 2); c = is.read(); } } private void getGifInfo(final InputStream is) throws IOException { IOUtils.skipFully(is, 3); width = readInt(is, 2, false); height = readInt(is, 2, false); mimeType = "gif"; } private int readInt(final InputStream is, final int noOfBytes, final boolean bigEndian) throws IOException { int ret = 0; int sv = bigEndian ? (noOfBytes - 1) * 8 : 0; final int cnt = bigEndian ? -8 : 8; for (int i = 0; i < noOfBytes; i++) { ret |= is.read() << sv; sv += cnt; } return ret; } } // CHECKSTYLE.ON: MagicNumber