Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
EXIF.cs
1using System;
3using System.IO;
7
9{
16 public static class EXIF
17 {
18 private static readonly Dictionary<int, ExifTagName> names = GetNames();
19
20 private static Dictionary<int, ExifTagName> GetNames()
21 {
22 Dictionary<int, ExifTagName> Names = new Dictionary<int, ExifTagName>();
23
24 foreach (ExifTagName Value in Enum.GetValues(typeof(ExifTagName)))
25 Names[(int)Value] = Value;
26
27 return Names;
28 }
29
36 public static bool TryParse(byte[] ExifData, out ExifTag[] Tags)
37 {
38 ExifReader Reader = new ExifReader(ExifData);
40
41 Tags = null;
42
43 // EXIF identifier
44 if (Reader.NextASCIIString() != "Exif")
45 return false;
46
47 // Padding
48 if (Reader.NextByte() < 0)
49 return false;
50
51 // Byte order
52 int RefPos = Reader.Position;
53 int i = Reader.NextSHORT();
54
55 switch (i)
56 {
57 case 0x4949:
58 Reader.BigEndian = false;
59 break;
60
61 case 0x4d4d:
62 Reader.BigEndian = true;
63 break;
64
65 default:
66 return false;
67 }
68
69 if (Reader.NextSHORT() != 0x2a)
70 return false;
71
72 // IFD 0 offset
73 uint? Offset = Reader.NextLONG();
74 if (!Offset.HasValue)
75 return false;
76
77 int Pos = RefPos + (int)Offset.Value;
78 if (Pos < Reader.Position || Pos >= Reader.Length)
79 return false;
80
81 Reader.Position = Pos;
82
83 do
84 {
85 int NrRecords = Reader.NextSHORT();
86 if (NrRecords < 0)
87 return false;
88
89 while (NrRecords > 0)
90 {
91 int TagID = Reader.NextSHORT();
92 if (TagID < 0)
93 return false;
94
95 if (!names.TryGetValue(TagID, out ExifTagName Name))
96 Name = ExifTagName.Unknown;
97
98 int Type = Reader.NextSHORT();
99 if (Type < 0)
100 return false;
101
102 uint? Count = Reader.NextLONG();
103 if (!Count.HasValue)
104 return false;
105
106 int ElementSize;
107 int NrElements = (int)Count.Value;
108
109 switch ((ExifTagType)Type)
110 {
111 case ExifTagType.ASCII:
112 ElementSize = 1;
113 NrElements = 1;
114 break;
115
116 case ExifTagType.BYTE:
117 case ExifTagType.UNDEFINED:
118 default:
119 ElementSize = 1;
120 break;
121
122 case ExifTagType.SHORT:
123 ElementSize = 2;
124 break;
125
126 case ExifTagType.LONG:
127 case ExifTagType.SLONG:
128 ElementSize = 4;
129 break;
130
131 case ExifTagType.RATIONAL:
132 case ExifTagType.SRATIONAL:
133 ElementSize = 8;
134 break;
135 }
136
137 int RecSize = (int)(Count.Value * ElementSize);
138 int PosBak;
139 int j;
140
141 if (RecSize <= 4)
142 PosBak = Reader.Position + 4;
143 else
144 {
145 uint? ValueOffset = Reader.NextLONG();
146 if (!ValueOffset.HasValue || RefPos + ValueOffset.Value > Reader.Length)
147 return false;
148
149 PosBak = Reader.Position;
150 Reader.Position = RefPos + (int)ValueOffset.Value;
151 }
152
153 switch ((ExifTagType)Type)
154 {
155 case ExifTagType.BYTE:
156 if (NrElements == 1)
157 {
158 i = Reader.NextByte();
159 if (i < 0)
160 return false;
161
162 List.Add(new ExifTypedTag<byte>(TagID, Name, (byte)i));
163 }
164 else
165 {
166 byte[] Items = new byte[NrElements];
167
168 for (j = 0; j < NrElements; j++)
169 {
170 i = Reader.NextByte();
171 if (i < 0)
172 return false;
173
174 Items[j] = (byte)i;
175 }
176
177 List.Add(new ExifTypedTag<byte[]>(TagID, Name, Items));
178 }
179 break;
180
181 case ExifTagType.ASCII:
182 string AsciiValue = Reader.NextASCIIString();
183 List.Add(new ExifAsciiTag(TagID, Name, AsciiValue));
184 break;
185
186 case ExifTagType.SHORT:
187 if (NrElements == 1)
188 {
189 i = Reader.NextSHORT();
190 if (i < 0)
191 return false;
192
193 List.Add(new ExifTypedTag<ushort>(TagID, Name, (ushort)i));
194 }
195 else
196 {
197 ushort[] Items = new ushort[NrElements];
198
199 for (j = 0; j < NrElements; j++)
200 {
201 i = Reader.NextSHORT();
202 if (i < 0)
203 return false;
204
205 Items[j] = (ushort)i;
206 }
207
208 List.Add(new ExifTypedTag<ushort[]>(TagID, Name, Items));
209 }
210 break;
211
212 case ExifTagType.LONG:
213 uint? u;
214
215 if (NrElements == 1)
216 {
217 u = Reader.NextLONG();
218 if (!u.HasValue)
219 return false;
220
221 List.Add(new ExifTypedTag<uint>(TagID, Name, u.Value));
222 }
223 else
224 {
225 uint[] Items = new uint[NrElements];
226
227 for (j = 0; j < NrElements; j++)
228 {
229 u = Reader.NextLONG();
230 if (!u.HasValue)
231 return false;
232
233 Items[j] = u.Value;
234 }
235
236 List.Add(new ExifTypedTag<uint[]>(TagID, Name, Items));
237 }
238 break;
239
240 case ExifTagType.RATIONAL:
241 if (NrElements == 1)
242 {
243 u = Reader.NextLONG();
244 if (!u.HasValue)
245 return false;
246
247 uint NumeratorValue = u.Value;
248
249 u = Reader.NextLONG();
250 if (!u.HasValue)
251 return false;
252
253 uint DenominatorValue = u.Value;
254 List.Add(new ExifTypedTag<Rational>(TagID, Name, new Rational(NumeratorValue, DenominatorValue)));
255 }
256 else
257 {
258 Rational[] Items = new Rational[NrElements];
259
260 for (j = 0; j < NrElements; j++)
261 {
262 u = Reader.NextLONG();
263 if (!u.HasValue)
264 return false;
265
266 uint NumeratorValue = u.Value;
267
268 u = Reader.NextLONG();
269 if (!u.HasValue)
270 return false;
271
272 uint DenominatorValue = u.Value;
273
274 Items[j] = new Rational(NumeratorValue, DenominatorValue);
275 }
276
277 List.Add(new ExifTypedTag<Rational[]>(TagID, Name, Items));
278 }
279 break;
280
281 case ExifTagType.UNDEFINED:
282 if (NrElements == 1)
283 {
284 i = Reader.NextByte();
285 if (i < 0)
286 return false;
287
288 List.Add(new ExifTypedTag<byte>(TagID, Name, (byte)i));
289 }
290 else
291 {
292 byte[] Items = new byte[NrElements];
293
294 for (j = 0; j < NrElements; j++)
295 {
296 i = Reader.NextByte();
297 if (i < 0)
298 return false;
299
300 Items[j] = (byte)i;
301 }
302
303 List.Add(new ExifTypedTag<byte[]>(TagID, Name, Items));
304 }
305 break;
306
307 case ExifTagType.SLONG:
308 if (NrElements == 1)
309 {
310 u = Reader.NextLONG();
311 if (!u.HasValue)
312 return false;
313
314 List.Add(new ExifTypedTag<int>(TagID, Name, (int)u.Value));
315 }
316 else
317 {
318 int[] Items = new int[NrElements];
319
320 for (j = 0; j < NrElements; j++)
321 {
322 u = Reader.NextLONG();
323 if (!u.HasValue)
324 return false;
325
326 Items[j] = (int)u.Value;
327 }
328
329 List.Add(new ExifTypedTag<int[]>(TagID, Name, Items));
330 }
331 break;
332
333 case ExifTagType.SRATIONAL:
334 if (NrElements == 1)
335 {
336 u = Reader.NextLONG();
337 if (!u.HasValue)
338 return false;
339
340 int NumeratorValue = (int)u.Value;
341
342 u = Reader.NextLONG();
343 if (!u.HasValue)
344 return false;
345
346 int DenominatorValue = (int)u.Value;
347 List.Add(new ExifTypedTag<SignedRational>(TagID, Name, new SignedRational(NumeratorValue, DenominatorValue)));
348 }
349 else
350 {
351 SignedRational[] Items = new SignedRational[NrElements];
352
353 for (j = 0; j < NrElements; j++)
354 {
355 u = Reader.NextLONG();
356 if (!u.HasValue)
357 return false;
358
359 int NumeratorValue = (int)u.Value;
360
361 u = Reader.NextLONG();
362 if (!u.HasValue)
363 return false;
364
365 int DenominatorValue = (int)u.Value;
366
367 Items[j] = new SignedRational(NumeratorValue, DenominatorValue);
368 }
369
370 List.Add(new ExifTypedTag<SignedRational[]>(TagID, Name, Items));
371 }
372 break;
373 }
374
375 Reader.Position = PosBak;
376 NrRecords--;
377 }
378
379 // Next IFD offset
380 Offset = Reader.NextLONG();
381 if (!Offset.HasValue)
382 return false;
383
384 if (Offset.Value == 0)
385 break;
386
387 Pos = RefPos + (int)Offset.Value;
388 if (Pos < Reader.Position || Pos >= Reader.Length)
389 return false;
390
391 Reader.Position = Pos;
392 }
393 while (true);
394
395 Tags = List.ToArray();
396 return true;
397 }
398
405 public static bool TryExtractFromJPeg(string FileName, out ExifTag[] Tags)
406 {
407 using (FileStream fs = File.OpenRead(FileName))
408 {
409 return TryExtractFromJPeg(fs, out Tags);
410 }
411 }
412
419 public static bool TryExtractFromJPeg(byte[] Image, out ExifTag[] Tags)
420 {
421 using (MemoryStream ms = new MemoryStream(Image))
422 {
423 return TryExtractFromJPeg(ms, out Tags);
424 }
425 }
426
433 public static bool TryExtractFromJPeg(Stream Image, out ExifTag[] Tags)
434 {
435 Tags = null;
436
437 if (Image.ReadByte() != 0xff)
438 return false;
439
440 if (Image.ReadByte() != 0xd8)
441 return false;
442
443 byte[] Buf = null;
444
445 do
446 {
447 if (!TryReadWord(Image, out ushort TagID))
448 return false;
449
450 if (!TryReadWord(Image, out ushort Len) || Len < 2)
451 return false;
452
453 Len -= 2;
454
455 switch (TagID)
456 {
457 case 0xffe1: //APP1
458 Buf = new byte[Len];
459
460 if (Image.TryReadAll(Buf, 0, Len) != Len)
461 return false;
462
463 return TryParse(Buf, out Tags);
464
465 default:
466 if (Image.CanSeek)
467 Image.Seek(Len, SeekOrigin.Current);
468 else
469 {
470 if (Buf is null || Buf.Length < Len)
471 Buf = new byte[Len];
472
473 if (Image.TryReadAll(Buf, 0, Len) != Len)
474 return false;
475 }
476 break;
477 }
478 }
479 while (true);
480 }
481
482 private static bool TryReadWord(Stream Input, out ushort w)
483 {
484 int i = Input.ReadByte();
485 if (i < 0)
486 {
487 w = 0;
488 return false;
489 }
490
491 w = (byte)i;
492
493 i = Input.ReadByte();
494 if (i < 0)
495 return false;
496
497 w <<= 8;
498 w |= (byte)i;
499
500 return true;
501 }
502
503 }
504}
Extracts EXIF meta-data from images.
Definition: EXIF.cs:17
static bool TryExtractFromJPeg(Stream Image, out ExifTag[] Tags)
Tries to extract EXIF meta-data from a JPEG stream.
Definition: EXIF.cs:433
static bool TryParse(byte[] ExifData, out ExifTag[] Tags)
Tries to parse EXIF data.
Definition: EXIF.cs:36
static bool TryExtractFromJPeg(byte[] Image, out ExifTag[] Tags)
Tries to extract EXIF meta-data from a JPEG image.
Definition: EXIF.cs:419
static bool TryExtractFromJPeg(string FileName, out ExifTag[] Tags)
Tries to extract EXIF meta-data from a JPEG image.
Definition: EXIF.cs:405
Class for ASCII-valued EXIF meta-data tags.
Definition: ExifAsciiTag.cs:7
uint? NextLONG()
Gets next LONG (unsigned int). If no more bytes are available, null is returned.
Definition: ExifReader.cs:107
string NextASCIIString()
Gets the next ASCII string.
Definition: ExifReader.cs:137
int NextByte()
Gets next byte. If no more bytes are available, -1 is returned.
Definition: ExifReader.cs:65
int Length
Length of data block
Definition: ExifReader.cs:43
int NextSHORT()
Gets next SHORT (unsigned short). If no more bytes are available, -1 is returned.
Definition: ExifReader.cs:77
Abstract base class for EXIF meta-data tags.
Definition: ExifTag.cs:10
Base class for typed EXIF meta-data tags.
Definition: ExifTypedTag.cs:7
Represents an EXIF RATIONAL value.
Definition: Rational.cs:7
Represents an EXIF SRATIONAL value.
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
T[] ToArray()
Returns an array containing all elements of the collection.
ExifTagName
Defined EXIF Tag names
Definition: ExifTagName.cs:10
ExifTagType
Data types of meta-data tags
Definition: ExifTagType.cs:7