1 /* Pango
2  * pangoxft-font.c: Routines for handling X fonts
3  *
4  * Copyright (C) 2000 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 
26 #include "pangofc-fontmap.h"
27 #include "pangoxft-private.h"
28 #include "pangofc-private.h"
29 
30 #define PANGO_XFT_FONT_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_XFT_FONT, PangoXftFontClass))
31 #define PANGO_XFT_IS_FONT_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_XFT_FONT))
32 #define PANGO_XFT_FONT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_XFT_FONT, PangoXftFontClass))
33 
34 typedef struct _PangoXftFontClass    PangoXftFontClass;
35 
36 struct _PangoXftFontClass
37 {
38   PangoFcFontClass  parent_class;
39 };
40 
41 static void pango_xft_font_finalize (GObject *object);
42 
43 static void                  pango_xft_font_get_glyph_extents (PangoFont        *font,
44 							       PangoGlyph        glyph,
45 							       PangoRectangle   *ink_rect,
46 							       PangoRectangle   *logical_rect);
47 
48 static FT_Face    pango_xft_font_real_lock_face         (PangoFcFont      *font);
49 static void       pango_xft_font_real_unlock_face       (PangoFcFont      *font);
50 static gboolean   pango_xft_font_real_has_char          (PangoFcFont      *font,
51 							 gunichar          wc);
52 static guint      pango_xft_font_real_get_glyph         (PangoFcFont      *font,
53 							 gunichar          wc);
54 static void       pango_xft_font_real_shutdown          (PangoFcFont      *font);
55 
56 static XftFont *xft_font_get_font (PangoFont *font);
57 
G_DEFINE_TYPE(PangoXftFont,pango_xft_font,PANGO_TYPE_FC_FONT)58 G_DEFINE_TYPE (PangoXftFont, pango_xft_font, PANGO_TYPE_FC_FONT)
59 
60 static void
61 pango_xft_font_class_init (PangoXftFontClass *class)
62 {
63   GObjectClass *object_class = G_OBJECT_CLASS (class);
64   PangoFontClass *font_class = PANGO_FONT_CLASS (class);
65   PangoFcFontClass *fc_font_class = PANGO_FC_FONT_CLASS (class);
66 
67   object_class->finalize = pango_xft_font_finalize;
68 
69   font_class->get_glyph_extents = pango_xft_font_get_glyph_extents;
70 
71   fc_font_class->lock_face = pango_xft_font_real_lock_face;
72   fc_font_class->unlock_face = pango_xft_font_real_unlock_face;
73   fc_font_class->has_char = pango_xft_font_real_has_char;
74   fc_font_class->get_glyph = pango_xft_font_real_get_glyph;
75   fc_font_class->shutdown = pango_xft_font_real_shutdown;
76 }
77 
78 static void
pango_xft_font_init(PangoXftFont * xftfont G_GNUC_UNUSED)79 pango_xft_font_init (PangoXftFont *xftfont G_GNUC_UNUSED)
80 {
81 }
82 
83 PangoXftFont *
_pango_xft_font_new(PangoXftFontMap * xftfontmap,FcPattern * pattern)84 _pango_xft_font_new (PangoXftFontMap *xftfontmap,
85 		     FcPattern	     *pattern)
86 {
87   PangoFontMap *fontmap = PANGO_FONT_MAP (xftfontmap);
88   PangoXftFont *xfont;
89 
90   g_return_val_if_fail (fontmap != NULL, NULL);
91   g_return_val_if_fail (pattern != NULL, NULL);
92 
93   xfont = (PangoXftFont *)g_object_new (PANGO_TYPE_XFT_FONT,
94 					"pattern", pattern,
95 					"fontmap", fontmap,
96 					NULL);
97 
98   /* Hack to force hinting of vertical metrics; hinting off for
99    * a Xft font just means to not hint outlines, but we still
100    * want integer line spacing, underline positions, etc
101    */
102   PANGO_FC_FONT (xfont)->is_hinted = TRUE;
103 
104   xfont->xft_font = NULL;
105 
106   return xfont;
107 }
108 
109 /**
110  * _pango_xft_font_get_mini_font:
111  * @xfont: a `PangoXftFont`
112  *
113  * Gets the font used for drawing the digits in the
114  * missing-character hex squares
115  *
116  * Return value: the `PangoFont` used for the digits; this
117  *  value is associated with the main font and will be freed
118  *  along with the main font.
119  */
120 PangoFont *
_pango_xft_font_get_mini_font(PangoXftFont * xfont)121 _pango_xft_font_get_mini_font (PangoXftFont *xfont)
122 {
123   PangoFcFont *fcfont = (PangoFcFont *)xfont;
124 
125   if (!fcfont || !fcfont->fontmap)
126     return NULL;
127 
128   if (!xfont->mini_font)
129     {
130       Display *display;
131       int screen;
132       PangoFontDescription *desc = pango_font_description_new ();
133       PangoContext *context;
134       int i;
135       int width = 0, height = 0;
136       XGlyphInfo extents;
137       XftFont *mini_xft;
138       int new_size;
139 
140       _pango_xft_font_map_get_info (fcfont->fontmap, &display, &screen);
141 
142       context = pango_font_map_create_context (pango_xft_get_font_map (display, screen));
143       pango_context_set_language (context, pango_language_from_string ("en"));
144 
145       pango_font_description_set_family_static (desc, "monospace");
146 
147       new_size = pango_font_description_get_size (fcfont->description) / 2;
148 
149       if (pango_font_description_get_size_is_absolute (fcfont->description))
150 	pango_font_description_set_absolute_size (desc, new_size);
151       else
152 	pango_font_description_set_size (desc, new_size);
153 
154       xfont->mini_font = pango_font_map_load_font (fcfont->fontmap, context, desc);
155       pango_font_description_free (desc);
156       g_object_unref (context);
157 
158       if (!xfont->mini_font)
159 	return NULL;
160 
161       mini_xft = xft_font_get_font (xfont->mini_font);
162 
163       for (i = 0 ; i < 16 ; i++)
164 	{
165 	  char c = i < 10 ? '0' + i : 'A' + i - 10;
166 	  XftTextExtents8 (display, mini_xft, (FcChar8 *) &c, 1, &extents);
167 	  width = MAX (width, extents.width);
168 	  height = MAX (height, extents.height);
169 	}
170 
171       xfont->mini_width = PANGO_SCALE * width;
172       xfont->mini_height = PANGO_SCALE * height;
173       xfont->mini_pad = PANGO_SCALE * MIN (height / 2, MAX ((int)(2.2 * height + 27) / 28, 1));
174     }
175 
176   return xfont->mini_font;
177 }
178 
179 static void
pango_xft_font_finalize(GObject * object)180 pango_xft_font_finalize (GObject *object)
181 {
182   PangoXftFont *xfont = (PangoXftFont *)object;
183   PangoFcFont *fcfont = (PangoFcFont *)object;
184 
185   if (xfont->mini_font)
186     g_object_unref (xfont->mini_font);
187 
188   if (xfont->xft_font)
189     {
190       Display *display;
191 
192       _pango_xft_font_map_get_info (fcfont->fontmap, &display, NULL);
193       XftFontClose (display, xfont->xft_font);
194     }
195 
196   if (xfont->glyph_info)
197     g_hash_table_destroy (xfont->glyph_info);
198 
199   G_OBJECT_CLASS (pango_xft_font_parent_class)->finalize (object);
200 }
201 
202 static void
get_glyph_extents_missing(PangoXftFont * xfont,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)203 get_glyph_extents_missing (PangoXftFont    *xfont,
204 			   PangoGlyph       glyph,
205 			   PangoRectangle  *ink_rect,
206 			   PangoRectangle  *logical_rect)
207 
208 {
209   PangoFont *font = PANGO_FONT (xfont);
210   XftFont *xft_font = xft_font_get_font (font);
211   gunichar ch;
212   gint cols;
213 
214   ch = glyph & ~PANGO_GLYPH_UNKNOWN_FLAG;
215 
216   if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || ch > 0x10FFFF))
217     cols = 1;
218   else
219     cols = ch > 0xffff ? 3 : 2;
220 
221   _pango_xft_font_get_mini_font (xfont);
222 
223   if (ink_rect)
224     {
225       ink_rect->x = 0;
226       ink_rect->y = - PANGO_SCALE * xft_font->ascent + PANGO_SCALE * (((xft_font->ascent + xft_font->descent) - (xfont->mini_height * 2 + xfont->mini_pad * 5 + PANGO_SCALE / 2) / PANGO_SCALE) / 2);
227       ink_rect->width = xfont->mini_width * cols + xfont->mini_pad * (2 * cols + 1);
228       ink_rect->height = xfont->mini_height * 2 + xfont->mini_pad * 5;
229     }
230 
231   if (logical_rect)
232     {
233       logical_rect->x = 0;
234       logical_rect->y = - PANGO_SCALE * xft_font->ascent;
235       logical_rect->width = xfont->mini_width * cols + xfont->mini_pad * (2 * cols + 2);
236       logical_rect->height = (xft_font->ascent + xft_font->descent) * PANGO_SCALE;
237     }
238 }
239 
240 static void
get_glyph_extents_xft(PangoFcFont * fcfont,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)241 get_glyph_extents_xft (PangoFcFont      *fcfont,
242 		       PangoGlyph        glyph,
243 		       PangoRectangle   *ink_rect,
244 		       PangoRectangle   *logical_rect)
245 {
246   XftFont *xft_font = xft_font_get_font ((PangoFont *)fcfont);
247   XGlyphInfo extents;
248   Display *display;
249   FT_UInt ft_glyph = glyph;
250 
251   _pango_xft_font_map_get_info (fcfont->fontmap, &display, NULL);
252 
253   XftGlyphExtents (display, xft_font, &ft_glyph, 1, &extents);
254 
255   if (ink_rect)
256     {
257       ink_rect->x = - extents.x * PANGO_SCALE; /* Xft crack-rock sign choice */
258       ink_rect->y = - extents.y * PANGO_SCALE; /*             "              */
259       ink_rect->width = extents.width * PANGO_SCALE;
260       ink_rect->height = extents.height * PANGO_SCALE;
261     }
262 
263   if (logical_rect)
264     {
265       logical_rect->x = 0;
266       logical_rect->y = - xft_font->ascent * PANGO_SCALE;
267       logical_rect->width = extents.xOff * PANGO_SCALE;
268       logical_rect->height = (xft_font->ascent + xft_font->descent) * PANGO_SCALE;
269     }
270 }
271 
272 typedef struct
273 {
274   PangoRectangle ink_rect;
275   PangoRectangle logical_rect;
276 } Extents;
277 
278 static void
extents_free(Extents * ext)279 extents_free (Extents *ext)
280 {
281   g_slice_free (Extents, ext);
282 }
283 
284 static void
get_glyph_extents_raw(PangoXftFont * xfont,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)285 get_glyph_extents_raw (PangoXftFont     *xfont,
286 		       PangoGlyph        glyph,
287 		       PangoRectangle   *ink_rect,
288 		       PangoRectangle   *logical_rect)
289 {
290   Extents *extents;
291 
292   if (!xfont->glyph_info)
293     xfont->glyph_info = g_hash_table_new_full (NULL, NULL,
294 					       NULL, (GDestroyNotify)extents_free);
295 
296   extents = g_hash_table_lookup (xfont->glyph_info,
297 				 GUINT_TO_POINTER (glyph));
298 
299   if (!extents)
300     {
301       extents = g_slice_new (Extents);
302 
303       pango_fc_font_get_raw_extents (PANGO_FC_FONT (xfont),
304 				     glyph,
305 				     &extents->ink_rect,
306 				     &extents->logical_rect);
307 
308       g_hash_table_insert (xfont->glyph_info,
309 			   GUINT_TO_POINTER (glyph),
310 			   extents);
311     }
312 
313   if (ink_rect)
314     *ink_rect = extents->ink_rect;
315 
316   if (logical_rect)
317     *logical_rect = extents->logical_rect;
318 }
319 
320 static void
pango_xft_font_get_glyph_extents(PangoFont * font,PangoGlyph glyph,PangoRectangle * ink_rect,PangoRectangle * logical_rect)321 pango_xft_font_get_glyph_extents (PangoFont        *font,
322 				  PangoGlyph        glyph,
323 				  PangoRectangle   *ink_rect,
324 				  PangoRectangle   *logical_rect)
325 {
326   PangoXftFont *xfont = (PangoXftFont *)font;
327   PangoFcFont *fcfont = PANGO_FC_FONT (font);
328   gboolean empty = FALSE;
329 
330   if (G_UNLIKELY (!fcfont->fontmap))	/* Display closed */
331     {
332       if (ink_rect)
333 	ink_rect->x = ink_rect->width = ink_rect->y = ink_rect->height = 0;
334       if (logical_rect)
335 	logical_rect->x = logical_rect->width = logical_rect->y = logical_rect->height = 0;
336       return;
337     }
338 
339   if (glyph == PANGO_GLYPH_EMPTY)
340     {
341       glyph = pango_fc_font_get_glyph (fcfont, ' ');
342       empty = TRUE;
343     }
344 
345   if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
346     {
347       get_glyph_extents_missing (xfont, glyph, ink_rect, logical_rect);
348     }
349   else
350     {
351       if (!fcfont->is_transformed)
352 	get_glyph_extents_xft (fcfont, glyph, ink_rect, logical_rect);
353       else
354 	get_glyph_extents_raw (xfont, glyph, ink_rect, logical_rect);
355     }
356 
357   if (empty)
358     {
359       if (ink_rect)
360 	ink_rect->x = ink_rect->y = ink_rect->height = ink_rect->width = 0;
361       if (logical_rect)
362 	logical_rect->x = logical_rect->width = 0;
363       return;
364     }
365 }
366 
367 static void
load_fallback_font(PangoXftFont * xfont)368 load_fallback_font (PangoXftFont *xfont)
369 {
370   PangoFcFont *fcfont = PANGO_FC_FONT (xfont);
371   Display *display;
372   int screen;
373   XftFont *xft_font;
374   gboolean size_is_absolute;
375   double size;
376 
377   _pango_xft_font_map_get_info (fcfont->fontmap, &display, &screen);
378 
379   size_is_absolute = pango_font_description_get_size_is_absolute (fcfont->description);
380   size = pango_font_description_get_size (fcfont->description) / PANGO_SCALE;
381 
382   xft_font = XftFontOpen (display,  screen,
383 			  FC_FAMILY, FcTypeString, "sans",
384 			  size_is_absolute ? FC_PIXEL_SIZE : FC_SIZE, FcTypeDouble, size,
385 			  NULL);
386 
387   xfont->xft_font = xft_font;
388 }
389 
390 static XftFont *
xft_font_get_font(PangoFont * font)391 xft_font_get_font (PangoFont *font)
392 {
393   PangoXftFont *xfont;
394   PangoFcFont *fcfont;
395   Display *display;
396   int screen;
397 
398   xfont = (PangoXftFont *)font;
399   fcfont = (PangoFcFont *)font;
400 
401   if (G_UNLIKELY (xfont->xft_font == NULL))
402     {
403       FcPattern *pattern = FcPatternDuplicate (fcfont->font_pattern);
404       FcPatternDel (pattern, FC_SPACING);
405 
406       _pango_xft_font_map_get_info (fcfont->fontmap, &display, &screen);
407 
408       xfont->xft_font = XftFontOpenPattern (display, pattern);
409       if (!xfont->xft_font)
410 	{
411 	  gchar *name = pango_font_description_to_string (fcfont->description);
412 	  g_warning ("Cannot open font file for font %s", name);
413 	  g_free (name);
414 
415 	  load_fallback_font (xfont);
416 	}
417     }
418 
419   return xfont->xft_font;
420 }
421 
422 static FT_Face
pango_xft_font_real_lock_face(PangoFcFont * font)423 pango_xft_font_real_lock_face (PangoFcFont *font)
424 {
425   XftFont *xft_font = xft_font_get_font ((PangoFont *)font);
426 
427   return XftLockFace (xft_font);
428 }
429 
430 static void
pango_xft_font_real_unlock_face(PangoFcFont * font)431 pango_xft_font_real_unlock_face (PangoFcFont *font)
432 {
433   XftFont *xft_font = xft_font_get_font ((PangoFont *)font);
434 
435   XftUnlockFace (xft_font);
436 }
437 
438 static gboolean
pango_xft_font_real_has_char(PangoFcFont * font,gunichar wc)439 pango_xft_font_real_has_char (PangoFcFont *font,
440 			      gunichar     wc)
441 {
442   XftFont *xft_font = xft_font_get_font ((PangoFont *)font);
443 
444   return XftCharExists (NULL, xft_font, wc);
445 }
446 
447 static guint
pango_xft_font_real_get_glyph(PangoFcFont * font,gunichar wc)448 pango_xft_font_real_get_glyph (PangoFcFont *font,
449 			       gunichar     wc)
450 {
451   XftFont *xft_font = xft_font_get_font ((PangoFont *)font);
452 
453   return XftCharIndex (NULL, xft_font, wc);
454 }
455 
456 static void
pango_xft_font_real_shutdown(PangoFcFont * fcfont)457 pango_xft_font_real_shutdown (PangoFcFont *fcfont)
458 {
459   PangoXftFont *xfont = PANGO_XFT_FONT (fcfont);
460 
461   if (xfont->xft_font)
462     {
463       Display *display;
464 
465       _pango_xft_font_map_get_info (fcfont->fontmap, &display, NULL);
466       XftFontClose (display, xfont->xft_font);
467       xfont->xft_font = NULL;
468     }
469 }
470 
471 /**
472  * pango_xft_font_get_font: (skip)
473  * @font: (nullable): a `PangoFont`
474  *
475  * Returns the `XftFont` of a font.
476  *
477  * Return value: (nullable): the `XftFont` associated to @font
478  */
479 XftFont *
pango_xft_font_get_font(PangoFont * font)480 pango_xft_font_get_font (PangoFont *font)
481 {
482   if (G_UNLIKELY (!font))
483     return NULL;
484 
485   return xft_font_get_font (font);
486 }
487 
488 /**
489  * pango_xft_font_get_display: (skip)
490  * @font: (type PangoXftFont): a `PangoFont`
491  *
492  * Returns the X display of the `XftFont` of a font.
493  *
494  * Return value: (transfer none): the X display of the XftFont associated to @font.
495  **/
496 Display *
pango_xft_font_get_display(PangoFont * font)497 pango_xft_font_get_display (PangoFont *font)
498 {
499   PangoFcFont *fcfont;
500   Display *display;
501 
502   g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL);
503 
504   fcfont = PANGO_FC_FONT (font);
505   _pango_xft_font_map_get_info (fcfont->fontmap, &display, NULL);
506 
507   return display;
508 }
509 
510 /**
511  * pango_xft_font_get_unknown_glyph:
512  * @font: (type PangoXftFont): a `PangoFont`
513  * @wc: the Unicode character for which a glyph is needed.
514  *
515  * Returns the index of a glyph suitable for drawing @wc as an
516  * unknown character.
517  *
518  * Use PANGO_GET_UNKNOWN_GLYPH() instead.
519  *
520  * Return value: a glyph index into @font.
521  **/
522 PangoGlyph
pango_xft_font_get_unknown_glyph(PangoFont * font,gunichar wc)523 pango_xft_font_get_unknown_glyph (PangoFont *font,
524 				  gunichar   wc)
525 {
526   g_return_val_if_fail (PANGO_XFT_IS_FONT (font), PANGO_GLYPH_EMPTY);
527 
528   return PANGO_GET_UNKNOWN_GLYPH (wc);
529 }
530 
531 /**
532  * pango_xft_font_lock_face: (skip)
533  * @font: (type PangoXftFont): a `PangoFont`
534  *
535  * Gets the FreeType `FT_Face` associated with a font.
536  *
537  * This face will be kept around until you call pango_xft_font_unlock_face().
538  *
539  * Use pango_fc_font_lock_face() instead.
540  *
541  * Return value: the FreeType `FT_Face` associated with @font.
542  *
543  * Since: 1.2
544  **/
545 FT_Face
pango_xft_font_lock_face(PangoFont * font)546 pango_xft_font_lock_face (PangoFont *font)
547 {
548   g_return_val_if_fail (PANGO_XFT_IS_FONT (font), NULL);
549 
550   return pango_fc_font_lock_face (PANGO_FC_FONT (font));
551 }
552 
553 /**
554  * pango_xft_font_unlock_face: (skip)
555  * @font: (type PangoXftFont): a `PangoFont`
556  *
557  * Releases a font previously obtained with
558  * pango_xft_font_lock_face().
559  *
560  * Use pango_fc_font_unlock_face() instead.
561  *
562  * Since: 1.2
563  **/
564 void
pango_xft_font_unlock_face(PangoFont * font)565 pango_xft_font_unlock_face (PangoFont *font)
566 {
567   g_return_if_fail (PANGO_XFT_IS_FONT (font));
568 
569   pango_fc_font_unlock_face (PANGO_FC_FONT (font));
570 }
571 
572 /**
573  * pango_xft_font_get_glyph:
574  * @font: (type PangoXftFont): a `PangoFont` for the Xft backend
575  * @wc: Unicode codepoint to look up
576  *
577  * Gets the glyph index for a given Unicode character
578  * for @font.
579  *
580  * If you only want to determine whether the font has
581  * the glyph, use pango_xft_font_has_char().
582  *
583  * Use pango_fc_font_get_glyph() instead.
584  *
585  * Return value: the glyph index, or 0, if the Unicode
586  *  character does not exist in the font.
587  *
588  * Since: 1.2
589  **/
590 guint
pango_xft_font_get_glyph(PangoFont * font,gunichar wc)591 pango_xft_font_get_glyph (PangoFont *font,
592 			  gunichar   wc)
593 {
594   g_return_val_if_fail (PANGO_XFT_IS_FONT (font), 0);
595 
596   return pango_fc_font_get_glyph (PANGO_FC_FONT (font), wc);
597 }
598 
599 /**
600  * pango_xft_font_has_char:
601  * @font: (type PangoXftFont): a `PangoFont` for the Xft backend
602  * @wc: Unicode codepoint to look up
603  *
604  * Determines whether @font has a glyph for the codepoint @wc.
605  *
606  * Use pango_fc_font_has_char() instead.
607  *
608  * Return value: %TRUE if @font has the requested codepoint.
609  *
610  * Since: 1.2
611  **/
612 gboolean
pango_xft_font_has_char(PangoFont * font,gunichar wc)613 pango_xft_font_has_char (PangoFont *font,
614 			 gunichar   wc)
615 {
616   g_return_val_if_fail (PANGO_XFT_IS_FONT (font), 0);
617 
618   return pango_fc_font_has_char (PANGO_FC_FONT (font), wc);
619 }
620