/* 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 #include #include #include #include #include "pango-fontmap.h" #include "pango-impl-utils.h" #include "pangowin32-private.h" #include "modules.h" typedef struct _PangoWin32Family PangoWin32Family; struct _PangoWin32Family { PangoFontFamily parent_instance; char *family_name; GSList *faces; gboolean is_monospace; }; #if !defined(NTM_PS_OPENTYPE) # define NTM_PS_OPENTYPE 0x20000 #endif #if !defined(NTM_TYPE1) # define NTM_TYPE1 0x100000 #endif #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 PangoWin32Family *pango_win32_get_font_family (PangoWin32FontMap *win32fontmap, const char *family_name); static const char *pango_win32_face_get_face_name (PangoFontFace *face); 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, NEWTEXTMETRICW *metrics, DWORD fontType, LPARAM lParam) { LOGFONTW lf; PING (("%S: %lu %lx", lfp->lfFaceName, fontType, metrics->ntmFlags)); if (fontType == TRUETYPE_FONTTYPE || (_pango_win32_os_version_info.dwMajorVersion >= 5 && ((metrics->ntmFlags & NTM_PS_OPENTYPE) || (metrics->ntmFlags & NTM_TYPE1)))) { lf = *lfp; EnumFontFamiliesExW (_pango_win32_hdc, &lf, (FONTENUMPROCW) pango_win32_inner_enum_proc, lParam, 0); } return 1; } static void synthesize_foreach (gpointer key, gpointer value, gpointer user_data) { PangoWin32Family *win32family = value; PangoWin32FontMap *win32fontmap = user_data; enum { NORMAL, BOLDER, SLANTED }; PangoWin32Face *variant[4] = { NULL, NULL, NULL, NULL }; GSList *p; LOGFONTW lf; p = win32family->faces; while (p) { PangoWin32Face *win32face = p->data; /* Don't synthesize anything unless it's a monospace, serif, or sans font */ if (!((win32face->logfontw.lfPitchAndFamily & 0xF0) == FF_MODERN || (win32face->logfontw.lfPitchAndFamily & 0xF0) == FF_ROMAN || (win32face->logfontw.lfPitchAndFamily & 0xF0) == FF_SWISS)) return; if (pango_font_description_get_weight (win32face->description) == PANGO_WEIGHT_NORMAL && pango_font_description_get_style (win32face->description) == PANGO_STYLE_NORMAL) variant[NORMAL] = win32face; if (pango_font_description_get_weight (win32face->description) > PANGO_WEIGHT_NORMAL && pango_font_description_get_style (win32face->description) == PANGO_STYLE_NORMAL) variant[BOLDER] = win32face; if (pango_font_description_get_weight (win32face->description) == PANGO_WEIGHT_NORMAL && pango_font_description_get_style (win32face->description) >= PANGO_STYLE_OBLIQUE) variant[SLANTED] = win32face; if (pango_font_description_get_weight (win32face->description) > PANGO_WEIGHT_NORMAL && pango_font_description_get_style (win32face->description) >= PANGO_STYLE_OBLIQUE) variant[BOLDER+SLANTED] = win32face; p = p->next; } if (variant[NORMAL] != NULL && variant[BOLDER] == NULL) { lf = variant[NORMAL]->logfontw; lf.lfWeight = FW_BOLD; pango_win32_insert_font (win32fontmap, &lf, TRUE); } if (variant[NORMAL] != NULL && variant[SLANTED] == NULL) { lf = variant[NORMAL]->logfontw; lf.lfItalic = 255; pango_win32_insert_font (win32fontmap, &lf, TRUE); } if (variant[NORMAL] != NULL && variant[BOLDER+SLANTED] == NULL) { lf = variant[NORMAL]->logfontw; lf.lfWeight = FW_BOLD; lf.lfItalic = 255; pango_win32_insert_font (win32fontmap, &lf, TRUE); } else if (variant[BOLDER] != NULL && variant[BOLDER+SLANTED] == NULL) { lf = variant[BOLDER]->logfontw; lf.lfItalic = 255; pango_win32_insert_font (win32fontmap, &lf, TRUE); } else if (variant[SLANTED] != NULL && variant[BOLDER+SLANTED] == NULL) { lf = variant[SLANTED]->logfontw; lf.lfWeight = FW_BOLD; pango_win32_insert_font (win32fontmap, &lf, TRUE); } } static void create_standard_family (PangoWin32FontMap *win32fontmap, const char *standard_family_name) { int i; int n_aliases; char **aliases; pango_lookup_aliases (standard_family_name, &aliases, &n_aliases); for (i = 0; i < n_aliases; i++) { PangoWin32Family *existing_family = g_hash_table_lookup (win32fontmap->families, aliases[i]); if (existing_family) { PangoWin32Family *new_family = pango_win32_get_font_family (win32fontmap, standard_family_name); GSList *p = existing_family->faces; new_family->is_monospace = existing_family->is_monospace; while (p) { const PangoWin32Face *old_face = p->data; PangoWin32Face *new_face = g_object_new (PANGO_WIN32_TYPE_FACE, NULL); int j; new_face->logfontw = old_face->logfontw; new_face->description = pango_font_description_copy_static (old_face->description); pango_font_description_set_family_static (new_face->description, standard_family_name); for (j = 0; j < PANGO_WIN32_N_COVERAGES; j++) { if (old_face->coverages[j] != NULL) new_face->coverages[j] = pango_coverage_ref (old_face->coverages[j]); else new_face->coverages[j] = NULL; } new_face->is_synthetic = TRUE; new_face->has_cmap = old_face->has_cmap; new_face->cmap_format = old_face->cmap_format; new_face->cmap = old_face->cmap; new_face->cached_fonts = NULL; new_family->faces = g_slist_append (new_family->faces, new_face); p = p->next; } return; } } /* XXX What to do if none of the members of aliases for standard_family_name * exists on this machine? */ } static void _pango_win32_font_map_init (PangoWin32FontMap *win32fontmap) { LOGFONTW logfont; win32fontmap->families = g_hash_table_new ((GHashFunc) case_insensitive_str_hash, (GEqualFunc) case_insensitive_str_equal); win32fontmap->fonts = g_hash_table_new ((GHashFunc) logfontw_nosize_hash, (GEqualFunc) logfontw_nosize_equal); 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); g_hash_table_foreach (win32fontmap->families, synthesize_foreach, win32fontmap); /* Create synthetic "Sans", "Serif" and "Monospace" families */ create_standard_family (win32fontmap, "Sans"); create_standard_family (win32fontmap, "Serif"); create_standard_family (win32fontmap, "Monospace"); 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); GSList *p; int n; p = win32family->faces; n = 0; while (p) { n++; p = p->next; } if (faces) { int i; *faces = g_new (PangoFontFace *, n); p = win32family->faces; i = 0; while (p) { (*faces)[i++] = p->data; p = p->next; } } if (n_faces) *n_faces = n; } 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->faces = 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->faces; 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); g_assert (result->fontmap == NULL); result->fontmap = fontmap; g_object_add_weak_pointer (G_OBJECT (result->fontmap), (gpointer *) (gpointer) &result->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->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_SEMIBOLD) / 2) weight = PANGO_WEIGHT_NORMAL; else if (lfp->lfWeight <= (FW_SEMIBOLD + FW_BOLD) / 2) weight = PANGO_WEIGHT_SEMIBOLD; 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); g_free(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, name_weight; PangoStretch stretch; static const struct { const char *marker; int marker_len; int remove_len; PangoWeight weight; } weight_names[] = { #define ENTRY(n, s) ENTRY2 (n, sizeof (#n) - 1, s) #define ENTRY2(n, l, s) ENTRY3 (n, l, l, s) #define ENTRY3(n, marker_len, remove_len, s) { #n, marker_len, remove_len, PANGO_WEIGHT_##s } ENTRY (Ultra Light, ULTRALIGHT), ENTRY (UltraLight, ULTRALIGHT), ENTRY (Light, LIGHT), ENTRY (Medium, NORMAL), ENTRY (Demi Bold, SEMIBOLD), ENTRY (Demi, SEMIBOLD), ENTRY (Ultra Bold, ULTRABOLD), ENTRY (Extra Bold, ULTRABOLD), ENTRY (SemiBold, SEMIBOLD), ENTRY (DemiBold, SEMIBOLD), ENTRY (UltraBold, ULTRABOLD), ENTRY (ExtraBold, ULTRABOLD), ENTRY (Bold, BOLD), ENTRY (Heavy, HEAVY), ENTRY (Black, HEAVY), #undef ENTRY #undef ENTRY2 #undef ENTRY3 }; static const struct { const char *marker; int marker_len; PangoStretch stretch; } stretch_names[] = { #define ENTRY(n, s) { #n, sizeof (#n) - 1, PANGO_STRETCH_##s } ENTRY (Ext Condensed, EXTRA_CONDENSED), ENTRY (Extra Condensed, EXTRA_CONDENSED), ENTRY (UltraCondensed, ULTRA_CONDENSED), ENTRY (ExtraCondensed, EXTRA_CONDENSED), ENTRY (Condensed, CONDENSED), ENTRY (Cond, CONDENSED), ENTRY (Narrow, CONDENSED), ENTRY (Ext Expanded, EXTRA_EXPANDED), ENTRY (Extra Expanded, EXTRA_EXPANDED), ENTRY (Ultra Expanded, ULTRA_EXPANDED), ENTRY (ExtraExpanded, EXTRA_EXPANDED), ENTRY (UltraExpanded, ULTRA_EXPANDED), ENTRY (Expanded, EXPANDED), #undef ENTRY }; int i; char *p; family = get_family_nameW (lfp); if ((lfp->lfPitchAndFamily & 0xF0) == FF_ROMAN && lfp->lfItalic) style = PANGO_STYLE_ITALIC; else if (lfp->lfItalic) style = PANGO_STYLE_OBLIQUE; else style = PANGO_STYLE_NORMAL; 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_SEMIBOLD) / 2) weight = PANGO_WEIGHT_NORMAL; else if (lfp->lfWeight <= (FW_SEMIBOLD + FW_BOLD) / 2) weight = PANGO_WEIGHT_SEMIBOLD; 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; name_weight = 0; p = family; while ((p = strchr (p, ' ')) != NULL) { for (i = 0; i < G_N_ELEMENTS (weight_names); i++) { if (g_ascii_strncasecmp (p + 1, weight_names[i].marker, weight_names[i].marker_len) == 0 && (p[1 + weight_names[i].marker_len] == '\0' || p[1 + weight_names[i].marker_len] == ' ')) { strcpy (p, p + 1 + weight_names[i].remove_len); name_weight = weight_names[i].weight; break; } } if (i < G_N_ELEMENTS (weight_names)) break; p++; } if (weight == PANGO_WEIGHT_NORMAL && name_weight > 0) weight = name_weight; stretch = PANGO_STRETCH_NORMAL; p = family; while ((p = strchr (p, ' ')) != NULL) { for (i = 0; i < G_N_ELEMENTS (stretch_names); i++) { if (g_ascii_strncasecmp (p + 1, stretch_names[i].marker, stretch_names[i].marker_len) == 0 && (p[1 + stretch_names[i].marker_len] == '\0' || p[1 + stretch_names[i].marker_len] == ' ')) { strcpy (p, p + 1 + stretch_names[i].marker_len); stretch = stretch_names[i].stretch; break; } } if (i < G_N_ELEMENTS (stretch_names)) break; p++; } description = pango_font_description_new (); pango_font_description_set_family (description, family); g_free(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 char * charset_name (int charset) { static char num[10]; switch (charset) { #define CASE(x) case x##_CHARSET: return #x CASE (ANSI); CASE (DEFAULT); CASE (SYMBOL); CASE (SHIFTJIS); CASE (HANGUL); CASE (GB2312); CASE (CHINESEBIG5); CASE (GREEK); CASE (TURKISH); CASE (HEBREW); CASE (ARABIC); CASE (BALTIC); CASE (RUSSIAN); CASE (THAI); CASE (EASTEUROPE); CASE (OEM); CASE (JOHAB); CASE (VIETNAMESE); CASE (MAC); #undef CASE default: sprintf (num, "%d", charset); return num; } } static char * ff_name (int ff) { static char num[10]; switch (ff) { #define CASE(x) case FF_##x: return #x CASE (DECORATIVE); CASE (DONTCARE); CASE (MODERN); CASE (ROMAN); CASE (SCRIPT); CASE (SWISS); #undef CASE default: sprintf (num, "%d", ff); return num; } } static void pango_win32_insert_font (PangoWin32FontMap *win32fontmap, LOGFONTW *lfp, gboolean is_synthetic) { LOGFONTW *lfp2 = NULL; PangoFontDescription *description; PangoWin32Family *win32family; PangoWin32Face *win32face; gint i; PING (("face=%S,charset=%s,it=%s,wt=%ld,ht=%ld,ff=%s%s", lfp->lfFaceName, charset_name (lfp->lfCharSet), lfp->lfItalic ? "yes" : "no", lfp->lfWeight, lfp->lfHeight, ff_name (lfp->lfPitchAndFamily & 0xF0), is_synthetic ? " synthetic" : "")); /* 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; if (g_hash_table_lookup (win32fontmap->fonts, lfp)) { PING (("already have it")); return; } PING (("not found")); lfp2 = g_new (LOGFONTW, 1); *lfp2 = *lfp; g_hash_table_insert (win32fontmap->fonts, lfp2, lfp2); 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); PING (("win32face created: %p for %S", win32face, lfp->lfFaceName)); 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->has_cmap = TRUE; win32face->cmap_format = 0; win32face->cmap = NULL; win32face->cached_fonts = NULL; win32family = pango_win32_get_font_family (win32fontmap, pango_font_description_get_family (win32face->description)); if ((lfp->lfPitchAndFamily & 0xF0) == FF_MODERN) win32family->is_monospace = TRUE; win32family->faces = g_slist_append (win32family->faces, win32face); PING (("name=%s, length(faces)=%d", win32family->family_name, g_slist_length (win32family->faces))); } /* 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; LOGFONTW *match; PING (("lfp.face=%S,wt=%ld,ht=%ld,size:%d", lfp->lfFaceName, lfp->lfWeight, lfp->lfHeight, size)); win32fontmap = PANGO_WIN32_FONT_MAP (fontmap); match = g_hash_table_lookup (win32fontmap->fonts, lfp); if (!match) { PING (("not found")); return; } /* OK, we have a match; let's modify it to fit this size */ *out = *match; out->lfHeight = -(int)((double)size / win32fontmap->resolution + 0.5); out->lfWidth = 0; } 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 (); }