themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / fl_set_fonts_xft.cxx
blob2b18178e2ef6b3d3fb1e9b05126031dd81cfc3ed
1 //
2 // "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $"
3 //
4 // More font utilities for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 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.
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
21 // USA.
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,
33 // and 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;
46 int type;
47 switch (p[0]) {
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];
62 return f->fontname;
65 ///////////////////////////////////////////////////////////
66 #define LOCAL_RAW_NAME_MAX 256
68 extern "C" {
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);
78 } // end of name_sort
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
89 // The plan is this:
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;
96 if (style)
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...
107 char *nm1 = pretty;
108 char *nm2 = strchr(nm1, ',');
109 while(nm2) {
110 nm1 = nm2 + 1;
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);
120 #endif
121 // At this point, the name is "marked" as regular...
122 if (style)
124 #define PLAIN 0
125 #define BOLD 1
126 #define ITALIC 2
127 #define BITALIC (BOLD | ITALIC)
128 int mods = PLAIN;
129 // Now try and parse the style string - look for the "=" sign
130 style = strchr(style, '=');
131 while ((style) && (style < last))
133 int type;
134 while ((*style == '=') || (*style == ' ') || (*style == '\t'))
136 style++; // Start of Style string
137 if ((style >= last) || (*style == 0)) continue;
139 type = toupper(style[0]);
140 switch (type)
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???
145 case 'I':
146 if (strncasecmp(style, "Italic", 6) == 0)
148 mods |= ITALIC;
150 goto NEXT_STYLE;
152 case 'B':
153 if (strncasecmp(style, "Bold", 4) == 0)
155 mods |= BOLD;
157 goto NEXT_STYLE;
159 case 'O':
160 if (strncasecmp(style, "Oblique", 7) == 0)
162 mods |= ITALIC;
164 goto NEXT_STYLE;
166 case 's':
167 if (strncasecmp(style, "SuperBold", 9) == 0)
169 mods |= BOLD;
171 goto NEXT_STYLE;
173 default: // find the next gap
174 goto NEXT_STYLE;
175 } // switch end
176 NEXT_STYLE:
177 while ((*style != ' ') && (*style != '\t'))
179 style++;
180 if ((style >= last) || (*style == 0)) goto STYLE_DONE;
183 STYLE_DONE:
184 // Set the "modifier" character in the raw string
185 switch(mods)
187 case BOLD: raw[0] = 'B';
188 break;
189 case ITALIC: raw[0] = 'I';
190 break;
191 case BITALIC: raw[0] = 'P';
192 break;
193 default: raw[0] = ' ';
194 break;
197 } // make_raw_name
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!
227 if (!FcInit())
229 // What to do? Just return defaults...
230 return FL_FREE_FONT;
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
237 // "pattern_name"?
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...
248 if (fnt_set)
250 char *stop;
251 char *start;
252 char *first;
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, ',');
282 else
284 first = (char *)font; // name is just what was returned
286 // Truncate the name after the (english) modifiers description
287 if (stop)
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;
297 else
298 { // The listed name has been modified
299 full_list[j] = strdup(first);
300 // Free the font name storage
301 free (font);
303 // replace "style=Regular" so strcmp sorts it first
304 if (start) {
305 char *reg = strstr(full_list[j], "=Regular");
306 if (reg) reg[1]='.';
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++)
319 if (full_list[j])
321 char xft_name[LOCAL_RAW_NAME_MAX];
322 char *stored_name;
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);
329 fl_free_font ++;
331 free(full_list[j]); // release that name from our internal array
334 // Now we are done with the list, release it fully
335 free(full_list);
337 return (Fl_Font)fl_free_font;
338 } // ::set_fonts
339 ////////////////////////////////////////////////////////////////
342 extern "C" {
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
357 fl_open_display();
358 XftFontSet* fs = XftListFonts(fl_display, fl_screen,
359 XFT_FAMILY, XftTypeString, s->name+1,
360 (void *)0,
361 XFT_PIXEL_SIZE,
362 (void *)0);
363 static int* array = 0;
364 static int array_size = 0;
365 if (fs->nfont >= array_size) {
366 delete[] array;
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++) {
371 double v;
372 if (XftPatternGetDouble(fs->fonts[i], XFT_PIXEL_SIZE, 0, &v) == XftResultMatch) {
373 array[j++] = int(v);
376 qsort(array+1, j-1, sizeof(int), int_sort);
377 XftFontSetDestroy(fs);
378 sizep = array;
379 return j;
383 // End of "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $".