Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docx-core/src/documents/elements/paragraph_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub struct ParagraphProperty {
pub page_break_before: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub widow_control: Option<bool>,
/// `w:wordWrap`. Word's switch between word-level and character-level
/// line breaking for East Asian text: `1` keeps a Hangul eojeol whole,
/// `0` allows a break at any syllable. Unlike the flags around it, the
/// `0` case carries meaning, so it is recorded rather than dropped.
#[serde(skip_serializing_if = "Option::is_none")]
pub word_wrap: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub outline_lvl: Option<OutlineLvl>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -149,6 +155,11 @@ impl ParagraphProperty {
self
}

pub fn word_wrap(mut self, v: bool) -> Self {
self.word_wrap = Some(v);
self
}

pub fn add_tab(mut self, t: Tab) -> Self {
self.tabs.push(t);
self
Expand Down
51 changes: 51 additions & 0 deletions docx-core/src/reader/paragraph_property.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ impl ElementReader for ParagraphProperty {
p.widow_control = Some(true);
}
}
XMLElement::WordWrap => {
// Recorded either way, unlike the flags above it:
// `w:val="0"` is the meaningful setting, asking for
// character-level breaking of East Asian text.
// `read_bool` already yields true for a bare
// `<w:wordWrap/>`, which is what OOXML means by it.
p.word_wrap = Some(read_bool(&attributes));
}
XMLElement::ParagraphPropertyChange => {
if let Ok(ppr_change) = ParagraphPropertyChange::read(r, &attributes) {
p.paragraph_property_change = Some(ppr_change);
Expand Down Expand Up @@ -190,4 +198,47 @@ mod tests {
let shd = p.shading.expect("paragraph shading must be parsed");
assert_eq!(shd.fill, "F4F4F4");
}

#[test]
fn test_read_word_wrap() {
// `w:val="0"` asks for character-level breaking of East Asian text.
// It has to survive as `Some(false)`: dropping it is indistinguishable
// from the property being absent (office2pdf#730).
let xml = r#"<w:pPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:wordWrap w:val="0"/>
</w:pPr>"#;
let mut parser = EventReader::new(Cursor::new(xml));
loop {
if let Ok(XmlEvent::StartElement { name, .. }) = parser.next() {
if name.local_name == "pPr" {
break;
}
}
}
let p = ParagraphProperty::read(&mut parser, &[]).unwrap();
assert_eq!(p.word_wrap, Some(false));
}

/// A bare `<w:wordWrap/>` means word-level breaking, and an absent one
/// leaves the choice to the style chain — the two must not collapse.
#[test]
fn test_read_word_wrap_defaults() {
let read = |body: &str| {
let xml = format!(
r#"<w:pPr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">{body}</w:pPr>"#
);
let mut parser = EventReader::new(Cursor::new(xml));
loop {
if let Ok(XmlEvent::StartElement { name, .. }) = parser.next() {
if name.local_name == "pPr" {
break;
}
}
}
ParagraphProperty::read(&mut parser, &[]).unwrap().word_wrap
};
assert_eq!(read(r#"<w:wordWrap/>"#), Some(true));
assert_eq!(read(r#"<w:wordWrap w:val="1"/>"#), Some(true));
assert_eq!(read(""), None);
}
}
2 changes: 2 additions & 0 deletions docx-core/src/reader/xml_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub enum XMLElement {
KeepLines,
PageBreakBefore,
WidowControl,
WordWrap,
DivId,
Div,
DivsChild,
Expand Down Expand Up @@ -417,6 +418,7 @@ impl FromStr for XMLElement {
"keepLines" => Ok(XMLElement::KeepLines),
"pageBreakBefore" => Ok(XMLElement::PageBreakBefore),
"widowControl" => Ok(XMLElement::WidowControl),
"wordWrap" => Ok(XMLElement::WordWrap),
"headerReference" => Ok(XMLElement::HeaderReference),
"footerReference" => Ok(XMLElement::FooterReference),
"titlePg" => Ok(XMLElement::TitlePg),
Expand Down
6 changes: 6 additions & 0 deletions docx-core/tests/snapshots/lib__reader__line_spacing.snap.new
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: docx-core/tests/reader.rs
assertion_line: 287
expression: "&json"
---
"{\n \"contentType\": {\n \"types\": {\n \"/_rels/.rels\": \"application/vnd.openxmlformats-package.relationships+xml\",\n \"/docProps/app.xml\": \"application/vnd.openxmlformats-officedocument.extended-properties+xml\",\n \"/docProps/core.xml\": \"application/vnd.openxmlformats-package.core-properties+xml\",\n \"/docProps/custom.xml\": \"application/vnd.openxmlformats-officedocument.custom-properties+xml\",\n \"/word/_rels/document.xml.rels\": \"application/vnd.openxmlformats-package.relationships+xml\",\n \"/word/comments.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml\",\n \"/word/commentsExtended.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml\",\n \"/word/document.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\",\n \"/word/fontTable.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml\",\n \"/word/header1.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml\",\n \"/word/numbering.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml\",\n \"/word/settings.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml\",\n \"/word/styles.xml\": \"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\"\n },\n \"web_extension_count\": 1,\n \"custom_xml_count\": 1,\n \"header_count\": 1,\n \"footer_count\": 0\n },\n \"rels\": {\n \"rels\": [\n [\n \"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\",\n \"rId1\",\n \"docProps/core.xml\"\n ],\n [\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\",\n \"rId2\",\n \"docProps/app.xml\"\n ],\n [\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\",\n \"rId3\",\n \"word/document.xml\"\n ],\n [\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties\",\n \"rId4\",\n \"docProps/custom.xml\"\n ]\n ]\n },\n \"documentRels\": {\n \"hasComments\": false,\n \"hasNumberings\": false,\n \"hasFootnotes\": false,\n \"images\": [],\n \"hyperlinks\": [],\n \"customXmlCount\": 0,\n \"headerCount\": 1,\n \"footerCount\": 0\n },\n \"docProps\": {\n \"app\": {},\n \"core\": {\n \"config\": {\n \"created\": null,\n \"creator\": null,\n \"description\": null,\n \"language\": null,\n \"lastModifiedBy\": null,\n \"modified\": null,\n \"revision\": null,\n \"subject\": null,\n \"title\": null\n }\n },\n \"custom\": {\n \"properties\": {}\n }\n },\n \"styles\": {\n \"docDefaults\": {\n \"runPropertyDefault\": {\n \"runProperty\": {}\n },\n \"paragraphPropertyDefault\": {\n \"paragraphProperty\": {\n \"runProperty\": {},\n \"tabs\": []\n }\n }\n },\n \"styles\": [\n {\n \"styleId\": \"Normal\",\n \"name\": \"Normal\",\n \"styleType\": \"paragraph\",\n \"runProperty\": {},\n \"paragraphProperty\": {\n \"runProperty\": {},\n \"tabs\": []\n },\n \"tableProperty\": {\n \"width\": {\n \"width\": 0,\n \"widthType\": \"auto\"\n },\n \"justification\": \"left\",\n \"borders\": {\n \"top\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"top\",\n \"space\": 0\n },\n \"left\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"left\",\n \"space\": 0\n },\n \"bottom\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"bottom\",\n \"space\": 0\n },\n \"right\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"right\",\n \"space\": 0\n },\n \"insideH\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"insideH\",\n \"space\": 0\n },\n \"insideV\": {\n \"borderType\": \"single\",\n \"size\": 2,\n \"color\": \"000000\",\n \"position\": \"insideV\",\n \"space\": 0\n }\n }\n },\n \"tableCellProperty\": {\n \"width\": null,\n \"borders\": null,\n \"gridSpan\": null,\n \"verticalMerge\": null,\n \"verticalAlign\": null,\n \"textDirection\": null,\n \"shading\": null\n },\n \"basedOn\": \"Normal\",\n \"next\": null,\n \"default\": false\n }\n ]\n },\n \"document\": {\n \"children\": [\n {\n \"type\": \"paragraph\",\n \"data\": {\n \"id\": \"00000001\",\n \"children\": [\n {\n \"type\": \"run\",\n \"data\": {\n \"runProperty\": {},\n \"children\": [\n {\n \"type\": \"text\",\n \"data\": {\n \"preserveSpace\": true,\n \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\"\n }\n }\n ]\n }\n }\n ],\n \"property\": {\n \"runProperty\": {},\n \"lineSpacing\": {\n \"lineRule\": \"auto\",\n \"before\": 300,\n \"line\": 300\n },\n \"tabs\": []\n },\n \"hasNumbering\": false\n }\n },\n {\n \"type\": \"paragraph\",\n \"data\": {\n \"id\": \"00000002\",\n \"children\": [\n {\n \"type\": \"run\",\n \"data\": {\n \"runProperty\": {},\n \"children\": [\n {\n \"type\": \"text\",\n \"data\": {\n \"preserveSpace\": true,\n \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\"\n }\n }\n ]\n }\n }\n ],\n \"property\": {\n \"runProperty\": {},\n \"lineSpacing\": {\n \"lineRule\": \"atLeast\",\n \"line\": 300\n },\n \"tabs\": []\n },\n \"hasNumbering\": false\n }\n },\n {\n \"type\": \"paragraph\",\n \"data\": {\n \"id\": \"00000003\",\n \"children\": [\n {\n \"type\": \"run\",\n \"data\": {\n \"runProperty\": {\n \"characterSpacing\": 100\n },\n \"children\": [\n {\n \"type\": \"text\",\n \"data\": {\n \"preserveSpace\": true,\n \"text\": \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\"\n }\n }\n ]\n }\n }\n ],\n \"property\": {\n \"runProperty\": {},\n \"lineSpacing\": {\n \"lineRule\": \"exact\",\n \"after\": 300,\n \"line\": 300\n },\n \"tabs\": []\n },\n \"hasNumbering\": false\n }\n }\n ],\n \"sectionProperty\": {\n \"pageSize\": {\n \"w\": 11906,\n \"h\": 16838,\n \"orient\": null\n },\n \"pageMargin\": {\n \"top\": 1985,\n \"left\": 1701,\n \"bottom\": 1701,\n \"right\": 1701,\n \"header\": 851,\n \"footer\": 992,\n \"gutter\": 0\n },\n \"columns\": 1,\n \"space\": 425,\n \"titlePg\": false,\n \"textDirection\": \"lrTb\",\n \"docGrid\": {\n \"gridType\": \"lines\",\n \"linePitch\": 360,\n \"charSpace\": null\n },\n \"headerReference\": {\n \"headerType\": \"default\",\n \"id\": \"rId4\"\n },\n \"header\": [\n \"rId4\",\n {\n \"hasNumbering\": false,\n \"children\": []\n }\n ]\n },\n \"hasNumbering\": false\n },\n \"comments\": {\n \"comments\": []\n },\n \"numberings\": {\n \"abstractNums\": [],\n \"numberings\": []\n },\n \"settings\": {\n \"defaultTabStop\": 840,\n \"zoom\": 100,\n \"docId\": null,\n \"docVars\": [],\n \"evenAndOddHeaders\": false,\n \"adjustLineHeightInTable\": true\n },\n \"fontTable\": {},\n \"media\": [],\n \"commentsExtended\": {\n \"children\": []\n },\n \"webSettings\": {\n \"divs\": []\n },\n \"taskpanes\": null,\n \"taskpanesRels\": {\n \"rels\": []\n },\n \"webExtensions\": [],\n \"customItems\": [],\n \"customItemProps\": [],\n \"customItemRels\": [],\n \"themes\": [],\n \"images\": [],\n \"hyperlinks\": [],\n \"footnotes\": {\n \"footnotes\": []\n }\n}"
6 changes: 6 additions & 0 deletions docx-core/tests/snapshots/lib__reader__read_bom.snap.new

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docx-core/tests/snapshots/lib__reader__read_bookmark.snap.new

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docx-core/tests/snapshots/lib__reader__read_comment.snap.new

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading
Loading