#[cfg(feature = "output")]
use crate::errors::LatexError;
use crate::{basetypes::AST, Values};
#[cfg(feature = "output")]
pub fn png_from_latex<S: Into<String>>(latex: String, height: u32, line_color: S) -> Result<Vec<u8>, LatexError> {
use resvg::{render, tiny_skia::Pixmap, usvg::{Options, Transform, Tree}};
let svg = svg_from_latex(latex, line_color)?;
let tree = Tree::from_str(&svg, &Options::default())?;
let dest_width = ((tree.size().width()/tree.size().height()) * height as f32).ceil();
let width_scale = dest_width/tree.size().width();
let height_scale = height as f32/tree.size().height();
let mut pixmap = Pixmap::new(dest_width as u32, height as u32).unwrap();
render(&tree, Transform::from_row(width_scale, 0., 0., height_scale, 0., 0.), &mut pixmap.as_mut());
Ok(pixmap.encode_png().ok().unwrap())
}
#[cfg(feature = "output")]
pub fn svg_from_latex<S: Into<String>>(latex: String, line_color: S) -> Result<String, LatexError> {
use mathjax_svg::convert_to_svg;
let mut svg = convert_to_svg(latex)?;
svg = svg.replace("currentColor", &line_color.into());
Ok(svg)
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Step {
Calc{
term: AST,
result: Values,
variable_save: Option<String>
},
Fun{
term: AST,
inputs: Vec<String>,
name: String
}
}
impl Step {
pub fn as_latex_with_tag(&self, equation_number: i32) -> String {
match self {
Step::Calc{term, result, variable_save} => {
let mut aligner = "&";
let mut latex = "".to_string();
if variable_save.is_some() {
latex += &format!("{} &= ", variable_save.clone().unwrap());
aligner = "";
}
let expression = term.as_latex();
let res = result.as_latex();
if expression != res {
latex += &format!("{} {}= {} \\tag{{{}}}\\label{{eq:{}}} \\\\ \\\\ \n", expression, aligner, res, equation_number, equation_number);
} else {
latex += &format!("{} \\tag{{{}}}\\label{{eq:{}}} \\\\ \\\\ \n", expression, equation_number, equation_number);
}
return latex;
},
Step::Fun{term, inputs, name} => {
return term.as_latex_at_fun(name, inputs.iter().collect(), true) + &format!(" \\tag{{{}}}\\label{{eq:{}}} \\\\ \\\\ \n", equation_number, equation_number);
}
}
}
pub fn as_latex(&self) -> String {
match self {
Step::Calc{term, result, variable_save} => {
let mut aligner = "&";
let mut latex = "".to_string();
if variable_save.is_some() {
latex += &format!("{} &= ", variable_save.clone().unwrap());
aligner = "";
}
let expression = term.as_latex();
let res = result.as_latex();
if expression != res {
latex += &format!("{} {}= {}", expression, aligner, res);
} else {
latex += &format!("{}", expression);
}
return latex;
},
Step::Fun{term, inputs, name} => return term.as_latex_at_fun(name, inputs.iter().collect(), true)
}
}
pub fn as_latex_inline(&self) -> String {
match self {
Step::Calc{term, result, variable_save} => {
let mut latex = "".to_string();
if variable_save.is_some() {
latex += &format!("{} = ", variable_save.clone().unwrap());
}
let expression = term.as_latex();
let res = result.as_latex();
if expression != res {
latex += &format!("{} = {}", expression, res);
} else {
latex += &format!("{}", expression);
}
return latex;
},
Step::Fun{term, inputs, name} => return term.as_latex_at_fun(name, inputs.iter().collect(), true)
}
}
}
#[cfg(feature = "output")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ExportType {
Pdf,
Tex
}
#[cfg(feature = "output")]
pub fn export_history(history: Vec<Step>, export_type: ExportType) -> Result<Vec<u8>, LatexError> {
let mut output_string = "\\documentclass[12pt, letterpaper]{article}\n\\usepackage{amsmath}\n\\usepackage[margin=1in]{geometry}\n\\allowdisplaybreaks\n\\begin{document}\n\\begin{align*}\n".to_string();
for (i, s) in history.iter().enumerate() {
output_string += &s.as_latex_with_tag(i as i32+1);
}
output_string += "\\end{align*}\n\\end{document}";
match export_type {
ExportType::Pdf => {
let pdf = tectonic::latex_to_pdf(output_string)?;
return Ok(pdf.to_vec());
},
ExportType::Tex => {
return Ok(output_string.into_bytes());
},
}
}