Skip to content

Commit

Permalink
Merge pull request #3 from kolesa-team/short-values
Browse files Browse the repository at this point in the history
writing short integers to image metadata
  • Loading branch information
antonsergeyev authored May 26, 2021
2 parents 00c1a75 + e308f9c commit 24eec5f
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 2 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ if err != nil {
return err
}

// Write an EXIF short integer
err = goexivImg.SetMetadataShort("exif", "Exif.Photo.ExposureProgram", "2")
if err != nil {
return err
}

// Read metadata
err = goexivImg.ReadMetadata()
if err != nil {
Expand Down
31 changes: 31 additions & 0 deletions exiv.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,34 @@ func (i *Image) SetMetadataString(format, key, value string) error {

return nil
}

// Sets an exif or iptc key with a given short value
func (i *Image) SetMetadataShort(format, key, value string) error {
if format != "iptc" && format != "exif" {
return errors.New("invalid metadata type: " + format)
}

cKey := C.CString(key)
cValue := C.CString(value)

defer func() {
C.free(unsafe.Pointer(cKey))
C.free(unsafe.Pointer(cValue))
}()

var cerr *C.Exiv2Error

if format == "iptc" {
C.exiv2_image_set_iptc_short(i.img, cKey, cValue, &cerr)
} else {
C.exiv2_image_set_exif_short(i.img, cKey, cValue, &cerr)
}

if cerr != nil {
err := makeError(cerr)
C.exiv2_error_free(cerr)
return err
}

return nil
}
98 changes: 96 additions & 2 deletions exiv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ type MetadataTestCase struct {
ExpectedErrorSubstring string
}

var metadataTestCases = []MetadataTestCase{
var metadataSetStringTestCases = []MetadataTestCase{
// valid exif key, jpeg
{
Format: "exif",
Expand Down Expand Up @@ -244,7 +244,7 @@ var metadataTestCases = []MetadataTestCase{
func Test_SetMetadataStringFromFile(t *testing.T) {
var data goexiv.MetadataProvider

for i, testcase := range metadataTestCases {
for i, testcase := range metadataSetStringTestCases {
img, err := goexiv.Open(testcase.ImageFilename)
require.NoErrorf(t, err, "case #%d Error while opening image file", i)

Expand Down Expand Up @@ -283,6 +283,100 @@ func Test_SetMetadataStringFromFile(t *testing.T) {
}
}

var metadataSetShortIntTestCases = []MetadataTestCase{
// valid exif key, jpeg
{
Format: "exif",
Key: "Exif.Photo.ExposureProgram",
Value: "1",
ImageFilename: "testdata/pixel.jpg",
ExpectedErrorSubstring: "", // no error
},
// valid exif key, webp
{
Format: "exif",
Key: "Exif.Photo.ExposureProgram",
Value: "2",
ImageFilename: "testdata/pixel.webp",
ExpectedErrorSubstring: "",
},
// valid iptc key, jpeg.
// webp iptc is not supported (see libexiv2/src/webpimage.cpp WebPImage::setIptcData))
{
Format: "iptc",
Key: "Iptc.Envelope.ModelVersion",
Value: "3",
ImageFilename: "testdata/pixel.jpg",
ExpectedErrorSubstring: "",
},
// invalid exif key, jpeg
{
Format: "exif",
Key: "Exif.Invalid.Key",
Value: "4",
ImageFilename: "testdata/pixel.jpg",
ExpectedErrorSubstring: "Invalid key",
},
// invalid exif key, webp
{
Format: "exif",
Key: "Exif.Invalid.Key",
Value: "5",
ImageFilename: "testdata/pixel.webp",
ExpectedErrorSubstring: "Invalid key",
},
// invalid iptc key, jpeg
{
Format: "iptc",
Key: "Iptc.Invalid.Key",
Value: "6",
ImageFilename: "testdata/pixel.jpg",
ExpectedErrorSubstring: "Invalid record name",
},
}

func Test_SetMetadataShortInt(t *testing.T) {
var data goexiv.MetadataProvider

for i, testcase := range metadataSetShortIntTestCases {
img, err := goexiv.Open(testcase.ImageFilename)
require.NoErrorf(t, err, "case #%d Error while opening image file", i)

err = img.SetMetadataShort(testcase.Format, testcase.Key, testcase.Value)
if testcase.ExpectedErrorSubstring != "" {
require.Errorf(t, err, "case #%d Error was expected", i)
require.Containsf(
t,
err.Error(),
testcase.ExpectedErrorSubstring,
"case #%d Error text must contain a given substring",
i,
)
continue
}

require.NoErrorf(t, err, "case #%d Cannot write image metadata", i)

err = img.ReadMetadata()
require.NoErrorf(t, err, "case #%d Cannot read image metadata", i)

if testcase.Format == "iptc" {
data = img.GetIptcData()
} else {
data = img.GetExifData()
}

receivedValue, err := data.GetString(testcase.Key)
require.Equalf(
t,
testcase.Value,
receivedValue,
"case #%d Value written must be equal to the value read",
i,
)
}
}

func Test_GetBytes(t *testing.T) {
bytes, err := ioutil.ReadFile("testdata/stripped_pixel.jpg")
require.NoError(t, err)
Expand Down
40 changes: 40 additions & 0 deletions helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,26 @@ exiv2_image_set_exif_string(Exiv2Image *img, char *key, char *value, Exiv2Error
}
}

void
exiv2_image_set_exif_short(Exiv2Image *img, char *key, char *value, Exiv2Error **error)
{
Exiv2::ExifData exifData = img->image->exifData();

try {
Exiv2::Exifdatum& tag = exifData[key];
Exiv2::Value::AutoPtr valueObject = Exiv2::Value::create(Exiv2::unsignedShort);
valueObject->read(value);
tag.setValue(valueObject.get());

img->image->setExifData(exifData);
img->image->writeMetadata();
} catch (Exiv2::Error &e) {
if (error) {
*error = new Exiv2Error(e);
}
}
}

void
exiv2_image_set_iptc_string(Exiv2Image *img, char *key, char *value, Exiv2Error **error)
{
Expand All @@ -151,6 +171,26 @@ exiv2_image_set_iptc_string(Exiv2Image *img, char *key, char *value, Exiv2Error
}
}

void
exiv2_image_set_iptc_short(Exiv2Image *img, char *key, char *value, Exiv2Error **error)
{
Exiv2::IptcData iptcData = img->image->iptcData();

try {
Exiv2::Iptcdatum& tag = iptcData[key];
Exiv2::Value::AutoPtr valueObject = Exiv2::Value::create(Exiv2::unsignedShort);
valueObject->read(value);
tag.setValue(valueObject.get());

img->image->setIptcData(iptcData);
img->image->writeMetadata();
} catch (Exiv2::Error &e) {
if (error) {
*error = new Exiv2Error(e);
}
}
}

long
exiv_image_get_size(Exiv2Image *img)
{
Expand Down
2 changes: 2 additions & 0 deletions helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ unsigned char* exiv_image_get_bytes_ptr(Exiv2Image *img);

void exiv2_image_read_metadata(Exiv2Image *img, Exiv2Error **error);
void exiv2_image_set_exif_string(Exiv2Image *img, char *key, char *value, Exiv2Error **error);
void exiv2_image_set_exif_short(Exiv2Image *img, char *key, char *value, Exiv2Error **error);
void exiv2_image_set_iptc_string(Exiv2Image *img, char *key, char *value, Exiv2Error **error);
void exiv2_image_set_iptc_short(Exiv2Image *img, char *key, char *value, Exiv2Error **error);
void exiv2_image_free(Exiv2Image *img);

int exiv2_image_get_pixel_width(Exiv2Image *img);
Expand Down
4 changes: 4 additions & 0 deletions iptc.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func (i *Image) SetIptcString(key, value string) error {
return i.SetMetadataString("iptc", key, value)
}

func (i *Image) SetIptcShort(key, value string) error {
return i.SetMetadataShort("iptc", key, value)
}

func (d *IptcData) GetString(key string) (string, error) {
datum, err := d.FindKey(key)
if err != nil {
Expand Down

0 comments on commit 24eec5f

Please sign in to comment.