1/* $Id$Revision: */
2/* vim:set shiftwidth=4 ts=8: */
3
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * http://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: See CVS logs. Details at http://www.graphviz.org/
12 *************************************************************************/
13
14#include "config.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <ctype.h>
20
21/* FIXME - the following declaration should be removed
22 * when configure is coordinated with flags passed to the
23 * compiler. On Linux, strcasestr is defined but needs a special
24 * preprocessor constant to be defined. Configure sets the
25 * HAVE_STRCASESTR, but the flag is not used during compilation,
26 * so strcasestr is undeclared.
27 */
28char* strcasestr (const char *str, const char *pat);
29#ifndef HAVE_STRCASESTR
30char* strcasestr (const char *str, const char *pat)
31{
32 int slen, plen;
33 char p0, pc;
34 const char *endp, *sp, *pp;
35 if (!(p0 = *pat)) return (char*)str;
36 plen = strlen (pat++);
37 slen = strlen (str);
38 if (slen < plen) return NULL;
39 endp = str + slen - plen;
40 p0 = toupper (p0);
41 do {
42 while ((str <= endp) && (p0 != toupper(*str))) str++;
43 if (str > endp) return NULL;
44 pp = pat;
45 sp = ++str;
46 while ((pc = *pp++) && (toupper(pc) == toupper(*sp))) sp++;
47 } while (pc);
48 return (char*)(str-1);
49}
50
51#endif
52
53#include "agxbuf.h"
54#include "gvplugin_textlayout.h"
55#include <pango/pangocairo.h>
56#include "gvgetfontlist.h"
57
58extern unsigned char Verbose;
59
60#define FNT_BOLD 1<<0
61#define FNT_BOOK 1<<1
62#define FNT_CONDENSED 1<<2
63#define FNT_DEMI 1<<3
64#define FNT_EXTRALIGHT 1<<4
65#define FNT_ITALIC 1<<5
66#define FNT_LIGHT 1<<6
67#define FNT_MEDIUM 1<<7
68#define FNT_OBLIQUE 1<<8
69#define FNT_REGULAR 1<<9
70#define FNT_ROMAN 1<<9
71
72#define PS_AVANTGARDE "AvantGarde"
73#define PS_BOOKMAN "Bookman"
74#define PS_COURIER "Courier"
75#define PS_HELVETICA SAN_5
76#define PS_NEWCENTURYSCHLBK "NewCenturySchlbk"
77#define PS_PALATINO "Palatino"
78#define PS_SYMBOL "Symbol"
79#define PS_TIMES SER_3
80#define PS_CHANCERY "ZapfChancery"
81#define PS_DINGBATS "ZapfDingbats"
82
83#define FNT_BOLD_ST "BOLD"
84#define FNT_BOOK_ST "BOOK"
85#define FNT_CONDENSED_ST "CONDENSED"
86#define FNT_DEMI_ST "DEMI"
87#define FNT_EXTRALIGHT_ST "EXTRALIGHT"
88#define FNT_ITALIC_ST "ITALIC"
89#define FNT_LIGHT_ST "LIGHT"
90#define FNT_MEDIUM_ST "MEDIUM"
91#define FNT_OBLIQUE_ST "OBLIQUE"
92#define FNT_REGULAR_ST "REGULAR"
93#define FNT_ROMAN_ST "ROMAN"
94
95#define SAN_0 "sans"
96#define SAN_1 "URW Gothic L"
97#define SAN_2 "Charcoal"
98#define SAN_3 "Nimbus Sans L"
99#define SAN_4 "Verdana"
100#define SAN_5 "Helvetica"
101#define SAN_6 "Bitstream Vera Sans"
102#define SAN_7 "DejaVu Sans"
103#define SAN_8 "Liberation Sans"
104#define SAN_9 "Luxi Sans"
105#define SAN_10 "FreeSans"
106#define SAN_11 "Arial"
107
108#define SER_0 "serif"
109#define SER_1 "URW Bookman L"
110#define SER_2 "Times New Roman"
111#define SER_3 "Times"
112#define SER_4 "Nimbus Roman No9 L"
113#define SER_5 "Bitstream Vera Serif"
114#define SER_6 "DejaVu Serif"
115#define SER_7 "Liberation Serif"
116#define SER_8 "Luxi Serif"
117#define SER_9 "FreeSerif"
118#define SER_10 "Century Schoolbook L"
119#define SER_11 "Charcoal"
120#define SER_12 "Georgia"
121#define SER_13 "URW Palladio L"
122#define SER_14 "Norasi"
123#define SER_15 "Rekha"
124#define SER_16 "URW Chancery L"
125
126#define MON_0 "monospace"
127#define MON_1 "Nimbus Mono L"
128#define MON_2 "Inconsolata"
129#define MON_3 "Courier New"
130#define MON_4 "Bitstream Vera Sans Mono"
131#define MON_5 "DejaVu Sans Mono"
132#define MON_6 "Liberation Mono"
133#define MON_7 "Luxi Mono"
134#define MON_8 "FreeMono"
135
136#define SYM_0 "fantasy"
137#define SYM_1 "Impact"
138#define SYM_2 "Copperplate Gothic Std"
139#define SYM_3 "Cooper Std"
140#define SYM_4 "Bauhaus Std"
141
142#define DING_0 "fantasy"
143#define DING_1 "Dingbats"
144#define DING_2 "Impact"
145#define DING_3 "Copperplate Gothic Std"
146#define DING_4 "Cooper Std"
147#define DING_5 "Bauhaus Std"
148
149
150typedef struct {
151 int flag;
152 char* name;
153} face_t;
154static face_t facelist[] = {
155 { FNT_BOLD, FNT_BOLD_ST},
156 { FNT_BOOK, FNT_BOOK_ST},
157 { FNT_CONDENSED, FNT_CONDENSED_ST},
158 { FNT_DEMI, FNT_DEMI_ST},
159 { FNT_EXTRALIGHT, FNT_EXTRALIGHT_ST},
160 { FNT_ITALIC, FNT_ITALIC_ST},
161 { FNT_LIGHT, FNT_LIGHT_ST},
162 { FNT_MEDIUM, FNT_MEDIUM_ST},
163 { FNT_OBLIQUE, FNT_OBLIQUE_ST},
164 { FNT_REGULAR, FNT_REGULAR_ST},
165 { FNT_ROMAN, FNT_ROMAN_ST},
166};
167#define FACELIST_SZ (sizeof(facelist)/sizeof(face_t))
168
169/* This is where the hierarchy of equivalent fonts is established. The order can be changed
170 here or new equivalent fonts can be added here. Each font family used by the Graphviz
171 PS fonts is set up.
172*/
173static const char *PS_AVANT_E[] = {
174 SAN_1, SAN_2, SAN_3, SAN_4, SAN_5, SAN_6, SAN_7, SAN_8, SAN_9, SAN_10
175};
176#define PS_AVANT_E_SZ (sizeof(PS_AVANT_E) / sizeof(char *))
177
178static const char *PS_BOOKMAN_E[] = {
179 SER_1, SER_2, SER_3, SER_4, SER_5, SER_6, SER_7, SER_8, SER_9
180};
181#define PS_BOOKMAN_E_SZ (sizeof(PS_BOOKMAN_E) / sizeof(char *))
182
183static const char *PS_COURIER_E[] = {
184 MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8
185};
186#define PS_COURIER_E_SZ (sizeof(PS_COURIER_E) / sizeof(char *))
187
188static const char *PS_HELVETICA_E[] = {
189 SAN_3, SAN_11, SAN_4, SAN_6, SAN_7, SAN_8, SAN_9, SAN_10
190};
191#define PS_HELVETICA_E_SZ (sizeof(PS_HELVETICA_E) / sizeof(char *))
192
193static const char *PS_NEWCENT_E[] = {
194 SER_10, SER_2, SER_3, SER_4, SER_12, SER_5, SER_6, SER_7, SER_8, SER_9
195};
196#define PS_NEWCENT_E_SZ (sizeof(PS_NEWCENT_E) / sizeof(char *))
197
198static const char *PS_PALATINO_E[] = {
199 SER_13, SER_2, SER_3, SER_4, SER_14, SER_15, SER_5, SER_6, SER_7, SER_8, SER_9
200};
201#define PS_PALATINO_E_SZ (sizeof(PS_PALATINO_E) / sizeof(char *))
202
203static const char *PS_TIMES_E[] = {
204 SER_4, SER_2, SER_11, SER_5, SER_6, SER_7, SER_8, SER_9
205};
206#define PS_TIMES_E_SZ (sizeof(PS_TIMES_E) / sizeof(char *))
207
208static const char *PS_SYMBOL_E[] = { SYM_1, SYM_2, SYM_3, SYM_4 };
209#define PS_SYMBOL_E_SZ (sizeof(PS_SYMBOL_E) / sizeof(char *))
210
211static const char *PS_CHANCERY_E[] = {
212 SER_16, SER_11, SER_2, SER_3, SER_4, SER_5, SER_6, SER_7, SER_8, SER_9
213};
214#define PS_CHANCERY_E_SZ (sizeof(PS_CHANCERY_E) / sizeof(char *))
215
216static const char *PS_DINGBATS_E[] = { DING_1, SYM_1, SYM_2, SYM_3, SYM_4 };
217#define PS_DINGBATS_E_SZ (sizeof(PS_DINGBATS_E) / sizeof(char *))
218
219typedef struct {
220 char *generic_name;
221 char *fontname;
222 int eq_sz;
223 const char **equiv;
224} fontdef_t;
225
226/* array of recognized Graphviz PS font names */
227static fontdef_t gv_ps_fontdefs[] = {
228 { SAN_0, PS_AVANTGARDE, PS_AVANT_E_SZ, PS_AVANT_E},
229 { SER_0, PS_BOOKMAN, PS_BOOKMAN_E_SZ, PS_BOOKMAN_E},
230 { MON_0, PS_COURIER, PS_COURIER_E_SZ, PS_COURIER_E},
231 { SAN_0, PS_HELVETICA, PS_HELVETICA_E_SZ, PS_HELVETICA_E},
232 { SER_0, PS_NEWCENTURYSCHLBK, PS_NEWCENT_E_SZ, PS_NEWCENT_E},
233 { SER_0, PS_PALATINO, PS_PALATINO_E_SZ, PS_PALATINO_E},
234 { SYM_0, PS_SYMBOL, PS_SYMBOL_E_SZ, PS_SYMBOL_E},
235 { SER_0, PS_TIMES, PS_TIMES_E_SZ, PS_TIMES_E},
236 { SER_0, PS_CHANCERY, PS_CHANCERY_E_SZ, PS_CHANCERY_E},
237 { DING_0, PS_DINGBATS, PS_DINGBATS_E_SZ, PS_DINGBATS_E},
238};
239#define GV_FONT_LIST_SIZE (sizeof(gv_ps_fontdefs)/sizeof(fontdef_t))
240
241typedef struct {
242 char *gv_ps_fontname;
243 char *fontname;
244 int faces;
245} availfont_t;
246
247#define NEW(t) (t*)malloc(sizeof(t))
248#define N_NEW(n,t) (t*)malloc((n)*sizeof(t))
249
250static PostscriptAlias postscript_alias[] = {
251#include "ps_font_equiv.h"
252};
253
254/* Frees memory used by the available system font definitions */
255static void gv_flist_free_af(availfont_t* gv_af_p)
256{
257 int i;
258
259 for (i = 0; i < GV_FONT_LIST_SIZE; i++) {
260 if (gv_af_p[i].fontname)
261 free(gv_af_p[i].fontname);
262 }
263 free(gv_af_p);
264}
265
266static int get_faces(PangoFontFamily * family)
267{
268 PangoFontFace **faces;
269 PangoFontFace *face;
270 int i, j, n_faces;
271 const char *name;
272 int availfaces = 0;
273 /* Get the faces (Bold, Italic, etc.) for the current font family */
274 pango_font_family_list_faces(family, &faces, &n_faces);
275 for (i = 0; i < n_faces; i++) {
276 face = faces[i];
277 name = pango_font_face_get_face_name(face);
278
279 /* if the family face type is one of the known types, logically OR the known type value
280 to the available faces integer */
281 for (j = 0; j < FACELIST_SZ; j++) {
282 if (strcasestr(name, facelist[j].name)) {
283 availfaces |= facelist[j].flag;
284 break;
285 }
286 }
287 }
288 g_free(faces);
289 return availfaces;
290}
291
292#ifdef DEBUG
293static void
294display_available_fonts(availfont_t* gv_af_p)
295{
296 int i, j, faces;
297
298/* Displays the Graphviz PS font name, system available font name and associated faces */
299 for (j = 0; j < GV_FONT_LIST_SIZE; j++) {
300 if ((gv_af_p[j].faces == 0) || (gv_af_p[j].fontname == NULL)) {
301 fprintf (stderr, "ps font = %s not available\n", gv_ps_fontdefs[j].fontname);
302 continue;
303 }
304 fprintf (stderr, "ps font = %s available %d font = %s\n",
305 gv_ps_fontdefs[j].fontname, gv_af_p[j].faces, gv_af_p[j].fontname);
306 faces = gv_af_p[j].faces;
307 for (i = 0; i < FACELIST_SZ; i++) {
308 if (faces & facelist[i].flag)
309 fprintf (stderr, "\t%s\n", facelist[i].name);
310 }
311 }
312}
313#endif
314
315/* Construct the list of font faces */
316static char *get_avail_faces(int faces, agxbuf* xb)
317{
318 int i;
319 for (i = 0; i < FACELIST_SZ; i++) {
320 if (faces & facelist[i].flag) {
321 agxbput (xb, facelist[i].name);
322 agxbputc(xb, ' ');
323 }
324 }
325 return agxbuse (xb);
326}
327
328
329/* This function creates an array of font definitions. Each entry corresponds to one of
330 the Graphviz PS fonts. The font definitions contain the generic font name and a list
331 of equivalent fonts that can be used in place of the PS font if the PS font is not
332 available on the system
333*/
334static availfont_t *gv_get_ps_fontlist(PangoFontMap * fontmap)
335{
336 PangoFontFamily **families;
337 PangoFontFamily *family;
338 fontdef_t* gv_ps_fontdef;
339 int n_families;
340 int i, j, k, array_sz, availfaces;
341 availfont_t *gv_af_p, *gv_afs;
342 const char *name;
343 char *family_name;
344
345 /* Get a list of font families installed on the system */
346 pango_font_map_list_families(fontmap, &families, &n_families);
347
348 /* Setup a pointer to available font structs */
349 gv_af_p = N_NEW(GV_FONT_LIST_SIZE, availfont_t);
350
351 for (j = 0; j < GV_FONT_LIST_SIZE; j++) {
352 /* get the Graphviz PS font information and create the
353 available font definition structs */
354 gv_afs = gv_af_p+j;
355 gv_ps_fontdef = gv_ps_fontdefs+j;
356 gv_afs->gv_ps_fontname = gv_ps_fontdef->fontname;
357 family_name = NULL;
358 /* Search the installed system font families for the current
359 Graphvis PS font family name, i.e. AvantGarde */
360 for (i = 0; i < n_families; i++) {
361 family = families[i];
362 name = pango_font_family_get_name(family);
363 /* if a match is found get the installed font faces */
364 if (strcasecmp(gv_ps_fontdef->fontname, name) == 0) {
365 family_name = strdup(name);
366 availfaces = get_faces(family);
367 }
368 if (family_name)
369 break;
370 }
371 /* if a match is not found on the primary Graphviz font family,
372 search for a match on the equivalent font family names */
373 if (!family_name) {
374 array_sz = gv_ps_fontdef->eq_sz;
375 for (k = 0; k < array_sz; k++) {
376 for (i = 0; i < n_families; i++) {
377 family = families[i];
378 name = pango_font_family_get_name(family);
379 if (strcasecmp(gv_ps_fontdef->equiv[k], name) == 0) {
380 family_name = strdup(name);
381 availfaces = get_faces(family);
382 break;
383 }
384 }
385 if (family_name)
386 break;
387 }
388 }
389 /* if a match is not found on the equivalent font family names, search
390 for a match on the generic family name assigned to the Graphviz PS font */
391 if (!family_name) {
392 for (i = 0; i < n_families; i++) {
393 family = families[i];
394 name = pango_font_family_get_name(family);
395 if (strcasecmp(gv_ps_fontdef->generic_name, name) == 0) {
396 family_name = strdup(name);
397 availfaces = get_faces(family);
398 break;
399 }
400 }
401 }
402 /* if not match is found on the generic name, set the available font
403 name to NULL */
404 if (family_name && availfaces) {
405 gv_afs->fontname = family_name;
406 gv_afs->faces = availfaces;
407 } else {
408 gv_afs->fontname = NULL;
409 gv_afs->faces = 0;
410 }
411 }
412 g_free(families);
413#ifdef DEBUG
414 display_available_fonts(gv_af_p);
415#endif
416/* Free the Graphviz PS font definitions */
417 return (gv_af_p);
418}
419
420static void copyUpper (agxbuf* xb, char* s)
421{
422 int c;
423
424 while ((c = *s++))
425 (void)agxbputc (xb, toupper(c));
426}
427
428/* Returns the font corresponding to a Graphviz PS font.
429 AvantGarde-Book may return URW Gothic L, book
430 Returns NULL if no appropriate font found.
431*/
432static char *gv_get_font(availfont_t* gv_af_p,
433 PostscriptAlias * ps_alias, agxbuf* xb, agxbuf *xb2)
434{
435 char *avail_faces;
436 int i;
437
438 for (i = 0; i < GV_FONT_LIST_SIZE; i++) {
439 /* Searches the array of available system fonts for the one that
440 corresponds to the current Graphviz PS font name. Sets up the
441 font string with the available font name and the installed font
442 faces that match what are required by the Graphviz PS font.
443 */
444 if (gv_af_p[i].faces && strstr(ps_alias->name, gv_af_p[i].gv_ps_fontname)) {
445 agxbput(xb2, gv_af_p[i].fontname);
446 agxbput(xb2, ", ");
447 avail_faces = get_avail_faces(gv_af_p[i].faces, xb);
448 if (ps_alias->weight) {
449 if (strcasestr(avail_faces, ps_alias->weight)) {
450 agxbputc(xb2, ' ');
451 copyUpper(xb2, ps_alias->weight);
452 }
453 } else if (strcasestr(avail_faces, "REGULAR")) {
454 agxbputc(xb2, ' ');
455 agxbput(xb2, "REGULAR");
456 } else if (strstr(avail_faces, "ROMAN")) {
457 agxbputc(xb2, ' ');
458 agxbput(xb2, "ROMAN");
459 }
460 if (ps_alias->stretch) {
461 if (strcasestr(avail_faces, ps_alias->stretch)) {
462 agxbputc(xb2, ' ');
463 copyUpper(xb2, ps_alias->stretch);
464 }
465 }
466 if (ps_alias->style) {
467 if (strcasestr(avail_faces, ps_alias->style)) {
468 agxbputc(xb2, ' ');
469 copyUpper(xb2, ps_alias->style);
470 } else if (!strcasecmp(ps_alias->style, "ITALIC")) {
471 /* try to use ITALIC in place of OBLIQUE & visa versa */
472 if (strcasestr(avail_faces, "OBLIQUE")) {
473 agxbputc(xb2, ' ');
474 agxbput(xb2, "OBLIQUE");
475 }
476 } else if (!strcasecmp(ps_alias->style, "OBLIQUE")) {
477 if (strcasestr(avail_faces, "ITALIC")) {
478 agxbputc(xb2, ' ');
479 agxbput(xb2, "ITALIC");
480 }
481 }
482 }
483 return strdup(agxbuse(xb2));
484 }
485 }
486 return NULL;
487}
488
489static void
490printFontMap (gv_font_map*gv_fmap, int sz)
491{
492 int j;
493 char* font;
494
495 for (j = 0; j < sz; j++) {
496 font = gv_fmap[j].gv_font;
497 if (!font)
498 fprintf (stderr, " [%d] %s => <Not available>\n", j, gv_fmap[j].gv_ps_fontname);
499 else
500 fprintf (stderr, " [%d] %s => \"%s\"\n", j, gv_fmap[j].gv_ps_fontname, font);
501 }
502}
503
504/* Sets up a structure array that contains the Graphviz PS font name
505 and the corresponding installed font string.
506*/
507gv_font_map* get_font_mapping(PangoFontMap * fontmap)
508{
509 PostscriptAlias *ps_alias;
510 availfont_t *gv_af_p;
511 int j, ps_fontnames_sz = sizeof(postscript_alias) / sizeof(PostscriptAlias);
512 gv_font_map* gv_fmap = N_NEW(ps_fontnames_sz, gv_font_map);
513 agxbuf xb;
514 agxbuf xb2;
515 unsigned char buf[BUFSIZ];
516 unsigned char buf2[BUFSIZ];
517
518 agxbinit(&xb, BUFSIZ, buf);
519 agxbinit(&xb2, BUFSIZ, buf2);
520 gv_af_p = gv_get_ps_fontlist(fontmap); // get the available installed fonts
521 /* add the Graphviz PS font name and available system font string to the array */
522 for (j = 0; j < ps_fontnames_sz; j++) {
523 ps_alias = &postscript_alias[j];
524 gv_fmap[ps_alias->xfig_code].gv_ps_fontname = ps_alias->name;
525 gv_fmap[ps_alias->xfig_code].gv_font = gv_get_font(gv_af_p, ps_alias, &xb, &xb2);
526 }
527 gv_flist_free_af(gv_af_p);
528 agxbfree(&xb);
529 agxbfree(&xb2);
530#ifndef _WIN32
531 if (Verbose > 1) {
532 fprintf(stderr, "Verbose %d\n", Verbose);
533 printFontMap (gv_fmap, ps_fontnames_sz);
534 }
535#endif
536 return gv_fmap;
537}
538
539/* Returns a list of the fonts that are available for use
540
541*/
542
543void get_font_list(char **fonts[], int *cnt){
544
545PangoFontMap *fontmap;
546availfont_t *gv_af_p;
547int j, i;
548char **fontlist;
549fontlist = N_NEW(GV_FONT_LIST_SIZE,char *);
550fontmap = pango_cairo_font_map_new();
551gv_af_p = gv_get_ps_fontlist(fontmap); // get the available installed fonts
552g_object_unref(fontmap);
553/* load array with available font names */
554i=0;
555for (j = 0; j < GV_FONT_LIST_SIZE; j++) {
556 *(fontlist + j) = 0;
557 if ((gv_af_p[j].faces == 0) || (gv_af_p[j].fontname == NULL)) {
558 continue;
559 }
560 *(fontlist + i++) = strdup(gv_af_p[j].fontname);
561}
562/* Free unused array elements */
563for(j=i;j<GV_FONT_LIST_SIZE;j++){
564 free(*(fontlist + j));
565}
566/* Free available fonts structure */
567gv_flist_free_af(gv_af_p);
568
569*cnt = i;
570*fonts = fontlist;
571return;
572}
573