pub mod data_resolve; pub mod expr_eval; pub mod page_break; pub mod sizing; pub mod table_layout; pub mod text_measure; pub mod tree; #[cfg(target_arch = "wasm32")] pub mod wasm_api; pub mod barcode_gen; pub mod chart_layout; pub mod chart_render; pub mod font_meta; pub mod font_provider; #[cfg(not(target_arch = "wasm32"))] pub mod pdf_render; use dreport_core::models::{ChartType, Template}; use serde::{Deserialize, Serialize}; /// Layout hesaplama hata tipi #[derive(Debug)] pub enum LayoutError { Taffy(taffy::TaffyError), } impl std::fmt::Display for LayoutError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LayoutError::Taffy(e) => write!(f, "Taffy layout hatası: {:?}", e), } } } impl std::error::Error for LayoutError {} impl From for LayoutError { fn from(e: taffy::TaffyError) -> Self { LayoutError::Taffy(e) } } // --- Layout sonuç tipleri --- #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LayoutResult { pub pages: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PageLayout { pub page_index: usize, pub width_mm: f64, pub height_mm: f64, pub elements: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ElementLayout { pub id: String, pub x_mm: f64, pub y_mm: f64, pub width_mm: f64, pub height_mm: f64, pub element_type: String, pub content: Option, pub style: ResolvedStyle, pub children: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type")] pub enum ResolvedContent { #[serde(rename = "text")] Text { value: String }, #[serde(rename = "image")] Image { src: String }, #[serde(rename = "line")] Line, #[serde(rename = "barcode")] Barcode { format: String, value: String }, #[serde(rename = "page_number")] PageNumber { current: usize, total: usize }, #[serde(rename = "shape")] Shape { #[serde(rename = "shapeType")] shape_type: String, }, #[serde(rename = "checkbox")] Checkbox { checked: bool }, #[serde(rename = "rich_text")] RichText { spans: Vec }, #[serde(rename = "table")] Table { headers: Vec, rows: Vec>, column_widths_mm: Vec, }, #[serde(rename = "chart")] Chart { svg: String, /// PDF render icin chart verisi (frontend bunu kullanmaz) #[serde(flatten)] chart_data: Box, }, } /// PDF renderer icin chart verisi — ResolvedContent::Chart icinde tasınır #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ChartRenderData { pub chart_type: ChartType, pub categories: Vec, pub series: Vec, #[serde(default)] pub title_text: Option, #[serde(default)] pub title_font_size: Option, #[serde(default)] pub title_color: Option, #[serde(default)] pub colors: Vec, #[serde(default)] pub show_labels: bool, #[serde(default)] pub label_font_size: Option, #[serde(default)] pub show_grid: bool, #[serde(default)] pub grid_color: Option, #[serde(default)] pub bar_gap: Option, #[serde(default)] pub stacked: bool, #[serde(default)] pub inner_radius: Option, #[serde(default)] pub show_points: Option, #[serde(default)] pub line_width: Option, #[serde(default)] pub background_color: Option, // Label color #[serde(default)] pub label_color: Option, // Legend #[serde(default)] pub legend_show: bool, #[serde(default)] pub legend_position: Option, #[serde(default)] pub legend_font_size: Option, // Axis labels #[serde(default)] pub x_label: Option, #[serde(default)] pub y_label: Option, // Title align #[serde(default)] pub title_align: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChartSeriesData { pub name: String, pub values: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolvedRichSpan { pub text: String, pub font_size: Option, pub font_weight: Option, pub font_family: Option, pub color: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TableHeaderCell { pub text: String, pub align: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TableCell { pub text: String, pub align: String, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ResolvedStyle { // Text pub font_size: Option, pub font_weight: Option, pub font_style: Option, pub font_family: Option, pub color: Option, pub text_align: Option, // Line pub stroke_color: Option, pub stroke_width: Option, // Container pub background_color: Option, pub border_color: Option, pub border_width: Option, pub border_radius: Option, pub border_style: Option, // Table pub header_bg: Option, pub header_color: Option, pub zebra_odd: Option, pub zebra_even: Option, pub header_font_size: Option, // Image pub object_fit: Option, // Barcode pub barcode_color: Option, pub barcode_include_text: Option, } /// Ana layout hesaplama fonksiyonu. /// Template + data + font verileri alır, her element için pozisyon döner. pub fn compute_layout( template: &Template, data: &serde_json::Value, font_data: &[FontData], ) -> Result { let mut measurer = text_measure::TextMeasurer::new(font_data); let resolved = data_resolve::resolve_template(template, data); tree::compute(template, &resolved, &mut measurer) } /// Cache-aware layout hesaplama. /// Önceki çağrıdan kalan text measurement cache'ini alır, hesaplama sonrası /// güncellenen cache'i geri döner. WASM tarafında cross-call persist için kullanılır. pub fn compute_layout_cached( template: &Template, data: &serde_json::Value, font_data: &[FontData], text_cache: text_measure::TextMeasureCache, ) -> Result<(LayoutResult, text_measure::TextMeasureCache), LayoutError> { let mut measurer = text_measure::TextMeasurer::new_with_cache(font_data, text_cache); let resolved = data_resolve::resolve_template(template, data); let result = tree::compute(template, &resolved, &mut measurer)?; Ok((result, measurer.take_cache())) } /// Font verisi (ham TTF/OTF bytes + metadata) #[derive(Debug, Clone)] pub struct FontData { pub family: String, pub weight: u16, pub italic: bool, pub data: Vec, } impl FontData { /// Create FontData from raw bytes, parsing metadata from the font file. /// Returns None if font metadata cannot be parsed. pub fn from_bytes(data: Vec) -> Option { let meta = font_meta::parse_font_meta(&data)?; Some(Self { family: meta.family, weight: meta.weight, italic: meta.italic, data, }) } /// Create FontData with explicit metadata (when metadata is already known). pub fn new(family: String, weight: u16, italic: bool, data: Vec) -> Self { Self { family, weight, italic, data, } } pub fn is_bold(&self) -> bool { self.weight >= 700 } }