Comparing version 2.0.0-beta.0 to 2.0.0-beta.1
110
CHANGELOG.md
### v2.0.x | ||
- Fix: avoid crashing when a static image was inserted in a loop in MS Word templates | ||
- Update totals in ODS and XSLX files | ||
- `formatC` supports many crypto currencies | ||
- Fix: Accepts comma in formatter parameters such as `{d.sentenceExist:ifEqual(1, 'Some sentence, some more sentences.')}` | ||
- Fix: nested array in XML (but not in JSON) was not printed correctly | ||
``` | ||
{d.countries[i].name} | ||
{d.movies[i].subObject.name} | ||
{d.movies[i+1].subObject.name} | ||
{d.countries[i+1].name} | ||
``` | ||
- Fix: avoid crashing when a sub-object is null or undefined in data | ||
- Fix: avoid crashing when the parent object of an array is null or undefined in data | ||
- Eslint code + add eslint tools | ||
- Fix: accepts dashes characters in JSON data. Before, Carbones crashes when using `{d.my-att-with-dash}` | ||
- Fix: avoid crashing when a XLSX template contains charts | ||
- Beta: supports dynamic charts rendering in XLSX if these conditions are met: | ||
- first, draw a chart in MS Excel and replace your data with Carbone markers | ||
- datas of the chart should be placed at the top-left corner of the spreadsheet | ||
- all numbers are formatted with formatN() formatter | ||
- Fix: accepts whitespace in array filters with simple quote and double quotes | ||
Example: `{d.cars[i, type='Tesla car'].name}` | ||
`{d.cars[i, type="Tesla car"].name}` | ||
- Accepts to iterate on attributes of objects as is if it was an array: | ||
```js | ||
{ | ||
myObject : { | ||
paul : '10', | ||
jack : '20', | ||
bob : '30' | ||
} | ||
} | ||
``` | ||
- 🚀 **Accepts dynamic variables in all formatters!** | ||
In the report: | ||
``` | ||
{d.myObject[i].att} {d.myObject[i].val} | ||
{d.myObject[i+1].att} {d.myObject[i+1].val} | ||
``` | ||
- use .att to print the attribue | ||
- use .val to print the value | ||
You can even access nested objects and nested arrays inside `val`: `{d.myObject[i].val.myArray[i].id}` | ||
- Accepts dynamic variables in all formatters! | ||
Carbone passes data to formatters if parameters start with a dot `.` and is not surrounded by quotes. Here is an example: | ||
**Examples:** | ||
*Data* | ||
@@ -85,4 +40,5 @@ ```js | ||
- New conditional formatters, and a new IF-block | ||
- `ifEQ (value)` : Matches values that are equal to a specified value | ||
- ⚡️ **New conditional formatters, and a new IF-block system to hide/show a part of the document** | ||
- `ifEQ (value)` : Matches values that are equal to a specified value, it replaces `ifEqual` | ||
- `ifNE (value)` : Matches all values that are not equal to a specified value | ||
@@ -93,5 +49,5 @@ - `ifGT (value)` : Matches values, string.length, array.length or object.length that are greater than a specified value | ||
- `ifLTE (value)` : Matches values, string.length, array.length or object.length that are less than or equal to a specified value | ||
- `ifIN (value)` : Matches any of the values specified in an array or string | ||
- `ifIN (value)` : Matches any of the values specified in an array or string, it replaces `ifContain` | ||
- `ifNIN (value)` : Matches none of the values specified in an array or string | ||
- `ifEM (value)` : Matches empty values, string, arrays or objects | ||
- `ifEM (value)` : Matches empty values, string, arrays or objects, it replaces `ifEmpty` | ||
- `ifNEM (value)` : Matches not empty values, string, arrays or objects | ||
@@ -105,4 +61,7 @@ - `and (value)` : AND operator between two consecutives conditional formatters | ||
**Examples:** | ||
No formatters can be chained after `hideBegin`, `hideEnd`, `showBegin`, `showEnd`. | ||
These new formatters replace the old ones `ifEqual`, `ifEmpty` and `ifContain`. We keep these old formatters | ||
for backwards compatibility. You should avoid using them in new templates. | ||
*Data* | ||
@@ -120,9 +79,54 @@ ```js | ||
print simple message according to the result of multiple conditions<br> | ||
print simple message according to the result of multiple conditions, combining with dynamic variables! 🤩<br> | ||
`{d.id:ifEQ(10):and(.qtyA):ifEQ(20):show('hey'):elseShow('hide')}` => hey | ||
hide or show a block of text in the document<br> | ||
hide or show a block of text in the document ⚡️<br> | ||
`{d.id:ifEQ(10):showBegin}` block of text `{d.id:showEnd}` => block of text<br> | ||
`{d.id:ifEQ(12):showBegin}` block of text `{d.id:showEnd}` => | ||
- ☀️ **Accepts to iterate on attributes of objects as is if it was an array** | ||
```js | ||
{ | ||
myObject : { | ||
paul : '10', | ||
jack : '20', | ||
bob : '30' | ||
} | ||
} | ||
``` | ||
In the report: | ||
``` | ||
{d.myObject[i].att} {d.myObject[i].val} | ||
{d.myObject[i+1].att} {d.myObject[i+1].val} | ||
``` | ||
- use .att to print the attribue | ||
- use .val to print the value | ||
You can even access nested objects and nested arrays inside `val`: `{d.myObject[i].val.myArray[i].id}` | ||
- Fix: avoid crashing when a static image was inserted in a loop in MS Word templates | ||
- Update totals in ODS and XSLX files | ||
- `formatC` supports many crypto currencies | ||
- Fix: Accepts comma in formatter parameters such as `{d.sentenceExist:ifEqual(1, 'Some sentence, some more sentences.')}` | ||
- Fix: nested array in XML (but not in JSON) was not printed correctly | ||
``` | ||
{d.countries[i].name} | ||
{d.movies[i].subObject.name} | ||
{d.movies[i+1].subObject.name} | ||
{d.countries[i+1].name} | ||
``` | ||
- Fix: avoid crashing when a sub-object is null or undefined in data | ||
- Fix: avoid crashing when the parent object of an array is null or undefined in data | ||
- Eslint code + add eslint tools | ||
- Fix: accepts dashes characters in JSON data. Before, Carbones crashes when using `{d.my-att-with-dash}` | ||
- Fix: avoid crashing when a XLSX template contains charts | ||
- Beta: supports dynamic charts rendering in XLSX if these conditions are met: | ||
- first, draw a chart in MS Excel and replace your data with Carbone markers | ||
- datas of the chart should be placed at the top-left corner of the spreadsheet | ||
- all numbers are formatted with formatN() formatter | ||
- Fix: accepts whitespace in array filters with simple quote and double quotes | ||
Example: `{d.cars[i, type='Tesla car'].name}` | ||
`{d.cars[i, type="Tesla car"].name}` | ||
- (Fix LibreOffice detection on Windows) | ||
@@ -129,0 +133,0 @@ |
@@ -538,2 +538,3 @@ var parser = require('./parser'); | ||
var _conditionalBlockDetected = []; | ||
var _emptyParts = []; | ||
for (var i = 0; i < _xmlParts.length; i++) { | ||
@@ -547,12 +548,23 @@ var _part = _xmlParts[i]; | ||
var _lastFormatter = _formatters[_nbFormatters - 1]; | ||
if (_lastFormatter.indexOf('hideBegin') !== -1) { | ||
if (_lastFormatter.indexOf('showBegin') !== -1 || _lastFormatter.indexOf('hideBegin') !== -1) { // TODO show hide, TODO return error if not last | ||
_conditionalBlockDetected.push(_part); | ||
} | ||
else if (_lastFormatter.indexOf('hideEnd') !== -1) { | ||
else if (_lastFormatter.indexOf('showEnd') !== -1 || _lastFormatter.indexOf('hideEnd') !== -1) { | ||
var _beginPart = _conditionalBlockDetected.pop(); | ||
var _newPos = parser.findSafeConditionalBlockPosition(xml, _beginPart.pos, _part.pos); | ||
_beginPart.pos = _newPos[0] - 0.1; | ||
_part.pos = _newPos[1] - 0.1; | ||
_part.pos = _newPos[1] - 0.2; | ||
_emptyParts.push({ | ||
obj : _beginPart.obj, | ||
formatters : [], | ||
pos : _beginPart.pos - 0.1, | ||
}); | ||
_emptyParts.push({ | ||
obj : _part.obj, | ||
formatters : [], | ||
pos : _part.pos + 0.1, | ||
}); | ||
} | ||
} | ||
descriptor[_objName].xmlParts = descriptor[_objName].xmlParts.concat(_emptyParts); | ||
} | ||
@@ -559,0 +571,0 @@ } |
@@ -578,2 +578,4 @@ var helper = require('./helper'); | ||
var _isCurrentPartValid = true; | ||
var _tagFoundType = 0; // 001 => opening tag / 010 => self-closing tag / 100 => closing tag | ||
var _validCandidates = []; | ||
@@ -595,3 +597,4 @@ // edge case: when there is not XML tag, return directly | ||
if (_tag[1] === '/') { | ||
_isCurrentPartValid = false; | ||
_tagFoundType |= 4; | ||
_isCurrentPartValid = (_tagFoundType === 4) ? true : false; | ||
var _beginPos = _openingTagPos.pop(); | ||
@@ -607,3 +610,3 @@ if (_beginPos !== undefined) { | ||
// when the string starts with many closing XML tag, "push" _firstValidTagPos | ||
else if (_minimalXmlDepth === Infinity) { | ||
else if (_tagFoundType === 4) { | ||
_firstValidTagPos = _tagRegExp.lastIndex; | ||
@@ -613,5 +616,12 @@ _lastValidTagPos = _firstValidTagPos; | ||
else { | ||
// when we have already included a part with XML-tag and there is ending tag | ||
// Do we need to handle multiple if-block in that case? | ||
return [_firstValidTagPos, _lastValidTagPos]; | ||
// when we have already included a valid XML part with XML-tag and here is the last ending tag (_openingTagPos is empty) | ||
// We can have other valid XML part after, so we keep this valid XML part and start again the search for | ||
// another valid XML part. At the end, we select the longest valid XML part. | ||
// We could improve it further if we generate multiple if-block in that case, to remove multiple valid XML part | ||
_validCandidates.push([_firstValidTagPos, _lastValidTagPos]); | ||
// start again to find a valid XML part | ||
_tagFoundType = 0; | ||
_isCurrentPartValid = true; | ||
_firstValidTagPos = _tagRegExp.lastIndex; | ||
_lastValidTagPos = _firstValidTagPos; | ||
} | ||
@@ -621,16 +631,14 @@ } | ||
else if ( _tag[2] === '/') { | ||
_tagFoundType |= 2; | ||
if (_isCurrentPartValid === true) { | ||
_lastValidTagPos = _tagRegExp.lastIndex; | ||
} | ||
// when the string starts with many self-closing XML tag | ||
if (_minimalXmlDepth === Infinity) { | ||
_lastValidTagPos = _tagRegExp.lastIndex; | ||
} | ||
} | ||
// opening tag | ||
else { | ||
_isCurrentPartValid = false; | ||
_tagFoundType |= 1; | ||
_isCurrentPartValid = (_tagFoundType === 1) ? true : false; | ||
_openingTagPos.push(_lastTagEndingPos); | ||
// when the string starts with many opening XML tag, "push" _firstValidTagPos | ||
if (_minimalXmlDepth === Infinity) { | ||
if (_tagFoundType === 1) { | ||
_firstValidTagPos = _tagRegExp.lastIndex; | ||
@@ -642,3 +650,3 @@ _lastValidTagPos = _firstValidTagPos; | ||
_tag = _tagRegExp.exec(xml); | ||
if (_tag !== null && _isCurrentPartValid === true && _tag.index > _lastTagEndingPos) { | ||
if (_tag !== null && _tag.index > _lastTagEndingPos) { | ||
_lastValidTagPos = Math.min(_tag.index, endSearchIndex); | ||
@@ -651,6 +659,23 @@ } | ||
// include last trailing non-XML characters when no XML tags have been found | ||
if (_minimalXmlDepth === Infinity && _tag !== null && _lastTagEndingPos < _tag.index ) { | ||
if (_isCurrentPartValid === true && _tag !== null && _lastTagEndingPos < _tag.index ) { | ||
_lastValidTagPos = endSearchIndex; | ||
} | ||
return [_firstValidTagPos, _lastValidTagPos]; | ||
if (_validCandidates.length === 0) { | ||
return [_firstValidTagPos, _lastValidTagPos]; | ||
} | ||
// Select the biggest valid XML part candidate when there are multiple choices | ||
if (_validCandidates.length > 0) { | ||
_validCandidates.push([_firstValidTagPos, _lastValidTagPos]); | ||
var _longestValidPart = 0; | ||
var _selectedValidPart = 0; | ||
for (var i = 0; i < _validCandidates.length; i++) { | ||
var _candidatePart = _validCandidates[i]; | ||
var _partLength = _candidatePart[1] - _candidatePart[0]; | ||
if (_longestValidPart < _partLength) { | ||
_longestValidPart = _partLength; | ||
_selectedValidPart = i; | ||
} | ||
} | ||
return _validCandidates[_selectedValidPart]; | ||
} | ||
} | ||
@@ -657,0 +682,0 @@ |
{ | ||
"name": "carbone", | ||
"description": "Fast, Simple and Powerful report generator. Injects JSON and produces PDF, DOCX, XLSX, ODT, PPTX, ODS, ...!", | ||
"version": "2.0.0-beta.0", | ||
"version": "2.0.0-beta.1", | ||
"bin": "bin/carbone", | ||
@@ -6,0 +6,0 @@ "main": "./lib", |
@@ -51,7 +51,6 @@ <p align="center"> | ||
- [Command line tools](#command-line-tools) | ||
- [Issues](#issues) | ||
- [Roadmap](#roadmap) | ||
- [Performance](#performance) | ||
- [Licenses and editions](#licenses-and-editions) | ||
- [Philosophy](#philosophy) | ||
- [Roadmap](#roadmap) | ||
- [Contributors](#contributors) | ||
@@ -304,7 +303,6 @@ | ||
- Maxime Vincent | ||
- [Steeve Payraudeau](https://github.com/steevepay) | ||
- Florian Bezagu | ||
- Matthieu Robin | ||
- Arnaud Lelièvre | ||
- Maxime Vincent | ||
- Enzo Ghemard | ||
@@ -319,3 +317,4 @@ - Jordan Nourry | ||
- Aurélien Kermabon | ||
- [Steeve Payraudeau](https://github.com/steevepay) | ||
Thanks to all French citizens (Crédit Impôt Recherche, Jeune Entreprise Innovante, BPI)! |
408171
7433
318