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::configFile = '';
|
---|
22 | $Image::ExifTool::debug = 1; # enabled debug messages
|
---|
23 | }
|
---|
24 |
|
---|
25 | use vars qw($VERSION @ISA);
|
---|
26 | use Image::ExifTool qw(:Utils :Vars);
|
---|
27 | use Image::ExifTool::Exif;
|
---|
28 | use Image::ExifTool::Shortcuts;
|
---|
29 | use Image::ExifTool::HTML qw(EscapeHTML);
|
---|
30 | use Image::ExifTool::IPTC;
|
---|
31 | use Image::ExifTool::XMP;
|
---|
32 | use Image::ExifTool::Canon;
|
---|
33 | use Image::ExifTool::Nikon;
|
---|
34 |
|
---|
35 | $VERSION = '2.28';
|
---|
36 | @ISA = qw(Exporter);
|
---|
37 |
|
---|
38 | sub NumbersFirst;
|
---|
39 |
|
---|
40 | my $numbersFirst = 1; # set to -1 to sort numbers last
|
---|
41 |
|
---|
42 | # list of all tables in plug-in modules
|
---|
43 | my @pluginTables = ('Image::ExifTool::MWG::Composite');
|
---|
44 |
|
---|
45 | # colors for html pages
|
---|
46 | my $noteFont = "<span class=n>";
|
---|
47 | my $noteFontSmall = "<span class='n s'>";
|
---|
48 |
|
---|
49 | my $docType = q{<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
---|
50 | "http://www.w3.org/TR/html4/loose.dtd">
|
---|
51 | };
|
---|
52 |
|
---|
53 | my $homePage = 'http://owl.phy.queensu.ca/~phil/exiftool';
|
---|
54 |
|
---|
55 | # list of all recognized Format strings
|
---|
56 | # (not a complete list, but this is all we use so far)
|
---|
57 | # (also, formats like "var_X[Y]" are allowed for any valid X)
|
---|
58 | my %formatOK = (
|
---|
59 | %Image::ExifTool::Exif::formatNumber,
|
---|
60 | 0 => 1,
|
---|
61 | 1 => 1,
|
---|
62 | real => 1,
|
---|
63 | integer => 1,
|
---|
64 | date => 1,
|
---|
65 | boolean => 1,
|
---|
66 | rational => 1,
|
---|
67 | 'lang-alt' => 1,
|
---|
68 | fixed16u => 1,
|
---|
69 | fixed16s => 1,
|
---|
70 | fixed32u => 1,
|
---|
71 | fixed32s => 1,
|
---|
72 | extended => 1,
|
---|
73 | resize => 1,
|
---|
74 | digits => 1,
|
---|
75 | int16uRev => 1,
|
---|
76 | rational32u => 1,
|
---|
77 | rational32s => 1,
|
---|
78 | var_string => 1,
|
---|
79 | var_int16u => 1,
|
---|
80 | var_pstr32 => 1,
|
---|
81 | # Matroska
|
---|
82 | signed => 1,
|
---|
83 | unsigned => 1,
|
---|
84 | utf8 => 1,
|
---|
85 | );
|
---|
86 |
|
---|
87 | my $caseInsensitive; # flag to ignore case when sorting tag names
|
---|
88 |
|
---|
89 | # Descriptions for the TagNames documentation
|
---|
90 | # (descriptions may also be defined in tag table NOTES)
|
---|
91 | # Note: POD headers in these descriptions start with '~' instead of '=' to keep
|
---|
92 | # from confusing POD parsers which apparently parse inside quoted strings.
|
---|
93 | my %docs = (
|
---|
94 | PodHeader => q{
|
---|
95 | ~head1 NAME
|
---|
96 |
|
---|
97 | Image::ExifTool::TagNames - ExifTool tag name documentation
|
---|
98 |
|
---|
99 | ~head1 DESCRIPTION
|
---|
100 |
|
---|
101 | This document contains a complete list of ExifTool tag names, organized into
|
---|
102 | tables based on information type. Tag names are used to reference specific
|
---|
103 | meta information extracted from or written to a file.
|
---|
104 |
|
---|
105 | ~head1 TAG TABLES
|
---|
106 | },
|
---|
107 | ExifTool => q{
|
---|
108 | The tables listed below give the names of all tags recognized by ExifTool.
|
---|
109 | },
|
---|
110 | ExifTool2 => q{
|
---|
111 | B<Tag ID>, B<Index> or B<Sequence> is given in the first column of each
|
---|
112 | table. A B<Tag ID> is the computer-readable equivalent of a tag name, and
|
---|
113 | is the identifier that is actually stored in the file. An B<Index> refers
|
---|
114 | to the location of a value when found at a fixed position within a data
|
---|
115 | block, and B<Sequence> gives the order of values for a serial data stream.
|
---|
116 |
|
---|
117 | A B<Tag Name> is the handle by which the information is accessed in
|
---|
118 | ExifTool. In some instances, more than one name may correspond to a single
|
---|
119 | tag ID. In these cases, the actual name used depends on the context in
|
---|
120 | which the information is found. Case is not significant for tag names. A
|
---|
121 | question mark (C<?>) after a tag name indicates that the information is
|
---|
122 | either not understood, not verified, or not very useful -- these tags are
|
---|
123 | not extracted by ExifTool unless the Unknown (-u) option is enabled. Be
|
---|
124 | aware that some tag names are different than the descriptions printed out by
|
---|
125 | default when extracting information with exiftool. To see the tag names
|
---|
126 | instead of the descriptions, use C<exiftool -s>.
|
---|
127 |
|
---|
128 | The B<Writable> column indicates whether the tag is writable by ExifTool.
|
---|
129 | Anything but an C<N> in this column means the tag is writable. A C<Y>
|
---|
130 | indicates writable information that is either unformatted or written using
|
---|
131 | the existing format. Other expressions give details about the information
|
---|
132 | format, and vary depending on the general type of information. The format
|
---|
133 | name may be followed by a number in square brackets to indicate the number
|
---|
134 | of values written, or the number of characters in a fixed-length string
|
---|
135 | (including a null terminator which is added if required).
|
---|
136 |
|
---|
137 | A plus sign (C<+>) after an entry in the B<Writable> column indicates a
|
---|
138 | "list" tag which supports multiple values and allows individual values to be
|
---|
139 | added and deleted. A slash (C</>) indicates an "avoided" tag that is not
|
---|
140 | created when writing if another same-named tag may be created instead. To
|
---|
141 | write these tags, the group should be specified. A tilde (C<~>) indicates a
|
---|
142 | tag this is writable only when the print conversion is disabled (by setting
|
---|
143 | PrintConv to 0, using the -n option, or suffixing the tag name with a C<#>
|
---|
144 | character). An exclamation point (C<!>) indicates a tag that is considered
|
---|
145 | unsafe to write under normal circumstances. These "unsafe" tags are not set
|
---|
146 | when calling SetNewValuesFromFile() or copied with the exiftool
|
---|
147 | -tagsFromFile option unless specified explicitly, and care should be taken
|
---|
148 | when editing them manually since they may affect the way an image is
|
---|
149 | rendered. An asterisk (C<*>) indicates a "protected" tag which is not
|
---|
150 | writable directly, but is written automatically by ExifTool (often when a
|
---|
151 | corresponding Composite or Extra tag is written). A colon (C<:>) indicates
|
---|
152 | a mandatory tag which may be added automatically when writing.
|
---|
153 |
|
---|
154 | The HTML version of these tables also lists possible B<Values> for
|
---|
155 | discrete-valued tags, as well as B<Notes> for some tags. The B<Values> are
|
---|
156 | listed as the computer-readable and human-readable values on the left and
|
---|
157 | right hand side of an equals sign (C<=>) respectively. The human-readable
|
---|
158 | values are used by default when reading and writing, but the
|
---|
159 | computer-readable values may be accessed by disabling the value conversion
|
---|
160 | with the -n option on the command line, by setting the ValueConv option to 0
|
---|
161 | in the API, or or on a per-tag basis by appending a number symbol (C<#>) to
|
---|
162 | the tag name.
|
---|
163 |
|
---|
164 | B<Note>: If you are familiar with common meta-information tag names, you may
|
---|
165 | find that some ExifTool tag names are different than expected. The usual
|
---|
166 | reason for this is to make the tag names more consistent across different
|
---|
167 | types of meta information. To determine a tag name, either consult this
|
---|
168 | documentation or run C<exiftool -s> on a file containing the information in
|
---|
169 | question.
|
---|
170 | },
|
---|
171 | EXIF => q{
|
---|
172 | EXIF stands for "Exchangeable Image File Format". This type of information
|
---|
173 | is formatted according to the TIFF specification, and may be found in JPG,
|
---|
174 | TIFF, PNG, PGF, MIFF, HDP, PSP and XCF images, as well as many TIFF-based
|
---|
175 | RAW images, and even some AVI and MOV videos.
|
---|
176 |
|
---|
177 | The EXIF meta information is organized into different Image File Directories
|
---|
178 | (IFD's) within an image. The names of these IFD's correspond to the
|
---|
179 | ExifTool family 1 group names. When writing EXIF information, the default
|
---|
180 | B<Group> listed below is used unless another group is specified.
|
---|
181 |
|
---|
182 | The table below lists all EXIF tags. Also listed are TIFF, DNG, HDP and
|
---|
183 | other tags which are not part of the EXIF specification, but may co-exist
|
---|
184 | with EXIF tags in some images. Tags which are part of the EXIF 2.3
|
---|
185 | specification have an underlined B<Tag Name> in the HTML version of this
|
---|
186 | documentation. See
|
---|
187 | L<http://www.cipa.jp/english/hyoujunka/kikaku/pdf/DC-008-2010_E.pdf> for the
|
---|
188 | official EXIF 2.3 specification.
|
---|
189 | },
|
---|
190 | GPS => q{
|
---|
191 | These GPS tags are part of the EXIF standard, and are stored in a separate
|
---|
192 | IFD within the EXIF information.
|
---|
193 |
|
---|
194 | ExifTool is very flexible about the input format when writing lat/long
|
---|
195 | coordinates, and will accept from 1 to 3 floating point numbers (for decimal
|
---|
196 | degrees, degrees and minutes, or degrees, minutes and seconds) separated by
|
---|
197 | just about anything, and will format them properly according to the EXIF
|
---|
198 | specification.
|
---|
199 |
|
---|
200 | Some GPS tags have values which are fixed-length strings. For these, the
|
---|
201 | indicated string lengths include a null terminator which is added
|
---|
202 | automatically by ExifTool. Remember that the descriptive values are used
|
---|
203 | when writing (ie. 'Above Sea Level', not '0') unless the print conversion is
|
---|
204 | disabled (with '-n' on the command line or the PrintConv option in the API,
|
---|
205 | or by suffixing the tag name with a C<#> character).
|
---|
206 |
|
---|
207 | When adding GPS information to an image, it is important to set all of the
|
---|
208 | following tags: GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef,
|
---|
209 | GPSAltitude and GPSAltitudeRef. ExifTool will write the required
|
---|
210 | GPSVersionID tag automatically if new a GPS IFD is added to an image.
|
---|
211 | },
|
---|
212 | XMP => q{
|
---|
213 | XMP stands for "Extensible Metadata Platform", an XML/RDF-based metadata
|
---|
214 | format which is being pushed by Adobe. Information in this format can be
|
---|
215 | embedded in many different image file types including JPG, JP2, TIFF, GIF,
|
---|
216 | EPS, PDF, PSD, IND, PNG, DJVU, SVG, PGF, MIFF, XCF, CRW, DNG and a variety
|
---|
217 | of proprietary TIFF-based RAW images, as well as MOV, AVI, ASF, WMV, FLV,
|
---|
218 | SWF and MP4 videos, and WMA and audio formats supporting ID3v2 information.
|
---|
219 |
|
---|
220 | The XMP B<Tag ID>'s aren't listed because in most cases they are identical
|
---|
221 | to the B<Tag Name> (aside from differences in case). Tags with different
|
---|
222 | ID's are mentioned in the B<Notes> column of the HTML version of this
|
---|
223 | document.
|
---|
224 |
|
---|
225 | All XMP information is stored as character strings. The B<Writable> column
|
---|
226 | specifies the information format: C<string> is an unformatted string,
|
---|
227 | C<integer> is a string of digits (possibly beginning with a '+' or '-'),
|
---|
228 | C<real> is a floating point number, C<rational> is entered as a floating
|
---|
229 | point number but stored as two C<integer> strings separated by a '/'
|
---|
230 | character, C<date> is a date/time string entered in the format "YYYY:mm:dd
|
---|
231 | HH:MM:SS[.ss][+/-HH:MM]", C<boolean> is either "True" or "False",
|
---|
232 | C<lang-alt> indicates that the tag supports alternate languages (see below),
|
---|
233 | and C<struct> is an XMP structure. When reading, structures are extracted
|
---|
234 | only if the Struct (-struct) option is used. Otherwise the corresponding
|
---|
235 | "flattened" tags, indicated by an underline (C<_>) after the B<Writable>
|
---|
236 | type, are extracted. When copying information, the Struct option is in
|
---|
237 | effect by default. When writing, the Struct option has no effect, and both
|
---|
238 | structured and flattened tags may be written. See
|
---|
239 | L<http://owl.phy.queensu.ca/~phil/exiftool/struct.html> for more details.
|
---|
240 |
|
---|
241 | Individual languages for C<lang-alt> tags are accessed by suffixing the tag
|
---|
242 | name with a '-', followed by an RFC 3066 language code (ie. "XMP:Title-fr",
|
---|
243 | or "Rights-en-US"). (See L<http://www.ietf.org/rfc/rfc3066.txt> for the RFC
|
---|
244 | 3066 specification.) A C<lang-alt> tag with no language code accesses the
|
---|
245 | "x-default" language, but causes other languages for this tag to be deleted
|
---|
246 | when writing. The "x-default" language code may be specified when writing
|
---|
247 | to preserve other existing languages (ie. "XMP-dc:Description-x-default").
|
---|
248 | When reading, "x-default" is not specified.
|
---|
249 |
|
---|
250 | The XMP tags are organized according to schema B<Namespace> in the following
|
---|
251 | tables. Note that a few of the longer namespace prefixes given below have
|
---|
252 | been shortened for convenience (since the family 1 group names are derived
|
---|
253 | from these by adding a leading "XMP-"). In cases where a tag name exists in
|
---|
254 | more than one namespace, less common namespaces are avoided when writing.
|
---|
255 | However, any namespace may be written by specifying a family 1 group name
|
---|
256 | for the tag, ie) XMP-exif:Contrast or XMP-crs:Contrast. When deciding on
|
---|
257 | which tags to add to an image, using standard schemas such as
|
---|
258 | L<dc|/XMP dc Tags>, L<xmp|/XMP xmp Tags> or L<iptc|/XMP iptcCore Tags> is
|
---|
259 | recommended if possible.
|
---|
260 |
|
---|
261 | For structures, the heading of the first column is B<Field Name>. Field
|
---|
262 | names are very similar to tag names, except they are used to identify fields
|
---|
263 | inside structures instead of stand-alone tags. See
|
---|
264 | L<the Field Name section of the Structured Information documentation|http://owl.phy.queensu.ca/~phil/exiftool/struct.html#Fields> for more
|
---|
265 | details.
|
---|
266 |
|
---|
267 | ExifTool will extract XMP information even if it is not listed in these
|
---|
268 | tables. For example, the C<pdfx> namespace doesn't have a predefined set of
|
---|
269 | tag names because it is used to store application-defined PDF information,
|
---|
270 | but this information is extracted by ExifTool.
|
---|
271 |
|
---|
272 | See L<http://www.adobe.com/devnet/xmp/> for the official XMP specification.
|
---|
273 | },
|
---|
274 | IPTC => q{
|
---|
275 | The tags listed below are part of the International Press Telecommunications
|
---|
276 | Council (IPTC) and the Newspaper Association of America (NAA) Information
|
---|
277 | Interchange Model (IIM). This is an older meta information format, slowly
|
---|
278 | being phased out in favor of XMP. (In fact, the newer IPTCCore
|
---|
279 | specification actually uses XMP format!) IPTC information may be embedded
|
---|
280 | in JPG, TIFF, PNG, MIFF, PS, PDF, PSD, XCF and DNG images.
|
---|
281 |
|
---|
282 | IPTC information is separated into different records, each of which has its
|
---|
283 | own set of tags. See
|
---|
284 | L<http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf> for the
|
---|
285 | official specification.
|
---|
286 |
|
---|
287 | This specification dictates a length for ASCII (C<string> or C<digits>) and
|
---|
288 | binary (C<undef>) values. These lengths are given in square brackets after
|
---|
289 | the B<Writable> format name. For tags where a range of lengths is allowed,
|
---|
290 | the minimum and maximum lengths are separated by a comma within the
|
---|
291 | brackets. IPTC strings are not null terminated. When writing, ExifTool
|
---|
292 | issues a minor warning and truncates the value if it is longer than allowed
|
---|
293 | by the IPTC specification. Minor errors may be ignored with the
|
---|
294 | IgnoreMinorErrors (-m) option, allowing longer values to be written, but
|
---|
295 | beware that values like this may cause problems for some other IPTC readers.
|
---|
296 |
|
---|
297 | Separate IPTC date and time tags may be written with a combined date/time
|
---|
298 | value and ExifTool automagically takes the appropriate part of the date/time
|
---|
299 | string depending on whether a date or time tag is being written. This is
|
---|
300 | very useful when copying date/time values to IPTC from other metadata
|
---|
301 | formats.
|
---|
302 |
|
---|
303 | IPTC time values include a timezone offset. If written with a value which
|
---|
304 | doesn't include a timezone then the current local timezone offset is used
|
---|
305 | (unless written with a combined date/time, in which case the local timezone
|
---|
306 | offset at the specified date/time is used, which may be different due to
|
---|
307 | changes in daylight savings time).
|
---|
308 | },
|
---|
309 | Photoshop => q{
|
---|
310 | Photoshop tags are found in PSD and PSB files, as well as inside embedded
|
---|
311 | Photoshop information in many other file types (JPEG, TIFF, PDF, PNG to name
|
---|
312 | a few).
|
---|
313 |
|
---|
314 | Many Photoshop tags are marked as Unknown (indicated by a question mark
|
---|
315 | after the tag name) because the information they provide is not very useful
|
---|
316 | under normal circumstances I<[and because Adobe denied my application for
|
---|
317 | their file format documentation -- apparently open source software is too
|
---|
318 | big a concept for them]>. These unknown tags are not extracted unless the
|
---|
319 | Unknown (-u) option is used.
|
---|
320 | },
|
---|
321 | PrintIM => q{
|
---|
322 | The format of the PrintIM information is known, however no PrintIM tags have
|
---|
323 | been decoded. Use the Unknown (-u) option to extract PrintIM information.
|
---|
324 | },
|
---|
325 | GeoTiff => q{
|
---|
326 | ExifTool extracts the following tags from GeoTIFF images. See
|
---|
327 | L<http://www.remotesensing.org/geotiff/spec/geotiffhome.html> for the
|
---|
328 | complete GeoTIFF specification.
|
---|
329 | },
|
---|
330 | JFIF => q{
|
---|
331 | The following information is extracted from the JPEG JFIF header. See
|
---|
332 | L<http://www.jpeg.org/public/jfif.pdf> for the JFIF 1.02 specification.
|
---|
333 | },
|
---|
334 | Kodak => q{
|
---|
335 | Many Kodak models don't store the maker notes in standard IFD format, and
|
---|
336 | these formats vary with different models. Some information has been
|
---|
337 | decoded, but much of the Kodak information remains unknown.
|
---|
338 | },
|
---|
339 | 'Kodak SpecialEffects' => q{
|
---|
340 | The Kodak SpecialEffects and Borders tags are found in sub-IFD's within the
|
---|
341 | Kodak JPEG APP3 "Meta" segment.
|
---|
342 | },
|
---|
343 | Minolta => q{
|
---|
344 | These tags are used by Minolta, Konica/Minolta as well as some Sony cameras.
|
---|
345 | Minolta doesn't make things easy for decoders because the meaning of some
|
---|
346 | tags and the location where some information is stored is different for
|
---|
347 | different camera models. (Take MinoltaQuality for example, which may be
|
---|
348 | located in 5 different places.)
|
---|
349 | },
|
---|
350 | Olympus => q{
|
---|
351 | Tags 0x0000 through 0x0103 are used by some older Olympus cameras, and are
|
---|
352 | the same as Konica/Minolta tags. The Olympus tags are also used for Epson
|
---|
353 | and Agfa cameras.
|
---|
354 | },
|
---|
355 | Panasonic => q{
|
---|
356 | These tags are used in Panasonic/Leica cameras.
|
---|
357 | },
|
---|
358 | Pentax => q{
|
---|
359 | These tags are used in Pentax/Asahi cameras.
|
---|
360 | },
|
---|
361 | Sigma => q{
|
---|
362 | These tags are used in Sigma/Foveon cameras.
|
---|
363 | },
|
---|
364 | Sony => q{
|
---|
365 | The maker notes in images from most recent Sony camera models contain a
|
---|
366 | wealth of information, but for some models very little has been decoded.
|
---|
367 | Use the ExifTool Unknown (-u) or Verbose (-v) options to see information
|
---|
368 | about the unknown tags. Also see the Minolta tags which are used by some
|
---|
369 | Sony models.
|
---|
370 | },
|
---|
371 | CanonRaw => q{
|
---|
372 | These tags apply to CRW-format Canon RAW files and information in the APP0
|
---|
373 | "CIFF" segment of JPEG images. When writing CanonRaw/CIFF information, the
|
---|
374 | length of the information is preserved (and the new information is truncated
|
---|
375 | or padded as required) unless B<Writable> is C<resize>. Currently, only
|
---|
376 | JpgFromRaw and ThumbnailImage are allowed to change size.
|
---|
377 |
|
---|
378 | CRW images also support the addition of a CanonVRD trailer, which in turn
|
---|
379 | supports XMP. This trailer is created automatically if necessary when
|
---|
380 | ExifTool is used to write XMP to a CRW image.
|
---|
381 | },
|
---|
382 | Unknown => q{
|
---|
383 | The following tags are decoded in unsupported maker notes. Use the Unknown
|
---|
384 | (-u) option to display other unknown tags.
|
---|
385 | },
|
---|
386 | PDF => q{
|
---|
387 | The tags listed in the PDF tables below are those which are used by ExifTool
|
---|
388 | to extract meta information, but they are only a small fraction of the total
|
---|
389 | number of available PDF tags. See
|
---|
390 | L<http://www.adobe.com/devnet/pdf/pdf_reference.html> for the official PDF
|
---|
391 | specification.
|
---|
392 |
|
---|
393 | ExifTool supports reading and writing PDF documents up to version 1.7
|
---|
394 | extension level 3, including support for RC4, AES-128 and AES-256
|
---|
395 | encryption. A Password option is provided to allow processing of
|
---|
396 | password-protected PDF files.
|
---|
397 |
|
---|
398 | When writing PDF files, ExifTool uses an incremental update. This has the
|
---|
399 | advantages of being fast and reversible. The original PDF can be easily
|
---|
400 | recovered by deleting the C<PDF-update> pseudo-group (with
|
---|
401 | C<-PDF-update:all=> on the command line). But there are two main
|
---|
402 | disadvantages to this technique:
|
---|
403 |
|
---|
404 | 1) A linearized PDF file is no longer linearized after the update, so it
|
---|
405 | must be subsequently re-linearized if this is required.
|
---|
406 |
|
---|
407 | 2) All metadata edits are reversible. While this would normally be
|
---|
408 | considered an advantage, it is a potential security problem because old
|
---|
409 | information is never actually deleted from the file.
|
---|
410 | },
|
---|
411 | DNG => q{
|
---|
412 | The main DNG tags are found in the EXIF table. The tables below define only
|
---|
413 | information found within structures of these main DNG tag values. See
|
---|
414 | L<http://www.adobe.com/products/dng/> for the official DNG specification.
|
---|
415 | },
|
---|
416 | MPEG => q{
|
---|
417 | The MPEG format doesn't specify any file-level meta information. In lieu of
|
---|
418 | this, information is extracted from the first audio and video frame headers
|
---|
419 | in the file.
|
---|
420 | },
|
---|
421 | Real => q{
|
---|
422 | ExifTool recognizes three basic types of Real audio/video files: 1)
|
---|
423 | RealMedia (RM, RV and RMVB), 2) RealAudio (RA), and 3) Real Metafile (RAM
|
---|
424 | and RPM).
|
---|
425 | },
|
---|
426 | Extra => q{
|
---|
427 | The extra tags represent extra information extracted or generated by
|
---|
428 | ExifTool that is not directly associated with another tag group. The three
|
---|
429 | writable "pseudo" tags (FileName, Directory and FileModifyDate) may be
|
---|
430 | written without the need to rewrite the file since their values are not
|
---|
431 | contained within the file data. These "pseudo" tags belong to the family 1
|
---|
432 | "System" group.
|
---|
433 | },
|
---|
434 | Composite => q{
|
---|
435 | The values of the composite tags are B<Derived From> the values of other
|
---|
436 | tags. These are convenience tags which are calculated after all other
|
---|
437 | information is extracted.
|
---|
438 | },
|
---|
439 | Shortcuts => q{
|
---|
440 | Shortcut tags are convenience tags that represent one or more other tag
|
---|
441 | names. They are used like regular tags to read and write the information
|
---|
442 | for a specified set of tags.
|
---|
443 |
|
---|
444 | The shortcut tags below have been pre-defined, but user-defined shortcuts
|
---|
445 | may be added via the %Image::ExifTool::UserDefined::Shortcuts lookup in the
|
---|
446 | ~/.ExifTool_config file. See the Image::ExifTool::Shortcuts documentation
|
---|
447 | for more details.
|
---|
448 | },
|
---|
449 | PodTrailer => q{
|
---|
450 | ~head1 NOTES
|
---|
451 |
|
---|
452 | This document generated automatically by
|
---|
453 | L<Image::ExifTool::BuildTagLookup|Image::ExifTool::BuildTagLookup>.
|
---|
454 |
|
---|
455 | ~head1 AUTHOR
|
---|
456 |
|
---|
457 | Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
458 |
|
---|
459 | This library is free software; you can redistribute it and/or modify it
|
---|
460 | under the same terms as Perl itself.
|
---|
461 |
|
---|
462 | ~head1 SEE ALSO
|
---|
463 |
|
---|
464 | L<Image::ExifTool(3pm)|Image::ExifTool>
|
---|
465 |
|
---|
466 | ~cut
|
---|
467 | },
|
---|
468 | );
|
---|
469 |
|
---|
470 | # notes for Shortcuts tags
|
---|
471 | my %shortcutNotes = (
|
---|
472 | MakerNotes => q{
|
---|
473 | useful when copying tags between files to either copy the maker notes as a
|
---|
474 | block or prevent it from being copied
|
---|
475 | },
|
---|
476 | CommonIFD0 => q{
|
---|
477 | common metadata tags found in IFD0 of TIFF-format images. Used to simpify
|
---|
478 | deletion of all metadata from these images. See FAQ number 7 for details
|
---|
479 | },
|
---|
480 | Unsafe => q{
|
---|
481 | "unsafe" tags in JPEG images which are normally not copied. Defined here
|
---|
482 | as a shortcut to use when rebuilding JPEG EXIF from scratch
|
---|
483 | },
|
---|
484 | );
|
---|
485 |
|
---|
486 |
|
---|
487 |
|
---|
488 | # EXIF table tag ID's which are part of the EXIF 2.3 specification
|
---|
489 | # (used only to add underlines in HTML version of EXIF Tag Table)
|
---|
490 | my %exifSpec = (
|
---|
491 | 0x100 => 1, 0x212 => 1, 0x9204 => 1, 0xa217 => 1,
|
---|
492 | 0x101 => 1, 0x213 => 1, 0x9205 => 1, 0xa300 => 1,
|
---|
493 | 0x102 => 1, 0x214 => 1, 0x9206 => 1, 0xa301 => 1,
|
---|
494 | 0x103 => 1, 0x8298 => 1, 0x9207 => 1, 0xa302 => 1,
|
---|
495 | 0x106 => 1, 0x829a => 1, 0x9208 => 1, 0xa401 => 1,
|
---|
496 | 0x10e => 1, 0x829d => 1, 0x9209 => 1, 0xa402 => 1,
|
---|
497 | 0x10f => 1, 0x8769 => 1, 0x920a => 1, 0xa403 => 1,
|
---|
498 | 0x110 => 1, 0x8822 => 1, 0x9214 => 1, 0xa404 => 1,
|
---|
499 | 0x111 => 1, 0x8824 => 1, 0x927c => 1, 0xa405 => 1,
|
---|
500 | 0x112 => 1, 0x8825 => 1, 0x9286 => 1, 0xa406 => 1,
|
---|
501 | 0x115 => 1, 0x8827 => 1, 0x9290 => 1, 0xa407 => 1,
|
---|
502 | 0x116 => 1, 0x8828 => 1, 0x9291 => 1, 0xa408 => 1,
|
---|
503 | 0x117 => 1, 0x8830 => 1, 0x9292 => 1, 0xa409 => 1,
|
---|
504 | 0x11a => 1, 0x8831 => 1, 0xa000 => 1, 0xa40a => 1,
|
---|
505 | 0x11b => 1, 0x8832 => 1, 0xa001 => 1, 0xa40b => 1,
|
---|
506 | 0x11c => 1, 0x8833 => 1, 0xa002 => 1, 0xa40c => 1,
|
---|
507 | 0x128 => 1, 0x8834 => 1, 0xa003 => 1, 0xa420 => 1,
|
---|
508 | 0x12d => 1, 0x8835 => 1, 0xa004 => 1, 0xa430 => 1,
|
---|
509 | 0x131 => 1, 0x9000 => 1, 0xa005 => 1, 0xa431 => 1,
|
---|
510 | 0x132 => 1, 0x9003 => 1, 0xa20b => 1, 0xa432 => 1,
|
---|
511 | 0x13b => 1, 0x9004 => 1, 0xa20c => 1, 0xa433 => 1,
|
---|
512 | 0x13e => 1, 0x9101 => 1, 0xa20e => 1, 0xa434 => 1,
|
---|
513 | 0x13f => 1, 0x9102 => 1, 0xa20f => 1, 0xa435 => 1,
|
---|
514 | 0x201 => 1, 0x9201 => 1, 0xa210 => 1,
|
---|
515 | 0x202 => 1, 0x9202 => 1, 0xa214 => 1,
|
---|
516 | 0x211 => 1, 0x9203 => 1, 0xa215 => 1,
|
---|
517 | );
|
---|
518 | # same thing for RIFF INFO tags found in the EXIF spec
|
---|
519 | my %riffSpec = (
|
---|
520 | IARL => 1, ICRD => 1, IGNR => 1, IPLT => 1, ISRC => 1,
|
---|
521 | IART => 1, ICRP => 1, IKEY => 1, IPRD => 1, ISRF => 1,
|
---|
522 | ICMS => 1, IDIM => 1, ILGT => 1, ISBJ => 1, ITCH => 1,
|
---|
523 | ICMT => 1, IDPI => 1, IMED => 1, ISFT => 1,
|
---|
524 | ICOP => 1, IENG => 1, INAM => 1, ISHP => 1,
|
---|
525 | );
|
---|
526 |
|
---|
527 | #------------------------------------------------------------------------------
|
---|
528 | # New - create new BuildTagLookup object
|
---|
529 | # Inputs: 0) reference to BuildTagLookup object or BuildTagLookup class name
|
---|
530 | sub new
|
---|
531 | {
|
---|
532 | local $_;
|
---|
533 | my $that = shift;
|
---|
534 | my $class = ref($that) || $that || 'Image::ExifTool::BuildTagLookup';
|
---|
535 | my $self = bless {}, $class;
|
---|
536 | my (%subdirs, %isShortcut, %allStructs);
|
---|
537 | my %count = (
|
---|
538 | 'unique tag names' => 0,
|
---|
539 | 'total tags' => 0,
|
---|
540 | );
|
---|
541 | #
|
---|
542 | # loop through all tables, accumulating TagLookup and TagName information
|
---|
543 | #
|
---|
544 | my (%tagNameInfo, %id, %longID, %longName, %shortName, %tableNum,
|
---|
545 | %tagLookup, %tagExists, %tableWritable, %sepTable, %structs,
|
---|
546 | %compositeModules, %isPlugin, %flattened, %structLookup);
|
---|
547 | $self->{TAG_NAME_INFO} = \%tagNameInfo;
|
---|
548 | $self->{ID_LOOKUP} = \%id;
|
---|
549 | $self->{LONG_ID} = \%longID;
|
---|
550 | $self->{LONG_NAME} = \%longName;
|
---|
551 | $self->{SHORT_NAME} = \%shortName;
|
---|
552 | $self->{TABLE_NUM} = \%tableNum;
|
---|
553 | $self->{TAG_LOOKUP} = \%tagLookup;
|
---|
554 | $self->{TAG_EXISTS} = \%tagExists;
|
---|
555 | $self->{FLATTENED} = \%flattened;
|
---|
556 | $self->{TABLE_WRITABLE} = \%tableWritable;
|
---|
557 | $self->{SEPARATE_TABLE} = \%sepTable;
|
---|
558 | $self->{STRUCTURES} = \%structs;
|
---|
559 | $self->{STRUCT_LOOKUP} = \%structLookup; # lookup for Struct hash ref based on Struct name
|
---|
560 | $self->{COMPOSITE_MODULES} = \%compositeModules;
|
---|
561 | $self->{COUNT} = \%count;
|
---|
562 |
|
---|
563 | Image::ExifTool::LoadAllTables();
|
---|
564 | my @tableNames = sort keys %allTables;
|
---|
565 | # add Shortcuts after other tables
|
---|
566 | push @tableNames, 'Image::ExifTool::Shortcuts::Main';
|
---|
567 | # add plug-in modules last
|
---|
568 | $Image::ExifTool::documentOnly = 1; # (don't really load them)
|
---|
569 | foreach (@pluginTables) {
|
---|
570 | push @tableNames, $_;
|
---|
571 | $isPlugin{$_} = 1;
|
---|
572 | }
|
---|
573 |
|
---|
574 | my $tableNum = 0;
|
---|
575 | my $exifTool = new Image::ExifTool;
|
---|
576 | my ($tableName, $tag);
|
---|
577 | # create lookup for short table names
|
---|
578 | foreach $tableName (@tableNames) {
|
---|
579 | my $short = $tableName;
|
---|
580 | $short =~ s/^Image::ExifTool:://;
|
---|
581 | $short =~ s/::Main$//;
|
---|
582 | $short =~ s/::/ /;
|
---|
583 | $short =~ s/^(.+)Tags$/\u$1/ unless $short eq 'Nikon AVITags';
|
---|
584 | $short =~ s/^Exif\b/EXIF/;
|
---|
585 | $shortName{$tableName} = $short; # remember short name
|
---|
586 | $tableNum{$tableName} = $tableNum++;
|
---|
587 | }
|
---|
588 | # validate DICOM UID values
|
---|
589 | foreach (values %Image::ExifTool::DICOM::uid) {
|
---|
590 | next unless /[\0-\x1f\x7f-\xff]/;
|
---|
591 | warn "Warning: Special characters in DICOM UID value ($_)\n";
|
---|
592 | }
|
---|
593 | # make lookup table to check for shortcut tags
|
---|
594 | foreach $tag (keys %Image::ExifTool::Shortcuts::Main) {
|
---|
595 | my $entry = $Image::ExifTool::Shortcuts::Main{$tag};
|
---|
596 | # ignore if shortcut tag name includes itself
|
---|
597 | next if ref $entry eq 'ARRAY' and grep /^$tag$/, @$entry;
|
---|
598 | $isShortcut{lc($tag)} = 1;
|
---|
599 | }
|
---|
600 | foreach $tableName (@tableNames) {
|
---|
601 | # create short table name
|
---|
602 | my $short = $shortName{$tableName};
|
---|
603 | my $info = $tagNameInfo{$tableName} = [ ];
|
---|
604 | my $isPlugin = $isPlugin{$tableName};
|
---|
605 | my ($table, $shortcut, %isOffset, %datamember, %hasSubdir);
|
---|
606 | if ($short eq 'Shortcuts') {
|
---|
607 | # can't use GetTagTable() for Shortcuts (not a normal table)
|
---|
608 | $table = \%Image::ExifTool::Shortcuts::Main;
|
---|
609 | $shortcut = 1;
|
---|
610 | } elsif ($isPlugin) {
|
---|
611 | $table = GetTagTable($tableName);
|
---|
612 | # don't add to allTables list because this messes our table order
|
---|
613 | delete $allTables{$tableName};
|
---|
614 | pop @tableOrder;
|
---|
615 | } else {
|
---|
616 | $table = GetTagTable($tableName);
|
---|
617 | }
|
---|
618 | my $tableNum = $tableNum{$tableName};
|
---|
619 | my $writeProc = $$table{WRITE_PROC};
|
---|
620 | my $vars = $$table{VARS} || { };
|
---|
621 | $longID{$tableName} = 0;
|
---|
622 | $longName{$tableName} = 0;
|
---|
623 | # save all tag names
|
---|
624 | my ($tagID, $binaryTable, $noID, $isIPTC, $isXMP);
|
---|
625 | $isIPTC = 1 if $writeProc and $writeProc eq \&Image::ExifTool::IPTC::WriteIPTC;
|
---|
626 | # generate flattened tag names for structure fields if this is an XMP table
|
---|
627 | if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') {
|
---|
628 | $isXMP = 1;
|
---|
629 | foreach $tagID (TagTableKeys($table)) {
|
---|
630 | my $tagInfo = $$table{$tagID};
|
---|
631 | next unless ref $tagInfo eq 'HASH' and $$tagInfo{Struct};
|
---|
632 | Image::ExifTool::XMP::AddFlattenedTags($table, $tagID);
|
---|
633 | }
|
---|
634 | }
|
---|
635 | $noID = 1 if $isXMP or $short =~ /^(Shortcuts|ASF.*)$/ or $$vars{NO_ID};
|
---|
636 | my $processBinaryData = ($$table{PROCESS_PROC} and (
|
---|
637 | $$table{PROCESS_PROC} eq \&Image::ExifTool::ProcessBinaryData or
|
---|
638 | $$table{PROCESS_PROC} eq \&Image::ExifTool::Nikon::ProcessNikonEncrypted));
|
---|
639 | if ($$vars{ID_LABEL} or $processBinaryData) {
|
---|
640 | $binaryTable = 1;
|
---|
641 | $id{$tableName} = $$vars{ID_LABEL} || 'Index';
|
---|
642 | } elsif ($isIPTC and $$table{PROCESS_PROC}) { #only the main IPTC table has a PROCESS_PROC
|
---|
643 | $id{$tableName} = 'Record';
|
---|
644 | } elsif (not $noID) {
|
---|
645 | $id{$tableName} = 'Tag ID';
|
---|
646 | }
|
---|
647 | $caseInsensitive = $isXMP;
|
---|
648 | $numbersFirst = -1 if $$table{VARS} and $$table{VARS}{ALPHA_FIRST};
|
---|
649 | my @keys = sort NumbersFirst TagTableKeys($table);
|
---|
650 | $numbersFirst = 1;
|
---|
651 | my $defFormat = $table->{FORMAT};
|
---|
652 | # use default format for binary data tables
|
---|
653 | $defFormat = 'int8u' if not $defFormat and $binaryTable;
|
---|
654 |
|
---|
655 | TagID: foreach $tagID (@keys) {
|
---|
656 | my ($tagInfo, @tagNames, $subdir, $format, @values);
|
---|
657 | my (@infoArray, @require, @writeGroup, @writable);
|
---|
658 | if ($shortcut) {
|
---|
659 | # must build a dummy tagInfo list since Shortcuts is not a normal table
|
---|
660 | $tagInfo = {
|
---|
661 | Name => $tagID,
|
---|
662 | Notes => $shortcutNotes{$tagID},
|
---|
663 | Writable => 1,
|
---|
664 | Require => { },
|
---|
665 | };
|
---|
666 | my $i;
|
---|
667 | for ($i=0; $i<@{$$table{$tagID}}; ++$i) {
|
---|
668 | $tagInfo->{Require}->{$i} = $table->{$tagID}->[$i];
|
---|
669 | }
|
---|
670 | @infoArray = ( $tagInfo );
|
---|
671 | } else {
|
---|
672 | @infoArray = GetTagInfoList($table,$tagID);
|
---|
673 | }
|
---|
674 | $format = $defFormat;
|
---|
675 | foreach $tagInfo (@infoArray) {
|
---|
676 | my $name = $$tagInfo{Name};
|
---|
677 | # validate Name
|
---|
678 | warn "Warning: Invalid tag name $short '$name'\n" if $name !~ /^[-\w]+$/;
|
---|
679 | # accumulate information for consistency check of BinaryData tables
|
---|
680 | if ($processBinaryData and $$table{WRITABLE}) {
|
---|
681 | $isOffset{$tagID} = $name if $$tagInfo{IsOffset};
|
---|
682 | $hasSubdir{$tagID} = $name if $$tagInfo{SubDirectory};
|
---|
683 | # require DATAMEMBER for writable var-format tags, Hook and DataMember tags
|
---|
684 | if ($$tagInfo{Format} and $$tagInfo{Format} =~ /^var_/) {
|
---|
685 | $datamember{$tagID} = $name;
|
---|
686 | unless (defined $$tagInfo{Writable} and not $$tagInfo{Writable}) {
|
---|
687 | warn "Warning: Var-format tag is writable - $short $name\n"
|
---|
688 | }
|
---|
689 | } elsif ($$tagInfo{Hook} or ($$tagInfo{RawConv} and
|
---|
690 | $$tagInfo{RawConv} =~ /\$self(->)?\{\w+\}\s*=(?!~)/))
|
---|
691 | {
|
---|
692 | $datamember{$tagID} = $name;
|
---|
693 | }
|
---|
694 | }
|
---|
695 | if ($$tagInfo{Hidden}) {
|
---|
696 | warn "Warning: Hidden tag in list - $short $name\n" if @infoArray > 1;
|
---|
697 | next TagID;
|
---|
698 | }
|
---|
699 | my $writable;
|
---|
700 | if (defined $$tagInfo{Writable}) {
|
---|
701 | $writable = $$tagInfo{Writable};
|
---|
702 | # validate Writable
|
---|
703 | unless ($formatOK{$writable} or ($writable =~ /(.*)\[/ and $formatOK{$1})) {
|
---|
704 | warn "Warning: Unknown Writable ($writable) for $short $name\n",
|
---|
705 | }
|
---|
706 | } elsif (not $$tagInfo{SubDirectory}) {
|
---|
707 | $writable = $$table{WRITABLE};
|
---|
708 | }
|
---|
709 | # validate some characteristics of obvious date/time tags
|
---|
710 | if ($$tagInfo{PrintConv} and $$tagInfo{PrintConv} eq '$self->ConvertDateTime($val)') {
|
---|
711 | my @g = $exifTool->GetGroup($tagInfo);
|
---|
712 | warn "$short $name should be in 'Time' group!\n" unless $g[2] eq 'Time';
|
---|
713 | if ($writable and not $$tagInfo{Shift} and $g[0] ne 'Composite' and
|
---|
714 | $short ne 'PostScript')
|
---|
715 | {
|
---|
716 | warn "$short $name is not shiftable!\n";
|
---|
717 | }
|
---|
718 | } elsif ($name =~ /DateTime(?!Stamp)/ and (not $$tagInfo{Groups}{2} or
|
---|
719 | $$tagInfo{Groups}{2} ne 'Time') and $short ne 'DICOM') {
|
---|
720 | warn "$short $name should be in 'Time' group!\n";
|
---|
721 | }
|
---|
722 | # validate Description (can't contain special characters)
|
---|
723 | if ($$tagInfo{Description} and
|
---|
724 | $$tagInfo{Description} ne EscapeHTML($$tagInfo{Description}))
|
---|
725 | {
|
---|
726 | # this is a problem because the Escape option currently only
|
---|
727 | # escapes descriptions if the default Lang option isn't default
|
---|
728 | warn "$name description contains special characters!\n";
|
---|
729 | }
|
---|
730 | # validate SubIFD flag
|
---|
731 | my $subdir = $$tagInfo{SubDirectory};
|
---|
732 | my $struct = $$tagInfo{Struct};
|
---|
733 | my $strTable;
|
---|
734 | if (ref $struct) {
|
---|
735 | $strTable = $struct;
|
---|
736 | $struct = $$strTable{STRUCT_NAME};
|
---|
737 | if ($struct) {
|
---|
738 | my $oldTable = $structLookup{$struct};
|
---|
739 | if ($oldTable and $oldTable ne $strTable) {
|
---|
740 | warn "Duplicate XMP structure with name $struct\n";
|
---|
741 | } else {
|
---|
742 | $structLookup{$struct} = $strTable;
|
---|
743 | }
|
---|
744 | } else {
|
---|
745 | warn "Missing STRUCT_NAME for structure in $$tagInfo{Name}\n";
|
---|
746 | undef $strTable;
|
---|
747 | }
|
---|
748 | } elsif ($struct) {
|
---|
749 | $strTable = $structLookup{$struct};
|
---|
750 | unless ($strTable) {
|
---|
751 | warn "Missing XMP $struct structure!\n";
|
---|
752 | undef $struct;
|
---|
753 | }
|
---|
754 | }
|
---|
755 | my $isSub = ($subdir and $$subdir{Start} and $$subdir{Start} eq '$val');
|
---|
756 | if ($$tagInfo{SubIFD}) {
|
---|
757 | warn "Warning: Wrong SubDirectory Start for SubIFD tag - $short $name\n" unless $isSub;
|
---|
758 | } else {
|
---|
759 | warn "Warning: SubIFD flag not set for $short $name\n" if $isSub;
|
---|
760 | }
|
---|
761 | if ($$tagInfo{Notes}) {
|
---|
762 | my $note = $$tagInfo{Notes};
|
---|
763 | # remove leading/trailing blank lines
|
---|
764 | $note =~ s/(^\s+|\s+$)//g;
|
---|
765 | # remove leading/trailing spaces on each line
|
---|
766 | $note =~ s/(^[ \t]+|[ \t]+$)//mg;
|
---|
767 | push @values, "($note)";
|
---|
768 | } elsif ($isXMP and lc $tagID ne lc $name) {
|
---|
769 | # add note about different XMP Tag ID
|
---|
770 | if ($$tagInfo{RootTagInfo}) {
|
---|
771 | push @values, "($tagID)";
|
---|
772 | } else {
|
---|
773 | push @values,"(called $tagID by the spec)";
|
---|
774 | }
|
---|
775 | }
|
---|
776 | my $writeGroup;
|
---|
777 | $writeGroup = $$tagInfo{WriteGroup};
|
---|
778 | unless ($writeGroup) {
|
---|
779 | $writeGroup = $$table{WRITE_GROUP} if $writable;
|
---|
780 | $writeGroup = '-' unless $writeGroup;
|
---|
781 | }
|
---|
782 | if (defined $$tagInfo{Format}) {
|
---|
783 | $format = $$tagInfo{Format};
|
---|
784 | # validate Format
|
---|
785 | unless ($formatOK{$format} or $short eq 'PICT' or
|
---|
786 | ($format =~ /^(var_)?(.*)\[/ and $formatOK{$2}))
|
---|
787 | {
|
---|
788 | warn "Warning: Unknown Format ($format) for $short $name\n";
|
---|
789 | }
|
---|
790 | }
|
---|
791 | if ($subdir) {
|
---|
792 | my $subTable = $$subdir{TagTable} || $tableName;
|
---|
793 | push @values, $shortName{$subTable}
|
---|
794 | } elsif ($struct) {
|
---|
795 | push @values, $struct;
|
---|
796 | $structs{$struct} = 1;
|
---|
797 | }
|
---|
798 | my $type;
|
---|
799 | foreach $type ('Require','Desire') {
|
---|
800 | my $require = $$tagInfo{$type};
|
---|
801 | if (ref $require) {
|
---|
802 | foreach (sort { $a <=> $b } keys %$require) {
|
---|
803 | push @require, $$require{$_};
|
---|
804 | }
|
---|
805 | } elsif ($require) {
|
---|
806 | push @require, $require;
|
---|
807 | }
|
---|
808 | }
|
---|
809 | my $printConv = $$tagInfo{PrintConv};
|
---|
810 | if ($$tagInfo{Mask}) {
|
---|
811 | my $val = $$tagInfo{Mask};
|
---|
812 | push @values, sprintf('[Mask 0x%.2x]',$val);
|
---|
813 | $$tagInfo{PrintHex} = 1 unless defined $$tagInfo{PrintHex};
|
---|
814 | # verify that all values are within the mask
|
---|
815 | if (ref $printConv eq 'HASH') {
|
---|
816 | # convert mask if necessary
|
---|
817 | if ($$tagInfo{ValueConv}) {
|
---|
818 | my $v = eval $$tagInfo{ValueConv};
|
---|
819 | $val = $v if defined $v;
|
---|
820 | }
|
---|
821 | foreach (keys %$printConv) {
|
---|
822 | next if $_ !~ /^\d+$/ or ($_ & $val) == $_;
|
---|
823 | my $hex = sprintf '0x%.2x', $_;
|
---|
824 | warn "$short $name PrintConv value $hex is not in Mask!\n";
|
---|
825 | }
|
---|
826 | }
|
---|
827 | }
|
---|
828 | if (ref($printConv) =~ /^(HASH|ARRAY)$/) {
|
---|
829 | my (@printConvList, @indexList, $index);
|
---|
830 | if (ref $printConv eq 'ARRAY') {
|
---|
831 | for ($index=0; $index<@$printConv; ++$index) {
|
---|
832 | next if ref $$printConv[$index] ne 'HASH';
|
---|
833 | next unless %{$$printConv[$index]};
|
---|
834 | push @printConvList, $$printConv[$index];
|
---|
835 | push @indexList, $index;
|
---|
836 | # collapse values with identical PrintConv's
|
---|
837 | if (@printConvList >= 2 and $printConvList[-1] eq $printConvList[-2]) {
|
---|
838 | if (ref $indexList[-2]) {
|
---|
839 | push @{$indexList[-2]}, $indexList[-1];
|
---|
840 | } else {
|
---|
841 | $indexList[-2] = [ $indexList[-2], $indexList[-1] ];
|
---|
842 | }
|
---|
843 | pop @printConvList;
|
---|
844 | pop @indexList;
|
---|
845 | }
|
---|
846 | }
|
---|
847 | $printConv = shift @printConvList;
|
---|
848 | $index = shift @indexList;
|
---|
849 | }
|
---|
850 | while (defined $printConv) {
|
---|
851 | if (defined $index) {
|
---|
852 | # (print indices of original values if reorganized)
|
---|
853 | my $s = '';
|
---|
854 | my $idx = $$tagInfo{Relist} ? $tagInfo->{Relist}->[$index] : $index;
|
---|
855 | if (ref $idx) {
|
---|
856 | $s = 's' if @$idx > 1;
|
---|
857 | # collapse consecutive number ranges
|
---|
858 | my ($i, @i, $rngStart);
|
---|
859 | for ($i=0; $i<@$idx; ++$i) {
|
---|
860 | if ($i < @$idx - 1 and $$idx[$i+1] == $$idx[$i] + 1) {
|
---|
861 | $rngStart = $i unless defined $rngStart;
|
---|
862 | next;
|
---|
863 | }
|
---|
864 | push @i, defined($rngStart) ? "$rngStart-$i" : $i;
|
---|
865 | }
|
---|
866 | ($idx = join ', ', @i) =~ s/(.*),/$1 and/;
|
---|
867 | } elsif (not $$tagInfo{Relist}) {
|
---|
868 | while (@printConvList and $printConv eq $printConvList[0]) {
|
---|
869 | shift @printConvList;
|
---|
870 | $index = shift @indexList;
|
---|
871 | }
|
---|
872 | if ($idx != $index) {
|
---|
873 | $idx = "$idx-$index";
|
---|
874 | $s = 's';
|
---|
875 | }
|
---|
876 | }
|
---|
877 | push @values, "[Value$s $idx]";
|
---|
878 | }
|
---|
879 | if ($$tagInfo{SeparateTable}) {
|
---|
880 | $subdir = 1;
|
---|
881 | my $s = $$tagInfo{SeparateTable};
|
---|
882 | $s = $name if $s eq '1';
|
---|
883 | # add module name if not specified
|
---|
884 | $s =~ / / or ($short =~ /^(\w+)/ and $s = "$1 $s");
|
---|
885 | push @values, $s;
|
---|
886 | $sepTable{$s} = $printConv;
|
---|
887 | # add PrintHex flag to PrintConv so we can check it later
|
---|
888 | $$printConv{PrintHex} = 1 if $$tagInfo{PrintHex};
|
---|
889 | $$printConv{PrintString} = 1 if $$tagInfo{PrintString};
|
---|
890 | } else {
|
---|
891 | $caseInsensitive = 0;
|
---|
892 | my @pk = sort NumbersFirst keys %$printConv;
|
---|
893 | my $n = scalar @values;
|
---|
894 | my ($bits, $cols, $i);
|
---|
895 | foreach (@pk) {
|
---|
896 | next if $_ eq '';
|
---|
897 | $_ eq 'BITMASK' and $bits = $$printConv{$_}, next;
|
---|
898 | $_ eq 'OTHER' and next;
|
---|
899 | my $index;
|
---|
900 | if (($$tagInfo{PrintHex} or $$printConv{BITMASK}) and /^\d+$/) {
|
---|
901 | $index = sprintf('0x%x',$_);
|
---|
902 | } elsif (/^[+-]?(?=\d|\.\d)\d*(\.\d*)?$/ and not $$tagInfo{PrintString}) {
|
---|
903 | $index = $_;
|
---|
904 | } else {
|
---|
905 | $index = $_;
|
---|
906 | # translate unprintable values
|
---|
907 | if ($index =~ s/([\x00-\x1f\x80-\xff])/sprintf("\\x%.2x",ord $1)/eg) {
|
---|
908 | $index = qq{"$index"};
|
---|
909 | } else {
|
---|
910 | $index = qq{'$index'};
|
---|
911 | }
|
---|
912 | }
|
---|
913 | push @values, "$index = " . $$printConv{$_};
|
---|
914 | # validate all PrintConv values
|
---|
915 | if ($$printConv{$_} =~ /[\0-\x1f\x7f-\xff]/) {
|
---|
916 | warn "Warning: Special characters in $short $name PrintConv ($$printConv{$_})\n";
|
---|
917 | }
|
---|
918 | }
|
---|
919 | if ($bits) {
|
---|
920 | my @pk = sort NumbersFirst keys %$bits;
|
---|
921 | foreach (@pk) {
|
---|
922 | push @values, "Bit $_ = " . $$bits{$_};
|
---|
923 | }
|
---|
924 | }
|
---|
925 | # organize values into columns if specified
|
---|
926 | if (defined($cols = $$tagInfo{PrintConvColumns})) {
|
---|
927 | my @new = splice @values, $n;
|
---|
928 | my $v = '[!HTML]<table class=cols><tr>';
|
---|
929 | my $rows = int((scalar(@new) + $cols - 1) / $cols);
|
---|
930 | for ($n=0; $n<@new; $n+=$rows) {
|
---|
931 | $v .= "\n <td>";
|
---|
932 | for ($i=0; $i<$rows and $n+$i<@new; ++$i) {
|
---|
933 | $v .= "\n <br>" if $i;
|
---|
934 | $v .= EscapeHTML($new[$n+$i]);
|
---|
935 | }
|
---|
936 | $v .= '</td><td> </td>';
|
---|
937 | }
|
---|
938 | push @values, $v . "</tr></table>\n";
|
---|
939 | }
|
---|
940 | }
|
---|
941 | last unless @printConvList;
|
---|
942 | $printConv = shift @printConvList;
|
---|
943 | $index = shift @indexList;
|
---|
944 | }
|
---|
945 | } elsif ($printConv and $printConv =~ /DecodeBits\(\$val,\s*(\{.*\})\s*\)/s) {
|
---|
946 | $$self{Model} = ''; # needed for Nikon ShootingMode
|
---|
947 | my $bits = eval $1;
|
---|
948 | delete $$self{Model};
|
---|
949 | if ($@) {
|
---|
950 | warn $@;
|
---|
951 | } else {
|
---|
952 | my @pk = sort NumbersFirst keys %$bits;
|
---|
953 | foreach (@pk) {
|
---|
954 | push @values, "Bit $_ = " . $$bits{$_};
|
---|
955 | }
|
---|
956 | }
|
---|
957 | }
|
---|
958 | if ($subdir and not $$tagInfo{SeparateTable}) {
|
---|
959 | # subdirectories are only writable if specified explicitly
|
---|
960 | my $tw = $$tagInfo{Writable};
|
---|
961 | $writable = '-' . ($tw ? $writable : '');
|
---|
962 | $writable .= '!' if $tw and ($$tagInfo{Protected} || 0) & 0x01;
|
---|
963 | } else {
|
---|
964 | # not writable if we can't do the inverse conversions
|
---|
965 | my $noPrintConvInv;
|
---|
966 | if ($writable) {
|
---|
967 | foreach ('PrintConv','ValueConv') {
|
---|
968 | next unless $$tagInfo{$_};
|
---|
969 | next if $$tagInfo{$_ . 'Inv'};
|
---|
970 | next if ref($$tagInfo{$_}) =~ /^(HASH|ARRAY)$/;
|
---|
971 | next if $$tagInfo{WriteAlso};
|
---|
972 | if ($_ eq 'ValueConv') {
|
---|
973 | undef $writable;
|
---|
974 | } else {
|
---|
975 | $noPrintConvInv = 1;
|
---|
976 | }
|
---|
977 | }
|
---|
978 | }
|
---|
979 | if (not $writable) {
|
---|
980 | $writable = 'N';
|
---|
981 | } else {
|
---|
982 | $writable eq '1' and $writable = $format ? $format : 'Y';
|
---|
983 | my $count = $$tagInfo{Count} || 1;
|
---|
984 | # adjust count to Writable size if different than Format
|
---|
985 | if ($writable and $format and $writable ne $format and
|
---|
986 | $Image::ExifTool::Exif::formatNumber{$writable} and
|
---|
987 | $Image::ExifTool::Exif::formatNumber{$format})
|
---|
988 | {
|
---|
989 | my $n1 = $Image::ExifTool::Exif::formatNumber{$format};
|
---|
990 | my $n2 = $Image::ExifTool::Exif::formatNumber{$writable};
|
---|
991 | $count *= $Image::ExifTool::Exif::formatSize[$n1] /
|
---|
992 | $Image::ExifTool::Exif::formatSize[$n2];
|
---|
993 | }
|
---|
994 | if ($count != 1) {
|
---|
995 | $count = 'n' if $count < 0;
|
---|
996 | $writable .= "[$count]";
|
---|
997 | }
|
---|
998 | $writable .= '~' if $noPrintConvInv;
|
---|
999 | # add a '*' if this tag is protected or a '!' for unsafe tags
|
---|
1000 | if ($$tagInfo{Protected}) {
|
---|
1001 | $writable .= '*' if $$tagInfo{Protected} & 0x02;
|
---|
1002 | $writable .= '!' if $$tagInfo{Protected} & 0x01;
|
---|
1003 | }
|
---|
1004 | $writable .= '/' if $$tagInfo{Avoid};
|
---|
1005 | }
|
---|
1006 | $writable = "=struct" if $struct;
|
---|
1007 | $writable .= '_' if defined $$tagInfo{Flat};
|
---|
1008 | $writable .= '+' if $$tagInfo{List};
|
---|
1009 | $writable .= ':' if $$tagInfo{Mandatory};
|
---|
1010 | # separate tables link like subdirectories (flagged with leading '-')
|
---|
1011 | $writable = "-$writable" if $subdir;
|
---|
1012 | }
|
---|
1013 | # don't duplicate a tag name unless an entry is different
|
---|
1014 | my $lcName = lc($name);
|
---|
1015 | # check for conflicts with shortcut names
|
---|
1016 | if ($isShortcut{$lcName} and $short ne 'Shortcuts' and
|
---|
1017 | ($$tagInfo{Writable} or not $$tagInfo{SubDirectory}))
|
---|
1018 | {
|
---|
1019 | warn "WARNING: $short $name is a shortcut tag!\n";
|
---|
1020 | }
|
---|
1021 | $name .= '?' if $$tagInfo{Unknown};
|
---|
1022 | unless (@tagNames and $tagNames[-1] eq $name and
|
---|
1023 | $writeGroup[-1] eq $writeGroup and $writable[-1] eq $writable)
|
---|
1024 | {
|
---|
1025 | push @tagNames, $name;
|
---|
1026 | push @writeGroup, $writeGroup;
|
---|
1027 | push @writable, $writable;
|
---|
1028 | }
|
---|
1029 | #
|
---|
1030 | # add this tag to the tag lookup unless PROCESS_PROC is 0 or shortcut or plug-in tag
|
---|
1031 | #
|
---|
1032 | next if $shortcut or $isPlugin;
|
---|
1033 | next if defined $$table{PROCESS_PROC} and not $$table{PROCESS_PROC};
|
---|
1034 | # count our tags
|
---|
1035 | if ($$tagInfo{SubDirectory}) {
|
---|
1036 | $subdirs{$lcName} or $subdirs{$lcName} = 0;
|
---|
1037 | ++$subdirs{$lcName};
|
---|
1038 | } else {
|
---|
1039 | ++$count{'total tags'};
|
---|
1040 | unless ($tagExists{$lcName} and (not $subdirs{$lcName} or $subdirs{$lcName} == $tagExists{$lcName})) {
|
---|
1041 | ++$count{'unique tag names'};
|
---|
1042 | }
|
---|
1043 | }
|
---|
1044 | $tagExists{$lcName} or $tagExists{$lcName} = 0;
|
---|
1045 | ++$tagExists{$lcName};
|
---|
1046 | # only add writable tags to lookup table (for speed)
|
---|
1047 | my $wflag = $$tagInfo{Writable};
|
---|
1048 | next unless $writeProc and ($wflag or ($$table{WRITABLE} and
|
---|
1049 | not defined $wflag and not $$tagInfo{SubDirectory}));
|
---|
1050 | $tagLookup{$lcName} or $tagLookup{$lcName} = { };
|
---|
1051 | # add to lookup for flattened tags if necessary
|
---|
1052 | if ($$tagInfo{RootTagInfo}) {
|
---|
1053 | $flattened{$lcName} or $flattened{$lcName} = { };
|
---|
1054 | $flattened{$lcName}{$tableNum} = $$tagInfo{RootTagInfo}{TagID};
|
---|
1055 | }
|
---|
1056 | # remember number for this table
|
---|
1057 | my $tagIDs = $tagLookup{$lcName}->{$tableNum};
|
---|
1058 | # must allow for duplicate tags with the same name in a single table!
|
---|
1059 | if ($tagIDs) {
|
---|
1060 | if (ref $tagIDs eq 'HASH') {
|
---|
1061 | $$tagIDs{$tagID} = 1;
|
---|
1062 | next;
|
---|
1063 | } elsif ($tagID eq $tagIDs) {
|
---|
1064 | next;
|
---|
1065 | } else {
|
---|
1066 | $tagIDs = { $tagIDs => 1, $tagID => 1 };
|
---|
1067 | }
|
---|
1068 | } else {
|
---|
1069 | $tagIDs = $tagID;
|
---|
1070 | }
|
---|
1071 | $tableWritable{$tableName} = 1;
|
---|
1072 | $tagLookup{$lcName}->{$tableNum} = $tagIDs;
|
---|
1073 | if ($short eq 'Composite' and $$tagInfo{Module}) {
|
---|
1074 | $compositeModules{$lcName} = $$tagInfo{Module};
|
---|
1075 | }
|
---|
1076 | }
|
---|
1077 | #
|
---|
1078 | # save TagName information
|
---|
1079 | #
|
---|
1080 | my $tagIDstr;
|
---|
1081 | if ($tagID =~ /^\d+(\.\d+)?$/) {
|
---|
1082 | if ($1 or $binaryTable or $isIPTC or ($short =~ /^CanonCustom/ and $tagID < 256)) {
|
---|
1083 | if ($tagID < 0x10000) {
|
---|
1084 | $tagIDstr = $tagID;
|
---|
1085 | } else {
|
---|
1086 | $tagIDstr = sprintf('0x%.8x',$tagID);
|
---|
1087 | }
|
---|
1088 | } else {
|
---|
1089 | $tagIDstr = sprintf('0x%.4x',$tagID);
|
---|
1090 | }
|
---|
1091 | } elsif ($short eq 'DICOM') {
|
---|
1092 | ($tagIDstr = $tagID) =~ s/_/,/;
|
---|
1093 | } else {
|
---|
1094 | # convert non-printable characters to hex escape sequences
|
---|
1095 | if ($tagID =~ s/([\x00-\x1f\x7f-\xff])/'\x'.unpack('H*',$1)/eg) {
|
---|
1096 | $tagID =~ s/\\x00/\\0/g;
|
---|
1097 | next if $tagID eq 'jP\x1a\x1a'; # ignore abnormal JP2 signature tag
|
---|
1098 | $tagIDstr = qq{"$tagID"};
|
---|
1099 | } else {
|
---|
1100 | $tagIDstr = "'$tagID'";
|
---|
1101 | }
|
---|
1102 | }
|
---|
1103 | my $len = length $tagIDstr;
|
---|
1104 | $longID{$tableName} = $len if $longID{$tableName} < $len;
|
---|
1105 | foreach (@tagNames) {
|
---|
1106 | $len = length $_;
|
---|
1107 | $longName{$tableName} = $len if $longName{$tableName} < $len;
|
---|
1108 | }
|
---|
1109 | push @$info, [ $tagIDstr, \@tagNames, \@writable, \@values, \@require, \@writeGroup ];
|
---|
1110 | }
|
---|
1111 | # do consistency check of writable BinaryData tables
|
---|
1112 | if ($processBinaryData and $$table{WRITABLE}) {
|
---|
1113 | my %lookup = (
|
---|
1114 | IS_OFFSET => \%isOffset,
|
---|
1115 | IS_SUBDIR => \%hasSubdir,
|
---|
1116 | DATAMEMBER => \%datamember,
|
---|
1117 | );
|
---|
1118 | my ($var, $tagID);
|
---|
1119 | foreach $var (sort keys %lookup) {
|
---|
1120 | my $hash = $lookup{$var};
|
---|
1121 | if ($$table{$var}) {
|
---|
1122 | foreach $tagID (@{$$table{$var}}) {
|
---|
1123 | $$hash{$tagID} and delete($$hash{$tagID}), next;
|
---|
1124 | warn "Warning: Extra $var for $short tag $tagID\n";
|
---|
1125 | }
|
---|
1126 | }
|
---|
1127 | foreach $tagID (sort keys %$hash) {
|
---|
1128 | warn "Warning: Missing $var for $short $$hash{$tagID}\n";
|
---|
1129 | }
|
---|
1130 | }
|
---|
1131 | }
|
---|
1132 | }
|
---|
1133 | # save information about structures
|
---|
1134 | my $strName;
|
---|
1135 | foreach $strName (keys %structs) {
|
---|
1136 | my $struct = $structLookup{$strName};
|
---|
1137 | my $info = $tagNameInfo{"XMP $strName Struct"} = [ ];
|
---|
1138 | my $tag;
|
---|
1139 | foreach $tag (sort keys %$struct) {
|
---|
1140 | my $tagInfo = $$struct{$tag};
|
---|
1141 | next unless ref $tagInfo eq 'HASH';
|
---|
1142 | my $writable = $$tagInfo{Writable};
|
---|
1143 | my @vals;
|
---|
1144 | unless ($writable) {
|
---|
1145 | $writable = $$tagInfo{Struct};
|
---|
1146 | ref $writable and $writable = $$writable{STRUCT_NAME};
|
---|
1147 | if ($writable) {
|
---|
1148 | push @vals, $writable;
|
---|
1149 | $structs{$writable} = 1;
|
---|
1150 | $writable = "=$writable";
|
---|
1151 | } else {
|
---|
1152 | $writable = 'string';
|
---|
1153 | }
|
---|
1154 | }
|
---|
1155 | $writable .= '+' if $$tagInfo{List};
|
---|
1156 | push @$info, [
|
---|
1157 | $tag,
|
---|
1158 | [ $$tagInfo{Name} || ucfirst($tag) ],
|
---|
1159 | [ $writable ],
|
---|
1160 | \@vals,
|
---|
1161 | [], []
|
---|
1162 | ];
|
---|
1163 | }
|
---|
1164 | }
|
---|
1165 | return $self;
|
---|
1166 | }
|
---|
1167 |
|
---|
1168 | #------------------------------------------------------------------------------
|
---|
1169 | # Rewrite this file to build the lookup tables
|
---|
1170 | # Inputs: 0) BuildTagLookup object reference
|
---|
1171 | # 1) output tag lookup module name (ie. 'lib/Image/ExifTool/TagLookup.pm')
|
---|
1172 | # Returns: true on success
|
---|
1173 | sub WriteTagLookup($$)
|
---|
1174 | {
|
---|
1175 | local ($_, *INFILE, *OUTFILE);
|
---|
1176 | my ($self, $file) = @_;
|
---|
1177 | my $tagLookup = $self->{TAG_LOOKUP};
|
---|
1178 | my $tagExists = $self->{TAG_EXISTS};
|
---|
1179 | my $flattened = $self->{FLATTENED};
|
---|
1180 | my $tableWritable = $self->{TABLE_WRITABLE};
|
---|
1181 | #
|
---|
1182 | # open/create necessary files and transfer file headers
|
---|
1183 | #
|
---|
1184 | my $tmpFile = "${file}_tmp";
|
---|
1185 | open(INFILE, $file) or warn("Can't open $file\n"), return 0;
|
---|
1186 | unless (open(OUTFILE, ">$tmpFile")) {
|
---|
1187 | warn "Can't create temporary file $tmpFile\n";
|
---|
1188 | close(INFILE);
|
---|
1189 | return 0;
|
---|
1190 | }
|
---|
1191 | my $success;
|
---|
1192 | while (<INFILE>) {
|
---|
1193 | print OUTFILE $_ or last;
|
---|
1194 | if (/^#\+{4} Begin/) {
|
---|
1195 | $success = 1;
|
---|
1196 | last;
|
---|
1197 | }
|
---|
1198 | }
|
---|
1199 | print OUTFILE "\n# list of tables containing writable tags\n";
|
---|
1200 | print OUTFILE "my \@tableList = (\n";
|
---|
1201 |
|
---|
1202 | #
|
---|
1203 | # write table list
|
---|
1204 | #
|
---|
1205 | my @tableNames = sort keys %allTables;
|
---|
1206 | my $tableName;
|
---|
1207 | my %wrNum; # translate from allTables index to writable tables index
|
---|
1208 | my $count = 0;
|
---|
1209 | my $num = 0;
|
---|
1210 | foreach $tableName (@tableNames) {
|
---|
1211 | if ($$tableWritable{$tableName}) {
|
---|
1212 | print OUTFILE "\t'$tableName',\n";
|
---|
1213 | $wrNum{$count} = $num++;
|
---|
1214 | }
|
---|
1215 | $count++;
|
---|
1216 | }
|
---|
1217 | #
|
---|
1218 | # write the tag lookup table
|
---|
1219 | #
|
---|
1220 | my $tag;
|
---|
1221 | # verify that certain critical tag names aren't duplicated
|
---|
1222 | foreach $tag (qw{filename directory}) {
|
---|
1223 | next unless $$tagLookup{$tag};
|
---|
1224 | my $n = scalar keys %{$$tagLookup{$tag}};
|
---|
1225 | warn "Warning: $n writable '$tag' tags!\n" if $n > 1;
|
---|
1226 | }
|
---|
1227 | print OUTFILE ");\n\n# lookup for all writable tags\nmy \%tagLookup = (\n";
|
---|
1228 | foreach $tag (sort keys %$tagLookup) {
|
---|
1229 | print OUTFILE "\t'$tag' => { ";
|
---|
1230 | my @tableNums = sort { $a <=> $b } keys %{$$tagLookup{$tag}};
|
---|
1231 | my (@entries, $tableNum);
|
---|
1232 | foreach $tableNum (@tableNums) {
|
---|
1233 | my $tagID = $$tagLookup{$tag}{$tableNum};
|
---|
1234 | my $rootID = $$flattened{$tag}{$tableNum};
|
---|
1235 | my $entry;
|
---|
1236 | if (ref $tagID eq 'HASH' or $rootID) {
|
---|
1237 | $tagID = { $tagID => 1 } unless ref $tagID eq 'HASH';
|
---|
1238 | my @tagIDs = sort keys %$tagID;
|
---|
1239 | foreach (@tagIDs) {
|
---|
1240 | if (/^\d+$/) {
|
---|
1241 | $_ = sprintf('0x%x',$_);
|
---|
1242 | } else {
|
---|
1243 | my $quot = "'";
|
---|
1244 | # escape non-printable characters in tag ID if necessary
|
---|
1245 | $quot = '"' if s/[\x00-\x1f,\x7f-\xff]/sprintf('\\x%.2x',ord($&))/ge;
|
---|
1246 | $_ = $quot . $_ . $quot;
|
---|
1247 | }
|
---|
1248 | }
|
---|
1249 | # reference to root structure ID must come first in lookup
|
---|
1250 | # (so we can generate the flattened tags just before we need them)
|
---|
1251 | unshift @tagIDs, "\\'$rootID'" if $rootID;
|
---|
1252 | $entry = '[' . join(',', @tagIDs) . ']';
|
---|
1253 | } elsif ($tagID =~ /^\d+$/) {
|
---|
1254 | $entry = sprintf('0x%x',$tagID);
|
---|
1255 | } else {
|
---|
1256 | $entry = "'$tagID'";
|
---|
1257 | }
|
---|
1258 | my $wrNum = $wrNum{$tableNum};
|
---|
1259 | push @entries, "$wrNum => $entry";
|
---|
1260 | }
|
---|
1261 | print OUTFILE join(', ', @entries);
|
---|
1262 | print OUTFILE " },\n";
|
---|
1263 | }
|
---|
1264 | #
|
---|
1265 | # write tag exists lookup
|
---|
1266 | #
|
---|
1267 | print OUTFILE ");\n\n# lookup for non-writable tags to check if the name exists\n";
|
---|
1268 | print OUTFILE "my \%tagExists = (\n";
|
---|
1269 | foreach $tag (sort keys %$tagExists) {
|
---|
1270 | next if $$tagLookup{$tag};
|
---|
1271 | print OUTFILE "\t'$tag' => 1,\n";
|
---|
1272 | }
|
---|
1273 | #
|
---|
1274 | # write module lookup for writable composite tags
|
---|
1275 | #
|
---|
1276 | my $compositeModules = $self->{COMPOSITE_MODULES};
|
---|
1277 | print OUTFILE ");\n\n# module names for writable Composite tags\n";
|
---|
1278 | print OUTFILE "my \%compositeModules = (\n";
|
---|
1279 | foreach (sort keys %$compositeModules) {
|
---|
1280 | print OUTFILE "\t'$_' => '$$compositeModules{$_}',\n";
|
---|
1281 | }
|
---|
1282 | print OUTFILE ");\n\n";
|
---|
1283 | #
|
---|
1284 | # finish writing TagLookup.pm and clean up
|
---|
1285 | #
|
---|
1286 | if ($success) {
|
---|
1287 | $success = 0;
|
---|
1288 | while (<INFILE>) {
|
---|
1289 | $success or /^#\+{4} End/ or next;
|
---|
1290 | print OUTFILE $_;
|
---|
1291 | $success = 1;
|
---|
1292 | }
|
---|
1293 | }
|
---|
1294 | close(INFILE);
|
---|
1295 | close(OUTFILE) or $success = 0;
|
---|
1296 | #
|
---|
1297 | # return success code
|
---|
1298 | #
|
---|
1299 | if ($success) {
|
---|
1300 | local (*ORG, *TMP);
|
---|
1301 | # only rename the file if something changed
|
---|
1302 | open ORG, $file or return 0;
|
---|
1303 | open TMP, $tmpFile or return 0;
|
---|
1304 | my ($buff, $buf2, $changed);
|
---|
1305 | for (;;) {
|
---|
1306 | my $n1 = read ORG, $buff, 65536;
|
---|
1307 | my $n2 = read TMP, $buf2, 65536;
|
---|
1308 | $n1 eq $n2 or $changed = 1, last;
|
---|
1309 | last unless $n1;
|
---|
1310 | $buff eq $buf2 or $changed = 1, last;
|
---|
1311 | }
|
---|
1312 | close ORG;
|
---|
1313 | close TMP;
|
---|
1314 | if ($changed) {
|
---|
1315 | rename($tmpFile, $file) or warn("Error renaming $tmpFile\n"), $success = 0;
|
---|
1316 | } else {
|
---|
1317 | unlink($tmpFile);
|
---|
1318 | }
|
---|
1319 | } else {
|
---|
1320 | unlink($tmpFile);
|
---|
1321 | warn "Error rewriting file\n";
|
---|
1322 | }
|
---|
1323 | return $success;
|
---|
1324 | }
|
---|
1325 |
|
---|
1326 | #------------------------------------------------------------------------------
|
---|
1327 | # sort numbers first numerically, then strings alphabetically (case insensitive)
|
---|
1328 | sub NumbersFirst
|
---|
1329 | {
|
---|
1330 | my $rtnVal;
|
---|
1331 | my $bNum = ($b =~ /^-?[0-9]+(\.\d*)?$/);
|
---|
1332 | if ($a =~ /^-?[0-9]+(\.\d*)?$/) {
|
---|
1333 | $rtnVal = ($bNum ? $a <=> $b : -$numbersFirst);
|
---|
1334 | } elsif ($bNum) {
|
---|
1335 | $rtnVal = $numbersFirst;
|
---|
1336 | } else {
|
---|
1337 | my ($a2, $b2) = ($a, $b);
|
---|
1338 | # expand numbers to 3 digits (with restrictions to avoid messing up ascii-hex tags)
|
---|
1339 | $a2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $a2 =~ /^(APP)?[.0-9 ]*$/ and length($a2)<16;
|
---|
1340 | $b2 =~ s/(\d+)/sprintf("%.3d",$1)/eg if $b2 =~ /^(APP)?[.0-9 ]*$/ and length($b2)<16;
|
---|
1341 | $caseInsensitive and $rtnVal = (lc($a2) cmp lc($b2));
|
---|
1342 | $rtnVal or $rtnVal = ($a2 cmp $b2);
|
---|
1343 | }
|
---|
1344 | return $rtnVal;
|
---|
1345 | }
|
---|
1346 |
|
---|
1347 | #------------------------------------------------------------------------------
|
---|
1348 | # Convert pod documentation to pod
|
---|
1349 | # (funny, I know, but the pod headings must be hidden to prevent confusing
|
---|
1350 | # the pod parser)
|
---|
1351 | # Inputs: 0-N) documentation strings
|
---|
1352 | sub Doc2Pod($;@)
|
---|
1353 | {
|
---|
1354 | my $doc = shift;
|
---|
1355 | local $_;
|
---|
1356 | $doc .= shift while @_;
|
---|
1357 | $doc =~ s/\n~/\n=/g;
|
---|
1358 | $doc =~ s/L<[^>]+?\|(http[^>]+)>/L<$1>/g; # POD doesn't support text for http links
|
---|
1359 | return $doc;
|
---|
1360 | }
|
---|
1361 |
|
---|
1362 | #------------------------------------------------------------------------------
|
---|
1363 | # Convert pod documentation to html
|
---|
1364 | # Inputs: 0) string
|
---|
1365 | sub Doc2Html($)
|
---|
1366 | {
|
---|
1367 | my $doc = EscapeHTML(shift);
|
---|
1368 | $doc =~ s/\n\n/<\/p>\n\n<p>/g;
|
---|
1369 | $doc =~ s/B<(.*?)>/<b>$1<\/b>/sg;
|
---|
1370 | $doc =~ s/C<(.*?)>/<code>$1<\/code>/sg;
|
---|
1371 | $doc =~ s/I<(.*?)>/<i>$1<\/i>/sg;
|
---|
1372 | $doc =~ s{L<([^&]+?)\|\Q$homePage\E/TagNames/(.*?)>}{<a href="$2">$1<\/a>}sg;
|
---|
1373 | $doc =~ s{L<([^&]+?)\|\Q$homePage\E/(.*?)>}{<a href="../$2">$1<\/a>}sg;
|
---|
1374 | $doc =~ s{L<\Q$homePage\E/TagNames/(.*?)>}{<a href="$1">$1<\/a>}sg;
|
---|
1375 | $doc =~ s{L<\Q$homePage\E/(.*?)>}{<a href="../$1">$1<\/a>}sg;
|
---|
1376 | $doc =~ s{L<([^&]+?)\|/\w+ ([^/&|]+) Tags>}{<a href="#$2">$1</a>}sg;
|
---|
1377 | $doc =~ s/L<([^&]+?)\|(.+?)>/<a href="$2">$1<\/a>/sg;
|
---|
1378 | $doc =~ s/L<(.*?)>/<a href="$1">$1<\/a>/sg;
|
---|
1379 | return $doc;
|
---|
1380 | }
|
---|
1381 |
|
---|
1382 | #------------------------------------------------------------------------------
|
---|
1383 | # Get the order that we want to print the tables in the documentation
|
---|
1384 | # Returns: tables in the order we want
|
---|
1385 | sub GetTableOrder()
|
---|
1386 | {
|
---|
1387 | my %gotTable;
|
---|
1388 | my @tableNames = @tableOrder;
|
---|
1389 | my (@orderedTables, %mainTables, @outOfOrder);
|
---|
1390 | my $lastTable = '';
|
---|
1391 |
|
---|
1392 | while (@tableNames) {
|
---|
1393 | my $tableName = shift @tableNames;
|
---|
1394 | next if $gotTable{$tableName};
|
---|
1395 | if ($tableName =~ /^Image::ExifTool::(\w+)::Main/) {
|
---|
1396 | $mainTables{$1} = 1;
|
---|
1397 | } elsif ($lastTable and not $tableName =~ /^${lastTable}::/) {
|
---|
1398 | push @outOfOrder, $tableName;
|
---|
1399 | }
|
---|
1400 | ($lastTable) = ($tableName =~ /^(Image::ExifTool::\w+)/);
|
---|
1401 | push @orderedTables, $tableName;
|
---|
1402 | $gotTable{$tableName} = 1;
|
---|
1403 | my $table = GetTagTable($tableName);
|
---|
1404 | # recursively scan through tables in subdirectories
|
---|
1405 | my @moreTables;
|
---|
1406 | $caseInsensitive = ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP');
|
---|
1407 | $numbersFirst = -1 if $$table{VARS} and $$table{VARS}{ALPHA_FIRST};
|
---|
1408 | my @keys = sort NumbersFirst TagTableKeys($table);
|
---|
1409 | $numbersFirst = 1;
|
---|
1410 | foreach (@keys) {
|
---|
1411 | my @infoArray = GetTagInfoList($table,$_);
|
---|
1412 | my $tagInfo;
|
---|
1413 | foreach $tagInfo (@infoArray) {
|
---|
1414 | my $subdir = $$tagInfo{SubDirectory} or next;
|
---|
1415 | $tableName = $$subdir{TagTable} or next;
|
---|
1416 | next if $gotTable{$tableName}; # next if table already loaded
|
---|
1417 | push @moreTables, $tableName; # must scan this one too
|
---|
1418 | }
|
---|
1419 | }
|
---|
1420 | unshift @tableNames, @moreTables;
|
---|
1421 | }
|
---|
1422 | # clean up the order for tables which are out of order
|
---|
1423 | # (groups all Canon and Kodak tables together)
|
---|
1424 | my %fixOrder;
|
---|
1425 | foreach (@outOfOrder) {
|
---|
1426 | next unless /^Image::ExifTool::(\w+)/;
|
---|
1427 | # only re-order tables which have a corresponding main table
|
---|
1428 | next unless $mainTables{$1};
|
---|
1429 | $fixOrder{$1} = []; # fix the order of these tables
|
---|
1430 | }
|
---|
1431 | my (@sortedTables, %fixPos, $pos);
|
---|
1432 | foreach (@orderedTables) {
|
---|
1433 | if (/^Image::ExifTool::(\w+)/ and $fixOrder{$1}) {
|
---|
1434 | my $fix = $fixOrder{$1};
|
---|
1435 | unless (@$fix) {
|
---|
1436 | $pos = @sortedTables;
|
---|
1437 | $fixPos{$pos} or $fixPos{$pos} = [];
|
---|
1438 | push @{$fixPos{$pos}}, $1;
|
---|
1439 | }
|
---|
1440 | push @{$fix}, $_;
|
---|
1441 | } else {
|
---|
1442 | push @sortedTables, $_;
|
---|
1443 | }
|
---|
1444 | }
|
---|
1445 | # insert back in better order
|
---|
1446 | foreach $pos (sort { $b <=> $a } keys %fixPos) { # (reverse sort)
|
---|
1447 | my $fix = $fixPos{$pos};
|
---|
1448 | foreach (@$fix) {
|
---|
1449 | splice(@sortedTables, $pos, 0, @{$fixOrder{$_}});
|
---|
1450 | }
|
---|
1451 | }
|
---|
1452 | # tweak the table order
|
---|
1453 | my %tweakOrder = (
|
---|
1454 | JPEG => '-', # JPEG comes first
|
---|
1455 | IPTC => 'Exif', # put IPTC after EXIF,
|
---|
1456 | GPS => 'XMP', # etc...
|
---|
1457 | GeoTiff => 'GPS',
|
---|
1458 | CanonVRD=> 'CanonCustom',
|
---|
1459 | Kodak => 'JVC',
|
---|
1460 | 'Kodak::IFD' => 'Kodak::Unknown',
|
---|
1461 | 'Kodak::TextualInfo' => 'Kodak::IFD',
|
---|
1462 | 'Kodak::Processing' => 'Kodak::TextualInfo',
|
---|
1463 | Leaf => 'Kodak',
|
---|
1464 | Minolta => 'Leaf',
|
---|
1465 | SonyIDC => 'Sony',
|
---|
1466 | Unknown => 'SonyIDC',
|
---|
1467 | DNG => 'Unknown',
|
---|
1468 | PrintIM => 'ICC_Profile',
|
---|
1469 | ID3 => 'PostScript',
|
---|
1470 | MinoltaRaw => 'KyoceraRaw',
|
---|
1471 | KyoceraRaw => 'CanonRaw',
|
---|
1472 | SigmaRaw => 'PanasonicRaw',
|
---|
1473 | Olympus => 'NikonCapture',
|
---|
1474 | PhotoMechanic => 'FotoStation',
|
---|
1475 | Microsoft => 'PhotoMechanic',
|
---|
1476 | 'Microsoft::MP'=> 'Microsoft::MP1',
|
---|
1477 | GIMP => 'Microsoft',
|
---|
1478 | 'Nikon::CameraSettingsD300' => 'Nikon::ShotInfoD300b',
|
---|
1479 | 'Pentax::LensData' => 'Pentax::LensInfo2',
|
---|
1480 | 'Sony::SRF2' => 'Sony::SRF',
|
---|
1481 | 'Samsung::INFO' => 'Samsung::Type2', # (necessary because Samsung doesn't have a main table)
|
---|
1482 | 'Samsung::MP4' => 'Samsung::INFO', # (necessary because Samsung doesn't have a main table)
|
---|
1483 | );
|
---|
1484 | my @tweak = sort keys %tweakOrder;
|
---|
1485 | while (@tweak) {
|
---|
1486 | my $table = shift @tweak;
|
---|
1487 | my $first = $tweakOrder{$table};
|
---|
1488 | if ($tweakOrder{$first}) {
|
---|
1489 | push @tweak, $table; # must defer this till later
|
---|
1490 | next;
|
---|
1491 | }
|
---|
1492 | delete $tweakOrder{$table}; # because the table won't move again
|
---|
1493 | my @moving = grep /^Image::ExifTool::$table\b/, @sortedTables;
|
---|
1494 | my @notMoving = grep !/^Image::ExifTool::$table\b/, @sortedTables;
|
---|
1495 | my @after;
|
---|
1496 | while (@notMoving) {
|
---|
1497 | last if $notMoving[-1] =~ /^Image::ExifTool::$first\b/;
|
---|
1498 | unshift @after, pop @notMoving;
|
---|
1499 | }
|
---|
1500 | @sortedTables = (@notMoving, @moving, @after);
|
---|
1501 | }
|
---|
1502 | return @sortedTables
|
---|
1503 | }
|
---|
1504 |
|
---|
1505 | #------------------------------------------------------------------------------
|
---|
1506 | # Open HTMLFILE and print header and description
|
---|
1507 | # Inputs: 0) Filename, 1) optional category
|
---|
1508 | # Returns: True on success
|
---|
1509 | my %createdFiles;
|
---|
1510 | sub OpenHtmlFile($;$$)
|
---|
1511 | {
|
---|
1512 | my ($htmldir, $category, $sepTable) = @_;
|
---|
1513 | my ($htmlFile, $head, $title, $url, $class);
|
---|
1514 | my $top = '';
|
---|
1515 |
|
---|
1516 | if ($category) {
|
---|
1517 | my @names = split ' ', $category;
|
---|
1518 | $class = shift @names;
|
---|
1519 | $htmlFile = "$htmldir/TagNames/$class.html";
|
---|
1520 | $head = $category;
|
---|
1521 | if ($head =~ /^XMP .+ Struct$/) {
|
---|
1522 | pop @names;
|
---|
1523 | } else {
|
---|
1524 | $head .= ($sepTable ? ' Values' : ' Tags');
|
---|
1525 | }
|
---|
1526 | ($title = $head) =~ s/ .* / /;
|
---|
1527 | @names and $url = join '_', @names;
|
---|
1528 | } else {
|
---|
1529 | $htmlFile = "$htmldir/TagNames/index.html";
|
---|
1530 | $category = $class = 'ExifTool';
|
---|
1531 | $head = $title = 'ExifTool Tag Names';
|
---|
1532 | }
|
---|
1533 | if ($createdFiles{$htmlFile}) {
|
---|
1534 | open(HTMLFILE, ">>${htmlFile}_tmp") or return 0;
|
---|
1535 | } else {
|
---|
1536 | open(HTMLFILE, ">${htmlFile}_tmp") or return 0;
|
---|
1537 | print HTMLFILE "$docType<html>\n<head>\n<title>$title</title>\n";
|
---|
1538 | print HTMLFILE "<link rel=stylesheet type='text/css' href='style.css' title='Style'>\n";
|
---|
1539 | print HTMLFILE "</head>\n<body>\n";
|
---|
1540 | if ($category ne $class and $docs{$class}) {
|
---|
1541 | print HTMLFILE "<h2 class=top>$class Tags</h2>\n" or return 0;
|
---|
1542 | print HTMLFILE '<p>',Doc2Html($docs{$class}),"</p>\n" or return 0;
|
---|
1543 | } else {
|
---|
1544 | $top = " class=top";
|
---|
1545 | }
|
---|
1546 | }
|
---|
1547 | $head = "<a name='$url'>$head</a>" if $url;
|
---|
1548 | print HTMLFILE "<h2$top>$head</h2>\n" or return 0;
|
---|
1549 | print HTMLFILE '<p>',Doc2Html($docs{$category}),"</p>\n" if $docs{$category};
|
---|
1550 | $createdFiles{$htmlFile} = 1;
|
---|
1551 | return 1;
|
---|
1552 | }
|
---|
1553 |
|
---|
1554 | #------------------------------------------------------------------------------
|
---|
1555 | # Close all html files and write trailers
|
---|
1556 | # Returns: true on success
|
---|
1557 | # Inputs: 0) BuildTagLookup object reference
|
---|
1558 | sub CloseHtmlFiles($)
|
---|
1559 | {
|
---|
1560 | my $self = shift;
|
---|
1561 | my $preserveDate = $$self{PRESERVE_DATE};
|
---|
1562 | my $success = 1;
|
---|
1563 | # get the date
|
---|
1564 | my ($sec,$min,$hr,$day,$mon,$yr) = localtime;
|
---|
1565 | my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
|
---|
1566 | $yr += 1900;
|
---|
1567 | my $date = "$month[$mon] $day, $yr";
|
---|
1568 | my $htmlFile;
|
---|
1569 | my $countNewFiles = 0;
|
---|
1570 | my $countSameFiles = 0;
|
---|
1571 | foreach $htmlFile (keys %createdFiles) {
|
---|
1572 | my $tmpFile = $htmlFile . '_tmp';
|
---|
1573 | my $fileDate = $date;
|
---|
1574 | if ($preserveDate) {
|
---|
1575 | my @lines = `grep '<i>Last revised' $htmlFile`;
|
---|
1576 | $fileDate = $1 if @lines and $lines[-1] =~ m{<i>Last revised (.*)</i>};
|
---|
1577 | }
|
---|
1578 | open(HTMLFILE, ">>$tmpFile") or $success = 0, next;
|
---|
1579 | # write the trailers
|
---|
1580 | print HTMLFILE "<hr>\n";
|
---|
1581 | print HTMLFILE "(This document generated automatically by Image::ExifTool::BuildTagLookup)\n";
|
---|
1582 | print HTMLFILE "<br><i>Last revised $fileDate</i>\n";
|
---|
1583 | print HTMLFILE "<p class=lf><a href=";
|
---|
1584 | if ($htmlFile =~ /index\.html$/) {
|
---|
1585 | print HTMLFILE "'../index.html'><-- Back to ExifTool home page</a></p>\n";
|
---|
1586 | } else {
|
---|
1587 | print HTMLFILE "'index.html'><-- ExifTool Tag Names</a></p>\n"
|
---|
1588 | }
|
---|
1589 | print HTMLFILE "</body>\n</html>\n" or $success = 0;
|
---|
1590 | close HTMLFILE or $success = 0;
|
---|
1591 | # check for differences and only use new file if it was changed
|
---|
1592 | # (so the date only gets updated if changes were really made)
|
---|
1593 | my $useNewFile;
|
---|
1594 | if ($success) {
|
---|
1595 | open (TEMPFILE, $tmpFile) or $success = 0, last;
|
---|
1596 | if (open (HTMLFILE, $htmlFile)) {
|
---|
1597 | while (<HTMLFILE>) {
|
---|
1598 | my $newLine = <TEMPFILE>;
|
---|
1599 | if (defined $newLine) {
|
---|
1600 | next if /^<br><i>Last revised/;
|
---|
1601 | next if $_ eq $newLine;
|
---|
1602 | }
|
---|
1603 | # files are different -- use the new file
|
---|
1604 | $useNewFile = 1;
|
---|
1605 | last;
|
---|
1606 | }
|
---|
1607 | $useNewFile = 1 if <TEMPFILE>;
|
---|
1608 | close HTMLFILE;
|
---|
1609 | } else {
|
---|
1610 | $useNewFile = 1;
|
---|
1611 | }
|
---|
1612 | close TEMPFILE;
|
---|
1613 | if ($useNewFile) {
|
---|
1614 | ++$countNewFiles;
|
---|
1615 | rename $tmpFile, $htmlFile or warn("Error renaming temporary file\n"), $success = 0;
|
---|
1616 | } else {
|
---|
1617 | ++$countSameFiles;
|
---|
1618 | unlink $tmpFile; # erase new file and use existing file
|
---|
1619 | }
|
---|
1620 | }
|
---|
1621 | last unless $success;
|
---|
1622 | }
|
---|
1623 | # save number of files processed so we can check the results later
|
---|
1624 | $self->{COUNT}->{'HTML files changed'} = $countNewFiles;
|
---|
1625 | $self->{COUNT}->{'HTML files unchanged'} = $countSameFiles;
|
---|
1626 | return $success;
|
---|
1627 | }
|
---|
1628 |
|
---|
1629 | #------------------------------------------------------------------------------
|
---|
1630 | # Write the TagName HTML and POD documentation
|
---|
1631 | # Inputs: 0) BuildTagLookup object reference
|
---|
1632 | # 1) output pod file (ie. 'lib/Image/ExifTool/TagNames.pod')
|
---|
1633 | # 2) output html directory (ie. 'html')
|
---|
1634 | # Returns: true on success
|
---|
1635 | # Notes: My apologies for the patchwork code, but this is only used to generate the docs.
|
---|
1636 | sub WriteTagNames($$)
|
---|
1637 | {
|
---|
1638 | my ($self, $podFile, $htmldir) = @_;
|
---|
1639 | my ($tableName, $short, $url, @sepTables, @structs);
|
---|
1640 | my $tagNameInfo = $self->{TAG_NAME_INFO} or return 0;
|
---|
1641 | my $idLabel = $self->{ID_LOOKUP};
|
---|
1642 | my $shortName = $self->{SHORT_NAME};
|
---|
1643 | my $sepTable = $self->{SEPARATE_TABLE};
|
---|
1644 | my $structs = $self->{STRUCTURES};
|
---|
1645 | my $structLookup = $self->{STRUCT_LOOKUP};
|
---|
1646 | my $success = 1;
|
---|
1647 | my $columns = 6; # number of columns in html index
|
---|
1648 | my $percent = int(100 / $columns);
|
---|
1649 |
|
---|
1650 | # open the file and write the header
|
---|
1651 | open(PODFILE, ">$podFile") or return 0;
|
---|
1652 | print PODFILE Doc2Pod($docs{PodHeader}, $docs{ExifTool}, $docs{ExifTool2});
|
---|
1653 | mkdir "$htmldir/TagNames";
|
---|
1654 | OpenHtmlFile($htmldir) or return 0;
|
---|
1655 | print HTMLFILE "<blockquote>\n";
|
---|
1656 | print HTMLFILE "<table width='100%' class=frame><tr><td>\n";
|
---|
1657 | print HTMLFILE "<table width='100%' class=inner cellspacing=1><tr class=h>\n";
|
---|
1658 | print HTMLFILE "<th colspan=$columns><span class=l>Tag Table Index</span></th></tr>\n";
|
---|
1659 | print HTMLFILE "<tr class=b><td width='$percent%'>\n";
|
---|
1660 | # write the index
|
---|
1661 | my @tableNames = GetTableOrder();
|
---|
1662 | # add shortcuts last
|
---|
1663 | push @tableNames, 'Image::ExifTool::Shortcuts::Main';
|
---|
1664 | push @tableNames, @pluginTables;
|
---|
1665 | # get list of headings and add any missing ones
|
---|
1666 | my $heading = 'xxx';
|
---|
1667 | my (@tableIndexNames, @headings);
|
---|
1668 | foreach $tableName (@tableNames) {
|
---|
1669 | $short = $$shortName{$tableName};
|
---|
1670 | my @names = split ' ', $short;
|
---|
1671 | my $class = shift @names;
|
---|
1672 | if (@names) {
|
---|
1673 | # add heading for tables without a Main
|
---|
1674 | unless ($heading eq $class) {
|
---|
1675 | $heading = $class;
|
---|
1676 | push @tableIndexNames, $heading;
|
---|
1677 | push @headings, $heading;
|
---|
1678 | }
|
---|
1679 | } else {
|
---|
1680 | $heading = $short;
|
---|
1681 | push @headings, $heading;
|
---|
1682 | }
|
---|
1683 | push @tableIndexNames, $tableName;
|
---|
1684 | }
|
---|
1685 | @tableNames = @tableIndexNames;
|
---|
1686 | # print html index of headings only
|
---|
1687 | my $count = 0;
|
---|
1688 | my $lines = int((scalar(@headings) + $columns - 1) / $columns);
|
---|
1689 | foreach $tableName (@headings) {
|
---|
1690 | if ($count) {
|
---|
1691 | if ($count % $lines) {
|
---|
1692 | print HTMLFILE "<br>\n";
|
---|
1693 | } else {
|
---|
1694 | print HTMLFILE "</td><td width='$percent%'>\n";
|
---|
1695 | }
|
---|
1696 | }
|
---|
1697 | $short = $$shortName{$tableName};
|
---|
1698 | $short = $tableName unless $short;
|
---|
1699 | $url = "$short.html";
|
---|
1700 | print HTMLFILE "<a href='$url'>$short</a>";
|
---|
1701 | ++$count;
|
---|
1702 | }
|
---|
1703 | print HTMLFILE "\n</td></tr></table></td></tr></table></blockquote>\n";
|
---|
1704 | print HTMLFILE '<p>',Doc2Html($docs{ExifTool2}),"</p>\n";
|
---|
1705 | # write all the tag tables
|
---|
1706 | while (@tableNames or @sepTables or @structs) {
|
---|
1707 | while (@sepTables) {
|
---|
1708 | $tableName = shift @sepTables;
|
---|
1709 | my $printConv = $$sepTable{$tableName};
|
---|
1710 | next unless ref $printConv eq 'HASH';
|
---|
1711 | $$sepTable{$tableName} = 1;
|
---|
1712 | my $notes = $$printConv{Notes};
|
---|
1713 | if ($notes) {
|
---|
1714 | # remove unnecessary whitespace
|
---|
1715 | $notes =~ s/(^\s+|\s+$)//g;
|
---|
1716 | $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
|
---|
1717 | }
|
---|
1718 | my $head = $tableName;
|
---|
1719 | $head =~ s/.* //;
|
---|
1720 | close HTMLFILE;
|
---|
1721 | if (OpenHtmlFile($htmldir, $tableName, 1)) {
|
---|
1722 | print HTMLFILE '<p>', Doc2Html($notes), "</p>\n" if $notes;
|
---|
1723 | print HTMLFILE "<blockquote>\n";
|
---|
1724 | print HTMLFILE "<table class=frame><tr><td>\n";
|
---|
1725 | print HTMLFILE "<table class='inner sep' cellspacing=1>\n";
|
---|
1726 | my $align = ' class=r';
|
---|
1727 | my $wid = 0;
|
---|
1728 | my @keys;
|
---|
1729 | foreach (sort NumbersFirst keys %$printConv) {
|
---|
1730 | next if /^(Notes|PrintHex|PrintString|OTHER)$/;
|
---|
1731 | $align = '' if $align and /[^\d]/;
|
---|
1732 | my $w = length($_) + length($$printConv{$_});
|
---|
1733 | $wid = $w if $wid < $w;
|
---|
1734 | push @keys, $_;
|
---|
1735 | }
|
---|
1736 | $wid = length($tableName)+7 if $wid < length($tableName)+7;
|
---|
1737 | # print in multiple columns if there is room
|
---|
1738 | my $cols = int(80 / ($wid + 4));
|
---|
1739 | $cols = 1 if $cols < 1 or $cols > @keys or @keys < 4;
|
---|
1740 | my $rows = int((scalar(@keys) + $cols - 1) / $cols);
|
---|
1741 | my ($r, $c);
|
---|
1742 | print HTMLFILE '<tr class=h>';
|
---|
1743 | for ($c=0; $c<$cols; ++$c) {
|
---|
1744 | print HTMLFILE "<th>Value</th><th>$head</th>";
|
---|
1745 | }
|
---|
1746 | print HTMLFILE "</tr>\n";
|
---|
1747 | for ($r=0; $r<$rows; ++$r) {
|
---|
1748 | print HTMLFILE '<tr>';
|
---|
1749 | for ($c=0; $c<$cols; ++$c) {
|
---|
1750 | my $key = $keys[$r + $c*$rows];
|
---|
1751 | my ($index, $prt);
|
---|
1752 | if (defined $key) {
|
---|
1753 | $index = $key;
|
---|
1754 | $prt = '= ' . EscapeHTML($$printConv{$key});
|
---|
1755 | if ($$printConv{PrintHex}) {
|
---|
1756 | $index = sprintf('0x%x',$index);
|
---|
1757 | } elsif ($$printConv{PrintString} or
|
---|
1758 | $index !~ /^[+-]?(?=\d|\.\d)\d*(\.\d*)?$/)
|
---|
1759 | {
|
---|
1760 | $index = "'" . EscapeHTML($index) . "'";
|
---|
1761 | }
|
---|
1762 | } else {
|
---|
1763 | $index = $prt = ' ';
|
---|
1764 | }
|
---|
1765 | my ($ic, $pc);
|
---|
1766 | if ($c & 0x01) {
|
---|
1767 | $pc = ' class=b';
|
---|
1768 | $ic = $align ? " class='r b'" : $pc;
|
---|
1769 | } else {
|
---|
1770 | $ic = $align;
|
---|
1771 | $pc = '';
|
---|
1772 | }
|
---|
1773 | print HTMLFILE "<td$ic>$index</td><td$pc>$prt</td>\n";
|
---|
1774 | }
|
---|
1775 | print HTMLFILE '</tr>';
|
---|
1776 | }
|
---|
1777 | print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
|
---|
1778 | }
|
---|
1779 | }
|
---|
1780 | last unless @tableNames or @structs;
|
---|
1781 | my $isStruct;
|
---|
1782 | if (@structs) {
|
---|
1783 | $tableName = shift @structs;
|
---|
1784 | next if $$structs{$tableName} == 2; # only list each structure once
|
---|
1785 | $$structs{$tableName} = 2;
|
---|
1786 | $isStruct = $$structLookup{$tableName};
|
---|
1787 | $isStruct or warn("Missing structure $tableName\n"), next;
|
---|
1788 | $short = $tableName = "XMP $tableName Struct";
|
---|
1789 | my $maxLen = 0;
|
---|
1790 | $maxLen < length and $maxLen = length foreach keys %$isStruct;
|
---|
1791 | $$self{LONG_ID}{$tableName} = $maxLen;
|
---|
1792 | } else {
|
---|
1793 | $tableName = shift @tableNames;
|
---|
1794 | $short = $$shortName{$tableName};
|
---|
1795 | unless ($short) {
|
---|
1796 | # this is just an index heading
|
---|
1797 | print PODFILE "\n=head2 $tableName Tags\n";
|
---|
1798 | print PODFILE Doc2Pod($docs{$tableName}) if $docs{$tableName};
|
---|
1799 | next;
|
---|
1800 | }
|
---|
1801 | }
|
---|
1802 | my $isExif = $tableName eq 'Image::ExifTool::Exif::Main' ? 1 : undef;
|
---|
1803 | my $isRiff = $tableName eq 'Image::ExifTool::RIFF::Info' ? 1 : undef;
|
---|
1804 | my $info = $$tagNameInfo{$tableName};
|
---|
1805 | my $id = $$idLabel{$tableName};
|
---|
1806 | my ($hid, $showGrp);
|
---|
1807 | # widths of the different columns in the POD documentation
|
---|
1808 | my ($wID,$wTag,$wReq,$wGrp) = (8,36,24,10);
|
---|
1809 | my ($composite, $derived, $notes, $prefix);
|
---|
1810 | if ($short eq 'Shortcuts') {
|
---|
1811 | $derived = '<th>Refers To</th>';
|
---|
1812 | $composite = 2;
|
---|
1813 | } elsif ($isStruct) {
|
---|
1814 | $derived = '';
|
---|
1815 | $notes = $$isStruct{NOTES};
|
---|
1816 | } else {
|
---|
1817 | my $table = GetTagTable($tableName);
|
---|
1818 | $notes = $$table{NOTES};
|
---|
1819 | if ($$table{GROUPS}{0} eq 'Composite') {
|
---|
1820 | $composite = 1;
|
---|
1821 | $derived = '<th>Derived From</th>';
|
---|
1822 | } else {
|
---|
1823 | $composite = 0;
|
---|
1824 | $derived = '';
|
---|
1825 | }
|
---|
1826 | }
|
---|
1827 | my $podIdLen = $self->{LONG_ID}->{$tableName};
|
---|
1828 | if ($notes) {
|
---|
1829 | # remove unnecessary whitespace
|
---|
1830 | $notes =~ s/(^\s+|\s+$)//g;
|
---|
1831 | $notes =~ s/(^[ \t]+|[ \t]+$)//mg;
|
---|
1832 | if ($notes =~ /leading '(.*?_)' which/) {
|
---|
1833 | $prefix = $1;
|
---|
1834 | $podIdLen -= length $prefix;
|
---|
1835 | }
|
---|
1836 | }
|
---|
1837 | if ($podIdLen <= $wID) {
|
---|
1838 | $podIdLen = $wID;
|
---|
1839 | } elsif ($short eq 'DICOM') {
|
---|
1840 | $podIdLen = 10;
|
---|
1841 | } else {
|
---|
1842 | # align tag names in secondary columns if possible
|
---|
1843 | my $col = ($podIdLen <= 10) ? 12 : 20;
|
---|
1844 | $podIdLen = $col if $podIdLen < $col;
|
---|
1845 | }
|
---|
1846 | if ($id) {
|
---|
1847 | ($hid = "<th>$id</th>") =~ s/ / /g;
|
---|
1848 | $wTag -= $podIdLen - $wID;
|
---|
1849 | $wID = $podIdLen;
|
---|
1850 | my $longTag = $self->{LONG_NAME}->{$tableName};
|
---|
1851 | if ($wTag < $longTag) {
|
---|
1852 | warn "Notice: Long tags in $tableName table\n";
|
---|
1853 | if ($wID - $longTag + $wTag >= 6) { # don't let ID column get too narrow
|
---|
1854 | $wID -= $longTag - $wTag;
|
---|
1855 | $wTag = $longTag;
|
---|
1856 | }
|
---|
1857 | }
|
---|
1858 | } elsif ($composite) {
|
---|
1859 | $wTag += $wID - $wReq;
|
---|
1860 | $hid = '';
|
---|
1861 | } else {
|
---|
1862 | $wTag += 9;
|
---|
1863 | $hid = '';
|
---|
1864 | }
|
---|
1865 | if ($short eq 'EXIF') {
|
---|
1866 | $derived = '<th>Group</th>';
|
---|
1867 | $showGrp = 1;
|
---|
1868 | $wTag -= $wGrp + 1;
|
---|
1869 | }
|
---|
1870 | my $head = ($short =~ / /) ? 'head3' : 'head2';
|
---|
1871 | my $str = $isStruct ? '' : ' Tags';
|
---|
1872 | print PODFILE "\n=$head $short$str\n";
|
---|
1873 | print PODFILE Doc2Pod($docs{$short}) if $docs{$short};
|
---|
1874 | print PODFILE "\n", Doc2Pod($notes), "\n" if $notes;
|
---|
1875 | my $line = "\n";
|
---|
1876 | if ($id) {
|
---|
1877 | # shift over 'Index' heading by one character for a bit more balance
|
---|
1878 | $id = " $id" if $id eq 'Index';
|
---|
1879 | $line .= sprintf " %-${wID}s", $id;
|
---|
1880 | } else {
|
---|
1881 | $line .= ' ';
|
---|
1882 | }
|
---|
1883 | my $tagNameHeading = ($short eq 'XMP') ? 'Namespace' : ($isStruct?'Field':'Tag').' Name';
|
---|
1884 | $line .= sprintf " %-${wTag}s", $tagNameHeading;
|
---|
1885 | $line .= sprintf " %-${wReq}s", $composite == 2 ? 'Refers To' : 'Derived From' if $composite;
|
---|
1886 | $line .= sprintf " %-${wGrp}s", 'Group' if $showGrp;
|
---|
1887 | $line .= ' Writable';
|
---|
1888 | print PODFILE $line;
|
---|
1889 | $line =~ s/^(\s*\w.{6}\w) /$1\t/; # change space to tab after long ID label (ie. "Sequence")
|
---|
1890 | $line =~ s/\S/-/g;
|
---|
1891 | $line =~ s/- -/---/g;
|
---|
1892 | $line =~ tr/\t/ /; # change tab back to space
|
---|
1893 | print PODFILE $line,"\n";
|
---|
1894 | close HTMLFILE;
|
---|
1895 | OpenHtmlFile($htmldir, $short) or $success = 0;
|
---|
1896 | print HTMLFILE '<p>',Doc2Html($notes), "</p>\n" if $notes;
|
---|
1897 | print HTMLFILE "<blockquote>\n";
|
---|
1898 | print HTMLFILE "<table class=frame><tr><td>\n";
|
---|
1899 | print HTMLFILE "<table class=inner cellspacing=1>\n";
|
---|
1900 | print HTMLFILE "<tr class=h>$hid<th>$tagNameHeading</th>\n";
|
---|
1901 | print HTMLFILE "<th>Writable</th>$derived<th>Values / ${noteFont}Notes</span></th></tr>\n";
|
---|
1902 | my $rowClass = 1;
|
---|
1903 | my $infoCount = 0;
|
---|
1904 | my $infoList;
|
---|
1905 | foreach $infoList (@$info) {
|
---|
1906 | ++$infoCount;
|
---|
1907 | my ($tagIDstr, $tagNames, $writable, $values, $require, $writeGroup) = @$infoList;
|
---|
1908 | my ($align, $idStr, $w, $tip);
|
---|
1909 | my $wTag2 = $wTag;
|
---|
1910 | if (not $id) {
|
---|
1911 | $idStr = ' ';
|
---|
1912 | } elsif ($tagIDstr =~ /^\d+(\.\d+)?$/) {
|
---|
1913 | $w = $wID - 3;
|
---|
1914 | $idStr = sprintf " %${w}g ", $tagIDstr;
|
---|
1915 | $align = " class=r";
|
---|
1916 | } else {
|
---|
1917 | $tagIDstr =~ s/^'$prefix/'/ if $prefix;
|
---|
1918 | $w = $wID;
|
---|
1919 | my $over = length($tagIDstr) - $w;
|
---|
1920 | if ($over > 0) {
|
---|
1921 | # shift over tag name if there is room
|
---|
1922 | if ($over <= $wTag - length($$tagNames[0])) {
|
---|
1923 | $wTag2 -= $over;
|
---|
1924 | $w += $over;
|
---|
1925 | } else {
|
---|
1926 | # put tag name on next line if ID is too long
|
---|
1927 | $idStr = " $tagIDstr\n " . (' ' x $w);
|
---|
1928 | warn "Notice: Split $$tagNames[0] line\n";
|
---|
1929 | }
|
---|
1930 | }
|
---|
1931 | $idStr = sprintf " %-${w}s ", $tagIDstr unless defined $idStr;
|
---|
1932 | $align = '';
|
---|
1933 | }
|
---|
1934 | my @reqs;
|
---|
1935 | my @tags = @$tagNames;
|
---|
1936 | my @wGrp = @$writeGroup;
|
---|
1937 | my @vals = @$writable;
|
---|
1938 | my $wrStr = shift @vals;
|
---|
1939 | my $subdir;
|
---|
1940 | my @masks = grep /^\[Mask 0x[\da-f]+\]/, @$values;
|
---|
1941 | my $tag = shift @tags;
|
---|
1942 | # if this is a subdirectory or structure, print subdir name (from values) instead of writable
|
---|
1943 | if ($wrStr =~ /^[-=]/) {
|
---|
1944 | $subdir = 1;
|
---|
1945 | if (@masks) {
|
---|
1946 | # combine any mask into the format string
|
---|
1947 | $wrStr .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/;
|
---|
1948 | shift @masks;
|
---|
1949 | @vals = grep !/^\[Mask 0x[\da-f]+\]/, @$values;
|
---|
1950 | } else {
|
---|
1951 | @vals = @$values;
|
---|
1952 | }
|
---|
1953 | # remove Notes if subdir has Notes as well
|
---|
1954 | shift @vals if $vals[0] =~ /^\(/ and @vals >= @$writable;
|
---|
1955 | foreach (@vals) { /^\(/ and $_ = '-' }
|
---|
1956 | my $i; # fill in any missing entries from non-directory tags
|
---|
1957 | for ($i=0; $i<@$writable; ++$i) {
|
---|
1958 | $vals[$i] = $$writable[$i] unless defined $vals[$i];
|
---|
1959 | if (@masks) {
|
---|
1960 | $vals[$i] .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/;
|
---|
1961 | shift @masks;
|
---|
1962 | }
|
---|
1963 | }
|
---|
1964 | if ($$sepTable{$vals[0]}) {
|
---|
1965 | $wrStr =~ s/^[-=]//;
|
---|
1966 | $wrStr = 'N' unless $wrStr;
|
---|
1967 | } elsif ($$structs{$vals[0]}) {
|
---|
1968 | my $flags = $wrStr =~ /([+_]+)$/ ? $1 : '';
|
---|
1969 | $wrStr = "$vals[0] Struct$flags";
|
---|
1970 | } else {
|
---|
1971 | $wrStr = $vals[0];
|
---|
1972 | }
|
---|
1973 | shift @vals;
|
---|
1974 | } elsif ($wrStr and $wrStr ne 'N' and @masks) {
|
---|
1975 | # fill in missing entries if masks are different
|
---|
1976 | my $mask = shift @masks;
|
---|
1977 | while (@masks > @vals) {
|
---|
1978 | last if $masks[@vals] eq $mask;
|
---|
1979 | push @vals, $wrStr;
|
---|
1980 | push @tags, $tag if @tags < @vals;
|
---|
1981 | }
|
---|
1982 | # add Mask to Writable column in POD doc
|
---|
1983 | $wrStr .= " & $1" if $mask =~ /(0x[\da-f]+)/;
|
---|
1984 | }
|
---|
1985 | printf PODFILE "%s%-${wTag2}s", $idStr, $tag;
|
---|
1986 | warn "Warning: Pushed $tag\n" if $id and length($tag) > $wTag2;
|
---|
1987 | printf PODFILE " %-${wGrp}s", shift(@wGrp) || '-' if $showGrp;
|
---|
1988 | if ($composite) {
|
---|
1989 | @reqs = @$require;
|
---|
1990 | $w = $wReq; # Keep writable column in line
|
---|
1991 | length($tag) > $wTag2 and $w -= length($tag) - $wTag2;
|
---|
1992 | printf PODFILE " %-${w}s", shift(@reqs) || '';
|
---|
1993 | }
|
---|
1994 | printf PODFILE " $wrStr\n";
|
---|
1995 | my $numTags = scalar @$tagNames;
|
---|
1996 | my $n = 0;
|
---|
1997 | while (@tags or @reqs or @vals) {
|
---|
1998 | my $more = (@tags or @reqs);
|
---|
1999 | $line = ' ';
|
---|
2000 | $line .= ' 'x($wID+1) if $id;
|
---|
2001 | $line .= sprintf("%-${wTag2}s", shift(@tags) || '');
|
---|
2002 | $line .= sprintf(" %-${wReq}s", shift(@reqs) || '') if $composite;
|
---|
2003 | $line .= sprintf(" %-${wGrp}s", shift(@wGrp) || '-') if $showGrp;
|
---|
2004 | ++$n;
|
---|
2005 | if (@vals) {
|
---|
2006 | my $val = shift @vals;
|
---|
2007 | # use writable if this is a note
|
---|
2008 | my $wrStr = $$writable[$n];
|
---|
2009 | if ($subdir and ($val =~ /^\(/ or $val =~ /=/ or ($wrStr and $wrStr !~ /^[-=]/))) {
|
---|
2010 | $val = $wrStr;
|
---|
2011 | if (defined $val) {
|
---|
2012 | $val =~ s/^[-=]//;
|
---|
2013 | } else {
|
---|
2014 | # done with tag if nothing else to print
|
---|
2015 | last unless $more;
|
---|
2016 | }
|
---|
2017 | }
|
---|
2018 | if (defined $val) {
|
---|
2019 | $line .= " $val";
|
---|
2020 | if (@masks) {
|
---|
2021 | $line .= " & $1" if $masks[0] =~ /(0x[\da-f]+)/;
|
---|
2022 | shift @masks;
|
---|
2023 | }
|
---|
2024 | }
|
---|
2025 | }
|
---|
2026 | $line =~ s/\s+$//; # trim trailing white space
|
---|
2027 | print PODFILE "$line\n";
|
---|
2028 | }
|
---|
2029 | my @htmlTags;
|
---|
2030 | foreach (@$tagNames) {
|
---|
2031 | push @htmlTags, EscapeHTML($_);
|
---|
2032 | }
|
---|
2033 | if (($isExif and $exifSpec{hex $tagIDstr}) or
|
---|
2034 | ($isRiff and $tagIDstr=~/(\w+)/ and $riffSpec{$1}))
|
---|
2035 | {
|
---|
2036 | # underline "unknown" makernote tags only
|
---|
2037 | my $n = $tagIDstr eq '0x927c' ? -1 : 0;
|
---|
2038 | $htmlTags[$n] = "<u>$htmlTags[$n]</u>";
|
---|
2039 | }
|
---|
2040 | $rowClass = $rowClass ? '' : " class=b";
|
---|
2041 | my $isSubdir;
|
---|
2042 | if ($$writable[0] =~ /^[-=]/) {
|
---|
2043 | $isSubdir = 1;
|
---|
2044 | s/^[-=](.+)/$1/ foreach @$writable;
|
---|
2045 | }
|
---|
2046 | # add tooltip for hex conversion of Tag ID
|
---|
2047 | if ($tagIDstr =~ /^0x[0-9a-f]+$/i) {
|
---|
2048 | $tip = sprintf(" title='$tagIDstr = %u'",hex $tagIDstr);
|
---|
2049 | } elsif ($tagIDstr =~ /^(\d+)(\.\d*)?$/) {
|
---|
2050 | $tip = sprintf(" title='%u = 0x%x'", $1, $1);
|
---|
2051 | } else {
|
---|
2052 | $tip = '';
|
---|
2053 | # use copyright symbol in QuickTime UserData tags
|
---|
2054 | $tagIDstr =~ s/^"\\xa9/"©/;
|
---|
2055 | }
|
---|
2056 | # add tooltip for special writable attributes
|
---|
2057 | my $wtip = '';
|
---|
2058 | my %wattr = (
|
---|
2059 | '_' => 'Flattened',
|
---|
2060 | '+' => 'List',
|
---|
2061 | '/' => 'Avoided',
|
---|
2062 | '~' => 'Writable only with -n',
|
---|
2063 | '!' => 'Unsafe',
|
---|
2064 | '*' => 'Protected',
|
---|
2065 | ':' => 'Mandatory',
|
---|
2066 | );
|
---|
2067 | my ($wstr, %hasAttr, @hasAttr);
|
---|
2068 | foreach $wstr (@$writable) {
|
---|
2069 | next unless $wstr =~ m{([+/~!*:_]+)$};
|
---|
2070 | my @a = split //, $1;
|
---|
2071 | foreach (@a) {
|
---|
2072 | next if $hasAttr{$_};
|
---|
2073 | push @hasAttr, $_;
|
---|
2074 | $hasAttr{$_} = 1;
|
---|
2075 | }
|
---|
2076 | }
|
---|
2077 | if (@hasAttr) {
|
---|
2078 | $wtip = " title='";
|
---|
2079 | my $n = 0;
|
---|
2080 | foreach (@hasAttr) {
|
---|
2081 | $wtip .= "\n" if $n;
|
---|
2082 | $wtip .= " $_ = $wattr{$_}";
|
---|
2083 | ++$n;
|
---|
2084 | }
|
---|
2085 | $wtip .= "'";
|
---|
2086 | }
|
---|
2087 | # print this row in the tag table
|
---|
2088 | print HTMLFILE "<tr$rowClass>\n";
|
---|
2089 | print HTMLFILE "<td$align$tip>$tagIDstr</td>\n" if $id;
|
---|
2090 | print HTMLFILE "<td>", join("\n <br>",@htmlTags), "</td>\n";
|
---|
2091 | print HTMLFILE "<td class=c$wtip>",join('<br>',@$writable),"</td>\n";
|
---|
2092 | print HTMLFILE '<td class=n>',join("\n <br>",@$require),"</td>\n" if $composite;
|
---|
2093 | print HTMLFILE "<td class=c>",join('<br>',@$writeGroup),"</td>\n" if $showGrp;
|
---|
2094 | print HTMLFILE "<td>";
|
---|
2095 | if (@$values) {
|
---|
2096 | if ($isSubdir) {
|
---|
2097 | my ($smallNote, @values);
|
---|
2098 | foreach (@$values) {
|
---|
2099 | if (/^\(/) {
|
---|
2100 | # set the note font
|
---|
2101 | $smallNote = 1 if $numTags < 2;
|
---|
2102 | push @values, ($smallNote ? $noteFontSmall : $noteFont) . "$_</span>";
|
---|
2103 | next;
|
---|
2104 | }
|
---|
2105 | # make text in square brackets small
|
---|
2106 | /^\[/ and push(@values, "<span class=s>$_</span>"), next;
|
---|
2107 | /=/ and push(@values, $_), next;
|
---|
2108 | my @names = split;
|
---|
2109 | my $suffix = ' Tags';
|
---|
2110 | if ($$sepTable{$_}) {
|
---|
2111 | push @sepTables, $_;
|
---|
2112 | $suffix = ' Values';
|
---|
2113 | }
|
---|
2114 | # currently all structures are in XMP documentation
|
---|
2115 | if ($$structs{$_} and $short =~ /^XMP/) {
|
---|
2116 | unshift @names, 'XMP';
|
---|
2117 | push @structs, $_; # list this later
|
---|
2118 | $suffix = ' Struct';
|
---|
2119 | }
|
---|
2120 | $url = (shift @names) . '.html';
|
---|
2121 | @names and $url .= '#' . join '_', @names;
|
---|
2122 | push @values, "--> <a href='$url'>$_$suffix</a>";
|
---|
2123 | }
|
---|
2124 | # put small note last
|
---|
2125 | $smallNote and push @values, shift @values;
|
---|
2126 | print HTMLFILE join("\n <br>",@values);
|
---|
2127 | } else {
|
---|
2128 | my ($close, $br) = ('', '');
|
---|
2129 | foreach (@$values) {
|
---|
2130 | if (s/^\[!HTML\]//) {
|
---|
2131 | print HTMLFILE $close if $close;
|
---|
2132 | print HTMLFILE $_;
|
---|
2133 | $close = $br = '';
|
---|
2134 | } else {
|
---|
2135 | if (/^\(/) {
|
---|
2136 | # notes can use POD syntax
|
---|
2137 | $_ = $noteFont . Doc2Html($_) . "</span>";
|
---|
2138 | } else {
|
---|
2139 | $_ = EscapeHTML($_);
|
---|
2140 | }
|
---|
2141 | $close or $_ = "<span class=s>$_", $close = '</span>';
|
---|
2142 | print HTMLFILE $br, $_;
|
---|
2143 | $br = "\n <br>";
|
---|
2144 | }
|
---|
2145 | }
|
---|
2146 | print HTMLFILE $close if $close;
|
---|
2147 | }
|
---|
2148 | } else {
|
---|
2149 | print HTMLFILE ' ';
|
---|
2150 | }
|
---|
2151 | print HTMLFILE "</td></tr>\n";
|
---|
2152 | }
|
---|
2153 | unless ($infoCount) {
|
---|
2154 | printf PODFILE " [no tags known]\n";
|
---|
2155 | my $cols = 3;
|
---|
2156 | ++$cols if $hid;
|
---|
2157 | ++$cols if $derived;
|
---|
2158 | print HTMLFILE "<tr><td colspan=$cols class=c><i>[no tags known]</i></td></tr>\n";
|
---|
2159 | }
|
---|
2160 | print HTMLFILE "</table></td></tr></table></blockquote>\n\n";
|
---|
2161 | }
|
---|
2162 | close(HTMLFILE) or $success = 0;
|
---|
2163 | CloseHtmlFiles($self) or $success = 0;
|
---|
2164 | print PODFILE Doc2Pod($docs{PodTrailer}) or $success = 0;
|
---|
2165 | close(PODFILE) or $success = 0;
|
---|
2166 | return $success;
|
---|
2167 | }
|
---|
2168 |
|
---|
2169 | 1; # end
|
---|
2170 |
|
---|
2171 |
|
---|
2172 | __END__
|
---|
2173 |
|
---|
2174 | =head1 NAME
|
---|
2175 |
|
---|
2176 | Image::ExifTool::BuildTagLookup - Build ExifTool tag lookup tables
|
---|
2177 |
|
---|
2178 | =head1 DESCRIPTION
|
---|
2179 |
|
---|
2180 | This module is used to generate the tag lookup tables in
|
---|
2181 | Image::ExifTool::TagLookup.pm and tag name documentation in
|
---|
2182 | Image::ExifTool::TagNames.pod, as well as HTML tag name documentation. It
|
---|
2183 | is used before each new ExifTool release to update the lookup tables and
|
---|
2184 | documentation, but it is not used otherwise.
|
---|
2185 |
|
---|
2186 | =head1 SYNOPSIS
|
---|
2187 |
|
---|
2188 | use Image::ExifTool::BuildTagLookup;
|
---|
2189 |
|
---|
2190 | $builder = new Image::ExifTool::BuildTagLookup;
|
---|
2191 |
|
---|
2192 | $ok = $builder->WriteTagLookup('lib/Image/ExifTool/TagLookup.pm');
|
---|
2193 |
|
---|
2194 | $ok = $builder->WriteTagNames('lib/Image/ExifTool/TagNames.pod','html');
|
---|
2195 |
|
---|
2196 | =head1 MEMBER VARIABLES
|
---|
2197 |
|
---|
2198 | =over 4
|
---|
2199 |
|
---|
2200 | =item PRESERVE_DATE
|
---|
2201 |
|
---|
2202 | Flag to preserve "Last revised" date in HTML files. Set before calling
|
---|
2203 | WriteTagNames().
|
---|
2204 |
|
---|
2205 | =item COUNT
|
---|
2206 |
|
---|
2207 | Reference to hash containing counting statistics. Keys are the
|
---|
2208 | descriptions, and values are the numerical counts. Valid after
|
---|
2209 | BuildTagLookup object is created, but additional statistics are added by
|
---|
2210 | WriteTagNames().
|
---|
2211 |
|
---|
2212 | =back
|
---|
2213 |
|
---|
2214 | =head1 AUTHOR
|
---|
2215 |
|
---|
2216 | Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
|
---|
2217 |
|
---|
2218 | This library is free software; you can redistribute it and/or modify it
|
---|
2219 | under the same terms as Perl itself.
|
---|
2220 |
|
---|
2221 | =head1 SEE ALSO
|
---|
2222 |
|
---|
2223 | L<Image::ExifTool(3pm)|Image::ExifTool>,
|
---|
2224 | L<Image::ExifTool::TagLookup(3pm)|Image::ExifTool::TagLookup>,
|
---|
2225 | L<Image::ExifTool::TagNames(3pm)|Image::ExifTool::TagNames>
|
---|
2226 |
|
---|
2227 | =cut
|
---|