use zenith_core::Diagnostic;
pub(in crate::compile) fn snap_to_baseline_grid(
text_y: f64,
ascent: f64,
line_height: f64,
g: f64,
) -> (f64, f64) {
let natural_baseline = text_y + ascent;
let snapped_baseline = (natural_baseline / g).ceil() * g;
let effective_line_height = (line_height / g).ceil() * g;
let snapped_text_y = snapped_baseline - ascent;
(snapped_text_y, effective_line_height)
}
pub(in crate::compile) fn baseline_grid_snap_failed_diag(
node_id: &str,
line_height: f64,
g: f64,
span: Option<zenith_core::Span>,
) -> Diagnostic {
let multiple = (line_height / g).ceil();
let effective = multiple * g;
Diagnostic::warning(
"baseline-grid.snap_failed",
format!(
"text node '{node_id}' line-height {line_height}px exceeds baseline-grid \
pitch {g}px; lines snap to {effective}px ({multiple}× grid)"
),
span,
Some(node_id.to_owned()),
)
}
#[cfg(test)]
mod baseline_grid_unit_tests {
use super::*;
#[test]
fn snaps_first_baseline_down_to_next_grid_line() {
let (ty, lh) = snap_to_baseline_grid( 338.0, 12.0, 18.0, 14.0);
assert_eq!(ty + 12.0, 350.0, "baseline already on the grid stays put");
assert_eq!(lh, 28.0);
}
#[test]
fn natural_baseline_355_snaps_to_364() {
let (ty, lh) = snap_to_baseline_grid(343.0, 12.0, 14.0, 14.0);
assert_eq!(ty + 12.0, 364.0);
assert_eq!(lh, 14.0);
}
#[test]
fn effective_advance_is_smallest_multiple_ge_line_height() {
let (_, lh1) = snap_to_baseline_grid(0.0, 10.0, 13.9, 14.0);
assert_eq!(lh1, 14.0);
let (_, lh2) = snap_to_baseline_grid(0.0, 10.0, 14.1, 14.0);
assert_eq!(lh2, 28.0);
}
#[test]
fn snap_failed_diag_names_node_and_pitch() {
let d = baseline_grid_snap_failed_diag("col1", 18.0, 14.0, None);
assert_eq!(d.code, "baseline-grid.snap_failed");
assert!(d.message.contains("col1"), "message names the node id");
assert!(d.message.contains("18"), "message names line-height");
assert!(d.message.contains("14"), "message names the grid pitch");
assert!(
d.message.contains("28"),
"message names the snapped advance"
);
}
}