1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
//! The `font::Id` and `font::Map` types.
use crate::text::{Font, FontCollection};
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};
/// A type-safe wrapper around the `FontId`.
///
/// This is used as both:
///
/// - The key for the `font::Map`'s inner `HashMap`.
/// - The `font_id` field for the rusttype::gpu_cache::Cache.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Id(usize);
/// A collection of mappings from `font::Id`s to `rusttype::Font`s.
#[derive(Debug)]
pub struct Map {
next_index: usize,
map: HashMap<Id, Font>,
}
/// An iterator yielding an `Id` for each new `rusttype::Font` inserted into the `Map` via the
/// `insert_collection` method.
pub struct NewIds {
index_range: std::ops::Range<usize>,
}
/// Yields the `Id` for each `Font` within the `Map`.
#[derive(Clone)]
pub struct Ids<'a> {
keys: std::collections::hash_map::Keys<'a, Id, Font>,
}
/// Returned when loading new fonts from file or bytes.
#[derive(Debug)]
pub enum Error {
/// Some error occurred while loading a `FontCollection` from a file.
Io(std::io::Error),
/// No `Font`s could be yielded from the `FontCollection`.
NoFont,
}
/// The name of the default directory that is searched for fonts.
pub const DEFAULT_DIRECTORY_NAME: &str = "fonts";
impl Id {
/// Returns the inner `usize` from the `Id`.
pub fn index(self) -> usize {
self.0
}
}
impl Map {
/// Construct the new, empty `Map`.
pub fn new() -> Self {
Map {
next_index: 0,
map: HashMap::default(),
}
}
/// Borrow the `rusttype::Font` associated with the given `font::Id`.
pub fn get(&self, id: Id) -> Option<&Font> {
self.map.get(&id)
}
/// Adds the given `rusttype::Font` to the `Map` and returns a unique `Id` for it.
pub fn insert(&mut self, font: Font) -> Id {
let index = self.next_index;
self.next_index = index.wrapping_add(1);
let id = Id(index);
self.map.insert(id, font);
id
}
/// Insert a single `Font` into the map by loading it from the given file path.
pub fn insert_from_file<P>(&mut self, path: P) -> Result<Id, Error>
where
P: AsRef<std::path::Path>,
{
let font = from_file(path)?;
Ok(self.insert(font))
}
// /// Adds each font in the given `rusttype::FontCollection` to the `Map` and returns an
// /// iterator yielding a unique `Id` for each.
// pub fn insert_collection(&mut self, collection: FontCollection) -> NewIds {
// let start_index = self.next_index;
// let mut end_index = start_index;
// for index in 0.. {
// match collection.font_at(index) {
// Some(font) => {
// self.insert(font);
// end_index += 1;
// }
// None => break,
// }
// }
// NewIds { index_range: start_index..end_index }
// }
/// Produces an iterator yielding the `Id` for each `Font` within the `Map`.
pub fn ids(&self) -> Ids {
Ids {
keys: self.map.keys(),
}
}
}
/// Produce a unique ID for the given font.
pub fn id(font: &Font) -> Id {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for name in font.font_name_strings() {
name.hash(&mut hasher);
}
Id((hasher.finish() % std::usize::MAX as u64) as usize)
}
/// Load a `FontCollection` from a file at a given path.
pub fn collection_from_file<P>(path: P) -> Result<FontCollection, std::io::Error>
where
P: AsRef<std::path::Path>,
{
use std::io::Read;
let path = path.as_ref();
let mut file = std::fs::File::open(path)?;
let mut file_buffer = Vec::new();
file.read_to_end(&mut file_buffer)?;
Ok(FontCollection::from_bytes(file_buffer)?)
}
/// Load a single `Font` from a file at the given path.
pub fn from_file<P>(path: P) -> Result<Font, Error>
where
P: AsRef<std::path::Path>,
{
let collection = collection_from_file(path)?;
collection.into_font().or(Err(Error::NoFont))
}
/// Load the default notosans font.
///
/// This function is only available if the `notosans` feature is enabled, which it is by default.
#[cfg(feature = "notosans")]
pub fn default_notosans() -> Font {
let collection = FontCollection::from_bytes(notosans::REGULAR_TTF)
.expect("failed to load the `notosans::REGULAR_TTF` font collection");
collection
.into_font()
.expect("the `notosans::REGULAR_TTF` font collection contained no fonts")
}
/// The directory that is searched for default fonts.
pub fn default_directory(assets: &Path) -> PathBuf {
assets.join(DEFAULT_DIRECTORY_NAME)
}
/// Load the default font.
///
/// If the `notosans` feature is enabled, this will return the font loaded from
/// `notosans::REGULAR_TTF`.
///
/// Otherwise this will attempt to locate the `assets/fonts` directory. If the directory exists,
/// the first font that is found will be loaded. If no fonts are found, an error is returned.
#[allow(unreachable_code, unused_variables)]
pub fn default(assets: &Path) -> Result<Font, Error> {
#[cfg(feature = "notosans")]
{
return Ok(default_notosans());
}
// Find a font in `assets/fonts`.
let fonts_dir = default_directory(assets);
if fonts_dir.exists() && fonts_dir.is_dir() {
for res in crate::io::walk_dir(&fonts_dir) {
let entry = match res {
Ok(e) => e,
Err(_) => continue,
};
match from_file(entry.path()) {
Err(_) => continue,
Ok(font) => return Ok(font),
}
}
}
Err(Error::NoFont)
}
impl Iterator for NewIds {
type Item = Id;
fn next(&mut self) -> Option<Self::Item> {
self.index_range.next().map(|i| Id(i))
}
}
impl<'a> Iterator for Ids<'a> {
type Item = Id;
fn next(&mut self) -> Option<Self::Item> {
self.keys.next().map(|&id| id)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
impl std::error::Error for Error {
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
Error::Io(ref e) => Some(e),
Error::NoFont => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match *self {
Error::Io(ref e) => std::fmt::Display::fmt(e, f),
Error::NoFont => write!(f, "No `Font` found in the loaded `FontCollection`."),
}
}
}