Support doc vars (#211)

* feat: Impl doc_var writer

* feat: Impl doc_vars reader

* update snaps
main
bokuweb 2020-12-15 15:38:17 +09:00 committed by GitHub
parent 71991d97b8
commit e346121dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 192 additions and 44 deletions

View File

@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
use crate::documents::BuildXML;
use crate::xml_builder::*;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocVar {
name: String,
val: String,
}
impl DocVar {
pub fn new(name: impl Into<String>, val: impl Into<String>) -> DocVar {
DocVar {
name: name.into(),
val: val.into(),
}
}
}
impl BuildXML for DocVar {
fn build(&self) -> Vec<u8> {
let b = XMLBuilder::new();
b.doc_var(&self.name, &self.val).build()
}
}

View File

@ -17,6 +17,7 @@ mod delete;
mod delete_text; mod delete_text;
mod doc_defaults; mod doc_defaults;
mod doc_id; mod doc_id;
mod doc_var;
mod drawing; mod drawing;
mod font; mod font;
mod grid_span; mod grid_span;
@ -102,6 +103,7 @@ pub use delete::*;
pub use delete_text::*; pub use delete_text::*;
pub use doc_defaults::*; pub use doc_defaults::*;
pub use doc_id::*; pub use doc_id::*;
pub use doc_var::*;
pub use drawing::*; pub use drawing::*;
pub use font::*; pub use font::*;
pub use grid_span::*; pub use grid_span::*;

View File

@ -199,6 +199,11 @@ impl Docx {
self self
} }
pub fn add_doc_var(mut self, name: &str, val: &str) -> Self {
self.settings = self.settings.add_doc_var(name, val);
self
}
pub fn page_size(mut self, w: u32, h: u32) -> Self { pub fn page_size(mut self, w: u32, h: u32) -> Self {
self.document = self.document.page_size(PageSize::new().size(w, h)); self.document = self.document.page_size(PageSize::new().size(w, h));
self self

View File

@ -11,6 +11,7 @@ pub struct Settings {
default_tab_stop: DefaultTabStop, default_tab_stop: DefaultTabStop,
zoom: Zoom, zoom: Zoom,
doc_id: Option<DocId>, doc_id: Option<DocId>,
doc_vars: Vec<DocVar>,
} }
impl Settings { impl Settings {
@ -22,6 +23,11 @@ impl Settings {
self.doc_id = Some(DocId::new(id.into())); self.doc_id = Some(DocId::new(id.into()));
self self
} }
pub fn add_doc_var(mut self, name: impl Into<String>, val: impl Into<String>) -> Self {
self.doc_vars.push(DocVar::new(name, val));
self
}
} }
impl Default for Settings { impl Default for Settings {
@ -30,6 +36,7 @@ impl Default for Settings {
default_tab_stop: DefaultTabStop::new(709), default_tab_stop: DefaultTabStop::new(709),
zoom: Zoom::new(100), zoom: Zoom::new(100),
doc_id: None, doc_id: None,
doc_vars: vec![],
} }
} }
} }
@ -37,7 +44,8 @@ impl Default for Settings {
impl BuildXML for Settings { impl BuildXML for Settings {
fn build(&self) -> Vec<u8> { fn build(&self) -> Vec<u8> {
let b = XMLBuilder::new(); let b = XMLBuilder::new();
b.declaration(Some(true)) let mut b = b
.declaration(Some(true))
.open_settings() .open_settings()
.add_child(&self.default_tab_stop) .add_child(&self.default_tab_stop)
.add_child(&self.zoom) .add_child(&self.zoom)
@ -80,9 +88,16 @@ impl BuildXML for Settings {
"0", "0",
) )
.close() .close()
.add_optional_child(&self.doc_id) .add_optional_child(&self.doc_id);
.close()
.build() if !self.doc_vars.is_empty() {
b = b.open_doc_vars();
for v in self.doc_vars.iter() {
b = b.add_child(v);
}
b = b.close();
}
b.close().build()
} }
} }

View File

@ -2,6 +2,7 @@ mod bool_value;
mod border; mod border;
mod indent; mod indent;
mod indent_level; mod indent_level;
mod name;
mod val; mod val;
mod width; mod width;
@ -9,5 +10,6 @@ pub use bool_value::*;
pub use border::*; pub use border::*;
pub use indent::*; pub use indent::*;
pub use indent_level::*; pub use indent_level::*;
pub use name::*;
pub use val::*; pub use val::*;
pub use width::*; pub use width::*;

View File

@ -0,0 +1,11 @@
use xml::attribute::OwnedAttribute;
pub fn read_name(attrs: &[OwnedAttribute]) -> Option<String> {
for a in attrs {
let local_name = &a.name.local_name;
if local_name == "name" {
return Some(a.value.to_owned());
}
}
None
}

View File

@ -18,18 +18,30 @@ impl FromXML for Settings {
attributes, name, .. attributes, name, ..
}) => { }) => {
let e = XMLElement::from_str(&name.local_name).unwrap(); let e = XMLElement::from_str(&name.local_name).unwrap();
if let XMLElement::DocId = e { match e {
for a in attributes { XMLElement::DocId => {
if let Some(prefix) = a.name.prefix { for a in attributes {
let local_name = &a.name.local_name; if let Some(prefix) = a.name.prefix {
// Ignore w14:val let local_name = &a.name.local_name;
if local_name == "val" && prefix == "w15" { // Ignore w14:val
settings = settings.doc_id( if local_name == "val" && prefix == "w15" {
&a.value.to_owned().replace("{", "").replace("}", ""), settings = settings.doc_id(
); &a.value.to_owned().replace("{", "").replace("}", ""),
);
}
} }
} }
} }
XMLElement::DocVar => {
let name = attributes::read_name(&attributes);
let val = attributes::read_val(&attributes);
if let Some(name) = name {
if let Some(val) = val {
settings = settings.add_doc_var(name, val);
}
}
}
_ => {}
} }
} }
Ok(XmlEvent::EndElement { name, .. }) => { Ok(XmlEvent::EndElement { name, .. }) => {

View File

@ -101,6 +101,8 @@ pub enum XMLElement {
TxbxContent, TxbxContent,
Pict, Pict,
DocId, DocId,
DocVars,
DocVar,
DocDefaults, DocDefaults,
RunPropertyDefault, RunPropertyDefault,
SectionProperty, SectionProperty,
@ -258,6 +260,8 @@ impl FromStr for XMLElement {
"lvlOverride" => Ok(XMLElement::LvlOverride), "lvlOverride" => Ok(XMLElement::LvlOverride),
"startOverride" => Ok(XMLElement::StartOverride), "startOverride" => Ok(XMLElement::StartOverride),
"docId" => Ok(XMLElement::DocId), "docId" => Ok(XMLElement::DocId),
"docVar" => Ok(XMLElement::DocVar),
"docVars" => Ok(XMLElement::DocVars),
"sectPr" => Ok(XMLElement::SectionProperty), "sectPr" => Ok(XMLElement::SectionProperty),
"pgSz" => Ok(XMLElement::PageSize), "pgSz" => Ok(XMLElement::PageSize),
"rFonts" => Ok(XMLElement::RunFonts), "rFonts" => Ok(XMLElement::RunFonts),

View File

@ -311,6 +311,9 @@ impl XMLBuilder {
closed!(doc_id, "w15:docId", "w15:val"); closed!(doc_id, "w15:docId", "w15:val");
open!(open_doc_vars, "w:docVars");
closed!(doc_var, "w:docVar", "w:name", "w:val");
// CommentExtended // CommentExtended
// w15:commentEx w15:paraId="00000001" w15:paraIdParent="57D1BD7C" w15:done="0" // w15:commentEx w15:paraId="00000001" w15:paraIdParent="57D1BD7C" w15:done="0"
pub(crate) fn comment_extended( pub(crate) fn comment_extended(

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

View File

@ -100,6 +100,11 @@ export class Docx {
return this; return this;
} }
addDocVar(name: string, val: string) {
this.settings.addDocVar(name, val);
return this;
}
pageSize(w: number, h: number) { pageSize(w: number, h: number) {
this.sectionProperty.pageSize(w, h); this.sectionProperty.pageSize(w, h);
return this; return this;
@ -606,6 +611,10 @@ export class Docx {
docx = docx.doc_id(this.settings._docId); docx = docx.doc_id(this.settings._docId);
} }
this.settings._docVars.forEach((v) => {
docx = docx.add_doc_var(v.name, v.val);
});
if (this.sectionProperty._pageMargin) { if (this.sectionProperty._pageMargin) {
const { const {
top, top,

View File

@ -39,6 +39,7 @@ export type DocxJSON = {
docId: string | null; docId: string | null;
defaultTabStop: number; defaultTabStop: number;
zoom: number; zoom: number;
docVars: { name: string; val: string }[];
}; };
fontTable: {}; fontTable: {};
}; };

View File

@ -1,8 +1,19 @@
export type DocVar = {
name: string;
val: string;
};
export class Settings { export class Settings {
_docId: string | null = null; _docId: string | null = null;
_docVars: DocVar[] = [];
docId(id: string) { docId(id: string) {
this._docId = id; this._docId = id;
return this; return this;
} }
addDocVar(name: string, val: string) {
this._docVars.push({ name, val });
return this;
}
} }

View File

@ -48,6 +48,11 @@ impl Docx {
self self
} }
pub fn add_doc_var(mut self, name: &str, val: &str) -> Docx {
self.0 = self.0.add_doc_var(name, val);
self
}
pub fn page_size(mut self, w: u32, h: u32) -> Docx { pub fn page_size(mut self, w: u32, h: u32) -> Docx {
self.0 = self.0.page_size(w, h); self.0 = self.0.page_size(w, h);
self self

View File

@ -1888,6 +1888,7 @@ Object {
"settings": Object { "settings": Object {
"defaultTabStop": 709, "defaultTabStop": 709,
"docId": "E43E077A-3477-A242-BD53-4313974E06A2", "docId": "E43E077A-3477-A242-BD53-4313974E06A2",
"docVars": Array [],
"zoom": 100, "zoom": 100,
}, },
"styles": Object { "styles": Object {
@ -4185,6 +4186,7 @@ Object {
"settings": Object { "settings": Object {
"defaultTabStop": 709, "defaultTabStop": 709,
"docId": "10BE20B6-DCA9-7441-B548-606D7D9EDD92", "docId": "10BE20B6-DCA9-7441-B548-606D7D9EDD92",
"docVars": Array [],
"zoom": 100, "zoom": 100,
}, },
"styles": Object { "styles": Object {
@ -5446,6 +5448,7 @@ Object {
"settings": Object { "settings": Object {
"defaultTabStop": 709, "defaultTabStop": 709,
"docId": "C272B628-6DE8-264A-8734-9D2219FFD42F", "docId": "C272B628-6DE8-264A-8734-9D2219FFD42F",
"docVars": Array [],
"zoom": 100, "zoom": 100,
}, },
"styles": Object { "styles": Object {
@ -5984,6 +5987,33 @@ exports[`writer should write default font 3`] = `
</w:num></w:numbering>" </w:num></w:numbering>"
`; `;
exports[`writer should write doc vars 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\\" />
</Relationships>"
`;
exports[`writer should write doc vars 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=\\"0000002f\\"><w:pPr><w:rPr /></w:pPr><w:r><w:rPr><w:rFonts /></w:rPr><w:t xml:space=\\"preserve\\">Hello 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:sectPr></w:body>
</w:document>"
`;
exports[`writer should write doc vars 3`] = `
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\" standalone=\\"yes\\"?>
<w:numbering 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\\"><w:abstractNum w:abstractNumId=\\"1\\"><w:lvl w:ilvl=\\"0\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"%1.\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"420\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"1\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"(%2)\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"840\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"2\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimalEnclosedCircle\\" /><w:lvlText w:val=\\"%3\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"1260\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"3\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"%4.\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"1680\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"4\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"(%5)\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"2100\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"5\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimalEnclosedCircle\\" /><w:lvlText w:val=\\"%6\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"2520\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"6\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"%7.\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"2940\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"7\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimal\\" /><w:lvlText w:val=\\"(%8)\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"3360\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl><w:lvl w:ilvl=\\"8\\"><w:start w:val=\\"1\\" /><w:numFmt w:val=\\"decimalEnclosedCircle\\" /><w:lvlText w:val=\\"%9\\" /><w:lvlJc w:val=\\"left\\" /><w:pPr><w:rPr /><w:ind w:left=\\"3780\\" w:right=\\"0\\" w:hanging=\\"420\\" /></w:pPr><w:rPr /></w:lvl></w:abstractNum><w:num w:numId=\\"1\\">
<w:abstractNumId w:val=\\"1\\" />
</w:num></w:numbering>"
`;
exports[`writer should write hello 1`] = ` exports[`writer should write hello 1`] = `
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?> "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>
<Relationships xmlns=\\"http://schemas.openxmlformats.org/package/2006/relationships\\"> <Relationships xmlns=\\"http://schemas.openxmlformats.org/package/2006/relationships\\">

View File

@ -94,7 +94,19 @@ describe("writer", () => {
.defaultSize(40) .defaultSize(40)
.defaultFonts(fonts) .defaultFonts(fonts)
.build(); .build();
writeFileSync("default_font.docx", buf); writeFileSync("../output/default_font.docx", buf);
const z = new Zip(Buffer.from(buf));
for (const e of z.getEntries()) {
if (e.entryName.match(/document.xml|numbering.xml/)) {
expect(z.readAsText(e)).toMatchSnapshot();
}
}
});
test("should write doc vars", () => {
const p = new w.Paragraph().addRun(new w.Run().addText("Hello world!!!!"));
const buf = new w.Docx().addParagraph(p).addDocVar("foo", "bar").build();
writeFileSync("../output/doc_vars.docx", buf);
const z = new Zip(Buffer.from(buf)); const z = new Zip(Buffer.from(buf));
for (const e of z.getEntries()) { for (const e of z.getEntries()) {
if (e.entryName.match(/document.xml|numbering.xml/)) { if (e.entryName.match(/document.xml|numbering.xml/)) {