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  */
28 char* strcasestr (const char *str, const char *pat);
29 #ifndef HAVE_STRCASESTR
strcasestr(const char * str,const char * pat)30 char* 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 
58 extern 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 
150 typedef struct {
151     int flag;
152     char* name;
153 } face_t;
154 static 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 */
173 static 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 
178 static 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 
183 static 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 
188 static 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 
193 static 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 
198 static 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 
203 static 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 
208 static 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 
211 static 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 
216 static 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 
219 typedef 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 */
227 static 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 
241 typedef 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*)calloc((n),sizeof(t))
249 
250 static PostscriptAlias postscript_alias[] = {
251 #include "ps_font_equiv.h"
252 };
253 
254 /* Frees memory used by the available system font definitions */
gv_flist_free_af(availfont_t * gv_af_p)255 static 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 
get_faces(PangoFontFamily * family)266 static 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
293 static void
display_available_fonts(availfont_t * gv_af_p)294 display_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 */
get_avail_faces(int faces,agxbuf * xb)316 static 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 */
gv_get_ps_fontlist(PangoFontMap * fontmap)334 static 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 
copyUpper(agxbuf * xb,char * s)420 static 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 */
gv_get_font(availfont_t * gv_af_p,PostscriptAlias * ps_alias,agxbuf * xb,agxbuf * xb2)432 static 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 
489 static void
printFontMap(gv_font_map * gv_fmap,int sz)490 printFontMap (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 */
get_font_mapping(PangoFontMap * fontmap)507 gv_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 
get_font_list(char ** fonts[],int * cnt)543 void get_font_list(char **fonts[], int *cnt){
544 
545 PangoFontMap *fontmap;
546 availfont_t *gv_af_p;
547 int j, i;
548 char **fontlist;
549 fontlist = N_NEW(GV_FONT_LIST_SIZE,char *);
550 fontmap = pango_cairo_font_map_new();
551 gv_af_p = gv_get_ps_fontlist(fontmap);	// get the available installed fonts
552 g_object_unref(fontmap);
553 /* load array with available font names */
554 i=0;
555 for (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 */
563 for(j=i;j<GV_FONT_LIST_SIZE;j++){
564     free(*(fontlist + j));
565 }
566 /* Free available fonts structure */
567 gv_flist_free_af(gv_af_p);
568 
569 *cnt = i;
570 *fonts = fontlist;
571 return;
572 }
573