mirror of
https://github.com/duhanbalci/dreport.git
synced 2026-07-01 18:39:16 +00:00
repofactor
This commit is contained in:
@@ -247,11 +247,8 @@ pub struct ChartStyle {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChartElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub chart_type: ChartType,
|
||||
pub data_source: ArrayBinding,
|
||||
pub category_field: String,
|
||||
@@ -272,6 +269,138 @@ pub struct ChartElement {
|
||||
pub style: ChartStyle,
|
||||
}
|
||||
|
||||
// --- Element Base (ortak alanlar) ---
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ElementBase {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
#[serde(default)]
|
||||
pub position: PositionMode,
|
||||
#[serde(default)]
|
||||
pub size: SizeConstraint,
|
||||
}
|
||||
|
||||
impl ElementBase {
|
||||
/// Flow pozisyonlu, condition'sız, verilen size ile base oluştur
|
||||
pub fn flow(id: String, size: SizeConstraint) -> Self {
|
||||
Self {
|
||||
id,
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasBase {
|
||||
fn base(&self) -> &ElementBase;
|
||||
fn base_mut(&mut self) -> &mut ElementBase;
|
||||
}
|
||||
|
||||
macro_rules! impl_has_base {
|
||||
($($t:ty),+ $(,)?) => {
|
||||
$(impl HasBase for $t {
|
||||
fn base(&self) -> &ElementBase { &self.base }
|
||||
fn base_mut(&mut self) -> &mut ElementBase { &mut self.base }
|
||||
})+
|
||||
};
|
||||
}
|
||||
|
||||
impl_has_base!(
|
||||
ContainerElement,
|
||||
StaticTextElement,
|
||||
TextElement,
|
||||
LineElement,
|
||||
ImageElement,
|
||||
PageNumberElement,
|
||||
BarcodeElement,
|
||||
RepeatingTableElement,
|
||||
PageBreakElement,
|
||||
CurrentDateElement,
|
||||
ShapeElement,
|
||||
CheckboxElement,
|
||||
CalculatedTextElement,
|
||||
RichTextElement,
|
||||
ChartElement,
|
||||
);
|
||||
|
||||
pub trait ElementTypeStr {
|
||||
fn type_str(&self) -> &'static str;
|
||||
}
|
||||
|
||||
macro_rules! impl_type_str {
|
||||
($($t:ty => $s:literal),+ $(,)?) => {
|
||||
$(impl ElementTypeStr for $t {
|
||||
fn type_str(&self) -> &'static str { $s }
|
||||
})+
|
||||
};
|
||||
}
|
||||
|
||||
impl_type_str!(
|
||||
ContainerElement => "container",
|
||||
StaticTextElement => "static_text",
|
||||
TextElement => "text",
|
||||
LineElement => "line",
|
||||
ImageElement => "image",
|
||||
PageNumberElement => "page_number",
|
||||
BarcodeElement => "barcode",
|
||||
RepeatingTableElement => "repeating_table",
|
||||
PageBreakElement => "page_break",
|
||||
CurrentDateElement => "current_date",
|
||||
ShapeElement => "shape",
|
||||
CheckboxElement => "checkbox",
|
||||
CalculatedTextElement => "calculated_text",
|
||||
RichTextElement => "rich_text",
|
||||
ChartElement => "chart",
|
||||
);
|
||||
|
||||
pub trait HasTextStyle {
|
||||
fn text_style(&self) -> &TextStyle;
|
||||
}
|
||||
|
||||
macro_rules! impl_has_text_style {
|
||||
($($t:ty),+ $(,)?) => {
|
||||
$(impl HasTextStyle for $t {
|
||||
fn text_style(&self) -> &TextStyle { &self.style }
|
||||
})+
|
||||
};
|
||||
}
|
||||
|
||||
impl_has_text_style!(
|
||||
StaticTextElement,
|
||||
TextElement,
|
||||
PageNumberElement,
|
||||
CurrentDateElement,
|
||||
CalculatedTextElement,
|
||||
RichTextElement,
|
||||
);
|
||||
|
||||
pub trait HasOptionalBinding {
|
||||
fn binding(&self) -> Option<&ScalarBinding>;
|
||||
fn static_value(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
impl HasOptionalBinding for ImageElement {
|
||||
fn binding(&self) -> Option<&ScalarBinding> {
|
||||
self.binding.as_ref()
|
||||
}
|
||||
fn static_value(&self) -> Option<&str> {
|
||||
self.src.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasOptionalBinding for BarcodeElement {
|
||||
fn binding(&self) -> Option<&ScalarBinding> {
|
||||
self.binding.as_ref()
|
||||
}
|
||||
fn static_value(&self) -> Option<&str> {
|
||||
self.value.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
// --- Element tipleri ---
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
@@ -316,91 +445,59 @@ pub enum TemplateElement {
|
||||
}
|
||||
|
||||
impl TemplateElement {
|
||||
pub fn id(&self) -> &str {
|
||||
fn inner_base(&self) -> &ElementBase {
|
||||
match self {
|
||||
Self::Container(e) => &e.id,
|
||||
Self::StaticText(e) => &e.id,
|
||||
Self::Text(e) => &e.id,
|
||||
Self::Line(e) => &e.id,
|
||||
Self::RepeatingTable(e) => &e.id,
|
||||
Self::Image(e) => &e.id,
|
||||
Self::PageNumber(e) => &e.id,
|
||||
Self::Barcode(e) => &e.id,
|
||||
Self::PageBreak(e) => &e.id,
|
||||
Self::CurrentDate(e) => &e.id,
|
||||
Self::Shape(e) => &e.id,
|
||||
Self::Checkbox(e) => &e.id,
|
||||
Self::CalculatedText(e) => &e.id,
|
||||
Self::RichText(e) => &e.id,
|
||||
Self::Chart(e) => &e.id,
|
||||
Self::Container(e) => e.base(),
|
||||
Self::StaticText(e) => e.base(),
|
||||
Self::Text(e) => e.base(),
|
||||
Self::Line(e) => e.base(),
|
||||
Self::RepeatingTable(e) => e.base(),
|
||||
Self::Image(e) => e.base(),
|
||||
Self::PageNumber(e) => e.base(),
|
||||
Self::Barcode(e) => e.base(),
|
||||
Self::PageBreak(e) => e.base(),
|
||||
Self::CurrentDate(e) => e.base(),
|
||||
Self::Shape(e) => e.base(),
|
||||
Self::Checkbox(e) => e.base(),
|
||||
Self::CalculatedText(e) => e.base(),
|
||||
Self::RichText(e) => e.base(),
|
||||
Self::Chart(e) => e.base(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &str {
|
||||
&self.inner_base().id
|
||||
}
|
||||
|
||||
pub fn position(&self) -> &PositionMode {
|
||||
match self {
|
||||
Self::Container(e) => &e.position,
|
||||
Self::StaticText(e) => &e.position,
|
||||
Self::Text(e) => &e.position,
|
||||
Self::Line(e) => &e.position,
|
||||
Self::RepeatingTable(e) => &e.position,
|
||||
Self::Image(e) => &e.position,
|
||||
Self::PageNumber(e) => &e.position,
|
||||
Self::Barcode(e) => &e.position,
|
||||
Self::PageBreak(_) => &PositionMode::Flow,
|
||||
Self::CurrentDate(e) => &e.position,
|
||||
Self::Shape(e) => &e.position,
|
||||
Self::Checkbox(e) => &e.position,
|
||||
Self::CalculatedText(e) => &e.position,
|
||||
Self::RichText(e) => &e.position,
|
||||
Self::Chart(e) => &e.position,
|
||||
}
|
||||
&self.inner_base().position
|
||||
}
|
||||
|
||||
pub fn condition(&self) -> Option<&Condition> {
|
||||
match self {
|
||||
Self::Container(e) => e.condition.as_ref(),
|
||||
Self::StaticText(e) => e.condition.as_ref(),
|
||||
Self::Text(e) => e.condition.as_ref(),
|
||||
Self::Line(e) => e.condition.as_ref(),
|
||||
Self::RepeatingTable(e) => e.condition.as_ref(),
|
||||
Self::Image(e) => e.condition.as_ref(),
|
||||
Self::PageNumber(e) => e.condition.as_ref(),
|
||||
Self::Barcode(e) => e.condition.as_ref(),
|
||||
Self::PageBreak(e) => e.condition.as_ref(),
|
||||
Self::CurrentDate(e) => e.condition.as_ref(),
|
||||
Self::Shape(e) => e.condition.as_ref(),
|
||||
Self::Checkbox(e) => e.condition.as_ref(),
|
||||
Self::CalculatedText(e) => e.condition.as_ref(),
|
||||
Self::RichText(e) => e.condition.as_ref(),
|
||||
Self::Chart(e) => e.condition.as_ref(),
|
||||
}
|
||||
self.inner_base().condition.as_ref()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> &SizeConstraint {
|
||||
static DEFAULT_SIZE: SizeConstraint = SizeConstraint {
|
||||
width: SizeValue::Auto,
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
};
|
||||
&self.inner_base().size
|
||||
}
|
||||
|
||||
pub fn type_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Container(e) => &e.size,
|
||||
Self::StaticText(e) => &e.size,
|
||||
Self::Text(e) => &e.size,
|
||||
Self::Line(e) => &e.size,
|
||||
Self::RepeatingTable(e) => &e.size,
|
||||
Self::Image(e) => &e.size,
|
||||
Self::PageNumber(e) => &e.size,
|
||||
Self::Barcode(e) => &e.size,
|
||||
Self::PageBreak(_) => &DEFAULT_SIZE,
|
||||
Self::CurrentDate(e) => &e.size,
|
||||
Self::Shape(e) => &e.size,
|
||||
Self::Checkbox(e) => &e.size,
|
||||
Self::CalculatedText(e) => &e.size,
|
||||
Self::RichText(e) => &e.size,
|
||||
Self::Chart(e) => &e.size,
|
||||
Self::Container(e) => e.type_str(),
|
||||
Self::StaticText(e) => e.type_str(),
|
||||
Self::Text(e) => e.type_str(),
|
||||
Self::Line(e) => e.type_str(),
|
||||
Self::RepeatingTable(e) => e.type_str(),
|
||||
Self::Image(e) => e.type_str(),
|
||||
Self::PageNumber(e) => e.type_str(),
|
||||
Self::Barcode(e) => e.type_str(),
|
||||
Self::PageBreak(e) => e.type_str(),
|
||||
Self::CurrentDate(e) => e.type_str(),
|
||||
Self::Shape(e) => e.type_str(),
|
||||
Self::Checkbox(e) => e.type_str(),
|
||||
Self::CalculatedText(e) => e.type_str(),
|
||||
Self::RichText(e) => e.type_str(),
|
||||
Self::Chart(e) => e.type_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,11 +505,8 @@ impl TemplateElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RichTextElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
#[serde(default)]
|
||||
pub style: TextStyle, // varsayilan stil (span'lar override edebilir)
|
||||
pub content: Vec<RichTextSpan>,
|
||||
@@ -421,13 +515,8 @@ pub struct RichTextElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ContainerElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
#[serde(default)]
|
||||
pub position: PositionMode,
|
||||
#[serde(default)]
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
#[serde(default = "default_column")]
|
||||
pub direction: String,
|
||||
#[serde(default)]
|
||||
@@ -463,11 +552,8 @@ fn default_start() -> String {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StaticTextElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: TextStyle,
|
||||
pub content: String,
|
||||
}
|
||||
@@ -475,11 +561,8 @@ pub struct StaticTextElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TextElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: TextStyle,
|
||||
pub content: Option<String>,
|
||||
pub binding: ScalarBinding,
|
||||
@@ -488,22 +571,16 @@ pub struct TextElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LineElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: LineStyle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImageElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub src: Option<String>,
|
||||
pub binding: Option<ScalarBinding>,
|
||||
pub style: ImageStyle,
|
||||
@@ -512,11 +589,8 @@ pub struct ImageElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PageNumberElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: TextStyle,
|
||||
pub format: Option<String>,
|
||||
}
|
||||
@@ -524,11 +598,8 @@ pub struct PageNumberElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BarcodeElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub format: String, // qr, ean13, ean8, code128, code39
|
||||
pub value: Option<String>,
|
||||
pub binding: Option<ScalarBinding>,
|
||||
@@ -538,11 +609,8 @@ pub struct BarcodeElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RepeatingTableElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub data_source: ArrayBinding,
|
||||
pub columns: Vec<TableColumn>,
|
||||
pub style: TableStyle,
|
||||
@@ -557,19 +625,15 @@ fn default_true() -> Option<bool> {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PageBreakElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CurrentDateElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: TextStyle,
|
||||
pub format: Option<String>,
|
||||
}
|
||||
@@ -577,11 +641,8 @@ pub struct CurrentDateElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShapeElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub shape_type: String, // rectangle, ellipse, rounded_rectangle
|
||||
pub style: ContainerStyle,
|
||||
}
|
||||
@@ -598,11 +659,8 @@ pub struct CheckboxStyle {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CheckboxElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub checked: Option<bool>, // statik değer
|
||||
pub binding: Option<ScalarBinding>, // dinamik boolean binding
|
||||
pub style: CheckboxStyle,
|
||||
@@ -611,11 +669,8 @@ pub struct CheckboxElement {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CalculatedTextElement {
|
||||
pub id: String,
|
||||
#[serde(default)]
|
||||
pub condition: Option<Condition>,
|
||||
pub position: PositionMode,
|
||||
pub size: SizeConstraint,
|
||||
#[serde(flatten)]
|
||||
pub base: ElementBase,
|
||||
pub style: TextStyle,
|
||||
pub expression: String,
|
||||
pub format: Option<String>,
|
||||
|
||||
@@ -116,10 +116,19 @@ export interface BarcodeStyle {
|
||||
includeText?: boolean // barkod altına değer yazılsın mı (QR hariç)
|
||||
}
|
||||
|
||||
// --- Condition (koşullu gösterim) ---
|
||||
|
||||
export interface Condition {
|
||||
path: string
|
||||
operator: string
|
||||
value?: unknown
|
||||
}
|
||||
|
||||
// --- Element tipleri ---
|
||||
|
||||
interface BaseElement {
|
||||
id: string
|
||||
condition?: Condition
|
||||
position: PositionMode
|
||||
size: SizeConstraint
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 166 KiB |
@@ -2,6 +2,9 @@ use dreport_core::models::*;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Re-export HasOptionalBinding for convenience
|
||||
pub use dreport_core::models::HasOptionalBinding;
|
||||
|
||||
/// Şu anki tarihi verilen format string'ine göre formatla.
|
||||
/// Desteklenen tokenlar: YYYY, MM, DD, HH, mm, ss
|
||||
/// WASM'da js_sys::Date, native'de SystemTime kullanır.
|
||||
@@ -226,6 +229,15 @@ fn json_values_eq(a: &Value, b: &Value) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Çözümle optional binding: binding varsa data'dan, yoksa static value'dan
|
||||
fn resolve_optional_binding(el: &impl HasOptionalBinding, data: &Value) -> String {
|
||||
if let Some(binding) = el.binding() {
|
||||
value_to_string(resolve_path(data, &binding.path))
|
||||
} else {
|
||||
el.static_value().unwrap_or_default().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedData, format_config: &dreport_core::models::FormatConfig) {
|
||||
// Koşul kontrolü: condition varsa ve sağlanmıyorsa, hidden olarak işaretle ve çık
|
||||
if let Some(condition) = el.condition() && !evaluate_condition(condition, data) {
|
||||
@@ -235,7 +247,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
|
||||
match el {
|
||||
TemplateElement::StaticText(e) => {
|
||||
resolved.texts.insert(e.id.clone(), e.content.clone());
|
||||
resolved.texts.insert(e.base.id.clone(), e.content.clone());
|
||||
}
|
||||
TemplateElement::Text(e) => {
|
||||
let bound_value = value_to_string(resolve_path(data, &e.binding.path));
|
||||
@@ -243,7 +255,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
Some(prefix) if !prefix.is_empty() => format!("{}{}", prefix, bound_value),
|
||||
_ => bound_value,
|
||||
};
|
||||
resolved.texts.insert(e.id.clone(), text);
|
||||
resolved.texts.insert(e.base.id.clone(), text);
|
||||
}
|
||||
TemplateElement::PageNumber(e) => {
|
||||
// Format string'i sakla — sayfa bölme sonrası gerçek değerlerle çözülecek
|
||||
@@ -254,28 +266,18 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
.to_string();
|
||||
resolved
|
||||
.page_number_formats
|
||||
.insert(e.id.clone(), fmt.clone());
|
||||
.insert(e.base.id.clone(), fmt.clone());
|
||||
// Placeholder koy (tek sayfalık fallback)
|
||||
resolved.texts.insert(
|
||||
e.id.clone(),
|
||||
e.base.id.clone(),
|
||||
fmt.replace("{current}", "1").replace("{total}", "1"),
|
||||
);
|
||||
}
|
||||
TemplateElement::Barcode(e) => {
|
||||
let value = if let Some(binding) = &e.binding {
|
||||
value_to_string(resolve_path(data, &binding.path))
|
||||
} else {
|
||||
e.value.clone().unwrap_or_default()
|
||||
};
|
||||
resolved.barcodes.insert(e.id.clone(), value);
|
||||
resolved.barcodes.insert(e.base.id.clone(), resolve_optional_binding(e, data));
|
||||
}
|
||||
TemplateElement::Image(e) => {
|
||||
let src = if let Some(binding) = &e.binding {
|
||||
value_to_string(resolve_path(data, &binding.path))
|
||||
} else {
|
||||
e.src.clone().unwrap_or_default()
|
||||
};
|
||||
resolved.images.insert(e.id.clone(), src);
|
||||
resolved.images.insert(e.base.id.clone(), resolve_optional_binding(e, data));
|
||||
}
|
||||
TemplateElement::RepeatingTable(e) => {
|
||||
let array = resolve_path(data, &e.data_source.path);
|
||||
@@ -302,7 +304,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
resolved.tables.insert(e.id.clone(), ResolvedTable { rows });
|
||||
resolved.tables.insert(e.base.id.clone(), ResolvedTable { rows });
|
||||
}
|
||||
TemplateElement::Container(e) => {
|
||||
for child in &e.children {
|
||||
@@ -312,7 +314,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
TemplateElement::CurrentDate(e) => {
|
||||
let fmt = e.format.as_deref().unwrap_or("DD.MM.YYYY");
|
||||
let text = format_current_date(fmt);
|
||||
resolved.texts.insert(e.id.clone(), text);
|
||||
resolved.texts.insert(e.base.id.clone(), text);
|
||||
}
|
||||
TemplateElement::Checkbox(e) => {
|
||||
let checked = if let Some(binding) = &e.binding {
|
||||
@@ -327,7 +329,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
e.checked.unwrap_or(false)
|
||||
};
|
||||
// Store as "true"/"false" string in texts map
|
||||
resolved.texts.insert(e.id.clone(), checked.to_string());
|
||||
resolved.texts.insert(e.base.id.clone(), checked.to_string());
|
||||
}
|
||||
TemplateElement::CalculatedText(e) => {
|
||||
let result = crate::expr_eval::evaluate_expression(&e.expression, data);
|
||||
@@ -338,7 +340,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
} else {
|
||||
formatted
|
||||
};
|
||||
resolved.texts.insert(e.id.clone(), text);
|
||||
resolved.texts.insert(e.base.id.clone(), text);
|
||||
}
|
||||
TemplateElement::RichText(e) => {
|
||||
let spans: Vec<ResolvedRichSpan> = e
|
||||
@@ -371,7 +373,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
resolved.rich_texts.insert(e.id.clone(), spans);
|
||||
resolved.rich_texts.insert(e.base.id.clone(), spans);
|
||||
}
|
||||
TemplateElement::Chart(e) => {
|
||||
let array = resolve_path(data, &e.data_source.path);
|
||||
@@ -389,7 +391,7 @@ fn resolve_element(el: &TemplateElement, data: &Value, resolved: &mut ResolvedDa
|
||||
group_mode: e.group_mode.clone(),
|
||||
},
|
||||
};
|
||||
resolved.charts.insert(e.id.clone(), chart_data);
|
||||
resolved.charts.insert(e.base.id.clone(), chart_data);
|
||||
}
|
||||
TemplateElement::Line(_) => {}
|
||||
TemplateElement::Shape(_) => {}
|
||||
@@ -542,10 +544,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -554,10 +553,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::Text(TextElement {
|
||||
id: "el_name".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("el_name".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle::default(),
|
||||
content: None,
|
||||
binding: ScalarBinding {
|
||||
@@ -593,10 +589,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -605,10 +598,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::Text(TextElement {
|
||||
id: "el_no".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("el_no".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle::default(),
|
||||
content: Some("Fatura No: ".to_string()),
|
||||
binding: ScalarBinding {
|
||||
@@ -641,10 +631,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -653,10 +640,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "title".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("title".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle::default(),
|
||||
content: "FATURA".to_string(),
|
||||
})],
|
||||
@@ -682,10 +666,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -694,10 +675,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("tbl".to_string(), SizeConstraint::default()),
|
||||
data_source: ArrayBinding {
|
||||
path: "kalemler".to_string(),
|
||||
},
|
||||
@@ -754,10 +732,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -766,10 +741,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("tbl".to_string(), SizeConstraint::default()),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -808,10 +780,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -820,10 +789,7 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::Text(TextElement {
|
||||
id: "el_missing".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("el_missing".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle::default(),
|
||||
content: None,
|
||||
binding: ScalarBinding {
|
||||
|
||||
@@ -223,6 +223,75 @@ pub struct ResolvedStyle {
|
||||
pub barcode_include_text: Option<bool>,
|
||||
}
|
||||
|
||||
// --- From<&XStyle> for ResolvedStyle ---
|
||||
|
||||
impl From<&dreport_core::models::TextStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::TextStyle) -> Self {
|
||||
Self {
|
||||
font_size: s.font_size,
|
||||
font_weight: s.font_weight.clone(),
|
||||
font_style: s.font_style.clone(),
|
||||
font_family: s.font_family.clone(),
|
||||
color: s.color.clone(),
|
||||
text_align: s.align.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dreport_core::models::ContainerStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::ContainerStyle) -> Self {
|
||||
Self {
|
||||
background_color: s.background_color.clone(),
|
||||
border_color: s.border_color.clone(),
|
||||
border_width: s.border_width,
|
||||
border_radius: s.border_radius,
|
||||
border_style: s.border_style.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dreport_core::models::LineStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::LineStyle) -> Self {
|
||||
Self {
|
||||
stroke_color: s.stroke_color.clone(),
|
||||
stroke_width: s.stroke_width,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dreport_core::models::ImageStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::ImageStyle) -> Self {
|
||||
Self {
|
||||
object_fit: s.object_fit.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dreport_core::models::BarcodeStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::BarcodeStyle) -> Self {
|
||||
Self {
|
||||
barcode_color: s.color.clone(),
|
||||
barcode_include_text: s.include_text,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&dreport_core::models::CheckboxStyle> for ResolvedStyle {
|
||||
fn from(s: &dreport_core::models::CheckboxStyle) -> Self {
|
||||
Self {
|
||||
color: s.check_color.clone(),
|
||||
border_color: s.border_color.clone(),
|
||||
border_width: s.border_width,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ana layout hesaplama fonksiyonu.
|
||||
/// Template + data + font verileri alır, her element için pozisyon döner.
|
||||
pub fn compute_layout(
|
||||
|
||||
@@ -1603,17 +1603,14 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint {
|
||||
width: SizeValue::Auto,
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -1628,17 +1625,14 @@ mod tests {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "title".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("title".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -1647,34 +1641,28 @@ mod tests {
|
||||
content: "FATURA".to_string(),
|
||||
}),
|
||||
TemplateElement::Line(LineElement {
|
||||
id: "line1".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("line1".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: LineStyle {
|
||||
stroke_color: Some("#000000".to_string()),
|
||||
stroke_width: Some(0.5),
|
||||
},
|
||||
}),
|
||||
TemplateElement::Text(TextElement {
|
||||
id: "firma".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("firma".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
|
||||
@@ -138,7 +138,7 @@ pub fn container_to_style(el: &ContainerElement, parent_direction: Option<&str>)
|
||||
};
|
||||
|
||||
// Pozisyon moduna göre
|
||||
match &el.position {
|
||||
match &el.base.position {
|
||||
PositionMode::Absolute { x, y } => {
|
||||
style.position = Position::Absolute;
|
||||
style.inset = Rect {
|
||||
@@ -152,7 +152,7 @@ pub fn container_to_style(el: &ContainerElement, parent_direction: Option<&str>)
|
||||
}
|
||||
|
||||
// Boyut
|
||||
apply_size_to_style(&mut style, &el.size, parent_direction);
|
||||
apply_size_to_style(&mut style, &el.base.size, parent_direction);
|
||||
|
||||
// Container border
|
||||
if let Some(bw) = el.style.border_width {
|
||||
@@ -197,7 +197,7 @@ pub fn leaf_style(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use dreport_core::models::{ContainerStyle, Padding};
|
||||
use dreport_core::models::{ContainerStyle, ElementBase, Padding};
|
||||
|
||||
#[test]
|
||||
fn test_mm_to_pt_conversion() {
|
||||
@@ -327,10 +327,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_container_to_style_direction() {
|
||||
let el = ContainerElement {
|
||||
id: "test".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("test".to_string(), SizeConstraint::default()),
|
||||
direction: "row".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -354,10 +351,12 @@ mod tests {
|
||||
#[test]
|
||||
fn test_container_to_style_absolute() {
|
||||
let el = ContainerElement {
|
||||
id: "test".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Absolute { x: 20.0, y: 30.0 },
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "test".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Absolute { x: 20.0, y: 30.0 },
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
|
||||
@@ -186,7 +186,7 @@ pub fn expand_table_cached(
|
||||
) -> ContainerElement {
|
||||
let rows = resolved
|
||||
.tables
|
||||
.get(&table.id)
|
||||
.get(&table.base.id)
|
||||
.map(|t| t.rows.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
let key = table_cache_key(table, rows, available_width_mm);
|
||||
@@ -211,7 +211,7 @@ pub fn expand_table(
|
||||
measurer: &mut TextMeasurer,
|
||||
available_width_mm: f64,
|
||||
) -> ContainerElement {
|
||||
let resolved_table = resolved.tables.get(&table.id);
|
||||
let resolved_table = resolved.tables.get(&table.base.id);
|
||||
let rows = resolved_table.map(|t| t.rows.as_slice()).unwrap_or(&[]);
|
||||
|
||||
// Auto sütunlar için içerik bazlı genişlik hesapla
|
||||
@@ -232,17 +232,14 @@ pub fn expand_table(
|
||||
.enumerate()
|
||||
.map(|(i, col)| {
|
||||
let text = TemplateElement::StaticText(StaticTextElement {
|
||||
id: format!("{}_hdr_{}", table.id, i),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_hdr_{}", table.base.id, i),
|
||||
SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: table.style.header_font_size.or(table.style.font_size),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -254,25 +251,13 @@ pub fn expand_table(
|
||||
content: col.title.clone(),
|
||||
});
|
||||
TemplateElement::Container(ContainerElement {
|
||||
id: format!("{}_hdr_{}_wrap", table.id, i),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: effective_widths[i].clone(),
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_hdr_{}_wrap", table.base.id, i),
|
||||
SizeConstraint { width: effective_widths[i].clone(), ..Default::default() },
|
||||
),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
top: header_pad_v,
|
||||
right: header_pad_h,
|
||||
bottom: header_pad_v,
|
||||
left: header_pad_h,
|
||||
},
|
||||
padding: Padding { top: header_pad_v, right: header_pad_h, bottom: header_pad_v, left: header_pad_h },
|
||||
align: "stretch".to_string(),
|
||||
justify: "start".to_string(),
|
||||
style: ContainerStyle::default(),
|
||||
@@ -283,31 +268,16 @@ pub fn expand_table(
|
||||
.collect();
|
||||
|
||||
children.push(TemplateElement::Container(ContainerElement {
|
||||
id: format!("{}_header", table.id),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_header", table.base.id),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
direction: "row".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
},
|
||||
padding: Padding::default(),
|
||||
align: "stretch".to_string(),
|
||||
justify: "start".to_string(),
|
||||
style: ContainerStyle {
|
||||
background_color: table.style.header_bg.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
style: ContainerStyle { background_color: table.style.header_bg.clone(), ..Default::default() },
|
||||
children: header_cells,
|
||||
break_inside: "auto".to_string(),
|
||||
}));
|
||||
@@ -315,17 +285,10 @@ pub fn expand_table(
|
||||
// Header altına ayırıcı çizgi
|
||||
if table.style.border_color.is_some() {
|
||||
children.push(TemplateElement::Line(LineElement {
|
||||
id: format!("{}_header_line", table.id),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_header_line", table.base.id),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
style: LineStyle {
|
||||
stroke_color: table.style.border_color.clone(),
|
||||
stroke_width: table.style.border_width,
|
||||
@@ -343,17 +306,10 @@ pub fn expand_table(
|
||||
let text_content = row_data.get(col_idx).cloned().unwrap_or_default();
|
||||
|
||||
let text = TemplateElement::StaticText(StaticTextElement {
|
||||
id: format!("{}_r{}c{}", table.id, row_idx, col_idx),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_r{}c{}", table.base.id, row_idx, col_idx),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: table.style.font_size,
|
||||
font_weight: None,
|
||||
@@ -365,25 +321,13 @@ pub fn expand_table(
|
||||
content: text_content,
|
||||
});
|
||||
TemplateElement::Container(ContainerElement {
|
||||
id: format!("{}_r{}c{}_wrap", table.id, row_idx, col_idx),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: effective_widths[col_idx].clone(),
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_r{}c{}_wrap", table.base.id, row_idx, col_idx),
|
||||
SizeConstraint { width: effective_widths[col_idx].clone(), ..Default::default() },
|
||||
),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
top: cell_pad_v,
|
||||
right: cell_pad_h,
|
||||
bottom: cell_pad_v,
|
||||
left: cell_pad_h,
|
||||
},
|
||||
padding: Padding { top: cell_pad_v, right: cell_pad_h, bottom: cell_pad_v, left: cell_pad_h },
|
||||
align: "stretch".to_string(),
|
||||
justify: "start".to_string(),
|
||||
style: ContainerStyle::default(),
|
||||
@@ -401,31 +345,16 @@ pub fn expand_table(
|
||||
};
|
||||
|
||||
children.push(TemplateElement::Container(ContainerElement {
|
||||
id: format!("{}_row_{}", table.id, row_idx),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
format!("{}_row_{}", table.base.id, row_idx),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
direction: "row".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
},
|
||||
padding: Padding::default(),
|
||||
align: "stretch".to_string(),
|
||||
justify: "start".to_string(),
|
||||
style: ContainerStyle {
|
||||
background_color: bg,
|
||||
..Default::default()
|
||||
},
|
||||
style: ContainerStyle { background_color: bg, ..Default::default() },
|
||||
children: cells,
|
||||
break_inside: "auto".to_string(),
|
||||
}));
|
||||
@@ -433,18 +362,15 @@ pub fn expand_table(
|
||||
|
||||
// Wrapper container (column direction, tüm tablo)
|
||||
ContainerElement {
|
||||
id: table.id.clone(),
|
||||
condition: None,
|
||||
position: table.position.clone(),
|
||||
size: table.size.clone(),
|
||||
base: ElementBase {
|
||||
id: table.base.id.clone(),
|
||||
condition: None,
|
||||
position: table.base.position.clone(),
|
||||
size: table.base.size.clone(),
|
||||
},
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
top: 0.0,
|
||||
right: 0.0,
|
||||
bottom: 0.0,
|
||||
left: 0.0,
|
||||
},
|
||||
padding: Padding::default(),
|
||||
align: "stretch".to_string(),
|
||||
justify: "start".to_string(),
|
||||
style: ContainerStyle {
|
||||
@@ -478,14 +404,10 @@ mod tests {
|
||||
.collect();
|
||||
|
||||
RepeatingTableElement {
|
||||
id: "tbl".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"tbl".to_string(),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -554,7 +476,7 @@ mod tests {
|
||||
let container = expand_table(&table, &resolved, &mut measurer, 180.0);
|
||||
|
||||
// Wrapper container properties
|
||||
assert_eq!(container.id, "tbl");
|
||||
assert_eq!(container.base.id, "tbl");
|
||||
assert_eq!(container.direction, "column");
|
||||
|
||||
// Children: header row + 2 data rows (no border_color so no separator line)
|
||||
@@ -563,7 +485,7 @@ mod tests {
|
||||
// First child is header row container
|
||||
match &container.children[0] {
|
||||
TemplateElement::Container(c) => {
|
||||
assert_eq!(c.id, "tbl_header");
|
||||
assert_eq!(c.base.id, "tbl_header");
|
||||
assert_eq!(c.direction, "row");
|
||||
assert_eq!(c.children.len(), 2); // 2 columns
|
||||
// Check header cell text (inside wrapper container)
|
||||
@@ -577,7 +499,7 @@ mod tests {
|
||||
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.base.id, format!("tbl_row_{}", row_idx));
|
||||
assert_eq!(c.direction, "row");
|
||||
assert_eq!(c.children.len(), 2);
|
||||
}
|
||||
@@ -666,7 +588,7 @@ mod tests {
|
||||
// Second child should be a Line
|
||||
match &container.children[1] {
|
||||
TemplateElement::Line(l) => {
|
||||
assert_eq!(l.id, "tbl_header_line");
|
||||
assert_eq!(l.base.id, "tbl_header_line");
|
||||
}
|
||||
_ => panic!("Expected Line separator after header"),
|
||||
}
|
||||
@@ -738,14 +660,10 @@ mod tests {
|
||||
];
|
||||
|
||||
let table = RepeatingTableElement {
|
||||
id: "tbl".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"tbl".to_string(),
|
||||
SizeConstraint { width: SizeValue::Fr { value: 1.0 }, ..Default::default() },
|
||||
),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -769,14 +687,14 @@ mod tests {
|
||||
match &container.children[0] {
|
||||
TemplateElement::Container(c) => {
|
||||
let w0 = match &c.children[0] {
|
||||
TemplateElement::Container(wrap) => match &wrap.size.width {
|
||||
TemplateElement::Container(wrap) => match &wrap.base.size.width {
|
||||
SizeValue::Fixed { value } => *value,
|
||||
_ => panic!("Expected Fixed width for auto column wrapper"),
|
||||
},
|
||||
_ => panic!("Expected Container wrapper"),
|
||||
};
|
||||
let w1 = match &c.children[1] {
|
||||
TemplateElement::Container(wrap) => match &wrap.size.width {
|
||||
TemplateElement::Container(wrap) => match &wrap.base.size.width {
|
||||
SizeValue::Fixed { value } => *value,
|
||||
_ => panic!("Expected Fixed width for auto column wrapper"),
|
||||
},
|
||||
@@ -811,7 +729,7 @@ mod tests {
|
||||
// Second call — same inputs — cache hit
|
||||
let result2 = expand_table_cached(&table, &resolved, &mut measurer, 180.0, &mut cache);
|
||||
assert_eq!(cache.len(), 1); // no new entry
|
||||
assert_eq!(result1.id, result2.id);
|
||||
assert_eq!(result1.base.id, result2.base.id);
|
||||
assert_eq!(result1.children.len(), result2.children.len());
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ fn collect_break_modes(root: &ContainerElement) -> HashMap<String, String> {
|
||||
|
||||
fn collect_break_modes_recursive(el: &TemplateElement, modes: &mut HashMap<String, String>) {
|
||||
if let TemplateElement::Container(c) = el {
|
||||
modes.insert(c.id.clone(), c.break_inside.clone());
|
||||
modes.insert(c.base.id.clone(), c.break_inside.clone());
|
||||
for child in &c.children {
|
||||
collect_break_modes_recursive(child, modes);
|
||||
}
|
||||
@@ -208,7 +208,7 @@ fn collect_no_repeat_recursive(el: &TemplateElement, set: &mut std::collections:
|
||||
}
|
||||
TemplateElement::RepeatingTable(t) => {
|
||||
if t.repeat_header == Some(false) {
|
||||
set.insert(t.id.clone());
|
||||
set.insert(t.base.id.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -233,7 +233,7 @@ fn build_container(
|
||||
// Child'lar için kullanılabilir genişliği hesapla
|
||||
// Container'ın kendi padding ve border'ını çıkar
|
||||
let border_w = el.style.border_width.unwrap_or(0.0);
|
||||
let container_own_width = match &el.size.width {
|
||||
let container_own_width = match &el.base.size.width {
|
||||
SizeValue::Fixed { value } => *value,
|
||||
_ => page_width_mm, // Fr veya Auto ise parent'ın genişliğini kullan
|
||||
};
|
||||
@@ -268,17 +268,10 @@ fn build_container(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: el.id.clone(),
|
||||
element_type: "container".to_string(),
|
||||
element_id: el.base.id.clone(),
|
||||
element_type: el.type_str().to_string(),
|
||||
content: None,
|
||||
style: ResolvedStyle {
|
||||
background_color: el.style.background_color.clone(),
|
||||
border_color: el.style.border_color.clone(),
|
||||
border_width: el.style.border_width,
|
||||
border_radius: el.style.border_radius,
|
||||
border_style: el.style.border_style.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
style: (&el.style).into(),
|
||||
children_ids,
|
||||
},
|
||||
);
|
||||
@@ -309,88 +302,63 @@ fn build_element(
|
||||
page_width_mm,
|
||||
table_cache,
|
||||
),
|
||||
TemplateElement::StaticText(e) => build_text_leaf(
|
||||
TemplateElement::StaticText(e) => build_resolved_text_leaf(
|
||||
&e.base,
|
||||
e.type_str(),
|
||||
&e.style,
|
||||
taffy,
|
||||
node_map,
|
||||
&e.id,
|
||||
"static_text",
|
||||
resolved
|
||||
.texts
|
||||
.get(&e.id)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(&e.content),
|
||||
&e.style,
|
||||
&e.size,
|
||||
&e.position,
|
||||
resolved,
|
||||
parent_direction,
|
||||
&e.content,
|
||||
),
|
||||
TemplateElement::Text(e) => build_resolved_text_leaf(
|
||||
&e.base,
|
||||
e.type_str(),
|
||||
&e.style,
|
||||
taffy,
|
||||
node_map,
|
||||
resolved,
|
||||
parent_direction,
|
||||
"",
|
||||
),
|
||||
TemplateElement::PageNumber(e) => build_resolved_text_leaf(
|
||||
&e.base,
|
||||
e.type_str(),
|
||||
&e.style,
|
||||
taffy,
|
||||
node_map,
|
||||
resolved,
|
||||
parent_direction,
|
||||
"1 / 1",
|
||||
),
|
||||
TemplateElement::CurrentDate(e) => build_resolved_text_leaf(
|
||||
&e.base,
|
||||
e.type_str(),
|
||||
&e.style,
|
||||
taffy,
|
||||
node_map,
|
||||
resolved,
|
||||
parent_direction,
|
||||
"",
|
||||
),
|
||||
TemplateElement::CalculatedText(e) => build_resolved_text_leaf(
|
||||
&e.base,
|
||||
e.type_str(),
|
||||
&e.style,
|
||||
taffy,
|
||||
node_map,
|
||||
resolved,
|
||||
parent_direction,
|
||||
"",
|
||||
),
|
||||
TemplateElement::Text(e) => {
|
||||
let text = resolved.texts.get(&e.id).map(|s| s.as_str()).unwrap_or("");
|
||||
build_text_leaf(
|
||||
taffy,
|
||||
node_map,
|
||||
&e.id,
|
||||
"text",
|
||||
text,
|
||||
&e.style,
|
||||
&e.size,
|
||||
&e.position,
|
||||
parent_direction,
|
||||
)
|
||||
}
|
||||
TemplateElement::PageNumber(e) => {
|
||||
let text = resolved
|
||||
.texts
|
||||
.get(&e.id)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("1 / 1");
|
||||
build_text_leaf(
|
||||
taffy,
|
||||
node_map,
|
||||
&e.id,
|
||||
"page_number",
|
||||
text,
|
||||
&e.style,
|
||||
&e.size,
|
||||
&e.position,
|
||||
parent_direction,
|
||||
)
|
||||
}
|
||||
TemplateElement::CurrentDate(e) => {
|
||||
let text = resolved.texts.get(&e.id).map(|s| s.as_str()).unwrap_or("");
|
||||
build_text_leaf(
|
||||
taffy,
|
||||
node_map,
|
||||
&e.id,
|
||||
"current_date",
|
||||
text,
|
||||
&e.style,
|
||||
&e.size,
|
||||
&e.position,
|
||||
parent_direction,
|
||||
)
|
||||
}
|
||||
TemplateElement::CalculatedText(e) => {
|
||||
let text = resolved.texts.get(&e.id).map(|s| s.as_str()).unwrap_or("");
|
||||
build_text_leaf(
|
||||
taffy,
|
||||
node_map,
|
||||
&e.id,
|
||||
"calculated_text",
|
||||
text,
|
||||
&e.style,
|
||||
&e.size,
|
||||
&e.position,
|
||||
parent_direction,
|
||||
)
|
||||
}
|
||||
TemplateElement::Line(e) => {
|
||||
let stroke_w = e.style.stroke_width.unwrap_or(0.5);
|
||||
let style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
|
||||
// Line: genişlik parent'tan, yükseklik stroke width
|
||||
let mut leaf_style = style;
|
||||
if matches!(e.size.height, SizeValue::Auto) {
|
||||
if matches!(e.base.size.height, SizeValue::Auto) {
|
||||
leaf_style.size.height = Dimension::length(mm_to_pt(stroke_w));
|
||||
}
|
||||
|
||||
@@ -398,13 +366,13 @@ fn build_element(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "line".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::Line),
|
||||
style: ResolvedStyle {
|
||||
stroke_color: e.style.stroke_color.clone(),
|
||||
stroke_width: Some(stroke_w),
|
||||
..Default::default()
|
||||
style: {
|
||||
let mut s: ResolvedStyle = (&e.style).into();
|
||||
s.stroke_width = Some(stroke_w);
|
||||
s
|
||||
},
|
||||
children_ids: vec![],
|
||||
},
|
||||
@@ -412,37 +380,34 @@ fn build_element(
|
||||
Ok(node)
|
||||
}
|
||||
TemplateElement::Image(e) => {
|
||||
let style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let src = resolved.images.get(&e.id).cloned().unwrap_or_default();
|
||||
let style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
let src = resolved.images.get(&e.base.id).cloned().unwrap_or_default();
|
||||
|
||||
let node = taffy.new_leaf(style)?;
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "image".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::Image { src }),
|
||||
style: ResolvedStyle {
|
||||
object_fit: e.style.object_fit.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
style: (&e.style).into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
Ok(node)
|
||||
}
|
||||
TemplateElement::Barcode(e) => {
|
||||
let mut style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let value = resolved.barcodes.get(&e.id).cloned().unwrap_or_default();
|
||||
let mut style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
let value = resolved.barcodes.get(&e.base.id).cloned().unwrap_or_default();
|
||||
|
||||
// Barcode leaf'e minimum boyut ver (MeasureFunc yok, Auto=0 olur)
|
||||
let is_qr = e.format == "qr";
|
||||
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) {
|
||||
if matches!(e.base.size.height, SizeValue::Auto) {
|
||||
style.min_size.height = Dimension::length(mm_to_pt(default_h));
|
||||
}
|
||||
if matches!(e.size.width, SizeValue::Auto) {
|
||||
if matches!(e.base.size.width, SizeValue::Auto) {
|
||||
style.min_size.width = Dimension::length(mm_to_pt(default_w));
|
||||
}
|
||||
|
||||
@@ -450,17 +415,13 @@ fn build_element(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "barcode".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::Barcode {
|
||||
format: e.format.clone(),
|
||||
value,
|
||||
}),
|
||||
style: ResolvedStyle {
|
||||
barcode_color: e.style.color.clone(),
|
||||
barcode_include_text: e.style.include_text,
|
||||
..Default::default()
|
||||
},
|
||||
style: (&e.style).into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
@@ -497,23 +458,17 @@ fn build_element(
|
||||
)
|
||||
}
|
||||
TemplateElement::Shape(e) => {
|
||||
let style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
let node = taffy.new_leaf(style)?;
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "shape".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::Shape {
|
||||
shape_type: e.shape_type.clone(),
|
||||
}),
|
||||
style: ResolvedStyle {
|
||||
background_color: e.style.background_color.clone(),
|
||||
border_color: e.style.border_color.clone(),
|
||||
border_width: e.style.border_width,
|
||||
border_radius: e.style.border_radius,
|
||||
..Default::default()
|
||||
},
|
||||
style: (&e.style).into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
@@ -522,19 +477,19 @@ fn build_element(
|
||||
TemplateElement::Checkbox(e) => {
|
||||
let checked_str = resolved
|
||||
.texts
|
||||
.get(&e.id)
|
||||
.get(&e.base.id)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("false");
|
||||
let checked = checked_str == "true";
|
||||
let box_size_mm = e.style.size.unwrap_or(4.0);
|
||||
let style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
|
||||
// Auto size → square based on style.size
|
||||
let mut leaf_style = style;
|
||||
if matches!(e.size.width, SizeValue::Auto) {
|
||||
if matches!(e.base.size.width, SizeValue::Auto) {
|
||||
leaf_style.size.width = Dimension::length(mm_to_pt(box_size_mm));
|
||||
}
|
||||
if matches!(e.size.height, SizeValue::Auto) {
|
||||
if matches!(e.base.size.height, SizeValue::Auto) {
|
||||
leaf_style.size.height = Dimension::length(mm_to_pt(box_size_mm));
|
||||
}
|
||||
|
||||
@@ -542,22 +497,17 @@ fn build_element(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "checkbox".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::Checkbox { checked }),
|
||||
style: ResolvedStyle {
|
||||
color: e.style.check_color.clone(),
|
||||
border_color: e.style.border_color.clone(),
|
||||
border_width: e.style.border_width,
|
||||
..Default::default()
|
||||
},
|
||||
style: (&e.style).into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
Ok(node)
|
||||
}
|
||||
TemplateElement::RichText(e) => {
|
||||
let spans = resolved.rich_texts.get(&e.id).cloned().unwrap_or_default();
|
||||
let spans = resolved.rich_texts.get(&e.base.id).cloned().unwrap_or_default();
|
||||
let rich_span_measures: Vec<crate::text_measure::RichSpanMeasure> = spans
|
||||
.iter()
|
||||
.map(|s| crate::text_measure::RichSpanMeasure {
|
||||
@@ -573,7 +523,7 @@ fn build_element(
|
||||
.map(|s| s.font_size_pt)
|
||||
.fold(11.0f32, f32::max);
|
||||
|
||||
let style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
|
||||
let context = MeasureContext {
|
||||
text: String::new(),
|
||||
@@ -600,39 +550,32 @@ fn build_element(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "rich_text".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: Some(ResolvedContent::RichText {
|
||||
spans: resolved_spans,
|
||||
}),
|
||||
style: ResolvedStyle {
|
||||
font_size: e.style.font_size,
|
||||
font_weight: e.style.font_weight.clone(),
|
||||
font_family: e.style.font_family.clone(),
|
||||
color: e.style.color.clone(),
|
||||
text_align: e.style.align.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
style: (&e.style).into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
Ok(node)
|
||||
}
|
||||
TemplateElement::Chart(e) => {
|
||||
let mut style = sizing::leaf_style(&e.size, &e.position, parent_direction);
|
||||
let mut style = sizing::leaf_style(&e.base.size, &e.base.position, parent_direction);
|
||||
// Default minimum boyut — Auto ise chart cok kucuk olmasin
|
||||
if matches!(e.size.width, SizeValue::Auto) {
|
||||
if matches!(e.base.size.width, SizeValue::Auto) {
|
||||
style.min_size.width = Dimension::length(mm_to_pt(80.0));
|
||||
}
|
||||
if matches!(e.size.height, SizeValue::Auto) {
|
||||
if matches!(e.base.size.height, SizeValue::Auto) {
|
||||
style.min_size.height = Dimension::length(mm_to_pt(60.0));
|
||||
}
|
||||
let node = taffy.new_leaf(style)?;
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "chart".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: None, // SVG collect_layout'ta uretilecek
|
||||
style: ResolvedStyle::default(),
|
||||
children_ids: vec![],
|
||||
@@ -653,8 +596,8 @@ fn build_element(
|
||||
node_map.insert(
|
||||
node,
|
||||
NodeInfo {
|
||||
element_id: e.id.clone(),
|
||||
element_type: "page_break".to_string(),
|
||||
element_id: e.base.id.clone(),
|
||||
element_type: e.type_str().to_string(),
|
||||
content: None,
|
||||
style: ResolvedStyle::default(),
|
||||
children_ids: vec![],
|
||||
@@ -669,7 +612,7 @@ fn build_element(
|
||||
fn register_expanded_texts(el: &TemplateElement, resolved: &mut ResolvedData) {
|
||||
match el {
|
||||
TemplateElement::StaticText(e) => {
|
||||
resolved.texts.insert(e.id.clone(), e.content.clone());
|
||||
resolved.texts.insert(e.base.id.clone(), e.content.clone());
|
||||
}
|
||||
TemplateElement::Container(e) => {
|
||||
for child in &e.children {
|
||||
@@ -680,6 +623,35 @@ fn register_expanded_texts(el: &TemplateElement, resolved: &mut ResolvedData) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic text leaf builder — HasTextStyle trait ile text-benzeri elementleri tek yerde build eder
|
||||
fn build_resolved_text_leaf(
|
||||
el_base: &ElementBase,
|
||||
el_type_str: &str,
|
||||
text_style: &TextStyle,
|
||||
taffy: &mut TaffyTree<MeasureContext>,
|
||||
node_map: &mut HashMap<NodeId, NodeInfo>,
|
||||
resolved: &ResolvedData,
|
||||
parent_direction: Option<&str>,
|
||||
fallback_text: &str,
|
||||
) -> Result<NodeId, LayoutError> {
|
||||
let text = resolved
|
||||
.texts
|
||||
.get(&el_base.id)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(fallback_text);
|
||||
build_text_leaf(
|
||||
taffy,
|
||||
node_map,
|
||||
&el_base.id,
|
||||
el_type_str,
|
||||
text,
|
||||
text_style,
|
||||
&el_base.size,
|
||||
&el_base.position,
|
||||
parent_direction,
|
||||
)
|
||||
}
|
||||
|
||||
/// Text leaf node oluştur (static_text, text, page_number için ortak)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_text_leaf(
|
||||
@@ -714,15 +686,7 @@ fn build_text_leaf(
|
||||
content: Some(ResolvedContent::Text {
|
||||
value: text.to_string(),
|
||||
}),
|
||||
style: ResolvedStyle {
|
||||
font_size: text_style.font_size,
|
||||
font_weight: text_style.font_weight.clone(),
|
||||
font_style: text_style.font_style.clone(),
|
||||
font_family: text_style.font_family.clone(),
|
||||
color: text_style.color.clone(),
|
||||
text_align: text_style.align.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
style: text_style.into(),
|
||||
children_ids: vec![],
|
||||
},
|
||||
);
|
||||
@@ -902,17 +866,14 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint {
|
||||
width: SizeValue::Auto,
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -927,17 +888,14 @@ mod tests {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "title".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("title".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -946,34 +904,28 @@ mod tests {
|
||||
content: "FATURA".to_string(),
|
||||
}),
|
||||
TemplateElement::Line(LineElement {
|
||||
id: "line1".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("line1".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: LineStyle {
|
||||
stroke_color: Some("#000000".to_string()),
|
||||
stroke_width: Some(0.5),
|
||||
},
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "body".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("body".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
@@ -1051,17 +1003,14 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint {
|
||||
width: SizeValue::Auto,
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
@@ -1075,17 +1024,14 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::Container(ContainerElement {
|
||||
id: "row".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("row".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
direction: "row".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -1100,17 +1046,14 @@ mod tests {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "left".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("left".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
@@ -1118,17 +1061,14 @@ mod tests {
|
||||
content: "Sol".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "right".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("right".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
@@ -1184,17 +1124,14 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint {
|
||||
width: SizeValue::Auto,
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
}),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
@@ -1208,16 +1145,18 @@ mod tests {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "abs_text".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Absolute { x: 50.0, y: 80.0 },
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 100.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
base: ElementBase {
|
||||
id: "abs_text".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Absolute { x: 50.0, y: 80.0 },
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 100.0 },
|
||||
height: SizeValue::Auto,
|
||||
min_width: None,
|
||||
min_height: None,
|
||||
max_width: None,
|
||||
max_height: None,
|
||||
},
|
||||
},
|
||||
style: TextStyle {
|
||||
font_size: Some(14.0),
|
||||
@@ -1276,10 +1215,7 @@ mod tests {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("root".to_string(), sz_auto.clone()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -1295,10 +1231,7 @@ mod tests {
|
||||
children: vec![
|
||||
// Header row
|
||||
TemplateElement::Container(ContainerElement {
|
||||
id: "c_header".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_fr_auto.clone(),
|
||||
base: ElementBase::flow("c_header".to_string(), sz_fr_auto.clone()),
|
||||
direction: "row".to_string(),
|
||||
gap: 5.0,
|
||||
padding: p0.clone(),
|
||||
@@ -1309,10 +1242,7 @@ mod tests {
|
||||
children: vec![
|
||||
// Sol: firma bilgileri
|
||||
TemplateElement::Container(ContainerElement {
|
||||
id: "c_firma".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_fr_auto.clone(),
|
||||
base: ElementBase::flow("c_firma".to_string(), sz_fr_auto.clone()),
|
||||
direction: "column".to_string(),
|
||||
gap: 1.0,
|
||||
padding: p0.clone(),
|
||||
@@ -1322,10 +1252,7 @@ mod tests {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_unvan".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_unvan".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(14.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -1334,10 +1261,7 @@ mod tests {
|
||||
content: "Teknova Yazılım ve Danışmanlık A.Ş.".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_adres".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_adres".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(9.0),
|
||||
..Default::default()
|
||||
@@ -1346,10 +1270,7 @@ mod tests {
|
||||
.to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_il".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_il".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(9.0),
|
||||
..Default::default()
|
||||
@@ -1357,10 +1278,7 @@ mod tests {
|
||||
content: "Istanbul".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_tel".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_tel".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(9.0),
|
||||
..Default::default()
|
||||
@@ -1368,10 +1286,7 @@ mod tests {
|
||||
content: "Tel: +90 212 555 0042".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_vd".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_vd".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(9.0),
|
||||
..Default::default()
|
||||
@@ -1379,10 +1294,7 @@ mod tests {
|
||||
content: "VD: Levent VD".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_firma_vn".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_firma_vn".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(9.0),
|
||||
..Default::default()
|
||||
@@ -1393,10 +1305,7 @@ mod tests {
|
||||
}),
|
||||
// Sağ: fatura başlığı
|
||||
TemplateElement::Container(ContainerElement {
|
||||
id: "c_fatura_baslik".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("c_fatura_baslik".to_string(), sz_auto.clone()),
|
||||
direction: "column".to_string(),
|
||||
gap: 2.0,
|
||||
padding: p0.clone(),
|
||||
@@ -1406,10 +1315,7 @@ mod tests {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_fatura_baslik".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_fatura_baslik".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -1418,10 +1324,7 @@ mod tests {
|
||||
content: "FATURA".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_fatura_no".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_fatura_no".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(10.0),
|
||||
..Default::default()
|
||||
@@ -1429,10 +1332,7 @@ mod tests {
|
||||
content: "No: FTR-2026-001547".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_fatura_tarih".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_fatura_tarih".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(10.0),
|
||||
..Default::default()
|
||||
@@ -1440,10 +1340,7 @@ mod tests {
|
||||
content: "Tarih: 2026-03-29".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "el_fatura_vade".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: sz_auto.clone(),
|
||||
base: ElementBase::flow("el_fatura_vade".to_string(), sz_auto.clone()),
|
||||
style: TextStyle {
|
||||
font_size: Some(10.0),
|
||||
..Default::default()
|
||||
|
||||
@@ -27,12 +27,9 @@ fn base_template() -> Template {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
condition: None,
|
||||
padding: Padding {
|
||||
top: 15.0,
|
||||
right: 15.0,
|
||||
@@ -57,14 +54,11 @@ fn test_1_2_text_wrapping_layout_height() {
|
||||
// Dar bir container'da uzun metin → yükseklik tek satırdan fazla olmalı
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "long_text".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("long_text".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 40.0 }, // 40mm genişlik — kısa
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
..Default::default()
|
||||
@@ -94,14 +88,11 @@ fn test_1_2_text_wrapping_pdf_renders() {
|
||||
// PDF render sırasında text wrapping çalışmalı — crash olmamalı
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "wrap_pdf".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("wrap_pdf".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 50.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
@@ -125,14 +116,11 @@ fn test_1_2_text_wrapping_pdf_renders() {
|
||||
fn test_1_3_image_object_fit_in_layout() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::Image(ImageElement {
|
||||
id: "img_contain".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("img_contain".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 40.0 },
|
||||
height: SizeValue::Fixed { value: 30.0 },
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
src: Some("data:image/png;base64,iVBORw0KGgo=".to_string()),
|
||||
binding: None,
|
||||
style: ImageStyle {
|
||||
@@ -167,14 +155,11 @@ fn test_1_4_italic_font_in_pdf() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "italic_text".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("italic_text".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
font_style: Some("italic".to_string()),
|
||||
@@ -205,14 +190,11 @@ fn test_1_4_bold_italic_font_in_pdf() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "bold_italic".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("bold_italic".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(14.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -239,14 +221,11 @@ fn test_2_1_repeat_header_false_no_repeat_on_second_page() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl_no_repeat".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("tbl_no_repeat".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -305,14 +284,11 @@ fn test_2_1_repeat_header_true_repeats_on_second_page() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl_repeat".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("tbl_repeat".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -389,14 +365,11 @@ fn test_2_2_table_column_format_currency() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl_fmt".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("tbl_fmt".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
data_source: ArrayBinding {
|
||||
path: "items".to_string(),
|
||||
},
|
||||
@@ -459,14 +432,11 @@ fn test_2_2_table_column_format_currency() {
|
||||
fn test_2_3_rounded_rectangle_renders() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::Shape(ShapeElement {
|
||||
id: "rounded_shape".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("rounded_shape".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 50.0 },
|
||||
height: SizeValue::Fixed { value: 30.0 },
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
shape_type: "rounded_rectangle".to_string(),
|
||||
style: ContainerStyle {
|
||||
background_color: Some("#3b82f6".to_string()),
|
||||
@@ -505,14 +475,11 @@ fn test_2_3_container_border_radius_renders() {
|
||||
tpl.root
|
||||
.children
|
||||
.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "text_in_rounded".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("text_in_rounded".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
..Default::default()
|
||||
@@ -604,14 +571,11 @@ fn test_2_7_format_config_in_template() {
|
||||
fn test_ellipse_shape_renders() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::Shape(ShapeElement {
|
||||
id: "ellipse".to_string(),
|
||||
position: PositionMode::Flow,
|
||||
condition: None,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("ellipse".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fixed { value: 40.0 },
|
||||
height: SizeValue::Fixed { value: 20.0 },
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
shape_type: "ellipse".to_string(),
|
||||
style: ContainerStyle {
|
||||
background_color: Some("#ff6600".to_string()),
|
||||
@@ -635,22 +599,21 @@ fn test_ellipse_shape_renders() {
|
||||
fn test_7_1_condition_gt_hides_element() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "always_visible".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("always_visible".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: "Visible".to_string(),
|
||||
}));
|
||||
tpl.root.children.push(TemplateElement::Text(TextElement {
|
||||
id: "conditional_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "toplamlar.iskonto".to_string(),
|
||||
operator: "gt".to_string(),
|
||||
value: Some(serde_json::json!(0)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "conditional_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "toplamlar.iskonto".to_string(),
|
||||
operator: "gt".to_string(),
|
||||
value: Some(serde_json::json!(0)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: None,
|
||||
binding: ScalarBinding { path: "toplamlar.iskonto".to_string() },
|
||||
@@ -676,14 +639,16 @@ fn test_7_1_condition_gt_hides_element() {
|
||||
fn test_7_1_condition_gt_shows_element() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::Text(TextElement {
|
||||
id: "conditional_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "toplamlar.iskonto".to_string(),
|
||||
operator: "gt".to_string(),
|
||||
value: Some(serde_json::json!(0)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "conditional_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "toplamlar.iskonto".to_string(),
|
||||
operator: "gt".to_string(),
|
||||
value: Some(serde_json::json!(0)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: None,
|
||||
binding: ScalarBinding { path: "toplamlar.iskonto".to_string() },
|
||||
@@ -705,14 +670,16 @@ fn test_7_1_condition_gt_shows_element() {
|
||||
fn test_7_1_condition_eq_operator() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "status_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "durum".to_string(),
|
||||
operator: "eq".to_string(),
|
||||
value: Some(serde_json::json!("aktif")),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "status_text".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "durum".to_string(),
|
||||
operator: "eq".to_string(),
|
||||
value: Some(serde_json::json!("aktif")),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: "Aktif".to_string(),
|
||||
}));
|
||||
@@ -732,14 +699,16 @@ fn test_7_1_condition_eq_operator() {
|
||||
fn test_7_1_condition_empty_not_empty() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::StaticText(StaticTextElement {
|
||||
id: "show_if_exists".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "note".to_string(),
|
||||
operator: "not_empty".to_string(),
|
||||
value: None,
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "show_if_exists".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "note".to_string(),
|
||||
operator: "not_empty".to_string(),
|
||||
value: None,
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: "Has note".to_string(),
|
||||
}));
|
||||
@@ -763,14 +732,16 @@ fn test_7_1_condition_empty_not_empty() {
|
||||
fn test_7_1_condition_on_container_hides_children() {
|
||||
let mut tpl = base_template();
|
||||
tpl.root.children.push(TemplateElement::Container(ContainerElement {
|
||||
id: "cond_container".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "show".to_string(),
|
||||
operator: "eq".to_string(),
|
||||
value: Some(serde_json::json!(true)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase {
|
||||
id: "cond_container".to_string(),
|
||||
condition: Some(Condition {
|
||||
path: "show".to_string(),
|
||||
operator: "eq".to_string(),
|
||||
value: Some(serde_json::json!(true)),
|
||||
}),
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
},
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -779,10 +750,7 @@ fn test_7_1_condition_on_container_hides_children() {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "child_text".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("child_text".to_string(), SizeConstraint::default()),
|
||||
style: TextStyle { font_size: Some(10.0), ..Default::default() },
|
||||
content: "Child".to_string(),
|
||||
})],
|
||||
@@ -854,10 +822,7 @@ fn test_7_5_effective_format_config_priority() {
|
||||
header: None,
|
||||
footer: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -889,10 +854,7 @@ fn test_7_5_effective_format_config_locale_fallback() {
|
||||
header: None,
|
||||
footer: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding::default(),
|
||||
@@ -915,14 +877,11 @@ fn test_7_5_locale_affects_table_currency_format() {
|
||||
let mut tpl = base_template();
|
||||
tpl.locale = Some("en-US".to_string());
|
||||
tpl.root.children.push(TemplateElement::RepeatingTable(RepeatingTableElement {
|
||||
id: "tbl_locale".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("tbl_locale".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
data_source: ArrayBinding { path: "items".to_string() },
|
||||
columns: vec![
|
||||
TableColumn {
|
||||
|
||||
@@ -20,10 +20,7 @@ fn simple_template() -> Template {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -37,14 +34,14 @@ fn simple_template() -> Template {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "title".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"title".to_string(),
|
||||
SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: Some(14.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -166,10 +163,7 @@ fn test_compute_layout_with_data_binding() {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
@@ -183,14 +177,14 @@ fn test_compute_layout_with_data_binding() {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::Text(TextElement {
|
||||
id: "bound_text".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"bound_text".to_string(),
|
||||
SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
..Default::default()
|
||||
@@ -235,10 +229,7 @@ fn test_compute_layout_multiple_children_ordering() {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -253,14 +244,14 @@ fn test_compute_layout_multiple_children_ordering() {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "first".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"first".to_string(),
|
||||
SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
..Default::default()
|
||||
@@ -268,14 +259,14 @@ fn test_compute_layout_multiple_children_ordering() {
|
||||
content: "First".to_string(),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "second".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
base: ElementBase::flow(
|
||||
"second".to_string(),
|
||||
SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
..Default::default()
|
||||
|
||||
@@ -23,10 +23,7 @@ fn simple_template() -> Template {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -40,14 +37,11 @@ fn simple_template() -> Template {
|
||||
style: ContainerStyle::default(),
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "title".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("title".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -94,10 +88,7 @@ fn test_render_pdf_with_multiple_elements() {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -112,14 +103,11 @@ fn test_render_pdf_with_multiple_elements() {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "header".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("header".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(16.0),
|
||||
font_weight: Some("bold".to_string()),
|
||||
@@ -128,28 +116,22 @@ fn test_render_pdf_with_multiple_elements() {
|
||||
content: "FATURA".to_string(),
|
||||
}),
|
||||
TemplateElement::Line(LineElement {
|
||||
id: "sep".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("sep".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: LineStyle {
|
||||
stroke_color: Some("#000000".to_string()),
|
||||
stroke_width: Some(0.5),
|
||||
},
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "body".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("body".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(11.0),
|
||||
..Default::default()
|
||||
@@ -192,10 +174,7 @@ fn test_render_pdf_with_container_styles() {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 0.0,
|
||||
padding: Padding {
|
||||
@@ -214,14 +193,11 @@ fn test_render_pdf_with_container_styles() {
|
||||
},
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![TemplateElement::StaticText(StaticTextElement {
|
||||
id: "text".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("text".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(12.0),
|
||||
color: Some("#ff0000".to_string()),
|
||||
@@ -257,10 +233,7 @@ fn test_page_break_produces_multiple_pages() {
|
||||
format_config: None,
|
||||
locale: None,
|
||||
root: ContainerElement {
|
||||
id: "root".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint::default(),
|
||||
base: ElementBase::flow("root".to_string(), SizeConstraint::default()),
|
||||
direction: "column".to_string(),
|
||||
gap: 5.0,
|
||||
padding: Padding {
|
||||
@@ -275,14 +248,11 @@ fn test_page_break_produces_multiple_pages() {
|
||||
break_inside: "auto".to_string(),
|
||||
children: vec![
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "t1".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("t1".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
..Default::default()
|
||||
@@ -290,18 +260,14 @@ fn test_page_break_produces_multiple_pages() {
|
||||
content: "Page 1 content".to_string(),
|
||||
}),
|
||||
TemplateElement::PageBreak(PageBreakElement {
|
||||
id: "pb1".to_string(),
|
||||
condition: None,
|
||||
base: ElementBase::flow("pb1".to_string(), SizeConstraint::default()),
|
||||
}),
|
||||
TemplateElement::StaticText(StaticTextElement {
|
||||
id: "t2".to_string(),
|
||||
condition: None,
|
||||
position: PositionMode::Flow,
|
||||
size: SizeConstraint {
|
||||
base: ElementBase::flow("t2".to_string(), SizeConstraint {
|
||||
width: SizeValue::Fr { value: 1.0 },
|
||||
height: SizeValue::Auto,
|
||||
..Default::default()
|
||||
},
|
||||
}),
|
||||
style: TextStyle {
|
||||
font_size: Some(18.0),
|
||||
..Default::default()
|
||||
|
||||
Reference in New Issue
Block a user