Support default footer (#366)
* feat: Support default footer writer with rust * feat: Support footer for default section with js * fix: improve footer interfacemain
parent
f44faea75f
commit
025aee280f
|
@ -0,0 +1,14 @@
|
|||
use docx_rs::*;
|
||||
|
||||
pub fn main() -> Result<(), DocxError> {
|
||||
let path = std::path::Path::new("./output/footer.docx");
|
||||
let file = std::fs::File::create(&path).unwrap();
|
||||
let footer =
|
||||
Footer::new().add_paragraph(Paragraph::new().add_run(Run::new().add_text("Hello")));
|
||||
Docx::new()
|
||||
.footer(footer)
|
||||
.add_paragraph(Paragraph::new().add_run(Run::new().add_text("World")))
|
||||
.build()
|
||||
.pack(file)?;
|
||||
Ok(())
|
||||
}
|
|
@ -12,6 +12,7 @@ pub struct ContentTypes {
|
|||
types: BTreeMap<String, String>,
|
||||
web_extension_count: usize,
|
||||
custom_xml_count: usize,
|
||||
footer_count: usize,
|
||||
}
|
||||
|
||||
impl ContentTypes {
|
||||
|
@ -114,6 +115,15 @@ impl ContentTypes {
|
|||
self.custom_xml_count += 1;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_footer(mut self) -> Self {
|
||||
self.types.insert(
|
||||
format!("/word/footer{}.xml", self.footer_count),
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml".to_owned(),
|
||||
);
|
||||
self.footer_count += 1;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ContentTypes {
|
||||
|
@ -122,6 +132,7 @@ impl Default for ContentTypes {
|
|||
types: BTreeMap::new(),
|
||||
web_extension_count: 1,
|
||||
custom_xml_count: 1,
|
||||
footer_count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +211,8 @@ mod tests {
|
|||
ContentTypes {
|
||||
types,
|
||||
web_extension_count: 1,
|
||||
custom_xml_count: 1
|
||||
custom_xml_count: 1,
|
||||
footer_count: 1,
|
||||
},
|
||||
c
|
||||
);
|
||||
|
|
|
@ -149,6 +149,11 @@ impl Document {
|
|||
self.section_property = property;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer_reference(mut self, r: FooterReference) -> Self {
|
||||
self.section_property = self.section_property.footer_reference(r);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for DocumentChild {
|
||||
|
|
|
@ -11,6 +11,7 @@ pub struct DocumentRels {
|
|||
pub has_numberings: bool,
|
||||
pub image_ids: Vec<usize>,
|
||||
pub custom_xml_count: usize,
|
||||
pub footer_count: usize,
|
||||
}
|
||||
|
||||
impl DocumentRels {
|
||||
|
@ -31,6 +32,7 @@ impl Default for DocumentRels {
|
|||
has_numberings: false,
|
||||
image_ids: vec![],
|
||||
custom_xml_count: 0,
|
||||
footer_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +85,14 @@ impl BuildXML for DocumentRels {
|
|||
)
|
||||
}
|
||||
|
||||
for i in 0..self.footer_count {
|
||||
b = b.relationship(
|
||||
&create_footer_rid(i + 1),
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||
&format!("footer{}.xml", i + 1),
|
||||
)
|
||||
}
|
||||
|
||||
for i in 0..self.custom_xml_count {
|
||||
b = b.relationship(
|
||||
&format!("rId{}", i + 8),
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct FooterReference {
|
||||
footer_type: String,
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl FooterReference {
|
||||
pub fn new(t: impl Into<String>, id: impl Into<String>) -> FooterReference {
|
||||
FooterReference {
|
||||
footer_type: t.into(),
|
||||
id: id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for FooterReference {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
XMLBuilder::new()
|
||||
.footer_reference(&self.footer_type, &self.id)
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ mod drawing;
|
|||
mod font;
|
||||
mod grid_span;
|
||||
mod header_reference;
|
||||
mod footer_reference;
|
||||
mod highlight;
|
||||
mod indent;
|
||||
mod indent_level;
|
||||
|
@ -121,6 +122,7 @@ pub use drawing::*;
|
|||
pub use font::*;
|
||||
pub use grid_span::*;
|
||||
pub use header_reference::*;
|
||||
pub use footer_reference::*;
|
||||
pub use highlight::*;
|
||||
pub use indent::*;
|
||||
pub use indent_level::*;
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct SectionProperty {
|
|||
columns: usize,
|
||||
doc_grid: DocGrid,
|
||||
header_reference: HeaderReference,
|
||||
footer_reference: Option<FooterReference>,
|
||||
section_type: Option<SectionType>,
|
||||
}
|
||||
|
||||
|
@ -40,6 +41,11 @@ impl SectionProperty {
|
|||
self.doc_grid = doc_grid;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer_reference(mut self, r: FooterReference) -> Self {
|
||||
self.footer_reference = Some(r);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SectionProperty {
|
||||
|
@ -50,6 +56,7 @@ impl Default for SectionProperty {
|
|||
columns: 425,
|
||||
doc_grid: DocGrid::default(),
|
||||
header_reference: HeaderReference::default(),
|
||||
footer_reference: None,
|
||||
section_type: None,
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +71,8 @@ impl BuildXML for SectionProperty {
|
|||
.add_child(&self.page_margin)
|
||||
.add_child(&self.header_reference)
|
||||
.columns(&format!("{}", &self.columns))
|
||||
.add_child(&self.doc_grid);
|
||||
.add_child(&self.doc_grid)
|
||||
.add_optional_child(&self.footer_reference);
|
||||
|
||||
if let Some(t) = self.section_type {
|
||||
b = b.type_tag(&t.to_string());
|
||||
|
@ -90,4 +98,14 @@ mod tests {
|
|||
r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:headerReference w:type="default" r:id="rId4" /><w:cols w:space="425" /><w:docGrid w:type="lines" w:linePitch="360" /></w:sectPr>"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_section_property_with_footer() {
|
||||
let c = SectionProperty::new().footer_reference(FooterReference::new("default", "rId6"));
|
||||
let b = c.build();
|
||||
assert_eq!(
|
||||
str::from_utf8(&b).unwrap(),
|
||||
r#"<w:sectPr><w:pgSz w:w="11906" w:h="16838" /><w:pgMar w:top="1985" w:right="1701" w:bottom="1701" w:left="1701" w:header="851" w:footer="992" w:gutter="0" /><w:headerReference w:type="default" r:id="rId4" /><w:cols w:space="425" /><w:docGrid w:type="lines" w:linePitch="360" /><w:footerReference w:type="default" r:id="rId6" /></w:sectPr>"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::Serialize;
|
||||
|
||||
use super::*;
|
||||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Footer {
|
||||
pub has_numbering: bool,
|
||||
pub children: Vec<FooterChild>,
|
||||
}
|
||||
|
||||
impl Footer {
|
||||
pub fn new() -> Footer {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn add_paragraph(mut self, p: Paragraph) -> Self {
|
||||
if p.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(FooterChild::Paragraph(p));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_table(mut self, t: Table) -> Self {
|
||||
if t.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(FooterChild::Table(t));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Footer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
children: vec![],
|
||||
has_numbering: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FooterChild {
|
||||
Paragraph(Paragraph),
|
||||
Table(Table),
|
||||
}
|
||||
|
||||
impl Serialize for FooterChild {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
FooterChild::Paragraph(ref p) => {
|
||||
let mut t = serializer.serialize_struct("Paragraph", 2)?;
|
||||
t.serialize_field("type", "paragraph")?;
|
||||
t.serialize_field("data", p)?;
|
||||
t.end()
|
||||
}
|
||||
FooterChild::Table(ref c) => {
|
||||
let mut t = serializer.serialize_struct("Table", 2)?;
|
||||
t.serialize_field("type", "table")?;
|
||||
t.serialize_field("data", c)?;
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for Footer {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let mut b = XMLBuilder::new();
|
||||
b = b.declaration(Some(true)).open_footer();
|
||||
|
||||
for c in &self.children {
|
||||
match c {
|
||||
FooterChild::Paragraph(p) => b = b.add_child(p),
|
||||
FooterChild::Table(t) => b = b.add_child(t),
|
||||
}
|
||||
}
|
||||
b.close().build()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
#[cfg(test)]
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::str;
|
||||
|
||||
#[test]
|
||||
fn test_settings() {
|
||||
let c = Footer::new();
|
||||
let b = c.build();
|
||||
assert_eq!(
|
||||
str::from_utf8(&b).unwrap(),
|
||||
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" />"#
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
#[cfg(not(test))]
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
#[cfg(not(test))]
|
||||
static FOOTER_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn generate_pic_id() -> usize {
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
let id = FOOTER_ID.load(Ordering::Relaxed);
|
||||
FOOTER_ID.store(id.wrapping_add(1), Ordering::Relaxed);
|
||||
id
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn generate_footer_id() -> usize {
|
||||
123
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn create_footer_rid(id: usize) -> String {
|
||||
format!("rIdFooter{}", id)
|
||||
}
|
|
@ -12,6 +12,8 @@ mod document;
|
|||
mod document_rels;
|
||||
mod elements;
|
||||
mod font_table;
|
||||
mod footer;
|
||||
mod footer_id;
|
||||
mod header;
|
||||
mod header_id;
|
||||
mod history_id;
|
||||
|
@ -43,6 +45,8 @@ pub use document::*;
|
|||
pub use document_rels::*;
|
||||
pub use elements::*;
|
||||
pub use font_table::*;
|
||||
pub use footer::*;
|
||||
pub use footer_id::*;
|
||||
pub use header::*;
|
||||
pub use numberings::*;
|
||||
pub use rels::*;
|
||||
|
@ -71,6 +75,7 @@ pub struct Docx {
|
|||
pub font_table: FontTable,
|
||||
pub media: Vec<(usize, Vec<u8>)>,
|
||||
pub header: Header,
|
||||
pub footer: Option<Footer>,
|
||||
pub comments_extended: CommentsExtended,
|
||||
pub web_settings: WebSettings,
|
||||
pub taskpanes: Option<Taskpanes>,
|
||||
|
@ -111,6 +116,7 @@ impl Default for Docx {
|
|||
font_table,
|
||||
media,
|
||||
header,
|
||||
footer: None,
|
||||
comments_extended,
|
||||
web_settings,
|
||||
taskpanes: None,
|
||||
|
@ -219,6 +225,49 @@ impl Docx {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn footer(mut self, footer: Footer) -> Docx {
|
||||
if footer.has_numbering {
|
||||
// If this document has numbering, set numberings.xml to document_rels.
|
||||
// This is because numberings.xml without numbering cause an error on word online.
|
||||
self.document_rels.has_numberings = true;
|
||||
}
|
||||
if self.footer.is_none() {
|
||||
self.document.section_property = self
|
||||
.document
|
||||
.section_property
|
||||
// Add default footer reference
|
||||
.footer_reference(FooterReference::new("default", create_footer_rid(1)));
|
||||
self.document_rels.footer_count += 1;
|
||||
self.content_type = self.content_type.add_footer();
|
||||
}
|
||||
self.footer = Some(footer);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_footer_paragraph(mut self, p: Paragraph) -> Docx {
|
||||
if p.has_numbering {
|
||||
// If this document has numbering, set numberings.xml to document_rels.
|
||||
// This is because numberings.xml without numbering cause an error on word online.
|
||||
self.document_rels.has_numberings = true;
|
||||
}
|
||||
if self.footer.is_none() {
|
||||
self.footer = Some(Footer::new());
|
||||
self.document.section_property = self
|
||||
.document
|
||||
.section_property
|
||||
// Add default footer reference
|
||||
.footer_reference(FooterReference::new("default", create_footer_rid(1)));
|
||||
self.document_rels.footer_count += 1;
|
||||
self.content_type = self.content_type.add_footer();
|
||||
}
|
||||
|
||||
if let Some(footer) = self.footer {
|
||||
let footer = footer.add_paragraph(p);
|
||||
self.footer = Some(footer);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_abstract_numbering(mut self, num: AbstractNumbering) -> Docx {
|
||||
self.numberings = self.numberings.add_abstract_numbering(num);
|
||||
self
|
||||
|
@ -320,7 +369,6 @@ impl Docx {
|
|||
self.update_comments();
|
||||
|
||||
let (image_ids, images) = self.create_images();
|
||||
|
||||
let web_extensions = self.web_extensions.iter().map(|ext| ext.build()).collect();
|
||||
let custom_items = self.custom_items.iter().map(|xml| xml.build()).collect();
|
||||
let custom_item_props = self.custom_item_props.iter().map(|p| p.build()).collect();
|
||||
|
@ -332,6 +380,8 @@ impl Docx {
|
|||
|
||||
self.document_rels.image_ids = image_ids;
|
||||
|
||||
let footer = self.footer.as_ref().map(|footer| footer.build());
|
||||
|
||||
XMLDocx {
|
||||
content_type: self.content_type.build(),
|
||||
rels: self.rels.build(),
|
||||
|
@ -345,6 +395,7 @@ impl Docx {
|
|||
numberings: self.numberings.build(),
|
||||
media: images,
|
||||
header: self.header.build(),
|
||||
footer,
|
||||
comments_extended: self.comments_extended.build(),
|
||||
taskpanes: self.taskpanes.map(|taskpanes| taskpanes.build()),
|
||||
taskpanes_rels: self.taskpanes_rels.build(),
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct XMLDocx {
|
|||
pub numberings: Vec<u8>,
|
||||
pub media: Vec<(usize, Vec<u8>)>,
|
||||
pub header: Vec<u8>,
|
||||
pub footer: Option<Vec<u8>>,
|
||||
pub comments_extended: Vec<u8>,
|
||||
pub taskpanes: Option<Vec<u8>>,
|
||||
pub taskpanes_rels: Vec<u8>,
|
||||
|
|
|
@ -11,7 +11,6 @@ impl XMLBuilder {
|
|||
"distR"
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
open!(
|
||||
open_wp_anchor,
|
||||
"wp:anchor",
|
||||
|
|
|
@ -262,6 +262,7 @@ impl XMLBuilder {
|
|||
|
||||
open!(open_section_property, "w:sectPr");
|
||||
closed!(header_reference, "w:headerReference", "w:type", "r:id");
|
||||
closed!(footer_reference, "w:footerReference", "w:type", "r:id");
|
||||
|
||||
closed_with_str!(type_tag, "w:type");
|
||||
closed!(page_size, "w:pgSz", "w:w", "w:h");
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
use super::XMLBuilder;
|
||||
use super::XmlEvent;
|
||||
|
||||
impl XMLBuilder {
|
||||
pub(crate) fn open_footer(mut self) -> Self {
|
||||
self.writer
|
||||
.write(
|
||||
XmlEvent::start_element("w:ftr")
|
||||
.attr(
|
||||
"xmlns:r",
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
)
|
||||
.attr("xmlns:o", "urn:schemas-microsoft-com:office:office")
|
||||
.attr("xmlns:v", "urn:schemas-microsoft-com:vml")
|
||||
.attr(
|
||||
"xmlns:w",
|
||||
"http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
)
|
||||
.attr("xmlns:w10", "urn:schemas-microsoft-com:office:word")
|
||||
.attr(
|
||||
"xmlns:wp",
|
||||
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
)
|
||||
.attr(
|
||||
"xmlns:wps",
|
||||
"http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
)
|
||||
.attr(
|
||||
"xmlns:wpg",
|
||||
"http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
)
|
||||
.attr(
|
||||
"xmlns:mc",
|
||||
"http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
)
|
||||
.attr(
|
||||
"xmlns:wp14",
|
||||
"http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
)
|
||||
.attr(
|
||||
"xmlns:w14",
|
||||
"http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
)
|
||||
.attr("mc:Ignorable", "w14 wp14"),
|
||||
)
|
||||
.expect("should write to buf");
|
||||
self
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ mod document;
|
|||
mod drawing;
|
||||
mod elements;
|
||||
mod fonts;
|
||||
mod footer;
|
||||
mod header;
|
||||
mod numbering;
|
||||
mod pic;
|
||||
|
|
|
@ -48,6 +48,11 @@ where
|
|||
zip.start_file("word/commentsExtended.xml", options)?;
|
||||
zip.write_all(&xml.comments_extended)?;
|
||||
|
||||
if let Some(footer) = xml.footer {
|
||||
zip.start_file("word/footer1.xml", options)?;
|
||||
zip.write_all(&footer)?;
|
||||
}
|
||||
|
||||
if !xml.media.is_empty() {
|
||||
zip.add_directory("word/media/", Default::default())?;
|
||||
for m in xml.media {
|
||||
|
|
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
|
@ -0,0 +1,14 @@
|
|||
import { Paragraph } from "./paragraph";
|
||||
|
||||
export class Footer {
|
||||
hasNumberings = false;
|
||||
children: Paragraph[] = [];
|
||||
|
||||
addParagraph(p: Paragraph) {
|
||||
if (p.hasNumberings) {
|
||||
this.hasNumberings = true;
|
||||
}
|
||||
this.children.push(p);
|
||||
return this;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
import { DocGridType } from ".";
|
||||
import { Footer } from "./footer";
|
||||
|
||||
export type DocGrid = {
|
||||
gridType: DocGridType;
|
||||
|
@ -16,6 +17,7 @@ export class SectionProperty {
|
|||
gridType: "lines",
|
||||
linePitch: 360,
|
||||
};
|
||||
_footer: Footer | null = null;
|
||||
|
||||
pageSize(w: number, h: number) {
|
||||
this._pageSize.w = w;
|
||||
|
@ -37,6 +39,11 @@ export class SectionProperty {
|
|||
this._docGrid = { gridType, linePitch, charSpace };
|
||||
return this;
|
||||
}
|
||||
|
||||
footer(footer: Footer) {
|
||||
this._footer = footer;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export type PageOrientationType = "landscape" | "portrait";
|
||||
|
|
|
@ -73,6 +73,11 @@ impl Docx {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn footer(mut self, footer: Footer) -> Self {
|
||||
self.0 = self.0.footer(footer.take());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn page_size(mut self, w: u32, h: u32) -> Docx {
|
||||
self.0 = self.0.page_size(w, h);
|
||||
self
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
use super::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct Footer(docx_rs::Footer);
|
||||
|
||||
#[wasm_bindgen(js_name = createFooter)]
|
||||
pub fn create_footer() -> Footer {
|
||||
Footer(docx_rs::Footer::new())
|
||||
}
|
||||
|
||||
impl Footer {
|
||||
pub fn take(self) -> docx_rs::Footer {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Footer {
|
||||
pub fn add_paragraph(mut self, p: Paragraph) -> Self {
|
||||
self.0 = self.0.add_paragraph(p.take());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_table(mut self, t: Table) -> Self {
|
||||
self.0 = self.0.add_table(t.take());
|
||||
self
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ mod adaptors;
|
|||
mod comment;
|
||||
mod delete;
|
||||
mod doc;
|
||||
mod footer;
|
||||
mod insert;
|
||||
mod level;
|
||||
mod level_override;
|
||||
|
@ -23,6 +24,7 @@ pub use adaptors::*;
|
|||
pub use comment::*;
|
||||
pub use delete::*;
|
||||
pub use doc::*;
|
||||
pub use footer::*;
|
||||
pub use insert::*;
|
||||
pub use level::*;
|
||||
pub use level_override::*;
|
||||
|
|
|
@ -10,6 +10,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -134,6 +135,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -157,11 +159,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -383,6 +387,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -793,6 +798,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -816,11 +822,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -1716,6 +1724,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -2260,6 +2269,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -2283,11 +2293,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": true,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -2509,6 +2521,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -4778,6 +4791,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -4801,11 +4815,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -5399,6 +5415,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -5676,6 +5693,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -5699,11 +5717,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -5925,6 +5945,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -6363,6 +6384,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -6386,11 +6408,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -9711,6 +9735,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -10948,6 +10973,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -10971,11 +10997,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": true,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -13718,6 +13746,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -14245,6 +14274,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -14268,11 +14298,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -18168,6 +18200,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -19018,6 +19051,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -19041,11 +19075,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -19793,6 +19829,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -20303,6 +20340,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -20326,11 +20364,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -21060,6 +21100,7 @@ Object {
|
|||
},
|
||||
"contentType": Object {
|
||||
"custom_xml_count": 1,
|
||||
"footer_count": 1,
|
||||
"types": Object {
|
||||
"/_rels/.rels": "application/vnd.openxmlformats-package.relationships+xml",
|
||||
"/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||
|
@ -21366,6 +21407,7 @@ Object {
|
|||
"gridType": "lines",
|
||||
"linePitch": 360,
|
||||
},
|
||||
"footerReference": null,
|
||||
"headerReference": Object {
|
||||
"headerType": "default",
|
||||
"id": "rId4",
|
||||
|
@ -21389,11 +21431,13 @@ Object {
|
|||
},
|
||||
"documentRels": Object {
|
||||
"customXmlCount": 0,
|
||||
"footerCount": 0,
|
||||
"hasComments": false,
|
||||
"hasNumberings": false,
|
||||
"imageIds": Array [],
|
||||
},
|
||||
"fontTable": Object {},
|
||||
"footer": null,
|
||||
"header": Object {
|
||||
"children": Array [],
|
||||
},
|
||||
|
@ -22162,6 +22206,30 @@ exports[`writer should write doc vars 3`] = `
|
|||
</w:num></w:numbering>"
|
||||
`;
|
||||
|
||||
exports[`writer should write footer for default section 1`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>
|
||||
<Relationships xmlns=\\"http://schemas.openxmlformats.org/package/2006/relationships\\">
|
||||
<Relationship Id=\\"rId1\\" Type=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\\" Target=\\"styles.xml\\" />
|
||||
<Relationship Id=\\"rId2\\" Type=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable\\" Target=\\"fontTable.xml\\" />
|
||||
<Relationship Id=\\"rId3\\" Type=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings\\" Target=\\"settings.xml\\" />
|
||||
<Relationship Id=\\"rId4\\" Type=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header\\" Target=\\"header1.xml\\" />
|
||||
<Relationship Id=\\"rId5\\" Type=\\"http://schemas.microsoft.com/office/2011/relationships/commentsExtended\\" Target=\\"commentsExtended.xml\\" />
|
||||
<Relationship Id=\\"rIdFooter1\\" Type=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer\\" Target=\\"footer1.xml\\" />
|
||||
</Relationships>"
|
||||
`;
|
||||
|
||||
exports[`writer should write footer for default section 2`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\" standalone=\\"yes\\"?>
|
||||
<w:document xmlns:o=\\"urn:schemas-microsoft-com:office:office\\" xmlns:r=\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\\" 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\\" xmlns:w15=\\"http://schemas.microsoft.com/office/word/2012/wordml\\" mc:Ignorable=\\"w14 wp14\\">
|
||||
<w:body><w:p w14:paraId=\\"00000001\\"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr><w:rFonts /></w:rPr><w:t xml:space=\\"preserve\\">World </w:t></w:r></w:p><w:sectPr><w:pgSz w:w=\\"11906\\" w:h=\\"16838\\" /><w:pgMar w:top=\\"1985\\" w:right=\\"1701\\" w:bottom=\\"1701\\" w:left=\\"1701\\" w:header=\\"851\\" w:footer=\\"992\\" w:gutter=\\"0\\" /><w:headerReference w:type=\\"default\\" r:id=\\"rId4\\" /><w:cols w:space=\\"425\\" /><w:docGrid w:type=\\"lines\\" w:linePitch=\\"360\\" /><w:footerReference w:type=\\"default\\" r:id=\\"rIdFooter1\\" /></w:sectPr></w:body>
|
||||
</w:document>"
|
||||
`;
|
||||
|
||||
exports[`writer should write footer for default section 3`] = `
|
||||
"<?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=\\"00000002\\"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr><w:rFonts /></w:rPr><w:t xml:space=\\"preserve\\">Hello Footer</w:t></w:r></w:p></w:ftr>"
|
||||
`;
|
||||
|
||||
exports[`writer should write hello 1`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>
|
||||
<Relationships xmlns=\\"http://schemas.openxmlformats.org/package/2006/relationships\\">
|
||||
|
|
|
@ -362,4 +362,18 @@ describe("writer", () => {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("should write footer for default section", () => {
|
||||
const p1 = new w.Paragraph().addRun(new w.Run().addText("Hello Footer"));
|
||||
const p2 = new w.Paragraph().addRun(new w.Run().addText("World "));
|
||||
const footer = new w.Footer().addParagraph(p1);
|
||||
const buffer = new w.Docx().footer(footer).addParagraph(p2).build();
|
||||
writeFileSync("../output/footer.docx", buffer);
|
||||
const z = new Zip(Buffer.from(buffer));
|
||||
for (const e of z.getEntries()) {
|
||||
if (e.entryName.match(/document.xml|footer1.xml/)) {
|
||||
expect(z.readAsText(e)).toMatchSnapshot();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue