diff options
Diffstat (limited to 'trunk/pango/modules.c')
-rw-r--r-- | trunk/pango/modules.c | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/trunk/pango/modules.c b/trunk/pango/modules.c new file mode 100644 index 00000000..5a9d6883 --- /dev/null +++ b/trunk/pango/modules.c @@ -0,0 +1,763 @@ +/* Pango + * modules.c: + * + * Copyright (C) 1999 Red Hat Software + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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 <string.h> +#include <limits.h> +#include <errno.h> + +#include <gmodule.h> +#include <glib/gstdio.h> + +#include "pango-enum-types.h" +#include "pango-modules.h" +#include "pango-impl-utils.h" +#include "modules.h" + +typedef struct _PangoModule PangoModule; +typedef struct _PangoModuleClass PangoModuleClass; + +#define PANGO_TYPE_MODULE (pango_module_get_type ()) +#define PANGO_MODULE(module) (G_TYPE_CHECK_INSTANCE_CAST ((module), PANGO_TYPE_MODULE, PangoModule)) +#define PANGO_IS_MODULE(module) (G_TYPE_CHECK_INSTANCE_TYPE ((module), PANGO_TYPE_MODULE)) + +typedef struct _PangoMapInfo PangoMapInfo; +typedef struct _PangoEnginePair PangoEnginePair; +typedef struct _PangoSubmap PangoSubmap; + +struct _PangoMap +{ + GArray *entries; +}; + +struct _PangoMapEntry +{ + GSList *exact; + GSList *fallback; +}; + +struct _PangoMapInfo +{ + PangoLanguage *language; + guint engine_type_id; + guint render_type_id; + PangoMap *map; +}; + +struct _PangoEnginePair +{ + PangoEngineInfo info; + PangoModule *module; + PangoEngine *engine; +}; + +struct _PangoModule +{ + GTypeModule parent_instance; + + char *path; + GModule *library; + + void (*list) (PangoEngineInfo **engines, gint *n_engines); + void (*init) (GTypeModule *module); + void (*exit) (void); + PangoEngine *(*create) (const gchar *id); +}; + +struct _PangoModuleClass +{ + GTypeModuleClass parent_class; +}; + +static GList *maps = NULL; +static GSList *registered_engines = NULL; +static GSList *dlloaded_engines = NULL; +static GHashTable *dlloaded_modules; + +static GObjectClass *parent_class; + +static void build_map (PangoMapInfo *info); +static void init_modules (void); + +static GType pango_module_get_type (void); + +/** + * pango_find_map: + * @language: the language tag for which to find the map + * @engine_type_id: the engine type for the map to find + * @render_type_id: the render type for the map to find + * + * Locate a #PangoMap for a particular engine type and render + * type. The resulting map can be used to determine the engine + * for each character. + * + * Return value: the suitable #PangoMap. + **/ +PangoMap * +pango_find_map (PangoLanguage *language, + guint engine_type_id, + guint render_type_id) +{ + GList *tmp_list = maps; + PangoMapInfo *map_info = NULL; + gboolean found_earlier = FALSE; + + while (tmp_list) + { + map_info = tmp_list->data; + if (map_info->engine_type_id == engine_type_id && + map_info->render_type_id == render_type_id) + { + if (map_info->language == language) + break; + else + found_earlier = TRUE; + } + + tmp_list = tmp_list->next; + } + + if (!tmp_list) + { + map_info = g_slice_new (PangoMapInfo); + map_info->language = language; + map_info->engine_type_id = engine_type_id; + map_info->render_type_id = render_type_id; + + build_map (map_info); + + maps = g_list_prepend (maps, map_info); + } + else if (found_earlier) + { + /* Move the found map to the beginning of the list + * for speed next time around if we had to do + * any failing comparison. (No longer so important, + * since we don't strcmp.) + */ + maps = g_list_remove_link(maps, tmp_list); + maps = g_list_prepend(maps, tmp_list->data); + g_list_free_1(tmp_list); + } + + return map_info->map; +} + +static gboolean +pango_module_load (GTypeModule *module) +{ + PangoModule *pango_module = PANGO_MODULE (module); + + if (pango_module->path) + { + pango_module->library = g_module_open (pango_module->path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!pango_module->library) + { + g_warning ("%s", g_module_error()); + return FALSE; + } + + /* extract symbols from the lib */ + if (!g_module_symbol (pango_module->library, "script_engine_init", + (gpointer *)&pango_module->init) || + !g_module_symbol (pango_module->library, "script_engine_exit", + (gpointer *)&pango_module->exit) || + !g_module_symbol (pango_module->library, "script_engine_list", + (gpointer *)&pango_module->list) || + !g_module_symbol (pango_module->library, "script_engine_create", + (gpointer *)&pango_module->create)) + { + g_warning ("%s", g_module_error()); + g_module_close (pango_module->library); + + return FALSE; + } + } + + /* call the module's init function to let it */ + /* setup anything it needs to set up. */ + pango_module->init (module); + + return TRUE; +} + +static void +pango_module_unload (GTypeModule *module) +{ + PangoModule *pango_module = PANGO_MODULE (module); + + pango_module->exit(); + + if (pango_module->path) + { + g_module_close (pango_module->library); + pango_module->library = NULL; + + pango_module->init = NULL; + pango_module->exit = NULL; + pango_module->list = NULL; + pango_module->create = NULL; + } +} + +/* This only will ever be called if an error occurs during + * initialization + */ +static void +pango_module_finalize (GObject *object) +{ + PangoModule *module = PANGO_MODULE (object); + + g_free (module->path); + + parent_class->finalize (object); +} + +static void +pango_module_class_init (PangoModuleClass *class) +{ + GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class); + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (class)); + + module_class->load = pango_module_load; + module_class->unload = pango_module_unload; + + gobject_class->finalize = pango_module_finalize; +} + +static PANGO_DEFINE_TYPE (PangoModule, pango_module, + pango_module_class_init, NULL, + G_TYPE_TYPE_MODULE) + +static PangoEngine * +pango_engine_pair_get_engine (PangoEnginePair *pair) +{ + if (!pair->engine) + { + if (g_type_module_use (G_TYPE_MODULE (pair->module))) + { + pair->engine = pair->module->create (pair->info.id); + g_type_module_unuse (G_TYPE_MODULE (pair->module)); + } + + if (!pair->engine) + { + /* If a module cannot be used, or doesn't not create an engine + * correctly, we print out an error containing module name and id, + * but to not flood the terminal with zillions of the message, we + * set a flag on the module to only err once per module. + */ + static GQuark warned_quark = 0; + + if (!warned_quark) + warned_quark = g_quark_from_static_string ("pango-module-warned"); + + if (!g_object_get_qdata (G_OBJECT (pair->module), warned_quark)) + { + g_warning ("Failed to load Pango module '%s' for id '%s'", pair->module->path, pair->info.id); + + g_object_set_qdata_full (G_OBJECT (pair->module), warned_quark, + GINT_TO_POINTER (1), NULL); + } + } + } + + return pair->engine; +} + +static void +handle_included_module (PangoIncludedModule *included_module, + GSList **engine_list) +{ + PangoModule *module = g_object_new (PANGO_TYPE_MODULE, NULL); + PangoEngineInfo *engine_info; + int n_engines; + int i; + + module->list = included_module->list; + module->init = included_module->init; + module->exit = included_module->exit; + module->create = included_module->create; + + module->list (&engine_info, &n_engines); + + for (i = 0; i < n_engines; i++) + { + PangoEnginePair *pair = g_slice_new (PangoEnginePair); + + pair->info = engine_info[i]; + pair->module = module; + pair->engine = NULL; + + *engine_list = g_slist_prepend (*engine_list, pair); + } +} + +static PangoModule * +find_or_create_module (const char *raw_path) +{ + PangoModule *module; + char *path; + +#if defined(G_OS_WIN32) && defined(LIBDIR) + if (strncmp (raw_path, + LIBDIR "/pango/" MODULE_VERSION "/modules/", + strlen (LIBDIR "/pango/" MODULE_VERSION "/modules/")) == 0) + { + /* This is an entry put there by make install on the + * packager's system. On Windows a prebuilt Pango + * package can be installed in a random + * location. The pango.modules file distributed in + * such a package contains paths from the package + * builder's machine. Replace the path with the real + * one on this machine. */ + path = + g_strconcat (pango_get_lib_subdirectory (), + "\\" MODULE_VERSION "\\modules\\", + raw_path + strlen (LIBDIR "/pango/" MODULE_VERSION "/modules/"), + NULL); + } + else +#endif + { + path = g_strdup (raw_path); + } + + module = g_hash_table_lookup (dlloaded_modules, path); + if (module) + g_free (path); + else + { + module = g_object_new (PANGO_TYPE_MODULE, NULL); + module->path = path; + g_hash_table_insert (dlloaded_modules, path, module); + } + + return module; +} + +static PangoScript +script_from_string (const char *str) +{ + static GEnumClass *class = NULL; + GEnumValue *value; + if (!class) + class = g_type_class_ref (PANGO_TYPE_SCRIPT); + + value = g_enum_get_value_by_nick (class, str); + if (!value) + return PANGO_SCRIPT_INVALID_CODE; + + return value->value; +} + +static void +script_info_free (PangoEngineScriptInfo *script_info, gpointer data) +{ + g_slice_free (PangoEngineScriptInfo, script_info); +} + +static gboolean /* Returns true if succeeded, false if failed */ +process_module_file (FILE *module_file) +{ + GString *line_buf = g_string_new (NULL); + GString *tmp_buf = g_string_new (NULL); + gboolean have_error = FALSE; + + while (pango_read_line (module_file, line_buf)) + { + PangoEnginePair *pair = g_slice_new (PangoEnginePair); + PangoEngineScriptInfo *script_info; + PangoScript script; + GList *scripts = NULL; + GList *tmp_list; + + const char *p; + char *q; + int i; + + p = line_buf->str; + + if (!pango_skip_space (&p)) + { + g_slice_free (PangoEnginePair, pair); + continue; + } + + i = 0; + while (1) + { + if (!pango_scan_string (&p, tmp_buf)) + { + have_error = TRUE; + goto error; + } + + switch (i) + { + case 0: + pair->module = find_or_create_module (tmp_buf->str); + break; + case 1: + pair->info.id = g_strdup (tmp_buf->str); + break; + case 2: + pair->info.engine_type = g_strdup (tmp_buf->str); + break; + case 3: + pair->info.render_type = g_strdup (tmp_buf->str); + break; + default: + q = strchr (tmp_buf->str, ':'); + if (!q) + { + have_error = TRUE; + goto error; + } + *q = '\0'; + script = script_from_string (tmp_buf->str); + if (script == PANGO_SCRIPT_INVALID_CODE) + { + have_error = TRUE; + goto error; + } + + script_info = g_slice_new (PangoEngineScriptInfo); + script_info->script = script; + script_info->langs = g_strdup (q + 1); + + scripts = g_list_prepend (scripts, script_info); + } + + if (!pango_skip_space (&p)) + break; + + i++; + } + + if (i<3) + { + have_error = TRUE; + goto error; + } + + scripts = g_list_reverse (scripts); + pair->info.n_scripts = g_list_length (scripts); + pair->info.scripts = g_new (PangoEngineScriptInfo, pair->info.n_scripts); + + tmp_list = scripts; + for (i=0; i<pair->info.n_scripts; i++) + { + pair->info.scripts[i] = *(PangoEngineScriptInfo *)tmp_list->data; + tmp_list = tmp_list->next; + } + + pair->engine = NULL; + + dlloaded_engines = g_slist_prepend (dlloaded_engines, pair); + + error: + g_list_foreach (scripts, (GFunc)script_info_free, NULL); + g_list_free (scripts); + + if (have_error) + { + g_printerr ("Error reading Pango modules file\n"); + g_slice_free(PangoEnginePair, pair); + break; + } + } + + g_string_free (line_buf, TRUE); + g_string_free (tmp_buf, TRUE); + + return !have_error; +} + +static void +read_modules (void) +{ + FILE *module_file; + + char *file_str = pango_config_key_get ("Pango/ModuleFiles"); + char **files; + int n; + + dlloaded_modules = g_hash_table_new (g_str_hash, g_str_equal); + + if (!file_str) + file_str = g_build_filename (pango_get_sysconf_subdirectory (), + "pango.modules", + NULL); + + files = pango_split_file_list (file_str); + + n = 0; + while (files[n]) + n++; + + while (n-- > 0) + { + module_file = g_fopen (files[n], "r"); + if (module_file) + { + process_module_file(module_file); + fclose(module_file); + } + } + + g_strfreev (files); + g_free (file_str); + + dlloaded_engines = g_slist_reverse (dlloaded_engines); +} + +static void +init_modules (void) +{ + static gboolean init = FALSE; + int i; + + if (init) + return; + else + init = TRUE; + + /* Make sure that the type system is initialized */ + g_type_init (); + + for (i = 0; _pango_included_lang_modules[i].list; i++) + pango_module_register (&_pango_included_lang_modules[i]); + read_modules (); +} + +static void +map_add_engine (PangoMapInfo *info, + PangoEnginePair *pair) +{ + PangoMap *map = info->map; + int i; + + for (i=0; i<pair->info.n_scripts; i++) + { + PangoScript script; + PangoMapEntry *entry; + gboolean is_exact = FALSE; + + if (pair->info.scripts[i].langs) + { + if (pango_language_matches (info->language, pair->info.scripts[i].langs)) + is_exact = TRUE; + } + + script = pair->info.scripts[i].script; + if ((guint)script >= map->entries->len) + g_array_set_size (map->entries, script + 1); + + entry = &g_array_index (map->entries, PangoMapEntry, script); + + if (is_exact) + entry->exact = g_slist_prepend (entry->exact, pair); + else + entry->exact = g_slist_prepend (entry->fallback, pair); + } +} + +static void +map_add_engine_list (PangoMapInfo *info, + GSList *engines, + const char *engine_type, + const char *render_type) +{ + GSList *tmp_list = engines; + + while (tmp_list) + { + PangoEnginePair *pair = tmp_list->data; + tmp_list = tmp_list->next; + + if (strcmp (pair->info.engine_type, engine_type) == 0 && + strcmp (pair->info.render_type, render_type) == 0) + { + map_add_engine (info, pair); + } + } +} + +static void +build_map (PangoMapInfo *info) +{ + const char *engine_type = g_quark_to_string (info->engine_type_id); + const char *render_type = g_quark_to_string (info->render_type_id); + + init_modules(); + + if (!dlloaded_engines && !registered_engines) + { + static gboolean no_module_warning = FALSE; + if (!no_module_warning) + { + gchar *filename = g_build_filename (pango_get_sysconf_subdirectory (), + "pango.modules", + NULL); + g_warning ("No builtin or dynamically\n" + "loaded modules were found. Pango will not work correctly.\n" + "This probably means there was an error in the creation of:\n" + " '%s'\n" + "You should create this file by running pango-querymodules.", + filename); + g_free (filename); + + no_module_warning = TRUE; + } + } + + info->map = g_slice_new (PangoMap); + info->map->entries = g_array_new (FALSE, TRUE, sizeof (PangoMapEntry)); + + map_add_engine_list (info, dlloaded_engines, engine_type, render_type); + map_add_engine_list (info, registered_engines, engine_type, render_type); +} + +/** + * pango_map_get_engine: + * @map: a #PangoMap + * @script: a #PangoScript + * + * Returns the best engine listed in the map for a given script + * + * Return value: the best engine, if one is listed for the script, + * or %NULL. The lookup may cause the engine to be loaded; + * once an engine is loaded, it won't be unloaded. If multiple + * engines are exact for the script, the choice of which is + * returned is arbitrary. + **/ +PangoEngine * +pango_map_get_engine (PangoMap *map, + PangoScript script) +{ + PangoMapEntry *entry = NULL; + PangoMapEntry *common_entry = NULL; + + if ((guint)script < map->entries->len) + entry = &g_array_index (map->entries, PangoMapEntry, script); + + if (PANGO_SCRIPT_COMMON < map->entries->len) + common_entry = &g_array_index (map->entries, PangoMapEntry, PANGO_SCRIPT_COMMON); + + if (entry && entry->exact) + return pango_engine_pair_get_engine (entry->exact->data); + else if (common_entry && common_entry->exact) + return pango_engine_pair_get_engine (common_entry->exact->data); + else if (entry && entry->fallback) + return pango_engine_pair_get_engine (entry->fallback->data); + else if (common_entry && common_entry->fallback) + return pango_engine_pair_get_engine (common_entry->fallback->data); + else + return NULL; +} + +static void +append_engines (GSList **engine_list, + GSList *pair_list) +{ + GSList *l; + + for (l = pair_list; l; l = l->next) + { + PangoEngine *engine = pango_engine_pair_get_engine (l->data); + if (engine) + *engine_list = g_slist_append (*engine_list, engine); + } +} + +/** + * pango_map_get_engines: + * @map: a #PangoMap + * @script: a #PangoScript + * @exact_engines: location to store list of engines that exactly + * handle this script. + * @fallback_engines: location to store list of engines that approximately + * handle this script. + * + * Finds engines in the map that handle the given script. The returned + * lists should be freed with g_slist_free, but the engines in the + * lists are owned by GLib and will be kept around permanently, so + * they should not be unref'ed. + * + * Since: 1.4 + **/ +void +pango_map_get_engines (PangoMap *map, + PangoScript script, + GSList **exact_engines, + GSList **fallback_engines) +{ + PangoMapEntry *entry = NULL; + PangoMapEntry *common_entry = NULL; + + if ((guint)script < map->entries->len) + entry = &g_array_index (map->entries, PangoMapEntry, script); + + if (PANGO_SCRIPT_COMMON < map->entries->len) + common_entry = &g_array_index (map->entries, PangoMapEntry, PANGO_SCRIPT_COMMON); + + if (exact_engines) + { + *exact_engines = NULL; + if (entry && entry->exact) + append_engines (exact_engines, entry->exact); + else if (common_entry && common_entry->exact) + append_engines (exact_engines, common_entry->exact); + } + + if (fallback_engines) + { + *fallback_engines = NULL; + if (entry && entry->fallback) + append_engines (fallback_engines, entry->fallback); + else if (common_entry && common_entry->fallback) + append_engines (fallback_engines, common_entry->fallback); + } +} + +/** + * pango_module_register: + * @module: a #PangoIncludedModule + * + * Registers a statically linked module with Pango. The + * #PangoIncludedModule structure that is passed in contains the + * functions that would otherwise be loaded from a dynamically loaded + * module. + **/ +void +pango_module_register (PangoIncludedModule *module) +{ + GSList *tmp_list = NULL; + + handle_included_module (module, &tmp_list); + + registered_engines = g_slist_concat (registered_engines, + g_slist_reverse (tmp_list)); +} |