parent
0ff716318a
commit
00e01479e6
|
@ -1,16 +1,16 @@
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct OutlineLvl {
|
||||
pub v: usize,
|
||||
}
|
||||
|
||||
impl OutlineLvl {
|
||||
pub fn new(v: usize) -> OutlineLvl {
|
||||
assert!(v < 10, "outline level should be less than 10");
|
||||
OutlineLvl { v }
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,25 @@ impl BuildXML for OutlineLvl {
|
|||
}
|
||||
}
|
||||
|
||||
impl Serialize for OutlineLvl {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u32(self.v as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{BuildXML, OutlineLvl};
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_outline_lvl_build(){
|
||||
fn test_outline_lvl_build() {
|
||||
let bytes = OutlineLvl::new(1).build();
|
||||
assert_eq!(std::str::from_utf8(&bytes).unwrap(),r#"<w:outlineLvl w:val="1" />"#);
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&bytes).unwrap(),
|
||||
r#"<w:outlineLvl w:val="1" />"#
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -203,12 +203,12 @@ impl Paragraph {
|
|||
self
|
||||
}
|
||||
|
||||
pub(crate) fn hanging_chars(mut self, chars: i32) -> Paragraph {
|
||||
pub fn hanging_chars(mut self, chars: i32) -> Paragraph {
|
||||
self.property = self.property.hanging_chars(chars);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn first_line_chars(mut self, chars: i32) -> Paragraph {
|
||||
pub fn first_line_chars(mut self, chars: i32) -> Paragraph {
|
||||
self.property = self.property.first_line_chars(chars);
|
||||
self
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ impl Paragraph {
|
|||
self
|
||||
}
|
||||
|
||||
pub(crate) fn run_property(mut self, p: RunProperty) -> Self {
|
||||
pub fn run_property(mut self, p: RunProperty) -> Self {
|
||||
self.property.run_property = p;
|
||||
self
|
||||
}
|
||||
|
|
|
@ -105,6 +105,9 @@ impl ParagraphProperty {
|
|||
}
|
||||
|
||||
pub fn outline_lvl(mut self, v: usize) -> Self {
|
||||
if v >= 10 {
|
||||
return self;
|
||||
}
|
||||
self.outline_lvl = Some(OutlineLvl::new(v));
|
||||
self
|
||||
}
|
||||
|
|
|
@ -111,16 +111,21 @@ impl Style {
|
|||
self
|
||||
}
|
||||
|
||||
pub(crate) fn hanging_chars(mut self, chars: i32) -> Self {
|
||||
pub fn hanging_chars(mut self, chars: i32) -> Self {
|
||||
self.paragraph_property = self.paragraph_property.hanging_chars(chars);
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn first_line_chars(mut self, chars: i32) -> Self {
|
||||
pub fn first_line_chars(mut self, chars: i32) -> Self {
|
||||
self.paragraph_property = self.paragraph_property.first_line_chars(chars);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn outline_lvl(mut self, l: usize) -> Self {
|
||||
self.paragraph_property = self.paragraph_property.outline_lvl(l);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn table_property(mut self, p: TableProperty) -> Self {
|
||||
self.table_property = p;
|
||||
self
|
||||
|
|
|
@ -25,6 +25,7 @@ mod mc_fallback;
|
|||
mod numbering_property;
|
||||
mod numberings;
|
||||
mod paragraph;
|
||||
mod paragraph_property;
|
||||
mod read_zip;
|
||||
mod rels;
|
||||
mod run;
|
||||
|
|
|
@ -7,7 +7,6 @@ use xml::reader::{EventReader, XmlEvent};
|
|||
use super::*;
|
||||
|
||||
use super::attributes::*;
|
||||
use crate::types::*;
|
||||
|
||||
impl ElementReader for Paragraph {
|
||||
fn read<R: Read>(
|
||||
|
@ -71,63 +70,14 @@ impl ElementReader for Paragraph {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::Indent => {
|
||||
let (start, end, special, start_chars, hanging_chars, first_line_chars) =
|
||||
read_indent(&attributes)?;
|
||||
p = p.indent(start, special, end, start_chars);
|
||||
|
||||
if let Some(chars) = hanging_chars {
|
||||
p = p.hanging_chars(chars);
|
||||
}
|
||||
if let Some(chars) = first_line_chars {
|
||||
p = p.first_line_chars(chars);
|
||||
// pPr
|
||||
XMLElement::ParagraphProperty => {
|
||||
if let Ok(pr) = ParagraphProperty::read(r, attrs) {
|
||||
p.has_numbering = pr.numbering_property.is_some();
|
||||
p.property = pr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::Spacing => {
|
||||
let (before, after, line, spacing_type) =
|
||||
attributes::line_spacing::read_line_spacing(&attributes)?;
|
||||
p = p.line_spacing(before, after, line, spacing_type);
|
||||
continue;
|
||||
}
|
||||
XMLElement::Justification => {
|
||||
p = p.align(AlignmentType::from_str(&attributes[0].value)?);
|
||||
continue;
|
||||
}
|
||||
XMLElement::ParagraphStyle => {
|
||||
p = p.style(&attributes[0].value);
|
||||
continue;
|
||||
}
|
||||
XMLElement::DivId => {
|
||||
if let Some(val) = read_val(&attributes) {
|
||||
p.property.div_id = Some(val)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::NumberingProperty => {
|
||||
let num_pr = NumberingProperty::read(r, attrs)?;
|
||||
if num_pr.id.is_some() && num_pr.level.is_some() {
|
||||
p = p.numbering(num_pr.id.unwrap(), num_pr.level.unwrap());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::RunProperty => {
|
||||
let run_pr = RunProperty::read(r, attrs)?;
|
||||
p = p.run_property(run_pr);
|
||||
continue;
|
||||
}
|
||||
XMLElement::KeepNext => {
|
||||
p.property.keep_next = true;
|
||||
}
|
||||
XMLElement::KeepLines => {
|
||||
p.property.keep_lines = true;
|
||||
}
|
||||
XMLElement::PageBreakBefore => {
|
||||
p.property.page_break_before = true;
|
||||
}
|
||||
XMLElement::WindowControl => {
|
||||
p.property.window_control = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -148,6 +98,7 @@ impl ElementReader for Paragraph {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::types::*;
|
||||
#[cfg(test)]
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
use std::io::Read;
|
||||
use std::str::FromStr;
|
||||
|
||||
use xml::attribute::OwnedAttribute;
|
||||
use xml::reader::{EventReader, XmlEvent};
|
||||
|
||||
use super::*;
|
||||
|
||||
use super::attributes::*;
|
||||
use crate::types::*;
|
||||
|
||||
impl ElementReader for ParagraphProperty {
|
||||
fn read<R: Read>(
|
||||
r: &mut EventReader<R>,
|
||||
attrs: &[OwnedAttribute],
|
||||
) -> Result<Self, ReaderError> {
|
||||
let mut p = ParagraphProperty::new();
|
||||
loop {
|
||||
let e = r.next();
|
||||
match e {
|
||||
Ok(XmlEvent::StartElement {
|
||||
attributes, name, ..
|
||||
}) => {
|
||||
let e = XMLElement::from_str(&name.local_name).unwrap();
|
||||
|
||||
ignore::ignore_element(e.clone(), XMLElement::ParagraphPropertyChange, r);
|
||||
|
||||
match e {
|
||||
XMLElement::Indent => {
|
||||
let (start, end, special, start_chars, hanging_chars, first_line_chars) =
|
||||
read_indent(&attributes)?;
|
||||
p = p.indent(start, special, end, start_chars);
|
||||
|
||||
if let Some(chars) = hanging_chars {
|
||||
p = p.hanging_chars(chars);
|
||||
}
|
||||
if let Some(chars) = first_line_chars {
|
||||
p = p.first_line_chars(chars);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::Spacing => {
|
||||
let (before, after, line, spacing_type) =
|
||||
attributes::line_spacing::read_line_spacing(&attributes)?;
|
||||
p = p.line_spacing(before, after, line, spacing_type);
|
||||
continue;
|
||||
}
|
||||
XMLElement::Justification => {
|
||||
if let Ok(v) = AlignmentType::from_str(&attributes[0].value) {
|
||||
p = p.align(v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::ParagraphStyle => {
|
||||
p = p.style(&attributes[0].value);
|
||||
continue;
|
||||
}
|
||||
XMLElement::RunProperty => {
|
||||
if let Ok(run_pr) = RunProperty::read(r, attrs) {
|
||||
p.run_property = run_pr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::DivId => {
|
||||
if let Some(val) = read_val(&attributes) {
|
||||
p.div_id = Some(val)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::NumberingProperty => {
|
||||
let num_pr = NumberingProperty::read(r, attrs)?;
|
||||
if num_pr.id.is_some() && num_pr.level.is_some() {
|
||||
p = p.numbering(num_pr.id.unwrap(), num_pr.level.unwrap());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::OutlineLvl => {
|
||||
if let Some(val) = read_val(&attributes) {
|
||||
if let Ok(val) = usize::from_str(&val) {
|
||||
p = p.outline_lvl(val);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::KeepNext => {
|
||||
p.keep_next = true;
|
||||
}
|
||||
XMLElement::KeepLines => {
|
||||
p.keep_lines = true;
|
||||
}
|
||||
XMLElement::PageBreakBefore => {
|
||||
p.page_break_before = true;
|
||||
}
|
||||
XMLElement::WindowControl => {
|
||||
p.window_control = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = XMLElement::from_str(&name.local_name).unwrap();
|
||||
if e == XMLElement::ParagraphProperty {
|
||||
return Ok(p);
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ReaderError::XMLReadError),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,22 +43,10 @@ impl ElementReader for Style {
|
|||
continue;
|
||||
}
|
||||
// pPr
|
||||
XMLElement::Indent => {
|
||||
let (start, end, special, start_chars, hanging_chars, first_line_chars) =
|
||||
read_indent(&attributes)?;
|
||||
style = style.indent(start, special, end, start_chars);
|
||||
|
||||
if let Some(chars) = hanging_chars {
|
||||
style = style.hanging_chars(chars);
|
||||
XMLElement::ParagraphProperty => {
|
||||
if let Ok(pr) = ParagraphProperty::read(r, attrs) {
|
||||
style.paragraph_property = pr;
|
||||
}
|
||||
if let Some(chars) = first_line_chars {
|
||||
style = style.first_line_chars(chars);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
XMLElement::Justification => {
|
||||
style = style.align(AlignmentType::from_str(&attributes[0].value)?);
|
||||
continue;
|
||||
}
|
||||
// rPr
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::reader::ReaderError;
|
|||
pub enum XMLElement {
|
||||
Body,
|
||||
Paragraph,
|
||||
ParagraphProperty,
|
||||
Run,
|
||||
RunProperty,
|
||||
Color,
|
||||
|
@ -40,6 +41,7 @@ pub enum XMLElement {
|
|||
IndentLevel,
|
||||
NumberingId,
|
||||
Justification,
|
||||
OutlineLvl,
|
||||
Insert,
|
||||
KeepNext,
|
||||
KeepLines,
|
||||
|
@ -205,6 +207,7 @@ impl FromStr for XMLElement {
|
|||
match s {
|
||||
"body" => Ok(XMLElement::Body),
|
||||
"p" => Ok(XMLElement::Paragraph),
|
||||
"pPr" => Ok(XMLElement::ParagraphProperty),
|
||||
"r" => Ok(XMLElement::Run),
|
||||
"rPr" => Ok(XMLElement::RunProperty),
|
||||
"rPrChange" => Ok(XMLElement::RunPropertyChange),
|
||||
|
@ -300,6 +303,7 @@ impl FromStr for XMLElement {
|
|||
"lvlText" => Ok(XMLElement::LevelText),
|
||||
"lvlRestart" => Ok(XMLElement::LevelRestart),
|
||||
"lvlJc" => Ok(XMLElement::LevelJustification),
|
||||
"outlineLvl" => Ok(XMLElement::OutlineLvl),
|
||||
"numStyleLink" => Ok(XMLElement::NumStyleLink),
|
||||
"styleLink" => Ok(XMLElement::StyleLink),
|
||||
"vAlign" => Ok(XMLElement::VAlign),
|
||||
|
|
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
|
@ -1,7 +1,7 @@
|
|||
import { RunJSON, RunPropertyJSON } from "./run";
|
||||
import { IndentJSON } from "./indent";
|
||||
import { CommentRangeStartJSON, CommentRangeEndJSON } from "..";
|
||||
import {LineSpacingJSON} from "./line_spacing";
|
||||
import { LineSpacingJSON } from "./line_spacing";
|
||||
|
||||
export type ParagraphChildJSON =
|
||||
| RunJSON
|
||||
|
@ -29,6 +29,7 @@ export type ParagraphPropertyJSON = {
|
|||
keepLines: boolean;
|
||||
pageBreakBefore: boolean;
|
||||
windowControl: boolean;
|
||||
outlineLvl: number | null;
|
||||
};
|
||||
|
||||
export type ParagraphJSON = {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -64,9 +64,13 @@ describe("reader", () => {
|
|||
});
|
||||
|
||||
test("should read line spacing docx", () => {
|
||||
const buffer = readFileSync(
|
||||
"../fixtures/line_spacing/line_spacing.docx"
|
||||
);
|
||||
const buffer = readFileSync("../fixtures/line_spacing/line_spacing.docx");
|
||||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should read outlineLvl docx", () => {
|
||||
const buffer = readFileSync("../fixtures/outline_lvl/outline_lvl.docx");
|
||||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue