Skip to content

Commit e25a944

Browse files
committed
feat(markdown-docx): add formula transformer - #397
Transformation logic(OOXML<->CiceroMark) Tests for formula Update tests(markdown-transform, markdown-cli) Signed-off-by: k-kumar-01 <[email protected]>
1 parent 58551c9 commit e25a944

File tree

9 files changed

+148
-49
lines changed

9 files changed

+148
-49
lines changed

packages/markdown-cli/test/data/acceptance/omitted-acceptance-of-delivery.xml

+15-15
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
<w:sz w:val="24"/>
8080

8181
</w:rPr>
82-
<w:t xml:space="preserve">"Party A"</w:t>
82+
<w:t xml:space="preserve">&quot;Party A&quot;</w:t>
8383
</w:r>
8484
</w:sdtContent>
8585
</w:sdt>
@@ -114,15 +114,15 @@
114114
<w:sz w:val="24"/>
115115

116116
</w:rPr>
117-
<w:t xml:space="preserve">"Party B"</w:t>
117+
<w:t xml:space="preserve">&quot;Party B&quot;</w:t>
118118
</w:r>
119119
</w:sdtContent>
120120
</w:sdt>
121121

122122
<w:r>
123123

124124

125-
<w:t xml:space="preserve">'s opinion, the </w:t>
125+
<w:t xml:space="preserve">&apos;s opinion, the </w:t>
126126

127127
</w:r>
128128
<w:sdt>
@@ -139,7 +139,7 @@
139139
<w:sz w:val="24"/>
140140

141141
</w:rPr>
142-
<w:t xml:space="preserve">"Widgets"</w:t>
142+
<w:t xml:space="preserve">&quot;Widgets&quot;</w:t>
143143
</w:r>
144144
</w:sdtContent>
145145
</w:sdt>
@@ -174,7 +174,7 @@
174174
<w:sz w:val="24"/>
175175

176176
</w:rPr>
177-
<w:t xml:space="preserve">"Party B"</w:t>
177+
<w:t xml:space="preserve">&quot;Party B&quot;</w:t>
178178
</w:r>
179179
</w:sdtContent>
180180
</w:sdt>
@@ -199,7 +199,7 @@
199199
<w:sz w:val="24"/>
200200

201201
</w:rPr>
202-
<w:t xml:space="preserve">"Party A"</w:t>
202+
<w:t xml:space="preserve">&quot;Party A&quot;</w:t>
203203
</w:r>
204204
</w:sdtContent>
205205
</w:sdt>
@@ -234,7 +234,7 @@
234234
<w:sz w:val="24"/>
235235

236236
</w:rPr>
237-
<w:t xml:space="preserve">"Widgets"</w:t>
237+
<w:t xml:space="preserve">&quot;Widgets&quot;</w:t>
238238
</w:r>
239239
</w:sdtContent>
240240
</w:sdt>
@@ -277,7 +277,7 @@
277277
<w:sz w:val="24"/>
278278

279279
</w:rPr>
280-
<w:t xml:space="preserve">"Party B"</w:t>
280+
<w:t xml:space="preserve">&quot;Party B&quot;</w:t>
281281
</w:r>
282282
</w:sdtContent>
283283
</w:sdt>
@@ -337,7 +337,7 @@
337337
<w:sz w:val="24"/>
338338

339339
</w:rPr>
340-
<w:t xml:space="preserve">"Widgets"</w:t>
340+
<w:t xml:space="preserve">&quot;Widgets&quot;</w:t>
341341
</w:r>
342342
</w:sdtContent>
343343
</w:sdt>
@@ -366,7 +366,7 @@
366366
<w:sz w:val="24"/>
367367

368368
</w:rPr>
369-
<w:t xml:space="preserve">"Party A"</w:t>
369+
<w:t xml:space="preserve">&quot;Party A&quot;</w:t>
370370
</w:r>
371371
</w:sdtContent>
372372
</w:sdt>
@@ -395,7 +395,7 @@
395395
<w:sz w:val="24"/>
396396

397397
</w:rPr>
398-
<w:t xml:space="preserve">"Widgets"</w:t>
398+
<w:t xml:space="preserve">&quot;Widgets&quot;</w:t>
399399
</w:r>
400400
</w:sdtContent>
401401
</w:sdt>
@@ -427,7 +427,7 @@
427427
<w:r>
428428

429429

430-
<w:t xml:space="preserve">The "Acceptance Criteria" are the specifications the </w:t>
430+
<w:t xml:space="preserve">The &quot;Acceptance Criteria&quot; are the specifications the </w:t>
431431

432432
</w:r>
433433
<w:sdt>
@@ -444,7 +444,7 @@
444444
<w:sz w:val="24"/>
445445

446446
</w:rPr>
447-
<w:t xml:space="preserve">"Widgets"</w:t>
447+
<w:t xml:space="preserve">&quot;Widgets&quot;</w:t>
448448
</w:r>
449449
</w:sdtContent>
450450
</w:sdt>
@@ -473,7 +473,7 @@
473473
<w:sz w:val="24"/>
474474

475475
</w:rPr>
476-
<w:t xml:space="preserve">"Party A"</w:t>
476+
<w:t xml:space="preserve">&quot;Party A&quot;</w:t>
477477
</w:r>
478478
</w:sdtContent>
479479
</w:sdt>
@@ -508,7 +508,7 @@
508508
<w:sz w:val="24"/>
509509

510510
</w:rPr>
511-
<w:t xml:space="preserve">"Attachment X"</w:t>
511+
<w:t xml:space="preserve">&quot;Attachment X&quot;</w:t>
512512
</w:r>
513513
</w:sdtContent>
514514
</w:sdt>

packages/markdown-docx/src/ToCiceroMarkVisitor.js

+27
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ class ToCiceroMarkVisitor {
6969
}
7070
}
7171

72+
/**
73+
* Gets the dependencies of a formula node
74+
*
75+
* @param {Array} variableProperties the variable elements
76+
* @returns {string} the name of the variable
77+
*/
78+
getDependencies(variableProperties) {
79+
for (const property of variableProperties) {
80+
if (property.name === 'w:tag') {
81+
return property.attributes['w:val'].split(SEPARATOR)[2];
82+
}
83+
}
84+
}
85+
7286
/**
7387
* Get the type of the element.
7488
*
@@ -194,6 +208,14 @@ class ToCiceroMarkVisitor {
194208
elementType: nodeInformation.elementType,
195209
name: nodeInformation.name,
196210
};
211+
} else if (nodeInformation.nodeType === TRANSFORMED_NODES.formula) {
212+
ciceroMarkNode = {
213+
$class: TRANSFORMED_NODES.formula,
214+
value: nodeInformation.value,
215+
name: nodeInformation.name,
216+
code: nodeInformation.code,
217+
dependencies: nodeInformation.dependencies.split(','),
218+
};
197219
} else if (nodeInformation.nodeType === TRANSFORMED_NODES.code) {
198220
ciceroMarkNode = {
199221
$class: TRANSFORMED_NODES.code,
@@ -563,6 +585,11 @@ class ToCiceroMarkVisitor {
563585
nodeInformation.name = this.getName(variableSubNodes.elements);
564586
nodeInformation.elementType = this.getElementType(variableSubNodes.elements);
565587
nodeInformation.nodeType = this.getNodeType(variableSubNodes.elements);
588+
if (nodeInformation.nodeType === TRANSFORMED_NODES.formula) {
589+
nodeInformation.code = nodeInformation.elementType;
590+
nodeInformation.dependencies = this.getDependencies(variableSubNodes.elements);
591+
delete nodeInformation.elementType;
592+
}
566593
}
567594
if (variableSubNodes.name === 'w:sdtContent') {
568595
if (nodeInformation.nodeType === TRANSFORMED_NODES.clause) {

packages/markdown-docx/src/ToOOXMLVisitor/helpers.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@
1515
'use strict';
1616

1717
/**
18-
* Replaces the angular brackets with the respective codes.
18+
* Escapes certain characters in OOXML with respective character encodings.
19+
* (https://stackoverflow.com/questions/1091945/what-characters-do-i-need-to-escape-in-xml-documents)
1920
*
2021
* @param {string} node String to be replaced
2122
* @returns {string} String with replaced angular brackets
2223
*/
2324
function sanitizeHtmlChars(node) {
24-
return node.replace(/>/g, '&gt;').replace(/</g, '&lt;');
25+
return node
26+
.replace(/&/, '&amp;')
27+
.replace(/>/g, '&gt;')
28+
.replace(/</g, '&lt;')
29+
.replace(/"/g, '&quot;')
30+
.replace(/'/, '&apos;');
2531
}
2632

2733
/**
@@ -63,7 +69,6 @@ function wrapAroundLockedContentControls(ooxml) {
6369
* @returns {string} OOXML wraped in docx headers
6470
*/
6571
function wrapAroundDefaultDocxTags(ooxml, relationships) {
66-
6772
const LINK_STYLE_SPEC = `
6873
<w:style w:type="character" w:styleId="Hyperlink">
6974
<w:name w:val="Hyperlink"/>

packages/markdown-docx/src/ToOOXMLVisitor/index.js

+32-1
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ const {
3535
VANISH_PROPERTY_RULE,
3636
CONDITIONAL_OR_OPTIONAL_FONT_FAMILY_RULE,
3737
CONDITIONAL_RULE,
38+
FORMULA_RULE,
3839
} = require('./rules');
39-
const { wrapAroundDefaultDocxTags, wrapAroundLockedContentControls } = require('./helpers');
40+
const { wrapAroundDefaultDocxTags, wrapAroundLockedContentControls, sanitizeHtmlChars } = require('./helpers');
4041
const { TRANSFORMED_NODES, RELATIONSHIP_OFFSET } = require('../constants');
4142

4243
/**
@@ -194,6 +195,36 @@ class ToOOXMLVisitor {
194195
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
195196
this.tags = [...this.tags, VARIABLE_RULE(title, tag, value, type)];
196197
}
198+
} else if (this.getClass(subNode) === TRANSFORMED_NODES.formula) {
199+
// Dependencies are added for the reason to extract them
200+
// when converting from ooxml -> ciceromark
201+
const tag = subNode.name;
202+
const type = sanitizeHtmlChars(subNode.code);
203+
this.createOrUpdateCounter(tag, type);
204+
const value = subNode.value;
205+
const dependencies = subNode.dependencies.join(',');
206+
const title = `${tag.toUpperCase()[0]}${tag.substring(1)}${this.counter[tag].count}`;
207+
inlineOOXML += FORMULA_RULE(
208+
title,
209+
tag,
210+
value,
211+
type,
212+
dependencies,
213+
parentProperties.traversingNodeHiddenInConditional
214+
);
215+
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {
216+
this.tags = [
217+
...this.tags,
218+
FORMULA_RULE(
219+
title,
220+
tag,
221+
value,
222+
type,
223+
dependencies,
224+
parentProperties.traversingNodeHiddenInConditional
225+
),
226+
];
227+
}
197228
} else if (this.getClass(subNode) === TRANSFORMED_NODES.softbreak) {
198229
inlineOOXML += SOFTBREAK_RULE();
199230
if (!(parent === TRANSFORMED_NODES.optional || parent === TRANSFORMED_NODES.conditional)) {

packages/markdown-docx/src/ToOOXMLVisitor/rules.js

+34
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,39 @@ const CONDITIONAL_RULE = (title, tag, value) => {
314314
</w:sdt>
315315
`;
316316
};
317+
/**
318+
* Inserts a formula.
319+
*
320+
* @param {string} title Title of the formula. Eg. receiver-1, shipper-1
321+
* @param {string} tag Name of the formula. Eg. receiver, shipper
322+
* @param {string} value Value of the formula
323+
* @param {string} type Type of the formula - Long, Double, etc.
324+
* @param {string} dependencies Dependencies of the formula
325+
* @param {boolean} vanish Should vanish property be present
326+
* @returns {string} OOXML string for the formula
327+
*/
328+
const FORMULA_RULE = (title, tag, value, type, dependencies, vanish = false) => {
329+
return `
330+
<w:sdt>
331+
<w:sdtPr>
332+
<w:rPr>
333+
<w:sz w:val="24"/>
334+
</w:rPr>
335+
<w:alias w:val="${titleGenerator(title, type)}"/>
336+
<w:tag w:val="${TRANSFORMED_NODES.formula}${SEPARATOR}${tag}${SEPARATOR}${dependencies}"/>
337+
</w:sdtPr>
338+
<w:sdtContent>
339+
<w:r>
340+
<w:rPr>
341+
<w:sz w:val="24"/>
342+
${vanish ? VANISH_PROPERTY_RULE() : ''}
343+
</w:rPr>
344+
<w:t xml:space="preserve">${sanitizeHtmlChars(value)}</w:t>
345+
</w:r>
346+
</w:sdtContent>
347+
</w:sdt>
348+
`;
349+
};
317350

318351
module.exports = {
319352
TEXT_RULE,
@@ -336,4 +369,5 @@ module.exports = {
336369
VANISH_PROPERTY_RULE,
337370
CONDITIONAL_OR_OPTIONAL_FONT_FAMILY_RULE,
338371
CONDITIONAL_RULE,
372+
FORMULA_RULE,
339373
};

packages/markdown-docx/src/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const TRANSFORMED_NODES = {
2525
optional: `${NS_PREFIX_CiceroMarkModel}Optional`,
2626
document: `${NS_PREFIX_CommonMarkModel}Document`,
2727
emphasize: `${NS_PREFIX_CommonMarkModel}Emph`,
28+
formula: `${NS_PREFIX_CiceroMarkModel}Formula`,
2829
heading: `${NS_PREFIX_CommonMarkModel}Heading`,
2930
item: `${NS_PREFIX_CommonMarkModel}Item`,
3031
link: `${NS_PREFIX_CommonMarkModel}Link`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"$class":"org.accordproject.commonmark.Document","xmlns":"http://commonmark.org/xml/1.0","nodes":[{"$class":"org.accordproject.commonmark.Heading","level":"2","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"Fixed rate loan"}]},{"$class":"org.accordproject.commonmark.Paragraph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"This is a "},{"$class":"org.accordproject.commonmark.Emph","nodes":[{"$class":"org.accordproject.commonmark.Text","text":"fixed interest"}]},{"$class":"org.accordproject.commonmark.Text","text":" loan to the amount of "},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"at the yearly interest rate of "},{"$class":"org.accordproject.ciceromark.Variable","value":"2.5","name":"rate","elementType":"Double"},{"$class":"org.accordproject.commonmark.Text","text":"%"},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"with a loan term of "},{"$class":"org.accordproject.ciceromark.Variable","value":"15","name":"loanDuration","elementType":"Integer"},{"$class":"org.accordproject.commonmark.Text","text":","},{"$class":"org.accordproject.commonmark.Softbreak"},{"$class":"org.accordproject.commonmark.Text","text":"and monthly payments of "},{"$class":"org.accordproject.ciceromark.Formula","value":"\"£667.00\"","dependencies":["loanAmount","rate","loanDuration"],"code":" monthlyPaymentFormula(loanAmount,rate,loanDuration) as \"K0,0.00\" ","name":"formula_d02c8642fa12d6ed08dea71f0af7a77b0c7893804d0b43b537eb18ea6f666463"}]}]}

0 commit comments

Comments
 (0)