diff options
Diffstat (limited to 'src/info.c')
| -rw-r--r-- | src/info.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/info.c b/src/info.c new file mode 100644 index 0000000..664ea64 --- /dev/null +++ b/src/info.c @@ -0,0 +1,171 @@ +/*:* + * ggaze — image info / EXIF gather + * + * Gathers file info (via GFileInfo + GdkPixbuf for dims) and EXIF tags (via + * libexif) into a GgazeInfo struct. Plain-C, no GtkWidget. + * + * Copyright (c) 2026 ggaze contributors + * SPDX-License-Identifier: GPL-3.0-or-later + *:*/ + +#include "info.h" + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <libexif/exif-data.h> +#include <libexif/exif-tag.h> + +static char * +_dup_exif_value(ExifData *p_data, ExifTag e_tag) { + if (p_data == NULL) { + return (NULL); + } + ExifEntry *p_entry = exif_data_get_entry(p_data, e_tag); + if (p_entry == NULL) { + return (NULL); + } + char c_buf[256]; + exif_entry_get_value(p_entry, c_buf, sizeof(c_buf)); + if (c_buf[0] == '\0') { + return (NULL); + } + return (g_strdup(c_buf)); +} + +static char * +_dup_camera(ExifData *p_data) { + char *c_make = _dup_exif_value(p_data, EXIF_TAG_MAKE); + char *c_model = _dup_exif_value(p_data, EXIF_TAG_MODEL); + char *c_out = NULL; + if (c_make != NULL && c_model != NULL) { + c_out = g_strdup_printf("%s %s", c_make, c_model); + } else if (c_make != NULL) { + c_out = g_strdup(c_make); + } else if (c_model != NULL) { + c_out = g_strdup(c_model); + } + g_free(c_make); + g_free(c_model); + return (c_out); +} + +static int +_get_orientation(ExifData *p_data) { + if (p_data == NULL) { + return (0); + } + ExifEntry *p_entry = exif_data_get_entry(p_data, EXIF_TAG_ORIENTATION); + if (p_entry == NULL || p_entry->components == 0 || p_entry->data == NULL) { + return (0); + } + /* Orientation is a SHORT (2 bytes, big/little endian per the data's byte + * order). */ + ExifByteOrder e_order = exif_data_get_byte_order(p_data); + return ((int)exif_get_short(p_entry->data, e_order)); +} + +GgazeInfo * +info_new(GFile *p_file) { + g_return_val_if_fail(G_IS_FILE(p_file), NULL); + char *c_path = g_file_get_path(p_file); + if (c_path == NULL) { + return (NULL); + } + + GgazeInfo *p_info = g_new0(GgazeInfo, 1); + + /* File info: size + content type. */ + GError *p_err = NULL; + GFileInfo *p_fi = + g_file_query_info(p_file, "standard::size,standard::content-type", + G_FILE_QUERY_INFO_NONE, NULL, &p_err); + if (p_fi != NULL) { + p_info->i_size = (gint64)g_file_info_get_size(p_fi); + const char *c_ct = g_file_info_get_content_type(p_fi); + if (c_ct != NULL) { + p_info->c_format = g_strdup(c_ct); + } + g_object_unref(p_fi); + } else { + g_clear_error(&p_err); + } + + /* Dimensions via GdkPixbuf (without applying orientation). */ + GdkPixbuf *p_pix = gdk_pixbuf_new_from_file(c_path, &p_err); + if (p_pix != NULL) { + p_info->i_width = gdk_pixbuf_get_width(p_pix); + p_info->i_height = gdk_pixbuf_get_height(p_pix); + g_object_unref(p_pix); + } else { + g_clear_error(&p_err); + } + + /* EXIF via libexif. */ + ExifData *p_exif = exif_data_new_from_file(c_path); + if (p_exif != NULL) { + p_info->c_camera = _dup_camera(p_exif); + p_info->c_focal = _dup_exif_value(p_exif, EXIF_TAG_FOCAL_LENGTH); + p_info->c_aperture = _dup_exif_value(p_exif, EXIF_TAG_FNUMBER); + p_info->c_shutter = _dup_exif_value(p_exif, EXIF_TAG_EXPOSURE_TIME); + p_info->c_iso = _dup_exif_value(p_exif, EXIF_TAG_ISO_SPEED_RATINGS); + p_info->c_datetime = _dup_exif_value(p_exif, EXIF_TAG_DATE_TIME_ORIGINAL); + p_info->i_orientation = _get_orientation(p_exif); + exif_data_unref(p_exif); + } + + g_free(c_path); + return (p_info); +} + +void +info_delete(GgazeInfo *p_info) { + if (p_info == NULL) { + return; + } + g_free(p_info->c_format); + g_free(p_info->c_camera); + g_free(p_info->c_lens); + g_free(p_info->c_focal); + g_free(p_info->c_aperture); + g_free(p_info->c_shutter); + g_free(p_info->c_iso); + g_free(p_info->c_datetime); + g_free(p_info); +} + +static char * +_join(GString *p_str, const char *c_label, const char *c_val) { + if (c_val != NULL && c_val[0] != '\0') { + g_string_append_printf(p_str, "%s: %s\n", c_label, c_val); + } + return (NULL); +} + +char * +info_format(const GgazeInfo *p_info) { + if (p_info == NULL) { + return (g_strdup("")); + } + GString *p_str = g_string_new(NULL); + g_string_append_printf(p_str, "%d×%d\n", p_info->i_width, p_info->i_height); + _join(p_str, "Format", p_info->c_format); + if (p_info->i_size > 0) { + char *c_sz = g_format_size(p_info->i_size); + g_string_append_printf(p_str, "Size: %s\n", c_sz); + g_free(c_sz); + } + _join(p_str, "Camera", p_info->c_camera); + _join(p_str, "Lens", p_info->c_lens); + _join(p_str, "Focal", p_info->c_focal); + _join(p_str, "Aperture", p_info->c_aperture); + _join(p_str, "Shutter", p_info->c_shutter); + _join(p_str, "ISO", p_info->c_iso); + _join(p_str, "Date", p_info->c_datetime); + if (p_info->i_orientation > 0) { + g_string_append_printf(p_str, "Orientation: %d\n", p_info->i_orientation); + } + /* trim trailing newline */ + if (p_str->len > 0 && p_str->str[p_str->len - 1] == '\n') { + g_string_truncate(p_str, p_str->len - 1); + } + return (g_string_free(p_str, FALSE)); +}
\ No newline at end of file |
