Merge commit 'hamnis/master'

Conflicts:
	imageio/imageio-batik/pom.xml
	imageio/imageio-core/pom.xml
	imageio/imageio-ico/pom.xml
	imageio/imageio-iff/pom.xml
	imageio/imageio-metadata/pom.xml
	imageio/imageio-pict/pom.xml
	imageio/imageio-psd/pom.xml
	imageio/imageio-reference/pom.xml
	imageio/imageio-thumbsdb/pom.xml
	twelvemonkeys-core/pom.xml
	twelvemonkeys-imageio/pom.xml
	twelvemonkeys-servlet/pom.xml
This commit is contained in:
Harald Kuhr
2010-02-07 20:43:44 +01:00
576 changed files with 440 additions and 439 deletions

View File

@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio</artifactId>
<version>3.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>imageio-metadata</artifactId>
<name>TwelveMonkeys ImageIO Metadata</name>
<description>
ImageIO metadata module.
</description>
<dependencies>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-core</artifactId>
<classifier>tests</classifier>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* AbstractDirectory
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: AbstractDirectory.java,v 1.0 Nov 11, 2009 5:31:04 PM haraldk Exp$
*/
public abstract class AbstractDirectory implements Directory {
private final List<Entry> mEntries = new ArrayList<Entry>();
protected AbstractDirectory(final Collection<? extends Entry> pEntries) {
if (pEntries != null) {
mEntries.addAll(pEntries);
}
}
public Entry getEntryById(final Object pIdentifier) {
for (Entry entry : this) {
if (entry.getIdentifier().equals(pIdentifier)) {
return entry;
}
}
return null;
}
public Entry getEntryByFieldName(final String pFieldName) {
for (Entry entry : this) {
if (entry.getFieldName() != null && entry.getFieldName().equals(pFieldName)) {
return entry;
}
}
return null;
}
public Iterator<Entry> iterator() {
return mEntries.iterator();
}
/**
* Throws {@code UnsupportedOperationException} if this directory is read-only.
*
* @throws UnsupportedOperationException if this directory is read-only.
* @see #isReadOnly()
*/
protected final void assertMutable() {
if (isReadOnly()) {
throw new UnsupportedOperationException("Directory is read-only");
}
}
public boolean add(final Entry pEntry) {
assertMutable();
// TODO: Replace if entry is already present?
// Some directories may need special ordering, or may/may not support multiple entries for certain ids...
return mEntries.add(pEntry);
}
@SuppressWarnings({"SuspiciousMethodCalls"})
public boolean remove(final Object pEntry) {
assertMutable();
return mEntries.remove(pEntry);
}
public int size() {
return mEntries.size();
}
/**
* This implementation returns {@code true}.
* Subclasses should override this method, if the directory is mutable.
*
* @return {@code true}
*/
public boolean isReadOnly() {
return true;
}
/// Standard object support
@Override
public int hashCode() {
return mEntries.hashCode();
}
@Override
public boolean equals(final Object pOther) {
if (this == pOther) {
return true;
}
if (getClass() != pOther.getClass()) {
return false;
}
// Safe cast, as it must be a subclass for the classes to be equal
AbstractDirectory other = (AbstractDirectory) pOther;
return mEntries.equals(other.mEntries);
}
@Override
public String toString() {
return String.format("%s%s", getClass().getSimpleName(), mEntries.toString());
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata;
import com.twelvemonkeys.lang.Validate;
import java.lang.reflect.Array;
/**
* AbstractEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: AbstractEntry.java,v 1.0 Nov 12, 2009 12:43:13 AM haraldk Exp$
*/
public abstract class AbstractEntry implements Entry {
private final Object mIdentifier;
private final Object mValue; // TODO: Might need to be mutable..
protected AbstractEntry(final Object pIdentifier, final Object pValue) {
Validate.notNull(pIdentifier, "identifier");
mIdentifier = pIdentifier;
mValue = pValue;
}
public final Object getIdentifier() {
return mIdentifier;
}
/**
* Returns {@code null}, meaning unknown or undefined.
*
* @return {@code null}.
*/
public String getFieldName() {
return null;
}
public Object getValue() {
return mValue;
}
public String getValueAsString() {
return String.valueOf(mValue);
}
public String getTypeName() {
if (mValue == null) {
return null;
}
return mValue.getClass().getSimpleName();
}
public int valueCount() {
// TODO: Collection support?
if (mValue != null && mValue.getClass().isArray()) {
return Array.getLength(mValue);
}
return 1;
}
/// Object
@Override
public int hashCode() {
return mIdentifier.hashCode() + 31 * mValue.hashCode();
}
@Override
public boolean equals(final Object pOther) {
if (this == pOther) {
return true;
}
if (!(pOther instanceof AbstractEntry)) {
return false;
}
AbstractEntry other = (AbstractEntry) pOther;
return mIdentifier.equals(other.mIdentifier) && (
mValue == null && other.mValue == null || mValue != null && mValue.equals(other.mValue)
);
}
@Override
public String toString() {
String name = getFieldName();
String nameStr = name != null ? "/" + name + "" : "";
String type = getTypeName();
String typeStr = type != null ? " (" + type + ")" : "";
return String.format("%s%s: %s%s", getIdentifier(), nameStr, getValueAsString(), typeStr);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata;
/**
* Directory
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: Directory.java,v 1.0 Nov 11, 2009 4:20:58 PM haraldk Exp$
*/
public interface Directory extends Iterable<Entry> {
// TODO: Spec when more entries exist? Or make Entry support multi-values!?
// For multiple entries with same id in directory, the first entry (using the order from the stream) will be returned
Entry getEntryById(Object pIdentifier);
Entry getEntryByFieldName(String pName);
// Iterator containing the entries in
//Iterator<Entry> getBestEntries(Object pIdentifier, Object pQualifier, String pLanguage);
/// Collection-like API
// TODO: addOrReplaceIfPresent... (trouble for multi-values) Or mutable entries?
// boolean replace(Entry pEntry)??
// boolean contains(Object pIdentifier)?
boolean add(Entry pEntry);
boolean remove(Object pEntry); // Object in case we retro-fit Collection/Map..
int size();
boolean isReadOnly();
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata;
/**
* Entry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: Entry.java,v 1.0 Nov 11, 2009 4:21:08 PM haraldk Exp$
*/
public interface Entry {
// "tag" identifier from spec
Object getIdentifier();
// Human readable "tag" (field) name from sepc
String getFieldName();
// The internal "tag" value as stored in the stream, may be a Directory
Object getValue();
// Human readable "tag" value
String getValueAsString();
//void setValue(Object pValue); // TODO: qualifiers...
// Optional, implementation/spec specific type, describing the object returned from getValue
String getTypeName();
// TODO: Or something like getValue(qualifierType, qualifierValue) + getQualifiers()/getQualifierValues
// TODO: The problem with current model is getEntry() which only has single value support
// Optional, xml:lang-support
//String getLanguage();
// Optional, XMP alt-support. TODO: Do we need both?
//Object getQualifier();
// For arrays only
int valueCount();
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
/**
* MetadataReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: MetadataReader.java,v 1.0 Nov 13, 2009 8:38:11 PM haraldk Exp$
*/
public abstract class MetadataReader {
public abstract Directory read(ImageInputStream pInput) throws IOException;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.exif;
/**
* EXIF
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIF.java,v 1.0 Nov 11, 2009 5:36:04 PM haraldk Exp$
*/
public interface EXIF {
int TAG_COLOR_SPACE = 40961;
int TAG_PIXEL_X_DIMENSION = 40962;
int TAG_PIXEL_Y_DIMENSION = 40963;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.exif;
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
import com.twelvemonkeys.imageio.metadata.Entry;
import java.util.Collection;
/**
* EXIFDirectory
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFDirectory.java,v 1.0 Nov 11, 2009 5:02:59 PM haraldk Exp$
*/
final class EXIFDirectory extends AbstractDirectory {
EXIFDirectory(final Collection<? extends Entry> pEntries) {
super(pEntries);
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.exif;
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
/**
* EXIFEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFEntry.java,v 1.0 Nov 13, 2009 5:47:35 PM haraldk Exp$
*/
final class EXIFEntry extends AbstractEntry {
final private short mType;
EXIFEntry(final int pIdentifier, final Object pValue, final short pType) {
super(pIdentifier, pValue);
if (pType < 1 || pType > TIFF.TYPE_NAMES.length) {
throw new IllegalArgumentException(String.format("Illegal EXIF type: %s", pType));
}
mType = pType;
}
@Override
public String getFieldName() {
switch ((Integer) getIdentifier()) {
case TIFF.TAG_COMPRESSION:
return "Compression";
case TIFF.TAG_ORIENTATION:
return "Orientation";
case TIFF.TAG_X_RESOLUTION:
return "XResolution";
case TIFF.TAG_Y_RESOLUTION:
return "YResolution";
case TIFF.TAG_RESOLUTION_UNIT:
return "ResolutionUnit";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT:
return "JPEGInterchangeFormat";
case TIFF.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
return "JPEGInterchangeFormatLength";
case TIFF.TAG_SOFTWARE:
return "Software";
case TIFF.TAG_DATE_TIME:
return "DateTime";
case TIFF.TAG_ARTIST:
return "Artist";
case TIFF.TAG_COPYRIGHT:
return "Copyright";
case EXIF.TAG_COLOR_SPACE:
return "ColorSpace";
case EXIF.TAG_PIXEL_X_DIMENSION:
return "PixelXDimension";
case EXIF.TAG_PIXEL_Y_DIMENSION:
return "PixelYDimension";
// TODO: More field names
}
return null;
}
@Override
public String getTypeName() {
return TIFF.TYPE_NAMES[mType - 1];
}
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.exif;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* EXIFReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: EXIFReader.java,v 1.0 Nov 13, 2009 5:42:51 PM haraldk Exp$
*/
public final class EXIFReader extends MetadataReader {
@Override
public Directory read(final ImageInputStream pInput) throws IOException {
byte[] bom = new byte[2];
pInput.readFully(bom);
if (bom[0] == 'I' && bom[1] == 'I') {
pInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
}
else if (!(bom[0] == 'M' && bom[1] == 'M')) {
throw new IIOException(String.format("Invalid TIFF byte order mark '%s', expected: 'II' or 'MM'", StringUtil.decode(bom, 0, bom.length, "ASCII")));
}
int magic = pInput.readUnsignedShort();
if (magic != TIFF.TIFF_MAGIC) {
throw new IIOException(String.format("Wrong TIFF magic in EXIF data: %04x, expected: %04x", magic, TIFF.TIFF_MAGIC));
}
long directoryOffset = pInput.readUnsignedInt();
return readDirectory(pInput, directoryOffset);
}
private EXIFDirectory readDirectory(final ImageInputStream pInput, final long pOffset) throws IOException {
List<Entry> entries = new ArrayList<Entry>();
pInput.seek(pOffset);
int entryCount = pInput.readUnsignedShort();
for (int i = 0; i < entryCount; i++) {
entries.add(readEntry(pInput));
}
long nextOffset = pInput.readUnsignedInt();
if (nextOffset != 0) {
EXIFDirectory next = readDirectory(pInput, nextOffset);
for (Entry entry : next) {
entries.add(entry);
}
}
return new EXIFDirectory(entries);
}
private EXIFEntry readEntry(final ImageInputStream pInput) throws IOException {
int tagId = pInput.readUnsignedShort();
short type = pInput.readShort();
int count = pInput.readInt(); // Number of values
Object value;
if (tagId == TIFF.IFD_EXIF || tagId == TIFF.IFD_GPS || tagId == TIFF.IFD_INTEROP) {
// Parse sub IFDs
long offset = pInput.readUnsignedInt();
pInput.mark();
try {
value = readDirectory(pInput, offset);
}
finally {
pInput.reset();
}
}
else {
int valueLength = getValueLength(type, count);
if (valueLength > 0 && valueLength <= 4) {
value = readValueInLine(pInput, type, count);
pInput.skipBytes(4 - valueLength);
}
else {
long valueOffset = pInput.readUnsignedInt(); // This is the *value* iff the value size is <= 4 bytes
value = readValue(pInput, valueOffset, type, count);
}
}
return new EXIFEntry(tagId, value, type);
}
private Object readValue(final ImageInputStream pInput, final long pOffset, final short pType, final int pCount) throws IOException {
long pos = pInput.getStreamPosition();
try {
pInput.seek(pOffset);
return readValueInLine(pInput, pType, pCount);
}
finally {
pInput.seek(pos);
}
}
private Object readValueInLine(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
return readValueDirect(pInput, pType, pCount);
}
private static Object readValueDirect(final ImageInputStream pInput, final short pType, final int pCount) throws IOException {
switch (pType) {
case 2:
// TODO: This might be UTF-8 or ISO-8859-1, even though spec says ASCII
byte[] ascii = new byte[pCount];
pInput.readFully(ascii);
return StringUtil.decode(ascii, 0, ascii.length, "UTF-8"); // UTF-8 is ASCII compatible
case 1:
if (pCount == 1) {
return pInput.readUnsignedByte();
}
case 6:
if (pCount == 1) {
return pInput.readByte();
}
case 7:
byte[] bytes = new byte[pCount];
pInput.readFully(bytes);
return bytes;
case 3:
if (pCount == 1) {
return pInput.readUnsignedShort();
}
case 8:
if (pCount == 1) {
return pInput.readShort();
}
short[] shorts = new short[pCount];
pInput.readFully(shorts, 0, shorts.length);
return shorts;
case 4:
if (pCount == 1) {
return pInput.readUnsignedInt();
}
case 9:
if (pCount == 1) {
return pInput.readInt();
}
int[] ints = new int[pCount];
pInput.readFully(ints, 0, ints.length);
return ints;
case 11:
if (pCount == 1) {
return pInput.readFloat();
}
float[] floats = new float[pCount];
pInput.readFully(floats, 0, floats.length);
return floats;
case 12:
if (pCount == 1) {
return pInput.readDouble();
}
double[] doubles = new double[pCount];
pInput.readFully(doubles, 0, doubles.length);
return doubles;
case 5:
if (pCount == 1) {
return new Rational(pInput.readUnsignedInt(), pInput.readUnsignedInt());
}
Rational[] rationals = new Rational[pCount];
for (int i = 0; i < rationals.length; i++) {
rationals[i] = new Rational(pInput.readUnsignedInt(), pInput.readUnsignedInt());
}
return rationals;
case 10:
if (pCount == 1) {
return new Rational(pInput.readInt(), pInput.readInt());
}
Rational[] srationals = new Rational[pCount];
for (int i = 0; i < srationals.length; i++) {
srationals[i] = new Rational(pInput.readInt(), pInput.readInt());
}
return srationals;
default:
throw new IIOException(String.format("Unknown EXIF type '%s'", pType));
}
}
private int getValueLength(final int pType, final int pCount) {
if (pType > 0 && pType <= TIFF.TYPE_LENGTHS.length) {
return TIFF.TYPE_LENGTHS[pType - 1] * pCount;
}
return -1;
}
}

View File

@@ -0,0 +1,214 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Adapted from sample code featured in
* "Intro to Programming in Java: An Interdisciplinary Approach" (Addison Wesley)
* by Robert Sedgewick and Kevin Wayne. Permission granted to redistribute under BSD license.
*/
package com.twelvemonkeys.imageio.metadata.exif;
/**
* Represents a rational number with a {@code long} numerator and {@code long} denominator.
* Rational numbers are stored in reduced form with the sign stored with the numerator.
* Rationals are immutable.
* <p/>
* Adapted from sample code featured in
* <a href="http://www.cs.princeton.edu/introcs/home/">"Intro to Programming in Java: An Interdisciplinary Approach" (Addison Wesley)</a>
* by Robert Sedgewick and Kevin Wayne. Permission granted to redistribute under BSD license.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author <a href="http://www.cs.princeton.edu/introcs/92symbolic/Rational.java.html">Robert Sedgewick and Kevin Wayne (original version)</a>
* @author last modified by $Author: haraldk$
* @version $Id: Rational.java,v 1.0 Nov 18, 2009 1:12:00 AM haraldk Exp$
*/
public final class Rational extends Number implements Comparable<Rational> {
// TODO: Document public API
// TODO: Move to com.tm.lang?
// Inspired by http://www.cs.princeton.edu/introcs/92symbolic/Rational.java.html and java.lang.Integer
static final Rational ZERO = new Rational(0, 1);
private final long mNumerator;
private final long mDenominator;
public Rational(final long pNumber) {
this(pNumber, 1);
}
public Rational(final long pNumerator, final long pDenominator) {
if (pDenominator == 0) {
throw new IllegalArgumentException("denominator == 0");
}
if (pNumerator == Long.MIN_VALUE || pDenominator == Long.MIN_VALUE) {
throw new IllegalArgumentException("value == Long.MIN_VALUE");
}
// Reduce fractions
long gcd = gcd(pNumerator, pDenominator);
long num = pNumerator / gcd;
long den = pDenominator / gcd;
mNumerator = pDenominator >= 0 ? num : -num;
mDenominator = pDenominator >= 0 ? den : -den;
}
private static long gcd(final long m, final long n) {
if (m < 0) {
return gcd(n, -m);
}
return n == 0 ? m : gcd(n, m % n);
}
private static long lcm(final long m, final long n) {
if (m < 0) {
return lcm(n, -m);
}
return m * (n / gcd(m, n)); // parentheses important to avoid overflow
}
public long numerator() {
return mNumerator;
}
public long denominator() {
return mDenominator;
}
/// Number implementation
@Override
public int intValue() {
return (int) doubleValue();
}
@Override
public long longValue() {
return (long) doubleValue();
}
@Override
public float floatValue() {
return (float) doubleValue();
}
@Override
public double doubleValue() {
return mNumerator / (double) mDenominator;
}
/// Comparable implementation
public int compareTo(final Rational pOther) {
double thisVal = doubleValue();
double otherVal = pOther.doubleValue();
return thisVal < otherVal ? -1 : thisVal == otherVal ? 0 : 1;
}
/// Object overrides
@Override
public int hashCode() {
return Float.floatToIntBits(floatValue());
}
@Override
public boolean equals(final Object pOther) {
return pOther == this || pOther instanceof Rational && compareTo((Rational) pOther) == 0;
}
@Override
public String toString() {
return mDenominator == 1 ? Long.toString(mNumerator) : String.format("%s/%s", mNumerator, mDenominator);
}
/// Operations (adapted from http://www.cs.princeton.edu/introcs/92symbolic/Rational.java.html)
// TODO: Naming! multiply/divide/add/subtract or times/divides/plus/minus
// return a * b, staving off overflow as much as possible by cross-cancellation
public Rational times(final Rational pOther) {
// special cases
if (equals(ZERO) || pOther.equals(ZERO)) {
return ZERO;
}
// reduce p1/q2 and p2/q1, then multiply, where a = p1/q1 and b = p2/q2
Rational c = new Rational(mNumerator, pOther.mDenominator);
Rational d = new Rational(pOther.mNumerator, mDenominator);
return new Rational(c.mNumerator * d.mNumerator, c.mDenominator * d.mDenominator);
}
// return a + b, staving off overflow
public Rational plus(final Rational pOther) {
// special cases
if (equals(ZERO)) {
return pOther;
}
if (pOther.equals(ZERO)) {
return this;
}
// Find gcd of numerators and denominators
long f = gcd(mNumerator, pOther.mNumerator);
long g = gcd(mDenominator, pOther.mDenominator);
// add cross-product terms for numerator
// multiply back in
return new Rational(
((mNumerator / f) * (pOther.mDenominator / g) + (pOther.mNumerator / f) * (mDenominator / g)) * f,
lcm(mDenominator, pOther.mDenominator)
);
}
// return -a
public Rational negate() {
return new Rational(-mNumerator, mDenominator);
}
// return a - b
public Rational minus(final Rational pOther) {
return plus(pOther.negate());
}
public Rational reciprocal() {
return new Rational(mDenominator, mNumerator);
}
// return a / b
public Rational divides(final Rational pOther) {
if (pOther.equals(ZERO)) {
throw new ArithmeticException("/ by zero");
}
return times(pOther.reciprocal());
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.exif;
/**
* TIFF
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: TIFF.java,v 1.0 Nov 15, 2009 3:02:24 PM haraldk Exp$
*/
public interface TIFF {
int TIFF_MAGIC = 42;
/*
1 = BYTE 8-bit unsigned integer.
2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte
must be NUL (binary zero).
3 = SHORT 16-bit (2-byte) unsigned integer.
4 = LONG 32-bit (4-byte) unsigned integer.
5 = RATIONAL Two LONGs: the first represents the numerator of a
fraction; the second, the denominator.
TIFF 6.0 and above:
6 = SBYTE An 8-bit signed (twos-complement) integer.
7 = UNDEFINED An 8-bit byte that may contain anything, depending on
the definition of the field.
8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer.
10 = SRATIONAL Two SLONGs: the first represents the numerator of a
fraction, the second the denominator.
11 = FLOAT Single precision (4-byte) IEEE format.
12 = DOUBLE Double precision (8-byte) IEEE format.
*/
String[] TYPE_NAMES = {
"BYTE", "ASCII", "SHORT", "LONG", "RATIONAL",
"SBYTE", "UNDEFINED", "SSHORT", "SLONG", "SRATIONAL", "FLOAT", "DOUBLE",
};
int[] TYPE_LENGTHS = {
1, 1, 2, 4, 8,
1, 1, 2, 4, 8, 4, 8,
};
int IFD_EXIF = 0x8769;
int IFD_GPS = 0x8825;
int IFD_INTEROP = 0xA005;
/// A. Tags relating to image data structure:
int TAG_IMAGE_WIDTH = 256;
int TAG_IMAGE_HEIGHT = 257;
int TAG_BITS_PER_SAMPLE = 258;
int TAG_COMPRESSION = 259;
int TAG_PHOTOMETRIC_INTERPRETATION = 262;
int TAG_ORIENTATION = 274;
int TAG_SAMPLES_PER_PIXELS = 277;
int TAG_PLANAR_CONFIGURATION = 284;
int TAG_YCBCR_SUB_SAMPLING = 530;
int TAG_YCBCR_POSITIONING = 531;
int TAG_X_RESOLUTION = 282;
int TAG_Y_RESOLUTION = 283;
int TAG_RESOLUTION_UNIT = 296;
/// B. Tags relating to recording offset
int TAG_STRIP_OFFSETS = 273;
int TAG_ROWS_PER_STRIP = 278;
int TAG_STRIP_BYTE_COUNTS = 279;
int TAG_JPEG_INTERCHANGE_FORMAT = 513;
int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = 514;
/// C. Tags relating to image data characteristics
int TAG_TRANSFER_FUNCTION = 301;
int TAG_WHITE_POINT = 318;
int TAG_PRIMARY_CHROMATICITIES = 319;
int TAG_YCBCR_COEFFICIENTS = 529;
int TAG_REFERENCE_BLACK_WHITE = 532;
/// D. Other tags
int TAG_DATE_TIME = 306;
int TAG_IMAGE_DESCRIPTION = 270;
int TAG_MAKE = 271;
int TAG_MODEL = 272;
int TAG_SOFTWARE = 305;
int TAG_ARTIST = 315;
int TAG_COPYRIGHT = 33432;
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.iptc;
/**
* IPTC
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IPTC.java,v 1.0 Nov 11, 2009 6:20:21 PM haraldk Exp$
*/
public interface IPTC {
static final int ENVELOPE_RECORD = 1 << 8;
static final int APPLICATION_RECORD = 2 << 8;
static final int TAG_CODED_CHARACTER_SET = ENVELOPE_RECORD | 90;
/** 2:00 Record Version (mandatory) */
public static final int TAG_RECORD_VERSION = APPLICATION_RECORD; // 0x0200
/** 2:03 Object Type Reference */
public static final int TAG_OBJECT_TYPE_REFERENCE = APPLICATION_RECORD | 3;
/** 2:04 Object Attribute Reference (repeatable) */
public static final int TAG_OBJECT_ATTRIBUTE_REFERENCE = APPLICATION_RECORD | 4;
/** 2:05 Object Name */
public static final int TAG_OBJECT_NAME = APPLICATION_RECORD | 5; // 0x0205
/** 2:07 Edit Status */
public static final int TAG_EDIT_STATUS = APPLICATION_RECORD | 7;
/** 2:08 Editorial Update */
public static final int TAG_EDITORIAL_UPDATE = APPLICATION_RECORD | 8;
/** 2:10 Urgency */
public static final int TAG_URGENCY = APPLICATION_RECORD | 10;
/** 2:12 Subect Reference (repeatable) */
public static final int TAG_SUBJECT_REFERENCE = APPLICATION_RECORD | 12;
/** 2:15 Category */
public static final int TAG_CATEGORY = APPLICATION_RECORD | 15; // 0x020f
/** 2:20 Supplemental Category (repeatable) */
public static final int TAG_SUPPLEMENTAL_CATEGORIES = APPLICATION_RECORD | 20;
/** 2:22 Fixture Identifier */
public static final int TAG_FIXTURE_IDENTIFIER = APPLICATION_RECORD | 22;
/** 2:25 Keywords (repeatable) */
public static final int TAG_KEYWORDS = APPLICATION_RECORD | 25;
/** 2:26 Content Locataion Code (repeatable) */
public static final int TAG_CONTENT_LOCATION_CODE = APPLICATION_RECORD | 26;
/** 2:27 Content Locataion Name (repeatable) */
public static final int TAG_CONTENT_LOCATION_NAME = APPLICATION_RECORD | 27;
/** 2:30 Release Date */
public static final int TAG_RELEASE_DATE = APPLICATION_RECORD | 30;
/** 2:35 Release Time */
public static final int TAG_RELEASE_TIME = APPLICATION_RECORD | 35;
/** 2:37 Expiration Date */
public static final int TAG_EXPIRATION_DATE = APPLICATION_RECORD | 37;
/** 2:38 Expiration Time */
public static final int TAG_EXPIRATION_TIME = APPLICATION_RECORD | 38;
/** 2:40 Special Instructions */
public static final int TAG_SPECIAL_INSTRUCTIONS = APPLICATION_RECORD | 40; // 0x0228
/** 2:42 Action Advised (1: Kill, 2: Replace, 3: Append, 4: Reference) */
public static final int TAG_ACTION_ADVICED = APPLICATION_RECORD | 42;
/** 2:45 Reference Service (repeatable in triplets with 2:47 and 2:50) */
public static final int TAG_REFERENCE_SERVICE = APPLICATION_RECORD | 45;
/** 2:47 Reference Date (mandatory if 2:45 present) */
public static final int TAG_REFERENCE_DATE = APPLICATION_RECORD | 47;
/** 2:50 Reference Number (mandatory if 2:45 present) */
public static final int TAG_REFERENCE_NUMBER = APPLICATION_RECORD | 50;
/** 2:55 Date Created */
public static final int TAG_DATE_CREATED = APPLICATION_RECORD | 55; // 0x0237
/** 2:60 Time Created */
public static final int TAG_TIME_CREATED = APPLICATION_RECORD | 60;
/** 2:62 Digital Creation Date */
public static final int TAG_DIGITAL_CREATION_DATE = APPLICATION_RECORD | 62;
/** 2:63 Digital Creation Date */
public static final int TAG_DIGITAL_CREATION_TIME = APPLICATION_RECORD | 63;
/** 2:65 Originating Program */
public static final int TAG_ORIGINATING_PROGRAM = APPLICATION_RECORD | 65;
/** 2:70 Program Version (only valid if 2:65 present) */
public static final int TAG_PROGRAM_VERSION = APPLICATION_RECORD | 70;
/** 2:75 Object Cycle (a: morning, p: evening, b: both) */
public static final int TAG_OBJECT_CYCLE = APPLICATION_RECORD | 75;
/** 2:80 By-line (repeatable) */
public static final int TAG_BY_LINE = APPLICATION_RECORD | 80; // 0x0250
/** 2:85 By-line Title (repeatable) */
public static final int TAG_BY_LINE_TITLE = APPLICATION_RECORD | 85; // 0x0255
/** 2:90 City */
public static final int TAG_CITY = APPLICATION_RECORD | 90; // 0x025a
/** 2:92 Sub-location */
public static final int TAG_SUB_LOCATION = APPLICATION_RECORD | 92;
/** 2:95 Province/State */
public static final int TAG_PROVINCE_OR_STATE = APPLICATION_RECORD | 95; // 0x025f
/** 2:100 Country/Primary Location Code */
public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE = APPLICATION_RECORD | 100;
/** 2:101 Country/Primary Location Name */
public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION = APPLICATION_RECORD | 101; // 0x0265
/** 2:103 Original Transmission Reference */
public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE = APPLICATION_RECORD | 103; // 0x0267
/** 2:105 Headline */
public static final int TAG_HEADLINE = APPLICATION_RECORD | 105; // 0x0269
/** 2:110 Credit */
public static final int TAG_CREDIT = APPLICATION_RECORD | 110; // 0x026e
/** 2:115 Source */
public static final int TAG_SOURCE = APPLICATION_RECORD | 115; // 0x0273
/** 2:116 Copyright Notice */
public static final int TAG_COPYRIGHT_NOTICE = APPLICATION_RECORD | 116; // 0x0274
/** 2:118 Contact */
public static final int TAG_CONTACT = APPLICATION_RECORD | 118;
/** 2:120 Catption/Abstract */
public static final int TAG_CAPTION = APPLICATION_RECORD | 120; // 0x0278
/** 2:122 Writer/Editor (repeatable) */
public static final int TAG_WRITER = APPLICATION_RECORD | 122; // 0x027a
/** 2:125 Rasterized Caption (binary data) */
public static final int TAG_RASTERIZED_CATPTION = APPLICATION_RECORD | 125;
/** 2:130 Image Type */
public static final int TAG_IMAGE_TYPE = APPLICATION_RECORD | 130;
/** 2:131 Image Orientation */
public static final int TAG_IMAGE_ORIENTATION = APPLICATION_RECORD | 131;
/** 2:135 Language Identifier */
public static final int TAG_LANGUAGE_IDENTIFIER = APPLICATION_RECORD | 135;
// TODO: 2:150-2:154 Audio
// TODO: Should we expose this field?
/**
* 2:199 JobMinder Assignment Data (Custom IPTC field).
* A common custom IPTC field used by a now discontinued application called JobMinder.
*
* @see <a href="http://www.jobminder.net/">JobMinder Homepage</a>
*/
static final int CUSTOM_TAG_JOBMINDER_ASSIGNMENT_DATA = APPLICATION_RECORD | 199;
// TODO: Other custom fields in 155-200 range?
// TODO: 2:200-2:202 Object Preview Data
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.iptc;
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
import com.twelvemonkeys.imageio.metadata.Entry;
import java.util.Collection;
/**
* IPTCDirectory
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IPTCDirectory.java,v 1.0 Nov 11, 2009 5:02:59 PM haraldk Exp$
*/
final class IPTCDirectory extends AbstractDirectory {
IPTCDirectory(final Collection<? extends Entry> pEntries) {
super(pEntries);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.iptc;
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
/**
* IPTCEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IPTCEntry.java,v 1.0 Nov 13, 2009 8:57:04 PM haraldk Exp$
*/
class IPTCEntry extends AbstractEntry {
public IPTCEntry(final int pTagId, final Object pValue) {
super(pTagId, pValue);
}
@Override
public String getFieldName() {
switch ((Integer) getIdentifier()) {
case IPTC.TAG_SOURCE:
return "Source";
// TODO: More tags...
}
return null;
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.iptc;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.lang.StringUtil;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import java.util.List;
/**
* IPTCReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: IPTCReader.java,v 1.0 Nov 13, 2009 8:37:23 PM haraldk Exp$
*/
public final class IPTCReader extends MetadataReader {
private static final int ENCODING_UNKNOWN = -1;
private static final int ENCODING_UNSPECIFIED = 0;
private static final int ENCODING_UTF_8 = 0x1b2547;
private int mEncoding = ENCODING_UNSPECIFIED;
@Override
public Directory read(final ImageInputStream pInput) throws IOException {
final List<Entry> entries = new ArrayList<Entry>();
// 0x1c identifies start of a tag
while (pInput.read() == 0x1c) {
short tagId = pInput.readShort();
int tagByteCount = pInput.readUnsignedShort();
Entry entry = readEntry(pInput, tagId, tagByteCount);
if (entry != null) {
entries.add(entry);
}
}
return new IPTCDirectory(entries);
}
private IPTCEntry readEntry(final ImageInputStream pInput, final short pTagId, final int pLength) throws IOException {
Object value = null;
switch (pTagId) {
case IPTC.TAG_CODED_CHARACTER_SET:
// TODO: Mapping from ISO 646 to Java supported character sets?
// TODO: Move somewhere else?
mEncoding = parseEncoding(pInput, pLength);
return null;
case IPTC.TAG_RECORD_VERSION:
// A single unsigned short value
value = pInput.readUnsignedShort();
break;
default:
// Skip non-Application fields, as they are typically not human readable
if ((pTagId & 0xff00) != IPTC.APPLICATION_RECORD) {
pInput.skipBytes(pLength);
return null;
}
// fall through
}
// If we don't have a value, treat it as a string
if (value == null) {
if (pLength < 1) {
value = null;
}
else {
value = parseString(pInput, pLength);
}
}
return new IPTCEntry(pTagId, value);
}
private int parseEncoding(final ImageInputStream pInput, int tagByteCount) throws IOException {
return tagByteCount == 3
&& (pInput.readUnsignedByte() << 16 | pInput.readUnsignedByte() << 8 | pInput.readUnsignedByte()) == ENCODING_UTF_8
? ENCODING_UTF_8 : ENCODING_UNKNOWN;
}
// TODO: Pass encoding as parameter? Use if specified
private String parseString(final ImageInputStream pInput, final int pLength) throws IOException {
byte[] data = new byte[pLength];
pInput.readFully(data);
// NOTE: The IPTC specification says character data should use ISO 646 or ISO 2022 encoding.
// UTF-8 contains all 646 characters, but not 2022.
// This is however close to what libiptcdata does, see: http://libiptcdata.sourceforge.net/docs/iptc-i18n.html
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder();
try {
// First try to decode using UTF-8 (which seems to be the de-facto standard)
// Will fail fast on illegal UTF-8-sequences
CharBuffer chars = decoder.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(data));
return chars.toString();
}
catch (CharacterCodingException notUTF8) {
if (mEncoding == ENCODING_UTF_8) {
throw new IIOException("Wrong encoding of IPTC data, explicitly set to UTF-8 in DataSet 1:90", notUTF8);
}
// Fall back to use ISO-8859-1
// This will not fail, but may may create wrong fallback-characters
return StringUtil.decode(data, 0, data.length, "ISO8859_1");
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import java.util.Collections;
import java.util.Map;
/**
* XMP
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMP.java,v 1.0 Nov 12, 2009 12:19:32 AM haraldk Exp$
*
* @see <a href="http://www.adobe.com/products/xmp/">Extensible Metadata Platform (XMP)</a>
*/
public interface XMP {
/** W3C Resource Description Format namespace */
String NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
/** Dublin Core Metadata Initiative namespace */
String NS_DC = "http://purl.org/dc/elements/1.1/";
String NS_EXIF = "http://ns.adobe.com/exif/1.0/";
String NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/";
String NS_ST_REF = "http://ns.adobe.com/xap/1.0/sType/ResourceRef#";
String NS_TIFF = "http://ns.adobe.com/tiff/1.0/";
String NS_XAP = "http://ns.adobe.com/xap/1.0/";
String NS_XAP_MM = "http://ns.adobe.com/xap/1.0/mm/";
/** Contains the mapping from URI to default namespace prefix. */
Map<String, String> DEFAULT_NS_MAPPING = Collections.unmodifiableMap(new XMPNamespaceMapping());
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
import com.twelvemonkeys.imageio.metadata.Entry;
import java.util.List;
/**
* XMPDirectory
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPDirectory.java,v 1.0 Nov 17, 2009 9:38:58 PM haraldk Exp$
*/
final class XMPDirectory extends AbstractDirectory {
// TODO: Store size of root directory, to allow serializing
// TODO: XMPDirectory, maybe not even an AbstractDirectory
// - Keeping the Document would allow for easier serialization
// TODO: Or use direct SAX parsing
public XMPDirectory(List<Entry> pEntries) {
super(pEntries);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.metadata.AbstractEntry;
/**
* XMPEntry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPEntry.java,v 1.0 Nov 17, 2009 9:38:39 PM haraldk Exp$
*/
final class XMPEntry extends AbstractEntry {
private final String mFieldName;
public XMPEntry(final String pIdentifier, final Object pValue) {
this(pIdentifier, null, pValue);
}
public XMPEntry(final String pIdentifier, final String pFieldName, final Object pValue) {
super(pIdentifier, pValue);
mFieldName = pFieldName;
}
@SuppressWarnings({"SuspiciousMethodCalls"})
@Override
public String getFieldName() {
return mFieldName != null ? mFieldName : XMP.DEFAULT_NS_MAPPING.get(getIdentifier());
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import java.util.HashMap;
/**
* XMPNamespaceMapping
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPNamespaceMapping.java,v 1.0 Nov 17, 2009 6:35:21 PM haraldk Exp$
*/
final class XMPNamespaceMapping extends HashMap<String, String> {
public XMPNamespaceMapping() {
put(XMP.NS_RDF, "rdf");
put(XMP.NS_DC, "dc");
put(XMP.NS_EXIF, "exif");
put(XMP.NS_PHOTOSHOP, "photoshop");
put(XMP.NS_ST_REF, "stRef");
put(XMP.NS_TIFF, "tiff");
put(XMP.NS_XAP, "xap");
put(XMP.NS_XAP_MM, "xapMM");
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.MetadataReader;
import com.twelvemonkeys.imageio.util.IIOUtil;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.*;
/**
* XMPReader
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPReader.java,v 1.0 Nov 14, 2009 11:04:30 PM haraldk Exp$
*/
public final class XMPReader extends MetadataReader {
@Override
public Directory read(final ImageInputStream pInput) throws IOException {
// pInput.mark();
//
// BufferedReader reader = new BufferedReader(new InputStreamReader(IIOUtil.createStreamAdapter(pInput), Charset.forName("UTF-8")));
// String line;
// while ((line = reader.readLine()) != null) {
// System.out.println(line);
// }
//
// pInput.reset();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try {
// TODO: Consider parsing using SAX?
// TODO: Determine encoding and parse using a Reader...
// TODO: Refactor scanner to return inputstream?
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(IIOUtil.createStreamAdapter(pInput)));
// XMLSerializer serializer = new XMLSerializer(System.err, System.getProperty("file.encoding"));
// serializer.serialize(document);
// Each rdf:Description is a Directory (but we can't really rely on that structure.. it's only convention)
// - Each element inside the rdf:Desc is an Entry
Node rdfRoot = document.getElementsByTagNameNS(XMP.NS_RDF, "RDF").item(0);
NodeList descriptions = document.getElementsByTagNameNS(XMP.NS_RDF, "Description");
return parseDirectories(rdfRoot, descriptions);
}
catch (SAXException e) {
throw new IIOException(e.getMessage(), e);
}
catch (ParserConfigurationException e) {
throw new RuntimeException(e); // TODO: Or IOException?
}
}
private XMPDirectory parseDirectories(final Node pParentNode, NodeList pNodes) {
Map<String, List<Entry>> subdirs = new LinkedHashMap<String, List<Entry>>();
for (Node desc : asIterable(pNodes)) {
if (desc.getParentNode() != pParentNode) {
continue;
}
for (Node node : asIterable(desc.getChildNodes())) {
if (node.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
// Lookup
List<Entry> dir = subdirs.get(node.getNamespaceURI());
if (dir == null) {
dir = new ArrayList<Entry>();
subdirs.put(node.getNamespaceURI(), dir);
}
Object value;
Node parseType = node.getAttributes().getNamedItemNS(XMP.NS_RDF, "parseType");
if (parseType != null && "Resource".equals(parseType.getNodeValue())) {
List<Entry> entries = new ArrayList<Entry>();
for (Node child : asIterable(node.getChildNodes())) {
if (child.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
entries.add(new XMPEntry(child.getNamespaceURI() + child.getLocalName(), child.getLocalName(), getChildTextValue(child)));
}
value = new XMPDirectory(entries);
}
else {
// TODO: Support alternative RDF syntax (short-form), using attributes on desc
// NamedNodeMap attributes = node.getAttributes();
//
// for (Node attr : asIterable(attributes)) {
// System.out.println("attr.getNodeName(): " + attr.getNodeName());
// System.out.println("attr.getNodeValue(): " + attr.getNodeValue());
// }
value = getChildTextValue(node);
}
XMPEntry entry = new XMPEntry(node.getNamespaceURI() + node.getLocalName(), node.getLocalName(), value);
dir.add(entry);
}
}
// TODO: Consider flattening the somewhat artificial directory structure
List<Entry> entries = new ArrayList<Entry>();
for (Map.Entry<String, List<Entry>> entry : subdirs.entrySet()) {
entries.add(new XMPEntry(entry.getKey(), new XMPDirectory(entry.getValue())));
}
return new XMPDirectory(entries);
}
private Object getChildTextValue(Node node) {
Object value;
Node child = node.getFirstChild();
String strVal = null;
if (child != null) {
strVal = child.getNodeValue();
}
value = strVal != null ? strVal.trim() : "";
return value;
}
private Iterable<? extends Node> asIterable(final NamedNodeMap pNodeList) {
return new Iterable<Node>() {
public Iterator<Node> iterator() {
return new Iterator<Node>() {
private int mIndex;
public boolean hasNext() {
return pNodeList != null && pNodeList.getLength() > mIndex;
}
public Node next() {
return pNodeList.item(mIndex++);
}
public void remove() {
throw new UnsupportedOperationException("Method remove not supported");
}
};
}
};
}
private Iterable<? extends Node> asIterable(final NodeList pNodeList) {
return new Iterable<Node>() {
public Iterator<Node> iterator() {
return new Iterator<Node>() {
private int mIndex;
public boolean hasNext() {
return pNodeList != null && pNodeList.getLength() > mIndex;
}
public Node next() {
return pNodeList.item(mIndex++);
}
public void remove() {
throw new UnsupportedOperationException("Method remove not supported");
}
};
}
};
}
}

View File

@@ -0,0 +1,243 @@
/*
* Copyright (c) 2009, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.metadata.xmp;
import com.twelvemonkeys.imageio.stream.BufferedImageInputStream;
import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import java.io.*;
import java.nio.charset.Charset;
/**
* XMPScanner
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPScanner.java,v 1.0 Nov 11, 2009 4:49:00 PM haraldk Exp$
*/
public final class XMPScanner {
/**
* {@code &lt;?xpacket begin=}
* <p/>
* <ul>
* <li>
* 8-bit (UTF-8):
* 0x3C 0x3F 0x78 0x70 0x61 0x63 0x6B 0x65 0x74 0x20
* 0x62 0x65 0x67 0x69 0x6E 0x3D
* </li>
* <li>16-bit encoding (UCS-2, UTF-16): (either big- or little-endian order)
* 0x3C 0x00 0x3F 0x00 0x78 0x00 0x70 0x00 0x61 0x00
* 0x63 0x00 0x6B 0x00 0x65 0x00 0x74 0x00 0x20 0x00 0x62 0x00
* 0x65 0x00 0x67 0x00 0x69 0x00 0x6E 0x00 0x3D [0x00]
* </li>
* <li>32-bit encoding (UCS-4):
* As 16 bit UCS2, with three 0x00 instead of one.</li>
* </ul>
*/
private static final byte[] XMP_PACKET_BEGIN = {
0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20,
0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D
};
/**
* {@code &lt;?xpacket end=}
*/
private static final byte[] XMP_PACKET_END = {
0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20,
0x65, 0x6E, 0x64, 0x3D
};
/**
* Scans the given input for an XML metadata packet.
* The scanning process involves reading every byte in the file, while searching for an XMP packet.
* This process is very inefficient, compared to reading a known file format.
* <p/>
* <em>NOTE: The XMP Specification says this method of reading an XMP packet
* should be considered a last resort.</em><br/>
* This is because files may contain multiple XMP packets, some which may be related to embedded resources,
* some which may be obsolete (or even incomplete).
*
* @param pInput the input to scan. The input may be an {@link javax.imageio.stream.ImageInputStream} or
* any object that can be passed to {@link ImageIO#createImageInputStream(Object)}.
* Typically this may be a {@link File}, {@link InputStream} or {@link java.io.RandomAccessFile}.
*
* @return a character Reader
*
* @throws java.nio.charset.UnsupportedCharsetException if the encoding specified within the BOM is not supported
* by the JRE.
* @throws IOException if an I/O exception occurs reading from {@code pInput}.
* @see ImageIO#createImageInputStream(Object)
*/
static public Reader scanForXMPPacket(final Object pInput) throws IOException {
ImageInputStream stream = pInput instanceof ImageInputStream ? (ImageInputStream) pInput : ImageIO.createImageInputStream(pInput);
// TODO: Consider if BufferedIIS is a good idea
if (!(stream instanceof BufferedImageInputStream)) {
stream = new BufferedImageInputStream(stream);
}
// TODO: Might be more than one XMP block per file (it's possible to re-start for now)..
long pos;
pos = scanForSequence(stream, XMP_PACKET_BEGIN);
if (pos >= 0) {
// Skip ' OR " (plus possible nulls for 16/32 bit)
byte quote = stream.readByte();
if (quote == '\'' || quote == '"') {
Charset cs = null;
// Read BOM
byte[] bom = new byte[4];
stream.readFully(bom);
// NOTE: Empty string should be treated as UTF-8 for backwards compatibility
if (bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF && bom[3] == quote ||
bom[0] == quote) {
// UTF-8
cs = Charset.forName("UTF-8");
}
else if (bom[0] == (byte) 0xFE && bom[1] == (byte) 0xFF && bom[2] == 0x00 && bom[3] == quote) {
// UTF-16 BIG endian
cs = Charset.forName("UTF-16BE");
}
else if (bom[0] == 0x00 && bom[1] == (byte) 0xFF && bom[2] == (byte) 0xFE && bom[3] == quote) {
stream.skipBytes(1); // Alignment
// UTF-16 little endian
cs = Charset.forName("UTF-16LE");
}
else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == (byte) 0xFE && bom[3] == (byte) 0xFF) {
// NOTE: 32-bit character set not supported by default
// UTF 32 BIG endian
cs = Charset.forName("UTF-32BE");
}
else if (bom[0] == (byte) 0xFF && bom[1] == (byte) 0xFE && bom[2] == 0x00 && bom[3] == 0x00) {
// TODO: FixMe..
// NOTE: 32-bit character set not supported by default
// UTF 32 little endian
cs = Charset.forName("UTF-32LE");
}
if (cs != null) {
// Read all bytes until <?xpacket end= up-front or filter stream
stream.mark();
long end = scanForSequence(stream, XMP_PACKET_END);
stream.reset();
long length = end - stream.getStreamPosition();
Reader reader = new InputStreamReader(IIOUtil.createStreamAdapter(stream, length), cs);
// Skip until ?>
while (reader.read() != '>') {
}
// Return reader?
// How to decide between w or r?!
return reader;
}
}
}
return null;
}
/**
* Scans for a given ASCII sequence.
*
* @param pStream the stream to scan
* @param pSequence the byte sequence to search for
*
* @return the start position of the given sequence.
*
* @throws IOException if an I/O exception occurs during scanning
*/
private static long scanForSequence(final ImageInputStream pStream, final byte[] pSequence) throws IOException {
long start = -1l;
int index = 0;
int nullBytes = 0;
for (int read; (read = pStream.read()) >= 0;) {
if (pSequence[index] == (byte) read) {
// If this is the first byte in the sequence, store position
if (start == -1) {
start = pStream.getStreamPosition() - 1;
}
// Inside the sequence, there might be 1 or 3 null bytes, depending on 16/32 byte encoding
if (nullBytes == 1 || nullBytes == 3) {
pStream.skipBytes(nullBytes);
}
index++;
// If we found the entire sequence, we're done, return start position
if (index == pSequence.length) {
return start;
}
}
else if (index == 1 && read == 0 && nullBytes < 3) {
// Skip 1 or 3 null bytes for 16/32 bit encoding
nullBytes++;
}
else if (index != 0) {
// Start over
index = 0;
start = -1;
nullBytes = 0;
}
}
return -1l;
}
//static public XMPDirectory parse(input);
public static void main(final String[] pArgs) throws IOException {
ImageInputStream stream = ImageIO.createImageInputStream(new File(pArgs[0]));
Reader xmp;
while ((xmp = scanForXMPPacket(stream)) != null) {
BufferedReader reader = new BufferedReader(xmp);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
stream.close();
// else {
// System.err.println("XMP not found");
// }
}
}

View File

@@ -0,0 +1,143 @@
package com.twelvemonkeys.imageio.metadata.exif;
import junit.framework.TestCase;
/**
* RationalTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: RationalTestCase.java,v 1.0 Nov 18, 2009 3:23:17 PM haraldk Exp$
*/
public class RationalTestCase extends TestCase {
public void testZeroDenominator() {
try {
new Rational(1, 0);
fail("IllegalArgumentException expected");
}
catch (IllegalArgumentException expected) {
}
}
// TODO: Find a solution to this problem, as we should be able to work with it...
public void testLongMinValue() {
try {
new Rational(Long.MIN_VALUE, 1);
fail("IllegalArgumentException expected");
}
catch (IllegalArgumentException expected) {
}
try {
new Rational(1, Long.MIN_VALUE);
fail("IllegalArgumentException expected");
}
catch (IllegalArgumentException expected) {
}
}
public void testEquals() {
assertEquals(new Rational(0, 1), new Rational(0, 999));
assertEquals(new Rational(0, 1), new Rational(0, -1));
assertEquals(new Rational(1, 2), new Rational(1000000, 2000000));
assertEquals(new Rational(1, -2), new Rational(-1, 2));
Rational x = new Rational(1, -2);
Rational y = new Rational(-1000000, 2000000);
assertEquals(x, y);
assertEquals(x.numerator(), y.numerator());
assertEquals(x.denominator(), y.denominator());
}
public void testEqualsBoundaries() {
assertEquals(new Rational(Long.MAX_VALUE, Long.MAX_VALUE), new Rational(1, 1));
// NOTE: Math.abs(Long.MIN_VALUE) == Long.MIN_VALUE... :-P
assertEquals(new Rational(Long.MIN_VALUE + 1, Long.MIN_VALUE + 1), new Rational(1, 1));
assertEquals(new Rational(Long.MIN_VALUE + 1, Long.MAX_VALUE), new Rational(-1, 1));
assertEquals(new Rational(Long.MAX_VALUE, Long.MIN_VALUE + 1), new Rational(-1, 1));
}
public void testReciprocal() {
assertEquals(new Rational(1, 99), new Rational(99, 1).reciprocal());
assertEquals(new Rational(-1, 1234567), new Rational(-1234567, 1).reciprocal());
}
public void testNegate() {
assertEquals(new Rational(-1, 99), new Rational(1, 99).negate());
assertEquals(new Rational(1, 1234567), new Rational(1, -1234567).negate());
}
public void testPlus() {
Rational x, y;
// 1/2 + 1/3 = 5/6
x = new Rational(1, 2);
y = new Rational(1, 3);
assertEquals(new Rational(5, 6), x.plus(y));
// 8/9 + 1/9 = 1
x = new Rational(8, 9);
y = new Rational(1, 9);
assertEquals(new Rational(1, 1), x.plus(y));
// 1/200000000 + 1/300000000 = 1/120000000
x = new Rational(1, 200000000);
y = new Rational(1, 300000000);
assertEquals(new Rational(1, 120000000), x.plus(y));
// 1073741789/20 + 1073741789/30 = 1073741789/12
x = new Rational(1073741789, 20);
y = new Rational(1073741789, 30);
assertEquals(new Rational(1073741789, 12), x.plus(y));
// x + 0 = x
assertEquals(x, x.plus(Rational.ZERO));
}
public void testTimes() {
Rational x, y;
// 4/17 * 17/4 = 1
x = new Rational(4, 17);
y = new Rational(17, 4);
assertEquals(new Rational(1, 1), x.times(y));
// 3037141/3247033 * 3037547/3246599 = 841/961
x = new Rational(3037141, 3247033);
y = new Rational(3037547, 3246599);
assertEquals(new Rational(841, 961), x.times(y));
// x * 0 = 0
assertEquals(Rational.ZERO, x.times(Rational.ZERO));
}
public void testMinus() {
// 1/6 - -4/-8 = -1/3
Rational x = new Rational(1, 6);
Rational y = new Rational(-4, -8);
assertEquals(new Rational(-1, 3), x.minus(y));
// x - 0 = x
assertEquals(x, x.minus(Rational.ZERO));
}
public void testDivides() {
// 3037141/3247033 / 3246599/3037547 = 841/961
Rational x = new Rational(3037141, 3247033);
Rational y = new Rational(3246599, 3037547);
assertEquals(new Rational(841, 961), x.divides(y));
// 0 / x = 0
assertEquals(Rational.ZERO, new Rational(0, 386).divides(x));
}
public void testDivideZero() {
try {
new Rational(3037141, 3247033).divides(new Rational(0, 1));
}
catch (ArithmeticException expected) {
}
}
}

View File

@@ -0,0 +1,127 @@
package com.twelvemonkeys.imageio.metadata.xmp;
import junit.framework.TestCase;
import java.io.*;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
/**
* XMPScannerTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: XMPScannerTestCase.java,v 1.0 Nov 13, 2009 3:59:43 PM haraldk Exp$
*/
public class XMPScannerTestCase extends TestCase {
static final String XMP =
"<?xpacket begin=\"\uFEFF\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>" +
"<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 4.1-c036 46.276720, Fri Nov 13 2009 15:59:43 \">\n"+
" <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"+
" <rdf:Description rdf:about=\"\"\n"+
" xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\">\n"+
" <photoshop:Source>twelvemonkeys.com</photoshop:Source>\n"+
" </rdf:Description>\n"+
" <rdf:Description rdf:about=\"\"\n"+
" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"+
" <dc:format>application/vnd.adobe.photoshop</dc:format>\n"+
" </rdf:Description>\n"+
" </rdf:RDF>\n"+
"</x:xmpmeta>" +
"<?xpacket end=\"w\"?>";
final Random mRandom = new Random(4934638567l);
private InputStream createRandomStream(final int pLength) {
byte[] bytes = new byte[pLength];
mRandom.nextBytes(bytes);
return new ByteArrayInputStream(bytes);
}
private InputStream createXMPStream(final String pXMP, final String pCharsetName) {
try {
return new SequenceInputStream(
Collections.enumeration(
Arrays.asList(
createRandomStream(79),
new ByteArrayInputStream(pXMP.getBytes(pCharsetName)),
createRandomStream(31)
)
)
);
}
catch (UnsupportedEncodingException e) {
UnsupportedCharsetException uce = new UnsupportedCharsetException(pCharsetName);
uce.initCause(e);
throw uce;
}
}
public void testScanForUTF8() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-8");
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
public void testScanForUTF8singleQuote() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-8".replace("\"", "'"));
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
public void testScanForUTF16BE() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-16BE");
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
public void testScanForUTF16BEsingleQuote() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-16BE".replace("\"", "'"));
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
public void testScanForUTF16LE() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-16LE");
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
public void testScanForUTF16LEsingleQuote() throws IOException {
InputStream stream = createXMPStream(XMP, "UTF-16LE".replace("\"", "'"));
Reader reader = XMPScanner.scanForXMPPacket(stream);
assertNotNull(reader);
}
// TODO: Default Java installation on OS X don't seem to have UTF-32 installed. Hmmm..
// public void testUTF32BE() throws IOException {
// InputStream stream = createXMPStream("UTF-32BE");
//
// Reader reader = XMPScanner.scanForXMPPacket(stream);
//
// assertNotNull(reader);
// }
//
// public void testUTF32LE() throws IOException {
// InputStream stream = createXMPStream("UTF-32LE");
//
// Reader reader = XMPScanner.scanForXMPPacket(stream);
//
// assertNotNull(reader);
// }
}