refactor & improvements

This commit is contained in:
2026-03-29 22:35:57 +03:00
parent 5a51d3b4c3
commit 1675d2611c
68 changed files with 4803 additions and 7387 deletions

View File

@@ -130,7 +130,7 @@ fn render_text_cosmic(
buffer.set_size(&mut font_system, Some(img_w as f32), Some(text_h as f32));
let attrs = Attrs::new().family(Family::SansSerif);
buffer.set_text(&mut font_system, text, attrs, Shaping::Advanced);
buffer.set_text(&mut font_system, text, &attrs, Shaping::Advanced, None);
buffer.shape_until_scroll(&mut font_system, false);
let mut swash_cache = SwashCache::new();

View File

@@ -125,7 +125,13 @@ mod tests {
use super::*;
#[test]
fn test_resolve_path() {
fn test_resolve_path_simple() {
let data: Value = serde_json::json!({"name": "test"});
assert_eq!(value_to_string(resolve_path(&data, "name")), "test");
}
#[test]
fn test_resolve_path_nested() {
let data: Value = serde_json::json!({
"firma": {
"unvan": "Acme A.Ş.",
@@ -140,10 +146,30 @@ mod tests {
value_to_string(resolve_path(&data, "firma.vergiNo")),
"123"
);
assert_eq!(
value_to_string(resolve_path(&data, "nonexistent.path")),
""
);
}
#[test]
fn test_resolve_path_missing() {
let data: Value = serde_json::json!({"name": "test"});
let result = resolve_path(&data, "nonexistent.path");
assert!(result.is_null());
assert_eq!(value_to_string(result), "");
}
#[test]
fn test_resolve_path_deep_missing() {
let data: Value = serde_json::json!({"a": {"b": 42}});
let result = resolve_path(&data, "a.b.c.d");
assert!(result.is_null());
}
#[test]
fn test_value_to_string_types() {
assert_eq!(value_to_string(&serde_json::json!("hello")), "hello");
assert_eq!(value_to_string(&serde_json::json!(42)), "42");
assert_eq!(value_to_string(&serde_json::json!(3.14)), "3.14");
assert_eq!(value_to_string(&serde_json::json!(true)), "true");
assert_eq!(value_to_string(&serde_json::json!(null)), "");
}
#[test]
@@ -158,4 +184,261 @@ mod tests {
assert!(arr.is_array());
assert_eq!(arr.as_array().unwrap().len(), 2);
}
#[test]
fn test_resolve_template_text_binding() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::Text(TextElement {
id: "el_name".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
style: TextStyle::default(),
content: None,
binding: ScalarBinding { path: "firma.unvan".to_string() },
}),
],
},
};
let data = serde_json::json!({
"firma": { "unvan": "Acme Teknoloji A.Ş." }
});
let resolved = resolve_template(&template, &data);
assert_eq!(
resolved.texts.get("el_name").unwrap(),
"Acme Teknoloji A.Ş."
);
}
#[test]
fn test_resolve_template_text_with_prefix() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::Text(TextElement {
id: "el_no".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
style: TextStyle::default(),
content: Some("Fatura No: ".to_string()),
binding: ScalarBinding { path: "fatura.no".to_string() },
}),
],
},
};
let data = serde_json::json!({
"fatura": { "no": "FTR-001" }
});
let resolved = resolve_template(&template, &data);
assert_eq!(
resolved.texts.get("el_no").unwrap(),
"Fatura No: FTR-001"
);
}
#[test]
fn test_resolve_template_static_text() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::StaticText(StaticTextElement {
id: "title".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
style: TextStyle::default(),
content: "FATURA".to_string(),
}),
],
},
};
let resolved = resolve_template(&template, &serde_json::json!({}));
assert_eq!(resolved.texts.get("title").unwrap(), "FATURA");
}
#[test]
fn test_resolve_template_table_binding() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::RepeatingTable(RepeatingTableElement {
id: "tbl".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
data_source: ArrayBinding { path: "kalemler".to_string() },
columns: vec![
TableColumn {
id: "col_adi".to_string(),
field: "adi".to_string(),
title: "Urun Adi".to_string(),
width: SizeValue::Fr { value: 1.0 },
align: "left".to_string(),
format: None,
},
TableColumn {
id: "col_tutar".to_string(),
field: "tutar".to_string(),
title: "Tutar".to_string(),
width: SizeValue::Fixed { value: 30.0 },
align: "right".to_string(),
format: None,
},
],
style: TableStyle::default(),
}),
],
},
};
let data = serde_json::json!({
"kalemler": [
{ "adi": "Widget", "tutar": 100 },
{ "adi": "Gadget", "tutar": 200 }
]
});
let resolved = resolve_template(&template, &data);
let table = resolved.tables.get("tbl").unwrap();
assert_eq!(table.rows.len(), 2);
assert_eq!(table.rows[0], vec!["Widget", "100"]);
assert_eq!(table.rows[1], vec!["Gadget", "200"]);
}
#[test]
fn test_resolve_template_table_empty_array() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::RepeatingTable(RepeatingTableElement {
id: "tbl".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
data_source: ArrayBinding { path: "items".to_string() },
columns: vec![
TableColumn {
id: "c1".to_string(),
field: "name".to_string(),
title: "Name".to_string(),
width: SizeValue::Fr { value: 1.0 },
align: "left".to_string(),
format: None,
},
],
style: TableStyle::default(),
}),
],
},
};
let data = serde_json::json!({ "items": [] });
let resolved = resolve_template(&template, &data);
let table = resolved.tables.get("tbl").unwrap();
assert_eq!(table.rows.len(), 0);
}
#[test]
fn test_resolve_template_missing_binding_path() {
let template = Template {
id: "t1".to_string(),
name: "Test".to_string(),
page: PageSettings { width: 210.0, height: 297.0 },
fonts: vec![],
root: ContainerElement {
id: "root".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![
TemplateElement::Text(TextElement {
id: "el_missing".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
style: TextStyle::default(),
content: None,
binding: ScalarBinding { path: "does.not.exist".to_string() },
}),
],
},
};
let data = serde_json::json!({});
let resolved = resolve_template(&template, &data);
// Missing binding path should resolve to empty string
assert_eq!(resolved.texts.get("el_missing").unwrap(), "");
}
}

View File

@@ -15,16 +15,16 @@ pub fn pt_to_mm(pt: f32) -> f64 {
/// SizeValue → taffy Dimension (width veya height için)
fn size_value_to_dimension(sv: &SizeValue) -> Dimension {
match sv {
SizeValue::Fixed { value } => Dimension::Length(mm_to_pt(*value)),
SizeValue::Auto => Dimension::Auto,
SizeValue::Fixed { value } => Dimension::length(mm_to_pt(*value)),
SizeValue::Auto => Dimension::auto(),
// Fr için dimension Auto, flex_grow ayrıca set edilir
SizeValue::Fr { .. } => Dimension::Auto,
SizeValue::Fr { .. } => Dimension::auto(),
}
}
/// SizeValue → taffy LengthPercentage (min/max constraint'ler için)
fn mm_to_length(mm: f64) -> Dimension {
Dimension::Length(mm_to_pt(mm))
Dimension::length(mm_to_pt(mm))
}
/// Fr değerini döndür (yoksa 0)
@@ -78,7 +78,7 @@ pub fn apply_size_to_style(
if main_fr > 0.0 {
style.flex_grow = main_fr;
style.flex_shrink = 1.0;
style.flex_basis = Dimension::Length(0.0);
style.flex_basis = Dimension::length(0.0);
// min-width: 0 (row) veya min-height: 0 (column) ayarla —
// taffy'de min_size default Auto = içerik boyutunun altına küçülemez.
@@ -86,12 +86,12 @@ pub fn apply_size_to_style(
match parent_direction {
Some("row") => {
if size.min_width.is_none() {
style.min_size.width = Dimension::Length(0.0);
style.min_size.width = Dimension::length(0.0);
}
}
_ => {
if size.min_height.is_none() {
style.min_size.height = Dimension::Length(0.0);
style.min_size.height = Dimension::length(0.0);
}
}
}
@@ -113,14 +113,14 @@ pub fn container_to_style(el: &ContainerElement, parent_direction: Option<&str>)
_ => FlexDirection::Column,
},
gap: Size {
width: LengthPercentage::Length(mm_to_pt(el.gap)),
height: LengthPercentage::Length(mm_to_pt(el.gap)),
width: LengthPercentage::length(mm_to_pt(el.gap)),
height: LengthPercentage::length(mm_to_pt(el.gap)),
},
padding: Rect {
top: LengthPercentage::Length(mm_to_pt(el.padding.top)),
right: LengthPercentage::Length(mm_to_pt(el.padding.right)),
bottom: LengthPercentage::Length(mm_to_pt(el.padding.bottom)),
left: LengthPercentage::Length(mm_to_pt(el.padding.left)),
top: LengthPercentage::length(mm_to_pt(el.padding.top)),
right: LengthPercentage::length(mm_to_pt(el.padding.right)),
bottom: LengthPercentage::length(mm_to_pt(el.padding.bottom)),
left: LengthPercentage::length(mm_to_pt(el.padding.left)),
},
align_items: Some(match el.align.as_str() {
"center" => AlignItems::Center,
@@ -142,8 +142,8 @@ pub fn container_to_style(el: &ContainerElement, parent_direction: Option<&str>)
PositionMode::Absolute { x, y } => {
style.position = Position::Absolute;
style.inset = Rect {
top: LengthPercentageAuto::Length(mm_to_pt(*y)),
left: LengthPercentageAuto::Length(mm_to_pt(*x)),
top: LengthPercentageAuto::length(mm_to_pt(*y)),
left: LengthPercentageAuto::length(mm_to_pt(*x)),
right: auto(),
bottom: auto(),
};
@@ -158,10 +158,10 @@ pub fn container_to_style(el: &ContainerElement, parent_direction: Option<&str>)
if let Some(bw) = el.style.border_width {
let bpt = mm_to_pt(bw);
style.border = Rect {
top: LengthPercentage::Length(bpt),
right: LengthPercentage::Length(bpt),
bottom: LengthPercentage::Length(bpt),
left: LengthPercentage::Length(bpt),
top: LengthPercentage::length(bpt),
right: LengthPercentage::length(bpt),
bottom: LengthPercentage::length(bpt),
left: LengthPercentage::length(bpt),
};
}
@@ -180,8 +180,8 @@ pub fn leaf_style(
PositionMode::Absolute { x, y } => {
style.position = Position::Absolute;
style.inset = Rect {
top: LengthPercentageAuto::Length(mm_to_pt(*y)),
left: LengthPercentageAuto::Length(mm_to_pt(*x)),
top: LengthPercentageAuto::length(mm_to_pt(*y)),
left: LengthPercentageAuto::length(mm_to_pt(*x)),
right: auto(),
bottom: auto(),
};
@@ -197,6 +197,7 @@ pub fn leaf_style(
#[cfg(test)]
mod tests {
use super::*;
use dreport_core::models::{ContainerStyle, Padding};
#[test]
fn test_mm_to_pt_conversion() {
@@ -205,18 +206,171 @@ mod tests {
assert!((pt - 595.28).abs() < 0.1);
}
#[test]
fn test_mm_to_pt_one_inch() {
// 1 inch = 25.4mm = 72pt
let pt = mm_to_pt(25.4);
assert!((pt - 72.0).abs() < 0.01, "25.4mm should be ~72pt, got {}", pt);
}
#[test]
fn test_pt_to_mm_conversion() {
// 72pt = 25.4mm (1 inch)
let mm = pt_to_mm(72.0);
assert!((mm - 25.4).abs() < 0.01, "72pt should be ~25.4mm, got {}", mm);
}
#[test]
fn test_roundtrip_mm_pt_mm() {
// mm → pt → mm should preserve value within tolerance
let original = 100.0_f64;
let pt = mm_to_pt(original);
let back = pt_to_mm(pt);
assert!(
(back - original).abs() < 0.01,
"Roundtrip failed: {} → {}pt → {}",
original,
pt,
back
);
}
#[test]
fn test_mm_to_pt_zero() {
assert_eq!(mm_to_pt(0.0), 0.0);
}
#[test]
fn test_pt_to_mm_zero() {
assert!((pt_to_mm(0.0) - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_fixed_size() {
let sv = SizeValue::Fixed { value: 50.0 };
match size_value_to_dimension(&sv) {
Dimension::Length(pt) => assert!((pt - mm_to_pt(50.0)).abs() < 0.01),
_ => panic!("Expected Length"),
}
assert_eq!(size_value_to_dimension(&sv), Dimension::length(mm_to_pt(50.0)));
}
#[test]
fn test_auto_size() {
let sv = SizeValue::Auto;
assert_eq!(size_value_to_dimension(&sv), Dimension::auto());
}
#[test]
fn test_fr_maps_to_auto_dimension() {
let sv = SizeValue::Fr { value: 2.0 };
assert!(matches!(size_value_to_dimension(&sv), Dimension::Auto));
assert_eq!(size_value_to_dimension(&sv), Dimension::auto());
}
#[test]
fn test_fr_value_extraction() {
assert_eq!(fr_value(&SizeValue::Fr { value: 3.0 }), 3.0);
assert_eq!(fr_value(&SizeValue::Auto), 0.0);
assert_eq!(fr_value(&SizeValue::Fixed { value: 10.0 }), 0.0);
}
#[test]
fn test_apply_size_fr_sets_flex_grow() {
let size = SizeConstraint {
width: SizeValue::Fr { value: 2.0 },
height: SizeValue::Auto,
..Default::default()
};
let mut style = Style::default();
apply_size_to_style(&mut style, &size, Some("row"));
assert_eq!(style.flex_grow, 2.0);
assert_eq!(style.flex_basis, Dimension::length(0.0));
}
#[test]
fn test_apply_size_fixed_no_flex_grow() {
let size = SizeConstraint {
width: SizeValue::Fixed { value: 50.0 },
height: SizeValue::Fixed { value: 30.0 },
..Default::default()
};
let mut style = Style::default();
apply_size_to_style(&mut style, &size, Some("row"));
assert_eq!(style.flex_grow, 0.0);
}
#[test]
fn test_apply_size_min_max_constraints() {
let size = SizeConstraint {
width: SizeValue::Auto,
height: SizeValue::Auto,
min_width: Some(20.0),
max_width: Some(100.0),
min_height: Some(10.0),
max_height: Some(50.0),
};
let mut style = Style::default();
apply_size_to_style(&mut style, &size, None);
assert_eq!(style.min_size.width, Dimension::length(mm_to_pt(20.0)));
assert_eq!(style.max_size.width, Dimension::length(mm_to_pt(100.0)));
assert_eq!(style.min_size.height, Dimension::length(mm_to_pt(10.0)));
assert_eq!(style.max_size.height, Dimension::length(mm_to_pt(50.0)));
}
#[test]
fn test_container_to_style_direction() {
let el = ContainerElement {
id: "test".to_string(),
position: PositionMode::Flow,
size: SizeConstraint::default(),
direction: "row".to_string(),
gap: 5.0,
padding: Padding { top: 10.0, right: 10.0, bottom: 10.0, left: 10.0 },
align: "center".to_string(),
justify: "space-between".to_string(),
style: ContainerStyle::default(),
children: vec![],
};
let style = container_to_style(&el, None);
assert_eq!(style.flex_direction, FlexDirection::Row);
assert_eq!(style.align_items, Some(AlignItems::Center));
assert_eq!(style.justify_content, Some(JustifyContent::SpaceBetween));
}
#[test]
fn test_container_to_style_absolute() {
let el = ContainerElement {
id: "test".to_string(),
position: PositionMode::Absolute { x: 20.0, y: 30.0 },
size: SizeConstraint::default(),
direction: "column".to_string(),
gap: 0.0,
padding: Padding::default(),
align: "stretch".to_string(),
justify: "start".to_string(),
style: ContainerStyle::default(),
children: vec![],
};
let style = container_to_style(&el, None);
assert_eq!(style.position, Position::Absolute);
}
#[test]
fn test_leaf_style_flow() {
let size = SizeConstraint {
width: SizeValue::Fixed { value: 60.0 },
height: SizeValue::Auto,
..Default::default()
};
let style = leaf_style(&size, &PositionMode::Flow, Some("column"));
assert_eq!(style.position, Position::Relative);
assert_eq!(style.size.width, Dimension::length(mm_to_pt(60.0)));
}
#[test]
fn test_leaf_style_absolute() {
let size = SizeConstraint {
width: SizeValue::Fixed { value: 40.0 },
height: SizeValue::Fixed { value: 20.0 },
..Default::default()
};
let style = leaf_style(&size, &PositionMode::Absolute { x: 10.0, y: 15.0 }, None);
assert_eq!(style.position, Position::Absolute);
}
}

View File

@@ -189,3 +189,220 @@ pub fn expand_table(
children,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data_resolve::{ResolvedData, ResolvedTable};
use std::collections::HashMap;
fn make_table(num_columns: usize) -> RepeatingTableElement {
let columns: Vec<TableColumn> = (0..num_columns)
.map(|i| TableColumn {
id: format!("col_{}", i),
field: format!("field_{}", i),
title: format!("Column {}", i),
width: SizeValue::Fr { value: 1.0 },
align: "left".to_string(),
format: None,
})
.collect();
RepeatingTableElement {
id: "tbl".to_string(),
position: PositionMode::Flow,
size: SizeConstraint {
width: SizeValue::Fr { value: 1.0 },
height: SizeValue::Auto,
..Default::default()
},
data_source: ArrayBinding { path: "items".to_string() },
columns,
style: TableStyle::default(),
}
}
fn make_resolved(table_id: &str, rows: Vec<Vec<String>>) -> ResolvedData {
let mut tables = HashMap::new();
tables.insert(table_id.to_string(), ResolvedTable { rows });
ResolvedData {
texts: HashMap::new(),
tables,
barcodes: HashMap::new(),
images: HashMap::new(),
}
}
#[test]
fn test_expand_table_structure() {
let table = make_table(2);
let resolved = make_resolved("tbl", vec![
vec!["A".to_string(), "1".to_string()],
vec!["B".to_string(), "2".to_string()],
]);
let container = expand_table(&table, &resolved);
// Wrapper container properties
assert_eq!(container.id, "tbl");
assert_eq!(container.direction, "column");
// Children: header row + 2 data rows (no border_color so no separator line)
assert_eq!(container.children.len(), 3);
// First child is header row container
match &container.children[0] {
TemplateElement::Container(c) => {
assert_eq!(c.id, "tbl_header");
assert_eq!(c.direction, "row");
assert_eq!(c.children.len(), 2); // 2 columns
// Check header cell text
match &c.children[0] {
TemplateElement::StaticText(t) => assert_eq!(t.content, "Column 0"),
_ => panic!("Expected StaticText for header cell"),
}
}
_ => panic!("Expected Container for header row"),
}
// Data rows
for (row_idx, child) in container.children[1..].iter().enumerate() {
match child {
TemplateElement::Container(c) => {
assert_eq!(c.id, format!("tbl_row_{}", row_idx));
assert_eq!(c.direction, "row");
assert_eq!(c.children.len(), 2);
}
_ => panic!("Expected Container for data row"),
}
}
}
#[test]
fn test_expand_table_empty_data() {
let table = make_table(3);
let resolved = make_resolved("tbl", vec![]);
let container = expand_table(&table, &resolved);
// Only header row, no data rows
assert_eq!(container.children.len(), 1);
// Header should still have all 3 columns
match &container.children[0] {
TemplateElement::Container(c) => {
assert_eq!(c.children.len(), 3);
}
_ => panic!("Expected Container for header row"),
}
}
#[test]
fn test_expand_table_column_count() {
let table = make_table(4);
let resolved = make_resolved("tbl", vec![
vec!["a".into(), "b".into(), "c".into(), "d".into()],
]);
let container = expand_table(&table, &resolved);
// header + 1 data row
assert_eq!(container.children.len(), 2);
// Both header and data row should have 4 cells
match &container.children[0] {
TemplateElement::Container(c) => assert_eq!(c.children.len(), 4),
_ => panic!("Expected Container"),
}
match &container.children[1] {
TemplateElement::Container(c) => assert_eq!(c.children.len(), 4),
_ => panic!("Expected Container"),
}
}
#[test]
fn test_expand_table_data_cell_content() {
let table = make_table(2);
let resolved = make_resolved("tbl", vec![
vec!["Hello".to_string(), "42".to_string()],
]);
let container = expand_table(&table, &resolved);
// Data row cells should contain the resolved text
match &container.children[1] {
TemplateElement::Container(c) => {
match &c.children[0] {
TemplateElement::StaticText(t) => assert_eq!(t.content, "Hello"),
_ => panic!("Expected StaticText"),
}
match &c.children[1] {
TemplateElement::StaticText(t) => assert_eq!(t.content, "42"),
_ => panic!("Expected StaticText"),
}
}
_ => panic!("Expected Container"),
}
}
#[test]
fn test_expand_table_with_border_adds_separator() {
let mut table = make_table(2);
table.style.border_color = Some("#000000".to_string());
let resolved = make_resolved("tbl", vec![
vec!["A".to_string(), "1".to_string()],
]);
let container = expand_table(&table, &resolved);
// header + separator line + 1 data row = 3
assert_eq!(container.children.len(), 3);
// Second child should be a Line
match &container.children[1] {
TemplateElement::Line(l) => {
assert_eq!(l.id, "tbl_header_line");
}
_ => panic!("Expected Line separator after header"),
}
}
#[test]
fn test_expand_table_zebra_stripes() {
let mut table = make_table(1);
table.style.zebra_odd = Some("#f0f0f0".to_string());
table.style.zebra_even = Some("#ffffff".to_string());
let resolved = make_resolved("tbl", vec![
vec!["row0".into()],
vec!["row1".into()],
vec!["row2".into()],
]);
let container = expand_table(&table, &resolved);
// header + 3 data rows
assert_eq!(container.children.len(), 4);
// row_0 (even index) => zebra_odd
match &container.children[1] {
TemplateElement::Container(c) => {
assert_eq!(c.style.background_color, Some("#f0f0f0".to_string()));
}
_ => panic!("Expected Container"),
}
// row_1 (odd index) => zebra_even
match &container.children[2] {
TemplateElement::Container(c) => {
assert_eq!(c.style.background_color, Some("#ffffff".to_string()));
}
_ => panic!("Expected Container"),
}
// row_2 (even index) => zebra_odd
match &container.children[3] {
TemplateElement::Container(c) => {
assert_eq!(c.style.background_color, Some("#f0f0f0".to_string()));
}
_ => panic!("Expected Container"),
}
}
}

View File

@@ -143,7 +143,7 @@ impl TextMeasurer {
.family(Family::Name(family_name))
.weight(weight);
buffer.set_text(&mut self.font_system, text, attrs, Shaping::Advanced);
buffer.set_text(&mut self.font_system, text, &attrs, Shaping::Advanced, None);
buffer.shape_until_scroll(&mut self.font_system, false);
let mut max_width: f32 = 0.0;

View File

@@ -54,8 +54,8 @@ pub fn compute(
display: Display::Flex,
flex_direction: FlexDirection::Column,
size: Size {
width: Dimension::Length(page_w_pt),
height: Dimension::Length(page_h_pt),
width: Dimension::length(page_w_pt),
height: Dimension::length(page_h_pt),
},
..Default::default()
};
@@ -197,7 +197,7 @@ fn build_element(
// Line: genişlik parent'tan, yükseklik stroke width
let mut leaf_style = style;
if matches!(e.size.height, SizeValue::Auto) {
leaf_style.size.height = Dimension::Length(mm_to_pt(stroke_w));
leaf_style.size.height = Dimension::length(mm_to_pt(stroke_w));
}
let node = taffy.new_leaf(leaf_style).unwrap();
@@ -246,10 +246,10 @@ fn build_element(
let default_h = if is_qr { 20.0 } else { 15.0 }; // mm
let default_w = if is_qr { 20.0 } else { 40.0 }; // mm
if matches!(e.size.height, SizeValue::Auto) {
style.min_size.height = Dimension::Length(mm_to_pt(default_h));
style.min_size.height = Dimension::length(mm_to_pt(default_h));
}
if matches!(e.size.width, SizeValue::Auto) {
style.min_size.width = Dimension::Length(mm_to_pt(default_w));
style.min_size.width = Dimension::length(mm_to_pt(default_w));
}
let node = taffy.new_leaf(style).unwrap();