typer 0.1.1

Typer - simple text rendering library, with some additional features.
Documentation
extern crate rusttype;

use img_buffer::{ImgBuffer, ColorRGBA};
use self::rusttype::{ScaledGlyph};


#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum TextAlignHorizontal {
	Left,
	Right,
	Center,
	Justify,
}

#[derive(Debug)]
pub enum FormatChunks {
	Chunk(FormatChunk),
	String(String),
}


#[derive(Debug)]
pub struct RenderChunk {
	pub line_height: f32,
	pub color: ColorRGBA,
}


#[derive(Debug)]
pub struct Layout<'a> {
	pub blocks: Vec<(FormatBlock, RenderBlock<'a>)>,
	pub width: f32,
	pub height: f32,
	pub x: f32,
	pub y: f32,
}

impl <'a> Layout<'a> {

	pub fn calk_view(&mut self) {
		let mut width = - std::f32::MAX;
		let mut height = - std::f32::MAX;
		let mut x = - std::f32::MAX;
		let mut y = - std::f32::MAX;

		self.blocks
			.iter()
			.for_each(|(_, block)| {
				x = x.max(block.x);
				y = y.max(block.y);
				width = width.max(block.x + block.width);
				height = height.max(block.y + block.height);
			});

		self.width = width;
		self.height = height;
		self.x = x;
		self.y = y;
	}


	pub fn create_buffer(&mut self, color: &[u8; 4]) -> Option<ImgBuffer> {
		if self.width < 1.0 && self.height < 1.00 {return None};
		Some(ImgBuffer::new(self.width.ceil() as usize, self.height.ceil() as usize, color))
	}
}


#[derive(Debug)]
pub struct FormatChunk {
	pub font_size: usize,
	pub line_height: f32,
	pub color: ColorRGBA,
	pub font: Option<String>,
	pub chunks: Vec<FormatChunks>,
}


impl FormatChunk {

	pub fn new() -> Self {
		Self {
			font_size: 10,
			line_height: 1.0,
			color: [0, 0, 0, 255],
			font: None,
			chunks: Vec::new(),
		}
	}

	pub fn set_attribute(&mut self, key: &str, val: &str) {
		match key {
			"font-size" 	=> { self.font_size = val.parse::<usize>().unwrap() }
			"line-height" 	=> { self.line_height = val.parse::<f32>().unwrap() }
			"font" 			=> { self.font = Some(val.to_string()) }
			"color" 			=> {
				let err = &format!("wrong color: {}", val);
				if val.starts_with('#') && val.len() == 7 {
					self.color = [
						u8::from_str_radix(val.get(1..3).expect(err), 16).expect(err),
						u8::from_str_radix(val.get(3..5).expect(err), 16).expect(err),
						u8::from_str_radix(val.get(5..).expect(err), 16).expect(err),
						255
					];
				} else if val.starts_with('#') && val.len() == 9 {
					self.color = [
						u8::from_str_radix(val.get(1..3).expect(err), 16).expect(err),
						u8::from_str_radix(val.get(3..5).expect(err), 16).expect(err),
						u8::from_str_radix(val.get(5..7).expect(err), 16).expect(err),
						u8::from_str_radix(val.get(7..).expect(err), 16).expect(err)
					];
				} else {
					println!("wrong value '{}' for attribute: '{}' => #xxxxxx or #xxxxxxxx", val, key);
				}
			}
			_ => {
				println!("unknown attribute: '{}'", key);
			}
		}
	}


	pub fn new_empty(&self) -> Self {
		Self {
			font_size: self.font_size,
			line_height: self.line_height,
			color: self.color,
			font: self.font.clone(),
			chunks: Vec::new(),
		}
	}


	pub fn iter(&self) -> FormatChunkIter {
		FormatChunkIter{
			index: 0,
			chunk: self,
			sub_iter: None,
		}
	}

	pub fn get_render_chunk (&self) -> RenderChunk {
		RenderChunk{
			line_height: self.line_height,
			color: self.color,
		}
	}
}


#[derive(Debug)]
pub struct FormatBlock {
	pub text_align: TextAlignHorizontal,
	pub break_word: bool,
	pub width: f32,
	pub height: f32,
	pub x: f32,
	pub y: f32,
	pub chunk: FormatChunk,
}


impl FormatBlock {

	pub fn new() -> Self {
		Self {
			text_align: TextAlignHorizontal::Left,
			break_word: false,
			width: 0.0,
			height: 0.0,
			x: 0.0,
			y: 0.0,
			chunk: FormatChunk::new(),
		}
	}

	pub fn set_attribute(&mut self, key: &str, val: &str) {
		match key {
			"text-align" 			=> {
				match val {
					"left"|"LEFT" 		=> { self.text_align = TextAlignHorizontal::Left }
					"right"|"RIGHT" 	=> { self.text_align = TextAlignHorizontal::Right }
					"center"|"CENTER" 	=> { self.text_align = TextAlignHorizontal::Center }
					"justify"|"JUSTIFY" => { self.text_align = TextAlignHorizontal::Justify }
					_ => { println!("unknown value '{}' for attribute: '{}'", val, key); }
				}
			}

			"width" 		=> { self.width = val.parse::<f32>().unwrap().abs() }
			"height" 		=> { self.height = val.parse::<f32>().unwrap().abs() }
			"x" 			=> { self.x = val.parse::<f32>().unwrap() }
			"y" 			=> { self.y = val.parse::<f32>().unwrap() }
			"break_word" 		=> {
				match val {
					"true"|"TRUE"|"1"|"yes" 	=> { self.break_word = true }
					"false"|"FALSE"|"0"|"no" 	=> { self.break_word = false }
					_ => { println!("unknown value '{}' for attribute: '{}'", val, key); }
				}
			}
			_ => {
				println!("unknown attribute: '{}'", key);
			}
		}
	}


	pub fn new_empty(&self) -> Self {
		let res = Self {
			break_word: self.break_word,
			width: self.width,
			height: self.height,
			x: self.x,
			y: self.y,
			text_align: self.text_align,
			chunk: self.chunk.new_empty(),
		};

		res
	}

	pub fn to_render_block <'a> (&self) -> RenderBlock<'a> {
		let mut b = RenderBlock {
			text_align: self.text_align,
			width: self.width,
			height: self.height,
			x: self.x,
			y: self.y,
			lines: Vec::new(),
		};
		b.add_line();
		b
	}
}



#[derive(Debug)]
pub struct Line<'a> {
	pub width: f32,
	pub descent: f32,
	pub height: f32,
	pub chars_width: f32,
	pub force_break: bool,
	pub glyphs: Vec<(ScaledGlyph<'a>, RenderChunk, char, f32)>,
}

impl <'a> Line<'a> {
	pub fn new() -> Self {
		Self {
			width: 0.0,
			descent: 0.0,
			height: 0.0,
			chars_width: 0.0,
			force_break: false,
			glyphs: Vec::new(),
		}
	}

}


#[derive(Debug)]
pub struct RenderBlock<'a> {
	pub lines: Vec<Line<'a>>,
	pub text_align: TextAlignHorizontal,
	pub width: f32,
	pub height: f32,
	pub x: f32,
	pub y: f32,
}


impl <'a> RenderBlock<'a> {

	pub fn add_line(&mut self) {
		self.lines.push(Line::new());
	}

	pub fn get_last_line(&mut self) -> &mut Line<'a> {
		self.lines.last_mut().unwrap()
	}

	pub fn get_prev_line(&mut self) -> &mut Line<'a> {
		let i = self.lines.len();
		&mut self.lines[i-2]
	}
}


pub struct FormatChunkIter<'a> {
	index: usize,
	chunk: &'a FormatChunk,
	sub_iter: Option<Box<FormatChunkIter<'a>>>,
}


impl <'a> Iterator for FormatChunkIter<'a> {
	type Item = (&'a FormatChunk, &'a str);

	fn next (&mut self) -> Option<Self::Item> {
		if self.chunk.chunks.len() == self.index {return None};
		
		if self.sub_iter.is_some() {
			let res = self.sub_iter.as_mut().unwrap().next();
			if res.is_none() {
				self.index += 1;
				self.sub_iter = None;
				return self.next();
			}
			return res;
		}

		match &self.chunk.chunks[self.index] {
			FormatChunks::String(s) => {
				self.index += 1;
				Some((self.chunk, s))
			}
			FormatChunks::Chunk(sub_iter) => {
				self.sub_iter = Some(Box::new(sub_iter.iter()));
				return self.next();
			}
		}
	}
}