Start working on parsing

This commit is contained in:
Michael Mikovsky
2025-09-23 13:08:30 -06:00
parent 8c23fa25b1
commit 0ef040d5c3
9 changed files with 657 additions and 99 deletions
Generated
+45
View File
@@ -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]]
+1
View File
@@ -7,3 +7,4 @@ edition = "2024"
fontdue = "0.9.3"
icy_sixel = "0.1.3"
lazy_static = "1.5.0"
regex = "1.11.2"
+2 -2
View File
@@ -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;
+81
View File
@@ -0,0 +1,81 @@
use std::rc::Rc;
use crate::element::{text_parser::{self, ParsedObject}, KElement};
impl KElement {
pub fn parse(input: &str) -> Result<KElement, String> {
let elems = text_parser::parse(input)?;
Self::parse_object(&elems)
}
pub fn parse_object(elems: &Vec<ParsedObject>) -> Result<KElement, String> {
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));
}
}
+33
View File
@@ -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<Vec<ParsedObject>>) -> Result<KElement, String> {
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))
}
}
}
+22
View File
@@ -0,0 +1,22 @@
use std::rc::Rc;
mod rasterizer;
mod text_parser;
mod element_parser;
mod functions;
pub enum KElement {
LinearGroup(Vec<KElement>),
Integer(i64),
Decimal(f64),
Text(String),
Fraction {
upper: Rc<KElement>,
lower: Rc<KElement>
},
SuperSub{
inner: Rc<KElement>,
upper: Option<Rc<KElement>>,
lower: Option<Rc<KElement>>
},
}
+50 -42
View File
@@ -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<KElement>),
Integer(i64),
Decimal(f64),
Text(String),
Fraction(Rc<KElement>, Rc<KElement>),
Superscript(Rc<KElement>, Rc<KElement>),
}
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!()
}
}
}
+307
View File
@@ -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<Vec<ParsedObject>>,
super_script: Vec<ParsedObject>,
sub_script: Vec<ParsedObject>,
},
Var {
text: String,
super_script: Vec<ParsedObject>,
sub_script: Vec<ParsedObject>,
},
Operator {
text: String,
},
Parenthesis {
inner: Vec<ParsedObject>,
parenthesis_type: PerenthesisType,
super_script: Vec<ParsedObject>,
sub_script: Vec<ParsedObject>,
},
}
#[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<Vec<ParsedObject>, 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<Vec<Token>, String> {
let mut tokens = Vec::new();
let mut pos = 0;
let input_chars: Vec<char> = 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<Token>) -> Result<Vec<ParsedObject>, 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<Token>) -> Result<ParsedObject, String> {
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<Token>) -> Result<ParsedObject, String> {
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<Token>) -> Result<ParsedObject, String> {
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<Token>) -> Result<Vec<ParsedObject>, 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<Token>) -> Result<(Vec<ParsedObject>, Vec<ParsedObject>), 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<Token>) -> Result<Vec<ParsedObject>, 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())
}
}
+116 -55
View File
@@ -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