source: main/trunk/greenstone2/perllib/cpan/Image/ExifTool/LNK.pm@ 34921

Last change on this file since 34921 was 34921, checked in by anupama, 3 years ago

Committing the improvements to EmbeddedMetaPlugin's processing of Keywords vs other metadata fields. Keywords were literally stored as arrays of words rather than phrases in PDFs (at least in Diego's sample PDF), whereas other meta fields like Subjects and Creators stored them as arrays of phrases. To get both to work, Kathy updated EXIF to a newer version, to retrieve the actual EXIF values stored in the PDF. And Kathy and Dr Bainbridge came up with a new option that I added called apply_join_before_split_to_metafields that's a regex which can list the metadata fields to apply the join_before_split to and whcih previously always got applied to all metadata fields. Now it's applied to any *Keywords metafields by default, as that's the metafield we have experience of that behaves differently to the others, as it stores by word instead of phrases. Tested on Diego's sample PDF. Diego has double-checked it to works on his sample PDF too, setting the split char to ; and turning on the join_before_split and leaving apply_join_before_split_to_metafields at its default of .*Keywords. File changes are strings.properties for the tooltip, the plugin introducing the option and working with it and Kathy's EXIF updates affecting cpan/File and cpan/Image.

  • Property svn:executable set to *
File size: 22.0 KB
Line 
1#------------------------------------------------------------------------------
2# File: LNK.pm
3#
4# Description: Read meta information from MS Shell Link files
5#
6# Revisions: 2009/09/19 - P. Harvey Created
7#
8# References: 1) http://msdn.microsoft.com/en-us/library/dd871305(PROT.10).aspx
9# 2) http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf
10#------------------------------------------------------------------------------
11
12package Image::ExifTool::LNK;
13
14use strict;
15use vars qw($VERSION);
16use Image::ExifTool qw(:DataAccess :Utils);
17
18$VERSION = '1.07';
19
20sub ProcessItemID($$$);
21sub ProcessLinkInfo($$$);
22
23# Information extracted from LNK (Windows Shortcut) files
24%Image::ExifTool::LNK::Main = (
25 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
26 GROUPS => { 2 => 'Other' },
27 VARS => { HEX_ID => 1 }, # print hex ID's in documentation
28 NOTES => 'Information extracted from MS Shell Link (Windows shortcut) files.',
29 # maybe the Flags aren't very useful to the user (since they are
30 # mainly structural), but extract them anyway for completeness
31 0x14 => {
32 Name => 'Flags',
33 Format => 'int32u',
34 PrintConv => { BITMASK => {
35 0 => 'IDList',
36 1 => 'LinkInfo',
37 2 => 'Description',
38 3 => 'RelativePath',
39 4 => 'WorkingDir',
40 5 => 'CommandArgs',
41 6 => 'IconFile',
42 7 => 'Unicode',
43 8 => 'NoLinkInfo',
44 9 => 'ExpString',
45 10 => 'SeparateProc',
46 12 => 'DarwinID',
47 13 => 'RunAsUser',
48 14 => 'ExpIcon',
49 15 => 'NoPidAlias',
50 17 => 'RunWithShim',
51 18 => 'NoLinkTrack',
52 19 => 'TargetMetadata',
53 20 => 'NoLinkPathTracking',
54 21 => 'NoKnownFolderTracking',
55 22 => 'NoKnownFolderAlias',
56 23 => 'LinkToLink',
57 24 => 'UnaliasOnSave',
58 25 => 'PreferEnvPath',
59 26 => 'KeepLocalIDList',
60 }},
61 },
62 0x18 => {
63 Name => 'FileAttributes',
64 Format => 'int32u',
65 PrintConv => { BITMASK => {
66 0 => 'Read-only',
67 1 => 'Hidden',
68 2 => 'System',
69 3 => 'Volume', #(not used)
70 4 => 'Directory',
71 5 => 'Archive',
72 6 => 'Encrypted?', #(ref 2, not used in XP)
73 7 => 'Normal',
74 8 => 'Temporary',
75 9 => 'Sparse',
76 10 => 'Reparse point',
77 11 => 'Compressed',
78 12 => 'Offline',
79 13 => 'Not indexed',
80 14 => 'Encrypted',
81 }},
82 },
83 0x1c => {
84 Name => 'CreateDate',
85 Format => 'int64u',
86 Groups => { 2 => 'Time' },
87 # convert time from 100-ns intervals since Jan 1, 1601
88 RawConv => '$val ? $val : undef',
89 ValueConv => '$val=$val/1e7-11644473600; ConvertUnixTime($val,1)',
90 PrintConv => '$self->ConvertDateTime($val)',
91 },
92 0x24 => {
93 Name => 'AccessDate',
94 Format => 'int64u',
95 Groups => { 2 => 'Time' },
96 RawConv => '$val ? $val : undef',
97 ValueConv => '$val=$val/1e7-11644473600; ConvertUnixTime($val,1)',
98 PrintConv => '$self->ConvertDateTime($val)',
99 },
100 0x2c => {
101 Name => 'ModifyDate',
102 Format => 'int64u',
103 Groups => { 2 => 'Time' },
104 RawConv => '$val ? $val : undef',
105 ValueConv => '$val=$val/1e7-11644473600; ConvertUnixTime($val,1)',
106 PrintConv => '$self->ConvertDateTime($val)',
107 },
108 0x34 => {
109 Name => 'TargetFileSize',
110 Format => 'int32u',
111 },
112 0x38 => {
113 Name => 'IconIndex',
114 Format => 'int32u',
115 PrintConv => '$val ? $val : "(none)"',
116 },
117 0x3c => {
118 Name => 'RunWindow',
119 Format => 'int32u',
120 PrintConv => {
121 0 => 'Hide',
122 1 => 'Normal',
123 2 => 'Show Minimized',
124 3 => 'Show Maximized',
125 4 => 'Show No Activate',
126 5 => 'Show',
127 6 => 'Minimized',
128 7 => 'Show Minimized No Activate',
129 8 => 'Show NA',
130 9 => 'Restore',
131 10 => 'Show Default',
132 },
133 },
134 0x40 => {
135 Name => 'HotKey',
136 Format => 'int32u',
137 PrintHex => 1,
138 PrintConv => {
139 OTHER => sub {
140 my $val = shift;
141 my $ch = $val & 0xff;
142 if (chr $ch =~ /^[A-Z0-9]$/) {
143 $ch = chr $ch;
144 } elsif ($ch >= 0x70 and $ch <= 0x87) {
145 $ch = 'F' . ($ch - 0x6f);
146 } elsif ($ch == 0x90) {
147 $ch = 'Num Lock';
148 } elsif ($ch == 0x91) {
149 $ch = 'Scroll Lock';
150 } else {
151 $ch = sprintf('Unknown (0x%x)', $ch);
152 }
153 $ch = "Alt-$ch" if $val & 0x400;
154 $ch = "Control-$ch" if $val & 0x200;
155 $ch = "Shift-$ch" if $val & 0x100;
156 return $ch;
157 },
158 0x00 => '(none)',
159 # these entries really only for documentation
160 0x90 => 'Num Lock',
161 0x91 => 'Scroll Lock',
162 "0x30'-'0x39" => "0-9",
163 "0x41'-'0x5a" => "A-Z",
164 "0x70'-'0x87" => "F1-F24",
165 0x100 => 'Shift',
166 0x200 => 'Control',
167 0x400 => 'Alt',
168 },
169 },
170 # note: tags 0x10xx are synthesized tag ID's
171 0x10000 => {
172 Name => 'ItemID',
173 SubDirectory => { TagTable => 'Image::ExifTool::LNK::ItemID' },
174 },
175 0x20000 => {
176 Name => 'LinkInfo',
177 SubDirectory => { TagTable => 'Image::ExifTool::LNK::LinkInfo' },
178 },
179 0x30004 => 'Description',
180 0x30008 => 'RelativePath',
181 0x30010 => 'WorkingDirectory',
182 0x30020 => 'CommandLineArguments',
183 0x30040 => 'IconFileName',
184 # note: tags 0xa000000x are actually ID's (not indices)
185 0xa0000000 => {
186 Name => 'UnknownData',
187 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
188 },
189 0xa0000001 => {
190 Name => 'EnvVarData',
191 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
192 },
193 0xa0000002 => {
194 Name => 'ConsoleData',
195 SubDirectory => { TagTable => 'Image::ExifTool::LNK::ConsoleData' },
196 },
197 0xa0000003 => {
198 Name => 'TrackerData',
199 SubDirectory => { TagTable => 'Image::ExifTool::LNK::TrackerData' },
200 },
201 0xa0000004 => {
202 Name => 'ConsoleFEData',
203 SubDirectory => { TagTable => 'Image::ExifTool::LNK::ConsoleFEData' },
204 },
205 0xa0000005 => {
206 Name => 'SpecialFolderData',
207 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
208 },
209 0xa0000006 => {
210 Name => 'DarwinData',
211 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
212 },
213 0xa0000007 => {
214 Name => 'IconEnvData',
215 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
216 },
217 0xa0000008 => {
218 Name => 'ShimData',
219 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
220 },
221 0xa0000009 => {
222 Name => 'PropertyStoreData',
223 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
224 },
225 0xa000000b => {
226 Name => 'KnownFolderData',
227 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
228 },
229 0xa000000c => {
230 Name => 'VistaIDListData',
231 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
232 },
233);
234
235%Image::ExifTool::LNK::ItemID = (
236 GROUPS => { 2 => 'Other' },
237 PROCESS_PROC => \&ProcessItemID,
238 # (can't find any documentation on these items)
239 0x0032 => {
240 Name => 'Item0032',
241 SubDirectory => { TagTable => 'Image::ExifTool::LNK::Item0032' },
242 },
243);
244
245%Image::ExifTool::LNK::Item0032 = (
246 GROUPS => { 2 => 'Other' },
247 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
248 0x0e => {
249 Name => 'TargetFileDOSName',
250 Format => 'var_string',
251 },
252 #not at a fixed offset -- offset is given by last 2 bytes of the item + 0x14
253 #0x22 => {
254 # Name => 'TargetFileName',
255 # Format => 'var_ustring',
256 #},
257);
258
259%Image::ExifTool::LNK::LinkInfo = (
260 GROUPS => { 2 => 'Other' },
261 PROCESS_PROC => \&ProcessLinkInfo,
262 FORMAT => 'int32u',
263 VARS => { NO_ID => 1 },
264 VolumeID => { },
265 DriveType => {
266 PrintConv => {
267 0 => 'Unknown',
268 1 => 'Invalid Root Path',
269 2 => 'Removable Media',
270 3 => 'Fixed Disk',
271 4 => 'Remote Drive',
272 5 => 'CD-ROM',
273 6 => 'Ram Disk',
274 },
275 },
276 DriveSerialNumber => { },
277 VolumeLabel => { },
278 LocalBasePath => { },
279 CommonNetworkRelLink => { },
280 CommonPathSuffix => { },
281 NetName => { },
282 DeviceName => { },
283 NetProviderType => {
284 PrintHex => 1,
285 PrintConv => {
286 0x1a0000 => 'AVID',
287 0x1b0000 => 'DOCUSPACE',
288 0x1c0000 => 'MANGOSOFT',
289 0x1d0000 => 'SERNET',
290 0x1e0000 => 'RIVERFRONT1',
291 0x1f0000 => 'RIVERFRONT2',
292 0x200000 => 'DECORB',
293 0x210000 => 'PROTSTOR',
294 0x220000 => 'FJ_REDIR',
295 0x230000 => 'DISTINCT',
296 0x240000 => 'TWINS',
297 0x250000 => 'RDR2SAMPLE',
298 0x260000 => 'CSC',
299 0x270000 => '3IN1',
300 0x290000 => 'EXTENDNET',
301 0x2a0000 => 'STAC',
302 0x2b0000 => 'FOXBAT',
303 0x2c0000 => 'YAHOO',
304 0x2d0000 => 'EXIFS',
305 0x2e0000 => 'DAV',
306 0x2f0000 => 'KNOWARE',
307 0x300000 => 'OBJECT_DIRE',
308 0x310000 => 'MASFAX',
309 0x320000 => 'HOB_NFS',
310 0x330000 => 'SHIVA',
311 0x340000 => 'IBMAL',
312 0x350000 => 'LOCK',
313 0x360000 => 'TERMSRV',
314 0x370000 => 'SRT',
315 0x380000 => 'QUINCY',
316 0x390000 => 'OPENAFS',
317 0x3a0000 => 'AVID1',
318 0x3b0000 => 'DFS',
319 },
320 },
321);
322
323%Image::ExifTool::LNK::UnknownData = (
324 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
325 GROUPS => { 2 => 'Other' },
326);
327
328%Image::ExifTool::LNK::ConsoleData = (
329 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
330 GROUPS => { 2 => 'Other' },
331 0x08 => {
332 Name => 'FillAttributes',
333 Format => 'int16u',
334 PrintConv => 'sprintf("0x%.2x", $val)',
335 },
336 0x0a => {
337 Name => 'PopupFillAttributes',
338 Format => 'int16u',
339 PrintConv => 'sprintf("0x%.2x", $val)',
340 },
341 0x0c => {
342 Name => 'ScreenBufferSize',
343 Format => 'int16u[2]',
344 PrintConv => '$val=~s/ / x /; $val',
345 },
346 0x10 => {
347 Name => 'WindowSize',
348 Format => 'int16u[2]',
349 PrintConv => '$val=~s/ / x /; $val',
350 },
351 0x14 => {
352 Name => 'WindowOrigin',
353 Format => 'int16u[2]',
354 PrintConv => '$val=~s/ / x /; $val',
355 },
356 0x20 => {
357 Name => 'FontSize',
358 Format => 'int16u[2]',
359 PrintConv => '$val=~s/ / x /; $val',
360 },
361 0x24 => {
362 Name => 'FontFamily',
363 Format => 'int32u',
364 PrintHex => 1,
365 PrintConv => {
366 0 => "Don't Care",
367 0x10 => 'Roman',
368 0x20 => 'Swiss',
369 0x30 => 'Modern',
370 0x40 => 'Script',
371 0x50 => 'Decorative',
372 },
373 },
374 0x28 => {
375 Name => 'FontWeight',
376 Format => 'int32u',
377 },
378 0x2c => {
379 Name => 'FontName',
380 Format => 'undef[64]',
381 RawConv => q{
382 $val = $self->Decode($val, 'UCS2');
383 $val =~ s/\0.*//s;
384 return length($val) ? $val : undef;
385 },
386 },
387 0x6c => {
388 Name => 'CursorSize',
389 Format => 'int32u',
390 },
391 0x70 => {
392 Name => 'FullScreen',
393 Format => 'int32u',
394 PrintConv => '$val ? "Yes" : "No"',
395 },
396 0x74 => { #PH (MISSING FROM MS DOCUMENTATION! -- screws up subsequent offsets)
397 Name => 'QuickEdit',
398 Format => 'int32u',
399 PrintConv => '$val ? "Yes" : "No"',
400 },
401 0x78 => {
402 Name => 'InsertMode',
403 Format => 'int32u',
404 PrintConv => '$val ? "Yes" : "No"',
405 },
406 0x7c => {
407 Name => 'WindowOriginAuto',
408 Format => 'int32u',
409 PrintConv => '$val ? "Yes" : "No"',
410 },
411 0x80 => {
412 Name => 'HistoryBufferSize',
413 Format => 'int32u',
414 },
415 0x84 => {
416 Name => 'NumHistoryBuffers',
417 Format => 'int32u',
418 },
419 0x88 => {
420 Name => 'RemoveHistoryDuplicates',
421 Format => 'int32u',
422 PrintConv => '$val ? "Yes" : "No"',
423 },
424);
425
426%Image::ExifTool::LNK::TrackerData = (
427 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
428 GROUPS => { 2 => 'Other' },
429 0x10 => {
430 Name => 'MachineID',
431 Format => 'var_string',
432 },
433);
434
435%Image::ExifTool::LNK::ConsoleFEData = (
436 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
437 GROUPS => { 2 => 'Other' },
438 0x08 => {
439 Name => 'CodePage',
440 Format => 'int32u',
441 },
442);
443
444#------------------------------------------------------------------------------
445# Extract null-terminated ASCII or Unicode string from buffer
446# Inputs: 0) buffer ref, 1) start position, 2) flag for unicode string
447# Return: string or undef if start position is outside bounds
448sub GetString($$;$)
449{
450 my ($dataPt, $pos, $unicode) = @_;
451 return undef if $pos >= length($$dataPt);
452 pos($$dataPt) = $pos;
453 return $1 if ($unicode ? $$dataPt=~/\G((?:..)*?)\0\0/sg : $$dataPt=~/\G(.*?)\0/sg);
454 return substr($$dataPt, $pos);
455}
456
457#------------------------------------------------------------------------------
458# Process item ID data
459# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
460# Returns: 1 on success
461sub ProcessItemID($$$)
462{
463 my ($et, $dirInfo, $tagTablePtr) = @_;
464 my $dataPt = $$dirInfo{DataPt};
465 my $dataLen = length $$dataPt;
466 my $pos = 0;
467 my %opts = (
468 DataPt => $dataPt,
469 DataPos => $$dirInfo{DataPos},
470 );
471 $et->VerboseDir('ItemID', undef, $dataLen);
472 for (;;) {
473 last if $pos + 4 >= $dataLen;
474 my $size = Get16u($dataPt, $pos);
475 last if $size < 2 or $pos + $size > $dataLen;
476 my $tag = Get16u($dataPt, $pos+2); # (just a guess -- may not be a tag at all)
477 AddTagToTable($tagTablePtr, $tag, {
478 Name => sprintf('Item%.4x', $tag),
479 SubDirectory => { TagTable => 'Image::ExifTool::LNK::UnknownData' },
480 }) unless $$tagTablePtr{$tag};
481 $et->HandleTag($tagTablePtr, $tag, undef, %opts, Start => $pos, Size => $size);
482 $pos += $size;
483 }
484}
485
486#------------------------------------------------------------------------------
487# Process link information data
488# Inputs: 0) ExifTool object reference, 1) dirInfo reference, 2) tag table ref
489# Returns: 1 on success
490sub ProcessLinkInfo($$$)
491{
492 my ($et, $dirInfo, $tagTablePtr) = @_;
493 my $dataPt = $$dirInfo{DataPt};
494 my $dataLen = length $$dataPt;
495 return 0 if $dataLen < 0x20;
496 my $hdrLen = Get32u($dataPt, 4);
497 my $lif = Get32u($dataPt, 8); # link info flags
498 my %opts = (
499 DataPt => $dataPt,
500 DataPos => $$dirInfo{DataPos},
501 Size => 4, # (typical value size)
502 );
503 my ($off, $unicode, $pos, $val, $size);
504 $et->VerboseDir('LinkInfo', undef, $dataLen);
505 if ($lif & 0x01) {
506 # read Volume ID
507 $off = Get32u($dataPt, 0x0c);
508 if ($off + 0x20 <= $dataLen) {
509 # my $len = Get32u($dataPt, $off);
510 $et->HandleTag($tagTablePtr, 'DriveType', undef, %opts, Start=>$off+4);
511 $pos = Get32u($dataPt, $off + 0x0c);
512 if ($pos == 0x14) {
513 # use VolumeLabelOffsetUnicode instead
514 $pos = Get32u($dataPt, $off + 0x10);
515 $unicode = 1;
516 }
517 $pos += $off;
518 $val = GetString($dataPt, $pos, $unicode);
519 if (defined $val) {
520 $size = length $val;
521 $val = $et->Decode($val, 'UCS2') if $unicode;
522 $et->HandleTag($tagTablePtr, 'VolumeLabel', $val, %opts, Start=>$pos, Size=>$size);
523 }
524 }
525 # read local base path
526 if ($hdrLen >= 0x24) {
527 $pos = Get32u($dataPt, 0x1c);
528 $unicode = 1;
529 } else {
530 $pos = Get32u($dataPt, 0x10);
531 undef $unicode;
532 }
533 $val = GetString($dataPt, $pos, $unicode);
534 if (defined $val) {
535 $size = length $val;
536 $val = $et->Decode($val, 'UCS2') if $unicode;
537 $et->HandleTag($tagTablePtr, 'LocalBasePath', $val, %opts, Start=>$pos, Size=>$size);
538 }
539 }
540 if ($lif & 0x02) {
541 # read common network relative link
542 $off = Get32u($dataPt, 0x14);
543 if ($off and $off + 0x14 <= $dataLen) {
544 my $siz = Get32u($dataPt, $off);
545 $pos = Get32u($dataPt, $off + 0x08);
546 if ($pos > 0x14 and $siz >= 0x18) {
547 $pos = Get32u($dataPt, $off + 0x14);
548 $unicode = 1;
549 } else {
550 undef $unicode;
551 }
552 $val = GetString($dataPt, $pos, $unicode);
553 if (defined $val) {
554 $size = length $val;
555 $val = $et->Decode($val, 'UCS2') if $unicode;
556 $et->HandleTag($tagTablePtr, 'NetName', $val, %opts, Start=>$pos, Size=>$size);
557 }
558 my $flg = Get32u($dataPt, $off + 0x04);
559 if ($flg & 0x01) {
560 $pos = Get32u($dataPt, $off + 0x0c);
561 if ($pos > 0x14 and $siz >= 0x1c) {
562 $pos = Get32u($dataPt, $off + 0x18);
563 $unicode = 1;
564 } else {
565 undef $unicode;
566 }
567 $val = GetString($dataPt, $pos, $unicode);
568 if (defined $val) {
569 $size = length $val;
570 $val = $et->Decode($val, 'UCS2') if $unicode;
571 $et->HandleTag($tagTablePtr, 'DeviceName', $val, %opts, Start=>$pos, Size=>$size);
572 }
573 }
574 if ($flg & 0x02) {
575 $val = Get32u($dataPt, $off + 0x10);
576 $et->HandleTag($tagTablePtr, 'NetProviderType', $val, %opts, Start=>$off + 0x10);
577 }
578 }
579 }
580 return 1;
581}
582
583#------------------------------------------------------------------------------
584# Extract information from a MS Shell Link (Windows shortcut) file
585# Inputs: 0) ExifTool object reference, 1) dirInfo reference
586# Returns: 1 on success, 0 if this wasn't a valid LNK file
587sub ProcessLNK($$)
588{
589 my ($et, $dirInfo) = @_;
590 my $raf = $$dirInfo{RAF};
591 my ($buff, $buf2, $len, $i);
592
593 # read LNK file header
594 $raf->Read($buff, 0x4c) == 0x4c or return 0;
595 $buff =~ /^.{4}\x01\x14\x02\0{5}\xc0\0{6}\x46/s or return 0;
596 $len = unpack('V', $buff);
597 $len >= 0x4c or return 0;
598 if ($len > 0x4c) {
599 $raf->Read($buf2, $len - 0x4c) == $len - 0x4c or return 0;
600 $buff .= $buf2;
601 }
602 $et->SetFileType();
603 SetByteOrder('II');
604
605 my $tagTablePtr = GetTagTable('Image::ExifTool::LNK::Main');
606 my %dirInfo = (
607 DataPt => \$buff,
608 DataPos => 0,
609 DataLen => length $buff,
610 DirLen => length $buff,
611 );
612 $et->ProcessDirectory(\%dirInfo, $tagTablePtr);
613
614 my $flags = Get32u(\$buff, 0x14);
615
616 # read link target ID list
617 if ($flags & 0x01) {
618 $raf->Read($buff, 2) or return 1;
619 $len = unpack('v', $buff);
620 $raf->Read($buff, $len) == $len or return 1;
621 $et->HandleTag($tagTablePtr, 0x10000, undef,
622 DataPt => \$buff,
623 DataPos => $raf->Tell() - $len,
624 Size => $len,
625 );
626 }
627
628 # read link information
629 if ($flags & 0x02) {
630 $raf->Read($buff, 4) or return 1;
631 $len = unpack('V', $buff);
632 return 1 if $len < 4;
633 $raf->Read($buf2, $len - 4) == $len - 4 or return 1;
634 $buff .= $buf2;
635 $et->HandleTag($tagTablePtr, 0x20000, undef,
636 DataPt => \$buff,
637 DataPos => $raf->Tell() - $len,
638 Size => $len,
639 );
640 }
641
642 # read string data
643 my @strings = qw(Description RelativePath WorkingDirectory
644 CommandLineArguments IconFileName);
645 for ($i=0; $i<@strings; ++$i) {
646 my $mask = 0x04 << $i;
647 next unless $flags & $mask;
648 $raf->Read($buff, 2) or return 1;
649 $len = unpack('v', $buff);
650 $len *= 2 if $flags & 0x80; # characters are 2 bytes if Unicode flag is set
651 $raf->Read($buff, $len) or return 1;
652 my $val;
653 $val = $et->Decode($buff, 'UCS2') if $flags & 0x80;
654 $et->HandleTag($tagTablePtr, 0x30000 | $mask, $val,
655 DataPt => \$buff,
656 DataPos => $raf->Tell() - $len,
657 Size => $len,
658 );
659 }
660
661 # read extra data
662 while ($raf->Read($buff, 4) == 4) {
663 $len = unpack('V', $buff);
664 last if $len < 4;
665 $len -= 4;
666 $raf->Read($buf2, $len) == $len or last;
667 next unless $len > 4;
668 $buff .= $buf2;
669 my $tag = Get32u(\$buff, 4);
670 my $tagInfo = $$tagTablePtr{$tag};
671 unless (ref $tagInfo eq 'HASH' and $$tagInfo{SubDirectory}) {
672 $tagInfo = $$tagTablePtr{0xa0000000};
673 }
674 $et->HandleTag($tagTablePtr, $tag, undef,
675 DataPt => \$buff,
676 DataPos => $raf->Tell() - $len - 4,
677 TagInfo => $tagInfo,
678 );
679 }
680 return 1;
681}
682
6831; # end
684
685__END__
686
687=head1 NAME
688
689Image::ExifTool::LNK - Read MS Shell Link (.LNK) meta information
690
691=head1 SYNOPSIS
692
693This module is used by Image::ExifTool
694
695=head1 DESCRIPTION
696
697This module contains definitions required by Image::ExifTool to extract meta
698information MS Shell Link (Windows shortcut) files.
699
700=head1 AUTHOR
701
702Copyright 2003-2021, Phil Harvey (philharvey66 at gmail.com)
703
704This library is free software; you can redistribute it and/or modify it
705under the same terms as Perl itself.
706
707=head1 REFERENCES
708
709=over 4
710
711=item L<http://msdn.microsoft.com/en-us/library/dd871305(PROT.10).aspx>
712
713=item L<http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf>
714
715=back
716
717=head1 SEE ALSO
718
719L<Image::ExifTool::TagNames/LNK Tags>,
720L<Image::ExifTool(3pm)|Image::ExifTool>
721
722=cut
723
Note: See TracBrowser for help on using the repository browser.