feat: Support custom numbering

main
bokuweb 2019-12-07 02:15:21 +09:00
parent 7dff9e771d
commit 182f82e9a0
12 changed files with 78 additions and 54 deletions

View File

@ -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> {

View File

@ -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),

View File

@ -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" />"#);
} }
} }

View File

@ -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>"#
); );
} }
} }

View File

@ -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>"#
); );
} }
} }

View File

@ -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()

View File

@ -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

View File

@ -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>,

View File

@ -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,

View File

@ -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);

View File

@ -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");

View File

@ -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)?;