
com.day.cq.dam.commons.handler.StandardImageHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* Copyright 1997-2008 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.cq.dam.commons.handler;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.jcr.RepositoryException;
import com.adobe.xmp.core.XMPMetadata;
import com.adobe.xmp.core.serializer.RDFXMLSerializer;
import com.adobe.xmp.core.serializer.RDFXMLSerializerContext;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.handler.xmp.XMPHandler;
import com.day.cq.dam.api.metadata.ExtractedMetadata;
import com.day.cq.dam.commons.util.AssetCache;
import com.day.cq.dam.commons.util.DamUtil;
import com.day.image.Layer;
import org.apache.commons.imaging.FormatCompliance;
import org.apache.commons.imaging.ImageFormat;
import org.apache.commons.imaging.ImageFormats;
import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.ImageParser;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.GenericImageMetadata;
import org.apache.commons.imaging.common.ImageMetadata;
import org.apache.commons.imaging.common.RationalNumber;
import org.apache.commons.imaging.common.bytesource.ByteSource;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.jpeg.JpegPhotoshopMetadata;
import org.apache.commons.imaging.formats.tiff.TiffContents;
import org.apache.commons.imaging.formats.tiff.TiffDirectory;
import org.apache.commons.imaging.formats.tiff.TiffField;
import org.apache.commons.imaging.formats.tiff.TiffImageParser;
import org.apache.commons.imaging.formats.tiff.TiffReader;
import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The StandardImageHandler
supports currently following image types: - gif
- png
- photoshop
*
- jpeg
- tiff
- bmp
*/
@Component(inherit = true, metatype = true)
@Service
public class StandardImageHandler extends AbstractAssetHandler {
/**
* the default logger
*/
protected final Logger log = LoggerFactory.getLogger(StandardImageHandler.class);
public static final String CONFIG_LARGE_FILE_THRESHOLD = "large_file_threshold";
private static final long DEFAULT_LARGE_FILE_THRESHOLD = 0;
public static final String CONFIG_LARGE_COMMENT_THRESHOLD = "large_comment_threshold";
private static final long DEFAULT_LARGE_COMMENT_THRESHOLD = 5000;
public static final String METADATA_IGNORE_LIST = "metadata_ignore_list";
private static final String[] DEFAULT_METADATA_IGNORE_LIST = new String[]{"Image Description", "Artist", "Copyright"};
@Property(longValue = DEFAULT_LARGE_FILE_THRESHOLD, name = CONFIG_LARGE_FILE_THRESHOLD, label = "Threshold size to use intermediate temporary file", description = "Asset size greater than threshold use temporary file instead of memory buffer to avoid OutOfMemoryError. Value of -1 means that the use of temporary file is disabled. 0 means it is enabled for all sizes.")
private long largeFileThreshold = DEFAULT_LARGE_FILE_THRESHOLD;
@Property(longValue = DEFAULT_LARGE_COMMENT_THRESHOLD, name = CONFIG_LARGE_COMMENT_THRESHOLD, label = "Threshold size to skip comments being stored", description = "Comment size greater than threshold will not be stored. Value of -1 means that all comments would be stored irrespective of their size.")
private long largeCommentThreshold = DEFAULT_LARGE_COMMENT_THRESHOLD;
@Property(unbounded = PropertyUnbounded.ARRAY, propertyPrivate = true, value = {"Image Description", "Artist", "Copyright"}, name = "metadata_ignore_list", label = "List of metadata tags to be ignored from extraction")
private List metadataIgnoreList = Arrays.asList(DEFAULT_METADATA_IGNORE_LIST);
/**
* Mime type
*/
public static final String GIF_MIMETYPE = "image/gif";
public static final String PNG1_MIMETYPE = "image/png";
public static final String PNG2_MIMETYPE = "image/x-png";
public static final String JPEG_MIMETYPE = "image/jpeg";
public static final String PJPEG_MIMETYPE = "image/pjpeg";
public static final String TIFF_MIMETYPE = "image/tiff";
public static final String TIFF1_MIMETYPE = "image/x-tiff";
public static final String BMP1_MIMETYPE = "image/x-ms-bmp";
public static final String BMP2_MIMETYPE = "image/bmp";
public static final String RAW1_MIMETYPE = "image/x-raw-adobe";
public static final String RAW2_MIMETYPE = "image/x-raw-hasselblad";
public static final String RAW3_MIMETYPE = "image/x-raw-fuji";
public static final String RAW4_MIMETYPE = "image/x-raw-canon";
public static final String RAW5_MIMETYPE = "image/x-raw-kodak";
public static final String RAW6_MIMETYPE = "image/x-raw-minolta";
public static final String RAW7_MIMETYPE = "image/x-raw-nikon";
public static final String RAW8_MIMETYPE = "image/x-raw-olympus";
public static final String RAW9_MIMETYPE = "image/x-raw-pentax";
public static final String RAW10_MIMETYPE = "image/x-raw-sony";
public static final String RAW11_MIMETYPE = "image/x-raw-sigma";
public static final String RAW12_MIMETYPE = "image/x-raw-epson";
public static final String RAW13_MIMETYPE = "image/x-raw-mamiya";
public static final String RAW14_MIMETYPE = "image/x-raw-leaf";
public static final String RAW15_MIMETYPE = "image/x-raw-panasonic";
public static final String RAW16_MIMETYPE = "image/x-raw-phaseone";
public static final String RAW17_MIMETYPE = "image/x-raw-red";
public static final String RAW18_MIMETYPE = "image/x-raw-imacon";
public static final String RAW19_MIMETYPE = "image/x-raw-logitech";
public static final String RAW20_MIMETYPE = "image/x-raw-casio";
public static final String RAW21_MIMETYPE = "image/x-raw-rawzor";
public static final String RAW22_MIMETYPE = "image/x-canon-cr2";
public static final String RAW23_MIMETYPE = "image/x-nikon-nef";
public static final String DNG_MIMETYPE = "image/dng";
protected static final String[] MIME_TYPES = {GIF_MIMETYPE,
PNG1_MIMETYPE,
PNG2_MIMETYPE,
TIFF_MIMETYPE,
TIFF1_MIMETYPE,
BMP1_MIMETYPE,
BMP2_MIMETYPE,
RAW1_MIMETYPE,
RAW2_MIMETYPE,
RAW3_MIMETYPE,
RAW4_MIMETYPE,
RAW5_MIMETYPE,
RAW6_MIMETYPE,
RAW7_MIMETYPE,
RAW8_MIMETYPE,
RAW9_MIMETYPE,
RAW10_MIMETYPE,
RAW11_MIMETYPE,
RAW12_MIMETYPE,
RAW13_MIMETYPE,
RAW14_MIMETYPE,
RAW15_MIMETYPE,
RAW16_MIMETYPE,
RAW17_MIMETYPE,
RAW18_MIMETYPE,
RAW19_MIMETYPE,
RAW20_MIMETYPE,
RAW21_MIMETYPE,
RAW22_MIMETYPE,
RAW23_MIMETYPE,
DNG_MIMETYPE};
@Property(boolValue = false)
protected static final String ENABLE_BINARY_META_EXTRACTION = "cq.dam.enable.ext.meta.extraction";
protected boolean enableExtMetaExtraction = false;
@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL_UNARY)
protected XMPHandler xmpHandler;
protected synchronized void bindXmpHandler(final XMPHandler handler) {
xmpHandler = handler;
log.debug("binding xmp handler");
}
protected synchronized void unbindXmpHandler(final XMPHandler handler) {
xmpHandler = null;
log.debug("un-binding xmp handler");
}
/**
* @see com.day.cq.dam.api.handler.AssetHandler#getMimeTypes()
*/
public String[] getMimeTypes() {
return MIME_TYPES;
}
public ExtractedMetadata extractMetadata(final Asset asset) {
final ExtractedMetadata metadata = new ExtractedMetadata();
try {
extractMetadata(asset, metadata);
setMimetype(metadata, asset);
} catch (Exception e) {
if(log.isDebugEnabled())
log.debug("extractMetadata: error while extracting metadata for asset [{}]: ", asset.getPath(), e);
else
log.warn("extractMetadata: error while extracting metadata for asset [{}]: ", asset.getPath());
}
return metadata;
}
/**
* {@inheritDoc}
*/
public BufferedImage getImage(final Rendition rendition) throws IOException {
return getImage(rendition, null);
}
/**
* {@inheritDoc}
*/
public BufferedImage getImage(final Rendition rendition, Dimension maxDimension) throws IOException {
AssetCache cache = DamUtil.getAssetCache();
try {
if (isGif(rendition) || isPng(rendition) || isRaw(rendition)) {
return new Layer(cache.getStream(rendition, false), maxDimension).getImage();
} else {
Layer layer;
// workaround since the gfx lib sometimes is not able to resize tiff images (BufferedImage!!)
// todo: is this still needed? gfx 2.0.18 uses a different resize algorithm
InputStream iis = null;
File imageTmpFile = null;
File assetTempFile = null;
FileOutputStream fos = null;
try {
BufferedImage bufImage = null;
imageTmpFile = File.createTempFile("image", ".tmp");
long startTime = System.currentTimeMillis();
if (doFileBuffering(rendition)) {
bufImage = Imaging.getBufferedImage(cache.getFile(rendition));
ImageIO.write(bufImage, "png", imageTmpFile);
} else {
bufImage = Imaging.getBufferedImage(cache.getStream(rendition, false));
Imaging.writeImage(bufImage, imageTmpFile, ImageFormats.PNG, null);
}
long endTime = System.currentTimeMillis();
log.debug("time taken to convert to png = " + ((endTime - startTime)) + " ms");
iis = FileUtils.openInputStream(imageTmpFile);
return (new Layer(iis, maxDimension)).getImage();
} finally {
IOUtils.closeQuietly(iis);
FileUtils.deleteQuietly(imageTmpFile);
}
// end workaround
}
} catch (ImageReadException e) {
log.warn("getImage: error while reading image at path [{}]: ", rendition.getPath(), e);
} catch (ImageWriteException e) {
log.warn("getImage: error while writing image at path [{}]: ", rendition.getPath(), e);
} catch (IOException e) {
log.warn("getImage: error while reading image at path [{}]: ", rendition.getPath(), e);
} finally {
cache.release();
}
return null;
}
private boolean doFileBuffering(final Rendition r) {
return (largeFileThreshold != -1) && (largeFileThreshold <= r.getSize());
}
private boolean doAddComment(int commentSize) {
return (largeCommentThreshold == -1 || (largeCommentThreshold != -1 && largeCommentThreshold >= commentSize));
}
protected Map extractImageInfo(final Asset asset) {
AssetCache cache = DamUtil.getAssetCache();
try {
Rendition original = asset.getOriginal();
ByteSource bs = cache.getByteSource(original, doFileBuffering(original));
ImageParser imageParser = getImageParser(bs);
return extractImageInfo(asset.getPath(), bs, imageParser);
} catch (ImageReadException e) {
if(log.isDebugEnabled())
log.debug("extractImageInfo: error while reading metadata from image [{}]: ", asset.getPath(), e);
else
log.warn("extractImageInfo: error while reading metadata from image [{}]: ", asset.getPath());
} catch (IOException e) {
if(log.isDebugEnabled())
log.debug("extractImageInfo: error while reading info from image [{}]: ", asset.getPath(), e);
else
log.warn("extractImageInfo: error while reading info from image [{}]: ", asset.getPath());
} finally {
cache.release();
}
return new HashMap();
}
private Map extractImageInfo(final String path, ByteSource bsis, ImageParser ip) {
try {
// get Metadata
return extractImageInfo(path, ip.getImageInfo(bsis));
} catch (ImageReadException e) {
log.warn("extractImageInfo: error while reading metadata from image [{}]: ", path, e);
} catch (IOException e) {
log.warn("extractImageInfo: error while reading info from image [{}]: ", path, e);
}
return new HashMap();
}
private Map extractImageInfo(String path, ImageInfo imageInfo) {
final Map metadata = new HashMap();
/**
* CQ5-5795 Media Handler: BMP: dam:Numberofimages always -1 if
* number of images is -1 lets make it to 1.
*/
int numberOfImages = imageInfo.getNumberOfImages();
if (numberOfImages < 0) {
numberOfImages = 1;
}
metadata.put("File format", imageInfo.getFormat().getName());
metadata.put("MIME type", imageInfo.getMimeType());
metadata.put("Image Width", imageInfo.getWidth());
metadata.put("Image Length", imageInfo.getHeight());
metadata.put("Bits per pixel", imageInfo.getBitsPerPixel());
metadata.put("Progressive", imageInfo.isProgressive() ? "yes" : "no");
metadata.put("Number of images", numberOfImages);
metadata.put("Physical width in dpi", imageInfo.getPhysicalWidthDpi());
metadata.put("Physical height in dpi", imageInfo.getPhysicalHeightDpi());
metadata.put("Physical width in inches", (double) imageInfo.getPhysicalWidthInch());
metadata.put("Physical height in inches", (double) imageInfo.getPhysicalHeightInch());
final List comments = imageInfo.getComments();
int numComments = (comments == null ? 0 : comments.size());
int commentsStored = 0;
if (numComments > 0) {
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i < numComments; i++) {
if (comments != null) {
if (doAddComment(comments.get(i).length())) {
buffer.append(comments.get(i)).append('\n');
commentsStored++;
}
}
}
metadata.put("Comments", buffer.toString());
}
metadata.put("Number of textual comments", commentsStored);
return metadata;
}
protected void extractMetadata(Asset asset, ExtractedMetadata metadata) throws IOException, ImageReadException
{
AssetCache cache = DamUtil.getAssetCache();
ImageMetadata data = null;
try {
Rendition original = asset.getOriginal();
ByteSource bs = cache.getByteSource(original, doFileBuffering(original));
ImageParser imageParser = null;
try {
log.debug("extractMetadata, imageParser.getMetadata()");
imageParser = getImageParser(bs);
if (imageParser instanceof TiffImageParser) {
extractMetadataTiff(metadata, bs, asset.getPath(), imageParser);
}
else {
extractMetadataGeneric(metadata, bs, asset.getPath(), imageParser);
}
} catch (Exception e) {
log.warn("extractMetadata: cannot read metadata from image [{}]: ", asset.getPath(), e);
if (e instanceof ImageReadException) {
if (bs != null && imageParser != null) {
final Map imageDimensionData = getDimensionMetadata(asset.getPath(), bs, imageParser);
if (!imageDimensionData.isEmpty())
metadata.addMetadataProperties(imageDimensionData);
}
}
}
if (enableExtMetaExtraction) {
// get XMP from imaging
// jpeg can have XMP in the app1 segment, generic processor is not able to extract it
// as the generic processor only looks for xmppacket
// http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html
extractImageXMP(bs, metadata);
}
// if the image have xmppacket and then that will overwrite the above xmp, which should be fine
// Use this in any case since sanselan does not extract unicode properly
//todo move this to exception block once Sanselan.getMetadata is fixed
if (xmpHandler != null) { //it could be null on some platform like AIX
// Since NCommXMPHandler is faster, use that if available...
InputStream xmps = null;
try {
XMPMetadata xmpMetadata = xmpHandler.readXmpMetadata(asset);
if (xmpMetadata != null) {
byte[] xmpBytes = (new RDFXMLSerializer()).serializeToBuffer(
xmpMetadata, new RDFXMLSerializerContext());
xmps = new ByteArrayInputStream(xmpBytes);
metadata.setXmp(xmps);
}
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.error("Couldn't extract metadata using XMPhandler, attempting brute-force extraction", e);
} else {
log.warn("Couldn't extract metadata using XMPhandler, attempting brute-force extraction");
}
// fallback to 'brute-force'
execGenericProcessor(cache.getStream(original,doFileBuffering(original)), metadata);
} finally {
IOUtils.closeQuietly(xmps);
}
} else {
//... else fallback to 'brute-force'
execGenericProcessor(cache.getStream(original,doFileBuffering(original)), metadata);
}
if (metadata.getXmp() == null && !enableExtMetaExtraction) {
extractImageXMP(bs, metadata);
}
} catch (Exception e) {
log.warn("extractMetadata: cannot read metadata from image [{}]: ", asset.getPath(), e);
} finally {
cache.release();
log.debug("extractMetadata, done");
}
}
private void extractMetadataGeneric(ExtractedMetadata metadata, ByteSource bs, String path, ImageParser imageParser)
throws Exception {
/* bug 39584, for all image formats beside Tiff, imageInfo has priority over metadata. */
ImageMetadata data = imageParser.getMetadata(bs);
log.debug("extractMetadata, got ImageMetadata");
JpegImageMetadata jpegMetadata;
if ((data != null) && ((data instanceof JpegImageMetadata))) {
jpegMetadata = (JpegImageMetadata) data;
JpegPhotoshopMetadata jpegphotoshopMetadata = jpegMetadata.getPhotoshop();
// first extract photoshop segment if present, so that same value can be overridden if present in exif
// we give preference to exif/tiff
if (jpegphotoshopMetadata != null) {
for (ImageMetadata.ImageMetadataItem item : jpegphotoshopMetadata.getItems()) {
setPhotoshopItemValue(item, metadata);
}
}
// for tiff extraction sanselan now stores the tiff metadata under exif for jpeg
data = jpegMetadata.getExif();
}
log.debug("extractMetadata, extract ImageInfo");
final Map imageData = extractImageInfo(path, bs, imageParser);
metadata.addMetadataProperties(imageData);
}
private void extractMetadataTiff(ExtractedMetadata metadata, ByteSource bs, String path, ImageParser imageParser)
throws Exception {
TiffReader reader = new TiffReader(false);
TiffContents contents = reader.readContents(bs, null, FormatCompliance.getDefault());
/* bug 39584, in Tiff metadata has priority over imageInfo.*/
log.debug("extractMetadata, extract ImageInfo");
final Map imageData = extractImageInfo(path, getImageInfo(contents));
metadata.addMetadataProperties(imageData);
for (Iterator i = contents.directories.iterator(); i.hasNext(); /**/) {
TiffDirectory dir = (TiffDirectory)i.next();
for (Iterator j = dir.getDirectoryEntries().iterator(); j.hasNext(); /**/) {
TiffField tf = (TiffField)j.next();
if (tf.getDirectoryType() != TiffDirectoryConstants.DIRECTORY_TYPE_DIR_1) {//ignore IFD1 (thumbnail metadata)
String tagName = tf.getTagName();
FieldType fieldType = tf.getFieldType();
if ((enableExtMetaExtraction || !(fieldType.equals(FieldType.BYTE)
|| fieldType.equals(FieldType.UNDEFINED)))
&& !tagName.equals(TiffTagConstants.TIFF_TAG_XMP.name)
&& !metadataIgnoreList.contains(tagName)) {
log.debug("name =" + tagName + ", type=" + tf.getFieldTypeName());
metadata.setMetaDataProperty(tagName, trimValue(tf));
} else {
// skip, we scan for XMP at the end
log.debug("Skipping XMP tag [" + tagName + "], of type [" + fieldType.getName()
+ "] XMP is scanned by CQ generic XMP scanner", tagName);
}
}
}
}
log.debug("extractMetadata, got ImageMetadata");
}
private ImageInfo getImageInfo(TiffContents contents) throws ImageReadException, IOException {
TiffDirectory directory = (TiffDirectory)contents.directories.get(0);
TiffField widthField = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, true);
TiffField heightField = directory.findField(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, true);
if(widthField != null && heightField != null) {
int height = heightField.getIntValue();
int width = widthField.getIntValue();
TiffField resolutionUnitField = directory.findField(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT);
int resolutionUnit = 2;
if(resolutionUnitField != null && resolutionUnitField.getValue() != null) {
resolutionUnit = resolutionUnitField.getIntValue();
}
double unitsPerInch = -1.0D;
switch (resolutionUnit) {
case 1:
default:
break;
case 2:
unitsPerInch = 1.0D;
break;
case 3:
unitsPerInch = 2.54D;
}
TiffField xResolutionField = directory.findField(TiffTagConstants.TIFF_TAG_XRESOLUTION);
TiffField yResolutionField = directory.findField(TiffTagConstants.TIFF_TAG_YRESOLUTION);
int physicalWidthDpi = -1;
float physicalWidthInch = -1.0F;
int physicalHeightDpi = -1;
float physicalHeightInch = -1.0F;
if (unitsPerInch > 0.0D) {
double bitsPerSampleField;
if (xResolutionField != null && xResolutionField.getValue() != null) {
bitsPerSampleField = xResolutionField.getDoubleValue();
physicalWidthDpi = (int)Math.round(bitsPerSampleField * unitsPerInch);
physicalWidthInch = (float)((double)width / (bitsPerSampleField * unitsPerInch));
}
if (yResolutionField != null && yResolutionField.getValue() != null) {
bitsPerSampleField = yResolutionField.getDoubleValue();
physicalHeightDpi = (int)Math.round(bitsPerSampleField * unitsPerInch);
physicalHeightInch = (float)((double)height / (bitsPerSampleField * unitsPerInch));
}
}
TiffField var38 = directory.findField(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE);
int bitsPerSample = 1;
if(var38 != null && var38.getValue() != null) {
bitsPerSample = var38.getIntValueOrArraySum();
}
ArrayList comments = new ArrayList();
List entries = directory.entries;
String mimeType;
for(int format = 0; format < entries.size(); ++format) {
TiffField formatName = (TiffField)entries.get(format);
mimeType = formatName.toString();
comments.add(mimeType);
}
int numberOfImages = contents.directories.size();
String formatDetails = "Tiff v." + contents.header.tiffVersion;
boolean usesPalette = false;
TiffField colorMapField = directory.findField(TiffTagConstants.TIFF_TAG_COLOR_MAP);
if (colorMapField != null) {
usesPalette = true;
}
boolean colorType = true;
int compression = '\uffff' & directory.getSingleFieldValue(TiffTagConstants.TIFF_TAG_COMPRESSION);
ImageInfo.CompressionAlgorithm ca;
ImageInfo.ColorType ctype = ImageInfo.ColorType.UNKNOWN;
switch(compression) {
case 1:
ca = ImageInfo.CompressionAlgorithm.NONE;
break;
case 2:
ca = ImageInfo.CompressionAlgorithm.CCITT_1D;
break;
case 3:
ca = ImageInfo.CompressionAlgorithm.CCITT_GROUP_3;
break;
case 4:
ca = ImageInfo.CompressionAlgorithm.CCITT_GROUP_4;
break;
case 5:
ca = ImageInfo.CompressionAlgorithm.LZW;
break;
case 6:
ca = ImageInfo.CompressionAlgorithm.JPEG;
break;
case 32771:
ca = ImageInfo.CompressionAlgorithm.NONE;
break;
case 32773:
ca = ImageInfo.CompressionAlgorithm.PACKBITS;
break;
default:
ca = ImageInfo.CompressionAlgorithm.UNKNOWN;
}
ImageInfo result = new ImageInfo(formatDetails, bitsPerSample, comments, ImageFormats.TIFF,
"TIFF Tag-based Image File Format", height, "image/tiff", numberOfImages,
physicalHeightDpi, physicalHeightInch, physicalWidthDpi, physicalWidthInch, width,
false, false, usesPalette, ctype, ca);
return result;
} else {
throw new ImageReadException("TIFF image missing size info.");
}
}
/**
* CQ-59676, NPR-8278 : DAM does not extract width and height metadata if parsing is failed due to any reason
* Below is fallback attempt to at least get image size properties. Long term fix should come from apache commons imaging( https://issues.apache.org/jira/browse/IMAGING-100)
*/
private Map getDimensionMetadata(final String path, ByteSource bsis, ImageParser ip) {
final Map metadata = new HashMap();
try {
Dimension d = ip.getImageSize(bsis, null);
if (d != null) {
metadata.put("Image Width", (int) d.getWidth());
metadata.put("Image Length", (int) d.getHeight());
}
} catch (ImageReadException e) {
log.warn("getDimensionMetadata - imageParser.getImageSize() fallback attempt: error while reading metadata from image [{}]: ", path, e);
} catch (IOException e) {
log.warn("getDimensionMetadata - imageParser.getImageSize() fallback attempt: error while reading info from image [{}]: ", path, e);
}
return metadata;
}
private void extractImageXMP(ByteSource bs, ExtractedMetadata metadata) {
try {
String xmpStr = Imaging.getXmpXml(bs, null);
log.debug("extracted xmp string is {}", xmpStr);
if (xmpStr != null) {
metadata.setXmp(new ByteArrayInputStream(xmpStr.getBytes("utf-8")));
}
} catch (Exception e) {
if(log.isDebugEnabled())
log.debug("could not extract XMP from the image", e);
else
log.warn("could not extract XMP from the image");
// don't want to throw exception so as to keep backward compatible behavior
// any error here should not stop the processing
}
}
private void setPhotoshopItemValue(ImageMetadata.ImageMetadataItem item, ExtractedMetadata extractedMetadata) {
if ((item instanceof GenericImageMetadata.GenericImageMetadataItem)) {
GenericImageMetadata.GenericImageMetadataItem imageItem = (GenericImageMetadata.GenericImageMetadataItem)item;
String keyword = imageItem.getKeyword();
String text = imageItem.getText();
Object existingVal;
if ((existingVal = extractedMetadata.getMetaDataProperty(keyword)) != null) {
if (existingVal instanceof String) {
List newVal = new ArrayList();
newVal.add((String)existingVal);
newVal.add(text);
extractedMetadata.setMetaDataProperty(keyword, newVal);
} else if (existingVal instanceof List>) {
((List) existingVal).add(text);
}
} else {
extractedMetadata.setMetaDataProperty(keyword, text);
}
}
}
private ImageParser getImageParser(ByteSource byteSrc) throws ImageReadException, IOException {
final ImageFormat format = Imaging.guessFormat(byteSrc);
if (!format.equals(ImageFormats.UNKNOWN)) {
final ImageParser imageParsers[] = ImageParser.getAllImageParsers();
for (final ImageParser imageParser : imageParsers) {
if (imageParser.canAcceptType(format)) {
return imageParser;
}
}
}
throw new ImageReadException("Can't parse this format.");
}
/**
* Sanselan seems to add control chars at the end which needs to be removed
* This method removes any ctrl chars if the TIFF field type is String
*
* @param field, Tiff field which needs to be trimmed if String
* @return Trimmed string or the original field value in case the field is not a String
* @throws ImageReadException if unable to extract field value
*/
private Object trimValue(TiffField field) throws ImageReadException {
Object fieldValue = field.getValue();
if (fieldValue instanceof String) {
fieldValue = StringUtils.trimToEmpty(fieldValue.toString());
}
if (fieldValue instanceof RationalNumber) {
if(((RationalNumber)fieldValue).toString().indexOf(',') > 0)
fieldValue = ((RationalNumber) fieldValue).numerator/((RationalNumber) fieldValue).divisor;
}
return fieldValue;
}
private boolean isPng(final Rendition rendition) {
return rendition.getMimeType().startsWith(PNG1_MIMETYPE) || rendition.getMimeType().startsWith(PNG2_MIMETYPE);
}
private boolean isGif(final Rendition rendition) {
return rendition.getMimeType().startsWith(GIF_MIMETYPE);
}
private boolean isRaw(final Rendition rendition) {
return rendition.getMimeType().startsWith("image/x-raw-");
}
@Activate
@SuppressWarnings("unused")
protected void activate(final ComponentContext context) throws RepositoryException {
enableExtMetaExtraction = PropertiesUtil.toBoolean(context.getProperties().get(ENABLE_BINARY_META_EXTRACTION), false);
largeFileThreshold = PropertiesUtil.toLong(context.getProperties().get(CONFIG_LARGE_FILE_THRESHOLD), DEFAULT_LARGE_FILE_THRESHOLD);
largeCommentThreshold = PropertiesUtil.toLong(context.getProperties().get(CONFIG_LARGE_COMMENT_THRESHOLD), DEFAULT_LARGE_COMMENT_THRESHOLD);
metadataIgnoreList = Arrays.asList(PropertiesUtil.toStringArray(context.getProperties().get(METADATA_IGNORE_LIST), DEFAULT_METADATA_IGNORE_LIST));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy