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.
Store fallback font in database
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 using Sqlite; 17 using Bird; 18 19 namespace BirdFont { 20 21 // TODO: use font config 22 public class FallbackFont : GLib.Object { 23 static unowned Database db; 24 static Database? database = null; 25 26 Gee.ArrayList<File> fallback_fonts; 27 Gee.ArrayList<File> font_directories; 28 29 public FallbackFont () { 30 string home = Environment.get_home_dir (); 31 32 fallback_fonts = new Gee.ArrayList<File> (); 33 font_directories = new Gee.ArrayList<File> (); 34 35 open_database (); 36 37 add_font_folder ("/usr/share/fonts/"); 38 add_font_folder ("/usr/local/share/fonts/"); 39 add_font_folder (home + "/.local/share/fonts"); 40 add_font_folder (home + "/.fonts"); 41 add_font_folder ("C:\\Windows\\Fonts"); 42 //FIXME: MAC 43 44 add_fallback_fonts (); 45 } 46 47 void add_fallback_fonts () { 48 add_font ("times.ttf"); 49 add_font ("arial.ttf"); 50 add_font ("verdana.ttf"); 51 add_font ("calibri.ttf"); 52 53 add_font ("DejaVuSans.ttf"); 54 add_font ("Ubuntu-R.ttf"); 55 56 add_font ("DroidKufi.ttf"); 57 add_font ("DroidSansGeorgian.ttf"); 58 add_font ("DroidSansHebrew.ttf"); 59 add_font ("DroidNaskh.ttf"); 60 add_font ("DroidSansJapanese.ttf"); 61 add_font ("DroidSansArabic.ttf"); 62 add_font ("DroidSansArmenian.ttf"); 63 add_font ("DroidSans.ttf"); 64 add_font ("DroidSansEthiopic.ttf"); 65 add_font ("DroidSansFallbackFull.ttf"); 66 67 add_font ("Roboto-Regular.ttf"); 68 } 69 70 void add_font (string font) { 71 File f = find_font_file (font); 72 73 if (f.query_exists ()) { 74 fallback_fonts.add (f); 75 } 76 } 77 78 File find_font_file (string font_file) { 79 File d, f; 80 81 for (int i = font_directories.size - 1; i >= 0; i--) { 82 d = font_directories.get (i); 83 f = get_child (d, font_file); 84 85 if (f.query_exists ()) { 86 return f; 87 } 88 } 89 90 return File.new_for_path (font_file); 91 } 92 93 void add_font_folder (string f) { 94 File folder = File.new_for_path (f); 95 FileInfo? file_info; 96 string fn; 97 98 try { 99 if (folder.query_exists ()) { 100 font_directories.add (folder); 101 102 var enumerator = folder.enumerate_children (FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE, 0); 103 104 while ((file_info = enumerator.next_file ()) != null) { 105 fn = ((!) file_info).get_name (); 106 107 if (((!)file_info).get_file_type () == FileType.DIRECTORY) { 108 add_font_folder ((!) get_child (folder, fn).get_path ()); 109 } 110 } 111 } 112 } catch (GLib.Error e) { 113 warning (e.message); 114 } 115 } 116 117 public Font get_single_glyph_font (unichar c) { 118 BirdFontFile bf_parser; 119 Font bf_font; 120 StringBuilder? glyph_data; 121 string data; 122 123 glyph_data = find_cached_glyph (c); 124 125 if (glyph_data == null) { 126 print (@"Load from TTF $((!) c.to_string ())\n"); 127 glyph_data = load_glyph_from_ttf (c); 128 } else print (@"Found cached gd $((!) c.to_string ())\n"); 129 130 bf_font = new Font (); 131 132 if (glyph_data != null) { 133 bf_parser = new BirdFontFile (bf_font); 134 data = ((!) glyph_data).str; 135 136 if (data != "") { 137 bf_parser.load_data (data); 138 } 139 } 140 141 return bf_font; 142 } 143 144 public StringBuilder? load_glyph_from_ttf (unichar c) { 145 StringBuilder? glyph_data; 146 string data; 147 148 glyph_data = load_glyph_data_from_ttf (c); 149 150 if (glyph_data != null) { 151 cache_glyph (c, ((!) glyph_data).str); 152 } else { 153 cache_glyph (c, ""); 154 } 155 156 return glyph_data; 157 } 158 159 public StringBuilder? load_glyph_data_from_ttf (unichar c) { 160 File f; 161 FontFace* font; 162 StringBuilder? data = null; 163 164 for (int i = fallback_fonts.size - 1; i >= 0; i--) { 165 f = fallback_fonts.get (i); 166 167 font = open_font ((!) f.get_path ()); 168 data = get_glyph_in_font (font, c); 169 close_font (font); 170 171 if (data != null) { 172 return data; 173 } 174 } 175 176 return null; 177 } 178 179 public StringBuilder? get_glyph_in_font (FontFace font, unichar c) { 180 StringBuilder? glyph_data = null; 181 GlyphCollection gc; 182 Font bf_font = new Font (); 183 184 gc = new GlyphCollection (c, (!)c.to_string ()); 185 glyph_data = load_glyph (font, (uint) c); 186 187 return glyph_data; 188 } 189 190 File get_fallback_database () { 191 File f = BirdFont.get_settings_directory (); 192 return get_child (f, "fallback_font.sqlite"); 193 } 194 195 public void open_database () { 196 File db_file = get_fallback_database (); 197 bool create_table = !db_file.query_exists (); 198 int rc = Database.open ((!) db_file.get_path (), out database); 199 200 db = (!) database; 201 202 if (rc != Sqlite.OK) { 203 stderr.printf ("Can't open database: %d, %s\n", rc, db.errmsg ()); 204 } 205 206 if (create_table) { 207 create_tables (); 208 } 209 } 210 211 public void create_tables () { 212 int ec; 213 string? errmsg; 214 string create_font_table = """ 215 CREATE TABLE FallbackFont ( 216 unicode INTEGER PRIMARY KEY NOT NULL, 217 font_data TEXT NOT NULL 218 ); 219 """; 220 221 ec = db.exec (create_font_table, null, out errmsg); 222 if (ec != Sqlite.OK) { 223 warning ("Error: %s\n", (!) errmsg); 224 } 225 } 226 227 void cache_glyph (unichar c, string glyph_data) { 228 int64 character; 229 int ec; 230 string? errmsg; 231 string insert; 232 233 character = (int64) c; 234 insert = "INSERT INTO FallbackFont (unicode, font_data) " 235 + @"VALUES ('$character', '" + glyph_data.replace ("'", "''") + "');"; 236 237 ec = db.exec (insert, null, out errmsg); 238 if (ec != Sqlite.OK) { 239 warning ("Error: %s\n", (!) errmsg); 240 } 241 } 242 243 StringBuilder? find_cached_glyph (unichar c) { 244 int rc, cols; 245 Statement statement; 246 string select; 247 StringBuilder? font_data = null; 248 249 select = "SELECT font_data FROM FallbackFont " 250 + "WHERE unicode = '" + @"$((int64) c)" + "';"; 251 252 rc = db.prepare_v2 (select, select.length, out statement, null); 253 254 if (rc == Sqlite.OK) { 255 cols = statement.column_count(); 256 257 if (cols != 1) { 258 warning ("Expecting one column."); 259 return font_data; 260 } 261 262 while (true) { 263 rc = statement.step (); 264 265 if (rc == Sqlite.DONE) { 266 break; 267 } else if (rc == Sqlite.ROW) { 268 font_data = new StringBuilder (); 269 ((!) font_data).append (statement.column_text (0)); 270 } else { 271 warning ("Error: %d, %s\n", rc, db.errmsg ()); 272 break; 273 } 274 } 275 } else { 276 warning ("SQL error: %d, %s\n", rc, db.errmsg ()); 277 } 278 279 return font_data; 280 } 281 } 282 283 } 284