exif/
tag.rs

1//
2// Copyright (c) 2016 KAMADA Ken'ichi.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9//    notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11//    notice, this list of conditions and the following disclaimer in the
12//    documentation and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24// SUCH DAMAGE.
25//
26
27use std::fmt;
28
29use crate::error::Error;
30use crate::value;
31use crate::value::Value;
32use crate::util::atou16;
33
34/// A tag of a TIFF/Exif field.
35///
36/// Some well-known tags are provided as associated constants of
37/// this type.  The constant names follow the Exif specification
38/// but not the Rust naming conventions.
39///
40/// A non-predefined tag can also be specified
41/// by the context and the number as in `Tag(Context::Tiff, 0x100)`.
42//
43// This is not an enum to keep safety and API stability, while
44// supporting unknown tag numbers.  This comment is based on the
45// behavior of Rust 1.12.
46// Storing unknown values in a repr(u16) enum is unsafe.  The compiler
47// assumes that there is no undefined discriminant even with a C-like
48// enum, so the exhaustiveness check of a match expression will break.
49// Storing unknown values in a special variant such as Unknown(u16)
50// tends to break backward compatibility.  When Tag::VariantFoo is
51// defined in a new version of the library, the old codes using
52// Tag::Unknown(Foo's tag number) will break.
53//
54// Use of constants is restricted in patterns.  As of Rust 1.12,
55// PartialEq and Eq need to be _automatically derived_ for Tag to
56// emulate structural equivalency.
57// <https://github.com/rust-lang/rfcs/pull/1445>
58#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub struct Tag(pub Context, pub u16);
60
61impl Tag {
62    /// Returns the context of the tag.
63    ///
64    /// # Examples
65    /// ```
66    /// use exif::{Context, Tag};
67    /// assert_eq!(Tag::XResolution.context(), Context::Tiff);
68    /// assert_eq!(Tag::DateTimeOriginal.context(), Context::Exif);
69    /// ```
70    #[inline]
71    pub fn context(self) -> Context {
72        self.0
73    }
74
75    /// Returns the tag number.
76    ///
77    /// # Examples
78    /// ```
79    /// use exif::Tag;
80    /// assert_eq!(Tag::XResolution.number(), 0x11a);
81    /// assert_eq!(Tag::DateTimeOriginal.number(), 0x9003);
82    /// ```
83    #[inline]
84    pub fn number(self) -> u16 {
85        self.1
86    }
87
88    /// Returns the description of the tag.
89    #[inline]
90    pub fn description(&self) -> Option<&str> {
91        get_tag_info(*self).map(|ti| ti.desc)
92    }
93
94    /// Returns the default value of the tag.  `None` is returned if
95    /// it is not defined in the standard or it depends on another tag.
96    #[inline]
97    pub fn default_value(&self) -> Option<Value> {
98        get_tag_info(*self).and_then(|ti| (&ti.default).into())
99    }
100
101    pub(crate) fn unit(self) -> Option<&'static [UnitPiece]> {
102        get_tag_info(self).and_then(|ti| ti.unit)
103    }
104}
105
106impl fmt::Display for Tag {
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        match get_tag_info(*self) {
109            Some(ti) => f.pad(ti.name),
110            None => f.pad(&format!("{:?}", self)),
111        }
112    }
113}
114
115/// An enum that indicates how a tag number is interpreted.
116#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117#[non_exhaustive]
118pub enum Context {
119    /// TIFF attributes defined in the TIFF Rev. 6.0 specification.
120    Tiff,	// 0th/1st IFD (toplevel)
121    /// Exif attributes.
122    Exif,	// -- Exif IFD
123    /// GPS attributes.
124    Gps,	// -- GPS IFD
125    /// Interoperability attributes.
126    Interop,	// -- Exif IFD -- Interoperability IFD
127}
128
129#[derive(Debug)]
130pub enum UnitPiece {
131    Value,
132    Str(&'static str),
133    Tag(Tag),
134}
135
136macro_rules! unit {
137    () => ( None );
138    ( $str:literal ) => ( unit![V, concat!(" ", $str)] );
139    ( Tag::$tag:ident ) => ( unit![V, " ", Tag::$tag] );
140    ( $($tokens:tt)* ) => ( Some(unit_expand!( ; $($tokens)* , )) );
141}
142
143macro_rules! unit_expand {
144    ( $($built:expr),* ; ) => ( &[$($built),*] );
145    ( $($built:expr),* ; , ) => ( &[$($built),*] );
146    ( $($built:expr),* ; V, $($rest:tt)* ) => (
147        unit_expand!($($built,)* UnitPiece::Value ; $($rest)*) );
148    ( $($built:expr),* ; $str:literal, $($rest:tt)* ) => (
149        unit_expand!($($built,)* UnitPiece::Str($str) ; $($rest)*) );
150    ( $($built:expr),* ; concat!($($strs:literal),*), $($rest:tt)* ) => (
151        unit_expand!($($built,)* UnitPiece::Str(concat!($($strs),*)) ; $($rest)*) );
152    ( $($built:expr),* ; Tag::$tag:ident, $($rest:tt)* ) => (
153        unit_expand!($($built,)* UnitPiece::Tag(Tag::$tag) ; $($rest)*) );
154}
155
156macro_rules! generate_well_known_tag_constants {
157    (
158        $( |$ctx:path| $(
159            // Copy the doc attribute to the actual definition.
160            $( #[$attr:meta] )*
161            ($name:ident, $num:expr, $defval:expr, $dispval:ident, $unit:expr,
162             $desc:expr)
163        ),+, )+
164    ) => (
165        // This is not relevant for associated constants, because
166        // they cannot be imported even with "uniform paths".
167        // /// It is not recommended to import the constants directly into
168        // /// your namespace; import the module and use with the module name
169        // /// like `tag::DateTime`.  The constant names follow the Exif
170        // /// specification but not the Rust naming conventions, and a user
171        // /// of the constants will get the non_upper_case_globals warning
172        // /// if a bare constant is used in a match arm.
173        // // This is discussed in
174        // // <https://github.com/rust-lang/rust/issues/25207>.
175        impl Tag {
176            $($(
177                $( #[$attr] )*
178                #[allow(non_upper_case_globals)]
179                pub const $name: Tag = Tag($ctx, $num);
180            )+)+
181        }
182
183        mod tag_info {
184            use std::fmt;
185            use crate::value::Value;
186            use crate::value::DefaultValue;
187            use super::{Tag, UnitPiece};
188
189            pub struct TagInfo {
190                pub name: &'static str,
191                pub desc: &'static str,
192                pub default: DefaultValue,
193                pub dispval: fn(&mut dyn fmt::Write, &Value) -> fmt::Result,
194                pub unit: Option<&'static [UnitPiece]>,
195            }
196
197            $($(
198                #[allow(non_upper_case_globals)]
199                pub static $name: TagInfo = TagInfo {
200                    name: stringify!($name),
201                    desc: $desc,
202                    default: $defval,
203                    dispval: super::$dispval,
204                    unit: $unit,
205                };
206            )+)+
207        }
208
209        fn get_tag_info(tag: Tag) -> Option<&'static tag_info::TagInfo> {
210            match tag {
211                $($(
212                    Tag::$name => Some(&tag_info::$name),
213                )+)+
214                _ => None,
215            }
216        }
217    )
218}
219
220// Tag constant names do not follow the Rust naming conventions but
221// the Exif field names: camel cases and all-capital acronyms.
222generate_well_known_tag_constants!(
223    // Exif-specific IFDs [EXIF23 4.6.3].
224    |Context::Tiff|
225
226    /// A pointer to the Exif IFD.  This is used for the internal structure
227    /// of Exif data and will not be returned to the user.
228    #[doc(hidden)]
229    (ExifIFDPointer, 0x8769, DefaultValue::None, d_default,
230     unit![],
231     "Exif IFD pointer"),
232    /// A pointer to the GPS IFD.  This is used for the internal structure
233    /// of Exif data and will not be returned to the user.
234    #[doc(hidden)]
235    (GPSInfoIFDPointer, 0x8825, DefaultValue::None, d_default,
236     unit![],
237     "GPS Info IFD pointer"),
238
239    |Context::Exif|
240
241    /// A pointer to the interoperability IFD.  This is used for the internal
242    /// structure of Exif data and will not be returned to the user.
243    #[doc(hidden)]
244    (InteropIFDPointer, 0xa005, DefaultValue::None, d_default,
245     unit![],
246     "Interoperability IFD pointer"),
247
248    // TIFF primary and thumbnail attributes [EXIF23 4.6.4 Table 4,
249    // 4.6.8 Table 17, and 4.6.8 Table 21].
250    |Context::Tiff|
251
252    (ImageWidth, 0x100, DefaultValue::None, d_default,
253     unit!["pixels"],
254     "Image width"),
255    (ImageLength, 0x101, DefaultValue::None, d_default,
256     unit!["pixels"],
257     "Image height"),
258    (BitsPerSample, 0x102, DefaultValue::Short(&[8, 8, 8]), d_default,
259     unit![],
260     "Number of bits per component"),
261    (Compression, 0x103, DefaultValue::None, d_compression,
262     unit![],
263     "Compression scheme"),
264    (PhotometricInterpretation, 0x106, DefaultValue::None, d_photointp,
265     unit![],
266     "Pixel composition"),
267    (ImageDescription, 0x10e, DefaultValue::None, d_default,
268     unit![],
269     "Image title"),
270    /// Manufacturer of the image input equipment.
271    (Make, 0x10f, DefaultValue::None, d_default,
272     unit![],
273     "Manufacturer of image input equipment"),
274    /// Model name or number of the image input equipment.
275    (Model, 0x110, DefaultValue::None, d_default,
276     unit![],
277     "Model of image input equipment"),
278    (StripOffsets, 0x111, DefaultValue::None, d_default,
279     unit![],
280     "Image data location"),
281    (Orientation, 0x112, DefaultValue::Short(&[1]), d_orientation,
282     unit![],
283     "Orientation of image"),
284    (SamplesPerPixel, 0x115, DefaultValue::Short(&[3]), d_default,
285     unit![],
286     "Number of components"),
287    (RowsPerStrip, 0x116, DefaultValue::None, d_default,
288     unit![],
289     "Number of rows per strip"),
290    (StripByteCounts, 0x117, DefaultValue::None, d_default,
291     unit![],
292     "Bytes per compressed strip"),
293    (XResolution, 0x11a, DefaultValue::Rational(&[(72, 1)]), d_decimal,
294     unit![V, " pixels per ", Tag::ResolutionUnit],
295     "Image resolution in width direction"),
296    (YResolution, 0x11b, DefaultValue::Rational(&[(72, 1)]), d_decimal,
297     unit![V, " pixels per ", Tag::ResolutionUnit],
298     "Image resolution in height direction"),
299    (PlanarConfiguration, 0x11c, DefaultValue::Short(&[1]), d_planarcfg,
300     unit![],
301     "Image data arrangement"),
302    /// Unit of XResolution and YResolution fields.
303    (ResolutionUnit, 0x128, DefaultValue::Short(&[2]), d_resunit,
304     unit![],
305     "Unit of X and Y resolution"),
306    (TransferFunction, 0x12d, DefaultValue::None, d_default,
307     unit![],
308     "Transfer function"),
309    /// Name and version of the software used to create the image.
310    (Software, 0x131, DefaultValue::None, d_default,
311     unit![],
312     "Software used"),
313    /// Date and time when the image file was created or last edited.
314    /// For the time when the picture was taken, see DateTimeOriginal field.
315    (DateTime, 0x132, DefaultValue::None, d_datetime,
316     unit![],
317     "File change date and time"),
318    (Artist, 0x13b, DefaultValue::None, d_default,
319     unit![],
320     "Person who created the image"),
321    (WhitePoint, 0x13e, DefaultValue::None, d_decimal,
322     unit![],
323     "White point chromaticity"),
324    (PrimaryChromaticities, 0x13f, DefaultValue::None, d_decimal,
325     unit![],
326     "Chromaticities of primaries"),
327    // Not referenced in Exif.
328    (TileOffsets, 0x144, DefaultValue::None, d_default,
329     unit![],
330     "Tiled image data location"),
331    // Not referenced in Exif.
332    (TileByteCounts, 0x145, DefaultValue::None, d_default,
333     unit![],
334     "Bytes per compressed tile"),
335    (JPEGInterchangeFormat, 0x201, DefaultValue::None, d_default,
336     unit![],
337     "Offset to JPEG SOI"),
338    (JPEGInterchangeFormatLength, 0x202, DefaultValue::None, d_default,
339     unit![],
340     "Bytes of JPEG data"),
341    (YCbCrCoefficients, 0x211, DefaultValue::Unspecified, d_decimal,
342     unit![],
343     "Color space transformation matrix coefficients"),
344    (YCbCrSubSampling, 0x212, DefaultValue::None, d_ycbcrsubsamp,
345     unit![],
346     "Subsampling ratio of Y to C"),
347    (YCbCrPositioning, 0x213, DefaultValue::Short(&[1]), d_ycbcrpos,
348     unit![],
349     "Y and C positioning"),
350    (ReferenceBlackWhite, 0x214, DefaultValue::ContextDependent, d_decimal,
351     unit![],
352     "Pair of black and white reference values"),
353    (Copyright, 0x8298, DefaultValue::None, d_default,
354     unit![],
355     "Copyright holder"),
356
357    // Exif IFD attributes [EXIF23 4.6.5 Table 7 and 4.6.8 Table 18].
358    |Context::Exif|
359
360    (ExposureTime, 0x829a, DefaultValue::None, d_exptime,
361     unit!["s"],
362     "Exposure time"),
363    (FNumber, 0x829d, DefaultValue::None, d_decimal,
364     // F-number is dimensionless, but usually prefixed with "F" in Japan,
365     // "f/" in the U.S., and so on.
366     unit!["f/", V],
367     "F number"),
368    (ExposureProgram, 0x8822, DefaultValue::None, d_expprog,
369     unit![],
370     "Exposure program"),
371    (SpectralSensitivity, 0x8824, DefaultValue::None, d_default,
372     unit![],
373     "Spectral sensitivity"),
374    /// Sensitivity of the device.
375    ///
376    /// The value may be represented by standard output sensitivity (SOS),
377    /// recommended exposure index (REI), or ISO speed.
378    /// What is stored is designated by SensitivityType field.
379    ///
380    /// This field is 16-bit and may be saturated.  For 32-bit values,
381    /// see StandardOutputSensitivity, RecommendedExposureIndex,
382    /// ISOSpeed, ISOSpeedLatitudeyyy, and ISOSpeedLatitudezzz fields.
383    (PhotographicSensitivity, 0x8827, DefaultValue::None, d_default,
384     unit![],
385     "Photographic sensitivity"),
386    (OECF, 0x8828, DefaultValue::None, d_default,
387     unit![],
388     "Optoelectric conversion factor"),
389    (SensitivityType, 0x8830, DefaultValue::None, d_sensitivitytype,
390     unit![],
391     "Sensitivity type"),
392    (StandardOutputSensitivity, 0x8831, DefaultValue::None, d_default,
393     unit![],
394     "Standard output sensitivity"),
395    (RecommendedExposureIndex, 0x8832, DefaultValue::None, d_default,
396     unit![],
397     "Recommended exposure index"),
398    (ISOSpeed, 0x8833, DefaultValue::None, d_default,
399     unit![],
400     "ISO speed"),
401    (ISOSpeedLatitudeyyy, 0x8834, DefaultValue::None, d_default,
402     unit![],
403     "ISO speed latitude yyy"),
404    (ISOSpeedLatitudezzz, 0x8835, DefaultValue::None, d_default,
405     unit![],
406     "ISO speed latitude zzz"),
407    // The absence of this field means non-conformance to Exif, so the default
408    // value specified in the standard (e.g., "0231") should not apply.
409    (ExifVersion, 0x9000, DefaultValue::None, d_exifver,
410     unit![],
411     "Exif version"),
412    /// Date and time when the original image was generated (e.g.,
413    /// the picture was taken by a camera).
414    (DateTimeOriginal, 0x9003, DefaultValue::None, d_datetime,
415     unit![],
416     "Date and time of original data generation"),
417    /// Date and time when the image was stored as digital data.
418    /// If a picture is taken by a film camera and then digitized later,
419    /// this value will be different from DateTimeOriginal field.
420    (DateTimeDigitized, 0x9004, DefaultValue::None, d_datetime,
421     unit![],
422     "Date and time of digital data generation"),
423    /// Timezone offset for DateTime field.
424    (OffsetTime, 0x9010, DefaultValue::None, d_default,
425     unit![],
426     "Offset data of DateTime"),
427    /// Timezone offset for DateTimeOriginal field.
428    (OffsetTimeOriginal, 0x9011, DefaultValue::None, d_default,
429     unit![],
430     "Offset data of DateTimeOriginal"),
431    /// Timezone offset for DateTimeDigitized field.
432    (OffsetTimeDigitized, 0x9012, DefaultValue::None, d_default,
433     unit![],
434     "Offset data of DateTimeDigitized"),
435    (ComponentsConfiguration, 0x9101, DefaultValue::ContextDependent, d_cpntcfg,
436     unit![],
437     "Meaning of each component"),
438    (CompressedBitsPerPixel, 0x9102, DefaultValue::None, d_decimal,
439     unit![],
440     "Image compression mode"),
441    (ShutterSpeedValue, 0x9201, DefaultValue::None, d_decimal,
442     unit!["EV"],
443     "Shutter speed"),
444    (ApertureValue, 0x9202, DefaultValue::None, d_decimal,
445     unit!["EV"],
446     "Aperture"),
447    (BrightnessValue, 0x9203, DefaultValue::None, d_optdecimal,
448     unit!["EV"],
449     "Brightness"),
450    (ExposureBiasValue, 0x9204, DefaultValue::None, d_decimal,
451     unit!["EV"],
452     "Exposure bias"),
453    (MaxApertureValue, 0x9205, DefaultValue::None, d_decimal,
454     unit!["EV"],
455     "Maximum lens aperture"),
456    (SubjectDistance, 0x9206, DefaultValue::None, d_subjdist,
457     unit!["m"],
458     "Subject distance"),
459    (MeteringMode, 0x9207, DefaultValue::Short(&[0]), d_metering,
460     unit![],
461     "Metering mode"),
462    (LightSource, 0x9208, DefaultValue::Short(&[0]), d_lightsrc,
463     unit![],
464     "Light source"),
465    (Flash, 0x9209, DefaultValue::Unspecified, d_flash,
466     unit![],
467     "Flash"),
468    (FocalLength, 0x920a, DefaultValue::None, d_decimal,
469     unit!["mm"],
470     "Lens focal length"),
471    (SubjectArea, 0x9214, DefaultValue::None, d_subjarea,
472     unit![],
473     "Subject area"),
474    (MakerNote, 0x927c, DefaultValue::None, d_default,
475     unit![],
476     "Manufacturer notes"),
477    (UserComment, 0x9286, DefaultValue::None, d_default,
478     unit![],
479     "User comments"),
480    /// Subseconds for DateTime field.
481    (SubSecTime, 0x9290, DefaultValue::None, d_default,
482     unit![],
483     "DateTime subseconds"),
484    /// Subseconds for DateTimeOriginal field.
485    (SubSecTimeOriginal, 0x9291, DefaultValue::None, d_default,
486     unit![],
487     "DateTimeOriginal subseconds"),
488    /// Subseconds for DateTimeDigitized field.
489    (SubSecTimeDigitized, 0x9292, DefaultValue::None, d_default,
490     unit![],
491     "DateTimeDigitized subseconds"),
492    (Temperature, 0x9400, DefaultValue::None, d_optdecimal,
493     unit!["degC"],
494     "Temperature"),
495    (Humidity, 0x9401, DefaultValue::None, d_optdecimal,
496     unit!["%"],
497     "Humidity"),
498    (Pressure, 0x9402, DefaultValue::None, d_optdecimal,
499     unit!["hPa"],
500     "Pressure"),
501    (WaterDepth, 0x9403, DefaultValue::None, d_optdecimal,
502     unit!["m"],
503     "Water depth"),
504    (Acceleration, 0x9404, DefaultValue::None, d_optdecimal,
505     unit!["mGal"],
506     "Acceleration"),
507    (CameraElevationAngle, 0x9405, DefaultValue::None, d_optdecimal,
508     unit!["deg"],
509     "Camera elevation angle"),
510    (FlashpixVersion, 0xa000, DefaultValue::Undefined(b"0100"), d_exifver,
511     unit![],
512     "Supported Flashpix version"),
513    (ColorSpace, 0xa001, DefaultValue::Unspecified, d_cspace,
514     unit![],
515     "Color space information"),
516    (PixelXDimension, 0xa002, DefaultValue::None, d_default,
517     unit!["pixels"],
518     "Valid image width"),
519    (PixelYDimension, 0xa003, DefaultValue::Unspecified, d_default,
520     unit!["pixels"],
521     "Valid image height"),
522    (RelatedSoundFile, 0xa004, DefaultValue::None, d_default,
523     unit![],
524     "Related audio file"),
525    (FlashEnergy, 0xa20b, DefaultValue::None, d_decimal,
526     unit!["BCPS"],
527     "Flash energy"),
528    (SpatialFrequencyResponse, 0xa20c, DefaultValue::None, d_default,
529     unit![],
530     "Spatial frequency response"),
531    (FocalPlaneXResolution, 0xa20e, DefaultValue::None, d_decimal,
532     unit![V, " pixels per ", Tag::FocalPlaneResolutionUnit],
533     "Focal plane X resolution"),
534    (FocalPlaneYResolution, 0xa20f, DefaultValue::None, d_decimal,
535     unit![V, " pixels per ", Tag::FocalPlaneResolutionUnit],
536     "Focal plane Y resolution"),
537    /// Unit of FocalPlaneXResolution and FocalPlaneYResolution fields.
538    (FocalPlaneResolutionUnit, 0xa210, DefaultValue::Short(&[2]), d_resunit,
539     unit![],
540     "Focal plane resolution unit"),
541    (SubjectLocation, 0xa214, DefaultValue::None, d_subjarea,
542     unit![],
543     "Subject location"),
544    (ExposureIndex, 0xa215, DefaultValue::None, d_decimal,
545     unit![],
546     "Exposure index"),
547    (SensingMethod, 0xa217, DefaultValue::None, d_sensingmethod,
548     unit![],
549     "Sensing method"),
550    (FileSource, 0xa300, DefaultValue::Undefined(&[3]), d_filesrc,
551     unit![],
552     "File source"),
553    (SceneType, 0xa301, DefaultValue::Undefined(&[1]), d_scenetype,
554     unit![],
555     "Scene type"),
556    (CFAPattern, 0xa302, DefaultValue::None, d_default,
557     unit![],
558     "CFA pattern"),
559    (CustomRendered, 0xa401, DefaultValue::Short(&[0]), d_customrendered,
560     unit![],
561     "Custom image processing"),
562    (ExposureMode, 0xa402, DefaultValue::None, d_expmode,
563     unit![],
564     "Exposure mode"),
565    (WhiteBalance, 0xa403, DefaultValue::None, d_whitebalance,
566     unit![],
567     "White balance"),
568    (DigitalZoomRatio, 0xa404, DefaultValue::None, d_dzoomratio,
569     unit![],
570     "Digital zoom ratio"),
571    (FocalLengthIn35mmFilm, 0xa405, DefaultValue::None, d_focallen35,
572     unit!["mm"],
573     "Focal length in 35 mm film"),
574    (SceneCaptureType, 0xa406, DefaultValue::Short(&[0]), d_scenecaptype,
575     unit![],
576     "Scene capture type"),
577    (GainControl, 0xa407, DefaultValue::None, d_gainctrl,
578     unit![],
579     "Gain control"),
580    (Contrast, 0xa408, DefaultValue::Short(&[0]), d_contrast,
581     unit![],
582     "Contrast"),
583    (Saturation, 0xa409, DefaultValue::Short(&[0]), d_saturation,
584     unit![],
585     "Saturation"),
586    (Sharpness, 0xa40a, DefaultValue::Short(&[0]), d_sharpness,
587     unit![],
588     "Sharpness"),
589    (DeviceSettingDescription, 0xa40b, DefaultValue::None, d_default,
590     unit![],
591     "Device settings description"),
592    (SubjectDistanceRange, 0xa40c, DefaultValue::None, d_subjdistrange,
593     unit![],
594     "Subject distance range"),
595    (ImageUniqueID, 0xa420, DefaultValue::None, d_default,
596     unit![],
597     "Unique image ID"),
598    (CameraOwnerName, 0xa430, DefaultValue::None, d_default,
599     unit![],
600     "Camera owner name"),
601    (BodySerialNumber, 0xa431, DefaultValue::None, d_default,
602     unit![],
603     "Body serial number"),
604    (LensSpecification, 0xa432, DefaultValue::None, d_lensspec,
605     unit![],
606     "Lens specification"),
607    (LensMake, 0xa433, DefaultValue::None, d_default,
608     unit![],
609     "Lens make"),
610    (LensModel, 0xa434, DefaultValue::None, d_default,
611     unit![],
612     "Lens model"),
613    (LensSerialNumber, 0xa435, DefaultValue::None, d_default,
614     unit![],
615     "Lens serial number"),
616    (CompositeImage, 0xa460, DefaultValue::Short(&[0]), d_cpstimg,
617     unit![],
618     "Composite image"),
619    (SourceImageNumberOfCompositeImage, 0xa461, DefaultValue::None, d_numcpstimg,
620     unit![],
621     "Source image number of composite image"),
622    (SourceExposureTimesOfCompositeImage, 0xa462, DefaultValue::None, d_default,
623     unit![],
624     "Source exposure times of composite image"),
625    (Gamma, 0xa500, DefaultValue::None, d_decimal,
626     unit![],
627     "Gamma"),
628
629    // GPS attributes [EXIF23 4.6.6 Table 15 and 4.6.8 Table 19].
630    |Context::Gps|
631
632    // Depends on the Exif version.
633    (GPSVersionID, 0x0, DefaultValue::ContextDependent, d_gpsver,
634     unit![],
635     "GPS tag version"),
636    (GPSLatitudeRef, 0x1, DefaultValue::None, d_gpslatlongref,
637     unit![],
638     "North or south latitude"),
639    (GPSLatitude, 0x2, DefaultValue::None, d_gpsdms,
640     unit![Tag::GPSLatitudeRef],
641     "Latitude"),
642    (GPSLongitudeRef, 0x3, DefaultValue::None, d_gpslatlongref,
643     unit![],
644     "East or West Longitude"),
645    (GPSLongitude, 0x4, DefaultValue::None, d_gpsdms,
646     unit![Tag::GPSLongitudeRef],
647     "Longitude"),
648    (GPSAltitudeRef, 0x5, DefaultValue::Byte(&[0]), d_gpsaltref,
649     unit![],
650     "Altitude reference"),
651    (GPSAltitude, 0x6, DefaultValue::None, d_decimal,
652     unit![V, " meters ", Tag::GPSAltitudeRef],
653     "Altitude"),
654    (GPSTimeStamp, 0x7, DefaultValue::None, d_gpstimestamp,
655     unit![],
656     "GPS time (atomic clock)"),
657    (GPSSatellites, 0x8, DefaultValue::None, d_default,
658     unit![],
659     "GPS satellites used for measurement"),
660    (GPSStatus, 0x9, DefaultValue::None, d_gpsstatus,
661     unit![],
662     "GPS receiver status"),
663    (GPSMeasureMode, 0xa, DefaultValue::None, d_gpsmeasuremode,
664     unit![],
665     "GPS measurement mode"),
666    (GPSDOP, 0xb, DefaultValue::None, d_decimal,
667     unit![],
668     "Measurement precision"),
669    (GPSSpeedRef, 0xc, DefaultValue::Ascii(&[b"K"]), d_gpsspeedref,
670     unit![],
671     "Speed unit"),
672    (GPSSpeed, 0xd, DefaultValue::None, d_decimal,
673     unit![Tag::GPSSpeedRef],
674     "Speed of GPS receiver"),
675    (GPSTrackRef, 0xe, DefaultValue::Ascii(&[b"T"]), d_gpsdirref,
676     unit![],
677     "Reference for direction of movement"),
678    (GPSTrack, 0xf, DefaultValue::None, d_decimal,
679     unit![V, " degrees in ", Tag::GPSTrackRef],
680     "Direction of movement"),
681    (GPSImgDirectionRef, 0x10, DefaultValue::Ascii(&[b"T"]), d_gpsdirref,
682     unit![],
683     "Reference for direction of image"),
684    (GPSImgDirection, 0x11, DefaultValue::None, d_decimal,
685     unit![V, " degrees in ", Tag::GPSImgDirectionRef],
686     "Direction of image"),
687    (GPSMapDatum, 0x12, DefaultValue::None, d_default,
688     unit![],
689     "Geodetic survey data used"),
690    (GPSDestLatitudeRef, 0x13, DefaultValue::None, d_gpslatlongref,
691     unit![],
692     "Reference for latitude of destination"),
693    (GPSDestLatitude, 0x14, DefaultValue::None, d_gpsdms,
694     unit![Tag::GPSDestLatitudeRef],
695     "Latitude of destination"),
696    (GPSDestLongitudeRef, 0x15, DefaultValue::None, d_gpslatlongref,
697     unit![],
698     "Reference for longitude of destination"),
699    (GPSDestLongitude, 0x16, DefaultValue::None, d_gpsdms,
700     unit![Tag::GPSDestLongitudeRef],
701     "Longitude of destination"),
702    (GPSDestBearingRef, 0x17, DefaultValue::Ascii(&[b"T"]), d_gpsdirref,
703     unit![],
704     "Reference for bearing of destination"),
705    (GPSDestBearing, 0x18, DefaultValue::None, d_decimal,
706     unit![V, " degrees in ", Tag::GPSDestBearingRef],
707     "Bearing of destination"),
708    (GPSDestDistanceRef, 0x19, DefaultValue::Ascii(&[b"K"]), d_gpsdistref,
709     unit![],
710     "Reference for distance to destination"),
711    (GPSDestDistance, 0x1a, DefaultValue::None, d_decimal,
712     unit![Tag::GPSDestDistanceRef],
713     "Distance to destination"),
714    (GPSProcessingMethod, 0x1b, DefaultValue::None, d_ascii_in_undef,
715     unit![],
716     "Name of GPS processing method"),
717    (GPSAreaInformation, 0x1c, DefaultValue::None, d_default,
718     unit![],
719     "Name of GPS area"),
720    (GPSDateStamp, 0x1d, DefaultValue::None, d_gpsdatestamp,
721     unit![],
722     "GPS date"),
723    (GPSDifferential, 0x1e, DefaultValue::None, d_gpsdifferential,
724     unit![],
725     "GPS differential correction"),
726    (GPSHPositioningError, 0x1f, DefaultValue::None, d_decimal,
727     unit!["m"],
728     "Horizontal positioning error"),
729
730    // Interoperability attributes [EXIF23 4.6.7 Table 16 and 4.6.8 Table 20]
731    // [DCF20 4.4.5.3, 4.5.4.3, and 4.6.4.3].
732    |Context::Interop|
733
734    (InteroperabilityIndex, 0x1, DefaultValue::None, d_default,
735     unit![],
736     "Interoperability identification"),
737    (InteroperabilityVersion, 0x2, DefaultValue::None, d_interopver,
738     unit![],
739     "Interoperability version"),
740    (RelatedImageFileFormat, 0x1000, DefaultValue::None, d_default,
741     unit![],
742     "Related image file format"),
743    (RelatedImageWidth, 0x1001, DefaultValue::None, d_default,
744     unit!["pixels"],
745     "Related image width"),
746    (RelatedImageLength, 0x1002, DefaultValue::None, d_default,
747     unit!["pixels"],
748     "Related image height"),
749);
750
751// For Value::display_as().
752pub fn display_value_as<'a>(value: &'a Value, tag: Tag) -> value::Display<'a> {
753    match get_tag_info(tag) {
754        Some(ti) => value::Display { fmt: ti.dispval, value: value },
755        None => value::Display { fmt: d_default, value: value },
756    }
757}
758
759// Compression (TIFF 0x103)
760fn d_compression(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
761    let s = match value.get_uint(0) {
762        Some(1) => "uncompressed",
763        Some(2) => "Modified Huffman",
764        Some(6) => "JPEG",
765        Some(32773) => "PackBits",
766        _ => return d_reserved(w, value, "compression"),
767    };
768    w.write_str(s)
769}
770
771// PhotometricInterpretation (TIFF 0x106)
772fn d_photointp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
773    let s = match value.get_uint(0) {
774        Some(0) => "white is zero",
775        Some(1) => "black is zero",
776        Some(2) => "RGB",
777        Some(3) => "palette color",
778        Some(4) => "transparency mask",
779        Some(6) => "YCbCr",
780        _ => return d_reserved(w, value, "photometric interpretation"),
781    };
782    w.write_str(s)
783}
784
785// Orientation (TIFF 0x112)
786fn d_orientation(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
787    let s = match value.get_uint(0) {
788        Some(1) => "row 0 at top and column 0 at left",
789        Some(2) => "row 0 at top and column 0 at right",
790        Some(3) => "row 0 at bottom and column 0 at right",
791        Some(4) => "row 0 at bottom and column 0 at left",
792        Some(5) => "row 0 at left and column 0 at top",
793        Some(6) => "row 0 at right and column 0 at top",
794        Some(7) => "row 0 at right and column 0 at bottom",
795        Some(8) => "row 0 at left and column 0 at bottom",
796        _ => return d_reserved(w, value, "orientation"),
797    };
798    w.write_str(s)
799}
800
801// PlanarConfiguration (TIFF 0x11c)
802fn d_planarcfg(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
803    let s = match value.get_uint(0) {
804        Some(1) => "chunky",
805        Some(2) => "planar",
806        _ => return d_reserved(w, value, "planar configuration"),
807    };
808    w.write_str(s)
809}
810
811// ResolutionUnit (TIFF 0x128)
812// FocalPlaneResolutionUnit (Exif 0xa210)
813fn d_resunit(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
814    let s = match value.get_uint(0) {
815        Some(1) => "no absolute unit",
816        Some(2) => "inch",
817        Some(3) => "cm",
818        _ => return d_reserved(w, value, "resolution unit"),
819    };
820    w.write_str(s)
821}
822
823// DateTime (TIFF 0x132), DateTimeOriginal (Exif 0x9003), and
824// DateTimeDigitized (Exif 0x9004)
825fn d_datetime(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
826    if let Some(dt) = value.ascii().and_then(|x| x.first()) {
827        match crate::tiff::DateTime::from_ascii(dt) {
828            Ok(dt) => return write!(w, "{}", dt),
829            Err(Error::BlankValue(_)) => return w.write_str("unknown"),
830            _ => {},
831        }
832    }
833    d_default(w, value)
834}
835
836// YCbCrSubSampling (TIFF 0x212)
837fn d_ycbcrsubsamp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
838    // 0 is used to go to d_default in the match below.
839    let horiz = value.get_uint(0).unwrap_or(0);
840    let vert = value.get_uint(1).unwrap_or(0);
841    let s = match (horiz, vert) {
842        (1, 1) => "full horizontally, full vertically (4:4:4)",
843        (2, 1) => "half horizontally, full vertically (4:2:2)",
844        (2, 2) => "half horizontally, half vertically (4:2:0)",
845        (4, 1) => "quarter horizontally, full vertically (4:1:1)",
846        (4, 2) => "quarter horizontally, half vertically",
847        (4, 4) => "quarter horizontally, quarter vertically",
848        _ => return d_default(w, value),
849    };
850    w.write_str(s)
851}
852
853// YCbCrPositioning (TIFF 0x213)
854fn d_ycbcrpos(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
855    let s = match value.get_uint(0) {
856        Some(1) => "centered",
857        Some(2) => "co-sited",
858        _ => return d_reserved(w, value, "YCbCr positioning"),
859    };
860    w.write_str(s)
861}
862
863// ExposureTime (Exif 0x829a)
864fn d_exptime(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
865    if let Some(et) = value.rational().and_then(|x| x.first()) {
866        if et.num >= et.denom {
867            return write!(w, "{}", et.to_f64());
868        } else if et.num != 0 {
869            return write!(w, "1/{}", et.denom as f64 / et.num as f64);
870        }
871    }
872    d_default(w, value)
873}
874
875// ExposureProgram (Exif 0x8822)
876fn d_expprog(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
877    let s = match value.get_uint(0) {
878        Some(0) => "not defined",
879        Some(1) => "manual",
880        Some(2) => "normal program",
881        Some(3) => "aperture priority",
882        Some(4) => "shutter priority",
883        Some(5) => "creative program",
884        Some(6) => "action program",
885        Some(7) => "portrait mode",
886        Some(8) => "landscape mode",
887        _ => return d_reserved(w, value, "exposure program"),
888    };
889    w.write_str(s)
890}
891
892// SensitivityType (Exif 0x8830)
893fn d_sensitivitytype(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
894    let s = match value.get_uint(0) {
895        Some(0) => "unknown",
896        Some(1) => "SOS",
897        Some(2) => "REI",
898        Some(3) => "ISO speed",
899        Some(4) => "SOS/REI",
900        Some(5) => "SOS/ISO speed",
901        Some(6) => "REI/ISO speed",
902        Some(7) => "SOS/REI/ISO speed",
903        _ => return d_reserved(w, value, "sensitivity type"),
904    };
905    w.write_str(s)
906}
907
908// ExifVersion (Exif 0x9000), FlashpixVersion (Exif 0xa000)
909fn d_exifver(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
910    if let Some(s) = value.undefined().filter(|s| s.len() == 4) {
911        if let Ok(major) = atou16(&s[0..2]) {
912            if let Ok(minor) = atou16(&s[2..4]) {
913                if minor % 10 == 0 {
914                    return write!(w, "{}.{}", major, minor / 10);
915                } else {
916                    return write!(w, "{}.{:02}", major, minor);
917                }
918            }
919        }
920    }
921    d_default(w, value)
922}
923
924// ComponentsConfiguration (Exif 0x9101)
925fn d_cpntcfg(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
926    match value.undefined() {
927        Some(s) => s.iter().try_for_each(|x| match x {
928            0 => w.write_char('_'),
929            1 => w.write_char('Y'),
930            2 => w.write_str("Cb"),
931            3 => w.write_str("Cr"),
932            4 => w.write_char('R'),
933            5 => w.write_char('G'),
934            6 => w.write_char('B'),
935            _ => w.write_char('?'),
936        }),
937        None => d_default(w, value),
938    }
939}
940
941// SubjectDistance (Exif 0x9206)
942fn d_subjdist(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
943    if let Some(dist) = value.rational().and_then(|x| x.first()) {
944        if dist.num == 0 {
945            return w.write_str("unknown");
946        } else if dist.num == 0xffffffff {
947            return w.write_str("infinity");
948        }
949    }
950    d_decimal(w, value)
951}
952
953// MeteringMode (Exif 0x9207)
954fn d_metering(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
955    let s = match value.get_uint(0) {
956        Some(0) => "unknown",
957        Some(1) => "average",
958        Some(2) => "center-weighted average",
959        Some(3) => "spot",
960        Some(4) => "multi-spot",
961        Some(5) => "pattern",
962        Some(6) => "partial",
963        Some(255) => "other",
964        _ => return d_reserved(w, value, "metering mode"),
965    };
966    w.write_str(s)
967}
968
969// LightSource (Exif 0x9208)
970fn d_lightsrc(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
971    let s = match value.get_uint(0) {
972        Some(0) => "unknown",
973        Some(1) => "daylight",
974        Some(2) => "fluorescent",
975        Some(3) => "tungsten",
976        Some(4) => "flash",
977        Some(9) => "fine weather",
978        Some(10) => "cloudy weather",
979        Some(11) => "shade",
980        Some(12) => "daylight fluorescent (D 5700-7100K)",
981        Some(13) => "day white fluorescent (N 4600-5500K)",
982        Some(14) => "cool white fluorescent (W 3800-4500K)",
983        Some(15) => "white fluorescent (WW 3250-3800K)",
984        Some(16) => "warm white fluorescent (L 2600-3250K)",
985        Some(17) => "standard light A",
986        Some(18) => "standard light B",
987        Some(19) => "standard light C",
988        Some(20) => "D55",
989        Some(21) => "D65",
990        Some(22) => "D75",
991        Some(23) => "D50",
992        Some(24) => "ISO studio tungsten",
993        Some(255) => "other",
994        _ => return d_reserved(w, value, "light source"),
995    };
996    w.write_str(s)
997}
998
999// Flash (Exif 0x9209)
1000fn d_flash(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1001    const FIRED: &[&str] = &["not fired", "fired"];
1002    const RETURN: &[&str] = &[
1003        ", no return light detection function",
1004        ", return light status 1 (reserved)",
1005        ", return light not detected",
1006        ", return light detected",
1007    ];
1008    const AUTO: &[&str] = &[
1009        ", auto mode 0 (unknown)", ", forced", ", suppressed", ", auto"];
1010    const FUNCTION: &[&str] = &["", ", no function present"];
1011    const RED_EYE: &[&str] = &["", ", red-eye reduction"];
1012
1013    if let Some(v) = value.get_uint(0) {
1014        write!(w, "{}{}{}{}{}{}",
1015               FIRED[v as usize & 1],
1016               RETURN[v as usize >> 1 & 3],
1017               AUTO[v as usize >> 3 & 3],
1018               FUNCTION[v as usize >> 5 & 1],
1019               RED_EYE[v as usize >> 6 & 1],
1020               if v >> 7 != 0 { ", unknown MSB bits" } else { "" })
1021    } else {
1022        d_default(w, value)
1023    }
1024}
1025
1026// SubjectArea (Exif 0x9214), SubjectLocation (Exif 0xa214)
1027// Only (x, y) case is valid for SubjectLocation.
1028fn d_subjarea(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1029    if let Some(x) = value.get_uint(0) {
1030        if let Some(y) = value.get_uint(1) {
1031            if let Some(d) = value.get_uint(2) {
1032                if let Some(h) = value.get_uint(3) {
1033                    return write!(w, "rectangle (x={}, y={}, w={}, h={})",
1034                                  x, y, d, h);
1035                }
1036                return write!(w, "circle (x={}, y={}, d={})", x, y, d);
1037            }
1038            return write!(w, "point (x={}, y={})", x, y);
1039        }
1040    }
1041    d_default(w, value)
1042}
1043
1044// Rational/SRational with 0xffffffff being unknown.
1045// BrightnessValue (Exif 0x9203),
1046// Temperature (Exif 0x9400), Humidity (Exif 0x9401),
1047// Pressure (Exif 0x9402), WaterDepth (Exif 0x9403),
1048// Acceleration (Exif 0x9404), CameraElevationAngle (Exif 0x9405)
1049fn d_optdecimal(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1050    match *value {
1051        Value::Rational(ref v) if v.len() > 0 =>
1052            if v[0].denom != 0xffffffff {
1053                write!(w, "{}", v[0].to_f64())
1054            } else {
1055                w.write_str("unknown")
1056            },
1057        Value::SRational(ref v) if v.len() > 0 =>
1058            if v[0].denom != -1 {
1059                write!(w, "{}", v[0].to_f64())
1060            } else {
1061                w.write_str("unknown")
1062            },
1063        _ => d_decimal(w, value),
1064    }
1065}
1066
1067// ColorSpace (Exif 0xa001)
1068fn d_cspace(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1069    let s = match value.get_uint(0) {
1070        Some(1) => "sRGB",
1071        Some(0xffff) => "uncalibrated",
1072        _ => return d_reserved(w, value, "color space"),
1073    };
1074    w.write_str(s)
1075}
1076
1077// SensingMethod (Exif 0xa217)
1078fn d_sensingmethod(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1079    let s = match value.get_uint(0) {
1080        Some(1) => "not defined",
1081        Some(2) => "one-chip color area sensor",
1082        Some(3) => "two-chip color area sensor",
1083        Some(4) => "three-chip color area sensor",
1084        Some(5) => "color sequential area sensor",
1085        Some(7) => "trilinear sensor",
1086        Some(8) => "color sequential linear sensor",
1087        _ => return d_reserved(w, value, "sensing method"),
1088    };
1089    w.write_str(s)
1090}
1091
1092// FileSource (Exif 0xa300)
1093fn d_filesrc(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1094    let s = match value.undefined().and_then(|x| x.first().copied()) {
1095        Some(0) => "others",
1096        Some(1) => "transparency scanner",
1097        Some(2) => "reflective scanner",
1098        Some(3) => "digital still camera",
1099        _ => return d_reserved(w, value, "file source"),
1100    };
1101    w.write_str(s)
1102}
1103
1104// SceneType (Exif 0xa301)
1105fn d_scenetype(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1106    let s = match value.undefined().and_then(|x| x.first().copied()) {
1107        Some(1) => "directly photographed image",
1108        _ => return d_reserved(w, value, "scene type"),
1109    };
1110    w.write_str(s)
1111}
1112
1113// CustomRendered (Exif 0xa401)
1114fn d_customrendered(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1115    let s = match value.get_uint(0) {
1116        Some(0) => "normal process",
1117        Some(1) => "custom process",
1118        _ => return d_reserved(w, value, "custom rendered"),
1119    };
1120    w.write_str(s)
1121}
1122
1123// ExposureMode (Exif 0xa402)
1124fn d_expmode(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1125    let s = match value.get_uint(0) {
1126        Some(0) => "auto exposure",
1127        Some(1) => "manual exposure",
1128        Some(2) => "auto bracket",
1129        _ => return d_reserved(w, value, "exposure mode"),
1130    };
1131    w.write_str(s)
1132}
1133
1134// WhiteBalance (Exif 0xa403)
1135fn d_whitebalance(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1136    let s = match value.get_uint(0) {
1137        Some(0) => "auto white balance",
1138        Some(1) => "manual white balance",
1139        _ => return d_reserved(w, value, "white balance mode"),
1140    };
1141    w.write_str(s)
1142}
1143
1144// DigitalZoomRatio (Exif 0xa404)
1145fn d_dzoomratio(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1146    if value.rational().and_then(|x| x.first()).map(|x| x.num) == Some(0) {
1147        return w.write_str("unused");
1148    }
1149    d_decimal(w, value)
1150}
1151
1152// FocalLengthIn35mmFilm (Exif 0xa405)
1153fn d_focallen35(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1154    match value.get_uint(0) {
1155        Some(0) => w.write_str("unknown"),
1156        _ => d_default(w, value),
1157    }
1158}
1159
1160// SceneCaptureType (Exif 0xa406)
1161fn d_scenecaptype(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1162    let s = match value.get_uint(0) {
1163        Some(0) => "standard",
1164        Some(1) => "landscape",
1165        Some(2) => "portrait",
1166        Some(3) => "night scene",
1167        _ => return d_reserved(w, value, "scene capture type"),
1168    };
1169    w.write_str(s)
1170}
1171
1172// GainControl (Exif 0xa407)
1173fn d_gainctrl(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1174    let s = match value.get_uint(0) {
1175        Some(0) => "none",
1176        Some(1) => "low gain up",
1177        Some(2) => "high gain up",
1178        Some(3) => "low gain down",
1179        Some(4) => "high gain down",
1180        _ => return d_reserved(w, value, "gain control"),
1181    };
1182    w.write_str(s)
1183}
1184
1185// Contrast (Exif 0xa408)
1186fn d_contrast(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1187    let s = match value.get_uint(0) {
1188        Some(0) => "normal",
1189        Some(1) => "soft",
1190        Some(2) => "hard",
1191        _ => return d_reserved(w, value, "contrast processing"),
1192    };
1193    w.write_str(s)
1194}
1195
1196// Saturation (Exif 0xa409)
1197fn d_saturation(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1198    let s = match value.get_uint(0) {
1199        Some(0) => "normal",
1200        Some(1) => "low saturation",
1201        Some(2) => "high saturation",
1202        _ => return d_reserved(w, value, "saturation processing"),
1203    };
1204    w.write_str(s)
1205}
1206
1207// Sharpness (Exif 0xa40a)
1208fn d_sharpness(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1209    let s = match value.get_uint(0) {
1210        Some(0) => "normal",
1211        Some(1) => "soft",
1212        Some(2) => "hard",
1213        _ => return d_reserved(w, value, "sharpness processing"),
1214    };
1215    w.write_str(s)
1216}
1217
1218// SubjectDistanceRange (Exif 0xa40c)
1219fn d_subjdistrange(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1220    let s = match value.get_uint(0) {
1221        Some(0) => "unknown",
1222        Some(1) => "macro",
1223        Some(2) => "close view",
1224        Some(3) => "distant view",
1225        _ => return d_reserved(w, value, "subject distance range"),
1226    };
1227    w.write_str(s)
1228}
1229
1230// LensSpecification (Exif 0xa432)
1231fn d_lensspec(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1232    match value.rational().and_then(|x| x.get(..4)) {
1233        // There are several notations: "F1.4" in Japan, "f/1.4"
1234        // in the U.S., and so on.
1235        Some(s) => write!(w, "{}-{} mm, f/{}-{}",
1236                          s[0].to_f64(), s[1].to_f64(),
1237                          s[2].to_f64(), s[3].to_f64()),
1238        _ => d_default(w, value),
1239    }
1240}
1241
1242// CompositeImage (Exif 0xa460)
1243fn d_cpstimg(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1244    let s = match value.get_uint(0) {
1245        Some(0) => "unknown",
1246        Some(1) => "non-composite",
1247        Some(2) => "composite (general)",
1248        Some(3) => "composite (at the moment of shooting)",
1249        _ => return d_reserved(w, value, "composite image"),
1250    };
1251    w.write_str(s)
1252}
1253
1254// SourceImageNumberOfCompositeImage (Exif 0xa461)
1255fn d_numcpstimg(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1256    match (value.get_uint(0), value.get_uint(1)) {
1257        (Some(t), Some(u)) => write!(w, "total {}, used {}", t, u),
1258        _ => d_default(w, value),
1259    }
1260}
1261
1262// GPSVersionID (GPS 0x0)
1263fn d_gpsver(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1264    match value.byte().and_then(|x| x.get(..4)) {
1265        Some(s) => write!(w, "{}.{}.{}.{}", s[0], s[1], s[2], s[3]),
1266        _ => d_default(w, value),
1267    }
1268}
1269
1270// GPSLatitudeRef (GPS 0x1), GPSLongitudeRef (GPS 0x3)
1271// GPSDestLatitudeRef (GPS 0x13), GPSDestLongitudeRef (GPS 0x15)
1272fn d_gpslatlongref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1273    match value.ascii().and_then(|x| x.first()) {
1274        Some([c]) if c.is_ascii_uppercase() => w.write_char(*c as char),
1275        _ => d_default(w, value),
1276    }
1277}
1278
1279// GPSLatitude (GPS 0x2), GPSLongitude (GPS 0x4),
1280// GPSDestLatitude (GPS 0x14), GPSDestLongitude (GPS 0x16)
1281fn d_gpsdms(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1282    match value.rational().and_then(|x| x.get(..3)) {
1283        Some(s) => write!(w, "{} deg {} min {} sec",
1284                          s[0].to_f64(), s[1].to_f64(), s[2].to_f64()),
1285        _ => d_default(w, value),
1286    }
1287}
1288
1289// GPSAltitudeRef (GPS 0x5)
1290fn d_gpsaltref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1291    let s = match value.get_uint(0) {
1292        Some(0) => "above sea level",
1293        Some(1) => "below sea level",
1294        _ => return d_reserved(w, value, "GPS altitude ref"),
1295    };
1296    w.write_str(s)
1297}
1298
1299// GPSTimeStamp (GPS 0x7)
1300fn d_gpstimestamp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1301    match value.rational().and_then(|x| x.get(..3)) {
1302        Some(s) => {
1303            let (h, m, s) = (s[0].to_f64(), s[1].to_f64(), s[2].to_f64());
1304            write!(w, "{}{}:{}{}:{}{}",
1305                   if h < 10.0 { "0" } else { "" }, h,
1306                   if m < 10.0 { "0" } else { "" }, m,
1307                   if s < 10.0 { "0" } else { "" }, s)
1308        },
1309        _ => d_default(w, value),
1310    }
1311}
1312
1313// GPSStatus (GPS 0x9)
1314fn d_gpsstatus(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1315    let s = match value.ascii().and_then(|x| x.first()) {
1316        Some(b"A") => "measurement in progress",
1317        Some(b"V") => "measurement interrupted",
1318        _ => return d_reserved(w, value, "GPS status"),
1319    };
1320    w.write_str(s)
1321}
1322
1323// GPSMeasureMode (GPS 0xa)
1324fn d_gpsmeasuremode(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1325    let s = match value.ascii().and_then(|x| x.first()) {
1326        Some(b"2") => "2-dimensional measurement",
1327        Some(b"3") => "3-dimensional measurement",
1328        _ => return d_reserved(w, value, "GPS measurement mode"),
1329    };
1330    w.write_str(s)
1331}
1332
1333// GPSSpeedRef (GPS 0xc)
1334fn d_gpsspeedref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1335    let s = match value.ascii().and_then(|x| x.first()) {
1336        Some(b"K") => "km/h",
1337        Some(b"M") => "mph",
1338        Some(b"N") => "knots",
1339        _ => return d_reserved(w, value, "GPS speed ref"),
1340    };
1341    w.write_str(s)
1342}
1343
1344// GPSTrackRef (GPS 0xe), GPSImgDirectionRef (GPS 0x10),
1345// GPSDestBearingRef (GPS 0x17)
1346fn d_gpsdirref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1347    let s = match value.ascii().and_then(|x| x.first()) {
1348        Some(b"T") => "true direction",
1349        Some(b"M") => "magnetic direction",
1350        _ => return d_reserved(w, value, "GPS direction ref"),
1351    };
1352    w.write_str(s)
1353}
1354
1355// GPSDestDistanceRef (GPS 0x19)
1356fn d_gpsdistref(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1357    let s = match value.ascii().and_then(|x| x.first()) {
1358        Some(b"K") => "km",
1359        Some(b"M") => "miles",
1360        Some(b"N") => "nautical miles",
1361        _ => return d_reserved(w, value, "GPS distance ref"),
1362    };
1363    w.write_str(s)
1364}
1365
1366// GPSDateStamp (GPS 0x1d)
1367fn d_gpsdatestamp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1368    if let Some(data) = value.ascii().and_then(|x| x.first()) {
1369        if data.len() >= 10 && data[4] == b':' && data[7] == b':' {
1370            if let Ok(year) = atou16(&data[0..4]) {
1371                if let Ok(month) = atou16(&data[5..7]) {
1372                    if let Ok(day) = atou16(&data[8..10]) {
1373                        return write!(w, "{:04}-{:02}-{:02}",
1374                                      year, month, day);
1375                    }
1376                }
1377            }
1378        }
1379    }
1380    d_default(w, value)
1381}
1382
1383// GPSDifferential (GPS 0x1e)
1384fn d_gpsdifferential(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1385    let s = match value.get_uint(0) {
1386        Some(0) => "no differential correction",
1387        Some(1) => "differential correction applied",
1388        _ => return d_reserved(w, value, "GPS differential correction"),
1389    };
1390    w.write_str(s)
1391}
1392
1393// InteroperabilityVersion (Interoperability 0x2)
1394fn d_interopver(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1395    if let Some(s) = value.undefined().filter(|s| s.len() == 4) {
1396        if let Ok(major) = atou16(&s[0..2]) {
1397            if let Ok(minor) = atou16(&s[2..4]) {
1398                return write!(w, "{}.{:02}", major, minor);
1399            }
1400        }
1401    }
1402    d_default(w, value)
1403}
1404
1405fn d_ascii_in_undef(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1406    match *value {
1407        Value::Undefined(ref v, _) => d_sub_ascii(w, v),
1408        _ => d_default(w, value),
1409    }
1410}
1411
1412fn d_decimal(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1413    match *value {
1414        Value::Rational(ref v) =>
1415            d_sub_comma(w, v.iter().map(|x| x.to_f64())),
1416        Value::SRational(ref v) =>
1417            d_sub_comma(w, v.iter().map(|x| x.to_f64())),
1418        _ => d_default(w, value),
1419    }
1420}
1421
1422#[inline(never)]
1423fn d_reserved(w: &mut dyn fmt::Write, value: &Value, name: &str)
1424              -> fmt::Result {
1425    write!(w, "[reserved {} ", name)?;
1426    d_default(w, value)?;
1427    w.write_char(']')
1428}
1429
1430fn d_default(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1431    match *value {
1432        Value::Byte(ref v) => d_sub_comma(w, v),
1433        Value::Ascii(ref v) =>
1434            d_sub_comma(w, v.iter().map(|x| AsciiDisplay(x))),
1435        Value::Short(ref v) => d_sub_comma(w, v),
1436        Value::Long(ref v) => d_sub_comma(w, v),
1437        Value::Rational(ref v) => d_sub_comma(w, v),
1438        Value::SByte(ref v) => d_sub_comma(w, v),
1439        Value::Undefined(ref v, _) => d_sub_hex(w, v),
1440        Value::SShort(ref v) => d_sub_comma(w, v),
1441        Value::SLong(ref v) => d_sub_comma(w, v),
1442        Value::SRational(ref v) => d_sub_comma(w, v),
1443        Value::Float(ref v) => d_sub_comma(w, v),
1444        Value::Double(ref v) => d_sub_comma(w, v),
1445        Value::Unknown(t, c, o) =>
1446            write!(w, "unknown value (type={}, count={}, offset={:#x})",
1447                   t, c, o),
1448    }
1449}
1450
1451fn d_sub_comma<I, T>(w: &mut dyn fmt::Write, itit: I) -> fmt::Result
1452where I: IntoIterator<Item = T>, T: fmt::Display {
1453    let mut first = true;
1454    for x in itit {
1455        match first {
1456            true => write!(w, "{}", x),
1457            false => write!(w, ", {}", x),
1458        }?;
1459        first = false;
1460    }
1461    Ok(())
1462}
1463
1464struct AsciiDisplay<'a>(&'a [u8]);
1465
1466impl<'a> fmt::Display for AsciiDisplay<'a> {
1467    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1468        d_sub_ascii(f, self.0)
1469    }
1470}
1471
1472fn d_sub_hex(w: &mut dyn fmt::Write, bytes: &[u8]) -> fmt::Result {
1473    w.write_str("0x")?;
1474    for x in bytes {
1475        write!(w, "{:02x}", x)?;
1476    }
1477    Ok(())
1478}
1479
1480fn d_sub_ascii(w: &mut dyn fmt::Write, bytes: &[u8]) -> fmt::Result {
1481    w.write_char('"')?;
1482    for &c in bytes {
1483        match c {
1484            b'\\' | b'"' => {
1485                w.write_char('\\')?;
1486                w.write_char(c as char)?;
1487            },
1488            0x20..=0x7e => w.write_char(c as char)?,
1489            _ => write!(w, "\\x{:02x}", c)?,
1490        }
1491    }
1492    w.write_char('"')
1493}
1494
1495#[cfg(test)]
1496mod tests {
1497    use value::Rational;
1498    use super::*;
1499
1500    // This test checks if Tag constants can be used in patterns.
1501    #[test]
1502    fn tag_constant_in_pattern() {
1503        // Destructuring, which will always work.
1504        match Tag(Context::Tiff, 0x132) {
1505            Tag(Context::Tiff, 0x132) => {},
1506            _ => panic!("failed to match Tag"),
1507        }
1508        // Matching against a constant.  Test if this compiles.
1509        match Tag(Context::Tiff, 0x132) {
1510            Tag::DateTime => {},
1511            _ => panic!("failed to match Tag"),
1512        }
1513    }
1514
1515    #[test]
1516    fn default_value() {
1517        assert_pat!(Tag::DateTime.default_value(), None);
1518        match Tag::BitsPerSample.default_value() {
1519            Some(Value::Short(v)) => assert_eq!(v, &[8, 8, 8]),
1520            _ => panic!(),
1521        }
1522        match Tag::XResolution.default_value() {
1523            Some(Value::Rational(v)) => {
1524                assert_eq!(v.len(), 1);
1525                assert_eq!(v[0].num, 72);
1526                assert_eq!(v[0].denom, 1);
1527            },
1528            _ => panic!(),
1529        }
1530        match Tag::FileSource.default_value() {
1531            Some(Value::Undefined(v, _)) => assert_eq!(v, &[3]),
1532            _ => panic!(),
1533        }
1534        match Tag::GPSAltitudeRef.default_value() {
1535            Some(Value::Byte(v)) => assert_eq!(v, &[0]),
1536            _ => panic!(),
1537        }
1538        match Tag::GPSSpeedRef.default_value() {
1539            Some(Value::Ascii(v)) => assert_eq!(v, &[b"K"]),
1540            _ => panic!(),
1541        }
1542    }
1543
1544    #[test]
1545    fn tag_fmt_display() {
1546        let tag1 = Tag(Context::Tiff, 0x132);
1547        assert_eq!(format!("{:15}", tag1), "DateTime       ");
1548        assert_eq!(format!("{:>15}", tag1), "       DateTime");
1549        assert_eq!(format!("{:5.6}", tag1), "DateTi");
1550        let tag2 = Tag(Context::Exif, 0);
1551        assert_eq!(format!("{:15}", tag2), "Tag(Exif, 0)   ");
1552        assert_eq!(format!("{:>15}", tag2), "   Tag(Exif, 0)");
1553        assert_eq!(format!("{:5.6}", tag2), "Tag(Ex");
1554    }
1555
1556    #[test]
1557    fn disp_val_sub() {
1558        let mut buf = String::new();
1559        d_sub_comma(&mut buf, &[0u16, 1, 2]).unwrap();
1560        assert_eq!(buf, "0, 1, 2");
1561
1562        let mut buf = String::new();
1563        d_sub_comma(&mut buf, &[Rational::from((3, 5))]).unwrap();
1564        assert_eq!(buf, "3/5");
1565
1566        let mut buf = String::new();
1567        let list = &[Rational::from((1, 2))];
1568        d_sub_comma(&mut buf, list.iter().map(|x| x.to_f64())).unwrap();
1569        assert_eq!(buf, "0.5");
1570
1571        let mut buf = String::new();
1572        d_sub_hex(&mut buf, b"abc\x00\xff").unwrap();
1573        assert_eq!(buf, "0x61626300ff");
1574
1575        let mut buf = String::new();
1576        d_sub_ascii(&mut buf, b"a \"\\b\"\n").unwrap();
1577        assert_eq!(buf, r#""a \"\\b\"\x0a""#);
1578    }
1579}