1use std::fmt;
28
29use crate::error::Error;
30use crate::value;
31use crate::value::Value;
32use crate::util::atou16;
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub struct Tag(pub Context, pub u16);
60
61impl Tag {
62 #[inline]
71 pub fn context(self) -> Context {
72 self.0
73 }
74
75 #[inline]
84 pub fn number(self) -> u16 {
85 self.1
86 }
87
88 #[inline]
90 pub fn description(&self) -> Option<&str> {
91 get_tag_info(*self).map(|ti| ti.desc)
92 }
93
94 #[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#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
117#[non_exhaustive]
118pub enum Context {
119 Tiff, Exif, Gps, Interop, }
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 $( #[$attr:meta] )*
161 ($name:ident, $num:expr, $defval:expr, $dispval:ident, $unit:expr,
162 $desc:expr)
163 ),+, )+
164 ) => (
165 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
220generate_well_known_tag_constants!(
223 |Context::Tiff|
225
226 #[doc(hidden)]
229 (ExifIFDPointer, 0x8769, DefaultValue::None, d_default,
230 unit![],
231 "Exif IFD pointer"),
232 #[doc(hidden)]
235 (GPSInfoIFDPointer, 0x8825, DefaultValue::None, d_default,
236 unit![],
237 "GPS Info IFD pointer"),
238
239 |Context::Exif|
240
241 #[doc(hidden)]
244 (InteropIFDPointer, 0xa005, DefaultValue::None, d_default,
245 unit![],
246 "Interoperability IFD pointer"),
247
248 |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 (Make, 0x10f, DefaultValue::None, d_default,
272 unit![],
273 "Manufacturer of image input equipment"),
274 (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 (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 (Software, 0x131, DefaultValue::None, d_default,
311 unit![],
312 "Software used"),
313 (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 (TileOffsets, 0x144, DefaultValue::None, d_default,
329 unit![],
330 "Tiled image data location"),
331 (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 |Context::Exif|
359
360 (ExposureTime, 0x829a, DefaultValue::None, d_exptime,
361 unit!["s"],
362 "Exposure time"),
363 (FNumber, 0x829d, DefaultValue::None, d_decimal,
364 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 (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 (ExifVersion, 0x9000, DefaultValue::None, d_exifver,
410 unit![],
411 "Exif version"),
412 (DateTimeOriginal, 0x9003, DefaultValue::None, d_datetime,
415 unit![],
416 "Date and time of original data generation"),
417 (DateTimeDigitized, 0x9004, DefaultValue::None, d_datetime,
421 unit![],
422 "Date and time of digital data generation"),
423 (OffsetTime, 0x9010, DefaultValue::None, d_default,
425 unit![],
426 "Offset data of DateTime"),
427 (OffsetTimeOriginal, 0x9011, DefaultValue::None, d_default,
429 unit![],
430 "Offset data of DateTimeOriginal"),
431 (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 (SubSecTime, 0x9290, DefaultValue::None, d_default,
482 unit![],
483 "DateTime subseconds"),
484 (SubSecTimeOriginal, 0x9291, DefaultValue::None, d_default,
486 unit![],
487 "DateTimeOriginal subseconds"),
488 (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 (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 |Context::Gps|
631
632 (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 |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
751pub 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
759fn 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
771fn 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
785fn 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
801fn 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
811fn 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
823fn 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
836fn d_ycbcrsubsamp(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
838 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
853fn 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
863fn 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
875fn 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
892fn 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
908fn 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
924fn 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
941fn 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
953fn 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
969fn 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
999fn 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
1026fn 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
1044fn 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
1067fn 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
1077fn 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
1092fn 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
1104fn 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
1113fn 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
1123fn 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
1134fn 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
1144fn 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
1152fn 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
1160fn 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
1172fn 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
1185fn 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
1196fn 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
1207fn 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
1218fn 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
1230fn d_lensspec(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result {
1232 match value.rational().and_then(|x| x.get(..4)) {
1233 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
1242fn 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
1254fn 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
1262fn 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
1270fn 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
1279fn 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
1289fn 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
1299fn 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
1313fn 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
1323fn 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
1333fn 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
1344fn 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
1355fn 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
1366fn 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
1383fn 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
1393fn 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 #[test]
1502 fn tag_constant_in_pattern() {
1503 match Tag(Context::Tiff, 0x132) {
1505 Tag(Context::Tiff, 0x132) => {},
1506 _ => panic!("failed to match Tag"),
1507 }
1508 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}