use crate::types::{Color, Handle, LineWeight};
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TableFlowDirection {
#[default]
Down = 0,
Up = 1,
}
impl From<i16> for TableFlowDirection {
fn from(value: i16) -> Self {
match value {
1 => Self::Up,
_ => Self::Down,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum CellAlignment {
TopLeft = 1,
TopCenter = 2,
TopRight = 3,
MiddleLeft = 4,
#[default]
MiddleCenter = 5,
MiddleRight = 6,
BottomLeft = 7,
BottomCenter = 8,
BottomRight = 9,
}
impl From<i16> for CellAlignment {
fn from(value: i16) -> Self {
match value {
1 => Self::TopLeft,
2 => Self::TopCenter,
3 => Self::TopRight,
4 => Self::MiddleLeft,
5 => Self::MiddleCenter,
6 => Self::MiddleRight,
7 => Self::BottomLeft,
8 => Self::BottomCenter,
9 => Self::BottomRight,
_ => Self::MiddleCenter,
}
}
}
impl CellAlignment {
pub fn is_top(&self) -> bool {
matches!(self, Self::TopLeft | Self::TopCenter | Self::TopRight)
}
pub fn is_middle(&self) -> bool {
matches!(
self,
Self::MiddleLeft | Self::MiddleCenter | Self::MiddleRight
)
}
pub fn is_bottom(&self) -> bool {
matches!(
self,
Self::BottomLeft | Self::BottomCenter | Self::BottomRight
)
}
pub fn is_left(&self) -> bool {
matches!(self, Self::TopLeft | Self::MiddleLeft | Self::BottomLeft)
}
pub fn is_center(&self) -> bool {
matches!(
self,
Self::TopCenter | Self::MiddleCenter | Self::BottomCenter
)
}
pub fn is_right(&self) -> bool {
matches!(self, Self::TopRight | Self::MiddleRight | Self::BottomRight)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(i16)]
pub enum TableBorderType {
#[default]
Single = 1,
Double = 2,
}
impl From<i16> for TableBorderType {
fn from(value: i16) -> Self {
match value {
2 => Self::Double,
_ => Self::Single,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableCellStylePropertyFlags: i32 {
const NONE = 0x0;
const DATA_TYPE = 0x1;
const DATA_FORMAT = 0x2;
const ROTATION = 0x4;
const BLOCK_SCALE = 0x8;
const ALIGNMENT = 0x10;
const CONTENT_COLOR = 0x20;
const TEXT_STYLE = 0x40;
const TEXT_HEIGHT = 0x80;
const AUTO_SCALE = 0x100;
const BACKGROUND_COLOR = 0x200;
const MARGIN_LEFT = 0x400;
const MARGIN_TOP = 0x800;
const MARGIN_RIGHT = 0x1000;
const MARGIN_BOTTOM = 0x2000;
const CONTENT_LAYOUT = 0x4000;
const MERGE_ALL = 0x8000;
const FLOW_DIRECTION_BOTTOM_TO_TOP = 0x10000;
const MARGIN_HORIZONTAL_SPACING = 0x20000;
const MARGIN_VERTICAL_SPACING = 0x40000;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableBorderPropertyFlags: i32 {
const NONE = 0x0;
const LINE_WEIGHT = 0x1;
const LINE_TYPE = 0x2;
const COLOR = 0x4;
const VISIBILITY = 0x8;
const DOUBLE_LINE_SPACING = 0x10;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableStyleFlags: i16 {
const NONE = 0;
const TITLE_SUPPRESSED = 1;
const HEADER_SUPPRESSED = 2;
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableCellBorder {
pub property_flags: TableBorderPropertyFlags,
pub border_type: TableBorderType,
pub line_weight: LineWeight,
pub color: Color,
pub is_invisible: bool,
pub double_line_spacing: f64,
}
impl TableCellBorder {
pub fn new() -> Self {
TableCellBorder {
property_flags: TableBorderPropertyFlags::NONE,
border_type: TableBorderType::Single,
line_weight: LineWeight::ByBlock,
color: Color::ByBlock,
is_invisible: false,
double_line_spacing: 0.0,
}
}
pub fn invisible() -> Self {
TableCellBorder {
is_invisible: true,
..Self::new()
}
}
pub fn with_style(color: Color, line_weight: LineWeight) -> Self {
TableCellBorder {
color,
line_weight,
..Self::new()
}
}
}
impl Default for TableCellBorder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RowCellStyle {
pub text_style_name: String,
pub text_style_handle: Option<Handle>,
pub text_height: f64,
pub alignment: CellAlignment,
pub text_color: Color,
pub fill_color: Color,
pub fill_enabled: bool,
pub data_type: i32,
pub unit_type: i32,
pub format_string: String,
pub left_border: TableCellBorder,
pub right_border: TableCellBorder,
pub top_border: TableCellBorder,
pub bottom_border: TableCellBorder,
pub horizontal_inside_border: TableCellBorder,
pub vertical_inside_border: TableCellBorder,
}
impl RowCellStyle {
pub fn new() -> Self {
RowCellStyle {
text_style_name: "Standard".to_string(),
text_style_handle: None,
text_height: 0.18,
alignment: CellAlignment::MiddleCenter,
text_color: Color::ByBlock,
fill_color: Color::Index(7), fill_enabled: false,
data_type: 512,
unit_type: 0,
format_string: String::new(),
left_border: TableCellBorder::new(),
right_border: TableCellBorder::new(),
top_border: TableCellBorder::new(),
bottom_border: TableCellBorder::new(),
horizontal_inside_border: TableCellBorder::new(),
vertical_inside_border: TableCellBorder::new(),
}
}
pub fn data_row() -> Self {
RowCellStyle {
text_height: 0.18,
alignment: CellAlignment::MiddleCenter,
..Self::new()
}
}
pub fn header_row() -> Self {
RowCellStyle {
text_height: 0.25,
alignment: CellAlignment::TopCenter,
..Self::new()
}
}
pub fn title_row() -> Self {
RowCellStyle {
text_height: 0.25,
alignment: CellAlignment::TopCenter,
..Self::new()
}
}
pub fn set_all_borders(&mut self, border: TableCellBorder) {
self.left_border = border.clone();
self.right_border = border.clone();
self.top_border = border.clone();
self.bottom_border = border.clone();
self.horizontal_inside_border = border.clone();
self.vertical_inside_border = border;
}
pub fn set_all_borders_invisible(&mut self) {
self.set_all_borders(TableCellBorder::invisible());
}
}
impl Default for RowCellStyle {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableContentFormat {
pub property_override_flags: i32,
pub property_flags: i32,
pub value_data_type: i32,
pub value_unit_type: i32,
pub value_format_string: String,
pub rotation: f64,
pub block_scale: f64,
pub cell_alignment: i32,
pub content_color: Color,
pub text_style: Handle,
pub text_height: f64,
}
impl Default for TableContentFormat {
fn default() -> Self {
Self {
property_override_flags: 0,
property_flags: 0,
value_data_type: 512,
value_unit_type: 0,
value_format_string: String::new(),
rotation: 0.0,
block_scale: 1.0,
cell_alignment: 5,
content_color: Color::ByBlock,
text_style: Handle::NULL,
text_height: 0.18,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableGridFormat {
pub index_mask: i32,
pub border: TableCellBorder,
pub line_type: Handle,
}
impl Default for TableGridFormat {
fn default() -> Self {
Self {
index_mask: 0,
border: TableCellBorder::default(),
line_type: Handle::NULL,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableCellStyleData {
pub style_type: i32,
pub data_flags: i16,
pub property_override_flags: i32,
pub merge_flags: i32,
pub background_color: Color,
pub content_layout: i32,
pub content_format: TableContentFormat,
pub margin_override_flags: i16,
pub vertical_margin: f64,
pub horizontal_margin: f64,
pub bottom_margin: f64,
pub right_margin: f64,
pub horizontal_spacing: f64,
pub vertical_spacing: f64,
pub borders: Vec<TableGridFormat>,
}
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NamedTableCellStyle {
pub cell_style: TableCellStyleData,
pub id: i32,
pub style_type: i32,
pub name: String,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TableStyle {
pub handle: Handle,
pub owner_handle: Handle,
pub name: String,
pub description: String,
pub version: i16,
pub flow_direction: TableFlowDirection,
pub flags: TableStyleFlags,
pub horizontal_margin: f64,
pub vertical_margin: f64,
pub title_suppressed: bool,
pub header_suppressed: bool,
pub data_row_style: RowCellStyle,
pub header_row_style: RowCellStyle,
pub title_row_style: RowCellStyle,
pub modern_unknown_byte: u8,
pub modern_unknown_long1: i32,
pub modern_unknown_long2: i32,
pub modern_cell_style_handle: Handle,
pub modern_style: Option<NamedTableCellStyle>,
pub modern_overrides: Vec<(i32, NamedTableCellStyle)>,
pub annotative: bool,
#[cfg_attr(feature = "serde", serde(skip))]
pub raw_dxf_codes: Option<Vec<(i32, String)>>,
}
impl TableStyle {
pub const OBJECT_NAME: &'static str = "TABLESTYLE";
pub const SUBCLASS_MARKER: &'static str = "AcDbTableStyle";
pub const STANDARD: &'static str = "Standard";
pub fn new(name: &str) -> Self {
TableStyle {
handle: Handle::NULL,
owner_handle: Handle::NULL,
name: name.to_string(),
description: String::new(),
version: 0,
flow_direction: TableFlowDirection::Down,
flags: TableStyleFlags::NONE,
horizontal_margin: 0.06,
vertical_margin: 0.06,
title_suppressed: false,
header_suppressed: false,
data_row_style: RowCellStyle::data_row(),
header_row_style: RowCellStyle::header_row(),
title_row_style: RowCellStyle::title_row(),
modern_unknown_byte: 0,
modern_unknown_long1: 8,
modern_unknown_long2: 101,
modern_cell_style_handle: Handle::NULL,
modern_style: None,
modern_overrides: Vec::new(),
annotative: false,
raw_dxf_codes: None,
}
}
pub fn standard() -> Self {
Self::new(Self::STANDARD)
}
pub fn set_margins(&mut self, margin: f64) {
self.horizontal_margin = margin;
self.vertical_margin = margin;
}
pub fn set_all_text_heights(&mut self, height: f64) {
self.data_row_style.text_height = height;
self.header_row_style.text_height = height;
self.title_row_style.text_height = height;
}
pub fn set_all_text_styles(&mut self, name: &str, handle: Option<Handle>) {
self.data_row_style.text_style_name = name.to_string();
self.data_row_style.text_style_handle = handle;
self.header_row_style.text_style_name = name.to_string();
self.header_row_style.text_style_handle = handle;
self.title_row_style.text_style_name = name.to_string();
self.title_row_style.text_style_handle = handle;
}
pub fn set_all_text_colors(&mut self, color: Color) {
self.data_row_style.text_color = color;
self.header_row_style.text_color = color;
self.title_row_style.text_color = color;
}
pub fn set_all_fill_colors(&mut self, color: Color) {
self.data_row_style.fill_color = color;
self.header_row_style.fill_color = color;
self.title_row_style.fill_color = color;
}
pub fn set_all_fill_enabled(&mut self, enabled: bool) {
self.data_row_style.fill_enabled = enabled;
self.header_row_style.fill_enabled = enabled;
self.title_row_style.fill_enabled = enabled;
}
pub fn has_title_row(&self) -> bool {
!self.title_suppressed
}
pub fn has_header_row(&self) -> bool {
!self.header_suppressed
}
pub fn set_title_visible(&mut self, visible: bool) {
self.title_suppressed = !visible;
}
pub fn set_header_visible(&mut self, visible: bool) {
self.header_suppressed = !visible;
}
}
impl Default for TableStyle {
fn default() -> Self {
Self::standard()
}
}
const CELL_STYLE_MAP_KEY: &str = "ACAD_ROUNDTRIP_2008_TABLESTYLE_CELLSTYLEMAP";
const ROUNDTRIP_RECORD_KEY: &str = "ACAD_XREC_ROUNDTRIP";
const PRE2007_MARKER: &str = "ACAD_ROUNDTRIP_PRE2007_TABLESTYLE";
fn legacy_rows_mut(style: &mut TableStyle) -> [&mut RowCellStyle; 3] {
[
&mut style.data_row_style,
&mut style.title_row_style,
&mut style.header_row_style,
]
}
pub(crate) fn restore_table_style_roundtrip(document: &mut crate::document::CadDocument) {
use crate::objects::{DataObjectData, ObjectType, XRecordValue};
let styles: Vec<_> = document
.objects
.iter()
.filter_map(|(handle, object)| match object {
ObjectType::TableStyle(style) if style.modern_overrides.is_empty() => Some(*handle),
_ => None,
})
.collect();
for handle in styles {
let Some(dictionary) = document.extension_dictionary_handle(handle) else {
continue;
};
let Some(ObjectType::Dictionary(dictionary)) = document.objects.get(&dictionary) else {
continue;
};
let cells = dictionary
.get(CELL_STYLE_MAP_KEY)
.and_then(|map| match document.objects.get(&map) {
Some(ObjectType::DataObject(object)) => match &object.data {
DataObjectData::CellStyleMap(map) => Some(map.cells.clone()),
_ => None,
},
_ => None,
});
let mut row_types: [Option<(i32, i32, String)>; 3] = Default::default();
if let Some(Some(ObjectType::XRecord(record))) = dictionary
.get(ROUNDTRIP_RECORD_KEY)
.map(|record| document.objects.get(&record))
{
let mut entries = record
.entries
.iter()
.skip_while(|entry| entry.value.as_string() != Some(PRE2007_MARKER))
.skip(1)
.take_while(|entry| entry.code != 102);
for slot in &mut row_types {
let (Some(data_type), Some(unit_type), Some(format)) =
(entries.next(), entries.next(), entries.next())
else {
break;
};
if let (XRecordValue::Int32(data_type), XRecordValue::Int32(unit_type)) =
(&data_type.value, &unit_type.value)
{
*slot = Some((
*data_type,
*unit_type,
format.value.as_string().unwrap_or_default().to_string(),
));
}
}
}
let Some(ObjectType::TableStyle(style)) = document.objects.get_mut(&handle) else {
continue;
};
if let Some(cells) = cells {
style.modern_overrides = cells.into_iter().map(|cell| (cell.id, cell)).collect();
}
for (row, types) in legacy_rows_mut(style).into_iter().zip(row_types) {
if let Some((data_type, unit_type, format)) = types {
row.data_type = data_type;
row.unit_type = unit_type;
row.format_string = format;
}
}
}
}
pub(crate) fn store_table_style_roundtrip(document: &mut crate::document::CadDocument) {
use crate::objects::{
CellStyleMap, DataObject, DataObjectData, ObjectType, XRecordEntry, XRecordValue,
};
use crate::types::DxfVersion;
if document.version >= DxfVersion::AC1024 {
return;
}
let with_row_types = document.version < DxfVersion::AC1021;
let mut styles: Vec<_> = document
.objects
.iter()
.filter_map(|(handle, object)| match object {
ObjectType::TableStyle(style) if !style.modern_overrides.is_empty() => Some((
*handle,
style
.modern_overrides
.iter()
.map(|(_, cell)| cell.clone())
.collect::<Vec<_>>(),
[
&style.data_row_style,
&style.title_row_style,
&style.header_row_style,
]
.map(|row| (row.data_type, row.unit_type, row.format_string.clone())),
)),
_ => None,
})
.collect();
styles.sort_by_key(|style| style.0);
for (handle, cells, row_types) in styles {
let dictionary = document.ensure_extension_dictionary(handle);
let existing = match document.objects.get(&dictionary) {
Some(ObjectType::Dictionary(entries)) => entries.get(CELL_STYLE_MAP_KEY),
_ => None,
};
match existing.and_then(|map| document.objects.get_mut(&map)) {
Some(ObjectType::DataObject(DataObject {
data: DataObjectData::CellStyleMap(map),
..
})) => map.cells = cells,
_ => {
if !document.classes.contains("CELLSTYLEMAP") {
if let Some(class) = crate::classes::default_classes()
.into_iter()
.find(|class| class.dxf_name == "CELLSTYLEMAP")
{
document.classes.add_or_update(class);
}
}
let map = document.allocate_handle();
let mut object = DataObject::new(DataObjectData::CellStyleMap(CellStyleMap {
cells,
}));
object.handle = map;
object.owner = dictionary;
object.reactors = vec![dictionary];
document.objects.insert(map, ObjectType::DataObject(object));
if let Some(ObjectType::Dictionary(entries)) =
document.objects.get_mut(&dictionary)
{
entries.entries.retain(|(name, _)| name != CELL_STYLE_MAP_KEY);
entries.add_entry(CELL_STYLE_MAP_KEY, map);
}
}
}
if !with_row_types {
continue;
}
let record = document.ensure_xrecord(handle, ROUNDTRIP_RECORD_KEY);
let Some(ObjectType::XRecord(record)) = document.objects.get_mut(&record) else {
continue;
};
let start = record
.entries
.iter()
.position(|entry| entry.value.as_string() == Some(PRE2007_MARKER));
if let Some(start) = start {
let end = record.entries[start + 1..]
.iter()
.position(|entry| entry.code == 102)
.map_or(record.entries.len(), |offset| start + 1 + offset);
record.entries.drain(start..end);
}
let mut group = vec![XRecordEntry {
code: 102,
value: XRecordValue::String(PRE2007_MARKER.to_string()),
}];
for (index, (data_type, unit_type, format)) in row_types.into_iter().enumerate() {
let index = index as i32;
group.push(XRecordEntry {
code: 90 + 2 * index,
value: XRecordValue::Int32(data_type),
});
group.push(XRecordEntry {
code: 91 + 2 * index,
value: XRecordValue::Int32(unit_type),
});
group.push(XRecordEntry {
code: 1 + index,
value: XRecordValue::String(format),
});
}
let at = start.unwrap_or(record.entries.len());
record.entries.splice(at..at, group);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tablestyle_creation() {
let style = TableStyle::new("TestStyle");
assert_eq!(style.name, "TestStyle");
assert!(style.description.is_empty());
}
#[test]
fn test_standard_style() {
let style = TableStyle::standard();
assert_eq!(style.name, "Standard");
}
#[test]
fn test_default_values() {
let style = TableStyle::default();
assert!((style.horizontal_margin - 0.06).abs() < 1e-10);
assert!((style.vertical_margin - 0.06).abs() < 1e-10);
assert!(!style.title_suppressed);
assert!(!style.header_suppressed);
assert_eq!(style.flow_direction, TableFlowDirection::Down);
}
#[test]
fn test_data_row_style() {
let style = TableStyle::default();
assert!((style.data_row_style.text_height - 0.18).abs() < 1e-10);
assert_eq!(style.data_row_style.alignment, CellAlignment::MiddleCenter);
}
#[test]
fn test_header_row_style() {
let style = TableStyle::default();
assert!((style.header_row_style.text_height - 0.25).abs() < 1e-10);
assert_eq!(style.header_row_style.alignment, CellAlignment::TopCenter);
}
#[test]
fn test_title_row_style() {
let style = TableStyle::default();
assert!((style.title_row_style.text_height - 0.25).abs() < 1e-10);
}
#[test]
fn test_flow_direction_enum() {
assert_eq!(TableFlowDirection::from(0), TableFlowDirection::Down);
assert_eq!(TableFlowDirection::from(1), TableFlowDirection::Up);
}
#[test]
fn test_cell_alignment_enum() {
assert_eq!(CellAlignment::from(1), CellAlignment::TopLeft);
assert_eq!(CellAlignment::from(5), CellAlignment::MiddleCenter);
assert_eq!(CellAlignment::from(9), CellAlignment::BottomRight);
assert_eq!(CellAlignment::from(99), CellAlignment::MiddleCenter);
}
#[test]
fn test_cell_alignment_helpers() {
let top_left = CellAlignment::TopLeft;
assert!(top_left.is_top());
assert!(top_left.is_left());
assert!(!top_left.is_center());
assert!(!top_left.is_bottom());
let middle_center = CellAlignment::MiddleCenter;
assert!(middle_center.is_middle());
assert!(middle_center.is_center());
let bottom_right = CellAlignment::BottomRight;
assert!(bottom_right.is_bottom());
assert!(bottom_right.is_right());
}
#[test]
fn test_set_margins() {
let mut style = TableStyle::new("Test");
style.set_margins(0.15);
assert_eq!(style.horizontal_margin, 0.15);
assert_eq!(style.vertical_margin, 0.15);
}
#[test]
fn test_set_all_text_heights() {
let mut style = TableStyle::new("Test");
style.set_all_text_heights(0.3);
assert_eq!(style.data_row_style.text_height, 0.3);
assert_eq!(style.header_row_style.text_height, 0.3);
assert_eq!(style.title_row_style.text_height, 0.3);
}
#[test]
fn test_set_all_text_colors() {
let mut style = TableStyle::new("Test");
style.set_all_text_colors(Color::Index(1));
assert_eq!(style.data_row_style.text_color, Color::Index(1));
assert_eq!(style.header_row_style.text_color, Color::Index(1));
assert_eq!(style.title_row_style.text_color, Color::Index(1));
}
#[test]
fn test_set_all_fill_enabled() {
let mut style = TableStyle::new("Test");
style.set_all_fill_enabled(true);
assert!(style.data_row_style.fill_enabled);
assert!(style.header_row_style.fill_enabled);
assert!(style.title_row_style.fill_enabled);
}
#[test]
fn test_row_visibility() {
let mut style = TableStyle::new("Test");
assert!(style.has_title_row());
assert!(style.has_header_row());
style.set_title_visible(false);
style.set_header_visible(false);
assert!(!style.has_title_row());
assert!(!style.has_header_row());
}
#[test]
fn test_cell_border_creation() {
let border = TableCellBorder::new();
assert!(!border.is_invisible);
assert_eq!(border.border_type, TableBorderType::Single);
}
#[test]
fn test_cell_border_invisible() {
let border = TableCellBorder::invisible();
assert!(border.is_invisible);
}
#[test]
fn test_cell_border_with_style() {
let border = TableCellBorder::with_style(Color::Index(1), LineWeight::W0_50);
assert_eq!(border.color, Color::Index(1));
assert_eq!(border.line_weight, LineWeight::W0_50);
}
#[test]
fn test_set_all_borders() {
let mut style = RowCellStyle::new();
let border = TableCellBorder::with_style(Color::Index(3), LineWeight::W1_00);
style.set_all_borders(border);
assert_eq!(style.left_border.color, Color::Index(3));
assert_eq!(style.right_border.color, Color::Index(3));
assert_eq!(style.top_border.color, Color::Index(3));
assert_eq!(style.bottom_border.color, Color::Index(3));
}
#[test]
fn test_set_all_borders_invisible() {
let mut style = RowCellStyle::new();
style.set_all_borders_invisible();
assert!(style.left_border.is_invisible);
assert!(style.right_border.is_invisible);
assert!(style.top_border.is_invisible);
assert!(style.bottom_border.is_invisible);
}
#[test]
fn test_cell_style_property_flags() {
let flags = TableCellStylePropertyFlags::ALIGNMENT
| TableCellStylePropertyFlags::TEXT_HEIGHT
| TableCellStylePropertyFlags::BACKGROUND_COLOR;
assert!(flags.contains(TableCellStylePropertyFlags::ALIGNMENT));
assert!(flags.contains(TableCellStylePropertyFlags::TEXT_HEIGHT));
assert!(!flags.contains(TableCellStylePropertyFlags::ROTATION));
}
#[test]
fn test_border_property_flags() {
let flags = TableBorderPropertyFlags::COLOR | TableBorderPropertyFlags::LINE_WEIGHT;
assert!(flags.contains(TableBorderPropertyFlags::COLOR));
assert!(flags.contains(TableBorderPropertyFlags::LINE_WEIGHT));
assert!(!flags.contains(TableBorderPropertyFlags::VISIBILITY));
}
#[test]
fn test_table_style_flags() {
let flags = TableStyleFlags::TITLE_SUPPRESSED;
assert!(flags.contains(TableStyleFlags::TITLE_SUPPRESSED));
assert!(!flags.contains(TableStyleFlags::HEADER_SUPPRESSED));
}
#[test]
fn test_border_type_enum() {
assert_eq!(TableBorderType::from(1), TableBorderType::Single);
assert_eq!(TableBorderType::from(2), TableBorderType::Double);
}
}