com.moviejukebox.scanner.artwork.PosterScanner.java Source code

Java tutorial

Introduction

Here is the source code for com.moviejukebox.scanner.artwork.PosterScanner.java

Source

/*
 *      Copyright (c) 2004-2016 YAMJ Members
 *      https://github.com/orgs/YAMJ/people
 *
 *      This file is part of the Yet Another Movie Jukebox (YAMJ) project.
 *
 *      YAMJ is free software: you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation, either version 3 of the License, or
 *      any later version.
 *
 *      YAMJ is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with YAMJ.  If not, see <http://www.gnu.org/licenses/>.
 *
 *      Web: https://github.com/YAMJ/yamj-v2
 *
 */
package com.moviejukebox.scanner.artwork;

import java.awt.Dimension;
import java.awt.color.CMMException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.StringTokenizer;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.moviejukebox.model.IImage;
import com.moviejukebox.model.Image;
import com.moviejukebox.model.Jukebox;
import com.moviejukebox.model.Movie;
import com.moviejukebox.model.enumerations.DirtyFlag;
import com.moviejukebox.plugin.ImdbPlugin;
import com.moviejukebox.plugin.poster.IMoviePosterPlugin;
import com.moviejukebox.plugin.poster.IPosterPlugin;
import com.moviejukebox.plugin.poster.ITvShowPosterPlugin;
import com.moviejukebox.scanner.AttachmentScanner;
import com.moviejukebox.tools.FileTools;
import com.moviejukebox.tools.PropertiesUtil;
import com.moviejukebox.tools.StringTools;

/**
 * Scanner for poster files in local directory and from the Internet
 *
 * @author groll.troll
 * @author Stuart.Boston
 *
 * @version 1.0, 7 October 2008
 * @version 2.0 6 July 2009
 */
public final class PosterScanner {

    private static final Logger LOG = LoggerFactory.getLogger(PosterScanner.class);
    private static final Map<String, IPosterPlugin> PLUGINS;
    private static final Map<String, IMoviePosterPlugin> MOVIE_PLUGINS = new HashMap<>();
    private static final Map<String, ITvShowPosterPlugin> TV_PLUGINS = new HashMap<>();
    private static final String EXISTING_MOVIE = "moviename";
    private static final String EXISTING_FIXED = "fixedcoverartname";
    private static final String EXISTING_NO = "no";
    // We get covert art scanner behaviour
    private static final String SEARCH_FOR_EXISTING_POSTER = PropertiesUtil
            .getProperty("poster.scanner.searchForExistingCoverArt", EXISTING_MOVIE);
    // See if we use folder.* image or not
    // Note: We need the useFolderImage because of the special "folder.jpg" case in windows.
    private static final Boolean USE_FOLDER_IMAGE = PropertiesUtil
            .getBooleanProperty("poster.scanner.useFolderImage", Boolean.FALSE);
    // We get the fixed name property
    private static final String FIXED_POSTER_NAME = PropertiesUtil.getProperty("poster.scanner.fixedCoverArtName",
            "folder");
    private static final Collection<String> POSTER_EXTENSIONS = new ArrayList<>();
    private static final String POSTER_DIRECTORY;
    private static final Collection<String> POSTER_IMAGE_NAME = new ArrayList<>();
    private static final boolean POSTER_VALIDATE;
    private static final int POSTER_VALIDATE_MATCH;
    private static final boolean POSTER_VALIDATE_ASPECT;
    private static final int POSTER_WIDTH;
    private static final int POSTER_HEIGHT;
    private static final String TV_POSTER_SEARCH_PRIORITY;
    private static final String MOVIE_POSTER_SEARCH_PRIORITY;

    static {
        StringTokenizer st;

        if (USE_FOLDER_IMAGE) {
            st = new StringTokenizer(PropertiesUtil.getProperty("poster.scanner.imageName", "folder,poster"),
                    ",;|");
            while (st.hasMoreTokens()) {
                POSTER_IMAGE_NAME.add(st.nextToken());
            }
        }

        // We get valid extensions
        st = new StringTokenizer(PropertiesUtil.getProperty("poster.scanner.coverArtExtensions", "jpg,png,gif"),
                ",;| ");
        while (st.hasMoreTokens()) {
            POSTER_EXTENSIONS.add(st.nextToken());
        }

        // We get Poster Directory if needed
        POSTER_DIRECTORY = PropertiesUtil.getProperty("poster.scanner.coverArtDirectory", "");

        TV_POSTER_SEARCH_PRIORITY = PropertiesUtil.getProperty("poster.scanner.SearchPriority.tv",
                "thetvdb,cdon,filmaffinity");
        MOVIE_POSTER_SEARCH_PRIORITY = PropertiesUtil.getProperty("poster.scanner.SearchPriority.movie",
                "themoviedb,impawards,imdb,moviecovers,google,yahoo,motechnet");

        POSTER_WIDTH = PropertiesUtil.getIntProperty("posters.width", 0);
        POSTER_HEIGHT = PropertiesUtil.getIntProperty("posters.height", 0);
        POSTER_VALIDATE = PropertiesUtil.getBooleanProperty("poster.scanner.Validate", Boolean.TRUE);
        POSTER_VALIDATE_MATCH = PropertiesUtil.getIntProperty("poster.scanner.ValidateMatch", 75);
        POSTER_VALIDATE_ASPECT = PropertiesUtil.getBooleanProperty("poster.scanner.ValidateAspect", Boolean.TRUE);

        // Load plugins
        PLUGINS = new HashMap<>();

        ServiceLoader<IMoviePosterPlugin> moviePosterPluginsSet = ServiceLoader.load(IMoviePosterPlugin.class);
        for (IMoviePosterPlugin iPosterPlugin : moviePosterPluginsSet) {
            register(iPosterPlugin.getName().toLowerCase().trim(), iPosterPlugin);
        }

        ServiceLoader<ITvShowPosterPlugin> tvShowPosterPluginsSet = ServiceLoader.load(ITvShowPosterPlugin.class);
        for (ITvShowPosterPlugin iPosterPlugin : tvShowPosterPluginsSet) {
            register(iPosterPlugin.getName().toLowerCase().trim(), iPosterPlugin);
        }

    }

    private PosterScanner() {
        throw new UnsupportedOperationException("Class cannot be instantiated");
    }

    public static String scan(Jukebox jukebox, Movie movie) {
        if (SEARCH_FOR_EXISTING_POSTER.equalsIgnoreCase(EXISTING_NO)) {
            // nothing to do we return
            return Movie.UNKNOWN;
        }

        String localPosterBaseFilename;
        String parentPath = FileTools.getParentFolder(movie.getFile());
        String fullPosterFilename = parentPath;
        File localPosterFile;

        if (SEARCH_FOR_EXISTING_POSTER.equalsIgnoreCase(EXISTING_MOVIE)) {
            // Encode the basename to ensure that non-usable file system characters are replaced
            // Issue 1155 : YAMJ refuses to pickup fanart and poster for a movie -
            // Do not make safe file name before searching.
            localPosterBaseFilename = movie.getBaseFilename();
        } else if (SEARCH_FOR_EXISTING_POSTER.equalsIgnoreCase(EXISTING_FIXED)) {
            localPosterBaseFilename = FIXED_POSTER_NAME;
        } else {
            LOG.info("Wrong value for 'poster.scanner.searchForExistingCoverArt' property ('{}')!",
                    SEARCH_FOR_EXISTING_POSTER);
            LOG.info("Expected '{}' or '{}'", EXISTING_MOVIE, EXISTING_FIXED);
            return Movie.UNKNOWN;
        }

        if (StringUtils.isNotBlank(POSTER_DIRECTORY)) {
            fullPosterFilename = StringTools.appendToPath(fullPosterFilename, POSTER_DIRECTORY);
        }

        // Check to see if the fullPosterFilename ends with a "\/" and only add it if needed
        // Usually this occurs because the files are at the root of a folder
        fullPosterFilename = StringTools.appendToPath(fullPosterFilename, localPosterBaseFilename);
        localPosterFile = FileTools.findFileFromExtensions(fullPosterFilename, POSTER_EXTENSIONS);
        boolean foundLocalPoster = localPosterFile.exists();

        // Try searching the fileCache for the filename, but only for non-fixed filenames
        if (!foundLocalPoster && !SEARCH_FOR_EXISTING_POSTER.equalsIgnoreCase(EXISTING_FIXED)) {
            Boolean searchInJukebox = Boolean.TRUE;
            // if the poster URL is invalid, but the poster filename is valid, then this is likely a recheck, so don't search on the jukebox folder
            if (StringTools.isNotValidString(movie.getPosterURL())
                    && StringTools.isValidString(movie.getPosterFilename())) {
                searchInJukebox = Boolean.FALSE;
            }

            localPosterFile = FileTools.findFilenameInCache(localPosterBaseFilename, POSTER_EXTENSIONS, jukebox,
                    searchInJukebox);
            if (localPosterFile != null) {
                foundLocalPoster = Boolean.TRUE;
            }
        }

        /**
         * This part will look for a filename with the same name as the
         * directory for the poster or for folder.* poster The intention is for
         * you to be able to create the season / TV series art for the whole
         * series and not for the first show. Useful if you change the files
         * regularly.
         *
         * @author Stuart.Boston
         * @version 1.0
         * @date 18th October 2008
         */
        if (!foundLocalPoster) {
            // If no poster has been found, try the foldername
            // No need to check the poster directory
            localPosterBaseFilename = FileTools.getParentFolderName(movie.getFile());

            if (USE_FOLDER_IMAGE) {
                // Checking for MovieFolderName.* AND folder.*
                LOG.debug("Checking for '{}.*' posters AND {}.* posters", localPosterBaseFilename,
                        POSTER_IMAGE_NAME);
            } else {
                // Only checking for the MovieFolderName.* and not folder.*
                LOG.debug("Checking for '{}.*' posters", localPosterBaseFilename);
            }

            // Check for the directory name with extension for poster
            fullPosterFilename = StringTools.appendToPath(parentPath, localPosterBaseFilename);
            localPosterFile = FileTools.findFileFromExtensions(fullPosterFilename, POSTER_EXTENSIONS);
            foundLocalPoster = localPosterFile.exists();
            if (!foundLocalPoster && USE_FOLDER_IMAGE) {
                for (String imageFileName : POSTER_IMAGE_NAME) {
                    fullPosterFilename = StringTools.appendToPath(FileTools.getParentFolder(movie.getFile()),
                            imageFileName);
                    localPosterFile = FileTools.findFileFromExtensions(fullPosterFilename, POSTER_EXTENSIONS);
                    foundLocalPoster = localPosterFile.exists();

                    if (!foundLocalPoster && movie.isTVShow()) {
                        // Get the parent directory and check that
                        fullPosterFilename = StringTools.appendToPath(
                                FileTools.getParentFolder(movie.getFile().getParentFile().getParentFile()),
                                imageFileName);
                        localPosterFile = FileTools.findFileFromExtensions(fullPosterFilename, POSTER_EXTENSIONS);
                        foundLocalPoster = localPosterFile.exists();
                        if (foundLocalPoster) {
                            // We found the artwork so quit the loop
                            break;
                        }
                    } else {
                        // We found the artwork so quit the loop
                        break;
                    }
                }
            }
        }
        /*
         * END OF Folder Poster
         */

        // Check file attachments
        if (!foundLocalPoster) {
            localPosterFile = AttachmentScanner.extractAttachedPoster(movie);
            foundLocalPoster = (localPosterFile != null);
        }

        if (foundLocalPoster) {
            fullPosterFilename = localPosterFile.getAbsolutePath();
            Dimension imageSize = getFileImageSize(localPosterFile);
            LOG.debug("Local poster file {} found, size {} x {}", fullPosterFilename, imageSize.width,
                    imageSize.height);

            String safePosterFilename = movie.getPosterFilename();
            String finalJukeboxPosterFileName = StringTools.appendToPath(jukebox.getJukeboxRootLocationDetails(),
                    safePosterFilename);
            String tempJukeboxPosterFileName = StringTools.appendToPath(jukebox.getJukeboxTempLocationDetails(),
                    safePosterFilename);

            File finalJukeboxFile = FileTools.fileCache.getFile(finalJukeboxPosterFileName);
            File tempJukeboxFile = new File(tempJukeboxPosterFileName);

            FileTools.makeDirsForFile(finalJukeboxFile);
            FileTools.makeDirsForFile(tempJukeboxFile);

            boolean copyLocalPoster = Boolean.FALSE;

            //            logger.debug(LOG_MESSAGE + "finalJukeboxFile       : {}", finalJukeboxFile.getAbsolutePath());
            //            logger.debug(LOG_MESSAGE + "tempJukeboxFile        : {}", tempJukeboxFile.getAbsolutePath());
            //            logger.debug(LOG_MESSAGE + "finalJukeboxFile exists: {}", finalJukeboxFile.exists());
            //            logger.debug(LOG_MESSAGE + "Local newer than temp? : {}", (tempJukeboxFile.exists() && FileTools.isNewer(localPosterFile, tempJukeboxFile)));
            //            logger.debug(LOG_MESSAGE + "Posters same size?     : {}", (localPosterFile.length() != finalJukeboxFile.length()));
            //            logger.debug(LOG_MESSAGE + "Local newer than final?: {}", (FileTools.isNewer(localPosterFile, finalJukeboxFile)));
            if (!finalJukeboxFile.exists() || // temp jukebox file exists and is newer ?
                    (tempJukeboxFile.exists() && FileTools.isNewer(localPosterFile, tempJukeboxFile)) || // file size is different ?
                    (localPosterFile.length() != finalJukeboxFile.length()) || // local file is newer ?
                    (FileTools.isNewer(localPosterFile, finalJukeboxFile))) {
                // Force copy of local poster file
                copyLocalPoster = Boolean.TRUE;
            }

            if (copyLocalPoster) {
                FileTools.copyFile(localPosterFile, tempJukeboxFile);
                LOG.debug("'{}' has been copied to '{}'", fullPosterFilename, tempJukeboxPosterFileName);
            }
            // Update poster URL with local poster
            String posterURI = localPosterFile.toURI().toString();
            movie.setPosterURL(posterURI);

            return posterURI;
        }

        LOG.debug("No local poster found for {}", movie.getBaseFilename());
        return Movie.UNKNOWN;
    }

    private static String getPluginsCode() {
        StringBuilder response = new StringBuilder();

        Set<String> keySet = PLUGINS.keySet();
        for (String string : keySet) {
            response.append(string).append(Movie.SPACE_SLASH_SPACE);
        }
        return response.toString();
    }

    /**
     * Locate the PosterURL from the Internet. This is the main method and
     * should be called instead of the individual getPosterFrom* methods.
     *
     * @param movie The movieBean to search for
     * @return The posterImage with poster url that was found (Maybe
     * Image.UNKNOWN)
     */
    public static IImage getPosterURL(Movie movie) {
        String posterSearchToken;
        IImage posterImage = Image.UNKNOWN;
        StringTokenizer st;

        if (movie.isTVShow()) {
            st = new StringTokenizer(TV_POSTER_SEARCH_PRIORITY, ",");
        } else {
            st = new StringTokenizer(MOVIE_POSTER_SEARCH_PRIORITY, ",");
        }

        while (st.hasMoreTokens() && StringTools.isNotValidString(posterImage.getUrl())) {
            posterSearchToken = st.nextToken();

            IPosterPlugin iPosterPlugin = PLUGINS.get(posterSearchToken);

            // Check that plugin is register even on movie or tv
            if (iPosterPlugin == null) {
                LOG.error(
                        "'{}' plugin doesn't exist, please check your moviejukebox properties. Valid plugins are : {}",
                        posterSearchToken, getPluginsCode());
            }

            String msg;

            if (movie.isTVShow()) {
                iPosterPlugin = TV_PLUGINS.get(posterSearchToken);
                msg = "TvShow";
            } else {
                iPosterPlugin = MOVIE_PLUGINS.get(posterSearchToken);
                msg = "Movie";
            }

            if (iPosterPlugin == null) {
                LOG.info("{} is not a {} Poster plugin - skipping", posterSearchToken, msg);
            } else {
                LOG.debug("Using {} to search for a {} poster for {}", posterSearchToken, msg, movie.getTitle());
                posterImage = iPosterPlugin.getPosterUrl(movie, movie);
            }

            // Validate the poster- No need to validate if we're UNKNOWN
            if (!Movie.UNKNOWN.equalsIgnoreCase(posterImage.getUrl()) && POSTER_VALIDATE
                    && !validatePoster(posterImage, POSTER_WIDTH, POSTER_HEIGHT, POSTER_VALIDATE_ASPECT)) {
                posterImage = Image.UNKNOWN;
            } else {
                if (!Movie.UNKNOWN.equalsIgnoreCase(posterImage.getUrl())) {
                    LOG.debug("Poster URL found at {}: {}", posterSearchToken, posterImage.getUrl());
                    // TODO: This is a hack, but seeing as only one poster scanner uses it, it should be safe until it's all refactored to use the Artwork class
                    posterImage.setSubimage(posterSearchToken);
                    movie.setDirty(DirtyFlag.POSTER, Boolean.TRUE);
                }
            }
        }

        return posterImage;
    }

    /**
     * Validate the artwork against the dimensions and aspect.
     *
     * @param posterImage
     * @return
     */
    public static boolean validatePoster(IImage posterImage) {
        return validatePoster(posterImage, POSTER_WIDTH, POSTER_HEIGHT, POSTER_VALIDATE_ASPECT);
    }

    /**
     * Validate the poster against the provided dimensions and aspect
     *
     * Get the size of the file at the end of the URL Taken from:
     * http://forums.sun.com/thread.jspa?threadID=528155&messageID=2537096
     *
     * @param posterImage Poster image to check
     * @param posterWidth The width to check
     * @param posterHeight The height to check
     * @param checkAspect Should the aspect ratio be checked
     * @return True if the poster is good, false otherwise
     */
    public static boolean validatePoster(IImage posterImage, int posterWidth, int posterHeight,
            boolean checkAspect) {
        float urlAspect;
        if (!POSTER_VALIDATE) {
            return Boolean.TRUE;
        }

        if (StringTools.isNotValidString(posterImage.getUrl())) {
            return Boolean.FALSE;
        }

        Dimension imageDimension = getUrlDimensions(posterImage.getUrl());
        double urlWidth = imageDimension.getWidth();
        double urlHeight = imageDimension.getHeight();

        // Check if we need to cut the poster into a sub image
        if (StringTools.isValidString(posterImage.getSubimage())) {
            StringTokenizer st = new StringTokenizer(posterImage.getSubimage(), ", ");
            int x = Integer.parseInt(st.nextToken());
            int y = Integer.parseInt(st.nextToken());
            int l = Integer.parseInt(st.nextToken());
            int h = Integer.parseInt(st.nextToken());

            urlWidth = urlWidth * l / 100 - urlWidth * x / 100;
            urlHeight = urlHeight * h / 100 - urlHeight * y / 100;
        }

        urlAspect = (float) urlWidth / (float) urlHeight;

        if (checkAspect && urlAspect > 1.0) {
            LOG.debug("{} rejected: URL is landscape format", posterImage);
            return Boolean.FALSE;
        }

        // Adjust poster width / height by the ValidateMatch figure
        int newPosterWidth = (posterWidth * POSTER_VALIDATE_MATCH) / 100;
        int newPosterHeight = (posterHeight * POSTER_VALIDATE_MATCH) / 100;

        if (urlWidth < newPosterWidth) {
            LOG.debug("{} rejected: URL width ({}) is smaller than poster width ({})", posterImage, urlWidth,
                    newPosterWidth);
            return Boolean.FALSE;
        }

        if (urlHeight < newPosterHeight) {
            LOG.debug("{} rejected: URL height ({}) is smaller than poster height ({})", posterImage, urlHeight,
                    newPosterHeight);
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * Read an URL and get the dimensions of the image.
     *
     * This will try to determine the image type from the URL, if that fails
     * then it will default to JPEG.
     *
     * If the reading of the image fails, then the other type (PNG or JPEG) will
     * be used instead in case there was an incorrectly named extension
     *
     * @param imageUrl
     * @return
     */
    public static Dimension getUrlDimensions(String imageUrl) {
        String imageExtension = FilenameUtils.getExtension(imageUrl);
        if (StringUtils.isBlank(imageExtension)) {
            imageExtension = "jpeg";
        }

        Dimension imageDimension = getUrlDimensions(imageUrl, imageExtension);

        if (imageDimension.equals(new Dimension(0, 0))) {
            LOG.info("Looks like an invalid image, trying a different reader for URL: {}", imageUrl);
            if ("png".equals(imageExtension)) {
                imageExtension = "jpeg";
            } else {
                imageExtension = "png";
            }
            imageDimension = getUrlDimensions(imageUrl, imageExtension);
        }

        return imageDimension;
    }

    /**
     * Read an URL and get the dimensions of the image using a specific image
     * type
     *
     * @param imageUrl
     * @param imageType
     * @return
     */
    public static Dimension getUrlDimensions(String imageUrl, String imageType) {
        Dimension imageDimension = new Dimension(0, 0);

        @SuppressWarnings("rawtypes")
        Iterator readers = ImageIO.getImageReadersBySuffix(imageType);

        if (readers.hasNext()) {
            ImageReader reader = (ImageReader) readers.next();

            try (InputStream in = new URL(imageUrl).openStream();
                    ImageInputStream iis = ImageIO.createImageInputStream(in)) {
                reader.setInput(iis, Boolean.TRUE);
                imageDimension.setSize(reader.getWidth(0), reader.getHeight(0));
            } catch (IOException ex) {
                LOG.debug("getUrlDimensions error: {}: can't open url: {}", ex.getMessage(), imageUrl);
            } finally {
                reader.dispose();
            }
        }

        return imageDimension;
    }

    public static void register(String key, IPosterPlugin posterPlugin) {
        PLUGINS.put(key, posterPlugin);
    }

    private static void register(String key, IMoviePosterPlugin posterPlugin) {
        if (posterPlugin.isNeeded()) {
            LOG.debug("{} registered as Movie Poster Plugin with key '{}'", posterPlugin.getClass().getName(), key);
            MOVIE_PLUGINS.put(key, posterPlugin);
            register(key, (IPosterPlugin) posterPlugin);
        } else {
            LOG.debug("{} available, but not loaded use key '{}' to enable it.", posterPlugin.getClass().getName(),
                    key);
        }
    }

    public static void register(String key, ITvShowPosterPlugin posterPlugin) {
        if (posterPlugin.isNeeded()) {
            LOG.debug("{} registered as TvShow Poster Plugin with key '{}'", posterPlugin.getClass().getName(),
                    key);
            TV_PLUGINS.put(key, posterPlugin);
            register(key, (IPosterPlugin) posterPlugin);
        } else {
            LOG.debug("{} available, but not loaded use key '{}' to enable it.", posterPlugin.getClass().getName(),
                    key);
        }
    }

    public static void scan(Movie movie) {
        // check the default ID for a 0 or -1 and skip poster processing
        String id = movie.getId(ImdbPlugin.IMDB_PLUGIN_ID);
        if (!movie.isScrapeLibrary() || "0".equals(id) || "-1".equals(id)) {
            LOG.debug("Skipping online poster search for {}", movie.getBaseFilename());
            return;
        }

        LOG.debug("Searching online for {}", movie.getBaseFilename());
        IImage posterImage = getPosterURL(movie);
        if (StringTools.isValidString(posterImage.getUrl())) {
            movie.setPosterURL(posterImage.getUrl());
        }
    }

    /**
     * Return the dimensions of a local image file
     *
     * @param imageFile
     * @return Dimension
     */
    public static Dimension getFileImageSize(File imageFile) {
        Dimension imageSize = new Dimension(0, 0);

        ImageReader reader = null;

        try (ImageInputStream in = ImageIO.createImageInputStream(imageFile)) {
            @SuppressWarnings("rawtypes")
            Iterator readers = ImageIO.getImageReaders(in);
            if (readers.hasNext()) {
                reader = (ImageReader) readers.next();
                if (reader != null) {
                    reader.setInput(in);
                    return new Dimension(reader.getWidth(0), reader.getHeight(0));
                }
            }
        } catch (IOException | CMMException ex) {
            LOG.error("Failed to read image dimensions for {}", imageFile.getName());
            LOG.error("Error: {}", ex.getMessage());
            return imageSize;
        } finally {
            if (reader != null) {
                reader.dispose();
            }
        }

        return imageSize;
    }
}