js.documents 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -29
- package/dist/codecs/registry.cjs +14 -0
- package/dist/codecs/registry.js +14 -0
- package/dist/convert/capability.cjs +10 -0
- package/dist/convert/capability.js +10 -0
- package/dist/convert/codec.cjs +30 -0
- package/dist/convert/codec.d.cts +7 -1
- package/dist/convert/codec.d.ts +7 -1
- package/dist/convert/codec.js +27 -3
- package/dist/convert/composition.cjs +49 -7
- package/dist/convert/composition.d.cts +12 -5
- package/dist/convert/composition.d.ts +12 -5
- package/dist/convert/composition.js +49 -7
- package/dist/convert/convert.cjs +48 -0
- package/dist/convert/convert.d.cts +30 -1
- package/dist/convert/convert.d.ts +30 -1
- package/dist/convert/convert.js +37 -1
- package/dist/convert/local.cjs +4 -1
- package/dist/convert/local.js +4 -1
- package/dist/convert/port.cjs +2 -0
- package/dist/convert/port.d.cts +5 -0
- package/dist/convert/port.d.ts +5 -0
- package/dist/convert/port.js +2 -0
- package/dist/csv/read.cjs +85 -0
- package/dist/csv/read.d.cts +10 -0
- package/dist/csv/read.d.ts +10 -0
- package/dist/csv/read.js +84 -0
- package/dist/csv/records.cjs +85 -0
- package/dist/csv/records.d.cts +10 -0
- package/dist/csv/records.d.ts +10 -0
- package/dist/csv/records.js +80 -0
- package/dist/csv/text.cjs +22 -0
- package/dist/csv/text.d.cts +8 -0
- package/dist/csv/text.d.ts +8 -0
- package/dist/csv/text.js +19 -0
- package/dist/csv/write.cjs +75 -0
- package/dist/csv/write.d.cts +22 -0
- package/dist/csv/write.d.ts +22 -0
- package/dist/csv/write.js +71 -0
- package/dist/edit/odg/scaffold.js +8 -8
- package/dist/edit/odp/scaffold.js +8 -8
- package/dist/edit/ods/scaffold.js +8 -8
- package/dist/edit/odt/scaffold.js +8 -8
- package/dist/index.cjs +48 -1
- package/dist/index.d.cts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +13 -5
- package/dist/layout/reconstruct.cjs +1 -1
- package/dist/layout/reconstruct.js +1 -1
- package/dist/metadata/write.cjs +2 -0
- package/dist/metadata/write.js +2 -0
- package/dist/model/bytes.cjs +7 -0
- package/dist/model/bytes.d.cts +3 -1
- package/dist/model/bytes.d.ts +3 -1
- package/dist/model/bytes.js +6 -1
- package/dist/odb/csv.cjs +3 -6
- package/dist/odb/csv.js +3 -6
- package/dist/svg/diagnostics.cjs +19 -0
- package/dist/svg/diagnostics.d.cts +10 -0
- package/dist/svg/diagnostics.d.ts +10 -0
- package/dist/svg/diagnostics.js +18 -0
- package/dist/svg/paint.cjs +817 -0
- package/dist/svg/paint.d.cts +19 -0
- package/dist/svg/paint.d.ts +19 -0
- package/dist/svg/paint.js +814 -0
- package/dist/svg/path.cjs +337 -0
- package/dist/svg/path.d.cts +22 -0
- package/dist/svg/path.d.ts +22 -0
- package/dist/svg/path.js +336 -0
- package/dist/svg/read.cjs +677 -0
- package/dist/svg/read.d.cts +12 -0
- package/dist/svg/read.d.ts +12 -0
- package/dist/svg/read.js +675 -0
- package/dist/svg/text.cjs +22 -0
- package/dist/svg/text.d.cts +8 -0
- package/dist/svg/text.d.ts +8 -0
- package/dist/svg/text.js +19 -0
- package/dist/svg/transform.cjs +206 -0
- package/dist/svg/transform.d.cts +23 -0
- package/dist/svg/transform.d.ts +23 -0
- package/dist/svg/transform.js +197 -0
- package/dist/svg/units.cjs +47 -0
- package/dist/svg/units.d.cts +12 -0
- package/dist/svg/units.d.ts +12 -0
- package/dist/svg/units.js +44 -0
- package/dist/svg/write.cjs +127 -0
- package/dist/svg/write.d.cts +23 -0
- package/dist/svg/write.d.ts +23 -0
- package/dist/svg/write.js +123 -0
- package/dist/xml/odf-text.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_layout_cell_typing = require("../layout/cell-typing.cjs");
|
|
3
|
+
const require_csv_records = require("./records.cjs");
|
|
4
|
+
let document_schema_js = require("document-schema.js");
|
|
5
|
+
//#region src/csv/read.ts
|
|
6
|
+
const HEADER_ROW_INDEX = 0;
|
|
7
|
+
const SHEET_NAME = "Sheet1";
|
|
8
|
+
const COLUMN_WIDTH_PT = 64;
|
|
9
|
+
const ROW_HEIGHT_PT = 15;
|
|
10
|
+
const MARGIN_PT = 56.69291338582677;
|
|
11
|
+
const DEFAULT_PRINT_SETTINGS = {
|
|
12
|
+
pageSize: document_schema_js.PAGE_SIZE_A4,
|
|
13
|
+
margins: {
|
|
14
|
+
topPt: MARGIN_PT,
|
|
15
|
+
rightPt: MARGIN_PT,
|
|
16
|
+
bottomPt: MARGIN_PT,
|
|
17
|
+
leftPt: MARGIN_PT
|
|
18
|
+
},
|
|
19
|
+
gridlines: true,
|
|
20
|
+
headers: true,
|
|
21
|
+
pageOrder: "downThenOver"
|
|
22
|
+
};
|
|
23
|
+
function dataCell(rowIndex, columnIndex, field, onCellTypeInference) {
|
|
24
|
+
const inference = field === "" ? void 0 : require_layout_cell_typing.inferCellValue(field);
|
|
25
|
+
if (inference !== void 0) onCellTypeInference?.({
|
|
26
|
+
sheetIndex: 0,
|
|
27
|
+
row: rowIndex,
|
|
28
|
+
column: columnIndex,
|
|
29
|
+
displayText: field,
|
|
30
|
+
...inference
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
row: rowIndex,
|
|
34
|
+
column: columnIndex,
|
|
35
|
+
value: field === "" ? { kind: "empty" } : inference?.outcome === "retyped" ? inference.value : {
|
|
36
|
+
kind: "string",
|
|
37
|
+
value: field
|
|
38
|
+
},
|
|
39
|
+
displayText: field
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function readCsvContent(text, options) {
|
|
43
|
+
const records = require_csv_records.parseCsvRecords(text, options?.delimiter ?? ",");
|
|
44
|
+
const onCellTypeInference = options?.onCellTypeInference;
|
|
45
|
+
const columnCount = records.reduce((max, record) => Math.max(max, record.length), 0);
|
|
46
|
+
const cells = [];
|
|
47
|
+
records.forEach((record, rowIndex) => {
|
|
48
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
|
49
|
+
const field = record[columnIndex] ?? "";
|
|
50
|
+
if (rowIndex === HEADER_ROW_INDEX) cells.push({
|
|
51
|
+
row: rowIndex,
|
|
52
|
+
column: columnIndex,
|
|
53
|
+
value: {
|
|
54
|
+
kind: "string",
|
|
55
|
+
value: field
|
|
56
|
+
},
|
|
57
|
+
displayText: field
|
|
58
|
+
});
|
|
59
|
+
else cells.push(dataCell(rowIndex, columnIndex, field, onCellTypeInference));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const columns = Array.from({ length: columnCount }, (_unused, index) => ({
|
|
63
|
+
index,
|
|
64
|
+
widthPt: COLUMN_WIDTH_PT
|
|
65
|
+
}));
|
|
66
|
+
const rows = Array.from({ length: records.length }, (_unused, index) => ({
|
|
67
|
+
index,
|
|
68
|
+
heightPt: ROW_HEIGHT_PT
|
|
69
|
+
}));
|
|
70
|
+
return {
|
|
71
|
+
kind: "spreadsheet",
|
|
72
|
+
formatVersion: document_schema_js.CONTENT_FORMAT_VERSION,
|
|
73
|
+
metadata: {},
|
|
74
|
+
sheets: [{
|
|
75
|
+
name: SHEET_NAME,
|
|
76
|
+
cells,
|
|
77
|
+
columns,
|
|
78
|
+
rows,
|
|
79
|
+
images: [],
|
|
80
|
+
printSettings: DEFAULT_PRINT_SETTINGS
|
|
81
|
+
}]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
exports.readCsvContent = readCsvContent;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CellTypeInferenceSink } from "../layout/cell-typing.cjs";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/csv/read.d.ts
|
|
4
|
+
interface ReadCsvContentOptions {
|
|
5
|
+
readonly delimiter?: string;
|
|
6
|
+
readonly onCellTypeInference?: CellTypeInferenceSink;
|
|
7
|
+
}
|
|
8
|
+
declare function readCsvContent(text: string, options?: ReadCsvContentOptions): ContentDocument;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { ReadCsvContentOptions, readCsvContent };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CellTypeInferenceSink } from "../layout/cell-typing.js";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/csv/read.d.ts
|
|
4
|
+
interface ReadCsvContentOptions {
|
|
5
|
+
readonly delimiter?: string;
|
|
6
|
+
readonly onCellTypeInference?: CellTypeInferenceSink;
|
|
7
|
+
}
|
|
8
|
+
declare function readCsvContent(text: string, options?: ReadCsvContentOptions): ContentDocument;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { ReadCsvContentOptions, readCsvContent };
|
package/dist/csv/read.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { inferCellValue } from "../layout/cell-typing.js";
|
|
2
|
+
import { parseCsvRecords } from "./records.js";
|
|
3
|
+
import { CONTENT_FORMAT_VERSION, PAGE_SIZE_A4 } from "document-schema.js";
|
|
4
|
+
//#region src/csv/read.ts
|
|
5
|
+
const HEADER_ROW_INDEX = 0;
|
|
6
|
+
const SHEET_NAME = "Sheet1";
|
|
7
|
+
const COLUMN_WIDTH_PT = 64;
|
|
8
|
+
const ROW_HEIGHT_PT = 15;
|
|
9
|
+
const MARGIN_PT = 56.69291338582677;
|
|
10
|
+
const DEFAULT_PRINT_SETTINGS = {
|
|
11
|
+
pageSize: PAGE_SIZE_A4,
|
|
12
|
+
margins: {
|
|
13
|
+
topPt: MARGIN_PT,
|
|
14
|
+
rightPt: MARGIN_PT,
|
|
15
|
+
bottomPt: MARGIN_PT,
|
|
16
|
+
leftPt: MARGIN_PT
|
|
17
|
+
},
|
|
18
|
+
gridlines: true,
|
|
19
|
+
headers: true,
|
|
20
|
+
pageOrder: "downThenOver"
|
|
21
|
+
};
|
|
22
|
+
function dataCell(rowIndex, columnIndex, field, onCellTypeInference) {
|
|
23
|
+
const inference = field === "" ? void 0 : inferCellValue(field);
|
|
24
|
+
if (inference !== void 0) onCellTypeInference?.({
|
|
25
|
+
sheetIndex: 0,
|
|
26
|
+
row: rowIndex,
|
|
27
|
+
column: columnIndex,
|
|
28
|
+
displayText: field,
|
|
29
|
+
...inference
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
row: rowIndex,
|
|
33
|
+
column: columnIndex,
|
|
34
|
+
value: field === "" ? { kind: "empty" } : inference?.outcome === "retyped" ? inference.value : {
|
|
35
|
+
kind: "string",
|
|
36
|
+
value: field
|
|
37
|
+
},
|
|
38
|
+
displayText: field
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function readCsvContent(text, options) {
|
|
42
|
+
const records = parseCsvRecords(text, options?.delimiter ?? ",");
|
|
43
|
+
const onCellTypeInference = options?.onCellTypeInference;
|
|
44
|
+
const columnCount = records.reduce((max, record) => Math.max(max, record.length), 0);
|
|
45
|
+
const cells = [];
|
|
46
|
+
records.forEach((record, rowIndex) => {
|
|
47
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
|
48
|
+
const field = record[columnIndex] ?? "";
|
|
49
|
+
if (rowIndex === HEADER_ROW_INDEX) cells.push({
|
|
50
|
+
row: rowIndex,
|
|
51
|
+
column: columnIndex,
|
|
52
|
+
value: {
|
|
53
|
+
kind: "string",
|
|
54
|
+
value: field
|
|
55
|
+
},
|
|
56
|
+
displayText: field
|
|
57
|
+
});
|
|
58
|
+
else cells.push(dataCell(rowIndex, columnIndex, field, onCellTypeInference));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const columns = Array.from({ length: columnCount }, (_unused, index) => ({
|
|
62
|
+
index,
|
|
63
|
+
widthPt: COLUMN_WIDTH_PT
|
|
64
|
+
}));
|
|
65
|
+
const rows = Array.from({ length: records.length }, (_unused, index) => ({
|
|
66
|
+
index,
|
|
67
|
+
heightPt: ROW_HEIGHT_PT
|
|
68
|
+
}));
|
|
69
|
+
return {
|
|
70
|
+
kind: "spreadsheet",
|
|
71
|
+
formatVersion: CONTENT_FORMAT_VERSION,
|
|
72
|
+
metadata: {},
|
|
73
|
+
sheets: [{
|
|
74
|
+
name: SHEET_NAME,
|
|
75
|
+
cells,
|
|
76
|
+
columns,
|
|
77
|
+
rows,
|
|
78
|
+
images: [],
|
|
79
|
+
printSettings: DEFAULT_PRINT_SETTINGS
|
|
80
|
+
}]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { readCsvContent };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/csv/records.ts
|
|
3
|
+
const DEFAULT_CSV_DELIMITER = ",";
|
|
4
|
+
const TSV_DELIMITER = " ";
|
|
5
|
+
var CsvParseError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "CsvParseError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function requireSingleCharacterDelimiter(delimiter) {
|
|
12
|
+
if (delimiter.length !== 1) throw new CsvParseError(`delimiter must be exactly one character, got ${JSON.stringify(delimiter)}`);
|
|
13
|
+
}
|
|
14
|
+
function isBlankRecord(record) {
|
|
15
|
+
return record.length === 1 && record[0] === "";
|
|
16
|
+
}
|
|
17
|
+
function parseCsvRecords(text, delimiter = ",") {
|
|
18
|
+
requireSingleCharacterDelimiter(delimiter);
|
|
19
|
+
const records = [];
|
|
20
|
+
let record = [];
|
|
21
|
+
let field = "";
|
|
22
|
+
let inQuotedField = false;
|
|
23
|
+
let fieldStarted = false;
|
|
24
|
+
const endField = () => {
|
|
25
|
+
record.push(field);
|
|
26
|
+
field = "";
|
|
27
|
+
fieldStarted = false;
|
|
28
|
+
};
|
|
29
|
+
const endRecord = () => {
|
|
30
|
+
endField();
|
|
31
|
+
records.push(record);
|
|
32
|
+
record = [];
|
|
33
|
+
};
|
|
34
|
+
let index = 0;
|
|
35
|
+
while (index < text.length) {
|
|
36
|
+
const ch = text[index];
|
|
37
|
+
if (inQuotedField) {
|
|
38
|
+
if (ch === "\"") {
|
|
39
|
+
if (text[index + 1] === "\"") {
|
|
40
|
+
field += "\"";
|
|
41
|
+
index += 2;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
inQuotedField = false;
|
|
45
|
+
index += 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
field += ch;
|
|
49
|
+
index += 1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (ch === "\"" && !fieldStarted) {
|
|
53
|
+
inQuotedField = true;
|
|
54
|
+
fieldStarted = true;
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (ch === delimiter) {
|
|
59
|
+
endField();
|
|
60
|
+
index += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (ch === "\r" || ch === "\n") {
|
|
64
|
+
if (ch === "\r" && text[index + 1] === "\n") index += 2;
|
|
65
|
+
else index += 1;
|
|
66
|
+
endRecord();
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
field += ch;
|
|
70
|
+
fieldStarted = true;
|
|
71
|
+
index += 1;
|
|
72
|
+
}
|
|
73
|
+
if (inQuotedField) throw new CsvParseError(`unterminated quoted field: no closing double quote before end of input (field so far: ${field.slice(0, 40)})`);
|
|
74
|
+
if (fieldStarted || field !== "" || record.length > 0) endRecord();
|
|
75
|
+
return records.filter((candidate) => !isBlankRecord(candidate));
|
|
76
|
+
}
|
|
77
|
+
function quoteCsvField(field, delimiter = ",") {
|
|
78
|
+
return field.includes(delimiter) || field.includes("\"") || field.includes("\r") || field.includes("\n") ? `"${field.replaceAll("\"", "\"\"")}"` : field;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
exports.CsvParseError = CsvParseError;
|
|
82
|
+
exports.DEFAULT_CSV_DELIMITER = DEFAULT_CSV_DELIMITER;
|
|
83
|
+
exports.TSV_DELIMITER = TSV_DELIMITER;
|
|
84
|
+
exports.parseCsvRecords = parseCsvRecords;
|
|
85
|
+
exports.quoteCsvField = quoteCsvField;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/csv/records.d.ts
|
|
2
|
+
declare const DEFAULT_CSV_DELIMITER = ",";
|
|
3
|
+
declare const TSV_DELIMITER = "\t";
|
|
4
|
+
declare class CsvParseError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
declare function parseCsvRecords(text: string, delimiter?: string): string[][];
|
|
8
|
+
declare function quoteCsvField(field: string, delimiter?: string): string;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { CsvParseError, DEFAULT_CSV_DELIMITER, TSV_DELIMITER, parseCsvRecords, quoteCsvField };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/csv/records.d.ts
|
|
2
|
+
declare const DEFAULT_CSV_DELIMITER = ",";
|
|
3
|
+
declare const TSV_DELIMITER = "\t";
|
|
4
|
+
declare class CsvParseError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
declare function parseCsvRecords(text: string, delimiter?: string): string[][];
|
|
8
|
+
declare function quoteCsvField(field: string, delimiter?: string): string;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { CsvParseError, DEFAULT_CSV_DELIMITER, TSV_DELIMITER, parseCsvRecords, quoteCsvField };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region src/csv/records.ts
|
|
2
|
+
const DEFAULT_CSV_DELIMITER = ",";
|
|
3
|
+
const TSV_DELIMITER = " ";
|
|
4
|
+
var CsvParseError = class extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "CsvParseError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
function requireSingleCharacterDelimiter(delimiter) {
|
|
11
|
+
if (delimiter.length !== 1) throw new CsvParseError(`delimiter must be exactly one character, got ${JSON.stringify(delimiter)}`);
|
|
12
|
+
}
|
|
13
|
+
function isBlankRecord(record) {
|
|
14
|
+
return record.length === 1 && record[0] === "";
|
|
15
|
+
}
|
|
16
|
+
function parseCsvRecords(text, delimiter = ",") {
|
|
17
|
+
requireSingleCharacterDelimiter(delimiter);
|
|
18
|
+
const records = [];
|
|
19
|
+
let record = [];
|
|
20
|
+
let field = "";
|
|
21
|
+
let inQuotedField = false;
|
|
22
|
+
let fieldStarted = false;
|
|
23
|
+
const endField = () => {
|
|
24
|
+
record.push(field);
|
|
25
|
+
field = "";
|
|
26
|
+
fieldStarted = false;
|
|
27
|
+
};
|
|
28
|
+
const endRecord = () => {
|
|
29
|
+
endField();
|
|
30
|
+
records.push(record);
|
|
31
|
+
record = [];
|
|
32
|
+
};
|
|
33
|
+
let index = 0;
|
|
34
|
+
while (index < text.length) {
|
|
35
|
+
const ch = text[index];
|
|
36
|
+
if (inQuotedField) {
|
|
37
|
+
if (ch === "\"") {
|
|
38
|
+
if (text[index + 1] === "\"") {
|
|
39
|
+
field += "\"";
|
|
40
|
+
index += 2;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
inQuotedField = false;
|
|
44
|
+
index += 1;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
field += ch;
|
|
48
|
+
index += 1;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === "\"" && !fieldStarted) {
|
|
52
|
+
inQuotedField = true;
|
|
53
|
+
fieldStarted = true;
|
|
54
|
+
index += 1;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (ch === delimiter) {
|
|
58
|
+
endField();
|
|
59
|
+
index += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (ch === "\r" || ch === "\n") {
|
|
63
|
+
if (ch === "\r" && text[index + 1] === "\n") index += 2;
|
|
64
|
+
else index += 1;
|
|
65
|
+
endRecord();
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
field += ch;
|
|
69
|
+
fieldStarted = true;
|
|
70
|
+
index += 1;
|
|
71
|
+
}
|
|
72
|
+
if (inQuotedField) throw new CsvParseError(`unterminated quoted field: no closing double quote before end of input (field so far: ${field.slice(0, 40)})`);
|
|
73
|
+
if (fieldStarted || field !== "" || record.length > 0) endRecord();
|
|
74
|
+
return records.filter((candidate) => !isBlankRecord(candidate));
|
|
75
|
+
}
|
|
76
|
+
function quoteCsvField(field, delimiter = ",") {
|
|
77
|
+
return field.includes(delimiter) || field.includes("\"") || field.includes("\r") || field.includes("\n") ? `"${field.replaceAll("\"", "\"\"")}"` : field;
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { CsvParseError, DEFAULT_CSV_DELIMITER, TSV_DELIMITER, parseCsvRecords, quoteCsvField };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/csv/text.ts
|
|
3
|
+
var CsvInvalidUtf8Error = class extends Error {
|
|
4
|
+
constructor() {
|
|
5
|
+
super("csv text must be well-formed UTF-8");
|
|
6
|
+
this.name = "CsvInvalidUtf8Error";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
function decodeCsvText(bytes) {
|
|
10
|
+
try {
|
|
11
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
12
|
+
} catch {
|
|
13
|
+
throw new CsvInvalidUtf8Error();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function encodeCsvText(text) {
|
|
17
|
+
return new TextEncoder().encode(text);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
exports.CsvInvalidUtf8Error = CsvInvalidUtf8Error;
|
|
21
|
+
exports.decodeCsvText = decodeCsvText;
|
|
22
|
+
exports.encodeCsvText = encodeCsvText;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/csv/text.d.ts
|
|
2
|
+
declare class CsvInvalidUtf8Error extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
declare function decodeCsvText(bytes: Uint8Array): string;
|
|
6
|
+
declare function encodeCsvText(text: string): Uint8Array<ArrayBuffer>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { CsvInvalidUtf8Error, decodeCsvText, encodeCsvText };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/csv/text.d.ts
|
|
2
|
+
declare class CsvInvalidUtf8Error extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
declare function decodeCsvText(bytes: Uint8Array): string;
|
|
6
|
+
declare function encodeCsvText(text: string): Uint8Array<ArrayBuffer>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { CsvInvalidUtf8Error, decodeCsvText, encodeCsvText };
|
package/dist/csv/text.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/csv/text.ts
|
|
2
|
+
var CsvInvalidUtf8Error = class extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("csv text must be well-formed UTF-8");
|
|
5
|
+
this.name = "CsvInvalidUtf8Error";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
function decodeCsvText(bytes) {
|
|
9
|
+
try {
|
|
10
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
11
|
+
} catch {
|
|
12
|
+
throw new CsvInvalidUtf8Error();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function encodeCsvText(text) {
|
|
16
|
+
return new TextEncoder().encode(text);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { CsvInvalidUtf8Error, decodeCsvText, encodeCsvText };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_csv_records = require("./records.cjs");
|
|
3
|
+
//#region src/csv/write.ts
|
|
4
|
+
var CsvUnsupportedDocumentKindError = class extends Error {
|
|
5
|
+
kind;
|
|
6
|
+
constructor(kind) {
|
|
7
|
+
super(`buildCsvText: expected a spreadsheet ContentDocument, got kind '${kind}'`);
|
|
8
|
+
this.name = "CsvUnsupportedDocumentKindError";
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var CsvSheetNotSpecifiedError = class extends Error {
|
|
13
|
+
availableSheets;
|
|
14
|
+
constructor(availableSheets) {
|
|
15
|
+
super(`buildCsvText: this document has more than one sheet (${availableSheets.join(", ")}) -- pass { sheet: '<name>' } to select one`);
|
|
16
|
+
this.name = "CsvSheetNotSpecifiedError";
|
|
17
|
+
this.availableSheets = availableSheets;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var CsvSheetNotFoundError = class extends Error {
|
|
21
|
+
sheet;
|
|
22
|
+
availableSheets;
|
|
23
|
+
constructor(sheet, availableSheets) {
|
|
24
|
+
super(`buildCsvText: sheet "${sheet}" not found -- available sheet(s): ${availableSheets.length === 0 ? "(none)" : availableSheets.join(", ")}`);
|
|
25
|
+
this.name = "CsvSheetNotFoundError";
|
|
26
|
+
this.sheet = sheet;
|
|
27
|
+
this.availableSheets = availableSheets;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
function selectSheet(sheets, sheetName) {
|
|
31
|
+
const availableNames = sheets.map((candidate) => candidate.name);
|
|
32
|
+
if (sheetName !== void 0) {
|
|
33
|
+
const found = sheets.find((candidate) => candidate.name === sheetName);
|
|
34
|
+
if (found === void 0) throw new CsvSheetNotFoundError(sheetName, availableNames);
|
|
35
|
+
return found;
|
|
36
|
+
}
|
|
37
|
+
if (sheets.length === 0) throw new CsvSheetNotFoundError("(unspecified)", availableNames);
|
|
38
|
+
if (sheets.length > 1) throw new CsvSheetNotSpecifiedError(availableNames);
|
|
39
|
+
const only = sheets[0];
|
|
40
|
+
if (only === void 0) throw new CsvSheetNotFoundError("(unspecified)", availableNames);
|
|
41
|
+
return only;
|
|
42
|
+
}
|
|
43
|
+
function buildCsvText(content, options) {
|
|
44
|
+
if (content.kind !== "spreadsheet") throw new CsvUnsupportedDocumentKindError(content.kind);
|
|
45
|
+
const sheet = selectSheet(content.sheets, options?.sheet);
|
|
46
|
+
const delimiter = options?.delimiter ?? ",";
|
|
47
|
+
const fieldsByRow = /* @__PURE__ */ new Map();
|
|
48
|
+
let maxRowIndex = -1;
|
|
49
|
+
let maxColumnIndex = -1;
|
|
50
|
+
for (const cell of sheet.cells) {
|
|
51
|
+
let row = fieldsByRow.get(cell.row);
|
|
52
|
+
if (row === void 0) {
|
|
53
|
+
row = /* @__PURE__ */ new Map();
|
|
54
|
+
fieldsByRow.set(cell.row, row);
|
|
55
|
+
}
|
|
56
|
+
row.set(cell.column, cell.displayText);
|
|
57
|
+
maxRowIndex = Math.max(maxRowIndex, cell.row);
|
|
58
|
+
maxColumnIndex = Math.max(maxColumnIndex, cell.column);
|
|
59
|
+
}
|
|
60
|
+
const rowCount = Math.max(sheet.rows.length, maxRowIndex + 1);
|
|
61
|
+
const columnCount = Math.max(sheet.columns.length, maxColumnIndex + 1);
|
|
62
|
+
const lines = [];
|
|
63
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
64
|
+
const row = fieldsByRow.get(rowIndex);
|
|
65
|
+
const fields = [];
|
|
66
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) fields.push(require_csv_records.quoteCsvField(row?.get(columnIndex) ?? "", delimiter));
|
|
67
|
+
lines.push(fields.join(delimiter));
|
|
68
|
+
}
|
|
69
|
+
return lines.length === 0 ? "" : `${lines.join("\r\n")}\r\n`;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
exports.CsvSheetNotFoundError = CsvSheetNotFoundError;
|
|
73
|
+
exports.CsvSheetNotSpecifiedError = CsvSheetNotSpecifiedError;
|
|
74
|
+
exports.CsvUnsupportedDocumentKindError = CsvUnsupportedDocumentKindError;
|
|
75
|
+
exports.buildCsvText = buildCsvText;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ContentDocument } from "document-schema.js";
|
|
2
|
+
//#region src/csv/write.d.ts
|
|
3
|
+
declare class CsvUnsupportedDocumentKindError extends Error {
|
|
4
|
+
readonly kind: ContentDocument['kind'];
|
|
5
|
+
constructor(kind: ContentDocument['kind']);
|
|
6
|
+
}
|
|
7
|
+
declare class CsvSheetNotSpecifiedError extends Error {
|
|
8
|
+
readonly availableSheets: readonly string[];
|
|
9
|
+
constructor(availableSheets: readonly string[]);
|
|
10
|
+
}
|
|
11
|
+
declare class CsvSheetNotFoundError extends Error {
|
|
12
|
+
readonly sheet: string;
|
|
13
|
+
readonly availableSheets: readonly string[];
|
|
14
|
+
constructor(sheet: string, availableSheets: readonly string[]);
|
|
15
|
+
}
|
|
16
|
+
interface BuildCsvTextOptions {
|
|
17
|
+
readonly delimiter?: string;
|
|
18
|
+
readonly sheet?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function buildCsvText(content: ContentDocument, options?: BuildCsvTextOptions): string;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { BuildCsvTextOptions, CsvSheetNotFoundError, CsvSheetNotSpecifiedError, CsvUnsupportedDocumentKindError, buildCsvText };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ContentDocument } from "document-schema.js";
|
|
2
|
+
//#region src/csv/write.d.ts
|
|
3
|
+
declare class CsvUnsupportedDocumentKindError extends Error {
|
|
4
|
+
readonly kind: ContentDocument['kind'];
|
|
5
|
+
constructor(kind: ContentDocument['kind']);
|
|
6
|
+
}
|
|
7
|
+
declare class CsvSheetNotSpecifiedError extends Error {
|
|
8
|
+
readonly availableSheets: readonly string[];
|
|
9
|
+
constructor(availableSheets: readonly string[]);
|
|
10
|
+
}
|
|
11
|
+
declare class CsvSheetNotFoundError extends Error {
|
|
12
|
+
readonly sheet: string;
|
|
13
|
+
readonly availableSheets: readonly string[];
|
|
14
|
+
constructor(sheet: string, availableSheets: readonly string[]);
|
|
15
|
+
}
|
|
16
|
+
interface BuildCsvTextOptions {
|
|
17
|
+
readonly delimiter?: string;
|
|
18
|
+
readonly sheet?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function buildCsvText(content: ContentDocument, options?: BuildCsvTextOptions): string;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { BuildCsvTextOptions, CsvSheetNotFoundError, CsvSheetNotSpecifiedError, CsvUnsupportedDocumentKindError, buildCsvText };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { quoteCsvField } from "./records.js";
|
|
2
|
+
//#region src/csv/write.ts
|
|
3
|
+
var CsvUnsupportedDocumentKindError = class extends Error {
|
|
4
|
+
kind;
|
|
5
|
+
constructor(kind) {
|
|
6
|
+
super(`buildCsvText: expected a spreadsheet ContentDocument, got kind '${kind}'`);
|
|
7
|
+
this.name = "CsvUnsupportedDocumentKindError";
|
|
8
|
+
this.kind = kind;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var CsvSheetNotSpecifiedError = class extends Error {
|
|
12
|
+
availableSheets;
|
|
13
|
+
constructor(availableSheets) {
|
|
14
|
+
super(`buildCsvText: this document has more than one sheet (${availableSheets.join(", ")}) -- pass { sheet: '<name>' } to select one`);
|
|
15
|
+
this.name = "CsvSheetNotSpecifiedError";
|
|
16
|
+
this.availableSheets = availableSheets;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var CsvSheetNotFoundError = class extends Error {
|
|
20
|
+
sheet;
|
|
21
|
+
availableSheets;
|
|
22
|
+
constructor(sheet, availableSheets) {
|
|
23
|
+
super(`buildCsvText: sheet "${sheet}" not found -- available sheet(s): ${availableSheets.length === 0 ? "(none)" : availableSheets.join(", ")}`);
|
|
24
|
+
this.name = "CsvSheetNotFoundError";
|
|
25
|
+
this.sheet = sheet;
|
|
26
|
+
this.availableSheets = availableSheets;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
function selectSheet(sheets, sheetName) {
|
|
30
|
+
const availableNames = sheets.map((candidate) => candidate.name);
|
|
31
|
+
if (sheetName !== void 0) {
|
|
32
|
+
const found = sheets.find((candidate) => candidate.name === sheetName);
|
|
33
|
+
if (found === void 0) throw new CsvSheetNotFoundError(sheetName, availableNames);
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
if (sheets.length === 0) throw new CsvSheetNotFoundError("(unspecified)", availableNames);
|
|
37
|
+
if (sheets.length > 1) throw new CsvSheetNotSpecifiedError(availableNames);
|
|
38
|
+
const only = sheets[0];
|
|
39
|
+
if (only === void 0) throw new CsvSheetNotFoundError("(unspecified)", availableNames);
|
|
40
|
+
return only;
|
|
41
|
+
}
|
|
42
|
+
function buildCsvText(content, options) {
|
|
43
|
+
if (content.kind !== "spreadsheet") throw new CsvUnsupportedDocumentKindError(content.kind);
|
|
44
|
+
const sheet = selectSheet(content.sheets, options?.sheet);
|
|
45
|
+
const delimiter = options?.delimiter ?? ",";
|
|
46
|
+
const fieldsByRow = /* @__PURE__ */ new Map();
|
|
47
|
+
let maxRowIndex = -1;
|
|
48
|
+
let maxColumnIndex = -1;
|
|
49
|
+
for (const cell of sheet.cells) {
|
|
50
|
+
let row = fieldsByRow.get(cell.row);
|
|
51
|
+
if (row === void 0) {
|
|
52
|
+
row = /* @__PURE__ */ new Map();
|
|
53
|
+
fieldsByRow.set(cell.row, row);
|
|
54
|
+
}
|
|
55
|
+
row.set(cell.column, cell.displayText);
|
|
56
|
+
maxRowIndex = Math.max(maxRowIndex, cell.row);
|
|
57
|
+
maxColumnIndex = Math.max(maxColumnIndex, cell.column);
|
|
58
|
+
}
|
|
59
|
+
const rowCount = Math.max(sheet.rows.length, maxRowIndex + 1);
|
|
60
|
+
const columnCount = Math.max(sheet.columns.length, maxColumnIndex + 1);
|
|
61
|
+
const lines = [];
|
|
62
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
|
63
|
+
const row = fieldsByRow.get(rowIndex);
|
|
64
|
+
const fields = [];
|
|
65
|
+
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) fields.push(quoteCsvField(row?.get(columnIndex) ?? "", delimiter));
|
|
66
|
+
lines.push(fields.join(delimiter));
|
|
67
|
+
}
|
|
68
|
+
return lines.length === 0 ? "" : `${lines.join("\r\n")}\r\n`;
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { CsvSheetNotFoundError, CsvSheetNotSpecifiedError, CsvUnsupportedDocumentKindError, buildCsvText };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { el, txt } from "../../xml/fragment.js";
|
|
2
|
-
import { encodeXmlText } from "../../xml/entities.js";
|
|
2
|
+
import { encodeXmlText as encodeXmlText$1 } from "../../xml/entities.js";
|
|
3
3
|
import { PAGE_SIZE_A4 } from "document-schema.js";
|
|
4
4
|
import { ODF_MEDIA_TYPES, setDocumentMediaType, syncManifest, xmlnsAttributes } from "odf.js";
|
|
5
5
|
//#region src/edit/odg/scaffold.ts
|
|
@@ -78,13 +78,13 @@ function buildContentXml() {
|
|
|
78
78
|
function buildOfficeMeta(metadata) {
|
|
79
79
|
if (metadata === void 0) return [];
|
|
80
80
|
const children = [];
|
|
81
|
-
if (metadata.title !== void 0) children.push(el("dc:title", {}, [txt(encodeXmlText(metadata.title))]));
|
|
82
|
-
if (metadata.author !== void 0) children.push(el("meta:initial-creator", {}, [txt(encodeXmlText(metadata.author))]));
|
|
83
|
-
if (metadata.subject !== void 0) children.push(el("dc:subject", {}, [txt(encodeXmlText(metadata.subject))]));
|
|
84
|
-
for (const keyword of metadata.keywords ?? []) children.push(el("meta:keyword", {}, [txt(encodeXmlText(keyword))]));
|
|
85
|
-
if (metadata.creator !== void 0) children.push(el("meta:generator", {}, [txt(encodeXmlText(metadata.creator))]));
|
|
86
|
-
if (metadata.createdIso !== void 0) children.push(el("meta:creation-date", {}, [txt(encodeXmlText(metadata.createdIso))]));
|
|
87
|
-
if (metadata.modifiedIso !== void 0) children.push(el("dc:date", {}, [txt(encodeXmlText(metadata.modifiedIso))]));
|
|
81
|
+
if (metadata.title !== void 0) children.push(el("dc:title", {}, [txt(encodeXmlText$1(metadata.title))]));
|
|
82
|
+
if (metadata.author !== void 0) children.push(el("meta:initial-creator", {}, [txt(encodeXmlText$1(metadata.author))]));
|
|
83
|
+
if (metadata.subject !== void 0) children.push(el("dc:subject", {}, [txt(encodeXmlText$1(metadata.subject))]));
|
|
84
|
+
for (const keyword of metadata.keywords ?? []) children.push(el("meta:keyword", {}, [txt(encodeXmlText$1(keyword))]));
|
|
85
|
+
if (metadata.creator !== void 0) children.push(el("meta:generator", {}, [txt(encodeXmlText$1(metadata.creator))]));
|
|
86
|
+
if (metadata.createdIso !== void 0) children.push(el("meta:creation-date", {}, [txt(encodeXmlText$1(metadata.createdIso))]));
|
|
87
|
+
if (metadata.modifiedIso !== void 0) children.push(el("dc:date", {}, [txt(encodeXmlText$1(metadata.modifiedIso))]));
|
|
88
88
|
return children;
|
|
89
89
|
}
|
|
90
90
|
function buildMetaXml(metadata) {
|