1/* Pango
2 * pangoft2.c: Routines for handling FreeType2 fonts
3 *
4 * Copyright (C) 1999 Red Hat Software
5 * Copyright (C) 2000 Tor Lillqvist
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23#include "config.h"
24
25#include <string.h>
26#include <stdlib.h>
27#include <math.h>
28#include <glib.h>
29#include <glib/gprintf.h>
30
31#include "pangoft2.h"
32#include "pangoft2-private.h"
33#include "pangofc-fontmap-private.h"
34#include "pangofc-private.h"
35
36/* for compatibility with older freetype versions */
37#ifndef FT_LOAD_TARGET_MONO
38#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
39#endif
40
41#define PANGO_FT2_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FT2_FONT, PangoFT2FontClass))
42#define PANGO_FT2_IS_FONT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FT2_FONT))
43#define PANGO_FT2_FONT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FT2_FONT, PangoFT2FontClass))
44
45typedef struct _PangoFT2FontClass PangoFT2FontClass;
46
47struct _PangoFT2FontClass
48{
49 PangoFcFontClass parent_class;
50};
51
52static void pango_ft2_font_finalize (GObject *object);
53
54static void pango_ft2_font_get_glyph_extents (PangoFont *font,
55 PangoGlyph glyph,
56 PangoRectangle *ink_rect,
57 PangoRectangle *logical_rect);
58
59static FT_Face pango_ft2_font_real_lock_face (PangoFcFont *font);
60static void pango_ft2_font_real_unlock_face (PangoFcFont *font);
61
62
63PangoFT2Font *
64_pango_ft2_font_new (PangoFT2FontMap *ft2fontmap,
65 FcPattern *pattern)
66{
67 PangoFontMap *fontmap = PANGO_FONT_MAP (ft2fontmap);
68 PangoFT2Font *ft2font;
69 double d;
70
71 g_return_val_if_fail (fontmap != NULL, NULL);
72 g_return_val_if_fail (pattern != NULL, NULL);
73
74 ft2font = (PangoFT2Font *)g_object_new (PANGO_TYPE_FT2_FONT,
75 first_property_name: "pattern", pattern,
76 "fontmap", fontmap,
77 NULL);
78
79 if (FcPatternGetDouble (p: pattern, FC_PIXEL_SIZE, n: 0, d: &d) == FcResultMatch)
80 ft2font->size = d*PANGO_SCALE;
81
82 return ft2font;
83}
84
85static void
86load_fallback_face (PangoFT2Font *ft2font,
87 const char *original_file)
88{
89 PangoFcFont *fcfont = PANGO_FC_FONT (ft2font);
90 FcPattern *sans;
91 FcPattern *matched;
92 FcResult result;
93 FT_Error error;
94 FcChar8 *filename2 = NULL;
95 gchar *name;
96 int id;
97
98 sans = FcPatternBuild (NULL,
99 FC_FAMILY, FcTypeString, "sans",
100 FC_PIXEL_SIZE, FcTypeDouble, (double)ft2font->size / PANGO_SCALE,
101 NULL);
102
103 _pango_ft2_font_map_default_substitute (fcfontmap: (PangoFcFontMap *)fcfont->fontmap, pattern: sans);
104
105 matched = FcFontMatch (config: pango_fc_font_map_get_config (fcfontmap: (PangoFcFontMap *)fcfont->fontmap), p: sans, result: &result);
106
107 if (FcPatternGetString (p: matched, FC_FILE, n: 0, s: &filename2) != FcResultMatch)
108 goto bail1;
109
110 if (FcPatternGetInteger (p: matched, FC_INDEX, n: 0, i: &id) != FcResultMatch)
111 goto bail1;
112
113 error = FT_New_Face (library: _pango_ft2_font_map_get_library (fontmap: fcfont->fontmap),
114 filepathname: (char *) filename2, face_index: id, aface: &ft2font->face);
115
116
117 if (error)
118 {
119 bail1:
120 name = pango_font_description_to_string (desc: fcfont->description);
121 g_error ("Unable to open font file %s for font %s, exiting\n", filename2, name);
122 }
123 else
124 {
125 name = pango_font_description_to_string (desc: fcfont->description);
126 g_warning ("Unable to open font file %s for font %s, falling back to %s\n", original_file, name, filename2);
127 g_free (mem: name);
128 }
129
130 FcPatternDestroy (p: sans);
131 FcPatternDestroy (p: matched);
132}
133
134static void
135set_transform (PangoFT2Font *ft2font)
136{
137 PangoFcFont *fcfont = (PangoFcFont *)ft2font;
138 FcMatrix *fc_matrix;
139
140 if (FcPatternGetMatrix (p: fcfont->font_pattern, FC_MATRIX, n: 0, s: &fc_matrix) == FcResultMatch)
141 {
142 FT_Matrix ft_matrix;
143
144 ft_matrix.xx = 0x10000L * fc_matrix->xx;
145 ft_matrix.yy = 0x10000L * fc_matrix->yy;
146 ft_matrix.xy = 0x10000L * fc_matrix->xy;
147 ft_matrix.yx = 0x10000L * fc_matrix->yx;
148
149 FT_Set_Transform (face: ft2font->face, matrix: &ft_matrix, NULL);
150 }
151}
152
153/**
154 * pango_ft2_font_get_face: (skip)
155 * @font: a `PangoFont`
156 *
157 * Returns the native FreeType2 `FT_Face` structure
158 * used for this `PangoFont`.
159 *
160 * This may be useful if you want to use FreeType2
161 * functions directly.
162 *
163 * Use [method@PangoFc.Font.lock_face] instead; when you are
164 * done with a face from [method@PangoFc.Font.lock_face], you
165 * must call [method@PangoFc.Font.unlock_face].
166 *
167 * Return value: (nullable): a pointer to a `FT_Face` structure,
168 * with the size set correctly
169 */
170FT_Face
171pango_ft2_font_get_face (PangoFont *font)
172{
173 PangoFT2Font *ft2font = (PangoFT2Font *)font;
174 PangoFcFont *fcfont = (PangoFcFont *)font;
175 FT_Error error;
176 FcPattern *pattern;
177 FcChar8 *filename;
178 FcBool antialias, hinting, autohint;
179 int hintstyle;
180 int id;
181
182 if (G_UNLIKELY (!font))
183 return NULL;
184
185 pattern = fcfont->font_pattern;
186
187 if (!ft2font->face)
188 {
189 ft2font->load_flags = 0;
190
191 /* disable antialiasing if requested */
192 if (FcPatternGetBool (p: pattern,
193 FC_ANTIALIAS, n: 0, b: &antialias) != FcResultMatch)
194 antialias = FcTrue;
195
196 if (antialias)
197 ft2font->load_flags |= FT_LOAD_NO_BITMAP;
198 else
199 ft2font->load_flags |= FT_LOAD_TARGET_MONO;
200
201 /* disable hinting if requested */
202 if (FcPatternGetBool (p: pattern,
203 FC_HINTING, n: 0, b: &hinting) != FcResultMatch)
204 hinting = FcTrue;
205
206#ifdef FC_HINT_STYLE
207 if (FcPatternGetInteger (p: pattern, FC_HINT_STYLE, n: 0, i: &hintstyle) != FcResultMatch)
208 hintstyle = FC_HINT_FULL;
209
210 if (!hinting || hintstyle == FC_HINT_NONE)
211 ft2font->load_flags |= FT_LOAD_NO_HINTING;
212
213 switch (hintstyle) {
214 case FC_HINT_SLIGHT:
215 case FC_HINT_MEDIUM:
216 ft2font->load_flags |= FT_LOAD_TARGET_LIGHT;
217 break;
218 default:
219 ft2font->load_flags |= FT_LOAD_TARGET_NORMAL;
220 break;
221 }
222#else
223 if (!hinting)
224 ft2font->load_flags |= FT_LOAD_NO_HINTING;
225#endif
226
227 /* force autohinting if requested */
228 if (FcPatternGetBool (p: pattern,
229 FC_AUTOHINT, n: 0, b: &autohint) != FcResultMatch)
230 autohint = FcFalse;
231
232 if (autohint)
233 ft2font->load_flags |= FT_LOAD_FORCE_AUTOHINT;
234
235 if (FcPatternGetString (p: pattern, FC_FILE, n: 0, s: &filename) != FcResultMatch)
236 goto bail0;
237
238 if (FcPatternGetInteger (p: pattern, FC_INDEX, n: 0, i: &id) != FcResultMatch)
239 goto bail0;
240
241 error = FT_New_Face (library: _pango_ft2_font_map_get_library (fontmap: fcfont->fontmap),
242 filepathname: (char *) filename, face_index: id, aface: &ft2font->face);
243 if (error != FT_Err_Ok)
244 {
245 bail0:
246 load_fallback_face (ft2font, original_file: (char *) filename);
247 }
248
249 g_assert (ft2font->face);
250
251 set_transform (ft2font);
252
253 error = FT_Set_Char_Size (face: ft2font->face,
254 PANGO_PIXELS_26_6 (ft2font->size),
255 PANGO_PIXELS_26_6 (ft2font->size),
256 horz_resolution: 0, vert_resolution: 0);
257 if (error)
258 g_warning ("Error in FT_Set_Char_Size: %d", error);
259 }
260
261 return ft2font->face;
262}
263
264G_DEFINE_TYPE (PangoFT2Font, pango_ft2_font, PANGO_TYPE_FC_FONT)
265
266static void
267pango_ft2_font_init (PangoFT2Font *ft2font)
268{
269 ft2font->face = NULL;
270
271 ft2font->size = 0;
272
273 ft2font->glyph_info = g_hash_table_new (NULL, NULL);
274}
275
276static void
277pango_ft2_font_class_init (PangoFT2FontClass *class)
278{
279 GObjectClass *object_class = G_OBJECT_CLASS (class);
280 PangoFontClass *font_class = PANGO_FONT_CLASS (class);
281 PangoFcFontClass *fc_font_class = PANGO_FC_FONT_CLASS (class);
282
283 object_class->finalize = pango_ft2_font_finalize;
284
285 font_class->get_glyph_extents = pango_ft2_font_get_glyph_extents;
286
287 fc_font_class->lock_face = pango_ft2_font_real_lock_face;
288 fc_font_class->unlock_face = pango_ft2_font_real_unlock_face;
289}
290
291static PangoFT2GlyphInfo *
292pango_ft2_font_get_glyph_info (PangoFont *font,
293 PangoGlyph glyph,
294 gboolean create)
295{
296 PangoFT2Font *ft2font = (PangoFT2Font *)font;
297 PangoFcFont *fcfont = (PangoFcFont *)font;
298 PangoFT2GlyphInfo *info;
299
300 info = g_hash_table_lookup (hash_table: ft2font->glyph_info, GUINT_TO_POINTER (glyph));
301
302 if ((info == NULL) && create)
303 {
304 info = g_slice_new0 (PangoFT2GlyphInfo);
305
306 pango_fc_font_get_raw_extents (font: fcfont,
307 glyph,
308 ink_rect: &info->ink_rect,
309 logical_rect: &info->logical_rect);
310
311 g_hash_table_insert (hash_table: ft2font->glyph_info, GUINT_TO_POINTER(glyph), value: info);
312 }
313
314 return info;
315}
316
317static void
318pango_ft2_font_get_glyph_extents (PangoFont *font,
319 PangoGlyph glyph,
320 PangoRectangle *ink_rect,
321 PangoRectangle *logical_rect)
322{
323 PangoFT2GlyphInfo *info;
324 gboolean empty = FALSE;
325
326 if (glyph == PANGO_GLYPH_EMPTY)
327 {
328 glyph = pango_fc_font_get_glyph (font: (PangoFcFont *) font, wc: ' ');
329 empty = TRUE;
330 }
331
332 if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
333 {
334 PangoFontMetrics *metrics = pango_font_get_metrics (font, NULL);
335
336 if (metrics)
337 {
338 if (ink_rect)
339 {
340 ink_rect->x = PANGO_SCALE;
341 ink_rect->width = metrics->approximate_char_width - 2 * PANGO_SCALE;
342 ink_rect->y = - (metrics->ascent - PANGO_SCALE);
343 ink_rect->height = metrics->ascent + metrics->descent - 2 * PANGO_SCALE;
344 }
345 if (logical_rect)
346 {
347 logical_rect->x = 0;
348 logical_rect->width = metrics->approximate_char_width;
349 logical_rect->y = -metrics->ascent;
350 logical_rect->height = metrics->ascent + metrics->descent;
351 }
352
353 pango_font_metrics_unref (metrics);
354 }
355 else
356 {
357 if (ink_rect)
358 ink_rect->x = ink_rect->y = ink_rect->height = ink_rect->width = 0;
359 if (logical_rect)
360 logical_rect->x = logical_rect->y = logical_rect->height = logical_rect->width = 0;
361 }
362 return;
363 }
364
365 info = pango_ft2_font_get_glyph_info (font, glyph, TRUE);
366
367 if (ink_rect)
368 *ink_rect = info->ink_rect;
369 if (logical_rect)
370 *logical_rect = info->logical_rect;
371
372 if (empty)
373 {
374 if (ink_rect)
375 ink_rect->x = ink_rect->y = ink_rect->height = ink_rect->width = 0;
376 if (logical_rect)
377 logical_rect->x = logical_rect->width = 0;
378 return;
379 }
380}
381
382/**
383 * pango_ft2_font_get_kerning:
384 * @font: a `PangoFont`
385 * @left: the left `PangoGlyph`
386 * @right: the right `PangoGlyph`
387 *
388 * Retrieves kerning information for a combination of two glyphs.
389 *
390 * Use pango_fc_font_kern_glyphs() instead.
391 *
392 * Return value: The amount of kerning (in Pango units) to
393 * apply for the given combination of glyphs.
394 */
395int
396pango_ft2_font_get_kerning (PangoFont *font,
397 PangoGlyph left,
398 PangoGlyph right)
399{
400 PangoFcFont *fc_font = PANGO_FC_FONT (font);
401
402 FT_Face face;
403 FT_Error error;
404 FT_Vector kerning;
405
406 face = pango_fc_font_lock_face (font: fc_font);
407 if (!face)
408 return 0;
409
410 if (!FT_HAS_KERNING (face))
411 {
412 pango_fc_font_unlock_face (font: fc_font);
413 return 0;
414 }
415
416 error = FT_Get_Kerning (face, left_glyph: left, right_glyph: right, ft_kerning_default, akerning: &kerning);
417 if (error != FT_Err_Ok)
418 {
419 pango_fc_font_unlock_face (font: fc_font);
420 return 0;
421 }
422
423 pango_fc_font_unlock_face (font: fc_font);
424 return PANGO_UNITS_26_6 (kerning.x);
425}
426
427static FT_Face
428pango_ft2_font_real_lock_face (PangoFcFont *font)
429{
430 return pango_ft2_font_get_face (font: (PangoFont *)font);
431}
432
433static void
434pango_ft2_font_real_unlock_face (PangoFcFont *font G_GNUC_UNUSED)
435{
436}
437
438static gboolean
439pango_ft2_free_glyph_info_callback (gpointer key G_GNUC_UNUSED,
440 gpointer value,
441 gpointer data)
442{
443 PangoFT2Font *font = PANGO_FT2_FONT (data);
444 PangoFT2GlyphInfo *info = value;
445
446 if (font->glyph_cache_destroy && info->cached_glyph)
447 (*font->glyph_cache_destroy) (info->cached_glyph);
448
449 g_slice_free (PangoFT2GlyphInfo, info);
450 return TRUE;
451}
452
453static void
454pango_ft2_font_finalize (GObject *object)
455{
456 PangoFT2Font *ft2font = (PangoFT2Font *)object;
457
458 if (ft2font->face)
459 {
460 FT_Done_Face (face: ft2font->face);
461 ft2font->face = NULL;
462 }
463
464 g_hash_table_foreach_remove (hash_table: ft2font->glyph_info,
465 func: pango_ft2_free_glyph_info_callback, user_data: object);
466 g_hash_table_destroy (hash_table: ft2font->glyph_info);
467
468 G_OBJECT_CLASS (pango_ft2_font_parent_class)->finalize (object);
469}
470
471/**
472 * pango_ft2_font_get_coverage:
473 * @font: a Pango FT2 font
474 * @language: a language tag.
475 *
476 * Gets the `PangoCoverage` for a `PangoFT2Font`.
477 *
478 * Use [method@Pango.Font.get_coverage] instead.
479 *
480 * Return value: (transfer full): a `PangoCoverage`
481 */
482PangoCoverage *
483pango_ft2_font_get_coverage (PangoFont *font,
484 PangoLanguage *language)
485{
486 return pango_font_get_coverage (font, language);
487}
488
489/* Utility functions */
490
491/**
492 * pango_ft2_get_unknown_glyph:
493 * @font: a `PangoFont`
494 *
495 * Return the index of a glyph suitable for drawing unknown
496 * characters with @font, or %PANGO_GLYPH_EMPTY if no suitable
497 * glyph found.
498 *
499 * If you want to draw an unknown-box for a character that
500 * is not covered by the font, use PANGO_GET_UNKNOWN_GLYPH()
501 * instead.
502 *
503 * Return value: a glyph index into @font, or %PANGO_GLYPH_EMPTY
504 */
505PangoGlyph
506pango_ft2_get_unknown_glyph (PangoFont *font)
507{
508 FT_Face face = pango_ft2_font_get_face (font);
509 if (face && FT_IS_SFNT (face))
510 /* TrueType fonts have an 'unknown glyph' box on glyph index 0 */
511 return 0;
512 else
513 return PANGO_GLYPH_EMPTY;
514}
515
516void *
517_pango_ft2_font_get_cache_glyph_data (PangoFont *font,
518 int glyph_index)
519{
520 PangoFT2GlyphInfo *info;
521
522 if (!PANGO_FT2_IS_FONT (font))
523 return NULL;
524
525 info = pango_ft2_font_get_glyph_info (font, glyph: glyph_index, FALSE);
526
527 if (info == NULL)
528 return NULL;
529
530 return info->cached_glyph;
531}
532
533void
534_pango_ft2_font_set_cache_glyph_data (PangoFont *font,
535 int glyph_index,
536 void *cached_glyph)
537{
538 PangoFT2GlyphInfo *info;
539
540 if (!PANGO_FT2_IS_FONT (font))
541 return;
542
543 info = pango_ft2_font_get_glyph_info (font, glyph: glyph_index, TRUE);
544
545 info->cached_glyph = cached_glyph;
546
547 /* TODO: Implement limiting of the number of cached glyphs */
548}
549
550void
551_pango_ft2_font_set_glyph_cache_destroy (PangoFont *font,
552 GDestroyNotify destroy_notify)
553{
554 if (!PANGO_FT2_IS_FONT (font))
555 return;
556
557 PANGO_FT2_FONT (font)->glyph_cache_destroy = destroy_notify;
558}
559

source code of gtk/subprojects/pango/pango/pangoft2.c