]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/jpeg/drew/JpegSegmentReader.java
Version 18.6, December 2016
[GpsPrune.git] / tim / prune / jpeg / drew / JpegSegmentReader.java
1 package tim.prune.jpeg.drew;\r
2 \r
3 import java.io.*;\r
4 \r
5 /**\r
6  * Class to perform read functions of Jpeg files, returning specific file segments\r
7  * Based on Drew Noakes' Metadata extractor at http://drewnoakes.com\r
8  */\r
9 public class JpegSegmentReader\r
10 {\r
11         /** Start of scan marker */\r
12         private static final byte SEGMENT_SOS = (byte)0xDA;\r
13 \r
14         /** End of image marker */\r
15         private static final byte MARKER_EOI = (byte)0xD9;\r
16 \r
17         /** APP1 Jpeg segment identifier -- where Exif data is kept. */\r
18         private static final byte SEGMENT_APP1 = (byte)0xE1;\r
19 \r
20         /** Magic numbers to mark the beginning of all Jpegs */\r
21         private static final int MAGIC_JPEG_BYTE_1 = 0xFF;\r
22         private static final int MAGIC_JPEG_BYTE_2 = 0xD8;\r
23 \r
24 \r
25         /**\r
26          * Get the Exif data segment for the specified file\r
27          * @param inFile File to read\r
28          * @return Exif data segment as byte array\r
29          * @throws JpegException on file read errors or exif data errors\r
30          */\r
31         public static byte[] readExifSegment(File inFile) throws JpegException\r
32         {\r
33                 JpegSegmentData data = readSegments(inFile);\r
34                 return data.getSegment(SEGMENT_APP1);\r
35         }\r
36 \r
37 \r
38         /**\r
39          * Obtain the Jpeg segment data from the specified file\r
40          * @param inFile File to read\r
41          * @return Jpeg segment data from file\r
42          * @throws JpegException on file read errors or exif data errors\r
43          */\r
44         private static JpegSegmentData readSegments(File inFile) throws JpegException\r
45         {\r
46                 JpegSegmentData segmentData = new JpegSegmentData();\r
47                 BufferedInputStream bStream = null;\r
48 \r
49                 try\r
50                 {\r
51                         bStream = new BufferedInputStream(new FileInputStream(inFile));\r
52                         // first two bytes should be jpeg magic number\r
53                         final int magic1 = bStream.read() & 0xFF;\r
54                         final int magic2 = bStream.read() & 0xFF;\r
55                         if (magic1 != MAGIC_JPEG_BYTE_1 || magic2 != MAGIC_JPEG_BYTE_2) {\r
56                                 throw new JpegException("not a jpeg file");\r
57                         }\r
58 \r
59                         // Loop around segments found\r
60                         boolean foundExif = false;\r
61                         do\r
62                         {\r
63                                 // next byte is 0xFF\r
64                                 byte segmentIdentifier = (byte) (bStream.read() & 0xFF);\r
65                                 if ((segmentIdentifier & 0xFF) != 0xFF)\r
66                                 {\r
67                                         throw new JpegException("expected jpeg segment start 0xFF, not 0x"\r
68                                                 + Integer.toHexString(segmentIdentifier & 0xFF));\r
69                                 }\r
70                                 // next byte is <segment-marker>\r
71                                 byte thisSegmentMarker = (byte) (bStream.read() & 0xFF);\r
72                                 // next 2-bytes are <segment-size>: [high-byte] [low-byte]\r
73                                 byte[] segmentLengthBytes = new byte[2];\r
74                                 bStream.read(segmentLengthBytes, 0, 2);\r
75                                 int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF);\r
76                                 // segment length includes size bytes, so subtract two\r
77                                 segmentLength -= 2;\r
78                                 if (segmentLength > bStream.available())\r
79                                         throw new JpegException("segment size would extend beyond file stream length");\r
80                                 else if (segmentLength < 0)\r
81                                         throw new JpegException("segment size would be less than zero");\r
82                                 byte[] segmentBytes = new byte[segmentLength];\r
83                                 int bytesRead = bStream.read(segmentBytes, 0, segmentLength);\r
84                                 // Bail if not all bytes read in one go - otherwise following sections will be out of step\r
85                                 if (bytesRead != segmentLength) {\r
86                                         throw new JpegException("Tried to read " + segmentLength + " bytes but only got " + bytesRead);\r
87                                 }\r
88                                 if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF))\r
89                                 {\r
90                                         // The 'Start-Of-Scan' segment comes last so break out of loop\r
91                                         break;\r
92                                 }\r
93                                 else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF))\r
94                                 {\r
95                                         // the 'End-Of-Image' segment - should already have exited by now\r
96                                         break;\r
97                                 }\r
98                                 else\r
99                                 {\r
100                                         segmentData.addSegment(thisSegmentMarker, segmentBytes);\r
101                                 }\r
102                                 // loop through to the next segment if exif hasn't already been found\r
103                                 foundExif = (thisSegmentMarker == SEGMENT_APP1);\r
104                         }\r
105                         while (!foundExif);\r
106                 }\r
107                 catch (FileNotFoundException fnfe)\r
108                 {\r
109                         throw new JpegException("Jpeg file not found");\r
110                 }\r
111                 catch (IOException ioe)\r
112                 {\r
113                         throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);\r
114                 }\r
115                 finally\r
116                 {\r
117                         try\r
118                         {\r
119                                 if (bStream != null) {\r
120                                         bStream.close();\r
121                                 }\r
122                         }\r
123                         catch (IOException ioe) {\r
124                                 throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);\r
125                         }\r
126                 }\r
127                 // Return the result\r
128                 return segmentData;\r
129         }\r
130 }\r