use crate::{error, AsPaletteColor, Result, XmlWritable, XmlWriter};
use decorum::R64;
use indexmap::{indexmap, IndexMap, IndexSet};
use rgb::RGB8;
use std::cell::RefCell;
use std::rc::Rc;
const SIZE_MIN: f64 = 1.0f64;
const SIZE_MAX: f64 = 409.0f64;
const SIZE_DEFAULT: f64 = 11.0f64;
const NAME_DEFAULT: &str = "Calibri";
const FONT_FAMILY_DEFAULT: u8 = 2;
#[derive(PartialEq, Eq, Clone, Hash, Debug, Default)]
pub struct Font {
pub name: Option<String>,
pub size: FontSize,
pub color: Option<RGB8>,
pub bold: bool,
pub italic: bool,
pub underline: Option<Underline>,
pub strikeout: bool,
pub script: Option<Script>,
pub outline: bool,
pub shadow: bool,
}
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub struct FontSize {
value: R64,
}
impl Default for FontSize {
fn default() -> Self {
FontSize {
value: R64::from(SIZE_DEFAULT),
}
}
}
impl XmlWritable for FontSize {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
w.empty_tag_with_attrs(
"sz",
indexmap! { "val" => format!("{}", self.value) },
)
}
}
impl FontSize {
pub fn new(size: f64) -> Result<Self> {
Ok(FontSize {
value: Self::build_value(size)?,
})
}
pub fn set(&mut self, size: f64) -> Result<()> {
self.value = Self::build_value(size)?;
Ok(())
}
pub fn get(&self) -> f64 {
self.value.into_inner()
}
fn build_value(size: f64) -> Result<R64> {
if size < SIZE_MIN || size > SIZE_MAX {
error::FontSizeOutOfRange {
min: SIZE_MIN,
max: SIZE_MAX,
size,
}
.fail()?
} else {
Ok(R64::from(size))
}
}
}
#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub enum Underline {
Single,
Double,
SingleAccounting,
DoubleAccounting,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Script {
SuperScript,
SubScript,
}
#[derive(Default, Debug)]
struct Inner {
fonts: IndexSet<Font>,
}
#[derive(Clone, Debug)]
pub(crate) struct Fonts {
inner: Rc<RefCell<Inner>>,
}
impl Default for Fonts {
fn default() -> Self {
let mut inner = Inner::default();
inner.fonts.insert(Font::default());
Fonts {
inner: Rc::new(RefCell::new(inner)),
}
}
}
impl Font {
pub fn set_name<S: Into<String>>(&mut self, name: S) {
self.name = Some(name.into());
}
}
impl Fonts {
pub fn create_or_get_index(
&mut self,
font: Option<&Font>,
) -> Option<usize> {
let mut inner = self.inner.borrow_mut();
font.map(|f| inner.fonts.insert_full(f.clone()).0)
}
}
impl XmlWritable for Fonts {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
self.inner.borrow().write_xml(w)
}
}
impl XmlWritable for Inner {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if !self.fonts.is_empty() {
let tag = "fonts";
let attrs = indexmap! {
"count" => format!("{}", self.fonts.len()),
};
w.start_tag_with_attrs(tag, attrs)?;
for font in self.fonts.iter() {
font.write_xml(w)?;
}
w.end_tag(tag)?;
}
Ok(())
}
}
impl XmlWritable for Font {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
let tag = "font";
w.start_tag(tag)?;
self.write_bold(w)?;
self.write_italic(w)?;
self.write_strikeout(w)?;
self.write_outline(w)?;
self.write_shadow(w)?;
self.write_underline(w)?;
self.write_script(w)?;
self.write_size(w)?;
self.write_color(w)?;
self.write_name(w)?;
self.write_family(w)?;
self.write_scheme(w)?;
w.end_tag(tag)?;
Ok(())
}
}
impl Font {
fn write_bold<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if self.bold {
w.empty_tag("b")?;
}
Ok(())
}
fn write_italic<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if self.italic {
w.empty_tag("i")?;
}
Ok(())
}
fn write_strikeout<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if self.strikeout {
w.empty_tag("strike")?;
}
Ok(())
}
fn write_outline<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if self.outline {
w.empty_tag("outline")?;
}
Ok(())
}
fn write_shadow<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if self.shadow {
w.empty_tag("shadow")?;
}
Ok(())
}
fn write_underline<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if let Some(ref underline) = self.underline {
underline.write_xml(w)?;
}
Ok(())
}
fn write_script<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if let Some(ref script) = self.script {
script.write_xml(w)?;
}
Ok(())
}
fn write_size<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
self.size.write_xml(w)
}
fn write_color<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
if let Some(color) = self.color {
w.empty_tag_with_attrs(
"color",
indexmap! {
"rgb" => color.as_palette_color()
},
)?;
}
Ok(())
}
fn write_name<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
let name = self
.name
.as_ref()
.map(|s| s.as_str())
.unwrap_or(NAME_DEFAULT);
let attrs = indexmap! {"val" => name };
w.empty_tag_with_attrs("name", attrs)
}
fn write_family<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
w.empty_tag_with_attrs(
"family",
indexmap! {
"val" => format!("{}", FONT_FAMILY_DEFAULT)
},
)
}
fn write_scheme<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
Ok(())
}
}
impl Underline {
fn get_xml_attrs(&self) -> IndexMap<&'static str, &'static str> {
match self {
Underline::Single => indexmap! {},
Underline::Double => indexmap! {"val" => "double"},
Underline::SingleAccounting => indexmap! {
"val" => "singleAccounting",
},
Underline::DoubleAccounting => indexmap! {
"val" => "doubleAccounting",
},
}
}
}
impl XmlWritable for Underline {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
w.empty_tag_with_attrs("u", self.get_xml_attrs())
}
}
impl Script {
fn get_xml_attrs(&self) -> IndexMap<&'static str, &'static str> {
match self {
Script::SuperScript => indexmap! { "val" => "superscript"},
Script::SubScript => indexmap! { "val" => "subscript"},
}
}
}
impl XmlWritable for Script {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()> {
w.empty_tag_with_attrs("vertAlign", self.get_xml_attrs())
}
}