.
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