feat: impl footer reader (#378)

* feat: impl footer reader

* fix: snaps
main
bokuweb 2021-12-02 00:07:58 +09:00 committed by GitHub
parent aa0ccfa6c6
commit d8f5e19e7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1855 additions and 75 deletions

View File

@ -114,20 +114,20 @@ impl ContentTypes {
}
pub fn add_header(mut self) -> Self {
self.header_count += 1;
self.types.insert(
format!("/word/header{}.xml", self.header_count),
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml".to_owned(),
);
self.header_count += 1;
self
}
pub fn add_footer(mut self) -> Self {
self.footer_count += 1;
self.types.insert(
format!("/word/footer{}.xml", self.footer_count),
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml".to_owned(),
);
self.footer_count += 1;
self
}
}
@ -138,8 +138,8 @@ impl Default for ContentTypes {
types: BTreeMap::new(),
web_extension_count: 1,
custom_xml_count: 1,
header_count: 1,
footer_count: 1,
header_count: 0,
footer_count: 0,
}
}
}
@ -219,8 +219,8 @@ mod tests {
types,
web_extension_count: 1,
custom_xml_count: 1,
header_count: 1,
footer_count: 1,
header_count: 0,
footer_count: 0,
},
c
);

View File

@ -6,8 +6,8 @@ use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FooterReference {
footer_type: String,
id: String,
pub footer_type: String,
pub id: String,
}
impl FooterReference {

View File

@ -0,0 +1,71 @@
use std::io::Read;
use std::str::FromStr;
use crate::reader::*;
use xml::reader::{EventReader, XmlEvent};
use super::{Paragraph, Table};
impl FromXML for Footer {
fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
let mut parser = EventReader::new(reader);
let mut footer = Self::default();
loop {
let e = parser.next();
match e {
Ok(XmlEvent::StartElement {
attributes, name, ..
}) => {
let e = XMLElement::from_str(&name.local_name).unwrap();
match e {
XMLElement::Paragraph => {
let p = Paragraph::read(&mut parser, &attributes)?;
footer = footer.add_paragraph(p);
continue;
}
XMLElement::Table => {
let t = Table::read(&mut parser, &attributes)?;
footer = footer.add_table(t);
continue;
}
_ => {}
}
}
Ok(XmlEvent::EndDocument) => break,
Err(_) => return Err(ReaderError::XMLReadError),
_ => {}
}
}
Ok(footer)
}
}
#[test]
fn test_footer_from_xml() {
let xml = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:ftr xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" mc:Ignorable="w14 wp14">
<w:p w14:paraId="12345678">
<w:pPr>
<w:rPr />
</w:pPr>
<w:r>
<w:rPr />
<w:t xml:space="preserve">Hello Footer</w:t>
</w:r>
</w:p>
</w:ftr>"#;
let h = Footer::from_xml(xml.as_bytes()).unwrap();
let expected =
Footer::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello Footer")));
assert_eq!(h, expected)
}

View File

@ -16,6 +16,7 @@ mod document;
mod document_rels;
mod drawing;
mod errors;
mod footer;
mod from_xml;
mod header;
mod ignore;
@ -81,6 +82,8 @@ const WEB_SETTINGS_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings";
const HEADER_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
const FOOTER_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
// 2011
const COMMENTS_EXTENDED_TYPE: &str =
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
@ -97,18 +100,36 @@ fn read_headers(
let data = read_zip(archive, path.to_str().expect("should have header path."));
if let Ok(d) = data {
if let Ok(h) = Header::from_xml(&d[..]) {
Some((rid, h))
} else {
None
return Some((rid, h));
}
} else {
None
}
None
})
.collect();
headers
}
fn read_footers(
rels: &ReadDocumentRels,
archive: &mut ZipArchive<Cursor<&[u8]>>,
) -> HashMap<RId, Footer> {
let footer_paths = rels.find_target_path(FOOTER_TYPE);
let footers: HashMap<RId, Footer> = footer_paths
.unwrap_or_default()
.into_iter()
.filter_map(|(rid, path)| {
let data = read_zip(archive, path.to_str().expect("should have footer path."));
if let Ok(d) = data {
if let Ok(h) = Footer::from_xml(&d[..]) {
return Some((rid, h));
}
}
None
})
.collect();
footers
}
pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
let mut docx = Docx::new();
let cur = Cursor::new(buf);
@ -150,6 +171,7 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
let rels = read_document_rels(&mut archive, &document_path)?;
let headers = read_headers(&rels, &mut archive);
let footers = read_footers(&rels, &mut archive);
// Read commentsExtended
let comments_extended_path = rels.find_target_path(COMMENTS_EXTENDED_TYPE);
@ -235,6 +257,9 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
if let Some(h) = docx.document.section_property.header_reference.clone() {
if let Some(header) = headers.get(&h.id) {
docx.document = docx.document.header(header.clone(), &h.id);
let count = docx.document_rels.header_count + 1;
docx.document_rels.header_count = count;
docx.content_type = docx.content_type.add_header();
}
}
if let Some(ref h) = docx
@ -245,11 +270,48 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
{
if let Some(header) = headers.get(&h.id) {
docx.document = docx.document.first_header(header.clone(), &h.id);
let count = docx.document_rels.header_count + 1;
docx.document_rels.header_count = count;
docx.content_type = docx.content_type.add_header();
}
}
if let Some(ref h) = docx.document.section_property.even_header_reference.clone() {
if let Some(header) = headers.get(&h.id) {
docx.document = docx.document.even_header(header.clone(), &h.id);
let count = docx.document_rels.header_count + 1;
docx.document_rels.header_count = count;
docx.content_type = docx.content_type.add_header();
}
}
// assign footers
if let Some(f) = docx.document.section_property.footer_reference.clone() {
if let Some(footer) = footers.get(&f.id) {
docx.document = docx.document.footer(footer.clone(), &f.id);
let count = docx.document_rels.footer_count + 1;
docx.document_rels.footer_count = count;
docx.content_type = docx.content_type.add_footer();
}
}
if let Some(ref f) = docx
.document
.section_property
.first_footer_reference
.clone()
{
if let Some(footer) = footers.get(&f.id) {
docx.document = docx.document.first_footer(footer.clone(), &f.id);
let count = docx.document_rels.footer_count + 1;
docx.document_rels.footer_count = count;
docx.content_type = docx.content_type.add_footer();
}
}
if let Some(ref f) = docx.document.section_property.even_footer_reference.clone() {
if let Some(footer) = footers.get(&f.id) {
docx.document = docx.document.even_footer(footer.clone(), &f.id);
let count = docx.document_rels.footer_count + 1;
docx.document_rels.footer_count = count;
docx.content_type = docx.content_type.add_footer();
}
}

View File

@ -57,7 +57,9 @@ fn read_page_margin(
Ok(margin)
}
fn read_header_reference(attributes: &[OwnedAttribute]) -> Result<(String, String), ReaderError> {
fn read_header_or_footer_reference(
attributes: &[OwnedAttribute],
) -> Result<(String, String), ReaderError> {
let mut rid = "".to_owned();
let mut header_type = "default".to_owned();
for a in attributes {
@ -103,7 +105,9 @@ impl ElementReader for SectionProperty {
}
}
XMLElement::HeaderReference => {
if let Ok((rid, header_type)) = read_header_reference(&attributes) {
if let Ok((rid, header_type)) =
read_header_or_footer_reference(&attributes)
{
match header_type.as_str() {
"default" => {
sp.header_reference =
@ -121,6 +125,27 @@ impl ElementReader for SectionProperty {
}
}
}
XMLElement::FooterReference => {
if let Ok((rid, footer_type)) =
read_header_or_footer_reference(&attributes)
{
match footer_type.as_str() {
"default" => {
sp.footer_reference =
Some(FooterReference::new(footer_type, rid));
}
"first" => {
sp.first_footer_reference =
Some(FooterReference::new(footer_type, rid));
}
"even" => {
sp.even_footer_reference =
Some(FooterReference::new(footer_type, rid));
}
_ => {}
}
}
}
XMLElement::TitlePg => sp = sp.title_pg(),
_ => {}
}

View File

@ -140,6 +140,7 @@ pub enum XMLElement {
PageMargin,
WebSettings,
HeaderReference,
FooterReference,
TitlePg,
EvenAndOddHeaders,
Unsupported,
@ -339,6 +340,7 @@ impl FromStr for XMLElement {
"pageBreakBefore" => Ok(XMLElement::PageBreakBefore),
"windowControl" => Ok(XMLElement::WindowControl),
"headerReference" => Ok(XMLElement::HeaderReference),
"footerReference" => Ok(XMLElement::FooterReference),
"titlePg" => Ok(XMLElement::TitlePg),
"evenAndOddHeaders" => Ok(XMLElement::EvenAndOddHeaders),
_ => Ok(XMLElement::Unsupported),

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

View File

@ -80,6 +80,12 @@ describe("reader", () => {
const json = w.readDocx(buffer);
expect(json).toMatchSnapshot();
});
test("should read footer docx", () => {
const buffer = readFileSync("../fixtures/footer/footer.docx");
const json = w.readDocx(buffer);
expect(json).toMatchSnapshot();
});
});
describe("writer", () => {

Binary file not shown.