///|
pub using @fonts {type Font}
///|
#derive(Eq)
pub suberror LoaderError {
LoaderError(String)
}
///|
pub impl Show for LoaderError with fn output(self, logger) {
let LoaderError(msg) = self
logger.write_string(
(
$|LoaderError(\{msg.escape(quote=true)})
),
)
}
///|
/// `load_font` loads a font by its simple name in the compressed JSON format
/// produced by `gmlewis/fonts` (for example, `baloo.json.gz`).
///
/// `font_name` is the short font name (for example, `"baloo"`), not the full
/// package path. For the `baloo` font from package `gmlewis/fonts-b/baloo`,
/// use `load_font("baloo")`. The examples' `baloo` font is vendored in
/// `assets/fonts/`, so a normal checkout needs no environment variable.
///
/// Paths are checked in this order:
///
/// 1. `./assets/fonts/{font_name}.json.gz`
/// 2. `./all-fonts/{font_name}.json.gz` (legacy working-directory layout)
/// 3. `$MOONBIT_STEP_ROOT/assets/fonts/{font_name}.json.gz`
/// 4. `$MOONBIT_FONTS_DIR/all-fonts/{font_name}.json.gz`
///
/// If the font is not found, it raises a `LoaderError` listing every path that
/// was checked, along with instructions on how to generate the font.
pub async fn load_font(font_name : String) -> Font raise LoaderError {
let file_name = "\{font_name}.json.gz"
let candidates : Array[String] = [
"./assets/fonts/\{file_name}",
"./all-fonts/\{file_name}",
]
if @env.get_env_var("MOONBIT_STEP_ROOT") is Some(root) {
let dir = if root.has_suffix("/") { root } else { "\{root}/" }
candidates.push("\{dir}assets/fonts/\{file_name}")
}
let env_dir = @env.get_env_var("MOONBIT_FONTS_DIR")
if env_dir is Some(dir) {
let base = if dir.has_suffix("/") { dir } else { "\{dir}/" }
candidates.push("\{base}all-fonts/\{file_name}")
}
//
let mut font_path : String? = None
for path in candidates {
if font_path is None {
let exists = @fs.exists(path) catch { _ => false }
if exists {
font_path = Some(path)
}
}
}
//
guard font_path is Some(font_path) else {
let checked_paths = candidates.map(fn(path) { "- \{path}" }).join("\n")
let msg =
$|Font '\{font_name}' not found.
$|Checked paths:
$|\{checked_paths}
$|
$|To generate this font, follow these steps:
$|1. Navigate to the 'gmlewis/fonts' repo directory.
$|2. Ensure you have the 'all-fonts.txt' file containing '\{font_name}'.
$|3. Run the generation script:
$| python3 scripts/compress-all-fonts.py --force --font \{font_name}
$|4. Copy 'all-fonts/\{file_name}' to 'assets/fonts/\{file_name}' or set
$| MOONBIT_FONTS_DIR to the fonts repo, then rerun your program.
try! @stdio.stderr.write(msg + "\n")
raise LoaderError(
"Font '\{font_name}' not found. See above for instructions.",
)
}
//
try {
let data = @fs.read_file(font_path) catch {
e => raise LoaderError("Error reading font file at '\{font_path}': \{e}")
}
//
let bytes = data.binary()
let buffer = @io.Buffer::from_bytes(bytes)
//
// Decompress the gzip data
let (gzip_reader, err) = @gzip.Reader::new(buffer)
match err {
Some(e) => raise LoaderError("Gzip decompression error: \{e}")
None => ()
}
//
let (json_bytes, err) = @io.read_all(gzip_reader)
match err {
Some(e) => raise LoaderError("Read error during decompression: \{e}")
None => ()
}
//
let json_str = @base64.bytes2str(json_bytes.to_bytes())
let json = @json.parse(json_str)
let font : Font = @json.from_json(json)
font
} catch {
@json.JsonDecodeError(e) =>
raise LoaderError("JSON decode error: \{Repr(e)}")
e => raise LoaderError("Unexpected error loading font: \{Repr(e)}")
}
}