7c382b34942cc1809ad1b6ffae1dd08c51f9d330
[external/pango1.0.git] / pango / pango-fontset.c
1 /* Pango
2  * pango-fontset.c:
3  *
4  * Copyright (C) 2001 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 /*
25  * PangoFontset
26  */
27
28 #include "pango-types.h"
29 #include "pango-font.h"
30 #include "pango-fontset.h"
31 #include "pango-impl-utils.h"
32
33 static PangoFontMetrics *pango_fontset_real_get_metrics (PangoFontset      *fontset);
34
35
36 G_DEFINE_ABSTRACT_TYPE (PangoFontset, pango_fontset, G_TYPE_OBJECT);
37
38 static void
39 pango_fontset_init (PangoFontset *self)
40 {
41 }
42
43 static void
44 pango_fontset_class_init (PangoFontsetClass *class)
45 {
46   class->get_metrics = pango_fontset_real_get_metrics;
47 }
48
49
50 /**
51  * pango_fontset_get_font:
52  * @fontset: a #PangoFontset
53  * @wc: a Unicode character
54  *
55  * Returns the font in the fontset that contains the best glyph for the
56  * Unicode character @wc.
57  *
58  * Return value: (transfer full): a #PangoFont. The caller must call
59  *          g_object_unref when finished with the font.
60  **/
61 PangoFont *
62 pango_fontset_get_font (PangoFontset  *fontset,
63                         guint          wc)
64 {
65
66   g_return_val_if_fail (PANGO_IS_FONTSET (fontset), NULL);
67
68   return PANGO_FONTSET_GET_CLASS (fontset)->get_font (fontset, wc);
69 }
70
71 /**
72  * pango_fontset_get_metrics:
73  * @fontset: a #PangoFontset
74  *
75  * Get overall metric information for the fonts in the fontset.
76  *
77  * Return value: a #PangoFontMetrics object. The caller must call pango_font_metrics_unref()
78  *   when finished using the object.
79  **/
80 PangoFontMetrics *
81 pango_fontset_get_metrics (PangoFontset  *fontset)
82 {
83   g_return_val_if_fail (PANGO_IS_FONTSET (fontset), NULL);
84
85   return PANGO_FONTSET_GET_CLASS (fontset)->get_metrics (fontset);
86 }
87
88 /**
89  * pango_fontset_foreach:
90  * @fontset: a #PangoFontset
91  * @func: (closure data) (scope call): Callback function
92  * @data: (closure): data to pass to the callback function
93  *
94  * Iterates through all the fonts in a fontset, calling @func for
95  * each one. If @func returns %TRUE, that stops the iteration.
96  *
97  * Since: 1.4
98  **/
99 void
100 pango_fontset_foreach (PangoFontset           *fontset,
101                        PangoFontsetForeachFunc func,
102                        gpointer                data)
103 {
104   g_return_if_fail (PANGO_IS_FONTSET (fontset));
105   g_return_if_fail (func != NULL);
106
107   PANGO_FONTSET_GET_CLASS (fontset)->foreach (fontset, func, data);
108 }
109
110 static gboolean
111 get_first_metrics_foreach (PangoFontset  *fontset,
112                            PangoFont     *font,
113                            gpointer       data)
114 {
115   PangoFontMetrics *fontset_metrics = data;
116   PangoLanguage *language = PANGO_FONTSET_GET_CLASS (fontset)->get_language (fontset);
117   PangoFontMetrics *font_metrics = pango_font_get_metrics (font, language);
118   guint save_ref_count;
119
120   /* Initialize the fontset metrics to metrics of the first font in the
121    * fontset; saving the refcount and restoring it is a bit of hack but avoids
122    * having to update this code for each metrics addition.
123    */
124   save_ref_count = fontset_metrics->ref_count;
125   *fontset_metrics = *font_metrics;
126   fontset_metrics->ref_count = save_ref_count;
127
128   pango_font_metrics_unref (font_metrics);
129
130   return TRUE;                  /* Stops iteration */
131 }
132
133 static PangoFontMetrics *
134 pango_fontset_real_get_metrics (PangoFontset  *fontset)
135 {
136   PangoFontMetrics *metrics, *raw_metrics;
137   const char *sample_str;
138   const char *p;
139   int count;
140   GHashTable *fonts_seen;
141   PangoFont *font;
142   PangoLanguage *language;
143
144   language = PANGO_FONTSET_GET_CLASS (fontset)->get_language (fontset);
145   sample_str = pango_language_get_sample_string (language);
146
147   count = 0;
148   metrics = pango_font_metrics_new ();
149   fonts_seen = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
150
151   /* Initialize the metrics from the first font in the fontset */
152   pango_fontset_foreach (fontset, get_first_metrics_foreach, metrics);
153
154   p = sample_str;
155   while (*p)
156     {
157       gunichar wc = g_utf8_get_char (p);
158       font = pango_fontset_get_font (fontset, wc);
159       if (font)
160         {
161           if (g_hash_table_lookup (fonts_seen, font) == NULL)
162             {
163               raw_metrics = pango_font_get_metrics (font, language);
164               g_hash_table_insert (fonts_seen, font, font);
165
166               if (count == 0)
167                 {
168                   metrics->ascent = raw_metrics->ascent;
169                   metrics->descent = raw_metrics->descent;
170                   metrics->approximate_char_width = raw_metrics->approximate_char_width;
171                   metrics->approximate_digit_width = raw_metrics->approximate_digit_width;
172                 }
173               else
174                 {
175                   metrics->ascent = MAX (metrics->ascent, raw_metrics->ascent);
176                   metrics->descent = MAX (metrics->descent, raw_metrics->descent);
177                   metrics->approximate_char_width += raw_metrics->approximate_char_width;
178                   metrics->approximate_digit_width += raw_metrics->approximate_digit_width;
179                 }
180               count++;
181               pango_font_metrics_unref (raw_metrics);
182             }
183           else
184             g_object_unref (font);
185         }
186
187       p = g_utf8_next_char (p);
188     }
189
190   g_hash_table_destroy (fonts_seen);
191
192   if (count)
193     {
194       metrics->approximate_char_width /= count;
195       metrics->approximate_digit_width /= count;
196     }
197
198   return metrics;
199 }
200
201
202 /*
203  * PangoFontsetSimple
204  */
205
206 #define PANGO_FONTSET_SIMPLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FONTSET_SIMPLE, PangoFontsetSimpleClass))
207 #define PANGO_IS_FONTSET_SIMPLE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FONTSET_SIMPLE))
208 #define PANGO_FONTSET_SIMPLE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FONTSET_SIMPLE, PangoFontsetSimpleClass))
209
210 static void              pango_fontset_simple_finalize     (GObject                 *object);
211 static PangoFontMetrics *pango_fontset_simple_get_metrics  (PangoFontset            *fontset);
212 static PangoLanguage *   pango_fontset_simple_get_language (PangoFontset            *fontset);
213 static  PangoFont *      pango_fontset_simple_get_font     (PangoFontset            *fontset,
214                                                             guint                    wc);
215 static void              pango_fontset_simple_foreach      (PangoFontset            *fontset,
216                                                             PangoFontsetForeachFunc  func,
217                                                             gpointer                 data);
218
219 struct _PangoFontsetSimple
220 {
221   PangoFontset parent_instance;
222
223   GPtrArray *fonts;
224   GPtrArray *coverages;
225   PangoLanguage *language;
226 };
227
228 struct _PangoFontsetSimpleClass
229 {
230   PangoFontsetClass parent_class;
231 };
232
233 /**
234  * pango_fontset_simple_new:
235  * @language: a #PangoLanguage tag
236  *
237  * Creates a new #PangoFontsetSimple for the given language.
238  *
239  * Return value: the newly allocated #PangoFontsetSimple, which should
240  *               be freed with g_object_unref().
241  **/
242 PangoFontsetSimple *
243 pango_fontset_simple_new (PangoLanguage *language)
244 {
245   PangoFontsetSimple *fontset;
246
247   fontset = g_object_new (PANGO_TYPE_FONTSET_SIMPLE, NULL);
248   fontset->language = language;
249
250   return fontset;
251 }
252
253
254 G_DEFINE_TYPE (PangoFontsetSimple, pango_fontset_simple, PANGO_TYPE_FONTSET);
255
256 static void
257 pango_fontset_simple_class_init (PangoFontsetSimpleClass *class)
258 {
259   GObjectClass *object_class = G_OBJECT_CLASS (class);
260   PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (class);
261
262   object_class->finalize = pango_fontset_simple_finalize;
263
264   fontset_class->get_font = pango_fontset_simple_get_font;
265   fontset_class->get_metrics = pango_fontset_simple_get_metrics;
266   fontset_class->get_language = pango_fontset_simple_get_language;
267   fontset_class->foreach = pango_fontset_simple_foreach;
268 }
269
270 static void
271 pango_fontset_simple_init (PangoFontsetSimple *fontset)
272 {
273   fontset->fonts = g_ptr_array_new ();
274   fontset->coverages = g_ptr_array_new ();
275   fontset->language = NULL;
276 }
277
278 static void
279 pango_fontset_simple_finalize (GObject *object)
280 {
281   PangoFontsetSimple *fontset = PANGO_FONTSET_SIMPLE (object);
282   PangoCoverage *coverage;
283   unsigned int i;
284
285   for (i = 0; i < fontset->fonts->len; i++)
286     g_object_unref (g_ptr_array_index(fontset->fonts, i));
287
288   g_ptr_array_free (fontset->fonts, TRUE);
289
290   for (i = 0; i < fontset->coverages->len; i++)
291     {
292       coverage = g_ptr_array_index (fontset->coverages, i);
293       if (coverage)
294         pango_coverage_unref (coverage);
295     }
296
297   g_ptr_array_free (fontset->coverages, TRUE);
298
299   G_OBJECT_CLASS (pango_fontset_simple_parent_class)->finalize (object);
300 }
301
302 /**
303  * pango_fontset_simple_append:
304  * @fontset: a #PangoFontsetSimple.
305  * @font: a #PangoFont.
306  *
307  * Adds a font to the fontset.
308  **/
309 void
310 pango_fontset_simple_append (PangoFontsetSimple *fontset,
311                              PangoFont          *font)
312 {
313   g_ptr_array_add (fontset->fonts, font);
314   g_ptr_array_add (fontset->coverages, NULL);
315 }
316
317 /**
318  * pango_fontset_simple_size:
319  * @fontset: a #PangoFontsetSimple.
320  *
321  * Returns the number of fonts in the fontset.
322  *
323  * Return value: the size of @fontset.
324  **/
325 int
326 pango_fontset_simple_size (PangoFontsetSimple *fontset)
327 {
328   return fontset->fonts->len;
329 }
330
331 static PangoLanguage *
332 pango_fontset_simple_get_language (PangoFontset  *fontset)
333 {
334   PangoFontsetSimple *simple = PANGO_FONTSET_SIMPLE (fontset);
335
336   return simple->language;
337 }
338
339 static PangoFontMetrics *
340 pango_fontset_simple_get_metrics (PangoFontset  *fontset)
341 {
342   PangoFontsetSimple *simple = PANGO_FONTSET_SIMPLE (fontset);
343
344   if (simple->fonts->len == 1)
345     return pango_font_get_metrics (PANGO_FONT (g_ptr_array_index(simple->fonts, 0)),
346                                    simple->language);
347
348   return PANGO_FONTSET_CLASS (pango_fontset_simple_parent_class)->get_metrics (fontset);
349 }
350
351 static PangoFont *
352 pango_fontset_simple_get_font (PangoFontset  *fontset,
353                                guint          wc)
354 {
355   PangoFontsetSimple *simple = PANGO_FONTSET_SIMPLE (fontset);
356   PangoCoverageLevel best_level = PANGO_COVERAGE_NONE;
357   PangoCoverageLevel level;
358   PangoFont *font;
359   PangoCoverage *coverage;
360   int result = -1;
361   unsigned int i;
362
363   for (i = 0; i < simple->fonts->len; i++)
364     {
365       coverage = g_ptr_array_index (simple->coverages, i);
366
367       if (coverage == NULL)
368         {
369           font = g_ptr_array_index (simple->fonts, i);
370
371           coverage = pango_font_get_coverage (font, simple->language);
372           g_ptr_array_index (simple->coverages, i) = coverage;
373         }
374
375       level = pango_coverage_get (coverage, wc);
376
377       if (result == -1 || level > best_level)
378         {
379           result = i;
380           best_level = level;
381           if (level == PANGO_COVERAGE_EXACT)
382             break;
383         }
384     }
385
386   if (G_UNLIKELY (result == -1))
387     return NULL;
388
389   font = g_ptr_array_index(simple->fonts, result);
390   return g_object_ref (font);
391 }
392
393 static void
394 pango_fontset_simple_foreach (PangoFontset           *fontset,
395                               PangoFontsetForeachFunc func,
396                               gpointer                data)
397 {
398   PangoFontsetSimple *simple = PANGO_FONTSET_SIMPLE (fontset);
399   unsigned int i;
400
401   for (i = 0; i < simple->fonts->len; i++)
402     {
403       if ((*func) (fontset,
404                    g_ptr_array_index (simple->fonts, i),
405                    data))
406         return;
407     }
408 }