commonform-docx 5.5.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin.js +47 -40
- package/escape.js +3 -5
- package/index.js +60 -33
- package/lint.log +4 -0
- package/package.json +5 -5
- package/templates/document-relationships.js +15 -0
- package/templates/document.js +28 -21
- package/templates/hash.js +4 -5
- package/templates/paragraph.js +150 -92
- package/templates/run.js +43 -51
- package/templates/tag.js +2 -2
- package/templates/title.js +6 -7
- package/test.log +54 -51
package/README.md
CHANGED
package/bin.js
CHANGED
|
@@ -1,52 +1,55 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
const docopt = require('@kemitchell/docopt').docopt
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const has = require('has')
|
|
5
|
+
const path = require('path')
|
|
6
6
|
|
|
7
7
|
// Parse arguments and options.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const usage = [
|
|
10
10
|
'Usage:',
|
|
11
11
|
' commonform-docx [options] <FILE>',
|
|
12
12
|
'',
|
|
13
13
|
'Options:',
|
|
14
|
-
' -h, --help
|
|
15
|
-
' -v, --version
|
|
16
|
-
' -H, --hash
|
|
17
|
-
' -a, --a4-paper
|
|
18
|
-
' -b TEXT, --blank-text TEXT
|
|
19
|
-
' -
|
|
20
|
-
' -
|
|
21
|
-
' -
|
|
22
|
-
' -
|
|
23
|
-
'
|
|
24
|
-
' -
|
|
25
|
-
' -
|
|
26
|
-
' -
|
|
27
|
-
' -
|
|
28
|
-
' -
|
|
29
|
-
' -
|
|
30
|
-
' -
|
|
14
|
+
' -h, --help Show this screen.',
|
|
15
|
+
' -v, --version Show version.',
|
|
16
|
+
' -H, --hash Render form hash',
|
|
17
|
+
' -a, --a4-paper A4 paper',
|
|
18
|
+
' -b TEXT, --blank-text TEXT Render blanks with custom text',
|
|
19
|
+
' --component-style STYLE Change component style [default: both]',
|
|
20
|
+
' -d JSON --directions JSON Use directions to fill in blanks',
|
|
21
|
+
' -e VERSION, --form-version VERSION Form version to be rendered',
|
|
22
|
+
' -i, --indent-margins Indent margins, commonwealth style',
|
|
23
|
+
' --incorporate-component-text TEXT Verb for component references [default: Incorporate]',
|
|
24
|
+
' --quote-component-text TEXT Text for introducing quoted components',
|
|
25
|
+
' -l, --left-align-title Align title flush to left margin',
|
|
26
|
+
' -m, --mark-filled Mark filled blanks',
|
|
27
|
+
' -p, --smart Render Unicode punctuation',
|
|
28
|
+
' -n STYLE, --number STYLE Numbering style [default: decimal]',
|
|
29
|
+
' -r --left-align-body Left-align body paragraphs.',
|
|
30
|
+
' -s PAGES, --signatures PAGES Signature page data',
|
|
31
|
+
' -t TITLE, --title TITLE Render title as <h1>',
|
|
32
|
+
' -v JSON --values JSON Use values to fill in blanks',
|
|
33
|
+
' -y JSON, --styles JSON Render with custom styles'
|
|
31
34
|
].join('\n')
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
const parsed = docopt(usage, { version: require('./package.json').version })
|
|
34
37
|
|
|
35
38
|
// Parse arguments and options.
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
const form = readJSON(parsed['<FILE>'])
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const values = parsed['--values'] ? readJSON(parsed['--values']) : {}
|
|
43
|
+
const directions = parsed['--directions'] ? readJSON(parsed['--directions']) : []
|
|
44
|
+
const blanks = require('commonform-prepare-blanks')(values, directions)
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
const options = {}
|
|
44
47
|
|
|
45
48
|
if (parsed['--title']) options.title = parsed['--title']
|
|
46
49
|
|
|
47
50
|
if (parsed['--number']) {
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const numberStyle = parsed['--number']
|
|
52
|
+
const supportedNumberingStyles = {
|
|
50
53
|
rse: 'resolutions-schedules-exhibits-numbering',
|
|
51
54
|
ase: 'agreement-schedules-exhibits-numbering',
|
|
52
55
|
pae: 'plan-addenda-exhibits-numbering',
|
|
@@ -55,18 +58,18 @@ if (parsed['--number']) {
|
|
|
55
58
|
}
|
|
56
59
|
if (!has(supportedNumberingStyles, numberStyle)) {
|
|
57
60
|
process.stderr.write([
|
|
58
|
-
|
|
61
|
+
`"${numberStyle}" is not a valid numbering style.`,
|
|
59
62
|
'Valid styles are ' +
|
|
60
|
-
Object.keys(supportedNumberingStyles)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
Object.keys(supportedNumberingStyles)
|
|
64
|
+
.map(s => `"${s}"`)
|
|
65
|
+
.join(', ') + '.'
|
|
63
66
|
].join('\n') + '\n')
|
|
64
67
|
process.exit(1)
|
|
65
68
|
} else {
|
|
66
|
-
options.
|
|
69
|
+
options.numberStyle = require(supportedNumberingStyles[numberStyle])
|
|
67
70
|
}
|
|
68
71
|
} else {
|
|
69
|
-
options.
|
|
72
|
+
options.numberStyle = require('decimal-numbering')
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
if (parsed['--signatures']) {
|
|
@@ -81,7 +84,7 @@ if (parsed['--styles']) {
|
|
|
81
84
|
|
|
82
85
|
if (parsed['--title']) options.title = parsed['--title']
|
|
83
86
|
|
|
84
|
-
if (parsed['--
|
|
87
|
+
if (parsed['--form-version']) options.version = parsed['--form-version']
|
|
85
88
|
|
|
86
89
|
if (parsed['--hash']) options.hash = true
|
|
87
90
|
|
|
@@ -89,21 +92,25 @@ if (parsed['--a4-paper']) options.a4 = true
|
|
|
89
92
|
|
|
90
93
|
options.indentMargins = parsed['--indent-margins']
|
|
91
94
|
|
|
92
|
-
options.
|
|
95
|
+
options.leftAlignTitle = parsed['--left-align-title']
|
|
93
96
|
|
|
94
97
|
options.leftAlignBody = parsed['--left-align-body']
|
|
95
98
|
|
|
96
|
-
options.
|
|
99
|
+
options.smart = !!parsed['--smart']
|
|
97
100
|
|
|
98
101
|
if (parsed['--blank-text']) options.blanks = parsed['--blank-text']
|
|
99
102
|
|
|
100
103
|
if (parsed['--mark-filled']) options.markFilled = true
|
|
101
104
|
|
|
105
|
+
options.loadedComponentStyle = parsed['--component-style']
|
|
106
|
+
if (parsed['--quote-component-text']) options.quoteComponentText = parsed['--quote-component-text']
|
|
107
|
+
options.incorporateComponentText = parsed['--incorporate-component-text']
|
|
108
|
+
|
|
102
109
|
function readJSON (file) {
|
|
103
110
|
return JSON.parse(fs.readFileSync(path.resolve(file)))
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
// Render and print.
|
|
107
|
-
|
|
114
|
+
const rendered = require('./')(form, blanks, options)
|
|
108
115
|
|
|
109
116
|
rendered.generateNodeStream().pipe(process.stdout)
|
package/escape.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
module.exports =
|
|
2
|
-
|
|
1
|
+
module.exports = string => {
|
|
2
|
+
const special = {
|
|
3
3
|
'&': /&/g,
|
|
4
4
|
''': /'/g,
|
|
5
5
|
'>': />/g,
|
|
6
6
|
'<': /</g,
|
|
7
7
|
'"': /"/g
|
|
8
8
|
}
|
|
9
|
-
return Object.keys(special).reduce(
|
|
10
|
-
return string.replace(special[escaped], escaped)
|
|
11
|
-
}, string)
|
|
9
|
+
return Object.keys(special).reduce((string, escaped) => string.replace(special[escaped], escaped), string)
|
|
12
10
|
}
|
package/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const JSZip = require('jszip')
|
|
2
|
+
const commonformHash = require('commonform-hash')
|
|
3
|
+
const decimalNumbering = require('decimal-numbering')
|
|
4
|
+
const smartify = require('commonform-smartify')
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const doc = require('./templates/document')
|
|
7
|
+
const docRels = require('./templates/document-relationships')
|
|
7
8
|
|
|
8
9
|
function defaultStyles (smart) {
|
|
9
10
|
return {
|
|
@@ -24,41 +25,67 @@ function defaultStyles (smart) {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
module.exports =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
module.exports = (
|
|
29
|
+
form,
|
|
30
|
+
values = [],
|
|
31
|
+
{
|
|
32
|
+
a4 = false,
|
|
33
|
+
after = '',
|
|
34
|
+
blanks = { text: '[•]', highlight: 'yellow' },
|
|
35
|
+
hash = false,
|
|
36
|
+
incorporateComponentText = 'Incorporate',
|
|
37
|
+
indentMargins = false,
|
|
38
|
+
leftAlignBody = false,
|
|
39
|
+
leftAlignTitle = false,
|
|
40
|
+
loadedComponentStyle = 'inline',
|
|
41
|
+
markFilled = false,
|
|
42
|
+
numberStyle = decimalNumbering,
|
|
43
|
+
quoteComponentText = 'Quoting for convenience, with any conflicts resolved in favor of the standard:',
|
|
44
|
+
smart,
|
|
45
|
+
styles,
|
|
46
|
+
title,
|
|
47
|
+
version
|
|
48
|
+
}
|
|
49
|
+
) => {
|
|
50
|
+
styles = styles
|
|
51
|
+
? Object.assign({}, defaultStyles(smart), styles)
|
|
40
52
|
: defaultStyles(smart)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
? { text: options.blanks }
|
|
45
|
-
: options.blanks
|
|
46
|
-
var markFilled = !!options.markFilled
|
|
47
|
-
var scaffold = require('./data/scaffold.json')
|
|
48
|
-
scaffold.word['document.xml'] = doc(
|
|
53
|
+
if (typeof blanks === 'string') blanks = { text: blanks }
|
|
54
|
+
hash = hash ? commonformHash(form) : undefined
|
|
55
|
+
const result = doc(
|
|
49
56
|
smart ? smartify(form) : form,
|
|
50
|
-
values,
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
values,
|
|
58
|
+
{
|
|
59
|
+
a4,
|
|
60
|
+
after,
|
|
61
|
+
blanks,
|
|
62
|
+
hash,
|
|
63
|
+
incorporateComponentText,
|
|
64
|
+
indentMargins,
|
|
65
|
+
leftAlignBody,
|
|
66
|
+
leftAlignTitle,
|
|
67
|
+
loadedComponentStyle,
|
|
68
|
+
markFilled,
|
|
69
|
+
numberStyle,
|
|
70
|
+
quoteComponentText,
|
|
71
|
+
smart,
|
|
72
|
+
styles,
|
|
73
|
+
title,
|
|
74
|
+
version
|
|
75
|
+
}
|
|
53
76
|
)
|
|
54
|
-
|
|
55
|
-
|
|
77
|
+
const scaffold = require('./data/scaffold.json')
|
|
78
|
+
const clone = Object.assign({}, scaffold)
|
|
79
|
+
clone.word['document.xml'] = result.xml
|
|
80
|
+
clone.word._rels['document.xml.rels'] = docRels(result.hrefs)
|
|
81
|
+
const zip = new JSZip()
|
|
82
|
+
zipObject(zip, clone)
|
|
56
83
|
return zip
|
|
57
84
|
}
|
|
58
85
|
|
|
59
86
|
function zipObject (zip, object) {
|
|
60
|
-
Object.keys(object).forEach(
|
|
61
|
-
|
|
87
|
+
Object.keys(object).forEach(path => {
|
|
88
|
+
const content = object[path]
|
|
62
89
|
// File
|
|
63
90
|
if (typeof content === 'string') {
|
|
64
91
|
zip.file(path, content.trim())
|
package/lint.log
ADDED
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commonform-docx",
|
|
3
3
|
"description": "render Common Forms in Office Open XML (Microsoft Word .docx format)",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "7.0.0",
|
|
5
5
|
"author": "Kyle E. Mitchell <kyle@kemitchell.com> (http://kemitchell.com)",
|
|
6
6
|
"bin": "bin.js",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"@kemitchell/docopt": "^1.0.0",
|
|
9
9
|
"agreement-schedules-exhibits-numbering": "^2.0.0",
|
|
10
|
-
"commonform-flatten": "^1.0
|
|
10
|
+
"commonform-flatten": "^2.1.0",
|
|
11
11
|
"commonform-hash": "^1.0.0",
|
|
12
12
|
"commonform-prepare-blanks": "^1.0.1",
|
|
13
13
|
"commonform-smartify": "^1.0.1",
|
|
14
14
|
"decimal-numbering": "^3.0.2",
|
|
15
15
|
"has": "^1.0.3",
|
|
16
16
|
"jszip": "^3.2.1",
|
|
17
|
-
"
|
|
17
|
+
"number-to-words-en": "^1.2.5",
|
|
18
18
|
"ooxml-signature-pages": "^3.0.2",
|
|
19
19
|
"outline-numbering": "^2.0.0",
|
|
20
20
|
"plan-addenda-exhibits-numbering": "^3.0.0",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"istanbul": "^0.4.0",
|
|
25
25
|
"run-series": "^1.1.8",
|
|
26
|
-
"standard": "^
|
|
26
|
+
"standard": "^17.0.0",
|
|
27
27
|
"tape": "^5.0.1",
|
|
28
|
-
"textract": "^2.
|
|
28
|
+
"textract": "^2.5.0"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
31
|
"Microsoft Office",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const escape = require('../escape')
|
|
2
|
+
|
|
3
|
+
module.exports = array => {
|
|
4
|
+
const scaffolded = require('../data/scaffold.json').word._rels['document.xml.rels']
|
|
5
|
+
if (array.length === 0) return scaffolded
|
|
6
|
+
const appended = []
|
|
7
|
+
for (let index = 0; index < array.length; index++) {
|
|
8
|
+
const assignment = array[index]
|
|
9
|
+
appended.push(`<Relationship Id="${assignment.rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${escape(assignment.url)}" TargetMode="External"/>`)
|
|
10
|
+
}
|
|
11
|
+
return scaffolded.replace(
|
|
12
|
+
'</Relationships>',
|
|
13
|
+
`${appended.join('')}</Relationships>`
|
|
14
|
+
)
|
|
15
|
+
}
|
package/templates/document.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const flatten = require('commonform-flatten')
|
|
2
|
+
const hashRun = require('./hash')
|
|
3
|
+
const titleRun = require('./title')
|
|
4
|
+
const paragraph = require('./paragraph')
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const DOCUMENT_XMLNS = (
|
|
7
7
|
/* jscs:disable maximumLineLength */
|
|
8
8
|
/* jshint ignore: start */
|
|
9
9
|
'xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" ' +
|
|
@@ -27,7 +27,7 @@ var DOCUMENT_XMLNS = (
|
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
function section (a4) {
|
|
30
|
-
|
|
30
|
+
let returned = '<w:sectPr>'
|
|
31
31
|
if (a4) {
|
|
32
32
|
returned += '<w:pgSz w:w="11907" w:h="16839"/>'
|
|
33
33
|
} else {
|
|
@@ -40,28 +40,35 @@ function section (a4) {
|
|
|
40
40
|
return returned
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
module.exports =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
module.exports = (form, values, options) => {
|
|
44
|
+
// Hyperlinks in documents must refer to Relationships by rId.
|
|
45
|
+
// Set up a running list of HREFs to turn into Relationships,
|
|
46
|
+
// then pass a helper function that assigns rIds to HREFs.
|
|
47
|
+
const hrefs = []
|
|
48
|
+
options.rIdForHREF = url => {
|
|
49
|
+
const rId = `rId${100 + (hrefs.length - 1)}`
|
|
50
|
+
hrefs.push({ rId, url })
|
|
51
|
+
return rId
|
|
52
|
+
}
|
|
53
|
+
const paragraphs = flatten(form, values)
|
|
54
|
+
.map(element => {
|
|
55
|
+
if (options.leftAlignBody) element.alignment = 'left'
|
|
56
|
+
return paragraph(element, options)
|
|
53
57
|
})
|
|
54
58
|
.join('')
|
|
55
|
-
|
|
59
|
+
const { title, version, hash, after, a4 } = options
|
|
60
|
+
const xml = (
|
|
61
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
56
62
|
'<w:document ' + DOCUMENT_XMLNS + '>' +
|
|
57
63
|
'<w:body>' +
|
|
58
|
-
(title ? titleRun(title,
|
|
59
|
-
(
|
|
60
|
-
(hash ? hashRun(hash,
|
|
64
|
+
(title ? titleRun(title, options) : '') +
|
|
65
|
+
(version ? titleRun(version, options) : '') +
|
|
66
|
+
(hash ? hashRun(hash, options) : '') +
|
|
61
67
|
paragraphs +
|
|
62
68
|
after +
|
|
63
|
-
section(
|
|
69
|
+
section(a4) +
|
|
64
70
|
'</w:body>' +
|
|
65
71
|
'</w:document>'
|
|
66
72
|
)
|
|
73
|
+
return { xml, hrefs }
|
|
67
74
|
}
|
package/templates/hash.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
const paragraph = require('./paragraph')
|
|
2
2
|
|
|
3
|
-
module.exports =
|
|
3
|
+
module.exports = (string, options) => {
|
|
4
4
|
return paragraph({
|
|
5
|
-
alignment: center ? 'center' : 'left',
|
|
6
|
-
depth: 1,
|
|
7
5
|
title: true,
|
|
6
|
+
alignment: options.leftAlignTitle ? 'left' : 'center',
|
|
8
7
|
content: [{ monospaced: string }]
|
|
9
|
-
},
|
|
8
|
+
}, options)
|
|
10
9
|
}
|
package/templates/paragraph.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const escape = require('../escape')
|
|
2
|
+
const has = require('has')
|
|
3
|
+
const numberToWords = require('number-to-words-en')
|
|
4
|
+
const run = require('./run')
|
|
5
|
+
const tag = require('./tag')
|
|
4
6
|
|
|
5
7
|
// Half an inch in twentieths of a point
|
|
6
|
-
|
|
8
|
+
const HALF_INCH = 720
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
const alignments = {
|
|
9
11
|
left: 'start',
|
|
10
12
|
right: 'end',
|
|
11
13
|
center: 'center',
|
|
@@ -13,113 +15,169 @@ var alignments = {
|
|
|
13
15
|
distribute: 'distribute'
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
function properties (o, number, indentMargins) {
|
|
19
|
+
if (o.title) {
|
|
20
|
+
// Titles don't have margins or indentation.
|
|
21
|
+
return tag('w:pPr', `<w:jc w:val="${alignments[o.alignment]}" />`)
|
|
22
|
+
}
|
|
23
|
+
const depth = ('heading' in o || 'numbering' in o)
|
|
19
24
|
? o.depth
|
|
20
25
|
: o.depth + 1
|
|
21
|
-
|
|
26
|
+
// CAVEAT: The order of properties is important.
|
|
22
27
|
return tag('w:pPr',
|
|
23
28
|
'<w:ind' +
|
|
24
29
|
(
|
|
25
30
|
indentMargins
|
|
26
31
|
? (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
number.length === 0
|
|
33
|
+
? (
|
|
34
|
+
' w:left="' + ((depth - 2) * HALF_INCH) + '"'
|
|
35
|
+
)
|
|
36
|
+
: (
|
|
37
|
+
' w:left="' + ((depth - 1) * HALF_INCH) + '"' +
|
|
38
|
+
' w:firstLine="-' + HALF_INCH + '"'
|
|
39
|
+
)
|
|
40
|
+
)
|
|
34
41
|
: (' w:firstLine="' + ((depth - 1) * HALF_INCH) + '"')
|
|
35
42
|
) + ' />' +
|
|
36
|
-
'<w:jc w:val="' + alignments[alignment] + '" />'
|
|
43
|
+
'<w:jc w:val="' + alignments[o.alignment] + '" />'
|
|
37
44
|
)
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
const TAB = '<w:r><w:tab/></w:r>'
|
|
41
48
|
|
|
42
|
-
module.exports =
|
|
43
|
-
element, numberStyle, indentMargins, blanks, markFilled, styles
|
|
44
|
-
) {
|
|
49
|
+
module.exports = (element, options) => {
|
|
45
50
|
if (!has(element, 'alignment')) {
|
|
46
|
-
element.alignment = styles.alignment || 'justify'
|
|
51
|
+
element.alignment = options.styles.alignment || 'justify'
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
? numberStyle(element.numbering, true)
|
|
53
|
+
const number = has(element, 'numbering')
|
|
54
|
+
? options.numberStyle(element.numbering, true)
|
|
50
55
|
: ''
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
const conspicuous = has(element, 'conspicuous')
|
|
57
|
+
const hasComponent = has(element, 'component')
|
|
58
|
+
const hasContent = has(element, 'content')
|
|
59
|
+
let returned = '<w:p>'
|
|
60
|
+
returned += properties(element, number, options.indentMargins)
|
|
61
|
+
returned += number ? makeRun(number, false) + TAB : ''
|
|
62
|
+
if (has(element, 'heading')) {
|
|
63
|
+
returned += makeRun({ caption: element.heading }, conspicuous)
|
|
64
|
+
if (!/\.$/.test(element.heading)) {
|
|
65
|
+
returned += makeRun('.', false)
|
|
66
|
+
}
|
|
67
|
+
if ((hasComponent && !hasContent) || element.content.length === 0) {
|
|
68
|
+
// pass
|
|
69
|
+
} else {
|
|
70
|
+
returned += makeRun(' ', false)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (hasComponent) {
|
|
74
|
+
if (hasContent) {
|
|
75
|
+
const style = options.loadedComponentStyle
|
|
76
|
+
if (style === 'both') {
|
|
77
|
+
returned += componentReference(element.reference, element.component)
|
|
78
|
+
returned += makeRun(' ' + options.quoteComponentText)
|
|
79
|
+
returned += '</w:p><w:p>'
|
|
80
|
+
returned += properties(
|
|
81
|
+
Object.assign({}, element, { depth: element.depth + 1 }),
|
|
82
|
+
number,
|
|
83
|
+
options.indentMargins
|
|
64
84
|
)
|
|
65
|
-
:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
returned += childContent({ content: element.content })
|
|
86
|
+
} else if (style === 'copy') {
|
|
87
|
+
returned += childContent({ content: element.content })
|
|
88
|
+
} else if (style === 'reference') {
|
|
89
|
+
returned += componentReference(element.reference, element.component)
|
|
90
|
+
} else {
|
|
91
|
+
throw new Error('Uknown Loaded Component Style: ' + style)
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
returned += componentReference(element)
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
returned += childContent(element)
|
|
98
|
+
}
|
|
99
|
+
returned += '</w:p>'
|
|
100
|
+
return returned
|
|
101
|
+
|
|
102
|
+
function childContent (element) {
|
|
103
|
+
const conspicuous = has(element, 'conspicuous')
|
|
104
|
+
return element.content
|
|
105
|
+
.map(element => makeRun(element, conspicuous, options))
|
|
106
|
+
.join('')
|
|
107
|
+
}
|
|
108
|
+
|
|
81
109
|
function makeRun (element, conspicuous) {
|
|
82
|
-
return run(element,
|
|
110
|
+
return run(element, conspicuous, options)
|
|
83
111
|
}
|
|
84
|
-
}
|
|
85
112
|
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
var firstString = url
|
|
100
|
-
if (hasSubstitutions) {
|
|
101
|
-
if (component.upgrade) firstString += ' with updates and corrections, replacing '
|
|
102
|
-
else firstString += ' replacing '
|
|
103
|
-
returned.push(firstString)
|
|
104
|
-
var substitutionContent = []
|
|
105
|
-
Object.keys(substitutions.terms).forEach(function (from) {
|
|
106
|
-
var to = substitutions.terms[from]
|
|
107
|
-
substitutionContent.push(from + ' with ', { use: to })
|
|
108
|
-
})
|
|
109
|
-
Object.keys(substitutions.headings).forEach(function (from) {
|
|
110
|
-
var to = substitutions.headings[from]
|
|
111
|
-
substitutionContent.push(from + ' with ', { referene: to })
|
|
112
|
-
})
|
|
113
|
-
var length = substitutionContent.length
|
|
114
|
-
for (var index = 2; index < length - 1; index += 2) {
|
|
115
|
-
substitutionContent[index] = ', ' + substitutionContent[index]
|
|
113
|
+
function componentReference (component, meta) {
|
|
114
|
+
const href = component.component + '/' + component.version
|
|
115
|
+
const returned = [makeRun(options.incorporateComponentText + ' ')]
|
|
116
|
+
const rId = options.rIdForHREF(href)
|
|
117
|
+
const link = '<w:hyperlink r:id="' + rId + '" w:history="1"><w:r><w:rPr><w:color w:val="0000EE"/><w:u w:val="single"/></w:rPr><w:t>' + escape(href) + '</w:t></w:r></w:hyperlink>'
|
|
118
|
+
if (meta) {
|
|
119
|
+
returned.push(
|
|
120
|
+
makeRun(meta.publisher + ' ' + meta.name + ' Version ' + meta.version + ' ('),
|
|
121
|
+
link,
|
|
122
|
+
makeRun(')')
|
|
123
|
+
)
|
|
124
|
+
} else {
|
|
125
|
+
returned.push(link)
|
|
116
126
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
127
|
+
const substitutions = component.substitutions
|
|
128
|
+
const hasSubstitutions = (
|
|
129
|
+
Object.keys(substitutions.terms).length > 0 ||
|
|
130
|
+
Object.keys(substitutions.headings).length > 0
|
|
131
|
+
)
|
|
132
|
+
if (hasSubstitutions) {
|
|
133
|
+
returned.push(makeRun(' substituting '))
|
|
134
|
+
const phrases = []
|
|
135
|
+
Object.keys(substitutions.terms).forEach(from => {
|
|
136
|
+
const to = substitutions.terms[from]
|
|
137
|
+
phrases.push(
|
|
138
|
+
makeRun('the term ') +
|
|
139
|
+
makeRun({ use: to }) +
|
|
140
|
+
makeRun(' for the term ') +
|
|
141
|
+
makeRun({ use: from })
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
Object.keys(substitutions.headings).forEach(from => {
|
|
145
|
+
const to = substitutions.headings[from]
|
|
146
|
+
phrases.push(
|
|
147
|
+
makeRun('references to ') +
|
|
148
|
+
makeRun({ use: to }) +
|
|
149
|
+
makeRun(' for references to ') +
|
|
150
|
+
makeRun({ use: from })
|
|
151
|
+
)
|
|
152
|
+
})
|
|
153
|
+
Object.keys(substitutions.blanks).forEach(number => {
|
|
154
|
+
const value = substitutions.blanks[number]
|
|
155
|
+
phrases.push(
|
|
156
|
+
makeRun(quote(value)) +
|
|
157
|
+
makeRun(' for the ' + numberToWords.toWordsOrdinal(parseInt(number)) + ' blank')
|
|
158
|
+
)
|
|
159
|
+
})
|
|
160
|
+
const length = phrases.length
|
|
161
|
+
if (length === 1) {
|
|
162
|
+
returned.push(phrases[0])
|
|
163
|
+
} else if (length === 2) {
|
|
164
|
+
returned.push(phrases[0])
|
|
165
|
+
returned.push(makeRun(' and '))
|
|
166
|
+
returned.push(phrases[1])
|
|
167
|
+
} else {
|
|
168
|
+
returned.push(
|
|
169
|
+
phrases.slice(0, -1).join(makeRun(', ')) +
|
|
170
|
+
makeRun(', and ') +
|
|
171
|
+
phrases[phrases.length - 1]
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
returned.push(makeRun('.'))
|
|
176
|
+
return returned.join('')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function quote (string) {
|
|
180
|
+
if (options.smart) return '“' + string + '”'
|
|
181
|
+
else return '"' + string + '"'
|
|
123
182
|
}
|
|
124
|
-
return returned
|
|
125
183
|
}
|
package/templates/run.js
CHANGED
|
@@ -1,91 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var tag = require('./tag')
|
|
1
|
+
const escape = require('../escape')
|
|
2
|
+
const has = require('has')
|
|
3
|
+
const tag = require('./tag')
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const defaults = {
|
|
7
6
|
highlight: false,
|
|
8
7
|
bold: false,
|
|
9
8
|
italic: false,
|
|
10
9
|
underline: false
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
module.exports = function run (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var properties = assign({}, defaults)
|
|
12
|
+
module.exports = function run (element, conspicuous, options) {
|
|
13
|
+
const { styles } = options
|
|
14
|
+
const properties = Object.assign({}, defaults)
|
|
17
15
|
if (conspicuous === true) {
|
|
18
|
-
assign(properties, styles.conspicuous)
|
|
16
|
+
Object.assign(properties, styles.conspicuous)
|
|
19
17
|
}
|
|
20
|
-
|
|
18
|
+
let text = ''
|
|
21
19
|
/* istanbul ignore else */
|
|
22
20
|
if (typeof element === 'string') {
|
|
23
|
-
assign(properties, styles.text)
|
|
21
|
+
Object.assign(properties, styles.text)
|
|
24
22
|
text = element
|
|
25
23
|
} else if (has(element, 'caption')) {
|
|
26
|
-
assign(properties, styles.heading)
|
|
24
|
+
Object.assign(properties, styles.heading)
|
|
27
25
|
text = element.caption
|
|
28
26
|
} else if (has(element, 'title')) {
|
|
29
|
-
assign(properties, styles.title)
|
|
27
|
+
Object.assign(properties, styles.title)
|
|
30
28
|
text = element.title
|
|
31
29
|
} else if (has(element, 'monospaced')) {
|
|
32
|
-
assign(properties, styles.monospaced)
|
|
30
|
+
Object.assign(properties, styles.monospaced)
|
|
33
31
|
text = element.monospaced
|
|
34
32
|
} else if (has(element, 'definition')) {
|
|
35
|
-
|
|
33
|
+
const term = element.definition
|
|
36
34
|
return (
|
|
37
35
|
(
|
|
38
36
|
styles.beforeDefinition
|
|
39
|
-
? run(
|
|
40
|
-
styles.beforeDefinition, numberStyle, conspicuous, blanks,
|
|
41
|
-
markFilled, styles
|
|
42
|
-
)
|
|
37
|
+
? run(styles.beforeDefinition, conspicuous, options)
|
|
43
38
|
: ''
|
|
44
39
|
) +
|
|
45
40
|
tag('w:r', runProperties(styles.definition) + runText(term)) +
|
|
46
41
|
(
|
|
47
42
|
styles.afterDefinition
|
|
48
|
-
? run(
|
|
49
|
-
styles.afterDefinition, numberStyle, conspicuous, blanks,
|
|
50
|
-
markFilled, styles
|
|
51
|
-
)
|
|
43
|
+
? run(styles.afterDefinition, conspicuous, options)
|
|
52
44
|
: ''
|
|
53
45
|
)
|
|
54
46
|
)
|
|
55
47
|
} else if (has(element, 'blank')) {
|
|
56
|
-
assign(properties, styles.text)
|
|
48
|
+
Object.assign(properties, styles.text)
|
|
57
49
|
if (element.blank !== undefined) {
|
|
58
50
|
text = element.blank
|
|
59
|
-
if (markFilled) assign(properties, styles.filled)
|
|
51
|
+
if (options.markFilled) Object.assign(properties, styles.filled)
|
|
60
52
|
} else {
|
|
61
|
-
text = blanks.text
|
|
62
|
-
if (blanks.highlight) assign(properties, styles.highlighted)
|
|
53
|
+
text = options.blanks.text
|
|
54
|
+
if (options.blanks.highlight) Object.assign(properties, styles.highlighted)
|
|
63
55
|
}
|
|
64
56
|
} else if (has(element, 'use')) {
|
|
65
|
-
assign(properties, styles.use)
|
|
57
|
+
Object.assign(properties, styles.use)
|
|
66
58
|
text = element.use
|
|
67
59
|
} else if (has(element, 'heading')) {
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
const numbering = element.numbering
|
|
61
|
+
const heading = element.heading
|
|
70
62
|
if (
|
|
71
63
|
has(element, 'broken') ||
|
|
72
64
|
has(element, 'ambiguous')
|
|
73
65
|
) {
|
|
74
|
-
assign(properties, styles.broken)
|
|
66
|
+
Object.assign(properties, styles.broken)
|
|
75
67
|
text = '[Broken Cross-Reference to "' + heading + '"]'
|
|
76
68
|
} else {
|
|
77
|
-
text = numberStyle(numbering)
|
|
69
|
+
text = options.numberStyle(numbering)
|
|
78
70
|
return (
|
|
79
71
|
// Underlined reference.
|
|
80
72
|
tag(
|
|
81
73
|
'w:r',
|
|
82
|
-
runProperties(assign({}, properties, styles.reference)) +
|
|
74
|
+
runProperties(Object.assign({}, properties, styles.reference)) +
|
|
83
75
|
runText(text)
|
|
84
76
|
) +
|
|
85
77
|
// Name of referenced section in parentheses.
|
|
86
78
|
tag(
|
|
87
79
|
'w:r',
|
|
88
|
-
runProperties(assign({}, properties, styles.referenceHeading)) +
|
|
80
|
+
runProperties(Object.assign({}, properties, styles.referenceHeading)) +
|
|
89
81
|
runText(' (' + heading + ')')
|
|
90
82
|
)
|
|
91
83
|
)
|
|
@@ -98,36 +90,36 @@ module.exports = function run (
|
|
|
98
90
|
|
|
99
91
|
function underlineFlag (underline) {
|
|
100
92
|
if (typeof underline === 'string') {
|
|
101
|
-
return
|
|
93
|
+
return `<w:u w:val="${underline}"/>`
|
|
102
94
|
} else {
|
|
103
95
|
return '<w:u w:val="none"/>'
|
|
104
96
|
}
|
|
105
97
|
}
|
|
106
98
|
|
|
107
99
|
function highlightFlag (highlight) {
|
|
108
|
-
return
|
|
100
|
+
return `<w:highlight w:val="${highlight}"/>`
|
|
109
101
|
}
|
|
110
102
|
|
|
111
103
|
function flag (name, value) {
|
|
112
|
-
return value ?
|
|
104
|
+
return value ? `<w:${name}/>` : ''
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
function runProperties (
|
|
107
|
+
function runProperties (style) {
|
|
116
108
|
return tag('w:rPr',
|
|
117
109
|
(
|
|
118
|
-
flag('b',
|
|
119
|
-
flag('i',
|
|
120
|
-
(
|
|
121
|
-
underlineFlag(
|
|
110
|
+
flag('b', style.bold || false) +
|
|
111
|
+
flag('i', style.italic || false) +
|
|
112
|
+
(style.highlight ? highlightFlag(style.highlight) : '') +
|
|
113
|
+
underlineFlag(style.underline || false) +
|
|
122
114
|
(
|
|
123
|
-
|
|
115
|
+
style.monospaced
|
|
124
116
|
? (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
'<w:rFonts ' +
|
|
118
|
+
'w:ascii="Courier New" ' +
|
|
119
|
+
'w:hAnsi="Courier New" ' +
|
|
120
|
+
'w:cs="Courier New"/>' +
|
|
121
|
+
'<w:sz w:val="20"/>'
|
|
122
|
+
)
|
|
131
123
|
: ''
|
|
132
124
|
)
|
|
133
125
|
)
|
|
@@ -135,5 +127,5 @@ function runProperties (options) {
|
|
|
135
127
|
}
|
|
136
128
|
|
|
137
129
|
function runText (text) {
|
|
138
|
-
return
|
|
130
|
+
return `<w:t xml:space="preserve">${escape(text)}</w:t>`
|
|
139
131
|
}
|
package/templates/tag.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports =
|
|
2
|
-
return
|
|
1
|
+
module.exports = (name, content) => {
|
|
2
|
+
return `<${name}>${content}</${name}>`
|
|
3
3
|
}
|
package/templates/title.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
const paragraph = require('./paragraph')
|
|
2
2
|
|
|
3
|
-
module.exports =
|
|
4
|
-
|
|
5
|
-
alignment: 'left',
|
|
6
|
-
depth: 1,
|
|
3
|
+
module.exports = (string, options) => {
|
|
4
|
+
const argument = {
|
|
7
5
|
title: true,
|
|
6
|
+
alignment: 'center',
|
|
8
7
|
content: [{ title: string }]
|
|
9
8
|
}
|
|
10
|
-
if (
|
|
11
|
-
return paragraph(argument,
|
|
9
|
+
if (options.leftAlignTitle) argument.alignment = 'left'
|
|
10
|
+
return paragraph(argument, options)
|
|
12
11
|
}
|
package/test.log
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
|
|
2
|
-
> commonform-docx@
|
|
2
|
+
> commonform-docx@6.0.0 pretest
|
|
3
3
|
> npm run prepublish
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> commonform-docx@
|
|
6
|
+
> commonform-docx@6.0.0 prepublish
|
|
7
7
|
> node build/scaffold.js data/scaffold.docx > data/scaffold.json
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
> commonform-docx@
|
|
10
|
+
> commonform-docx@6.0.0 test
|
|
11
11
|
> tape test.js
|
|
12
12
|
|
|
13
13
|
TAP version 13
|
|
@@ -27,69 +27,72 @@ ok 9 term appears in output
|
|
|
27
27
|
ok 10 no render error
|
|
28
28
|
ok 11 no textract error
|
|
29
29
|
ok 12 reference appears in output
|
|
30
|
-
#
|
|
30
|
+
# handles components without headings
|
|
31
31
|
ok 13 no render error
|
|
32
|
-
|
|
33
|
-
ok
|
|
32
|
+
# handles components with substitutions
|
|
33
|
+
ok 14 no render error
|
|
34
|
+
# omits period after heading ending w/ period
|
|
35
|
+
ok 15 no render error
|
|
36
|
+
ok 16 no textract error
|
|
37
|
+
ok 17 double period does not appear in output
|
|
34
38
|
# adds space between heading and defined term
|
|
35
|
-
ok
|
|
36
|
-
|
|
37
|
-
ok
|
|
38
|
-
ok 18 space between heading and term
|
|
39
|
+
ok 18 no render error
|
|
40
|
+
ok 19 no textract error
|
|
41
|
+
ok 20 space between heading and term
|
|
39
42
|
# adds space between heading and text
|
|
40
|
-
ok
|
|
41
|
-
ok
|
|
42
|
-
ok
|
|
43
|
+
ok 21 no render error
|
|
44
|
+
ok 22 no textract error
|
|
45
|
+
ok 23 space between heading and text
|
|
43
46
|
# renders broken references
|
|
44
|
-
ok
|
|
45
|
-
ok
|
|
46
|
-
ok
|
|
47
|
+
ok 24 no render error
|
|
48
|
+
ok 25 no textract error
|
|
49
|
+
ok 26 reference appears in output
|
|
47
50
|
# fills blanks
|
|
48
|
-
ok
|
|
49
|
-
ok
|
|
50
|
-
ok
|
|
51
|
+
ok 27 no render error
|
|
52
|
+
ok 28 no textract error
|
|
53
|
+
ok 29 value appears in output
|
|
51
54
|
# custom blank text
|
|
52
|
-
ok
|
|
53
|
-
ok
|
|
54
|
-
ok
|
|
55
|
+
ok 30 no render error
|
|
56
|
+
ok 31 no textract error
|
|
57
|
+
ok 32 value appears in output
|
|
55
58
|
# renders empty blank placeholders
|
|
56
|
-
ok
|
|
57
|
-
ok
|
|
58
|
-
ok
|
|
59
|
+
ok 33 no render error
|
|
60
|
+
ok 34 no textract error
|
|
61
|
+
ok 35 placeholder appears in output
|
|
59
62
|
# renders custom empty blank placeholders
|
|
60
|
-
ok
|
|
61
|
-
ok
|
|
62
|
-
ok
|
|
63
|
+
ok 36 no render error
|
|
64
|
+
ok 37 no textract error
|
|
65
|
+
ok 38 placeholder appears in output
|
|
63
66
|
# renders custom empty blank placeholders
|
|
64
|
-
ok
|
|
65
|
-
ok
|
|
66
|
-
ok
|
|
67
|
+
ok 39 no render error
|
|
68
|
+
ok 40 no textract error
|
|
69
|
+
ok 41 placeholder appears in output
|
|
67
70
|
# renders conspicuous text
|
|
68
|
-
ok
|
|
69
|
-
ok
|
|
70
|
-
ok
|
|
71
|
+
ok 42 no render error
|
|
72
|
+
ok 43 no textract error
|
|
73
|
+
ok 44 conspicuous text appears in output
|
|
71
74
|
# renders titles
|
|
72
|
-
ok
|
|
73
|
-
ok
|
|
74
|
-
ok
|
|
75
|
+
ok 45 no render error
|
|
76
|
+
ok 46 no textract error
|
|
77
|
+
ok 47 title appears in output
|
|
75
78
|
# renders centered titles
|
|
76
|
-
ok
|
|
77
|
-
ok
|
|
78
|
-
ok
|
|
79
|
-
# renders
|
|
80
|
-
ok
|
|
81
|
-
ok
|
|
82
|
-
ok
|
|
79
|
+
ok 48 no render error
|
|
80
|
+
ok 49 no textract error
|
|
81
|
+
ok 50 title appears in output
|
|
82
|
+
# renders versions
|
|
83
|
+
ok 51 no render error
|
|
84
|
+
ok 52 no textract error
|
|
85
|
+
ok 53 version appears in output
|
|
83
86
|
# renders hashes
|
|
84
|
-
ok
|
|
85
|
-
ok
|
|
86
|
-
ok
|
|
87
|
+
ok 54 no render error
|
|
88
|
+
ok 55 no textract error
|
|
89
|
+
ok 56 hash symbol appears in output
|
|
87
90
|
# throws for invalid content
|
|
88
|
-
ok
|
|
91
|
+
ok 57 throw an error
|
|
89
92
|
|
|
90
|
-
1..
|
|
91
|
-
# tests
|
|
92
|
-
# pass
|
|
93
|
+
1..57
|
|
94
|
+
# tests 57
|
|
95
|
+
# pass 57
|
|
93
96
|
|
|
94
97
|
# ok
|
|
95
98
|
|