Skip to content

Commit

Permalink
bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
antonsergeyev committed Sep 13, 2020
1 parent 25b060b commit 6ed2746
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 96 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ It is based on https://github.com/abustany/goexiv and https://github.com/gitschn

## Requirements

A [libexiv2](http://www.exiv2.org) library v0.27 is required (this might also work with never versions, but it hasn't been tested).
A [libexiv2](http://www.exiv2.org) library v0.27 is required (this might also work with newer versions, but it hasn't been tested).

On Ubuntu, libexiv2 can be installed from the package manager (`sudo apt install libexiv2-dev`), but there is no guarantee it comes with the version needed.
So it is safer to install it manually:
Expand All @@ -23,12 +23,9 @@ So it is safer to install it manually:
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .
sudo make install
sudo ldconfig
```
* Create a file `/etc/ld.so.conf.d/libexiv.conf` with the following contents:
```
/usr/local/lib/libexiv2
```
* Run `sudo ldconfig`
* It may be needed to set the following variable: `export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig`
Now the Go code in this project can interface with the libexiv2 library.
Expand Down Expand Up @@ -89,6 +86,17 @@ if err != nil {
img = goexivImg.GetBytes()
```
Retrieving all metadata keys and values:
```
img.ReadMetadata()
// map[string]string
exif := img.GetExifData().AllTags()

// map[string]string
iptc := img.GetIptcData().AllTags()
```
A complete image processing workflow in Go can be organized with the following additional libraries:
* https://github.com/kolesa-team/go-webp - Go bindings for libwebp to process WEBP images
Expand Down
11 changes: 11 additions & 0 deletions exif.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ func (d *ExifDatum) String() string {
return C.GoString(cstr)
}

// Returns all EXIF tags
func (d *ExifData) AllTags() map[string]string {
keyValues := map[string]string{}
for i := d.Iterator(); i.HasNext(); {
d := i.Next()
keyValues[d.Key()] = d.String()
}

return keyValues
}

// Iterator returns a new ExifDatumIterator to iterate over all Exif data.
func (d *ExifData) Iterator() *ExifDatumIterator {
return makeExifDatumIterator(d, C.exiv2_exif_data_iterator(d.data))
Expand Down
109 changes: 21 additions & 88 deletions exiv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func Test_OpenBytesFailures(t *testing.T) {
"no image",
[]byte("no image"),
"Failed to read input data",
12,
20,
},
{
"empty byte slice",
Expand Down Expand Up @@ -145,105 +145,38 @@ func TestMetadata(t *testing.T) {
t.Fatalf("FindKey returns a non null datum for a valid, non existing key")
}

// Iterate over all Exif data accessing Key() and String()
{
keyValues := map[string]string{}
for i := data.Iterator(); i.HasNext(); {
d := i.Next()
keyValues[d.Key()] = d.String()
}
assert.Equal(t, keyValues, map[string]string{
"Exif.Image.ExifTag": "134",
"Exif.Image.Make": "FakeMake",
"Exif.Image.Model": "FakeModel",
"Exif.Image.ResolutionUnit": "2",
"Exif.Image.XResolution": "72/1",
"Exif.Image.YCbCrPositioning": "1",
"Exif.Image.YResolution": "72/1",
"Exif.Photo.ColorSpace": "65535",
"Exif.Photo.ComponentsConfiguration": "1 2 3 0",
"Exif.Photo.DateTimeDigitized": "2013:12:08 21:06:10",
"Exif.Photo.ExifVersion": "48 50 51 48",
"Exif.Photo.FlashpixVersion": "48 49 48 48",
})
}
assert.Equal(t, map[string]string{
"Exif.Image.ExifTag": "130",
"Exif.Image.Make": "FakeMake",
"Exif.Image.Model": "FakeModel",
"Exif.Image.ResolutionUnit": "2",
"Exif.Image.XResolution": "72/1",
"Exif.Image.YCbCrPositioning": "1",
"Exif.Image.YResolution": "72/1",
"Exif.Photo.ColorSpace": "65535",
"Exif.Photo.ComponentsConfiguration": "1 2 3 0",
"Exif.Photo.DateTimeDigitized": "2013:12:08 21:06:10",
"Exif.Photo.ExifVersion": "48 50 51 48",
"Exif.Photo.FlashpixVersion": "48 49 48 48",
}, data.AllTags())

//
// IPTC
//
iptcData := img.GetIptcData()

// Iterate over all IPCT data accessing Key() and String()
{
keyValues := map[string]string{}
for i := iptcData.Iterator(); i.HasNext(); {
d := i.Next()
keyValues[d.Key()] = d.String()
}
assert.Equal(t, keyValues, map[string]string{
"Iptc.Application2.Copyright": "this is the copy, right?",
"Iptc.Application2.CountryName": "Lancre",
"Iptc.Application2.DateCreated": "1848-10-13",
"Iptc.Application2.TimeCreated": "12:49:32+01:00",
})
}

//
// ICC profile
//
iccProfile := img.ICCProfile()
assert.Equal(t,
// 128 bytes header
"\x00\x00\x02\x30"+
"ADBE\x02\x10\x00\x00"+
"mntrRGB XYZ \x07\xcf\x00\x06\x00\x03\x00\x00\x00\x00\x00\x00"+
"acspAPPL\x00\x00\x00\x00"+
"none\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\xf6\xd6\x00\x01\x00\x00\x00\x00\xd3-"+
"ADBE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00"+
// tag table (124 bytes)
// tag count (10)
"\x00\x00\x00\x0a"+
// tag references (4 bytes signature, 4 bytes position from start of profile, 4 bytes length)
"cprt\x00\x00\x00\xfc\x00\x00\x00\x32"+
"desc\x00\x00\x01\x30\x00\x00\x00k"+
"wtpt\x00\x00\x01\x9c\x00\x00\x00\x14"+
"bkpt\x00\x00\x01\xb0\x00\x00\x00\x14"+
"rTRC\x00\x00\x01\xc4\x00\x00\x00\x0e"+
"gTRC\x00\x00\x01\xd4\x00\x00\x00\x0e"+
"bTRC\x00\x00\x01\xe4\x00\x00\x00\x0e"+
"rXYZ\x00\x00\x01\xf4\x00\x00\x00\x14"+
"gXYZ\x00\x00\x02\b\x00\x00\x00\x14"+
"bXYZ\x00\x00\x02\x1c\x00\x00\x00\x14"+
// tagged element data (308 bytes; sum of the length of the ten tags)
"text\x00\x00\x00\x00Copyright 1999 Adobe Systems Incorporated\x00\x00\x00"+
"desc\x00\x00\x00\x00\x00\x00\x00\x11Adobe RGB (1998)"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"XYZ \x00\x00\x00\x00\x00\x00\xf3Q\x00\x01\x00\x00\x00\x01\x16\xcc"+
"XYZ \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
"curv\x00\x00\x00\x00\x00\x00\x00\x01\x023\x00\x00"+
"curv\x00\x00\x00\x00\x00\x00\x00\x01\x023\x00\x00"+
"curv\x00\x00\x00\x00\x00\x00\x00\x01\x023\x00\x00"+
"XYZ \x00\x00\x00\x00\x00\x00\x9c\x18\x00\x00O\xa5\x00\x00\x04\xfc"+
"XYZ \x00\x00\x00\x00\x00\x004\x8d\x00\x00\xa0,\x00\x00\x0f\x95"+
"XYZ \x00\x00\x00\x00\x00\x00&1\x00\x00\x10/\x00\x00\xbe\x9c",
string(iccProfile),
)
assert.Equal(t, map[string]string{
"Iptc.Application2.Copyright": "this is the copy, right?",
"Iptc.Application2.CountryName": "Lancre",
"Iptc.Application2.DateCreated": "2012-10-13",
"Iptc.Application2.TimeCreated": "12:49:32+01:00",
}, iptcData.AllTags())
}

func TestNoMetadata(t *testing.T) {
img, err := goexiv.Open("testdata/stripped_pixel.jpg")
require.NoError(t, err)
err = img.ReadMetadata()
require.NoError(t, err)

// no ICC profile

assert.Nil(t, img.ICCProfile())
}

Expand Down
4 changes: 2 additions & 2 deletions helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ DEFINE_FREE_FUNCTION(exiv2_iptc_data, Exiv2IptcData*);

const char* exiv2_iptc_datum_key(const Exiv2IptcDatum *datum)
{
return datum->datum.key().c_str();
return strdup(datum->datum.key().c_str());
}

const char* exiv2_iptc_datum_to_string(const Exiv2IptcDatum *datum)
Expand Down Expand Up @@ -365,7 +365,7 @@ DEFINE_FREE_FUNCTION(exiv2_exif_data, Exiv2ExifData*);

const char* exiv2_exif_datum_key(const Exiv2ExifDatum *datum)
{
return datum->datum.key().c_str();
return strdup(datum->datum.key().c_str());
}

const char* exiv2_exif_datum_to_string(const Exiv2ExifDatum *datum)
Expand Down
11 changes: 11 additions & 0 deletions iptc.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ func (d *IptcDatum) String() string {
return C.GoString(cstr)
}

// Returns all IPTC tags
func (d *IptcData) AllTags() map[string]string {
keyValues := map[string]string{}
for i := d.Iterator(); i.HasNext(); {
d := i.Next()
keyValues[d.Key()] = d.String()
}

return keyValues
}

// Iterator returns a new IptcDatumIterator to iterate over all IPTC data.
func (d *IptcData) Iterator() *IptcDatumIterator {
return makeIptcDatumIterator(d, C.exiv2_iptc_data_iterator(d.data))
Expand Down

0 comments on commit 6ed2746

Please sign in to comment.