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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
|
Copyright (C) 2009 Volker Grabsch
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject
to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Fri Jul 3 11:44:14 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* avoid_extended_Photoshop_IRB_warnings
Fri Jun 19 17:57:06 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* handle_extended_Photoshop_IRBs
Thu Jun 4 20:12:36 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* recognize_small_corrupt_IRBs
Thu Jun 4 15:48:36 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* new_function_Photoshop_valid
Thu Jun 4 13:05:49 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* skip_writing_redundant_IPTC_IRBs
Tue May 19 16:03:52 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* read_and_modify_only_the_first_XMP_segment
Tue May 19 14:05:38 CEST 2009 Volker Grabsch <vog@notjusthosting.com>
* handle_empty_IRB
diff -rN -u old-trunk-1/src/jpgimage.cpp new-trunk-1/src/jpgimage.cpp
--- old-trunk-1/src/jpgimage.cpp 2009-06-25 17:10:32.000000000 +0200
+++ new-trunk-1/src/jpgimage.cpp 2009-06-25 17:10:32.000000000 +0200
@@ -87,6 +87,23 @@
const char Photoshop::bimId_[] = "8BIM";
const uint16_t Photoshop::iptc_ = 0x0404;
+ bool Photoshop::valid(const byte* pPsData,
+ long sizePsData)
+ {
+ const byte *record = 0;
+ uint32_t sizeIptc = 0;
+ uint32_t sizeHdr = 0;
+ const byte* pCur = pPsData;
+ const byte* pEnd = pPsData + sizePsData;
+ int ret = 0;
+ while (pCur < pEnd
+ && 0 == (ret = Photoshop::locateIptcIrb(pCur, static_cast<long>(pEnd - pCur),
+ &record, &sizeHdr, &sizeIptc))) {
+ pCur = record + sizeHdr + sizeIptc + (sizeIptc & 1);
+ }
+ return ret >= 0;
+ }
+
// Todo: Generalised from JpegBase::locateIptcData without really understanding
// the format (in particular the header). So it remains to be confirmed
// if this also makes sense for psTag != Photoshop::iptc
@@ -106,7 +123,7 @@
std::cerr << "Photoshop::locateIrb: ";
#endif
// Data should follow Photoshop format, if not exit
- while ( position <= sizePsData - 14
+ while ( position <= sizePsData - 12
&& memcmp(pPsData + position, Photoshop::bimId_, 4) == 0) {
const byte *hrd = pPsData + position;
position += 4;
@@ -121,8 +138,8 @@
position += psSize;
if (position + 4 > sizePsData) {
#ifndef SUPPRESS_WARNINGS
- std::cerr << "Error: "
- << "Invalid Photoshop IRB\n";
+ std::cerr << "Warning: "
+ << "Invalid or extended Photoshop IRB\n";
#endif
return -2;
}
@@ -130,9 +147,9 @@
position += 4;
if (dataSize > static_cast<uint32_t>(sizePsData - position)) {
#ifndef SUPPRESS_WARNINGS
- std::cerr << "Error: "
+ std::cerr << "Warning: "
<< "Invalid Photoshop IRB data size "
- << dataSize << "\n";
+ << dataSize << " or extended Photoshop IRB\n";
#endif
return -2;
}
@@ -158,6 +175,13 @@
#ifdef DEBUG
std::cerr << "pPsData doesn't start with '8BIM'\n";
#endif
+ if (position < sizePsData) {
+#ifndef SUPPRESS_WARNINGS
+ std::cerr << "Warning: "
+ << "Invalid or extended Photoshop IRB\n";
+#endif
+ return -2;
+ }
return 3;
} // Photoshop::locateIrb
@@ -210,15 +234,23 @@
// Data is padded to be even (but not included in size)
if (rawIptc.size_ & 1) psBlob.push_back(0x00);
}
- // Write existing stuff after record, data is rounded to be even.
- const uint32_t sizeOldData = sizeHdr + sizeIptc + (sizeIptc & 1);
- // Note: Because of the rounding, sizeFront + sizeOldData can be
- // _greater_ than sizePsData by 1 (not just equal), if the original
- // data was not padded.
- if (static_cast<uint32_t>(sizePsData) > sizeFront + sizeOldData) {
- append(psBlob, record + sizeOldData,
- sizePsData - sizeFront - sizeOldData);
+ // Write existing stuff after record,
+ // skip the current and all remaining IPTC blocks
+ long pos = sizeFront;
+ while (0 == Photoshop::locateIptcIrb(pPsData + pos, sizePsData - pos,
+ &record, &sizeHdr, &sizeIptc)) {
+ const long newPos = static_cast<long>(record - pPsData);
+ // Copy data up to the IPTC IRB
+ if (newPos > pos) {
+ append(psBlob, pPsData + pos, newPos - pos);
+ }
+ // Skip the IPTC IRB
+ pos = newPos + sizeHdr + sizeIptc + (sizeIptc & 1);
+ }
+ if (pos < sizePsData) {
+ append(psBlob, pPsData + pos, sizePsData - pos);
}
+ // Data is rounded to be even
if (psBlob.size() > 0) rc = DataBuf(&psBlob[0], static_cast<long>(psBlob.size()));
#ifdef DEBUG
std::cerr << "IRB block at the end of Photoshop::setIptcIrb\n";
@@ -281,9 +313,10 @@
const long bufMinSize = 36;
long bufRead = 0;
DataBuf buf(bufMinSize);
- Blob iptcBlob;
- bool foundPsData = false;
+ Blob psBlob;
+ bool foundCompletePsData = false;
bool foundExifData = false;
+ bool foundXmpData = false;
// Read section marker
int marker = advanceToMarker();
@@ -297,15 +330,6 @@
if (bufRead < 2) throw Error(15);
uint16_t size = getUShort(buf.pData_, bigEndian);
- if (foundPsData && marker != app13_) {
- // For IPTC, decrement search only after all app13 segments are
- // loaded, assuming they all appear in sequence. But decode IPTC
- // data after the loop, in case an app13 is the last segment
- // before sos or eoi.
- foundPsData = false;
- if (--search == 0) break;
- }
-
if ( !foundExifData
&& marker == app1_ && memcmp(buf.pData_ + 2, exifId_, 6) == 0) {
if (size < 8) {
@@ -328,7 +352,8 @@
--search;
foundExifData = true;
}
- else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
+ else if ( !foundXmpData
+ && marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
if (size < 31) {
rc = 6;
break;
@@ -345,9 +370,10 @@
#endif
}
--search;
+ foundXmpData = true;
}
- else if ( marker == app13_
- && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
+ else if ( !foundCompletePsData
+ && marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
if (size < 16) {
rc = 2;
break;
@@ -357,32 +383,17 @@
DataBuf psData(size - 16);
io_->read(psData.pData_, psData.size_);
if (io_->error() || io_->eof()) throw Error(14);
- const byte *record = 0;
- uint32_t sizeIptc = 0;
- uint32_t sizeHdr = 0;
#ifdef DEBUG
std::cerr << "Found app13 segment, size = " << size << "\n";
//hexdump(std::cerr, psData.pData_, psData.size_);
#endif
- // Find actual IPTC data within the APP13 segment
- const byte* pEnd = psData.pData_ + psData.size_;
- const byte* pCur = psData.pData_;
- while ( pCur < pEnd
- && 0 == Photoshop::locateIptcIrb(pCur,
- static_cast<long>(pEnd - pCur),
- &record,
- &sizeHdr,
- &sizeIptc)) {
- if (sizeIptc) {
-#ifdef DEBUG
- std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n";
-#endif
- append(iptcBlob, record + sizeHdr, sizeIptc);
- }
- pCur = record + sizeHdr + sizeIptc;
- pCur += (sizeIptc & 1);
+ // Append to psBlob
+ append(psBlob, psData.pData_, psData.size_);
+ // Check whether psBlob is complete
+ if (Photoshop::valid(&psBlob[0], psBlob.size())) {
+ --search;
+ foundCompletePsData = true;
}
- foundPsData = true;
}
else if (marker == com_ && comment_.empty())
{
@@ -437,6 +448,24 @@
}
} // while there are segments to process
+ // Find actual IPTC data within the psBlob
+ Blob iptcBlob;
+ const byte *record = 0;
+ uint32_t sizeIptc = 0;
+ uint32_t sizeHdr = 0;
+ const byte* pCur = &psBlob[0];
+ const byte* pEnd = pCur + psBlob.size();
+ while ( pCur < pEnd
+ && 0 == Photoshop::locateIptcIrb(pCur, static_cast<long>(pEnd - pCur),
+ &record, &sizeHdr, &sizeIptc)) {
+#ifdef DEBUG
+ std::cerr << "Found IPTC IRB, size = " << sizeIptc << "\n";
+#endif
+ if (sizeIptc) {
+ append(iptcBlob, record + sizeHdr, sizeIptc);
+ }
+ pCur = record + sizeHdr + sizeIptc + (sizeIptc & 1);
+ }
if ( iptcBlob.size() > 0
&& IptcParser::decode(iptcData_,
&iptcBlob[0],
@@ -489,9 +518,10 @@
int comPos = 0;
int skipApp1Exif = -1;
int skipApp1Xmp = -1;
- int skipApp13Ps3 = -1;
+ bool foundCompletePsData = false;
+ std::vector<int> skipApp13Ps3;
int skipCom = -1;
- DataBuf psData;
+ Blob psBlob;
DataBuf rawExif;
// Write image header
@@ -526,24 +556,31 @@
io_->read(rawExif.pData_, rawExif.size_);
if (io_->error() || io_->eof()) throw Error(22);
}
- else if (marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
+ else if ( skipApp1Xmp == -1
+ && marker == app1_ && memcmp(buf.pData_ + 2, xmpId_, 29) == 0) {
if (size < 31) throw Error(22);
skipApp1Xmp = count;
++search;
if (io_->seek(size-bufRead, BasicIo::cur)) throw Error(22);
}
- else if (marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
+ else if ( !foundCompletePsData
+ && marker == app13_ && memcmp(buf.pData_ + 2, Photoshop::ps3Id_, 14) == 0) {
#ifdef DEBUG
std::cerr << "Found APP13 Photoshop PS3 segment\n";
#endif
if (size < 16) throw Error(22);
- skipApp13Ps3 = count;
- ++search;
+ skipApp13Ps3.push_back(count);
io_->seek(16 - bufRead, BasicIo::cur);
- psData.alloc(size - 16);
// Load PS data now to allow reinsertion at any point
+ DataBuf psData(size - 16);
io_->read(psData.pData_, size - 16);
if (io_->error() || io_->eof()) throw Error(20);
+ // Append to psBlob
+ append(psBlob, psData.pData_, psData.size_);
+ // Check whether psBlob is complete
+ if (Photoshop::valid(&psBlob[0], psBlob.size())) {
+ foundCompletePsData = true;
+ }
}
else if (marker == com_ && skipCom == -1) {
if (size < 2) throw Error(22);
@@ -583,6 +620,10 @@
if (marker < 0) throw Error(22);
++count;
}
+
+ if (!foundCompletePsData && skipApp13Ps3.size() > 0) throw Error(22);
+ search += skipApp13Ps3.size();
+
if (comPos == 0) {
if (marker == eoi_) comPos = count;
else comPos = insertPos;
@@ -591,7 +632,7 @@
if (exifData_.count() > 0) ++search;
if (writeXmpFromPacket() == false && xmpData_.count() > 0) ++search;
if (writeXmpFromPacket() == true && xmpPacket_.size() > 0) ++search;
- if (iptcData_.count() > 0) ++search;
+ if (foundCompletePsData || iptcData_.count() > 0) ++search;
if (!comment_.empty()) ++search;
io_->seek(seek, BasicIo::beg);
@@ -673,31 +714,44 @@
if (outIo.error()) throw Error(21);
--search;
}
- if (psData.size_ > 0 || iptcData_.count() > 0) {
+ if (foundCompletePsData || iptcData_.count() > 0) {
// Set the new IPTC IRB, keeps existing IRBs but removes the
// IPTC block if there is no new IPTC data to write
- DataBuf newPsData = Photoshop::setIptcIrb(psData.pData_,
- psData.size_,
+ DataBuf newPsData = Photoshop::setIptcIrb(&psBlob[0],
+ psBlob.size(),
iptcData_);
- if (newPsData.size_ > 0) {
- // Write APP13 marker, new size, and ps3Id
+ const long maxChunkSize = 0xffff - 16;
+ const byte* chunkStart = newPsData.pData_;
+ const byte* chunkEnd = chunkStart + newPsData.size_;
+ while (chunkStart < chunkEnd) {
+ // Determine size of next chunk
+ long chunkSize = static_cast<long>(chunkEnd - chunkStart);
+ if (chunkSize > maxChunkSize) {
+ chunkSize = maxChunkSize;
+ // Don't break at a valid IRB boundary
+ const long writtenSize = static_cast<long>(chunkStart - newPsData.pData_);
+ if (Photoshop::valid(newPsData.pData_, writtenSize + chunkSize)) {
+ // Since an IRB has minimum size 12,
+ // (chunkSize - 8) can't be also a IRB boundary
+ chunkSize -= 8;
+ }
+ }
+
+ // Write APP13 marker, chunk size, and ps3Id
tmpBuf[0] = 0xff;
tmpBuf[1] = app13_;
-
- if (newPsData.size_ + 16 > 0xffff) throw Error(37, "IPTC");
- us2Data(tmpBuf + 2, static_cast<uint16_t>(newPsData.size_ + 16), bigEndian);
+ us2Data(tmpBuf + 2, static_cast<uint16_t>(chunkSize + 16), bigEndian);
std::memcpy(tmpBuf + 4, Photoshop::ps3Id_, 14);
if (outIo.write(tmpBuf, 18) != 18) throw Error(21);
if (outIo.error()) throw Error(21);
- // Write new Photoshop IRB data buffer
- if ( outIo.write(newPsData.pData_, newPsData.size_)
- != newPsData.size_) throw Error(21);
+ // Write next chunk of the Photoshop IRB data buffer
+ if (outIo.write(chunkStart, chunkSize) != chunkSize) throw Error(21);
if (outIo.error()) throw Error(21);
+
+ chunkStart += chunkSize;
}
- if (iptcData_.count() > 0) {
- --search;
- }
+ --search;
}
}
if (comPos == count) {
@@ -724,7 +778,7 @@
}
else if ( skipApp1Exif == count
|| skipApp1Xmp == count
- || skipApp13Ps3 == count
+ || find(skipApp13Ps3.begin(), skipApp13Ps3.end(), count) != skipApp13Ps3.end()
|| skipCom == count) {
--search;
io_->seek(size-bufRead, BasicIo::cur);
diff -rN -u old-trunk-1/src/jpgimage.hpp new-trunk-1/src/jpgimage.hpp
--- old-trunk-1/src/jpgimage.hpp 2009-06-25 17:10:32.000000000 +0200
+++ new-trunk-1/src/jpgimage.hpp 2009-06-25 17:10:32.000000000 +0200
@@ -64,6 +64,16 @@
static const uint16_t iptc_; //!< %Photoshop IPTC marker
/*!
+ @brief Validates all IRBs
+
+ @param pPsData Existing IRB buffer
+ @param sizePsData Size of the IRB buffer, may be 0
+ @return true if all IRBs are valid;<BR>
+ false otherwise
+ */
+ static bool valid(const byte* pPsData,
+ long sizePsData);
+ /*!
@brief Locates the data for a %Photoshop tag in a %Photoshop formated memory
buffer. Operates on raw data to simplify reuse.
@param pPsData Pointer to buffer containing entire payload of
diff -rN -u old-trunk-1/src/jpgimage.cpp new-trunk-1/src/jpgimage.cpp
--- old-trunk-1/src/jpgimage.cpp 2009-07-03 12:02:48.000000000 +0200
+++ new-trunk-1/src/jpgimage.cpp 2009-07-03 12:02:48.000000000 +0200
@@ -137,7 +137,7 @@
psSize += (psSize & 1);
position += psSize;
if (position + 4 > sizePsData) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
std::cerr << "Warning: "
<< "Invalid or extended Photoshop IRB\n";
#endif
@@ -146,7 +146,7 @@
uint32_t dataSize = getULong(pPsData + position, bigEndian);
position += 4;
if (dataSize > static_cast<uint32_t>(sizePsData - position)) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
std::cerr << "Warning: "
<< "Invalid Photoshop IRB data size "
<< dataSize << " or extended Photoshop IRB\n";
@@ -176,7 +176,7 @@
std::cerr << "pPsData doesn't start with '8BIM'\n";
#endif
if (position < sizePsData) {
-#ifndef SUPPRESS_WARNINGS
+#ifdef DEBUG
std::cerr << "Warning: "
<< "Invalid or extended Photoshop IRB\n";
#endif
|