// The `fonts` command (upstream `typst-cli/src/fonts.rs`).
///|
/// Execute a font listing command.
async fn fonts(command : FontsCommand) -> Unit raise HintedFailure {
let fonts = discover_fonts(command.font) catch {
err => raise bail(err.to_string())
}
let out = StringBuilder()
for entry in fonts.book().families() {
let (family, indices) = entry
out.write_string("\{family}\n")
if command.variants {
for k, index in indices {
let info = fonts.book().info(index).unwrap()
let path = match fonts.source(index) {
Some(Path(path~, ..)) => Some(path)
_ => None
}
let last = k == indices.length() - 1
write_variant(out, info, path, last)
}
out.write_string("\n")
}
}
print_out(out.to_string())
}
///|
/// Rust's `Debug` for a `FontStyle`.
fn style_debug(style : @library.FontStyle) -> String {
match style {
Normal => "Normal"
Italic => "Italic"
Oblique => "Oblique"
}
}
///|
/// `Display for FontWeight`.
fn weight_display(weight : @library.FontWeight) -> String {
weight.0.to_string()
}
///|
/// `Display for FontStretch`.
fn stretch_display(stretch : @library.FontStretch) -> String {
let int_part = stretch.0 / 10
let dec_part = stretch.0 % 10
if dec_part == 0 {
"\{int_part}%"
} else {
"\{int_part}.\{dec_part}%"
}
}
///|
/// `Display for AxisValue`.
fn axis_value_display(value : @library.AxisValue) -> String {
@library.rust_display_f64(
@library.round_with_precision(value.get().to_double(), 2),
)
}
///|
/// Displays information for one font file / variant.
fn write_variant(
f : StringBuilder,
info : @library.FontInfo,
path : String?,
last : Bool,
) -> Unit {
let path = match path {
Some(path) => path
None => "(Embedded)"
}
let { style, weight, stretch, } = info.variant
let marker = if last { "└" } else { "├" }
let pad = if last { " " } else { " │ " }
let axes = info.axes.mapi((i, axis) => (i, axis))
axes.sort_by((a, b) => {
let c = @library.StandardAxes::order(a.1.tag).compare(
@library.StandardAxes::order(b.1.tag),
)
if c != 0 {
c
} else {
a.0.compare(b.0)
}
})
let axes = axes.map(a => a.1)
if axes.is_empty() {
f.write_string(" \{marker} \{path}\n")
f.write_string(
"\{pad} Style: \{style_debug(style)}, Weight: \{weight_display(weight)}, Stretch: \{stretch_display(stretch)}\n",
)
} else {
f.write_string(" \{marker} \{path} (Variable)\n")
let standard = @library.StandardAxes::parse(axes)
if standard.ital is None && standard.slnt is None {
f.write_string("\{pad} Style: \{style_debug(style)}\n")
}
if standard.wght is None {
f.write_string("\{pad} Weight: \{weight_display(weight)}\n")
}
if standard.wdth is None {
f.write_string("\{pad} Stretch: \{stretch_display(stretch)}\n")
}
for axis in axes {
f.write_string("\{pad} \{write_axis(axis)}\n")
}
}
}
///|
/// Formats a variation axis.
fn write_axis(axis : @library.FontAxis) -> String {
let identity = (v : @library.AxisValue) => axis_value_display(v)
match axis.tag.0 {
"ital" => write_axis_with(axis, "Italic", identity)
"slnt" => write_axis_with(axis, "Slant", identity)
"wght" =>
write_axis_with(axis, "Weight", v => {
weight_display(@library.FontWeight::from_wght(v))
})
"wdth" =>
write_axis_with(axis, "Stretch", v => {
stretch_display(@library.FontStretch::from_wdth(v))
})
"opsz" =>
write_axis_with(axis, "Optical Size", v => "\{axis_value_display(v)}pt")
_ => write_axis_with(axis, axis.tag.to_str_lossy(), identity)
}
}
///|
/// Formats a variation axis with a specific name and value display
/// function.
fn write_axis_with(
axis : @library.FontAxis,
name : String,
show : (@library.AxisValue) -> String,
) -> String {
"\{name}: \{show(axis.min)}-\{show(axis.max)} (Default: \{show(axis.default)})"
}