The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

FallbackFont.vala in libbirdfont/Renderer

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/Renderer/FallbackFont.vala.
Fix warning about missing version list
1 /* 2 Copyright (C) 2015 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 using Gee; 16 17 [SimpleType] 18 [CCode (has_type_id = false)] 19 public extern struct FcConfig { 20 } 21 22 [CCode (cname = "FcInitLoadConfigAndFonts")] 23 public extern FcConfig* FcInitLoadConfigAndFonts (); 24 25 [CCode (cname = "FcConfigAppFontAddDir")] 26 public extern string* FcConfigAppFontAddDir (FcConfig* config, string path); 27 28 [CCode (cname = "FcConfigSetSysRoot")] 29 public extern void FcConfigSetSysRoot (FcConfig* config, string path); 30 31 [CCode (cname = "FcConfigParseAndLoad")] 32 public extern bool FcConfigParseAndLoad (FcConfig* config, string path, bool complain); 33 34 [CCode (cname = "FcConfigSetCurrent")] 35 public extern void FcConfigSetCurrent (FcConfig* config); 36 37 [CCode (cname = "FcConfigCreate")] 38 public extern FcConfig* FcConfigCreate (); 39 40 [CCode (cname = "FcConfigFilename")] 41 public extern string FcConfigFilename (string path); 42 43 [CCode (cname = "find_font")] 44 public extern string? find_font (FcConfig* font_config, string characters); 45 46 [CCode (cname = "find_font_family")] 47 public extern string? find_font_family (FcConfig* font_config, string characters); 48 49 [CCode (cname = "find_font_file")] 50 public extern string? find_font_file (FcConfig* font_config, string font_name); 51 52 namespace BirdFont { 53 54 // TODO: use font config 55 public class FallbackFont : GLib.Object { 56 Gee.ArrayList<File> font_directories; 57 58 FontFace* default_font = null; 59 public static FcConfig* font_config = null; 60 static bool font_config_started = false; 61 62 string default_font_file_name = "Roboto-Regular.ttf"; 63 string default_font_family_name = "Roboto"; 64 65 Gee.HashMap<unichar, CachePair> glyphs; 66 Gee.ArrayList<CachePair> cached; 67 68 public int max_cached_fonts = 300; 69 70 string? default_font_file = null; 71 72 public FallbackFont () { 73 string home = Environment.get_home_dir (); 74 font_directories = new Gee.ArrayList<File> (); 75 76 if (!font_config_started) { 77 font_config_started = true; 78 79 IdleSource idle = new IdleSource (); 80 idle.set_callback (() => { 81 Task t = new Task (init_font_config); 82 MainWindow.native_window.run_non_blocking_background_thread (t); 83 return false; 84 }); 85 idle.attach (null); 86 } 87 88 add_font_folder ("/usr/share/fonts/"); 89 add_font_folder ("/usr/local/share/fonts/"); 90 add_font_folder (home + "/.local/share/fonts"); 91 add_font_folder (home + "/.fonts"); 92 add_font_folder ("C:\\Windows\\Fonts"); 93 add_font_folder (home + "/Library/Fonts"); 94 add_font_folder ("/Library/Fonts"); 95 add_font_folder ("/Network/Library/Fonts"); 96 add_font_folder ("/System/Library/Fonts"); 97 add_font_folder ("/System Folder/Fonts"); 98 99 glyphs = new Gee.HashMap<unichar, CachePair> (); 100 cached = new Gee.ArrayList<CachePair> (); 101 102 open_default_font (); 103 } 104 105 ~FallbackFont () { 106 if (default_font != null) { 107 close_font (default_font); 108 } 109 } 110 111 public void init_font_config () { 112 print("init_font_config\n"); 113 FcConfig* config; 114 115 #if MAC 116 config = FcConfigCreate(); 117 118 string bundle = (!) BirdFont.get_settings_directory ().get_path (); 119 FcConfigSetSysRoot(config, bundle); 120 121 string path = FcConfigFilename((!) SearchPaths.search_file(null, "fontconfig.settings").get_path ()); 122 bool loaded = FcConfigParseAndLoad(config, path, true); 123 124 if (!loaded) { 125 warning ("Cannot load fontconfig."); 126 } 127 128 FcConfigSetCurrent (config); 129 #else 130 config = FcInitLoadConfigAndFonts (); 131 #endif 132 133 IdleSource idle = new IdleSource (); 134 135 idle.set_callback (() => { 136 font_config = config; 137 print("Fontconfog loaded."); 138 return false; 139 }); 140 idle.attach (null); 141 } 142 143 public Font get_single_glyph_font (unichar c) { 144 Font f; 145 unichar last; 146 CachePair p; 147 148 if (likely (glyphs.has_key (c))) { 149 p = glyphs.get (c); 150 151 if (p.referenced < int.MAX) { 152 p.referenced++; 153 } 154 155 return p.font; 156 } 157 158 // remove glyphs from cache if it is full 159 if (cached.size > max_cached_fonts - 100) { 160 161 cached.sort ((a, b) => { 162 CachePair pa = (CachePair) a; 163 CachePair pb = (CachePair) b; 164 return pb.referenced - pa.referenced; 165 }); 166 167 int j = 0; 168 for (int i = cached.size - 1; i > 0; i--) { 169 if (j > 100) { 170 break; 171 } 172 173 j++; 174 175 last = cached.get (i).character; 176 glyphs.unset (last); 177 cached.remove_at (i); 178 } 179 } 180 181 f = get_single_fallback_glyph_font (c); 182 p = new CachePair (f, c); 183 184 glyphs.set (c, p); 185 cached.add (p); 186 187 return (Font) f; 188 } 189 190 Font get_single_fallback_glyph_font (unichar c) { 191 string? font_file; 192 BirdFontFile bf_parser; 193 Font bf_font; 194 StringBuilder? glyph_data; 195 FontFace* font; 196 197 bf_font = new Font (); 198 font_file = null; 199 glyph_data = null; 200 201 // don't use fallback font in private use area 202 if (0xe000 <= c <= 0xf8ff) { 203 return bf_font; 204 } 205 206 // control characters 207 if (c <= 0x001f || (0x007f <= c <= 0x008d)) { 208 return bf_font; 209 } 210 211 // check if glyph is available in roboto 212 if (default_font != null) { 213 glyph_data = get_glyph_in_font ((!) default_font, c); 214 } 215 216 // use fontconfig to find a fallback font 217 if (glyph_data == null) { 218 font_file = find_font (font_config, (!) c.to_string ()); 219 if (font_file != null) { 220 font = open_font ((!) font_file); 221 glyph_data = get_glyph_in_font (font, c); 222 close_font (font); 223 } 224 } 225 226 if (glyph_data != null) { 227 bf_parser = new BirdFontFile (bf_font); 228 bf_parser.load_data (((!) glyph_data).str); 229 } 230 231 return bf_font; 232 } 233 234 public StringBuilder? get_glyph_in_font (FontFace* font, unichar c) { 235 StringBuilder? glyph_data = null; 236 GlyphCollection gc; 237 238 gc = new GlyphCollection (c, (!)c.to_string ()); 239 glyph_data = load_glyph (font, (uint) c); 240 241 return glyph_data; 242 } 243 244 void add_font_folder (string f) { 245 File folder = File.new_for_path (f); 246 FileInfo? file_info; 247 string fn; 248 string file_attributes; 249 try { 250 if (folder.query_exists ()) { 251 font_directories.add (folder); 252 253 file_attributes = FileAttribute.STANDARD_NAME; 254 file_attributes += ","; 255 file_attributes += FileAttribute.STANDARD_TYPE; 256 var enumerator = folder.enumerate_children (file_attributes, 0); 257 258 while ((file_info = enumerator.next_file ()) != null) { 259 fn = ((!) file_info).get_name (); 260 261 if (((!)file_info).get_file_type () == FileType.DIRECTORY) { 262 add_font_folder ((!) get_child (folder, fn).get_path ()); 263 } 264 } 265 } 266 } catch (GLib.Error e) { 267 warning (e.message); 268 } 269 } 270 271 File search_font_file (string font_file) { 272 File d, f; 273 274 for (int i = font_directories.size - 1; i >= 0; i--) { 275 d = font_directories.get (i); 276 f = get_child (d, font_file); 277 278 if (f.query_exists ()) { 279 return f; 280 } 281 } 282 283 warning (@"The font $font_file not found"); 284 return File.new_for_path (font_file); 285 } 286 287 public string? get_default_font_file () { 288 File font_file; 289 string? fn = null; 290 291 if (likely (default_font_file != null)) { 292 return default_font_file; 293 } 294 295 font_file = SearchPaths.search_file (null, default_font_file_name); 296 297 if (font_file.query_exists ()) { 298 fn = (!) font_file.get_path (); 299 } else { 300 font_file = search_font_file (default_font_file_name); 301 302 if (font_file.query_exists ()) { 303 fn = (!) font_file.get_path (); 304 } else { 305 fn = find_font_file (font_config, default_font_family_name); 306 } 307 } 308 309 if (likely (fn != null)) { 310 default_font_file = fn; 311 return fn; 312 } 313 314 warning(default_font_family_name + " not found"); 315 return null; 316 } 317 318 void open_default_font () { 319 string? fn = get_default_font_file (); 320 321 if (fn != null) { 322 default_font = open_font ((!) fn); 323 } 324 } 325 326 class CachePair : GLib.Object { 327 public Font font; 328 public unichar character; 329 public int referenced = 1; 330 331 public CachePair (Font f, unichar c) { 332 font = f; 333 character = c; 334 } 335 } 336 } 337 338 } 339