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}