Support for Paragraph's Line Spacing (#346)
* Support for Line Spacing Signed-off-by: lkadalski <kadalski.lukasz@gmail.com> * Fixing clippy and yarn test Signed-off-by: lkadalski <kadalski.lukasz@gmail.com>main
parent
7fa473e9df
commit
241a08dd42
|
@ -97,39 +97,39 @@ impl BuildXML for CoreProps {
|
||||||
self.config
|
self.config
|
||||||
.created
|
.created
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "1970-01-01T00:00:00Z", |v| &v),
|
.map_or_else(|| "1970-01-01T00:00:00Z", |v| v),
|
||||||
)
|
)
|
||||||
.dc_creator(
|
.dc_creator(
|
||||||
self.config
|
self.config
|
||||||
.creator
|
.creator
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "unknown", |v| &v),
|
.map_or_else(|| "unknown", |v| v),
|
||||||
)
|
)
|
||||||
.cp_last_modified_by(
|
.cp_last_modified_by(
|
||||||
self.config
|
self.config
|
||||||
.last_modified_by
|
.last_modified_by
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "unknown", |v| &v),
|
.map_or_else(|| "unknown", |v| v),
|
||||||
)
|
)
|
||||||
.dcterms_modified(
|
.dcterms_modified(
|
||||||
"dcterms:W3CDTF",
|
"dcterms:W3CDTF",
|
||||||
self.config
|
self.config
|
||||||
.modified
|
.modified
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "1970-01-01T00:00:00Z", |v| &v),
|
.map_or_else(|| "1970-01-01T00:00:00Z", |v| v),
|
||||||
)
|
)
|
||||||
.cp_revision(&self.config.revision.map_or_else(|| "1".to_owned(), convert));
|
.cp_revision(&self.config.revision.map_or_else(|| "1".to_owned(), convert));
|
||||||
if let Some(v) = self.config.description.as_ref() {
|
if let Some(v) = self.config.description.as_ref() {
|
||||||
base = base.dc_description(&v);
|
base = base.dc_description(v);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.config.language.as_ref() {
|
if let Some(v) = self.config.language.as_ref() {
|
||||||
base = base.dc_language(&v);
|
base = base.dc_language(v);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.config.subject.as_ref() {
|
if let Some(v) = self.config.subject.as_ref() {
|
||||||
base = base.dc_subject(&v);
|
base = base.dc_subject(v);
|
||||||
}
|
}
|
||||||
if let Some(v) = self.config.title.as_ref() {
|
if let Some(v) = self.config.title.as_ref() {
|
||||||
base = base.dc_title(&v);
|
base = base.dc_title(v);
|
||||||
}
|
}
|
||||||
base.close().build()
|
base.close().build()
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,8 @@ mod tests {
|
||||||
});
|
});
|
||||||
let b = c.build();
|
let b = c.build();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
str::from_utf8(&b).unwrap(),
|
str::from_utf8(&b).unwrap(),
|
||||||
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<dcterms:created xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:created>
|
<dcterms:created xsi:type="dcterms:W3CDTF">2019-01-01</dcterms:created>
|
||||||
<dc:creator>foo</dc:creator>
|
<dc:creator>foo</dc:creator>
|
||||||
|
@ -198,6 +198,6 @@ mod tests {
|
||||||
<dc:subject>subject</dc:subject>
|
<dc:subject>subject</dc:subject>
|
||||||
<dc:title>title</dc:title>
|
<dc:title>title</dc:title>
|
||||||
</cp:coreProperties>"#
|
</cp:coreProperties>"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&graphic).unwrap(),
|
serde_json::to_string(&graphic).unwrap(),
|
||||||
r#"{"children":[{"dataType":"wpShape","children":[{"type":"shape","data":{"children":[{"type":"textbox","data":{"children":[{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"pattern1"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"has_numbering":false}],"hasNumbering":false}}]}}]}]}"#,
|
r#"{"children":[{"dataType":"wpShape","children":[{"type":"shape","data":{"children":[{"type":"textbox","data":{"children":[{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"pattern1"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"has_numbering":false}],"hasNumbering":false}}]}}]}]}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,7 @@ mod tests {
|
||||||
.num_style_link("style1");
|
.num_style_link("style1");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&c).unwrap(),
|
serde_json::to_string(&c).unwrap(),
|
||||||
r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#,
|
r#"{"id":0,"styleLink":null,"numStyleLink":"style1","levels":[{"level":1,"start":1,"format":"decimal","text":"%4.","jc":"left","paragraphProperty":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"suffix":"tab","pstyle":null,"levelRestart":null}]}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
use crate::documents::BuildXML;
|
||||||
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CharacterSpacing {
|
||||||
|
value: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterSpacing {
|
||||||
|
pub fn new(s: i32) -> CharacterSpacing {
|
||||||
|
Self { value: s }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildXML for CharacterSpacing {
|
||||||
|
fn build(&self) -> Vec<u8> {
|
||||||
|
let b = XMLBuilder::new();
|
||||||
|
b.spacing(self.value).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for CharacterSpacing {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_i32(self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spacing() {
|
||||||
|
let b = CharacterSpacing::new(200).build();
|
||||||
|
assert_eq!(str::from_utf8(&b).unwrap(), r#"<w:spacing w:val="200" />"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spacing_json() {
|
||||||
|
let s = CharacterSpacing { value: 100 };
|
||||||
|
assert_eq!(serde_json::to_string(&s).unwrap(), r#"100"#);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::documents::BuildXML;
|
||||||
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
|
use crate::line_spacing_type::LineSpacingType;
|
||||||
|
use serde::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct LineSpacing {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
line_rule: Option<LineSpacingType>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
before: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
after: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
line: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineSpacing {
|
||||||
|
pub fn new(spacing: Option<LineSpacingType>) -> Self {
|
||||||
|
Self {
|
||||||
|
line_rule: spacing,
|
||||||
|
before: None,
|
||||||
|
after: None,
|
||||||
|
line: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn before(mut self, before: Option<u32>) -> Self {
|
||||||
|
self.before = before;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn after(mut self, after: Option<u32>) -> Self {
|
||||||
|
self.after = after;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn line(mut self, line: Option<u32>) -> Self {
|
||||||
|
self.line = line;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildXML for LineSpacing {
|
||||||
|
fn build(&self) -> Vec<u8> {
|
||||||
|
let b = XMLBuilder::new();
|
||||||
|
b.line_spacing(self.before, self.after, self.line, self.line_rule)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spacing() {
|
||||||
|
let b = LineSpacing::new(Some(LineSpacingType::Auto))
|
||||||
|
.line(Some(100))
|
||||||
|
.build();
|
||||||
|
assert_eq!(
|
||||||
|
str::from_utf8(&b).unwrap(),
|
||||||
|
r#"<w:spacing w:line="100" w:lineRule="auto" />"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spacing_json() {
|
||||||
|
let s = LineSpacing {
|
||||||
|
line_rule: Some(LineSpacingType::Auto),
|
||||||
|
before: None,
|
||||||
|
after: None,
|
||||||
|
line: Some(100),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::to_string(&s).unwrap(),
|
||||||
|
r#"{"lineRule":"Auto","line":100}"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ mod bold_cs;
|
||||||
mod bookmark_end;
|
mod bookmark_end;
|
||||||
mod bookmark_start;
|
mod bookmark_start;
|
||||||
mod br;
|
mod br;
|
||||||
|
mod character_spacing;
|
||||||
mod color;
|
mod color;
|
||||||
mod comment;
|
mod comment;
|
||||||
mod comment_extended;
|
mod comment_extended;
|
||||||
|
@ -36,6 +37,7 @@ mod level_jc;
|
||||||
mod level_override;
|
mod level_override;
|
||||||
mod level_restart;
|
mod level_restart;
|
||||||
mod level_text;
|
mod level_text;
|
||||||
|
mod line_spacing;
|
||||||
mod mc_fallback;
|
mod mc_fallback;
|
||||||
mod name;
|
mod name;
|
||||||
mod next;
|
mod next;
|
||||||
|
@ -58,7 +60,6 @@ mod run_property_default;
|
||||||
mod section;
|
mod section;
|
||||||
mod section_property;
|
mod section_property;
|
||||||
mod shading;
|
mod shading;
|
||||||
mod spacing;
|
|
||||||
mod start;
|
mod start;
|
||||||
mod style;
|
mod style;
|
||||||
mod sz;
|
mod sz;
|
||||||
|
@ -102,6 +103,7 @@ pub use bold_cs::*;
|
||||||
pub use bookmark_end::*;
|
pub use bookmark_end::*;
|
||||||
pub use bookmark_start::*;
|
pub use bookmark_start::*;
|
||||||
pub use br::*;
|
pub use br::*;
|
||||||
|
pub use character_spacing::*;
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use comment::*;
|
pub use comment::*;
|
||||||
pub use comment_extended::*;
|
pub use comment_extended::*;
|
||||||
|
@ -131,6 +133,7 @@ pub use level_jc::*;
|
||||||
pub use level_override::*;
|
pub use level_override::*;
|
||||||
pub use level_restart::*;
|
pub use level_restart::*;
|
||||||
pub use level_text::*;
|
pub use level_text::*;
|
||||||
|
pub use line_spacing::*;
|
||||||
pub use mc_fallback::*;
|
pub use mc_fallback::*;
|
||||||
pub use name::*;
|
pub use name::*;
|
||||||
pub use next::*;
|
pub use next::*;
|
||||||
|
@ -153,7 +156,6 @@ pub use run_property_default::*;
|
||||||
pub use section::*;
|
pub use section::*;
|
||||||
pub use section_property::*;
|
pub use section_property::*;
|
||||||
pub use shading::*;
|
pub use shading::*;
|
||||||
pub use spacing::*;
|
|
||||||
pub use start::*;
|
pub use start::*;
|
||||||
pub use style::*;
|
pub use style::*;
|
||||||
pub use sz::*;
|
pub use sz::*;
|
||||||
|
|
|
@ -244,8 +244,16 @@ impl Paragraph {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_height(mut self, h: u32) -> Self {
|
pub fn line_spacing(
|
||||||
self.property = self.property.line_height(h);
|
mut self,
|
||||||
|
before: Option<u32>,
|
||||||
|
after: Option<u32>,
|
||||||
|
line: Option<u32>,
|
||||||
|
spacing_type: Option<LineSpacingType>,
|
||||||
|
) -> Self {
|
||||||
|
self.property = self
|
||||||
|
.property
|
||||||
|
.line_spacing(before, after, line, spacing_type);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,13 +332,25 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_spacing_and_character_spacing() {
|
||||||
|
let b = Paragraph::new()
|
||||||
|
.line_spacing(Some(20), Some(30), Some(200), Some(LineSpacingType::Auto))
|
||||||
|
.add_run(Run::new().add_text("Hello"))
|
||||||
|
.build();
|
||||||
|
assert_eq!(
|
||||||
|
str::from_utf8(&b).unwrap(),
|
||||||
|
r#"<w:p w14:paraId="12345678"><w:pPr><w:rPr /><w:spacing w:before="20" w:after="30" w:line="200" w:lineRule="auto" /></w:pPr><w:r><w:rPr /><w:t xml:space="preserve">Hello</w:t></w:r></w:p>"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_paragraph_run_json() {
|
fn test_paragraph_run_json() {
|
||||||
let run = Run::new().add_text("Hello");
|
let run = Run::new().add_text("Hello");
|
||||||
let p = Paragraph::new().add_run(run);
|
let p = Paragraph::new().add_run(run);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&p).unwrap(),
|
serde_json::to_string(&p).unwrap(),
|
||||||
r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"#,
|
r#"{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +361,7 @@ mod tests {
|
||||||
let p = Paragraph::new().add_insert(ins);
|
let p = Paragraph::new().add_insert(ins);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&p).unwrap(),
|
serde_json::to_string(&p).unwrap(),
|
||||||
r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"#
|
r#"{"id":"12345678","children":[{"type":"insert","data":{"children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"author":"unnamed","date":"1970-01-01T00:00:00Z"}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use serde::Serialize;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::documents::BuildXML;
|
use crate::documents::BuildXML;
|
||||||
use crate::types::{AlignmentType, SpecialIndentType};
|
use crate::types::{AlignmentType, LineSpacingType, SpecialIndentType};
|
||||||
use crate::xml_builder::*;
|
use crate::xml_builder::*;
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||||
|
@ -13,7 +13,7 @@ pub struct ParagraphProperty {
|
||||||
pub numbering_property: Option<NumberingProperty>,
|
pub numbering_property: Option<NumberingProperty>,
|
||||||
pub alignment: Option<Justification>,
|
pub alignment: Option<Justification>,
|
||||||
pub indent: Option<Indent>,
|
pub indent: Option<Indent>,
|
||||||
pub line_height: Option<u32>,
|
pub line_spacing: Option<LineSpacing>,
|
||||||
pub keep_next: bool,
|
pub keep_next: bool,
|
||||||
pub keep_lines: bool,
|
pub keep_lines: bool,
|
||||||
pub page_break_before: bool,
|
pub page_break_before: bool,
|
||||||
|
@ -31,7 +31,7 @@ impl Default for ParagraphProperty {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
keep_next: false,
|
keep_next: false,
|
||||||
keep_lines: false,
|
keep_lines: false,
|
||||||
page_break_before: false,
|
page_break_before: false,
|
||||||
|
@ -78,8 +78,19 @@ impl ParagraphProperty {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_height(mut self, h: u32) -> Self {
|
pub fn line_spacing(
|
||||||
self.line_height = Some(h);
|
mut self,
|
||||||
|
before: Option<u32>,
|
||||||
|
after: Option<u32>,
|
||||||
|
line: Option<u32>,
|
||||||
|
spacing_type: Option<LineSpacingType>,
|
||||||
|
) -> Self {
|
||||||
|
self.line_spacing = Some(
|
||||||
|
LineSpacing::new(spacing_type)
|
||||||
|
.after(after)
|
||||||
|
.before(before)
|
||||||
|
.line(line),
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,11 +136,6 @@ impl ParagraphProperty {
|
||||||
|
|
||||||
impl BuildXML for ParagraphProperty {
|
impl BuildXML for ParagraphProperty {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let spacing = if let Some(s) = self.line_height {
|
|
||||||
Some(Spacing::new(crate::SpacingType::Line(s)))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let mut b = XMLBuilder::new()
|
let mut b = XMLBuilder::new()
|
||||||
.open_paragraph_property()
|
.open_paragraph_property()
|
||||||
.add_child(&self.run_property)
|
.add_child(&self.run_property)
|
||||||
|
@ -137,7 +143,7 @@ impl BuildXML for ParagraphProperty {
|
||||||
.add_optional_child(&self.numbering_property)
|
.add_optional_child(&self.numbering_property)
|
||||||
.add_optional_child(&self.alignment)
|
.add_optional_child(&self.alignment)
|
||||||
.add_optional_child(&self.indent)
|
.add_optional_child(&self.indent)
|
||||||
.add_optional_child(&spacing)
|
.add_optional_child(&self.line_spacing)
|
||||||
.add_optional_child(&self.outline_lvl);
|
.add_optional_child(&self.outline_lvl);
|
||||||
|
|
||||||
if self.keep_next {
|
if self.keep_next {
|
||||||
|
@ -222,7 +228,19 @@ mod tests {
|
||||||
let b = c.indent(Some(20), Some(SpecialIndentType::FirstLine(10)), None, None);
|
let b = c.indent(Some(20), Some(SpecialIndentType::FirstLine(10)), None, None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&b).unwrap(),
|
serde_json::to_string(&b).unwrap(),
|
||||||
r#"{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":{"start":20,"startChars":null,"end":null,"specialIndent":{"type":"firstLine","val":10},"hangingChars":null,"firstLineChars":null},"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null}"#
|
r#"{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":{"start":20,"startChars":null,"end":null,"specialIndent":{"type":"firstLine","val":10},"hangingChars":null,"firstLineChars":null},"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_spacing() {
|
||||||
|
let props = ParagraphProperty::new();
|
||||||
|
let bytes = props
|
||||||
|
.line_spacing(None, None, Some(100), Some(LineSpacingType::AtLeast))
|
||||||
|
.build();
|
||||||
|
assert_eq!(
|
||||||
|
str::from_utf8(&bytes).unwrap(),
|
||||||
|
r#"<w:pPr><w:rPr /><w:spacing w:line="100" w:lineRule="atLeast" /></w:pPr>"#
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl Run {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spacing(mut self, v: i32) -> Run {
|
pub fn character_spacing(mut self, v: i32) -> Run {
|
||||||
self.run_property = self.run_property.spacing(v);
|
self.run_property = self.run_property.spacing(v);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -277,13 +277,13 @@ mod tests {
|
||||||
italic: Some(Italic::new()),
|
italic: Some(Italic::new()),
|
||||||
italic_cs: Some(ItalicCs::new()),
|
italic_cs: Some(ItalicCs::new()),
|
||||||
vanish: Some(Vanish::new()),
|
vanish: Some(Vanish::new()),
|
||||||
spacing: Some(100),
|
character_spacing: Some(CharacterSpacing::new(100)),
|
||||||
..RunProperty::default()
|
..RunProperty::default()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&run).unwrap(),
|
serde_json::to_string(&run).unwrap(),
|
||||||
r#"{"runProperty":{"sz":30,"szCs":30,"color":"C9211E","highlight":"yellow","vertAlign":null,"underline":"single","bold":true,"boldCs":true,"italic":true,"italicCs":true,"vanish":true,"spacing":100,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"#,
|
r#"{"runProperty":{"sz":30,"szCs":30,"color":"C9211E","highlight":"yellow","vertAlign":null,"underline":"single","bold":true,"boldCs":true,"italic":true,"italicCs":true,"vanish":true,"characterSpacing":100,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub struct RunProperty {
|
||||||
pub italic: Option<Italic>,
|
pub italic: Option<Italic>,
|
||||||
pub italic_cs: Option<ItalicCs>,
|
pub italic_cs: Option<ItalicCs>,
|
||||||
pub vanish: Option<Vanish>,
|
pub vanish: Option<Vanish>,
|
||||||
pub spacing: Option<i32>,
|
pub character_spacing: Option<CharacterSpacing>,
|
||||||
pub fonts: Option<RunFonts>,
|
pub fonts: Option<RunFonts>,
|
||||||
pub text_border: Option<TextBorder>,
|
pub text_border: Option<TextBorder>,
|
||||||
pub del: Option<Delete>,
|
pub del: Option<Delete>,
|
||||||
|
@ -38,7 +38,7 @@ impl RunProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spacing(mut self, spacing: i32) -> RunProperty {
|
pub fn spacing(mut self, spacing: i32) -> RunProperty {
|
||||||
self.spacing = Some(spacing);
|
self.character_spacing = Some(CharacterSpacing::new(spacing));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ impl Default for RunProperty {
|
||||||
italic_cs: None,
|
italic_cs: None,
|
||||||
vanish: None,
|
vanish: None,
|
||||||
fonts: None,
|
fonts: None,
|
||||||
spacing: None,
|
character_spacing: None,
|
||||||
text_border: None,
|
text_border: None,
|
||||||
del: None,
|
del: None,
|
||||||
ins: None,
|
ins: None,
|
||||||
|
@ -138,9 +138,6 @@ impl Default for RunProperty {
|
||||||
impl BuildXML for RunProperty {
|
impl BuildXML for RunProperty {
|
||||||
fn build(&self) -> Vec<u8> {
|
fn build(&self) -> Vec<u8> {
|
||||||
let b = XMLBuilder::new();
|
let b = XMLBuilder::new();
|
||||||
let spacing = self
|
|
||||||
.spacing
|
|
||||||
.map(|s| Spacing::new(crate::SpacingType::Value(s)));
|
|
||||||
b.open_run_property()
|
b.open_run_property()
|
||||||
.add_optional_child(&self.sz)
|
.add_optional_child(&self.sz)
|
||||||
.add_optional_child(&self.sz_cs)
|
.add_optional_child(&self.sz_cs)
|
||||||
|
@ -157,7 +154,7 @@ impl BuildXML for RunProperty {
|
||||||
.add_optional_child(&self.ins)
|
.add_optional_child(&self.ins)
|
||||||
.add_optional_child(&self.del)
|
.add_optional_child(&self.del)
|
||||||
.add_optional_child(&self.vert_align)
|
.add_optional_child(&self.vert_align)
|
||||||
.add_optional_child(&spacing)
|
.add_optional_child(&self.character_spacing)
|
||||||
.close()
|
.close()
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
@ -230,4 +227,13 @@ mod tests {
|
||||||
r#"<w:rPr><w:rFonts w:eastAsia="Hiragino" /></w:rPr>"#
|
r#"<w:rPr><w:rFonts w:eastAsia="Hiragino" /></w:rPr>"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_character_spacing() {
|
||||||
|
let c = RunProperty::new().spacing(20);
|
||||||
|
let b = c.build();
|
||||||
|
assert_eq!(
|
||||||
|
str::from_utf8(&b).unwrap(),
|
||||||
|
r#"<w:rPr><w:spacing w:val="20" /></w:rPr>"#
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
use crate::documents::BuildXML;
|
|
||||||
use crate::types::*;
|
|
||||||
use crate::xml_builder::*;
|
|
||||||
|
|
||||||
use serde::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct Spacing {
|
|
||||||
spacing_type: SpacingType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Spacing {
|
|
||||||
pub fn new(s: SpacingType) -> Spacing {
|
|
||||||
Self { spacing_type: s }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuildXML for Spacing {
|
|
||||||
fn build(&self) -> Vec<u8> {
|
|
||||||
let b = XMLBuilder::new();
|
|
||||||
b.spacing(self.spacing_type).build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
#[cfg(test)]
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_spacing() {
|
|
||||||
let b = Spacing::new(SpacingType::Value(200)).build();
|
|
||||||
assert_eq!(str::from_utf8(&b).unwrap(), r#"<w:spacing w:val="200" />"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_spacing_json() {
|
|
||||||
let s = Spacing {
|
|
||||||
spacing_type: SpacingType::Value(100),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
serde_json::to_string(&s).unwrap(),
|
|
||||||
r#"{"spacingType":{"type":"value","data":100}}"#
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -185,7 +185,7 @@ mod tests {
|
||||||
.grid_span(2);
|
.grid_span(2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
serde_json::to_string(&c).unwrap(),
|
serde_json::to_string(&c).unwrap(),
|
||||||
r#"{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"spacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineHeight":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#,
|
r#"{"children":[{"type":"paragraph","data":{"id":"12345678","children":[{"type":"run","data":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"children":[{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}]}}],"property":{"runProperty":{"sz":null,"szCs":null,"color":null,"highlight":null,"vertAlign":null,"underline":null,"bold":null,"boldCs":null,"italic":null,"italicCs":null,"vanish":null,"characterSpacing":null,"fonts":null,"textBorder":null,"del":null,"ins":null},"style":null,"numberingProperty":null,"alignment":null,"indent":null,"lineSpacing":null,"keepNext":false,"keepLines":false,"pageBreakBefore":false,"windowControl":false,"outlineLvl":null,"divId":null},"hasNumbering":false}}],"property":{"width":null,"borders":null,"gridSpan":2,"verticalMerge":null,"verticalAlign":null,"textDirection":null,"shading":null},"hasNumbering":false}"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,7 +637,7 @@ impl Docx {
|
||||||
if let RunChild::Drawing(d) = child {
|
if let RunChild::Drawing(d) = child {
|
||||||
if let Some(DrawingData::Pic(pic)) = &mut d.data {
|
if let Some(DrawingData::Pic(pic)) = &mut d.data {
|
||||||
image_ids.push(pic.id);
|
image_ids.push(pic.id);
|
||||||
let b = std::mem::replace(&mut pic.image, vec![]);
|
let b = std::mem::take(&mut pic.image);
|
||||||
images.push((pic.id, b));
|
images.push((pic.id, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -659,10 +659,7 @@ impl Docx {
|
||||||
&mut d.data
|
&mut d.data
|
||||||
{
|
{
|
||||||
image_ids.push(pic.id);
|
image_ids.push(pic.id);
|
||||||
let b = std::mem::replace(
|
let b = std::mem::take(&mut pic.image);
|
||||||
&mut pic.image,
|
|
||||||
vec![],
|
|
||||||
);
|
|
||||||
images.push((pic.id, b));
|
images.push((pic.id, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
use crate::line_spacing_type::LineSpacingType;
|
||||||
|
use crate::ReaderError;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use xml::attribute::OwnedAttribute;
|
||||||
|
|
||||||
|
pub type LineSpacingResult = Result<
|
||||||
|
(
|
||||||
|
Option<u32>,
|
||||||
|
Option<u32>,
|
||||||
|
Option<u32>,
|
||||||
|
Option<LineSpacingType>,
|
||||||
|
),
|
||||||
|
ReaderError,
|
||||||
|
>;
|
||||||
|
|
||||||
|
pub fn read_line_spacing(attributes: &[OwnedAttribute]) -> LineSpacingResult {
|
||||||
|
let mut before: Option<u32> = None;
|
||||||
|
let mut after: Option<u32> = None;
|
||||||
|
let mut line: Option<u32> = None;
|
||||||
|
let mut spacing_type: Option<LineSpacingType> = None;
|
||||||
|
for a in attributes {
|
||||||
|
let local_name = &a.name.local_name;
|
||||||
|
if local_name == "before" {
|
||||||
|
before = Some(u32::from_str(&a.value)?);
|
||||||
|
} else if local_name == "after" {
|
||||||
|
after = Some(u32::from_str(&a.value)?);
|
||||||
|
} else if local_name == "line" {
|
||||||
|
line = Some(u32::from_str(&a.value)?);
|
||||||
|
} else if local_name == "lineRule" {
|
||||||
|
spacing_type = Some(LineSpacingType::from_str(&a.value)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((before, after, line, spacing_type))
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ mod border;
|
||||||
mod id;
|
mod id;
|
||||||
mod indent;
|
mod indent;
|
||||||
mod indent_level;
|
mod indent_level;
|
||||||
|
pub(crate) mod line_spacing;
|
||||||
mod name;
|
mod name;
|
||||||
mod val;
|
mod val;
|
||||||
mod width;
|
mod width;
|
||||||
|
|
|
@ -11,7 +11,7 @@ impl ElementReader for Div {
|
||||||
r: &mut EventReader<R>,
|
r: &mut EventReader<R>,
|
||||||
attrs: &[OwnedAttribute],
|
attrs: &[OwnedAttribute],
|
||||||
) -> Result<Self, ReaderError> {
|
) -> Result<Self, ReaderError> {
|
||||||
let id = read_id(&attrs).unwrap_or_default();
|
let id = read_id(attrs).unwrap_or_default();
|
||||||
let mut div = Div::new(id);
|
let mut div = Div::new(id);
|
||||||
loop {
|
loop {
|
||||||
let e = r.next();
|
let e = r.next();
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub fn read_document_rels(
|
||||||
.ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
.ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||||
let p = find_rels_filename(&main_path)?;
|
let p = find_rels_filename(&main_path)?;
|
||||||
let p = p.to_str().ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
let p = p.to_str().ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||||
let data = read_zip(archive, &p)?;
|
let data = read_zip(archive, p)?;
|
||||||
let rels = read_rels_xml(&data[..], dir)?;
|
let rels = read_rels_xml(&data[..], dir)?;
|
||||||
Ok(rels)
|
Ok(rels)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,6 @@ use super::*;
|
||||||
use super::attributes::*;
|
use super::attributes::*;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
fn read_lineheight(attributes: &[OwnedAttribute]) -> Option<u32> {
|
|
||||||
for a in attributes {
|
|
||||||
let local_name = &a.name.local_name;
|
|
||||||
if let "line" = local_name.as_str() {
|
|
||||||
return value_to_dax(&a.value).ok().map(|l| l as u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ElementReader for Paragraph {
|
impl ElementReader for Paragraph {
|
||||||
fn read<R: Read>(
|
fn read<R: Read>(
|
||||||
r: &mut EventReader<R>,
|
r: &mut EventReader<R>,
|
||||||
|
@ -95,9 +85,9 @@ impl ElementReader for Paragraph {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
XMLElement::Spacing => {
|
XMLElement::Spacing => {
|
||||||
if let Some(line) = read_lineheight(&attributes) {
|
let (before, after, line, spacing_type) =
|
||||||
p = p.line_height(line);
|
attributes::line_spacing::read_line_spacing(&attributes)?;
|
||||||
}
|
p = p.line_spacing(before, after, line, spacing_type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
XMLElement::Justification => {
|
XMLElement::Justification => {
|
||||||
|
@ -194,7 +184,7 @@ mod tests {
|
||||||
Some(1270),
|
Some(1270),
|
||||||
None,
|
None,
|
||||||
)),
|
)),
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -230,7 +220,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: Some(Indent::new(None, None, None, Some(100))),
|
indent: Some(Indent::new(None, None, None, Some(100))),
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -261,7 +251,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: Some(Justification::new(AlignmentType::Left.to_string())),
|
alignment: Some(Justification::new(AlignmentType::Left.to_string())),
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -296,7 +286,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: true,
|
has_numbering: true,
|
||||||
|
@ -333,7 +323,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -372,7 +362,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -410,7 +400,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -460,7 +450,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
@ -502,7 +492,7 @@ mod tests {
|
||||||
numbering_property: None,
|
numbering_property: None,
|
||||||
alignment: None,
|
alignment: None,
|
||||||
indent: None,
|
indent: None,
|
||||||
line_height: None,
|
line_spacing: None,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
has_numbering: false,
|
has_numbering: false,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// One inch equates to 914400 EMUs and a centimeter is 360000 one pixel equates to 9525
|
// One inch equates to 914400 EMUs and a centimeter is 360000 one pixel equates to 9525
|
||||||
type EMU = u32;
|
type Emu = u32;
|
||||||
|
|
||||||
pub fn to_px(v: EMU) -> u32 {
|
pub fn to_px(v: Emu) -> u32 {
|
||||||
v / 9525
|
v / 9525
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_px(v: EMU) -> u32 {
|
pub fn from_px(v: Emu) -> u32 {
|
||||||
v * 9525
|
v * 9525
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::types::errors;
|
||||||
|
use crate::TypeError;
|
||||||
|
use serde::*;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum LineSpacingType {
|
||||||
|
Auto,
|
||||||
|
AtLeast,
|
||||||
|
Exact,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for LineSpacingType {
|
||||||
|
type Err = errors::TypeError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"auto" => Ok(LineSpacingType::Auto),
|
||||||
|
"atLeast" => Ok(LineSpacingType::AtLeast),
|
||||||
|
"exact" => Ok(LineSpacingType::Exact),
|
||||||
|
_ => Err(TypeError::FromStrError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,11 @@ pub mod errors;
|
||||||
pub mod font_pitch_type;
|
pub mod font_pitch_type;
|
||||||
pub mod height_rule;
|
pub mod height_rule;
|
||||||
pub mod level_suffix_type;
|
pub mod level_suffix_type;
|
||||||
|
pub mod line_spacing_type;
|
||||||
pub mod page_margin;
|
pub mod page_margin;
|
||||||
pub mod page_orientation_type;
|
pub mod page_orientation_type;
|
||||||
pub mod section_type;
|
pub mod section_type;
|
||||||
pub mod shd_type;
|
pub mod shd_type;
|
||||||
pub mod spacing;
|
|
||||||
pub mod special_indent_type;
|
pub mod special_indent_type;
|
||||||
pub mod style_type;
|
pub mod style_type;
|
||||||
pub mod table_alignment_type;
|
pub mod table_alignment_type;
|
||||||
|
@ -33,11 +33,11 @@ pub use errors::*;
|
||||||
pub use font_pitch_type::*;
|
pub use font_pitch_type::*;
|
||||||
pub use height_rule::*;
|
pub use height_rule::*;
|
||||||
pub use level_suffix_type::*;
|
pub use level_suffix_type::*;
|
||||||
|
pub use line_spacing_type::*;
|
||||||
pub use page_margin::*;
|
pub use page_margin::*;
|
||||||
pub use page_orientation_type::*;
|
pub use page_orientation_type::*;
|
||||||
pub use section_type::*;
|
pub use section_type::*;
|
||||||
pub use shd_type::*;
|
pub use shd_type::*;
|
||||||
pub use spacing::*;
|
|
||||||
pub use special_indent_type::*;
|
pub use special_indent_type::*;
|
||||||
pub use style_type::*;
|
pub use style_type::*;
|
||||||
pub use table_alignment_type::*;
|
pub use table_alignment_type::*;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
use serde::ser::{SerializeStruct, Serializer};
|
|
||||||
use serde::*;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Deserialize)]
|
|
||||||
pub enum SpacingType {
|
|
||||||
Value(i32),
|
|
||||||
Line(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for SpacingType {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
SpacingType::Value(ref s) => {
|
|
||||||
let mut t = serializer.serialize_struct("SpacingType", 2)?;
|
|
||||||
t.serialize_field("type", "value")?;
|
|
||||||
t.serialize_field("data", s)?;
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
SpacingType::Line(ref s) => {
|
|
||||||
let mut t = serializer.serialize_struct("SpacingType", 2)?;
|
|
||||||
t.serialize_field("type", "line")?;
|
|
||||||
t.serialize_field("data", s)?;
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::XMLBuilder;
|
use super::XMLBuilder;
|
||||||
use super::XmlEvent;
|
use super::XmlEvent;
|
||||||
|
use crate::types::line_spacing_type::LineSpacingType;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
const EXPECT_MESSAGE: &str = "should write buf";
|
const EXPECT_MESSAGE: &str = "should write buf";
|
||||||
|
@ -153,25 +154,53 @@ impl XMLBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// i.e. <w:spacing ... >
|
// i.e. <w:spacing ... >
|
||||||
pub(crate) fn spacing(mut self, s: crate::types::SpacingType) -> Self {
|
pub(crate) fn spacing(mut self, s: i32) -> Self {
|
||||||
match s {
|
self.writer
|
||||||
SpacingType::Value(v) => {
|
.write(XmlEvent::start_element("w:spacing").attr("w:val", &format!("{}", s)))
|
||||||
self.writer
|
.expect(EXPECT_MESSAGE);
|
||||||
.write(XmlEvent::start_element("w:spacing").attr("w:val", &format!("{}", v)))
|
self.close()
|
||||||
.expect(EXPECT_MESSAGE);
|
}
|
||||||
self.close()
|
|
||||||
}
|
// i.e. <w:spacing ... >
|
||||||
SpacingType::Line(v) => {
|
pub(crate) fn line_spacing(
|
||||||
self.writer
|
mut self,
|
||||||
.write(
|
before: Option<u32>,
|
||||||
XmlEvent::start_element("w:spacing")
|
after: Option<u32>,
|
||||||
.attr("w:line", &format!("{}", v))
|
line: Option<u32>,
|
||||||
.attr("w:lineRule", "auto"),
|
spacing: Option<LineSpacingType>,
|
||||||
)
|
) -> Self {
|
||||||
.expect(EXPECT_MESSAGE);
|
let mut xml_event = XmlEvent::start_element("w:spacing");
|
||||||
self.close()
|
let before_val: String;
|
||||||
|
let after_val: String;
|
||||||
|
let line_val: String;
|
||||||
|
|
||||||
|
if let Some(before) = before {
|
||||||
|
before_val = format!("{}", before);
|
||||||
|
xml_event = xml_event.attr("w:before", &before_val)
|
||||||
|
}
|
||||||
|
if let Some(after) = after {
|
||||||
|
after_val = format!("{}", after);
|
||||||
|
xml_event = xml_event.attr("w:after", &after_val)
|
||||||
|
}
|
||||||
|
if let Some(line) = line {
|
||||||
|
line_val = format!("{}", line);
|
||||||
|
xml_event = xml_event.attr("w:line", &line_val)
|
||||||
|
}
|
||||||
|
if let Some(spacing_type) = spacing {
|
||||||
|
match spacing_type {
|
||||||
|
LineSpacingType::Auto => {
|
||||||
|
xml_event = xml_event.attr("w:lineRule", "auto");
|
||||||
|
}
|
||||||
|
LineSpacingType::AtLeast => {
|
||||||
|
xml_event = xml_event.attr("w:lineRule", "atLeast");
|
||||||
|
}
|
||||||
|
LineSpacingType::Exact => {
|
||||||
|
xml_event = xml_event.attr("w:lineRule", "exact");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.writer.write(xml_event).expect(EXPECT_MESSAGE);
|
||||||
|
self.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -413,7 +442,7 @@ impl XMLBuilder {
|
||||||
w = w.attr("w:linePitch", &line_pitch_string);
|
w = w.attr("w:linePitch", &line_pitch_string);
|
||||||
}
|
}
|
||||||
if char_space.is_some() {
|
if char_space.is_some() {
|
||||||
w = w.attr("w:charSpace", &&char_space_string);
|
w = w.attr("w:charSpace", &char_space_string);
|
||||||
}
|
}
|
||||||
self.writer.write(w).expect(EXPECT_MESSAGE);
|
self.writer.write(w).expect(EXPECT_MESSAGE);
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,8 @@ impl XMLBuilder {
|
||||||
self.writer
|
self.writer
|
||||||
.write(
|
.write(
|
||||||
XmlEvent::start_element("Override")
|
XmlEvent::start_element("Override")
|
||||||
.attr("PartName", &name)
|
.attr("PartName", name)
|
||||||
.attr("ContentType", &content_type),
|
.attr("ContentType", content_type),
|
||||||
)
|
)
|
||||||
.expect("should write to buf");
|
.expect("should write to buf");
|
||||||
self.close()
|
self.close()
|
||||||
|
@ -68,8 +68,8 @@ impl XMLBuilder {
|
||||||
self.writer
|
self.writer
|
||||||
.write(
|
.write(
|
||||||
XmlEvent::start_element("Default")
|
XmlEvent::start_element("Default")
|
||||||
.attr("ContentType", &extension)
|
.attr("ContentType", extension)
|
||||||
.attr("Extension", &name),
|
.attr("Extension", name),
|
||||||
)
|
)
|
||||||
.expect("should write to buf");
|
.expect("should write to buf");
|
||||||
self.close()
|
self.close()
|
||||||
|
|
|
@ -71,7 +71,7 @@ where
|
||||||
format!("word/webextensions/webextension{}.xml", i + 1),
|
format!("word/webextensions/webextension{}.xml", i + 1),
|
||||||
options,
|
options,
|
||||||
)?;
|
)?;
|
||||||
zip.write_all(&ext)?;
|
zip.write_all(ext)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -415,3 +415,28 @@ pub fn date() -> Result<(), DocxError> {
|
||||||
.pack(file)?;
|
.pack(file)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn line_spacing() -> Result<(), DocxError> {
|
||||||
|
let path = std::path::Path::new("./tests/output/line_spacing.docx");
|
||||||
|
let file = std::fs::File::create(&path).unwrap();
|
||||||
|
Docx::new()
|
||||||
|
.add_paragraph(
|
||||||
|
Paragraph::new()
|
||||||
|
.add_run(Run::new().add_text(DUMMY))
|
||||||
|
.line_spacing(Some(300), None, Some(300), Some(LineSpacingType::Auto)),
|
||||||
|
)
|
||||||
|
.add_paragraph(
|
||||||
|
Paragraph::new()
|
||||||
|
.add_run(Run::new().add_text(DUMMY))
|
||||||
|
.line_spacing(None, None, Some(300), Some(LineSpacingType::AtLeast)),
|
||||||
|
)
|
||||||
|
.add_paragraph(
|
||||||
|
Paragraph::new()
|
||||||
|
.add_run(Run::new().add_text(DUMMY).character_spacing(100))
|
||||||
|
.line_spacing(None, Some(300), Some(300), Some(LineSpacingType::Exact)),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.pack(file)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -259,3 +259,18 @@ pub fn read_extended_comment() {
|
||||||
file.write_all(json.as_bytes()).unwrap();
|
file.write_all(json.as_bytes()).unwrap();
|
||||||
file.flush().unwrap();
|
file.flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn read_line_spacing() {
|
||||||
|
let mut file = File::open("../fixtures/line_spacing/line_spacing.docx").unwrap();
|
||||||
|
let mut buf = vec![];
|
||||||
|
file.read_to_end(&mut buf).unwrap();
|
||||||
|
let json = read_docx(&buf).unwrap().json();
|
||||||
|
|
||||||
|
assert_json_snapshot!("line_spacing", &json);
|
||||||
|
|
||||||
|
let path = std::path::Path::new("./tests/output/line_spacing.json");
|
||||||
|
let mut file = std::fs::File::create(&path).unwrap();
|
||||||
|
file.write_all(json.as_bytes()).unwrap();
|
||||||
|
file.flush().unwrap();
|
||||||
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
||||||
|
export type LineSpacingJSON = {
|
||||||
|
before: number | null;
|
||||||
|
after: number | null;
|
||||||
|
line: number | null;
|
||||||
|
lineRule: {
|
||||||
|
type: "atLeast" | "auto" | "exact";
|
||||||
|
val: number;
|
||||||
|
} | null;
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
import { RunJSON, RunPropertyJSON } from "./run";
|
import { RunJSON, RunPropertyJSON } from "./run";
|
||||||
import { IndentJSON } from "./indent";
|
import { IndentJSON } from "./indent";
|
||||||
import { CommentRangeStartJSON, CommentRangeEndJSON } from "..";
|
import { CommentRangeStartJSON, CommentRangeEndJSON } from "..";
|
||||||
|
import {LineSpacingJSON} from "./line_spacing";
|
||||||
|
|
||||||
export type ParagraphChildJSON =
|
export type ParagraphChildJSON =
|
||||||
| RunJSON
|
| RunJSON
|
||||||
|
@ -22,7 +23,7 @@ export type ParagraphPropertyJSON = {
|
||||||
numberingProperty: NumberingPropertyJSON | null;
|
numberingProperty: NumberingPropertyJSON | null;
|
||||||
alignment: "left" | "center" | "right" | "justified" | "both";
|
alignment: "left" | "center" | "right" | "justified" | "both";
|
||||||
indent: IndentJSON | null;
|
indent: IndentJSON | null;
|
||||||
lineHeight: number | null;
|
lineSpacing: LineSpacingJSON | null;
|
||||||
divId: string | null;
|
divId: string | null;
|
||||||
keepNext: boolean;
|
keepNext: boolean;
|
||||||
keepLines: boolean;
|
keepLines: boolean;
|
||||||
|
|
|
@ -1,169 +1,181 @@
|
||||||
import { Run, RunProperty, RunFonts, createDefaultRunProperty } from "./run";
|
import {Run, RunProperty, RunFonts, createDefaultRunProperty} from "./run";
|
||||||
import { Insert } from "./insert";
|
import {Insert} from "./insert";
|
||||||
import { Delete } from "./delete";
|
import {Delete} from "./delete";
|
||||||
import { BookmarkStart } from "./bookmark-start";
|
import {BookmarkStart} from "./bookmark-start";
|
||||||
import { BookmarkEnd } from "./bookmark-end";
|
import {BookmarkEnd} from "./bookmark-end";
|
||||||
import { Comment } from "./comment";
|
import {Comment} from "./comment";
|
||||||
import { CommentEnd } from "./comment-end";
|
import {CommentEnd} from "./comment-end";
|
||||||
|
|
||||||
export type ParagraphChild =
|
export type ParagraphChild =
|
||||||
| Run
|
| Run
|
||||||
| Insert
|
| Insert
|
||||||
| Delete
|
| Delete
|
||||||
| BookmarkStart
|
| BookmarkStart
|
||||||
| BookmarkEnd
|
| BookmarkEnd
|
||||||
| Comment
|
| Comment
|
||||||
| CommentEnd;
|
| CommentEnd;
|
||||||
|
|
||||||
export type AlignmentType =
|
export type AlignmentType =
|
||||||
| "center"
|
| "center"
|
||||||
| "left"
|
| "left"
|
||||||
| "right"
|
| "right"
|
||||||
| "both"
|
| "both"
|
||||||
| "justified"
|
| "justified"
|
||||||
| "distribute"
|
| "distribute"
|
||||||
| "end";
|
| "end";
|
||||||
|
|
||||||
export type SpecialIndentKind = "firstLine" | "hanging";
|
export type SpecialIndentKind = "firstLine" | "hanging";
|
||||||
|
|
||||||
|
export type LineSpacingType = "atLeast" | "auto" | "exact"
|
||||||
|
|
||||||
export type ParagraphProperty = {
|
export type ParagraphProperty = {
|
||||||
align?: AlignmentType;
|
align?: AlignmentType;
|
||||||
styleId?: string;
|
styleId?: string;
|
||||||
indent?: {
|
indent?: {
|
||||||
left: number;
|
left: number;
|
||||||
specialIndentKind?: SpecialIndentKind;
|
specialIndentKind?: SpecialIndentKind;
|
||||||
specialIndentSize?: number;
|
specialIndentSize?: number;
|
||||||
};
|
};
|
||||||
numbering?: {
|
numbering?: {
|
||||||
id: number;
|
id: number;
|
||||||
level: number;
|
level: number;
|
||||||
};
|
};
|
||||||
lineHeight?: number;
|
lineSpacing?: {
|
||||||
runProperty: RunProperty;
|
before?: number;
|
||||||
keepNext: boolean;
|
after?: number;
|
||||||
keepLines: boolean;
|
line?: number;
|
||||||
pageBreakBefore: boolean;
|
lineRule?: LineSpacingType;
|
||||||
windowControl: boolean;
|
};
|
||||||
|
runProperty: RunProperty;
|
||||||
|
keepNext: boolean;
|
||||||
|
keepLines: boolean;
|
||||||
|
pageBreakBefore: boolean;
|
||||||
|
windowControl: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createDefaultParagraphProperty = (): ParagraphProperty => {
|
export const createDefaultParagraphProperty = (): ParagraphProperty => {
|
||||||
return {
|
return {
|
||||||
runProperty: createDefaultRunProperty(),
|
runProperty: createDefaultRunProperty(),
|
||||||
keepNext: false,
|
keepNext: false,
|
||||||
keepLines: false,
|
keepLines: false,
|
||||||
pageBreakBefore: false,
|
pageBreakBefore: false,
|
||||||
windowControl: false,
|
windowControl: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Paragraph {
|
export class Paragraph {
|
||||||
hasNumberings = false;
|
hasNumberings = false;
|
||||||
children: ParagraphChild[] = [];
|
children: ParagraphChild[] = [];
|
||||||
property: ParagraphProperty = createDefaultParagraphProperty();
|
property: ParagraphProperty = createDefaultParagraphProperty();
|
||||||
|
|
||||||
addRun(run: Run) {
|
addRun(run: Run) {
|
||||||
this.children.push(run);
|
this.children.push(run);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addInsert(ins: Insert) {
|
addInsert(ins: Insert) {
|
||||||
this.children.push(ins);
|
this.children.push(ins);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addDelete(del: Delete) {
|
addDelete(del: Delete) {
|
||||||
this.children.push(del);
|
this.children.push(del);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addBookmarkStart(id: number, name: string) {
|
addBookmarkStart(id: number, name: string) {
|
||||||
this.children.push(new BookmarkStart(id, name));
|
this.children.push(new BookmarkStart(id, name));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addBookmarkEnd(id: number) {
|
addBookmarkEnd(id: number) {
|
||||||
this.children.push(new BookmarkEnd(id));
|
this.children.push(new BookmarkEnd(id));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommentStart(comment: Comment) {
|
addCommentStart(comment: Comment) {
|
||||||
this.children.push(comment);
|
this.children.push(comment);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommentEnd(end: CommentEnd) {
|
addCommentEnd(end: CommentEnd) {
|
||||||
this.children.push(end);
|
this.children.push(end);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
align(type: AlignmentType) {
|
align(type: AlignmentType) {
|
||||||
this.property.align = type;
|
this.property.align = type;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
style(id: string) {
|
style(id: string) {
|
||||||
this.property.styleId = id;
|
this.property.styleId = id;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
indent(
|
indent(
|
||||||
left: number,
|
left: number,
|
||||||
specialIndentKind?: SpecialIndentKind,
|
specialIndentKind?: SpecialIndentKind,
|
||||||
specialIndentSize?: number
|
specialIndentSize?: number
|
||||||
) {
|
) {
|
||||||
this.property.indent = { left, specialIndentKind, specialIndentSize };
|
this.property.indent = {left, specialIndentKind, specialIndentSize};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
numbering(id: number, level: number) {
|
numbering(id: number, level: number) {
|
||||||
this.hasNumberings = true;
|
this.hasNumberings = true;
|
||||||
this.property.numbering = { id, level };
|
this.property.numbering = {id, level};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
lineHeight(lineHeight: number) {
|
lineSpacing(before?: number, after?: number, line?: number, lineRule?: LineSpacingType) {
|
||||||
this.property = { ...this.property, lineHeight };
|
this.property.lineSpacing = {
|
||||||
return this;
|
before,
|
||||||
}
|
after,
|
||||||
|
line,
|
||||||
|
lineRule
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
keepNext(v: boolean) {
|
keepNext(v: boolean) {
|
||||||
this.property = { ...this.property, keepNext: v };
|
this.property = {...this.property, keepNext: v};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
keepLines(v: boolean) {
|
keepLines(v: boolean) {
|
||||||
this.property = { ...this.property, keepLines: v };
|
this.property = {...this.property, keepLines: v};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
pageBreakBefore(v: boolean) {
|
pageBreakBefore(v: boolean) {
|
||||||
this.property = { ...this.property, pageBreakBefore: v };
|
this.property = {...this.property, pageBreakBefore: v};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
windowControl(v: boolean) {
|
windowControl(v: boolean) {
|
||||||
this.property = { ...this.property, windowControl: v };
|
this.property = {...this.property, windowControl: v};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// run property
|
// run property
|
||||||
size(size: number) {
|
size(size: number) {
|
||||||
this.property.runProperty = { ...this.property.runProperty, size };
|
this.property.runProperty = {...this.property.runProperty, size};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bold() {
|
bold() {
|
||||||
this.property.runProperty = { ...this.property.runProperty, bold: true };
|
this.property.runProperty = {...this.property.runProperty, bold: true};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
italic() {
|
italic() {
|
||||||
this.property.runProperty = { ...this.property.runProperty, italic: true };
|
this.property.runProperty = {...this.property.runProperty, italic: true};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
fonts(fonts: RunFonts) {
|
fonts(fonts: RunFonts) {
|
||||||
this.property.runProperty = { ...this.property.runProperty, fonts };
|
this.property.runProperty = {...this.property.runProperty, fonts};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,14 @@ impl Paragraph {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_height(mut self, line_height: u32) -> Self {
|
pub fn line_spacing(
|
||||||
self.0 = self.0.line_height(line_height);
|
mut self,
|
||||||
|
before: Option<u32>,
|
||||||
|
after: Option<u32>,
|
||||||
|
line: Option<u32>,
|
||||||
|
spacing_type: Option<docx_rs::LineSpacingType>,
|
||||||
|
) -> Self {
|
||||||
|
self.0 = self.0.line_spacing(before, after, line, spacing_type);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -62,6 +62,14 @@ describe("reader", () => {
|
||||||
const json = w.readDocx(buffer);
|
const json = w.readDocx(buffer);
|
||||||
expect(json).toMatchSnapshot();
|
expect(json).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should read line spacing docx", () => {
|
||||||
|
const buffer = readFileSync(
|
||||||
|
"../fixtures/line_spacing/line_spacing.docx"
|
||||||
|
);
|
||||||
|
const json = w.readDocx(buffer);
|
||||||
|
expect(json).toMatchSnapshot();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("writer", () => {
|
describe("writer", () => {
|
||||||
|
@ -336,4 +344,18 @@ describe("writer", () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should write line spacing", () => {
|
||||||
|
const p = new w.Paragraph()
|
||||||
|
.addRun(new w.Run().addText("Hello "))
|
||||||
|
.lineSpacing(100, "", 100, 1);
|
||||||
|
const buffer = new w.Docx().addParagraph(p).build();
|
||||||
|
writeFileSync("../output/line_spacing.docx", buffer);
|
||||||
|
const z = new Zip(Buffer.from(buffer));
|
||||||
|
for (const e of z.getEntries()) {
|
||||||
|
if (e.entryName.match(/document.xml|numbering.xml/)) {
|
||||||
|
expect(z.readAsText(e)).toMatchSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue