kordoc 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +20 -0
  2. package/dist/{-EYPKRLMN.js → -H7N62NZU.js} +17 -12
  3. package/dist/{chunk-734GJLXH.js → chunk-5RHRQ7HK.js} +10 -176
  4. package/dist/chunk-5RHRQ7HK.js.map +1 -0
  5. package/dist/{chunk-DMBTOJK4.cjs → chunk-BIP4JAVG.cjs} +2 -2
  6. package/dist/{chunk-DMBTOJK4.cjs.map → chunk-BIP4JAVG.cjs.map} +1 -1
  7. package/dist/chunk-FGL7BH4P.js +1019 -0
  8. package/dist/chunk-FGL7BH4P.js.map +1 -0
  9. package/dist/{chunk-SCFFABCT.js → chunk-IC56KHBL.js} +2 -2
  10. package/dist/chunk-JFNWJFKP.js +185 -0
  11. package/dist/chunk-JFNWJFKP.js.map +1 -0
  12. package/dist/{chunk-Q5ZPWQID.js → chunk-LYL4IL3E.js} +2 -2
  13. package/dist/{chunk-ZRNYYEE6.js → chunk-Q7EGGW3H.js} +2 -2
  14. package/dist/{chunk-Q3IHI3E6.js → chunk-WD5NUUU6.js} +267 -705
  15. package/dist/chunk-WD5NUUU6.js.map +1 -0
  16. package/dist/cli.js +63 -11
  17. package/dist/cli.js.map +1 -1
  18. package/dist/index.cjs +856 -304
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +65 -1
  21. package/dist/index.d.ts +65 -1
  22. package/dist/index.js +682 -130
  23. package/dist/index.js.map +1 -1
  24. package/dist/mcp.js +65 -7
  25. package/dist/mcp.js.map +1 -1
  26. package/dist/{parser-SNBQZO5E.js → parser-5G247XYD.js} +2 -2
  27. package/dist/{parser-O2SD4DFR.cjs → parser-6V7F3GET.cjs} +14 -14
  28. package/dist/{parser-O2SD4DFR.cjs.map → parser-6V7F3GET.cjs.map} +1 -1
  29. package/dist/{parser-76JSTEP3.js → parser-HBHCD7TE.js} +3 -3
  30. package/dist/render-VA4NHROW.js +10 -0
  31. package/dist/seal-UVDRGHEF.js +10 -0
  32. package/dist/seal-UVDRGHEF.js.map +1 -0
  33. package/dist/{watch-LEK5U5P6.js → watch-HIYNOSFD.js} +7 -5
  34. package/dist/{watch-LEK5U5P6.js.map → watch-HIYNOSFD.js.map} +1 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-734GJLXH.js.map +0 -1
  37. package/dist/chunk-Q3IHI3E6.js.map +0 -1
  38. package/dist/render-LDI3LO2P.js +0 -9
  39. /package/dist/{-EYPKRLMN.js.map → -H7N62NZU.js.map} +0 -0
  40. /package/dist/{chunk-SCFFABCT.js.map → chunk-IC56KHBL.js.map} +0 -0
  41. /package/dist/{chunk-Q5ZPWQID.js.map → chunk-LYL4IL3E.js.map} +0 -0
  42. /package/dist/{chunk-ZRNYYEE6.js.map → chunk-Q7EGGW3H.js.map} +0 -0
  43. /package/dist/{parser-SNBQZO5E.js.map → parser-5G247XYD.js.map} +0 -0
  44. /package/dist/{parser-76JSTEP3.js.map → parser-HBHCD7TE.js.map} +0 -0
  45. /package/dist/{render-LDI3LO2P.js.map → render-VA4NHROW.js.map} +0 -0
@@ -0,0 +1,1019 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseRenderStyles
4
+ } from "./chunk-JFNWJFKP.js";
5
+ import {
6
+ KordocError
7
+ } from "./chunk-IC56KHBL.js";
8
+
9
+ // src/form/seal.ts
10
+ import JSZip from "jszip";
11
+
12
+ // src/roundtrip/source-map.ts
13
+ function escapeXmlText(text) {
14
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15
+ }
16
+ function decodeXmlEntities(text) {
17
+ return text.replace(/&(lt|gt|amp|quot|apos|#x?[0-9a-fA-F]+);/g, (m, ent) => {
18
+ switch (ent) {
19
+ case "lt":
20
+ return "<";
21
+ case "gt":
22
+ return ">";
23
+ case "amp":
24
+ return "&";
25
+ case "quot":
26
+ return '"';
27
+ case "apos":
28
+ return "'";
29
+ }
30
+ try {
31
+ const code = ent[1] === "x" || ent[1] === "X" ? parseInt(ent.slice(2), 16) : parseInt(ent.slice(1), 10);
32
+ if (!isNaN(code) && code >= 0 && code <= 1114111) return String.fromCodePoint(code);
33
+ } catch {
34
+ }
35
+ return m;
36
+ });
37
+ }
38
+ function tContentToText(raw) {
39
+ return decodeXmlEntities(
40
+ raw.replace(/<\/?(?:[A-Za-z0-9_]+:)?(?:tab|fwSpace|hwSpace|br|lineBreak)(?:\s[^>]*)?\/?>/g, " ").replace(/<[^>]*>/g, "")
41
+ );
42
+ }
43
+ var TAG_RE = /<!--[\s\S]*?-->|<!\[CDATA\[[\s\S]*?\]\]>|<\?[\s\S]*?\?>|<!(?:"[^"]*"|'[^']*'|[^>"'])*>|<\/([^\s>]+)\s*>|<([^\s/>!?]+)((?:"[^"]*"|'[^']*'|[^>"'])*?)(\/?)>/g;
44
+ var T_BARRIER = /* @__PURE__ */ new Set([
45
+ "tbl",
46
+ "ctrl",
47
+ "caption",
48
+ "pic",
49
+ "shape",
50
+ "drawingObject",
51
+ "drawText",
52
+ "shapeComment",
53
+ "memogroup",
54
+ "memo",
55
+ "hiddenComment",
56
+ "equation",
57
+ "parameters",
58
+ "subList",
59
+ "p"
60
+ ]);
61
+ var PARA_CONTAINER = /* @__PURE__ */ new Set([
62
+ "tc",
63
+ "ctrl",
64
+ "caption",
65
+ "drawText",
66
+ "pic",
67
+ "shape",
68
+ "drawingObject",
69
+ "memogroup",
70
+ "memo",
71
+ "hiddenComment",
72
+ "footNote",
73
+ "endNote",
74
+ "fn",
75
+ "en"
76
+ // 각주/미주 — 파서는 호스트 블록 footnoteText로만 흡수
77
+ ]);
78
+ var TABLE_BARRIER = /* @__PURE__ */ new Set([
79
+ "tbl",
80
+ "ctrl",
81
+ "caption",
82
+ "memogroup",
83
+ "memo",
84
+ "hiddenComment"
85
+ ]);
86
+ function localOf(qname) {
87
+ const i = qname.indexOf(":");
88
+ return i >= 0 ? qname.slice(i + 1) : qname;
89
+ }
90
+ function prefixOf(qname) {
91
+ const i = qname.indexOf(":");
92
+ return i >= 0 ? qname.slice(0, i) : "";
93
+ }
94
+ function scanSectionXml(xml, sectionIndex) {
95
+ const stack = [];
96
+ const bodyParagraphs = [];
97
+ const tables = [];
98
+ const headerTexts = [];
99
+ const footerTexts = [];
100
+ const excludedParagraphs = [];
101
+ const orphanTables = [];
102
+ const paraStack = [];
103
+ const tableStack = [];
104
+ const rowStack = [];
105
+ const trStartStack = [];
106
+ const cellStack = [];
107
+ let pendingT = null;
108
+ const ctrlSubStack = [];
109
+ const classifyPara = () => {
110
+ let sawDrawText = false;
111
+ for (let i = stack.length - 1; i >= 0; i--) {
112
+ const l = stack[i].local;
113
+ if (l === "tc") return { kind: "cell", inTextbox: sawDrawText };
114
+ if (l === "drawText") {
115
+ sawDrawText = true;
116
+ continue;
117
+ }
118
+ if (PARA_CONTAINER.has(l)) return { kind: "excluded", inTextbox: sawDrawText };
119
+ }
120
+ return sawDrawText ? { kind: "draw", inTextbox: true } : { kind: "body", inTextbox: false };
121
+ };
122
+ const owningPara = () => {
123
+ if (paraStack.length === 0) return null;
124
+ for (let i = stack.length - 1; i >= 0; i--) {
125
+ const l = stack[i].local;
126
+ if (l === "p") return paraStack[paraStack.length - 1];
127
+ if (T_BARRIER.has(l)) return null;
128
+ }
129
+ return null;
130
+ };
131
+ const isTableTopLevel = () => {
132
+ for (let i = stack.length - 1; i >= 0; i--) {
133
+ if (TABLE_BARRIER.has(stack[i].local)) return false;
134
+ }
135
+ return true;
136
+ };
137
+ const currentCtrlSub = () => ctrlSubStack.length > 0 ? ctrlSubStack[ctrlSubStack.length - 1] : null;
138
+ TAG_RE.lastIndex = 0;
139
+ let m;
140
+ while ((m = TAG_RE.exec(xml)) !== null) {
141
+ const [full, closeName, openName, , selfClose] = m;
142
+ if (closeName === void 0 && openName === void 0) continue;
143
+ if (closeName !== void 0) {
144
+ const local2 = localOf(closeName);
145
+ if (local2 === "t" && pendingT) {
146
+ const { para, contentStart: contentStart2 } = pendingT;
147
+ para.tRanges.push({ contentStart: contentStart2, contentEnd: m.index });
148
+ para.text += tContentToText(xml.slice(contentStart2, m.index));
149
+ pendingT = null;
150
+ }
151
+ for (let i = stack.length - 1; i >= 0; i--) {
152
+ if (stack[i].local === local2) {
153
+ stack.length = i;
154
+ break;
155
+ }
156
+ }
157
+ if (local2 === "p") {
158
+ const para = paraStack.pop();
159
+ if (para && para.kind === "excluded") {
160
+ const sub = currentCtrlSub();
161
+ if (sub && para.text.trim()) sub.texts.push(para.text);
162
+ }
163
+ } else if (local2 === "tc") {
164
+ const cell = cellStack.pop();
165
+ const row = rowStack[rowStack.length - 1];
166
+ if (cell && row) row.push(cell);
167
+ } else if (local2 === "tr") {
168
+ const row = rowStack[rowStack.length - 1];
169
+ const table = tableStack[tableStack.length - 1];
170
+ if (row && table && row.length > 0) {
171
+ table.rows.push(row);
172
+ const trStart = trStartStack[trStartStack.length - 1];
173
+ if (trStart >= 0) table.rowRanges.push({ start: trStart, end: m.index + full.length });
174
+ }
175
+ if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
176
+ if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = -1;
177
+ } else if (local2 === "tbl") {
178
+ const table = tableStack.pop();
179
+ rowStack.pop();
180
+ trStartStack.pop();
181
+ if (table) {
182
+ finalizeTable(table);
183
+ if (!table.topLevel) {
184
+ const cell = cellStack[cellStack.length - 1];
185
+ if (cell) cell.tables.push(table);
186
+ else orphanTables.push(table);
187
+ }
188
+ }
189
+ } else if (local2 === "header" || local2 === "footer") {
190
+ const sub = ctrlSubStack[ctrlSubStack.length - 1];
191
+ if (sub) {
192
+ ctrlSubStack.pop();
193
+ const joined = sub.texts.join("\n").trim();
194
+ if (joined) (sub.kind === "header" ? headerTexts : footerTexts).push(joined);
195
+ }
196
+ }
197
+ continue;
198
+ }
199
+ const qname = openName;
200
+ const local = localOf(qname);
201
+ const attrsRaw = m[3] || "";
202
+ const isSelfClose = selfClose === "/";
203
+ const contentStart = m.index + full.length;
204
+ if (isSelfClose) {
205
+ if (local === "t") {
206
+ const para = owningPara();
207
+ if (para) para.tRanges.push({ contentStart: m.index, contentEnd: m.index + full.length, selfClosing: true, prefix: prefixOf(qname) });
208
+ } else if (local === "tab" || local === "fwSpace" || local === "hwSpace" || local === "br" || local === "lineBreak") {
209
+ if (!pendingT) {
210
+ const para = owningPara();
211
+ if (para) para.text += " ";
212
+ }
213
+ } else if (local === "run" || local === "r") {
214
+ const para = owningPara();
215
+ if (para && !para.selfCloseRun) para.selfCloseRun = { start: m.index, end: m.index + full.length };
216
+ } else if (local === "cellAddr") {
217
+ const cell = cellStack[cellStack.length - 1];
218
+ if (cell && insideCurrentTable(stack, tableStack)) {
219
+ const ca = parseInt(getAttr(attrsRaw, "colAddr") || "", 10);
220
+ const ra = parseInt(getAttr(attrsRaw, "rowAddr") || "", 10);
221
+ if (!isNaN(ca)) cell.colAddr = ca;
222
+ if (!isNaN(ra)) cell.rowAddr = ra;
223
+ cell.addrTagRange = { start: m.index, end: m.index + full.length };
224
+ }
225
+ } else if (local === "cellSpan") {
226
+ const cell = cellStack[cellStack.length - 1];
227
+ if (cell && insideCurrentTable(stack, tableStack)) {
228
+ const cs = parseInt(getAttr(attrsRaw, "colSpan") || "1", 10);
229
+ const rs = parseInt(getAttr(attrsRaw, "rowSpan") || "1", 10);
230
+ cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
231
+ cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
232
+ }
233
+ }
234
+ continue;
235
+ }
236
+ if (local === "t") {
237
+ const para = owningPara();
238
+ if (para) pendingT = { para, contentStart };
239
+ stack.push({ local, qname, contentStart });
240
+ continue;
241
+ }
242
+ stack.push({ local, qname, contentStart });
243
+ if (local === "p") {
244
+ const para = {
245
+ sectionIndex,
246
+ kind: "excluded",
247
+ // 분류는 push 직후 스택 기준 (자기 자신 제외)
248
+ start: m.index,
249
+ tRanges: [],
250
+ text: ""
251
+ };
252
+ stack.pop();
253
+ const cls = classifyPara();
254
+ para.kind = cls.kind;
255
+ if (cls.inTextbox) para.inTextbox = true;
256
+ stack.push({ local, qname, contentStart });
257
+ paraStack.push(para);
258
+ if (para.kind === "body" || para.kind === "draw") bodyParagraphs.push(para);
259
+ else if (para.kind === "cell") {
260
+ const cell = cellStack[cellStack.length - 1];
261
+ if (cell) cell.paragraphs.push(para);
262
+ } else if (para.kind === "excluded") {
263
+ excludedParagraphs.push(para);
264
+ }
265
+ } else if (local === "run" || local === "r") {
266
+ const para = owningPara();
267
+ if (para && para.runPrefix === void 0) para.runPrefix = prefixOf(qname);
268
+ } else if (local === "tbl") {
269
+ const table = {
270
+ sectionIndex,
271
+ start: m.index,
272
+ topLevel: false,
273
+ rows: [],
274
+ rowRanges: [],
275
+ cellByAnchor: /* @__PURE__ */ new Map()
276
+ };
277
+ stack.pop();
278
+ table.topLevel = isTableTopLevel();
279
+ stack.push({ local, qname, contentStart });
280
+ tableStack.push(table);
281
+ rowStack.push([]);
282
+ trStartStack.push(-1);
283
+ if (table.topLevel) tables.push(table);
284
+ } else if (local === "tr") {
285
+ if (rowStack.length > 0) rowStack[rowStack.length - 1] = [];
286
+ if (trStartStack.length > 0) trStartStack[trStartStack.length - 1] = m.index;
287
+ } else if (local === "tc") {
288
+ cellStack.push({ colSpan: 1, rowSpan: 1, paragraphs: [], tables: [] });
289
+ } else if (local === "cellAddr" || local === "cellSpan") {
290
+ const cell = cellStack[cellStack.length - 1];
291
+ if (cell && insideCurrentTable(stack, tableStack)) {
292
+ if (local === "cellAddr") {
293
+ const ca = parseInt(getAttr(attrsRaw, "colAddr") || "", 10);
294
+ const ra = parseInt(getAttr(attrsRaw, "rowAddr") || "", 10);
295
+ if (!isNaN(ca)) cell.colAddr = ca;
296
+ if (!isNaN(ra)) cell.rowAddr = ra;
297
+ cell.addrTagRange = { start: m.index, end: contentStart };
298
+ } else {
299
+ const cs = parseInt(getAttr(attrsRaw, "colSpan") || "1", 10);
300
+ const rs = parseInt(getAttr(attrsRaw, "rowSpan") || "1", 10);
301
+ cell.colSpan = isNaN(cs) || cs < 1 ? 1 : cs;
302
+ cell.rowSpan = isNaN(rs) || rs < 1 ? 1 : rs;
303
+ }
304
+ }
305
+ } else if (local === "header" || local === "footer") {
306
+ if (stack.some((f) => f.local === "ctrl")) {
307
+ ctrlSubStack.push({ kind: local, texts: [] });
308
+ }
309
+ } else if (local === "tab" || local === "fwSpace" || local === "hwSpace" || local === "br" || local === "lineBreak") {
310
+ const para = owningPara();
311
+ if (para) para.text += " ";
312
+ }
313
+ }
314
+ for (const para of bodyParagraphs) fillRunInsertPos(para, xml);
315
+ for (const para of excludedParagraphs) fillRunInsertPos(para, xml);
316
+ const fillTableInsertPos = (table, depth = 0) => {
317
+ if (depth > 16) return;
318
+ for (const row of table.rows) {
319
+ for (const cell of row) {
320
+ for (const para of cell.paragraphs) fillRunInsertPos(para, xml);
321
+ for (const nested of cell.tables) fillTableInsertPos(nested, depth + 1);
322
+ }
323
+ }
324
+ };
325
+ for (const table of tables) fillTableInsertPos(table);
326
+ for (const table of orphanTables) fillTableInsertPos(table);
327
+ return { sectionIndex, xml, bodyParagraphs, tables, headerTexts, footerTexts, excludedParagraphs, orphanTables };
328
+ }
329
+ function getAttr(attrsRaw, name) {
330
+ const re = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`);
331
+ const m = attrsRaw.match(re);
332
+ return m ? m[1] ?? m[2] : void 0;
333
+ }
334
+ function insideCurrentTable(stack, tableStack) {
335
+ if (tableStack.length === 0) return false;
336
+ for (let i = stack.length - 1; i >= 0; i--) {
337
+ const l = stack[i].local;
338
+ if (l === "tc") return true;
339
+ if (l === "tbl") return false;
340
+ }
341
+ return false;
342
+ }
343
+ function fillRunInsertPos(para, xml) {
344
+ if (para.tRanges.length > 0) return;
345
+ const pEnd = findElementEnd(xml, para.start);
346
+ if (pEnd < 0) return;
347
+ const slice = xml.slice(para.start, pEnd);
348
+ const runOpen = slice.match(/<((?:[A-Za-z0-9_]+:)?run)(?:\s(?:"[^"]*"|'[^']*'|[^>"'])*?)?(\/?)>/);
349
+ if (!runOpen || runOpen.index === void 0) return;
350
+ if (runOpen[2] === "/") return;
351
+ const qname = runOpen[1];
352
+ const closeIdx = slice.indexOf(`</${qname}>`, runOpen.index);
353
+ if (closeIdx < 0) return;
354
+ para.runInsertPos = para.start + closeIdx;
355
+ para.runPrefix = prefixOf(qname);
356
+ }
357
+ function findElementEnd(xml, start) {
358
+ const open = xml.slice(start).match(/^<([^\s/>!?]+)/);
359
+ if (!open) return -1;
360
+ const qname = open[1];
361
+ const re = new RegExp(`<${qname}(?=[\\s/>])(?:"[^"]*"|'[^']*'|[^>"'])*?(/?)>|</${qname}\\s*>`, "g");
362
+ re.lastIndex = start;
363
+ let depth = 0;
364
+ let mm;
365
+ while ((mm = re.exec(xml)) !== null) {
366
+ if (mm[0].startsWith("</")) {
367
+ depth--;
368
+ if (depth === 0) return mm.index + mm[0].length;
369
+ } else if (mm[1] !== "/") {
370
+ depth++;
371
+ }
372
+ }
373
+ return -1;
374
+ }
375
+ function finalizeTable(table) {
376
+ const hasAddr = table.rows.some((row) => row.some((c) => c.colAddr !== void 0 && c.rowAddr !== void 0));
377
+ if (hasAddr) {
378
+ for (const row of table.rows) {
379
+ for (const cell of row) {
380
+ if (cell.rowAddr !== void 0 && cell.colAddr !== void 0) {
381
+ table.cellByAnchor.set(`${cell.rowAddr},${cell.colAddr}`, cell);
382
+ }
383
+ }
384
+ }
385
+ return;
386
+ }
387
+ const numRows = table.rows.length;
388
+ const occupied = Array.from({ length: numRows }, () => []);
389
+ for (let rowIdx = 0; rowIdx < numRows; rowIdx++) {
390
+ let colIdx = 0;
391
+ for (const cell of table.rows[rowIdx]) {
392
+ while (occupied[rowIdx][colIdx]) colIdx++;
393
+ cell.rowAddr = rowIdx;
394
+ cell.colAddr = colIdx;
395
+ table.cellByAnchor.set(`${rowIdx},${colIdx}`, cell);
396
+ for (let r = rowIdx; r < Math.min(rowIdx + cell.rowSpan, numRows); r++) {
397
+ for (let c = colIdx; c < colIdx + cell.colSpan; c++) {
398
+ occupied[r][c] = true;
399
+ }
400
+ }
401
+ colIdx += cell.colSpan;
402
+ }
403
+ }
404
+ }
405
+ function buildParagraphSplices(para, newText, xml) {
406
+ if (newText && xml) {
407
+ const orig = paraTText(para, xml);
408
+ if (orig && orig.trim() !== "") {
409
+ const lead = orig.match(/^\s*/)[0];
410
+ const trail = orig.match(/\s*$/)[0];
411
+ if ((lead || trail) && newText.trim() !== "") {
412
+ newText = lead + newText.replace(/^\s+|\s+$/g, "") + trail;
413
+ }
414
+ }
415
+ }
416
+ const escaped = escapeXmlText(newText);
417
+ if (para.tRanges.length > 0) {
418
+ const splices = [];
419
+ const first = para.tRanges[0];
420
+ if (first.selfClosing) {
421
+ const prefix = first.prefix ? first.prefix + ":" : "";
422
+ splices.push({ start: first.contentStart, end: first.contentEnd, replacement: `<${prefix}t>${escaped}</${prefix}t>` });
423
+ } else {
424
+ splices.push({ start: first.contentStart, end: first.contentEnd, replacement: escaped });
425
+ }
426
+ for (let i = 1; i < para.tRanges.length; i++) {
427
+ const r = para.tRanges[i];
428
+ if (!r.selfClosing && r.contentStart < r.contentEnd) {
429
+ splices.push({ start: r.contentStart, end: r.contentEnd, replacement: "" });
430
+ }
431
+ }
432
+ return splices;
433
+ }
434
+ if (para.runInsertPos !== void 0) {
435
+ if (!newText) return [];
436
+ const prefix = para.runPrefix ? para.runPrefix + ":" : "";
437
+ return [{ start: para.runInsertPos, end: para.runInsertPos, replacement: `<${prefix}t>${escaped}</${prefix}t>` }];
438
+ }
439
+ if (para.selfCloseRun && xml) {
440
+ if (!newText) return [];
441
+ const { start, end } = para.selfCloseRun;
442
+ const tag = xml.slice(start, end);
443
+ const qm = tag.match(/^<([^\s/>]+)/);
444
+ if (!qm || !tag.endsWith("/>")) return null;
445
+ const qname = qm[1];
446
+ const colon = qname.indexOf(":");
447
+ const prefix = colon >= 0 ? qname.slice(0, colon) + ":" : "";
448
+ const opened = tag.slice(0, tag.length - 2).trimEnd() + ">";
449
+ return [{ start, end, replacement: `${opened}<${prefix}t>${escaped}</${prefix}t></${qname}>` }];
450
+ }
451
+ return newText ? null : [];
452
+ }
453
+ function paraTText(para, xml) {
454
+ let text = "";
455
+ for (const t of para.tRanges) {
456
+ if (t.selfClosing) continue;
457
+ const raw = xml.slice(t.contentStart, t.contentEnd);
458
+ if (/[<&]/.test(raw)) return null;
459
+ text += raw;
460
+ }
461
+ return text;
462
+ }
463
+ function paraTextPureT(para, xml) {
464
+ let len = 0;
465
+ for (const t of para.tRanges) {
466
+ if (t.selfClosing) continue;
467
+ len += tContentToText(xml.slice(t.contentStart, t.contentEnd)).length;
468
+ }
469
+ return len === para.text.length;
470
+ }
471
+ function buildRangeSplices(para, xml, start, end, replacement) {
472
+ if (start < 0 || end < start) return null;
473
+ const segs = [];
474
+ let offset = 0;
475
+ for (const t of para.tRanges) {
476
+ if (t.selfClosing) continue;
477
+ const raw = xml.slice(t.contentStart, t.contentEnd);
478
+ if (/[<&]/.test(raw)) return null;
479
+ segs.push({ contentStart: t.contentStart, from: offset, to: offset + raw.length });
480
+ offset += raw.length;
481
+ }
482
+ if (segs.length === 0 || end > offset) return null;
483
+ const escaped = escapeXmlText(replacement);
484
+ if (start === end) {
485
+ for (const seg of segs) {
486
+ if (start >= seg.from && start <= seg.to) {
487
+ const at = seg.contentStart + (start - seg.from);
488
+ return [{ start: at, end: at, replacement: escaped }];
489
+ }
490
+ }
491
+ return null;
492
+ }
493
+ const splices = [];
494
+ let placed = false;
495
+ for (const seg of segs) {
496
+ if (seg.to <= start || seg.from >= end) continue;
497
+ const localStart = Math.max(seg.from, start) - seg.from;
498
+ const localEnd = Math.min(seg.to, end) - seg.from;
499
+ splices.push({
500
+ start: seg.contentStart + localStart,
501
+ end: seg.contentStart + localEnd,
502
+ replacement: placed ? "" : escaped
503
+ });
504
+ placed = true;
505
+ }
506
+ return placed ? splices : null;
507
+ }
508
+ function allLinesegRemovalSplices(xml) {
509
+ const segRe = /<(\w+:)?linesegarray\b[^>]*?(?:\/>|>[\s\S]*?<\/\1linesegarray>)/g;
510
+ const splices = [];
511
+ let m;
512
+ while ((m = segRe.exec(xml)) !== null) {
513
+ splices.push({ start: m.index, end: m.index + m[0].length, replacement: "" });
514
+ }
515
+ return splices;
516
+ }
517
+ function applySplices(xml, splices) {
518
+ const sorted = [...splices].sort((a, b) => a.start - b.start);
519
+ for (let i = 1; i < sorted.length; i++) {
520
+ if (sorted[i].start < sorted[i - 1].end) {
521
+ throw new Error("\uC18C\uC2A4\uB9F5 splice \uBC94\uC704 \uACB9\uCE68 \u2014 \uB0B4\uBD80 \uC624\uB958");
522
+ }
523
+ }
524
+ let result = xml;
525
+ for (let i = sorted.length - 1; i >= 0; i--) {
526
+ const s = sorted[i];
527
+ result = result.slice(0, s.start) + s.replacement + result.slice(s.end);
528
+ }
529
+ return result;
530
+ }
531
+
532
+ // src/roundtrip/zip-patch.ts
533
+ import { deflateRawSync } from "zlib";
534
+ var EOCD_SIG = 101010256;
535
+ var CD_SIG = 33639248;
536
+ var LOCAL_SIG = 67324752;
537
+ var ZIP64_EOCD_LOC_SIG = 117853008;
538
+ function copyBytes(buf, start, end) {
539
+ return new Uint8Array(buf.subarray(start, end));
540
+ }
541
+ function parseCentralDirectory(buf) {
542
+ const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
543
+ const minEocd = Math.max(0, buf.length - 22 - 65535);
544
+ let eocdOffset = -1;
545
+ for (let i = buf.length - 22; i >= minEocd; i--) {
546
+ if (view.getUint32(i, true) === EOCD_SIG && i + 22 + view.getUint16(i + 20, true) === buf.length) {
547
+ eocdOffset = i;
548
+ break;
549
+ }
550
+ }
551
+ if (eocdOffset < 0) {
552
+ for (let i = buf.length - 22; i >= minEocd; i--) {
553
+ if (view.getUint32(i, true) !== EOCD_SIG) continue;
554
+ if (i + 22 + view.getUint16(i + 20, true) > buf.length) continue;
555
+ const cand = view.getUint32(i + 16, true);
556
+ if (cand < buf.length - 4 && view.getUint32(cand, true) === CD_SIG) {
557
+ eocdOffset = i;
558
+ break;
559
+ }
560
+ }
561
+ }
562
+ if (eocdOffset < 0) throw new KordocError("ZIP EOCD\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
563
+ const totalEntries = view.getUint16(eocdOffset + 10, true);
564
+ const cdSize = view.getUint32(eocdOffset + 12, true);
565
+ const cdOffset = view.getUint32(eocdOffset + 16, true);
566
+ if (cdOffset === 4294967295 || totalEntries === 65535) throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
567
+ if (eocdOffset >= 20 && view.getUint32(eocdOffset - 20, true) === ZIP64_EOCD_LOC_SIG) {
568
+ throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
569
+ }
570
+ const decoder = new TextDecoder("utf-8");
571
+ const entries = [];
572
+ let pos = cdOffset;
573
+ for (let i = 0; i < totalEntries; i++) {
574
+ if (view.getUint32(pos, true) !== CD_SIG) throw new KordocError("ZIP Central Directory \uC190\uC0C1");
575
+ const flags = view.getUint16(pos + 8, true);
576
+ const method = view.getUint16(pos + 10, true);
577
+ const crc = view.getUint32(pos + 16, true);
578
+ const compSize = view.getUint32(pos + 20, true);
579
+ const uncompSize = view.getUint32(pos + 24, true);
580
+ const nameLen = view.getUint16(pos + 28, true);
581
+ const extraLen = view.getUint16(pos + 30, true);
582
+ const commentLen = view.getUint16(pos + 32, true);
583
+ const localOffset = view.getUint32(pos + 42, true);
584
+ if (compSize === 4294967295 || uncompSize === 4294967295 || localOffset === 4294967295) {
585
+ throw new KordocError("ZIP64\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4");
586
+ }
587
+ const name = decoder.decode(buf.subarray(pos + 46, pos + 46 + nameLen));
588
+ const cdEnd = pos + 46 + nameLen + extraLen + commentLen;
589
+ entries.push({ cdStart: pos, cdEnd, name, flags, method, crc, compSize, uncompSize, localOffset });
590
+ pos = cdEnd;
591
+ }
592
+ return { entries, cdOffset, cdSize, eocdOffset };
593
+ }
594
+ var CRC_TABLE = (() => {
595
+ const table = new Uint32Array(256);
596
+ for (let n = 0; n < 256; n++) {
597
+ let c = n;
598
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
599
+ table[n] = c >>> 0;
600
+ }
601
+ return table;
602
+ })();
603
+ function crc32(data) {
604
+ let crc = 4294967295;
605
+ for (let i = 0; i < data.length; i++) {
606
+ crc = CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8;
607
+ }
608
+ return (crc ^ 4294967295) >>> 0;
609
+ }
610
+ function patchZipEntries(original, replacements, additions) {
611
+ const { entries, cdOffset, eocdOffset } = parseCentralDirectory(original);
612
+ const view = new DataView(original.buffer, original.byteOffset, original.byteLength);
613
+ for (const name of replacements.keys()) {
614
+ if (!entries.some((e) => e.name === name)) throw new KordocError(`ZIP\uC5D0 \uC5C6\uB294 \uC5D4\uD2B8\uB9AC: ${name}`);
615
+ }
616
+ if (additions) {
617
+ for (const name of additions.keys()) {
618
+ if (entries.some((e) => e.name === name)) throw new KordocError(`ZIP\uC5D0 \uC774\uBBF8 \uC788\uB294 \uC5D4\uD2B8\uB9AC: ${name}`);
619
+ }
620
+ }
621
+ const byLocal = [...entries].sort((a, b) => a.localOffset - b.localOffset);
622
+ const segments = [];
623
+ const newLocalOffset = /* @__PURE__ */ new Map();
624
+ const newMeta = /* @__PURE__ */ new Map();
625
+ let offset = 0;
626
+ for (let i = 0; i < byLocal.length; i++) {
627
+ const e = byLocal[i];
628
+ const segEnd = i + 1 < byLocal.length ? byLocal[i + 1].localOffset : cdOffset;
629
+ newLocalOffset.set(e, offset);
630
+ const newData = replacements.get(e.name);
631
+ if (newData === void 0) {
632
+ const seg = original.subarray(e.localOffset, segEnd);
633
+ segments.push(seg);
634
+ offset += seg.length;
635
+ continue;
636
+ }
637
+ if (view.getUint32(e.localOffset, true) !== LOCAL_SIG) throw new KordocError("ZIP \uB85C\uCEEC \uD5E4\uB354 \uC2DC\uADF8\uB2C8\uCC98 \uBD88\uC77C\uCE58");
638
+ const nameLen = view.getUint16(e.localOffset + 26, true);
639
+ const extraLen = view.getUint16(e.localOffset + 28, true);
640
+ const headerLen = 30 + nameLen + extraLen;
641
+ const header = copyBytes(original, e.localOffset, e.localOffset + headerLen);
642
+ const hview = new DataView(header.buffer, header.byteOffset, header.byteLength);
643
+ const method = e.method;
644
+ const compData = method === 0 ? newData : new Uint8Array(deflateRawSync(newData));
645
+ const crc = crc32(newData);
646
+ const flags = e.flags & ~8;
647
+ hview.setUint16(6, flags, true);
648
+ hview.setUint32(14, crc, true);
649
+ hview.setUint32(18, compData.length, true);
650
+ hview.setUint32(22, newData.length, true);
651
+ segments.push(header, compData);
652
+ offset += headerLen + compData.length;
653
+ newMeta.set(e, { crc, compSize: compData.length, uncompSize: newData.length, flags });
654
+ }
655
+ const added = [];
656
+ if (additions) {
657
+ const encoder = new TextEncoder();
658
+ for (const [name, data] of additions) {
659
+ const nameBytes = encoder.encode(name);
660
+ const deflated = new Uint8Array(deflateRawSync(data));
661
+ const method = deflated.length < data.length ? 8 : 0;
662
+ const compData = method === 8 ? deflated : data;
663
+ const crc = crc32(data);
664
+ const header = new Uint8Array(30 + nameBytes.length);
665
+ const hv = new DataView(header.buffer);
666
+ hv.setUint32(0, LOCAL_SIG, true);
667
+ hv.setUint16(4, 20, true);
668
+ hv.setUint16(6, 2048, true);
669
+ hv.setUint16(8, method, true);
670
+ hv.setUint16(10, 0, true);
671
+ hv.setUint16(12, 33, true);
672
+ hv.setUint32(14, crc, true);
673
+ hv.setUint32(18, compData.length, true);
674
+ hv.setUint32(22, data.length, true);
675
+ hv.setUint16(26, nameBytes.length, true);
676
+ hv.setUint16(28, 0, true);
677
+ header.set(nameBytes, 30);
678
+ added.push({ nameBytes, crc, compSize: compData.length, uncompSize: data.length, method, localOffset: offset });
679
+ segments.push(header, compData);
680
+ offset += header.length + compData.length;
681
+ }
682
+ }
683
+ const newCdOffset = offset;
684
+ for (const e of entries) {
685
+ const cd = copyBytes(original, e.cdStart, e.cdEnd);
686
+ const cview = new DataView(cd.buffer, cd.byteOffset, cd.byteLength);
687
+ cview.setUint32(42, newLocalOffset.get(e), true);
688
+ const meta = newMeta.get(e);
689
+ if (meta) {
690
+ cview.setUint16(8, meta.flags, true);
691
+ cview.setUint32(16, meta.crc, true);
692
+ cview.setUint32(20, meta.compSize, true);
693
+ cview.setUint32(24, meta.uncompSize, true);
694
+ }
695
+ segments.push(cd);
696
+ offset += cd.length;
697
+ }
698
+ for (const a of added) {
699
+ const cd = new Uint8Array(46 + a.nameBytes.length);
700
+ const cv = new DataView(cd.buffer);
701
+ cv.setUint32(0, CD_SIG, true);
702
+ cv.setUint16(4, 20, true);
703
+ cv.setUint16(6, 20, true);
704
+ cv.setUint16(8, 2048, true);
705
+ cv.setUint16(10, a.method, true);
706
+ cv.setUint16(12, 0, true);
707
+ cv.setUint16(14, 33, true);
708
+ cv.setUint32(16, a.crc, true);
709
+ cv.setUint32(20, a.compSize, true);
710
+ cv.setUint32(24, a.uncompSize, true);
711
+ cv.setUint16(28, a.nameBytes.length, true);
712
+ cv.setUint32(42, a.localOffset, true);
713
+ cd.set(a.nameBytes, 46);
714
+ segments.push(cd);
715
+ offset += cd.length;
716
+ }
717
+ const newCdSize = offset - newCdOffset;
718
+ const eocd = copyBytes(original, eocdOffset);
719
+ const eview = new DataView(eocd.buffer, eocd.byteOffset, eocd.byteLength);
720
+ if (added.length > 0) {
721
+ eview.setUint16(8, view.getUint16(eocdOffset + 8, true) + added.length, true);
722
+ eview.setUint16(10, view.getUint16(eocdOffset + 10, true) + added.length, true);
723
+ }
724
+ eview.setUint32(12, newCdSize, true);
725
+ eview.setUint32(16, newCdOffset, true);
726
+ segments.push(eocd);
727
+ offset += eocd.length;
728
+ const result = new Uint8Array(offset);
729
+ let pos = 0;
730
+ for (const seg of segments) {
731
+ result.set(seg, pos);
732
+ pos += seg.length;
733
+ }
734
+ return result;
735
+ }
736
+
737
+ // src/form/seal.ts
738
+ var HU_PER_MM = 7200 / 25.4;
739
+ var mm2hu = (mm) => Math.round(mm * HU_PER_MM);
740
+ var MIME = {
741
+ png: "image/png",
742
+ jpg: "image/jpeg",
743
+ jpeg: "image/jpeg",
744
+ bmp: "image/bmp",
745
+ gif: "image/gif"
746
+ };
747
+ function glyphEm(code) {
748
+ if (code < 32) return 0;
749
+ if (code <= 126) return 0.5;
750
+ if (code >= 65377 && code <= 65500) return 0.5;
751
+ return 1;
752
+ }
753
+ function measureMm(text, emMm) {
754
+ let w = 0;
755
+ for (const ch of text) w += glyphEm(ch.codePointAt(0) ?? 0) * emMm;
756
+ return w;
757
+ }
758
+ function collectSites(scan) {
759
+ const sites = [];
760
+ for (const p of scan.bodyParagraphs) sites.push({ para: p });
761
+ const walkTables = (tables, depth) => {
762
+ if (depth > 16) return;
763
+ for (const t of tables) {
764
+ for (const row of t.rows) {
765
+ for (const cell of row) {
766
+ for (const p of cell.paragraphs) sites.push({ para: p, cell, table: t });
767
+ walkTables(cell.tables, depth + 1);
768
+ }
769
+ }
770
+ }
771
+ };
772
+ walkTables(scan.tables, 0);
773
+ walkTables(scan.orphanTables, 0);
774
+ sites.sort((a, b) => a.para.start - b.para.start);
775
+ return sites;
776
+ }
777
+ function attrOf(openTag, name) {
778
+ const m = openTag.match(new RegExp(`\\b${name}="([^"]*)"`));
779
+ return m?.[1];
780
+ }
781
+ function paraOpenTag(xml, para) {
782
+ const end = xml.indexOf(">", para.start);
783
+ return end === -1 ? "" : xml.slice(para.start, end + 1);
784
+ }
785
+ function anchorRunInfo(xml, para, anchor) {
786
+ let tr = para.tRanges.find((r) => !r.selfClosing && xml.slice(r.contentStart, r.contentEnd).includes(anchor));
787
+ if (!tr) tr = para.tRanges[para.tRanges.length - 1];
788
+ if (!tr) return null;
789
+ const before = xml.slice(Math.max(0, para.start), tr.contentStart);
790
+ const runOpen = [...before.matchAll(/<([A-Za-z0-9]+):run\b[^>]*>/g)].pop();
791
+ const prefix = runOpen?.[1] ?? tr.prefix ?? "hp";
792
+ const charPr = runOpen && attrOf(runOpen[0], "charPrIDRef") || "0";
793
+ const close = xml.indexOf(`</${prefix}:run>`, tr.contentEnd);
794
+ if (close === -1) return null;
795
+ return { charPr, prefix, insertAt: close + `</${prefix}:run>`.length };
796
+ }
797
+ function cellAddrWindow(xml, cell) {
798
+ if (!cell.addrTagRange) return null;
799
+ return xml.slice(cell.addrTagRange.end, cell.addrTagRange.end + 400);
800
+ }
801
+ function cellSzWidthHu(xml, cell) {
802
+ const win = cellAddrWindow(xml, cell);
803
+ if (win) {
804
+ const m = win.match(/<[A-Za-z0-9]+:cellSz\b[^>]*\bwidth="(\d+)"/);
805
+ if (m) return Number(m[1]);
806
+ }
807
+ const firstPara = cell.paragraphs[0];
808
+ if (!firstPara) return null;
809
+ const upto = xml.slice(0, firstPara.start);
810
+ const szMatch = [...upto.matchAll(/<[A-Za-z0-9]+:cellSz\b[^>]*\bwidth="(\d+)"[^>]*>/g)].pop();
811
+ return szMatch ? Number(szMatch[1]) : null;
812
+ }
813
+ function cellContentWidthMm(xml, cell) {
814
+ const width = cellSzWidthHu(xml, cell);
815
+ if (width === null) return null;
816
+ let content = width;
817
+ const win = cellAddrWindow(xml, cell);
818
+ const mg = win?.match(/<[A-Za-z0-9]+:cellMargin\b[^>]*>/);
819
+ if (mg) {
820
+ content -= Number(attrOf(mg[0], "left") ?? 0) + Number(attrOf(mg[0], "right") ?? 0);
821
+ }
822
+ return content > 0 ? content / HU_PER_MM : null;
823
+ }
824
+ function cellLeftOffsetMm(xml, table, cell) {
825
+ if (cell.colAddr === void 0) return 0;
826
+ const row = table.rows.find((r) => r.includes(cell));
827
+ if (!row) return 0;
828
+ let sum = 0;
829
+ for (const c of row) {
830
+ if (c === cell || c.colAddr === void 0 || c.colAddr >= cell.colAddr) continue;
831
+ const w = cellSzWidthHu(xml, c);
832
+ if (w) sum += w;
833
+ }
834
+ return sum / HU_PER_MM;
835
+ }
836
+ function bodyColumnWidthMm(xml) {
837
+ const page = xml.match(/<[A-Za-z0-9]+:pagePr\b[^>]*\bwidth="(\d+)"[^>]*>/);
838
+ const margin = xml.match(/<[A-Za-z0-9]+:margin\b[^>]*\bleft="(\d+)"[^>]*\bright="(\d+)"[^>]*>/);
839
+ if (!page) return 150;
840
+ const w = Number(page[1]) - (margin ? Number(margin[1]) + Number(margin[2]) : 0);
841
+ return w > 0 ? w / HU_PER_MM : 150;
842
+ }
843
+ function buildFloatPicXml(itemId, sizeHu, posXHu, posYHu, ids) {
844
+ const w = sizeHu;
845
+ const h = sizeHu;
846
+ return `<hp:pic xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core" id="${ids.id}" zOrder="0" numberingType="PICTURE" textWrap="IN_FRONT_OF_TEXT" textFlow="BOTH_SIDES" lock="0" dropcapstyle="None" href="" groupLevel="0" instid="${ids.instid}" reverse="0"><hp:offset x="0" y="0"/><hp:orgSz width="${w}" height="${h}"/><hp:curSz width="${w}" height="${h}"/><hp:flip horizontal="0" vertical="0"/><hp:rotationInfo angle="0" centerX="${Math.round(w / 2)}" centerY="${Math.round(h / 2)}" rotateimage="1"/><hp:renderingInfo><hc:transMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:scaMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/><hc:rotMatrix e1="1" e2="0" e3="0" e4="0" e5="1" e6="0"/></hp:renderingInfo><hp:imgRect><hc:pt0 x="0" y="0"/><hc:pt1 x="${w}" y="0"/><hc:pt2 x="${w}" y="${h}"/><hc:pt3 x="0" y="${h}"/></hp:imgRect><hp:imgClip left="0" right="${w}" top="0" bottom="${h}"/><hp:inMargin left="0" right="0" top="0" bottom="0"/><hp:imgDim dimwidth="${w}" dimheight="${h}"/><hc:img binaryItemIDRef="${itemId}" bright="0" contrast="0" effect="REAL_PIC" alpha="0"/><hp:effects/><hp:sz width="${w}" widthRelTo="ABSOLUTE" height="${h}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="0" affectLSpacing="0" flowWithText="0" allowOverlap="1" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="COLUMN" vertAlign="TOP" horzAlign="LEFT" vertOffset="${posYHu}" horzOffset="${posXHu}"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:shapeComment>kordoc seal</hp:shapeComment></hp:pic>`;
847
+ }
848
+ async function placeSealHwpx(hwpxBuffer, ops) {
849
+ if (ops.length === 0) throw new KordocError("place_seal: \uBC30\uCE58\uD560 \uB3C4\uC7A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4");
850
+ const u8 = new Uint8Array(hwpxBuffer);
851
+ const zip = await JSZip.loadAsync(hwpxBuffer);
852
+ const sectionPaths = Object.keys(zip.files).filter((name) => /[Ss]ection\d+\.xml$/i.test(name)).sort();
853
+ if (sectionPaths.length === 0) throw new KordocError("HWPX\uC5D0\uC11C \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
854
+ const manifestPath = Object.keys(zip.files).find((name) => /\.hpf$/i.test(name));
855
+ const headerPath = Object.keys(zip.files).find((name) => /(^|\/)header\.xml$/i.test(name));
856
+ const styles = headerPath ? parseRenderStyles(await zip.file(headerPath).async("text")) : null;
857
+ const sectionXmls = [];
858
+ const scans = [];
859
+ for (let si = 0; si < sectionPaths.length; si++) {
860
+ const xml = await zip.file(sectionPaths[si]).async("text");
861
+ sectionXmls.push(xml);
862
+ scans.push(scanSectionXml(xml, si));
863
+ }
864
+ const sitesBySection = scans.map(collectSites);
865
+ const usedIds = /* @__PURE__ */ new Set();
866
+ let manifestXml = "";
867
+ if (manifestPath) {
868
+ manifestXml = await zip.file(manifestPath).async("text");
869
+ for (const m of manifestXml.matchAll(/<opf:item\b[^>]*\bid="([^"]+)"/g)) usedIds.add(m[1]);
870
+ }
871
+ const usedImageNums = /* @__PURE__ */ new Set();
872
+ for (const name of Object.keys(zip.files)) {
873
+ const m = name.match(/^BinData\/(?:image|img)(\d+)\./i);
874
+ if (m) usedImageNums.add(Number(m[1]));
875
+ }
876
+ const nextImageNum = () => {
877
+ let n = 1;
878
+ while (usedImageNums.has(n) || usedIds.has(`image${n}`)) n++;
879
+ usedImageNums.add(n);
880
+ return n;
881
+ };
882
+ let maxId = 1e6;
883
+ for (const xml of sectionXmls) {
884
+ for (const m of xml.matchAll(/\b(?:id|instid)="(\d+)"/g)) {
885
+ const v = Number(m[1]);
886
+ if (Number.isFinite(v) && v > maxId) maxId = v;
887
+ }
888
+ }
889
+ const splicesBySection = sectionPaths.map(() => []);
890
+ const additions = /* @__PURE__ */ new Map();
891
+ const manifestItems = [];
892
+ const placed = [];
893
+ for (const op of ops) {
894
+ if (!op.anchor) throw new KordocError("place_seal: anchor \uBB38\uAD6C\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
895
+ if (!op.image || op.image.length === 0) throw new KordocError("place_seal: \uB3C4\uC7A5 \uC774\uBBF8\uC9C0\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
896
+ const ext = (op.ext ?? "png").toLowerCase();
897
+ if (!MIME[ext]) throw new KordocError(`place_seal: \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC774\uBBF8\uC9C0 \uD655\uC7A5\uC790 .${ext} (png/jpg/bmp/gif)`);
898
+ const wantOcc = op.occurrence ?? 0;
899
+ let found = null;
900
+ let total = 0;
901
+ for (let si2 = 0; si2 < scans.length && !found; si2++) {
902
+ for (const site2 of sitesBySection[si2]) {
903
+ let from = 0;
904
+ for (; ; ) {
905
+ const idx = site2.para.text.indexOf(op.anchor, from);
906
+ if (idx === -1) break;
907
+ if (total === wantOcc) {
908
+ found = { si: si2, site: site2, idxInText: idx };
909
+ break;
910
+ }
911
+ total++;
912
+ from = idx + op.anchor.length;
913
+ }
914
+ if (found) break;
915
+ }
916
+ }
917
+ if (!found) {
918
+ total = 0;
919
+ for (let si2 = 0; si2 < scans.length; si2++) {
920
+ for (const site2 of sitesBySection[si2]) {
921
+ let from = 0;
922
+ for (; ; ) {
923
+ const idx = site2.para.text.indexOf(op.anchor, from);
924
+ if (idx === -1) break;
925
+ total++;
926
+ from = idx + op.anchor.length;
927
+ }
928
+ }
929
+ }
930
+ throw new KordocError(
931
+ `place_seal: \uC575\uCEE4 "${op.anchor}" ${wantOcc}\uBC88\uC9F8 \uB4F1\uC7A5\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (\uBCF8\uBB38 \uB0B4 ${total}\uD68C \uB4F1\uC7A5 \u2014 occurrence 0..${Math.max(0, total - 1)})`
932
+ );
933
+ }
934
+ const { si, site, idxInText } = found;
935
+ const xml = sectionXmls[si];
936
+ const run = anchorRunInfo(xml, site.para, op.anchor);
937
+ if (!run) {
938
+ throw new KordocError(`place_seal: \uC575\uCEE4 "${op.anchor}" \uBB38\uB2E8\uC5D0\uC11C run\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4`);
939
+ }
940
+ const charPrHeight = styles?.charPr.get(run.charPr)?.height ?? 1e3;
941
+ const fontPt = charPrHeight / 100;
942
+ const emMm = fontPt * 25.4 / 72;
943
+ const lineHMm = emMm;
944
+ const startXMm = measureMm(site.para.text.slice(0, idxInText), emMm);
945
+ const anchorWMm = measureMm(op.anchor, emMm);
946
+ const sizeMm = op.sizeMm ?? Math.max(7, Math.min(18, lineHMm * 1.6));
947
+ const availMm = (site.cell ? cellContentWidthMm(xml, site.cell) : null) ?? bodyColumnWidthMm(xml);
948
+ const paraPrId = attrOf(paraOpenTag(xml, site.para), "paraPrIDRef") ?? "0";
949
+ const align = styles?.paraAlign.get(paraPrId);
950
+ let alignShiftMm = 0;
951
+ if (align === "CENTER" || align === "RIGHT") {
952
+ const paraWMm = measureMm(site.para.text, emMm);
953
+ alignShiftMm = align === "CENTER" ? (availMm - paraWMm) / 2 : availMm - paraWMm;
954
+ if (!Number.isFinite(alignShiftMm) || alignShiftMm < 0) alignShiftMm = 0;
955
+ }
956
+ let mode = op.mode ?? "auto";
957
+ if (mode === "auto") {
958
+ mode = availMm - (alignShiftMm + startXMm + anchorWMm) >= sizeMm + 2 ? "right" : "overlap";
959
+ }
960
+ const cellShiftMm = site.cell && site.table ? cellLeftOffsetMm(xml, site.table, site.cell) : 0;
961
+ const posXMm = (mode === "right" ? startXMm + anchorWMm + 2 : startXMm + anchorWMm / 2 - sizeMm / 2) + alignShiftMm + cellShiftMm + (op.dxMm ?? 0);
962
+ const posYMm = -(sizeMm - lineHMm) / 2 + (op.dyMm ?? 0);
963
+ const n = nextImageNum();
964
+ const entry = `BinData/image${n}.${ext}`;
965
+ const itemId = `image${n}`;
966
+ usedIds.add(itemId);
967
+ additions.set(entry, op.image);
968
+ manifestItems.push(`<opf:item id="${itemId}" href="${entry}" media-type="${MIME[ext]}" isEmbeded="1"/>`);
969
+ const pic = buildFloatPicXml(itemId, mm2hu(sizeMm), mm2hu(posXMm), mm2hu(posYMm), {
970
+ id: ++maxId,
971
+ instid: ++maxId
972
+ });
973
+ splicesBySection[si].push({
974
+ start: run.insertAt,
975
+ end: run.insertAt,
976
+ replacement: `<${run.prefix}:run charPrIDRef="${run.charPr}">${pic}</${run.prefix}:run>`
977
+ });
978
+ placed.push({
979
+ anchor: op.anchor,
980
+ occurrence: wantOcc,
981
+ sectionIndex: si,
982
+ mode,
983
+ posXMm: Math.round(posXMm * 100) / 100,
984
+ posYMm: Math.round(posYMm * 100) / 100,
985
+ sizeMm: Math.round(sizeMm * 100) / 100,
986
+ entry
987
+ });
988
+ }
989
+ const encoder = new TextEncoder();
990
+ const replacements = /* @__PURE__ */ new Map();
991
+ for (let si = 0; si < sectionPaths.length; si++) {
992
+ if (splicesBySection[si].length === 0) continue;
993
+ replacements.set(sectionPaths[si], encoder.encode(applySplices(sectionXmls[si], splicesBySection[si])));
994
+ }
995
+ if (manifestPath && manifestItems.length > 0) {
996
+ const patched = manifestXml.includes("</opf:manifest>") ? manifestXml.replace("</opf:manifest>", `${manifestItems.join("")}</opf:manifest>`) : manifestXml;
997
+ if (patched !== manifestXml) replacements.set(manifestPath, encoder.encode(patched));
998
+ }
999
+ const out = patchZipEntries(u8, replacements, additions);
1000
+ return {
1001
+ buffer: out.buffer.slice(out.byteOffset, out.byteOffset + out.byteLength),
1002
+ placed
1003
+ };
1004
+ }
1005
+
1006
+ export {
1007
+ escapeXmlText,
1008
+ scanSectionXml,
1009
+ findElementEnd,
1010
+ buildParagraphSplices,
1011
+ paraTText,
1012
+ paraTextPureT,
1013
+ buildRangeSplices,
1014
+ allLinesegRemovalSplices,
1015
+ applySplices,
1016
+ patchZipEntries,
1017
+ placeSealHwpx
1018
+ };
1019
+ //# sourceMappingURL=chunk-FGL7BH4P.js.map