Sketch a TagFilter option for copying metadata
This commit is contained in:
1 parent
1bd6293857
commit
348bbda906
2 files changed
+219
-1
No files matched your search
+48
-1
@@ -9,7 +9,7 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
decoder::ifd::Entry,
|
||||
decoder::{ifd::Entry, IfdDecoder},
|
||||
error::{TiffResult, UsageError},
|
||||
tags::{
|
||||
ByteOrder, CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation,
|
||||
@@ -22,6 +22,7 @@ pub mod colortype;
|
||||
pub mod compression;
|
||||
mod tiff_value;
|
||||
mod writer;
|
||||
mod tag_filter;
|
||||
|
||||
use self::colortype::*;
|
||||
use self::compression::Compression as Comp;
|
||||
@@ -40,6 +41,8 @@ pub type Predictor = crate::tags::Predictor;
|
||||
#[cfg(feature = "deflate")]
|
||||
pub type DeflateLevel = compression::DeflateLevel;
|
||||
|
||||
pub use tag_filter::TagFilter;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Default)]
|
||||
pub enum Compression {
|
||||
#[default]
|
||||
@@ -434,6 +437,22 @@ impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> {
|
||||
self.directory.extend(entries);
|
||||
}
|
||||
|
||||
/// Copy metadata from the IFD that a decoder refers to.
|
||||
pub fn write_metadata_from(
|
||||
&mut self,
|
||||
mut decoder: IfdDecoder<'_>,
|
||||
options: EncodeMetadataOptions<'_>,
|
||||
) -> TiffResult<EncodeMetadataReport> {
|
||||
let mut report = EncodeMetadataReport {
|
||||
};
|
||||
|
||||
for tag in decoder.tag_iter() {
|
||||
let (tag, entry) = tag?;
|
||||
}
|
||||
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
/// Define the parent directory.
|
||||
///
|
||||
/// Each directory has an offset based link to its successor, forming a linked list in the
|
||||
@@ -582,6 +601,34 @@ impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> {
|
||||
}
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct EncodeMetadataOptions<'lt> {
|
||||
/// Several metadata kinds contain additional tags in further, linked directories that are only
|
||||
/// referenced with an `Ifd`/`Ifd8` tag. These directories may have their own semantics, e.g. a
|
||||
/// different set of tags or even new value types.
|
||||
///
|
||||
/// Since we know that the whole directory contains only metadata it would be possible to copy
|
||||
/// all tags contained in it, however tags that can not be semantically validated might contain
|
||||
/// data that is only valid within the original image or must be transformed during the copy.
|
||||
/// For instance, JPEG thumbnails are embedded with separate offset and length fields (`0x0201`
|
||||
/// and `0x202`). (This has caused the author mental harm, please do not consult me about it).
|
||||
///
|
||||
/// Setting this flag will skip such unknown tags instead of attempting a potentially incorrect
|
||||
/// copy. However the set of copied metadata is then dependent on the `tiff` library.
|
||||
pub tag_filter: &'lt TagFilter,
|
||||
/// Some tags are not allowed to be recorded or can be semantically validated. The library can
|
||||
/// skip these tags while copying metadata. For instance, `YCbCrCoefficients` _may_ be
|
||||
/// recorded for images in `YCbCr` photometric interpretation but _must not_ be recorded for
|
||||
/// images in any other interpretation (colorspace conversion should have been done during the
|
||||
/// transformation).
|
||||
pub skip_invalid_tags: bool,
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct EncodeMetadataReport {
|
||||
}
|
||||
|
||||
/// Type to encode images strip by strip.
|
||||
///
|
||||
/// You should call `finish` on this when you are finished with it.
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/// A filter of tags copied in
|
||||
/// [`DirectoryEncoder::write_metadata_from`][`super::DirectoryEncoder::write_metadata_from`].
|
||||
pub struct TagFilter {
|
||||
unknown: bool,
|
||||
}
|
||||
|
||||
impl TagFilter {
|
||||
/// Return a filter that allows all tags from metadata subdirectories.
|
||||
pub fn allow_all_in_subdirectories() -> &'static TagFilter {
|
||||
static INSTANCE: TagFilter = TagFilter { unknown: true };
|
||||
&INSTANCE
|
||||
}
|
||||
|
||||
/// Return a filter that has an explicit allow list of tags, for each of a known list of
|
||||
/// metadata subdirectories.
|
||||
pub fn allow_known() -> &'static TagFilter {
|
||||
static INSTANCE: TagFilter = TagFilter { unknown: false };
|
||||
&INSTANCE
|
||||
}
|
||||
|
||||
/// As per EXIF V3.0 tags, Primary Image (0th IFD).
|
||||
pub(crate) fn filter_primary_exif(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
// width, height, etc
|
||||
0x0100..=0x0103 => Level::SampleLayout,
|
||||
// photometric interpretation
|
||||
0x0106 => Level::SampleLayout,
|
||||
0x010e..=0x0110 => Level::Basic,
|
||||
// strip offsets
|
||||
0x0111 => Level::SampleLayout,
|
||||
0x0112 => Level::Basic,
|
||||
// strip byte counts, samples
|
||||
0x0115..=0x0117 => Level::SampleLayout,
|
||||
0x011a..=0x011b => Level::Basic,
|
||||
// planar configuration
|
||||
0x011c => Level::Photometric(Photometric::Planar),
|
||||
0x0128 => Level::Basic,
|
||||
// transfer function
|
||||
0x012d => Level::Photometric(Photometric::All),
|
||||
0x0131..=0x0132 => Level::Basic,
|
||||
0x013b => Level::Basic,
|
||||
// Whitepoint, Chromaticities
|
||||
0x013e..=0x013f => Level::Photometric(Photometric::All),
|
||||
// The table lists these as Not allowed for anything but we may have a choice here for
|
||||
// older models of images.
|
||||
0x0201..=0x0202 => Level::Special(Special::JpegThumbnail),
|
||||
0x0211..=0x0213 => Level::Photometric(Photometric::YCbCr),
|
||||
0x0214 => Level::Photometric(Photometric::All),
|
||||
0x8298 => Level::Basic,
|
||||
0x8769 => Level::Ifd(SubifdKind::ExifPrivate),
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_primary_gps(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
0x8825 => Level::Ifd(SubifdKind::Gps),
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_icc(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
0x8ff3 => Level::Basic,
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_exif_private(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
0x829a => Level::Basic,
|
||||
0x829d => Level::Basic,
|
||||
0x8822 => Level::Basic,
|
||||
0x8824 => Level::Basic,
|
||||
0x8827 => Level::Basic,
|
||||
0x8828 => Level::Basic,
|
||||
0x8830..=0x8835 => Level::Basic,
|
||||
0x9000 => Level::Basic,
|
||||
0x9003..=0x9004 => Level::Basic,
|
||||
0x9010..=0x9012 => Level::Basic,
|
||||
// Not allowed for uncompressed data!
|
||||
0x9101..=0x9102 => Level::Special(Special::CompressedExclusive),
|
||||
0x9201..=0x9209 => Level::Basic,
|
||||
0x920a => Level::Basic,
|
||||
0x9214 => Level::Basic,
|
||||
0x927c => Level::Basic,
|
||||
0x9286 => Level::Basic,
|
||||
0x9290..=0x9292 => Level::Basic,
|
||||
0x9400..=0x9405 => Level::Basic,
|
||||
0xa000 => Level::Basic,
|
||||
0xa001 => Level::Photometric(Photometric::All),
|
||||
0xa002..=0xa003 => Level::Special(Special::CompressedExclusive),
|
||||
0xa004 => Level::Basic,
|
||||
0xa005 => Level::Ifd(SubifdKind::Interoperability),
|
||||
0xa20b..=0xa20c => Level::Basic,
|
||||
0xa20e..=0xa210 => Level::Basic,
|
||||
0xa214..=0xa215 => Level::Basic,
|
||||
0xa217 => Level::Basic,
|
||||
0xa300..=0xa302 => Level::Basic,
|
||||
0xa401..=0xa40c => Level::Basic,
|
||||
0xa420 => Level::Basic,
|
||||
0xa430..=0xa43c => Level::Basic,
|
||||
0xa460..=0xa462 => Level::Basic,
|
||||
0xa500 => Level::Photometric(Photometric::All),
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_gps_private(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
0x0000..=0x001f => Level::Basic,
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_interoperability(&self, tag: u16) -> Level {
|
||||
match tag {
|
||||
0x0001 => Level::Special(Special::CompressedExclusive),
|
||||
_ => Level::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn filter_secondary_exif(&self, tag: u16) -> Level {
|
||||
// No difference for now. The only tabular difference I could spot is the treatment of
|
||||
// sample positioning which is mandatory in the primary IFD of YCbCr but optional for all
|
||||
// following IFDs..
|
||||
self.filter_primary_exif(tag)
|
||||
}
|
||||
|
||||
pub(crate) fn filter_secondary_gps(&self, tag: u16) -> Level {
|
||||
self.filter_primary_gps(tag)
|
||||
}
|
||||
}
|
||||
|
||||
/// The level of support for a value.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum Level {
|
||||
Unknown,
|
||||
/// Value describes the image data layout, which we will not want to write into the encoder's
|
||||
/// directory where the image data is written later.
|
||||
SampleLayout,
|
||||
/// Value is an IFD and must be copied by making a subdirectory.
|
||||
Ifd(SubifdKind),
|
||||
Photometric(Photometric),
|
||||
Basic,
|
||||
/// We recognize the tag but it can not be copied by itself through normal means.
|
||||
Special(Special),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum Photometric {
|
||||
All,
|
||||
/// Do not copy for non-planar data.
|
||||
Planar,
|
||||
/// Do not copy for non-YCbCr data.
|
||||
YCbCr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum SubifdKind {
|
||||
ExifPrivate,
|
||||
Gps,
|
||||
Interoperability,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum Special {
|
||||
JpegThumbnail,
|
||||
/// May only occur in compressed (JPEG APP segments) data.
|
||||
CompressedExclusive,
|
||||
}
|
||||
Reference in new issue
Block a user