#[derive(Debug, Clone)]
pub struct TextState {
pub font_name: Option<String>,
pub font_size: f64,
pub character_spacing: f64,
pub word_spacing: f64,
pub leading: f64,
pub horizontal_scaling: f64,
pub rise: f64,
pub rendering_mode: u8,
}
impl Default for TextState {
fn default() -> Self {
Self {
font_name: None,
font_size: 0.0,
character_spacing: 0.0,
word_spacing: 0.0,
leading: 0.0,
horizontal_scaling: 100.0,
rise: 0.0,
rendering_mode: 0,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct GraphicsState {
pub text_state: TextState,
}
#[derive(Debug)]
pub struct GraphicsStateStack {
stack: Vec<GraphicsState>,
current: GraphicsState,
}
impl GraphicsStateStack {
pub fn new() -> Self {
Self {
stack: Vec::new(),
current: GraphicsState::default(),
}
}
pub fn save(&mut self) {
self.stack.push(self.current.clone());
}
pub fn restore(&mut self) {
self.current = self.stack.pop().unwrap_or_default();
}
pub fn current(&self) -> &GraphicsState {
&self.current
}
pub fn current_mut(&mut self) -> &mut GraphicsState {
&mut self.current
}
pub fn depth(&self) -> usize {
self.stack.len()
}
}
impl Default for GraphicsStateStack {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_text_state() {
let ts = TextState::default();
assert_eq!(ts.font_name, None);
assert_eq!(ts.font_size, 0.0);
assert_eq!(ts.character_spacing, 0.0);
assert_eq!(ts.word_spacing, 0.0);
assert_eq!(ts.leading, 0.0);
assert_eq!(ts.horizontal_scaling, 100.0);
assert_eq!(ts.rise, 0.0);
}
#[test]
fn save_and_restore() {
let mut stack = GraphicsStateStack::new();
stack.current_mut().text_state.font_name = Some("F1".to_string());
stack.current_mut().text_state.font_size = 12.0;
stack.save();
assert_eq!(stack.depth(), 1);
stack.current_mut().text_state.font_name = Some("F2".to_string());
stack.current_mut().text_state.font_size = 24.0;
assert_eq!(stack.current().text_state.font_name, Some("F2".to_string()));
stack.restore();
assert_eq!(stack.depth(), 0);
assert_eq!(stack.current().text_state.font_name, Some("F1".to_string()));
assert_eq!(stack.current().text_state.font_size, 12.0);
}
#[test]
fn restore_empty_stack_resets_to_default() {
let mut stack = GraphicsStateStack::new();
stack.current_mut().text_state.font_size = 42.0;
stack.restore(); assert_eq!(stack.current().text_state.font_size, 0.0);
assert_eq!(stack.current().text_state.font_name, None);
}
#[test]
fn nested_save_restore() {
let mut stack = GraphicsStateStack::new();
stack.current_mut().text_state.font_name = Some("F1".to_string());
stack.save();
stack.current_mut().text_state.font_name = Some("F2".to_string());
stack.save();
stack.current_mut().text_state.font_name = Some("F3".to_string());
assert_eq!(stack.depth(), 2);
assert_eq!(stack.current().text_state.font_name, Some("F3".to_string()));
stack.restore();
assert_eq!(stack.current().text_state.font_name, Some("F2".to_string()));
stack.restore();
assert_eq!(stack.current().text_state.font_name, Some("F1".to_string()));
}
#[test]
fn text_state_operators() {
let mut stack = GraphicsStateStack::new();
let ts = &mut stack.current_mut().text_state;
ts.font_name = Some("F1".to_string());
ts.font_size = 12.0;
ts.character_spacing = 0.5;
ts.word_spacing = 1.0;
ts.leading = 14.0;
assert_eq!(stack.current().text_state.font_name, Some("F1".to_string()));
assert_eq!(stack.current().text_state.font_size, 12.0);
assert_eq!(stack.current().text_state.character_spacing, 0.5);
assert_eq!(stack.current().text_state.word_spacing, 1.0);
assert_eq!(stack.current().text_state.leading, 14.0);
}
}