2 // "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $"
4 // More font utilities for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <X11/Xft/Xft.h>
30 // This function fills in the fltk font table with all the fonts that
31 // are found on the X server. It tries to place the fonts into families
32 // and to sort them so the first 4 in a family are normal, bold, italic,
35 // Bug: older versions calculated the value for *ap as a side effect of
36 // making the name, and then forgot about it. To avoid having to change
37 // the header files I decided to store this value in the last character
38 // of the font name array.
39 #define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1
41 // turn a stored font name into a pretty name:
42 const char* Fl::get_font_name(Fl_Font fnum
, int* ap
) {
43 Fl_Fontdesc
*f
= fl_fonts
+ fnum
;
44 if (!f
->fontname
[0]) {
45 const char* p
= f
->name
;
48 case 'B': type
= FL_BOLD
; break;
49 case 'I': type
= FL_ITALIC
; break;
50 case 'P': type
= FL_BOLD
| FL_ITALIC
; break;
51 default: type
= 0; break;
54 // NOTE: This can cause duplications in fonts that already have Bold or Italic in
55 // their "name". Maybe we need to find a cleverer way?
56 strlcpy(f
->fontname
, p
+1, ENDOFBUFFER
);
57 if (type
& FL_BOLD
) strlcat(f
->fontname
, " bold", ENDOFBUFFER
);
58 if (type
& FL_ITALIC
) strlcat(f
->fontname
, " italic", ENDOFBUFFER
);
59 f
->fontname
[ENDOFBUFFER
] = (char)type
;
61 if (ap
) *ap
= f
->fontname
[ENDOFBUFFER
];
65 ///////////////////////////////////////////////////////////
66 #define LOCAL_RAW_NAME_MAX 256
69 // sort returned fontconfig font names
70 static int name_sort(const void *aa
, const void *bb
) {
71 // What should we do here? Just do a string compare for now...
72 // NOTE: This yeilds some oddities - in particular a Blah Bold font will be
73 // listed before Blah...
74 // Also - the fontconfig listing returns some faces that are effectively duplicates
75 // as far as fltk is concerned, e.g. where there are ko or ja variants that we
76 // can't distinguish (since we are not yet fully UTF-*) - should we strip them here?
77 return strcasecmp(*(char**)aa
, *(char**)bb
);
79 } // end of extern C section
82 // Read the "pretty" name we have derived from fontconfig then convert
83 // it into the format fltk uses internally for Xft names...
84 // This is just a mess - I should have tokenised the strings and gone from there,
85 // but I really thought this would be easier!
86 static void make_raw_name(char *raw
, char *pretty
)
88 // Input name will be "Some Name:style = Bold Italic" or whatever
90 // - the first char in the "raw" name becomes either I, B, P or " " for
91 // italic, bold, bold italic or normal - this seems to be the fltk way...
93 char *style
= strchr(pretty
, ':');
94 char *last
= style
+ strlen(style
) - 2;
98 *style
= 0; // Terminate "name" string
99 style
++; // point to start of style section
102 // It is still possible that the "pretty" name has multiple comma separated entries
103 // I've seen this often in CJK fonts, for example... Keep only the first one... This
104 // is not ideal, the CJK fonts often have the name in utf8 in several languages. What
105 // we ought to do is use fontconfig to query the available languages and pick one... But which?
106 #if 0 // loop to keep the LAST name entry...
108 char *nm2
= strchr(nm1
, ',');
111 nm2
= strchr(nm1
, ',');
113 raw
[0] = ' '; raw
[1] = 0; // Default start of "raw name" text
114 strncat(raw
, nm1
, LOCAL_RAW_NAME_MAX
);
115 #else // keep the first remaining name entry
116 char *nm2
= strchr(pretty
, ',');
117 if(nm2
) *nm2
= 0; // terminate name after first entry
118 raw
[0] = ' '; raw
[1] = 0; // Default start of "raw name" text
119 strncat(raw
, pretty
, LOCAL_RAW_NAME_MAX
-1);
121 // At this point, the name is "marked" as regular...
127 #define BITALIC (BOLD | ITALIC)
129 // Now try and parse the style string - look for the "=" sign
130 style
= strchr(style
, '=');
131 while ((style
) && (style
< last
))
134 while ((*style
== '=') || (*style
== ' ') || (*style
== '\t'))
136 style
++; // Start of Style string
137 if ((style
>= last
) || (*style
== 0)) continue;
139 type
= toupper(style
[0]);
142 // Things we might see: Regular Normal Bold Italic Oblique (??what??) Medium
143 // Roman Light Demi Sans SemiCondensed SuperBold Book... etc...
144 // Things we actually care about: Bold Italic Oblique SuperBold - Others???
146 if (strncasecmp(style
, "Italic", 6) == 0)
153 if (strncasecmp(style
, "Bold", 4) == 0)
160 if (strncasecmp(style
, "Oblique", 7) == 0)
167 if (strncasecmp(style
, "SuperBold", 9) == 0)
173 default: // find the next gap
177 while ((*style
!= ' ') && (*style
!= '\t'))
180 if ((style
>= last
) || (*style
== 0)) goto STYLE_DONE
;
184 // Set the "modifier" character in the raw string
187 case BOLD
: raw
[0] = 'B';
189 case ITALIC
: raw
[0] = 'I';
191 case BITALIC
: raw
[0] = 'P';
193 default: raw
[0] = ' ';
199 ///////////////////////////////////////////////////////////
201 static int fl_free_font
= FL_FREE_FONT
;
203 // Uses the fontconfig lib to construct a list of all installed fonts.
204 // I tried using XftListFonts for this, but the API is tricky - and when
205 // I looked at the XftList* code, it calls the Fc* functions anyway, so...
207 // Also, for now I'm ignoring the "pattern_name" and just getting everything...
208 // AND I don't try and skip the fonts we've already loaded in the defaults.
209 // Blimey! What a hack!
210 Fl_Font
Fl::set_fonts(const char* pattern_name
)
212 FcFontSet
*fnt_set
; // Will hold the list of fonts we find
213 FcPattern
*fnt_pattern
; // Holds the generic "match all names" pattern
214 FcObjectSet
*fnt_obj_set
= 0; // Holds the generic "match all objects"
216 int j
; // loop iterator variable
217 int font_count
; // Total number of fonts found to process
218 char **full_list
; // The list of font names we build
220 if (fl_free_font
> FL_FREE_FONT
) // already been here
221 return (Fl_Font
)fl_free_font
;
223 fl_open_display(); // Just in case...
225 // Make sure fontconfig is ready... is this necessary? The docs say it is
226 // safe to call it multiple times, so just go for it anyway!
229 // What to do? Just return defaults...
233 // Create a search pattern that will match every font name - I think this
234 // does the Right Thing, but am not certain...
236 // This could possibly be "enhanced" to pay attention to the requested
238 fnt_pattern
= FcPatternCreate();
239 fnt_obj_set
= FcObjectSetBuild(FC_FAMILY
, FC_STYLE
, (void *)0);
241 // Hopefully, this is a set of all the fonts...
242 fnt_set
= FcFontList(0, fnt_pattern
, fnt_obj_set
);
244 // We don't need the fnt_pattern any more, release it
245 FcPatternDestroy(fnt_pattern
);
247 // Now, if we got any fonts, iterate through them...
254 font_count
= fnt_set
->nfont
; // How many fonts?
256 // Allocate array of char*'s to hold the name strings
257 full_list
= (char **)malloc(sizeof(char *) * font_count
);
259 // iterate through all the font patterns and get the names out...
260 for (j
= 0; j
< font_count
; j
++)
262 // NOTE: FcChar8 is a typedef of "unsigned char"...
263 FcChar8
*font
; // String to hold the font's name
265 // Convert from fontconfig internal pattern to human readable name
266 // NOTE: This WILL malloc storage, so we need to free it later...
267 font
= FcNameUnparse(fnt_set
->fonts
[j
]);
269 // The returned strings look like this...
270 // Century Schoolbook:style=Bold Italic,fed kursiv,Fett Kursiv,...
271 // So the bit we want is up to the first comma - BUT some strings have
272 // more than one name, separated by, guess what?, a comma...
273 stop
= start
= first
= 0;
274 stop
= strchr((char *)font
, ',');
275 start
= strchr((char *)font
, ':');
276 if ((stop
) && (start
) && (stop
< start
))
278 first
= stop
+ 1; // discard first version of name
279 // find first comma *after* the end of the name
280 stop
= strchr((char *)start
, ',');
284 first
= (char *)font
; // name is just what was returned
286 // Truncate the name after the (english) modifiers description
289 *stop
= 0; // Terminate the string at the first comma, if there is one
292 // Copy the font description into our list
293 if (first
== (char *)font
)
294 { // The listed name is still OK
295 full_list
[j
] = (char *)font
;
298 { // The listed name has been modified
299 full_list
[j
] = strdup(first
);
300 // Free the font name storage
303 // replace "style=Regular" so strcmp sorts it first
305 char *reg
= strstr(full_list
[j
], "=Regular");
310 // Release the fnt_set - we don't need it any more
311 FcFontSetDestroy(fnt_set
);
313 // Sort the list into alphabetic order
314 qsort(full_list
, font_count
, sizeof(*full_list
), name_sort
);
316 // Now let us add the names we got to fltk's font list...
317 for (j
= 0; j
< font_count
; j
++)
321 char xft_name
[LOCAL_RAW_NAME_MAX
];
323 // Parse the strings into FLTK-XFT style..
324 make_raw_name(xft_name
, full_list
[j
]);
325 // NOTE: This just adds on AFTER the default fonts - no attempt is made
326 // to identify already loaded fonts. Is this bad?
327 stored_name
= strdup(xft_name
);
328 Fl::set_font((Fl_Font
)(j
+ FL_FREE_FONT
), stored_name
);
331 free(full_list
[j
]); // release that name from our internal array
334 // Now we are done with the list, release it fully
337 return (Fl_Font
)fl_free_font
;
339 ////////////////////////////////////////////////////////////////
343 static int int_sort(const void *aa
, const void *bb
) {
344 return (*(int*)aa
)-(*(int*)bb
);
348 ////////////////////////////////////////////////////////////////
350 // Return all the point sizes supported by this font:
351 // Suprisingly enough Xft works exactly like fltk does and returns
352 // the same list. Except there is no way to tell if the font is scalable.
353 int Fl::get_font_sizes(Fl_Font fnum
, int*& sizep
) {
354 Fl_Fontdesc
*s
= fl_fonts
+fnum
;
355 if (!s
->name
) s
= fl_fonts
; // empty slot in table, use entry 0
358 XftFontSet
* fs
= XftListFonts(fl_display
, fl_screen
,
359 XFT_FAMILY
, XftTypeString
, s
->name
+1,
363 static int* array
= 0;
364 static int array_size
= 0;
365 if (fs
->nfont
>= array_size
) {
367 array
= new int[array_size
= fs
->nfont
+1];
369 array
[0] = 0; int j
= 1; // claim all fonts are scalable
370 for (int i
= 0; i
< fs
->nfont
; i
++) {
372 if (XftPatternGetDouble(fs
->fonts
[i
], XFT_PIXEL_SIZE
, 0, &v
) == XftResultMatch
) {
376 qsort(array
+1, j
-1, sizeof(int), int_sort
);
377 XftFontSetDestroy(fs
);
383 // End of "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $".