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