summaryrefslogtreecommitdiff
path: root/trunk/pango/pangowin32-fontmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/pango/pangowin32-fontmap.c')
-rw-r--r--trunk/pango/pangowin32-fontmap.c1334
1 files changed, 1334 insertions, 0 deletions
diff --git a/trunk/pango/pangowin32-fontmap.c b/trunk/pango/pangowin32-fontmap.c
new file mode 100644
index 00000000..0f79dd7f
--- /dev/null
+++ b/trunk/pango/pangowin32-fontmap.c
@@ -0,0 +1,1334 @@
+/* Pango
+ * pangowin32-fontmap.c: Win32 font handling
+ *
+ * Copyright (C) 2000 Red Hat Software
+ * Copyright (C) 2000 Tor Lillqvist
+ * Copyright (C) 2001 Alexander Larsson
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "pango-fontmap.h"
+#include "pango-impl-utils.h"
+#include "pangowin32-private.h"
+#include "modules.h"
+
+typedef struct _PangoWin32Family PangoWin32Family;
+typedef struct _PangoWin32SizeInfo PangoWin32SizeInfo;
+
+struct _PangoWin32Family
+{
+ PangoFontFamily parent_instance;
+
+ char *family_name;
+ GSList *font_entries;
+
+ gboolean is_monospace;
+};
+
+struct _PangoWin32SizeInfo
+{
+ GSList *logfontws;
+};
+
+#define PANGO_WIN32_TYPE_FAMILY (pango_win32_family_get_type ())
+#define PANGO_WIN32_FAMILY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_WIN32_TYPE_FAMILY, PangoWin32Family))
+#define PANGO_WIN32_IS_FAMILY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_WIN32_TYPE_FAMILY))
+
+#define PANGO_WIN32_TYPE_FACE (pango_win32_face_get_type ())
+#define PANGO_WIN32_FACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_WIN32_TYPE_FACE, PangoWin32Face))
+#define PANGO_WIN32_IS_FACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_WIN32_TYPE_FACE))
+
+static GType pango_win32_face_get_type (void);
+
+static GType pango_win32_family_get_type (void);
+
+static void pango_win32_face_list_sizes (PangoFontFace *face,
+ int **sizes,
+ int *n_sizes);
+
+static void pango_win32_font_map_finalize (GObject *object);
+static PangoFont *pango_win32_font_map_load_font (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description);
+static void pango_win32_font_map_list_families (PangoFontMap *fontmap,
+ PangoFontFamily ***families,
+ int *n_families);
+
+static PangoFont *pango_win32_font_map_real_find_font (PangoWin32FontMap *win32fontmap,
+ PangoContext *context,
+ PangoWin32Face *face,
+ const PangoFontDescription *description);
+
+static void pango_win32_fontmap_cache_clear (PangoWin32FontMap *win32fontmap);
+
+static void pango_win32_insert_font (PangoWin32FontMap *fontmap,
+ LOGFONTW *lfp,
+ gboolean is_synthetic);
+
+static PangoWin32FontMap *default_fontmap = NULL;
+
+G_DEFINE_TYPE (PangoWin32FontMap, _pango_win32_font_map, PANGO_TYPE_FONT_MAP)
+
+#define TOLOWER(c) \
+ (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+
+static guint
+case_insensitive_str_hash (const char *key)
+{
+ const char *p = key;
+ guint h = TOLOWER (*p);
+
+ if (h)
+ {
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + TOLOWER (*p);
+ }
+
+ return h;
+}
+
+static gboolean
+case_insensitive_str_equal (const char *key1,
+ const char *key2)
+{
+ while (*key1 && *key2 && TOLOWER (*key1) == TOLOWER (*key2))
+ key1++, key2++;
+ return (!*key1 && !*key2);
+}
+
+static guint
+case_insensitive_wcs_hash (const wchar_t *key)
+{
+ const wchar_t *p = key;
+ guint h = TOLOWER (*p);
+
+ if (h)
+ {
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + TOLOWER (*p);
+ }
+
+ return h;
+}
+
+static gboolean
+case_insensitive_wcs_equal (const wchar_t *key1,
+ const wchar_t *key2)
+{
+ while (*key1 && *key2 && TOLOWER (*key1) == TOLOWER (*key2))
+ key1++, key2++;
+ return (!*key1 && !*key2);
+}
+
+/* A hash function for LOGFONTWs that takes into consideration only
+ * those fields that indicate a specific .ttf file is in use:
+ * lfFaceName, lfItalic and lfWeight. Dunno how correct this is.
+ */
+static guint
+logfontw_nosize_hash (const LOGFONTW *lfp)
+{
+ return case_insensitive_wcs_hash (lfp->lfFaceName) + (lfp->lfItalic != 0) + lfp->lfWeight;
+}
+
+/* Ditto comparison function */
+static gboolean
+logfontw_nosize_equal (const LOGFONTW *lfp1,
+ const LOGFONTW *lfp2)
+{
+ return (case_insensitive_wcs_equal (lfp1->lfFaceName, lfp2->lfFaceName)
+ && (lfp1->lfItalic != 0) == (lfp2->lfItalic != 0)
+ && lfp1->lfWeight == lfp2->lfWeight);
+}
+
+static int CALLBACK
+pango_win32_inner_enum_proc (LOGFONTW *lfp,
+ TEXTMETRICW *metrics,
+ DWORD fontType,
+ LPARAM lParam)
+{
+ PangoWin32FontMap *win32fontmap = (PangoWin32FontMap *)lParam;
+
+ /* Windows generates synthetic vertical writing versions of East
+ * Asian fonts with @ prepended to their name, ignore them.
+ */
+ if (lfp->lfFaceName[0] != '@')
+ pango_win32_insert_font (win32fontmap, lfp, FALSE);
+
+ return 1;
+}
+
+static int CALLBACK
+pango_win32_enum_proc (LOGFONTW *lfp,
+ TEXTMETRICW *metrics,
+ DWORD fontType,
+ LPARAM lParam)
+{
+ LOGFONTW lf;
+
+ PING(("%S", lfp->lfFaceName));
+
+ if (fontType != TRUETYPE_FONTTYPE)
+ return 1;
+
+ lf = *lfp;
+
+ EnumFontFamiliesExW (_pango_win32_hdc, &lf,
+ (FONTENUMPROCW) pango_win32_inner_enum_proc,
+ lParam, 0);
+
+ return 1;
+}
+
+typedef struct _ItalicHelper
+{
+ PangoWin32FontMap *fontmap;
+ GSList *list;
+} ItalicHelper;
+
+static void
+ensure_italic (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ ItalicHelper *helper = (ItalicHelper *)user_data;
+ PangoWin32SizeInfo *sip = (PangoWin32SizeInfo *) value;
+ GSList *list = sip->logfontws;
+
+ while (list)
+ {
+ LOGFONTW *lfp = (LOGFONTW *) list->data;
+ PING(("%S it=%d wt=%ld", lfp->lfFaceName, lfp->lfItalic, lfp->lfWeight));
+ if (!lfp->lfItalic)
+ {
+ /* we have a non italic variant, look if there is an italic */
+ LOGFONTW logfontw = *lfp;
+ logfontw.lfItalic = 1;
+ sip = (PangoWin32SizeInfo *) g_hash_table_lookup (helper->fontmap->size_infos, &logfontw);
+ if (!sip)
+ {
+ /* remember the non italic variant to be added later as italic */
+ PING(("synthesizing italic"));
+ helper->list = g_slist_append (helper->list, lfp);
+ }
+ }
+ list = list->next;
+ }
+}
+
+static void
+_pango_win32_font_map_init (PangoWin32FontMap *win32fontmap)
+{
+ LOGFONTW logfont;
+ ItalicHelper helper = { win32fontmap, NULL };
+ GSList *list;
+
+ win32fontmap->families = g_hash_table_new ((GHashFunc) case_insensitive_str_hash,
+ (GEqualFunc) case_insensitive_str_equal);
+ win32fontmap->size_infos =
+ g_hash_table_new ((GHashFunc) logfontw_nosize_hash, (GEqualFunc) logfontw_nosize_equal);
+ win32fontmap->n_fonts = 0;
+
+ win32fontmap->font_cache = pango_win32_font_cache_new ();
+ win32fontmap->freed_fonts = g_queue_new ();
+
+ memset (&logfont, 0, sizeof (logfont));
+ logfont.lfCharSet = DEFAULT_CHARSET;
+ EnumFontFamiliesExW (_pango_win32_hdc, &logfont,
+ (FONTENUMPROCW) pango_win32_enum_proc,
+ (LPARAM) win32fontmap, 0);
+
+ /* Create synthetic italic entries */
+ g_hash_table_foreach (win32fontmap->size_infos, ensure_italic, &helper);
+
+ /* Can't modify while iterating */
+ list = helper.list;
+ while (list)
+ {
+ LOGFONTW logfontw = *((LOGFONTW *)list->data);
+ logfontw.lfItalic = 1;
+ pango_win32_insert_font (win32fontmap, &logfontw, TRUE);
+ list = list->next;
+ }
+ g_slist_free (helper.list);
+
+ win32fontmap->resolution = (PANGO_SCALE / (double) GetDeviceCaps (_pango_win32_hdc, LOGPIXELSY)) * 72.0;
+}
+
+static void
+_pango_win32_font_map_class_init (PangoWin32FontMapClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (class);
+ int i;
+
+ class->find_font = pango_win32_font_map_real_find_font;
+ object_class->finalize = pango_win32_font_map_finalize;
+ fontmap_class->load_font = pango_win32_font_map_load_font;
+ fontmap_class->list_families = pango_win32_font_map_list_families;
+ fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_WIN32;
+
+ pango_win32_get_dc ();
+
+ for (i = 0; _pango_included_win32_modules[i].list; i++)
+ pango_module_register (&_pango_included_win32_modules[i]);
+}
+
+/**
+ * pango_win32_font_map_for_display:
+ *
+ * Returns a #PangoWin32FontMap. Font maps are cached and should
+ * not be freed. If the font map is no longer needed, it can
+ * be released with pango_win32_shutdown_display().
+ *
+ * Return value: a #PangoFontMap.
+ **/
+PangoFontMap *
+pango_win32_font_map_for_display (void)
+{
+ /* Make sure that the type system is initialized */
+ g_type_init ();
+
+ if (default_fontmap != NULL)
+ return PANGO_FONT_MAP (default_fontmap);
+
+ default_fontmap = g_object_new (PANGO_TYPE_WIN32_FONT_MAP, NULL);
+
+ return PANGO_FONT_MAP (default_fontmap);
+}
+
+/**
+ * pango_win32_shutdown_display:
+ *
+ * Free cached resources.
+ **/
+void
+pango_win32_shutdown_display (void)
+{
+ if (default_fontmap)
+ {
+ pango_win32_fontmap_cache_clear (default_fontmap);
+ g_object_unref (default_fontmap);
+
+ default_fontmap = NULL;
+ }
+}
+
+static void
+pango_win32_font_map_finalize (GObject *object)
+{
+ PangoWin32FontMap *win32fontmap = PANGO_WIN32_FONT_MAP (object);
+
+ g_list_foreach (win32fontmap->freed_fonts->head, (GFunc)g_object_unref, NULL);
+ g_queue_free (win32fontmap->freed_fonts);
+
+ pango_win32_font_cache_free (win32fontmap->font_cache);
+
+ G_OBJECT_CLASS (_pango_win32_font_map_parent_class)->finalize (object);
+}
+
+/*
+ * PangoWin32Family
+ */
+static void
+pango_win32_family_list_faces (PangoFontFamily *family,
+ PangoFontFace ***faces,
+ int *n_faces)
+{
+ PangoWin32Family *win32family = PANGO_WIN32_FAMILY (family);
+
+ *n_faces = g_slist_length (win32family->font_entries);
+ if (faces)
+ {
+ GSList *tmp_list;
+ int i = 0;
+
+ *faces = g_new (PangoFontFace *, *n_faces);
+
+ tmp_list = win32family->font_entries;
+ while (tmp_list)
+ {
+ (*faces)[i++] = tmp_list->data;
+ tmp_list = tmp_list->next;
+ }
+ }
+}
+
+static const char *
+pango_win32_family_get_name (PangoFontFamily *family)
+{
+ PangoWin32Family *win32family = PANGO_WIN32_FAMILY (family);
+ return win32family->family_name;
+}
+
+static gboolean
+pango_win32_family_is_monospace (PangoFontFamily *family)
+{
+ PangoWin32Family *win32family = PANGO_WIN32_FAMILY (family);
+
+ return win32family->is_monospace;
+}
+
+static void
+pango_win32_family_class_init (PangoFontFamilyClass *class)
+{
+ class->list_faces = pango_win32_family_list_faces;
+ class->get_name = pango_win32_family_get_name;
+ class->is_monospace = pango_win32_family_is_monospace;
+}
+
+static GType
+pango_win32_family_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (G_UNLIKELY (!object_type))
+ {
+ const GTypeInfo object_info =
+ {
+ sizeof (PangoFontFamilyClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) pango_win32_family_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoWin32Family),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ };
+
+ object_type = g_type_register_static (PANGO_TYPE_FONT_FAMILY,
+ I_("PangoWin32Family"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+list_families_foreach (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **list = user_data;
+
+ *list = g_slist_prepend (*list, value);
+}
+
+static void
+pango_win32_font_map_list_families (PangoFontMap *fontmap,
+ PangoFontFamily ***families,
+ int *n_families)
+{
+ GSList *family_list = NULL;
+ GSList *tmp_list;
+ PangoWin32FontMap *win32fontmap = (PangoWin32FontMap *)fontmap;
+
+ if (!n_families)
+ return;
+
+ g_hash_table_foreach (win32fontmap->families, list_families_foreach, &family_list);
+
+ *n_families = g_slist_length (family_list);
+
+ if (families)
+ {
+ int i = 0;
+
+ *families = g_new (PangoFontFamily *, *n_families);
+
+ tmp_list = family_list;
+ while (tmp_list)
+ {
+ (*families)[i] = tmp_list->data;
+ i++;
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ g_slist_free (family_list);
+}
+
+static PangoWin32Family *
+pango_win32_get_font_family (PangoWin32FontMap *win32fontmap,
+ const char *family_name)
+{
+ PangoWin32Family *win32family = g_hash_table_lookup (win32fontmap->families, family_name);
+ if (!win32family)
+ {
+ win32family = g_object_new (PANGO_WIN32_TYPE_FAMILY, NULL);
+ win32family->family_name = g_strdup (family_name);
+ win32family->font_entries = NULL;
+
+ g_hash_table_insert (win32fontmap->families, win32family->family_name, win32family);
+ }
+
+ return win32family;
+}
+
+static PangoFont *
+pango_win32_font_map_load_font (PangoFontMap *fontmap,
+ PangoContext *context,
+ const PangoFontDescription *description)
+{
+ PangoWin32FontMap *win32fontmap = (PangoWin32FontMap *)fontmap;
+ PangoWin32Family *win32family;
+ PangoFont *result = NULL;
+ GSList *tmp_list;
+
+ g_return_val_if_fail (description != NULL, NULL);
+
+ PING(("name=%s", pango_font_description_get_family (description)));
+
+ win32family = g_hash_table_lookup (win32fontmap->families,
+ pango_font_description_get_family (description));
+ if (win32family)
+ {
+ PangoWin32Face *best_match = NULL;
+
+ PING (("got win32family"));
+ tmp_list = win32family->font_entries;
+ while (tmp_list)
+ {
+ PangoWin32Face *face = tmp_list->data;
+
+ if (pango_font_description_better_match (description,
+ best_match ? best_match->description : NULL,
+ face->description))
+ best_match = face;
+
+ tmp_list = tmp_list->next;
+ }
+
+ if (best_match)
+ result = PANGO_WIN32_FONT_MAP_GET_CLASS (win32fontmap)->find_font (win32fontmap, context,
+ best_match,
+ description);
+ /* TODO: Handle the case that result == NULL. */
+ else
+ PING(("no best match!"));
+ }
+
+ return result;
+}
+
+static PangoWin32Font *
+pango_win32_font_neww (PangoFontMap *fontmap,
+ const LOGFONTW *lfp,
+ int size)
+{
+ PangoWin32Font *result;
+
+ g_return_val_if_fail (fontmap != NULL, NULL);
+ g_return_val_if_fail (lfp != NULL, NULL);
+
+ result = (PangoWin32Font *)g_object_new (PANGO_TYPE_WIN32_FONT, NULL);
+
+ result->fontmap = fontmap;
+ g_object_ref (fontmap);
+
+ result->size = size;
+ _pango_win32_make_matching_logfontw (fontmap, lfp, size, &result->logfontw);
+
+ return result;
+}
+
+static PangoFont *
+pango_win32_font_map_real_find_font (PangoWin32FontMap *win32fontmap,
+ PangoContext *context,
+ PangoWin32Face *face,
+ const PangoFontDescription *description)
+{
+ PangoFontMap *fontmap = PANGO_FONT_MAP (win32fontmap);
+ PangoWin32Font *win32font;
+ GSList *tmp_list = face->cached_fonts;
+ int size = pango_font_description_get_size (description);
+
+ if (pango_font_description_get_size_is_absolute (description))
+ size = (int) 0.5 + (size * win32fontmap->resolution) / PANGO_SCALE;
+
+ PING(("got best match:%S size=%d",face->logfontw.lfFaceName,size));
+
+ while (tmp_list)
+ {
+ win32font = tmp_list->data;
+ if (win32font->size == size)
+ {
+ PING (("size matches"));
+
+ g_object_ref (win32font);
+ if (win32font->in_cache)
+ _pango_win32_fontmap_cache_remove (fontmap, win32font);
+
+ return (PangoFont *)win32font;
+ }
+ tmp_list = tmp_list->next;
+ }
+
+ win32font = pango_win32_font_neww (fontmap, &face->logfontw, size);
+
+ if (!win32font)
+ return NULL;
+
+ win32font->fontmap = fontmap;
+ win32font->win32face = face;
+ face->cached_fonts = g_slist_prepend (face->cached_fonts, win32font);
+
+ return (PangoFont *)win32font;
+}
+
+static gchar *
+get_family_nameA (const LOGFONTA *lfp)
+{
+ HFONT hfont;
+ HFONT oldhfont;
+
+ struct name_header header;
+ struct name_record record;
+
+ gint unicode_ix = -1, mac_ix = -1, microsoft_ix = -1;
+ gint name_ix;
+ gchar *codeset;
+
+ gchar *string = NULL;
+ gchar *name;
+
+ gint i, l;
+ gsize nbytes;
+
+ /* If lfFaceName is ASCII, assume it is the common (English) name
+ * for the font. Is this valid? Do some TrueType fonts have
+ * different names in French, German, etc, and does the system
+ * return these if the locale is set to use French, German, etc?
+ */
+ l = strlen (lfp->lfFaceName);
+ for (i = 0; i < l; i++)
+ if (lfp->lfFaceName[i] < ' ' || lfp->lfFaceName[i] > '~')
+ break;
+
+ if (i == l)
+ return g_strdup (lfp->lfFaceName);
+
+ if ((hfont = CreateFontIndirect (lfp)) == NULL)
+ goto fail0;
+
+ if ((oldhfont = SelectObject (_pango_win32_hdc, hfont)) == NULL)
+ goto fail1;
+
+ if (!_pango_win32_get_name_header (_pango_win32_hdc, &header))
+ goto fail2;
+
+ PING (("%d name records", header.num_records));
+
+ for (i = 0; i < header.num_records; i++)
+ {
+ if (!_pango_win32_get_name_record (_pango_win32_hdc, i, &record))
+ goto fail2;
+
+ if ((record.name_id != 1 && record.name_id != 16) || record.string_length <= 0)
+ continue;
+
+ PING(("platform:%d encoding:%d language:%04x name_id:%d",
+ record.platform_id, record.encoding_id, record.language_id, record.name_id));
+
+ if (record.platform_id == APPLE_UNICODE_PLATFORM_ID ||
+ record.platform_id == ISO_PLATFORM_ID)
+ unicode_ix = i;
+ else if (record.platform_id == MACINTOSH_PLATFORM_ID &&
+ record.encoding_id == 0 && /* Roman */
+ record.language_id == 0) /* English */
+ mac_ix = i;
+ else if (record.platform_id == MICROSOFT_PLATFORM_ID)
+ if ((microsoft_ix == -1 ||
+ PRIMARYLANGID (record.language_id) == LANG_ENGLISH) &&
+ (record.encoding_id == SYMBOL_ENCODING_ID ||
+ record.encoding_id == UNICODE_ENCODING_ID ||
+ record.encoding_id == UCS4_ENCODING_ID))
+ microsoft_ix = i;
+ }
+
+ if (microsoft_ix >= 0)
+ name_ix = microsoft_ix;
+ else if (mac_ix >= 0)
+ name_ix = mac_ix;
+ else if (unicode_ix >= 0)
+ name_ix = unicode_ix;
+ else
+ goto fail2;
+
+ if (!_pango_win32_get_name_record (_pango_win32_hdc, name_ix, &record))
+ goto fail2;
+
+ string = g_malloc (record.string_length + 1);
+ if (GetFontData (_pango_win32_hdc, NAME,
+ header.string_storage_offset + record.string_offset,
+ string, record.string_length) != record.string_length)
+ goto fail2;
+
+ string[record.string_length] = '\0';
+
+ if (name_ix == microsoft_ix)
+ if (record.encoding_id == SYMBOL_ENCODING_ID ||
+ record.encoding_id == UNICODE_ENCODING_ID ||
+ record.encoding_id == UCS4_ENCODING_ID)
+ codeset = "UTF-16BE";
+ else
+ codeset = "UCS-4BE";
+ else if (name_ix == mac_ix)
+ codeset = "MacRoman";
+ else /* name_ix == unicode_ix */
+ codeset = "UCS-4BE";
+
+ name = g_convert (string, record.string_length, "UTF-8", codeset, NULL, &nbytes, NULL);
+ if (name == NULL)
+ goto fail2;
+ g_free (string);
+
+ PING(("%s", name));
+
+ SelectObject (_pango_win32_hdc, oldhfont);
+ DeleteObject (hfont);
+
+ return name;
+
+ fail2:
+ g_free (string);
+ SelectObject (_pango_win32_hdc, oldhfont);
+
+ fail1:
+ DeleteObject (hfont);
+
+ fail0:
+ return g_locale_to_utf8 (lfp->lfFaceName, -1, NULL, NULL, NULL);
+}
+
+/**
+ * pango_win32_font_description_from_logfont:
+ * @lfp: a LOGFONTA
+ *
+ * Creates a #PangoFontDescription that matches the specified LOGFONTA.
+ *
+ * The face name, italicness and weight fields in the LOGFONTA are used
+ * to set up the resulting #PangoFontDescription. If the face name in
+ * the LOGFONTA contains non-ASCII characters the font is temporarily
+ * loaded (using CreateFontIndirect()) and an ASCII (usually English)
+ * name for it is looked up from the font name tables in the font
+ * data. If that doesn't work, the face name is converted from the
+ * system codepage to UTF-8 and that is used.
+ *
+ * Return value: the newly allocated #PangoFontDescription, which
+ * should be freed using pango_font_description_free()
+ *
+ * Since: 1.12
+ */
+PangoFontDescription *
+pango_win32_font_description_from_logfont (const LOGFONT *lfp)
+{
+ PangoFontDescription *description;
+ gchar *family;
+ PangoStyle style;
+ PangoVariant variant;
+ PangoWeight weight;
+ PangoStretch stretch;
+
+ family = get_family_nameA (lfp);
+
+ if (!lfp->lfItalic)
+ style = PANGO_STYLE_NORMAL;
+ else
+ style = PANGO_STYLE_ITALIC;
+
+ variant = PANGO_VARIANT_NORMAL;
+
+ /* The PangoWeight values PANGO_WEIGHT_* map exactly do Windows FW_*
+ * values. Is this on purpose? Quantize the weight to exact
+ * PANGO_WEIGHT_* values. Is this a good idea?
+ */
+ if (lfp->lfWeight == FW_DONTCARE)
+ weight = PANGO_WEIGHT_NORMAL;
+ else if (lfp->lfWeight <= (FW_ULTRALIGHT + FW_LIGHT) / 2)
+ weight = PANGO_WEIGHT_ULTRALIGHT;
+ else if (lfp->lfWeight <= (FW_LIGHT + FW_NORMAL) / 2)
+ weight = PANGO_WEIGHT_LIGHT;
+ else if (lfp->lfWeight <= (FW_NORMAL + FW_BOLD) / 2)
+ weight = PANGO_WEIGHT_NORMAL;
+ else if (lfp->lfWeight <= (FW_BOLD + FW_ULTRABOLD) / 2)
+ weight = PANGO_WEIGHT_BOLD;
+ else if (lfp->lfWeight <= (FW_ULTRABOLD + FW_HEAVY) / 2)
+ weight = PANGO_WEIGHT_ULTRABOLD;
+ else
+ weight = PANGO_WEIGHT_HEAVY;
+
+ /* XXX No idea how to figure out the stretch */
+ stretch = PANGO_STRETCH_NORMAL;
+
+ description = pango_font_description_new ();
+ pango_font_description_set_family (description, family);
+ pango_font_description_set_style (description, style);
+ pango_font_description_set_weight (description, weight);
+ pango_font_description_set_stretch (description, stretch);
+ pango_font_description_set_variant (description, variant);
+
+ return description;
+}
+
+static gchar *
+get_family_nameW (const LOGFONTW *lfp)
+{
+ HFONT hfont;
+ HFONT oldhfont;
+
+ struct name_header header;
+ struct name_record record;
+
+ gint unicode_ix = -1, mac_ix = -1, microsoft_ix = -1;
+ gint name_ix;
+ gchar *codeset;
+
+ gchar *string = NULL;
+ gchar *name;
+
+ gint i, l;
+ gsize nbytes;
+
+ /* If lfFaceName is ASCII, assume it is the common (English) name
+ * for the font. Is this valid? Do some TrueType fonts have
+ * different names in French, German, etc, and does the system
+ * return these if the locale is set to use French, German, etc?
+ */
+ l = wcslen (lfp->lfFaceName);
+ for (i = 0; i < l; i++)
+ if (lfp->lfFaceName[i] < ' ' || lfp->lfFaceName[i] > '~')
+ break;
+
+ if (i == l)
+ return g_utf16_to_utf8 (lfp->lfFaceName, -1, NULL, NULL, NULL);
+
+ if ((hfont = CreateFontIndirectW (lfp)) == NULL)
+ goto fail0;
+
+ if ((oldhfont = SelectObject (_pango_win32_hdc, hfont)) == NULL)
+ goto fail1;
+
+ if (!_pango_win32_get_name_header (_pango_win32_hdc, &header))
+ goto fail2;
+
+ PING (("%d name records", header.num_records));
+
+ for (i = 0; i < header.num_records; i++)
+ {
+ if (!_pango_win32_get_name_record (_pango_win32_hdc, i, &record))
+ goto fail2;
+
+ if ((record.name_id != 1 && record.name_id != 16) || record.string_length <= 0)
+ continue;
+
+ PING(("platform:%d encoding:%d language:%04x name_id:%d",
+ record.platform_id, record.encoding_id, record.language_id, record.name_id));
+
+ if (record.platform_id == APPLE_UNICODE_PLATFORM_ID ||
+ record.platform_id == ISO_PLATFORM_ID)
+ unicode_ix = i;
+ else if (record.platform_id == MACINTOSH_PLATFORM_ID &&
+ record.encoding_id == 0 && /* Roman */
+ record.language_id == 0) /* English */
+ mac_ix = i;
+ else if (record.platform_id == MICROSOFT_PLATFORM_ID)
+ if ((microsoft_ix == -1 ||
+ PRIMARYLANGID (record.language_id) == LANG_ENGLISH) &&
+ (record.encoding_id == SYMBOL_ENCODING_ID ||
+ record.encoding_id == UNICODE_ENCODING_ID ||
+ record.encoding_id == UCS4_ENCODING_ID))
+ microsoft_ix = i;
+ }
+
+ if (microsoft_ix >= 0)
+ name_ix = microsoft_ix;
+ else if (mac_ix >= 0)
+ name_ix = mac_ix;
+ else if (unicode_ix >= 0)
+ name_ix = unicode_ix;
+ else
+ goto fail2;
+
+ if (!_pango_win32_get_name_record (_pango_win32_hdc, name_ix, &record))
+ goto fail2;
+
+ string = g_malloc (record.string_length + 1);
+ if (GetFontData (_pango_win32_hdc, NAME,
+ header.string_storage_offset + record.string_offset,
+ string, record.string_length) != record.string_length)
+ goto fail2;
+
+ string[record.string_length] = '\0';
+
+ if (name_ix == microsoft_ix)
+ if (record.encoding_id == SYMBOL_ENCODING_ID ||
+ record.encoding_id == UNICODE_ENCODING_ID ||
+ record.encoding_id == UCS4_ENCODING_ID)
+ codeset = "UTF-16BE";
+ else
+ codeset = "UCS-4BE";
+ else if (name_ix == mac_ix)
+ codeset = "MacRoman";
+ else /* name_ix == unicode_ix */
+ codeset = "UCS-4BE";
+
+ name = g_convert (string, record.string_length, "UTF-8", codeset, NULL, &nbytes, NULL);
+ if (name == NULL)
+ goto fail2;
+ g_free (string);
+
+ PING(("%s", name));
+
+ SelectObject (_pango_win32_hdc, oldhfont);
+ DeleteObject (hfont);
+
+ return name;
+
+ fail2:
+ g_free (string);
+ SelectObject (_pango_win32_hdc, oldhfont);
+
+ fail1:
+ DeleteObject (hfont);
+
+ fail0:
+ return g_utf16_to_utf8 (lfp->lfFaceName, -1, NULL, NULL, NULL);
+}
+
+/**
+ * pango_win32_font_description_from_logfontw:
+ * @lfp: a LOGFONTW
+ *
+ * Creates a #PangoFontDescription that matches the specified LOGFONTW.
+ *
+ * The face name, italicness and weight fields in the LOGFONTW are used
+ * to set up the resulting #PangoFontDescription. If the face name in
+ * the LOGFONTW contains non-ASCII characters the font is temporarily
+ * loaded (using CreateFontIndirect()) and an ASCII (usually English)
+ * name for it is looked up from the font name tables in the font
+ * data. If that doesn't work, the face name is converted from UTF-16
+ * to UTF-8 and that is used.
+ *
+ * Return value: the newly allocated #PangoFontDescription, which
+ * should be freed using pango_font_description_free()
+ *
+ * Since: 1.16
+ */
+PangoFontDescription *
+pango_win32_font_description_from_logfontw (const LOGFONTW *lfp)
+{
+ PangoFontDescription *description;
+ gchar *family;
+ PangoStyle style;
+ PangoVariant variant;
+ PangoWeight weight;
+ PangoStretch stretch;
+
+ family = get_family_nameW (lfp);
+
+ if (!lfp->lfItalic)
+ style = PANGO_STYLE_NORMAL;
+ else
+ style = PANGO_STYLE_ITALIC;
+
+ variant = PANGO_VARIANT_NORMAL;
+
+ /* The PangoWeight values PANGO_WEIGHT_* map exactly do Windows FW_*
+ * values. Is this on purpose? Quantize the weight to exact
+ * PANGO_WEIGHT_* values. Is this a good idea?
+ */
+ if (lfp->lfWeight == FW_DONTCARE)
+ weight = PANGO_WEIGHT_NORMAL;
+ else if (lfp->lfWeight <= (FW_ULTRALIGHT + FW_LIGHT) / 2)
+ weight = PANGO_WEIGHT_ULTRALIGHT;
+ else if (lfp->lfWeight <= (FW_LIGHT + FW_NORMAL) / 2)
+ weight = PANGO_WEIGHT_LIGHT;
+ else if (lfp->lfWeight <= (FW_NORMAL + FW_BOLD) / 2)
+ weight = PANGO_WEIGHT_NORMAL;
+ else if (lfp->lfWeight <= (FW_BOLD + FW_ULTRABOLD) / 2)
+ weight = PANGO_WEIGHT_BOLD;
+ else if (lfp->lfWeight <= (FW_ULTRABOLD + FW_HEAVY) / 2)
+ weight = PANGO_WEIGHT_ULTRABOLD;
+ else
+ weight = PANGO_WEIGHT_HEAVY;
+
+ /* XXX No idea how to figure out the stretch */
+ stretch = PANGO_STRETCH_NORMAL;
+
+ description = pango_font_description_new ();
+ pango_font_description_set_family (description, family);
+ pango_font_description_set_style (description, style);
+ pango_font_description_set_weight (description, weight);
+ pango_font_description_set_stretch (description, stretch);
+ pango_font_description_set_variant (description, variant);
+
+ return description;
+}
+
+
+/* This inserts the given font into the size_infos table. If a
+ * SizeInfo already exists with the same typeface name, italicness and
+ * weight, then the font is added to the SizeInfo's list, else a
+ * new SizeInfo is created and inserted in the table.
+ */
+static void
+pango_win32_insert_font (PangoWin32FontMap *win32fontmap,
+ LOGFONTW *lfp,
+ gboolean is_synthetic)
+{
+ LOGFONTW *lfp2 = NULL;
+ PangoFontDescription *description;
+ PangoWin32Family *font_family;
+ PangoWin32Face *win32face;
+ PangoWin32SizeInfo *size_info;
+ GSList *tmp_list;
+ gint i;
+ gchar *p;
+
+ PING(("face=%S,charset=%d,it=%d,wt=%ld,ht=%ld",lfp->lfFaceName,lfp->lfCharSet,lfp->lfItalic,lfp->lfWeight,lfp->lfHeight));
+
+ /* Ignore Symbol fonts (which don't have any Unicode mapping
+ * table). We could also be fancy and use the PostScript glyph name
+ * table for such if present, and build a Unicode map by mapping
+ * each PostScript glyph name to Unicode character. Oh well.
+ */
+ if (lfp->lfCharSet == SYMBOL_CHARSET)
+ return;
+
+ /* First insert the LOGFONTW into the list of LOGFONTWs for the
+ * typeface name, italicness and weight.
+ */
+ size_info = g_hash_table_lookup (win32fontmap->size_infos, lfp);
+ if (!size_info)
+ {
+ PING(("SizeInfo not found"));
+ size_info = g_new (PangoWin32SizeInfo, 1);
+ size_info->logfontws = NULL;
+
+ lfp2 = g_new (LOGFONTW, 1);
+ *lfp2 = *lfp;
+ g_hash_table_insert (win32fontmap->size_infos, lfp2, size_info);
+ }
+ else
+ {
+ /* Don't store LOGFONTWs that differ only in charset
+ */
+ tmp_list = size_info->logfontws;
+ while (tmp_list)
+ {
+ LOGFONTW *rover = tmp_list->data;
+
+ /* We know that lfWeight, lfItalic and lfFaceName match. We
+ * don't check lfHeight and lfWidth, those are used
+ * when creating a font.
+ */
+ if (rover->lfEscapement == lfp->lfEscapement &&
+ rover->lfOrientation == lfp->lfOrientation &&
+ rover->lfUnderline == lfp->lfUnderline &&
+ rover->lfStrikeOut == lfp->lfStrikeOut)
+ {
+ PING(("already have it"));
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ if (lfp2 == NULL)
+ {
+ lfp2 = g_new (LOGFONTW, 1);
+ *lfp2 = *lfp;
+ }
+
+ size_info->logfontws = g_slist_prepend (size_info->logfontws, lfp2);
+
+ PING(("g_slist_length(size_info->logfontws)=%d", g_slist_length(size_info->logfontws)));
+
+ description = pango_win32_font_description_from_logfontw (lfp2);
+
+ /* In some cases, extracting a name for a font can fail; such fonts
+ * aren't usable for us
+ */
+ if (!pango_font_description_get_family (description))
+ {
+ pango_font_description_free (description);
+ return;
+ }
+
+ win32face = g_object_new (PANGO_WIN32_TYPE_FACE, NULL);
+
+ win32face->logfontw = *lfp;
+ win32face->description = description;
+
+ for (i = 0; i < PANGO_WIN32_N_COVERAGES; i++)
+ win32face->coverages[i] = NULL;
+
+ win32face->is_synthetic = is_synthetic;
+
+ win32face->cmap_format = 0;
+ win32face->cmap = NULL;
+
+ win32face->cached_fonts = NULL;
+
+ font_family =
+ pango_win32_get_font_family (win32fontmap,
+ pango_font_description_get_family (win32face->description));
+ font_family->font_entries = g_slist_append (font_family->font_entries, win32face);
+ PING(("g_slist_length(font_family->font_entries)=%d", g_slist_length(font_family->font_entries)));
+
+ win32fontmap->n_fonts++;
+
+#if 1 /* Thought pango.aliases would make this code unnecessary, but no. */
+ /*
+ * There are magic family names coming from the X implementation.
+ * They can be simply mapped to lfPitchAndFamily flag of the logfont
+ * struct. These additional entries should probably only be references
+ * to the respective entry created above. Thy are simply using the
+ * same entry at the moment and it isn't crashing on g_free () ???
+ * Maybe a memory leak ...
+ */
+ switch (lfp->lfPitchAndFamily & 0xF0)
+ {
+ case FF_MODERN : /* monospace */
+ PING(("monospace"));
+ font_family->is_monospace = TRUE; /* modify before reuse */
+ font_family = pango_win32_get_font_family (win32fontmap, "monospace");
+ font_family->font_entries = g_slist_append (font_family->font_entries, win32face);
+ win32fontmap->n_fonts++;
+ break;
+ case FF_ROMAN : /* serif */
+ PING(("serif"));
+ font_family = pango_win32_get_font_family (win32fontmap, "serif");
+ font_family->font_entries = g_slist_append (font_family->font_entries, win32face);
+ win32fontmap->n_fonts++;
+ break;
+ case FF_SWISS : /* sans */
+ PING(("sans"));
+ font_family = pango_win32_get_font_family (win32fontmap, "sans");
+ font_family->font_entries = g_slist_append (font_family->font_entries, win32face);
+ win32fontmap->n_fonts++;
+ break;
+ }
+
+ /* Some other magic names */
+
+ /* Recognize just "courier" for "courier new" */
+ p = g_utf16_to_utf8 (win32face->logfontw.lfFaceName, -1, NULL, NULL, NULL);
+ if (p && g_ascii_strcasecmp (p, "courier new") == 0)
+ {
+ font_family = pango_win32_get_font_family (win32fontmap, "courier");
+ font_family->font_entries = g_slist_append (font_family->font_entries, win32face);
+ win32fontmap->n_fonts++;
+ }
+ g_free (p);
+#endif
+}
+
+/* Given a LOGFONTW and size, make a matching LOGFONTW corresponding to
+ * an installed font.
+ */
+void
+_pango_win32_make_matching_logfontw (PangoFontMap *fontmap,
+ const LOGFONTW *lfp,
+ int size,
+ LOGFONTW *out)
+{
+ PangoWin32FontMap *win32fontmap;
+ GSList *tmp_list;
+ PangoWin32SizeInfo *size_info;
+ LOGFONTW *closest_match = NULL;
+ gint match_distance = 0;
+
+ PING(("lfp.face=%S,wt=%ld,ht=%ld,size:%d",lfp->lfFaceName,lfp->lfWeight,lfp->lfHeight,size));
+ win32fontmap = PANGO_WIN32_FONT_MAP (fontmap);
+
+ size_info = g_hash_table_lookup (win32fontmap->size_infos, lfp);
+
+ if (!size_info)
+ {
+ PING(("SizeInfo not found"));
+ return;
+ }
+
+ tmp_list = size_info->logfontws;
+ while (tmp_list)
+ {
+ LOGFONTW *tmp_logfontw = tmp_list->data;
+ int font_size = abs (tmp_logfontw->lfHeight);
+
+ if (size != -1)
+ {
+ int new_distance = (font_size == 0) ? 0 : abs (font_size - size);
+
+ if (!closest_match ||
+ new_distance < match_distance ||
+ (new_distance < PANGO_SCALE && font_size != 0))
+ {
+ closest_match = tmp_logfontw;
+ match_distance = new_distance;
+ }
+ }
+
+ tmp_list = tmp_list->next;
+ }
+
+ if (closest_match)
+ {
+ /* OK, we have a match; let's modify it to fit this size */
+
+ *out = *closest_match;
+ out->lfHeight = -(int)((double)size / win32fontmap->resolution + 0.5);
+ out->lfWidth = 0;
+ }
+ else
+ *out = *lfp; /* Whatever. We need to pass something... */
+}
+
+static PangoFontDescription *
+pango_win32_face_describe (PangoFontFace *face)
+{
+ PangoWin32Face *win32face = PANGO_WIN32_FACE (face);
+
+ return pango_font_description_copy (win32face->description);
+}
+
+static const char *
+pango_win32_face_get_face_name (PangoFontFace *face)
+{
+ PangoWin32Face *win32face = PANGO_WIN32_FACE (face);
+
+ if (!win32face->face_name)
+ {
+ PangoFontDescription *desc = pango_font_face_describe (face);
+
+ pango_font_description_unset_fields (desc,
+ PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_SIZE);
+
+ win32face->face_name = pango_font_description_to_string (desc);
+ pango_font_description_free (desc);
+ }
+
+ return win32face->face_name;
+}
+
+static gboolean
+pango_win32_face_is_synthesized (PangoFontFace *face)
+{
+ PangoWin32Face *win32face = PANGO_WIN32_FACE (face);
+
+ return win32face->is_synthetic;
+}
+
+static void
+pango_win32_face_class_init (PangoFontFaceClass *class)
+{
+ class->describe = pango_win32_face_describe;
+ class->get_face_name = pango_win32_face_get_face_name;
+ class->list_sizes = pango_win32_face_list_sizes;
+ class->is_synthesized = pango_win32_face_is_synthesized;
+}
+
+static void
+pango_win32_face_list_sizes (PangoFontFace *face,
+ int **sizes,
+ int *n_sizes)
+{
+ /*
+ * for scalable fonts it's simple, and currently we only have such
+ * see : pango_win32_enum_proc(), TRUETYPE_FONTTYPE
+ */
+ *sizes = NULL;
+ *n_sizes = 0;
+}
+
+static GType
+pango_win32_face_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (G_UNLIKELY (!object_type))
+ {
+ static const GTypeInfo object_info =
+ {
+ sizeof (PangoFontFaceClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) pango_win32_face_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoWin32Face),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL,
+ };
+
+ object_type = g_type_register_static (PANGO_TYPE_FONT_FACE,
+ I_("PangoWin32Face"),
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+/**
+ * pango_win32_font_map_get_font_cache:
+ * @font_map: a #PangoWin32FontMap.
+ *
+ * Obtains the font cache associated with the given font map.
+ *
+ * Return value: the #PangoWin32FontCache of @font_map.
+ **/
+PangoWin32FontCache *
+pango_win32_font_map_get_font_cache (PangoFontMap *font_map)
+{
+ g_return_val_if_fail (font_map != NULL, NULL);
+ g_return_val_if_fail (PANGO_WIN32_IS_FONT_MAP (font_map), NULL);
+
+ return PANGO_WIN32_FONT_MAP (font_map)->font_cache;
+}
+
+void
+_pango_win32_fontmap_cache_remove (PangoFontMap *fontmap,
+ PangoWin32Font *win32font)
+{
+ PangoWin32FontMap *win32fontmap = PANGO_WIN32_FONT_MAP (fontmap);
+ GList *link = g_queue_find (win32fontmap->freed_fonts, win32font);
+
+ if (link)
+ g_queue_delete_link (win32fontmap->freed_fonts, link);
+ win32font->in_cache = FALSE;
+
+ g_object_unref (win32font);
+}
+
+static void
+pango_win32_fontmap_cache_clear (PangoWin32FontMap *win32fontmap)
+{
+ g_list_foreach (win32fontmap->freed_fonts->head, (GFunc)g_object_unref, NULL);
+ g_queue_free (win32fontmap->freed_fonts);
+ win32fontmap->freed_fonts = g_queue_new ();
+}