carbone
Version:
Fast, Simple and Powerful report generator. Injects JSON and produces PDF, DOCX, XLSX, ODT, PPTX, ODS, ...!
405 lines (385 loc) • 12.1 kB
JavaScript
var path = require('path');
var fs = require('fs');
var params = require('./params');
var yauzl = require('yauzl');
var unzipEmbeddedFileTypes = ['.xlsx', '.ods'];
var yazl = require('yazl');
var debug = require('debug')('carbone');
var file = {
/**
* is Zipped return callback(true) if the file is zipped
* @param {String} filePath file path
* @param {Function} callback(err, isZipped)
*/
isZipped : function (filePath, callback) {
var _buf = Buffer.allocUnsafe(10);
fs.open(filePath, 'r', function (err, fd) {
if (err) {
return callback(err, false);
}
fs.read(fd, _buf, 0, 10, 0, function (err, bytesRead, buffer) {
fs.close(fd, function () {
callback(err, (buffer.slice(0, 2).toString() === 'PK'));
});
});
});
},
/**
* Get specific file in template
*
* TODO test it
*
* @param {Object} template
* @param {String} filename
* @return {Object|null}
*/
getTemplateFile : function (template, filename) {
for (var i = 0; i < template.files.length; i++) {
if (template.files[i].name === filename) {
return template.files[i];
}
}
return null;
},
/**
* Unzip a file
* @param {String} filePath file to unzip
* @param {Function} callback(err, files) files is an array of files ['name':'filename', 'buffer':Buffer]
*/
unzip : function (filePath, callback) {
var _unzippedFiles = [];
var _unzipFn = yauzl.open;
if (Buffer.isBuffer(filePath) === true) {
_unzipFn = yauzl.fromBuffer;
}
_unzipFn(filePath, {lazyEntries : true, decodeStrings : true}, function (err, zipfile) {
if (err) {
return callback(err);
}
zipfile.on('end', function () {
zipfile.close();
return callback(null, _unzippedFiles);
});
zipfile.on('error', callback);
zipfile.readEntry();
zipfile.on('entry', function (entry) {
var _unzippedFile = {
name : entry.fileName,
data : Buffer.from([])
};
_unzippedFiles.push(_unzippedFile);
if (/\/$/.test(entry.fileName)) {
// directory file names end with '/'
zipfile.readEntry();
}
else {
zipfile.openReadStream(entry, function (err, readStream) {
if (err) {
zipfile.close();
return callback(err);
}
var buffers = [];
readStream.on('data', function (data) {
buffers.push(data);
});
readStream.on('end', function () {
_unzippedFile.data = Buffer.concat(buffers);
zipfile.readEntry();
});
readStream.on('error', function (err) {
zipfile.close();
return callback(err);
});
});
}
});
});
},
/**
* Zip a group of files
* @param {Array} files files is an array of files ['name':'filename', 'data':Buffer]
* @param {Function} callback(err, result) result is a buffer (the zip file)
*/
zip : function (files, callback) {
var _buffer = [];
var _zip = new yazl.ZipFile();
_zip.outputStream.on('data', function (data) {
_buffer.push(data);
});
_zip.outputStream.on('error', function (err) {
debug('Error when building zip file ' + err);
});
_zip.outputStream.on('end', function () {
var _finalBuffer = Buffer.concat(_buffer);
callback(null, _finalBuffer);
});
for (var i = 0; i < files.length; i++) {
var _file = files[i];
if (_file.name.endsWith('/') === true) {
_zip.addEmptyDirectory(_file.name);
}
else {
_zip.addBuffer(Buffer.from(_file.data), _file.name);
}
}
_zip.end();
},
/**
* Open a template (zipped or not). It will find the template and convert the buffer into strings if it contains xml
* @param {String} templateId template name (with or without the path)
* @param {Function} callback(err, template)
*/
openTemplate : function (templateId, callback) {
var _template = {
isZipped : false,
filename : templateId,
embeddings : [],
files : []
};
if (templateId instanceof Object) {
// accept already dezipped files (for test purpose)
return callback(null, templateId);
}
// security, remove access on parent directory
// if (/\.\./.test(path.dirname(templateId)) === true) {
// return callback('access forbidden');
// }
// and then use path instead of resolve
var _templateFile = path.resolve(params.templatePath, templateId);
file.isZipped(_templateFile, function (err, isZipped) {
if (err) {
return callback(err, _template);
}
if (isZipped === true) {
_template.isZipped = true;
var _filesToUnzip = [{
name : '',
data : _templateFile
}];
return unzipFiles(_template, _filesToUnzip, callback);
}
else {
fs.readFile(_templateFile, 'utf8', function (err, data) {
var _file = {
name : path.basename(templateId),
data : data,
isMarked : true,
parent : ''
};
_template.files.push(_file);
return callback(err, _template);
});
}
});
},
/**
* Check the extension of template
* @param {Object} template Template to analyze
*/
detectType : function (template) {
if (this._checkWordInFilename(template, 'word/')) {
return 'docx';
}
if (this._checkWordInFilename(template, 'xl/')) {
return 'xlsx';
}
if (this._checkWordInFilename(template, 'ppt/')) {
return 'pptx';
}
if (this._checkMimetypeFile(template, 'application/vnd.oasis.opendocument.graphics')) {
return 'odg';
}
if (this._checkMimetypeFile(template, 'application/vnd.oasis.opendocument.text')) {
return 'odt';
}
if (this._checkMimetypeFile(template, 'application/vnd.oasis.opendocument.spreadsheet')) {
return 'ods';
}
if (this._checkMimetypeFile(template, 'application/vnd.oasis.opendocument.presentation')) {
return 'odp';
}
if (this._checkMimetypeFile(template, 'application/vnd.adobe.indesign-idml-package')) {
return 'idml';
}
if (template.files.length === 1 && /office:mimetype="application\/vnd\.oasis\.opendocument\.text"/.test(template.files[0].data) === true) {
return 'fodt';
}
if (this._isXHTMLFile(template)) {
return 'xhtml';
}
if (this._isHTMLFile(template)) {
return 'html';
}
if (this._isXMLFile(template)) {
return 'xml';
}
var _extname = path.extname(template.filename).slice(1);
if (template.isZipped === false && _extname !== '') {
return _extname;
}
return null;
},
/**
* Check if the file is a XML file
* @param {Object} template File content to analyze
*/
_isXMLFile : function (template) {
for (var i = 0; i < template.files.length; i++) {
var _template = template.files[i].data;
if (/^\s*</.test(_template) === true) {
return true;
}
}
return false;
},
/**
* Check if the file is an XHTML file
* @param {Object} template File content to analyze
*/
_isXHTMLFile : function (template) {
for (var i = 0; i < template.files.length; i++) {
var _template = template.files[i].data;
if (/^\s*