Content of file ReadOp.java.html
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../jacoco-resources/report.gif" type="image/gif"/><title>ReadOp.java</title><link rel="stylesheet" href="../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../index.html" class="el_report">Mistral Core</a> > <a href="index.source.html" class="el_package">it.tidalwave.image.op</a> > <span class="el_source">ReadOp.java</span></div><h1>ReadOp.java</h1><pre class="source lang-java linenums">/*
* *********************************************************************************************************************
*
* Mistral: open source imaging engine
* http://tidalwave.it/projects/mistral
*
* Copyright (C) 2003 - 2023 by Tidalwave s.a.s. (http://tidalwave.it)
*
* *********************************************************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* *********************************************************************************************************************
*
* git clone https://bitbucket.org/tidalwave/mistral-src
* git clone https://github.com/tidalwave-it/mistral-src
*
* *********************************************************************************************************************
*/
package it.tidalwave.image.op;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import java.net.URL;
import java.awt.image.BufferedImage;
import it.tidalwave.util.Parameters;
import it.tidalwave.image.EditableImage;
import it.tidalwave.image.java2d.ImplementationFactoryJ2D;
import it.tidalwave.image.java2d.Java2DUtils;
import it.tidalwave.image.metadata.Directory;
import it.tidalwave.image.metadata.EXIF;
import it.tidalwave.image.metadata.IPTC;
import it.tidalwave.image.metadata.MakerNote;
import it.tidalwave.image.metadata.TIFF;
import it.tidalwave.image.metadata.XMP;
import it.tidalwave.image.metadata.loader.DirectoryLoader;
import it.tidalwave.image.metadata.loader.DrewMetadataLoader;
import it.tidalwave.image.metadata.loader.JpegDrewMetadataLoader;
import it.tidalwave.image.metadata.loader.MetadataLoader;
import it.tidalwave.image.metadata.loader.RAWMetadataLoader;
import it.tidalwave.image.metadata.loader.TIFFMetadataLoader;
import it.tidalwave.image.op.impl.FileChannelImageInputStream;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import static it.tidalwave.util.FunctionalCheckedExceptionWrappers.*;
/***********************************************************************************************************************
*
* @author Fabrizio Giudici
*
**********************************************************************************************************************/
<span class="pc" id="L82">@ToString(of = {"input", "imageIndex", "thumbnailIndex"}) @Slf4j</span>
public class ReadOp extends Operation
{
/*******************************************************************************************************************
*
* A marker interface for allowable options for {@link ReadOp} constructor.
*
******************************************************************************************************************/
public static interface Options
{
}
/*******************************************************************************************************************
*
* A container of plugin names that should not be used to load an image.
*
******************************************************************************************************************/
<span class="fc" id="L99"> @ToString</span>
public static class PluginBlackList implements Options
{
<span class="fc" id="L102"> public static final PluginBlackList DEFAULT = new PluginBlackList(</span>
// WRONG! These are the good ones! But keep for compability until you test everything.
"com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageReader",
"com.sun.media.imageioimpl.plugins.jpeg.CLibJPEGImageWriter",
//
// Considered harmful on Mac OS X. For instance, can only deal with Mac OS X endianness.
//
"com.sun.imageio.plugins.tiff.TIFFImageReader"
);
private final Set<String> plugins;
public PluginBlackList (@Nonnull final String... plugins)
<span class="fc" id="L115"> {</span>
<span class="fc" id="L116"> this.plugins = new HashSet<>(Arrays.asList(plugins));</span>
<span class="fc" id="L117"> }</span>
public boolean contains (@Nonnull final String pluginName)
{
<span class="fc" id="L121"> return plugins.contains(pluginName);</span>
}
}
<span class="fc" id="L125"> @Getter @Nonnull</span>
private final Object input;
<span class="fc" id="L128"> @Getter @Nonnull</span>
private final PluginBlackList pluginBlackList;
<span class="nc" id="L131"> @Getter @Nonnull</span>
private final Type type;
<span class="fc" id="L134"> @Getter @Nonnegative</span>
private final int imageIndex;
<span class="nc" id="L137"> @Getter @Nonnegative</span>
private final int thumbnailIndex;
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
private abstract static class Reader
{
public static EditableImage read (@Nonnull final Object input,
@Nonnull final Reader reader,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="fc" id="L151"> return reader.run(input, pluginBlackList);</span>
}
@Nonnull
private EditableImage run (@Nonnull final Object input,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="fc" id="L159"> Objects.requireNonNull(input, "input");</span>
<span class="pc bpc" id="L161" title="1 of 2 branches missed."> if (input instanceof Path)</span>
{
<span class="fc" id="L163"> final var imageReader = createImageReader(((Path)input), pluginBlackList);</span>
<span class="fc" id="L164"> final var editableImage = read(imageReader);</span>
<span class="fc" id="L165"> setProperties(editableImage, imageReader);</span>
<span class="fc" id="L166"> imageReader.dispose();</span>
<span class="fc" id="L167"> return editableImage;</span>
}
<span class="nc bnc" id="L170" title="All 2 branches missed."> else if (input instanceof File)</span>
{
<span class="nc" id="L172"> final var imageReader = createImageReader(((File)input).toPath(), pluginBlackList);</span>
<span class="nc" id="L173"> final var editableImage = read(imageReader);</span>
<span class="nc" id="L174"> setProperties(editableImage, imageReader);</span>
<span class="nc" id="L175"> imageReader.dispose();</span>
<span class="nc" id="L176"> return editableImage;</span>
}
<span class="nc bnc" id="L179" title="All 2 branches missed."> else if (input instanceof URL)</span>
{
<span class="nc" id="L181"> final var imageReader = createImageReader((URL)input, pluginBlackList);</span>
<span class="nc" id="L182"> final var editableImage = read(imageReader);</span>
<span class="nc" id="L183"> setProperties(editableImage, imageReader);</span>
<span class="nc" id="L184"> imageReader.dispose();</span>
<span class="nc" id="L185"> return editableImage;</span>
}
<span class="nc bnc" id="L188" title="All 2 branches missed."> else if (input instanceof InputStream)</span>
{
<span class="nc" id="L190"> final var imageReader = createImageReader((InputStream)input, pluginBlackList);</span>
<span class="nc" id="L191"> final var editableImage = read(imageReader);</span>
<span class="nc" id="L192"> setProperties(editableImage, imageReader);</span>
<span class="nc" id="L193"> imageReader.dispose();</span>
<span class="nc" id="L194"> return editableImage;</span>
}
<span class="nc bnc" id="L197" title="All 2 branches missed."> else if (input instanceof byte[])</span>
{
<span class="nc" id="L199"> final var imageReader = createImageReader(new ByteArrayInputStream((byte[])input), pluginBlackList);</span>
<span class="nc" id="L200"> final var editableImage = read(imageReader);</span>
<span class="nc" id="L201"> setProperties(editableImage, imageReader);</span>
<span class="nc" id="L202"> imageReader.dispose();</span>
<span class="nc" id="L203"> return editableImage;</span>
}
<span class="nc bnc" id="L206" title="All 2 branches missed."> else if (input instanceof ImageReader)</span>
{
<span class="nc" id="L208"> final var editableImage = read((ImageReader)input);</span>
<span class="nc" id="L209"> setProperties(editableImage, (ImageReader)input);</span>
<span class="nc" id="L210"> return editableImage;</span>
// don't dispose the ImageReader in this case
}
else
{
<span class="nc" id="L216"> throw new IllegalArgumentException("Bad input type: " + input.getClass());</span>
}
}
protected abstract EditableImage read (@Nonnull ImageReader imageReader)
throws IOException;
private void setProperties (@Nonnull final EditableImage image, @Nonnull final ImageReader imageReader)
throws IOException
{
<span class="fc" id="L226"> image.setAttribute(EditableImage.PROP_FORMAT, imageReader.getFormatName());</span>
<span class="fc" id="L227"> image.setAttribute(EditableImage.PROP_MIME_TYPE, imageReader.getOriginatingProvider().getMIMETypes()[0]);</span>
<span class="fc" id="L228"> }</span>
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
<span class="fc" id="L235"> public static enum Type implements Options</span>
{
/***************************************************************************************************************
*
*
**************************************************************************************************************/
<span class="fc" id="L241"> IMAGE</span>
{
@Override @Nonnull
protected EditableImage read (@Nonnull final ReadOp readOp)
throws IOException
{
<span class="fc" id="L247"> final var input = readOp.getInput();</span>
<span class="fc" id="L248"> final var imageIndex = readOp.getImageIndex();</span>
<span class="fc" id="L249"> log.info("read({}, {})", input, imageIndex);</span>
<span class="fc" id="L251"> return Reader.read(input, new Reader()</span>
<span class="fc" id="L252"> {</span>
@Override
protected EditableImage read (final ImageReader imageReader)
throws IOException
{
<span class="fc" id="L257"> final var time = Instant.now();</span>
<span class="fc" id="L258"> final var image = imageReader.read(imageIndex);</span>
<span class="fc" id="L259"> final var editableImage = create(image);</span>
<span class="fc" id="L260"> loadMetadata(editableImage, imageReader, imageIndex);</span>
<span class="fc" id="L261"> Java2DUtils.logImage(log, ">>>> Loaded image: ", image);</span>
<span class="fc" id="L262"> editableImage.getInnerProperty(AccessorOp.class).setLatestOperationDuration(Duration.between(Instant.now(), time));</span>
<span class="fc" id="L263"> return editableImage;</span>
}
<span class="fc" id="L265"> }, readOp.getPluginBlackList());</span>
}
},
/***************************************************************************************************************
*
*
**************************************************************************************************************/
<span class="fc" id="L273"> THUMBNAIL</span>
{
@Override @Nonnull
protected EditableImage read (@Nonnull final ReadOp readOp)
throws IOException
{
<span class="nc" id="L279"> final var input = readOp.getInput();</span>
<span class="nc" id="L280"> final var imageIndex = readOp.getImageIndex();</span>
<span class="nc" id="L281"> final var thumbnailIndex = readOp.getThumbnailIndex();</span>
<span class="nc" id="L282"> log.info("read({}, {}, {})", input, imageIndex, thumbnailIndex);</span>
<span class="nc" id="L284"> return Reader.read(input, new Reader()</span>
<span class="nc" id="L285"> {</span>
@Nonnull
@Override
protected EditableImage read (@Nonnull final ImageReader imageReader)
throws IOException
{
<span class="nc" id="L291"> final var time = Instant.now();</span>
<span class="nc" id="L292"> return create(imageReader.readThumbnail(imageIndex, thumbnailIndex),</span>
<span class="nc" id="L293"> Duration.between(Instant.now(), time));</span>
}
<span class="nc" id="L295"> }, readOp.getPluginBlackList());</span>
}
},
/***************************************************************************************************************
*
*
**************************************************************************************************************/
<span class="fc" id="L303"> METADATA</span>
{
@Override @Nonnull
protected EditableImage read (@Nonnull final ReadOp readOp)
throws IOException
{
<span class="fc" id="L309"> final var input = readOp.getInput();</span>
<span class="fc" id="L310"> final var imageIndex = readOp.getImageIndex();</span>
<span class="fc" id="L311"> log.info("read({}, {})", input, imageIndex);</span>
<span class="fc" id="L313"> return Reader.read(input, new Reader()</span>
<span class="fc" id="L314"> {</span>
@Nonnull
@Override
protected EditableImage read (@Nonnull final ImageReader imageReader)
{
<span class="fc" id="L319"> final var editableImage = new EditableImage(null);</span>
<span class="fc" id="L320"> loadMetadata(editableImage, imageReader, imageIndex);</span>
<span class="fc" id="L321"> return editableImage;</span>
}
<span class="fc" id="L323"> }, readOp.getPluginBlackList());</span>
}
};
/***************************************************************************************************************
*
*
**************************************************************************************************************/
@Nonnull
protected abstract EditableImage read (@Nonnull ReadOp readOp)
throws IOException;
/***************************************************************************************************************
*
*
**************************************************************************************************************/
@Nonnull
private static EditableImage create (@Nonnull final BufferedImage image)
{
<span class="fc" id="L342"> return new EditableImage(ImplementationFactoryJ2D.getDefault().createImageModel(image));</span>
}
/***************************************************************************************************************
*
*
**************************************************************************************************************/
@Nonnull
private static EditableImage create (@Nonnull final BufferedImage image,
@Nonnull final Duration latestOperationDuration)
{
<span class="nc" id="L353"> final var editableImage = create(image);</span>
<span class="nc" id="L354"> editableImage.getInnerProperty(AccessorOp.class).setLatestOperationDuration(latestOperationDuration);</span>
<span class="nc" id="L355"> return editableImage;</span>
}
}
/*******************************************************************************************************************
*
* @param input the input (an ImageReader or a Path)
*
******************************************************************************************************************/
public ReadOp (@Nonnull final Object input)
{
<span class="fc" id="L366"> this(input, 0, 0);</span>
<span class="fc" id="L367"> }</span>
/*******************************************************************************************************************
*
* @param input the input (an ImageReader or a Path)
* @param type the type of read
*
******************************************************************************************************************/
public ReadOp (@Nonnull final Object input, @Nonnull final Options... options)
{
<span class="fc" id="L377"> this(input, 0, 0, options);</span>
<span class="fc" id="L378"> }</span>
/*******************************************************************************************************************
*
* @param input the input (an ImageReader or a Path)
* @param type the type of read
* @param imageIndex the index of the image to read
*
******************************************************************************************************************/
public ReadOp (@Nonnull final Object input, @Nonnegative final int imageIndex, @Nonnull final Options... options)
{
<span class="nc" id="L389"> this(input, imageIndex, 0, options);</span>
<span class="nc" id="L390"> }</span>
/*******************************************************************************************************************
*
* @param input the input (an ImageReader or a Path)
* @param type the type of read
* @param imageIndex the index of the image to read
* @param thumbnailIndex the index of the thumbnail to read
*
******************************************************************************************************************/
public ReadOp (@Nonnull final Object input,
@Nonnegative final int imageIndex,
@Nonnegative final int thumbnailIndex,
@Nonnull final Options... options)
<span class="fc" id="L404"> {</span>
<span class="fc" id="L405"> this.input = input;</span>
<span class="fc" id="L406"> this.type = Parameters.find(Type.class, Type.IMAGE, options);</span>
<span class="fc" id="L407"> this.pluginBlackList = Parameters.find(PluginBlackList.class, PluginBlackList.DEFAULT, options);</span>
<span class="fc" id="L408"> this.imageIndex = imageIndex;</span>
<span class="fc" id="L409"> this.thumbnailIndex = thumbnailIndex;</span>
<span class="fc" id="L410"> log.trace("ReadOp({}, {}, {}, {})", input, imageIndex, thumbnailIndex, options);</span>
<span class="fc" id="L411"> }</span>
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
@Nonnull
public EditableImage execute()
throws IOException
{
<span class="fc" id="L421"> return type.read(this);</span>
}
/*******************************************************************************************************************
*
* Creates an ImageReader for the given Path. Using a Path as argument is
* important for photos that are stored in multiple files (e.g. Canon .CRW format).
* This method supports files GZIP compression (but multiple file formats such
* as .CRW aren't supported in this case).
*
* @throws IOException if it is not possible
*
******************************************************************************************************************/
@Nonnull
public static ImageReader createImageReader (@Nonnull final Path file,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="fc" id="L439"> log.trace("createImageReader({}, {})", file, pluginBlackList);</span>
<span class="fc bfc" id="L441" title="All 2 branches covered."> if (!Files.exists(file))</span>
{
<span class="fc" id="L443"> throw new FileNotFoundException(file.toAbsolutePath().toString());</span>
}
<span class="pc bpc" id="L446" title="1 of 2 branches missed."> if (!Files.isReadable(file))</span>
{
<span class="nc" id="L448"> throw new IOException("Cannot read " + file.toAbsolutePath());</span>
}
<span class="fc" id="L451"> var fileName = file.getFileName().toString();</span>
<span class="fc" id="L452"> var suffix = "";</span>
<span class="fc" id="L453"> final var gzipCompression = fileName.toLowerCase().endsWith(".gz");</span>
<span class="pc bpc" id="L455" title="1 of 2 branches missed."> if (gzipCompression)</span>
{
<span class="nc" id="L457"> fileName = fileName.substring(0, fileName.length() - 3);</span>
}
<span class="fc" id="L460"> final var i = fileName.lastIndexOf('.');</span>
<span class="pc bpc" id="L462" title="1 of 2 branches missed."> if (i > 0)</span>
{
<span class="fc" id="L464"> suffix = fileName.substring(i + 1);</span>
}
<span class="fc" id="L467"> log.trace(">>>> suffix is {}", suffix);</span>
<span class="fc" id="L468"> ImageInputStream imageInputStream = null;</span>
//
// For reasons stated in the javadoc comment of this method, it's better
// to create the ImageInputStream by passing a Path.
//
<span class="pc bpc" id="L474" title="1 of 2 branches missed."> if (!gzipCompression)</span>
{
<span class="fc" id="L476"> imageInputStream = new FileChannelImageInputStream(file.toFile());</span>
// imageInputStream = ImageIO.createImageInputStream(file);
}
//
// This will not work with multiple-file formats such as Canon .CRW.
//
else
{
<span class="nc" id="L484"> final InputStream inputStream = new GZIPInputStream(Files.newInputStream(file));</span>
<span class="nc" id="L485"> imageInputStream = ImageIO.createImageInputStream(inputStream);</span>
}
<span class="fc" id="L488"> return createImageReader(imageInputStream, gzipCompression, suffix, pluginBlackList);</span>
}
/*******************************************************************************************************************
*
* Returns a valid <code>ImageReader</code> for the given URL, or throw an
* <code>IOException</code> if it's not possible.
*
* @param url the URL
* @throws IOException if it is not possible
*
******************************************************************************************************************/
@Nonnull
public static ImageReader createImageReader (@Nonnull final URL url, @Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="nc" id="L504"> log.trace("createImageReader({})", url);</span>
<span class="nc" id="L506"> final var fileName = url.getPath();</span>
<span class="nc" id="L507"> var suffix = "";</span>
<span class="nc" id="L508"> final var gzipCompression = fileName.toLowerCase().endsWith(".gz");</span>
<span class="nc" id="L510"> final var i = fileName.lastIndexOf('.');</span>
<span class="nc bnc" id="L512" title="All 2 branches missed."> if (i > 0)</span>
{
<span class="nc" id="L514"> suffix = fileName.substring(i + 1);</span>
}
<span class="nc" id="L517"> log.trace(">>>> suffix is {}", suffix);</span>
<span class="nc" id="L518"> ImageInputStream imageInputStream = null;</span>
//
// This will not work with multiple-file formats such as Canon .CRW.
//
<span class="nc bnc" id="L522" title="All 2 branches missed."> final var inputStream = gzipCompression ? new GZIPInputStream(url.openStream()) : url.openStream();</span>
<span class="nc" id="L523"> imageInputStream = ImageIO.createImageInputStream(inputStream);</span>
<span class="nc" id="L524"> return createImageReader(imageInputStream, gzipCompression, suffix, pluginBlackList);</span>
}
/*******************************************************************************************************************
*
* Returns a valid <code>ImageReader</code> for the given stream, or throw
* an <code>IOException</code> if it's not possible.
*
* @param inputStream the input stream
* @throws IOException if it is not possible
*
******************************************************************************************************************/
@Nonnull
private static ImageReader createImageReader (@Nonnull final InputStream inputStream,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="nc" id="L541"> log.info("createImageReader({})", inputStream);</span>
<span class="nc" id="L542"> final var imageInputStream = ImageIO.createImageInputStream(inputStream);</span>
<span class="nc" id="L543"> final var iterator = ImageIO.getImageReaders(imageInputStream);</span>
<span class="nc" id="L544"> return createImageReader(imageInputStream, iterator, pluginBlackList);</span>
}
/*******************************************************************************************************************
*
* Returns a valid <code>ImageReader</code> for the given stream, or throw
* an <code>IOException</code> if it's not possible.
*
* @param imageInputStream the input stream
* @param gzipCompression if the stream is compressed
* @param suffix the file format suffix (e.g. jpg, tiff,...)
* @throws IOException if it is not possible
*
******************************************************************************************************************/
private static ImageReader createImageReader (@Nonnull final ImageInputStream imageInputStream,
final boolean gzipCompression,
@Nonnull final String suffix,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="fc" id="L564"> log.info("createImageReader({}, {}, {})", imageInputStream, gzipCompression, suffix);</span>
// logger.finest(">>>> Suffixes: " + Arrays.asList(ImageIO.getReaderFileSuffixes()));
<span class="fc" id="L566"> final var iterator = ImageIO.getImageReaders(imageInputStream);</span>
<span class="fc" id="L567"> return createImageReader(imageInputStream, iterator, pluginBlackList);</span>
}
/*******************************************************************************************************************
*
* Returns a valid <code>ImageReader</code> for the given stream, or throw
* an <code>IOException</code> if it's not possible.
*
* @param imageInputStream the input stream
* @throws IOException if it is not possible
*
******************************************************************************************************************/
@Nonnull
private static ImageReader createImageReader (@Nonnull final ImageInputStream imageInputStream,
@Nonnull final Iterator<? extends ImageReader> iterator,
@Nonnull final PluginBlackList pluginBlackList)
throws IOException
{
<span class="fc" id="L585"> log.info("createImageReader({}, {})", imageInputStream, iterator);</span>
// See http://bluemarine.tidalwave.it/issues/browse/MST-137
<span class="fc" id="L588"> final List<ImageReader> readers = new ArrayList<>();</span>
<span class="fc" id="L589"> final List<ImageReader> tiffReaders = new ArrayList<>();</span>
<span class="pc bpc" id="L591" title="1 of 2 branches missed."> if (!iterator.hasNext())</span>
{
<span class="nc" id="L593"> log.warn("Iterator is empty");</span>
}
<span class="fc bfc" id="L596" title="All 2 branches covered."> while (iterator.hasNext())</span>
{
<span class="fc" id="L598"> final var reader = iterator.next();</span>
<span class="fc" id="L599"> final var pluginClassName = reader.getOriginatingProvider().getPluginClassName();</span>
<span class="pc bpc" id="L601" title="1 of 2 branches missed."> if (reader != null)</span>
{
<span class="fc" id="L603"> log.trace(">>>> pre-testing reader: {}, vendor: {}",</span>
reader,
<span class="fc" id="L605"> reader.getOriginatingProvider().getVendorName());</span>
<span class="pc bpc" id="L607" title="1 of 2 branches missed."> if (pluginBlackList.contains(pluginClassName))</span>
{
<span class="nc" id="L609"> log.trace(">>>> {} discarded because it's in the black list", reader);</span>
}
<span class="pc bpc" id="L611" title="1 of 2 branches missed."> else if (!pluginClassName.contains("TIFF")) // TODO: maybe is it better to test for supported extension or mime?</span>
| maybe is it better to test for supported extension or mime? | |
{
<span class="fc" id="L613"> readers.add(reader);</span>
}
else
{
<span class="nc" id="L617"> tiffReaders.add(reader);</span>
}
}
<span class="fc" id="L620"> }</span>
<span class="fc" id="L622"> readers.addAll(tiffReaders);</span>
<span class="pc bpc" id="L624" title="1 of 2 branches missed."> for (final var reader : readers)</span>
{
<span class="fc" id="L626"> log.trace(">>>> testing reader: {}, vendor: {}", reader, reader.getOriginatingProvider().getVendorName());</span>
<span class="pc bpc" id="L628" title="1 of 2 branches missed."> if (!reader.getOriginatingProvider().canDecodeInput(imageInputStream))</span>
{
<span class="nc" id="L630"> log.trace(">>>> discarded because it can't decode the input");</span>
<span class="nc" id="L631"> continue;</span>
}
<span class="fc" id="L634"> reader.setInput(imageInputStream);</span>
<span class="fc" id="L635"> log.trace(">>>> returning reader: {}", reader);</span>
<span class="fc" id="L636"> return reader;</span>
}
<span class="nc" id="L639"> throw new IOException("No ImageReader");</span>
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
private static void loadMetadata (final @Nonnull EditableImage image,
final @Nonnull ImageReader reader,
final @Nonnegative int imageIndex)
{
<span class="fc" id="L650"> log.trace("loadMetadata({}, {})", reader, imageIndex);</span>
<span class="fc" id="L651"> var accessor = image.getInnerProperty(AccessorOp.class);</span>
<span class="fc" id="L652"> var metadataMapByClass = accessor.getMetadataMapByClass();</span>
final IIOMetadata iioMetadata;
try
{
<span class="fc" id="L657"> iioMetadata = reader.getImageMetadata(imageIndex);</span>
}
<span class="nc" id="L659"> catch (Exception e)</span>
{
<span class="nc" id="L661"> throw new RuntimeException(e);</span>
/*
if ("ICC APP2 encountered without prior JFIF!".equals(e.getMessage()) && (workaroundBM25 != null))
{
try
{
var tiff = new TIFF();
var exif = new EXIF();
var iptc = new IPTC();
var xmp = new XMP();
workaroundBM25.loadExifAndIptcFromJpeg(reader, tiff, exif, iptc, xmp);
metadataMapByClass.put(TIFF.class, List.of(tiff));
metadataMapByClass.put(EXIF.class, List.of(exif));
metadataMapByClass.put(IPTC.class, List.of(iptc));
metadataMapByClass.put(XMP.class, List.of(xmp));
}
catch (Exception e1)
{
log.error("Cannot load EXIF/IPTC metadata: ", e1);
}
}
else
{
log.error("Cannot load EXIF/IPTC metadata: ", e);
}
return;
*/
<span class="fc" id="L689"> }</span>
<span class="pc bpc" id="L691" title="1 of 2 branches missed."> if (iioMetadata == null)</span>
{
<span class="nc" id="L693"> log.warn(">>>> null imagemetadata");</span>
<span class="nc" id="L694"> return;</span>
}
<span class="fc" id="L697"> accessor.setIIOMetadata(iioMetadata);</span>
<span class="fc" id="L699"> var iioMetadataClass = iioMetadata.getClass();</span>
final MetadataLoader metadataLoader;
<span class="pc bpc" id="L702" title="1 of 2 branches missed."> if (isSubClass(iioMetadataClass, "com.sun.imageio.plugins.jpeg.JPEGMetadata"))</span>
{
<span class="fc" id="L704"> metadataLoader = new JpegDrewMetadataLoader(reader);</span>
}
<span class="nc bnc" id="L706" title="All 2 branches missed."> else if (isSubClass(iioMetadataClass, "com.sun.media.imageio.plugins.tiff.TIFFImageMetadata"))</span>
{
<span class="nc" id="L708"> metadataLoader = new TIFFMetadataLoader();</span>
}
<span class="nc bnc" id="L710" title="All 2 branches missed."> else if (isSubClass(iioMetadataClass, "it.tidalwave.imageio.raw.RAWMetadataSupport"))</span>
{
<span class="nc" id="L712"> metadataLoader = new RAWMetadataLoader();</span>
}
else
{
<span class="nc" id="L716"> metadataLoader = new DrewMetadataLoader();</span>
}
<span class="fc" id="L719"> log.debug(">>>> iioMetadata class: {}, using metadata loader: {}", iioMetadataClass, metadataLoader.getClass());</span>
try
{
<span class="fc" id="L723"> List.of(TIFF.class, EXIF.class, MakerNote.class, IPTC.class, XMP.class)</span>
<span class="fc" id="L724"> .forEach(_c(t -> metadataMapByClass.put(t, loadDirectories(iioMetadata, metadataLoader, t))));</span>
}
<span class="nc" id="L726"> catch (Exception e)</span>
{
<span class="nc" id="L728"> log.error("loadMetadata()", e);</span>
<span class="fc" id="L729"> }</span>
<span class="fc" id="L730"> }</span>
/*******************************************************************************************************************
*
* Loads directories of metadata by means of a loader.
*
* @param iioMetadata
* @param metadataLoader the metadata loader
* @param directoryClass the type of the directory to laod
* @return the loaded items
*
******************************************************************************************************************/
private static <T extends Directory> List<Directory> loadDirectories (final @Nonnull IIOMetadata iioMetadata,
final @Nonnull MetadataLoader metadataLoader,
final @Nonnull Class<T> directoryClass)
{
<span class="fc" id="L746"> log.debug("loadDirectories({}, {}, {})", iioMetadata, metadataLoader, directoryClass);</span>
<span class="fc" id="L747"> final var items = new ArrayList<Directory>();</span>
<span class="fc" id="L748"> Optional<DirectoryLoader> loader = Optional.empty();</span>
// FIXME: get rid of the if chain
<span class="fc bfc" id="L751" title="All 2 branches covered."> if (TIFF.class.equals(directoryClass))</span>
{
<span class="fc" id="L753"> loader = metadataLoader.getTiffLoader(iioMetadata);</span>
}
<span class="fc bfc" id="L755" title="All 2 branches covered."> else if (EXIF.class.equals(directoryClass))</span>
{
<span class="fc" id="L757"> loader = metadataLoader.getExifLoader(iioMetadata);</span>
}
<span class="fc bfc" id="L759" title="All 2 branches covered."> else if (IPTC.class.equals(directoryClass))</span>
{
<span class="fc" id="L761"> loader = metadataLoader.getIptcLoader(iioMetadata);</span>
}
<span class="fc bfc" id="L763" title="All 2 branches covered."> else if (XMP.class.equals(directoryClass))</span>
{
<span class="fc" id="L765"> loader = metadataLoader.getXmpLoader(iioMetadata);</span>
}
<span class="pc bpc" id="L767" title="1 of 2 branches missed."> else if (MakerNote.class.equals(directoryClass))</span>
{
<span class="fc" id="L769"> loader = metadataLoader.getMakerNoteLoader(iioMetadata);</span>
}
<span class="fc" id="L772"> loader.ifPresentOrElse(_c(a -> items.addAll(loadDirectories(a, directoryClass))),</span>
<span class="fc" id="L773"> () -> log.warn("No loader for {}", directoryClass));</span>
<span class="fc" id="L774"> return items;</span>
}
/*******************************************************************************************************************
*
******************************************************************************************************************/
private static List<Directory> loadDirectories (@Nonnull DirectoryLoader loader,
final @Nonnull Class<? extends Directory> itemClass)
throws InstantiationException, IllegalAccessException
{
<span class="fc" id="L784"> var result = new ArrayList<Directory>();</span>
<span class="fc" id="L786"> for (; ; loader = loader.next())</span>
{
<span class="fc" id="L788"> final var item = itemClass.newInstance();</span>
<span class="fc" id="L789"> item.load(loader);</span>
<span class="fc" id="L790"> result.add(item);</span>
<span class="fc bfc" id="L792" title="All 2 branches covered."> if (!loader.hasNext())</span>
{
<span class="fc" id="L794"> break;</span>
}
}
<span class="fc" id="L798"> return result;</span>
}
/*******************************************************************************************************************
*
*
******************************************************************************************************************/
private static boolean isSubClass (@Nonnull Class<?> aClass, final @Nonnull String ancestorClassName)
{
<span class="pc bpc" id="L807" title="1 of 2 branches missed."> for (; aClass != null; aClass = aClass.getSuperclass())</span>
{
<span class="pc bpc" id="L809" title="1 of 2 branches missed."> if (aClass.getName().equals(ancestorClassName))</span>
{
<span class="fc" id="L811"> return true;</span>
}
}
<span class="nc" id="L815"> return false;</span>
}
}
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html>