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.
Create fontconfig cache in a background thread
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 extern struct FcConfig { 20 } 21 22 [CCode (cname = "FcInitLoadConfigAndFonts")] 23 extern FcConfig* FcInitLoadConfigAndFonts (); 24 25 [CCode (cname = "find_font")] 26 extern string? find_font (FcConfig* font_config, string characters); 27 28 [CCode (cname = "find_font_file")] 29 extern string? find_font_file (FcConfig* font_config, string font_name); 30 31 namespace BirdFont { 32 33 // TODO: use font config 34 public class FallbackFont : GLib.Object { 35 Gee.ArrayList<File> font_directories; 36 37 FcConfig* font_config = null; 38 FontFace* default_font = null; 39 static bool font_config_stated = false; 40 41 string default_font_file_name = "Roboto-Regular.ttf"; 42 string default_font_family_name = "Roboto"; 43 44 Gee.HashMap<unichar, CachePair> glyphs; 45 Gee.ArrayList<CachePair> cached; 46 47 public int max_cached_fonts = 300; 48 49 public FallbackFont () { 50 string home = Environment.get_home_dir (); 51 52 font_directories = new Gee.ArrayList<File> (); 53 54 if (!font_config_stated) { 55 font_config_stated = true; 56 57 IdleSource idle = new IdleSource (); 58 idle.set_callback (() => { 59 Task t = new Task (); 60 t.task.connect (init_font_config); 61 MainWindow.native_window.run_non_blocking_background_thread (t); 62 return false; 63 }); 64 idle.attach (null); 65 } 66 67 add_font_folder ("/usr/share/fonts/"); 68 add_font_folder ("/usr/local/share/fonts/"); 69 add_font_folder (home + "/.local/share/fonts"); 70 add_font_folder (home + "/.fonts"); 71 add_font_folder ("C:\\Windows\\Fonts"); 72 add_font_folder (home + "/Library/Fonts"); 73 add_font_folder ("/Library/Fonts"); 74 add_font_folder ("/Network/Library/Fonts"); 75 add_font_folder ("/System/Library/Fonts"); 76 add_font_folder ("/System Folder/Fonts"); 77 78 glyphs = new Gee.HashMap<unichar, CachePair> (); 79 cached = new Gee.ArrayList<CachePair> (); 80 81 open_default_font (); 82 } 83 84 ~FallbackFont () { 85 if (default_font != null) { 86 close_font (default_font); 87 } 88 } 89 90 public void init_font_config () { 91 FcConfig* fc = FcInitLoadConfigAndFonts (); 92 IdleSource idle = new IdleSource (); 93 94 idle.set_callback (() => { 95 font_config = fc; 96 return false; 97 }); 98 idle.attach (null); 99 } 100 101 public Font get_single_glyph_font (unichar c) { 102 Font f; 103 unichar last; 104 CachePair p; 105 106 if (likely (glyphs.has_key (c))) { 107 p = glyphs.get (c); 108 109 if (p.referenced < int.MAX) { 110 p.referenced++; 111 } 112 113 return p.font; 114 } 115 116 // remove glyphs from cache if it is full 117 if (cached.size > max_cached_fonts - 100) { 118 119 cached.sort ((a, b) => { 120 CachePair pa = (CachePair) a; 121 CachePair pb = (CachePair) b; 122 return pb.referenced - pa.referenced; 123 }); 124 125 int j = 0; 126 for (int i = cached.size - 1; i > 0; i--) { 127 if (j > 100) { 128 break; 129 } 130 131 j++; 132 133 last = cached.get (i).character; 134 glyphs.unset (last); 135 cached.remove_at (i); 136 } 137 } 138 139 f = get_single_fallback_glyph_font (c); 140 p = new CachePair (f, c); 141 142 glyphs.set (c, p); 143 cached.add (p); 144 145 return (Font) f; 146 } 147 148 Font get_single_fallback_glyph_font (unichar c) { 149 string? font_file; 150 BirdFontFile bf_parser; 151 Font bf_font; 152 StringBuilder? glyph_data; 153 FontFace* font; 154 155 bf_font = new Font (); 156 font_file = null; 157 glyph_data = null; 158 159 // don't use fallback font in private use area 160 if (0xe000 <= c <= 0xf8ff) { 161 return bf_font; 162 } 163 164 // control characters 165 if (c <= 0x001f || (0x007f <= c <= 0x008d)) { 166 return bf_font; 167 } 168 169 // check if glyph is available in roboto 170 if (default_font != null) { 171 glyph_data = get_glyph_in_font ((!) default_font, c); 172 } 173 174 // use fontconfig to find a fallback font 175 if (glyph_data == null) { 176 font_file = find_font (font_config, (!) c.to_string ()); 177 if (font_file != null) { 178 font = open_font ((!) font_file); 179 glyph_data = get_glyph_in_font (font, c); 180 close_font (font); 181 } 182 } 183 184 if (glyph_data != null) { 185 bf_parser = new BirdFontFile (bf_font); 186 bf_parser.load_data (((!) glyph_data).str); 187 } 188 189 return bf_font; 190 } 191 192 public StringBuilder? get_glyph_in_font (FontFace* font, unichar c) { 193 StringBuilder? glyph_data = null; 194 GlyphCollection gc; 195 196 gc = new GlyphCollection (c, (!)c.to_string ()); 197 glyph_data = load_glyph (font, (uint) c); 198 199 return glyph_data; 200 } 201 202 void add_font_folder (string f) { 203 File folder = File.new_for_path (f); 204 FileInfo? file_info; 205 string fn; 206 string file_attributes; 207 try { 208 if (folder.query_exists ()) { 209 font_directories.add (folder); 210 211 file_attributes = FileAttribute.STANDARD_NAME; 212 file_attributes += ","; 213 file_attributes += FileAttribute.STANDARD_TYPE; 214 var enumerator = folder.enumerate_children (file_attributes, 0); 215 216 while ((file_info = enumerator.next_file ()) != null) { 217 fn = ((!) file_info).get_name (); 218 219 if (((!)file_info).get_file_type () == FileType.DIRECTORY) { 220 add_font_folder ((!) get_child (folder, fn).get_path ()); 221 } 222 } 223 } 224 } catch (GLib.Error e) { 225 warning (e.message); 226 } 227 } 228 229 File search_font_file (string font_file) { 230 File d, f; 231 232 for (int i = font_directories.size - 1; i >= 0; i--) { 233 d = font_directories.get (i); 234 f = get_child (d, font_file); 235 236 if (f.query_exists ()) { 237 return f; 238 } 239 } 240 241 warning (@"The font $font_file not found"); 242 return File.new_for_path (font_file); 243 } 244 245 void open_default_font () { 246 File font_file; 247 string? fn = null; 248 249 font_file = SearchPaths.search_file (null, default_font_file_name); 250 251 if (font_file.query_exists ()) { 252 fn = (!) font_file.get_path (); 253 } else { 254 font_file = search_font_file (default_font_file_name); 255 256 if (font_file.query_exists ()) { 257 fn = (!) font_file.get_path (); 258 } else { 259 fn = find_font_file (font_config, default_font_family_name); 260 } 261 } 262 263 if (fn != null) { 264 default_font = open_font ((!) fn); 265 } 266 } 267 268 class CachePair : GLib.Object { 269 public Font font; 270 public unichar character; 271 public int referenced = 1; 272 273 public CachePair (Font f, unichar c) { 274 font = f; 275 character = c; 276 } 277 } 278 } 279 280 } 281