/* Pango * pangoxft-font.c: Routines for handling X fonts * * 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 #include "pangoxft-private.h" #include "X11/Xft/XftFreetype.h" #include "pango-layout.h" #include "pango-modules.h" #include "pango-utils.h" #define PANGO_XFT_FONT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_XFT_FONT, PangoXftFont)) #define PANGO_XFT_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_XFT_FONT, PangoXftFontClass)) #define PANGO_XFT_IS_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_XFT_FONT)) #define PANGO_XFT_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_XFT_FONT, PangoXftFontClass)) #define PANGO_XFT_UNKNOWN_FLAG 0x10000000 typedef struct _PangoXftFontClass PangoXftFontClass; typedef struct _PangoXftMetricsInfo PangoXftMetricsInfo; struct _PangoXftFontClass { PangoFontClass parent_class; }; struct _PangoXftMetricsInfo { const char *sample_str; PangoFontMetrics *metrics; }; static PangoFontClass *parent_class; /* Parent class structure for PangoXftFont */ static void pango_xft_font_class_init (PangoXftFontClass *class); static void pango_xft_font_init (PangoXftFont *xfont); static void pango_xft_font_dispose (GObject *object); static void pango_xft_font_finalize (GObject *object); static PangoFontDescription *pango_xft_font_describe (PangoFont *font); static PangoCoverage * pango_xft_font_get_coverage (PangoFont *font, PangoLanguage *language); static PangoEngineShape * pango_xft_font_find_shaper (PangoFont *font, PangoLanguage *language, guint32 ch); static void pango_xft_font_get_glyph_extents (PangoFont *font, PangoGlyph glyph, PangoRectangle *ink_rect, PangoRectangle *logical_rect); static PangoFontMetrics * pango_xft_font_get_metrics (PangoFont *font, PangoLanguage *language); GType pango_xft_font_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (PangoXftFontClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) pango_xft_font_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (PangoXftFont), 0, /* n_preallocs */ (GInstanceInitFunc) pango_xft_font_init, }; object_type = g_type_register_static (PANGO_TYPE_FONT, "PangoXftFont", &object_info, 0); } return object_type; } static void pango_xft_font_init (PangoXftFont *xfont) { xfont->metrics_by_lang = NULL; xfont->in_cache = FALSE; } static void pango_xft_font_class_init (PangoXftFontClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); PangoFontClass *font_class = PANGO_FONT_CLASS (class); parent_class = g_type_class_peek_parent (class); object_class->finalize = pango_xft_font_finalize; object_class->dispose = pango_xft_font_dispose; font_class->describe = pango_xft_font_describe; font_class->get_coverage = pango_xft_font_get_coverage; font_class->find_shaper = pango_xft_font_find_shaper; font_class->get_glyph_extents = pango_xft_font_get_glyph_extents; font_class->get_metrics = pango_xft_font_get_metrics; } PangoXftFont * _pango_xft_font_new (PangoFontMap *fontmap, XftPattern *pattern) { PangoXftFont *xfont; g_return_val_if_fail (fontmap != NULL, NULL); g_return_val_if_fail (pattern != NULL, NULL); xfont = (PangoXftFont *)g_object_new (PANGO_TYPE_XFT_FONT, NULL); xfont->fontmap = fontmap; xfont->font_pattern = pattern; g_object_ref (G_OBJECT (fontmap)); xfont->description = _pango_xft_font_desc_from_pattern (pattern, TRUE); xfont->xft_font = NULL; _pango_xft_font_map_add (xfont->fontmap, xfont); return xfont; } static PangoFont * get_mini_font (PangoFont *font) { PangoXftFont *xfont = (PangoXftFont *)font; if (!xfont->mini_font) { Display *display; PangoFontDescription *desc = pango_font_description_new (); int i; int width = 0, height = 0; XGlyphInfo extents; XftFont *mini_xft; FT_Face face; _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); pango_font_description_set_family_static (desc, "monospace"); pango_font_description_set_size (desc, 0.5 * pango_font_description_get_size (xfont->description)); xfont->mini_font = pango_font_map_load_font (xfont->fontmap, NULL, desc); pango_font_description_free (desc); mini_xft = pango_xft_font_get_font (xfont->mini_font); face = pango_xft_font_get_face (xfont->mini_font); for (i = 0 ; i < 16 ; i++) { char c = i < 10 ? '0' + i : 'A' + i - 10; XftChar32 glyph = FT_Get_Char_Index (face, c); XftTextExtents32 (display, mini_xft, &glyph, 1, &extents); width = MAX (width, extents.width); height = MAX (height, extents.height); } xfont->mini_width = width; xfont->mini_height = height; xfont->mini_pad = MAX (height / 10, 1); } return xfont->mini_font; } static void draw_rectangle (Display *display, Picture src_picture, Picture dest_picture, XftDraw *draw, XftColor *color, gint x, gint y, gint width, gint height) { if (draw) { XftDrawRect (draw, color, x, y, width, height); } else { XRenderComposite (display, PictOpOver, src_picture, None, dest_picture, 0, 0, 0, 0, x, y, width, height); } } static void draw_box (Display *display, Picture src_picture, Picture dest_picture, XftDraw *draw, XftColor *color, PangoXftFont *xfont, gint x, gint y, gint width, gint height) { draw_rectangle (display, src_picture, dest_picture, draw, color, x, y, width, xfont->mini_pad); draw_rectangle (display, src_picture, dest_picture, draw, color, x, y + xfont->mini_pad, xfont->mini_pad, height - xfont->mini_pad * 2); draw_rectangle (display, src_picture, dest_picture, draw, color, x + width - xfont->mini_pad, y + xfont->mini_pad, xfont->mini_pad, height - xfont->mini_pad * 2); draw_rectangle (display, src_picture, dest_picture, draw, color, x, y + height - xfont->mini_pad, width, xfont->mini_pad); } /** * pango_xft_render: * @draw: the XftDraw object. * @color: the color in which to draw the string * @font: the font in which to draw the string * @glyphs: the glyph string to draw * @x: the x position of start of string (in pixels) * @y: the y position of baseline (in pixels) * * Renders a #PangoGlyphString onto an XftDraw object wrapping an X drawable. */ static void pango_xft_real_render (Display *display, Picture src_picture, Picture dest_picture, XftDraw *draw, XftColor *color, PangoFont *font, PangoGlyphString *glyphs, gint x, gint y) { PangoXftFont *xfont = PANGO_XFT_FONT (font); XftFont *xft_font = pango_xft_font_get_font (font); int i; int x_off = 0; /* Slow initial implementation. For speed, it should really * collect the characters into runs, and draw multiple * characters with each XftDrawString32 call. */ if (!display) _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); for (i=0; inum_glyphs; i++) { PangoGlyph glyph = glyphs->glyphs[i].glyph; int glyph_x = x + PANGO_PIXELS (x_off + glyphs->glyphs[i].geometry.x_offset); int glyph_y = y + PANGO_PIXELS (glyphs->glyphs[i].geometry.y_offset); /* Clip glyphs into the X coordinate range; we really * want to clip glyphs with an ink rect outside the * [0,32767] x [0,32767] rectangle but looking up * the ink rect here would be a noticeable speed hit. * This is close enough. */ if (glyph && glyph_x >= -16384 && glyph_x <= 32767 && glyph_y >= -16384 && glyph_y <= 32767) { if (glyph & PANGO_XFT_UNKNOWN_FLAG) { char buf[5]; int ys[3]; int xs[3]; int j, k; PangoFont *mini_font = get_mini_font (font); XftFont *mini_xft = pango_xft_font_get_font (mini_font); FT_Face face = pango_xft_font_get_face (xfont->mini_font); glyph &= ~PANGO_XFT_UNKNOWN_FLAG; ys[0] = glyph_y - xft_font->ascent + (xft_font->ascent + xft_font->descent - xfont->mini_height * 2 - xfont->mini_pad * 5) / 2; ys[1] = ys[0] + 2 * xfont->mini_pad + xfont->mini_height; ys[2] = ys[1] + xfont->mini_height + xfont->mini_pad; xs[0] = glyph_x; xs[1] = xs[0] + 2 * xfont->mini_pad; xs[2] = xs[1] + xfont->mini_width + xfont->mini_pad; draw_box (display, src_picture, dest_picture, draw, color, xfont, xs[0], ys[0], xfont->mini_width * 2 + xfont->mini_pad * 5, xfont->mini_height * 2 + xfont->mini_pad * 5); g_snprintf (buf, sizeof(buf), "%04X", glyph); for (j = 0; j < 2; j++) for (k = 0; k < 2; k++) { XftChar32 glyph = FT_Get_Char_Index (face, buf[2*j + k]); if (draw) XftDrawString32 (draw, color, mini_xft, xs[k+1], ys[j+1], &glyph, 1); else XftRenderString32 (display, src_picture, mini_xft->u.ft.font, dest_picture, 0, 0, xs[k+1], ys[j+1], &glyph, 1); } } else if (glyph) { if (draw) XftDrawString32 (draw, color, xft_font, glyph_x, glyph_y, &glyph, 1); else XftRenderString32 (display, src_picture, xft_font->u.ft.font, dest_picture, 0, 0, glyph_x, glyph_y, &glyph, 1); } } x_off += glyphs->glyphs[i].geometry.width; } } /** * pango_xft_render: * @draw: the XftDraw object. * @color: the color in which to draw the string * @font: the font in which to draw the string * @glyphs: the glyph string to draw * @x: the x position of start of string (in pixels) * @y: the y position of baseline (in pixels) * * Renders a #PangoGlyphString onto an XftDraw object wrapping an X drawable. */ void pango_xft_render (XftDraw *draw, XftColor *color, PangoFont *font, PangoGlyphString *glyphs, gint x, gint y) { g_return_if_fail (draw != NULL); g_return_if_fail (color != NULL); g_return_if_fail (PANGO_XFT_IS_FONT (font)); g_return_if_fail (glyphs != NULL); pango_xft_real_render (NULL, None, None, draw, color, font, glyphs, x, y); } /** * pango_xft_picture_render: * @display: an X display * @src_picture: the source picture to draw the string with * @dest_picture: the destination picture to draw the strign onto * @font: the font in which to draw the string * @glyphs: the glyph string to draw * @x: the x position of start of string (in pixels) * @y: the y position of baseline (in pixels) * * Renders a #PangoGlyphString onto an Xrender Picture object. */ void pango_xft_picture_render (Display *display, Picture src_picture, Picture dest_picture, PangoFont *font, PangoGlyphString *glyphs, gint x, gint y) { g_return_if_fail (display != NULL); g_return_if_fail (src_picture != None); g_return_if_fail (dest_picture != None); g_return_if_fail (PANGO_XFT_IS_FONT (font)); g_return_if_fail (glyphs != NULL); pango_xft_real_render (display, src_picture, dest_picture, NULL, NULL, font, glyphs, x, y); } static PangoFontMetrics * pango_xft_font_get_metrics (PangoFont *font, PangoLanguage *language) { PangoXftFont *xfont = PANGO_XFT_FONT (font); PangoXftMetricsInfo *info = NULL; /* Quiet gcc */ GSList *tmp_list; const char *sample_str = pango_language_get_sample_string (language); tmp_list = xfont->metrics_by_lang; while (tmp_list) { info = tmp_list->data; if (info->sample_str == sample_str) /* We _don't_ need strcmp */ break; tmp_list = tmp_list->next; } if (!tmp_list) { PangoLayout *layout; PangoRectangle extents; PangoContext *context; XftFont *xft_font = pango_xft_font_get_font (font); Display *display; _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); context = pango_xft_get_context (display, 0); info = g_new (PangoXftMetricsInfo, 1); info->sample_str = sample_str; info->metrics = pango_font_metrics_new (); info->metrics->ascent = PANGO_SCALE * xft_font->ascent; info->metrics->descent = PANGO_SCALE * xft_font->descent; info->metrics->approximate_char_width = info->metrics->approximate_digit_width = PANGO_SCALE * xft_font->max_advance_width; xfont->metrics_by_lang = g_slist_prepend (xfont->metrics_by_lang, info); pango_context_set_language (context, language); layout = pango_layout_new (context); pango_layout_set_font_description (layout, xfont->description); pango_layout_set_text (layout, sample_str, -1); pango_layout_get_extents (layout, NULL, &extents); info->metrics->approximate_char_width = extents.width / g_utf8_strlen (sample_str, -1); pango_layout_set_text (layout, "0123456789", -1); pango_layout_get_extents (layout, NULL, &extents); info->metrics->approximate_digit_width = extents.width / 10; g_object_unref (G_OBJECT (layout)); g_object_unref (G_OBJECT (context)); } return pango_font_metrics_ref (info->metrics); } static void pango_xft_font_dispose (GObject *object) { PangoXftFont *xfont = PANGO_XFT_FONT (object); /* If the font is not already in the freed-fonts cache, add it, * if it is already there, do nothing and the font will be * freed. */ if (!xfont->in_cache && xfont->fontmap) _pango_xft_font_map_cache_add (xfont->fontmap, xfont); G_OBJECT_CLASS (parent_class)->dispose (object); } static void free_metrics_info (PangoXftMetricsInfo *info) { pango_font_metrics_unref (info->metrics); g_free (info); } static void pango_xft_font_finalize (GObject *object) { PangoXftFont *xfont = (PangoXftFont *)object; Display *display; _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); _pango_xft_font_map_remove (xfont->fontmap, xfont); if (xfont->mini_font) g_object_unref (xfont->mini_font); if (xfont->ot_info) g_object_unref (xfont->ot_info); pango_font_description_free (xfont->description); g_slist_foreach (xfont->metrics_by_lang, (GFunc)free_metrics_info, NULL); g_slist_free (xfont->metrics_by_lang); if (xfont->xft_font) XftFontClose (display, xfont->xft_font); else XftPatternDestroy (xfont->font_pattern); /* If we opened the font it will own and destroy this pattern */ G_OBJECT_CLASS (parent_class)->finalize (object); } static PangoFontDescription * pango_xft_font_describe (PangoFont *font) { PangoXftFont *xfont = (PangoXftFont *)font; return pango_font_description_copy (xfont->description); } static PangoCoverage * pango_xft_font_get_coverage (PangoFont *font, PangoLanguage *language) { PangoXftFont *xfont = (PangoXftFont *)font; char *filename = NULL; FT_Face face; PangoCoverage *coverage; Display *display; _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); XftPatternGetString (xfont->font_pattern, XFT_FILE, 0, &filename); coverage = _pango_xft_font_map_get_coverage (xfont->fontmap, filename); if (coverage) return pango_coverage_ref (coverage); face = pango_xft_font_get_face (font); coverage = pango_coverage_new (); #ifdef HAVE_FT_GET_FIRST_CHAR { FT_UInt gindex; FT_ULong charcode; charcode = FT_Get_First_Char (face, &gindex); while (gindex) { pango_coverage_set (coverage, charcode, PANGO_COVERAGE_EXACT); charcode = FT_Get_Next_Char (face, charcode, &gindex); } } #else /* Ugh, this is going to be SLOW */ { gunichar wc; for (wc = 0; wc < G_MAXUSHORT; wc++) { FT_UInt glyph = FT_Get_Char_Index (face, wc); if (glyph && glyph < face->num_glyphs) pango_coverage_set (coverage, wc, PANGO_COVERAGE_EXACT); } } #endif _pango_xft_font_map_set_coverage (xfont->fontmap, filename, coverage); return coverage; } static void pango_xft_font_get_glyph_extents (PangoFont *font, PangoGlyph glyph, PangoRectangle *ink_rect, PangoRectangle *logical_rect) { PangoXftFont *xfont = (PangoXftFont *)font; XftFont *xft_font = pango_xft_font_get_font (font); XGlyphInfo extents; Display *display; _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); if (glyph == (PangoGlyph)-1) glyph = 0; if (glyph & PANGO_XFT_UNKNOWN_FLAG) { get_mini_font (font); if (ink_rect) { ink_rect->x = 0; ink_rect->y = PANGO_SCALE * (- xft_font->ascent + (xft_font->ascent + xft_font->descent - xfont->mini_height * 2 - xfont->mini_pad * 5) / 2); ink_rect->width = PANGO_SCALE * (xfont->mini_width * 2 + xfont->mini_pad * 5); ink_rect->height = PANGO_SCALE * (xfont->mini_height * 2 + xfont->mini_pad * 5); } if (logical_rect) { logical_rect->x = 0; logical_rect->y = - PANGO_SCALE * xft_font->ascent; logical_rect->width = PANGO_SCALE * (xfont->mini_width * 2 + xfont->mini_pad * 6); logical_rect->height = (xft_font->ascent + xft_font->descent) * PANGO_SCALE; } } else if (glyph) { XftTextExtents32 (display, xft_font, &glyph, 1, &extents); if (ink_rect) { ink_rect->x = - extents.x * PANGO_SCALE; /* Xft crack-rock sign choice */ ink_rect->y = - extents.y * PANGO_SCALE; /* " */ ink_rect->width = extents.width * PANGO_SCALE; ink_rect->height = extents.height * PANGO_SCALE; } if (logical_rect) { logical_rect->x = 0; logical_rect->y = - xft_font->ascent * PANGO_SCALE; logical_rect->width = extents.xOff * PANGO_SCALE; logical_rect->height = (xft_font->ascent + xft_font->descent) * PANGO_SCALE; } } else { if (ink_rect) { ink_rect->x = 0; ink_rect->width = 0; ink_rect->y = 0; ink_rect->height = 0; } if (logical_rect) { logical_rect->x = 0; logical_rect->width = 0; logical_rect->y = 0; logical_rect->height = 0; } } } static PangoMap * pango_xft_get_shaper_map (PangoLanguage *language) { static guint engine_type_id = 0; static guint render_type_id = 0; if (engine_type_id == 0) { engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_SHAPE); render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_XFT); } return pango_find_map (language, engine_type_id, render_type_id); } static PangoEngineShape * pango_xft_font_find_shaper (PangoFont *font, PangoLanguage *language, guint32 ch) { PangoMap *shape_map = NULL; shape_map = pango_xft_get_shaper_map (language); return (PangoEngineShape *)pango_map_get_engine (shape_map, ch); } static gboolean set_unicode_charmap (FT_Face face) { int charmap; for (charmap = 0; charmap < face->num_charmaps; charmap++) if (face->charmaps[charmap]->encoding == ft_encoding_unicode) { FT_Error error = FT_Set_Charmap(face, face->charmaps[charmap]); return error == FT_Err_Ok; } return FALSE; } static void load_fallback_font (PangoXftFont *xfont) { Display *display; int screen; XftFont *xft_font; _pango_xft_font_map_get_info (xfont->fontmap, &display, &screen); xft_font = XftFontOpen (display, screen, XFT_FAMILY, XftTypeString, "sans", XFT_ENCODING, XftTypeString, "glyphs-fontspecific", XFT_CORE, XftTypeBool, False, XFT_SIZE, XftTypeDouble, (double)pango_font_description_get_size (xfont->description)/PANGO_SCALE, NULL); if (!xft_font) { g_warning ("Cannot open fallback font, nothing to do"); exit (1); } if (!set_unicode_charmap (xft_font->u.ft.font->face)) { g_warning ("Cannot set unicode character map for fallback font, nothing to do"); exit (1); } xfont->xft_font = xft_font; } /** * pango_xft_font_get_font: * @font: a #PangoFont. * * Returns the XftFont of a font. * * Returns: the XftFont associated to @font. **/ XftFont * pango_xft_font_get_font (PangoFont *font) { PangoXftFont *xfont; Display *display; int screen; g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL); xfont = PANGO_XFT_FONT (font); if (xfont->xft_font == NULL) { _pango_xft_font_map_get_info (xfont->fontmap, &display, &screen); xfont->xft_font = XftFontOpenPattern (display, xfont->font_pattern); if (!xfont->xft_font) { gchar *name = pango_font_description_to_string (xfont->description); /* No provision for failure here, unfortunately, various fonts * fail only when FT tries to open them... e.g., fonts with only * bitmaps. */ g_warning ("Cannot open font file for font %s", name); g_free (name); load_fallback_font (xfont); } else { /* There should be a unicode encoding, since we queried for it */ if (!set_unicode_charmap (xfont->xft_font->u.ft.font->face)) { gchar *name = pango_font_description_to_string (xfont->description); g_warning ("Cannot load unicode character map for font %s", name); g_free (name); XftFontClose (display, xfont->xft_font); xfont->xft_font = NULL; load_fallback_font (xfont); } } } return xfont->xft_font; } /** * pango_xft_font_get_display: * @font: a #PangoFont. * * Returns the X display of the XftFont of a font. * * Returns: the X display of the XftFont associated to @font. **/ Display * pango_xft_font_get_display (PangoFont *font) { PangoXftFont *xfont; Display *display; g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL); xfont = PANGO_XFT_FONT (font); _pango_xft_font_map_get_info (xfont->fontmap, &display, NULL); return display; } /** * pango_xft_font_get_unknown_glyph: * @font: a #PangoFont. * @wc: the Unicode character for which a glyph is needed. * * Returns the index of a glyph suitable for drawing @wc as an * unknown character. * * Return value: a glyph index into @font. **/ PangoGlyph pango_xft_font_get_unknown_glyph (PangoFont *font, gunichar wc) { g_return_val_if_fail (PANGO_XFT_IS_FONT (font), -1); return wc | PANGO_XFT_UNKNOWN_FLAG; } /** * pango_xft_font_get_face: * @font: a #PangoFont. * * Gets the FreeType FT_Face associated with a font. * * Returns: the FreeType FT_Face associated with @font. **/ FT_Face pango_xft_font_get_face (PangoFont *font) { XftFont *xft_font; g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL); xft_font = pango_xft_font_get_font (font); if (xft_font->core) return NULL; else return xft_font->u.ft.font->face; } /** * pango_xft_font_get_ot_info: * @font: a #PangoFont. * * Gets the OpenType info of a font as a #PangoOTInfo. * * Returns: the OpenType info of @font, or %NULL if there is none. **/ PangoOTInfo * pango_xft_font_get_ot_info (PangoFont *font) { PangoXftFont *xfont; g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL); xfont = PANGO_XFT_FONT (font); if (!xfont->ot_info) { FT_Face face = pango_xft_font_get_face (font); if (!face) return NULL; xfont->ot_info = pango_ot_info_new (face); } return xfont->ot_info; }