Coverage Report - org.devlib.schmidt.imageinfo.ImageInfo
 
Classes in this File Line Coverage Branch Coverage Complexity
ImageInfo
0%
0/462
0%
0/370
0
 
 1  
 /*
 2  
  * ImageInfo.java
 3  
  *
 4  
  * Version 1.9
 5  
  *
 6  
  * A Java class to determine image width, height and color depth for
 7  
  * a number of image file formats.
 8  
  *
 9  
  * Written by Marco Schmidt 
 10  
  *
 11  
  * Contributed to the Public Domain.
 12  
  */
 13  
 package org.devlib.schmidt.imageinfo;
 14  
 
 15  
 import java.io.DataInput;
 16  
 import java.io.FileInputStream;
 17  
 import java.io.InputStream;
 18  
 import java.io.IOException;
 19  
 import java.net.URL;
 20  
 import java.util.Vector;
 21  
 
 22  
 /**
 23  
  * Get file format, image resolution, number of bits per pixel and optionally 
 24  
  * number of images, comments and physical resolution from 
 25  
  * JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM, PPM and PSD files 
 26  
  * (or input streams).
 27  
  * <p>
 28  
  * Use the class like this:
 29  
  * <pre>
 30  
  * ImageInfo ii = new ImageInfo();
 31  
  * ii.setInput(in); // in can be InputStream or RandomAccessFile
 32  
  * ii.setDetermineImageNumber(true); // default is false
 33  
  * ii.setCollectComments(true); // default is false
 34  
  * if (!ii.check()) {
 35  
  *   System.err.println("Not a supported image file format.");
 36  
  *   return;
 37  
  * }
 38  
  * System.out.println(ii.getFormatName() + ", " + ii.getMimeType() + 
 39  
  *   ", " + ii.getWidth() + " x " + ii.getHeight() + " pixels, " + 
 40  
  *   ii.getBitsPerPixel() + " bits per pixel, " + ii.getNumberOfImages() +
 41  
  *   " image(s), " + ii.getNumberOfComments() + " comment(s).");
 42  
  *  // there are other properties, check out the API documentation
 43  
  * </pre>
 44  
  * You can also use this class as a command line program.
 45  
  * Call it with a number of image file names and URLs as parameters:
 46  
  * <pre>
 47  
  *   java ImageInfo *.jpg *.png *.gif http://somesite.tld/image.jpg
 48  
  * </pre>
 49  
  * or call it without parameters and pipe data to it:
 50  
  * <pre>
 51  
  *   java ImageInfo &lt; image.jpg  
 52  
  * </pre>
 53  
  * <p>
 54  
  * Known limitations:
 55  
  * <ul>
 56  
  * <li>When the determination of the number of images is turned off, GIF bits 
 57  
  *  per pixel are only read from the global header.
 58  
  *  For some GIFs, local palettes change this to a typically larger
 59  
  *  value. To be certain to get the correct color depth, call
 60  
  *  setDetermineImageNumber(true) before calling check().
 61  
  *  The complete scan over the GIF file will take additional time.</li>
 62  
  * <li>Transparency information is not included in the bits per pixel count.
 63  
  *  Actually, it was my decision not to include those bits, so it's a feature! ;-)</li>
 64  
  * </ul>
 65  
  * <p>
 66  
  * Requirements:
 67  
  * <ul>
 68  
  * <li>Java 1.1 or higher</li>
 69  
  * </ul>
 70  
  * <p>
 71  
  * The latest version can be found at <a href="http://schmidt.devlib.org/image-info/">http://schmidt.devlib.org/image-info/</a>.
 72  
  * <p>
 73  
  * Written by Marco Schmidt.
 74  
  * <p>
 75  
  * This class is contributed to the Public Domain.
 76  
  * Use it at your own risk.
 77  
  * <p>
 78  
  * <a name="history">History</a>:
 79  
  * <ul>
 80  
  * <li><strong>2001-08-24</strong> Initial version.</li>
 81  
  * <li><strong>2001-10-13</strong> Added support for the file formats BMP and PCX.</li>
 82  
  * <li><strong>2001-10-16</strong> Fixed bug in read(int[], int, int) that returned
 83  
  * <li><strong>2002-01-22</strong> Added support for file formats Amiga IFF and Sun Raster (RAS).</li>
 84  
  * <li><strong>2002-01-24</strong> Added support for file formats Portable Bitmap / Graymap / Pixmap (PBM, PGM, PPM) and Adobe Photoshop (PSD).
 85  
  *   Added new method getMimeType() to return the MIME type associated with a particular file format.</li>
 86  
  * <li><strong>2002-03-15</strong> Added support to recognize number of images in file. Only works with GIF.
 87  
  *   Use {@link #setDetermineImageNumber} with <code>true</code> as argument to identify animated GIFs
 88  
  *   ({@link #getNumberOfImages()} will return a value larger than <code>1</code>).</li>
 89  
  * <li><strong>2002-04-10</strong> Fixed a bug in the feature 'determine number of images in animated GIF' introduced with version 1.1.
 90  
  *   Thanks to Marcelo P. Lima for sending in the bug report. 
 91  
  *   Released as 1.1.1.</li>
 92  
  * <li><strong>2002-04-18</strong> Added {@link #setCollectComments(boolean)}. 
 93  
  *  That new method lets the user specify whether textual comments are to be  
 94  
  *  stored in an internal list when encountered in an input image file / stream.
 95  
  *  Added two methods to return the physical width and height of the image in dpi: 
 96  
  *   {@link #getPhysicalWidthDpi()} and {@link #getPhysicalHeightDpi()}.
 97  
  *  If the physical resolution could not be retrieved, these methods return <code>-1</code>.
 98  
  *  </li>
 99  
  * <li><strong>2002-04-23</strong> Added support for the new properties physical resolution and
 100  
  *   comments for some formats. Released as 1.2.</li>
 101  
  * <li><strong>2002-06-17</strong> Added support for SWF, sent in by Michael Aird.
 102  
  *  Changed checkJpeg() so that other APP markers than APP0 will not lead to a failure anymore.
 103  
  *  Released as 1.3.</li>
 104  
  * <li><strong>2003-07-28</strong> Bug fix - skip method now takes return values into consideration.
 105  
  *  Less bytes than necessary may have been skipped, leading to flaws in the retrieved information in some cases.
 106  
  *  Thanks to Bernard Bernstein for pointing that out.
 107  
  *  Released as 1.4.</li>
 108  
  * <li><strong>2004-02-29</strong> Added support for recognizing progressive JPEG and
 109  
  *  interlaced PNG and GIF. A new method {@link #isProgressive()} returns whether ImageInfo
 110  
  *  has found that the storage type is progressive (or interlaced). 
 111  
  *  Thanks to Joe Germuska for suggesting the feature.
 112  
  *  Bug fix: BMP physical resolution is now correctly determined.
 113  
  *  Released as 1.5.</li>
 114  
  * <li><strong>2004-11-30</strong> Bug fix: recognizing progressive GIFs 
 115  
  * (interlaced in GIF terminology) did not work (thanks to Franz Jeitler for 
 116  
  *   pointing this out). Now it should work, but only if the number of images is determined.
 117  
  *  This is because information on interlacing is stored in a local image header.
 118  
  *  In theory, different images could be stored interlaced and non-interlaced in one 
 119  
  *  file. However, I think  that's unlikely. Right now, the last image in the GIF file 
 120  
  *  that is examined by ImageInfo is used for the "progressive" status.</li>
 121  
  * <li><strong>2005-01-02</strong> Some code clean up (unused methods and variables
 122  
  *  commented out, missing javadoc comments, etc.). Thanks to George Sexton for a long list.
 123  
  *  Removed usage of Boolean.toString because
 124  
  *  it's a Java 1.4+ feature (thanks to Gregor Dupont).
 125  
  *  Changed delimiter character in compact output from semicolon to tabulator
 126  
  * (for better integration with cut(1) and other Unix tools).
 127  
  *  Added some points to the <a href="http://schmidt.devlib.org/image-info/index.html#knownissues">'Known
 128  
  *  issues' section of the website</a>. 
 129  
  *  Released as 1.6.</li>
 130  
  * <li><strong>2005-07-26</strong> Removed code to identify Flash (SWF) files.
 131  
  *  Has repeatedly led to problems and support requests, and I don't know the
 132  
  *  format and don't have the time and interest to fix it myself.
 133  
  *  I repeatedly included fixes by others which didn't work for some people.
 134  
  *  I give up on SWF. Please do not contact me about it anymore.
 135  
  *  Set package of ImageInfo class to org.devlib.schmidt.imageinfo (a package
 136  
  *  was repeatedly requested by some users).
 137  
  *  Released as 1.7.</li>
 138  
  *  <li><strong>2006-02-23</strong> Removed Flash helper methods which weren't used elsewhere.
 139  
  *   Updated skip method which tries "read" whenever "skip(Bytes)" returns a result of 0.
 140  
  *   The old method didn't work with certain input stream types on truncated data streams.
 141  
  *   Thanks to Martin Leidig for reporting this and sending in code.
 142  
  *   Released as 1.8.</li>
 143  
  *  </li>
 144  
  *  <li><strong>2006-11-13</strong> Removed check that made ImageInfo report JPEG APPx
 145  
  *   markers smaller than 14 bytes as files in unknown format. Such JPEGs seem to be
 146  
  *   generated by Google's Picasa application. First reported with fix by 
 147  
  *   Karl von Randow. Released as 1.9.</li>  
 148  
  * </ul>
 149  
  * @author Marco Schmidt
 150  
  */
 151  
 @SuppressWarnings("unchecked")
 152  0
 public class ImageInfo {
 153  
         /**
 154  
          * Return value of {@link #getFormat()} for JPEG streams.
 155  
          * ImageInfo can extract physical resolution and comments
 156  
          * from JPEGs (only from APP0 headers).
 157  
          * Only one image can be stored in a file.
 158  
          * It is determined whether the JPEG stream is progressive 
 159  
          * (see {@link #isProgressive()}).
 160  
          */
 161  
         public static final int FORMAT_JPEG = 0;
 162  
 
 163  
         /**
 164  
          * Return value of {@link #getFormat()} for GIF streams.
 165  
          * ImageInfo can extract comments from GIFs and count the number
 166  
          * of images (GIFs with more than one image are animations).
 167  
          * It is determined whether the GIF stream is interlaced (see {@link #isProgressive()}).
 168  
          */
 169  
         public static final int FORMAT_GIF = 1;
 170  
 
 171  
         /**
 172  
          * Return value of {@link #getFormat()} for PNG streams.
 173  
          * PNG only supports one image per file.
 174  
          * Both physical resolution and comments can be stored with PNG,
 175  
          * but ImageInfo is currently not able to extract those.
 176  
          * It is determined whether the PNG stream is interlaced (see {@link #isProgressive()}).
 177  
          */
 178  
         public static final int FORMAT_PNG = 2;
 179  
 
 180  
         /**
 181  
          * Return value of {@link #getFormat()} for BMP streams.
 182  
          * BMP only supports one image per file.
 183  
          * BMP does not allow for comments.
 184  
          * The physical resolution can be stored.
 185  
          */
 186  
         public static final int FORMAT_BMP = 3;
 187  
 
 188  
         /**
 189  
          * Return value of {@link #getFormat()} for PCX streams.
 190  
          * PCX does not allow for comments or more than one image per file.
 191  
          * However, the physical resolution can be stored.
 192  
          */
 193  
         public static final int FORMAT_PCX = 4;
 194  
 
 195  
         /**
 196  
          * Return value of {@link #getFormat()} for IFF streams.
 197  
          */
 198  
         public static final int FORMAT_IFF = 5;
 199  
 
 200  
         /**
 201  
          * Return value of {@link #getFormat()} for RAS streams.
 202  
          * Sun Raster allows for one image per file only and is not able to
 203  
          * store physical resolution or comments.
 204  
          */
 205  
         public static final int FORMAT_RAS = 6;
 206  
 
 207  
         /** Return value of {@link #getFormat()} for PBM streams. */
 208  
         public static final int FORMAT_PBM = 7;
 209  
 
 210  
         /** Return value of {@link #getFormat()} for PGM streams. */
 211  
         public static final int FORMAT_PGM = 8;
 212  
 
 213  
         /** Return value of {@link #getFormat()} for PPM streams. */
 214  
         public static final int FORMAT_PPM = 9;
 215  
 
 216  
         /** Return value of {@link #getFormat()} for PSD streams. */
 217  
         public static final int FORMAT_PSD = 10;
 218  
 
 219  
 /*        public static final int COLOR_TYPE_UNKNOWN = -1;
 220  
         public static final int COLOR_TYPE_TRUECOLOR_RGB = 0;
 221  
         public static final int COLOR_TYPE_PALETTED = 1;
 222  
         public static final int COLOR_TYPE_GRAYSCALE= 2;
 223  
         public static final int COLOR_TYPE_BLACK_AND_WHITE = 3;*/
 224  
 
 225  
         /**
 226  
          * The names of all supported file formats.
 227  
          * The FORMAT_xyz int constants can be used as index values for
 228  
          * this array.
 229  
          */
 230  0
         private static final String[] FORMAT_NAMES =
 231  
                 {"JPEG", "GIF", "PNG", "BMP", "PCX", 
 232  
                  "IFF", "RAS", "PBM", "PGM", "PPM", 
 233  
                  "PSD"};
 234  
 
 235  
         /**
 236  
          * The names of the MIME types for all supported file formats.
 237  
          * The FORMAT_xyz int constants can be used as index values for
 238  
          * this array.
 239  
          */
 240  0
         private static final String[] MIME_TYPE_STRINGS =
 241  
                 {"image/jpeg", "image/gif", "image/png", "image/bmp", "image/pcx", 
 242  
                  "image/iff", "image/ras", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", 
 243  
                  "image/psd"};
 244  
 
 245  
         private int width;
 246  
         private int height;
 247  
         private int bitsPerPixel;
 248  
         //private int colorType = COLOR_TYPE_UNKNOWN;
 249  
         private boolean progressive;
 250  
         private int format;
 251  
         private InputStream in;
 252  
         private DataInput din;
 253  0
         private boolean collectComments = true;
 254  
         private Vector comments;
 255  
         private boolean determineNumberOfImages;
 256  
         private int numberOfImages;
 257  
         private int physicalHeightDpi;
 258  
         private int physicalWidthDpi;
 259  
 
 260  
         private void addComment(String s) {
 261  0
                 if (comments == null) {
 262  0
                         comments = new Vector();
 263  
                 }
 264  0
                 comments.addElement(s);
 265  0
         }
 266  
 
 267  
         /**
 268  
          * Call this method after you have provided an input stream or file
 269  
          * using {@link #setInput(InputStream)} or {@link #setInput(DataInput)}.
 270  
          * If true is returned, the file format was known and information
 271  
          * on the file's content can be retrieved using the various getXyz methods.
 272  
          * @return if information could be retrieved from input
 273  
          */
 274  
         public boolean check() {
 275  0
                 format = -1;
 276  0
                 width = -1;
 277  0
                 height = -1;
 278  0
                 bitsPerPixel = -1;
 279  0
                 numberOfImages = 1;
 280  0
                 physicalHeightDpi = -1;
 281  0
                 physicalWidthDpi = -1;
 282  0
                 comments = null;
 283  
                 try {
 284  0
                         int b1 = read() & 0xff;
 285  0
                         int b2 = read() & 0xff;
 286  0
                         if (b1 == 0x47 && b2 == 0x49) {
 287  0
                                 return checkGif();
 288  
                         }
 289  
                         else
 290  0
                         if (b1 == 0x89 && b2 == 0x50) {
 291  0
                                 return checkPng();
 292  
                         }
 293  
                         else
 294  0
                         if (b1 == 0xff && b2 == 0xd8) {
 295  0
                                 return checkJpeg();
 296  
                         }
 297  
                         else
 298  0
                         if (b1 == 0x42 && b2 == 0x4d) {
 299  0
                                 return checkBmp();
 300  
                         }
 301  
                         else
 302  0
                         if (b1 == 0x0a && b2 < 0x06) {
 303  0
                                 return checkPcx();
 304  
                         }
 305  
                         else
 306  0
                         if (b1 == 0x46 && b2 == 0x4f) {
 307  0
                                 return checkIff();
 308  
                         }
 309  
                         else
 310  0
                         if (b1 == 0x59 && b2 == 0xa6) {
 311  0
                                 return checkRas();
 312  
                         }
 313  
                         else
 314  0
                         if (b1 == 0x50 && b2 >= 0x31 && b2 <= 0x36) {
 315  0
                                 return checkPnm(b2 - '0');
 316  
                         }
 317  
                         else
 318  0
                         if (b1 == 0x38 && b2 == 0x42) {
 319  0
                                 return checkPsd();
 320  
                         }
 321  
                         else {
 322  0
                                 return false;
 323  
                         }
 324  0
                 } catch (IOException ioe) {
 325  0
                         return false;
 326  
                 }
 327  
         }
 328  
 
 329  
         private boolean checkBmp() throws IOException {
 330  0
                 byte[] a = new byte[44];
 331  0
                 if (read(a) != a.length) {
 332  0
                         return false;
 333  
                 }
 334  0
                 width = getIntLittleEndian(a, 16);
 335  0
                 height = getIntLittleEndian(a, 20);
 336  0
                 if (width < 1 || height < 1) {
 337  0
                         return false;
 338  
                 }
 339  0
                 bitsPerPixel = getShortLittleEndian(a, 26);
 340  0
                 if (bitsPerPixel != 1 && bitsPerPixel != 4 &&
 341  
                     bitsPerPixel != 8 && bitsPerPixel != 16 &&
 342  
                     bitsPerPixel != 24 && bitsPerPixel != 32) {
 343  0
                     return false;
 344  
                 }
 345  0
                 int x = (int)(getIntLittleEndian(a, 36) * 0.0254);
 346  0
                 if (x > 0) {
 347  0
                         setPhysicalWidthDpi(x);
 348  
                 }
 349  0
                 int y = (int)(getIntLittleEndian(a, 40) * 0.0254);
 350  0
                 if (y > 0) {
 351  0
                         setPhysicalHeightDpi(y);
 352  
                 }
 353  0
                 format = FORMAT_BMP;
 354  0
                 return true;
 355  
         }
 356  
 
 357  
         private boolean checkGif() throws IOException {
 358  0
                 final byte[] GIF_MAGIC_87A = {0x46, 0x38, 0x37, 0x61};
 359  0
                 final byte[] GIF_MAGIC_89A = {0x46, 0x38, 0x39, 0x61};
 360  0
                 byte[] a = new byte[11]; // 4 from the GIF signature + 7 from the global header
 361  0
                 if (read(a) != 11) {
 362  0
                         return false;
 363  
                 }
 364  0
                 if ((!equals(a, 0, GIF_MAGIC_89A, 0, 4)) &&
 365  
                         (!equals(a, 0, GIF_MAGIC_87A, 0, 4))) {
 366  0
                         return false;
 367  
                 }
 368  0
                 format = FORMAT_GIF;
 369  0
                 width = getShortLittleEndian(a, 4);
 370  0
                 height = getShortLittleEndian(a, 6);
 371  0
                 int flags = a[8] & 0xff;
 372  0
                 bitsPerPixel = ((flags >> 4) & 0x07) + 1;
 373  
                 //progressive = (flags & 0x02) != 0;
 374  0
                 if (!determineNumberOfImages) {
 375  0
                         return true;
 376  
                 }
 377  
                 // skip global color palette
 378  0
                 if ((flags & 0x80) != 0) {
 379  0
                         int tableSize = (1 << ((flags & 7) + 1)) * 3;
 380  0
                         skip(tableSize);
 381  
                 }
 382  0
                 numberOfImages = 0;
 383  
                 int blockType;
 384  
                 do
 385  
                 {
 386  0
                         blockType = read();
 387  0
                         switch(blockType)
 388  
                         {
 389  
                                 case(0x2c): // image separator
 390  
                                 {
 391  0
                                         if (read(a, 0, 9) != 9) {
 392  0
                                                 return false;
 393  
                                         }
 394  0
                                         flags = a[8] & 0xff;
 395  0
                                         progressive = (flags & 0x40) != 0;
 396  
                                         /*int locWidth = getShortLittleEndian(a, 4);
 397  
                                         int locHeight = getShortLittleEndian(a, 6);
 398  
                                         System.out.println("LOCAL: " + locWidth + " x " + locHeight);*/
 399  0
                                         int localBitsPerPixel = (flags & 0x07) + 1;
 400  0
                                         if (localBitsPerPixel > bitsPerPixel) {
 401  0
                                                 bitsPerPixel = localBitsPerPixel;
 402  
                                         }
 403  0
                                         if ((flags & 0x80) != 0) {
 404  0
                                                 skip((1 << localBitsPerPixel) * 3);
 405  
                                         }
 406  0
                                         skip(1); // initial code length
 407  
                                         int n;
 408  
                                         do
 409  
                                         {
 410  0
                                                 n = read();
 411  0
                                                 if (n > 0) {
 412  0
                                                         skip(n);
 413  
                                                 }
 414  
                                                 else
 415  0
                                                 if (n == -1) {
 416  0
                                                         return false;
 417  
                                                 }
 418  
                                         }
 419  0
                                         while (n > 0);
 420  0
                                         numberOfImages++;
 421  0
                                         break;
 422  
                                 }
 423  
                                 case(0x21): // extension
 424  
                                 {
 425  0
                                         int extensionType = read();
 426  0
                                         if (collectComments && extensionType == 0xfe) {
 427  0
                                                 StringBuffer sb = new StringBuffer();
 428  
                                                 int n;
 429  
                                                 do
 430  
                                                 {
 431  0
                                                         n = read();
 432  0
                                                         if (n == -1) {
 433  0
                                                                 return false;
 434  
                                                         }
 435  0
                                                         if (n > 0) {
 436  0
                                                                 for (int i = 0; i < n; i++) {
 437  0
                                                                         int ch = read();
 438  0
                                                                         if (ch == -1) {
 439  0
                                                                                 return false;
 440  
                                                                         }
 441  0
                                                                         sb.append((char)ch);
 442  
                                                                 }
 443  
                                                         }
 444  
                                                 }
 445  0
                                                 while (n > 0);
 446  0
                                         } else {
 447  
                                                 int n;
 448  
                                                 do
 449  
                                                 {
 450  0
                                                         n = read();
 451  0
                                                         if (n > 0) {
 452  0
                                                                 skip(n);
 453  
                                                         }
 454  
                                                         else
 455  0
                                                         if (n == -1) {
 456  0
                                                                 return false;
 457  
                                                         }
 458  
                                                 }
 459  0
                                                 while (n > 0);
 460  
                                         }
 461  0
                                         break;
 462  
                                 }
 463  
                                 case(0x3b): // end of file
 464  
                                 {
 465  0
                                         break;
 466  
                                 }
 467  
                                 default:
 468  
                                 {
 469  0
                                         return false;
 470  
                                 }
 471  
                         }
 472  
                 }
 473  0
                 while (blockType != 0x3b);
 474  0
                 return true;
 475  
         }
 476  
 
 477  
         private boolean checkIff() throws IOException {
 478  0
                 byte[] a = new byte[10];
 479  
                 // read remaining 2 bytes of file id, 4 bytes file size 
 480  
                 // and 4 bytes IFF subformat
 481  0
                 if (read(a, 0, 10) != 10) {
 482  0
                         return false;
 483  
                 }
 484  0
                 final byte[] IFF_RM = {0x52, 0x4d};
 485  0
                 if (!equals(a, 0, IFF_RM, 0, 2)) {
 486  0
                         return false;
 487  
                 }
 488  0
                 int type = getIntBigEndian(a, 6);
 489  0
                 if (type != 0x494c424d && // type must be ILBM...
 490  
                     type != 0x50424d20) { // ...or PBM
 491  0
                     return false;
 492  
                 }
 493  
                 // loop chunks to find BMHD chunk
 494  
                 do {
 495  0
                         if (read(a, 0, 8) != 8) {
 496  0
                                 return false;
 497  
                         }
 498  0
                         int chunkId = getIntBigEndian(a, 0);
 499  0
                         int size = getIntBigEndian(a, 4);
 500  0
                         if ((size & 1) == 1) {
 501  0
                                 size++;
 502  
                         }
 503  0
                         if (chunkId == 0x424d4844) { // BMHD chunk
 504  0
                                 if (read(a, 0, 9) != 9) {
 505  0
                                         return false;
 506  
                                 }
 507  0
                                 format = FORMAT_IFF;
 508  0
                                 width = getShortBigEndian(a, 0);
 509  0
                                 height = getShortBigEndian(a, 2);
 510  0
                                 bitsPerPixel = a[8] & 0xff;
 511  0
                                 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel < 33);
 512  
                         } else {
 513  0
                                 skip(size);
 514  
                         }
 515  0
                 } while (true);
 516  
         }
 517  
 
 518  
         private boolean checkJpeg() throws IOException {
 519  0
                 byte[] data = new byte[12];
 520  
                 while (true) {
 521  0
                         if (read(data, 0, 4) != 4) {
 522  0
                                 return false;
 523  
                         }
 524  0
                         int marker = getShortBigEndian(data, 0);
 525  0
                         int size = getShortBigEndian(data, 2);
 526  0
                         if ((marker & 0xff00) != 0xff00) {
 527  0
                                 return false; // not a valid marker
 528  
                         }
 529  0
                         if (marker == 0xffe0) { // APPx 
 530  0
                                 if (size < 14) {
 531  
                                         // not an APPx header as we know it, skip
 532  0
                                         skip(size - 2);
 533  0
                                         continue;
 534  
                                 }
 535  0
                                 if (read(data, 0, 12) != 12) {
 536  0
                                         return false;
 537  
                                 }
 538  0
                                 final byte[] APP0_ID = {0x4a, 0x46, 0x49, 0x46, 0x00};
 539  0
                                 if (equals(APP0_ID, 0, data, 0, 5)) {
 540  
                                         //System.out.println("data 7=" + data[7]);
 541  0
                                         if (data[7] == 1) {
 542  0
                                                 setPhysicalWidthDpi(getShortBigEndian(data, 8));
 543  0
                                                 setPhysicalHeightDpi(getShortBigEndian(data, 10));
 544  
                                         }
 545  
                                         else
 546  0
                                         if (data[7] == 2) {
 547  0
                                                 int x = getShortBigEndian(data, 8);
 548  0
                                                 int y = getShortBigEndian(data, 10);
 549  0
                                                 setPhysicalWidthDpi((int)(x * 2.54f));
 550  0
                                                 setPhysicalHeightDpi((int)(y * 2.54f));
 551  
                                         }
 552  
                                 }
 553  0
                                 skip(size - 14);
 554  0
                         }
 555  
                         else
 556  0
                         if (collectComments && size > 2 && marker == 0xfffe) { // comment
 557  0
                                 size -= 2;
 558  0
                                 byte[] chars = new byte[size];
 559  0
                                 if (read(chars, 0, size) != size) {
 560  0
                                         return false;
 561  
                                 }
 562  0
                                 String comment = new String(chars, "iso-8859-1");
 563  0
                                 comment = comment.trim();
 564  0
                                 addComment(comment);
 565  0
                         }
 566  
                         else
 567  0
                         if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) {
 568  0
                                 if (read(data, 0, 6) != 6) {
 569  0
                                         return false;
 570  
                                 }
 571  0
                                 format = FORMAT_JPEG;
 572  0
                                 bitsPerPixel = (data[0] & 0xff) * (data[5] & 0xff);
 573  0
                                 progressive = marker == 0xffc2 || marker == 0xffc6 ||
 574  
                                         marker == 0xffca || marker == 0xffce;
 575  0
                                 width = getShortBigEndian(data, 3);
 576  0
                                 height = getShortBigEndian(data, 1);
 577  0
                                 return true;
 578  
                         } else {
 579  0
                                 skip(size - 2);
 580  
                         }
 581  0
                 }
 582  
         }
 583  
 
 584  
         private boolean checkPcx() throws IOException {
 585  0
                 byte[] a = new byte[64];
 586  0
                 if (read(a) != a.length) {
 587  0
                         return false;
 588  
                 }
 589  0
                 if (a[0] != 1) { // encoding, 1=RLE is only valid value
 590  0
                         return false;
 591  
                 }
 592  
                 // width / height
 593  0
                 int x1 = getShortLittleEndian(a, 2);
 594  0
                 int y1 = getShortLittleEndian(a, 4);
 595  0
                 int x2 = getShortLittleEndian(a, 6);
 596  0
                 int y2 = getShortLittleEndian(a, 8);
 597  0
                 if (x1 < 0 || x2 < x1 || y1 < 0 || y2 < y1) {
 598  0
                         return false;
 599  
                 }
 600  0
                 width = x2 - x1 + 1;
 601  0
                 height = y2 - y1 + 1;
 602  
                 // color depth
 603  0
                 int bits = a[1];
 604  0
                 int planes = a[63];
 605  0
                 if (planes == 1 &&
 606  
                     (bits == 1 || bits == 2 || bits == 4 || bits == 8)) {
 607  
                         // paletted
 608  0
                         bitsPerPixel = bits;
 609  
                 } else
 610  0
                 if (planes == 3 && bits == 8) {
 611  
                         // RGB truecolor
 612  0
                         bitsPerPixel = 24;
 613  
                 } else {
 614  0
                         return false;
 615  
                 }
 616  0
                 setPhysicalWidthDpi(getShortLittleEndian(a, 10));
 617  0
                 setPhysicalHeightDpi(getShortLittleEndian(a, 10));
 618  0
                 format = FORMAT_PCX;
 619  0
                 return true;
 620  
         }
 621  
 
 622  
         private boolean checkPng() throws IOException {
 623  0
                 final byte[] PNG_MAGIC = {0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
 624  0
                 byte[] a = new byte[27];
 625  0
                 if (read(a) != 27) {
 626  0
                         return false;
 627  
                 }
 628  0
                 if (!equals(a, 0, PNG_MAGIC, 0, 6)) {
 629  0
                         return false;
 630  
                 }
 631  0
                 format = FORMAT_PNG;
 632  0
                 width = getIntBigEndian(a, 14);
 633  0
                 height = getIntBigEndian(a, 18);
 634  0
                 bitsPerPixel = a[22] & 0xff;
 635  0
                 int colorType = a[23] & 0xff;
 636  0
                 if (colorType == 2 || colorType == 6) {
 637  0
                         bitsPerPixel *= 3;
 638  
                 }
 639  0
                 progressive = (a[26] & 0xff) != 0;
 640  0
                 return true;
 641  
         }
 642  
 
 643  
         private boolean checkPnm(int id) throws IOException {
 644  0
                 if (id < 1 || id > 6) {
 645  0
                         return false;
 646  
                 }
 647  0
                 final int[] PNM_FORMATS = {FORMAT_PBM, FORMAT_PGM, FORMAT_PPM};
 648  0
                 format = PNM_FORMATS[(id - 1) % 3];
 649  0
                 boolean hasPixelResolution = false;
 650  
                 String s;
 651  
                 while (true)
 652  
                 {
 653  0
                         s = readLine();
 654  0
                         if (s != null) {
 655  0
                                 s = s.trim();
 656  
                         }
 657  0
                         if (s == null || s.length() < 1) {
 658  0
                                 continue;
 659  
                         }
 660  0
                         if (s.charAt(0) == '#') { // comment
 661  0
                                 if (collectComments && s.length() > 1) {
 662  0
                                         addComment(s.substring(1));
 663  
                                 }
 664  
                                 continue;
 665  
                         }
 666  0
                         if (!hasPixelResolution) { // split "343 966" into width=343, height=966
 667  0
                                 int spaceIndex = s.indexOf(' ');
 668  0
                                 if (spaceIndex == -1) {
 669  0
                                         return false;
 670  
                                 }
 671  0
                                 String widthString = s.substring(0, spaceIndex);
 672  0
                                 spaceIndex = s.lastIndexOf(' ');
 673  0
                                 if (spaceIndex == -1) {
 674  0
                                         return false;
 675  
                                 }
 676  0
                                 String heightString = s.substring(spaceIndex + 1);
 677  
                                 try {
 678  0
                                         width = Integer.parseInt(widthString);
 679  0
                                         height = Integer.parseInt(heightString);
 680  0
                                 } catch (NumberFormatException nfe) {
 681  0
                                         return false;
 682  0
                                 }
 683  0
                                 if (width < 1 || height < 1) {
 684  0
                                         return false;
 685  
                                 }
 686  0
                                 if (format == FORMAT_PBM) {
 687  0
                                         bitsPerPixel = 1;
 688  0
                                         return true;
 689  
                                 }
 690  0
                                 hasPixelResolution = true;
 691  0
                         }
 692  
                         else
 693  
                         {
 694  
                                 int maxSample;
 695  
                                 try {
 696  0
                                         maxSample = Integer.parseInt(s);
 697  0
                                 } catch (NumberFormatException nfe) {
 698  0
                                         return false;
 699  0
                                 }
 700  0
                                 if (maxSample < 0) {
 701  0
                                         return false;
 702  
                                 }
 703  0
                                 for (int i = 0; i < 25; i++) {
 704  0
                                         if (maxSample < (1 << (i + 1))) {
 705  0
                                                 bitsPerPixel = i + 1;
 706  0
                                                 if (format == FORMAT_PPM) {
 707  0
                                                         bitsPerPixel *= 3;
 708  
                                                 }
 709  0
                                                 return true;
 710  
                                         }
 711  
                                 }
 712  0
                                 return false;
 713  
                         }
 714  
                 }
 715  
         }
 716  
 
 717  
         private boolean checkPsd() throws IOException {
 718  0
                 byte[] a = new byte[24];
 719  0
                 if (read(a) != a.length) {
 720  0
                         return false;
 721  
                 }
 722  0
                 final byte[] PSD_MAGIC = {0x50, 0x53};
 723  0
                 if (!equals(a, 0, PSD_MAGIC, 0, 2)) {
 724  0
                         return false;
 725  
                 }
 726  0
                 format = FORMAT_PSD;
 727  0
                 width = getIntBigEndian(a, 16);
 728  0
                 height = getIntBigEndian(a, 12);
 729  0
                 int channels = getShortBigEndian(a, 10);
 730  0
                 int depth = getShortBigEndian(a, 20);
 731  0
                 bitsPerPixel = channels * depth;
 732  0
                 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel <= 64);
 733  
         }
 734  
 
 735  
         private boolean checkRas() throws IOException {
 736  0
                 byte[] a = new byte[14];
 737  0
                 if (read(a) != a.length) {
 738  0
                         return false;
 739  
                 }
 740  0
                 final byte[] RAS_MAGIC = {0x6a, (byte)0x95};
 741  0
                 if (!equals(a, 0, RAS_MAGIC, 0, 2)) {
 742  0
                         return false;
 743  
                 }
 744  0
                 format = FORMAT_RAS;
 745  0
                 width = getIntBigEndian(a, 2);
 746  0
                 height = getIntBigEndian(a, 6);
 747  0
                 bitsPerPixel = getIntBigEndian(a, 10);
 748  0
                 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel <= 24);
 749  
         }
 750  
 
 751  
         /**
 752  
          * Run over String list, return false iff at least one of the arguments
 753  
          * equals <code>-c</code>.
 754  
          * @param args string list to check
 755  
          */
 756  
         private static boolean determineVerbosity(String[] args) {
 757  0
                 if (args != null && args.length > 0) {
 758  0
                         for (int i = 0; i < args.length; i++) {
 759  0
                                 if ("-c".equals(args[i])) {
 760  0
                                         return false;
 761  
                                 }
 762  
                         }
 763  
                 }
 764  0
                 return true;
 765  
         }
 766  
 
 767  
         private static boolean equals(byte[] a1, int offs1, byte[] a2, int offs2, int num) {
 768  0
                 while (num-- > 0) {
 769  0
                         if (a1[offs1++] != a2[offs2++]) {
 770  0
                                 return false;
 771  
                         }
 772  
                 }
 773  0
                 return true;
 774  
         }
 775  
 
 776  
         /** 
 777  
          * If {@link #check()} was successful, returns the image's number of bits per pixel.
 778  
          * Does not include transparency information like the alpha channel.
 779  
          * @return number of bits per image pixel
 780  
          */
 781  
         public int getBitsPerPixel() {
 782  0
                 return bitsPerPixel;
 783  
         }
 784  
 
 785  
         /**
 786  
          * Returns the index'th comment retrieved from the file.
 787  
          * @param index int index of comment to return
 788  
          * @throws IllegalArgumentException if index is smaller than 0 or larger than or equal
 789  
          * to the number of comments retrieved
 790  
          * @see #getNumberOfComments
 791  
          */
 792  
         public String getComment(int index) {
 793  0
                 if (comments == null || index < 0 || index >= comments.size()) {
 794  0
                         throw new IllegalArgumentException("Not a valid comment index: " + index);
 795  
                 }
 796  0
                 return (String)comments.elementAt(index);
 797  
         }
 798  
 
 799  
         /**
 800  
          * If {@link #check()} was successful, returns the image format as one
 801  
          * of the FORMAT_xyz constants from this class.
 802  
          * Use {@link #getFormatName()} to get a textual description of the file format.
 803  
          * @return file format as a FORMAT_xyz constant
 804  
          */
 805  
         public int getFormat() {
 806  0
                 return format;
 807  
         }
 808  
 
 809  
         /**
 810  
          * If {@link #check()} was successful, returns the image format's name.
 811  
          * Use {@link #getFormat()} to get a unique number.
 812  
          * @return file format name
 813  
          */
 814  
         public String getFormatName() {
 815  0
                 if (format >= 0 && format < FORMAT_NAMES.length) {
 816  0
                         return FORMAT_NAMES[format];
 817  
                 } else {
 818  0
                         return "?";
 819  
                 }
 820  
         }
 821  
 
 822  
         /** 
 823  
          * If {@link #check()} was successful, returns one the image's vertical
 824  
          * resolution in pixels.
 825  
          * @return image height in pixels
 826  
          */
 827  
         public int getHeight() {
 828  0
                 return height;
 829  
         }
 830  
 
 831  
         private static int getIntBigEndian(byte[] a, int offs) {
 832  0
                 return
 833  
                         (a[offs] & 0xff) << 24 | 
 834  
                         (a[offs + 1] & 0xff) << 16 | 
 835  
                         (a[offs + 2] & 0xff) << 8 | 
 836  
                         a[offs + 3] & 0xff;
 837  
         }
 838  
 
 839  
         private static int getIntLittleEndian(byte[] a, int offs) {
 840  0
                 return
 841  
                         (a[offs + 3] & 0xff) << 24 | 
 842  
                         (a[offs + 2] & 0xff) << 16 | 
 843  
                         (a[offs + 1] & 0xff) << 8 | 
 844  
                         a[offs] & 0xff;
 845  
         }
 846  
 
 847  
         /** 
 848  
          * If {@link #check()} was successful, returns a String with the
 849  
          * MIME type of the format.
 850  
          * @return MIME type, e.g. <code>image/jpeg</code>
 851  
          */
 852  
         public String getMimeType() {
 853  0
                 if (format >= 0 && format < MIME_TYPE_STRINGS.length) {
 854  0
                         if (format == FORMAT_JPEG && progressive)
 855  
                         {
 856  0
                                 return "image/pjpeg";
 857  
                         }
 858  0
                         return MIME_TYPE_STRINGS[format];
 859  
                 } else {
 860  0
                         return null;
 861  
                 }
 862  
         }
 863  
 
 864  
         /**
 865  
          * If {@link #check()} was successful and {@link #setCollectComments(boolean)} was called with
 866  
          * <code>true</code> as argument, returns the number of comments retrieved 
 867  
          * from the input image stream / file.
 868  
          * Any number &gt;= 0 and smaller than this number of comments is then a
 869  
          * valid argument for the {@link #getComment(int)} method.
 870  
          * @return number of comments retrieved from input image
 871  
          */
 872  
         public int getNumberOfComments()
 873  
         {
 874  0
                 if (comments == null) {
 875  0
                         return 0;
 876  
                 } else {
 877  0
                         return comments.size();
 878  
                 }
 879  
         }
 880  
 
 881  
         /**
 882  
          * Returns the number of images in the examined file.
 883  
          * Assumes that <code>setDetermineImageNumber(true);</code> was called before
 884  
          * a successful call to {@link #check()}.
 885  
          * This value can currently be only different from <code>1</code> for GIF images.
 886  
          * @return number of images in file
 887  
          */
 888  
         public int getNumberOfImages()
 889  
         {
 890  0
                 return numberOfImages;
 891  
         }
 892  
 
 893  
         /**
 894  
          * Returns the physical height of this image in dots per inch (dpi).
 895  
          * Assumes that {@link #check()} was successful.
 896  
          * Returns <code>-1</code> on failure.
 897  
          * @return physical height (in dpi)
 898  
          * @see #getPhysicalWidthDpi()
 899  
          * @see #getPhysicalHeightInch()
 900  
          */
 901  
         public int getPhysicalHeightDpi() {
 902  0
                 return physicalHeightDpi;
 903  
         }
 904  
 
 905  
         /**
 906  
          * If {@link #check()} was successful, returns the physical width of this image in dpi (dots per inch)
 907  
          * or -1 if no value could be found.
 908  
          * @return physical height (in dpi)
 909  
          * @see #getPhysicalHeightDpi()
 910  
          * @see #getPhysicalWidthDpi()
 911  
          * @see #getPhysicalWidthInch()
 912  
          */
 913  
         public float getPhysicalHeightInch() {
 914  0
                 int h = getHeight();
 915  0
                 int ph = getPhysicalHeightDpi();
 916  0
                 if (h > 0 && ph > 0) {
 917  0
                         return ((float)h) / ((float)ph);
 918  
                 } else {
 919  0
                         return -1.0f;
 920  
                 }
 921  
         }
 922  
 
 923  
         /**
 924  
          * If {@link #check()} was successful, returns the physical width of this image in dpi (dots per inch)
 925  
          * or -1 if no value could be found.
 926  
          * @return physical width (in dpi)
 927  
          * @see #getPhysicalHeightDpi()
 928  
          * @see #getPhysicalWidthInch()
 929  
          * @see #getPhysicalHeightInch()
 930  
          */
 931  
         public int getPhysicalWidthDpi() {
 932  0
                 return physicalWidthDpi;
 933  
         }
 934  
 
 935  
         /**
 936  
          * Returns the physical width of an image in inches, or
 937  
          * <code>-1.0f</code> if width information is not available.
 938  
          * Assumes that {@link #check} has been called successfully.
 939  
          * @return physical width in inches or <code>-1.0f</code> on failure
 940  
          * @see #getPhysicalWidthDpi
 941  
          * @see #getPhysicalHeightInch
 942  
          */
 943  
         public float getPhysicalWidthInch() {
 944  0
                 int w = getWidth();
 945  0
                 int pw = getPhysicalWidthDpi();
 946  0
                 if (w > 0 && pw > 0) {
 947  0
                         return ((float)w) / ((float)pw);
 948  
                 } else {
 949  0
                         return -1.0f;
 950  
                 }
 951  
         }
 952  
 
 953  
         private static int getShortBigEndian(byte[] a, int offs) {
 954  0
                 return
 955  
                         (a[offs] & 0xff) << 8 | 
 956  
                         (a[offs + 1] & 0xff);
 957  
         }
 958  
 
 959  
         private static int getShortLittleEndian(byte[] a, int offs) {
 960  0
                 return (a[offs] & 0xff) | (a[offs + 1] & 0xff) << 8;
 961  
         }
 962  
 
 963  
         /** 
 964  
          * If {@link #check()} was successful, returns one the image's horizontal
 965  
          * resolution in pixels.
 966  
          * @return image width in pixels
 967  
          */
 968  
         public int getWidth() {
 969  0
                 return width;
 970  
         }
 971  
 
 972  
         /**
 973  
          * Returns whether the image is stored in a progressive (also called: interlaced) way.
 974  
          * @return true for progressive/interlaced, false otherwise
 975  
          */
 976  
         public boolean isProgressive()
 977  
         {
 978  0
                 return progressive;
 979  
         }
 980  
 
 981  
         /**
 982  
          * To use this class as a command line application, give it either 
 983  
          * some file names as parameters (information on them will be
 984  
          * printed to standard output, one line per file) or call
 985  
          * it with no parameters. It will then check data given to it
 986  
          * via standard input.
 987  
          * @param args the program arguments which must be file names
 988  
          */
 989  
         public static void main(String[] args) {
 990  0
                 ImageInfo imageInfo = new ImageInfo();
 991  0
                 imageInfo.setDetermineImageNumber(true);
 992  0
                 boolean verbose = determineVerbosity(args);
 993  0
                 if (args.length == 0) {
 994  0
                         run(null, System.in, imageInfo, verbose);
 995  
                 } else {
 996  0
                         int index = 0;
 997  0
                         while (index < args.length) {
 998  0
                                 InputStream in = null;
 999  
                                 try {
 1000  0
                                         String name = args[index++];
 1001  0
                                         System.out.print(name + ";");
 1002  0
                                         if (name.startsWith("http://")) {
 1003  0
                                                 in = new URL(name).openConnection().getInputStream();
 1004  
                                         } else {
 1005  0
                                                 in = new FileInputStream(name);
 1006  
                                         }
 1007  0
                                         run(name, in, imageInfo, verbose);
 1008  0
                                         in.close();
 1009  0
                                 } catch (IOException e) {
 1010  0
                                         System.out.println(e);
 1011  
                                         try {
 1012  0
                                                 if (in != null) {
 1013  0
                                                         in.close();
 1014  
                                                 }
 1015  0
                                         } catch (IOException ee) {
 1016  0
                                         }
 1017  0
                                 }
 1018  0
                         }
 1019  
                 }
 1020  0
         }
 1021  
 
 1022  
         private static void print(String sourceName, ImageInfo ii, boolean verbose) {
 1023  0
                 if (verbose) {
 1024  0
                         printVerbose(sourceName, ii);
 1025  
                 } else {
 1026  0
                         printCompact(sourceName, ii);
 1027  
                 }
 1028  0
         }
 1029  
 
 1030  
         private static void printCompact(String sourceName, ImageInfo imageInfo) {
 1031  0
                 final String SEP = "\t";
 1032  0
                 System.out.println(
 1033  
                         sourceName + SEP + 
 1034  
                         imageInfo.getFormatName() + SEP +
 1035  
                         imageInfo.getMimeType() + SEP +
 1036  
                         imageInfo.getWidth() + SEP +
 1037  
                         imageInfo.getHeight() + SEP +
 1038  
                         imageInfo.getBitsPerPixel() + SEP +
 1039  
                         imageInfo.getNumberOfImages() + SEP +
 1040  
                         imageInfo.getPhysicalWidthDpi() + SEP +
 1041  
                         imageInfo.getPhysicalHeightDpi() + SEP +
 1042  
                         imageInfo.getPhysicalWidthInch() + SEP +
 1043  
                         imageInfo.getPhysicalHeightInch() + SEP +
 1044  
                         imageInfo.isProgressive()
 1045  
                 );
 1046  0
         }
 1047  
 
 1048  
         private static void printLine(int indentLevels, String text, float value, float minValidValue) {
 1049  0
                 if (value < minValidValue) {
 1050  0
                         return;
 1051  
                 }
 1052  0
                 printLine(indentLevels, text, Float.toString(value));
 1053  0
         }
 1054  
 
 1055  
         private static void printLine(int indentLevels, String text, int value, int minValidValue) {
 1056  0
                 if (value >= minValidValue) {
 1057  0
                         printLine(indentLevels, text, Integer.toString(value));
 1058  
                 }
 1059  0
         }
 1060  
 
 1061  
         private static void printLine(int indentLevels, String text, String value) {
 1062  0
                 if (value == null || value.length() == 0) {
 1063  0
                         return;
 1064  
                 }
 1065  0
                 while (indentLevels-- > 0) {
 1066  0
                         System.out.print("\t");
 1067  
                 }
 1068  0
                 if (text != null && text.length() > 0) {
 1069  0
                         System.out.print(text);
 1070  0
                         System.out.print(" ");
 1071  
                 }
 1072  0
                 System.out.println(value);
 1073  0
         }
 1074  
 
 1075  
         private static void printVerbose(String sourceName, ImageInfo ii) {
 1076  0
                 printLine(0, null, sourceName);
 1077  0
                 printLine(1, "File format: ", ii.getFormatName());
 1078  0
                 printLine(1, "MIME type: ", ii.getMimeType());
 1079  0
                 printLine(1, "Width (pixels): ", ii.getWidth(), 1);
 1080  0
                 printLine(1, "Height (pixels): ", ii.getHeight(), 1);
 1081  0
                 printLine(1, "Bits per pixel: ", ii.getBitsPerPixel(), 1);
 1082  0
                 printLine(1, "Progressive: ", ii.isProgressive() ? "yes" : "no");
 1083  0
                 printLine(1, "Number of images: ", ii.getNumberOfImages(), 1);
 1084  0
                 printLine(1, "Physical width (dpi): ", ii.getPhysicalWidthDpi(), 1);
 1085  0
                 printLine(1, "Physical height (dpi): ", ii.getPhysicalHeightDpi(), 1);
 1086  0
                 printLine(1, "Physical width (inches): ", ii.getPhysicalWidthInch(), 1.0f);
 1087  0
                 printLine(1, "Physical height (inches): ", ii.getPhysicalHeightInch(), 1.0f);
 1088  0
                 int numComments = ii.getNumberOfComments();
 1089  0
                 printLine(1, "Number of textual comments: ", numComments, 1);
 1090  0
                 if (numComments > 0) {
 1091  0
                         for (int i = 0; i < numComments; i++) {
 1092  0
                                 printLine(2, null, ii.getComment(i));
 1093  
                         }
 1094  
                 }
 1095  0
         }
 1096  
 
 1097  
         private int read() throws IOException {
 1098  0
                 if (in != null) {
 1099  0
                         return in.read();
 1100  
                 } else {
 1101  0
                         return din.readByte();
 1102  
                 }
 1103  
         }
 1104  
 
 1105  
         private int read(byte[] a) throws IOException {
 1106  0
                 if (in != null) {
 1107  0
                         return in.read(a);
 1108  
                 } else {
 1109  0
                         din.readFully(a);
 1110  0
                         return a.length;
 1111  
                 }
 1112  
         }
 1113  
 
 1114  
         private int read(byte[] a, int offset, int num) throws IOException {
 1115  0
                 if (in != null) {
 1116  0
                         return in.read(a, offset, num);
 1117  
                 } else {
 1118  0
                         din.readFully(a, offset, num);
 1119  0
                         return num;
 1120  
                 }
 1121  
         }
 1122  
 
 1123  
         private String readLine() throws IOException {
 1124  0
                 return readLine(new StringBuffer());
 1125  
         }
 1126  
 
 1127  
         private String readLine(StringBuffer sb) throws IOException {
 1128  
                 boolean finished;
 1129  
                 do {
 1130  0
                         int value = read();
 1131  0
                         finished = (value == -1 || value == 10);
 1132  0
                         if (!finished) {
 1133  0
                                 sb.append((char)value);
 1134  
                         }
 1135  0
                 } while (!finished);
 1136  0
                 return sb.toString();
 1137  
         }
 1138  
 
 1139  
         private static void run(String sourceName, InputStream in, ImageInfo imageInfo, boolean verbose) {
 1140  0
                 imageInfo.setInput(in);
 1141  0
                 imageInfo.setDetermineImageNumber(true);
 1142  0
                 imageInfo.setCollectComments(verbose);
 1143  0
                 if (imageInfo.check()) {
 1144  0
                         print(sourceName, imageInfo, verbose);
 1145  
                 }
 1146  0
         }
 1147  
 
 1148  
         /**
 1149  
          * Specify whether textual comments are supposed to be extracted from input.
 1150  
          * Default is <code>false</code>.
 1151  
          * If enabled, comments will be added to an internal list.
 1152  
          * @param newValue if <code>true</code>, this class will read comments
 1153  
          * @see #getNumberOfComments
 1154  
          * @see #getComment
 1155  
          */
 1156  
         public void setCollectComments(boolean newValue)
 1157  
         {
 1158  0
                 collectComments = newValue;
 1159  0
         }
 1160  
 
 1161  
         /**
 1162  
          * Specify whether the number of images in a file is to be
 1163  
          * determined - default is <code>false</code>.
 1164  
          * This is a special option because some file formats require running over
 1165  
          * the entire file to find out the number of images, a rather time-consuming
 1166  
          * task.
 1167  
          * Not all file formats support more than one image.
 1168  
          * If this method is called with <code>true</code> as argument,
 1169  
          * the actual number of images can be queried via 
 1170  
          * {@link #getNumberOfImages()} after a successful call to
 1171  
          * {@link #check()}.
 1172  
          * @param newValue will the number of images be determined?
 1173  
          * @see #getNumberOfImages
 1174  
          */
 1175  
         public void setDetermineImageNumber(boolean newValue)
 1176  
         {
 1177  0
                 determineNumberOfImages = newValue;
 1178  0
         }
 1179  
 
 1180  
         /**
 1181  
          * Set the input stream to the argument stream (or file). 
 1182  
          * Note that {@link java.io.RandomAccessFile} implements
 1183  
          * {@link java.io.DataInput}.
 1184  
          * @param dataInput the input stream to read from
 1185  
          */
 1186  
         public void setInput(DataInput dataInput) {
 1187  0
                 din = dataInput;
 1188  0
                 in = null;
 1189  0
         }
 1190  
 
 1191  
         /**
 1192  
          * Set the input stream to the argument stream (or file).
 1193  
          * @param inputStream the input stream to read from
 1194  
          */
 1195  
         public void setInput(InputStream inputStream) {
 1196  0
                 in = inputStream;
 1197  0
                 din = null;
 1198  0
         }
 1199  
 
 1200  
         private void setPhysicalHeightDpi(int newValue) {
 1201  0
                 physicalWidthDpi = newValue;
 1202  0
         }
 1203  
 
 1204  
         private void setPhysicalWidthDpi(int newValue) {
 1205  0
                 physicalHeightDpi = newValue;
 1206  0
         }
 1207  
 
 1208  
     private void skip(int num) throws IOException {
 1209  0
         while (num > 0) {
 1210  
             long result;
 1211  0
             if (in != null) {
 1212  0
                 result = in.skip(num);
 1213  
             } else {
 1214  0
                 result = din.skipBytes(num);
 1215  
             }
 1216  0
             if (result > 0) {
 1217  0
                 num -= result;
 1218  
             } else {
 1219  0
                 if (in != null) {
 1220  0
                     result = in.read();
 1221  
                 } else {
 1222  0
                     result = din.readByte();
 1223  
                 }
 1224  0
                 if (result == -1) {
 1225  0
                         throw new IOException("Premature end of input.");
 1226  
                 } else {
 1227  0
                         num--;
 1228  
                 }
 1229  
             }
 1230  0
         }
 1231  0
     }
 1232  
 }