1#------------------------------------------------------------------------------
2# File:         Microsoft.pm
3#
4# Description:  Definitions for custom Microsoft tags
5#
6# Revisions:    2010/10/01 - P. Harvey Created
7#               2011/10/05 - PH Added ProcessXtra()
8#               2021/02/23 - PH Added abiltity to write Xtra tags
9#
10# References:   1) http://research.microsoft.com/en-us/um/redmond/groups/ivm/hdview/hdmetadataspec.htm
11#------------------------------------------------------------------------------
12
13package Image::ExifTool::Microsoft;
14
15use strict;
16use vars qw($VERSION);
17use Image::ExifTool qw(:DataAccess :Utils);
18use Image::ExifTool::XMP;
19
20$VERSION = '1.23';
21
22sub ProcessXtra($$$);
23sub WriteXtra($$$);
24sub CheckXtra($$$);
25
26# tags written by Microsoft HDView (ref 1)
27%Image::ExifTool::Microsoft::Stitch = (
28    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
29    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
30    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
31    WRITABLE => 1,
32    FORMAT => 'float',
33    FIRST_ENTRY => 0,
34    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
35    NOTES => q{
36        Information found in the Microsoft custom EXIF tag 0x4748, as written by
37        Windows Live Photo Gallery.
38    },
39    0 => {
40        Name => 'PanoramicStitchVersion',
41        Format => 'int32u',
42    },
43    1 => {
44        Name => 'PanoramicStitchCameraMotion',
45        Format => 'int32u',
46        PrintConv => {
47            2 => 'Rigid Scale',
48            3 => 'Affine',
49            4 => '3D Rotation',
50            5 => 'Homography',
51        },
52    },
53    2 => {
54        Name => 'PanoramicStitchMapType',
55        Format => 'int32u',
56        PrintConv => {
57            0 => 'Perspective',
58            1 => 'Horizontal Cylindrical',
59            2 => 'Horizontal Spherical',
60            257 => 'Vertical Cylindrical',
61            258 => 'Vertical Spherical',
62        },
63    },
64    3 => 'PanoramicStitchTheta0',
65    4 => 'PanoramicStitchTheta1',
66    5 => 'PanoramicStitchPhi0',
67    6 => 'PanoramicStitchPhi1',
68);
69
70# Microsoft Photo schema properties (MicrosoftPhoto) (ref PH)
71%Image::ExifTool::Microsoft::XMP = (
72    %Image::ExifTool::XMP::xmpTableDefaults,
73    GROUPS => { 0 => 'XMP', 1 => 'XMP-microsoft', 2 => 'Image' },
74    NAMESPACE => 'MicrosoftPhoto',
75    TABLE_DESC => 'XMP Microsoft',
76    VARS => { NO_ID => 1 },
77    NOTES => q{
78        Microsoft Photo 1.0 schema XMP tags.  This is likely not a complete list,
79        but represents tags which have been observed in sample images.  The actual
80        namespace prefix is "MicrosoftPhoto", but ExifTool shortens this in the
81        family 1 group name.
82    },
83    CameraSerialNumber => { },
84    DateAcquired       => { Groups => { 2 => 'Time' }, %Image::ExifTool::XMP::dateTimeInfo },
85    FlashManufacturer  => { },
86    FlashModel         => { },
87    LastKeywordIPTC    => { List => 'Bag' },
88    LastKeywordXMP     => { List => 'Bag' },
89    LensManufacturer   => { },
90    LensModel          => { Avoid => 1 },
91    Rating => {
92        Name => 'RatingPercent',
93        Notes => q{
94            XMP-xmp:Rating values of 1,2,3,4 and 5 stars correspond to RatingPercent
95            values of 1,25,50,75 and 99 respectively
96        },
97    },
98    CreatorAppId             => { Name => 'CreatorAppID' },
99    CreatorOpenWithUIOptions => { },
100    ItemSubType              => { },
101);
102
103# Microsoft Photo 1.1 schema properties (MP1 - written as 'prefix0' by MSPhoto) (ref PH)
104%Image::ExifTool::Microsoft::MP1 = (
105    %Image::ExifTool::XMP::xmpTableDefaults,
106    GROUPS => { 0 => 'XMP', 1 => 'XMP-MP1', 2 => 'Image' },
107    NAMESPACE => 'MP1',
108    TABLE_DESC => 'XMP Microsoft Photo',
109    VARS => { NO_ID => 1 },
110    NOTES => q{
111        Microsoft Photo 1.1 schema XMP tags which have been observed.
112    },
113    PanoramicStitchCameraMotion => {
114        PrintConv => {
115            'RigidScale' => 'Rigid Scale',
116            'Affine'     => 'Affine',
117            '3DRotation' => '3D Rotation',
118            'Homography' => 'Homography',
119        },
120    },
121    PanoramicStitchMapType => {
122        PrintConv => {
123            'Perspective'            => 'Perspective',
124            'Horizontal-Cylindrical' => 'Horizontal Cylindrical',
125            'Horizontal-Spherical'   => 'Horizontal Spherical',
126            'Vertical-Cylindrical'   => 'Vertical Cylindrical',
127            'Vertical-Spherical'     => 'Vertical Spherical',
128        },
129    },
130    PanoramicStitchPhi0   => { Writable => 'real' },
131    PanoramicStitchPhi1   => { Writable => 'real' },
132    PanoramicStitchTheta0 => { Writable => 'real' },
133    PanoramicStitchTheta1 => { Writable => 'real' },
134    WhiteBalance0         => { Writable => 'real' },
135    WhiteBalance1         => { Writable => 'real' },
136    WhiteBalance2         => { Writable => 'real' },
137    Brightness            => { Avoid => 1 },
138    Contrast              => { Avoid => 1 },
139    CameraModelID         => { Avoid => 1 },
140    ExposureCompensation  => { Avoid => 1 },
141    PipelineVersion       => { },
142    StreamType            => { },
143);
144
145# Microsoft Photo 1.2 schema properties (MP) (ref PH)
146# (also ref http://msdn.microsoft.com/en-us/library/windows/desktop/ee719905(v=vs.85).aspx)
147my %sRegions = (
148    STRUCT_NAME => 'Microsoft Regions',
149    NAMESPACE   => 'MPReg',
150    NOTES => q{
151        Note that PersonLiveIdCID element is called PersonLiveCID according to the
152        Microsoft specification, but in practice their software actually writes
153        PersonLiveIdCID, so ExifTool uses this too.
154    },
155    Rectangle         => { },
156    PersonDisplayName => { },
157    PersonEmailDigest => { },
158    PersonLiveIdCID   => { },  # (see https://exiftool.org/forum/index.php?topic=4274.msg20368#msg20368)
159    PersonSourceID    => { },
160);
161%Image::ExifTool::Microsoft::MP = (
162    %Image::ExifTool::XMP::xmpTableDefaults,
163    GROUPS => { 0 => 'XMP', 1 => 'XMP-MP', 2 => 'Image' },
164    NAMESPACE => 'MP',
165    TABLE_DESC => 'XMP Microsoft Photo',
166    VARS => { NO_ID => 1 },
167    NOTES => q{
168        Microsoft Photo 1.2 schema XMP tags which have been observed.
169    },
170    RegionInfo => {
171        Name => 'RegionInfoMP',
172        Struct => {
173            STRUCT_NAME => 'Microsoft RegionInfo',
174            NAMESPACE   => 'MPRI',
175            Regions   => { Struct => \%sRegions, List => 'Bag' },
176            DateRegionsValid => {
177                Writable => 'date',
178                Shift => 'Time',
179                Groups => { 2 => 'Time'},
180                PrintConv => '$self->ConvertDateTime($val)',
181                PrintConvInv => '$self->InverseDateTime($val,undef,1)',
182            },
183        },
184    },
185    # remove "MP" from tag name (was added only to avoid conflict with XMP-mwg-rs:RegionInfo)
186    RegionInfoRegions                  => { Flat => 1, Name => 'RegionInfoRegions' },
187    RegionInfoDateRegionsValid         => { Flat => 1, Name => 'RegionInfoDateRegionsValid' },
188    # shorten flattened Regions tag names to make them easier to use
189    RegionInfoRegionsRectangle         => { Flat => 1, Name => 'RegionRectangle' },
190    RegionInfoRegionsPersonDisplayName => { Flat => 1, Name => 'RegionPersonDisplayName' },
191    RegionInfoRegionsPersonEmailDigest => { Flat => 1, Name => 'RegionPersonEmailDigest' },
192    RegionInfoRegionsPersonLiveIdCID   => { Flat => 1, Name => 'RegionPersonLiveIdCID' },
193    RegionInfoRegionsPersonSourceID    => { Flat => 1, Name => 'RegionPersonSourceID' },
194);
195
196# Xtra tags written in MP4 files written by Microsoft Windows Media Player
197# (ref http://msdn.microsoft.com/en-us/library/windows/desktop/dd562330(v=VS.85).aspx)
198# Note: These tags are closely related to tags in Image::ExifTool::ASF::ExtendedDescr
199#       and Image::ExifTool::WTV::Metadata
200%Image::ExifTool::Microsoft::Xtra = (
201    PROCESS_PROC => \&ProcessXtra,
202    WRITE_PROC => \&WriteXtra,
203    CHECK_PROC => \&CheckXtra,
204    WRITE_GROUP => 'Microsoft',
205    AVOID => 1,
206    GROUPS => { 0 => 'QuickTime', 2 => 'Video' },
207    VARS => { NO_ID => 1 },
208    NOTES => q{
209        Tags found in the Microsoft "Xtra" atom of QuickTime videos.  Tag ID's are
210        not shown because some are unruly GUID's.  Currently most of these tags are
211        not writable because the Microsoft documentation is poor and samples were
212        not available, but more tags may be made writable in the future if samples
213        are provided.  Note that writable tags in this table are are flagged to
214        "Avoid", which means that other more common tags will be written instead if
215        possible unless the Microsoft group is specified explicitly.
216    },
217    Abstract                    => { },
218    AcquisitionTime             => { Groups => { 2 => 'Time' } },
219    AcquisitionTimeDay          => { Groups => { 2 => 'Time' } },
220    AcquisitionTimeMonth        => { Groups => { 2 => 'Time' } },
221    AcquisitionTimeYear         => { Groups => { 2 => 'Time' } },
222    AcquisitionTimeYearMonth    => { Groups => { 2 => 'Time' } },
223    AcquisitionTimeYearMonthDay => { Groups => { 2 => 'Time' } },
224    AlbumArtistSortOrder        => { },
225    AlbumID                     => { },
226    AlbumIDAlbumArtist          => { },
227    AlbumTitleSortOrder         => { },
228    AlternateSourceURL          => { },
229    AudioBitrate                => { },
230    AudioFormat                 => { },
231    Author                      => { Groups => { 2 => 'Author' } },
232    AuthorSortOrder             => { },
233    AverageLevel                => { },
234    Bitrate                     => { },
235    BuyNow                      => { },
236    BuyTickets                  => { },
237    CallLetters                 => { },
238    CameraManufacturer          => { },
239    CameraModel                 => { },
240    CDTrackEnabled              => { },
241    Channels                    => { },
242    chapterNum                  => { },
243    Comment                     => { },
244    ContentDistributorDuration  => { },
245    Copyright                   => { Groups => { 2 => 'Author' } },
246    Count                       => { },
247    CurrentBitrate              => { },
248    Description                 => { },
249    DisplayArtist               => { },
250    DLNAServerUDN               => { },
251    DLNASourceURI               => { },
252    DRMKeyID                    => { },
253    DRMIndividualizedVersion    => { },
254    DTCPIPHost                  => { },
255    DTCPIPPort                  => { },
256    Duration                    => { },
257    DVDID                       => { },
258    Event                       => { },
259    FileSize                    => { },
260    FileType                    => { },
261    FourCC                      => { },
262    FormatTag                   => { },
263    FrameRate                   => { },
264    Frequency                   => { },
265    IsNetworkFeed               => { },
266    Is_Protected                => 'IsProtected',
267    IsVBR                       => { },
268    LeadPerformer               => { },
269    LibraryID                   => { },
270    LibraryName                 => { },
271    Location                    => { },
272    MediaContentTypes           => { },
273    MediaType                   => { },
274    ModifiedBy                  => { },
275    MoreInfo                    => { },
276    PartOfSet                   => { },
277    PeakValue                   => { },
278    PixelAspectRatioX           => { },
279    PixelAspectRatioY           => { },
280    PlaylistIndex               => { },
281    Provider                    => { },
282    ProviderLogoURL             => { },
283    ProviderURL                 => { },
284    RadioBand                   => { },
285    RadioFormat                 => { },
286    RatingOrg                   => { },
287    RecordingTime               => { Groups => { 2 => 'Time' } },
288    RecordingTimeDay            => { Groups => { 2 => 'Time' } },
289    RecordingTimeMonth          => { Groups => { 2 => 'Time' } },
290    RecordingTimeYear           => { Groups => { 2 => 'Time' } },
291    RecordingTimeYearMonth      => { Groups => { 2 => 'Time' } },
292    RecordingTimeYearMonthDay   => { Groups => { 2 => 'Time' } },
293    ReleaseDate                 => { Groups => { 2 => 'Time' } },
294    ReleaseDateDay              => { Groups => { 2 => 'Time' } },
295    ReleaseDateMonth            => { Groups => { 2 => 'Time' } },
296    ReleaseDateYear             => { Groups => { 2 => 'Time' } },
297    ReleaseDateYearMonth        => { Groups => { 2 => 'Time' } },
298    ReleaseDateYearMonthDay     => { Groups => { 2 => 'Time' } },
299    RequestState                => { },
300    ShadowFilePath              => { },
301    SourceURL                   => { },
302    Subject                     => { },
303    SyncState                   => { },
304    Sync01                      => { },
305    Sync02                      => { },
306    Sync03                      => { },
307    Sync04                      => { },
308    Sync05                      => { },
309    Sync06                      => { },
310    Sync07                      => { },
311    Sync08                      => { },
312    Sync09                      => { },
313    Sync10                      => { },
314    Sync11                      => { },
315    Sync12                      => { },
316    Sync13                      => { },
317    Sync14                      => { },
318    Sync15                      => { },
319    Sync16                      => { },
320    SyncOnly                    => { },
321    Temporary                   => { },
322    Title                       => { },
323    titleNum                    => { },
324    TitleSortOrder              => { },
325    TotalDuration               => { },
326    TrackingID                  => { },
327    UserCustom1                 => { },
328    UserCustom2                 => { },
329    UserEffectiveRating         => { },
330    UserLastPlayedTime          => { },
331    UserPlayCount               => { },
332    UserPlaycountAfternoon      => { },
333    UserPlaycountEvening        => { },
334    UserPlaycountMorning        => { },
335    UserPlaycountNight          => { },
336    UserPlaycountWeekday        => { },
337    UserPlaycountWeekend        => { },
338    UserRating                  => { },
339    UserServiceRating           => { },
340    VideoBitrate                => { },
341    VideoFormat                 => { },
342    'WM/AlbumArtist'            => { Name => 'AlbumArtist', Writable => 'Unicode' }, # (NC)
343    'WM/AlbumCoverURL'          => { Name => 'AlbumCoverURL', Writable => 'Unicode' }, # (NC)
344    'WM/AlbumTitle'             => { Name => 'AlbumTitle',  Writable => 'Unicode' }, # (NC)
345    'WM/BeatsPerMinute'         => 'BeatsPerMinute',
346    'WM/Category'               => { Name => 'Category',    Writable => 'Unicode', List => 1 },
347    'WM/Composer'               => { Name => 'Composer',    Writable => 'Unicode' }, # (NC)
348    'WM/Conductor'              => { Name => 'Conductor',   Writable => 'Unicode', List => 1 },
349    'WM/ContentDistributor'     => { Name => 'ContentDistributor', Writable => 'Unicode' },
350    'WM/ContentDistributorType' => 'ContentDistributorType',
351    'WM/ContentGroupDescription'=> 'ContentGroupDescription',
352    'WM/Director'               => { Name => 'Director',    Writable => 'Unicode', List => 1 },
353    'WM/EncodingTime'           => {
354        Name => 'EncodingTime',
355        Groups => { 2 => 'Time' },
356        Shift => 'Time',
357        Writable => 'date',
358        PrintConv => '$self->ConvertDateTime($val)',
359        PrintConvInv => '$self->InverseDateTime($val)',
360    },
361    'WM/Genre'                  => 'Genre',
362    'WM/GenreID'                => 'GenreID',
363    'WM/InitialKey'             => { Name => 'InitialKey',  Writable => 'Unicode' },
364    'WM/Language'               => 'Language',
365    'WM/Lyrics'                 => 'Lyrics',
366    'WM/MCDI'                   => 'MCDI',
367    'WM/MediaClassPrimaryID'    => {
368        Name => 'MediaClassPrimaryID',
369        Writable => 'GUID',
370        PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
371            'D1607DBC-E323-4BE2-86A1-48A42A28441E' => 'Music',
372            'DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B' => 'Video',
373            '01CD0F29-DA4E-4157-897B-6275D50C4F11' => 'Audio (not music)',
374            'FCF24A76-9A57-4036-990D-E35DD8B244E1' => 'Other (not audio or video)',
375        },
376    },
377    'WM/MediaClassSecondaryID' => {
378        Name => 'MediaClassSecondaryID',
379        Writable => 'GUID',
380        PrintConv => { #http://msdn.microsoft.com/en-us/library/windows/desktop/dd757960(v=vs.85).aspx
381            'E0236BEB-C281-4EDE-A36D-7AF76A3D45B5' => 'Audio Book',
382            '3A172A13-2BD9-4831-835B-114F6A95943F' => 'Spoken Word',
383            '6677DB9B-E5A0-4063-A1AD-ACEB52840CF1' => 'Audio News',
384            '1B824A67-3F80-4E3E-9CDE-F7361B0F5F1B' => 'Talk Show',
385            '1FE2E091-4E1E-40CE-B22D-348C732E0B10' => 'Video News',
386            'D6DE1D88-C77C-4593-BFBC-9C61E8C373E3' => 'Web-based Video',
387            '00033368-5009-4AC3-A820-5D2D09A4E7C1' => 'Sound Clip from Game',
388            'F24FF731-96FC-4D0F-A2F5-5A3483682B1A' => 'Song from Game',
389            'E3E689E2-BA8C-4330-96DF-A0EEEFFA6876' => 'Music Video',
390            'B76628F4-300D-443D-9CB5-01C285109DAF' => 'Home Movie',
391            'A9B87FC9-BD47-4BF0-AC4F-655B89F7D868' => 'Feature Film',
392            'BA7F258A-62F7-47A9-B21F-4651C42A000E' => 'TV Show',
393            '44051B5B-B103-4B5C-92AB-93060A9463F0' => 'Corporate Video',
394            '0B710218-8C0C-475E-AF73-4C41C0C8F8CE' => 'Home Video from Pictures',
395            '00000000-0000-0000-0000-000000000000' => 'Unknown Content', #PH
396        },
397    },
398    'WM/MediaOriginalBroadcastDateTime' => {
399        Name => 'MediaOriginalBroadcastDateTime',
400        Groups => { 2 => 'Time' },
401        PrintConv => '$self->ConvertDateTime($val)',
402    },
403    'WM/MediaOriginalChannel'   => 'MediaOriginalChannel',
404    'WM/MediaStationName'       => 'MediaStationName',
405    'WM/Mood'                   => { Name => 'Mood',        Writable => 'Unicode' },
406    'WM/OriginalAlbumTitle'     => { Name => 'OriginalAlbumTitle',  Writable => 'Unicode' }, # (NC)
407    'WM/OriginalArtist'         => { Name => 'OriginalArtist',      Writable => 'Unicode' }, # (NC)
408    'WM/OriginalLyricist'       => { Name => 'OriginalLyricist',    Writable => 'Unicode' }, # (NC)
409    'WM/ParentalRating'         => { Name => 'ParentalRating',      Writable => 'Unicode' },
410    'WM/PartOfSet'              => 'PartOfSet',
411    'WM/Period'                 => { Name => 'Period',      Writable => 'Unicode' },
412    'WM/Producer'               => { Name => 'Producer',    Writable => 'Unicode', List => 1 },
413    'WM/ProtectionType'         => 'ProtectionType',
414    'WM/Provider'               => { Name => 'Provider',    Writable => 'Unicode' }, # (NC)
415    'WM/ProviderRating'         => 'ProviderRating',
416    'WM/ProviderStyle'          => 'ProviderStyle',
417    'WM/Publisher'              => { Name => 'Publisher',   Writable => 'Unicode' }, # (multiple entries separated by semicolon)
418    'WM/SharedUserRating'       => { Name => 'SharedUserRating', Writable => 'int64u' },
419    'WM/SubscriptionContentID'  => 'SubscriptionContentID',
420    'WM/SubTitle'               => { Name => 'Subtitle',    Writable => 'Unicode' },
421    'WM/SubTitleDescription'    => 'SubtitleDescription',
422    'WM/TrackNumber'            => 'TrackNumber',
423    'WM/UniqueFileIdentifier'   => 'UniqueFileIdentifier',
424    'WM/VideoFrameRate'         => 'VideoFrameRate',
425    'WM/VideoHeight'            => 'VideoHeight',
426    'WM/VideoWidth'             => 'VideoWidth',
427    'WM/WMCollectionGroupID'    => 'WMCollectionGroupID',
428    'WM/WMCollectionID'         => 'WMCollectionID',
429    'WM/WMContentID'            => 'WMContentID',
430    'WM/WMShadowFileSourceDRMType' => 'WMShadowFileSourceDRMType',
431    'WM/WMShadowFileSourceFileType' => 'WMShadowFileSourceFileType',
432    'WM/Writer'                 => { Name => 'Writer',  Groups => { 2 => 'Author' }, Writable => 'Unicode' }, # (NC)
433    'WM/Year'                   => { Name => 'Year',    Groups => { 2 => 'Time' } },
434    'WM/PromotionURL'           => { Name => 'PromotionURL',Writable => 'Unicode' },
435    'WM/AuthorURL'              => { Name => 'AuthorURL', Groups => { 2 => 'Author' }, Writable => 'Unicode' },
436    'WM/EncodedBy',             => { Name => 'EncodedBy',   Writable => 'Unicode' },
437
438    # I can't find documentation for the following tags in videos,
439    # but the tag ID's correspond to Microsoft property GUID+ID's
440    # References:
441    #  http://msdn.microsoft.com/en-us/library/cc251929%28v=prot.10%29.aspx
442    #  http://multi-rename-script.googlecode.com/svn-history/r4/trunk/plugins/ShellDetails/ShellDetails.ini
443    # I have observed only 1 so far:
444    '{2CBAA8F5-D81F-47CA-B17A-F8D822300131} 100' => {
445        Name => 'DateAcquired', # (seems to be when videos are downloaded from the camera)
446        Groups => { 2 => 'Time' },
447        Shift => 'Time',
448        Writable => 'vt_filetime',
449        PrintConv => '$self->ConvertDateTime($val)',
450        PrintConvInv => '$self->InverseDateTime($val,undef)',
451    },
452    # the following have not yet been observed...
453    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 10'    => 'Name',
454    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 12'    => 'Size',
455    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 4'     => 'Type',
456    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 14'    => {
457        Name => 'DateModified',
458        Groups => { 2 => 'Time' },
459        PrintConv => '$self->ConvertDateTime($val)',
460    },
461    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 15'    => {
462        Name => 'DateCreated',
463        Groups => { 2 => 'Time' },
464        PrintConv => '$self->ConvertDateTime($val)',
465    },
466    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 16'    => {
467        Name => 'DateAccessed',
468        Groups => { 2 => 'Time' },
469        PrintConv => '$self->ConvertDateTime($val)',
470    },
471    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 13'    => 'Attributes',
472    '{D8C3986F-813B-449C-845D-87B95D674ADE} 2'     => 'Status',
473    '{9B174B34-40FF-11D2-A27E-00C04FC30871} 4'     => 'Owner',
474    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 4'     => {
475        Name => 'Author',
476        Groups => { 2 => 'Author' },
477    },
478    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 2'     => 'Title',
479    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 3'     => 'Subject',
480    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 2'     => 'Category',
481    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 14'    => 'Pages',
482    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 6'     => 'Comments',
483    '{64440492-4C8B-11D1-8B70-080036B11A03} 11'    => {
484        Name => 'Copyright',
485        Groups => { 2 => 'Author' },
486    },
487    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 2'     => 'Artist',
488    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 4'     => 'AlbumTitle',
489    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 5'     => {
490        Name => 'Year',
491        Groups => { 2 => 'Time' },
492    },
493    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 7'     => 'TrackNumber',
494    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 11'    => 'Genre',
495    '{64440490-4C8B-11D1-8B70-080036B11A03} 3'     => 'Duration',
496    '{64440490-4C8B-11D1-8B70-080036B11A03} 4'     => 'Bitrate',
497    '{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED} 2'     => 'Protected',
498    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 272'   => 'CameraModel',
499    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 36867' => {
500        Name => 'DatePictureTaken',
501        Groups => { 2 => 'Time' },
502        PrintConv => '$self->ConvertDateTime($val)',
503    },
504    '{6444048F-4C8B-11D1-8B70-080036B11A03} 13'    => 'Dimensions',
505    '{6444048F-4C8B-11D1-8B70-080036B11A03} 3'     => 'Untitled0',
506    '{6444048F-4C8B-11D1-8B70-080036B11A03} 4'     => 'Untitled1',
507    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 2'     => 'EpisodeName',
508    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 3'     => 'ProgramDescription',
509    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 12'    => 'Untitled2',
510    '{64440490-4C8B-11D1-8B70-080036B11A03} 6'     => 'AudioSampleSize',
511    '{64440490-4C8B-11D1-8B70-080036B11A03} 5'     => 'AudioSampleRate',
512    '{64440490-4C8B-11D1-8B70-080036B11A03} 7'     => 'Channels',
513    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 15'    => 'Company',
514    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 3'     => 'Description',
515    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 4'     => 'FileVersion',
516    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 7'     => 'ProductName',
517    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 8'     => 'ProductVersion',
518    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 5'     => 'Keywords',
519    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 11'    => 'Type',
520    '{6D24888F-4718-4BDA-AFED-EA0FB4386CD8} 100'   => 'OfflineStatus',
521    '{A94688B6-7D9F-4570-A648-E3DFC0AB2B3F} 100'   => 'OfflineAvailability',
522    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 9'     => 'PerceivedType',
523    '{1E3EE840-BC2B-476C-8237-2ACD1A839B22} 3'     => 'Kinds',
524    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 36'    => 'Conductors',
525    '{64440492-4C8B-11D1-8B70-080036B11A03} 9'     => 'Rating',
526    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 271'   => 'CameraMaker',
527    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 18'    => 'ProgramName',
528    '{293CA35A-09AA-4DD2-B180-1FE245728A52} 100'   => 'Duration',
529    '{BFEE9149-E3E2-49A7-A862-C05988145CEC} 100'   => 'IsOnline',
530    '{315B9C8D-80A9-4EF9-AE16-8E746DA51D70} 100'   => 'IsRecurring',
531    '{F6272D18-CECC-40B1-B26A-3911717AA7BD} 100'   => 'Location',
532    '{D55BAE5A-3892-417A-A649-C6AC5AAAEAB3} 100'   => 'OptionalAttendeeAddresses',
533    '{09429607-582D-437F-84C3-DE93A2B24C3C} 100'   => 'OptionalAttendees',
534    '{744C8242-4DF5-456C-AB9E-014EFB9021E3} 100'   => 'OrganizerAddress',
535    '{AAA660F9-9865-458E-B484-01BC7FE3973E} 100'   => 'OrganizerName',
536    '{72FC5BA4-24F9-4011-9F3F-ADD27AFAD818} 100'   => 'ReminderTime',
537    '{0BA7D6C3-568D-4159-AB91-781A91FB71E5} 100'   => 'RequiredAttendeeAddresses',
538    '{B33AF30B-F552-4584-936C-CB93E5CDA29F} 100'   => 'RequiredAttendees',
539    '{00F58A38-C54B-4C40-8696-97235980EAE1} 100'   => 'Resources',
540    '{5BF396D4-5EB2-466F-BDE9-2FB3F2361D6E} 100'   => 'Free-busyStatus',
541    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 3'     => 'TotalSize',
542    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 9'     => 'AccountName',
543    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 5'     => 'Computer',
544    '{9AD5BADB-CEA7-4470-A03D-B84E51B9949E} 100'   => 'Anniversary',
545    '{CD102C9C-5540-4A88-A6F6-64E4981C8CD1} 100'   => 'AssistantsName',
546    '{9A93244D-A7AD-4FF8-9B99-45EE4CC09AF6} 100'   => 'AssistantsPhone',
547    '{176DC63C-2688-4E89-8143-A347800F25E9} 47'    => 'Birthday',
548    '{730FB6DD-CF7C-426B-A03F-BD166CC9EE24} 100'   => 'BusinessAddress',
549    '{402B5934-EC5A-48C3-93E6-85E86A2D934E} 100'   => 'BusinessCity',
550    '{B0B87314-FCF6-4FEB-8DFF-A50DA6AF561C} 100'   => 'BusinessCountry-Region',
551    '{BC4E71CE-17F9-48D5-BEE9-021DF0EA5409} 100'   => 'BusinessPOBox',
552    '{E1D4A09E-D758-4CD1-B6EC-34A8B5A73F80} 100'   => 'BusinessPostalCode',
553    '{446F787F-10C4-41CB-A6C4-4D0343551597} 100'   => 'BusinessStateOrProvince',
554    '{DDD1460F-C0BF-4553-8CE4-10433C908FB0} 100'   => 'BusinessStreet',
555    '{91EFF6F3-2E27-42CA-933E-7C999FBE310B} 100'   => 'BusinessFax',
556    '{56310920-2491-4919-99CE-EADB06FAFDB2} 100'   => 'BusinessHomePage',
557    '{6A15E5A0-0A1E-4CD7-BB8C-D2F1B0C929BC} 100'   => 'BusinessPhone',
558    '{BF53D1C3-49E0-4F7F-8567-5A821D8AC542} 100'   => 'CallbackNumber',
559    '{8FDC6DEA-B929-412B-BA90-397A257465FE} 100'   => 'CarPhone',
560    '{D4729704-8EF1-43EF-9024-2BD381187FD5} 100'   => 'Children',
561    '{8589E481-6040-473D-B171-7FA89C2708ED} 100'   => 'CompanyMainPhone',
562    '{FC9F7306-FF8F-4D49-9FB6-3FFE5C0951EC} 100'   => 'Department',
563    '{F8FA7FA3-D12B-4785-8A4E-691A94F7A3E7} 100'   => 'E-mailAddress',
564    '{38965063-EDC8-4268-8491-B7723172CF29} 100'   => 'E-mail2',
565    '{644D37B4-E1B3-4BAD-B099-7E7C04966ACA} 100'   => 'E-mail3',
566    '{84D8F337-981D-44B3-9615-C7596DBA17E3} 100'   => 'E-mailList',
567    '{CC6F4F24-6083-4BD4-8754-674D0DE87AB8} 100'   => 'E-mailDisplayName',
568    '{F1A24AA7-9CA7-40F6-89EC-97DEF9FFE8DB} 100'   => 'FileAs',
569    '{14977844-6B49-4AAD-A714-A4513BF60460} 100'   => 'FirstName',
570    '{635E9051-50A5-4BA2-B9DB-4ED056C77296} 100'   => 'FullName',
571    '{3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD} 100'   => 'Gender',
572    '{176DC63C-2688-4E89-8143-A347800F25E9} 70'    => 'GivenName',
573    '{5DC2253F-5E11-4ADF-9CFE-910DD01E3E70} 100'   => 'Hobbies',
574    '{98F98354-617A-46B8-8560-5B1B64BF1F89} 100'   => 'HomeAddress',
575    '{176DC63C-2688-4E89-8143-A347800F25E9} 65'    => 'HomeCity',
576    '{08A65AA1-F4C9-43DD-9DDF-A33D8E7EAD85} 100'   => 'HomeCountry-Region',
577    '{7B9F6399-0A3F-4B12-89BD-4ADC51C918AF} 100'   => 'HomePOBox',
578    '{8AFCC170-8A46-4B53-9EEE-90BAE7151E62} 100'   => 'HomePostalCode',
579    '{C89A23D0-7D6D-4EB8-87D4-776A82D493E5} 100'   => 'HomeStateOrProvince',
580    '{0ADEF160-DB3F-4308-9A21-06237B16FA2A} 100'   => 'HomeStreet',
581    '{660E04D6-81AB-4977-A09F-82313113AB26} 100'   => 'HomeFax',
582    '{176DC63C-2688-4E89-8143-A347800F25E9} 20'    => 'HomePhone',
583    '{D68DBD8A-3374-4B81-9972-3EC30682DB3D} 100'   => 'IMAddresses',
584    '{F3D8F40D-50CB-44A2-9718-40CB9119495D} 100'   => 'Initials',
585    '{176DC63C-2688-4E89-8143-A347800F25E9} 6'     => 'JobTitle',
586    '{97B0AD89-DF49-49CC-834E-660974FD755B} 100'   => 'Label',
587    '{8F367200-C270-457C-B1D4-E07C5BCD90C7} 100'   => 'LastName',
588    '{C0AC206A-827E-4650-95AE-77E2BB74FCC9} 100'   => 'MailingAddress',
589    '{176DC63C-2688-4E89-8143-A347800F25E9} 71'    => 'MiddleName',
590    '{176DC63C-2688-4E89-8143-A347800F25E9} 35'    => 'CellPhone',
591    '{176DC63C-2688-4E89-8143-A347800F25E9} 74'    => 'Nickname',
592    '{176DC63C-2688-4E89-8143-A347800F25E9} 7'     => 'OfficeLocation',
593    '{508161FA-313B-43D5-83A1-C1ACCF68622C} 100'   => 'OtherAddress',
594    '{6E682923-7F7B-4F0C-A337-CFCA296687BF} 100'   => 'OtherCity',
595    '{8F167568-0AAE-4322-8ED9-6055B7B0E398} 100'   => 'OtherCountry-Region',
596    '{8B26EA41-058F-43F6-AECC-4035681CE977} 100'   => 'OtherPOBox',
597    '{95C656C1-2ABF-4148-9ED3-9EC602E3B7CD} 100'   => 'OtherPostalCode',
598    '{71B377D6-E570-425F-A170-809FAE73E54E} 100'   => 'OtherStateOrProvince',
599    '{FF962609-B7D6-4999-862D-95180D529AEA} 100'   => 'OtherStreet',
600    '{D6304E01-F8F5-4F45-8B15-D024A6296789} 100'   => 'Pager',
601    '{176DC63C-2688-4E89-8143-A347800F25E9} 69'    => 'PersonalTitle',
602    '{C8EA94F0-A9E3-4969-A94B-9C62A95324E0} 100'   => 'City',
603    '{E53D799D-0F3F-466E-B2FF-74634A3CB7A4} 100'   => 'Country-Region',
604    '{DE5EF3C7-46E1-484E-9999-62C5308394C1} 100'   => 'POBox',
605    '{18BBD425-ECFD-46EF-B612-7B4A6034EDA0} 100'   => 'PostalCode',
606    '{F1176DFE-7138-4640-8B4C-AE375DC70A6D} 100'   => 'StateOrProvince',
607    '{63C25B20-96BE-488F-8788-C09C407AD812} 100'   => 'Street',
608    '{176DC63C-2688-4E89-8143-A347800F25E9} 48'    => 'PrimaryE-mail',
609    '{176DC63C-2688-4E89-8143-A347800F25E9} 25'    => 'PrimaryPhone',
610    '{7268AF55-1CE4-4F6E-A41F-B6E4EF10E4A9} 100'   => 'Profession',
611    '{9D2408B6-3167-422B-82B0-F583B7A7CFE3} 100'   => 'Spouse',
612    '{176DC63C-2688-4E89-8143-A347800F25E9} 73'    => 'Suffix',
613    '{AAF16BAC-2B55-45E6-9F6D-415EB94910DF} 100'   => 'TTY-TTDPhone',
614    '{C554493C-C1F7-40C1-A76C-EF8C0614003E} 100'   => 'Telex',
615    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 18'    => 'Webpage',
616    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 27'    => 'Status',
617    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 26'    => 'ContentType',
618    '{43F8D7B7-A444-4F87-9383-52271C9B915C} 100'   => {
619        Name => 'DateArchived',
620        Groups => { 2 => 'Time' },
621        PrintConv => '$self->ConvertDateTime($val)',
622    },
623    '{72FAB781-ACDA-43E5-B155-B2434F85E678} 100'   => {
624        Name => 'DateCompleted',
625        Groups => { 2 => 'Time' },
626        PrintConv => '$self->ConvertDateTime($val)',
627    },
628    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 18258' => {
629        Name => 'DateImported',
630        Groups => { 2 => 'Time' },
631        PrintConv => '$self->ConvertDateTime($val)',
632    },
633    '{276D7BB0-5B34-4FB0-AA4B-158ED12A1809} 100'   => 'ClientID',
634    '{F334115E-DA1B-4509-9B3D-119504DC7ABB} 100'   => 'Contributors',
635    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 11'    => 'LastPrinted',
636    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 13'    => {
637        Name => 'DateLastSaved',
638        Groups => { 2 => 'Time' },
639        PrintConv => '$self->ConvertDateTime($val)',
640    },
641    '{1E005EE6-BF27-428B-B01C-79676ACD2870} 100'   => 'Division',
642    '{E08805C8-E395-40DF-80D2-54F0D6C43154} 100'   => 'DocumentID',
643    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 7'     => 'Slides',
644    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 10'    => 'TotalEditingTime',
645    '{F29F85E0-4FF9-1068-AB91-08002B27B3D9} 15'    => 'WordCount',
646    '{3F8472B5-E0AF-4DB2-8071-C53FE76AE7CE} 100'   => 'DueDate',
647    '{C75FAA05-96FD-49E7-9CB4-9F601082D553} 100'   => 'EndDate',
648    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 12'    => 'FileCount',
649    '{41CF5AE0-F75A-4806-BD87-59C7D9248EB9} 100'   => 'WindowsFileName',
650    '{67DF94DE-0CA7-4D6F-B792-053A3E4F03CF} 100'   => 'FlagColor',
651    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 12'    => 'FlagStatus',
652    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 2'     => 'SpaceFree',
653    '{6444048F-4C8B-11D1-8B70-080036B11A03} 7'     => 'BitDepth',
654    '{6444048F-4C8B-11D1-8B70-080036B11A03} 5'     => 'HorizontalResolution',
655    '{6444048F-4C8B-11D1-8B70-080036B11A03} 6'     => 'VerticalResolution',
656    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 11'    => 'Importance',
657    '{F23F425C-71A1-4FA8-922F-678EA4A60408} 100'   => 'IsAttachment',
658    '{5CDA5FC8-33EE-4FF3-9094-AE7BD8868C4D} 100'   => 'IsDeleted',
659    '{5DA84765-E3FF-4278-86B0-A27967FBDD03} 100'   => 'HasFlag',
660    '{A6F360D2-55F9-48DE-B909-620E090A647C} 100'   => 'IsCompleted',
661    '{346C8BD1-2E6A-4C45-89A4-61B78E8E700F} 100'   => 'Incomplete',
662    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 10'    => 'ReadStatus',
663    '{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902} 100'   => 'Shared',
664    '{D0A04F0A-462A-48A4-BB2F-3706E88DBD7D} 100'   => {
665        Name => 'Creator',
666        Groups => { 2 => 'Author' },
667    },
668    '{F7DB74B4-4287-4103-AFBA-F1B13DCD75CF} 100'   => {
669        Name => 'Date',
670        Groups => { 2 => 'Time' },
671        PrintConv => '$self->ConvertDateTime($val)',
672    },
673    '{B725F130-47EF-101A-A5F1-02608C9EEBAC} 2'     => 'FolderName',
674    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 6'     => 'FolderPath',
675    '{DABD30ED-0043-4789-A7F8-D013A4736622} 100'   => 'Folder',
676    '{D4D0AA16-9948-41A4-AA85-D97FF9646993} 100'   => 'Participants',
677    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 7'     => 'Path',
678    '{DEA7C82C-1D89-4A66-9427-A4E3DEBABCB1} 100'   => 'ContactNames',
679    '{95BEB1FC-326D-4644-B396-CD3ED90E6DDF} 100'   => 'EntryType',
680    '{D5CDD502-2E9C-101B-9397-08002B2CF9AE} 28'    => 'Language',
681    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 23'    => {
682        Name => 'DateVisited',
683        Groups => { 2 => 'Time' },
684        PrintConv => '$self->ConvertDateTime($val)',
685    },
686    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 21'    => 'Description',
687    '{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25} 3'     => 'LinkStatus',
688    '{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25} 2'     => 'LinkTarget',
689    '{5CBF2787-48CF-4208-B90E-EE5E5D420294} 2'     => 'URL',
690    '{2E4B640D-5019-46D8-8881-55414CC5CAA0} 100'   => 'MediaCreated',
691    '{DE41CC29-6971-4290-B472-F59F2E2F31E2} 100'   => {
692        Name => 'DateReleased',
693        Groups => { 2 => 'Time' },
694        PrintConv => '$self->ConvertDateTime($val)',
695    },
696    '{64440492-4C8B-11D1-8B70-080036B11A03} 36'    => 'EncodedBy',
697    '{64440492-4C8B-11D1-8B70-080036B11A03} 22'    => 'Producers',
698    '{64440492-4C8B-11D1-8B70-080036B11A03} 30'    => 'Publisher',
699    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 38'    => 'Subtitle',
700    '{64440492-4C8B-11D1-8B70-080036B11A03} 34'    => 'UserWebURL',
701    '{64440492-4C8B-11D1-8B70-080036B11A03} 23'    => 'Writers',
702    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 21'    => 'Attachments',
703    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 2'     => 'BccAddresses',
704    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 3'     => 'BccNames',
705    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 4'     => 'CcAddresses',
706    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 5'     => 'CcNames',
707    '{DC8F80BD-AF1E-4289-85B6-3DFC1B493992} 100'   => 'ConversationID',
708    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 20'    => {
709        Name => 'DateReceived',
710        Groups => { 2 => 'Time' },
711        PrintConv => '$self->ConvertDateTime($val)',
712    },
713    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 19'    => {
714        Name => 'DateSent',
715        Groups => { 2 => 'Time' },
716        PrintConv => '$self->ConvertDateTime($val)',
717    },
718    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 13'    => 'FromAddresses',
719    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 14'    => 'FromNames',
720    '{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4} 8'     => 'HasAttachments',
721    '{0BE1C8E7-1981-4676-AE14-FDD78F05A6E7} 100'   => 'SenderAddress',
722    '{0DA41CFA-D224-4A18-AE2F-596158DB4B3A} 100'   => 'SenderName',
723    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 15'    => 'Store',
724    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 16'    => 'ToAddresses',
725    '{BCCC8A3C-8CEF-42E5-9B1C-C69079398BC7} 100'   => 'ToDoTitle',
726    '{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD} 17'    => 'ToNames',
727    '{FDF84370-031A-4ADD-9E91-0D775F1C6605} 100'   => 'Mileage',
728    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 13'    => 'AlbumArtist',
729    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 35'    => 'Beats-per-minute',
730    '{64440492-4C8B-11D1-8B70-080036B11A03} 19'    => 'Composers',
731    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 34'    => 'InitialKey',
732    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 39'    => 'Mood',
733    '{56A3372E-CE9C-11D2-9F0E-006097C686F6} 37'    => 'PartOfSet',
734    '{64440492-4C8B-11D1-8B70-080036B11A03} 31'    => 'Period',
735    '{4776CAFA-BCE4-4CB1-A23E-265E76D8EB11} 100'   => 'Color',
736    '{64440492-4C8B-11D1-8B70-080036B11A03} 21'    => 'ParentalRating',
737    '{10984E0A-F9F2-4321-B7EF-BAF195AF4319} 100'   => 'ParentalRatingReason',
738    '{9B174B35-40FF-11D2-A27E-00C04FC30871} 5'     => 'SpaceUsed',
739    '{D35F743A-EB2E-47F2-A286-844132CB1427} 100'   => 'ExifVersion',
740    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 18248' => 'Event',
741    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37380' => 'ExposureBias',
742    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 34850' => 'ExposureProgram',
743    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 33434' => 'ExposureTime',
744    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 33437' => 'F-stop',
745    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37385' => 'FlashMode',
746    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37386' => 'FocalLength',
747    '{A0E74609-B84D-4F49-B860-462BD9971F98} 100'   => 'FocalLength35mm',
748    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 34855' => 'ISOSpeed',
749    '{E6DDCAF7-29C5-4F0A-9A68-D19412EC7090} 100'   => 'LensMaker',
750    '{E1277516-2B5F-4869-89B1-2E585BD38B7A} 100'   => 'LensModel',
751    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37384' => 'LightSource',
752    '{08F6D7C2-E3F2-44FC-AF1E-5AA5C81A2D3E} 100'   => 'MaxAperture',
753    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37383' => 'MeteringMode',
754    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 274'   => 'Orientation',
755    '{6D217F6D-3F6A-4825-B470-5F03CA2FBE9B} 100'   => 'ProgramMode',
756    '{49237325-A95A-4F67-B211-816B2D45D2E0} 100'   => 'Saturation',
757    '{14B81DA1-0135-4D31-96D9-6CBFC9671A99} 37382' => 'SubjectDistance',
758    '{EE3D3D8A-5381-4CFA-B13B-AAF66B5F4EC9} 100'   => 'WhiteBalance',
759    '{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4} 5'     => 'Priority',
760    '{39A7F922-477C-48DE-8BC8-B28441E342E3} 100'   => 'Project',
761    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 7'     => 'ChannelNumber',
762    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 12'    => 'ClosedCaptioning',
763    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 13'    => 'Rerun',
764    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 14'    => 'SAP',
765    '{4684FE97-8765-4842-9C13-F006447B178C} 100'   => 'BroadcastDate',
766    '{A5477F61-7A82-4ECA-9DDE-98B69B2479B3} 100'   => 'RecordingTime',
767    '{6D748DE2-8D38-4CC3-AC60-F009B057C557} 5'     => 'StationCallSign',
768    '{1B5439E7-EBA1-4AF8-BDD7-7AF1D4549493} 100'   => 'StationName',
769    '{560C36C0-503A-11CF-BAA1-00004C752A9A} 2'     => 'AutoSummary',
770    '{560C36C0-503A-11CF-BAA1-00004C752A9A} 3'     => 'Summary',
771    '{49691C90-7E17-101A-A91C-08002B2ECDA9} 3'     => 'SearchRanking',
772    '{F8D3F6AC-4874-42CB-BE59-AB454B30716A} 100'   => 'Sensitivity',
773    '{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902} 200'   => 'SharedWith',
774    '{668CDFA5-7A1B-4323-AE4B-E527393A1D81} 100'   => 'Source',
775    '{48FD6EC8-8A12-4CDF-A03E-4EC5A511EDDE} 100'   => 'StartDate',
776    '{D37D52C6-261C-4303-82B3-08B926AC6F12} 100'   => 'BillingInformation',
777    '{084D8A0A-E6D5-40DE-BF1F-C8820E7C877C} 100'   => 'Complete',
778    '{08C7CC5F-60F2-4494-AD75-55E3E0B5ADD0} 100'   => 'TaskOwner',
779    '{28636AA6-953D-11D2-B5D6-00C04FD918D0} 14'    => 'TotalFileSize',
780    '{0CEF7D53-FA64-11D1-A203-0000F81FEDEE} 9'     => 'LegalTrademarks',
781    '{64440491-4C8B-11D1-8B70-080036B11A03} 10'    => 'VideoCompression',
782    '{64440492-4C8B-11D1-8B70-080036B11A03} 20'    => 'Directors',
783    '{64440491-4C8B-11D1-8B70-080036B11A03} 8'     => 'DataRate',
784    '{64440491-4C8B-11D1-8B70-080036B11A03} 4'     => 'FrameHeight',
785    '{64440491-4C8B-11D1-8B70-080036B11A03} 6'     => 'FrameRate',
786    '{64440491-4C8B-11D1-8B70-080036B11A03} 3'     => 'FrameWidth',
787    '{64440491-4C8B-11D1-8B70-080036B11A03} 43'    => 'TotalBitrate',
788);
789
790#------------------------------------------------------------------------------
791# check new value for Xtra tag
792# Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref
793# Returns: error string, or undef on success
794sub CheckXtra($$$)
795{
796    my ($et, $tagInfo, $valPt) = @_;
797    my $format = $$tagInfo{Writable};
798    return 'Unknown format' unless $format;
799    if ($format =~ /^int/) {
800        return 'Not an integer' unless Image::ExifTool::IsInt($$valPt);
801    } elsif ($format ne 'Unicode') {
802        my @vals = ($$valPt);
803        return 'Invalid format' unless WriteXtraValue($et, $tagInfo, \@vals);
804    }
805    return undef;
806}
807
808#------------------------------------------------------------------------------
809# Decode value(s) in Microsoft Xtra tag
810# Inputs: 0) ExifTool object ref, 1) value data
811# Returns: Scalar context: decoded value, List context: 0) decoded value, 1) format string
812sub ReadXtraValue($$)
813{
814    my ($et, $data) = @_;
815    my ($format, $i, @vals);
816
817    return undef if length($data) < 10;
818
819    # (version flags according to the reference, but looks more like a count - PH)
820    my $count = Get32u(\$data, 0);
821    # point to start of first value (after 4-byte count, 4-byte length and 2-byte type)
822    my $valPos = 10;
823    for ($i=0; ;) {
824        # (stored value includes size of $valLen and $valType, so subtract 6)
825        my $valLen = Get32u(\$data, $valPos - 6) - 6;
826        last if $valPos + $valLen > length($data);
827        my $valType = Get16u(\$data, $valPos - 2);
828        my $val = substr($data, $valPos, $valLen);
829        # Note: all dumb Microsoft values are little-endian inside a big-endian-format file
830        SetByteOrder('II');
831        if ($valType == 8) {
832            $format = 'Unicode';
833            $val = $et->Decode($val, 'UCS2');
834        } elsif ($valType == 19 and $valLen == 8) {
835            $format = 'int64u';
836            $val = Get64u(\$val, 0);
837        } elsif ($valType == 21 and $valLen == 8) {
838            $format = 'date';
839            $val = Get64u(\$val, 0);
840            # convert time from 100 ns intervals since Jan 1, 1601
841            $val = $val * 1e-7 - 11644473600 if $val;
842            # (the Nikon S100 uses UTC timezone, same as ASF - PH)
843            $val = Image::ExifTool::ConvertUnixTime($val, 1);
844        } elsif ($valType == 72 and $valLen == 16) {
845            $format = 'GUID';
846            $val = uc unpack('H*',pack('NnnNN',unpack('VvvNN',$val)));
847            $val =~ s/(.{8})(.{4})(.{4})(.{4})/$1-$2-$3-$4-/;
848        } elsif ($valType == 65 and $valLen > 4) { #PH (empirical)
849            $format = 'variant';
850            require Image::ExifTool::FlashPix;
851            my $vPos = 0; # (necessary because ReadFPXValue updates this)
852            # read entry as a VT_VARIANT (use FlashPix module for this)
853            $val = Image::ExifTool::FlashPix::ReadFPXValue($et, \$val, $vPos,
854                   Image::ExifTool::FlashPix::VT_VARIANT(), $valLen, 1);
855        } else {
856            $format = "Unknown($valType)";
857        }
858        SetByteOrder('MM'); # back to native QuickTime byte ordering
859        push @vals, $val;
860        last if ++$i >= $count;
861        $valPos += $valLen + 6; # step to next value
862        last if $valPos > length($data);
863    }
864    return wantarray ? (\@vals, $format) : \@vals;
865}
866
867#------------------------------------------------------------------------------
868# Write a Microsoft Xtra value
869# Inputs: 0) ExifTool object ref, 1) tagInfo ref, 2) reference to list of values
870# Returns: new value binary data (or empty string)
871sub WriteXtraValue($$$)
872{
873    my ($et, $tagInfo, $vals) = @_;
874    my $format = $$tagInfo{Writable};
875    my $buff = '';
876    my $count = 0;
877    my $val;
878    foreach $val (@$vals) {
879        SetByteOrder('II');
880        my ($type, $dat);
881        if ($format eq 'Unicode') {
882            $dat = $et->Encode($val,'UCS2','II') . "\0\0";  # (must be null terminated)
883            $type = 8;
884        } elsif ($format eq 'int64u') {
885            if (Image::ExifTool::IsInt($val)) {
886                $dat = Set64u($val);
887                $type = 19;
888            }
889        } elsif ($format eq 'date') {
890            $dat = Image::ExifTool::GetUnixTime($val, 1);   # (convert to UTC, NC)
891            if ($dat) {
892                # 100ns intervals since Jan 1, 1601
893                $dat = Set64u(($dat + 11644473600) * 1e7);
894                $type = 21;
895            }
896        } elsif ($format eq 'vt_filetime') { # 'date' value inside a VT_VARIANT
897            $dat = Image::ExifTool::GetUnixTime($val);  # (leave as local time, NC)
898            if ($dat) {
899                # 100ns intervals since Jan 1, 1601
900                $dat = Set32u(64) . Set64u(($dat + 11644473600) * 1e7);
901                $type = 65;
902            }
903        } elsif ($format eq 'GUID') {
904            ($dat = $val) =~ tr/-//d;
905            if (length($dat) == 32) {
906                $dat = pack('VvvNN',unpack('NnnNN',pack('H*', $dat)));
907                $type = 72;
908            }
909        } else {
910            $et->WarnOnce("Error converting value for Microsoft:$$tagInfo{Name}");
911        }
912        SetByteOrder('MM');
913        if (defined $type) {
914            ++$count;
915            $buff .= Set32u(length($dat)+6) . Set16u($type) . $dat;
916        }
917    }
918    return $count ? Set32u($count) . $buff : '';
919}
920
921#------------------------------------------------------------------------------
922# Add new values to list
923# Inputs: 0) ExifTool ref, 1) new value list ref, 2) nvHash ref
924# Returns: true if something was added
925sub AddNewValues($$$)
926{
927    my ($et, $vals, $nvHash) = @_;
928    my @newVals = $et->GetNewValue($nvHash) or return undef;
929    if ($$et{OPTIONS}{Verbose} > 1) {
930        $et->VPrint(1, "  + Microsoft:$$nvHash{TagInfo}{Name} = $_\n") foreach @newVals;
931    }
932    push @$vals, @newVals;
933    return 1;
934}
935
936#------------------------------------------------------------------------------
937# Write tags to a Microsoft Xtra MP4 atom
938# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
939# Returns: Microsoft Xtra data block (may be empty if no Xtra data) or undef on error
940sub WriteXtra($$$)
941{
942    my ($et, $dirInfo, $tagTablePtr) = @_;
943    $et or return 1;      # allow dummy access
944
945    my $delGroup = ($$et{DEL_GROUP} and $$et{DEL_GROUP}{Microsoft});
946    my $newTags = $et->GetNewTagInfoHash($tagTablePtr);
947
948    return undef unless $delGroup or %$newTags;  # don't rewrite if nothing to do
949
950    my $dataPt = $$dirInfo{DataPt};
951    my $dataLen = length $$dataPt;
952    my $newData = '';
953    my $pos = 0;
954    my ($err, %done, $changed, $tag);
955
956    if ($delGroup) {
957        $changed = 1 if $dataLen;
958        my $empty = '';
959        $dataPt = $empty;
960        $dataLen = 0;
961    }
962    for (;;) {
963        last if $pos + 4 > $dataLen;
964        my $size = Get32u($dataPt, $pos); # (includes $size word)
965        ($size < 8 or $pos + $size > $dataLen) and $err=1, last;
966        my $tagLen = Get32u($dataPt, $pos + 4);
967        $tagLen + 18 > $size and $err=1, last;
968        $tag = substr($$dataPt, $pos + 8, $tagLen);
969        my @newVals;
970        while ($$newTags{$tag}) {
971            my $nvHash = $et->GetNewValueHash($$newTags{$tag});
972            $$nvHash{CreateOnly} and delete($$newTags{$tag}), last; # don't edit this tag
973            my $valPos = $pos + 8 + $tagLen;
974            my $valLen = $size - 8 - $tagLen;
975            my $val = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
976            foreach $val (@$val) {
977                my $overwrite = $et->IsOverwriting($nvHash, $val);
978                $overwrite or push(@newVals, $val), next;
979                $et->VPrint(1, "  - Microsoft:$$newTags{$tag}{Name} = $val\n");
980                next if $done{$tag};
981                $done{$tag} = 1;
982                AddNewValues($et, \@newVals, $nvHash);
983            }
984            # add to the end of the list if this was a List-type tag and we didn't delete anything
985            if (not $done{$tag} and $$newTags{$tag}{List}) {
986                AddNewValues($et, \@newVals, $nvHash) or last;
987                $done{$tag} = 1;
988            }
989            last;   # (it was a cheap goto)
990        }
991        if ($done{$tag}) {
992            $changed = 1;
993            # write changed values
994            my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
995            if (length $buff) {
996                $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
997            }
998        } else {
999            # nothing changed; just copy over
1000            $newData .= substr($$dataPt, $pos, $size);
1001        }
1002        $pos += $size;  # step to next entry
1003    }
1004    if ($err) {
1005        $et->Warn('Microsoft Xtra format error');
1006        return undef;
1007    }
1008    # add any new tags
1009    foreach $tag (sort keys %$newTags) {
1010        next if $done{$tag};
1011        my $nvHash = $et->GetNewValueHash($$newTags{$tag});
1012        next unless $$nvHash{IsCreating} and not $$nvHash{EditOnly};
1013        my @newVals;
1014        AddNewValues($et, \@newVals, $nvHash) or next;
1015        my $buff = WriteXtraValue($et, $$newTags{$tag}, \@newVals);
1016        if (length $buff) {
1017            $newData .= Set32u(8+length($tag)+length($buff)) . Set32u(length($tag)) . $tag . $buff;
1018            $changed = 1;
1019        }
1020    }
1021    if ($changed) {
1022        ++$$et{CHANGED};
1023    } else {
1024        undef $newData;
1025    }
1026    return $newData;
1027}
1028
1029#------------------------------------------------------------------------------
1030# Extract information from Xtra MP4 atom
1031# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
1032# Returns: 1 on success
1033# Reference: http://code.google.com/p/mp4v2/ [since removed from trunk]
1034sub ProcessXtra($$$)
1035{
1036    my ($et, $dirInfo, $tagTablePtr) = @_;
1037    my $dataPt = $$dirInfo{DataPt};
1038    my $dataPos = $$dirInfo{Base} || 0;
1039    my $dataLen = $$dirInfo{DataLen};
1040    my $pos = 0;
1041    $et->VerboseDir('Xtra', 0, $dataLen);
1042    for (;;) {
1043        last if $pos + 4 > $dataLen;
1044        my $size = Get32u($dataPt, $pos); # (includes $size word)
1045        last if $size < 8 or $pos + $size > $dataLen;
1046        my $tagLen = Get32u($dataPt, $pos + 4);
1047        last if $tagLen + 18 > $size;
1048        my $valLen = $size - 8 - $tagLen;
1049        if ($tagLen > 0 and $valLen > 0) {
1050            my $tag = substr($$dataPt, $pos + 8, $tagLen);
1051            my $valPos = $pos + 8 + $tagLen;
1052            my ($val, $format) = ReadXtraValue($et, substr($$dataPt, $valPos, $valLen));
1053            last unless defined $val;
1054            $val = $$val[0] if @$val == 1;
1055            my $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
1056            unless ($tagInfo) {
1057                # generate tag information for unrecognized tags
1058                my $name = $tag;
1059                $name =~ s{^WM/}{};
1060              # $name =~ tr/-_A-Za-z0-9//dc;
1061                if ($name =~ /^[-\w]+$/) {
1062                    $tagInfo = { Name => ucfirst($name) };
1063                    AddTagToTable($tagTablePtr, $tag, $tagInfo);
1064                    $et->VPrint(0, $$et{INDENT}, "[adding Microsoft:$tag]\n");
1065                }
1066            }
1067            my $count = ref $val ? scalar @$val : 1;
1068            $et->HandleTag($tagTablePtr, $tag, $val,
1069                TagInfo => $tagInfo,
1070                DataPt  => $dataPt,
1071                DataPos => $dataPos,
1072                Start   => $valPos,
1073                Size    => $valLen,
1074                Format  => $format,
1075                Extra   => " count=$count",
1076            );
1077        }
1078        $pos += $size;  # step to next entry
1079    }
1080    return 1;
1081}
1082
10831;  # end
1084
1085__END__
1086
1087=head1 NAME
1088
1089Image::ExifTool::Microsoft - Definitions for custom Microsoft tags
1090
1091=head1 SYNOPSIS
1092
1093This module is used by Image::ExifTool
1094
1095=head1 DESCRIPTION
1096
1097This module contains definitions required by Image::ExifTool to interpret
1098Microsoft-specific EXIF and XMP tags, and routines to read/write Microsoft
1099Xtra tags in videos.
1100
1101=head1 AUTHOR
1102
1103Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
1104
1105This library is free software; you can redistribute it and/or modify it
1106under the same terms as Perl itself.
1107
1108=head1 REFERENCES
1109
1110=over 4
1111
1112=item L<http://research.microsoft.com/en-us/um/redmond/groups/ivm/hdview/hdmetadataspec.htm>
1113
1114=back
1115
1116=head1 SEE ALSO
1117
1118L<Image::ExifTool::TagNames/Microsoft Tags>,
1119L<Image::ExifTool(3pm)|Image::ExifTool>
1120
1121=cut
1122
1123