diff --git a/.gitignore b/.gitignore
index a4de448..1c7e3e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,9 @@ node_modules
pkg
*.docx#
test.docx
-output/*.docx
\ No newline at end of file
+output/*.docx
+vrt/screenshot/actual
+vrt/screenshot/diff
+vrt/report.html
+reg.json
+docx-core/tests/output/*.docx
\ No newline at end of file
diff --git a/docx-core/src/documents/document.rs b/docx-core/src/documents/document.rs
index a1a11af..3695b98 100644
--- a/docx-core/src/documents/document.rs
+++ b/docx-core/src/documents/document.rs
@@ -57,7 +57,7 @@ mod tests {
str::from_utf8(&b).unwrap(),
r#"
- Hello
+ Hello
"#
);
}
diff --git a/docx-core/src/documents/elements/paragraph.rs b/docx-core/src/documents/elements/paragraph.rs
index 8e43fc6..5fc7d91 100644
--- a/docx-core/src/documents/elements/paragraph.rs
+++ b/docx-core/src/documents/elements/paragraph.rs
@@ -73,7 +73,7 @@ mod tests {
let b = Paragraph::new().add_run(Run::new("Hello")).build();
assert_eq!(
str::from_utf8(&b).unwrap(),
- r#"Hello"#
+ r#"Hello"#
);
}
@@ -82,7 +82,7 @@ mod tests {
let b = Paragraph::new().add_run(Run::new("Hello")).size(60).build();
assert_eq!(
str::from_utf8(&b).unwrap(),
- r#"Hello"#
+ r#"Hello"#
);
}
}
diff --git a/docx-core/src/documents/elements/style.rs b/docx-core/src/documents/elements/style.rs
index 0a91b4c..9ffd107 100644
--- a/docx-core/src/documents/elements/style.rs
+++ b/docx-core/src/documents/elements/style.rs
@@ -75,7 +75,7 @@ mod tests {
let b = c.build();
assert_eq!(
str::from_utf8(&b).unwrap(),
- r#""#
+ r#""#
);
}
}
diff --git a/docx-core/src/documents/styles.rs b/docx-core/src/documents/styles.rs
index dd79f32..f1e8cfe 100644
--- a/docx-core/src/documents/styles.rs
+++ b/docx-core/src/documents/styles.rs
@@ -49,12 +49,12 @@ mod tests {
use std::str;
#[test]
- fn test_build() {
+ fn test_style() {
let c = Styles::new().add_style(Style::new("Title", "TitleName", StyleType::Paragraph));
let b = c.build();
assert_eq!(
str::from_utf8(&b).unwrap(),
- r#""#
+ r#""#
);
}
}
diff --git a/docx-core/src/types/border_type.rs b/docx-core/src/types/border_type.rs
new file mode 100644
index 0000000..40d9265
--- /dev/null
+++ b/docx-core/src/types/border_type.rs
@@ -0,0 +1,199 @@
+//
+// Please see p3813
+//
+
+/*
+"nil"
+"none"
+"single"
+"thick"
+"double"
+"dotted"
+"dashed"
+"dotDash"
+"dotDotDash"
+"triple"
+"thinThickSmallGap"
+"thickThinSmallGap"
+"thinThickThinSmallGap"
+"thinThickMediumGap"
+"thickThinMediumGap"
+"thinThickThinMediumGap"
+"thinThickLargeGap"
+"thickThinLargeGap"
+"thinThickThinLargeGap"
+"wave"
+"doubleWave"
+"dashSmallGap"
+"dashDotStroked"
+"threeDEmboss"
+"threeDEngrave"
+"outset"
+"inset"
+"apples"
+"archedScallops"
+"babyPacifier"
+"babyRattle"
+"balloons3Colors"
+"balloonsHotAir"
+"basicBlackDashes"
+"basicBlackDots"
+"basicBlackSquares"
+"basicThinLines"
+"basicWhiteDashes"
+"basicWhiteDots"
+"basicWhiteSquares"
+"basicWideInline"
+"basicWideMidline"
+"basicWideOutline"
+"bats"
+"birds"
+"birdsFlight"
+"cabins"
+"cakeSlice"
+"candyCorn"
+"celticKnotwork"
+"certificateBanner"
+"chainLink"
+"champagneBottle"
+"checkedBarBlack"
+"checkedBarColor"
+"checkered"
+"christmasTree"
+"circlesLines"
+"circlesRectangles"
+"classicalWave"
+"clocks"
+"compass"
+"confetti"
+"confettiGrays"
+"confettiOutline"
+"confettiStreamers"
+"confettiWhite"
+"cornerTriangles"
+"couponCutoutDashes"
+"couponCutoutDots"
+"crazyMaze"
+"creaturesButterfly"
+"creaturesFish"
+"creaturesInsects"
+"creaturesLadyBug"
+"crossStitch"
+"cup"
+"decoArch"
+"decoArchColor"
+"decoBlocks"
+"diamondsGray"
+"doubleD"
+"doubleDiamonds"
+"earth1"
+"earth2"
+"earth3"
+"eclipsingSquares1"
+"eclipsingSquares2"
+"eggsBlack"
+"fans"
+"film"
+"firecrackers"
+"flowersBlockPrint"
+"flowersDaisies"
+"flowersModern1"
+"flowersModern2"
+"flowersPansy"
+"flowersRedRose"
+"flowersRoses"
+"flowersTeacup"
+"flowersTiny"
+"gems"
+"gingerbreadMan"
+"gradient"
+"handmade1"
+"handmade2"
+"heartBalloon"
+"heartGray"
+"hearts"
+"heebieJeebies"
+"holly"
+"houseFunky"
+"hypnotic"
+"iceCreamCones"
+"lightBulb"
+"lightning1"
+"lightning2"
+"mapPins"
+"mapleLeaf"
+"mapleMuffins"
+"marquee"
+"marqueeToothed"
+"moons"
+"mosaic"
+"musicNotes"
+"northwest"
+"ovals"
+"packages"
+"palmsBlack"
+"palmsColor"
+"paperClips"
+"papyrus"
+"partyFavor"
+"partyGlass"
+"pencils"
+"people"
+"peopleWaving"
+"peopleHats"
+"poinsettias"
+"postageStamp"
+"pumpkin1"
+"pushPinNote2"
+"pushPinNote1"
+"pyramids"
+"pyramidsAbove"
+"quadrants"
+"rings"
+"safari"
+"sawtooth"
+"sawtoothGray"
+"scaredCat"
+"seattle"
+"shadowedSquares"
+"sharksTeeth"
+"shorebirdTracks"
+"skyrocket"
+"snowflakeFancy"
+"snowflakes"
+"sombrero"
+"southwest"
+"stars"
+"starsTop"
+"stars3d"
+"starsBlack"
+"starsShadowed"
+"sun"
+"swirligig"
+"tornPaper"
+"tornPaperBlack"
+"trees"
+"triangleParty"
+"triangles"
+"triangle1"
+"triangle2"
+"triangleCircle1"
+"triangleCircle2"
+"shapes1"
+"shapes2"
+"twistedLines1"
+"twistedLines2"
+"vine"
+"waveline"
+"weavingAngles"
+"weavingBraid"
+"weavingRibbon"
+"weavingStrips"
+"whiteFlowers"
+"woodwork"
+"xIllusions"
+"zanyTriangles"
+"zigZag"
+"zigZagStitch"
+"custom"
+*/
diff --git a/docx-core/src/types/mod.rs b/docx-core/src/types/mod.rs
index 243f6c6..5798544 100644
--- a/docx-core/src/types/mod.rs
+++ b/docx-core/src/types/mod.rs
@@ -1,7 +1,9 @@
pub mod alignment_type;
pub mod special_indent_type;
pub mod style_type;
+pub mod width_type;
pub use alignment_type::*;
pub use special_indent_type::*;
pub use style_type::*;
+pub use width_type::*;
diff --git a/docx-core/src/types/width_type.rs b/docx-core/src/types/width_type.rs
new file mode 100644
index 0000000..f51bcfc
--- /dev/null
+++ b/docx-core/src/types/width_type.rs
@@ -0,0 +1,16 @@
+use std::fmt;
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+#[derive(Copy, Clone, Debug)]
+pub enum WidthType {
+ DXA,
+}
+
+impl fmt::Display for WidthType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ WidthType::DXA => write!(f, "dxa"),
+ }
+ }
+}
diff --git a/docx-core/src/xml_builder/elements.rs b/docx-core/src/xml_builder/elements.rs
index 34bb5e9..db10969 100644
--- a/docx-core/src/xml_builder/elements.rs
+++ b/docx-core/src/xml_builder/elements.rs
@@ -87,6 +87,29 @@ impl XMLBuilder {
};
self.close()
}
+
+ //
+ // Table elements
+ //
+ opened_el!(open_table, "w:tbl");
+ opened_el!(open_table_property, "w:tblPr");
+ opened_el!(open_table_grid, "w:tblGrid");
+ opened_el!(open_table_row, "w:tr");
+ opened_el!(open_table_row_property, "w:trPr");
+ opened_el!(open_table_cell, "w:tc");
+ opened_el!(open_table_cell_property, "w:tcPr");
+ opened_el!(open_table_borders, "w:tblBorders");
+ opened_el!(open_table_cell_margins, "w:tblCellMar");
+
+ closed_w_with_type_el!(table_width, "w:tblW");
+ closed_w_with_type_el!(table_indent, "w:tblInd");
+ closed_w_with_type_el!(grid_column, "w:gridCol");
+ closed_w_with_type_el!(table_cell_width, "w:tcW");
+
+ // TODO:
+ // w:shd
+ // w:top/left/bottom/right
+ // w:insideH/insideV
}
#[cfg(test)]
diff --git a/docx-core/src/xml_builder/macros.rs b/docx-core/src/xml_builder/macros.rs
index ad3981d..daf211f 100644
--- a/docx-core/src/xml_builder/macros.rs
+++ b/docx-core/src/xml_builder/macros.rs
@@ -152,3 +152,33 @@ macro_rules! only_usize_val_el {
}
};
}
+
+macro_rules! closed_w_with_type_el {
+ ($name: ident, $el_name: expr) => {
+ pub(crate) fn $name(mut self, w: usize, t: WidthType) -> Self {
+ self.writer
+ .write(
+ XmlEvent::start_element($el_name)
+ .attr("w:w", &format!("{}", w))
+ .attr("w:type", &t.to_string()),
+ )
+ .expect(EXPECT_MESSAGE);
+ self.close()
+ }
+ };
+}
+
+macro_rules! closed_border_el {
+ ($name: ident, $el_name: expr) => {
+ pub(crate) fn $name(mut self, size: usize, space: usize, color: &str) -> Self {
+ self.writer
+ .write(
+ XmlEvent::start_element($el_name)
+ .attr("w:w", &format!("{}", w))
+ .attr("w:type", &t.to_string()),
+ )
+ .expect(EXPECT_MESSAGE);
+ self.close()
+ }
+ };
+}
diff --git a/docx-core/tests/lib.rs b/docx-core/tests/lib.rs
new file mode 100644
index 0000000..f705007
--- /dev/null
+++ b/docx-core/tests/lib.rs
@@ -0,0 +1,60 @@
+extern crate docx_core;
+
+use docx_core::*;
+
+pub const DUMMY: &str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+
+#[test]
+pub fn indent() -> Result<(), DocxError> {
+ let path = std::path::Path::new("./tests/output/indent.docx");
+ let file = std::fs::File::create(&path).unwrap();
+ Docx::new()
+ .add_paragraph(Paragraph::new().add_run(Run::new(DUMMY)).indent(840, None))
+ .add_paragraph(Paragraph::new())
+ .add_paragraph(
+ Paragraph::new()
+ .add_run(Run::new(DUMMY))
+ .indent(840, Some(SpecialIndentType::FirstLine(720))),
+ )
+ .add_paragraph(Paragraph::new())
+ .add_paragraph(
+ Paragraph::new()
+ .add_run(Run::new(DUMMY))
+ .indent(1560, Some(SpecialIndentType::Hanging(720))),
+ )
+ .build()
+ .pack(file)?;
+ Ok(())
+}
+
+#[test]
+pub fn size() -> Result<(), DocxError> {
+ let path = std::path::Path::new("./tests/output/size.docx");
+ let file = std::fs::File::create(&path).unwrap();
+ Docx::new()
+ .add_paragraph(Paragraph::new().add_run(Run::new("Hello")).size(60))
+ .add_paragraph(
+ Paragraph::new()
+ .add_run(Run::new(" Wor").size(50))
+ .add_run(Run::new("ld")),
+ )
+ .build()
+ .pack(file)?;
+ Ok(())
+}
+
+#[test]
+pub fn alignment() -> Result<(), DocxError> {
+ let path = std::path::Path::new("./tests/output/alignment.docx");
+ let file = std::fs::File::create(&path).unwrap();
+ Docx::new()
+ .add_paragraph(Paragraph::new().add_run(Run::new("Hello")))
+ .add_paragraph(
+ Paragraph::new()
+ .add_run(Run::new(" World"))
+ .align(AlignmentType::Right),
+ )
+ .build()
+ .pack(file)?;
+ Ok(())
+}
diff --git a/docx-core/tests/output/.keep b/docx-core/tests/output/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..b48caa3
--- /dev/null
+++ b/makefile
@@ -0,0 +1,8 @@
+test:
+ cargo test
+
+vrt:
+ node vrt/index.js && reg-cli vrt/screenshot/actual vrt/screenshot/expected vrt/screenshot/diff -R vrt/report.html
+
+vrt-update:
+ node vrt/index.js && reg-cli vrt/screenshot/actual vrt/screenshot/expected vrt/screenshot/diff -R vrt/report.html -U
\ No newline at end of file
diff --git a/vrt/index.js b/vrt/index.js
new file mode 100644
index 0000000..1d4a562
--- /dev/null
+++ b/vrt/index.js
@@ -0,0 +1,13 @@
+const glob = require("glob");
+const path = require("path");
+const createPDF = require("./pdf");
+
+glob(
+ path.join(__dirname, "..", "./docx-core/tests/output/**/*.docx"),
+ {},
+ async (err, files) => {
+ for await (file of files) {
+ await createPDF(file, path.join(__dirname, "./screenshot/actual"));
+ }
+ }
+);
diff --git a/vrt/package.json b/vrt/package.json
new file mode 100644
index 0000000..7120501
--- /dev/null
+++ b/vrt/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "docx-rs",
+ "version": "1.0.0",
+ "main": "index.js",
+ "repository": "https://github.com/bokuweb/docx-rs.git",
+ "author": "bokuweb ",
+ "license": "MIT",
+ "devDependencies": {
+ "libreoffice-convert": "^1.0.3"
+ },
+ "dependencies": {
+ "glob": "^7.1.6"
+ }
+}
diff --git a/vrt/pdf.js b/vrt/pdf.js
new file mode 100644
index 0000000..ba5e597
--- /dev/null
+++ b/vrt/pdf.js
@@ -0,0 +1,25 @@
+const libre = require("libreoffice-convert");
+const path = require("path");
+const fs = require("fs");
+
+const extend = "png";
+
+module.exports = (docxPath, outputDir) =>
+ new Promise((resolve, reject) => {
+ const filename = path.basename(docxPath, ".docx");
+ const docxFile = fs.readFileSync(docxPath);
+ libre.convert(docxFile, extend, undefined, async (err, done) => {
+ if (err) {
+ reject(err);
+ }
+ try {
+ fs.mkdirSync(outputDir, { recursive: true });
+ } catch (e) {
+ if (e.code !== "EEXIST") {
+ reject(e);
+ }
+ }
+ fs.writeFileSync(path.join(outputDir, `${filename}.${extend}`), done);
+ resolve();
+ });
+ });
diff --git a/vrt/screenshot/expected/alignment.png b/vrt/screenshot/expected/alignment.png
new file mode 100644
index 0000000..3022f9b
Binary files /dev/null and b/vrt/screenshot/expected/alignment.png differ
diff --git a/vrt/screenshot/expected/indent.png b/vrt/screenshot/expected/indent.png
new file mode 100644
index 0000000..ca69f6b
Binary files /dev/null and b/vrt/screenshot/expected/indent.png differ
diff --git a/vrt/screenshot/expected/size.png b/vrt/screenshot/expected/size.png
new file mode 100644
index 0000000..e49024c
Binary files /dev/null and b/vrt/screenshot/expected/size.png differ
diff --git a/vrt/yarn.lock b/vrt/yarn.lock
new file mode 100644
index 0000000..25d6e64
--- /dev/null
+++ b/vrt/yarn.lock
@@ -0,0 +1,109 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+async@^2.6.2:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
+ integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
+ dependencies:
+ lodash "^4.17.14"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+glob@^7.1.3, glob@^7.1.6:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+libreoffice-convert@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/libreoffice-convert/-/libreoffice-convert-1.0.3.tgz#ddcee6da4f271b6d161d9dfd8bf17db534e099a2"
+ integrity sha512-dr2cM5+pV0OGLxWYVEUKFdEBMe9cyaIiQ/OfnsI1aVYVSDDyaHA6dURRGzCSu0pAtIG3jUfvxqMSv8wWx6ttqQ==
+ dependencies:
+ async "^2.6.2"
+ temp "^0.9.0"
+
+lodash@^4.17.14:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+rimraf@~2.6.2:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
+ dependencies:
+ glob "^7.1.3"
+
+temp@^0.9.0:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.1.tgz#2d666114fafa26966cd4065996d7ceedd4dd4697"
+ integrity sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA==
+ dependencies:
+ rimraf "~2.6.2"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=