* installer/win32/dia.nsi: don't uninstall old version; overwrite
[dia.git] / lib / font.c
blob8111dcc9ec3e2a205766bb9d7e91f7860ddf93c0
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
3 * Font code completely reworked for the Pango conversion
4 * Copyright (C) 2002 Cyrille Chepelov
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h> /* strlen */
28 #include <time.h>
30 #include <pango/pango.h>
31 #ifdef HAVE_FREETYPE
32 #include <pango/pangoft2.h>
33 #endif
34 #include <gdk/gdk.h>
35 #include <gtk/gtk.h> /* just for gtk_get_default_language() */
37 #include "font.h"
38 #include "message.h"
39 #include "intl.h"
41 /** Define this if you want to use the layout cache to speed up output.
42 * Appears to be buggy, see bug #307320. */
43 #undef LAYOUT_CACHE
45 static PangoContext* pango_context = NULL;
47 struct _DiaFont
49 GObject parent_instance;
51 PangoFontDescription* pfd;
52 /* mutable */ char* legacy_name;
56 /* This is the global factor that says what zoom factor is 100%. It's
57 * normally 20.0 (and likely to stay that way). It is defined by how many
58 * pixels one cm is represented as.
60 static real global_zoom_factor = 20.0;
62 #if 0
63 /* For debugging: Sort families */
64 static int
65 cmp_families (const void *a, const void *b)
67 const char *a_name = pango_font_family_get_name (*(PangoFontFamily **)a);
68 const char *b_name = pango_font_family_get_name (*(PangoFontFamily **)b);
70 return g_utf8_collate (a_name, b_name);
73 /* For debugging: List all font families */
74 static void
75 list_families()
77 PangoFontFamily **families;
78 int nfamilies;
79 int i;
81 pango_context_list_families(dia_font_get_context(), &families, &nfamilies);
82 qsort (families, nfamilies, sizeof (PangoFontFamily *), cmp_families);
83 for (i = 0; i < nfamilies; i++) {
84 puts(pango_font_family_get_name(families[i]));
86 g_free(families);
88 #endif
90 static void
91 dia_font_check_for_font(int font) {
92 DiaFont *check;
93 PangoFont *loaded;
95 check = dia_font_new_from_style(font, 1.0);
96 loaded = pango_context_load_font(dia_font_get_context(),
97 check->pfd);
98 if (loaded == NULL) {
99 message_error(_("Can't load font %s.\n"), dia_font_get_family(check));
103 void
104 dia_font_init(PangoContext* pcontext)
106 pango_context = pcontext;
107 /* We must have these three fonts! */
108 dia_font_check_for_font(DIA_FONT_SANS);
109 dia_font_check_for_font(DIA_FONT_SERIF);
110 dia_font_check_for_font(DIA_FONT_MONOSPACE);
113 /* We might not need these anymore, when using FT2/Win32 fonts only */
114 static GList *pango_contexts;
116 void
117 dia_font_push_context(PangoContext *pcontext) {
118 pango_contexts = g_list_prepend(pango_contexts, pango_context);
119 pango_context = pcontext;
120 pango_context_set_language (pango_context, gtk_get_default_language ());
121 g_object_ref(pcontext);
124 void
125 dia_font_pop_context() {
126 g_object_unref(pango_context);
127 pango_context = (PangoContext*)pango_contexts->data;
128 pango_context_set_language (pango_context, gtk_get_default_language ());
129 pango_contexts = g_list_next(pango_contexts);
132 PangoContext *
133 dia_font_get_context() {
134 if (pango_context == NULL) {
135 #ifdef HAVE_FREETYPE
136 /* This is suggested by new Pango (1.2.4+), but doesn't get us the
137 * right resolution:(
138 dia_font_push_context(pango_ft2_font_map_create_context(pango_ft2_font_map_new()));
140 dia_font_push_context(pango_ft2_get_context(75,75));
141 #else
142 dia_font_push_context(gdk_pango_context_get());
143 #endif
146 return pango_context;
149 /* dia centimetres to pango device units */
150 static gint
151 dcm_to_pdu(real dcm) { return dcm * global_zoom_factor * PANGO_SCALE; }
152 /* pango device units to dia centimetres */
153 static real
154 pdu_to_dcm(gint pdu) { return (real)pdu / (global_zoom_factor * PANGO_SCALE); }
156 static void dia_font_class_init(DiaFontClass* class);
157 static void dia_font_finalize(GObject* object);
158 static void dia_font_init_instance(DiaFont*);
160 GType
161 dia_font_get_type (void)
163 static GType object_type = 0;
165 if (!object_type) {
166 static const GTypeInfo object_info =
168 sizeof (DiaFontClass),
169 (GBaseInitFunc) NULL,
170 (GBaseFinalizeFunc) NULL,
171 (GClassInitFunc) dia_font_class_init, /* class_init */
172 NULL, /* class_finalize */
173 NULL, /* class_data */
174 sizeof (DiaFont),
175 0, /* n_preallocs */
176 (GInstanceInitFunc)dia_font_init_instance
178 object_type = g_type_register_static (G_TYPE_OBJECT,
179 "DiaFont",
180 &object_info, 0);
182 return object_type;
184 static gpointer parent_class;
186 static void
187 dia_font_class_init(DiaFontClass* klass)
189 GObjectClass* object_class = G_OBJECT_CLASS(klass);
190 parent_class = g_type_class_peek_parent(klass);
191 object_class->finalize = dia_font_finalize;
194 static void
195 dia_font_init_instance(DiaFont* font)
197 /*GObject *gobject = G_OBJECT(font); */
200 DiaFont*
201 dia_font_new(const char *family, DiaFontStyle style, real height)
203 DiaFont* retval = dia_font_new_from_style(style, height);
205 pango_font_description_set_family(retval->pfd,family);
207 pango_context_load_font(dia_font_get_context(), retval->pfd);
209 return retval;
212 static void
213 dia_pfd_set_family(PangoFontDescription* pfd, DiaFontFamily fam) {
214 switch (fam) {
215 case DIA_FONT_SANS :
216 pango_font_description_set_family(pfd, "sans");
217 break;
218 case DIA_FONT_SERIF :
219 pango_font_description_set_family(pfd, "serif");
220 break;
221 case DIA_FONT_MONOSPACE :
222 pango_font_description_set_family(pfd, "monospace");
223 break;
224 default :
225 /* Pango does allow fonts without a name */
230 static void
231 dia_pfd_set_weight(PangoFontDescription* pfd, DiaFontWeight fw) {
232 switch (fw) {
233 case DIA_FONT_ULTRALIGHT :
234 pango_font_description_set_weight(pfd, PANGO_WEIGHT_ULTRALIGHT);
235 break;
236 case DIA_FONT_LIGHT :
237 pango_font_description_set_weight(pfd, PANGO_WEIGHT_LIGHT);
238 break;
239 case DIA_FONT_WEIGHT_NORMAL :
240 pango_font_description_set_weight(pfd, PANGO_WEIGHT_NORMAL);
241 break;
242 case DIA_FONT_MEDIUM : /* Pango doesn't have this, but
243 'intermediate values are possible' */
244 pango_font_description_set_weight(pfd, 500);
245 break;
246 case DIA_FONT_DEMIBOLD : /* Pango doesn't have this, ... */
247 pango_font_description_set_weight(pfd, 600);
248 break;
249 case DIA_FONT_BOLD :
250 pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
251 break;
252 case DIA_FONT_ULTRABOLD :
253 pango_font_description_set_weight(pfd, PANGO_WEIGHT_ULTRABOLD);
254 break;
255 case DIA_FONT_HEAVY :
256 pango_font_description_set_weight(pfd, PANGO_WEIGHT_HEAVY);
257 break;
258 default :
259 g_assert_not_reached();
263 static void
264 dia_pfd_set_slant(PangoFontDescription* pfd, DiaFontSlant fo) {
265 switch (fo) {
266 case DIA_FONT_NORMAL :
267 pango_font_description_set_style(pfd,PANGO_STYLE_NORMAL);
268 break;
269 case DIA_FONT_OBLIQUE :
270 pango_font_description_set_style(pfd,PANGO_STYLE_OBLIQUE);
271 break;
272 case DIA_FONT_ITALIC :
273 pango_font_description_set_style(pfd,PANGO_STYLE_ITALIC);
274 break;
275 default :
276 g_assert_not_reached();
280 static void dia_pfd_set_size(PangoFontDescription* pfd, real height)
281 { /* inline candidate... */
282 pango_font_description_set_size(pfd, dcm_to_pdu(height) );
286 DiaFont*
287 dia_font_new_from_style(DiaFontStyle style, real height)
289 DiaFont* retval;
290 /* in the future we could establish Dia's own default font
291 * matching to be as (font-)system independent as possible.
292 * For now fall back to Pangos configuration --hb
294 PangoFontDescription* pfd = pango_font_description_new();
295 dia_pfd_set_family(pfd,DIA_FONT_STYLE_GET_FAMILY(style));
296 dia_pfd_set_weight(pfd,DIA_FONT_STYLE_GET_WEIGHT(style));
297 dia_pfd_set_slant(pfd,DIA_FONT_STYLE_GET_SLANT(style));
298 dia_pfd_set_size(pfd,height);
300 retval = DIA_FONT(g_object_new(DIA_TYPE_FONT, NULL));
302 retval->pfd = pfd;
303 retval->legacy_name = NULL;
304 return retval;
307 DiaFont* dia_font_copy(const DiaFont* font)
309 if (!font) return NULL;
310 return dia_font_new(dia_font_get_family(font),
311 dia_font_get_style(font),
312 dia_font_get_height(font));
315 void
316 dia_font_finalize(GObject* object)
318 DiaFont* font;
319 font = DIA_FONT(object);
320 if (font->pfd) pango_font_description_free(font->pfd);
321 G_OBJECT_CLASS(parent_class)->finalize(object);
324 DiaFont* dia_font_ref(DiaFont* font)
326 g_object_ref(G_OBJECT(font));
327 return font;
330 void dia_font_unref(DiaFont* font)
332 g_object_unref(G_OBJECT(font));
335 DiaFontStyle
336 dia_font_get_style(const DiaFont* font)
338 guint style;
340 static int weight_map[] = {
341 DIA_FONT_ULTRALIGHT, DIA_FONT_LIGHT,
342 DIA_FONT_WEIGHT_NORMAL, /* intentionaly ==0 */
343 DIA_FONT_MEDIUM, DIA_FONT_DEMIBOLD, /* not yet in Pango */
344 DIA_FONT_BOLD, DIA_FONT_ULTRABOLD, DIA_FONT_HEAVY
347 PangoStyle pango_style = pango_font_description_get_style(font->pfd);
348 PangoWeight pango_weight = pango_font_description_get_weight(font->pfd);
350 g_assert(PANGO_WEIGHT_ULTRALIGHT <= pango_weight && pango_weight <= PANGO_WEIGHT_HEAVY);
351 g_assert(PANGO_WEIGHT_ULTRALIGHT == 200);
352 g_assert(PANGO_WEIGHT_NORMAL == 400);
353 g_assert(PANGO_WEIGHT_BOLD == 700);
355 style = weight_map[(pango_weight - PANGO_WEIGHT_ULTRALIGHT) / 100];
356 style |= (pango_style << 2);
358 return style;
361 G_CONST_RETURN char*
362 dia_font_get_family(const DiaFont* font)
364 return pango_font_description_get_family(font->pfd);
367 G_CONST_RETURN PangoFontDescription *
368 dia_font_get_description(const DiaFont* font)
370 return font->pfd;
373 real
374 dia_font_get_height(const DiaFont* font)
376 return pdu_to_dcm(pango_font_description_get_size(font->pfd));
379 void
380 dia_font_set_height(DiaFont* font, real height)
382 pango_font_description_set_size(font->pfd, dcm_to_pdu(height));
386 G_CONST_RETURN char*
387 dia_font_get_psfontname(const DiaFont *font)
389 /* FIXME: this will very likely not work ! */
390 return dia_font_get_legacy_name(font);
393 void dia_font_set_any_family(DiaFont* font, const char* family)
395 g_assert(font != NULL);
396 pango_font_description_set_family(font->pfd, family);
397 if (font->legacy_name) {
398 g_free(font->legacy_name);
399 font->legacy_name = NULL;
403 void dia_font_set_family(DiaFont* font, DiaFontFamily family)
405 g_assert(font != NULL);
406 dia_pfd_set_family(font->pfd,family);
407 if (font->legacy_name) {
408 g_free(font->legacy_name);
409 font->legacy_name = NULL;
413 void dia_font_set_weight(DiaFont* font, DiaFontWeight weight)
415 g_assert(font != NULL);
416 dia_pfd_set_weight(font->pfd,weight);
419 void dia_font_set_slant(DiaFont* font, DiaFontSlant slant)
421 g_assert(font != NULL);
422 dia_pfd_set_slant(font->pfd,slant);
426 struct weight_name { DiaFontWeight fw; const char *name; };
427 static const struct weight_name weight_names[] = {
428 {DIA_FONT_ULTRALIGHT, "200"},
429 {DIA_FONT_LIGHT,"300"},
430 {DIA_FONT_WEIGHT_NORMAL,"normal"},
431 {DIA_FONT_WEIGHT_NORMAL,"400"},
432 {DIA_FONT_MEDIUM, "500"},
433 {DIA_FONT_DEMIBOLD, "600"},
434 {DIA_FONT_BOLD, "700"},
435 {DIA_FONT_ULTRABOLD, "800"},
436 {DIA_FONT_HEAVY, "900"},
437 {0,NULL}};
439 G_CONST_RETURN char *dia_font_get_weight_string(const DiaFont* font)
441 const struct weight_name* p;
442 DiaFontWeight fw = DIA_FONT_STYLE_GET_WEIGHT(dia_font_get_style(font));
444 for (p = weight_names; p->name != NULL; ++p) {
445 if (p->fw == fw) return p->name;
447 return "normal";
450 void dia_font_set_weight_from_string(DiaFont* font, const char* weight) {
451 DiaFontWeight fw = DIA_FONT_WEIGHT_NORMAL;
452 const struct weight_name* p;
454 for (p = weight_names; p->name != NULL; ++p) {
455 if (0 == strncmp(weight,p->name,8)) {
456 fw = p->fw;
457 break;
461 dia_font_set_weight(font,fw);
465 struct slant_name { DiaFontSlant fo; const char *name; };
466 static const struct slant_name slant_names[] = {
467 { DIA_FONT_NORMAL, "normal"},
468 { DIA_FONT_OBLIQUE, "oblique"},
469 { DIA_FONT_ITALIC, "italic"},
470 { 0, NULL} };
472 G_CONST_RETURN char *
473 dia_font_get_slant_string(const DiaFont* font)
475 const struct slant_name* p;
476 DiaFontSlant fo =
477 DIA_FONT_STYLE_GET_SLANT(dia_font_get_style(font));
479 for (p = slant_names; p->name != NULL; ++p) {
480 if (p->fo == fo) return p->name;
482 return "normal";
485 void dia_font_set_slant_from_string(DiaFont* font, const char* obli) {
486 DiaFontSlant fo = DIA_FONT_NORMAL;
487 const struct slant_name* p;
489 DiaFontStyle old_style;
490 DiaFontSlant old_fo;
491 old_style = dia_font_get_style(font);
492 old_fo = DIA_FONT_STYLE_GET_SLANT(old_style);
494 for (p = slant_names; p->name != NULL; ++p) {
495 if (0 == strncmp(obli,p->name,8)) {
496 fo = p->fo;
497 break;
500 dia_font_set_slant(font,fo);
504 /* ************************************************************************ */
505 /* Non-scaled versions of the utility routines */
506 /* ************************************************************************ */
508 real
509 dia_font_string_width(const char* string, DiaFont *font, real height)
511 return dia_font_scaled_string_width(string,font,height,global_zoom_factor);
514 real
515 dia_font_ascent(const char* string, DiaFont* font, real height)
517 return dia_font_scaled_ascent(string,font,height,global_zoom_factor);
520 real dia_font_descent(const char* string, DiaFont* font, real height)
522 return dia_font_scaled_descent(string,font,height,global_zoom_factor);
525 typedef struct {
526 gchar *string;
527 DiaFont *font;
528 PangoLayout *layout;
529 int usecount;
530 } LayoutCacheItem;
532 static GHashTable *layoutcache;
534 static gboolean
535 layout_cache_equals(gconstpointer e1, gconstpointer e2)
537 LayoutCacheItem *i1 = (LayoutCacheItem*)e1,
538 *i2 = (LayoutCacheItem*)e2;
540 /* don't try strcmp() with null pointers ... */
541 if (!i1->string || i2->string)
542 return FALSE;
544 return strcmp(i1->string, i2->string) == 0 &&
545 pango_font_description_equal(i1->font->pfd, i2->font->pfd);
548 static guint
549 layout_cache_hash(gconstpointer el)
551 LayoutCacheItem *item = (LayoutCacheItem*)el;
553 return g_str_hash(item->string) ^
554 pango_font_description_hash(item->font->pfd);
557 static long layout_cache_last_use;
559 static gboolean
560 layout_cache_cleanup_entry(gpointer key, gpointer value, gpointer data)
562 LayoutCacheItem *item = (LayoutCacheItem*)value;
563 /** Remove unused items */
564 if (item->usecount == 0) return TRUE;
565 item->usecount = 0;
566 return FALSE;
569 /** The actual hash cleanup, called when idle. */
570 static gboolean
571 layout_cache_cleanup_idle(gpointer data)
573 GHashTable *table = (GHashTable*)(data);
575 g_hash_table_foreach_remove(table, layout_cache_cleanup_entry, NULL);
576 return FALSE;
579 /** Every ten minutes, clean up those strings that haven't seen use since
580 * last cleanup.
582 static gboolean
583 layout_cache_cleanup(gpointer data)
585 /* Only cleanup if there has been font activity since last cleanup */
586 if (time(0) - layout_cache_last_use < 10) {
587 /* Don't go directly to cleanup, wait till there's a pause. */
588 g_idle_add(layout_cache_cleanup_idle, data);
590 /* Keep doing this */
591 return TRUE;
594 static void
595 layout_cache_free_key(gpointer data)
597 LayoutCacheItem *item = (LayoutCacheItem*)data;
599 if (item->string != NULL) {
600 g_free(item->string);
601 item->string = NULL;
604 if (item->font != NULL) {
605 dia_font_unref(item->font);
606 item->font = NULL;
609 if (item->layout != NULL) {
610 g_object_unref(item->layout);
611 item->layout = NULL;
613 g_free(item);
617 PangoLayout*
618 dia_font_build_layout(const char* string, DiaFont* font, real height)
620 PangoLayout* layout;
621 PangoAttrList* list;
622 PangoAttribute* attr;
623 guint length;
624 gchar *desc = NULL;
626 #ifdef LAYOUT_CACHE
627 LayoutCacheItem *cached, *item;
629 layout_cache_last_use = time(0);
630 if (layoutcache == NULL) {
631 /** Note that key and value are the same -- it's a HashSet */
632 layoutcache = g_hash_table_new_full(layout_cache_hash,
633 layout_cache_equals,
634 layout_cache_free_key,
635 NULL);
636 /** Check for cache cleanup every 10 seconds. */
637 /** This frequent a check is really a hack while we figure out the
638 * exact problems with reffing the fonts.
639 * Note to self: The equals function should compare pfd's, but
640 * then DiaFonts are freed too early.
642 g_timeout_add(10*1000, layout_cache_cleanup, (gpointer)layoutcache);
644 #endif
646 height *= 0.7;
647 dia_font_set_height(font, height);
649 #ifdef LAYOUT_CACHE
650 item = g_new0(LayoutCacheItem,1);
651 item->string = g_strdup(string);
652 item->font = font;
654 /* If it's in the cache, use that instead. */
655 cached = g_hash_table_lookup(layoutcache, item);
656 if (cached != NULL) {
657 g_object_ref(cached->layout);
658 g_free(item->string);
659 g_free(item);
660 cached->usecount ++;
661 return cached->layout;
664 item->font = dia_font_copy(font);
665 dia_font_ref(item->font);
666 #endif
668 /* This could should account for DPI, but it doesn't do right. Grrr...
670 GdkScreen *screen = gdk_screen_get_default();
671 real dpi = gdk_screen_get_width(screen)/
672 (gdk_screen_get_width_mm(screen)/25.4);
673 printf("height = %f, dpi = %f, new height = %f\n",
674 height, dpi, height * (dpi/100));
675 height *= dpi/100;
679 layout = pango_layout_new(dia_font_get_context());
681 length = string ? strlen(string) : 0;
682 pango_layout_set_text(layout, string, length);
684 list = pango_attr_list_new();
685 desc = g_utf8_strdown(pango_font_description_get_family(font->pfd), -1);
686 pango_font_description_set_family(font->pfd, desc);
687 g_free(desc);
688 attr = pango_attr_font_desc_new(font->pfd);
689 attr->start_index = 0;
690 attr->end_index = length;
691 pango_attr_list_insert(list,attr); /* eats attr */
693 pango_layout_set_attributes(layout,list);
694 pango_attr_list_unref(list);
696 pango_layout_set_indent(layout,0);
697 pango_layout_set_justify(layout,FALSE);
698 pango_layout_set_alignment(layout,PANGO_ALIGN_LEFT);
700 #ifdef LAYOUT_CACHE
701 item->layout = layout;
702 g_object_ref(layout);
703 item->usecount = 1;
704 g_hash_table_replace(layoutcache, item, item);
705 #endif
707 return layout;
710 /* ************************************************************************ */
711 /* scaled versions of the utility routines */
712 /* ************************************************************************ */
714 void
715 dia_font_set_nominal_zoom_factor(real size_one)
716 { global_zoom_factor = size_one; }
720 real
721 dia_font_scaled_string_width(const char* string, DiaFont *font, real height,
722 real zoom_factor)
724 int lw,lh;
725 real result;
726 PangoLayout* layout;
728 if (string == NULL || string[0] == '\0') {
729 return 0.0;
732 layout = dia_font_scaled_build_layout(string, font, height, zoom_factor);
733 pango_layout_get_size(layout,&lw,&lh);
734 g_object_unref(G_OBJECT(layout));
736 result = pdu_to_dcm(lw);
737 /* Scale the result back for the zoom factor */
738 result /= (zoom_factor/global_zoom_factor);
739 return result;
742 static gboolean
743 dia_font_vertical_extents(const char* string, DiaFont* font,
744 real height, real zoom_factor,
745 guint line_no,
746 real* top, real* baseline, real* bottom)
748 PangoRectangle ink_rect,logical_rect;
749 PangoLayout* layout;
750 PangoLayoutIter* iter;
751 guint i;
753 if (string == NULL || string[0] == '\0') {
754 return FALSE;
757 layout = dia_font_scaled_build_layout(string, font,
758 height, zoom_factor);
759 iter = pango_layout_get_iter(layout);
760 for (i = 0; i < line_no; ++i) {
761 if (!pango_layout_iter_next_line(iter)) {
762 pango_layout_iter_free(iter);
763 g_object_unref(G_OBJECT(layout));
764 return FALSE;
768 pango_layout_iter_get_line_extents(iter,&ink_rect,&logical_rect);
770 *top = pdu_to_dcm(logical_rect.y);
771 *bottom = pdu_to_dcm(logical_rect.y + logical_rect.height);
772 *baseline = pdu_to_dcm(pango_layout_iter_get_baseline(iter));
774 pango_layout_iter_free(iter);
775 g_object_unref(G_OBJECT(layout));
777 return TRUE;
781 real
782 dia_font_scaled_ascent(const char* string, DiaFont* font, real height,
783 real zoom_factor)
785 real top,bline,bottom;
786 if (!string || string[0] == '\0') {
787 /* This hack won't work for fonts that don't cover ASCII */
788 dia_font_vertical_extents("XjgM149",font,height,zoom_factor,
789 0,&top,&bline,&bottom);
790 } else {
791 dia_font_vertical_extents(string,font,height,zoom_factor,
792 0,&top,&bline,&bottom);
794 return (bline-top)/(zoom_factor/global_zoom_factor);
797 real dia_font_scaled_descent(const char* string, DiaFont* font,
798 real height, real zoom_factor)
800 real top,bline,bottom;
802 if (!string || string[0] == '\0') {
803 /* This hack won't work for fonts that don't cover ASCII */
804 dia_font_vertical_extents("XjgM149",font,height,zoom_factor,
805 0,&top,&bline,&bottom);
806 } else {
807 dia_font_vertical_extents(string,font,height,zoom_factor,
808 0,&top,&bline,&bottom);
810 return (bottom-bline)/(zoom_factor/global_zoom_factor);
813 PangoLayout*
814 dia_font_scaled_build_layout(const char* string, DiaFont* font,
815 real height, real zoom_factor)
817 DiaFont* altered_font;
818 real scaling;
819 real nozoom_width;
820 real target_zoomed_width;
821 PangoLayout* layout;
822 real altered_scaling;
823 real real_width;
825 scaling = zoom_factor / global_zoom_factor;
826 if (fabs(1.0 - scaling) < 1E-7) {
827 return dia_font_build_layout(string,font,height);
830 nozoom_width = dia_font_string_width(string,font,height);
831 target_zoomed_width = nozoom_width * scaling;
833 /* First try: no tweaks. */
834 real_width = dia_font_string_width(string,font, height * scaling);
835 if (real_width <= target_zoomed_width) {
836 return dia_font_build_layout(string,font,height*scaling);
839 altered_font = dia_font_copy(font);
841 /* Last try. Using the "reduce overall size" strategy. */
842 for (altered_scaling = scaling;
843 altered_scaling > (scaling / 2);
844 altered_scaling *= (target_zoomed_width/real_width>0.98?0.98:
845 target_zoomed_width/real_width) ) {
846 real_width = dia_font_string_width(string,font,
847 height * altered_scaling);
849 if (real_width <= target_zoomed_width) {
850 layout = dia_font_build_layout(string,altered_font,
851 height*altered_scaling);
852 dia_font_unref(altered_font);
853 return layout;
857 /* Everything has failed. Returning non-tweaked variant. */
858 g_warning("Failed to appropriately tweak zoomed font for zoom factor %f.", zoom_factor);
859 dia_font_unref(altered_font);
860 return dia_font_build_layout(string,font,height*scaling);
864 * Compatibility with older files out of pre Pango Time.
865 * Make old files look as similar as possible
866 * List should be kept alphabetically sorted by oldname, in case of
867 * duplicates the one with the preferred newname comes first.
869 * FIXME: DIA_FONT_FAMILY_ANY in the list below does mean noone knows better
871 static struct _legacy_font {
872 gchar* oldname;
873 gchar* newname;
874 DiaFontStyle style; /* the DIA_FONT_FAMILY() is used as falback only */
875 } legacy_fonts[] = {
876 { "AvantGarde-Book", "AvantGarde", DIA_FONT_SERIF },
877 { "AvantGarde-BookOblique", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_OBLIQUE },
878 { "AvantGarde-Demi", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD },
879 { "AvantGarde-DemiOblique", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_OBLIQUE | DIA_FONT_DEMIBOLD },
880 { "Batang", "Batang", DIA_FONT_FAMILY_ANY },
881 { "Bookman-Demi", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD },
882 { "Bookman-DemiItalic", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD | DIA_FONT_ITALIC },
883 { "Bookman-Light", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_LIGHT },
884 { "Bookman-LightItalic", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_LIGHT | DIA_FONT_ITALIC },
885 { "BousungEG-Light-GB", "BousungEG-Light-GB", DIA_FONT_FAMILY_ANY },
886 { "Courier", "monospace", DIA_FONT_MONOSPACE },
887 { "Courier", "Courier New", DIA_FONT_MONOSPACE },
888 { "Courier-Bold", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_BOLD },
889 { "Courier-Bold", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_BOLD },
890 { "Courier-BoldOblique", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD },
891 { "Courier-BoldOblique", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD },
892 { "Courier-Oblique", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC},
893 { "Courier-Oblique", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC },
894 { "Dotum", "Dotum", DIA_FONT_FAMILY_ANY },
895 { "GBZenKai-Medium", "GBZenKai-Medium", DIA_FONT_FAMILY_ANY },
896 { "GothicBBB-Medium", "GothicBBB-Medium", DIA_FONT_FAMILY_ANY },
897 { "Gulim", "Gulim", DIA_FONT_FAMILY_ANY },
898 { "Headline", "Headline", DIA_FONT_FAMILY_ANY },
899 { "Helvetica", "sans", DIA_FONT_SANS },
900 { "Helvetica", "Arial", DIA_FONT_SANS },
901 { "Helvetica-Bold", "sans", DIA_FONT_SANS | DIA_FONT_BOLD },
902 { "Helvetica-Bold", "Arial", DIA_FONT_SANS | DIA_FONT_BOLD },
903 { "Helvetica-BoldOblique", "sans", DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC },
904 { "Helvetica-BoldOblique", "Arial", DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC },
905 { "Helvetica-Narrow", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM },
906 { "Helvetica-Narrow-Bold", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_DEMIBOLD },
907 { "Helvetica-Narrow-BoldOblique", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM | DIA_FONT_OBLIQUE },
908 { "Helvetica-Narrow-Oblique", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM | DIA_FONT_OBLIQUE },
909 { "Helvetica-Oblique", "sans", DIA_FONT_SANS | DIA_FONT_ITALIC },
910 { "Helvetica-Oblique", "Arial", DIA_FONT_SANS | DIA_FONT_ITALIC },
911 { "MOESung-Medium", "MOESung-Medium", DIA_FONT_FAMILY_ANY },
912 { "NewCenturySchoolbook-Bold", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_BOLD },
913 { "NewCenturySchoolbook-BoldItalic", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_BOLD | DIA_FONT_ITALIC },
914 { "NewCenturySchoolbook-Italic", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_ITALIC },
915 { "NewCenturySchoolbook-Roman", "Century Schoolbook SWA", DIA_FONT_SERIF },
916 { "Palatino-Bold", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_BOLD },
917 { "Palatino-BoldItalic", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_BOLD | DIA_FONT_ITALIC },
918 { "Palatino-Italic", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_ITALIC },
919 { "Palatino-Roman", "Palatino", DIA_FONT_FAMILY_ANY },
920 { "Ryumin-Light", "Ryumin", DIA_FONT_FAMILY_ANY | DIA_FONT_LIGHT },
921 { "ShanHeiSun-Light", "ShanHeiSun", DIA_FONT_FAMILY_ANY | DIA_FONT_LIGHT },
922 { "Song-Medium", "Song-Medium", DIA_FONT_FAMILY_ANY | DIA_FONT_MEDIUM },
923 { "Symbol", "Symbol", DIA_FONT_SANS | DIA_FONT_MEDIUM },
924 { "Times-Bold", "serif", DIA_FONT_SERIF | DIA_FONT_BOLD },
925 { "Times-Bold", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_BOLD },
926 { "Times-BoldItalic", "serif", DIA_FONT_SERIF | DIA_FONT_ITALIC | DIA_FONT_BOLD },
927 { "Times-BoldItalic", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_ITALIC | DIA_FONT_BOLD },
928 { "Times-Italic", "serif", DIA_FONT_SERIF | DIA_FONT_ITALIC },
929 { "Times-Italic", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_ITALIC },
930 { "Times-Roman", "serif", DIA_FONT_SERIF },
931 { "Times-Roman", "Times New Roman", DIA_FONT_SERIF },
932 { "ZapfChancery-MediumItalic", "Zapf Calligraphic 801 SWA", DIA_FONT_SERIF | DIA_FONT_MEDIUM },
933 { "ZapfDingbats", "Zapf Calligraphic 801 SWA", DIA_FONT_SERIF },
934 { "ZenKai-Medium", "ZenKai", DIA_FONT_FAMILY_ANY | DIA_FONT_MEDIUM },
939 * Given a legacy name as stored until Dia-0.90 construct
940 * a new DiaFont which is as similar as possible
942 DiaFont*
943 dia_font_new_from_legacy_name(const char* name)
945 /* do NOT translate anything here !!! */
946 DiaFont* retval;
947 struct _legacy_font* found = NULL;
948 real height = 1.0;
949 int i;
951 for (i = 0; i < G_N_ELEMENTS(legacy_fonts); i++) {
952 if (!strcmp(name, legacy_fonts[i].oldname)) {
953 found = &legacy_fonts[i];
954 break;
957 if (found) {
958 retval = dia_font_new (found->newname, found->style, height);
959 retval->legacy_name = found->oldname;
960 } else {
961 /* We tried our best, let Pango complain */
962 retval = dia_font_new (name, DIA_FONT_WEIGHT_NORMAL, height);
963 retval->legacy_name = NULL;
966 return retval;
969 G_CONST_RETURN char*
970 dia_font_get_legacy_name(const DiaFont *font)
972 const char* matched_name = NULL;
973 const char* family;
974 DiaFontStyle style;
975 int i;
977 /* if we have loaded it from an old file, use the old name */
978 if (font->legacy_name)
979 return font->legacy_name;
981 family = dia_font_get_family (font);
982 style = dia_font_get_style (font);
983 for (i = 0; i < G_N_ELEMENTS(legacy_fonts); i++) {
984 if (0 == g_strcasecmp (legacy_fonts[i].newname, family)) {
985 /* match weight and slant */
986 DiaFontStyle st = legacy_fonts[i].style;
987 if ((DIA_FONT_STYLE_GET_SLANT(style) | DIA_FONT_STYLE_GET_WEIGHT(style))
988 == (DIA_FONT_STYLE_GET_SLANT(st) | DIA_FONT_STYLE_GET_WEIGHT(st))) {
989 return legacy_fonts[i].oldname; /* exact match */
990 } else if (0 == (DIA_FONT_STYLE_GET_SLANT(st) | DIA_FONT_STYLE_GET_WEIGHT(st))) {
991 matched_name = legacy_fonts[i].oldname;
992 /* 'unmodified' font, continue matching */
996 return matched_name ? matched_name : "Courier";