Skip to content

Commit

Permalink
feat: support for custom fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
Cubxity committed May 29, 2023
1 parent a0dbbfd commit a1372d1
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 30 deletions.
30 changes: 30 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ chrono = "0.4"
png = "0.17"
log = "0.4"
env_logger = "0.10"
dirs = "5.0"
walkdir = "2.3"
memmap2 = "0.5"

typst = { git = "https://github.com/typst/typst" }
typst-library = { git = "https://github.com/typst/typst" }
Expand All @@ -42,7 +45,8 @@ comemo = "0.3"
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = ["custom-protocol"]
default = ["custom-protocol", "embed-fonts"]
# this feature is used used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = ["tauri/custom-protocol"]
embed-fonts = []
33 changes: 6 additions & 27 deletions src-tauri/src/engine/engine.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
use crate::engine::{FontSearcher, FontSlot};
use comemo::Prehashed;
use typst::eval::Library;
use typst::font::{Font, FontBook};
use typst::util::Buffer;

pub struct TypstEngine {
pub library: Prehashed<Library>,
pub fontbook: Prehashed<FontBook>,
pub fonts: Vec<Font>,
pub fonts: Vec<FontSlot>,
}

impl TypstEngine {
pub fn new() -> Self {
// https://github.com/typst/typst/blob/085282c138899dd5aaa06bc6ae7bd2f79d75d7e1/cli/src/main.rs#L695
const EMBEDDED_FONTS: [&[u8]; 10] = [
include_bytes!("../../assets/fonts/LinLibertine_R.ttf"),
include_bytes!("../../assets/fonts/LinLibertine_RB.ttf"),
include_bytes!("../../assets/fonts/LinLibertine_RBI.ttf"),
include_bytes!("../../assets/fonts/LinLibertine_RI.ttf"),
include_bytes!("../../assets/fonts/NewCMMath-Book.otf"),
include_bytes!("../../assets/fonts/NewCMMath-Regular.otf"),
include_bytes!("../../assets/fonts/DejaVuSansMono.ttf"),
include_bytes!("../../assets/fonts/DejaVuSansMono-Bold.ttf"),
include_bytes!("../../assets/fonts/DejaVuSansMono-Oblique.ttf"),
include_bytes!("../../assets/fonts/DejaVuSansMono-BoldOblique.ttf"),
];

let mut fontbook = FontBook::new();
let mut fonts = vec![];

for file in EMBEDDED_FONTS {
for font in Font::iter(Buffer::from_static(file)) {
fontbook.push(font.info().clone());
fonts.push(font);
}
}
let mut searcher = FontSearcher::new();
searcher.search(&[]);

Self {
library: Prehashed::new(typst_library::build()),
fontbook: Prehashed::new(fontbook),
fonts,
fontbook: Prehashed::new(searcher.book),
fonts: searcher.fonts,
}
}
}
153 changes: 153 additions & 0 deletions src-tauri/src/engine/font.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use log::{debug, trace};
use memmap2::Mmap;
use once_cell::sync::OnceCell;
use std::fs::File;
use std::path::{Path, PathBuf};
use typst::font::{Font, FontBook, FontInfo};
use typst::util::Buffer;
use walkdir::WalkDir;

// Taken from typst-cli

/// Holds details about the location of a font and lazily the font itself.
pub struct FontSlot {
pub path: PathBuf,
pub index: u32,
pub font: OnceCell<Option<Font>>,
}

pub struct FontSearcher {
pub book: FontBook,
pub fonts: Vec<FontSlot>,
}

impl FontSearcher {
/// Create a new, empty system searcher.
pub fn new() -> Self {
Self {
book: FontBook::new(),
fonts: vec![],
}
}

/// Search everything that is available.
pub fn search(&mut self, font_paths: &[PathBuf]) {
self.search_system();

#[cfg(feature = "embed-fonts")]
self.search_embedded();

for path in font_paths {
self.search_dir(path);
}

debug!("discovered {} fonts", self.fonts.len());
}

/// Add fonts that are embedded in the binary.
#[cfg(feature = "embed-fonts")]
fn search_embedded(&mut self) {
let mut search = |bytes: &'static [u8]| {
let buffer = Buffer::from_static(bytes);
for (i, font) in Font::iter(buffer).enumerate() {
self.book.push(font.info().clone());
self.fonts.push(FontSlot {
path: PathBuf::new(),
index: i as u32,
font: OnceCell::from(Some(font)),
});
}
};

// Embed default fonts.
search(include_bytes!("../../assets/fonts/LinLibertine_R.ttf"));
search(include_bytes!("../../assets/fonts/LinLibertine_RB.ttf"));
search(include_bytes!("../../assets/fonts/LinLibertine_RBI.ttf"));
search(include_bytes!("../../assets/fonts/LinLibertine_RI.ttf"));
search(include_bytes!("../../assets/fonts/NewCMMath-Book.otf"));
search(include_bytes!("../../assets/fonts/NewCMMath-Regular.otf"));
search(include_bytes!("../../assets/fonts/DejaVuSansMono.ttf"));
search(include_bytes!("../../assets/fonts/DejaVuSansMono-Bold.ttf"));
search(include_bytes!(
"../../assets/fonts/DejaVuSansMono-Oblique.ttf"
));
search(include_bytes!(
"../../assets/fonts/DejaVuSansMono-BoldOblique.ttf"
));
}

/// Search for fonts in the linux system font directories.
#[cfg(all(unix, not(target_os = "macos")))]
fn search_system(&mut self) {
self.search_dir("/usr/share/fonts");
self.search_dir("/usr/local/share/fonts");

if let Some(dir) = dirs::font_dir() {
self.search_dir(dir);
}
}

/// Search for fonts in the macOS system font directories.
#[cfg(target_os = "macos")]
fn search_system(&mut self) {
self.search_dir("/Library/Fonts");
self.search_dir("/Network/Library/Fonts");
self.search_dir("/System/Library/Fonts");

if let Some(dir) = dirs::font_dir() {
self.search_dir(dir);
}
}

/// Search for fonts in the Windows system font directories.
#[cfg(windows)]
fn search_system(&mut self) {
let windir = std::env::var("WINDIR").unwrap_or_else(|_| "C:\\Windows".to_string());

self.search_dir(Path::new(&windir).join("Fonts"));

if let Some(roaming) = dirs::config_dir() {
self.search_dir(roaming.join("Microsoft\\Windows\\Fonts"));
}

if let Some(local) = dirs::cache_dir() {
self.search_dir(local.join("Microsoft\\Windows\\Fonts"));
}
}

/// Search for all fonts in a directory recursively.
fn search_dir(&mut self, path: impl AsRef<Path>) {
for entry in WalkDir::new(path)
.follow_links(true)
.sort_by(|a, b| a.file_name().cmp(b.file_name()))
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if matches!(
path.extension().and_then(|s| s.to_str()),
Some("ttf" | "otf" | "TTF" | "OTF" | "ttc" | "otc" | "TTC" | "OTC"),
) {
self.search_file(path);
}
}
}

/// Index the fonts in the file at the given path.
fn search_file(&mut self, path: impl AsRef<Path>) {
trace!("searching font file {:?}", path.as_ref());
let path = path.as_ref();
if let Ok(file) = File::open(path) {
if let Ok(mmap) = unsafe { Mmap::map(&file) } {
for (i, info) in FontInfo::iter(&mmap).enumerate() {
self.book.push(info);
self.fonts.push(FontSlot {
path: path.into(),
index: i as u32,
font: OnceCell::new(),
});
}
}
}
}
}
2 changes: 2 additions & 0 deletions src-tauri/src/engine/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod engine;
mod font;

pub use engine::*;
pub use font::*;
8 changes: 7 additions & 1 deletion src-tauri/src/project/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ impl World for ProjectWorld {
}

fn font(&self, id: usize) -> Option<Font> {
self.engine.fonts.get(id).cloned()
let slot = &self.engine.fonts[id];
slot.font
.get_or_init(|| {
let data = fs::read(&slot.path).map(Buffer::from).ok()?;
Font::new(data, slot.index)
})
.clone()
}

fn file(&self, path: &Path) -> FileResult<Buffer> {
Expand Down
5 changes: 4 additions & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"distDir": "../build",
"devPath": "http:https://localhost:5173",
"beforeDevCommand": "pnpm run dev",
"beforeBuildCommand": "pnpm run build"
"beforeBuildCommand": "pnpm run build",
"features": [
"embed-fonts"
]
},
"tauri": {
"bundle": {
Expand Down

0 comments on commit a1372d1

Please sign in to comment.