diff --git a/exif.go b/exif.go index 4aa6270..4ac8681 100644 --- a/exif.go +++ b/exif.go @@ -141,3 +141,7 @@ func makeExifDatumIterator(data *ExifData, cIter *C.Exiv2ExifDatumIterator) *Exi return datum } + +func (i *Image) ExifStripKey(key string) error { + return i.StripKey(EXIF, key) +} diff --git a/exiv.go b/exiv.go index b1be378..1e2d598 100644 --- a/exiv.go +++ b/exiv.go @@ -25,6 +25,14 @@ type MetadataProvider interface { GetString(key string) (string, error) } +type MetadataFormat int + +const ( + EXIF MetadataFormat = iota + IPTC + XMP +) + var ErrMetadataKeyNotFound = errors.New("key not found") func (e *Error) Error() string { @@ -224,3 +232,29 @@ func (i *Image) SetMetadataShort(format, key, value string) error { return nil } + +func (i *Image) StripKey(f MetadataFormat, key string) error { + ckey := C.CString(key) + defer C.free(unsafe.Pointer(ckey)) + + var cErr *C.Exiv2Error + + switch f { + case EXIF: + C.exiv2_exif_strip_key(i.img, ckey, &cErr) + case IPTC: + C.exiv2_iptc_strip_key(i.img, ckey, &cErr) + case XMP: + C.exiv2_xmp_strip_key(i.img, ckey, &cErr) + default: + return errors.New("invalid metadata format") + } + + if cErr != nil { + err := makeError(cErr) + C.exiv2_error_free(cErr) + return err + } + + return nil +} diff --git a/exiv_test.go b/exiv_test.go index c2e8ee8..19f79ea 100644 --- a/exiv_test.go +++ b/exiv_test.go @@ -447,6 +447,64 @@ func Test_GetBytes_Goroutine(t *testing.T) { t.Logf("Allocated bytes after test: %+v\n", memStats.HeapAlloc) } +func TestExifStripKey(t *testing.T) { + img, err := goexiv.Open("testdata/pixel.jpg") + require.NoError(t, err) + + err = img.SetExifString("Exif.Photo.UserComment", "123") + require.NoError(t, err) + + err = img.ExifStripKey("Exif.Photo.UserComment") + require.NoError(t, err) + + err = img.ReadMetadata() + require.NoError(t, err) + + data := img.GetExifData() + + _, err = data.GetString("Exif.Photo.UserComment") + require.Error(t, err) +} + +func TestIptcStripKey(t *testing.T) { + img, err := goexiv.Open("testdata/pixel.jpg") + require.NoError(t, err) + + err = img.SetIptcString("Iptc.Application2.Caption", "123") + require.NoError(t, err) + + err = img.IptcStripKey("Iptc.Application2.Caption") + require.NoError(t, err) + + err = img.ReadMetadata() + require.NoError(t, err) + + data := img.GetIptcData() + + _, err = data.GetString("Iptc.Application2.Caption") + require.Error(t, err) +} + +func TestXmpStripKey(t *testing.T) { + t.Skip("XMP SetXmpString and GetString is not implemented yet") + //img, err := goexiv.Open("testdata/pixel.jpg") + //require.NoError(t, err) + // + //err = img.SetXmpString("Xmp.dc.description", "123") + //require.NoError(t, err) + // + //err = img.XmpStripKey("Xmp.dc.description") + //require.NoError(t, err) + // + //err = img.ReadMetadata() + //require.NoError(t, err) + // + //data := img.GetXmpData() + // + //_, err = data.GetString("Xmp.dc.description") + //require.Error(t, err) +} + func BenchmarkImage_GetBytes_KeepAlive(b *testing.B) { bytes, err := ioutil.ReadFile("testdata/stripped_pixel.jpg") require.NoError(b, err) diff --git a/helper.cpp b/helper.cpp index 46ee501..4c453b5 100644 --- a/helper.cpp +++ b/helper.cpp @@ -401,6 +401,66 @@ Exiv2ExifDatum* exiv2_exif_datum_iterator_next(Exiv2ExifDatumIterator *iter) return iter->next(); } +void +exiv2_exif_strip_key(Exiv2Image *img, char *key, Exiv2Error **error) +{ + Exiv2::ExifData exifData = img->image->exifData(); + + try { + Exiv2::ExifData::iterator pos = exifData.findKey(Exiv2::ExifKey(key)); + if (pos == exifData.end()) { + return; + } + exifData.erase(pos); + img->image->setExifData(exifData); + img->image->writeMetadata(); + } catch (Exiv2::Error &e) { + if (error) { + *error = new Exiv2Error(e); + } + } +} + +void +exiv2_iptc_strip_key(Exiv2Image *img, char *key, Exiv2Error **error) +{ + Exiv2::IptcData iptcData = img->image->iptcData(); + + try { + Exiv2::IptcData::iterator pos = iptcData.findKey(Exiv2::IptcKey(key)); + if (pos == iptcData.end()) { + return; + } + iptcData.erase(pos); + img->image->setIptcData(iptcData); + img->image->writeMetadata(); + } catch (Exiv2::Error &e) { + if (error) { + *error = new Exiv2Error(e); + } + } +} + +void +exiv2_xmp_strip_key(Exiv2Image *img, char *key, Exiv2Error **error) +{ + Exiv2::XmpData xmpData = img->image->xmpData(); + + try { + Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(key)); + if (pos == xmpData.end()) { + return; + } + xmpData.erase(pos); + img->image->setXmpData(xmpData); + img->image->writeMetadata(); + } catch (Exiv2::Error &e) { + if (error) { + *error = new Exiv2Error(e); + } + } +} + DEFINE_FREE_FUNCTION(exiv2_exif_data, Exiv2ExifData*); const char* exiv2_exif_datum_key(const Exiv2ExifDatum *datum) diff --git a/helper.h b/helper.h index be65621..cba21e6 100644 --- a/helper.h +++ b/helper.h @@ -61,6 +61,10 @@ Exiv2ExifDatumIterator* exiv2_exif_data_iterator(const Exiv2ExifData *data); int exiv2_exif_data_iterator_has_next(const Exiv2ExifDatumIterator *iter); Exiv2ExifDatum* exiv2_exif_datum_iterator_next(Exiv2ExifDatumIterator *iter); +void exiv2_exif_strip_key(Exiv2Image *img, char *key, Exiv2Error **error); +void exiv2_iptc_strip_key(Exiv2Image *img, char *key, Exiv2Error **error); +void exiv2_xmp_strip_key(Exiv2Image *img, char *key, Exiv2Error **error); + const unsigned char* exiv2_image_icc_profile(Exiv2Image *img); long exiv2_image_icc_profile_size(Exiv2Image *img); diff --git a/iptc.go b/iptc.go index 11a6b14..58f47e5 100644 --- a/iptc.go +++ b/iptc.go @@ -145,3 +145,7 @@ func makeIptcDatumIterator(data *IptcData, cIter *C.Exiv2IptcDatumIterator) *Ipt return datum } + +func (i *Image) IptcStripKey(key string) error { + return i.StripKey(IPTC, key) +} diff --git a/xmp.go b/xmp.go index 0c3e0f1..ab655a6 100644 --- a/xmp.go +++ b/xmp.go @@ -84,3 +84,7 @@ func (d *XmpDatum) String() string { return C.GoString(cstr) } + +func (i *Image) XmpStripKey(key string) error { + return i.StripKey(XMP, key) +}