mirror of
https://github.com/typst/typst
synced 2025-05-19 11:35:27 +08:00
Use fontdb to search fonts, with basic fontconfig support (#2472)
This commit is contained in:
parent
e686a11ae7
commit
b80382b216
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -786,13 +786,24 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontconfig-parser"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4"
|
||||||
|
dependencies = [
|
||||||
|
"roxmltree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fontdb"
|
name = "fontdb"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38"
|
checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"fontconfig-parser",
|
||||||
"log",
|
"log",
|
||||||
|
"memmap2",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
@ -1458,9 +1469,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
|
checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@ -2821,8 +2832,8 @@ dependencies = [
|
|||||||
"env_proxy",
|
"env_proxy",
|
||||||
"filetime",
|
"filetime",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
"fontdb",
|
||||||
"inferno",
|
"inferno",
|
||||||
"memmap2",
|
|
||||||
"notify",
|
"notify",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"open",
|
"open",
|
||||||
@ -2845,7 +2856,6 @@ dependencies = [
|
|||||||
"typst",
|
"typst",
|
||||||
"typst-library",
|
"typst-library",
|
||||||
"ureq",
|
"ureq",
|
||||||
"walkdir",
|
|
||||||
"xz2",
|
"xz2",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
@ -30,8 +30,8 @@ ecow = "0.2"
|
|||||||
dirs = "5"
|
dirs = "5"
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
|
fontdb = "0.15.0"
|
||||||
inferno = "0.11.15"
|
inferno = "0.11.15"
|
||||||
memmap2 = "0.7"
|
|
||||||
notify = "6"
|
notify = "6"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
open = "5"
|
open = "5"
|
||||||
@ -54,7 +54,6 @@ ureq = "2"
|
|||||||
rustls = "0.21"
|
rustls = "0.21"
|
||||||
rustls-pemfile = "1"
|
rustls-pemfile = "1"
|
||||||
env_proxy = "0.4"
|
env_proxy = "0.4"
|
||||||
walkdir = "2"
|
|
||||||
xz2 = { version = "0.1", optional = true }
|
xz2 = { version = "0.1", optional = true }
|
||||||
zip = { version = "0.6", optional = true }
|
zip = { version = "0.6", optional = true }
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use std::cell::OnceCell;
|
use std::cell::OnceCell;
|
||||||
use std::env;
|
use std::fs;
|
||||||
use std::fs::{self, File};
|
use std::path::PathBuf;
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use memmap2::Mmap;
|
use fontdb::{Database, Source};
|
||||||
use typst::diag::StrResult;
|
use typst::diag::StrResult;
|
||||||
use typst::font::{Font, FontBook, FontInfo, FontVariant};
|
use typst::font::{Font, FontBook, FontInfo, FontVariant};
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
use crate::args::FontsCommand;
|
use crate::args::FontsCommand;
|
||||||
|
|
||||||
@ -67,12 +65,39 @@ impl FontSearcher {
|
|||||||
|
|
||||||
/// Search everything that is available.
|
/// Search everything that is available.
|
||||||
pub fn search(&mut self, font_paths: &[PathBuf]) {
|
pub fn search(&mut self, font_paths: &[PathBuf]) {
|
||||||
|
let mut db = Database::new();
|
||||||
|
|
||||||
|
// Font paths have highest priority.
|
||||||
for path in font_paths {
|
for path in font_paths {
|
||||||
self.search_dir(path)
|
db.load_fonts_dir(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.search_system();
|
// System fonts have second priority.
|
||||||
|
db.load_system_fonts();
|
||||||
|
|
||||||
|
for face in db.faces() {
|
||||||
|
let path = match &face.source {
|
||||||
|
Source::File(path) | Source::SharedFile(path, _) => path,
|
||||||
|
// We never add binary sources to the database, so there
|
||||||
|
// shouln't be any.
|
||||||
|
Source::Binary(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let info = db
|
||||||
|
.with_face_data(face.id, FontInfo::new)
|
||||||
|
.expect("database must contain this font");
|
||||||
|
|
||||||
|
if let Some(info) = info {
|
||||||
|
self.book.push(info);
|
||||||
|
self.fonts.push(FontSlot {
|
||||||
|
path: path.clone(),
|
||||||
|
index: face.index,
|
||||||
|
font: OnceCell::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedded fonts have lowest priority.
|
||||||
#[cfg(feature = "embed-fonts")]
|
#[cfg(feature = "embed-fonts")]
|
||||||
self.add_embedded();
|
self.add_embedded();
|
||||||
}
|
}
|
||||||
@ -114,69 +139,4 @@ impl FontSearcher {
|
|||||||
add!("DejaVuSansMono-Oblique.ttf");
|
add!("DejaVuSansMono-Oblique.ttf");
|
||||||
add!("DejaVuSansMono-BoldOblique.ttf");
|
add!("DejaVuSansMono-BoldOblique.ttf");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for fonts in the linux system font directories.
|
|
||||||
fn search_system(&mut self) {
|
|
||||||
if cfg!(target_os = "macos") {
|
|
||||||
self.search_dir("/Library/Fonts");
|
|
||||||
self.search_dir("/Network/Library/Fonts");
|
|
||||||
self.search_dir("/System/Library/Fonts");
|
|
||||||
} else if cfg!(unix) {
|
|
||||||
self.search_dir("/usr/share/fonts");
|
|
||||||
self.search_dir("/usr/local/share/fonts");
|
|
||||||
} else if cfg!(windows) {
|
|
||||||
self.search_dir(
|
|
||||||
env::var_os("WINDIR")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.unwrap_or_else(|| "C:\\Windows".into())
|
|
||||||
.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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(dir) = dirs::font_dir() {
|
|
||||||
self.search_dir(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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: &Path) {
|
|
||||||
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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -191,13 +191,16 @@ bitflags::bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FontInfo {
|
impl FontInfo {
|
||||||
|
/// Compute metadata for font at the `index` of the given data.
|
||||||
|
pub fn new(data: &[u8], index: u32) -> Option<Self> {
|
||||||
|
let ttf = ttf_parser::Face::parse(data, index).ok()?;
|
||||||
|
Self::from_ttf(&ttf)
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute metadata for all fonts in the given data.
|
/// Compute metadata for all fonts in the given data.
|
||||||
pub fn iter(data: &[u8]) -> impl Iterator<Item = FontInfo> + '_ {
|
pub fn iter(data: &[u8]) -> impl Iterator<Item = FontInfo> + '_ {
|
||||||
let count = ttf_parser::fonts_in_collection(data).unwrap_or(1);
|
let count = ttf_parser::fonts_in_collection(data).unwrap_or(1);
|
||||||
(0..count).filter_map(move |index| {
|
(0..count).filter_map(move |index| Self::new(data, index))
|
||||||
let ttf = ttf_parser::Face::parse(data, index).ok()?;
|
|
||||||
Self::from_ttf(&ttf)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute metadata for a single ttf-parser face.
|
/// Compute metadata for a single ttf-parser face.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user