1 /* gtkpsfont - PostScript Fonts handling
2  * Copyright 1999-2006  Adrian E. Feiguin <feiguin@ifir.edu.ar>
3  *
4  * Some code borrowed from
5  * DiaCanvas -- a technical canvas widget
6  * Copyright (C) 1999 Arjan Molenaar
7  * Dia -- an diagram creation/manipulation program
8  * Copyright (C) 1998 Alexander Larsson
9  *
10  * and Xfig
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27 
28 /**
29  * SECTION: gtkpsfont
30  * @short_description: PostScript Fonts handling.
31  *
32  *  Handles Postscript fonts.
33  */
34 
35 
36 #include <gtk/gtk.h>
37 #include <stdio.h>
38 #include <string.h>
39 #ifndef G_PLATFORM_WIN32
40 #include <fontconfig/fontconfig.h>
41 #endif //G_PLATFORM_WIN32
42 #include <pango/pango.h>
43 #include "gtkpsfont.h"
44 
45 #define FONTCACHE_SIZE 17
46 #define NUM_X11_FONTS 2
47 
48 
49 #if defined __GNUC__ && 7 <= __GNUC__
50 # pragma GCC diagnostic push
51 # pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
52 #endif
53 static GtkPSFont font_data[] =
54 {
55   { "Times-Roman",
56     "Times-Roman",
57     "Times-Roman",
58     "Nimbus Roman No9 L, Regular",
59     NULL,
60     FALSE, FALSE
61   },
62   { "Times-Italic",
63     "Times-Italic",
64     "Times-Roman",
65     "Nimbus Roman No9 L, Italic",
66     NULL,
67     TRUE, FALSE
68   },
69   { "Times-Bold",
70     "Times-Bold",
71     "Times-Roman",
72     "Nimbus Roman No9 L, Bold",
73     NULL,
74     FALSE, TRUE
75   },
76   { "Times-BoldItalic",
77     "Times-BoldItalic",
78     "Times-Roman",
79     "Nimbus Roman No9 L, Bold Italic",
80     NULL,
81     TRUE, TRUE
82   },
83   { "AvantGarde-Book",
84     "AvantGarde-Book",
85     "AvantGarde",
86     "URW Gothic L, Book",
87     NULL,
88     FALSE, FALSE
89   },
90   { "AvantGarde-BookOblique",
91     "AvantGarde-BookOblique",
92     "AvantGarde",
93     "URW Gothic L, Book Oblique",
94     NULL,
95     TRUE, FALSE
96   },
97   { "AvantGarde-Demi",
98     "AvantGarde-Demi",
99     "AvantGarde",
100     "URW Gothic L, Demi",
101     NULL,
102     FALSE, TRUE
103   },
104   { "AvantGarde-DemiOblique",
105     "AvantGarde-DemiOblique",
106     "AvantGarde",
107     "URW Gothic L, Demi Oblique",
108     NULL,
109     TRUE, TRUE
110   },
111   { "Bookman-Light",
112     "Bookman-Light",
113     "Bookman",
114     "URW Bookman L, Light",
115     NULL,
116     FALSE, FALSE
117   },
118   { "Bookman-LightItalic",
119     "Bookman-LightItalic",
120     "Bookman",
121     "URW Bookman L, Light Italic",
122     NULL,
123     TRUE, FALSE
124   },
125   { "Bookman-Demi",
126     "Bookman-Demi",
127     "Bookman",
128     "URW Bookman L, Demi Bold",
129     NULL,
130     FALSE, TRUE
131   },
132   { "Bookman-DemiItalic",
133     "Bookman-DemiItalic",
134     "Bookman",
135     "URW Bookman L, Demi Bold Italic",
136     NULL,
137     TRUE, TRUE
138   },
139   { "Courier",
140     "Courier",
141     "Courier",
142     "Nimbus Mono L, Regular",
143     NULL,
144     FALSE, FALSE
145   },
146   { "Courier-Oblique",
147     "Courier-Oblique",
148     "Courier",
149     "Nimbus Mono L, Regular Oblique",
150     NULL,
151     TRUE, FALSE
152   },
153   { "Courier-Bold",
154     "Courier-Bold",
155     "Courier",
156     "Nimbus Mono L, Bold",
157     NULL,
158     FALSE, TRUE
159   },
160   { "Courier-BoldOblique",
161     "Courier-BoldOblique",
162     "Courier",
163     "Nimbus Mono L, Bold Oblique",
164     NULL,
165     TRUE, TRUE
166   },
167   { "Helvetica",
168     "Helvetica",
169     "Helvetica",
170     "Sans, Regular",
171     NULL,
172     FALSE, FALSE
173   },
174   { "Helvetica-Oblique",
175     "Helvetica-Oblique",
176     "Helvetica",
177     "Sans, Regular Italic",
178     NULL,
179     TRUE, FALSE
180   },
181   { "Helvetica-Bold",
182     "Helvetica-Bold",
183     "Helvetica",
184     "Sans, Bold",
185     NULL,
186     FALSE, TRUE
187   },
188   { "Helvetica-BoldOblique",
189     "Helvetica-BoldOblique",
190     "Helvetica",
191     "Sans, Bold Italic",
192     NULL,
193     TRUE, TRUE
194   },
195   { "Helvetica-Narrow",
196     "Helvetica-Narrow",
197     "Helvetica-Narrow",
198     "Sans, Regular Condensed",
199     NULL,
200     FALSE, FALSE
201   },
202   { "Helvetica-Narrow-Oblique",
203     "Helvetica-Narrow-Oblique",
204     "Helvetica-Narrow",
205     "Sans, Regular Condensed Italic",
206     NULL,
207     TRUE, FALSE
208   },
209   { "Helvetica-Narrow-Bold",
210     "Helvetica-Narrow-Bold",
211     "Helvetica-Narrow",
212     "Sans, Bold Condensed",
213     NULL,
214     FALSE, TRUE
215   },
216   { "Helvetica-Narrow-BoldOblique",
217     "Helvetica-Narrow-BoldOblique",
218     "Helvetica-Narrow",
219     "Sans, Bold Condensed Italic",
220     NULL,
221     TRUE, TRUE
222   },
223   { "NewCenturySchoolbook-Roman",
224     "NewCenturySchlbk-Roman",
225     "NewCenturySchlbk",
226     "Century Schoolbook L, Roman",
227     NULL,
228     FALSE, FALSE
229   },
230   { "NewCenturySchoolbook-Italic",
231     "NewCenturySchlbk-Italic",
232     "NewCenturySchlbk",
233     "Century Schoolbook L, Italic",
234     NULL,
235     TRUE, FALSE
236   },
237   { "NewCenturySchoolbook-Bold",
238     "NewCenturySchlbk-Bold",
239     "NewCenturySchlbk",
240     "Century Schoolbook L, Bold",
241     NULL,
242     FALSE, TRUE
243   },
244   { "NewCenturySchoolbook-BoldItalic",
245     "NewCenturySchlbk-BoldItalic",
246     "NewCenturySchlbk",
247     "Century Schoolbook L, Bold Italic",
248     NULL,
249     TRUE, TRUE
250   },
251   { "Palatino-Roman",
252     "Palatino-Roman",
253     "Palatino",
254     "URW Palladio L, Roman",
255     NULL,
256     FALSE, FALSE
257   },
258   { "Palatino-Italic",
259     "Palatino-Italic",
260     "Palatino",
261     "URW Palladio L, Italic",
262     NULL,
263     TRUE, FALSE
264   },
265   { "Palatino-Bold",
266     "Palatino-Bold",
267     "Palatino",
268     "URW Palladio L, Bold",
269     NULL,
270     FALSE, TRUE
271   },
272   { "Palatino-BoldItalic",
273     "Palatino-BoldItalic",
274     "Palatino",
275     "URW Palladio L, Bold Italic",
276     NULL,
277     TRUE, TRUE
278   },
279   { "Symbol",
280     "Symbol",
281     "Symbol",
282     "Standard Symbols L, Regular",
283     NULL,
284     FALSE, FALSE
285   },
286   { "ZapfChancery-MediumItalic",
287     "ZapfChancery-MediumItalic",
288     "ZapfChancery",
289     "URW Chancery L, Medium Italic",
290     NULL,
291     FALSE, FALSE
292   },
293   { "ZapfDingbats",
294     "ZapfDingbats",
295     "ZapfDingbats",
296     "Dingbats, Regular",
297     NULL,
298     FALSE, FALSE
299   },
300 };
301 #if defined __GNUC__ && 7 <= __GNUC__
302 # pragma GCC diagnostic pop
303 #endif
304 
305 
306 #define NUM_FONTS (sizeof(font_data)/sizeof(GtkPSFont))
307 
308 static GList *user_fonts;
309 static gboolean psfont_init = FALSE;
310 static GList *psfont_families;
311 static gint numf;
312 static gint psfont_refcount = 0;
313 
314 #ifdef G_PLATFORM_WIN32
315 static const char *default_font = "sans";
316 #else
317 static const char *default_font = "fixed";
318 #endif
319 
320 static GtkPSFont *find_psfont		(const gchar *name);
321 
322 gint
gtk_psfont_init(void)323 gtk_psfont_init(void)
324 {
325   GtkPSFont *data = NULL;
326   GList *fonts;
327   gint i, j;
328   gboolean new_family = TRUE;
329 
330   psfont_refcount++;
331 
332 /*  if(psfont_refcount > 1) printf("PS fonts already initilized\n");;
333 */
334   if(psfont_refcount > 1) return FALSE;
335 
336 /*  printf("Initializing PS fonts\n");;
337 */
338 
339   psfont_init = TRUE;
340   psfont_families = NULL;
341   numf = 0;
342 
343   for(i = 0; i < (int)NUM_FONTS; i++){
344     new_family = TRUE;
345     for(j = 0; j < numf; j++){
346        if(strcmp(font_data[i].family, (gchar *)g_list_nth_data(psfont_families, j)) == 0)
347          new_family = FALSE;
348     }
349     if(new_family){
350          numf = numf + 1;
351          psfont_families = g_list_append(psfont_families, font_data[i].family);
352     }
353   }
354 
355   fonts = user_fonts;
356   while(fonts){
357     data = (GtkPSFont *) fonts->data;
358     new_family = TRUE;
359     for(j = 0; j < numf; j++){
360        if(strcmp(data->family, (gchar *)g_list_nth_data(psfont_families, j)) == 0)
361          new_family = FALSE;
362     }
363     if(new_family){
364          numf = numf + 1;
365          psfont_families = g_list_append(psfont_families, data->family);
366     }
367     fonts = fonts->next;
368   }
369 
370   return TRUE;
371 }
372 
373 /**
374  * gtk_psfont_unref:
375  *
376  * Unref ps fonts.
377  */
378 void
gtk_psfont_unref(void)379 gtk_psfont_unref(void)
380 {
381   GList *list;
382 
383   if(psfont_refcount <= 0) return;
384 
385   psfont_refcount--;
386 
387   if(psfont_refcount > 0) return;
388 
389   list = psfont_families;
390   while(list){
391     psfont_families = g_list_remove_link(psfont_families, list);
392     g_list_free_1(list);
393     list = psfont_families;
394   }
395 
396   list = user_fonts;
397   while(list){
398     user_fonts = g_list_remove_link(user_fonts, list);
399     g_list_free_1(list);
400     list = user_fonts;
401   }
402 
403   psfont_init = FALSE;
404 }
405 
406 /*
407  * gtk_psfont_init:
408  *
409  * Unref ps fonts.
410  *
411 void
412 gtk_psfont_unref()
413 {
414   GList *list;
415 
416   if(psfont_refcount <= 0) return;
417 
418   psfont_refcount--;
419 
420   if(psfont_refcount > 0) return;
421 
422   list = psfont_families;
423   while(list){
424     psfont_families = g_list_remove_link(psfont_families, list);
425     g_list_free_1(list);
426     list = psfont_families;
427   }
428 */
429 
430 /**
431  * gtk_psfont_get_by_name:
432  * @name: font name
433  *
434  * Get PS Font by font name.
435  *
436  * Returns: (transfer none) a #GtkPSFont pointer.
437  */
438 GtkPSFont *
gtk_psfont_get_by_name(const gchar * name)439 gtk_psfont_get_by_name(const gchar *name)
440 {
441   GtkPSFont *font;
442 
443   font = find_psfont(name);
444 
445   if (font == NULL) {
446     font = find_psfont(default_font);
447     if (font == NULL) {
448       g_warning ("Error, couldn't locate default font. Shouldn't happen.");
449     } else {
450       g_message ("Postscript font %s not found, using %s instead.",
451 		 name, default_font);
452     }
453   }
454 
455   return font;
456 }
457 
458 /**
459  * gtk_psfont_get_font_description:
460  * @font: a #GtkPSFont
461  * @height: font height
462  *
463  * Get a #PangoDescriptionFont from PS Font.
464  *
465  * Returns: a #PangoFontDescription pointer.
466  */
467 PangoFontDescription *
gtk_psfont_get_font_description(GtkPSFont * font,gint height)468 gtk_psfont_get_font_description(GtkPSFont *font, gint height)
469 {
470   PangoFontDescription *font_desc;
471   gchar *font_string;
472   GtkSettings *settings = gtk_settings_get_for_screen(gdk_screen_get_default());
473   GObjectClass *klass;
474   gdouble dpi;
475 
476   g_return_val_if_fail (font != NULL, NULL);
477 
478   if (height <= 0) height = 1;
479 
480 /* Dirty hack to get the correct font size for this device
481 http://mail.gnome.org/archives/gtk-i18n-list/2003-August/msg00001.html
482 */
483 
484   klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings));
485 /* Check that the properties we're looking at are defined. */
486   if (!g_object_class_find_property(klass, "gtk-xft-dpi")) {
487     dpi = 96.;
488   } else {
489     /* Read the settings. */
490     gint int_dpi;
491     g_object_get(G_OBJECT(settings),
492     	         "gtk-xft-dpi", &int_dpi,
493 	         NULL);
494     if(int_dpi <= 0)
495       dpi = 96;
496     else
497       dpi = int_dpi / PANGO_SCALE;
498   }
499 
500 /*
501 {
502     GdkScreen *screen = gdk_screen_get_default ();
503     FcPattern *pattern;
504 
505     pattern = FcPatternCreate();
506     if (pattern)
507       {
508         XftDefaultSubstitute (GDK_SCREEN_XDISPLAY (screen),
509                               GDK_SCREEN_XNUMBER (screen),
510                               pattern);
511         FcPatternGetDouble (pattern, FC_DPI, 0, &dpi);
512         FcPatternDestroy (pattern);
513       }
514 }
515 */
516   height *= 75./dpi;
517 
518   font_string = g_strdup_printf("%s %i", font->pango_description, height);
519   font_desc = pango_font_description_from_string(font_string);
520   g_free(font_string);
521 
522   if (!font_desc) {
523     font_string = g_strdup_printf("%s %i", default_font, height);
524     font_desc = pango_font_description_from_string(font_string);
525     g_free(font_string);
526     if (font_desc)
527       g_message ("Font %s not describable, using %s instead.",
528 		 font->fontname, default_font);
529     else
530       g_warning ("Error, couldn't describe default font. Shouldn't happen.");
531   }
532 
533   /* Loading via the pango fontset facility means that pango.aliases is used */
534 /* This is not working */
535 /* It is screwing up the whole thing */
536 /*
537   if (font_desc) {
538     PangoContext *context = gdk_pango_context_get();
539     PangoFontset *pffontset;
540     PangoFont *pffont;
541 
542     pffontset = pango_context_load_fontset(context, font_desc,
543     pango_context_get_language(context));
544     if (pffontset) {
545       pffont = pango_fontset_get_font(pffontset, g_utf8_get_char(" "));
546       if (pffont) {
547         PangoFontDescription *font_desc;
548 	desc = pango_font_describe(pffont);
549 	g_object_unref(G_OBJECT(pffont));
550 	if (desc) {
551 	  pango_font_description_free(font_desc);
552 	  font_desc = desc;
553 	}
554       }
555       g_object_unref(G_OBJECT(pffontset));
556     }
557   }
558 */
559   return font_desc;
560 }
561 
562 
563 /**
564  * gtk_psfont_get_psfontname:
565  * @psfont: a #GtkPSFont
566  *
567  * Get font name from PS Font.
568  *
569  * Returns: font name.
570  */
571 const gchar *
gtk_psfont_get_psfontname(GtkPSFont * psfont)572 gtk_psfont_get_psfontname(GtkPSFont *psfont)
573 {
574 
575   g_return_val_if_fail (psfont != NULL, NULL);
576 
577   return psfont->psname;
578 }
579 
580 /**
581  * gtk_psfont_add_font:
582  * @fontname: font name
583  * @psname: PS font name
584  * @family: font family
585  * @pango_description: font Pango description
586  * @italic: TRUE or FALSE
587  * @bold: TRUE or FALSE
588  *
589  * Add font in user font list.
590  */
591 void
gtk_psfont_add_font(const gchar * fontname,const gchar * psname,const gchar * family,const gchar * pango_description,gboolean italic,gboolean bold)592 gtk_psfont_add_font (const gchar *fontname,
593                      const gchar *psname, const gchar *family,
594                      const gchar *pango_description,
595                      gboolean italic, gboolean bold)
596 {
597   GtkPSFont *font;
598 
599   font = g_new0(GtkPSFont, 1);
600 
601   font->fontname = g_strdup(fontname);
602   font->psname = g_strdup(psname);
603   font->family = g_strdup(family);
604   font->pango_description = g_strdup(pango_description);
605   font->i18n_latinfamily = NULL;
606   font->italic = italic;
607   font->bold = bold;
608   font->vertical = FALSE;
609 
610   user_fonts = g_list_append(user_fonts, font);
611 }
612 
613 /**
614  * gtk_psfont_add_i18n_font:
615  * @fontname: font name
616  * @psname: PS font name
617  * @family: font family
618  * @i18n_latinfamily: International font family
619  * @pango_description: font Pango description
620  * @italic: TRUE or FALSE
621  * @bold: TRUE or FALSE
622  * @vertical: TRUE or FALSE
623  *
624  * Add international font in user font list.
625  */
626 void
gtk_psfont_add_i18n_font(const gchar * fontname,const gchar * psname,const gchar * family,const gchar * i18n_latinfamily,const gchar * pango_description,gboolean italic,gboolean bold,gboolean vertical)627 gtk_psfont_add_i18n_font (const gchar *fontname,
628                          const gchar *psname, const gchar *family,
629                          const gchar *i18n_latinfamily,
630                          const gchar *pango_description,
631                          gboolean italic, gboolean bold, gboolean vertical)
632 {
633   GtkPSFont *font;
634 
635   font = g_new0(GtkPSFont, 1);
636 
637   font->fontname = g_strdup(fontname);
638   font->psname = g_strdup(psname);
639   font->family = g_strdup(family);
640   font->pango_description = g_strdup(pango_description);
641   font->i18n_latinfamily = g_strdup(i18n_latinfamily);
642   font->italic = italic;
643   font->bold = bold;
644   font->vertical = vertical;
645 
646   user_fonts = g_list_append(user_fonts, font);
647 }
648 
649 static GtkPSFont *
find_psfont(const gchar * name)650 find_psfont(const gchar *name)
651 {
652   GtkPSFont *fontdata = NULL;
653   GtkPSFont *data = NULL;
654   GList *fonts;
655   gint i;
656 
657   /* user_fonts should be superior to font_data, the built-in default
658      settings because user_fonts is supposed to store a more appropriate
659      existent xfont than font_data.
660   */
661 
662   fonts = user_fonts;
663   while(fonts){
664     data = (GtkPSFont *) fonts->data;
665     if(strcmp(name, data->fontname) == 0) {
666       fontdata = data;
667       break;
668     }
669     if(strcmp(name, data->psname) == 0) {
670       fontdata = data;
671       break;
672     }
673     fonts = fonts->next;
674   }
675 
676   if(fontdata == NULL) {
677     for(i = 0; i < (int)NUM_FONTS; i++){
678       if(strcmp(name, font_data[i].fontname) == 0) {
679 	fontdata = &font_data[i];
680 	break;
681       }
682       if(strcmp(name, font_data[i].psname) == 0) {
683 	fontdata = &font_data[i];
684        break;
685       }
686     }
687   }
688 
689   return fontdata;
690 }
691 
692 /**
693  * gtk_psfont_get_by_family:
694  * @family_name: font name
695  * @italic: TRUE or FALSE
696  * @bold: TRUE or FALSE
697  *
698  * Get #GtkPSFont by family.
699  *
700  * Returns: (transfer none) the #GtkPSFont
701  */
702 GtkPSFont *
gtk_psfont_get_by_family(const gchar * family_name,gboolean italic,gboolean bold)703 gtk_psfont_get_by_family(const gchar *family_name, gboolean italic, gboolean bold)
704 {
705   GtkPSFont *fontdata = NULL;
706   GtkPSFont *data = NULL;
707   GtkPSFont *return_data = NULL;
708   GList *fonts;
709   gint i;
710 
711   /* user_fonts should be superior to font_data, the built-in default
712      settings because user_fonts is supposed to store a more appropriate
713      existent xfont than font_data.
714   */
715 
716   fonts = user_fonts;
717   while(fonts){
718     data = (GtkPSFont *) fonts->data;
719     if(strcmp(family_name, data->family) == 0) {
720       return_data = data;
721       if(data->italic == italic && data->bold == bold){
722 	fontdata = data;
723 	break;
724       }
725     }
726     fonts = fonts->next;
727   }
728 
729   if(fontdata == NULL) {
730     for(i = 0; i < (int)NUM_FONTS; i++){
731       if(strcmp(family_name, font_data[i].family) == 0) {
732 	return_data = &font_data[i];
733 	if(font_data[i].italic == italic && font_data[i].bold == bold){
734 	  fontdata = &font_data[i];
735 	  break;
736 	}
737       }
738     }
739   }
740 
741   if(!fontdata) fontdata = return_data;
742   return fontdata;
743 }
744 
745 
746 /**
747  * gtk_psfont_get_families:
748  * @families:  (element-type gchar*) font families
749  * @num_families: families number
750  *
751  * Get #GtkPSFont by family.
752  */
753 void
gtk_psfont_get_families(GList ** families,gint * num_families)754 gtk_psfont_get_families(GList **families, gint *num_families)
755 {
756   if(!psfont_init || psfont_refcount == 0){
757     g_warning("PS fonts have not been initialized. Use gtk_psfont_init first.");
758     return;
759   }
760 
761   *families = psfont_families;
762   *num_families = numf;
763 }
764