package de.dpa.oss.metadata.mapper.imaging;
import com.adobe.xmp.XMPException;
import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.io.ByteStreams;
import de.dpa.oss.metadata.mapper.imaging.backend.exiftool.ExifTool;
import de.dpa.oss.metadata.mapper.imaging.backend.exiftool.ExifToolIntegrationException;
import de.dpa.oss.metadata.mapper.imaging.backend.exiftool.ExifToolWrapper;
import de.dpa.oss.metadata.mapper.imaging.common.ImageMetadata;
import de.dpa.oss.metadata.mapper.imaging.xmp.metadata.XMPMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
/**
* @author oliver langer
*/
public class ChainedImageMetadataOperations
{
private static Logger logger = LoggerFactory.getLogger(ChainedImageMetadataOperations.class);
Path tmpFileDir = null;
final private InputStream inputImage;
final private OutputStream modifiedImage;
private ListMultimap<String, String> tagsToSet = null;
private ExifTool.CodedCharset iptcCodedCharset = null;
private TimeZone timeZone = TimeZone.getDefault();
private final ListMultimap<String, String> groupToSpecificLocationToRemove = ArrayListMultimap.create();
private boolean clearAllMetadataGroups = false;
public static ChainedImageMetadataOperations modifyImage(final InputStream inputImage, final OutputStream modifiedImage)
{
return new ChainedImageMetadataOperations(inputImage, modifiedImage);
}
private ChainedImageMetadataOperations(final InputStream inputImage, final OutputStream modifiedImage)
{
this.inputImage = inputImage;
this.modifiedImage = modifiedImage;
}
public ChainedImageMetadataOperations useTemporaryDirectory(final Path tmpFileDir)
{
this.tmpFileDir = tmpFileDir;
return this;
}
public ChainedImageMetadataOperations setMetadata(final ImageMetadata imageMetadata) throws XMPException
{
tagsToSet = ImageMetadataToExifToolTagInfoFactory
.createExifToolTagInfo(imageMetadata, timeZone);
if (imageMetadata.getIimCharset() != null)
{
switch (imageMetadata.getIimCharset())
{
case ISO_8859_1:
iptcCodedCharset = ExifTool.CodedCharset.LATIN1;
break;
default:
iptcCodedCharset = ExifTool.CodedCharset.UTF8;
}
}
return this;
}
@SuppressWarnings("UnusedDeclaration") public ChainedImageMetadataOperations useTimeZone(final TimeZone timeZone)
{
this.timeZone = timeZone;
return this;
}
/**
* The intention of this method is to avoid a mixture of metadata filled by a particular mapping and already existing metadata
* in the file. This method collects all tag groups(like e. g. IPTC:ALL, XMP:XMP-dc, ...) referred by the mapping.
* Then it removes of these groups from the file
*
* @param metadataMapping underlying metadata mapper
*/
public ChainedImageMetadataOperations clearMetadataGroupsReferredByMapping(final ImageMetadata metadataMapping)
{
if (metadataMapping.getIptcEntries().size() > 0)
{
groupToSpecificLocationToRemove.put("IPTC", "ALL");
}
Set<String> usedXMPNamespaces = new HashSet<>();
for (XMPMetadata xmpMetadata : metadataMapping.getXmpMetadata())
{
String namespace = xmpMetadata.getNamespace();
if (!Strings.isNullOrEmpty(namespace))
{
usedXMPNamespaces.add(namespace);
}
}
for (String usedXMPNamespace : usedXMPNamespaces)
{
groupToSpecificLocationToRemove.put("XMP", ConfigToExifToolTagNames.getExiftoolNamespaceRefForConfigNamspace(usedXMPNamespace));
}
return this;
}
public ChainedImageMetadataOperations clearAllMetadataGroups()
{
this.clearAllMetadataGroups = true;
return this;
}
public ChainedImageMetadataOperations clearMetadataGroups(final Map<String, String> tagGroupsToClear)
{
for (String group : tagGroupsToClear.keySet())
{
groupToSpecificLocationToRemove.put(group, tagGroupsToClear.get(group));
}
return this;
}
public void execute(final ExifToolWrapper exifTool) throws XMPException, ExifToolIntegrationException, IOException
{
Path tmpFilePath = null;
try
{ // copy input image to tempoary file
if (tmpFileDir != null)
{
tmpFilePath = Files.createTempFile(tmpFileDir, "metadata-mapper", "tmpimage");
}
else
{
tmpFilePath = Files.createTempFile("metadata-mapper", "tmpimage");
}
logger.debug("Using temporary file \"" + tmpFilePath + "\" for image processing.");
try (OutputStream fos = Files.newOutputStream(tmpFilePath))
{
ByteStreams.copy(inputImage, fos);
}
ExifTool exifToolOperationChainBuilder = ExifTool.modifyImage(tmpFilePath.toFile());
exifToolOperationChainBuilder.overwriteOriginalFile(true);
if (tagsToSet != null)
{
exifToolOperationChainBuilder.setImageMetadata(tagsToSet);
}
if (iptcCodedCharset != null)
{
exifToolOperationChainBuilder.useEncodingCharsetForIPTC(iptcCodedCharset);
}
if (clearAllMetadataGroups)
{
exifToolOperationChainBuilder.clearAllTagGroups();
}
else if (groupToSpecificLocationToRemove.size() > 0)
{
for (Map.Entry<String, String> groupAndSpecificLocation : groupToSpecificLocationToRemove.entries())
{
exifToolOperationChainBuilder.clearTagGroup(groupAndSpecificLocation.getKey(), groupAndSpecificLocation.getValue());
}
}
exifToolOperationChainBuilder.execute(exifTool);
try (InputStream inputStream = Files.newInputStream(tmpFilePath))
{
//Files.co
ByteStreams.copy(inputStream, modifiedImage);
}
}
finally
{
if (tmpFilePath != null)
{
Files.delete(tmpFilePath);
}
}
}
}