    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 java.awt.Dimension;
    020import java.awt.image.BufferedImage;
    021import java.io.File;
    022import java.io.IOException;
    023import java.io.OutputStream;
    024import java.io.PrintWriter;
    025import java.io.StringWriter;
    026import java.util.ArrayList;
    027import java.util.List;
    028import java.util.Locale;
    029import java.util.Map;
    030import java.util.logging.Level;
    031import java.util.logging.Logger;
    032
    033import org.apache.commons.imaging.common.BinaryFileParser;
    034import org.apache.commons.imaging.common.BufferedImageFactory;
    035import org.apache.commons.imaging.common.ImageMetadata;
    036import org.apache.commons.imaging.common.SimpleBufferedImageFactory;
    037import org.apache.commons.imaging.common.bytesource.ByteSource;
    038import org.apache.commons.imaging.common.bytesource.ByteSourceArray;
    039import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
    040import org.apache.commons.imaging.formats.bmp.BmpImageParser;
    041import org.apache.commons.imaging.formats.dcx.DcxImageParser;
    042import org.apache.commons.imaging.formats.gif.GifImageParser;
    043import org.apache.commons.imaging.formats.icns.IcnsImageParser;
    044import org.apache.commons.imaging.formats.ico.IcoImageParser;
    045import org.apache.commons.imaging.formats.jpeg.JpegImageParser;
    046import org.apache.commons.imaging.formats.pcx.PcxImageParser;
    047import org.apache.commons.imaging.formats.png.PngImageParser;
    048import org.apache.commons.imaging.formats.pnm.PnmImageParser;
    049import org.apache.commons.imaging.formats.psd.PsdImageParser;
    050import org.apache.commons.imaging.formats.rgbe.RgbeImageParser;
    051import org.apache.commons.imaging.formats.tiff.TiffImageParser;
    052import org.apache.commons.imaging.formats.wbmp.WbmpImageParser;
    053import org.apache.commons.imaging.formats.xbm.XbmImageParser;
    054import org.apache.commons.imaging.formats.xpm.XpmImageParser;
    055
    056/**
    057 * Provides the abstract base class for all image reading and writing
    058 * utilities.  ImageParser implementations are expected to extend this
    059 * class providing logic for identifying and processing data in their
    060 * own specific format.   Specific implementations are found
    061 * under the com.apache.commons.imaging.formats package.
    062 *
    063 * <h2>Application Notes</h2>
    064 *
    065 * <h3>Format support</h3>
    066 *
    067 * For the most recent information on format support for the
    068 * Apache Commons Imaging package, refer to
    069 * <a href="https://commons.apache.org/imaging/formatsupport.html">Format Support</a>
    070 * at the main project development web site.
    071 *
    072 * <h3>On the accuracy of this Javadoc</h3>
    073 *
    074 * The original authors of this class did not supply documentation.
    075 * The Javadoc for this class is based on inspection of the
    076 * source code.  In some cases, the purpose and usage for particular
    077 * methods was deduced from the source and may not perfectly reflect
    078 * the intentions of the original. Therefore, you should not assume
    079 * that the documentation is perfect, especially in the more obscure
    080 * and specialized areas of implementation.
    081 *
    082 * <h3>The "Map params" argument</h3>
    083 *
    084 * Many of the methods specified by this class accept an argument of
    085 * type Map giving a list of parameters to be used when processing an
    086 * image. For example, some of the output formats permit the specification
    087 * of different kinds of image compression or color models. Some of the
    088 * reading methods permit the calling application to require strict
    089 * format compliance.   In many cases, however, an application will not
    090 * require the use of this argument.  While some of the ImageParser
    091 * implementations check for (and ignore) null arguments for this parameter,
    092 * not all of them do (at least not at the time these notes were written).
    093 * Therefore, a prudent programmer will always supply an valid, though
    094 * empty instance of a Map implementation when calling such methods.
    095 * Generally, the java HashMap class is useful for this purpose.
    096 *
    097 * <p>Additionally, developers creating or enhancing classes derived
    098 * from ImageParser are encouraged to include such checks in their code.
    099 */
    100public abstract class ImageParser extends BinaryFileParser {
    101
    102    private static final Logger LOGGER = Logger.getLogger(ImageParser.class.getName());
    103
    104    /**
    105     * Gets an array of new instances of all image parsers.
    106     *
    107     * @return A valid array of image parsers
    108     */
    109    public static ImageParser[] getAllImageParsers() {
    110
    111        return new ImageParser[]{
    112                new BmpImageParser(),
    113                new DcxImageParser(),
    114                new GifImageParser(),
    115                new IcnsImageParser(),
    116                new IcoImageParser(),
    117                new JpegImageParser(),
    118                new PcxImageParser(),
    119                new PngImageParser(),
    120                new PnmImageParser(),
    121                new PsdImageParser(),
    122                new RgbeImageParser(),
    123                new TiffImageParser(),
    124                new WbmpImageParser(),
    125                new XbmImageParser(),
    126                new XpmImageParser(),
    127                // new JBig2ImageParser(),
    128                // new TgaImageParser(),
    129        };
    130    }
    131
    132    /**
    133     * Get image metadata from the specified byte source.  Format-specific
    134     * ImageParser implementations are expected to return a valid
    135     * IImageMetadata object or to throw an ImageReadException if unable
    136     * to process the specified byte source.
    137     *
    138     * @param byteSource A valid byte source.
    139     * @return A valid, potentially subject-matter-specific implementation of
    140     *         the IImageMetadata interface describing the content extracted
    141     *         from the source content.
    142     * @throws ImageReadException In the event that the ByteSource
    143     *                            content does not conform to the format of the specific parser
    144     *                            implementation.
    145     * @throws IOException        In the event of unsuccessful data read operation.
    146     */
    147    public final ImageMetadata getMetadata(final ByteSource byteSource) throws ImageReadException, IOException {
    148        return getMetadata(byteSource, null);
    149    }
    150
    151    /**
    152     * Get image metadata from the specified byte source.  Format-specific
    153     * ImageParser implementations are expected to return a valid
    154     * IImageMetadata object or to throw an ImageReadException if unable
    155     * to process the specified byte source.
    156     *
    157     * <p>The params argument provides a mechanism for individual
    158     * implementations to pass optional information into the parser.
    159     * Not all formats will require this capability.  Because the
    160     * base class may call this method with a null params argument,
    161     * implementations should <strong>always</strong> include logic
    162     * for ignoring null input.
    163     *
    164     * @param byteSource A valid byte source.
    165     * @param params     Optional instructions for special-handling or
    166     *                   interpretation of the input data (null objects are permitted and
    167     *                   must be supported by implementations).
    168     * @return A valid, potentially subject-matter-specific implementation of
    169     *         the IImageMetadata interface describing the content extracted
    170     *         from the source content.
    171     * @throws ImageReadException In the event that the ByteSource
    172     *                            content does not conform to the format of the specific parser
    173     *                            implementation.
    174     * @throws IOException        In the event of unsuccessful data read operation.
    175     */
    176    public abstract ImageMetadata getMetadata(ByteSource byteSource, Map<String, Object> params)
    177            throws ImageReadException, IOException;
    178
    179    /**
    180     * Get image metadata from the specified array of bytes.  Format-specific
    181     * ImageParser implementations are expected to return a valid
    182     * IImageMetadata object or to throw an ImageReadException if unable
    183     * to process the specified data.
    184     *
    185     * @param bytes A valid array of bytes
    186     * @return A valid, potentially subject-matter-specific implementation of
    187     *         the IImageMetadata interface describing the content extracted
    188     *         from the source content.
    189     * @throws ImageReadException In the event that the specified content
    190     *                            does not conform to the format of the specific
    191     *                            parser implementation.
    192     * @throws IOException        In the event of unsuccessful data read operation.
    193     */
    194    public final ImageMetadata getMetadata(final byte[] bytes) throws ImageReadException, IOException {
    195        return getMetadata(bytes, null);
    196    }
    197
    198    /**
    199     * Get image metadata from the specified array of bytes.  Format-specific
    200     * ImageParser implementations are expected to return a valid
    201     * IImageMetadata object or to throw an ImageReadException if unable
    202     * to process the specified data.
    203     *
    204     * <p>The params argument provides a mechanism for individual
    205     * implementations to pass optional information into the parser.
    206     * Not all formats will require this capability.  Because the
    207     * base class may call this method with a null params argument,
    208     * implementations should <strong>always</strong> include logic
    209     * for ignoring null input.
    210     *
    211     * @param bytes  A valid array of bytes
    212     * @param params Optional instructions for special-handling or
    213     *               interpretation of the input data (null objects are permitted and
    214     *               must be supported by implementations).
    215     * @return A valid image metadata object describing the content extracted
    216     *         from  the specified content.
    217     * @throws ImageReadException In the event that the specified content
    218     *                            does not conform to the format of the specific
    219     *                            parser implementation.
    220     * @throws IOException        In the event of unsuccessful data read operation.
    221     */
    222    public final ImageMetadata getMetadata(final byte[] bytes, final Map<String, Object> params)
    223            throws ImageReadException, IOException {
    224        return getMetadata(new ByteSourceArray(bytes), params);
    225    }
    226
    227    /**
    228     * Get image metadata from the specified file.  Format-specific
    229     * ImageParser implementations are expected to return a valid
    230     * IImageMetadata object or to throw an ImageReadException if unable
    231     * to process the specified data.
    232     *
    233     * @param file A valid reference to a file.
    234     * @return A valid image metadata object describing the content extracted
    235     *         from  the specified content.
    236     * @throws ImageReadException In the event that the specified content
    237     *                            does not conform to the format of the specific
    238     *                            parser implementation.
    239     * @throws IOException        In the event of unsuccessful file read or
    240     *                            access operation.
    241     */
    242    public final ImageMetadata getMetadata(final File file) throws ImageReadException, IOException {
    243        return getMetadata(file, null);
    244    }
    245
    246    /**
    247     * Get image metadata from the specified file.  Format-specific
    248     * ImageParser implementations are expected to return a valid
    249     * IImageMetadata object or to throw an ImageReadException if unable
    250     * to process the specified data.
    251     *
    252     * <p>The params argument provides a mechanism for individual
    253     * implementations to pass optional information into the parser.
    254     * Not all formats will require this capability.  Because the
    255     * base class may call this method with a null params argument,
    256     * implementations should <strong>always</strong> include logic
    257     * for ignoring null input.
    258     *
    259     * @param file   A valid reference to a file.
    260     * @param params Optional instructions for special-handling or
    261     *               interpretation of the input data (null objects are permitted and
    262     *               must be supported by implementations).
    263     * @return A valid image metadata object describing the content extracted
    264     *         from  the specified content.
    265     * @throws ImageReadException In the event that the specified content
    266     *                            does not conform to the format of the specific
    267     *                            parser implementation.
    268     * @throws IOException        In the event of unsuccessful file read or
    269     *                            access operation.
    270     */
    271    public final ImageMetadata getMetadata(final File file, final Map<String, Object> params)
    272            throws ImageReadException, IOException {
    273        if (LOGGER.isLoggable(Level.FINEST)) {
    274            LOGGER.finest(getName() + ".getMetadata" + ": " + file.getName());
    275        }
    276
    277        if (!canAcceptExtension(file)) {
    278            return null;
    279        }
    280
    281        return getMetadata(new ByteSourceFile(file), params);
    282    }
    283
    284    /**
    285     * Get image information from the specified ByteSource. Format-specific
    286     * ImageParser implementations are expected to return a valid
    287     * ImageInfo object or to throw an ImageReadException if unable
    288     * to process the specified data.
    289     *
    290     * <p>The params argument provides a mechanism for individual
    291     * implementations to pass optional information into the parser.
    292     * Not all formats will require this capability.  Because the
    293     * base class may call this method with a null params argument,
    294     * implementations should <strong>always</strong> include logic
    295     * for ignoring null input.
    296     *
    297     * @param byteSource A valid ByteSource object
    298     * @param params     Optional instructions for special-handling or interpretation
    299     *                   of the input data (null objects are permitted and
    300     *                   must be supported by implementations).
    301     * @return A valid image information object describing the content extracted
    302     *         from the specified data.
    303     * @throws ImageReadException In the event that the specified content
    304     *                            does not conform to the format of the specific
    305     *                            parser implementation.
    306     * @throws IOException        In the event of unsuccessful data access operation.
    307     */
    308    public abstract ImageInfo getImageInfo(ByteSource byteSource, Map<String, Object> params)
    309            throws ImageReadException, IOException;
    310
    311    /**
    312     * Get image information from the specified ByteSource.  Format-specific
    313     * ImageParser implementations are expected to return a valid
    314     * ImageInfo object or to throw an ImageReadException if unable
    315     * to process the specified data.
    316     *
    317     * @param byteSource A valid ByteSource object
    318     * @return A valid image information object describing the content extracted
    319     *         from the specified data.
    320     * @throws ImageReadException In the event that the specified content
    321     *                            does not conform to the format of the specific
    322     *                            parser implementation.
    323     * @throws IOException        In the event of unsuccessful data
    324     *                            access operation.
    325     */
    326    public final ImageInfo getImageInfo(final ByteSource byteSource) throws ImageReadException, IOException {
    327        return getImageInfo(byteSource, null);
    328    }
    329
    330    /**
    331     * Get image information from the specified array of bytes.  Format-specific
    332     * ImageParser implementations are expected to return a valid
    333     * ImageInfo object or to throw an ImageReadException if unable
    334     * to process the specified data.
    335     * <p>The params argument provides a mechanism for individual
    336     * implementations to pass optional information into the parser.
    337     * Not all formats will require this capability.  Because the
    338     * base class may call this method with a null params argument,
    339     * implementations should <strong>always</strong> include logic
    340     * for ignoring null input.
    341     *
    342     * @param bytes  A valid array of bytes
    343     * @param params Optional instructions for special-handling or
    344     *               interpretation of the input data (null objects are permitted and
    345     *               must be supported by implementations).
    346     * @return A valid image information object describing the content extracted
    347     *         from the specified data.
    348     * @throws ImageReadException In the event that the specified content
    349     *                            does not conform to the format of the specific
    350     *                            parser implementation.
    351     * @throws IOException        In the event of unsuccessful data
    352     *                            access operation.
    353     */
    354    public final ImageInfo getImageInfo(final byte[] bytes, final Map<String, Object> params)
    355            throws ImageReadException, IOException {
    356        return getImageInfo(new ByteSourceArray(bytes), params);
    357    }
    358
    359    /**
    360     * Get image information from the specified file  Format-specific
    361     * ImageParser implementations are expected to return a valid
    362     * ImageInfo object or to throw an ImageReadException if unable
    363     * to process the specified data.
    364     * <p>The params argument provides a mechanism for individual
    365     * implementations to pass optional information into the parser.
    366     * Not all formats will require this capability.  Because the
    367     * base class may call this method with a null params argument,
    368     * implementations should <strong>always</strong> include logic
    369     * for ignoring null input.
    370     *
    371     * @param file   A valid File object
    372     * @param params Optional instructions for special-handling or
    373     *               interpretation of the input data (null objects are permitted and
    374     *               must be supported by implementations).
    375     * @return A valid image information object describing the content extracted
    376     *         from the specified data.
    377     * @throws ImageReadException In the event that the specified content
    378     *                            does not conform to the format of the specific
    379     *                            parser implementation.
    380     * @throws IOException        In the event of unsuccessful file read or
    381     *                            access operation.
    382     */
    383    public final ImageInfo getImageInfo(final File file, final Map<String, Object> params)
    384            throws ImageReadException, IOException {
    385        if (!canAcceptExtension(file)) {
    386            return null;
    387        }
    388
    389        return getImageInfo(new ByteSourceFile(file), params);
    390    }
    391
    392    /**
    393     * Determines the format compliance of the content of the supplied byte
    394     * source based on rules provided by a specific implementation.
    395     *
    396     * @param byteSource A valid instance of ByteSource
    397     * @return true if the content is format-compliant; otherwise, false
    398     * @throws ImageReadException may be thrown by sub-classes
    399     * @throws IOException        may be thrown by sub-classes
    400     */
    401    public FormatCompliance getFormatCompliance(final ByteSource byteSource)
    402            throws ImageReadException, IOException {
    403        return null;
    404    }
    405
    406    /**
    407     * Determines the format compliance of the content of the supplied byte
    408     * array based on rules provided by a specific implementation.
    409     *
    410     * @param bytes A valid byte array.
    411     * @return A valid FormatCompliance object.
    412     * @throws ImageReadException may be thrown by sub-classes
    413     * @throws IOException        may be thrown by sub-classes
    414     */
    415    public final FormatCompliance getFormatCompliance(final byte[] bytes)
    416            throws ImageReadException, IOException {
    417        return getFormatCompliance(new ByteSourceArray(bytes));
    418    }
    419
    420    /**
    421     * Determines the format compliance of the specified file based on
    422     * rules provided by a specific implementation.
    423     *
    424     * @param file A valid reference to a file.
    425     * @return A valid format compliance object.
    426     * @throws ImageReadException may be thrown by sub-classes
    427     * @throws IOException        may be thrown by sub-classes
    428     */
    429    public final FormatCompliance getFormatCompliance(final File file)
    430            throws ImageReadException, IOException {
    431        if (!canAcceptExtension(file)) {
    432            return null;
    433        }
    434
    435        return getFormatCompliance(new ByteSourceFile(file));
    436    }
    437
    438    /**
    439     * Gets all images specified by the byte source (some
    440     * formats may include multiple images within a single data source).
    441     *
    442     * @param byteSource A valid instance of ByteSource.
    443     * @return A valid (potentially empty) list of BufferedImage objects.
    444     * @throws ImageReadException In the event that the specified content
    445     *                            does not conform to the format of the specific
    446     *                            parser implementation.
    447     * @throws IOException        In the event of unsuccessful read or access operation.
    448     */
    449    public List<BufferedImage> getAllBufferedImages(final ByteSource byteSource)
    450            throws ImageReadException, IOException {
    451        final BufferedImage bi = getBufferedImage(byteSource, null);
    452
    453        final List<BufferedImage> result = new ArrayList<>();
    454
    455        // FIXME this doesn't look like we're actually getting all images contained in the given ByteSource...
    456        result.add(bi);
    457
    458        return result;
    459    }
    460
    461    /**
    462     * Gets all images specified by the byte array (some
    463     * formats may include multiple images within a single data source).
    464     *
    465     * @param bytes A valid byte array
    466     * @return A valid (potentially empty) list of BufferedImage objects.
    467     * @throws ImageReadException In the event that the specified content
    468     *                            does not conform to the format of the specific
    469     *                            parser implementation.
    470     * @throws IOException        In the event of unsuccessful read or access operation.
    471     */
    472    public final List<BufferedImage> getAllBufferedImages(final byte[] bytes)
    473            throws ImageReadException, IOException {
    474        return getAllBufferedImages(new ByteSourceArray(bytes));
    475    }
    476
    477    /**
    478     * Gets all images specified by indicated file (some
    479     * formats may include multiple images within a single data source).
    480     *
    481     * @param file A valid reference to a file.
    482     * @return A valid (potentially empty) list of BufferedImage objects.
    483     * @throws ImageReadException In the event that the specified content
    484     *                            does not conform to the format of the specific
    485     *                            parser implementation.
    486     * @throws IOException        In the event of unsuccessful read or access operation.
    487     */
    488    public final List<BufferedImage> getAllBufferedImages(final File file) throws ImageReadException, IOException {
    489        if (!canAcceptExtension(file)) {
    490            return null;
    491        }
    492
    493        return getAllBufferedImages(new ByteSourceFile(file));
    494    }
    495
    496    /**
    497     * Gets a buffered image specified by the byte source (for
    498     * sources that specify multiple images, choice of which image
    499     * is returned is implementation dependent).
    500     *
    501     * @param byteSource A valid instance of ByteSource
    502     * @param params     Optional instructions for special-handling or
    503     *                   interpretation of the input data (null objects are permitted and
    504     *                   must be supported by implementations).
    505     * @return A valid instance of BufferedImage.
    506     * @throws ImageReadException In the event that the specified content
    507     *                            does not conform to the format of the specific
    508     *                            parser implementation.
    509     * @throws IOException        In the event of unsuccessful read or access operation.
    510     */
    511    public abstract BufferedImage getBufferedImage(ByteSource byteSource, Map<String, Object> params)
    512            throws ImageReadException, IOException;
    513
    514    /**
    515     * Gets a buffered image specified by the byte array (for
    516     * sources that specify multiple images, choice of which image
    517     * is returned is implementation dependent).
    518     *
    519     * @param bytes  A valid byte array
    520     * @param params Optional instructions for special-handling or
    521     *               interpretation of the input data (null objects are permitted and
    522     *               must be supported by implementations).
    523     * @return A valid instance of BufferedImage.
    524     * @throws ImageReadException In the event that the specified content
    525     *                            does not conform to the format of the specific
    526     *                            parser implementation.
    527     * @throws IOException        In the event of unsuccessful read or access operation.
    528     */
    529    public final BufferedImage getBufferedImage(final byte[] bytes, final Map<String, Object> params)
    530            throws ImageReadException, IOException {
    531        return getBufferedImage(new ByteSourceArray(bytes), params);
    532    }
    533
    534    /**
    535     * Gets a buffered image specified by the indicated file  (for
    536     * sources that specify multiple images, choice of which image
    537     * is returned is implementation dependent).
    538     *
    539     * @param file   A valid file reference.
    540     * @param params Optional instructions for special-handling or
    541     *               interpretation of the input data (null objects are permitted and
    542     *               must be supported by implementations).
    543     * @return A valid instance of BufferedImage.
    544     * @throws ImageReadException In the event that the specified content
    545     *                            does not conform to the format of the specific
    546     *                            parser implementation.
    547     * @throws IOException        In the event of unsuccessful read or access operation.
    548     */
    549    public final BufferedImage getBufferedImage(final File file, final Map<String, Object> params)
    550            throws ImageReadException, IOException {
    551        if (!canAcceptExtension(file)) {
    552            return null;
    553        }
    554
    555        return getBufferedImage(new ByteSourceFile(file), params);
    556    }
    557
    558
    559    /**
    560     * Writes the content of a BufferedImage to the specified output
    561     * stream.
    562     *
    563     * <p>The params argument provides a mechanism for individual
    564     * implementations to pass optional information into the parser.
    565     * Not all formats will support this capability.  Currently,
    566     * some of the parsers do not check for null arguments. So in cases
    567     * where no optional specifications are supported, application
    568     * code should pass in an empty instance of an implementation of
    569     * the map interface (i.e. an empty HashMap).
    570     *
    571     * @param src    An image giving the source content for output
    572     * @param os     A valid output stream for storing the formatted image
    573     * @param params A non-null Map implementation supplying optional,
    574     *               format-specific instructions for output
    575     *               (such as selections for data compression, color models, etc.)
    576     * @throws ImageWriteException In the event that the output format
    577     *                             cannot handle the input image or invalid params are specified.
    578     * @throws IOException         In the event of an write error from
    579     *                             the output stream.
    580     */
    581    public void writeImage(final BufferedImage src, final OutputStream os, final Map<String, Object> params)
    582            throws ImageWriteException, IOException {
    583        os.close(); // we are obligated to close stream.
    584
    585        throw new ImageWriteException("This image format (" + getName()
    586                + ") cannot be written.");
    587    }
    588
    589    /**
    590     * Get the size of the image described by the specified byte array.
    591     *
    592     * @param bytes A valid byte array.
    593     * @return A valid instance of Dimension.
    594     * @throws ImageReadException In the event that the specified content
    595     *                            does not conform to the format of the specific
    596     *                            parser implementation.
    597     * @throws IOException        In the event of unsuccessful read or access operation.
    598     */
    599    public final Dimension getImageSize(final byte[] bytes) throws ImageReadException, IOException {
    600        return getImageSize(bytes, null);
    601    }
    602
    603    /**
    604     * Get the size of the image described by the specified byte array.
    605     *
    606     * @param bytes  A valid byte array.
    607     * @param params Optional instructions for special-handling or
    608     *               interpretation of the input data.
    609     * @return A valid instance of Dimension.
    610     * @throws ImageReadException In the event that the specified content
    611     *                            does not conform to the format of the specific
    612     *                            parser implementation.
    613     * @throws IOException        In the event of unsuccessful read or access operation.
    614     */
    615    public final Dimension getImageSize(final byte[] bytes, final Map<String, Object> params)
    616            throws ImageReadException, IOException {
    617        return getImageSize(new ByteSourceArray(bytes), params);
    618    }
    619
    620    /**
    621     * Get the size of the image described by the specified file.
    622     *
    623     * @param file A valid reference to a file.
    624     * @return A valid instance of Dimension.
    625     * @throws ImageReadException In the event that the specified content
    626     *                            does not conform to the format of the specific
    627     *                            parser implementation.
    628     * @throws IOException        In the event of unsuccessful read or access operation.
    629     */
    630    public final Dimension getImageSize(final File file) throws ImageReadException, IOException {
    631        return getImageSize(file, null);
    632    }
    633
    634    /**
    635     * Get the size of the image described by the specified file.
    636     *
    637     * @param file   A valid reference to a file.
    638     * @param params Optional instructions for special-handling or
    639     *               interpretation of the input data.
    640     * @return A valid instance of Dimension.
    641     * @throws ImageReadException In the event that the specified content
    642     *                            does not conform to the format of the specific
    643     *                            parser implementation.
    644     * @throws IOException        In the event of unsuccessful read or access operation.
    645     */
    646    public final Dimension getImageSize(final File file, final Map<String, Object> params)
    647            throws ImageReadException, IOException {
    648
    649        if (!canAcceptExtension(file)) {
    650            return null;
    651        }
    652
    653        return getImageSize(new ByteSourceFile(file), params);
    654    }
    655
    656    /**
    657     * Get the size of the image described by the specified ByteSource.
    658     *
    659     * @param byteSource A valid reference to a ByteSource.
    660     * @param params     Optional instructions for special-handling or
    661     *                   interpretation of the input data.
    662     * @return A valid instance of Dimension.
    663     * @throws ImageReadException In the event that the specified content
    664     *                            does not conform to the format of the specific
    665     *                            parser implementation.
    666     * @throws IOException        In the event of unsuccessful read or access operation.
    667     */
    668    public abstract Dimension getImageSize(ByteSource byteSource, Map<String, Object> params)
    669            throws ImageReadException, IOException;
    670
    671    /**
    672     * Get an array of bytes describing the International Color Consortium (ICC)
    673     * specification for the color space of the image contained in the
    674     * input byte array. Not all formats support ICC profiles.
    675     *
    676     * @param bytes A valid array of bytes.
    677     * @return If available, a valid array of bytes; otherwise, a null
    678     * @throws ImageReadException In the event that the specified content
    679     *                            does not conform to the format of the specific
    680     *                            parser implementation.
    681     * @throws IOException        In the event of unsuccessful read or access operation.
    682     */
    683    public final byte[] getICCProfileBytes(final byte[] bytes) throws ImageReadException, IOException {
    684        return getICCProfileBytes(bytes, null);
    685    }
    686
    687    /**
    688     * Get an array of bytes describing the International Color Consortium (ICC)
    689     * specification for the color space of the image contained in the
    690     * input byte array. Not all formats support ICC profiles.
    691     *
    692     * @param bytes  A valid array of bytes.
    693     * @param params Optional instructions for special-handling or
    694     *               interpretation of the input data.
    695     * @return If available, a valid array of bytes; otherwise, a null
    696     * @throws ImageReadException In the event that the specified content
    697     *                            does not conform to the format of the specific
    698     *                            parser implementation.
    699     * @throws IOException        In the event of unsuccessful read or access operation.
    700     */
    701    public final byte[] getICCProfileBytes(final byte[] bytes, final Map<String, Object> params)
    702            throws ImageReadException, IOException {
    703        return getICCProfileBytes(new ByteSourceArray(bytes), params);
    704    }
    705
    706    /**
    707     * Get an array of bytes describing the International Color Consortium (ICC)
    708     * specification for the color space of the image contained in the
    709     * input file. Not all formats support ICC profiles.
    710     *
    711     * @param file A valid file reference.
    712     * @return If available, a valid array of bytes; otherwise, a null
    713     * @throws ImageReadException In the event that the specified content
    714     *                            does not conform to the format of the specific
    715     *                            parser implementation.
    716     * @throws IOException        In the event of unsuccessful read or access operation.
    717     */
    718    public final byte[] getICCProfileBytes(final File file) throws ImageReadException, IOException {
    719        return getICCProfileBytes(file, null);
    720    }
    721
    722    /**
    723     * Get an array of bytes describing the International Color Consortium (ICC)
    724     * specification for the color space of the image contained in the
    725     * input file. Not all formats support ICC profiles.
    726     *
    727     * @param file   A valid file reference.
    728     * @param params Optional instructions for special-handling or
    729     *               interpretation of the input data.
    730     * @return If available, a valid array of bytes; otherwise, a null
    731     * @throws ImageReadException In the event that the specified content
    732     *                            does not conform to the format of the specific
    733     *                            parser implementation.
    734     * @throws IOException        In the event of unsuccessful read or access operation.
    735     */
    736    public final byte[] getICCProfileBytes(final File file, final Map<String, Object> params)
    737            throws ImageReadException, IOException {
    738        if (!canAcceptExtension(file)) {
    739            return null;
    740        }
    741
    742        if (LOGGER.isLoggable(Level.FINEST)) {
    743            LOGGER.finest(getName() + ": " + file.getName());
    744        }
    745
    746        return getICCProfileBytes(new ByteSourceFile(file), params);
    747    }
    748
    749    /**
    750     * Get an array of bytes describing the International Color Consortium (ICC)
    751     * specification for the color space of the image contained in the
    752     * input byteSource. Not all formats support ICC profiles.
    753     *
    754     * @param byteSource A valid ByteSource.
    755     * @param params     Optional instructions for special-handling or
    756     *                   interpretation of the input data.
    757     * @return If available, a valid array of bytes; otherwise, a null
    758     * @throws ImageReadException In the event that the specified content
    759     *                            does not conform to the format of the specific
    760     *                            parser implementation.
    761     * @throws IOException        In the event of unsuccessful read or access operation.
    762     */
    763    public abstract byte[] getICCProfileBytes(ByteSource byteSource, Map<String, Object> params)
    764            throws ImageReadException, IOException;
    765
    766    /**
    767     * Write the ImageInfo and format-specific information for the image
    768     * content of the specified byte array to a string.
    769     *
    770     * @param bytes A valid array of bytes.
    771     * @return A valid string.
    772     * @throws ImageReadException In the event that the specified content
    773     *                            does not conform to the format of the specific
    774     *                            parser implementation.
    775     * @throws IOException        In the event of unsuccessful read or access operation.
    776     */
    777    public final String dumpImageFile(final byte[] bytes) throws ImageReadException, IOException {
    778        return dumpImageFile(new ByteSourceArray(bytes));
    779    }
    780
    781
    782    /**
    783     * Write the ImageInfo and format-specific information for the image
    784     * content of the specified file to a string.
    785     *
    786     * @param file A valid file reference.
    787     * @return A valid string.
    788     * @throws ImageReadException In the event that the specified content
    789     *                            does not conform to the format of the specific
    790     *                            parser implementation.
    791     * @throws IOException        In the event of unsuccessful read or access operation.
    792     */
    793    public final String dumpImageFile(final File file) throws ImageReadException, IOException {
    794        if (!canAcceptExtension(file)) {
    795            return null;
    796        }
    797
    798        if (LOGGER.isLoggable(Level.FINEST)) {
    799            LOGGER.finest(getName() + ": " + file.getName());
    800        }
    801
    802        return dumpImageFile(new ByteSourceFile(file));
    803    }
    804
    805    /**
    806     * Write the ImageInfo and format-specific information for the image
    807     * content of the specified byte source to a string.
    808     *
    809     * @param byteSource A valid byte source.
    810     * @return A valid string.
    811     * @throws ImageReadException In the event that the specified content
    812     *                            does not conform to the format of the specific
    813     *                            parser implementation.
    814     * @throws IOException        In the event of unsuccessful read or access operation.
    815     */
    816    public final String dumpImageFile(final ByteSource byteSource)
    817            throws ImageReadException, IOException {
    818        final StringWriter sw = new StringWriter();
    819        final PrintWriter pw = new PrintWriter(sw);
    820
    821        dumpImageFile(pw, byteSource);
    822
    823        pw.flush();
    824
    825        return sw.toString();
    826    }
    827
    828    /**
    829     * Write the ImageInfo and format-specific information for the image
    830     * content of the specified byte source to a PrintWriter
    831     *
    832     * @param pw print writer used for writing the ImageInfo
    833     * @param byteSource A valid byte source.
    834     * @return A valid PrintWriter.
    835     * @throws ImageReadException In the event that the specified content
    836     *                            does not conform to the format of the specific
    837     *                            parser implementation.
    838     * @throws IOException        In the event of unsuccessful read or access operation.
    839     */
    840    public boolean dumpImageFile(final PrintWriter pw, final ByteSource byteSource)
    841            throws ImageReadException, IOException {
    842        return false;
    843    }
    844
    845
    846    /**
    847     * Get a descriptive name for the implementation of an ImageParser.
    848     *
    849     * @return a valid, subject-matter-specific string.
    850     */
    851    public abstract String getName();
    852
    853    /**
    854     * Get the default extension for the format specified by an implementation
    855     * of ImageParser.  Some parsers can support more than one extension
    856     * (i.e. .JPEG, .JPG;  .TIF, .TIFF, etc.).
    857     *
    858     * @return A valid string.
    859     */
    860    public abstract String getDefaultExtension();
    861
    862    /**
    863     * Get an array of all accepted extensions
    864     *
    865     * @return A valid array of one or more elements.
    866     */
    867    protected abstract String[] getAcceptedExtensions();
    868
    869    /**
    870     * Get an array of ImageFormat objects describing all accepted types
    871     *
    872     * @return A valid array of one or more elements.
    873     */
    874    protected abstract ImageFormat[] getAcceptedTypes();
    875
    876    /**
    877     * Indicates whether the ImageParser implementation can accept
    878     * the specified format
    879     *
    880     * @param type An instance of ImageFormat.
    881     * @return If the parser can accept the format, true; otherwise, false.
    882     */
    883    public boolean canAcceptType(final ImageFormat type) {
    884        final ImageFormat[] types = getAcceptedTypes();
    885
    886        for (final ImageFormat type2 : types) {
    887            if (type2.equals(type)) {
    888                return true;
    889            }
    890        }
    891        return false;
    892    }
    893
    894    /**
    895     * Indicates whether the ImageParser implementation can accept
    896     * the specified file based on its extension.
    897     *
    898     * @param file An valid file reference.
    899     * @return If the parser can accept the format, true; otherwise, false.
    900     */
    901    protected final boolean canAcceptExtension(final File file) {
    902        return canAcceptExtension(file.getName());
    903    }
    904
    905    /**
    906     * Indicates whether the ImageParser implementation can accept
    907     * the specified file name based on its extension.
    908     *
    909     * @param fileName A valid string giving a file name or file path.
    910     * @return If the parser can accept the format, true; otherwise, false.
    911     */
    912    protected final boolean canAcceptExtension(final String fileName) {
    913        final String[] exts = getAcceptedExtensions();
    914        if (exts == null) {
    915            return true;
    916        }
    917
    918        final int index = fileName.lastIndexOf('.');
    919        if (index >= 0) {
    920            String ext = fileName.substring(index);
    921            ext = ext.toLowerCase(Locale.ENGLISH);
    922
    923            for (final String ext2 : exts) {
    924                final String ext2Lower = ext2.toLowerCase(Locale.ENGLISH);
    925                if (ext2Lower.equals(ext)) {
    926                    return true;
    927                }
    928            }
    929        }
    930        return false;
    931    }
    932
    933    /**
    934     * Get an instance of IBufferedImageFactory based on the presence
    935     * of a specification for ImagingConstants.&#46;BUFFERED_IMAGE_FACTORY
    936     * within the supplied params.
    937     *
    938     * @param params A valid Map object, or a null.
    939     * @return A valid instance of an implementation of a IBufferedImageFactory.
    940     */
    941    protected BufferedImageFactory getBufferedImageFactory(final Map<String, Object> params) {
    942        if (params == null) {
    943            return new SimpleBufferedImageFactory();
    944        }
    945
    946        final BufferedImageFactory result = (BufferedImageFactory) params.get(
    947                ImagingConstants.BUFFERED_IMAGE_FACTORY);
    948
    949        if (null != result) {
    950            return result;
    951        }
    952
    953        return new SimpleBufferedImageFactory();
    954    }
    955
    956    /**
    957     * A utility method to search a params specification and determine
    958     * whether it contains the ImagingConstants&#46;PARAM_KEY_STRICT
    959     * specification. Intended
    960     * for internal use by ImageParser implementations.
    961     *
    962     * @param params A valid Map object (or a null).
    963     * @return If the params specify strict format compliance, true;
    964     *         otherwise, false.
    965     */
    966    public static boolean isStrict(final Map<String, Object> params) {
    967        if (params == null || !params.containsKey(ImagingConstants.PARAM_KEY_STRICT)) {
    968            return false;
    969        }
    970        return ((Boolean) params.get(ImagingConstants.PARAM_KEY_STRICT)).booleanValue();
    971    }
    972}