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)  

fl_set_fonts_xft.cxx

Go to the documentation of this file.
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 //