diff --git a/vfs-methods/fontilus/.cvsignore b/vfs-methods/fontilus/.cvsignore new file mode 100644 index 000000000..cb758d74f --- /dev/null +++ b/vfs-methods/fontilus/.cvsignore @@ -0,0 +1,9 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +*.so +.libs +.deps +thumbnailer diff --git a/vfs-methods/fontilus/ChangeLog b/vfs-methods/fontilus/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/vfs-methods/fontilus/Makefile.am b/vfs-methods/fontilus/Makefile.am new file mode 100644 index 000000000..8cacdbcf5 --- /dev/null +++ b/vfs-methods/fontilus/Makefile.am @@ -0,0 +1,14 @@ + +INCLUDES = $(FONT_METHOD_CFLAGS) $(THUMBNAILER_CFLAGS) + +moduledir = $(libdir)/gnome-vfs-2.0/modules +module_LTLIBRARIES = libfont-method.la + +noinst_PROGRAMS = thumbnailer + +libfont_method_la_LDFLAGS = -module -avoid-version +libfont_method_la_LIBS = $(FONT_METHOD_LIBS) +libfont_method_la_SOURCES = font-method.c + +thumbnailer_LDADD = $(THUMBNAILER_LIBS) +thumbnailer_SOURCES = ftstream-vfs.c thumbnailer.c diff --git a/vfs-methods/fontilus/font-method.c b/vfs-methods/fontilus/font-method.c new file mode 100644 index 000000000..1b6fe4fce --- /dev/null +++ b/vfs-methods/fontilus/font-method.c @@ -0,0 +1,754 @@ +/* -*- mode: C; c-basic-offset: 4 -*- */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* this is from gnome-vfs-monitor-private.h */ +void gnome_vfs_monitor_callback (GnomeVFSMethodHandle *method_handle, + GnomeVFSURI *info_uri, + GnomeVFSMonitorEventType event_type); + +static void invoke_monitors(void); + +/* -------- code for creating the font list -------- */ + +/* list of fonts in fontconfig database */ +G_LOCK_DEFINE_STATIC(font_list); +static FcFontSet *font_list = NULL; +static gchar **font_names = NULL; +static GHashTable *font_hash = NULL; + +static gchar * +get_pango_name(FcPattern *pat) +{ + FcChar8 *family; + GString *str; + gint i; + + FcPatternGetString(pat, FC_FAMILY, 0, &family); + str = g_string_new(family); + g_string_append_c(str, ','); + + /* add weight word */ + if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &i) == FcResultMatch) { + gchar *weight = NULL; + + if (i < FC_WEIGHT_LIGHT) + weight = " Ultra-Light"; + else if (i < (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2) + weight = " Light"; + else if (i < (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) + weight = NULL; + else if (i < (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) + weight = " Semi-Bold"; + else if (i < (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2) + weight = " Bold"; + else + weight = " Ultra-Bold"; + + if (weight) + g_string_append(str, weight); + } + + /* add slant word */ + if (FcPatternGetInteger(pat, FC_SLANT, 0, &i) == FcResultMatch) { + gchar *style = NULL; + + if (i == FC_SLANT_ROMAN) + style = NULL; + else if (i == FC_SLANT_OBLIQUE) + style = " Oblique"; + else + style = " Italic"; + + if (style) + g_string_append(str, style); + } + + /* if ends in a comma, check to see if the last word matches a modifier. + * if not, remove the comma. */ + if (str->str[str->len-1] == ',') { + const gchar *lastword; + gint wordlen, i; + gboolean word_matches; + const char *modifier_words[] = { + "Oblique", "Italic", "Small-Caps", "Ultra-Light", "Light", + "Medium", "Semi-Bold", "Bold", "Ultra-Bold", "Heavy", + "Ultra-Condensed", "Extra-Condensed", "Condensed", + "Semi-Condensed", "Semi-Expanded", "Expanded", + "Extra-Expanded", "Ultra-Expanded" }; + + lastword = strrchr(str->str, ' '); + if (lastword) + lastword++; + else + lastword = str->str; + wordlen = strlen(lastword) - 1; /* exclude comma */ + + word_matches = FALSE; + for (i = 0; i < G_N_ELEMENTS(modifier_words); i++) { + if (g_ascii_strncasecmp(modifier_words[i], lastword, wordlen)==0) { + word_matches = TRUE; + break; + } + } + + /* if the last word doesn't match, then we can remove the comma */ + if (!word_matches) + g_string_truncate(str, str->len-1); + } + + return g_string_free(str, FALSE); +} + +/* make sure the font_list is valid */ +static gboolean +ensure_font_list(void) +{ + gboolean result = FALSE; + FcPattern *pat; + FcObjectSet *os; + gint i; + + G_LOCK(font_list); + /* if the config exists, and is up to date, return */ + if (font_list != NULL) { + if (FcInitBringUptoDate()) { + result = TRUE; + goto end; + } + + /* otherwise, destroy the fonts list and recreate */ + FcFontSetDestroy(font_list); + font_list = NULL; + g_strfreev(font_names); + font_names = NULL; + g_hash_table_destroy(font_hash); + font_hash = NULL; + } + + pat = FcPatternCreate(); + os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_WEIGHT, FC_SLANT, 0); + + font_list = FcFontList(0, pat, os); + + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + + if (!font_list) { + result = FALSE; + goto end; + } + + /* set up name list and hash */ + font_names = g_new(gchar *, font_list->nfont); + font_hash = g_hash_table_new(g_str_hash, g_str_equal); + for (i = 0; i < font_list->nfont; i++) { + font_names[i] = get_pango_name(font_list->fonts[i]); + g_hash_table_insert(font_hash, font_names[i], font_list->fonts[i]); + } + + result = TRUE; + + /* invoke monitors */ + invoke_monitors(); + + end: + G_UNLOCK(font_list); + return result; +} + +static GnomeVFSURI * +create_local_uri(const GnomeVFSURI *orig_uri) +{ + gchar *fontsdir, *fontsdir_escaped, *basename; + GnomeVFSURI *fontsdir_uri, *new_uri; + + /* make sure ~/.fonts exists ... */ + fontsdir = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S, ".fonts",NULL); + if (mkdir(fontsdir, 0755) && errno != EEXIST) { + g_free(fontsdir); + return NULL; + } + /* get URI for fontsdir */ + fontsdir_escaped = gnome_vfs_get_uri_from_local_path(fontsdir); + g_free(fontsdir); + fontsdir_uri = gnome_vfs_uri_new(fontsdir_escaped); + g_free(fontsdir_escaped); + + basename = gnome_vfs_uri_extract_short_name(orig_uri); + new_uri = gnome_vfs_uri_append_file_name(fontsdir_uri, basename); + g_free(basename); + gnome_vfs_uri_unref(fontsdir_uri); + + return new_uri; +} + +/* -------- VFS method ------ */ + +static gchar * +get_path_from_uri (GnomeVFSURI const *uri) +{ + gchar *path; + gint len; + + path = gnome_vfs_unescape_string (uri->text, + G_DIR_SEPARATOR_S); + + if (path == NULL) { + return NULL; + } + + if (path[0] != G_DIR_SEPARATOR) { + g_free (path); + return NULL; + } + + len = strlen(path); + if (path[len-1] == G_DIR_SEPARATOR) path[len-1] = '\0'; + + return path; +} + +static GnomeVFSResult +fill_file_info(GnomeVFSFileInfo *file_info, GnomeVFSFileInfoOptions options, + FcChar8 *file, gchar *name) +{ + gchar *uri; + GnomeVFSResult result; + + uri = gnome_vfs_get_uri_from_local_path(file); + result = gnome_vfs_get_file_info(uri, file_info, options); + if (result == GNOME_VFS_OK) { + g_free(file_info->name); + file_info->name = g_strdup(name); + } + + return result; +} + +typedef struct _FontListHandle FontListHandle; +struct _FontListHandle { + gint font; + GnomeVFSFileInfoOptions options; +}; + +static GnomeVFSResult +do_open_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle, + GnomeVFSURI *uri, + GnomeVFSFileInfoOptions options, + GnomeVFSContext *context) +{ + GnomeVFSResult result = GNOME_VFS_ERROR_NOT_SUPPORTED; + char *path = NULL; + FontListHandle *handle; + + path = get_path_from_uri(uri); + if (!path) { + result = GNOME_VFS_ERROR_INVALID_URI; + goto end; + } + if (strcmp(path, "") != 0) { + result = GNOME_VFS_ERROR_NOT_FOUND; + goto end; + } + + if (!ensure_font_list()) { + result = GNOME_VFS_ERROR_INTERNAL; + goto end; + } + + /* handle used to iterate over font names */ + handle = g_new0(FontListHandle, 1); + handle->font = 0; + handle->options = options; + *method_handle = (GnomeVFSMethodHandle *)handle; + result = GNOME_VFS_OK; + + end: + g_free(path); + return result; +} + +static GnomeVFSResult +do_close_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSContext *context) +{ + FontListHandle *handle; + + handle = (FontListHandle *)method_handle; + g_free(handle); + + return GNOME_VFS_OK; +} + +static GnomeVFSResult +do_read_directory(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSFileInfo *file_info, + GnomeVFSContext *context) +{ + GnomeVFSResult result = GNOME_VFS_ERROR_NOT_SUPPORTED; + FontListHandle *handle; + FcChar8 *file; + + handle = (FontListHandle *)method_handle; + + G_LOCK(font_list); + if (!font_list) { + result = GNOME_VFS_ERROR_INTERNAL; + goto end; + } + + if (handle->font >= font_list->nfont) { + result = GNOME_VFS_ERROR_EOF; + goto end; + } + + /* get information about this font, skipping unfound fonts */ + result = GNOME_VFS_ERROR_NOT_FOUND; + while (handle->font < font_list->nfont && + result == GNOME_VFS_ERROR_NOT_FOUND) { + FcPatternGetString(font_list->fonts[handle->font], FC_FILE, 0, &file); + result = fill_file_info(file_info, handle->options, file, + font_names[handle->font]); + + /* move on to next font */ + handle->font++; + } + + end: + G_UNLOCK(font_list); + return result; +} + +/* -------- handling of file objects -------- */ + +static GnomeVFSResult +do_open(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle, + GnomeVFSURI *uri, + GnomeVFSOpenMode mode, + GnomeVFSContext *context) +{ + GnomeVFSResult result = GNOME_VFS_ERROR_NOT_FOUND; + char *path = NULL; + FcPattern *font; + + path = get_path_from_uri(uri); + if (!path) { + result = GNOME_VFS_ERROR_INVALID_URI; + goto end; + } + + if (!ensure_font_list()) { + result = GNOME_VFS_ERROR_INTERNAL; + goto end; + } + + if (path[0] == '\0') { + result = GNOME_VFS_ERROR_IS_DIRECTORY; + goto end; + } + + /* we don't support openning existing files for writing */ + if ((mode & GNOME_VFS_OPEN_WRITE) != 0) { + result = GNOME_VFS_ERROR_READ_ONLY; + goto end; + } + + G_LOCK(font_list); + font = g_hash_table_lookup(font_hash, &path[1]); + if (font) { + FcChar8 *file; + gchar *text_uri; + GnomeVFSURI *font_uri; + + FcPatternGetString(font, FC_FILE, 0, &file); + text_uri = gnome_vfs_get_uri_from_local_path(file); + font_uri = gnome_vfs_uri_new(text_uri); + g_free(text_uri); + + result = gnome_vfs_open_uri_cancellable( + (GnomeVFSHandle **)method_handle, font_uri, mode, context); + + gnome_vfs_uri_unref(font_uri); + } else { + result = GNOME_VFS_ERROR_NOT_FOUND; + } + G_UNLOCK(font_list); + + end: + g_free(path); + return result; +} + +static GnomeVFSResult +do_create(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle, + GnomeVFSURI *uri, + GnomeVFSOpenMode mode, + gboolean exclusive, + guint perm, + GnomeVFSContext *context) +{ + GnomeVFSResult result; + GnomeVFSURI *new_uri; + + g_message("font-method: do_create"); + + new_uri = create_local_uri(uri); + if (!new_uri) + return gnome_vfs_result_from_errno(); + result = gnome_vfs_create_uri_cancellable((GnomeVFSHandle **)method_handle, + new_uri, mode, exclusive, perm, + context); + gnome_vfs_uri_unref(new_uri); + + return result; +} + + +static GnomeVFSResult +do_close(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSContext *context) +{ + g_message("font-method: do_close"); + + return gnome_vfs_close_cancellable((GnomeVFSHandle *)method_handle, + context); +} + +static GnomeVFSResult +do_read(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + gpointer buffer, + GnomeVFSFileSize bytes, + GnomeVFSFileSize *bytes_read, + GnomeVFSContext *context) +{ + return gnome_vfs_read_cancellable((GnomeVFSHandle *)method_handle, + buffer, bytes, bytes_read, context); +} + +static GnomeVFSResult +do_write(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + gconstpointer buffer, + GnomeVFSFileSize bytes, + GnomeVFSFileSize *bytes_written, + GnomeVFSContext *context) +{ + return gnome_vfs_write_cancellable((GnomeVFSHandle *)method_handle, + buffer, bytes, bytes_written, context); +} + +static GnomeVFSResult +do_seek(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSSeekPosition whence, + GnomeVFSFileOffset offset, + GnomeVFSContext *context) +{ + return gnome_vfs_seek_cancellable((GnomeVFSHandle *)method_handle, + whence, offset, context); +} + +static GnomeVFSResult +do_tell(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSFileOffset *offset_return) +{ + return gnome_vfs_tell((GnomeVFSHandle *)method_handle, offset_return); +} + +static GnomeVFSResult +do_get_file_info_from_handle(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle, + GnomeVFSFileInfo *file_info, + GnomeVFSFileInfoOptions options, + GnomeVFSContext *context) +{ + return gnome_vfs_get_file_info_from_handle_cancellable + ((GnomeVFSHandle *)method_handle, file_info, options, context); +} + + +/* -------- file metadata -------- */ + +static GnomeVFSResult +do_get_file_info(GnomeVFSMethod *method, + GnomeVFSURI *uri, + GnomeVFSFileInfo *file_info, + GnomeVFSFileInfoOptions options, + GnomeVFSContext *context) +{ + GnomeVFSResult result = GNOME_VFS_ERROR_NOT_FOUND; + char *path = NULL; + + path = get_path_from_uri(uri); + if (!path) { + result = GNOME_VFS_ERROR_INVALID_URI; + goto end; + } + + if (!ensure_font_list()) { + result = GNOME_VFS_ERROR_INTERNAL; + goto end; + } + + /* root directory */ + if (!strcmp(path, "")) { + g_free(file_info->name); + file_info->name = g_strdup("Fonts"); + + file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; + file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE; + + g_free(file_info->mime_type); + file_info->mime_type = g_strdup("x-directory/normal"); + file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE; + + result = GNOME_VFS_OK; + } else { + FcPattern *font; + + G_LOCK(font_list); + font = g_hash_table_lookup(font_hash, &path[1]); + if (font) { + FcChar8 *file; + + FcPatternGetString(font, FC_FILE, 0, &file); + result = fill_file_info(file_info, options, file, &path[1]); + } + G_UNLOCK(font_list); + } + + end: + G_UNLOCK(font_list); + g_free(path); + return result; +} + +static gboolean +do_is_local(GnomeVFSMethod *method, + const GnomeVFSURI *uri) +{ + gboolean result = FALSE; + char *path = NULL; + + path = get_path_from_uri(uri); + if (!path) { /* invalid path */ + goto end; + } + if (!ensure_font_list()) { /* could not build font list */ + goto end; + } + + /* root directory */ + if (!strcmp(path, "")) { /* base dir */ + result = TRUE; + } else { + FcPattern *font; + + G_LOCK(font_list); + font = g_hash_table_lookup(font_hash, &path[1]); + if (font) { /* check if underlying uri is local */ + FcChar8 *file; + gchar *file_text_uri; + GnomeVFSURI *file_uri; + + FcPatternGetString(font, FC_FILE, 0, &file); + file_text_uri = gnome_vfs_get_uri_from_local_path(file); + file_uri = gnome_vfs_uri_new(file_text_uri); + g_free(file_text_uri); + + result = gnome_vfs_uri_is_local(file_uri); + gnome_vfs_uri_unref(file_uri); + } + G_UNLOCK(font_list); + } + + end: + g_free(path); + return result; +} + +static GnomeVFSResult +do_unlink(GnomeVFSMethod *method, + GnomeVFSURI *uri, + GnomeVFSContext *context) +{ + GnomeVFSResult result = GNOME_VFS_ERROR_NOT_SUPPORTED; + char *path = NULL; + + path = get_path_from_uri(uri); + if (!path) { /* invalid path */ + result = GNOME_VFS_ERROR_INVALID_URI; + goto end; + } + if (!ensure_font_list()) { /* could not build font list */ + result = GNOME_VFS_ERROR_INTERNAL; + goto end; + } + + if (!strcmp(path, "")) { /* base dir */ + result = GNOME_VFS_ERROR_NOT_PERMITTED; + } else { + FcPattern *font; + + G_LOCK(font_list); + font = g_hash_table_lookup(font_hash, &path[1]); + if (font) { /* check if underlying uri is local */ + FcChar8 *file; + gchar *file_text_uri; + GnomeVFSURI *file_uri; + + FcPatternGetString(font, FC_FILE, 0, &file); + file_text_uri = gnome_vfs_get_uri_from_local_path(file); + file_uri = gnome_vfs_uri_new(file_text_uri); + g_free(file_text_uri); + + result = gnome_vfs_unlink_from_uri_cancellable(file_uri, context); + gnome_vfs_uri_unref(file_uri); + } else { + result = GNOME_VFS_ERROR_NOT_FOUND; + } + G_UNLOCK(font_list); + } + end: + g_free(path); + return result; +} + + +/* -------- Directory monitor -------- */ + +/* list of monitors attached to fonts:/// */ +G_LOCK_DEFINE_STATIC(monitor_list); +static GList *monitor_list = NULL; + +static void +invoke_monitors(void) +{ + GList *tmp; + + G_LOCK(monitor_list); + for (tmp = monitor_list; tmp != NULL; tmp = tmp->next) { + GnomeVFSURI *uri = tmp->data; + + gnome_vfs_monitor_callback((GnomeVFSMethodHandle *)uri, uri, + GNOME_VFS_MONITOR_EVENT_CHANGED); + } + G_UNLOCK(monitor_list); +} + +static GnomeVFSResult +do_monitor_add(GnomeVFSMethod *method, + GnomeVFSMethodHandle **method_handle, + GnomeVFSURI *uri, + GnomeVFSMonitorType monitor_type) +{ + char *path = NULL; + GnomeVFSURI *uri_copy; + + path = get_path_from_uri(uri); + if (!path) { /* invalid path */ + return GNOME_VFS_ERROR_INVALID_URI; + } + + if (path[0] != '\0' || monitor_type != GNOME_VFS_MONITOR_DIRECTORY) { + g_free(path); + return GNOME_VFS_ERROR_NOT_SUPPORTED; + } + g_free(path); + + /* it is a directory monitor on fonts:/// */ + uri_copy = gnome_vfs_uri_dup(uri); + *method_handle = (GnomeVFSMethodHandle *)uri_copy; + G_LOCK(monitor_list); + monitor_list = g_list_prepend(monitor_list, uri_copy); + G_UNLOCK(monitor_list); + + return GNOME_VFS_OK; +} + +static GnomeVFSResult +do_monitor_cancel(GnomeVFSMethod *method, + GnomeVFSMethodHandle *method_handle) +{ + GnomeVFSURI *uri; + + uri = (GnomeVFSURI *)method_handle; + G_LOCK(monitor_list); + monitor_list = g_list_remove(monitor_list, uri); + G_UNLOCK(monitor_list); + gnome_vfs_uri_unref(uri); + return GNOME_VFS_OK; +} + + +/* -------- Initialisation of the method -------- */ + +static GnomeVFSMethod method = { + sizeof(GnomeVFSMethod), + + .open = do_open, + .create = do_create, + .close = do_close, + .read = do_read, + .write = do_write, + .seek = do_seek, + .tell = do_tell, + .get_file_info_from_handle = do_get_file_info_from_handle, + + .open_directory = do_open_directory, + .close_directory = do_close_directory, + .read_directory = do_read_directory, + + .get_file_info = do_get_file_info, + .is_local = do_is_local, + .unlink = do_unlink, + + .monitor_add = do_monitor_add, + .monitor_cancel = do_monitor_cancel +}; + +GnomeVFSMethod * +vfs_module_init(const char *method_name, const char *args) +{ + if (!strcmp(method_name, "fonts")) { + if (!FcInit()) { + g_warning("can't init fontconfig library"); + return NULL; + } + return &method; + } + return NULL; +} + +void +vfs_module_shutdown(GnomeVFSMethod *method) +{ + /* clean up font list */ + if (font_list) FcFontSetDestroy(font_list); + if (font_names) g_strfreev(font_names); + if (font_hash) g_hash_table_destroy(font_hash); + font_list = NULL; + font_names = NULL; + font_hash = NULL; +} diff --git a/vfs-methods/fontilus/font-method.conf b/vfs-methods/fontilus/font-method.conf new file mode 100644 index 000000000..9c0101063 --- /dev/null +++ b/vfs-methods/fontilus/font-method.conf @@ -0,0 +1,2 @@ +fonts: font-method + diff --git a/vfs-methods/fontilus/ftstream-vfs.c b/vfs-methods/fontilus/ftstream-vfs.c new file mode 100644 index 000000000..c7cd31aec --- /dev/null +++ b/vfs-methods/fontilus/ftstream-vfs.c @@ -0,0 +1,118 @@ +/* -*- mode: C; c-basic-offset: 4 -*- */ +#include +#include +#include FT_FREETYPE_H +#include + +static unsigned long +vfs_stream_read(FT_Stream stream, + unsigned long offset, + unsigned char *buffer, + unsigned long count) +{ + GnomeVFSHandle *handle = (GnomeVFSHandle *)stream->descriptor.pointer; + GnomeVFSFileSize bytes_read = 0; + + if (gnome_vfs_seek(handle, GNOME_VFS_SEEK_START, offset) != GNOME_VFS_OK) + return 0; + if (count > 0) { + if (gnome_vfs_read(handle, buffer, count, &bytes_read) != GNOME_VFS_OK) + return 0; + } + return bytes_read; +} + +static void +vfs_stream_close(FT_Stream stream) +{ + GnomeVFSHandle *handle = (GnomeVFSHandle *)stream->descriptor.pointer; + + g_message("closing handle"); + if (!handle) + return; + gnome_vfs_close(handle); + + stream->descriptor.pointer = NULL; + stream->size = 0; + stream->base = 0; +} + +static FT_Error +vfs_stream_open(FT_Stream stream, + const char *uri) +{ + GnomeVFSHandle *handle; + GnomeVFSFileInfo *finfo; + + if (!stream) + return FT_Err_Invalid_Stream_Handle; + + if (gnome_vfs_open(&handle, uri, + GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_RANDOM) != GNOME_VFS_OK) { + g_message("could not open URI"); + return FT_Err_Cannot_Open_Resource; + } + + finfo = gnome_vfs_file_info_new(); + if (gnome_vfs_get_file_info_from_handle(handle, finfo,0) != GNOME_VFS_OK) { + g_warning("could not get file info"); + gnome_vfs_file_info_unref(finfo); + gnome_vfs_close(handle); + return FT_Err_Cannot_Open_Resource; + } + + if ((finfo->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) == 0) { + g_warning("file info did not include file size"); + gnome_vfs_file_info_unref(finfo); + gnome_vfs_close(handle); + return FT_Err_Cannot_Open_Resource; + } + stream->size = finfo->size; + gnome_vfs_file_info_unref(finfo); + + stream->descriptor.pointer = handle; + stream->pathname.pointer = NULL; + stream->pos = 0; + + stream->read = vfs_stream_read; + stream->close = vfs_stream_close; + + return FT_Err_Ok; +} + +/* load a typeface from a URI */ +FT_Error +FT_New_URI_Face(FT_Library library, + const gchar* uri, + FT_Long face_index, + FT_Face *aface) +{ + FT_Open_Args args; + FT_Stream stream; + FT_Error error; + + if ((stream = calloc(1, sizeof(FT_Stream))) == NULL) + return FT_Err_Out_Of_Memory; + + error = vfs_stream_open(stream, uri); + if (error != FT_Err_Ok) { + free(stream); + return error; + } + +#ifndef FT_OPEN_STREAM +# define FT_OPEN_STREAM ft_open_stream +#endif + args.flags = FT_OPEN_STREAM; + args.stream = stream; + + error = FT_Open_Face(library, &args, face_index, aface); + + if (error != FT_Err_Ok) { + stream->close(stream); + free(stream); + return error; + } + + return error; +} diff --git a/vfs-methods/fontilus/thumbnailer.c b/vfs-methods/fontilus/thumbnailer.c new file mode 100644 index 000000000..e012e06fb --- /dev/null +++ b/vfs-methods/fontilus/thumbnailer.c @@ -0,0 +1,146 @@ +/* -*- mode: C; c-basic-offset: 4 -*- */ + +#include +#include FT_FREETYPE_H + +#include +#include + +FT_Error FT_New_URI_Face(FT_Library library, + const gchar *uri, + FT_Long face_index, + FT_Face *aface); + +static void +draw_bitmap(GdkPixbuf *pixbuf, FT_Bitmap *bitmap, gint off_x, gint off_y) +{ + guchar *buffer; + gint p_width, p_height, p_rowstride; + gint i, j; + + buffer = gdk_pixbuf_get_pixels(pixbuf); + p_width = gdk_pixbuf_get_width(pixbuf); + p_height = gdk_pixbuf_get_height(pixbuf); + p_rowstride = gdk_pixbuf_get_rowstride(pixbuf); + + for (j = 0; j < bitmap->rows; j++) { + if (j + off_y < 0 || j + off_y >= p_height) + continue; + for (i = 0; i < bitmap->width; i++) { + guchar pixel; + gint pos; + + if (i + off_x < 0 || i + off_x >= p_width) + continue; + pixel = 255 - bitmap->buffer[j*bitmap->pitch + i]; + pos = (j + off_y) * p_rowstride + 3 * (i + off_x); + buffer[pos] = pixel; + buffer[pos+1] = pixel; + buffer[pos+2] = pixel; + } + } +} + +int +main(int argc, char **argv) +{ + FT_Error error; + FT_Library library; + FT_Face face; + FT_GlyphSlot slot; + GdkPixbuf *pixbuf, *pixbuf2; + guchar *buffer; + gint width, height, i, pen_x, pen_y, max_width, max_height; + + if (argc != 3) { + g_message("eek: bad args"); + return 1; + } + + if (!gnome_vfs_init()) { + g_message("eek: gnome-vfs init"); + return 1; + } + + error = FT_Init_FreeType(&library); + if (error) { + g_message("eek: library"); + return 1; + } + + error = FT_New_URI_Face(library, argv[1], 0, &face); + if (error) { + g_message("eek: face"); + return 1; + } + + slot = face->glyph; + + error = FT_Set_Pixel_Sizes(face, 0, 64); + if (error) { + g_message("eek: set pixel size"); + return 1; + } + error = FT_Load_Char(face, 'A', FT_LOAD_RENDER); + if (error) { + g_message("eek: load char"); + return 1; + } + + g_assert(slot->bitmap.pixel_mode == ft_pixel_mode_grays); + + width = slot->bitmap.width; + height = slot->bitmap.rows; + + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width*3, height*1.5); + buffer = gdk_pixbuf_get_pixels(pixbuf); + + for (i = gdk_pixbuf_get_rowstride(pixbuf) * + gdk_pixbuf_get_height(pixbuf); i >= 0; i--) + buffer[i] = 255; + + pen_x = 0; + pen_y = slot->bitmap_top; + max_height = height; + max_width = pen_x + slot->bitmap_left + width; + + draw_bitmap(pixbuf, &slot->bitmap, + pen_x + slot->bitmap_left, pen_y - slot->bitmap_top); + + pen_x += slot->advance.x >> 6; + + error = FT_Load_Char(face, 'a', FT_LOAD_RENDER); + if (error) { + g_message("ekk: load char 2"); + return 1; + } + + max_height = MAX(max_height, pen_y + slot->bitmap.rows - + slot->bitmap_top); + max_width = pen_x + slot->bitmap_left + slot->bitmap.width; + + draw_bitmap(pixbuf, &slot->bitmap, + pen_x + slot->bitmap_left, pen_y - slot->bitmap_top); + + pixbuf2 = gdk_pixbuf_new_subpixbuf(pixbuf, 0,0, max_width,max_height); + + gdk_pixbuf_save(pixbuf2, argv[2], "png", NULL, NULL); + gdk_pixbuf_unref(pixbuf2); + gdk_pixbuf_unref(pixbuf); + + /* freeing the face causes a crash I haven't tracked down yet */ +#if 0 + error = FT_Done_Face(face); + if (error) { + g_message("eek: done face"); + return 1; + } + error = FT_Done_FreeType(library); + if (error) { + g_message("eek: done library"); + return 1; + } +#endif + + return 0; +}