use super::align::{line_gaps, source_lines};
use crate::coord::{millipt_to_twips, pt_to_emu};
use crate::ir::TextAlign;
use k2f_core::{GeometryNode, Pt, Rect};
pub(crate) fn insets(geo: Option<&GeometryNode>, align: TextAlign) -> (i64, i64, i64, i64) {
let Some(geo) = geo else {
return (0, 0, 0, 0);
};
let lines = source_lines(geo);
if lines.is_empty() {
return (0, 0, 0, 0);
}
let box_w = geo.width.0;
let min_left = lines
.iter()
.map(|g| line_gaps(g, box_w).1)
.min()
.unwrap_or(0)
.max(0);
let min_right = lines
.iter()
.map(|g| line_gaps(g, box_w).2)
.min()
.unwrap_or(0)
.max(0);
let top = first_line_top_emu(geo);
match align {
TextAlign::Left => {
if line_fills_padded_width(min_left, min_right, box_w) {
(0, top, 0, 0)
} else {
(emu(min_left), top, 0, 0)
}
}
TextAlign::Right => {
if line_fills_padded_width(min_right, min_left, box_w) {
(0, top, 0, 0)
} else {
(0, top, emu(min_right), 0)
}
}
TextAlign::Center | TextAlign::Justify => (0, top, 0, 0),
}
}
fn line_fills_padded_width(pad: i128, opposite_slack: i128, box_w: i128) -> bool {
let avail = box_w.saturating_sub(pad);
if avail <= 0 {
return false;
}
let content = avail.saturating_sub(opposite_slack.max(0));
content.saturating_mul(100) >= avail.saturating_mul(85)
}
fn first_line_top_emu(geo: &GeometryNode) -> i64 {
let Some(line) = source_lines(geo).into_iter().next() else {
return 0;
};
let y = line.iter().map(|g| g.y_offset.0).min().unwrap_or(0);
if y < 1_000 {
return 0;
}
emu(y)
}
pub(crate) fn line_spacing_twips(geo: Option<&GeometryNode>) -> Option<i64> {
let geo = geo?;
let lines = source_lines(geo);
if lines.len() < 2 {
return None;
}
let baseline = |line: &[&k2f_core::GlyphPosition]| -> i128 {
let mut ys: Vec<i128> = line.iter().map(|g| g.y_offset.0).collect();
ys.sort_unstable();
ys[ys.len() / 2]
};
let y0 = baseline(&lines[0]);
let y1 = baseline(&lines[1]);
let delta = (y1 - y0).abs();
if delta < 2_000 {
return None;
}
Some(millipt_to_twips(i64::try_from(delta).unwrap_or(0)))
}
pub(crate) fn vert_center(geo: Option<&GeometryNode>, rect: &Rect, font_size: Pt) -> bool {
let Some(geo) = geo else {
return false;
};
if rect.height.0 <= font_size.0.saturating_mul(2) {
return false;
}
let lines = source_lines(geo);
if lines.is_empty() {
return false;
}
let h = geo.height.0;
if h <= 0 {
return false;
}
let first_y = lines[0].iter().map(|g| g.y_offset.0).min().unwrap_or(0);
let last_y = lines[lines.len() - 1]
.iter()
.map(|g| g.y_offset.0)
.min()
.unwrap_or(first_y);
let fs = font_size.0.max(1);
let top = first_y;
let bot_after = (h - last_y - fs).max(0);
gaps_look_centered(top, bot_after)
}
fn gaps_look_centered(top: i128, bot_after: i128) -> bool {
let lo = top.min(bot_after);
let hi = top.max(bot_after);
lo >= 2_000 && lo.saturating_mul(2) >= hi
}
fn emu(millipt: i128) -> i64 {
pt_to_emu(Pt(millipt))
}
#[cfg(test)]
mod tests {
use super::gaps_look_centered;
#[test]
fn padded_one_line_cell_is_centered() {
assert!(gaps_look_centered(12_312, 34_000 - 12_312 - 8_000));
}
#[test]
fn two_line_cell_with_equal_padding_is_centered() {
assert!(gaps_look_centered(7_000, 34_000 - 17_000 - 8_000));
}
#[test]
fn top_padded_stretch_is_not_centered() {
assert!(!gaps_look_centered(7_000, 34_000 - 7_000 - 8_000));
}
}