001/* 002 * This code is licensed under the Apache License, Version 2.0 (the "License"); 003 * You may not use this file except in compliance with the License. 004 * You may obtain a copy of the License at 005 * 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * 008 * Unless required by applicable law or agreed to in writing, software 009 * distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and 012 * limitations under the License. 013 * 014 * Source 015 * http://blog.jaimon.co.uk/simpleimageinfo/SimpleImageInfo.java.html 016 * http://jaimonmathew.wordpress.com/2011/01/29/simpleimageinfo/ 017 * Revision history 018 * 0.1 - 29/Jan/2011 - Initial version created 019 * 0.x - 25/Oct/2011 - Refactoring and cleanups, adding length (Nuxeo) 020 * 021 * @author Jaimon Mathew <http://www.jaimon.co.uk> 022 * @author Florent Guillaume <http://nuxeo.com/> 023 */ 024 025package org.nuxeo.ecm.core.opencmis.impl.util; 026 027import java.io.BufferedInputStream; 028import java.io.IOException; 029import java.io.InputStream; 030 031/** 032 * A Java class to determine image width, height, length and MIME types for a number of image file formats without 033 * loading the whole image data. 034 */ 035public class SimpleImageInfo { 036 037 protected int width; 038 039 protected int height; 040 041 protected long length; 042 043 protected String mimeType; 044 045 protected InputStream in; 046 047 public SimpleImageInfo(InputStream stream) throws IOException { 048 if (stream instanceof BufferedInputStream) { 049 in = stream; 050 } else { 051 in = new BufferedInputStream(stream); 052 } 053 processStream(); 054 finishStream(); 055 stream.close(); 056 } 057 058 public int getWidth() { 059 return width; 060 } 061 062 public int getHeight() { 063 return height; 064 } 065 066 public long getLength() { 067 return length; 068 } 069 070 public String getMimeType() { 071 return mimeType; 072 } 073 074 protected void processStream() throws IOException { 075 int c1 = read(); 076 int c2 = read(); 077 int c3 = read(); 078 079 mimeType = "application/octet-stream"; 080 width = -1; 081 height = -1; 082 083 if (c1 == 'G' && c2 == 'I' && c3 == 'F') { // GIF 084 skip(3); 085 width = readInt(2, false); 086 height = readInt(2, false); 087 mimeType = "image/gif"; 088 } else if (c1 == 0xFF && c2 == 0xD8) { // JPG 089 while (c3 == 255) { 090 int marker = read(); 091 int len = readInt(2, true); 092 if (marker == 192 || marker == 193 || marker == 194) { 093 skip(1); 094 height = readInt(2, true); 095 width = readInt(2, true); 096 mimeType = "image/jpeg"; 097 break; 098 } 099 skip(len - 2); 100 c3 = read(); 101 } 102 } else if (c1 == 137 && c2 == 80 && c3 == 78) { // PNG 103 skip(15); 104 width = readInt(2, true); 105 skip(2); 106 height = readInt(2, true); 107 mimeType = "image/png"; 108 } else if (c1 == 66 && c2 == 77) { // BMP 109 skip(15); 110 width = readInt(2, false); 111 skip(2); 112 height = readInt(2, false); 113 mimeType = "image/bmp"; 114 } else { 115 int c4 = read(); 116 if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42) || (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) { // TIFF 117 boolean bigEndian = c1 == 'M'; 118 int ifd = 0; 119 int entries; 120 ifd = readInt(4, bigEndian); 121 skip(ifd - 8); 122 entries = readInt(2, bigEndian); 123 for (int i = 1; i <= entries; i++) { 124 int tag = readInt(2, bigEndian); 125 int fieldType = readInt(2, bigEndian); 126 @SuppressWarnings("unused") 127 long count = readInt(4, bigEndian); 128 int valOffset; 129 if ((fieldType == 3 || fieldType == 8)) { 130 valOffset = readInt(2, bigEndian); 131 skip(2); 132 } else { 133 valOffset = readInt(4, bigEndian); 134 } 135 if (tag == 256) { 136 width = valOffset; 137 } else if (tag == 257) { 138 height = valOffset; 139 } 140 if (width != -1 && height != -1) { 141 mimeType = "image/tiff"; 142 break; 143 } 144 } 145 } 146 } 147 } 148 149 protected int read() throws IOException { 150 int c = in.read(); 151 if (c != -1) { 152 length++; 153 } 154 return c; 155 } 156 157 protected void skip(long n) throws IOException { 158 long done = 0; 159 while (done < n) { 160 long num = in.skip(n - done); 161 if (num == 0) { 162 break; 163 } 164 done += num; 165 } 166 length += done; 167 } 168 169 protected void finishStream() throws IOException { 170 byte[] buf = new byte[4096]; 171 int n; 172 while ((n = in.read(buf)) != -1) { 173 length += n; 174 } 175 } 176 177 protected int readInt(int noOfBytes, boolean bigEndian) throws IOException { 178 int ret = 0; 179 int sv = bigEndian ? (noOfBytes - 1) * 8 : 0; 180 int cnt = bigEndian ? -8 : 8; 181 for (int i = 0; i < noOfBytes; i++) { 182 ret |= read() << sv; 183 sv += cnt; 184 } 185 return ret; 186 } 187 188}