add Paragraph borders (#642)
* fix widow_control xml * add character_spacing_control * snapshot fix - character_spacing_values, snack to camelCase * wasm snapshot * snapshot * paragraph_borders * Update docx-core/src/documents/elements/paragraph_property.rs Co-authored-by: bokuweb <bokuweb12@gmail.com> --------- Co-authored-by: gwq <guowanqi@tianchuangsec.com> Co-authored-by: bokuweb <bokuweb12@gmail.com>main
parent
8aec069b83
commit
86786eaa7b
|
@ -63,6 +63,7 @@ mod outline_lvl;
|
|||
mod page_margin;
|
||||
mod page_size;
|
||||
mod paragraph;
|
||||
mod paragraph_borders;
|
||||
mod paragraph_property;
|
||||
mod paragraph_property_change;
|
||||
mod paragraph_style;
|
||||
|
@ -184,6 +185,7 @@ pub use outline_lvl::*;
|
|||
pub use page_margin::*;
|
||||
pub use page_size::*;
|
||||
pub use paragraph::*;
|
||||
pub use paragraph_borders::*;
|
||||
pub use paragraph_property::*;
|
||||
pub use paragraph_property_change::*;
|
||||
pub use paragraph_style::*;
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::documents::BuildXML;
|
||||
use crate::types::*;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ParagraphBorder {
|
||||
position: ParagraphBorderPosition,
|
||||
pub val: BorderType,
|
||||
pub size: usize,
|
||||
pub space: usize,
|
||||
pub color: String,
|
||||
// pub shadow: Option<bool>,
|
||||
// pub theme_color: Option<String>,
|
||||
// pub theme_shade: Option<String>,
|
||||
// pub theme_tint: Option<String>,
|
||||
// pub frame: Option<bool>,
|
||||
}
|
||||
|
||||
impl ParagraphBorder {
|
||||
pub fn new(position: ParagraphBorderPosition) -> Self {
|
||||
ParagraphBorder {
|
||||
position,
|
||||
val: BorderType::Single,
|
||||
size: 2,
|
||||
space: 0,
|
||||
color: "auto".to_owned(),
|
||||
// shadow: None,
|
||||
// theme_color: None,
|
||||
// theme_shade: None,
|
||||
// theme_tint: None,
|
||||
// frame: None,
|
||||
}
|
||||
}
|
||||
pub fn val(mut self, val: BorderType) -> Self {
|
||||
self.val = val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: usize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn space(mut self, space: usize) -> Self {
|
||||
self.space = space;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: impl Into<String>) -> Self {
|
||||
self.color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn shadow(mut self, shadow: bool) -> Self {
|
||||
// self.shadow = Some(shadow);
|
||||
// self
|
||||
// }
|
||||
//
|
||||
// pub fn theme_color(mut self, theme_color: impl Into<String>) -> Self {
|
||||
// self.theme_color = Some(theme_color.into());
|
||||
// self
|
||||
// }
|
||||
//
|
||||
// pub fn theme_shade(mut self, theme_shade: impl Into<String>) -> Self {
|
||||
// self.theme_shade = Some(theme_shade.into());
|
||||
// self
|
||||
// }
|
||||
//
|
||||
// pub fn theme_tint(mut self, theme_tint: impl Into<String>) -> Self {
|
||||
// self.theme_tint = Some(theme_tint.into());
|
||||
// self
|
||||
// }
|
||||
//
|
||||
// pub fn frame(mut self, frame: bool) -> Self {
|
||||
// self.frame = Some(frame);
|
||||
// self
|
||||
// }
|
||||
}
|
||||
|
||||
impl BuildXML for ParagraphBorder {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let base = XMLBuilder::new();
|
||||
let base = {
|
||||
let val = self.val.to_string();
|
||||
let space = self.space.to_string();
|
||||
let size = self.size.to_string();
|
||||
match self.position {
|
||||
ParagraphBorderPosition::Top => base.paragraph_border_top(&val, &space, &size, &self.color),
|
||||
ParagraphBorderPosition::Left => base.paragraph_border_left(&val, &space, &size, &self.color),
|
||||
ParagraphBorderPosition::Bottom => base.paragraph_border_bottom(&val, &space, &size, &self.color),
|
||||
ParagraphBorderPosition::Right => base.paragraph_border_right(&val, &space, &size, &self.color),
|
||||
ParagraphBorderPosition::Between => base.paragraph_border_between(&val, &space, &size, &self.color),
|
||||
ParagraphBorderPosition::Bar => base.paragraph_border_bar(&val, &space, &size, &self.color),
|
||||
}
|
||||
};
|
||||
base.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ParagraphBorders {
|
||||
left: Option<ParagraphBorder>,
|
||||
right: Option<ParagraphBorder>,
|
||||
top: Option<ParagraphBorder>,
|
||||
bottom: Option<ParagraphBorder>,
|
||||
between: Option<ParagraphBorder>,
|
||||
bar: Option<ParagraphBorder>,
|
||||
}
|
||||
|
||||
|
||||
impl Default for ParagraphBorders {
|
||||
fn default() -> Self {
|
||||
ParagraphBorders {
|
||||
left: Some(ParagraphBorder::new(ParagraphBorderPosition::Left)),
|
||||
right: Some(ParagraphBorder::new(ParagraphBorderPosition::Right)),
|
||||
top: Some(ParagraphBorder::new(ParagraphBorderPosition::Top)),
|
||||
bottom: Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom)),
|
||||
between: None,
|
||||
bar: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParagraphBorders {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_empty() -> Self {
|
||||
ParagraphBorders {
|
||||
left: None,
|
||||
right: None,
|
||||
top: None,
|
||||
bottom: None,
|
||||
between: None,
|
||||
bar: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(mut self, border: ParagraphBorder) -> Self {
|
||||
match border.position {
|
||||
ParagraphBorderPosition::Top => self.top = Some(border),
|
||||
ParagraphBorderPosition::Left => self.left = Some(border),
|
||||
ParagraphBorderPosition::Bottom => self.bottom = Some(border),
|
||||
ParagraphBorderPosition::Right => self.right = Some(border),
|
||||
ParagraphBorderPosition::Between => self.between = Some(border),
|
||||
ParagraphBorderPosition::Bar => self.bar = Some(border),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear(mut self, position: ParagraphBorderPosition) -> Self {
|
||||
let nil = ParagraphBorder::new(position.clone()).val(BorderType::Nil);
|
||||
match position {
|
||||
ParagraphBorderPosition::Top => self.top = Some(nil),
|
||||
ParagraphBorderPosition::Left => self.left = Some(nil),
|
||||
ParagraphBorderPosition::Bottom => self.bottom = Some(nil),
|
||||
ParagraphBorderPosition::Right => self.right = Some(nil),
|
||||
ParagraphBorderPosition::Between => self.between = Some(nil),
|
||||
ParagraphBorderPosition::Bar => self.bar = Some(nil),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_all(mut self) -> Self {
|
||||
self.left = Some(ParagraphBorder::new(ParagraphBorderPosition::Left).val(BorderType::Nil));
|
||||
self.right = Some(ParagraphBorder::new(ParagraphBorderPosition::Right).val(BorderType::Nil));
|
||||
self.top = Some(ParagraphBorder::new(ParagraphBorderPosition::Top).val(BorderType::Nil));
|
||||
self.bottom = Some(ParagraphBorder::new(ParagraphBorderPosition::Bottom).val(BorderType::Nil));
|
||||
self.between = Some(ParagraphBorder::new(ParagraphBorderPosition::Between).val(BorderType::Nil));
|
||||
self.bar = Some(ParagraphBorder::new(ParagraphBorderPosition::Bar).val(BorderType::Nil));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for ParagraphBorders {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
XMLBuilder::new()
|
||||
.open_paragraph_borders()
|
||||
.add_optional_child(&self.left)
|
||||
.add_optional_child(&self.right)
|
||||
.add_optional_child(&self.top)
|
||||
.add_optional_child(&self.bottom)
|
||||
.add_optional_child(&self.between)
|
||||
.add_optional_child(&self.bar)
|
||||
.close()
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use serde::Serialize;
|
|||
|
||||
use super::*;
|
||||
use crate::documents::BuildXML;
|
||||
use crate::ParagraphBorderPosition;
|
||||
use crate::types::{AlignmentType, SpecialIndentType};
|
||||
use crate::xml_builder::*;
|
||||
|
||||
|
@ -37,6 +38,8 @@ pub struct ParagraphProperty {
|
|||
pub(crate) div_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub paragraph_property_change: Option<ParagraphPropertyChange>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub borders: Option<ParagraphBorders>,
|
||||
}
|
||||
|
||||
// 17.3.1.26
|
||||
|
@ -148,6 +151,26 @@ impl ParagraphProperty {
|
|||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_borders(mut self, borders: ParagraphBorders) -> Self {
|
||||
self.borders = Some(borders);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_border(mut self, border: ParagraphBorder) -> Self {
|
||||
self.borders = Some(self.borders.unwrap_or_default().set(border));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_border(mut self, position: ParagraphBorderPosition) -> Self {
|
||||
self.borders = Some(self.borders.unwrap_or_default().clear(position));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clear_all_borders(mut self) -> Self {
|
||||
self.borders = Some(self.borders.unwrap_or_default().clear_all());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_build(p: &ParagraphProperty) -> Vec<u8> {
|
||||
|
@ -160,7 +183,8 @@ fn inner_build(p: &ParagraphProperty) -> Vec<u8> {
|
|||
.add_optional_child(&p.indent)
|
||||
.add_optional_child(&p.line_spacing)
|
||||
.add_optional_child(&p.outline_lvl)
|
||||
.add_optional_child(&p.paragraph_property_change);
|
||||
.add_optional_child(&p.paragraph_property_change)
|
||||
.add_optional_child(&p.borders);
|
||||
|
||||
if let Some(v) = p.keep_next {
|
||||
if v {
|
||||
|
|
|
@ -27,3 +27,15 @@ pub enum TableCellBorderPosition {
|
|||
Tl2br,
|
||||
Tr2bl,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", wasm_bindgen)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum ParagraphBorderPosition {
|
||||
Left,
|
||||
Right,
|
||||
Top,
|
||||
Bottom,
|
||||
Between,
|
||||
Bar,
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ impl XMLBuilder {
|
|||
// i.e. <w:r ... >
|
||||
open!(open_run, "w:r");
|
||||
open!(open_run_property, "w:rPr");
|
||||
open!(open_paragraph_borders, "w:pBdr");
|
||||
open!(open_run_property_default, "w:rPrDefault");
|
||||
// i.e. <w:qFormat ... >
|
||||
closed!(q_format, "w:qFormat");
|
||||
|
@ -139,6 +140,13 @@ impl XMLBuilder {
|
|||
open!(open_structured_tag_property, "w:sdtPr");
|
||||
closed_with_str!(alias, "w:alias");
|
||||
|
||||
closed_paragraph_border_el!(paragraph_border_top, "w:top");
|
||||
closed_paragraph_border_el!(paragraph_border_left, "w:left");
|
||||
closed_paragraph_border_el!(paragraph_border_bottom, "w:bottom");
|
||||
closed_paragraph_border_el!(paragraph_border_right, "w:right");
|
||||
closed_paragraph_border_el!(paragraph_border_between, "w:between");
|
||||
closed_paragraph_border_el!(paragraph_border_bar, "w:bar");
|
||||
|
||||
// i.e. <w:outlineLvl ...>
|
||||
closed_with_usize!(outline_lvl, "w:outlineLvl");
|
||||
// i.e. <w:name ... >
|
||||
|
|
|
@ -559,3 +559,24 @@ macro_rules! closed_border_el {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! closed_paragraph_border_el {
|
||||
($name: ident, $ el_name: expr) => {
|
||||
pub(crate) fn $name<'a>(
|
||||
mut self,
|
||||
val: &str,
|
||||
space: &str,
|
||||
size: &str,
|
||||
color: &str,
|
||||
) -> Self {
|
||||
self.writer.write(
|
||||
XmlEvent::start_element($el_name)
|
||||
.attr("w:val", val)
|
||||
.attr("w:space", space)
|
||||
.attr("w:sz", size)
|
||||
.attr("w:color", color)
|
||||
).expect(EXPECT_MESSAGE);
|
||||
self.close()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -77,8 +77,8 @@ impl XMLBuilder {
|
|||
}
|
||||
|
||||
pub(crate) fn add_child<T>(mut self, child: &T) -> Self
|
||||
where
|
||||
T: BuildXML,
|
||||
where
|
||||
T: BuildXML,
|
||||
{
|
||||
let buf = child.build();
|
||||
let text = str::from_utf8(&buf).unwrap();
|
||||
|
@ -93,8 +93,8 @@ impl XMLBuilder {
|
|||
}
|
||||
|
||||
pub(crate) fn add_optional_child<T>(mut self, child: &Option<T>) -> Self
|
||||
where
|
||||
T: BuildXML,
|
||||
where
|
||||
T: BuildXML,
|
||||
{
|
||||
if let Some(c) = child {
|
||||
self = self.add_child(c)
|
||||
|
@ -103,8 +103,8 @@ impl XMLBuilder {
|
|||
}
|
||||
|
||||
pub(crate) fn add_children<T>(mut self, children: &[T]) -> Self
|
||||
where
|
||||
T: BuildXML,
|
||||
where
|
||||
T: BuildXML,
|
||||
{
|
||||
for c in children {
|
||||
self = self.add_child(c);
|
||||
|
@ -134,7 +134,6 @@ impl XMLBuilder {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in New Issue