/* LIBGIMP - The GIMP Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * Thumbnail handling according to the Thumbnail Managing Standard. * http://triq.net/~pearl/thumbnail-spec/ * * Copyright (C) 2001-2003 Sven Neumann * Michael Natterer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef G_OS_WIN32 #include #endif #include "libgimpmath/gimpmath.h" #include "gimpthumb-error.h" #include "gimpthumb-types.h" #include "gimpthumb-utils.h" #include "libgimp/libgimp-intl.h" static gint gimp_thumb_size (GimpThumbSize size); static const gchar * gimp_thumb_png_name (const gchar *uri); static void gimp_thumb_exit (void); static gboolean gimp_thumb_initialized = FALSE; static gint thumb_num_sizes = 0; static gint *thumb_sizes = NULL; static gchar *thumb_dir = NULL; static gchar **thumb_subdirs = NULL; static gchar *thumb_fail_subdir = NULL; /** * gimp_thumb_init: * @creator: an ASCII string that identifies the thumbnail creator * @thumb_basedir: an absolute path or %NULL to use the default * * This function initializes the thumbnail system. It must be called * before any other functions from libgimpthumb are used. You may call * it more than once if you want to change the @thumb_basedir but if * you do that, you should make sure that no thread is still using the * library. Apart from this function, libgimpthumb is multi-thread * safe. * * The @creator string must be 7bit ASCII and should contain the name * of the software that creates the thumbnails. It is used to handle * thumbnail creation failures. See the spec for more details. * * Usually you will pass %NULL for @thumb_basedir. Thumbnails will * then be stored in the user's personal thumbnail directory as * defined in the spec. If you wish to use libgimpthumb to store * application-specific thumbnails, you can specify a different base * directory here. * * Return value: %TRUE if the library was successfully initialized. **/ gboolean gimp_thumb_init (const gchar *creator, const gchar *thumb_basedir) { GEnumClass *enum_class; GEnumValue *enum_value; gint i; g_return_val_if_fail (creator != NULL, FALSE); g_return_val_if_fail (thumb_basedir == NULL || g_path_is_absolute (thumb_basedir), FALSE); if (gimp_thumb_initialized) gimp_thumb_exit (); thumb_dir = (thumb_basedir ? g_strdup (thumb_basedir) : g_build_filename (g_get_home_dir(), ".thumbnails", NULL)); enum_class = g_type_class_ref (GIMP_TYPE_THUMB_SIZE); thumb_num_sizes = enum_class->n_values; thumb_sizes = g_new (gint, thumb_num_sizes); thumb_subdirs = g_new (gchar *, thumb_num_sizes); for (i = 0, enum_value = enum_class->values; i < enum_class->n_values; i++, enum_value++) { thumb_sizes[i] = enum_value->value; thumb_subdirs[i] = g_build_filename (thumb_dir, enum_value->value_nick, NULL); } thumb_fail_subdir = thumb_subdirs[0]; thumb_subdirs[0] = g_build_filename (thumb_fail_subdir, creator, NULL); gimp_thumb_initialized = TRUE; return gimp_thumb_initialized; } /** * gimp_thumb_get_thumb_dir: * @size: a GimpThumbSize * * Retrieve the name of a thumbnail folder for a specific size. The * returned pointer will become invalid if gimp_thumb_init() is used * again. It must not be changed or freed. * * Return value: the thumbnail directory in the encoding of the filesystem **/ const gchar * gimp_thumb_get_thumb_dir (GimpThumbSize size) { g_return_val_if_fail (gimp_thumb_initialized, FALSE); size = gimp_thumb_size (size); return thumb_subdirs[size]; } /** * gimp_thumb_ensure_thumb_dir: * @size: a GimpThumbSize * @error: return location for possible errors * * This function checks if the directory that is required to store * thumbnails for a particular @size exist and attempts to create it * if necessary. * * You shouldn't have to call this function directly since * gimp_thumbnail_save_thumb() and gimp_thumbnail_save_failure() will * do this for you. * * Return value: %TRUE is the directory exists, %FALSE if it could not * be created **/ gboolean gimp_thumb_ensure_thumb_dir (GimpThumbSize size, GError **error) { gint i; g_return_val_if_fail (gimp_thumb_initialized, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); i = gimp_thumb_size (size); if (g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR)) return TRUE; if (g_file_test (thumb_dir, G_FILE_TEST_IS_DIR) || (mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0)) { if (i == 0) mkdir (thumb_fail_subdir, S_IRUSR | S_IWUSR | S_IXUSR); mkdir (thumb_subdirs[i], S_IRUSR | S_IWUSR | S_IXUSR); } if (g_file_test (thumb_subdirs[i], G_FILE_TEST_IS_DIR)) return TRUE; g_set_error (error, GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR, _("Failed to create thumbnail folder '%s'."), thumb_subdirs[i]); return FALSE; } /** * gimp_thumb_name_from_uri: * @uri: an escaped URI in UTF-8 encoding * @size: a #GimpThumbSize * * Creates the name of the thumbnail file of the specified @size that * belongs to an image file located at the given @uri. * * Return value: a newly allocated filename in the encoding of the * filesystem or %NULL if you attempt to create thumbnails * for files in the thumbnail directory. **/ gchar * gimp_thumb_name_from_uri (const gchar *uri, GimpThumbSize size) { gint i; g_return_val_if_fail (gimp_thumb_initialized, NULL); g_return_val_if_fail (uri != NULL, NULL); if (strstr (uri, thumb_dir)) return NULL; i = gimp_thumb_size (size); return g_build_filename (thumb_subdirs[i], gimp_thumb_png_name (uri), NULL); } /** * gimp_thumb_find_thumb: * @uri: an escaped URI in UTF-8 encoding * @size: pointer to a #GimpThumbSize * * This function attempts to locate a thumbnail for the given * @url. First it tries the size that is stored at @size. If no * thumbnail of that size is found, it will look for a larger * thumbnail, then falling back to a smaller size. If a thumbnail is * found, it's size is written to the variable pointer to by @size * and the file location is returned. * * Return value: a newly allocated string in the encoding of the * filesystem or %NULL if no thumbnail for @uri was found **/ gchar * gimp_thumb_find_thumb (const gchar *uri, GimpThumbSize *size) { const gchar *name; gchar *thumb_name; gint i, n; g_return_val_if_fail (gimp_thumb_initialized, NULL); g_return_val_if_fail (uri != NULL, NULL); g_return_val_if_fail (size != NULL, NULL); g_return_val_if_fail (*size > GIMP_THUMB_SIZE_FAIL, NULL); name = gimp_thumb_png_name (uri); i = n = gimp_thumb_size (*size); for (; i < thumb_num_sizes; i++) { thumb_name = g_build_filename (thumb_subdirs[i], name, NULL); if (gimp_thumb_file_test (thumb_name, NULL, NULL, NULL) == GIMP_THUMB_FILE_TYPE_REGULAR) { *size = thumb_sizes[i]; return thumb_name; } g_free (thumb_name); } for (i = n - 1; i >= 0; i--) { thumb_name = g_build_filename (thumb_subdirs[i], name, NULL); if (gimp_thumb_file_test (thumb_name, NULL, NULL, NULL) == GIMP_THUMB_FILE_TYPE_REGULAR) { *size = thumb_sizes[i]; return thumb_name; } g_free (thumb_name); } return NULL; } /** * gimp_thumb_file_test: * @filename: a filename in the encoding of the filesystem * @mtime: return location for modification time * @size: return location for file size * @err_no: return location for system "errno" * * This is a convenience and portability wrapper around stat(). It * checks if the given @filename exists and returns modification time * and file size in 64bit integer values. * * Return value: The type of the file, or #GIMP_THUMB_FILE_TYPE_NONE if * the file doesn't exist. **/ GimpThumbFileType gimp_thumb_file_test (const gchar *filename, gint64 *mtime, gint64 *size, gint *err_no) { struct stat s; g_return_val_if_fail (filename != NULL, FALSE); if (stat (filename, &s) == 0) { if (mtime) *mtime = s.st_mtime; if (size) *size = s.st_size; if (err_no) *err_no = 0; if (S_ISREG (s.st_mode)) { return GIMP_THUMB_FILE_TYPE_REGULAR; } else if (S_ISDIR (s.st_mode)) { return GIMP_THUMB_FILE_TYPE_FOLDER; } return GIMP_THUMB_FILE_TYPE_SPECIAL; } if (mtime) *mtime = 0; if (size) *size = 0; if (err_no) *err_no = errno; return GIMP_THUMB_FILE_TYPE_NONE; } static void gimp_thumb_exit (void) { gint i; g_free (thumb_dir); g_free (thumb_sizes); for (i = 0; i < thumb_num_sizes; i++) g_free (thumb_subdirs[i]); g_free (thumb_subdirs); g_free (thumb_fail_subdir); thumb_num_sizes = 0; thumb_sizes = NULL; thumb_dir = NULL; thumb_subdirs = NULL; thumb_fail_subdir = NULL; gimp_thumb_initialized = FALSE; } static gint gimp_thumb_size (GimpThumbSize size) { gint i = 0; if (size > GIMP_THUMB_SIZE_FAIL) { for (i = 1; i < thumb_num_sizes && thumb_sizes[i] < size; i++) /* nothing */; if (i == thumb_num_sizes) i--; } return i; } static const gchar * gimp_thumb_png_name (const gchar *uri) { static gchar name[40]; guchar digest[16]; guchar n; gint i; gimp_md5_get_digest (uri, -1, digest); for (i = 0; i < 16; i++) { n = (digest[i] >> 4) & 0xF; name[i * 2] = (n > 9) ? 'a' + n - 10 : '0' + n; n = digest[i] & 0xF; name[i * 2 + 1] = (n > 9) ? 'a' + n - 10 : '0' + n; } strncpy (name + 32, ".png", 5); return (const gchar *) name; }