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
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h> /* strlen */
28 #include <time.h>
29
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() */
36 #ifdef GDK_WINDOWING_WIN32
37 /* avoid namespace clashes caused by inclusion of windows.h */
38 #define Rectangle Win32Rectangle
39 #define WIN32_LEAN_AND_MEAN
40 #include <pango/pangowin32.h>
41 #undef Rectangle
42 #endif
43 #include "font.h"
44 #include "message.h"
45 #include "intl.h"
46 #include "textline.h"
47
48 static PangoContext* pango_context = NULL;
49
50 struct _DiaFont
51 {
52 GObject parent_instance;
53
54 PangoFontDescription* pfd;
55 /* mutable */ char* legacy_name;
56
57 /* there is a difference between Pango's font size and Dia's font height */
58 /* Calculated font_size is to make 'font_height = ascent + descent */
59 /* The font_height is used as default line height, there used to be a hard-coded size = 0.7 * height */
60 /* before using pango_set_absolute_size() to overcome font size differences between renderers */
61 real height;
62 /* Need to load a font to query it's metrics */
63 PangoFont *loaded;
64 PangoFontMetrics *metrics;
65 };
66
67
68 /* This is the global factor that says what zoom factor is 100%. It's
69 * normally 20.0 (and likely to stay that way). It is defined by how many
70 * pixels one cm is represented as.
71 */
72 static real global_zoom_factor = 20.0;
73
74 static void
dia_font_check_for_font(int font)75 dia_font_check_for_font(int font)
76 {
77 DiaFont *check;
78 PangoFont *loaded;
79 static real height = 1.0;
80
81 check = dia_font_new_from_style(font, height);
82 loaded = pango_context_load_font(dia_font_get_context(), check->pfd);
83 if (!loaded) {
84 message_error(_("Can't load font %s.\n"), dia_font_get_family(check));
85 } else {
86 g_object_unref(loaded);
87 }
88 dia_font_unref(check);
89 }
90
91 void
dia_font_init(PangoContext * pcontext)92 dia_font_init(PangoContext* pcontext)
93 {
94 pango_context = pcontext;
95 /* We must have these three fonts! */
96 dia_font_check_for_font(DIA_FONT_SANS);
97 dia_font_check_for_font(DIA_FONT_SERIF);
98 dia_font_check_for_font(DIA_FONT_MONOSPACE);
99 }
100
101 /* We might not need these anymore, when using FT2/Win32 fonts only */
102 static GList *pango_contexts = NULL;
103
104 /*! DEPRECATED: there should be only one "measure context" */
105 void
dia_font_push_context(PangoContext * pcontext)106 dia_font_push_context(PangoContext *pcontext)
107 {
108 pango_contexts = g_list_prepend(pango_contexts, pango_context);
109 pango_context = pcontext;
110 pango_context_set_language (pango_context, gtk_get_default_language ());
111 g_object_ref(pcontext);
112 }
113
114 /*! DEPRECATED: there should be only one "measure context" */
115 void
dia_font_pop_context()116 dia_font_pop_context() {
117 g_object_unref(pango_context);
118 pango_context = (PangoContext*)pango_contexts->data;
119 pango_context_set_language (pango_context, gtk_get_default_language ());
120 pango_contexts = g_list_next(pango_contexts);
121 }
122
123 PangoContext *
dia_font_get_context()124 dia_font_get_context()
125 {
126 if (pango_context == NULL) {
127 /* Maybe this one with pangocairo
128 dia_font_push_context (pango_cairo_font_map_create_context (pango_cairo_font_map_get_default()));
129 * but it gives:
130 (lt-dia:30476): Pango-CRITICAL **: pango_renderer_draw_layout: assertion `PANGO_IS_RENDERER (renderer)' failed
131 */
132 #ifdef HAVE_FREETYPE
133 /* This is suggested by new Pango (1.2.4+), but doesn't get us the
134 * right resolution:(
135 dia_font_push_context(pango_ft2_font_map_create_context(pango_ft2_font_map_new()));
136 */
137 /* with 96x96 it gives consistent - too big - sizes with the cairo renderer, it was 75x75 with 0.96.x
138 dia_font_push_context(pango_ft2_get_context(96,96));
139 */
140 dia_font_push_context(pango_ft2_get_context(75,75));
141 #else
142 if (gdk_display_get_default ())
143 dia_font_push_context(gdk_pango_context_get());
144 else {
145 # ifdef GDK_WINDOWING_WIN32
146 dia_font_push_context(pango_win32_get_context ());
147 # else
148 g_warning ("dia_font_get_context() : not font context w/o display. Crashing soon.");
149 # endif
150 }
151 #endif
152 }
153 return pango_context;
154 }
155
156 /* dia centimetres to pango device units */
157 static gint
dcm_to_pdu(real dcm)158 dcm_to_pdu(real dcm) { return dcm * global_zoom_factor * PANGO_SCALE; }
159 /* pango device units to dia centimetres */
160 static real
pdu_to_dcm(gint pdu)161 pdu_to_dcm(gint pdu) { return (real)pdu / (global_zoom_factor * PANGO_SCALE); }
162
163 static void dia_font_class_init(DiaFontClass* class);
164 static void dia_font_finalize(GObject* object);
165 static void dia_font_init_instance(DiaFont*);
166
167 GType
dia_font_get_type(void)168 dia_font_get_type (void)
169 {
170 static GType object_type = 0;
171
172 if (!object_type) {
173 static const GTypeInfo object_info =
174 {
175 sizeof (DiaFontClass),
176 (GBaseInitFunc) NULL,
177 (GBaseFinalizeFunc) NULL,
178 (GClassInitFunc) dia_font_class_init, /* class_init */
179 NULL, /* class_finalize */
180 NULL, /* class_data */
181 sizeof (DiaFont),
182 0, /* n_preallocs */
183 (GInstanceInitFunc)dia_font_init_instance
184 };
185 object_type = g_type_register_static (G_TYPE_OBJECT,
186 "DiaFont",
187 &object_info, 0);
188 }
189 return object_type;
190 }
191 static gpointer parent_class;
192
193 static void
dia_font_class_init(DiaFontClass * klass)194 dia_font_class_init(DiaFontClass* klass)
195 {
196 GObjectClass* object_class = G_OBJECT_CLASS(klass);
197 parent_class = g_type_class_peek_parent(klass);
198 object_class->finalize = dia_font_finalize;
199 }
200
201 static void
dia_font_init_instance(DiaFont * font)202 dia_font_init_instance(DiaFont* font)
203 {
204 /*GObject *gobject = G_OBJECT(font); */
205 }
206
207 static void
dia_pfd_set_height(PangoFontDescription * pfd,real height)208 dia_pfd_set_height(PangoFontDescription* pfd, real height)
209 {
210 /* ONLY place for the magic factor! */
211 pango_font_description_set_absolute_size(pfd, dcm_to_pdu(height) * 0.8);
212 }
213
214 /*!
215 * In Dia a font is usually referred to by it's (line-) height, not it's size.
216 *
217 * This methods "calculates" the latter from the former. This used to be some magic factor of 0.7 which did not
218 * solve the resolution dependencance of the former calculation. In fact there is new magic factor now because
219 * really calculating the font size from the height would involve two font loads which seem to be two expensive.
220 */
221 static void
_dia_font_adjust_size(DiaFont * font,real height,gboolean recalc_alwways)222 _dia_font_adjust_size (DiaFont *font, real height, gboolean recalc_alwways)
223 {
224
225 if (font->height != height || !font->metrics || recalc_alwways) {
226 PangoFont *loaded;
227
228 dia_pfd_set_height (font->pfd, height);
229 /* need to load a font to get it's metrics */
230 loaded = font->loaded;
231 font->loaded = pango_context_load_font(dia_font_get_context(), font->pfd);
232 if (loaded) /* drop old reference */
233 g_object_unref (loaded);
234 if (font->metrics)
235 pango_font_metrics_unref (font->metrics);
236
237 /* caching metrics */
238 font->metrics = pango_font_get_metrics (font->loaded, NULL);
239 font->height = height;
240 }
241 }
242 DiaFont*
dia_font_new(const char * family,DiaFontStyle style,real height)243 dia_font_new(const char *family, DiaFontStyle style, real height)
244 {
245 DiaFont* font = dia_font_new_from_style(style, height);
246 gboolean changed;
247
248 changed = family != NULL && strcmp (pango_font_description_get_family(font->pfd), family) != 0;
249 pango_font_description_set_family(font->pfd, family);
250
251 if (changed)
252 _dia_font_adjust_size (font, font->height, TRUE);
253
254 return font;
255 }
256
257 static void
dia_pfd_set_family(PangoFontDescription * pfd,DiaFontFamily fam)258 dia_pfd_set_family(PangoFontDescription* pfd, DiaFontFamily fam)
259 {
260 switch (fam) {
261 case DIA_FONT_SANS :
262 pango_font_description_set_family(pfd, "sans");
263 break;
264 case DIA_FONT_SERIF :
265 pango_font_description_set_family(pfd, "serif");
266 break;
267 case DIA_FONT_MONOSPACE :
268 pango_font_description_set_family(pfd, "monospace");
269 break;
270 default :
271 /* Pango does _not_ allow fonts without a name (or at least they are not useful) */
272 pango_font_description_set_family(pfd, "sans");
273 break;
274 }
275 }
276
277 static void
dia_pfd_set_weight(PangoFontDescription * pfd,DiaFontWeight fw)278 dia_pfd_set_weight(PangoFontDescription* pfd, DiaFontWeight fw)
279 {
280 switch (fw) {
281 case DIA_FONT_ULTRALIGHT :
282 pango_font_description_set_weight(pfd, PANGO_WEIGHT_ULTRALIGHT);
283 break;
284 case DIA_FONT_LIGHT :
285 pango_font_description_set_weight(pfd, PANGO_WEIGHT_LIGHT);
286 break;
287 case DIA_FONT_WEIGHT_NORMAL :
288 pango_font_description_set_weight(pfd, PANGO_WEIGHT_NORMAL);
289 break;
290 case DIA_FONT_MEDIUM : /* Pango doesn't have this, but
291 'intermediate values are possible' */
292 pango_font_description_set_weight(pfd, 500);
293 break;
294 case DIA_FONT_DEMIBOLD : /* Pango doesn't have this, ... */
295 pango_font_description_set_weight(pfd, 600);
296 break;
297 case DIA_FONT_BOLD :
298 pango_font_description_set_weight(pfd, PANGO_WEIGHT_BOLD);
299 break;
300 case DIA_FONT_ULTRABOLD :
301 pango_font_description_set_weight(pfd, PANGO_WEIGHT_ULTRABOLD);
302 break;
303 case DIA_FONT_HEAVY :
304 pango_font_description_set_weight(pfd, PANGO_WEIGHT_HEAVY);
305 break;
306 default :
307 g_assert_not_reached();
308 }
309 }
310
311 static void
dia_pfd_set_slant(PangoFontDescription * pfd,DiaFontSlant fo)312 dia_pfd_set_slant(PangoFontDescription* pfd, DiaFontSlant fo)
313 {
314 switch (fo) {
315 case DIA_FONT_NORMAL :
316 pango_font_description_set_style(pfd,PANGO_STYLE_NORMAL);
317 break;
318 case DIA_FONT_OBLIQUE :
319 pango_font_description_set_style(pfd,PANGO_STYLE_OBLIQUE);
320 break;
321 case DIA_FONT_ITALIC :
322 pango_font_description_set_style(pfd,PANGO_STYLE_ITALIC);
323 break;
324 default :
325 g_assert_not_reached();
326 }
327 }
328
329 DiaFont*
dia_font_new_from_style(DiaFontStyle style,real height)330 dia_font_new_from_style(DiaFontStyle style, real height)
331 {
332 DiaFont* retval;
333 /* in the future we could establish Dia's own default font
334 * matching to be as (font-)system independent as possible.
335 * For now fall back to Pangos configuration --hb
336 */
337 PangoFontDescription* pfd = pango_font_description_new();
338 dia_pfd_set_family(pfd,DIA_FONT_STYLE_GET_FAMILY(style));
339 dia_pfd_set_weight(pfd,DIA_FONT_STYLE_GET_WEIGHT(style));
340 dia_pfd_set_slant(pfd,DIA_FONT_STYLE_GET_SLANT(style));
341 dia_pfd_set_height(pfd,height);
342
343 retval = DIA_FONT(g_object_new(DIA_TYPE_FONT, NULL));
344 retval->pfd = pfd;
345 _dia_font_adjust_size (retval, height, FALSE);
346 retval->legacy_name = NULL;
347 return retval;
348 }
349
dia_font_copy(const DiaFont * font)350 DiaFont* dia_font_copy(const DiaFont* font)
351 {
352 if (!font)
353 return NULL;
354 return dia_font_new(dia_font_get_family(font),
355 dia_font_get_style(font),
356 dia_font_get_height(font));
357 }
358
359 void
dia_font_finalize(GObject * object)360 dia_font_finalize(GObject* object)
361 {
362 DiaFont* font = DIA_FONT(object);
363
364 if (font->pfd)
365 pango_font_description_free(font->pfd);
366 font->pfd = NULL;
367 if (font->metrics)
368 pango_font_metrics_unref(font->metrics);
369 font->metrics = NULL;
370 if (font->loaded)
371 g_object_unref(font->loaded);
372 font->loaded = NULL;
373 G_OBJECT_CLASS(parent_class)->finalize(object);
374 }
375
376 DiaFont *
dia_font_ref(DiaFont * font)377 dia_font_ref(DiaFont* font)
378 {
379 g_object_ref(G_OBJECT(font));
380 return font;
381 }
382
383 void
dia_font_unref(DiaFont * font)384 dia_font_unref(DiaFont* font)
385 {
386 g_object_unref(G_OBJECT(font));
387 }
388
389 DiaFontStyle
dia_font_get_style(const DiaFont * font)390 dia_font_get_style(const DiaFont* font)
391 {
392 guint style;
393
394 static int weight_map[] = {
395 DIA_FONT_ULTRALIGHT, DIA_FONT_LIGHT,
396 DIA_FONT_WEIGHT_NORMAL, /* intentionaly ==0 */
397 DIA_FONT_MEDIUM, DIA_FONT_DEMIBOLD, /* not yet in Pango */
398 DIA_FONT_BOLD, DIA_FONT_ULTRABOLD, DIA_FONT_HEAVY
399 };
400
401 PangoStyle pango_style = pango_font_description_get_style(font->pfd);
402 PangoWeight pango_weight = pango_font_description_get_weight(font->pfd);
403
404 g_assert(PANGO_WEIGHT_ULTRALIGHT <= pango_weight && pango_weight <= PANGO_WEIGHT_HEAVY);
405 g_assert(PANGO_WEIGHT_ULTRALIGHT == 200);
406 g_assert(PANGO_WEIGHT_NORMAL == 400);
407 g_assert(PANGO_WEIGHT_BOLD == 700);
408
409 style = weight_map[(pango_weight - PANGO_WEIGHT_ULTRALIGHT) / 100];
410 style |= (pango_style << 2);
411
412 return style;
413 }
414
415 G_CONST_RETURN char*
dia_font_get_family(const DiaFont * font)416 dia_font_get_family(const DiaFont* font)
417 {
418 return pango_font_description_get_family(font->pfd);
419 }
420
421 G_CONST_RETURN PangoFontDescription *
dia_font_get_description(const DiaFont * font)422 dia_font_get_description(const DiaFont* font)
423 {
424 return font->pfd;
425 }
426
427 real
dia_font_get_height(const DiaFont * font)428 dia_font_get_height(const DiaFont* font)
429 {
430 return font->height;
431 }
432
433 real
dia_font_get_size(const DiaFont * font)434 dia_font_get_size(const DiaFont* font)
435 {
436 if (!pango_font_description_get_size_is_absolute (font->pfd))
437 g_warning ("dia_font_get_size() : no absolute size");
438 return pdu_to_dcm(pango_font_description_get_size(font->pfd));
439 }
440
441 void
dia_font_set_height(DiaFont * font,real height)442 dia_font_set_height(DiaFont* font, real height)
443 {
444 _dia_font_adjust_size (font, height, FALSE);
445 }
446
447
448 G_CONST_RETURN char*
dia_font_get_psfontname(const DiaFont * font)449 dia_font_get_psfontname(const DiaFont *font)
450 {
451 /* This hack corrects a couple fonts that were misnamed in
452 * earlier versions of Dia. See bug #477079.
453 */
454 const char *fontname = dia_font_get_legacy_name(font);
455
456 if (!fontname)
457 return NULL;
458
459 if (strcmp(fontname, "NewCenturySchoolbook-Roman") == 0)
460 return "NewCenturySchlbk-Roman";
461 else if (strcmp(fontname, "NewCenturySchoolbook-Italic") == 0)
462 return "NewCenturySchlbk-Italic";
463 else if (strcmp(fontname, "NewCenturySchoolbook-Bold") == 0)
464 return "NewCenturySchlbk-Bold";
465 else if (strcmp(fontname, "NewCenturySchoolbook-BoldItalic") == 0)
466 return "NewCenturySchlbk-BoldItalic";
467
468 return fontname;
469 }
470
471 void
dia_font_set_any_family(DiaFont * font,const char * family)472 dia_font_set_any_family(DiaFont* font, const char* family)
473 {
474 gboolean changed;
475
476 g_return_if_fail(font != NULL);
477
478 changed = strcmp (pango_font_description_get_family(font->pfd), family) != 0;
479 pango_font_description_set_family(font->pfd, family);
480 if (changed) /* force recalculation on name change */
481 _dia_font_adjust_size (font, font->height, TRUE);
482 if (font->legacy_name) {
483 g_free(font->legacy_name);
484 font->legacy_name = NULL;
485 }
486 }
487
488 void
dia_font_set_family(DiaFont * font,DiaFontFamily family)489 dia_font_set_family(DiaFont* font, DiaFontFamily family)
490 {
491 g_return_if_fail(font != NULL);
492
493 dia_pfd_set_family(font->pfd,family);
494 if (font->legacy_name) {
495 g_free(font->legacy_name);
496 font->legacy_name = NULL;
497 }
498 }
499
500 void
dia_font_set_weight(DiaFont * font,DiaFontWeight weight)501 dia_font_set_weight(DiaFont* font, DiaFontWeight weight)
502 {
503 DiaFontWeight old_weight = DIA_FONT_STYLE_GET_WEIGHT(dia_font_get_style(font));
504 g_return_if_fail(font != NULL);
505 dia_pfd_set_weight(font->pfd,weight);
506 if (old_weight != weight)
507 _dia_font_adjust_size (font, font->height, TRUE);
508 }
509
510 void
dia_font_set_slant(DiaFont * font,DiaFontSlant slant)511 dia_font_set_slant(DiaFont* font, DiaFontSlant slant)
512 {
513 DiaFontSlant old_slant = DIA_FONT_STYLE_GET_SLANT(dia_font_get_style(font));
514 g_return_if_fail(font != NULL);
515 dia_pfd_set_slant(font->pfd,slant);
516 if (slant != old_slant)
517 _dia_font_adjust_size (font, font->height, TRUE);
518 }
519
520
521 typedef struct _WeightName {
522 DiaFontWeight fw;
523 const char *name;
524 } WeightName;
525 static const WeightName weight_names[] = {
526 {DIA_FONT_ULTRALIGHT, "200"},
527 {DIA_FONT_LIGHT,"300"},
528 {DIA_FONT_WEIGHT_NORMAL,"normal"},
529 {DIA_FONT_WEIGHT_NORMAL,"400"},
530 {DIA_FONT_MEDIUM, "500"},
531 {DIA_FONT_DEMIBOLD, "600"},
532 {DIA_FONT_BOLD, "700"},
533 {DIA_FONT_ULTRABOLD, "800"},
534 {DIA_FONT_HEAVY, "900"},
535 {0,NULL}
536 };
537
538 G_CONST_RETURN char *
dia_font_get_weight_string(const DiaFont * font)539 dia_font_get_weight_string(const DiaFont* font)
540 {
541 const WeightName* p;
542 DiaFontWeight fw = DIA_FONT_STYLE_GET_WEIGHT(dia_font_get_style(font));
543
544 for (p = weight_names; p->name != NULL; ++p) {
545 if (p->fw == fw) return p->name;
546 }
547 return "normal";
548 }
549
550 void
dia_font_set_weight_from_string(DiaFont * font,const char * weight)551 dia_font_set_weight_from_string(DiaFont* font, const char* weight)
552 {
553 DiaFontWeight fw = DIA_FONT_WEIGHT_NORMAL;
554 const WeightName* p;
555
556 for (p = weight_names; p->name != NULL; ++p) {
557 if (0 == strncmp(weight,p->name,8)) {
558 fw = p->fw;
559 break;
560 }
561 }
562
563 dia_font_set_weight(font,fw);
564 }
565
566
567 typedef struct _SlantName {
568 DiaFontSlant fo;
569 const char *name;
570 } SlantName;
571 static const SlantName slant_names[] = {
572 { DIA_FONT_NORMAL, "normal" },
573 { DIA_FONT_OBLIQUE, "oblique" },
574 { DIA_FONT_ITALIC, "italic" },
575 { 0, NULL}
576 };
577
578 G_CONST_RETURN char *
dia_font_get_slant_string(const DiaFont * font)579 dia_font_get_slant_string(const DiaFont* font)
580 {
581 const SlantName* p;
582 DiaFontSlant fo =
583 DIA_FONT_STYLE_GET_SLANT(dia_font_get_style(font));
584
585 for (p = slant_names; p->name != NULL; ++p) {
586 if (p->fo == fo)
587 return p->name;
588 }
589 return "normal";
590 }
591
592 void
dia_font_set_slant_from_string(DiaFont * font,const char * obli)593 dia_font_set_slant_from_string(DiaFont* font, const char* obli)
594 {
595 DiaFontSlant fo = DIA_FONT_NORMAL;
596 const SlantName* p;
597
598 DiaFontStyle old_style;
599 DiaFontSlant old_fo;
600 old_style = dia_font_get_style(font);
601 old_fo = DIA_FONT_STYLE_GET_SLANT(old_style);
602
603 for (p = slant_names; p->name != NULL; ++p) {
604 if (0 == strncmp(obli,p->name,8)) {
605 fo = p->fo;
606 break;
607 }
608 }
609 dia_font_set_slant(font,fo);
610 }
611
612
613 /* ************************************************************************ */
614 /* Non-scaled versions of the utility routines */
615 /* ************************************************************************ */
616
617 real
dia_font_string_width(const char * string,DiaFont * font,real height)618 dia_font_string_width(const char* string, DiaFont *font, real height)
619 {
620 real result = 0;
621 if (string && *string) {
622 TextLine *text_line = text_line_new(string, font, height);
623 result = text_line_get_width(text_line);
624 text_line_destroy(text_line);
625 }
626 return result;
627 }
628
629 real
dia_font_ascent(const char * string,DiaFont * font,real height)630 dia_font_ascent(const char* string, DiaFont* font, real height)
631 {
632 if (font->metrics) {
633 real ascent = pdu_to_dcm(pango_font_metrics_get_ascent (font->metrics));
634 return ascent * (height / font->height);
635 } else {
636 /* previous, _expensive_ but string specific way */
637 TextLine *text_line = text_line_new(string, font, height);
638 real result = text_line_get_ascent(text_line);
639 text_line_destroy(text_line);
640 return result;
641 }
642 }
643
644 real
dia_font_descent(const char * string,DiaFont * font,real height)645 dia_font_descent(const char* string, DiaFont* font, real height)
646 {
647 if (font->metrics) {
648 real descent = pdu_to_dcm(pango_font_metrics_get_descent (font->metrics));
649 return descent * (height / font->height);
650 } else {
651 /* previous, _expensive_ but string specific way */
652 TextLine *text_line = text_line_new(string, font, height);
653 real result = text_line_get_descent(text_line);
654 text_line_destroy(text_line);
655 return result;
656 }
657 }
658
659
660 PangoLayout*
dia_font_build_layout(const char * string,DiaFont * font,real height)661 dia_font_build_layout(const char* string, DiaFont* font, real height)
662 {
663 PangoLayout* layout;
664 PangoAttrList* list;
665 PangoAttribute* attr;
666 guint length;
667 PangoFontDescription *pfd;
668 real factor;
669
670 layout = pango_layout_new(dia_font_get_context());
671
672 length = string ? strlen(string) : 0;
673 pango_layout_set_text(layout, string, length);
674
675 list = pango_attr_list_new();
676
677 pfd = pango_font_description_copy (font->pfd);
678 /* account for difference between size and height as well as between font height and given one */
679 factor = dia_font_get_size(font) / dia_font_get_height (font);
680 pango_font_description_set_absolute_size (pfd, dcm_to_pdu (height) * factor);
681 attr = pango_attr_font_desc_new(pfd);
682 pango_font_description_free (pfd);
683
684 attr->start_index = 0;
685 attr->end_index = length;
686 pango_attr_list_insert(list,attr); /* eats attr */
687
688 pango_layout_set_attributes(layout,list);
689 pango_attr_list_unref(list);
690
691 pango_layout_set_indent(layout,0);
692 pango_layout_set_justify(layout,FALSE);
693 pango_layout_set_alignment(layout,PANGO_ALIGN_LEFT);
694
695 return layout;
696 }
697
698 /** Find the offsets of the individual letters in the iter and place them
699 * in an array.
700 * This currently assumes only one run per iter, which is all we can input.
701 * @param iter The PangoLayoutIter to count characters in.
702 * @param offsets The place to return the offsets
703 * @param n_offsets The place to return the number of offsets
704 */
705 static void
get_string_offsets(PangoLayoutIter * iter,real ** offsets,int * n_offsets)706 get_string_offsets(PangoLayoutIter *iter, real** offsets, int* n_offsets)
707 {
708 int i;
709 PangoLayoutLine* line = pango_layout_iter_get_line(iter);
710 PangoGlyphItem* item;
711 PangoGlyphString* string;
712
713 if(0 == line->length)
714 {
715 *n_offsets = 0;
716 return;
717 }
718 item = (PangoGlyphItem*)line->runs->data;
719 string = item->glyphs;
720
721 *n_offsets = string->num_glyphs;
722 *offsets = g_new(real, *n_offsets);
723
724 for (i = 0; i < string->num_glyphs; i++) {
725 PangoGlyphGeometry geom = string->glyphs[i].geometry;
726
727 (*offsets)[i] = pdu_to_dcm(geom.width) / global_zoom_factor;
728 }
729 }
730
731 /** Copy the offsets into a storage object, adjusting for exaggerated size.
732 */
733 static void
get_layout_offsets(PangoLayoutLine * line,PangoLayoutLine ** layout_line)734 get_layout_offsets(PangoLayoutLine *line, PangoLayoutLine **layout_line)
735 {
736 /* Some info not copied. */
737 GSList *layout_runs = NULL;
738 GSList *runs = line->runs;
739
740 *layout_line = g_new0(PangoLayoutLine, 1);
741
742 /* A LayoutLine contains a GSList runs of PangoGlyphItems.
743 * Each PangoGlyphItem contains an array glyphs of PangoGlyphString.
744 * This array is run->item->num_chars long?
745 * Each PangoGlyphString contains an array glyphs of PangoGlyphInfo.
746 * This array is run->glyphs[i].num_glyphs long.
747 */
748 for (; runs != NULL; runs = g_slist_next(runs)) {
749 PangoGlyphItem *run = (PangoGlyphItem *) runs->data;
750 PangoGlyphItem *layout_run = g_new0(PangoGlyphItem, 1);
751 int j;
752 /* Make single pointer */
753 PangoGlyphString *glyph_string = run->glyphs;
754 PangoGlyphString *layout_glyph_string;
755
756 layout_run->glyphs = g_new0(PangoGlyphString, 1);
757 layout_glyph_string = layout_run->glyphs;
758
759 layout_glyph_string->num_glyphs = glyph_string->num_glyphs;
760 layout_glyph_string->glyphs =
761 g_new0(PangoGlyphInfo, glyph_string->num_glyphs);
762 for (j = 0; j < layout_glyph_string->num_glyphs; j++) {
763 PangoGlyphInfo *info = &glyph_string->glyphs[j];
764 PangoGlyphInfo *layout_info = &layout_glyph_string->glyphs[j];
765 layout_info->geometry.width = info->geometry.width;
766 layout_info->geometry.x_offset = info->geometry.x_offset;
767 layout_info->geometry.y_offset = info->geometry.y_offset;
768 }
769 layout_runs = g_slist_append(layout_runs, layout_run);
770 }
771 (*layout_line)->runs = layout_runs;
772 }
773
774 /** Get size information for the given string, font and height.
775 *
776 * @returns an array of offsets of the individual glyphs in the layout.
777 */
778 real*
dia_font_get_sizes(const char * string,DiaFont * font,real height,real * width,real * ascent,real * descent,int * n_offsets,PangoLayoutLine ** layout_offsets)779 dia_font_get_sizes(const char* string, DiaFont *font, real height,
780 real *width, real *ascent, real *descent, int *n_offsets,
781 PangoLayoutLine **layout_offsets)
782 {
783 PangoLayout* layout;
784 PangoLayoutIter* iter;
785 real top, bline, bottom;
786 const gchar* non_empty_string;
787 PangoRectangle ink_rect,logical_rect;
788 real* offsets = NULL; /* avoid: 'offsets' may be used uninitialized in this function */
789
790 /* We need some reasonable ascent/descent values even for empty strings. */
791 if (string == NULL || string[0] == '\0') {
792 non_empty_string = "XjgM149";
793 } else {
794 non_empty_string = string;
795 }
796 layout = dia_font_build_layout(non_empty_string, font, height * global_zoom_factor);
797
798 /* Only one line here ? */
799 iter = pango_layout_get_iter(layout);
800
801 pango_layout_iter_get_line_extents(iter, &ink_rect, &logical_rect);
802
803 top = pdu_to_dcm(logical_rect.y) / global_zoom_factor;
804 bottom = pdu_to_dcm(logical_rect.y + logical_rect.height) / global_zoom_factor;
805 bline = pdu_to_dcm(pango_layout_iter_get_baseline(iter)) / global_zoom_factor;
806
807 get_string_offsets(iter, &offsets, n_offsets);
808 get_layout_offsets(pango_layout_get_line(layout, 0), layout_offsets);
809
810 /* FIXME: the above assumption of 'one line' is wrong. At least calculate the overall width correctly
811 * to avoid text overflowing its box, like in bug #482585 */
812 while (pango_layout_iter_next_line (iter)) {
813 PangoRectangle more_ink_rect, more_logical_rect;
814
815 pango_layout_iter_get_line_extents(iter, &more_ink_rect, &more_logical_rect);
816 if (more_logical_rect.width > logical_rect.width)
817 logical_rect.width = more_logical_rect.width;
818 /* also calculate for the ink rect (true space needed for drawing the glyphs) */
819 if (more_ink_rect.width > ink_rect.width)
820 ink_rect.width = more_ink_rect.width;
821 }
822
823 pango_layout_iter_free(iter);
824 g_object_unref(G_OBJECT(layout));
825
826 *ascent = bline-top;
827 *descent = bottom-bline;
828 if (non_empty_string != string) {
829 *width = 0.0;
830 } else {
831 /* take the bigger rectangle to avoid cutting of any part of the string */
832 *width = pdu_to_dcm(logical_rect.width > ink_rect.width ? logical_rect.width : ink_rect.width) / global_zoom_factor;
833 }
834 return offsets;
835 }
836
837
838 /**
839 * Compatibility with older files out of pre Pango Time.
840 * Make old files look as similar as possible
841 * List should be kept alphabetically sorted by oldname, in case of
842 * duplicates the one with the preferred newname comes first.
843 *
844 * FIXME: DIA_FONT_FAMILY_ANY in the list below does mean noone knows better
845 *
846 * The PostScript names can be found on page 139 of
847 * http://partners.adobe.com/public/developer/en/ps/PS3010and3011.Supplement.pdf
848 *
849 * Note that these are not strictly the Adobe names, as a few were used
850 * incorrectly and are kept for backwards compatibility. The latin-1
851 * postscript renderer mangles these.
852 *
853 * Some additional fairly standard fonts for asian scripts are also added.
854 */
855 static struct _legacy_font {
856 gchar* oldname;
857 gchar* newname;
858 DiaFontStyle style; /* the DIA_FONT_FAMILY() is used as falback only */
859 } legacy_fonts[] = {
860 { "AvantGarde-Book", "AvantGarde", DIA_FONT_SERIF },
861 { "AvantGarde-BookOblique", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_OBLIQUE },
862 { "AvantGarde-Demi", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD },
863 { "AvantGarde-DemiOblique", "AvantGarde", DIA_FONT_SERIF | DIA_FONT_OBLIQUE | DIA_FONT_DEMIBOLD },
864 { "Batang", "Batang", DIA_FONT_FAMILY_ANY },
865 { "Bookman-Demi", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD },
866 { "Bookman-DemiItalic", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_DEMIBOLD | DIA_FONT_ITALIC },
867 { "Bookman-Light", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_LIGHT },
868 { "Bookman-LightItalic", "Bookman Old Style", DIA_FONT_SERIF | DIA_FONT_LIGHT | DIA_FONT_ITALIC },
869 { "BousungEG-Light-GB", "BousungEG-Light-GB", DIA_FONT_FAMILY_ANY },
870 { "Courier", "monospace", DIA_FONT_MONOSPACE },
871 { "Courier", "Courier New", DIA_FONT_MONOSPACE },
872 { "Courier-Bold", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_BOLD },
873 { "Courier-Bold", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_BOLD },
874 { "Courier-BoldOblique", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD },
875 { "Courier-BoldOblique", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC | DIA_FONT_BOLD },
876 { "Courier-Oblique", "monospace", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC},
877 { "Courier-Oblique", "Courier New", DIA_FONT_MONOSPACE | DIA_FONT_ITALIC },
878 { "Dotum", "Dotum", DIA_FONT_FAMILY_ANY },
879 { "GBZenKai-Medium", "GBZenKai-Medium", DIA_FONT_FAMILY_ANY },
880 { "GothicBBB-Medium", "GothicBBB-Medium", DIA_FONT_FAMILY_ANY },
881 { "Gulim", "Gulim", DIA_FONT_FAMILY_ANY },
882 { "Headline", "Headline", DIA_FONT_FAMILY_ANY },
883 { "Helvetica", "sans", DIA_FONT_SANS },
884 { "Helvetica", "Arial", DIA_FONT_SANS },
885 { "Helvetica-Bold", "sans", DIA_FONT_SANS | DIA_FONT_BOLD },
886 { "Helvetica-Bold", "Arial", DIA_FONT_SANS | DIA_FONT_BOLD },
887 { "Helvetica-BoldOblique", "sans", DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC },
888 { "Helvetica-BoldOblique", "Arial", DIA_FONT_SANS | DIA_FONT_BOLD | DIA_FONT_ITALIC },
889 { "Helvetica-Narrow", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM },
890 { "Helvetica-Narrow-Bold", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_DEMIBOLD },
891 { "Helvetica-Narrow-BoldOblique", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM | DIA_FONT_OBLIQUE },
892 { "Helvetica-Narrow-Oblique", "Arial Narrow", DIA_FONT_SANS | DIA_FONT_MEDIUM | DIA_FONT_OBLIQUE },
893 { "Helvetica-Oblique", "sans", DIA_FONT_SANS | DIA_FONT_ITALIC },
894 { "Helvetica-Oblique", "Arial", DIA_FONT_SANS | DIA_FONT_ITALIC },
895 { "MOESung-Medium", "MOESung-Medium", DIA_FONT_FAMILY_ANY },
896 { "NewCenturySchoolbook-Bold", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_BOLD },
897 { "NewCenturySchoolbook-BoldItalic", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_BOLD | DIA_FONT_ITALIC },
898 { "NewCenturySchoolbook-Italic", "Century Schoolbook SWA", DIA_FONT_SERIF | DIA_FONT_ITALIC },
899 { "NewCenturySchoolbook-Roman", "Century Schoolbook SWA", DIA_FONT_SERIF },
900 { "Palatino-Bold", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_BOLD },
901 { "Palatino-BoldItalic", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_BOLD | DIA_FONT_ITALIC },
902 { "Palatino-Italic", "Palatino", DIA_FONT_FAMILY_ANY | DIA_FONT_ITALIC },
903 { "Palatino-Roman", "Palatino", DIA_FONT_FAMILY_ANY },
904 { "Ryumin-Light", "Ryumin", DIA_FONT_FAMILY_ANY | DIA_FONT_LIGHT },
905 { "ShanHeiSun-Light", "ShanHeiSun", DIA_FONT_FAMILY_ANY | DIA_FONT_LIGHT },
906 { "Song-Medium", "Song-Medium", DIA_FONT_FAMILY_ANY | DIA_FONT_MEDIUM },
907 { "Symbol", "Symbol", DIA_FONT_SANS | DIA_FONT_MEDIUM },
908 { "Times-Bold", "serif", DIA_FONT_SERIF | DIA_FONT_BOLD },
909 { "Times-Bold", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_BOLD },
910 { "Times-BoldItalic", "serif", DIA_FONT_SERIF | DIA_FONT_ITALIC | DIA_FONT_BOLD },
911 { "Times-BoldItalic", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_ITALIC | DIA_FONT_BOLD },
912 { "Times-Italic", "serif", DIA_FONT_SERIF | DIA_FONT_ITALIC },
913 { "Times-Italic", "Times New Roman", DIA_FONT_SERIF | DIA_FONT_ITALIC },
914 { "Times-Roman", "serif", DIA_FONT_SERIF },
915 { "Times-Roman", "Times New Roman", DIA_FONT_SERIF },
916 { "ZapfChancery-MediumItalic", "Zapf Calligraphic 801 SWA", DIA_FONT_SERIF | DIA_FONT_MEDIUM },
917 { "ZapfDingbats", "Zapf Calligraphic 801 SWA", DIA_FONT_SERIF },
918 { "ZenKai-Medium", "ZenKai", DIA_FONT_FAMILY_ANY | DIA_FONT_MEDIUM },
919 };
920
921
922 /**
923 * Given a legacy name as stored until Dia-0.90 construct
924 * a new DiaFont which is as similar as possible
925 */
926 DiaFont*
dia_font_new_from_legacy_name(const char * name)927 dia_font_new_from_legacy_name(const char* name)
928 {
929 /* do NOT translate anything here !!! */
930 DiaFont* retval;
931 struct _legacy_font* found = NULL;
932 real height = 1.0;
933 int i;
934
935 for (i = 0; i < G_N_ELEMENTS(legacy_fonts); i++) {
936 if (!strcmp(name, legacy_fonts[i].oldname)) {
937 found = &legacy_fonts[i];
938 break;
939 }
940 }
941 if (found) {
942 retval = dia_font_new (found->newname, found->style, height);
943 retval->legacy_name = found->oldname;
944 } else {
945 /* We tried our best, let Pango complain */
946 retval = dia_font_new (name, DIA_FONT_WEIGHT_NORMAL, height);
947 retval->legacy_name = NULL;
948 }
949
950 return retval;
951 }
952
953 G_CONST_RETURN char*
dia_font_get_legacy_name(const DiaFont * font)954 dia_font_get_legacy_name(const DiaFont *font)
955 {
956 const char* matched_name = NULL;
957 const char* family;
958 DiaFontStyle style;
959 int i;
960
961 /* if we have loaded it from an old file, use the old name */
962 if (font->legacy_name)
963 return font->legacy_name;
964
965 family = dia_font_get_family (font);
966 style = dia_font_get_style (font);
967 for (i = 0; i < G_N_ELEMENTS(legacy_fonts); i++) {
968 if (0 == g_ascii_strcasecmp (legacy_fonts[i].newname, family)) {
969 /* match weight and slant */
970 DiaFontStyle st = legacy_fonts[i].style;
971 if ((DIA_FONT_STYLE_GET_SLANT(style) | DIA_FONT_STYLE_GET_WEIGHT(style))
972 == (DIA_FONT_STYLE_GET_SLANT(st) | DIA_FONT_STYLE_GET_WEIGHT(st))) {
973 return legacy_fonts[i].oldname; /* exact match */
974 } else if (0 == (DIA_FONT_STYLE_GET_SLANT(st) | DIA_FONT_STYLE_GET_WEIGHT(st))) {
975 matched_name = legacy_fonts[i].oldname;
976 /* 'unmodified' font, continue matching */
977 }
978 }
979 }
980 return matched_name ? matched_name : "Courier";
981 }
982
983