workbook-writer.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. "use strict";
  2. function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
  3. function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
  4. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  5. function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
  6. function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
  7. var fs = require('fs');
  8. var Archiver = require('archiver');
  9. var StreamBuf = require('../../utils/stream-buf');
  10. var RelType = require('../../xlsx/rel-type');
  11. var StylesXform = require('../../xlsx/xform/style/styles-xform');
  12. var SharedStrings = require('../../utils/shared-strings');
  13. var DefinedNames = require('../../doc/defined-names');
  14. var CoreXform = require('../../xlsx/xform/core/core-xform');
  15. var RelationshipsXform = require('../../xlsx/xform/core/relationships-xform');
  16. var ContentTypesXform = require('../../xlsx/xform/core/content-types-xform');
  17. var AppXform = require('../../xlsx/xform/core/app-xform');
  18. var WorkbookXform = require('../../xlsx/xform/book/workbook-xform');
  19. var SharedStringsXform = require('../../xlsx/xform/strings/shared-strings-xform');
  20. var WorksheetWriter = require('./worksheet-writer');
  21. var theme1Xml = require('../../xlsx/xml/theme1.js');
  22. var WorkbookWriter = /*#__PURE__*/function () {
  23. function WorkbookWriter(options) {
  24. _classCallCheck(this, WorkbookWriter);
  25. options = options || {};
  26. this.created = options.created || new Date();
  27. this.modified = options.modified || this.created;
  28. this.creator = options.creator || 'ExcelJS';
  29. this.lastModifiedBy = options.lastModifiedBy || 'ExcelJS';
  30. this.lastPrinted = options.lastPrinted; // using shared strings creates a smaller xlsx file but may use more memory
  31. this.useSharedStrings = options.useSharedStrings || false;
  32. this.sharedStrings = new SharedStrings(); // style manager
  33. this.styles = options.useStyles ? new StylesXform(true) : new StylesXform.Mock(true); // defined names
  34. this._definedNames = new DefinedNames();
  35. this._worksheets = [];
  36. this.views = [];
  37. this.zipOptions = options.zip;
  38. this.media = [];
  39. this.commentRefs = [];
  40. this.zip = Archiver('zip', this.zipOptions);
  41. if (options.stream) {
  42. this.stream = options.stream;
  43. } else if (options.filename) {
  44. this.stream = fs.createWriteStream(options.filename);
  45. } else {
  46. this.stream = new StreamBuf();
  47. }
  48. this.zip.pipe(this.stream); // these bits can be added right now
  49. this.promise = Promise.all([this.addThemes(), this.addOfficeRels()]);
  50. }
  51. _createClass(WorkbookWriter, [{
  52. key: "_openStream",
  53. value: function _openStream(path) {
  54. var stream = new StreamBuf({
  55. bufSize: 65536,
  56. batch: true
  57. });
  58. this.zip.append(stream, {
  59. name: path
  60. });
  61. stream.on('finish', function () {
  62. stream.emit('zipped');
  63. });
  64. return stream;
  65. }
  66. }, {
  67. key: "_commitWorksheets",
  68. value: function _commitWorksheets() {
  69. var commitWorksheet = function commitWorksheet(worksheet) {
  70. if (!worksheet.committed) {
  71. return new Promise(function (resolve) {
  72. worksheet.stream.on('zipped', function () {
  73. resolve();
  74. });
  75. worksheet.commit();
  76. });
  77. }
  78. return Promise.resolve();
  79. }; // if there are any uncommitted worksheets, commit them now and wait
  80. var promises = this._worksheets.map(commitWorksheet);
  81. if (promises.length) {
  82. return Promise.all(promises);
  83. }
  84. return Promise.resolve();
  85. }
  86. }, {
  87. key: "commit",
  88. value: function () {
  89. var _commit = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
  90. return regeneratorRuntime.wrap(function _callee$(_context) {
  91. while (1) {
  92. switch (_context.prev = _context.next) {
  93. case 0:
  94. _context.next = 2;
  95. return this.promise;
  96. case 2:
  97. _context.next = 4;
  98. return this.addMedia();
  99. case 4:
  100. _context.next = 6;
  101. return this._commitWorksheets();
  102. case 6:
  103. _context.next = 8;
  104. return Promise.all([this.addContentTypes(), this.addApp(), this.addCore(), this.addSharedStrings(), this.addStyles(), this.addWorkbookRels()]);
  105. case 8:
  106. _context.next = 10;
  107. return this.addWorkbook();
  108. case 10:
  109. return _context.abrupt("return", this._finalize());
  110. case 11:
  111. case "end":
  112. return _context.stop();
  113. }
  114. }
  115. }, _callee, this);
  116. }));
  117. function commit() {
  118. return _commit.apply(this, arguments);
  119. }
  120. return commit;
  121. }()
  122. }, {
  123. key: "addImage",
  124. value: function addImage(image) {
  125. var id = this.media.length;
  126. var medium = Object.assign({}, image, {
  127. type: 'image',
  128. name: "image".concat(id, ".").concat(image.extension)
  129. });
  130. this.media.push(medium);
  131. return id;
  132. }
  133. }, {
  134. key: "getImage",
  135. value: function getImage(id) {
  136. return this.media[id];
  137. }
  138. }, {
  139. key: "addWorksheet",
  140. value: function addWorksheet(name, options) {
  141. // it's possible to add a worksheet with different than default
  142. // shared string handling
  143. // in fact, it's even possible to switch it mid-sheet
  144. options = options || {};
  145. var useSharedStrings = options.useSharedStrings !== undefined ? options.useSharedStrings : this.useSharedStrings;
  146. if (options.tabColor) {
  147. // eslint-disable-next-line no-console
  148. console.trace('tabColor option has moved to { properties: tabColor: {...} }');
  149. options.properties = Object.assign({
  150. tabColor: options.tabColor
  151. }, options.properties);
  152. }
  153. var id = this.nextId;
  154. name = name || "sheet".concat(id);
  155. var worksheet = new WorksheetWriter({
  156. id: id,
  157. name: name,
  158. workbook: this,
  159. useSharedStrings: useSharedStrings,
  160. properties: options.properties,
  161. state: options.state,
  162. pageSetup: options.pageSetup,
  163. views: options.views,
  164. autoFilter: options.autoFilter,
  165. headerFooter: options.headerFooter
  166. });
  167. this._worksheets[id] = worksheet;
  168. return worksheet;
  169. }
  170. }, {
  171. key: "getWorksheet",
  172. value: function getWorksheet(id) {
  173. if (id === undefined) {
  174. return this._worksheets.find(function () {
  175. return true;
  176. });
  177. }
  178. if (typeof id === 'number') {
  179. return this._worksheets[id];
  180. }
  181. if (typeof id === 'string') {
  182. return this._worksheets.find(function (worksheet) {
  183. return worksheet && worksheet.name === id;
  184. });
  185. }
  186. return undefined;
  187. }
  188. }, {
  189. key: "addStyles",
  190. value: function addStyles() {
  191. var _this = this;
  192. return new Promise(function (resolve) {
  193. _this.zip.append(_this.styles.xml, {
  194. name: 'xl/styles.xml'
  195. });
  196. resolve();
  197. });
  198. }
  199. }, {
  200. key: "addThemes",
  201. value: function addThemes() {
  202. var _this2 = this;
  203. return new Promise(function (resolve) {
  204. _this2.zip.append(theme1Xml, {
  205. name: 'xl/theme/theme1.xml'
  206. });
  207. resolve();
  208. });
  209. }
  210. }, {
  211. key: "addOfficeRels",
  212. value: function addOfficeRels() {
  213. var _this3 = this;
  214. return new Promise(function (resolve) {
  215. var xform = new RelationshipsXform();
  216. var xml = xform.toXml([{
  217. Id: 'rId1',
  218. Type: RelType.OfficeDocument,
  219. Target: 'xl/workbook.xml'
  220. }, {
  221. Id: 'rId2',
  222. Type: RelType.CoreProperties,
  223. Target: 'docProps/core.xml'
  224. }, {
  225. Id: 'rId3',
  226. Type: RelType.ExtenderProperties,
  227. Target: 'docProps/app.xml'
  228. }]);
  229. _this3.zip.append(xml, {
  230. name: '/_rels/.rels'
  231. });
  232. resolve();
  233. });
  234. }
  235. }, {
  236. key: "addContentTypes",
  237. value: function addContentTypes() {
  238. var _this4 = this;
  239. return new Promise(function (resolve) {
  240. var model = {
  241. worksheets: _this4._worksheets.filter(Boolean),
  242. sharedStrings: _this4.sharedStrings,
  243. commentRefs: _this4.commentRefs,
  244. media: _this4.media
  245. };
  246. var xform = new ContentTypesXform();
  247. var xml = xform.toXml(model);
  248. _this4.zip.append(xml, {
  249. name: '[Content_Types].xml'
  250. });
  251. resolve();
  252. });
  253. }
  254. }, {
  255. key: "addMedia",
  256. value: function addMedia() {
  257. var _this5 = this;
  258. return Promise.all(this.media.map(function (medium) {
  259. if (medium.type === 'image') {
  260. var filename = "xl/media/".concat(medium.name);
  261. if (medium.filename) {
  262. return _this5.zip.file(medium.filename, {
  263. name: filename
  264. });
  265. }
  266. if (medium.buffer) {
  267. return _this5.zip.append(medium.buffer, {
  268. name: filename
  269. });
  270. }
  271. if (medium.base64) {
  272. var dataimg64 = medium.base64;
  273. var content = dataimg64.substring(dataimg64.indexOf(',') + 1);
  274. return _this5.zip.append(content, {
  275. name: filename,
  276. base64: true
  277. });
  278. }
  279. }
  280. throw new Error('Unsupported media');
  281. }));
  282. }
  283. }, {
  284. key: "addApp",
  285. value: function addApp() {
  286. var _this6 = this;
  287. return new Promise(function (resolve) {
  288. var model = {
  289. worksheets: _this6._worksheets.filter(Boolean)
  290. };
  291. var xform = new AppXform();
  292. var xml = xform.toXml(model);
  293. _this6.zip.append(xml, {
  294. name: 'docProps/app.xml'
  295. });
  296. resolve();
  297. });
  298. }
  299. }, {
  300. key: "addCore",
  301. value: function addCore() {
  302. var _this7 = this;
  303. return new Promise(function (resolve) {
  304. var coreXform = new CoreXform();
  305. var xml = coreXform.toXml(_this7);
  306. _this7.zip.append(xml, {
  307. name: 'docProps/core.xml'
  308. });
  309. resolve();
  310. });
  311. }
  312. }, {
  313. key: "addSharedStrings",
  314. value: function addSharedStrings() {
  315. var _this8 = this;
  316. if (this.sharedStrings.count) {
  317. return new Promise(function (resolve) {
  318. var sharedStringsXform = new SharedStringsXform();
  319. var xml = sharedStringsXform.toXml(_this8.sharedStrings);
  320. _this8.zip.append(xml, {
  321. name: '/xl/sharedStrings.xml'
  322. });
  323. resolve();
  324. });
  325. }
  326. return Promise.resolve();
  327. }
  328. }, {
  329. key: "addWorkbookRels",
  330. value: function addWorkbookRels() {
  331. var _this9 = this;
  332. var count = 1;
  333. var relationships = [{
  334. Id: "rId".concat(count++),
  335. Type: RelType.Styles,
  336. Target: 'styles.xml'
  337. }, {
  338. Id: "rId".concat(count++),
  339. Type: RelType.Theme,
  340. Target: 'theme/theme1.xml'
  341. }];
  342. if (this.sharedStrings.count) {
  343. relationships.push({
  344. Id: "rId".concat(count++),
  345. Type: RelType.SharedStrings,
  346. Target: 'sharedStrings.xml'
  347. });
  348. }
  349. this._worksheets.forEach(function (worksheet) {
  350. if (worksheet) {
  351. worksheet.rId = "rId".concat(count++);
  352. relationships.push({
  353. Id: worksheet.rId,
  354. Type: RelType.Worksheet,
  355. Target: "worksheets/sheet".concat(worksheet.id, ".xml")
  356. });
  357. }
  358. });
  359. return new Promise(function (resolve) {
  360. var xform = new RelationshipsXform();
  361. var xml = xform.toXml(relationships);
  362. _this9.zip.append(xml, {
  363. name: '/xl/_rels/workbook.xml.rels'
  364. });
  365. resolve();
  366. });
  367. }
  368. }, {
  369. key: "addWorkbook",
  370. value: function addWorkbook() {
  371. var zip = this.zip;
  372. var model = {
  373. worksheets: this._worksheets.filter(Boolean),
  374. definedNames: this._definedNames.model,
  375. views: this.views,
  376. properties: {},
  377. calcProperties: {}
  378. };
  379. return new Promise(function (resolve) {
  380. var xform = new WorkbookXform();
  381. xform.prepare(model);
  382. zip.append(xform.toXml(model), {
  383. name: '/xl/workbook.xml'
  384. });
  385. resolve();
  386. });
  387. }
  388. }, {
  389. key: "_finalize",
  390. value: function _finalize() {
  391. var _this10 = this;
  392. return new Promise(function (resolve, reject) {
  393. _this10.stream.on('error', reject);
  394. _this10.stream.on('finish', function () {
  395. resolve(_this10);
  396. });
  397. _this10.zip.on('error', reject);
  398. _this10.zip.finalize();
  399. });
  400. }
  401. }, {
  402. key: "definedNames",
  403. get: function get() {
  404. return this._definedNames;
  405. }
  406. }, {
  407. key: "nextId",
  408. get: function get() {
  409. // find the next unique spot to add worksheet
  410. var i;
  411. for (i = 1; i < this._worksheets.length; i++) {
  412. if (!this._worksheets[i]) {
  413. return i;
  414. }
  415. }
  416. return this._worksheets.length || 1;
  417. }
  418. }]);
  419. return WorkbookWriter;
  420. }();
  421. module.exports = WorkbookWriter;
  422. //# sourceMappingURL=workbook-writer.js.map