@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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import { num, obj, xml as xmlU } from "@simplysm/core-common";
|
|
3
|
+
import "@simplysm/core-common";
|
|
4
|
+
import type { IStyleModel } from "../models/i-style-model";
|
|
5
|
+
import { convertExcelStyleOptions, type ExcelStyle } from "../models/shared/excel-style";
|
|
1
6
|
import type {
|
|
2
|
-
ExcelBorderPosition,
|
|
3
7
|
ExcelConditionalRuleStyle,
|
|
4
8
|
ExcelFont,
|
|
5
|
-
ExcelHorizontalAlign,
|
|
6
|
-
ExcelStyleOptions,
|
|
7
|
-
ExcelVerticalAlign,
|
|
8
|
-
ExcelXml,
|
|
9
9
|
ExcelXmlStyleData,
|
|
10
10
|
ExcelXmlStyleDataBorder,
|
|
11
11
|
ExcelXmlStyleDataDxf,
|
|
@@ -13,73 +13,21 @@ import type {
|
|
|
13
13
|
ExcelXmlStyleDataFont,
|
|
14
14
|
ExcelXmlStyleDataXf,
|
|
15
15
|
} from "../types";
|
|
16
|
-
import "
|
|
17
|
-
import { num, obj } from "@simplysm/core-common";
|
|
18
|
-
import { ExcelUtils } from "../utils/excel-utils";
|
|
19
|
-
|
|
20
|
-
export interface ExcelStyle {
|
|
21
|
-
numFmtId?: string;
|
|
22
|
-
numFmtCode?: string;
|
|
23
|
-
border?: ExcelBorderPosition[];
|
|
24
|
-
background?: string;
|
|
25
|
-
verticalAlign?: ExcelVerticalAlign;
|
|
26
|
-
horizontalAlign?: ExcelHorizontalAlign;
|
|
27
|
-
font?: ExcelFont;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* `ExcelStyleOptions` (사용자 표면) → 내부 `ExcelStyle` 변환.
|
|
32
|
-
* cell.setStyle 과 wb.setDefaultStyle 이 공유한다.
|
|
33
|
-
*
|
|
34
|
-
* - background ARGB 8자리 형식 검증
|
|
35
|
-
* - numberFormatCode 가 numberFormat 보다 우선
|
|
36
|
-
* - font 는 그대로 전달 (구체 검증은 ExcelXmlStyle 내부에서 수행)
|
|
37
|
-
*/
|
|
38
|
-
export function convertExcelStyleOptions(opts: ExcelStyleOptions): ExcelStyle {
|
|
39
|
-
const style: ExcelStyle = {};
|
|
40
|
-
|
|
41
|
-
if (opts.background != null) {
|
|
42
|
-
if (!/^[0-9A-F]{8}$/i.test(opts.background)) {
|
|
43
|
-
throw new Error("잘못된 색상 형식입니다. (형식: 00000000: alpha(반전)+rgb)");
|
|
44
|
-
}
|
|
45
|
-
style.background = opts.background;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (opts.border != null) {
|
|
49
|
-
style.border = opts.border;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (opts.horizontalAlign != null) {
|
|
53
|
-
style.horizontalAlign = opts.horizontalAlign;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (opts.verticalAlign != null) {
|
|
57
|
-
style.verticalAlign = opts.verticalAlign;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (opts.numberFormatCode != null) {
|
|
61
|
-
style.numFmtCode = opts.numberFormatCode;
|
|
62
|
-
} else if (opts.numberFormat != null) {
|
|
63
|
-
style.numFmtId = ExcelUtils.convertNumFmtNameToId(opts.numberFormat).toString();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (opts.font != null) {
|
|
67
|
-
style.font = opts.font;
|
|
68
|
-
}
|
|
16
|
+
import type { ExcelBorderPosition } from "../types";
|
|
69
17
|
|
|
70
|
-
|
|
71
|
-
}
|
|
18
|
+
// 구현 중립 위치(models/shared)로 이전. 기존 import 경로 호환을 위해 re-export 유지.
|
|
19
|
+
export { convertExcelStyleOptions, type ExcelStyle };
|
|
72
20
|
|
|
73
21
|
/**
|
|
74
22
|
* xl/styles.xml을 관리하는 클래스.
|
|
75
23
|
* 숫자 형식, 배경색, 테두리, 정렬 등의 스타일을 처리한다.
|
|
76
24
|
*/
|
|
77
|
-
export class ExcelXmlStyle implements
|
|
78
|
-
|
|
25
|
+
export class ExcelXmlStyle implements IStyleModel {
|
|
26
|
+
private readonly _data: ExcelXmlStyleData;
|
|
79
27
|
|
|
80
28
|
constructor(data?: ExcelXmlStyleData) {
|
|
81
29
|
if (data == null) {
|
|
82
|
-
this.
|
|
30
|
+
this._data = {
|
|
83
31
|
styleSheet: {
|
|
84
32
|
$: {
|
|
85
33
|
xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -114,10 +62,15 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
114
62
|
},
|
|
115
63
|
};
|
|
116
64
|
} else {
|
|
117
|
-
this.
|
|
65
|
+
this._data = data;
|
|
118
66
|
}
|
|
119
67
|
}
|
|
120
68
|
|
|
69
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
70
|
+
get data(): ExcelXmlStyleData {
|
|
71
|
+
return this._data;
|
|
72
|
+
}
|
|
73
|
+
|
|
121
74
|
add(style: ExcelStyle): string {
|
|
122
75
|
const newXf: ExcelXmlStyleDataXf = { $: {} };
|
|
123
76
|
this._applyStyleToXf(newXf, style);
|
|
@@ -134,17 +87,17 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
134
87
|
* 미호출 시 기존 cellXfs[0]·0번 슬롯 모두 그대로 보존된다.
|
|
135
88
|
*/
|
|
136
89
|
setDefaultStyle(style: ExcelStyle): void {
|
|
137
|
-
this.
|
|
138
|
-
this.
|
|
139
|
-
this.
|
|
90
|
+
this._data.styleSheet.fonts[0].font[0] = {};
|
|
91
|
+
this._data.styleSheet.fills[0].fill[0] = { patternFill: [{ $: { patternType: "none" } }] };
|
|
92
|
+
this._data.styleSheet.borders[0].border[0] = {};
|
|
140
93
|
|
|
141
94
|
if (style.font != null) {
|
|
142
95
|
this._validateFont(style.font);
|
|
143
|
-
this.
|
|
96
|
+
this._data.styleSheet.fonts[0].font[0] = this._buildFontXml(style.font);
|
|
144
97
|
}
|
|
145
98
|
|
|
146
99
|
if (style.background != null) {
|
|
147
|
-
this.
|
|
100
|
+
this._data.styleSheet.fills[0].fill[0] = {
|
|
148
101
|
patternFill: [
|
|
149
102
|
{
|
|
150
103
|
$: { patternType: "solid" },
|
|
@@ -155,7 +108,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
155
108
|
}
|
|
156
109
|
|
|
157
110
|
if (style.border != null) {
|
|
158
|
-
this.
|
|
111
|
+
this._data.styleSheet.borders[0].border[0] = this._createBorderFromPositions(style.border);
|
|
159
112
|
}
|
|
160
113
|
|
|
161
114
|
const newXf: ExcelXmlStyleDataXf = { $: { numFmtId: "0" } };
|
|
@@ -170,7 +123,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
170
123
|
}
|
|
171
124
|
|
|
172
125
|
this._applyAlignment(newXf, style);
|
|
173
|
-
this.
|
|
126
|
+
this._data.styleSheet.cellXfs[0].xf[0] = newXf;
|
|
174
127
|
}
|
|
175
128
|
|
|
176
129
|
addWithClone(id: string, style: ExcelStyle): string {
|
|
@@ -178,7 +131,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
178
131
|
if (idNum == null) {
|
|
179
132
|
throw new Error(`잘못된 스타일 ID: ${id}`);
|
|
180
133
|
}
|
|
181
|
-
const xfArray = this.
|
|
134
|
+
const xfArray = this._data.styleSheet.cellXfs[0].xf;
|
|
182
135
|
if (idNum < 0 || idNum >= xfArray.length) {
|
|
183
136
|
throw new Error(`존재하지 않는 스타일 ID: ${id} (범위: 0-${xfArray.length - 1})`);
|
|
184
137
|
}
|
|
@@ -197,7 +150,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
197
150
|
if (style.background != null) {
|
|
198
151
|
const fillIdNum = cloneXf.$.fillId != null ? num.parseInt(cloneXf.$.fillId) : undefined;
|
|
199
152
|
const prevFill =
|
|
200
|
-
fillIdNum != null ? this.
|
|
153
|
+
fillIdNum != null ? this._data.styleSheet.fills[0].fill[fillIdNum] : undefined;
|
|
201
154
|
|
|
202
155
|
if (prevFill != null) {
|
|
203
156
|
const cloneFill = obj.clone(prevFill);
|
|
@@ -229,7 +182,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
229
182
|
const borderIdNum =
|
|
230
183
|
cloneXf.$.borderId != null ? num.parseInt(cloneXf.$.borderId) : undefined;
|
|
231
184
|
const prevBorder =
|
|
232
|
-
borderIdNum != null ? this.
|
|
185
|
+
borderIdNum != null ? this._data.styleSheet.borders[0].border[borderIdNum] : undefined;
|
|
233
186
|
|
|
234
187
|
if (prevBorder != null) {
|
|
235
188
|
const cloneBorder = obj.clone(prevBorder);
|
|
@@ -263,7 +216,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
263
216
|
if (idNum == null) {
|
|
264
217
|
throw new Error(`잘못된 스타일 ID: ${id}`);
|
|
265
218
|
}
|
|
266
|
-
const xf = this.
|
|
219
|
+
const xf = this._data.styleSheet.cellXfs[0].xf[idNum] as ExcelXmlStyleDataXf | undefined;
|
|
267
220
|
|
|
268
221
|
const result: ExcelStyle = {};
|
|
269
222
|
|
|
@@ -273,12 +226,12 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
273
226
|
if (xf.$.fillId != null) {
|
|
274
227
|
const fillIdNum = num.parseInt(xf.$.fillId);
|
|
275
228
|
if (fillIdNum != null) {
|
|
276
|
-
const fill = this.
|
|
229
|
+
const fill = this._data.styleSheet.fills[0].fill[fillIdNum] as
|
|
277
230
|
| ExcelXmlStyleDataFill
|
|
278
231
|
| undefined;
|
|
279
232
|
if (fill == null) {
|
|
280
233
|
throw new Error(
|
|
281
|
-
`존재하지 않는 fill ID: ${xf.$.fillId} (범위: 0-${this.
|
|
234
|
+
`존재하지 않는 fill ID: ${xf.$.fillId} (범위: 0-${this._data.styleSheet.fills[0].fill.length - 1})`,
|
|
282
235
|
);
|
|
283
236
|
}
|
|
284
237
|
result.background = fill.patternFill[0].fgColor?.[0].$.rgb;
|
|
@@ -290,12 +243,12 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
290
243
|
if (borderIdNum == null) {
|
|
291
244
|
throw new Error(`잘못된 border ID: ${xf.$.borderId}`);
|
|
292
245
|
}
|
|
293
|
-
const border = this.
|
|
246
|
+
const border = this._data.styleSheet.borders[0].border[borderIdNum] as
|
|
294
247
|
| ExcelXmlStyleDataBorder
|
|
295
248
|
| undefined;
|
|
296
249
|
if (border == null) {
|
|
297
250
|
throw new Error(
|
|
298
|
-
`존재하지 않는 border ID: ${xf.$.borderId} (범위: 0-${this.
|
|
251
|
+
`존재하지 않는 border ID: ${xf.$.borderId} (범위: 0-${this._data.styleSheet.borders[0].border.length - 1})`,
|
|
299
252
|
);
|
|
300
253
|
}
|
|
301
254
|
if (
|
|
@@ -326,7 +279,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
326
279
|
if (xf.$.fontId != null) {
|
|
327
280
|
const fontIdNum = num.parseInt(xf.$.fontId);
|
|
328
281
|
if (fontIdNum != null) {
|
|
329
|
-
const font = this.
|
|
282
|
+
const font = this._data.styleSheet.fonts[0].font[fontIdNum] as
|
|
330
283
|
| ExcelXmlStyleDataFont
|
|
331
284
|
| undefined;
|
|
332
285
|
if (font != null) {
|
|
@@ -369,7 +322,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
369
322
|
];
|
|
370
323
|
}
|
|
371
324
|
|
|
372
|
-
const dxfs = (this.
|
|
325
|
+
const dxfs = (this._data.styleSheet.dxfs = this._data.styleSheet.dxfs ?? [
|
|
373
326
|
{ $: { count: "0" }, dxf: [] },
|
|
374
327
|
]);
|
|
375
328
|
|
|
@@ -384,21 +337,26 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
384
337
|
}
|
|
385
338
|
|
|
386
339
|
getNumFmtCode(numFmtId: string): string | undefined {
|
|
387
|
-
return (this.
|
|
340
|
+
return (this._data.styleSheet.numFmts?.[0].numFmt ?? []).single(
|
|
388
341
|
(item) => item.$.numFmtId === numFmtId,
|
|
389
342
|
)?.$.formatCode;
|
|
390
343
|
}
|
|
391
344
|
|
|
392
|
-
|
|
345
|
+
serialize(): Bytes {
|
|
346
|
+
this._cleanup();
|
|
347
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private _cleanup(): void {
|
|
393
351
|
const result = {} as ExcelXmlStyleData["styleSheet"];
|
|
394
352
|
|
|
395
353
|
// 정렬 순서 (numFmts를 먼저)
|
|
396
354
|
|
|
397
|
-
if (this.
|
|
398
|
-
result.numFmts = this.
|
|
355
|
+
if (this._data.styleSheet.numFmts != null) {
|
|
356
|
+
result.numFmts = this._data.styleSheet.numFmts;
|
|
399
357
|
}
|
|
400
358
|
|
|
401
|
-
const styleSheetRec = this.
|
|
359
|
+
const styleSheetRec = this._data.styleSheet as Record<string, unknown>;
|
|
402
360
|
const resultRec = result as Record<string, unknown>;
|
|
403
361
|
for (const key of Object.keys(styleSheetRec)) {
|
|
404
362
|
if (key === "numFmts") continue;
|
|
@@ -406,45 +364,45 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
406
364
|
resultRec[key] = styleSheetRec[key];
|
|
407
365
|
}
|
|
408
366
|
|
|
409
|
-
this.
|
|
367
|
+
this._data.styleSheet = result;
|
|
410
368
|
}
|
|
411
369
|
|
|
412
370
|
//#region Private Methods
|
|
413
371
|
|
|
414
372
|
private _setNumFmtCode(numFmtCode: string): string {
|
|
415
373
|
// 코드가 이미 존재하면 건너뛰기
|
|
416
|
-
const existsNumFmtId = (this.
|
|
374
|
+
const existsNumFmtId = (this._data.styleSheet.numFmts?.[0].numFmt ?? []).single(
|
|
417
375
|
(item) => item.$.formatCode === numFmtCode,
|
|
418
376
|
)?.$.numFmtId;
|
|
419
377
|
if (existsNumFmtId != null) {
|
|
420
378
|
return existsNumFmtId;
|
|
421
379
|
}
|
|
422
380
|
|
|
423
|
-
this.
|
|
381
|
+
this._data.styleSheet.numFmts = this._data.styleSheet.numFmts ?? [
|
|
424
382
|
{
|
|
425
383
|
$: { count: "0" },
|
|
426
384
|
numFmt: [],
|
|
427
385
|
},
|
|
428
386
|
];
|
|
429
387
|
|
|
430
|
-
this.
|
|
388
|
+
this._data.styleSheet.numFmts[0].numFmt = this._data.styleSheet.numFmts[0].numFmt ?? [];
|
|
431
389
|
|
|
432
390
|
// Excel 사용자 정의 숫자 형식은 ID 180+부터 시작 (0-163: 내장, 164-179: 예약)
|
|
433
|
-
const numFmts = this.
|
|
391
|
+
const numFmts = this._data.styleSheet.numFmts[0].numFmt;
|
|
434
392
|
const maxItem =
|
|
435
393
|
numFmts.length > 0
|
|
436
394
|
? numFmts.orderByDesc((item) => num.parseInt(item.$.numFmtId) ?? 180).first()
|
|
437
395
|
: undefined;
|
|
438
396
|
const maxId = maxItem ? (num.parseInt(maxItem.$.numFmtId) ?? 180) : 180;
|
|
439
397
|
const nextNumFmtId = (maxId + 1).toString();
|
|
440
|
-
this.
|
|
398
|
+
this._data.styleSheet.numFmts[0].numFmt.push({
|
|
441
399
|
$: {
|
|
442
400
|
numFmtId: nextNumFmtId,
|
|
443
401
|
formatCode: numFmtCode,
|
|
444
402
|
},
|
|
445
403
|
});
|
|
446
|
-
this.
|
|
447
|
-
(num.parseInt(this.
|
|
404
|
+
this._data.styleSheet.numFmts[0].$.count = (
|
|
405
|
+
(num.parseInt(this._data.styleSheet.numFmts[0].$.count) ?? 0) + 1
|
|
448
406
|
).toString();
|
|
449
407
|
|
|
450
408
|
return nextNumFmtId;
|
|
@@ -542,13 +500,13 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
542
500
|
}
|
|
543
501
|
|
|
544
502
|
private _getSameOrCreateFont(item: ExcelXmlStyleDataFont): string {
|
|
545
|
-
const prevSameFont = this.
|
|
503
|
+
const prevSameFont = this._data.styleSheet.fonts[0].font.single((f) => obj.equal(f, item));
|
|
546
504
|
if (prevSameFont != null) {
|
|
547
|
-
return this.
|
|
505
|
+
return this._data.styleSheet.fonts[0].font.indexOf(prevSameFont).toString();
|
|
548
506
|
} else {
|
|
549
|
-
this.
|
|
550
|
-
this.
|
|
551
|
-
return (this.
|
|
507
|
+
this._data.styleSheet.fonts[0].font.push(item);
|
|
508
|
+
this._data.styleSheet.fonts[0].$.count = this._data.styleSheet.fonts[0].font.length.toString();
|
|
509
|
+
return (this._data.styleSheet.fonts[0].font.length - 1).toString();
|
|
552
510
|
}
|
|
553
511
|
}
|
|
554
512
|
|
|
@@ -609,44 +567,44 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
609
567
|
}
|
|
610
568
|
|
|
611
569
|
private _getSameOrCreateXf(xfItem: ExcelXmlStyleDataXf): string {
|
|
612
|
-
const prevSameXf = this.
|
|
570
|
+
const prevSameXf = this._data.styleSheet.cellXfs[0].xf.single((item) => obj.equal(item, xfItem));
|
|
613
571
|
|
|
614
572
|
if (prevSameXf != null) {
|
|
615
|
-
return this.
|
|
573
|
+
return this._data.styleSheet.cellXfs[0].xf.indexOf(prevSameXf).toString();
|
|
616
574
|
} else {
|
|
617
|
-
this.
|
|
618
|
-
this.
|
|
619
|
-
this.
|
|
620
|
-
return (this.
|
|
575
|
+
this._data.styleSheet.cellXfs[0].xf.push(xfItem);
|
|
576
|
+
this._data.styleSheet.cellXfs[0].$.count =
|
|
577
|
+
this._data.styleSheet.cellXfs[0].xf.length.toString();
|
|
578
|
+
return (this._data.styleSheet.cellXfs[0].xf.length - 1).toString();
|
|
621
579
|
}
|
|
622
580
|
}
|
|
623
581
|
|
|
624
582
|
private _getSameOrCreateFill(fillItem: ExcelXmlStyleDataFill): string {
|
|
625
|
-
const prevSameFill = this.
|
|
583
|
+
const prevSameFill = this._data.styleSheet.fills[0].fill.single((item) =>
|
|
626
584
|
obj.equal(item, fillItem),
|
|
627
585
|
);
|
|
628
586
|
|
|
629
587
|
if (prevSameFill != null) {
|
|
630
|
-
return this.
|
|
588
|
+
return this._data.styleSheet.fills[0].fill.indexOf(prevSameFill).toString();
|
|
631
589
|
} else {
|
|
632
|
-
this.
|
|
633
|
-
this.
|
|
634
|
-
return (this.
|
|
590
|
+
this._data.styleSheet.fills[0].fill.push(fillItem);
|
|
591
|
+
this._data.styleSheet.fills[0].$.count = this._data.styleSheet.fills[0].fill.length.toString();
|
|
592
|
+
return (this._data.styleSheet.fills[0].fill.length - 1).toString();
|
|
635
593
|
}
|
|
636
594
|
}
|
|
637
595
|
|
|
638
596
|
private _getSameOrCreateBorder(borderItem: ExcelXmlStyleDataBorder): string {
|
|
639
|
-
const prevSameBorder = this.
|
|
597
|
+
const prevSameBorder = this._data.styleSheet.borders[0].border.single((item) =>
|
|
640
598
|
obj.equal(item, borderItem),
|
|
641
599
|
);
|
|
642
600
|
|
|
643
601
|
if (prevSameBorder != null) {
|
|
644
|
-
return this.
|
|
602
|
+
return this._data.styleSheet.borders[0].border.indexOf(prevSameBorder).toString();
|
|
645
603
|
} else {
|
|
646
|
-
this.
|
|
647
|
-
this.
|
|
648
|
-
this.
|
|
649
|
-
return (this.
|
|
604
|
+
this._data.styleSheet.borders[0].border.push(borderItem);
|
|
605
|
+
this._data.styleSheet.borders[0].$.count =
|
|
606
|
+
this._data.styleSheet.borders[0].border.length.toString();
|
|
607
|
+
return (this._data.styleSheet.borders[0].border.length - 1).toString();
|
|
650
608
|
}
|
|
651
609
|
}
|
|
652
610
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import { xml as xmlU } from "@simplysm/core-common";
|
|
3
|
+
import type { IExcelModel } from "../models/excel-model";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* 알 수 없는 형식의 Excel XML 데이터를 보존하는 클래스.
|
|
5
7
|
* 원본 데이터를 손실 없이 유지한다.
|
|
6
8
|
*/
|
|
7
|
-
export class ExcelXmlUnknown implements
|
|
8
|
-
constructor(
|
|
9
|
+
export class ExcelXmlUnknown implements IExcelModel {
|
|
10
|
+
constructor(private readonly _data: Record<string, unknown>) {}
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
serialize(): Bytes {
|
|
13
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
14
|
+
}
|
|
11
15
|
}
|
|
@@ -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 { IWorkbookModel } from "../models/i-workbook-model";
|
|
5
|
+
import type { ExcelXmlWorkbookData } from "../types";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* xl/workbook.xml을 관리하는 클래스.
|
|
7
9
|
* 워크시트 목록 및 관계 ID를 처리한다.
|
|
8
10
|
*/
|
|
9
|
-
export class ExcelXmlWorkbook implements
|
|
10
|
-
|
|
11
|
+
export class ExcelXmlWorkbook implements IWorkbookModel {
|
|
12
|
+
private readonly _data: ExcelXmlWorkbookData;
|
|
11
13
|
|
|
12
14
|
constructor(data?: ExcelXmlWorkbookData) {
|
|
13
15
|
if (data == null) {
|
|
14
|
-
this.
|
|
16
|
+
this._data = {
|
|
15
17
|
workbook: {
|
|
16
18
|
$: {
|
|
17
19
|
"xmlns": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
|
|
@@ -20,26 +22,31 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
20
22
|
},
|
|
21
23
|
};
|
|
22
24
|
} else {
|
|
23
|
-
this.
|
|
25
|
+
this._data = data;
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
/** @internal 테스트·디버그용 내부 트리 접근. 상위 레이어는 인터페이스만 사용. */
|
|
30
|
+
get data(): ExcelXmlWorkbookData {
|
|
31
|
+
return this._data;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
get lastWsRelId(): number | undefined {
|
|
28
|
-
const sheets = this.
|
|
35
|
+
const sheets = this._data.workbook.sheets?.[0].sheet;
|
|
29
36
|
if (!sheets || sheets.length === 0) return undefined;
|
|
30
37
|
const maxSheet = sheets.orderByDesc((sheet) => num.parseInt(sheet.$["r:id"])!).first();
|
|
31
38
|
return maxSheet ? num.parseInt(maxSheet.$["r:id"]) : undefined;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
get lastSheetId(): number | undefined {
|
|
35
|
-
const sheets = this.
|
|
42
|
+
const sheets = this._data.workbook.sheets?.[0].sheet;
|
|
36
43
|
if (!sheets || sheets.length === 0) return undefined;
|
|
37
44
|
const maxSheet = sheets.orderByDesc((sheet) => num.parseInt(sheet.$.sheetId)!).first();
|
|
38
45
|
return maxSheet ? num.parseInt(maxSheet.$.sheetId) : undefined;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
get sheetNames(): string[] {
|
|
42
|
-
return this.
|
|
49
|
+
return this._data.workbook.sheets?.[0].sheet.map((item) => item.$.name) ?? [];
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
addWorksheet(name: string): this {
|
|
@@ -48,8 +55,8 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
48
55
|
const newWsRelId = (this.lastWsRelId ?? 0) + 1;
|
|
49
56
|
const newSheetId = (this.lastSheetId ?? 0) + 1;
|
|
50
57
|
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
58
|
+
this._data.workbook.sheets = this._data.workbook.sheets ?? [{ sheet: [] }];
|
|
59
|
+
this._data.workbook.sheets[0].sheet.push({
|
|
53
60
|
$: {
|
|
54
61
|
"name": replacedName,
|
|
55
62
|
"sheetId": newSheetId.toString(),
|
|
@@ -60,44 +67,49 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
60
67
|
return this;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
serialize(): Bytes {
|
|
71
|
+
this._cleanup();
|
|
72
|
+
return new TextEncoder().encode(xmlU.stringify(this._data));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private _cleanup(): void {
|
|
64
76
|
const result = {} as ExcelXmlWorkbookData["workbook"];
|
|
65
77
|
|
|
66
78
|
// 정렬 순서 ("sheets" 기준, 나머지는 원래 위치 유지)
|
|
67
79
|
|
|
68
|
-
const workbookRec = this.
|
|
80
|
+
const workbookRec = this._data.workbook as Record<string, unknown>;
|
|
69
81
|
const resultRec = result as Record<string, unknown>;
|
|
70
82
|
|
|
71
|
-
for (const key of Object.keys(this.
|
|
83
|
+
for (const key of Object.keys(this._data.workbook)) {
|
|
72
84
|
if (key === "bookViews") continue;
|
|
73
85
|
|
|
74
86
|
if (key === "sheets") {
|
|
75
|
-
if (this.
|
|
76
|
-
result.bookViews = this.
|
|
87
|
+
if (this._data.workbook.bookViews != null) {
|
|
88
|
+
result.bookViews = this._data.workbook.bookViews;
|
|
77
89
|
}
|
|
78
|
-
result.sheets = this.
|
|
90
|
+
result.sheets = this._data.workbook.sheets;
|
|
79
91
|
} else {
|
|
80
92
|
resultRec[key] = workbookRec[key];
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
this.
|
|
96
|
+
this._data.workbook = result;
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
initializeView(): void {
|
|
88
|
-
this.
|
|
100
|
+
this._data.workbook.bookViews = this._data.workbook.bookViews ?? [{ workbookView: [{}] }];
|
|
89
101
|
}
|
|
90
102
|
|
|
91
103
|
getWsRelIdByName(name: string): number | undefined {
|
|
92
104
|
return num.parseInt(
|
|
93
|
-
(this.
|
|
105
|
+
(this._data.workbook.sheets?.[0].sheet ?? []).single((item) => item.$.name === name)?.$[
|
|
94
106
|
"r:id"
|
|
95
107
|
],
|
|
96
108
|
);
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
getWsRelIdByIndex(index: number): number | undefined {
|
|
100
|
-
return num.parseInt(this.
|
|
112
|
+
return num.parseInt(this._data.workbook.sheets?.[0].sheet[index]?.$["r:id"]);
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
getWorksheetNameById(id: number): string | undefined {
|
|
@@ -114,7 +126,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
114
126
|
}
|
|
115
127
|
|
|
116
128
|
private _getSheetDataById(id: number) {
|
|
117
|
-
return (this.
|
|
129
|
+
return (this._data.workbook.sheets?.[0].sheet ?? []).single(
|
|
118
130
|
(item) => num.parseInt(item.$["r:id"]) === id,
|
|
119
131
|
);
|
|
120
132
|
}
|