1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* This file is part of the GtkHTML library.
3  *
4  * Copyright (C) 2000 Helix Code, Inc.
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 License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20 */
21 
22 #include <config.h>
23 #include <string.h>
24 #include <pango/pango.h>
25 
26 #include "gtkhtmlfontstyle.h"
27 #include "htmlfontmanager.h"
28 #include "htmlpainter.h"
29 #include "htmlengine.h"
30 
31 static void
html_font_set_init(HTMLFontSet * set,gchar * face)32 html_font_set_init (HTMLFontSet *set,
33                     gchar *face)
34 {
35 	memset (set, 0, GTK_HTML_FONT_STYLE_MAX_FONT * sizeof (HTMLFont *));
36 	set->ref_count = 1;
37 	set->face = g_strdup (face);
38 }
39 
40 static HTMLFontSet *
html_font_set_new(gchar * face)41 html_font_set_new (gchar *face)
42 {
43 	HTMLFontSet *set;
44 
45 	set = g_new (HTMLFontSet, 1);
46 	html_font_set_init (set, face);
47 
48 	return set;
49 }
50 
51 static gboolean
html_font_set_face(HTMLFontSet * set,gchar * face)52 html_font_set_face (HTMLFontSet *set,
53                     gchar *face)
54 {
55 	if (!set->face || strcmp (set->face, face)) {
56 		if (set->face)
57 			g_free (set->face);
58 		set->face = g_strdup (face);
59 		return TRUE;
60 	}
61 	return FALSE;
62 }
63 
64 static void
html_font_set_release(HTMLFontSet * set,HTMLPainter * painter)65 html_font_set_release (HTMLFontSet *set,
66                        HTMLPainter *painter)
67 {
68 	gint i;
69 
70 	for (i = 0; i < GTK_HTML_FONT_STYLE_MAX_FONT; i++) {
71 		if (set->font[i])
72 			html_font_unref (set->font[i], painter);
73 		set->font[i] = NULL;
74 	}
75 }
76 
77 static void
html_font_set_unref(HTMLFontSet * set,HTMLPainter * painter)78 html_font_set_unref (HTMLFontSet *set,
79                      HTMLPainter *painter)
80 {
81 	set->ref_count--;
82 	if (!set->ref_count) {
83 		html_font_set_release (set, painter);
84 		if (set->face)
85 			g_free (set->face);
86 
87 		g_free (set);
88 	}
89 }
90 
91 void
html_font_manager_init(HTMLFontManager * manager,HTMLPainter * painter)92 html_font_manager_init (HTMLFontManager *manager,
93                         HTMLPainter *painter)
94 {
95 	manager->font_sets     = g_hash_table_new (g_str_hash, g_str_equal);
96 	manager->var_size      = 12 * PANGO_SCALE;
97 	manager->var_points    = FALSE;
98 	manager->fix_size      = 12 * PANGO_SCALE;
99 	manager->fix_points    = FALSE;
100 	manager->magnification = 1.0;
101 	manager->painter       = painter;
102 
103 	html_font_set_init (&manager->variable, NULL);
104 	html_font_set_init (&manager->fixed, NULL);
105 }
106 
107 void
html_font_manager_set_magnification(HTMLFontManager * manager,gdouble magnification)108 html_font_manager_set_magnification (HTMLFontManager *manager,
109                                      gdouble magnification)
110 {
111 	g_return_if_fail (magnification > 0.0);
112 
113 	if (magnification != manager->magnification) {
114 		manager->magnification = magnification;
115 		html_font_manager_clear_font_cache (manager);
116 	}
117 }
118 
119 static gboolean
destroy_font_set_foreach(gpointer key,gpointer font_set,gpointer data)120 destroy_font_set_foreach (gpointer key,
121                           gpointer font_set,
122                           gpointer data)
123 {
124 	g_free (key);
125 	html_font_set_unref (font_set, HTML_PAINTER (data));
126 
127 	return TRUE;
128 }
129 
130 static void
clear_additional_font_sets(HTMLFontManager * manager)131 clear_additional_font_sets (HTMLFontManager *manager)
132 {
133 	g_hash_table_foreach_remove (manager->font_sets, destroy_font_set_foreach, manager->painter);
134 }
135 
136 void
html_font_manager_clear_font_cache(HTMLFontManager * manager)137 html_font_manager_clear_font_cache (HTMLFontManager *manager)
138 {
139 	html_font_set_release (&manager->variable, manager->painter);
140 	html_font_set_release (&manager->fixed, manager->painter);
141 	clear_additional_font_sets (manager);
142 }
143 
144 void
html_font_manager_finalize(HTMLFontManager * manager)145 html_font_manager_finalize (HTMLFontManager *manager)
146 {
147 	html_font_set_release (&manager->variable, manager->painter);
148 	html_font_set_release (&manager->fixed, manager->painter);
149 	g_free (manager->fixed.face);
150 	g_free (manager->variable.face);
151 
152 	clear_additional_font_sets (manager);
153 	g_hash_table_destroy (manager->font_sets);
154 }
155 
156 void
html_font_manager_set_default(HTMLFontManager * manager,gchar * variable,gchar * fixed,gint var_size,gboolean var_points,gint fix_size,gboolean fix_points)157 html_font_manager_set_default (HTMLFontManager *manager,
158                                gchar *variable,
159                                gchar *fixed,
160                                gint var_size,
161                                gboolean var_points,
162                                gint fix_size,
163                                gboolean fix_points)
164 {
165 	gboolean changed = FALSE;
166 
167 	/* variable width fonts */
168 	changed = html_font_set_face (&manager->variable, variable);
169 	if (manager->var_size != var_size || manager->var_points != var_points) {
170 		manager->var_size = var_size;
171 		manager->var_points = var_points;
172 		clear_additional_font_sets (manager);
173 		changed = TRUE;
174 	}
175 
176 	if (changed) {
177 		html_font_set_release (&manager->variable, manager->painter);
178 	}
179 
180 	/* fixed width fonts */
181 	changed = html_font_set_face (&manager->fixed, fixed);
182 	if (manager->fix_size != fix_size || manager->fix_points != fix_points) {
183 		manager->fix_size = fix_size;
184 		manager->fix_points = fix_points;
185 		changed = TRUE;
186 	}
187 
188 	if (changed) {
189 		/*
190 		 * NOTE we clear both if fixed changes because the plain painter pulls nasty
191 		 * tricks with using fixed fonts in the variable manager so if the fixed font
192 		 * change the variable font may change too.
193  *               */
194 		html_font_set_release (&manager->variable, manager->painter);
195 		html_font_set_release (&manager->fixed, manager->painter);
196 	}
197 }
198 
199 static gint
get_font_num(GtkHTMLFontStyle style)200 get_font_num (GtkHTMLFontStyle style)
201 {
202 	style |= (style & GTK_HTML_FONT_STYLE_SIZE_MASK) ? 0 : GTK_HTML_FONT_STYLE_SIZE_3;
203 
204 	return (style & GTK_HTML_FONT_STYLE_MAX_FONT_MASK);
205 }
206 
207 static gint
html_font_set_get_idx(GtkHTMLFontStyle style)208 html_font_set_get_idx (GtkHTMLFontStyle style)
209 {
210 	return get_font_num (style) - 1;
211 }
212 
213 static HTMLFontSet *
get_font_set(HTMLFontManager * manager,gchar * face,GtkHTMLFontStyle style)214 get_font_set (HTMLFontManager *manager,
215               gchar *face,
216               GtkHTMLFontStyle style)
217 {
218 	return (face)
219 		? g_hash_table_lookup (manager->font_sets, face)
220 		: ((style & GTK_HTML_FONT_STYLE_FIXED) ? &manager->fixed : &manager->variable);
221 }
222 
223 static gdouble
get_real_font_size(HTMLFontManager * manager,GtkHTMLFontStyle style)224 get_real_font_size (HTMLFontManager *manager,
225                     GtkHTMLFontStyle style)
226 {
227 	gint size = (get_font_num (style) & GTK_HTML_FONT_STYLE_SIZE_MASK) -  GTK_HTML_FONT_STYLE_SIZE_3;
228 	gint base_size = style & GTK_HTML_FONT_STYLE_FIXED ? manager->fix_size : manager->var_size;
229 
230 	return manager->magnification * (base_size + (size > 0 ? (1 << size) : size) * base_size / 8.0);
231 }
232 
233 static void
html_font_set_font(HTMLFontManager * manager,HTMLFontSet * set,GtkHTMLFontStyle style,HTMLFont * font)234 html_font_set_font (HTMLFontManager *manager,
235                     HTMLFontSet *set,
236                     GtkHTMLFontStyle style,
237                     HTMLFont *font)
238 {
239 	gint idx;
240 
241 	g_assert (font);
242 	g_assert (set);
243 
244 	/* set font in font set */
245 	idx = html_font_set_get_idx (style);
246 	if (set->font[idx] && font != set->font[idx])
247 		html_font_unref (set->font[idx], manager->painter);
248 	set->font[idx] = font;
249 }
250 
251 static HTMLFont *
get_font(HTMLFontManager * manager,HTMLFontSet ** set,gchar * face,GtkHTMLFontStyle style)252 get_font (HTMLFontManager *manager,
253           HTMLFontSet **set,
254           gchar *face,
255           GtkHTMLFontStyle style)
256 {
257 	HTMLFont *font = NULL;
258 
259 	*set = get_font_set (manager, face, style);
260 	if (*set)
261 		font = (*set)->font[html_font_set_get_idx (style)];
262 	return font;
263 }
264 
265 gchar *
html_font_manager_get_attr(gchar * font_name,gint n)266 html_font_manager_get_attr (gchar *font_name,
267                             gint n)
268 {
269     gchar *s, *end;
270 
271     /* Search paramether */
272     for (s = font_name; n; n--,s++)
273 	    s = strchr (s,'-');
274 
275     if (s && *s != 0) {
276 	    end = strchr (s, '-');
277 	    if (end)
278 		    return g_strndup (s, end - s);
279 	    else
280 		    return g_strdup (s);
281     } else
282 	    return g_strdup ("Unknown");
283 }
284 
285 static gboolean
get_points(HTMLFontManager * manager,GtkHTMLFontStyle style)286 get_points (HTMLFontManager *manager,
287             GtkHTMLFontStyle style)
288 {
289 	return (style & GTK_HTML_FONT_STYLE_FIXED) ? manager->fix_points : manager->var_points;
290 }
291 
292 static gpointer
manager_alloc_font(HTMLFontManager * manager,gchar * face,GtkHTMLFontStyle style)293 manager_alloc_font (HTMLFontManager *manager,
294                     gchar *face,
295                     GtkHTMLFontStyle style)
296 {
297 	return html_painter_alloc_font (manager->painter, face, get_real_font_size (manager, style), get_points (manager, style), style);
298 }
299 
300 static gchar *
strip_white_space(gchar * name)301 strip_white_space (gchar *name)
302 {
303 	gint end;
304 	while (name[0] == ' ' || name[0] == '\t')
305 		name++;
306 	end = strlen (name);
307 	while (end && (name[end - 1] == ' ' || name[end - 1] == '\t')) {
308 		name[end - 1] = 0;
309 		end--;
310 	}
311 
312 	return name;
313 }
314 
315 static HTMLFont *
alloc_new_font(HTMLFontManager * manager,HTMLFontSet ** set,gchar * face_list,GtkHTMLFontStyle style)316 alloc_new_font (HTMLFontManager *manager,
317                 HTMLFontSet **set,
318                 gchar *face_list,
319                 GtkHTMLFontStyle style)
320 {
321 	HTMLFont *font = NULL;
322 	gchar   **faces;
323 	gchar   **face;
324 
325 	if (!(*set)) {
326 		face = faces = g_strsplit (face_list, ",", 0);
327 		while (*face) {
328 			gchar *face_name = strip_white_space (*face);
329 
330 			/* first try to get font from available sets */
331 			font = get_font (manager, set, face_name, style);
332 			if (!font)
333 				font = manager_alloc_font (manager, face_name, style);
334 			if (font) {
335 				if (!(*set)) {
336 					*set = html_font_set_new (face_name);
337 					g_hash_table_insert (manager->font_sets, g_strdup (face_name), *set);
338 				}
339 				if (strcmp (face_list, *face)) {
340 					(*set)->ref_count++;
341 					g_hash_table_insert (manager->font_sets, g_strdup (face_list), *set);
342 				}
343 				break;
344 			}
345 			face++;
346 		}
347 		g_strfreev (faces);
348 		if (!(*set)) {
349 			/* none of faces exist, so create empty set for him and let manager later set fixed font here */
350 			*set = html_font_set_new (face_list);
351 			g_hash_table_insert (manager->font_sets, g_strdup (face_list), *set);
352 		}
353 	} else
354 		font = manager_alloc_font (manager, (*set)->face, style);
355 
356 	if ((*set) && font)
357 		html_font_set_font (manager, (*set), style, font);
358 
359 	return font;
360 }
361 
362 HTMLFont *
html_font_manager_get_font(HTMLFontManager * manager,gchar * face_list,GtkHTMLFontStyle style)363 html_font_manager_get_font (HTMLFontManager *manager,
364                             gchar *face_list,
365                             GtkHTMLFontStyle style)
366 {
367 	HTMLFontSet *set;
368 	HTMLFont *font = NULL;
369 
370 	font = get_font (manager, &set, face_list, style);
371 
372 	if (!font) {
373 		/* first try to alloc right one */
374 		font = alloc_new_font (manager, &set, face_list, style);
375 		if (!font) {
376 			g_assert (set);
377 			if (!face_list) {
378 				/* default font, so the last chance is to get fixed font */
379 				font = html_painter_alloc_font (manager->painter, NULL,
380 								get_real_font_size (manager, style),
381 								get_points (manager, style), style);
382 				if (!font)
383 					g_warning ("Cannot allocate fixed font\n");
384 			} else {
385 				/* some unavailable non-default font => use default one */
386 
387 			       font = html_font_manager_get_font (manager, NULL, style);
388 			       html_font_ref (font, manager->painter);
389 			}
390 			if (font)
391 				html_font_set_font (manager, set, style, font);
392 		}
393 	}
394 
395 	return font;
396 }
397 
398 HTMLFont *
html_font_new(gpointer data,guint space_width,guint space_asc,guint space_dsc,guint nbsp_width,guint tab_width,guint e_width,guint indent_width,guint cite_width_ltr,guint cite_width_rtl)399 html_font_new (gpointer data,
400                guint space_width,
401                guint space_asc,
402                guint space_dsc,
403                guint nbsp_width,
404                guint tab_width,
405                guint e_width,
406                guint indent_width,
407                guint cite_width_ltr,
408                guint cite_width_rtl)
409 {
410 	HTMLFont *font = g_new (HTMLFont, 1);
411 
412 	font->data = data;
413 	font->space_width = space_width;
414 	font->space_asc = space_asc;
415 	font->space_dsc = space_dsc;
416 	font->nbsp_width = nbsp_width;
417 	font->tab_width = tab_width;
418 	font->e_width = e_width;
419 	font->indent_width = indent_width;
420 	font->cite_width_ltr = cite_width_ltr;
421 	font->cite_width_rtl = cite_width_rtl;
422 	font->ref_count = 1;
423 
424 	return font;
425 }
426 
427 void
html_font_destroy(HTMLFont * font)428 html_font_destroy (HTMLFont *font)
429 {
430 	g_free (font);
431 }
432 
433 void
html_font_ref(HTMLFont * font,HTMLPainter * painter)434 html_font_ref (HTMLFont *font,
435                HTMLPainter *painter)
436 {
437 	html_painter_ref_font (painter, font);
438 	font->ref_count++;
439 }
440 
441 void
html_font_unref(HTMLFont * font,HTMLPainter * painter)442 html_font_unref (HTMLFont *font,
443                  HTMLPainter *painter)
444 {
445 	font->ref_count--;
446 	html_painter_unref_font (painter, font);
447 
448 	if (font->ref_count < 1)
449 		html_font_destroy (font);
450 }
451