feat: Support custom numbering
parent
7dff9e771d
commit
182f82e9a0
|
@ -9,7 +9,7 @@ pub struct Level<'a> {
|
||||||
format: NumberFormat<'a>,
|
format: NumberFormat<'a>,
|
||||||
text: LevelText<'a>,
|
text: LevelText<'a>,
|
||||||
jc: LevelJc<'a>,
|
jc: LevelJc<'a>,
|
||||||
paragraph_property: ParagraphProperty<'a>,
|
paragraph_property: ParagraphProperty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Level<'a> {
|
impl<'a> Level<'a> {
|
||||||
|
|
|
@ -3,12 +3,12 @@ use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Numbering<'a> {
|
pub struct Numbering<'a> {
|
||||||
id: &'a str,
|
id: usize,
|
||||||
levels: Vec<Level<'a>>,
|
levels: Vec<Level<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Numbering<'a> {
|
impl<'a> Numbering<'a> {
|
||||||
pub fn new(id: &'a str) -> Self {
|
pub fn new(id: usize) -> Self {
|
||||||
Self { id, levels: vec![] }
|
Self { id, levels: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,16 +20,13 @@ impl<'a> Numbering<'a> {
|
||||||
|
|
||||||
impl<'a> BuildXML for Numbering<'a> {
|
impl<'a> BuildXML for Numbering<'a> {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
|
let id = format!("{}", self.id);
|
||||||
let mut b = XMLBuilder::new();
|
let mut b = XMLBuilder::new();
|
||||||
b = b.open_abstract_num(self.id);
|
b = b.open_abstract_num(&id);
|
||||||
for l in &self.levels {
|
for l in &self.levels {
|
||||||
b = b.add_child(l);
|
b = b.add_child(l);
|
||||||
}
|
}
|
||||||
b.close()
|
b.close().open_num(&id).abstract_num_id(&id).close().build()
|
||||||
.open_num(self.id)
|
|
||||||
.abstract_num_id(self.id)
|
|
||||||
.close()
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +41,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_numbering() {
|
fn test_numbering() {
|
||||||
let mut c = Numbering::new("0");
|
let mut c = Numbering::new(0);
|
||||||
c = c.add_level(Level::new(
|
c = c.add_level(Level::new(
|
||||||
1,
|
1,
|
||||||
Start::new(1),
|
Start::new(1),
|
||||||
|
|
|
@ -2,17 +2,17 @@ use crate::documents::BuildXML;
|
||||||
use crate::xml_builder::*;
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NumberingId<'a> {
|
pub struct NumberingId {
|
||||||
id: &'a str,
|
id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NumberingId<'a> {
|
impl NumberingId {
|
||||||
pub fn new(id: &'a str) -> NumberingId<'a> {
|
pub fn new(id: usize) -> NumberingId {
|
||||||
NumberingId { id }
|
NumberingId { id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuildXML for NumberingId<'a> {
|
impl BuildXML for NumberingId {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let b = XMLBuilder::new();
|
let b = XMLBuilder::new();
|
||||||
b.num_id(self.id).build()
|
b.num_id(self.id).build()
|
||||||
|
@ -29,8 +29,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_num_id() {
|
fn test_num_id() {
|
||||||
let c = NumberingId::new("abc");
|
let c = NumberingId::new(0);
|
||||||
let b = c.build();
|
let b = c.build();
|
||||||
assert_eq!(str::from_utf8(&b).unwrap(), r#"<w:numId w:val="abc" />"#);
|
assert_eq!(str::from_utf8(&b).unwrap(), r#"<w:numId w:val="0" />"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,18 @@ use crate::documents::BuildXML;
|
||||||
use crate::xml_builder::*;
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NumberingProperty<'a> {
|
pub struct NumberingProperty {
|
||||||
id: NumberingId<'a>,
|
id: NumberingId,
|
||||||
level: IndentLevel,
|
level: IndentLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> NumberingProperty<'a> {
|
impl NumberingProperty {
|
||||||
pub fn new(id: NumberingId<'a>, level: IndentLevel) -> NumberingProperty {
|
pub fn new(id: NumberingId, level: IndentLevel) -> NumberingProperty {
|
||||||
Self { id, level }
|
Self { id, level }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuildXML for NumberingProperty<'a> {
|
impl BuildXML for NumberingProperty {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let b = XMLBuilder::new();
|
let b = XMLBuilder::new();
|
||||||
b.open_numbering_property()
|
b.open_numbering_property()
|
||||||
|
@ -35,11 +35,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_num_property() {
|
fn test_num_property() {
|
||||||
let c = NumberingProperty::new(NumberingId::new("abc"), IndentLevel::new(3));
|
let c = NumberingProperty::new(NumberingId::new(0), IndentLevel::new(3));
|
||||||
let b = c.build();
|
let b = c.build();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
str::from_utf8(&b).unwrap(),
|
str::from_utf8(&b).unwrap(),
|
||||||
r#"<w:numPr><w:numId w:val="abc" /><w:ilvl w:val="3" /></w:numPr>"#
|
r#"<w:numPr><w:numId w:val="0" /><w:ilvl w:val="3" /></w:numPr>"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::xml_builder::*;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Paragraph<'a> {
|
pub struct Paragraph<'a> {
|
||||||
pub(crate) children: Vec<ParagraphChild<'a>>,
|
pub(crate) children: Vec<ParagraphChild<'a>>,
|
||||||
property: ParagraphProperty<'a>,
|
property: ParagraphProperty,
|
||||||
attrs: Vec<(String, String)>,
|
attrs: Vec<(String, String)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ impl<'a> Paragraph<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn numbering(mut self, id: NumberingId<'a>, level: IndentLevel) -> Self {
|
pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self {
|
||||||
self.property = self.property.numbering(id, level);
|
self.property = self.property.numbering(id, level);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -205,11 +205,11 @@ mod tests {
|
||||||
fn test_numbering() {
|
fn test_numbering() {
|
||||||
let b = Paragraph::new()
|
let b = Paragraph::new()
|
||||||
.add_run(Run::new().add_text("Hello"))
|
.add_run(Run::new().add_text("Hello"))
|
||||||
.numbering(NumberingId::new("abc"), IndentLevel::new(1))
|
.numbering(NumberingId::new(0), IndentLevel::new(1))
|
||||||
.build();
|
.build();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
str::from_utf8(&b).unwrap(),
|
str::from_utf8(&b).unwrap(),
|
||||||
r#"<w:p><w:pPr><w:pStyle w:val="Normal" /><w:rPr /><w:numPr><w:numId w:val="abc" /><w:ilvl w:val="1" /></w:numPr></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
|
r#"<w:p><w:pPr><w:pStyle w:val="Normal" /><w:rPr /><w:numPr><w:numId w:val="0" /><w:ilvl w:val="1" /></w:numPr></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,15 @@ use crate::types::{AlignmentType, SpecialIndentType};
|
||||||
use crate::xml_builder::*;
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParagraphProperty<'a> {
|
pub struct ParagraphProperty {
|
||||||
run_property: RunProperty,
|
run_property: RunProperty,
|
||||||
style: ParagraphStyle,
|
style: ParagraphStyle,
|
||||||
numbering_property: Option<NumberingProperty<'a>>,
|
numbering_property: Option<NumberingProperty>,
|
||||||
alignment: Option<Justification>,
|
alignment: Option<Justification>,
|
||||||
indent: Option<Indent>,
|
indent: Option<Indent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for ParagraphProperty<'a> {
|
impl Default for ParagraphProperty {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let s: Option<&str> = None;
|
let s: Option<&str> = None;
|
||||||
ParagraphProperty {
|
ParagraphProperty {
|
||||||
|
@ -32,8 +32,8 @@ impl<'a> Default for ParagraphProperty<'a> {
|
||||||
// This element specifies a set of paragraph properties which shall be applied to the contents of the parent
|
// This element specifies a set of paragraph properties which shall be applied to the contents of the parent
|
||||||
// paragraph after all style/numbering/table properties have been applied to the text. These properties are defined
|
// paragraph after all style/numbering/table properties have been applied to the text. These properties are defined
|
||||||
// as direct formatting, since they are directly applied to the paragraph and supersede any formatting from styles.
|
// as direct formatting, since they are directly applied to the paragraph and supersede any formatting from styles.
|
||||||
impl<'a> ParagraphProperty<'a> {
|
impl ParagraphProperty {
|
||||||
pub fn new() -> ParagraphProperty<'a> {
|
pub fn new() -> ParagraphProperty {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,13 +52,13 @@ impl<'a> ParagraphProperty<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn numbering(mut self, id: NumberingId<'a>, level: IndentLevel) -> Self {
|
pub fn numbering(mut self, id: NumberingId, level: IndentLevel) -> Self {
|
||||||
self.numbering_property = Some(NumberingProperty::new(id, level));
|
self.numbering_property = Some(NumberingProperty::new(id, level));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuildXML for ParagraphProperty<'a> {
|
impl BuildXML for ParagraphProperty {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
XMLBuilder::new()
|
XMLBuilder::new()
|
||||||
.open_paragraph_property()
|
.open_paragraph_property()
|
||||||
|
|
|
@ -5,15 +5,15 @@ use crate::StyleType;
|
||||||
use super::{BasedOn, Name, Next, ParagraphProperty, QFormat, RunProperty};
|
use super::{BasedOn, Name, Next, ParagraphProperty, QFormat, RunProperty};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Style<'a> {
|
pub struct Style {
|
||||||
style_id: String,
|
style_id: String,
|
||||||
name: Name,
|
name: Name,
|
||||||
style_type: StyleType,
|
style_type: StyleType,
|
||||||
run_property: RunProperty,
|
run_property: RunProperty,
|
||||||
paragraph_property: ParagraphProperty<'a>,
|
paragraph_property: ParagraphProperty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for Style<'a> {
|
impl Default for Style {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let name = Name::new("");
|
let name = Name::new("");
|
||||||
let rpr = RunProperty::new();
|
let rpr = RunProperty::new();
|
||||||
|
@ -28,7 +28,7 @@ impl<'a> Default for Style<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Style<'a> {
|
impl Style {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
style_id: impl Into<String>,
|
style_id: impl Into<String>,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
|
@ -65,7 +65,7 @@ impl<'a> Style<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuildXML for Style<'a> {
|
impl BuildXML for Style {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let b = XMLBuilder::new();
|
let b = XMLBuilder::new();
|
||||||
// Set "Normal" as default if you need change these values please fix it
|
// Set "Normal" as default if you need change these values please fix it
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub struct Docx<'a> {
|
||||||
rels: Rels,
|
rels: Rels,
|
||||||
document_rels: DocumentRels,
|
document_rels: DocumentRels,
|
||||||
doc_props: DocProps<'a>,
|
doc_props: DocProps<'a>,
|
||||||
styles: Styles<'a>,
|
styles: Styles,
|
||||||
document: Document<'a>,
|
document: Document<'a>,
|
||||||
comments: Comments<'a>,
|
comments: Comments<'a>,
|
||||||
numberings: Numberings<'a>,
|
numberings: Numberings<'a>,
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl<'a> BuildXML for Numberings<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_default_numbering() -> Numbering<'static> {
|
fn create_default_numbering() -> Numbering<'static> {
|
||||||
Numbering::new("0")
|
Numbering::new(0)
|
||||||
.add_level(
|
.add_level(
|
||||||
Level::new(
|
Level::new(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -4,23 +4,23 @@ use crate::types::*;
|
||||||
use crate::xml_builder::*;
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Styles<'a> {
|
pub struct Styles {
|
||||||
doc_defaults: DocDefaults,
|
doc_defaults: DocDefaults,
|
||||||
styles: Vec<Style<'a>>,
|
styles: Vec<Style>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Styles<'a> {
|
impl Styles {
|
||||||
pub fn new() -> Styles<'a> {
|
pub fn new() -> Styles {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_style(mut self, style: Style<'a>) -> Self {
|
pub fn add_style(mut self, style: Style) -> Self {
|
||||||
self.styles.push(style);
|
self.styles.push(style);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for Styles<'a> {
|
impl Default for Styles {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
doc_defaults: DocDefaults::new(),
|
doc_defaults: DocDefaults::new(),
|
||||||
|
@ -29,7 +29,7 @@ impl<'a> Default for Styles<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BuildXML for Styles<'a> {
|
impl BuildXML for Styles {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let b = XMLBuilder::new();
|
let b = XMLBuilder::new();
|
||||||
let normal = Style::new("Normal", "Normal", StyleType::Paragraph);
|
let normal = Style::new("Normal", "Normal", StyleType::Paragraph);
|
||||||
|
|
|
@ -191,7 +191,7 @@ impl XMLBuilder {
|
||||||
opened_el!(open_num, "w:num", "w:numId");
|
opened_el!(open_num, "w:num", "w:numId");
|
||||||
opened_el!(open_numbering_property, "w:numPr");
|
opened_el!(open_numbering_property, "w:numPr");
|
||||||
only_usize_val_el!(indent_level, "w:ilvl");
|
only_usize_val_el!(indent_level, "w:ilvl");
|
||||||
only_str_val_el!(num_id, "w:numId");
|
only_usize_val_el!(num_id, "w:numId");
|
||||||
only_usize_val_el!(start, "w:start");
|
only_usize_val_el!(start, "w:start");
|
||||||
only_str_val_el!(number_format, "w:numFmt");
|
only_str_val_el!(number_format, "w:numFmt");
|
||||||
only_str_val_el!(level_text, "w:lvlText");
|
only_str_val_el!(level_text, "w:lvlText");
|
||||||
|
|
|
@ -323,22 +323,49 @@ pub fn default_numbering() -> Result<(), DocxError> {
|
||||||
.add_paragraph(
|
.add_paragraph(
|
||||||
Paragraph::new()
|
Paragraph::new()
|
||||||
.add_run(Run::new().add_text("Hello"))
|
.add_run(Run::new().add_text("Hello"))
|
||||||
.numbering(NumberingId::new("0"), IndentLevel::new(0)),
|
.numbering(NumberingId::new(0), IndentLevel::new(0)),
|
||||||
)
|
)
|
||||||
.add_paragraph(
|
.add_paragraph(
|
||||||
Paragraph::new()
|
Paragraph::new()
|
||||||
.add_run(Run::new().add_text("World!"))
|
.add_run(Run::new().add_text("World!"))
|
||||||
.numbering(NumberingId::new("0"), IndentLevel::new(1)),
|
.numbering(NumberingId::new(0), IndentLevel::new(1)),
|
||||||
)
|
)
|
||||||
.add_paragraph(
|
.add_paragraph(
|
||||||
Paragraph::new()
|
Paragraph::new()
|
||||||
.add_run(Run::new().add_text("Foooo!"))
|
.add_run(Run::new().add_text("Foooo!"))
|
||||||
.numbering(NumberingId::new("0"), IndentLevel::new(2)),
|
.numbering(NumberingId::new(0), IndentLevel::new(2)),
|
||||||
)
|
)
|
||||||
.add_paragraph(
|
.add_paragraph(
|
||||||
Paragraph::new()
|
Paragraph::new()
|
||||||
.add_run(Run::new().add_text("Bar!"))
|
.add_run(Run::new().add_text("Bar!"))
|
||||||
.numbering(NumberingId::new("0"), IndentLevel::new(3)),
|
.numbering(NumberingId::new(0), IndentLevel::new(3)),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.pack(file)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn user_numbering() -> Result<(), DocxError> {
|
||||||
|
let path = std::path::Path::new("./tests/output/user_numbering.docx");
|
||||||
|
let file = std::fs::File::create(&path).unwrap();
|
||||||
|
Docx::new()
|
||||||
|
.add_paragraph(
|
||||||
|
Paragraph::new()
|
||||||
|
.add_run(Run::new().add_text("Hello"))
|
||||||
|
.numbering(NumberingId::new(2), IndentLevel::new(0)),
|
||||||
|
)
|
||||||
|
.add_numbering(
|
||||||
|
Numbering::new(2).add_level(
|
||||||
|
Level::new(
|
||||||
|
0,
|
||||||
|
Start::new(1),
|
||||||
|
NumberFormat::new("decimal"),
|
||||||
|
LevelText::new("Section %1."),
|
||||||
|
LevelJc::new("left"),
|
||||||
|
)
|
||||||
|
.indent(1620, Some(SpecialIndentType::Hanging(320))),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
.pack(file)?;
|
.pack(file)?;
|
||||||
|
|
Loading…
Reference in New Issue