Skip to content

Commit

Permalink
Merge pull request #3 from nerds-odd-e/Number_formats
Browse files Browse the repository at this point in the history
Thank you for the excellent patch!
  • Loading branch information
nuNuKim committed Mar 18, 2014
2 parents e9ecb07 + d29b0dd commit 78267a6
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 56 deletions.
14 changes: 13 additions & 1 deletion README.md
Expand Up @@ -7,6 +7,10 @@ Creates SpreadsheetML (.xlsx) files in sequence with streaming interface.

npm install xlsx-stream

* Features

Multiple sheets, String, Number, Date, Duration, Cell Formats

* Usage

# coffee-script
Expand All @@ -18,7 +22,12 @@ Creates SpreadsheetML (.xlsx) files in sequence with streaming interface.
x.write ["foo", "bar", "buz"]
x.write [1,2,3]
x.write ["Date", new Date]
x.write ["Duration", { v: 1.5, t: 'n', nf: '[h]:mm:ss' }]
x.write ["Formula", {v: "ok", f: "CONCATENATE(A1,B2)"}]
x.write ["Percentage Built-in format #9", { v: 0.5, nf: '0.00%' }]
x.write ["Percentage Custom format", { v: 0.5, nf: '00.000%' }]

x.end()

* Multiple sheets support
Expand All @@ -38,3 +47,6 @@ Creates SpreadsheetML (.xlsx) files in sequence with streaming interface.

x.finalize()

* Help Wanted

Comments
64 changes: 49 additions & 15 deletions lib/index.js
Expand Up @@ -15,7 +15,7 @@ utils = require("./utils");
sheetStream = require("./sheet");

module.exports = xlsxStream = function(opts) {
var defaultRepeater, defaultSheet, proxy, sheets, zip;
var defaultRepeater, defaultSheet, index, item, proxy, sheets, styles, zip, _i, _len, _ref;
if (opts == null) {
opts = {};
}
Expand All @@ -28,48 +28,82 @@ module.exports = xlsxStream = function(opts) {
});
defaultSheet = null;
sheets = [];
styles = {
numFmts: [
{
numFmtId: "0",
formatCode: ""
}
],
cellStyleXfs: [
{
numFmtId: "0",
formatCode: ""
}, {
numFmtId: "1",
formatCode: "0"
}, {
numFmtId: "14",
formatCode: "m/d/yy"
}
],
customFormatsCount: 0,
formatCodesToStyleIndex: {}
};
index = 0;
_ref = styles.cellStyleXfs;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
item = _ref[_i];
styles.formatCodesToStyleIndex[item.formatCode || ""] = index;
index++;
}
defaultRepeater.once('data', function(data) {
defaultSheet = proxy.sheet('Sheet1');
defaultSheet.write(data);
defaultRepeater.pipe(defaultSheet);
return defaultRepeater.on('end', proxy.finalize);
});
proxy.sheet = function(name) {
var index, sheet;
var sheet;
index = sheets.length + 1;
sheet = {
index: index,
name: name || ("Sheet" + index),
rel: "worksheets/sheet" + index + ".xml",
path: "xl/worksheets/sheet" + index + ".xml"
path: "xl/worksheets/sheet" + index + ".xml",
styles: styles
};
sheets.push(sheet);
return sheetStream(zip, sheet, opts);
};
proxy.finalize = function() {
var buffer, func, name, obj, sheet, _i, _len, _ref, _ref1, _ref2;
_ref = templates.statics;
for (name in _ref) {
buffer = _ref[name];
var buffer, func, name, obj, sheet, _j, _len1, _ref1, _ref2, _ref3;
zip.append(templates.styles(styles), {
name: "xl/styles.xml",
store: opts.store
});
_ref1 = templates.statics;
for (name in _ref1) {
buffer = _ref1[name];
zip.append(buffer, {
name: name,
store: opts.store
});
}
_ref1 = templates.semiStatics;
for (name in _ref1) {
func = _ref1[name];
_ref2 = templates.semiStatics;
for (name in _ref2) {
func = _ref2[name];
zip.append(func(opts), {
name: name,
store: opts.store
});
}
_ref2 = templates.sheet_related;
for (name in _ref2) {
obj = _ref2[name];
_ref3 = templates.sheet_related;
for (name in _ref3) {
obj = _ref3[name];
buffer = obj.header;
for (_i = 0, _len = sheets.length; _i < _len; _i++) {
sheet = sheets[_i];
for (_j = 0, _len1 = sheets.length; _j < _len1; _j++) {
sheet = sheets[_j];
buffer += obj.sheet(sheet);
}
buffer += obj.footer;
Expand Down
4 changes: 2 additions & 2 deletions lib/sheet.js
Expand Up @@ -23,12 +23,12 @@ module.exports = sheetStream = function(zip, sheet, opts) {
_ref = opts.columns;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
col = _ref[i];
buf += utils.buildCell("" + (colChar(i)) + nRow, row[col]);
buf += utils.buildCell("" + (colChar(i)) + nRow, row[col], sheet.styles);
}
} else {
for (i = _j = 0, _len1 = row.length; _j < _len1; i = ++_j) {
val = row[i];
buf += utils.buildCell("" + (colChar(i)) + nRow, val);
buf += utils.buildCell("" + (colChar(i)) + nRow, val, sheet.styles);
}
}
buf += '</row>';
Expand Down
19 changes: 18 additions & 1 deletion lib/templates.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

128 changes: 120 additions & 8 deletions lib/utils.js
Expand Up @@ -20,18 +20,130 @@ module.exports = {
compress: compress = function(str) {
return String(str).replace(/\n\s*/g, '');
},
buildCell: function(ref, val) {
buildCell: function(ref, val, styles) {
var f, getStyle, r, s, t, v;
getStyle = function(nf) {
var getBuiltinNumFmtId, numFmtId, r, s;
if (!nf) {
return;
}
r = styles.formatCodesToStyleIndex[nf];
if (r) {
return r;
}
getBuiltinNumFmtId = function(nf) {
var builtin_nfs;
builtin_nfs = {
'General': 0,
'': 0,
'0': 1,
'0.00': 2,
'#,##0': 3,
'#,##0.00': 4,
'0%': 9,
'0.00%': 10,
'0.00E+00': 11,
'# ?/?': 12,
'# ??/??': 13,
'm/d/yy': 14,
'd-mmm-yy': 15,
'd-mmm': 16,
'mmm-yy': 17,
'h:mm AM/PM': 18,
'h:mm:ss AM/PM': 19,
'h:mm': 20,
'h:mm:ss': 21,
'm/d/yy h:mm': 22,
'[$-404]e/m/d': 27,
'#,##0 ;(#,##0)': 37,
'#,##0 ;[Red](#,##0)': 38,
'#,##0.00;(#,##0.00)': 39,
'#,##0.00;[Red](#,##0.00)': 40,
'_("$"* #,##0.00_);_("$"* \\(#,##0.00\\);_("$"* "-"??_);_(@_)': 44,
'mm:ss': 45,
'[h]:mm:ss': 46,
'mmss.0': 47,
'##0.0E+0': 48,
'@': 49,
't0': 59,
't0.00': 60,
't#,##0': 61,
't#,##0.00': 62,
't0%': 67,
't0.00%': 68,
't# ?/?': 69,
't# ??/??': 70
};
r = builtin_nfs[nf];
return r;
};
numFmtId = getBuiltinNumFmtId(nf);
if (!numFmtId) {
styles.customFormatsCount++;
numFmtId = 164 + styles.customFormatsCount;
styles.numFmts.push({
numFmtId: numFmtId,
formatCode: nf
});
}
s = styles.cellStyleXfs.length;
styles.cellStyleXfs.push({
numFmtId: numFmtId,
formatCode: nf
});
styles.formatCodesToStyleIndex[nf] = s;
return s;
};
if (val == null) {
return '';
}
if (_.isNumber(val) && _.isFinite(val)) {
return "<c r='" + ref + "'><v>" + val + "</v></c>";
} else if (_.isDate(val)) {
return "<c r='" + ref + "' t='d' s='2'><v>" + (val.toISOString()) + "</v></c>";
} else if (_.isBoolean(val)) {
return "<c r='" + ref + "' t='b'><v>" + (val ? '1' : '0') + "</v></c>";
if (typeof val === 'object' && !_.isDate(val)) {
v = val.v;
t = val.t;
s = val.s;
f = val.f;
if (!s && val.nf) {
s = getStyle(val.nf);
}
} else {
return "<c r='" + ref + "' t='inlineStr'><is><t>" + (escapeXML(val)) + "</t></is></c>";
v = val;
}
if (_.isNumber(v) && _.isFinite(v)) {
v = '<v>' + v + '</v>';
if (val.nf && !t) {
t = 'n';
}
} else if (_.isDate(v)) {
t = 'd';
if (s == null) {
s = '2';
}
v = '<v>' + v.toISOString() + '</v>';
} else if (_.isBoolean(v)) {
t = 'b';
v = '<v>' + (v === true ? '1' : '0') + '</v>';
} else if (v) {
v = '<is><t>' + escapeXML(v) + '</t></is>';
t = 'inlineStr';
}
if (!(v || f)) {
return '';
}
r = '<c r="' + ref + '"';
if (t) {
r += ' t="' + t + '"';
}
if (s) {
r += ' s="' + s + '"';
}
r += '>';
if (f) {
r += '<f>' + f + '</f>';
}
if (v) {
r += v;
}
r += '</c>';
return r;
}
};
20 changes: 20 additions & 0 deletions src/index.coffee
Expand Up @@ -33,6 +33,22 @@ module.exports = xlsxStream = (opts = {})->

defaultSheet = null
sheets = []
styles =
numFmts: [
numFmtId: "0",
formatCode: ""
]
cellStyleXfs: [
{ numFmtId: "0", formatCode: "" }
{ numFmtId: "1", formatCode: "0" }
{ numFmtId: "14", formatCode: "m/d/yy" }
]
customFormatsCount: 0
formatCodesToStyleIndex: {}
index = 0
for item in styles.cellStyleXfs
styles.formatCodesToStyleIndex[item.formatCode || ""] = index
index++

# writing data without sheet() results in creating a default worksheet named 'Sheet1'
defaultRepeater.once 'data', (data)->
Expand All @@ -49,11 +65,15 @@ module.exports = xlsxStream = (opts = {})->
name: name || "Sheet#{index}"
rel: "worksheets/sheet#{index}.xml"
path: "xl/worksheets/sheet#{index}.xml"
styles: styles
sheets.push sheet
sheetStream(zip, sheet, opts)

# finalize the xlsx file
proxy.finalize = ->
# styles
zip.append templates.styles(styles), { name: "xl/styles.xml", store: opts.store }

# static files
for name, buffer of templates.statics
zip.append buffer, {name, store: opts.store}
Expand Down
4 changes: 2 additions & 2 deletions src/sheet.coffee
Expand Up @@ -15,9 +15,9 @@ module.exports = sheetStream = (zip, sheet, opts={})->
nRow++
buf = "<row r='#{nRow}'>"
if opts.columns?
buf += utils.buildCell("#{colChar(i)}#{nRow}", row[col]) for col, i in opts.columns
buf += utils.buildCell("#{colChar(i)}#{nRow}", row[col], sheet.styles) for col, i in opts.columns
else
buf += utils.buildCell("#{colChar(i)}#{nRow}", val) for val, i in row
buf += utils.buildCell("#{colChar(i)}#{nRow}", val, sheet.styles) for val, i in row
buf += '</row>'
@queue buf
onEnd = ->
Expand Down

0 comments on commit 78267a6

Please sign in to comment.