|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $" 00003 // 00004 // More font utilities for the Fast Light Tool Kit (FLTK). 00005 // 00006 // Copyright 1998-2010 by Bill Spitzak and others. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems on the following page: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 #include <X11/Xft/Xft.h> 00029 00030 // This function fills in the fltk font table with all the fonts that 00031 // are found on the X server. It tries to place the fonts into families 00032 // and to sort them so the first 4 in a family are normal, bold, italic, 00033 // and bold italic. 00034 00035 // Bug: older versions calculated the value for *ap as a side effect of 00036 // making the name, and then forgot about it. To avoid having to change 00037 // the header files I decided to store this value in the last character 00038 // of the font name array. 00039 #define ENDOFBUFFER 127 // sizeof(Fl_Font.fontname)-1 00040 00041 // turn a stored font name into a pretty name: 00042 const char* Fl::get_font_name(Fl_Font fnum, int* ap) { 00043 Fl_Fontdesc *f = fl_fonts + fnum; 00044 if (!f->fontname[0]) { 00045 const char* p = f->name; 00046 int type; 00047 switch (p[0]) { 00048 case 'B': type = FL_BOLD; break; 00049 case 'I': type = FL_ITALIC; break; 00050 case 'P': type = FL_BOLD | FL_ITALIC; break; 00051 default: type = 0; break; 00052 } 00053 00054 // NOTE: This can cause duplications in fonts that already have Bold or Italic in 00055 // their "name". Maybe we need to find a cleverer way? 00056 strlcpy(f->fontname, p+1, ENDOFBUFFER); 00057 if (type & FL_BOLD) strlcat(f->fontname, " bold", ENDOFBUFFER); 00058 if (type & FL_ITALIC) strlcat(f->fontname, " italic", ENDOFBUFFER); 00059 f->fontname[ENDOFBUFFER] = (char)type; 00060 } 00061 if (ap) *ap = f->fontname[ENDOFBUFFER]; 00062 return f->fontname; 00063 } 00064 00066 #define LOCAL_RAW_NAME_MAX 256 00067 00068 extern "C" { 00069 // sort returned fontconfig font names 00070 static int name_sort(const void *aa, const void *bb) { 00071 // What should we do here? Just do a string compare for now... 00072 // NOTE: This yeilds some oddities - in particular a Blah Bold font will be 00073 // listed before Blah... 00074 // Also - the fontconfig listing returns some faces that are effectively duplicates 00075 // as far as fltk is concerned, e.g. where there are ko or ja variants that we 00076 // can't distinguish (since we are not yet fully UTF-*) - should we strip them here? 00077 return strcasecmp(*(char**)aa, *(char**)bb); 00078 } // end of name_sort 00079 } // end of extern C section 00080 00081 00082 // Read the "pretty" name we have derived from fontconfig then convert 00083 // it into the format fltk uses internally for Xft names... 00084 // This is just a mess - I should have tokenised the strings and gone from there, 00085 // but I really thought this would be easier! 00086 static void make_raw_name(char *raw, char *pretty) 00087 { 00088 // Input name will be "Some Name:style = Bold Italic" or whatever 00089 // The plan is this: 00090 // - the first char in the "raw" name becomes either I, B, P or " " for 00091 // italic, bold, bold italic or normal - this seems to be the fltk way... 00092 00093 char *style = strchr(pretty, ':'); 00094 char *last = style + strlen(style) - 2; 00095 00096 if (style) 00097 { 00098 *style = 0; // Terminate "name" string 00099 style ++; // point to start of style section 00100 } 00101 00102 // It is still possible that the "pretty" name has multiple comma separated entries 00103 // I've seen this often in CJK fonts, for example... Keep only the first one... This 00104 // is not ideal, the CJK fonts often have the name in utf8 in several languages. What 00105 // we ought to do is use fontconfig to query the available languages and pick one... But which? 00106 #if 0 // loop to keep the LAST name entry... 00107 char *nm1 = pretty; 00108 char *nm2 = strchr(nm1, ','); 00109 while(nm2) { 00110 nm1 = nm2 + 1; 00111 nm2 = strchr(nm1, ','); 00112 } 00113 raw[0] = ' '; raw[1] = 0; // Default start of "raw name" text 00114 strncat(raw, nm1, LOCAL_RAW_NAME_MAX); 00115 #else // keep the first remaining name entry 00116 char *nm2 = strchr(pretty, ','); 00117 if(nm2) *nm2 = 0; // terminate name after first entry 00118 raw[0] = ' '; raw[1] = 0; // Default start of "raw name" text 00119 strncat(raw, pretty, LOCAL_RAW_NAME_MAX-1); 00120 #endif 00121 // At this point, the name is "marked" as regular... 00122 if (style) 00123 { 00124 #define PLAIN 0 00125 #define BOLD 1 00126 #define ITALIC 2 00127 #define BITALIC (BOLD | ITALIC) 00128 int mods = PLAIN; 00129 // Now try and parse the style string - look for the "=" sign 00130 style = strchr(style, '='); 00131 while ((style) && (style < last)) 00132 { 00133 int type; 00134 while ((*style == '=') || (*style == ' ') || (*style == '\t')) 00135 { 00136 style++; // Start of Style string 00137 if ((style >= last) || (*style == 0)) continue; 00138 } 00139 type = toupper(style[0]); 00140 switch (type) 00141 { 00142 // Things we might see: Regular Normal Bold Italic Oblique (??what??) Medium 00143 // Roman Light Demi Sans SemiCondensed SuperBold Book... etc... 00144 // Things we actually care about: Bold Italic Oblique SuperBold - Others??? 00145 case 'I': 00146 if (strncasecmp(style, "Italic", 6) == 0) 00147 { 00148 mods |= ITALIC; 00149 } 00150 goto NEXT_STYLE; 00151 00152 case 'B': 00153 if (strncasecmp(style, "Bold", 4) == 0) 00154 { 00155 mods |= BOLD; 00156 } 00157 goto NEXT_STYLE; 00158 00159 case 'O': 00160 if (strncasecmp(style, "Oblique", 7) == 0) 00161 { 00162 mods |= ITALIC; 00163 } 00164 goto NEXT_STYLE; 00165 00166 case 's': 00167 if (strncasecmp(style, "SuperBold", 9) == 0) 00168 { 00169 mods |= BOLD; 00170 } 00171 goto NEXT_STYLE; 00172 00173 default: // find the next gap 00174 goto NEXT_STYLE; 00175 } // switch end 00176 NEXT_STYLE: 00177 while ((*style != ' ') && (*style != '\t')) 00178 { 00179 style++; 00180 if ((style >= last) || (*style == 0)) goto STYLE_DONE; 00181 } 00182 } 00183 STYLE_DONE: 00184 // Set the "modifier" character in the raw string 00185 switch(mods) 00186 { 00187 case BOLD: raw[0] = 'B'; 00188 break; 00189 case ITALIC: raw[0] = 'I'; 00190 break; 00191 case BITALIC: raw[0] = 'P'; 00192 break; 00193 default: raw[0] = ' '; 00194 break; 00195 } 00196 } 00197 } // make_raw_name 00198 00200 00201 static int fl_free_font = FL_FREE_FONT; 00202 00203 // Uses the fontconfig lib to construct a list of all installed fonts. 00204 // I tried using XftListFonts for this, but the API is tricky - and when 00205 // I looked at the XftList* code, it calls the Fc* functions anyway, so... 00206 // 00207 // Also, for now I'm ignoring the "pattern_name" and just getting everything... 00208 // AND I don't try and skip the fonts we've already loaded in the defaults. 00209 // Blimey! What a hack! 00210 Fl_Font Fl::set_fonts(const char* pattern_name) 00211 { 00212 FcFontSet *fnt_set; // Will hold the list of fonts we find 00213 FcPattern *fnt_pattern; // Holds the generic "match all names" pattern 00214 FcObjectSet *fnt_obj_set = 0; // Holds the generic "match all objects" 00215 00216 int j; // loop iterator variable 00217 int font_count; // Total number of fonts found to process 00218 char **full_list; // The list of font names we build 00219 00220 if (fl_free_font > FL_FREE_FONT) // already been here 00221 return (Fl_Font)fl_free_font; 00222 00223 fl_open_display(); // Just in case... 00224 00225 // Make sure fontconfig is ready... is this necessary? The docs say it is 00226 // safe to call it multiple times, so just go for it anyway! 00227 if (!FcInit()) 00228 { 00229 // What to do? Just return defaults... 00230 return FL_FREE_FONT; 00231 } 00232 00233 // Create a search pattern that will match every font name - I think this 00234 // does the Right Thing, but am not certain... 00235 // 00236 // This could possibly be "enhanced" to pay attention to the requested 00237 // "pattern_name"? 00238 fnt_pattern = FcPatternCreate(); 00239 fnt_obj_set = FcObjectSetBuild(FC_FAMILY, FC_STYLE, (void *)0); 00240 00241 // Hopefully, this is a set of all the fonts... 00242 fnt_set = FcFontList(0, fnt_pattern, fnt_obj_set); 00243 00244 // We don't need the fnt_pattern any more, release it 00245 FcPatternDestroy(fnt_pattern); 00246 00247 // Now, if we got any fonts, iterate through them... 00248 if (fnt_set) 00249 { 00250 char *stop; 00251 char *start; 00252 char *first; 00253 00254 font_count = fnt_set->nfont; // How many fonts? 00255 00256 // Allocate array of char*'s to hold the name strings 00257 full_list = (char **)malloc(sizeof(char *) * font_count); 00258 00259 // iterate through all the font patterns and get the names out... 00260 for (j = 0; j < font_count; j++) 00261 { 00262 // NOTE: FcChar8 is a typedef of "unsigned char"... 00263 FcChar8 *font; // String to hold the font's name 00264 00265 // Convert from fontconfig internal pattern to human readable name 00266 // NOTE: This WILL malloc storage, so we need to free it later... 00267 font = FcNameUnparse(fnt_set->fonts[j]); 00268 00269 // The returned strings look like this... 00270 // Century Schoolbook:style=Bold Italic,fed kursiv,Fett Kursiv,... 00271 // So the bit we want is up to the first comma - BUT some strings have 00272 // more than one name, separated by, guess what?, a comma... 00273 stop = start = first = 0; 00274 stop = strchr((char *)font, ','); 00275 start = strchr((char *)font, ':'); 00276 if ((stop) && (start) && (stop < start)) 00277 { 00278 first = stop + 1; // discard first version of name 00279 // find first comma *after* the end of the name 00280 stop = strchr((char *)start, ','); 00281 } 00282 else 00283 { 00284 first = (char *)font; // name is just what was returned 00285 } 00286 // Truncate the name after the (english) modifiers description 00287 if (stop) 00288 { 00289 *stop = 0; // Terminate the string at the first comma, if there is one 00290 } 00291 00292 // Copy the font description into our list 00293 if (first == (char *)font) 00294 { // The listed name is still OK 00295 full_list[j] = (char *)font; 00296 } 00297 else 00298 { // The listed name has been modified 00299 full_list[j] = strdup(first); 00300 // Free the font name storage 00301 free (font); 00302 } 00303 // replace "style=Regular" so strcmp sorts it first 00304 if (start) { 00305 char *reg = strstr(full_list[j], "=Regular"); 00306 if (reg) reg[1]='.'; 00307 } 00308 } 00309 00310 // Release the fnt_set - we don't need it any more 00311 FcFontSetDestroy(fnt_set); 00312 00313 // Sort the list into alphabetic order 00314 qsort(full_list, font_count, sizeof(*full_list), name_sort); 00315 00316 // Now let us add the names we got to fltk's font list... 00317 for (j = 0; j < font_count; j++) 00318 { 00319 if (full_list[j]) 00320 { 00321 char xft_name[LOCAL_RAW_NAME_MAX]; 00322 char *stored_name; 00323 // Parse the strings into FLTK-XFT style.. 00324 make_raw_name(xft_name, full_list[j]); 00325 // NOTE: This just adds on AFTER the default fonts - no attempt is made 00326 // to identify already loaded fonts. Is this bad? 00327 stored_name = strdup(xft_name); 00328 Fl::set_font((Fl_Font)(j + FL_FREE_FONT), stored_name); 00329 fl_free_font ++; 00330 00331 free(full_list[j]); // release that name from our internal array 00332 } 00333 } 00334 // Now we are done with the list, release it fully 00335 free(full_list); 00336 } 00337 return (Fl_Font)fl_free_font; 00338 } // ::set_fonts 00340 00341 00342 extern "C" { 00343 static int int_sort(const void *aa, const void *bb) { 00344 return (*(int*)aa)-(*(int*)bb); 00345 } 00346 } 00347 00349 00350 // Return all the point sizes supported by this font: 00351 // Suprisingly enough Xft works exactly like fltk does and returns 00352 // the same list. Except there is no way to tell if the font is scalable. 00353 int Fl::get_font_sizes(Fl_Font fnum, int*& sizep) { 00354 Fl_Fontdesc *s = fl_fonts+fnum; 00355 if (!s->name) s = fl_fonts; // empty slot in table, use entry 0 00356 00357 fl_open_display(); 00358 XftFontSet* fs = XftListFonts(fl_display, fl_screen, 00359 XFT_FAMILY, XftTypeString, s->name+1, 00360 (void *)0, 00361 XFT_PIXEL_SIZE, 00362 (void *)0); 00363 static int* array = 0; 00364 static int array_size = 0; 00365 if (fs->nfont >= array_size) { 00366 delete[] array; 00367 array = new int[array_size = fs->nfont+1]; 00368 } 00369 array[0] = 0; int j = 1; // claim all fonts are scalable 00370 for (int i = 0; i < fs->nfont; i++) { 00371 double v; 00372 if (XftPatternGetDouble(fs->fonts[i], XFT_PIXEL_SIZE, 0, &v) == XftResultMatch) { 00373 array[j++] = int(v); 00374 } 00375 } 00376 qsort(array+1, j-1, sizeof(int), int_sort); 00377 XftFontSetDestroy(fs); 00378 sizep = array; 00379 return j; 00380 } 00381 00382 // 00383 // End of "$Id: fl_set_fonts_xft.cxx 7913 2010-11-29 18:18:27Z greg.ercolano $". 00384 //