@simplysm/excel 13.0.100 → 14.0.4

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.
Files changed (97) hide show
  1. package/README.md +66 -78
  2. package/dist/excel-cell.d.ts +28 -28
  3. package/dist/excel-cell.d.ts.map +1 -1
  4. package/dist/excel-cell.js +273 -264
  5. package/dist/excel-cell.js.map +1 -6
  6. package/dist/excel-col.d.ts +4 -4
  7. package/dist/excel-col.d.ts.map +1 -1
  8. package/dist/excel-col.js +33 -35
  9. package/dist/excel-col.js.map +1 -6
  10. package/dist/excel-row.d.ts +3 -3
  11. package/dist/excel-row.d.ts.map +1 -1
  12. package/dist/excel-row.js +28 -30
  13. package/dist/excel-row.js.map +1 -6
  14. package/dist/excel-workbook.d.ts +23 -23
  15. package/dist/excel-workbook.d.ts.map +1 -1
  16. package/dist/excel-workbook.js +151 -125
  17. package/dist/excel-workbook.js.map +1 -6
  18. package/dist/excel-worksheet.d.ts +32 -32
  19. package/dist/excel-worksheet.d.ts.map +1 -1
  20. package/dist/excel-worksheet.js +281 -253
  21. package/dist/excel-worksheet.js.map +1 -6
  22. package/dist/excel-wrapper.d.ts +7 -7
  23. package/dist/excel-wrapper.d.ts.map +1 -1
  24. package/dist/excel-wrapper.js +190 -226
  25. package/dist/excel-wrapper.js.map +1 -6
  26. package/dist/index.js +4 -1
  27. package/dist/index.js.map +1 -6
  28. package/dist/types.d.ts +13 -13
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/types.js +3 -1
  31. package/dist/types.js.map +1 -6
  32. package/dist/utils/excel-utils.d.ts +23 -23
  33. package/dist/utils/excel-utils.d.ts.map +1 -1
  34. package/dist/utils/excel-utils.js +183 -151
  35. package/dist/utils/excel-utils.js.map +1 -6
  36. package/dist/utils/zip-cache.d.ts +6 -6
  37. package/dist/utils/zip-cache.js +78 -60
  38. package/dist/utils/zip-cache.js.map +1 -6
  39. package/dist/xml/excel-xml-content-type.d.ts +2 -2
  40. package/dist/xml/excel-xml-content-type.js +55 -53
  41. package/dist/xml/excel-xml-content-type.js.map +1 -6
  42. package/dist/xml/excel-xml-drawing.d.ts +2 -2
  43. package/dist/xml/excel-xml-drawing.js +74 -73
  44. package/dist/xml/excel-xml-drawing.js.map +1 -6
  45. package/dist/xml/excel-xml-relationship.d.ts +2 -2
  46. package/dist/xml/excel-xml-relationship.js +67 -67
  47. package/dist/xml/excel-xml-relationship.js.map +1 -6
  48. package/dist/xml/excel-xml-shared-string.d.ts +2 -2
  49. package/dist/xml/excel-xml-shared-string.js +57 -55
  50. package/dist/xml/excel-xml-shared-string.js.map +1 -6
  51. package/dist/xml/excel-xml-style.d.ts +2 -2
  52. package/dist/xml/excel-xml-style.js +311 -295
  53. package/dist/xml/excel-xml-style.js.map +1 -6
  54. package/dist/xml/excel-xml-unknown.d.ts +2 -2
  55. package/dist/xml/excel-xml-unknown.js +11 -10
  56. package/dist/xml/excel-xml-unknown.js.map +1 -6
  57. package/dist/xml/excel-xml-workbook.d.ts +3 -2
  58. package/dist/xml/excel-xml-workbook.d.ts.map +1 -1
  59. package/dist/xml/excel-xml-workbook.js +95 -90
  60. package/dist/xml/excel-xml-workbook.js.map +1 -6
  61. package/dist/xml/excel-xml-worksheet.d.ts +6 -6
  62. package/dist/xml/excel-xml-worksheet.js +450 -393
  63. package/dist/xml/excel-xml-worksheet.js.map +1 -6
  64. package/docs/core.md +147 -0
  65. package/docs/types.md +201 -212
  66. package/docs/wrapper.md +32 -62
  67. package/package.json +8 -6
  68. package/src/excel-cell.ts +36 -36
  69. package/src/excel-col.ts +4 -4
  70. package/src/excel-row.ts +3 -3
  71. package/src/excel-workbook.ts +38 -38
  72. package/src/excel-worksheet.ts +69 -51
  73. package/src/excel-wrapper.ts +55 -50
  74. package/src/index.ts +3 -3
  75. package/src/types.ts +17 -17
  76. package/src/utils/excel-utils.ts +47 -41
  77. package/src/utils/zip-cache.ts +6 -6
  78. package/src/xml/excel-xml-content-type.ts +3 -3
  79. package/src/xml/excel-xml-drawing.ts +2 -2
  80. package/src/xml/excel-xml-relationship.ts +3 -3
  81. package/src/xml/excel-xml-shared-string.ts +2 -2
  82. package/src/xml/excel-xml-style.ts +13 -13
  83. package/src/xml/excel-xml-unknown.ts +2 -2
  84. package/src/xml/excel-xml-workbook.ts +14 -6
  85. package/src/xml/excel-xml-worksheet.ts +43 -43
  86. package/docs/core-classes.md +0 -541
  87. package/docs/utilities.md +0 -128
  88. package/tests/excel-cell.spec.ts +0 -393
  89. package/tests/excel-col.spec.ts +0 -81
  90. package/tests/excel-row.spec.ts +0 -61
  91. package/tests/excel-workbook.spec.ts +0 -205
  92. package/tests/excel-worksheet.spec.ts +0 -469
  93. package/tests/excel-wrapper.spec.ts +0 -273
  94. package/tests/fixtures/logo.png +0 -0
  95. package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  96. package/tests/image-insert.spec.ts +0 -190
  97. package/tests/utils/excel-utils.spec.ts +0 -198
@@ -2,49 +2,50 @@ import { num } from "@simplysm/core-common";
2
2
  import type { ExcelAddressPoint, ExcelAddressRangePoint, ExcelNumberFormat } from "../types";
3
3
 
4
4
  /**
5
- * Collection of Excel utility functions.
6
- * Provides cell address conversion, date/number conversion, and number format processing.
5
+ * Excel 유틸리티 함수 모음.
6
+ * 주소 변환, 날짜/숫자 변환, 숫자 형식 처리 기능을 제공한다.
7
7
  */
8
8
  export class ExcelUtils {
9
- /** Convert cell coordinates to "A1" format string */
9
+ /** 좌표를 "A1" 형식 문자열로 변환 */
10
10
  static stringifyAddr(point: ExcelAddressPoint): string {
11
11
  const rowStr = this.stringifyRowAddr(point.r);
12
12
  const colStr = this.stringifyColAddr(point.c);
13
13
  return `${colStr}${rowStr}`;
14
14
  }
15
15
 
16
- /** Convert row index (0-based) to row address string (e.g. 0 -> "1") */
16
+ /** 인덱스(0 기반) 주소 문자열로 변환 (예: 0 -> "1") */
17
17
  static stringifyRowAddr(r: number): string {
18
18
  return (r + 1).toString();
19
19
  }
20
20
 
21
- /** Convert column index (0-based) to column address string (e.g. 0 -> "A", 26 -> "AA") */
21
+ /** 인덱스(0 기반) 주소 문자열로 변환 (예: 0 -> "A", 26 -> "AA") */
22
22
  static stringifyColAddr(c: number): string {
23
23
  if (c < 0 || c > 16383) {
24
- throw new Error(`Column index must be in range 0~16383: ${c}`);
24
+ throw new Error(`열 인덱스는 0~16383 범위여야 합니다: ${c}`);
25
25
  }
26
26
 
27
27
  let remained = c;
28
28
  let result = String.fromCharCode((remained % 26) + 65);
29
29
  remained = Math.floor(remained / 26);
30
30
  while (remained !== 0) {
31
- result = String.fromCharCode((remained % 26) + 64) + result;
31
+ remained -= 1;
32
+ result = String.fromCharCode((remained % 26) + 65) + result;
32
33
  remained = Math.floor(remained / 26);
33
34
  }
34
35
  return result;
35
36
  }
36
37
 
37
- /** Extract row index from cell address (e.g. "A3" -> 2) */
38
+ /** 주소에서 인덱스 추출 (예: "A3" -> 2) */
38
39
  static parseRowAddr(addr: string): number {
39
40
  const rowAddrCode = /\d*$/.exec(addr)?.[0] ?? "";
40
41
  const parsed = num.parseInt(rowAddrCode);
41
42
  if (parsed == null) {
42
- throw new Error(`Invalid row address code: ${rowAddrCode}`);
43
+ throw new Error(`잘못된 주소 코드: ${rowAddrCode}`);
43
44
  }
44
45
  return parsed - 1;
45
46
  }
46
47
 
47
- /** Extract column index from cell address (e.g. "B3" -> 1) */
48
+ /** 주소에서 인덱스 추출 (예: "B3" -> 1) */
48
49
  static parseColAddr(addr: string): number {
49
50
  const colAddrCode = /^[a-zA-Z]*/.exec(addr)?.[0] ?? "";
50
51
 
@@ -57,7 +58,7 @@ export class ExcelUtils {
57
58
  return result;
58
59
  }
59
60
 
60
- /** Convert cell address to coordinates (e.g. "B3" -> {r: 2, c: 1}) */
61
+ /** 주소를 좌표로 변환 (예: "B3" -> {r: 2, c: 1}) */
61
62
  static parseCellAddr(addr: string): ExcelAddressPoint {
62
63
  return {
63
64
  r: ExcelUtils.parseRowAddr(addr),
@@ -65,7 +66,7 @@ export class ExcelUtils {
65
66
  };
66
67
  }
67
68
 
68
- /** Convert range address to coordinates (e.g. "A1:C3" -> {s: {r:0,c:0}, e: {r:2,c:2}}) */
69
+ /** 범위 주소를 좌표로 변환 (예: "A1:C3" -> {s: {r:0,c:0}, e: {r:2,c:2}}) */
69
70
  static parseRangeAddr(rangeAddr: string): ExcelAddressRangePoint {
70
71
  const parts = rangeAddr.split(":");
71
72
  return {
@@ -74,7 +75,7 @@ export class ExcelUtils {
74
75
  };
75
76
  }
76
77
 
77
- /** Convert range coordinates to address string */
78
+ /** 범위 좌표를 주소 문자열로 변환 */
78
79
  static stringifyRangeAddr(point: ExcelAddressRangePoint): string {
79
80
  const sAddr = this.stringifyAddr(point.s);
80
81
  const eAddr = this.stringifyAddr(point.e);
@@ -87,8 +88,8 @@ export class ExcelUtils {
87
88
  }
88
89
 
89
90
  /**
90
- * Convert JavaScript timestamp (ms) to Excel date number.
91
- * Excel counts 1900-01-01 as 1 (1899-12-30 is date 0).
91
+ * JavaScript 타임스탬프(ms) Excel 날짜 숫자로 변환한다.
92
+ * Excel 1900-01-01 1 계산한다 (1899-12-30 날짜 0).
92
93
  */
93
94
  static convertTimeTickToNumber(tick: number): number {
94
95
  const currDate = new Date(tick);
@@ -99,8 +100,8 @@ export class ExcelUtils {
99
100
  }
100
101
 
101
102
  /**
102
- * Convert Excel date number to JavaScript timestamp (ms).
103
- * Excel counts 1900-01-01 as 1 (1899-12-30 is date 0).
103
+ * Excel 날짜 숫자를 JavaScript 타임스탬프(ms)로 변환한다.
104
+ * Excel 1900-01-01 1 계산한다 (1899-12-30 날짜 0).
104
105
  */
105
106
  static convertNumberToTimeTick(value: number): number {
106
107
  const excelBaseDateNumberUtc = Date.UTC(1899, 11, 31);
@@ -111,50 +112,55 @@ export class ExcelUtils {
111
112
  return date.getTime();
112
113
  }
113
114
 
114
- /** Convert number format code to format name */
115
+ /** 숫자 형식 코드를 형식 이름으로 변환 */
115
116
  static convertNumFmtCodeToName(numFmtCode: string): ExcelNumberFormat {
116
117
  if (numFmtCode === "General") {
117
118
  return "number";
118
119
  }
119
120
 
120
- const hasDate = /yy/i.test(numFmtCode) || /dd/i.test(numFmtCode) || /mm/i.test(numFmtCode);
121
- const hasTime = /hh/i.test(numFmtCode) || /ss/i.test(numFmtCode);
121
+ // "mm"이 시간 문맥(h/hh 뒤 또는 ss 앞)에서 쓰이면 (minute)이므로 날짜 판별에서 제외
122
+ const codeForDateCheck = numFmtCode
123
+ .replace(/h{1,2}\s*:?\s*mm/gi, "")
124
+ .replace(/mm\s*:?\s*ss/gi, "");
125
+ const hasDate =
126
+ /yy/i.test(numFmtCode) || /dd/i.test(numFmtCode) || /mm/i.test(codeForDateCheck);
127
+ const hasTime = /h{1,2}/i.test(numFmtCode) || /ss/i.test(numFmtCode);
122
128
 
123
129
  if (hasDate && hasTime) {
124
- return "DateTime"; // date+time = DateTime
130
+ return "DateTime"; // 날짜+시간 = DateTime
125
131
  } else if (hasDate) {
126
- return "DateOnly"; // date only = DateOnly
132
+ return "DateOnly"; // 날짜만 = DateOnly
127
133
  } else if (hasTime) {
128
- return "Time"; // time only = Time
134
+ return "Time"; // 시간만 = Time
129
135
  }
130
- // Number format pattern: 0, #, decimal, thousands separator, negative separator, parentheses, currency, space, exponent, percent, etc.
131
- // In "[conditional format]actual format" structure, only check the actual format part (split("]").at(-1))
136
+ // 숫자 형식 패턴: 0, #, 소수점, 천단위 구분, 음수 구분, 괄호, 통화, 공백, 지수, 퍼센트
137
+ // "[조건부 형식]실제 형식" 구조에서 실제 형식 부분만 검사 (split("]").at(-1))
132
138
  else if (/^[0.#,_;()\-\\$ @*?"E%+]*$/.test(numFmtCode.split("]").at(-1) ?? "")) {
133
139
  return "number";
134
140
  } else if ((numFmtCode.split("]").at(-1) ?? "").includes("#,0")) {
135
141
  return "number";
136
142
  } else {
137
- throw new Error(`Unknown format for [numFmtCode: ${numFmtCode}].`);
143
+ throw new Error(`알 없는 형식 [numFmtCode: ${numFmtCode}]`);
138
144
  }
139
145
  }
140
146
 
141
147
  /**
142
- * Convert number format ID to format name
148
+ * 숫자 형식 ID 형식 이름으로 변환
143
149
  *
144
150
  * @remarks
145
- * Excel built-in format ID ranges:
146
- * - 0~13, 37~40, 48: number/general/currency/percent formats
147
- * - 14~17, 27~31, 34~36, 50~58: date formats (including localized)
148
- * - 22: date+time format
149
- * - 18~21, 32~33, 45~47: time formats
150
- * - 49: text format
151
+ * Excel 내장 형식 ID 범위:
152
+ * - 0~13, 37~40, 48: 숫자/일반/통화/퍼센트 형식
153
+ * - 14~17, 27~31, 34~36, 50~58: 날짜 형식 (로컬라이즈 포함)
154
+ * - 22: 날짜+시간 형식
155
+ * - 18~21, 32~33, 45~47: 시간 형식
156
+ * - 49: 텍스트 형식
151
157
  */
152
158
  static convertNumFmtIdToName(numFmtId: number): ExcelNumberFormat {
153
- // Number/general/currency/percent formats
159
+ // 숫자/일반/통화/퍼센트 형식
154
160
  if (numFmtId <= 13 || (numFmtId >= 37 && numFmtId <= 40) || numFmtId === 48) {
155
161
  return "number";
156
162
  }
157
- // Date formats (including localized)
163
+ // 날짜 형식 (로컬라이즈 포함)
158
164
  else if (
159
165
  (numFmtId >= 14 && numFmtId <= 17) ||
160
166
  (numFmtId >= 27 && numFmtId <= 31) ||
@@ -163,11 +169,11 @@ export class ExcelUtils {
163
169
  ) {
164
170
  return "DateOnly";
165
171
  }
166
- // Date+time format
172
+ // 날짜+시간 형식
167
173
  else if (numFmtId === 22) {
168
174
  return "DateTime";
169
175
  }
170
- // Time formats
176
+ // 시간 형식
171
177
  else if (
172
178
  (numFmtId >= 18 && numFmtId <= 21) ||
173
179
  (numFmtId >= 32 && numFmtId <= 33) ||
@@ -175,15 +181,15 @@ export class ExcelUtils {
175
181
  ) {
176
182
  return "Time";
177
183
  }
178
- // Text format
184
+ // 텍스트 형식
179
185
  else if (numFmtId === 49) {
180
186
  return "string";
181
187
  } else {
182
- throw new Error(`Unknown format for [numFmtId: ${numFmtId}].`);
188
+ throw new Error(`알 없는 형식 [numFmtId: ${numFmtId}]`);
183
189
  }
184
190
  }
185
191
 
186
- /** Convert number format name to format ID */
192
+ /** 숫자 형식 이름을 형식 ID로 변환 */
187
193
  static convertNumFmtNameToId(numFmtName: ExcelNumberFormat): number {
188
194
  if (numFmtName === "number") {
189
195
  return 0;
@@ -194,7 +200,7 @@ export class ExcelUtils {
194
200
  } else if (numFmtName === "Time") {
195
201
  return 18;
196
202
  } else {
197
- // Last case: "string" (TypeScript verifies automatically through type narrowing)
203
+ // 마지막 케이스: "string" (TypeScript 타입 좁히기를 통해 자동 검증)
198
204
  return 49;
199
205
  }
200
206
  }
@@ -20,15 +20,15 @@ import { ExcelXmlWorkbook } from "../xml/excel-xml-workbook";
20
20
  import { ExcelXmlWorksheet } from "../xml/excel-xml-worksheet";
21
21
 
22
22
  /**
23
- * Class managing file cache for Excel ZIP archives.
24
- * XML files are parsed into ExcelXml objects; other files are cached as byte arrays.
23
+ * Excel ZIP 아카이브의 파일 캐시를 관리하는 클래스.
24
+ * XML 파일은 ExcelXml 객체로 파싱하고, 기타 파일은 바이트 배열로 캐싱한다.
25
25
  *
26
26
  * @remarks
27
- * ## Lazy Loading Cache Strategy
27
+ * ## Lazy Loading 캐시 전략
28
28
  *
29
- * - Files are read and parsed from the ZIP only on first access
30
- * - Subsequent accesses return the cached object
31
- * - Loads only the needed parts from large Excel files for memory efficiency
29
+ * - 파일은 최초 접근 시에만 ZIP에서 읽고 파싱함
30
+ * - 이후 접근은 캐싱된 객체를 반환함
31
+ * - 대용량 Excel 파일에서 필요한 부분만 로드하여 메모리 효율적
32
32
  */
33
33
  export class ZipCache {
34
34
  private readonly _cache = new Map<string, ExcelXml | Bytes | undefined>();
@@ -1,8 +1,8 @@
1
1
  import type { ExcelXml, ExcelXmlContentTypeData } from "../types";
2
2
 
3
3
  /**
4
- * Class managing [Content_Types].xml.
5
- * Manages MIME type information per file.
4
+ * [Content_Types].xml을 관리하는 클래스.
5
+ * 파일별 MIME 타입 정보를 관리한다.
6
6
  */
7
7
  export class ExcelXmlContentType implements ExcelXml {
8
8
  data: ExcelXmlContentTypeData;
@@ -45,7 +45,7 @@ export class ExcelXmlContentType implements ExcelXml {
45
45
  }
46
46
 
47
47
  add(partName: string, contentType: string): this {
48
- // Duplicate check
48
+ // 중복 검사
49
49
  const exists = this.data.Types.Override.some((item) => item.$.PartName === partName);
50
50
  if (exists) {
51
51
  return this;
@@ -1,8 +1,8 @@
1
1
  import type { ExcelXml, ExcelXmlDrawingData } from "../types";
2
2
 
3
3
  /**
4
- * Class managing xl/drawings/drawing*.xml files.
5
- * Handles position and reference information for image insertion.
4
+ * xl/drawings/drawing*.xml 파일을 관리하는 클래스.
5
+ * 이미지 삽입을 위한 위치 참조 정보를 처리한다.
6
6
  */
7
7
  export class ExcelXmlDrawing implements ExcelXml {
8
8
  data: ExcelXmlDrawingData;
@@ -3,8 +3,8 @@ import { num } from "@simplysm/core-common";
3
3
  import type { ExcelRelationshipData, ExcelXml, ExcelXmlRelationshipData } from "../types";
4
4
 
5
5
  /**
6
- * Class managing *.rels files.
7
- * Handles reference relationships between files.
6
+ * *.rels 파일을 관리하는 클래스.
7
+ * 파일 간의 참조 관계를 처리한다.
8
8
  */
9
9
  export class ExcelXmlRelationship implements ExcelXml {
10
10
  data: ExcelXmlRelationshipData;
@@ -82,7 +82,7 @@ export class ExcelXmlRelationship implements ExcelXml {
82
82
  private _getRelId(rel: ExcelRelationshipData): number {
83
83
  const match = /[0-9]+$/.exec(rel.$.Id);
84
84
  if (match == null) {
85
- throw new Error(`Invalid relationship ID format: ${rel.$.Id}`);
85
+ throw new Error(`잘못된 관계 ID 형식: ${rel.$.Id}`);
86
86
  }
87
87
  return num.parseInt(match[0])!;
88
88
  }
@@ -7,8 +7,8 @@ import type {
7
7
  import "@simplysm/core-common";
8
8
 
9
9
  /**
10
- * Class managing xl/sharedStrings.xml.
11
- * Optimizes file size by preventing string duplication.
10
+ * xl/sharedStrings.xml을 관리하는 클래스.
11
+ * 문자열 중복을 방지하여 파일 크기를 최적화한다.
12
12
  */
13
13
  export class ExcelXmlSharedString implements ExcelXml {
14
14
  data: ExcelXmlSharedStringData;
@@ -21,8 +21,8 @@ export interface ExcelStyle {
21
21
  }
22
22
 
23
23
  /**
24
- * Class managing xl/styles.xml.
25
- * Handles styles such as number formats, background colors, borders, and alignment.
24
+ * xl/styles.xml을 관리하는 클래스.
25
+ * 숫자 형식, 배경색, 테두리, 정렬 등의 스타일을 처리한다.
26
26
  */
27
27
  export class ExcelXmlStyle implements ExcelXml {
28
28
  data: ExcelXmlStyleData;
@@ -108,11 +108,11 @@ export class ExcelXmlStyle implements ExcelXml {
108
108
  addWithClone(id: string, style: ExcelStyle): string {
109
109
  const idNum = num.parseInt(id);
110
110
  if (idNum == null) {
111
- throw new Error(`Invalid style ID: ${id}`);
111
+ throw new Error(`잘못된 스타일 ID: ${id}`);
112
112
  }
113
113
  const xfArray = this.data.styleSheet.cellXfs[0].xf;
114
114
  if (idNum < 0 || idNum >= xfArray.length) {
115
- throw new Error(`Non-existent style ID: ${id} (Range: 0-${xfArray.length - 1})`);
115
+ throw new Error(`존재하지 않는 스타일 ID: ${id} (범위: 0-${xfArray.length - 1})`);
116
116
  }
117
117
  const prevXf = xfArray[idNum];
118
118
  const cloneXf = obj.clone(prevXf);
@@ -136,9 +136,9 @@ export class ExcelXmlStyle implements ExcelXml {
136
136
  cloneFill.patternFill[0].$.patternType = "solid";
137
137
 
138
138
  if (cloneFill.patternFill[0].fgColor == null) {
139
- cloneFill.patternFill[0].fgColor = [{ $: { rgb: style.background } }];
139
+ cloneFill.patternFill[0].fgColor = [{ $: { rgb: style.background.toUpperCase() } }];
140
140
  } else {
141
- cloneFill.patternFill[0].fgColor[0].$.rgb = style.background;
141
+ cloneFill.patternFill[0].fgColor[0].$.rgb = style.background.toUpperCase();
142
142
  }
143
143
 
144
144
  cloneXf.$.applyFill = "1";
@@ -187,7 +187,7 @@ export class ExcelXmlStyle implements ExcelXml {
187
187
  get(id: string): ExcelStyle {
188
188
  const idNum = num.parseInt(id);
189
189
  if (idNum == null) {
190
- throw new Error(`Invalid style ID: ${id}`);
190
+ throw new Error(`잘못된 스타일 ID: ${id}`);
191
191
  }
192
192
  const xf = this.data.styleSheet.cellXfs[0].xf[idNum] as ExcelXmlStyleDataXf | undefined;
193
193
 
@@ -204,7 +204,7 @@ export class ExcelXmlStyle implements ExcelXml {
204
204
  | undefined;
205
205
  if (fill == null) {
206
206
  throw new Error(
207
- `Non-existent fill ID: ${xf.$.fillId} (Range: 0-${this.data.styleSheet.fills[0].fill.length - 1})`,
207
+ `존재하지 않는 fill ID: ${xf.$.fillId} (범위: 0-${this.data.styleSheet.fills[0].fill.length - 1})`,
208
208
  );
209
209
  }
210
210
  result.background = fill.patternFill[0].fgColor?.[0].$.rgb;
@@ -214,14 +214,14 @@ export class ExcelXmlStyle implements ExcelXml {
214
214
  if (xf.$.borderId !== undefined) {
215
215
  const borderIdNum = num.parseInt(xf.$.borderId);
216
216
  if (borderIdNum == null) {
217
- throw new Error(`Invalid border ID: ${xf.$.borderId}`);
217
+ throw new Error(`잘못된 border ID: ${xf.$.borderId}`);
218
218
  }
219
219
  const border = this.data.styleSheet.borders[0].border[borderIdNum] as
220
220
  | ExcelXmlStyleDataBorder
221
221
  | undefined;
222
222
  if (border == null) {
223
223
  throw new Error(
224
- `Non-existent border ID: ${xf.$.borderId} (Range: 0-${this.data.styleSheet.borders[0].border.length - 1})`,
224
+ `존재하지 않는 border ID: ${xf.$.borderId} (범위: 0-${this.data.styleSheet.borders[0].border.length - 1})`,
225
225
  );
226
226
  }
227
227
  if (
@@ -262,7 +262,7 @@ export class ExcelXmlStyle implements ExcelXml {
262
262
  cleanup(): void {
263
263
  const result = {} as ExcelXmlStyleData["styleSheet"];
264
264
 
265
- // Sort order (numFmts first)
265
+ // 정렬 순서 (numFmts 먼저)
266
266
 
267
267
  if (this.data.styleSheet.numFmts != null) {
268
268
  result.numFmts = this.data.styleSheet.numFmts;
@@ -282,7 +282,7 @@ export class ExcelXmlStyle implements ExcelXml {
282
282
  //#region Private Methods
283
283
 
284
284
  private _setNumFmtCode(numFmtCode: string): string {
285
- // Skip if the code already exists
285
+ // 코드가 이미 존재하면 건너뛰기
286
286
  const existsNumFmtId = (this.data.styleSheet.numFmts?.[0].numFmt ?? []).single(
287
287
  (item) => item.$.formatCode === numFmtCode,
288
288
  )?.$.numFmtId;
@@ -299,7 +299,7 @@ export class ExcelXmlStyle implements ExcelXml {
299
299
 
300
300
  this.data.styleSheet.numFmts[0].numFmt = this.data.styleSheet.numFmts[0].numFmt ?? [];
301
301
 
302
- // Excel custom number formats start from ID 180+ (0-163: built-in, 164-179: reserved)
302
+ // Excel 사용자 정의 숫자 형식은 ID 180+부터 시작 (0-163: 내장, 164-179: 예약)
303
303
  const numFmts = this.data.styleSheet.numFmts[0].numFmt;
304
304
  const maxItem =
305
305
  numFmts.length > 0
@@ -1,8 +1,8 @@
1
1
  import type { ExcelXml } from "../types";
2
2
 
3
3
  /**
4
- * Class preserving Excel XML data of unknown format.
5
- * Maintains original data without loss.
4
+ * 없는 형식의 Excel XML 데이터를 보존하는 클래스.
5
+ * 원본 데이터를 손실 없이 유지한다.
6
6
  */
7
7
  export class ExcelXmlUnknown implements ExcelXml {
8
8
  constructor(public readonly data: Record<string, unknown>) {}
@@ -3,8 +3,8 @@ import { num } from "@simplysm/core-common";
3
3
  import type { ExcelXml, ExcelXmlWorkbookData } from "../types";
4
4
 
5
5
  /**
6
- * Class managing xl/workbook.xml.
7
- * Handles the worksheet list and relationship IDs.
6
+ * xl/workbook.xml을 관리하는 클래스.
7
+ * 워크시트 목록 관계 ID를 처리한다.
8
8
  */
9
9
  export class ExcelXmlWorkbook implements ExcelXml {
10
10
  data: ExcelXmlWorkbookData;
@@ -31,6 +31,13 @@ export class ExcelXmlWorkbook implements ExcelXml {
31
31
  return maxSheet ? num.parseInt(maxSheet.$["r:id"]) : undefined;
32
32
  }
33
33
 
34
+ get lastSheetId(): number | undefined {
35
+ const sheets = this.data.workbook.sheets?.[0].sheet;
36
+ if (!sheets || sheets.length === 0) return undefined;
37
+ const maxSheet = sheets.orderByDesc((sheet) => num.parseInt(sheet.$.sheetId)!).first();
38
+ return maxSheet ? num.parseInt(maxSheet.$.sheetId) : undefined;
39
+ }
40
+
34
41
  get sheetNames(): string[] {
35
42
  return this.data.workbook.sheets?.[0].sheet.map((item) => item.$.name) ?? [];
36
43
  }
@@ -39,12 +46,13 @@ export class ExcelXmlWorkbook implements ExcelXml {
39
46
  const replacedName = this._getReplacedName(name);
40
47
 
41
48
  const newWsRelId = (this.lastWsRelId ?? 0) + 1;
49
+ const newSheetId = (this.lastSheetId ?? 0) + 1;
42
50
 
43
51
  this.data.workbook.sheets = this.data.workbook.sheets ?? [{ sheet: [] }];
44
52
  this.data.workbook.sheets[0].sheet.push({
45
53
  $: {
46
54
  "name": replacedName,
47
- "sheetId": newWsRelId.toString(),
55
+ "sheetId": newSheetId.toString(),
48
56
  "r:id": `rId${newWsRelId}`,
49
57
  },
50
58
  });
@@ -55,7 +63,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
55
63
  cleanup(): void {
56
64
  const result = {} as ExcelXmlWorkbookData["workbook"];
57
65
 
58
- // Sort order (around "sheets", keep others in original position)
66
+ // 정렬 순서 ("sheets" 기준, 나머지는 원래 위치 유지)
59
67
 
60
68
  const workbookRec = this.data.workbook as Record<string, unknown>;
61
69
  const resultRec = result as Record<string, unknown>;
@@ -99,7 +107,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
99
107
  setWorksheetNameById(id: number, newName: string): void {
100
108
  const sheetData = this._getSheetDataById(id);
101
109
  if (sheetData == null) {
102
- throw new Error(`Cannot find worksheet ID ${id}`);
110
+ throw new Error(`워크시트 ID ${id}를 찾을 수 없습니다`);
103
111
  }
104
112
  const replacedName = this._getReplacedName(newName);
105
113
  sheetData.$.name = replacedName;
@@ -112,7 +120,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
112
120
  }
113
121
 
114
122
  private _getReplacedName(name: string): string {
115
- //-- Replace invalid sheet name characters with "_"
123
+ //-- 잘못된 시트 이름 문자를 "_"로 대체
116
124
  return name.replace(/[:\\/?*\[\]']/g, "_");
117
125
  }
118
126
  }