parent
6ec9be7fcd
commit
513faa67c1
|
@ -79,10 +79,29 @@ impl Docx {
|
|||
}
|
||||
|
||||
pub fn document(mut self, d: Document) -> Docx {
|
||||
for child in &self.document.children {
|
||||
match child {
|
||||
DocumentChild::Paragraph(paragraph) => {
|
||||
if paragraph.has_numbering {
|
||||
self.document_rels.has_numberings = true;
|
||||
}
|
||||
}
|
||||
DocumentChild::Table(table) => {
|
||||
if table.has_numbering {
|
||||
self.document_rels.has_numberings = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.document = d;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn styles(mut self, s: Styles) -> Self {
|
||||
self.styles = s;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_paragraph(mut self, p: Paragraph) -> Docx {
|
||||
if p.has_numbering {
|
||||
// If this document has numbering, set numberings.xml to document_rels.
|
||||
|
|
|
@ -10,7 +10,6 @@ impl FromXML for Document {
|
|||
fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
|
||||
let mut parser = EventReader::new(reader);
|
||||
let mut doc = Self::default();
|
||||
|
||||
loop {
|
||||
let e = parser.next();
|
||||
match e {
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
use std::collections::HashMap;
|
||||
use std::io::{Cursor, Read};
|
||||
use std::path::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
use xml::reader::{EventReader, XmlEvent};
|
||||
|
||||
use super::errors::*;
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ReadDocumentRels {
|
||||
rels: HashMap<String, PathBuf>,
|
||||
}
|
||||
|
||||
impl ReadDocumentRels {
|
||||
pub fn find_target_path(&self, target: &str) -> Option<PathBuf> {
|
||||
self.rels.get(target).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_document_rels(
|
||||
archive: &mut zip::read::ZipArchive<Cursor<&[u8]>>,
|
||||
main_path: impl AsRef<Path>,
|
||||
) -> Result<ReadDocumentRels, ReaderError> {
|
||||
let dir = &main_path
|
||||
.as_ref()
|
||||
.parent()
|
||||
.ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||
let p = find_rels_filename(&main_path)?;
|
||||
let p = p.to_str().ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||
let rels_xml = archive.by_name(&p)?;
|
||||
let rels = read_rels_xml(rels_xml, dir)?;
|
||||
Ok(rels)
|
||||
}
|
||||
|
||||
fn read_rels_xml<R: Read>(
|
||||
reader: R,
|
||||
dir: impl AsRef<Path>,
|
||||
) -> Result<ReadDocumentRels, ReaderError> {
|
||||
let mut parser = EventReader::new(reader);
|
||||
let mut rels = ReadDocumentRels {
|
||||
rels: HashMap::new(),
|
||||
};
|
||||
loop {
|
||||
let e = parser.next();
|
||||
match e {
|
||||
Ok(XmlEvent::StartElement {
|
||||
attributes, name, ..
|
||||
}) => {
|
||||
let e = XMLElement::from_str(&name.local_name).unwrap();
|
||||
if let XMLElement::Relationship = e {
|
||||
let mut rel_type = "".to_owned();
|
||||
let mut target = PathBuf::default();
|
||||
for a in attributes {
|
||||
let local_name = &a.name.local_name;
|
||||
if local_name == "Type" {
|
||||
rel_type = a.value.to_owned();
|
||||
} else if local_name == "Target" {
|
||||
target = Path::new(dir.as_ref()).join(a.value);
|
||||
}
|
||||
}
|
||||
rels.rels.insert(rel_type, target);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = XMLElement::from_str(&name.local_name).unwrap();
|
||||
if let XMLElement::Relationships = e {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ReaderError::XMLReadError),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(rels)
|
||||
}
|
||||
|
||||
fn find_rels_filename(main_path: impl AsRef<Path>) -> Result<PathBuf, ReaderError> {
|
||||
let path = main_path.as_ref();
|
||||
let dir = path
|
||||
.parent()
|
||||
.ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||
let base = path
|
||||
.file_stem()
|
||||
.ok_or(ReaderError::DocumentRelsNotFoundError)?;
|
||||
Ok(Path::new(dir)
|
||||
.join("_rels")
|
||||
.join(base)
|
||||
.with_extension("xml.rels"))
|
||||
}
|
|
@ -12,6 +12,10 @@ pub enum ReaderError {
|
|||
XMLReadError,
|
||||
#[error("Failed to find document.")]
|
||||
DocumentNotFoundError,
|
||||
#[error("Failed to find document rels.")]
|
||||
DocumentRelsNotFoundError,
|
||||
#[error("Failed to find styles.")]
|
||||
DocumentStylesNotFoundError,
|
||||
#[error("Unknown error")]
|
||||
Unknown,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod attributes;
|
||||
mod delete;
|
||||
mod document;
|
||||
mod document_rels;
|
||||
mod errors;
|
||||
mod from_xml;
|
||||
mod insert;
|
||||
|
@ -21,12 +22,15 @@ use zip;
|
|||
use crate::documents::*;
|
||||
|
||||
pub use attributes::*;
|
||||
pub use document_rels::*;
|
||||
pub use errors::ReaderError;
|
||||
pub use from_xml::*;
|
||||
pub use xml_element::*;
|
||||
|
||||
const DOC_RELATIONSHIP_TYPE: &str =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
||||
const STYLE_RELATIONSHIP_TYPE: &str =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
||||
|
||||
pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
|
||||
let cur = Cursor::new(buf);
|
||||
|
@ -46,6 +50,15 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
|
|||
.ok_or(ReaderError::DocumentNotFoundError)?;
|
||||
let document_xml = archive.by_name(&main_rel.2)?;
|
||||
let document = Document::from_xml(document_xml)?;
|
||||
let docx = Docx::new().document(document);
|
||||
|
||||
// Read document relationships
|
||||
let rels = read_document_rels(&mut archive, &main_rel.2)?;
|
||||
let style_path = rels
|
||||
.find_target_path(STYLE_RELATIONSHIP_TYPE)
|
||||
.ok_or(ReaderError::DocumentStylesNotFoundError)?;
|
||||
let styles_xml = archive.by_name(style_path.to_str().expect("should have styles"))?;
|
||||
let styles = Styles::from_xml(styles_xml)?;
|
||||
|
||||
let docx = Docx::new().document(document).styles(styles);
|
||||
Ok(docx)
|
||||
}
|
||||
|
|
|
@ -37,28 +37,39 @@ impl FromXML for Styles {
|
|||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
//
|
||||
// use super::*;
|
||||
// #[cfg(test)]
|
||||
// use pretty_assertions::assert_eq;
|
||||
//
|
||||
// #[test]
|
||||
// fn test_from_xml() {
|
||||
// let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
|
||||
// <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
// <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml" />
|
||||
// </Relationships>"#;
|
||||
// let c = Rels::from_xml(xml.as_bytes()).unwrap();
|
||||
// let mut rels = Vec::new();
|
||||
// rels.push((
|
||||
// "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
|
||||
// .to_owned(),
|
||||
// "rId1".to_owned(),
|
||||
// "docProps/core.xml".to_owned(),
|
||||
// ));
|
||||
// assert_eq!(Rels { rels }, c);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::types::*;
|
||||
#[cfg(test)]
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_from_xml() {
|
||||
let xml =
|
||||
r#"<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:style w:type="character" w:styleId="FootnoteTextChar">
|
||||
<w:name w:val="Footnote Text Char"></w:name>
|
||||
<w:rPr>
|
||||
<w:sz w:val="20"></w:sz>
|
||||
<w:szCs w:val="20"></w:szCs>
|
||||
</w:rPr>
|
||||
<w:uiPriority w:val="99"></w:uiPriority>
|
||||
<w:unhideWhenUsed></w:unhideWhenUsed>
|
||||
<w:basedOn w:val="DefaultParagraphFont"></w:basedOn>
|
||||
<w:link w:val="FootnoteText"></w:link>
|
||||
<w:uiPriority w:val="99"></w:uiPriority>
|
||||
<w:semiHidden></w:semiHidden>
|
||||
</w:style>
|
||||
</w:styles>"#;
|
||||
let s = Styles::from_xml(xml.as_bytes()).unwrap();
|
||||
let mut styles = Styles::new();
|
||||
styles = styles.add_style(
|
||||
Style::new("FootnoteTextChar", StyleType::Character)
|
||||
.name("Footnote Text Char")
|
||||
.size(20),
|
||||
);
|
||||
assert_eq!(s, styles);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ pub enum XMLElement {
|
|||
VertAlign,
|
||||
Spacing,
|
||||
Styles,
|
||||
Relationship,
|
||||
Relationships,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
|
@ -129,6 +131,8 @@ impl FromStr for XMLElement {
|
|||
"vertAlign" => Ok(XMLElement::VertAlign),
|
||||
"spacing" => Ok(XMLElement::Spacing),
|
||||
"styles" => Ok(XMLElement::Styles),
|
||||
"Relationships" => Ok(XMLElement::Relationships),
|
||||
"Relationship" => Ok(XMLElement::Relationship),
|
||||
_ => Ok(XMLElement::Unsupported),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ use std::str::FromStr;
|
|||
pub enum StyleType {
|
||||
Paragraph,
|
||||
Character,
|
||||
Numbering,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl fmt::Display for StyleType {
|
||||
|
@ -18,6 +20,8 @@ impl fmt::Display for StyleType {
|
|||
match *self {
|
||||
StyleType::Paragraph => write!(f, "paragraph"),
|
||||
StyleType::Character => write!(f, "character"),
|
||||
StyleType::Numbering => write!(f, "numbering"),
|
||||
StyleType::Unsupported => write!(f, "unsupported"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +32,8 @@ impl FromStr for StyleType {
|
|||
match s {
|
||||
"paragraph" => Ok(StyleType::Paragraph),
|
||||
"character" => Ok(StyleType::Character),
|
||||
_ => Err(errors::TypeError::FromStrError),
|
||||
"numbering" => Ok(StyleType::Numbering),
|
||||
_ => Ok(StyleType::Unsupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue