mod common;
use common::{Paint, SceneCommand, compile, default_provider, parse};
#[test]
fn header_fill_applied_to_first_row_cells() {
let src = r##"zenith version=1 {
project id="proj.hf" name="HF"
tokens format="zenith-token-v1" {
token id="color.header" type="color" value="#aabbcc"
token id="color.body" type="color" value="#112233"
}
styles {}
document id="doc.hf" title="HF" {
page id="page.hf" w=(px)400 h=(px)300 {
table id="t.hf" x=(px)0 y=(px)0 w=(px)200 h=(px)200 fill=(token)"color.body" header-rows=1 header-fill=(token)"color.header" cell-padding=(px)0 gap=(px)0 {
column width=(px)100
column width=(px)100
row {
cell { text id="h1" x=(px)0 y=(px)0 { span "H1" } }
cell { text id="h2" x=(px)0 y=(px)0 { span "H2" } }
}
row {
cell { text id="b1" x=(px)0 y=(px)0 { span "B1" } }
cell { text id="b2" x=(px)0 y=(px)0 { span "B2" } }
}
}
}
}
}
"##;
let result = compile(&parse(src), &default_provider());
let fill_colors: Vec<(u8, u8, u8)> = result
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::FillRect {
paint: Paint::Solid { color },
..
} => Some((color.r, color.g, color.b)),
_ => None,
})
.collect();
assert_eq!(
fill_colors.len(),
4,
"expected 4 cell fills; got {fill_colors:?}"
);
assert_eq!(
fill_colors[0],
(0xaa, 0xbb, 0xcc),
"header cell 0 must use header-fill; got {fill_colors:?}"
);
assert_eq!(
fill_colors[1],
(0xaa, 0xbb, 0xcc),
"header cell 1 must use header-fill; got {fill_colors:?}"
);
assert_eq!(
fill_colors[2],
(0x11, 0x22, 0x33),
"body cell 0 must use table fill; got {fill_colors:?}"
);
assert_eq!(
fill_colors[3],
(0x11, 0x22, 0x33),
"body cell 1 must use table fill; got {fill_colors:?}"
);
}
#[test]
fn header_cell_own_fill_overrides_header_fill() {
let src = r##"zenith version=1 {
project id="proj.hco" name="HCO"
tokens format="zenith-token-v1" {
token id="color.header" type="color" value="#aabbcc"
token id="color.cell" type="color" value="#ff0000"
token id="color.body" type="color" value="#112233"
}
styles {}
document id="doc.hco" title="HCO" {
page id="page.hco" w=(px)400 h=(px)300 {
table id="t.hco" x=(px)0 y=(px)0 w=(px)200 h=(px)200 fill=(token)"color.body" header-rows=1 header-fill=(token)"color.header" cell-padding=(px)0 gap=(px)0 {
column width=(px)200
row {
cell fill=(token)"color.cell" { text id="hc" x=(px)0 y=(px)0 { span "Header" } }
}
row {
cell { text id="bc" x=(px)0 y=(px)0 { span "Body" } }
}
}
}
}
}
"##;
let result = compile(&parse(src), &default_provider());
let fill_colors: Vec<(u8, u8, u8)> = result
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::FillRect {
paint: Paint::Solid { color },
..
} => Some((color.r, color.g, color.b)),
_ => None,
})
.collect();
assert_eq!(
fill_colors.len(),
2,
"expected 2 cell fills; got {fill_colors:?}"
);
assert_eq!(
fill_colors[0],
(0xff, 0x00, 0x00),
"header cell with own fill must use cell.fill; got {fill_colors:?}"
);
assert_eq!(
fill_colors[1],
(0x11, 0x22, 0x33),
"body cell must use table fill; got {fill_colors:?}"
);
}
#[test]
fn header_style_applied_to_unstyled_text_children() {
let src = r##"zenith version=1 {
project id="proj.hs" name="HS"
tokens format="zenith-token-v1" {
token id="color.orange" type="color" value="#ff8800"
token id="color.bg" type="color" value="#ffffff"
}
styles {
style id="style.header" {
fill (token)"color.orange"
}
}
document id="doc.hs" title="HS" {
page id="page.hs" w=(px)400 h=(px)300 {
table id="t.hs" x=(px)0 y=(px)0 w=(px)200 h=(px)200 fill=(token)"color.bg" header-rows=1 header-style="style.header" cell-padding=(px)0 gap=(px)0 {
column width=(px)200
row {
cell { text id="ht" x=(px)0 y=(px)0 { span "Header" } }
}
row {
cell { text id="bt" x=(px)0 y=(px)0 { span "Body" } }
}
}
}
}
}
"##;
let result = compile(&parse(src), &default_provider());
let run_colors: Vec<(u8, u8, u8)> = result
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::DrawGlyphRun { color, .. } => Some((color.r, color.g, color.b)),
_ => None,
})
.collect();
assert_eq!(
run_colors.len(),
2,
"expected 2 glyph runs; got {run_colors:?}"
);
assert_eq!(
run_colors[0],
(0xff, 0x88, 0x00),
"header text must use header-style fill; got {run_colors:?}"
);
assert_eq!(
run_colors[1],
(0x00, 0x00, 0x00),
"body text must NOT use header-style; got {run_colors:?}"
);
}
#[test]
fn no_header_rows_emits_table_fill_for_all_cells() {
let src_no_header = r##"zenith version=1 {
project id="proj.nh" name="NH"
tokens format="zenith-token-v1" {
token id="color.body" type="color" value="#334455"
}
styles {}
document id="doc.nh" title="NH" {
page id="page.nh" w=(px)400 h=(px)300 {
table id="t.nh" x=(px)0 y=(px)0 w=(px)200 h=(px)200 fill=(token)"color.body" cell-padding=(px)0 gap=(px)0 {
column width=(px)200
row {
cell { text id="r0" x=(px)0 y=(px)0 { span "R0" } }
}
row {
cell { text id="r1" x=(px)0 y=(px)0 { span "R1" } }
}
}
}
}
}
"##;
let src_with_header_fill = src_no_header.replace(
"fill=(token)\"color.body\" cell-padding",
"fill=(token)\"color.body\" header-fill=(token)\"color.body\" cell-padding",
);
let result_a = compile(&parse(src_no_header), &default_provider());
let result_b = compile(&parse(&src_with_header_fill), &default_provider());
let colors_a: Vec<(u8, u8, u8)> = result_a
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::FillRect {
paint: Paint::Solid { color },
..
} => Some((color.r, color.g, color.b)),
_ => None,
})
.collect();
let colors_b: Vec<(u8, u8, u8)> = result_b
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::FillRect {
paint: Paint::Solid { color },
..
} => Some((color.r, color.g, color.b)),
_ => None,
})
.collect();
assert_eq!(
colors_a.len(),
2,
"expected 2 fills without header-rows; got {colors_a:?}"
);
assert!(
colors_a.iter().all(|&c| c == (0x33, 0x44, 0x55)),
"all cells must use table fill; got {colors_a:?}"
);
assert_eq!(
colors_a, colors_b,
"header-fill with no header-rows must not change fill output"
);
}
#[test]
fn header_style_bold_widens_auto_column() {
fn make_src(weight: u32) -> String {
format!(
r##"zenith version=1 {{
project id="proj.bh" name="BH"
tokens format="zenith-token-v1" {{
token id="weight.val" type="fontWeight" value={weight}
token id="color.ink" type="color" value="#000000"
}}
styles {{
style id="style.bold" {{
font-weight (token)"weight.val"
}}
}}
document id="doc.bh" title="BH" {{
page id="page.bh" w=(px)800 h=(px)400 {{
table id="t.bh" x=(px)0 y=(px)0 w=(px)800 h=(px)300 fill=(token)"color.ink" header-rows=1 header-style="style.bold" cell-padding=(px)0 gap=(px)0 {{
column
row {{
cell {{ text id="hdr" x=(px)0 y=(px)0 {{ span "Supercalifragilistic" }} }}
}}
row {{
cell {{ text id="bod" x=(px)0 y=(px)0 {{ span "Hi" }} }}
}}
}}
}}
}}
}}
"##
)
}
let result_bold = compile(&parse(&make_src(700)), &default_provider());
let result_norm = compile(&parse(&make_src(400)), &default_provider());
let col_w = |result: &zenith_scene::CompileResult| -> f64 {
result
.scene
.commands
.iter()
.filter_map(|c| match c {
SceneCommand::FillRect { w, .. } => Some(*w),
_ => None,
})
.nth(1) .expect("body cell FillRect must exist")
};
let bold_col_w = col_w(&result_bold);
let norm_col_w = col_w(&result_norm);
assert!(
bold_col_w > norm_col_w,
"bold header-style must widen the AUTO column vs normal weight: \
bold={bold_col_w} normal={norm_col_w}"
);
}