splash screen support
[dia.git] / lib / font.c
blob8029bde932479b970d432205145fad6d9afe29a7
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 /* Some of the font-definitions were borrowed from xfig.
20 Here is the original copyright text from that code:
22 * FIG : Facility for Interactive Generation of figures
23 * Copyright (c) 1991 by Brian V. Smith
25 * The X Consortium, and any party obtaining a copy of these files from
26 * the X Consortium, directly or indirectly, is granted, free of charge, a
27 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
28 * nonexclusive right and license to deal in this software and
29 * documentation files (the "Software"), including without limitation the
30 * rights to use, copy, modify, merge, publish, distribute, sublicense,
31 * and/or sell copies of the Software subject to the restriction stated
32 * below, and to permit persons who receive copies from any such party to
33 * do so, with the only requirement being that this copyright notice remain
34 * intact.
35 * This license includes without limitation a license to do the foregoing
36 * actions under any patents of the party supplying this software to the
37 * X Consortium.
41 * The font suck code was taken from the gnome canvas text object
42 * bearing the following copyright header:
43 * Copyright (C) 1998 The Free Software Foundation
45 * Author: Federico Mena <federico@nuclecu.unam.mx>
49 #include <stdio.h>
50 #include <stdlib.h>
52 #include "config.h"
53 #include "intl.h"
54 #include "utils.h"
55 #include "font.h"
56 #include "color.h"
57 #include "message.h"
59 #define FONTCACHE_SIZE 17
61 #define NUM_X11_FONTS 2
63 typedef struct _FontPrivate FontPrivate;
64 typedef struct _FontCacheItem FontCacheItem;
66 struct _FontCacheItem {
67 int height;
68 GdkFont *gdk_font;
69 SuckFont *suck_font;
72 struct _FontPrivate {
73 Font public;
74 char *fontname_x11;
75 char **fontname_x11_vec;
76 char *fontname_ps;
77 FontCacheItem *cache[FONTCACHE_SIZE];
78 real ascent_ratio, descent_ratio;
82 typedef struct _FontData {
83 char *fontname;
84 char *fontname_ps;
85 char *fontname_x11[NUM_X11_FONTS]; /* First choice */
86 } FontData;
88 FontData font_data[] = {
89 { "Times-Roman",
90 "Times-Roman",
91 { "-adobe-times-medium-r-normal-*-%d-*-*-*-*-*-*-*",
92 NULL
94 },
95 { "Times-Italic",
96 "Times-Italic",
97 { "-adobe-times-medium-i-normal-*-%d-*-*-*-*-*-*-*",
98 NULL
101 { "Times-Bold",
102 "Times-Bold",
103 { "-adobe-times-bold-r-normal-*-%d-*-*-*-*-*-*-*",
104 NULL
107 { "Times-BoldItalic",
108 "Times-BoldItalic",
109 { "-adobe-times-bold-i-normal-*-%d-*-*-*-*-*-*-*",
110 NULL
113 { "AvantGarde-Book",
114 "AvantGarde-Book",
115 { "-adobe-avantgarde-book-r-normal-*-%d-*-*-*-*-*-*-*",
116 "-schumacher-clean-medium-r-normal-*-%d-*-*-*-*-*-*-*"
119 { "AvantGarde-BookOblique",
120 "AvantGarde-BookOblique",
121 { "-adobe-avantgarde-book-o-normal-*-%d-*-*-*-*-*-*-*",
122 "-schumacher-clean-medium-i-normal-*-%d-*-*-*-*-*-*-*"
125 { "AvantGarde-Demi",
126 "AvantGarde-Demi",
127 { "-adobe-avantgarde-demibold-r-normal-*-%d-*-*-*-*-*-*-*",
128 "-schumacher-clean-bold-r-normal-*-%d-*-*-*-*-*-*-*"
131 { "AvantGarde-DemiOblique",
132 "AvantGarde-DemiOblique",
133 { "-adobe-avantgarde-demibold-o-normal-*-%d-*-*-*-*-*-*-*",
134 "-schumacher-clean-bold-i-normal-*-%d-*-*-*-*-*-*-*"
137 { "Bookman-Light",
138 "Bookman-Light",
139 { "-adobe-bookman-light-r-normal-*-%d-*-*-*-*-*-*-*",
140 "-adobe-times-medium-r-normal-*-%d-*-*-*-*-*-*-*"
143 { "Bookman-LightItalic",
144 "Bookman-LightItalic",
145 { "-adobe-bookman-light-i-normal-*-%d-*-*-*-*-*-*-*",
146 "-adobe-times-medium-i-normal-*-%d-*-*-*-*-*-*-*"
149 { "Bookman-Demi",
150 "Bookman-Demi",
151 { "-adobe-bookman-demibold-r-normal-*-%d-*-*-*-*-*-*-*",
152 "-adobe-times-bold-r-normal-*-%d-*-*-*-*-*-*-*"
155 { "Bookman-DemiItalic",
156 "Bookman-DemiItalic",
157 { "-adobe-bookman-demibold-i-normal-*-%d-*-*-*-*-*-*-*",
158 "-adobe-times-bold-i-normal-*-%d-*-*-*-*-*-*-*"
161 #ifndef G_OS_WIN32
162 { "Courier",
163 "Courier",
164 { "-adobe-courier-medium-r-normal-*-%d-*-*-*-*-*-*-*",
165 NULL
168 { "Courier-Oblique",
169 "Courier-Oblique",
170 { "-adobe-courier-medium-o-normal-*-%d-*-*-*-*-*-*-*",
171 NULL
174 { "Courier-Bold",
175 "Courier-Bold",
176 { "-adobe-courier-bold-r-normal-*-%d-*-*-*-*-*-*-*",
177 NULL
180 { "Courier-BoldOblique",
181 "Courier-BoldOblique",
182 { "-adobe-courier-bold-o-normal-*-%d-*-*-*-*-*-*-*",
183 NULL
186 #else /* G_OS_WIN32 */
187 /* HB: force usage of true type font "Courier New", using the bitmap
188 * version causes scaling problems mainly with uml. FIXME: there
189 * must be a better way to do this ?
191 { "Courier",
192 "Courier",
193 { "-adobe-courier new-medium-r-normal-*-%d-*-*-*-*-*-*-*",
194 NULL
197 { "Courier-Oblique",
198 "Courier-Oblique",
199 { "-adobe-courier new-medium-o-normal-*-%d-*-*-*-*-*-*-*",
200 NULL
203 { "Courier-Bold",
204 "Courier-Bold",
205 { "-adobe-courier new-bold-r-normal-*-%d-*-*-*-*-*-*-*",
206 NULL
209 { "Courier-BoldOblique",
210 "Courier-BoldOblique",
211 { "-adobe-courier new-bold-o-normal-*-%d-*-*-*-*-*-*-*",
212 NULL
215 #endif
216 { "Helvetica",
217 "Helvetica",
218 { "-adobe-helvetica-medium-r-normal-*-%d-*-*-*-*-*-*-*",
219 NULL
222 { "Helvetica-Oblique",
223 "Helvetica-Oblique",
224 { "-adobe-helvetica-medium-o-normal-*-%d-*-*-*-*-*-*-*",
225 NULL
228 { "Helvetica-Bold",
229 "Helvetica-Bold",
230 { "-adobe-helvetica-bold-r-normal-*-%d-*-*-*-*-*-*-*",
231 NULL
234 { "Helvetica-BoldOblique",
235 "Helvetica-BoldOblique",
236 { "-adobe-helvetica-bold-o-normal-*-%d-*-*-*-*-*-*-*",
237 NULL
240 { "Helvetica-Narrow",
241 "Helvetica-Narrow",
242 { "-adobe-helvetica-medium-r-normal-*-%d-*-*-*-*-*-*-*",
243 NULL
246 { "Helvetica-Narrow-Oblique",
247 "Helvetica-Narrow-Oblique",
248 { "-adobe-helvetica-medium-o-normal-*-%d-*-*-*-*-*-*-*",
249 NULL
252 { "Helvetica-Narrow-Bold",
253 "Helvetica-Narrow-Bold",
254 { "-adobe-helvetica-bold-r-normal-*-%d-*-*-*-*-*-*-*",
255 NULL
258 { "Helvetica-Narrow-BoldOblique",
259 "Helvetica-Narrow-BoldOblique",
260 { "-adobe-helvetica-bold-o-normal-*-%d-*-*-*-*-*-*-*",
261 NULL
264 { "NewCenturySchoolbook-Roman",
265 "NewCenturySchlbk-Roman",
266 { "-adobe-new century schoolbook-medium-r-normal-*-%d-*-*-*-*-*-*-*",
267 NULL
270 { "NewCenturySchoolbook-Italic",
271 "NewCenturySchlbk-Italic",
272 { "-adobe-new century schoolbook-medium-i-normal-*-%d-*-*-*-*-*-*-*",
273 NULL
276 { "NewCenturySchoolbook-Bold",
277 "NewCenturySchlbk-Bold",
278 { "-adobe-new century schoolbook-bold-r-normal-*-%d-*-*-*-*-*-*-*",
279 NULL
282 { "NewCenturySchoolbook-BoldItalic",
283 "NewCenturySchlbk-BoldItalic",
284 { "-adobe-new century schoolbook-bold-i-normal-*-%d-*-*-*-*-*-*-*",
285 NULL
288 { "Palatino-Roman",
289 "Palatino-Roman",
290 { "-adobe-palatino-medium-r-normal-*-%d-*-*-*-*-*-*-*",
291 "-*-lucidabright-medium-r-normal-*-%d-*-*-*-*-*-*-*"
294 { "Palatino-Italic",
295 "Palatino-Italic",
296 { "-adobe-palatino-medium-i-normal-*-%d-*-*-*-*-*-*-*",
297 "-*-lucidabright-medium-i-normal-*-%d-*-*-*-*-*-*-*"
300 { "Palatino-Bold",
301 "Palatino-Bold",
302 { "-adobe-palatino-bold-r-normal-*-%d-*-*-*-*-*-*-*",
303 "-*-lucidabright-demibold-r-normal-*-%d-*-*-*-*-*-*-*"
306 { "Palatino-BoldItalic",
307 "Palatino-BoldItalic",
308 { "-adobe-palatino-bold-i-normal-*-%d-*-*-*-*-*-*-*",
309 "-*-lucidabright-demibold-i-normal-*-%d-*-*-*-*-*-*-*"
312 { "Symbol",
313 "Symbol",
315 "-adobe-symbol-medium-r-normal-*-%d-*-*-*-*-*-*-*",
316 "-*-symbol-medium-r-normal-*-%d-*-*-*-*-*-*-*"
319 { "ZapfChancery-MediumItalic",
320 "ZapfChancery-MediumItalic",
321 { "-adobe-zapf chancery-medium-i-normal-*-%d-*-*-*-*-*-*-*",
322 "-*-itc zapf chancery-medium-i-normal-*-%d-*-*-*-*-*-*-*"
325 { "ZapfDingbats",
326 "ZapfDingbats",
327 { "-adobe-zapf dingbats-medium-r-normal-*-%d-*-*-*-*-*-*-*",
328 "-*-itc zapf dingbats-*-*-*-*-%d-*-*-*-*-*-*-*"
333 #define NUM_FONTS (sizeof(font_data)/sizeof(FontData))
335 GList *fonts = NULL;
336 GList *font_names;
337 GHashTable *fonts_hash = NULL;
339 char *last_resort_fonts[] = {
340 "-adobe-courier-medium-r-normal-*-%d-*-*-*-*-*-*-*",
341 "fixed" /* Must be last. This is guaranteed to exist on an X11 system. */
343 #define NUM_LAST_RESORT_FONTS (sizeof(last_resort_fonts)/sizeof(char *))
345 static void suck_font_free (SuckFont *suckfont);
346 static SuckFont *suck_font (GdkFont *font);
348 static void
349 init_x11_font(FontPrivate *font)
351 int i;
352 GdkFont *gdk_font = NULL;
353 int bufsize;
354 char *buffer;
355 char *x11_font;
356 real height;
358 for (i=0;i<NUM_X11_FONTS;i++) {
359 x11_font = font->fontname_x11_vec[i];
360 if (x11_font == NULL)
361 break;
362 bufsize = strlen(x11_font)+6; /* Should be enought*/
363 buffer = (char *)g_malloc(bufsize);
364 g_snprintf(buffer, bufsize, x11_font, 100);
366 gdk_font = gdk_font_load(buffer);
367 if (gdk_font!=NULL) {
368 font->fontname_x11 = x11_font;
370 g_free(buffer);
372 if (font->fontname_x11!=NULL)
373 break;
376 if (font->fontname_x11 == NULL) {
377 for (i=0;i<NUM_LAST_RESORT_FONTS;i++) {
378 x11_font = last_resort_fonts[i];
379 bufsize = strlen(x11_font)+6; /* Should be enought*/
380 buffer = (char *)g_malloc(bufsize);
381 g_snprintf(buffer, bufsize, x11_font, 100);
383 gdk_font = gdk_font_load(buffer);
384 g_free(buffer);
385 if (gdk_font!=NULL) {
386 message_warning(_("Warning no X Font for %s found, \nusing %s instead.\n"), font->public.name, x11_font);
387 font->fontname_x11 = x11_font;
388 break;
393 height = (real)gdk_font->ascent + gdk_font->descent;
394 font->ascent_ratio = gdk_font->ascent/height;
395 font->descent_ratio = gdk_font->descent/height;
397 gdk_font_unref(gdk_font);
400 void
401 font_init(void)
403 int i,j;
404 FontPrivate *font;
406 fonts_hash = g_hash_table_new((GHashFunc)g_str_hash,
407 (GCompareFunc)g_str_equal);
409 for (i=0;i<NUM_FONTS;i++) {
410 font = g_new(FontPrivate, 1);
411 font->public.name = font_data[i].fontname;
412 font->fontname_ps = font_data[i].fontname_ps;
414 font->fontname_x11 = NULL;
415 font->fontname_x11_vec = font_data[i].fontname_x11;
417 for (j=0;j<FONTCACHE_SIZE;j++) {
418 font->cache[j] = NULL;
421 fonts = g_list_append(fonts, font);
422 font_names = g_list_append(font_names, font->public.name);
423 g_hash_table_insert(fonts_hash, font->public.name, font);
428 Font *
429 font_getfont(const char *name)
431 FontPrivate *font;
433 font = (FontPrivate *)g_hash_table_lookup(fonts_hash, (char *)name);
435 if (font == NULL) {
436 font = g_hash_table_lookup(fonts_hash, "Courier");
437 if (font == NULL) {
438 message_error("Error, couldn't locate font. Shouldn't happend.\n");
439 } else {
440 message_notice(_("Font %s not found, using Courier instead.\n"), name);
444 if (font->fontname_x11 == NULL)
445 init_x11_font(font);
447 return (Font *)font;
450 static FontCacheItem *
451 font_get_cache(FontPrivate *font, int height)
453 int index;
455 if (height<=0)
456 height = 1;
458 index = height % FONTCACHE_SIZE;
460 if (font->cache[index]==NULL) {
461 font->cache[index] = g_new(FontCacheItem, 1);
462 font->cache[index]->height = height;
463 font->cache[index]->gdk_font = NULL;
464 font->cache[index]->suck_font = NULL;
465 } else if (font->cache[index]->height != height) {
466 gdk_font_unref(font->cache[index]->gdk_font);
467 if (font->cache[index]->suck_font)
468 suck_font_free(font->cache[index]->suck_font);
469 font->cache[index]->height = height;
470 font->cache[index]->gdk_font = NULL;
471 font->cache[index]->suck_font = NULL;
473 return font->cache[index];
476 static GdkFont *
477 font_get_gdkfont_helper(FontPrivate *font, int height)
479 int bufsize;
480 char *buffer;
481 GdkFont *gdk_font;
483 bufsize = strlen(font->fontname_x11)+6; /* Should be enought*/
484 buffer = (char *)malloc(bufsize);
485 g_snprintf(buffer, bufsize, font->fontname_x11, height);
486 gdk_font = gdk_font_load(buffer);
487 free(buffer);
489 return gdk_font;
493 GdkFont *
494 font_get_gdkfont(Font *font, int height)
496 FontCacheItem *cache_item;
497 FontPrivate *fontprivate;
499 fontprivate = (FontPrivate *)font;
501 cache_item = font_get_cache(fontprivate, height);
503 if (cache_item->gdk_font)
504 return cache_item->gdk_font;
506 /* Not in cache: */
508 cache_item->gdk_font = font_get_gdkfont_helper(fontprivate, cache_item->height);
510 return cache_item->gdk_font;
513 SuckFont *
514 font_get_suckfont(Font *font, int height)
516 FontCacheItem *cache_item;
517 FontPrivate *fontprivate;
519 fontprivate = (FontPrivate *)font;
521 cache_item = font_get_cache(fontprivate, height);
523 if (!cache_item->gdk_font) {
524 /* gdk_font not in cache: */
525 cache_item->gdk_font = font_get_gdkfont_helper(fontprivate, cache_item->height);
528 if (!cache_item->suck_font) {
529 /* Not in cache: */
530 cache_item->suck_font = suck_font(cache_item->gdk_font);
533 return cache_item->suck_font;
536 char *
537 font_get_psfontname(Font *font)
539 FontPrivate *fontprivate;
541 fontprivate = (FontPrivate *)font;
543 return fontprivate->fontname_ps;
546 real
547 font_string_width(const char *string, Font *font, real height)
549 int iwidth, iheight;
550 double width_height;
551 GdkFont *gdk_font;
553 /* Note: This is an ugly hack. It tries to overestimate the width with
554 some magic stuff. No guarantees. */
555 gdk_font = font_get_gdkfont(font, 100);
556 iwidth = gdk_string_width(gdk_font, string);
557 iheight = gdk_string_height(gdk_font, string);
559 if ((iwidth==0) || (iheight==0))
560 return 0.0;
562 width_height = ((real)iwidth)/((real)iheight);
563 width_height *= 1.01;
564 return width_height*height*(iheight/100.0) + 0.2;
567 real
568 font_ascent(Font *font, real height)
570 FontPrivate *fontprivate;
571 fontprivate = (FontPrivate *)font;
572 return height*fontprivate->ascent_ratio;
575 real
576 font_descent(Font *font, real height)
578 FontPrivate *fontprivate;
579 fontprivate = (FontPrivate *)font;
580 return height*fontprivate->descent_ratio;
583 /* Routines for sucking fonts from the X server */
585 static SuckFont *
586 suck_font (GdkFont *font)
588 SuckFont *suckfont;
589 int i;
590 int x, y;
591 char text[1];
592 int lbearing, rbearing, ch_width, ascent, descent;
593 GdkPixmap *pixmap;
594 GdkColor black, white;
595 GdkImage *image;
596 GdkGC *gc;
597 guchar *line;
598 int width, height;
599 int black_pixel, pixel;
601 if (!font)
602 return NULL;
604 suckfont = g_new (SuckFont, 1);
606 height = font->ascent + font->descent;
607 x = 0;
608 for (i = 0; i < 256; i++) {
609 text[0] = i;
610 gdk_text_extents (font, text, 1,
611 &lbearing, &rbearing, &ch_width, &ascent, &descent);
612 suckfont->chars[i].left_sb = lbearing;
613 suckfont->chars[i].right_sb = ch_width - rbearing;
614 suckfont->chars[i].width = rbearing - lbearing;
615 suckfont->chars[i].ascent = ascent;
616 suckfont->chars[i].descent = descent;
617 suckfont->chars[i].bitmap_offset = x;
618 x += (ch_width + 31) & -32;
621 width = x;
623 suckfont->bitmap_width = width;
624 suckfont->bitmap_height = height+1;
625 suckfont->ascent = font->ascent+1;
627 pixmap = gdk_pixmap_new (NULL, suckfont->bitmap_width,
628 suckfont->bitmap_height, 1);
629 gc = gdk_gc_new (pixmap);
630 gdk_gc_set_font (gc, font);
632 /* this is a black and white pixmap: */
633 black.pixel = 0;
634 white.pixel = 1;
635 black_pixel = black.pixel;
636 gdk_gc_set_foreground (gc, &white);
637 gdk_draw_rectangle (pixmap, gc, 1, 0, 0, suckfont->bitmap_width, suckfont->bitmap_height);
639 gdk_gc_set_foreground (gc, &black);
640 for (i = 0; i < 256; i++) {
641 text[0] = i;
642 gdk_draw_text (pixmap, font, gc,
643 suckfont->chars[i].bitmap_offset - suckfont->chars[i].left_sb,
644 font->ascent+1,
645 text, 1);
648 /* The handling of the image leaves me with distinct unease. But this
649 * is more or less copied out of gimp/app/text_tool.c, so it _ought_ to
650 * work. -RLL
653 image = gdk_image_get (pixmap, 0, 0, suckfont->bitmap_width, suckfont->bitmap_height);
654 suckfont->bitmap = g_malloc0 ((width >> 3) * suckfont->bitmap_height);
656 line = suckfont->bitmap;
657 for (y = 0; y < suckfont->bitmap_height; y++) {
658 for (x = 0; x < suckfont->bitmap_width; x++) {
659 pixel = gdk_image_get_pixel (image, x, y);
660 if (pixel == black_pixel)
661 line[x >> 3] |= 128 >> (x & 7);
663 line += width >> 3;
666 gdk_image_destroy (image);
668 /* free the pixmap */
669 gdk_pixmap_unref (pixmap);
671 /* free the gc */
672 gdk_gc_destroy (gc);
674 return suckfont;
677 static void
678 suck_font_free (SuckFont *suckfont)
680 g_free (suckfont->bitmap);
681 g_free (suckfont);