parent
6ec9be7fcd
commit
513faa67c1
|
@ -79,10 +79,29 @@ impl Docx {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn document(mut self, d: Document) -> 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.document = d;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn styles(mut self, s: Styles) -> Self {
|
||||||
|
self.styles = s;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_paragraph(mut self, p: Paragraph) -> Docx {
|
pub fn add_paragraph(mut self, p: Paragraph) -> Docx {
|
||||||
if p.has_numbering {
|
if p.has_numbering {
|
||||||
// If this document has numbering, set numberings.xml to document_rels.
|
// 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> {
|
fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
|
||||||
let mut parser = EventReader::new(reader);
|
let mut parser = EventReader::new(reader);
|
||||||
let mut doc = Self::default();
|
let mut doc = Self::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let e = parser.next();
|
let e = parser.next();
|
||||||
match e {
|
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,
|
XMLReadError,
|
||||||
#[error("Failed to find document.")]
|
#[error("Failed to find document.")]
|
||||||
DocumentNotFoundError,
|
DocumentNotFoundError,
|
||||||
|
#[error("Failed to find document rels.")]
|
||||||
|
DocumentRelsNotFoundError,
|
||||||
|
#[error("Failed to find styles.")]
|
||||||
|
DocumentStylesNotFoundError,
|
||||||
#[error("Unknown error")]
|
#[error("Unknown error")]
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod delete;
|
mod delete;
|
||||||
mod document;
|
mod document;
|
||||||
|
mod document_rels;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod from_xml;
|
mod from_xml;
|
||||||
mod insert;
|
mod insert;
|
||||||
|
@ -21,12 +22,15 @@ use zip;
|
||||||
use crate::documents::*;
|
use crate::documents::*;
|
||||||
|
|
||||||
pub use attributes::*;
|
pub use attributes::*;
|
||||||
|
pub use document_rels::*;
|
||||||
pub use errors::ReaderError;
|
pub use errors::ReaderError;
|
||||||
pub use from_xml::*;
|
pub use from_xml::*;
|
||||||
pub use xml_element::*;
|
pub use xml_element::*;
|
||||||
|
|
||||||
const DOC_RELATIONSHIP_TYPE: &str =
|
const DOC_RELATIONSHIP_TYPE: &str =
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
"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> {
|
pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
|
||||||
let cur = Cursor::new(buf);
|
let cur = Cursor::new(buf);
|
||||||
|
@ -46,6 +50,15 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
|
||||||
.ok_or(ReaderError::DocumentNotFoundError)?;
|
.ok_or(ReaderError::DocumentNotFoundError)?;
|
||||||
let document_xml = archive.by_name(&main_rel.2)?;
|
let document_xml = archive.by_name(&main_rel.2)?;
|
||||||
let document = Document::from_xml(document_xml)?;
|
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)
|
Ok(docx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,28 +37,39 @@ impl FromXML for Styles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
#[cfg(test)]
|
||||||
// mod tests {
|
mod tests {
|
||||||
//
|
|
||||||
// use super::*;
|
use super::*;
|
||||||
// #[cfg(test)]
|
use crate::types::*;
|
||||||
// use pretty_assertions::assert_eq;
|
#[cfg(test)]
|
||||||
//
|
use pretty_assertions::assert_eq;
|
||||||
// #[test]
|
|
||||||
// fn test_from_xml() {
|
#[test]
|
||||||
// let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
|
fn test_from_xml() {
|
||||||
// <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
let xml =
|
||||||
// <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml" />
|
r#"<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||||
// </Relationships>"#;
|
<w:style w:type="character" w:styleId="FootnoteTextChar">
|
||||||
// let c = Rels::from_xml(xml.as_bytes()).unwrap();
|
<w:name w:val="Footnote Text Char"></w:name>
|
||||||
// let mut rels = Vec::new();
|
<w:rPr>
|
||||||
// rels.push((
|
<w:sz w:val="20"></w:sz>
|
||||||
// "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
|
<w:szCs w:val="20"></w:szCs>
|
||||||
// .to_owned(),
|
</w:rPr>
|
||||||
// "rId1".to_owned(),
|
<w:uiPriority w:val="99"></w:uiPriority>
|
||||||
// "docProps/core.xml".to_owned(),
|
<w:unhideWhenUsed></w:unhideWhenUsed>
|
||||||
// ));
|
<w:basedOn w:val="DefaultParagraphFont"></w:basedOn>
|
||||||
// assert_eq!(Rels { rels }, c);
|
<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,
|
VertAlign,
|
||||||
Spacing,
|
Spacing,
|
||||||
Styles,
|
Styles,
|
||||||
|
Relationship,
|
||||||
|
Relationships,
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,6 +131,8 @@ impl FromStr for XMLElement {
|
||||||
"vertAlign" => Ok(XMLElement::VertAlign),
|
"vertAlign" => Ok(XMLElement::VertAlign),
|
||||||
"spacing" => Ok(XMLElement::Spacing),
|
"spacing" => Ok(XMLElement::Spacing),
|
||||||
"styles" => Ok(XMLElement::Styles),
|
"styles" => Ok(XMLElement::Styles),
|
||||||
|
"Relationships" => Ok(XMLElement::Relationships),
|
||||||
|
"Relationship" => Ok(XMLElement::Relationship),
|
||||||
_ => Ok(XMLElement::Unsupported),
|
_ => Ok(XMLElement::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ use std::str::FromStr;
|
||||||
pub enum StyleType {
|
pub enum StyleType {
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Character,
|
Character,
|
||||||
|
Numbering,
|
||||||
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for StyleType {
|
impl fmt::Display for StyleType {
|
||||||
|
@ -18,6 +20,8 @@ impl fmt::Display for StyleType {
|
||||||
match *self {
|
match *self {
|
||||||
StyleType::Paragraph => write!(f, "paragraph"),
|
StyleType::Paragraph => write!(f, "paragraph"),
|
||||||
StyleType::Character => write!(f, "character"),
|
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 {
|
match s {
|
||||||
"paragraph" => Ok(StyleType::Paragraph),
|
"paragraph" => Ok(StyleType::Paragraph),
|
||||||
"character" => Ok(StyleType::Character),
|
"character" => Ok(StyleType::Character),
|
||||||
_ => Err(errors::TypeError::FromStrError),
|
"numbering" => Ok(StyleType::Numbering),
|
||||||
|
_ => Ok(StyleType::Unsupported),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue