You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					692 lines
				
				22 KiB
			
		
		
			
		
	
	
					692 lines
				
				22 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								const fs = require('fs');
							 | 
						||
| 
								 | 
							
								const JSZip = require('jszip');
							 | 
						||
| 
								 | 
							
								const {PassThrough} = require('readable-stream');
							 | 
						||
| 
								 | 
							
								const ZipStream = require('../utils/zip-stream');
							 | 
						||
| 
								 | 
							
								const StreamBuf = require('../utils/stream-buf');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const utils = require('../utils/utils');
							 | 
						||
| 
								 | 
							
								const XmlStream = require('../utils/xml-stream');
							 | 
						||
| 
								 | 
							
								const {bufferToString} = require('../utils/browser-buffer-decode');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const StylesXform = require('./xform/style/styles-xform');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const CoreXform = require('./xform/core/core-xform');
							 | 
						||
| 
								 | 
							
								const SharedStringsXform = require('./xform/strings/shared-strings-xform');
							 | 
						||
| 
								 | 
							
								const RelationshipsXform = require('./xform/core/relationships-xform');
							 | 
						||
| 
								 | 
							
								const ContentTypesXform = require('./xform/core/content-types-xform');
							 | 
						||
| 
								 | 
							
								const AppXform = require('./xform/core/app-xform');
							 | 
						||
| 
								 | 
							
								const WorkbookXform = require('./xform/book/workbook-xform');
							 | 
						||
| 
								 | 
							
								const WorksheetXform = require('./xform/sheet/worksheet-xform');
							 | 
						||
| 
								 | 
							
								const DrawingXform = require('./xform/drawing/drawing-xform');
							 | 
						||
| 
								 | 
							
								const TableXform = require('./xform/table/table-xform');
							 | 
						||
| 
								 | 
							
								const CommentsXform = require('./xform/comment/comments-xform');
							 | 
						||
| 
								 | 
							
								const VmlNotesXform = require('./xform/comment/vml-notes-xform');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const theme1Xml = require('./xml/theme1.js');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function fsReadFileAsync(filename, options) {
							 | 
						||
| 
								 | 
							
								  return new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								    fs.readFile(filename, options, (error, data) => {
							 | 
						||
| 
								 | 
							
								      if (error) {
							 | 
						||
| 
								 | 
							
								        reject(error);
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        resolve(data);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class XLSX {
							 | 
						||
| 
								 | 
							
								  constructor(workbook) {
							 | 
						||
| 
								 | 
							
								    this.workbook = workbook;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // ===============================================================================
							 | 
						||
| 
								 | 
							
								  // Workbook
							 | 
						||
| 
								 | 
							
								  // =========================================================================
							 | 
						||
| 
								 | 
							
								  // Read
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async readFile(filename, options) {
							 | 
						||
| 
								 | 
							
								    if (!(await utils.fs.exists(filename))) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`File not found: ${filename}`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const stream = fs.createReadStream(filename);
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      const workbook = await this.read(stream, options);
							 | 
						||
| 
								 | 
							
								      stream.close();
							 | 
						||
| 
								 | 
							
								      return workbook;
							 | 
						||
| 
								 | 
							
								    } catch (error) {
							 | 
						||
| 
								 | 
							
								      stream.close();
							 | 
						||
| 
								 | 
							
								      throw error;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  parseRels(stream) {
							 | 
						||
| 
								 | 
							
								    const xform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    return xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  parseWorkbook(stream) {
							 | 
						||
| 
								 | 
							
								    const xform = new WorkbookXform();
							 | 
						||
| 
								 | 
							
								    return xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  parseSharedStrings(stream) {
							 | 
						||
| 
								 | 
							
								    const xform = new SharedStringsXform();
							 | 
						||
| 
								 | 
							
								    return xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  reconcile(model, options) {
							 | 
						||
| 
								 | 
							
								    const workbookXform = new WorkbookXform();
							 | 
						||
| 
								 | 
							
								    const worksheetXform = new WorksheetXform(options);
							 | 
						||
| 
								 | 
							
								    const drawingXform = new DrawingXform();
							 | 
						||
| 
								 | 
							
								    const tableXform = new TableXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    workbookXform.reconcile(model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // reconcile drawings with their rels
							 | 
						||
| 
								 | 
							
								    const drawingOptions = {
							 | 
						||
| 
								 | 
							
								      media: model.media,
							 | 
						||
| 
								 | 
							
								      mediaIndex: model.mediaIndex,
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    Object.keys(model.drawings).forEach(name => {
							 | 
						||
| 
								 | 
							
								      const drawing = model.drawings[name];
							 | 
						||
| 
								 | 
							
								      const drawingRel = model.drawingRels[name];
							 | 
						||
| 
								 | 
							
								      if (drawingRel) {
							 | 
						||
| 
								 | 
							
								        drawingOptions.rels = drawingRel.reduce((o, rel) => {
							 | 
						||
| 
								 | 
							
								          o[rel.Id] = rel;
							 | 
						||
| 
								 | 
							
								          return o;
							 | 
						||
| 
								 | 
							
								        }, {});
							 | 
						||
| 
								 | 
							
								        (drawing.anchors || []).forEach(anchor => {
							 | 
						||
| 
								 | 
							
								          const hyperlinks = anchor.picture && anchor.picture.hyperlinks;
							 | 
						||
| 
								 | 
							
								          if (hyperlinks && drawingOptions.rels[hyperlinks.rId]) {
							 | 
						||
| 
								 | 
							
								            hyperlinks.hyperlink = drawingOptions.rels[hyperlinks.rId].Target;
							 | 
						||
| 
								 | 
							
								            delete hyperlinks.rId;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        drawingXform.reconcile(drawing, drawingOptions);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // reconcile tables with the default styles
							 | 
						||
| 
								 | 
							
								    const tableOptions = {
							 | 
						||
| 
								 | 
							
								      styles: model.styles,
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    Object.values(model.tables).forEach(table => {
							 | 
						||
| 
								 | 
							
								      tableXform.reconcile(table, tableOptions);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const sheetOptions = {
							 | 
						||
| 
								 | 
							
								      styles: model.styles,
							 | 
						||
| 
								 | 
							
								      sharedStrings: model.sharedStrings,
							 | 
						||
| 
								 | 
							
								      media: model.media,
							 | 
						||
| 
								 | 
							
								      mediaIndex: model.mediaIndex,
							 | 
						||
| 
								 | 
							
								      date1904: model.properties && model.properties.date1904,
							 | 
						||
| 
								 | 
							
								      drawings: model.drawings,
							 | 
						||
| 
								 | 
							
								      comments: model.comments,
							 | 
						||
| 
								 | 
							
								      tables: model.tables,
							 | 
						||
| 
								 | 
							
								      vmlDrawings: model.vmlDrawings,
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      worksheet.relationships = model.worksheetRels[worksheet.sheetNo];
							 | 
						||
| 
								 | 
							
								      worksheetXform.reconcile(worksheet, sheetOptions);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // delete unnecessary parts
							 | 
						||
| 
								 | 
							
								    delete model.worksheetHash;
							 | 
						||
| 
								 | 
							
								    delete model.worksheetRels;
							 | 
						||
| 
								 | 
							
								    delete model.globalRels;
							 | 
						||
| 
								 | 
							
								    delete model.sharedStrings;
							 | 
						||
| 
								 | 
							
								    delete model.workbookRels;
							 | 
						||
| 
								 | 
							
								    delete model.sheetDefs;
							 | 
						||
| 
								 | 
							
								    delete model.styles;
							 | 
						||
| 
								 | 
							
								    delete model.mediaIndex;
							 | 
						||
| 
								 | 
							
								    delete model.drawings;
							 | 
						||
| 
								 | 
							
								    delete model.drawingRels;
							 | 
						||
| 
								 | 
							
								    delete model.vmlDrawings;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processWorksheetEntry(stream, model, sheetNo, options, path) {
							 | 
						||
| 
								 | 
							
								    const xform = new WorksheetXform(options);
							 | 
						||
| 
								 | 
							
								    const worksheet = await xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								    worksheet.sheetNo = sheetNo;
							 | 
						||
| 
								 | 
							
								    model.worksheetHash[path] = worksheet;
							 | 
						||
| 
								 | 
							
								    model.worksheets.push(worksheet);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processCommentEntry(stream, model, name) {
							 | 
						||
| 
								 | 
							
								    const xform = new CommentsXform();
							 | 
						||
| 
								 | 
							
								    const comments = await xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								    model.comments[`../${name}.xml`] = comments;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processTableEntry(stream, model, name) {
							 | 
						||
| 
								 | 
							
								    const xform = new TableXform();
							 | 
						||
| 
								 | 
							
								    const table = await xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								    model.tables[`../tables/${name}.xml`] = table;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processWorksheetRelsEntry(stream, model, sheetNo) {
							 | 
						||
| 
								 | 
							
								    const xform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    const relationships = await xform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								    model.worksheetRels[sheetNo] = relationships;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processMediaEntry(entry, model, filename) {
							 | 
						||
| 
								 | 
							
								    const lastDot = filename.lastIndexOf('.');
							 | 
						||
| 
								 | 
							
								    // if we can't determine extension, ignore it
							 | 
						||
| 
								 | 
							
								    if (lastDot >= 1) {
							 | 
						||
| 
								 | 
							
								      const extension = filename.substr(lastDot + 1);
							 | 
						||
| 
								 | 
							
								      const name = filename.substr(0, lastDot);
							 | 
						||
| 
								 | 
							
								      await new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								        const streamBuf = new StreamBuf();
							 | 
						||
| 
								 | 
							
								        streamBuf.on('finish', () => {
							 | 
						||
| 
								 | 
							
								          model.mediaIndex[filename] = model.media.length;
							 | 
						||
| 
								 | 
							
								          model.mediaIndex[name] = model.media.length;
							 | 
						||
| 
								 | 
							
								          const medium = {
							 | 
						||
| 
								 | 
							
								            type: 'image',
							 | 
						||
| 
								 | 
							
								            name,
							 | 
						||
| 
								 | 
							
								            extension,
							 | 
						||
| 
								 | 
							
								            buffer: streamBuf.toBuffer(),
							 | 
						||
| 
								 | 
							
								          };
							 | 
						||
| 
								 | 
							
								          model.media.push(medium);
							 | 
						||
| 
								 | 
							
								          resolve();
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        entry.on('error', error => {
							 | 
						||
| 
								 | 
							
								          reject(error);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        entry.pipe(streamBuf);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processDrawingEntry(entry, model, name) {
							 | 
						||
| 
								 | 
							
								    const xform = new DrawingXform();
							 | 
						||
| 
								 | 
							
								    const drawing = await xform.parseStream(entry);
							 | 
						||
| 
								 | 
							
								    model.drawings[name] = drawing;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processDrawingRelsEntry(entry, model, name) {
							 | 
						||
| 
								 | 
							
								    const xform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    const relationships = await xform.parseStream(entry);
							 | 
						||
| 
								 | 
							
								    model.drawingRels[name] = relationships;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processVmlDrawingEntry(entry, model, name) {
							 | 
						||
| 
								 | 
							
								    const xform = new VmlNotesXform();
							 | 
						||
| 
								 | 
							
								    const vmlDrawing = await xform.parseStream(entry);
							 | 
						||
| 
								 | 
							
								    model.vmlDrawings[`../drawings/${name}.vml`] = vmlDrawing;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async _processThemeEntry(entry, model, name) {
							 | 
						||
| 
								 | 
							
								    await new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								      // TODO: stream entry into buffer and store the xml in the model.themes[]
							 | 
						||
| 
								 | 
							
								      const stream = new StreamBuf();
							 | 
						||
| 
								 | 
							
								      entry.on('error', reject);
							 | 
						||
| 
								 | 
							
								      stream.on('error', reject);
							 | 
						||
| 
								 | 
							
								      stream.on('finish', () => {
							 | 
						||
| 
								 | 
							
								        model.themes[name] = stream.read().toString();
							 | 
						||
| 
								 | 
							
								        resolve();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      entry.pipe(stream);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @deprecated since version 4.0. You should use `#read` instead. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  createInputStream() {
							 | 
						||
| 
								 | 
							
								    throw new Error(
							 | 
						||
| 
								 | 
							
								      '`XLSX#createInputStream` is deprecated. You should use `XLSX#read` instead. This method will be removed in version 5.0. Please follow upgrade instruction: https://github.com/exceljs/exceljs/blob/master/UPGRADE-4.0.md'
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async read(stream, options) {
							 | 
						||
| 
								 | 
							
								    // TODO: Remove once node v8 is deprecated
							 | 
						||
| 
								 | 
							
								    // Detect and upgrade old streams
							 | 
						||
| 
								 | 
							
								    if (!stream[Symbol.asyncIterator] && stream.pipe) {
							 | 
						||
| 
								 | 
							
								      stream = stream.pipe(new PassThrough());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const chunks = [];
							 | 
						||
| 
								 | 
							
								    for await (const chunk of stream) {
							 | 
						||
| 
								 | 
							
								      chunks.push(chunk);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return this.load(Buffer.concat(chunks), options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async load(data, options) {
							 | 
						||
| 
								 | 
							
								    let buffer;
							 | 
						||
| 
								 | 
							
								    if (options && options.base64) {
							 | 
						||
| 
								 | 
							
								      buffer = Buffer.from(data.toString(), 'base64');
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      buffer = data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const model = {
							 | 
						||
| 
								 | 
							
								      worksheets: [],
							 | 
						||
| 
								 | 
							
								      worksheetHash: {},
							 | 
						||
| 
								 | 
							
								      worksheetRels: [],
							 | 
						||
| 
								 | 
							
								      themes: {},
							 | 
						||
| 
								 | 
							
								      media: [],
							 | 
						||
| 
								 | 
							
								      mediaIndex: {},
							 | 
						||
| 
								 | 
							
								      drawings: {},
							 | 
						||
| 
								 | 
							
								      drawingRels: {},
							 | 
						||
| 
								 | 
							
								      comments: {},
							 | 
						||
| 
								 | 
							
								      tables: {},
							 | 
						||
| 
								 | 
							
								      vmlDrawings: {},
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const zip = await JSZip.loadAsync(buffer);
							 | 
						||
| 
								 | 
							
								    for (const entry of Object.values(zip.files)) {
							 | 
						||
| 
								 | 
							
								      /* eslint-disable no-await-in-loop */
							 | 
						||
| 
								 | 
							
								      if (!entry.dir) {
							 | 
						||
| 
								 | 
							
								        let entryName = entry.name;
							 | 
						||
| 
								 | 
							
								        if (entryName[0] === '/') {
							 | 
						||
| 
								 | 
							
								          entryName = entryName.substr(1);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        let stream;
							 | 
						||
| 
								 | 
							
								        if (entryName.match(/xl\/media\//) ||
							 | 
						||
| 
								 | 
							
								          // themes are not parsed as stream
							 | 
						||
| 
								 | 
							
								          entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/)) {
							 | 
						||
| 
								 | 
							
								          stream = new PassThrough();
							 | 
						||
| 
								 | 
							
								          stream.write(await entry.async('nodebuffer'));
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          // use object mode to avoid buffer-string convention
							 | 
						||
| 
								 | 
							
								          stream = new PassThrough({
							 | 
						||
| 
								 | 
							
								            writableObjectMode: true,
							 | 
						||
| 
								 | 
							
								            readableObjectMode: true,
							 | 
						||
| 
								 | 
							
								          });
							 | 
						||
| 
								 | 
							
								          let content;
							 | 
						||
| 
								 | 
							
								          // https://www.npmjs.com/package/process
							 | 
						||
| 
								 | 
							
								          if (process.browser) {
							 | 
						||
| 
								 | 
							
								            // running in browser, use TextDecoder if possible
							 | 
						||
| 
								 | 
							
								            content = bufferToString(await entry.async('nodebuffer'));
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            // running in node.js
							 | 
						||
| 
								 | 
							
								            content = await entry.async('string');
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          const chunkSize = 16 * 1024;
							 | 
						||
| 
								 | 
							
								          for (let i = 0; i < content.length; i += chunkSize) {
							 | 
						||
| 
								 | 
							
								            stream.write(content.substring(i, i + chunkSize));
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        stream.end();
							 | 
						||
| 
								 | 
							
								        switch (entryName) {
							 | 
						||
| 
								 | 
							
								          case '_rels/.rels':
							 | 
						||
| 
								 | 
							
								            model.globalRels = await this.parseRels(stream);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'xl/workbook.xml': {
							 | 
						||
| 
								 | 
							
								            const workbook = await this.parseWorkbook(stream);
							 | 
						||
| 
								 | 
							
								            model.sheets = workbook.sheets;
							 | 
						||
| 
								 | 
							
								            model.definedNames = workbook.definedNames;
							 | 
						||
| 
								 | 
							
								            model.views = workbook.views;
							 | 
						||
| 
								 | 
							
								            model.properties = workbook.properties;
							 | 
						||
| 
								 | 
							
								            model.calcProperties = workbook.calcProperties;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'xl/_rels/workbook.xml.rels':
							 | 
						||
| 
								 | 
							
								            model.workbookRels = await this.parseRels(stream);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'xl/sharedStrings.xml':
							 | 
						||
| 
								 | 
							
								            model.sharedStrings = new SharedStringsXform();
							 | 
						||
| 
								 | 
							
								            await model.sharedStrings.parseStream(stream);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'xl/styles.xml':
							 | 
						||
| 
								 | 
							
								            model.styles = new StylesXform();
							 | 
						||
| 
								 | 
							
								            await model.styles.parseStream(stream);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'docProps/app.xml': {
							 | 
						||
| 
								 | 
							
								            const appXform = new AppXform();
							 | 
						||
| 
								 | 
							
								            const appProperties = await appXform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								            model.company = appProperties.company;
							 | 
						||
| 
								 | 
							
								            model.manager = appProperties.manager;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          case 'docProps/core.xml': {
							 | 
						||
| 
								 | 
							
								            const coreXform = new CoreXform();
							 | 
						||
| 
								 | 
							
								            const coreProperties = await coreXform.parseStream(stream);
							 | 
						||
| 
								 | 
							
								            Object.assign(model, coreProperties);
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          default: {
							 | 
						||
| 
								 | 
							
								            let match = entryName.match(/xl\/worksheets\/sheet(\d+)[.]xml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processWorksheetEntry(stream, model, match[1], options, entryName);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/worksheets\/_rels\/sheet(\d+)[.]xml.rels/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processWorksheetRelsEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processThemeEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/media\/([a-zA-Z0-9]+[.][a-zA-Z0-9]{3,4})$/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processMediaEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/drawings\/([a-zA-Z0-9]+)[.]xml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processDrawingEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/(comments\d+)[.]xml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processCommentEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/tables\/(table\d+)[.]xml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processTableEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/drawings\/_rels\/([a-zA-Z0-9]+)[.]xml[.]rels/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processDrawingRelsEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            match = entryName.match(/xl\/drawings\/(vmlDrawing\d+)[.]vml/);
							 | 
						||
| 
								 | 
							
								            if (match) {
							 | 
						||
| 
								 | 
							
								              await this._processVmlDrawingEntry(stream, model, match[1]);
							 | 
						||
| 
								 | 
							
								              break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.reconcile(model, options);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // apply model
							 | 
						||
| 
								 | 
							
								    this.workbook.model = model;
							 | 
						||
| 
								 | 
							
								    return this.workbook;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // =========================================================================
							 | 
						||
| 
								 | 
							
								  // Write
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addMedia(zip, model) {
							 | 
						||
| 
								 | 
							
								    await Promise.all(
							 | 
						||
| 
								 | 
							
								      model.media.map(async medium => {
							 | 
						||
| 
								 | 
							
								        if (medium.type === 'image') {
							 | 
						||
| 
								 | 
							
								          const filename = `xl/media/${medium.name}.${medium.extension}`;
							 | 
						||
| 
								 | 
							
								          if (medium.filename) {
							 | 
						||
| 
								 | 
							
								            const data = await fsReadFileAsync(medium.filename);
							 | 
						||
| 
								 | 
							
								            return zip.append(data, {name: filename});
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if (medium.buffer) {
							 | 
						||
| 
								 | 
							
								            return zip.append(medium.buffer, {name: filename});
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if (medium.base64) {
							 | 
						||
| 
								 | 
							
								            const dataimg64 = medium.base64;
							 | 
						||
| 
								 | 
							
								            const content = dataimg64.substring(dataimg64.indexOf(',') + 1);
							 | 
						||
| 
								 | 
							
								            return zip.append(content, {name: filename, base64: true});
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        throw new Error('Unsupported media');
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  addDrawings(zip, model) {
							 | 
						||
| 
								 | 
							
								    const drawingXform = new DrawingXform();
							 | 
						||
| 
								 | 
							
								    const relsXform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      const {drawing} = worksheet;
							 | 
						||
| 
								 | 
							
								      if (drawing) {
							 | 
						||
| 
								 | 
							
								        drawingXform.prepare(drawing, {});
							 | 
						||
| 
								 | 
							
								        let xml = drawingXform.toXml(drawing);
							 | 
						||
| 
								 | 
							
								        zip.append(xml, {name: `xl/drawings/${drawing.name}.xml`});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        xml = relsXform.toXml(drawing.rels);
							 | 
						||
| 
								 | 
							
								        zip.append(xml, {name: `xl/drawings/_rels/${drawing.name}.xml.rels`});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  addTables(zip, model) {
							 | 
						||
| 
								 | 
							
								    const tableXform = new TableXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      const {tables} = worksheet;
							 | 
						||
| 
								 | 
							
								      tables.forEach(table => {
							 | 
						||
| 
								 | 
							
								        tableXform.prepare(table, {});
							 | 
						||
| 
								 | 
							
								        const tableXml = tableXform.toXml(table);
							 | 
						||
| 
								 | 
							
								        zip.append(tableXml, {name: `xl/tables/${table.target}`});
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addContentTypes(zip, model) {
							 | 
						||
| 
								 | 
							
								    const xform = new ContentTypesXform();
							 | 
						||
| 
								 | 
							
								    const xml = xform.toXml(model);
							 | 
						||
| 
								 | 
							
								    zip.append(xml, {name: '[Content_Types].xml'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addApp(zip, model) {
							 | 
						||
| 
								 | 
							
								    const xform = new AppXform();
							 | 
						||
| 
								 | 
							
								    const xml = xform.toXml(model);
							 | 
						||
| 
								 | 
							
								    zip.append(xml, {name: 'docProps/app.xml'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addCore(zip, model) {
							 | 
						||
| 
								 | 
							
								    const coreXform = new CoreXform();
							 | 
						||
| 
								 | 
							
								    zip.append(coreXform.toXml(model), {name: 'docProps/core.xml'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addThemes(zip, model) {
							 | 
						||
| 
								 | 
							
								    const themes = model.themes || {theme1: theme1Xml};
							 | 
						||
| 
								 | 
							
								    Object.keys(themes).forEach(name => {
							 | 
						||
| 
								 | 
							
								      const xml = themes[name];
							 | 
						||
| 
								 | 
							
								      const path = `xl/theme/${name}.xml`;
							 | 
						||
| 
								 | 
							
								      zip.append(xml, {name: path});
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addOfficeRels(zip) {
							 | 
						||
| 
								 | 
							
								    const xform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    const xml = xform.toXml([
							 | 
						||
| 
								 | 
							
								      {Id: 'rId1', Type: XLSX.RelType.OfficeDocument, Target: 'xl/workbook.xml'},
							 | 
						||
| 
								 | 
							
								      {Id: 'rId2', Type: XLSX.RelType.CoreProperties, Target: 'docProps/core.xml'},
							 | 
						||
| 
								 | 
							
								      {Id: 'rId3', Type: XLSX.RelType.ExtenderProperties, Target: 'docProps/app.xml'},
							 | 
						||
| 
								 | 
							
								    ]);
							 | 
						||
| 
								 | 
							
								    zip.append(xml, {name: '_rels/.rels'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addWorkbookRels(zip, model) {
							 | 
						||
| 
								 | 
							
								    let count = 1;
							 | 
						||
| 
								 | 
							
								    const relationships = [
							 | 
						||
| 
								 | 
							
								      {Id: `rId${count++}`, Type: XLSX.RelType.Styles, Target: 'styles.xml'},
							 | 
						||
| 
								 | 
							
								      {Id: `rId${count++}`, Type: XLSX.RelType.Theme, Target: 'theme/theme1.xml'},
							 | 
						||
| 
								 | 
							
								    ];
							 | 
						||
| 
								 | 
							
								    if (model.sharedStrings.count) {
							 | 
						||
| 
								 | 
							
								      relationships.push({
							 | 
						||
| 
								 | 
							
								        Id: `rId${count++}`,
							 | 
						||
| 
								 | 
							
								        Type: XLSX.RelType.SharedStrings,
							 | 
						||
| 
								 | 
							
								        Target: 'sharedStrings.xml',
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      worksheet.rId = `rId${count++}`;
							 | 
						||
| 
								 | 
							
								      relationships.push({
							 | 
						||
| 
								 | 
							
								        Id: worksheet.rId,
							 | 
						||
| 
								 | 
							
								        Type: XLSX.RelType.Worksheet,
							 | 
						||
| 
								 | 
							
								        Target: `worksheets/sheet${worksheet.id}.xml`,
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    const xform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    const xml = xform.toXml(relationships);
							 | 
						||
| 
								 | 
							
								    zip.append(xml, {name: 'xl/_rels/workbook.xml.rels'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addSharedStrings(zip, model) {
							 | 
						||
| 
								 | 
							
								    if (model.sharedStrings && model.sharedStrings.count) {
							 | 
						||
| 
								 | 
							
								      zip.append(model.sharedStrings.xml, {name: 'xl/sharedStrings.xml'});
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addStyles(zip, model) {
							 | 
						||
| 
								 | 
							
								    const {xml} = model.styles;
							 | 
						||
| 
								 | 
							
								    if (xml) {
							 | 
						||
| 
								 | 
							
								      zip.append(xml, {name: 'xl/styles.xml'});
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addWorkbook(zip, model) {
							 | 
						||
| 
								 | 
							
								    const xform = new WorkbookXform();
							 | 
						||
| 
								 | 
							
								    zip.append(xform.toXml(model), {name: 'xl/workbook.xml'});
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async addWorksheets(zip, model) {
							 | 
						||
| 
								 | 
							
								    // preparation phase
							 | 
						||
| 
								 | 
							
								    const worksheetXform = new WorksheetXform();
							 | 
						||
| 
								 | 
							
								    const relationshipsXform = new RelationshipsXform();
							 | 
						||
| 
								 | 
							
								    const commentsXform = new CommentsXform();
							 | 
						||
| 
								 | 
							
								    const vmlNotesXform = new VmlNotesXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // write sheets
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      let xmlStream = new XmlStream();
							 | 
						||
| 
								 | 
							
								      worksheetXform.render(xmlStream, worksheet);
							 | 
						||
| 
								 | 
							
								      zip.append(xmlStream.xml, {name: `xl/worksheets/sheet${worksheet.id}.xml`});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (worksheet.rels && worksheet.rels.length) {
							 | 
						||
| 
								 | 
							
								        xmlStream = new XmlStream();
							 | 
						||
| 
								 | 
							
								        relationshipsXform.render(xmlStream, worksheet.rels);
							 | 
						||
| 
								 | 
							
								        zip.append(xmlStream.xml, {name: `xl/worksheets/_rels/sheet${worksheet.id}.xml.rels`});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (worksheet.comments.length > 0) {
							 | 
						||
| 
								 | 
							
								        xmlStream = new XmlStream();
							 | 
						||
| 
								 | 
							
								        commentsXform.render(xmlStream, worksheet);
							 | 
						||
| 
								 | 
							
								        zip.append(xmlStream.xml, {name: `xl/comments${worksheet.id}.xml`});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        xmlStream = new XmlStream();
							 | 
						||
| 
								 | 
							
								        vmlNotesXform.render(xmlStream, worksheet);
							 | 
						||
| 
								 | 
							
								        zip.append(xmlStream.xml, {name: `xl/drawings/vmlDrawing${worksheet.id}.vml`});
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _finalize(zip) {
							 | 
						||
| 
								 | 
							
								    return new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								      zip.on('finish', () => {
							 | 
						||
| 
								 | 
							
								        resolve(this);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      zip.on('error', reject);
							 | 
						||
| 
								 | 
							
								      zip.finalize();
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  prepareModel(model, options) {
							 | 
						||
| 
								 | 
							
								    // ensure following properties have sane values
							 | 
						||
| 
								 | 
							
								    model.creator = model.creator || 'ExcelJS';
							 | 
						||
| 
								 | 
							
								    model.lastModifiedBy = model.lastModifiedBy || 'ExcelJS';
							 | 
						||
| 
								 | 
							
								    model.created = model.created || new Date();
							 | 
						||
| 
								 | 
							
								    model.modified = model.modified || new Date();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    model.useSharedStrings =
							 | 
						||
| 
								 | 
							
								      options.useSharedStrings !== undefined ? options.useSharedStrings : true;
							 | 
						||
| 
								 | 
							
								    model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // Manage the shared strings
							 | 
						||
| 
								 | 
							
								    model.sharedStrings = new SharedStringsXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // add a style manager to handle cell formats, fonts, etc.
							 | 
						||
| 
								 | 
							
								    model.styles = model.useStyles ? new StylesXform(true) : new StylesXform.Mock();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // prepare all of the things before the render
							 | 
						||
| 
								 | 
							
								    const workbookXform = new WorkbookXform();
							 | 
						||
| 
								 | 
							
								    const worksheetXform = new WorksheetXform();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    workbookXform.prepare(model);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const worksheetOptions = {
							 | 
						||
| 
								 | 
							
								      sharedStrings: model.sharedStrings,
							 | 
						||
| 
								 | 
							
								      styles: model.styles,
							 | 
						||
| 
								 | 
							
								      date1904: model.properties.date1904,
							 | 
						||
| 
								 | 
							
								      drawingsCount: 0,
							 | 
						||
| 
								 | 
							
								      media: model.media,
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    worksheetOptions.drawings = model.drawings = [];
							 | 
						||
| 
								 | 
							
								    worksheetOptions.commentRefs = model.commentRefs = [];
							 | 
						||
| 
								 | 
							
								    let tableCount = 0;
							 | 
						||
| 
								 | 
							
								    model.tables = [];
							 | 
						||
| 
								 | 
							
								    model.worksheets.forEach(worksheet => {
							 | 
						||
| 
								 | 
							
								      // assign unique filenames to tables
							 | 
						||
| 
								 | 
							
								      worksheet.tables.forEach(table => {
							 | 
						||
| 
								 | 
							
								        tableCount++;
							 | 
						||
| 
								 | 
							
								        table.target = `table${tableCount}.xml`;
							 | 
						||
| 
								 | 
							
								        table.id = tableCount;
							 | 
						||
| 
								 | 
							
								        model.tables.push(table);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      worksheetXform.prepare(worksheet, worksheetOptions);
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // TODO: workbook drawing list
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async write(stream, options) {
							 | 
						||
| 
								 | 
							
								    options = options || {};
							 | 
						||
| 
								 | 
							
								    const {model} = this.workbook;
							 | 
						||
| 
								 | 
							
								    const zip = new ZipStream.ZipWriter(options.zip);
							 | 
						||
| 
								 | 
							
								    zip.pipe(stream);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.prepareModel(model, options);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // render
							 | 
						||
| 
								 | 
							
								    await this.addContentTypes(zip, model);
							 | 
						||
| 
								 | 
							
								    await this.addOfficeRels(zip, model);
							 | 
						||
| 
								 | 
							
								    await this.addWorkbookRels(zip, model);
							 | 
						||
| 
								 | 
							
								    await this.addWorksheets(zip, model);
							 | 
						||
| 
								 | 
							
								    await this.addSharedStrings(zip, model); // always after worksheets
							 | 
						||
| 
								 | 
							
								    await this.addDrawings(zip, model);
							 | 
						||
| 
								 | 
							
								    await this.addTables(zip, model);
							 | 
						||
| 
								 | 
							
								    await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
							 | 
						||
| 
								 | 
							
								    await this.addMedia(zip, model);
							 | 
						||
| 
								 | 
							
								    await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
							 | 
						||
| 
								 | 
							
								    await this.addWorkbook(zip, model);
							 | 
						||
| 
								 | 
							
								    return this._finalize(zip);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  writeFile(filename, options) {
							 | 
						||
| 
								 | 
							
								    const stream = fs.createWriteStream(filename);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								      stream.on('finish', () => {
							 | 
						||
| 
								 | 
							
								        resolve();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      stream.on('error', error => {
							 | 
						||
| 
								 | 
							
								        reject(error);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      this.write(stream, options).then(() => {
							 | 
						||
| 
								 | 
							
								        stream.end();
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  async writeBuffer(options) {
							 | 
						||
| 
								 | 
							
								    const stream = new StreamBuf();
							 | 
						||
| 
								 | 
							
								    await this.write(stream, options);
							 | 
						||
| 
								 | 
							
								    return stream.read();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								XLSX.RelType = require('./rel-type');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = XLSX;
							 |