While this was a nice exercise, I just switched to using JAlbum on this site, that is able to pull the tags with its built-in skins. 24-01-2011
Photo management tools, such Windows Live Photo Gallery allow annotating photos with tags. They often save it according to the XMP format, that is a readable RDF representation. For example, like the following
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:exif="http://ns.adobe.com/exif/1.0/" ... <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default"> japan 034 </rdf:li></rdf:Alt> </dc:description> ... </rdf:Description> </rdf:RDF>
For more information on the RDF technology and this example in detail, see my writeup on metadata technology.
The following Java class allows extracting tags from JPEG files. It is self-explanatory and a little bit dirty. I use it to extract tags made with Windows Live Photo Gallery and show them on Maven-generated pages of this site.
Here is the class, originally from the borys.name package, conveniently distributed under the BSD license.
package name.borys;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import javax.imageio.ImageIO;
/**
*
* JPEG loading file stream that extracts XMP tags.
*
* @author Borys Omelayenko, http://borys.name
*
*
*/
public class MetadataExtractingImageInputStream extends FileInputStream {
/**
*
* Loads a JPEG image, pulls XMP tags, and resizes.
*
* @param sourceLargeFile
* source file, typically a large original JPEG image
* @param destinationResizedFile
* destination file, typically a small thumb limited in height
* @param destinationHeight
* height limit on destination files
* @return
* @throws IOException
*/
public static Collection<String> resizeAndExtractTags(
File sourceLargeFile,
File destinationResizedFile,
int destinationHeight
)
throws IOException {
// load image
MetadataExtractingImageInputStream stream =
new MetadataExtractingImageInputStream(sourceLargeFile);
BufferedImage image = ImageIO.read(stream);
stream.close();
// resize
double ratio = image.getHeight(null) / destinationHeight;
int width = (int)Math.round(image.getWidth(null) / ratio);
BufferedImage bufferedImage = new BufferedImage(
width,
destinationHeight,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bufferedImage.createGraphics();
g2d.drawImage(image, 0, 0, width, destinationHeight, Color.white, null);
g2d.dispose();
// write resized
ImageIO.write(bufferedImage, "jpg", destinationResizedFile);
// return tags
return stream.getTags();
}
private TreeSet<String> tags = new TreeSet<String>();
private String tag = "";
public MetadataExtractingImageInputStream(File file)
throws FileNotFoundException {
super(file);
}
public Set<String> getTags() {
return tags;
}
/**
* Checks if the last bytes passed to the <code>match</code> method matched the string.
*/
private class TailMatch {
byte[] stringToMatch = new byte[]{'<','/','r','d','f',':','l','i','>'};
int positionInStringToMatch = -1;
public TailMatch(byte[] stringToMatch) {
super();
this.stringToMatch = stringToMatch;
}
public boolean match(byte c) {
if (positionInStringToMatch >= stringToMatch.length - 1) {
positionInStringToMatch = -1;
}
if (stringToMatch[positionInStringToMatch + 1] == c) {
positionInStringToMatch ++;
} else {
positionInStringToMatch = -1;
}
return positionInStringToMatch == stringToMatch.length - 1;
}
}
final byte[] startTag = new byte[]{'<','r','d','f',':','l','i','>'};
final byte[] endTag = new byte[]{'<','/','r','d','f',':','l','i','>'};
TailMatch startMatch = new TailMatch(startTag);
TailMatch endMatch = new TailMatch(endTag);
boolean inTag = false;
/**
* All read bytes are pulled through this analyzer
*/
public void analyse(byte[] b, int off, int len) {
for (int i = off; i < len; i ++)
{
byte c = b[i];
if (startMatch.match(c)) {
inTag = true;
tag = "";
} else {
if (inTag) {
tag += (char)c;
}
}
if (endMatch.match(c)) {
if (inTag) {
String cleanTag = tag.substring(0, tag.length() - endTag.length);
if (!"web".equals(cleanTag)) {
tags.add(cleanTag);
}
}
tag = "";
inTag = false;
}
}
}
@Override
public int read(byte[] b, int off, int len)
throws IOException {
int count = super.read(b, off, len);
if (count > 0 ) {
analyse(b, off, len);
}
return count;
}
@Override
public int read(byte[] b) throws IOException {
int count = super.read(b);
if (count > 0 ) {
analyse(b, 0, b.length);
}
return count;
}
@Override
public int read() throws IOException {
return super.read();
}
}
(c) 2011, Borys Omelayenko, 