Clone in a terminal:
FallbackFont.vala
/*
    Copyright (C) 2015 Johan Mattsson

    This library is free software; you can redistribute it and/or modify 
    it under the terms of the GNU Lesser General Public License as 
    published by the Free Software Foundation; either version 3 of the 
    License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful, but 
    WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
    Lesser General Public License for more details.
*/

using Gee;
using Sqlite;
using Bird;

namespace BirdFont {

public class FallbackFont : GLib.Object {
	static unowned Database db;
	static Database? database = null;

	Gee.ArrayList<FontFace> fallback_fonts;
	Gee.ArrayList<File> font_directories;

	public FallbackFont () {	
		string home = Environment.get_home_dir ();
		
		fallback_fonts = new Gee.ArrayList<FontFace> ();
		font_directories = new Gee.ArrayList<File> ();
		
		add_font_folder ("/usr/share/fonts/");
		add_font_folder ("/usr/local/share/fonts/");
		add_font_folder (home + "/.local/share/fonts");
		add_font_folder ("C:\\Windows\\Fonts");
		
		open_fallback_fonts ();
	}

	~FallbackFont () {
		foreach (FontFace f in fallback_fonts) {
			close_font (f);
		}
	}
	
	void open_fallback_fonts () {
		add_font ("times.ttf");
		add_font ("verdana.ttf");
		add_font ("arial.ttf");
		add_font ("calibri.ttf");
		
		add_font ("Ubuntu-R.ttf");
		
		add_font ("DroidKufi.ttf");
		add_font ("DroidSansGeorgian.ttf");
		add_font ("DroidSansHebrew.ttf");
		add_font ("DroidNaskh.ttf");
		add_font ("DroidSansJapanese.ttf");
		add_font ("DroidSansArabic.ttf");
		add_font ("DroidSansArmenian.ttf");
		add_font ("DroidSans.ttf");
		add_font ("DroidSansEthiopic.ttf");
		add_font ("DroidSansFallbackFull.ttf");

		add_font ("Roboto-Regular.ttf");
	}

	void add_font (string font) {
		File f = find_font_file (font);
		
		if (f.query_exists ()) {
			add_font_file (f);
		}
	}

	void add_font_file (File font_file) {
		FontFace f = open_font ((!) font_file.get_path ());
		fallback_fonts.add (f);
	}

	File find_font_file (string font_file) {
		File d, f;
		
		for (int i = font_directories.size - 1; i >= 0; i--) {
			d = font_directories.get (i);
			f = get_child (d, font_file);
			
			if (f.query_exists ()) {
				return f;
			}
		}
		
		return File.new_for_path (font_file);
	}
		
	void add_font_folder (string f) {
		File folder = File.new_for_path (f);
		FileInfo? file_info;
		string fn;
		
		try {
			if (folder.query_exists ()) {
				font_directories.add (folder);
				
				var enumerator = folder.enumerate_children (FileAttribute.STANDARD_NAME + "," + FileAttribute.STANDARD_TYPE, 0);
				
				while ((file_info = enumerator.next_file ()) != null) {
					fn = ((!) file_info).get_name ();

					if (((!)file_info).get_file_type () == FileType.DIRECTORY) {
						add_font_folder ((!) get_child (folder, fn).get_path ());
					}
				}
			}
		} catch (GLib.Error e) {
			warning (e.message);
		}
	}
	
	public Glyph get_glyph (unichar c) {
		Glyph? g;
		FontFace f;
		
		for (int i = fallback_fonts.size - 1; i >= 0; i--) {
			f = fallback_fonts.get (i);
			g = get_glyph_in_font (f, c);
			
			if (g != null) {
				return (!) g;
			}
		}
		
		return new Glyph ("");
	}
	
	public Glyph? get_glyph_in_font (FontFace font, unichar c) {
		StringBuilder? glyph_data;
		GlyphCollection gc;
		XmlParser parser;
		BirdFontFile bf_parser;
		
		gc = new GlyphCollection (c, (!)c.to_string ());		
		glyph_data = load_glyph (font, (uint) c);
		
		if (glyph_data == null) {
			return null;
		}
		
		parser = new XmlParser (((!) glyph_data).str);	
		bf_parser = new BirdFontFile (new Font ());
		
		bf_parser.parse_glyph (parser.get_root_tag (),
			gc, gc.get_name (), gc.get_unicode_character (), 0, false);
		
		return gc.get_current ();
	}

	public File get_database_file () {
		return SearchPaths.find_file (null, "fallback-font.sqlite");
	}
	
	public File get_new_database_file () {
		string? fn = BirdFont.get_argument ("--fallback-font");
		
		if (fn != null && ((!) fn) != "") {
			return File.new_for_path ((!) fn);
		}
		
		return File.new_for_path ("fallback-font.sqlite");
	}
		
	public void generate_fallback_font () {
		File f = get_new_database_file ();
		string? fonts = BirdFont.get_argument ("--fonts");
		string fallback;
		
		if (fonts == null) {
			stderr.printf ("Add a list of fonts to use as fallback to the \"--fonts\" argument.\n");
			stderr.printf ("Separate each font file with \":\"\n");
			return;
		}
		
		fallback = (!) fonts;
		
		stdout.printf ("Generating fallback font: %s\n", (!) f.get_path ());
		
		try {
			if (f.query_exists ()) {
				f.delete ();
			}
			
			open_database (f);
			create_tables ();
			
			foreach (string font in fallback.split (":")) {
				add_font (font);
			}		
		} catch (GLib.Error e) {
			warning (e.message);
		}
		
	}

/* //FIXME:DELETE
	public void add_font (string font_file) {
		Font font;
		Glyph g;
		BirdFontFile bf;
		File single_glyph_font;
		
		font = new Font ();
		single_glyph_font = File.new_for_path ("/tmp/fallback_glyph.bf");
		
		font.set_file (font_file);
		if (!font.load ()) {
			stderr.printf ("Failed to load font: " + font_file);
			return;
		}
		
		for (int i = 0; i < font.length (); i++) {
			g = (!) font.get_glyph_indice (i);
			bf = new BirdFontFile (font);
		}
	} */

	public void open_database (File db_file) {
		int rc = Database.open ((!) db_file.get_path (), out database);

		db = (!) database;

		if (rc != Sqlite.OK) {
			stderr.printf ("Can't open database: %d, %s\n", rc, db.errmsg ());
		}
	}

	public void create_tables () {
		int ec;
		string? errmsg;
		string create_font_table = """
			CREATE TABLE FallbackFont (
				unicode        INTEGER     PRIMARY KEY    NOT NULL,
				font_data      TEXT                       NOT NULL
			);
		""";
		
		ec = db.exec (create_font_table, null, out errmsg);
		if (ec != Sqlite.OK) {
			warning ("Error: %s\n", (!) errmsg);
		}
	}
}

}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112