diff --git a/lib/xlsx/xform/sheet/cell-xform.js b/lib/xlsx/xform/sheet/cell-xform.js
index e5a6c1eab..2fb075333 100644
--- a/lib/xlsx/xform/sheet/cell-xform.js
+++ b/lib/xlsx/xform/sheet/cell-xform.js
@@ -12,6 +12,7 @@ var BaseXform = require('../base-xform');
var Enums = require('../../../doc/enums');
var Range = require('../../../doc/range');
+var RichTextXform = require('../strings/rich-text-xform');
function getValueType(v) {
if ((v === null) || (v === undefined)) {
@@ -45,6 +46,7 @@ function getEffectiveCellType(cell) {
var CellXform = module.exports = function() {
+ this.richTextXForm = new RichTextXform();
};
utils.inherits(CellXform, BaseXform, {
@@ -191,8 +193,18 @@ utils.inherits(CellXform, BaseXform, {
xmlStream.addAttribute('t', 's');
xmlStream.leafNode('v', null, model.ssId);
} else {
- xmlStream.addAttribute('t', 'str');
- xmlStream.leafNode('v', null, model.value);
+ if (model.value && model.value.richText) {
+ xmlStream.addAttribute('t', 'inlineStr');
+ xmlStream.openNode('is');
+ var self = this;
+ model.value.richText.forEach(function(text) {
+ self.richTextXForm.render(xmlStream, text);
+ });
+ xmlStream.closeNode('is');
+ } else {
+ xmlStream.addAttribute('t', 'str');
+ xmlStream.leafNode('v', null, model.value);
+ }
}
break;
@@ -224,8 +236,12 @@ utils.inherits(CellXform, BaseXform, {
xmlStream.closeNode(); //
},
-
+
parseOpen: function(node) {
+ if (this.parser) {
+ this.parser.parseOpen(node);
+ return true;
+ }
switch (node.name) {
case 'c':
// var address = colCache.decodeAddress(node.attributes.r);
@@ -251,17 +267,35 @@ utils.inherits(CellXform, BaseXform, {
this.currentNode = 'v';
return true;
+ case 't':
+ this.currentNode = 't';
+ return true;
+
+ case 'r':
+ this.parser = this.richTextXForm;
+ this.parser.parseOpen(node);
+ return true;
+
default:
return false;
}
},
parseText: function(text) {
+ if (this.parser) {
+ this.parser.parseText(text);
+ return;
+ }
switch (this.currentNode) {
case 'f':
this.model.formula = this.model.formula ? this.model.formula + text : text;
break;
case 'v':
- this.model.value = this.model.value ? this.model.value + text : text;
+ case 't':
+ if (this.model.value && this.model.value.richText) {
+ this.model.value.richText.text = this.model.value.richText.text ? this.model.value.richText.text + text : text;
+ } else {
+ this.model.value = this.model.value ? this.model.value + text : text;
+ }
break;
default:
break;
@@ -297,6 +331,9 @@ utils.inherits(CellXform, BaseXform, {
model.type = Enums.ValueType.String;
model.value = utils.xmlDecode(model.value);
break;
+ case 'inlineStr':
+ model.type = Enums.ValueType.String;
+ break;
case 'b':
model.type = Enums.ValueType.Boolean;
model.value = parseInt(model.value, 10) !== 0;
@@ -318,9 +355,29 @@ utils.inherits(CellXform, BaseXform, {
return false;
case 'f':
case 'v':
+ case 'is':
+ this.currentNode = undefined;
+ return true;
+ case 't':
+ if (this.parser) {
+ this.parser.parseClose(name);
+ return true;
+ } else {
+ this.currentNode = undefined;
+ return true;
+ }
+ case 'r':
+ this.model.value = this.model.value || {};
+ this.model.value.richText = this.model.value.richText || [];
+ this.model.value.richText.push(this.parser.model);
+ this.parser = undefined;
this.currentNode = undefined;
return true;
default:
+ if (this.parser) {
+ this.parser.parseClose(name);
+ return true;
+ }
return false;
}
},
@@ -366,7 +423,7 @@ utils.inherits(CellXform, BaseXform, {
default:
break;
}
-
+
// look for hyperlink
var hyperlink = options.hyperlinkMap[model.address];
if (hyperlink) {
diff --git a/spec/integration/workbook-xlsx-writer/workbook-xlsx-writer.spec.js b/spec/integration/workbook-xlsx-writer/workbook-xlsx-writer.spec.js
index a9c18fda8..842858ecc 100644
--- a/spec/integration/workbook-xlsx-writer/workbook-xlsx-writer.spec.js
+++ b/spec/integration/workbook-xlsx-writer/workbook-xlsx-writer.spec.js
@@ -166,6 +166,49 @@ describe('WorkbookWriter', function() {
});
});
+ it('rich text', function() {
+ var options = {
+ filename: TEST_XLSX_FILE_NAME,
+ useStyles: true
+ };
+ var wb = new Excel.stream.xlsx.WorkbookWriter(options);
+ var ws = wb.addWorksheet('Hello');
+
+ ws.getCell('A1').value = {
+ richText: [
+ {
+ font: {color: {argb: 'FF0000'}}, text: 'red '
+ },
+ {
+ font: {color: {argb: '00FF00'}, bold: true}, text: ' bold green'
+ }
+ ]
+ };
+
+ ws.getCell('B1').value = 'plain text'
+
+ ws.commit();
+ return wb.commit()
+ .then(function() {
+ var wb2 = new Excel.Workbook();
+ return wb2.xlsx.readFile(TEST_XLSX_FILE_NAME);
+ })
+ .then(function(wb2) {
+ var ws2 = wb2.getWorksheet('Hello');
+ expect(ws2.getCell('A1').value).to.deep.equal({
+ richText: [
+ {
+ font: {color: {argb: 'FF0000'}}, text: 'red '
+ },
+ {
+ font: {color: {argb: '00FF00'}, bold: true}, text: ' bold green'
+ }
+ ]
+ });
+ expect(ws2.getCell('B1').value).to.equal('plain text');
+ });
+ });
+
it('A lot of sheets', function() {
this.timeout(5000);
diff --git a/spec/unit/xlsx/xform/sheet/cell-xform.spec.js b/spec/unit/xlsx/xform/sheet/cell-xform.spec.js
index f73ae6684..03001f971 100644
--- a/spec/unit/xlsx/xform/sheet/cell-xform.spec.js
+++ b/spec/unit/xlsx/xform/sheet/cell-xform.spec.js
@@ -61,7 +61,7 @@ var expectations = [
tests: ['render', 'renderIn', 'parse']
},
{
- title: 'Inline String',
+ title: 'String',
create: function() { return new CellXform(); },
initialModel: {address: 'A1', type: Enums.ValueType.String, value: 'Foo'},
preparedModel: {address: 'A1', type: Enums.ValueType.String, value: 'Foo'},
@@ -71,6 +71,26 @@ var expectations = [
tests: ['prepare', 'render', 'renderIn', 'parse', 'reconcile'],
options: { hyperlinkMap: fakeHyperlinkMap, styles: fakeStyles }
},
+ {
+ title: 'Inline String with plain text',
+ create: function() { return new CellXform(); },
+ xml: 'Foo',
+ parsedModel: {address: 'A1', type: Enums.ValueType.String, value: 'Foo'},
+ reconciledModel: {address: 'A1', type: Enums.ValueType.String, value: 'Foo'},
+ tests: ['parse', 'reconcile'],
+ options: { hyperlinkMap: fakeHyperlinkMap, styles: fakeStyles }
+ },
+ {
+ title: 'Inline String with RichText',
+ create: function() { return new CellXform(); },
+ initialModel: {address: 'A1', type: Enums.ValueType.String, value: { richText: [ {font: {color: {argb: 'FF0000'}}, text: 'red'}, {font: {color: {argb: '00FF00'}}, text: 'green'} ] }},
+ preparedModel: {address: 'A1', type: Enums.ValueType.String, value: { richText: [ {font: {color: {argb: 'FF0000'}}, text: 'red'}, {font: {color: {argb: '00FF00'}}, text: 'green'} ] }},
+ xml: 'redgreen',
+ parsedModel: {address: 'A1', type: Enums.ValueType.String, value: { richText: [ {font: {color: {argb: 'FF0000'}}, text: 'red'}, {font: {color: {argb: '00FF00'}}, text: 'green'} ] }},
+ reconciledModel: {address: 'A1', type: Enums.ValueType.RichText, value: { richText: [ {font: {color: {argb: 'FF0000'}}, text: 'red'}, {font: {color: {argb: '00FF00'}}, text: 'green'} ] }},
+ tests: ['prepare', 'render', 'renderIn', 'parse', 'reconcile'],
+ options: { hyperlinkMap: fakeHyperlinkMap, styles: fakeStyles }
+ },
{
title: 'Shared String',
create: function() { return new CellXform(); },