DrewMediaMetadataExtractor.java
/*
* Copyright (C) 2000 - 2024 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.silverpeas.components.gallery.media;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Descriptor;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.iptc.IptcDirectory;
import org.silverpeas.components.gallery.model.MetaData;
import org.silverpeas.core.i18n.I18NHelper;
import org.silverpeas.core.util.ArrayUtil;
import org.silverpeas.core.util.Charsets;
import org.silverpeas.core.util.EncodingUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import static com.drew.metadata.iptc.IptcDirectory.*;
/**
* @author ehugonnet
*/
public class DrewMediaMetadataExtractor extends AbstractMediaMetadataExtractor {
public DrewMediaMetadataExtractor(String instanceId) {
init(instanceId);
}
@Override
public List<MetaData> extractImageExifMetaData(File image)
throws MediaMetadataException, IOException {
return extractImageExifMetaData(image, I18NHelper.DEFAULT_LANGUAGE);
}
@Override
public List<MetaData> extractImageExifMetaData(File image, String lang)
throws MediaMetadataException, IOException {
try {
List<MetaData> result = new ArrayList<>();
// lire le fichier des properties
// 1. Traitement des metadata EXIF
Metadata metadata = ImageMetadataReader.readMetadata(image);
ExifIFD0Directory exifDirectory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
ExifIFD0Descriptor descriptor = new ExifIFD0Descriptor(exifDirectory);
String value;
if (exifDirectory != null) {
for (ExifProperty property : imageProperties) {
value = fetchLabel(exifDirectory, descriptor, property);
if (value != null) {
addLabelToMediaMetadata(lang, result, value, property);
}
}
}
return result;
} catch (IOException | ImageProcessingException ex) {
throw new MediaMetadataException(ex);
}
}
private void addLabelToMediaMetadata(final String lang, final List<MetaData> result,
final String value, final ExifProperty property) {
// ajout de cette metadata à la photo
MetaData metaData = new MetaData(value);
metaData.setLabel(property.getLabel(lang));
metaData.setProperty(property.getProperty() + "");
result.add(metaData);
}
private String fetchLabel(final ExifIFD0Directory exifDirectory,
final ExifIFD0Descriptor descriptor, final ExifProperty property) {
// rechercher la valeur de la metadata "label"
final String value;
int currentMetadata = property.getProperty();
switch (currentMetadata) {
case ExifIFD0Directory.TAG_WIN_AUTHOR:
value = descriptor.getWindowsAuthorDescription();
break;
case ExifIFD0Directory.TAG_WIN_COMMENT:
value = descriptor.getWindowsCommentDescription();
break;
case ExifIFD0Directory.TAG_WIN_KEYWORDS:
value = descriptor.getWindowsKeywordsDescription();
break;
case ExifIFD0Directory.TAG_WIN_SUBJECT:
value = descriptor.getWindowsSubjectDescription();
break;
case ExifIFD0Directory.TAG_WIN_TITLE:
value = descriptor.getWindowsTitleDescription();
break;
case ExifIFD0Directory.TAG_RESOLUTION_UNIT:
value = getExifDirectoryValue(exifDirectory, currentMetadata);
break;
default:
value = exifDirectory.getString(currentMetadata);
}
return value;
}
private String getExifDirectoryValue(final ExifIFD0Directory exifDirectory,
final int currentMetadata) {
String value = exifDirectory.getString(currentMetadata);
if("2".equals(value)) {
//dots per inch
value = "DPI";
} else if ("3".equals(value)) {
//dots per cm
value = "DPC";
}
return value;
}
@Override
public List<MetaData> extractImageIptcMetaData(File image)
throws MediaMetadataException, IOException {
return extractImageIptcMetaData(image, I18NHelper.DEFAULT_LANGUAGE);
}
@Override
public List<MetaData> extractImageIptcMetaData(File image, String lang)
throws IOException, MediaMetadataException {
try {
final int defaultSize = 1024;
List<MetaData> result = new ArrayList<>();
Metadata metadata = ImageMetadataReader.readMetadata(image);
ByteArrayOutputStream forEncodingDetection = new ByteArrayOutputStream(defaultSize);
IptcDirectory iptcDirectory = metadata.getFirstDirectoryOfType(IptcDirectory.class);
String iptcCharset = getIptcCharset(iptcDirectory);
for (IptcProperty iptcProperty : imageIptcProperties) {
switch (iptcProperty.getProperty()) {
case TAG_RELEASE_DATE:
case TAG_DATE_CREATED:
case TAG_KEYWORDS:
addStringMetaData(result, iptcDirectory, iptcProperty, lang, forEncodingDetection);
break;
default:
addMetaData(result, iptcDirectory, iptcProperty, lang, forEncodingDetection);
break;
}
}
String defaultEncoding = Charsets.UTF_8.name();
if (iptcCharset != null) {
defaultEncoding = iptcCharset;
}
String encoding =
EncodingUtil.detectStringEncoding(forEncodingDetection.toByteArray(), defaultEncoding);
for (MetaData metaData : result) {
metaData.convert(encoding);
}
return result;
} catch (ImageProcessingException ex) {
throw new MediaMetadataException(ex);
}
}
private void addStringMetaData(List<MetaData> metadata, IptcDirectory iptcDirectory,
IptcProperty iptcProperty, String lang, ByteArrayOutputStream forEncodingDetection)
throws IOException {
String value = getIptcStringValue(iptcDirectory, iptcProperty.getProperty());
if (value != null) {
MetaData meta = new MetaData(value.getBytes());
meta.setLabel(iptcProperty.getLabel(lang));
meta.setProperty(String.valueOf(iptcProperty.getProperty()));
if (iptcProperty.isDate()) {
meta.setDate(true);
meta.setDateValue(iptcDirectory.getDate(iptcProperty.getProperty(), TimeZone.getDefault()));
} else {
forEncodingDetection.write(value.getBytes());
}
metadata.add(meta);
}
}
private void addMetaData(List<MetaData> metadata, IptcDirectory iptcDirectory,
IptcProperty iptcProperty, String lang, ByteArrayOutputStream forEncodingDetection)
throws IOException {
byte[] data = getIptcValue(iptcDirectory, iptcProperty.getProperty());
if (data != null && !ArrayUtil.isEmpty(data)) {
MetaData meta = new MetaData(data);
meta.setLabel(iptcProperty.getLabel(lang));
meta.setProperty(String.valueOf(iptcProperty.getProperty()));
if (iptcProperty.isDate() && iptcDirectory != null) {
meta.setDate(true);
meta.setDateValue(iptcDirectory.getDate(iptcProperty.getProperty(), TimeZone.getDefault()));
} else {
forEncodingDetection.write(data);
}
metadata.add(meta);
}
}
private byte[] getIptcValue(IptcDirectory iptcDirectory, int iptcTag) {
if (iptcDirectory != null && iptcDirectory.containsTag(iptcTag)) {
return iptcDirectory.getByteArray(iptcTag);
}
return new byte[0];
}
private String getIptcStringValue(IptcDirectory iptcDirectory, int iptcTag) {
if (iptcDirectory != null && iptcDirectory.containsTag(iptcTag)) {
return iptcDirectory.getString(iptcTag);
}
return null;
}
private String getIptcCharset(IptcDirectory iptcDirectory) {
if (iptcDirectory != null && iptcDirectory.containsTag(TAG_CODED_CHARACTER_SET)) {
byte[] data = iptcDirectory.getByteArray(TAG_CODED_CHARACTER_SET);
if (data != null) {
String escapeCode = new String(data, Charsets.UTF_8);
if ("%G".equals(escapeCode)) {
return Charsets.UTF_8.name();
}
}
}
return null;
}
}