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