@simplysm/excel 14.0.100 → 14.0.101
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/dist/biff/biff-model-factory.d.ts +32 -0
- package/dist/biff/biff-model-factory.d.ts.map +1 -0
- package/dist/biff/biff-model-factory.js +88 -0
- package/dist/biff/biff-model-factory.js.map +1 -0
- package/dist/biff/biff-ptg.d.ts +14 -0
- package/dist/biff/biff-ptg.d.ts.map +1 -0
- package/dist/biff/biff-ptg.js +277 -0
- package/dist/biff/biff-ptg.js.map +1 -0
- package/dist/biff/biff-records.d.ts +163 -0
- package/dist/biff/biff-records.d.ts.map +1 -0
- package/dist/biff/biff-records.js +579 -0
- package/dist/biff/biff-records.js.map +1 -0
- package/dist/biff/biff-shared-string-model.d.ts +13 -0
- package/dist/biff/biff-shared-string-model.d.ts.map +1 -0
- package/dist/biff/biff-shared-string-model.js +41 -0
- package/dist/biff/biff-shared-string-model.js.map +1 -0
- package/dist/biff/biff-style-model.d.ts +39 -0
- package/dist/biff/biff-style-model.d.ts.map +1 -0
- package/dist/biff/biff-style-model.js +182 -0
- package/dist/biff/biff-style-model.js.map +1 -0
- package/dist/biff/biff-workbook-model.d.ts +17 -0
- package/dist/biff/biff-workbook-model.d.ts.map +1 -0
- package/dist/biff/biff-workbook-model.js +64 -0
- package/dist/biff/biff-workbook-model.js.map +1 -0
- package/dist/biff/biff-worksheet-model.d.ts +69 -0
- package/dist/biff/biff-worksheet-model.d.ts.map +1 -0
- package/dist/biff/biff-worksheet-model.js +495 -0
- package/dist/biff/biff-worksheet-model.js.map +1 -0
- package/dist/biff/biff12-codec.d.ts +149 -0
- package/dist/biff/biff12-codec.d.ts.map +1 -0
- package/dist/biff/biff12-codec.js +262 -0
- package/dist/biff/biff12-codec.js.map +1 -0
- package/dist/excel-cell.d.ts +0 -6
- package/dist/excel-cell.d.ts.map +1 -1
- package/dist/excel-cell.js +2 -24
- package/dist/excel-cell.js.map +1 -1
- package/dist/excel-col.js.map +1 -1
- package/dist/excel-row.js.map +1 -1
- package/dist/excel-workbook.d.ts +4 -21
- package/dist/excel-workbook.d.ts.map +1 -1
- package/dist/excel-workbook.js +16 -49
- package/dist/excel-workbook.js.map +1 -1
- package/dist/excel-worksheet.d.ts +0 -11
- package/dist/excel-worksheet.d.ts.map +1 -1
- package/dist/excel-worksheet.js +9 -46
- package/dist/excel-worksheet.js.map +1 -1
- package/dist/excel-wrapper.d.ts +0 -10
- package/dist/excel-wrapper.d.ts.map +1 -1
- package/dist/excel-wrapper.js +0 -10
- package/dist/excel-wrapper.js.map +1 -1
- package/dist/models/excel-format.d.ts +3 -0
- package/dist/models/excel-format.d.ts.map +1 -0
- package/dist/models/excel-format.js +2 -0
- package/dist/models/excel-format.js.map +1 -0
- package/dist/models/excel-model-factory.d.ts +33 -0
- package/dist/models/excel-model-factory.d.ts.map +1 -0
- package/dist/models/excel-model-factory.js +2 -0
- package/dist/models/excel-model-factory.js.map +1 -0
- package/dist/models/excel-model.d.ts +12 -0
- package/dist/models/excel-model.d.ts.map +1 -0
- package/dist/models/excel-model.js +2 -0
- package/dist/models/excel-model.js.map +1 -0
- package/dist/models/i-content-type-model.d.ts +7 -0
- package/dist/models/i-content-type-model.d.ts.map +1 -0
- package/dist/models/i-content-type-model.js +2 -0
- package/dist/models/i-content-type-model.js.map +1 -0
- package/dist/models/i-drawing-model.d.ts +23 -0
- package/dist/models/i-drawing-model.d.ts.map +1 -0
- package/dist/models/i-drawing-model.js +2 -0
- package/dist/models/i-drawing-model.js.map +1 -0
- package/dist/models/i-relationship-model.d.ts +16 -0
- package/dist/models/i-relationship-model.d.ts.map +1 -0
- package/dist/models/i-relationship-model.js +2 -0
- package/dist/models/i-relationship-model.js.map +1 -0
- package/dist/models/i-shared-string-model.d.ts +9 -0
- package/dist/models/i-shared-string-model.d.ts.map +1 -0
- package/dist/models/i-shared-string-model.js +2 -0
- package/dist/models/i-shared-string-model.js.map +1 -0
- package/dist/models/i-style-model.d.ts +18 -0
- package/dist/models/i-style-model.d.ts.map +1 -0
- package/dist/models/i-style-model.js +2 -0
- package/dist/models/i-style-model.js.map +1 -0
- package/dist/models/i-workbook-model.d.ts +17 -0
- package/dist/models/i-workbook-model.d.ts.map +1 -0
- package/dist/models/i-workbook-model.js +2 -0
- package/dist/models/i-workbook-model.js.map +1 -0
- package/dist/models/i-worksheet-model.d.ts +39 -0
- package/dist/models/i-worksheet-model.d.ts.map +1 -0
- package/dist/models/i-worksheet-model.js +2 -0
- package/dist/models/i-worksheet-model.js.map +1 -0
- package/dist/models/shared/excel-cf-spec.d.ts +13 -0
- package/dist/models/shared/excel-cf-spec.d.ts.map +1 -0
- package/dist/models/shared/excel-cf-spec.js +2 -0
- package/dist/models/shared/excel-cf-spec.js.map +1 -0
- package/dist/models/shared/excel-style.d.ts +24 -0
- package/dist/models/shared/excel-style.d.ts.map +1 -0
- package/dist/models/shared/excel-style.js +38 -0
- package/dist/models/shared/excel-style.js.map +1 -0
- package/dist/types.d.ts +0 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/excel-style-data.d.ts +3 -5
- package/dist/utils/excel-style-data.d.ts.map +1 -1
- package/dist/utils/excel-style-data.js +2 -16
- package/dist/utils/excel-style-data.js.map +1 -1
- package/dist/utils/zip-cache.d.ts +46 -9
- package/dist/utils/zip-cache.d.ts.map +1 -1
- package/dist/utils/zip-cache.js +138 -52
- package/dist/utils/zip-cache.js.map +1 -1
- package/dist/xml/excel-xml-content-type.d.ts +8 -4
- package/dist/xml/excel-xml-content-type.d.ts.map +1 -1
- package/dist/xml/excel-xml-content-type.js +13 -6
- package/dist/xml/excel-xml-content-type.js.map +1 -1
- package/dist/xml/excel-xml-drawing.d.ts +8 -4
- package/dist/xml/excel-xml-drawing.d.ts.map +1 -1
- package/dist/xml/excel-xml-drawing.js +14 -7
- package/dist/xml/excel-xml-drawing.js.map +1 -1
- package/dist/xml/excel-xml-relationship.d.ts +12 -4
- package/dist/xml/excel-xml-relationship.d.ts.map +1 -1
- package/dist/xml/excel-xml-relationship.js +22 -12
- package/dist/xml/excel-xml-relationship.js.map +1 -1
- package/dist/xml/excel-xml-shared-string.d.ts +8 -4
- package/dist/xml/excel-xml-shared-string.d.ts.map +1 -1
- package/dist/xml/excel-xml-shared-string.js +16 -9
- package/dist/xml/excel-xml-shared-string.js.map +1 -1
- package/dist/xml/excel-xml-style.d.ts +11 -22
- package/dist/xml/excel-xml-style.d.ts.map +1 -1
- package/dist/xml/excel-xml-style.js +66 -92
- package/dist/xml/excel-xml-style.js.map +1 -1
- package/dist/xml/excel-xml-unknown.d.ts +6 -5
- package/dist/xml/excel-xml-unknown.d.ts.map +1 -1
- package/dist/xml/excel-xml-unknown.js +7 -4
- package/dist/xml/excel-xml-unknown.js.map +1 -1
- package/dist/xml/excel-xml-workbook.d.ts +9 -4
- package/dist/xml/excel-xml-workbook.d.ts.map +1 -1
- package/dist/xml/excel-xml-workbook.js +28 -20
- package/dist/xml/excel-xml-workbook.js.map +1 -1
- package/dist/xml/excel-xml-worksheet.d.ts +12 -7
- package/dist/xml/excel-xml-worksheet.d.ts.map +1 -1
- package/dist/xml/excel-xml-worksheet.js +65 -50
- package/dist/xml/excel-xml-worksheet.js.map +1 -1
- package/dist/xml/xml-model-factory.d.ts +25 -0
- package/dist/xml/xml-model-factory.d.ts.map +1 -0
- package/dist/xml/xml-model-factory.js +66 -0
- package/dist/xml/xml-model-factory.js.map +1 -0
- package/package.json +2 -2
- package/src/biff/biff-model-factory.ts +101 -0
- package/src/biff/biff-ptg.ts +289 -0
- package/src/biff/biff-records.ts +686 -0
- package/src/biff/biff-shared-string-model.ts +52 -0
- package/src/biff/biff-style-model.ts +219 -0
- package/src/biff/biff-workbook-model.ts +83 -0
- package/src/biff/biff-worksheet-model.ts +545 -0
- package/src/biff/biff12-codec.ts +286 -0
- package/src/excel-cell.ts +12 -46
- package/src/excel-col.ts +3 -3
- package/src/excel-row.ts +3 -3
- package/src/excel-workbook.ts +26 -69
- package/src/excel-worksheet.ts +36 -107
- package/src/excel-wrapper.ts +0 -10
- package/src/models/excel-format.ts +2 -0
- package/src/models/excel-model-factory.ts +33 -0
- package/src/models/excel-model.ts +12 -0
- package/src/models/i-content-type-model.ts +7 -0
- package/src/models/i-drawing-model.ts +13 -0
- package/src/models/i-relationship-model.ts +13 -0
- package/src/models/i-shared-string-model.ts +9 -0
- package/src/models/i-style-model.ts +18 -0
- package/src/models/i-workbook-model.ts +17 -0
- package/src/models/i-worksheet-model.ts +40 -0
- package/src/models/shared/excel-cf-spec.ts +24 -0
- package/src/models/shared/excel-style.ts +65 -0
- package/src/types.ts +0 -13
- package/src/utils/excel-style-data.ts +4 -25
- package/src/utils/zip-cache.ts +189 -58
- package/src/xml/excel-xml-content-type.ts +18 -8
- package/src/xml/excel-xml-drawing.ts +19 -9
- package/src/xml/excel-xml-relationship.ts +28 -14
- package/src/xml/excel-xml-shared-string.ts +20 -11
- package/src/xml/excel-xml-style.ts +74 -116
- package/src/xml/excel-xml-unknown.ts +8 -4
- package/src/xml/excel-xml-workbook.ts +34 -22
- package/src/xml/excel-xml-worksheet.ts +73 -53
- package/src/xml/xml-model-factory.ts +83 -0
package/src/utils/zip-cache.ts
CHANGED
|
@@ -1,93 +1,224 @@
|
|
|
1
1
|
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
-
import { ZipArchive
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "../
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { ExcelXmlRelationship } from "../xml/excel-xml-relationship";
|
|
16
|
-
import { ExcelXmlSharedString } from "../xml/excel-xml-shared-string";
|
|
17
|
-
import { ExcelXmlStyle } from "../xml/excel-xml-style";
|
|
18
|
-
import { ExcelXmlUnknown } from "../xml/excel-xml-unknown";
|
|
19
|
-
import { ExcelXmlWorkbook } from "../xml/excel-xml-workbook";
|
|
20
|
-
import { ExcelXmlWorksheet } from "../xml/excel-xml-worksheet";
|
|
2
|
+
import { ZipArchive } from "@simplysm/core-common";
|
|
3
|
+
import { BiffModelFactory } from "../biff/biff-model-factory";
|
|
4
|
+
import type { ExcelFormat } from "../models/excel-format";
|
|
5
|
+
import type { IExcelModel } from "../models/excel-model";
|
|
6
|
+
import type { IExcelModelFactory } from "../models/excel-model-factory";
|
|
7
|
+
import type { IContentTypeModel } from "../models/i-content-type-model";
|
|
8
|
+
import type { IDrawingModel } from "../models/i-drawing-model";
|
|
9
|
+
import type { IRelationshipModel } from "../models/i-relationship-model";
|
|
10
|
+
import type { ISharedStringModel } from "../models/i-shared-string-model";
|
|
11
|
+
import type { IStyleModel } from "../models/i-style-model";
|
|
12
|
+
import type { IWorkbookModel } from "../models/i-workbook-model";
|
|
13
|
+
import type { IWorksheetModel } from "../models/i-worksheet-model";
|
|
14
|
+
import { XmlModelFactory } from "../xml/xml-model-factory";
|
|
21
15
|
|
|
22
16
|
/**
|
|
23
17
|
* Excel ZIP 아카이브의 파일 캐시를 관리하는 클래스.
|
|
24
|
-
*
|
|
18
|
+
* 모델 파트는 포맷별 팩토리로 파싱/직렬화하고, 기타 파일(media 등)은 바이트 배열로 캐싱한다.
|
|
25
19
|
*
|
|
26
20
|
* @remarks
|
|
27
|
-
* ##
|
|
21
|
+
* ## 포맷 판별·경로 정규화
|
|
28
22
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
23
|
+
* 기존 파일은 `xl/workbook.bin` 존재 여부로 xlsx/xlsb 를 1회 판별한다(없으면 xlsx).
|
|
24
|
+
* 상위 레이어는 `xl/workbook.xml` 같은 OOXML(.xml) 경로를 사용하므로, xlsb 워크북에서는
|
|
25
|
+
* 이를 `.bin` 경로로 정규화하여 상위 레이어가 포맷을 모르게 한다.
|
|
26
|
+
*
|
|
27
|
+
* ## Lazy Loading
|
|
28
|
+
*
|
|
29
|
+
* 파일은 최초 접근 시에만 ZIP 에서 읽고 파싱하며, 이후 접근은 캐싱된 객체를 반환한다.
|
|
32
30
|
*/
|
|
33
31
|
export class ZipCache {
|
|
34
|
-
private readonly _cache = new Map<string,
|
|
32
|
+
private readonly _cache = new Map<string, IExcelModel | Bytes | undefined>();
|
|
35
33
|
private readonly _zip: ZipArchive;
|
|
34
|
+
private _factory: IExcelModelFactory | undefined;
|
|
36
35
|
|
|
37
|
-
constructor(arg?: Blob | Bytes) {
|
|
36
|
+
constructor(arg?: Blob | Bytes, format?: ExcelFormat) {
|
|
38
37
|
this._zip = new ZipArchive(arg);
|
|
38
|
+
if (format != null) {
|
|
39
|
+
this._factory = format === "xlsb" ? new BiffModelFactory() : new XmlModelFactory();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 워크북 포맷. 미판별 시 xlsx 로 간주. */
|
|
44
|
+
get format(): ExcelFormat {
|
|
45
|
+
return (this._factory ??= new XmlModelFactory()).format;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** 기존 파일 포맷을 1회 판별해 팩토리 확정 (async). */
|
|
49
|
+
private async _resolveFactory(): Promise<IExcelModelFactory> {
|
|
50
|
+
if (this._factory == null) {
|
|
51
|
+
const isXlsb = (await this._zip.get("xl/workbook.bin")) != null;
|
|
52
|
+
this._factory = isXlsb ? new BiffModelFactory() : new XmlModelFactory();
|
|
53
|
+
}
|
|
54
|
+
return this._factory;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 새(빈) 워크북의 동기 생성 경로용 팩토리. 빈 워크북은 xlsx 로 본다. */
|
|
58
|
+
private get _syncFactory(): IExcelModelFactory {
|
|
59
|
+
return (this._factory ??= new XmlModelFactory());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 상위 레이어의 .xml 고정 경로를 xlsb 의 실제 .bin 경로로 정규화. */
|
|
63
|
+
private _resolvePath(filePath: string): string {
|
|
64
|
+
if (this._factory?.format !== "xlsb") return filePath;
|
|
65
|
+
switch (filePath) {
|
|
66
|
+
case "xl/workbook.xml":
|
|
67
|
+
return "xl/workbook.bin";
|
|
68
|
+
case "xl/sharedStrings.xml":
|
|
69
|
+
return "xl/sharedStrings.bin";
|
|
70
|
+
case "xl/styles.xml":
|
|
71
|
+
return "xl/styles.bin";
|
|
72
|
+
case "xl/_rels/workbook.xml.rels":
|
|
73
|
+
return "xl/_rels/workbook.bin.rels";
|
|
74
|
+
default: {
|
|
75
|
+
const m = /^(xl\/worksheets\/_rels\/sheet\d+)\.xml\.rels$/.exec(filePath);
|
|
76
|
+
return m != null ? `${m[1]}.bin.rels` : filePath;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
39
79
|
}
|
|
40
80
|
|
|
41
|
-
async get(filePath: string): Promise<
|
|
42
|
-
|
|
43
|
-
|
|
81
|
+
async get(filePath: string): Promise<IExcelModel | Bytes | undefined> {
|
|
82
|
+
const factory = await this._resolveFactory();
|
|
83
|
+
const path = this._resolvePath(filePath);
|
|
84
|
+
|
|
85
|
+
if (this._cache.has(path)) {
|
|
86
|
+
return this._cache.get(path);
|
|
44
87
|
}
|
|
45
88
|
|
|
46
|
-
const fileData = await this._zip.get(
|
|
89
|
+
const fileData = await this._zip.get(path);
|
|
47
90
|
if (fileData == null) {
|
|
48
|
-
this._cache.set(
|
|
91
|
+
this._cache.set(path, undefined);
|
|
49
92
|
return undefined;
|
|
50
93
|
}
|
|
51
94
|
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
const xml = xmlU.parse(fileText, { stripTagPrefix: true });
|
|
55
|
-
if (filePath.endsWith(".rels")) {
|
|
56
|
-
this._cache.set(filePath, new ExcelXmlRelationship(xml as ExcelXmlRelationshipData));
|
|
57
|
-
} else if (filePath === "[Content_Types].xml") {
|
|
58
|
-
this._cache.set(filePath, new ExcelXmlContentType(xml as ExcelXmlContentTypeData));
|
|
59
|
-
} else if (filePath === "xl/workbook.xml") {
|
|
60
|
-
this._cache.set(filePath, new ExcelXmlWorkbook(xml as ExcelXmlWorkbookData));
|
|
61
|
-
} else if (filePath.startsWith("xl/worksheets/sheet") && filePath.endsWith(".xml")) {
|
|
62
|
-
this._cache.set(filePath, new ExcelXmlWorksheet(xml as ExcelXmlWorksheetData));
|
|
63
|
-
} else if (filePath.startsWith("xl/drawings/drawing") && filePath.endsWith(".xml")) {
|
|
64
|
-
this._cache.set(filePath, new ExcelXmlDrawing(xml as ExcelXmlDrawingData));
|
|
65
|
-
} else if (filePath === "xl/sharedStrings.xml") {
|
|
66
|
-
this._cache.set(filePath, new ExcelXmlSharedString(xml as ExcelXmlSharedStringData));
|
|
67
|
-
} else if (filePath === "xl/styles.xml") {
|
|
68
|
-
this._cache.set(filePath, new ExcelXmlStyle(xml as ExcelXmlStyleData));
|
|
69
|
-
} else {
|
|
70
|
-
this._cache.set(filePath, new ExcelXmlUnknown(xml as Record<string, unknown>));
|
|
71
|
-
}
|
|
95
|
+
if (factory.isModelPart(path)) {
|
|
96
|
+
this._cache.set(path, factory.parse(path, fileData));
|
|
72
97
|
} else {
|
|
73
|
-
this._cache.set(
|
|
98
|
+
this._cache.set(path, fileData);
|
|
74
99
|
}
|
|
75
100
|
|
|
76
|
-
return this._cache.get(
|
|
101
|
+
return this._cache.get(path);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
set(filePath: string, content: IExcelModel | Bytes): void {
|
|
105
|
+
this._cache.set(this._resolvePath(filePath), content);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#region Factory Delegation (빈 모델 생성)
|
|
109
|
+
|
|
110
|
+
createWorkbook(): IWorkbookModel {
|
|
111
|
+
return this._syncFactory.createWorkbook();
|
|
77
112
|
}
|
|
113
|
+
createWorksheet(): IWorksheetModel {
|
|
114
|
+
return this._syncFactory.createWorksheet();
|
|
115
|
+
}
|
|
116
|
+
createStyle(): IStyleModel {
|
|
117
|
+
return this._syncFactory.createStyle();
|
|
118
|
+
}
|
|
119
|
+
createSharedString(): ISharedStringModel {
|
|
120
|
+
return this._syncFactory.createSharedString();
|
|
121
|
+
}
|
|
122
|
+
createContentType(): IContentTypeModel {
|
|
123
|
+
return this._syncFactory.createContentType();
|
|
124
|
+
}
|
|
125
|
+
createRelationship(): IRelationshipModel {
|
|
126
|
+
return this._syncFactory.createRelationship();
|
|
127
|
+
}
|
|
128
|
+
createDrawing(): IDrawingModel {
|
|
129
|
+
return this._syncFactory.createDrawing();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
|
|
134
|
+
//#region Part 등록 (포맷별 content-type·rel·part 생성)
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 워크시트 파트를 등록한다. content-type override, workbook rels, 빈 worksheet 모델을 포맷에 맞게 생성하고
|
|
138
|
+
* worksheet 파일명(`sheetN.xml` | `sheetN.bin`)을 반환한다.
|
|
139
|
+
*/
|
|
140
|
+
async registerWorksheet(relId: number): Promise<string> {
|
|
141
|
+
const xlsb = this.format === "xlsb";
|
|
142
|
+
const fileName = `sheet${relId}.${xlsb ? "bin" : "xml"}`;
|
|
143
|
+
|
|
144
|
+
const typeXml = (await this.get("[Content_Types].xml")) as IContentTypeModel;
|
|
145
|
+
typeXml.add(
|
|
146
|
+
`/xl/worksheets/${fileName}`,
|
|
147
|
+
xlsb
|
|
148
|
+
? "application/vnd.ms-excel.worksheet"
|
|
149
|
+
: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const wbRel = (await this.get("xl/_rels/workbook.xml.rels")) as IRelationshipModel;
|
|
153
|
+
wbRel.insert(
|
|
154
|
+
relId,
|
|
155
|
+
`worksheets/${fileName}`,
|
|
156
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet",
|
|
157
|
+
);
|
|
78
158
|
|
|
79
|
-
|
|
80
|
-
|
|
159
|
+
this.set(`xl/worksheets/${fileName}`, this.createWorksheet());
|
|
160
|
+
return fileName;
|
|
81
161
|
}
|
|
82
162
|
|
|
163
|
+
/** sharedStrings 파트를 보장(없으면 생성+등록)하고 모델을 반환한다. */
|
|
164
|
+
async ensureSharedStrings(): Promise<ISharedStringModel> {
|
|
165
|
+
let ss = (await this.get("xl/sharedStrings.xml")) as ISharedStringModel | undefined;
|
|
166
|
+
if (ss == null) {
|
|
167
|
+
const xlsb = this.format === "xlsb";
|
|
168
|
+
ss = this.createSharedString();
|
|
169
|
+
this.set("xl/sharedStrings.xml", ss);
|
|
170
|
+
|
|
171
|
+
const typeXml = (await this.get("[Content_Types].xml")) as IContentTypeModel;
|
|
172
|
+
typeXml.add(
|
|
173
|
+
`/xl/sharedStrings.${xlsb ? "bin" : "xml"}`,
|
|
174
|
+
xlsb
|
|
175
|
+
? "application/vnd.ms-excel.sharedStrings"
|
|
176
|
+
: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const wbRel = (await this.get("xl/_rels/workbook.xml.rels")) as IRelationshipModel;
|
|
180
|
+
wbRel.add(
|
|
181
|
+
`sharedStrings.${xlsb ? "bin" : "xml"}`,
|
|
182
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return ss;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** styles 파트를 보장(없으면 생성+등록)하고 모델을 반환한다. */
|
|
189
|
+
async ensureStyles(): Promise<IStyleModel> {
|
|
190
|
+
let st = (await this.get("xl/styles.xml")) as IStyleModel | undefined;
|
|
191
|
+
if (st == null) {
|
|
192
|
+
const xlsb = this.format === "xlsb";
|
|
193
|
+
st = this.createStyle();
|
|
194
|
+
this.set("xl/styles.xml", st);
|
|
195
|
+
|
|
196
|
+
const typeXml = (await this.get("[Content_Types].xml")) as IContentTypeModel;
|
|
197
|
+
typeXml.add(
|
|
198
|
+
`/xl/styles.${xlsb ? "bin" : "xml"}`,
|
|
199
|
+
xlsb
|
|
200
|
+
? "application/vnd.ms-excel.styles"
|
|
201
|
+
: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const wbRel = (await this.get("xl/_rels/workbook.xml.rels")) as IRelationshipModel;
|
|
205
|
+
wbRel.add(
|
|
206
|
+
`styles.${xlsb ? "bin" : "xml"}`,
|
|
207
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
return st;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
|
|
83
215
|
async toBytes(): Promise<Bytes> {
|
|
84
216
|
for (const filePath of this._cache.keys()) {
|
|
85
217
|
const content = this._cache.get(filePath);
|
|
86
218
|
if (content == null) continue;
|
|
87
219
|
|
|
88
|
-
if ("
|
|
89
|
-
content.
|
|
90
|
-
this._zip.write(filePath, new TextEncoder().encode(xmlU.stringify(content.data)));
|
|
220
|
+
if ("serialize" in content) {
|
|
221
|
+
this._zip.write(filePath, content.serialize());
|
|
91
222
|
} else {
|
|
92
223
|
this._zip.write(filePath, content);
|
|
93
224
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import { xml as xmlU } from "@simplysm/core-common";
|
|
3
|
+
import type { IContentTypeModel } from "../models/i-content-type-model";
|
|
4
|
+
import type { ExcelXmlContentTypeData } from "../types";
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* [Content_Types].xml을 관리하는 클래스.
|
|
5
8
|
* 파일별 MIME 타입 정보를 관리한다.
|
|
6
9
|
*/
|
|
7
|
-
export class ExcelXmlContentType implements
|
|
8
|
-
|
|
10
|
+
export class ExcelXmlContentType implements IContentTypeModel {
|
|
11
|
+
private readonly _data: ExcelXmlContentTypeData;
|
|
9
12
|
|
|
10
13
|
constructor(data?: ExcelXmlContentTypeData) {
|
|
11
14
|
if (data == null) {
|
|
12
|
-
this.
|
|
15
|
+
this._data = {
|
|
13
16
|
Types: {
|
|
14
17
|
$: {
|
|
15
18
|
xmlns: "http://schemas.openxmlformats.org/package/2006/content-types",
|
|
@@ -40,18 +43,23 @@ export class ExcelXmlContentType implements ExcelXml {
|
|
|
40
43
|
},
|
|
41
44
|
};
|
|
42
45
|
} else {
|
|
43
|
-
this.
|
|
46
|
+
this._data = data;
|
|
44
47
|
}
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
51
|
+
get data(): ExcelXmlContentTypeData {
|
|
52
|
+
return this._data;
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
add(partName: string, contentType: string): this {
|
|
48
56
|
// 중복 검사
|
|
49
|
-
const exists = this.
|
|
57
|
+
const exists = this._data.Types.Override.some((item) => item.$.PartName === partName);
|
|
50
58
|
if (exists) {
|
|
51
59
|
return this;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
this.
|
|
62
|
+
this._data.Types.Override.push({
|
|
55
63
|
$: {
|
|
56
64
|
PartName: partName,
|
|
57
65
|
ContentType: contentType,
|
|
@@ -61,5 +69,7 @@ export class ExcelXmlContentType implements ExcelXml {
|
|
|
61
69
|
return this;
|
|
62
70
|
}
|
|
63
71
|
|
|
64
|
-
|
|
72
|
+
serialize(): Bytes {
|
|
73
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
74
|
+
}
|
|
65
75
|
}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import { xml as xmlU } from "@simplysm/core-common";
|
|
3
|
+
import type { IDrawingModel } from "../models/i-drawing-model";
|
|
4
|
+
import type { ExcelXmlDrawingData } from "../types";
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* xl/drawings/drawing*.xml 파일을 관리하는 클래스.
|
|
5
8
|
* 이미지 삽입을 위한 위치 및 참조 정보를 처리한다.
|
|
6
9
|
*/
|
|
7
|
-
export class ExcelXmlDrawing implements
|
|
8
|
-
|
|
10
|
+
export class ExcelXmlDrawing implements IDrawingModel {
|
|
11
|
+
private readonly _data: ExcelXmlDrawingData;
|
|
9
12
|
|
|
10
13
|
constructor(data?: ExcelXmlDrawingData) {
|
|
11
14
|
if (data == null) {
|
|
12
|
-
this.
|
|
15
|
+
this._data = {
|
|
13
16
|
wsDr: {
|
|
14
17
|
$: {
|
|
15
18
|
"xmlns": "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing",
|
|
@@ -20,22 +23,27 @@ export class ExcelXmlDrawing implements ExcelXml {
|
|
|
20
23
|
},
|
|
21
24
|
};
|
|
22
25
|
} else {
|
|
23
|
-
this.
|
|
26
|
+
this._data = data;
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
31
|
+
get data(): ExcelXmlDrawingData {
|
|
32
|
+
return this._data;
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
addPicture(opts: {
|
|
28
36
|
from: { r: number; c: number; rOff?: number | string; cOff?: number | string };
|
|
29
37
|
to: { r: number; c: number; rOff?: number | string; cOff?: number | string };
|
|
30
38
|
blipRelId: string;
|
|
31
39
|
}): void {
|
|
32
|
-
this.
|
|
40
|
+
this._data.wsDr.twoCellAnchor = this._data.wsDr.twoCellAnchor ?? [];
|
|
33
41
|
|
|
34
|
-
const anchors = this.
|
|
42
|
+
const anchors = this._data.wsDr.twoCellAnchor;
|
|
35
43
|
const picId = anchors.length + 1;
|
|
36
44
|
const name = `Picture ${picId}`;
|
|
37
45
|
|
|
38
|
-
this.
|
|
46
|
+
this._data.wsDr.twoCellAnchor.push({
|
|
39
47
|
from: [
|
|
40
48
|
{
|
|
41
49
|
col: [opts.from.c.toString()],
|
|
@@ -83,5 +91,7 @@ export class ExcelXmlDrawing implements ExcelXml {
|
|
|
83
91
|
});
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
serialize(): Bytes {
|
|
95
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
96
|
+
}
|
|
87
97
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import "@simplysm/core-common";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
3
|
+
import { num, xml as xmlU } from "@simplysm/core-common";
|
|
4
|
+
import type { IRelationshipModel } from "../models/i-relationship-model";
|
|
5
|
+
import type { ExcelRelationshipData, ExcelXmlRelationshipData } from "../types";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* *.rels 파일을 관리하는 클래스.
|
|
7
9
|
* 파일 간의 참조 관계를 처리한다.
|
|
8
10
|
*/
|
|
9
|
-
export class ExcelXmlRelationship implements
|
|
10
|
-
|
|
11
|
+
export class ExcelXmlRelationship implements IRelationshipModel {
|
|
12
|
+
private readonly _data: ExcelXmlRelationshipData;
|
|
11
13
|
|
|
12
14
|
constructor(data?: ExcelXmlRelationshipData) {
|
|
13
15
|
if (data == null) {
|
|
14
|
-
this.
|
|
16
|
+
this._data = {
|
|
15
17
|
Relationships: {
|
|
16
18
|
$: {
|
|
17
19
|
xmlns: "http://schemas.openxmlformats.org/package/2006/relationships",
|
|
@@ -19,12 +21,17 @@ export class ExcelXmlRelationship implements ExcelXml {
|
|
|
19
21
|
},
|
|
20
22
|
};
|
|
21
23
|
} else {
|
|
22
|
-
this.
|
|
24
|
+
this._data = data;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
29
|
+
get data(): ExcelXmlRelationshipData {
|
|
30
|
+
return this._data;
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
getTargetByRelId(rId: number): string | undefined {
|
|
27
|
-
return (this.
|
|
34
|
+
return (this._data.Relationships.Relationship ?? []).single((rel) => this._getRelId(rel) === rId)
|
|
28
35
|
?.$.Target;
|
|
29
36
|
}
|
|
30
37
|
|
|
@@ -34,11 +41,11 @@ export class ExcelXmlRelationship implements ExcelXml {
|
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
addAndGetId(target: string, type: string): number {
|
|
37
|
-
this.
|
|
44
|
+
this._data.Relationships.Relationship = this._data.Relationships.Relationship ?? [];
|
|
38
45
|
|
|
39
46
|
const newId = (this._lastId ?? 0) + 1;
|
|
40
47
|
|
|
41
|
-
this.
|
|
48
|
+
this._data.Relationships.Relationship.push({
|
|
42
49
|
$: {
|
|
43
50
|
Id: `rId${newId}`,
|
|
44
51
|
Target: target,
|
|
@@ -50,16 +57,16 @@ export class ExcelXmlRelationship implements ExcelXml {
|
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
insert(rId: number, target: string, type: string): this {
|
|
53
|
-
this.
|
|
60
|
+
this._data.Relationships.Relationship = this._data.Relationships.Relationship ?? [];
|
|
54
61
|
|
|
55
|
-
const shiftRels = this.
|
|
62
|
+
const shiftRels = this._data.Relationships.Relationship.filter(
|
|
56
63
|
(rel) => this._getRelId(rel) >= rId,
|
|
57
64
|
);
|
|
58
65
|
for (const shiftRel of shiftRels) {
|
|
59
66
|
shiftRel.$.Id = `rId${this._getRelId(shiftRel) + 1}`;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
this.
|
|
69
|
+
this._data.Relationships.Relationship.push({
|
|
63
70
|
$: {
|
|
64
71
|
Id: `rId${rId}`,
|
|
65
72
|
Target: target,
|
|
@@ -70,10 +77,17 @@ export class ExcelXmlRelationship implements ExcelXml {
|
|
|
70
77
|
return this;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
findRelByType(type: string): { relId: string; target: string } | undefined {
|
|
81
|
+
const rel = (this._data.Relationships.Relationship ?? []).find((r) => r.$.Type === type);
|
|
82
|
+
return rel != null ? { relId: rel.$.Id, target: rel.$.Target } : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
serialize(): Bytes {
|
|
86
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
87
|
+
}
|
|
74
88
|
|
|
75
89
|
private get _lastId(): number | undefined {
|
|
76
|
-
const rels = this.
|
|
90
|
+
const rels = this._data.Relationships.Relationship;
|
|
77
91
|
if (!rels || rels.length === 0) return undefined;
|
|
78
92
|
const maxRel = rels.orderByDesc((rel) => this._getRelId(rel)).first();
|
|
79
93
|
return maxRel ? this._getRelId(maxRel) : undefined;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import { xml as xmlU } from "@simplysm/core-common";
|
|
3
|
+
import type { ISharedStringModel } from "../models/i-shared-string-model";
|
|
1
4
|
import type {
|
|
2
|
-
ExcelXml,
|
|
3
5
|
ExcelXmlSharedStringData,
|
|
4
6
|
ExcelXmlSharedStringDataSi,
|
|
5
7
|
ExcelXmlSharedStringDataText,
|
|
@@ -10,14 +12,14 @@ import "@simplysm/core-common";
|
|
|
10
12
|
* xl/sharedStrings.xml을 관리하는 클래스.
|
|
11
13
|
* 문자열 중복을 방지하여 파일 크기를 최적화한다.
|
|
12
14
|
*/
|
|
13
|
-
export class ExcelXmlSharedString implements
|
|
14
|
-
|
|
15
|
+
export class ExcelXmlSharedString implements ISharedStringModel {
|
|
16
|
+
private readonly _data: ExcelXmlSharedStringData;
|
|
15
17
|
|
|
16
18
|
private readonly _stringIndexesMap: Map<string, number[]>;
|
|
17
19
|
|
|
18
20
|
constructor(data?: ExcelXmlSharedStringData) {
|
|
19
21
|
if (data == null) {
|
|
20
|
-
this.
|
|
22
|
+
this._data = {
|
|
21
23
|
sst: {
|
|
22
24
|
$: {
|
|
23
25
|
xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -25,11 +27,11 @@ export class ExcelXmlSharedString implements ExcelXml {
|
|
|
25
27
|
},
|
|
26
28
|
};
|
|
27
29
|
} else {
|
|
28
|
-
this.
|
|
30
|
+
this._data = data;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
this._stringIndexesMap = this.
|
|
32
|
-
? this.
|
|
33
|
+
this._stringIndexesMap = this._data.sst.si
|
|
34
|
+
? this._data.sst.si
|
|
33
35
|
.map((tag, id) => ({ id, tag }))
|
|
34
36
|
.filter((item) => !this._getHasInnerStyleOnSiTag(item.tag))
|
|
35
37
|
.toArrayMap(
|
|
@@ -39,24 +41,31 @@ export class ExcelXmlSharedString implements ExcelXml {
|
|
|
39
41
|
: new Map<string, number[]>();
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
45
|
+
get data(): ExcelXmlSharedStringData {
|
|
46
|
+
return this._data;
|
|
47
|
+
}
|
|
48
|
+
|
|
42
49
|
getIdByString(str: string): number | undefined {
|
|
43
50
|
return this._stringIndexesMap.get(str)?.[0];
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
getStringById(id: number): string | undefined {
|
|
47
|
-
const si = this.
|
|
54
|
+
const si = this._data.sst.si?.[id];
|
|
48
55
|
return si != null ? this._getStringFromSiTag(si) : undefined;
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
add(str: string): number {
|
|
52
|
-
this.
|
|
53
|
-
const newLength = this.
|
|
59
|
+
this._data.sst.si = this._data.sst.si ?? [];
|
|
60
|
+
const newLength = this._data.sst.si.push({ t: [str] });
|
|
54
61
|
const arr = this._stringIndexesMap.getOrCreate(str, []);
|
|
55
62
|
arr.push(newLength - 1);
|
|
56
63
|
return newLength - 1;
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
serialize(): Bytes {
|
|
67
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
68
|
+
}
|
|
60
69
|
|
|
61
70
|
private _getStringFromSiTag(si: ExcelXmlSharedStringDataSi): string {
|
|
62
71
|
if ("t" in si) {
|