Skip to content

Commit

Permalink
feat(loader): support custom font paths (#65)
Browse files Browse the repository at this point in the history
Add -I/--include option to add custom font paths.

Fixes #62.
  • Loading branch information
7sDream authored Nov 15, 2023
1 parent 86fe8a7 commit 18b977b
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Remove dependency of fontconfig and freetype lib
- Add `-vv` option to show font file location and face index
- ASCII mode render result now not narrow (Issue [#61](https://github.com/7sDream/fontfor/issues/61), fixed by PR [#63](https://github.com/7sDream/fontfor/pull/63))
- Support custom font paths (Issue [#62](https://github.com/7sDream/fontfor/issues/62))
- Now release contains ia32/x64/arm64 binary for Windows
- Now release contains x64/arm64 binary for macOS
- Now release contains x64/arm64/armhf binary Linux
Expand Down
7 changes: 7 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use std::path::PathBuf;

use clap::Parser;

use super::one_char::OneChar;
Expand All @@ -36,6 +38,11 @@ pub struct Args {
#[arg(short, long)]
pub tui: bool,

/// Also load fonts in a custom path.
/// This arg can be provided multiple times.
#[arg(short = 'I', long = "include", name = "PATH", action = clap::ArgAction::Append)]
pub custom_font_paths: Vec<PathBuf>,

/// The character
#[arg(name = "CHAR")]
pub char: OneChar,
Expand Down
14 changes: 8 additions & 6 deletions src/loader/face_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@

use std::{borrow::Cow, path::Path};

use fontdb::Source;
use ttf_parser::{
name::{name_id, Table as NameTable},
GlyphId, Language, RawFace,
Language, RawFace,
};

use super::{
cmap::CMapTable,
error::{BROKEN_NAME_TABLE, MISSING_NAME_TABLE, NAME_TAG},
Error, Result, DATABASE,
Error, Result,
};
use crate::loader::database;

/// FaceInfo contains basic font face info like family and name,
/// and pre-located glyph id for target character.
Expand All @@ -40,20 +42,20 @@ pub struct FaceInfo {
pub path: &'static Path,
pub index: u32,

pub gid: GlyphId,
pub gid: u16,
}

impl FaceInfo {
pub fn parse_if_contains(face: &'static fontdb::FaceInfo, c: char) -> Result<Option<Self>> {
let Some((gid, name)) = DATABASE
let Some((gid, name)) = database()
.with_face_data(face.id, |data, index| -> Result<_> {
let rf = RawFace::parse(data, index)?;
let Some(gid) = CMapTable::parse(rf)?.glyph_index(c) else {
return Ok(None);
};

let name = Self::parse_full_name(rf)?;
Ok(Some((gid, name)))
Ok(Some((gid.0, name)))
})
.expect("we only load font from database so it must not None")?
else {
Expand All @@ -71,7 +73,7 @@ impl FaceInfo {
.unwrap_or_else(|| face.post_script_name.as_str().into());
let path = match face.source {
fontdb::Source::File(ref path) => path,
Source::File(ref path) => path,
_ => unreachable!("we only load font file, so source must be File variant"),
};

Expand Down
35 changes: 28 additions & 7 deletions src/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,40 @@ mod face_info;
mod cmap;
mod error;

use once_cell::sync::Lazy;
use std::path::Path;

use fontdb::Database;
use once_cell::sync::OnceCell;

pub use self::{error::Error, face_info::FaceInfo};
pub type Result<T> = std::result::Result<T, Error>;

pub static DATABASE: Lazy<fontdb::Database> = Lazy::new(|| {
let mut db = fontdb::Database::default();
static DATABASE: OnceCell<Database> = OnceCell::new();

pub fn init<I, P>(paths: I)
where
I: IntoIterator<Item = P>,
P: AsRef<Path>,
{
let mut db = Database::default();

db.load_system_fonts();
db
});

pub fn faces_contains(c: char) -> Vec<FaceInfo> {
DATABASE
for path in paths.into_iter() {
db.load_fonts_dir(path)
}

if DATABASE.set(db).is_err() {
panic!("call init more then once")
}
}

pub fn database() -> &'static Database {
DATABASE.get().expect("initialized")
}

pub fn query(c: char) -> Vec<FaceInfo> {
database()
.faces()
.filter_map(|info| {
let face = FaceInfo::parse_if_contains(info, c);
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ use preview::{browser::ServerBuilder as PreviewServerBuilder, terminal::ui::UI};
fn main() {
let argument = args::get();

let font_set = loader::faces_contains(argument.char.0);
loader::init(&argument.custom_font_paths);

let font_set = loader::query(argument.char.0);
let families = family::group_by_family_sort_by_name(&font_set);

if families.is_empty() {
Expand Down
6 changes: 3 additions & 3 deletions src/preview/terminal/ui/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use tui::widgets::ListState;
use super::cache::{CacheKey, GlyphCache, GlyphCanvasShape, RenderType, CHAR_RENDERS, MONO_RENDER};
use crate::{
family::Family,
loader::{FaceInfo, DATABASE},
loader::{self, FaceInfo},
preview::terminal::{render::Render, ui::cache::GlyphParagraph},
rasterizer::{Bitmap, Rasterizer},
};
Expand Down Expand Up @@ -105,14 +105,14 @@ impl<'a> State<'a> {
None
};

DATABASE
loader::database()
.with_face_data(info.id, |data, index| -> Option<Bitmap> {
let mut r = Rasterizer::new(data, index).ok()?;
r.set_pixel_height(height);
if let Some(scale) = scale {
r.set_hscale(scale);
}
r.rasterize(info.gid.0)
r.rasterize(info.gid)
})
.ok_or("Can't read this font file")?
.ok_or("Can't get glyph from this font")
Expand Down

0 comments on commit 18b977b

Please sign in to comment.