Skip to content

Commit 41f24b7

Browse files
authored
Merge pull request #460 from cidgoh/csv-save-fix
Csv save fix
2 parents 1dd4766 + 3fda8d8 commit 41f24b7

File tree

3 files changed

+112
-91
lines changed

3 files changed

+112
-91
lines changed

lib/Toolbar.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ class Toolbar {
313313

314314
updateTemplateOptions() {
315315
this.$selectTemplate.empty();
316-
for (const [schema_name, schema_obj] of Object.entries(this.menu)) {
316+
//for (const [schema_name, schema_obj] of Object.entries(this.menu)) {
317+
for (const schema_obj of Object.values(this.menu)) {
317318
const templates = schema_obj['templates'];
318319
for (const [template_name, template_obj] of Object.entries(templates)) {
319320
let path = schema_obj.folder + '/' + template_name;

lib/utils/files.js

+108-88
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { utils as XlsxUtils, writeFile } from 'xlsx/xlsx.js';
1+
import { utils as XlsxUtils, writeFile} from 'xlsx/xlsx.js';
22
import { saveAs } from 'file-saver';
33

44
/**
@@ -120,6 +120,7 @@ export function createWorkbookFromJSON(jsonData) {
120120
return workbook;
121121
}
122122

123+
/*
123124
function writeWorkbook(workbook, baseName, ext, opt = {}) {
124125
if (ext === 'xlsx' || ext === 'xls') {
125126
// can support multiple sheets in one file
@@ -144,122 +145,141 @@ function writeWorkbook(workbook, baseName, ext, opt = {}) {
144145
});
145146
}
146147
}
148+
*/
147149

148150
/**
149151
* Download matrix to file.
150152
* Note that BOM and UTF-8 can create problems on some systems when importing
151153
* file. See "Supported Output Formats" and "UTF-16 Unicode Text" sections of
152-
* https://reactian.com/sheetjs-community-edition-spreadsheet-data-toolkit/
153154
* and https://github.com/SheetJS/sheetjs
154155
* Solution at bottom of: https://github.com/SheetJS/sheetjs/issues/943
155156
* The "Comma Separated Values" format is actually UTF-8 with BOM prefix.
157+
* A “U+” designation refers to a character, and in this case that would be
158+
* U+FEFF; the start of the file is the three-byte sequence EF BB BF which is
159+
* how U+FEFF is represented in UTF-8.
156160
* @param {Workbook} workbook workboox to download.
157161
* @param {String} baseName Basename of downloaded file.
158162
* @param {String} ext Extension of downloaded file.
159163
*/
160164
export function exportWorkbook(workbook, baseName, ext) {
161-
switch (ext) {
162-
case 'xlsx':
163-
case 'xls':
164-
writeWorkbook(workbook, baseName, ext);
165-
break;
166-
case 'csv':
167-
processAndSave(
168-
workbook,
169-
baseName,
170-
'csv',
171-
',',
172-
'text/plain;charset=UTF-8'
173-
);
174-
break;
175-
176-
case 'csv (UTF-16)':
177-
processAndSave(
178-
workbook,
179-
baseName,
180-
'csv',
181-
',',
182-
'text/plain;charset=UTF-16LE'
183-
);
184-
break;
185-
186-
case 'tsv':
187-
processAndSave(
188-
workbook,
189-
baseName,
190-
'tsv',
191-
'\t',
192-
'text/plain;charset=UTF-8'
193-
);
194-
break;
195-
196-
case 'tsv (UTF-16)':
197-
processAndSave(
198-
workbook,
199-
baseName,
200-
'tsv',
201-
'\t',
202-
'text/plain;charset=UTF-16LE'
203-
);
204-
break;
205-
206-
case 'csv (UTF-8, no BOM)':
207-
processAndSave(
208-
workbook,
209-
baseName,
210-
'csv',
211-
',',
212-
'text/plain;charset=UTF-8',
213-
false
214-
);
215-
break;
216-
217-
case 'csv (ASCII)':
218-
processAndSave(
219-
workbook,
220-
baseName,
221-
'csv',
222-
',',
223-
'text/plain;charset=us-ascii',
224-
false
225-
);
226-
break;
227-
}
228-
}
229165

230-
function processAndSave(
231-
workbook,
232-
baseName,
233-
ext,
234-
delimiter,
235-
mimeType,
236-
includeBOM = true
237-
) {
166+
// Often just one sheet, but if multiple, then each gets file name + _ + template (class) name
238167
const sheets = workbook.SheetNames;
239168
sheets.forEach((sheetName) => {
240-
let data = '';
241-
242169
const worksheet = workbook.Sheets[sheetName];
170+
const fileName = `${baseName}${sheets.length > 1 ? `_${sheetName}` : ''}.${ext.split(' ')[0]}`;
171+
var data = '';
172+
switch (ext) {
173+
case 'xlsx':
174+
case 'xls':
175+
// Note, mimeType always set to application/zip in these cases.
176+
writeFile(workbook, `${baseName}.${ext}`); //, opt
177+
break;
178+
179+
/* Notes:
180+
See
181+
- https://docs.sheetjs.com/docs/api/write-options/
182+
- https://docs.sheetjs.com/docs/api/utilities/csv#csv-output
183+
saveBlob() enables more accurate mimeTypes?
184+
* writeFile(bookType: 'csv'...) output includes the UTF-8 byte order
185+
* mark ("BOM").
186+
* sheet_to_csv() will return JavaScript strings without the UTF-8 BOM.
187+
188+
*/
189+
190+
/* Phasing this out. UTF-8 doesn't need a BOM
191+
case 'csv': // UTF-8
192+
// writeFile(workbook, fileName, {bookType: 'csv', FS: ','});
193+
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
194+
data = '\uFEFF' + data; //BOM
195+
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
196+
break;
197+
*/
198+
199+
/* This case won't work until we convert data to UTF-16
200+
case 'csv (UTF-16)':
201+
//writeFile(workbook, fileName, {bookType: 'txt', FS: ','});
202+
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
203+
data = '\uFEFF' + data; //BOM
204+
saveBlob(data, fileName, 'text/plain;charset=UTF-16LE');
205+
break;
206+
*/
207+
208+
case 'csv':
209+
case 'csv (UTF-8, no BOM)':
210+
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
211+
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
212+
break;
213+
214+
/* This case won't work until we convert data to ASCII
215+
case 'csv (ASCII)': // no BOM
216+
data = XlsxUtils.sheet_to_csv(worksheet, {FS: ','});
217+
saveBlob(data, fileName, 'text/plain;charset=us-ascii');
218+
break;
219+
*/
220+
221+
/*
222+
* https://stackoverflow.com/questions/8336355/what-exactly-is-unicode-codepage-1200
223+
* sheet_to_txt(): sheetjs notes: "If encoding support is available, the
224+
* output will be encoded in CP1200 and the UTF-16 BOM will be added. If
225+
* encoding support is not available, the output will be encoded as a
226+
* standard string." In DH tests it seems "encoding support" is not
227+
* available, and resulting file is UTF-8 +BOM anyways.
228+
*/
229+
case 'tsv': // UTF-8 BOM version
230+
// SheetJS note: For compatibility with Excel, writeFile() csv output
231+
// will always include the UTF-8 byte order mark ("BOM").
232+
//writeFile(workbook, fileName, {bookType: 'csv', FS: '\t'});
233+
data = XlsxUtils.sheet_to_csv(worksheet, {FS: '\t'});
234+
//data = '\uFEFF' + data; //BOM
235+
saveBlob(data, fileName, 'text/plain;charset=UTF-8');
236+
break;
237+
238+
/* Not working, produces hexidecimal file - is charset="UTF-16LE" recognized?
239+
* See Table 2-4: unicode.org/versions/Unicode6.0.0/ch02.pdf"
240+
* UTF-16 little endian, aka code page 1200, is not permitted to have a BOM,
241+
* according to the Unicode standard.
242+
* DATA NEEDS TO BE CONVERTED TO UTF-16
243+
*
244+
case 'tsv (UTF-16)': // no BOM
245+
// See: https://localizely.com/character-encodings/utf16le/
246+
//writeFile(workbook, fileName, {bookType: 'tsv', FS: '\t'});
247+
data = XlsxUtils.sheet_to_txt(worksheet, {FS: '\t'});
248+
saveBlob(data, fileName, 'text/plain;charset=UTF-16LE');
249+
break;
250+
*/
251+
}
252+
})
253+
};
254+
255+
// Saves workbook which may have multiple sheets into one or more files.
256+
// ext: csv, csv (UTF-16), tsv, tsv (UTF-16)
257+
// ext no BOM: csv (UTF-8, no BOM), csv (ASCII)
258+
// This script can enhance file type with mimeType - but is that something sheetJS can't do?
259+
function saveBlob(
260+
data,
261+
fileName,
262+
mimeType
263+
) {
264+
265+
/*
243266
const sheetData = XlsxUtils.sheet_to_json(worksheet, { header: 1 });
244267
245268
const formattedData = sheetData
246269
.map((row) => row.join(delimiter))
247270
.join('\n');
248271
data += formattedData + '\n';
249272
273+
// Insert BOM character.
250274
if (includeBOM && mimeType.includes('UTF-8')) {
251275
data = '\uFEFF' + data;
252276
}
277+
*/
253278

279+
// Enhancing with mimeType
254280
const blob = new Blob([data], { type: mimeType });
255-
saveAs(
256-
blob,
257-
`${baseName}${sheets.length > 1 ? `_${sheetName}` : ''}.${
258-
ext.split(' ')[0]
259-
}`
260-
);
261-
});
262-
}
281+
saveAs(blob, fileName);
282+
};
263283

264284
// TODO: refactor to export matrix
265285
export function exportFile(matrix, baseName, ext) {

lib/utils/templates.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ const schemaFromChildPath = (childPath) =>
2828

2929
// Returns default template built from schema
3030
async function compileSchema(schema_folder) { // e.g. canada_covid19
31-
32-
for (const [schema_name, schema_obj] of Object.entries(menu)) {
31+
//for (const [schema_name, schema_obj] of Object.entries(menu)) {
32+
for (const schema_obj of Object.values(menu)) {
3333
if (schema_obj.folder === schema_folder) {
3434
var schema = await fetchSchema(`/templates/${schema_folder}/schema.json`);
3535
const template = {

0 commit comments

Comments
 (0)