diff --git a/crates/fonts/src/fontdb.rs b/crates/fonts/src/fontdb.rs index 02f8b8b7..94721b38 100644 --- a/crates/fonts/src/fontdb.rs +++ b/crates/fonts/src/fontdb.rs @@ -719,15 +719,23 @@ pub fn system_font_dirs() -> Vec { dirs.push(std::path::PathBuf::from(l).join("Microsoft\\Windows\\Fonts")); } } else { - dirs.extend(["/usr/share/fonts", "/usr/local/share/fonts"].map(Into::into)); - if let Some(h) = &home { - dirs.push(h.join(".fonts")); - dirs.push(h.join(".local/share/fonts")); - } + let xdg_data_home = std::env::var_os("XDG_DATA_HOME").map(std::path::PathBuf::from); + dirs.extend(unix_font_dirs(home.as_deref(), xdg_data_home.as_deref())); } dirs } +fn unix_font_dirs(home: Option<&std::path::Path>, xdg_data_home: Option<&std::path::Path>) -> Vec { + let mut dirs: Vec = ["/usr/share/fonts", "/usr/local/share/fonts"].map(Into::into).into(); + if let Some(home) = home { + dirs.push(home.join(".fonts")); + } + let data_home = + xdg_data_home.filter(|path| path.is_absolute()).map(std::path::Path::to_path_buf).or_else(|| home.map(|path| path.join(".local/share"))); + dirs.extend(data_home.map(|path| path.join("fonts"))); + dirs +} + /// One face found in a font file: index, family, style and (variable fonts) the named instance's /// axis settings. type Found = (u32, String, String, Vec<([u8; 4], f32)>); diff --git a/crates/fonts/src/tests_sysfonts.rs b/crates/fonts/src/tests_sysfonts.rs index deaf7750..474ffbcb 100644 --- a/crates/fonts/src/tests_sysfonts.rs +++ b/crates/fonts/src/tests_sysfonts.rs @@ -6,6 +6,64 @@ use super::*; const FAMILY: &str = "Sysfont Sans3"; +#[cfg(not(any(target_os = "macos", windows)))] +mod user_font_dirs { + use super::*; + + #[test] + fn user_font_dirs_when_xdg_is_absolute() { + let home = Path::new("/home/font-user"); + let xdg = Path::new("/custom/data"); + let dirs = unix_font_dirs(Some(home), Some(xdg)); + assert_eq!(dirs, ["/usr/share/fonts", "/usr/local/share/fonts", "/home/font-user/.fonts", "/custom/data/fonts"].map(PathBuf::from)); + } + + #[test] + fn user_font_dirs_when_xdg_is_unset() { + let home = Path::new("/home/font-user"); + let dirs = unix_font_dirs(Some(home), None); + assert_eq!( + dirs, + ["/usr/share/fonts", "/usr/local/share/fonts", "/home/font-user/.fonts", "/home/font-user/.local/share/fonts"].map(PathBuf::from) + ); + } + + #[test] + fn user_font_dirs_when_xdg_is_relative() { + let home = Path::new("/home/font-user"); + let xdg = Path::new("relative/data"); + let dirs = unix_font_dirs(Some(home), Some(xdg)); + assert_eq!( + dirs, + ["/usr/share/fonts", "/usr/local/share/fonts", "/home/font-user/.fonts", "/home/font-user/.local/share/fonts"].map(PathBuf::from) + ); + } + + #[test] + fn user_font_dirs_when_xdg_is_empty() { + let home = Path::new("/home/font-user"); + let xdg = Path::new(""); + let dirs = unix_font_dirs(Some(home), Some(xdg)); + assert_eq!( + dirs, + ["/usr/share/fonts", "/usr/local/share/fonts", "/home/font-user/.fonts", "/home/font-user/.local/share/fonts"].map(PathBuf::from) + ); + } + + #[test] + fn user_font_dirs_when_home_is_unset_and_xdg_is_absolute() { + let xdg = Path::new("/custom/data"); + let dirs = unix_font_dirs(None, Some(xdg)); + assert_eq!(dirs, ["/usr/share/fonts", "/usr/local/share/fonts", "/custom/data/fonts"].map(PathBuf::from)); + } + + #[test] + fn user_font_dirs_when_home_and_xdg_are_unset() { + let dirs = unix_font_dirs(None, None); + assert_eq!(dirs, ["/usr/share/fonts", "/usr/local/share/fonts"].map(PathBuf::from)); + } +} + /// A bundled Source Sans 3 file renamed [`FAMILY`] (as long as the original name). fn renamed(file: &str) -> Vec { let mut data = std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../assets/fonts").join(file)).unwrap();