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 doc_defaults;
mod doc_id;
mod doc_var;
mod drawing;
mod font;
mod grid_span;
@ -102,6 +103,7 @@ pub use delete::*;
pub use delete_text::*;
pub use doc_defaults::*;
pub use doc_id::*;
pub use doc_var::*;
pub use drawing::*;
pub use font::*;
pub use grid_span::*;

View File

@ -199,6 +199,11 @@ impl Docx {
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 {
self.document = self.document.page_size(PageSize::new().size(w, h));
self

View File

@ -11,6 +11,7 @@ pub struct Settings {
default_tab_stop: DefaultTabStop,
zoom: Zoom,
doc_id: Option<DocId>,
doc_vars: Vec<DocVar>,
}
impl Settings {
@ -22,6 +23,11 @@ impl Settings {
self.doc_id = Some(DocId::new(id.into()));
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 {
@ -30,6 +36,7 @@ impl Default for Settings {
default_tab_stop: DefaultTabStop::new(709),
zoom: Zoom::new(100),
doc_id: None,
doc_vars: vec![],
}
}
}
@ -37,7 +44,8 @@ impl Default for Settings {
impl BuildXML for Settings {
fn build(&self) -> Vec<u8> {
let b = XMLBuilder::new();
b.declaration(Some(true))
let mut b = b
.declaration(Some(true))
.open_settings()
.add_child(&self.default_tab_stop)
.add_child(&self.zoom)
@ -80,9 +88,16 @@ impl BuildXML for Settings {
"0",
)
.close()
.add_optional_child(&self.doc_id)
.close()
.build()
.add_optional_child(&self.doc_id);
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 indent;
mod indent_level;
mod name;
mod val;
mod width;
@ -9,5 +10,6 @@ pub use bool_value::*;
pub use border::*;
pub use indent::*;
pub use indent_level::*;
pub use name::*;
pub use val::*;
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,7 +18,8 @@ impl FromXML for Settings {
attributes, name, ..
}) => {
let e = XMLElement::from_str(&name.local_name).unwrap();
if let XMLElement::DocId = e {
match e {
XMLElement::DocId => {
for a in attributes {
if let Some(prefix) = a.name.prefix {
let local_name = &a.name.local_name;
@ -31,6 +32,17 @@ impl FromXML for Settings {
}
}
}
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, .. }) => {
let e = XMLElement::from_str(&name.local_name).unwrap();

View File

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

View File

@ -311,6 +311,9 @@ impl XMLBuilder {
closed!(doc_id, "w15:docId", "w15:val");
open!(open_doc_vars, "w:docVars");
closed!(doc_var, "w:docVar", "w:name", "w:val");
// CommentExtended
// w15:commentEx w15:paraId="00000001" w15:paraIdParent="57D1BD7C" w15:done="0"
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;
}
addDocVar(name: string, val: string) {
this.settings.addDocVar(name, val);
return this;
}
pageSize(w: number, h: number) {
this.sectionProperty.pageSize(w, h);
return this;
@ -606,6 +611,10 @@ export class Docx {
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) {
const {
top,

View File

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

View File

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

View File

@ -48,6 +48,11 @@ impl Docx {
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 {
self.0 = self.0.page_size(w, h);
self

View File

@ -1888,6 +1888,7 @@ Object {
"settings": Object {
"defaultTabStop": 709,
"docId": "E43E077A-3477-A242-BD53-4313974E06A2",
"docVars": Array [],
"zoom": 100,
},
"styles": Object {
@ -4185,6 +4186,7 @@ Object {
"settings": Object {
"defaultTabStop": 709,
"docId": "10BE20B6-DCA9-7441-B548-606D7D9EDD92",
"docVars": Array [],
"zoom": 100,
},
"styles": Object {
@ -5446,6 +5448,7 @@ Object {
"settings": Object {
"defaultTabStop": 709,
"docId": "C272B628-6DE8-264A-8734-9D2219FFD42F",
"docVars": Array [],
"zoom": 100,
},
"styles": Object {
@ -5984,6 +5987,33 @@ exports[`writer should write default font 3`] = `
</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`] = `
"<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>
<Relationships xmlns=\\"http://schemas.openxmlformats.org/package/2006/relationships\\">

View File

@ -94,7 +94,19 @@ describe("writer", () => {
.defaultSize(40)
.defaultFonts(fonts)
.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));
for (const e of z.getEntries()) {
if (e.entryName.match(/document.xml|numbering.xml/)) {