1 namespace Beagle
.Util
{
3 public class JpegHeader
{
4 public enum JpegMarker
{
5 // The Following tags are listed as containing the content within the
6 // marker itself and the data is stored in the two bytes that would
7 // otherwise hold the length.
8 Tem
= 0x01, // no length, ignore
9 Rst0
= 0xd0, // RstN used for resync, ignore
18 Sof0
= 0xc0, // SOFn Start of frame 0-1 common
23 Dht
= 0xc4, // Define Huffman Table
29 Jpg
= 0xc8, // reserved
39 // These tags all consist of a marker and then a length.
41 // These are the major structure tags.
42 Soi
= 0xd8, // Start of Image
43 Eoi
= 0xd9, // End of Image
44 Sos
= 0xda, // Start of Scan
47 Dri
= 0xdd, // Define restart interval
51 Dqt
= 0xdb, // Define Quantization Table
53 // These are the app marker tags that contain the application metadata
54 // in its various forms.
55 App0
= 0xe0, // AppN Markers for application data
91 public Marker (JpegMarker type
, byte [] data
, long position
)
95 this.Position
= position
;
98 public JpegMarker Type
;
100 public long Position
;
103 public byte [] GetRawXmp ()
105 return this.GetRaw ("http://ns.adobe.com/xap/1.0/");
108 public byte [] GetRawExif ()
110 return this.GetRaw ("Exif");
112 public byte [] GetRawJfif ()
114 return this.GetRaw ("JFIF");
117 public byte [] GetJFIFComment ()
119 return this.GetRaw ("COM");
122 public byte [] GetRawIcc ()
124 return this.GetRaw ("ICC_PROFILE");
127 public byte [] GetRaw (string name
)
129 Marker m
= (Marker
)app_marker_hash
[name
];
136 private void AddNamed (Marker m
)
139 for (j
= 0; j
< m
.Data
.Length
; j
++) {
140 if (m
.Data
[j
] == 0x00)
144 string header
= System
.Text
.Encoding
.ASCII
.GetString (m
.Data
, 0, j
);
145 app_marker_hash
[header
] = m
;
146 //System.Console.WriteLine (header);
149 System
.Collections
.Hashtable app_marker_hash
= new System
.Collections
.Hashtable ();
150 System
.Collections
.ArrayList marker_list
= new System
.Collections
.ArrayList ();
152 public JpegHeader (string filename
)
154 //System.Console.WriteLine ("opening {0}", filename);
155 System
.IO
.FileStream stream
= new System
.IO
.FileStream (filename
, System
.IO
.FileMode
.Open
);
156 LoadFromStream (stream
);
160 public JpegHeader (System
.IO
.Stream stream
)
162 LoadFromStream (stream
);
165 private void LoadFromStream (System
.IO
.Stream stream
)
167 byte [] length_data
= new byte [2];
169 if (stream
.ReadByte () != 0xff || (JpegMarker
)stream
.ReadByte () != JpegMarker
.Soi
)
170 throw new System
.Exception ("Invalid file Type, not a JPEG file");
172 bool at_image
= false;
177 // 0xff can be used as padding between markers, ignore it all
180 marker
= (JpegMarker
)stream
.ReadByte ();
182 } while (marker
== (JpegMarker
)0xff);
186 throw new System
.Exception ("Invalid Marker");
188 long position
= stream
.Position
- 1;
190 // FIXME use real byteswapping later
191 if (System
.BitConverter
.IsLittleEndian
) {
192 length_data
[1] = (byte)stream
.ReadByte ();
193 length_data
[0] = (byte)stream
.ReadByte ();
195 length_data
[0] = (byte)stream
.ReadByte ();
196 length_data
[1] = (byte)stream
.ReadByte ();
199 ushort length
= System
.BitConverter
.ToUInt16 (length_data
, 0);
200 //System.Console.WriteLine ("Marker {0} Length = {1}", marker.ToString (), length);
203 throw new System
.Exception ("Invalid Marker length");
209 byte [] commentdata
= new byte [length
];
210 if (stream
.Read (commentdata
, 0, length
) != length
)
211 throw new System
.Exception ("Incomplete Marker");
212 Marker m1
= new Marker (marker
, commentdata
, position
);
213 marker_list
.Add (m1
);
214 app_marker_hash
["COM"] = m1
;
216 case JpegMarker
.App0
:
217 case JpegMarker
.App1
:
218 case JpegMarker
.App2
:
219 case JpegMarker
.App3
:
220 case JpegMarker
.App4
:
221 case JpegMarker
.App5
:
222 case JpegMarker
.App6
:
223 case JpegMarker
.App7
:
224 case JpegMarker
.App8
:
225 case JpegMarker
.App9
:
226 case JpegMarker
.App10
:
227 case JpegMarker
.App11
:
228 case JpegMarker
.App12
:
229 case JpegMarker
.App13
:
230 case JpegMarker
.App14
:
231 case JpegMarker
.App15
:
232 byte [] data
= new byte [length
];
233 if (stream
.Read (data
, 0, length
) != length
)
234 throw new System
.Exception ("Incomplete Marker");
236 Marker m
= new Marker (marker
, data
, position
);
240 case JpegMarker
.Rst0
:
241 case JpegMarker
.Rst1
:
242 case JpegMarker
.Rst2
:
243 case JpegMarker
.Rst3
:
244 case JpegMarker
.Rst4
:
245 case JpegMarker
.Rst5
:
246 case JpegMarker
.Rst6
:
247 case JpegMarker
.Rst7
:
249 // These markers have no data it is in length_data
251 marker_list
.Add (new Marker (marker
, length_data
, position
));
254 byte [] d
= new byte [length
];
255 if (stream
.Read (d
, 0, length
) != length
)
256 throw new System
.Exception ("Incomplete Marker");
258 marker_list
.Add (new Marker (marker
, d
, position
));
262 if (marker
== JpegMarker
.Sos
)
268 public static int Main (string [] args
)
270 JpegHeader data
= new JpegHeader (args
[0]);
271 byte [] value = data
.GetRawXmp ();
274 string xml
= System
.Text
.Encoding
.UTF8
.GetString (value, 29, value.Length
- 29);
275 System
.Console
.WriteLine (xml
);
278 value = data
.GetRaw ("ICC_PROFILE");
280 System
.IO
.FileStream stream
= new System
.IO
.FileStream ("profile.icc", System
.IO
.FileMode
.Create
);
281 stream
.Write (value, 12, value.Length
- 12);
285 value = data
.GetRawExif ();
287 System
.IO
.MemoryStream stream
= new System
.IO
.MemoryStream (value, 6, value.Length
- 6);
288 Tiff
.Header tiff
= new Tiff
.Header (stream
);