use super::package::Result;
use super::parts::chp::{CharacterProperties, UnderlineStyle, VerticalPosition};
#[derive(Debug, Clone)]
pub struct Paragraph {
text: String,
runs: Vec<Run>,
properties: super::parts::pap::ParagraphProperties,
}
impl Paragraph {
pub(crate) fn new(text: String) -> Self {
Self {
text: text.clone(),
runs: vec![Run::new(text, CharacterProperties::default())],
properties: super::parts::pap::ParagraphProperties::default(),
}
}
#[allow(unused)]
pub(crate) fn with_runs(runs: Vec<Run>) -> Self {
let text = runs.iter().map(|r| r.text.as_str()).collect::<String>();
Self {
text,
runs,
properties: super::parts::pap::ParagraphProperties::default(),
}
}
#[allow(dead_code)] pub(crate) fn with_properties(
text: String,
properties: super::parts::pap::ParagraphProperties,
) -> Self {
Self {
text,
runs: Vec::new(),
properties,
}
}
pub fn text(&self) -> Result<&str> {
Ok(&self.text)
}
pub fn runs(&self) -> Result<Vec<Run>> {
Ok(self.runs.clone())
}
pub(crate) fn set_runs(&mut self, runs: Vec<Run>) {
self.runs = runs;
}
pub(crate) fn set_properties(&mut self, properties: super::parts::pap::ParagraphProperties) {
self.properties = properties;
}
pub fn properties(&self) -> &super::parts::pap::ParagraphProperties {
&self.properties
}
pub fn formulas_as_latex(&self) -> Result<Vec<String>> {
let mut formulas = Vec::new();
for run in &self.runs {
if let Some(latex) = run.formula_as_latex()? {
formulas.push(latex);
}
}
Ok(formulas)
}
pub fn has_formulas(&self) -> bool {
self.runs.iter().any(|r| r.has_mtef_formula())
}
}
#[derive(Debug, Clone)]
pub struct Run {
text: String,
properties: CharacterProperties,
#[cfg(feature = "formula")]
mtef_formula_ast: Option<Vec<crate::formula::MathNode<'static>>>,
#[cfg(not(feature = "formula"))]
mtef_formula_ast: Option<Vec<()>>,
}
impl Run {
pub(crate) fn new(text: String, properties: CharacterProperties) -> Self {
Self {
text,
properties,
mtef_formula_ast: None,
}
}
#[cfg(feature = "formula")]
pub(crate) fn with_mtef_formula(
text: String,
properties: CharacterProperties,
mtef_ast: Vec<crate::formula::MathNode<'static>>,
) -> Self {
Self {
text,
properties,
mtef_formula_ast: Some(mtef_ast),
}
}
#[cfg(not(feature = "formula"))]
pub(crate) fn with_mtef_formula(
text: String,
properties: CharacterProperties,
_mtef_ast: Vec<()>,
) -> Self {
Self {
text,
properties,
mtef_formula_ast: None,
}
}
pub fn text(&self) -> Result<&str> {
Ok(&self.text)
}
pub fn bold(&self) -> Option<bool> {
self.properties.is_bold
}
pub fn italic(&self) -> Option<bool> {
self.properties.is_italic
}
pub fn underline(&self) -> Option<bool> {
match self.properties.underline {
UnderlineStyle::None => None,
_ => Some(true),
}
}
pub fn underline_style(&self) -> UnderlineStyle {
self.properties.underline
}
pub fn strikethrough(&self) -> Option<bool> {
self.properties.is_strikethrough
}
pub fn font_size(&self) -> Option<u16> {
self.properties.font_size
}
pub fn color(&self) -> Option<(u8, u8, u8)> {
self.properties.color
}
pub fn is_superscript(&self) -> bool {
self.properties.vertical_position == VerticalPosition::Superscript
}
pub fn is_subscript(&self) -> bool {
self.properties.vertical_position == VerticalPosition::Subscript
}
pub fn small_caps(&self) -> Option<bool> {
self.properties.is_small_caps
}
pub fn all_caps(&self) -> Option<bool> {
self.properties.is_all_caps
}
pub fn properties(&self) -> &CharacterProperties {
&self.properties
}
pub fn has_mtef_formula(&self) -> bool {
self.mtef_formula_ast.is_some()
}
#[cfg(feature = "formula")]
pub fn mtef_formula_ast(&self) -> Option<&Vec<crate::formula::MathNode<'static>>> {
self.mtef_formula_ast.as_ref()
}
#[cfg(not(feature = "formula"))]
pub fn mtef_formula_ast(&self) -> Option<&Vec<()>> {
self.mtef_formula_ast.as_ref()
}
#[cfg(feature = "formula")]
pub fn mtef_formula_ast_mut(&mut self) -> &mut Option<Vec<crate::formula::MathNode<'static>>> {
&mut self.mtef_formula_ast
}
#[cfg(not(feature = "formula"))]
pub fn mtef_formula_ast_mut(&mut self) -> &mut Option<Vec<()>> {
&mut self.mtef_formula_ast
}
#[cfg(feature = "formula")]
pub fn formula_as_latex(&self) -> Result<Option<String>> {
if let Some(ast) = &self.mtef_formula_ast {
use crate::formula::LatexConverter;
let mut converter = LatexConverter::new();
match converter.convert_nodes(ast) {
Ok(latex) => Ok(Some(latex.to_string())),
Err(e) => {
Ok(Some(format!("[Formula conversion error: {}]", e)))
}
}
} else {
Ok(None)
}
}
#[cfg(not(feature = "formula"))]
pub fn formula_as_latex(&self) -> Result<Option<String>> {
if self.mtef_formula_ast.is_some() {
Ok(Some("[Formula support disabled - enable 'formula' feature]".to_string()))
} else {
Ok(None)
}
}
pub fn is_ole_object(&self) -> bool {
self.properties.is_ole2
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_paragraph_text() {
let para = Paragraph::new("Hello, World!".to_string());
assert_eq!(para.text().unwrap(), "Hello, World!");
}
#[test]
fn test_run_text() {
let run = Run::new("Test text".to_string(), CharacterProperties::default());
assert_eq!(run.text().unwrap(), "Test text");
assert_eq!(run.bold(), None);
assert_eq!(run.italic(), None);
}
#[test]
#[allow(clippy::field_reassign_with_default)]
fn test_run_with_formatting() {
let mut props = CharacterProperties::default();
props.is_bold = Some(true);
props.is_italic = Some(true);
props.font_size = Some(24);
let run = Run::new("Formatted text".to_string(), props);
assert!(run.bold().unwrap_or(false));
assert!(run.italic().unwrap_or(false));
assert_eq!(run.font_size(), Some(24));
}
}