This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.
This library 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 59
Temple Place, Suite 330, Boston, MA 02111-1307 USA
Users who would rather have a commercial license, warranty or support should
contact the following company who invented, built and supports the technology:
public boolean equals(Object o) { return o instanceof ImageInfo && toString().equals(o.toString());
}
public int hashCode() { return toString().hashCode();
}
public String toString() { if (stringValue == null) stringValue = "ImageInfo{name:" + name + ",format:" + format + ",width:" + width + ",height:" + height + "}"; return stringValue;
}
}
/* XXX Note: This class is a public domain class that is used by
* ImageInfo (above) to determine the width and height of an image.
* It is made package-private because it is not intended to be used
* directly.
*/
/*
* ImageInfo.java
*
* Version 1.5
*
* A Java class to determine image width, height and color depth for
* a number of image file formats.
*
* Written by Marco Schmidt
* <http://www.geocities.com/marcoschmidt.geo/contact.html>.
*
* Contributed to the Public Domain.
*
* Last modification 2004-02-29
*/
/**
* 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).");
* // there are other properties, check out the API documentation
* </pre>
* You can also use this class as a command line program.
* Call it with a number of image file names and URLs as parameters:
* <pre>
* java ImageInfo *.jpg *.png *.gif http://somesite.tld/image.jpg
* </pre>
* or call it without parameters and pipe data to it:
* <pre>
* java ImageInfo < image.jpg
* </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="http://www.geocities.com/marcoschmidt.geo/contact.html">Marco Schmidt</a>.
* <p>
* This class is contributed to the Public Domain.
* Use it at your own risk.
* <p>
* Last modification 2004-02-29.
* <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>
* <li><strong>2003-07-28</strong> Bug fix - skip method now takes return values into consideration.
* Less bytes than necessary may have been skipped, leading to flaws in the retrieved information in some cases.
* Thanks to Bernard Bernstein for pointing that out.
* Released as 1.4.</li>
* <li><strong>2004-02-29</strong> Added support for recognizing progressive JPEG and
* interlaced PNG and GIF. A new method {@link #isProgressive()} returns whether ImageInfo
* has found that the storage type is progressive (or interlaced).
* Thanks to Joe Germuska for suggesting the feature.
* Bug fix: BMP physical resolution is now correctly determined.
* Released as 1.5.</li>
* </ul>
*/ final class ImageParser {
/**
* 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.
* It is determined whether the JPEG stream is progressive
* (see {@link #isProgressive()}).
*/ 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>!
* It is determined whether the GIF stream is interlaced (see {@link #isProgressive()}).
*/ 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.
* It is determined whether the PNG stream is interlaced (see {@link #isProgressive()}).
*/ 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.
*/ 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;
public static final int COLOR_TYPE_UNKNOWN = -1; public static final int COLOR_TYPE_TRUECOLOR_RGB = 0; public static final int COLOR_TYPE_PALETTED = 1; public static final int COLOR_TYPE_GRAYSCALE= 2; public static final int COLOR_TYPE_BLACK_AND_WHITE = 3;
/**
* 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 boolean progressive; private int format; private InputStream in; private DataInput din; private boolean collectComments = true; private List<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 information
* on the file's 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 = (int)(getIntLittleEndian(a, 36) * 0.0254); if (x > 0) {
setPhysicalWidthDpi(x);
} int y = (int)(getIntLittleEndian(a, 40) * 0.0254); 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;
progressive = (flags & 0x02) != 0; 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 >= 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();
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);
progressive = marker == 0xffc2 || marker == 0xffc6 ||
marker == 0xffca || marker == 0xffce;
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;
}
// 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 maxX = (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);
}
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 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;
}
/**
* 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) { if (format == FORMAT_JPEG && progressive)
{ return "image/pjpeg";
} 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;
}
/**
* Returns whether the image is stored in a progressive (also called: interlaced) way.
* @return true for progressive/interlaced, false otherwise
*/ public boolean isProgressive()
{ return progressive;
}
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(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();
}
private 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;
}
/**
* 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;
}