Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / jigsaw / src / org / w3c / tools / jpeg / Exif.java
diff --git a/JMCR-Stable/real-world application/jigsaw/src/org/w3c/tools/jpeg/Exif.java b/JMCR-Stable/real-world application/jigsaw/src/org/w3c/tools/jpeg/Exif.java
new file mode 100644 (file)
index 0000000..4a59851
--- /dev/null
@@ -0,0 +1,379 @@
+// Exif.java\r
+// $Id: Exif.java,v 1.2 2010/06/15 17:53:10 smhuang Exp $\r
+// Copyright (c) 2003 Norman Walsh\r
+// Please first read the full copyright statement in file COPYRIGHT\r
+\r
+package org.w3c.tools.jpeg;\r
+\r
+import java.util.Hashtable;\r
+\r
+/**\r
+ * An API for accessing EXIF encoded information in a JPEG file.\r
+ *\r
+ * <p>JPEG images are stored in a tagged format reminiscent of TIFF files.\r
+ * Each component of the image is identified with a tag number and a size.\r
+ * This allows applications that read JPEG files to skip over information\r
+ * that they don't understand.</p>\r
+ *\r
+ * <p>Additional resources:</p>\r
+ * <ul>\r
+ * <li>Official standards, http://www.exif.org/specifications.html</li>\r
+ * <li>TsuruZoh Tachibanaya's excellent description,\r
+ * http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html\r
+ * </li>\r
+ * <li>Matthias Wandel's JHead, http://www.sentex.net/~mwandel/jhead\r
+ * </ul>\r
+ *\r
+ * <p>This class treats a byte array as a JPEG image and parses the tags\r
+ * searching for EXIF data. EXIF data is also tagged. Based on information\r
+ * provided by the caller, this class builds a hash of EXIF data and\r
+ * makes it available to the caller.</p>\r
+ *\r
+ * <p>Most simple EXIF values are tagged with both their identity and their\r
+ * format. For example, ExposureTime (0x829A) is a rational number and\r
+ * this class can extract that value. However, some fields are of "unknown"\r
+ * format. If you decode one of these, you can add a special purpose decode\r
+ * for that field by associating the name of the decoder class with the\r
+ * field name. For example, my Nikon CoolPix 950 includes a MakerNote (0x927C)\r
+ * field that's tagged "unknown" format. Using information from TsuruZoh's\r
+ * page, I've built a decoder for that field and added it as an example.</p>\r
+ *\r
+ * <p>In addition to the tagged data, JPEG images have five intrinisic\r
+ * properties: height, width, the compression algorithm used, the number of \r
+ * bits used to store each pixel value, and the number of color components.\r
+ * This class allows the caller to unify those intrinsic components with the\r
+ * tagged data.</p>\r
+ *\r
+ * <p>In an effort to be flexible without requiring users to change the\r
+ * source, a fair bit of setup is needed to use this class.</p>\r
+ *\r
+ * <p>The caller must:</p>\r
+ *\r
+ * <ol>\r
+ * <li>Construct an Exif object, <code>exif = new Exif()</code>.</li>\r
+ * <li>Associate names with each of the tags of interest,\r
+ *     <code>exif.addTag(<em>nnn</em>, "<em>name</em>")</code>.</li>\r
+ * <li>Associate names with the intrinsic values:\r
+ *   <ul>\r
+ *     <li><code>exif.addHeight("<em>name</em>")</code>\r
+ *     <li><code>exif.addWidth("<em>name</em>")</code>\r
+ *     <li><code>exif.addCompression("<em>name</em>")</code>\r
+ *     <li><code>exif.addNumberOfColorComponents("<em>name</em>")</code>\r
+ *     <li><code>exif.addBitsPerPixel("<em>name</em>")</code>\r
+ *   </ul>\r
+ * </li>\r
+ * <li>Finally, the caller may associate a decoder with specific fields:\r
+ * <code>exif.addDecoder("<em>name</em>", "<em>java.class.name</em>")</code>.\r
+ * </li>\r
+ * </ol>\r
+ *\r
+ * <p>Having setup the exif object, it can be passed to JpegHeaders to be \r
+ * filled in when the JPEG file is parsed.</p>\r
+ *\r
+ * <p>The caller must also explicitly set the intrinsic values since they do\r
+ * not come from the EXIF data.</p>\r
+ *\r
+ * <p>After parsing the JPEG, call <code>exif.getTags()</code> to get back the\r
+ * has of name/value pairs.</p>\r
+ *\r
+ * @version $Revision: 1.2 $\r
+ * @author  Norman Walsh\r
+ * @see ExifData\r
+ * @see TagDecoder\r
+ * @see JpegHeaders\r
+ */\r
+public class Exif {\r
+    private static final int TAG_EXIF_OFFSET = 0x8769;\r
+    private static final int TAG_INTEROP_OFFSET = 0xa005;\r
+\r
+    private Hashtable tags = new Hashtable();\r
+    private Hashtable exif = new Hashtable();\r
+    private Hashtable decoder = new Hashtable();\r
+    private ExifData data = null;\r
+    private boolean intelOrder = false;\r
+\r
+    private String tagHeight = null;\r
+    private String tagWidth = null;\r
+    private String tagComp = null;\r
+    private String tagBPP = null;\r
+    private String tagNumCC = null;\r
+\r
+    public void parseExif(byte[] exifData) {\r
+       data = new ExifData(exifData);\r
+       if (!data.isExifData()) {\r
+           return;\r
+       }\r
+\r
+       int firstOffset = data.get32u(10);\r
+       processExifDir(6+firstOffset, 6);\r
+    }\r
+\r
+    public void setHeight(int height) {\r
+       if (tagHeight != null) {\r
+           exif.put(tagHeight, ""+height);\r
+       }\r
+    }\r
+\r
+    public void setWidth(int width) {\r
+       if (tagWidth != null) {\r
+           exif.put(tagWidth, ""+width);\r
+       }\r
+    }\r
+\r
+    public void setCompression(String comp) {\r
+       if (tagComp != null) {\r
+           exif.put(tagComp, comp);\r
+       }\r
+    }\r
+\r
+    public void setBPP(int bitsPP) {\r
+       if (tagBPP != null) {\r
+           exif.put(tagBPP, ""+bitsPP);\r
+       }\r
+    }\r
+\r
+    public void setNumCC(int numCC) {\r
+       if (tagNumCC != null) {\r
+           exif.put(tagNumCC, ""+numCC);\r
+       }\r
+    }\r
+\r
+    public void addHeight(String name) {\r
+       tagHeight = name;\r
+    }\r
+\r
+    public void addWidth(String name) {\r
+       tagWidth = name;\r
+    }\r
+\r
+    public void addCompression(String name) {\r
+       tagComp = name;\r
+    }\r
+\r
+    public void addBitsPerPixel(String name) {\r
+       tagBPP = name;\r
+    }\r
+    public void addNumberOfColorComponents(String name) {\r
+       tagNumCC = name;\r
+    }\r
+\r
+    public void addTag(int tag, String tagName) {\r
+       tags.put(new Integer(tag), tagName);\r
+    }\r
+\r
+    public void addDecoder(String name, String className) {\r
+       decoder.put(name, className);\r
+    }\r
+\r
+    public Hashtable getTags() {\r
+       return exif;\r
+    }\r
+\r
+    protected void processExifDir(int dirStart,\r
+                                 int offsetBase) {\r
+\r
+       int numEntries = data.get16u(dirStart);\r
+       //System.err.println("EXIF: numEntries: " + numEntries);\r
+\r
+       for (int de = 0; de < numEntries; de++) {\r
+           int dirOffset = dirStart + 2 + (12 * de);\r
+\r
+           int tag = data.get16u(dirOffset);\r
+           int format = data.get16u(dirOffset+2);\r
+           int components = data.get32u(dirOffset+4);\r
+\r
+           //System.err.println("EXIF: entry: 0x" + Integer.toHexString(tag)\r
+           //           + " " + format\r
+           //           + " " + components);\r
+\r
+           if (format < 0 || format > data.NUM_FORMATS) {\r
+               System.err.println("Bad number of formats in EXIF dir: " +\r
+                                  format);\r
+               return;\r
+           }\r
+\r
+           int byteCount = components * ExifData.bytesPerFormat[format];\r
+           int valueOffset = dirOffset + 8;\r
+\r
+           if (byteCount > 4) {\r
+               int offsetVal = data.get32u(dirOffset+8);\r
+               valueOffset = offsetBase + offsetVal;\r
+           }\r
+\r
+           //System.err.println("valueOffset: " + valueOffset + \r
+           //                                     " byteCount: " + byteCount);\r
+\r
+           Integer iTag = new Integer(tag);\r
+\r
+           if (tag == TAG_EXIF_OFFSET || tag == TAG_INTEROP_OFFSET) {\r
+               int subdirOffset = data.get32u(valueOffset);\r
+\r
+               //System.err.println("offset: " + subdirOffset+\r
+               //                                ":"+offsetBase+subdirOffset);\r
+\r
+               processExifDir(offsetBase+subdirOffset, offsetBase);\r
+           } else {\r
+               String tagName = "BugBugBug";\r
+               boolean usedTag = false;\r
+\r
+               if (tags.containsKey(iTag)) {\r
+                   tagName = (String) tags.get(iTag);\r
+                   usedTag = true;\r
+               } else {\r
+                   tagName = ":unknown0x" + \r
+                                         Integer.toHexString(iTag.intValue());\r
+               }\r
+\r
+               if (decoder.containsKey(tagName)) {\r
+                   String className = (String) decoder.get(tagName);\r
+                   TagDecoder decoder = null;\r
+\r
+                   try {\r
+                       decoder = (TagDecoder) \r
+                                       Class.forName(className).newInstance();\r
+                   //Added by Jeff Huang\r
+                   //TODO: FIXIT\r
+                   } catch (ClassNotFoundException cnfe) {\r
+                       System.err.println("Class not found: " + className);\r
+                   } catch (InstantiationException cnfe) {\r
+                       System.err.println("Failed to instantiate " +\r
+                                          className);\r
+                   } catch (IllegalAccessException cnfe) {\r
+                       System.err.println("Illegal access instantiating " +\r
+                                          className);\r
+                   } catch (ClassCastException cnfe) {\r
+                       System.err.println("Class " + className + \r
+                                          " is not a TagDecoder");\r
+                   }\r
+\r
+                   if (decoder != null) {\r
+                       decoder.decode(exif, data, format, valueOffset, \r
+                                      byteCount);\r
+                   }\r
+               } else {\r
+                   switch (format) {\r
+                   case ExifData.FMT_UNDEFINED:\r
+                       assignUndefined(tagName, valueOffset, byteCount);\r
+                       break;\r
+                   case ExifData.FMT_STRING:\r
+                       assignString(tagName, valueOffset, byteCount);\r
+                       break;\r
+                   case ExifData.FMT_SBYTE:\r
+                       assignSByte(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_BYTE:\r
+                       assignByte(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_USHORT:\r
+                       assignUShort(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_SSHORT:\r
+                       assignSShort(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_ULONG:\r
+                       assignULong(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_SLONG:\r
+                       assignSLong(tagName, valueOffset);\r
+                       break;\r
+                   case ExifData.FMT_URATIONAL:\r
+                   case ExifData.FMT_SRATIONAL:\r
+                       assignRational(tagName, valueOffset);\r
+                       break;\r
+                   default:\r
+                       //System.err.println("Unknown format " + format + \r
+                       //                   " for " + tagName);\r
+                   }\r
+               }\r
+           }\r
+       }\r
+    }\r
+\r
+    protected void assignUndefined(String tagName, int offset, int length) {\r
+       String result = data.getUndefined(offset, length);\r
+       if (!"".equals(result)) {\r
+           exif.put(tagName, result);\r
+       }\r
+    }\r
+\r
+    protected void assignString(String tagName, int offset, int length) {\r
+       String result = data.getString(offset, length);\r
+       if (!"".equals(result)) {\r
+           exif.put(tagName, result);\r
+       }\r
+    }\r
+\r
+    protected void assignSByte(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_SBYTE, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignByte(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_BYTE, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignUShort(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_USHORT, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignSShort(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_SSHORT, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignULong(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_ULONG, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignSLong(String tagName, int offset) {\r
+       int result = (int) data.convertAnyValue(ExifData.FMT_SLONG, offset);\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+\r
+    protected void assignRational(String tagName, int offset) {\r
+       int num = data.get32s(offset);\r
+       int den = data.get32s(offset+4);\r
+       String result = "";\r
+\r
+       // This is a bit silly, I really ought to find a real GCD algorithm\r
+       if (num % 10 == 0 && den % 10 == 0) {\r
+           num = num / 10;\r
+           den = den / 10;\r
+       }\r
+\r
+       if (num % 5 == 0 && den % 5 == 0) {\r
+           num = num / 5;\r
+           den = den / 5;\r
+       }\r
+\r
+       if (num % 3 == 0 && den % 3 == 0) {\r
+           num = num / 3;\r
+           den = den / 3;\r
+       }\r
+\r
+       if (num % 2 == 0 && den % 2 == 0) {\r
+           num = num / 2;\r
+           den = den / 2;\r
+       }\r
+\r
+       if (den == 0) {\r
+           result = "0";\r
+       } else if (den == 1) {\r
+           result = "" + num; // "" + int sure looks ugly...\r
+       } else {\r
+           result = "" + num + "/" + den;\r
+       }\r
+\r
+       //System.err.println("\t" + tagName + ": " + result);\r
+       exif.put(tagName, ""+result);\r
+    }\r
+}\r