summaryrefslogtreecommitdiff
path: root/trunk/pango/pango-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pango-utils.c')
-rw-r--r--trunk/pango/pango-utils.c1643
1 files changed, 1643 insertions, 0 deletions
diff --git a/trunk/pango/pango-utils.c b/trunk/pango/pango-utils.c
new file mode 100644
index 00000000..39ade732
--- /dev/null
+++ b/trunk/pango/pango-utils.c
@@ -0,0 +1,1643 @@
+/* Pango
+ * pango-utils.c: Utilities for internal functions and modules
+ *
+ * Copyright (C) 2000 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 <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <locale.h>
+
+#include "pango-font.h"
+#include "pango-features.h"
+#include "pango-impl-utils.h"
+
+#include <glib/gstdio.h>
+
+#include "mini-fribidi/fribidi.h"
+
+#ifndef HAVE_FLOCKFILE
+# define flockfile(f) (void)1
+# define funlockfile(f) (void)1
+# define getc_unlocked(f) getc(f)
+#endif /* !HAVE_FLOCKFILE */
+
+#ifdef G_OS_WIN32
+
+#include <sys/types.h>
+
+#define STRICT
+#include <windows.h>
+
+#endif
+
+struct PangoAlias
+{
+ char *alias;
+ int n_families;
+ char **families;
+ gboolean visible; /* Do we want/need this? */
+};
+
+static GHashTable *pango_aliases_ht = NULL;
+
+PangoWarningHistory _pango_warning_history = { FALSE, FALSE, FALSE };
+
+/**
+ * pango_version:
+ *
+ * This is similar to the macro %PANGO_VERSION except that
+ * it returns the encoded version of Pango available at run-time,
+ * as opposed to the version available at compile-time.
+ *
+ * A version number can be encoded into an integer using
+ * PANGO_VERSION_ENCODE().
+ *
+ * Returns value: The encoded version of Pango library
+ * available at run time.
+ *
+ * Since: 1.16
+ **/
+int
+pango_version (void)
+{
+ return PANGO_VERSION;
+}
+
+/**
+ * pango_version_string:
+ *
+ * This is similar to the macro %PANGO_VERSION_STRING except that
+ * it returns the version of Pango available at run-time, as opposed to
+ * the version available at compile-time.
+ *
+ * Returns value: A string containing the version of Pango library
+ * available at run time.
+ * The returned string is owned by Pango and should not be modified
+ * or freed.
+ *
+ * Since: 1.16
+ **/
+const char *
+pango_version_string (void)
+{
+ return PANGO_VERSION_STRING;
+}
+
+/**
+ * pango_version_check:
+ * @required_major: the required major version.
+ * @required_minor: the required minor version.
+ * @required_micro: the required major version.
+ *
+ * Checks that the Pango library in use is compatible with the
+ * given version. Generally you would pass in the constants
+ * %PANGO_VERSION_MAJOR, %PANGO_VERSION_MINOR, %PANGO_VERSION_MICRO
+ * as the three arguments to this function; that produces
+ * a check that the library in use at run-time is compatible with
+ * the version of Pango the application or module was compiled against.
+ *
+ * Compatibility is defined by two things: first the version
+ * of the running library is newer than the version
+ * @required_major.required_minor.@required_micro. Second
+ * the running library must be binary compatible with the
+ * version @required_major.required_minor.@required_micro
+ * (same major version.)
+ *
+ * For compile-time version checking use PANGO_VERSION_CHECK().
+ *
+ * Return value: %NULL if the Pango library is compatible with the
+ * given version, or a string describing the version mismatch.
+ * The returned string is owned by Pango and should not be modified
+ * or freed.
+ *
+ * Since: 1.16
+ **/
+const gchar*
+pango_version_check (int required_major,
+ int required_minor,
+ int required_micro)
+{
+ gint pango_effective_micro = 100 * PANGO_VERSION_MINOR + PANGO_VERSION_MICRO;
+ gint required_effective_micro = 100 * required_minor + required_micro;
+
+ if (required_major < PANGO_VERSION_MAJOR)
+ return "Pango version too new (major mismatch)";
+ if (required_effective_micro < pango_effective_micro - PANGO_BINARY_AGE)
+ return "Pango version too new (micro mismatch)";
+ if (required_effective_micro > pango_effective_micro)
+ return "Pango version too old (micro mismatch)";
+ return NULL;
+}
+
+/**
+ * pango_trim_string:
+ * @str: a string
+ *
+ * Trims leading and trailing whitespace from a string.
+ *
+ * Return value: A newly-allocated string that must be freed with g_free()
+ **/
+char *
+pango_trim_string (const char *str)
+{
+ int len;
+
+ g_return_val_if_fail (str != NULL, NULL);
+
+ while (*str && g_ascii_isspace (*str))
+ str++;
+
+ len = strlen (str);
+ while (len > 0 && g_ascii_isspace (str[len-1]))
+ len--;
+
+ return g_strndup (str, len);
+}
+
+/**
+ * pango_split_file_list:
+ * @str: a %G_SEARCHPATH_SEPARATOR separated list of filenames
+ *
+ * Splits a %G_SEARCHPATH_SEPARATOR-separated list of files, stripping
+ * white space and substituting ~/ with $HOME/.
+ *
+ * Return value: a list of strings to be freed with g_strfreev()
+ **/
+char **
+pango_split_file_list (const char *str)
+{
+ int i = 0;
+ int j;
+ char **files;
+
+ files = g_strsplit (str, G_SEARCHPATH_SEPARATOR_S, -1);
+
+ while (files[i])
+ {
+ char *file = pango_trim_string (files[i]);
+
+ /* If the resulting file is empty, skip it */
+ if (file[0] == '\0')
+ {
+ g_free(file);
+ g_free (files[i]);
+
+ for (j = i + 1; files[j]; j++)
+ files[j - 1] = files[j];
+
+ files[j - 1] = NULL;
+
+ continue;
+ }
+#ifndef G_OS_WIN32
+ /* '~' is a quite normal and common character in file names on
+ * Windows, especially in the 8.3 versions of long file names, which
+ * still occur now and then. Also, few Windows user are aware of the
+ * Unix shell convention that '~' stands for the home directory,
+ * even if they happen to have a home directory.
+ */
+ if (file[0] == '~' && file[1] == G_DIR_SEPARATOR)
+ {
+ char *tmp = g_strconcat (g_get_home_dir(), file + 1, NULL);
+ g_free (file);
+ file = tmp;
+ }
+ else if (file[0] == '~' && file[1] == '\0')
+ {
+ g_free (file);
+ file = g_strdup (g_get_home_dir());
+ }
+#endif
+ g_free (files[i]);
+ files[i] = file;
+
+ i++;
+ }
+
+ return files;
+}
+
+/**
+ * pango_read_line:
+ * @stream: a stdio stream
+ * @str: #GString buffer into which to write the result
+ *
+ * Reads an entire line from a file into a buffer. Lines may
+ * be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter
+ * is not written into the buffer. Text after a '#' character is treated as
+ * a comment and skipped. '\' can be used to escape a # character.
+ * '\' proceeding a line delimiter combines adjacent lines. A '\' proceeding
+ * any other character is ignored and written into the output buffer
+ * unmodified.
+ *
+ * Return value: 0 if the stream was already at an %EOF character, otherwise
+ * the number of lines read (this is useful for maintaining
+ * a line number counter which doesn't combine lines with '\')
+ **/
+gint
+pango_read_line (FILE *stream, GString *str)
+{
+ gboolean quoted = FALSE;
+ gboolean comment = FALSE;
+ int n_read = 0;
+ int lines = 1;
+
+ flockfile (stream);
+
+ g_string_truncate (str, 0);
+
+ while (1)
+ {
+ int c;
+
+ c = getc_unlocked (stream);
+
+ if (c == EOF)
+ {
+ if (quoted)
+ g_string_append_c (str, '\\');
+
+ goto done;
+ }
+ else
+ n_read++;
+
+ if (quoted)
+ {
+ quoted = FALSE;
+
+ switch (c)
+ {
+ case '#':
+ g_string_append_c (str, '#');
+ break;
+ case '\r':
+ case '\n':
+ {
+ int next_c = getc_unlocked (stream);
+
+ if (!(next_c == EOF ||
+ (c == '\r' && next_c == '\n') ||
+ (c == '\n' && next_c == '\r')))
+ ungetc (next_c, stream);
+
+ lines++;
+
+ break;
+ }
+ default:
+ g_string_append_c (str, '\\');
+ g_string_append_c (str, c);
+ }
+ }
+ else
+ {
+ switch (c)
+ {
+ case '#':
+ comment = TRUE;
+ break;
+ case '\\':
+ if (!comment)
+ quoted = TRUE;
+ break;
+ case '\n':
+ {
+ int next_c = getc_unlocked (stream);
+
+ if (!(c == EOF ||
+ (c == '\r' && next_c == '\n') ||
+ (c == '\n' && next_c == '\r')))
+ ungetc (next_c, stream);
+
+ goto done;
+ }
+ default:
+ if (!comment)
+ g_string_append_c (str, c);
+ }
+ }
+ }
+
+ done:
+
+ funlockfile (stream);
+
+ return (n_read > 0) ? lines : 0;
+}
+
+/**
+ * pango_skip_space:
+ * @pos: in/out string position
+ *
+ * Skips 0 or more characters of white space.
+ *
+ * Return value: %FALSE if skipping the white space leaves
+ * the position at a '\0' character.
+ **/
+gboolean
+pango_skip_space (const char **pos)
+{
+ const char *p = *pos;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ *pos = p;
+
+ return !(*p == '\0');
+}
+
+/**
+ * pango_scan_word:
+ * @pos: in/out string position
+ * @out: a #GString into which to write the result
+ *
+ * Scans a word into a #GString buffer. A word consists
+ * of [A-Za-z_] followed by zero or more [A-Za-z_0-9]
+ * Leading white space is skipped.
+ *
+ * Return value: %FALSE if a parse error occurred.
+ **/
+gboolean
+pango_scan_word (const char **pos, GString *out)
+{
+ const char *p = *pos;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ if (!((*p >= 'A' && *p <= 'Z') ||
+ (*p >= 'a' && *p <= 'z') ||
+ *p == '_'))
+ return FALSE;
+
+ g_string_truncate (out, 0);
+ g_string_append_c (out, *p);
+ p++;
+
+ while ((*p >= 'A' && *p <= 'Z') ||
+ (*p >= 'a' && *p <= 'z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '_')
+ {
+ g_string_append_c (out, *p);
+ p++;
+ }
+
+ *pos = p;
+
+ return TRUE;
+}
+
+/**
+ * pango_scan_string:
+ * @pos: in/out string position
+ * @out: a #GString into which to write the result
+ *
+ * Scans a string into a #GString buffer. The string may either
+ * be a sequence of non-white-space characters, or a quoted
+ * string with '"'. Instead a quoted string, '\"' represents
+ * a literal quote. Leading white space outside of quotes is skipped.
+ *
+ * Return value: %FALSE if a parse error occurred.
+ **/
+gboolean
+pango_scan_string (const char **pos, GString *out)
+{
+ const char *p = *pos;
+
+ while (g_ascii_isspace (*p))
+ p++;
+
+ if (!*p)
+ return FALSE;
+ else if (*p == '"')
+ {
+ gboolean quoted = FALSE;
+ g_string_truncate (out, 0);
+
+ p++;
+
+ while (TRUE)
+ {
+ if (quoted)
+ {
+ int c = *p;
+
+ switch (c)
+ {
+ case '\0':
+ return FALSE;
+ case 'n':
+ c = '\n';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ default:
+ break;
+ }
+
+ quoted = FALSE;
+ g_string_append_c (out, c);
+ }
+ else
+ {
+ switch (*p)
+ {
+ case '\0':
+ return FALSE;
+ case '\\':
+ quoted = TRUE;
+ break;
+ case '"':
+ p++;
+ goto done;
+ default:
+ g_string_append_c (out, *p);
+ break;
+ }
+ }
+ p++;
+ }
+ done:
+ ;
+ }
+ else
+ {
+ g_string_truncate (out, 0);
+
+ while (*p && !g_ascii_isspace (*p))
+ {
+ g_string_append_c (out, *p);
+ p++;
+ }
+ }
+
+ *pos = p;
+
+ return TRUE;
+}
+
+/**
+ * pango_scan_int:
+ * @pos: in/out string position
+ * @out: an int into which to write the result
+ *
+ * Scans an integer.
+ * Leading white space is skipped.
+ *
+ * Return value: %FALSE if a parse error occurred.
+ **/
+gboolean
+pango_scan_int (const char **pos, int *out)
+{
+ unsigned int i = 0;
+ char *end;
+ long temp;
+
+ errno = 0;
+ temp = strtol (*pos, &end, 10);
+ if (errno == ERANGE)
+ {
+ errno = 0;
+ return FALSE;
+ }
+
+ *out = (int)temp;
+ if ((long)(*out) != temp)
+ {
+ return FALSE;
+ }
+
+ *pos = end;
+
+ return TRUE;
+}
+
+static GHashTable *config_hash = NULL;
+
+static void
+read_config_file (const char *filename, gboolean enoent_error)
+{
+ GKeyFile *key_file = g_key_file_new();
+ GError *key_file_error = NULL;
+ gchar **groups;
+ gsize groups_count = 0;
+ guint group_index;
+
+ if (!g_key_file_load_from_file(key_file,filename, 0, &key_file_error))
+ {
+ if (key_file_error)
+ {
+ if (key_file_error->domain != G_FILE_ERROR || key_file_error->code != G_FILE_ERROR_NOENT || enoent_error)
+ {
+ g_warning ("error opening config file '%s': %s\n",
+ filename, key_file_error->message);
+ }
+ g_error_free(key_file_error);
+ }
+ g_key_file_free(key_file);
+ return;
+ }
+
+ groups = g_key_file_get_groups (key_file, &groups_count);
+ for (group_index = 0; group_index < groups_count; group_index++)
+ {
+ gsize keys_count = 0;
+ const gchar *group = groups[group_index];
+ GError *keys_error = NULL;
+ gchar **keys;
+
+ keys = g_key_file_get_keys(key_file, group, &keys_count, &keys_error);
+
+ if (keys)
+ {
+ guint key_index;
+
+ for (key_index = 0; key_index < keys_count; key_index++)
+ {
+ const gchar *key = keys[key_index];
+ GError *key_error = NULL;
+ gchar *value = g_key_file_get_value(key_file, group, key, &key_error);
+ if (value != NULL)
+ {
+ g_hash_table_insert (config_hash,
+ g_strdup_printf ("%s/%s", group, key),
+ value);
+ }
+ if (key_error)
+ {
+ g_warning ("error getting key '%s/%s' in config file '%s'\n",
+ group, key, filename);
+ g_error_free(key_error);
+ }
+ }
+ g_strfreev(keys);
+ }
+
+ if (keys_error)
+ {
+ g_warning ("error getting keys in group '%s' of config file '%s'\n",
+ filename, group);
+ g_error_free(keys_error);
+ }
+ }
+ g_strfreev(groups);
+ g_key_file_free(key_file);
+}
+
+static void
+read_config (void)
+{
+ if (!config_hash)
+ {
+ char *filename;
+ const char *home;
+ const char *envvar;
+
+ config_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+ filename = g_build_filename (pango_get_sysconf_subdirectory (),
+ "pangorc",
+ NULL);
+ read_config_file (filename, FALSE);
+ g_free (filename);
+
+ home = g_get_home_dir ();
+ if (home && *home)
+ {
+ filename = g_build_filename (home, ".pangorc", NULL);
+ read_config_file (filename, FALSE);
+ g_free (filename);
+ }
+
+ envvar = g_getenv ("PANGO_RC_FILE");
+ if (envvar)
+ read_config_file (envvar, TRUE);
+ }
+}
+
+/**
+ * pango_config_key_get:
+ * @key: Key to look up, in the form "SECTION/KEY".
+ *
+ * Looks up a key in the Pango config database
+ * (pseudo-win.ini style, read from $sysconfdir/pango/pangorc,
+ * ~/.pangorc, and getenv (PANGO_RC_FILE).)
+ *
+ * Return value: the value, if found, otherwise %NULL. The value is a
+ * newly-allocated string and must be freed with g_free().
+ **/
+char *
+pango_config_key_get (const char *key)
+{
+ g_return_val_if_fail (key != NULL, NULL);
+
+ read_config ();
+
+ return g_strdup (g_hash_table_lookup (config_hash, key));
+}
+
+#ifdef G_OS_WIN32
+
+/* DllMain function needed to tuck away the DLL name */
+
+G_WIN32_DLLMAIN_FOR_DLL_NAME(static, dll_name)
+#endif
+
+/**
+ * pango_get_sysconf_subdirectory:
+ *
+ * On Unix, returns the name of the "pango" subdirectory of SYSCONFDIR
+ * (which is set at compile time). On Win32, returns a subdirectory of
+ * the Pango installation directory (which is deduced at run time from
+ * the DLL's location, or stored in the Registry).
+ *
+ * Return value: the Pango sysconf directory. The returned string should
+ * not be freed.
+ */
+G_CONST_RETURN char *
+pango_get_sysconf_subdirectory (void)
+{
+#ifdef G_OS_WIN32
+ static gchar *result = NULL;
+
+ if (result == NULL)
+ result = g_win32_get_package_installation_subdirectory
+ (PACKAGE " " VERSION, dll_name, "etc\\pango");
+
+ return result;
+#else
+ return SYSCONFDIR "/pango";
+#endif
+}
+
+/**
+ * pango_get_lib_subdirectory:
+ *
+ * On Unix, returns the name of the "pango" subdirectory of LIBDIR
+ * (which is set at compile time). On Win32, returns the Pango
+ * installation directory (which is deduced at run time from the DLL's
+ * location, or stored in the Registry). The returned string should
+ * not be freed.
+ *
+ * Return value: the Pango lib directory. The returned string should
+ * not be freed.
+ */
+G_CONST_RETURN char *
+pango_get_lib_subdirectory (void)
+{
+#ifdef G_OS_WIN32
+ static gchar *result = NULL;
+
+ if (result == NULL)
+ result = g_win32_get_package_installation_subdirectory
+ (PACKAGE " " VERSION, dll_name, "lib\\pango");
+
+ return result;
+#else
+ return LIBDIR "/pango";
+#endif
+}
+
+
+/**
+ * pango_parse_enum:
+ * @type: enum type to parse, eg. %PANGO_TYPE_ELLIPSIZE_MODE.
+ * @str: string to parse. May be %NULL.
+ * @value: integer to store the result in, or %NULL.
+ * @warn: if %TRUE, issue a g_warning() on bad input.
+ * @possible_values: place to store list of possible values on failure, or %NULL.
+ *
+ * Parses an enum type and stored the result in @value.
+ *
+ * If @str does not match the nick name of any of the possible values for the
+ * enum, %FALSE is returned, a warning is issued if @warn is %TRUE, and a
+ * string representing the list of possible values is stored in
+ * @possible_values. The list is slash-separated, eg.
+ * "none/start/middle/end". If failed and @possible_values is not %NULL,
+ * returned string should be freed using g_free().
+ *
+ * Return value: %TRUE if @str was successfully parsed.
+ *
+ * Since: 1.16
+ **/
+gboolean
+pango_parse_enum (GType type,
+ const char *str,
+ int *value,
+ gboolean warn,
+ char **possible_values)
+{
+ GEnumClass *class = NULL;
+ gboolean ret = TRUE;
+ GEnumValue *v = NULL;
+
+ class = g_type_class_ref (type);
+
+ if (G_LIKELY (str))
+ v = g_enum_get_value_by_nick (class, str);
+
+ if (v)
+ {
+ if (G_LIKELY (value))
+ *value = v->value;
+ }
+ else
+ {
+ ret = FALSE;
+ if (warn || possible_values)
+ {
+ int i;
+ GString *s = g_string_new (NULL);
+
+ for (i = 0, v = g_enum_get_value (class, i); v;
+ i++ , v = g_enum_get_value (class, i))
+ {
+ if (i)
+ g_string_append_c (s, '/');
+ g_string_append (s, v->value_nick);
+ }
+
+ if (warn)
+ g_warning ("%s must be one of %s",
+ G_ENUM_CLASS_TYPE_NAME(class),
+ s->str);
+
+ if (possible_values)
+ *possible_values = s->str;
+
+ g_string_free (s, possible_values ? FALSE : TRUE);
+ }
+ }
+
+ g_type_class_unref (class);
+
+ return ret;
+}
+
+/**
+ * pango_parse_style:
+ * @str: a string to parse.
+ * @style: a #PangoStyle to store the result in.
+ * @warn: if %TRUE, issue a g_warning() on bad input.
+ *
+ * Parses a font style. The allowed values are "normal",
+ * "italic" and "oblique", case variations being
+ * ignored.
+ *
+ * Return value: %TRUE if @str was successfully parsed.
+ **/
+gboolean
+pango_parse_style (const char *str,
+ PangoStyle *style,
+ gboolean warn)
+{
+ if (*str == '\0')
+ return FALSE;
+
+ switch (str[0])
+ {
+ case 'n':
+ case 'N':
+ if (g_ascii_strcasecmp (str, "normal") == 0)
+ {
+ *style = PANGO_STYLE_NORMAL;
+ return TRUE;
+ }
+ break;
+ case 'i':
+ case 'I':
+ if (g_ascii_strcasecmp (str, "italic") == 0)
+ {
+ *style = PANGO_STYLE_ITALIC;
+ return TRUE;
+ }
+ break;
+ case 'o':
+ case 'O':
+ if (g_ascii_strcasecmp (str, "oblique") == 0)
+ {
+ *style = PANGO_STYLE_OBLIQUE;
+ return TRUE;
+ }
+ break;
+ }
+ if (warn)
+ g_warning ("style must be normal, italic, or oblique");
+
+ return FALSE;
+}
+
+/**
+ * pango_parse_variant:
+ * @str: a string to parse.
+ * @variant: a #PangoVariant to store the result in.
+ * @warn: if %TRUE, issue a g_warning() on bad input.
+ *
+ * Parses a font variant. The allowed values are "normal"
+ * and "smallcaps" or "small_caps", case variations being
+ * ignored.
+ *
+ * Return value: %TRUE if @str was successfully parsed.
+ **/
+gboolean
+pango_parse_variant (const char *str,
+ PangoVariant *variant,
+ gboolean warn)
+{
+ if (*str == '\0')
+ return FALSE;
+
+ switch (str[0])
+ {
+ case 'n':
+ case 'N':
+ if (g_ascii_strcasecmp (str, "normal") == 0)
+ {
+ *variant = PANGO_VARIANT_NORMAL;
+ return TRUE;
+ }
+ break;
+ case 's':
+ case 'S':
+ if (g_ascii_strcasecmp (str, "small_caps") == 0 ||
+ g_ascii_strcasecmp (str, "smallcaps") == 0)
+ {
+ *variant = PANGO_VARIANT_SMALL_CAPS;
+ return TRUE;
+ }
+ break;
+ }
+
+ if (warn)
+ g_warning ("variant must be normal or small_caps");
+ return FALSE;
+}
+
+/**
+ * pango_parse_weight:
+ * @str: a string to parse.
+ * @weight: a #PangoWeight to store the result in.
+ * @warn: if %TRUE, issue a g_warning() on bad input.
+ *
+ * Parses a font weight. The allowed values are "heavy",
+ * "ultrabold", "bold", "normal", "light", "ultraleight"
+ * and integers. Case variations are ignored.
+ *
+ * Return value: %TRUE if @str was successfully parsed.
+ **/
+gboolean
+pango_parse_weight (const char *str,
+ PangoWeight *weight,
+ gboolean warn)
+{
+ if (*str == '\0')
+ return FALSE;
+
+ switch (str[0])
+ {
+ case 'b':
+ case 'B':
+ if (g_ascii_strcasecmp (str, "bold") == 0)
+ {
+ *weight = PANGO_WEIGHT_BOLD;
+ return TRUE;
+ }
+ break;
+ case 'h':
+ case 'H':
+ if (g_ascii_strcasecmp (str, "heavy") == 0)
+ {
+ *weight = PANGO_WEIGHT_HEAVY;
+ return TRUE;
+ }
+ break;
+ case 'l':
+ case 'L':
+ if (g_ascii_strcasecmp (str, "light") == 0)
+ {
+ *weight = PANGO_WEIGHT_LIGHT;
+ return TRUE;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (g_ascii_strcasecmp (str, "normal") == 0)
+ {
+ *weight = PANGO_WEIGHT_NORMAL;
+ return TRUE;
+ }
+ break;
+ case 'u':
+ case 'U':
+ if (g_ascii_strcasecmp (str, "ultralight") == 0)
+ {
+ *weight = PANGO_WEIGHT_ULTRALIGHT;
+ return TRUE;
+ }
+ else if (g_ascii_strcasecmp (str, "ultrabold") == 0)
+ {
+ *weight = PANGO_WEIGHT_ULTRABOLD;
+ return TRUE;
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ char *end;
+
+ *weight = strtol (str, &end, 10);
+ if (*end != '\0')
+ {
+ if (warn)
+ g_warning ("failed parsing numerical weight '%s'", str);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+
+ if (warn)
+ g_warning ("weight must be ultralight, light, normal, bold, ultrabold, heavy, or an integer");
+ return FALSE;
+}
+
+/**
+ * pango_parse_stretch:
+ * @str: a string to parse.
+ * @stretch: a #PangoStretch to store the result in.
+ * @warn: if %TRUE, issue a g_warning() on bad input.
+ *
+ * Parses a font stretch. The allowed values are
+ * "ultra_condensed", "extra_condensed", "condensed",
+ * "semi_condensed", "normal", "semi_expanded", "expanded",
+ * "extra_expanded" and "ultra_expanded". Case variations are
+ * ignored and the '_' characters may be omitted.
+ *
+ * Return value: %TRUE if @str was successfully parsed.
+ **/
+gboolean
+pango_parse_stretch (const char *str,
+ PangoStretch *stretch,
+ gboolean warn)
+{
+ if (*str == '\0')
+ return FALSE;
+
+ switch (str[0])
+ {
+ case 'c':
+ case 'C':
+ if (g_ascii_strcasecmp (str, "condensed") == 0)
+ {
+ *stretch = PANGO_STRETCH_CONDENSED;
+ return TRUE;
+ }
+ break;
+ case 'e':
+ case 'E':
+ if (g_ascii_strcasecmp (str, "extra_condensed") == 0 ||
+ g_ascii_strcasecmp (str, "extracondensed") == 0)
+ {
+ *stretch = PANGO_STRETCH_EXTRA_CONDENSED;
+ return TRUE;
+ }
+ if (g_ascii_strcasecmp (str, "extra_expanded") == 0 ||
+ g_ascii_strcasecmp (str, "extraexpanded") == 0)
+ {
+ *stretch = PANGO_STRETCH_EXTRA_EXPANDED;
+ return TRUE;
+ }
+ if (g_ascii_strcasecmp (str, "expanded") == 0)
+ {
+ *stretch = PANGO_STRETCH_EXPANDED;
+ return TRUE;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (g_ascii_strcasecmp (str, "normal") == 0)
+ {
+ *stretch = PANGO_STRETCH_NORMAL;
+ return TRUE;
+ }
+ break;
+ case 's':
+ case 'S':
+ if (g_ascii_strcasecmp (str, "semi_condensed") == 0 ||
+ g_ascii_strcasecmp (str, "semicondensed") == 0)
+ {
+ *stretch = PANGO_STRETCH_SEMI_CONDENSED;
+ return TRUE;
+ }
+ if (g_ascii_strcasecmp (str, "semi_expanded") == 0 ||
+ g_ascii_strcasecmp (str, "semiexpanded") == 0)
+ {
+ *stretch = PANGO_STRETCH_SEMI_EXPANDED;
+ return TRUE;
+ }
+ break;
+ case 'u':
+ case 'U':
+ if (g_ascii_strcasecmp (str, "ultra_condensed") == 0 ||
+ g_ascii_strcasecmp (str, "ultracondensed") == 0)
+ {
+ *stretch = PANGO_STRETCH_ULTRA_CONDENSED;
+ return TRUE;
+ }
+ if (g_ascii_strcasecmp (str, "ultra_expanded") == 0 ||
+ g_ascii_strcasecmp (str, "ultraexpanded") == 0)
+ {
+ *stretch = PANGO_STRETCH_ULTRA_EXPANDED;
+ return TRUE;
+ }
+ break;
+ }
+
+ if (warn)
+ g_warning ("stretch must be ultra_condensed, extra_condensed, condensed, semi_condensed, normal, semi_expanded, expanded, extra_expanded, or ultra_expanded");
+ return FALSE;
+}
+
+/**
+ * pango_log2vis_get_embedding_levels:
+ * @text: the text to itemize.
+ * @length: the number of bytes (not characters) to process, or -1
+ * if @text is nul-terminated and the length should be calculated.
+ * @pbase_dir: input base direction, and output resolved direction.
+ *
+ * This will return the bidirectional embedding levels of the input paragraph
+ * as defined by the Unicode Bidirectional Algorithm available at:
+ *
+ * http://www.unicode.org/reports/tr9/
+ *
+ * If the input base direction is a weak direction, the direction of the
+ * characters in the text will determine the final resolved direction.
+ *
+ * Return value: a newly allocated array of embedding levels, one item per
+ * character (not byte), that should be freed using g_free.
+ *
+ * Since: 1.4
+ */
+guint8 *
+pango_log2vis_get_embedding_levels (const gchar *text,
+ int length,
+ PangoDirection *pbase_dir)
+{
+ FriBidiCharType fribidi_base_dir;
+ guint8 *embedding_levels_list;
+
+ switch (*pbase_dir)
+ {
+ case PANGO_DIRECTION_LTR:
+ case PANGO_DIRECTION_TTB_RTL:
+ fribidi_base_dir = FRIBIDI_TYPE_L;
+ break;
+ case PANGO_DIRECTION_RTL:
+ case PANGO_DIRECTION_TTB_LTR:
+ fribidi_base_dir = FRIBIDI_TYPE_R;
+ break;
+ case PANGO_DIRECTION_WEAK_RTL:
+ fribidi_base_dir = FRIBIDI_TYPE_WR;
+ break;
+ /*
+ case PANGO_DIRECTION_WEAK_LTR:
+ case PANGO_DIRECTION_NEUTRAL:
+ */
+ default:
+ fribidi_base_dir = FRIBIDI_TYPE_WL;
+ break;
+ }
+
+#ifdef FRIBIDI_HAVE_UTF8
+ {
+ if (length < 0)
+ length = strlen (text);
+ embedding_levels_list = fribidi_log2vis_get_embedding_levels_new_utf8 (text, length, &fribidi_base_dir);
+ }
+#else
+ {
+ gunichar *text_ucs4;
+ int n_chars;
+ text_ucs4 = g_utf8_to_ucs4_fast (text, length, &n_chars);
+ embedding_levels_list = g_new (guint8, n_chars);
+ fribidi_log2vis_get_embedding_levels ((FriBidiChar*)text_ucs4, n_chars,
+ &fribidi_base_dir,
+ (FriBidiLevel*)embedding_levels_list);
+ g_free (text_ucs4);
+ }
+#endif
+
+ *pbase_dir = (fribidi_base_dir == FRIBIDI_TYPE_L) ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
+
+ return embedding_levels_list;
+}
+
+/**
+ * pango_unichar_direction:
+ * @ch: a Unicode character
+ *
+ * Determines the direction of a character; either
+ * %PANGO_DIRECTION_LTR, %PANGO_DIRECTION_RTL, or
+ * %PANGO_DIRECTION_NEUTRAL.
+ *
+ * Return value: the direction of the character, as used in the
+ * Unicode bidirectional algorithm.
+ */
+PangoDirection
+pango_unichar_direction (gunichar ch)
+{
+ FriBidiCharType fribidi_ch_type = fribidi_get_type (ch);
+
+ if (!FRIBIDI_IS_LETTER (fribidi_ch_type))
+ return PANGO_DIRECTION_NEUTRAL;
+ else if (FRIBIDI_IS_RTL (fribidi_ch_type))
+ return PANGO_DIRECTION_RTL;
+ else
+ return PANGO_DIRECTION_LTR;
+}
+
+/**
+ * pango_get_mirror_char:
+ * @ch: a Unicode character
+ * @mirrored_ch: location to store the mirrored character
+ *
+ * If @ch has the Unicode mirrored property and there is another Unicode
+ * character that typically has a glyph that is the mirror image of @ch's
+ * glyph, puts that character in the address pointed to by @mirrored_ch.
+ *
+ * Use g_unichar_get_mirror_char() instead; the docs for that function
+ * provide full details.
+ *
+ * Return value: %TRUE if @ch has a mirrored character and @mirrored_ch is
+ * filled in, %FALSE otherwise
+ **/
+gboolean
+pango_get_mirror_char (gunichar ch,
+ gunichar *mirrored_ch)
+{
+ return g_unichar_get_mirror_char (ch, mirrored_ch);
+}
+
+
+static guint
+alias_hash (struct PangoAlias *alias)
+{
+ return g_str_hash (alias->alias);
+}
+
+static gboolean
+alias_equal (struct PangoAlias *alias1,
+ struct PangoAlias *alias2)
+{
+ return g_str_equal (alias1->alias,
+ alias2->alias);
+}
+
+
+static void
+alias_free (struct PangoAlias *alias)
+{
+ int i;
+ g_free (alias->alias);
+
+ for (i = 0; i < alias->n_families; i++)
+ g_free (alias->families[i]);
+
+ g_free (alias->families);
+
+ g_slice_free (struct PangoAlias, alias);
+}
+
+static void
+read_alias_file (const char *filename)
+{
+ FILE *file;
+
+ GString *line_buffer;
+ GString *tmp_buffer1;
+ GString *tmp_buffer2;
+ char *errstring = NULL;
+ const char *pos;
+ int line = 0;
+ struct PangoAlias alias_key;
+ struct PangoAlias *alias;
+ char **new_families;
+ int n_new;
+ int i;
+
+ file = g_fopen (filename, "r");
+ if (!file)
+ return;
+
+ line_buffer = g_string_new (NULL);
+ tmp_buffer1 = g_string_new (NULL);
+ tmp_buffer2 = g_string_new (NULL);
+
+ while (pango_read_line (file, line_buffer))
+ {
+ gboolean append = FALSE;
+ line++;
+
+ pos = line_buffer->str;
+ if (!pango_skip_space (&pos))
+ continue;
+
+ if (!pango_scan_word (&pos, tmp_buffer1) ||
+ !pango_skip_space (&pos))
+ {
+ errstring = g_strdup ("Line is not of the form KEY=VALUE or KEY+=VALUE");
+ goto error;
+ }
+
+ if (*pos == '+')
+ {
+ append = TRUE;
+ pos++;
+ }
+
+ if (*(pos++) != '=')
+ {
+ errstring = g_strdup ("Line is not of the form KEY=VALUE or KEY+=VALUE");
+ goto error;
+ }
+
+ if (!pango_scan_string (&pos, tmp_buffer2))
+ {
+ errstring = g_strdup ("Error parsing value string");
+ goto error;
+ }
+ if (pango_skip_space (&pos))
+ {
+ errstring = g_strdup ("Junk after value string");
+ goto error;
+ }
+
+ alias_key.alias = g_ascii_strdown (tmp_buffer1->str, -1);
+
+ /* Remove any existing values */
+ alias = g_hash_table_lookup (pango_aliases_ht, &alias_key);
+
+ if (!alias)
+ {
+ alias = g_slice_new0 (struct PangoAlias);
+ alias->alias = alias_key.alias;
+
+ g_hash_table_insert (pango_aliases_ht,
+ alias, alias);
+ }
+ else
+ g_free (alias_key.alias);
+
+ new_families = g_strsplit (tmp_buffer2->str, ",", -1);
+
+ n_new = 0;
+ while (new_families[n_new])
+ n_new++;
+
+ if (alias->families && append)
+ {
+ alias->families = g_realloc (alias->families,
+ sizeof (char *) *(n_new + alias->n_families));
+ for (i = 0; i < n_new; i++)
+ alias->families[alias->n_families + i] = new_families[i];
+ g_free (new_families);
+ alias->n_families += n_new;
+ }
+ else
+ {
+ for (i = 0; i < alias->n_families; i++)
+ g_free (alias->families[i]);
+ g_free (alias->families);
+
+ alias->families = new_families;
+ alias->n_families = n_new;
+ }
+ }
+
+ if (ferror (file))
+ errstring = g_strdup (g_strerror(errno));
+
+ error:
+
+ if (errstring)
+ {
+ g_warning ("error reading alias file: %s:%d: %s\n", filename, line, errstring);
+ g_free (errstring);
+ }
+
+ g_string_free (line_buffer, TRUE);
+ g_string_free (tmp_buffer1, TRUE);
+ g_string_free (tmp_buffer2, TRUE);
+
+ fclose (file);
+}
+
+static void
+pango_load_aliases (void)
+{
+ char *filename;
+ const char *home;
+
+ pango_aliases_ht = g_hash_table_new_full ((GHashFunc)alias_hash,
+ (GEqualFunc)alias_equal,
+ (GDestroyNotify)alias_free,
+ NULL);
+
+
+ filename = g_strconcat (pango_get_sysconf_subdirectory (),
+ G_DIR_SEPARATOR_S "pango.aliases",
+ NULL);
+ read_alias_file (filename);
+ g_free (filename);
+
+ home = g_get_home_dir ();
+ if (home && *home)
+ {
+ filename = g_strconcat (home,
+ G_DIR_SEPARATOR_S ".pango.aliases",
+ NULL);
+ read_alias_file (filename);
+ g_free (filename);
+ }
+}
+
+
+/**
+ * pango_lookup_aliases:
+ * @fontname: an ascii string
+ * @families: will be set to an array of font family names.
+ * this array is owned by pango and should not be freed.
+ * @n_families: will be set to the length of the @families array.
+ *
+ * Look up all user defined aliases for the alias @fontname.
+ * The resulting font family names will be stored in @families,
+ * and the number of families in @n_families.
+ **/
+void
+pango_lookup_aliases (const char *fontname,
+ char ***families,
+ int *n_families)
+{
+ struct PangoAlias alias_key;
+ struct PangoAlias *alias;
+
+ if (pango_aliases_ht == NULL)
+ pango_load_aliases ();
+
+ alias_key.alias = g_ascii_strdown (fontname, -1);
+ alias = g_hash_table_lookup (pango_aliases_ht, &alias_key);
+ g_free (alias_key.alias);
+
+ if (alias)
+ {
+ *families = alias->families;
+ *n_families = alias->n_families;
+ }
+ else
+ {
+ *families = NULL;
+ *n_families = 0;
+ }
+}
+
+/**
+ * pango_find_base_dir:
+ * @text: the text to process
+ * @length: length of @text in bytes (may be -1 if @text is nul-terminated)
+ *
+ * Searches a string the first character that has a strong
+ * direction, according to the Unicode bidirectional algorithm.
+ *
+ * Return value: The direction corresponding to the first strong character.
+ * If no such character is found, then %PANGO_DIRECTION_NEUTRAL is returned.
+ *
+ * Since: 1.4
+ */
+PangoDirection
+pango_find_base_dir (const gchar *text,
+ gint length)
+{
+ PangoDirection dir = PANGO_DIRECTION_NEUTRAL;
+ const gchar *p;
+
+ g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL);
+
+ p = text;
+ while ((length < 0 || p < text + length) && *p)
+ {
+ gunichar wc = g_utf8_get_char (p);
+
+ dir = pango_unichar_direction (wc);
+
+ if (dir != PANGO_DIRECTION_NEUTRAL)
+ break;
+
+ p = g_utf8_next_char (p);
+ }
+
+ return dir;
+}
+
+/**
+ * pango_is_zero_width:
+ * @ch: a Unicode character
+ *
+ * Checks @ch to see if it is a character that should not be
+ * normally rendered on the screen. This includes all Unicode characters
+ * with "ZERO WIDTH" in their name, as well as <firstterm>bidi</firstterm> formatting characters, and
+ * a few other ones. This is totally different from g_unichar_iszerowidth()
+ * and is at best misnamed.
+ *
+ * Return value: %TRUE if @ch is a zero-width character, %FALSE otherwise
+ *
+ * Since: 1.10
+ */
+gboolean
+pango_is_zero_width (gunichar ch)
+{
+/* Zero Width characters:
+ *
+ * 00AD SOFT HYPHEN
+ * 034F COMBINING GRAPHEME JOINER
+ *
+ * 200B ZERO WIDTH SPACE
+ * 200C ZERO WIDTH NON-JOINER
+ * 200D ZERO WIDTH JOINER
+ * 200E LEFT-TO-RIGHT MARK
+ * 200F RIGHT-TO-LEFT MARK
+ *
+ * 2028 LINE SEPARATOR
+ *
+ * 202A LEFT-TO-RIGHT EMBEDDING
+ * 202B RIGHT-TO-LEFT EMBEDDING
+ * 202C POP DIRECTIONAL FORMATTING
+ * 202D LEFT-TO-RIGHT OVERRIDE
+ * 202E RIGHT-TO-LEFT OVERRIDE
+ *
+ * 2060 WORD JOINER
+ * 2061 FUNCTION APPLICATION
+ * 2062 INVISIBLE TIMES
+ * 2063 INVISIBLE SEPARATOR
+ *
+ * FEFF ZERO WIDTH NO-BREAK SPACE
+ */
+ return ((ch & ~(gunichar)0x007F) == 0x2000 && (
+ (ch >= 0x200B && ch <= 0x200F) ||
+ (ch >= 0x202A && ch <= 0x202E) ||
+ (ch >= 0x2060 && ch <= 0x2063) ||
+ (ch == 0x2028)
+ )) || G_UNLIKELY (ch == 0x00AD
+ || ch == 0x034F
+ || ch == 0xFEFF);
+}
+
+/**
+ * pango_quantize_line_geometry:
+ * @thickness: pointer to the thickness of a line, in Pango scaled units
+ * @position: corresponding position
+ *
+ * Quantizes the thickness and position of a line, typically an
+ * underline or strikethrough, to whole device pixels, that is integer
+ * multiples of %PANGO_SCALE. The purpose of this function is to avoid
+ * such lines looking blurry.
+ *
+ * Since: 1.12
+ */
+void
+pango_quantize_line_geometry (int *thickness,
+ int *position)
+{
+ int thickness_pixels = (*thickness + PANGO_SCALE / 2) / PANGO_SCALE;
+ if (thickness_pixels == 0)
+ thickness_pixels = 1;
+
+ if (thickness_pixels & 1)
+ {
+ int new_center = ((*position - *thickness / 2) & ~(PANGO_SCALE - 1)) + PANGO_SCALE / 2;
+ *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
+ }
+ else
+ {
+ int new_center = ((*position - *thickness / 2 + PANGO_SCALE / 2) & ~(PANGO_SCALE - 1));
+ *position = new_center + (PANGO_SCALE * thickness_pixels) / 2;
+ }
+
+ *thickness = thickness_pixels * PANGO_SCALE;
+}
+
+/**
+ * pango_units_from_double:
+ * @d: double floating-point value
+ *
+ * Converts a floating-point number to Pango units: multiplies
+ * it by %PANGO_SCALE and rounds to nearest integer.
+ *
+ * Return value: the value in Pango units.
+ *
+ * Since: 1.16
+ */
+int
+pango_units_from_double (double d)
+{
+ return (int)floor (d * PANGO_SCALE + 0.5);
+}
+
+/**
+ * pango_units_to_double:
+ * @i: value in Pango units
+ *
+ * Converts a number in Pango units to floating-point: divides
+ * it by %PANGO_SCALE.
+ *
+ * Return value: the double value.
+ *
+ * Since: 1.16
+ */
+double
+pango_units_to_double (int i)
+{
+ return (double)i / PANGO_SCALE;
+}
+
+/**
+ * pango_extents_to_pixels:
+ * @ink_rect: ink rectangle to convert, or %NULL.
+ * @logical_rect: logical rectangle to convert, or %NULL.
+ *
+ * Converts extents from Pango units to device units, dividing by the
+ * %PANGO_SCALE factor and performing rounding.
+ *
+ * The ink rectangle is converted by flooring the x/y coordinates and extending
+ * width/height, such that the final rectangle completely includes the original
+ * rectangle.
+ *
+ * The logical rectangle is converted by rounding the coordinates
+ * of the rectangle to the nearest device unit.
+ *
+ * Note that in certain situations you may want pass a logical extents
+ * rectangle to this function as @ink_rect. The rule is: if you want the
+ * resulting device-space rectangle to completely contain the original
+ * rectangle, pass it in as @ink_rect.
+ *
+ * Since: 1.16
+ **/
+void
+pango_extents_to_pixels (PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ if (ink_rect)
+ {
+ int orig_x = ink_rect->x;
+ int orig_y = ink_rect->y;
+
+ ink_rect->x = PANGO_PIXELS_FLOOR (ink_rect->x);
+ ink_rect->y = PANGO_PIXELS_FLOOR (ink_rect->y);
+
+ ink_rect->width = PANGO_PIXELS_CEIL (orig_x + ink_rect->width ) - ink_rect->x;
+ ink_rect->height = PANGO_PIXELS_CEIL (orig_y + ink_rect->height) - ink_rect->y;
+ }
+
+ if (logical_rect)
+ {
+ int orig_x = logical_rect->x;
+ int orig_y = logical_rect->y;
+
+ logical_rect->x = PANGO_PIXELS (logical_rect->x);
+ logical_rect->y = PANGO_PIXELS (logical_rect->y);
+
+ logical_rect->width = PANGO_PIXELS (orig_x + logical_rect->width ) - logical_rect->x;
+ logical_rect->height = PANGO_PIXELS (orig_y + logical_rect->height) - logical_rect->y;
+ }
+}