54#include <glib/gstdio.h>
63#if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE \
64 || defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__ \
65 || defined __NetBSD__ || defined __OpenBSD__
79#include <glib/gi18n.h>
88#include <librsvg/rsvg.h>
91#include <librsvg/rsvg-cairo.h>
96 return str ? strlen(str) : 0;
103 va_start(args, format);
104 const size_t clen = str ? strlen(str) : 0;
105 const int alen = g_vsnprintf(NULL, 0, format, args);
106 const int nsize = alen + clen + 1;
109 ns = g_realloc(str, nsize);
114 va_start(args, format);
115 g_vsnprintf(ns + clen, alen + 1, format, args);
118 ns[nsize - 1] =
'\0';
126 if(haystack && needle)
128 const gchar *
p = haystack;
129 if((
p = g_strstr_len(
p, strlen(
p), needle)) != NULL)
134 }
while((
p = g_strstr_len((
p + 1), strlen(
p + 1), needle)) != NULL);
143 gchar *nstring = NULL;
147 nstring = g_malloc_n(strlen(
string) + (occurrences * strlen(substitute)) + 1,
sizeof(gchar));
148 const gchar *pend =
string + strlen(
string);
149 const gchar *s = string, *
p = string;
151 if((s = g_strstr_len(s, strlen(s), pattern)) != NULL)
155 memcpy(np,
p, s -
p);
157 memcpy(np, substitute, strlen(substitute));
158 np += strlen(substitute);
159 p = s + strlen(pattern);
160 }
while((s = g_strstr_len((s + 1), strlen(s + 1), pattern)) != NULL);
162 memcpy(np,
p, pend -
p);
166 nstring = g_strdup(
string);
174 const unsigned int count = g_list_length(
items);
175 gchar *result = NULL;
178 gchar **strings = g_malloc0_n(count + 1,
sizeof(gchar *));
184 strings[
i++] =
items->data;
189 result = g_strjoinv(separator, strings);
202 GList *last_item = NULL;
204 items = g_list_sort(
items, (GCompareFunc)g_strcmp0);
208 gchar *
value = (gchar *)iter->data;
209 if(!g_strcmp0(last,
value))
220 iter = g_list_next(iter);
238 const size_t len = strlen(path);
249 if(len > 1 && path[1] !=
'/')
251 while(path[off] !=
'\0' && path[off] !=
'/')
256 user = g_strndup(path + 1, off - 1);
264 return g_strdup(path);
267 rpath = g_build_filename(home_path, path + off, NULL);
272 rpath = g_strdup(path);
295 register const gchar *s = src;
296 while(s - src <
n && *s)
298 s = g_utf8_next_char(s);
304 s = g_utf8_prev_char(s);
305 strncpy(dest, src, s - src);
306 dest[s - src] =
'\0';
310 s = g_utf8_next_char(s);
316 strncpy(dest, src, s - src);
317 dest[s - src] =
'\0';
324 if(g_access(filename, R_OK))
return FALSE;
326 struct _stati64 stats;
333 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
334 const int result = _wstati64(wfilename, &stats);
336 if(result)
return FALSE;
339 if(stat(filename, &stats))
return FALSE;
342 const gboolean regular = (S_ISREG(stats.st_mode)) != 0;
343 const gboolean size_ok = stats.st_size > 0;
345 return regular && size_ok;
352 struct _stati64 stats;
354 wchar_t *wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
355 const int result = _wstati64(wpath, &stats);
364 if(stat(path, &stats))
return FALSE;
366 if(S_ISDIR(stats.st_mode) == 0)
return FALSE;
367 if(g_access(path, W_OK | X_OK) != 0)
return FALSE;
376 return g_file_test(dir, G_FILE_TEST_IS_DIR);
382 GDir *dir = g_dir_open(dirname, 0, NULL);
385 while(g_dir_read_name(dir) != NULL)
400 if(g_utf8_validate(
string, -1, NULL))
401 tag = g_strdup(
string);
403 tag = g_convert(
string, -1,
"UTF-8",
"LATIN1", NULL, NULL, NULL);
407 tag = g_strdup(
string);
411 if((*c < 0x20) || (*c >= 0x7f)) *c =
'?';
421 const int a = Y % 19;
422 const int b = Y / 100;
423 const int c = Y % 100;
426 const int f = (b + 8) / 25;
427 const int g = (b -
f + 1) / 3;
428 const int h = (19*a + b -
d -
g + 15) % 30;
431 const int L = (32 + 2*e + 2*
i - h -
k) % 7;
432 const int m = (a + 11*h + 22*
L) / 451;
433 *month = (h +
L - 7*
m + 114) / 31;
434 *
day = ((h +
L - 7*
m + 114) % 31) + 1;
443 localtime_r(&now, <);
446 if((lt.tm_mon == 9 && lt.tm_mday == 31) || (lt.tm_mon == 10 && lt.tm_mday == 1))
454 struct tm easter_sunday = lt;
455 easter(lt.tm_year+1900, &easter_sunday.tm_mon, &easter_sunday.tm_mday);
456 easter_sunday.tm_mon--;
457 easter_sunday.tm_hour = easter_sunday.tm_min = easter_sunday.tm_sec = 0;
458 easter_sunday.tm_isdst = -1;
459 time_t easter_sunday_sec = mktime(&easter_sunday);
469 GError *
error = NULL;
470 cairo_surface_t *surface = NULL;
474 char *dtlogo = g_build_filename(datadir,
"pixmaps", logo, NULL);
475 RsvgHandle *svg = rsvg_handle_new_from_file(dtlogo, &
error);
487 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, final_width);
489 guint8 *image_buffer = (guint8 *)calloc(stride * final_height,
sizeof(guint8));
494 final_height, stride);
495 if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
497 fprintf(stderr,
"warning: can't load darktable logo from SVG file `%s'\n", dtlogo);
498 cairo_surface_destroy(surface);
504 cairo_t *cr = cairo_create(surface);
508 cairo_surface_flush(surface);
514 fprintf(stderr,
"warning: can't load darktable logo from SVG file `%s'\n%s\n", dtlogo,
error->message);
529 logo = g_strdup_printf(
"idbutton-%d.svg", (
int)season);
531 logo = g_strdup(
"idbutton.svg");
545#define OSD_COORDINATES_CHR_N "N"
546#define OSD_COORDINATES_CHR_S "S"
547#define OSD_COORDINATES_CHR_E "E"
548#define OSD_COORDINATES_CHR_W "W"
557 float integral, fractional;
559 if(isnan(latitude))
return NULL;
563 latitude = fabsf(latitude);
567 fractional = modff(latitude, &integral);
569 return g_strdup_printf(
"%s %02d\302\260 %06.3f'", c, (
int)integral, fractional*60.0);
575 float integral, fractional;
577 if(isnan(longitude))
return NULL;
581 longitude = fabsf(longitude);
585 fractional = modff(longitude, &integral);
587 return g_strdup_printf(
"%s %03d\302\260 %06.3f'", c, (
int)integral, fractional*60.0);
594 if(isnan(elevation))
return NULL;
598 elevation = fabsf(elevation);
602 return g_strdup_printf(
"%.2f %s %s", elevation, _(
"m"), _(c));
612 gchar dir = toupper(input[strlen(input) - 1]);
613 gchar **list = g_strsplit(input,
",", 0);
617 res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtod(list[1], NULL) / 60.0);
618 else if(list[3] == NULL)
619 res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtoll(list[1], NULL, 10) / 60.0)
620 + (g_ascii_strtoll(list[2], NULL, 10) / 3600.0);
621 if(dir ==
'S' || dir ==
'W')
res *= -1.0;
628 const double r1_2,
const double r2_1,
const double r2_2,
char sign,
634 double num, den,
min, sec;
637 if(den == 0)
return FALSE;
642 if(den == 0)
return FALSE;
657 if(sec != -1.0)
res += sec / 3600.0;
670 const double num = r_1;
671 const double den = r_2;
672 if(den == 0)
return FALSE;
687 if(g_utf8_validate(_input, -1, NULL))
688 input = g_strdup(_input);
691 input = g_locale_to_utf8(_input, -1, NULL, NULL, NULL);
695 const gchar *input = _input;
698 gchar *filename = g_filename_from_uri(input, NULL, NULL);
702 if(g_str_has_prefix(input,
"file://"))
705 input += strlen(
"file://");
706 filename = g_uri_unescape_string(input, NULL);
709 filename = g_strdup(input);
716 if(g_path_is_absolute(filename) ==
FALSE)
718 char *current_dir = g_get_current_dir();
719 char *tmp_filename = g_build_filename(current_dir, filename, NULL);
741 GFile *gfile = g_file_new_for_path(filename);
745 filename = g_file_get_path(gfile);
746 g_object_unref(gfile);
750 const char first = g_ascii_toupper(filename[0]);
751 if(first >=
'A' && first <=
'Z' && filename[1] ==
':')
756 else if(first ==
'\\' && filename[1] ==
'\\')
770const gboolean dt_util_path_is_UNC(
const gchar *filename)
772 return filename[0] == G_DIR_SEPARATOR && filename[1] == G_DIR_SEPARATOR;
779 gchar *dirname = g_path_get_dirname(filename);
784 int last = strlen(dirname) - 1;
785 if(G_IS_DIR_SEPARATOR(dirname[last]))
786 dirname[last] =
'\0';
796 GFile *file = g_file_new_for_path(path);
797 GError *
error = NULL;
798 GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_NAME
"," G_FILE_ATTRIBUTE_TIME_MODIFIED,
799 G_FILE_QUERY_INFO_NONE, NULL, &
error);
803 g_object_unref(file);
807 const guint64 datetime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
808 g_object_unref(file);
809 g_object_unref(info);
810 return g_date_time_new_from_unix_local(datetime);
819 if(text[0] == needle) count ++;
827 const struct lconv *currentLocalConv = localeconv();
828 const gchar loc_decimal_point = currentLocalConv->decimal_point[0];
829 const gchar *en_decimal_point =
".";
830 g_strdelimit(data, en_decimal_point, loc_decimal_point);
838 gchar *entry = g_strdup(text);
840 int len = strlen(prev);
843 gchar *next = g_strstr_len(prev, -1, separator);
846 const gchar c = next[0];
848 item = g_strdup(prev);
850 prev = next + strlen(separator);
852 list = g_list_prepend(list, item);
853 if(!len) list = g_list_prepend(list, g_strdup(
""));
857 item = g_strdup(prev);
859 list = g_list_prepend(list, item);
862 list = g_list_reverse(list);
871 if(exposuretime >= 1.0f)
873 if(nearbyintf(exposuretime) == exposuretime)
874 result = g_strdup_printf(
"%.0f\"", exposuretime);
876 result = g_strdup_printf(
"%.1f\"", exposuretime);
879 else if(exposuretime < 0.29f)
880 result = g_strdup_printf(
"1/%.0f", 1.0 / exposuretime);
883 else if(nearbyintf(1.0f / exposuretime) == 1.0f / exposuretime)
884 result = g_strdup_printf(
"1/%.0f", 1.0 / exposuretime);
887 else if(10 * nearbyintf(10.0f / exposuretime) == nearbyintf(100.0f / exposuretime))
888 result = g_strdup_printf(
"1/%.1f", 1.0 / exposuretime);
891 result = g_strdup_printf(
"%.1f\"", exposuretime);
898 if (filesize) *filesize = 0;
899 FILE *fd = g_fopen(filename,
"rb");
902 fseek(fd, 0, SEEK_END);
903 const size_t end = ftell(fd);
906 char *content = (
char *)malloc(
sizeof(
char) * end);
909 const size_t count = fread(content,
sizeof(
char), end, fd);
913 if (filesize) *filesize = end;
933 FILE *fin = g_fopen(sourcefile,
"rb");
934 FILE *fout = g_fopen(dst,
"wb");
938 char buffer[64 * 1024];
939 size_t bytes_read = fread(buffer,
sizeof(
char),
sizeof(buffer), fin);
941 while(bytes_read > 0)
943 if(fwrite(buffer,
sizeof(
char), bytes_read, fout) != bytes_read)
break;
944 bytes_read = fread(buffer,
sizeof(
char),
sizeof(buffer), fin);
956 gchar *sourcefile = g_build_filename(share, src, NULL);
965 #if LIBRSVG_CHECK_VERSION(2,52,0)
968 if(rsvg_handle_get_intrinsic_size_in_pixels(svg, &
width, &
height))
975#define VIEWPORT_SIZE 32767
976 const RsvgRectangle viewport = {
979 .width = VIEWPORT_SIZE,
980 .height = VIEWPORT_SIZE,
983 RsvgRectangle rectangle;
984 rsvg_handle_get_geometry_for_layer(svg, NULL, &viewport, NULL, &rectangle, NULL);
985 dimension.width = lround(rectangle.width);
986 dimension.height = lround(rectangle.height);
989 rsvg_handle_get_dimensions(svg, &
dimension);
997 #if LIBRSVG_CHECK_VERSION(2,52,0)
998 RsvgRectangle viewport = {
1004 rsvg_handle_render_document(svg, cr, &viewport, NULL);
1006 rsvg_handle_render_cairo(svg, cr);
1014 if(!filename1 || !filename2)
return FALSE;
1015 const char *dot1 = strrchr(filename1,
'.');
1017 const char *dot2 = strrchr(filename2,
'.');
1019 const int length1 = dot1 - filename1;
1020 const int length2 = dot2 - filename2;
1021 if(length1 != length2)
1023 for(
int i = length1 - 1;
i > 0;
i--)
1024 if(filename1[
i] != filename2[
i])
1033 if(!filename1 || !filename2)
return NULL;
1034 const char *dot1 = strrchr(filename1,
'.');
1036 const char *dot2 = strrchr(filename2,
'.');
1038 const int name_lgth = dot1 - filename1;
1039 const int ext_lgth = strlen(dot2);
1040 char *output = g_malloc(name_lgth + ext_lgth + 1);
1043 memcpy(output, filename1, name_lgth);
1044 memcpy(&output[name_lgth], &filename2[strlen(filename2) - ext_lgth], ext_lgth + 1);
1052 gchar **split = g_strsplit(
string, search, -1);
1053 gchar *
res = g_strjoinv(replace, split);
1072 gchar **split = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
1073 for(
int i = 0;
i < g_strv_length(split);
i++)
1074 g_strstrip(split[
i]);
1076 char* result = g_strjoinv(G_DIR_SEPARATOR_S, split);
static void error(char *msg)
static const float const float const float min
void dt_loc_get_datadir(char *datadir, size_t bufsize)
gchar * dt_loc_get_home_dir(const gchar *user)
const dt_collection_sort_t items[]
static gchar * g_realpath(const char *path)
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
double dt_screen_ppd(void)
static cairo_surface_t * dt_cairo_image_surface_create_for_data(unsigned char *data, cairo_format_t format, int width, int height, int stride)
static const dt_aligned_pixel_simd_t sign
static const dt_aligned_pixel_simd_t value
static const char *const day[7]
gboolean dt_util_gps_elevation_to_number(const double r_1, const double r_2, char sign, double *result)
guint dt_util_string_count_char(const char *text, const char needle)
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
gchar * dt_util_longitude_str(float longitude)
gchar * dt_str_replace(const char *string, const char *search, const char *replace)
gboolean dt_has_same_path_basename(const char *filename1, const char *filename2)
void dt_util_str_to_loc_numbers_format(char *data)
#define OSD_COORDINATES_CHR_N
gchar * dt_cleanup_separators(gchar *string)
gchar * dt_util_elevation_str(float elevation)
gboolean dt_util_dir_exist(const char *dir)
dt_logo_season_t dt_util_get_logo_season(void)
RsvgDimensionData dt_get_svg_dimension(RsvgHandle *svg)
char * dt_read_file(const char *const filename, size_t *filesize)
static const char * OSD_ELEVATION_BSL
#define OSD_COORDINATES_CHR_S
cairo_surface_t * dt_util_get_logo_text(const float size)
static void easter(int Y, int *month, int *day)
GDateTime * dt_util_get_file_datetime(const char *const path)
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
gboolean dt_util_test_image_file(const char *filename)
#define OSD_COORDINATES_CHR_W
void dt_copy_file(const char *const sourcefile, const char *dst)
void dt_copy_resource_file(const char *src, const char *dst)
guint dt_util_str_occurence(const gchar *haystack, const gchar *needle)
double dt_util_gps_string_to_number(const gchar *input)
gchar * dt_util_path_get_dirname(const gchar *filename)
gboolean dt_util_is_dir_empty(const char *dirname)
#define OSD_COORDINATES_CHR_E
gchar * dt_util_normalize_path(const gchar *_input)
size_t dt_utf8_strlcpy(char *dest, const char *src, size_t n)
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
char * dt_util_format_exposure(const float exposuretime)
gboolean dt_util_test_writable_dir(const char *path)
gboolean dt_util_gps_rationale_to_number(const double r0_1, const double r0_2, const double r1_1, const double r1_2, const double r2_1, const double r2_2, char sign, double *result)
gchar * dt_util_fix_path(const gchar *path)
gchar * dt_util_foo_to_utf8(const char *string)
gchar * dt_util_latitude_str(float latitude)
gchar * dt_util_remove_whitespace(const gchar *path)
void dt_render_svg(RsvgHandle *svg, cairo_t *cr, double width, double height, double offset_x, double offset_y)
static const char * OSD_ELEVATION_ASL
gchar * dt_util_glist_to_str(const gchar *separator, GList *items)
char * dt_copy_filename_extension(const char *filename1, const char *filename2)
static cairo_surface_t * _util_get_svg_img(gchar *logo, const float size)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
GList * dt_util_glist_uniq(GList *items)
cairo_surface_t * dt_util_get_logo(const float size)
@ DT_LOGO_SEASON_HALLOWEEN