@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/excel-worksheet.ts
CHANGED
|
@@ -5,24 +5,22 @@ import mime from "mime";
|
|
|
5
5
|
import { ExcelCell } from "./excel-cell";
|
|
6
6
|
import { ExcelCol } from "./excel-col";
|
|
7
7
|
import { ExcelRow } from "./excel-row";
|
|
8
|
+
import type { IContentTypeModel } from "./models/i-content-type-model";
|
|
9
|
+
import type { IDrawingModel } from "./models/i-drawing-model";
|
|
10
|
+
import type { IRelationshipModel } from "./models/i-relationship-model";
|
|
11
|
+
import type { ISharedStringModel } from "./models/i-shared-string-model";
|
|
12
|
+
import type { IStyleModel } from "./models/i-style-model";
|
|
13
|
+
import type { IWorkbookModel } from "./models/i-workbook-model";
|
|
14
|
+
import type { IWorksheetModel } from "./models/i-worksheet-model";
|
|
15
|
+
import type { ICfRuleSpec } from "./models/shared/excel-cf-spec";
|
|
8
16
|
import type {
|
|
9
17
|
ExcelAddressPoint,
|
|
10
18
|
ExcelAddressRangePoint,
|
|
11
19
|
ExcelConditionalRule,
|
|
12
20
|
ExcelValueType,
|
|
13
|
-
ExcelXmlCfRuleData,
|
|
14
21
|
} from "./types";
|
|
15
22
|
import { ExcelUtils } from "./utils/excel-utils";
|
|
16
23
|
import type { ZipCache } from "./utils/zip-cache";
|
|
17
|
-
import type { ExcelXmlContentType } from "./xml/excel-xml-content-type";
|
|
18
|
-
import { ExcelXmlDrawing } from "./xml/excel-xml-drawing";
|
|
19
|
-
import { ExcelXmlRelationship } from "./xml/excel-xml-relationship";
|
|
20
|
-
import type { ExcelXmlSharedString } from "./xml/excel-xml-shared-string";
|
|
21
|
-
import { ExcelXmlSharedString as ExcelXmlSharedStringClass } from "./xml/excel-xml-shared-string";
|
|
22
|
-
import type { ExcelXmlStyle } from "./xml/excel-xml-style";
|
|
23
|
-
import { ExcelXmlStyle as ExcelXmlStyleClass } from "./xml/excel-xml-style";
|
|
24
|
-
import type { ExcelXmlWorkbook } from "./xml/excel-xml-workbook";
|
|
25
|
-
import type { ExcelXmlWorksheet } from "./xml/excel-xml-worksheet";
|
|
26
24
|
|
|
27
25
|
/**
|
|
28
26
|
* Excel 워크시트를 나타내는 클래스.
|
|
@@ -339,17 +337,6 @@ export class ExcelWorksheet {
|
|
|
339
337
|
* @remarks
|
|
340
338
|
* 같은 시트에 여러 번 호출하면 호출마다 `<conditionalFormatting>` 블록이 누적된다.
|
|
341
339
|
* 정적 셀 스타일과 조건부 서식의 합성은 Excel native CF 오버레이에 위임한다.
|
|
342
|
-
*
|
|
343
|
-
* @example
|
|
344
|
-
* ```typescript
|
|
345
|
-
* await ws.addConditionalFormat({
|
|
346
|
-
* ref: "B2:B100",
|
|
347
|
-
* rules: [
|
|
348
|
-
* { type: "cellIs", op: "<", value: 1000, style: { background: "00FF0000" } },
|
|
349
|
-
* { type: "cellIs", op: "<", value: 4999, style: { background: "00FFFF00" } },
|
|
350
|
-
* ],
|
|
351
|
-
* });
|
|
352
|
-
* ```
|
|
353
340
|
*/
|
|
354
341
|
async addConditionalFormat(opts: {
|
|
355
342
|
ref: string;
|
|
@@ -361,15 +348,7 @@ export class ExcelWorksheet {
|
|
|
361
348
|
const styleData = await this._ensureStyleData();
|
|
362
349
|
const topLeft = opts.ref.split(":")[0];
|
|
363
350
|
|
|
364
|
-
const dxfRules: {
|
|
365
|
-
dxfId: string;
|
|
366
|
-
cfRule: {
|
|
367
|
-
type: ExcelXmlCfRuleData["$"]["type"];
|
|
368
|
-
operator?: ExcelXmlCfRuleData["$"]["operator"];
|
|
369
|
-
formula: string[];
|
|
370
|
-
text?: string;
|
|
371
|
-
};
|
|
372
|
-
}[] = [];
|
|
351
|
+
const dxfRules: { dxfId: string; cfRule: ICfRuleSpec }[] = [];
|
|
373
352
|
|
|
374
353
|
for (const rule of opts.rules) {
|
|
375
354
|
const dxfId = styleData.addDxf(rule.style);
|
|
@@ -383,12 +362,7 @@ export class ExcelWorksheet {
|
|
|
383
362
|
private static _buildCfRuleSpec(
|
|
384
363
|
rule: ExcelConditionalRule,
|
|
385
364
|
topLeft: string,
|
|
386
|
-
): {
|
|
387
|
-
type: ExcelXmlCfRuleData["$"]["type"];
|
|
388
|
-
operator?: ExcelXmlCfRuleData["$"]["operator"];
|
|
389
|
-
formula: string[];
|
|
390
|
-
text?: string;
|
|
391
|
-
} {
|
|
365
|
+
): ICfRuleSpec {
|
|
392
366
|
const encodeLiteral = (v: number | string): string =>
|
|
393
367
|
typeof v === "number" ? v.toString() : `"${v.replaceAll('"', '""')}"`;
|
|
394
368
|
|
|
@@ -434,7 +408,7 @@ export class ExcelWorksheet {
|
|
|
434
408
|
}
|
|
435
409
|
}
|
|
436
410
|
|
|
437
|
-
const operator:
|
|
411
|
+
const operator: ICfRuleSpec["operator"] = (() => {
|
|
438
412
|
switch (rule.op) {
|
|
439
413
|
case "<":
|
|
440
414
|
return "lessThan";
|
|
@@ -493,36 +467,34 @@ export class ExcelWorksheet {
|
|
|
493
467
|
this._zipCache.set(mediaPath, opts.bytes);
|
|
494
468
|
|
|
495
469
|
// 2. [Content_Types].xml 갱신
|
|
496
|
-
const typeXml = (await this._zipCache.get("[Content_Types].xml")) as
|
|
470
|
+
const typeXml = (await this._zipCache.get("[Content_Types].xml")) as IContentTypeModel;
|
|
497
471
|
typeXml.add(`/xl/media/image${mediaIndex}.${opts.ext}`, mimeType);
|
|
498
472
|
|
|
499
473
|
// 3. 워크시트의 기존 drawing 확인
|
|
500
474
|
const wsXml = await this._getWsData();
|
|
501
475
|
const sheetRelsPath = `xl/worksheets/_rels/${this._targetFileName}.rels`;
|
|
502
|
-
let sheetRels = (await this._zipCache.get(sheetRelsPath)) as
|
|
476
|
+
let sheetRels = (await this._zipCache.get(sheetRelsPath)) as IRelationshipModel | undefined;
|
|
503
477
|
|
|
504
478
|
// 기존 drawing 찾기
|
|
505
479
|
let drawingIndex: number | undefined;
|
|
506
480
|
let drawingPath: string | undefined;
|
|
507
|
-
let drawing:
|
|
508
|
-
let drawingRels:
|
|
481
|
+
let drawing: IDrawingModel | undefined;
|
|
482
|
+
let drawingRels: IRelationshipModel | undefined;
|
|
509
483
|
|
|
510
484
|
if (sheetRels != null) {
|
|
511
|
-
const existingDrawingRel = sheetRels.
|
|
512
|
-
|
|
513
|
-
r.$.Type ===
|
|
514
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
485
|
+
const existingDrawingRel = sheetRels.findRelByType(
|
|
486
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
515
487
|
);
|
|
516
488
|
if (existingDrawingRel != null) {
|
|
517
489
|
// 기존 drawing 경로에서 인덱스 추출
|
|
518
|
-
const match = existingDrawingRel
|
|
490
|
+
const match = existingDrawingRel.target.match(/drawing(\d+)\.xml$/);
|
|
519
491
|
if (match != null) {
|
|
520
492
|
drawingIndex = parseInt(match[1], 10);
|
|
521
493
|
drawingPath = `xl/drawings/drawing${drawingIndex}.xml`;
|
|
522
|
-
drawing = (await this._zipCache.get(drawingPath)) as
|
|
494
|
+
drawing = (await this._zipCache.get(drawingPath)) as IDrawingModel | undefined;
|
|
523
495
|
drawingRels = (await this._zipCache.get(
|
|
524
496
|
`xl/drawings/_rels/drawing${drawingIndex}.xml.rels`,
|
|
525
|
-
)) as
|
|
497
|
+
)) as IRelationshipModel | undefined;
|
|
526
498
|
}
|
|
527
499
|
}
|
|
528
500
|
}
|
|
@@ -534,13 +506,13 @@ export class ExcelWorksheet {
|
|
|
534
506
|
drawingIndex++;
|
|
535
507
|
}
|
|
536
508
|
drawingPath = `xl/drawings/drawing${drawingIndex}.xml`;
|
|
537
|
-
drawing =
|
|
509
|
+
drawing = this._zipCache.createDrawing();
|
|
538
510
|
|
|
539
511
|
// [Content_Types].xml에 drawing 타입 추가
|
|
540
512
|
typeXml.add("/" + drawingPath, "application/vnd.openxmlformats-officedocument.drawing+xml");
|
|
541
513
|
|
|
542
514
|
// 워크시트 rels에 drawing 추가
|
|
543
|
-
sheetRels = sheetRels ??
|
|
515
|
+
sheetRels = sheetRels ?? this._zipCache.createRelationship();
|
|
544
516
|
const sheetRelNum = sheetRels.addAndGetId(
|
|
545
517
|
`../drawings/drawing${drawingIndex}.xml`,
|
|
546
518
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
@@ -549,16 +521,12 @@ export class ExcelWorksheet {
|
|
|
549
521
|
this._zipCache.set(sheetRelsPath, sheetRels);
|
|
550
522
|
|
|
551
523
|
// 워크시트 XML에 drawing 추가
|
|
552
|
-
wsXml.
|
|
553
|
-
wsXml.data.worksheet.$["xmlns:r"] ??
|
|
554
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
555
|
-
wsXml.data.worksheet.drawing = wsXml.data.worksheet.drawing ?? [];
|
|
556
|
-
wsXml.data.worksheet.drawing.push({ $: { "r:id": drawingRelIdOnWorksheet } });
|
|
524
|
+
wsXml.setDrawingRelId(drawingRelIdOnWorksheet);
|
|
557
525
|
this._zipCache.set(`xl/worksheets/${this._targetFileName}`, wsXml);
|
|
558
526
|
}
|
|
559
527
|
|
|
560
528
|
// 5. drawing rels 준비 (없으면 생성)
|
|
561
|
-
drawingRels = drawingRels ??
|
|
529
|
+
drawingRels = drawingRels ?? this._zipCache.createRelationship();
|
|
562
530
|
const mediaFileName = mediaPath.slice(3);
|
|
563
531
|
const drawingTarget = `../${mediaFileName}`;
|
|
564
532
|
const relNum = drawingRels.addAndGetId(
|
|
@@ -581,66 +549,27 @@ export class ExcelWorksheet {
|
|
|
581
549
|
|
|
582
550
|
//#region Private Methods
|
|
583
551
|
|
|
584
|
-
private async _getWsData(): Promise<
|
|
585
|
-
return (await this._zipCache.get(`xl/worksheets/${this._targetFileName}`)) as
|
|
552
|
+
private async _getWsData(): Promise<IWorksheetModel> {
|
|
553
|
+
return (await this._zipCache.get(`xl/worksheets/${this._targetFileName}`)) as IWorksheetModel;
|
|
586
554
|
}
|
|
587
555
|
|
|
588
|
-
private async _getWbData(): Promise<
|
|
589
|
-
return (await this._zipCache.get("xl/workbook.xml")) as
|
|
556
|
+
private async _getWbData(): Promise<IWorkbookModel> {
|
|
557
|
+
return (await this._zipCache.get("xl/workbook.xml")) as IWorkbookModel;
|
|
590
558
|
}
|
|
591
559
|
|
|
592
|
-
private async _ensureSsData(): Promise<
|
|
593
|
-
|
|
594
|
-
| ExcelXmlSharedString
|
|
595
|
-
| undefined;
|
|
596
|
-
if (ssData == null) {
|
|
597
|
-
ssData = new ExcelXmlSharedStringClass();
|
|
598
|
-
this._zipCache.set("xl/sharedStrings.xml", ssData);
|
|
599
|
-
|
|
600
|
-
const typeData = (await this._zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
|
|
601
|
-
typeData.add(
|
|
602
|
-
"/xl/sharedStrings.xml",
|
|
603
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
const wbRelData = (await this._zipCache.get(
|
|
607
|
-
"xl/_rels/workbook.xml.rels",
|
|
608
|
-
)) as ExcelXmlRelationship;
|
|
609
|
-
wbRelData.add(
|
|
610
|
-
"sharedStrings.xml",
|
|
611
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings",
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
return ssData;
|
|
560
|
+
private async _ensureSsData(): Promise<ISharedStringModel> {
|
|
561
|
+
return this._zipCache.ensureSharedStrings();
|
|
615
562
|
}
|
|
616
563
|
|
|
617
|
-
private async _ensureStyleData(): Promise<
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
styleData = new ExcelXmlStyleClass();
|
|
621
|
-
this._zipCache.set("xl/styles.xml", styleData);
|
|
622
|
-
|
|
623
|
-
const typeData = (await this._zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
|
|
624
|
-
typeData.add(
|
|
625
|
-
"/xl/styles.xml",
|
|
626
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
627
|
-
);
|
|
628
|
-
|
|
629
|
-
const wbRelData = (await this._zipCache.get(
|
|
630
|
-
"xl/_rels/workbook.xml.rels",
|
|
631
|
-
)) as ExcelXmlRelationship;
|
|
632
|
-
wbRelData.add(
|
|
633
|
-
"styles.xml",
|
|
634
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
635
|
-
);
|
|
636
|
-
}
|
|
637
|
-
return styleData;
|
|
564
|
+
private async _ensureStyleData(): Promise<IStyleModel> {
|
|
565
|
+
// styles 파트 content-type·rel 은 포맷(xlsx/.xml, xlsb/.bin)에 맞춰야 하므로 zipCache 에 위임.
|
|
566
|
+
return this._zipCache.ensureStyles();
|
|
638
567
|
}
|
|
639
568
|
|
|
640
569
|
private _setCellValueSync(
|
|
641
|
-
wsData:
|
|
642
|
-
ssData:
|
|
643
|
-
styleData:
|
|
570
|
+
wsData: IWorksheetModel,
|
|
571
|
+
ssData: ISharedStringModel,
|
|
572
|
+
styleData: IStyleModel,
|
|
644
573
|
addr: ExcelAddressPoint,
|
|
645
574
|
val: ExcelValueType,
|
|
646
575
|
): void {
|
package/src/excel-wrapper.ts
CHANGED
|
@@ -97,16 +97,6 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
97
97
|
* @remarks
|
|
98
98
|
* 반환된 워크북의 리소스 관리는 호출자의 책임이다.
|
|
99
99
|
* 사용 후 `close()`를 호출해야 한다.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```typescript
|
|
103
|
-
* const wb = await wrapper.write("Sheet1", records);
|
|
104
|
-
* try {
|
|
105
|
-
* const bytes = await wb.toBytes();
|
|
106
|
-
* } finally {
|
|
107
|
-
* await wb.close();
|
|
108
|
-
* }
|
|
109
|
-
* ```
|
|
110
100
|
*/
|
|
111
101
|
async write(
|
|
112
102
|
wsName: string,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
import type { ExcelFormat } from "./excel-format";
|
|
3
|
+
import type { IExcelModel } from "./excel-model";
|
|
4
|
+
import type { IContentTypeModel } from "./i-content-type-model";
|
|
5
|
+
import type { IDrawingModel } from "./i-drawing-model";
|
|
6
|
+
import type { IRelationshipModel } from "./i-relationship-model";
|
|
7
|
+
import type { ISharedStringModel } from "./i-shared-string-model";
|
|
8
|
+
import type { IStyleModel } from "./i-style-model";
|
|
9
|
+
import type { IWorkbookModel } from "./i-workbook-model";
|
|
10
|
+
import type { IWorksheetModel } from "./i-worksheet-model";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 포맷별 파트 모델 팩토리 계약.
|
|
14
|
+
*
|
|
15
|
+
* - `createXxx()`: 빈(신규) 파트 모델 생성 — 상위 레이어의 `new XmlXxx()` 직접 생성을 대체.
|
|
16
|
+
* - `parse()`: ZIP 내부 파일 바이트를 경로 패턴에 맞는 파트 모델로 역직렬화 — `ZipCache.get` 의 분기 대체.
|
|
17
|
+
*
|
|
18
|
+
* `ZipCache` 는 워크북 포맷에 맞는 팩토리 1개를 보유하고 모든 파트 생성·복원을 위임한다.
|
|
19
|
+
*/
|
|
20
|
+
export interface IExcelModelFactory {
|
|
21
|
+
readonly format: ExcelFormat;
|
|
22
|
+
/** 이 파일 경로를 모델로 파싱할 파트인지(true) 원시 바이트로 둘지(false) 판정. */
|
|
23
|
+
isModelPart(filePath: string): boolean;
|
|
24
|
+
createWorkbook(): IWorkbookModel;
|
|
25
|
+
createWorksheet(): IWorksheetModel;
|
|
26
|
+
createStyle(): IStyleModel;
|
|
27
|
+
createSharedString(): ISharedStringModel;
|
|
28
|
+
createContentType(): IContentTypeModel;
|
|
29
|
+
createRelationship(): IRelationshipModel;
|
|
30
|
+
createDrawing(): IDrawingModel;
|
|
31
|
+
/** 경로 패턴 → 적절한 파트 모델로 역직렬화. 매칭 없으면 unknown(패스스루) 모델. */
|
|
32
|
+
parse(filePath: string, bytes: Bytes): IExcelModel;
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Bytes } from "@simplysm/core-common";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Excel ZIP 파트 1개의 포맷 중립 모델 계약.
|
|
5
|
+
*
|
|
6
|
+
* 구현체(xml/biff)는 자신이 담당하는 파트를 메모리 모델로 들고 있다가, 직렬화 시점에
|
|
7
|
+
* 자기 포맷의 바이트로 변환한다. 직렬화 직전 정규화(OOXML 자식 순서 재배치 등)는 구현 내부에서 수행한다.
|
|
8
|
+
*/
|
|
9
|
+
export interface IExcelModel {
|
|
10
|
+
/** 이 파트를 자기 포맷의 바이트로 직렬화. */
|
|
11
|
+
serialize(): Bytes;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IExcelModel } from "./excel-model";
|
|
2
|
+
|
|
3
|
+
/** [Content_Types].xml 파트 모델 계약. (xlsb 도 이 파트는 XML.) */
|
|
4
|
+
export interface IContentTypeModel extends IExcelModel {
|
|
5
|
+
/** 파트별 ContentType override 추가. 중복 PartName 은 무시. */
|
|
6
|
+
add(partName: string, contentType: string): this;
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IExcelModel } from "./excel-model";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* xl/drawings/drawing*.xml 파트 모델 계약.
|
|
5
|
+
* xlsb 도 drawing 은 OOXML XML 을 그대로 사용하므로 단일(xml) 구현으로 충분하다.
|
|
6
|
+
*/
|
|
7
|
+
export interface IDrawingModel extends IExcelModel {
|
|
8
|
+
addPicture(opts: {
|
|
9
|
+
from: { r: number; c: number; rOff?: number | string; cOff?: number | string };
|
|
10
|
+
to: { r: number; c: number; rOff?: number | string; cOff?: number | string };
|
|
11
|
+
blipRelId: string;
|
|
12
|
+
}): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IExcelModel } from "./excel-model";
|
|
2
|
+
|
|
3
|
+
/** *.rels 파트 모델 계약. (xlsb 도 이 파트는 XML.) */
|
|
4
|
+
export interface IRelationshipModel extends IExcelModel {
|
|
5
|
+
getTargetByRelId(rId: number): string | undefined;
|
|
6
|
+
add(target: string, type: string): this;
|
|
7
|
+
/** 관계 추가 후 rId 숫자부 반환. */
|
|
8
|
+
addAndGetId(target: string, type: string): number;
|
|
9
|
+
/** 지정 rId 위치에 삽입하고 이후 id 를 시프트. */
|
|
10
|
+
insert(rId: number, target: string, type: string): this;
|
|
11
|
+
/** 특정 Type 의 첫 관계를 찾아 relId(`rId..`)·target 반환. addImage 의 drawing rel 탐색용. */
|
|
12
|
+
findRelByType(type: string): { relId: string; target: string } | undefined;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IExcelModel } from "./excel-model";
|
|
2
|
+
|
|
3
|
+
/** xl/sharedStrings.{xml,bin} 파트 모델 계약. 문자열 중복 제거 테이블. */
|
|
4
|
+
export interface ISharedStringModel extends IExcelModel {
|
|
5
|
+
getIdByString(str: string): number | undefined;
|
|
6
|
+
getStringById(id: number): string | undefined;
|
|
7
|
+
/** 문자열 추가 후 인덱스 반환. */
|
|
8
|
+
add(str: string): number;
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ExcelConditionalRuleStyle } from "../types";
|
|
2
|
+
import type { ExcelStyle } from "./shared/excel-style";
|
|
3
|
+
import type { IExcelModel } from "./excel-model";
|
|
4
|
+
|
|
5
|
+
/** xl/styles.{xml,bin} 파트 모델 계약. numFmt·font·fill·border·xf·dxf 관리. */
|
|
6
|
+
export interface IStyleModel extends IExcelModel {
|
|
7
|
+
/** 스타일을 등록하고 styleId(문자열 핸들) 반환. 동일 스타일은 재사용. */
|
|
8
|
+
add(style: ExcelStyle): string;
|
|
9
|
+
/** 기존 styleId 를 clone 후 일부 속성만 덮어쓴 새 스타일 등록. */
|
|
10
|
+
addWithClone(id: string, style: ExcelStyle): string;
|
|
11
|
+
/** styleId 의 스타일 역조회. */
|
|
12
|
+
get(id: string): ExcelStyle;
|
|
13
|
+
getNumFmtCode(numFmtId: string): string | undefined;
|
|
14
|
+
/** 조건부 서식 dxf 등록 후 dxfId 반환. */
|
|
15
|
+
addDxf(style: ExcelConditionalRuleStyle): string;
|
|
16
|
+
/** 워크북 전역 기본 스타일 (0번 슬롯) 설정. */
|
|
17
|
+
setDefaultStyle(style: ExcelStyle): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { IExcelModel } from "./excel-model";
|
|
2
|
+
|
|
3
|
+
/** xl/workbook.{xml,bin} 파트 모델 계약. 워크시트 목록·관계 ID 관리. */
|
|
4
|
+
export interface IWorkbookModel extends IExcelModel {
|
|
5
|
+
/** 등록된 워크시트 이름 목록. */
|
|
6
|
+
readonly sheetNames: string[];
|
|
7
|
+
/** 최대 워크시트 관계 ID (rId 숫자부). 없으면 undefined. */
|
|
8
|
+
readonly lastWsRelId: number | undefined;
|
|
9
|
+
/** 새 워크시트 엔트리 추가 (이름 sanitize·relId/sheetId 자동 증가). */
|
|
10
|
+
addWorksheet(name: string): this;
|
|
11
|
+
getWsRelIdByName(name: string): number | undefined;
|
|
12
|
+
getWsRelIdByIndex(index: number): number | undefined;
|
|
13
|
+
getWorksheetNameById(id: number): string | undefined;
|
|
14
|
+
setWorksheetNameById(id: number, newName: string): void;
|
|
15
|
+
/** bookViews 기본 골격 보장 (zoom·freeze 전 선행). */
|
|
16
|
+
initializeView(): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ExcelAddressPoint, ExcelAddressRangePoint, ExcelCellType } from "../types";
|
|
2
|
+
import type { ICfRuleSpec } from "./shared/excel-cf-spec";
|
|
3
|
+
import type { IExcelModel } from "./excel-model";
|
|
4
|
+
|
|
5
|
+
/** xl/worksheets/sheet*.{xml,bin} 파트 모델 계약. 셀·병합·뷰·CF 관리. */
|
|
6
|
+
export interface IWorksheetModel extends IExcelModel {
|
|
7
|
+
/** 데이터가 존재하는 셀 범위 (s: 좌상단, e: 우하단). */
|
|
8
|
+
readonly range: ExcelAddressRangePoint;
|
|
9
|
+
|
|
10
|
+
getCellStyleId(addr: ExcelAddressPoint): string | undefined;
|
|
11
|
+
setCellStyleId(addr: ExcelAddressPoint, styleId: string | undefined): void;
|
|
12
|
+
getCellType(addr: ExcelAddressPoint): ExcelCellType | undefined;
|
|
13
|
+
setCellType(addr: ExcelAddressPoint, type: ExcelCellType | undefined): void;
|
|
14
|
+
getCellVal(addr: ExcelAddressPoint): string | undefined;
|
|
15
|
+
setCellVal(addr: ExcelAddressPoint, val: string | undefined): void;
|
|
16
|
+
getCellFormula(addr: ExcelAddressPoint): string | undefined;
|
|
17
|
+
setCellFormula(addr: ExcelAddressPoint, val: string | undefined): void;
|
|
18
|
+
deleteCell(addr: ExcelAddressPoint): void;
|
|
19
|
+
|
|
20
|
+
setMergeCells(startAddr: ExcelAddressPoint, endAddr: ExcelAddressPoint): void;
|
|
21
|
+
getMergeCells(): ExcelAddressRangePoint[];
|
|
22
|
+
shiftMergeCells(fromRow: number, delta: number): void;
|
|
23
|
+
|
|
24
|
+
copyRow(sourceR: number, targetR: number, options?: { skipMerge?: boolean }): void;
|
|
25
|
+
copyCell(sourceAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint): void;
|
|
26
|
+
|
|
27
|
+
addConditionalFormatting(
|
|
28
|
+
sqref: string,
|
|
29
|
+
rules: { dxfId: string; cfRule: ICfRuleSpec }[],
|
|
30
|
+
): void;
|
|
31
|
+
|
|
32
|
+
setTabColor(rgb: string): void;
|
|
33
|
+
setZoom(percent: number): void;
|
|
34
|
+
freezeAt(point: { r?: number; c?: number }): void;
|
|
35
|
+
setAutoFilter(range: ExcelAddressRangePoint): void;
|
|
36
|
+
setColWidth(colIndex: string, width: string): void;
|
|
37
|
+
|
|
38
|
+
/** 워크시트에 drawing 파트 참조(rId) 연결. addImage 가 .data 직접 조작 대신 호출. */
|
|
39
|
+
setDrawingRelId(relId: string): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 포맷 중립 조건부 서식 규칙 spec.
|
|
3
|
+
*
|
|
4
|
+
* 사용자 표면 `ExcelConditionalRule` 을 워크시트 모델이 소비하기 좋은 형태로 정규화한 것.
|
|
5
|
+
* xml 은 `<cfRule>` 엘리먼트로, biff 는 CF 레코드군으로 직렬화한다.
|
|
6
|
+
*/
|
|
7
|
+
export interface ICfRuleSpec {
|
|
8
|
+
type: "cellIs" | "containsText" | "notContainsText" | "beginsWith" | "endsWith" | "expression";
|
|
9
|
+
operator?:
|
|
10
|
+
| "lessThan"
|
|
11
|
+
| "lessThanOrEqual"
|
|
12
|
+
| "equal"
|
|
13
|
+
| "notEqual"
|
|
14
|
+
| "greaterThanOrEqual"
|
|
15
|
+
| "greaterThan"
|
|
16
|
+
| "between"
|
|
17
|
+
| "notBetween"
|
|
18
|
+
| "containsText"
|
|
19
|
+
| "notContains"
|
|
20
|
+
| "beginsWith"
|
|
21
|
+
| "endsWith";
|
|
22
|
+
text?: string;
|
|
23
|
+
formula: string[];
|
|
24
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExcelBorderPosition,
|
|
3
|
+
ExcelFont,
|
|
4
|
+
ExcelHorizontalAlign,
|
|
5
|
+
ExcelStyleOptions,
|
|
6
|
+
ExcelVerticalAlign,
|
|
7
|
+
} from "../../types";
|
|
8
|
+
import { ExcelUtils } from "../../utils/excel-utils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 포맷 중립 셀 스타일. 사용자 표면 `ExcelStyleOptions` 를 내부 표현으로 정규화한 것으로,
|
|
12
|
+
* xml/biff 두 구현의 스타일 모델이 공유한다.
|
|
13
|
+
*/
|
|
14
|
+
export interface ExcelStyle {
|
|
15
|
+
numFmtId?: string;
|
|
16
|
+
numFmtCode?: string;
|
|
17
|
+
border?: ExcelBorderPosition[];
|
|
18
|
+
background?: string;
|
|
19
|
+
verticalAlign?: ExcelVerticalAlign;
|
|
20
|
+
horizontalAlign?: ExcelHorizontalAlign;
|
|
21
|
+
font?: ExcelFont;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* `ExcelStyleOptions` (사용자 표면) → 내부 `ExcelStyle` 변환.
|
|
26
|
+
* cell.setStyle 과 wb.setDefaultStyle 이 공유한다.
|
|
27
|
+
*
|
|
28
|
+
* - background ARGB 8자리 형식 검증
|
|
29
|
+
* - numberFormatCode 가 numberFormat 보다 우선
|
|
30
|
+
* - font 는 그대로 전달 (구체 검증은 스타일 모델 내부에서 수행)
|
|
31
|
+
*/
|
|
32
|
+
export function convertExcelStyleOptions(opts: ExcelStyleOptions): ExcelStyle {
|
|
33
|
+
const style: ExcelStyle = {};
|
|
34
|
+
|
|
35
|
+
if (opts.background != null) {
|
|
36
|
+
if (!/^[0-9A-F]{8}$/i.test(opts.background)) {
|
|
37
|
+
throw new Error("잘못된 색상 형식입니다. (형식: 00000000: alpha(반전)+rgb)");
|
|
38
|
+
}
|
|
39
|
+
style.background = opts.background;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (opts.border != null) {
|
|
43
|
+
style.border = opts.border;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (opts.horizontalAlign != null) {
|
|
47
|
+
style.horizontalAlign = opts.horizontalAlign;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (opts.verticalAlign != null) {
|
|
51
|
+
style.verticalAlign = opts.verticalAlign;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (opts.numberFormatCode != null) {
|
|
55
|
+
style.numFmtCode = opts.numberFormatCode;
|
|
56
|
+
} else if (opts.numberFormat != null) {
|
|
57
|
+
style.numFmtId = ExcelUtils.convertNumFmtNameToId(opts.numberFormat).toString();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (opts.font != null) {
|
|
61
|
+
style.font = opts.font;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return style;
|
|
65
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -475,19 +475,6 @@ export interface ExcelFont {
|
|
|
475
475
|
|
|
476
476
|
/**
|
|
477
477
|
* 셀 스타일 옵션
|
|
478
|
-
* @example
|
|
479
|
-
* ```typescript
|
|
480
|
-
* await cell.setStyle({
|
|
481
|
-
* background: "00FF0000", // 빨강
|
|
482
|
-
* border: ["left", "right", "top", "bottom"],
|
|
483
|
-
* horizontalAlign: "center",
|
|
484
|
-
* verticalAlign: "center",
|
|
485
|
-
* numberFormat: "number",
|
|
486
|
-
* });
|
|
487
|
-
*
|
|
488
|
-
* // 임의의 Excel formatCode 지정
|
|
489
|
-
* await cell.setStyle({ numberFormatCode: "0.000000" });
|
|
490
|
-
* ```
|
|
491
478
|
*/
|
|
492
479
|
export interface ExcelStyleOptions {
|
|
493
480
|
/** 배경색 (ARGB 형식, 예: "00FF0000") */
|
|
@@ -1,31 +1,10 @@
|
|
|
1
1
|
import type { ZipCache } from "./zip-cache";
|
|
2
|
-
import {
|
|
3
|
-
import { ExcelXmlRelationship } from "../xml/excel-xml-relationship";
|
|
4
|
-
import { ExcelXmlStyle } from "../xml/excel-xml-style";
|
|
2
|
+
import type { IStyleModel } from "../models/i-style-model";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
|
-
* `xl/styles
|
|
8
|
-
* Content_Types / workbook.xml.rels 에도 styles.xml 항목을 추가한다.
|
|
9
|
-
*
|
|
5
|
+
* `xl/styles` 파트를 가져오거나 없으면 생성·등록한다 (포맷별 처리는 ZipCache 에 위임).
|
|
10
6
|
* `ExcelCell.setStyle` 과 `ExcelWorkbook.setDefaultStyle` 양쪽이 공유한다.
|
|
11
7
|
*/
|
|
12
|
-
export async function getOrCreateStyleData(zipCache: ZipCache): Promise<
|
|
13
|
-
|
|
14
|
-
if (styleData == null) {
|
|
15
|
-
styleData = new ExcelXmlStyle();
|
|
16
|
-
zipCache.set("xl/styles.xml", styleData);
|
|
17
|
-
|
|
18
|
-
const typeData = (await zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
|
|
19
|
-
typeData.add(
|
|
20
|
-
"/xl/styles.xml",
|
|
21
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const wbRelData = (await zipCache.get("xl/_rels/workbook.xml.rels")) as ExcelXmlRelationship;
|
|
25
|
-
wbRelData.add(
|
|
26
|
-
"styles.xml",
|
|
27
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
return styleData;
|
|
8
|
+
export async function getOrCreateStyleData(zipCache: ZipCache): Promise<IStyleModel> {
|
|
9
|
+
return zipCache.ensureStyles();
|
|
31
10
|
}
|