| 1 16 17 package imageinfo; 18 import java.io.DataInput ; 19 import java.io.FileInputStream ; 20 import java.io.InputStream ; 21 import java.io.IOException ; 22 import java.net.*; 23 import java.util.Vector ; 24 25 114 public class ImageInfo { 115 121 public static final int FORMAT_JPEG = 0; 122 123 131 public static final int FORMAT_GIF = 1; 132 133 139 public static final int FORMAT_PNG = 2; 140 141 152 public static final int FORMAT_BMP = 3; 153 154 159 public static final int FORMAT_PCX = 4; 160 161 164 public static final int FORMAT_IFF = 5; 165 166 171 public static final int FORMAT_RAS = 6; 172 173 174 public static final int FORMAT_PBM = 7; 175 176 177 public static final int FORMAT_PGM = 8; 178 179 180 public static final int FORMAT_PPM = 9; 181 182 183 public static final int FORMAT_PSD = 10; 184 185 186 public static final int FORMAT_SWF = 11; 187 188 public static final int COLOR_TYPE_UNKNOWN = -1; 189 public static final int COLOR_TYPE_TRUECOLOR_RGB = 0; 190 public static final int COLOR_TYPE_PALETTED = 1; 191 public static final int COLOR_TYPE_GRAYSCALE= 2; 192 public static final int COLOR_TYPE_BLACK_AND_WHITE = 3; 193 194 199 private static final String [] FORMAT_NAMES = 200 {"JPEG", "GIF", "PNG", "BMP", "PCX", 201 "IFF", "RAS", "PBM", "PGM", "PPM", 202 "PSD", "SWF"}; 203 204 209 private static final String [] MIME_TYPE_STRINGS = 210 {"image/jpeg", "image/gif", "image/png", "image/bmp", "image/pcx", 211 "image/iff", "image/ras", "image/x-portable-bitmap", "image/x-portable-graymap", "image/x-portable-pixmap", 212 "image/psd", "application/x-shockwave-flash"}; 213 214 private int width; 215 private int height; 216 private int bitsPerPixel; 217 private int colorType = COLOR_TYPE_UNKNOWN; 218 private int format; 219 private InputStream in; 220 private DataInput din; 221 private boolean collectComments = true; 222 private Vector comments; 223 private boolean determineNumberOfImages; 224 private int numberOfImages; 225 private int physicalHeightDpi; 226 private int physicalWidthDpi; 227 private int bitBuf; 228 private int bitPos; 229 230 private void addComment(String s) { 231 if (comments == null) { 232 comments = new Vector (); 233 } 234 comments.addElement(s); 235 } 236 237 244 public boolean check() { 245 format = -1; 246 width = -1; 247 height = -1; 248 bitsPerPixel = -1; 249 numberOfImages = 1; 250 physicalHeightDpi = -1; 251 physicalWidthDpi = -1; 252 comments = null; 253 try { 254 int b1 = read() & 0xff; 255 int b2 = read() & 0xff; 256 if (b1 == 0x47 && b2 == 0x49) { 257 return checkGif(); 258 } 259 else 260 if (b1 == 0x89 && b2 == 0x50) { 261 return checkPng(); 262 } 263 else 264 if (b1 == 0xff && b2 == 0xd8) { 265 return checkJpeg(); 266 } 267 else 268 if (b1 == 0x42 && b2 == 0x4d) { 269 return checkBmp(); 270 } 271 else 272 if (b1 == 0x0a && b2 < 0x06) { 273 return checkPcx(); 274 } 275 else 276 if (b1 == 0x46 && b2 == 0x4f) { 277 return checkIff(); 278 } 279 else 280 if (b1 == 0x59 && b2 == 0xa6) { 281 return checkRas(); 282 } 283 else 284 if (b1 == 0x50 && b2 >= 0x31 && b2 <= 0x36) { 285 return checkPnm(b2 - '0'); 286 } 287 else 288 if (b1 == 0x38 && b2 == 0x42) { 289 return checkPsd(); 290 } 291 else 292 if (b1 == 0x46 && b2 == 0x57) { 293 return checkSwf(); 294 } 295 else { 296 return false; 297 } 298 } catch (IOException ioe) { 299 return false; 300 } 301 } 302 303 private boolean checkBmp() throws IOException { 304 byte[] a = new byte[44]; 305 if (read(a) != a.length) { 306 return false; 307 } 308 width = getIntLittleEndian(a, 16); 309 height = getIntLittleEndian(a, 20); 310 if (width < 1 || height < 1) { 311 return false; 312 } 313 bitsPerPixel = getShortLittleEndian(a, 26); 314 if (bitsPerPixel != 1 && bitsPerPixel != 4 && 315 bitsPerPixel != 8 && bitsPerPixel != 16 && 316 bitsPerPixel != 24 && bitsPerPixel != 32) { 317 return false; 318 } 319 int x = getIntLittleEndian(a, 36); 320 if (x > 0) { 321 setPhysicalWidthDpi(x); 322 } 323 int y = getIntLittleEndian(a, 40); 324 if (y > 0) { 325 setPhysicalHeightDpi(y); 326 } 327 format = FORMAT_BMP; 328 return true; 329 } 330 331 private boolean checkGif() throws IOException { 332 final byte[] GIF_MAGIC_87A = {0x46, 0x38, 0x37, 0x61}; 333 final byte[] GIF_MAGIC_89A = {0x46, 0x38, 0x39, 0x61}; 334 byte[] a = new byte[11]; if (read(a) != 11) { 336 return false; 337 } 338 if ((!equals(a, 0, GIF_MAGIC_89A, 0, 4)) && 339 (!equals(a, 0, GIF_MAGIC_87A, 0, 4))) { 340 return false; 341 } 342 format = FORMAT_GIF; 343 width = getShortLittleEndian(a, 4); 344 height = getShortLittleEndian(a, 6); 345 int flags = a[8] & 0xff; 346 bitsPerPixel = ((flags >> 4) & 0x07) + 1; 347 if (!determineNumberOfImages) { 348 return true; 349 } 350 if ((flags & 0x80) != 0) { 352 int tableSize = (1 << ((flags & 7) + 1)) * 3; 353 skip(tableSize); 354 } 355 numberOfImages = 0; 356 int blockType; 357 do 358 { 359 blockType = read(); 360 switch(blockType) 361 { 362 case(0x2c): { 364 if (read(a, 0, 9) != 9) { 365 return false; 366 } 367 flags = a[8] & 0xff; 368 int localBitsPerPixel = (flags & 0x07) + 1; 369 if (localBitsPerPixel > bitsPerPixel) { 370 bitsPerPixel = localBitsPerPixel; 371 } 372 if ((flags & 0x80) != 0) { 373 skip((1 << localBitsPerPixel) * 3); 374 } 375 skip(1); int n; 377 do 378 { 379 n = read(); 380 if (n > 0) { 381 skip(n); 382 } 383 else 384 if (n == -1) { 385 return false; 386 } 387 } 388 while (n > 0); 389 numberOfImages++; 390 break; 391 } 392 case(0x21): { 394 int extensionType = read(); 395 if (collectComments && extensionType == 0xfe) { 396 StringBuffer sb = new StringBuffer (); 397 int n; 398 do 399 { 400 n = read(); 401 if (n == -1) { 402 return false; 403 } 404 if (n > 0) { 405 for (int i = 0; i < n; i++) { 406 int ch = read(); 407 if (ch == -1) { 408 return false; 409 } 410 sb.append((char)ch); 411 } 412 } 413 } 414 while (n > 0); 415 } else { 416 int n; 417 do 418 { 419 n = read(); 420 if (n > 0) { 421 skip(n); 422 } 423 else 424 if (n == -1) { 425 return false; 426 } 427 } 428 while (n > 0); 429 } 430 break; 431 } 432 case(0x3b): { 434 break; 435 } 436 default: 437 { 438 return false; 439 } 440 } 441 } 442 while (blockType != 0x3b); 443 return true; 444 } 445 446 private boolean checkIff() throws IOException { 447 byte[] a = new byte[10]; 448 if (read(a, 0, 10) != 10) { 451 return false; 452 } 453 final byte[] IFF_RM = {0x52, 0x4d}; 454 if (!equals(a, 0, IFF_RM, 0, 2)) { 455 return false; 456 } 457 int type = getIntBigEndian(a, 6); 458 if (type != 0x494c424d && type != 0x50424d20) { return false; 461 } 462 do { 464 if (read(a, 0, 8) != 8) { 465 return false; 466 } 467 int chunkId = getIntBigEndian(a, 0); 468 int size = getIntBigEndian(a, 4); 469 if ((size & 1) == 1) { 470 size++; 471 } 472 if (chunkId == 0x424d4844) { if (read(a, 0, 9) != 9) { 474 return false; 475 } 476 format = FORMAT_IFF; 477 width = getShortBigEndian(a, 0); 478 height = getShortBigEndian(a, 2); 479 bitsPerPixel = a[8] & 0xff; 480 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel < 33); 481 } else { 482 skip(size); 483 } 484 } while (true); 485 } 486 487 private boolean checkJpeg() throws IOException { 488 byte[] data = new byte[12]; 489 while (true) { 490 if (read(data, 0, 4) != 4) { 491 return false; 492 } 493 int marker = getShortBigEndian(data, 0); 494 int size = getShortBigEndian(data, 2); 495 if ((marker & 0xff00) != 0xff00) { 496 return false; } 498 if (marker == 0xffe0) { if (size < 14) { 500 return false; } 502 if (read(data, 0, 12) != 12) { 503 return false; 504 } 505 final byte[] APP0_ID = {0x4a, 0x46, 0x49, 0x46, 0x00}; 506 if (equals(APP0_ID, 0, data, 0, 5)) { 507 if (data[7] == 1) { 509 setPhysicalWidthDpi(getShortBigEndian(data, 8)); 510 setPhysicalHeightDpi(getShortBigEndian(data, 10)); 511 } 512 else 513 if (data[7] == 2) { 514 int x = getShortBigEndian(data, 8); 515 int y = getShortBigEndian(data, 10); 516 setPhysicalWidthDpi((int)(x * 2.54f)); 517 setPhysicalHeightDpi((int)(y * 2.54f)); 518 } 519 } 520 skip(size - 14); 521 } 522 else 523 if (collectComments && size > 2 && marker == 0xfffe) { size -= 2; 525 byte[] chars = new byte[size]; 526 if (read(chars, 0, size) != size) { 527 return false; 528 } 529 String comment = new String (chars, "iso-8859-1"); 530 comment = comment.trim(); 531 addComment(comment); 533 } 534 else 535 if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) { 536 if (read(data, 0, 6) != 6) { 537 return false; 538 } 539 format = FORMAT_JPEG; 540 bitsPerPixel = (data[0] & 0xff) * (data[5] & 0xff); 541 width = getShortBigEndian(data, 3); 542 height = getShortBigEndian(data, 1); 543 return true; 544 } else { 545 skip(size - 2); 546 } 547 } 548 } 549 550 private boolean checkPcx() throws IOException { 551 byte[] a = new byte[64]; 552 if (read(a) != a.length) { 553 return false; 554 } 555 if (a[0] != 1) { return false; 557 } 558 int x1 = getShortLittleEndian(a, 2); 560 int y1 = getShortLittleEndian(a, 4); 561 int x2 = getShortLittleEndian(a, 6); 562 int y2 = getShortLittleEndian(a, 8); 563 if (x1 < 0 || x2 < x1 || y1 < 0 || y2 < y1) { 564 return false; 565 } 566 width = x2 - x1 + 1; 567 height = y2 - y1 + 1; 568 int bits = a[1]; 570 int planes = a[63]; 571 if (planes == 1 && 572 (bits == 1 || bits == 2 || bits == 4 || bits == 8)) { 573 bitsPerPixel = bits; 575 } else 576 if (planes == 3 && bits == 8) { 577 bitsPerPixel = 24; 579 } else { 580 return false; 581 } 582 setPhysicalWidthDpi(getShortLittleEndian(a, 10)); 583 setPhysicalHeightDpi(getShortLittleEndian(a, 10)); 584 format = FORMAT_PCX; 585 return true; 586 } 587 588 private boolean checkPng() throws IOException { 589 final byte[] PNG_MAGIC = {0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}; 590 byte[] a = new byte[24]; 591 if (read(a) != 24) { 592 return false; 593 } 594 if (!equals(a, 0, PNG_MAGIC, 0, 6)) { 595 return false; 596 } 597 format = FORMAT_PNG; 598 width = getIntBigEndian(a, 14); 599 height = getIntBigEndian(a, 18); 600 bitsPerPixel = a[22] & 0xff; 601 int colorType = a[23] & 0xff; 602 if (colorType == 2 || colorType == 6) { 603 bitsPerPixel *= 3; 604 } 605 return true; 606 } 607 608 private boolean checkPnm(int id) throws IOException { 609 if (id < 1 || id > 6) { 610 return false; 611 } 612 final int[] PNM_FORMATS = {FORMAT_PBM, FORMAT_PGM, FORMAT_PPM}; 613 format = PNM_FORMATS[(id - 1) % 3]; 614 boolean hasPixelResolution = false; 615 String s; 616 while (true) 617 { 618 s = readLine(); 619 if (s != null) { 620 s = s.trim(); 621 } 622 if (s == null || s.length() < 1) { 623 continue; 624 } 625 if (s.charAt(0) == '#') { if (collectComments && s.length() > 1) { 627 addComment(s.substring(1)); 628 } 629 continue; 630 } 631 if (!hasPixelResolution) { int spaceIndex = s.indexOf(' '); 633 if (spaceIndex == -1) { 634 return false; 635 } 636 String widthString = s.substring(0, spaceIndex); 637 spaceIndex = s.lastIndexOf(' '); 638 if (spaceIndex == -1) { 639 return false; 640 } 641 String heightString = s.substring(spaceIndex + 1); 642 try { 643 width = Integer.parseInt(widthString); 644 height = Integer.parseInt(heightString); 645 } catch (NumberFormatException nfe) { 646 return false; 647 } 648 if (width < 1 || height < 1) { 649 return false; 650 } 651 if (format == FORMAT_PBM) { 652 bitsPerPixel = 1; 653 return true; 654 } 655 hasPixelResolution = true; 656 } 657 else 658 { 659 int maxSample; 660 try { 661 maxSample = Integer.parseInt(s); 662 } catch (NumberFormatException nfe) { 663 return false; 664 } 665 if (maxSample < 0) { 666 return false; 667 } 668 for (int i = 0; i < 25; i++) { 669 if (maxSample < (1 << (i + 1))) { 670 bitsPerPixel = i + 1; 671 if (format == FORMAT_PPM) { 672 bitsPerPixel *= 3; 673 } 674 return true; 675 } 676 } 677 return false; 678 } 679 } 680 } 681 682 private boolean checkPsd() throws IOException { 683 byte[] a = new byte[24]; 684 if (read(a) != a.length) { 685 return false; 686 } 687 final byte[] PSD_MAGIC = {0x50, 0x53}; 688 if (!equals(a, 0, PSD_MAGIC, 0, 2)) { 689 return false; 690 } 691 format = FORMAT_PSD; 692 width = getIntBigEndian(a, 16); 693 height = getIntBigEndian(a, 12); 694 int channels = getShortBigEndian(a, 10); 695 int depth = getShortBigEndian(a, 20); 696 bitsPerPixel = channels * depth; 697 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel <= 64); 698 } 699 700 private boolean checkRas() throws IOException { 701 byte[] a = new byte[14]; 702 if (read(a) != a.length) { 703 return false; 704 } 705 final byte[] RAS_MAGIC = {0x6a, (byte)0x95}; 706 if (!equals(a, 0, RAS_MAGIC, 0, 2)) { 707 return false; 708 } 709 format = FORMAT_RAS; 710 width = getIntBigEndian(a, 2); 711 height = getIntBigEndian(a, 6); 712 bitsPerPixel = getIntBigEndian(a, 10); 713 return (width > 0 && height > 0 && bitsPerPixel > 0 && bitsPerPixel <= 24); 714 } 715 716 private boolean checkSwf() throws IOException { 718 byte[] a = new byte[6]; 720 if (read(a) != a.length) { 721 return false; 722 } 723 format = FORMAT_SWF; 724 int bitSize = (int)readUBits( 5 ); 725 int minX = (int)readSBits( bitSize ); 726 int maxX = (int)readSBits( bitSize ); 727 int minY = (int)readSBits( bitSize ); 728 int maxY = (int)readSBits( bitSize ); 729 width = maxX/20; height = maxY/20; setPhysicalWidthDpi(72); 732 setPhysicalHeightDpi(72); 733 return (width > 0 && height > 0); 734 } 735 736 740 private static boolean determineVerbosity(String [] args) { 741 if (args != null && args.length > 0) { 742 for (int i = 0; i < args.length; i++) { 743 if ("-c".equals(args[i])) { 744 return false; 745 } 746 } 747 } 748 return true; 749 } 750 751 private boolean equals(byte[] a1, int offs1, byte[] a2, int offs2, int num) { 752 while (num-- > 0) { 753 if (a1[offs1++] != a2[offs2++]) { 754 return false; 755 &
|