1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.imaging; 18 19 import java.awt.Dimension; 20 import java.awt.color.ICC_Profile; 21 import java.awt.image.BufferedImage; 22 import java.io.BufferedOutputStream; 23 import java.io.ByteArrayOutputStream; 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.util.Arrays; 30 import java.util.List; 31 import java.util.Locale; 32 import java.util.Objects; 33 import java.util.stream.Stream; 34 35 import org.apache.commons.imaging.bytesource.ByteSource; 36 import org.apache.commons.imaging.common.ImageMetadata; 37 import org.apache.commons.imaging.common.XmpEmbeddable; 38 import org.apache.commons.imaging.icc.IccProfileInfo; 39 import org.apache.commons.imaging.icc.IccProfileParser; 40 import org.apache.commons.imaging.internal.ImageParserFactory; 41 42 /** 43 * The primary application programming interface (API) to the Imaging library. 44 * 45 * <h2>Application Notes</h2> 46 * 47 * <h3>Using this class</h3> 48 * 49 * <p> 50 * Almost all of the Apache Commons Imaging library's core functionality can be accessed through the methods provided by this class. The use of the Imaging 51 * class is similar to the Java API's ImageIO class, though Imaging supports formats not included in the standard Java API. 52 * </p> 53 * 54 * <p> 55 * All of methods provided by the Imaging class are declared static. 56 * </p> 57 * 58 * <p> 59 * The Apache Commons Imaging package is a pure Java implementation. 60 * </p> 61 * 62 * <h3>Format support</h3> 63 * 64 * <p> 65 * While the Apache Commons Imaging package handles a number of different graphics formats, support for some formats is not yet complete. For the most recent 66 * information on support for specific formats, refer to <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a> at the main project 67 * development web site. 68 * </p> 69 * 70 * <h3>Optional parameters for image reading and writing</h3> 71 * 72 * <p> 73 * Many of the operations provided in this class as static calls can be accessed directly using format-specific {@link AbstractImageParser} instances. These 74 * static methods are provided for convenience in simple use cases. 75 * </p> 76 * 77 * <h3>Example code</h3> 78 * 79 * <p> 80 * See the source of the SampleUsage class and other classes in the org.apache.commons.imaging.examples package for examples. 81 * </p> 82 * 83 * @see <a href="https://svn.apache.org/repos/asf/commons/proper/imaging/trunk/src/test/java/org/apache/commons/imaging/examples/SampleUsage.java"> 84 * org.apache.commons.imaging.examples.SampleUsage</a> 85 * @see <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a> 86 */ 87 public final class Imaging { 88 89 private static final int[] MAGIC_NUMBERS_GIF = { 0x47, 0x49, }; 90 private static final int[] MAGIC_NUMBERS_PNG = { 0x89, 0x50, }; 91 private static final int[] MAGIC_NUMBERS_JPEG = { 0xff, 0xd8, }; 92 private static final int[] MAGIC_NUMBERS_BMP = { 0x42, 0x4d, }; 93 private static final int[] MAGIC_NUMBERS_TIFF_MOTOROLA = { 0x4D, 0x4D, }; 94 private static final int[] MAGIC_NUMBERS_TIFF_INTEL = { 0x49, 0x49, }; 95 private static final int[] MAGIC_NUMBERS_PAM = { 0x50, 0x37, }; 96 private static final int[] MAGIC_NUMBERS_PSD = { 0x38, 0x42, }; 97 private static final int[] MAGIC_NUMBERS_PBM_A = { 0x50, 0x31, }; 98 private static final int[] MAGIC_NUMBERS_PBM_B = { 0x50, 0x34, }; 99 private static final int[] MAGIC_NUMBERS_PGM_A = { 0x50, 0x32, }; 100 private static final int[] MAGIC_NUMBERS_PGM_B = { 0x50, 0x35, }; 101 private static final int[] MAGIC_NUMBERS_PPM_A = { 0x50, 0x33, }; 102 private static final int[] MAGIC_NUMBERS_PPM_B = { 0x50, 0x36, }; 103 private static final int[] MAGIC_NUMBERS_JBIG2_1 = { 0x97, 0x4A, }; 104 private static final int[] MAGIC_NUMBERS_JBIG2_2 = { 0x42, 0x32, }; 105 private static final int[] MAGIC_NUMBERS_ICNS = { 0x69, 0x63, }; 106 private static final int[] MAGIC_NUMBERS_DCX = { 0xB1, 0x68, }; 107 private static final int[] MAGIC_NUMBERS_RGBE = { 0x23, 0x3F, }; 108 private static final int[] MAGIC_NUMBERS_RIFF_1 = { 0x52, 0x49, }; 109 private static final int[] MAGIC_NUMBERS_RIFF_2 = { 0x46, 0x46, }; 110 private static final byte[] MAGIC_NUMBERS_WEBP = { 0x57, 0x45, 0x42, 0x50, }; 111 112 private static boolean compareBytePair(final int[] a, final int[] b) { 113 if (a.length != 2 && b.length != 2) { 114 throw new IllegalArgumentException("Invalid Byte Pair."); 115 } 116 return a[0] == b[0] && a[1] == b[1]; 117 } 118 119 /** 120 * Write the ImageInfo and format-specific information for the image content of the specified byte array to a string. 121 * 122 * @param bytes A valid array of bytes. 123 * @return A valid string. 124 * @throws ImagingException In the event that the specified content does not conform to the format of the specific parser implementation. 125 * @throws IOException In the event of unsuccessful read or access operation. 126 */ 127 public static String dumpImageFile(final byte[] bytes) throws ImagingException, IOException { 128 return dumpImageFile(ByteSource.array(bytes)); 129 } 130 131 private static String dumpImageFile(final ByteSource byteSource) throws ImagingException, IOException { 132 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 133 return imageParser.dumpImageFile(byteSource); 134 } 135 136 /** 137 * Write the ImageInfo and format-specific information for the image content of the specified file to a string. 138 * 139 * @param file A valid file reference. 140 * @return A valid string. 141 * @throws ImagingException In the event that the specified content does not conform to the format of the specific parser implementation. 142 * @throws IOException In the event of unsuccessful read or access operation. 143 */ 144 public static String dumpImageFile(final File file) throws ImagingException, IOException { 145 return dumpImageFile(ByteSource.file(file)); 146 } 147 148 /** 149 * Gets all images specified by the byte array (some formats may include multiple images within a single data source). 150 * 151 * @param bytes a valid array of bytes 152 * @return A valid (potentially empty) list of BufferedImage objects. 153 * @throws ImagingException In the event that the specified content does not conform to the format of the specific parser implementation. 154 * @throws IOException In the event of unsuccessful read or access operation. 155 */ 156 public static List<BufferedImage> getAllBufferedImages(final byte[] bytes) throws ImagingException, IOException { 157 return getAllBufferedImages(ByteSource.array(bytes)); 158 } 159 160 private static List<BufferedImage> getAllBufferedImages(final ByteSource byteSource) throws ImagingException, IOException { 161 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 162 return imageParser.getAllBufferedImages(byteSource); 163 } 164 165 /** 166 * Gets all images specified by the file (some formats may include multiple images within a single data source). 167 * 168 * @param file A reference to a valid data file. 169 * @return A valid (potentially empty) list of BufferedImage objects. 170 * @throws ImagingException In the event that the specified content does not conform to the format of the specific parser implementation. 171 * @throws IOException In the event of unsuccessful read or access operation. 172 */ 173 public static List<BufferedImage> getAllBufferedImages(final File file) throws ImagingException, IOException { 174 return getAllBufferedImages(ByteSource.file(file)); 175 } 176 177 /** 178 * Gets all images specified by the InputStream (some formats may include multiple images within a single data source). 179 * 180 * @param is A valid InputStream 181 * @param fileName File name associated with image data (optional). 182 * @return A valid (potentially empty) list of BufferedImage objects. 183 * @throws ImagingException In the event that the specified content does not conform to the format of the specific parser implementation. 184 * @throws IOException In the event of unsuccessful read or access operation. 185 */ 186 public static List<BufferedImage> getAllBufferedImages(final InputStream is, final String fileName) throws ImagingException, IOException { 187 return getAllBufferedImages(ByteSource.inputStream(is, fileName)); 188 } 189 190 /** 191 * Reads the first image from a byte array. 192 * 193 * <p> 194 * For the most recent information on support for specific formats, refer to <a href="https://commons.apache.org/imaging/formatsupport.html">Format 195 * Support</a> at the main project development web site. While the Apache Commons Imaging package does not fully support all formats, it can read image 196 * info, metadata and ICC profiles from all image formats that provide this data. 197 * </p> 198 * 199 * @param bytes a valid array of bytes from which to read data. 200 * @return if successful, a valid buffered image 201 * @throws ImagingException in the event of a processing error while reading an image (i.e. a format violation, etc.). 202 * @throws IOException in the event of an unrecoverable I/O exception. 203 */ 204 public static BufferedImage getBufferedImage(final byte[] bytes) throws ImagingException, IOException { 205 return getBufferedImage(ByteSource.array(bytes)); 206 } 207 208 private static BufferedImage getBufferedImage(final ByteSource byteSource) throws ImagingException, IOException { 209 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 210 return imageParser.getBufferedImage(byteSource, null); 211 } 212 213 /** 214 * Reads the first image from a file. 215 * 216 * <p> 217 * For the most recent information on support for specific formats, refer to <a href="https://commons.apache.org/imaging/formatsupport.html">Format 218 * Support</a> at the main project development web site. While the Apache Commons Imaging package does not fully support all formats, it can read image 219 * info, metadata and ICC profiles from all image formats that provide this data. 220 * </p> 221 * 222 * @param file a valid reference to a file containing image data. 223 * @return if successful, a valid buffered image 224 * @throws ImagingException in the event of a processing error while reading an image (i.e. a format violation, etc.). 225 * @throws IOException in the event of an unrecoverable I/O exception. 226 */ 227 public static BufferedImage getBufferedImage(final File file) throws ImagingException, IOException { 228 return getBufferedImage(ByteSource.file(file)); 229 } 230 231 /** 232 * Reads the first image from an InputStream. 233 * 234 * <p> 235 * For the most recent information on support for specific formats, refer to <a href="https://commons.apache.org/imaging/formatsupport.html">Format 236 * Support</a> at the main project development web site. While the Apache Commons Imaging package does not fully support all formats, it can read image 237 * info, metadata and ICC profiles from all image formats that provide this data. 238 * </p> 239 * 240 * @param is a valid ImageStream from which to read data. 241 * @return if successful, a valid buffered image 242 * @throws ImagingException in the event of a processing errorfileName while reading an image (i.e. a format violation, etc.). 243 * @throws IOException in the event of an unrecoverable I/O exception. 244 */ 245 public static BufferedImage getBufferedImage(final InputStream is) throws ImagingException, IOException { 246 return getBufferedImage(is, null); 247 } 248 249 /** 250 * Reads the first image from an InputStream. 251 * 252 * <p> 253 * For the most recent information on support for specific formats, refer to <a href="https://commons.apache.org/imaging/formatsupport.html">Format 254 * Support</a> at the main project development web site. While the Apache Commons Imaging package does not fully support all formats, it can read image 255 * info, metadata and ICC profiles from all image formats that provide this data. 256 * </p> 257 * 258 * @param is a valid ImageStream from which to read data. 259 * @param fileName the image file name. 260 * @return if successful, a valid buffered image 261 * @throws ImagingException in the event of a processing error while reading an image (i.e. a format violation, etc.). 262 * @throws IOException in the event of an unrecoverable I/O exception. 263 */ 264 public static BufferedImage getBufferedImage(final InputStream is, final String fileName) throws ImagingException, IOException { 265 return getBufferedImage(ByteSource.inputStream(is, fileName)); 266 } 267 268 /** 269 * Attempts to determine the image format of the specified data and evaluates its format compliance. 270 * 271 * <p> 272 * This method returns a FormatCompliance object which includes information about the data's compliance to a specific format. 273 * </p> 274 * 275 * @param bytes a valid array of bytes containing image data. 276 * @return if successful, a valid FormatCompliance object. 277 * @throws ImagingException in the event of unreadable data. 278 * @throws IOException in the event of an unrecoverable I/O condition. 279 */ 280 public static FormatCompliance getFormatCompliance(final byte[] bytes) throws ImagingException, IOException { 281 return getFormatCompliance(ByteSource.array(bytes)); 282 } 283 284 private static FormatCompliance getFormatCompliance(final ByteSource byteSource) throws ImagingException, IOException { 285 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 286 return imageParser.getFormatCompliance(byteSource); 287 } 288 289 /** 290 * Attempts to determine the image format of the specified data and evaluates its format compliance. This method returns a FormatCompliance object which 291 * includes information about the data's compliance to a specific format. 292 * 293 * @param file valid file containing image data 294 * @return if successful, a valid FormatCompliance object. 295 * @throws ImagingException in the event of unreadable data. 296 * @throws IOException in the event of an unrecoverable I/O condition. 297 */ 298 public static FormatCompliance getFormatCompliance(final File file) throws ImagingException, IOException { 299 return getFormatCompliance(ByteSource.file(file)); 300 } 301 302 /** 303 * Extracts an ICC Profile (if present) from JPEG, PNG, PSD (Photoshop) and TIFF images. 304 * 305 * @param bytes Byte array containing an image file. 306 * @return An instance of ICC_Profile or null if the image contains no ICC profile. 307 * @throws ImagingException if it fails to parse the image 308 * @throws IOException if it fails to read the image data 309 */ 310 public static ICC_Profile getIccProfile(final byte[] bytes) throws ImagingException, IOException { 311 return getIccProfile(ByteSource.array(bytes)); 312 } 313 314 protected static ICC_Profile getIccProfile(final ByteSource byteSource) throws ImagingException, IOException { 315 final byte[] bytes = getIccProfileBytes(byteSource); 316 if (bytes == null) { 317 return null; 318 } 319 320 final IccProfileParser parser = new IccProfileParser(); 321 final IccProfileInfo info = parser.getIccProfileInfo(bytes); 322 if (info == null) { 323 return null; 324 } 325 if (info.isSrgb()) { 326 return null; 327 } 328 329 return ICC_Profile.getInstance(bytes); 330 } 331 332 /** 333 * Extracts an ICC Profile (if present) from JPEG, PNG, PSD (Photoshop) and TIFF images. 334 * 335 * @param file File containing image data. 336 * @return An instance of ICC_Profile or null if the image contains no ICC profile. 337 * @throws ImagingException if it fails to parse the image 338 * @throws IOException if it fails to read the image data 339 */ 340 public static ICC_Profile getIccProfile(final File file) throws ImagingException, IOException { 341 return getIccProfile(ByteSource.file(file)); 342 } 343 344 /** 345 * Extracts an ICC Profile (if present) from JPEG, PNG, PSD (Photoshop) and TIFF images. 346 * 347 * @param is InputStream from which to read image data. 348 * @param fileName File name associated with image data (optional). 349 * @return An instance of ICC_Profile or null if the image contains no ICC profile. 350 * @throws ImagingException if it fails to parse the image 351 * @throws IOException if it fails to read the image data 352 */ 353 public static ICC_Profile getIccProfile(final InputStream is, final String fileName) throws ImagingException, IOException { 354 return getIccProfile(ByteSource.inputStream(is, fileName)); 355 } 356 357 /** 358 * Extracts the raw bytes of an ICC Profile (if present) from JPEG, PNG, PSD (Photoshop) and TIFF images. 359 * 360 * <p> 361 * To parse the result use IccProfileParser or ICC_Profile.getInstance(bytes). 362 * </p> 363 * 364 * @param bytes Byte array containing an image file. 365 * @return A byte array. 366 * @see IccProfileParser 367 * @see ICC_Profile 368 * @throws ImagingException if it fails to parse the image 369 * @throws IOException if it fails to read the image data 370 */ 371 public static byte[] getIccProfileBytes(final byte[] bytes) throws ImagingException, IOException { 372 return getIccProfileBytes(ByteSource.array(bytes)); 373 } 374 375 private static byte[] getIccProfileBytes(final ByteSource byteSource) throws ImagingException, IOException { 376 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 377 return imageParser.getIccProfileBytes(byteSource, null); 378 } 379 380 /** 381 * Extracts the raw bytes of an ICC Profile (if present) from JPEG, PNG, PSD (Photoshop) and TIFF images. 382 * 383 * <p> 384 * To parse the result use IccProfileParser or ICC_Profile.getInstance(bytes). 385 * </p> 386 * 387 * @param file File containing image data. 388 * @return A byte array. 389 * @see IccProfileParser 390 * @see ICC_Profile 391 * @throws ImagingException if it fails to parse the image 392 * @throws IOException if it fails to read the image data 393 */ 394 public static byte[] getIccProfileBytes(final File file) throws ImagingException, IOException { 395 return getIccProfileBytes(ByteSource.file(file)); 396 } 397 398 /** 399 * Parses the "image info" of an image. 400 * 401 * <p> 402 * "Image info" is a summary of basic information about the image such as: width, height, file format, bit depth, color type, etc. 403 * </p> 404 * 405 * <p> 406 * Not to be confused with "image metadata." 407 * </p> 408 * 409 * @param bytes Byte array containing an image file. 410 * @return An instance of ImageInfo. 411 * @see ImageInfo 412 * @throws ImagingException if it fails to parse the image 413 * @throws IOException if it fails to read the image data 414 */ 415 public static ImageInfo getImageInfo(final byte[] bytes) throws ImagingException, IOException { 416 return getImageInfo(ByteSource.array(bytes)); 417 } 418 419 private static ImageInfo getImageInfo(final ByteSource byteSource) throws ImagingException, IOException { 420 return ImageParserFactory.getImageParser(byteSource).getImageInfo(byteSource, null); 421 } 422 423 /** 424 * Parses the "image info" of an image file. 425 * 426 * <p> 427 * "Image info" is a summary of basic information about the image such as: width, height, file format, bit depth, color type, etc. 428 * </p> 429 * 430 * <p> 431 * Not to be confused with "image metadata." 432 * </p> 433 * 434 * @param file File containing image data. 435 * @return An instance of ImageInfo. 436 * @see ImageInfo 437 * @throws ImagingException if it fails to parse the image 438 * @throws IOException if it fails to read the image data 439 */ 440 public static ImageInfo getImageInfo(final File file) throws ImagingException, IOException { 441 return getImageInfo(ByteSource.file(file)); 442 } 443 444 /** 445 * Parses the "image info" of an image. 446 * 447 * <p> 448 * "Image info" is a summary of basic information about the image such as: width, height, file format, bit depth, color type, etc. 449 * </p> 450 * 451 * <p> 452 * Not to be confused with "image metadata." 453 * </p> 454 * 455 * @param is InputStream from which to read image data. 456 * @param fileName File name associated with image data (optional). 457 * @return An instance of ImageInfo. 458 * @see ImageInfo 459 * @throws ImagingException if it fails to parse the image 460 * @throws IOException if it fails to read the image data 461 */ 462 public static ImageInfo getImageInfo(final InputStream is, final String fileName) throws ImagingException, IOException { 463 return getImageInfo(ByteSource.inputStream(is, fileName)); 464 } 465 466 /** 467 * Parses the "image info" of an image. 468 * 469 * <p> 470 * "Image info" is a summary of basic information about the image such as: width, height, file format, bit depth, color type, etc. 471 * </p> 472 * 473 * <p> 474 * Not to be confused with "image metadata." 475 * </p> 476 * 477 * @param fileName String. 478 * @param bytes Byte array containing an image file. 479 * @return An instance of ImageInfo. 480 * @see ImageInfo 481 * @throws ImagingException if it fails to parse the image 482 * @throws IOException if it fails to read the image data 483 */ 484 public static ImageInfo getImageInfo(final String fileName, final byte[] bytes) throws ImagingException, IOException { 485 return getImageInfo(ByteSource.array(bytes, fileName)); 486 } 487 488 /** 489 * Determines the width and height of an image. 490 * 491 * @param bytes Byte array containing an image file. 492 * @return The width and height of the image. 493 * @throws ImagingException if it fails to parse the image 494 * @throws IOException if it fails to read the image data 495 */ 496 public static Dimension getImageSize(final byte[] bytes) throws ImagingException, IOException { 497 return getImageSize(ByteSource.array(bytes)); 498 } 499 500 /** 501 * Determines the width and height of an image byte source. 502 * 503 * @param byteSource Byte source data. 504 * @return The width and height of the image. 505 * @throws ImagingException if it fails to parse the image 506 * @throws IOException if it fails to read the image data 507 */ 508 public static Dimension getImageSize(final ByteSource byteSource) throws ImagingException, IOException { 509 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 510 return imageParser.getImageSize(byteSource, null); 511 } 512 513 /** 514 * Determines the width and height of an image file. 515 * 516 * @param file File containing image data. 517 * @return The width and height of the image. 518 * @throws ImagingException if it fails to parse the image 519 * @throws IOException if it fails to read the image data 520 */ 521 public static Dimension getImageSize(final File file) throws ImagingException, IOException { 522 return getImageSize(ByteSource.file(file)); 523 } 524 525 /** 526 * Determines the width and height of an image. 527 * 528 * @param is InputStream from which to read image data. 529 * @param fileName File name associated with image data (optional). 530 * @return The width and height of the image. 531 * @throws ImagingException if it fails to parse the image 532 * @throws IOException if it fails to read the image data 533 */ 534 public static Dimension getImageSize(final InputStream is, final String fileName) throws ImagingException, IOException { 535 return getImageSize(ByteSource.inputStream(is, fileName)); 536 } 537 538 /** 539 * Parses the metadata of an image. This metadata depends on the format of the image. 540 * 541 * <p> 542 * JPEG/JFIF files may contain EXIF and/or IPTC metadata. PNG files may contain comments. TIFF files may contain metadata. 543 * </p> 544 * 545 * <p> 546 * The instance of IImageMetadata returned by getMetadata() should be upcast (depending on image format). 547 * </p> 548 * 549 * <p> 550 * Not to be confused with "image info." 551 * </p> 552 * 553 * @param bytes Byte array containing an image file. 554 * @return An instance of ImageMetadata. 555 * @see org.apache.commons.imaging.common.ImageMetadata 556 * @throws ImagingException if it fails to read the image metadata 557 * @throws IOException if it fails to read the image data 558 */ 559 public static ImageMetadata getMetadata(final byte[] bytes) throws ImagingException, IOException { 560 return getMetadata(ByteSource.array(bytes)); 561 } 562 563 private static ImageMetadata getMetadata(final ByteSource byteSource) throws ImagingException, IOException { 564 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 565 return imageParser.getMetadata(byteSource, null); 566 } 567 568 /** 569 * Parses the metadata of an image file. This metadata depends on the format of the image. 570 * 571 * <p> 572 * JPEG/JFIF files may contain EXIF and/or IPTC metadata. PNG files may contain comments. TIFF files may contain metadata. 573 * </p> 574 * 575 * <p> 576 * The instance of IImageMetadata returned by getMetadata() should be upcast (depending on image format). 577 * </p> 578 * 579 * <p> 580 * Not to be confused with "image info." 581 * </p> 582 * 583 * @param file File containing image data. 584 * @return An instance of IImageMetadata. 585 * @see org.apache.commons.imaging.common.ImageMetadata 586 * @throws ImagingException if it fails to read the image metadata 587 * @throws IOException if it fails to read the image data 588 */ 589 public static ImageMetadata getMetadata(final File file) throws ImagingException, IOException { 590 return getMetadata(ByteSource.file(file)); 591 } 592 593 /** 594 * Parses the metadata of an image file. This metadata depends on the format of the image. 595 * 596 * <p> 597 * JPEG/JFIF files may contain EXIF and/or IPTC metadata. PNG files may contain comments. TIFF files may contain metadata. 598 * </p> 599 * 600 * <p> 601 * The instance of IImageMetadata returned by getMetadata() should be upcast (depending on image format). 602 * </p> 603 * 604 * <p> 605 * Not to be confused with "image info." 606 * </p> 607 * 608 * @param is InputStream from which to read image data. 609 * @param fileName File name associated with image data (optional). 610 * @return An instance of IImageMetadata. 611 * @see org.apache.commons.imaging.common.ImageMetadata 612 * @throws ImagingException if it fails to read the image metadata 613 * @throws IOException if it fails to read the image data 614 */ 615 public static ImageMetadata getMetadata(final InputStream is, final String fileName) throws ImagingException, IOException { 616 return getMetadata(ByteSource.inputStream(is, fileName)); 617 } 618 619 /** 620 * Extracts the embedded XML metadata as an XML string. 621 * 622 * @param bytes Byte array containing an image file. 623 * @return Xmp Xml as String, if present. Otherwise, returns null. 624 * @throws ImagingException if it fails to parse the image 625 * @throws IOException if it fails to read the image data 626 */ 627 public static String getXmpXml(final byte[] bytes) throws ImagingException, IOException { 628 return getXmpXml(ByteSource.array(bytes)); 629 } 630 631 /** 632 * Extracts the embedded XML metadata as an XML string. 633 * 634 * @param byteSource File containing image data. 635 * @return Xmp Xml as String, if present. Otherwise, returns null. 636 * @throws ImagingException if it fails to parse the image 637 * @throws IOException if it fails to read the image data 638 */ 639 public static String getXmpXml(final ByteSource byteSource) throws ImagingException, IOException { 640 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(byteSource); 641 if (imageParser instanceof XmpEmbeddable) { 642 return ((XmpEmbeddable<?>) imageParser).getXmpXml(byteSource, null); 643 } 644 return null; 645 } 646 647 /** 648 * Extracts the embedded XML metadata as an XML string. 649 * 650 * @param file File containing image data. 651 * @return Xmp Xml as String, if present. Otherwise, returns null. 652 * @throws ImagingException if it fails to parse the image 653 * @throws IOException if it fails to read the image data 654 */ 655 public static String getXmpXml(final File file) throws ImagingException, IOException { 656 return getXmpXml(ByteSource.file(file)); 657 } 658 659 /** 660 * Extracts the embedded XML metadata as an XML string. 661 * 662 * @param is InputStream from which to read image data. 663 * @param fileName File name associated with image data (optional). 664 * @return Xmp Xml as String, if present. Otherwise, returns null. 665 * @throws ImagingException if it fails to parse the image 666 * @throws IOException if it fails to read the image data 667 */ 668 public static String getXmpXml(final InputStream is, final String fileName) throws ImagingException, IOException { 669 return getXmpXml(ByteSource.inputStream(is, fileName)); 670 } 671 672 /** 673 * Attempts to determine the image format of a file based on its "magic numbers," the first bytes of the data. 674 * 675 * <p> 676 * Many graphics format specify identifying byte values that appear at the beginning of the data file. This method checks for such identifying elements and 677 * returns a ImageFormat enumeration indicating what it detects. Note that this method can return "false positives" in cases where non-image files begin 678 * with the specified byte values. 679 * </p> 680 * 681 * @param bytes Byte array containing an image file. 682 * @return An ImageFormat, such as ImageFormat.IMAGE_FORMAT_JPEG. Returns ImageFormat.IMAGE_FORMAT_UNKNOWN if the image type cannot be determined. 683 * @throws IOException in the event of an unrecoverable I/O condition. 684 */ 685 public static ImageFormat guessFormat(final byte[] bytes) throws IOException { 686 return guessFormat(ByteSource.array(bytes)); 687 } 688 689 /** 690 * Attempts to determine the image format of a file based on its "magic numbers," the first bytes of the data. 691 * 692 * <p> 693 * Many graphics formats specify identifying byte values that appear at the beginning of the data file. This method checks for such identifying elements and 694 * returns a ImageFormat enumeration indicating what it detects. Note that this method can return "false positives" in cases where non-image files begin 695 * with the specified byte values. 696 * </p> 697 * 698 * @param byteSource a valid ByteSource object potentially supplying data for an image. 699 * @return An ImageFormat, such as ImageFormat.IMAGE_FORMAT_JPEG. Returns ImageFormat.IMAGE_FORMAT_UNKNOWN if the image type cannot be determined. 700 * @throws IllegalArgumentException in the event of an unsuccessful attempt to read the image data 701 * @throws IOException in the event of an unrecoverable I/O condition. 702 */ 703 public static ImageFormat guessFormat(final ByteSource byteSource) throws IOException { 704 if (byteSource == null) { 705 return ImageFormats.UNKNOWN; 706 } 707 708 try (InputStream is = byteSource.getInputStream()) { 709 final int i1 = is.read(); 710 final int i2 = is.read(); 711 if (i1 < 0 || i2 < 0) { 712 throw new IllegalArgumentException("Couldn't read magic numbers to guess format."); 713 } 714 715 final int b1 = i1 & 0xff; 716 final int b2 = i2 & 0xff; 717 final int[] bytePair = { b1, b2, }; 718 719 if (compareBytePair(MAGIC_NUMBERS_GIF, bytePair)) { 720 return ImageFormats.GIF; 721 // } else if (b1 == 0x00 && b2 == 0x00) // too similar to TGA 722 // { 723 // return ImageFormat.IMAGE_FORMAT_ICO; 724 } 725 if (compareBytePair(MAGIC_NUMBERS_PNG, bytePair)) { 726 return ImageFormats.PNG; 727 } 728 if (compareBytePair(MAGIC_NUMBERS_JPEG, bytePair)) { 729 return ImageFormats.JPEG; 730 } 731 if (compareBytePair(MAGIC_NUMBERS_BMP, bytePair)) { 732 return ImageFormats.BMP; 733 } 734 if (compareBytePair(MAGIC_NUMBERS_TIFF_MOTOROLA, bytePair)) { 735 return ImageFormats.TIFF; 736 } 737 if (compareBytePair(MAGIC_NUMBERS_TIFF_INTEL, bytePair)) { 738 return ImageFormats.TIFF; 739 } 740 if (compareBytePair(MAGIC_NUMBERS_PSD, bytePair)) { 741 return ImageFormats.PSD; 742 } 743 if (compareBytePair(MAGIC_NUMBERS_PAM, bytePair)) { 744 return ImageFormats.PAM; 745 } 746 if (compareBytePair(MAGIC_NUMBERS_PBM_A, bytePair)) { 747 return ImageFormats.PBM; 748 } 749 if (compareBytePair(MAGIC_NUMBERS_PBM_B, bytePair)) { 750 return ImageFormats.PBM; 751 } 752 if (compareBytePair(MAGIC_NUMBERS_PGM_A, bytePair)) { 753 return ImageFormats.PGM; 754 } 755 if (compareBytePair(MAGIC_NUMBERS_PGM_B, bytePair)) { 756 return ImageFormats.PGM; 757 } 758 if (compareBytePair(MAGIC_NUMBERS_PPM_A, bytePair)) { 759 return ImageFormats.PPM; 760 } 761 if (compareBytePair(MAGIC_NUMBERS_PPM_B, bytePair)) { 762 return ImageFormats.PPM; 763 } 764 if (compareBytePair(MAGIC_NUMBERS_JBIG2_1, bytePair)) { 765 final int i3 = is.read(); 766 final int i4 = is.read(); 767 if (i3 < 0 || i4 < 0) { 768 throw new IllegalArgumentException("Couldn't read magic numbers to guess format."); 769 } 770 771 final int b3 = i3 & 0xff; 772 final int b4 = i4 & 0xff; 773 final int[] bytePair2 = { b3, b4, }; 774 if (compareBytePair(MAGIC_NUMBERS_JBIG2_2, bytePair2)) { 775 return ImageFormats.JBIG2; 776 } 777 } else if (compareBytePair(MAGIC_NUMBERS_ICNS, bytePair)) { 778 return ImageFormats.ICNS; 779 } else if (compareBytePair(MAGIC_NUMBERS_DCX, bytePair)) { 780 return ImageFormats.DCX; 781 } else if (compareBytePair(MAGIC_NUMBERS_RGBE, bytePair)) { 782 return ImageFormats.RGBE; 783 } else if (compareBytePair(MAGIC_NUMBERS_RIFF_1, bytePair)) { 784 final int i3 = is.read(); 785 final int i4 = is.read(); 786 if (i3 < 0 || i4 < 0) { 787 throw new IllegalArgumentException("Couldn't read magic numbers to guess format."); 788 } 789 790 final int b3 = i3 & 0xff; 791 final int b4 = i4 & 0xff; 792 final int[] bytePair2 = { b3, b4, }; 793 if (compareBytePair(MAGIC_NUMBERS_RIFF_2, bytePair2)) { 794 final byte[] bytes = new byte[4]; 795 if (is.read(bytes) < 4) { // Skip file size 796 throw new IllegalArgumentException("Couldn't read magic numbers to guess format."); 797 } 798 799 if (is.read(bytes) == 4 && Arrays.equals(MAGIC_NUMBERS_WEBP, bytes)) { 800 return ImageFormats.WEBP; 801 } 802 } 803 } 804 return Stream.of(ImageFormats.values()).filter(imageFormat -> Stream.of(imageFormat.getExtensions()).anyMatch(extension -> { 805 final String fileName = byteSource.getFileName(); 806 if (fileName == null || fileName.trim().isEmpty()) { 807 return false; 808 } 809 final String fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1); 810 return extension != null && !extension.trim().isEmpty() && fileExtension.equalsIgnoreCase(extension); 811 })).findFirst().orElse(ImageFormats.UNKNOWN); 812 } 813 } 814 815 /** 816 * Attempts to determine the image format of a file based on its "magic numbers," the first bytes of the data. 817 * 818 * <p> 819 * Many graphics formats specify identifying byte values that appear at the beginning of the data file. This method checks for such identifying elements and 820 * returns a ImageFormat enumeration indicating what it detects. Note that this method can return "false positives" in cases where non-image files begin 821 * with the specified byte values. 822 * </p> 823 * 824 * @param file File containing image data. 825 * @return An ImageFormat, such as ImageFormat.IMAGE_FORMAT_JPEG. Returns ImageFormat.IMAGE_FORMAT_UNKNOWN if the image type cannot be determined. 826 * @throws IOException in the event of an unrecoverable I/O condition. 827 */ 828 public static ImageFormat guessFormat(final File file) throws IOException { 829 return guessFormat(ByteSource.file(file)); 830 } 831 832 /** 833 * Attempts to determine if a file contains an image recorded in a supported graphics format based on its file-name extension (for example ".jpg", 834 * ".gif", ".png", etc.). 835 * 836 * @param file A valid File object providing a reference to a file that may contain an image. 837 * @return true if the file-name includes a supported image format file extension; otherwise, false. 838 */ 839 public static boolean hasImageFileExtension(final File file) { 840 if (file == null || !file.isFile()) { 841 return false; 842 } 843 return hasImageFileExtension(file.getName()); 844 } 845 846 /** 847 * Attempts to determine if a file contains an image recorded in a supported graphics format based on its file-name extension (for example ".jpg", 848 * ".gif", ".png", etc.). 849 * 850 * @param fileName A valid string representing name of file which may contain an image. 851 * @return true if the file name has an image format file extension. 852 */ 853 public static boolean hasImageFileExtension(final String fileName) { 854 if (fileName == null) { 855 return false; 856 } 857 858 final String normalizedFileName = fileName.toLowerCase(Locale.ROOT); 859 860 for (final AbstractImageParser<?> imageParser : AbstractImageParser.getAllImageParsers()) { 861 for (final String extension : imageParser.getAcceptedExtensions()) { 862 if (normalizedFileName.endsWith(extension.toLowerCase(Locale.ROOT))) { 863 return true; 864 } 865 } 866 } 867 868 return false; 869 } 870 871 /** 872 * Writes the content of a BufferedImage to a file using the specified image format. 873 * 874 * <p> 875 * Image writing is not supported for all graphics formats. For the most recent information on support for specific formats, refer to 876 * <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a> at the main project development web site. While the Apache Commons 877 * Imaging package does not fully support all formats, it can read image info, metadata and ICC profiles from all image formats that provide this data. 878 * </p> 879 * 880 * @param src a valid BufferedImage object 881 * @param file the file to which the output image is to be written 882 * @param format the format in which the output image is to be written 883 * @throws ImagingException in the event of a format violation, unsupported image format, etc. 884 * @throws IOException in the event of an unrecoverable I/O exception. 885 * @see ImagingConstants 886 */ 887 public static void writeImage(final BufferedImage src, final File file, final ImageFormat format) throws ImagingException, IOException { 888 try (FileOutputStream fos = new FileOutputStream(file); 889 BufferedOutputStream os = new BufferedOutputStream(fos)) { 890 writeImage(src, os, format); 891 } 892 } 893 894 /** 895 * Writes the content of a BufferedImage to an OutputStream using the specified image format. 896 * 897 * <p> 898 * Image writing is not supported for all graphics formats. For the most recent information on support for specific formats, refer to 899 * <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a> at the main project development web site. While the Apache Commons 900 * Imaging package does not fully support all formats, it can read image info, metadata and ICC profiles from all image formats that provide this data. 901 * </p> 902 * 903 * @param src a valid BufferedImage object 904 * @param outputStream the OutputStream to which the output image is to be written 905 * @param format the format in which the output image is to be written 906 * @throws ImagingException in the event of a format violation, unsupported image format, etc. 907 * @throws IOException in the event of an unrecoverable I/O exception. 908 * @see ImagingConstants 909 */ 910 public static void writeImage(final BufferedImage src, final OutputStream outputStream, final ImageFormat format) throws ImagingException, IOException { 911 Objects.requireNonNull(src, "src"); 912 Objects.requireNonNull(outputStream, "outputStream"); 913 Objects.requireNonNull(format, "format"); 914 915 final AbstractImageParser<?> imageParser = ImageParserFactory.getImageParser(format); 916 imageParser.writeImage(src, outputStream, null); 917 } 918 919 /** 920 * Writes the content of a BufferedImage to a byte array using the specified image format. 921 * 922 * <p> 923 * Image writing is not supported for all graphics formats. For the most recent information on support for specific formats, refer to 924 * <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a> at the main project development web site. While the Apache Commons 925 * Imaging package does not fully support all formats, it can read image info, metadata and ICC profiles from all image formats that provide this data. 926 * </p> 927 * 928 * @param src a valid BufferedImage object 929 * @param format the format in which the output image is to be written 930 * @return if successful, a valid array of bytes. 931 * @throws ImagingException in the event of a format violation, unsupported image format, etc. 932 * @throws IOException in the event of an unrecoverable I/O exception. 933 * @see ImagingConstants 934 */ 935 public static byte[] writeImageToBytes(final BufferedImage src, final ImageFormat format) throws ImagingException, IOException { 936 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { 937 writeImage(src, os, format); 938 return os.toByteArray(); 939 } 940 } 941 942 private Imaging() { 943 // Instances can not be created 944 } 945 }