diff --git a/Cargo.toml b/Cargo.toml index a12a28e9..8232d888 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets" -version = "0.1.0" +version = "0.2.0" description = "A web spreadsheets application written in Rust and Typescript" keywords = ["excel", "spreadsheets", "ooxml", "logisheets"] readme = "README.md" @@ -21,5 +21,5 @@ members = [ ] [dependencies] -logisheets_controller = {path = "crates/controller", version = "0.1.0"} -logisheets_workbook = {path = "crates/workbook", version = "0.1.0"} +logisheets_controller = {path = "crates/controller", version = "0.2.0"} +logisheets_workbook = {path = "crates/workbook", version = "0.2.0"} diff --git a/crates/controller/Cargo.toml b/crates/controller/Cargo.toml index f8ac8b27..4581e999 100644 --- a/crates/controller/Cargo.toml +++ b/crates/controller/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets_controller" -version = "0.1.0" +version = "0.2.0" description = "the core of LogiSheets" authors = ["ImJeremyHe"] edition = "2018" @@ -21,7 +21,7 @@ statrs = "0.15.0" unicode-segmentation = "1.8.0" ts-rs = "6.1.2" -logisheets_base = {version = "0.1.0", path = "./base"} -logisheets_lexer = {version = "0.1.0", path = "./lexer"} -logisheets_parser = {version = "0.1.1", path = "./parser"} -logisheets_workbook = {version = "0.1.0", path = "../workbook"} +logisheets_base = {version = "0.2.0", path = "./base"} +logisheets_lexer = {version = "0.2.0", path = "./lexer"} +logisheets_parser = {version = "0.2.0", path = "./parser"} +logisheets_workbook = {version = "0.2.0", path = "../workbook"} diff --git a/crates/controller/base/Cargo.toml b/crates/controller/base/Cargo.toml index 8c2c6dd2..8d9f6385 100644 --- a/crates/controller/base/Cargo.toml +++ b/crates/controller/base/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets_base" -version = "0.1.0" +version = "0.2.0" description = "some basic definitions for LogiSheets" authors = ["ImJeremyHe"] license = "MIT" @@ -11,4 +11,4 @@ futures = "0.3.19" im = "15.0.0" serde = {version = "1.0.125", features = ["derive"]} ts-rs = "6.1.2" -logisheets_workbook = {version = "0.1.0", path = "../../workbook"} +logisheets_workbook = {version = "0.2.0", path = "../../workbook"} diff --git a/crates/controller/lexer/Cargo.toml b/crates/controller/lexer/Cargo.toml index 9f5363dd..34295e7d 100644 --- a/crates/controller/lexer/Cargo.toml +++ b/crates/controller/lexer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets_lexer" -version = "0.1.0" +version = "0.2.0" edition = "2018" description = "the lexer for excel formula" authors = ["ImJeremyHe"] diff --git a/crates/controller/parser/Cargo.toml b/crates/controller/parser/Cargo.toml index a8447be0..d1567071 100644 --- a/crates/controller/parser/Cargo.toml +++ b/crates/controller/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets_parser" -version = "0.1.1" +version = "0.2.0" edition = "2018" description = "the parser of excel formula" authors = ["ImJeremyHe"] @@ -12,5 +12,5 @@ pest = "2.1.3" pest_derive = "2.1.0" chrono = "0.4.19" regex = "1" -logisheets_lexer = {version = "0.1.0", path = "../lexer"} -logisheets_base = {version = "0.1.0", path = "../base"} +logisheets_lexer = {version = "0.2.0", path = "../lexer"} +logisheets_base = {version = "0.2.0", path = "../base"} diff --git a/crates/controller/src/controller/display.rs b/crates/controller/src/controller/display.rs index 0ed9558b..3c4564d4 100644 --- a/crates/controller/src/controller/display.rs +++ b/crates/controller/src/controller/display.rs @@ -1,5 +1,5 @@ +use super::style::Style; use logisheets_base::BlockId; -use logisheets_workbook::prelude::*; use serde::Serialize; #[derive(Debug, Clone, Serialize, TS)] @@ -137,18 +137,6 @@ pub fn get_default_col_width() -> f64 { 8.38 } -#[derive(Debug, Clone, Serialize, TS)] -#[ts(export_to = "../../src/bindings/style.ts")] -#[serde(rename_all = "camelCase")] -pub struct Style { - pub font: CtFont, - pub fill: CtFill, - pub border: CtBorder, - pub alignment: Option, - pub protection: Option, - pub formatter: String, -} - #[derive(Debug, Clone, Serialize, TS)] #[ts(export, export_to = "../../src/bindings/value.ts")] #[serde(rename_all = "camelCase")] diff --git a/crates/controller/src/controller/edit_action/style_payload.rs b/crates/controller/src/controller/edit_action/style_payload.rs index 5cd13fe1..40e10f4f 100644 --- a/crates/controller/src/controller/edit_action/style_payload.rs +++ b/crates/controller/src/controller/edit_action/style_payload.rs @@ -1,3 +1,4 @@ +use super::super::style::PatternFill; use logisheets_workbook::prelude::*; use serde::Serialize; @@ -37,5 +38,5 @@ pub struct StyleUpdateType { pub set_bottom_border_style: Option, pub set_border_giagonal_up: Option, pub set_border_giagonal_down: Option, - pub set_pattern_fill: Option, + pub set_pattern_fill: Option, } diff --git a/crates/controller/src/controller/mod.rs b/crates/controller/src/controller/mod.rs index edb0f9ae..167a0deb 100644 --- a/crates/controller/src/controller/mod.rs +++ b/crates/controller/src/controller/mod.rs @@ -5,6 +5,7 @@ use logisheets_workbook::prelude::{read, SerdeErr}; pub mod display; pub mod edit_action; pub mod status; +pub mod style; mod transaction; mod viewer; use crate::file_loader2::load; diff --git a/crates/controller/src/controller/style.rs b/crates/controller/src/controller/style.rs new file mode 100644 index 00000000..22b87b74 --- /dev/null +++ b/crates/controller/src/controller/style.rs @@ -0,0 +1,296 @@ +use crate::style_manager::RawStyle; +use crate::theme_manager::ThemeManager; +use logisheets_workbook::prelude::{ + CtBorder, CtBorderPr, CtCellAlignment, CtCellProtection, CtColor, CtFill, CtFont, CtFontFamily, + CtFontName, CtFontScheme, CtUnderlineProperty, CtVerticalAlignFontProperty, StBorderStyle, + StGradientType, StPatternType, +}; +use serde::Serialize; + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export, export_to = "../../src/bindings/style.ts")] +#[serde(rename_all = "camelCase")] +pub struct Style { + pub font: Font, + pub fill: Fill, + pub border: Border, + pub alignment: Option, + pub protection: Option, + pub formatter: String, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export_to = "../../src/bindings/font.ts")] +#[serde(rename_all = "camelCase")] +pub struct Font { + pub bold: bool, + pub italic: bool, + pub underline: Option, + pub color: Color, + pub sz: Option, + pub name: Option, + pub charset: Option, + pub family: Option, + pub strike: bool, + pub outline: bool, + pub shadow: bool, + pub condense: bool, + pub extend: bool, + pub vert_align: Option, + pub scheme: Option, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export, export_to = "../../src/bindings/fill.ts")] +#[serde(rename_all = "camelCase")] +pub enum Fill { + PatternFill(PatternFill), + GradientFill(GradientFill), +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export_to = "../../src/bindings/pattern_fill.ts")] +#[serde(rename_all = "camelCase")] +pub struct PatternFill { + pub fg_color: Option, + pub bg_color: Option, + pub pattern_type: Option, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export_to = "../../src/bindings/gradient_fill.ts")] +#[serde(rename_all = "camelCase")] +pub struct GradientFill { + pub stops: Vec, + pub ty: StGradientType, + pub degree: f64, + pub left: f64, + pub right: f64, + pub top: f64, + pub bottom: f64, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export, export_to = "../../src/bindings/gradient_stop.ts")] +#[serde(rename_all = "camelCase")] +pub struct GradientStop { + pub color: Color, + pub position: f64, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export_to = "../../src/bindings/border_pr.ts")] +#[serde(rename_all = "camelCase")] +pub struct BorderPr { + pub color: Option, + pub style: StBorderStyle, +} + +#[derive(Debug, Clone, Serialize, TS)] +#[ts(export, export_to = "../../src/bindings/border.ts")] +#[serde(rename_all = "camelCase")] +pub struct Border { + pub left: Option, + pub right: Option, + pub top: Option, + pub bottom: Option, + pub diagonal: Option, + pub vertical: Option, + pub horizontal: Option, + pub diagonal_up: Option, + pub diagonal_down: Option, + pub outline: bool, +} + +#[derive(Debug, Clone, Serialize, TS, Default)] +#[ts(export, export_to = "../../src/bindings/color.ts")] +#[serde(rename_all = "camelCase")] +pub struct Color { + pub rgb: String, + pub tint: f64, +} + +pub struct StyleConverter<'a> { + pub theme_manager: &'a ThemeManager, +} + +impl<'a> StyleConverter<'a> { + pub fn convert_style(&self, raw_style: RawStyle) -> Style { + Style { + font: self.convert_font(raw_style.font), + fill: self.convert_fill(raw_style.fill), + border: self.convert_border(raw_style.border), + alignment: raw_style.alignment, + protection: raw_style.protection, + formatter: raw_style.formatter, + } + } + + fn convert_font(&self, font: CtFont) -> Font { + Font { + bold: font.bold, + italic: font.italic, + underline: font.underline.clone(), + color: font + .color + .map_or(Color::default(), |c| self.convert_color(c)), + sz: font.sz.as_ref().map_or(None, |s| Some(s.val)), + name: font.name.clone(), + charset: font.charset.as_ref().map_or(None, |s| Some(s.val)), + family: font.family.clone(), + strike: font.strike, + outline: font.outline, + shadow: font.shadow, + condense: font.condense, + extend: font.extend, + vert_align: font.vert_align.clone(), + scheme: font.scheme.clone(), + } + } + + fn convert_fill(&self, fill: CtFill) -> Fill { + match fill { + CtFill::PatternFill(pf) => Fill::PatternFill(PatternFill { + fg_color: pf.fg_color.map_or(None, |c| Some(self.convert_color(c))), + bg_color: pf.bg_color.map_or(None, |c| Some(self.convert_color(c))), + pattern_type: pf.pattern_type.clone(), + }), + CtFill::GradientFill(gf) => Fill::GradientFill(GradientFill { + stops: gf + .stops + .into_iter() + .map(|stop| GradientStop { + color: self.convert_color(stop.color), + position: stop.position, + }) + .collect(), + ty: gf.ty.clone(), + degree: gf.degree, + left: gf.left, + right: gf.right, + top: gf.top, + bottom: gf.bottom, + }), + } + } + + fn convert_border(&self, border: CtBorder) -> Border { + Border { + left: border + .left + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + right: border + .right + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + top: border + .top + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + bottom: border + .bottom + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + diagonal: border + .diagonal + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + vertical: border + .vertical + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + horizontal: border + .horizontal + .map_or(None, |pr| Some(self.convert_border_pr(pr))), + diagonal_up: border.diagonal_up, + diagonal_down: border.diagonal_down, + outline: border.outline, + } + } + + fn convert_border_pr(&self, border_pr: CtBorderPr) -> BorderPr { + BorderPr { + color: border_pr + .color + .map_or(None, |c| Some(self.convert_color(c))), + style: border_pr.style.clone(), + } + } + + fn convert_color(&self, color: CtColor) -> Color { + let tint = color.tint; + let rgb = { + if let Some(rgb) = &color.rgb { + rgb.clone() + } else if let Some(indexed) = color.indexed { + match indexed { + 0 => String::from("00000000"), + 1 => String::from("00FFFFFF"), + 2 => String::from("00FF0000"), + 3 => String::from("0000FF00"), + 4 => String::from("000000FF"), + 5 => String::from("00FFFF00"), + 6 => String::from("00FF00FF"), + 7 => String::from("0000FFFF"), + 8 => String::from("00000000"), + 9 => String::from("00FFFFFF"), + 10 => String::from("00FF0000"), + 11 => String::from("0000FF00"), + 12 => String::from("000000FF"), + 13 => String::from("00FFFF00"), + 14 => String::from("00FF00FF"), + 15 => String::from("0000FFFF"), + 16 => String::from("00800000"), + 17 => String::from("00008000"), + 18 => String::from("00000080"), + 19 => String::from("00808000"), + 20 => String::from("00800080"), + 21 => String::from("00008080"), + 22 => String::from("00C0C0C0"), + 23 => String::from("00808080"), + 24 => String::from("009999FF"), + 25 => String::from("00993366"), + 26 => String::from("00FFFFCC"), + 27 => String::from("00CCFFFF"), + 28 => String::from("00660066"), + 29 => String::from("00FF8080"), + 30 => String::from("000066CC"), + 31 => String::from("00CCCCFF"), + 32 => String::from("00000080"), + 33 => String::from("00FF00FF"), + 34 => String::from("00FFFF00"), + 35 => String::from("0000FFFF"), + 36 => String::from("00800080"), + 37 => String::from("00800000"), + 38 => String::from("00008080"), + 39 => String::from("000000FF"), + 40 => String::from("0000CCFF"), + 41 => String::from("00CCFFFF"), + 42 => String::from("00CCFFCC"), + 43 => String::from("00FFFF99"), + 44 => String::from("0099CCFF"), + 45 => String::from("00FF99CC"), + 46 => String::from("00CC99FF"), + 47 => String::from("00FFCC99"), + 48 => String::from("003366FF"), + 49 => String::from("0033CCCC"), + 50 => String::from("0099CC00"), + 51 => String::from("00FFCC00"), + 52 => String::from("00FF9900"), + 53 => String::from("00FF6600"), + 54 => String::from("00666699"), + 55 => String::from("00969696"), + 56 => String::from("00003366"), + 57 => String::from("00339966"), + 58 => String::from("00003300"), + 59 => String::from("00333300"), + 60 => String::from("00993300"), + 61 => String::from("00993366"), + 62 => String::from("00333399"), + 63 => String::from("00333333"), + _ => String::from(""), + } + } else if let Some(theme) = color.theme { + self.theme_manager.get_color(theme) + } else { + String::from("") + } + }; + Color { rgb, tint } + } +} diff --git a/crates/controller/src/controller/viewer.rs b/crates/controller/src/controller/viewer.rs index 44e3f5a6..618b6631 100644 --- a/crates/controller/src/controller/viewer.rs +++ b/crates/controller/src/controller/viewer.rs @@ -11,6 +11,7 @@ use super::display::{ BlockInfo, CellFormulaValue, CellStyle, ColInfo, Comment, DisplayPatch, DisplayResponse, MergeCell, RowInfo, SheetBlocks, }; +use super::style::StyleConverter; use super::Controller; #[derive(Debug, Default)] @@ -79,14 +80,21 @@ impl SheetViewer { self.row_infos.push(info) } }); + let style_converter = StyleConverter { + theme_manager: &controller.settings.theme, + }; sheet_data.cells.iter().for_each(|(cell_id, cell)| { let coord = navigator.fetch_cell_idx(sheet_id, cell_id); if coord.is_none() { panic!() } let (row, col) = coord.unwrap(); - let style = style_manager.get_cell_style(cell.style); - self.styles.push(CellStyle { row, col, style }); + let raw_style = style_manager.get_cell_style(cell.style); + self.styles.push(CellStyle { + row, + col, + style: style_converter.convert_style(raw_style), + }); let mut name_fetcher = NameFetcher { func_manager, sheet_id_manager, diff --git a/crates/controller/src/file_loader2/mod.rs b/crates/controller/src/file_loader2/mod.rs index c909254f..1faa03c4 100644 --- a/crates/controller/src/file_loader2/mod.rs +++ b/crates/controller/src/file_loader2/mod.rs @@ -19,6 +19,7 @@ use crate::{ }, id_manager::SheetIdManager, settings::Settings, + theme_manager::ThemeManager, }; pub struct SheetIdFetcher<'a> { pub sheet_id_manager: &'a mut SheetIdManager, @@ -128,5 +129,8 @@ pub fn load(wb: Workbook, book_name: String) -> Controller { style_manager, cell_attachment_manager, }; + if let Some(theme) = wb.theme { + settings.theme = ThemeManager::from(theme); + } Controller::from(status, book_name, settings) } diff --git a/crates/controller/src/lib.rs b/crates/controller/src/lib.rs index 8938e0f6..f869a1ff 100644 --- a/crates/controller/src/lib.rs +++ b/crates/controller/src/lib.rs @@ -23,12 +23,15 @@ mod navigator; mod payloads; mod settings; mod style_manager; +mod theme_manager; mod vertex_manager; mod workbook; use connectors::NameFetcher; +use controller::style::StyleConverter; pub use controller::{ - display::{Comment, MergeCell, Style, Value}, + display::{Comment, MergeCell, Value}, + style::{Border, BorderPr, Fill, Font, Style}, Controller, }; use logisheets_parser::unparse; @@ -213,12 +216,15 @@ impl<'a> Worksheet<'a> { } } }; - let style = self + let raw_style = self .controller .status .style_manager .get_cell_style(style_id); - Ok(style) + let style_converter = StyleConverter { + theme_manager: &self.controller.settings.theme, + }; + Ok(style_converter.convert_style(raw_style)) } else { Err(Err::NotFound) } diff --git a/crates/controller/src/settings/mod.rs b/crates/controller/src/settings/mod.rs index b5f3d951..ee223177 100644 --- a/crates/controller/src/settings/mod.rs +++ b/crates/controller/src/settings/mod.rs @@ -3,10 +3,13 @@ use std::collections::{HashMap, HashSet}; use logisheets_base::SheetId; use logisheets_workbook::prelude::CtSheetFormatPr; +use crate::theme_manager::ThemeManager; + pub struct Settings { pub sheet_format_pr: HashMap, pub calc_config: CalcConfig, pub async_funcs: HashSet, // function names in upper case. + pub theme: ThemeManager, } impl Default for Settings { @@ -18,6 +21,7 @@ impl Default for Settings { sheet_format_pr, calc_config, async_funcs: afuncs.into_iter().collect(), + theme: ThemeManager::default(), } } } diff --git a/crates/controller/src/style_manager/mod.rs b/crates/controller/src/style_manager/mod.rs index 7a6ce9da..0985b88f 100644 --- a/crates/controller/src/style_manager/mod.rs +++ b/crates/controller/src/style_manager/mod.rs @@ -18,9 +18,18 @@ use num_fmt_manager::NumFmtManager; use xf_manager::XfManager; use crate::payloads::sheet_process::style::CellStylePayload; +use logisheets_workbook::prelude::{CtBorder, CtCellAlignment, CtCellProtection, CtFill, CtFont}; use self::execute::execute_style_payload; -use crate::controller::display::Style; + +pub struct RawStyle { + pub font: CtFont, + pub fill: CtFill, + pub border: CtBorder, + pub alignment: Option, + pub protection: Option, + pub formatter: String, +} #[derive(Debug, Clone, Default)] pub struct StyleManager { @@ -41,7 +50,7 @@ impl StyleManager { execute_style_payload(self, payload, idx) } - pub fn get_cell_style(&self, id: StyleId) -> Style { + pub fn get_cell_style(&self, id: StyleId) -> RawStyle { let xf = self .cell_xfs_manager .get_data(id) @@ -68,7 +77,7 @@ impl StyleManager { .unwrap_or(self.num_fmt_manager.get_data(0).unwrap()); let alignment = xf.alignment.clone(); let protection = xf.protection.clone(); - Style { + RawStyle { font: font.clone(), fill: fill.clone(), border: border.clone(), diff --git a/crates/controller/src/theme_manager/mod.rs b/crates/controller/src/theme_manager/mod.rs new file mode 100644 index 00000000..9d18f7f9 --- /dev/null +++ b/crates/controller/src/theme_manager/mod.rs @@ -0,0 +1,38 @@ +use logisheets_workbook::prelude::theme::{CtColorScheme, ThemePart}; + +#[derive(Default)] +pub struct ThemeManager { + theme: Option, +} + +impl ThemeManager { + pub fn from(part: ThemePart) -> Self { + ThemeManager { theme: Some(part) } + } + + pub fn get_color(&self, idx: u32) -> String { + if self.theme.is_none() { + return String::from(""); + } + match idx { + 0 => self.get_color_scheme().dk1.get_color(), + 1 => self.get_color_scheme().lt1.get_color(), + 2 => self.get_color_scheme().dk2.get_color(), + 3 => self.get_color_scheme().lt2.get_color(), + 4 => self.get_color_scheme().accent1.get_color(), + 5 => self.get_color_scheme().accent2.get_color(), + 6 => self.get_color_scheme().accent3.get_color(), + 7 => self.get_color_scheme().accent4.get_color(), + 8 => self.get_color_scheme().accent5.get_color(), + 9 => self.get_color_scheme().accent6.get_color(), + 10 => self.get_color_scheme().hlink.get_color(), + 11 => self.get_color_scheme().fol_hlink.get_color(), + _ => String::from(""), + } + } + + fn get_color_scheme(&self) -> &CtColorScheme { + let clr_scheme = &self.theme.as_ref().unwrap().theme_elements.clr_scheme; + clr_scheme + } +} diff --git a/crates/workbook/Cargo.toml b/crates/workbook/Cargo.toml index f76c2c93..ec3fc77f 100644 --- a/crates/workbook/Cargo.toml +++ b/crates/workbook/Cargo.toml @@ -2,7 +2,7 @@ authors = ["ImJeremyHe"] edition = "2018" name = "logisheets_workbook" -version = "0.1.0" +version = "0.2.0" license = "MIT" description = "read and write .xlsx files" @@ -17,6 +17,6 @@ serde_json = "1.0.59" serde_repr = "0.1" thiserror = "1.0.24" zip = {version = "0.6.0", default-features = false, features = ["deflate"]} -logisheets_xmlserde = {path = "./xmlserde", version = "0.1.0"} -logisheets_derives = {path = "./derives", version = "0.1.0"} +logisheets_xmlserde = {path = "./xmlserde", version = "0.2.0"} +logisheets_derives = {path = "./derives", version = "0.2.0"} diff --git a/crates/workbook/derives/Cargo.toml b/crates/workbook/derives/Cargo.toml index 5b20addd..7cf15892 100644 --- a/crates/workbook/derives/Cargo.toml +++ b/crates/workbook/derives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "logisheets_derives" -version = "0.1.0" +version = "0.2.0" description = "macros that help LogiSheets serde the xml files" authors = ["ImJeremyHe"] license = "MIT" diff --git a/crates/workbook/derives/src/container.rs b/crates/workbook/derives/src/container.rs index f62b9221..5fce6182 100644 --- a/crates/workbook/derives/src/container.rs +++ b/crates/workbook/derives/src/container.rs @@ -8,15 +8,21 @@ use syn::Meta::NameValue; use syn::Meta::Path; use syn::NestedMeta::Lit; use syn::NestedMeta::Meta; +use syn::Variant; pub struct Container<'a> { - pub fields: Vec>, + pub struct_fields: Vec>, // Struct fields + pub enum_variants: Vec>, pub original: &'a syn::DeriveInput, pub with_ns: Option, pub custom_ns: Vec<(syn::LitByteStr, syn::LitByteStr)>, } impl<'a> Container<'a> { + pub fn is_enum(&self) -> bool { + self.enum_variants.len() > 0 + } + pub fn from_ast(item: &'a syn::DeriveInput, _derive: Derive) -> Container<'a> { let mut with_ns = Option::::None; let mut custom_ns = Vec::<(syn::LitByteStr, syn::LitByteStr)>::new(); @@ -58,32 +64,46 @@ impl<'a> Container<'a> { let fields = ds .fields .iter() - .map(|f| Field::from_ast(f)) + .map(|f| StructField::from_ast(f)) .filter(|f| f.is_some()) .map(|f| f.unwrap()) .collect::>(); Container { - fields, + struct_fields: fields, + enum_variants: vec![], + original: item, + with_ns, + custom_ns, + } + } + syn::Data::Enum(e) => { + let variants = e + .variants + .iter() + .map(|v| EnumVariant::from_ast(v)) + .collect::>(); + Container { + struct_fields: vec![], + enum_variants: variants, original: item, with_ns, custom_ns, } } - syn::Data::Enum(_) => panic!("Only support struct type, enum is found"), syn::Data::Union(_) => panic!("Only support struct type, union is found"), } } } pub struct FieldsSummary<'a> { - pub children: Vec>, - pub text: Option>, - pub attrs: Vec>, - pub self_closed_children: Vec>, + pub children: Vec>, + pub text: Option>, + pub attrs: Vec>, + pub self_closed_children: Vec>, } impl<'a> FieldsSummary<'a> { - pub fn from_fields(fields: Vec>) -> Self { + pub fn from_fields(fields: Vec>) -> Self { let mut result = FieldsSummary { children: vec![], text: None, @@ -100,7 +120,7 @@ impl<'a> FieldsSummary<'a> { } } -pub struct Field<'a> { +pub struct StructField<'a> { pub ty: EleType, pub name: Option, pub skip_serializing: bool, @@ -110,7 +130,7 @@ pub struct Field<'a> { pub generic: Generic<'a>, } -impl<'a> Field<'a> { +impl<'a> StructField<'a> { pub fn from_ast(f: &'a syn::Field) -> Option { let mut name = Option::::None; let mut skip_serializing = false; @@ -166,7 +186,7 @@ impl<'a> Field<'a> { if ty.is_none() { None } else { - Some(Field { + Some(StructField { ty: ty.unwrap(), name, skip_serializing, @@ -185,6 +205,40 @@ impl<'a> Field<'a> { } } +pub struct EnumVariant<'a> { + pub name: syn::LitByteStr, + pub ident: &'a syn::Ident, + pub ty: &'a syn::Type, +} + +impl<'a> EnumVariant<'a> { + pub fn from_ast(v: &'a Variant) -> Self { + let mut name = Option::::None; + for meta_item in v + .attrs + .iter() + .flat_map(|attr| get_xmlserde_meta_items(attr)) + .flatten() + { + match meta_item { + Meta(NameValue(m)) if m.path == NAME => { + if let Ok(s) = get_lit_byte_str(&m.lit) { + name = Some(s.clone()); + } + } + _ => panic!("Invalid attr"), + } + } + let ty = &v.fields.iter().next().unwrap().ty; + let ident = &v.ident; + EnumVariant { + name: name.unwrap(), + ty, + ident, + } + } +} + /// Specify where this field is in the xml. pub enum EleType { Attr, diff --git a/crates/workbook/derives/src/de.rs b/crates/workbook/derives/src/de.rs index e033de2b..17226a96 100644 --- a/crates/workbook/derives/src/de.rs +++ b/crates/workbook/derives/src/de.rs @@ -1,11 +1,89 @@ use syn::DeriveInput; -use crate::container::{self, Container, EleType, Field, FieldsSummary, Generic}; +use crate::container::{self, Container, EleType, FieldsSummary, Generic, StructField}; pub fn get_de_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { let container = Container::from_ast(&input, container::Derive::Deserialize); - let result = get_result(&container.fields); - let summary = FieldsSummary::from_fields(container.fields); + if container.is_enum() { + get_de_enum_impl_block(container) + } else { + get_de_struct_impl_block(container) + } +} + +pub fn get_de_enum_impl_block(container: Container) -> proc_macro2::TokenStream { + let ident = &container.original.ident; + let event_start_branches = container.enum_variants.iter().map(|v| { + let name = &v.name; + let ty = v.ty; + let ident = v.ident; + quote! { + #name => { + let _r = #ty::deserialize( + #name, + reader, + _s.attributes(), + false, + ); + result = Some(Self::#ident(_r)); + } + } + }); + let event_empty_branches = container.enum_variants.iter().map(|v| { + let name = &v.name; + let ty = v.ty; + let ident = v.ident; + quote! { + #name => { + let _r = #ty::deserialize( + #name, + reader, + _s.attributes(), + true, + ); + result = Some(Self::#ident(_r)); + } + } + }); + quote! { + #[allow(unused_assignments)] + impl crate::XmlDeserialize for #ident { + fn deserialize( + tag: &[u8], + reader: &mut quick_xml::Reader, + attrs: quick_xml::events::attributes::Attributes, + is_empty: bool, + ) -> Self { + use quick_xml::events::*; + let mut buf = Vec::::new(); + let mut result = Option::::None; + loop { + match reader.read_event(&mut buf) { + Ok(Event::End(e)) if e.name() == tag => { + break + }, + Ok(Event::Start(_s)) => match _s.name() { + #(#event_start_branches)* + _ => {}, + }, + Ok(Event::Empty(_s)) => match _s.name() { + #(#event_empty_branches)* + _ => {}, + } + Ok(Event::Eof) => break, + Err(_) => break, + _ => {}, + } + } + result.unwrap() + } + } + } +} + +pub fn get_de_struct_impl_block(container: Container) -> proc_macro2::TokenStream { + let result = get_result(&container.struct_fields); + let summary = FieldsSummary::from_fields(container.struct_fields); let fields_init = get_fields_init(&summary); let FieldsSummary { children, @@ -17,7 +95,7 @@ pub fn get_de_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { let attr_branches = attrs.into_iter().map(|a| attr_match_branch(a)); let child_branches = children_match_branch(children); let sfc_branch = sfc_match_branch(self_closed_children); - let ident = &input.ident; + let ident = &container.original.ident; let text_branch = { if let Some(t) = text { Some(text_match_branch(t)) @@ -69,7 +147,7 @@ pub fn get_de_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { } } -fn get_result(fields: &[Field]) -> proc_macro2::TokenStream { +fn get_result(fields: &[StructField]) -> proc_macro2::TokenStream { let branch = fields.iter().map(|f| { let ident = f.original.ident.as_ref().unwrap(); if f.is_required() { @@ -158,7 +236,7 @@ fn get_fields_init(fields: &FieldsSummary) -> proc_macro2::TokenStream { } } -fn get_vec_init(children: &[Field]) -> proc_macro2::TokenStream { +fn get_vec_init(children: &[StructField]) -> proc_macro2::TokenStream { let vec_inits = children .iter() .filter(|c| c.generic.is_vec()) @@ -190,7 +268,7 @@ fn get_vec_init(children: &[Field]) -> proc_macro2::TokenStream { } } -fn sfc_match_branch(fields: Vec) -> proc_macro2::TokenStream { +fn sfc_match_branch(fields: Vec) -> proc_macro2::TokenStream { if fields.len() == 0 { return quote! {}; } @@ -220,7 +298,7 @@ fn sfc_match_branch(fields: Vec) -> proc_macro2::TokenStream { // } } -fn attr_match_branch(field: Field) -> proc_macro2::TokenStream { +fn attr_match_branch(field: StructField) -> proc_macro2::TokenStream { if !matches!(field.ty, EleType::Attr) { panic!("") } @@ -270,7 +348,7 @@ fn attr_match_branch(field: Field) -> proc_macro2::TokenStream { } } -fn text_match_branch(field: Field) -> proc_macro2::TokenStream { +fn text_match_branch(field: StructField) -> proc_macro2::TokenStream { if !matches!(field.ty, EleType::Text) { panic!("") } @@ -303,7 +381,7 @@ fn text_match_branch(field: Field) -> proc_macro2::TokenStream { } } -fn children_match_branch(fields: Vec) -> proc_macro2::TokenStream { +fn children_match_branch(fields: Vec) -> proc_macro2::TokenStream { if fields.len() == 0 { return quote! {}; } diff --git a/crates/workbook/derives/src/ser.rs b/crates/workbook/derives/src/ser.rs index 84e7903a..272f9faa 100644 --- a/crates/workbook/derives/src/ser.rs +++ b/crates/workbook/derives/src/ser.rs @@ -1,9 +1,45 @@ use syn::DeriveInput; -use crate::container::{Container, Derive, Field, FieldsSummary, Generic}; +use crate::container::{Container, Derive, FieldsSummary, Generic, StructField}; pub fn get_ser_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { let container = Container::from_ast(&input, Derive::Serialize); + if container.is_enum() { + get_ser_enum_impl_block(container) + } else { + get_ser_struct_impl_block(container) + } +} + +fn get_ser_enum_impl_block(container: Container) -> proc_macro2::TokenStream { + let ident = &container.original.ident; + let branches = container.enum_variants.iter().map(|v| { + let f = v.ident; + let name = &v.name; + quote! { + Self::#f(c) => c.serialize(#name, writer), + } + }); + quote! { + #[allow(unused_must_use)] + impl crate::XmlSerialize for #ident { + fn serialize( + &self, + tag: &[u8], + writer: &mut quick_xml::Writer, + ) { + use quick_xml::events::*; + let _ = writer.write_event(Event::Start(BytesStart::borrowed_name(tag))); + match self { + #(#branches)* + } + let _ = writer.write_event(Event::End(BytesEnd::borrowed(tag))); + } + } + } +} + +fn get_ser_struct_impl_block(container: Container) -> proc_macro2::TokenStream { let write_ns = match container.with_ns { Some(ns) => quote! { attrs.push(Attribute::from((b"xmlns".as_ref(), #ns.as_ref()))); @@ -27,7 +63,7 @@ pub fn get_ser_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { text, attrs, self_closed_children, - } = FieldsSummary::from_fields(container.fields); + } = FieldsSummary::from_fields(container.struct_fields); if text.is_some() && (children.len() > 0 || self_closed_children.len() > 0) { panic!("Cannot owns the text and children at the same time.") } @@ -111,19 +147,7 @@ pub fn get_ser_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { #(#write_children)* } }; - let ident = &input.ident; - // let write_event = if is_empty { - // quote! { - // writer.write_event(Event::Empty(start)); - // } - // } else { - // quote! { - // writer.write_event(Event::Start(start)); - // #write_text_or_children - // let end = BytesEnd::borrowed(tag); - // writer.write_event(Event::End(end)); - // } - // }; + let ident = &container.original.ident; let write_event = quote! { if is_empty { writer.write_event(Event::Empty(start)); @@ -159,9 +183,9 @@ pub fn get_ser_impl_block(input: DeriveInput) -> proc_macro2::TokenStream { } fn init_is_empty( - children: &Vec, - scf: &Vec, - text: &Option, + children: &Vec, + scf: &Vec, + text: &Option, ) -> proc_macro2::TokenStream { let children_init = children.iter().map(|c| { let ident = c.original.ident.as_ref().unwrap(); diff --git a/crates/workbook/examples/theme1.xml b/crates/workbook/examples/theme1.xml new file mode 100644 index 00000000..c4d09524 --- /dev/null +++ b/crates/workbook/examples/theme1.xml @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crates/workbook/src/reader.rs b/crates/workbook/src/reader.rs index e8c8c963..0a03bd5a 100644 --- a/crates/workbook/src/reader.rs +++ b/crates/workbook/src/reader.rs @@ -1,4 +1,5 @@ use super::rtypes::*; +use logisheets_xmlserde::theme::ThemePart; use logisheets_xmlserde::{ comments::Comments, sst::SstPart, style_sheet::StylesheetPart, workbook::WorkbookPart, worksheet::WorksheetPart, @@ -90,6 +91,7 @@ fn de_workbook( let mut sst = Option::::None; let mut worksheets = HashMap::::new(); let mut external_links = HashMap::::new(); + let mut theme = Option::::None; let path_buf = get_rels(path)?; let rels = path_buf.to_str(); if rels.is_none() { @@ -159,6 +161,20 @@ fn de_workbook( } } } + THEME => { + let target = &r.target; + let path = get_target_abs_path(rels, target); + if let Some(s) = path.to_str() { + match de_theme(s, archive) { + Ok(w) => { + theme = Some(w); + } + Err(e) => { + println!("parsing file: {:?} but meet error:{:?}", s, e) + } + } + } + } _ => {} }); Ok(Workbook { @@ -167,6 +183,7 @@ fn de_workbook( sst, worksheets, external_links, + theme, }) } @@ -235,6 +252,7 @@ define_de_func!(de_worksheet_part, WorksheetPart, b"worksheet"); define_de_func!(de_comments, Comments, b"comments"); define_de_func!(de_sst, SstPart, b"sst"); define_de_func!(de_style_part, StylesheetPart, b"styleSheet"); +define_de_func!(de_theme, ThemePart, b"a:theme"); /// Given a path `/foo/test.xml`, find its relationships `/foo/_rels/test.xml.rels` fn get_rels(path: &str) -> Result { diff --git a/crates/workbook/src/rtypes.rs b/crates/workbook/src/rtypes.rs index f98181a9..b790906d 100644 --- a/crates/workbook/src/rtypes.rs +++ b/crates/workbook/src/rtypes.rs @@ -13,6 +13,8 @@ pub const STYLE: RType = RType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"); pub const COMMENTS: RType = RType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"); +pub const THEME: RType = + RType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme"); impl<'a> PartialEq for RType<'a> { fn eq(&self, other: &str) -> bool { diff --git a/crates/workbook/src/workbook.rs b/crates/workbook/src/workbook.rs index db103ac5..22f426a9 100644 --- a/crates/workbook/src/workbook.rs +++ b/crates/workbook/src/workbook.rs @@ -2,6 +2,7 @@ use logisheets_xmlserde::comments::Comments; use logisheets_xmlserde::external_links::*; use logisheets_xmlserde::sst::SstPart; use logisheets_xmlserde::style_sheet::StylesheetPart; +use logisheets_xmlserde::theme::ThemePart; use logisheets_xmlserde::workbook::WorkbookPart; use logisheets_xmlserde::worksheet::WorksheetPart; use std::collections::HashMap; @@ -15,6 +16,7 @@ pub struct Workbook { pub sst: Option, pub worksheets: HashMap, pub external_links: HashMap, + pub theme: Option, } #[derive(Debug)] diff --git a/crates/workbook/xmlserde/Cargo.toml b/crates/workbook/xmlserde/Cargo.toml index c891f4e7..011855a0 100644 --- a/crates/workbook/xmlserde/Cargo.toml +++ b/crates/workbook/xmlserde/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "logisheets_xmlserde" edition = "2018" -version = "0.1.0" +version = "0.2.0" description = "read and write the ooxml files" authors = ["ImJeremyHe"] license = "MIT" @@ -11,7 +11,7 @@ paste = "1.0.5" quick-xml = {version = "0.22.0", features = ["serialize"]} regex = "1" serde = {version = "1.0.125", features = ["derive"]} -logisheets_derives = {path = "../derives", version = "0.1.0"} +logisheets_derives = {path = "../derives", version = "0.2.0"} ts-rs = "6.1.2" [dev-dependencies] diff --git a/crates/workbook/xmlserde/src/complex_types.rs b/crates/workbook/xmlserde/src/complex_types.rs index 705db6bf..2959a4b4 100644 --- a/crates/workbook/xmlserde/src/complex_types.rs +++ b/crates/workbook/xmlserde/src/complex_types.rs @@ -128,9 +128,7 @@ pub struct CtMruColors { pub color: Vec, } -#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/color.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj)] pub struct CtColor { #[xmlserde(name = b"auto", ty = "attr")] pub auto: Option, @@ -144,9 +142,7 @@ pub struct CtColor { pub tint: f64, } -#[derive(XmlSerialize, XmlDeserialize, Debug, Hash, PartialEq, Eq, Clone, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/border.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Hash, PartialEq, Eq, Clone)] pub struct CtBorder { #[xmlserde(name = b"left", ty = "child")] pub left: Option, @@ -170,9 +166,7 @@ pub struct CtBorder { pub outline: bool, } -#[derive(XmlSerialize, XmlDeserialize, Debug, Hash, PartialEq, Eq, Clone, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/border_pr.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Hash, PartialEq, Eq, Clone)] pub struct CtBorderPr { #[xmlserde(name = b"color", ty = "child")] pub color: Option, @@ -210,9 +204,7 @@ pub struct CtCellStyle { pub custom_builtin: Option, } -#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/pattern_fill.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, Hash, PartialEq, Eq)] pub struct CtPatternFill { #[xmlserde(name = b"fgColor", ty = "child")] pub fg_color: Option, @@ -222,9 +214,7 @@ pub struct CtPatternFill { pub pattern_type: Option, } -#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/gradient_fill.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj)] pub struct CtGradientFill { #[xmlserde(name = b"stop", ty = "child")] pub stops: Vec, @@ -242,25 +232,7 @@ pub struct CtGradientFill { pub bottom: f64, } -#[cfg(test)] -mod tests2 { - use super::CtPatternFill; - use serde_json::to_string; - #[test] - fn t() { - let f = CtPatternFill { - fg_color: None, - bg_color: None, - pattern_type: None, - }; - let a = to_string(&f); - println!("{:?}", a); - } -} - -#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/gradient_stop.ts")] -#[serde(rename_all = "camelCase")] +#[derive(XmlSerialize, XmlDeserialize, Debug, Clone, MapObj)] pub struct CtGradientStop { #[xmlserde(name = b"color", ty = "child")] pub color: CtColor, @@ -276,8 +248,7 @@ pub struct CtFills { pub fills: Vec, } -#[derive(Debug, Clone, Hash, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/fill.ts")] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub enum CtFill { PatternFill(CtPatternFill), GradientFill(CtGradientFill), @@ -411,9 +382,7 @@ pub struct CtFonts { pub fonts: Vec, } -#[derive(Debug, XmlSerialize, XmlDeserialize, Hash, Eq, PartialEq, Clone, serde::Serialize, TS)] -#[ts(export, export_to = "../../../src/bindings/font.ts")] -#[serde(rename_all = "camelCase")] +#[derive(Debug, XmlSerialize, XmlDeserialize, Hash, Eq, PartialEq, Clone)] pub struct CtFont { #[xmlserde(name = b"b", ty = "sfc")] pub bold: bool, diff --git a/crates/workbook/xmlserde/src/lib.rs b/crates/workbook/xmlserde/src/lib.rs index f34599ad..8db06085 100644 --- a/crates/workbook/xmlserde/src/lib.rs +++ b/crates/workbook/xmlserde/src/lib.rs @@ -44,6 +44,7 @@ pub mod sst; pub mod style_sheet; #[cfg(test)] mod test_utils; +pub mod theme; pub mod workbook; pub mod worksheet; use std::io::{BufRead, Write}; @@ -495,4 +496,44 @@ mod tests { let p = xml_serialize(b"Child", c); assert_eq!(p, ""); } + + #[test] + fn enum_serialize_test() { + #[derive(XmlDeserialize, XmlSerialize)] + struct TestA { + #[xmlserde(name = b"age", ty = "attr")] + pub age: u16, + } + + #[derive(XmlDeserialize, XmlSerialize)] + struct TestB { + #[xmlserde(name = b"name", ty = "attr")] + pub name: String, + } + + #[derive(XmlSerialize, XmlDeserialize)] + enum TestEnum { + #[xmlserde(name = b"testA")] + TestA(TestA), + #[xmlserde(name = b"testB")] + TestB(TestB), + } + + #[derive(XmlSerialize, XmlDeserialize)] + struct Child { + #[xmlserde(name = b"dummy", ty = "child")] + pub c: TestEnum, + } + + let obj = Child { + c: TestEnum::TestA(TestA { age: 23 }), + }; + let xml = xml_serialize(b"Child", obj); + println!("{}", xml); + let p = xml_deserialize_from_str::(b"Child", &xml).unwrap(); + match p.c { + TestEnum::TestA(a) => assert_eq!(a.age, 23), + TestEnum::TestB(_) => panic!(), + } + } } diff --git a/crates/workbook/xmlserde/src/theme.rs b/crates/workbook/xmlserde/src/theme.rs new file mode 100644 index 00000000..4fb5bcfc --- /dev/null +++ b/crates/workbook/xmlserde/src/theme.rs @@ -0,0 +1,208 @@ +use crate::defaults::default_zero_u8; + +// Ct_OfficeStyleSheet 20.1.6.2 +#[derive(Debug, XmlSerialize, XmlDeserialize)] +#[xmlserde(with_custom_ns(b"a", b"http://schemas.openxmlformats.org/drawingml/2006/main"))] +pub struct ThemePart { + #[xmlserde(name = b"name", ty = "attr")] + pub name: String, + #[xmlserde(name = b"a:themeElements", ty = "child")] + pub theme_elements: CtBaseStyles, + // pub object_defaults: Option, + // pub extra_clr_scheme_lst: Option, + // pub ext_lst: Option, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtBaseStyles { + #[xmlserde(name = b"a:clrScheme", ty = "child")] + pub clr_scheme: CtColorScheme, + #[xmlserde(name = b"a:fontScheme", ty = "child")] + pub font_scheme: CtFontScheme, + // #[xmlserde(name = b"a:fmtScheme", ty = "child")] + // pub fmt_scheme: CtStyleMatrix, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtColorScheme { + #[xmlserde(name = b"name", ty = "attr")] + pub name: String, + #[xmlserde(name = b"a:dk1", ty = "child")] + pub dk1: EgColorChoice, + #[xmlserde(name = b"a:lt1", ty = "child")] + pub lt1: EgColorChoice, + #[xmlserde(name = b"a:dk2", ty = "child")] + pub dk2: EgColorChoice, + #[xmlserde(name = b"a:lt2", ty = "child")] + pub lt2: EgColorChoice, + #[xmlserde(name = b"a:accent1", ty = "child")] + pub accent1: EgColorChoice, + #[xmlserde(name = b"a:accent2", ty = "child")] + pub accent2: EgColorChoice, + #[xmlserde(name = b"a:accent3", ty = "child")] + pub accent3: EgColorChoice, + #[xmlserde(name = b"a:accent4", ty = "child")] + pub accent4: EgColorChoice, + #[xmlserde(name = b"a:accent5", ty = "child")] + pub accent5: EgColorChoice, + #[xmlserde(name = b"a:accent6", ty = "child")] + pub accent6: EgColorChoice, + #[xmlserde(name = b"a:hlink", ty = "child")] + pub hlink: EgColorChoice, + #[xmlserde(name = b"a:folHlink", ty = "child")] + pub fol_hlink: EgColorChoice, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub enum EgColorChoice { + #[xmlserde(name = b"a:sysClr")] + SysClr(CtSystemColor), + #[xmlserde(name = b"a:srgbClr")] + SrgbClr(CtSrgbColor), +} + +impl EgColorChoice { + pub fn get_color(&self) -> String { + match self { + EgColorChoice::SysClr(sys) => { + if let Some(rgb) = &sys.last_clr { + let mut a = String::from("FF"); + a.push_str(&rgb); + a + } else { + String::from("") + } + } + EgColorChoice::SrgbClr(srgb_color) => { + let rgb = &srgb_color.val; + let mut a = String::from("FF"); + a.push_str(&rgb); + a + } + } + } +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtSystemColor { + #[xmlserde(name = b"val", ty = "attr")] + pub val: String, + #[xmlserde(name = b"lastClr", ty = "attr")] + pub last_clr: Option, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtSrgbColor { + #[xmlserde(name = b"val", ty = "attr")] + pub val: String, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtFontScheme { + #[xmlserde(name = b"name", ty = "attr")] + pub name: String, + #[xmlserde(name = b"a:majorFont", ty = "child")] + pub major_font: CtFontCollection, + #[xmlserde(name = b"a:minorFont", ty = "child")] + pub minor_font: CtFontCollection, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtFontCollection { + #[xmlserde(name = b"a:latin", ty = "child")] + pub latin: CtTextFont, + #[xmlserde(name = b"a:ea", ty = "child")] + pub ea: CtTextFont, + #[xmlserde(name = b"a:cs", ty = "child")] + pub cs: CtTextFont, + #[xmlserde(name = b"a:font", ty = "child", vec_size = 30)] + pub fonts: Vec, +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtTextFont { + #[xmlserde(name = b"typeface", ty = "attr")] + pub typeface: String, + #[xmlserde(name = b"panose", ty = "attr")] + pub panose: Option, + #[xmlserde(name = b"pitchFamily", ty = "attr", default = "default_zero_u8")] + pub pitch_family: u8, + #[xmlserde(name = b"charset", ty = "attr", default = "default_charset")] + pub charset: u8, +} + +fn default_charset() -> u8 { + 1 +} + +#[derive(Debug, XmlSerialize, XmlDeserialize)] +pub struct CtSupplementalFont { + #[xmlserde(name = b"script", ty = "attr")] + pub script: String, + #[xmlserde(name = b"typeface", ty = "attr")] + pub typeface: String, +} + +// pub struct CtStyleMatrix { +// pub name: String, +// pub fill_style_lst: CtFillStyleList, +// pub ln_style_lst: CtLineStyleList, +// pub effect_style_lst: CtEffectStyleList, +// pub bg_fill_style_lst: CtBackgroundFillStyleList, +// } + +// pub type CtFillStyleList = Vec; + +// pub enum EgFillProperties { +// NoFill(CtNoFillProperties), +// SolidFill(EgColorChoice), +// GradFill(CtGradientFillProperties), +// BlipFill(CtBlipFillProperties), +// PattFill(CtPatternFillProperties), +// GrpFill(CtGroupFillProperties), +// } + +// #[derive(XmlSerialize, XmlDeserialize)] +// pub struct CtNoFillProperties {} + +// pub struct CtGradientFillProperties { +// pub flip: Option, +// pub rot_with_shape: Option, +// } + +// pub struct CtGradientStopList { +// pub gss: Vec, // minOccurs= "2" +// } + +// pub struct CtGradientStop2 { +// pos: u8, +// } + +#[cfg(test)] +mod tests { + use super::ThemePart; + use crate::xml_deserialize_from_str; + + #[test] + fn test1() { + let xml = include_str!("../../examples/theme1.xml"); + let r = xml_deserialize_from_str::(b"a:theme", xml); + match r { + Ok(theme) => { + assert_eq!(theme.name, "Office 主题​​") + // use crate::test_utils::*; + // use crate::xml_serialize_with_decl; + // let expected = to_tree(&in_one_line(xml)); + // let actual = xml_serialize_with_decl(b"a:theme", theme); + // let r = to_tree(&in_one_line(&actual)); + // println!("{:?}", actual); + // use std::io::Write; + // let mut file1 = std::fs::File::create("data1.txt").expect("create failed"); + // file1.write_all(expected.as_bytes()).expect("write failed"); + // let mut file2 = std::fs::File::create("data2.txt").expect("create failed"); + // file2.write_all(r.as_bytes()).expect("write failed"); + } + Err(_) => todo!(), + } + } +} diff --git a/src/bindings/border.ts b/src/bindings/border.ts index a9ebab98..0a2100c3 100644 --- a/src/bindings/border.ts +++ b/src/bindings/border.ts @@ -1,3 +1,3 @@ -import type { CtBorderPr } from "./border_pr"; +import type { BorderPr } from "./border_pr"; -export interface CtBorder { left: CtBorderPr | null, right: CtBorderPr | null, top: CtBorderPr | null, bottom: CtBorderPr | null, diagonal: CtBorderPr | null, vertical: CtBorderPr | null, horizontal: CtBorderPr | null, diagonalUp: boolean | null, diagonalDown: boolean | null, outline: boolean, } \ No newline at end of file +export interface Border { left: BorderPr | null, right: BorderPr | null, top: BorderPr | null, bottom: BorderPr | null, diagonal: BorderPr | null, vertical: BorderPr | null, horizontal: BorderPr | null, diagonalUp: boolean | null, diagonalDown: boolean | null, outline: boolean, } \ No newline at end of file diff --git a/src/bindings/border_pr.ts b/src/bindings/border_pr.ts index 02ed8909..e7043bf5 100644 --- a/src/bindings/border_pr.ts +++ b/src/bindings/border_pr.ts @@ -1,4 +1,4 @@ -import type { CtColor } from "./color"; +import type { Color } from "./color"; import type { StBorderStyle } from "./st_border_style"; -export interface CtBorderPr { color: CtColor | null, style: StBorderStyle, } \ No newline at end of file +export interface BorderPr { color: Color | null, style: StBorderStyle, } \ No newline at end of file diff --git a/src/bindings/color.ts b/src/bindings/color.ts index e78647de..b33bcc45 100644 --- a/src/bindings/color.ts +++ b/src/bindings/color.ts @@ -1,2 +1,2 @@ -export interface CtColor { auto: boolean | null, indexed: number | null, rgb: string | null, theme: number | null, tint: number, } \ No newline at end of file +export interface Color { rgb: string, tint: number, } \ No newline at end of file diff --git a/src/bindings/fill.ts b/src/bindings/fill.ts index b38d9231..f9349b36 100644 --- a/src/bindings/fill.ts +++ b/src/bindings/fill.ts @@ -1,4 +1,4 @@ -import type { CtGradientFill } from "./gradient_fill"; -import type { CtPatternFill } from "./pattern_fill"; +import type { GradientFill } from "./gradient_fill"; +import type { PatternFill } from "./pattern_fill"; -export type CtFill = { PatternFill: CtPatternFill } | { GradientFill: CtGradientFill }; \ No newline at end of file +export type Fill = { patternFill: PatternFill } | { gradientFill: GradientFill }; \ No newline at end of file diff --git a/src/bindings/font.ts b/src/bindings/font.ts index 037aedaf..eb643a36 100644 --- a/src/bindings/font.ts +++ b/src/bindings/font.ts @@ -1,10 +1,8 @@ -import type { CtColor } from "./color"; +import type { Color } from "./color"; import type { CtFontFamily } from "./font_family"; import type { CtFontName } from "./font_name"; import type { CtFontScheme } from "./font_scheme"; -import type { CtFontSize } from "./font_size"; -import type { CtIntProperty } from "./int_property"; import type { CtUnderlineProperty } from "./underline_property"; import type { CtVerticalAlignFontProperty } from "./vertical_align_font_property"; -export interface CtFont { bold: boolean, italic: boolean, underline: CtUnderlineProperty | null, color: CtColor | null, sz: CtFontSize | null, name: CtFontName | null, charset: CtIntProperty | null, family: CtFontFamily | null, strike: boolean, outline: boolean, shadow: boolean, condense: boolean, extend: boolean, vertAlign: CtVerticalAlignFontProperty | null, scheme: CtFontScheme | null, } \ No newline at end of file +export interface Font { bold: boolean, italic: boolean, underline: CtUnderlineProperty | null, color: Color, sz: number | null, name: CtFontName | null, charset: number | null, family: CtFontFamily | null, strike: boolean, outline: boolean, shadow: boolean, condense: boolean, extend: boolean, vertAlign: CtVerticalAlignFontProperty | null, scheme: CtFontScheme | null, } \ No newline at end of file diff --git a/src/bindings/gradient_fill.ts b/src/bindings/gradient_fill.ts index 8d087287..16c70197 100644 --- a/src/bindings/gradient_fill.ts +++ b/src/bindings/gradient_fill.ts @@ -1,4 +1,4 @@ -import type { CtGradientStop } from "./gradient_stop"; +import type { GradientStop } from "./gradient_stop"; import type { StGradientType } from "./st_gradient_type"; -export interface CtGradientFill { stops: Array, ty: StGradientType, degree: number, left: number, right: number, top: number, bottom: number, } \ No newline at end of file +export interface GradientFill { stops: Array, ty: StGradientType, degree: number, left: number, right: number, top: number, bottom: number, } \ No newline at end of file diff --git a/src/bindings/gradient_stop.ts b/src/bindings/gradient_stop.ts index d6d37574..e9201b7a 100644 --- a/src/bindings/gradient_stop.ts +++ b/src/bindings/gradient_stop.ts @@ -1,3 +1,3 @@ -import type { CtColor } from "./color"; +import type { Color } from "./color"; -export interface CtGradientStop { color: CtColor, position: number, } \ No newline at end of file +export interface GradientStop { color: Color, position: number, } \ No newline at end of file diff --git a/src/bindings/pattern_fill.ts b/src/bindings/pattern_fill.ts index c54b2ca1..600c21fe 100644 --- a/src/bindings/pattern_fill.ts +++ b/src/bindings/pattern_fill.ts @@ -1,4 +1,4 @@ -import type { CtColor } from "./color"; +import type { Color } from "./color"; import type { StPatternType } from "./st_pattern_type"; -export interface CtPatternFill { fgColor: CtColor | null, bgColor: CtColor | null, patternType: StPatternType | null, } \ No newline at end of file +export interface PatternFill { fgColor: Color | null, bgColor: Color | null, patternType: StPatternType | null, } \ No newline at end of file diff --git a/src/bindings/style.ts b/src/bindings/style.ts index 801c07e6..029d673a 100644 --- a/src/bindings/style.ts +++ b/src/bindings/style.ts @@ -1,16 +1,7 @@ -// WARNING: unlike other files in this directory, this file will not be -// re-generated every time we run -// ``` -// cargo test --workspace -// ``` -// Check in crates/controller/src/controller/display.rs::Style, it has no -// ``` -// #[ts(export)] -// ``` -import type { CtBorder } from "./border"; -import type { CtCellAlignment } from "./cell_alignment"; -import type { CtCellProtection } from "./cell_protection"; -import type { CtFill } from "./fill"; -import type { CtFont } from "./font"; +import type { Border } from "./border"; +import type { CtCellAlignment } from "../../../src/bindings/cell_alignment"; +import type { CtCellProtection } from "../../../src/bindings/cell_protection"; +import type { Fill } from "./fill"; +import type { Font } from "./font"; -export interface Style { font: CtFont, fill: CtFill, border: CtBorder, alignment: CtCellAlignment | null, protection: CtCellProtection | null, formatter: string, } \ No newline at end of file +export interface Style { font: Font, fill: Fill, border: Border, alignment: CtCellAlignment | null, protection: CtCellProtection | null, formatter: string, } \ No newline at end of file diff --git a/src/bindings/style_update_type.ts b/src/bindings/style_update_type.ts index b85a81f0..f376b5a6 100644 --- a/src/bindings/style_update_type.ts +++ b/src/bindings/style_update_type.ts @@ -1,5 +1,5 @@ -import type { CtPatternFill } from "./pattern_fill" +import type { PatternFill } from "./pattern_fill" import type { StBorderStyle } from "./st_border_style" import type { StUnderlineValues } from "./st_underline_values" -export interface StyleUpdateType { setFontBold: boolean | null, setFontItalic: boolean | null, setFontUnderline: StUnderlineValues | null, setFontColor: string | null, setFontSize: number | null, setFontName: string | null, setFontOutline: boolean | null, setFontShadow: boolean | null, setFontStrike: boolean | null, setFontCondense: boolean | null, setLeftBorderColor: string | null, setRightBorderColor: string | null, setTopBorderColor: string | null, setBottomBorderColor: string | null, setLeftBorderStyle: StBorderStyle | null, setRightBorderStyle: StBorderStyle | null, setTopBorderStyle: StBorderStyle | null, setBottomBorderStyle: StBorderStyle | null, setBorderGiagonalUp: boolean | null, setBorderGiagonalDown: boolean | null, setPatternFill: CtPatternFill | null, } \ No newline at end of file +export interface StyleUpdateType { setFontBold: boolean | null, setFontItalic: boolean | null, setFontUnderline: StUnderlineValues | null, setFontColor: string | null, setFontSize: number | null, setFontName: string | null, setFontOutline: boolean | null, setFontShadow: boolean | null, setFontStrike: boolean | null, setFontCondense: boolean | null, setLeftBorderColor: string | null, setRightBorderColor: string | null, setTopBorderColor: string | null, setBottomBorderColor: string | null, setLeftBorderStyle: StBorderStyle | null, setRightBorderStyle: StBorderStyle | null, setTopBorderStyle: StBorderStyle | null, setBottomBorderStyle: StBorderStyle | null, setBorderGiagonalUp: boolean | null, setBorderGiagonalDown: boolean | null, setPatternFill: PatternFill | null, } \ No newline at end of file diff --git a/src/components/canvas/managers/render.ts b/src/components/canvas/managers/render.ts index 9921e0ae..59f3f403 100644 --- a/src/components/canvas/managers/render.ts +++ b/src/components/canvas/managers/render.ts @@ -94,9 +94,9 @@ export class Render { private _fill (box: Box, style?: StandardStyle) { const fill = style?.fill - if (!fill || !hasOwnProperty(fill, 'PatternFill')) + if (!fill || !hasOwnProperty(fill, 'patternFill')) return - const patternFill = fill.PatternFill + const patternFill = fill.patternFill if (patternFill.bgColor) { const color = StandardColor.fromCtColor(patternFill.bgColor) const fillAttr = new CanvasAttr() diff --git a/src/core/painter/painter.service.ts b/src/core/painter/painter.service.ts index 2c666d6f..428e58f8 100644 --- a/src/core/painter/painter.service.ts +++ b/src/core/painter/painter.service.ts @@ -1,4 +1,4 @@ -import { CtBorderPr, StPatternType } from 'bindings' +import { BorderPr, StPatternType } from 'bindings' import { StandardColor } from 'core/standable' import { CanvasAttr } from './canvas_attr' import { Box } from './box' @@ -44,7 +44,7 @@ export class PainterService extends CanvasApi { } // tslint:disable-next-line: max-func-body-length - border (border: CtBorderPr, box: Box, type: Direction) { + border (border: BorderPr, box: Box, type: Direction) { this.save() const stdColor = StandardColor.fromCtColor(border.color) const dot = npx(1) @@ -206,7 +206,7 @@ export class PainterService extends CanvasApi { case 'Left': break default: - throw Error(`Not support underline horizontal ${attr.alignment?.horizontal}`) + console.log(`Not support underline horizontal ${attr.alignment?.horizontal}`) } break default: diff --git a/src/core/standable/color.ts b/src/core/standable/color.ts index 41a79cd3..4e8dc88e 100644 --- a/src/core/standable/color.ts +++ b/src/core/standable/color.ts @@ -1,4 +1,4 @@ -import {CtColor} from 'bindings' +import {Color} from 'bindings' // https://css-tricks.com/snippets/javascript/random-hex-color/ function getRandomColor() { return Math.floor(Math.random()*16777215).toString(16); @@ -24,82 +24,13 @@ export class StandardColor { color.#blue = parseInt(rgb.slice(4,6), 16) return color } - static fromCtColor(color: CtColor | null) { + static fromCtColor(color: Color | null) { if (color === null) { return new StandardColor() } if (color.rgb !== null) { return StandardColor.fromArgb(color.rgb) } - if (color.indexed !== null) { - switch (color.indexed) { - case 0: return StandardColor.fromArgb("00000000") - case 1: return StandardColor.fromArgb("00FFFFFF") - case 2: return StandardColor.fromArgb("00FF0000") - case 3: return StandardColor.fromArgb("0000FF00") - case 4: return StandardColor.fromArgb("000000FF") - case 5: return StandardColor.fromArgb("00FFFF00") - case 6: return StandardColor.fromArgb("00FF00FF") - case 7: return StandardColor.fromArgb("0000FFFF") - case 8: return StandardColor.fromArgb("00000000") - case 9: return StandardColor.fromArgb("00FFFFFF") - case 10: return StandardColor.fromArgb("00FF0000") - case 11: return StandardColor.fromArgb("0000FF00") - case 12: return StandardColor.fromArgb("000000FF") - case 13: return StandardColor.fromArgb("00FFFF00") - case 14: return StandardColor.fromArgb("00FF00FF") - case 15: return StandardColor.fromArgb("0000FFFF") - case 16: return StandardColor.fromArgb("00800000") - case 17: return StandardColor.fromArgb("00008000") - case 18: return StandardColor.fromArgb("00000080") - case 19: return StandardColor.fromArgb("00808000") - case 20: return StandardColor.fromArgb("00800080") - case 21: return StandardColor.fromArgb("00008080") - case 22: return StandardColor.fromArgb("00C0C0C0") - case 23: return StandardColor.fromArgb("00808080") - case 24: return StandardColor.fromArgb("009999FF") - case 25: return StandardColor.fromArgb("00993366") - case 26: return StandardColor.fromArgb("00FFFFCC") - case 27: return StandardColor.fromArgb("00CCFFFF") - case 28: return StandardColor.fromArgb("00660066") - case 29: return StandardColor.fromArgb("00FF8080") - case 30: return StandardColor.fromArgb("000066CC") - case 31: return StandardColor.fromArgb("00CCCCFF") - case 32: return StandardColor.fromArgb("00000080") - case 33: return StandardColor.fromArgb("00FF00FF") - case 34: return StandardColor.fromArgb("00FFFF00") - case 35: return StandardColor.fromArgb("0000FFFF") - case 36: return StandardColor.fromArgb("00800080") - case 37: return StandardColor.fromArgb("00800000") - case 38: return StandardColor.fromArgb("00008080") - case 39: return StandardColor.fromArgb("000000FF") - case 40: return StandardColor.fromArgb("0000CCFF") - case 41: return StandardColor.fromArgb("00CCFFFF") - case 42: return StandardColor.fromArgb("00CCFFCC") - case 43: return StandardColor.fromArgb("00FFFF99") - case 44: return StandardColor.fromArgb("0099CCFF") - case 45: return StandardColor.fromArgb("00FF99CC") - case 46: return StandardColor.fromArgb("00CC99FF") - case 47: return StandardColor.fromArgb("00FFCC99") - case 48: return StandardColor.fromArgb("003366FF") - case 49: return StandardColor.fromArgb("0033CCCC") - case 50: return StandardColor.fromArgb("0099CC00") - case 51: return StandardColor.fromArgb("00FFCC00") - case 52: return StandardColor.fromArgb("00FF9900") - case 53: return StandardColor.fromArgb("00FF6600") - case 54: return StandardColor.fromArgb("00666699") - case 55: return StandardColor.fromArgb("00969696") - case 56: return StandardColor.fromArgb("00003366") - case 57: return StandardColor.fromArgb("00339966") - case 58: return StandardColor.fromArgb("00003300") - case 59: return StandardColor.fromArgb("00333300") - case 60: return StandardColor.fromArgb("00993300") - case 61: return StandardColor.fromArgb("00993366") - case 62: return StandardColor.fromArgb("00333399") - case 63: return StandardColor.fromArgb("00333333") - default: return new StandardColor() - } - } return new StandardColor() } diff --git a/src/core/standable/font.ts b/src/core/standable/font.ts index 78c77fa2..fefa1785 100644 --- a/src/core/standable/font.ts +++ b/src/core/standable/font.ts @@ -1,4 +1,4 @@ -import { CtFont, CtFontName, CtUnderlineProperty } from 'bindings' +import { Font, CtFontName, CtUnderlineProperty, Color } from 'bindings' import { shallowCopy } from 'common' import { StandardColor } from './color' const DEFAULT_FONT_SIZE = 10 @@ -13,8 +13,8 @@ const DEFAULT_FONT_SIZE = 10 * font-size(字体大小): 可通过多种不同单位(比如像素或百分比等)来设置, 如:12xp,12pt,120%,1em */ export type FontSizeUnit = 'px' | 'pt' -export class StandardFont implements CtFont { - static from(font: CtFont): StandardFont { +export class StandardFont implements Font { + static from(font: Font): StandardFont { const f = new StandardFont() if (font.color === null) f.standardColor = StandardColor.from(0, 0, 0) @@ -23,14 +23,14 @@ export class StandardFont implements CtFont { shallowCopy(font, f) // ooxml标准存的是pt f.fontSizeUnit = 'pt' - if (font.sz?.val === 0) { + if (font.sz === 0) { f.fontSizeUnit = 'px' - f.sz = {val: DEFAULT_FONT_SIZE} + f.sz = DEFAULT_FONT_SIZE } return f } get size() { - return this.sz.val + return this.sz } name: CtFontName = {val: 'Arial'} @@ -39,9 +39,9 @@ export class StandardFont implements CtFont { lineHeight = '100%' standardColor = StandardColor.from(0, 0, 0, 1) bold = false - color = null + color!: Color family = null - sz = {val: 10} + sz = 10 condense = false italic = false outline = false @@ -53,7 +53,7 @@ export class StandardFont implements CtFont { scheme = null setSize(s: number) { - this.sz.val = s + this.sz = s return this } diff --git a/src/core/standable/style.ts b/src/core/standable/style.ts index 1a40564a..f6a53a00 100644 --- a/src/core/standable/style.ts +++ b/src/core/standable/style.ts @@ -1,12 +1,12 @@ -import { CtCellAlignment, CtBorder, CtFont, CtFill, Style, CtCellProtection } from 'bindings' +import { CtCellAlignment, Border, Font, Fill, Style, CtCellProtection } from 'bindings' import { shallowCopy } from 'common' import { StandardFont } from './font' export class StandardStyle implements Style { protection!: CtCellProtection - border!: CtBorder - font!: CtFont - fill!: CtFill + border!: Border + font!: Font + fill!: Fill alignment!: CtCellAlignment formatter: string = '' static from (style: Style) { diff --git a/src/wasms/fc/Cargo.toml b/src/wasms/fc/Cargo.toml index efe1e0bf..7f7f1d92 100644 --- a/src/wasms/fc/Cargo.toml +++ b/src/wasms/fc/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Jiangming He"] edition = "2018" name = "logisheets_wasm_fc" -version = "0.1.0" +version = "0.2.0" [lib] crate-type = ["cdylib", "rlib"] @@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"] default = ["console_error_panic_hook"] [dependencies] -logisheets_controller = {versoin = "0.1.0", path = '../../../crates/controller'} +logisheets_controller = {versoin = "0.2.0", path = '../../../crates/controller'} wasm-bindgen = {version = "0.2.79", features = ["serde-serialize"]} web-sys = {version = "0.3.56", features = ["console"]} console_error_panic_hook = {version = "0.1.6", optional = true} diff --git a/src/wasms/server/Cargo.toml b/src/wasms/server/Cargo.toml index bd875e37..69ba9143 100644 --- a/src/wasms/server/Cargo.toml +++ b/src/wasms/server/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Jiangming He"] edition = "2018" name = "logisheets_wasm_server" -version = "0.1.0" +version = "0.2.0" [lib] crate-type = ["cdylib", "rlib"] @@ -18,7 +18,7 @@ web-sys = {version = "0.3.56", features = ["console"]} getrandom = {version = "0.2.3", features = ["js"]} serde = {version = "1.0.125", features = ["derive"]} serde_json = "1.0.59" -logisheets_controller = {versoin = "0.1.0", path = '../../../crates/controller'} +logisheets_controller = {versoin = "0.2.0", path = '../../../crates/controller'} # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires diff --git a/tests/builtin_style.xlsx b/tests/builtin_style.xlsx new file mode 100644 index 00000000..70a99d25 Binary files /dev/null and b/tests/builtin_style.xlsx differ diff --git a/tests/test.rs b/tests/test.rs index c7dbc522..da3cc719 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,48 +1,77 @@ extern crate logisheets; +extern crate logisheets_controller; +extern crate logisheets_workbook; -#[test] -fn test_value1() { - use logisheets::{Value, Workbook}; - use std::fs; - let mut buf = fs::read("tests/6.xlsx").unwrap(); - let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); - let mut ws = wb.get_sheet_by_idx(0).unwrap(); - let v = ws.get_value(9, 1).unwrap(); - match v { - Value::Number(f) => assert_eq!(f, 32.0), - _ => panic!(), - } - let v = ws.get_value(8, 1).unwrap(); - match v { - Value::Str(f) => assert_eq!(f, "Q1"), - _ => panic!(), - } - let v = ws.get_value(100, 1).unwrap(); - match v { - Value::Empty => {} - _ => panic!(), +#[cfg(test)] +mod test_builtin_style { + #[test] + fn test_builtin1() { + use logisheets::Workbook; + use logisheets_controller::Fill; + use std::fs; + let mut buf = fs::read("tests/builtin_style.xlsx").unwrap(); + let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); + let mut ws = wb.get_sheet_by_idx(0).unwrap(); + let s = ws.get_style(3, 1).unwrap(); + match s.fill { + Fill::PatternFill(f) => { + if let Some(clr) = f.fg_color { + assert_eq!(clr.rgb, "FF4F81BD") + } else { + panic!() + } + } + Fill::GradientFill(_) => todo!(), + } } } -#[test] -fn test_formula1() { - use logisheets::Workbook; - use std::fs; - let mut buf = fs::read("tests/6.xlsx").unwrap(); - let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); - let mut ws = wb.get_sheet_by_idx(0).unwrap(); - let f = ws.get_formula(9, 1).unwrap(); - assert_eq!(f, "B18") -} +#[cfg(test)] +mod test_6 { + #[test] + fn test_value1() { + use logisheets::{Value, Workbook}; + use std::fs; + let mut buf = fs::read("tests/6.xlsx").unwrap(); + let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); + let mut ws = wb.get_sheet_by_idx(0).unwrap(); + let v = ws.get_value(9, 1).unwrap(); + match v { + Value::Number(f) => assert_eq!(f, 32.0), + _ => panic!(), + } + let v = ws.get_value(8, 1).unwrap(); + match v { + Value::Str(f) => assert_eq!(f, "Q1"), + _ => panic!(), + } + let v = ws.get_value(100, 1).unwrap(); + match v { + Value::Empty => {} + _ => panic!(), + } + } -#[test] -fn test_style1() { - use logisheets::{StUnderlineValues, Workbook}; - use std::fs; - let mut buf = fs::read("tests/6.xlsx").unwrap(); - let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); - let mut ws = wb.get_sheet_by_idx(0).unwrap(); - let style = ws.get_style(9, 1).unwrap(); - let underline = style.font.underline.unwrap().val; - assert!(matches!(underline, StUnderlineValues::Single)); + #[test] + fn test_formula1() { + use logisheets::Workbook; + use std::fs; + let mut buf = fs::read("tests/6.xlsx").unwrap(); + let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); + let mut ws = wb.get_sheet_by_idx(0).unwrap(); + let f = ws.get_formula(9, 1).unwrap(); + assert_eq!(f, "B18") + } + + #[test] + fn test_style1() { + use logisheets::{StUnderlineValues, Workbook}; + use std::fs; + let mut buf = fs::read("tests/6.xlsx").unwrap(); + let mut wb = Workbook::from_file(&mut buf, String::from("6")).unwrap(); + let mut ws = wb.get_sheet_by_idx(0).unwrap(); + let style = ws.get_style(9, 1).unwrap(); + let underline = style.font.underline.unwrap().val; + assert!(matches!(underline, StUnderlineValues::Single)); + } }