use crate::dml::color::ColorFormat;
use crate::dml::fill::FillFormat;
use crate::enums::text::MsoVerticalAnchor;
use crate::text::TextFrame;
use crate::units::Emu;
use crate::xml_util::WriteXml;
#[derive(Debug, Clone, PartialEq)]
pub struct Cell {
pub text_frame: TextFrame,
pub fill: Option<FillFormat>,
pub grid_span: u32,
pub row_span: u32,
pub h_merge: bool,
pub v_merge: bool,
pub margin_left: Option<Emu>,
pub margin_right: Option<Emu>,
pub margin_top: Option<Emu>,
pub margin_bottom: Option<Emu>,
pub borders: CellBorders,
pub vertical_anchor: Option<MsoVerticalAnchor>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct CellBorders {
pub left: Option<CellBorder>,
pub right: Option<CellBorder>,
pub top: Option<CellBorder>,
pub bottom: Option<CellBorder>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CellBorder {
pub color: ColorFormat,
pub width: Emu,
}
impl Cell {
#[must_use]
pub fn new() -> Self {
Self {
text_frame: TextFrame::new(),
fill: None,
grid_span: 1,
row_span: 1,
h_merge: false,
v_merge: false,
margin_left: Some(Emu(91440)),
margin_right: Some(Emu(91440)),
margin_top: Some(Emu(45720)),
margin_bottom: Some(Emu(45720)),
borders: CellBorders::default(),
vertical_anchor: None,
}
}
#[must_use]
pub fn text(&self) -> String {
self.text_frame.text()
}
pub fn set_text(&mut self, text: &str) {
self.text_frame.set_text(text);
}
pub fn merge_with(&mut self, span_width: u32, span_height: u32) {
self.grid_span = span_width.max(1);
self.row_span = span_height.max(1);
}
#[must_use]
pub const fn is_merge_origin(&self) -> bool {
self.grid_span > 1 || self.row_span > 1
}
#[must_use]
pub const fn is_spanned(&self) -> bool {
self.h_merge || self.v_merge
}
pub fn split(&mut self) {
self.grid_span = 1;
self.row_span = 1;
self.h_merge = false;
self.v_merge = false;
}
pub fn write_xml<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
w.write_str("<a:tc")?;
if self.grid_span > 1 {
write!(w, r#" gridSpan="{}""#, self.grid_span)?;
}
if self.row_span > 1 {
write!(w, r#" rowSpan="{}""#, self.row_span)?;
}
if self.h_merge {
w.write_str(r#" hMerge="1""#)?;
}
if self.v_merge {
w.write_str(r#" vMerge="1""#)?;
}
w.write_char('>')?;
self.text_frame.write_xml_with_tag(w, "a:txBody")?;
w.write_str("<a:tcPr")?;
if let Some(l) = self.margin_left {
write!(w, r#" marL="{l}""#)?;
}
if let Some(r) = self.margin_right {
write!(w, r#" marR="{r}""#)?;
}
if let Some(t) = self.margin_top {
write!(w, r#" marT="{t}""#)?;
}
if let Some(b) = self.margin_bottom {
write!(w, r#" marB="{b}""#)?;
}
if let Some(anchor) = self.vertical_anchor {
write!(w, r#" anchor="{}""#, anchor.to_xml_str())?;
}
let has_tc_children = self.fill.is_some() || self.borders.has_any();
if has_tc_children {
w.write_char('>')?;
if let Some(ref border) = self.borders.left {
write_border_xml(w, "a:lnL", border)?;
}
if let Some(ref border) = self.borders.right {
write_border_xml(w, "a:lnR", border)?;
}
if let Some(ref border) = self.borders.top {
write_border_xml(w, "a:lnT", border)?;
}
if let Some(ref border) = self.borders.bottom {
write_border_xml(w, "a:lnB", border)?;
}
if let Some(ref fill) = self.fill {
fill.write_xml(w)?;
}
w.write_str("</a:tcPr>")?;
} else {
w.write_str("/>")?;
}
w.write_str("</a:tc>")
}
#[must_use]
pub fn to_xml_string(&self) -> String {
let mut s = String::with_capacity(200);
self.write_xml(&mut s)
.unwrap_or_else(|_| unreachable!("fmt::Write for String is infallible"));
s
}
}
impl Default for Cell {
fn default() -> Self {
Self::new()
}
}
impl CellBorders {
#[must_use]
pub const fn has_any(&self) -> bool {
self.left.is_some() || self.right.is_some() || self.top.is_some() || self.bottom.is_some()
}
}
fn write_border_xml<W: std::fmt::Write>(
w: &mut W,
tag: &str,
border: &CellBorder,
) -> std::fmt::Result {
write!(w, r#"<{} w="{}">"#, tag, border.width.0)?;
w.write_str("<a:solidFill>")?;
border.color.write_xml(w)?;
w.write_str("</a:solidFill>")?;
write!(w, "</{tag}>")
}