1 package tim.prune.jpeg.drew;
\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
9 public class JpegSegmentReader
\r
11 /** Start of scan marker */
\r
12 private static final byte SEGMENT_SOS = (byte)0xDA;
\r
14 /** End of image marker */
\r
15 private static final byte MARKER_EOI = (byte)0xD9;
\r
17 /** APP1 Jpeg segment identifier -- where Exif data is kept. */
\r
18 private static final byte SEGMENT_APP1 = (byte)0xE1;
\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
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
31 public static byte[] readExifSegment(File inFile) throws JpegException
\r
33 JpegSegmentData data = readSegments(inFile);
\r
34 return data.getSegment(SEGMENT_APP1);
\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
44 private static JpegSegmentData readSegments(File inFile) throws JpegException
\r
46 JpegSegmentData segmentData = new JpegSegmentData();
\r
47 BufferedInputStream bStream = null;
\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
59 // Loop around segments found
\r
60 boolean foundExif = false;
\r
63 // next byte is 0xFF
\r
64 byte segmentIdentifier = (byte) (bStream.read() & 0xFF);
\r
65 if ((segmentIdentifier & 0xFF) != 0xFF)
\r
67 throw new JpegException("expected jpeg segment start 0xFF, not 0x"
\r
68 + Integer.toHexString(segmentIdentifier & 0xFF));
\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
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
88 if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF))
\r
90 // The 'Start-Of-Scan' segment comes last so break out of loop
\r
93 else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF))
\r
95 // the 'End-Of-Image' segment - should already have exited by now
\r
100 segmentData.addSegment(thisSegmentMarker, segmentBytes);
\r
102 // loop through to the next segment if exif hasn't already been found
\r
103 foundExif = (thisSegmentMarker == SEGMENT_APP1);
\r
105 while (!foundExif);
\r
107 catch (FileNotFoundException fnfe)
\r
109 throw new JpegException("Jpeg file not found");
\r
111 catch (IOException ioe)
\r
113 throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
\r
119 if (bStream != null) {
\r
123 catch (IOException ioe) {
\r
124 throw new JpegException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
\r
127 // Return the result
\r
128 return segmentData;
\r