1 | #------------------------------------------------------------------------------
|
---|
2 | # File: XMP.pm
|
---|
3 | #
|
---|
4 | # Description: Read XMP meta information
|
---|
5 | #
|
---|
6 | # Revisions: 11/25/2003 - P. Harvey Created
|
---|
7 | # 10/28/2004 - P. Harvey Major overhaul to conform with XMP spec
|
---|
8 | # 02/27/2005 - P. Harvey Also read UTF-16 and UTF-32 XMP
|
---|
9 | # 08/30/2005 - P. Harvey Split tag tables into separate namespaces
|
---|
10 | # 10/24/2005 - P. Harvey Added ability to parse .XMP files
|
---|
11 | # 08/25/2006 - P. Harvey Added ability to handle blank nodes
|
---|
12 | # 08/22/2007 - P. Harvey Added ability to handle alternate language tags
|
---|
13 | #
|
---|
14 | # References: 1) http://www.adobe.com/products/xmp/pdfs/xmpspec.pdf
|
---|
15 | # 2) http://www.w3.org/TR/rdf-syntax-grammar/ (20040210)
|
---|
16 | # 3) http://www.portfoliofaq.com/pfaq/v7mappings.htm
|
---|
17 | # 4) http://www.iptc.org/IPTC4XMP/
|
---|
18 | # 5) http://creativecommons.org/technology/xmp
|
---|
19 | # 6) http://www.optimasc.com/products/fileid/xmp-extensions.pdf
|
---|
20 | # 7) Lou Salkind private communication
|
---|
21 | # 8) http://partners.adobe.com/public/developer/en/xmp/sdk/XMPspecification.pdf
|
---|
22 | #
|
---|
23 | # Notes: - I am handling property qualifiers as if they were separate
|
---|
24 | # properties (with no associated namespace).
|
---|
25 | #
|
---|
26 | # - Currently, there is no special treatment of the following
|
---|
27 | # properties which could potentially affect the extracted
|
---|
28 | # information: xml:base, rdf:parseType (note that parseType
|
---|
29 | # Literal isn't allowed by the XMP spec).
|
---|
30 | #
|
---|
31 | # - The family 2 group names will be set to 'Unknown' for any XMP
|
---|
32 | # tags not found in the XMP or Exif tag tables.
|
---|
33 | #------------------------------------------------------------------------------
|
---|
34 |
|
---|
35 | package Image::ExifTool::XMP;
|
---|
36 |
|
---|
37 | use strict;
|
---|
38 | use vars qw($VERSION $AUTOLOAD @ISA @EXPORT_OK);
|
---|
39 | use Image::ExifTool qw(:Utils);
|
---|
40 | use Image::ExifTool::Exif;
|
---|
41 | require Exporter;
|
---|
42 |
|
---|
43 | $VERSION = '1.78';
|
---|
44 | @ISA = qw(Exporter);
|
---|
45 | @EXPORT_OK = qw(EscapeXML UnescapeXML);
|
---|
46 |
|
---|
47 | sub ProcessXMP($$;$);
|
---|
48 | sub WriteXMP($$;$);
|
---|
49 | sub ParseXMPElement($$$;$$$);
|
---|
50 | sub DecodeBase64($);
|
---|
51 | sub SaveBlankInfo($$$;$);
|
---|
52 | sub ProcessBlankInfo($$$;$);
|
---|
53 | sub ValidateXMP($;$);
|
---|
54 | sub UnescapeChar($$);
|
---|
55 |
|
---|
56 | # conversions for GPS coordinates
|
---|
57 | sub ToDegrees
|
---|
58 | {
|
---|
59 | require Image::ExifTool::GPS;
|
---|
60 | Image::ExifTool::GPS::ToDegrees($_[0], 1);
|
---|
61 | }
|
---|
62 | my %latConv = (
|
---|
63 | ValueConv => \&ToDegrees,
|
---|
64 | RawConv => 'require Image::ExifTool::GPS; $val', # to load Composite tags and routines
|
---|
65 | ValueConvInv => q{
|
---|
66 | require Image::ExifTool::GPS;
|
---|
67 | Image::ExifTool::GPS::ToDMS($self, $val, 2, "N");
|
---|
68 | },
|
---|
69 | PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "N")',
|
---|
70 | PrintConvInv => \&ToDegrees,
|
---|
71 | );
|
---|
72 | my %longConv = (
|
---|
73 | ValueConv => \&ToDegrees,
|
---|
74 | RawConv => 'require Image::ExifTool::GPS; $val',
|
---|
75 | ValueConvInv => q{
|
---|
76 | require Image::ExifTool::GPS;
|
---|
77 | Image::ExifTool::GPS::ToDMS($self, $val, 2, "E");
|
---|
78 | },
|
---|
79 | PrintConv => 'Image::ExifTool::GPS::ToDMS($self, $val, 1, "E")',
|
---|
80 | PrintConvInv => \&ToDegrees,
|
---|
81 | );
|
---|
82 | my %dateTimeInfo = (
|
---|
83 | Groups => { 2 => 'Time' },
|
---|
84 | Writable => 'date',
|
---|
85 | Shift => 'Time',
|
---|
86 | PrintConv => '$self->ConvertDateTime($val)',
|
---|
87 | );
|
---|
88 |
|
---|
89 | # XMP namespaces which we don't want to contribute to generated EXIF tag names
|
---|
90 | my %ignoreNamespace = ( 'x'=>1, 'rdf'=>1, 'xmlns'=>1, 'xml'=>1);
|
---|
91 |
|
---|
92 | # translate XMP namespaces when reading
|
---|
93 | my %xlatNamespace = (
|
---|
94 | # shorten ugly IPTC Core namespace prefix
|
---|
95 | 'Iptc4xmpCore' => 'iptcCore',
|
---|
96 | 'photomechanic'=> 'photomech',
|
---|
97 | 'MicrosoftPhoto' => 'microsoft',
|
---|
98 | # also translate older 'xap...' prefixes to 'xmp...'
|
---|
99 | 'xap' => 'xmp',
|
---|
100 | 'xapBJ' => 'xmpBJ',
|
---|
101 | 'xapMM' => 'xmpMM',
|
---|
102 | 'xapRights' => 'xmpRights',
|
---|
103 | );
|
---|
104 |
|
---|
105 | # these are the attributes that we handle for properties that contain
|
---|
106 | # sub-properties. Attributes for simple properties are easy, and we
|
---|
107 | # just copy them over. These are harder since we don't store attributes
|
---|
108 | # for properties without simple values. (maybe this will change...)
|
---|
109 | my %recognizedAttrs = (
|
---|
110 | 'x:xaptk' => 1,
|
---|
111 | 'x:xmptk' => 1,
|
---|
112 | 'rdf:about' => 1,
|
---|
113 | 'rdf:parseType' => 1,
|
---|
114 | 'rdf:nodeID' => 1,
|
---|
115 | );
|
---|
116 |
|
---|
117 | # main XMP tag table
|
---|
118 | %Image::ExifTool::XMP::Main = (
|
---|
119 | GROUPS => { 2 => 'Unknown' },
|
---|
120 | PROCESS_PROC => \&ProcessXMP,
|
---|
121 | WRITE_PROC => \&WriteXMP,
|
---|
122 | dc => {
|
---|
123 | Name => 'dc', # (otherwise generated name would be 'Dc')
|
---|
124 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::dc' },
|
---|
125 | },
|
---|
126 | xmp => {
|
---|
127 | Name => 'xmp',
|
---|
128 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmp' },
|
---|
129 | },
|
---|
130 | xmpDM => {
|
---|
131 | Name => 'xmpDM',
|
---|
132 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpDM' },
|
---|
133 | },
|
---|
134 | xmpRights => {
|
---|
135 | Name => 'xmpRights',
|
---|
136 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpRights' },
|
---|
137 | },
|
---|
138 | xmpMM => {
|
---|
139 | Name => 'xmpMM',
|
---|
140 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpMM' },
|
---|
141 | },
|
---|
142 | xmpBJ => {
|
---|
143 | Name => 'xmpBJ',
|
---|
144 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpBJ' },
|
---|
145 | },
|
---|
146 | xmpTPg => {
|
---|
147 | Name => 'xmpTPg',
|
---|
148 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpTPg' },
|
---|
149 | },
|
---|
150 | pdf => {
|
---|
151 | Name => 'pdf',
|
---|
152 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::pdf' },
|
---|
153 | },
|
---|
154 | photoshop => {
|
---|
155 | Name => 'photoshop',
|
---|
156 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::photoshop' },
|
---|
157 | },
|
---|
158 | crs => {
|
---|
159 | Name => 'crs',
|
---|
160 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::crs' },
|
---|
161 | },
|
---|
162 | # crss - it would be difficult to add the ability to write this
|
---|
163 | aux => {
|
---|
164 | Name => 'aux',
|
---|
165 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::aux' },
|
---|
166 | },
|
---|
167 | tiff => {
|
---|
168 | Name => 'tiff',
|
---|
169 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::tiff' },
|
---|
170 | },
|
---|
171 | exif => {
|
---|
172 | Name => 'exif',
|
---|
173 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::exif' },
|
---|
174 | },
|
---|
175 | iptcCore => {
|
---|
176 | Name => 'iptcCore',
|
---|
177 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::iptcCore' },
|
---|
178 | },
|
---|
179 | PixelLive => {
|
---|
180 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::PixelLive' },
|
---|
181 | },
|
---|
182 | xmpPLUS => {
|
---|
183 | Name => 'xmpPLUS',
|
---|
184 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::xmpPLUS' },
|
---|
185 | },
|
---|
186 | cc => {
|
---|
187 | Name => 'cc',
|
---|
188 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::cc' },
|
---|
189 | },
|
---|
190 | dex => {
|
---|
191 | Name => 'dex',
|
---|
192 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::dex' },
|
---|
193 | },
|
---|
194 | photomech => {
|
---|
195 | Name => 'photomech',
|
---|
196 | SubDirectory => { TagTable => 'Image::ExifTool::PhotoMechanic::XMP' },
|
---|
197 | },
|
---|
198 | mediapro => {
|
---|
199 | Name => 'mediapro',
|
---|
200 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::MediaPro' },
|
---|
201 | },
|
---|
202 | microsoft => {
|
---|
203 | Name => 'microsoft',
|
---|
204 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::Microsoft' },
|
---|
205 | },
|
---|
206 | lr => {
|
---|
207 | Name => 'lr',
|
---|
208 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::Lightroom' },
|
---|
209 | },
|
---|
210 | DICOM => {
|
---|
211 | Name => 'DICOM',
|
---|
212 | SubDirectory => { TagTable => 'Image::ExifTool::XMP::DICOM' },
|
---|
213 | },
|
---|
214 | );
|
---|
215 |
|
---|
216 | #
|
---|
217 | # Tag tables for all XMP schemas:
|
---|
218 | #
|
---|
219 | # Writable - only need to define this for writable tags if not plain text
|
---|
220 | # (boolean, integer, rational, real, date or lang-alt)
|
---|
221 | # List - XMP list type (Bag, Seq or Alt, or set to 1 for elements in Struct lists)
|
---|
222 | #
|
---|
223 | # (Note that family 1 group names are generated from the property namespace, not
|
---|
224 | # the group1 names below which exist so the groups will appear in the list.)
|
---|
225 | #
|
---|
226 | my %xmpTableDefaults = (
|
---|
227 | WRITE_PROC => \&WriteXMP,
|
---|
228 | WRITABLE => 'string',
|
---|
229 | LANG_INFO => \&GetLangInfo,
|
---|
230 | );
|
---|
231 |
|
---|
232 | # Dublin Core schema properties (dc)
|
---|
233 | %Image::ExifTool::XMP::dc = (
|
---|
234 | %xmpTableDefaults,
|
---|
235 | GROUPS => { 1 => 'XMP-dc', 2 => 'Other' },
|
---|
236 | NAMESPACE => 'dc',
|
---|
237 | NOTES => 'Dublin Core schema tags.',
|
---|
238 | contributor => { Groups => { 2 => 'Author' }, List => 'Bag' },
|
---|
239 | coverage => { },
|
---|
240 | creator => { Groups => { 2 => 'Author' }, List => 'Seq' },
|
---|
241 | date => { %dateTimeInfo, List => 'Seq' },
|
---|
242 | description => { Groups => { 2 => 'Image' }, Writable => 'lang-alt' },
|
---|
243 | 'format' => { Groups => { 2 => 'Image' } },
|
---|
244 | identifier => { Groups => { 2 => 'Image' } },
|
---|
245 | language => { List => 'Bag' },
|
---|
246 | publisher => { Groups => { 2 => 'Author' }, List => 'Bag' },
|
---|
247 | relation => { List => 'Bag' },
|
---|
248 | rights => { Groups => { 2 => 'Author' }, Writable => 'lang-alt' },
|
---|
249 | source => { Groups => { 2 => 'Author' } },
|
---|
250 | subject => { Groups => { 2 => 'Image' }, List => 'Bag' },
|
---|
251 | title => { Groups => { 2 => 'Image' }, Writable => 'lang-alt' },
|
---|
252 | type => { Groups => { 2 => 'Image' }, List => 'Bag' },
|
---|
253 | );
|
---|
254 |
|
---|
255 | # XMP Basic schema properties (xmp, xap)
|
---|
256 | %Image::ExifTool::XMP::xmp = (
|
---|
257 | %xmpTableDefaults,
|
---|
258 | GROUPS => { 1 => 'XMP-xmp', 2 => 'Image' },
|
---|
259 | NAMESPACE => 'xmp',
|
---|
260 | NOTES => q{
|
---|
261 | XMP Basic schema tags. If the older "xap", "xapBJ", "xapMM" or "xapRights"
|
---|
262 | namespace prefixes are found, they are translated to the newer "xmp",
|
---|
263 | "xmpBJ", "xmpMM" and "xmpRights" prefixes for use in family 1 group names.
|
---|
264 | },
|
---|
265 | Advisory => { List => 'Bag' },
|
---|
266 | BaseURL => { },
|
---|
267 | CreateDate => { %dateTimeInfo },
|
---|
268 | CreatorTool => { },
|
---|
269 | Identifier => { Avoid => 1, List => 'Bag' },
|
---|
270 | Label => { },
|
---|
271 | MetadataDate=> { %dateTimeInfo },
|
---|
272 | ModifyDate => { %dateTimeInfo },
|
---|
273 | Nickname => { },
|
---|
274 | Rating => { Writable => 'integer' },
|
---|
275 | Thumbnails => {
|
---|
276 | SubDirectory => { },
|
---|
277 | Struct => 'Thumbnail',
|
---|
278 | List => 'Alt',
|
---|
279 | },
|
---|
280 | ThumbnailsHeight => { List => 1 },
|
---|
281 | ThumbnailsWidth => { List => 1 },
|
---|
282 | ThumbnailsFormat => { List => 1 },
|
---|
283 | ThumbnailsImage => {
|
---|
284 | # Eventually may want to handle this like a normal thumbnail image
|
---|
285 | # -- but not yet! (need to write EncodeBase64() routine....)
|
---|
286 | # Name => 'ThumbnailImage',
|
---|
287 | List => 1,
|
---|
288 | # translate Base64-encoded thumbnail
|
---|
289 | ValueConv => 'Image::ExifTool::XMP::DecodeBase64($val)',
|
---|
290 | },
|
---|
291 | );
|
---|
292 |
|
---|
293 | # XMP Rights Management schema properties (xmpRights, xapRights)
|
---|
294 | %Image::ExifTool::XMP::xmpRights = (
|
---|
295 | %xmpTableDefaults,
|
---|
296 | GROUPS => { 1 => 'XMP-xmpRights', 2 => 'Author' },
|
---|
297 | NAMESPACE => 'xmpRights',
|
---|
298 | NOTES => 'XMP Rights Management schema tags.',
|
---|
299 | Certificate => { },
|
---|
300 | Marked => { Writable => 'boolean' },
|
---|
301 | Owner => { List => 'Bag' },
|
---|
302 | UsageTerms => { Writable => 'lang-alt' },
|
---|
303 | WebStatement => { },
|
---|
304 | );
|
---|
305 |
|
---|
306 | # XMP Media Management schema properties (xmpMM, xapMM)
|
---|
307 | %Image::ExifTool::XMP::xmpMM = (
|
---|
308 | %xmpTableDefaults,
|
---|
309 | GROUPS => { 1 => 'XMP-xmpMM', 2 => 'Other' },
|
---|
310 | NAMESPACE => 'xmpMM',
|
---|
311 | NOTES => 'XMP Media Management schema tags.',
|
---|
312 | DerivedFrom => {
|
---|
313 | SubDirectory => { },
|
---|
314 | Struct => 'ResourceRef',
|
---|
315 | },
|
---|
316 | DerivedFromInstanceID => { },
|
---|
317 | DerivedFromDocumentID => { },
|
---|
318 | DerivedFromVersionID => { },
|
---|
319 | DerivedFromRenditionClass => { },
|
---|
320 | DerivedFromRenditionParams => { },
|
---|
321 | DerivedFromManager => { },
|
---|
322 | DerivedFromManagerVariant => { },
|
---|
323 | DerivedFromManageTo => { },
|
---|
324 | DerivedFromManageUI => { },
|
---|
325 | DocumentID => { },
|
---|
326 | History => {
|
---|
327 | SubDirectory => { },
|
---|
328 | Struct => 'ResourceEvent',
|
---|
329 | List => 'Seq',
|
---|
330 | },
|
---|
331 | # we treat these like list items since History is a list
|
---|
332 | HistoryAction => { List => 1 },
|
---|
333 | HistoryInstanceID => { List => 1 },
|
---|
334 | HistoryParameters => { List => 1 },
|
---|
335 | HistorySoftwareAgent => { List => 1 },
|
---|
336 | HistoryWhen => { List => 1, %dateTimeInfo },
|
---|
337 | InstanceID => { }, #PH (CS3)
|
---|
338 | ManagedFrom => { SubDirectory => { }, Struct => 'ResourceRef' },
|
---|
339 | ManagedFromInstanceID => { },
|
---|
340 | ManagedFromDocumentID => { },
|
---|
341 | ManagedFromVersionID => { },
|
---|
342 | ManagedFromRenditionClass => { },
|
---|
343 | ManagedFromRenditionParams => { },
|
---|
344 | ManagedFromManager => { },
|
---|
345 | ManagedFromManagerVariant => { },
|
---|
346 | ManagedFromManageTo => { },
|
---|
347 | ManagedFromManageUI => { },
|
---|
348 | Manager => { Groups => { 2 => 'Author' } },
|
---|
349 | ManageTo => { Groups => { 2 => 'Author' } },
|
---|
350 | ManageUI => { },
|
---|
351 | ManagerVariant => { },
|
---|
352 | PreservedFileName => { }, # undocumented
|
---|
353 | RenditionClass => { },
|
---|
354 | RenditionParams => { },
|
---|
355 | VersionID => { },
|
---|
356 | Versions => {
|
---|
357 | SubDirectory => { },
|
---|
358 | Struct => 'Version',
|
---|
359 | List => 'Seq',
|
---|
360 | },
|
---|
361 | VersionsComments => { List => 1 }, # we treat these like list items
|
---|
362 | VersionsEvent => { SubDirectory => { }, Struct => 'ResourceEvent' },
|
---|
363 | VersionsEventAction => { List => 1 },
|
---|
364 | VersionsEventInstanceID => { List => 1 },
|
---|
365 | VersionsEventParameters => { List => 1 },
|
---|
366 | VersionsEventSoftwareAgent => { List => 1 },
|
---|
367 | VersionsEventWhen => { List => 1, %dateTimeInfo },
|
---|
368 | VersionsModifyDate => { List => 1, %dateTimeInfo },
|
---|
369 | VersionsModifier => { List => 1 },
|
---|
370 | VersionsVersion => { List => 1 },
|
---|
371 | LastURL => { },
|
---|
372 | RenditionOf => { SubDirectory => { }, Struct => 'ResourceRef' },
|
---|
373 | RenditionOfInstanceID => { },
|
---|
374 | RenditionOfDocumentID => { },
|
---|
375 | RenditionOfVersionID => { },
|
---|
376 | RenditionOfRenditionClass => { },
|
---|
377 | RenditionOfRenditionParams => { },
|
---|
378 | RenditionOfManager => { },
|
---|
379 | RenditionOfManagerVariant => { },
|
---|
380 | RenditionOfManageTo => { },
|
---|
381 | RenditionOfManageUI => { },
|
---|
382 | SaveID => { Writable => 'integer' },
|
---|
383 | );
|
---|
384 |
|
---|
385 | # XMP Basic Job Ticket schema properties (xmpBJ, xapBJ)
|
---|
386 | %Image::ExifTool::XMP::xmpBJ = (
|
---|
387 | %xmpTableDefaults,
|
---|
388 | GROUPS => { 1 => 'XMP-xmpBJ', 2 => 'Other' },
|
---|
389 | NAMESPACE => 'xmpBJ',
|
---|
390 | NOTES => 'XMP Basic Job Ticket schema tags.',
|
---|
391 | # Note: JobRef is a List of structures. To accomplish this, we set the XMP
|
---|
392 | # List=>'Bag', but since SubDirectory is defined, this tag isn't writable
|
---|
393 | # directly. Then we need to set List=>1 for the members so the Writer logic
|
---|
394 | # will allow us to add list elements.
|
---|
395 | JobRef => {
|
---|
396 | SubDirectory => { },
|
---|
397 | Struct => 'JobRef',
|
---|
398 | List => 'Bag',
|
---|
399 | },
|
---|
400 | JobRefName => { List => 1 }, # we treat these like list items
|
---|
401 | JobRefId => { List => 1 },
|
---|
402 | JobRefUrl => { List => 1 },
|
---|
403 | );
|
---|
404 |
|
---|
405 | # XMP Paged-Text schema properties (xmpTPg)
|
---|
406 | %Image::ExifTool::XMP::xmpTPg = (
|
---|
407 | %xmpTableDefaults,
|
---|
408 | GROUPS => { 1 => 'XMP-xmpTPg', 2 => 'Image' },
|
---|
409 | NAMESPACE => 'xmpTPg',
|
---|
410 | NOTES => 'XMP Paged-Text schema tags.',
|
---|
411 | MaxPageSize => { SubDirectory => { }, Struct => 'Dimensions' },
|
---|
412 | MaxPageSizeW => { Writable => 'real' },
|
---|
413 | MaxPageSizeH => { Writable => 'real' },
|
---|
414 | MaxPageSizeUnit => { },
|
---|
415 | NPages => { Writable => 'integer' },
|
---|
416 | Fonts => {
|
---|
417 | SubDirectory => { },
|
---|
418 | Struct => 'Font',
|
---|
419 | List => 'Bag',
|
---|
420 | },
|
---|
421 | FontsFontName => { List => 1 },
|
---|
422 | FontsFontFamily => { List => 1 },
|
---|
423 | FontsFontFace => { List => 1 },
|
---|
424 | FontsFontType => { List => 1 },
|
---|
425 | FontsVersionString => { List => 1 },
|
---|
426 | FontsComposite => { List => 1, Writable => 'boolean' },
|
---|
427 | FontsFontFileName => { List => 1 },
|
---|
428 | FontsChildFontFiles => { List => 1 },
|
---|
429 | Colorants => {
|
---|
430 | SubDirectory => { },
|
---|
431 | Struct => 'Colorant',
|
---|
432 | List => 'Seq',
|
---|
433 | },
|
---|
434 | ColorantsSwatchName => { List => 1 },
|
---|
435 | ColorantsMode => { List => 1 },
|
---|
436 | ColorantsType => { List => 1 },
|
---|
437 | ColorantsCyan => { List => 1, Writable => 'real' },
|
---|
438 | ColorantsMagenta => { List => 1, Writable => 'real' },
|
---|
439 | ColorantsYellow => { List => 1, Writable => 'real' },
|
---|
440 | ColorantsBlack => { List => 1, Writable => 'real' },
|
---|
441 | ColorantsRed => { List => 1, Writable => 'integer' },
|
---|
442 | ColorantsGreen => { List => 1, Writable => 'integer' },
|
---|
443 | ColorantsBlue => { List => 1, Writable => 'integer' },
|
---|
444 | ColorantsL => { List => 1, Writable => 'real' },
|
---|
445 | ColorantsA => { List => 1, Writable => 'integer' },
|
---|
446 | ColorantsB => { List => 1, Writable => 'integer' },
|
---|
447 | PlateNames => { List => 'Seq' },
|
---|
448 | );
|
---|
449 |
|
---|
450 | # XMP Dynamic Media schema properties (xmpDM)
|
---|
451 | %Image::ExifTool::XMP::xmpDM = (
|
---|
452 | %xmpTableDefaults,
|
---|
453 | GROUPS => { 1 => 'XMP-xmpDM', 2 => 'Image' },
|
---|
454 | NAMESPACE => 'xmpDM',
|
---|
455 | NOTES => 'XMP Dynamic Media schema tags.',
|
---|
456 | projectRef => {
|
---|
457 | SubDirectory => { },
|
---|
458 | Struct => 'ProjectLink',
|
---|
459 | },
|
---|
460 | projectRefType => { },
|
---|
461 | projectRefPath => { },
|
---|
462 | videoFrameRate => { },
|
---|
463 | videoFrameSize => {
|
---|
464 | SubDirectory => { },
|
---|
465 | Struct => 'Dimensions',
|
---|
466 | },
|
---|
467 | videoFrameSizeW => { Writable => 'real' },
|
---|
468 | videoFrameSizeH => { Writable => 'real' },
|
---|
469 | videoFrameSizeUnit => { },
|
---|
470 | videoPixelAspectRatio => { Writable => 'rational' },
|
---|
471 | videoPixelDepth => { },
|
---|
472 | videoColorSpace => { },
|
---|
473 | videoAlphaMode => { },
|
---|
474 | videoAlphaPremultipleColor => {
|
---|
475 | SubDirectory => { },
|
---|
476 | Struct => 'Colorant',
|
---|
477 | },
|
---|
478 | videoAlphaPremultipleColorSwatchName => { },
|
---|
479 | videoAlphaPremultipleColorMode => { },
|
---|
480 | videoAlphaPremultipleColorType => { },
|
---|
481 | videoAlphaPremultipleColorCyan => { Writable => 'real' },
|
---|
482 | videoAlphaPremultipleColorMagenta => { Writable => 'real' },
|
---|
483 | videoAlphaPremultipleColorYellow => { Writable => 'real' },
|
---|
484 | videoAlphaPremultipleColorBlack => { Writable => 'real' },
|
---|
485 | videoAlphaPremultipleColorRed => { Writable => 'integer' },
|
---|
486 | videoAlphaPremultipleColorGreen => { Writable => 'integer' },
|
---|
487 | videoAlphaPremultipleColorBlue => { Writable => 'integer' },
|
---|
488 | videoAlphaPremultipleColorL => { Writable => 'real' },
|
---|
489 | videoAlphaPremultipleColorA => { Writable => 'integer' },
|
---|
490 | videoAlphaPremultipleColorB => { Writable => 'integer' },
|
---|
491 | videoAlphaUnityIsTransparent => { Writable => 'boolean' },
|
---|
492 | videoCompressor => { },
|
---|
493 | videoFieldOrder => { },
|
---|
494 | pullDown => { },
|
---|
495 | audioSampleRate => { Writable => 'integer' },
|
---|
496 | audioSampleType => { },
|
---|
497 | audioChannelType => { },
|
---|
498 | audioCompressor => { },
|
---|
499 | speakerPlacement => { },
|
---|
500 | fileDataRate => { Writable => 'rational' },
|
---|
501 | tapeName => { },
|
---|
502 | altTapeName => { },
|
---|
503 | startTimecode => {
|
---|
504 | SubDirectory => { },
|
---|
505 | Struct => 'Timecode',
|
---|
506 | },
|
---|
507 | startTimecodeTimeValue => { },
|
---|
508 | startTimecodeTimeFormat => { },
|
---|
509 | altTimecode => {
|
---|
510 | SubDirectory => { },
|
---|
511 | Struct => 'Timecode',
|
---|
512 | },
|
---|
513 | altTimecodeTimeValue => { },
|
---|
514 | altTimecodeTimeFormat => { },
|
---|
515 | duration => { },
|
---|
516 | scene => { Avoid => 1 },
|
---|
517 | shotName => { },
|
---|
518 | shotDate => { %dateTimeInfo },
|
---|
519 | shotLocation => { },
|
---|
520 | logComment => { },
|
---|
521 | markers => {
|
---|
522 | SubDirectory => { },
|
---|
523 | Struct => 'Marker',
|
---|
524 | List => 'Seq',
|
---|
525 | },
|
---|
526 | markersStartTime => { List => 1 },
|
---|
527 | markersDuration => { List => 1 },
|
---|
528 | markersComment => { List => 1 },
|
---|
529 | markersName => { List => 1 },
|
---|
530 | markersLocation => { List => 1 },
|
---|
531 | markersTarget => { List => 1 },
|
---|
532 | markersType => { List => 1 },
|
---|
533 | contributedMedia => {
|
---|
534 | SubDirectory => { },
|
---|
535 | Struct => 'Media',
|
---|
536 | List => 'Bag',
|
---|
537 | },
|
---|
538 | contributedMediaPath => { List => 1 },
|
---|
539 | contributedMediaTrack => { List => 1 },
|
---|
540 | contributedMediaStartTime => { List => 1 },
|
---|
541 | contributedMediaDuration => { List => 1 },
|
---|
542 | contributedMediaManaged => { List => 1, Writable => 'boolean' },
|
---|
543 | contributedMediaWebStatement => { List => 1 },
|
---|
544 | absPeakAudioFilePath => { },
|
---|
545 | relativePeakAudioFilePath => { },
|
---|
546 | videoModDate => { %dateTimeInfo },
|
---|
547 | audioModDate => { %dateTimeInfo },
|
---|
548 | metadataModDate => { %dateTimeInfo },
|
---|
549 | artist => { Avoid => 1, Groups => { 2 => 'Author' } },
|
---|
550 | album => { },
|
---|
551 | trackNumber => { Writable => 'integer' },
|
---|
552 | genre => { },
|
---|
553 | copyright => { Avoid => 1, Groups => { 2 => 'Author' } },
|
---|
554 | releaseDate => { %dateTimeInfo },
|
---|
555 | composer => { Groups => { 2 => 'Author' } },
|
---|
556 | engineer => { },
|
---|
557 | tempo => { Writable => 'real' },
|
---|
558 | instrument => { },
|
---|
559 | introTime => { },
|
---|
560 | outCue => { },
|
---|
561 | relativeTimestamp => { },
|
---|
562 | loop => { Writable => 'boolean' },
|
---|
563 | numberOfBeats => { Writable => 'real' },
|
---|
564 | key => { },
|
---|
565 | stretchMode => { },
|
---|
566 | timeScaleParams => {
|
---|
567 | SubDirectory => { },
|
---|
568 | Struct => 'TimeScaleStretch',
|
---|
569 | },
|
---|
570 | timeScaleParamsQuality => { },
|
---|
571 | timeScaleParamsFrameSize=> { Writable => 'real' },
|
---|
572 | timeScaleParamsFrameOverlappingPercentage => { Writable => 'real' },
|
---|
573 | resampleParams => {
|
---|
574 | SubDirectory => { },
|
---|
575 | Struct => 'ResampleStretch',
|
---|
576 | },
|
---|
577 | resampleParamsQuality => { },
|
---|
578 | beatSpliceParams => {
|
---|
579 | SubDirectory => { },
|
---|
580 | Struct => 'BeatSpliceStretch',
|
---|
581 | },
|
---|
582 | beatSpliceParamsUseFileBeatsMarker => { Writable => 'boolean' },
|
---|
583 | beatSpliceParamsRiseInDecibel => { Writable => 'real' },
|
---|
584 | beatSpliceParamsRiseInTimeDuration => { },
|
---|
585 | timeSignature => { },
|
---|
586 | scaleType => { },
|
---|
587 | );
|
---|
588 |
|
---|
589 | # PDF schema properties (pdf)
|
---|
590 | %Image::ExifTool::XMP::pdf = (
|
---|
591 | %xmpTableDefaults,
|
---|
592 | GROUPS => { 1 => 'XMP-pdf', 2 => 'Image' },
|
---|
593 | NAMESPACE => 'pdf',
|
---|
594 | NOTES => q{
|
---|
595 | Adobe PDF schema tags. The official XMP specification defines only
|
---|
596 | Keywords, PDFVersion and Producer. The other tags are included because they
|
---|
597 | have been observed in PDF files, but Creator, Subject and Title are avoided
|
---|
598 | when writing due to name conflicts with XMP-dc tags.
|
---|
599 | },
|
---|
600 | Author => { Groups => { 2 => 'Author' } }, #PH
|
---|
601 | ModDate => { %dateTimeInfo }, #PH
|
---|
602 | CreationDate=> { %dateTimeInfo }, #PH
|
---|
603 | Creator => { Groups => { 2 => 'Author' }, Avoid => 1 },
|
---|
604 | Subject => { Avoid => 1 },
|
---|
605 | Title => { Avoid => 1 },
|
---|
606 | Keywords => { },
|
---|
607 | PDFVersion => { },
|
---|
608 | Producer => { Groups => { 2 => 'Author' } },
|
---|
609 | );
|
---|
610 |
|
---|
611 | # Photoshop schema properties (photoshop)
|
---|
612 | %Image::ExifTool::XMP::photoshop = (
|
---|
613 | %xmpTableDefaults,
|
---|
614 | GROUPS => { 1 => 'XMP-photoshop', 2 => 'Image' },
|
---|
615 | NAMESPACE => 'photoshop',
|
---|
616 | NOTES => 'Adobe Photoshop schema tags.',
|
---|
617 | AuthorsPosition => { Groups => { 2 => 'Author' } },
|
---|
618 | CaptionWriter => { Groups => { 2 => 'Author' } },
|
---|
619 | Category => { },
|
---|
620 | City => { Groups => { 2 => 'Location' } },
|
---|
621 | Country => { Groups => { 2 => 'Location' } },
|
---|
622 | ColorMode => { }, #PH
|
---|
623 | Credit => { Groups => { 2 => 'Author' } },
|
---|
624 | DateCreated => { %dateTimeInfo },
|
---|
625 | History => { }, #PH (CS3)
|
---|
626 | Headline => { },
|
---|
627 | Instructions => { },
|
---|
628 | ICCProfile => { Name => 'ICCProfileName' }, #PH
|
---|
629 | LegacyIPTCDigest=> { }, #PH
|
---|
630 | SidecarForExtension => { }, #PH (CS3)
|
---|
631 | Source => { Groups => { 2 => 'Author' }, Avoid => 1 },
|
---|
632 | State => { Groups => { 2 => 'Location' } },
|
---|
633 | # the documentation doesn't show this as a 'Bag', but that's the
|
---|
634 | # way Photoshop7.0 writes it - PH
|
---|
635 | SupplementalCategories => { List => 'Bag' },
|
---|
636 | TransmissionReference => { },
|
---|
637 | Urgency => { Writable => 'integer' },
|
---|
638 | );
|
---|
639 |
|
---|
640 | # Photoshop Camera Raw Schema properties (crs) - (ref 8,PH)
|
---|
641 | %Image::ExifTool::XMP::crs = (
|
---|
642 | %xmpTableDefaults,
|
---|
643 | GROUPS => { 1 => 'XMP-crs', 2 => 'Image' },
|
---|
644 | NAMESPACE => 'crs',
|
---|
645 | NOTES => 'Photoshop Camera Raw Schema tags.',
|
---|
646 | AlreadyApplied => { Writable => 'boolean' }, #PH (written by LightRoom beta 4.1)
|
---|
647 | AutoBrightness => { Writable => 'boolean' },
|
---|
648 | AutoContrast => { Writable => 'boolean' },
|
---|
649 | AutoExposure => { Writable => 'boolean' },
|
---|
650 | AutoShadows => { Writable => 'boolean' },
|
---|
651 | BlueHue => { Writable => 'integer' },
|
---|
652 | BlueSaturation => { Writable => 'integer' },
|
---|
653 | Brightness => { Writable => 'integer' },
|
---|
654 | CameraProfile => { },
|
---|
655 | ChromaticAberrationB=> { Writable => 'integer' },
|
---|
656 | ChromaticAberrationR=> { Writable => 'integer' },
|
---|
657 | ColorNoiseReduction => { Writable => 'integer' },
|
---|
658 | Contrast => { Writable => 'integer', Avoid => 1 },
|
---|
659 | CropTop => { Writable => 'real' },
|
---|
660 | CropLeft => { Writable => 'real' },
|
---|
661 | CropBottom => { Writable => 'real' },
|
---|
662 | CropRight => { Writable => 'real' },
|
---|
663 | CropAngle => { Writable => 'real' },
|
---|
664 | CropWidth => { Writable => 'real' },
|
---|
665 | CropHeight => { Writable => 'real' },
|
---|
666 | CropUnits => {
|
---|
667 | Writable => 'integer',
|
---|
668 | PrintConv => {
|
---|
669 | 0 => 'pixels',
|
---|
670 | 1 => 'inches',
|
---|
671 | 2 => 'cm',
|
---|
672 | },
|
---|
673 | },
|
---|
674 | Exposure => { Writable => 'real' },
|
---|
675 | GreenHue => { Writable => 'integer' },
|
---|
676 | GreenSaturation => { Writable => 'integer' },
|
---|
677 | HasCrop => { Writable => 'boolean' },
|
---|
678 | HasSettings => { Writable => 'boolean' },
|
---|
679 | LuminanceSmoothing => { Writable => 'integer' },
|
---|
680 | RawFileName => { },
|
---|
681 | RedHue => { Writable => 'integer' },
|
---|
682 | RedSaturation => { Writable => 'integer' },
|
---|
683 | Saturation => { Writable => 'integer', Avoid => 1 },
|
---|
684 | Shadows => { Writable => 'integer' },
|
---|
685 | ShadowTint => { Writable => 'integer' },
|
---|
686 | Sharpness => { Writable => 'integer', Avoid => 1 },
|
---|
687 | Temperature => { Writable => 'integer' },
|
---|
688 | Tint => { Writable => 'integer' },
|
---|
689 | ToneCurve => { List => 'Seq' },
|
---|
690 | ToneCurveName => {
|
---|
691 | PrintConv => {
|
---|
692 | Linear => 'Linear',
|
---|
693 | 'Medium Contrast' => 'Medium Contrast',
|
---|
694 | 'Strong Contrast' => 'Strong Contrast',
|
---|
695 | Custom => 'Custom',
|
---|
696 | },
|
---|
697 | },
|
---|
698 | Version => { },
|
---|
699 | VignetteAmount => { Writable => 'integer' },
|
---|
700 | VignetteMidpoint=> { Writable => 'integer' },
|
---|
701 | WhiteBalance => {
|
---|
702 | Avoid => 1,
|
---|
703 | PrintConv => {
|
---|
704 | 'As Shot' => 'As Shot',
|
---|
705 | Auto => 'Auto',
|
---|
706 | Daylight => 'Daylight',
|
---|
707 | Cloudy => 'Cloudy',
|
---|
708 | Shade => 'Shade',
|
---|
709 | Tungsten => 'Tungsten',
|
---|
710 | Fluorescent => 'Fluorescent',
|
---|
711 | Flash => 'Flash',
|
---|
712 | Custom => 'Custom',
|
---|
713 | },
|
---|
714 | },
|
---|
715 | # new tags observed in Adobe Lightroom output - PH
|
---|
716 | CameraProfileDigest => { },
|
---|
717 | Clarity => { Writable => 'integer' },
|
---|
718 | ConvertToGrayscale => { Writable => 'boolean' },
|
---|
719 | Defringe => { Writable => 'integer' },
|
---|
720 | FillLight => { Writable => 'integer' },
|
---|
721 | HighlightRecovery => { Writable => 'integer' },
|
---|
722 | HueAdjustmentAqua => { Writable => 'integer' },
|
---|
723 | HueAdjustmentBlue => { Writable => 'integer' },
|
---|
724 | HueAdjustmentGreen => { Writable => 'integer' },
|
---|
725 | HueAdjustmentMagenta => { Writable => 'integer' },
|
---|
726 | HueAdjustmentOrange => { Writable => 'integer' },
|
---|
727 | HueAdjustmentPurple => { Writable => 'integer' },
|
---|
728 | HueAdjustmentRed => { Writable => 'integer' },
|
---|
729 | HueAdjustmentYellow => { Writable => 'integer' },
|
---|
730 | IncrementalTemperature => { Writable => 'integer' },
|
---|
731 | IncrementalTint => { Writable => 'integer' },
|
---|
732 | LuminanceAdjustmentAqua => { Writable => 'integer' },
|
---|
733 | LuminanceAdjustmentBlue => { Writable => 'integer' },
|
---|
734 | LuminanceAdjustmentGreen => { Writable => 'integer' },
|
---|
735 | LuminanceAdjustmentMagenta => { Writable => 'integer' },
|
---|
736 | LuminanceAdjustmentOrange => { Writable => 'integer' },
|
---|
737 | LuminanceAdjustmentPurple => { Writable => 'integer' },
|
---|
738 | LuminanceAdjustmentRed => { Writable => 'integer' },
|
---|
739 | LuminanceAdjustmentYellow => { Writable => 'integer' },
|
---|
740 | ParametricDarks => { Writable => 'integer' },
|
---|
741 | ParametricHighlights => { Writable => 'integer' },
|
---|
742 | ParametricHighlightSplit => { Writable => 'integer' },
|
---|
743 | ParametricLights => { Writable => 'integer' },
|
---|
744 | ParametricMidtoneSplit => { Writable => 'integer' },
|
---|
745 | ParametricShadows => { Writable => 'integer' },
|
---|
746 | ParametricShadowSplit => { Writable => 'integer' },
|
---|
747 | SaturationAdjustmentAqua => { Writable => 'integer' },
|
---|
748 | SaturationAdjustmentBlue => { Writable => 'integer' },
|
---|
749 | SaturationAdjustmentGreen => { Writable => 'integer' },
|
---|
750 | SaturationAdjustmentMagenta => { Writable => 'integer' },
|
---|
751 | SaturationAdjustmentOrange => { Writable => 'integer' },
|
---|
752 | SaturationAdjustmentPurple => { Writable => 'integer' },
|
---|
753 | SaturationAdjustmentRed => { Writable => 'integer' },
|
---|
754 | SaturationAdjustmentYellow => { Writable => 'integer' },
|
---|
755 | SharpenDetail => { Writable => 'integer' },
|
---|
756 | SharpenEdgeMasking => { Writable => 'integer' },
|
---|
757 | SharpenRadius => { Writable => 'real' },
|
---|
758 | SplitToningBalance => { Writable => 'integer' },
|
---|
759 | SplitToningHighlightHue => { Writable => 'integer' },
|
---|
760 | SplitToningHighlightSaturation => { Writable => 'integer' },
|
---|
761 | SplitToningShadowHue => { Writable => 'integer' },
|
---|
762 | SplitToningShadowSaturation => { Writable => 'integer' },
|
---|
763 | Vibrance => { Writable => 'integer' },
|
---|
764 | );
|
---|
765 |
|
---|
766 | # Tiff schema properties (tiff)
|
---|
767 | %Image::ExifTool::XMP::tiff = (
|
---|
768 | %xmpTableDefaults,
|
---|
769 | GROUPS => { 1 => 'XMP-tiff', 2 => 'Image' },
|
---|
770 | NAMESPACE => 'tiff',
|
---|
771 | NOTES => 'EXIF schema for TIFF tags.',
|
---|
772 | ImageWidth => { Writable => 'integer' },
|
---|
773 | ImageLength => {
|
---|
774 | Name => 'ImageHeight',
|
---|
775 | Writable => 'integer',
|
---|
776 | },
|
---|
777 | BitsPerSample => { Writable => 'integer', List => 'Seq' },
|
---|
778 | Compression => {
|
---|
779 | Writable => 'integer',
|
---|
780 | PrintConv => \%Image::ExifTool::Exif::compression,
|
---|
781 | },
|
---|
782 | PhotometricInterpretation => {
|
---|
783 | Writable => 'integer',
|
---|
784 | PrintConv => \%Image::ExifTool::Exif::photometricInterpretation,
|
---|
785 | },
|
---|
786 | Orientation => {
|
---|
787 | Writable => 'integer',
|
---|
788 | PrintConv => \%Image::ExifTool::Exif::orientation,
|
---|
789 | },
|
---|
790 | SamplesPerPixel => { Writable => 'integer' },
|
---|
791 | PlanarConfiguration => {
|
---|
792 | Writable => 'integer',
|
---|
793 | PrintConv => {
|
---|
794 | 1 => 'Chunky',
|
---|
795 | 2 => 'Planar',
|
---|
796 | },
|
---|
797 | },
|
---|
798 | YCbCrSubSampling => {
|
---|
799 | PrintConv => {
|
---|
800 | '1 1' => 'YCbCr4:4:4',
|
---|
801 | '2 1' => 'YCbCr4:2:2',
|
---|
802 | '2 2' => 'YCbCr4:2:0',
|
---|
803 | '4 1' => 'YCbCr4:1:1',
|
---|
804 | '4 2' => 'YCbCr4:1:0',
|
---|
805 | '1 2' => 'YCbCr4:4:0',
|
---|
806 | },
|
---|
807 | },
|
---|
808 | YCbCrPositioning => {
|
---|
809 | Writable => 'integer',
|
---|
810 | PrintConv => {
|
---|
811 | 1 => 'Centered',
|
---|
812 | 2 => 'Co-sited',
|
---|
813 | },
|
---|
814 | },
|
---|
815 | XResolution => { Writable => 'rational' },
|
---|
816 | YResolution => { Writable => 'rational' },
|
---|
817 | ResolutionUnit => {
|
---|
818 | Writable => 'integer',
|
---|
819 | PrintConv => {
|
---|
820 | 1 => 'None',
|
---|
821 | 2 => 'inches',
|
---|
822 | 3 => 'cm',
|
---|
823 | },
|
---|
824 | },
|
---|
825 | TransferFunction => { Writable => 'integer', List => 'Seq' },
|
---|
826 | WhitePoint => { Writable => 'rational', List => 'Seq' },
|
---|
827 | PrimaryChromaticities => { Writable => 'rational', List => 'Seq' },
|
---|
828 | YCbCrCoefficients => { Writable => 'rational', List => 'Seq' },
|
---|
829 | ReferenceBlackWhite => { Writable => 'rational', List => 'Seq' },
|
---|
830 | DateTime => {
|
---|
831 | Description => 'Date/Time Modified',
|
---|
832 | %dateTimeInfo,
|
---|
833 | },
|
---|
834 | ImageDescription => { Writable => 'lang-alt' },
|
---|
835 | Make => { Groups => { 2 => 'Camera' } },
|
---|
836 | Model => {
|
---|
837 | Description => 'Camera Model Name',
|
---|
838 | Groups => { 2 => 'Camera' },
|
---|
839 | },
|
---|
840 | Software => { },
|
---|
841 | Artist => { Groups => { 2 => 'Author' } },
|
---|
842 | Copyright => {
|
---|
843 | Groups => { 2 => 'Author' },
|
---|
844 | Writable => 'lang-alt',
|
---|
845 | },
|
---|
846 | NativeDigest => { }, #PH
|
---|
847 | );
|
---|
848 |
|
---|
849 | # Exif schema properties (exif)
|
---|
850 | %Image::ExifTool::XMP::exif = (
|
---|
851 | %xmpTableDefaults,
|
---|
852 | GROUPS => { 1 => 'XMP-exif', 2 => 'Image' },
|
---|
853 | NAMESPACE => 'exif',
|
---|
854 | NOTES => 'EXIF schema for EXIF tags.',
|
---|
855 | ExifVersion => { },
|
---|
856 | FlashpixVersion => { },
|
---|
857 | ColorSpace => {
|
---|
858 | Writable => 'integer',
|
---|
859 | PrintConv => {
|
---|
860 | 1 => 'sRGB',
|
---|
861 | 2 => 'Adobe RGB',
|
---|
862 | 0xffff => 'Uncalibrated',
|
---|
863 | 0xffffffff => 'Uncalibrated',
|
---|
864 | },
|
---|
865 | },
|
---|
866 | ComponentsConfiguration => {
|
---|
867 | List => 'Seq',
|
---|
868 | Writable => 'integer',
|
---|
869 | PrintConv => {
|
---|
870 | 0 => '.',
|
---|
871 | 1 => 'Y',
|
---|
872 | 2 => 'Cb',
|
---|
873 | 3 => 'Cr',
|
---|
874 | 4 => 'R',
|
---|
875 | 5 => 'G',
|
---|
876 | 6 => 'B',
|
---|
877 | },
|
---|
878 | },
|
---|
879 | CompressedBitsPerPixel => {
|
---|
880 | Writable => 'rational',
|
---|
881 | },
|
---|
882 | PixelXDimension => {
|
---|
883 | Name => 'ExifImageWidth',
|
---|
884 | Writable => 'integer',
|
---|
885 | },
|
---|
886 | PixelYDimension => {
|
---|
887 | Name => 'ExifImageLength',
|
---|
888 | Writable => 'integer',
|
---|
889 | },
|
---|
890 | MakerNote => { },
|
---|
891 | UserComment => {
|
---|
892 | Writable => 'lang-alt',
|
---|
893 | },
|
---|
894 | RelatedSoundFile => { },
|
---|
895 | DateTimeOriginal => {
|
---|
896 | Description => 'Date/Time Original',
|
---|
897 | %dateTimeInfo,
|
---|
898 | },
|
---|
899 | DateTimeDigitized => {
|
---|
900 | Description => 'Date/Time Digitized',
|
---|
901 | %dateTimeInfo,
|
---|
902 | },
|
---|
903 | ExposureTime => {
|
---|
904 | Writable => 'rational',
|
---|
905 | PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
|
---|
906 | PrintConvInv => 'eval $val',
|
---|
907 | },
|
---|
908 | FNumber => {
|
---|
909 | Writable => 'rational',
|
---|
910 | PrintConv => 'sprintf("%.1f",$val)',
|
---|
911 | PrintConvInv => '$val',
|
---|
912 | },
|
---|
913 | ExposureProgram => {
|
---|
914 | Groups => { 2 => 'Camera' },
|
---|
915 | Writable => 'integer',
|
---|
916 | PrintConv => {
|
---|
917 | 1 => 'Manual',
|
---|
918 | 2 => 'Program AE',
|
---|
919 | 3 => 'Aperture-priority AE',
|
---|
920 | 4 => 'Shutter speed priority AE',
|
---|
921 | 5 => 'Creative (Slow speed)',
|
---|
922 | 6 => 'Action (High speed)',
|
---|
923 | 7 => 'Portrait',
|
---|
924 | 8 => 'Landscape',
|
---|
925 | },
|
---|
926 | },
|
---|
927 | SpectralSensitivity => {
|
---|
928 | Groups => { 2 => 'Camera' },
|
---|
929 | },
|
---|
930 | ISOSpeedRatings => {
|
---|
931 | Name => 'ISO',
|
---|
932 | Writable => 'integer',
|
---|
933 | List => 'Seq',
|
---|
934 | },
|
---|
935 | OECF => {
|
---|
936 | Name => 'Opto-ElectricConvFactor',
|
---|
937 | Groups => { 2 => 'Camera' },
|
---|
938 | SubDirectory => { },
|
---|
939 | Struct => 'OECF',
|
---|
940 | },
|
---|
941 | OECFColumns => {
|
---|
942 | Groups => { 2 => 'Camera' },
|
---|
943 | Writable => 'integer',
|
---|
944 | },
|
---|
945 | OECFRows => {
|
---|
946 | Groups => { 2 => 'Camera' },
|
---|
947 | Writable => 'integer',
|
---|
948 | },
|
---|
949 | OECFNames => {
|
---|
950 | Groups => { 2 => 'Camera' },
|
---|
951 | List => 'Seq',
|
---|
952 | },
|
---|
953 | OECFValues => {
|
---|
954 | Groups => { 2 => 'Camera' },
|
---|
955 | Writable => 'rational',
|
---|
956 | List => 'Seq',
|
---|
957 | },
|
---|
958 | ShutterSpeedValue => {
|
---|
959 | Writable => 'rational',
|
---|
960 | ValueConv => 'abs($val)<100 ? 1/(2**$val) : 0',
|
---|
961 | PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
|
---|
962 | ValueConvInv => '$val>0 ? -log($val)/log(2) : 0',
|
---|
963 | # do eval to convert things like '1/100'
|
---|
964 | PrintConvInv => 'eval $val',
|
---|
965 | },
|
---|
966 | ApertureValue => {
|
---|
967 | Writable => 'rational',
|
---|
968 | ValueConv => 'sqrt(2) ** $val',
|
---|
969 | PrintConv => 'sprintf("%.1f",$val)',
|
---|
970 | ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
|
---|
971 | PrintConvInv => '$val',
|
---|
972 | },
|
---|
973 | BrightnessValue => {
|
---|
974 | Writable => 'rational',
|
---|
975 | },
|
---|
976 | ExposureBiasValue => {
|
---|
977 | Name => 'ExposureCompensation',
|
---|
978 | Writable => 'rational',
|
---|
979 | PrintConv => 'Image::ExifTool::Exif::ConvertFraction($val)',
|
---|
980 | PrintConvInv => '$val',
|
---|
981 | },
|
---|
982 | MaxApertureValue => {
|
---|
983 | Groups => { 2 => 'Camera' },
|
---|
984 | Writable => 'rational',
|
---|
985 | ValueConv => 'sqrt(2) ** $val',
|
---|
986 | PrintConv => 'sprintf("%.1f",$val)',
|
---|
987 | ValueConvInv => '$val>0 ? 2*log($val)/log(2) : 0',
|
---|
988 | PrintConvInv => '$val',
|
---|
989 | },
|
---|
990 | SubjectDistance => {
|
---|
991 | Groups => { 2 => 'Camera' },
|
---|
992 | Writable => 'rational',
|
---|
993 | PrintConv => '$val eq "inf" ? $val : "$val m"',
|
---|
994 | PrintConvInv => '$val=~s/\s*m$//;$val',
|
---|
995 | },
|
---|
996 | MeteringMode => {
|
---|
997 | Groups => { 2 => 'Camera' },
|
---|
998 | Writable => 'integer',
|
---|
999 | PrintConv => {
|
---|
1000 | 1 => 'Average',
|
---|
1001 | 2 => 'Center-weighted average',
|
---|
1002 | 3 => 'Spot',
|
---|
1003 | 4 => 'Multi-spot',
|
---|
1004 | 5 => 'Multi-segment',
|
---|
1005 | 6 => 'Partial',
|
---|
1006 | 255 => 'Other',
|
---|
1007 | },
|
---|
1008 | },
|
---|
1009 | LightSource => {
|
---|
1010 | Groups => { 2 => 'Camera' },
|
---|
1011 | PrintConv => \%Image::ExifTool::Exif::lightSource,
|
---|
1012 | },
|
---|
1013 | Flash => {
|
---|
1014 | Groups => { 2 => 'Camera' },
|
---|
1015 | SubDirectory => { },
|
---|
1016 | Struct => 'Flash',
|
---|
1017 | },
|
---|
1018 | FlashFired => {
|
---|
1019 | Groups => { 2 => 'Camera' },
|
---|
1020 | Writable => 'boolean',
|
---|
1021 | },
|
---|
1022 | FlashReturn => {
|
---|
1023 | Groups => { 2 => 'Camera' },
|
---|
1024 | Writable => 'integer',
|
---|
1025 | PrintConv => {
|
---|
1026 | 0 => 'No return detection',
|
---|
1027 | 2 => 'Return not detected',
|
---|
1028 | 3 => 'Return detected',
|
---|
1029 | },
|
---|
1030 | },
|
---|
1031 | FlashMode => {
|
---|
1032 | Groups => { 2 => 'Camera' },
|
---|
1033 | Writable => 'integer',
|
---|
1034 | PrintConv => {
|
---|
1035 | 0 => 'Unknown',
|
---|
1036 | 1 => 'On',
|
---|
1037 | 2 => 'Off',
|
---|
1038 | 3 => 'Auto',
|
---|
1039 | },
|
---|
1040 | },
|
---|
1041 | FlashFunction => {
|
---|
1042 | Groups => { 2 => 'Camera' },
|
---|
1043 | Writable => 'boolean',
|
---|
1044 | },
|
---|
1045 | FlashRedEyeMode => {
|
---|
1046 | Groups => { 2 => 'Camera' },
|
---|
1047 | Writable => 'boolean',
|
---|
1048 | },
|
---|
1049 | FocalLength=> {
|
---|
1050 | Groups => { 2 => 'Camera' },
|
---|
1051 | Writable => 'rational',
|
---|
1052 | PrintConv => 'sprintf("%.1fmm",$val)',
|
---|
1053 | PrintConvInv => '$val=~s/mm$//;$val',
|
---|
1054 | },
|
---|
1055 | SubjectArea => {
|
---|
1056 | Writable => 'integer',
|
---|
1057 | List => 'Seq',
|
---|
1058 | },
|
---|
1059 | FlashEnergy => {
|
---|
1060 | Groups => { 2 => 'Camera' },
|
---|
1061 | Writable => 'rational',
|
---|
1062 | },
|
---|
1063 | SpatialFrequencyResponse => {
|
---|
1064 | Groups => { 2 => 'Camera' },
|
---|
1065 | SubDirectory => { },
|
---|
1066 | Struct => 'OECF',
|
---|
1067 | },
|
---|
1068 | SpatialFrequencyResponseColumns => {
|
---|
1069 | Groups => { 2 => 'Camera' },
|
---|
1070 | Writable => 'integer',
|
---|
1071 | },
|
---|
1072 | SpatialFrequencyResponseRows => {
|
---|
1073 | Groups => { 2 => 'Camera' },
|
---|
1074 | Writable => 'integer',
|
---|
1075 | },
|
---|
1076 | SpatialFrequencyResponseNames => {
|
---|
1077 | Groups => { 2 => 'Camera' },
|
---|
1078 | List => 'Seq',
|
---|
1079 | },
|
---|
1080 | SpatialFrequencyResponseValues => {
|
---|
1081 | Groups => { 2 => 'Camera' },
|
---|
1082 | Writable => 'rational',
|
---|
1083 | List => 'Seq',
|
---|
1084 | },
|
---|
1085 | FocalPlaneXResolution => {
|
---|
1086 | Groups => { 2 => 'Camera' },
|
---|
1087 | Writable => 'rational',
|
---|
1088 | },
|
---|
1089 | FocalPlaneYResolution => {
|
---|
1090 | Groups => { 2 => 'Camera' },
|
---|
1091 | Writable => 'rational',
|
---|
1092 | },
|
---|
1093 | FocalPlaneResolutionUnit => {
|
---|
1094 | Groups => { 2 => 'Camera' },
|
---|
1095 | Writable => 'integer',
|
---|
1096 | PrintConv => {
|
---|
1097 | 1 => 'None', # (not standard EXIF)
|
---|
1098 | 2 => 'inches',
|
---|
1099 | 3 => 'cm',
|
---|
1100 | 4 => 'mm', # (not standard EXIF)
|
---|
1101 | 5 => 'um', # (not standard EXIF)
|
---|
1102 | },
|
---|
1103 | },
|
---|
1104 | SubjectLocation => {
|
---|
1105 | Writable => 'integer',
|
---|
1106 | List => 'Seq',
|
---|
1107 | },
|
---|
1108 | ExposureIndex => {
|
---|
1109 | Writable => 'rational',
|
---|
1110 | },
|
---|
1111 | SensingMethod => {
|
---|
1112 | Groups => { 2 => 'Camera' },
|
---|
1113 | Writable => 'integer',
|
---|
1114 | PrintConv => {
|
---|
1115 | 1 => 'Not defined',
|
---|
1116 | 2 => 'One-chip color area',
|
---|
1117 | 3 => 'Two-chip color area',
|
---|
1118 | 4 => 'Three-chip color area',
|
---|
1119 | 5 => 'Color sequential area',
|
---|
1120 | 7 => 'Trilinear',
|
---|
1121 | 8 => 'Color sequential linear',
|
---|
1122 | },
|
---|
1123 | },
|
---|
1124 | FileSource => {
|
---|
1125 | Writable => 'integer',
|
---|
1126 | PrintConv => {
|
---|
1127 | 1 => 'Film Scanner',
|
---|
1128 | 2 => 'Reflection Print Scanner',
|
---|
1129 | 3 => 'Digital Camera',
|
---|
1130 | }
|
---|
1131 | },
|
---|
1132 | SceneType => { Writable => 'integer', PrintConv => { 1 => 'Directly photographed' } },
|
---|
1133 | CFAPattern => {
|
---|
1134 | SubDirectory => { },
|
---|
1135 | Struct => 'CFAPattern',
|
---|
1136 | },
|
---|
1137 | CFAPatternColumns => { Writable => 'integer' },
|
---|
1138 | CFAPatternRows => { Writable => 'integer' },
|
---|
1139 | CFAPatternValues => { List => 'Seq', Writable => 'integer' },
|
---|
1140 | CustomRendered => {
|
---|
1141 | Writable => 'integer',
|
---|
1142 | PrintConv => {
|
---|
1143 | 0 => 'Normal',
|
---|
1144 | 1 => 'Custom',
|
---|
1145 | },
|
---|
1146 | },
|
---|
1147 | ExposureMode => {
|
---|
1148 | Groups => { 2 => 'Camera' },
|
---|
1149 | Writable => 'integer',
|
---|
1150 | PrintConv => {
|
---|
1151 | 0 => 'Auto',
|
---|
1152 | 1 => 'Manual',
|
---|
1153 | 2 => 'Auto bracket',
|
---|
1154 | },
|
---|
1155 | },
|
---|
1156 | WhiteBalance => {
|
---|
1157 | Groups => { 2 => 'Camera' },
|
---|
1158 | Writable => 'integer',
|
---|
1159 | PrintConv => {
|
---|
1160 | 0 => 'Auto',
|
---|
1161 | 1 => 'Manual',
|
---|
1162 | },
|
---|
1163 | },
|
---|
1164 | DigitalZoomRatio => { Writable => 'rational' },
|
---|
1165 | FocalLengthIn35mmFilm => {
|
---|
1166 | Name => 'FocalLengthIn35mmFormat',
|
---|
1167 | Writable => 'integer',
|
---|
1168 | Groups => { 2 => 'Camera' },
|
---|
1169 | },
|
---|
1170 | SceneCaptureType => {
|
---|
1171 | Groups => { 2 => 'Camera' },
|
---|
1172 | Writable => 'integer',
|
---|
1173 | PrintConv => {
|
---|
1174 | 0 => 'Standard',
|
---|
1175 | 1 => 'Landscape',
|
---|
1176 | 2 => 'Portrait',
|
---|
1177 | 3 => 'Night',
|
---|
1178 | },
|
---|
1179 | },
|
---|
1180 | GainControl => {
|
---|
1181 | Groups => { 2 => 'Camera' },
|
---|
1182 | Writable => 'integer',
|
---|
1183 | PrintConv => {
|
---|
1184 | 0 => 'None',
|
---|
1185 | 1 => 'Low gain up',
|
---|
1186 | 2 => 'High gain up',
|
---|
1187 | 3 => 'Low gain down',
|
---|
1188 | 4 => 'High gain down',
|
---|
1189 | },
|
---|
1190 | },
|
---|
1191 | Contrast => {
|
---|
1192 | Groups => { 2 => 'Camera' },
|
---|
1193 | Writable => 'integer',
|
---|
1194 | PrintConv => {
|
---|
1195 | 0 => 'Normal',
|
---|
1196 | 1 => 'Low',
|
---|
1197 | 2 => 'High',
|
---|
1198 | },
|
---|
1199 | PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
|
---|
1200 | },
|
---|
1201 | Saturation => {
|
---|
1202 | Groups => { 2 => 'Camera' },
|
---|
1203 | Writable => 'integer',
|
---|
1204 | PrintConv => {
|
---|
1205 | 0 => 'Normal',
|
---|
1206 | 1 => 'Low',
|
---|
1207 | 2 => 'High',
|
---|
1208 | },
|
---|
1209 | PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
|
---|
1210 | },
|
---|
1211 | Sharpness => {
|
---|
1212 | Groups => { 2 => 'Camera' },
|
---|
1213 | Writable => 'integer',
|
---|
1214 | PrintConv => {
|
---|
1215 | 0 => 'Normal',
|
---|
1216 | 1 => 'Soft',
|
---|
1217 | 2 => 'Hard',
|
---|
1218 | },
|
---|
1219 | PrintConvInv => 'Image::ExifTool::Exif::ConvertParameter($val)',
|
---|
1220 | },
|
---|
1221 | DeviceSettingDescription => {
|
---|
1222 | Groups => { 2 => 'Camera' },
|
---|
1223 | SubDirectory => { },
|
---|
1224 | Struct => 'DeviceSettings',
|
---|
1225 | },
|
---|
1226 | DeviceSettingDescriptionColumns => {
|
---|
1227 | Groups => { 2 => 'Camera' },
|
---|
1228 | Writable => 'integer',
|
---|
1229 | },
|
---|
1230 | DeviceSettingDescriptionRows => {
|
---|
1231 | Groups => { 2 => 'Camera' },
|
---|
1232 | Writable => 'integer',
|
---|
1233 | },
|
---|
1234 | DeviceSettingDescriptionSettings => {
|
---|
1235 | Groups => { 2 => 'Camera' },
|
---|
1236 | List => 'Seq',
|
---|
1237 | },
|
---|
1238 | SubjectDistanceRange => {
|
---|
1239 | Groups => { 2 => 'Camera' },
|
---|
1240 | Writable => 'integer',
|
---|
1241 | PrintConv => {
|
---|
1242 | 0 => 'Unknown',
|
---|
1243 | 1 => 'Macro',
|
---|
1244 | 2 => 'Close',
|
---|
1245 | 3 => 'Distant',
|
---|
1246 | },
|
---|
1247 | },
|
---|
1248 | ImageUniqueID => { },
|
---|
1249 | GPSVersionID => { Groups => { 2 => 'Location' } },
|
---|
1250 | GPSLatitude => { Groups => { 2 => 'Location' }, %latConv },
|
---|
1251 | GPSLongitude => { Groups => { 2 => 'Location' }, %longConv },
|
---|
1252 | GPSAltitudeRef => {
|
---|
1253 | Groups => { 2 => 'Location' },
|
---|
1254 | Writable => 'integer',
|
---|
1255 | PrintConv => {
|
---|
1256 | 0 => 'Above Sea Level',
|
---|
1257 | 1 => 'Below Sea Level',
|
---|
1258 | },
|
---|
1259 | },
|
---|
1260 | GPSAltitude => {
|
---|
1261 | Groups => { 2 => 'Location' },
|
---|
1262 | Writable => 'rational',
|
---|
1263 | PrintConv => '$val eq "inf" ? $val : "$val m"',
|
---|
1264 | PrintConvInv => '$val=~s/\s*m$//;$val',
|
---|
1265 | },
|
---|
1266 | GPSTimeStamp => { %dateTimeInfo },
|
---|
1267 | GPSSatellites => { Groups => { 2 => 'Location' } },
|
---|
1268 | GPSStatus => {
|
---|
1269 | Groups => { 2 => 'Location' },
|
---|
1270 | PrintConv => {
|
---|
1271 | A => 'Measurement In Progress',
|
---|
1272 | V => 'Measurement Interoperability',
|
---|
1273 | },
|
---|
1274 | },
|
---|
1275 | GPSMeasureMode => {
|
---|
1276 | Groups => { 2 => 'Location' },
|
---|
1277 | Writable => 'integer',
|
---|
1278 | PrintConv => {
|
---|
1279 | 2 => '2-Dimensional',
|
---|
1280 | 3 => '3-Dimensional',
|
---|
1281 | },
|
---|
1282 | },
|
---|
1283 | GPSDOP => {
|
---|
1284 | Groups => { 2 => 'Location' },
|
---|
1285 | Writable => 'rational',
|
---|
1286 | },
|
---|
1287 | GPSSpeedRef => {
|
---|
1288 | Groups => { 2 => 'Location' },
|
---|
1289 | PrintConv => {
|
---|
1290 | K => 'km/h',
|
---|
1291 | M => 'mph',
|
---|
1292 | N => 'knots',
|
---|
1293 | },
|
---|
1294 | },
|
---|
1295 | GPSSpeed => {
|
---|
1296 | Groups => { 2 => 'Location' },
|
---|
1297 | Writable => 'rational',
|
---|
1298 | },
|
---|
1299 | GPSTrackRef => {
|
---|
1300 | Groups => { 2 => 'Location' },
|
---|
1301 | PrintConv => {
|
---|
1302 | M => 'Magnetic North',
|
---|
1303 | T => 'True North',
|
---|
1304 | },
|
---|
1305 | },
|
---|
1306 | GPSTrack => {
|
---|
1307 | Groups => { 2 => 'Location' },
|
---|
1308 | Writable => 'rational',
|
---|
1309 | },
|
---|
1310 | GPSImgDirectionRef => {
|
---|
1311 | PrintConv => {
|
---|
1312 | M => 'Magnetic North',
|
---|
1313 | T => 'True North',
|
---|
1314 | },
|
---|
1315 | },
|
---|
1316 | GPSImgDirection => {
|
---|
1317 | Groups => { 2 => 'Location' },
|
---|
1318 | Writable => 'rational',
|
---|
1319 | },
|
---|
1320 | GPSMapDatum => { Groups => { 2 => 'Location' } },
|
---|
1321 | GPSDestLatitude => { Groups => { 2 => 'Location' }, %latConv },
|
---|
1322 | GPSDestLongitude=> { Groups => { 2 => 'Location' }, %longConv },
|
---|
1323 | GPSDestBearingRef => {
|
---|
1324 | Groups => { 2 => 'Location' },
|
---|
1325 | PrintConv => {
|
---|
1326 | M => 'Magnetic North',
|
---|
1327 | T => 'True North',
|
---|
1328 | },
|
---|
1329 | },
|
---|
1330 | GPSDestBearing => {
|
---|
1331 | Groups => { 2 => 'Location' },
|
---|
1332 | Writable => 'rational',
|
---|
1333 | },
|
---|
1334 | GPSDestDistanceRef => {
|
---|
1335 | Groups => { 2 => 'Location' },
|
---|
1336 | PrintConv => {
|
---|
1337 | K => 'Kilometers',
|
---|
1338 | M => 'Miles',
|
---|
1339 | N => 'Nautical Miles',
|
---|
1340 | },
|
---|
1341 | },
|
---|
1342 | GPSDestDistance => {
|
---|
1343 | Groups => { 2 => 'Location' },
|
---|
1344 | Writable => 'rational',
|
---|
1345 | },
|
---|
1346 | GPSProcessingMethod => { Groups => { 2 => 'Location' } },
|
---|
1347 | GPSAreaInformation => { Groups => { 2 => 'Location' } },
|
---|
1348 | GPSDifferential => {
|
---|
1349 | Groups => { 2 => 'Location' },
|
---|
1350 | Writable => 'integer',
|
---|
1351 | PrintConv => {
|
---|
1352 | 0 => 'No Correction',
|
---|
1353 | 1 => 'Differential Corrected',
|
---|
1354 | },
|
---|
1355 | },
|
---|
1356 | NativeDigest => { }, #PH
|
---|
1357 | );
|
---|
1358 |
|
---|
1359 | # Auxiliary schema properties (aux) - not fully documented
|
---|
1360 | %Image::ExifTool::XMP::aux = (
|
---|
1361 | %xmpTableDefaults,
|
---|
1362 | GROUPS => { 1 => 'XMP-aux', 2 => 'Camera' },
|
---|
1363 | NAMESPACE => 'aux',
|
---|
1364 | NOTES => 'Photoshop Auxiliary schema tags.',
|
---|
1365 | Firmware => { }, #7
|
---|
1366 | FlashCompensation => { Writable => 'rational' }, #7
|
---|
1367 | ImageNumber => { }, #7
|
---|
1368 | LensInfo => { }, #7
|
---|
1369 | Lens => { },
|
---|
1370 | OwnerName => { }, #7
|
---|
1371 | SerialNumber => { },
|
---|
1372 | );
|
---|
1373 |
|
---|
1374 | # IPTC Core schema properties (Iptc4xmpCore)
|
---|
1375 | %Image::ExifTool::XMP::iptcCore = (
|
---|
1376 | %xmpTableDefaults,
|
---|
1377 | GROUPS => { 1 => 'XMP-iptcCore', 2 => 'Author' },
|
---|
1378 | NAMESPACE => 'Iptc4xmpCore',
|
---|
1379 | NOTES => q{
|
---|
1380 | IPTC Core schema tags. The actual IPTC Core namespace schema prefix is
|
---|
1381 | "Iptc4xmpCore", which is the prefix recorded in the file, but ExifTool
|
---|
1382 | shortens this for the "XMP-iptcCore" family 1 group name.
|
---|
1383 | },
|
---|
1384 | CountryCode => { Groups => { 2 => 'Location' } },
|
---|
1385 | CreatorContactInfo => {
|
---|
1386 | SubDirectory => { },
|
---|
1387 | Struct => 'ContactInfo',
|
---|
1388 | },
|
---|
1389 | CreatorContactInfoCiAdrCity => { Description => 'Creator City' },
|
---|
1390 | CreatorContactInfoCiAdrCtry => { Description => 'Creator Country' },
|
---|
1391 | CreatorContactInfoCiAdrExtadr => { Description => 'Creator Address' },
|
---|
1392 | CreatorContactInfoCiAdrPcode => { Description => 'Creator Postal Code' },
|
---|
1393 | CreatorContactInfoCiAdrRegion => { Description => 'Creator Region' },
|
---|
1394 | CreatorContactInfoCiEmailWork => { Description => 'Creator Work Email' },
|
---|
1395 | CreatorContactInfoCiTelWork => { Description => 'Creator Work Telephone' },
|
---|
1396 | CreatorContactInfoCiUrlWork => { Description => 'Creator Work URL' },
|
---|
1397 | IntellectualGenre => { Groups => { 2 => 'Other' } },
|
---|
1398 | Location => { Groups => { 2 => 'Location' } },
|
---|
1399 | Scene => { Groups => { 2 => 'Other' }, List => 'Bag' },
|
---|
1400 | SubjectCode => { Groups => { 2 => 'Other' }, List => 'Bag' },
|
---|
1401 | );
|
---|
1402 |
|
---|
1403 | # PixelLive schema properties (PixelLive) (ref 3)
|
---|
1404 | %Image::ExifTool::XMP::PixelLive = (
|
---|
1405 | GROUPS => { 1 => 'XMP-PixelLive', 2 => 'Image' },
|
---|
1406 | NAMESPACE => 'PixelLive',
|
---|
1407 | WRITE_PROC => \&WriteXMP,
|
---|
1408 | NOTES => q{
|
---|
1409 | PixelLive schema tags. These tags are not writable becase they are very
|
---|
1410 | uncommon and I haven't been able to locate a reference which gives the
|
---|
1411 | namespace URI.
|
---|
1412 | },
|
---|
1413 | AUTHOR => { Name => 'Author', Avoid => 1, Groups => { 2 => 'Author'} },
|
---|
1414 | COMMENTS => { Name => 'Comments', Avoid => 1 },
|
---|
1415 | COPYRIGHT => { Name => 'Copyright',Avoid => 1, Groups => { 2 => 'Author'} },
|
---|
1416 | DATE => { Name => 'Date', Avoid => 1, Groups => { 2 => 'Time'} },
|
---|
1417 | GENRE => { Name => 'Genre', Avoid => 1 },
|
---|
1418 | TITLE => { Name => 'Title', Avoid => 1 },
|
---|
1419 | );
|
---|
1420 |
|
---|
1421 | # Picture Licensing Universal System schema properties (xmpPLUS)
|
---|
1422 | %Image::ExifTool::XMP::xmpPLUS = (
|
---|
1423 | %xmpTableDefaults,
|
---|
1424 | GROUPS => { 1 => 'XMP-xmpPLUS', 2 => 'Author' },
|
---|
1425 | NAMESPACE => 'xmpPLUS',
|
---|
1426 | NOTES => 'XMP Picture Licensing Universal System (PLUS) schema tags.',
|
---|
1427 | CreditLineReq => { Writable => 'boolean' },
|
---|
1428 | ReuseAllowed => { Writable => 'boolean' },
|
---|
1429 | );
|
---|
1430 |
|
---|
1431 | # Creative Commons schema properties (cc) (ref 5)
|
---|
1432 | %Image::ExifTool::XMP::cc = (
|
---|
1433 | %xmpTableDefaults,
|
---|
1434 | GROUPS => { 1 => 'XMP-cc', 2 => 'Author' },
|
---|
1435 | NAMESPACE => 'cc',
|
---|
1436 | NOTES => 'Creative Commons schema tags.',
|
---|
1437 | license => { },
|
---|
1438 | );
|
---|
1439 |
|
---|
1440 | # Description Explorer schema properties (dex) (ref 6)
|
---|
1441 | %Image::ExifTool::XMP::dex = (
|
---|
1442 | %xmpTableDefaults,
|
---|
1443 | GROUPS => { 1 => 'XMP-dex', 2 => 'Image' },
|
---|
1444 | NAMESPACE => 'dex',
|
---|
1445 | NOTES => q{
|
---|
1446 | Description Explorer schema tags. These tags are not very common. The
|
---|
1447 | Source and Rating tags are avoided when writing due to name conflicts with
|
---|
1448 | other XMP tags.
|
---|
1449 | },
|
---|
1450 | crc32 => { Name => 'CRC32', Writable => 'integer' },
|
---|
1451 | source => { Avoid => 1 },
|
---|
1452 | shortdescription => {
|
---|
1453 | Name => 'ShortDescription',
|
---|
1454 | Writable => 'lang-alt',
|
---|
1455 | },
|
---|
1456 | licensetype => {
|
---|
1457 | Name => 'LicenseType',
|
---|
1458 | PrintConv => {
|
---|
1459 | unknown => 'Unknown',
|
---|
1460 | shareware => 'Shareware',
|
---|
1461 | freeware => 'Freeware',
|
---|
1462 | adware => 'Adware',
|
---|
1463 | demo => 'Demo',
|
---|
1464 | commercial => 'Commercial',
|
---|
1465 | 'public domain' => 'Public Domain',
|
---|
1466 | 'open source' => 'Open Source',
|
---|
1467 | },
|
---|
1468 | },
|
---|
1469 | revision => { },
|
---|
1470 | rating => { Avoid => 1 },
|
---|
1471 | os => { Name => 'OS', Writable => 'integer' },
|
---|
1472 | ffid => { Name => 'FFID' },
|
---|
1473 | );
|
---|
1474 |
|
---|
1475 | # IView MediaPro schema properties (mediapro) (ref PH)
|
---|
1476 | %Image::ExifTool::XMP::MediaPro = (
|
---|
1477 | %xmpTableDefaults,
|
---|
1478 | GROUPS => { 1 => 'XMP-mediapro', 2 => 'Image' },
|
---|
1479 | NAMESPACE => 'mediapro',
|
---|
1480 | NOTES => 'IView MediaPro schema tags.',
|
---|
1481 | Event => { },
|
---|
1482 | Location => {
|
---|
1483 | Avoid => 1,
|
---|
1484 | Groups => { 2 => 'Location' },
|
---|
1485 | Notes => 'avoided due to conflict with XMP-iptcCore:Location',
|
---|
1486 | },
|
---|
1487 | Status => { },
|
---|
1488 | People => { List => 'Bag' },
|
---|
1489 | UserFields => { List => 'Bag' },
|
---|
1490 | CatalogSets => { List => 'Bag' },
|
---|
1491 | );
|
---|
1492 |
|
---|
1493 | # Microsoft Photo schema properties (MicrosoftPhoto) (ref PH)
|
---|
1494 | %Image::ExifTool::XMP::Microsoft = (
|
---|
1495 | %xmpTableDefaults,
|
---|
1496 | GROUPS => { 1 => 'XMP-microsoft', 2 => 'Image' },
|
---|
1497 | NAMESPACE => 'MicrosoftPhoto',
|
---|
1498 | NOTES => q{
|
---|
1499 | Microsoft Photo schema tags. This is likely not a complete list, but
|
---|
1500 | represents tags which have been observed in sample images. The actual
|
---|
1501 | namespace prefix is "MicrosoftPhoto", but ExifTool shortens this to
|
---|
1502 | "XMP-microsoft" in the family 1 group name.
|
---|
1503 | },
|
---|
1504 | CameraSerialNumber => { },
|
---|
1505 | DateAcquired => { %dateTimeInfo },
|
---|
1506 | FlashManufacturer => { },
|
---|
1507 | FlashModel => { },
|
---|
1508 | LastKeywordIPTC => { List => 'Bag' },
|
---|
1509 | LastKeywordXMP => { List => 'Bag' },
|
---|
1510 | LensManufacturer => { },
|
---|
1511 | LensModel => { },
|
---|
1512 | Rating => {
|
---|
1513 | Name => 'RatingPercent',
|
---|
1514 | Notes => q{
|
---|
1515 | normal Rating values of 1,2,3,4 and 5 stars correspond to RatingPercent
|
---|
1516 | values of 1,25,50,75 and 99 respectively
|
---|
1517 | },
|
---|
1518 | },
|
---|
1519 | );
|
---|
1520 |
|
---|
1521 | # Adobe Lightroom schema properties (lr) (ref PH)
|
---|
1522 | %Image::ExifTool::XMP::Lightroom = (
|
---|
1523 | %xmpTableDefaults,
|
---|
1524 | GROUPS => { 1 => 'XMP-lr', 2 => 'Image' },
|
---|
1525 | NAMESPACE => 'lr',
|
---|
1526 | NOTES => 'Adobe Lightroom "lr" schema tags.',
|
---|
1527 | privateRTKInfo => { },
|
---|
1528 | hierarchicalSubject => { List => 'Bag' },
|
---|
1529 | );
|
---|
1530 |
|
---|
1531 | # DICOM schema properties (DICOM) (ref PH, written by CS3)
|
---|
1532 | %Image::ExifTool::XMP::DICOM = (
|
---|
1533 | %xmpTableDefaults,
|
---|
1534 | GROUPS => { 1 => 'XMP-DICOM', 2 => 'Image' },
|
---|
1535 | NAMESPACE => 'DICOM',
|
---|
1536 | NOTES => 'DICOM schema tags.',
|
---|
1537 | # change some tag names to correspond with DICOM tags
|
---|
1538 | PatientName => { Name => 'PatientsName' },
|
---|
1539 | PatientID => { },
|
---|
1540 | PatientSex => { Name => 'PatientsSex' },
|
---|
1541 | PatientDOB => {
|
---|
1542 | Name => 'PatientsBirthDate',
|
---|
1543 | %dateTimeInfo,
|
---|
1544 | },
|
---|
1545 | StudyID => { },
|
---|
1546 | StudyPhysician => { },
|
---|
1547 | StudyDateTime => { %dateTimeInfo },
|
---|
1548 | StudyDescription => { },
|
---|
1549 | SeriesNumber => { },
|
---|
1550 | SeriesModality => { },
|
---|
1551 | SeriesDateTime => { %dateTimeInfo },
|
---|
1552 | SeriesDescription => { },
|
---|
1553 | EquipmentInstitution => { },
|
---|
1554 | EquipmentManufacturer => { },
|
---|
1555 | );
|
---|
1556 |
|
---|
1557 | # table to add tags in other namespaces
|
---|
1558 | %Image::ExifTool::XMP::other = (
|
---|
1559 | GROUPS => { 2 => 'Unknown' },
|
---|
1560 | LANG_INFO => \&GetLangInfo,
|
---|
1561 | );
|
---|
1562 |
|
---|
1563 | # Composite XMP tags
|
---|
1564 | %Image::ExifTool::XMP::Composite = (
|
---|
1565 | # get latitude/logitude reference from XMP lat/long tags
|
---|
1566 | # (used to set EXIF GPS position from XMP tags)
|
---|
1567 | GPSLatitudeRef => {
|
---|
1568 | Require => {
|
---|
1569 | 0 => 'XMP:GPSLatitude',
|
---|
1570 | },
|
---|
1571 | ValueConv => '$val[0] < 0 ? "S" : "N"',
|
---|
1572 | PrintConv => {
|
---|
1573 | N => 'North',
|
---|
1574 | S => 'South',
|
---|
1575 | },
|
---|
1576 | },
|
---|
1577 | GPSLongitudeRef => {
|
---|
1578 | Require => {
|
---|
1579 | 0 => 'XMP:GPSLongitude',
|
---|
1580 | },
|
---|
1581 | ValueConv => '$val[0] < 0 ? "W" : "E"',
|
---|
1582 | PrintConv => {
|
---|
1583 | E => 'East',
|
---|
1584 | W => 'West',
|
---|
1585 | },
|
---|
1586 | },
|
---|
1587 | );
|
---|
1588 |
|
---|
1589 | # add our composite tags
|
---|
1590 | Image::ExifTool::AddCompositeTags('Image::ExifTool::XMP');
|
---|
1591 |
|
---|
1592 | #------------------------------------------------------------------------------
|
---|
1593 | # AutoLoad our writer routines when necessary
|
---|
1594 | #
|
---|
1595 | sub AUTOLOAD
|
---|
1596 | {
|
---|
1597 | return Image::ExifTool::DoAutoLoad($AUTOLOAD, @_);
|
---|
1598 | }
|
---|
1599 |
|
---|
1600 | #------------------------------------------------------------------------------
|
---|
1601 | # Escape necessary XML characters in UTF-8 string
|
---|
1602 | # Inputs: 0) string to be escaped
|
---|
1603 | # Returns: escaped string
|
---|
1604 | my %charName = ('"'=>'quot', '&'=>'amp', "'"=>'#39', '<'=>'lt', '>'=>'gt');
|
---|
1605 | sub EscapeXML($)
|
---|
1606 | {
|
---|
1607 | my $str = shift;
|
---|
1608 | $str =~ s/([&><'"])/&$charName{$1};/sg; # escape necessary XML characters
|
---|
1609 | return $str;
|
---|
1610 | }
|
---|
1611 |
|
---|
1612 | #------------------------------------------------------------------------------
|
---|
1613 | # Unescape XML character references (entities and numerical)
|
---|
1614 | # Inputs: 0) string to be unescaped
|
---|
1615 | # 1) optional hash reference to convert entity names to numbers
|
---|
1616 | # Returns: unescaped string
|
---|
1617 | my %charNum = ('quot'=>34, 'amp'=>38, 'apos'=>39, 'lt'=>60, 'gt'=>62);
|
---|
1618 | sub UnescapeXML($;$)
|
---|
1619 | {
|
---|
1620 | my ($str, $conv) = @_;
|
---|
1621 | $conv = \%charNum unless $conv;
|
---|
1622 | $str =~ s/&(#?\w+);/UnescapeChar($1,$conv)/sge;
|
---|
1623 | return $str;
|
---|
1624 | }
|
---|
1625 |
|
---|
1626 | #------------------------------------------------------------------------------
|
---|
1627 | # Convert XML character reference to UTF-8
|
---|
1628 | # Inputs: 0) XML character reference stripped of the '&' and ';' (ie. 'quot', '#34', '#x22')
|
---|
1629 | # 1) hash reference for looking up character numbers by name
|
---|
1630 | # Returns: UTF-8 equivalent (or original character on conversion error)
|
---|
1631 | sub UnescapeChar($$)
|
---|
1632 | {
|
---|
1633 | my ($ch, $conv) = @_;
|
---|
1634 | my $val = $$conv{$ch};
|
---|
1635 | unless (defined $val) {
|
---|
1636 | if ($ch =~ /^#x([0-9a-fA-F]+)$/) {
|
---|
1637 | $val = hex($1);
|
---|
1638 | } elsif ($ch =~ /^#(\d+)$/) {
|
---|
1639 | $val = $1;
|
---|
1640 | } else {
|
---|
1641 | return "&$ch;"; # should issue a warning here?
|
---|
1642 | }
|
---|
1643 | }
|
---|
1644 | if ($val < 0x80) {
|
---|
1645 | return chr($val); # simple ASCII
|
---|
1646 | } elsif ($] >= 5.006001) {
|
---|
1647 | return pack('C0U', $val);
|
---|
1648 | } else {
|
---|
1649 | return Image::ExifTool::PackUTF8($val);
|
---|
1650 | }
|
---|
1651 | }
|
---|
1652 |
|
---|
1653 | #------------------------------------------------------------------------------
|
---|
1654 | # Utility routine to decode a base64 string
|
---|
1655 | # Inputs: 0) base64 string
|
---|
1656 | # Returns: reference to decoded data
|
---|
1657 | sub DecodeBase64($)
|
---|
1658 | {
|
---|
1659 | local($^W) = 0; # unpack('u',...) gives bogus warning in 5.00[123]
|
---|
1660 | my $str = shift;
|
---|
1661 |
|
---|
1662 | # truncate at first unrecognized character (base 64 data
|
---|
1663 | # may only contain A-Z, a-z, 0-9, +, /, =, or white space)
|
---|
1664 | $str =~ s/[^A-Za-z0-9+\/= \t\n\r\f].*//;
|
---|
1665 | # translate to uucoded and remove padding and white space
|
---|
1666 | $str =~ tr/A-Za-z0-9+\/= \t\n\r\f/ -_/d;
|
---|
1667 |
|
---|
1668 | # convert the data to binary in chunks
|
---|
1669 | my $chunkSize = 60;
|
---|
1670 | my $uuLen = pack('c', 32 + $chunkSize * 3 / 4); # calculate length byte
|
---|
1671 | my $dat = '';
|
---|
1672 | my ($i, $substr);
|
---|
1673 | # loop through the whole chunks
|
---|
1674 | my $len = length($str) - $chunkSize;
|
---|
1675 | for ($i=0; $i<=$len; $i+=$chunkSize) {
|
---|
1676 | $substr = substr($str, $i, $chunkSize); # get a chunk of the data
|
---|
1677 | $dat .= unpack('u', $uuLen . $substr); # decode it
|
---|
1678 | }
|
---|
1679 | $len += $chunkSize;
|
---|
1680 | # handle last partial chunk if necessary
|
---|
1681 | if ($i < $len) {
|
---|
1682 | $uuLen = pack('c', 32 + ($len-$i) * 3 / 4); # recalculate length
|
---|
1683 | $substr = substr($str, $i, $len-$i); # get the last partial chunk
|
---|
1684 | $dat .= unpack('u', $uuLen . $substr); # decode it
|
---|
1685 | }
|
---|
1686 | return \$dat;
|
---|
1687 | }
|
---|
1688 |
|
---|
1689 | #------------------------------------------------------------------------------
|
---|
1690 | # Generate a name for this XMP tag
|
---|
1691 | # Inputs: 0) reference to tag property name list
|
---|
1692 | # Returns: tagID and outtermost interesting namespace
|
---|
1693 | sub GetXMPTagID($)
|
---|
1694 | {
|
---|
1695 | my $props = shift;
|
---|
1696 | my ($tag, $prop, $namespace);
|
---|
1697 | foreach $prop (@$props) {
|
---|
1698 | # split name into namespace and property name
|
---|
1699 | # (Note: namespace can be '' for property qualifiers)
|
---|
1700 | my ($ns, $nm) = ($prop =~ /(.*?):(.*)/) ? ($1, $2) : ('', $prop);
|
---|
1701 | $nm =~ s/ .*//; # remove nodeID if it exists
|
---|
1702 | if ($ignoreNamespace{$ns}) {
|
---|
1703 | # special case: don't ignore rdf numbered items
|
---|
1704 | next unless $prop =~ /^rdf:(_\d+)$/;
|
---|
1705 | $tag .= $1;
|
---|
1706 | } else {
|
---|
1707 | # all uppercase is ugly, so convert it
|
---|
1708 | if ($nm !~ /[a-z]/) {
|
---|
1709 | my $xlatNS = $xlatNamespace{$ns} || $ns;
|
---|
1710 | my $info = $Image::ExifTool::XMP::Main{$xlatNS};
|
---|
1711 | my $table;
|
---|
1712 | if (ref $info eq 'HASH' and $info->{SubDirectory}) {
|
---|
1713 | $table = GetTagTable($info->{SubDirectory}->{TagTable});
|
---|
1714 | }
|
---|
1715 | unless ($table and $table->{$nm}) {
|
---|
1716 | $nm = lc($nm);
|
---|
1717 | $nm =~ s/_([a-z])/\u$1/g;
|
---|
1718 | }
|
---|
1719 | }
|
---|
1720 | if (defined $tag) {
|
---|
1721 | $tag .= ucfirst($nm); # add to tag name
|
---|
1722 | } else {
|
---|
1723 | $tag = $nm;
|
---|
1724 | }
|
---|
1725 | }
|
---|
1726 | # save namespace of first property to contribute to tag name
|
---|
1727 | $namespace = $ns unless defined $namespace;
|
---|
1728 | }
|
---|
1729 | if (wantarray) {
|
---|
1730 | return ($tag, $namespace);
|
---|
1731 | } else {
|
---|
1732 | return $tag;
|
---|
1733 | }
|
---|
1734 | }
|
---|
1735 |
|
---|
1736 | #------------------------------------------------------------------------------
|
---|
1737 | # Get localized version of tagInfo hash
|
---|
1738 | # Inputs: 0) tagInfo hash ref, 1) language code (ie. "x-default")
|
---|
1739 | # Returns: new tagInfo hash ref, or undef if invalid
|
---|
1740 | sub GetLangInfo($$)
|
---|
1741 | {
|
---|
1742 | my ($tagInfo, $langCode) = @_;
|
---|
1743 | # only allow alternate language tags in lang-alt lists
|
---|
1744 | return undef unless $$tagInfo{Writable} and $$tagInfo{Writable} eq 'lang-alt';
|
---|
1745 | $langCode =~ tr/_/-/; # RFC 3066 specifies '-' as a separator
|
---|
1746 | return Image::ExifTool::GetLangInfo($tagInfo, $langCode);
|
---|
1747 | }
|
---|
1748 |
|
---|
1749 | #------------------------------------------------------------------------------
|
---|
1750 | # Get standard case for language code
|
---|
1751 | # Inputs: 0) Language code
|
---|
1752 | # Returns: Language code in standard case
|
---|
1753 | sub StandardLangCase($)
|
---|
1754 | {
|
---|
1755 | my $lang = shift;
|
---|
1756 | # make 2nd subtag uppercase only if it is 2 letters
|
---|
1757 | return lc($1) . uc($2) . lc($3) if $lang =~ /^([a-z]{2,3}|[xi])(-[a-z]{2})\b(.*)/i;
|
---|
1758 | return lc($lang);
|
---|
1759 | }
|
---|
1760 |
|
---|
1761 | #------------------------------------------------------------------------------
|
---|
1762 | # Scan for XMP in a file
|
---|
1763 | # Inputs: 0) ExifTool object ref, 1) RAF reference
|
---|
1764 | # Returns: 1 if xmp was found, 0 otherwise
|
---|
1765 | # Notes: Currently only recognizes UTF8-encoded XMP
|
---|
1766 | sub ScanForXMP($$)
|
---|
1767 | {
|
---|
1768 | my ($exifTool, $raf) = @_;
|
---|
1769 | my ($buff, $xmp);
|
---|
1770 | my $lastBuff = '';
|
---|
1771 |
|
---|
1772 | $exifTool->VPrint(0,"Scanning for XMP\n");
|
---|
1773 | for (;;) {
|
---|
1774 | defined $buff or $raf->Read($buff, 65536) or return 0;
|
---|
1775 | unless (defined $xmp) {
|
---|
1776 | $lastBuff .= $buff;
|
---|
1777 | unless ($lastBuff =~ /(<\?xpacket begin=)/g) {
|
---|
1778 | # must keep last 15 bytes to match 16-byte "xpacket begin" string
|
---|
1779 | $lastBuff = length($buff) <= 15 ? $buff : substr($buff, -15);
|
---|
1780 | undef $buff;
|
---|
1781 | next;
|
---|
1782 | }
|
---|
1783 | $xmp = $1;
|
---|
1784 | $buff = substr($lastBuff, pos($lastBuff));
|
---|
1785 | }
|
---|
1786 | $xmp .= $buff;
|
---|
1787 | my $pos = length($xmp) - length($buff) - 18; # 18 is length("<xpacket end...")-1
|
---|
1788 | pos($xmp) = $pos if $pos > 0;
|
---|
1789 | if ($xmp =~ /<\?xpacket end=['"][wr]['"]\?>/g) {
|
---|
1790 | $xmp = substr($xmp, 0, pos($xmp));
|
---|
1791 | # XMP is not valid if it contains null bytes
|
---|
1792 | pos($xmp) = 0;
|
---|
1793 | last unless $xmp =~ /\0/g;
|
---|
1794 | my $null = pos $xmp;
|
---|
1795 | while ($xmp =~ /\0/g) {
|
---|
1796 | $null = pos($xmp);
|
---|
1797 | }
|
---|
1798 | # re-parse beginning after last null byte
|
---|
1799 | $buff = substr($xmp, $null);
|
---|
1800 | $lastBuff = '';
|
---|
1801 | undef $xmp;
|
---|
1802 | } else {
|
---|
1803 | undef $buff;
|
---|
1804 | }
|
---|
1805 | }
|
---|
1806 | unless ($exifTool->{VALUE}->{FileType}) {
|
---|
1807 | $exifTool->{FILE_TYPE} = $exifTool->{FILE_EXT};
|
---|
1808 | $exifTool->SetFileType('<unknown file containing XMP>');
|
---|
1809 | }
|
---|
1810 | my %dirInfo = (
|
---|
1811 | DataPt => \$xmp,
|
---|
1812 | DirLen => length $xmp,
|
---|
1813 | DataLen => length $xmp,
|
---|
1814 | );
|
---|
1815 | ProcessXMP($exifTool, \%dirInfo);
|
---|
1816 | return 1;
|
---|
1817 | }
|
---|
1818 |
|
---|
1819 | #------------------------------------------------------------------------------
|
---|
1820 | # We found an XMP property name/value
|
---|
1821 | # Inputs: 0) ExifTool object ref, 1) Pointer to tag table
|
---|
1822 | # 2) reference to array of XMP property names (last is current property)
|
---|
1823 | # 3) property value, 4) xml:lang attribute (or undef)
|
---|
1824 | # Returns: 1 if valid tag was found
|
---|
1825 | sub FoundXMP($$$$;$)
|
---|
1826 | {
|
---|
1827 | local $_;
|
---|
1828 | my ($exifTool, $tagTablePtr, $props, $val, $lang) = @_;
|
---|
1829 |
|
---|
1830 | my ($tag, $namespace) = GetXMPTagID($props);
|
---|
1831 | return 0 unless $tag; # ignore things that aren't valid tags
|
---|
1832 |
|
---|
1833 | # translate namespace if necessary
|
---|
1834 | $namespace = $xlatNamespace{$namespace} if $xlatNamespace{$namespace};
|
---|
1835 | my $info = $tagTablePtr->{$namespace};
|
---|
1836 | my $table;
|
---|
1837 | if ($info) {
|
---|
1838 | $table = $info->{SubDirectory}->{TagTable} or warn "Missing TagTable for $tag!\n";
|
---|
1839 | } else {
|
---|
1840 | $table = 'Image::ExifTool::XMP::other';
|
---|
1841 | }
|
---|
1842 | # change pointer to the table for this namespace
|
---|
1843 | $tagTablePtr = GetTagTable($table);
|
---|
1844 |
|
---|
1845 | # look up this tag in the appropriate table
|
---|
1846 | my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
|
---|
1847 |
|
---|
1848 | unless ($tagInfo) {
|
---|
1849 | if ($exifTool->{OPTIONS}->{Verbose}) {
|
---|
1850 | my $group1 = $namespace ? "XMP-$namespace" : $tagTablePtr->{GROUPS}->{1};
|
---|
1851 | $exifTool->VPrint(0, $exifTool->{INDENT}, "[adding $group1:$tag]\n");
|
---|
1852 | }
|
---|
1853 | # construct tag information for this unknown tag
|
---|
1854 | $tagInfo = { Name => ucfirst($tag), WasAdded => 1 };
|
---|
1855 | # make this a List type if necessary and not lang-alt
|
---|
1856 | $$tagInfo{List} = $1 if @$props > 2 and not $lang and
|
---|
1857 | $$props[-1] =~ /^rdf:li \d+$/ and $$props[-2] =~ /^rdf:(Bag|Seq|Alt)/;
|
---|
1858 | Image::ExifTool::AddTagToTable($tagTablePtr, $tag, $tagInfo);
|
---|
1859 | }
|
---|
1860 | if (defined $lang and lc($lang) ne 'x-default') {
|
---|
1861 | $lang = StandardLangCase($lang);
|
---|
1862 | my $langInfo = GetLangInfo($tagInfo, $lang);
|
---|
1863 | $tagInfo = $langInfo if $langInfo;
|
---|
1864 | }
|
---|
1865 | if ($exifTool->{OPTIONS}->{Charset} ne 'UTF8' and $val =~ /[\x80-\xff]/) {
|
---|
1866 | # convert from UTF-8 to specified characterset
|
---|
1867 | $val = $exifTool->UTF82Charset($val);
|
---|
1868 | }
|
---|
1869 | # convert rational and date values to a more sensible format
|
---|
1870 | my $fmt = $$tagInfo{Writable};
|
---|
1871 | my $new = $$tagInfo{WasAdded};
|
---|
1872 | if ($fmt or $new) {
|
---|
1873 | if (($new or $fmt eq 'rational') and $val =~ m{^(-?\d+)/(-?\d+)$}) {
|
---|
1874 | $val = $1 / $2 if $2; # calculate quotient
|
---|
1875 | } elsif ($new or $fmt eq 'date') {
|
---|
1876 | if ($val =~ /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}:\d{2})(:\d{2})?(\S*)$/) {
|
---|
1877 | my $s = $5 || ':00'; # seconds may be missing
|
---|
1878 | $val = "$1:$2:$3 $4$s$6"; # convert back to EXIF time format
|
---|
1879 | } elsif ($fmt and $fmt eq 'date' and $val =~ /^(\d{4})(-\d{2}){0,2}/) {
|
---|
1880 | $val =~ tr/-/:/;
|
---|
1881 | }
|
---|
1882 | }
|
---|
1883 | }
|
---|
1884 | $tag = $exifTool->FoundTag($tagInfo, UnescapeXML($val));
|
---|
1885 | $exifTool->SetGroup1($tag, "XMP-$namespace") if $namespace;
|
---|
1886 |
|
---|
1887 | if ($exifTool->Options('Verbose')) {
|
---|
1888 | my $tagID = join('/',@$props);
|
---|
1889 | $exifTool->VerboseInfo($tagID, $tagInfo, Value=>$val);
|
---|
1890 | }
|
---|
1891 | return 1;
|
---|
1892 | }
|
---|
1893 |
|
---|
1894 | #------------------------------------------------------------------------------
|
---|
1895 | # Recursively parse nested XMP data element
|
---|
1896 | # Inputs: 0) ExifTool object reference
|
---|
1897 | # 1) Pointer to tag table
|
---|
1898 | # 2) reference to XMP data
|
---|
1899 | # 3) start of xmp element
|
---|
1900 | # 4) reference to array of enclosing XMP property names (undef if none)
|
---|
1901 | # 5) reference to blank node information hash
|
---|
1902 | # Returns: Number of contained XMP elements
|
---|
1903 | sub ParseXMPElement($$$;$$$)
|
---|
1904 | {
|
---|
1905 | my ($exifTool, $tagTablePtr, $dataPt, $start, $propListPt, $blankInfo) = @_;
|
---|
1906 | my $count = 0;
|
---|
1907 | my $isWriting = $exifTool->{XMP_CAPTURE};
|
---|
1908 | $start or $start = 0;
|
---|
1909 | $propListPt or $propListPt = [ ];
|
---|
1910 |
|
---|
1911 | my $processBlankInfo;
|
---|
1912 | # create empty blank node information hash if necessary
|
---|
1913 | $blankInfo or $blankInfo = $processBlankInfo = { Prop => { } };
|
---|
1914 | # keep track of current nodeID at this nesting level
|
---|
1915 | my $oldNodeID = $$blankInfo{NodeID};
|
---|
1916 |
|
---|
1917 | pos($$dataPt) = $start;
|
---|
1918 | Element: for (;;) {
|
---|
1919 | # reset nodeID before processing each element
|
---|
1920 | my $nodeID = $$blankInfo{NodeID} = $oldNodeID;
|
---|
1921 | # get next element
|
---|
1922 | last unless $$dataPt =~ m/<([\w:-]+)(.*?)>/sg;
|
---|
1923 | my ($prop, $attrs) = ($1, $2);
|
---|
1924 | my $val = '';
|
---|
1925 | # only look for closing token if this is not an empty element
|
---|
1926 | # (empty elements end with '/', ie. <a:b/>)
|
---|
1927 | if ($attrs !~ s/\/$//) {
|
---|
1928 | my $nesting = 1;
|
---|
1929 | for (;;) {
|
---|
1930 | # this match fails with perl 5.6.2 (perl bug!), but it works without
|
---|
1931 | # the '(.*?)', so do it the hard way instead...
|
---|
1932 | # $$dataPt =~ m/(.*?)<\/$prop>/sg or last Element;
|
---|
1933 | # my $val2 = $1;
|
---|
1934 | my $pos = pos($$dataPt);
|
---|
1935 | $$dataPt =~ m/<\/$prop>/sg or last Element;
|
---|
1936 | my $len = pos($$dataPt) - $pos - length($prop) - 3;
|
---|
1937 | my $val2 = substr($$dataPt, $pos, $len);
|
---|
1938 | # increment nesting level for each contained similar opening token
|
---|
1939 | ++$nesting while $val2 =~ m/<$prop\b.*?(\/?)>/sg and $1 ne '/';
|
---|
1940 | $val .= $val2;
|
---|
1941 | --$nesting or last;
|
---|
1942 | $val .= "</$prop>";
|
---|
1943 | }
|
---|
1944 | }
|
---|
1945 | if ($prop eq 'rdf:li') {
|
---|
1946 | # add index to list items so we can keep them in order
|
---|
1947 | # (this also enables us to keep structure elements grouped properly
|
---|
1948 | # for lists of structures, like JobRef)
|
---|
1949 | $prop .= sprintf(' %.3d', $count);
|
---|
1950 | } elsif ($prop eq 'rdf:Description') {
|
---|
1951 | # trim comments and whitespace from rdf:Description properties only
|
---|
1952 | $val =~ s/<!--.*?-->//g;
|
---|
1953 | $val =~ s/^\s*(.*)\s*$/$1/;
|
---|
1954 | } elsif ($prop eq 'xmp:xmpmeta') {
|
---|
1955 | # patch MicrosoftPhoto unconformity
|
---|
1956 | $prop = 'x:xmpmeta';
|
---|
1957 | }
|
---|
1958 |
|
---|
1959 | # extract property attributes
|
---|
1960 | my (%attrs, @attrs);
|
---|
1961 | while ($attrs =~ m/(\S+?)=(['"])(.*?)\2/sg) {
|
---|
1962 | push @attrs, $1; # preserve order
|
---|
1963 | $attrs{$1} = $3;
|
---|
1964 | }
|
---|
1965 |
|
---|
1966 | # add nodeID to property path (with leading ' #') if it exists
|
---|
1967 | if (defined $attrs{'rdf:nodeID'}) {
|
---|
1968 | $nodeID = $$blankInfo{NodeID} = $attrs{'rdf:nodeID'};
|
---|
1969 | delete $attrs{'rdf:nodeID'};
|
---|
1970 | $prop .= ' #' . $nodeID;
|
---|
1971 | }
|
---|
1972 |
|
---|
1973 | # push this property name onto our hierarchy list
|
---|
1974 | push @$propListPt, $prop;
|
---|
1975 |
|
---|
1976 | # handle properties inside element attributes (RDF shorthand format):
|
---|
1977 | # (attributes take the form a:b='c' or a:b="c")
|
---|
1978 | my ($shortName, $shorthand, $ignored);
|
---|
1979 | foreach $shortName (@attrs) {
|
---|
1980 | my $propName = $shortName;
|
---|
1981 | my ($ns, $name);
|
---|
1982 | if ($propName =~ /(.*?):(.*)/) {
|
---|
1983 | $ns = $1; # specified namespace
|
---|
1984 | $name = $2;
|
---|
1985 | } elsif ($prop =~ /(\S*?):/) {
|
---|
1986 | $ns = $1; # assume same namespace as parent
|
---|
1987 | $name = $propName;
|
---|
1988 | $propName = "$ns:$name"; # generate full property name
|
---|
1989 | } else {
|
---|
1990 | # a property qualifier is the only property name that may not
|
---|
1991 | # have a namespace, and a qualifier shouldn't have attributes,
|
---|
1992 | # but what the heck, let's allow this anyway
|
---|
1993 | $ns = '';
|
---|
1994 | $name = $propName;
|
---|
1995 | }
|
---|
1996 | if ($isWriting) {
|
---|
1997 | # keep track of our namespaces when writing
|
---|
1998 | if ($ns eq 'xmlns') {
|
---|
1999 | unless ($name eq 'x' or $name eq 'iX') {
|
---|
2000 | my $nsUsed = $exifTool->{XMP_NS};
|
---|
2001 | $$nsUsed{$name} = $attrs{$shortName} unless defined $$nsUsed{$name};
|
---|
2002 | }
|
---|
2003 | next;
|
---|
2004 | } elsif ($recognizedAttrs{$propName}) {
|
---|
2005 | # save UUID to use same ID when writing
|
---|
2006 | if ($propName eq 'rdf:about') {
|
---|
2007 | if (not $exifTool->{XMP_UUID}) {
|
---|
2008 | $exifTool->{XMP_UUID} = $attrs{$shortName};
|
---|
2009 | } elsif ($exifTool->{XMP_UUID} ne $attrs{$shortName}) {
|
---|
2010 | $exifTool->Error("Multiple XMP UUID's not handled", 1);
|
---|
2011 | }
|
---|
2012 | }
|
---|
2013 | next;
|
---|
2014 | }
|
---|
2015 | }
|
---|
2016 | if ($ignoreNamespace{$ns}) {
|
---|
2017 | $ignored = $propName;
|
---|
2018 | next;
|
---|
2019 | }
|
---|
2020 | my $shortVal = $attrs{$shortName};
|
---|
2021 | delete $attrs{$shortName}; # don't re-use this attribute
|
---|
2022 | push @$propListPt, $propName;
|
---|
2023 | # save this shorthand XMP property
|
---|
2024 | if (defined $nodeID) {
|
---|
2025 | SaveBlankInfo($blankInfo, $propListPt, $shortVal);
|
---|
2026 | } elsif ($isWriting) {
|
---|
2027 | CaptureXMP($exifTool, $propListPt, $shortVal);
|
---|
2028 | } else {
|
---|
2029 | FoundXMP($exifTool, $tagTablePtr, $propListPt, $shortVal);
|
---|
2030 | }
|
---|
2031 | pop @$propListPt;
|
---|
2032 | $shorthand = 1;
|
---|
2033 | }
|
---|
2034 | if ($isWriting) {
|
---|
2035 | if (ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) {
|
---|
2036 | # undefine value since we found more properties within this one
|
---|
2037 | undef $val;
|
---|
2038 | # set an error on any ignored attributes here, because they will be lost
|
---|
2039 | $exifTool->{XMP_ERROR} = "Can't handle XMP attribute '$ignored'" if $ignored;
|
---|
2040 | }
|
---|
2041 | if (defined $val and (length $val or not $shorthand)) {
|
---|
2042 | if (defined $nodeID) {
|
---|
2043 | SaveBlankInfo($blankInfo, $propListPt, $val, \%attrs);
|
---|
2044 | } else {
|
---|
2045 | CaptureXMP($exifTool, $propListPt, $val, \%attrs);
|
---|
2046 | }
|
---|
2047 | }
|
---|
2048 | } else {
|
---|
2049 | # if element value is empty, take value from 'resource' attribute
|
---|
2050 | # (preferentially) or 'about' attribute (if no 'resource')
|
---|
2051 | $val = $2 if $val eq '' and ($attrs =~ /\bresource=(['"])(.*?)\1/ or
|
---|
2052 | $attrs =~ /\babout=(['"])(.*?)\1/);
|
---|
2053 |
|
---|
2054 | # look for additional elements contained within this one
|
---|
2055 | if (!ParseXMPElement($exifTool, $tagTablePtr, \$val, 0, $propListPt, $blankInfo)) {
|
---|
2056 | # there are no contained elements, so this must be a simple property value
|
---|
2057 | # (unless we already extracted shorthand values from this element)
|
---|
2058 | if (length $val or not $shorthand) {
|
---|
2059 | if (defined $nodeID) {
|
---|
2060 | SaveBlankInfo($blankInfo, $propListPt, $val);
|
---|
2061 | } else {
|
---|
2062 | FoundXMP($exifTool, $tagTablePtr, $propListPt, $val, $attrs{'xml:lang'});
|
---|
2063 | }
|
---|
2064 | }
|
---|
2065 | }
|
---|
2066 | }
|
---|
2067 | pop @$propListPt;
|
---|
2068 | ++$count;
|
---|
2069 | }
|
---|
2070 | #
|
---|
2071 | # process resources referenced by blank nodeID's
|
---|
2072 | #
|
---|
2073 | if ($processBlankInfo and %{$$blankInfo{Prop}}) {
|
---|
2074 | ProcessBlankInfo($exifTool, $tagTablePtr, $blankInfo, $isWriting);
|
---|
2075 | %$blankInfo = (); # free some memory
|
---|
2076 | }
|
---|
2077 | return $count; # return the number of elements found at this level
|
---|
2078 | }
|
---|
2079 |
|
---|
2080 | #------------------------------------------------------------------------------
|
---|
2081 | # Process XMP data
|
---|
2082 | # Inputs: 0) ExifTool object reference, 1) DirInfo reference, 2) Pointer to tag table
|
---|
2083 | # Returns: 1 on success
|
---|
2084 | sub ProcessXMP($$;$)
|
---|
2085 | {
|
---|
2086 | my ($exifTool, $dirInfo, $tagTablePtr) = @_;
|
---|
2087 | my $dataPt = $$dirInfo{DataPt};
|
---|
2088 | my $dirStart = $$dirInfo{DirStart} || 0;
|
---|
2089 | my $dirLen = $$dirInfo{DirLen};
|
---|
2090 | my $dataLen = $$dirInfo{DataLen};
|
---|
2091 | my $rtnVal = 0;
|
---|
2092 | my $bom = 0;
|
---|
2093 | my ($buff, $fmt, $isXML);
|
---|
2094 |
|
---|
2095 | # read information from XMP file if necessary
|
---|
2096 | unless ($dataPt) {
|
---|
2097 | my $raf = $$dirInfo{RAF} or return 0;
|
---|
2098 | $raf->Read($buff, 128) or return 0;
|
---|
2099 | my ($buf2, $double);
|
---|
2100 | ($buf2 = $buff) =~ tr/\0//d; # cheap conversion to UTF-8
|
---|
2101 | # check to see if this is XMP format
|
---|
2102 | # - CS2 writes .XMP files without the "xpacket begin"
|
---|
2103 | unless ($buf2 =~ /^(<\?xpacket begin=|<x(mp)?:xmpmeta)/) {
|
---|
2104 | # also recognize XML files (and .XMP files with BOM)
|
---|
2105 | if ($buf2 =~ /^(\xfe\xff)(<\?xml|<x(mp)?:xmpmeta)/g) {
|
---|
2106 | $fmt = 'n'; # UTF-16 or 32 MM with BOM
|
---|
2107 | } elsif ($buf2 =~ /^(\xff\xfe)(<\?xml|<x(mp)?:xmpmeta)/g) {
|
---|
2108 | $fmt = 'v'; # UTF-16 or 32 II with BOM
|
---|
2109 | } elsif ($buf2 =~ /^(\xef\xbb\xbf)?(<\?xml|<x(mp)?:xmpmeta)/g) {
|
---|
2110 | $fmt = 0; # UTF-8 with BOM or unknown encoding without BOM
|
---|
2111 | } elsif ($buf2 =~ /^(\xfe\xff|\xff\xfe|\xef\xbb\xbf)(<\?xpacket begin=)/g) {
|
---|
2112 | $double = $1; # double-encoded UTF
|
---|
2113 | } else {
|
---|
2114 | return 0; # not XMP or XML
|
---|
2115 | }
|
---|
2116 | $bom = 1 if $1;
|
---|
2117 | if ($2 eq '<?xml') {
|
---|
2118 | return 0 unless $buf2 =~ /<x(mp)?:xmpmeta/;
|
---|
2119 | $isXML = 1;
|
---|
2120 | }
|
---|
2121 | if ($buff =~ /^\0\0/) {
|
---|
2122 | $fmt = 'N'; # UTF-32 MM with or without BOM
|
---|
2123 | } elsif ($buff =~ /^..\0\0/) {
|
---|
2124 | $fmt = 'V'; # UTF-32 II with or without BOM
|
---|
2125 | } elsif (not $fmt) {
|
---|
2126 | if ($buff =~ /^\0/) {
|
---|
2127 | $fmt = 'n'; # UTF-16 MM without BOM
|
---|
2128 | } elsif ($buff =~ /^.\0/) {
|
---|
2129 | $fmt = 'v'; # UTF-16 II without BOM
|
---|
2130 | }
|
---|
2131 | }
|
---|
2132 | }
|
---|
2133 | $raf->Seek(0, 2) or return 0;
|
---|
2134 | my $size = $raf->Tell() or return 0;
|
---|
2135 | $raf->Seek(0, 0) or return 0;
|
---|
2136 | $raf->Read($buff, $size) == $size or return 0;
|
---|
2137 | # decode the first layer of double-encoded UTF text
|
---|
2138 | if ($double) {
|
---|
2139 | $buff = substr($buff, length $double); # remove leading BOM
|
---|
2140 | Image::ExifTool::SetWarning(undef); # clear old warning
|
---|
2141 | local $SIG{'__WARN__'} = \&Image::ExifTool::SetWarning;
|
---|
2142 | my $tmp;
|
---|
2143 | # assume that character data has been re-encoded in UTF, so re-pack
|
---|
2144 | # as characters and look for warnings indicating a false assumption
|
---|
2145 | if ($double eq "\xef\xbb\xbf") {
|
---|
2146 | $tmp = Image::ExifTool::UTF82Unicode($buff, 'C');
|
---|
2147 | } else {
|
---|
2148 | my $fmt = ($double eq "\xfe\xff") ? 'n' : 'v';
|
---|
2149 | $tmp = pack('C*', unpack("$fmt*",$buff));
|
---|
2150 | }
|
---|
2151 | if (Image::ExifTool::GetWarning()) {
|
---|
2152 | $exifTool->Warn('Superfluous BOM at start of XMP');
|
---|
2153 | } else {
|
---|
2154 | $exifTool->Warn('XMP is double UTF-encoded');
|
---|
2155 | $buff = $tmp; # use the decoded XMP
|
---|
2156 | }
|
---|
2157 | $size = length $buff;
|
---|
2158 | }
|
---|
2159 | $dataPt = \$buff;
|
---|
2160 | $dirStart = 0;
|
---|
2161 | $dirLen = $dataLen = $size;
|
---|
2162 | $exifTool->SetFileType();
|
---|
2163 | }
|
---|
2164 |
|
---|
2165 | # take substring if necessary
|
---|
2166 | if ($dataLen != $dirStart + $dirLen) {
|
---|
2167 | $buff = substr($$dataPt, $dirStart, $dirLen);
|
---|
2168 | $dataPt = \$buff;
|
---|
2169 | $dirStart = 0;
|
---|
2170 | }
|
---|
2171 | if ($exifTool->{REQ_TAG_LOOKUP}->{xmp}) {
|
---|
2172 | $exifTool->FoundTag('XMP', substr($$dataPt, $dirStart, $dirLen));
|
---|
2173 | }
|
---|
2174 | if ($exifTool->Options('Verbose') and not $exifTool->{XMP_CAPTURE}) {
|
---|
2175 | $exifTool->VerboseDir('XMP', 0, $dirLen);
|
---|
2176 | }
|
---|
2177 | #
|
---|
2178 | # convert UTF-16 or UTF-32 encoded XMP to UTF-8 if necessary
|
---|
2179 | #
|
---|
2180 | my $begin = '<?xpacket begin=';
|
---|
2181 | pos($$dataPt) = $dirStart;
|
---|
2182 | delete $$exifTool{XMP_IS_XML};
|
---|
2183 | if ($isXML) {
|
---|
2184 | $$exifTool{XMP_IS_XML} = 1;
|
---|
2185 | $$exifTool{XMP_NO_XPACKET} = 1 + $bom;
|
---|
2186 | } elsif ($$dataPt =~ /\G\Q$begin\E/gc) {
|
---|
2187 | delete $$exifTool{XMP_NO_XPACKET};
|
---|
2188 | } elsif ($$dataPt =~ /<x(mp)?:xmpmeta/gc) {
|
---|
2189 | $$exifTool{XMP_NO_XPACKET} = 1 + $bom;
|
---|
2190 | } else {
|
---|
2191 | delete $$exifTool{XMP_NO_XPACKET};
|
---|
2192 | # check for UTF-16 encoding (insert one \0 between characters)
|
---|
2193 | $begin = join "\0", split //, $begin;
|
---|
2194 | if ($$dataPt =~ /\G(\0)?\Q$begin\E\0./g) {
|
---|
2195 | # validate byte ordering by checking for U+FEFF character
|
---|
2196 | if ($1) {
|
---|
2197 | # should be big-endian since we had a leading \0
|
---|
2198 | $fmt = 'n' if $$dataPt =~ /\G\xfe\xff/g;
|
---|
2199 | } else {
|
---|
2200 | $fmt = 'v' if $$dataPt =~ /\G\0\xff\xfe/g;
|
---|
2201 | }
|
---|
2202 | } else {
|
---|
2203 | # check for UTF-32 encoding (with three \0's between characters)
|
---|
2204 | $begin =~ s/\0/\0\0\0/g;
|
---|
2205 | # must reset pos because it was killed by previous unsuccessful //g match
|
---|
2206 | pos($$dataPt) = $dirStart;
|
---|
2207 | if ($$dataPt !~ /\G(\0\0\0)?\Q$begin\E\0\0\0./g) {
|
---|
2208 | $fmt = 0; # set format to zero as indication we didn't find encoded XMP
|
---|
2209 | } elsif ($1) {
|
---|
2210 | # should be big-endian
|
---|
2211 | $fmt = 'N' if $$dataPt =~ /\G\0\0\xfe\xff/g;
|
---|
2212 | } else {
|
---|
2213 | $fmt = 'V' if $$dataPt =~ /\G\0\0\0\xff\xfe\0\0/g;
|
---|
2214 | }
|
---|
2215 | }
|
---|
2216 | defined $fmt or $exifTool->Warn('XMP character encoding error');
|
---|
2217 | }
|
---|
2218 | if ($fmt) {
|
---|
2219 | # translate into UTF-8
|
---|
2220 | if ($] >= 5.006001) {
|
---|
2221 | $buff = pack('C0U*', unpack("x$dirStart$fmt*",$$dataPt));
|
---|
2222 | } else {
|
---|
2223 | $buff = Image::ExifTool::PackUTF8(unpack("x$dirStart$fmt*",$$dataPt));
|
---|
2224 | }
|
---|
2225 | $dataPt = \$buff;
|
---|
2226 | $dirStart = 0;
|
---|
2227 | }
|
---|
2228 | # avoid scanning for XMP later in case ScanForXMP is set
|
---|
2229 | $$exifTool{FoundXMP} = 1;
|
---|
2230 |
|
---|
2231 | # parse the XMP
|
---|
2232 | $tagTablePtr or $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
|
---|
2233 | $rtnVal = 1 if ParseXMPElement($exifTool, $tagTablePtr, $dataPt, $dirStart);
|
---|
2234 |
|
---|
2235 | # return DataPt if successful in case we want it for writing
|
---|
2236 | $$dirInfo{DataPt} = $dataPt if $rtnVal and $$dirInfo{RAF};
|
---|
2237 |
|
---|
2238 | return $rtnVal;
|
---|
2239 | }
|
---|
2240 |
|
---|
2241 |
|
---|
2242 | 1; #end
|
---|
2243 |
|
---|
2244 | __END__
|
---|
2245 |
|
---|
2246 | =head1 NAME
|
---|
2247 |
|
---|
2248 | Image::ExifTool::XMP - Read XMP meta information
|
---|
2249 |
|
---|
2250 | =head1 SYNOPSIS
|
---|
2251 |
|
---|
2252 | This module is loaded automatically by Image::ExifTool when required.
|
---|
2253 |
|
---|
2254 | =head1 DESCRIPTION
|
---|
2255 |
|
---|
2256 | XMP stands for Extensible Metadata Platform. It is a format based on XML
|
---|
2257 | that Adobe developed for embedding metadata information in image files.
|
---|
2258 | This module contains the definitions required by Image::ExifTool to read XMP
|
---|
2259 | information.
|
---|
2260 |
|
---|
2261 | =head1 AUTHOR
|
---|
2262 |
|
---|
2263 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
2264 |
|
---|
2265 | This library is free software; you can redistribute it and/or modify it
|
---|
2266 | under the same terms as Perl itself.
|
---|
2267 |
|
---|
2268 | =head1 REFERENCES
|
---|
2269 |
|
---|
2270 | =over 4
|
---|
2271 |
|
---|
2272 | =item L<http://www.adobe.com/products/xmp/pdfs/xmpspec.pdf>
|
---|
2273 |
|
---|
2274 | =item L<http://www.w3.org/TR/rdf-syntax-grammar/>
|
---|
2275 |
|
---|
2276 | =item L<http://www.portfoliofaq.com/pfaq/v7mappings.htm>
|
---|
2277 |
|
---|
2278 | =item L<http://www.iptc.org/IPTC4XMP/>
|
---|
2279 |
|
---|
2280 | =item L<http://creativecommons.org/technology/xmp>
|
---|
2281 |
|
---|
2282 | =item L<http://www.optimasc.com/products/fileid/xmp-extensions.pdf>
|
---|
2283 |
|
---|
2284 | =back
|
---|
2285 |
|
---|
2286 | =head1 SEE ALSO
|
---|
2287 |
|
---|
2288 | L<Image::ExifTool::TagNames/XMP Tags>,
|
---|
2289 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
2290 |
|
---|
2291 | =cut
|
---|