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 [] GetRawIcc ()
119 return this.GetRaw ("ICC_PROFILE");
122 public byte [] GetRaw (string name
)
124 Marker m
= (Marker
)app_marker_hash
[name
];
131 private void AddNamed (Marker m
)
134 for (j
= 0; j
< m
.Data
.Length
; j
++) {
135 if (m
.Data
[j
] == 0x00)
139 string header
= System
.Text
.Encoding
.ASCII
.GetString (m
.Data
, 0, j
);
140 app_marker_hash
[header
] = m
;
141 //System.Console.WriteLine (header);
144 System
.Collections
.Hashtable app_marker_hash
= new System
.Collections
.Hashtable ();
145 System
.Collections
.ArrayList marker_list
= new System
.Collections
.ArrayList ();
147 public JpegHeader (string filename
)
149 //System.Console.WriteLine ("opening {0}", filename);
150 System
.IO
.FileStream stream
= new System
.IO
.FileStream (filename
, System
.IO
.FileMode
.Open
);
151 LoadFromStream (stream
);
155 public JpegHeader (System
.IO
.Stream stream
)
157 LoadFromStream (stream
);
160 private void LoadFromStream (System
.IO
.Stream stream
)
162 byte [] length_data
= new byte [2];
164 if (stream
.ReadByte () != 0xff || (JpegMarker
)stream
.ReadByte () != JpegMarker
.Soi
)
165 throw new System
.Exception ("Invalid file Type, not a JPEG file");
167 bool at_image
= false;
172 // 0xff can be used as padding between markers, ignore it all
175 marker
= (JpegMarker
)stream
.ReadByte ();
177 } while (marker
== (JpegMarker
)0xff);
181 throw new System
.Exception ("Invalid Marker");
183 long position
= stream
.Position
- 1;
185 // FIXME use real byteswapping later
186 if (System
.BitConverter
.IsLittleEndian
) {
187 length_data
[1] = (byte)stream
.ReadByte ();
188 length_data
[0] = (byte)stream
.ReadByte ();
190 length_data
[0] = (byte)stream
.ReadByte ();
191 length_data
[1] = (byte)stream
.ReadByte ();
194 ushort length
= System
.BitConverter
.ToUInt16 (length_data
, 0);
195 //System.Console.WriteLine ("Marker {0} Length = {1}", marker.ToString (), length);
198 throw new System
.Exception ("Invalid Marker length");
203 case JpegMarker
.App0
:
204 case JpegMarker
.App1
:
205 case JpegMarker
.App2
:
206 case JpegMarker
.App3
:
207 case JpegMarker
.App4
:
208 case JpegMarker
.App5
:
209 case JpegMarker
.App6
:
210 case JpegMarker
.App7
:
211 case JpegMarker
.App8
:
212 case JpegMarker
.App9
:
213 case JpegMarker
.App10
:
214 case JpegMarker
.App11
:
215 case JpegMarker
.App12
:
216 case JpegMarker
.App13
:
217 case JpegMarker
.App14
:
218 case JpegMarker
.App15
:
219 byte [] data
= new byte [length
];
220 if (stream
.Read (data
, 0, length
) != length
)
221 throw new System
.Exception ("Incomplete Marker");
223 Marker m
= new Marker (marker
, data
, position
);
227 case JpegMarker
.Rst0
:
228 case JpegMarker
.Rst1
:
229 case JpegMarker
.Rst2
:
230 case JpegMarker
.Rst3
:
231 case JpegMarker
.Rst4
:
232 case JpegMarker
.Rst5
:
233 case JpegMarker
.Rst6
:
234 case JpegMarker
.Rst7
:
236 // These markers have no data it is in length_data
238 marker_list
.Add (new Marker (marker
, length_data
, position
));
241 byte [] d
= new byte [length
];
242 if (stream
.Read (d
, 0, length
) != length
)
243 throw new System
.Exception ("Incomplete Marker");
245 marker_list
.Add (new Marker (marker
, d
, position
));
249 if (marker
== JpegMarker
.Sos
)
255 public static int Main (string [] args
)
257 JpegHeader data
= new JpegHeader (args
[0]);
258 byte [] value = data
.GetRawXmp ();
261 string xml
= System
.Text
.Encoding
.UTF8
.GetString (value, 29, value.Length
- 29);
262 System
.Console
.WriteLine (xml
);
265 value = data
.GetRaw ("ICC_PROFILE");
267 System
.IO
.FileStream stream
= new System
.IO
.FileStream ("profile.icc", System
.IO
.FileMode
.Create
);
268 stream
.Write (value, 12, value.Length
- 12);
272 value = data
.GetRawExif ();
274 System
.IO
.MemoryStream stream
= new System
.IO
.MemoryStream (value, 6, value.Length
- 6);
275 Tiff
.Header tiff
= new Tiff
.Header (stream
);