Write tag filter based on tag kind/'Level'
This commit is contained in:
1 parent
76721c6893
commit
b3ced063ef
1 file changed
+169
-43
+169
-43
@@ -1,23 +1,68 @@
|
|||||||
|
use crate::tags::{PhotometricInterpretation, PlanarConfiguration};
|
||||||
|
|
||||||
/// A filter of tags copied in
|
/// A filter of tags copied in
|
||||||
/// [`DirectoryEncoder::write_metadata_from`][`super::DirectoryEncoder::write_metadata_from`].
|
/// [`DirectoryEncoder::write_metadata_from`][`super::DirectoryEncoder::write_metadata_from`].
|
||||||
pub struct TagFilter {
|
pub struct TagFilter {
|
||||||
unknown: bool,
|
filter_level: FilterLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TagFilter {
|
impl TagFilter {
|
||||||
/// Return a filter that allows all tags from metadata subdirectories.
|
/// Return a filter that allows all tags from metadata subdirectories.
|
||||||
pub fn allow_all_in_subdirectories() -> &'static TagFilter {
|
pub fn allow_all_in_subdirectories() -> &'static TagFilter {
|
||||||
static INSTANCE: TagFilter = TagFilter { unknown: true };
|
static INSTANCE: TagFilter = TagFilter {
|
||||||
|
filter_level: FilterLevel::All,
|
||||||
|
};
|
||||||
&INSTANCE
|
&INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a filter that has an explicit allow list of tags, for each of a known list of
|
/// Return a filter that has an explicit allow list of tags, for each of a known list of
|
||||||
/// metadata subdirectories.
|
/// metadata subdirectories.
|
||||||
pub fn allow_known() -> &'static TagFilter {
|
pub fn only_known_tags() -> &'static TagFilter {
|
||||||
static INSTANCE: TagFilter = TagFilter { unknown: false };
|
static INSTANCE: TagFilter = TagFilter {
|
||||||
|
filter_level: FilterLevel::Known,
|
||||||
|
};
|
||||||
&INSTANCE
|
&INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a filter that attempts to remove identifying tags. This is stricter than
|
||||||
|
/// [`Self::only_known_tags`] and excludes many tags except those describing the image data
|
||||||
|
/// layout.
|
||||||
|
///
|
||||||
|
/// **Note that it comes with *no warranty*.** although this will remove tags with prejudice.
|
||||||
|
pub fn attempt_at_privacy() -> &'static TagFilter {
|
||||||
|
static INSTANCE: TagFilter = TagFilter {
|
||||||
|
filter_level: FilterLevel::Privacy,
|
||||||
|
};
|
||||||
|
&INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME: take a configuration by which to filter out complete metadata kinds.
|
||||||
|
/// FIXME: when we iterate over a whole directory of tags, this iteration is more expensive
|
||||||
|
/// than necessary. Tags within a directory should be ordered. We can thus perform a binary set
|
||||||
|
/// join which is more efficient than checking the individual filter on each. Does not matter
|
||||||
|
/// much in most cases though.
|
||||||
|
pub(crate) fn filter_primary(&self, tag: u16) -> Level {
|
||||||
|
let as_exif = self.filter_primary_exif(tag);
|
||||||
|
|
||||||
|
if !matches!(as_exif, Level::Unknown) {
|
||||||
|
return as_exif;
|
||||||
|
}
|
||||||
|
|
||||||
|
let as_gps = self.filter_primary_gps(tag);
|
||||||
|
|
||||||
|
if !matches!(as_gps, Level::Unknown) {
|
||||||
|
return as_gps;
|
||||||
|
}
|
||||||
|
|
||||||
|
let as_icc = self.filter_icc(tag);
|
||||||
|
|
||||||
|
if !matches!(as_icc, Level::Unknown) {
|
||||||
|
return as_icc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Level::Unknown
|
||||||
|
}
|
||||||
|
|
||||||
/// As per EXIF V3.0 tags, Primary Image (0th IFD).
|
/// As per EXIF V3.0 tags, Primary Image (0th IFD).
|
||||||
pub(crate) fn filter_primary_exif(&self, tag: u16) -> Level {
|
pub(crate) fn filter_primary_exif(&self, tag: u16) -> Level {
|
||||||
match tag {
|
match tag {
|
||||||
@@ -25,20 +70,26 @@ impl TagFilter {
|
|||||||
0x0100..=0x0103 => Level::SampleLayout,
|
0x0100..=0x0103 => Level::SampleLayout,
|
||||||
// photometric interpretation
|
// photometric interpretation
|
||||||
0x0106 => Level::SampleLayout,
|
0x0106 => Level::SampleLayout,
|
||||||
0x010e..=0x0110 => Level::Basic,
|
// data tied to the camera and customized description, potentially PI.
|
||||||
|
0x010f..=0x0110 => Level::Value,
|
||||||
// strip offsets
|
// strip offsets
|
||||||
0x0111 => Level::SampleLayout,
|
0x0111 => Level::SampleLayout,
|
||||||
0x0112 => Level::Basic,
|
// orientation
|
||||||
|
0x0112 => Level::Value,
|
||||||
// strip byte counts, samples
|
// strip byte counts, samples
|
||||||
0x0115..=0x0117 => Level::SampleLayout,
|
0x0115..=0x0117 => Level::SampleLayout,
|
||||||
0x011a..=0x011b => Level::Basic,
|
// pixel dimensions (ratio). Should be generic enough.
|
||||||
|
0x011a..=0x011b => Level::Value,
|
||||||
// planar configuration
|
// planar configuration
|
||||||
0x011c => Level::Photometric(Photometric::Planar),
|
0x011c => Level::Photometric(Photometric::Planar),
|
||||||
0x0128 => Level::Basic,
|
// units for 0x011a,0x011b
|
||||||
|
0x0128 => Level::Value,
|
||||||
// transfer function
|
// transfer function
|
||||||
0x012d => Level::Photometric(Photometric::All),
|
0x012d => Level::Photometric(Photometric::All),
|
||||||
0x0131..=0x0132 => Level::Basic,
|
// file data time, software pipeline. I'm wary of privacy implications.
|
||||||
0x013b => Level::Basic,
|
0x0131..=0x0132 => Level::Value,
|
||||||
|
// artist, clearly PI unless you explicitly wish it to be attributed.
|
||||||
|
0x013b => Level::Value,
|
||||||
// Whitepoint, Chromaticities
|
// Whitepoint, Chromaticities
|
||||||
0x013e..=0x013f => Level::Photometric(Photometric::All),
|
0x013e..=0x013f => Level::Photometric(Photometric::All),
|
||||||
// The table lists these as Not allowed for anything but we may have a choice here for
|
// The table lists these as Not allowed for anything but we may have a choice here for
|
||||||
@@ -46,7 +97,8 @@ impl TagFilter {
|
|||||||
0x0201..=0x0202 => Level::Special(Special::JpegThumbnail),
|
0x0201..=0x0202 => Level::Special(Special::JpegThumbnail),
|
||||||
0x0211..=0x0213 => Level::Photometric(Photometric::YCbCr),
|
0x0211..=0x0213 => Level::Photometric(Photometric::YCbCr),
|
||||||
0x0214 => Level::Photometric(Photometric::All),
|
0x0214 => Level::Photometric(Photometric::All),
|
||||||
0x8298 => Level::Basic,
|
// Copyright holder (not same as artist).
|
||||||
|
0x8298 => Level::Value,
|
||||||
0x8769 => Level::Ifd(SubifdKind::ExifPrivate),
|
0x8769 => Level::Ifd(SubifdKind::ExifPrivate),
|
||||||
_ => Level::Unknown,
|
_ => Level::Unknown,
|
||||||
}
|
}
|
||||||
@@ -61,54 +113,57 @@ impl TagFilter {
|
|||||||
|
|
||||||
pub(crate) fn filter_icc(&self, tag: u16) -> Level {
|
pub(crate) fn filter_icc(&self, tag: u16) -> Level {
|
||||||
match tag {
|
match tag {
|
||||||
0x8ff3 => Level::Basic,
|
0x8ff3 => Level::Value,
|
||||||
_ => Level::Unknown,
|
_ => Level::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn filter_exif_private(&self, tag: u16) -> Level {
|
pub(crate) fn filter_exif_private(&self, tag: u16) -> Level {
|
||||||
match tag {
|
match tag {
|
||||||
0x829a => Level::Basic,
|
// Maybe this is not personal identifying but some equipment may be very specifically
|
||||||
0x829d => Level::Basic,
|
// known through these. And it's not critical to displaying it correctly.
|
||||||
0x8822 => Level::Basic,
|
0x829a => Level::Value,
|
||||||
0x8824 => Level::Basic,
|
0x829d => Level::Value,
|
||||||
0x8827 => Level::Basic,
|
0x8822 => Level::Value,
|
||||||
0x8828 => Level::Basic,
|
0x8824 => Level::Value,
|
||||||
0x8830..=0x8835 => Level::Basic,
|
0x8827 => Level::Value,
|
||||||
0x9000 => Level::Basic,
|
// Opto-electric coefficients
|
||||||
0x9003..=0x9004 => Level::Basic,
|
0x8828 => Level::Value,
|
||||||
0x9010..=0x9012 => Level::Basic,
|
0x8830..=0x8835 => Level::Value,
|
||||||
|
0x9000 => Level::Value,
|
||||||
|
0x9003..=0x9004 => Level::Value,
|
||||||
|
0x9010..=0x9012 => Level::Value,
|
||||||
// Not allowed for uncompressed data!
|
// Not allowed for uncompressed data!
|
||||||
0x9101..=0x9102 => Level::Special(Special::CompressedExclusive),
|
0x9101..=0x9102 => Level::Special(Special::CompressedExclusive),
|
||||||
0x9201..=0x9209 => Level::Basic,
|
0x9201..=0x9209 => Level::Value,
|
||||||
0x920a => Level::Basic,
|
0x920a => Level::Value,
|
||||||
0x9214 => Level::Basic,
|
0x9214 => Level::Value,
|
||||||
0x927c => Level::Basic,
|
0x927c => Level::Value,
|
||||||
0x9286 => Level::Basic,
|
0x9286 => Level::Value,
|
||||||
0x9290..=0x9292 => Level::Basic,
|
0x9290..=0x9292 => Level::Value,
|
||||||
0x9400..=0x9405 => Level::Basic,
|
0x9400..=0x9405 => Level::Value,
|
||||||
0xa000 => Level::Basic,
|
0xa000 => Level::Value,
|
||||||
0xa001 => Level::Photometric(Photometric::All),
|
0xa001 => Level::Photometric(Photometric::All),
|
||||||
0xa002..=0xa003 => Level::Special(Special::CompressedExclusive),
|
0xa002..=0xa003 => Level::Special(Special::CompressedExclusive),
|
||||||
0xa004 => Level::Basic,
|
0xa004 => Level::Value,
|
||||||
0xa005 => Level::Ifd(SubifdKind::Interoperability),
|
0xa005 => Level::Ifd(SubifdKind::Interoperability),
|
||||||
0xa20b..=0xa20c => Level::Basic,
|
0xa20b..=0xa20c => Level::Value,
|
||||||
0xa20e..=0xa210 => Level::Basic,
|
0xa20e..=0xa210 => Level::Value,
|
||||||
0xa214..=0xa215 => Level::Basic,
|
0xa214..=0xa215 => Level::Value,
|
||||||
0xa217 => Level::Basic,
|
0xa217 => Level::Value,
|
||||||
0xa300..=0xa302 => Level::Basic,
|
0xa300..=0xa302 => Level::Value,
|
||||||
0xa401..=0xa40c => Level::Basic,
|
0xa401..=0xa40c => Level::Value,
|
||||||
0xa420 => Level::Basic,
|
0xa420 => Level::Value,
|
||||||
0xa430..=0xa43c => Level::Basic,
|
0xa430..=0xa43c => Level::Value,
|
||||||
0xa460..=0xa462 => Level::Basic,
|
0xa460..=0xa462 => Level::Value,
|
||||||
0xa500 => Level::Photometric(Photometric::All),
|
0xa500 => Level::Photometric(Photometric::All),
|
||||||
_ => Level::Unknown,
|
_ => Level::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_gps_private(&self, tag: u16) -> Level {
|
pub(crate) fn filter_gps_private(&self, tag: u16) -> Level {
|
||||||
match tag {
|
match tag {
|
||||||
0x0000..=0x001f => Level::Basic,
|
0x0000..=0x001f => Level::Value,
|
||||||
_ => Level::Unknown,
|
_ => Level::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,6 +185,61 @@ impl TagFilter {
|
|||||||
pub(crate) fn filter_secondary_gps(&self, tag: u16) -> Level {
|
pub(crate) fn filter_secondary_gps(&self, tag: u16) -> Level {
|
||||||
self.filter_primary_gps(tag)
|
self.filter_primary_gps(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn should_keep_on_level(
|
||||||
|
&self,
|
||||||
|
level: Level,
|
||||||
|
target: &super::EncodeMetadataApplicability,
|
||||||
|
) -> Choice {
|
||||||
|
match level {
|
||||||
|
Level::Unknown if matches!(self.filter_level, FilterLevel::All) => Choice::Ok,
|
||||||
|
Level::SampleLayout => Choice::Ok,
|
||||||
|
Level::Ifd(subifd_kind) => Choice::Descend(subifd_kind),
|
||||||
|
Level::Photometric(photometric) => {
|
||||||
|
match (photometric, target.planar, target.photometric) {
|
||||||
|
(Photometric::All, _, _) => Choice::Ok,
|
||||||
|
(Photometric::Planar, Some(PlanarConfiguration::Planar), _) => Choice::Ok,
|
||||||
|
(Photometric::Planar, None, _)
|
||||||
|
if matches!(self.filter_level, FilterLevel::All) =>
|
||||||
|
{
|
||||||
|
Choice::Ok
|
||||||
|
}
|
||||||
|
(Photometric::YCbCr, _, Some(PhotometricInterpretation::YCbCr)) => Choice::Ok,
|
||||||
|
(Photometric::YCbCr, _, None)
|
||||||
|
if matches!(self.filter_level, FilterLevel::All) =>
|
||||||
|
{
|
||||||
|
Choice::Ok
|
||||||
|
}
|
||||||
|
_ => Choice::Discard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Level::Value if matches!(self.filter_level, FilterLevel::Privacy) => Choice::Discard,
|
||||||
|
Level::Value => Choice::Ok,
|
||||||
|
Level::Special(Special::JpegThumbnail)
|
||||||
|
if matches!(self.filter_level, FilterLevel::Privacy) =>
|
||||||
|
{
|
||||||
|
// Contains arbitrary unverified other data, so, no, we do not heed backward
|
||||||
|
// compatibility of a tag that should no longer be in active use if possible.
|
||||||
|
Choice::Discard
|
||||||
|
}
|
||||||
|
Level::Special(special) => Choice::Special(special),
|
||||||
|
_ => Choice::Discard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum Choice {
|
||||||
|
Ok,
|
||||||
|
Descend(SubifdKind),
|
||||||
|
Discard,
|
||||||
|
Special(Special),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum FilterLevel {
|
||||||
|
All,
|
||||||
|
Known,
|
||||||
|
Privacy,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The level of support for a value.
|
/// The level of support for a value.
|
||||||
@@ -138,12 +248,27 @@ pub(crate) enum Level {
|
|||||||
Unknown,
|
Unknown,
|
||||||
/// Value describes the image data layout, which we will not want to write into the encoder's
|
/// 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.
|
/// directory where the image data is written later.
|
||||||
|
///
|
||||||
|
/// These are treated as privacy preserving.
|
||||||
SampleLayout,
|
SampleLayout,
|
||||||
/// Value is an IFD and must be copied by making a subdirectory.
|
/// Value is an IFD and must be copied by making a subdirectory.
|
||||||
|
///
|
||||||
|
/// These are treated as privacy preserving, independent of the tags within the subdirectory
|
||||||
|
/// which may not be. (For instance the presence of an empty directory is fine but we will omit
|
||||||
|
/// a GPS anyways).
|
||||||
Ifd(SubifdKind),
|
Ifd(SubifdKind),
|
||||||
|
/// Value is related to photometric interpretation and should not be copied when the target
|
||||||
|
/// does not match the expected interpretation.
|
||||||
|
///
|
||||||
|
/// These are *generally* treated as privacy preserving.
|
||||||
Photometric(Photometric),
|
Photometric(Photometric),
|
||||||
Basic,
|
/// This is a value we know of.
|
||||||
|
/// FIXME: we do not differentiate by personal identifiable information and otherwise which is
|
||||||
|
/// quite important for some variations of copying metadata.
|
||||||
|
Value,
|
||||||
/// We recognize the tag but it can not be copied by itself through normal means.
|
/// We recognize the tag but it can not be copied by itself through normal means.
|
||||||
|
///
|
||||||
|
/// Privacy treatment depends on the tag.
|
||||||
Special(Special),
|
Special(Special),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +290,7 @@ pub(crate) enum SubifdKind {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum Special {
|
pub(crate) enum Special {
|
||||||
|
/// Thumbnail which we regard as PI since we do not deep-clean the JPEG structure.
|
||||||
JpegThumbnail,
|
JpegThumbnail,
|
||||||
/// May only occur in compressed (JPEG APP segments) data.
|
/// May only occur in compressed (JPEG APP segments) data.
|
||||||
CompressedExclusive,
|
CompressedExclusive,
|
||||||
|
|||||||
Reference in new issue
Block a user