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 |
|
---|
14 | package Image::ExifTool::BuildTagLookup;
|
---|
15 |
|
---|
16 | use strict;
|
---|
17 | require Exporter;
|
---|
18 |
|
---|
19 | BEGIN {
|
---|
20 | # prevent ExifTool from loading the user config file
|
---|
21 | $Image::ExifTool::noConfig = 1;
|
---|
22 | }
|
---|
23 |
|
---|
24 | use vars qw($VERSION @ISA);
|
---|
25 | use Image::ExifTool qw(:Utils :Vars);
|
---|
26 | use Image::ExifTool::Shortcuts;
|
---|
27 | use Image::ExifTool::HTML qw(EscapeHTML);
|
---|
28 | use Image::ExifTool::IPTC;
|
---|
29 | use Image::ExifTool::Canon;
|
---|
30 | use Image::ExifTool::Nikon;
|
---|
31 |
|
---|
32 | $VERSION = '1.59';
|
---|
33 | @ISA = qw(Exporter);
|
---|
34 |
|
---|
35 | sub NumbersFirst;
|
---|
36 |
|
---|
37 | # colors for html pages
|
---|
38 | my $noteFont = "<span class=n>";
|
---|
39 | my $noteFontSmall = "<span class='n s'>";
|
---|
40 |
|
---|
41 | my $docType = q{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
---|
42 | "http://www.w3.org/TR/html4/loose.dtd">
|
---|
43 | };
|
---|
44 |
|
---|
45 |
|
---|
46 | my $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.
|
---|
51 | my %docs = (
|
---|
52 | PodHeader => q{
|
---|
53 | ~head1 NAME
|
---|
54 |
|
---|
55 | Image::ExifTool::TagNames - ExifTool tag name documentation
|
---|
56 |
|
---|
57 | ~head1 DESCRIPTION
|
---|
58 |
|
---|
59 | This document contains a complete list of ExifTool tag names, organized into
|
---|
60 | tables based on information type. Tag names are used to indicate the
|
---|
61 | specific meta information that is extracted or written in an image.
|
---|
62 |
|
---|
63 | ~head1 TAG TABLES
|
---|
64 | },
|
---|
65 | ExifTool => q{
|
---|
66 | The tables listed below give the names of all tags recognized by ExifTool.
|
---|
67 | },
|
---|
68 | ExifTool2 => q{
|
---|
69 | B<Tag ID>, B<Index> or B<Sequence> is given in the first column of each
|
---|
70 | table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
|
---|
71 | is the identifier that is actually stored in the file. An B<Index> refers
|
---|
72 | to the location of a value when found at a fixed position within a data
|
---|
73 | block, and B<Sequence> gives the order of values for a serial data stream.
|
---|
74 |
|
---|
75 | A B<Tag Name> is the handle by which the information is accessed in
|
---|
76 | ExifTool. In some instances, more than one name may correspond to a single
|
---|
77 | tag ID. In these cases, the actual name used depends on the context in
|
---|
78 | which the information is found. Case is not significant for tag names. A
|
---|
79 | question mark after a tag name indicates that the information is either not
|
---|
80 | understood, not verified, or not very useful -- these tags are not extracted
|
---|
81 | by ExifTool unless the Unknown (-u) option is enabled. Be aware that some
|
---|
82 | tag names are different than the descriptions printed out by default when
|
---|
83 | extracting information with exiftool. To see the tag names instead of the
|
---|
84 | descriptions, use C<exiftool -s>.
|
---|
85 |
|
---|
86 | The B<Writable> column indicates whether the tag is writable by ExifTool.
|
---|
87 | Anything but an C<N> in this column means the tag is writable. A C<Y>
|
---|
88 | indicates writable information that is either unformatted or written using
|
---|
89 | the existing format. Other expressions give details about the information
|
---|
90 | format, and vary depending on the general type of information. The format
|
---|
91 | name may be followed by a number in square brackets to indicate the number
|
---|
92 | of values written, or the number of characters in a fixed-length string
|
---|
93 | (including a null terminator which is added if required).
|
---|
94 |
|
---|
95 | An 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
|
---|
97 | tag. A tilde (C<~>) indicates a tag this is only writable when print
|
---|
98 | conversion is disabled (by setting PrintConv to 0, or using the -n option).
|
---|
99 | A slash (C</>) indicates an "avoided" tag that is not created unless the
|
---|
100 | group is specified (due to name conflicts with other tags). An exclamation
|
---|
101 | point (C<!>) indicates a tag that is considered unsafe to write under normal
|
---|
102 | circumstances. These "unsafe" tags are not set when calling
|
---|
103 | SetNewValuesFromFile() or when using the exiftool -TagsFromFile option
|
---|
104 | unless specified explicitly, and care should be taken when editing them
|
---|
105 | manually since they may affect the way an image is rendered. A plus sign
|
---|
106 | (C<+>) indicates a "list" tag which supports multiple instances.
|
---|
107 |
|
---|
108 | The HTML version of these tables also list possible B<Values> for
|
---|
109 | discrete-valued tags, as well as B<Notes> for some tags.
|
---|
110 |
|
---|
111 | B<Note>: If you are familiar with common meta-information tag names, you may
|
---|
112 | find that some ExifTool tag names are different than expected. The usual
|
---|
113 | reason for this is to make the tag names more consistent across different
|
---|
114 | types of meta information. To determine a tag name, either consult this
|
---|
115 | documentation or run C<exiftool -s> on a file containing the information in
|
---|
116 | question.
|
---|
117 | },
|
---|
118 | EXIF => q{
|
---|
119 | EXIF stands for "Exchangeable Image File Format". This type of information
|
---|
120 | is formatted according to the TIFF specification, and may be found in JPG,
|
---|
121 | TIFF, PNG, MIFF and WDP images, as well as many TIFF-based RAW images.
|
---|
122 |
|
---|
123 | The 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
|
---|
125 | ExifTool family 1 group names. When writing EXIF information, the default
|
---|
126 | B<Group> listed below is used unless another group is specified.
|
---|
127 |
|
---|
128 | Also listed in the table below are TIFF, DNG, WDP and other tags which are
|
---|
129 | not part of the EXIF specification, but may co-exist with EXIF tags in some
|
---|
130 | images.
|
---|
131 | },
|
---|
132 | GPS => q{
|
---|
133 | These GPS tags are part of the EXIF standard, and are stored in a separate
|
---|
134 | IFD within the EXIF information.
|
---|
135 |
|
---|
136 | ExifTool is very flexible about the input format when writing lat/long
|
---|
137 | coordinates, and will accept from 1 to 3 floating point numbers (for decimal
|
---|
138 | degrees, degrees and minutes, or degrees, minutes and seconds) separated by
|
---|
139 | just about anything, and will format them properly according to the EXIF
|
---|
140 | specification.
|
---|
141 |
|
---|
142 | Some GPS tags have values which are fixed-length strings. For these, the
|
---|
143 | indicated string lengths include a null terminator which is added
|
---|
144 | automatically by ExifTool. Remember that the descriptive values are used
|
---|
145 | when writing (ie. 'Above Sea Level', not '0') unless the print conversion is
|
---|
146 | disabled (with '-n' on the command line, or the PrintConv option in the
|
---|
147 | API).
|
---|
148 | },
|
---|
149 | XMP => q{
|
---|
150 | XMP stands for "Extensible Metadata Platform", an XML/RDF-based metadata
|
---|
151 | format which is being pushed by Adobe. Information in this format can be
|
---|
152 | embedded in many different image file types including JPG, JP2, TIFF, PNG,
|
---|
153 | MIFF, PS, PDF, PSD and DNG, as well as audio file formats supporting ID3v2
|
---|
154 | information.
|
---|
155 |
|
---|
156 | The XMP B<Tag ID>'s aren't listed because in most cases they are identical
|
---|
157 | to the B<Tag Name>.
|
---|
158 |
|
---|
159 | All XMP information is stored as character strings. The B<Writable> column
|
---|
160 | specifies the information format: C<integer> is a string of digits
|
---|
161 | (possibly beginning with a '+' or '-'), C<real> is a floating point number,
|
---|
162 | C<rational> is two C<integer> strings separated by a '/' character, C<date>
|
---|
163 | is a date/time string in the format "YYYY:MM:DD HH:MM:SS[+/-HH:MM]",
|
---|
164 | C<boolean> is either "True" or "False", and C<lang-alt> is a list of string
|
---|
165 | alternatives in different languages.
|
---|
166 |
|
---|
167 | Individual languages for C<lang-alt> tags are accessed by suffixing the tag
|
---|
168 | name with a '-', followed by an RFC 3066 language code (ie. "XMP:Title-fr",
|
---|
169 | or "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.
|
---|
171 | The "x-default" language code may be specified when writing a new value to
|
---|
172 | write only the default language, but note that all languages are still
|
---|
173 | deleted if "x-default" tag is deleted. When reading, "x-default" is not
|
---|
174 | specified.
|
---|
175 |
|
---|
176 | The XMP tags are organized according to schema B<Namespace> in the following
|
---|
177 | tables. Note that a few of the longer namespace prefixes given below have
|
---|
178 | been shortened for convenience (since the family 1 group names are derived
|
---|
179 | from these by adding a leading "XMP-"). In cases where a tag name exists in
|
---|
180 | more than one namespace, less common namespaces are avoided when writing.
|
---|
181 | However, any namespace may be written by specifying a family 1 group name
|
---|
182 | for the tag, ie) XMP-exif:Contrast or XMP-crs:Contrast.
|
---|
183 |
|
---|
184 | ExifTool will extract XMP information even if it is not listed in these
|
---|
185 | tables. For example, the C<pdfx> namespace doesn't have a predefined set of
|
---|
186 | tag names because it is used to store application-defined PDF information,
|
---|
187 | but this information is extracted by ExifTool anyway.
|
---|
188 | },
|
---|
189 | IPTC => q{
|
---|
190 | IPTC stands for "International Press Telecommunications Council". This is
|
---|
191 | an older meta information format that is slowly being phased out in favor of
|
---|
192 | XMP. IPTC information may be embedded in JPG, TIFF, PNG, MIFF, PS, PDF, PSD
|
---|
193 | and DNG images.
|
---|
194 |
|
---|
195 | The IPTC specification dictates a length for ASCII (C<string> or C<digits>)
|
---|
196 | values. These lengths are given in square brackets after the B<Writable>
|
---|
197 | format name. For tags where a range of lengths is allowed, the minimum and
|
---|
198 | maximum lengths are separated by a comma within the brackets. IPTC strings
|
---|
199 | are not null terminated.
|
---|
200 |
|
---|
201 | IPTC information is separated into different records, each of which has its
|
---|
202 | own set of tags.
|
---|
203 | },
|
---|
204 | Photoshop => q{
|
---|
205 | Photoshop tags are found in PSD files, as well as inside embedded Photoshop
|
---|
206 | information in many other file types (JPEG, TIFF, PDF, PNG to name a few).
|
---|
207 |
|
---|
208 | Many Photoshop tags are marked as Unknown (indicated by a question mark
|
---|
209 | after the tag name) because the information they provide is not very useful
|
---|
210 | under normal circumstances (and because Adobe denied my application for
|
---|
211 | their file format documentation -- apparently open source software is too
|
---|
212 | big a concept for them). These unknown tags are not extracted unless the
|
---|
213 | Unknown (-u) option is used.
|
---|
214 | },
|
---|
215 | PrintIM => q{
|
---|
216 | The format of the PrintIM information is known, however no PrintIM tags have
|
---|
217 | been decoded. Use the Unknown (-u) option to extract PrintIM information.
|
---|
218 | },
|
---|
219 | Kodak => q{
|
---|
220 | The Kodak maker notes aren't in standard IFD format, and the format varies
|
---|
221 | frequently with different models. Some information has been decoded, but
|
---|
222 | much of the Kodak information remains unknown.
|
---|
223 | },
|
---|
224 | 'Kodak SpecialEffects' => q{
|
---|
225 | The Kodak SpecialEffects and Borders tags are found in sub-IFD's within the
|
---|
226 | Kodak JPEG APP3 "Meta" segment.
|
---|
227 | },
|
---|
228 | Minolta => q{
|
---|
229 | These tags are used by Minolta and Konica/Minolta cameras. Minolta doesn't
|
---|
230 | make things easy for decoders because the meaning of some tags and the
|
---|
231 | location where some information is stored is different for different camera
|
---|
232 | models. (Take MinoltaQuality for example, which may be located in 5
|
---|
233 | different places.)
|
---|
234 | },
|
---|
235 | Olympus => q{
|
---|
236 | Tags 0x0000 through 0x0103 are used by some older Olympus cameras, and are
|
---|
237 | the same as Konica/Minolta tags. The Olympus tags are also used for Epson
|
---|
238 | and Agfa cameras.
|
---|
239 | },
|
---|
240 | Panasonic => q{
|
---|
241 | Panasonic tags are also used for Leica cameras.
|
---|
242 | },
|
---|
243 | Pentax => q{
|
---|
244 | The Pentax tags are also used in Asahi cameras.
|
---|
245 | },
|
---|
246 | Sigma => q{
|
---|
247 | These tags are used in Sigma/Foveon cameras.
|
---|
248 | },
|
---|
249 | Sony => q{
|
---|
250 | The maker notes in images from current Sony camera models contain a wealth
|
---|
251 | of information, but very little is known about these tags. Use the ExifTool
|
---|
252 | Unknown (-u) or Verbose (-v) options to see information about the unknown
|
---|
253 | tags.
|
---|
254 | },
|
---|
255 | CanonRaw => q{
|
---|
256 | These 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
|
---|
258 | length of the information is preserved (and the new information is truncated
|
---|
259 | or padded as required) unless B<Writable> is C<resize>. Currently, only
|
---|
260 | JpgFromRaw and ThumbnailImage are allowed to change size.
|
---|
261 | },
|
---|
262 | Unknown => q{
|
---|
263 | The following tags are decoded in unsupported maker notes. Use the Unknown
|
---|
264 | (-u) option to display other unknown tags.
|
---|
265 | },
|
---|
266 | PDF => q{
|
---|
267 | The tags listed in the PDF tables below are those which are used by ExifTool
|
---|
268 | to extract meta information, but they are only a small fraction of the total
|
---|
269 | number of available PDF tags.
|
---|
270 | },
|
---|
271 | DNG => q{
|
---|
272 | The main DNG tags are found in the EXIF table. The tables below define only
|
---|
273 | information found within structures of these main DNG tag values.
|
---|
274 | },
|
---|
275 | MPEG => q{
|
---|
276 | The MPEG format doesn't specify any file-level meta information. In lieu of
|
---|
277 | this, information is extracted from the first audio and video frame headers
|
---|
278 | in the file.
|
---|
279 | },
|
---|
280 | Real => q{
|
---|
281 | ExifTool recognizes three basic types of Real audio/video files: 1)
|
---|
282 | RealMedia (RM, RV and RMVB), 2) RealAudio (RA), and 3) Real Metafile (RAM
|
---|
283 | and RPM).
|
---|
284 | },
|
---|
285 | Extra => q{
|
---|
286 | The extra tags represent information found in the image but not associated
|
---|
287 | with any other tag group. The three writable "pseudo" tags (Filename,
|
---|
288 | Directory and FileModifyDate) may be written without the need to rewrite the
|
---|
289 | file since their values are not contained within the file data.
|
---|
290 | },
|
---|
291 | Composite => q{
|
---|
292 | The values of the composite tags are derived from the values of other tags.
|
---|
293 | These are convenience tags which are calculated after all other information
|
---|
294 | is extracted.
|
---|
295 | },
|
---|
296 | Shortcuts => q{
|
---|
297 | Shortcut tags are convenience tags that represent one or more other tag
|
---|
298 | names. They are used like regular tags to read and write the information
|
---|
299 | for a specified set of tags.
|
---|
300 |
|
---|
301 | The shortcut tags below have been pre-defined, but user-defined shortcuts
|
---|
302 | may be added via the %Image::ExifTool::Shortcuts::UserDefined lookup in the
|
---|
303 | ~/.ExifTool_config file. See the Image::ExifTool::Shortcuts documentation
|
---|
304 | for more details.
|
---|
305 | },
|
---|
306 | PodTrailer => q{
|
---|
307 | ~head1 NOTES
|
---|
308 |
|
---|
309 | This document generated automatically by
|
---|
310 | L<Image::ExifTool::BuildTagLookup|Image::ExifTool::BuildTagLookup>.
|
---|
311 |
|
---|
312 | ~head1 AUTHOR
|
---|
313 |
|
---|
314 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
315 |
|
---|
316 | This library is free software; you can redistribute it and/or modify it
|
---|
317 | under the same terms as Perl itself.
|
---|
318 |
|
---|
319 | ~head1 SEE ALSO
|
---|
320 |
|
---|
321 | L<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
|
---|
331 | sub 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 | }
|
---|
428 | TagID: 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
|
---|
696 | sub 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)
|
---|
827 | sub 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
|
---|
851 | sub 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
|
---|
861 | sub Doc2Html($)
|
---|
862 | {
|
---|
863 | my $doc = EscapeHTML(shift);
|
---|
864 | $doc =~ s/\n\n/<\/p>\n\n<p>/g;
|
---|
865 | $doc =~ s/B<(.*?)>/<b>$1<\/b>/sg;
|
---|
866 | $doc =~ s/C<(.*?)>/<code>$1<\/code>/sg;
|
---|
867 | $doc =~ s/L<(.*?)>/<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
|
---|
874 | sub 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
|
---|
981 | my %createdFiles;
|
---|
982 | sub 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
|
---|
1025 | sub 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'><-- Back to ExifTool home page</a></p>\n";
|
---|
1053 | } else {
|
---|
1054 | print HTMLFILE "'index.html'><-- 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
|
---|
1102 | sub 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 = ' ';
|
---|
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/ / /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, "--> <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, ' ';
|
---|
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 |
|
---|
1501 | 1; # end
|
---|
1502 |
|
---|
1503 |
|
---|
1504 | __END__
|
---|
1505 |
|
---|
1506 | =head1 NAME
|
---|
1507 |
|
---|
1508 | Image::ExifTool::BuildTagLookup - Build ExifTool tag lookup tables
|
---|
1509 |
|
---|
1510 | =head1 DESCRIPTION
|
---|
1511 |
|
---|
1512 | This module is used to generate the tag lookup tables in
|
---|
1513 | Image::ExifTool::TagLookup.pm and tag name documentation in
|
---|
1514 | Image::ExifTool::TagNames.pod, as well as HTML tag name documentation. It
|
---|
1515 | is used before each new ExifTool release to update the lookup tables and
|
---|
1516 | documentation.
|
---|
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 |
|
---|
1530 | Copyright 2003-2007, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
1531 |
|
---|
1532 | This library is free software; you can redistribute it and/or modify it
|
---|
1533 | under the same terms as Perl itself.
|
---|
1534 |
|
---|
1535 | =head1 SEE ALSO
|
---|
1536 |
|
---|
1537 | L<Image::ExifTool(3pm)|Image::ExifTool>,
|
---|
1538 | L<Image::ExifTool::TagLookup(3pm)|Image::ExifTool::TagLookup>,
|
---|
1539 | L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>
|
---|
1540 |
|
---|
1541 | =cut
|
---|