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 022 * @author Florent Guillaume 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 readInt(4, bigEndian); // count unused 127 int valOffset; 128 if ((fieldType == 3 || fieldType == 8)) { 129 valOffset = readInt(2, bigEndian); 130 skip(2); 131 } else { 132 valOffset = readInt(4, bigEndian); 133 } 134 if (tag == 256) { 135 width = valOffset; 136 } else if (tag == 257) { 137 height = valOffset; 138 } 139 if (width != -1 && height != -1) { 140 mimeType = "image/tiff"; 141 break; 142 } 143 } 144 } 145 } 146 } 147 148 protected int read() throws IOException { 149 int c = in.read(); 150 if (c != -1) { 151 length++; 152 } 153 return c; 154 } 155 156 protected void skip(int n) throws IOException { 157 long done = 0; 158 while (done < n) { 159 long num = in.skip(n - done); 160 if (num == 0) { 161 break; 162 } 163 done += num; 164 } 165 length += done; 166 } 167 168 protected void finishStream() throws IOException { 169 byte[] buf = new byte[4096]; 170 int n; 171 while ((n = in.read(buf)) != -1) { 172 length += n; 173 } 174 } 175 176 protected int readInt(int noOfBytes, boolean bigEndian) throws IOException { 177 int ret = 0; 178 int sv = bigEndian ? (noOfBytes - 1) * 8 : 0; 179 int cnt = bigEndian ? -8 : 8; 180 for (int i = 0; i < noOfBytes; i++) { 181 ret |= read() << sv; 182 sv += cnt; 183 } 184 return ret; 185 } 186 187}