I think there has to be a short solution either through some ImageIO
classes, or maybe even ImageObserver?
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
will appear to be 0x0 until late in its career, which is what usually
screws you up.
If you want to find out before reading the *.gif, you need some code
something like this:
Here is a simple one I wrote:
package com.mindprod.business;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import com.mindprod.ledatastream.LEDataInputStream;
/**
* Rapidly get width and height information about
* a gif or jpg or png.
*
* @author Roedy Green
* @version 1.0
* @since 2003 May 15
*/
public class ImageInfo
{
/**
* get the height and width of a gif or jpg image
* without having to read the entire Image into RAM.
* @see com.mindprod.ledatastream.LEDataStream
* for how little endian reads are done.
* Available from
http://mindprod.com/products1.html#LEDATASTREAM
*
* @param imageFilename filename. Must end in .jpg or .gif
* @return length-2 array of two numbers, width and height
* of the image, or 0,0 if it could not be found.
* We don't return a Dimension object because it
* provides doubles, not ints.
*/
public static int[] getImageDimensions( String imageFilename )
{
int width = 0;
int height = 0;
LEDataInputStream inle = null;
DataInputStream inbe = null;
try
{
try
{
if ( imageFilename.endsWith( ".gif" ) )
{
// signature GIF89a i.e. 0x474946383961
// or GIF87a
// just check first 4 chars
// width at offset 0x06 and height at 0x08 16-bit
little endian
inle = new LEDataInputStream ( new FileInputStream (
imageFilename ) );
int signature4 = inle.readInt( );
if ( signature4 != 0x38464947 /* reversed */ )
{
throw new IOException( "not a valid gif" );
}
inle.skipBytes(2);
width = inle.readShort();
height = inle.readShort();
inle.close();
}
else if ( imageFilename.endsWith( ".jpg" ) )
{
// ffd8
// in variable location: height, then width, big
endian.
inbe = new DataInputStream ( new FileInputStream (
imageFilename ) );
if ( inbe.readUnsignedByte() != 0xff )
{
throw new IOException( "not a valid jpg" );
}
if ( inbe.readUnsignedByte() != 0xd8 )
{
throw new IOException( "not a valid jpg" );
}
while ( true )
{
int p1 = inbe.readUnsignedByte();
int p2 = inbe.readUnsignedByte();
if ( p1 == 0xff && 0xc0 <= p2 && p2 <= 0xc3 )
{
inbe.skipBytes(3);
height = inbe.readShort();
width = inbe.readShort();
break;
}
else
{
// bypass this marker
int length = inbe.readShort();
inbe.skipBytes ( length-2 );
}
} // end while
inbe.close();
} // end else
if ( imageFilename.endsWith( ".png" ) )
{
// see
http://mindprod.com/jgloss/png.html
// The PNG file header looks like this:
// signature \211PNG\r\n\032\n 8-bytes
// ie. in hex 89504e470d0a1a0a
// chunksize 4 bytes 0x0000000D
// chunkid 4 bytes "IHDR" 0x49484452
// width 4 bytes big-endian binary
// height 4 bytes big-endian binary
inbe = new DataInputStream ( new FileInputStream (
imageFilename ) );
long signature = inbe.readLong();
if ( signature != 0x89504e470d0a1a0aL )
{
throw new IOException( "not a valid png file" );
}
inbe.skipBytes( 4+4 );
width = inbe.readInt();
height = inbe.readInt();
inbe.close();
}
// other file types will default to 0,0
} // end try
catch ( IOException e )
{
if ( inle != null )
{
inle.close();
}
if ( inbe != null )
{
inbe.close();
}
width = 0;
height = 0;
}
}
catch ( Exception e )
{
width = 0;
height = 0;
}
return new int[] { width, height };
} // end getImageDimensions
/**
* Test driver.
*
* @param args name of a *.gif or *.jpg or *.png image file to
test.
* Should print out its width and height.
*/
public static void main( String[] args )
{
if ( args.length != 1 )
{
System.out.println( "Need exactly one image filename on the
command line." );
}
String imageFilename = args[0];
int[] d = getImageDimensions( imageFilename );
System.out.println( imageFilename + " width:" + d[0] + "
height:" + d[1] );
}
} // end class ImageInfo
here is a fancy one.
package com.mindprod.htmlmacros;
import java.io.DataInput;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
/*
* ImageInfo.java
*
* Version 1..
*
* A Java class to determine image width, height and color depth for
* a number of image file formats.
*
* Written by Marco Schmidt <
[email protected]>
*
* Contributed to the Public Domain.
*
* Last modification 2002-06-17
* verlsion 1.4 2005-07-02 flipped to Java 1.5 checking.
*/
import java.io.DataInput;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
/**
* Get file format, image resolution, number of bits per pixel and
optionally
* number of images, comments and physical resolution from
* JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM, PPM, PSD and SWF
files
* (or input streams).
* <p>
* Use the class like this:
* <pre>
* ImageInfo ii = new ImageInfo();
* ii.setInput(in); // in can be InputStream or RandomAccessFile
* ii.setDetermineImageNumber(true); // default is false
* ii.setCollectComments(true); // default is false
* if (!ii.check()) {
* System.err.println("Not a supported image file format.");
* return;
* }
* System.out.println(ii.getFormatName() + ", " + ii.getMimeType() +
* ", " + ii.getWidth() + " x " + ii.getHeight() + " pixels, " +
* ii.getBitsPerPixel() + " bits per pixel, " +
ii.getNumberOfImages() +
* " image(s), " + ii.getNumberOfComments() + " comment(s).");
* </pre>
* You can also use this class as a command line program.
* Call it with a number of image file names as parameters:
* <pre>
* java ImageInfo *.jpg *.png *.gif
* </pre>
* or call it without parameters and pipe data to it:
* <pre>
* cat image.jpg | java ImageInfo
* </pre>
* <p>
* Known limitations:
* <ul>
* <li>When the determination of the number of images is turned off,
GIF bits
* per pixel are only read from the global header.
* For some GIFs, local palettes change this to a typically larger
* value. To be certain to get the correct color depth, call
* setDetermineImageNumber(true) before calling check().
* The complete scan over the GIF file will take additional
time.</li>
* <li>Transparency information is not included in the bits per pixel
count.
* Actually, it was my decision not to include those bits, so it's a
feature! ;-)</li>
* </ul>
* <p>
* Requirements:
* <ul>
* <li>Java 1.1 or higher</li>
* </ul>
* <p>
* The latest version can be found at <a
href="
http://www.geocities.com/marcoschmidt.geo/image-info.html">
http://www.geocities.com/marcoschmidt.geo/image-info.html</a>.
* <p>
* Written by <a
href="mailto:
[email protected]">Marco Schmidt</a>.
* <p>
* This class is contributed to the Public Domain.
* Use it at your own risk.
* <p>
* Last modification 2002-06-17.
* <p>
* History:
* <ul>
* <li><strong>2001-08-24</strong> Initial version.</li>
* <li><strong>2001-10-13</strong> Added support for the file formats
BMP and PCX.</li>
* <li><strong>2001-10-16</strong> Fixed bug in read(int[], int, int)
that returned
* <li><strong>2002-01-22</strong> Added support for file formats
Amiga IFF and Sun Raster (RAS).</li>
* <li><strong>2002-01-24</strong> Added support for file formats
Portable Bitmap / Graymap / Pixmap (PBM, PGM, PPM) and Adobe Photoshop
(PSD).
* Added new method getMimeType() to return the MIME type associated
with a particular file format.</li>
* <li><strong>2002-03-15</strong> Added support to recognize number
of images in file. Only works with GIF.
* Use {@link #setDetermineImageNumber} with <code>true</code> as
argument to identify animated GIFs
* ({@link #getNumberOfImages()} will return a value larger than
<code>1</code>).</li>
* <li><strong>2002-04-10</strong> Fixed a bug in the feature
'determine number of images in animated GIF' introduced with version
1.1.
* Thanks to Marcelo P. Lima for sending in the bug report.
* Released as 1.1.1.</li>
* <li><strong>2002-04-18</strong> Added {@link
#setCollectComments(boolean)}.
* That new method lets the user specify whether textual comments are
to be
* stored in an internal list when encountered in an input image file
/ stream.
* Added two methods to return the physical width and height of the
image in dpi:
* {@link #getPhysicalWidthDpi()} and {@link
#getPhysicalHeightDpi()}.
* If the physical resolution could not be retrieved, these methods
return <code>-1</code>.
* </li>
* <li><strong>2002-04-23</strong> Added support for the new
properties physical resolution and
* comments for some formats. Released as 1.2.</li>
* <li><strong>2002-06-17</strong> Added support for SWF, sent in by
Michael Aird.
* Changed checkJpeg() so that other APP markers than APP0 will not
lead to a failure anymore.
* Released as 1.3.</li>
* </ul>
*/
public class ImageInfo
{
/**
* Return value of {@link #getFormat()} for JPEG streams.
* ImageInfo can extract physical resolution and comments
* from JPEGs (only from APP0 headers).
* Only one image can be stored in a file.
*/
public static final int FORMAT_JPEG = 0;
/**
* Return value of {@link #getFormat()} for GIF streams.
* ImageInfo can extract comments from GIFs and count the number
* of images (GIFs with more than one image are animations).
* If you know of a place where GIFs store the physical resolution
* of an image, please
* <a
href="
http://www.geocities.com/marcoschmidt.geo/contact.html">send me
a mail</a>!
*/
public static final int FORMAT_GIF = 1;
/**
* Return value of {@link #getFormat()} for PNG streams.
* PNG only supports one image per file.
* Both physical resolution and comments can be stored with PNG,
* but ImageInfo is currently not able to extract those.
*/
public static final int FORMAT_PNG = 2;
/**
* Return value of {@link #getFormat()} for BMP streams.
* BMP only supports one image per file.
* BMP does not allow for comments.
* The physical resolution can be stored.
* <em>The specification that I have says that the values must be
* interpreted as dots per meter. However, given that I only
* encounter typical dpi values like 72 or 300, I currently
* consider those values dpi. Maybe someone can shed some light
* on this, please send me a mail in that case.</em>
*/
public static final int FORMAT_BMP = 3;
/**
* Return value of {@link #getFormat()} for PCX streams.
* PCX does not allow for comments or more than one image per file.
* However, the physical resolution can be stored.
*/
public static final int FORMAT_PCX = 4;
/**
* Return value of {@link #getFormat()} for IFF streams.
*/
public static final int FORMAT_IFF = 5;
/**
* Return value of {@link #getFormat()} for RAS streams.
* Sun Raster allows for one image per file only and is not able to
* store physical resolution or comments.
*/
public static final int FORMAT_RAS = 6;
/** Return value of {@link #getFormat()} for PBM streams. */
public static final int FORMAT_PBM = 7;
/** Return value of {@link #getFormat()} for PGM streams. */
public static final int FORMAT_PGM = 8;
/** Return value of {@link #getFormat()} for PPM streams. */
public static final int FORMAT_PPM = 9;
/** Return value of {@link #getFormat()} for PSD streams. */
public static final int FORMAT_PSD = 10;
/** Return value of {@link #getFormat()} for SWF (Shockwave)
streams. */
public static final int FORMAT_SWF = 11;
/**
* The names of all supported file formats.
* The FORMAT_xyz int constants can be used as index values for
* this array.
*/
private static final String[] FORMAT_NAMES =
{"JPEG", "GIF", "PNG", "BMP", "PCX",
"IFF", "RAS", "PBM", "PGM", "PPM",
"PSD", "SWF"};
/**
* The names of the MIME types for all supported file formats.
* The FORMAT_xyz int constants can be used as index values for
* this array.
*/
private static final String[] MIME_TYPE_STRINGS =
{"image/jpeg", "image/gif", "image/png", "image/bmp", "image/pcx",
"image/iff", "image/ras", "image/x-portable-bitmap",
"image/x-portable-graymap", "image/x-portable-pixmap",
"image/psd", "application/x-shockwave-flash"};
private int width;
private int height;
private int bitsPerPixel;
private int format;
private InputStream in;
private DataInput din;
private boolean collectComments = true;
private ArrayList<String> comments;
private boolean determineNumberOfImages;
private int numberOfImages;
private int physicalHeightDpi;
private int physicalWidthDpi;
private int bitBuf;
private int bitPos;
private void addComment(String s)
{
if ( comments == null )
{
comments = new ArrayList<String>();
}
comments.add(s);
}
/**
* Call this method after you have provided an input stream or file
* using {@link #setInput(InputStream)} or {@link
#setInput(DataInput)}.
* If true is returned, the file format was known and you
information
* about its content can be retrieved using the various getXyz
methods.
* @return if information could be retrieved from input
*/
public boolean check()
{
format = -1;
width = -1;
height = -1;
bitsPerPixel = -1;
numberOfImages = 1;
physicalHeightDpi = -1;
physicalWidthDpi = -1;
comments = null;
try
{
int b1 = read() & 0xff;
int b2 = read() & 0xff;
if ( b1 == 0x47 && b2 == 0x49 )
{
return checkGif();
}
else
if ( b1 == 0x89 && b2 == 0x50 )
{
return checkPng();
}
else
if ( b1 == 0xff && b2 == 0xd8 )
{
return checkJpeg();
}
else
if ( b1 == 0x42 && b2 == 0x4d )
{
return checkBmp();
}
else
if ( b1 == 0x0a && b2 < 0x06 )
{
return checkPcx();
}
else
if ( b1 == 0x46 && b2 == 0x4f )
{
return checkIff();
}
else
if ( b1 == 0x59 && b2 == 0xa6 )
{
return checkRas();
}
else
if ( b1 == 0x50 && b2 >= 0x31 && b2 <= 0x36 )
{
return checkPnm(b2 - '0');
}
else
if ( b1 == 0x38 && b2 == 0x42 )
{
return checkPsd();
}
else
if ( b1 == 0x46 && b2 == 0x57 )
{
return checkSwf();
}
else
{
return false;
}
}
catch ( IOException ioe )
{
return false;
}
}
private boolean checkBmp() throws IOException {
byte[] a = new byte[44];
if ( read(a) != a.length )
{
return false;
}
width = getIntLittleEndian(a, 16);
height = getIntLittleEndian(a, 20);
if ( width < 1 || height < 1 )
{
return false;
}
bitsPerPixel = getShortLittleEndian(a, 26);
if ( bitsPerPixel != 1 && bitsPerPixel != 4 &&
bitsPerPixel != 8 && bitsPerPixel != 16 &&
bitsPerPixel != 24 && bitsPerPixel != 32 )
{
return false;
}
int x = getIntLittleEndian(a, 36);
if ( x > 0 )
{
setPhysicalWidthDpi(x);
}
int y = getIntLittleEndian(a, 40);
if ( y > 0 )
{
setPhysicalHeightDpi(y);
}
format = FORMAT_BMP;
return true;
}
private boolean checkGif() throws IOException {
final byte[] GIF_MAGIC_87A = {0x46, 0x38, 0x37, 0x61};
final byte[] GIF_MAGIC_89A = {0x46, 0x38, 0x39, 0x61};
byte[] a = new byte[11]; // 4 from the GIF signature + 7 from
the global header
if ( read(a) != 11 )
{
return false;
}
if ( (!equals(a, 0, GIF_MAGIC_89A, 0, 4)) &&
(!equals(a, 0, GIF_MAGIC_87A, 0, 4)) )
{
return false;
}
format = FORMAT_GIF;
width = getShortLittleEndian(a, 4);
height = getShortLittleEndian(a, 6);
int flags = a[8] & 0xff;
bitsPerPixel = ((flags >> 4) & 0x07) + 1;
if ( !determineNumberOfImages )
{
return true;
}
// skip global color palette
if ( (flags & 0x80) != 0 )
{
int tableSize = (1 << ((flags & 7) + 1)) * 3;
skip(tableSize);
}
numberOfImages = 0;
int blockType;
do
{
blockType = read();
switch ( blockType )
{
case(0x2c): // image separator
{
if ( read(a, 0, 9) != 9 )
{
return false;
}
flags = a[8] & 0xff;
int localBitsPerPixel = (flags & 0x07) + 1;
if ( localBitsPerPixel > bitsPerPixel )
{
bitsPerPixel = localBitsPerPixel;
}
if ( (flags & 0x80) != 0 )
{
skip((1 << localBitsPerPixel) * 3);
}
skip(1); // initial code length
int n;
do
{
n = read();
if ( n > 0 )
{
skip(n);
}
else
if ( n == -1 )
{
return false;
}
}
while ( n > 0 );
numberOfImages++;
break;
}
case(0x21): // extension
{
int extensionType = read();
if ( collectComments && extensionType == 0xfe )
{
StringBuilder sb = new StringBuilder();
int n;
do
{
n = read();
if ( n == -1 )
{
return false;
}
if ( n > 0 )
{
for ( int i = 0; i < n; i++ )
{
int ch = read();
if ( ch == -1 )
{
return false;
}
sb.append((char)ch);
}
}
}
while ( n > 0 );
}
else
{
int n;
do
{
n = read();
if ( n > 0 )
{
skip(n);
}
else
if ( n == -1 )
{
return false;
}
}
while ( n > 0 );
}
break;
}
case(0x3b): // end of file
{
break;
}
default:
{
return false;
}
}
}
while ( blockType != 0x3b );
return true;
}
private boolean checkIff() throws IOException {
byte[] a = new byte[10];
// read remaining 2 bytes of file id, 4 bytes file size
// and 4 bytes IFF subformat
if ( read(a, 0, 10) != 10 )
{
return false;
}
final byte[] IFF_RM = {0x52, 0x4d};
if ( !equals(a, 0, IFF_RM, 0, 2) )
{
return false;
}
int type = getIntBigEndian(a, 6);
if ( type != 0x494c424d && // type must be ILBM...
type != 0x50424d20 )
{ // ...or PBM
return false;
}
// loop chunks to find BMHD chunk
do
{
if ( read(a, 0, 8) != 8 )
{
return false;
}
int chunkId = getIntBigEndian(a, 0);
int size = getIntBigEndian(a, 4);
if ( (size & 1) == 1 )
{
size++;
}
if ( chunkId == 0x424d4844 )
{ // BMHD chunk
if ( read(a, 0, 9) != 9 )
{
return false;
}
format = FORMAT_IFF;
width = getShortBigEndian(a, 0);
height = getShortBigEndian(a, 2);
bitsPerPixel = a[8] & 0xff;
return(width > 0 && height > 0 && bitsPerPixel > 0 &&
bitsPerPixel < 33);
}
else
{
skip(size);
}
} while ( true );
}
private boolean checkJpeg() throws IOException {
byte[] data = new byte[12];
while ( true )
{
if ( read(data, 0, 4) != 4 )
{
return false;
}
int marker = getShortBigEndian(data, 0);
int size = getShortBigEndian(data, 2);
if ( (marker & 0xff00) != 0xff00 )
{
return false; // not a valid marker
}
if ( marker == 0xffe0 )
{ // APPx
if ( size < 14 )
{
return false; // APPx header must be larger than 14
bytes
}
if ( read(data, 0, 12) != 12 )
{
return false;
}
final byte[] APP0_ID = {0x4a, 0x46, 0x49, 0x46, 0x00};
if ( equals(APP0_ID, 0, data, 0, 5) )
{
if ( data[7] == 1 )
{
setPhysicalWidthDpi(getShortBigEndian(data, 8));
setPhysicalHeightDpi(getShortBigEndian(data, 10));
}
else
if ( data[7] == 2 )
{
int x = getShortBigEndian(data, 8);
int y = getShortBigEndian(data, 10);
setPhysicalWidthDpi((int)(x * 2.54f));
setPhysicalHeightDpi((int)(y * 2.54f));
}
}
skip(size - 14);
}
else
if ( collectComments && size > 2 && marker == 0xfffe )
{ // comment
size -= 2;
byte[] chars = new byte[size];
if ( read(chars, 0, size) != size )
{
return false;
}
String comment = new String(chars, "iso-8859-1");
comment = comment.trim();
System.out.println(comment);
addComment(comment);
}
else
if ( marker >= 0xffc0 && marker <= 0xffcf && marker !=
0xffc4 && marker != 0xffc8 )
{
if ( read(data, 0, 6) != 6 )
{
return false;
}
format = FORMAT_JPEG;
bitsPerPixel = (data[0] & 0xff) * (data[5] & 0xff);
width = getShortBigEndian(data, 3);
height = getShortBigEndian(data, 1);
return true;
}
else
{
skip(size - 2);
}
}
}
private boolean checkPcx() throws IOException {
byte[] a = new byte[64];
if ( read(a) != a.length )
{
return false;
}
if ( a[0] != 1 )
{ // encoding, 1=RLE is only valid value
return false;
}
// width / height
int x1 = getShortLittleEndian(a, 2);
int y1 = getShortLittleEndian(a, 4);
int x2 = getShortLittleEndian(a, 6);
int y2 = getShortLittleEndian(a, 8);
if ( x1 < 0 || x2 < x1 || y1 < 0 || y2 < y1 )
{
return false;
}
width = x2 - x1 + 1;
height = y2 - y1 + 1;
// color depth
int bits = a[1];
int planes = a[63];
if ( planes == 1 &&
(bits == 1 || bits == 2 || bits == 4 || bits == 8) )
{
// paletted
bitsPerPixel = bits;
}
else
if ( planes == 3 && bits == 8 )
{
// RGB truecolor
bitsPerPixel = 24;
}
else
{
return false;
}
setPhysicalWidthDpi(getShortLittleEndian(a, 10));
setPhysicalHeightDpi(getShortLittleEndian(a, 10));
format = FORMAT_PCX;
return true;
}
private boolean checkPng() throws IOException {
final byte[] PNG_MAGIC = {0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
byte[] a = new byte[24];
if ( read(a) != 24 )
{
return false;
}
if ( !equals(a, 0, PNG_MAGIC, 0, 6) )
{
return false;
}
format = FORMAT_PNG;
width = getIntBigEndian(a, 14);
height = getIntBigEndian(a, 18);
bitsPerPixel = a[22] & 0xff;
int colorType = a[23] & 0xff;
if ( colorType == 2 || colorType == 6 )
{
bitsPerPixel *= 3;
}
return true;
}
private boolean checkPnm(int id) throws IOException {
if ( id < 1 || id > 6 )
{
return false;
}
final int[] PNM_FORMATS = {FORMAT_PBM, FORMAT_PGM, FORMAT_PPM};
format = PNM_FORMATS[(id - 1) % 3];
boolean hasPixelResolution = false;
String s;
while ( true )
{
s = readLine();
if ( s != null )
{
s = s.trim();
}
if ( s == null || s.length() < 1 )
{
continue;
}
if ( s.charAt(0) == '#' )
{ // comment
if ( collectComments && s.length() > 1 )
{
addComment(s.substring(1));
}
continue;
}
if ( !hasPixelResolution )
{ // split "343 966" into width=343, height=966
int spaceIndex = s.indexOf(' ');
if ( spaceIndex == -1 )
{
return false;
}
String widthString = s.substring(0, spaceIndex);
spaceIndex = s.lastIndexOf(' ');
if ( spaceIndex == -1 )
{
return false;
}
String heightString = s.substring(spaceIndex + 1);
try
{
width = Integer.parseInt(widthString);
height = Integer.parseInt(heightString);
}
catch ( NumberFormatException nfe )
{
return false;
}
if ( width < 1 || height < 1 )
{
return false;
}
if ( format == FORMAT_PBM )
{
bitsPerPixel = 1;
return true;
}
hasPixelResolution = true;
}
else
{
int maxSample;
try
{
maxSample = Integer.parseInt(s);
}
catch ( NumberFormatException nfe )
{
return false;
}
if ( maxSample < 0 )
{
return false;
}
for ( int i = 0; i < 25; i++ )
{
if ( maxSample < (1 << (i + 1)) )
{
bitsPerPixel = i + 1;
if ( format == FORMAT_PPM )
{
bitsPerPixel *= 3;
}
return true;
}
}
return false;
}
}
}
private boolean checkPsd() throws IOException {
byte[] a = new byte[24];
if ( read(a) != a.length )
{
return false;
}
final byte[] PSD_MAGIC = {0x50, 0x53};
if ( !equals(a, 0, PSD_MAGIC, 0, 2) )
{
return false;
}
format = FORMAT_PSD;
width = getIntBigEndian(a, 16);
height = getIntBigEndian(a, 12);
int channels = getShortBigEndian(a, 10);
int depth = getShortBigEndian(a, 20);
bitsPerPixel = channels * depth;
return(width > 0 && height > 0 && bitsPerPixel > 0 &&
bitsPerPixel <= 64);
}
private boolean checkRas() throws IOException {
byte[] a = new byte[14];
if ( read(a) != a.length )
{
return false;
}
final byte[] RAS_MAGIC = {0x6a, (byte)0x95};
if ( !equals(a, 0, RAS_MAGIC, 0, 2) )
{
return false;
}
format = FORMAT_RAS;
width = getIntBigEndian(a, 2);
height = getIntBigEndian(a, 6);
bitsPerPixel = getIntBigEndian(a, 10);
return(width > 0 && height > 0 && bitsPerPixel > 0 &&
bitsPerPixel <= 24);
}
// Written by Michael Aird.
private boolean checkSwf() throws IOException {
//get rid of the last byte of the signature, the byte of the
version and 4 bytes of the size
byte[] a = new byte[6];
if ( read(a) != a.length )
{
return false;
}
format = FORMAT_SWF;
int bitSize = (int)readUBits( 5 );
int minX = (int)readSBits( bitSize );
int maxX = (int)readSBits( bitSize );
int minY = (int)readSBits( bitSize );
int maxY = (int)readSBits( bitSize );
width = maxX/20; //cause we're in twips
height = maxY/20; //cause we're in twips
setPhysicalWidthDpi(72);
setPhysicalHeightDpi(72);
return(width > 0 && height > 0);
}
/**
* Run over String list, return false iff at least one of the
arguments
* equals <code>-c</code>.
*/
private static boolean determineVerbosity(String[] args)
{
if ( args != null && args.length > 0 )
{
for ( int i = 0; i < args.length; i++ )
{
if ( "-c".equals(args
) )
{
return false;
}
}
}
return true;
}
private boolean equals(byte[] a1, int offs1, byte[] a2, int offs2,
int num)
{
while ( num-- > 0 )
{
if ( a1[offs1++] != a2[offs2++] )
{
return false;
}
}
return true;
}
/**
* If {@link #check()} was successful, returns the image's number
of bits per pixel.
* Does not include transparency information like the alpha
channel.
* @return number of bits per image pixel
*/
public int getBitsPerPixel()
{
return bitsPerPixel;
}
/**
* Returns the index'th comment retrieved from the image.
* @throws IllegalArgumentException if index is smaller than 0 or
larger than or equal
* to the number of comments retrieved
* @see #getNumberOfComments
*/
public String getComment(int index)
{
if ( comments == null || index < 0 || index >= comments.size() )
{
throw new IllegalArgumentException("Not a valid comment
index: " + index);
}
return(String)comments.get(index);
}
/**
* If {@link #check()} was successful, returns the image format as
one
* of the FORMAT_xyz constants from this class.
* Use {@link #getFormatName()} to get a textual description of the
file format.
* @return file format as a FORMAT_xyz constant
*/
public int getFormat()
{
return format;
}
/**
* If {@link #check()} was successful, returns the image format's
name.
* Use {@link #getFormat()} to get a unique number.
* @return file format name
*/
public String getFormatName()
{
if ( format >= 0 && format < FORMAT_NAMES.length )
{
return FORMAT_NAMES[format];
}
else
{
return "?";
}
}
/**
* If {@link #check()} was successful, returns one the image's
vertical
* resolution in pixels.
* @return image height in pixels
*/
public int getHeight()
{
return height;
}
private int getIntBigEndian(byte[] a, int offs)
{
return
(a[offs] & 0xff) << 24 |
(a[offs + 1] & 0xff) << 16 |
(a[offs + 2] & 0xff) << 8 |
a[offs + 3] & 0xff;
}
private int getIntLittleEndian(byte[] a, int offs)
{
return
(a[offs + 3] & 0xff) << 24 |
(a[offs + 2] & 0xff) << 16 |
(a[offs + 1] & 0xff) << 8 |
a[offs] & 0xff;
}
/**
* If {@link #check()} was successful, returns a String with the
* MIME type of the format.
* @return MIME type, e.g. <code>image/jpeg</code>
*/
public String getMimeType()
{
if ( format >= 0 && format < MIME_TYPE_STRINGS.length )
{
return MIME_TYPE_STRINGS[format];
}
else
{
return null;
}
}
/**
* If {@link #check()} was successful and {@link
#setCollectComments(boolean)} was called with
* <code>true</code> as argument, returns the number of comments
retrieved
* from the input image stream / file.
* Any number >= 0 and smaller than this number of comments is
then a
* valid argument for the {@link #getComment(int)} method.
* @return number of comments retrieved from input image
*/
public int getNumberOfComments()
{
if ( comments == null )
{
return 0;
}
else
{
return comments.size();
}
}
/**
* Returns the number of images in the examined file.
* Assumes that <code>setDetermineImageNumber(true);</code> was
called before
* a successful call to {@link #check()}.
* This value can currently be only different from <code>1</code>
for GIF images.
* @return number of images in file
*/
public int getNumberOfImages()
{
return numberOfImages;
}
/**
* Returns the physical height of this image in dots per inch
(dpi).
* Assumes that {@link #check()} was successful.
* Returns <code>-1</code> on failure.
* @return physical height (in dpi)
* @see #getPhysicalWidthDpi()
* @see #getPhysicalHeightInch()
*/
public int getPhysicalHeightDpi()
{
return physicalHeightDpi;
}
/**
* If {@link #check()} was successful, returns the physical width
of this image in dpi (dots per inch)
* or -1 if no value could be found.
* @return physical height (in dpi)
* @see #getPhysicalHeightDpi()
* @see #getPhysicalWidthDpi()
* @see #getPhysicalWidthInch()
*/
public float getPhysicalHeightInch()
{
int h = getHeight();
int ph = getPhysicalHeightDpi();
if ( h > 0 && ph > 0 )
{
return((float)h) / ((float)ph);
}
else
{
return -1.0f;
}
}
/**
* If {@link #check()} was successful, returns the physical width
of this image in dpi (dots per inch)
* or -1 if no value could be found.
* @return physical width (in dpi)
* @see #getPhysicalHeightDpi()
* @see #getPhysicalWidthInch()
* @see #getPhysicalHeightInch()
*/
public int getPhysicalWidthDpi()
{
return physicalWidthDpi;
}
/**
* Returns the physical width of an image in inches, or
* <code>-1.0f</code> if width information is not available.
* Assumes that {@link #check} has been called successfully.
* @return physical width in inches or <code>-1.0f</code> on
failure
* @see #getPhysicalWidthDpi
* @see #getPhysicalHeightInch
*/
public float getPhysicalWidthInch()
{
int w = getWidth();
int pw = getPhysicalWidthDpi();
if ( w > 0 && pw > 0 )
{
return((float)w) / ((float)pw);
}
else
{
return -1.0f;
}
}
private int getShortBigEndian(byte[] a, int offs)
{
return
(a[offs] & 0xff) << 8 |
(a[offs + 1] & 0xff);
}
private int getShortLittleEndian(byte[] a, int offs)
{
return(a[offs] & 0xff) | (a[offs + 1] & 0xff) << 8;
}
/**
* If {@link #check()} was successful, returns one the image's
horizontal
* resolution in pixels.
* @return image width in pixels
*/
public int getWidth()
{
return width;
}
/**
* To use this class as a command line application, give it either
* some file names as parameters (information on them will be
* printed to standard output, one line per file) or call
* it with no parameters. It will then check data given to it
* via standard input.
* @param args the program arguments which must be file names
*/
public static void main(String[] args)
{
ImageInfo imageInfo = new ImageInfo();
boolean verbose = determineVerbosity(args);
if ( args.length == 0 )
{
run(null, System.in, imageInfo, verbose);
}
else
{
int index = 0;
while ( index < args.length )
{
FileInputStream instream = null;
try
{
String filename = args[index++];
System.out.print(filename + ";");
instream = new FileInputStream(filename);
run(filename, instream, imageInfo, verbose);
instream.close();
}
catch ( Exception e )
{
System.out.println(e);
try
{
instream.close();
}
catch ( Exception ee )
{
}
}
}
}
}
private static void print(String sourceName, ImageInfo ii, boolean
verbose)
{
if ( verbose )
{
printVerbose(sourceName, ii);
}
else
{
printCompact(sourceName, ii);
}
}
private static void printCompact(String sourceName, ImageInfo
imageInfo)
{
System.out.println(
imageInfo.getFormatName() + ";" +
imageInfo.getMimeType() + ";" +
imageInfo.getWidth() + ";" +
imageInfo.getHeight() + ";" +
imageInfo.getBitsPerPixel() + ";" +
imageInfo.getNumberOfImages() + ";" +
imageInfo.getPhysicalWidthDpi() + ";" +
imageInfo.getPhysicalHeightDpi() + ";" +
imageInfo.getPhysicalWidthInch() + ";" +
imageInfo.getPhysicalHeightInch()
);
}
private static void printLine(int indentLevels, String text, float
value, float minValidValue)
{
if ( value < minValidValue )
{
return;
}
printLine(indentLevels, text, Float.toString(value));
}
private static void printLine(int indentLevels, String text, int
value, int minValidValue)
{
if ( value >= minValidValue )
{
printLine(indentLevels, text, Integer.toString(value));
}
}
private static void printLine(int indentLevels, String text, String
value)
{
if ( value == null || value.length() == 0 )
{
return;
}
while ( indentLevels-- > 0 )
{
System.out.print("\t");
}
if ( text != null && text.length() > 0 )
{
System.out.print(text);
System.out.print(" ");
}
System.out.println(value);
}
private static void printVerbose(String sourceName, ImageInfo ii)
{
printLine(0, null, sourceName);
printLine(1, "File format: ", ii.getFormatName());
printLine(1, "MIME type: ", ii.getMimeType());
printLine(1, "Width (pixels): ", ii.getWidth(), 1);
printLine(1, "Height (pixels): ", ii.getHeight(), 1);
printLine(1, "Bits per pixel: ", ii.getBitsPerPixel(), 1);
printLine(1, "Number of images: ", ii.getNumberOfImages(), 1);
printLine(1, "Physical width (dpi): ", ii.getPhysicalWidthDpi(),
1);
printLine(1, "Physical height (dpi): ",
ii.getPhysicalHeightDpi(), 1);
printLine(1, "Physical width (inches): ",
ii.getPhysicalWidthInch(), 1.0f);
printLine(1, "Physical height (inches): ",
ii.getPhysicalHeightInch(), 1.0f);
int numComments = ii.getNumberOfComments();
printLine(1, "Number of textual comments: ", numComments, 1);
if ( numComments > 0 )
{
for ( int i = 0; i < numComments; i++ )
{
printLine(2, null, ii.getComment(i));
}
}
}
private int read() throws IOException {
if ( in != null )
{
return in.read();
}
else
{
return din.readByte();
}
}
private int read(byte[] a) throws IOException {
if ( in != null )
{
return in.read(a);
}
else
{
din.readFully(a);
return a.length;
}
}
private int read(byte[] a, int offset, int num) throws IOException
{
if ( in != null )
{
return in.read(a, offset, num);
}
else
{
din.readFully(a, offset, num);
return num;
}
}
private String readLine() throws IOException {
return readLine(new StringBuilder());
}
private String readLine(StringBuilder sb) throws IOException {
boolean finished;
do
{
int value = read();
finished = (value == -1 || value == 10);
if ( !finished )
{
sb.append((char)value);
}
} while ( !finished );
return sb.toString();
}
/**
* Read an unsigned value from the given number of bits
*/
public long readUBits( int numBits ) throws IOException
{
if ( numBits == 0 )
{
return 0;
}
int bitsLeft = numBits;
long result = 0;
if ( bitPos == 0 )
{ //no value in the buffer - read a byte
if ( in != null )
{
bitBuf = in.read();
}
else
{
bitBuf = din.readByte();
}
bitPos = 8;
}
while ( true )
{
int shift = bitsLeft - bitPos;
if ( shift > 0 )
{
// Consume the entire buffer
result |= bitBuf << shift;
bitsLeft -= bitPos;
// Get the next byte from the input stream
if ( in != null )
{
bitBuf = in.read();
}
else
{
bitBuf = din.readByte();
}
bitPos = 8;
}
else
{
// Consume a portion of the buffer
result |= bitBuf >> -shift;
bitPos -= bitsLeft;
bitBuf &= 0xff >> (8 - bitPos); // mask off the consumed
bits
return result;
}
}
}
/**
* Read a signed value from the given number of bits
*/
private int readSBits( int numBits ) throws IOException
{
// Get the number as an unsigned value.
long uBits = readUBits( numBits );
// Is the number negative?
if ( ( uBits & (1L << (numBits - 1))) != 0 )
{
// Yes. Extend the sign.
uBits |= -1L << numBits;
}
return(int)uBits;
}
/**
* Reset the bit buffer
*/
public void synchBits()
{
bitBuf = 0;
bitPos = 0;
}
private String readLine(int firstChar) throws IOException {
StringBuilder result = new StringBuilder();
result.append((char)firstChar);
return readLine(result);
}
private static void run(String sourceName, InputStream in,
ImageInfo imageInfo, boolean verbose)
{
imageInfo.setInput(in);
imageInfo.setDetermineImageNumber(true);
imageInfo.setCollectComments(verbose);
if ( imageInfo.check() )
{
print(sourceName, imageInfo, verbose);
}
}
/**
* Specify whether textual comments are supposed to be extracted
from input.
* Default is <code>false</code>.
* If enabled, comments will be added to an internal list.
* @param newValue if <code>true</code>, this class will read
comments
* @see #getNumberOfComments
* @see #getComment
*/
public void setCollectComments(boolean newValue)
{
collectComments = newValue;
}
/**
* Specify whether the number of images in a file is to be
* determined - default is <code>false</code>.
* This is a special option because some file formats require
running over
* the entire file to find out the number of images, a rather
time-consuming
* task.
* Not all file formats support more than one image.
* If this method is called with <code>true</code> as argument,
* the actual number of images can be queried via
* {@link #getNumberOfImages()} after a successful call to
* {@link #check()}.
* @param newValue will the number of images be determined?
* @see #getNumberOfImages
*/
public void setDetermineImageNumber(boolean newValue)
{
determineNumberOfImages = newValue;
}
/**
* Set the input stream to the argument stream (or file).
* Note that {@link java.io.RandomAccessFile} implements
* {@link java.io.DataInput}.
* @param dataInput the input stream to read from
*/
public void setInput(DataInput dataInput)
{
din = dataInput;
in = null;
}
/**
* Set the input stream to the argument stream (or file).
* @param inputStream the input stream to read from
*/
public void setInput(InputStream inputStream)
{
in = inputStream;
din = null;
}
private void setPhysicalHeightDpi(int newValue)
{
physicalWidthDpi = newValue;
}
private void setPhysicalWidthDpi(int newValue)
{
physicalHeightDpi = newValue;
}
private void skip(int num) throws IOException {
if ( in != null )
{
in.skip(num);
}
else
{
din.skipBytes(num);
}
}
}
--
Bush crime family lost/embezzled $3 trillion from Pentagon.
Complicit Bush-friendly media keeps mum. Rumsfeld confesses on video.
http://www.infowars.com/articles/us/mckinney_grills_rumsfeld.htm
Canadian Mind Products, Roedy Green.
See http://mindprod.com/iraq.html photos of Bush's war crimes