Skip to content
Commits on Source (21)
.libs/
build
build-am
gexiv2/.libs
gexiv2/.dirstamp
gexiv2/gexiv2-version.h
/gexiv2.pc
*.lo
*.o
*.la
/configure.mk
/GExiv2-*.*.gir
/GExiv2-*.*.typelib
vapi/gexiv2.gi
test/gexiv2-dump
test/python/gi
*.pyc
__pycache__
.flatpak-builder
_build
*.user
.vscode
# Autotools files.
Makefile.in
Makefile
configure
build-aux/
config.log
config.status
libtool
.deps/
aclocal.m4
autom4te.cache/
m4/
test/*.log
test/*.trs
test-suite.log
......@@ -3,13 +3,13 @@
- flatpak
variables:
MANIFEST_PATH: "build-aux/org.gnome.GExiv2.json"
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:40
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:44
stage: build
only:
- schedules
script:
- flatpak-builder build-dir ${MANIFEST_PATH} --stop-at=gexiv2 --user --disable-rofiles-fuse
- flatpak build build-dir meson _build
- flatpak build build-dir meson _build -Dtests=true
- flatpak build build-dir ninja -C _build
- flatpak build build-dir ninja -C _build test
artifacts:
......@@ -87,7 +87,7 @@ check-merge-request:
.build-template: &build
stage: build
script:
- meson . build --prefix=/usr -Db_coverage=true --werror
- meson . build --prefix=/usr -Db_coverage=true -Dtests=true --werror
- ninja -C build
artifacts:
expire_in: 1 day
......@@ -208,6 +208,7 @@ pages:
stage: website
script:
- meson doc-build -Dgtk_doc=true
- ninja -C doc-build
- ninja -C doc-build gexiv2-doc
- mkdir -p public
- mv doc-build/docs/reference/html public/docs
......
......@@ -7,6 +7,7 @@
$ meson build && cd build
$ ninja
$ ninja test
$ ninja install
* By default, gexiv2 will install under /usr/local. The configure script
can customize the prefix directory. Run 'meson setup --help' for
......
gexiv2 0.14.1 - 5 May 2023 (stable)
* Clean-up python support
* Drop python2 support
* Add option for building tests
* Only run python tests when python is enabled
* Fix get_gps_info() return data with unset altitude
* Fix generate_xmp_packet() ignoring its parameters
* Fix gexiv2-tool to call initialize()
Bugs fixed in this release:
- Addresses https://gitlab.gnome.org/GNOME/gexiv2/-/issues/72
- https://gitlab.gnome.org/GNOME/gexiv2/issues/69
- https://gitlab.gnome.org/GNOME/gexiv2/issues/70
All contributors to this release:
- Peter Eisenmann <p3732@getgoogleoff.me>
- Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
- Felix Crux <felixc@felixcrux.com>
- postscript-dev <43813-postscript-dev@users.noreply.gitlab.gnome.org>
- Seth Falco <seth@falco.fun>
- Jens Georg <mail@jensge.org>
gexiv2 0.14.0 - 18 Sep 2021 (stable)
* Fix BMFF compile test
......
{
"app-id" : "org.gnome.GExiv2",
"runtime" : "org.gnome.Platform",
"runtime-version" : "40",
"runtime-version" : "44",
"sdk" : "org.gnome.Sdk",
"command" : "gexiv2",
"tags" : [
......
{
"app-id" : "org.gnome.GExiv2",
"runtime" : "org.gnome.Platform",
"runtime-version" : "40",
"runtime-version" : "44",
"sdk" : "org.gnome.Sdk",
"command" : "gexiv2",
"tags" : [
......
{
"app-id" : "org.gnome.GExiv2",
"runtime" : "org.gnome.Platform",
"runtime-version" : "40",
"runtime-version" : "44",
"sdk" : "org.gnome.Sdk",
"command" : "gexiv2",
"tags" : [
......@@ -20,6 +20,18 @@
"*.a"
],
"modules" : [
{
"name" : "inih",
"buildsystem" : "meson",
"sources" : [
{
"type": "git",
"url" : "https://github.com/benhoyt/inih",
"tag" : "r56",
"commit" : "5e1d9e2625842dddb3f9c086a50f22e4f45dfc2b"
}
]
},
{
"name" : "exiv2",
"cleanup" : [
......
......@@ -245,21 +245,24 @@ gboolean gexiv2_metadata_get_gps_altitude (GExiv2Metadata *self, gdouble *altitu
gboolean gexiv2_metadata_try_get_gps_info (GExiv2Metadata *self, gdouble *longitude, gdouble *latitude,
gdouble *altitude, GError **error) {
gboolean result = TRUE;
gboolean result = FALSE;
if (!gexiv2_metadata_try_get_gps_longitude (self, longitude, error)) {
*longitude = 0.0;
result = FALSE;
} else {
result = TRUE;
}
if (result && !gexiv2_metadata_try_get_gps_latitude (self, latitude, error)) {
if (!gexiv2_metadata_try_get_gps_latitude (self, latitude, error)) {
*latitude = 0.0;
result = FALSE;
} else {
result = TRUE;
}
if (result && !gexiv2_metadata_try_get_gps_altitude (self, altitude, error)) {
if (!gexiv2_metadata_try_get_gps_altitude (self, altitude, error)) {
*altitude = 0.0;
result = FALSE;
} else {
result = TRUE;
}
return result;
......
......@@ -288,6 +288,9 @@ gboolean gexiv2_metadata_set_iptc_tag_multiple (GExiv2Metadata *self, const gcha
g_return_val_if_fail(self->priv->image.get() != nullptr, FALSE);
g_return_val_if_fail(error == nullptr || *error == nullptr, FALSE);
if (values[0] == nullptr)
return TRUE;
try {
auto& iptc_data = self->priv->image->iptcData();
......
......@@ -39,16 +39,19 @@ gchar *gexiv2_metadata_try_generate_xmp_packet(GExiv2Metadata *self,
g_return_val_if_fail(GEXIV2_IS_METADATA (self), NULL);
g_return_val_if_fail(self->priv->image.get() != NULL, NULL);
g_return_val_if_fail(error == nullptr || *error == nullptr, nullptr);
Exiv2::XmpData &xmp_data = self->priv->image->xmpData();
auto const& xmp_data = self->priv->image->xmpData();
try {
if (Exiv2::XmpParser::encode(self->priv->image->xmpPacket(), xmp_data, xmp_format_flags, padding) == 0)
return g_strdup(self->priv->image->xmpPacket().c_str());
std::string packet;
if (Exiv2::XmpParser::encode(packet, xmp_data, xmp_format_flags, padding) == 0) {
return g_strdup(packet.c_str());
}
} catch (Exiv2::Error& e) {
g_set_error_literal(error, g_quark_from_string("GExiv2"), e.code(), e.what());
}
return NULL;
return nullptr;
}
gchar *gexiv2_metadata_generate_xmp_packet(GExiv2Metadata *self,
......
project(
'gexiv2',
['c', 'cpp'],
version : '0.14.0',
version : '0.14.1',
license: 'GPL-2.0-or-later',
meson_version : '>=0.51',
default_options : [
'cpp_std=c++11'
'cpp_std=c++17'
]
)
......@@ -23,7 +23,6 @@ gio = dependency('gio-2.0', version : '>= 2.46.0')
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
math = cc.find_library('m', required : false)
python3 = import('python').find_installation('python3', modules: 'gi', required: get_option('python3'))
bmff_test = '''#include <exiv2/exiv2.hpp>
#ifndef EXV_ENABLE_BMFF
......@@ -48,27 +47,19 @@ if get_option('gtk_doc')
subdir('docs')
endif
if get_option('python3') == false
warning('\'python3\' is disabled, any Python tests will not be run.')
else
if get_option('python3')
if get_option('introspection') == false
error('Build option \'python3\' requires \'introspection\' to be enabled.')
endif
girdir = get_option('python3_girdir')
if girdir == 'auto'
python3_output = run_command(python3, ['-c', 'import gi; print(gi._overridesdir)'])
if python3_output.returncode() != 0
error('Finding the Python 3 gi/override path: ' + python3_output.stderr())
endif
girdir = python3_output.stdout().strip()
endif
message('Installing \'GExiv2.py\' into \'' + girdir + '\'')
python3.install_sources('GExiv2.py', subdir : girdir)
python3 = import('python').find_installation('python3', modules: 'gi')
python3.install_sources('GExiv2.py', subdir: 'gi/overrides')
else
python3 = disabler()
endif
subdir('test')
if get_option('tests')
subdir('test')
endif
if get_option('tools')
subdir('tools')
......
option('tests', type: 'boolean', value: false, description: 'Enable or disable building tests')
option('gtk_doc', type: 'boolean', value: false, description: 'Enable or disable generating the API reference (depends on GTK-Doc)')
option('introspection', type: 'boolean', value : true, description: 'Enable or disable GObject Introspection')
option('vapi', type: 'boolean', value: true, description: 'Enable or disable generation of vala vapi file')
option('tools', type: 'boolean', value: true, description: 'Enable or disable building the commandline tools')
option('python3_girdir', type: 'string', value : 'auto', description : 'Installation dir for PyGObject3 overrides (default = auto)')
option('python3', type: 'boolean', value : true, description : 'Enable or disable using Python 3 (and PyGObject module)')
......@@ -478,6 +478,56 @@ static void test_ggo_62(void){
g_clear_object(&meta);
}
// Regression test for https://gitlab.gnome.org/GNOME/gexiv2/issues/70
static void test_ggo_70(void) {
GExiv2Metadata *meta = NULL;
gboolean result = FALSE;
GError *error = NULL;
gchar** values = NULL;
meta = gexiv2_metadata_new();
g_assert_nonnull(meta);
result = gexiv2_metadata_open_path (meta, SAMPLE_PATH "/no-metadata.jpg", &error);
g_assert_no_error(error);
g_assert_true(result);
values = g_new (gchar*, 1);
values[0] = NULL;
result = gexiv2_metadata_try_set_tag_multiple(meta, "Iptc.Application2.Urgency", (const gchar**)values, &error);
g_assert_no_error(error);
g_assert_true(result);
g_free(values);
g_clear_object(&meta);
}
static void test_ggo_69(void) {
GExiv2Metadata* meta = NULL;
gboolean result = FALSE;
char* packet = NULL;
GError* error = NULL;
meta = gexiv2_metadata_new();
g_assert_nonnull(meta);
result = gexiv2_metadata_open_path(meta, SAMPLE_PATH "/no-metadata.jpg", &error);
g_assert_no_error(error);
g_assert_true(result);
result = gexiv2_metadata_try_set_tag_string(meta, "Xmp.dc.description", "some description", &error);
g_assert_no_error(error);
g_assert_true(result);
packet = gexiv2_metadata_try_generate_xmp_packet(meta, GEXIV2_OMIT_PACKET_WRAPPER, 0, &error);
g_assert_nonnull(packet);
g_assert_no_error(error);
g_assert(!g_str_has_prefix(packet, "<?xpacket"));
g_free(packet);
g_object_unref(meta);
}
int main(int argc, char *argv[static argc + 1])
{
g_test_init(&argc, &argv, NULL);
......@@ -494,6 +544,8 @@ int main(int argc, char *argv[static argc + 1])
g_test_add_func("/bugs/gnome/gitlab/58", test_ggo_58);
g_test_add_func("/bugs/gnome/gitlab/62", test_ggo_62);
g_test_add_func("/bugs/gnome/gitlab/60", test_ggo_66);
g_test_add_func("/bugs/gnome/gitlab/69", test_ggo_69);
g_test_add_func("/bugs/gnome/gitlab/70", test_ggo_70);
return g_test_run();
}
test_sample_path = join_paths(meson.current_source_dir(), 'data')
python_module_path = join_paths(meson.current_build_dir(), 'python')
test_env = environment()
test_env.set('G_SLICE', 'always-malloc')
test_env.set('TEST_DATA_DIR', test_sample_path)
test_env.prepend('GI_TYPELIB_PATH', typelib_path)
test_env.prepend('LD_LIBRARY_PATH', typelib_path)
test_env.prepend('PYTHONPATH', python_module_path)
test_env.prepend('PYTHONPATH', join_paths(meson.current_source_dir(), 'python'))
regression_test = executable('gexiv2-regression', 'gexiv2-regression.c',
dependencies : [gobject, gio, math],
......@@ -19,11 +18,7 @@ regression_test = executable('gexiv2-regression', 'gexiv2-regression.c',
test('regression', regression_test, env : test_env)
subdir('python')
python3_test_conf = configuration_data()
python3_test_conf.set('PYTHON3_PATH', python3.path())
python3_test = configure_file(input: 'python3-test.in',
output : 'python3-test',
configuration: python3_test_conf)
test('python3', find_program(python3_test), env : test_env)
if get_option('python3')
test('python3-gexiv2', python3, args: ['-m', 'unittest', 'gexiv2'], env: test_env)
test('python3-metadata', python3, args: ['-m', 'unittest', 'test_metadata'], env: test_env)
endif
......@@ -22,7 +22,8 @@
import unittest
import os
gi.require_version('GExiv2', '@PROJECT_API_VERSION@')
import gi.overrides
gi.overrides.__path__.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from gi.repository import GExiv2
......@@ -51,6 +52,14 @@ class TestGexiv2(unittest.TestCase):
self.assertEqual(la, 48.631806166666664)
self.assertEqual(alt, -0.926000)
def test_xmp_packet_formatting(self):
sample = 'CaorVN.jpeg'
md = GExiv2.Metadata()
md.open_path(self.get_sample_path(sample))
packet = md.try_generate_xmp_packet(GExiv2.XmpFormatFlags.OMIT_PACKET_WRAPPER, 0);
self.assertEqual(packet.startswith("<?xpacket"), False)
if __name__ == '__main__':
unittest.main()
python_conf = configuration_data()
python_conf.set('PROJECT_API_VERSION', project_api_version)
gexiv2_py_file = configure_file(input: 'gexiv2.py.in',
output : 'gexiv2.py',
configuration: python_conf)
test_metadata_py_file = configure_file(input: 'test_metadata.py.in',
output : 'test_metadata.py',
configuration: python_conf)
......@@ -28,10 +28,8 @@ import shutil
import unittest
import tempfile
PY3K = sys.version_info[0] == 3
import gi
gi.require_version('GExiv2', '@PROJECT_API_VERSION@')
import gi.overrides
gi.overrides.__path__.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from gi.repository import GExiv2, GLib
from fractions import Fraction
......@@ -321,7 +319,7 @@ class TestMetadata(unittest.TestCase):
self.assertIn(method, methods)
def test_open_buf(self):
with open(self.get_input_file(), 'rb' if PY3K else 'r') as fd:
with open(self.get_input_file(), 'rb') as fd:
buf = fd.read()
metadata = GExiv2.Metadata()
metadata.open_buf(buf)
......@@ -764,7 +762,7 @@ generated the image. When the field is left blank, it is treated as unknown.""")
self.assertEqual(len(thumb), 36660)
def test_set_exif_thumbnail_from_buffer(self):
with open(self.get_input_file(), 'rb' if PY3K else 'r') as fd:
with open(self.get_input_file(), 'rb') as fd:
buf = fd.read()
self.metadata.set_exif_thumbnail_from_buffer(buf)
thumb = self.metadata.get_exif_thumbnail()
......@@ -773,7 +771,7 @@ generated the image. When the field is left blank, it is treated as unknown.""")
def test_erase_exif_thumbnail(self):
self.metadata.erase_exif_thumbnail()
thumb = self.metadata.get_exif_thumbnail()
self.assertEqual(thumb, b'' if PY3K else '')
self.assertEqual(thumb, b'')
def test_has_xmp(self):
self.assertFalse(self.metadata.has_xmp())
......@@ -895,6 +893,14 @@ generated the image. When the field is left blank, it is treated as unknown.""")
self.assertAlmostEqual(lat, 48.43, 9)
self.assertEqual(alt, 10)
def test_try_get_gps_info_without_altitude(self):
self.metadata.set_gps_info(-1.0, 2.0, 3.0)
self.metadata.clear_tag('Exif.GPSInfo.GPSAltitudeRef')
lon, lat, alt = self.metadata.try_get_gps_info()
self.assertAlmostEqual(lon, -1.0, 9)
self.assertAlmostEqual(lat, 2.0, 9)
self.assertEqual(alt, 0.0)
def test_set_gps_info(self):
# Longitude, latitude, altitude
self.metadata.set_gps_info(-123.35, 48.43, 10)
......
#!/bin/sh
'@PYTHON3_PATH@' -m unittest gexiv2
'@PYTHON3_PATH@' -m unittest test_metadata
......@@ -59,6 +59,8 @@ int main(string[] args) {
var program_name = extract_filename(args[0]);
print_single_tag = "";
GExiv2.initialize();
// Setup and parse program parameters
OptionContext opt_context;
try {
......