1 | #------------------------------------------------------------------------------
|
---|
2 | # File: ICC_Profile.pm
|
---|
3 | #
|
---|
4 | # Description: Read ICC Profile meta information
|
---|
5 | #
|
---|
6 | # Revisions: 11/16/04 - P. Harvey Created
|
---|
7 | #
|
---|
8 | # References: 1) http://www.color.org/icc_specs2.html (ICC.1:2003-09)
|
---|
9 | # 2) http://www.color.org/icc_specs2.html (ICC.1:2001-04)
|
---|
10 | # 3) http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/ColorSync_Manager.pdf
|
---|
11 | # 4) http://www.color.org/privatetag2007-01.pdf
|
---|
12 | #
|
---|
13 | # Notes: The ICC profile information is different: the format of each
|
---|
14 | # tag is embedded in the information instead of in the directory
|
---|
15 | # structure. This makes things a bit more complex because I need
|
---|
16 | # an extra level of logic to decode the variable-format tags.
|
---|
17 | #------------------------------------------------------------------------------
|
---|
18 |
|
---|
19 | package Image::ExifTool::ICC_Profile;
|
---|
20 |
|
---|
21 | use strict;
|
---|
22 | use vars qw($VERSION);
|
---|
23 | use Image::ExifTool qw(:DataAccess :Utils);
|
---|
24 |
|
---|
25 | $VERSION = '1.13';
|
---|
26 |
|
---|
27 | sub ProcessICC($$);
|
---|
28 | sub ProcessICC_Profile($$$);
|
---|
29 | sub WriteICC_Profile($$;$);
|
---|
30 | sub ValidateICC($);
|
---|
31 |
|
---|
32 | # illuminant type definitions
|
---|
33 | my %illuminantType = (
|
---|
34 | 1 => 'D50',
|
---|
35 | 2 => 'D65',
|
---|
36 | 3 => 'D93',
|
---|
37 | 4 => 'F2',
|
---|
38 | 5 => 'D55',
|
---|
39 | 6 => 'A',
|
---|
40 | 7 => 'Equi-Power (E)',
|
---|
41 | 8 => 'F8',
|
---|
42 | );
|
---|
43 | my %profileClass = (
|
---|
44 | scnr => 'Input Device Profile',
|
---|
45 | mntr => 'Display Device Profile',
|
---|
46 | prtr => 'Output Device Profile',
|
---|
47 | 'link'=> 'DeviceLink Profile',
|
---|
48 | spac => 'ColorSpace Conversion Profile',
|
---|
49 | abst => 'Abstract Profile',
|
---|
50 | nmcl => 'NamedColor Profile',
|
---|
51 | );
|
---|
52 |
|
---|
53 | # ICC_Profile tag table
|
---|
54 | %Image::ExifTool::ICC_Profile::Main = (
|
---|
55 | GROUPS => { 2 => 'Image' },
|
---|
56 | PROCESS_PROC => \&ProcessICC_Profile,
|
---|
57 | WRITE_PROC => \&WriteICC_Profile,
|
---|
58 | NOTES => q{
|
---|
59 | ICC profile information is used in many different file types including JPEG,
|
---|
60 | TIFF, PDF, PostScript, Photoshop, PNG, MIFF, PICT, QuickTime and some RAW
|
---|
61 | formats. While the tags listed below are not individually writable, the
|
---|
62 | entire profile itself can be accessed via the extra 'ICC_Profile' tag, but
|
---|
63 | this tag is neither extracted nor written unless specified explicitly.
|
---|
64 | },
|
---|
65 | A2B0 => 'AToB0',
|
---|
66 | A2B1 => 'AToB1',
|
---|
67 | A2B2 => 'AToB2',
|
---|
68 | bXYZ => 'BlueMatrixColumn', # (called BlueColorant in ref 2)
|
---|
69 | bTRC => {
|
---|
70 | Name => 'BlueTRC',
|
---|
71 | Description => 'Blue Tone Reproduction Curve',
|
---|
72 | },
|
---|
73 | B2A0 => 'BToA0',
|
---|
74 | B2A1 => 'BToA1',
|
---|
75 | B2A2 => 'BToA2',
|
---|
76 | calt => {
|
---|
77 | Name => 'CalibrationDateTime',
|
---|
78 | Groups => { 2 => 'Time' },
|
---|
79 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
80 | },
|
---|
81 | targ => 'CharTarget',
|
---|
82 | chad => 'ChromaticAdaptation',
|
---|
83 | chrm => {
|
---|
84 | Name => 'Chromaticity',
|
---|
85 | SubDirectory => {
|
---|
86 | TagTable => 'Image::ExifTool::ICC_Profile::Chromaticity',
|
---|
87 | Validate => '$type eq "chrm"',
|
---|
88 | },
|
---|
89 | },
|
---|
90 | clro => 'ColorantOrder',
|
---|
91 | clrt => {
|
---|
92 | Name => 'ColorantTable',
|
---|
93 | SubDirectory => {
|
---|
94 | TagTable => 'Image::ExifTool::ICC_Profile::ColorantTable',
|
---|
95 | Validate => '$type eq "clrt"',
|
---|
96 | },
|
---|
97 | },
|
---|
98 | cprt => 'ProfileCopyright',
|
---|
99 | crdi => 'CRDInfo', #2
|
---|
100 | dmnd => {
|
---|
101 | Name => 'DeviceMfgDesc',
|
---|
102 | Groups => { 2 => 'Camera' },
|
---|
103 | },
|
---|
104 | dmdd => {
|
---|
105 | Name => 'DeviceModelDesc',
|
---|
106 | Groups => { 2 => 'Camera' },
|
---|
107 | },
|
---|
108 | devs => {
|
---|
109 | Name => 'DeviceSettings', #2
|
---|
110 | Groups => { 2 => 'Camera' },
|
---|
111 | },
|
---|
112 | gamt => 'Gamut',
|
---|
113 | kTRC => {
|
---|
114 | Name => 'GrayTRC',
|
---|
115 | Description => 'Gray Tone Reproduction Curve',
|
---|
116 | },
|
---|
117 | gXYZ => 'GreenMatrixColumn', # (called GreenColorant in ref 2)
|
---|
118 | gTRC => {
|
---|
119 | Name => 'GreenTRC',
|
---|
120 | Description => 'Green Tone Reproduction Curve',
|
---|
121 | },
|
---|
122 | lumi => 'Luminance',
|
---|
123 | meas => {
|
---|
124 | Name => 'Measurement',
|
---|
125 | SubDirectory => {
|
---|
126 | TagTable => 'Image::ExifTool::ICC_Profile::Measurement',
|
---|
127 | Validate => '$type eq "meas"',
|
---|
128 | },
|
---|
129 | },
|
---|
130 | bkpt => 'MediaBlackPoint',
|
---|
131 | wtpt => 'MediaWhitePoint',
|
---|
132 | ncol => 'NamedColor', #2
|
---|
133 | ncl2 => 'NamedColor2',
|
---|
134 | resp => 'OutputResponse',
|
---|
135 | pre0 => 'Preview0',
|
---|
136 | pre1 => 'Preview1',
|
---|
137 | pre2 => 'Preview2',
|
---|
138 | desc => 'ProfileDescription',
|
---|
139 | pseq => 'ProfileSequenceDesc',
|
---|
140 | psd0 => 'PostScript2CRD0', #2
|
---|
141 | psd1 => 'PostScript2CRD1', #2
|
---|
142 | psd2 => 'PostScript2CRD2', #2
|
---|
143 | ps2s => 'PostScript2CSA', #2
|
---|
144 | ps2i => 'PS2RenteringIntent', #2
|
---|
145 | rXYZ => 'RedMatrixColumn', # (called RedColorant in ref 2)
|
---|
146 | rTRC => {
|
---|
147 | Name => 'RedTRC',
|
---|
148 | Description => 'Red Tone Reproduction Curve',
|
---|
149 | },
|
---|
150 | scrd => 'ScreeningDesc',
|
---|
151 | scrn => 'Screening',
|
---|
152 | 'bfd '=> {
|
---|
153 | Name => 'UCRBG',
|
---|
154 | Description => 'Under Color Removal & Black Gen.',
|
---|
155 | },
|
---|
156 | tech => {
|
---|
157 | Name => 'Technology',
|
---|
158 | PrintConv => {
|
---|
159 | fscn => 'Film Scanner',
|
---|
160 | dcam => 'Digital Camera',
|
---|
161 | rscn => 'Reflective Scanner',
|
---|
162 | ijet => 'Ink Jet Printer',
|
---|
163 | twax => 'Thermal Wax Printer',
|
---|
164 | epho => 'Electrophotographic Printer',
|
---|
165 | esta => 'Electrostatic Printer',
|
---|
166 | dsub => 'Dye Sublimation Printer',
|
---|
167 | rpho => 'Photographic Paper Printer',
|
---|
168 | fprn => 'Film Writer',
|
---|
169 | vidm => 'Video Monitor',
|
---|
170 | vidc => 'Video Camera',
|
---|
171 | pjtv => 'Projection Television',
|
---|
172 | 'CRT '=> 'Cathode Ray Tube Display',
|
---|
173 | 'PMD '=> 'Passive Matrix Display',
|
---|
174 | 'AMD '=> 'Active Matrix Display',
|
---|
175 | KPCD => 'Photo CD',
|
---|
176 | imgs => 'Photo Image Setter',
|
---|
177 | grav => 'Gravure',
|
---|
178 | offs => 'Offset Lithography',
|
---|
179 | silk => 'Silkscreen',
|
---|
180 | flex => 'Flexography',
|
---|
181 | },
|
---|
182 | },
|
---|
183 | vued => 'ViewingCondDesc',
|
---|
184 | view => {
|
---|
185 | Name => 'ViewingConditions',
|
---|
186 | SubDirectory => {
|
---|
187 | TagTable => 'Image::ExifTool::ICC_Profile::ViewingConditions',
|
---|
188 | Validate => '$type eq "view"',
|
---|
189 | },
|
---|
190 | },
|
---|
191 | # ColorSync custom tags (ref 3)
|
---|
192 | psvm => 'PS2CRDVMSize',
|
---|
193 | vcgt => 'VideoCardGamma',
|
---|
194 | mmod => 'MakeAndModel',
|
---|
195 | dscm => 'ProfileDescriptionML',
|
---|
196 | ndin => 'NativeDisplayInfo',
|
---|
197 |
|
---|
198 | # Microsoft custom tags (ref http://msdn2.microsoft.com/en-us/library/ms536870.aspx)
|
---|
199 | MS00 => 'WCSProfiles',
|
---|
200 |
|
---|
201 | # the following entry represents the ICC profile header, and doesn't
|
---|
202 | # exist as a tag in the directory. It is only in this table to provide
|
---|
203 | # a link so ExifTool can locate the header tags
|
---|
204 | Header => {
|
---|
205 | Name => 'ProfileHeader',
|
---|
206 | SubDirectory => {
|
---|
207 | TagTable => 'Image::ExifTool::ICC_Profile::Header',
|
---|
208 | },
|
---|
209 | },
|
---|
210 | );
|
---|
211 |
|
---|
212 | # ICC profile header definition
|
---|
213 | %Image::ExifTool::ICC_Profile::Header = (
|
---|
214 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
215 | GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-header', 2 => 'Image' },
|
---|
216 | 4 => {
|
---|
217 | Name => 'ProfileCMMType',
|
---|
218 | Format => 'string[4]',
|
---|
219 | },
|
---|
220 | 8 => {
|
---|
221 | Name => 'ProfileVersion',
|
---|
222 | Format => 'int16s',
|
---|
223 | PrintConv => '($val >> 8).".".(($val & 0xf0)>>4).".".($val & 0x0f)',
|
---|
224 | },
|
---|
225 | 12 => {
|
---|
226 | Name => 'ProfileClass',
|
---|
227 | Format => 'string[4]',
|
---|
228 | PrintConv => \%profileClass,
|
---|
229 | },
|
---|
230 | 16 => {
|
---|
231 | Name => 'ColorSpaceData',
|
---|
232 | Format => 'string[4]',
|
---|
233 | },
|
---|
234 | 20 => {
|
---|
235 | Name => 'ProfileConnectionSpace',
|
---|
236 | Format => 'string[4]',
|
---|
237 | },
|
---|
238 | 24 => {
|
---|
239 | Name => 'ProfileDateTime',
|
---|
240 | Groups => { 2 => 'Time' },
|
---|
241 | Format => 'int16u[6]',
|
---|
242 | ValueConv => 'sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",split(" ",$val));',
|
---|
243 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
244 | },
|
---|
245 | 36 => {
|
---|
246 | Name => 'ProfileFileSignature',
|
---|
247 | Format => 'string[4]',
|
---|
248 | },
|
---|
249 | 40 => {
|
---|
250 | Name => 'PrimaryPlatform',
|
---|
251 | Format => 'string[4]',
|
---|
252 | PrintConv => {
|
---|
253 | 'APPL' => 'Apple Computer Inc.',
|
---|
254 | 'MSFT' => 'Microsoft Corporation',
|
---|
255 | 'SGI ' => 'Silicon Graphics Inc.',
|
---|
256 | 'SUNW' => 'Sun Microsystems Inc.',
|
---|
257 | 'TGNT' => 'Taligent Inc.',
|
---|
258 | },
|
---|
259 | },
|
---|
260 | 44 => {
|
---|
261 | Name => 'CMMFlags',
|
---|
262 | Format => 'int32u',
|
---|
263 | PrintConv => q[
|
---|
264 | ($val & 0x01 ? "Embedded, " : "Not Embedded, ") .
|
---|
265 | ($val & 0x02 ? "Not Independent" : "Independent")
|
---|
266 | ],
|
---|
267 | },
|
---|
268 | 48 => {
|
---|
269 | Name => 'DeviceManufacturer',
|
---|
270 | Format => 'string[4]',
|
---|
271 | },
|
---|
272 | 52 => {
|
---|
273 | Name => 'DeviceModel',
|
---|
274 | Format => 'string[4]',
|
---|
275 | },
|
---|
276 | 56 => {
|
---|
277 | Name => 'DeviceAttributes',
|
---|
278 | Format => 'int32u[2]',
|
---|
279 | PrintConv => q[
|
---|
280 | my @v = split ' ', $val;
|
---|
281 | ($v[1] & 0x01 ? "Transparency, " : "Reflective, ") .
|
---|
282 | ($v[1] & 0x02 ? "Matte, " : "Glossy, ") .
|
---|
283 | ($v[1] & 0x04 ? "Negative, " : "Positive, ") .
|
---|
284 | ($v[1] & 0x08 ? "B&W" : "Color");
|
---|
285 | ],
|
---|
286 | },
|
---|
287 | 64 => {
|
---|
288 | Name => 'RenderingIntent',
|
---|
289 | Format => 'int32u',
|
---|
290 | PrintConv => {
|
---|
291 | 0 => 'Perceptual',
|
---|
292 | 1 => 'Media-Relative Colorimetric',
|
---|
293 | 2 => 'Saturation',
|
---|
294 | 3 => 'ICC-Absolute Colorimetric',
|
---|
295 | },
|
---|
296 | },
|
---|
297 | 68 => {
|
---|
298 | Name => 'ConnectionSpaceIlluminant',
|
---|
299 | Format => 'fixed32s[3]', # xyz
|
---|
300 | },
|
---|
301 | 80 => {
|
---|
302 | Name => 'ProfileCreator',
|
---|
303 | Format => 'string[4]',
|
---|
304 | },
|
---|
305 | 84 => {
|
---|
306 | Name => 'ProfileID',
|
---|
307 | Format => 'int8u[16]',
|
---|
308 | PrintConv => 'Image::ExifTool::ICC_Profile::HexID($val)',
|
---|
309 | },
|
---|
310 | );
|
---|
311 |
|
---|
312 | # viewingConditionsType (view) definition
|
---|
313 | %Image::ExifTool::ICC_Profile::ViewingConditions = (
|
---|
314 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
315 | GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-view', 2 => 'Image' },
|
---|
316 | 8 => {
|
---|
317 | Name => 'ViewingCondIlluminant',
|
---|
318 | Format => 'fixed32s[3]', # xyz
|
---|
319 | },
|
---|
320 | 20 => {
|
---|
321 | Name => 'ViewingCondSurround',
|
---|
322 | Format => 'fixed32s[3]', # xyz
|
---|
323 | },
|
---|
324 | 32 => {
|
---|
325 | Name => 'ViewingCondIlluminantType',
|
---|
326 | Format => 'int32u',
|
---|
327 | PrintConv => \%illuminantType,
|
---|
328 | },
|
---|
329 | );
|
---|
330 |
|
---|
331 | # measurementType (meas) definition
|
---|
332 | %Image::ExifTool::ICC_Profile::Measurement = (
|
---|
333 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
334 | GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-meas', 2 => 'Image' },
|
---|
335 | 8 => {
|
---|
336 | Name => 'MeasurementObserver',
|
---|
337 | Format => 'int32u',
|
---|
338 | PrintConv => {
|
---|
339 | 1 => 'CIE 1931',
|
---|
340 | 2 => 'CIE 1964',
|
---|
341 | },
|
---|
342 | },
|
---|
343 | 12 => {
|
---|
344 | Name => 'MeasurementBacking',
|
---|
345 | Format => 'fixed32s[3]', # xyz
|
---|
346 | },
|
---|
347 | 24 => {
|
---|
348 | Name => 'MeasurementGeometry',
|
---|
349 | Format => 'int32u',
|
---|
350 | PrintConv => {
|
---|
351 | 1 => '0/45 or 45/0',
|
---|
352 | 2 => '0/d or d/0',
|
---|
353 | },
|
---|
354 | },
|
---|
355 | 28 => {
|
---|
356 | Name => 'MeasurementFlare',
|
---|
357 | Format => 'fixed32u',
|
---|
358 | PrintConv => '$val*100 . "%"', # change into a percent
|
---|
359 | },
|
---|
360 | 32 => {
|
---|
361 | Name => 'MeasurementIlluminant',
|
---|
362 | Format => 'int32u',
|
---|
363 | PrintConv => \%illuminantType,
|
---|
364 | },
|
---|
365 | );
|
---|
366 |
|
---|
367 | # chromaticity (chrm) definition
|
---|
368 | %Image::ExifTool::ICC_Profile::Chromaticity = (
|
---|
369 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
370 | GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-chrm', 2 => 'Image' },
|
---|
371 | 8 => {
|
---|
372 | Name => 'ChromaticityChannels',
|
---|
373 | Format => 'int16u',
|
---|
374 | },
|
---|
375 | 10 => {
|
---|
376 | Name => 'ChromaticityColorant',
|
---|
377 | Format => 'int16u',
|
---|
378 | PrintConv => {
|
---|
379 | 1 => 'ITU-R BT.709',
|
---|
380 | 2 => 'SMPTE RP145-1994',
|
---|
381 | 3 => 'EBU Tech.3213-E',
|
---|
382 | 4 => 'P22',
|
---|
383 | },
|
---|
384 | },
|
---|
385 | # include definitions for 4 channels -- if there are
|
---|
386 | # fewer then the ProcessBinaryData logic won't print them.
|
---|
387 | # If there are more, oh well.
|
---|
388 | 12 => {
|
---|
389 | Name => 'ChromaticityChannel1',
|
---|
390 | Format => 'fixed32u[2]',
|
---|
391 | },
|
---|
392 | 20 => {
|
---|
393 | Name => 'ChromaticityChannel2',
|
---|
394 | Format => 'fixed32u[2]',
|
---|
395 | },
|
---|
396 | 28 => {
|
---|
397 | Name => 'ChromaticityChannel3',
|
---|
398 | Format => 'fixed32u[2]',
|
---|
399 | },
|
---|
400 | 36 => {
|
---|
401 | Name => 'ChromaticityChannel4',
|
---|
402 | Format => 'fixed32u[2]',
|
---|
403 | },
|
---|
404 | );
|
---|
405 |
|
---|
406 | # colorantTable (clrt) definition
|
---|
407 | %Image::ExifTool::ICC_Profile::ColorantTable = (
|
---|
408 | PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
|
---|
409 | GROUPS => { 0 => 'ICC_Profile', 1 => 'ICC-clrt', 2 => 'Image' },
|
---|
410 | 8 => {
|
---|
411 | Name => 'ColorantCount',
|
---|
412 | Format => 'int32u',
|
---|
413 | },
|
---|
414 | # include definitions for 3 colorants -- if there are
|
---|
415 | # fewer then the ProcessBinaryData logic won't print them.
|
---|
416 | # If there are more, oh well.
|
---|
417 | 12 => {
|
---|
418 | Name => 'Colorant1Name',
|
---|
419 | Format => 'string[32]',
|
---|
420 | },
|
---|
421 | 44 => {
|
---|
422 | Name => 'Colorant1Coordinates',
|
---|
423 | Format => 'int16u[3]',
|
---|
424 | },
|
---|
425 | 50 => {
|
---|
426 | Name => 'Colorant2Name',
|
---|
427 | Format => 'string[32]',
|
---|
428 | },
|
---|
429 | 82 => {
|
---|
430 | Name => 'Colorant2Coordinates',
|
---|
431 | Format => 'int16u[3]',
|
---|
432 | },
|
---|
433 | 88 => {
|
---|
434 | Name => 'Colorant3Name',
|
---|
435 | Format => 'string[32]',
|
---|
436 | },
|
---|
437 | 120 => {
|
---|
438 | Name => 'Colorant3Coordinates',
|
---|
439 | Format => 'int16u[3]',
|
---|
440 | },
|
---|
441 | );
|
---|
442 |
|
---|
443 |
|
---|
444 | #------------------------------------------------------------------------------
|
---|
445 | # print ICC Profile ID in hex
|
---|
446 | # Inputs: 1) string of numbers
|
---|
447 | # Returns: string of hex digits
|
---|
448 | sub HexID($)
|
---|
449 | {
|
---|
450 | my $val = shift;
|
---|
451 | my @vals = split(' ', $val);
|
---|
452 | # return a simple zero if no MD5 done
|
---|
453 | return 0 unless grep(!/^0/, @vals);
|
---|
454 | $val = '';
|
---|
455 | foreach (@vals) { $val .= sprintf("%.2x",$_); }
|
---|
456 | return $val;
|
---|
457 | }
|
---|
458 |
|
---|
459 | #------------------------------------------------------------------------------
|
---|
460 | # get formatted value from ICC tag (which has the type embedded)
|
---|
461 | # Inputs: 0) data reference, 1) offset to tag data, 2) tag data size
|
---|
462 | # Returns: Formatted value or undefined if format not supported
|
---|
463 | # Notes: The following types are handled by BinaryTables:
|
---|
464 | # chromaticityType, colorantTableType, measurementType, viewingConditionsType
|
---|
465 | # The following types are not currently handled (most are large tables):
|
---|
466 | # curveType, lut16Type, lut8Type, lutAtoBType, lutBtoAType, namedColor2Type,
|
---|
467 | # parametricCurveType, profileSeqDescType, responseCurveSet16Type
|
---|
468 | sub FormatICCTag($$$)
|
---|
469 | {
|
---|
470 | my ($dataPt, $offset, $size) = @_;
|
---|
471 |
|
---|
472 | my $type;
|
---|
473 | if ($size >= 8) {
|
---|
474 | # get data type from start of tag data
|
---|
475 | $type = substr($$dataPt, $offset, 4);
|
---|
476 | } else {
|
---|
477 | $type = 'err';
|
---|
478 | }
|
---|
479 | # colorantOrderType
|
---|
480 | if ($type eq 'clro' and $size >= 12) {
|
---|
481 | my $num = Get32u($dataPt, $offset+8);
|
---|
482 | if ($size >= $num + 12) {
|
---|
483 | my $pos = $offset + 12;
|
---|
484 | return join(' ',unpack("x$pos c$num", $$dataPt));
|
---|
485 | }
|
---|
486 | }
|
---|
487 | # dataType
|
---|
488 | if ($type eq 'data' and $size >= 12) {
|
---|
489 | my $form = Get32u($dataPt, $offset+8);
|
---|
490 | # format 0 is ASCII data
|
---|
491 | $form == 0 and return substr($$dataPt, $offset+12, $size-12);
|
---|
492 | # binary data and other data types treat as binary (ie. don't format)
|
---|
493 | }
|
---|
494 | # dateTimeType
|
---|
495 | if ($type eq 'dtim' and $size >= 20) {
|
---|
496 | return sprintf("%.4d:%.2d:%.2d %.2d:%.2d:%.2d",
|
---|
497 | Get16u($dataPt, $offset+8), Get16u($dataPt, $offset+10),
|
---|
498 | Get16u($dataPt, $offset+12), Get16u($dataPt, $offset+14),
|
---|
499 | Get16u($dataPt, $offset+16), Get16u($dataPt, $offset+18));
|
---|
500 | }
|
---|
501 | # multiLocalizedUnicodeType (replaces textDescriptionType of ref 2)
|
---|
502 | if ($type eq 'mluc' and $size >= 28) {
|
---|
503 | # take first language in list (pray that it is ascii)
|
---|
504 | my $len = Get32u($dataPt, $offset + 20);
|
---|
505 | my $pos = Get32u($dataPt, $offset + 24);
|
---|
506 | if ($size >= $pos + $len) {
|
---|
507 | my $str = substr($$dataPt, $offset + $pos, $len);
|
---|
508 | $str =~ tr/\x00-\x1f\x80-\xff//d; # remove control characters and non-ascii
|
---|
509 | return $str;
|
---|
510 | }
|
---|
511 | }
|
---|
512 | # s15Fixed16ArrayType
|
---|
513 | if ($type eq 'sf32') {
|
---|
514 | return ReadValue($dataPt,$offset+8,'fixed32s',($size-8)/4,$size-8);
|
---|
515 | }
|
---|
516 | # signatureType
|
---|
517 | if ($type eq 'sig ' and $size >= 12) {
|
---|
518 | return substr($$dataPt, $offset+8, 4);
|
---|
519 | }
|
---|
520 | # textType
|
---|
521 | $type eq 'text' and return substr($$dataPt, $offset+8, $size-8);
|
---|
522 | # textDescriptionType (ref 2, replaced by multiLocalizedUnicodeType)
|
---|
523 | if ($type eq 'desc' and $size >= 12) {
|
---|
524 | my $len = Get32u($dataPt, $offset+8);
|
---|
525 | if ($size >= $len + 12) {
|
---|
526 | my $str = substr($$dataPt, $offset+12, $len);
|
---|
527 | $str =~ s/\0.*//s; # truncate at null terminator
|
---|
528 | return $str;
|
---|
529 | }
|
---|
530 | }
|
---|
531 | # u16Fixed16ArrayType
|
---|
532 | if ($type eq 'uf32') {
|
---|
533 | return ReadValue($dataPt,$offset+8,'fixed32u',($size-8)/4,$size-8);
|
---|
534 | }
|
---|
535 | # uInt32ArrayType
|
---|
536 | if ($type eq 'ui32') {
|
---|
537 | return ReadValue($dataPt,$offset+8,'int32u',($size-8)/4,$size-8);
|
---|
538 | }
|
---|
539 | # uInt64ArrayType
|
---|
540 | if ($type eq 'ui64') {
|
---|
541 | return ReadValue($dataPt,$offset+8,'int64u',($size-8)/8,$size-8);
|
---|
542 | }
|
---|
543 | # uInt8ArrayType
|
---|
544 | if ($type eq 'ui08') {
|
---|
545 | return ReadValue($dataPt,$offset+8,'int8u',$size-8,$size-8);
|
---|
546 | }
|
---|
547 | # XYZType
|
---|
548 | if ($type eq 'XYZ ') {
|
---|
549 | my $str = '';
|
---|
550 | my $pos;
|
---|
551 | for ($pos=8; $pos+12<=$size; $pos+=12) {
|
---|
552 | $str and $str .= ', ';
|
---|
553 | $str .= ReadValue($dataPt,$offset+$pos,'fixed32s',3,$size-$pos);
|
---|
554 | }
|
---|
555 | return $str;
|
---|
556 | }
|
---|
557 | return undef; # data type is not supported
|
---|
558 | }
|
---|
559 |
|
---|
560 | #------------------------------------------------------------------------------
|
---|
561 | # Write ICC profile file
|
---|
562 | # Inputs: 0) ExifTool object reference, 1) Reference to directory information
|
---|
563 | # Returns: 1 on success, 0 if this wasn't a valid ICC file,
|
---|
564 | # or -1 if a write error occurred
|
---|
565 | sub WriteICC($$)
|
---|
566 | {
|
---|
567 | my ($exifTool, $dirInfo) = @_;
|
---|
568 | # first make sure this is a valid ICC file (or no file at all)
|
---|
569 | my $raf = $$dirInfo{RAF};
|
---|
570 | my $buff;
|
---|
571 | return 0 if $raf->Read($buff, 24) and ValidateICC(\$buff);
|
---|
572 | # now write the new ICC
|
---|
573 | $buff = WriteICC_Profile($exifTool, $dirInfo);
|
---|
574 | if (defined $buff and length $buff) {
|
---|
575 | Write($$dirInfo{OutFile}, $buff) or return -1;
|
---|
576 | } else {
|
---|
577 | $exifTool->Error('No ICC information to write');
|
---|
578 | }
|
---|
579 | return 1;
|
---|
580 | }
|
---|
581 |
|
---|
582 | #------------------------------------------------------------------------------
|
---|
583 | # Write ICC data record
|
---|
584 | # Inputs: 0) ExifTool object reference, 1) source dirInfo reference,
|
---|
585 | # 2) tag table reference
|
---|
586 | # Returns: ICC data block (may be empty if no ICC data)
|
---|
587 | # Notes: Increments ExifTool CHANGED flag if changed
|
---|
588 | sub WriteICC_Profile($$;$)
|
---|
589 | {
|
---|
590 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
591 | $exifTool or return 1; # allow dummy access
|
---|
592 | my $dirName = $$dirInfo{DirName} || 'ICC_Profile';
|
---|
593 | # (don't write AsShotICCProfile or CurrentICCProfile here)
|
---|
594 | return undef unless $dirName eq 'ICC_Profile';
|
---|
595 | my $newValueHash = $exifTool->GetNewValueHash($Image::ExifTool::Extra{$dirName});
|
---|
596 | return undef unless Image::ExifTool::IsOverwriting($newValueHash);
|
---|
597 | my $val = Image::ExifTool::GetNewValues($newValueHash);
|
---|
598 | $val = '' unless defined $val;
|
---|
599 | ++$exifTool->{CHANGED};
|
---|
600 | return $val;
|
---|
601 | }
|
---|
602 |
|
---|
603 | #------------------------------------------------------------------------------
|
---|
604 | # Validate ICC data
|
---|
605 | # Inputs: 0) ICC data reference
|
---|
606 | # Returns: error string or undef on success
|
---|
607 | sub ValidateICC($)
|
---|
608 | {
|
---|
609 | my $valPtr = shift;
|
---|
610 | my $err;
|
---|
611 | length($$valPtr) < 24 and return 'Invalid ICC profile';
|
---|
612 | $profileClass{substr($$valPtr, 12, 4)} or $err = 'profile class';
|
---|
613 | my $col = substr($$valPtr, 16, 4); # ColorSpaceData
|
---|
614 | my $con = substr($$valPtr, 20, 4); # ConnectionSpace
|
---|
615 | my $match = '(XYZ |Lab |Luv |YCbr|Yxy |RGB |GRAY|HSV |HLS |CMYK|CMY |[2-9A-F]CLR)';
|
---|
616 | $col =~ /$match/ or $err = 'color space';
|
---|
617 | $con =~ /$match/ or $err = 'connection space';
|
---|
618 | return $err ? "Invalid ICC profile (bad $err)" : undef;
|
---|
619 | }
|
---|
620 |
|
---|
621 | #------------------------------------------------------------------------------
|
---|
622 | # Process ICC profile file
|
---|
623 | # Inputs: 0) ExifTool object reference, 1) Reference to directory information
|
---|
624 | # Returns: 1 if this was an ICC file
|
---|
625 | sub ProcessICC($$)
|
---|
626 | {
|
---|
627 | my ($exifTool, $dirInfo) = @_;
|
---|
628 | my $raf = $$dirInfo{RAF};
|
---|
629 | my $buff;
|
---|
630 | $raf->Read($buff, 24) == 24 or return 0;
|
---|
631 | # check to see if this is a valid ICC profile file
|
---|
632 | return 0 if ValidateICC(\$buff);
|
---|
633 | $exifTool->SetFileType();
|
---|
634 | # read the profile
|
---|
635 | my $size = unpack('N', $buff);
|
---|
636 | if ($size < 128 or $size & 0x80000000) {
|
---|
637 | $exifTool->Error("Bad ICC Profile length ($size)");
|
---|
638 | return 1;
|
---|
639 | }
|
---|
640 | $raf->Seek(0, 0);
|
---|
641 | unless ($raf->Read($buff, $size)) {
|
---|
642 | $exifTool->Error('Truncated ICC profile');
|
---|
643 | return 1;
|
---|
644 | }
|
---|
645 | my %dirInfo = (
|
---|
646 | DataPt => \$buff,
|
---|
647 | DataLen => $size,
|
---|
648 | DirStart => 0,
|
---|
649 | DirLen => $size,
|
---|
650 | );
|
---|
651 | my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main');
|
---|
652 | return ProcessICC_Profile($exifTool, \%dirInfo, $tagTablePtr);
|
---|
653 | }
|
---|
654 |
|
---|
655 | #------------------------------------------------------------------------------
|
---|
656 | # Process ICC_Profile APP13 record
|
---|
657 | # Inputs: 0) ExifTool object reference, 1) Reference to directory information
|
---|
658 | # 2) Tag table reference (undefined to read ICC file)
|
---|
659 | # Returns: 1 on success
|
---|
660 | sub ProcessICC_Profile($$$)
|
---|
661 | {
|
---|
662 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
663 | my $dataPt = $$dirInfo{DataPt};
|
---|
664 | my $dirStart = $$dirInfo{DirStart};
|
---|
665 | my $dirLen = $$dirInfo{DirLen};
|
---|
666 | my $verbose = $exifTool->Options('Verbose');
|
---|
667 | my $raf;
|
---|
668 |
|
---|
669 | # extract binary ICC_Profile data block if binary mode or requested
|
---|
670 | if ($exifTool->{OPTIONS}->{Binary} or $exifTool->{REQ_TAG_LOOKUP}->{icc_profile} and
|
---|
671 | # (don't extract from AsShotICCProfile or CurrentICCProfile)
|
---|
672 | (not $$dirInfo{Name} or $$dirInfo{Name} eq 'ICC_Profile'))
|
---|
673 | {
|
---|
674 | $exifTool->FoundTag('ICC_Profile', substr($$dataPt, $dirStart, $dirLen));
|
---|
675 | }
|
---|
676 |
|
---|
677 | SetByteOrder('MM'); # ICC_Profile is always big-endian
|
---|
678 |
|
---|
679 | # check length of table
|
---|
680 | my $len = Get32u($dataPt, $dirStart);
|
---|
681 | if ($len != $dirLen or $len < 128) {
|
---|
682 | $exifTool->Warn("Bad length ICC_Profile (length $len)");
|
---|
683 | return 0 if $len < 128 or $dirLen < $len;
|
---|
684 | }
|
---|
685 | my $pos = $dirStart + 128; # position at start of table
|
---|
686 | my $numEntries = Get32u($dataPt, $pos);
|
---|
687 | if ($numEntries < 1 or $numEntries >= 0x100
|
---|
688 | or $numEntries * 12 + 132 > $dirLen)
|
---|
689 | {
|
---|
690 | $exifTool->Warn("Bad ICC_Profile table ($numEntries entries)");
|
---|
691 | return 0;
|
---|
692 | }
|
---|
693 |
|
---|
694 | if ($verbose) {
|
---|
695 | $exifTool->VerboseDir('ICC_Profile', $numEntries, $dirLen);
|
---|
696 | my $fakeInfo = { Name=>'ProfileHeader', SubDirectory => { } };
|
---|
697 | $exifTool->VerboseInfo(undef, $fakeInfo);
|
---|
698 | }
|
---|
699 | # increment ICC dir count
|
---|
700 | my $dirCount = $exifTool->{DIR_COUNT}->{ICC} = ($exifTool->{DIR_COUNT}->{ICC} || 0) + 1;
|
---|
701 | $exifTool->{SET_GROUP1} = '+' . $dirCount if $dirCount > 1;
|
---|
702 | # process the header block
|
---|
703 | my %subdirInfo = (
|
---|
704 | Name => 'ProfileHeader',
|
---|
705 | DataPt => $dataPt,
|
---|
706 | DataLen => $$dirInfo{DataLen},
|
---|
707 | DirStart => $dirStart,
|
---|
708 | DirLen => 128,
|
---|
709 | Parent => $$dirInfo{DirName},
|
---|
710 | );
|
---|
711 | my $newTagTable = GetTagTable('Image::ExifTool::ICC_Profile::Header');
|
---|
712 | $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable);
|
---|
713 |
|
---|
714 | $pos += 4; # skip item count
|
---|
715 | my $index;
|
---|
716 | for ($index=0; $index<$numEntries; ++$index) {
|
---|
717 | my $tagID = substr($$dataPt, $pos, 4);
|
---|
718 | my $offset = Get32u($dataPt, $pos + 4);
|
---|
719 | my $size = Get32u($dataPt, $pos + 8);
|
---|
720 | $pos += 12;
|
---|
721 | my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tagID);
|
---|
722 | # unknown tags aren't generated automatically by GetTagInfo()
|
---|
723 | # if the tagID's aren't numeric, so we must do this manually:
|
---|
724 | if (not $tagInfo and $exifTool->{OPTIONS}->{Unknown}) {
|
---|
725 | $tagInfo = { Unknown => 1 };
|
---|
726 | Image::ExifTool::AddTagToTable($tagTablePtr, $tagID, $tagInfo);
|
---|
727 | }
|
---|
728 | next unless defined $tagInfo;
|
---|
729 |
|
---|
730 | if ($offset + $size > $dirLen) {
|
---|
731 | $exifTool->Warn("Bad ICC_Profile table (truncated)");
|
---|
732 | last;
|
---|
733 | }
|
---|
734 | my $valuePtr = $dirStart + $offset;
|
---|
735 |
|
---|
736 | my $subdir = $$tagInfo{SubDirectory};
|
---|
737 | # format the value unless this is a subdirectory
|
---|
738 | my $value;
|
---|
739 | $value = FormatICCTag($dataPt, $valuePtr, $size) unless $subdir;
|
---|
740 | my $fmt;
|
---|
741 | $fmt = substr($$dataPt, $valuePtr, 4) if $size > 4;
|
---|
742 | $verbose and $exifTool->VerboseInfo($tagID, $tagInfo,
|
---|
743 | Table => $tagTablePtr,
|
---|
744 | Index => $index,
|
---|
745 | Value => $value,
|
---|
746 | DataPt => $dataPt,
|
---|
747 | Size => $size,
|
---|
748 | Start => $valuePtr,
|
---|
749 | Format => "type '$fmt'",
|
---|
750 | );
|
---|
751 | if ($subdir) {
|
---|
752 | my $name = $$tagInfo{Name};
|
---|
753 | undef $newTagTable;
|
---|
754 | if ($$subdir{TagTable}) {
|
---|
755 | $newTagTable = GetTagTable($$subdir{TagTable});
|
---|
756 | unless ($newTagTable) {
|
---|
757 | warn "Unknown tag table $$subdir{TagTable}\n";
|
---|
758 | next;
|
---|
759 | }
|
---|
760 | } else {
|
---|
761 | warn "Must specify TagTable for SubDirectory $name\n";
|
---|
762 | next;
|
---|
763 | }
|
---|
764 | %subdirInfo = (
|
---|
765 | Name => $name,
|
---|
766 | DataPt => $dataPt,
|
---|
767 | DataPos => $$dirInfo{DataPos},
|
---|
768 | DataLen => $$dirInfo{DataLen},
|
---|
769 | DirStart => $valuePtr,
|
---|
770 | DirLen => $size,
|
---|
771 | Parent => $$dirInfo{DirName},
|
---|
772 | );
|
---|
773 | my $type = substr($$dataPt, $valuePtr, 4);
|
---|
774 | #### eval Validate ($type)
|
---|
775 | if (defined $$subdir{Validate} and not eval $$subdir{Validate}) {
|
---|
776 | $exifTool->Warn("Invalid $name data");
|
---|
777 | } else {
|
---|
778 | $exifTool->ProcessDirectory(\%subdirInfo, $newTagTable, $$subdir{ProcessProc});
|
---|
779 | }
|
---|
780 | } elsif (defined $value) {
|
---|
781 | $exifTool->FoundTag($tagInfo, $value);
|
---|
782 | } else {
|
---|
783 | $value = substr($$dataPt, $valuePtr, $size);
|
---|
784 | # treat unsupported formats as binary data
|
---|
785 | my $oldValueConv = $$tagInfo{ValueConv};
|
---|
786 | $$tagInfo{ValueConv} = '\$val' unless defined $$tagInfo{ValueConv};
|
---|
787 | $exifTool->FoundTag($tagInfo, $value);
|
---|
788 | }
|
---|
789 | }
|
---|
790 | delete $exifTool->{SET_GROUP1};
|
---|
791 | return 1;
|
---|
792 | }
|
---|
793 |
|
---|
794 |
|
---|
795 | 1; # end
|
---|
796 |
|
---|
797 |
|
---|
798 | __END__
|
---|
799 |
|
---|
800 | =head1 NAME
|
---|
801 |
|
---|
802 | Image::ExifTool::ICC_Profile - Read ICC Profile meta information
|
---|
803 |
|
---|
804 | =head1 SYNOPSIS
|
---|
805 |
|
---|
806 | This module is loaded automatically by Image::ExifTool when required.
|
---|
807 |
|
---|
808 | =head1 DESCRIPTION
|
---|
809 |
|
---|
810 | This module contains the definitions to read information from ICC profiles.
|
---|
811 | ICC (International Color Consortium) profiles are used to translate color
|
---|
812 | data created on one device into another device's native color space.
|
---|
813 |
|
---|
814 | =head1 AUTHOR
|
---|
815 |
|
---|
816 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
817 |
|
---|
818 | This library is free software; you can redistribute it and/or modify it
|
---|
819 | under the same terms as Perl itself.
|
---|
820 |
|
---|
821 | =head1 REFERENCES
|
---|
822 |
|
---|
823 | =over 4
|
---|
824 |
|
---|
825 | =item L<http://www.color.org/icc_specs2.html>
|
---|
826 |
|
---|
827 | =item L<http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/ColorSync_Manager.pdf>
|
---|
828 |
|
---|
829 | =back
|
---|
830 |
|
---|
831 | =head1 SEE ALSO
|
---|
832 |
|
---|
833 | L<Image::ExifTool::TagNames/ICC_Profile Tags>,
|
---|
834 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
835 |
|
---|
836 | =cut
|
---|