-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathProgrammerGuide.Txt
341 lines (260 loc) · 14 KB
/
ProgrammerGuide.Txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
Programmer's Guide to dEXIF and dIPTC
Image Metadata Manipulators Written in Delphi/Lazarus
Gerry McGuire
mcguirez@hotmail.com
Copyright 2001-2006 - All Rights Reserved
Extended by Werner Pamler for an update to the Lazarus version
Theory of Operation:
An EXIF header is a multilevel binary structure with the following
characteristics: (Several details are glossed over here - a more complete
specification can be found at
http://www.pima.net/standards/iso/tc42/wg18/WG18_POW.htm )
* The image file is divided into sections. These sections are separated
by a marker byte followed by a section type byte.
* One of these sections is the "EXIF" section. This section is used by
the capturing device (camera) to record the various conditions and
settings in effect at the time of aquisition.
* Another of these sections is the "IPTC" section. IPTC stands for the
"International Press Telecommunications Council" and this section holds
metadata relevant for publishing an image. This section contains
data such as "Caption", "Byline", "Urgency" etc. Photoshop can read
and write to this area and is available under "Image Info..." on the
file menu.
* Another one of these sections is the "Comments" section. It basically
holds up to 64k of data. I believe there may be more than one Comments
section although dEXIF only handles the first one at this time.
* An EXIF section consists of a directory structure. There is only one
valid EXIF section per image. This section may also contain several
sub-sections which each contain their own directory.
* An EXIF directory consists of entries that consist of
a) a 16 bit field identifier (Tag)
b) a 16 bit type indicator
c) a 32 bit component count (used to determine the data length
d) If the data is 4 bytes in size or less, it is contained here
otherwise a 32 bit pointer to the data is contained here.
* One of the EXIF entries is the "MakerNote" entry. This information
varies by the manufacturer. Typically this is also a directory
structure that can be interpreted in the same fashion as the
EXIF directory.
* Typical jpegs have a JFIF section which is a brief 16 byte header.
* An EXIF section may be substituted for a JFIF section but there must
be one or the other. If a JFIF section exists it must be first, an
EXIF entry must also be first unless there is also a JFIF section
in which case the EXIF section must be second. (Anyone remember
Monty Python's holy hand grenade incantation?)
* A jpeg, using Borland's jepg unit, will not be altered unless
the compress method is called. That is, no degradation of the
image is caused by continually reading and writing via this
library.
* If a thumbnail is present, a pointer to the thumbnail block
occupies the 32 bytes immediately after the EXIF IFD0 directory.
In some formats (NIKON) there may be information following the
thumbnail block but still in the EXIF segment.
* The routines read the EXIF information from the file, copy it to
a buffer, and create the TAG list from the buffer.
File --------> EXIF Buffer ---------> Tag list ----------> User
In contrast to the original version of dExif, the EXIF buffer is
used only in a transient way during reading. User interaction occurs
via the tag list, and the changed tags can be written back to file.
This has the advantage over the old buffer method that new tags can be
added easily, and tag data can be extended beyond their original size
in the file.
The old methods WriteThruInt and WriteThruString were removed.
Incompatible changes to the original version
TImgData.CreateCommentSegment, .GetCommentSegment and .GetCommentStr are
protected now. Use the property TImgData.Comment to read and write
a comment.
TImageInfo.WriteThruInt and .WriteThruString were removed. Changing of
tags is done more naturally by using the properties of TImageInfo.
Sample Code:
dEXIF handles the major sections by parsing the initial image header and
putting each into it's own buffer. This is currently accomplished by the
dEXIF unit which also processes the EXIF data. The dIPTC processes the
IPTC section - surprise~!
An IPTC segment and a Comments segment can be added to an arbitrary
jpeg file, but an EXIF structure cannot be added. This is because
EXIF contains mostly camera setting etc, that cannot be meaningfully
generated. An EXIF structure can be copied over, presumably from a
file that was the root source of the targeted image file.
Sample Code [EXIF Section]:
The following examples assume this declaration:
Var ImgData: TImgData;
Begin
ImgData := TImgData.Create;
Open a file, extract the simple EXIF string summary:
// The following returns true if successful. Will parse both
// jpgs and tiff files.
if ImgData.ProcessFile('filename.jpg') then
if ImgData.HasExif then
MyString := ImgData.ExifObj.toString();
Open a file, extract a more detailed EXIF data summary:
// One string with embedded (CR/LF) breaks.
if ImgData.ProcessFile('filename.jpg') then
if ImgData.HasExif then
MyString := ImgData.ExifObj.ToLongString();
Open a file, extract all EXIF data, interpreted (decoded) with
all possible detail.
dExifDecode := true; // true by default - translates literals
// into something human-readable
ImgData.TraceLevel := 1; // set detail level for trace string
// One string with embedded (CR/LF) breaks.
if ImgData.ProcessFile('filename.jpg') then
if ImgData.HasExif then
// Parse this to extract specific data
MyTraceString := ImgData.ExifObj.TraceString
Some of the more common fields are accessible as properties of the
EXIFObj of the ImgData.
With ImgData do
if ProcessFile('filename.jpg') and HasExif then // Read in file
begin
// These first 3 are read from the OS
MyFileName := ExifObj.Filename;
FileDate := ExifObj.FileDateTime; // tDateTime
FileSize := ExifObj.FileSize; // longint
// These are extracted from the EXIF data of the image
Make := ExifObj.CameraMake; // string
Model := ExifObj.CameraModel; // string
DateTime := ExifObj.DateTime; // string
ImgDescr := ExifObj.ImageDescription; // string
ExifCmnt := ExifObj.ExifComment; // string
Author := ExifObj.Author; // string
Copyright := ExifObj.Copyright; // string
Height := ExifObj.Height; // integer
Width := ExifObj.Width; // integer
IsColor := ExifObj.IsColor; // integer
Process := ExifObj.Process; // integer
FlashUsed := ExifObj.FlashUsed; // integer
ImgDate := ExifObj.DateTimeOriginal; // TDateTime - when picture was taken
// or ExifObj.DateTimeDigitized; // ... when picture was digitized
// or ExifOjb.DateTimeModified; // ... when picture was modified
// --- normally all three date/times are the same
end;
Other EXIF field can be read by using the property TagValue and specifying
the name of the EXIF property - see https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html.
The return value of this property is a variant, i.e. it can be assigned to
most simple data types. If a tag not available then a NULL value is returned,
use VasIsNull() to check this.
With ImgData do
if ProcessFile('filename.jpg') and HasExif then
begin
Orientation := ExifObj.TagValue['Orientation']; // integer
ISO := ExifObj.TagValue['ISO']; // integer
SubjectDistance := ExifObj.TagValue['SubjectDistance']; // double
TimeZone := ExifObj.TagValue['OffsetTimeOriginal']; // string
end;
Conversely, tag values can be modified like this. Here we add 1 hour
to the date/time when the picture was taken. This is a typical correction
for daylight-saving-time:
with ImgData do
if ProcessFile('filename.jpg') and HasExif then
begin
imgDate := ExifObj.DateTimeOriginal; // TDateTime
oneHour := EncodeTime(1, 0, 0, 0); // TDateTime
ExifObj.DateTimeOriginal := imgDate + oneHour;
ExifObj.DateTimeDigitized := imgDate + oneHour;
ExifObj.DateTimeModified := imgDate + oneHour;
//or:
ExifObj.TagValue['DateTimeOriginal'] := ImgDate + oneHour;
// etc...
end;
Open a file, manipulate the image or its EXIF data and save to a new file
adjusting the EXIF size for the new file:
// DELPHI ONLY
// var MyImage:TJpegImage;
if ImgData.ProcessFile('FileName.jpg') then
begin
MyImage.ReadFromFile('FileName.jpg);
// manipulate image here
ImgData.WriteEXIF(MyImage,'NewFile.jpg');
end;
In order to avoid usage of the LCL in the Lazarus version the TJpegImage
cannot be used. Theefore, the method WriteEXIF was modified to read the
data from a stream:
// LAZARUS-ONLY
// var stream: TMemoryStream;
if ImgData.ProcessFile('Filename.jpg') then
begin
// manipulate image here
// ...
stream := TMemoryStream.Create;
try
stream.LoadfromFile('NewFile.jpg');
ImgData.WriteEXIF(stream, 'NewFile.jpg');
finally
stream.Free;
end;
end;
Open a file and save to a new file removing all metadata:
// var MyImage:TJpegImage;
if ImgData.ProcessFile('FileName.jpg') then
begin
ImgData.ClearSections(); // Get rid of all MetaData
ImgData.WriteEXIF('NewFile.jpg'); // When only one name is specified,
end; // The last file read is used as source.
Sample Code [Comments Section]:
The comments section can hold up to 64k bytes of data.
Don't confuse it with the UserComments tag of the EXIF
segament, which is fixed by the camera and is much shorter.
Open a jpeg file and extract the comments section
into a string. note that the comment section can
contain binary data - it's not necessarily a string:
ImgData.ProcessFile('filename.jpg'); // calls the EXIF parsing routine
if ImgData.HasComment then // there was a Comment Section
MyString := ImgData.Comment;
Open a file, do nothing other than add
a TStringList to the comment section and save:
ImgData.ProcessFile('filename.jpg'); // calls the EXIF parsing routine
// The following call will overwrite
ImgData.MakeCommentSegment(MyString); // or add a comment section
Sample Code [IPTC Section]:
Read IPTC strings from a jpeg file:
// We're not interested to deal with EXIF data
StringList := ImgData.IPTCObj.ReadFileStrings('filename.jpg');
Read IPTC data into dIPTC for further manipulation,
check the copyright tag, add it if it doesn't exisit
and save the file:
// normally use a WITH block but this IS an example:
if ImgData.IPTCObj.ReadFile('filename.jpg') then
begin // file parsed OK - and there was actual data
if ImgData.IPTCObj.GetTag('Copyright') = '' then
begin
ImgData.IPTCObj.AddTag('Copyright','Copyright 2004 - Gerry McGuire');
ImgData.IPTCObj.WriteFile(); // defaults to last ReadFile value
end
end;
Adding IPTC data to a generated image which does
not yet exist as a file: (typically you'd use a
With ImgData.IPTCObj... statement)
ImgData.IPTCObj.Reset; // clear out old values
ImgData.IPTCObj.AddTag('Copyright','Copyright 2004 - Gerry McGuire');
ImgData.IPTCObj.AddTag('Keywords','Software, Sample');
// To add a keyword without removing earlier values
ImgData.IPTCObj.AppendToTag('Keywords','Another');
ImgData.IPTCObj.AddTag('OriginatingProgram','dEXIF Version 1.2c');
ImgData.IPTCObj.WriteFile('newfile.jpg',NewJpgImage); // Last argument is TJpegImage
Read in a file and save any ITPC data as XML:
ImgData.IPTCObj.ReadFile('filename.jpg'); // as an alternative, you can
if ImgData.IPTCObj.HasData then // check for data after the ReadFile
ImgData.IPTCObj.IPTCArrayToXML('filename.xml');
Note that the EXIF section has a similar function and that the ImgData
object has a method ( MetaDataToXML() ) which returns a stringlist that
contains the XML of both segments.
Read all of the currently defined tags and their
labels into a stringgrid. The data array is
TIPTCdata's default value.
// var grid: tstringgrid; i:integer;
grid.colcount := 2;
grid.rowcount := ImgData.IPTCObj.count;
for i := 0 to ImgData.IPTCObj.Count-1 do
begin
grid.cells[0,i] := ImgData.IPTCObj[i].desc; // get label value
grid.cells[1,i] := ImgData.IPTCObj[i].data; // get actual data
end;
Write a file containing all the user tag descriptions:
ImgData.IPTCObj.IPTCWriteTransFile( 'translate.txt');
Read in file containing all the user tag descriptions (may be used
for translating UI into a non-English language). Perform this in
a form create method or before any images are read in.
ImgData.IPTCObj.IPTCWReadTransFile( 'translate.txt');
See the IPTCView application bundled with this source code
for a more detailed example then those presented above.