source: gsdl/trunk/perllib/cpan/Image/ExifTool/BuildTagLookup.pm@ 16842

Last change on this file since 16842 was 16842, checked in by davidb, 16 years ago

ExifTool added to cpan area to support metadata extraction from files such as JPEG. Primarily targetted as Image files (hence the Image folder name decided upon by the ExifTool author) it also can handle video such as flash and audio such as Wav

File size: 61.8 KB
Line 
1#------------------------------------------------------------------------------
2# File: BuildTagLookup.pm
3#
4# Description: Utility to build tag lookup tables in Image::ExifTool::TagLookup.pm
5#
6# Revisions: 12/31/2004 - P. Harvey Created
7# 02/15/2005 - PH Added ability to generate TagNames documentation
8#
9# Notes: Documentation for the tag tables may either be placed in the
10# %docs hash below or in a NOTES entry in the table itself, and
11# individual tags may have their own Notes entry.
12#------------------------------------------------------------------------------
13
14package Image::ExifTool::BuildTagLookup;
15
16use strict;
17require Exporter;
18
19BEGIN {
20 # prevent ExifTool from loading the user config file
21 $Image::ExifTool::noConfig = 1;
22}
23
24use vars qw($VERSION @ISA);
25use Image::ExifTool qw(:Utils :Vars);
26use Image::ExifTool::Shortcuts;
27use Image::ExifTool::HTML qw(EscapeHTML);
28use Image::ExifTool::IPTC;
29use Image::ExifTool::Canon;
30use Image::ExifTool::Nikon;
31
32$VERSION = '1.59';
33@ISA = qw(Exporter);
34
35sub NumbersFirst;
36
37# colors for html pages
38my $noteFont = "<span class=n>";
39my $noteFontSmall = "<span class='n s'>";
40
41my $docType = q{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
42 "http://www.w3.org/TR/html4/loose.dtd">
43};
44
45
46my $caseInsensitive; # flag to ignore case when sorting tag names
47
48# Descriptions for the TagNames documentation
49# Note: POD headers in these descriptions start with '~' instead of '=' to keep
50# from confusing POD parsers which apparently parse inside quoted strings.
51my %docs = (
52 PodHeader => q{
53~head1 NAME
54
55Image::ExifTool::TagNames - ExifTool tag name documentation
56
57~head1 DESCRIPTION
58
59This document contains a complete list of ExifTool tag names, organized into
60tables based on information type. Tag names are used to indicate the
61specific meta information that is extracted or written in an image.
62
63~head1 TAG TABLES
64},
65 ExifTool => q{
66The tables listed below give the names of all tags recognized by ExifTool.
67},
68 ExifTool2 => q{
69B<Tag ID>, B<Index> or B<Sequence> is given in the first column of each
70table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
71is the identifier that is actually stored in the file. An B<Index> refers
72to the location of a value when found at a fixed position within a data
73block, and B<Sequence> gives the order of values for a serial data stream.
74
75A B<Tag Name> is the handle by which the information is accessed in
76ExifTool. In some instances, more than one name may correspond to a single
77tag ID. In these cases, the actual name used depends on the context in
78which the information is found. Case is not significant for tag names. A
79question mark after a tag name indicates that the information is either not
80understood, not verified, or not very useful -- these tags are not extracted
81by ExifTool unless the Unknown (-u) option is enabled. Be aware that some
82tag names are different than the descriptions printed out by default when
83extracting information with exiftool. To see the tag names instead of the
84descriptions, use C<exiftool -s>.
85
86The B<Writable> column indicates whether the tag is writable by ExifTool.
87Anything but an C<N> in this column means the tag is writable. A C<Y>
88indicates writable information that is either unformatted or written using
89the existing format. Other expressions give details about the information
90format, and vary depending on the general type of information. The format
91name may be followed by a number in square brackets to indicate the number
92of values written, or the number of characters in a fixed-length string
93(including a null terminator which is added if required).
94
95An asterisk (C<*>) after an entry in the B<Writable> column indicates a
96"protected" tag which is not writable directly, but is set via a Composite
97tag. A tilde (C<~>) indicates a tag this is only writable when print
98conversion is disabled (by setting PrintConv to 0, or using the -n option).
99A slash (C</>) indicates an "avoided" tag that is not created unless the
100group is specified (due to name conflicts with other tags). An exclamation
101point (C<!>) indicates a tag that is considered unsafe to write under normal
102circumstances. These "unsafe" tags are not set when calling
103SetNewValuesFromFile() or when using the exiftool -TagsFromFile option
104unless specified explicitly, and care should be taken when editing them
105manually since they may affect the way an image is rendered. A plus sign
106(C<+>) indicates a "list" tag which supports multiple instances.
107
108The HTML version of these tables also list possible B<Values> for
109discrete-valued tags, as well as B<Notes> for some tags.
110
111B<Note>: If you are familiar with common meta-information tag names, you may
112find that some ExifTool tag names are different than expected. The usual
113reason for this is to make the tag names more consistent across different
114types of meta information. To determine a tag name, either consult this
115documentation or run C<exiftool -s> on a file containing the information in
116question.
117},
118 EXIF => q{
119EXIF stands for "Exchangeable Image File Format". This type of information
120is formatted according to the TIFF specification, and may be found in JPG,
121TIFF, PNG, MIFF and WDP images, as well as many TIFF-based RAW images.
122
123The EXIF meta information is organized into different Image File Directories
124(IFD's) within an image. The names of these IFD's correspond to the
125ExifTool family 1 group names. When writing EXIF information, the default
126B<Group> listed below is used unless another group is specified.
127
128Also listed in the table below are TIFF, DNG, WDP and other tags which are
129not part of the EXIF specification, but may co-exist with EXIF tags in some
130images.
131},
132 GPS => q{
133These GPS tags are part of the EXIF standard, and are stored in a separate
134IFD within the EXIF information.
135
136ExifTool is very flexible about the input format when writing lat/long
137coordinates, and will accept from 1 to 3 floating point numbers (for decimal
138degrees, degrees and minutes, or degrees, minutes and seconds) separated by
139just about anything, and will format them properly according to the EXIF
140specification.
141
142Some GPS tags have values which are fixed-length strings. For these, the
143indicated string lengths include a null terminator which is added
144automatically by ExifTool. Remember that the descriptive values are used
145when writing (ie. 'Above Sea Level', not '0') unless the print conversion is
146disabled (with '-n' on the command line, or the PrintConv option in the
147API).
148},
149 XMP => q{
150XMP stands for "Extensible Metadata Platform", an XML/RDF-based metadata
151format which is being pushed by Adobe. Information in this format can be
152embedded in many different image file types including JPG, JP2, TIFF, PNG,
153MIFF, PS, PDF, PSD and DNG, as well as audio file formats supporting ID3v2
154information.
155
156The XMP B<Tag ID>'s aren't listed because in most cases they are identical
157to the B<Tag Name>.
158
159All XMP information is stored as character strings. The B<Writable> column
160specifies the information format: C<integer> is a string of digits
161(possibly beginning with a '+' or '-'), C<real> is a floating point number,
162C<rational> is two C<integer> strings separated by a '/' character, C<date>
163is a date/time string in the format "YYYY:MM:DD HH:MM:SS[+/-HH:MM]",
164C<boolean> is either "True" or "False", and C<lang-alt> is a list of string
165alternatives in different languages.
166
167Individual languages for C<lang-alt> tags are accessed by suffixing the tag
168name with a '-', followed by an RFC 3066 language code (ie. "XMP:Title-fr",
169or "Rights-en-US"). A C<lang-alt> tag with no language code accesses the
170"x-default" language, but causes other languages to be deleted when writing.
171The "x-default" language code may be specified when writing a new value to
172write only the default language, but note that all languages are still
173deleted if "x-default" tag is deleted. When reading, "x-default" is not
174specified.
175
176The XMP tags are organized according to schema B<Namespace> in the following
177tables. Note that a few of the longer namespace prefixes given below have
178been shortened for convenience (since the family 1 group names are derived
179from these by adding a leading "XMP-"). In cases where a tag name exists in
180more than one namespace, less common namespaces are avoided when writing.
181However, any namespace may be written by specifying a family 1 group name
182for the tag, ie) XMP-exif:Contrast or XMP-crs:Contrast.
183
184ExifTool will extract XMP information even if it is not listed in these
185tables. For example, the C<pdfx> namespace doesn't have a predefined set of
186tag names because it is used to store application-defined PDF information,
187but this information is extracted by ExifTool anyway.
188},
189 IPTC => q{
190IPTC stands for "International Press Telecommunications Council". This is
191an older meta information format that is slowly being phased out in favor of
192XMP. IPTC information may be embedded in JPG, TIFF, PNG, MIFF, PS, PDF, PSD
193and DNG images.
194
195The IPTC specification dictates a length for ASCII (C<string> or C<digits>)
196values. These lengths are given in square brackets after the B<Writable>
197format name. For tags where a range of lengths is allowed, the minimum and
198maximum lengths are separated by a comma within the brackets. IPTC strings
199are not null terminated.
200
201IPTC information is separated into different records, each of which has its
202own set of tags.
203},
204 Photoshop => q{
205Photoshop tags are found in PSD files, as well as inside embedded Photoshop
206information in many other file types (JPEG, TIFF, PDF, PNG to name a few).
207
208Many Photoshop tags are marked as Unknown (indicated by a question mark
209after the tag name) because the information they provide is not very useful
210under normal circumstances (and because Adobe denied my application for
211their file format documentation -- apparently open source software is too
212big a concept for them). These unknown tags are not extracted unless the
213Unknown (-u) option is used.
214},
215 PrintIM => q{
216The format of the PrintIM information is known, however no PrintIM tags have
217been decoded. Use the Unknown (-u) option to extract PrintIM information.
218},
219 Kodak => q{
220The Kodak maker notes aren't in standard IFD format, and the format varies
221frequently with different models. Some information has been decoded, but
222much of the Kodak information remains unknown.
223},
224 'Kodak SpecialEffects' => q{
225The Kodak SpecialEffects and Borders tags are found in sub-IFD's within the
226Kodak JPEG APP3 "Meta" segment.
227},
228 Minolta => q{
229These tags are used by Minolta and Konica/Minolta cameras. Minolta doesn't
230make things easy for decoders because the meaning of some tags and the
231location where some information is stored is different for different camera
232models. (Take MinoltaQuality for example, which may be located in 5
233different places.)
234},
235 Olympus => q{
236Tags 0x0000 through 0x0103 are used by some older Olympus cameras, and are
237the same as Konica/Minolta tags. The Olympus tags are also used for Epson
238and Agfa cameras.
239},
240 Panasonic => q{
241Panasonic tags are also used for Leica cameras.
242},
243 Pentax => q{
244The Pentax tags are also used in Asahi cameras.
245},
246 Sigma => q{
247These tags are used in Sigma/Foveon cameras.
248},
249 Sony => q{
250The maker notes in images from current Sony camera models contain a wealth
251of information, but very little is known about these tags. Use the ExifTool
252Unknown (-u) or Verbose (-v) options to see information about the unknown
253tags.
254},
255 CanonRaw => q{
256These tags apply to CRW-format Canon RAW files and information in the APP0
257"CIFF" segment of JPEG images. When writing CanonRaw/CIFF information, the
258length of the information is preserved (and the new information is truncated
259or padded as required) unless B<Writable> is C<resize>. Currently, only
260JpgFromRaw and ThumbnailImage are allowed to change size.
261},
262 Unknown => q{
263The following tags are decoded in unsupported maker notes. Use the Unknown
264(-u) option to display other unknown tags.
265},
266 PDF => q{
267The tags listed in the PDF tables below are those which are used by ExifTool
268to extract meta information, but they are only a small fraction of the total
269number of available PDF tags.
270},
271 DNG => q{
272The main DNG tags are found in the EXIF table. The tables below define only
273information found within structures of these main DNG tag values.
274},
275 MPEG => q{
276The MPEG format doesn't specify any file-level meta information. In lieu of
277this, information is extracted from the first audio and video frame headers
278in the file.
279},
280 Real => q{
281ExifTool recognizes three basic types of Real audio/video files: 1)
282RealMedia (RM, RV and RMVB), 2) RealAudio (RA), and 3) Real Metafile (RAM
283and RPM).
284},
285 Extra => q{
286The extra tags represent information found in the image but not associated
287with any other tag group. The three writable "pseudo" tags (Filename,
288Directory and FileModifyDate) may be written without the need to rewrite the
289file since their values are not contained within the file data.
290},
291 Composite => q{
292The values of the composite tags are derived from the values of other tags.
293These are convenience tags which are calculated after all other information
294is extracted.
295},
296 Shortcuts => q{
297Shortcut tags are convenience tags that represent one or more other tag
298names. They are used like regular tags to read and write the information
299for a specified set of tags.
300
301The shortcut tags below have been pre-defined, but user-defined shortcuts
302may be added via the %Image::ExifTool::Shortcuts::UserDefined lookup in the
303~/.ExifTool_config file. See the Image::ExifTool::Shortcuts documentation
304for more details.
305},
306 PodTrailer => q{
307~head1 NOTES
308
309This document generated automatically by
310L<Image::ExifTool::BuildTagLookup|Image::ExifTool::BuildTagLookup>.
311
312~head1 AUTHOR
313
314Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
315
316This library is free software; you can redistribute it and/or modify it
317under the same terms as Perl itself.
318
319~head1 SEE ALSO
320
321L<Image::ExifTool(3pm)|Image::ExifTool>
322
323~cut
324},
325);
326
327
328#------------------------------------------------------------------------------
329# New - create new BuildTagLookup object
330# Inputs: 0) reference to BuildTagLookup object or BuildTagLookup class name
331sub new
332{
333 local $_;
334 my $that = shift;
335 my $class = ref($that) || $that || 'Image::ExifTool::BuildTagLookup';
336 my $self = bless {}, $class;
337 my (%subdirs, %isShortcut);
338 my %count = (
339 'unique tag names' => 0,
340 'total tags' => 0,
341 );
342#
343# loop through all tables, accumulating TagLookup and TagName information
344#
345 my (%tagNameInfo, %id, %longID, %longName, %shortName, %tableNum,
346 %tagLookup, %tagExists, %tableWritable, %sepTable, %compositeModules);
347 $self->{TAG_NAME_INFO} = \%tagNameInfo;
348 $self->{TAG_ID} = \%id;
349 $self->{LONG_ID} = \%longID;
350 $self->{LONG_NAME} = \%longName;
351 $self->{SHORT_NAME} = \%shortName;
352 $self->{TABLE_NUM} = \%tableNum;
353 $self->{TAG_LOOKUP} = \%tagLookup;
354 $self->{TAG_EXISTS} = \%tagExists;
355 $self->{TABLE_WRITABLE} = \%tableWritable;
356 $self->{SEPARATE_TABLE} = \%sepTable;
357 $self->{COMPOSITE_MODULES} = \%compositeModules;
358 $self->{COUNT} = \%count;
359
360 Image::ExifTool::LoadAllTables();
361 my @tableNames = sort keys %allTables;
362 push @tableNames, 'Image::ExifTool::Shortcuts::Main'; # add Shortcuts last
363 my $tableNum = 0;
364 my $tableName;
365 # create lookup for short table names
366 foreach $tableName (@tableNames) {
367 my $short = $tableName;
368 $short =~ s/^Image::ExifTool:://;
369 $short =~ s/::Main$//;
370 $short =~ s/::/ /;
371 $short =~ s/(.*)Tags$/\u$1/;
372 $short =~ s/^Exif\b/EXIF/;
373 $shortName{$tableName} = $short; # remember short name
374 $tableNum{$tableName} = $tableNum++;
375 }
376 # make lookup table to check for shortcut tags
377 my $tag;
378 foreach $tag (keys %Image::ExifTool::Shortcuts::Main) {
379 my $entry = $Image::ExifTool::Shortcuts::Main{$tag};
380 # ignore if shortcut tag name includes itself
381 next if ref $entry eq 'ARRAY' and grep /^$tag$/, @$entry;
382 $isShortcut{lc($tag)} = 1;
383 }
384 foreach $tableName (@tableNames) {
385 # create short table name
386 my $short = $shortName{$tableName};
387 my $info = $tagNameInfo{$tableName} = [ ];
388 my ($table, $shortcut);
389 if ($short eq 'Shortcuts') {
390 # can't use GetTagTable() for Shortcuts (not a normal table)
391 $table = \%Image::ExifTool::Shortcuts::Main;
392 $shortcut = 1;
393 } else {
394 $table = GetTagTable($tableName);
395 }
396 my $tableNum = $tableNum{$tableName};
397 my $writeProc = $table->{WRITE_PROC};
398 $longID{$tableName} = 0;
399 $longName{$tableName} = 0;
400 # save all tag names
401 my ($tagID, $binaryTable, $noID, $isIPTC);
402 $isIPTC = 1 if $$table{WRITE_PROC} and $$table{WRITE_PROC} eq \&Image::ExifTool::IPTC::WriteIPTC;
403 $noID = 1 if $short =~ /^(Composite|XMP|Extra|Shortcuts|ASF.*)$/ or $table->{VARS}->{NO_ID};
404 if (($table->{VARS} and $table->{VARS}->{INDEX}) or
405 ($table->{PROCESS_PROC} and
406 $table->{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData))
407 {
408 $binaryTable = 1;
409 $id{$tableName} = 'Index';
410 } elsif ($table->{PROCESS_PROC} and
411 $table->{PROCESS_PROC} eq \&Image::ExifTool::Canon::ProcessSerialData)
412 {
413 $binaryTable = 1;
414 $id{$tableName} = 'Sequence';
415 } elsif ($isIPTC and $$table{PROCESS_PROC}) { #only the main IPTC table has a PROCESS_PROC
416 $id{$tableName} = 'Record';
417 } elsif (not $noID) {
418 $id{$tableName} = 'Tag ID';
419 }
420 $caseInsensitive = ($tableName =~ /::XMP::/);
421 my @keys = sort NumbersFirst TagTableKeys($table);
422 my $defFormat = $table->{FORMAT};
423 if (not $defFormat and $table->{PROCESS_PROC} and
424 $table->{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData)
425 {
426 $defFormat = 'int8u'; # use default format for binary data tables
427 }
428TagID: foreach $tagID (@keys) {
429 my ($tagInfo, @tagNames, $subdir, $format, @values);
430 my (@infoArray, @require, @writeGroup, @writable);
431 if ($shortcut) {
432 # must build a dummy tagInfo list since Shortcuts is not a normal table
433 $tagInfo = { Name => $tagID, Writable => 1, Require => { } };
434 my $i;
435 for ($i=0; $i<@{$$table{$tagID}}; ++$i) {
436 $tagInfo->{Require}->{$i} = $table->{$tagID}->[$i];
437 }
438 @infoArray = ( $tagInfo );
439 } else {
440 @infoArray = GetTagInfoList($table,$tagID);
441 }
442 $format = $defFormat;
443 foreach $tagInfo (@infoArray) {
444 if ($$tagInfo{Notes}) {
445 my $note = $$tagInfo{Notes};
446 # remove leading/trailing blank lines
447 $note =~ s/(^\s+|\s+$)//g;
448 # remove leading/trailing spaces on each line
449 $note =~ s/(^[ \t]+|[ \t]+$)//mg;
450 push @values, "($note)";
451 }
452 my $writable;
453 if (defined $$tagInfo{Writable}) {
454 $writable = $$tagInfo{Writable};
455 } elsif (not $$tagInfo{SubDirectory}) {
456 $writable = $$table{WRITABLE};
457 }
458 my $writeGroup;
459 $writeGroup = $$tagInfo{WriteGroup};
460 unless ($writeGroup) {
461 $writeGroup = $$table{WRITE_GROUP} if $writable;
462 $writeGroup = '-' unless $writeGroup;
463 }
464 $format = $$tagInfo{Format} if defined $$tagInfo{Format};
465 if ($$tagInfo{SubDirectory}) {
466 # don't show XMP structure tags
467 next TagID if $short =~ /^XMP /;
468 $subdir = 1;
469 my $subTable = $tagInfo->{SubDirectory}->{TagTable} || $tableName;
470 push @values, $shortName{$subTable}
471 } else {
472 $subdir = 0;
473 }
474 my $type;
475 foreach $type ('Require','Desire') {
476 my $require = $$tagInfo{$type};
477 if ($require) {
478 foreach (sort { $a <=> $b } keys %$require) {
479 push @require, $$require{$_};
480 }
481 }
482 }
483 my $printConv = $$tagInfo{PrintConv};
484 if (ref($printConv) =~ /^(HASH|ARRAY)$/) {
485 my (@printConvList, @indexList, $index);
486 if (ref $printConv eq 'ARRAY') {
487 for ($index=0; $index<@$printConv; ++$index) {
488 next if ref $$printConv[$index] ne 'HASH';
489 push @printConvList, $$printConv[$index];
490 push @indexList, $index;
491 }
492 $printConv = shift @printConvList;
493 $index = shift @indexList;
494 }
495 while (defined $printConv) {
496 push @values, "[Value $index]" if defined $index;
497 if ($$tagInfo{SeparateTable}) {
498 $subdir = 1;
499 my $s = $$tagInfo{SeparateTable};
500 $s = $$tagInfo{Name} if $s eq '1';
501 # add module name if not specified
502 $s =~ / / or ($short =~ /^(\w+)/ and $s = "$1 $s");
503 push @values, $s;
504 $sepTable{$s} = $printConv;
505 # add PrintHex flag to PrintConv so we can check it later
506 $$printConv{PrintHex} = 1 if $$tagInfo{PrintHex};
507 } else {
508 $caseInsensitive = 0;
509 my @pk = sort NumbersFirst keys %$printConv;
510 my $bits;
511 foreach (@pk) {
512 next if $_ eq '';
513 $_ eq 'BITMASK' and $bits = $$printConv{$_}, next;
514 my $index;
515 if ($$tagInfo{PrintHex}) {
516 $index = sprintf('0x%x',$_);
517 } elsif (/^[-+]?\d+$/) {
518 $index = $_;
519 } else {
520 # ignore unprintable values
521 next if /[\x00-\x1f\x80-\xff]/;
522 $index = "'$_'";
523 }
524 push @values, "$index = " . $$printConv{$_};
525 }
526 if ($bits) {
527 my @pk = sort NumbersFirst keys %$bits;
528 foreach (@pk) {
529 push @values, "Bit $_ = " . $$bits{$_};
530 }
531 }
532 }
533 last unless @printConvList;
534 $printConv = shift @printConvList;
535 $index = shift @indexList;
536 }
537 } elsif ($printConv and $printConv =~ /DecodeBits\(\$val,\s*(\{.*\})\s*\)/s) {
538 my $bits = eval $1;
539 if ($@) {
540 warn $@;
541 } else {
542 my @pk = sort NumbersFirst keys %$bits;
543 foreach (@pk) {
544 push @values, "Bit $_ = " . $$bits{$_};
545 }
546 }
547 }
548 if ($subdir and not $$tagInfo{SeparateTable}) {
549 # subdirectories are only writable if specified explicitly
550 $writable = '-' . ($$tagInfo{Writable} ? $writable : '');
551 } else {
552 # not writable if we can't do the inverse conversions
553 my $noPrintConvInv;
554 if ($writable) {
555 foreach ('PrintConv','ValueConv') {
556 next unless $$tagInfo{$_};
557 next if $$tagInfo{$_ . 'Inv'};
558 next if ref($$tagInfo{$_}) =~ /^(HASH|ARRAY)$/;
559 next if $$tagInfo{WriteAlso};
560 if ($_ eq 'ValueConv') {
561 undef $writable;
562 } else {
563 $noPrintConvInv = 1;
564 }
565 }
566 }
567 if (not $writable) {
568 $writable = 'N';
569 } else {
570 $writable eq '1' and $writable = $format ? $format : 'Y';
571 my $count = $$tagInfo{Count} || 1;
572 # adjust count to Writable size if different than Format
573 if ($writable and $format and $writable ne $format and
574 $Image::ExifTool::Exif::formatNumber{$writable} and
575 $Image::ExifTool::Exif::formatNumber{$format})
576 {
577 my $n1 = $Image::ExifTool::Exif::formatNumber{$format};
578 my $n2 = $Image::ExifTool::Exif::formatNumber{$writable};
579 $count *= $Image::ExifTool::Exif::formatSize[$n1] /
580 $Image::ExifTool::Exif::formatSize[$n2];
581 }
582 if ($count != 1) {
583 $count = 'n' if $count < 0;
584 $writable .= "[$count]";
585 }
586 $writable .= '~' if $noPrintConvInv;
587 # add a '*' if this tag is protected or a '~' for unsafe tags
588 if ($$tagInfo{Protected}) {
589 $writable .= '*' if $$tagInfo{Protected} & 0x02;
590 $writable .= '!' if $$tagInfo{Protected} & 0x01;
591 }
592 $writable .= '/' if $$tagInfo{Avoid};
593 }
594 $writable .= '+' if $$tagInfo{List};
595 # Œseparate tables link like subdirectories (flagged with leading '-')
596 $writable = "-$writable" if $subdir;
597 }
598 # don't duplicate a tag name unless an entry is different
599 my $name = $$tagInfo{Name};
600 my $lcName = lc($name);
601 # check for conflicts with shortcut names
602 if ($isShortcut{$lcName} and $short ne 'Shortcuts' and
603 ($$tagInfo{Writable} or not $$tagInfo{SubDirectory}))
604 {
605 warn "WARNING: $short $name is a shortcut tag!\n";
606 }
607 $name .= '?' if $$tagInfo{Unknown};
608 unless (@tagNames and $tagNames[-1] eq $name and
609 $writeGroup[-1] eq $writeGroup and $writable[-1] eq $writable)
610 {
611 push @tagNames, $name;
612 push @writeGroup, $writeGroup;
613 push @writable, $writable;
614 }
615#
616# add this tag to the tag lookup unless PROCESS_PROC is 0 or shortcut tag
617#
618 next if $shortcut or (defined $$table{PROCESS_PROC} and not $$table{PROCESS_PROC});
619 # count our tags
620 if ($$tagInfo{SubDirectory}) {
621 $subdirs{$lcName} or $subdirs{$lcName} = 0;
622 ++$subdirs{$lcName};
623 } else {
624 ++$count{'total tags'};
625 unless ($tagExists{$lcName} and (not $subdirs{$lcName} or $subdirs{$lcName} == $tagExists{$lcName})) {
626 ++$count{'unique tag names'};
627 }
628 }
629 $tagExists{$lcName} or $tagExists{$lcName} = 0;
630 ++$tagExists{$lcName};
631 # only add writable tags to lookup table (for speed)
632 my $wflag = $$tagInfo{Writable};
633 next unless $writeProc and ($wflag or ($$table{WRITABLE} and
634 not defined $wflag and not $$tagInfo{SubDirectory}));
635 $tagLookup{$lcName} or $tagLookup{$lcName} = { };
636 # remember number for this table
637 my $tagIDs = $tagLookup{$lcName}->{$tableNum};
638 # must allow for duplicate tags with the same name in a single table!
639 if ($tagIDs) {
640 if (ref $tagIDs eq 'HASH') {
641 $$tagIDs{$tagID} = 1;
642 next;
643 } elsif ($tagID eq $tagIDs) {
644 next;
645 } else {
646 $tagIDs = { $tagIDs => 1, $tagID => 1 };
647 }
648 } else {
649 $tagIDs = $tagID;
650 }
651 $tableWritable{$tableName} = 1;
652 $tagLookup{$lcName}->{$tableNum} = $tagIDs;
653 if ($short eq 'Composite' and $$tagInfo{Module}) {
654 $compositeModules{$lcName} = $$tagInfo{Module};
655 }
656 }
657#
658# save TagName information
659#
660 my $tagIDstr;
661 if ($tagID =~ /^\d+$/) {
662 if ($binaryTable or $isIPTC or ($short =~ /^CanonCustom/ and $tagID < 256)) {
663 $tagIDstr = $tagID;
664 } else {
665 $tagIDstr = sprintf("0x%.4x",$tagID);
666 }
667 } elsif ($short eq 'DICOM') {
668 ($tagIDstr = $tagID) =~ s/_/,/;
669 } else {
670 # convert non-printable characters to hex escape sequences
671 if ($tagID =~ s/([\x00-\x1f\x7f-\xff])/'\x'.unpack('H*',$1)/eg) {
672 $tagID =~ s/\\x00/\\0/g;
673 next if $tagID eq 'jP\x1a\x1a'; # ignore abnormal JP2 signature tag
674 $tagIDstr = qq{"$tagID"};
675 } else {
676 $tagIDstr = "'$tagID'";
677 }
678 }
679 my $len = length $tagIDstr;
680 $longID{$tableName} = $len if $longID{$tableName} < $len;
681 foreach (@tagNames) {
682 $len = length $_;
683 $longName{$tableName} = $len if $longName{$tableName} < $len;
684 }
685 push @$info, [ $tagIDstr, \@tagNames, \@writable, \@values, \@require, \@writeGroup ];
686 }
687 }
688 return $self;
689}
690
691#------------------------------------------------------------------------------
692# Rewrite this file to build the lookup tables
693# Inputs: 0) BuildTagLookup object reference
694# 1) output tag lookup module name (ie. 'lib/Image/ExifTool/TagLookup.pm')
695# Returns: true on success
696sub WriteTagLookup($$)
697{
698 local $_;
699 my ($self, $file) = @_;
700 my $tagLookup = $self->{TAG_LOOKUP};
701 my $tagExists = $self->{TAG_EXISTS};
702 my $tableWritable = $self->{TABLE_WRITABLE};
703#
704# open/create necessary files and transfer file headers
705#
706 my $tmpFile = "${file}_tmp";
707 open(INFILE,$file) or warn("Can't open $file\n"), return 0;
708 unless (open(OUTFILE,">$tmpFile")) {
709 warn "Can't create temporary file $tmpFile\n";
710 close(INFILE);
711 return 0;
712 }
713 my $success;
714 while (<INFILE>) {
715 print OUTFILE $_ or last;
716 if (/^#\+{4} Begin/) {
717 $success = 1;
718 last;
719 }
720 }
721 print OUTFILE "\n# list of tables containing writable tags\n";
722 print OUTFILE "my \@tableList = (\n";
723
724#
725# write table list
726#
727 my @tableNames = sort keys %allTables;
728 my $tableName;
729 my %wrNum; # translate from allTables index to writable tables index
730 my $count = 0;
731 my $num = 0;
732 foreach $tableName (@tableNames) {
733 if ($$tableWritable{$tableName}) {
734 print OUTFILE "\t'$tableName',\n";
735 $wrNum{$count} = $num++;
736 }
737 $count++;
738 }
739#
740# write the tag lookup table
741#
742 my $tag;
743 # verify that certain critical tag names aren't duplicated
744 foreach $tag (qw{filename directory}) {
745 next unless $$tagLookup{$tag};
746 my $n = scalar keys %{$$tagLookup{$tag}};
747 warn "Warning: $n writable '$tag' tags!\n" if $n > 1;
748 }
749 print OUTFILE ");\n\n# lookup for all writable tags\nmy \%tagLookup = (\n";
750 foreach $tag (sort keys %$tagLookup) {
751 print OUTFILE "\t'$tag' => { ";
752 my @tableNums = sort { $a <=> $b } keys %{$$tagLookup{$tag}};
753 my (@entries, $tableNum);
754 foreach $tableNum (@tableNums) {
755 my $tagID = $$tagLookup{$tag}->{$tableNum};
756 my $entry;
757 if (ref $tagID eq 'HASH') {
758 my @tagIDs = sort keys %$tagID;
759 foreach (@tagIDs) {
760 if (/^\d+$/) {
761 $_ = sprintf("0x%x",$_);
762 } else {
763 my $quot = "'";
764 # escape non-printable characters in tag ID if necessary
765 $quot = '"' if s/[\x00-\x1f,\x7f-\xff]/sprintf('\\x%.2x',ord($&))/ge;
766 $_ = $quot . $_ . $quot;
767 }
768 }
769 $entry = '[' . join(',', @tagIDs) . ']';
770 } elsif ($tagID =~ /^\d+$/) {
771 $entry = sprintf("0x%x",$tagID);
772 } else {
773 $entry = "'$tagID'";
774 }
775 my $wrNum = $wrNum{$tableNum};
776 push @entries, "$wrNum => $entry";
777 }
778 print OUTFILE join(', ', @entries);
779 print OUTFILE " },\n";
780 }
781#
782# write tag exists lookup
783#
784 print OUTFILE ");\n\n# lookup for non-writable tags to check if the name exists\n";
785 print OUTFILE "my \%tagExists = (\n";
786 foreach $tag (sort keys %$tagExists) {
787 next if $$tagLookup{$tag};
788 print OUTFILE "\t'$tag' => 1,\n";
789 }
790#
791# write module lookup for writable composite tags
792#
793 my $compositeModules = $self->{COMPOSITE_MODULES};
794 print OUTFILE ");\n\n# module names for writable Composite tags\n";
795 print OUTFILE "my \%compositeModules = (\n";
796 foreach (sort keys %$compositeModules) {
797 print OUTFILE "\t'$_' => '$$compositeModules{$_}',\n";
798 }
799 print OUTFILE ");\n\n";
800#
801# finish writing TagLookup.pm and clean up
802#
803 if ($success) {
804 $success = 0;
805 while (<INFILE>) {
806 $success or /^#\+{4} End/ or next;
807 print OUTFILE $_;
808 $success = 1;
809 }
810 }
811 close(INFILE);
812 close(OUTFILE) or $success = 0;
813#
814# return success code
815#
816 if ($success) {
817 rename($tmpFile, $file);
818 } else {
819 unlink($tmpFile);
820 warn "Error rewriting file\n";
821 }
822 return $success;
823}
824
825#------------------------------------------------------------------------------
826# sort numbers first numerically, then strings alphabetically (case insensitive)
827sub NumbersFirst
828{
829 my $rtnVal;
830 my $bNum = ($b =~ /^-?[0-9]+$/);
831 if ($a =~ /^-?[0-9]+$/) {
832 $rtnVal = ($bNum ? $a <=> $b : -1);
833 } elsif ($bNum) {
834 $rtnVal = 1;
835 } else {
836 my ($a2, $b2) = ($a, $b);
837 # expand numbers to 3 digits (with restrictions to avoid messing up ascii-hex tags)
838 $a2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $a2 =~ /^(APP)?[0-9 ]*$/ and length($a2)<16;
839 $b2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $b2 =~ /^(APP)?[0-9 ]*$/ and length($b2)<16;
840 $caseInsensitive and $rtnVal = (lc($a2) cmp lc($b2));
841 $rtnVal or $rtnVal = ($a2 cmp $b2);
842 }
843 return $rtnVal;
844}
845
846#------------------------------------------------------------------------------
847# Convert pod documentation to pod
848# (funny, I know, but the pod headings must be hidden to prevent confusing
849# the pod parser)
850# Inputs: 0) string
851sub Doc2Pod($)
852{
853 my $doc = shift;
854 $doc =~ s/\n~/\n=/g;
855 return $doc;
856}
857
858#------------------------------------------------------------------------------
859# Convert pod documentation to html
860# Inputs: 0) string
861sub Doc2Html($)
862{
863 my $doc = EscapeHTML(shift);
864 $doc =~ s/\n\n/<\/p>\n\n<p>/g;
865 $doc =~ s/B&lt;(.*?)&gt;/<b>$1<\/b>/sg;
866 $doc =~ s/C&lt;(.*?)&gt;/<code>$1<\/code>/sg;
867 $doc =~ s/L&lt;(.*?)&gt;/<a href="$1">$1<\/a>/sg;
868 return $doc;
869}
870
871#------------------------------------------------------------------------------
872# Get the order that we want to print the tables in the documentation
873# Returns: tables in the order we want
874sub GetTableOrder()
875{
876 my %gotTable;
877 my $count = 0;
878 my @tableNames = @tableOrder;
879 my (@orderedTables, %mainTables, @outOfOrder);
880 my $lastTable = '';
881
882 while (@tableNames) {
883 my $tableName = shift @tableNames;
884 next if $gotTable{$tableName};
885 if ($tableName =~ /^Image::ExifTool::(\w+)::Main/) {
886 $mainTables{$1} = 1;
887 } elsif ($lastTable and not $tableName =~ /^${lastTable}::/) {
888 push @outOfOrder, $tableName;
889 }
890 ($lastTable) = ($tableName =~ /^(Image::ExifTool::\w+)/);
891 push @orderedTables, $tableName;
892 $gotTable{$tableName} = 1;
893 my $table = GetTagTable($tableName);
894 # recursively scan through tables in subdirectories
895 my @moreTables;
896 $caseInsensitive = ($tableName =~ /::XMP::/);
897 my @keys = sort NumbersFirst TagTableKeys($table);
898 foreach (@keys) {
899 my @infoArray = GetTagInfoList($table,$_);
900 my $tagInfo;
901 foreach $tagInfo (@infoArray) {
902 my $subdir = $$tagInfo{SubDirectory} or next;
903 $tableName = $$subdir{TagTable} or next;
904 next if $gotTable{$tableName}; # next if table already loaded
905 push @moreTables, $tableName; # must scan this one too
906 }
907 }
908 unshift @tableNames, @moreTables;
909 }
910 # clean up the order for tables which are out of order
911 # (groups all Canon and Kodak tables together)
912 my %fixOrder;
913 foreach (@outOfOrder) {
914 next unless /^Image::ExifTool::(\w+)/;
915 # only re-order tables which have a corresponding main table
916 next unless $mainTables{$1};
917 $fixOrder{$1} = []; # fix the order of these tables
918 }
919 my (@sortedTables, %fixPos, $pos);
920 foreach (@orderedTables) {
921 if (/^Image::ExifTool::(\w+)/ and $fixOrder{$1}) {
922 my $fix = $fixOrder{$1};
923 unless (@$fix) {
924 $pos = @sortedTables;
925 $fixPos{$pos} or $fixPos{$pos} = [];
926 push @{$fixPos{$pos}}, $1;
927 }
928 push @{$fix}, $_;
929 } else {
930 push @sortedTables, $_;
931 }
932 }
933 # insert back in better order
934 foreach $pos (reverse sort { $a <=> $b } keys %fixPos) {
935 my $fix = $fixPos{$pos};
936 foreach (@$fix) {
937 splice(@sortedTables, $pos, 0, @{$fixOrder{$_}});
938 }
939 }
940 # tweak the table order
941 my %tweakOrder = (
942 JPEG => '-', # JPEG comes first
943 IPTC => 'Exif', # put IPTC after EXIF,
944 GPS => 'XMP', # etc...
945 GeoTiff => 'GPS',
946 Leaf => 'Kodak',
947 Unknown => 'Sony',
948 DNG => 'Unknown',
949 PrintIM => 'ICC_Profile',
950 Olympus => 'NikonCapture',
951 Pentax => 'Panasonic',
952 Ricoh => 'Pentax',
953 Sanyo => 'Ricoh',
954 PhotoMechanic => 'FotoStation',
955 );
956 my @tweak = sort keys %tweakOrder;
957 while (@tweak) {
958 my $table = shift @tweak;
959 my $first = $tweakOrder{$table};
960 if ($tweakOrder{$first}) {
961 push @tweak, $table; # must defer this till later
962 next;
963 }
964 delete $tweakOrder{$table}; # because the table won't move again
965 my @moving = grep /^Image::ExifTool::$table\b/, @sortedTables;
966 my @notMoving = grep !/^Image::ExifTool::$table\b/, @sortedTables;
967 my @after;
968 while (@notMoving) {
969 last if $notMoving[-1] =~ /^Image::ExifTool::$first\b/;
970 unshift @after, pop @notMoving;
971 }
972 @sortedTables = (@notMoving, @moving, @after);
973 }
974 return @sortedTables
975}
976
977#------------------------------------------------------------------------------
978# Open HTMLFILE and print header and description
979# Inputs: 0) Filename, 1) optional category
980# Returns: True on success
981my %createdFiles;
982sub OpenHtmlFile($;$$)
983{
984 my ($htmldir, $category, $sepTable) = @_;
985 my ($htmlFile, $head, $title, $url, $class);
986 my $top = '';
987
988 if ($category) {
989 my @names = split ' ', $category;
990 $class = shift @names;
991 $htmlFile = "$htmldir/TagNames/$class.html";
992 $head = $category . ($sepTable ? ' Values' : ' Tags');
993 ($title = $head) =~ s/ .* / /;
994 @names and $url = join '_', @names;
995 } else {
996 $htmlFile = "$htmldir/TagNames/index.html";
997 $category = $class = 'ExifTool';
998 $head = $title = 'ExifTool Tag Names';
999 }
1000 if ($createdFiles{$htmlFile}) {
1001 open(HTMLFILE,">>${htmlFile}_tmp") or return 0;
1002 } else {
1003 open(HTMLFILE,">${htmlFile}_tmp") or return 0;
1004 print HTMLFILE "$docType<html>\n<head>\n<title>$title</title>\n";
1005 print HTMLFILE "<link rel=stylesheet type='text/css' href='style.css' title='Style'>\n";
1006 print HTMLFILE "</head>\n<body>\n";
1007 if ($category ne $class and $docs{$class}) {
1008 print HTMLFILE "<h2 class=top>$class Tags</h2>\n" or return 0;
1009 print HTMLFILE '<p>',Doc2Html($docs{$class}),"</p>\n" or return 0;
1010 } else {
1011 $top = " class=top";
1012 }
1013 }
1014 $head = "<a name='$url'>$head</a>" if $url;
1015 print HTMLFILE "<h2$top>$head</h2>\n" or return 0;
1016 print HTMLFILE '<p>',Doc2Html($docs{$category}),"</p>\n" if $docs{$category};
1017 $createdFiles{$htmlFile} = 1;
1018 return 1;
1019}
1020
1021#------------------------------------------------------------------------------
1022# Close all html files and write trailers
1023# Returns: true on success
1024# Inputs: 0) BuildTagLookup object reference
1025sub CloseHtmlFiles($)
1026{
1027 my $self = shift;
1028 my $preserveDate = $$self{PRESERVE_DATE};
1029 my $success = 1;
1030 # get the date
1031 my ($sec,$min,$hr,$day,$mon,$yr) = localtime;
1032 my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
1033 $yr += 1900;
1034 my $date = "$month[$mon] $day, $yr";
1035 my $htmlFile;
1036 my $countNewFiles = 0;
1037 my $countSameFiles = 0;
1038 foreach $htmlFile (keys %createdFiles) {
1039 my $tmpFile = $htmlFile . '_tmp';
1040 my $fileDate = $date;
1041 if ($preserveDate) {
1042 my @lines = `grep '<i>Last revised' $htmlFile`;
1043 $fileDate = $1 if @lines and $lines[-1] =~ m{<i>Last revised (.*)</i>};
1044 }
1045 open(HTMLFILE,">>$tmpFile") or $success = 0, next;
1046 # write the trailers
1047 print HTMLFILE "<hr>\n";
1048 print HTMLFILE "(This document generated automatically by Image::ExifTool::BuildTagLookup)\n";
1049 print HTMLFILE "<br><i>Last revised $fileDate</i>\n";
1050 print HTMLFILE "<p class=lf><a href=";
1051 if ($htmlFile =~ /index\.html$/) {
1052 print HTMLFILE "'../index.html'>&lt;-- Back to ExifTool home page</a></p>\n";
1053 } else {
1054 print HTMLFILE "'index.html'>&lt;-- ExifTool Tag Names</a></p>\n"
1055 }
1056 print HTMLFILE "</body>\n</html>\n" or $success = 0;
1057 close HTMLFILE or $success = 0;
1058 # check for differences and only use new file if it was changed
1059 # (so the date only gets updated if changes were really made)
1060 my $useNewFile;
1061 if ($success) {
1062 open (TEMPFILE, $tmpFile) or $success = 0, last;
1063 if (open (HTMLFILE, $htmlFile)) {
1064 while (<HTMLFILE>) {
1065 my $newLine = <TEMPFILE>;
1066 if (defined $newLine) {
1067 next if /^<br><i>Last revised/;
1068 next if $_ eq $newLine;
1069 }
1070 # files are different -- use the new file
1071 $useNewFile = 1;
1072 last;
1073 }
1074 $useNewFile = 1 if <TEMPFILE>;
1075 close HTMLFILE;
1076 } else {
1077 $useNewFile = 1;
1078 }
1079 close TEMPFILE;
1080 if ($useNewFile) {
1081 ++$countNewFiles;
1082 rename $tmpFile, $htmlFile or warn("Error renaming temporary file\n"), $success = 0;
1083 } else {
1084 ++$countSameFiles;
1085 unlink $tmpFile; # erase new file and use existing file
1086 }
1087 }
1088 last unless $success;
1089 }
1090 # save number of files processed so we can check the results later
1091 $self->{COUNT}->{'HTML files changed'} = $countNewFiles;
1092 $self->{COUNT}->{'HTML files unchanged'} = $countSameFiles;
1093 return $success;
1094}
1095
1096#------------------------------------------------------------------------------
1097# Write the TagName HTML and POD documentation
1098# Inputs: 0) BuildTagLookup object reference
1099# 1) output pod file (ie. 'lib/Image/ExifTool/TagNames.pod')
1100# 2) output html directory (ie. 'html')
1101# Returns: true on success
1102sub WriteTagNames($$)
1103{
1104 my ($self, $podFile, $htmldir) = @_;
1105 my ($tableName, $short, $url, @sepTables);
1106 my $tagNameInfo = $self->{TAG_NAME_INFO} or return 0;
1107 my $idTitle = $self->{TAG_ID};
1108 my $shortName = $self->{SHORT_NAME};
1109 my $sepTable = $self->{SEPARATE_TABLE};
1110 my $success = 1;
1111 my %htmlFiles;
1112 my $columns = 6; # number of columns in html index
1113 my $percent = int(100 / $columns);
1114
1115 # open the file and write the header
1116 open(PODFILE,">$podFile") or return 0;
1117 print PODFILE Doc2Pod($docs{PodHeader}), $docs{ExifTool}, $docs{ExifTool2};
1118 mkdir "$htmldir/TagNames";
1119 OpenHtmlFile($htmldir) or return 0;
1120 print HTMLFILE "<blockquote>\n";
1121 print HTMLFILE "<table width='100%' class=frame><tr><td>\n";
1122 print HTMLFILE "<table width='100%' class=inner cellspacing=1><tr class=h>\n";
1123 print HTMLFILE "<th colspan=$columns><span class=l>Tag Table Index</span></th></tr>\n";
1124 print HTMLFILE "<tr class=b><td width='$percent%'>\n";
1125 # write the index
1126 my @tableNames = GetTableOrder();
1127 push @tableNames, 'Image::ExifTool::Shortcuts::Main'; # do Shortcuts last
1128 # get list of headings and add any missing ones
1129 my $heading = 'xxx';
1130 my (@tableIndexNames, @headings);
1131 foreach $tableName (@tableNames) {
1132 $short = $$shortName{$tableName};
1133 my @names = split ' ', $short;
1134 my $class = shift @names;
1135 if (@names) {
1136 # add heading for tables without a Main
1137 unless ($heading eq $class) {
1138 $heading = $class;
1139 push @tableIndexNames, $heading;
1140 push @headings, $heading;
1141 }
1142 } else {
1143 $heading = $short;
1144 push @headings, $heading;
1145 }
1146 push @tableIndexNames, $tableName;
1147 }
1148 @tableNames = @tableIndexNames;
1149 # print html index of headings only
1150 my $count = 0;
1151 my $lines = int((scalar(@headings) + $columns - 1) / $columns);
1152 foreach $tableName (@headings) {
1153 if ($count) {
1154 if ($count % $lines) {
1155 print HTMLFILE "<br>\n";
1156 } else {
1157 print HTMLFILE "</td><td width='$percent%'>\n";
1158 }
1159 }
1160 $short = $$shortName{$tableName};
1161 $short = $tableName unless $short;
1162 $url = "$short.html";
1163 print HTMLFILE "<a href='$url'>$short</a>";
1164 ++$count;
1165 }
1166 print HTMLFILE "\n</td></tr></table></td></tr></table></blockquote>\n";
1167 print HTMLFILE '<p>',Doc2Html($docs{ExifTool2}),"</p>\n";
1168 # write all the tag tables
1169 while (@tableNames or @sepTables) {
1170 while (@sepTables) {
1171 $tableName = shift @sepTables;
1172 my $printConv = $$sepTable{$tableName};
1173 next unless ref $printConv eq 'HASH';
1174 $$sepTable{$tableName} = 1;
1175 my $notes = $$printConv{Notes};
1176 if ($notes) {
1177 # remove unnecessary whitespace
1178 $notes =~ s/(^\s+|\s+$)//g;
1179 $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
1180 }
1181 my $head = $tableName;
1182 $head =~ s/.* //;
1183 close HTMLFILE;
1184 if (OpenHtmlFile($htmldir, $tableName, 1)) {
1185 print HTMLFILE Doc2Html($notes), "\n" if $notes;
1186 print HTMLFILE "<blockquote>\n";
1187 print HTMLFILE "<table class=frame><tr><td>\n";
1188 print HTMLFILE "<table class='inner sep' cellspacing=1>\n";
1189 my $align = ' class=r';
1190 my $wid = 0;
1191 my @keys;
1192 foreach (sort NumbersFirst keys %$printConv) {
1193 next if /^(Notes|PrintHex)$/;
1194 $align = '' if $align and /[^\d]/;
1195 my $w = length($_) + length($$printConv{$_});
1196 $wid = $w if $wid < $w;
1197 push @keys, $_;
1198 }
1199 $wid = length($tableName)+7 if $wid < length($tableName)+7;
1200 # print in multiple columns if there is room
1201 my $cols = int(80 / ($wid + 4));
1202 $cols = 1 if $cols < 1 or $cols > @keys;
1203 my $rows = int((scalar(@keys) + $cols - 1) / $cols);
1204 my ($r, $c);
1205 print HTMLFILE '<tr class=h>';
1206 for ($c=0; $c<$cols; ++$c) {
1207 print HTMLFILE "<th>Value</th><th>$head</th>";
1208 }
1209 print HTMLFILE "</tr>\n";
1210 for ($r=0; $r<$rows; ++$r) {
1211 print HTMLFILE '<tr>';
1212 for ($c=0; $c<$cols; ++$c) {
1213 my $key = $keys[$r + $c*$rows];
1214 my ($index, $prt);
1215 if (defined $key) {
1216 $index = $key;
1217 $prt = "= $$printConv{$key}";
1218 if ($$printConv{PrintHex}) {
1219 $index = sprintf('0x%x',$index);
1220 } elsif ($index !~ /^[-+]?\d+$/) {
1221 $index = "'" . EscapeHTML($index) . "'";
1222 }
1223 } else {
1224 $index = $prt = '&nbsp;';
1225 }
1226 my ($ic, $pc);
1227 if ($c & 0x01) {
1228 $pc = ' class=b';
1229 $ic = $align ? " class='r b'" : $pc;
1230 } else {
1231 $ic = $align;
1232 $pc = '';
1233 }
1234 print HTMLFILE "<td$ic>$index</td><td$pc>$prt</td>\n";
1235 }
1236 print HTMLFILE '</tr>';
1237 }
1238 print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
1239 }
1240 }
1241 last unless @tableNames;
1242 $tableName = shift @tableNames;
1243 $short = $$shortName{$tableName};
1244 unless ($short) {
1245 # this is just an index heading
1246 print PODFILE "\n=head2 $tableName Tags\n";
1247 print PODFILE $docs{$tableName} if $docs{$tableName};
1248 next;
1249 }
1250 my $info = $$tagNameInfo{$tableName};
1251 my $id = $$idTitle{$tableName};
1252 my ($hid, $showGrp);
1253 # widths of the different columns in the POD documentation
1254 my ($wID,$wTag,$wReq,$wGrp) = (8,36,24,10);
1255 my $composite = $short eq 'Composite' ? 1 : 0;
1256 my $derived = $composite ? '<th>Derived From</th>' : '';
1257 if ($short eq 'Shortcuts') {
1258 $derived = '<th>Refers To</th>';
1259 $composite = 2;
1260 }
1261 my $podIdLen = $self->{LONG_ID}->{$tableName};
1262 my $notes;
1263 unless ($composite == 2) {
1264 my $table = GetTagTable($tableName);
1265 $notes = $$table{NOTES};
1266 }
1267 my $prefix;
1268 if ($notes) {
1269 # remove unnecessary whitespace
1270 $notes =~ s/(^\s+|\s+$)//g;
1271 $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
1272 if ($notes =~ /leading '(.*?_)' which/) {
1273 $prefix = $1;
1274 $podIdLen -= length $prefix;
1275 }
1276 }
1277 if ($podIdLen <= $wID) {
1278 $podIdLen = $wID;
1279 } elsif ($short eq 'DICOM') {
1280 $podIdLen = 10;
1281 } else {
1282 # align tag names in secondary columns if possible
1283 my $col = ($podIdLen <= 10) ? 12 : 20;
1284 $podIdLen = $col if $podIdLen < $col;
1285 }
1286 $id = '' if $short =~ /^XMP/;
1287 if ($id) {
1288 ($hid = "<th>$id</th>") =~ s/ /&nbsp;/g;
1289 $wTag -= $podIdLen - $wID;
1290 $wID = $podIdLen;
1291 my $longTag = $self->{LONG_NAME}->{$tableName};
1292 if ($wTag < $longTag) {
1293 $wID -= $longTag - $wTag;
1294 $wTag = $longTag;
1295 warn "Notice: Long tags in $tableName table\n";
1296 }
1297 } elsif ($short !~ /^(Composite|Shortcuts)/) {
1298 $wTag += 9;
1299 $hid = '';
1300 } else {
1301 $hid = '';
1302 $wTag += $wID - $wReq if $composite;
1303 }
1304 if ($short eq 'EXIF') {
1305 $derived = '<th>Group</th>';
1306 $showGrp = 1;
1307 $wTag -= $wGrp + 1;
1308 }
1309 my $head = ($short =~ / /) ? 'head3' : 'head2';
1310 print PODFILE "\n=$head $short Tags\n";
1311 print PODFILE $docs{$short} if $docs{$short};
1312 print PODFILE "\n$notes\n" if $notes;
1313 my $line = "\n";
1314 if ($id) {
1315 # shift over 'Index' heading by one character for a bit more balance
1316 $id = " $id" if $id eq 'Index';
1317 $line .= sprintf " %-${wID}s", $id;
1318 } else {
1319 $line .= ' ';
1320 }
1321 my $tagNameHeading = ($short eq 'XMP') ? 'Namespace' : 'Tag Name';
1322 $line .= sprintf " %-${wTag}s", $tagNameHeading;
1323 $line .= sprintf " %-${wReq}s", $composite == 2 ? 'Refers To' : 'Derived From' if $composite;
1324 $line .= sprintf " %-${wGrp}s", 'Group' if $showGrp;
1325 $line .= ' Writable';
1326 print PODFILE $line;
1327 $line =~ s/Sequence /Sequence\t/; # don't want an underline after 'Sequence'
1328 $line =~ s/\S/-/g;
1329 $line =~ s/- -/---/g;
1330 $line =~ tr/\t/ /;
1331 print PODFILE $line,"\n";
1332 close HTMLFILE;
1333 OpenHtmlFile($htmldir, $short) or $success = 0;
1334 print HTMLFILE '<p>',Doc2Html($notes), "</p>\n" if $notes;
1335 print HTMLFILE "<blockquote>\n";
1336 print HTMLFILE "<table class=frame><tr><td>\n";
1337 print HTMLFILE "<table class=inner cellspacing=1>\n";
1338 print HTMLFILE "<tr class=h>$hid<th>$tagNameHeading</th>\n";
1339 print HTMLFILE "<th>Writable</th>$derived<th>Values / ${noteFont}Notes</span></th></tr>\n";
1340 my $rowClass = 1;
1341 my $infoCount = 0;
1342 my $infoList;
1343 foreach $infoList (@$info) {
1344 ++$infoCount;
1345 my ($tagIDstr, $tagNames, $writable, $values, $require, $writeGroup) = @$infoList;
1346 my ($align, $idStr, $w);
1347 if (not $id) {
1348 $idStr = ' ';
1349 } elsif ($tagIDstr =~ /^\d+$/) {
1350 $w = $wID - 3;
1351 $idStr = sprintf " %${w}d ", $tagIDstr;
1352 $align = " class=r";
1353 } else {
1354 $tagIDstr =~ s/^'$prefix/'/ if $prefix;
1355 $w = $wID;
1356 if (length $tagIDstr > $w) {
1357 # put tag name on next line if ID is too long
1358 $idStr = " $tagIDstr\n " . (' ' x $w);
1359 warn "Notice: Split $$tagNames[0] line\n";
1360 } else {
1361 $idStr = sprintf " %-${w}s ", $tagIDstr;
1362 }
1363 $align = '';
1364 }
1365 my @reqs;
1366 my @tags = @$tagNames;
1367 my @wGrp = @$writeGroup;
1368 my @vals = @$writable;
1369 my $wrStr = shift @vals;
1370 my $subdir;
1371 # if this is a subdirectory, print subdir name (from values) instead of writable
1372 if ($wrStr =~ /^-/) {
1373 $subdir = 1;
1374 @vals = @$values;
1375 # remove Notes if subdir has Notes as well
1376 shift @vals if $vals[0] =~ /^\(/ and @vals >= @$writable;
1377 foreach (@vals) { /^\(/ and $_ = '-' }
1378 my $i; # fill in any missing entries from non-directory tags
1379 for ($i=0; $i<@$writable; ++$i) {
1380 $vals[$i] = $$writable[$i] unless defined $vals[$i];
1381 }
1382 if ($$sepTable{$vals[0]}) {
1383 $wrStr =~ s/^-//;
1384 $wrStr = 'N' unless $wrStr;
1385 } else {
1386 $wrStr = $vals[0];
1387 }
1388 shift @vals;
1389 }
1390 my $tag = shift @tags;
1391 printf PODFILE "%s%-${wTag}s", $idStr, $tag;
1392 warn "Warning: Pushed $tag\n" if $id and length($tag) > $wTag;
1393 printf PODFILE " %-${wGrp}s", shift(@wGrp) || '-' if $showGrp;
1394 if ($composite) {
1395 @reqs = @$require;
1396 $w = $wReq; # Keep writable column in line
1397 length($tag) > $wTag and $w -= length($tag) - $wTag;
1398 printf PODFILE " %-${w}s", shift(@reqs) || '';
1399 }
1400 printf PODFILE " $wrStr\n";
1401 my $n = 0;
1402 while (@tags or @reqs or @vals) {
1403 my $more = (@tags or @reqs);
1404 $line = ' ';
1405 $line .= ' 'x($wID+1) if $id;
1406 $line .= sprintf("%-${wTag}s", shift(@tags) || '');
1407 $line .= sprintf(" %-${wReq}s", shift(@reqs) || '') if $composite;
1408 $line .= sprintf(" %-${wGrp}s", shift(@wGrp) || '-') if $showGrp;
1409 ++$n;
1410 if (@vals) {
1411 my $val = shift @vals;
1412 # use writable if this is a note
1413 my $wrStr = $$writable[$n];
1414 if ($subdir and ($val =~ /^\(/ or $val =~ /=/ or ($wrStr and $wrStr !~ /^-/))) {
1415 $val = $wrStr;
1416 if (defined $val) {
1417 $val =~ s/^-//;
1418 } else {
1419 # done with tag if nothing else to print
1420 last unless $more;
1421 }
1422 }
1423 $line .= " $val" if defined $val;
1424 }
1425 $line =~ s/\s+$//; # trim trailing white space
1426 print PODFILE "$line\n";
1427 }
1428 my @htmlTags;
1429 foreach (@$tagNames) {
1430 push @htmlTags, EscapeHTML($_);
1431 }
1432 $rowClass = $rowClass ? '' : " class=b";
1433 my $isSubdir;
1434 if ($$writable[0] =~ /^-/) {
1435 $isSubdir = 1;
1436 foreach (@$writable) {
1437 s/^-(.+)/$1/;
1438 }
1439 }
1440 print HTMLFILE "<tr$rowClass>\n";
1441 print HTMLFILE "<td$align>$tagIDstr</td>\n" if $id;
1442 print HTMLFILE "<td>", join("\n <br>",@htmlTags), "</td>\n";
1443 print HTMLFILE "<td class=c>",join('<br>',@$writable),"</td>\n";
1444 print HTMLFILE '<td>',join("\n <br>",@$require),"</td>\n" if $composite;
1445 print HTMLFILE "<td class=c>",join('<br>',@$writeGroup),"</td>\n" if $showGrp;
1446 print HTMLFILE "<td>";
1447 my $close = '';
1448 my @values;
1449 if (@$values) {
1450 if ($isSubdir) {
1451 my $smallNote;
1452 foreach (@$values) {
1453 if (/^\(/) {
1454 $smallNote = 1 if $n <= 1;
1455 push @values, ($smallNote ? $noteFontSmall : $noteFont) . "$_</span>";
1456 next;
1457 }
1458 /=/ and push(@values, $_), next;
1459 my @names = split;
1460 $url = (shift @names) . '.html';
1461 @names and $url .= '#' . join '_', @names;
1462 my $suffix = ' Tags';
1463 if ($$sepTable{$_}) {
1464 push @sepTables, $_;
1465 $suffix = ' Values';
1466 }
1467 push @values, "--&gt; <a href='$url'>$_$suffix</a>";
1468 }
1469 # put small note last
1470 $smallNote and push @values, shift @values;
1471 } else {
1472 foreach (@$values) {
1473 $_ = EscapeHTML($_);
1474 /^\(/ and $_ = "$noteFont$_</span>";
1475 push @values, $_;
1476 }
1477 print HTMLFILE "<span class=s>";
1478 $close = '</span>';
1479 }
1480 } else {
1481 push @values, '&nbsp;';
1482 }
1483 print HTMLFILE join("\n <br>",@values),"$close</td></tr>\n";
1484 }
1485 unless ($infoCount) {
1486 printf PODFILE " [no tags known]\n";
1487 my $cols = 3;
1488 ++$cols if $hid;
1489 ++$cols if $derived;
1490 print HTMLFILE "<tr><td colspan=$cols class=c>[no tags known]</td></tr>\n";
1491 }
1492 print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
1493 }
1494 close(HTMLFILE) or $success = 0;
1495 CloseHtmlFiles($self) or $success = 0;
1496 print PODFILE Doc2Pod($docs{PodTrailer}) or $success = 0;
1497 close(PODFILE) or $success = 0;
1498 return $success;
1499}
1500
15011; # end
1502
1503
1504__END__
1505
1506=head1 NAME
1507
1508Image::ExifTool::BuildTagLookup - Build ExifTool tag lookup tables
1509
1510=head1 DESCRIPTION
1511
1512This module is used to generate the tag lookup tables in
1513Image::ExifTool::TagLookup.pm and tag name documentation in
1514Image::ExifTool::TagNames.pod, as well as HTML tag name documentation. It
1515is used before each new ExifTool release to update the lookup tables and
1516documentation.
1517
1518=head1 SYNOPSIS
1519
1520 use Image::ExifTool::BuildTagLookup;
1521
1522 $builder = new Image::ExifTool::BuildTagLookup;
1523
1524 $ok = $builder->WriteTagLookup('lib/Image/ExifTool/TagLookup.pm');
1525
1526 $ok = $builder->WriteTagNames('lib/Image/ExifTool/TagNames.pod','html');
1527
1528=head1 AUTHOR
1529
1530Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
1531
1532This library is free software; you can redistribute it and/or modify it
1533under the same terms as Perl itself.
1534
1535=head1 SEE ALSO
1536
1537L<Image::ExifTool(3pm)|Image::ExifTool>,
1538L<Image::ExifTool::TagLookup(3pm)|Image::ExifTool::TagLookup>,
1539L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>
1540
1541=cut
Note: See TracBrowser for help on using the repository browser.