]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/jpeg/drew/JpegSegmentReader.java
Version 10, May 2010
[GpsPrune.git] / tim / prune / jpeg / drew / JpegSegmentReader.java
diff --git a/tim/prune/jpeg/drew/JpegSegmentReader.java b/tim/prune/jpeg/drew/JpegSegmentReader.java
new file mode 100644 (file)
index 0000000..382e9f5
--- /dev/null
@@ -0,0 +1,171 @@
+package tim.prune.jpeg.drew;\r
+\r
+import java.io.*;\r
+\r
+/**\r
+ * Class to perform read functions of Jpeg files, returning specific file segments\r
+ * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com\r
+ */\r
+public class JpegSegmentReader\r
+{\r
+       /** Start of scan marker */\r
+       private static final byte SEGMENT_SOS = (byte)0xDA;\r
+\r
+       /** End of image marker */\r
+       private static final byte MARKER_EOI = (byte)0xD9;\r
+\r
+       /** APP0 Jpeg segment identifier -- Jfif data. */\r
+       public static final byte SEGMENT_APP0 = (byte)0xE0;\r
+       /** APP1 Jpeg segment identifier -- where Exif data is kept. */\r
+       public static final byte SEGMENT_APP1 = (byte)0xE1;\r
+       /** APP2 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP2 = (byte)0xE2;\r
+       /** APP3 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP3 = (byte)0xE3;\r
+       /** APP4 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP4 = (byte)0xE4;\r
+       /** APP5 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP5 = (byte)0xE5;\r
+       /** APP6 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP6 = (byte)0xE6;\r
+       /** APP7 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP7 = (byte)0xE7;\r
+       /** APP8 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP8 = (byte)0xE8;\r
+       /** APP9 Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APP9 = (byte)0xE9;\r
+       /** APPA Jpeg segment identifier -- can hold Unicode comments. */\r
+       public static final byte SEGMENT_APPA = (byte)0xEA;\r
+       /** APPB Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APPB = (byte)0xEB;\r
+       /** APPC Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APPC = (byte)0xEC;\r
+       /** APPD Jpeg segment identifier -- IPTC data in here. */\r
+       public static final byte SEGMENT_APPD = (byte)0xED;\r
+       /** APPE Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APPE = (byte)0xEE;\r
+       /** APPF Jpeg segment identifier. */\r
+       public static final byte SEGMENT_APPF = (byte)0xEF;\r
+       /** Start Of Image segment identifier. */\r
+       public static final byte SEGMENT_SOI = (byte)0xD8;\r
+       /** Define Quantization Table segment identifier. */\r
+       public static final byte SEGMENT_DQT = (byte)0xDB;\r
+       /** Define Huffman Table segment identifier. */\r
+       public static final byte SEGMENT_DHT = (byte)0xC4;\r
+       /** Start-of-Frame Zero segment identifier. */\r
+       public static final byte SEGMENT_SOF0 = (byte)0xC0;\r
+       /** Jpeg comment segment identifier. */\r
+       public static final byte SEGMENT_COM = (byte)0xFE;\r
+\r
+       /** Magic numbers to mark the beginning of all Jpegs */\r
+       private static final int MAGIC_JPEG_BYTE_1 = 0xFF;\r
+       private static final int MAGIC_JPEG_BYTE_2 = 0xD8;\r
+\r
+\r
+       /**\r
+        * Obtain the Jpeg segment data from the specified file\r
+        * @param inFile File to read\r
+        * @return Jpeg segment data from file\r
+        * @throws JpegException on file read errors or exif data errors\r
+        */\r
+       public static JpegSegmentData readSegments(File inFile) throws JpegException\r
+       {\r
+               JpegSegmentData segmentData = new JpegSegmentData();\r
+\r
+               BufferedInputStream bStream = null;\r
+\r
+               try\r
+               {\r
+                       bStream = new BufferedInputStream(new FileInputStream(inFile));\r
+                       int offset = 0;\r
+                       // first two bytes should be jpeg magic number\r
+                       int magic1 = bStream.read() & 0xFF;\r
+                       int magic2 = bStream.read() & 0xFF;\r
+                       checkMagicNumbers(magic1, magic2);\r
+\r
+                       offset += 2;\r
+                       // Loop around segments found\r
+                       do\r
+                       {\r
+                               // next byte is 0xFF\r
+                               byte segmentIdentifier = (byte) (bStream.read() & 0xFF);\r
+                               if ((segmentIdentifier & 0xFF) != 0xFF)\r
+                               {\r
+                                       throw new JpegException("expected jpeg segment start identifier 0xFF at offset "\r
+                                               + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF));\r
+                               }\r
+                               offset++;\r
+                               // next byte is <segment-marker>\r
+                               byte thisSegmentMarker = (byte) (bStream.read() & 0xFF);\r
+                               offset++;\r
+                               // next 2-bytes are <segment-size>: [high-byte] [low-byte]\r
+                               byte[] segmentLengthBytes = new byte[2];\r
+                               bStream.read(segmentLengthBytes, 0, 2);\r
+                               offset += 2;\r
+                               int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF);\r
+                               // segment length includes size bytes, so subtract two\r
+                               segmentLength -= 2;\r
+                               if (segmentLength > bStream.available())\r
+                                       throw new JpegException("segment size would extend beyond file stream length");\r
+                               else if (segmentLength < 0)\r
+                                       throw new JpegException("segment size would be less than zero");\r
+                               byte[] segmentBytes = new byte[segmentLength];\r
+                               bStream.read(segmentBytes, 0, segmentLength);\r
+                               offset += segmentLength;\r
+                               if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF))\r
+                               {\r
+                                       // The 'Start-Of-Scan' segment comes last so break out of loop\r
+                                       break;\r
+                               }\r
+                               else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF))\r
+                               {\r
+                                       // the 'End-Of-Image' segment - should already have exited by now\r
+                                       break;\r
+                               }\r
+                               else\r
+                               {\r
+                                       segmentData.addSegment(thisSegmentMarker, segmentBytes);\r
+                               }\r
+                               // loop through to the next segment\r
+                       }\r
+                       while (true);\r
+               }\r
+               catch (FileNotFoundException fnfe)\r
+               {\r
+                       throw new JpegException("Jpeg file not found");\r
+               }\r
+               catch (IOException ioe)\r
+               {\r
+                       throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);\r
+               }\r
+               finally\r
+               {\r
+                       try\r
+                       {\r
+                               if (bStream != null) {\r
+                                       bStream.close();\r
+                               }\r
+                       }\r
+                       catch (IOException ioe) {\r
+                               throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);\r
+                       }\r
+               }\r
+               // Return the result\r
+               return segmentData;\r
+       }\r
+\r
+\r
+       /**\r
+        * Helper method that validates the Jpeg file's magic number.\r
+        * @param inMagic1 first half of magic number\r
+        * @param inMagic2 second half of magic number\r
+        * @throws JpegException if numbers do not match magic numbers expected\r
+        */\r
+       private static void checkMagicNumbers(int inMagic1, int inMagic2) throws JpegException\r
+       {\r
+               if (inMagic1 != MAGIC_JPEG_BYTE_1 || inMagic2 != MAGIC_JPEG_BYTE_2)\r
+               {\r
+                       throw new JpegException("not a jpeg file");\r
+               }\r
+       }\r
+}
\ No newline at end of file