1 //
2 // "$Id: fl_set_fonts_x.cxx 5190 2006-06-09 16:16:34Z mike $"
3 //
4 // X11 font utilities for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2005 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 //     http://www.fltk.org/str.php
26 //
27 
28 // This function fills in the fltk font table with all the fonts that
29 // are found on the X server.  It tries to place the fonts into families
30 // and to sort them so the first 4 in a family are normal, bold, italic,
31 // and bold italic.
32 
33 // Standard X fonts are matched by a pattern that is always of
34 // this form, and this pattern is put in the table:
35 // "-*-family-weight-slant-width1-style-*-registry-encoding"
36 
37 // Non-standard font names (those not starting with '-') are matched
38 // by a pattern of the form "prefix*suffix", where the '*' is where
39 // fltk thinks the point size is, or by the actual font name if no
40 // point size is found.
41 
42 // Fltk knows how to pull an "attribute" out of a font name, such as
43 // bold or italic, by matching known x font field values.  All words
44 // that don't match a known attribute are combined into the "name"
45 // of the font.  Names are compared before attributes for sorting, this
46 // makes the bold and plain version of a font come out next to each
47 // other despite the poor X font naming scheme.
48 
49 // By default fl_set_fonts() only does iso8859-1 encoded fonts.  You can
50 // do all normal X fonts by passing "-*" or every possible font with "*".
51 
52 // Fl::set_font will take strings other than the ones this stores
53 // and can identify any font on X that way.  You may want to write your
54 // own system of font management and not use this code.
55 
56 // turn word N of a X font name into either some attribute bits
57 // (right now 0, FL_BOLD, or FL_ITALIC), or into -1 indicating that
58 // the word should be put into the name:
59 
attribute(int n,const char * p)60 static int attribute(int n, const char *p) {
61   // don't put blank things into name:
62   if (!*p || *p=='-' || *p=='*') return 0;
63   if (n == 3) { // weight
64     if (!strncmp(p,"normal",6) ||
65 	!strncmp(p,"light",5) ||
66 	!strncmp(p,"medium",6) ||
67 	!strncmp(p,"book",4)) return 0;
68     if (!strncmp(p,"bold",4) || !strncmp(p,"demi",4)) return FL_BOLD;
69   } else if (n == 4) { // slant
70     if (*p == 'r') return 0;
71     if (*p == 'i' || *p == 'o') return FL_ITALIC;
72   } else if (n == 5) { // sWidth
73     if (!strncmp(p,"normal",6)) return 0;
74   }
75   return -1;
76 }
77 
78 // return non-zero if the registry-encoding should be used:
79 extern const char* fl_encoding;
use_registry(const char * p)80 static int use_registry(const char *p) {
81   return *p && *p!='*' && strcmp(p,fl_encoding);
82 }
83 
84 // Bug: older versions calculated the value for *ap as a side effect of
85 // making the name, and then forgot about it. To avoid having to change
86 // the header files I decided to store this value in the last character
87 // of the font name array.
88 #define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1
89 
90 // turn a stored (with *'s) X font name into a pretty name:
get_font_name(Fl_Font fnum,int * ap)91 const char* Fl::get_font_name(Fl_Font fnum, int* ap) {
92   Fl_Fontdesc *f = fl_fonts + fnum;
93   if (!f->fontname[0]) {
94     int type = 0;
95     const char* p = f->name;
96     if (!p) {
97       if (ap) *ap = 0;
98       return "";
99     }
100     char *o = f->fontname;
101 
102     if (*p != '-') { // non-standard font, just replace * with spaces:
103       if (strstr(p,"bold")) type = FL_BOLD;
104       if (strstr(p,"ital")) type |= FL_ITALIC;
105       for (;*p; p++) {
106 	if (*p == '*' || *p == ' ' || *p == '-') {
107 	  do p++; while (*p == '*' || *p == ' ' || *p == '-');
108 	  if (!*p) break;
109 	  if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = ' ';
110 	}
111 	if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = *p;
112       }
113       *o = 0;
114 
115     } else { // standard dash-seperated font:
116 
117       // get the family:
118       const char *x = fl_font_word(p,2); if (*x) x++; if (*x=='*') x++;
119       if (!*x) {
120 	if (ap) *ap = 0;
121 	return p;
122       }
123       const char *e = fl_font_word(x,1);
124       if ((e - x) < (int)(ENDOFBUFFER - 1)) {
125 	// MRS: we want strncpy here, not strlcpy...
126 	strncpy(o,x,e-x);
127 	o += e-x;
128       } else {
129 	strlcpy(f->fontname, x, ENDOFBUFFER);
130 	o = f->fontname+ENDOFBUFFER-1;
131       }
132 
133       // collect all the attribute words:
134       for (int n = 3; n <= 6; n++) {
135 	// get the next word:
136 	if (*e) e++; x = e; e = fl_font_word(x,1);
137 	int t = attribute(n,x);
138 	if (t < 0) {
139 	  if (o < (f->fontname + ENDOFBUFFER - 1)) *o++ = ' ';
140 	  if ((e - x) < (int)(ENDOFBUFFER - (o - f->fontname) - 1)) {
141 	    // MRS: we want strncpy here, not strlcpy...
142 	    strncpy(o,x,e-x);
143 	    o += e-x;
144 	  } else {
145 	    strlcpy(o,x, ENDOFBUFFER - (o - f->fontname) - 1);
146 	    o = f->fontname+ENDOFBUFFER-1;
147 	  }
148 	} else type |= t;
149       }
150 
151       // skip over the '*' for the size and get the registry-encoding:
152       x = fl_font_word(e,2);
153       if (*x) {x++; *o++ = '('; while (*x) *o++ = *x++; *o++ = ')';}
154 
155       *o = 0;
156       if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER);
157       if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER);
158     }
159     f->fontname[ENDOFBUFFER] = (char)type;
160   }
161   if (ap) *ap = f->fontname[ENDOFBUFFER];
162   return f->fontname;
163 }
164 
165 extern "C" {
166 // sort raw (non-'*') X font names into perfect order:
167 
ultrasort(const void * aa,const void * bb)168 static int ultrasort(const void *aa, const void *bb) {
169   const char *a = *(char **)aa;
170   const char *b = *(char **)bb;
171 
172   // sort all non x-fonts at the end:
173   if (*a != '-') {
174     if (*b == '-') return 1;
175     // 2 non-x fonts are matched by "numeric sort"
176     int ret = 0;
177     for (;;) {
178       if (isdigit(*a) && isdigit(*b)) {
179 	int na = strtol(a, (char **)&a, 10);
180 	int nb = strtol(b, (char **)&b, 10);
181 	if (!ret) ret = na-nb;
182       } else if (*a != *b) {
183 	return (*a-*b);
184       } else if (!*a) {
185 	return ret;
186       } else {
187 	a++; b++;
188       }
189     }
190   } else {
191     if (*b != '-') return -1;
192   }
193 
194   // skip the foundry (assumme equal):
195   for (a++; *a && *a++!='-';);
196   for (b++; *b && *b++!='-';);
197 
198   // compare the family and all the attribute words:
199   int atype = 0;
200   int btype = 0;
201   for (int n = 2; n <= 6; n++) {
202     int at = attribute(n,a);
203     int bt = attribute(n,b);
204     if (at < 0) {
205       if (bt >= 0) return 1;
206       for (;;) {if (*a!=*b) return *a-*b; b++; if (!*a || *a++=='-') break;}
207     } else {
208       if (bt < 0) return -1;
209       a = fl_font_word(a,1); if (*a) a++;
210       b = fl_font_word(b,1); if (*b) b++;
211       atype |= at; btype |= bt;
212     }
213   }
214 
215   // remember the pixel size:
216   int asize = atoi(a);
217   int bsize = atoi(b);
218 
219   // compare the registry/encoding:
220   a = fl_font_word(a,6); if (*a) a++;
221   b = fl_font_word(b,6); if (*b) b++;
222   if (use_registry(a)) {
223     if (!use_registry(b)) return 1;
224     int r = strcmp(a,b); if (r) return r;
225   } else {
226     if (use_registry(b)) return -1;
227   }
228 
229   if (atype != btype) return atype-btype;
230   if (asize != bsize) return asize-bsize;
231 
232   // something wrong, just do a string compare...
233   return strcmp(*(char**)aa, *(char**)bb);
234 }
235 }
236 
237 // converts a X font name to a standard starname, returns point size:
to_canonical(char * to,const char * from,size_t tolen)238 static int to_canonical(char *to, const char *from, size_t tolen) {
239   char* c = fl_find_fontsize((char*)from);
240   if (!c) return -1; // no point size found...
241   const char* endptr;
242   int size = strtol(c,(char**)&endptr,10);
243   if (from[0] == '-') {
244     // replace the "foundry" with -*-:
245     *to++ = '-'; *to++ = '*';
246     for (from++; *from && *from != '-'; from++);
247     // skip to the registry-encoding:
248     endptr = (char*)fl_font_word(endptr,6);
249     if (*endptr && !use_registry(endptr+1)) endptr = "";
250   }
251   int n = c-from;
252   // MRS: we want strncpy here, not strlcpy...
253   if (n > (int)(tolen - 1)) return -1;
254   strncpy(to,from,n);
255   to[n++] = '*';
256   strlcpy(to+n,endptr, tolen - n);
257   return size;
258 }
259 
260 static int fl_free_font = FL_FREE_FONT;
261 
set_fonts(const char * xstarname)262 Fl_Font Fl::set_fonts(const char* xstarname) {
263   if (fl_free_font > FL_FREE_FONT) // already been here
264     return (Fl_Font)fl_free_font;
265   fl_open_display();
266   int xlistsize;
267   char buf[20];
268   if (!xstarname) {
269     strcpy(buf,"-*-"); strcpy(buf+3,fl_encoding);
270     xstarname = buf;
271   }
272   char **xlist = XListFonts(fl_display, xstarname, 10000, &xlistsize);
273   if (!xlist) return (Fl_Font)fl_free_font;
274   qsort(xlist, xlistsize, sizeof(*xlist), ultrasort);
275   int used_xlist = 0;
276   for (int i=0; i<xlistsize;) {
277     int first_xlist = i;
278     const char *p = xlist[i++];
279     char canon[1024];
280     int size = to_canonical(canon, p, sizeof(canon));
281     if (size >= 0) {
282       for (;;) { // find all matching fonts:
283 	if (i >= xlistsize) break;
284 	const char *q = xlist[i];
285 	char this_canon[1024];
286 	if (to_canonical(this_canon, q, sizeof(this_canon)) < 0) break;
287 	if (strcmp(canon, this_canon)) break;
288 	i++;
289       }
290       /*if (*p=='-' || i > first_xlist+1)*/ p = canon;
291     }
292     int j;
293     for (j = 0;; j++) {
294       if (j < FL_FREE_FONT) {
295 	// see if it is one of our built-in fonts:
296 	// if so, set the list of x fonts, since we have it anyway
297 	if (fl_fonts[j].name && !strcmp(fl_fonts[j].name, p)) break;
298       } else {
299 	j = fl_free_font++;
300 	if (p == canon) p = strdup(p); else used_xlist = 1;
301 	Fl::set_font((Fl_Font)j, p);
302 	break;
303       }
304     }
305     if (!fl_fonts[j].xlist) {
306       fl_fonts[j].xlist = xlist+first_xlist;
307       fl_fonts[j].n = -(i-first_xlist);
308       used_xlist = 1;
309     }
310   }
311   if (!used_xlist) XFreeFontNames(xlist);
312   return (Fl_Font)fl_free_font;
313 }
314 
get_font_sizes(Fl_Font fnum,int * & sizep)315 int Fl::get_font_sizes(Fl_Font fnum, int*& sizep) {
316   Fl_Fontdesc *s = fl_fonts+fnum;
317   if (!s->name) s = fl_fonts; // empty slot in table, use entry 0
318   if (!s->xlist) {
319     fl_open_display();
320     s->xlist = XListFonts(fl_display, s->name, 100, &(s->n));
321     if (!s->xlist) return 0;
322   }
323   int listsize = s->n; if (listsize<0) listsize = -listsize;
324   static int sizes[128];
325   int numsizes = 0;
326   for (int i = 0; i < listsize; i++) {
327     char *q = s->xlist[i];
328     char *d = fl_find_fontsize(q);
329     if (!d) continue;
330     int s = strtol(d,0,10);
331     if (!numsizes || sizes[numsizes-1] < s) {
332       sizes[numsizes++] = s;
333     } else {
334       // insert-sort the new size into list:
335       int n;
336       for (n = numsizes-1; n > 0; n--) if (sizes[n-1] < s) break;
337       if (sizes[n] != s) {
338 	for (int m = numsizes; m > n; m--) sizes[m] = sizes[m-1];
339 	sizes[n] = s;
340 	numsizes++;
341       }
342     }
343   }
344   sizep = sizes;
345   return numsizes;
346 }
347 
348 //
349 // End of "$Id: fl_set_fonts_x.cxx 5190 2006-06-09 16:16:34Z mike $".
350 //
351