diff --git a/Cargo.lock b/Cargo.lock index ed2ba2d..8fdc0e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -53,6 +62,41 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + [[package]] name = "rustex" version = "0.1.0" @@ -60,6 +104,7 @@ dependencies = [ "fontdue", "icy_sixel", "lazy_static", + "regex", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index da73cc1..a8e2288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" fontdue = "0.9.3" icy_sixel = "0.1.3" lazy_static = "1.5.0" +regex = "1.11.2" diff --git a/src/bitmap.rs b/src/bitmap.rs index 0d81c7f..e52884d 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -101,8 +101,8 @@ impl Bitmap { } // Unit vector perpendicular to the line - let perp_x = -dy / length; - let perp_y = dx / length; + // let perp_x = -dy / length; + // let perp_y = dx / length; // Half thickness for calculations let half_thickness = thickness * 0.5; diff --git a/src/element/element_parser.rs b/src/element/element_parser.rs new file mode 100644 index 0000000..9a627b9 --- /dev/null +++ b/src/element/element_parser.rs @@ -0,0 +1,81 @@ +use std::rc::Rc; + +use crate::element::{text_parser::{self, ParsedObject}, KElement}; + +impl KElement { + pub fn parse(input: &str) -> Result { + let elems = text_parser::parse(input)?; + Self::parse_object(&elems) + } + + pub fn parse_object(elems: &Vec) -> Result { + + let mut root = Vec::new(); + + for elem in elems { + match elem { + text_parser::ParsedObject::Func { + name, + content, + super_script, + sub_script + } => { + + if !super_script.is_empty() && !sub_script.is_empty() { + // root.push(KElement::SuperSub { inner: (), upper: (), lower: () } + } else if !super_script.is_empty() { + // root.push(KElement::SuperSub { inner: (), upper: (), lower: () } + } else if !sub_script.is_empty() { + // root.push(KElement::SuperSub { inner: (), upper: (), lower: () } + } else { + + root.push(Self::from_function(name, content)?); + } + }, + text_parser::ParsedObject::Var { + text, + super_script, + sub_script + } => { + if !super_script.is_empty() && !sub_script.is_empty() { + root.push(KElement::SuperSub { + inner: Rc::new(KElement::Text(text.clone())), + upper: Some(Rc::new(Self::parse_object(super_script)?)), + lower: Some(Rc::new(Self::parse_object(sub_script)?)), + }); + } else if !super_script.is_empty() { + root.push(KElement::SuperSub { + inner: Rc::new(KElement::Text(text.clone())), + upper: Some(Rc::new(Self::parse_object(super_script)?)), + lower: None + }); + } else if !sub_script.is_empty() { + root.push(KElement::SuperSub { + inner: Rc::new(KElement::Text(text.clone())), + upper: None, + lower: Some(Rc::new(Self::parse_object(super_script)?)) + }); + } else { + root.push(KElement::Text(text.clone())); + } + }, + text_parser::ParsedObject::Operator { + text + } => { + root.push(KElement::Text(text.clone())); + }, + text_parser::ParsedObject::Parenthesis { + inner, + parenthesis_type, + super_script, + sub_script + } => { + }, + } + } + + + return Ok(KElement::LinearGroup(root)); + } + +} \ No newline at end of file diff --git a/src/element/functions.rs b/src/element/functions.rs new file mode 100644 index 0000000..d61ddb9 --- /dev/null +++ b/src/element/functions.rs @@ -0,0 +1,33 @@ +use std::rc::Rc; + +use crate::element::{text_parser::ParsedObject, KElement}; + +fn assert_args(n: usize, start: usize, end: usize, err: &str) -> Result<(), String> { + if start <= n && n <= end { + Ok(()) + } else { + Err(format!("{err}, {n} != [{start}, {end}]")) + } +} + + +impl KElement { + pub fn from_function(name: &str, args: &Vec>) -> Result { + match name { + "frac" => { + assert_args(args.len(), 2, 2, "A fraction must have 2 arguments!")?; + + Ok(KElement::Fraction { + upper: Rc::new(Self::parse_object(&args[0])?), + lower: Rc::new(Self::parse_object(&args[1])?) + }) + } + "pm" => { + assert_args(args.len(), 0, 0, "Symbol cannot take in any args!")?; + + Ok(KElement::Text("±".to_string())) + } + _ => Err(format!("Invalid function: \\{}", name)) + } + } +} \ No newline at end of file diff --git a/src/element/mod.rs b/src/element/mod.rs new file mode 100644 index 0000000..bddfab1 --- /dev/null +++ b/src/element/mod.rs @@ -0,0 +1,22 @@ +use std::rc::Rc; + +mod rasterizer; +mod text_parser; +mod element_parser; +mod functions; + +pub enum KElement { + LinearGroup(Vec), + Integer(i64), + Decimal(f64), + Text(String), + Fraction { + upper: Rc, + lower: Rc + }, + SuperSub{ + inner: Rc, + upper: Option>, + lower: Option> + }, +} \ No newline at end of file diff --git a/src/parser.rs b/src/element/rasterizer.rs similarity index 65% rename from src/parser.rs rename to src/element/rasterizer.rs index c6b4b08..b2af521 100644 --- a/src/parser.rs +++ b/src/element/rasterizer.rs @@ -1,19 +1,8 @@ -use std::rc::Rc; - use fontdue::layout::{Layout, TextStyle}; -use crate::{bitmap::Bitmap, fonts::FONTS, RusTeX, consts::*}; +use crate::{bitmap::Bitmap, consts::*, element::KElement, fonts::FONTS, RusTeX}; -pub enum KElement { - LinearGroup(Vec), - Integer(i64), - Decimal(f64), - Text(String), - Fraction(Rc, Rc), - Superscript(Rc, Rc), -} - impl KElement { pub fn rasterize(&self, globals: &mut RusTeX, current_scale: f32) -> Bitmap { match self { @@ -48,10 +37,10 @@ impl KElement { KElement::Text(str) => { render_text_block(&mut globals.layout, &str, current_scale) }, - KElement::Fraction(a,b) => { - let padding = (FRACTION_PADDING * globals.settings.scale) as usize; - let (ax,ay) = a.get_bounds(globals, current_scale * FRACTION_SCALE); - let (bx,by) = b.get_bounds(globals, current_scale * FRACTION_SCALE); + KElement::Fraction{upper,lower} => { + let padding = (FRACTION_PADDING * current_scale) as usize; + let (ax,ay) = upper.get_bounds(globals, current_scale * FRACTION_SCALE); + let (bx,by) = lower.get_bounds(globals, current_scale * FRACTION_SCALE); let (width, height) = ( ax.max(bx) + padding*2, @@ -60,8 +49,8 @@ impl KElement { let mut bitmap = Bitmap::new(width, height); - let bitmap_a = &mut a.rasterize(globals, current_scale * FRACTION_SCALE); - let bitmap_b = &mut b.rasterize(globals, current_scale * FRACTION_SCALE); + let bitmap_a = &mut upper.rasterize(globals, current_scale * FRACTION_SCALE); + let bitmap_b = &mut lower.rasterize(globals, current_scale * FRACTION_SCALE); if bitmap_a.width > bitmap_b.width { bitmap.overlay(&bitmap_a, padding, 0); @@ -80,21 +69,31 @@ impl KElement { // symbols } - KElement::Superscript(a, b) => { - let (ax, ay) = a.get_bounds(globals, current_scale); - let (bx, by) = b.get_bounds(globals, current_scale * SUPERSCRIPT_SCALE); - let yoffset = (by as f32*SUPERSCRIPT_Y_OFFSET) as usize; + KElement::SuperSub{inner, upper, lower} => { + let (ax, ay) = inner.get_bounds(globals, current_scale); + if upper.is_some() && lower.is_some() { + todo!(); + } else if upper.is_some() { + let upper = upper.as_ref().unwrap(); + let (bx, by) = upper.get_bounds(globals, current_scale * SUPERSCRIPT_SCALE); + let yoffset = (by as f32*SUPERSCRIPT_Y_OFFSET) as usize; - let (width, height) = ( - ax+bx, - ay + yoffset - ); - let mut bitmap = Bitmap::new(width, height); + let (width, height) = ( + ax+bx, + ay + yoffset + ); + let mut bitmap = Bitmap::new(width, height); - bitmap.overlay(&a.rasterize(globals, current_scale), 0, yoffset); - bitmap.overlay(&b.rasterize(globals, current_scale * SUPERSCRIPT_SCALE), ax, 0); + bitmap.overlay(&inner.rasterize(globals, current_scale), 0, yoffset); + bitmap.overlay(&upper.rasterize(globals, current_scale * SUPERSCRIPT_SCALE), ax, 0); - bitmap + bitmap + + } else if lower.is_some() { + todo!(); + } else { + unreachable!() + } } } } @@ -118,21 +117,30 @@ impl KElement { KElement::Text(str) => { measure_text_bounds(&mut globals.layout, &str, current_scale) }, - KElement::Fraction(a,b) => { - let (ax,ay) = a.get_bounds(globals, current_scale * FRACTION_SCALE); - let (bx,by) = b.get_bounds(globals, current_scale * FRACTION_SCALE); + KElement::Fraction{upper,lower} => { + let (ax,ay) = upper.get_bounds(globals, current_scale * FRACTION_SCALE); + let (bx,by) = lower.get_bounds(globals, current_scale * FRACTION_SCALE); ( - (ax.max(bx)) + 2*(FRACTION_PADDING * globals.settings.scale) as usize, - ay+by + (FRACTION_PADDING * globals.settings.scale) as usize + (ax.max(bx)) + 2*(FRACTION_PADDING * current_scale) as usize, + ay+by + (FRACTION_PADDING * current_scale) as usize ) }, - KElement::Superscript(a, b) => { - let (ax, ay) = a.get_bounds(globals, current_scale); - let (bx, by) = b.get_bounds(globals, current_scale * SUPERSCRIPT_SCALE); - ( - ax+bx, - ay + (by as f32*SUPERSCRIPT_Y_OFFSET) as usize - ) + KElement::SuperSub{inner, upper, lower} => { + let (ax, ay) = inner.get_bounds(globals, current_scale); + if upper.is_some() && lower.is_some() { + todo!(); + } else if upper.is_some() { + let (bx, by) = upper.as_ref().unwrap().get_bounds(globals, current_scale * SUPERSCRIPT_SCALE); + ( + ax+bx, + ay + (by as f32*SUPERSCRIPT_Y_OFFSET) as usize + ) + } else if lower.is_some() { + todo!(); + } else { + unreachable!() + } + } } diff --git a/src/element/text_parser.rs b/src/element/text_parser.rs new file mode 100644 index 0000000..60fc99c --- /dev/null +++ b/src/element/text_parser.rs @@ -0,0 +1,307 @@ +use regex::Regex; +use lazy_static::lazy_static; +use std::collections::VecDeque; + +lazy_static! { + static ref FUNCTION_REGEX: Regex = Regex::new(r"\\[a-zA-Z]+").unwrap(); + static ref NUMBER_REGEX: Regex = Regex::new(r"\d+(?:\.\d+)?").unwrap(); + static ref LETTER_REGEX: Regex = Regex::new(r"[a-zA-Z]").unwrap(); + static ref OPERATOR_REGEX: Regex = Regex::new(r"[+\-=*/±]").unwrap(); + static ref WHITESPACE_REGEX: Regex = Regex::new(r"\s+").unwrap(); +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PerenthesisType { + Round, // () + Square, // [] + // Curly, // {} + // Angle, // <> +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ParsedObject { + Func { + name: String, + content: Vec>, + super_script: Vec, + sub_script: Vec, + }, + Var { + text: String, + super_script: Vec, + sub_script: Vec, + }, + Operator { + text: String, + }, + Parenthesis { + inner: Vec, + parenthesis_type: PerenthesisType, + super_script: Vec, + sub_script: Vec, + }, +} + +#[derive(Debug, Clone, PartialEq)] +enum Token { + Function(String), + Number(String), + Letter(String), + Operator(String), + LeftParen(PerenthesisType), + RightParen(PerenthesisType), + LeftBrace, + RightBrace, + Superscript, + Subscript, +} + +pub fn parse(input: &str) -> Result, String> { + let tokens = tokenize_with_regex(input)?; + let mut token_queue = VecDeque::from(tokens); + parse_tokens(&mut token_queue) +} + +fn tokenize_with_regex(input: &str) -> Result, String> { + let mut tokens = Vec::new(); + let mut pos = 0; + let input_chars: Vec = input.chars().collect(); + + while pos < input_chars.len() { + let remaining: String = input_chars[pos..].iter().collect(); + + // Skip whitespace + if let Some(mat) = WHITESPACE_REGEX.find(&remaining) { + if mat.start() == 0 { + pos += mat.len(); + continue; + } + } + + // Check for TeX functions + if let Some(mat) = FUNCTION_REGEX.find(&remaining) { + if mat.start() == 0 { + let func_name = mat.as_str()[1..].to_string(); // Remove the \ + tokens.push(Token::Function(func_name)); + pos += mat.len(); + continue; + } + } + + // Check for numbers (including decimals) + if let Some(mat) = NUMBER_REGEX.find(&remaining) { + if mat.start() == 0 { + tokens.push(Token::Number(mat.as_str().to_string())); + pos += mat.len(); + continue; + } + } + + // Check for single letters + if let Some(mat) = LETTER_REGEX.find(&remaining) { + if mat.start() == 0 { + tokens.push(Token::Letter(mat.as_str().to_string())); + pos += mat.len(); + continue; + } + } + + // Check for operators + if let Some(mat) = OPERATOR_REGEX.find(&remaining) { + if mat.start() == 0 { + tokens.push(Token::Operator(mat.as_str().to_string())); + pos += mat.len(); + continue; + } + } + + // Handle special characters + match input_chars[pos] { + '(' => tokens.push(Token::LeftParen(PerenthesisType::Round)), + ')' => tokens.push(Token::RightParen(PerenthesisType::Round)), + '[' => tokens.push(Token::LeftParen(PerenthesisType::Square)), + ']' => tokens.push(Token::RightParen(PerenthesisType::Square)), + '{' => tokens.push(Token::LeftBrace), + '}' => tokens.push(Token::RightBrace), + '^' => tokens.push(Token::Superscript), + '_' => tokens.push(Token::Subscript), + _ => return Err(format!("Unexpected character: '{}'", input_chars[pos])), + } + + pos += 1; + } + + Ok(tokens) +} + +fn parse_tokens(tokens: &mut VecDeque) -> Result, String> { + let mut result = Vec::new(); + + while let Some(token) = tokens.pop_front() { + match token { + Token::Function(name) => { + let func = parse_function_generic(name, tokens)?; + result.push(func); + } + Token::Number(text) | Token::Letter(text) => { + let var = parse_variable(text, tokens)?; + result.push(var); + } + Token::Operator(op) => { + result.push(ParsedObject::Operator { text: op }); + } + Token::LeftParen(paren_type) => { + let paren = parse_parenthesis(paren_type, tokens)?; + result.push(paren); + } + Token::RightParen(_) | Token::RightBrace => { + // Put it back - should be handled by parent context + tokens.push_front(token); + break; + } + _ => { + tokens.push_front(token); + break; + } + } + } + + Ok(result) +} + +fn parse_function_generic(name: String, tokens: &mut VecDeque) -> Result { + let mut content = Vec::new(); + + // Parse all braced content that follows this function + while let Some(Token::LeftBrace) = tokens.front() { + let braced_content = parse_braced_content(tokens)?; + content.push(braced_content); + } + + // Parse potential superscript and subscript + let (super_script, sub_script) = parse_scripts(tokens)?; + + Ok(ParsedObject::Func { + name, + content, + super_script, + sub_script, + }) +} + +fn parse_variable(text: String, tokens: &mut VecDeque) -> Result { + let (super_script, sub_script) = parse_scripts(tokens)?; + + Ok(ParsedObject::Var { + text, + super_script, + sub_script, + }) +} + +fn parse_parenthesis(paren_type: PerenthesisType, tokens: &mut VecDeque) -> Result { + let mut inner = Vec::new(); + + // Parse until we find the matching closing parenthesis + while !tokens.is_empty() { + if let Some(Token::RightParen(closing_type)) = tokens.front() { + if *closing_type == paren_type { + tokens.pop_front(); // consume the closing paren + break; + } + } + + let parsed = parse_tokens(tokens)?; + inner.extend(parsed); + + if tokens.is_empty() { + return Err("Unmatched opening parenthesis".to_string()); + } + } + + let (super_script, sub_script) = parse_scripts(tokens)?; + + Ok(ParsedObject::Parenthesis { + inner, + parenthesis_type: paren_type, + super_script, + sub_script, + }) +} + +fn parse_braced_content(tokens: &mut VecDeque) -> Result, String> { + if let Some(Token::LeftBrace) = tokens.pop_front() { + let mut content = Vec::new(); + + while !tokens.is_empty() { + if let Some(Token::RightBrace) = tokens.front() { + tokens.pop_front(); // consume the closing brace + break; + } + + let parsed = parse_tokens(tokens)?; + content.extend(parsed); + + if tokens.is_empty() { + return Err("Unmatched opening brace".to_string()); + } + } + + Ok(content) + } else { + Err("Expected opening brace".to_string()) + } +} + +fn parse_scripts(tokens: &mut VecDeque) -> Result<(Vec, Vec), String> { + let mut super_script = Vec::new(); + let mut sub_script = Vec::new(); + + // Parse superscript and subscript (can appear in any order) + while let Some(token) = tokens.front() { + match token { + Token::Superscript => { + tokens.pop_front(); // consume ^ + super_script = parse_script_content(tokens)?; + } + Token::Subscript => { + tokens.pop_front(); // consume _ + sub_script = parse_script_content(tokens)?; + } + _ => break, + } + } + + Ok((super_script, sub_script)) +} + +fn parse_script_content(tokens: &mut VecDeque) -> Result, String> { + if let Some(token) = tokens.front() { + match token { + Token::LeftBrace => { + // Multi-character script content in braces + parse_braced_content(tokens) + } + Token::Number(text) | Token::Letter(text) => { + // Single character/number script content + let text = text.clone(); + tokens.pop_front(); + Ok(vec![ParsedObject::Var { + text, + super_script: vec![], + sub_script: vec![], + }]) + } + Token::Function(name) => { + // Function in script + let name = name.clone(); + tokens.pop_front(); + let func = parse_function_generic(name, tokens)?; + Ok(vec![func]) + } + _ => Err("Invalid script content".to_string()), + } + } else { + Err("Expected script content".to_string()) + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a4e212d..5ca5e2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #[allow(non_upper_case_globals)] mod fonts; -mod parser; +mod element; mod bitmap; mod consts; @@ -9,70 +9,131 @@ use std::{rc::Rc, time::Instant}; use fontdue::{layout::{CoordinateSystem, Layout, LayoutSettings}}; -use crate::{bitmap::Bitmap, parser::{KElement}}; +use crate::{bitmap::Bitmap, element::{KElement}}; fn main() -> Result<(), std::fmt::Error> { - let start = Instant::now(); + parse_test() +} - let mut rustex = RusTeX::new(TeXSettings { scale: 100. }); +fn parse_test() -> Result<(), std::fmt::Error> { + // Test the quadratic formula + // let tex_input = r"x=\frac{-b\pm\sqrt{b^2 - 4ac}}{2a}"; + let tex_input = &std::env::args().nth(1).unwrap(); - let element = KElement::LinearGroup(vec![ - KElement::Fraction( - Rc::new(KElement::LinearGroup(vec![ - KElement::Integer(123), - KElement::Text("*".to_string()), - KElement::Superscript( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Integer(2)) - ) - ])), - Rc::new(KElement::Integer(12)) - ), - // KElement::Integer(12), - // KElement::Integer(12), - KElement::Decimal(12.34), - KElement::Fraction( - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Integer(12))) - ), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Fraction( - Rc::new(KElement::Integer(123)), - Rc::new(KElement::Decimal(1234.5678)) - )) - )) - )) - )) - )) - )) - )), - ), - ]); + match KElement::parse(tex_input) { + Ok(result) => { + let mut rustex = RusTeX::new(TeXSettings { scale: 100. }); + rustex.rasterize(result).print(); + } + Err(e) => println!("Error: {}", e), + } - // rustex.add_text(&TextStyle::new("testi12345ng! e^23", 100.0, 0)); - let bitmap = rustex.rasterize(element); - - println!("Rasterizing time: {:?}", start.elapsed()); + // println!("Parsing: {}", tex_input); - bitmap.print(); - - // print_bitmap(&bitmap, width, height); + // match parser::parse(tex_input) { + // Ok(result) => { + // println!("Parsed result:"); + // for (i, obj) in result.iter().enumerate() { + // println!(" [{}]: {:#?}", i, obj); + // } + // } + // Err(e) => { + // println!("Error parsing TeX: {}", e); + // } + // } + // // Test some other examples + // let examples = vec![ + // "a^2", + // r"\frac{12.34a+b}{2}", + // r"\sqrt{x}", + // "x_{a^2}^{2^a}", + // "(a+b)^2", + // ]; + + // for example in examples { + // println!("\n--- Parsing: {} ---", example); + // match parser.parse(example) { + // Ok(result) => { + // for obj in result { + // println!("{:#?}", obj); + // } + // } + // Err(e) => println!("Error: {}", e), + // } + // } + Ok(()) } +// fn raster_test() -> Result<(), std::fmt::Error> { +// let start = Instant::now(); + +// let mut rustex = RusTeX::new(TeXSettings { scale: 100. }); + +// let element = + +// KElement::Superscript(Rc::new( +// KElement::LinearGroup(vec![ +// KElement::Fraction( +// Rc::new(KElement::LinearGroup(vec![ +// KElement::Integer(123), +// KElement::Text("*".to_string()), +// KElement::Superscript( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(5)), +// Rc::new(KElement::Integer(2))) +// ), +// ) +// ])), +// Rc::new(KElement::Integer(12)) +// ), +// // KElement::Integer(12), +// // KElement::Integer(12), +// KElement::Decimal(12.34), +// KElement::Fraction( +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Integer(12))) +// ), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Fraction( +// Rc::new(KElement::Integer(123)), +// Rc::new(KElement::Decimal(1234.5678)) +// )) +// )) +// )) +// )) +// )) +// )) +// )), +// ), +// ])), Rc::new(KElement::Decimal(12.34))); + +// // rustex.add_text(&TextStyle::new("testi12345ng! e^23", 100.0, 0)); +// let bitmap = rustex.rasterize(element); + +// println!("Rasterizing time: {:?}", start.elapsed()); + +// bitmap.print(); + +// // print_bitmap(&bitmap, width, height); + +// Ok(()) +// } + struct RusTeX { pub settings: TeXSettings, pub layout: Layout